@qduc/term2 0.1.4 → 0.1.6

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 (575) hide show
  1. package/dist/agent.d.ts.map +1 -1
  2. package/dist/agent.js +4 -24
  3. package/dist/agent.js.map +1 -1
  4. package/dist/app.d.ts.map +1 -1
  5. package/dist/app.js +12 -33
  6. package/dist/app.js.map +1 -1
  7. package/dist/app.parseInput.test.js +4 -4
  8. package/dist/app.parseInput.test.js.map +1 -1
  9. package/dist/cli.js +44 -29
  10. package/dist/cli.js.map +1 -1
  11. package/dist/components/ApprovalPrompt.d.ts.map +1 -1
  12. package/dist/components/ApprovalPrompt.js +14 -11
  13. package/dist/components/ApprovalPrompt.js.map +1 -1
  14. package/dist/components/Banner.d.ts.map +1 -1
  15. package/dist/components/Banner.js +4 -12
  16. package/dist/components/Banner.js.map +1 -1
  17. package/dist/components/BottomArea.d.ts +2 -0
  18. package/dist/components/BottomArea.d.ts.map +1 -1
  19. package/dist/components/BottomArea.js +6 -9
  20. package/dist/components/BottomArea.js.map +1 -1
  21. package/dist/components/BottomArea.test.js +4 -6
  22. package/dist/components/BottomArea.test.js.map +1 -1
  23. package/dist/components/ChatMessage.d.ts.map +1 -1
  24. package/dist/components/ChatMessage.js.map +1 -1
  25. package/dist/components/CommandMessage.d.ts.map +1 -1
  26. package/dist/components/CommandMessage.js +19 -30
  27. package/dist/components/CommandMessage.js.map +1 -1
  28. package/dist/components/CommandMessage.test.js +2 -2
  29. package/dist/components/CommandMessage.test.js.map +1 -1
  30. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  31. package/dist/components/ErrorBoundary.js +3 -9
  32. package/dist/components/ErrorBoundary.js.map +1 -1
  33. package/dist/components/ErrorBoundary.test.js +2 -2
  34. package/dist/components/ErrorBoundary.test.js.map +1 -1
  35. package/dist/components/Input/PopupManager.d.ts.map +1 -1
  36. package/dist/components/Input/PopupManager.js.map +1 -1
  37. package/dist/components/InputBox.d.ts.map +1 -1
  38. package/dist/components/InputBox.js +15 -33
  39. package/dist/components/InputBox.js.map +1 -1
  40. package/dist/components/InputBox.menu-logic.test.js +6 -7
  41. package/dist/components/InputBox.menu-logic.test.js.map +1 -1
  42. package/dist/components/InputBox.test.js +7 -9
  43. package/dist/components/InputBox.test.js.map +1 -1
  44. package/dist/components/LiveResponse.d.ts.map +1 -1
  45. package/dist/components/LiveResponse.js.map +1 -1
  46. package/dist/components/MarkdownRenderer.d.ts.map +1 -1
  47. package/dist/components/MarkdownRenderer.js +12 -12
  48. package/dist/components/MarkdownRenderer.js.map +1 -1
  49. package/dist/components/MessageList.d.ts.map +1 -1
  50. package/dist/components/MessageList.js.map +1 -1
  51. package/dist/components/MessageList.test.js +1 -1
  52. package/dist/components/MessageList.test.js.map +1 -1
  53. package/dist/components/ModelSelectionMenu.d.ts.map +1 -1
  54. package/dist/components/ModelSelectionMenu.js +35 -31
  55. package/dist/components/ModelSelectionMenu.js.map +1 -1
  56. package/dist/components/ModelSelectionMenu.test.js +9 -9
  57. package/dist/components/ModelSelectionMenu.test.js.map +1 -1
  58. package/dist/components/PathSelectionMenu.d.ts.map +1 -1
  59. package/dist/components/PathSelectionMenu.js +1 -1
  60. package/dist/components/PathSelectionMenu.js.map +1 -1
  61. package/dist/components/SettingsSelectionMenu.d.ts.map +1 -1
  62. package/dist/components/SettingsSelectionMenu.js +1 -1
  63. package/dist/components/SettingsSelectionMenu.js.map +1 -1
  64. package/dist/components/SettingsSelectionMenu.test.js +4 -4
  65. package/dist/components/SettingsSelectionMenu.test.js.map +1 -1
  66. package/dist/components/SettingsValueSelectionMenu.d.ts.map +1 -1
  67. package/dist/components/SettingsValueSelectionMenu.js +5 -6
  68. package/dist/components/SettingsValueSelectionMenu.js.map +1 -1
  69. package/dist/components/SettingsValueSelectionMenu.test.js +5 -5
  70. package/dist/components/SettingsValueSelectionMenu.test.js.map +1 -1
  71. package/dist/components/SlashCommandMenu.d.ts.map +1 -1
  72. package/dist/components/SlashCommandMenu.js +1 -1
  73. package/dist/components/SlashCommandMenu.js.map +1 -1
  74. package/dist/components/StatusBar.d.ts +2 -0
  75. package/dist/components/StatusBar.d.ts.map +1 -1
  76. package/dist/components/StatusBar.js +45 -41
  77. package/dist/components/StatusBar.js.map +1 -1
  78. package/dist/components/StatusBar.test.d.ts +2 -0
  79. package/dist/components/StatusBar.test.d.ts.map +1 -0
  80. package/dist/components/StatusBar.test.js +19 -0
  81. package/dist/components/StatusBar.test.js.map +1 -0
  82. package/dist/context/InputContext.d.ts.map +1 -1
  83. package/dist/context/InputContext.js.map +1 -1
  84. package/dist/context/InputContext.stability.test.js +1 -1
  85. package/dist/context/InputContext.stability.test.js.map +1 -1
  86. package/dist/context/InputContext.test.js +15 -15
  87. package/dist/context/InputContext.test.js.map +1 -1
  88. package/dist/hooks/use-conversation.d.ts +5 -13
  89. package/dist/hooks/use-conversation.d.ts.map +1 -1
  90. package/dist/hooks/use-conversation.js +40 -148
  91. package/dist/hooks/use-conversation.js.map +1 -1
  92. package/dist/hooks/use-input-history.d.ts.map +1 -1
  93. package/dist/hooks/use-input-history.js +1 -3
  94. package/dist/hooks/use-input-history.js.map +1 -1
  95. package/dist/hooks/use-model-selection.d.ts.map +1 -1
  96. package/dist/hooks/use-model-selection.js +9 -35
  97. package/dist/hooks/use-model-selection.js.map +1 -1
  98. package/dist/hooks/use-path-completion.d.ts.map +1 -1
  99. package/dist/hooks/use-path-completion.js +6 -6
  100. package/dist/hooks/use-path-completion.js.map +1 -1
  101. package/dist/hooks/use-setting.d.ts.map +1 -1
  102. package/dist/hooks/use-setting.js +3 -6
  103. package/dist/hooks/use-setting.js.map +1 -1
  104. package/dist/hooks/use-settings-completion.d.ts.map +1 -1
  105. package/dist/hooks/use-settings-completion.js +10 -12
  106. package/dist/hooks/use-settings-completion.js.map +1 -1
  107. package/dist/hooks/use-settings-completion.test.js +46 -46
  108. package/dist/hooks/use-settings-completion.test.js.map +1 -1
  109. package/dist/hooks/use-settings-value-completion.d.ts.map +1 -1
  110. package/dist/hooks/use-settings-value-completion.js +13 -43
  111. package/dist/hooks/use-settings-value-completion.js.map +1 -1
  112. package/dist/hooks/use-settings-value-completion.test.js +12 -18
  113. package/dist/hooks/use-settings-value-completion.test.js.map +1 -1
  114. package/dist/hooks/use-slash-commands.d.ts +1 -1
  115. package/dist/hooks/use-slash-commands.d.ts.map +1 -1
  116. package/dist/hooks/use-slash-commands.js +6 -11
  117. package/dist/hooks/use-slash-commands.js.map +1 -1
  118. package/dist/hooks/use-slash-commands.test.js +24 -24
  119. package/dist/hooks/use-slash-commands.test.js.map +1 -1
  120. package/dist/lib/editor-impl.d.ts.map +1 -1
  121. package/dist/lib/editor-impl.js +6 -16
  122. package/dist/lib/editor-impl.js.map +1 -1
  123. package/dist/lib/editor-impl.test.js.map +1 -1
  124. package/dist/lib/mentor-client.d.ts +41 -0
  125. package/dist/lib/mentor-client.d.ts.map +1 -0
  126. package/dist/lib/mentor-client.js +146 -0
  127. package/dist/lib/mentor-client.js.map +1 -0
  128. package/dist/lib/mentor-client.test.d.ts +2 -0
  129. package/dist/lib/mentor-client.test.d.ts.map +1 -0
  130. package/dist/lib/mentor-client.test.js +440 -0
  131. package/dist/lib/mentor-client.test.js.map +1 -0
  132. package/dist/lib/openai-agent-client.chat.test.js +33 -8
  133. package/dist/lib/openai-agent-client.chat.test.js.map +1 -1
  134. package/dist/lib/openai-agent-client.d.ts.map +1 -1
  135. package/dist/lib/openai-agent-client.flex-tier.test.js +5 -5
  136. package/dist/lib/openai-agent-client.flex-tier.test.js.map +1 -1
  137. package/dist/lib/openai-agent-client.js +99 -88
  138. package/dist/lib/openai-agent-client.js.map +1 -1
  139. package/dist/lib/openai-agent-client.public-methods.test.js +186 -12
  140. package/dist/lib/openai-agent-client.public-methods.test.js.map +1 -1
  141. package/dist/lib/openai-agent-client.test.js +17 -17
  142. package/dist/lib/openai-agent-client.test.js.map +1 -1
  143. package/dist/lib/retry-strategy.d.ts +43 -0
  144. package/dist/lib/retry-strategy.d.ts.map +1 -0
  145. package/dist/lib/retry-strategy.js +93 -0
  146. package/dist/lib/retry-strategy.js.map +1 -0
  147. package/dist/lib/retry-strategy.test.d.ts +2 -0
  148. package/dist/lib/retry-strategy.test.d.ts.map +1 -0
  149. package/dist/lib/retry-strategy.test.js +200 -0
  150. package/dist/lib/retry-strategy.test.js.map +1 -0
  151. package/dist/lib/tool-invoke.d.ts.map +1 -1
  152. package/dist/lib/tool-invoke.js.map +1 -1
  153. package/dist/lib/tool-invoke.test.js.map +1 -1
  154. package/dist/no-singleton-imports.test.js +1 -1
  155. package/dist/no-singleton-imports.test.js.map +1 -1
  156. package/dist/non-interactive.d.ts +20 -0
  157. package/dist/non-interactive.d.ts.map +1 -0
  158. package/dist/non-interactive.js +86 -0
  159. package/dist/non-interactive.js.map +1 -0
  160. package/dist/non-interactive.test.d.ts +2 -0
  161. package/dist/non-interactive.test.d.ts.map +1 -0
  162. package/dist/non-interactive.test.js +213 -0
  163. package/dist/non-interactive.test.js.map +1 -0
  164. package/dist/prompts/anthropic.md +11 -11
  165. package/dist/prompts/codex.md +63 -63
  166. package/dist/prompts/gpt-5.md +95 -95
  167. package/dist/prompts/mentor-addon.md +6 -5
  168. package/dist/prompts/prompt-selector.d.ts +8 -0
  169. package/dist/prompts/prompt-selector.d.ts.map +1 -0
  170. package/dist/prompts/prompt-selector.js +21 -0
  171. package/dist/prompts/prompt-selector.js.map +1 -0
  172. package/dist/prompts/prompt-selector.test.d.ts +2 -0
  173. package/dist/prompts/prompt-selector.test.d.ts.map +1 -0
  174. package/dist/prompts/prompt-selector.test.js +21 -0
  175. package/dist/prompts/prompt-selector.test.js.map +1 -0
  176. package/dist/prompts/prompt-selector.test.ts +32 -0
  177. package/dist/prompts/prompt-selector.ts +30 -0
  178. package/dist/prompts/simple.md +58 -58
  179. package/dist/providers/index.d.ts.map +1 -1
  180. package/dist/providers/index.js.map +1 -1
  181. package/dist/providers/openai-compatible/api.d.ts.map +1 -1
  182. package/dist/providers/openai-compatible/api.js.map +1 -1
  183. package/dist/providers/openai-compatible/model.d.ts.map +1 -1
  184. package/dist/providers/openai-compatible/model.js +19 -53
  185. package/dist/providers/openai-compatible/model.js.map +1 -1
  186. package/dist/providers/openai-compatible/provider.d.ts.map +1 -1
  187. package/dist/providers/openai-compatible/provider.js.map +1 -1
  188. package/dist/providers/openai-compatible/reasoning-content.test.js +2 -2
  189. package/dist/providers/openai-compatible/reasoning-content.test.js.map +1 -1
  190. package/dist/providers/openai-compatible/utils.d.ts.map +1 -1
  191. package/dist/providers/openai-compatible/utils.js.map +1 -1
  192. package/dist/providers/openai-compatible.provider.d.ts.map +1 -1
  193. package/dist/providers/openai-compatible.provider.js +5 -1
  194. package/dist/providers/openai-compatible.provider.js.map +1 -1
  195. package/dist/providers/openai.provider.js +4 -0
  196. package/dist/providers/openai.provider.js.map +1 -1
  197. package/dist/providers/openrouter/api.d.ts.map +1 -1
  198. package/dist/providers/openrouter/api.js +5 -13
  199. package/dist/providers/openrouter/api.js.map +1 -1
  200. package/dist/providers/openrouter/converters.d.ts.map +1 -1
  201. package/dist/providers/openrouter/converters.js +9 -28
  202. package/dist/providers/openrouter/converters.js.map +1 -1
  203. package/dist/providers/openrouter/converters.test.js +7 -11
  204. package/dist/providers/openrouter/converters.test.js.map +1 -1
  205. package/dist/providers/openrouter/index.d.ts.map +1 -1
  206. package/dist/providers/openrouter/index.js.map +1 -1
  207. package/dist/providers/openrouter/model.d.ts.map +1 -1
  208. package/dist/providers/openrouter/model.js +19 -55
  209. package/dist/providers/openrouter/model.js.map +1 -1
  210. package/dist/providers/openrouter/provider.d.ts.map +1 -1
  211. package/dist/providers/openrouter/provider.js.map +1 -1
  212. package/dist/providers/openrouter/reasoning-content.test.js +1 -1
  213. package/dist/providers/openrouter/reasoning-content.test.js.map +1 -1
  214. package/dist/providers/openrouter/utils.d.ts.map +1 -1
  215. package/dist/providers/openrouter/utils.js +2 -3
  216. package/dist/providers/openrouter/utils.js.map +1 -1
  217. package/dist/providers/openrouter.api.retry.test.js.map +1 -1
  218. package/dist/providers/openrouter.provider.createRunner.test.js +1 -1
  219. package/dist/providers/openrouter.provider.createRunner.test.js.map +1 -1
  220. package/dist/providers/openrouter.provider.js +6 -4
  221. package/dist/providers/openrouter.provider.js.map +1 -1
  222. package/dist/providers/openrouter.test.js +13 -17
  223. package/dist/providers/openrouter.test.js.map +1 -1
  224. package/dist/providers/registry.d.ts +5 -0
  225. package/dist/providers/registry.d.ts.map +1 -1
  226. package/dist/providers/registry.js.map +1 -1
  227. package/dist/providers/registry.test.js +11 -11
  228. package/dist/providers/registry.test.js.map +1 -1
  229. package/dist/providers/web-search/index.d.ts +1 -1
  230. package/dist/providers/web-search/index.d.ts.map +1 -1
  231. package/dist/providers/web-search/index.js.map +1 -1
  232. package/dist/providers/web-search/registry.d.ts.map +1 -1
  233. package/dist/providers/web-search/registry.js.map +1 -1
  234. package/dist/providers/web-search/registry.test.js +14 -14
  235. package/dist/providers/web-search/registry.test.js.map +1 -1
  236. package/dist/providers/web-search/tavily.provider.d.ts.map +1 -1
  237. package/dist/providers/web-search/tavily.provider.js +4 -4
  238. package/dist/providers/web-search/tavily.provider.js.map +1 -1
  239. package/dist/providers/web-search/tavily.provider.test.js +7 -7
  240. package/dist/providers/web-search/tavily.provider.test.js.map +1 -1
  241. package/dist/providers/web-search/types.d.ts.map +1 -1
  242. package/dist/scripts/extract-provider-traffic.d.ts +3 -0
  243. package/dist/scripts/extract-provider-traffic.d.ts.map +1 -0
  244. package/dist/scripts/extract-provider-traffic.js +32 -0
  245. package/dist/scripts/extract-provider-traffic.js.map +1 -0
  246. package/dist/services/approval-state.d.ts +24 -0
  247. package/dist/services/approval-state.d.ts.map +1 -0
  248. package/dist/services/approval-state.js +41 -0
  249. package/dist/services/approval-state.js.map +1 -0
  250. package/dist/services/approval-state.test.d.ts +2 -0
  251. package/dist/services/approval-state.test.d.ts.map +1 -0
  252. package/dist/services/approval-state.test.js +59 -0
  253. package/dist/services/approval-state.test.js.map +1 -0
  254. package/dist/services/command-message-streaming.d.ts +12 -0
  255. package/dist/services/command-message-streaming.d.ts.map +1 -0
  256. package/dist/services/command-message-streaming.js +68 -0
  257. package/dist/services/command-message-streaming.js.map +1 -0
  258. package/dist/services/command-message-streaming.test.d.ts +2 -0
  259. package/dist/services/command-message-streaming.test.d.ts.map +1 -0
  260. package/dist/services/command-message-streaming.test.js +78 -0
  261. package/dist/services/command-message-streaming.test.js.map +1 -0
  262. package/dist/services/conversation-events.d.ts +5 -13
  263. package/dist/services/conversation-events.d.ts.map +1 -1
  264. package/dist/services/conversation-integration.test.d.ts +2 -0
  265. package/dist/services/conversation-integration.test.d.ts.map +1 -0
  266. package/dist/services/conversation-integration.test.js +155 -0
  267. package/dist/services/conversation-integration.test.js.map +1 -0
  268. package/dist/services/conversation-result-builder.d.ts +41 -0
  269. package/dist/services/conversation-result-builder.d.ts.map +1 -0
  270. package/dist/services/conversation-result-builder.js +110 -0
  271. package/dist/services/conversation-result-builder.js.map +1 -0
  272. package/dist/services/conversation-result-builder.test.d.ts +2 -0
  273. package/dist/services/conversation-result-builder.test.d.ts.map +1 -0
  274. package/dist/services/conversation-result-builder.test.js +76 -0
  275. package/dist/services/conversation-result-builder.test.js.map +1 -0
  276. package/dist/services/conversation-service.d.ts +1 -1
  277. package/dist/services/conversation-service.d.ts.map +1 -1
  278. package/dist/services/conversation-service.js.map +1 -1
  279. package/dist/services/conversation-session-reasoning.test.js +3 -3
  280. package/dist/services/conversation-session-reasoning.test.js.map +1 -1
  281. package/dist/services/conversation-session.d.ts +7 -17
  282. package/dist/services/conversation-session.d.ts.map +1 -1
  283. package/dist/services/conversation-session.js +290 -278
  284. package/dist/services/conversation-session.js.map +1 -1
  285. package/dist/services/conversation-session.usage.test.d.ts +2 -0
  286. package/dist/services/conversation-session.usage.test.d.ts.map +1 -0
  287. package/dist/services/conversation-session.usage.test.js +59 -0
  288. package/dist/services/conversation-session.usage.test.js.map +1 -0
  289. package/dist/services/conversation-store.d.ts.map +1 -1
  290. package/dist/services/conversation-store.js +3 -9
  291. package/dist/services/conversation-store.js.map +1 -1
  292. package/dist/services/conversation-store.test.js +17 -14
  293. package/dist/services/conversation-store.test.js.map +1 -1
  294. package/dist/services/execution-context.d.ts.map +1 -1
  295. package/dist/services/execution-context.js.map +1 -1
  296. package/dist/services/execution-context.test.js +7 -7
  297. package/dist/services/execution-context.test.js.map +1 -1
  298. package/dist/services/file-service.d.ts.map +1 -1
  299. package/dist/services/file-service.js +5 -9
  300. package/dist/services/file-service.js.map +1 -1
  301. package/dist/services/history-service.d.ts.map +1 -1
  302. package/dist/services/history-service.js +3 -7
  303. package/dist/services/history-service.js.map +1 -1
  304. package/dist/services/logging-contract.d.ts +74 -0
  305. package/dist/services/logging-contract.d.ts.map +1 -0
  306. package/dist/services/logging-contract.js +157 -0
  307. package/dist/services/logging-contract.js.map +1 -0
  308. package/dist/services/logging-contract.test.d.ts +2 -0
  309. package/dist/services/logging-contract.test.d.ts.map +1 -0
  310. package/dist/services/logging-contract.test.js +66 -0
  311. package/dist/services/logging-contract.test.js.map +1 -0
  312. package/dist/services/logging-service.d.ts +5 -0
  313. package/dist/services/logging-service.d.ts.map +1 -1
  314. package/dist/services/logging-service.js +93 -21
  315. package/dist/services/logging-service.js.map +1 -1
  316. package/dist/services/model-service.d.ts.map +1 -1
  317. package/dist/services/model-service.js +2 -2
  318. package/dist/services/model-service.js.map +1 -1
  319. package/dist/services/model-service.test.js +6 -6
  320. package/dist/services/model-service.test.js.map +1 -1
  321. package/dist/services/service-interfaces.d.ts.map +1 -1
  322. package/dist/services/settings-env.d.ts +9 -0
  323. package/dist/services/settings-env.d.ts.map +1 -0
  324. package/dist/services/settings-env.js +75 -0
  325. package/dist/services/settings-env.js.map +1 -0
  326. package/dist/services/settings-env.test.d.ts +2 -0
  327. package/dist/services/settings-env.test.d.ts.map +1 -0
  328. package/dist/services/settings-env.test.js +32 -0
  329. package/dist/services/settings-env.test.js.map +1 -0
  330. package/dist/services/settings-merger.d.ts +26 -0
  331. package/dist/services/settings-merger.d.ts.map +1 -0
  332. package/dist/services/settings-merger.js +122 -0
  333. package/dist/services/settings-merger.js.map +1 -0
  334. package/dist/services/settings-merger.test.d.ts +2 -0
  335. package/dist/services/settings-merger.test.d.ts.map +1 -0
  336. package/dist/services/settings-merger.test.js +26 -0
  337. package/dist/services/settings-merger.test.js.map +1 -0
  338. package/dist/services/settings-persistence.d.ts +32 -0
  339. package/dist/services/settings-persistence.d.ts.map +1 -0
  340. package/dist/services/settings-persistence.js +134 -0
  341. package/dist/services/settings-persistence.js.map +1 -0
  342. package/dist/services/settings-persistence.test.d.ts +2 -0
  343. package/dist/services/settings-persistence.test.d.ts.map +1 -0
  344. package/dist/services/settings-persistence.test.js +51 -0
  345. package/dist/services/settings-persistence.test.js.map +1 -0
  346. package/dist/services/settings-schema.d.ts +320 -0
  347. package/dist/services/settings-schema.d.ts.map +1 -0
  348. package/dist/services/settings-schema.js +272 -0
  349. package/dist/services/settings-schema.js.map +1 -0
  350. package/dist/services/settings-service.d.ts +4 -248
  351. package/dist/services/settings-service.d.ts.map +1 -1
  352. package/dist/services/settings-service.js +45 -641
  353. package/dist/services/settings-service.js.map +1 -1
  354. package/dist/services/settings-service.mock.d.ts.map +1 -1
  355. package/dist/services/settings-service.mock.js.map +1 -1
  356. package/dist/services/singleton-deprecation.test.js +2 -2
  357. package/dist/services/singleton-deprecation.test.js.map +1 -1
  358. package/dist/services/ssh-service.d.ts.map +1 -1
  359. package/dist/services/ssh-service.js +8 -7
  360. package/dist/services/ssh-service.js.map +1 -1
  361. package/dist/services/ssh-service.test.js +15 -13
  362. package/dist/services/ssh-service.test.js.map +1 -1
  363. package/dist/services/stream-event-parsing.d.ts +4 -0
  364. package/dist/services/stream-event-parsing.d.ts.map +1 -0
  365. package/dist/services/stream-event-parsing.js +76 -0
  366. package/dist/services/stream-event-parsing.js.map +1 -0
  367. package/dist/services/stream-event-parsing.test.d.ts +2 -0
  368. package/dist/services/stream-event-parsing.test.d.ts.map +1 -0
  369. package/dist/services/stream-event-parsing.test.js +59 -0
  370. package/dist/services/stream-event-parsing.test.js.map +1 -0
  371. package/dist/services/stream-event-processor.d.ts +32 -0
  372. package/dist/services/stream-event-processor.d.ts.map +1 -0
  373. package/dist/services/stream-event-processor.js +344 -0
  374. package/dist/services/stream-event-processor.js.map +1 -0
  375. package/dist/services/stream-event-processor.test.d.ts +2 -0
  376. package/dist/services/stream-event-processor.test.d.ts.map +1 -0
  377. package/dist/services/stream-event-processor.test.js +117 -0
  378. package/dist/services/stream-event-processor.test.js.map +1 -0
  379. package/dist/tools/apply-patch.d.ts.map +1 -1
  380. package/dist/tools/apply-patch.js +45 -35
  381. package/dist/tools/apply-patch.js.map +1 -1
  382. package/dist/tools/apply-patch.test.js +23 -0
  383. package/dist/tools/apply-patch.test.js.map +1 -1
  384. package/dist/tools/ask-mentor.d.ts.map +1 -1
  385. package/dist/tools/ask-mentor.js +5 -15
  386. package/dist/tools/ask-mentor.js.map +1 -1
  387. package/dist/tools/ask-mentor.test.js +1 -1
  388. package/dist/tools/ask-mentor.test.js.map +1 -1
  389. package/dist/tools/create-file.d.ts.map +1 -1
  390. package/dist/tools/create-file.js +2 -2
  391. package/dist/tools/create-file.js.map +1 -1
  392. package/dist/tools/create-file.test.js.map +1 -1
  393. package/dist/tools/edit-healing.d.ts.map +1 -1
  394. package/dist/tools/edit-healing.js +5 -4
  395. package/dist/tools/edit-healing.js.map +1 -1
  396. package/dist/tools/edit-healing.test.js.map +1 -1
  397. package/dist/tools/find-files.d.ts +1 -0
  398. package/dist/tools/find-files.d.ts.map +1 -1
  399. package/dist/tools/find-files.js +93 -24
  400. package/dist/tools/find-files.js.map +1 -1
  401. package/dist/tools/find-files.test.js +46 -2
  402. package/dist/tools/find-files.test.js.map +1 -1
  403. package/dist/tools/format-helpers.d.ts.map +1 -1
  404. package/dist/tools/format-helpers.js +2 -6
  405. package/dist/tools/format-helpers.js.map +1 -1
  406. package/dist/tools/grep.d.ts +1 -1
  407. package/dist/tools/grep.d.ts.map +1 -1
  408. package/dist/tools/grep.js +6 -21
  409. package/dist/tools/grep.js.map +1 -1
  410. package/dist/tools/read-file.d.ts.map +1 -1
  411. package/dist/tools/read-file.js +3 -7
  412. package/dist/tools/read-file.js.map +1 -1
  413. package/dist/tools/read-file.test.js.map +1 -1
  414. package/dist/tools/search-replace.d.ts.map +1 -1
  415. package/dist/tools/search-replace.js +14 -31
  416. package/dist/tools/search-replace.js.map +1 -1
  417. package/dist/tools/search-replace.test.js.map +1 -1
  418. package/dist/tools/search.d.ts +1 -1
  419. package/dist/tools/search.d.ts.map +1 -1
  420. package/dist/tools/search.js +6 -16
  421. package/dist/tools/search.js.map +1 -1
  422. package/dist/tools/shell.d.ts +1 -1
  423. package/dist/tools/shell.d.ts.map +1 -1
  424. package/dist/tools/shell.js +25 -51
  425. package/dist/tools/shell.js.map +1 -1
  426. package/dist/tools/shell.test.d.ts +2 -0
  427. package/dist/tools/shell.test.d.ts.map +1 -0
  428. package/dist/tools/shell.test.js +70 -0
  429. package/dist/tools/shell.test.js.map +1 -0
  430. package/dist/tools/tool-names.d.ts +3 -0
  431. package/dist/tools/tool-names.d.ts.map +1 -0
  432. package/dist/tools/tool-names.js +3 -0
  433. package/dist/tools/tool-names.js.map +1 -0
  434. package/dist/tools/tool-names.test.d.ts +2 -0
  435. package/dist/tools/tool-names.test.d.ts.map +1 -0
  436. package/dist/tools/tool-names.test.js +7 -0
  437. package/dist/tools/tool-names.test.js.map +1 -0
  438. package/dist/tools/types.d.ts.map +1 -1
  439. package/dist/tools/utils.d.ts.map +1 -1
  440. package/dist/tools/utils.js +3 -8
  441. package/dist/tools/utils.js.map +1 -1
  442. package/dist/tools/web-fetch.d.ts +4 -4
  443. package/dist/tools/web-fetch.d.ts.map +1 -1
  444. package/dist/tools/web-fetch.js +32 -249
  445. package/dist/tools/web-fetch.js.map +1 -1
  446. package/dist/tools/web-fetch.test.js +3 -3
  447. package/dist/tools/web-fetch.test.js.map +1 -1
  448. package/dist/tools/web-search.d.ts.map +1 -1
  449. package/dist/tools/web-search.js +6 -10
  450. package/dist/tools/web-search.js.map +1 -1
  451. package/dist/tools/web-search.test.js +12 -14
  452. package/dist/tools/web-search.test.js.map +1 -1
  453. package/dist/types/messages.d.ts +29 -0
  454. package/dist/types/messages.d.ts.map +1 -0
  455. package/dist/types/messages.js +2 -0
  456. package/dist/types/messages.js.map +1 -0
  457. package/dist/utils/command-logger.d.ts.map +1 -1
  458. package/dist/utils/command-logger.js.map +1 -1
  459. package/dist/utils/command-safety/constants.d.ts.map +1 -1
  460. package/dist/utils/command-safety/constants.js +1 -9
  461. package/dist/utils/command-safety/constants.js.map +1 -1
  462. package/dist/utils/command-safety/find-helpers.d.ts.map +1 -1
  463. package/dist/utils/command-safety/find-helpers.js +4 -9
  464. package/dist/utils/command-safety/find-helpers.js.map +1 -1
  465. package/dist/utils/command-safety/handlers/find-handler.d.ts.map +1 -1
  466. package/dist/utils/command-safety/handlers/find-handler.js +3 -10
  467. package/dist/utils/command-safety/handlers/find-handler.js.map +1 -1
  468. package/dist/utils/command-safety/handlers/git-handler.d.ts.map +1 -1
  469. package/dist/utils/command-safety/handlers/git-handler.js +2 -4
  470. package/dist/utils/command-safety/handlers/git-handler.js.map +1 -1
  471. package/dist/utils/command-safety/handlers/index.d.ts +1 -1
  472. package/dist/utils/command-safety/handlers/index.d.ts.map +1 -1
  473. package/dist/utils/command-safety/handlers/index.js.map +1 -1
  474. package/dist/utils/command-safety/handlers/sed-handler.d.ts.map +1 -1
  475. package/dist/utils/command-safety/handlers/sed-handler.js.map +1 -1
  476. package/dist/utils/command-safety/handlers/types.d.ts.map +1 -1
  477. package/dist/utils/command-safety/index.d.ts.map +1 -1
  478. package/dist/utils/command-safety/index.js +4 -10
  479. package/dist/utils/command-safety/index.js.map +1 -1
  480. package/dist/utils/command-safety/path-analysis.d.ts.map +1 -1
  481. package/dist/utils/command-safety/path-analysis.js +9 -20
  482. package/dist/utils/command-safety/path-analysis.js.map +1 -1
  483. package/dist/utils/command-safety/utils.d.ts.map +1 -1
  484. package/dist/utils/command-safety/utils.js +1 -3
  485. package/dist/utils/command-safety/utils.js.map +1 -1
  486. package/dist/utils/command-safety.find.test.js +44 -87
  487. package/dist/utils/command-safety.find.test.js.map +1 -1
  488. package/dist/utils/command-safety.path.test.js +32 -67
  489. package/dist/utils/command-safety.path.test.js.map +1 -1
  490. package/dist/utils/conversation-event-handler.d.ts +4 -16
  491. package/dist/utils/conversation-event-handler.d.ts.map +1 -1
  492. package/dist/utils/conversation-event-handler.js +4 -8
  493. package/dist/utils/conversation-event-handler.js.map +1 -1
  494. package/dist/utils/conversation-event-handler.test.js +16 -18
  495. package/dist/utils/conversation-event-handler.test.js.map +1 -1
  496. package/dist/utils/conversation-utils.d.ts.map +1 -1
  497. package/dist/utils/conversation-utils.js +5 -8
  498. package/dist/utils/conversation-utils.js.map +1 -1
  499. package/dist/utils/conversation-utils.test.js +45 -44
  500. package/dist/utils/conversation-utils.test.js.map +1 -1
  501. package/dist/utils/diff.d.ts.map +1 -1
  502. package/dist/utils/diff.js +2 -4
  503. package/dist/utils/diff.js.map +1 -1
  504. package/dist/utils/diff.test.js +12 -12
  505. package/dist/utils/diff.test.js.map +1 -1
  506. package/dist/utils/error-helpers.d.ts.map +1 -1
  507. package/dist/utils/error-helpers.js +2 -6
  508. package/dist/utils/error-helpers.js.map +1 -1
  509. package/dist/utils/error-helpers.test.js +32 -32
  510. package/dist/utils/error-helpers.test.js.map +1 -1
  511. package/dist/utils/execute-shell.d.ts.map +1 -1
  512. package/dist/utils/execute-shell.js.map +1 -1
  513. package/dist/utils/execute-shell.test.js.map +1 -1
  514. package/dist/utils/extract-command-messages.d.ts.map +1 -1
  515. package/dist/utils/extract-command-messages.js +16 -21
  516. package/dist/utils/extract-command-messages.js.map +1 -1
  517. package/dist/utils/ink-render-options.d.ts.map +1 -1
  518. package/dist/utils/ink-render-options.js.map +1 -1
  519. package/dist/utils/log-viewer-filters.d.ts +29 -0
  520. package/dist/utils/log-viewer-filters.d.ts.map +1 -0
  521. package/dist/utils/log-viewer-filters.js +49 -0
  522. package/dist/utils/log-viewer-filters.js.map +1 -0
  523. package/dist/utils/log-viewer-filters.test.d.ts +2 -0
  524. package/dist/utils/log-viewer-filters.test.d.ts.map +1 -0
  525. package/dist/utils/log-viewer-filters.test.js +48 -0
  526. package/dist/utils/log-viewer-filters.test.js.map +1 -0
  527. package/dist/utils/message-buffer.d.ts.map +1 -1
  528. package/dist/utils/message-buffer.js.map +1 -1
  529. package/dist/utils/message-buffer.test.js +4 -4
  530. package/dist/utils/message-buffer.test.js.map +1 -1
  531. package/dist/utils/output-trim.d.ts.map +1 -1
  532. package/dist/utils/output-trim.js.map +1 -1
  533. package/dist/utils/provider-credentials.d.ts.map +1 -1
  534. package/dist/utils/provider-credentials.js +1 -1
  535. package/dist/utils/provider-credentials.js.map +1 -1
  536. package/dist/utils/provider-traffic-extractor.d.ts +18 -0
  537. package/dist/utils/provider-traffic-extractor.d.ts.map +1 -0
  538. package/dist/utils/provider-traffic-extractor.js +121 -0
  539. package/dist/utils/provider-traffic-extractor.js.map +1 -0
  540. package/dist/utils/provider-traffic-extractor.test.d.ts +2 -0
  541. package/dist/utils/provider-traffic-extractor.test.d.ts.map +1 -0
  542. package/dist/utils/provider-traffic-extractor.test.js +84 -0
  543. package/dist/utils/provider-traffic-extractor.test.js.map +1 -0
  544. package/dist/utils/settings-command.d.ts.map +1 -1
  545. package/dist/utils/settings-command.js +5 -12
  546. package/dist/utils/settings-command.js.map +1 -1
  547. package/dist/utils/ssh-config-parser.d.ts.map +1 -1
  548. package/dist/utils/ssh-config-parser.js.map +1 -1
  549. package/dist/utils/ssh-config-parser.test.js.map +1 -1
  550. package/dist/utils/streaming-session-factory.d.ts +33 -0
  551. package/dist/utils/streaming-session-factory.d.ts.map +1 -0
  552. package/dist/utils/streaming-session-factory.js +75 -0
  553. package/dist/utils/streaming-session-factory.js.map +1 -0
  554. package/dist/utils/streaming-session-factory.test.d.ts +2 -0
  555. package/dist/utils/streaming-session-factory.test.d.ts.map +1 -0
  556. package/dist/utils/streaming-session-factory.test.js +74 -0
  557. package/dist/utils/streaming-session-factory.test.js.map +1 -0
  558. package/dist/utils/streaming-updater.d.ts.map +1 -1
  559. package/dist/utils/streaming-updater.js.map +1 -1
  560. package/dist/utils/throttle.d.ts.map +1 -1
  561. package/dist/utils/throttle.js.map +1 -1
  562. package/dist/utils/token-usage.d.ts +16 -0
  563. package/dist/utils/token-usage.d.ts.map +1 -0
  564. package/dist/utils/token-usage.js +109 -0
  565. package/dist/utils/token-usage.js.map +1 -0
  566. package/dist/utils/token-usage.test.d.ts +2 -0
  567. package/dist/utils/token-usage.test.d.ts.map +1 -0
  568. package/dist/utils/token-usage.test.js +38 -0
  569. package/dist/utils/token-usage.test.js.map +1 -0
  570. package/dist/utils/trim-tool-output.d.ts +2 -0
  571. package/dist/utils/trim-tool-output.d.ts.map +1 -0
  572. package/dist/utils/trim-tool-output.js +52 -0
  573. package/dist/utils/trim-tool-output.js.map +1 -0
  574. package/package.json +117 -110
  575. package/{README.md → readme.md} +208 -136
@@ -1,6 +1,12 @@
1
- import { extractCommandMessages, markToolCallAsApprovalRejection, } from '../utils/extract-command-messages.js';
1
+ import { extractCommandMessages, markToolCallAsApprovalRejection } from '../utils/extract-command-messages.js';
2
2
  import { ConversationStore } from './conversation-store.js';
3
3
  import { ModelBehaviorError } from '@openai/agents';
4
+ import { extractUsage } from '../utils/token-usage.js';
5
+ import { getProvider } from '../providers/index.js';
6
+ import { extractReasoningDelta, extractTextDelta } from './stream-event-parsing.js';
7
+ import { captureToolCallArguments, emitCommandMessagesFromItems } from './command-message-streaming.js';
8
+ import { ApprovalState } from './approval-state.js';
9
+ import { createInvalidToolCallDiagnostic } from './logging-contract.js';
4
10
  const getCommandFromArgs = (args) => {
5
11
  if (!args) {
6
12
  return '';
@@ -16,7 +22,7 @@ const getCommandFromArgs = (args) => {
16
22
  if (Array.isArray(parsed?.commands)) {
17
23
  return parsed.commands.join('\n');
18
24
  }
19
- return args;
25
+ return JSON.stringify(parsed);
20
26
  }
21
27
  catch {
22
28
  return args;
@@ -29,7 +35,21 @@ const getCommandFromArgs = (args) => {
29
35
  if ('commands' in args && Array.isArray(args.commands)) {
30
36
  return args.commands.join('\n');
31
37
  }
32
- const argsFromObject = 'arguments' in args ? String(args.arguments) : undefined;
38
+ let argsFromObject;
39
+ if ('arguments' in args) {
40
+ const rawArguments = args.arguments;
41
+ if (typeof rawArguments === 'string') {
42
+ try {
43
+ argsFromObject = JSON.stringify(JSON.parse(rawArguments));
44
+ }
45
+ catch {
46
+ argsFromObject = String(rawArguments);
47
+ }
48
+ }
49
+ else if (rawArguments !== undefined) {
50
+ argsFromObject = String(rawArguments);
51
+ }
52
+ }
33
53
  return cmdFromObject ?? argsFromObject ?? JSON.stringify(args);
34
54
  }
35
55
  return String(args);
@@ -48,19 +68,23 @@ const isToolHallucinationError = (error) => {
48
68
  const message = error.message.toLowerCase();
49
69
  return message.includes('tool') && message.includes('not found');
50
70
  };
71
+ const supportsConversationChaining = (providerId) => {
72
+ const providerDef = getProvider(providerId);
73
+ return providerDef?.capabilities?.supportsConversationChaining ?? false;
74
+ };
51
75
  export class ConversationSession {
52
76
  id;
53
77
  agentClient;
54
78
  logger;
55
79
  conversationStore;
56
80
  previousResponseId = null;
57
- pendingApprovalContext = null;
58
- abortedApprovalContext = null;
81
+ approvalState = new ApprovalState();
59
82
  textDeltaCount = 0;
60
83
  reasoningDeltaCount = 0;
61
84
  toolCallArgumentsById = new Map();
62
85
  lastEventType = null;
63
86
  eventTypeCount = 0;
87
+ emittedInvalidToolCallPackets = new Set();
64
88
  // private logStreamEvent = (eventType: string, eventData: any) => {
65
89
  // if (eventData.item) {
66
90
  // eventType = eventData.item.type;
@@ -97,7 +121,7 @@ export class ConversationSession {
97
121
  this.lastEventType = null;
98
122
  this.eventTypeCount = 0;
99
123
  };
100
- constructor(id, { agentClient, deps, }) {
124
+ constructor(id, { agentClient, deps }) {
101
125
  this.id = id;
102
126
  this.agentClient = agentClient;
103
127
  this.logger = deps.logger;
@@ -106,8 +130,8 @@ export class ConversationSession {
106
130
  reset() {
107
131
  this.previousResponseId = null;
108
132
  this.conversationStore.clear();
109
- this.pendingApprovalContext = null;
110
- this.abortedApprovalContext = null;
133
+ this.approvalState.clearPending();
134
+ this.approvalState.consumeAborted();
111
135
  this.toolCallArgumentsById.clear();
112
136
  if (typeof this.agentClient.clearConversations === 'function') {
113
137
  this.agentClient.clearConversations();
@@ -145,10 +169,14 @@ export class ConversationSession {
145
169
  abort() {
146
170
  this.agentClient.abort();
147
171
  // Save pending approval context so we can handle it in the next message
148
- if (this.pendingApprovalContext) {
149
- this.abortedApprovalContext = this.pendingApprovalContext;
150
- this.pendingApprovalContext = null;
151
- this.logger.debug('Aborted approval - will handle rejection on next message');
172
+ if (this.approvalState.abortPending()) {
173
+ this.logger.debug('Aborted approval - will handle rejection on next message', {
174
+ eventType: 'approval.aborted',
175
+ category: 'approval',
176
+ phase: 'abort',
177
+ sessionId: this.id,
178
+ traceId: this.logger.getCorrelationId(),
179
+ });
152
180
  }
153
181
  }
154
182
  /**
@@ -159,34 +187,40 @@ export class ConversationSession {
159
187
  async *run(text, { hallucinationRetryCount = 0, skipUserMessage = false, } = {}) {
160
188
  let stream = null;
161
189
  try {
190
+ this.logger.info('Conversation stream start', {
191
+ eventType: 'stream.started',
192
+ category: 'stream',
193
+ phase: 'request_start',
194
+ sessionId: this.id,
195
+ traceId: this.logger.getCorrelationId(),
196
+ });
197
+ const abortedContext = this.approvalState.consumeAborted();
198
+ const shouldAddUserMessage = !skipUserMessage && !abortedContext;
162
199
  // Maintain canonical local history regardless of provider.
163
- if (!skipUserMessage) {
200
+ if (shouldAddUserMessage) {
164
201
  this.conversationStore.addUserMessage(text);
165
202
  }
166
203
  // If there's an aborted approval, we need to resolve it first.
167
204
  // The user's message is a new input, but the agent is stuck waiting for tool output.
168
- if (this.abortedApprovalContext) {
205
+ if (abortedContext) {
169
206
  this.logger.debug('Resolving aborted approval with fake execution', {
170
207
  message: text,
171
208
  });
172
- const { state, interruption, emittedCommandIds, toolCallArgumentsById, } = this.abortedApprovalContext;
173
- this.abortedApprovalContext = null;
209
+ const { state, interruption, emittedCommandIds, toolCallArgumentsById } = abortedContext;
174
210
  // Restore cached tool-call arguments captured before abort so continuation can attach them
175
211
  this.toolCallArgumentsById.clear();
176
212
  if (toolCallArgumentsById?.size) {
177
- for (const [key, value,] of toolCallArgumentsById.entries()) {
213
+ for (const [key, value] of toolCallArgumentsById.entries()) {
178
214
  this.toolCallArgumentsById.set(key, value);
179
215
  }
180
216
  }
181
217
  // Add interceptor for this tool execution
182
218
  const toolName = interruption.name ?? 'unknown';
183
- const expectedCallId = interruption.rawItem?.callId ??
184
- interruption.callId;
219
+ const expectedCallId = interruption.rawItem?.callId ?? interruption.callId;
185
220
  const rejectionMessage = `Tool execution was not approved. User provided new input instead: ${text}`;
186
221
  const removeInterceptor = this.agentClient.addToolInterceptor(async (name, _params, toolCallId) => {
187
222
  // Match both tool name and call ID for stricter matching
188
- if (name === toolName &&
189
- (!expectedCallId || toolCallId === expectedCallId)) {
223
+ if (name === toolName && (!expectedCallId || toolCallId === expectedCallId)) {
190
224
  markToolCallAsApprovalRejection(toolCallId ?? expectedCallId);
191
225
  return rejectionMessage;
192
226
  }
@@ -201,6 +235,7 @@ export class ConversationSession {
201
235
  finalOutput: '',
202
236
  reasoningOutput: '',
203
237
  emittedCommandIds: new Set(emittedCommandIds),
238
+ latestUsage: undefined,
204
239
  };
205
240
  yield* this.#streamEvents(stream, acc, {
206
241
  preserveExistingToolArgs: true,
@@ -208,11 +243,10 @@ export class ConversationSession {
208
243
  this.previousResponseId = stream.lastResponseId;
209
244
  this.conversationStore.updateFromResult(stream);
210
245
  // Check if another interruption occurred
211
- if (stream.interruptions &&
212
- stream.interruptions.length > 0) {
246
+ if (stream.interruptions && stream.interruptions.length > 0) {
213
247
  this.logger.warn('Another interruption occurred after fake execution - handling as approval');
214
248
  // Let the normal flow handle this
215
- const result = this.#buildResult(stream, acc.finalOutput, acc.reasoningOutput, acc.emittedCommandIds);
249
+ const result = this.#buildResult(stream, acc.finalOutput, acc.reasoningOutput, acc.emittedCommandIds, acc.latestUsage);
216
250
  // Re-emit the terminal event explicitly.
217
251
  if (result.type === 'approval_required') {
218
252
  const interruption = result.approval.rawInterruption;
@@ -236,19 +270,16 @@ export class ConversationSession {
236
270
  yield {
237
271
  type: 'final',
238
272
  finalText: result.finalText,
239
- ...(result.reasoningText
240
- ? { reasoningText: result.reasoningText }
241
- : {}),
242
- ...(result.commandMessages?.length
243
- ? { commandMessages: result.commandMessages }
244
- : {}),
273
+ ...(result.reasoningText ? { reasoningText: result.reasoningText } : {}),
274
+ ...(result.commandMessages?.length ? { commandMessages: result.commandMessages } : {}),
275
+ ...(result.usage ? { usage: result.usage } : {}),
245
276
  };
246
277
  }
247
278
  return;
248
279
  }
249
280
  // Successfully resolved - agent should now have processed the fake rejection
250
281
  this.logger.debug('Fake execution completed, agent received rejection message');
251
- const result = this.#buildResult(stream, acc.finalOutput, acc.reasoningOutput, acc.emittedCommandIds);
282
+ const result = this.#buildResult(stream, acc.finalOutput, acc.reasoningOutput, acc.emittedCommandIds, acc.latestUsage);
252
283
  if (result.type === 'approval_required') {
253
284
  const interruption = result.approval.rawInterruption;
254
285
  const callId = interruption?.rawItem?.callId ??
@@ -271,21 +302,16 @@ export class ConversationSession {
271
302
  yield {
272
303
  type: 'final',
273
304
  finalText: result.finalText,
274
- ...(result.reasoningText
275
- ? { reasoningText: result.reasoningText }
276
- : {}),
277
- ...(result.commandMessages?.length
278
- ? { commandMessages: result.commandMessages }
279
- : {}),
305
+ ...(result.reasoningText ? { reasoningText: result.reasoningText } : {}),
306
+ ...(result.commandMessages?.length ? { commandMessages: result.commandMessages } : {}),
307
+ ...(result.usage ? { usage: result.usage } : {}),
280
308
  };
281
309
  }
282
310
  return;
283
311
  }
284
312
  catch (error) {
285
313
  this.logger.warn('Error resolving aborted approval with fake execution', {
286
- error: error instanceof Error
287
- ? error.message
288
- : String(error),
314
+ error: error instanceof Error ? error.message : String(error),
289
315
  });
290
316
  // Fall through to normal message flow
291
317
  }
@@ -298,17 +324,15 @@ export class ConversationSession {
298
324
  const provider = typeof this.agentClient.getProvider === 'function'
299
325
  ? this.agentClient.getProvider()
300
326
  : 'openai';
301
- // Only OpenAI uses server-side conversation management via previousResponseId.
302
- // All other providers (OpenRouter, openai-compatible) need full history.
303
- stream = await this.agentClient.startStream(provider !== 'openai'
304
- ? this.conversationStore.getHistory()
305
- : text, {
327
+ const supportsChaining = supportsConversationChaining(provider);
328
+ stream = await this.agentClient.startStream(supportsChaining ? text : this.conversationStore.getHistory(), {
306
329
  previousResponseId: this.previousResponseId,
307
330
  });
308
331
  const acc = {
309
332
  finalOutput: '',
310
333
  reasoningOutput: '',
311
334
  emittedCommandIds: new Set(),
335
+ latestUsage: undefined,
312
336
  };
313
337
  yield* this.#streamEvents(stream, acc, {
314
338
  preserveExistingToolArgs: false,
@@ -316,8 +340,16 @@ export class ConversationSession {
316
340
  this.previousResponseId = stream.lastResponseId;
317
341
  this.conversationStore.updateFromResult(stream);
318
342
  // Build terminal event (approval_required or final)
319
- const result = this.#buildResult(stream, acc.finalOutput || undefined, acc.reasoningOutput || undefined, acc.emittedCommandIds);
343
+ const result = this.#buildResult(stream, acc.finalOutput || undefined, acc.reasoningOutput || undefined, acc.emittedCommandIds, acc.latestUsage);
320
344
  if (result.type === 'approval_required') {
345
+ this.logger.info('Tool approval required', {
346
+ eventType: 'approval.required',
347
+ category: 'approval',
348
+ phase: 'approval',
349
+ sessionId: this.id,
350
+ traceId: this.logger.getCorrelationId(),
351
+ toolName: result.approval.toolName,
352
+ });
321
353
  const interruption = result.approval.rawInterruption;
322
354
  const callId = interruption?.rawItem?.callId ??
323
355
  interruption?.callId ??
@@ -339,26 +371,26 @@ export class ConversationSession {
339
371
  yield {
340
372
  type: 'final',
341
373
  finalText: result.finalText,
342
- ...(result.reasoningText
343
- ? { reasoningText: result.reasoningText }
344
- : {}),
345
- ...(result.commandMessages?.length
346
- ? { commandMessages: result.commandMessages }
347
- : {}),
374
+ ...(result.reasoningText ? { reasoningText: result.reasoningText } : {}),
375
+ ...(result.commandMessages?.length ? { commandMessages: result.commandMessages } : {}),
376
+ ...(result.usage ? { usage: result.usage } : {}),
348
377
  };
349
378
  }
350
379
  catch (error) {
351
380
  // Handle tool hallucination: model called a non-existent tool
352
- if (isToolHallucinationError(error) &&
353
- hallucinationRetryCount < MAX_HALLUCINATION_RETRIES) {
354
- const toolName = error instanceof Error
355
- ? error.message.match(/Tool (\S+) not found/)?.[1] ||
356
- 'unknown'
357
- : 'unknown';
381
+ if (isToolHallucinationError(error) && hallucinationRetryCount < MAX_HALLUCINATION_RETRIES) {
382
+ const toolName = error instanceof Error ? error.message.match(/Tool (\S+) not found/)?.[1] || 'unknown' : 'unknown';
358
383
  this.logger.warn('Tool hallucination detected, retrying', {
384
+ eventType: 'retry.hallucination',
385
+ category: 'retry',
386
+ phase: 'retry',
359
387
  toolName,
388
+ retryType: 'hallucination',
389
+ retryAttempt: hallucinationRetryCount + 1,
360
390
  attempt: hallucinationRetryCount + 1,
361
391
  maxRetries: MAX_HALLUCINATION_RETRIES,
392
+ sessionId: this.id,
393
+ traceId: this.logger.getCorrelationId(),
362
394
  errorMessage: error instanceof Error ? error.message : String(error),
363
395
  });
364
396
  yield {
@@ -391,6 +423,14 @@ export class ConversationSession {
391
423
  type: 'error',
392
424
  message: error instanceof Error ? error.message : String(error),
393
425
  };
426
+ this.logger.error('Conversation stream error', {
427
+ eventType: 'stream.failed',
428
+ category: 'stream',
429
+ phase: 'abort',
430
+ sessionId: this.id,
431
+ traceId: this.logger.getCorrelationId(),
432
+ errorMessage: error instanceof Error ? error.message : String(error),
433
+ });
394
434
  throw error;
395
435
  }
396
436
  }
@@ -400,23 +440,31 @@ export class ConversationSession {
400
440
  * Named as a string-literal because `continue` is a keyword.
401
441
  */
402
442
  async *['continue']({ answer, rejectionReason, }) {
403
- if (!this.pendingApprovalContext) {
443
+ const pendingApprovalContext = this.approvalState.getPending();
444
+ if (!pendingApprovalContext) {
404
445
  return;
405
446
  }
406
- const { state, interruption, emittedCommandIds: previouslyEmittedIds, toolCallArgumentsById, } = this.pendingApprovalContext;
447
+ const { state, interruption, emittedCommandIds: previouslyEmittedIds, toolCallArgumentsById, } = pendingApprovalContext;
448
+ let removeInterceptor = null;
407
449
  if (answer === 'y') {
450
+ this.logger.info('Tool approval granted', {
451
+ eventType: 'approval.granted',
452
+ category: 'approval',
453
+ phase: 'approval',
454
+ sessionId: this.id,
455
+ traceId: this.logger.getCorrelationId(),
456
+ });
408
457
  state.approve(interruption);
409
458
  }
410
459
  else {
411
- // If rejection reason provided, inject it as a tool interceptor result
412
- if (rejectionReason) {
413
- const toolName = interruption.name ?? 'unknown';
414
- const expectedCallId = interruption.rawItem?.callId ??
415
- interruption.callId;
416
- const rejectionMessage = `Tool execution was not approved. User's reason: ${rejectionReason}`;
460
+ const toolName = interruption.name ?? 'unknown';
461
+ const expectedCallId = interruption.rawItem?.callId ?? interruption.callId;
462
+ const rejectionMessage = rejectionReason
463
+ ? `Tool execution was not approved. User's reason: ${rejectionReason}`
464
+ : 'Tool execution was not approved.';
465
+ if (typeof this.agentClient.addToolInterceptor === 'function') {
417
466
  const removeInterceptor = this.agentClient.addToolInterceptor(async (name, _params, toolCallId) => {
418
- if (name === toolName &&
419
- (!expectedCallId || toolCallId === expectedCallId)) {
467
+ if (name === toolName && (!expectedCallId || toolCallId === expectedCallId)) {
420
468
  markToolCallAsApprovalRejection(toolCallId ?? expectedCallId);
421
469
  return rejectionMessage;
422
470
  }
@@ -425,15 +473,21 @@ export class ConversationSession {
425
473
  // Approve to continue but interceptor will return rejection message
426
474
  state.approve(interruption);
427
475
  // Store interceptor cleanup for after stream
428
- this.pendingApprovalContext = {
429
- ...this.pendingApprovalContext,
430
- removeInterceptor,
431
- };
476
+ this.approvalState.setPendingRemoveInterceptor(removeInterceptor);
432
477
  }
433
478
  else {
479
+ // Fallback for clients without tool interceptors
434
480
  state.reject(interruption);
435
481
  }
482
+ this.logger.info('Tool approval rejected', {
483
+ eventType: 'approval.rejected',
484
+ category: 'approval',
485
+ phase: 'approval',
486
+ sessionId: this.id,
487
+ traceId: this.logger.getCorrelationId(),
488
+ });
436
489
  }
490
+ removeInterceptor = this.approvalState.getPending()?.removeInterceptor ?? null;
437
491
  // Restore cached tool-call arguments so continuation outputs can attach them
438
492
  this.toolCallArgumentsById.clear();
439
493
  if (toolCallArgumentsById?.size) {
@@ -449,6 +503,7 @@ export class ConversationSession {
449
503
  finalOutput: '',
450
504
  reasoningOutput: '',
451
505
  emittedCommandIds: new Set(),
506
+ latestUsage: undefined,
452
507
  };
453
508
  yield* this.#streamEvents(stream, acc, {
454
509
  preserveExistingToolArgs: true,
@@ -457,12 +512,17 @@ export class ConversationSession {
457
512
  this.conversationStore.updateFromResult(stream);
458
513
  // Merge previously emitted command IDs with newly emitted ones
459
514
  // This prevents duplicates when result.history contains commands from the initial stream
460
- const allEmittedIds = new Set([
461
- ...previouslyEmittedIds,
462
- ...acc.emittedCommandIds,
463
- ]);
464
- const result = this.#buildResult(stream, acc.finalOutput || undefined, acc.reasoningOutput || undefined, allEmittedIds);
515
+ const allEmittedIds = new Set([...previouslyEmittedIds, ...acc.emittedCommandIds]);
516
+ const result = this.#buildResult(stream, acc.finalOutput || undefined, acc.reasoningOutput || undefined, allEmittedIds, acc.latestUsage);
465
517
  if (result.type === 'approval_required') {
518
+ this.logger.info('Tool approval required', {
519
+ eventType: 'approval.required',
520
+ category: 'approval',
521
+ phase: 'approval',
522
+ sessionId: this.id,
523
+ traceId: this.logger.getCorrelationId(),
524
+ toolName: result.approval.toolName,
525
+ });
466
526
  const interruption = result.approval.rawInterruption;
467
527
  const callId = interruption?.rawItem?.callId ??
468
528
  interruption?.callId ??
@@ -484,12 +544,9 @@ export class ConversationSession {
484
544
  yield {
485
545
  type: 'final',
486
546
  finalText: result.finalText,
487
- ...(result.reasoningText
488
- ? { reasoningText: result.reasoningText }
489
- : {}),
490
- ...(result.commandMessages?.length
491
- ? { commandMessages: result.commandMessages }
492
- : {}),
547
+ ...(result.reasoningText ? { reasoningText: result.reasoningText } : {}),
548
+ ...(result.commandMessages?.length ? { commandMessages: result.commandMessages } : {}),
549
+ ...(result.usage ? { usage: result.usage } : {}),
493
550
  };
494
551
  }
495
552
  catch (error) {
@@ -501,16 +558,14 @@ export class ConversationSession {
501
558
  }
502
559
  finally {
503
560
  // Clean up interceptor if one was added for rejection reason
504
- if (rejectionReason &&
505
- this.pendingApprovalContext?.removeInterceptor) {
506
- this.pendingApprovalContext.removeInterceptor();
507
- }
561
+ removeInterceptor?.();
508
562
  }
509
563
  }
510
564
  async sendMessage(text, { onTextChunk, onReasoningChunk, onCommandMessage, onEvent, hallucinationRetryCount = 0, } = {}) {
511
565
  let finalText = '';
512
566
  let reasoningText = '';
513
567
  const commandMessages = [];
568
+ let usage;
514
569
  let sawTerminalEvent = null;
515
570
  for await (const event of this.run(text, { hallucinationRetryCount })) {
516
571
  onEvent?.(event);
@@ -532,7 +587,7 @@ export class ConversationSession {
532
587
  case 'approval_required': {
533
588
  sawTerminalEvent = event;
534
589
  // pendingApprovalContext is set inside #buildResult during run()
535
- const rawInterruption = this.pendingApprovalContext?.interruption;
590
+ const rawInterruption = this.approvalState.getPending()?.interruption;
536
591
  return {
537
592
  type: 'approval_required',
538
593
  approval: {
@@ -547,6 +602,12 @@ export class ConversationSession {
547
602
  sawTerminalEvent = event;
548
603
  finalText = event.finalText;
549
604
  reasoningText = event.reasoningText ?? '';
605
+ usage = event.usage;
606
+ this.logger.debug('sendMessage received final event', {
607
+ sessionId: this.id,
608
+ hasUsage: Boolean(event.usage),
609
+ usage: event.usage,
610
+ });
550
611
  if (event.commandMessages?.length) {
551
612
  for (const msg of event.commandMessages) {
552
613
  commandMessages.push(msg);
@@ -566,20 +627,28 @@ export class ConversationSession {
566
627
  if (!sawTerminalEvent) {
567
628
  finalText = finalText || 'Done.';
568
629
  }
569
- return {
630
+ const response = {
570
631
  type: 'response',
571
632
  commandMessages,
572
633
  finalText: finalText || 'Done.',
573
634
  ...(reasoningText ? { reasoningText } : {}),
635
+ ...(usage ? { usage } : {}),
574
636
  };
637
+ this.logger.debug('sendMessage returning response', {
638
+ sessionId: this.id,
639
+ hasUsage: Boolean(usage),
640
+ usage,
641
+ });
642
+ return response;
575
643
  }
576
644
  async handleApprovalDecision(answer, rejectionReason, { onTextChunk, onReasoningChunk, onCommandMessage, onEvent, } = {}) {
577
- if (!this.pendingApprovalContext) {
645
+ if (!this.approvalState.getPending()) {
578
646
  return null;
579
647
  }
580
648
  let finalText = '';
581
649
  let reasoningText = '';
582
650
  const commandMessages = [];
651
+ let usage;
583
652
  let sawTerminalEvent = null;
584
653
  for await (const event of this['continue']({ answer, rejectionReason })) {
585
654
  onEvent?.(event);
@@ -600,7 +669,7 @@ export class ConversationSession {
600
669
  }
601
670
  case 'approval_required': {
602
671
  sawTerminalEvent = event;
603
- const rawInterruption = this.pendingApprovalContext?.interruption;
672
+ const rawInterruption = this.approvalState.getPending()?.interruption;
604
673
  return {
605
674
  type: 'approval_required',
606
675
  approval: {
@@ -615,6 +684,12 @@ export class ConversationSession {
615
684
  sawTerminalEvent = event;
616
685
  finalText = event.finalText;
617
686
  reasoningText = event.reasoningText ?? '';
687
+ usage = event.usage;
688
+ this.logger.debug('handleApprovalDecision received final event', {
689
+ sessionId: this.id,
690
+ hasUsage: Boolean(event.usage),
691
+ usage: event.usage,
692
+ });
618
693
  if (event.commandMessages?.length) {
619
694
  for (const msg of event.commandMessages) {
620
695
  commandMessages.push(msg);
@@ -630,19 +705,33 @@ export class ConversationSession {
630
705
  }
631
706
  }
632
707
  if (!sawTerminalEvent) {
633
- return {
708
+ const response = {
634
709
  type: 'response',
635
710
  commandMessages,
636
711
  finalText: finalText || 'Done.',
637
712
  ...(reasoningText ? { reasoningText } : {}),
713
+ ...(usage ? { usage } : {}),
638
714
  };
715
+ this.logger.debug('handleApprovalDecision returning response', {
716
+ sessionId: this.id,
717
+ hasUsage: Boolean(usage),
718
+ usage,
719
+ });
720
+ return response;
639
721
  }
640
- return {
722
+ const response = {
641
723
  type: 'response',
642
724
  commandMessages,
643
725
  finalText: finalText || 'Done.',
644
726
  ...(reasoningText ? { reasoningText } : {}),
727
+ ...(usage ? { usage } : {}),
645
728
  };
729
+ this.logger.debug('handleApprovalDecision returning response', {
730
+ sessionId: this.id,
731
+ hasUsage: Boolean(usage),
732
+ usage,
733
+ });
734
+ return response;
646
735
  }
647
736
  async *#streamEvents(stream, acc, { preserveExistingToolArgs }) {
648
737
  const toolCallArgumentsById = this.toolCallArgumentsById;
@@ -676,21 +765,26 @@ export class ConversationSession {
676
765
  };
677
766
  };
678
767
  for await (const event of stream) {
768
+ // Extract usage if present in any of the common locations
769
+ const usage = extractUsage(event);
770
+ if (usage) {
771
+ acc.latestUsage = usage;
772
+ this.logger.debug('Usage extracted from stream event', {
773
+ sessionId: this.id,
774
+ source: 'stream_event',
775
+ eventType: event?.type ?? event?.data?.type ?? 'unknown',
776
+ usage,
777
+ });
778
+ }
679
779
  // Log event type with deduplication for ordering understanding
680
- // const eventType = event?.type || 'unknown';
681
- // this.logStreamEvent(eventType, {
682
- // eventName: event?.name,
683
- // hasData: !!event?.data,
684
- // item: event?.item,
685
- // });
686
- const delta1 = this.#extractTextDelta(event);
780
+ const delta1 = extractTextDelta(event);
687
781
  if (delta1) {
688
782
  const e = emitText(delta1);
689
783
  if (e)
690
784
  yield e;
691
785
  }
692
786
  if (event?.data) {
693
- const delta2 = this.#extractTextDelta(event.data);
787
+ const delta2 = extractTextDelta(event.data);
694
788
  if (delta2) {
695
789
  const e = emitText(delta2);
696
790
  if (e)
@@ -698,71 +792,25 @@ export class ConversationSession {
698
792
  }
699
793
  }
700
794
  // Handle reasoning items
701
- const reasoningDelta = (() => {
702
- // OpenAI style
703
- const data = event?.data;
704
- if (data &&
705
- typeof data === 'object' &&
706
- data.type === 'model') {
707
- const eventDetail = data.event;
708
- if (eventDetail &&
709
- typeof eventDetail === 'object' &&
710
- eventDetail.type ===
711
- 'response.reasoning_summary_text.delta') {
712
- return eventDetail.delta ?? '';
713
- }
714
- }
715
- // OpenRouter style
716
- const choices = event?.data?.event?.choices;
717
- if (!choices)
718
- return '';
719
- if (Array.isArray(choices)) {
720
- return choices[0]?.delta?.reasoning ?? choices[0]?.delta?.reasoning_content ?? '';
721
- }
722
- if (typeof choices === 'object') {
723
- const byZero = choices['0'];
724
- const first = byZero ?? choices[Object.keys(choices)[0]];
725
- return first?.delta?.reasoning ?? first?.delta?.reasoning_content ?? '';
726
- }
727
- return '';
728
- })();
795
+ const reasoningDelta = extractReasoningDelta(event);
729
796
  if (reasoningDelta) {
730
797
  const e = emitReasoning(reasoningDelta);
731
798
  if (e)
732
799
  yield e;
733
800
  }
734
- const maybeEmitCommandMessagesFromItems = (items) => {
735
- this.#attachCachedArguments(items, toolCallArgumentsById);
736
- const commandMessages = extractCommandMessages(items);
737
- const out = [];
738
- for (const cmdMsg of commandMessages) {
739
- if (acc.emittedCommandIds.has(cmdMsg.id)) {
740
- continue;
741
- }
742
- if (cmdMsg.isApprovalRejection) {
743
- continue;
744
- }
745
- acc.emittedCommandIds.add(cmdMsg.id);
746
- out.push({ type: 'command_message', message: cmdMsg });
747
- }
748
- return out;
749
- };
801
+ const maybeEmitCommandMessagesFromItems = (items) => emitCommandMessagesFromItems(items, {
802
+ toolCallArgumentsById,
803
+ emittedCommandIds: acc.emittedCommandIds,
804
+ });
750
805
  if (event?.type === 'run_item_stream_event') {
751
- this.#captureToolCallArguments(event.item, toolCallArgumentsById);
806
+ captureToolCallArguments(event.item, toolCallArgumentsById);
752
807
  // Emit tool_started event when a function_call is detected
753
808
  const rawItem = event.item?.rawItem ?? event.item;
754
809
  if (rawItem?.type === 'function_call') {
755
- const callId = rawItem.callId ??
756
- rawItem.call_id ??
757
- rawItem.tool_call_id ??
758
- rawItem.toolCallId ??
759
- rawItem.id;
810
+ const callId = rawItem.callId ?? rawItem.call_id ?? rawItem.tool_call_id ?? rawItem.toolCallId ?? rawItem.id;
760
811
  if (callId) {
761
812
  const toolName = rawItem.name ?? event.item?.name;
762
- const args = rawItem.arguments ??
763
- rawItem.args ??
764
- event.item?.arguments ??
765
- event.item?.args;
813
+ const args = rawItem.arguments ?? rawItem.args ?? event.item?.arguments ?? event.item?.args;
766
814
  // Providers sometimes surface arguments as a JSON string.
767
815
  // Normalize here so downstream UI (pending/running display)
768
816
  // can reliably render parameters.
@@ -778,6 +826,30 @@ export class ConversationSession {
778
826
  return JSON.parse(trimmed);
779
827
  }
780
828
  catch {
829
+ if ((trimmed.startsWith('{') || trimmed.startsWith('[')) &&
830
+ !this.emittedInvalidToolCallPackets.has(String(callId))) {
831
+ this.emittedInvalidToolCallPackets.add(String(callId));
832
+ const diagnostic = createInvalidToolCallDiagnostic({
833
+ toolName: toolName ?? 'unknown',
834
+ toolCallId: String(callId),
835
+ rawPayload: trimmed,
836
+ normalizedToolCall: {
837
+ toolName: toolName ?? 'unknown',
838
+ toolCallId: String(callId),
839
+ arguments: args,
840
+ },
841
+ validationErrors: ['arguments must be valid JSON'],
842
+ traceId: this.logger.getCorrelationId() ?? 'trace-unknown',
843
+ retryContext: {
844
+ sessionId: this.id,
845
+ },
846
+ });
847
+ this.logger.error('Invalid tool call argument payload', {
848
+ ...diagnostic,
849
+ sessionId: this.id,
850
+ messageId: String(callId),
851
+ });
852
+ }
781
853
  return args;
782
854
  }
783
855
  })();
@@ -787,147 +859,86 @@ export class ConversationSession {
787
859
  toolName: toolName ?? 'unknown',
788
860
  arguments: normalizedArgs,
789
861
  };
862
+ this.logger.info('Tool execution started', {
863
+ eventType: 'tool_call.execution_started',
864
+ category: 'tool',
865
+ phase: 'execution',
866
+ sessionId: this.id,
867
+ traceId: this.logger.getCorrelationId(),
868
+ toolName: toolName ?? 'unknown',
869
+ toolCallId: String(callId),
870
+ messageId: String(callId),
871
+ });
790
872
  }
791
873
  }
792
- for (const e of maybeEmitCommandMessagesFromItems([
793
- event.item,
794
- ])) {
874
+ for (const e of maybeEmitCommandMessagesFromItems([event.item])) {
795
875
  yield e;
796
876
  }
797
877
  }
798
- else if (event?.type === 'tool_call_output_item' ||
799
- event?.rawItem?.type === 'function_call_output') {
800
- this.#captureToolCallArguments(event, toolCallArgumentsById);
878
+ else if (event?.type === 'tool_call_output_item' || event?.rawItem?.type === 'function_call_output') {
879
+ captureToolCallArguments(event, toolCallArgumentsById);
801
880
  for (const e of maybeEmitCommandMessagesFromItems([event])) {
802
881
  yield e;
803
882
  }
804
883
  }
805
884
  }
806
- await stream.completed;
807
- this.flushStreamEventLog();
808
- }
809
- #captureToolCallArguments(item, toolCallArgumentsById) {
810
- const rawItem = item?.rawItem ?? item;
811
- if (!rawItem) {
812
- return;
813
- }
814
- if (rawItem?.type !== 'function_call') {
815
- return;
816
- }
817
- const callId = rawItem.callId ??
818
- rawItem.call_id ??
819
- rawItem.tool_call_id ??
820
- rawItem.toolCallId ??
821
- rawItem.id;
822
- if (!callId) {
823
- return;
824
- }
825
- const args = rawItem.arguments ?? rawItem.args ?? item?.arguments ?? item?.args;
826
- if (!args) {
827
- return;
828
- }
829
- toolCallArgumentsById.set(callId, args);
830
- }
831
- #attachCachedArguments(items = [], toolCallArgumentsById) {
832
- if (!items?.length) {
833
- return;
834
- }
835
- for (const item of items) {
836
- if (!item) {
837
- continue;
838
- }
839
- if (item.arguments ||
840
- item.args ||
841
- item?.rawItem?.arguments ||
842
- item?.rawItem?.args) {
843
- continue;
844
- }
845
- const rawItem = item?.rawItem ?? item;
846
- const callId = rawItem?.callId ??
847
- rawItem?.call_id ??
848
- rawItem?.tool_call_id ??
849
- rawItem?.toolCallId ??
850
- rawItem?.id ??
851
- item?.callId ??
852
- item?.call_id ??
853
- item?.tool_call_id ??
854
- item?.toolCallId ??
855
- item?.id;
856
- if (!callId) {
857
- continue;
858
- }
859
- const args = toolCallArgumentsById.get(callId);
860
- if (!args) {
861
- continue;
885
+ const completedResult = await stream.completed;
886
+ const rawResponses = Array.isArray(stream?.rawResponses) ? stream.rawResponses : [];
887
+ let usageFromRawResponses;
888
+ for (let i = rawResponses.length - 1; i >= 0; i--) {
889
+ const candidate = extractUsage(rawResponses[i]);
890
+ if (candidate) {
891
+ usageFromRawResponses = candidate;
892
+ break;
862
893
  }
863
- item.arguments = args;
864
- }
865
- }
866
- #extractTextDelta(payload) {
867
- if (payload === null || payload === undefined) {
868
- return null;
869
- }
870
- if (typeof payload === 'string') {
871
- return payload || null;
872
894
  }
873
- if (typeof payload !== 'object') {
874
- return null;
875
- }
876
- const type = typeof payload.type === 'string' ? payload.type : '';
877
- const looksLikeOutput = typeof type === 'string' && type.includes('output_text');
878
- const hasOutputProperties = Boolean(payload.delta ??
879
- payload.output_text ??
880
- payload.text ??
881
- payload.content);
882
- if (!looksLikeOutput && !hasOutputProperties) {
883
- return null;
884
- }
885
- const deltaCandidate = payload.delta ??
886
- payload.output_text ??
887
- payload.text ??
888
- payload.content;
889
- const text = this.#coerceToText(deltaCandidate);
890
- return text || null;
891
- }
892
- #coerceToText(value) {
893
- if (value === null || value === undefined) {
894
- return '';
895
- }
896
- if (typeof value === 'string') {
897
- return value;
898
- }
899
- if (typeof value === 'number' || typeof value === 'boolean') {
900
- return String(value);
901
- }
902
- if (Array.isArray(value)) {
903
- return value
904
- .map(entry => this.#coerceToText(entry))
905
- .filter(Boolean)
906
- .join('');
895
+ const finalUsage = extractUsage(completedResult) || extractUsage(stream) || usageFromRawResponses;
896
+ if (finalUsage) {
897
+ acc.latestUsage = finalUsage;
898
+ const usageSource = extractUsage(completedResult)
899
+ ? 'completed_result'
900
+ : extractUsage(stream)
901
+ ? 'stream_object'
902
+ : 'stream_raw_responses';
903
+ this.logger.debug('Usage extracted from stream completion', {
904
+ sessionId: this.id,
905
+ source: 'stream_completed',
906
+ usageSource,
907
+ usage: finalUsage,
908
+ });
907
909
  }
908
- if (typeof value === 'object') {
909
- const record = value;
910
- const candidates = ['text', 'value', 'content', 'delta'];
911
- for (const field of candidates) {
912
- if (field in record) {
913
- const text = this.#coerceToText(record[field]);
914
- if (text) {
915
- return text;
916
- }
917
- }
918
- }
910
+ else {
911
+ const completedResultRecord = completedResult && typeof completedResult === 'object' && !Array.isArray(completedResult)
912
+ ? completedResult
913
+ : undefined;
914
+ const streamRecord = stream && typeof stream === 'object' && !Array.isArray(stream)
915
+ ? stream
916
+ : undefined;
917
+ this.logger.debug('No usage found in stream completion', {
918
+ sessionId: this.id,
919
+ source: 'stream_completed',
920
+ completedResultType: completedResult === null ? 'null' : Array.isArray(completedResult) ? 'array' : typeof completedResult,
921
+ completedResultKeys: completedResultRecord ? Object.keys(completedResultRecord) : [],
922
+ streamKeys: streamRecord ? Object.keys(streamRecord) : [],
923
+ completedResultHasUsagePath: {
924
+ usage: Boolean(completedResultRecord?.usage),
925
+ usageMetadata: Boolean(completedResultRecord?.usageMetadata),
926
+ usage_metadata: Boolean(completedResultRecord?.usage_metadata),
927
+ responseUsage: Boolean(completedResultRecord?.response?.usage),
928
+ },
929
+ });
919
930
  }
920
- return '';
931
+ this.flushStreamEventLog();
921
932
  }
922
- #buildResult(result, finalOutputOverride, reasoningOutputOverride, emittedCommandIds) {
933
+ #buildResult(result, finalOutputOverride, reasoningOutputOverride, emittedCommandIds, usage) {
923
934
  if (result.interruptions && result.interruptions.length > 0) {
924
935
  const interruption = result.interruptions[0];
925
- this.pendingApprovalContext = {
936
+ this.approvalState.setPending({
926
937
  state: result.state,
927
938
  interruption,
928
939
  emittedCommandIds: emittedCommandIds ?? new Set(),
929
940
  toolCallArgumentsById: new Map(this.toolCallArgumentsById),
930
- };
941
+ });
931
942
  let argumentsText = '';
932
943
  const toolName = interruption.name;
933
944
  // For shell_call (built-in shell tool), extract commands from action
@@ -959,18 +970,19 @@ export class ConversationSession {
959
970
  },
960
971
  };
961
972
  }
962
- this.pendingApprovalContext = null;
973
+ this.approvalState.clearPending();
963
974
  const allCommandMessages = extractCommandMessages(result.newItems || result.history || []);
964
975
  // Filter out commands that were already emitted in real-time
965
976
  const commandMessages = emittedCommandIds
966
- ? allCommandMessages.filter(msg => !emittedCommandIds.has(msg.id))
977
+ ? allCommandMessages.filter((msg) => !emittedCommandIds.has(msg.id))
967
978
  : allCommandMessages;
968
- const visibleCommandMessages = commandMessages.filter(msg => !msg.isApprovalRejection);
979
+ const visibleCommandMessages = commandMessages.filter((msg) => !msg.isApprovalRejection);
969
980
  const response = {
970
981
  type: 'response',
971
982
  commandMessages: visibleCommandMessages,
972
983
  finalText: finalOutputOverride ?? result.finalOutput ?? 'Done.',
973
984
  reasoningText: reasoningOutputOverride,
985
+ usage: usage ?? extractUsage(result),
974
986
  };
975
987
  return response;
976
988
  }