@qduc/term2 0.1.3 → 0.1.5

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 (375) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +5 -0
  3. package/dist/agent.js.map +1 -1
  4. package/dist/app.d.ts.map +1 -1
  5. package/dist/app.js +2 -2
  6. package/dist/app.js.map +1 -1
  7. package/dist/app.model-command-feedback.test.d.ts +2 -0
  8. package/dist/app.model-command-feedback.test.d.ts.map +1 -0
  9. package/dist/app.model-command-feedback.test.js +19 -0
  10. package/dist/app.model-command-feedback.test.js.map +1 -0
  11. package/dist/cli.js +2 -1
  12. package/dist/cli.js.map +1 -1
  13. package/dist/components/BottomArea.d.ts +2 -0
  14. package/dist/components/BottomArea.d.ts.map +1 -1
  15. package/dist/components/BottomArea.js +2 -2
  16. package/dist/components/BottomArea.js.map +1 -1
  17. package/dist/components/ChatMessage.js +1 -1
  18. package/dist/components/ChatMessage.js.map +1 -1
  19. package/dist/components/InputBox.js +1 -1
  20. package/dist/components/InputBox.js.map +1 -1
  21. package/dist/components/MarkdownRenderer.js +1 -1
  22. package/dist/components/MarkdownRenderer.js.map +1 -1
  23. package/dist/components/MentorMode.test.d.ts +2 -0
  24. package/dist/components/MentorMode.test.d.ts.map +1 -0
  25. package/dist/components/MentorMode.test.js.map +1 -0
  26. package/dist/components/ModelSelectionMenu.d.ts.map +1 -1
  27. package/dist/components/ModelSelectionMenu.error-tabs.test.d.ts +2 -0
  28. package/dist/components/ModelSelectionMenu.error-tabs.test.d.ts.map +1 -0
  29. package/dist/components/ModelSelectionMenu.error-tabs.test.js +18 -0
  30. package/dist/components/ModelSelectionMenu.error-tabs.test.js.map +1 -0
  31. package/dist/components/ModelSelectionMenu.js +24 -15
  32. package/dist/components/ModelSelectionMenu.js.map +1 -1
  33. package/dist/components/SettingsSelectionMenu.js +1 -1
  34. package/dist/components/SettingsSelectionMenu.js.map +1 -1
  35. package/dist/components/SlashCommandMenu.js +2 -2
  36. package/dist/components/SlashCommandMenu.js.map +1 -1
  37. package/dist/components/StatusBar.d.ts +2 -0
  38. package/dist/components/StatusBar.d.ts.map +1 -1
  39. package/dist/components/StatusBar.js +44 -39
  40. package/dist/components/StatusBar.js.map +1 -1
  41. package/dist/components/StatusBar.test.d.ts +2 -0
  42. package/dist/components/StatusBar.test.d.ts.map +1 -0
  43. package/dist/components/StatusBar.test.js +19 -0
  44. package/dist/components/StatusBar.test.js.map +1 -0
  45. package/dist/components/TextInput.d.ts.map +1 -1
  46. package/dist/components/TextInput.js +1 -2
  47. package/dist/components/TextInput.js.map +1 -1
  48. package/dist/components/TextInput.test.js.map +1 -1
  49. package/dist/debug-schema.d.ts +2 -0
  50. package/dist/debug-schema.d.ts.map +1 -0
  51. package/dist/debug-schema.js +22 -0
  52. package/dist/debug-schema.js.map +1 -0
  53. package/dist/hooks/use-conversation.d.ts +2 -0
  54. package/dist/hooks/use-conversation.d.ts.map +1 -1
  55. package/dist/hooks/use-conversation.js +79 -493
  56. package/dist/hooks/use-conversation.js.map +1 -1
  57. package/dist/hooks/use-model-selection.d.ts.map +1 -1
  58. package/dist/hooks/use-model-selection.js +1 -25
  59. package/dist/hooks/use-model-selection.js.map +1 -1
  60. package/dist/hooks/use-model-selection.test.d.ts +2 -0
  61. package/dist/hooks/use-model-selection.test.d.ts.map +1 -0
  62. package/dist/hooks/use-model-selection.test.js +28 -0
  63. package/dist/hooks/use-model-selection.test.js.map +1 -0
  64. package/dist/hooks/use-path-completion.test.d.ts +2 -0
  65. package/dist/hooks/use-path-completion.test.d.ts.map +1 -0
  66. package/dist/hooks/use-path-completion.test.js +29 -0
  67. package/dist/hooks/use-path-completion.test.js.map +1 -0
  68. package/dist/hooks/use-settings-completion.d.ts.map +1 -1
  69. package/dist/hooks/use-settings-completion.js +2 -0
  70. package/dist/hooks/use-settings-completion.js.map +1 -1
  71. package/dist/hooks/use-settings-value-completion.d.ts.map +1 -1
  72. package/dist/hooks/use-settings-value-completion.js +1 -0
  73. package/dist/hooks/use-settings-value-completion.js.map +1 -1
  74. package/dist/lib/editor-impl.test.d.ts +2 -0
  75. package/dist/lib/editor-impl.test.d.ts.map +1 -0
  76. package/dist/lib/editor-impl.test.js +188 -0
  77. package/dist/lib/editor-impl.test.js.map +1 -0
  78. package/dist/lib/openai-agent-client.d.ts.map +1 -1
  79. package/dist/lib/openai-agent-client.js +12 -4
  80. package/dist/lib/openai-agent-client.js.map +1 -1
  81. package/dist/lib/openai-agent-client.public-methods.test.d.ts +2 -0
  82. package/dist/lib/openai-agent-client.public-methods.test.d.ts.map +1 -0
  83. package/dist/lib/openai-agent-client.public-methods.test.js +188 -0
  84. package/dist/lib/openai-agent-client.public-methods.test.js.map +1 -0
  85. package/dist/prompts/default.md.bak +77 -0
  86. package/dist/prompts/simple-mentor.md +0 -8
  87. package/dist/providers/github-copilot/converters.d.ts +45 -0
  88. package/dist/providers/github-copilot/converters.d.ts.map +1 -0
  89. package/dist/providers/github-copilot/converters.js +118 -0
  90. package/dist/providers/github-copilot/converters.js.map +1 -0
  91. package/dist/providers/github-copilot/converters.test.d.ts +2 -0
  92. package/dist/providers/github-copilot/converters.test.d.ts.map +1 -0
  93. package/dist/providers/github-copilot/converters.test.js +162 -0
  94. package/dist/providers/github-copilot/converters.test.js.map +1 -0
  95. package/dist/providers/github-copilot/github-copilot.provider.d.ts +2 -0
  96. package/dist/providers/github-copilot/github-copilot.provider.d.ts.map +1 -0
  97. package/dist/providers/github-copilot/github-copilot.provider.js +75 -0
  98. package/dist/providers/github-copilot/github-copilot.provider.js.map +1 -0
  99. package/dist/providers/github-copilot/github-copilot.provider.test.d.ts +2 -0
  100. package/dist/providers/github-copilot/github-copilot.provider.test.d.ts.map +1 -0
  101. package/dist/providers/github-copilot/github-copilot.provider.test.js +26 -0
  102. package/dist/providers/github-copilot/github-copilot.provider.test.js.map +1 -0
  103. package/dist/providers/github-copilot/index.d.ts +4 -0
  104. package/dist/providers/github-copilot/index.d.ts.map +1 -0
  105. package/dist/providers/github-copilot/index.js +4 -0
  106. package/dist/providers/github-copilot/index.js.map +1 -0
  107. package/dist/providers/github-copilot/model-direct.d.ts +34 -0
  108. package/dist/providers/github-copilot/model-direct.d.ts.map +1 -0
  109. package/dist/providers/github-copilot/model-direct.js +443 -0
  110. package/dist/providers/github-copilot/model-direct.js.map +1 -0
  111. package/dist/providers/github-copilot/model.d.ts +24 -0
  112. package/dist/providers/github-copilot/model.d.ts.map +1 -0
  113. package/dist/providers/github-copilot/model.delta.test.d.ts +2 -0
  114. package/dist/providers/github-copilot/model.delta.test.d.ts.map +1 -0
  115. package/dist/providers/github-copilot/model.delta.test.js +15 -0
  116. package/dist/providers/github-copilot/model.delta.test.js.map +1 -0
  117. package/dist/providers/github-copilot/model.js +581 -0
  118. package/dist/providers/github-copilot/model.js.map +1 -0
  119. package/dist/providers/github-copilot/provider.d.ts +20 -0
  120. package/dist/providers/github-copilot/provider.d.ts.map +1 -0
  121. package/dist/providers/github-copilot/provider.js +30 -0
  122. package/dist/providers/github-copilot/provider.js.map +1 -0
  123. package/dist/providers/github-copilot/provider.test.d.ts +2 -0
  124. package/dist/providers/github-copilot/provider.test.d.ts.map +1 -0
  125. package/dist/providers/github-copilot/provider.test.js +52 -0
  126. package/dist/providers/github-copilot/provider.test.js.map +1 -0
  127. package/dist/providers/github-copilot/utils.d.ts +20 -0
  128. package/dist/providers/github-copilot/utils.d.ts.map +1 -0
  129. package/dist/providers/github-copilot/utils.js +142 -0
  130. package/dist/providers/github-copilot/utils.js.map +1 -0
  131. package/dist/providers/github-copilot/utils.test.d.ts +2 -0
  132. package/dist/providers/github-copilot/utils.test.d.ts.map +1 -0
  133. package/dist/providers/github-copilot/utils.test.js +21 -0
  134. package/dist/providers/github-copilot/utils.test.js.map +1 -0
  135. package/dist/providers/openai-compatible/reasoning-content.test.js.map +1 -1
  136. package/dist/providers/openrouter/converters.d.ts.map +1 -1
  137. package/dist/providers/openrouter/converters.js +64 -46
  138. package/dist/providers/openrouter/converters.js.map +1 -1
  139. package/dist/providers/openrouter/converters.test.js +13 -12
  140. package/dist/providers/openrouter/converters.test.js.map +1 -1
  141. package/dist/providers/openrouter/merge-messages.test.d.ts +2 -0
  142. package/dist/providers/openrouter/merge-messages.test.d.ts.map +1 -0
  143. package/dist/providers/openrouter/merge-messages.test.js +83 -0
  144. package/dist/providers/openrouter/merge-messages.test.js.map +1 -0
  145. package/dist/providers/openrouter/reasoning-content.test.js.map +1 -1
  146. package/dist/providers/openrouter.history.test.d.ts +2 -0
  147. package/dist/providers/openrouter.history.test.d.ts.map +1 -0
  148. package/dist/providers/openrouter.history.test.js +533 -0
  149. package/dist/providers/openrouter.history.test.js.map +1 -0
  150. package/dist/providers/openrouter.test.js.map +1 -1
  151. package/dist/reproduce_issue.test.d.ts +2 -0
  152. package/dist/reproduce_issue.test.d.ts.map +1 -0
  153. package/dist/reproduce_issue.test.js +31 -0
  154. package/dist/reproduce_issue.test.js.map +1 -0
  155. package/dist/services/conversation-events.d.ts +3 -0
  156. package/dist/services/conversation-events.d.ts.map +1 -1
  157. package/dist/services/conversation-service.test.js +183 -149
  158. package/dist/services/conversation-session.d.ts +2 -0
  159. package/dist/services/conversation-session.d.ts.map +1 -1
  160. package/dist/services/conversation-session.js +151 -28
  161. package/dist/services/conversation-session.js.map +1 -1
  162. package/dist/services/conversation-session.usage.test.d.ts +2 -0
  163. package/dist/services/conversation-session.usage.test.d.ts.map +1 -0
  164. package/dist/services/conversation-session.usage.test.js +59 -0
  165. package/dist/services/conversation-session.usage.test.js.map +1 -0
  166. package/dist/services/settings-service.d.ts +9 -3
  167. package/dist/services/settings-service.d.ts.map +1 -1
  168. package/dist/services/settings-service.js +14 -0
  169. package/dist/services/settings-service.js.map +1 -1
  170. package/dist/test-search-tool.d.ts +2 -0
  171. package/dist/test-search-tool.d.ts.map +1 -0
  172. package/dist/test-search-tool.js +36 -0
  173. package/dist/test-search-tool.js.map +1 -0
  174. package/dist/tools/edit-healing.d.ts +24 -0
  175. package/dist/tools/edit-healing.d.ts.map +1 -0
  176. package/dist/tools/edit-healing.js +230 -0
  177. package/dist/tools/edit-healing.js.map +1 -0
  178. package/dist/tools/edit-healing.test.d.ts +2 -0
  179. package/dist/tools/edit-healing.test.d.ts.map +1 -0
  180. package/dist/tools/edit-healing.test.js +34 -0
  181. package/dist/tools/edit-healing.test.js.map +1 -0
  182. package/dist/tools/find-files.d.ts +1 -0
  183. package/dist/tools/find-files.d.ts.map +1 -1
  184. package/dist/tools/find-files.js +92 -8
  185. package/dist/tools/find-files.js.map +1 -1
  186. package/dist/tools/find-files.test.js +45 -0
  187. package/dist/tools/find-files.test.js.map +1 -1
  188. package/dist/tools/search-replace.d.ts +2 -0
  189. package/dist/tools/search-replace.d.ts.map +1 -1
  190. package/dist/tools/search-replace.js +207 -23
  191. package/dist/tools/search-replace.js.map +1 -1
  192. package/dist/tools/search-replace.test.js +223 -2
  193. package/dist/tools/search-replace.test.js.map +1 -1
  194. package/dist/tools/web-fetch.d.ts +20 -0
  195. package/dist/tools/web-fetch.d.ts.map +1 -0
  196. package/dist/tools/web-fetch.js +87 -0
  197. package/dist/tools/web-fetch.js.map +1 -0
  198. package/dist/tools/web-fetch.test.d.ts +2 -0
  199. package/dist/tools/web-fetch.test.d.ts.map +1 -0
  200. package/dist/tools/web-fetch.test.js +94 -0
  201. package/dist/tools/web-fetch.test.js.map +1 -0
  202. package/dist/utils/command-safety/index.d.ts +2 -2
  203. package/dist/utils/command-safety/index.d.ts.map +1 -1
  204. package/dist/utils/command-safety/index.js +7 -6
  205. package/dist/utils/command-safety/index.js.map +1 -1
  206. package/dist/utils/command-safety.d.ts.map +1 -1
  207. package/dist/utils/command-safety.find.test.js +19 -21
  208. package/dist/utils/command-safety.find.test.js.map +1 -1
  209. package/dist/utils/command-safety.js +364 -10
  210. package/dist/utils/command-safety.js.map +1 -1
  211. package/dist/utils/conversation-event-handler.d.ts +63 -0
  212. package/dist/utils/conversation-event-handler.d.ts.map +1 -0
  213. package/dist/utils/conversation-event-handler.js +132 -0
  214. package/dist/utils/conversation-event-handler.js.map +1 -0
  215. package/dist/utils/conversation-event-handler.test.d.ts +2 -0
  216. package/dist/utils/conversation-event-handler.test.d.ts.map +1 -0
  217. package/dist/utils/conversation-event-handler.test.js +281 -0
  218. package/dist/utils/conversation-event-handler.test.js.map +1 -0
  219. package/dist/utils/conversation-utils.d.ts +41 -0
  220. package/dist/utils/conversation-utils.d.ts.map +1 -0
  221. package/dist/utils/conversation-utils.js +109 -0
  222. package/dist/utils/conversation-utils.js.map +1 -0
  223. package/dist/utils/conversation-utils.test.d.ts +2 -0
  224. package/dist/utils/conversation-utils.test.d.ts.map +1 -0
  225. package/dist/utils/conversation-utils.test.js +190 -0
  226. package/dist/utils/conversation-utils.test.js.map +1 -0
  227. package/dist/utils/extract-command-messages.repro.test.d.ts +2 -0
  228. package/dist/utils/extract-command-messages.repro.test.d.ts.map +1 -0
  229. package/dist/utils/extract-command-messages.repro.test.js +31 -0
  230. package/dist/utils/extract-command-messages.repro.test.js.map +1 -0
  231. package/dist/utils/extract-command-messages.test.js +51 -58
  232. package/dist/utils/ink-render-options.d.ts +9 -0
  233. package/dist/utils/ink-render-options.d.ts.map +1 -0
  234. package/dist/utils/ink-render-options.js +8 -0
  235. package/dist/utils/ink-render-options.js.map +1 -0
  236. package/dist/utils/message-utils.d.ts +17 -0
  237. package/dist/utils/message-utils.d.ts.map +1 -0
  238. package/dist/utils/message-utils.js +52 -0
  239. package/dist/utils/message-utils.js.map +1 -0
  240. package/dist/utils/message-utils.test.d.ts +2 -0
  241. package/dist/utils/message-utils.test.d.ts.map +1 -0
  242. package/dist/utils/message-utils.test.js +48 -0
  243. package/dist/utils/message-utils.test.js.map +1 -0
  244. package/dist/utils/token-usage.d.ts +16 -0
  245. package/dist/utils/token-usage.d.ts.map +1 -0
  246. package/dist/utils/token-usage.js +110 -0
  247. package/dist/utils/token-usage.js.map +1 -0
  248. package/dist/utils/token-usage.test.d.ts +2 -0
  249. package/dist/utils/token-usage.test.d.ts.map +1 -0
  250. package/dist/utils/token-usage.test.js +38 -0
  251. package/dist/utils/token-usage.test.js.map +1 -0
  252. package/dist/utils/trim-tool-output.d.ts +2 -0
  253. package/dist/utils/trim-tool-output.d.ts.map +1 -0
  254. package/dist/utils/trim-tool-output.js +52 -0
  255. package/dist/utils/trim-tool-output.js.map +1 -0
  256. package/package.json +10 -3
  257. package/readme.md +22 -1
  258. package/dist/agent.lite-mode.test.d.ts +0 -2
  259. package/dist/agent.lite-mode.test.d.ts.map +0 -1
  260. package/dist/agent.lite-mode.test.js +0 -39
  261. package/dist/agent.lite-mode.test.js.map +0 -1
  262. package/dist/debug_ask_mentor.d.ts +0 -2
  263. package/dist/debug_ask_mentor.d.ts.map +0 -1
  264. package/dist/debug_ask_mentor.js +0 -73
  265. package/dist/debug_ask_mentor.js.map +0 -1
  266. package/dist/modes/companion/command-index.d.ts +0 -26
  267. package/dist/modes/companion/command-index.d.ts.map +0 -1
  268. package/dist/modes/companion/command-index.js +0 -50
  269. package/dist/modes/companion/command-index.js.map +0 -1
  270. package/dist/modes/companion/command-index.test.d.ts +0 -2
  271. package/dist/modes/companion/command-index.test.d.ts.map +0 -1
  272. package/dist/modes/companion/command-index.test.js +0 -86
  273. package/dist/modes/companion/command-index.test.js.map +0 -1
  274. package/dist/modes/companion/companion-app.d.ts +0 -12
  275. package/dist/modes/companion/companion-app.d.ts.map +0 -1
  276. package/dist/modes/companion/companion-app.js +0 -297
  277. package/dist/modes/companion/companion-app.js.map +0 -1
  278. package/dist/modes/companion/companion-session.d.ts +0 -63
  279. package/dist/modes/companion/companion-session.d.ts.map +0 -1
  280. package/dist/modes/companion/companion-session.js +0 -146
  281. package/dist/modes/companion/companion-session.js.map +0 -1
  282. package/dist/modes/companion/companion-session.test.d.ts +0 -2
  283. package/dist/modes/companion/companion-session.test.d.ts.map +0 -1
  284. package/dist/modes/companion/companion-session.test.js +0 -28
  285. package/dist/modes/companion/companion-session.test.js.map +0 -1
  286. package/dist/modes/companion/components/status-bar.d.ts +0 -13
  287. package/dist/modes/companion/components/status-bar.d.ts.map +0 -1
  288. package/dist/modes/companion/components/status-bar.js +0 -26
  289. package/dist/modes/companion/components/status-bar.js.map +0 -1
  290. package/dist/modes/companion/context-buffer.d.ts +0 -65
  291. package/dist/modes/companion/context-buffer.d.ts.map +0 -1
  292. package/dist/modes/companion/context-buffer.js +0 -156
  293. package/dist/modes/companion/context-buffer.js.map +0 -1
  294. package/dist/modes/companion/context-buffer.test.d.ts +0 -2
  295. package/dist/modes/companion/context-buffer.test.d.ts.map +0 -1
  296. package/dist/modes/companion/context-buffer.test.js +0 -154
  297. package/dist/modes/companion/context-buffer.test.js.map +0 -1
  298. package/dist/modes/companion/event-detector.d.ts +0 -46
  299. package/dist/modes/companion/event-detector.d.ts.map +0 -1
  300. package/dist/modes/companion/event-detector.js +0 -169
  301. package/dist/modes/companion/event-detector.js.map +0 -1
  302. package/dist/modes/companion/event-detector.test.d.ts +0 -2
  303. package/dist/modes/companion/event-detector.test.d.ts.map +0 -1
  304. package/dist/modes/companion/event-detector.test.js +0 -121
  305. package/dist/modes/companion/event-detector.test.js.map +0 -1
  306. package/dist/modes/companion/index.d.ts +0 -33
  307. package/dist/modes/companion/index.d.ts.map +0 -1
  308. package/dist/modes/companion/index.js +0 -21
  309. package/dist/modes/companion/index.js.map +0 -1
  310. package/dist/modes/companion/input-key-mapper.d.ts +0 -3
  311. package/dist/modes/companion/input-key-mapper.d.ts.map +0 -1
  312. package/dist/modes/companion/input-key-mapper.js +0 -31
  313. package/dist/modes/companion/input-key-mapper.js.map +0 -1
  314. package/dist/modes/companion/input-key-mapper.test.d.ts +0 -2
  315. package/dist/modes/companion/input-key-mapper.test.d.ts.map +0 -1
  316. package/dist/modes/companion/input-key-mapper.test.js +0 -26
  317. package/dist/modes/companion/input-key-mapper.test.js.map +0 -1
  318. package/dist/modes/companion/input-parser.d.ts +0 -53
  319. package/dist/modes/companion/input-parser.d.ts.map +0 -1
  320. package/dist/modes/companion/input-parser.js +0 -114
  321. package/dist/modes/companion/input-parser.js.map +0 -1
  322. package/dist/modes/companion/input-parser.test.d.ts +0 -2
  323. package/dist/modes/companion/input-parser.test.d.ts.map +0 -1
  324. package/dist/modes/companion/input-parser.test.js +0 -123
  325. package/dist/modes/companion/input-parser.test.js.map +0 -1
  326. package/dist/modes/companion/mode-manager.d.ts +0 -41
  327. package/dist/modes/companion/mode-manager.d.ts.map +0 -1
  328. package/dist/modes/companion/mode-manager.js +0 -56
  329. package/dist/modes/companion/mode-manager.js.map +0 -1
  330. package/dist/modes/companion/mode-manager.test.d.ts +0 -2
  331. package/dist/modes/companion/mode-manager.test.d.ts.map +0 -1
  332. package/dist/modes/companion/mode-manager.test.js +0 -65
  333. package/dist/modes/companion/mode-manager.test.js.map +0 -1
  334. package/dist/modes/companion/output-classifier.d.ts +0 -15
  335. package/dist/modes/companion/output-classifier.d.ts.map +0 -1
  336. package/dist/modes/companion/output-classifier.js +0 -77
  337. package/dist/modes/companion/output-classifier.js.map +0 -1
  338. package/dist/modes/companion/output-classifier.test.d.ts +0 -2
  339. package/dist/modes/companion/output-classifier.test.d.ts.map +0 -1
  340. package/dist/modes/companion/output-classifier.test.js +0 -133
  341. package/dist/modes/companion/output-classifier.test.js.map +0 -1
  342. package/dist/modes/companion/pty-wrapper.d.ts +0 -46
  343. package/dist/modes/companion/pty-wrapper.d.ts.map +0 -1
  344. package/dist/modes/companion/pty-wrapper.js +0 -143
  345. package/dist/modes/companion/pty-wrapper.js.map +0 -1
  346. package/dist/modes/companion/safety-classifier.d.ts +0 -31
  347. package/dist/modes/companion/safety-classifier.d.ts.map +0 -1
  348. package/dist/modes/companion/safety-classifier.js +0 -140
  349. package/dist/modes/companion/safety-classifier.js.map +0 -1
  350. package/dist/modes/companion/safety-classifier.test.d.ts +0 -2
  351. package/dist/modes/companion/safety-classifier.test.d.ts.map +0 -1
  352. package/dist/modes/companion/safety-classifier.test.js +0 -151
  353. package/dist/modes/companion/safety-classifier.test.js.map +0 -1
  354. package/dist/modes/companion/summarizer.d.ts +0 -24
  355. package/dist/modes/companion/summarizer.d.ts.map +0 -1
  356. package/dist/modes/companion/summarizer.js +0 -132
  357. package/dist/modes/companion/summarizer.js.map +0 -1
  358. package/dist/modes/companion/terminal-history.d.ts +0 -27
  359. package/dist/modes/companion/terminal-history.d.ts.map +0 -1
  360. package/dist/modes/companion/terminal-history.js +0 -142
  361. package/dist/modes/companion/terminal-history.js.map +0 -1
  362. package/dist/services/check_mock.d.ts +0 -2
  363. package/dist/services/check_mock.d.ts.map +0 -1
  364. package/dist/services/check_mock.js +0 -22
  365. package/dist/services/check_mock.js.map +0 -1
  366. package/dist/services/conversation-service.test.d.ts +0 -2
  367. package/dist/services/conversation-service.test.d.ts.map +0 -1
  368. package/dist/services/conversation-service.test.js.map +0 -1
  369. package/dist/utils/command-safety.devnull.test.d.ts +0 -2
  370. package/dist/utils/command-safety.devnull.test.d.ts.map +0 -1
  371. package/dist/utils/command-safety.devnull.test.js +0 -13
  372. package/dist/utils/command-safety.devnull.test.js.map +0 -1
  373. package/dist/utils/extract-command-messages.test.d.ts +0 -2
  374. package/dist/utils/extract-command-messages.test.d.ts.map +0 -1
  375. package/dist/utils/extract-command-messages.test.js.map +0 -1
@@ -2,6 +2,8 @@ import { useCallback, useRef, useState } from 'react';
2
2
  import { isAbortLikeError } from '../utils/error-helpers.js';
3
3
  import { createStreamingUpdateCoordinator } from '../utils/streaming-updater.js';
4
4
  import { appendMessagesCapped } from '../utils/message-buffer.js';
5
+ import { createStreamingState, enhanceApiKeyError, isMaxTurnsError, } from '../utils/conversation-utils.js';
6
+ import { createConversationEventHandler } from '../utils/conversation-event-handler.js';
5
7
  const LIVE_RESPONSE_THROTTLE_MS = 150;
6
8
  const REASONING_RESPONSE_THROTTLE_MS = 200;
7
9
  const MAX_MESSAGE_COUNT = 300;
@@ -33,6 +35,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
33
35
  const [pendingApproval, setPendingApproval] = useState(null);
34
36
  const [isProcessing, setIsProcessing] = useState(false);
35
37
  const [liveResponse, setLiveResponse] = useState(null);
38
+ const [lastUsage, setLastUsage] = useState(null);
36
39
  const approvedContextRef = useRef(null);
37
40
  const createLiveResponseUpdater = useCallback((liveMessageId) => createStreamingUpdateCoordinator((text) => {
38
41
  setLiveResponse(prev => prev && prev.id === liveMessageId
@@ -161,6 +164,9 @@ export const useConversation = ({ conversationService, loggingService, }) => {
161
164
  });
162
165
  setWaitingForApproval(false);
163
166
  setPendingApproval(null);
167
+ if (result.usage) {
168
+ setLastUsage(result.usage);
169
+ }
164
170
  }, [annotateCommandMessage, appendMessages, trimMessages]);
165
171
  const sendUserMessage = useCallback(async (value) => {
166
172
  if (!value.trim()) {
@@ -180,16 +186,12 @@ export const useConversation = ({ conversationService, loggingService, }) => {
180
186
  text: '',
181
187
  });
182
188
  const liveResponseUpdater = createLiveResponseUpdater(liveMessageId);
183
- // Track accumulated text so we can flush it before command messages
184
- let accumulatedText = '';
185
- let accumulatedReasoningText = '';
186
- let flushedReasoningLength = 0; // Track how much reasoning has been flushed
187
- let textWasFlushed = false;
188
- let currentReasoningMessageId = null; // Track current reasoning message ID
189
+ // Create streaming state object for this message send
190
+ const streamingState = createStreamingState();
189
191
  const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
190
192
  setMessages(prev => {
191
- if (currentReasoningMessageId !== null) {
192
- const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
193
+ if (streamingState.currentReasoningMessageId !== null) {
194
+ const index = prev.findIndex(msg => msg.id === streamingState.currentReasoningMessageId);
193
195
  if (index === -1)
194
196
  return prev;
195
197
  const current = prev[index];
@@ -201,7 +203,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
201
203
  return trimMessages(next);
202
204
  }
203
205
  const newId = Date.now();
204
- currentReasoningMessageId = newId;
206
+ streamingState.currentReasoningMessageId = newId;
205
207
  return trimMessages([
206
208
  ...prev,
207
209
  {
@@ -212,175 +214,33 @@ export const useConversation = ({ conversationService, loggingService, }) => {
212
214
  ]);
213
215
  });
214
216
  }, REASONING_RESPONSE_THROTTLE_MS);
215
- // Create event logger with deduplication for this message send
216
- // const {logDeduplicated, flush: flushLog} = createEventLogger();
217
+ // Create event handler using extracted factory
218
+ const baseEventHandler = createConversationEventHandler({
219
+ liveResponseUpdater,
220
+ reasoningUpdater,
221
+ appendMessages,
222
+ setMessages,
223
+ setLiveResponse,
224
+ trimMessages,
225
+ annotateCommandMessage,
226
+ }, streamingState);
217
227
  const applyConversationEvent = (event) => {
218
- switch (event.type) {
219
- case 'text_delta': {
220
- // logDeduplicated('text_delta');
221
- accumulatedText += event.delta;
222
- liveResponseUpdater.push(accumulatedText);
223
- return;
224
- }
225
- case 'reasoning_delta': {
226
- // logDeduplicated('reasoning_delta');
227
- const fullReasoningText = event.fullText ?? '';
228
- // Only show reasoning text after what was already flushed
229
- const newReasoningText = fullReasoningText.slice(flushedReasoningLength);
230
- accumulatedReasoningText = newReasoningText;
231
- if (!newReasoningText.trim())
232
- return;
233
- reasoningUpdater.push(newReasoningText);
234
- return;
235
- }
236
- case 'tool_started': {
237
- // Flush reasoning state
238
- if (accumulatedReasoningText.trim()) {
239
- reasoningUpdater.flush();
240
- flushedReasoningLength += accumulatedReasoningText.length;
241
- accumulatedReasoningText = '';
242
- currentReasoningMessageId = null;
243
- }
244
- // Flush any accumulated text before showing the tool call
245
- if (accumulatedText.trim()) {
246
- const textMessage = {
247
- id: Date.now() + 1,
248
- sender: 'bot',
249
- text: accumulatedText,
250
- };
251
- appendMessages([textMessage]);
252
- accumulatedText = '';
253
- textWasFlushed = true;
254
- liveResponseUpdater.cancel();
255
- setLiveResponse(null);
256
- }
257
- // Emit a "pending" command message when tool starts running
258
- // This provides immediate UI feedback before output is available
259
- const { toolCallId, toolName, arguments: rawArgs } = event;
260
- // tool_started.arguments may be either an object or a JSON string
261
- // depending on provider. Normalize so we can render params.
262
- const args = (() => {
263
- if (typeof rawArgs !== 'string') {
264
- return rawArgs;
265
- }
266
- const trimmed = rawArgs.trim();
267
- if (!trimmed) {
268
- return rawArgs;
269
- }
270
- try {
271
- return JSON.parse(trimmed);
272
- }
273
- catch {
274
- return rawArgs;
275
- }
276
- })();
277
- // Create a command string from the tool info
278
- const command = (() => {
279
- if (toolName === 'shell') {
280
- const cmd = args?.command ?? args?.commands;
281
- if (typeof cmd === 'string' && cmd.trim()) {
282
- return cmd;
283
- }
284
- if (Array.isArray(cmd) && cmd.length > 0) {
285
- return cmd.join('\n');
286
- }
287
- }
288
- if (toolName === 'grep' && args?.pattern) {
289
- return `grep "${args.pattern}" ${args.path ?? '.'}`;
290
- }
291
- if (toolName === 'search_replace') {
292
- return `search_replace "${args.search_content ?? ''}" → "${args.replace_content ?? ''}" ${args.path ?? ''}`;
293
- }
294
- if (toolName === 'apply_patch') {
295
- return `apply_patch ${args?.type ?? 'unknown'} ${args?.path ?? ''}`;
296
- }
297
- if (toolName === 'ask_mentor') {
298
- return `ask_mentor: ${args?.question ?? ''}`;
299
- }
300
- return `${toolName ?? 'unknown_tool'}`;
301
- })();
302
- const pendingMessage = {
303
- id: toolCallId ?? String(Date.now()),
304
- sender: 'command',
305
- status: 'running',
306
- command,
307
- output: '',
308
- callId: toolCallId,
309
- toolName,
310
- toolArgs: args,
311
- };
312
- appendMessages([pendingMessage]);
313
- return;
314
- }
315
- case 'command_message': {
316
- // logDeduplicated('command_message');
317
- const cmdMsg = event.message;
318
- const annotated = annotateCommandMessage(cmdMsg);
319
- // Before adding command message, flush reasoning and text separately
320
- // This preserves the order: reasoning -> command -> response text
321
- const messagesToAdd = [];
322
- if (accumulatedReasoningText.trim()) {
323
- reasoningUpdater.flush();
324
- // Reasoning is already in messages via stream updates.
325
- // We just need to track what we've "flushed" (sealed) so next reasoning chunks start fresh.
326
- flushedReasoningLength +=
327
- accumulatedReasoningText.length;
328
- accumulatedReasoningText = '';
329
- currentReasoningMessageId = null; // Reset for potential post-command reasoning
330
- }
331
- if (accumulatedText.trim()) {
332
- const textMessage = {
333
- id: Date.now() + 1,
334
- sender: 'bot',
335
- text: accumulatedText,
336
- };
337
- messagesToAdd.push(textMessage);
338
- accumulatedText = '';
339
- textWasFlushed = true;
340
- }
341
- if (messagesToAdd.length > 0) {
342
- appendMessages(messagesToAdd);
343
- // Clear live response since we've committed the text
344
- liveResponseUpdater.cancel();
345
- setLiveResponse(null);
346
- }
347
- // Replace pending message with completed one, or add new if not found
348
- setMessages(prev => {
349
- // Try to find the pending message by callId
350
- const pendingIndex = annotated.callId
351
- ? prev.findIndex(msg => msg.sender === 'command' &&
352
- msg.callId === annotated.callId &&
353
- msg.status === 'running')
354
- : -1;
355
- if (pendingIndex !== -1) {
356
- // Replace the pending message with the completed one
357
- const next = [...prev];
358
- next[pendingIndex] = annotated;
359
- return trimMessages(next);
360
- }
361
- // If no pending message found, append the completed one
362
- return trimMessages([...prev, annotated]);
363
- });
364
- return;
228
+ if (event.type === 'final') {
229
+ if (event.usage) {
230
+ loggingService.debug('UI received final usage (sendUserMessage)', { usage: event.usage });
231
+ setLastUsage(event.usage);
365
232
  }
366
- case 'retry': {
367
- const systemMessage = {
368
- id: Date.now(),
369
- sender: 'system',
370
- text: `Tool hallucination detected (${event.toolName}). Retrying... (Attempt ${event.attempt}/${event.maxRetries})`,
371
- };
372
- setMessages(prev => [...prev, systemMessage]);
373
- return;
233
+ else {
234
+ loggingService.debug('UI final event has no usage (sendUserMessage)');
374
235
  }
375
- default:
376
- return;
377
236
  }
237
+ baseEventHandler(event);
378
238
  };
379
239
  try {
380
240
  const result = await conversationService.sendMessage(value, {
381
241
  onEvent: applyConversationEvent,
382
242
  });
383
- applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
243
+ applyServiceResult(result, streamingState.accumulatedText, streamingState.accumulatedReasoningText, streamingState.textWasFlushed);
384
244
  }
385
245
  catch (error) {
386
246
  loggingService.error('Error in sendUserMessage', {
@@ -393,19 +253,9 @@ export const useConversation = ({ conversationService, loggingService, }) => {
393
253
  // The finally block will handle cleanup
394
254
  return;
395
255
  }
396
- let errorMessage = error instanceof Error ? error.message : String(error);
397
- // Enhance error messages for common issues
398
- if (errorMessage.includes('OPENAI_API_KEY') ||
399
- (errorMessage.includes('401') &&
400
- errorMessage.toLowerCase().includes('unauthorized'))) {
401
- errorMessage =
402
- 'OpenAI API key is not configured or invalid. Please set the OPENAI_API_KEY environment variable. ' +
403
- 'Get your API key from: https://platform.openai.com/api-keys';
404
- }
405
- // Check if this is a max turns exceeded error
406
- const isMaxTurnsError = errorMessage.includes('Max turns') &&
407
- errorMessage.includes('exceeded');
408
- if (isMaxTurnsError) {
256
+ const rawErrorMessage = error instanceof Error ? error.message : String(error);
257
+ const errorMessage = enhanceApiKeyError(rawErrorMessage);
258
+ if (isMaxTurnsError(errorMessage)) {
409
259
  // Create an approval prompt for max turns continuation
410
260
  setPendingApproval({
411
261
  agentName: 'System',
@@ -470,16 +320,12 @@ export const useConversation = ({ conversationService, loggingService, }) => {
470
320
  text: '',
471
321
  });
472
322
  const liveResponseUpdater = createLiveResponseUpdater(liveMessageId);
473
- // Track accumulated text so we can flush it before command messages
474
- let accumulatedText = '';
475
- let accumulatedReasoningText = '';
476
- let flushedReasoningLength = 0;
477
- let textWasFlushed = false;
478
- let currentReasoningMessageId = null;
323
+ // Create streaming state object for max turns continuation
324
+ const streamingState = createStreamingState();
479
325
  const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
480
326
  setMessages(prev => {
481
- if (currentReasoningMessageId !== null) {
482
- const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
327
+ if (streamingState.currentReasoningMessageId !== null) {
328
+ const index = prev.findIndex(msg => msg.id === streamingState.currentReasoningMessageId);
483
329
  if (index === -1)
484
330
  return prev;
485
331
  const current = prev[index];
@@ -494,7 +340,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
494
340
  return trimMessages(next);
495
341
  }
496
342
  const newId = Date.now();
497
- currentReasoningMessageId = newId;
343
+ streamingState.currentReasoningMessageId = newId;
498
344
  return trimMessages([
499
345
  ...prev,
500
346
  {
@@ -505,154 +351,27 @@ export const useConversation = ({ conversationService, loggingService, }) => {
505
351
  ]);
506
352
  });
507
353
  }, REASONING_RESPONSE_THROTTLE_MS);
508
- // const {logDeduplicated, flush: flushLog} = createEventLogger();
354
+ // Create event handler using extracted factory
355
+ const baseEventHandler = createConversationEventHandler({
356
+ liveResponseUpdater,
357
+ reasoningUpdater,
358
+ appendMessages,
359
+ setMessages,
360
+ setLiveResponse,
361
+ trimMessages,
362
+ annotateCommandMessage,
363
+ }, streamingState);
509
364
  const applyConversationEvent = (event) => {
510
- switch (event.type) {
511
- case 'text_delta': {
512
- // logDeduplicated('text_delta');
513
- accumulatedText += event.delta;
514
- liveResponseUpdater.push(accumulatedText);
515
- return;
516
- }
517
- case 'reasoning_delta': {
518
- // logDeduplicated('reasoning_delta');
519
- const fullReasoningText = event.fullText ?? '';
520
- const newReasoningText = fullReasoningText.slice(flushedReasoningLength);
521
- accumulatedReasoningText = newReasoningText;
522
- if (!newReasoningText.trim())
523
- return;
524
- reasoningUpdater.push(newReasoningText);
525
- return;
526
- }
527
- case 'tool_started': {
528
- // Flush reasoning state
529
- if (accumulatedReasoningText.trim()) {
530
- reasoningUpdater.flush();
531
- flushedReasoningLength += accumulatedReasoningText.length;
532
- accumulatedReasoningText = '';
533
- currentReasoningMessageId = null;
534
- }
535
- // Flush any accumulated text before showing the tool call
536
- if (accumulatedText.trim()) {
537
- const textMessage = {
538
- id: Date.now() + 1,
539
- sender: 'bot',
540
- text: accumulatedText,
541
- };
542
- appendMessages([textMessage]);
543
- accumulatedText = '';
544
- textWasFlushed = true;
545
- liveResponseUpdater.cancel();
546
- setLiveResponse(null);
547
- }
548
- const { toolCallId, toolName, arguments: rawArgs } = event;
549
- const args = (() => {
550
- if (typeof rawArgs !== 'string') {
551
- return rawArgs;
552
- }
553
- const trimmed = rawArgs.trim();
554
- if (!trimmed) {
555
- return rawArgs;
556
- }
557
- try {
558
- return JSON.parse(trimmed);
559
- }
560
- catch {
561
- return rawArgs;
562
- }
563
- })();
564
- const command = (() => {
565
- if (toolName === 'shell') {
566
- const cmd = args?.command ?? args?.commands;
567
- if (typeof cmd === 'string' && cmd.trim()) {
568
- return cmd;
569
- }
570
- if (Array.isArray(cmd) && cmd.length > 0) {
571
- return cmd.join('\n');
572
- }
573
- }
574
- if (toolName === 'grep' && args?.pattern) {
575
- return `grep "${args.pattern}" ${args.path ?? '.'}`;
576
- }
577
- if (toolName === 'search_replace') {
578
- return `search_replace "${args.search_content ?? ''}" → "${args.replace_content ?? ''}" ${args.path ?? ''}`;
579
- }
580
- if (toolName === 'apply_patch') {
581
- return `apply_patch ${args?.type ?? 'unknown'} ${args?.path ?? ''}`;
582
- }
583
- if (toolName === 'ask_mentor') {
584
- return `ask_mentor: ${args?.question ?? ''}`;
585
- }
586
- return `${toolName ?? 'unknown_tool'}`;
587
- })();
588
- const pendingMessage = {
589
- id: toolCallId ?? String(Date.now()),
590
- sender: 'command',
591
- status: 'running',
592
- command,
593
- output: '',
594
- callId: toolCallId,
595
- toolName,
596
- toolArgs: args,
597
- };
598
- appendMessages([pendingMessage]);
599
- return;
600
- }
601
- case 'command_message': {
602
- // logDeduplicated('command_message');
603
- const cmdMsg = event.message;
604
- const annotated = annotateCommandMessage(cmdMsg);
605
- const messagesToAdd = [];
606
- if (accumulatedReasoningText.trim()) {
607
- reasoningUpdater.flush();
608
- flushedReasoningLength +=
609
- accumulatedReasoningText.length;
610
- accumulatedReasoningText = '';
611
- currentReasoningMessageId = null;
612
- }
613
- if (accumulatedText.trim()) {
614
- const textMessage = {
615
- id: Date.now() + 1,
616
- sender: 'bot',
617
- text: accumulatedText,
618
- };
619
- messagesToAdd.push(textMessage);
620
- accumulatedText = '';
621
- textWasFlushed = true;
622
- }
623
- if (messagesToAdd.length > 0) {
624
- appendMessages(messagesToAdd);
625
- liveResponseUpdater.cancel();
626
- setLiveResponse(null);
627
- }
628
- // Replace pending message with completed one, or add new if not found
629
- setMessages(prev => {
630
- const pendingIndex = annotated.callId
631
- ? prev.findIndex(msg => msg.sender === 'command' &&
632
- msg.callId === annotated.callId &&
633
- msg.status === 'running')
634
- : -1;
635
- if (pendingIndex !== -1) {
636
- const next = [...prev];
637
- next[pendingIndex] = annotated;
638
- return trimMessages(next);
639
- }
640
- return trimMessages([...prev, annotated]);
641
- });
642
- return;
365
+ if (event.type === 'final') {
366
+ if (event.usage) {
367
+ loggingService.debug('UI received final usage (maxTurnsContinuation)', { usage: event.usage });
368
+ setLastUsage(event.usage);
643
369
  }
644
- case 'retry': {
645
- const systemMessage = {
646
- id: Date.now(),
647
- sender: 'system',
648
- text: `Tool hallucination detected (${event.toolName}). Retrying... (Attempt ${event.attempt}/${event.maxRetries})`,
649
- };
650
- setMessages(prev => [...prev, systemMessage]);
651
- return;
370
+ else {
371
+ loggingService.debug('UI final event has no usage (maxTurnsContinuation)');
652
372
  }
653
- default:
654
- return;
655
373
  }
374
+ baseEventHandler(event);
656
375
  };
657
376
  try {
658
377
  // Send a continuation message to resume work
@@ -660,7 +379,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
660
379
  const result = await conversationService.sendMessage(continuationMessage, {
661
380
  onEvent: applyConversationEvent,
662
381
  });
663
- applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
382
+ applyServiceResult(result, streamingState.accumulatedText, streamingState.accumulatedReasoningText, streamingState.textWasFlushed);
664
383
  }
665
384
  catch (error) {
666
385
  loggingService.error('Error in continuation after max turns', {
@@ -704,16 +423,12 @@ export const useConversation = ({ conversationService, loggingService, }) => {
704
423
  text: '',
705
424
  });
706
425
  const liveResponseUpdater = createLiveResponseUpdater(liveMessageId);
707
- // Track accumulated text so we can flush it before command messages
708
- let accumulatedText = '';
709
- let accumulatedReasoningText = '';
710
- let flushedReasoningLength = 0; // Track how much reasoning has been flushed
711
- let textWasFlushed = false;
712
- let currentReasoningMessageId = null; // Track current reasoning message ID
426
+ // Create streaming state object for this approval decision
427
+ const streamingState = createStreamingState();
713
428
  const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
714
429
  setMessages(prev => {
715
- if (currentReasoningMessageId !== null) {
716
- const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
430
+ if (streamingState.currentReasoningMessageId !== null) {
431
+ const index = prev.findIndex(msg => msg.id === streamingState.currentReasoningMessageId);
717
432
  if (index === -1)
718
433
  return prev;
719
434
  const current = prev[index];
@@ -725,7 +440,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
725
440
  return trimMessages(next);
726
441
  }
727
442
  const newId = Date.now();
728
- currentReasoningMessageId = newId;
443
+ streamingState.currentReasoningMessageId = newId;
729
444
  return trimMessages([
730
445
  ...prev,
731
446
  {
@@ -736,163 +451,33 @@ export const useConversation = ({ conversationService, loggingService, }) => {
736
451
  ]);
737
452
  });
738
453
  }, REASONING_RESPONSE_THROTTLE_MS);
739
- // Create event logger with deduplication for this approval decision
740
- // const {logDeduplicated, flush: flushLog} = createEventLogger();
454
+ // Create event handler using extracted factory
455
+ const baseEventHandler = createConversationEventHandler({
456
+ liveResponseUpdater,
457
+ reasoningUpdater,
458
+ appendMessages,
459
+ setMessages,
460
+ setLiveResponse,
461
+ trimMessages,
462
+ annotateCommandMessage,
463
+ }, streamingState);
741
464
  const applyConversationEvent = (event) => {
742
- switch (event.type) {
743
- case 'text_delta': {
744
- // logDeduplicated('text_delta');
745
- accumulatedText += event.delta;
746
- liveResponseUpdater.push(accumulatedText);
747
- return;
748
- }
749
- case 'reasoning_delta': {
750
- // logDeduplicated('reasoning_delta');
751
- const fullReasoningText = event.fullText ?? '';
752
- const newReasoningText = fullReasoningText.slice(flushedReasoningLength);
753
- accumulatedReasoningText = newReasoningText;
754
- if (!newReasoningText.trim())
755
- return;
756
- reasoningUpdater.push(newReasoningText);
757
- return;
758
- }
759
- case 'tool_started': {
760
- // Flush reasoning state
761
- if (accumulatedReasoningText.trim()) {
762
- reasoningUpdater.flush();
763
- flushedReasoningLength += accumulatedReasoningText.length;
764
- accumulatedReasoningText = '';
765
- currentReasoningMessageId = null;
766
- }
767
- // Flush any accumulated text before showing the tool call
768
- if (accumulatedText.trim()) {
769
- const textMessage = {
770
- id: Date.now() + 1,
771
- sender: 'bot',
772
- text: accumulatedText,
773
- };
774
- appendMessages([textMessage]);
775
- accumulatedText = '';
776
- textWasFlushed = true;
777
- liveResponseUpdater.cancel();
778
- setLiveResponse(null);
779
- }
780
- const { toolCallId, toolName, arguments: rawArgs } = event;
781
- // tool_started.arguments may be either an object or a JSON string
782
- // depending on provider. Normalize so we can render params.
783
- const args = (() => {
784
- if (typeof rawArgs !== 'string') {
785
- return rawArgs;
786
- }
787
- const trimmed = rawArgs.trim();
788
- if (!trimmed) {
789
- return rawArgs;
790
- }
791
- try {
792
- return JSON.parse(trimmed);
793
- }
794
- catch {
795
- return rawArgs;
796
- }
797
- })();
798
- const command = (() => {
799
- if (toolName === 'shell') {
800
- const cmd = args?.command ?? args?.commands;
801
- if (typeof cmd === 'string' && cmd.trim()) {
802
- return cmd;
803
- }
804
- if (Array.isArray(cmd) && cmd.length > 0) {
805
- return cmd.join('\n');
806
- }
807
- }
808
- if (toolName === 'grep' && args?.pattern) {
809
- return `grep "${args.pattern}" ${args.path ?? '.'}`;
810
- }
811
- if (toolName === 'search_replace') {
812
- return `search_replace "${args.search_content ?? ''}" → "${args.replace_content ?? ''}" ${args.path ?? ''}`;
813
- }
814
- if (toolName === 'apply_patch') {
815
- return `apply_patch ${args?.type ?? 'unknown'} ${args?.path ?? ''}`;
816
- }
817
- if (toolName === 'ask_mentor') {
818
- return `ask_mentor: ${args?.question ?? ''}`;
819
- }
820
- return `${toolName ?? 'unknown_tool'}`;
821
- })();
822
- const pendingMessage = {
823
- id: toolCallId ?? String(Date.now()),
824
- sender: 'command',
825
- status: 'running',
826
- command,
827
- output: '',
828
- callId: toolCallId,
829
- toolName,
830
- toolArgs: args,
831
- };
832
- appendMessages([pendingMessage]);
833
- return;
834
- }
835
- case 'command_message': {
836
- // logDeduplicated('command_message');
837
- const cmdMsg = event.message;
838
- const annotated = annotateCommandMessage(cmdMsg);
839
- const messagesToAdd = [];
840
- if (accumulatedReasoningText.trim()) {
841
- reasoningUpdater.flush();
842
- flushedReasoningLength +=
843
- accumulatedReasoningText.length;
844
- accumulatedReasoningText = '';
845
- currentReasoningMessageId = null;
846
- }
847
- if (accumulatedText.trim()) {
848
- const textMessage = {
849
- id: Date.now() + 1,
850
- sender: 'bot',
851
- text: accumulatedText,
852
- };
853
- messagesToAdd.push(textMessage);
854
- accumulatedText = '';
855
- textWasFlushed = true;
856
- }
857
- if (messagesToAdd.length > 0) {
858
- appendMessages(messagesToAdd);
859
- liveResponseUpdater.cancel();
860
- setLiveResponse(null);
861
- }
862
- // Replace pending message with completed one, or add new if not found
863
- setMessages(prev => {
864
- const pendingIndex = annotated.callId
865
- ? prev.findIndex(msg => msg.sender === 'command' &&
866
- msg.callId === annotated.callId &&
867
- msg.status === 'running')
868
- : -1;
869
- if (pendingIndex !== -1) {
870
- const next = [...prev];
871
- next[pendingIndex] = annotated;
872
- return trimMessages(next);
873
- }
874
- return trimMessages([...prev, annotated]);
875
- });
876
- return;
465
+ if (event.type === 'final') {
466
+ if (event.usage) {
467
+ loggingService.debug('UI received final usage (approvalDecision)', { usage: event.usage });
468
+ setLastUsage(event.usage);
877
469
  }
878
- case 'retry': {
879
- const systemMessage = {
880
- id: Date.now(),
881
- sender: 'system',
882
- text: `Tool hallucination detected (${event.toolName}). Retrying... (Attempt ${event.attempt}/${event.maxRetries})`,
883
- };
884
- setMessages(prev => [...prev, systemMessage]);
885
- return;
470
+ else {
471
+ loggingService.debug('UI final event has no usage (approvalDecision)');
886
472
  }
887
- default:
888
- return;
889
473
  }
474
+ baseEventHandler(event);
890
475
  };
891
476
  try {
892
477
  const result = await conversationService.handleApprovalDecision(answer, rejectionReason, {
893
478
  onEvent: applyConversationEvent,
894
479
  });
895
- applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
480
+ applyServiceResult(result, streamingState.accumulatedText, streamingState.accumulatedReasoningText, streamingState.textWasFlushed);
896
481
  }
897
482
  catch (error) {
898
483
  loggingService.error('Error in handleApprovalDecision', {
@@ -998,6 +583,7 @@ export const useConversation = ({ conversationService, loggingService, }) => {
998
583
  return {
999
584
  messages,
1000
585
  liveResponse,
586
+ lastUsage,
1001
587
  pendingApproval,
1002
588
  waitingForApproval,
1003
589
  waitingForRejectionReason,