snow-ai 0.4.16 → 0.4.17

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 (351) hide show
  1. package/bundle/cli.mjs +477445 -0
  2. package/bundle/sql-wasm.wasm +0 -0
  3. package/package.json +31 -26
  4. package/dist/agents/codebaseIndexAgent.d.ts +0 -102
  5. package/dist/agents/codebaseIndexAgent.js +0 -641
  6. package/dist/agents/codebaseReviewAgent.d.ts +0 -61
  7. package/dist/agents/codebaseReviewAgent.js +0 -301
  8. package/dist/agents/compactAgent.d.ts +0 -55
  9. package/dist/agents/compactAgent.js +0 -306
  10. package/dist/agents/promptOptimizeAgent.d.ts +0 -54
  11. package/dist/agents/promptOptimizeAgent.js +0 -268
  12. package/dist/agents/reviewAgent.d.ts +0 -50
  13. package/dist/agents/reviewAgent.js +0 -265
  14. package/dist/agents/summaryAgent.d.ts +0 -57
  15. package/dist/agents/summaryAgent.js +0 -260
  16. package/dist/api/anthropic.d.ts +0 -44
  17. package/dist/api/anthropic.js +0 -598
  18. package/dist/api/chat.d.ts +0 -73
  19. package/dist/api/chat.js +0 -386
  20. package/dist/api/embedding.d.ts +0 -34
  21. package/dist/api/embedding.js +0 -80
  22. package/dist/api/gemini.d.ts +0 -31
  23. package/dist/api/gemini.js +0 -445
  24. package/dist/api/models.d.ts +0 -15
  25. package/dist/api/models.js +0 -139
  26. package/dist/api/responses.d.ts +0 -38
  27. package/dist/api/responses.js +0 -515
  28. package/dist/api/systemPrompt.d.ts +0 -4
  29. package/dist/api/systemPrompt.js +0 -408
  30. package/dist/api/types.d.ts +0 -53
  31. package/dist/api/types.js +0 -4
  32. package/dist/app.d.ts +0 -8
  33. package/dist/app.js +0 -112
  34. package/dist/cli.d.ts +0 -2
  35. package/dist/cli.js +0 -199
  36. package/dist/hooks/useAgentPicker.d.ts +0 -14
  37. package/dist/hooks/useAgentPicker.js +0 -119
  38. package/dist/hooks/useClipboard.d.ts +0 -4
  39. package/dist/hooks/useClipboard.js +0 -175
  40. package/dist/hooks/useCommandHandler.d.ts +0 -35
  41. package/dist/hooks/useCommandHandler.js +0 -346
  42. package/dist/hooks/useCommandPanel.d.ts +0 -17
  43. package/dist/hooks/useCommandPanel.js +0 -114
  44. package/dist/hooks/useConversation.d.ts +0 -49
  45. package/dist/hooks/useConversation.js +0 -1052
  46. package/dist/hooks/useFilePicker.d.ts +0 -18
  47. package/dist/hooks/useFilePicker.js +0 -224
  48. package/dist/hooks/useGlobalExit.d.ts +0 -5
  49. package/dist/hooks/useGlobalExit.js +0 -34
  50. package/dist/hooks/useGlobalNavigation.d.ts +0 -6
  51. package/dist/hooks/useGlobalNavigation.js +0 -17
  52. package/dist/hooks/useHistoryNavigation.d.ts +0 -35
  53. package/dist/hooks/useHistoryNavigation.js +0 -133
  54. package/dist/hooks/useInputBuffer.d.ts +0 -6
  55. package/dist/hooks/useInputBuffer.js +0 -45
  56. package/dist/hooks/useKeyboardInput.d.ts +0 -80
  57. package/dist/hooks/useKeyboardInput.js +0 -608
  58. package/dist/hooks/useSessionManagement.d.ts +0 -10
  59. package/dist/hooks/useSessionManagement.js +0 -43
  60. package/dist/hooks/useSessionSave.d.ts +0 -8
  61. package/dist/hooks/useSessionSave.js +0 -63
  62. package/dist/hooks/useSnapshotState.d.ts +0 -26
  63. package/dist/hooks/useSnapshotState.js +0 -28
  64. package/dist/hooks/useStreamingState.d.ts +0 -33
  65. package/dist/hooks/useStreamingState.js +0 -105
  66. package/dist/hooks/useTerminalFocus.d.ts +0 -28
  67. package/dist/hooks/useTerminalFocus.js +0 -87
  68. package/dist/hooks/useTerminalSize.d.ts +0 -4
  69. package/dist/hooks/useTerminalSize.js +0 -20
  70. package/dist/hooks/useTodoPicker.d.ts +0 -16
  71. package/dist/hooks/useTodoPicker.js +0 -94
  72. package/dist/hooks/useToolConfirmation.d.ts +0 -19
  73. package/dist/hooks/useToolConfirmation.js +0 -61
  74. package/dist/hooks/useVSCodeState.d.ts +0 -8
  75. package/dist/hooks/useVSCodeState.js +0 -81
  76. package/dist/i18n/I18nContext.d.ts +0 -14
  77. package/dist/i18n/I18nContext.js +0 -24
  78. package/dist/i18n/index.d.ts +0 -3
  79. package/dist/i18n/index.js +0 -2
  80. package/dist/i18n/lang/en.d.ts +0 -2
  81. package/dist/i18n/lang/en.js +0 -502
  82. package/dist/i18n/lang/es.d.ts +0 -2
  83. package/dist/i18n/lang/es.js +0 -502
  84. package/dist/i18n/lang/ja.d.ts +0 -2
  85. package/dist/i18n/lang/ja.js +0 -502
  86. package/dist/i18n/lang/ko.d.ts +0 -2
  87. package/dist/i18n/lang/ko.js +0 -502
  88. package/dist/i18n/lang/zh-TW.d.ts +0 -2
  89. package/dist/i18n/lang/zh-TW.js +0 -502
  90. package/dist/i18n/lang/zh.d.ts +0 -2
  91. package/dist/i18n/lang/zh.js +0 -502
  92. package/dist/i18n/translations.d.ts +0 -2
  93. package/dist/i18n/translations.js +0 -14
  94. package/dist/i18n/types.d.ts +0 -478
  95. package/dist/i18n/types.js +0 -1
  96. package/dist/mcp/aceCodeSearch.d.ts +0 -247
  97. package/dist/mcp/aceCodeSearch.js +0 -1058
  98. package/dist/mcp/bash.d.ts +0 -50
  99. package/dist/mcp/bash.js +0 -153
  100. package/dist/mcp/codebaseSearch.d.ts +0 -44
  101. package/dist/mcp/codebaseSearch.js +0 -275
  102. package/dist/mcp/filesystem.d.ts +0 -392
  103. package/dist/mcp/filesystem.js +0 -1445
  104. package/dist/mcp/ideDiagnostics.d.ts +0 -36
  105. package/dist/mcp/ideDiagnostics.js +0 -90
  106. package/dist/mcp/notebook.d.ts +0 -10
  107. package/dist/mcp/notebook.js +0 -367
  108. package/dist/mcp/subagent.d.ts +0 -37
  109. package/dist/mcp/subagent.js +0 -113
  110. package/dist/mcp/todo.d.ts +0 -46
  111. package/dist/mcp/todo.js +0 -511
  112. package/dist/mcp/types/aceCodeSearch.types.d.ts +0 -92
  113. package/dist/mcp/types/aceCodeSearch.types.js +0 -4
  114. package/dist/mcp/types/bash.types.d.ts +0 -13
  115. package/dist/mcp/types/bash.types.js +0 -4
  116. package/dist/mcp/types/filesystem.types.d.ts +0 -210
  117. package/dist/mcp/types/filesystem.types.js +0 -27
  118. package/dist/mcp/types/todo.types.d.ts +0 -27
  119. package/dist/mcp/types/todo.types.js +0 -4
  120. package/dist/mcp/types/websearch.types.d.ts +0 -30
  121. package/dist/mcp/types/websearch.types.js +0 -4
  122. package/dist/mcp/utils/aceCodeSearch/filesystem.utils.d.ts +0 -34
  123. package/dist/mcp/utils/aceCodeSearch/filesystem.utils.js +0 -146
  124. package/dist/mcp/utils/aceCodeSearch/language.utils.d.ts +0 -14
  125. package/dist/mcp/utils/aceCodeSearch/language.utils.js +0 -418
  126. package/dist/mcp/utils/aceCodeSearch/search.utils.d.ts +0 -31
  127. package/dist/mcp/utils/aceCodeSearch/search.utils.js +0 -136
  128. package/dist/mcp/utils/aceCodeSearch/symbol.utils.d.ts +0 -20
  129. package/dist/mcp/utils/aceCodeSearch/symbol.utils.js +0 -141
  130. package/dist/mcp/utils/bash/security.utils.d.ts +0 -20
  131. package/dist/mcp/utils/bash/security.utils.js +0 -34
  132. package/dist/mcp/utils/filesystem/batch-operations.utils.d.ts +0 -39
  133. package/dist/mcp/utils/filesystem/batch-operations.utils.js +0 -182
  134. package/dist/mcp/utils/filesystem/code-analysis.utils.d.ts +0 -18
  135. package/dist/mcp/utils/filesystem/code-analysis.utils.js +0 -165
  136. package/dist/mcp/utils/filesystem/match-finder.utils.d.ts +0 -16
  137. package/dist/mcp/utils/filesystem/match-finder.utils.js +0 -85
  138. package/dist/mcp/utils/filesystem/office-parser.utils.d.ts +0 -43
  139. package/dist/mcp/utils/filesystem/office-parser.utils.js +0 -163
  140. package/dist/mcp/utils/filesystem/path-fixer.utils.d.ts +0 -7
  141. package/dist/mcp/utils/filesystem/path-fixer.utils.js +0 -60
  142. package/dist/mcp/utils/filesystem/similarity.utils.d.ts +0 -22
  143. package/dist/mcp/utils/filesystem/similarity.utils.js +0 -75
  144. package/dist/mcp/utils/todo/date.utils.d.ts +0 -9
  145. package/dist/mcp/utils/todo/date.utils.js +0 -14
  146. package/dist/mcp/utils/websearch/browser.utils.d.ts +0 -8
  147. package/dist/mcp/utils/websearch/browser.utils.js +0 -58
  148. package/dist/mcp/utils/websearch/text.utils.d.ts +0 -16
  149. package/dist/mcp/utils/websearch/text.utils.js +0 -39
  150. package/dist/mcp/websearch.d.ts +0 -88
  151. package/dist/mcp/websearch.js +0 -375
  152. package/dist/test/logger-test.d.ts +0 -1
  153. package/dist/test/logger-test.js +0 -7
  154. package/dist/types/index.d.ts +0 -15
  155. package/dist/types/index.js +0 -1
  156. package/dist/ui/components/AgentPickerPanel.d.ts +0 -10
  157. package/dist/ui/components/AgentPickerPanel.js +0 -74
  158. package/dist/ui/components/ChatInput.d.ts +0 -46
  159. package/dist/ui/components/ChatInput.js +0 -384
  160. package/dist/ui/components/CommandPanel.d.ts +0 -15
  161. package/dist/ui/components/CommandPanel.js +0 -80
  162. package/dist/ui/components/DiffViewer.d.ts +0 -11
  163. package/dist/ui/components/DiffViewer.js +0 -178
  164. package/dist/ui/components/FileList.d.ts +0 -15
  165. package/dist/ui/components/FileList.js +0 -360
  166. package/dist/ui/components/FileRollbackConfirmation.d.ts +0 -8
  167. package/dist/ui/components/FileRollbackConfirmation.js +0 -108
  168. package/dist/ui/components/HelpPanel.d.ts +0 -2
  169. package/dist/ui/components/HelpPanel.js +0 -67
  170. package/dist/ui/components/MCPInfoPanel.d.ts +0 -2
  171. package/dist/ui/components/MCPInfoPanel.js +0 -108
  172. package/dist/ui/components/MCPInfoScreen.d.ts +0 -7
  173. package/dist/ui/components/MCPInfoScreen.js +0 -115
  174. package/dist/ui/components/MarkdownRenderer.d.ts +0 -6
  175. package/dist/ui/components/MarkdownRenderer.js +0 -70
  176. package/dist/ui/components/Menu.d.ts +0 -17
  177. package/dist/ui/components/Menu.js +0 -88
  178. package/dist/ui/components/MessageList.d.ts +0 -56
  179. package/dist/ui/components/MessageList.js +0 -97
  180. package/dist/ui/components/PendingMessages.d.ts +0 -13
  181. package/dist/ui/components/PendingMessages.js +0 -29
  182. package/dist/ui/components/PendingToolCalls.d.ts +0 -11
  183. package/dist/ui/components/PendingToolCalls.js +0 -35
  184. package/dist/ui/components/ScrollableSelectInput.d.ts +0 -29
  185. package/dist/ui/components/ScrollableSelectInput.js +0 -157
  186. package/dist/ui/components/SessionListPanel.d.ts +0 -7
  187. package/dist/ui/components/SessionListPanel.js +0 -175
  188. package/dist/ui/components/SessionListScreen.d.ts +0 -7
  189. package/dist/ui/components/SessionListScreen.js +0 -217
  190. package/dist/ui/components/SessionListScreenWrapper.d.ts +0 -7
  191. package/dist/ui/components/SessionListScreenWrapper.js +0 -14
  192. package/dist/ui/components/ShimmerText.d.ts +0 -9
  193. package/dist/ui/components/ShimmerText.js +0 -30
  194. package/dist/ui/components/TodoPickerPanel.d.ts +0 -14
  195. package/dist/ui/components/TodoPickerPanel.js +0 -119
  196. package/dist/ui/components/TodoTree.d.ts +0 -15
  197. package/dist/ui/components/TodoTree.js +0 -60
  198. package/dist/ui/components/ToolConfirmation.d.ts +0 -21
  199. package/dist/ui/components/ToolConfirmation.js +0 -204
  200. package/dist/ui/components/ToolResultPreview.d.ts +0 -13
  201. package/dist/ui/components/ToolResultPreview.js +0 -337
  202. package/dist/ui/components/UsagePanel.d.ts +0 -2
  203. package/dist/ui/components/UsagePanel.js +0 -394
  204. package/dist/ui/contexts/ThemeContext.d.ts +0 -13
  205. package/dist/ui/contexts/ThemeContext.js +0 -28
  206. package/dist/ui/pages/ChatScreen.d.ts +0 -6
  207. package/dist/ui/pages/ChatScreen.js +0 -1519
  208. package/dist/ui/pages/CodeBaseConfigScreen.d.ts +0 -8
  209. package/dist/ui/pages/CodeBaseConfigScreen.js +0 -350
  210. package/dist/ui/pages/ConfigScreen.d.ts +0 -8
  211. package/dist/ui/pages/ConfigScreen.js +0 -1101
  212. package/dist/ui/pages/CustomHeadersScreen.d.ts +0 -6
  213. package/dist/ui/pages/CustomHeadersScreen.js +0 -502
  214. package/dist/ui/pages/HeadlessModeScreen.d.ts +0 -7
  215. package/dist/ui/pages/HeadlessModeScreen.js +0 -381
  216. package/dist/ui/pages/LanguageSettingsScreen.d.ts +0 -7
  217. package/dist/ui/pages/LanguageSettingsScreen.js +0 -91
  218. package/dist/ui/pages/MCPConfigScreen.d.ts +0 -6
  219. package/dist/ui/pages/MCPConfigScreen.js +0 -55
  220. package/dist/ui/pages/ProxyConfigScreen.d.ts +0 -8
  221. package/dist/ui/pages/ProxyConfigScreen.js +0 -149
  222. package/dist/ui/pages/SensitiveCommandConfigScreen.d.ts +0 -7
  223. package/dist/ui/pages/SensitiveCommandConfigScreen.js +0 -271
  224. package/dist/ui/pages/SubAgentConfigScreen.d.ts +0 -9
  225. package/dist/ui/pages/SubAgentConfigScreen.js +0 -435
  226. package/dist/ui/pages/SubAgentListScreen.d.ts +0 -9
  227. package/dist/ui/pages/SubAgentListScreen.js +0 -131
  228. package/dist/ui/pages/SystemPromptConfigScreen.d.ts +0 -6
  229. package/dist/ui/pages/SystemPromptConfigScreen.js +0 -326
  230. package/dist/ui/pages/ThemeSettingsScreen.d.ts +0 -7
  231. package/dist/ui/pages/ThemeSettingsScreen.js +0 -106
  232. package/dist/ui/pages/WelcomeScreen.d.ts +0 -7
  233. package/dist/ui/pages/WelcomeScreen.js +0 -217
  234. package/dist/ui/themes/index.d.ts +0 -23
  235. package/dist/ui/themes/index.js +0 -140
  236. package/dist/utils/apiConfig.d.ts +0 -126
  237. package/dist/utils/apiConfig.js +0 -423
  238. package/dist/utils/autoCompress.d.ts +0 -15
  239. package/dist/utils/autoCompress.js +0 -24
  240. package/dist/utils/chatExporter.d.ts +0 -9
  241. package/dist/utils/chatExporter.js +0 -118
  242. package/dist/utils/checkpointManager.d.ts +0 -74
  243. package/dist/utils/checkpointManager.js +0 -181
  244. package/dist/utils/codebaseConfig.d.ts +0 -16
  245. package/dist/utils/codebaseConfig.js +0 -67
  246. package/dist/utils/codebaseDatabase.d.ts +0 -102
  247. package/dist/utils/codebaseDatabase.js +0 -333
  248. package/dist/utils/codebaseSearchEvents.d.ts +0 -16
  249. package/dist/utils/codebaseSearchEvents.js +0 -13
  250. package/dist/utils/commandExecutor.d.ts +0 -13
  251. package/dist/utils/commandExecutor.js +0 -26
  252. package/dist/utils/commands/agent.d.ts +0 -2
  253. package/dist/utils/commands/agent.js +0 -12
  254. package/dist/utils/commands/clear.d.ts +0 -2
  255. package/dist/utils/commands/clear.js +0 -12
  256. package/dist/utils/commands/compact.d.ts +0 -2
  257. package/dist/utils/commands/compact.js +0 -12
  258. package/dist/utils/commands/export.d.ts +0 -2
  259. package/dist/utils/commands/export.js +0 -12
  260. package/dist/utils/commands/help.d.ts +0 -2
  261. package/dist/utils/commands/help.js +0 -11
  262. package/dist/utils/commands/home.d.ts +0 -2
  263. package/dist/utils/commands/home.js +0 -34
  264. package/dist/utils/commands/ide.d.ts +0 -2
  265. package/dist/utils/commands/ide.js +0 -32
  266. package/dist/utils/commands/init.d.ts +0 -2
  267. package/dist/utils/commands/init.js +0 -93
  268. package/dist/utils/commands/mcp.d.ts +0 -2
  269. package/dist/utils/commands/mcp.js +0 -12
  270. package/dist/utils/commands/resume.d.ts +0 -2
  271. package/dist/utils/commands/resume.js +0 -12
  272. package/dist/utils/commands/review.d.ts +0 -2
  273. package/dist/utils/commands/review.js +0 -81
  274. package/dist/utils/commands/role.d.ts +0 -2
  275. package/dist/utils/commands/role.js +0 -37
  276. package/dist/utils/commands/todoPicker.d.ts +0 -2
  277. package/dist/utils/commands/todoPicker.js +0 -12
  278. package/dist/utils/commands/usage.d.ts +0 -2
  279. package/dist/utils/commands/usage.js +0 -12
  280. package/dist/utils/commands/yolo.d.ts +0 -2
  281. package/dist/utils/commands/yolo.js +0 -12
  282. package/dist/utils/configManager.d.ts +0 -45
  283. package/dist/utils/configManager.js +0 -303
  284. package/dist/utils/contextCompressor.d.ts +0 -16
  285. package/dist/utils/contextCompressor.js +0 -334
  286. package/dist/utils/devMode.d.ts +0 -13
  287. package/dist/utils/devMode.js +0 -54
  288. package/dist/utils/escapeHandler.d.ts +0 -79
  289. package/dist/utils/escapeHandler.js +0 -153
  290. package/dist/utils/fileDialog.d.ts +0 -9
  291. package/dist/utils/fileDialog.js +0 -74
  292. package/dist/utils/fileUtils.d.ts +0 -40
  293. package/dist/utils/fileUtils.js +0 -185
  294. package/dist/utils/historyManager.d.ts +0 -45
  295. package/dist/utils/historyManager.js +0 -159
  296. package/dist/utils/incrementalSnapshot.d.ts +0 -109
  297. package/dist/utils/incrementalSnapshot.js +0 -383
  298. package/dist/utils/index.d.ts +0 -11
  299. package/dist/utils/index.js +0 -18
  300. package/dist/utils/languageConfig.d.ts +0 -21
  301. package/dist/utils/languageConfig.js +0 -61
  302. package/dist/utils/logger.d.ts +0 -37
  303. package/dist/utils/logger.js +0 -122
  304. package/dist/utils/mcpToolsManager.d.ts +0 -52
  305. package/dist/utils/mcpToolsManager.js +0 -878
  306. package/dist/utils/messageFormatter.d.ts +0 -12
  307. package/dist/utils/messageFormatter.js +0 -115
  308. package/dist/utils/notebookManager.d.ts +0 -59
  309. package/dist/utils/notebookManager.js +0 -213
  310. package/dist/utils/patch-highlight.d.ts +0 -5
  311. package/dist/utils/patch-highlight.js +0 -23
  312. package/dist/utils/processManager.d.ts +0 -27
  313. package/dist/utils/processManager.js +0 -75
  314. package/dist/utils/proxyUtils.d.ts +0 -15
  315. package/dist/utils/proxyUtils.js +0 -50
  316. package/dist/utils/resourceMonitor.d.ts +0 -65
  317. package/dist/utils/resourceMonitor.js +0 -175
  318. package/dist/utils/retryUtils.d.ts +0 -49
  319. package/dist/utils/retryUtils.js +0 -303
  320. package/dist/utils/sensitiveCommandManager.d.ts +0 -53
  321. package/dist/utils/sensitiveCommandManager.js +0 -308
  322. package/dist/utils/sessionConverter.d.ts +0 -7
  323. package/dist/utils/sessionConverter.js +0 -306
  324. package/dist/utils/sessionManager.d.ts +0 -53
  325. package/dist/utils/sessionManager.js +0 -371
  326. package/dist/utils/subAgentConfig.d.ts +0 -50
  327. package/dist/utils/subAgentConfig.js +0 -221
  328. package/dist/utils/subAgentExecutor.d.ts +0 -40
  329. package/dist/utils/subAgentExecutor.js +0 -434
  330. package/dist/utils/terminal.d.ts +0 -5
  331. package/dist/utils/terminal.js +0 -13
  332. package/dist/utils/textBuffer.d.ts +0 -99
  333. package/dist/utils/textBuffer.js +0 -547
  334. package/dist/utils/textUtils.d.ts +0 -37
  335. package/dist/utils/textUtils.js +0 -102
  336. package/dist/utils/themeConfig.d.ts +0 -21
  337. package/dist/utils/themeConfig.js +0 -61
  338. package/dist/utils/todoPreprocessor.d.ts +0 -5
  339. package/dist/utils/todoPreprocessor.js +0 -18
  340. package/dist/utils/todoScanner.d.ts +0 -8
  341. package/dist/utils/todoScanner.js +0 -148
  342. package/dist/utils/toolDisplayConfig.d.ts +0 -16
  343. package/dist/utils/toolDisplayConfig.js +0 -47
  344. package/dist/utils/toolExecutor.d.ts +0 -37
  345. package/dist/utils/toolExecutor.js +0 -224
  346. package/dist/utils/usageLogger.d.ts +0 -11
  347. package/dist/utils/usageLogger.js +0 -114
  348. package/dist/utils/vscodeConnection.d.ts +0 -76
  349. package/dist/utils/vscodeConnection.js +0 -430
  350. package/dist/utils/workspaceSnapshot.d.ts +0 -63
  351. package/dist/utils/workspaceSnapshot.js +0 -300
@@ -1,1052 +0,0 @@
1
- import { encoding_for_model } from 'tiktoken';
2
- import { createStreamingChatCompletion } from '../api/chat.js';
3
- import { createStreamingResponse } from '../api/responses.js';
4
- import { createStreamingGeminiCompletion } from '../api/gemini.js';
5
- import { createStreamingAnthropicCompletion } from '../api/anthropic.js';
6
- import { getSystemPrompt } from '../api/systemPrompt.js';
7
- import { collectAllMCPTools, getTodoService } from '../utils/mcpToolsManager.js';
8
- import { executeToolCalls } from '../utils/toolExecutor.js';
9
- import { getOpenAiConfig } from '../utils/apiConfig.js';
10
- import { sessionManager } from '../utils/sessionManager.js';
11
- import { formatTodoContext } from '../utils/todoPreprocessor.js';
12
- import { formatToolCallMessage } from '../utils/messageFormatter.js';
13
- import { resourceMonitor } from '../utils/resourceMonitor.js';
14
- import { isToolNeedTwoStepDisplay } from '../utils/toolDisplayConfig.js';
15
- import { shouldAutoCompress, performAutoCompression, } from '../utils/autoCompress.js';
16
- /**
17
- * Handle conversation with streaming and tool calls
18
- * Returns the usage data collected during the conversation
19
- */
20
- export async function handleConversationWithTools(options) {
21
- const { userContent, imageContents, controller,
22
- // messages, // No longer used - we load from session instead to get complete history with tool calls
23
- saveMessage, setMessages, setStreamTokenCount, requestToolConfirmation, isToolAutoApproved, addMultipleToAlwaysApproved, yoloMode, setContextUsage, setIsReasoning, setRetryStatus, } = options;
24
- // Create a wrapper function for adding single tool to always-approved list
25
- const addToAlwaysApproved = (toolName) => {
26
- addMultipleToAlwaysApproved([toolName]);
27
- };
28
- // Step 1: Ensure session exists and get existing TODOs
29
- let currentSession = sessionManager.getCurrentSession();
30
- if (!currentSession) {
31
- currentSession = await sessionManager.createNewSession();
32
- }
33
- const todoService = getTodoService();
34
- // Get existing TODO list
35
- const existingTodoList = await todoService.getTodoList(currentSession.id);
36
- // Collect all MCP tools
37
- const mcpTools = await collectAllMCPTools();
38
- // Build conversation history with TODO context as pinned user message
39
- let conversationMessages = [
40
- { role: 'system', content: getSystemPrompt() },
41
- ];
42
- // If there are TODOs, add pinned context message at the front
43
- if (existingTodoList && existingTodoList.todos.length > 0) {
44
- const todoContext = formatTodoContext(existingTodoList.todos);
45
- conversationMessages.push({
46
- role: 'user',
47
- content: todoContext,
48
- });
49
- }
50
- // Add history messages from session (includes tool_calls and tool results)
51
- // Load from session to get complete conversation history with tool interactions
52
- // Filter out internal sub-agent messages (marked with subAgentInternal: true)
53
- const session = sessionManager.getCurrentSession();
54
- if (session && session.messages.length > 0) {
55
- // Use session messages directly (they are already in API format)
56
- // Filter out sub-agent internal messages before sending to API
57
- const filteredMessages = session.messages.filter(msg => !msg.subAgentInternal);
58
- conversationMessages.push(...filteredMessages);
59
- }
60
- // Add current user message
61
- conversationMessages.push({
62
- role: 'user',
63
- content: userContent,
64
- images: imageContents,
65
- });
66
- // Save user message (directly save API format message)
67
- // IMPORTANT: await to ensure message is saved before continuing
68
- // This prevents loss of user message if conversation is interrupted (ESC)
69
- try {
70
- await saveMessage({
71
- role: 'user',
72
- content: userContent,
73
- images: imageContents,
74
- });
75
- }
76
- catch (error) {
77
- console.error('Failed to save user message:', error);
78
- }
79
- // Initialize token encoder with proper cleanup tracking
80
- let encoder;
81
- let encoderFreed = false;
82
- const freeEncoder = () => {
83
- if (!encoderFreed && encoder) {
84
- try {
85
- encoder.free();
86
- encoderFreed = true;
87
- resourceMonitor.trackEncoderFreed();
88
- }
89
- catch (e) {
90
- console.error('Failed to free encoder:', e);
91
- }
92
- }
93
- };
94
- try {
95
- encoder = encoding_for_model('gpt-5');
96
- resourceMonitor.trackEncoderCreated();
97
- }
98
- catch (e) {
99
- encoder = encoding_for_model('gpt-3.5-turbo');
100
- resourceMonitor.trackEncoderCreated();
101
- }
102
- setStreamTokenCount(0);
103
- const config = getOpenAiConfig();
104
- const model = options.useBasicModel
105
- ? config.basicModel || config.advancedModel || 'gpt-5'
106
- : config.advancedModel || 'gpt-5';
107
- // Tool calling loop (no limit on rounds)
108
- let finalAssistantMessage = null;
109
- // Accumulate usage data across all rounds
110
- let accumulatedUsage = null;
111
- // Local set to track approved tools in this conversation (solves async setState issue)
112
- const sessionApprovedTools = new Set();
113
- try {
114
- while (true) {
115
- if (controller.signal.aborted) {
116
- freeEncoder();
117
- break;
118
- }
119
- let streamedContent = '';
120
- let receivedToolCalls;
121
- let receivedReasoning;
122
- let receivedThinking; // Accumulate thinking content from all platforms
123
- let hasStartedReasoning = false; // Track if reasoning has started (for Gemini thinking)
124
- // Stream AI response - choose API based on config
125
- let toolCallAccumulator = ''; // Accumulate tool call deltas for token counting
126
- let reasoningAccumulator = ''; // Accumulate reasoning summary deltas for token counting (Responses API only)
127
- let chunkCount = 0; // Track number of chunks received (to delay clearing retry status)
128
- // Get or create session for cache key
129
- const currentSession = sessionManager.getCurrentSession();
130
- // Use session ID as cache key to ensure same session requests share cache
131
- const cacheKey = currentSession?.id;
132
- // 重试回调函数
133
- const onRetry = (error, attempt, nextDelay) => {
134
- if (setRetryStatus) {
135
- setRetryStatus({
136
- isRetrying: true,
137
- attempt,
138
- nextDelay,
139
- errorMessage: error.message,
140
- });
141
- }
142
- };
143
- const streamGenerator = config.requestMethod === 'anthropic'
144
- ? createStreamingAnthropicCompletion({
145
- model,
146
- messages: conversationMessages,
147
- temperature: 0,
148
- max_tokens: config.maxTokens || 4096,
149
- tools: mcpTools.length > 0 ? mcpTools : undefined,
150
- sessionId: currentSession?.id,
151
- // Disable thinking for basicModel (e.g., init command)
152
- disableThinking: options.useBasicModel,
153
- }, controller.signal, onRetry)
154
- : config.requestMethod === 'gemini'
155
- ? createStreamingGeminiCompletion({
156
- model,
157
- messages: conversationMessages,
158
- temperature: 0,
159
- tools: mcpTools.length > 0 ? mcpTools : undefined,
160
- }, controller.signal, onRetry)
161
- : config.requestMethod === 'responses'
162
- ? createStreamingResponse({
163
- model,
164
- messages: conversationMessages,
165
- temperature: 0,
166
- tools: mcpTools.length > 0 ? mcpTools : undefined,
167
- tool_choice: 'auto',
168
- prompt_cache_key: cacheKey, // Use session ID as cache key
169
- // Don't pass reasoning for basicModel (small models may not support it)
170
- // Pass null to explicitly disable reasoning in API call
171
- reasoning: options.useBasicModel ? null : undefined,
172
- }, controller.signal, onRetry)
173
- : createStreamingChatCompletion({
174
- model,
175
- messages: conversationMessages,
176
- temperature: 0,
177
- tools: mcpTools.length > 0 ? mcpTools : undefined,
178
- }, controller.signal, onRetry);
179
- for await (const chunk of streamGenerator) {
180
- if (controller.signal.aborted)
181
- break;
182
- // Clear retry status after a delay when first chunk arrives
183
- // This gives users time to see the retry message (500ms delay)
184
- chunkCount++;
185
- if (setRetryStatus && chunkCount === 1) {
186
- setTimeout(() => {
187
- setRetryStatus(null);
188
- }, 500);
189
- }
190
- if (chunk.type === 'reasoning_started') {
191
- // Reasoning started (Responses API only) - set reasoning state
192
- setIsReasoning?.(true);
193
- }
194
- else if (chunk.type === 'reasoning_delta' && chunk.delta) {
195
- // Handle reasoning delta from Gemini thinking
196
- // When reasoning_delta is received, set reasoning state if not already set
197
- if (!hasStartedReasoning) {
198
- setIsReasoning?.(true);
199
- hasStartedReasoning = true;
200
- }
201
- // Note: reasoning content is NOT sent back to AI, only counted for display
202
- reasoningAccumulator += chunk.delta;
203
- try {
204
- const tokens = encoder.encode(streamedContent + toolCallAccumulator + reasoningAccumulator);
205
- setStreamTokenCount(tokens.length);
206
- }
207
- catch (e) {
208
- // Ignore encoding errors
209
- }
210
- }
211
- else if (chunk.type === 'content' && chunk.content) {
212
- // Accumulate content and update token count
213
- // When content starts, reasoning is done
214
- setIsReasoning?.(false);
215
- streamedContent += chunk.content;
216
- try {
217
- const tokens = encoder.encode(streamedContent + toolCallAccumulator + reasoningAccumulator);
218
- setStreamTokenCount(tokens.length);
219
- }
220
- catch (e) {
221
- // Ignore encoding errors
222
- }
223
- }
224
- else if (chunk.type === 'tool_call_delta' && chunk.delta) {
225
- // Accumulate tool call deltas and update token count in real-time
226
- // When tool calls start, reasoning is done (OpenAI generally doesn't output text content during tool calls)
227
- setIsReasoning?.(false);
228
- toolCallAccumulator += chunk.delta;
229
- try {
230
- const tokens = encoder.encode(streamedContent + toolCallAccumulator + reasoningAccumulator);
231
- setStreamTokenCount(tokens.length);
232
- }
233
- catch (e) {
234
- // Ignore encoding errors
235
- }
236
- }
237
- else if (chunk.type === 'tool_calls' && chunk.tool_calls) {
238
- receivedToolCalls = chunk.tool_calls;
239
- }
240
- else if (chunk.type === 'reasoning_data' && chunk.reasoning) {
241
- // Capture reasoning data from Responses API
242
- receivedReasoning = chunk.reasoning;
243
- }
244
- else if (chunk.type === 'done' && chunk.thinking) {
245
- // Capture thinking content from Anthropic only (includes signature)
246
- receivedThinking = chunk.thinking;
247
- }
248
- else if (chunk.type === 'usage' && chunk.usage) {
249
- // Capture usage information both in state and locally
250
- setContextUsage(chunk.usage);
251
- // Note: Usage is now saved at API layer (chat.ts, anthropic.ts, etc.)
252
- // No need to call onUsageUpdate here to avoid duplicate saves
253
- // Accumulate for final return (UI display purposes)
254
- if (!accumulatedUsage) {
255
- accumulatedUsage = {
256
- prompt_tokens: chunk.usage.prompt_tokens || 0,
257
- completion_tokens: chunk.usage.completion_tokens || 0,
258
- total_tokens: chunk.usage.total_tokens || 0,
259
- cache_creation_input_tokens: chunk.usage.cache_creation_input_tokens,
260
- cache_read_input_tokens: chunk.usage.cache_read_input_tokens,
261
- cached_tokens: chunk.usage.cached_tokens,
262
- };
263
- }
264
- else {
265
- // Add to existing usage for UI display
266
- accumulatedUsage.prompt_tokens += chunk.usage.prompt_tokens || 0;
267
- accumulatedUsage.completion_tokens +=
268
- chunk.usage.completion_tokens || 0;
269
- accumulatedUsage.total_tokens += chunk.usage.total_tokens || 0;
270
- if (chunk.usage.cache_creation_input_tokens !== undefined) {
271
- accumulatedUsage.cache_creation_input_tokens =
272
- (accumulatedUsage.cache_creation_input_tokens || 0) +
273
- chunk.usage.cache_creation_input_tokens;
274
- }
275
- if (chunk.usage.cache_read_input_tokens !== undefined) {
276
- accumulatedUsage.cache_read_input_tokens =
277
- (accumulatedUsage.cache_read_input_tokens || 0) +
278
- chunk.usage.cache_read_input_tokens;
279
- }
280
- if (chunk.usage.cached_tokens !== undefined) {
281
- accumulatedUsage.cached_tokens =
282
- (accumulatedUsage.cached_tokens || 0) +
283
- chunk.usage.cached_tokens;
284
- }
285
- }
286
- }
287
- }
288
- // Reset token count after stream ends
289
- setStreamTokenCount(0);
290
- // If aborted during streaming, exit the loop
291
- // (discontinued message already added by ChatScreen ESC handler)
292
- if (controller.signal.aborted) {
293
- freeEncoder();
294
- break;
295
- }
296
- // If there are tool calls, we need to handle them specially
297
- if (receivedToolCalls && receivedToolCalls.length > 0) {
298
- // Add assistant message with tool_calls to conversation (OpenAI requires this format)
299
- const assistantMessage = {
300
- role: 'assistant',
301
- content: streamedContent || '',
302
- tool_calls: receivedToolCalls.map(tc => ({
303
- id: tc.id,
304
- type: 'function',
305
- function: {
306
- name: tc.function.name,
307
- arguments: tc.function.arguments,
308
- },
309
- })),
310
- reasoning: receivedReasoning, // Include reasoning data for caching (Responses API)
311
- thinking: receivedThinking, // Include thinking content (Anthropic/OpenAI)
312
- };
313
- conversationMessages.push(assistantMessage);
314
- // Save assistant message with tool calls
315
- saveMessage(assistantMessage).catch(error => {
316
- console.error('Failed to save assistant message:', error);
317
- });
318
- // If there's text content before tool calls, display it first
319
- if (streamedContent && streamedContent.trim()) {
320
- setMessages(prev => [
321
- ...prev,
322
- {
323
- role: 'assistant',
324
- content: streamedContent.trim(),
325
- streaming: false,
326
- },
327
- ]);
328
- }
329
- // Display tool calls in UI - 只有耗时工具才显示进行中状态
330
- // Generate parallel group ID when there are multiple tools
331
- const parallelGroupId = receivedToolCalls.length > 1
332
- ? `parallel-${Date.now()}-${Math.random()}`
333
- : undefined;
334
- for (const toolCall of receivedToolCalls) {
335
- const toolDisplay = formatToolCallMessage(toolCall);
336
- let toolArgs;
337
- try {
338
- toolArgs = JSON.parse(toolCall.function.arguments);
339
- }
340
- catch (e) {
341
- toolArgs = {};
342
- }
343
- // 只有耗时工具才在动态区显示进行中状态
344
- if (isToolNeedTwoStepDisplay(toolCall.function.name)) {
345
- setMessages(prev => [
346
- ...prev,
347
- {
348
- role: 'assistant',
349
- content: `⚡ ${toolDisplay.toolName}`,
350
- streaming: false,
351
- toolCall: {
352
- name: toolCall.function.name,
353
- arguments: toolArgs,
354
- },
355
- toolDisplay,
356
- toolCallId: toolCall.id, // Store tool call ID for later update
357
- toolPending: true, // Mark as pending execution
358
- // Mark parallel group for ALL tools (time-consuming or not)
359
- parallelGroup: parallelGroupId,
360
- },
361
- ]);
362
- }
363
- }
364
- // Filter tools that need confirmation (not in always-approved list OR session-approved list)
365
- const toolsNeedingConfirmation = [];
366
- const autoApprovedTools = [];
367
- for (const toolCall of receivedToolCalls) {
368
- // Check both global approved list and session-approved list
369
- const isApproved = isToolAutoApproved(toolCall.function.name) ||
370
- sessionApprovedTools.has(toolCall.function.name);
371
- // Check if this is a sensitive command (terminal-execute with sensitive pattern)
372
- let isSensitiveCommand = false;
373
- if (toolCall.function.name === 'terminal-execute') {
374
- try {
375
- const args = JSON.parse(toolCall.function.arguments);
376
- const { isSensitiveCommand: checkSensitiveCommand } = await import('../utils/sensitiveCommandManager.js').then(m => ({
377
- isSensitiveCommand: m.isSensitiveCommand,
378
- }));
379
- const sensitiveCheck = checkSensitiveCommand(args.command);
380
- isSensitiveCommand = sensitiveCheck.isSensitive;
381
- }
382
- catch {
383
- // If parsing fails, treat as normal command
384
- }
385
- }
386
- // If sensitive command, always require confirmation regardless of approval status
387
- if (isSensitiveCommand) {
388
- toolsNeedingConfirmation.push(toolCall);
389
- }
390
- else if (isApproved) {
391
- autoApprovedTools.push(toolCall);
392
- }
393
- else {
394
- toolsNeedingConfirmation.push(toolCall);
395
- }
396
- }
397
- // Request confirmation only once for all tools needing confirmation
398
- let approvedTools = [...autoApprovedTools];
399
- // In YOLO mode, auto-approve all tools EXCEPT sensitive commands
400
- if (yoloMode) {
401
- // Filter out sensitive commands from auto-approval
402
- const nonSensitiveTools = [];
403
- const sensitiveTools = [];
404
- for (const toolCall of toolsNeedingConfirmation) {
405
- if (toolCall.function.name === 'terminal-execute') {
406
- try {
407
- const args = JSON.parse(toolCall.function.arguments);
408
- const { isSensitiveCommand: checkSensitiveCommand } = await import('../utils/sensitiveCommandManager.js').then(m => ({
409
- isSensitiveCommand: m.isSensitiveCommand,
410
- }));
411
- const sensitiveCheck = checkSensitiveCommand(args.command);
412
- if (sensitiveCheck.isSensitive) {
413
- sensitiveTools.push(toolCall);
414
- }
415
- else {
416
- nonSensitiveTools.push(toolCall);
417
- }
418
- }
419
- catch {
420
- nonSensitiveTools.push(toolCall);
421
- }
422
- }
423
- else {
424
- nonSensitiveTools.push(toolCall);
425
- }
426
- }
427
- approvedTools.push(...nonSensitiveTools);
428
- // If there are sensitive tools, still need confirmation even in YOLO mode
429
- if (sensitiveTools.length > 0) {
430
- const firstTool = sensitiveTools[0];
431
- const allTools = sensitiveTools.length > 1 ? sensitiveTools : undefined;
432
- const confirmation = await requestToolConfirmation(firstTool, undefined, allTools);
433
- if (confirmation === 'reject' ||
434
- (typeof confirmation === 'object' &&
435
- confirmation.type === 'reject_with_reply')) {
436
- setMessages(prev => prev.filter(msg => !msg.toolPending));
437
- const rejectMessage = typeof confirmation === 'object'
438
- ? `Tool execution rejected by user: ${confirmation.reason}`
439
- : 'Error: Tool execution rejected by user';
440
- // Create UI messages for rejected tools
441
- const rejectedToolUIMessages = [];
442
- for (const toolCall of sensitiveTools) {
443
- const rejectionMessage = {
444
- role: 'tool',
445
- tool_call_id: toolCall.id,
446
- content: rejectMessage,
447
- };
448
- conversationMessages.push(rejectionMessage);
449
- saveMessage(rejectionMessage).catch(error => {
450
- console.error('Failed to save tool rejection message:', error);
451
- });
452
- // Add UI message for each rejected tool
453
- const toolDisplay = formatToolCallMessage(toolCall);
454
- const statusIcon = '✗';
455
- let statusText = '';
456
- if (typeof confirmation === 'object' && confirmation.reason) {
457
- statusText = `\n └─ Rejection reason: ${confirmation.reason}`;
458
- }
459
- else {
460
- statusText = `\n └─ ${rejectMessage}`;
461
- }
462
- rejectedToolUIMessages.push({
463
- role: 'assistant',
464
- content: `${statusIcon} ${toolDisplay.toolName}${statusText}`,
465
- streaming: false,
466
- });
467
- }
468
- // Add rejected tool messages to UI
469
- if (rejectedToolUIMessages.length > 0) {
470
- setMessages(prev => [...prev, ...rejectedToolUIMessages]);
471
- }
472
- // If reject_with_reply, continue the conversation instead of ending
473
- if (typeof confirmation === 'object' &&
474
- confirmation.type === 'reject_with_reply') {
475
- // Continue to next iteration - AI will see the rejection message and respond
476
- continue;
477
- }
478
- else {
479
- // Original reject behavior - end session
480
- setMessages(prev => [
481
- ...prev,
482
- {
483
- role: 'assistant',
484
- content: 'Tool call rejected, session ended',
485
- streaming: false,
486
- },
487
- ]);
488
- if (options.setIsStreaming) {
489
- options.setIsStreaming(false);
490
- }
491
- freeEncoder();
492
- return { usage: accumulatedUsage };
493
- }
494
- }
495
- // Approved, add sensitive tools to approved list
496
- approvedTools.push(...sensitiveTools);
497
- }
498
- }
499
- else if (toolsNeedingConfirmation.length > 0) {
500
- const firstTool = toolsNeedingConfirmation[0];
501
- const allTools = toolsNeedingConfirmation.length > 1
502
- ? toolsNeedingConfirmation
503
- : undefined;
504
- const confirmation = await requestToolConfirmation(firstTool, undefined, allTools);
505
- if (confirmation === 'reject' ||
506
- (typeof confirmation === 'object' &&
507
- confirmation.type === 'reject_with_reply')) {
508
- setMessages(prev => prev.filter(msg => !msg.toolPending));
509
- const rejectMessage = typeof confirmation === 'object'
510
- ? `Tool execution rejected by user: ${confirmation.reason}`
511
- : 'Error: Tool execution rejected by user';
512
- // Create UI messages for rejected tools
513
- const rejectedToolUIMessages = [];
514
- for (const toolCall of toolsNeedingConfirmation) {
515
- const rejectionMessage = {
516
- role: 'tool',
517
- tool_call_id: toolCall.id,
518
- content: rejectMessage,
519
- };
520
- conversationMessages.push(rejectionMessage);
521
- saveMessage(rejectionMessage).catch(error => {
522
- console.error('Failed to save tool rejection message:', error);
523
- });
524
- // Add UI message for each rejected tool
525
- const toolDisplay = formatToolCallMessage(toolCall);
526
- const statusIcon = '✗';
527
- let statusText = '';
528
- if (typeof confirmation === 'object' && confirmation.reason) {
529
- statusText = `\n └─ Rejection reason: ${confirmation.reason}`;
530
- }
531
- else {
532
- statusText = `\n └─ ${rejectMessage}`;
533
- }
534
- rejectedToolUIMessages.push({
535
- role: 'assistant',
536
- content: `${statusIcon} ${toolDisplay.toolName}${statusText}`,
537
- streaming: false,
538
- });
539
- }
540
- // Add rejected tool messages to UI
541
- if (rejectedToolUIMessages.length > 0) {
542
- setMessages(prev => [...prev, ...rejectedToolUIMessages]);
543
- }
544
- // If reject_with_reply, continue the conversation instead of ending
545
- if (typeof confirmation === 'object' &&
546
- confirmation.type === 'reject_with_reply') {
547
- // Continue to next iteration - AI will see the rejection message and respond
548
- continue;
549
- }
550
- else {
551
- // Original reject behavior - end session
552
- setMessages(prev => [
553
- ...prev,
554
- {
555
- role: 'assistant',
556
- content: 'Tool call rejected, session ended',
557
- streaming: false,
558
- },
559
- ]);
560
- if (options.setIsStreaming) {
561
- options.setIsStreaming(false);
562
- }
563
- freeEncoder();
564
- return { usage: accumulatedUsage };
565
- }
566
- }
567
- // If approved_always, add ALL these tools to both global and session-approved sets
568
- if (confirmation === 'approve_always') {
569
- const toolNamesToAdd = toolsNeedingConfirmation.map(t => t.function.name);
570
- // Add to global state (async, for future sessions)
571
- addMultipleToAlwaysApproved(toolNamesToAdd);
572
- // Add to local session set (sync, for this conversation)
573
- toolNamesToAdd.forEach(name => sessionApprovedTools.add(name));
574
- }
575
- // Add all tools to approved list
576
- approvedTools.push(...toolsNeedingConfirmation);
577
- }
578
- // Execute approved tools with sub-agent message callback and terminal output callback
579
- // Track sub-agent content for token counting
580
- let subAgentContentAccumulator = '';
581
- const toolResults = await executeToolCalls(approvedTools, controller.signal, setStreamTokenCount, async (subAgentMessage) => {
582
- // Handle sub-agent messages - display and save to session
583
- setMessages(prev => {
584
- // Handle tool calls from sub-agent
585
- if (subAgentMessage.message.type === 'tool_calls') {
586
- const toolCalls = subAgentMessage.message.tool_calls;
587
- if (toolCalls && toolCalls.length > 0) {
588
- // Add tool call messages for each tool (only for time-consuming tools)
589
- const toolMessages = toolCalls
590
- .filter((toolCall) => isToolNeedTwoStepDisplay(toolCall.function.name))
591
- .map((toolCall) => {
592
- const toolDisplay = formatToolCallMessage(toolCall);
593
- let toolArgs;
594
- try {
595
- toolArgs = JSON.parse(toolCall.function.arguments);
596
- }
597
- catch (e) {
598
- toolArgs = {};
599
- }
600
- const uiMsg = {
601
- role: 'subagent',
602
- content: `\x1b[38;2;184;122;206m⚇⚡ ${toolDisplay.toolName}\x1b[0m`,
603
- streaming: false,
604
- toolCall: {
605
- name: toolCall.function.name,
606
- arguments: toolArgs,
607
- },
608
- // Don't include toolDisplay for sub-agent tools to avoid showing parameters
609
- toolCallId: toolCall.id,
610
- toolPending: true,
611
- subAgent: {
612
- agentId: subAgentMessage.agentId,
613
- agentName: subAgentMessage.agentName,
614
- isComplete: false,
615
- },
616
- subAgentInternal: true, // Mark as internal sub-agent message
617
- };
618
- return uiMsg;
619
- });
620
- // Save all tool calls to session (regardless of display type)
621
- const sessionMsg = {
622
- role: 'assistant',
623
- content: toolCalls
624
- .map((tc) => {
625
- const display = formatToolCallMessage(tc);
626
- return `⚇⚡ ${display.toolName}`;
627
- })
628
- .join(', '),
629
- subAgentInternal: true,
630
- tool_calls: toolCalls,
631
- };
632
- saveMessage(sessionMsg).catch(err => console.error('Failed to save sub-agent tool call:', err));
633
- return [...prev, ...toolMessages];
634
- }
635
- }
636
- // Handle tool results from sub-agent
637
- if (subAgentMessage.message.type === 'tool_result') {
638
- const msg = subAgentMessage.message;
639
- const isError = msg.content.startsWith('Error:');
640
- const statusIcon = isError ? '✗' : '✓';
641
- const statusText = isError ? `\n └─ ${msg.content}` : '';
642
- // For terminal-execute, try to extract terminal result data
643
- let terminalResultData;
644
- if (msg.tool_name === 'terminal-execute' && !isError) {
645
- try {
646
- const resultData = JSON.parse(msg.content);
647
- if (resultData.stdout !== undefined ||
648
- resultData.stderr !== undefined) {
649
- terminalResultData = {
650
- stdout: resultData.stdout,
651
- stderr: resultData.stderr,
652
- exitCode: resultData.exitCode,
653
- command: resultData.command,
654
- };
655
- }
656
- }
657
- catch (e) {
658
- // If parsing fails, just show regular result
659
- }
660
- }
661
- // Create completed tool result message for UI
662
- const uiMsg = {
663
- role: 'subagent',
664
- content: `\x1b[38;2;0;186;255m⚇${statusIcon} ${msg.tool_name}\x1b[0m${statusText}`,
665
- streaming: false,
666
- toolResult: !isError ? msg.content : undefined,
667
- terminalResult: terminalResultData,
668
- toolCall: terminalResultData
669
- ? {
670
- name: msg.tool_name,
671
- arguments: terminalResultData,
672
- }
673
- : undefined,
674
- subAgent: {
675
- agentId: subAgentMessage.agentId,
676
- agentName: subAgentMessage.agentName,
677
- isComplete: false,
678
- },
679
- subAgentInternal: true,
680
- };
681
- // Save to session as 'tool' role for API compatibility
682
- const sessionMsg = {
683
- role: 'tool',
684
- tool_call_id: msg.tool_call_id,
685
- content: msg.content,
686
- subAgentInternal: true,
687
- };
688
- saveMessage(sessionMsg).catch(err => console.error('Failed to save sub-agent tool result:', err));
689
- // Add completed tool result message
690
- return [...prev, uiMsg];
691
- }
692
- // Check if we already have a message for this agent
693
- const existingIndex = prev.findIndex(m => m.role === 'subagent' &&
694
- m.subAgent?.agentId === subAgentMessage.agentId &&
695
- !m.subAgent?.isComplete &&
696
- !m.toolCall);
697
- // Extract content from the sub-agent message
698
- let content = '';
699
- if (subAgentMessage.message.type === 'content') {
700
- content = subAgentMessage.message.content;
701
- // Update token count for sub-agent content
702
- subAgentContentAccumulator += content;
703
- try {
704
- const tokens = encoder.encode(subAgentContentAccumulator);
705
- setStreamTokenCount(tokens.length);
706
- }
707
- catch (e) {
708
- // Ignore encoding errors
709
- }
710
- }
711
- else if (subAgentMessage.message.type === 'done') {
712
- // Mark as complete and reset token counter
713
- subAgentContentAccumulator = '';
714
- setStreamTokenCount(0);
715
- if (existingIndex !== -1) {
716
- const updated = [...prev];
717
- const existing = updated[existingIndex];
718
- if (existing && existing.subAgent) {
719
- updated[existingIndex] = {
720
- ...existing,
721
- subAgent: {
722
- ...existing.subAgent,
723
- isComplete: true,
724
- },
725
- };
726
- }
727
- return updated;
728
- }
729
- return prev;
730
- }
731
- if (existingIndex !== -1) {
732
- // Update existing message
733
- const updated = [...prev];
734
- const existing = updated[existingIndex];
735
- if (existing) {
736
- updated[existingIndex] = {
737
- ...existing,
738
- content: (existing.content || '') + content,
739
- streaming: true,
740
- };
741
- }
742
- return updated;
743
- }
744
- else if (content) {
745
- // Add new sub-agent message
746
- return [
747
- ...prev,
748
- {
749
- role: 'subagent',
750
- content,
751
- streaming: true,
752
- subAgent: {
753
- agentId: subAgentMessage.agentId,
754
- agentName: subAgentMessage.agentName,
755
- isComplete: false,
756
- },
757
- },
758
- ];
759
- }
760
- return prev;
761
- });
762
- }, requestToolConfirmation, isToolAutoApproved, yoloMode, addToAlwaysApproved);
763
- // Check if aborted during tool execution
764
- if (controller.signal.aborted) {
765
- // Need to add tool results for all pending tool calls to complete conversation history
766
- // This is critical for sub-agents and any tools that were being executed
767
- if (receivedToolCalls && receivedToolCalls.length > 0) {
768
- for (const toolCall of receivedToolCalls) {
769
- const abortedResult = {
770
- role: 'tool',
771
- tool_call_id: toolCall.id,
772
- content: 'Error: Tool execution aborted by user',
773
- };
774
- conversationMessages.push(abortedResult);
775
- saveMessage(abortedResult).catch(error => {
776
- console.error('Failed to save aborted tool result:', error);
777
- });
778
- }
779
- }
780
- freeEncoder();
781
- break;
782
- }
783
- // 在工具执行完成后、发送结果到AI前,检查是否需要压缩
784
- const config = getOpenAiConfig();
785
- if (config.enableAutoCompress !== false &&
786
- options.getCurrentContextPercentage &&
787
- shouldAutoCompress(options.getCurrentContextPercentage())) {
788
- try {
789
- // 显示压缩提示消息
790
- const compressingMessage = {
791
- role: 'assistant',
792
- content: '✵ Auto-compressing context before sending tool results...',
793
- streaming: false,
794
- };
795
- setMessages(prev => [...prev, compressingMessage]);
796
- const compressionResult = await performAutoCompression();
797
- if (compressionResult && options.clearSavedMessages) {
798
- // 更新UI和token使用情况
799
- options.clearSavedMessages();
800
- setMessages(compressionResult.uiMessages);
801
- if (options.setRemountKey) {
802
- options.setRemountKey(prev => prev + 1);
803
- }
804
- options.setContextUsage(compressionResult.usage);
805
- // 更新累计的usage为压缩后的usage
806
- accumulatedUsage = compressionResult.usage;
807
- // 压缩后需要重新构建conversationMessages
808
- conversationMessages = [];
809
- const session = sessionManager.getCurrentSession();
810
- if (session && session.messages.length > 0) {
811
- conversationMessages.push(...session.messages);
812
- }
813
- }
814
- }
815
- catch (error) {
816
- console.error('Auto-compression after tool execution failed:', error);
817
- // 即使压缩失败也继续处理工具结果
818
- }
819
- }
820
- // Remove only streaming sub-agent content messages (not tool-related messages)
821
- // Keep sub-agent tool call and tool result messages for display
822
- setMessages(prev => prev.filter(m => m.role !== 'subagent' ||
823
- m.toolCall !== undefined ||
824
- m.toolResult !== undefined ||
825
- m.subAgentInternal === true));
826
- // Update existing tool call messages with results
827
- // Collect all result messages first, then add them in batch
828
- const resultMessages = [];
829
- for (const result of toolResults) {
830
- const toolCall = receivedToolCalls.find(tc => tc.id === result.tool_call_id);
831
- if (toolCall) {
832
- // Special handling for sub-agent tools - show completion message
833
- // Pass the full JSON result to ToolResultPreview for proper parsing
834
- if (toolCall.function.name.startsWith('subagent-')) {
835
- const isError = result.content.startsWith('Error:');
836
- const statusIcon = isError ? '✗' : '✓';
837
- const statusText = isError ? `\n └─ ${result.content}` : '';
838
- // Parse sub-agent result to extract usage information
839
- let usage = undefined;
840
- if (!isError) {
841
- try {
842
- const subAgentResult = JSON.parse(result.content);
843
- usage = subAgentResult.usage;
844
- }
845
- catch (e) {
846
- // Ignore parsing errors
847
- }
848
- }
849
- resultMessages.push({
850
- role: 'assistant',
851
- content: `${statusIcon} ${toolCall.function.name}${statusText}`,
852
- streaming: false,
853
- // Pass the full result.content for ToolResultPreview to parse
854
- toolResult: !isError ? result.content : undefined,
855
- subAgentUsage: usage,
856
- });
857
- // Save the tool result to conversation history
858
- conversationMessages.push(result);
859
- saveMessage(result).catch(error => {
860
- console.error('Failed to save tool result:', error);
861
- });
862
- continue;
863
- }
864
- const isError = result.content.startsWith('Error:');
865
- const statusIcon = isError ? '✗' : '✓';
866
- const statusText = isError ? `\n └─ ${result.content}` : '';
867
- // Check if this is an edit tool with diff data
868
- let editDiffData;
869
- if ((toolCall.function.name === 'filesystem-edit' ||
870
- toolCall.function.name === 'filesystem-edit_search') &&
871
- !isError) {
872
- try {
873
- const resultData = JSON.parse(result.content);
874
- // Handle single file edit
875
- if (resultData.oldContent && resultData.newContent) {
876
- editDiffData = {
877
- oldContent: resultData.oldContent,
878
- newContent: resultData.newContent,
879
- filename: JSON.parse(toolCall.function.arguments).filePath,
880
- completeOldContent: resultData.completeOldContent,
881
- completeNewContent: resultData.completeNewContent,
882
- contextStartLine: resultData.contextStartLine,
883
- };
884
- }
885
- // Handle batch edit
886
- else if (resultData.results &&
887
- Array.isArray(resultData.results)) {
888
- editDiffData = {
889
- batchResults: resultData.results,
890
- isBatch: true,
891
- };
892
- }
893
- }
894
- catch (e) {
895
- // If parsing fails, just show regular result
896
- }
897
- }
898
- // 处理工具执行结果的显示
899
- // - 耗时工具(两步显示):完成消息追加到静态区,之前的进行中消息已包含参数
900
- // - 普通工具(单步显示):完成消息需要包含参数和结果,使用 toolDisplay
901
- // 获取工具参数的格式化信息
902
- const toolDisplay = formatToolCallMessage(toolCall);
903
- const isNonTimeConsuming = !isToolNeedTwoStepDisplay(toolCall.function.name);
904
- resultMessages.push({
905
- role: 'assistant',
906
- content: `${statusIcon} ${toolCall.function.name}${statusText}`,
907
- streaming: false,
908
- toolCall: editDiffData
909
- ? {
910
- name: toolCall.function.name,
911
- arguments: editDiffData,
912
- }
913
- : undefined,
914
- // 为普通工具添加参数显示(耗时工具在进行中状态已经显示过参数)
915
- toolDisplay: isNonTimeConsuming ? toolDisplay : undefined,
916
- // Store tool result for preview rendering
917
- toolResult: !isError ? result.content : undefined,
918
- // Mark parallel group for ALL tools (time-consuming or not)
919
- parallelGroup: parallelGroupId,
920
- });
921
- }
922
- // Add tool result to conversation history and save (skip if already saved above)
923
- if (toolCall && !toolCall.function.name.startsWith('subagent-')) {
924
- conversationMessages.push(result);
925
- saveMessage(result).catch(error => {
926
- console.error('Failed to save tool result:', error);
927
- });
928
- }
929
- }
930
- // Add all result messages in batch to avoid intermediate renders
931
- if (resultMessages.length > 0) {
932
- setMessages(prev => [...prev, ...resultMessages]);
933
- }
934
- // Check if there are pending user messages to insert
935
- if (options.getPendingMessages && options.clearPendingMessages) {
936
- const pendingMessages = options.getPendingMessages();
937
- if (pendingMessages.length > 0) {
938
- // 检查 token 占用,如果 >= 80% 先执行自动压缩
939
- const config = getOpenAiConfig();
940
- if (config.enableAutoCompress !== false &&
941
- options.getCurrentContextPercentage &&
942
- shouldAutoCompress(options.getCurrentContextPercentage())) {
943
- try {
944
- // 显示压缩提示消息
945
- const compressingMessage = {
946
- role: 'assistant',
947
- content: '✵ Auto-compressing context before processing pending messages...',
948
- streaming: false,
949
- };
950
- setMessages(prev => [...prev, compressingMessage]);
951
- const compressionResult = await performAutoCompression();
952
- if (compressionResult && options.clearSavedMessages) {
953
- // 更新UI和token使用情况
954
- options.clearSavedMessages();
955
- setMessages(compressionResult.uiMessages);
956
- if (options.setRemountKey) {
957
- options.setRemountKey(prev => prev + 1);
958
- }
959
- options.setContextUsage(compressionResult.usage);
960
- // 更新累计的usage为压缩后的usage
961
- accumulatedUsage = compressionResult.usage;
962
- // 压缩后需要重新构建conversationMessages
963
- conversationMessages = [];
964
- const session = sessionManager.getCurrentSession();
965
- if (session && session.messages.length > 0) {
966
- conversationMessages.push(...session.messages);
967
- }
968
- }
969
- }
970
- catch (error) {
971
- console.error('Auto-compression before pending messages failed:', error);
972
- // 即使压缩失败也继续处理pending消息
973
- }
974
- }
975
- // Clear pending messages
976
- options.clearPendingMessages();
977
- // Combine multiple pending messages into one
978
- const combinedMessage = pendingMessages
979
- .map(m => m.text)
980
- .join('\n\n');
981
- // Collect all images from pending messages
982
- const allPendingImages = pendingMessages
983
- .flatMap(m => m.images || [])
984
- .map(img => ({
985
- type: 'image',
986
- data: img.data,
987
- mimeType: img.mimeType,
988
- }));
989
- // Add user message to UI
990
- const userMessage = {
991
- role: 'user',
992
- content: combinedMessage,
993
- images: allPendingImages.length > 0 ? allPendingImages : undefined,
994
- };
995
- setMessages(prev => [...prev, userMessage]);
996
- // Add user message to conversation history (using images field for image data)
997
- conversationMessages.push({
998
- role: 'user',
999
- content: combinedMessage,
1000
- images: allPendingImages.length > 0 ? allPendingImages : undefined,
1001
- });
1002
- // Save user message
1003
- saveMessage({
1004
- role: 'user',
1005
- content: combinedMessage,
1006
- images: allPendingImages.length > 0 ? allPendingImages : undefined,
1007
- }).catch(error => {
1008
- console.error('Failed to save pending user message:', error);
1009
- });
1010
- }
1011
- }
1012
- // Continue loop to get next response
1013
- continue;
1014
- }
1015
- // No tool calls - conversation is complete
1016
- // Display text content if any
1017
- if (streamedContent.trim()) {
1018
- finalAssistantMessage = {
1019
- role: 'assistant',
1020
- content: streamedContent.trim(),
1021
- streaming: false,
1022
- discontinued: controller.signal.aborted,
1023
- };
1024
- setMessages(prev => [...prev, finalAssistantMessage]);
1025
- // Add to conversation history and save
1026
- const assistantMessage = {
1027
- role: 'assistant',
1028
- content: streamedContent.trim(),
1029
- reasoning: receivedReasoning, // Include reasoning data for caching (Responses API)
1030
- thinking: receivedThinking, // Include thinking content (Anthropic/OpenAI)
1031
- };
1032
- conversationMessages.push(assistantMessage);
1033
- saveMessage(assistantMessage).catch(error => {
1034
- console.error('Failed to save assistant message:', error);
1035
- });
1036
- }
1037
- // Conversation complete - exit the loop
1038
- break;
1039
- }
1040
- // Free encoder
1041
- freeEncoder();
1042
- }
1043
- catch (error) {
1044
- throw error;
1045
- }
1046
- finally {
1047
- // ✅ 确保总是释放encoder资源,避免资源泄漏
1048
- freeEncoder();
1049
- }
1050
- // Return the accumulated usage data
1051
- return { usage: accumulatedUsage };
1052
- }