@within-7/minto 0.1.7 → 0.3.0

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 (601) hide show
  1. package/cli.js +155 -37
  2. package/dist/Tool.js +38 -0
  3. package/dist/Tool.js.map +3 -3
  4. package/dist/commands/agents/AgentsCommand.js +73 -49
  5. package/dist/commands/agents/AgentsCommand.js.map +2 -2
  6. package/dist/commands/agents/constants.js +1 -1
  7. package/dist/commands/agents/constants.js.map +1 -1
  8. package/dist/commands/agents/index.js +1 -1
  9. package/dist/commands/bug.js +74 -7
  10. package/dist/commands/bug.js.map +3 -3
  11. package/dist/commands/clear.js +3 -0
  12. package/dist/commands/clear.js.map +2 -2
  13. package/dist/commands/compact.js +37 -0
  14. package/dist/commands/compact.js.map +2 -2
  15. package/dist/commands/context.js +85 -0
  16. package/dist/commands/context.js.map +7 -0
  17. package/dist/commands/ctx_viz.js +18 -10
  18. package/dist/commands/ctx_viz.js.map +2 -2
  19. package/dist/commands/doctor.js +158 -12
  20. package/dist/commands/doctor.js.map +2 -2
  21. package/dist/commands/export.js +157 -0
  22. package/dist/commands/export.js.map +7 -0
  23. package/dist/commands/mcp-interactive.js +28 -18
  24. package/dist/commands/mcp-interactive.js.map +2 -2
  25. package/dist/commands/model.js +9 -7
  26. package/dist/commands/model.js.map +2 -2
  27. package/dist/commands/permissions.js +87 -0
  28. package/dist/commands/permissions.js.map +7 -0
  29. package/dist/commands/plugin/AddMarketplaceForm.js +3 -2
  30. package/dist/commands/plugin/AddMarketplaceForm.js.map +2 -2
  31. package/dist/commands/plugin/ConfirmDialog.js +2 -1
  32. package/dist/commands/plugin/ConfirmDialog.js.map +2 -2
  33. package/dist/commands/plugin/ErrorView.js +2 -1
  34. package/dist/commands/plugin/ErrorView.js.map +2 -2
  35. package/dist/commands/plugin/InstalledPluginsByMarketplace.js +5 -4
  36. package/dist/commands/plugin/InstalledPluginsByMarketplace.js.map +2 -2
  37. package/dist/commands/plugin/InstalledPluginsManager.js +5 -4
  38. package/dist/commands/plugin/InstalledPluginsManager.js.map +2 -2
  39. package/dist/commands/plugin/MainMenu.js +2 -1
  40. package/dist/commands/plugin/MainMenu.js.map +2 -2
  41. package/dist/commands/plugin/MarketplaceManager.js +5 -4
  42. package/dist/commands/plugin/MarketplaceManager.js.map +2 -2
  43. package/dist/commands/plugin/MarketplaceSelector.js +4 -3
  44. package/dist/commands/plugin/MarketplaceSelector.js.map +2 -2
  45. package/dist/commands/plugin/PlaceholderScreen.js +3 -2
  46. package/dist/commands/plugin/PlaceholderScreen.js.map +2 -2
  47. package/dist/commands/plugin/PluginBrowser.js +6 -5
  48. package/dist/commands/plugin/PluginBrowser.js.map +2 -2
  49. package/dist/commands/plugin/PluginDetailsInstall.js +5 -4
  50. package/dist/commands/plugin/PluginDetailsInstall.js.map +2 -2
  51. package/dist/commands/plugin/PluginDetailsManage.js +4 -3
  52. package/dist/commands/plugin/PluginDetailsManage.js.map +2 -2
  53. package/dist/commands/plugin.js +16 -15
  54. package/dist/commands/plugin.js.map +2 -2
  55. package/dist/commands/quit.js +3 -1
  56. package/dist/commands/quit.js.map +2 -2
  57. package/dist/commands/sandbox.js +105 -0
  58. package/dist/commands/sandbox.js.map +7 -0
  59. package/dist/commands/setup.js +2 -1
  60. package/dist/commands/setup.js.map +2 -2
  61. package/dist/commands/status.js +59 -0
  62. package/dist/commands/status.js.map +7 -0
  63. package/dist/commands/tasks.js +108 -0
  64. package/dist/commands/tasks.js.map +7 -0
  65. package/dist/commands/todos.js +123 -0
  66. package/dist/commands/todos.js.map +7 -0
  67. package/dist/commands/undo.js +245 -0
  68. package/dist/commands/undo.js.map +7 -0
  69. package/dist/commands.js +22 -2
  70. package/dist/commands.js.map +2 -2
  71. package/dist/components/AgentThinkingBlock.js +10 -18
  72. package/dist/components/AgentThinkingBlock.js.map +2 -2
  73. package/dist/components/AsciiLogo.js +7 -8
  74. package/dist/components/AsciiLogo.js.map +2 -2
  75. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js +3 -2
  76. package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
  77. package/dist/components/AskUserQuestionDialog/QuestionView.js +2 -1
  78. package/dist/components/AskUserQuestionDialog/QuestionView.js.map +2 -2
  79. package/dist/components/BackgroundTasksPanel.js +78 -29
  80. package/dist/components/BackgroundTasksPanel.js.map +2 -2
  81. package/dist/components/BashStreamingProgress.js +24 -0
  82. package/dist/components/BashStreamingProgress.js.map +7 -0
  83. package/dist/components/CollapsibleHint.js +15 -0
  84. package/dist/components/CollapsibleHint.js.map +7 -0
  85. package/dist/components/Config.js +3 -2
  86. package/dist/components/Config.js.map +2 -2
  87. package/dist/components/ConsoleOAuthFlow.js +2 -1
  88. package/dist/components/ConsoleOAuthFlow.js.map +2 -2
  89. package/dist/components/Cost.js +2 -1
  90. package/dist/components/Cost.js.map +2 -2
  91. package/dist/components/FileEditToolUpdatedMessage.js +1 -1
  92. package/dist/components/FileEditToolUpdatedMessage.js.map +2 -2
  93. package/dist/components/HeaderBar.js +13 -8
  94. package/dist/components/HeaderBar.js.map +2 -2
  95. package/dist/components/HistorySearchOverlay.js +4 -3
  96. package/dist/components/HistorySearchOverlay.js.map +2 -2
  97. package/dist/components/HotkeyHelpPanel.js +134 -0
  98. package/dist/components/HotkeyHelpPanel.js.map +7 -0
  99. package/dist/components/InvalidConfigDialog.js +2 -1
  100. package/dist/components/InvalidConfigDialog.js.map +2 -2
  101. package/dist/components/Logo.js +24 -68
  102. package/dist/components/Logo.js.map +2 -2
  103. package/dist/components/MCPServerApprovalDialog.js +2 -1
  104. package/dist/components/MCPServerApprovalDialog.js.map +2 -2
  105. package/dist/components/MCPServerDialogCopy.js +2 -1
  106. package/dist/components/MCPServerDialogCopy.js.map +2 -2
  107. package/dist/components/MCPServerMultiselectDialog.js +2 -1
  108. package/dist/components/MCPServerMultiselectDialog.js.map +2 -2
  109. package/dist/components/Message.js +23 -7
  110. package/dist/components/Message.js.map +3 -3
  111. package/dist/components/MessageSelector.js +4 -3
  112. package/dist/components/MessageSelector.js.map +2 -2
  113. package/dist/components/ModeIndicator.js +2 -1
  114. package/dist/components/ModeIndicator.js.map +2 -2
  115. package/dist/components/ModelConfig.js +20 -6
  116. package/dist/components/ModelConfig.js.map +2 -2
  117. package/dist/components/ModelListManager.js +7 -6
  118. package/dist/components/ModelListManager.js.map +2 -2
  119. package/dist/components/ModelSelector/ModelSelector.js +27 -14
  120. package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
  121. package/dist/components/Onboarding.js +22 -16
  122. package/dist/components/Onboarding.js.map +2 -2
  123. package/dist/components/OperationSummary.js +130 -0
  124. package/dist/components/OperationSummary.js.map +7 -0
  125. package/dist/components/ProgressBar.js +74 -0
  126. package/dist/components/ProgressBar.js.map +7 -0
  127. package/dist/components/PromptInput.js +210 -87
  128. package/dist/components/PromptInput.js.map +2 -2
  129. package/dist/components/RequestStatusIndicator.js +194 -0
  130. package/dist/components/RequestStatusIndicator.js.map +7 -0
  131. package/dist/components/SensitiveFileWarning.js +31 -0
  132. package/dist/components/SensitiveFileWarning.js.map +7 -0
  133. package/dist/components/Spinner.js +141 -27
  134. package/dist/components/Spinner.js.map +2 -2
  135. package/dist/components/SpinnerSymbol.js +21 -27
  136. package/dist/components/SpinnerSymbol.js.map +2 -2
  137. package/dist/components/StreamingBashOutput.js +9 -8
  138. package/dist/components/StreamingBashOutput.js.map +2 -2
  139. package/dist/components/StructuredDiff.js +6 -8
  140. package/dist/components/StructuredDiff.js.map +2 -2
  141. package/dist/components/SubagentBlock.js +5 -3
  142. package/dist/components/SubagentBlock.js.map +2 -2
  143. package/dist/components/SubagentProgress.js +17 -15
  144. package/dist/components/SubagentProgress.js.map +2 -2
  145. package/dist/components/TaskCard.js +30 -24
  146. package/dist/components/TaskCard.js.map +2 -2
  147. package/dist/components/TextInput.js +9 -1
  148. package/dist/components/TextInput.js.map +2 -2
  149. package/dist/components/TodoChangeBlock.js +1 -1
  150. package/dist/components/TodoChangeBlock.js.map +2 -2
  151. package/dist/components/TodoPanel.js +140 -31
  152. package/dist/components/TodoPanel.js.map +3 -3
  153. package/dist/components/TokenCounter.js +74 -0
  154. package/dist/components/TokenCounter.js.map +7 -0
  155. package/dist/components/TokenWarning.js +2 -1
  156. package/dist/components/TokenWarning.js.map +2 -2
  157. package/dist/components/ToolUseLoader.js +2 -2
  158. package/dist/components/ToolUseLoader.js.map +2 -2
  159. package/dist/components/TreeConnector.js +26 -0
  160. package/dist/components/TreeConnector.js.map +7 -0
  161. package/dist/components/TrustDialog.js +2 -1
  162. package/dist/components/TrustDialog.js.map +2 -2
  163. package/dist/components/TurnCompletionIndicator.js +18 -0
  164. package/dist/components/TurnCompletionIndicator.js.map +7 -0
  165. package/dist/components/binary-feedback/BinaryFeedbackView.js +2 -1
  166. package/dist/components/binary-feedback/BinaryFeedbackView.js.map +2 -2
  167. package/dist/components/messages/AssistantTextMessage.js +20 -9
  168. package/dist/components/messages/AssistantTextMessage.js.map +2 -2
  169. package/dist/components/messages/AssistantThinkingMessage.js +18 -3
  170. package/dist/components/messages/AssistantThinkingMessage.js.map +2 -2
  171. package/dist/components/messages/AssistantToolUseMessage.js +17 -10
  172. package/dist/components/messages/AssistantToolUseMessage.js.map +2 -2
  173. package/dist/components/messages/GroupRenderer.js +54 -0
  174. package/dist/components/messages/GroupRenderer.js.map +7 -0
  175. package/dist/components/messages/NestedTasksPreview.js +24 -0
  176. package/dist/components/messages/NestedTasksPreview.js.map +7 -0
  177. package/dist/components/messages/ParallelTasksGroupView.js +93 -0
  178. package/dist/components/messages/ParallelTasksGroupView.js.map +7 -0
  179. package/dist/components/messages/TaskInModuleView.js +218 -0
  180. package/dist/components/messages/TaskInModuleView.js.map +7 -0
  181. package/dist/components/messages/TaskOutputContent.js +56 -0
  182. package/dist/components/messages/TaskOutputContent.js.map +7 -0
  183. package/dist/components/messages/UserPromptMessage.js +2 -2
  184. package/dist/components/messages/UserPromptMessage.js.map +2 -2
  185. package/dist/components/messages/UserToolResultMessage/UserToolSuccessMessage.js +2 -3
  186. package/dist/components/messages/UserToolResultMessage/UserToolSuccessMessage.js.map +2 -2
  187. package/dist/components/permissions/FallbackPermissionRequest.js +4 -4
  188. package/dist/components/permissions/FallbackPermissionRequest.js.map +2 -2
  189. package/dist/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.js +4 -4
  190. package/dist/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.js.map +2 -2
  191. package/dist/constants/colors.js +120 -54
  192. package/dist/constants/colors.js.map +2 -2
  193. package/dist/constants/formatRules.js +102 -0
  194. package/dist/constants/formatRules.js.map +7 -0
  195. package/dist/constants/prompts.js +12 -34
  196. package/dist/constants/prompts.js.map +2 -2
  197. package/dist/constants/symbols.js +64 -6
  198. package/dist/constants/symbols.js.map +2 -2
  199. package/dist/constants/timing.js +5 -0
  200. package/dist/constants/timing.js.map +2 -2
  201. package/dist/constants/toolInputExamples.js +84 -0
  202. package/dist/constants/toolInputExamples.js.map +7 -0
  203. package/dist/core/backupManager.js +321 -0
  204. package/dist/core/backupManager.js.map +7 -0
  205. package/dist/core/config/defaults.js +84 -0
  206. package/dist/core/config/defaults.js.map +7 -0
  207. package/dist/core/config/index.js +111 -0
  208. package/dist/core/config/index.js.map +7 -0
  209. package/dist/core/config/loader.js +221 -0
  210. package/dist/core/config/loader.js.map +7 -0
  211. package/dist/core/config/migrations.js +128 -0
  212. package/dist/core/config/migrations.js.map +7 -0
  213. package/dist/core/config/schema.js +178 -0
  214. package/dist/core/config/schema.js.map +7 -0
  215. package/dist/core/costTracker.js +129 -0
  216. package/dist/core/costTracker.js.map +7 -0
  217. package/dist/core/gitAutoCommit.js +287 -0
  218. package/dist/core/gitAutoCommit.js.map +7 -0
  219. package/dist/core/index.js +8 -0
  220. package/dist/core/index.js.map +7 -0
  221. package/dist/core/operationTracker.js +212 -0
  222. package/dist/core/operationTracker.js.map +7 -0
  223. package/dist/core/permissions/auditLog.js +204 -0
  224. package/dist/core/permissions/auditLog.js.map +7 -0
  225. package/dist/core/permissions/engine/index.js +3 -0
  226. package/dist/core/permissions/engine/index.js.map +7 -0
  227. package/dist/core/permissions/engine/permissionEngine.js +106 -0
  228. package/dist/core/permissions/engine/permissionEngine.js.map +7 -0
  229. package/dist/core/permissions/engine/types.js +1 -0
  230. package/dist/core/permissions/engine/types.js.map +7 -0
  231. package/dist/core/permissions/index.js +84 -0
  232. package/dist/core/permissions/index.js.map +7 -0
  233. package/dist/core/permissions/ruleEngine.js +259 -0
  234. package/dist/core/permissions/ruleEngine.js.map +7 -0
  235. package/dist/core/permissions/rules/allowedToolsRule.js +62 -0
  236. package/dist/core/permissions/rules/allowedToolsRule.js.map +7 -0
  237. package/dist/core/permissions/rules/autoEscalationRule.js +296 -0
  238. package/dist/core/permissions/rules/autoEscalationRule.js.map +7 -0
  239. package/dist/core/permissions/rules/index.js +46 -0
  240. package/dist/core/permissions/rules/index.js.map +7 -0
  241. package/dist/core/permissions/rules/planModeRule.js +55 -0
  242. package/dist/core/permissions/rules/planModeRule.js.map +7 -0
  243. package/dist/core/permissions/rules/projectBoundaryRule.js +173 -0
  244. package/dist/core/permissions/rules/projectBoundaryRule.js.map +7 -0
  245. package/dist/core/permissions/rules/safeModeRule.js +65 -0
  246. package/dist/core/permissions/rules/safeModeRule.js.map +7 -0
  247. package/dist/core/permissions/rules/sensitivePathsRule.js +345 -0
  248. package/dist/core/permissions/rules/sensitivePathsRule.js.map +7 -0
  249. package/dist/core/permissions/types.js +127 -0
  250. package/dist/core/permissions/types.js.map +7 -0
  251. package/dist/core/tokenStats.js +9 -0
  252. package/dist/core/tokenStats.js.map +7 -0
  253. package/dist/core/tokenStatsManager.js +331 -0
  254. package/dist/core/tokenStatsManager.js.map +7 -0
  255. package/dist/core/tools/executor.js +143 -0
  256. package/dist/core/tools/executor.js.map +7 -0
  257. package/dist/core/tools/index.js +15 -0
  258. package/dist/core/tools/index.js.map +7 -0
  259. package/dist/core/tools/registry.js +183 -0
  260. package/dist/core/tools/registry.js.map +7 -0
  261. package/dist/core/tools/types.js +1 -0
  262. package/dist/core/tools/types.js.map +7 -0
  263. package/dist/cost-tracker.js +23 -15
  264. package/dist/cost-tracker.js.map +2 -2
  265. package/dist/entrypoints/cli.js +158 -130
  266. package/dist/entrypoints/cli.js.map +2 -2
  267. package/dist/entrypoints/mcp.js +12 -4
  268. package/dist/entrypoints/mcp.js.map +2 -2
  269. package/dist/history.js +14 -3
  270. package/dist/history.js.map +2 -2
  271. package/dist/hooks/useAgentTokenStats.js +72 -0
  272. package/dist/hooks/useAgentTokenStats.js.map +7 -0
  273. package/dist/hooks/useAgentTranscripts.js +140 -0
  274. package/dist/hooks/useAgentTranscripts.js.map +7 -0
  275. package/dist/hooks/useAnimationSync.js +53 -0
  276. package/dist/hooks/useAnimationSync.js.map +7 -0
  277. package/dist/hooks/useArrowKeyHistory.js +4 -2
  278. package/dist/hooks/useArrowKeyHistory.js.map +2 -2
  279. package/dist/hooks/useCanUseTool.js +3 -1
  280. package/dist/hooks/useCanUseTool.js.map +2 -2
  281. package/dist/hooks/useExitOnCtrlCD.js +9 -5
  282. package/dist/hooks/useExitOnCtrlCD.js.map +2 -2
  283. package/dist/hooks/useHookStatus.js +40 -0
  284. package/dist/hooks/useHookStatus.js.map +7 -0
  285. package/dist/hooks/useLogMessages.js +29 -2
  286. package/dist/hooks/useLogMessages.js.map +2 -2
  287. package/dist/hooks/useMessageGroups.js +43 -0
  288. package/dist/hooks/useMessageGroups.js.map +7 -0
  289. package/dist/hooks/useTerminalSize.js +62 -6
  290. package/dist/hooks/useTerminalSize.js.map +2 -2
  291. package/dist/hooks/useUnifiedCompletion.js +69 -0
  292. package/dist/hooks/useUnifiedCompletion.js.map +2 -2
  293. package/dist/i18n/index.js +109 -0
  294. package/dist/i18n/index.js.map +7 -0
  295. package/dist/i18n/locales/en.js +348 -0
  296. package/dist/i18n/locales/en.js.map +7 -0
  297. package/dist/i18n/locales/index.js +7 -0
  298. package/dist/i18n/locales/index.js.map +7 -0
  299. package/dist/i18n/locales/zh-CN.js +348 -0
  300. package/dist/i18n/locales/zh-CN.js.map +7 -0
  301. package/dist/i18n/types.js +8 -0
  302. package/dist/i18n/types.js.map +7 -0
  303. package/dist/permissions.js +28 -1
  304. package/dist/permissions.js.map +2 -2
  305. package/dist/query.js +253 -21
  306. package/dist/query.js.map +3 -3
  307. package/dist/screens/REPL.js +523 -194
  308. package/dist/screens/REPL.js.map +3 -3
  309. package/dist/services/adapters/chatCompletions.js +3 -1
  310. package/dist/services/adapters/chatCompletions.js.map +2 -2
  311. package/dist/services/adapters/messageNormalizer.js +354 -0
  312. package/dist/services/adapters/messageNormalizer.js.map +7 -0
  313. package/dist/services/adapters/responsesAPI.js +6 -3
  314. package/dist/services/adapters/responsesAPI.js.map +2 -2
  315. package/dist/services/checkpointManager.js +386 -0
  316. package/dist/services/checkpointManager.js.map +7 -0
  317. package/dist/services/claude.js +192 -14
  318. package/dist/services/claude.js.map +3 -3
  319. package/dist/services/compressionService.js +50 -1
  320. package/dist/services/compressionService.js.map +2 -2
  321. package/dist/services/contextMonitor.js +162 -0
  322. package/dist/services/contextMonitor.js.map +7 -0
  323. package/dist/services/customCommands.js +60 -41
  324. package/dist/services/customCommands.js.map +2 -2
  325. package/dist/services/hookExecutor.js +173 -1
  326. package/dist/services/hookExecutor.js.map +2 -2
  327. package/dist/services/intelligentCompactor.js +281 -0
  328. package/dist/services/intelligentCompactor.js.map +7 -0
  329. package/dist/services/lspConfig.js +109 -0
  330. package/dist/services/lspConfig.js.map +7 -0
  331. package/dist/services/mcpClient.js +338 -43
  332. package/dist/services/mcpClient.js.map +2 -2
  333. package/dist/services/modelOrchestrator.js +310 -0
  334. package/dist/services/modelOrchestrator.js.map +7 -0
  335. package/dist/services/openai.js +8 -1
  336. package/dist/services/openai.js.map +2 -2
  337. package/dist/services/outputStyles.js +138 -0
  338. package/dist/services/outputStyles.js.map +7 -0
  339. package/dist/services/plugins/index.js +5 -0
  340. package/dist/services/plugins/index.js.map +7 -0
  341. package/dist/services/plugins/lspServers.js +188 -0
  342. package/dist/services/plugins/lspServers.js.map +7 -0
  343. package/dist/services/plugins/pluginRuntime.js +229 -0
  344. package/dist/services/plugins/pluginRuntime.js.map +7 -0
  345. package/dist/services/plugins/pluginValidation.js +219 -0
  346. package/dist/services/plugins/pluginValidation.js.map +7 -0
  347. package/dist/services/plugins/skillMarketplace.js +556 -0
  348. package/dist/services/plugins/skillMarketplace.js.map +7 -0
  349. package/dist/services/responseStateManager.js +37 -3
  350. package/dist/services/responseStateManager.js.map +2 -2
  351. package/dist/services/sandbox/filesystemBoundary.js +341 -0
  352. package/dist/services/sandbox/filesystemBoundary.js.map +7 -0
  353. package/dist/services/sandbox/index.js +14 -0
  354. package/dist/services/sandbox/index.js.map +7 -0
  355. package/dist/services/sandbox/networkProxy.js +293 -0
  356. package/dist/services/sandbox/networkProxy.js.map +7 -0
  357. package/dist/services/sandbox/sandboxController.js +574 -0
  358. package/dist/services/sandbox/sandboxController.js.map +7 -0
  359. package/dist/services/sandbox/types.js +50 -0
  360. package/dist/services/sandbox/types.js.map +7 -0
  361. package/dist/services/sessionMemory.js +266 -0
  362. package/dist/services/sessionMemory.js.map +7 -0
  363. package/dist/services/taskRouter.js +324 -0
  364. package/dist/services/taskRouter.js.map +7 -0
  365. package/dist/tools/ArchitectTool/ArchitectTool.js +7 -1
  366. package/dist/tools/ArchitectTool/ArchitectTool.js.map +2 -2
  367. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js +6 -2
  368. package/dist/tools/AskExpertModelTool/AskExpertModelTool.js.map +2 -2
  369. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js +2 -1
  370. package/dist/tools/AskUserQuestionTool/AskUserQuestionTool.js.map +2 -2
  371. package/dist/tools/BaseTool.js +72 -0
  372. package/dist/tools/BaseTool.js.map +7 -0
  373. package/dist/tools/BashOutputTool/BashOutputToolResultMessage.js +3 -0
  374. package/dist/tools/BashOutputTool/BashOutputToolResultMessage.js.map +2 -2
  375. package/dist/tools/BashTool/BashTool.js +79 -3
  376. package/dist/tools/BashTool/BashTool.js.map +2 -2
  377. package/dist/tools/BashTool/BashToolResultMessage.js +3 -0
  378. package/dist/tools/BashTool/BashToolResultMessage.js.map +2 -2
  379. package/dist/tools/BashTool/OutputLine.js +54 -0
  380. package/dist/tools/BashTool/OutputLine.js.map +2 -2
  381. package/dist/tools/BashTool/prompt.js +336 -3
  382. package/dist/tools/BashTool/prompt.js.map +2 -2
  383. package/dist/tools/FileEditTool/FileEditTool.js +29 -4
  384. package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
  385. package/dist/tools/FileEditTool/prompt.js +6 -3
  386. package/dist/tools/FileEditTool/prompt.js.map +2 -2
  387. package/dist/tools/FileWriteTool/FileWriteTool.js +5 -5
  388. package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
  389. package/dist/tools/FileWriteTool/prompt.js +4 -2
  390. package/dist/tools/FileWriteTool/prompt.js.map +2 -2
  391. package/dist/tools/GlobTool/GlobTool.js +4 -2
  392. package/dist/tools/GlobTool/GlobTool.js.map +2 -2
  393. package/dist/tools/GrepTool/GrepTool.js +36 -7
  394. package/dist/tools/GrepTool/GrepTool.js.map +2 -2
  395. package/dist/tools/KillShellTool/KillShellToolResultMessage.js +3 -0
  396. package/dist/tools/KillShellTool/KillShellToolResultMessage.js.map +2 -2
  397. package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js +109 -0
  398. package/dist/tools/ListMcpResourcesTool/ListMcpResourcesTool.js.map +7 -0
  399. package/dist/tools/ListMcpResourcesTool/prompt.js +19 -0
  400. package/dist/tools/ListMcpResourcesTool/prompt.js.map +7 -0
  401. package/dist/tools/LspTool/LspTool.js +664 -0
  402. package/dist/tools/LspTool/LspTool.js.map +7 -0
  403. package/dist/tools/LspTool/prompt.js +27 -0
  404. package/dist/tools/LspTool/prompt.js.map +7 -0
  405. package/dist/tools/MCPTool/MCPTool.js +9 -1
  406. package/dist/tools/MCPTool/MCPTool.js.map +2 -2
  407. package/dist/tools/MemoryReadTool/MemoryReadTool.js +19 -6
  408. package/dist/tools/MemoryReadTool/MemoryReadTool.js.map +2 -2
  409. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js +6 -6
  410. package/dist/tools/MemoryWriteTool/MemoryWriteTool.js.map +2 -2
  411. package/dist/tools/MultiEditTool/MultiEditTool.js +19 -2
  412. package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
  413. package/dist/tools/MultiEditTool/prompt.js +5 -3
  414. package/dist/tools/MultiEditTool/prompt.js.map +2 -2
  415. package/dist/tools/NotebookEditTool/NotebookEditTool.js +7 -2
  416. package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
  417. package/dist/tools/NotebookReadTool/NotebookReadTool.js.map +2 -2
  418. package/dist/tools/PlanModeTool/EnterPlanModeTool.js +75 -0
  419. package/dist/tools/PlanModeTool/EnterPlanModeTool.js.map +7 -0
  420. package/dist/tools/PlanModeTool/ExitPlanModeTool.js +109 -0
  421. package/dist/tools/PlanModeTool/ExitPlanModeTool.js.map +7 -0
  422. package/dist/tools/PlanModeTool/prompt.js +94 -0
  423. package/dist/tools/PlanModeTool/prompt.js.map +7 -0
  424. package/dist/tools/ReadMcpResourceTool/ReadMcpResourceTool.js +130 -0
  425. package/dist/tools/ReadMcpResourceTool/ReadMcpResourceTool.js.map +7 -0
  426. package/dist/tools/ReadMcpResourceTool/prompt.js +17 -0
  427. package/dist/tools/ReadMcpResourceTool/prompt.js.map +7 -0
  428. package/dist/tools/SkillTool/SkillTool.js +10 -4
  429. package/dist/tools/SkillTool/SkillTool.js.map +2 -2
  430. package/dist/tools/SkillTool/prompt.js +1 -1
  431. package/dist/tools/SkillTool/prompt.js.map +1 -1
  432. package/dist/tools/SlashCommandTool/SlashCommandTool.js +260 -0
  433. package/dist/tools/SlashCommandTool/SlashCommandTool.js.map +7 -0
  434. package/dist/tools/SlashCommandTool/prompt.js +35 -0
  435. package/dist/tools/SlashCommandTool/prompt.js.map +7 -0
  436. package/dist/tools/TaskOutputTool/TaskOutputTool.js +190 -0
  437. package/dist/tools/TaskOutputTool/TaskOutputTool.js.map +7 -0
  438. package/dist/tools/TaskOutputTool/prompt.js +15 -0
  439. package/dist/tools/TaskOutputTool/prompt.js.map +7 -0
  440. package/dist/tools/TaskTool/TaskTool.js +310 -104
  441. package/dist/tools/TaskTool/TaskTool.js.map +2 -2
  442. package/dist/tools/TaskTool/prompt.js.map +2 -2
  443. package/dist/tools/TodoWriteTool/TodoWriteTool.js +42 -77
  444. package/dist/tools/TodoWriteTool/TodoWriteTool.js.map +2 -2
  445. package/dist/tools/URLFetcherTool/URLFetcherTool.js +4 -1
  446. package/dist/tools/URLFetcherTool/URLFetcherTool.js.map +2 -2
  447. package/dist/tools/URLFetcherTool/cache.js +55 -8
  448. package/dist/tools/URLFetcherTool/cache.js.map +2 -2
  449. package/dist/tools.js +31 -2
  450. package/dist/tools.js.map +2 -2
  451. package/dist/types/hooks.js +4 -0
  452. package/dist/types/hooks.js.map +2 -2
  453. package/dist/types/marketplace.js.map +2 -2
  454. package/dist/types/messageGroup.js +36 -0
  455. package/dist/types/messageGroup.js.map +7 -0
  456. package/dist/types/plugin.js.map +2 -2
  457. package/dist/types/thinking.js +1 -0
  458. package/dist/types/thinking.js.map +7 -0
  459. package/dist/utils/BackgroundShellManager.js +136 -39
  460. package/dist/utils/BackgroundShellManager.js.map +2 -2
  461. package/dist/utils/CircuitBreaker.js +242 -0
  462. package/dist/utils/CircuitBreaker.js.map +7 -0
  463. package/dist/utils/MessageBatchBuffer.js +102 -0
  464. package/dist/utils/MessageBatchBuffer.js.map +7 -0
  465. package/dist/utils/PersistentShell.js +151 -1
  466. package/dist/utils/PersistentShell.js.map +2 -2
  467. package/dist/utils/agentLoader.js +1 -23
  468. package/dist/utils/agentLoader.js.map +2 -2
  469. package/dist/utils/agentTranscripts.js +641 -0
  470. package/dist/utils/agentTranscripts.js.map +7 -0
  471. package/dist/utils/animationManager.js +213 -0
  472. package/dist/utils/animationManager.js.map +7 -0
  473. package/dist/utils/animationSync.js +110 -0
  474. package/dist/utils/animationSync.js.map +7 -0
  475. package/dist/utils/ask.js +2 -0
  476. package/dist/utils/ask.js.map +2 -2
  477. package/dist/utils/asyncFile.js +215 -0
  478. package/dist/utils/asyncFile.js.map +7 -0
  479. package/dist/utils/backgroundAgentManager.js +231 -0
  480. package/dist/utils/backgroundAgentManager.js.map +7 -0
  481. package/dist/utils/config.js +108 -10
  482. package/dist/utils/config.js.map +2 -2
  483. package/dist/utils/conversationRecovery.js +19 -0
  484. package/dist/utils/conversationRecovery.js.map +2 -2
  485. package/dist/utils/credentials/CredentialStore.js +1 -0
  486. package/dist/utils/credentials/CredentialStore.js.map +7 -0
  487. package/dist/utils/credentials/EncryptedFileStore.js +157 -0
  488. package/dist/utils/credentials/EncryptedFileStore.js.map +7 -0
  489. package/dist/utils/credentials/index.js +37 -0
  490. package/dist/utils/credentials/index.js.map +7 -0
  491. package/dist/utils/credentials/migration.js +82 -0
  492. package/dist/utils/credentials/migration.js.map +7 -0
  493. package/dist/utils/exit.js +73 -0
  494. package/dist/utils/exit.js.map +7 -0
  495. package/dist/utils/format.js +73 -5
  496. package/dist/utils/format.js.map +2 -2
  497. package/dist/utils/generators.js +76 -6
  498. package/dist/utils/generators.js.map +2 -2
  499. package/dist/utils/globalErrorHandler.js +149 -0
  500. package/dist/utils/globalErrorHandler.js.map +7 -0
  501. package/dist/utils/groupHandlers/index.js +8 -0
  502. package/dist/utils/groupHandlers/index.js.map +7 -0
  503. package/dist/utils/groupHandlers/parallelTasksHandler.js +140 -0
  504. package/dist/utils/groupHandlers/parallelTasksHandler.js.map +7 -0
  505. package/dist/utils/groupHandlers/taskHandler.js +104 -0
  506. package/dist/utils/groupHandlers/taskHandler.js.map +7 -0
  507. package/dist/utils/groupHandlers/types.js +1 -0
  508. package/dist/utils/groupHandlers/types.js.map +7 -0
  509. package/dist/utils/logRotation.js +224 -0
  510. package/dist/utils/logRotation.js.map +7 -0
  511. package/dist/utils/markdown.js +13 -1
  512. package/dist/utils/markdown.js.map +2 -2
  513. package/dist/utils/marketplaceManager.js +3 -5
  514. package/dist/utils/marketplaceManager.js.map +2 -2
  515. package/dist/utils/memSafety.js +264 -0
  516. package/dist/utils/memSafety.js.map +7 -0
  517. package/dist/utils/messageGroupManager.js +274 -0
  518. package/dist/utils/messageGroupManager.js.map +7 -0
  519. package/dist/utils/messages.js +13 -4
  520. package/dist/utils/messages.js.map +2 -2
  521. package/dist/utils/model.js +119 -15
  522. package/dist/utils/model.js.map +3 -3
  523. package/dist/utils/permissions/filesystem.js +162 -6
  524. package/dist/utils/permissions/filesystem.js.map +2 -2
  525. package/dist/utils/plan/planMode.js +143 -0
  526. package/dist/utils/plan/planMode.js.map +7 -0
  527. package/dist/utils/pluginLoader.js +17 -21
  528. package/dist/utils/pluginLoader.js.map +2 -2
  529. package/dist/utils/ripgrep.js +55 -2
  530. package/dist/utils/ripgrep.js.map +2 -2
  531. package/dist/utils/safePath.js +132 -0
  532. package/dist/utils/safePath.js.map +7 -0
  533. package/dist/utils/sanitizeInput.js +32 -0
  534. package/dist/utils/sanitizeInput.js.map +7 -0
  535. package/dist/utils/secureKeyStorage.js +312 -0
  536. package/dist/utils/secureKeyStorage.js.map +7 -0
  537. package/dist/utils/sensitiveFiles.js +125 -0
  538. package/dist/utils/sensitiveFiles.js.map +7 -0
  539. package/dist/utils/session/sessionPlugins.js +67 -0
  540. package/dist/utils/session/sessionPlugins.js.map +7 -0
  541. package/dist/utils/taskDisplayUtils.js +257 -0
  542. package/dist/utils/taskDisplayUtils.js.map +7 -0
  543. package/dist/utils/teamConfig.js +2 -1
  544. package/dist/utils/teamConfig.js.map +2 -2
  545. package/dist/utils/theme.js +6 -6
  546. package/dist/utils/theme.js.map +1 -1
  547. package/dist/utils/todoStorage.js +92 -2
  548. package/dist/utils/todoStorage.js.map +2 -2
  549. package/dist/utils/toolRiskClassification.js +207 -0
  550. package/dist/utils/toolRiskClassification.js.map +7 -0
  551. package/dist/utils/toolTimeout.js +136 -0
  552. package/dist/utils/toolTimeout.js.map +7 -0
  553. package/dist/utils/tooling/safeRender.js +116 -0
  554. package/dist/utils/tooling/safeRender.js.map +7 -0
  555. package/dist/utils/userFriendlyError.js +346 -0
  556. package/dist/utils/userFriendlyError.js.map +7 -0
  557. package/dist/utils/vendor/ripgrep/arm64-darwin/rg +0 -0
  558. package/dist/version.js +2 -2
  559. package/dist/version.js.map +1 -1
  560. package/package.json +17 -5
  561. package/scripts/postinstall.js +128 -38
  562. package/dist/commands/agents.js +0 -2086
  563. package/dist/commands/agents.js.map +0 -7
  564. package/dist/commands/build.js +0 -74
  565. package/dist/commands/build.js.map +0 -7
  566. package/dist/commands/compression.js +0 -57
  567. package/dist/commands/compression.js.map +0 -7
  568. package/dist/commands/listen.js +0 -37
  569. package/dist/commands/listen.js.map +0 -7
  570. package/dist/commands/login.js +0 -37
  571. package/dist/commands/login.js.map +0 -7
  572. package/dist/commands/logout.js +0 -33
  573. package/dist/commands/logout.js.map +0 -7
  574. package/dist/commands/mcp.js +0 -40
  575. package/dist/commands/mcp.js.map +0 -7
  576. package/dist/commands/mcp_refresh.js +0 -40
  577. package/dist/commands/mcp_refresh.js.map +0 -7
  578. package/dist/commands/modelstatus.js +0 -21
  579. package/dist/commands/modelstatus.js.map +0 -7
  580. package/dist/commands/onboarding.js +0 -36
  581. package/dist/commands/onboarding.js.map +0 -7
  582. package/dist/commands/plugin-interactive.js +0 -446
  583. package/dist/commands/plugin-interactive.js.map +0 -7
  584. package/dist/commands/pr_comments.js +0 -61
  585. package/dist/commands/pr_comments.js.map +0 -7
  586. package/dist/commands/release-notes.js +0 -30
  587. package/dist/commands/release-notes.js.map +0 -7
  588. package/dist/commands/review.js +0 -51
  589. package/dist/commands/review.js.map +0 -7
  590. package/dist/components/Bug.js +0 -147
  591. package/dist/components/Bug.js.map +0 -7
  592. package/dist/components/ModelSelector.js +0 -2062
  593. package/dist/components/ModelSelector.js.map +0 -7
  594. package/dist/components/ModelStatusDisplay.js +0 -87
  595. package/dist/components/ModelStatusDisplay.js.map +0 -7
  596. package/dist/entrypoints/cli-wrapper.js +0 -61
  597. package/dist/entrypoints/cli-wrapper.js.map +0 -7
  598. package/dist/hooks/useCancelRequest.js +0 -28
  599. package/dist/hooks/useCancelRequest.js.map +0 -7
  600. package/dist/screens/Doctor.js +0 -22
  601. package/dist/screens/Doctor.js.map +0 -7
@@ -34,12 +34,17 @@ class RegExpCache {
34
34
  return regex;
35
35
  }
36
36
  }
37
+ const AUTO_CLEANUP_INTERVAL = 5 * 60 * 1e3;
38
+ const MAX_COMPLETED_SHELL_AGE = 30 * 60 * 1e3;
39
+ const MAX_COMPLETED_SHELLS = 50;
37
40
  class BackgroundShellManager extends EventEmitter {
38
41
  static instance = null;
39
42
  shells = /* @__PURE__ */ new Map();
40
43
  regexCache = new RegExpCache();
44
+ autoCleanupTimer = null;
41
45
  constructor() {
42
46
  super();
47
+ this.startAutoCleanup();
43
48
  }
44
49
  static getInstance() {
45
50
  if (!BackgroundShellManager.instance) {
@@ -47,6 +52,62 @@ class BackgroundShellManager extends EventEmitter {
47
52
  }
48
53
  return BackgroundShellManager.instance;
49
54
  }
55
+ /**
56
+ * Start automatic cleanup timer
57
+ * Cleans up old completed shells periodically to prevent memory bloat
58
+ */
59
+ startAutoCleanup() {
60
+ if (this.autoCleanupTimer) return;
61
+ this.autoCleanupTimer = setInterval(() => {
62
+ this.autoCleanup();
63
+ }, AUTO_CLEANUP_INTERVAL);
64
+ this.autoCleanupTimer.unref();
65
+ }
66
+ /**
67
+ * Stop automatic cleanup timer
68
+ * Should be called when shutting down
69
+ */
70
+ stopAutoCleanup() {
71
+ if (this.autoCleanupTimer) {
72
+ clearInterval(this.autoCleanupTimer);
73
+ this.autoCleanupTimer = null;
74
+ }
75
+ }
76
+ /**
77
+ * Automatic cleanup of old and excessive completed shells
78
+ * Called periodically by the auto-cleanup timer
79
+ */
80
+ autoCleanup() {
81
+ const completedShells = [];
82
+ for (const [shellId, shell] of this.shells.entries()) {
83
+ if (shell.status !== "running") {
84
+ completedShells.push([shellId, shell]);
85
+ }
86
+ }
87
+ let removed = 0;
88
+ const now = Date.now();
89
+ for (const [shellId, shell] of completedShells) {
90
+ if (shell.endTime && now - shell.endTime > MAX_COMPLETED_SHELL_AGE) {
91
+ this.cleanupListeners(shell);
92
+ this.shells.delete(shellId);
93
+ removed++;
94
+ }
95
+ }
96
+ const remainingCompleted = completedShells.filter(([shellId]) => this.shells.has(shellId)).sort((a, b) => (a[1].endTime || 0) - (b[1].endTime || 0));
97
+ while (remainingCompleted.length > MAX_COMPLETED_SHELLS) {
98
+ const oldest = remainingCompleted.shift();
99
+ if (oldest) {
100
+ const [shellId, shell] = oldest;
101
+ this.cleanupListeners(shell);
102
+ this.shells.delete(shellId);
103
+ removed++;
104
+ }
105
+ }
106
+ if (removed > 0) {
107
+ this.emit("autoCleaned", removed);
108
+ this.emitListChange();
109
+ }
110
+ }
50
111
  /**
51
112
  * Create a new background shell and start executing the command
52
113
  */
@@ -74,46 +135,55 @@ class BackgroundShellManager extends EventEmitter {
74
135
  lastReadStdoutIndex: 0,
75
136
  lastReadStderrIndex: 0
76
137
  };
77
- childProcess.stdout?.on("data", (data) => {
78
- const lines = data.toString().split("\n");
79
- shell.stdout.push(...lines);
80
- if (shell.stdout.length > MAX_OUTPUT_LINES) {
81
- const removed = shell.stdout.length - MAX_OUTPUT_LINES;
82
- shell.stdout = shell.stdout.slice(removed);
83
- shell.lastReadStdoutIndex = Math.max(
84
- 0,
85
- shell.lastReadStdoutIndex - removed
86
- );
87
- }
88
- this.emit("output", shellId, "stdout", lines);
89
- });
90
- childProcess.stderr?.on("data", (data) => {
91
- const lines = data.toString().split("\n");
92
- shell.stderr.push(...lines);
93
- if (shell.stderr.length > MAX_OUTPUT_LINES) {
94
- const removed = shell.stderr.length - MAX_OUTPUT_LINES;
95
- shell.stderr = shell.stderr.slice(removed);
96
- shell.lastReadStderrIndex = Math.max(
97
- 0,
98
- shell.lastReadStderrIndex - removed
99
- );
138
+ const listeners = {
139
+ stdout: (data) => {
140
+ const lines = data.toString().split("\n");
141
+ shell.stdout.push(...lines);
142
+ if (shell.stdout.length > MAX_OUTPUT_LINES) {
143
+ const removed = shell.stdout.length - MAX_OUTPUT_LINES;
144
+ shell.stdout = shell.stdout.slice(removed);
145
+ shell.lastReadStdoutIndex = Math.max(
146
+ 0,
147
+ shell.lastReadStdoutIndex - removed
148
+ );
149
+ }
150
+ this.emit("output", shellId, "stdout", lines);
151
+ },
152
+ stderr: (data) => {
153
+ const lines = data.toString().split("\n");
154
+ shell.stderr.push(...lines);
155
+ if (shell.stderr.length > MAX_OUTPUT_LINES) {
156
+ const removed = shell.stderr.length - MAX_OUTPUT_LINES;
157
+ shell.stderr = shell.stderr.slice(removed);
158
+ shell.lastReadStderrIndex = Math.max(
159
+ 0,
160
+ shell.lastReadStderrIndex - removed
161
+ );
162
+ }
163
+ this.emit("output", shellId, "stderr", lines);
164
+ },
165
+ exit: (code, signal) => {
166
+ shell.status = signal ? "killed" : "done";
167
+ shell.exitCode = code ?? void 0;
168
+ shell.endTime = Date.now();
169
+ this.emit("statusChange", shellId, shell.status);
170
+ this.emitListChange();
171
+ this.cleanupListeners(shell);
172
+ },
173
+ error: (error) => {
174
+ logError(`Background shell ${shellId} error: ${error.message}`);
175
+ shell.status = "killed";
176
+ shell.endTime = Date.now();
177
+ this.emit("statusChange", shellId, shell.status);
178
+ this.emitListChange();
179
+ this.cleanupListeners(shell);
100
180
  }
101
- this.emit("output", shellId, "stderr", lines);
102
- });
103
- childProcess.on("exit", (code, signal) => {
104
- shell.status = signal ? "killed" : "done";
105
- shell.exitCode = code ?? void 0;
106
- shell.endTime = Date.now();
107
- this.emit("statusChange", shellId, shell.status);
108
- this.emitListChange();
109
- });
110
- childProcess.on("error", (error) => {
111
- logError(`Background shell ${shellId} error: ${error.message}`);
112
- shell.status = "killed";
113
- shell.endTime = Date.now();
114
- this.emit("statusChange", shellId, shell.status);
115
- this.emitListChange();
116
- });
181
+ };
182
+ shell._listeners = listeners;
183
+ if (listeners.stdout) childProcess.stdout?.on("data", listeners.stdout);
184
+ if (listeners.stderr) childProcess.stderr?.on("data", listeners.stderr);
185
+ if (listeners.exit) childProcess.on("exit", listeners.exit);
186
+ if (listeners.error) childProcess.on("error", listeners.error);
117
187
  this.shells.set(shellId, shell);
118
188
  this.emit("shellCreated", shellId);
119
189
  this.emitListChange();
@@ -126,6 +196,29 @@ class BackgroundShellManager extends EventEmitter {
126
196
  emitListChange() {
127
197
  this.emit("listChange", this.list());
128
198
  }
199
+ /**
200
+ * 🔧 CRITICAL FIX: Clean up event listeners to prevent memory leaks
201
+ * This removes all event listeners attached to the child process
202
+ */
203
+ cleanupListeners(shell) {
204
+ if (!shell._listeners || !shell.process) {
205
+ return;
206
+ }
207
+ const { stdout, stderr, exit, error } = shell._listeners;
208
+ if (stdout) {
209
+ shell.process.stdout?.off("data", stdout);
210
+ }
211
+ if (stderr) {
212
+ shell.process.stderr?.off("data", stderr);
213
+ }
214
+ if (exit) {
215
+ shell.process.off("exit", exit);
216
+ }
217
+ if (error) {
218
+ shell.process.off("error", error);
219
+ }
220
+ delete shell._listeners;
221
+ }
129
222
  /**
130
223
  * Get a background shell by ID
131
224
  */
@@ -204,6 +297,7 @@ class BackgroundShellManager extends EventEmitter {
204
297
  }
205
298
  shell.status = "killed";
206
299
  shell.endTime = Date.now();
300
+ this.cleanupListeners(shell);
207
301
  this.emit("statusChange", shellId, "killed");
208
302
  this.emitListChange();
209
303
  return true;
@@ -213,6 +307,7 @@ class BackgroundShellManager extends EventEmitter {
213
307
  error
214
308
  );
215
309
  logError(`Failed to kill shell ${shellId}: ${error}`);
310
+ this.cleanupListeners(shell);
216
311
  return false;
217
312
  }
218
313
  }
@@ -240,6 +335,7 @@ class BackgroundShellManager extends EventEmitter {
240
335
  remove(shellId) {
241
336
  const shell = this.shells.get(shellId);
242
337
  if (!shell || shell.status === "running") return false;
338
+ this.cleanupListeners(shell);
243
339
  this.shells.delete(shellId);
244
340
  this.emit("shellRemoved", shellId);
245
341
  this.emitListChange();
@@ -253,6 +349,7 @@ class BackgroundShellManager extends EventEmitter {
253
349
  let removed = 0;
254
350
  for (const [shellId, shell] of this.shells.entries()) {
255
351
  if (shell.status !== "running" && shell.endTime && now - shell.endTime > maxAge) {
352
+ this.cleanupListeners(shell);
256
353
  this.shells.delete(shellId);
257
354
  removed++;
258
355
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/utils/BackgroundShellManager.ts"],
4
- "sourcesContent": ["import { spawn, type ChildProcess } from 'child_process'\nimport { EventEmitter } from 'events'\nimport { randomUUID } from 'crypto'\nimport { logError } from './log'\n\nexport type BackgroundShellStatus = 'running' | 'done' | 'killed'\n\nexport interface BackgroundShell {\n id: string\n command: string\n status: BackgroundShellStatus\n startTime: number\n endTime?: number\n process: ChildProcess\n stdout: string[]\n stderr: string[]\n exitCode?: number\n lastReadStdoutIndex: number\n lastReadStderrIndex: number\n}\n\nconst MAX_OUTPUT_LINES = 1000 // Keep last N lines in memory\nconst REGEX_CACHE_SIZE = 50 // LRU cache size for compiled regexes\n\n// Simple LRU cache for compiled RegExp objects\nclass RegExpCache {\n private cache = new Map<string, RegExp>()\n private maxSize: number\n\n constructor(maxSize: number = REGEX_CACHE_SIZE) {\n this.maxSize = maxSize\n }\n\n get(pattern: string): RegExp | undefined {\n const regex = this.cache.get(pattern)\n if (regex) {\n // Move to end (most recently used)\n this.cache.delete(pattern)\n this.cache.set(pattern, regex)\n }\n return regex\n }\n\n set(pattern: string, regex: RegExp): void {\n // Evict oldest if at capacity\n if (this.cache.size >= this.maxSize) {\n const oldest = this.cache.keys().next().value\n if (oldest) this.cache.delete(oldest)\n }\n this.cache.set(pattern, regex)\n }\n\n getOrCreate(pattern: string): RegExp {\n let regex = this.get(pattern)\n if (!regex) {\n regex = new RegExp(pattern)\n this.set(pattern, regex)\n }\n return regex\n }\n}\n\nexport class BackgroundShellManager extends EventEmitter {\n private static instance: BackgroundShellManager | null = null\n private shells: Map<string, BackgroundShell> = new Map()\n private regexCache = new RegExpCache()\n\n private constructor() {\n super()\n }\n\n static getInstance(): BackgroundShellManager {\n if (!BackgroundShellManager.instance) {\n BackgroundShellManager.instance = new BackgroundShellManager()\n }\n return BackgroundShellManager.instance\n }\n\n /**\n * Create a new background shell and start executing the command\n */\n create(command: string, cwd?: string): string {\n const shellId = randomUUID().slice(0, 8)\n\n // Spawn the command in a shell\n const childProcess = spawn(command, [], {\n shell: true,\n cwd: cwd || process.cwd(),\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: true, // Create a new process group for easier cleanup\n env: {\n ...process.env,\n GIT_EDITOR: 'true',\n },\n })\n\n const shell: BackgroundShell = {\n id: shellId,\n command,\n status: 'running',\n startTime: Date.now(),\n process: childProcess,\n stdout: [],\n stderr: [],\n lastReadStdoutIndex: 0,\n lastReadStderrIndex: 0,\n }\n\n // Capture stdout\n childProcess.stdout?.on('data', (data: Buffer) => {\n const lines = data.toString().split('\\n')\n shell.stdout.push(...lines)\n\n // Keep only last MAX_OUTPUT_LINES\n if (shell.stdout.length > MAX_OUTPUT_LINES) {\n const removed = shell.stdout.length - MAX_OUTPUT_LINES\n shell.stdout = shell.stdout.slice(removed)\n shell.lastReadStdoutIndex = Math.max(\n 0,\n shell.lastReadStdoutIndex - removed,\n )\n }\n\n this.emit('output', shellId, 'stdout', lines)\n })\n\n // Capture stderr\n childProcess.stderr?.on('data', (data: Buffer) => {\n const lines = data.toString().split('\\n')\n shell.stderr.push(...lines)\n\n // Keep only last MAX_OUTPUT_LINES\n if (shell.stderr.length > MAX_OUTPUT_LINES) {\n const removed = shell.stderr.length - MAX_OUTPUT_LINES\n shell.stderr = shell.stderr.slice(removed)\n shell.lastReadStderrIndex = Math.max(\n 0,\n shell.lastReadStderrIndex - removed,\n )\n }\n\n this.emit('output', shellId, 'stderr', lines)\n })\n\n // Handle process exit\n childProcess.on('exit', (code, signal) => {\n shell.status = signal ? 'killed' : 'done'\n shell.exitCode = code ?? undefined\n shell.endTime = Date.now()\n this.emit('statusChange', shellId, shell.status)\n this.emitListChange()\n })\n\n childProcess.on('error', error => {\n logError(`Background shell ${shellId} error: ${error.message}`)\n shell.status = 'killed'\n shell.endTime = Date.now()\n this.emit('statusChange', shellId, shell.status)\n this.emitListChange()\n })\n\n this.shells.set(shellId, shell)\n this.emit('shellCreated', shellId)\n this.emitListChange()\n\n return shellId\n }\n\n /**\n * Emit a list change event with the current shell list\n * Used for event-driven updates instead of polling\n */\n private emitListChange(): void {\n this.emit('listChange', this.list())\n }\n\n /**\n * Get a background shell by ID\n */\n get(shellId: string): BackgroundShell | undefined {\n return this.shells.get(shellId)\n }\n\n /**\n * Get new output since last read\n */\n getNewOutput(\n shellId: string,\n filter?: string,\n ): {\n stdout: string[]\n stderr: string[]\n } | null {\n const shell = this.shells.get(shellId)\n if (!shell) return null\n\n let newStdout = shell.stdout.slice(shell.lastReadStdoutIndex)\n let newStderr = shell.stderr.slice(shell.lastReadStderrIndex)\n\n // Apply regex filter if provided (using cached regex)\n if (filter) {\n try {\n const regex = this.regexCache.getOrCreate(filter)\n newStdout = newStdout.filter(line => regex.test(line))\n newStderr = newStderr.filter(line => regex.test(line))\n } catch (e) {\n logError(`Invalid regex filter: ${filter}`)\n }\n }\n\n // Update read indices\n shell.lastReadStdoutIndex = shell.stdout.length\n shell.lastReadStderrIndex = shell.stderr.length\n\n return { stdout: newStdout, stderr: newStderr }\n }\n\n /**\n * Get all output (entire buffer)\n */\n getAllOutput(shellId: string): {\n stdout: string[]\n stderr: string[]\n } | null {\n const shell = this.shells.get(shellId)\n if (!shell) return null\n\n return {\n stdout: [...shell.stdout],\n stderr: [...shell.stderr],\n }\n }\n\n /**\n * Kill a background shell\n */\n kill(shellId: string): boolean {\n const shell = this.shells.get(shellId)\n if (!shell) {\n console.log(\n `[BackgroundShellManager] Cannot kill: shell ${shellId} not found`,\n )\n return false\n }\n\n if (shell.status === 'running') {\n try {\n const pid = shell.process.pid\n if (!pid) {\n console.log(\n `[BackgroundShellManager] Cannot kill: shell ${shellId} has no PID`,\n )\n return false\n }\n\n console.log(\n `[BackgroundShellManager] Killing shell ${shellId} (PID: ${pid}, command: ${shell.command})`,\n )\n\n // Kill the process and all its children\n if (process.platform === 'win32') {\n spawn('taskkill', ['/pid', pid.toString(), '/f', '/t'])\n } else {\n // Send SIGKILL to process group (more forceful than SIGTERM)\n // The negative PID sends signal to the entire process group\n try {\n process.kill(-pid, 'SIGKILL')\n console.log(\n `[BackgroundShellManager] Sent SIGKILL to process group -${pid}`,\n )\n } catch (e) {\n // If process group kill fails, try killing just the main process\n console.log(\n `[BackgroundShellManager] Process group kill failed, trying main process only`,\n )\n process.kill(pid, 'SIGKILL')\n }\n }\n\n shell.status = 'killed'\n shell.endTime = Date.now()\n this.emit('statusChange', shellId, 'killed')\n this.emitListChange()\n return true\n } catch (error) {\n console.error(\n `[BackgroundShellManager] Failed to kill shell ${shellId}:`,\n error,\n )\n logError(`Failed to kill shell ${shellId}: ${error}`)\n return false\n }\n }\n\n console.log(\n `[BackgroundShellManager] Shell ${shellId} is not running (status: ${shell.status})`,\n )\n return false\n }\n\n /**\n * List all background shells\n */\n list(): BackgroundShell[] {\n return Array.from(this.shells.values())\n }\n\n /**\n * Get shell status\n */\n getStatus(shellId: string): BackgroundShellStatus | null {\n const shell = this.shells.get(shellId)\n return shell ? shell.status : null\n }\n\n /**\n * Remove a shell from the manager (only if done or killed)\n */\n remove(shellId: string): boolean {\n const shell = this.shells.get(shellId)\n if (!shell || shell.status === 'running') return false\n\n this.shells.delete(shellId)\n this.emit('shellRemoved', shellId)\n this.emitListChange()\n return true\n }\n\n /**\n * Clean up old completed/killed shells\n */\n cleanup(maxAge: number = 3600000): number {\n const now = Date.now()\n let removed = 0\n\n for (const [shellId, shell] of this.shells.entries()) {\n if (\n shell.status !== 'running' &&\n shell.endTime &&\n now - shell.endTime > maxAge\n ) {\n this.shells.delete(shellId)\n removed++\n }\n }\n\n if (removed > 0) {\n this.emit('cleaned', removed)\n this.emitListChange()\n }\n\n return removed\n }\n\n /**\n * Get summary of shell for display\n */\n getSummary(shellId: string): string | null {\n const shell = this.shells.get(shellId)\n if (!shell) return null\n\n const duration = shell.endTime\n ? Math.floor((shell.endTime - shell.startTime) / 1000)\n : Math.floor((Date.now() - shell.startTime) / 1000)\n\n // Get last few lines of output for preview\n const lastLines = shell.stdout.slice(-2).join(' ').slice(0, 50)\n const preview = lastLines ? ` ${lastLines}\u2026` : ''\n\n return `${shell.command}${preview}`\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { spawn, type ChildProcess } from 'child_process'\nimport { EventEmitter } from 'events'\nimport { randomUUID } from 'crypto'\nimport { logError } from './log'\n\nexport type BackgroundShellStatus = 'running' | 'done' | 'killed'\n\n// \uD83D\uDD27 Internal event listener references for cleanup\ninterface ShellListeners {\n stdout?: (data: Buffer) => void\n stderr?: (data: Buffer) => void\n exit?: (code: number | null, signal: NodeJS.Signals | null) => void\n error?: (error: Error) => void\n}\n\nexport interface BackgroundShell {\n id: string\n command: string\n status: BackgroundShellStatus\n startTime: number\n endTime?: number\n process: ChildProcess\n stdout: string[]\n stderr: string[]\n exitCode?: number\n lastReadStdoutIndex: number\n lastReadStderrIndex: number\n /** @internal Event listeners for cleanup */\n _listeners?: ShellListeners\n}\n\nconst MAX_OUTPUT_LINES = 1000 // Keep last N lines in memory\nconst REGEX_CACHE_SIZE = 50 // LRU cache size for compiled regexes\n\n// Simple LRU cache for compiled RegExp objects\nclass RegExpCache {\n private cache = new Map<string, RegExp>()\n private maxSize: number\n\n constructor(maxSize: number = REGEX_CACHE_SIZE) {\n this.maxSize = maxSize\n }\n\n get(pattern: string): RegExp | undefined {\n const regex = this.cache.get(pattern)\n if (regex) {\n // Move to end (most recently used)\n this.cache.delete(pattern)\n this.cache.set(pattern, regex)\n }\n return regex\n }\n\n set(pattern: string, regex: RegExp): void {\n // Evict oldest if at capacity\n if (this.cache.size >= this.maxSize) {\n const oldest = this.cache.keys().next().value\n if (oldest) this.cache.delete(oldest)\n }\n this.cache.set(pattern, regex)\n }\n\n getOrCreate(pattern: string): RegExp {\n let regex = this.get(pattern)\n if (!regex) {\n regex = new RegExp(pattern)\n this.set(pattern, regex)\n }\n return regex\n }\n}\n\n// Auto-cleanup configuration\nconst AUTO_CLEANUP_INTERVAL = 5 * 60 * 1000 // 5 minutes\nconst MAX_COMPLETED_SHELL_AGE = 30 * 60 * 1000 // 30 minutes\nconst MAX_COMPLETED_SHELLS = 50 // Maximum completed shells to keep\n\nexport class BackgroundShellManager extends EventEmitter {\n private static instance: BackgroundShellManager | null = null\n private shells: Map<string, BackgroundShell> = new Map()\n private regexCache = new RegExpCache()\n private autoCleanupTimer: ReturnType<typeof setInterval> | null = null\n\n private constructor() {\n super()\n // Start auto-cleanup timer\n this.startAutoCleanup()\n }\n\n static getInstance(): BackgroundShellManager {\n if (!BackgroundShellManager.instance) {\n BackgroundShellManager.instance = new BackgroundShellManager()\n }\n return BackgroundShellManager.instance\n }\n\n /**\n * Start automatic cleanup timer\n * Cleans up old completed shells periodically to prevent memory bloat\n */\n private startAutoCleanup(): void {\n if (this.autoCleanupTimer) return\n\n this.autoCleanupTimer = setInterval(() => {\n this.autoCleanup()\n }, AUTO_CLEANUP_INTERVAL)\n\n // Don't keep the process alive just for cleanup\n this.autoCleanupTimer.unref()\n }\n\n /**\n * Stop automatic cleanup timer\n * Should be called when shutting down\n */\n stopAutoCleanup(): void {\n if (this.autoCleanupTimer) {\n clearInterval(this.autoCleanupTimer)\n this.autoCleanupTimer = null\n }\n }\n\n /**\n * Automatic cleanup of old and excessive completed shells\n * Called periodically by the auto-cleanup timer\n */\n private autoCleanup(): void {\n const completedShells: Array<[string, BackgroundShell]> = []\n\n // Collect all non-running shells\n for (const [shellId, shell] of this.shells.entries()) {\n if (shell.status !== 'running') {\n completedShells.push([shellId, shell])\n }\n }\n\n let removed = 0\n\n // First, remove shells older than MAX_COMPLETED_SHELL_AGE\n const now = Date.now()\n for (const [shellId, shell] of completedShells) {\n if (shell.endTime && now - shell.endTime > MAX_COMPLETED_SHELL_AGE) {\n this.cleanupListeners(shell)\n this.shells.delete(shellId)\n removed++\n }\n }\n\n // Then, if still too many, remove oldest until under limit\n const remainingCompleted = completedShells\n .filter(([shellId]) => this.shells.has(shellId))\n .sort((a, b) => (a[1].endTime || 0) - (b[1].endTime || 0))\n\n while (remainingCompleted.length > MAX_COMPLETED_SHELLS) {\n const oldest = remainingCompleted.shift()\n if (oldest) {\n const [shellId, shell] = oldest\n this.cleanupListeners(shell)\n this.shells.delete(shellId)\n removed++\n }\n }\n\n if (removed > 0) {\n this.emit('autoCleaned', removed)\n this.emitListChange()\n }\n }\n\n /**\n * Create a new background shell and start executing the command\n */\n create(command: string, cwd?: string): string {\n const shellId = randomUUID().slice(0, 8)\n\n // Spawn the command in a shell\n const childProcess = spawn(command, [], {\n shell: true,\n cwd: cwd || process.cwd(),\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: true, // Create a new process group for easier cleanup\n env: {\n ...process.env,\n GIT_EDITOR: 'true',\n },\n })\n\n const shell: BackgroundShell = {\n id: shellId,\n command,\n status: 'running',\n startTime: Date.now(),\n process: childProcess,\n stdout: [],\n stderr: [],\n lastReadStdoutIndex: 0,\n lastReadStderrIndex: 0,\n }\n\n // \uD83D\uDD27 CRITICAL FIX: Create named listener functions for cleanup\n const listeners: ShellListeners = {\n stdout: (data: Buffer) => {\n const lines = data.toString().split('\\n')\n shell.stdout.push(...lines)\n\n // Keep only last MAX_OUTPUT_LINES\n if (shell.stdout.length > MAX_OUTPUT_LINES) {\n const removed = shell.stdout.length - MAX_OUTPUT_LINES\n shell.stdout = shell.stdout.slice(removed)\n shell.lastReadStdoutIndex = Math.max(\n 0,\n shell.lastReadStdoutIndex - removed,\n )\n }\n\n this.emit('output', shellId, 'stdout', lines)\n },\n stderr: (data: Buffer) => {\n const lines = data.toString().split('\\n')\n shell.stderr.push(...lines)\n\n // Keep only last MAX_OUTPUT_LINES\n if (shell.stderr.length > MAX_OUTPUT_LINES) {\n const removed = shell.stderr.length - MAX_OUTPUT_LINES\n shell.stderr = shell.stderr.slice(removed)\n shell.lastReadStderrIndex = Math.max(\n 0,\n shell.lastReadStderrIndex - removed,\n )\n }\n\n this.emit('output', shellId, 'stderr', lines)\n },\n exit: (code, signal) => {\n shell.status = signal ? 'killed' : 'done'\n shell.exitCode = code ?? undefined\n shell.endTime = Date.now()\n this.emit('statusChange', shellId, shell.status)\n this.emitListChange()\n // \uD83D\uDD27 Auto-cleanup listeners when process exits\n this.cleanupListeners(shell)\n },\n error: error => {\n logError(`Background shell ${shellId} error: ${error.message}`)\n shell.status = 'killed'\n shell.endTime = Date.now()\n this.emit('statusChange', shellId, shell.status)\n this.emitListChange()\n // \uD83D\uDD27 Auto-cleanup listeners when process errors\n this.cleanupListeners(shell)\n },\n }\n\n // Store listeners for later cleanup\n shell._listeners = listeners\n\n // Attach listeners\n if (listeners.stdout) childProcess.stdout?.on('data', listeners.stdout)\n if (listeners.stderr) childProcess.stderr?.on('data', listeners.stderr)\n if (listeners.exit) childProcess.on('exit', listeners.exit)\n if (listeners.error) childProcess.on('error', listeners.error)\n\n this.shells.set(shellId, shell)\n this.emit('shellCreated', shellId)\n this.emitListChange()\n\n return shellId\n }\n\n /**\n * Emit a list change event with the current shell list\n * Used for event-driven updates instead of polling\n */\n private emitListChange(): void {\n this.emit('listChange', this.list())\n }\n\n /**\n * \uD83D\uDD27 CRITICAL FIX: Clean up event listeners to prevent memory leaks\n * This removes all event listeners attached to the child process\n */\n private cleanupListeners(shell: BackgroundShell): void {\n if (!shell._listeners || !shell.process) {\n return\n }\n\n const { stdout, stderr, exit, error } = shell._listeners\n\n // Remove all listeners\n if (stdout) {\n shell.process.stdout?.off('data', stdout)\n }\n if (stderr) {\n shell.process.stderr?.off('data', stderr)\n }\n if (exit) {\n shell.process.off('exit', exit)\n }\n if (error) {\n shell.process.off('error', error)\n }\n\n // Clear the reference\n delete shell._listeners\n }\n\n /**\n * Get a background shell by ID\n */\n get(shellId: string): BackgroundShell | undefined {\n return this.shells.get(shellId)\n }\n\n /**\n * Get new output since last read\n */\n getNewOutput(\n shellId: string,\n filter?: string,\n ): {\n stdout: string[]\n stderr: string[]\n } | null {\n const shell = this.shells.get(shellId)\n if (!shell) return null\n\n let newStdout = shell.stdout.slice(shell.lastReadStdoutIndex)\n let newStderr = shell.stderr.slice(shell.lastReadStderrIndex)\n\n // Apply regex filter if provided (using cached regex)\n if (filter) {\n try {\n const regex = this.regexCache.getOrCreate(filter)\n newStdout = newStdout.filter(line => regex.test(line))\n newStderr = newStderr.filter(line => regex.test(line))\n } catch (e) {\n logError(`Invalid regex filter: ${filter}`)\n }\n }\n\n // Update read indices\n shell.lastReadStdoutIndex = shell.stdout.length\n shell.lastReadStderrIndex = shell.stderr.length\n\n return { stdout: newStdout, stderr: newStderr }\n }\n\n /**\n * Get all output (entire buffer)\n */\n getAllOutput(shellId: string): {\n stdout: string[]\n stderr: string[]\n } | null {\n const shell = this.shells.get(shellId)\n if (!shell) return null\n\n return {\n stdout: [...shell.stdout],\n stderr: [...shell.stderr],\n }\n }\n\n /**\n * Kill a background shell\n */\n kill(shellId: string): boolean {\n const shell = this.shells.get(shellId)\n if (!shell) {\n console.log(\n `[BackgroundShellManager] Cannot kill: shell ${shellId} not found`,\n )\n return false\n }\n\n if (shell.status === 'running') {\n try {\n const pid = shell.process.pid\n if (!pid) {\n console.log(\n `[BackgroundShellManager] Cannot kill: shell ${shellId} has no PID`,\n )\n return false\n }\n\n console.log(\n `[BackgroundShellManager] Killing shell ${shellId} (PID: ${pid}, command: ${shell.command})`,\n )\n\n // Kill the process and all its children\n if (process.platform === 'win32') {\n spawn('taskkill', ['/pid', pid.toString(), '/f', '/t'])\n } else {\n // Send SIGKILL to process group (more forceful than SIGTERM)\n // The negative PID sends signal to the entire process group\n try {\n process.kill(-pid, 'SIGKILL')\n console.log(\n `[BackgroundShellManager] Sent SIGKILL to process group -${pid}`,\n )\n } catch (e) {\n // If process group kill fails, try killing just the main process\n console.log(\n `[BackgroundShellManager] Process group kill failed, trying main process only`,\n )\n process.kill(pid, 'SIGKILL')\n }\n }\n\n shell.status = 'killed'\n shell.endTime = Date.now()\n // \uD83D\uDD27 CRITICAL FIX: Clean up listeners when killing shell\n this.cleanupListeners(shell)\n this.emit('statusChange', shellId, 'killed')\n this.emitListChange()\n return true\n } catch (error) {\n console.error(\n `[BackgroundShellManager] Failed to kill shell ${shellId}:`,\n error,\n )\n logError(`Failed to kill shell ${shellId}: ${error}`)\n // \uD83D\uDD27 Still try to clean up listeners even on error\n this.cleanupListeners(shell)\n return false\n }\n }\n\n console.log(\n `[BackgroundShellManager] Shell ${shellId} is not running (status: ${shell.status})`,\n )\n return false\n }\n\n /**\n * List all background shells\n */\n list(): BackgroundShell[] {\n return Array.from(this.shells.values())\n }\n\n /**\n * Get shell status\n */\n getStatus(shellId: string): BackgroundShellStatus | null {\n const shell = this.shells.get(shellId)\n return shell ? shell.status : null\n }\n\n /**\n * Remove a shell from the manager (only if done or killed)\n */\n remove(shellId: string): boolean {\n const shell = this.shells.get(shellId)\n if (!shell || shell.status === 'running') return false\n\n // \uD83D\uDD27 CRITICAL FIX: Clean up listeners before removing\n this.cleanupListeners(shell)\n this.shells.delete(shellId)\n this.emit('shellRemoved', shellId)\n this.emitListChange()\n return true\n }\n\n /**\n * Clean up old completed/killed shells\n */\n cleanup(maxAge: number = 3600000): number {\n const now = Date.now()\n let removed = 0\n\n for (const [shellId, shell] of this.shells.entries()) {\n if (\n shell.status !== 'running' &&\n shell.endTime &&\n now - shell.endTime > maxAge\n ) {\n // \uD83D\uDD27 CRITICAL FIX: Clean up listeners before deleting\n this.cleanupListeners(shell)\n this.shells.delete(shellId)\n removed++\n }\n }\n\n if (removed > 0) {\n this.emit('cleaned', removed)\n this.emitListChange()\n }\n\n return removed\n }\n\n /**\n * Get summary of shell for display\n */\n getSummary(shellId: string): string | null {\n const shell = this.shells.get(shellId)\n if (!shell) return null\n\n const duration = shell.endTime\n ? Math.floor((shell.endTime - shell.startTime) / 1000)\n : Math.floor((Date.now() - shell.startTime) / 1000)\n\n // Get last few lines of output for preview\n const lastLines = shell.stdout.slice(-2).join(' ').slice(0, 50)\n const preview = lastLines ? ` ${lastLines}\u2026` : ''\n\n return `${shell.command}${preview}`\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,242 @@
1
+ class CircuitOpenError extends Error {
2
+ constructor(remainingCooldown, serverName) {
3
+ const message = serverName ? `Circuit breaker for "${serverName}" is open. Retry in ${Math.ceil(remainingCooldown / 1e3)}s` : `Circuit breaker is open. Retry in ${Math.ceil(remainingCooldown / 1e3)}s`;
4
+ super(message);
5
+ this.remainingCooldown = remainingCooldown;
6
+ this.name = "CircuitOpenError";
7
+ }
8
+ }
9
+ class CircuitBreaker {
10
+ state = "closed";
11
+ failureCount = 0;
12
+ successCount = 0;
13
+ lastFailureTime = null;
14
+ config;
15
+ listeners = /* @__PURE__ */ new Set();
16
+ name;
17
+ totalRequests = 0;
18
+ totalFailures = 0;
19
+ constructor(name = "default", config) {
20
+ this.name = name;
21
+ this.config = {
22
+ failureThreshold: config?.failureThreshold ?? 5,
23
+ successThreshold: config?.successThreshold ?? 2,
24
+ openTimeout: config?.openTimeout ?? 3e4,
25
+ halfOpenTimeout: config?.halfOpenTimeout ?? 3e4
26
+ };
27
+ }
28
+ /**
29
+ * Execute an operation through the circuit breaker
30
+ * @throws {CircuitOpenError} If circuit is open
31
+ * @throws {Error} If operation fails
32
+ */
33
+ async execute(operation) {
34
+ this.totalRequests++;
35
+ if (this.state === "open") {
36
+ const cooldownRemaining = this.getRemainingCooldown();
37
+ if (cooldownRemaining <= 0) {
38
+ this.transition("half-open");
39
+ } else {
40
+ const error = new CircuitOpenError(cooldownRemaining, this.name);
41
+ this.notifyListeners("failure", { error });
42
+ throw error;
43
+ }
44
+ }
45
+ try {
46
+ const result = await operation();
47
+ this.onSuccess();
48
+ return result;
49
+ } catch (error) {
50
+ this.onFailure(error instanceof Error ? error : new Error(String(error)));
51
+ throw error;
52
+ }
53
+ }
54
+ /**
55
+ * Execute with timeout protection
56
+ * @throws {CircuitOpenError} If circuit is open
57
+ * @throws {Error} If operation times out or fails
58
+ */
59
+ async executeWithTimeout(operation, timeoutMs) {
60
+ const timeout = timeoutMs ?? this.config.halfOpenTimeout;
61
+ if (!timeout) {
62
+ return this.execute(operation);
63
+ }
64
+ return Promise.race([
65
+ this.execute(operation),
66
+ new Promise(
67
+ (_, reject) => setTimeout(
68
+ () => reject(new Error(`Operation timeout after ${timeout}ms`)),
69
+ timeout
70
+ )
71
+ )
72
+ ]);
73
+ }
74
+ /**
75
+ * Get current circuit state
76
+ */
77
+ getState() {
78
+ return this.state;
79
+ }
80
+ /**
81
+ * Get detailed statistics
82
+ */
83
+ getStats() {
84
+ return {
85
+ state: this.state,
86
+ failureCount: this.failureCount,
87
+ successCount: this.successCount,
88
+ lastFailureTime: this.lastFailureTime,
89
+ totalRequests: this.totalRequests,
90
+ totalFailures: this.totalFailures
91
+ };
92
+ }
93
+ /**
94
+ * Get remaining cooldown time in milliseconds
95
+ */
96
+ getRemainingCooldown() {
97
+ if (!this.lastFailureTime) {
98
+ return 0;
99
+ }
100
+ return Math.max(
101
+ 0,
102
+ this.config.openTimeout - (Date.now() - this.lastFailureTime)
103
+ );
104
+ }
105
+ /**
106
+ * Register a listener for state changes and events
107
+ */
108
+ on(listener) {
109
+ this.listeners.add(listener);
110
+ return () => {
111
+ this.listeners.delete(listener);
112
+ };
113
+ }
114
+ /**
115
+ * Reset the circuit breaker to CLOSED state
116
+ */
117
+ reset() {
118
+ const previousState = this.state;
119
+ this.state = "closed";
120
+ this.failureCount = 0;
121
+ this.successCount = 0;
122
+ this.lastFailureTime = null;
123
+ this.totalRequests = 0;
124
+ this.totalFailures = 0;
125
+ this.notifyListeners("state-change", {
126
+ state: this.state,
127
+ previousState
128
+ });
129
+ }
130
+ /**
131
+ * Manually open the circuit (for testing or explicit fault injection)
132
+ */
133
+ forceOpen() {
134
+ this.transition("open");
135
+ }
136
+ /**
137
+ * Manually close the circuit (for recovery testing)
138
+ */
139
+ forceClosed() {
140
+ this.transition("closed");
141
+ }
142
+ /**
143
+ * Manually transition to half-open state (for testing)
144
+ */
145
+ forceHalfOpen() {
146
+ this.transition("half-open");
147
+ }
148
+ // Private methods
149
+ transition(newState) {
150
+ if (newState === this.state) {
151
+ return;
152
+ }
153
+ const previousState = this.state;
154
+ this.state = newState;
155
+ if (newState === "closed") {
156
+ this.failureCount = 0;
157
+ this.successCount = 0;
158
+ } else if (newState === "half-open") {
159
+ this.successCount = 0;
160
+ this.failureCount = 0;
161
+ }
162
+ this.notifyListeners("state-change", {
163
+ state: newState,
164
+ previousState
165
+ });
166
+ }
167
+ onSuccess() {
168
+ if (this.state === "half-open") {
169
+ this.successCount++;
170
+ if (this.successCount >= this.config.successThreshold) {
171
+ this.transition("closed");
172
+ }
173
+ } else if (this.state === "closed") {
174
+ this.failureCount = 0;
175
+ }
176
+ this.notifyListeners("success", {});
177
+ }
178
+ onFailure(error) {
179
+ this.failureCount++;
180
+ this.totalFailures++;
181
+ this.lastFailureTime = Date.now();
182
+ if (this.state === "closed") {
183
+ if (this.failureCount >= this.config.failureThreshold) {
184
+ this.transition("open");
185
+ }
186
+ } else if (this.state === "half-open") {
187
+ this.transition("open");
188
+ }
189
+ this.notifyListeners("failure", { error });
190
+ }
191
+ notifyListeners(event, details) {
192
+ for (const listener of this.listeners) {
193
+ try {
194
+ listener(event, details);
195
+ } catch {
196
+ }
197
+ }
198
+ }
199
+ }
200
+ class CircuitBreakerRegistry {
201
+ breakers = /* @__PURE__ */ new Map();
202
+ defaultConfig;
203
+ constructor(defaultConfig) {
204
+ this.defaultConfig = defaultConfig;
205
+ }
206
+ /**
207
+ * Get or create a circuit breaker by name
208
+ */
209
+ getOrCreate(name, config) {
210
+ if (!this.breakers.has(name)) {
211
+ const mergedConfig = { ...this.defaultConfig, ...config };
212
+ this.breakers.set(name, new CircuitBreaker(name, mergedConfig));
213
+ }
214
+ return this.breakers.get(name);
215
+ }
216
+ /**
217
+ * Get all registered circuit breakers
218
+ */
219
+ getAll() {
220
+ return new Map(this.breakers);
221
+ }
222
+ /**
223
+ * Reset all circuit breakers
224
+ */
225
+ resetAll() {
226
+ for (const breaker of this.breakers.values()) {
227
+ breaker.reset();
228
+ }
229
+ }
230
+ /**
231
+ * Clear all circuit breakers
232
+ */
233
+ clear() {
234
+ this.breakers.clear();
235
+ }
236
+ }
237
+ export {
238
+ CircuitBreaker,
239
+ CircuitBreakerRegistry,
240
+ CircuitOpenError
241
+ };
242
+ //# sourceMappingURL=CircuitBreaker.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/CircuitBreaker.ts"],
4
+ "sourcesContent": ["/**\n * Circuit Breaker pattern implementation for handling failing services gracefully\n *\n * States:\n * - CLOSED: Service is healthy, requests pass through normally\n * - OPEN: Service is unhealthy, requests fail immediately\n * - HALF_OPEN: Testing if service has recovered, limited requests allowed\n *\n * Transitions:\n * - CLOSED \u2192 OPEN: When failure count exceeds threshold\n * - OPEN \u2192 HALF_OPEN: After cooldown timeout\n * - HALF_OPEN \u2192 CLOSED: When success count exceeds threshold\n * - HALF_OPEN \u2192 OPEN: When a request fails\n */\n\nexport interface CircuitBreakerConfig {\n /** Number of failures that trigger opening the circuit (default: 5) */\n failureThreshold: number\n /** Number of successful requests needed to close circuit from HALF_OPEN (default: 2) */\n successThreshold: number\n /** Time in ms before transitioning from OPEN to HALF_OPEN (default: 30000) */\n openTimeout: number\n /** Optional timeout in ms for operations (default: none) */\n halfOpenTimeout?: number\n}\n\nexport class CircuitOpenError extends Error {\n constructor(\n public readonly remainingCooldown: number,\n serverName?: string,\n ) {\n const message = serverName\n ? `Circuit breaker for \"${serverName}\" is open. Retry in ${Math.ceil(remainingCooldown / 1000)}s`\n : `Circuit breaker is open. Retry in ${Math.ceil(remainingCooldown / 1000)}s`\n super(message)\n this.name = 'CircuitOpenError'\n }\n}\n\nexport type CircuitBreakerState = 'closed' | 'open' | 'half-open'\n\nexport interface CircuitBreakerStats {\n state: CircuitBreakerState\n failureCount: number\n successCount: number\n lastFailureTime: number | null\n totalRequests: number\n totalFailures: number\n}\n\nexport type CircuitBreakerListener = (\n event: 'state-change' | 'failure' | 'success',\n details: {\n state?: CircuitBreakerState\n previousState?: CircuitBreakerState\n error?: Error\n },\n) => void\n\n/**\n * Circuit Breaker implementation for fault tolerance\n *\n * Prevents cascading failures by failing fast when a service is unhealthy,\n * while automatically attempting recovery when the service potentially recovers.\n */\nexport class CircuitBreaker<T = unknown> {\n private state: CircuitBreakerState = 'closed'\n private failureCount: number = 0\n private successCount: number = 0\n private lastFailureTime: number | null = null\n private readonly config: Required<CircuitBreakerConfig>\n private readonly listeners: Set<CircuitBreakerListener> = new Set()\n private readonly name: string\n private totalRequests: number = 0\n private totalFailures: number = 0\n\n constructor(\n name: string = 'default',\n config?: Partial<CircuitBreakerConfig>,\n ) {\n this.name = name\n this.config = {\n failureThreshold: config?.failureThreshold ?? 5,\n successThreshold: config?.successThreshold ?? 2,\n openTimeout: config?.openTimeout ?? 30000,\n halfOpenTimeout: config?.halfOpenTimeout ?? 30000,\n }\n }\n\n /**\n * Execute an operation through the circuit breaker\n * @throws {CircuitOpenError} If circuit is open\n * @throws {Error} If operation fails\n */\n async execute<R>(operation: () => Promise<R>): Promise<R> {\n this.totalRequests++\n\n // Check if we need to transition from OPEN to HALF_OPEN\n if (this.state === 'open') {\n const cooldownRemaining = this.getRemainingCooldown()\n if (cooldownRemaining <= 0) {\n this.transition('half-open')\n } else {\n const error = new CircuitOpenError(cooldownRemaining, this.name)\n this.notifyListeners('failure', { error })\n throw error\n }\n }\n\n // Execute the operation\n try {\n const result = await operation()\n this.onSuccess()\n return result\n } catch (error) {\n this.onFailure(error instanceof Error ? error : new Error(String(error)))\n throw error\n }\n }\n\n /**\n * Execute with timeout protection\n * @throws {CircuitOpenError} If circuit is open\n * @throws {Error} If operation times out or fails\n */\n async executeWithTimeout<R>(\n operation: () => Promise<R>,\n timeoutMs?: number,\n ): Promise<R> {\n const timeout = timeoutMs ?? this.config.halfOpenTimeout\n if (!timeout) {\n return this.execute(operation)\n }\n\n return Promise.race([\n this.execute(operation),\n new Promise<R>((_, reject) =>\n setTimeout(\n () => reject(new Error(`Operation timeout after ${timeout}ms`)),\n timeout,\n ),\n ),\n ])\n }\n\n /**\n * Get current circuit state\n */\n getState(): CircuitBreakerState {\n return this.state\n }\n\n /**\n * Get detailed statistics\n */\n getStats(): CircuitBreakerStats {\n return {\n state: this.state,\n failureCount: this.failureCount,\n successCount: this.successCount,\n lastFailureTime: this.lastFailureTime,\n totalRequests: this.totalRequests,\n totalFailures: this.totalFailures,\n }\n }\n\n /**\n * Get remaining cooldown time in milliseconds\n */\n getRemainingCooldown(): number {\n if (!this.lastFailureTime) {\n return 0\n }\n return Math.max(\n 0,\n this.config.openTimeout - (Date.now() - this.lastFailureTime),\n )\n }\n\n /**\n * Register a listener for state changes and events\n */\n on(listener: CircuitBreakerListener): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n /**\n * Reset the circuit breaker to CLOSED state\n */\n reset(): void {\n const previousState = this.state\n this.state = 'closed'\n this.failureCount = 0\n this.successCount = 0\n this.lastFailureTime = null\n this.totalRequests = 0\n this.totalFailures = 0\n\n this.notifyListeners('state-change', {\n state: this.state,\n previousState,\n })\n }\n\n /**\n * Manually open the circuit (for testing or explicit fault injection)\n */\n forceOpen(): void {\n this.transition('open')\n }\n\n /**\n * Manually close the circuit (for recovery testing)\n */\n forceClosed(): void {\n this.transition('closed')\n }\n\n /**\n * Manually transition to half-open state (for testing)\n */\n forceHalfOpen(): void {\n this.transition('half-open')\n }\n\n // Private methods\n\n private transition(newState: CircuitBreakerState): void {\n if (newState === this.state) {\n return\n }\n\n const previousState = this.state\n this.state = newState\n\n // Reset counters on state transition\n if (newState === 'closed') {\n this.failureCount = 0\n this.successCount = 0\n } else if (newState === 'half-open') {\n this.successCount = 0\n this.failureCount = 0\n }\n\n this.notifyListeners('state-change', {\n state: newState,\n previousState,\n })\n }\n\n private onSuccess(): void {\n if (this.state === 'half-open') {\n this.successCount++\n if (this.successCount >= this.config.successThreshold) {\n this.transition('closed')\n }\n } else if (this.state === 'closed') {\n this.failureCount = 0\n }\n\n this.notifyListeners('success', {})\n }\n\n private onFailure(error: Error): void {\n this.failureCount++\n this.totalFailures++\n this.lastFailureTime = Date.now()\n\n if (this.state === 'closed') {\n if (this.failureCount >= this.config.failureThreshold) {\n this.transition('open')\n }\n } else if (this.state === 'half-open') {\n // Any failure in half-open state goes back to open\n this.transition('open')\n }\n\n this.notifyListeners('failure', { error })\n }\n\n private notifyListeners(\n event: 'state-change' | 'failure' | 'success',\n details: {\n state?: CircuitBreakerState\n previousState?: CircuitBreakerState\n error?: Error\n },\n ): void {\n for (const listener of this.listeners) {\n try {\n listener(event, details)\n } catch {\n // Ignore listener errors to prevent them from affecting the circuit\n }\n }\n }\n}\n\n/**\n * Create a simple factory for managing multiple circuit breakers\n */\nexport class CircuitBreakerRegistry {\n private readonly breakers = new Map<string, CircuitBreaker>()\n private readonly defaultConfig?: Partial<CircuitBreakerConfig>\n\n constructor(defaultConfig?: Partial<CircuitBreakerConfig>) {\n this.defaultConfig = defaultConfig\n }\n\n /**\n * Get or create a circuit breaker by name\n */\n getOrCreate(\n name: string,\n config?: Partial<CircuitBreakerConfig>,\n ): CircuitBreaker {\n if (!this.breakers.has(name)) {\n const mergedConfig = { ...this.defaultConfig, ...config }\n this.breakers.set(name, new CircuitBreaker(name, mergedConfig))\n }\n return this.breakers.get(name)!\n }\n\n /**\n * Get all registered circuit breakers\n */\n getAll(): Map<string, CircuitBreaker> {\n return new Map(this.breakers)\n }\n\n /**\n * Reset all circuit breakers\n */\n resetAll(): void {\n for (const breaker of this.breakers.values()) {\n breaker.reset()\n }\n }\n\n /**\n * Clear all circuit breakers\n */\n clear(): void {\n this.breakers.clear()\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }