@qduc/term2 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (621) hide show
  1. package/LICENSE +21 -0
  2. package/dist/agent.d.ts +19 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +143 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/app.d.ts +22 -0
  7. package/dist/app.d.ts.map +1 -0
  8. package/dist/app.js +403 -0
  9. package/dist/app.js.map +1 -0
  10. package/dist/app.model-command-feedback.test.d.ts +2 -0
  11. package/dist/app.model-command-feedback.test.d.ts.map +1 -0
  12. package/dist/app.model-command-feedback.test.js +19 -0
  13. package/dist/app.model-command-feedback.test.js.map +1 -0
  14. package/dist/app.parseInput.test.d.ts +2 -0
  15. package/dist/app.parseInput.test.d.ts.map +1 -0
  16. package/dist/app.parseInput.test.js +97 -0
  17. package/dist/app.parseInput.test.js.map +1 -0
  18. package/dist/cli.d.ts +3 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +241 -0
  21. package/dist/cli.js.map +1 -0
  22. package/dist/components/ApprovalPrompt.d.ts +10 -0
  23. package/dist/components/ApprovalPrompt.d.ts.map +1 -0
  24. package/dist/components/ApprovalPrompt.js +163 -0
  25. package/dist/components/ApprovalPrompt.js.map +1 -0
  26. package/dist/components/Banner.d.ts +9 -0
  27. package/dist/components/Banner.d.ts.map +1 -0
  28. package/dist/components/Banner.js +86 -0
  29. package/dist/components/Banner.js.map +1 -0
  30. package/dist/components/BottomArea.d.ts +33 -0
  31. package/dist/components/BottomArea.d.ts.map +1 -0
  32. package/dist/components/BottomArea.js +31 -0
  33. package/dist/components/BottomArea.js.map +1 -0
  34. package/dist/components/BottomArea.test.d.ts +2 -0
  35. package/dist/components/BottomArea.test.d.ts.map +1 -0
  36. package/dist/components/BottomArea.test.js +73 -0
  37. package/dist/components/BottomArea.test.js.map +1 -0
  38. package/dist/components/ChatMessage.d.ts +7 -0
  39. package/dist/components/ChatMessage.d.ts.map +1 -0
  40. package/dist/components/ChatMessage.js +10 -0
  41. package/dist/components/ChatMessage.js.map +1 -0
  42. package/dist/components/CommandMessage.d.ts +15 -0
  43. package/dist/components/CommandMessage.d.ts.map +1 -0
  44. package/dist/components/CommandMessage.js +188 -0
  45. package/dist/components/CommandMessage.js.map +1 -0
  46. package/dist/components/CommandMessage.test.d.ts +2 -0
  47. package/dist/components/CommandMessage.test.d.ts.map +1 -0
  48. package/dist/components/CommandMessage.test.js +35 -0
  49. package/dist/components/CommandMessage.test.js.map +1 -0
  50. package/dist/components/ErrorBoundary.d.ts +27 -0
  51. package/dist/components/ErrorBoundary.d.ts.map +1 -0
  52. package/dist/components/ErrorBoundary.js +77 -0
  53. package/dist/components/ErrorBoundary.js.map +1 -0
  54. package/dist/components/ErrorBoundary.test.d.ts +2 -0
  55. package/dist/components/ErrorBoundary.test.d.ts.map +1 -0
  56. package/dist/components/ErrorBoundary.test.js +32 -0
  57. package/dist/components/ErrorBoundary.test.js.map +1 -0
  58. package/dist/components/Input/PopupManager.d.ts +42 -0
  59. package/dist/components/Input/PopupManager.d.ts.map +1 -0
  60. package/dist/components/Input/PopupManager.js +13 -0
  61. package/dist/components/Input/PopupManager.js.map +1 -0
  62. package/dist/components/InputBox.d.ts +18 -0
  63. package/dist/components/InputBox.d.ts.map +1 -0
  64. package/dist/components/InputBox.js +384 -0
  65. package/dist/components/InputBox.js.map +1 -0
  66. package/dist/components/InputBox.menu-logic.test.d.ts +2 -0
  67. package/dist/components/InputBox.menu-logic.test.d.ts.map +1 -0
  68. package/dist/components/InputBox.menu-logic.test.js +151 -0
  69. package/dist/components/InputBox.menu-logic.test.js.map +1 -0
  70. package/dist/components/InputBox.test.d.ts +2 -0
  71. package/dist/components/InputBox.test.d.ts.map +1 -0
  72. package/dist/components/InputBox.test.js +91 -0
  73. package/dist/components/InputBox.test.js.map +1 -0
  74. package/dist/components/LiveResponse.d.ts +13 -0
  75. package/dist/components/LiveResponse.d.ts.map +1 -0
  76. package/dist/components/LiveResponse.js +16 -0
  77. package/dist/components/LiveResponse.js.map +1 -0
  78. package/dist/components/MarkdownRenderer.d.ts +8 -0
  79. package/dist/components/MarkdownRenderer.d.ts.map +1 -0
  80. package/dist/components/MarkdownRenderer.js +225 -0
  81. package/dist/components/MarkdownRenderer.js.map +1 -0
  82. package/dist/components/MentorMode.test.d.ts +2 -0
  83. package/dist/components/MentorMode.test.d.ts.map +1 -0
  84. package/dist/components/MentorMode.test.js.map +1 -0
  85. package/dist/components/MessageList.d.ts +7 -0
  86. package/dist/components/MessageList.d.ts.map +1 -0
  87. package/dist/components/MessageList.js +29 -0
  88. package/dist/components/MessageList.js.map +1 -0
  89. package/dist/components/MessageList.test.d.ts +2 -0
  90. package/dist/components/MessageList.test.d.ts.map +1 -0
  91. package/dist/components/MessageList.test.js +15 -0
  92. package/dist/components/MessageList.test.js.map +1 -0
  93. package/dist/components/ModelSelectionMenu.d.ts +18 -0
  94. package/dist/components/ModelSelectionMenu.d.ts.map +1 -0
  95. package/dist/components/ModelSelectionMenu.js +91 -0
  96. package/dist/components/ModelSelectionMenu.js.map +1 -0
  97. package/dist/components/ModelSelectionMenu.test.d.ts +2 -0
  98. package/dist/components/ModelSelectionMenu.test.d.ts.map +1 -0
  99. package/dist/components/ModelSelectionMenu.test.js +83 -0
  100. package/dist/components/ModelSelectionMenu.test.js.map +1 -0
  101. package/dist/components/PathSelectionMenu.d.ts +12 -0
  102. package/dist/components/PathSelectionMenu.d.ts.map +1 -0
  103. package/dist/components/PathSelectionMenu.js +42 -0
  104. package/dist/components/PathSelectionMenu.js.map +1 -0
  105. package/dist/components/SettingsSelectionMenu.d.ts +9 -0
  106. package/dist/components/SettingsSelectionMenu.d.ts.map +1 -0
  107. package/dist/components/SettingsSelectionMenu.js +21 -0
  108. package/dist/components/SettingsSelectionMenu.js.map +1 -0
  109. package/dist/components/SlashCommandMenu.d.ts +15 -0
  110. package/dist/components/SlashCommandMenu.d.ts.map +1 -0
  111. package/dist/components/SlashCommandMenu.js +20 -0
  112. package/dist/components/SlashCommandMenu.js.map +1 -0
  113. package/dist/components/StatusBar.d.ts +11 -0
  114. package/dist/components/StatusBar.d.ts.map +1 -0
  115. package/dist/components/StatusBar.js +59 -0
  116. package/dist/components/StatusBar.js.map +1 -0
  117. package/dist/components/TextInput.d.ts +42 -0
  118. package/dist/components/TextInput.d.ts.map +1 -0
  119. package/dist/components/TextInput.js +397 -0
  120. package/dist/components/TextInput.js.map +1 -0
  121. package/dist/components/TextInput.test.d.ts +2 -0
  122. package/dist/components/TextInput.test.d.ts.map +1 -0
  123. package/dist/components/TextInput.test.js +75 -0
  124. package/dist/components/TextInput.test.js.map +1 -0
  125. package/dist/context/InputContext.d.ts +31 -0
  126. package/dist/context/InputContext.d.ts.map +1 -0
  127. package/dist/context/InputContext.js +36 -0
  128. package/dist/context/InputContext.js.map +1 -0
  129. package/dist/context/InputContext.stability.test.d.ts +2 -0
  130. package/dist/context/InputContext.stability.test.d.ts.map +1 -0
  131. package/dist/context/InputContext.stability.test.js +28 -0
  132. package/dist/context/InputContext.stability.test.js.map +1 -0
  133. package/dist/context/InputContext.test.d.ts +2 -0
  134. package/dist/context/InputContext.test.d.ts.map +1 -0
  135. package/dist/context/InputContext.test.js +168 -0
  136. package/dist/context/InputContext.test.js.map +1 -0
  137. package/dist/debug-schema.d.ts +2 -0
  138. package/dist/debug-schema.d.ts.map +1 -0
  139. package/dist/debug-schema.js +22 -0
  140. package/dist/debug-schema.js.map +1 -0
  141. package/dist/hooks/use-conversation.d.ts +78 -0
  142. package/dist/hooks/use-conversation.d.ts.map +1 -0
  143. package/dist/hooks/use-conversation.js +1017 -0
  144. package/dist/hooks/use-conversation.js.map +1 -0
  145. package/dist/hooks/use-input-history.d.ts +16 -0
  146. package/dist/hooks/use-input-history.d.ts.map +1 -0
  147. package/dist/hooks/use-input-history.js +71 -0
  148. package/dist/hooks/use-input-history.js.map +1 -0
  149. package/dist/hooks/use-model-selection.d.ts +27 -0
  150. package/dist/hooks/use-model-selection.d.ts.map +1 -0
  151. package/dist/hooks/use-model-selection.js +187 -0
  152. package/dist/hooks/use-model-selection.js.map +1 -0
  153. package/dist/hooks/use-model-selection.test.d.ts +2 -0
  154. package/dist/hooks/use-model-selection.test.d.ts.map +1 -0
  155. package/dist/hooks/use-model-selection.test.js +28 -0
  156. package/dist/hooks/use-model-selection.test.js.map +1 -0
  157. package/dist/hooks/use-path-completion.d.ts +22 -0
  158. package/dist/hooks/use-path-completion.d.ts.map +1 -0
  159. package/dist/hooks/use-path-completion.js +153 -0
  160. package/dist/hooks/use-path-completion.js.map +1 -0
  161. package/dist/hooks/use-path-completion.test.d.ts +2 -0
  162. package/dist/hooks/use-path-completion.test.d.ts.map +1 -0
  163. package/dist/hooks/use-path-completion.test.js +29 -0
  164. package/dist/hooks/use-path-completion.test.js.map +1 -0
  165. package/dist/hooks/use-setting.d.ts +7 -0
  166. package/dist/hooks/use-setting.d.ts.map +1 -0
  167. package/dist/hooks/use-setting.js +35 -0
  168. package/dist/hooks/use-setting.js.map +1 -0
  169. package/dist/hooks/use-settings-completion.d.ts +23 -0
  170. package/dist/hooks/use-settings-completion.d.ts.map +1 -0
  171. package/dist/hooks/use-settings-completion.js +164 -0
  172. package/dist/hooks/use-settings-completion.js.map +1 -0
  173. package/dist/hooks/use-settings-completion.test.d.ts +2 -0
  174. package/dist/hooks/use-settings-completion.test.d.ts.map +1 -0
  175. package/dist/hooks/use-settings-completion.test.js +334 -0
  176. package/dist/hooks/use-settings-completion.test.js.map +1 -0
  177. package/dist/hooks/use-slash-commands.d.ts +21 -0
  178. package/dist/hooks/use-slash-commands.d.ts.map +1 -0
  179. package/dist/hooks/use-slash-commands.js +87 -0
  180. package/dist/hooks/use-slash-commands.js.map +1 -0
  181. package/dist/hooks/use-slash-commands.test.d.ts +2 -0
  182. package/dist/hooks/use-slash-commands.test.d.ts.map +1 -0
  183. package/dist/hooks/use-slash-commands.test.js +246 -0
  184. package/dist/hooks/use-slash-commands.test.js.map +1 -0
  185. package/dist/lib/editor-impl.d.ts +23 -0
  186. package/dist/lib/editor-impl.d.ts.map +1 -0
  187. package/dist/lib/editor-impl.js +235 -0
  188. package/dist/lib/editor-impl.js.map +1 -0
  189. package/dist/lib/openai-agent-client.chat.test.d.ts +2 -0
  190. package/dist/lib/openai-agent-client.chat.test.d.ts.map +1 -0
  191. package/dist/lib/openai-agent-client.chat.test.js +68 -0
  192. package/dist/lib/openai-agent-client.chat.test.js.map +1 -0
  193. package/dist/lib/openai-agent-client.d.ts +48 -0
  194. package/dist/lib/openai-agent-client.d.ts.map +1 -0
  195. package/dist/lib/openai-agent-client.js +653 -0
  196. package/dist/lib/openai-agent-client.js.map +1 -0
  197. package/dist/lib/openai-agent-client.test.d.ts +2 -0
  198. package/dist/lib/openai-agent-client.test.d.ts.map +1 -0
  199. package/dist/lib/openai-agent-client.test.js +181 -0
  200. package/dist/lib/openai-agent-client.test.js.map +1 -0
  201. package/dist/lib/shell.d.ts +7 -0
  202. package/dist/lib/shell.d.ts.map +1 -0
  203. package/dist/lib/shell.js +56 -0
  204. package/dist/lib/shell.js.map +1 -0
  205. package/dist/lib/tool-invoke.d.ts +4 -0
  206. package/dist/lib/tool-invoke.d.ts.map +1 -0
  207. package/dist/lib/tool-invoke.js +26 -0
  208. package/dist/lib/tool-invoke.js.map +1 -0
  209. package/dist/lib/tool-invoke.test.d.ts +2 -0
  210. package/dist/lib/tool-invoke.test.d.ts.map +1 -0
  211. package/dist/lib/tool-invoke.test.js +19 -0
  212. package/dist/lib/tool-invoke.test.js.map +1 -0
  213. package/dist/no-singleton-imports.test.d.ts +2 -0
  214. package/dist/no-singleton-imports.test.d.ts.map +1 -0
  215. package/dist/no-singleton-imports.test.js +30 -0
  216. package/dist/no-singleton-imports.test.js.map +1 -0
  217. package/dist/prompts/anthropic.md +79 -0
  218. package/dist/prompts/codex.md +97 -0
  219. package/dist/prompts/default.md +77 -0
  220. package/dist/prompts/default.md.bak +77 -0
  221. package/dist/prompts/gpt-5.md +318 -0
  222. package/dist/prompts/lite.md +29 -0
  223. package/dist/prompts/simple-mentor.md +207 -0
  224. package/dist/prompts/simple.md +189 -0
  225. package/dist/providers/index.d.ts +5 -0
  226. package/dist/providers/index.d.ts.map +1 -0
  227. package/dist/providers/index.js +8 -0
  228. package/dist/providers/index.js.map +1 -0
  229. package/dist/providers/openai-compatible/api.d.ts +17 -0
  230. package/dist/providers/openai-compatible/api.d.ts.map +1 -0
  231. package/dist/providers/openai-compatible/api.js +58 -0
  232. package/dist/providers/openai-compatible/api.js.map +1 -0
  233. package/dist/providers/openai-compatible/model.d.ts +17 -0
  234. package/dist/providers/openai-compatible/model.d.ts.map +1 -0
  235. package/dist/providers/openai-compatible/model.js +435 -0
  236. package/dist/providers/openai-compatible/model.js.map +1 -0
  237. package/dist/providers/openai-compatible/provider.d.ts +22 -0
  238. package/dist/providers/openai-compatible/provider.d.ts.map +1 -0
  239. package/dist/providers/openai-compatible/provider.js +43 -0
  240. package/dist/providers/openai-compatible/provider.js.map +1 -0
  241. package/dist/providers/openai-compatible/utils.d.ts +3 -0
  242. package/dist/providers/openai-compatible/utils.d.ts.map +1 -0
  243. package/dist/providers/openai-compatible/utils.js +11 -0
  244. package/dist/providers/openai-compatible/utils.js.map +1 -0
  245. package/dist/providers/openai-compatible.provider.d.ts +8 -0
  246. package/dist/providers/openai-compatible.provider.d.ts.map +1 -0
  247. package/dist/providers/openai-compatible.provider.js +71 -0
  248. package/dist/providers/openai-compatible.provider.js.map +1 -0
  249. package/dist/providers/openai.provider.d.ts +2 -0
  250. package/dist/providers/openai.provider.d.ts.map +1 -0
  251. package/dist/providers/openai.provider.js +36 -0
  252. package/dist/providers/openai.provider.js.map +1 -0
  253. package/dist/providers/openrouter/api.d.ts +39 -0
  254. package/dist/providers/openrouter/api.d.ts.map +1 -0
  255. package/dist/providers/openrouter/api.js +172 -0
  256. package/dist/providers/openrouter/api.js.map +1 -0
  257. package/dist/providers/openrouter/converters.d.ts +8 -0
  258. package/dist/providers/openrouter/converters.d.ts.map +1 -0
  259. package/dist/providers/openrouter/converters.js +382 -0
  260. package/dist/providers/openrouter/converters.js.map +1 -0
  261. package/dist/providers/openrouter/converters.test.d.ts +2 -0
  262. package/dist/providers/openrouter/converters.test.d.ts.map +1 -0
  263. package/dist/providers/openrouter/converters.test.js +158 -0
  264. package/dist/providers/openrouter/converters.test.js.map +1 -0
  265. package/dist/providers/openrouter/index.d.ts +4 -0
  266. package/dist/providers/openrouter/index.d.ts.map +1 -0
  267. package/dist/providers/openrouter/index.js +4 -0
  268. package/dist/providers/openrouter/index.js.map +1 -0
  269. package/dist/providers/openrouter/model.d.ts +14 -0
  270. package/dist/providers/openrouter/model.d.ts.map +1 -0
  271. package/dist/providers/openrouter/model.js +485 -0
  272. package/dist/providers/openrouter/model.js.map +1 -0
  273. package/dist/providers/openrouter/provider.d.ts +15 -0
  274. package/dist/providers/openrouter/provider.d.ts.map +1 -0
  275. package/dist/providers/openrouter/provider.js +21 -0
  276. package/dist/providers/openrouter/provider.js.map +1 -0
  277. package/dist/providers/openrouter/utils.d.ts +10 -0
  278. package/dist/providers/openrouter/utils.d.ts.map +1 -0
  279. package/dist/providers/openrouter/utils.js +27 -0
  280. package/dist/providers/openrouter/utils.js.map +1 -0
  281. package/dist/providers/openrouter.api.retry.test.d.ts +2 -0
  282. package/dist/providers/openrouter.api.retry.test.d.ts.map +1 -0
  283. package/dist/providers/openrouter.api.retry.test.js +148 -0
  284. package/dist/providers/openrouter.api.retry.test.js.map +1 -0
  285. package/dist/providers/openrouter.d.ts +2 -0
  286. package/dist/providers/openrouter.d.ts.map +1 -0
  287. package/dist/providers/openrouter.history.test.d.ts +2 -0
  288. package/dist/providers/openrouter.history.test.d.ts.map +1 -0
  289. package/dist/providers/openrouter.history.test.js +533 -0
  290. package/dist/providers/openrouter.history.test.js.map +1 -0
  291. package/dist/providers/openrouter.js +4 -0
  292. package/dist/providers/openrouter.js.map +1 -0
  293. package/dist/providers/openrouter.provider.createRunner.test.d.ts +2 -0
  294. package/dist/providers/openrouter.provider.createRunner.test.d.ts.map +1 -0
  295. package/dist/providers/openrouter.provider.createRunner.test.js +23 -0
  296. package/dist/providers/openrouter.provider.createRunner.test.js.map +1 -0
  297. package/dist/providers/openrouter.provider.d.ts +2 -0
  298. package/dist/providers/openrouter.provider.d.ts.map +1 -0
  299. package/dist/providers/openrouter.provider.js +56 -0
  300. package/dist/providers/openrouter.provider.js.map +1 -0
  301. package/dist/providers/openrouter.test.d.ts +2 -0
  302. package/dist/providers/openrouter.test.d.ts.map +1 -0
  303. package/dist/providers/openrouter.test.js +1382 -0
  304. package/dist/providers/openrouter.test.js.map +1 -0
  305. package/dist/providers/registry.d.ts +65 -0
  306. package/dist/providers/registry.d.ts.map +1 -0
  307. package/dist/providers/registry.js +44 -0
  308. package/dist/providers/registry.js.map +1 -0
  309. package/dist/providers/registry.test.d.ts +2 -0
  310. package/dist/providers/registry.test.d.ts.map +1 -0
  311. package/dist/providers/registry.test.js +76 -0
  312. package/dist/providers/registry.test.js.map +1 -0
  313. package/dist/providers/web-search/index.d.ts +8 -0
  314. package/dist/providers/web-search/index.d.ts.map +1 -0
  315. package/dist/providers/web-search/index.js +9 -0
  316. package/dist/providers/web-search/index.js.map +1 -0
  317. package/dist/providers/web-search/registry.d.ts +35 -0
  318. package/dist/providers/web-search/registry.d.ts.map +1 -0
  319. package/dist/providers/web-search/registry.js +56 -0
  320. package/dist/providers/web-search/registry.js.map +1 -0
  321. package/dist/providers/web-search/registry.test.d.ts +2 -0
  322. package/dist/providers/web-search/registry.test.d.ts.map +1 -0
  323. package/dist/providers/web-search/registry.test.js +105 -0
  324. package/dist/providers/web-search/registry.test.js.map +1 -0
  325. package/dist/providers/web-search/tavily.provider.d.ts +15 -0
  326. package/dist/providers/web-search/tavily.provider.d.ts.map +1 -0
  327. package/dist/providers/web-search/tavily.provider.js +69 -0
  328. package/dist/providers/web-search/tavily.provider.js.map +1 -0
  329. package/dist/providers/web-search/tavily.provider.test.d.ts +2 -0
  330. package/dist/providers/web-search/tavily.provider.test.d.ts.map +1 -0
  331. package/dist/providers/web-search/tavily.provider.test.js +67 -0
  332. package/dist/providers/web-search/tavily.provider.test.js.map +1 -0
  333. package/dist/providers/web-search/types.d.ts +55 -0
  334. package/dist/providers/web-search/types.d.ts.map +1 -0
  335. package/dist/providers/web-search/types.js +6 -0
  336. package/dist/providers/web-search/types.js.map +1 -0
  337. package/dist/safety-checker.js +57 -0
  338. package/dist/services/conversation-events.d.ts +76 -0
  339. package/dist/services/conversation-events.d.ts.map +1 -0
  340. package/dist/services/conversation-events.js +2 -0
  341. package/dist/services/conversation-events.js.map +1 -0
  342. package/dist/services/conversation-service.d.ts +31 -0
  343. package/dist/services/conversation-service.d.ts.map +1 -0
  344. package/dist/services/conversation-service.js +46 -0
  345. package/dist/services/conversation-service.js.map +1 -0
  346. package/dist/services/conversation-service.test.js +190 -0
  347. package/dist/services/conversation-session.d.ts +99 -0
  348. package/dist/services/conversation-session.d.ts.map +1 -0
  349. package/dist/services/conversation-session.js +978 -0
  350. package/dist/services/conversation-session.js.map +1 -0
  351. package/dist/services/conversation-store.d.ts +24 -0
  352. package/dist/services/conversation-store.d.ts.map +1 -0
  353. package/dist/services/conversation-store.js +216 -0
  354. package/dist/services/conversation-store.js.map +1 -0
  355. package/dist/services/conversation-store.test.d.ts +2 -0
  356. package/dist/services/conversation-store.test.d.ts.map +1 -0
  357. package/dist/services/conversation-store.test.js +167 -0
  358. package/dist/services/conversation-store.test.js.map +1 -0
  359. package/dist/services/execution-context.d.ts +10 -0
  360. package/dist/services/execution-context.d.ts.map +1 -0
  361. package/dist/services/execution-context.js +22 -0
  362. package/dist/services/execution-context.js.map +1 -0
  363. package/dist/services/execution-context.test.d.ts +2 -0
  364. package/dist/services/execution-context.test.d.ts.map +1 -0
  365. package/dist/services/execution-context.test.js +49 -0
  366. package/dist/services/execution-context.test.js.map +1 -0
  367. package/dist/services/file-service.d.ts +12 -0
  368. package/dist/services/file-service.d.ts.map +1 -0
  369. package/dist/services/file-service.js +90 -0
  370. package/dist/services/file-service.js.map +1 -0
  371. package/dist/services/history-service.d.ts +39 -0
  372. package/dist/services/history-service.d.ts.map +1 -0
  373. package/dist/services/history-service.js +152 -0
  374. package/dist/services/history-service.js.map +1 -0
  375. package/dist/services/logging-service.d.ts +75 -0
  376. package/dist/services/logging-service.d.ts.map +1 -0
  377. package/dist/services/logging-service.js +343 -0
  378. package/dist/services/logging-service.js.map +1 -0
  379. package/dist/services/model-service.d.ts +15 -0
  380. package/dist/services/model-service.d.ts.map +1 -0
  381. package/dist/services/model-service.js +46 -0
  382. package/dist/services/model-service.js.map +1 -0
  383. package/dist/services/model-service.test.d.ts +2 -0
  384. package/dist/services/model-service.test.d.ts.map +1 -0
  385. package/dist/services/model-service.test.js +128 -0
  386. package/dist/services/model-service.test.js.map +1 -0
  387. package/dist/services/service-interfaces.d.ts +33 -0
  388. package/dist/services/service-interfaces.d.ts.map +1 -0
  389. package/dist/services/service-interfaces.js +2 -0
  390. package/dist/services/service-interfaces.js.map +1 -0
  391. package/dist/services/settings-service.d.ts +316 -0
  392. package/dist/services/settings-service.d.ts.map +1 -0
  393. package/dist/services/settings-service.js +1128 -0
  394. package/dist/services/settings-service.js.map +1 -0
  395. package/dist/services/settings-service.mock.d.ts +20 -0
  396. package/dist/services/settings-service.mock.d.ts.map +1 -0
  397. package/dist/services/settings-service.mock.js +55 -0
  398. package/dist/services/settings-service.mock.js.map +1 -0
  399. package/dist/services/singleton-deprecation.test.d.ts +2 -0
  400. package/dist/services/singleton-deprecation.test.d.ts.map +1 -0
  401. package/dist/services/singleton-deprecation.test.js +59 -0
  402. package/dist/services/singleton-deprecation.test.js.map +1 -0
  403. package/dist/services/ssh-service.d.ts +32 -0
  404. package/dist/services/ssh-service.d.ts.map +1 -0
  405. package/dist/services/ssh-service.js +119 -0
  406. package/dist/services/ssh-service.js.map +1 -0
  407. package/dist/services/ssh-service.test.d.ts +2 -0
  408. package/dist/services/ssh-service.test.d.ts.map +1 -0
  409. package/dist/services/ssh-service.test.js +269 -0
  410. package/dist/services/ssh-service.test.js.map +1 -0
  411. package/dist/test-search-tool.d.ts +2 -0
  412. package/dist/test-search-tool.d.ts.map +1 -0
  413. package/dist/test-search-tool.js +36 -0
  414. package/dist/test-search-tool.js.map +1 -0
  415. package/dist/tools/apply-patch.d.ts +28 -0
  416. package/dist/tools/apply-patch.d.ts.map +1 -0
  417. package/dist/tools/apply-patch.js +399 -0
  418. package/dist/tools/apply-patch.js.map +1 -0
  419. package/dist/tools/apply-patch.test.d.ts +2 -0
  420. package/dist/tools/apply-patch.test.d.ts.map +1 -0
  421. package/dist/tools/apply-patch.test.js +155 -0
  422. package/dist/tools/apply-patch.test.js.map +1 -0
  423. package/dist/tools/ask-mentor.d.ts +11 -0
  424. package/dist/tools/ask-mentor.d.ts.map +1 -0
  425. package/dist/tools/ask-mentor.js +52 -0
  426. package/dist/tools/ask-mentor.js.map +1 -0
  427. package/dist/tools/ask-mentor.test.d.ts +2 -0
  428. package/dist/tools/ask-mentor.test.d.ts.map +1 -0
  429. package/dist/tools/ask-mentor.test.js +47 -0
  430. package/dist/tools/ask-mentor.test.js.map +1 -0
  431. package/dist/tools/bash.d.ts +10 -0
  432. package/dist/tools/bash.d.ts.map +1 -0
  433. package/dist/tools/bash.js +55 -0
  434. package/dist/tools/bash.js.map +1 -0
  435. package/dist/tools/find-files.d.ts +15 -0
  436. package/dist/tools/find-files.d.ts.map +1 -0
  437. package/dist/tools/find-files.js +179 -0
  438. package/dist/tools/find-files.js.map +1 -0
  439. package/dist/tools/find-files.test.d.ts +2 -0
  440. package/dist/tools/find-files.test.d.ts.map +1 -0
  441. package/dist/tools/find-files.test.js +131 -0
  442. package/dist/tools/find-files.test.js.map +1 -0
  443. package/dist/tools/format-helpers.d.ts +34 -0
  444. package/dist/tools/format-helpers.d.ts.map +1 -0
  445. package/dist/tools/format-helpers.js +131 -0
  446. package/dist/tools/format-helpers.js.map +1 -0
  447. package/dist/tools/grep.d.ts +16 -0
  448. package/dist/tools/grep.d.ts.map +1 -0
  449. package/dist/tools/grep.js +211 -0
  450. package/dist/tools/grep.js.map +1 -0
  451. package/dist/tools/read-file.d.ts +15 -0
  452. package/dist/tools/read-file.d.ts.map +1 -0
  453. package/dist/tools/read-file.js +114 -0
  454. package/dist/tools/read-file.js.map +1 -0
  455. package/dist/tools/read-file.test.d.ts +2 -0
  456. package/dist/tools/read-file.test.d.ts.map +1 -0
  457. package/dist/tools/read-file.test.js +122 -0
  458. package/dist/tools/read-file.test.js.map +1 -0
  459. package/dist/tools/search-replace.d.ts +19 -0
  460. package/dist/tools/search-replace.d.ts.map +1 -0
  461. package/dist/tools/search-replace.js +411 -0
  462. package/dist/tools/search-replace.js.map +1 -0
  463. package/dist/tools/search-replace.test.d.ts +2 -0
  464. package/dist/tools/search-replace.test.d.ts.map +1 -0
  465. package/dist/tools/search-replace.test.js +302 -0
  466. package/dist/tools/search-replace.test.js.map +1 -0
  467. package/dist/tools/search.d.ts +15 -0
  468. package/dist/tools/search.d.ts.map +1 -0
  469. package/dist/tools/search.js +143 -0
  470. package/dist/tools/search.js.map +1 -0
  471. package/dist/tools/shell.d.ts +19 -0
  472. package/dist/tools/shell.d.ts.map +1 -0
  473. package/dist/tools/shell.js +278 -0
  474. package/dist/tools/shell.js.map +1 -0
  475. package/dist/tools/tool-execution-context.d.ts +7 -0
  476. package/dist/tools/tool-execution-context.d.ts.map +1 -0
  477. package/dist/tools/tool-execution-context.js +7 -0
  478. package/dist/tools/tool-execution-context.js.map +1 -0
  479. package/dist/tools/types.d.ts +30 -0
  480. package/dist/tools/types.d.ts.map +1 -0
  481. package/dist/tools/types.js +2 -0
  482. package/dist/tools/types.js.map +1 -0
  483. package/dist/tools/utils.d.ts +12 -0
  484. package/dist/tools/utils.d.ts.map +1 -0
  485. package/dist/tools/utils.js +19 -0
  486. package/dist/tools/utils.js.map +1 -0
  487. package/dist/tools/web-search.d.ts +29 -0
  488. package/dist/tools/web-search.d.ts.map +1 -0
  489. package/dist/tools/web-search.js +106 -0
  490. package/dist/tools/web-search.js.map +1 -0
  491. package/dist/tools/web-search.test.d.ts +2 -0
  492. package/dist/tools/web-search.test.d.ts.map +1 -0
  493. package/dist/tools/web-search.test.js +176 -0
  494. package/dist/tools/web-search.test.js.map +1 -0
  495. package/dist/utils/command-logger.d.ts +11 -0
  496. package/dist/utils/command-logger.d.ts.map +1 -0
  497. package/dist/utils/command-logger.js +34 -0
  498. package/dist/utils/command-logger.js.map +1 -0
  499. package/dist/utils/command-safety/constants.d.ts +21 -0
  500. package/dist/utils/command-safety/constants.d.ts.map +1 -0
  501. package/dist/utils/command-safety/constants.js +245 -0
  502. package/dist/utils/command-safety/constants.js.map +1 -0
  503. package/dist/utils/command-safety/find-helpers.d.ts +15 -0
  504. package/dist/utils/command-safety/find-helpers.d.ts.map +1 -0
  505. package/dist/utils/command-safety/find-helpers.js +218 -0
  506. package/dist/utils/command-safety/find-helpers.js.map +1 -0
  507. package/dist/utils/command-safety/handlers/find-handler.d.ts +6 -0
  508. package/dist/utils/command-safety/handlers/find-handler.d.ts.map +1 -0
  509. package/dist/utils/command-safety/handlers/find-handler.js +113 -0
  510. package/dist/utils/command-safety/handlers/find-handler.js.map +1 -0
  511. package/dist/utils/command-safety/handlers/git-handler.d.ts +6 -0
  512. package/dist/utils/command-safety/handlers/git-handler.d.ts.map +1 -0
  513. package/dist/utils/command-safety/handlers/git-handler.js +68 -0
  514. package/dist/utils/command-safety/handlers/git-handler.js.map +1 -0
  515. package/dist/utils/command-safety/handlers/index.d.ts +13 -0
  516. package/dist/utils/command-safety/handlers/index.d.ts.map +1 -0
  517. package/dist/utils/command-safety/handlers/index.js +20 -0
  518. package/dist/utils/command-safety/handlers/index.js.map +1 -0
  519. package/dist/utils/command-safety/handlers/sed-handler.d.ts +6 -0
  520. package/dist/utils/command-safety/handlers/sed-handler.d.ts.map +1 -0
  521. package/dist/utils/command-safety/handlers/sed-handler.js +94 -0
  522. package/dist/utils/command-safety/handlers/sed-handler.js.map +1 -0
  523. package/dist/utils/command-safety/handlers/types.d.ts +36 -0
  524. package/dist/utils/command-safety/handlers/types.d.ts.map +1 -0
  525. package/dist/utils/command-safety/handlers/types.js +2 -0
  526. package/dist/utils/command-safety/handlers/types.js.map +1 -0
  527. package/dist/utils/command-safety/index.d.ts +14 -0
  528. package/dist/utils/command-safety/index.d.ts.map +1 -0
  529. package/dist/utils/command-safety/index.js +183 -0
  530. package/dist/utils/command-safety/index.js.map +1 -0
  531. package/dist/utils/command-safety/path-analysis.d.ts +4 -0
  532. package/dist/utils/command-safety/path-analysis.d.ts.map +1 -0
  533. package/dist/utils/command-safety/path-analysis.js +153 -0
  534. package/dist/utils/command-safety/path-analysis.js.map +1 -0
  535. package/dist/utils/command-safety/utils.d.ts +2 -0
  536. package/dist/utils/command-safety/utils.d.ts.map +1 -0
  537. package/dist/utils/command-safety/utils.js +22 -0
  538. package/dist/utils/command-safety/utils.js.map +1 -0
  539. package/dist/utils/command-safety.d.ts +21 -0
  540. package/dist/utils/command-safety.d.ts.map +1 -0
  541. package/dist/utils/command-safety.find.test.d.ts +2 -0
  542. package/dist/utils/command-safety.find.test.d.ts.map +1 -0
  543. package/dist/utils/command-safety.find.test.js +342 -0
  544. package/dist/utils/command-safety.find.test.js.map +1 -0
  545. package/dist/utils/command-safety.js +702 -0
  546. package/dist/utils/command-safety.js.map +1 -0
  547. package/dist/utils/command-safety.path.test.d.ts +2 -0
  548. package/dist/utils/command-safety.path.test.d.ts.map +1 -0
  549. package/dist/utils/command-safety.path.test.js +360 -0
  550. package/dist/utils/command-safety.path.test.js.map +1 -0
  551. package/dist/utils/diff.d.ts +2 -0
  552. package/dist/utils/diff.d.ts.map +1 -0
  553. package/dist/utils/diff.js +44 -0
  554. package/dist/utils/diff.js.map +1 -0
  555. package/dist/utils/diff.test.d.ts +2 -0
  556. package/dist/utils/diff.test.d.ts.map +1 -0
  557. package/dist/utils/diff.test.js +85 -0
  558. package/dist/utils/diff.test.js.map +1 -0
  559. package/dist/utils/error-helpers.d.ts +6 -0
  560. package/dist/utils/error-helpers.d.ts.map +1 -0
  561. package/dist/utils/error-helpers.js +46 -0
  562. package/dist/utils/error-helpers.js.map +1 -0
  563. package/dist/utils/error-helpers.test.d.ts +2 -0
  564. package/dist/utils/error-helpers.test.d.ts.map +1 -0
  565. package/dist/utils/error-helpers.test.js +152 -0
  566. package/dist/utils/error-helpers.test.js.map +1 -0
  567. package/dist/utils/execute-shell.d.ts +15 -0
  568. package/dist/utils/execute-shell.d.ts.map +1 -0
  569. package/dist/utils/execute-shell.js +34 -0
  570. package/dist/utils/execute-shell.js.map +1 -0
  571. package/dist/utils/execute-shell.test.d.ts +2 -0
  572. package/dist/utils/execute-shell.test.d.ts.map +1 -0
  573. package/dist/utils/execute-shell.test.js +20 -0
  574. package/dist/utils/execute-shell.test.js.map +1 -0
  575. package/dist/utils/extract-command-messages.d.ts +5 -0
  576. package/dist/utils/extract-command-messages.d.ts.map +1 -0
  577. package/dist/utils/extract-command-messages.js +140 -0
  578. package/dist/utils/extract-command-messages.js.map +1 -0
  579. package/dist/utils/extract-command-messages.repro.test.d.ts +2 -0
  580. package/dist/utils/extract-command-messages.repro.test.d.ts.map +1 -0
  581. package/dist/utils/extract-command-messages.repro.test.js +31 -0
  582. package/dist/utils/extract-command-messages.repro.test.js.map +1 -0
  583. package/dist/utils/extract-command-messages.test.js +57 -0
  584. package/dist/utils/message-buffer.d.ts +2 -0
  585. package/dist/utils/message-buffer.d.ts.map +1 -0
  586. package/dist/utils/message-buffer.js +15 -0
  587. package/dist/utils/message-buffer.js.map +1 -0
  588. package/dist/utils/message-buffer.test.d.ts +2 -0
  589. package/dist/utils/message-buffer.test.d.ts.map +1 -0
  590. package/dist/utils/message-buffer.test.js +17 -0
  591. package/dist/utils/message-buffer.test.js.map +1 -0
  592. package/dist/utils/output-trim.d.ts +31 -0
  593. package/dist/utils/output-trim.d.ts.map +1 -0
  594. package/dist/utils/output-trim.js +71 -0
  595. package/dist/utils/output-trim.js.map +1 -0
  596. package/dist/utils/provider-credentials.d.ts +10 -0
  597. package/dist/utils/provider-credentials.d.ts.map +1 -0
  598. package/dist/utils/provider-credentials.js +22 -0
  599. package/dist/utils/provider-credentials.js.map +1 -0
  600. package/dist/utils/settings-command.d.ts +13 -0
  601. package/dist/utils/settings-command.d.ts.map +1 -0
  602. package/dist/utils/settings-command.js +173 -0
  603. package/dist/utils/settings-command.js.map +1 -0
  604. package/dist/utils/ssh-config-parser.d.ts +21 -0
  605. package/dist/utils/ssh-config-parser.d.ts.map +1 -0
  606. package/dist/utils/ssh-config-parser.js +89 -0
  607. package/dist/utils/ssh-config-parser.js.map +1 -0
  608. package/dist/utils/ssh-config-parser.test.d.ts +2 -0
  609. package/dist/utils/ssh-config-parser.test.d.ts.map +1 -0
  610. package/dist/utils/ssh-config-parser.test.js +153 -0
  611. package/dist/utils/ssh-config-parser.test.js.map +1 -0
  612. package/dist/utils/streaming-updater.d.ts +7 -0
  613. package/dist/utils/streaming-updater.d.ts.map +1 -0
  614. package/dist/utils/streaming-updater.js +41 -0
  615. package/dist/utils/streaming-updater.js.map +1 -0
  616. package/dist/utils/throttle.d.ts +7 -0
  617. package/dist/utils/throttle.d.ts.map +1 -0
  618. package/dist/utils/throttle.js +49 -0
  619. package/dist/utils/throttle.js.map +1 -0
  620. package/package.json +108 -0
  621. package/readme.md +428 -0
@@ -0,0 +1,1017 @@
1
+ import { useCallback, useRef, useState } from 'react';
2
+ import { isAbortLikeError } from '../utils/error-helpers.js';
3
+ import { createStreamingUpdateCoordinator } from '../utils/streaming-updater.js';
4
+ import { appendMessagesCapped } from '../utils/message-buffer.js';
5
+ const LIVE_RESPONSE_THROTTLE_MS = 150;
6
+ const REASONING_RESPONSE_THROTTLE_MS = 200;
7
+ const MAX_MESSAGE_COUNT = 300;
8
+ export const filterPendingCommandMessagesForApproval = (messages, approval) => {
9
+ if (!Array.isArray(messages) || messages.length === 0) {
10
+ return messages ?? [];
11
+ }
12
+ const callId = approval?.callId ? String(approval.callId) : undefined;
13
+ const toolName = approval?.toolName;
14
+ if (!callId && !toolName) {
15
+ return messages;
16
+ }
17
+ return messages.filter(msg => {
18
+ if (!msg || msg.sender !== 'command') {
19
+ return true;
20
+ }
21
+ if (msg.status !== 'pending' && msg.status !== 'running') {
22
+ return true;
23
+ }
24
+ const matchesCallId = callId && msg.callId && String(msg.callId) === callId;
25
+ const matchesToolName = !callId && toolName && msg.toolName === toolName;
26
+ return !(matchesCallId || matchesToolName);
27
+ });
28
+ };
29
+ export const useConversation = ({ conversationService, loggingService, }) => {
30
+ const [messages, setMessages] = useState([]);
31
+ const [waitingForApproval, setWaitingForApproval] = useState(false);
32
+ const [waitingForRejectionReason, setWaitingForRejectionReason] = useState(false);
33
+ const [pendingApproval, setPendingApproval] = useState(null);
34
+ const [isProcessing, setIsProcessing] = useState(false);
35
+ const [liveResponse, setLiveResponse] = useState(null);
36
+ const approvedContextRef = useRef(null);
37
+ const createLiveResponseUpdater = useCallback((liveMessageId) => createStreamingUpdateCoordinator((text) => {
38
+ setLiveResponse(prev => prev && prev.id === liveMessageId
39
+ ? { ...prev, text }
40
+ : {
41
+ id: liveMessageId,
42
+ sender: 'bot',
43
+ text,
44
+ });
45
+ }, LIVE_RESPONSE_THROTTLE_MS), []);
46
+ const trimMessages = useCallback((list) => appendMessagesCapped(list, [], MAX_MESSAGE_COUNT), []);
47
+ const appendMessages = useCallback((additions) => {
48
+ if (!additions.length)
49
+ return;
50
+ setMessages(prev => trimMessages([...prev, ...additions]));
51
+ }, [trimMessages]);
52
+ // Helper to log events with deduplication
53
+ // const createEventLogger = () => {
54
+ // let lastEventType: string | null = null;
55
+ // let eventCount = 0;
56
+ // let eventSequence: string[] = [];
57
+ //
58
+ // const logDeduplicated = (eventType: string) => {
59
+ // if (eventType !== lastEventType) {
60
+ // if (lastEventType !== null) {
61
+ // loggingService.debug('Conversation event sequence', {
62
+ // event: lastEventType,
63
+ // count: eventCount,
64
+ // sequenceLength: eventSequence.length,
65
+ // sequence: eventSequence,
66
+ // });
67
+ // }
68
+ // lastEventType = eventType;
69
+ // eventCount = 1;
70
+ // if (!eventSequence.includes(eventType)) {
71
+ // eventSequence.push(eventType);
72
+ // }
73
+ // } else {
74
+ // eventCount++;
75
+ // }
76
+ // };
77
+ //
78
+ // const flush = () => {
79
+ // if (lastEventType !== null) {
80
+ // loggingService.debug('Conversation event sequence final', {
81
+ // event: lastEventType,
82
+ // count: eventCount,
83
+ // sequenceLength: eventSequence.length,
84
+ // sequence: eventSequence,
85
+ // });
86
+ // lastEventType = null;
87
+ // eventCount = 0;
88
+ // eventSequence = [];
89
+ // }
90
+ // };
91
+ //
92
+ // return {logDeduplicated, flush};
93
+ // };
94
+ const annotateCommandMessage = useCallback((cmdMsg) => {
95
+ const approvalContext = approvedContextRef.current;
96
+ if (!approvalContext || cmdMsg.toolName !== 'search_replace') {
97
+ return cmdMsg;
98
+ }
99
+ const matchesCallId = approvalContext.callId &&
100
+ cmdMsg.callId &&
101
+ approvalContext.callId === cmdMsg.callId;
102
+ const matchesToolName = !approvalContext.callId &&
103
+ approvalContext.toolName &&
104
+ approvalContext.toolName === cmdMsg.toolName;
105
+ if (!matchesCallId && !matchesToolName) {
106
+ return cmdMsg;
107
+ }
108
+ if (matchesToolName && !approvalContext.callId) {
109
+ approvedContextRef.current = null;
110
+ }
111
+ return { ...cmdMsg, hadApproval: true };
112
+ }, []);
113
+ const applyServiceResult = useCallback((result, remainingText, remainingReasoningText, textWasFlushed) => {
114
+ if (!result) {
115
+ return;
116
+ }
117
+ if (result.type === 'approval_required') {
118
+ // Flush reasoning and text separately before showing approval prompt
119
+ const messagesToAdd = [];
120
+ if (remainingReasoningText?.trim() && !textWasFlushed) {
121
+ // Ensure reasoning is captured if not already streamed (edge case)
122
+ // But with new logic, it should be in messages.
123
+ // We'll leave this empty or remove it as reasoning is handled via stream
124
+ }
125
+ if (remainingText?.trim() && !textWasFlushed) {
126
+ const textMessage = {
127
+ id: Date.now() + 1,
128
+ sender: 'bot',
129
+ text: remainingText,
130
+ };
131
+ messagesToAdd.push(textMessage);
132
+ }
133
+ appendMessages(messagesToAdd);
134
+ // If a tool call requires approval, we show it in the approval prompt.
135
+ // Don't also show the transient pending/running command message.
136
+ setMessages(prev => trimMessages(filterPendingCommandMessagesForApproval(prev, result.approval)));
137
+ setPendingApproval(result.approval);
138
+ // Set waiting state AFTER adding approval message to ensure proper render order
139
+ setWaitingForApproval(true);
140
+ return;
141
+ }
142
+ // If text was already flushed before command messages, don't add it again
143
+ // Only add final text if there's new text after the commands
144
+ const shouldAddBotMessage = !textWasFlushed || remainingText?.trim();
145
+ const finalText = remainingText?.trim()
146
+ ? remainingText
147
+ : result.finalText;
148
+ setMessages(prev => {
149
+ const messagesToAdd = [];
150
+ const annotatedCommands = result.commandMessages.map(annotateCommandMessage);
151
+ let next = [...prev, ...messagesToAdd, ...annotatedCommands];
152
+ if (shouldAddBotMessage && finalText) {
153
+ const botMessage = {
154
+ id: Date.now() + 1,
155
+ sender: 'bot',
156
+ text: finalText,
157
+ };
158
+ next = [...next, botMessage];
159
+ }
160
+ return trimMessages(next);
161
+ });
162
+ setWaitingForApproval(false);
163
+ setPendingApproval(null);
164
+ }, [annotateCommandMessage, appendMessages, trimMessages]);
165
+ const sendUserMessage = useCallback(async (value) => {
166
+ if (!value.trim()) {
167
+ return;
168
+ }
169
+ const userMessage = {
170
+ id: Date.now(),
171
+ sender: 'user',
172
+ text: value,
173
+ };
174
+ appendMessages([userMessage]);
175
+ setIsProcessing(true);
176
+ const liveMessageId = Date.now();
177
+ setLiveResponse({
178
+ id: liveMessageId,
179
+ sender: 'bot',
180
+ text: '',
181
+ });
182
+ 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
+ const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
190
+ setMessages(prev => {
191
+ if (currentReasoningMessageId !== null) {
192
+ const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
193
+ if (index === -1)
194
+ return prev;
195
+ const current = prev[index];
196
+ if (current.sender !== 'reasoning') {
197
+ return prev;
198
+ }
199
+ const next = prev.slice();
200
+ next[index] = { ...current, text: newReasoningText };
201
+ return trimMessages(next);
202
+ }
203
+ const newId = Date.now();
204
+ currentReasoningMessageId = newId;
205
+ return trimMessages([
206
+ ...prev,
207
+ {
208
+ id: newId,
209
+ sender: 'reasoning',
210
+ text: newReasoningText,
211
+ },
212
+ ]);
213
+ });
214
+ }, REASONING_RESPONSE_THROTTLE_MS);
215
+ // Create event logger with deduplication for this message send
216
+ // const {logDeduplicated, flush: flushLog} = createEventLogger();
217
+ 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;
365
+ }
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;
374
+ }
375
+ default:
376
+ return;
377
+ }
378
+ };
379
+ try {
380
+ const result = await conversationService.sendMessage(value, {
381
+ onEvent: applyConversationEvent,
382
+ });
383
+ applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
384
+ }
385
+ catch (error) {
386
+ loggingService.error('Error in sendUserMessage', {
387
+ error: error instanceof Error ? error.message : String(error),
388
+ stack: error instanceof Error ? error.stack : undefined,
389
+ });
390
+ // Don't show error messages for user-initiated aborts
391
+ if (isAbortLikeError(error)) {
392
+ loggingService.debug('Suppressing abort error in sendUserMessage');
393
+ // The finally block will handle cleanup
394
+ return;
395
+ }
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) {
409
+ // Create an approval prompt for max turns continuation
410
+ setPendingApproval({
411
+ agentName: 'System',
412
+ toolName: 'max_turns_exceeded',
413
+ argumentsText: errorMessage,
414
+ rawInterruption: null,
415
+ isMaxTurnsPrompt: true,
416
+ });
417
+ setWaitingForApproval(true);
418
+ }
419
+ else {
420
+ // For other errors, just show the error message
421
+ const botErrorMessage = {
422
+ id: Date.now(),
423
+ sender: 'bot',
424
+ text: `Error: ${errorMessage}`,
425
+ };
426
+ appendMessages([botErrorMessage]);
427
+ // Reset approval state on error to allow user to continue
428
+ setWaitingForApproval(false);
429
+ setPendingApproval(null);
430
+ }
431
+ }
432
+ finally {
433
+ loggingService.debug('sendUserMessage finally block - resetting state');
434
+ // flushLog();
435
+ reasoningUpdater.flush();
436
+ liveResponseUpdater.cancel();
437
+ setLiveResponse(null);
438
+ setIsProcessing(false);
439
+ // Don't reset waitingForApproval here - it's set by applyServiceResult
440
+ // and should only be cleared by handleApprovalDecision or stopProcessing
441
+ }
442
+ }, [conversationService, applyServiceResult, appendMessages, trimMessages, loggingService, createLiveResponseUpdater]);
443
+ const handleApprovalDecision = useCallback(async (answer, rejectionReason) => {
444
+ if (!waitingForApproval || !pendingApproval) {
445
+ return;
446
+ }
447
+ // Check if this is a max turns exceeded prompt
448
+ const isMaxTurnsPrompt = pendingApproval.isMaxTurnsPrompt;
449
+ if (answer === 'y' &&
450
+ pendingApproval.toolName === 'search_replace') {
451
+ approvedContextRef.current = {
452
+ callId: pendingApproval.callId,
453
+ toolName: pendingApproval.toolName,
454
+ };
455
+ }
456
+ setPendingApproval(null);
457
+ setWaitingForApproval(false);
458
+ // Handle "n" answer for max turns - return to input
459
+ if (isMaxTurnsPrompt && answer === 'n') {
460
+ setIsProcessing(false);
461
+ return;
462
+ }
463
+ // Handle "y" answer for max turns - continue execution automatically
464
+ if (isMaxTurnsPrompt && answer === 'y') {
465
+ setIsProcessing(true);
466
+ const liveMessageId = Date.now();
467
+ setLiveResponse({
468
+ id: liveMessageId,
469
+ sender: 'bot',
470
+ text: '',
471
+ });
472
+ 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;
479
+ const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
480
+ setMessages(prev => {
481
+ if (currentReasoningMessageId !== null) {
482
+ const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
483
+ if (index === -1)
484
+ return prev;
485
+ const current = prev[index];
486
+ if (current.sender !== 'reasoning') {
487
+ return prev;
488
+ }
489
+ const next = prev.slice();
490
+ next[index] = {
491
+ ...current,
492
+ text: newReasoningText,
493
+ };
494
+ return trimMessages(next);
495
+ }
496
+ const newId = Date.now();
497
+ currentReasoningMessageId = newId;
498
+ return trimMessages([
499
+ ...prev,
500
+ {
501
+ id: newId,
502
+ sender: 'reasoning',
503
+ text: newReasoningText,
504
+ },
505
+ ]);
506
+ });
507
+ }, REASONING_RESPONSE_THROTTLE_MS);
508
+ // const {logDeduplicated, flush: flushLog} = createEventLogger();
509
+ 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;
643
+ }
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;
652
+ }
653
+ default:
654
+ return;
655
+ }
656
+ };
657
+ try {
658
+ // Send a continuation message to resume work
659
+ const continuationMessage = 'Please continue with your previous task.';
660
+ const result = await conversationService.sendMessage(continuationMessage, {
661
+ onEvent: applyConversationEvent,
662
+ });
663
+ applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
664
+ }
665
+ catch (error) {
666
+ loggingService.error('Error in continuation after max turns', {
667
+ error: error instanceof Error
668
+ ? error.message
669
+ : String(error),
670
+ stack: error instanceof Error
671
+ ? error.stack
672
+ : undefined,
673
+ });
674
+ // Don't show error messages for user-initiated aborts
675
+ if (isAbortLikeError(error)) {
676
+ loggingService.debug('Suppressing abort error in max turns continuation');
677
+ // The finally block will handle cleanup
678
+ return;
679
+ }
680
+ const errorMessage = error instanceof Error ? error.message : String(error);
681
+ const botErrorMessage = {
682
+ id: Date.now(),
683
+ sender: 'bot',
684
+ text: `Error: ${errorMessage}`,
685
+ };
686
+ appendMessages([botErrorMessage]);
687
+ setWaitingForApproval(false);
688
+ setPendingApproval(null);
689
+ }
690
+ finally {
691
+ // flushLog();
692
+ reasoningUpdater.flush();
693
+ liveResponseUpdater.cancel();
694
+ setLiveResponse(null);
695
+ setIsProcessing(false);
696
+ }
697
+ return;
698
+ }
699
+ setIsProcessing(true);
700
+ const liveMessageId = Date.now();
701
+ setLiveResponse({
702
+ id: liveMessageId,
703
+ sender: 'bot',
704
+ text: '',
705
+ });
706
+ 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
713
+ const reasoningUpdater = createStreamingUpdateCoordinator((newReasoningText) => {
714
+ setMessages(prev => {
715
+ if (currentReasoningMessageId !== null) {
716
+ const index = prev.findIndex(msg => msg.id === currentReasoningMessageId);
717
+ if (index === -1)
718
+ return prev;
719
+ const current = prev[index];
720
+ if (current.sender !== 'reasoning') {
721
+ return prev;
722
+ }
723
+ const next = prev.slice();
724
+ next[index] = { ...current, text: newReasoningText };
725
+ return trimMessages(next);
726
+ }
727
+ const newId = Date.now();
728
+ currentReasoningMessageId = newId;
729
+ return trimMessages([
730
+ ...prev,
731
+ {
732
+ id: newId,
733
+ sender: 'reasoning',
734
+ text: newReasoningText,
735
+ },
736
+ ]);
737
+ });
738
+ }, REASONING_RESPONSE_THROTTLE_MS);
739
+ // Create event logger with deduplication for this approval decision
740
+ // const {logDeduplicated, flush: flushLog} = createEventLogger();
741
+ 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;
877
+ }
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;
886
+ }
887
+ default:
888
+ return;
889
+ }
890
+ };
891
+ try {
892
+ const result = await conversationService.handleApprovalDecision(answer, rejectionReason, {
893
+ onEvent: applyConversationEvent,
894
+ });
895
+ applyServiceResult(result, accumulatedText, accumulatedReasoningText, textWasFlushed);
896
+ }
897
+ catch (error) {
898
+ loggingService.error('Error in handleApprovalDecision', {
899
+ error: error instanceof Error ? error.message : String(error),
900
+ stack: error instanceof Error ? error.stack : undefined,
901
+ });
902
+ // Don't show error messages for user-initiated aborts
903
+ if (isAbortLikeError(error)) {
904
+ loggingService.debug('Suppressing abort error in handleApprovalDecision');
905
+ // The finally block will handle cleanup
906
+ return;
907
+ }
908
+ const errorMessage = error instanceof Error ? error.message : String(error);
909
+ const botErrorMessage = {
910
+ id: Date.now(),
911
+ sender: 'bot',
912
+ text: `Error: ${errorMessage}`,
913
+ };
914
+ appendMessages([botErrorMessage]);
915
+ // Reset approval state on error to allow user to continue
916
+ setWaitingForApproval(false);
917
+ setPendingApproval(null);
918
+ }
919
+ finally {
920
+ loggingService.debug('handleApprovalDecision finally block - resetting state');
921
+ // flushLog();
922
+ reasoningUpdater.flush();
923
+ liveResponseUpdater.cancel();
924
+ setLiveResponse(null);
925
+ setIsProcessing(false);
926
+ // Don't reset approval state here - if the result is another approval_required,
927
+ // applyServiceResult will set waitingForApproval=true, but this finally block
928
+ // would immediately clear it, causing the input box to reappear
929
+ }
930
+ }, [
931
+ applyServiceResult,
932
+ conversationService,
933
+ waitingForApproval,
934
+ pendingApproval,
935
+ appendMessages,
936
+ trimMessages,
937
+ loggingService,
938
+ createLiveResponseUpdater,
939
+ ]);
940
+ const clearConversation = useCallback(() => {
941
+ conversationService.reset();
942
+ setMessages([]);
943
+ setWaitingForApproval(false);
944
+ setPendingApproval(null);
945
+ approvedContextRef.current = null;
946
+ setIsProcessing(false);
947
+ setLiveResponse(null);
948
+ }, [conversationService]);
949
+ const stopProcessing = useCallback(() => {
950
+ conversationService.abort();
951
+ setWaitingForApproval(false);
952
+ setWaitingForRejectionReason(false);
953
+ setPendingApproval(null);
954
+ approvedContextRef.current = null;
955
+ setIsProcessing(false);
956
+ setLiveResponse(null);
957
+ }, [conversationService]);
958
+ const setModel = useCallback((model) => {
959
+ conversationService.setModel(model);
960
+ }, [conversationService]);
961
+ const setReasoningEffort = useCallback((effort) => {
962
+ conversationService.setReasoningEffort?.(effort);
963
+ }, [conversationService]);
964
+ const setTemperature = useCallback((temperature) => {
965
+ conversationService.setTemperature?.(temperature);
966
+ }, [conversationService]);
967
+ const addSystemMessage = useCallback((text) => {
968
+ appendMessages([
969
+ {
970
+ id: Date.now(),
971
+ sender: 'system',
972
+ text,
973
+ },
974
+ ]);
975
+ }, [appendMessages]);
976
+ const addShellMessage = useCallback((command, output, exitCode, timedOut) => {
977
+ const success = !timedOut && exitCode === 0;
978
+ const failureReason = timedOut
979
+ ? 'timeout'
980
+ : exitCode == null
981
+ ? 'error'
982
+ : exitCode !== 0
983
+ ? `exit ${exitCode}`
984
+ : undefined;
985
+ appendMessages([
986
+ {
987
+ id: String(Date.now()),
988
+ sender: 'command',
989
+ status: success ? 'completed' : 'failed',
990
+ command,
991
+ output,
992
+ success,
993
+ failureReason,
994
+ toolName: 'shell',
995
+ },
996
+ ]);
997
+ }, [appendMessages]);
998
+ return {
999
+ messages,
1000
+ liveResponse,
1001
+ pendingApproval,
1002
+ waitingForApproval,
1003
+ waitingForRejectionReason,
1004
+ setWaitingForRejectionReason,
1005
+ isProcessing,
1006
+ sendUserMessage,
1007
+ handleApprovalDecision,
1008
+ clearConversation,
1009
+ stopProcessing,
1010
+ setModel,
1011
+ setReasoningEffort,
1012
+ setTemperature,
1013
+ addSystemMessage,
1014
+ addShellMessage,
1015
+ };
1016
+ };
1017
+ //# sourceMappingURL=use-conversation.js.map