snow-ai 0.4.16 → 0.4.18

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 (352) hide show
  1. package/bundle/cli.mjs +477445 -0
  2. package/bundle/sql-wasm.wasm +0 -0
  3. package/bundle/tiktoken_bg.wasm +0 -0
  4. package/package.json +31 -26
  5. package/dist/agents/codebaseIndexAgent.d.ts +0 -102
  6. package/dist/agents/codebaseIndexAgent.js +0 -641
  7. package/dist/agents/codebaseReviewAgent.d.ts +0 -61
  8. package/dist/agents/codebaseReviewAgent.js +0 -301
  9. package/dist/agents/compactAgent.d.ts +0 -55
  10. package/dist/agents/compactAgent.js +0 -306
  11. package/dist/agents/promptOptimizeAgent.d.ts +0 -54
  12. package/dist/agents/promptOptimizeAgent.js +0 -268
  13. package/dist/agents/reviewAgent.d.ts +0 -50
  14. package/dist/agents/reviewAgent.js +0 -265
  15. package/dist/agents/summaryAgent.d.ts +0 -57
  16. package/dist/agents/summaryAgent.js +0 -260
  17. package/dist/api/anthropic.d.ts +0 -44
  18. package/dist/api/anthropic.js +0 -598
  19. package/dist/api/chat.d.ts +0 -73
  20. package/dist/api/chat.js +0 -386
  21. package/dist/api/embedding.d.ts +0 -34
  22. package/dist/api/embedding.js +0 -80
  23. package/dist/api/gemini.d.ts +0 -31
  24. package/dist/api/gemini.js +0 -445
  25. package/dist/api/models.d.ts +0 -15
  26. package/dist/api/models.js +0 -139
  27. package/dist/api/responses.d.ts +0 -38
  28. package/dist/api/responses.js +0 -515
  29. package/dist/api/systemPrompt.d.ts +0 -4
  30. package/dist/api/systemPrompt.js +0 -408
  31. package/dist/api/types.d.ts +0 -53
  32. package/dist/api/types.js +0 -4
  33. package/dist/app.d.ts +0 -8
  34. package/dist/app.js +0 -112
  35. package/dist/cli.d.ts +0 -2
  36. package/dist/cli.js +0 -199
  37. package/dist/hooks/useAgentPicker.d.ts +0 -14
  38. package/dist/hooks/useAgentPicker.js +0 -119
  39. package/dist/hooks/useClipboard.d.ts +0 -4
  40. package/dist/hooks/useClipboard.js +0 -175
  41. package/dist/hooks/useCommandHandler.d.ts +0 -35
  42. package/dist/hooks/useCommandHandler.js +0 -346
  43. package/dist/hooks/useCommandPanel.d.ts +0 -17
  44. package/dist/hooks/useCommandPanel.js +0 -114
  45. package/dist/hooks/useConversation.d.ts +0 -49
  46. package/dist/hooks/useConversation.js +0 -1052
  47. package/dist/hooks/useFilePicker.d.ts +0 -18
  48. package/dist/hooks/useFilePicker.js +0 -224
  49. package/dist/hooks/useGlobalExit.d.ts +0 -5
  50. package/dist/hooks/useGlobalExit.js +0 -34
  51. package/dist/hooks/useGlobalNavigation.d.ts +0 -6
  52. package/dist/hooks/useGlobalNavigation.js +0 -17
  53. package/dist/hooks/useHistoryNavigation.d.ts +0 -35
  54. package/dist/hooks/useHistoryNavigation.js +0 -133
  55. package/dist/hooks/useInputBuffer.d.ts +0 -6
  56. package/dist/hooks/useInputBuffer.js +0 -45
  57. package/dist/hooks/useKeyboardInput.d.ts +0 -80
  58. package/dist/hooks/useKeyboardInput.js +0 -608
  59. package/dist/hooks/useSessionManagement.d.ts +0 -10
  60. package/dist/hooks/useSessionManagement.js +0 -43
  61. package/dist/hooks/useSessionSave.d.ts +0 -8
  62. package/dist/hooks/useSessionSave.js +0 -63
  63. package/dist/hooks/useSnapshotState.d.ts +0 -26
  64. package/dist/hooks/useSnapshotState.js +0 -28
  65. package/dist/hooks/useStreamingState.d.ts +0 -33
  66. package/dist/hooks/useStreamingState.js +0 -105
  67. package/dist/hooks/useTerminalFocus.d.ts +0 -28
  68. package/dist/hooks/useTerminalFocus.js +0 -87
  69. package/dist/hooks/useTerminalSize.d.ts +0 -4
  70. package/dist/hooks/useTerminalSize.js +0 -20
  71. package/dist/hooks/useTodoPicker.d.ts +0 -16
  72. package/dist/hooks/useTodoPicker.js +0 -94
  73. package/dist/hooks/useToolConfirmation.d.ts +0 -19
  74. package/dist/hooks/useToolConfirmation.js +0 -61
  75. package/dist/hooks/useVSCodeState.d.ts +0 -8
  76. package/dist/hooks/useVSCodeState.js +0 -81
  77. package/dist/i18n/I18nContext.d.ts +0 -14
  78. package/dist/i18n/I18nContext.js +0 -24
  79. package/dist/i18n/index.d.ts +0 -3
  80. package/dist/i18n/index.js +0 -2
  81. package/dist/i18n/lang/en.d.ts +0 -2
  82. package/dist/i18n/lang/en.js +0 -502
  83. package/dist/i18n/lang/es.d.ts +0 -2
  84. package/dist/i18n/lang/es.js +0 -502
  85. package/dist/i18n/lang/ja.d.ts +0 -2
  86. package/dist/i18n/lang/ja.js +0 -502
  87. package/dist/i18n/lang/ko.d.ts +0 -2
  88. package/dist/i18n/lang/ko.js +0 -502
  89. package/dist/i18n/lang/zh-TW.d.ts +0 -2
  90. package/dist/i18n/lang/zh-TW.js +0 -502
  91. package/dist/i18n/lang/zh.d.ts +0 -2
  92. package/dist/i18n/lang/zh.js +0 -502
  93. package/dist/i18n/translations.d.ts +0 -2
  94. package/dist/i18n/translations.js +0 -14
  95. package/dist/i18n/types.d.ts +0 -478
  96. package/dist/i18n/types.js +0 -1
  97. package/dist/mcp/aceCodeSearch.d.ts +0 -247
  98. package/dist/mcp/aceCodeSearch.js +0 -1058
  99. package/dist/mcp/bash.d.ts +0 -50
  100. package/dist/mcp/bash.js +0 -153
  101. package/dist/mcp/codebaseSearch.d.ts +0 -44
  102. package/dist/mcp/codebaseSearch.js +0 -275
  103. package/dist/mcp/filesystem.d.ts +0 -392
  104. package/dist/mcp/filesystem.js +0 -1445
  105. package/dist/mcp/ideDiagnostics.d.ts +0 -36
  106. package/dist/mcp/ideDiagnostics.js +0 -90
  107. package/dist/mcp/notebook.d.ts +0 -10
  108. package/dist/mcp/notebook.js +0 -367
  109. package/dist/mcp/subagent.d.ts +0 -37
  110. package/dist/mcp/subagent.js +0 -113
  111. package/dist/mcp/todo.d.ts +0 -46
  112. package/dist/mcp/todo.js +0 -511
  113. package/dist/mcp/types/aceCodeSearch.types.d.ts +0 -92
  114. package/dist/mcp/types/aceCodeSearch.types.js +0 -4
  115. package/dist/mcp/types/bash.types.d.ts +0 -13
  116. package/dist/mcp/types/bash.types.js +0 -4
  117. package/dist/mcp/types/filesystem.types.d.ts +0 -210
  118. package/dist/mcp/types/filesystem.types.js +0 -27
  119. package/dist/mcp/types/todo.types.d.ts +0 -27
  120. package/dist/mcp/types/todo.types.js +0 -4
  121. package/dist/mcp/types/websearch.types.d.ts +0 -30
  122. package/dist/mcp/types/websearch.types.js +0 -4
  123. package/dist/mcp/utils/aceCodeSearch/filesystem.utils.d.ts +0 -34
  124. package/dist/mcp/utils/aceCodeSearch/filesystem.utils.js +0 -146
  125. package/dist/mcp/utils/aceCodeSearch/language.utils.d.ts +0 -14
  126. package/dist/mcp/utils/aceCodeSearch/language.utils.js +0 -418
  127. package/dist/mcp/utils/aceCodeSearch/search.utils.d.ts +0 -31
  128. package/dist/mcp/utils/aceCodeSearch/search.utils.js +0 -136
  129. package/dist/mcp/utils/aceCodeSearch/symbol.utils.d.ts +0 -20
  130. package/dist/mcp/utils/aceCodeSearch/symbol.utils.js +0 -141
  131. package/dist/mcp/utils/bash/security.utils.d.ts +0 -20
  132. package/dist/mcp/utils/bash/security.utils.js +0 -34
  133. package/dist/mcp/utils/filesystem/batch-operations.utils.d.ts +0 -39
  134. package/dist/mcp/utils/filesystem/batch-operations.utils.js +0 -182
  135. package/dist/mcp/utils/filesystem/code-analysis.utils.d.ts +0 -18
  136. package/dist/mcp/utils/filesystem/code-analysis.utils.js +0 -165
  137. package/dist/mcp/utils/filesystem/match-finder.utils.d.ts +0 -16
  138. package/dist/mcp/utils/filesystem/match-finder.utils.js +0 -85
  139. package/dist/mcp/utils/filesystem/office-parser.utils.d.ts +0 -43
  140. package/dist/mcp/utils/filesystem/office-parser.utils.js +0 -163
  141. package/dist/mcp/utils/filesystem/path-fixer.utils.d.ts +0 -7
  142. package/dist/mcp/utils/filesystem/path-fixer.utils.js +0 -60
  143. package/dist/mcp/utils/filesystem/similarity.utils.d.ts +0 -22
  144. package/dist/mcp/utils/filesystem/similarity.utils.js +0 -75
  145. package/dist/mcp/utils/todo/date.utils.d.ts +0 -9
  146. package/dist/mcp/utils/todo/date.utils.js +0 -14
  147. package/dist/mcp/utils/websearch/browser.utils.d.ts +0 -8
  148. package/dist/mcp/utils/websearch/browser.utils.js +0 -58
  149. package/dist/mcp/utils/websearch/text.utils.d.ts +0 -16
  150. package/dist/mcp/utils/websearch/text.utils.js +0 -39
  151. package/dist/mcp/websearch.d.ts +0 -88
  152. package/dist/mcp/websearch.js +0 -375
  153. package/dist/test/logger-test.d.ts +0 -1
  154. package/dist/test/logger-test.js +0 -7
  155. package/dist/types/index.d.ts +0 -15
  156. package/dist/types/index.js +0 -1
  157. package/dist/ui/components/AgentPickerPanel.d.ts +0 -10
  158. package/dist/ui/components/AgentPickerPanel.js +0 -74
  159. package/dist/ui/components/ChatInput.d.ts +0 -46
  160. package/dist/ui/components/ChatInput.js +0 -384
  161. package/dist/ui/components/CommandPanel.d.ts +0 -15
  162. package/dist/ui/components/CommandPanel.js +0 -80
  163. package/dist/ui/components/DiffViewer.d.ts +0 -11
  164. package/dist/ui/components/DiffViewer.js +0 -178
  165. package/dist/ui/components/FileList.d.ts +0 -15
  166. package/dist/ui/components/FileList.js +0 -360
  167. package/dist/ui/components/FileRollbackConfirmation.d.ts +0 -8
  168. package/dist/ui/components/FileRollbackConfirmation.js +0 -108
  169. package/dist/ui/components/HelpPanel.d.ts +0 -2
  170. package/dist/ui/components/HelpPanel.js +0 -67
  171. package/dist/ui/components/MCPInfoPanel.d.ts +0 -2
  172. package/dist/ui/components/MCPInfoPanel.js +0 -108
  173. package/dist/ui/components/MCPInfoScreen.d.ts +0 -7
  174. package/dist/ui/components/MCPInfoScreen.js +0 -115
  175. package/dist/ui/components/MarkdownRenderer.d.ts +0 -6
  176. package/dist/ui/components/MarkdownRenderer.js +0 -70
  177. package/dist/ui/components/Menu.d.ts +0 -17
  178. package/dist/ui/components/Menu.js +0 -88
  179. package/dist/ui/components/MessageList.d.ts +0 -56
  180. package/dist/ui/components/MessageList.js +0 -97
  181. package/dist/ui/components/PendingMessages.d.ts +0 -13
  182. package/dist/ui/components/PendingMessages.js +0 -29
  183. package/dist/ui/components/PendingToolCalls.d.ts +0 -11
  184. package/dist/ui/components/PendingToolCalls.js +0 -35
  185. package/dist/ui/components/ScrollableSelectInput.d.ts +0 -29
  186. package/dist/ui/components/ScrollableSelectInput.js +0 -157
  187. package/dist/ui/components/SessionListPanel.d.ts +0 -7
  188. package/dist/ui/components/SessionListPanel.js +0 -175
  189. package/dist/ui/components/SessionListScreen.d.ts +0 -7
  190. package/dist/ui/components/SessionListScreen.js +0 -217
  191. package/dist/ui/components/SessionListScreenWrapper.d.ts +0 -7
  192. package/dist/ui/components/SessionListScreenWrapper.js +0 -14
  193. package/dist/ui/components/ShimmerText.d.ts +0 -9
  194. package/dist/ui/components/ShimmerText.js +0 -30
  195. package/dist/ui/components/TodoPickerPanel.d.ts +0 -14
  196. package/dist/ui/components/TodoPickerPanel.js +0 -119
  197. package/dist/ui/components/TodoTree.d.ts +0 -15
  198. package/dist/ui/components/TodoTree.js +0 -60
  199. package/dist/ui/components/ToolConfirmation.d.ts +0 -21
  200. package/dist/ui/components/ToolConfirmation.js +0 -204
  201. package/dist/ui/components/ToolResultPreview.d.ts +0 -13
  202. package/dist/ui/components/ToolResultPreview.js +0 -337
  203. package/dist/ui/components/UsagePanel.d.ts +0 -2
  204. package/dist/ui/components/UsagePanel.js +0 -394
  205. package/dist/ui/contexts/ThemeContext.d.ts +0 -13
  206. package/dist/ui/contexts/ThemeContext.js +0 -28
  207. package/dist/ui/pages/ChatScreen.d.ts +0 -6
  208. package/dist/ui/pages/ChatScreen.js +0 -1519
  209. package/dist/ui/pages/CodeBaseConfigScreen.d.ts +0 -8
  210. package/dist/ui/pages/CodeBaseConfigScreen.js +0 -350
  211. package/dist/ui/pages/ConfigScreen.d.ts +0 -8
  212. package/dist/ui/pages/ConfigScreen.js +0 -1101
  213. package/dist/ui/pages/CustomHeadersScreen.d.ts +0 -6
  214. package/dist/ui/pages/CustomHeadersScreen.js +0 -502
  215. package/dist/ui/pages/HeadlessModeScreen.d.ts +0 -7
  216. package/dist/ui/pages/HeadlessModeScreen.js +0 -381
  217. package/dist/ui/pages/LanguageSettingsScreen.d.ts +0 -7
  218. package/dist/ui/pages/LanguageSettingsScreen.js +0 -91
  219. package/dist/ui/pages/MCPConfigScreen.d.ts +0 -6
  220. package/dist/ui/pages/MCPConfigScreen.js +0 -55
  221. package/dist/ui/pages/ProxyConfigScreen.d.ts +0 -8
  222. package/dist/ui/pages/ProxyConfigScreen.js +0 -149
  223. package/dist/ui/pages/SensitiveCommandConfigScreen.d.ts +0 -7
  224. package/dist/ui/pages/SensitiveCommandConfigScreen.js +0 -271
  225. package/dist/ui/pages/SubAgentConfigScreen.d.ts +0 -9
  226. package/dist/ui/pages/SubAgentConfigScreen.js +0 -435
  227. package/dist/ui/pages/SubAgentListScreen.d.ts +0 -9
  228. package/dist/ui/pages/SubAgentListScreen.js +0 -131
  229. package/dist/ui/pages/SystemPromptConfigScreen.d.ts +0 -6
  230. package/dist/ui/pages/SystemPromptConfigScreen.js +0 -326
  231. package/dist/ui/pages/ThemeSettingsScreen.d.ts +0 -7
  232. package/dist/ui/pages/ThemeSettingsScreen.js +0 -106
  233. package/dist/ui/pages/WelcomeScreen.d.ts +0 -7
  234. package/dist/ui/pages/WelcomeScreen.js +0 -217
  235. package/dist/ui/themes/index.d.ts +0 -23
  236. package/dist/ui/themes/index.js +0 -140
  237. package/dist/utils/apiConfig.d.ts +0 -126
  238. package/dist/utils/apiConfig.js +0 -423
  239. package/dist/utils/autoCompress.d.ts +0 -15
  240. package/dist/utils/autoCompress.js +0 -24
  241. package/dist/utils/chatExporter.d.ts +0 -9
  242. package/dist/utils/chatExporter.js +0 -118
  243. package/dist/utils/checkpointManager.d.ts +0 -74
  244. package/dist/utils/checkpointManager.js +0 -181
  245. package/dist/utils/codebaseConfig.d.ts +0 -16
  246. package/dist/utils/codebaseConfig.js +0 -67
  247. package/dist/utils/codebaseDatabase.d.ts +0 -102
  248. package/dist/utils/codebaseDatabase.js +0 -333
  249. package/dist/utils/codebaseSearchEvents.d.ts +0 -16
  250. package/dist/utils/codebaseSearchEvents.js +0 -13
  251. package/dist/utils/commandExecutor.d.ts +0 -13
  252. package/dist/utils/commandExecutor.js +0 -26
  253. package/dist/utils/commands/agent.d.ts +0 -2
  254. package/dist/utils/commands/agent.js +0 -12
  255. package/dist/utils/commands/clear.d.ts +0 -2
  256. package/dist/utils/commands/clear.js +0 -12
  257. package/dist/utils/commands/compact.d.ts +0 -2
  258. package/dist/utils/commands/compact.js +0 -12
  259. package/dist/utils/commands/export.d.ts +0 -2
  260. package/dist/utils/commands/export.js +0 -12
  261. package/dist/utils/commands/help.d.ts +0 -2
  262. package/dist/utils/commands/help.js +0 -11
  263. package/dist/utils/commands/home.d.ts +0 -2
  264. package/dist/utils/commands/home.js +0 -34
  265. package/dist/utils/commands/ide.d.ts +0 -2
  266. package/dist/utils/commands/ide.js +0 -32
  267. package/dist/utils/commands/init.d.ts +0 -2
  268. package/dist/utils/commands/init.js +0 -93
  269. package/dist/utils/commands/mcp.d.ts +0 -2
  270. package/dist/utils/commands/mcp.js +0 -12
  271. package/dist/utils/commands/resume.d.ts +0 -2
  272. package/dist/utils/commands/resume.js +0 -12
  273. package/dist/utils/commands/review.d.ts +0 -2
  274. package/dist/utils/commands/review.js +0 -81
  275. package/dist/utils/commands/role.d.ts +0 -2
  276. package/dist/utils/commands/role.js +0 -37
  277. package/dist/utils/commands/todoPicker.d.ts +0 -2
  278. package/dist/utils/commands/todoPicker.js +0 -12
  279. package/dist/utils/commands/usage.d.ts +0 -2
  280. package/dist/utils/commands/usage.js +0 -12
  281. package/dist/utils/commands/yolo.d.ts +0 -2
  282. package/dist/utils/commands/yolo.js +0 -12
  283. package/dist/utils/configManager.d.ts +0 -45
  284. package/dist/utils/configManager.js +0 -303
  285. package/dist/utils/contextCompressor.d.ts +0 -16
  286. package/dist/utils/contextCompressor.js +0 -334
  287. package/dist/utils/devMode.d.ts +0 -13
  288. package/dist/utils/devMode.js +0 -54
  289. package/dist/utils/escapeHandler.d.ts +0 -79
  290. package/dist/utils/escapeHandler.js +0 -153
  291. package/dist/utils/fileDialog.d.ts +0 -9
  292. package/dist/utils/fileDialog.js +0 -74
  293. package/dist/utils/fileUtils.d.ts +0 -40
  294. package/dist/utils/fileUtils.js +0 -185
  295. package/dist/utils/historyManager.d.ts +0 -45
  296. package/dist/utils/historyManager.js +0 -159
  297. package/dist/utils/incrementalSnapshot.d.ts +0 -109
  298. package/dist/utils/incrementalSnapshot.js +0 -383
  299. package/dist/utils/index.d.ts +0 -11
  300. package/dist/utils/index.js +0 -18
  301. package/dist/utils/languageConfig.d.ts +0 -21
  302. package/dist/utils/languageConfig.js +0 -61
  303. package/dist/utils/logger.d.ts +0 -37
  304. package/dist/utils/logger.js +0 -122
  305. package/dist/utils/mcpToolsManager.d.ts +0 -52
  306. package/dist/utils/mcpToolsManager.js +0 -878
  307. package/dist/utils/messageFormatter.d.ts +0 -12
  308. package/dist/utils/messageFormatter.js +0 -115
  309. package/dist/utils/notebookManager.d.ts +0 -59
  310. package/dist/utils/notebookManager.js +0 -213
  311. package/dist/utils/patch-highlight.d.ts +0 -5
  312. package/dist/utils/patch-highlight.js +0 -23
  313. package/dist/utils/processManager.d.ts +0 -27
  314. package/dist/utils/processManager.js +0 -75
  315. package/dist/utils/proxyUtils.d.ts +0 -15
  316. package/dist/utils/proxyUtils.js +0 -50
  317. package/dist/utils/resourceMonitor.d.ts +0 -65
  318. package/dist/utils/resourceMonitor.js +0 -175
  319. package/dist/utils/retryUtils.d.ts +0 -49
  320. package/dist/utils/retryUtils.js +0 -303
  321. package/dist/utils/sensitiveCommandManager.d.ts +0 -53
  322. package/dist/utils/sensitiveCommandManager.js +0 -308
  323. package/dist/utils/sessionConverter.d.ts +0 -7
  324. package/dist/utils/sessionConverter.js +0 -306
  325. package/dist/utils/sessionManager.d.ts +0 -53
  326. package/dist/utils/sessionManager.js +0 -371
  327. package/dist/utils/subAgentConfig.d.ts +0 -50
  328. package/dist/utils/subAgentConfig.js +0 -221
  329. package/dist/utils/subAgentExecutor.d.ts +0 -40
  330. package/dist/utils/subAgentExecutor.js +0 -434
  331. package/dist/utils/terminal.d.ts +0 -5
  332. package/dist/utils/terminal.js +0 -13
  333. package/dist/utils/textBuffer.d.ts +0 -99
  334. package/dist/utils/textBuffer.js +0 -547
  335. package/dist/utils/textUtils.d.ts +0 -37
  336. package/dist/utils/textUtils.js +0 -102
  337. package/dist/utils/themeConfig.d.ts +0 -21
  338. package/dist/utils/themeConfig.js +0 -61
  339. package/dist/utils/todoPreprocessor.d.ts +0 -5
  340. package/dist/utils/todoPreprocessor.js +0 -18
  341. package/dist/utils/todoScanner.d.ts +0 -8
  342. package/dist/utils/todoScanner.js +0 -148
  343. package/dist/utils/toolDisplayConfig.d.ts +0 -16
  344. package/dist/utils/toolDisplayConfig.js +0 -47
  345. package/dist/utils/toolExecutor.d.ts +0 -37
  346. package/dist/utils/toolExecutor.js +0 -224
  347. package/dist/utils/usageLogger.d.ts +0 -11
  348. package/dist/utils/usageLogger.js +0 -114
  349. package/dist/utils/vscodeConnection.d.ts +0 -76
  350. package/dist/utils/vscodeConnection.js +0 -430
  351. package/dist/utils/workspaceSnapshot.d.ts +0 -63
  352. 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
- }