@qduc/term2 0.6.1 → 0.7.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 (798) hide show
  1. package/dist/agent.d.ts +9 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +138 -69
  4. package/dist/agent.js.map +1 -1
  5. package/dist/agent.test.js +165 -15
  6. package/dist/agent.test.js.map +1 -1
  7. package/dist/app.d.ts +14 -3
  8. package/dist/app.d.ts.map +1 -1
  9. package/dist/app.js +168 -28
  10. package/dist/app.js.map +1 -1
  11. package/dist/app.large-uncached-warning.test.d.ts +2 -0
  12. package/dist/app.large-uncached-warning.test.d.ts.map +1 -0
  13. package/dist/app.large-uncached-warning.test.js +15 -0
  14. package/dist/app.large-uncached-warning.test.js.map +1 -0
  15. package/dist/app.startup-banner.test.js +3 -19
  16. package/dist/app.startup-banner.test.js.map +1 -1
  17. package/dist/cli.js +139 -69
  18. package/dist/cli.js.map +1 -1
  19. package/dist/components/Banner.d.ts.map +1 -1
  20. package/dist/components/Banner.js +35 -17
  21. package/dist/components/Banner.js.map +1 -1
  22. package/dist/components/BottomArea.d.ts +7 -1
  23. package/dist/components/BottomArea.d.ts.map +1 -1
  24. package/dist/components/BottomArea.js +8 -6
  25. package/dist/components/BottomArea.js.map +1 -1
  26. package/dist/components/BottomArea.test.js +14 -1
  27. package/dist/components/BottomArea.test.js.map +1 -1
  28. package/dist/components/CommandMessage.d.ts.map +1 -1
  29. package/dist/components/CommandMessage.js +31 -1
  30. package/dist/components/CommandMessage.js.map +1 -1
  31. package/dist/components/CommandMessage.test.js +150 -31
  32. package/dist/components/CommandMessage.test.js.map +1 -1
  33. package/dist/components/Common/MenuContainer.d.ts +20 -0
  34. package/dist/components/Common/MenuContainer.d.ts.map +1 -0
  35. package/dist/components/Common/MenuContainer.js +52 -0
  36. package/dist/components/Common/MenuContainer.js.map +1 -0
  37. package/dist/components/Common/ScrollableTabBar.d.ts +26 -0
  38. package/dist/components/Common/ScrollableTabBar.d.ts.map +1 -0
  39. package/dist/components/Common/ScrollableTabBar.js +26 -0
  40. package/dist/components/Common/ScrollableTabBar.js.map +1 -0
  41. package/dist/components/Common/compute-visible-tabs.d.ts +16 -0
  42. package/dist/components/Common/compute-visible-tabs.d.ts.map +1 -0
  43. package/dist/components/Common/compute-visible-tabs.js +53 -0
  44. package/dist/components/Common/compute-visible-tabs.js.map +1 -0
  45. package/dist/components/Common/compute-visible-tabs.test.d.ts +2 -0
  46. package/dist/components/Common/compute-visible-tabs.test.d.ts.map +1 -0
  47. package/dist/components/Common/compute-visible-tabs.test.js +97 -0
  48. package/dist/components/Common/compute-visible-tabs.test.js.map +1 -0
  49. package/dist/components/HandoffConfirmationPrompt.d.ts +9 -0
  50. package/dist/components/HandoffConfirmationPrompt.d.ts.map +1 -0
  51. package/dist/components/HandoffConfirmationPrompt.js +38 -0
  52. package/dist/components/HandoffConfirmationPrompt.js.map +1 -0
  53. package/dist/components/HandoffConfirmationPrompt.test.d.ts +2 -0
  54. package/dist/components/HandoffConfirmationPrompt.test.d.ts.map +1 -0
  55. package/dist/components/HandoffConfirmationPrompt.test.js +12 -0
  56. package/dist/components/HandoffConfirmationPrompt.test.js.map +1 -0
  57. package/dist/components/Input/PopupManager.d.ts +5 -1
  58. package/dist/components/Input/PopupManager.d.ts.map +1 -1
  59. package/dist/components/Input/PopupManager.js +2 -2
  60. package/dist/components/Input/PopupManager.js.map +1 -1
  61. package/dist/components/Input/popup-key-navigation.js +2 -2
  62. package/dist/components/Input/popup-key-navigation.js.map +1 -1
  63. package/dist/components/Input/popup-key-navigation.test.js +5 -3
  64. package/dist/components/Input/popup-key-navigation.test.js.map +1 -1
  65. package/dist/components/Input/popup-props.d.ts.map +1 -1
  66. package/dist/components/Input/popup-props.js +4 -0
  67. package/dist/components/Input/popup-props.js.map +1 -1
  68. package/dist/components/Input/popup-props.test.d.ts +2 -0
  69. package/dist/components/Input/popup-props.test.d.ts.map +1 -0
  70. package/dist/components/Input/popup-props.test.js +70 -0
  71. package/dist/components/Input/popup-props.test.js.map +1 -0
  72. package/dist/components/InputBox.d.ts +0 -1
  73. package/dist/components/InputBox.d.ts.map +1 -1
  74. package/dist/components/InputBox.js +66 -14
  75. package/dist/components/InputBox.js.map +1 -1
  76. package/dist/components/InputBox.test.js +192 -12
  77. package/dist/components/InputBox.test.js.map +1 -1
  78. package/dist/components/MessageList.d.ts +1 -0
  79. package/dist/components/MessageList.d.ts.map +1 -1
  80. package/dist/components/MessageList.js.map +1 -1
  81. package/dist/components/ModelSelectionMenu.d.ts.map +1 -1
  82. package/dist/components/ModelSelectionMenu.debug.test.d.ts +2 -0
  83. package/dist/components/ModelSelectionMenu.debug.test.d.ts.map +1 -0
  84. package/dist/components/ModelSelectionMenu.debug.test.js.map +1 -0
  85. package/dist/components/ModelSelectionMenu.js +29 -125
  86. package/dist/components/ModelSelectionMenu.js.map +1 -1
  87. package/dist/components/ModelSelectionMenu.test.js +9 -5
  88. package/dist/components/ModelSelectionMenu.test.js.map +1 -1
  89. package/dist/components/PathSelectionMenu.d.ts.map +1 -1
  90. package/dist/components/PathSelectionMenu.js +15 -55
  91. package/dist/components/PathSelectionMenu.js.map +1 -1
  92. package/dist/components/SettingsSelectionMenu.d.ts +4 -1
  93. package/dist/components/SettingsSelectionMenu.d.ts.map +1 -1
  94. package/dist/components/SettingsSelectionMenu.js +48 -104
  95. package/dist/components/SettingsSelectionMenu.js.map +1 -1
  96. package/dist/components/SettingsSelectionMenu.scroll.test.js +140 -0
  97. package/dist/components/SettingsSelectionMenu.scroll.test.js.map +1 -1
  98. package/dist/components/SettingsSelectionMenu.test.js +38 -5
  99. package/dist/components/SettingsSelectionMenu.test.js.map +1 -1
  100. package/dist/components/SettingsValueSelectionMenu.d.ts.map +1 -1
  101. package/dist/components/SettingsValueSelectionMenu.js +24 -51
  102. package/dist/components/SettingsValueSelectionMenu.js.map +1 -1
  103. package/dist/components/SlashCommandMenu.d.ts +1 -0
  104. package/dist/components/SlashCommandMenu.d.ts.map +1 -1
  105. package/dist/components/SlashCommandMenu.js +11 -14
  106. package/dist/components/SlashCommandMenu.js.map +1 -1
  107. package/dist/components/StatusBar.d.ts +2 -0
  108. package/dist/components/StatusBar.d.ts.map +1 -1
  109. package/dist/components/StatusBar.js +35 -2
  110. package/dist/components/StatusBar.js.map +1 -1
  111. package/dist/components/StatusBar.test.js +32 -0
  112. package/dist/components/StatusBar.test.js.map +1 -1
  113. package/dist/components/UndoSelectionMenu.d.ts.map +1 -1
  114. package/dist/components/UndoSelectionMenu.js +6 -33
  115. package/dist/components/UndoSelectionMenu.js.map +1 -1
  116. package/dist/context/InputContext.stability.test.js +9 -7
  117. package/dist/context/InputContext.stability.test.js.map +1 -1
  118. package/dist/env-setup.js +1 -0
  119. package/dist/env-setup.js.map +1 -1
  120. package/dist/hooks/use-app-commands.d.ts +22 -2
  121. package/dist/hooks/use-app-commands.d.ts.map +1 -1
  122. package/dist/hooks/use-app-commands.js +43 -3
  123. package/dist/hooks/use-app-commands.js.map +1 -1
  124. package/dist/hooks/use-app-commands.test.js +135 -1
  125. package/dist/hooks/use-app-commands.test.js.map +1 -1
  126. package/dist/hooks/use-conversation.clear.test.js +12 -7
  127. package/dist/hooks/use-conversation.clear.test.js.map +1 -1
  128. package/dist/hooks/use-conversation.d.ts +21 -1
  129. package/dist/hooks/use-conversation.d.ts.map +1 -1
  130. package/dist/hooks/use-conversation.js +81 -9
  131. package/dist/hooks/use-conversation.js.map +1 -1
  132. package/dist/hooks/use-escape-key.d.ts +8 -1
  133. package/dist/hooks/use-escape-key.d.ts.map +1 -1
  134. package/dist/hooks/use-escape-key.js +15 -4
  135. package/dist/hooks/use-escape-key.js.map +1 -1
  136. package/dist/hooks/use-escape-key.test.js +135 -28
  137. package/dist/hooks/use-escape-key.test.js.map +1 -1
  138. package/dist/hooks/use-input-history.test.js +14 -8
  139. package/dist/hooks/use-input-history.test.js.map +1 -1
  140. package/dist/hooks/use-mode-handlers.d.ts +12 -1
  141. package/dist/hooks/use-mode-handlers.d.ts.map +1 -1
  142. package/dist/hooks/use-mode-handlers.js +31 -1
  143. package/dist/hooks/use-mode-handlers.js.map +1 -1
  144. package/dist/hooks/use-model-selection.d.ts +6 -1
  145. package/dist/hooks/use-model-selection.d.ts.map +1 -1
  146. package/dist/hooks/use-model-selection.js +50 -8
  147. package/dist/hooks/use-model-selection.js.map +1 -1
  148. package/dist/hooks/use-model-selection.test.js +306 -50
  149. package/dist/hooks/use-model-selection.test.js.map +1 -1
  150. package/dist/hooks/use-path-completion.d.ts +4 -0
  151. package/dist/hooks/use-path-completion.d.ts.map +1 -1
  152. package/dist/hooks/use-path-completion.js +23 -17
  153. package/dist/hooks/use-path-completion.js.map +1 -1
  154. package/dist/hooks/use-path-completion.test.d.ts +2 -0
  155. package/dist/hooks/use-path-completion.test.d.ts.map +1 -0
  156. package/dist/hooks/use-path-completion.test.js +54 -0
  157. package/dist/hooks/use-path-completion.test.js.map +1 -0
  158. package/dist/hooks/use-runtime-settings.d.ts.map +1 -1
  159. package/dist/hooks/use-runtime-settings.js +7 -2
  160. package/dist/hooks/use-runtime-settings.js.map +1 -1
  161. package/dist/hooks/use-runtime-settings.test.d.ts +2 -0
  162. package/dist/hooks/use-runtime-settings.test.d.ts.map +1 -0
  163. package/dist/hooks/use-runtime-settings.test.js +45 -0
  164. package/dist/hooks/use-runtime-settings.test.js.map +1 -0
  165. package/dist/hooks/use-selection.d.ts +6 -2
  166. package/dist/hooks/use-selection.d.ts.map +1 -1
  167. package/dist/hooks/use-selection.js +53 -16
  168. package/dist/hooks/use-selection.js.map +1 -1
  169. package/dist/hooks/use-selection.test.d.ts +2 -0
  170. package/dist/hooks/use-selection.test.d.ts.map +1 -0
  171. package/dist/hooks/use-selection.test.js +132 -0
  172. package/dist/hooks/use-selection.test.js.map +1 -0
  173. package/dist/hooks/use-settings-completion.d.ts +19 -0
  174. package/dist/hooks/use-settings-completion.d.ts.map +1 -1
  175. package/dist/hooks/use-settings-completion.js +106 -18
  176. package/dist/hooks/use-settings-completion.js.map +1 -1
  177. package/dist/hooks/use-settings-completion.scroll.test.js +68 -29
  178. package/dist/hooks/use-settings-completion.scroll.test.js.map +1 -1
  179. package/dist/hooks/use-settings-completion.test.js +36 -1
  180. package/dist/hooks/use-settings-completion.test.js.map +1 -1
  181. package/dist/hooks/use-settings-value-completion.d.ts +7 -1
  182. package/dist/hooks/use-settings-value-completion.d.ts.map +1 -1
  183. package/dist/hooks/use-settings-value-completion.js +18 -7
  184. package/dist/hooks/use-settings-value-completion.js.map +1 -1
  185. package/dist/hooks/use-slash-commands.d.ts +5 -0
  186. package/dist/hooks/use-slash-commands.d.ts.map +1 -1
  187. package/dist/hooks/use-slash-commands.js +22 -2
  188. package/dist/hooks/use-slash-commands.js.map +1 -1
  189. package/dist/hooks/use-trigger-detection.test.js +9 -6
  190. package/dist/hooks/use-trigger-detection.test.js.map +1 -1
  191. package/dist/hooks/use-undo-selection.d.ts +4 -0
  192. package/dist/hooks/use-undo-selection.d.ts.map +1 -1
  193. package/dist/hooks/use-undo-selection.js +18 -1
  194. package/dist/hooks/use-undo-selection.js.map +1 -1
  195. package/dist/inspect_zod.d.ts +2 -0
  196. package/dist/inspect_zod.d.ts.map +1 -0
  197. package/dist/inspect_zod.js +16 -0
  198. package/dist/inspect_zod.js.map +1 -0
  199. package/dist/lib/openai-agent-client.d.ts +10 -3
  200. package/dist/lib/openai-agent-client.d.ts.map +1 -1
  201. package/dist/lib/openai-agent-client.js +191 -37
  202. package/dist/lib/openai-agent-client.js.map +1 -1
  203. package/dist/lib/openai-agent-client.public-methods.test.js +193 -0
  204. package/dist/lib/openai-agent-client.public-methods.test.js.map +1 -1
  205. package/dist/lib/openai-agent-client.test.js +71 -1
  206. package/dist/lib/openai-agent-client.test.js.map +1 -1
  207. package/dist/lib/tool-invoke.d.ts +3 -2
  208. package/dist/lib/tool-invoke.d.ts.map +1 -1
  209. package/dist/lib/tool-invoke.js +213 -10
  210. package/dist/lib/tool-invoke.js.map +1 -1
  211. package/dist/lib/tool-invoke.test.js +103 -0
  212. package/dist/lib/tool-invoke.test.js.map +1 -1
  213. package/dist/prompts/codex.md +105 -97
  214. package/dist/prompts/fragments/gpt-5.3-codex.md +7 -0
  215. package/dist/prompts/fragments/gpt-5.4-small.md +6 -0
  216. package/dist/prompts/fragments/gpt-5.4.md +7 -0
  217. package/dist/prompts/fragments/gpt-5.5.md +7 -0
  218. package/dist/prompts/gpt-5-modern.md +96 -54
  219. package/dist/prompts/gpt-5.md +7 -4
  220. package/dist/prompts/plan-mode-info.md +14 -3
  221. package/dist/prompts/prompt-constructor.d.ts +19 -0
  222. package/dist/prompts/prompt-constructor.d.ts.map +1 -0
  223. package/dist/prompts/prompt-constructor.js +53 -0
  224. package/dist/prompts/prompt-constructor.js.map +1 -0
  225. package/dist/prompts/prompt-constructor.test.d.ts +2 -0
  226. package/dist/prompts/prompt-constructor.test.d.ts.map +1 -0
  227. package/dist/prompts/prompt-constructor.test.js +99 -0
  228. package/dist/prompts/prompt-constructor.test.js.map +1 -0
  229. package/dist/prompts/prompt-constructor.test.ts +112 -0
  230. package/dist/prompts/prompt-constructor.ts +91 -0
  231. package/dist/prompts/prompt-profiles.d.ts +17 -0
  232. package/dist/prompts/prompt-profiles.d.ts.map +1 -0
  233. package/dist/prompts/prompt-profiles.js +61 -0
  234. package/dist/prompts/prompt-profiles.js.map +1 -0
  235. package/dist/prompts/prompt-profiles.ts +77 -0
  236. package/dist/prompts/prompt-selector.d.ts.map +1 -1
  237. package/dist/prompts/prompt-selector.js +3 -21
  238. package/dist/prompts/prompt-selector.js.map +1 -1
  239. package/dist/prompts/prompt-selector.ts +3 -25
  240. package/dist/prompts/reasoning-efficiency.d.ts.map +1 -1
  241. package/dist/prompts/reasoning-efficiency.js +2 -0
  242. package/dist/prompts/reasoning-efficiency.js.map +1 -1
  243. package/dist/prompts/reasoning-efficiency.test.d.ts +2 -0
  244. package/dist/prompts/reasoning-efficiency.test.d.ts.map +1 -0
  245. package/dist/prompts/reasoning-efficiency.test.js +11 -0
  246. package/dist/prompts/reasoning-efficiency.test.js.map +1 -0
  247. package/dist/prompts/reasoning-efficiency.test.ts +12 -0
  248. package/dist/prompts/reasoning-efficiency.ts +2 -0
  249. package/dist/prompts/search-via-shell.test.js +19 -16
  250. package/dist/prompts/search-via-shell.test.js.map +1 -1
  251. package/dist/prompts/search-via-shell.test.ts +20 -16
  252. package/dist/prompts/subagent-delegation.test.js +12 -10
  253. package/dist/prompts/subagent-delegation.test.js.map +1 -1
  254. package/dist/prompts/subagent-delegation.test.ts +13 -10
  255. package/dist/prompts/worktree-hygiene.md +9 -0
  256. package/dist/providers/ai-sdk-logging-fetch.d.ts.map +1 -1
  257. package/dist/providers/ai-sdk-logging-fetch.js +78 -3
  258. package/dist/providers/ai-sdk-logging-fetch.js.map +1 -1
  259. package/dist/providers/ai-sdk-logging-fetch.test.js +143 -0
  260. package/dist/providers/ai-sdk-logging-fetch.test.js.map +1 -1
  261. package/dist/providers/ai-sdk-openai-compatible.provider.d.ts +0 -2
  262. package/dist/providers/ai-sdk-openai-compatible.provider.d.ts.map +1 -1
  263. package/dist/providers/ai-sdk-openai-compatible.provider.js +4 -8
  264. package/dist/providers/ai-sdk-openai-compatible.provider.js.map +1 -1
  265. package/dist/providers/ai-sdk-openai-compatible.provider.test.js +1 -119
  266. package/dist/providers/ai-sdk-openai-compatible.provider.test.js.map +1 -1
  267. package/dist/providers/codex-responses-model.d.ts +20 -0
  268. package/dist/providers/codex-responses-model.d.ts.map +1 -0
  269. package/dist/providers/codex-responses-model.js +170 -0
  270. package/dist/providers/codex-responses-model.js.map +1 -0
  271. package/dist/providers/codex-responses-model.test.d.ts +2 -0
  272. package/dist/providers/codex-responses-model.test.d.ts.map +1 -0
  273. package/dist/providers/codex-responses-model.test.js +240 -0
  274. package/dist/providers/codex-responses-model.test.js.map +1 -0
  275. package/dist/providers/codex.provider.d.ts +29 -0
  276. package/dist/providers/codex.provider.d.ts.map +1 -0
  277. package/dist/providers/codex.provider.js +486 -0
  278. package/dist/providers/codex.provider.js.map +1 -0
  279. package/dist/providers/codex.provider.test.d.ts +2 -0
  280. package/dist/providers/codex.provider.test.d.ts.map +1 -0
  281. package/dist/providers/codex.provider.test.js +634 -0
  282. package/dist/providers/codex.provider.test.js.map +1 -0
  283. package/dist/providers/custom-provider-adapter.test.js +56 -0
  284. package/dist/providers/custom-provider-adapter.test.js.map +1 -1
  285. package/dist/providers/fallback-responses-model.d.ts +20 -0
  286. package/dist/providers/fallback-responses-model.d.ts.map +1 -0
  287. package/dist/providers/fallback-responses-model.js +277 -0
  288. package/dist/providers/fallback-responses-model.js.map +1 -0
  289. package/dist/providers/fallback-responses-model.test.d.ts +2 -0
  290. package/dist/providers/fallback-responses-model.test.d.ts.map +1 -0
  291. package/dist/providers/fallback-responses-model.test.js +279 -0
  292. package/dist/providers/fallback-responses-model.test.js.map +1 -0
  293. package/dist/providers/fetch/compose.d.ts +7 -0
  294. package/dist/providers/fetch/compose.d.ts.map +1 -0
  295. package/dist/providers/fetch/compose.js +20 -0
  296. package/dist/providers/fetch/compose.js.map +1 -0
  297. package/dist/providers/fetch/composer.d.ts +13 -0
  298. package/dist/providers/fetch/composer.d.ts.map +1 -0
  299. package/dist/providers/fetch/composer.js +15 -0
  300. package/dist/providers/fetch/composer.js.map +1 -0
  301. package/dist/providers/fetch/composer.test.d.ts +2 -0
  302. package/dist/providers/fetch/composer.test.d.ts.map +1 -0
  303. package/dist/providers/fetch/composer.test.js +448 -0
  304. package/dist/providers/fetch/composer.test.js.map +1 -0
  305. package/dist/providers/fetch/logging-middleware.d.ts +16 -0
  306. package/dist/providers/fetch/logging-middleware.d.ts.map +1 -0
  307. package/dist/providers/fetch/logging-middleware.js +214 -0
  308. package/dist/providers/fetch/logging-middleware.js.map +1 -0
  309. package/dist/providers/index.d.ts +1 -0
  310. package/dist/providers/index.d.ts.map +1 -1
  311. package/dist/providers/index.js +1 -0
  312. package/dist/providers/index.js.map +1 -1
  313. package/dist/providers/llama-cpp.provider.d.ts +3 -0
  314. package/dist/providers/llama-cpp.provider.d.ts.map +1 -0
  315. package/dist/providers/llama-cpp.provider.js +34 -0
  316. package/dist/providers/llama-cpp.provider.js.map +1 -0
  317. package/dist/providers/openai-compatible-behavior-gate.test.js +1 -42
  318. package/dist/providers/openai-compatible-behavior-gate.test.js.map +1 -1
  319. package/dist/providers/openai-compatible-middleware.d.ts +7 -0
  320. package/dist/providers/openai-compatible-middleware.d.ts.map +1 -0
  321. package/dist/providers/openai-compatible-middleware.js +133 -0
  322. package/dist/providers/openai-compatible-middleware.js.map +1 -0
  323. package/dist/providers/openai-compatible-models.d.ts +6 -0
  324. package/dist/providers/openai-compatible-models.d.ts.map +1 -0
  325. package/dist/providers/openai-compatible-models.js +17 -0
  326. package/dist/providers/openai-compatible-models.js.map +1 -0
  327. package/dist/providers/openai-compatible-response-normalizer.d.ts +7 -0
  328. package/dist/providers/openai-compatible-response-normalizer.d.ts.map +1 -0
  329. package/dist/providers/openai-compatible-response-normalizer.js +58 -0
  330. package/dist/providers/openai-compatible-response-normalizer.js.map +1 -0
  331. package/dist/providers/openai-compatible.provider.d.ts +4 -1
  332. package/dist/providers/openai-compatible.provider.d.ts.map +1 -1
  333. package/dist/providers/openai-compatible.provider.js +68 -324
  334. package/dist/providers/openai-compatible.provider.js.map +1 -1
  335. package/dist/providers/openai-compatible.provider.test.js +106 -1
  336. package/dist/providers/openai-compatible.provider.test.js.map +1 -1
  337. package/dist/providers/openai.provider.js +42 -10
  338. package/dist/providers/openai.provider.js.map +1 -1
  339. package/dist/providers/openai.provider.test.d.ts +2 -0
  340. package/dist/providers/openai.provider.test.d.ts.map +1 -0
  341. package/dist/providers/openai.provider.test.js +22 -0
  342. package/dist/providers/openai.provider.test.js.map +1 -0
  343. package/dist/providers/opencode.provider.d.ts +19 -0
  344. package/dist/providers/opencode.provider.d.ts.map +1 -0
  345. package/dist/providers/opencode.provider.js +45 -0
  346. package/dist/providers/opencode.provider.js.map +1 -0
  347. package/dist/providers/openrouter.provider.d.ts +2 -3
  348. package/dist/providers/openrouter.provider.d.ts.map +1 -1
  349. package/dist/providers/openrouter.provider.js +23 -24
  350. package/dist/providers/openrouter.provider.js.map +1 -1
  351. package/dist/providers/openrouter.provider.request-preprocessing.test.js +12 -9
  352. package/dist/providers/openrouter.provider.request-preprocessing.test.js.map +1 -1
  353. package/dist/providers/registry.d.ts +1 -0
  354. package/dist/providers/registry.d.ts.map +1 -1
  355. package/dist/providers/registry.js.map +1 -1
  356. package/dist/providers/registry.test.js +6 -22
  357. package/dist/providers/registry.test.js.map +1 -1
  358. package/dist/scripts/eval-auto-approval/dataset.d.ts +2 -2
  359. package/dist/services/approval-flow-coordinator.d.ts.map +1 -1
  360. package/dist/services/approval-flow-coordinator.js +17 -1
  361. package/dist/services/approval-flow-coordinator.js.map +1 -1
  362. package/dist/services/approval-flow-coordinator.test.js +22 -0
  363. package/dist/services/approval-flow-coordinator.test.js.map +1 -1
  364. package/dist/services/command-message-streaming.d.ts.map +1 -1
  365. package/dist/services/command-message-streaming.js +0 -3
  366. package/dist/services/command-message-streaming.js.map +1 -1
  367. package/dist/services/command-message-streaming.test.js +4 -2
  368. package/dist/services/command-message-streaming.test.js.map +1 -1
  369. package/dist/services/conversation-clear-save.test.js +38 -66
  370. package/dist/services/conversation-clear-save.test.js.map +1 -1
  371. package/dist/services/conversation-events.d.ts +44 -1
  372. package/dist/services/conversation-events.d.ts.map +1 -1
  373. package/dist/services/conversation-integration.test.js +3 -1
  374. package/dist/services/conversation-integration.test.js.map +1 -1
  375. package/dist/services/conversation-log-events.d.ts +107 -0
  376. package/dist/services/conversation-log-events.d.ts.map +1 -0
  377. package/dist/services/conversation-log-events.js +12 -0
  378. package/dist/services/conversation-log-events.js.map +1 -0
  379. package/dist/services/conversation-log-writer.d.ts +40 -0
  380. package/dist/services/conversation-log-writer.d.ts.map +1 -0
  381. package/dist/services/conversation-log-writer.js +264 -0
  382. package/dist/services/conversation-log-writer.js.map +1 -0
  383. package/dist/services/conversation-persistence-types.d.ts +14 -0
  384. package/dist/services/conversation-persistence-types.d.ts.map +1 -0
  385. package/dist/services/conversation-persistence-types.js +2 -0
  386. package/dist/services/conversation-persistence-types.js.map +1 -0
  387. package/dist/services/conversation-persistence.d.ts +38 -39
  388. package/dist/services/conversation-persistence.d.ts.map +1 -1
  389. package/dist/services/conversation-persistence.js +270 -82
  390. package/dist/services/conversation-persistence.js.map +1 -1
  391. package/dist/services/conversation-persistence.test.js +472 -637
  392. package/dist/services/conversation-persistence.test.js.map +1 -1
  393. package/dist/services/conversation-replay.d.ts +26 -0
  394. package/dist/services/conversation-replay.d.ts.map +1 -0
  395. package/dist/services/conversation-replay.js +252 -0
  396. package/dist/services/conversation-replay.js.map +1 -0
  397. package/dist/services/conversation-replay.test.d.ts +2 -0
  398. package/dist/services/conversation-replay.test.d.ts.map +1 -0
  399. package/dist/services/conversation-replay.test.js +153 -0
  400. package/dist/services/conversation-replay.test.js.map +1 -0
  401. package/dist/services/conversation-result-builder.d.ts.map +1 -1
  402. package/dist/services/conversation-result-builder.js +8 -2
  403. package/dist/services/conversation-result-builder.js.map +1 -1
  404. package/dist/services/conversation-result-builder.test.js +53 -0
  405. package/dist/services/conversation-result-builder.test.js.map +1 -1
  406. package/dist/services/conversation-retry-policy.d.ts +17 -0
  407. package/dist/services/conversation-retry-policy.d.ts.map +1 -1
  408. package/dist/services/conversation-retry-policy.js +35 -5
  409. package/dist/services/conversation-retry-policy.js.map +1 -1
  410. package/dist/services/conversation-retry-policy.test.js +56 -1
  411. package/dist/services/conversation-retry-policy.test.js.map +1 -1
  412. package/dist/services/conversation-service.d.ts +9 -0
  413. package/dist/services/conversation-service.d.ts.map +1 -1
  414. package/dist/services/conversation-service.js +18 -0
  415. package/dist/services/conversation-service.js.map +1 -1
  416. package/dist/services/conversation-session.d.ts +14 -0
  417. package/dist/services/conversation-session.d.ts.map +1 -1
  418. package/dist/services/conversation-session.js +426 -51
  419. package/dist/services/conversation-session.js.map +1 -1
  420. package/dist/services/input-surge-guard.d.ts +4 -0
  421. package/dist/services/input-surge-guard.d.ts.map +1 -1
  422. package/dist/services/input-surge-guard.js +2 -2
  423. package/dist/services/input-surge-guard.js.map +1 -1
  424. package/dist/services/input-surge-guard.test.js +15 -1
  425. package/dist/services/input-surge-guard.test.js.map +1 -1
  426. package/dist/services/large-uncached-input-guard.d.ts +35 -0
  427. package/dist/services/large-uncached-input-guard.d.ts.map +1 -0
  428. package/dist/services/large-uncached-input-guard.js +127 -0
  429. package/dist/services/large-uncached-input-guard.js.map +1 -0
  430. package/dist/services/large-uncached-input-guard.test.d.ts +2 -0
  431. package/dist/services/large-uncached-input-guard.test.d.ts.map +1 -0
  432. package/dist/services/large-uncached-input-guard.test.js +160 -0
  433. package/dist/services/large-uncached-input-guard.test.js.map +1 -0
  434. package/dist/services/model-service.d.ts +2 -0
  435. package/dist/services/model-service.d.ts.map +1 -1
  436. package/dist/services/model-service.js +7 -0
  437. package/dist/services/model-service.js.map +1 -1
  438. package/dist/services/provider-traffic.d.ts.map +1 -1
  439. package/dist/services/provider-traffic.js +82 -24
  440. package/dist/services/provider-traffic.js.map +1 -1
  441. package/dist/services/provider-traffic.test.js +98 -6
  442. package/dist/services/provider-traffic.test.js.map +1 -1
  443. package/dist/services/rtk-service.d.ts.map +1 -1
  444. package/dist/services/rtk-service.js +1 -2
  445. package/dist/services/rtk-service.js.map +1 -1
  446. package/dist/services/settings-schema.d.ts +36 -28
  447. package/dist/services/settings-schema.d.ts.map +1 -1
  448. package/dist/services/settings-schema.js +7 -2
  449. package/dist/services/settings-schema.js.map +1 -1
  450. package/dist/services/settings-sources.d.ts.map +1 -1
  451. package/dist/services/settings-sources.js +1 -0
  452. package/dist/services/settings-sources.js.map +1 -1
  453. package/dist/services/stream-event-processor.d.ts +4 -0
  454. package/dist/services/stream-event-processor.d.ts.map +1 -1
  455. package/dist/services/stream-event-processor.js +78 -39
  456. package/dist/services/stream-event-processor.js.map +1 -1
  457. package/dist/services/stream-event-processor.test.js +71 -0
  458. package/dist/services/stream-event-processor.test.js.map +1 -1
  459. package/dist/services/subagents/subagent-manager.d.ts.map +1 -1
  460. package/dist/services/subagents/subagent-manager.js +88 -16
  461. package/dist/services/subagents/subagent-manager.js.map +1 -1
  462. package/dist/services/subagents/subagent-manager.test.js +354 -0
  463. package/dist/services/subagents/subagent-manager.test.js.map +1 -1
  464. package/dist/services/terminal-result-collector.d.ts.map +1 -1
  465. package/dist/services/terminal-result-collector.js +6 -1
  466. package/dist/services/terminal-result-collector.js.map +1 -1
  467. package/dist/services/tool-call-arguments.d.ts +20 -0
  468. package/dist/services/tool-call-arguments.d.ts.map +1 -0
  469. package/dist/services/tool-call-arguments.js +49 -0
  470. package/dist/services/tool-call-arguments.js.map +1 -0
  471. package/dist/services/tool-execution-ledger.d.ts +35 -0
  472. package/dist/services/tool-execution-ledger.d.ts.map +1 -0
  473. package/dist/services/tool-execution-ledger.js +204 -0
  474. package/dist/services/tool-execution-ledger.js.map +1 -0
  475. package/dist/services/tool-execution-ledger.test.d.ts +2 -0
  476. package/dist/services/tool-execution-ledger.test.d.ts.map +1 -0
  477. package/dist/services/tool-execution-ledger.test.js +96 -0
  478. package/dist/services/tool-execution-ledger.test.js.map +1 -0
  479. package/dist/tools/apply-patch.d.ts +8 -0
  480. package/dist/tools/apply-patch.d.ts.map +1 -1
  481. package/dist/tools/apply-patch.js +157 -2
  482. package/dist/tools/apply-patch.js.map +1 -1
  483. package/dist/tools/apply-patch.test.js +81 -0
  484. package/dist/tools/apply-patch.test.js.map +1 -1
  485. package/dist/tools/search-replace.d.ts.map +1 -1
  486. package/dist/tools/search-replace.js +92 -140
  487. package/dist/tools/search-replace.js.map +1 -1
  488. package/dist/utils/conversation-event-handler.d.ts.map +1 -1
  489. package/dist/utils/conversation-event-handler.js +33 -1
  490. package/dist/utils/conversation-event-handler.js.map +1 -1
  491. package/dist/utils/conversation-event-handler.test.js +103 -0
  492. package/dist/utils/conversation-event-handler.test.js.map +1 -1
  493. package/dist/utils/error-helpers.d.ts +1 -0
  494. package/dist/utils/error-helpers.d.ts.map +1 -1
  495. package/dist/utils/error-helpers.js +61 -0
  496. package/dist/utils/error-helpers.js.map +1 -1
  497. package/dist/utils/error-helpers.test.js +13 -1
  498. package/dist/utils/error-helpers.test.js.map +1 -1
  499. package/dist/utils/ink-render-options.d.ts +1 -1
  500. package/dist/utils/ink-render-options.d.ts.map +1 -1
  501. package/dist/utils/ink-render-options.js +1 -1
  502. package/dist/utils/ink-render-options.js.map +1 -1
  503. package/dist/utils/project-tree.d.ts +14 -0
  504. package/dist/utils/project-tree.d.ts.map +1 -0
  505. package/dist/utils/project-tree.js +111 -0
  506. package/dist/utils/project-tree.js.map +1 -0
  507. package/dist/utils/project-tree.test.d.ts +2 -0
  508. package/dist/utils/project-tree.test.d.ts.map +1 -0
  509. package/dist/utils/project-tree.test.js +149 -0
  510. package/dist/utils/project-tree.test.js.map +1 -0
  511. package/dist/utils/streaming-session-factory.d.ts +2 -0
  512. package/dist/utils/streaming-session-factory.d.ts.map +1 -1
  513. package/dist/utils/streaming-session-factory.js +4 -0
  514. package/dist/utils/streaming-session-factory.js.map +1 -1
  515. package/dist/utils/terminal-clear.d.ts +7 -0
  516. package/dist/utils/terminal-clear.d.ts.map +1 -0
  517. package/dist/utils/terminal-clear.js +5 -0
  518. package/dist/utils/terminal-clear.js.map +1 -0
  519. package/dist/utils/terminal-clear.test.d.ts +2 -0
  520. package/dist/utils/terminal-clear.test.d.ts.map +1 -0
  521. package/dist/utils/terminal-clear.test.js +13 -0
  522. package/dist/utils/terminal-clear.test.js.map +1 -0
  523. package/package.json +2 -2
  524. package/dist/agent.lite-mode.test.d.ts +0 -2
  525. package/dist/agent.lite-mode.test.d.ts.map +0 -1
  526. package/dist/agent.lite-mode.test.js +0 -39
  527. package/dist/agent.lite-mode.test.js.map +0 -1
  528. package/dist/components/ApprovalPrompt.test.d.ts +0 -2
  529. package/dist/components/ApprovalPrompt.test.d.ts.map +0 -1
  530. package/dist/components/ApprovalPrompt.test.js +0 -23
  531. package/dist/components/ApprovalPrompt.test.js.map +0 -1
  532. package/dist/components/LiveResponse.test.d.ts +0 -2
  533. package/dist/components/LiveResponse.test.d.ts.map +0 -1
  534. package/dist/components/LiveResponse.test.js +0 -13
  535. package/dist/components/LiveResponse.test.js.map +0 -1
  536. package/dist/components/ModelSelectionMenu.error-tabs.test.d.ts +0 -2
  537. package/dist/components/ModelSelectionMenu.error-tabs.test.d.ts.map +0 -1
  538. package/dist/components/ModelSelectionMenu.error-tabs.test.js +0 -18
  539. package/dist/components/ModelSelectionMenu.error-tabs.test.js.map +0 -1
  540. package/dist/components/TextInput.d.ts +0 -42
  541. package/dist/components/TextInput.d.ts.map +0 -1
  542. package/dist/components/TextInput.js +0 -398
  543. package/dist/components/TextInput.js.map +0 -1
  544. package/dist/components/TextInput.test.d.ts +0 -2
  545. package/dist/components/TextInput.test.d.ts.map +0 -1
  546. package/dist/components/TextInput.test.js +0 -75
  547. package/dist/components/TextInput.test.js.map +0 -1
  548. package/dist/debug_ask_mentor.d.ts +0 -2
  549. package/dist/debug_ask_mentor.d.ts.map +0 -1
  550. package/dist/debug_ask_mentor.js +0 -73
  551. package/dist/debug_ask_mentor.js.map +0 -1
  552. package/dist/hooks/use-conversation.max-turns-undo.test.d.ts +0 -2
  553. package/dist/hooks/use-conversation.max-turns-undo.test.d.ts.map +0 -1
  554. package/dist/hooks/use-conversation.max-turns-undo.test.js +0 -118
  555. package/dist/hooks/use-conversation.max-turns-undo.test.js.map +0 -1
  556. package/dist/lib/shell.d.ts +0 -7
  557. package/dist/lib/shell.d.ts.map +0 -1
  558. package/dist/lib/shell.js +0 -56
  559. package/dist/lib/shell.js.map +0 -1
  560. package/dist/modes/companion/command-index.d.ts +0 -26
  561. package/dist/modes/companion/command-index.d.ts.map +0 -1
  562. package/dist/modes/companion/command-index.js +0 -50
  563. package/dist/modes/companion/command-index.js.map +0 -1
  564. package/dist/modes/companion/command-index.test.d.ts +0 -2
  565. package/dist/modes/companion/command-index.test.d.ts.map +0 -1
  566. package/dist/modes/companion/command-index.test.js +0 -86
  567. package/dist/modes/companion/command-index.test.js.map +0 -1
  568. package/dist/modes/companion/companion-app.d.ts +0 -12
  569. package/dist/modes/companion/companion-app.d.ts.map +0 -1
  570. package/dist/modes/companion/companion-app.js +0 -297
  571. package/dist/modes/companion/companion-app.js.map +0 -1
  572. package/dist/modes/companion/companion-session.d.ts +0 -63
  573. package/dist/modes/companion/companion-session.d.ts.map +0 -1
  574. package/dist/modes/companion/companion-session.js +0 -146
  575. package/dist/modes/companion/companion-session.js.map +0 -1
  576. package/dist/modes/companion/companion-session.test.d.ts +0 -2
  577. package/dist/modes/companion/companion-session.test.d.ts.map +0 -1
  578. package/dist/modes/companion/companion-session.test.js +0 -28
  579. package/dist/modes/companion/companion-session.test.js.map +0 -1
  580. package/dist/modes/companion/components/status-bar.d.ts +0 -13
  581. package/dist/modes/companion/components/status-bar.d.ts.map +0 -1
  582. package/dist/modes/companion/components/status-bar.js +0 -26
  583. package/dist/modes/companion/components/status-bar.js.map +0 -1
  584. package/dist/modes/companion/context-buffer.d.ts +0 -65
  585. package/dist/modes/companion/context-buffer.d.ts.map +0 -1
  586. package/dist/modes/companion/context-buffer.js +0 -156
  587. package/dist/modes/companion/context-buffer.js.map +0 -1
  588. package/dist/modes/companion/context-buffer.test.d.ts +0 -2
  589. package/dist/modes/companion/context-buffer.test.d.ts.map +0 -1
  590. package/dist/modes/companion/context-buffer.test.js +0 -154
  591. package/dist/modes/companion/context-buffer.test.js.map +0 -1
  592. package/dist/modes/companion/event-detector.d.ts +0 -46
  593. package/dist/modes/companion/event-detector.d.ts.map +0 -1
  594. package/dist/modes/companion/event-detector.js +0 -169
  595. package/dist/modes/companion/event-detector.js.map +0 -1
  596. package/dist/modes/companion/event-detector.test.d.ts +0 -2
  597. package/dist/modes/companion/event-detector.test.d.ts.map +0 -1
  598. package/dist/modes/companion/event-detector.test.js +0 -121
  599. package/dist/modes/companion/event-detector.test.js.map +0 -1
  600. package/dist/modes/companion/index.d.ts +0 -33
  601. package/dist/modes/companion/index.d.ts.map +0 -1
  602. package/dist/modes/companion/index.js +0 -21
  603. package/dist/modes/companion/index.js.map +0 -1
  604. package/dist/modes/companion/input-key-mapper.d.ts +0 -3
  605. package/dist/modes/companion/input-key-mapper.d.ts.map +0 -1
  606. package/dist/modes/companion/input-key-mapper.js +0 -31
  607. package/dist/modes/companion/input-key-mapper.js.map +0 -1
  608. package/dist/modes/companion/input-key-mapper.test.d.ts +0 -2
  609. package/dist/modes/companion/input-key-mapper.test.d.ts.map +0 -1
  610. package/dist/modes/companion/input-key-mapper.test.js +0 -26
  611. package/dist/modes/companion/input-key-mapper.test.js.map +0 -1
  612. package/dist/modes/companion/input-parser.d.ts +0 -53
  613. package/dist/modes/companion/input-parser.d.ts.map +0 -1
  614. package/dist/modes/companion/input-parser.js +0 -114
  615. package/dist/modes/companion/input-parser.js.map +0 -1
  616. package/dist/modes/companion/input-parser.test.d.ts +0 -2
  617. package/dist/modes/companion/input-parser.test.d.ts.map +0 -1
  618. package/dist/modes/companion/input-parser.test.js +0 -123
  619. package/dist/modes/companion/input-parser.test.js.map +0 -1
  620. package/dist/modes/companion/mode-manager.d.ts +0 -41
  621. package/dist/modes/companion/mode-manager.d.ts.map +0 -1
  622. package/dist/modes/companion/mode-manager.js +0 -56
  623. package/dist/modes/companion/mode-manager.js.map +0 -1
  624. package/dist/modes/companion/mode-manager.test.d.ts +0 -2
  625. package/dist/modes/companion/mode-manager.test.d.ts.map +0 -1
  626. package/dist/modes/companion/mode-manager.test.js +0 -65
  627. package/dist/modes/companion/mode-manager.test.js.map +0 -1
  628. package/dist/modes/companion/output-classifier.d.ts +0 -15
  629. package/dist/modes/companion/output-classifier.d.ts.map +0 -1
  630. package/dist/modes/companion/output-classifier.js +0 -77
  631. package/dist/modes/companion/output-classifier.js.map +0 -1
  632. package/dist/modes/companion/output-classifier.test.d.ts +0 -2
  633. package/dist/modes/companion/output-classifier.test.d.ts.map +0 -1
  634. package/dist/modes/companion/output-classifier.test.js +0 -133
  635. package/dist/modes/companion/output-classifier.test.js.map +0 -1
  636. package/dist/modes/companion/pty-wrapper.d.ts +0 -46
  637. package/dist/modes/companion/pty-wrapper.d.ts.map +0 -1
  638. package/dist/modes/companion/pty-wrapper.js +0 -143
  639. package/dist/modes/companion/pty-wrapper.js.map +0 -1
  640. package/dist/modes/companion/safety-classifier.d.ts +0 -31
  641. package/dist/modes/companion/safety-classifier.d.ts.map +0 -1
  642. package/dist/modes/companion/safety-classifier.js +0 -140
  643. package/dist/modes/companion/safety-classifier.js.map +0 -1
  644. package/dist/modes/companion/safety-classifier.test.d.ts +0 -2
  645. package/dist/modes/companion/safety-classifier.test.d.ts.map +0 -1
  646. package/dist/modes/companion/safety-classifier.test.js +0 -151
  647. package/dist/modes/companion/safety-classifier.test.js.map +0 -1
  648. package/dist/modes/companion/summarizer.d.ts +0 -24
  649. package/dist/modes/companion/summarizer.d.ts.map +0 -1
  650. package/dist/modes/companion/summarizer.js +0 -132
  651. package/dist/modes/companion/summarizer.js.map +0 -1
  652. package/dist/modes/companion/terminal-history.d.ts +0 -27
  653. package/dist/modes/companion/terminal-history.d.ts.map +0 -1
  654. package/dist/modes/companion/terminal-history.js +0 -142
  655. package/dist/modes/companion/terminal-history.js.map +0 -1
  656. package/dist/prompts/simple-mentor.md +0 -215
  657. package/dist/providers/ai-sdk-openai.provider.d.ts +0 -14
  658. package/dist/providers/ai-sdk-openai.provider.d.ts.map +0 -1
  659. package/dist/providers/ai-sdk-openai.provider.js +0 -18
  660. package/dist/providers/ai-sdk-openai.provider.js.map +0 -1
  661. package/dist/providers/github-copilot/converters.d.ts +0 -45
  662. package/dist/providers/github-copilot/converters.d.ts.map +0 -1
  663. package/dist/providers/github-copilot/converters.js +0 -118
  664. package/dist/providers/github-copilot/converters.js.map +0 -1
  665. package/dist/providers/github-copilot/converters.test.d.ts +0 -2
  666. package/dist/providers/github-copilot/converters.test.d.ts.map +0 -1
  667. package/dist/providers/github-copilot/converters.test.js +0 -162
  668. package/dist/providers/github-copilot/converters.test.js.map +0 -1
  669. package/dist/providers/github-copilot/github-copilot.provider.d.ts +0 -2
  670. package/dist/providers/github-copilot/github-copilot.provider.d.ts.map +0 -1
  671. package/dist/providers/github-copilot/github-copilot.provider.js +0 -75
  672. package/dist/providers/github-copilot/github-copilot.provider.js.map +0 -1
  673. package/dist/providers/github-copilot/github-copilot.provider.test.d.ts +0 -2
  674. package/dist/providers/github-copilot/github-copilot.provider.test.d.ts.map +0 -1
  675. package/dist/providers/github-copilot/github-copilot.provider.test.js +0 -26
  676. package/dist/providers/github-copilot/github-copilot.provider.test.js.map +0 -1
  677. package/dist/providers/github-copilot/index.d.ts +0 -4
  678. package/dist/providers/github-copilot/index.d.ts.map +0 -1
  679. package/dist/providers/github-copilot/index.js +0 -4
  680. package/dist/providers/github-copilot/index.js.map +0 -1
  681. package/dist/providers/github-copilot/model.d.ts +0 -24
  682. package/dist/providers/github-copilot/model.d.ts.map +0 -1
  683. package/dist/providers/github-copilot/model.delta.test.d.ts +0 -2
  684. package/dist/providers/github-copilot/model.delta.test.d.ts.map +0 -1
  685. package/dist/providers/github-copilot/model.delta.test.js +0 -15
  686. package/dist/providers/github-copilot/model.delta.test.js.map +0 -1
  687. package/dist/providers/github-copilot/model.js +0 -498
  688. package/dist/providers/github-copilot/model.js.map +0 -1
  689. package/dist/providers/github-copilot/model.test.d.ts +0 -2
  690. package/dist/providers/github-copilot/model.test.d.ts.map +0 -1
  691. package/dist/providers/github-copilot/model.test.js +0 -63
  692. package/dist/providers/github-copilot/model.test.js.map +0 -1
  693. package/dist/providers/github-copilot/provider.d.ts +0 -15
  694. package/dist/providers/github-copilot/provider.d.ts.map +0 -1
  695. package/dist/providers/github-copilot/provider.js +0 -25
  696. package/dist/providers/github-copilot/provider.js.map +0 -1
  697. package/dist/providers/github-copilot/provider.test.d.ts +0 -2
  698. package/dist/providers/github-copilot/provider.test.d.ts.map +0 -1
  699. package/dist/providers/github-copilot/provider.test.js +0 -52
  700. package/dist/providers/github-copilot/provider.test.js.map +0 -1
  701. package/dist/providers/github-copilot/utils.d.ts +0 -20
  702. package/dist/providers/github-copilot/utils.d.ts.map +0 -1
  703. package/dist/providers/github-copilot/utils.js +0 -142
  704. package/dist/providers/github-copilot/utils.js.map +0 -1
  705. package/dist/providers/github-copilot/utils.test.d.ts +0 -2
  706. package/dist/providers/github-copilot/utils.test.d.ts.map +0 -1
  707. package/dist/providers/github-copilot/utils.test.js +0 -21
  708. package/dist/providers/github-copilot/utils.test.js.map +0 -1
  709. package/dist/providers/openai-compatible/api.d.ts +0 -17
  710. package/dist/providers/openai-compatible/api.d.ts.map +0 -1
  711. package/dist/providers/openai-compatible/api.js +0 -58
  712. package/dist/providers/openai-compatible/api.js.map +0 -1
  713. package/dist/providers/openai-compatible/model.d.ts +0 -17
  714. package/dist/providers/openai-compatible/model.d.ts.map +0 -1
  715. package/dist/providers/openai-compatible/model.js +0 -473
  716. package/dist/providers/openai-compatible/model.js.map +0 -1
  717. package/dist/providers/openai-compatible/provider.d.ts +0 -22
  718. package/dist/providers/openai-compatible/provider.d.ts.map +0 -1
  719. package/dist/providers/openai-compatible/provider.js +0 -43
  720. package/dist/providers/openai-compatible/provider.js.map +0 -1
  721. package/dist/providers/openai-compatible/reasoning-content.test.d.ts +0 -2
  722. package/dist/providers/openai-compatible/reasoning-content.test.d.ts.map +0 -1
  723. package/dist/providers/openai-compatible/reasoning-content.test.js +0 -258
  724. package/dist/providers/openai-compatible/reasoning-content.test.js.map +0 -1
  725. package/dist/providers/openai-compatible/utils.d.ts +0 -3
  726. package/dist/providers/openai-compatible/utils.d.ts.map +0 -1
  727. package/dist/providers/openai-compatible/utils.js +0 -11
  728. package/dist/providers/openai-compatible/utils.js.map +0 -1
  729. package/dist/providers/openrouter/api.d.ts +0 -39
  730. package/dist/providers/openrouter/api.d.ts.map +0 -1
  731. package/dist/providers/openrouter/api.js +0 -164
  732. package/dist/providers/openrouter/api.js.map +0 -1
  733. package/dist/providers/openrouter/converters.d.ts +0 -8
  734. package/dist/providers/openrouter/converters.d.ts.map +0 -1
  735. package/dist/providers/openrouter/converters.js +0 -391
  736. package/dist/providers/openrouter/converters.js.map +0 -1
  737. package/dist/providers/openrouter/converters.test.d.ts +0 -2
  738. package/dist/providers/openrouter/converters.test.d.ts.map +0 -1
  739. package/dist/providers/openrouter/converters.test.js +0 -179
  740. package/dist/providers/openrouter/converters.test.js.map +0 -1
  741. package/dist/providers/openrouter/index.d.ts +0 -4
  742. package/dist/providers/openrouter/index.d.ts.map +0 -1
  743. package/dist/providers/openrouter/index.js +0 -4
  744. package/dist/providers/openrouter/index.js.map +0 -1
  745. package/dist/providers/openrouter/model.d.ts +0 -14
  746. package/dist/providers/openrouter/model.d.ts.map +0 -1
  747. package/dist/providers/openrouter/model.js +0 -457
  748. package/dist/providers/openrouter/model.js.map +0 -1
  749. package/dist/providers/openrouter/provider.d.ts +0 -15
  750. package/dist/providers/openrouter/provider.d.ts.map +0 -1
  751. package/dist/providers/openrouter/provider.js +0 -21
  752. package/dist/providers/openrouter/provider.js.map +0 -1
  753. package/dist/providers/openrouter/reasoning-content.test.d.ts +0 -2
  754. package/dist/providers/openrouter/reasoning-content.test.d.ts.map +0 -1
  755. package/dist/providers/openrouter/reasoning-content.test.js +0 -122
  756. package/dist/providers/openrouter/reasoning-content.test.js.map +0 -1
  757. package/dist/providers/openrouter/utils.d.ts +0 -11
  758. package/dist/providers/openrouter/utils.d.ts.map +0 -1
  759. package/dist/providers/openrouter/utils.js +0 -29
  760. package/dist/providers/openrouter/utils.js.map +0 -1
  761. package/dist/providers/openrouter.api.retry.test.d.ts +0 -2
  762. package/dist/providers/openrouter.api.retry.test.d.ts.map +0 -1
  763. package/dist/providers/openrouter.api.retry.test.js +0 -148
  764. package/dist/providers/openrouter.api.retry.test.js.map +0 -1
  765. package/dist/providers/openrouter.test.d.ts +0 -2
  766. package/dist/providers/openrouter.test.d.ts.map +0 -1
  767. package/dist/providers/openrouter.test.js +0 -1449
  768. package/dist/providers/openrouter.test.js.map +0 -1
  769. package/dist/safety-checker.js +0 -57
  770. package/dist/services/check_mock.d.ts +0 -2
  771. package/dist/services/check_mock.d.ts.map +0 -1
  772. package/dist/services/check_mock.js +0 -22
  773. package/dist/services/check_mock.js.map +0 -1
  774. package/dist/services/conversation-service.test.d.ts +0 -2
  775. package/dist/services/conversation-service.test.d.ts.map +0 -1
  776. package/dist/services/conversation-service.test.js +0 -156
  777. package/dist/services/conversation-service.test.js.map +0 -1
  778. package/dist/services/subagents/reasoning-inheritance.test.d.ts +0 -2
  779. package/dist/services/subagents/reasoning-inheritance.test.d.ts.map +0 -1
  780. package/dist/services/subagents/reasoning-inheritance.test.js +0 -73
  781. package/dist/services/subagents/reasoning-inheritance.test.js.map +0 -1
  782. package/dist/tools/bash.d.ts +0 -10
  783. package/dist/tools/bash.d.ts.map +0 -1
  784. package/dist/tools/bash.js +0 -55
  785. package/dist/tools/bash.js.map +0 -1
  786. package/dist/utils/command-safety.d.ts +0 -21
  787. package/dist/utils/command-safety.d.ts.map +0 -1
  788. package/dist/utils/command-safety.devnull.test.d.ts +0 -2
  789. package/dist/utils/command-safety.devnull.test.d.ts.map +0 -1
  790. package/dist/utils/command-safety.devnull.test.js +0 -13
  791. package/dist/utils/command-safety.devnull.test.js.map +0 -1
  792. package/dist/utils/command-safety.js +0 -348
  793. package/dist/utils/command-safety.js.map +0 -1
  794. package/dist/utils/extract-command-messages.test.d.ts +0 -2
  795. package/dist/utils/extract-command-messages.test.d.ts.map +0 -1
  796. package/dist/utils/provider-traffic-extractor.repro.test.d.ts +0 -2
  797. package/dist/utils/provider-traffic-extractor.repro.test.d.ts.map +0 -1
  798. package/dist/utils/provider-traffic-extractor.repro.test.js.map +0 -1
@@ -1,1449 +0,0 @@
1
- import test from 'ava';
2
- import { ReadableStream } from 'node:stream/web';
3
- import { OpenRouterModel, OpenRouterError } from './openrouter.js';
4
- import { createMockSettingsService } from '../services/settings-service.mock.js';
5
- import { LoggingService } from '../services/logging-service.js';
6
- // Helper to extract text from message content (handles discriminated union)
7
- const getContentText = (msg, index = 0) => {
8
- const content = msg?.content?.[index];
9
- return content?.type === 'output_text' ? content.text : undefined;
10
- };
11
- const originalFetch = globalThis.fetch;
12
- // Use a dedicated logger instance for this test suite (avoid deprecated singleton)
13
- const logger = new LoggingService({ disableLogging: true });
14
- // Create a mock settings service with OpenRouter API key for tests
15
- const mockSettingsService = createMockSettingsService({
16
- agent: {
17
- openrouter: {
18
- apiKey: 'mock-api-key',
19
- },
20
- },
21
- });
22
- const createJsonResponse = (body) => new Response(JSON.stringify(body), {
23
- status: 200,
24
- headers: { 'Content-Type': 'application/json' },
25
- });
26
- test.beforeEach(() => {
27
- // Nothing to do - mock is already configured
28
- });
29
- test.afterEach.always(() => {
30
- globalThis.fetch = originalFetch;
31
- });
32
- test.serial('builds messages from explicit history, tool calls, and reasoning config', async (t) => {
33
- const requests = [];
34
- const responses = [
35
- createJsonResponse({
36
- id: 'resp-1',
37
- choices: [
38
- {
39
- message: {
40
- content: 'Hello back',
41
- reasoning_details: [{ type: 'text', text: 'thinking' }],
42
- },
43
- },
44
- ],
45
- usage: {
46
- prompt_tokens: 1,
47
- completion_tokens: 2,
48
- total_tokens: 3,
49
- },
50
- }),
51
- createJsonResponse({
52
- id: 'resp-2',
53
- choices: [{ message: { content: 'Second turn reply' } }],
54
- usage: {
55
- prompt_tokens: 2,
56
- completion_tokens: 1,
57
- total_tokens: 3,
58
- },
59
- }),
60
- ];
61
- globalThis.fetch = async (_url, options) => {
62
- const body = JSON.parse(options.body);
63
- requests.push(body);
64
- return responses[requests.length - 1];
65
- };
66
- const model = new OpenRouterModel({
67
- settingsService: mockSettingsService,
68
- loggingService: logger,
69
- modelId: 'mock-model',
70
- });
71
- await model.getResponse({
72
- systemInstructions: 'system message',
73
- input: 'First user turn',
74
- modelSettings: { reasoningEffort: 'high' },
75
- });
76
- // Caller-managed history: include the prior user + assistant messages explicitly.
77
- const secondTurnItems = [
78
- { type: 'message', role: 'user', content: 'First user turn' },
79
- {
80
- type: 'message',
81
- role: 'assistant',
82
- content: [{ type: 'output_text', text: 'Hello back' }],
83
- status: 'completed',
84
- reasoning_details: [{ type: 'text', text: 'thinking' }],
85
- },
86
- { type: 'input_text', text: 'Second user turn' },
87
- {
88
- type: 'function_call',
89
- id: 'call-123',
90
- name: 'bash',
91
- arguments: '{"cmd":"ls"}',
92
- },
93
- {
94
- type: 'function_call_result',
95
- callId: 'call-123',
96
- output: 'files\n',
97
- },
98
- ];
99
- await model.getResponse({
100
- systemInstructions: 'system message',
101
- input: secondTurnItems,
102
- });
103
- t.truthy(requests[0]);
104
- t.deepEqual(requests[0].reasoning, { effort: 'high' });
105
- t.deepEqual(requests[0].tools, []);
106
- const secondRequest = requests[1];
107
- t.truthy(secondRequest);
108
- t.deepEqual(secondRequest.messages, [
109
- { role: 'system', content: 'system message' },
110
- { role: 'user', content: 'First user turn' },
111
- {
112
- role: 'assistant',
113
- content: 'Hello back',
114
- reasoning_details: [{ type: 'text', text: 'thinking' }],
115
- },
116
- { role: 'user', content: 'Second user turn' },
117
- {
118
- role: 'assistant',
119
- content: null,
120
- tool_calls: [
121
- {
122
- id: 'call-123',
123
- type: 'function',
124
- function: { name: 'bash', arguments: '{"cmd":"ls"}' },
125
- },
126
- ],
127
- },
128
- { role: 'tool', tool_call_id: 'call-123', content: 'files\n' },
129
- ]);
130
- });
131
- test.serial('preserves assistant reasoning_details when nested under rawItem in history', async (t) => {
132
- const requests = [];
133
- globalThis.fetch = async (_url, options) => {
134
- const body = JSON.parse(options.body);
135
- requests.push(body);
136
- return new Response(JSON.stringify({
137
- choices: [{ message: { content: 'ok' } }],
138
- usage: {},
139
- }), {
140
- status: 200,
141
- headers: { 'Content-Type': 'application/json' },
142
- });
143
- };
144
- const model = new OpenRouterModel({
145
- settingsService: mockSettingsService,
146
- loggingService: logger,
147
- modelId: 'mock-model',
148
- });
149
- await model.getResponse({
150
- systemInstructions: 'system message',
151
- input: [
152
- { type: 'message', role: 'user', content: 'hi' },
153
- {
154
- rawItem: {
155
- type: 'message',
156
- role: 'assistant',
157
- status: 'completed',
158
- content: [{ type: 'output_text', text: 'Hello back' }],
159
- reasoning_details: [{ type: 'text', text: 'thinking' }],
160
- },
161
- },
162
- ],
163
- });
164
- t.truthy(requests[0]);
165
- t.deepEqual(requests[0].messages, [
166
- { role: 'system', content: 'system message' },
167
- { role: 'user', content: 'hi' },
168
- {
169
- role: 'assistant',
170
- content: 'Hello back',
171
- reasoning_details: [{ type: 'text', text: 'thinking' }],
172
- },
173
- ]);
174
- });
175
- test.serial('preserves assistant reasoning (reasoning tokens) in history messages', async (t) => {
176
- const requests = [];
177
- globalThis.fetch = async (_url, options) => {
178
- const body = JSON.parse(options.body);
179
- requests.push(body);
180
- return createJsonResponse({
181
- id: 'resp-ok',
182
- choices: [{ message: { content: 'ok' } }],
183
- usage: {},
184
- });
185
- };
186
- const model = new OpenRouterModel({
187
- settingsService: mockSettingsService,
188
- loggingService: logger,
189
- modelId: 'mock-model',
190
- });
191
- await model.getResponse({
192
- systemInstructions: 'system message',
193
- input: [
194
- { type: 'message', role: 'user', content: 'hi' },
195
- {
196
- type: 'message',
197
- role: 'assistant',
198
- status: 'completed',
199
- content: [{ type: 'output_text', text: 'Hello back' }],
200
- reasoning: 'Thinking privately...',
201
- reasoning_details: [
202
- {
203
- type: 'reasoning.text',
204
- text: 'step1',
205
- format: null,
206
- index: 0,
207
- },
208
- ],
209
- },
210
- ],
211
- });
212
- t.truthy(requests[0]);
213
- t.deepEqual(requests[0].messages, [
214
- { role: 'system', content: 'system message' },
215
- { role: 'user', content: 'hi' },
216
- {
217
- role: 'assistant',
218
- content: 'Hello back',
219
- reasoning: 'Thinking privately...',
220
- reasoning_details: [
221
- {
222
- type: 'reasoning.text',
223
- text: 'step1',
224
- format: null,
225
- index: 0,
226
- },
227
- ],
228
- },
229
- ]);
230
- });
231
- test.serial('replays reasoning_details alongside tool_calls when stored on function_call items', async (t) => {
232
- const requests = [];
233
- globalThis.fetch = async (_url, options) => {
234
- const body = JSON.parse(options.body);
235
- requests.push(body);
236
- return createJsonResponse({
237
- id: 'resp-ok',
238
- choices: [{ message: { content: 'ok' } }],
239
- usage: {},
240
- });
241
- };
242
- const model = new OpenRouterModel({
243
- settingsService: mockSettingsService,
244
- loggingService: logger,
245
- modelId: 'mock-model',
246
- });
247
- await model.getResponse({
248
- systemInstructions: 'system message',
249
- input: [
250
- { type: 'message', role: 'user', content: 'hi' },
251
- {
252
- type: 'function_call',
253
- id: 'call-123',
254
- name: 'bash',
255
- arguments: '{"cmd":"ls"}',
256
- reasoning_details: [
257
- {
258
- type: 'reasoning.text',
259
- text: 'tool planning',
260
- format: 'anthropic-claude-v1',
261
- index: 0,
262
- },
263
- ],
264
- reasoning: 'I should run ls',
265
- },
266
- {
267
- type: 'function_call_result',
268
- callId: 'call-123',
269
- output: 'files\n',
270
- },
271
- ],
272
- });
273
- const messages = requests[0].messages;
274
- const assistantWithToolCalls = messages.find((m) => m.role === 'assistant' && Array.isArray(m.tool_calls));
275
- t.truthy(assistantWithToolCalls);
276
- t.deepEqual(assistantWithToolCalls.reasoning_details, [
277
- {
278
- type: 'reasoning.text',
279
- text: 'tool planning',
280
- format: 'anthropic-claude-v1',
281
- index: 0,
282
- },
283
- ]);
284
- t.is(assistantWithToolCalls.reasoning, 'I should run ls');
285
- });
286
- test.serial('reconstructs reasoning_details from standalone reasoning items before tool calls (Gemini requirement)', async (t) => {
287
- const requests = [];
288
- globalThis.fetch = async (_url, options) => {
289
- const body = JSON.parse(options.body);
290
- requests.push(body);
291
- return createJsonResponse({
292
- id: 'resp-ok',
293
- choices: [{ message: { content: 'ok' } }],
294
- usage: {},
295
- });
296
- };
297
- const model = new OpenRouterModel({
298
- settingsService: mockSettingsService,
299
- loggingService: logger,
300
- modelId: 'mock-model',
301
- });
302
- const input = [
303
- {
304
- role: 'user',
305
- type: 'message',
306
- content: 'do I have uncommitted change',
307
- },
308
- {
309
- id: 'reasoning-1',
310
- type: 'reasoning',
311
- providerData: {
312
- type: 'reasoning.text',
313
- text: 'step1',
314
- format: 'google-gemini-v1',
315
- index: 0,
316
- },
317
- content: [{ type: 'input_text', text: 'step1' }],
318
- },
319
- {
320
- id: 'tool_shell_1',
321
- type: 'reasoning',
322
- providerData: {
323
- id: 'tool_shell_1',
324
- type: 'reasoning.encrypted',
325
- data: 'ENC',
326
- format: 'google-gemini-v1',
327
- index: 1,
328
- },
329
- content: [],
330
- },
331
- {
332
- type: 'function_call',
333
- callId: 'tool_shell_1',
334
- name: 'shell',
335
- status: 'completed',
336
- arguments: '{"command":"git status"}',
337
- },
338
- {
339
- type: 'function_call_result',
340
- callId: 'tool_shell_1',
341
- name: 'shell',
342
- status: 'completed',
343
- output: { type: 'text', text: 'ok' },
344
- },
345
- ];
346
- await model.getResponse({
347
- systemInstructions: 'system message',
348
- input,
349
- });
350
- const body = requests[0];
351
- t.truthy(body);
352
- // Expect the reasoning blocks to be replayed as reasoning_details on the assistant tool_calls message.
353
- const assistantToolCall = body.messages.find((m) => m.role === 'assistant' && Array.isArray(m.tool_calls));
354
- t.truthy(assistantToolCall);
355
- t.deepEqual(assistantToolCall.reasoning_details, [
356
- {
357
- type: 'reasoning.text',
358
- text: 'step1',
359
- format: 'google-gemini-v1',
360
- index: 0,
361
- },
362
- {
363
- id: 'tool_shell_1',
364
- type: 'reasoning.encrypted',
365
- data: 'ENC',
366
- format: 'google-gemini-v1',
367
- index: 1,
368
- },
369
- ]);
370
- });
371
- test.serial('stores reasoning_details/reasoning on message output items for future turns', async (t) => {
372
- globalThis.fetch = async (_url, options) => {
373
- JSON.parse(options.body);
374
- return createJsonResponse({
375
- id: 'resp-1',
376
- choices: [
377
- {
378
- message: {
379
- content: 'Hello back',
380
- reasoning: 'Some thinking',
381
- reasoning_details: [
382
- {
383
- type: 'reasoning.text',
384
- text: 'step1',
385
- format: null,
386
- index: 0,
387
- },
388
- ],
389
- },
390
- },
391
- ],
392
- usage: {
393
- prompt_tokens: 1,
394
- completion_tokens: 2,
395
- total_tokens: 3,
396
- },
397
- });
398
- };
399
- const model = new OpenRouterModel({
400
- settingsService: mockSettingsService,
401
- loggingService: logger,
402
- modelId: 'mock-model',
403
- });
404
- const resp = await model.getResponse({
405
- systemInstructions: 'system message',
406
- input: 'hi',
407
- });
408
- const msg = resp.output.find((o) => o.type === 'message');
409
- t.truthy(msg);
410
- t.is(getContentText(msg), 'Hello back');
411
- t.is(msg?.reasoning, 'Some thinking');
412
- t.deepEqual(msg?.reasoning_details, [{ type: 'reasoning.text', text: 'step1', format: null, index: 0 }]);
413
- });
414
- test.serial('passes through full reasoning request object (max_tokens/exclude) even without reasoningEffort', async (t) => {
415
- const requests = [];
416
- globalThis.fetch = async (_url, options) => {
417
- const body = JSON.parse(options.body);
418
- requests.push(body);
419
- return createJsonResponse({
420
- id: 'resp-reasoning-obj',
421
- choices: [{ message: { content: 'ok' } }],
422
- usage: {},
423
- });
424
- };
425
- const model = new OpenRouterModel({
426
- settingsService: mockSettingsService,
427
- loggingService: logger,
428
- modelId: 'mock-model',
429
- });
430
- await model.getResponse({
431
- systemInstructions: 'system message',
432
- input: 'hi',
433
- modelSettings: {
434
- reasoning: {
435
- max_tokens: 2000,
436
- exclude: false,
437
- },
438
- },
439
- });
440
- t.truthy(requests[0]);
441
- t.deepEqual(requests[0].reasoning, { max_tokens: 2000, exclude: false });
442
- });
443
- test.serial('streams reasoning details and stores assistant history', async (t) => {
444
- const requests = [];
445
- const encoder = new TextEncoder();
446
- const stream = new ReadableStream({
447
- start(controller) {
448
- controller.enqueue(encoder.encode('data: {"id":"resp-stream","choices":[{"delta":{"content":"Hello","reasoning_details":[{"type":"reasoning.text","text":"step1","format":null,"index":0}]}}]}\n'));
449
- controller.enqueue(encoder.encode('data: {"choices":[{"delta":{"content":"!"}}]}\n'));
450
- controller.enqueue(encoder.encode('data: [DONE]\n'));
451
- controller.close();
452
- },
453
- });
454
- const responses = [
455
- new Response(stream, {
456
- status: 200,
457
- headers: { 'Content-Type': 'text/event-stream' },
458
- }),
459
- createJsonResponse({
460
- id: 'resp-stream-2',
461
- choices: [{ message: { content: 'Follow up' } }],
462
- usage: {},
463
- }),
464
- ];
465
- let call = 0;
466
- globalThis.fetch = async (_url, options) => {
467
- const body = JSON.parse(options.body);
468
- requests.push(body);
469
- return responses[call++];
470
- };
471
- const model = new OpenRouterModel({
472
- settingsService: mockSettingsService,
473
- loggingService: logger,
474
- modelId: 'mock-model',
475
- });
476
- const streamedEvents = [];
477
- for await (const event of model.getStreamedResponse({
478
- systemInstructions: 'system message',
479
- input: 'Streaming input',
480
- })) {
481
- streamedEvents.push(event);
482
- }
483
- const doneEvent = streamedEvents.find((event) => event.type === 'response_done');
484
- t.truthy(doneEvent);
485
- // First output item should be reasoning
486
- const reasoningOutput = doneEvent.response.output[0];
487
- t.is(reasoningOutput.type, 'reasoning');
488
- t.is(reasoningOutput.content[0].text, 'step1');
489
- t.is(reasoningOutput.providerData.type, 'reasoning.text');
490
- // Second output item should be the message
491
- t.is(doneEvent.response.output[1].content[0].text, 'Hello!');
492
- });
493
- test.serial('converts user message items in array inputs', async (t) => {
494
- const requests = [];
495
- globalThis.fetch = async (_url, options) => {
496
- const body = JSON.parse(options.body);
497
- requests.push(body);
498
- return new Response(JSON.stringify({ choices: [{ message: { content: 'ok' } }], usage: {} }), {
499
- status: 200,
500
- headers: { 'Content-Type': 'application/json' },
501
- });
502
- };
503
- const model = new OpenRouterModel({
504
- settingsService: mockSettingsService,
505
- loggingService: logger,
506
- modelId: 'mock-model',
507
- });
508
- await model.getResponse({
509
- systemInstructions: 'system message',
510
- input: [{ type: 'message', role: 'user', content: 'hi' }],
511
- });
512
- t.truthy(requests[0]);
513
- t.deepEqual(requests[0].messages, [
514
- { role: 'system', content: 'system message' },
515
- { role: 'user', content: 'hi' },
516
- ]);
517
- });
518
- test.serial('handles summary reasoning details', async (t) => {
519
- const requests = [];
520
- globalThis.fetch = async (_url, options) => {
521
- const body = JSON.parse(options.body);
522
- requests.push(body);
523
- return createJsonResponse({
524
- id: 'resp-summary',
525
- choices: [
526
- {
527
- message: {
528
- content: '1 + 1 equals 2.\n\nThis is the fundamental rule of addition in basic arithmetic, where adding 1 to 1 results in 2. For example, if you have one apple and get one more, you have two apples.',
529
- reasoning: 'First, the user asked "What is 1 + 1", which is a simple math question.\n',
530
- reasoning_details: [
531
- {
532
- format: 'xai-responses-v1',
533
- index: 0,
534
- type: 'reasoning.summary',
535
- summary: 'First, the user asked "What is 1 + 1", which is a simple math question.\n',
536
- },
537
- ],
538
- },
539
- },
540
- ],
541
- usage: { prompt_tokens: 10, completion_tokens: 50, total_tokens: 60 },
542
- });
543
- };
544
- const model = new OpenRouterModel({
545
- settingsService: mockSettingsService,
546
- loggingService: logger,
547
- modelId: 'mock-model',
548
- });
549
- const response = await model.getResponse({
550
- systemInstructions: 'system message',
551
- input: 'What is 1 + 1',
552
- });
553
- // Should have two output items: reasoning and message
554
- t.is(response.output.length, 2);
555
- // First item should be summary reasoning
556
- const reasoningOutput = response.output[0];
557
- t.is(reasoningOutput.type, 'reasoning');
558
- t.is(reasoningOutput.content.length, 1);
559
- t.is(reasoningOutput.content[0].type, 'input_text');
560
- t.is(reasoningOutput.content[0].text, 'First, the user asked "What is 1 + 1", which is a simple math question.\n');
561
- t.is(reasoningOutput.providerData?.type, 'reasoning.summary');
562
- t.is(reasoningOutput.providerData?.format, 'xai-responses-v1');
563
- t.is(reasoningOutput.providerData?.summary, 'First, the user asked "What is 1 + 1", which is a simple math question.\n');
564
- // Second item should be the message
565
- const messageOutput = response.output[1];
566
- t.is(messageOutput.type, 'message');
567
- t.truthy(getContentText(messageOutput)?.includes('1 + 1 equals 2'));
568
- });
569
- test.serial('handles encrypted reasoning details', async (t) => {
570
- const requests = [];
571
- globalThis.fetch = async (_url, options) => {
572
- const body = JSON.parse(options.body);
573
- requests.push(body);
574
- return createJsonResponse({
575
- id: 'resp-encrypted',
576
- choices: [
577
- {
578
- message: {
579
- content: 'Response with encrypted reasoning',
580
- reasoning_details: [
581
- {
582
- id: 'rs_05c9be144fb3fa3b016936cb941e48819183e63b141a094cac',
583
- format: 'openai-responses-v1',
584
- index: 0,
585
- type: 'reasoning.encrypted',
586
- data: 'gAAAAABpNsuVfXBOvLwub1...',
587
- },
588
- ],
589
- },
590
- },
591
- ],
592
- usage: { prompt_tokens: 10, completion_tokens: 20, total_tokens: 30 },
593
- });
594
- };
595
- const model = new OpenRouterModel({
596
- settingsService: mockSettingsService,
597
- loggingService: logger,
598
- modelId: 'mock-model',
599
- });
600
- const response = await model.getResponse({
601
- systemInstructions: 'system message',
602
- input: 'Test encrypted reasoning',
603
- });
604
- // Should have two output items: reasoning and message
605
- t.is(response.output.length, 2);
606
- // First item should be encrypted reasoning
607
- const reasoningOutput = response.output[0];
608
- t.is(reasoningOutput.type, 'reasoning');
609
- t.deepEqual(reasoningOutput.content, []); // No displayable content for encrypted
610
- t.is(reasoningOutput.providerData?.type, 'reasoning.encrypted');
611
- t.is(reasoningOutput.providerData?.id, 'rs_05c9be144fb3fa3b016936cb941e48819183e63b141a094cac');
612
- t.is(reasoningOutput.providerData?.format, 'openai-responses-v1');
613
- t.is(reasoningOutput.providerData?.data, 'gAAAAABpNsuVfXBOvLwub1...');
614
- // Second item should be the message
615
- const messageOutput = response.output[1];
616
- t.is(messageOutput.type, 'message');
617
- t.is(getContentText(messageOutput), 'Response with encrypted reasoning');
618
- });
619
- test.serial('adds fallback assistant message when response has empty content', async (t) => {
620
- globalThis.fetch = async (_url, options) => {
621
- const body = JSON.parse(options.body);
622
- t.truthy(body);
623
- return createJsonResponse({
624
- id: 'resp-empty-content',
625
- choices: [
626
- {
627
- message: {
628
- content: '',
629
- },
630
- },
631
- ],
632
- usage: {
633
- prompt_tokens: 1,
634
- completion_tokens: 0,
635
- total_tokens: 1,
636
- },
637
- });
638
- };
639
- const model = new OpenRouterModel({
640
- settingsService: mockSettingsService,
641
- loggingService: logger,
642
- modelId: 'mock-model',
643
- });
644
- const response = await model.getResponse({
645
- systemInstructions: 'system message',
646
- input: 'Hello?',
647
- });
648
- t.is(response.output.length, 1);
649
- const messageOutput = response.output[0];
650
- t.is(messageOutput.type, 'message');
651
- t.is(getContentText(messageOutput), 'No response from model.');
652
- });
653
- test.serial('correctly converts function_call_output to tool message', async (t) => {
654
- const requests = [];
655
- globalThis.fetch = async (_url, options) => {
656
- const body = JSON.parse(options.body);
657
- requests.push(body);
658
- return createJsonResponse({
659
- id: 'resp-1',
660
- choices: [{ message: { content: 'ok' } }],
661
- usage: {
662
- prompt_tokens: 1,
663
- completion_tokens: 1,
664
- total_tokens: 2,
665
- },
666
- });
667
- };
668
- const model = new OpenRouterModel({
669
- settingsService: mockSettingsService,
670
- loggingService: logger,
671
- modelId: 'mock-model',
672
- });
673
- // Simulate a conversation turn with function_call_output
674
- const inputItems = [
675
- { type: 'input_text', text: 'User message' },
676
- {
677
- type: 'function_call',
678
- id: 'call-abc',
679
- name: 'search_replace',
680
- arguments: '{"path":"test.txt","search_content":"old","replace_content":"new"}',
681
- },
682
- {
683
- rawItem: {
684
- type: 'function_call_output',
685
- callId: 'call-abc',
686
- id: 'call-abc',
687
- name: 'search_replace',
688
- output: '{"output":[{"success":true}]}',
689
- },
690
- },
691
- ];
692
- await model.getResponse({
693
- systemInstructions: 'system message',
694
- input: inputItems,
695
- });
696
- t.truthy(requests[0]);
697
- const messages = requests[0].messages;
698
- // Should have: system, user, assistant (with tool_calls), tool (with result)
699
- t.is(messages.length, 4);
700
- t.is(messages[0].role, 'system');
701
- t.is(messages[1].role, 'user');
702
- t.is(messages[1].content, 'User message');
703
- // function_call should be converted to assistant message with tool_calls
704
- t.is(messages[2].role, 'assistant');
705
- t.is(messages[2].content, null);
706
- t.truthy(messages[2].tool_calls);
707
- t.is(messages[2].tool_calls[0].id, 'call-abc');
708
- t.is(messages[2].tool_calls[0].function.name, 'search_replace');
709
- // function_call_output should be converted to tool message, NOT another assistant message
710
- t.is(messages[3].role, 'tool');
711
- t.is(messages[3].tool_call_id, 'call-abc');
712
- t.truthy(messages[3].content);
713
- });
714
- test.serial('handles mixed reasoning types (summary + encrypted)', async (t) => {
715
- const requests = [];
716
- globalThis.fetch = async (_url, options) => {
717
- const body = JSON.parse(options.body);
718
- requests.push(body);
719
- return createJsonResponse({
720
- id: 'resp-mixed',
721
- choices: [
722
- {
723
- message: {
724
- content: '1 + 1 equals 2.\n\nThis is the fundamental rule of addition.',
725
- reasoning: 'First, the user asked "What is 1 + 1", which is a simple math question.\n',
726
- reasoning_details: [
727
- {
728
- format: 'xai-responses-v1',
729
- index: 0,
730
- type: 'reasoning.summary',
731
- summary: 'First, the user asked "What is 1 + 1", which is a simple math question.\n',
732
- },
733
- {
734
- id: 'rs_b908e80d-7948-923b-a82b-0a61eba37a92',
735
- format: 'xai-responses-v1',
736
- index: 1,
737
- type: 'reasoning.encrypted',
738
- data: 'zDzBlL2u7sDbq1l6rOpNL',
739
- },
740
- ],
741
- },
742
- },
743
- ],
744
- usage: { prompt_tokens: 10, completion_tokens: 50, total_tokens: 60 },
745
- });
746
- };
747
- const model = new OpenRouterModel({
748
- settingsService: mockSettingsService,
749
- loggingService: logger,
750
- modelId: 'mock-model',
751
- });
752
- const response = await model.getResponse({
753
- systemInstructions: 'system message',
754
- input: 'What is 1 + 1',
755
- });
756
- // Should have three output items: summary reasoning, encrypted reasoning, and message
757
- t.is(response.output.length, 3);
758
- // First item should be summary reasoning
759
- const summaryOutput = response.output[0];
760
- t.is(summaryOutput.type, 'reasoning');
761
- t.is(summaryOutput.content[0].text, 'First, the user asked "What is 1 + 1", which is a simple math question.\n');
762
- t.is(summaryOutput.providerData?.type, 'reasoning.summary');
763
- // Second item should be encrypted reasoning
764
- const encryptedOutput = response.output[1];
765
- t.is(encryptedOutput.type, 'reasoning');
766
- t.deepEqual(encryptedOutput.content, []); // No displayable content
767
- t.is(encryptedOutput.providerData?.type, 'reasoning.encrypted');
768
- t.is(encryptedOutput.providerData?.data, 'zDzBlL2u7sDbq1l6rOpNL');
769
- // Third item should be the message
770
- const messageOutput = response.output[2];
771
- t.is(messageOutput.type, 'message');
772
- t.truthy(getContentText(messageOutput)?.includes('1 + 1 equals 2'));
773
- });
774
- test.serial('applies cache_control to last tool message for Anthropic models', async (t) => {
775
- const requests = [];
776
- globalThis.fetch = async (_url, options) => {
777
- const body = JSON.parse(options.body);
778
- requests.push(body);
779
- return createJsonResponse({
780
- id: 'resp-1',
781
- choices: [{ message: { content: 'ok' } }],
782
- usage: {},
783
- });
784
- };
785
- const model = new OpenRouterModel({
786
- settingsService: mockSettingsService,
787
- loggingService: logger,
788
- modelId: 'anthropic/claude-3.5-sonnet',
789
- });
790
- const inputItems = [
791
- { type: 'input_text', text: 'User message' },
792
- {
793
- type: 'function_call',
794
- id: 'call-1',
795
- name: 'tool1',
796
- arguments: '{}',
797
- },
798
- {
799
- type: 'function_call_result',
800
- callId: 'call-1',
801
- output: 'result1',
802
- },
803
- {
804
- type: 'function_call',
805
- id: 'call-2',
806
- name: 'tool2',
807
- arguments: '{}',
808
- },
809
- {
810
- type: 'function_call_result',
811
- callId: 'call-2',
812
- output: 'result2',
813
- },
814
- ];
815
- await model.getResponse({
816
- systemInstructions: 'system',
817
- input: inputItems,
818
- });
819
- t.truthy(requests[0]);
820
- const messages = requests[0].messages;
821
- // Verify last tool message has cache_control
822
- const lastToolMsg = messages[messages.length - 1];
823
- t.is(lastToolMsg.role, 'tool');
824
- t.is(lastToolMsg.tool_call_id, 'call-2');
825
- t.true(Array.isArray(lastToolMsg.content));
826
- t.is(lastToolMsg.content[0].text, 'result2');
827
- t.deepEqual(lastToolMsg.content[0].cache_control, { type: 'ephemeral' });
828
- // Verify previous tool message does NOT have cache_control
829
- const firstToolMsg = messages.find((m) => m.tool_call_id === 'call-1');
830
- t.truthy(firstToolMsg);
831
- t.is(typeof firstToolMsg.content, 'string');
832
- t.is(firstToolMsg.content, 'result1');
833
- });
834
- test.serial('preserves assistant message content when tool calls are present', async (t) => {
835
- const requests = [];
836
- globalThis.fetch = async (_url, options) => {
837
- const body = JSON.parse(options.body);
838
- requests.push(body);
839
- return createJsonResponse({
840
- id: 'resp-with-tools',
841
- choices: [{ message: { content: 'Response after tools' } }],
842
- usage: {
843
- prompt_tokens: 5,
844
- completion_tokens: 3,
845
- total_tokens: 8,
846
- },
847
- });
848
- };
849
- const model = new OpenRouterModel({
850
- settingsService: mockSettingsService,
851
- loggingService: logger,
852
- modelId: 'mock-model',
853
- });
854
- // First turn - get initial response
855
- await model.getResponse({
856
- systemInstructions: 'system message',
857
- input: 'First turn',
858
- });
859
- // Second turn - assistant message with both content and tool calls
860
- await model.getResponse({
861
- systemInstructions: 'system message',
862
- input: [
863
- { type: 'input_text', text: 'User message' },
864
- {
865
- type: 'message',
866
- role: 'assistant',
867
- content: [{ type: 'output_text', text: 'I will use a tool' }],
868
- tool_calls: [
869
- {
870
- id: 'call-456',
871
- type: 'function',
872
- function: {
873
- name: 'search',
874
- arguments: '{"query":"test"}',
875
- },
876
- },
877
- ],
878
- },
879
- {
880
- type: 'function_call_result',
881
- callId: 'call-456',
882
- output: 'search results',
883
- },
884
- ],
885
- });
886
- const secondRequest = requests[1];
887
- t.truthy(secondRequest);
888
- // Find the assistant message with tool calls
889
- const assistantMsg = secondRequest.messages.find((m) => m.role === 'assistant' && m.tool_calls);
890
- t.truthy(assistantMsg);
891
- // Verify both content and tool_calls are preserved
892
- t.is(assistantMsg.content, 'I will use a tool');
893
- t.truthy(assistantMsg.tool_calls);
894
- t.is(assistantMsg.tool_calls.length, 1);
895
- t.is(assistantMsg.tool_calls[0].id, 'call-456');
896
- t.is(assistantMsg.tool_calls[0].function.name, 'search');
897
- });
898
- test.serial('trims tool call names in non-streaming responses', async (t) => {
899
- globalThis.fetch = async () => createJsonResponse({
900
- id: 'resp-tools-trim',
901
- choices: [
902
- {
903
- message: {
904
- content: '',
905
- tool_calls: [
906
- {
907
- id: 'call-trim-1',
908
- type: 'function',
909
- function: {
910
- name: ' shell ',
911
- arguments: '{"command":"ls"}',
912
- },
913
- },
914
- ],
915
- },
916
- },
917
- ],
918
- usage: {},
919
- });
920
- const model = new OpenRouterModel({
921
- settingsService: mockSettingsService,
922
- loggingService: logger,
923
- modelId: 'mock-model',
924
- });
925
- const response = await model.getResponse({
926
- input: 'list files',
927
- });
928
- const toolCall = response.output.find((item) => item.type === 'function_call');
929
- t.truthy(toolCall);
930
- t.is(toolCall.name, 'shell');
931
- });
932
- test.serial('trims tool call names in streamed responses', async (t) => {
933
- const encoder = new TextEncoder();
934
- const stream = new ReadableStream({
935
- start(controller) {
936
- controller.enqueue(encoder.encode('data: {"id":"resp-tools-stream","choices":[{"delta":{"tool_calls":[{"index":0,"id":"call-stream-1","type":"function","function":{"name":" shell ","arguments":"{\\"command\\":\\"ls\\"}"}}]}}]}\n'));
937
- controller.enqueue(encoder.encode('data: [DONE]\n'));
938
- controller.close();
939
- },
940
- });
941
- globalThis.fetch = async () => new Response(stream, {
942
- status: 200,
943
- headers: { 'Content-Type': 'text/event-stream' },
944
- });
945
- const model = new OpenRouterModel({
946
- settingsService: mockSettingsService,
947
- loggingService: logger,
948
- modelId: 'mock-model',
949
- });
950
- const events = [];
951
- for await (const event of model.getStreamedResponse({
952
- input: 'list files',
953
- })) {
954
- events.push(event);
955
- }
956
- const doneEvent = events.find((event) => event.type === 'response_done');
957
- t.truthy(doneEvent);
958
- const toolCall = doneEvent.response.output.find((item) => item.type === 'function_call');
959
- t.truthy(toolCall);
960
- t.is(toolCall.name, 'shell');
961
- });
962
- // ========== Error Recovery Tests: OpenRouterError and Retry Classification ==========
963
- test('OpenRouterError includes status and headers', (t) => {
964
- const error = new OpenRouterError('Test error', 429, { 'retry-after': '5', 'x-custom': 'value' }, 'response body');
965
- t.is(error.message, 'Test error');
966
- t.is(error.status, 429);
967
- t.deepEqual(error.headers, { 'retry-after': '5', 'x-custom': 'value' });
968
- t.is(error.responseBody, 'response body');
969
- t.is(error.name, 'OpenRouterError');
970
- });
971
- test('OpenRouterError is throwable', (t) => {
972
- const fn = () => {
973
- throw new OpenRouterError('Error message', 500, {});
974
- };
975
- const error = t.throws(fn);
976
- t.true(error instanceof OpenRouterError);
977
- if (error instanceof OpenRouterError) {
978
- t.is(error.status, 500);
979
- }
980
- });
981
- test('OpenRouterError can be created with minimal arguments', (t) => {
982
- const error = new OpenRouterError('Minimal error', 400, {});
983
- t.is(error.message, 'Minimal error');
984
- t.is(error.status, 400);
985
- t.deepEqual(error.headers, {});
986
- t.is(error.responseBody, undefined);
987
- });
988
- test('OpenRouterError preserves response body', (t) => {
989
- const responseBody = '{"error": "Rate limit exceeded"}';
990
- const error = new OpenRouterError('Rate limited', 429, {}, responseBody);
991
- t.is(error.responseBody, responseBody);
992
- });
993
- test('OpenRouterError with 429 status for retry logic', (t) => {
994
- const error = new OpenRouterError('Too many requests', 429, {
995
- 'retry-after': '60',
996
- });
997
- t.is(error.status, 429);
998
- t.is(error.headers['retry-after'], '60');
999
- });
1000
- test('OpenRouterError with 5xx status codes for retry logic', (t) => {
1001
- const statuses = [500, 502, 503, 504];
1002
- for (const status of statuses) {
1003
- const error = new OpenRouterError(`Server error ${status}`, status, {});
1004
- t.is(error.status, status);
1005
- t.true(error.status >= 500);
1006
- }
1007
- });
1008
- test('OpenRouterError with 4xx status codes (non-retryable)', (t) => {
1009
- const statuses = [400, 401, 403, 404, 422];
1010
- for (const status of statuses) {
1011
- const error = new OpenRouterError(`Client error ${status}`, status, {});
1012
- t.is(error.status, status);
1013
- t.true(error.status >= 400 && error.status < 500);
1014
- t.not(error.status, 429); // 429 is retryable
1015
- }
1016
- });
1017
- test('OpenRouterError with headers for Retry-After extraction', (t) => {
1018
- const headers = {
1019
- 'retry-after': '120',
1020
- 'x-ratelimit-reset': '1234567890',
1021
- 'content-type': 'application/json',
1022
- };
1023
- const error = new OpenRouterError('Rate limit', 429, headers);
1024
- t.is(error.headers['retry-after'], '120');
1025
- t.is(error.headers['x-ratelimit-reset'], '1234567890');
1026
- t.is(error.headers['content-type'], 'application/json');
1027
- });
1028
- test('OpenRouterError error instanceof checks', (t) => {
1029
- const error = new OpenRouterError('Test', 500, {});
1030
- t.true(error instanceof Error);
1031
- t.true(error instanceof OpenRouterError);
1032
- });
1033
- test('OpenRouterError with empty headers', (t) => {
1034
- const error = new OpenRouterError('No headers', 503, {});
1035
- t.deepEqual(error.headers, {});
1036
- t.is(Object.keys(error.headers).length, 0);
1037
- });
1038
- // Anthropic prompt caching tests
1039
- test.serial('applies cache_control to system message for Anthropic models', async (t) => {
1040
- const requests = [];
1041
- globalThis.fetch = async (_url, options) => {
1042
- const body = JSON.parse(options.body);
1043
- requests.push(body);
1044
- return createJsonResponse({
1045
- id: 'resp-1',
1046
- choices: [{ message: { content: 'Hello' } }],
1047
- usage: {
1048
- prompt_tokens: 10,
1049
- completion_tokens: 5,
1050
- total_tokens: 15,
1051
- },
1052
- });
1053
- };
1054
- const model = new OpenRouterModel({
1055
- settingsService: mockSettingsService,
1056
- loggingService: logger,
1057
- modelId: 'anthropic/claude-3.5-sonnet',
1058
- });
1059
- await model.getResponse({
1060
- systemInstructions: 'You are a helpful assistant.',
1061
- input: 'Hello',
1062
- });
1063
- t.truthy(requests[0]);
1064
- t.deepEqual(requests[0].messages[0], {
1065
- role: 'system',
1066
- content: [
1067
- {
1068
- type: 'text',
1069
- text: 'You are a helpful assistant.',
1070
- cache_control: { type: 'ephemeral' },
1071
- },
1072
- ],
1073
- });
1074
- });
1075
- test.serial('applies cache_control for Claude model variants', async (t) => {
1076
- const requests = [];
1077
- globalThis.fetch = async (_url, options) => {
1078
- const body = JSON.parse(options.body);
1079
- requests.push(body);
1080
- return createJsonResponse({
1081
- id: 'resp-1',
1082
- choices: [{ message: { content: 'Hello' } }],
1083
- usage: {},
1084
- });
1085
- };
1086
- // Test with different Claude model IDs
1087
- const claudeModels = [
1088
- 'anthropic/claude-3-opus',
1089
- 'anthropic/claude-3.5-sonnet',
1090
- 'anthropic/claude-3-haiku',
1091
- 'openrouter/claude-instant',
1092
- ];
1093
- for (const modelId of claudeModels) {
1094
- requests.length = 0;
1095
- const model = new OpenRouterModel({
1096
- settingsService: mockSettingsService,
1097
- loggingService: logger,
1098
- modelId,
1099
- });
1100
- await model.getResponse({
1101
- systemInstructions: 'System prompt',
1102
- input: 'Test',
1103
- });
1104
- t.truthy(requests[0], `Failed for model: ${modelId}`);
1105
- t.is(requests[0].messages[0].role, 'system');
1106
- t.true(Array.isArray(requests[0].messages[0].content), `Content should be array for ${modelId}`);
1107
- t.deepEqual(requests[0].messages[0].content[0].cache_control, { type: 'ephemeral' }, `cache_control should be set for ${modelId}`);
1108
- }
1109
- });
1110
- test.serial('does not apply cache_control for non-Anthropic models', async (t) => {
1111
- const requests = [];
1112
- globalThis.fetch = async (_url, options) => {
1113
- const body = JSON.parse(options.body);
1114
- requests.push(body);
1115
- return createJsonResponse({
1116
- id: 'resp-1',
1117
- choices: [{ message: { content: 'Hello' } }],
1118
- usage: {},
1119
- });
1120
- };
1121
- // Test with non-Anthropic models
1122
- const nonAnthropicModels = [
1123
- 'openai/gpt-4o',
1124
- 'google/gemini-pro',
1125
- 'meta-llama/llama-3-70b',
1126
- 'mistralai/mistral-large',
1127
- 'openrouter/auto',
1128
- ];
1129
- for (const modelId of nonAnthropicModels) {
1130
- requests.length = 0;
1131
- const model = new OpenRouterModel({
1132
- settingsService: mockSettingsService,
1133
- loggingService: logger,
1134
- modelId,
1135
- });
1136
- await model.getResponse({
1137
- systemInstructions: 'System prompt',
1138
- input: 'Test',
1139
- });
1140
- t.truthy(requests[0], `Failed for model: ${modelId}`);
1141
- t.deepEqual(requests[0].messages[0], { role: 'system', content: 'System prompt' }, `System message should be plain string for ${modelId}`);
1142
- }
1143
- });
1144
- test.serial('applies cache_control in streamed response for Anthropic models', async (t) => {
1145
- const requests = [];
1146
- // Create a simple SSE stream response
1147
- const sseData = [
1148
- 'data: {"id":"resp-1","choices":[{"delta":{"content":"Hi"}}]}\n\n',
1149
- 'data: {"id":"resp-1","choices":[{"delta":{"content":"!"}}],"usage":{"prompt_tokens":10,"completion_tokens":2}}\n\n',
1150
- 'data: [DONE]\n\n',
1151
- ].join('');
1152
- globalThis.fetch = async (_url, options) => {
1153
- const body = JSON.parse(options.body);
1154
- requests.push(body);
1155
- const stream = new ReadableStream({
1156
- start(controller) {
1157
- controller.enqueue(new TextEncoder().encode(sseData));
1158
- controller.close();
1159
- },
1160
- });
1161
- return new Response(stream, {
1162
- status: 200,
1163
- headers: { 'Content-Type': 'text/event-stream' },
1164
- });
1165
- };
1166
- const model = new OpenRouterModel({
1167
- settingsService: mockSettingsService,
1168
- loggingService: logger,
1169
- modelId: 'anthropic/claude-3.5-sonnet',
1170
- });
1171
- // Consume the stream
1172
- for await (const _event of model.getStreamedResponse({
1173
- systemInstructions: 'You are helpful.',
1174
- input: 'Hi',
1175
- })) {
1176
- // Just consume the events
1177
- }
1178
- t.truthy(requests[0]);
1179
- t.deepEqual(requests[0].messages[0], {
1180
- role: 'system',
1181
- content: [
1182
- {
1183
- type: 'text',
1184
- text: 'You are helpful.',
1185
- cache_control: { type: 'ephemeral' },
1186
- },
1187
- ],
1188
- });
1189
- });
1190
- // Tests for last user message caching (using 2 of 4 cache points efficiently)
1191
- test.serial('applies cache_control to last user message for Anthropic models (string content)', async (t) => {
1192
- const requests = [];
1193
- globalThis.fetch = async (_url, options) => {
1194
- const body = JSON.parse(options.body);
1195
- requests.push(body);
1196
- return createJsonResponse({
1197
- id: 'resp-1',
1198
- choices: [{ message: { content: 'Hello' } }],
1199
- usage: {
1200
- prompt_tokens: 10,
1201
- completion_tokens: 5,
1202
- total_tokens: 15,
1203
- },
1204
- });
1205
- };
1206
- const model = new OpenRouterModel({
1207
- settingsService: mockSettingsService,
1208
- loggingService: logger,
1209
- modelId: 'anthropic/claude-3.5-sonnet',
1210
- });
1211
- await model.getResponse({
1212
- systemInstructions: 'You are a helpful assistant.',
1213
- input: 'Hello world',
1214
- });
1215
- t.truthy(requests[0]);
1216
- // Verify system message has cache_control
1217
- t.deepEqual(requests[0].messages[0], {
1218
- role: 'system',
1219
- content: [
1220
- {
1221
- type: 'text',
1222
- text: 'You are a helpful assistant.',
1223
- cache_control: { type: 'ephemeral' },
1224
- },
1225
- ],
1226
- });
1227
- // Verify last user message has cache_control (converted from string to array)
1228
- t.deepEqual(requests[0].messages[1], {
1229
- role: 'user',
1230
- content: [
1231
- {
1232
- type: 'text',
1233
- text: 'Hello world',
1234
- cache_control: { type: 'ephemeral' },
1235
- },
1236
- ],
1237
- });
1238
- });
1239
- test.serial('applies cache_control to last user message in multi-turn conversation', async (t) => {
1240
- const requests = [];
1241
- globalThis.fetch = async (_url, options) => {
1242
- const body = JSON.parse(options.body);
1243
- requests.push(body);
1244
- return createJsonResponse({
1245
- id: 'resp-1',
1246
- choices: [{ message: { content: 'Response' } }],
1247
- usage: {},
1248
- });
1249
- };
1250
- const model = new OpenRouterModel({
1251
- settingsService: mockSettingsService,
1252
- loggingService: logger,
1253
- modelId: 'anthropic/claude-3-opus',
1254
- });
1255
- // Simulate a multi-turn conversation
1256
- const conversationItems = [
1257
- { type: 'message', role: 'user', content: 'First question' },
1258
- {
1259
- type: 'message',
1260
- role: 'assistant',
1261
- content: [{ type: 'output_text', text: 'First answer' }],
1262
- status: 'completed',
1263
- },
1264
- { type: 'message', role: 'user', content: 'Second question' },
1265
- {
1266
- type: 'message',
1267
- role: 'assistant',
1268
- content: [{ type: 'output_text', text: 'Second answer' }],
1269
- status: 'completed',
1270
- },
1271
- { type: 'input_text', text: 'Third question' }, // Latest user input
1272
- ];
1273
- await model.getResponse({
1274
- systemInstructions: 'System prompt',
1275
- input: conversationItems,
1276
- });
1277
- t.truthy(requests[0]);
1278
- const messages = requests[0].messages;
1279
- // Find all user messages
1280
- const userMessages = messages.filter((m) => m.role === 'user');
1281
- t.is(userMessages.length, 3, 'Should have 3 user messages');
1282
- // First two user messages should NOT have cache_control (plain string)
1283
- t.is(userMessages[0].content, 'First question', 'First user message should be plain string');
1284
- t.is(userMessages[1].content, 'Second question', 'Second user message should be plain string');
1285
- // Only the LAST user message should have cache_control (array format)
1286
- t.deepEqual(userMessages[2], {
1287
- role: 'user',
1288
- content: [
1289
- {
1290
- type: 'text',
1291
- text: 'Third question',
1292
- cache_control: { type: 'ephemeral' },
1293
- },
1294
- ],
1295
- }, 'Last user message should have cache_control');
1296
- });
1297
- test.serial('does not apply cache_control to user messages for non-Anthropic models', async (t) => {
1298
- const requests = [];
1299
- globalThis.fetch = async (_url, options) => {
1300
- const body = JSON.parse(options.body);
1301
- requests.push(body);
1302
- return createJsonResponse({
1303
- id: 'resp-1',
1304
- choices: [{ message: { content: 'Hello' } }],
1305
- usage: {},
1306
- });
1307
- };
1308
- const model = new OpenRouterModel({
1309
- settingsService: mockSettingsService,
1310
- loggingService: logger,
1311
- modelId: 'openai/gpt-4o',
1312
- });
1313
- await model.getResponse({
1314
- systemInstructions: 'System prompt',
1315
- input: 'Hello',
1316
- });
1317
- t.truthy(requests[0]);
1318
- // System message should be plain string for non-Anthropic
1319
- t.deepEqual(requests[0].messages[0], {
1320
- role: 'system',
1321
- content: 'System prompt',
1322
- });
1323
- // User message should also be plain string
1324
- t.deepEqual(requests[0].messages[1], { role: 'user', content: 'Hello' });
1325
- });
1326
- test.serial('handles conversation with no user messages gracefully for Anthropic models', async (t) => {
1327
- const requests = [];
1328
- globalThis.fetch = async (_url, options) => {
1329
- const body = JSON.parse(options.body);
1330
- requests.push(body);
1331
- return createJsonResponse({
1332
- id: 'resp-1',
1333
- choices: [{ message: { content: 'Hello' } }],
1334
- usage: {},
1335
- });
1336
- };
1337
- const model = new OpenRouterModel({
1338
- settingsService: mockSettingsService,
1339
- loggingService: logger,
1340
- modelId: 'anthropic/claude-3.5-sonnet',
1341
- });
1342
- // Pass empty input array (edge case)
1343
- await model.getResponse({
1344
- systemInstructions: 'System prompt',
1345
- input: [],
1346
- });
1347
- t.truthy(requests[0]);
1348
- // Should still have system message with cache_control
1349
- t.deepEqual(requests[0].messages[0], {
1350
- role: 'system',
1351
- content: [
1352
- {
1353
- type: 'text',
1354
- text: 'System prompt',
1355
- cache_control: { type: 'ephemeral' },
1356
- },
1357
- ],
1358
- });
1359
- // Should only have system message, no user messages
1360
- t.is(requests[0].messages.length, 1);
1361
- });
1362
- test.serial('both system and last user message have cache_control for Anthropic', async (t) => {
1363
- const requests = [];
1364
- globalThis.fetch = async (_url, options) => {
1365
- const body = JSON.parse(options.body);
1366
- requests.push(body);
1367
- return createJsonResponse({
1368
- id: 'resp-1',
1369
- choices: [{ message: { content: 'Response' } }],
1370
- usage: {},
1371
- });
1372
- };
1373
- const model = new OpenRouterModel({
1374
- settingsService: mockSettingsService,
1375
- loggingService: logger,
1376
- modelId: 'anthropic/claude-3.5-sonnet',
1377
- });
1378
- await model.getResponse({
1379
- systemInstructions: 'You are helpful.',
1380
- input: 'What is 2+2?',
1381
- });
1382
- t.truthy(requests[0]);
1383
- const messages = requests[0].messages;
1384
- // Verify we use exactly 2 cache points: system + last user
1385
- const systemMsg = messages[0];
1386
- const userMsg = messages[1];
1387
- t.is(systemMsg.role, 'system');
1388
- t.truthy(systemMsg.content[0].cache_control, 'System message should have cache_control');
1389
- t.deepEqual(systemMsg.content[0].cache_control, { type: 'ephemeral' });
1390
- t.is(userMsg.role, 'user');
1391
- t.truthy(userMsg.content[0].cache_control, 'Last user message should have cache_control');
1392
- t.deepEqual(userMsg.content[0].cache_control, { type: 'ephemeral' });
1393
- });
1394
- test.serial('applies cache_control to last user message in streamed response for Anthropic', async (t) => {
1395
- const requests = [];
1396
- const sseData = [
1397
- 'data: {"id":"resp-1","choices":[{"delta":{"content":"Hi"}}]}\n\n',
1398
- 'data: {"id":"resp-1","choices":[{"delta":{"content":"!"}}],"usage":{"prompt_tokens":10,"completion_tokens":2}}\n\n',
1399
- 'data: [DONE]\n\n',
1400
- ].join('');
1401
- globalThis.fetch = async (_url, options) => {
1402
- const body = JSON.parse(options.body);
1403
- requests.push(body);
1404
- const stream = new ReadableStream({
1405
- start(controller) {
1406
- controller.enqueue(new TextEncoder().encode(sseData));
1407
- controller.close();
1408
- },
1409
- });
1410
- return new Response(stream, {
1411
- status: 200,
1412
- headers: { 'Content-Type': 'text/event-stream' },
1413
- });
1414
- };
1415
- const model = new OpenRouterModel({
1416
- settingsService: mockSettingsService,
1417
- loggingService: logger,
1418
- modelId: 'anthropic/claude-3.5-sonnet',
1419
- });
1420
- for await (const _event of model.getStreamedResponse({
1421
- systemInstructions: 'You are helpful.',
1422
- input: 'Hi there',
1423
- })) {
1424
- // Consume stream
1425
- }
1426
- t.truthy(requests[0]);
1427
- // Verify both system and user message have cache_control in streamed response
1428
- t.deepEqual(requests[0].messages[0], {
1429
- role: 'system',
1430
- content: [
1431
- {
1432
- type: 'text',
1433
- text: 'You are helpful.',
1434
- cache_control: { type: 'ephemeral' },
1435
- },
1436
- ],
1437
- });
1438
- t.deepEqual(requests[0].messages[1], {
1439
- role: 'user',
1440
- content: [
1441
- {
1442
- type: 'text',
1443
- text: 'Hi there',
1444
- cache_control: { type: 'ephemeral' },
1445
- },
1446
- ],
1447
- });
1448
- });
1449
- //# sourceMappingURL=openrouter.test.js.map