@qduc/term2 0.6.0 → 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 (546) hide show
  1. package/dist/agent.d.ts +9 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +139 -68
  4. package/dist/agent.js.map +1 -1
  5. package/dist/agent.test.js +179 -16
  6. package/dist/agent.test.js.map +1 -1
  7. package/dist/app.d.ts +16 -3
  8. package/dist/app.d.ts.map +1 -1
  9. package/dist/app.js +186 -23
  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 +170 -71
  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.d.ts +2 -0
  127. package/dist/hooks/use-conversation.clear.test.d.ts.map +1 -0
  128. package/dist/hooks/use-conversation.clear.test.js +54 -0
  129. package/dist/hooks/use-conversation.clear.test.js.map +1 -0
  130. package/dist/hooks/use-conversation.d.ts +25 -2
  131. package/dist/hooks/use-conversation.d.ts.map +1 -1
  132. package/dist/hooks/use-conversation.js +90 -12
  133. package/dist/hooks/use-conversation.js.map +1 -1
  134. package/dist/hooks/use-escape-key.d.ts +8 -1
  135. package/dist/hooks/use-escape-key.d.ts.map +1 -1
  136. package/dist/hooks/use-escape-key.js +15 -4
  137. package/dist/hooks/use-escape-key.js.map +1 -1
  138. package/dist/hooks/use-escape-key.test.js +135 -28
  139. package/dist/hooks/use-escape-key.test.js.map +1 -1
  140. package/dist/hooks/use-input-history.test.js +14 -8
  141. package/dist/hooks/use-input-history.test.js.map +1 -1
  142. package/dist/hooks/use-mode-handlers.d.ts +12 -1
  143. package/dist/hooks/use-mode-handlers.d.ts.map +1 -1
  144. package/dist/hooks/use-mode-handlers.js +31 -1
  145. package/dist/hooks/use-mode-handlers.js.map +1 -1
  146. package/dist/hooks/use-model-selection.d.ts +6 -1
  147. package/dist/hooks/use-model-selection.d.ts.map +1 -1
  148. package/dist/hooks/use-model-selection.js +50 -8
  149. package/dist/hooks/use-model-selection.js.map +1 -1
  150. package/dist/hooks/use-model-selection.test.js +306 -50
  151. package/dist/hooks/use-model-selection.test.js.map +1 -1
  152. package/dist/hooks/use-path-completion.d.ts +4 -0
  153. package/dist/hooks/use-path-completion.d.ts.map +1 -1
  154. package/dist/hooks/use-path-completion.js +23 -17
  155. package/dist/hooks/use-path-completion.js.map +1 -1
  156. package/dist/hooks/use-runtime-settings.d.ts.map +1 -1
  157. package/dist/hooks/use-runtime-settings.js +7 -2
  158. package/dist/hooks/use-runtime-settings.js.map +1 -1
  159. package/dist/hooks/use-runtime-settings.test.d.ts +2 -0
  160. package/dist/hooks/use-runtime-settings.test.d.ts.map +1 -0
  161. package/dist/hooks/use-runtime-settings.test.js +45 -0
  162. package/dist/hooks/use-runtime-settings.test.js.map +1 -0
  163. package/dist/hooks/use-selection.d.ts +6 -2
  164. package/dist/hooks/use-selection.d.ts.map +1 -1
  165. package/dist/hooks/use-selection.js +53 -16
  166. package/dist/hooks/use-selection.js.map +1 -1
  167. package/dist/hooks/use-selection.test.d.ts +2 -0
  168. package/dist/hooks/use-selection.test.d.ts.map +1 -0
  169. package/dist/hooks/use-selection.test.js +132 -0
  170. package/dist/hooks/use-selection.test.js.map +1 -0
  171. package/dist/hooks/use-settings-completion.d.ts +19 -0
  172. package/dist/hooks/use-settings-completion.d.ts.map +1 -1
  173. package/dist/hooks/use-settings-completion.js +106 -18
  174. package/dist/hooks/use-settings-completion.js.map +1 -1
  175. package/dist/hooks/use-settings-completion.scroll.test.js +68 -29
  176. package/dist/hooks/use-settings-completion.scroll.test.js.map +1 -1
  177. package/dist/hooks/use-settings-completion.test.js +36 -1
  178. package/dist/hooks/use-settings-completion.test.js.map +1 -1
  179. package/dist/hooks/use-settings-value-completion.d.ts +7 -1
  180. package/dist/hooks/use-settings-value-completion.d.ts.map +1 -1
  181. package/dist/hooks/use-settings-value-completion.js +18 -7
  182. package/dist/hooks/use-settings-value-completion.js.map +1 -1
  183. package/dist/hooks/use-slash-commands.d.ts +5 -0
  184. package/dist/hooks/use-slash-commands.d.ts.map +1 -1
  185. package/dist/hooks/use-slash-commands.js +22 -2
  186. package/dist/hooks/use-slash-commands.js.map +1 -1
  187. package/dist/hooks/use-trigger-detection.test.js +9 -6
  188. package/dist/hooks/use-trigger-detection.test.js.map +1 -1
  189. package/dist/hooks/use-undo-selection.d.ts +4 -0
  190. package/dist/hooks/use-undo-selection.d.ts.map +1 -1
  191. package/dist/hooks/use-undo-selection.js +18 -1
  192. package/dist/hooks/use-undo-selection.js.map +1 -1
  193. package/dist/inspect_zod.d.ts +2 -0
  194. package/dist/inspect_zod.d.ts.map +1 -0
  195. package/dist/inspect_zod.js +16 -0
  196. package/dist/inspect_zod.js.map +1 -0
  197. package/dist/lib/openai-agent-client.chat.test.js +40 -0
  198. package/dist/lib/openai-agent-client.chat.test.js.map +1 -1
  199. package/dist/lib/openai-agent-client.d.ts +18 -4
  200. package/dist/lib/openai-agent-client.d.ts.map +1 -1
  201. package/dist/lib/openai-agent-client.js +228 -32
  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/retry-executor.js +45 -7
  208. package/dist/lib/retry-executor.js.map +1 -1
  209. package/dist/lib/retry-executor.test.js +63 -0
  210. package/dist/lib/retry-executor.test.js.map +1 -1
  211. package/dist/lib/tool-invoke.d.ts +3 -2
  212. package/dist/lib/tool-invoke.d.ts.map +1 -1
  213. package/dist/lib/tool-invoke.js +213 -10
  214. package/dist/lib/tool-invoke.js.map +1 -1
  215. package/dist/lib/tool-invoke.test.js +103 -0
  216. package/dist/lib/tool-invoke.test.js.map +1 -1
  217. package/dist/non-interactive.d.ts +1 -0
  218. package/dist/non-interactive.d.ts.map +1 -1
  219. package/dist/non-interactive.js +3 -1
  220. package/dist/non-interactive.js.map +1 -1
  221. package/dist/non-interactive.test.js +8 -1
  222. package/dist/non-interactive.test.js.map +1 -1
  223. package/dist/prompts/codex.md +105 -97
  224. package/dist/prompts/fragments/gpt-5.3-codex.md +7 -0
  225. package/dist/prompts/fragments/gpt-5.4-small.md +6 -0
  226. package/dist/prompts/fragments/gpt-5.4.md +7 -0
  227. package/dist/prompts/fragments/gpt-5.5.md +7 -0
  228. package/dist/prompts/gpt-5-modern.md +96 -54
  229. package/dist/prompts/plan-mode-info.md +14 -3
  230. package/dist/prompts/prompt-constructor.d.ts +19 -0
  231. package/dist/prompts/prompt-constructor.d.ts.map +1 -0
  232. package/dist/prompts/prompt-constructor.js +53 -0
  233. package/dist/prompts/prompt-constructor.js.map +1 -0
  234. package/dist/prompts/prompt-constructor.test.d.ts +2 -0
  235. package/dist/prompts/prompt-constructor.test.d.ts.map +1 -0
  236. package/dist/prompts/prompt-constructor.test.js +99 -0
  237. package/dist/prompts/prompt-constructor.test.js.map +1 -0
  238. package/dist/prompts/prompt-constructor.test.ts +112 -0
  239. package/dist/prompts/prompt-constructor.ts +91 -0
  240. package/dist/prompts/prompt-profiles.d.ts +17 -0
  241. package/dist/prompts/prompt-profiles.d.ts.map +1 -0
  242. package/dist/prompts/prompt-profiles.js +61 -0
  243. package/dist/prompts/prompt-profiles.js.map +1 -0
  244. package/dist/prompts/prompt-profiles.ts +77 -0
  245. package/dist/prompts/prompt-selector.d.ts.map +1 -1
  246. package/dist/prompts/prompt-selector.js +3 -21
  247. package/dist/prompts/prompt-selector.js.map +1 -1
  248. package/dist/prompts/prompt-selector.ts +3 -25
  249. package/dist/prompts/reasoning-efficiency.d.ts.map +1 -1
  250. package/dist/prompts/reasoning-efficiency.js +2 -0
  251. package/dist/prompts/reasoning-efficiency.js.map +1 -1
  252. package/dist/prompts/reasoning-efficiency.test.d.ts +2 -0
  253. package/dist/prompts/reasoning-efficiency.test.d.ts.map +1 -0
  254. package/dist/prompts/reasoning-efficiency.test.js +11 -0
  255. package/dist/prompts/reasoning-efficiency.test.js.map +1 -0
  256. package/dist/prompts/reasoning-efficiency.test.ts +12 -0
  257. package/dist/prompts/reasoning-efficiency.ts +2 -0
  258. package/dist/prompts/search-via-shell.test.js +19 -16
  259. package/dist/prompts/search-via-shell.test.js.map +1 -1
  260. package/dist/prompts/search-via-shell.test.ts +20 -16
  261. package/dist/prompts/subagent-delegation.test.js +12 -10
  262. package/dist/prompts/subagent-delegation.test.js.map +1 -1
  263. package/dist/prompts/subagent-delegation.test.ts +13 -10
  264. package/dist/prompts/worktree-hygiene.md +9 -0
  265. package/dist/providers/ai-sdk-logging-fetch.d.ts +1 -1
  266. package/dist/providers/ai-sdk-logging-fetch.d.ts.map +1 -1
  267. package/dist/providers/ai-sdk-logging-fetch.js +145 -47
  268. package/dist/providers/ai-sdk-logging-fetch.js.map +1 -1
  269. package/dist/providers/ai-sdk-logging-fetch.test.js +202 -5
  270. package/dist/providers/ai-sdk-logging-fetch.test.js.map +1 -1
  271. package/dist/providers/codex-responses-model.d.ts +20 -0
  272. package/dist/providers/codex-responses-model.d.ts.map +1 -0
  273. package/dist/providers/codex-responses-model.js +170 -0
  274. package/dist/providers/codex-responses-model.js.map +1 -0
  275. package/dist/providers/codex-responses-model.test.d.ts +2 -0
  276. package/dist/providers/codex-responses-model.test.d.ts.map +1 -0
  277. package/dist/providers/codex-responses-model.test.js +240 -0
  278. package/dist/providers/codex-responses-model.test.js.map +1 -0
  279. package/dist/providers/codex.provider.d.ts +29 -0
  280. package/dist/providers/codex.provider.d.ts.map +1 -0
  281. package/dist/providers/codex.provider.js +486 -0
  282. package/dist/providers/codex.provider.js.map +1 -0
  283. package/dist/providers/codex.provider.test.d.ts +2 -0
  284. package/dist/providers/codex.provider.test.d.ts.map +1 -0
  285. package/dist/providers/codex.provider.test.js +634 -0
  286. package/dist/providers/codex.provider.test.js.map +1 -0
  287. package/dist/providers/custom-provider-adapter.test.js +56 -0
  288. package/dist/providers/custom-provider-adapter.test.js.map +1 -1
  289. package/dist/providers/fallback-responses-model.d.ts +20 -0
  290. package/dist/providers/fallback-responses-model.d.ts.map +1 -0
  291. package/dist/providers/fallback-responses-model.js +277 -0
  292. package/dist/providers/fallback-responses-model.js.map +1 -0
  293. package/dist/providers/fallback-responses-model.test.d.ts +2 -0
  294. package/dist/providers/fallback-responses-model.test.d.ts.map +1 -0
  295. package/dist/providers/fallback-responses-model.test.js +279 -0
  296. package/dist/providers/fallback-responses-model.test.js.map +1 -0
  297. package/dist/providers/fetch/compose.d.ts +7 -0
  298. package/dist/providers/fetch/compose.d.ts.map +1 -0
  299. package/dist/providers/fetch/compose.js +20 -0
  300. package/dist/providers/fetch/compose.js.map +1 -0
  301. package/dist/providers/fetch/composer.d.ts +13 -0
  302. package/dist/providers/fetch/composer.d.ts.map +1 -0
  303. package/dist/providers/fetch/composer.js +15 -0
  304. package/dist/providers/fetch/composer.js.map +1 -0
  305. package/dist/providers/fetch/composer.test.d.ts +2 -0
  306. package/dist/providers/fetch/composer.test.d.ts.map +1 -0
  307. package/dist/providers/fetch/composer.test.js +448 -0
  308. package/dist/providers/fetch/composer.test.js.map +1 -0
  309. package/dist/providers/fetch/logging-middleware.d.ts +16 -0
  310. package/dist/providers/fetch/logging-middleware.d.ts.map +1 -0
  311. package/dist/providers/fetch/logging-middleware.js +214 -0
  312. package/dist/providers/fetch/logging-middleware.js.map +1 -0
  313. package/dist/providers/index.d.ts +1 -0
  314. package/dist/providers/index.d.ts.map +1 -1
  315. package/dist/providers/index.js +1 -0
  316. package/dist/providers/index.js.map +1 -1
  317. package/dist/providers/llama-cpp.provider.d.ts +3 -0
  318. package/dist/providers/llama-cpp.provider.d.ts.map +1 -0
  319. package/dist/providers/llama-cpp.provider.js +34 -0
  320. package/dist/providers/llama-cpp.provider.js.map +1 -0
  321. package/dist/providers/openai-compatible-middleware.d.ts +7 -0
  322. package/dist/providers/openai-compatible-middleware.d.ts.map +1 -0
  323. package/dist/providers/openai-compatible-middleware.js +133 -0
  324. package/dist/providers/openai-compatible-middleware.js.map +1 -0
  325. package/dist/providers/openai-compatible-models.d.ts +6 -0
  326. package/dist/providers/openai-compatible-models.d.ts.map +1 -0
  327. package/dist/providers/openai-compatible-models.js +17 -0
  328. package/dist/providers/openai-compatible-models.js.map +1 -0
  329. package/dist/providers/openai-compatible-response-normalizer.d.ts +7 -0
  330. package/dist/providers/openai-compatible-response-normalizer.d.ts.map +1 -0
  331. package/dist/providers/openai-compatible-response-normalizer.js +58 -0
  332. package/dist/providers/openai-compatible-response-normalizer.js.map +1 -0
  333. package/dist/providers/openai-compatible.provider.d.ts +4 -1
  334. package/dist/providers/openai-compatible.provider.d.ts.map +1 -1
  335. package/dist/providers/openai-compatible.provider.js +68 -324
  336. package/dist/providers/openai-compatible.provider.js.map +1 -1
  337. package/dist/providers/openai-compatible.provider.test.js +106 -1
  338. package/dist/providers/openai-compatible.provider.test.js.map +1 -1
  339. package/dist/providers/openai.provider.js +53 -1
  340. package/dist/providers/openai.provider.js.map +1 -1
  341. package/dist/providers/opencode.provider.d.ts +19 -0
  342. package/dist/providers/opencode.provider.d.ts.map +1 -0
  343. package/dist/providers/opencode.provider.js +45 -0
  344. package/dist/providers/opencode.provider.js.map +1 -0
  345. package/dist/providers/openrouter.provider.d.ts +2 -1
  346. package/dist/providers/openrouter.provider.d.ts.map +1 -1
  347. package/dist/providers/openrouter.provider.js +39 -22
  348. package/dist/providers/openrouter.provider.js.map +1 -1
  349. package/dist/providers/openrouter.provider.request-preprocessing.test.d.ts +2 -0
  350. package/dist/providers/openrouter.provider.request-preprocessing.test.d.ts.map +1 -0
  351. package/dist/providers/openrouter.provider.request-preprocessing.test.js +51 -0
  352. package/dist/providers/openrouter.provider.request-preprocessing.test.js.map +1 -0
  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 +11 -2
  357. package/dist/providers/registry.test.js.map +1 -1
  358. package/dist/scripts/eval-auto-approval/dataset.d.ts +3 -3
  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.d.ts +2 -0
  370. package/dist/services/conversation-clear-save.test.d.ts.map +1 -0
  371. package/dist/services/conversation-clear-save.test.js +57 -0
  372. package/dist/services/conversation-clear-save.test.js.map +1 -0
  373. package/dist/services/conversation-events.d.ts +44 -1
  374. package/dist/services/conversation-events.d.ts.map +1 -1
  375. package/dist/services/conversation-history-repair.d.ts +25 -0
  376. package/dist/services/conversation-history-repair.d.ts.map +1 -0
  377. package/dist/services/conversation-history-repair.js +227 -0
  378. package/dist/services/conversation-history-repair.js.map +1 -0
  379. package/dist/services/conversation-history-repair.test.d.ts +2 -0
  380. package/dist/services/conversation-history-repair.test.d.ts.map +1 -0
  381. package/dist/services/conversation-history-repair.test.js +76 -0
  382. package/dist/services/conversation-history-repair.test.js.map +1 -0
  383. package/dist/services/conversation-integration.test.js +3 -1
  384. package/dist/services/conversation-integration.test.js.map +1 -1
  385. package/dist/services/conversation-log-events.d.ts +107 -0
  386. package/dist/services/conversation-log-events.d.ts.map +1 -0
  387. package/dist/services/conversation-log-events.js +12 -0
  388. package/dist/services/conversation-log-events.js.map +1 -0
  389. package/dist/services/conversation-log-writer.d.ts +40 -0
  390. package/dist/services/conversation-log-writer.d.ts.map +1 -0
  391. package/dist/services/conversation-log-writer.js +264 -0
  392. package/dist/services/conversation-log-writer.js.map +1 -0
  393. package/dist/services/conversation-persistence-types.d.ts +14 -0
  394. package/dist/services/conversation-persistence-types.d.ts.map +1 -0
  395. package/dist/services/conversation-persistence-types.js +2 -0
  396. package/dist/services/conversation-persistence-types.js.map +1 -0
  397. package/dist/services/conversation-persistence.d.ts +40 -38
  398. package/dist/services/conversation-persistence.d.ts.map +1 -1
  399. package/dist/services/conversation-persistence.js +312 -71
  400. package/dist/services/conversation-persistence.js.map +1 -1
  401. package/dist/services/conversation-persistence.test.js +485 -355
  402. package/dist/services/conversation-persistence.test.js.map +1 -1
  403. package/dist/services/conversation-replay.d.ts +26 -0
  404. package/dist/services/conversation-replay.d.ts.map +1 -0
  405. package/dist/services/conversation-replay.js +252 -0
  406. package/dist/services/conversation-replay.js.map +1 -0
  407. package/dist/services/conversation-replay.test.d.ts +2 -0
  408. package/dist/services/conversation-replay.test.d.ts.map +1 -0
  409. package/dist/services/conversation-replay.test.js +153 -0
  410. package/dist/services/conversation-replay.test.js.map +1 -0
  411. package/dist/services/conversation-result-builder.d.ts.map +1 -1
  412. package/dist/services/conversation-result-builder.js +8 -2
  413. package/dist/services/conversation-result-builder.js.map +1 -1
  414. package/dist/services/conversation-result-builder.test.js +53 -0
  415. package/dist/services/conversation-result-builder.test.js.map +1 -1
  416. package/dist/services/conversation-retry-policy.d.ts +24 -0
  417. package/dist/services/conversation-retry-policy.d.ts.map +1 -1
  418. package/dist/services/conversation-retry-policy.js +68 -4
  419. package/dist/services/conversation-retry-policy.js.map +1 -1
  420. package/dist/services/conversation-retry-policy.test.js +99 -2
  421. package/dist/services/conversation-retry-policy.test.js.map +1 -1
  422. package/dist/services/conversation-service.d.ts +12 -2
  423. package/dist/services/conversation-service.d.ts.map +1 -1
  424. package/dist/services/conversation-service.js +29 -3
  425. package/dist/services/conversation-service.js.map +1 -1
  426. package/dist/services/conversation-session.auto-approval.test.js +2 -1
  427. package/dist/services/conversation-session.auto-approval.test.js.map +1 -1
  428. package/dist/services/conversation-session.d.ts +17 -1
  429. package/dist/services/conversation-session.d.ts.map +1 -1
  430. package/dist/services/conversation-session.js +580 -134
  431. package/dist/services/conversation-session.js.map +1 -1
  432. package/dist/services/conversation-store.d.ts.map +1 -1
  433. package/dist/services/conversation-store.js +6 -5
  434. package/dist/services/conversation-store.js.map +1 -1
  435. package/dist/services/conversation-store.test.js +34 -0
  436. package/dist/services/conversation-store.test.js.map +1 -1
  437. package/dist/services/input-surge-guard.d.ts +4 -0
  438. package/dist/services/input-surge-guard.d.ts.map +1 -1
  439. package/dist/services/input-surge-guard.js +2 -2
  440. package/dist/services/input-surge-guard.js.map +1 -1
  441. package/dist/services/input-surge-guard.test.js +15 -1
  442. package/dist/services/input-surge-guard.test.js.map +1 -1
  443. package/dist/services/large-uncached-input-guard.d.ts +35 -0
  444. package/dist/services/large-uncached-input-guard.d.ts.map +1 -0
  445. package/dist/services/large-uncached-input-guard.js +127 -0
  446. package/dist/services/large-uncached-input-guard.js.map +1 -0
  447. package/dist/services/large-uncached-input-guard.test.d.ts +2 -0
  448. package/dist/services/large-uncached-input-guard.test.d.ts.map +1 -0
  449. package/dist/services/large-uncached-input-guard.test.js +160 -0
  450. package/dist/services/large-uncached-input-guard.test.js.map +1 -0
  451. package/dist/services/logging-contract.d.ts +1 -1
  452. package/dist/services/logging-service.d.ts +4 -0
  453. package/dist/services/logging-service.d.ts.map +1 -1
  454. package/dist/services/logging-service.js +91 -33
  455. package/dist/services/logging-service.js.map +1 -1
  456. package/dist/services/model-service.d.ts +2 -0
  457. package/dist/services/model-service.d.ts.map +1 -1
  458. package/dist/services/model-service.js +7 -1
  459. package/dist/services/model-service.js.map +1 -1
  460. package/dist/services/provider-traffic.d.ts +87 -0
  461. package/dist/services/provider-traffic.d.ts.map +1 -0
  462. package/dist/services/provider-traffic.js +603 -0
  463. package/dist/services/provider-traffic.js.map +1 -0
  464. package/dist/services/provider-traffic.test.d.ts +2 -0
  465. package/dist/services/provider-traffic.test.d.ts.map +1 -0
  466. package/dist/services/provider-traffic.test.js +475 -0
  467. package/dist/services/provider-traffic.test.js.map +1 -0
  468. package/dist/services/rtk-service.d.ts.map +1 -1
  469. package/dist/services/rtk-service.js +1 -2
  470. package/dist/services/rtk-service.js.map +1 -1
  471. package/dist/services/service-interfaces.d.ts +16 -0
  472. package/dist/services/service-interfaces.d.ts.map +1 -1
  473. package/dist/services/settings-schema.d.ts +36 -28
  474. package/dist/services/settings-schema.d.ts.map +1 -1
  475. package/dist/services/settings-schema.js +7 -2
  476. package/dist/services/settings-schema.js.map +1 -1
  477. package/dist/services/settings-service.js +6 -6
  478. package/dist/services/settings-service.js.map +1 -1
  479. package/dist/services/settings-sources.d.ts.map +1 -1
  480. package/dist/services/settings-sources.js +1 -0
  481. package/dist/services/settings-sources.js.map +1 -1
  482. package/dist/services/shell-auto-approval-evaluator.d.ts +1 -1
  483. package/dist/services/shell-auto-approval-evaluator.d.ts.map +1 -1
  484. package/dist/services/shell-auto-approval-evaluator.js +284 -81
  485. package/dist/services/shell-auto-approval-evaluator.js.map +1 -1
  486. package/dist/services/shell-auto-approval-evaluator.test.js +215 -15
  487. package/dist/services/shell-auto-approval-evaluator.test.js.map +1 -1
  488. package/dist/services/stream-event-processor.d.ts +4 -0
  489. package/dist/services/stream-event-processor.d.ts.map +1 -1
  490. package/dist/services/stream-event-processor.js +78 -39
  491. package/dist/services/stream-event-processor.js.map +1 -1
  492. package/dist/services/stream-event-processor.test.js +71 -0
  493. package/dist/services/stream-event-processor.test.js.map +1 -1
  494. package/dist/services/subagents/subagent-manager.d.ts +2 -0
  495. package/dist/services/subagents/subagent-manager.d.ts.map +1 -1
  496. package/dist/services/subagents/subagent-manager.js +124 -21
  497. package/dist/services/subagents/subagent-manager.js.map +1 -1
  498. package/dist/services/subagents/subagent-manager.test.js +453 -0
  499. package/dist/services/subagents/subagent-manager.test.js.map +1 -1
  500. package/dist/services/terminal-result-collector.d.ts.map +1 -1
  501. package/dist/services/terminal-result-collector.js +6 -1
  502. package/dist/services/terminal-result-collector.js.map +1 -1
  503. package/dist/services/tool-call-arguments.d.ts +20 -0
  504. package/dist/services/tool-call-arguments.d.ts.map +1 -0
  505. package/dist/services/tool-call-arguments.js +49 -0
  506. package/dist/services/tool-call-arguments.js.map +1 -0
  507. package/dist/services/tool-execution-ledger.d.ts +35 -0
  508. package/dist/services/tool-execution-ledger.d.ts.map +1 -0
  509. package/dist/services/tool-execution-ledger.js +204 -0
  510. package/dist/services/tool-execution-ledger.js.map +1 -0
  511. package/dist/services/tool-execution-ledger.test.d.ts +2 -0
  512. package/dist/services/tool-execution-ledger.test.d.ts.map +1 -0
  513. package/dist/services/tool-execution-ledger.test.js +96 -0
  514. package/dist/services/tool-execution-ledger.test.js.map +1 -0
  515. package/dist/tools/apply-patch.d.ts +8 -0
  516. package/dist/tools/apply-patch.d.ts.map +1 -1
  517. package/dist/tools/apply-patch.js +157 -2
  518. package/dist/tools/apply-patch.js.map +1 -1
  519. package/dist/tools/apply-patch.test.js +81 -0
  520. package/dist/tools/apply-patch.test.js.map +1 -1
  521. package/dist/tools/search-replace.d.ts.map +1 -1
  522. package/dist/tools/search-replace.js +92 -140
  523. package/dist/tools/search-replace.js.map +1 -1
  524. package/dist/utils/command-safety/path-analysis.d.ts.map +1 -1
  525. package/dist/utils/command-safety/path-analysis.js +143 -35
  526. package/dist/utils/command-safety/path-analysis.js.map +1 -1
  527. package/dist/utils/conversation-event-handler.d.ts.map +1 -1
  528. package/dist/utils/conversation-event-handler.js +52 -4
  529. package/dist/utils/conversation-event-handler.js.map +1 -1
  530. package/dist/utils/conversation-event-handler.test.js +175 -0
  531. package/dist/utils/conversation-event-handler.test.js.map +1 -1
  532. package/dist/utils/error-helpers.d.ts +1 -0
  533. package/dist/utils/error-helpers.d.ts.map +1 -1
  534. package/dist/utils/error-helpers.js +61 -0
  535. package/dist/utils/error-helpers.js.map +1 -1
  536. package/dist/utils/error-helpers.test.js +13 -1
  537. package/dist/utils/error-helpers.test.js.map +1 -1
  538. package/dist/utils/ink-render-options.d.ts +1 -1
  539. package/dist/utils/ink-render-options.d.ts.map +1 -1
  540. package/dist/utils/ink-render-options.js +1 -1
  541. package/dist/utils/ink-render-options.js.map +1 -1
  542. package/dist/utils/streaming-session-factory.d.ts +2 -0
  543. package/dist/utils/streaming-session-factory.d.ts.map +1 -1
  544. package/dist/utils/streaming-session-factory.js +4 -0
  545. package/dist/utils/streaming-session-factory.js.map +1 -1
  546. package/package.json +2 -2
@@ -0,0 +1,634 @@
1
+ import test from 'ava';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { getProvider } from './index.js';
5
+ import { CodexTokenManager, CODEX_MAX_RETRIES, CODEX_REQUEST_TIMEOUT_MS, resolveTokenPath, getJwtExpiry, extractAccountIdFromClaims, extractAccountId, resolveCodexClientVersion, sanitizeCodexRequestInit, } from './codex.provider.js';
6
+ // Helper to create a fake JWT with a specific expiry time in seconds from now
7
+ function createFakeJwt(expiresInSeconds) {
8
+ const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url');
9
+ const payload = Buffer.from(JSON.stringify({
10
+ exp: Math.floor(Date.now() / 1000) + expiresInSeconds,
11
+ })).toString('base64url');
12
+ return `${header}.${payload}.signature`;
13
+ }
14
+ // Temporary directory inside the workspace for safe testing
15
+ const TEST_DIR = path.join(process.cwd(), 'temp-codex-test');
16
+ test.before(() => {
17
+ if (fs.existsSync(TEST_DIR)) {
18
+ fs.rmSync(TEST_DIR, { recursive: true, force: true });
19
+ }
20
+ fs.mkdirSync(TEST_DIR, { recursive: true });
21
+ });
22
+ test.after.always(() => {
23
+ if (fs.existsSync(TEST_DIR)) {
24
+ fs.rmSync(TEST_DIR, { recursive: true, force: true });
25
+ }
26
+ });
27
+ test('getJwtExpiry decodes valid JWT and returns expiration timestamp', (t) => {
28
+ const expSeconds = 3600;
29
+ const jwt = createFakeJwt(expSeconds);
30
+ const expiry = getJwtExpiry(jwt);
31
+ t.truthy(expiry);
32
+ // Allow small clock drift tolerance
33
+ const expected = Math.floor(Date.now() / 1000) + expSeconds;
34
+ t.true(Math.abs((expiry || 0) / 1000 - expected) < 2);
35
+ });
36
+ test('getJwtExpiry returns null for invalid JWT', (t) => {
37
+ t.is(getJwtExpiry('invalid-token'), null);
38
+ t.is(getJwtExpiry('foo.bar'), null);
39
+ t.is(getJwtExpiry('foo.bar.baz'), null); // not valid base64 JSON
40
+ });
41
+ test.serial('resolveTokenPath resolves paths in correct order', (t) => {
42
+ // Save original env vars
43
+ const origHome = process.env.CHATGPT_LOCAL_HOME;
44
+ const origCodexHome = process.env.CODEX_HOME;
45
+ t.teardown(() => {
46
+ if (origHome)
47
+ process.env.CHATGPT_LOCAL_HOME = origHome;
48
+ else
49
+ delete process.env.CHATGPT_LOCAL_HOME;
50
+ if (origCodexHome)
51
+ process.env.CODEX_HOME = origCodexHome;
52
+ else
53
+ delete process.env.CODEX_HOME;
54
+ });
55
+ const chatgptLocalHomeDir = path.join(TEST_DIR, 'chatgpt-local-home');
56
+ const codexHomeDir = path.join(TEST_DIR, 'codex-home');
57
+ fs.mkdirSync(chatgptLocalHomeDir, { recursive: true });
58
+ fs.mkdirSync(codexHomeDir, { recursive: true });
59
+ const path1 = path.join(chatgptLocalHomeDir, 'auth.json');
60
+ const path2 = path.join(codexHomeDir, 'auth.json');
61
+ // Case 1: CHATGPT_LOCAL_HOME is set
62
+ fs.writeFileSync(path1, '{}');
63
+ fs.writeFileSync(path2, '{}');
64
+ process.env.CHATGPT_LOCAL_HOME = chatgptLocalHomeDir;
65
+ process.env.CODEX_HOME = codexHomeDir;
66
+ const resolved = resolveTokenPath();
67
+ t.is(resolved, path1);
68
+ // Case 2: Only CODEX_HOME is set
69
+ delete process.env.CHATGPT_LOCAL_HOME;
70
+ const resolved2 = resolveTokenPath();
71
+ t.is(resolved2, path2);
72
+ // Cleanup files
73
+ fs.unlinkSync(path1);
74
+ fs.unlinkSync(path2);
75
+ });
76
+ test('CodexTokenManager throws if no token file found', async (t) => {
77
+ // Use a manager with empty/non-existent paths
78
+ const manager = new CodexTokenManager({
79
+ tokenPathResolver: () => null,
80
+ });
81
+ await t.throwsAsync(async () => {
82
+ await manager.getOrRefreshAccessToken();
83
+ }, { message: /Codex token file not found/ });
84
+ });
85
+ test('CodexTokenManager does not refresh if access token is valid and refresh is recent', async (t) => {
86
+ const tokenPath = path.join(TEST_DIR, 'auth_valid.json');
87
+ const validToken = createFakeJwt(3600); // 1 hour expiry
88
+ const initialTokens = {
89
+ tokens: {
90
+ access_token: validToken,
91
+ refresh_token: 'valid-refresh-token',
92
+ id_token: 'id-token',
93
+ account_id: 'account-123',
94
+ },
95
+ last_refresh: new Date().toISOString(),
96
+ };
97
+ fs.writeFileSync(tokenPath, JSON.stringify(initialTokens));
98
+ let fetchCalled = false;
99
+ const mockFetch = async () => {
100
+ fetchCalled = true;
101
+ return new Response(JSON.stringify({}), { status: 200 });
102
+ };
103
+ const manager = new CodexTokenManager({
104
+ tokenPathResolver: () => tokenPath,
105
+ fetchImpl: mockFetch,
106
+ });
107
+ const token = await manager.getOrRefreshAccessToken();
108
+ t.is(token, validToken);
109
+ t.false(fetchCalled, 'Should not have triggered refresh fetch');
110
+ });
111
+ test('CodexTokenManager refreshes if access token is expired or within 5 minutes of expiry', async (t) => {
112
+ const tokenPath = path.join(TEST_DIR, 'auth_expired.json');
113
+ const expiredToken = createFakeJwt(120); // 2 minutes expiry (within 5 minutes boundary)
114
+ const initialTokens = {
115
+ tokens: {
116
+ access_token: expiredToken,
117
+ refresh_token: 'old-refresh-token',
118
+ id_token: 'old-id-token',
119
+ account_id: 'account-123',
120
+ },
121
+ last_refresh: new Date().toISOString(),
122
+ };
123
+ fs.writeFileSync(tokenPath, JSON.stringify(initialTokens));
124
+ const newToken = createFakeJwt(3600);
125
+ let fetchPayload = null;
126
+ const mockFetch = async (_url, init) => {
127
+ fetchPayload = JSON.parse(init.body);
128
+ return new Response(JSON.stringify({
129
+ access_token: newToken,
130
+ refresh_token: 'new-refresh-token',
131
+ id_token: 'new-id-token',
132
+ }), { status: 200, headers: { 'Content-Type': 'application/json' } });
133
+ };
134
+ const manager = new CodexTokenManager({
135
+ tokenPathResolver: () => tokenPath,
136
+ fetchImpl: mockFetch,
137
+ });
138
+ const token = await manager.getOrRefreshAccessToken();
139
+ t.is(token, newToken);
140
+ // Verify fetch payload
141
+ t.is(fetchPayload.grant_type, 'refresh_token');
142
+ t.is(fetchPayload.refresh_token, 'old-refresh-token');
143
+ t.is(fetchPayload.client_id, 'app_EMoamEEZ73f0CkXaXp7hrann');
144
+ // Verify file update & mode
145
+ const updatedContent = JSON.parse(fs.readFileSync(tokenPath, 'utf8'));
146
+ t.is(updatedContent.tokens.access_token, newToken);
147
+ t.is(updatedContent.tokens.refresh_token, 'new-refresh-token');
148
+ t.is(updatedContent.tokens.id_token, 'new-id-token');
149
+ t.is(updatedContent.tokens.account_id, 'account-123'); // preserved
150
+ t.truthy(updatedContent.last_refresh);
151
+ // Check file mode 0600 on non-windows
152
+ if (process.platform !== 'win32') {
153
+ const stat = fs.statSync(tokenPath);
154
+ t.is(stat.mode & 0o777, 0o600);
155
+ }
156
+ });
157
+ test('CodexTokenManager refreshes if last_refresh is more than 55 minutes ago', async (t) => {
158
+ const tokenPath = path.join(TEST_DIR, 'auth_old_refresh.json');
159
+ const validToken = createFakeJwt(3600); // JWT still valid for 1 hour
160
+ const initialTokens = {
161
+ tokens: {
162
+ access_token: validToken,
163
+ refresh_token: 'old-refresh-token',
164
+ },
165
+ // last refresh 60 minutes ago
166
+ last_refresh: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
167
+ };
168
+ fs.writeFileSync(tokenPath, JSON.stringify(initialTokens));
169
+ const newToken = createFakeJwt(3600);
170
+ const mockFetch = async () => {
171
+ return new Response(JSON.stringify({
172
+ access_token: newToken,
173
+ refresh_token: 'new-refresh',
174
+ }), { status: 200, headers: { 'Content-Type': 'application/json' } });
175
+ };
176
+ const manager = new CodexTokenManager({
177
+ tokenPathResolver: () => tokenPath,
178
+ fetchImpl: mockFetch,
179
+ });
180
+ const token = await manager.getOrRefreshAccessToken();
181
+ t.is(token, newToken);
182
+ });
183
+ test('Codex provider is registered in the registry', (t) => {
184
+ const provider = getProvider('codex');
185
+ t.truthy(provider, 'codex provider should be registered');
186
+ t.is(provider?.id, 'codex');
187
+ t.is(provider?.label, 'Codex');
188
+ t.is(typeof provider?.fetchModels, 'function');
189
+ t.is(typeof provider?.createRunner, 'function');
190
+ t.deepEqual(provider?.capabilities, {
191
+ supportsConversationChaining: false,
192
+ supportsTracingControl: false,
193
+ usesStrictToolSchema: true,
194
+ nativePatchModelPrefixes: ['gpt-5.1'],
195
+ });
196
+ });
197
+ test('Codex fetchModels parses custom models endpoint', async (t) => {
198
+ const provider = getProvider('codex');
199
+ t.truthy(provider);
200
+ const tokenPath = path.join(TEST_DIR, 'auth_models.json');
201
+ const validToken = createFakeJwt(3600);
202
+ fs.writeFileSync(tokenPath, JSON.stringify({
203
+ tokens: { access_token: validToken },
204
+ last_refresh: new Date().toISOString(),
205
+ }));
206
+ let fetchUrl = '';
207
+ let authHeader = '';
208
+ const mockFetch = async (url, init) => {
209
+ fetchUrl = url;
210
+ authHeader = init?.headers?.Authorization || init?.headers?.authorization || '';
211
+ return new Response(JSON.stringify({
212
+ models: [
213
+ { slug: 'gpt-5-codex', display_name: 'GPT-5 Codex', default_reasoning_level: 'medium' },
214
+ { slug: 'gpt-4o', display_name: 'GPT-4o', default_reasoning_level: 'low' },
215
+ ],
216
+ }), { status: 200, headers: { 'Content-Type': 'application/json' } });
217
+ };
218
+ const deps = {
219
+ settingsService: {
220
+ get: () => 'gpt-5-codex',
221
+ },
222
+ loggingService: {
223
+ info: () => { },
224
+ warn: () => { },
225
+ error: () => { },
226
+ debug: () => { },
227
+ },
228
+ };
229
+ const origHome = process.env.CHATGPT_LOCAL_HOME;
230
+ process.env.CHATGPT_LOCAL_HOME = TEST_DIR;
231
+ fs.renameSync(tokenPath, path.join(TEST_DIR, 'auth.json'));
232
+ try {
233
+ const models = await provider.fetchModels(deps, mockFetch);
234
+ t.true(fetchUrl.startsWith('https://chatgpt.com/backend-api/codex/models?client_version='));
235
+ t.is(authHeader, `Bearer ${validToken}`);
236
+ t.is(models.length, 2);
237
+ t.is(models[1].id, 'gpt-4o');
238
+ t.is(models[1].default_reasoning_level, 'low');
239
+ t.is(models[0].id, 'gpt-5-codex');
240
+ t.is(models[0].default_reasoning_level, 'medium');
241
+ }
242
+ finally {
243
+ process.env.CHATGPT_LOCAL_HOME = origHome;
244
+ try {
245
+ fs.unlinkSync(path.join(TEST_DIR, 'auth.json'));
246
+ }
247
+ catch {
248
+ // ignore
249
+ }
250
+ }
251
+ });
252
+ test('resolveCodexClientVersion returns local version if available and writes to cache', async (t) => {
253
+ const cacheDir = path.join(TEST_DIR, 'cache-local');
254
+ fs.mkdirSync(cacheDir, { recursive: true });
255
+ const execImpl = async (cmd) => {
256
+ t.is(cmd, 'codex --version');
257
+ return { stdout: 'codex-cli 1.2.3' };
258
+ };
259
+ const version = await resolveCodexClientVersion({
260
+ cacheDir,
261
+ execImpl: execImpl,
262
+ });
263
+ t.is(version, '1.2.3');
264
+ // Verify it is cached
265
+ const cachePath = path.join(cacheDir, 'codex-client-version.json');
266
+ t.true(fs.existsSync(cachePath));
267
+ const cached = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
268
+ t.is(cached.version, '1.2.3');
269
+ t.true(typeof cached.timestamp === 'number');
270
+ });
271
+ test('sanitizeCodexRequestInit leaves non-responses requests unchanged', (t) => {
272
+ const init = {
273
+ body: JSON.stringify({
274
+ input: [{ type: 'reasoning', id: 'rs_123' }],
275
+ }),
276
+ };
277
+ const sanitized = sanitizeCodexRequestInit('https://chatgpt.com/backend-api/codex/models', init);
278
+ t.deepEqual(sanitized, init);
279
+ });
280
+ test('sanitizeCodexRequestInit normalizes include JSON string to array', (t) => {
281
+ const init = {
282
+ body: JSON.stringify({
283
+ input: [{ type: 'message', role: 'user', content: [{ type: 'input_text', text: 'hi' }] }],
284
+ include: '["reasoning.encrypted_content"]',
285
+ }),
286
+ };
287
+ const sanitized = sanitizeCodexRequestInit('https://chatgpt.com/backend-api/codex/responses', init);
288
+ const body = JSON.parse(String(sanitized?.body));
289
+ t.true(Array.isArray(body.include));
290
+ t.deepEqual(body.include, ['reasoning.encrypted_content']);
291
+ });
292
+ test('resolveCodexClientVersion falls back to npm registry if local version fails', async (t) => {
293
+ const cacheDir = path.join(TEST_DIR, 'cache-npm');
294
+ fs.mkdirSync(cacheDir, { recursive: true });
295
+ const execImpl = async () => {
296
+ throw new Error('command not found');
297
+ };
298
+ let fetchUrl = '';
299
+ const mockFetch = async (url) => {
300
+ fetchUrl = url;
301
+ return new Response(JSON.stringify({ version: '2.3.4' }), { status: 200 });
302
+ };
303
+ const version = await resolveCodexClientVersion({
304
+ cacheDir,
305
+ execImpl: execImpl,
306
+ fetchImpl: mockFetch,
307
+ });
308
+ t.is(version, '2.3.4');
309
+ t.is(fetchUrl, 'https://registry.npmjs.org/@openai/codex/latest');
310
+ // Verify cached
311
+ const cachePath = path.join(cacheDir, 'codex-client-version.json');
312
+ t.true(fs.existsSync(cachePath));
313
+ const cached = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
314
+ t.is(cached.version, '2.3.4');
315
+ });
316
+ test('resolveCodexClientVersion falls back to fallback version if all fails', async (t) => {
317
+ const cacheDir = path.join(TEST_DIR, 'cache-fallback');
318
+ fs.mkdirSync(cacheDir, { recursive: true });
319
+ const execImpl = async () => {
320
+ throw new Error('command not found');
321
+ };
322
+ const mockFetch = async () => {
323
+ return new Response(JSON.stringify({}), { status: 500 });
324
+ };
325
+ const version = await resolveCodexClientVersion({
326
+ cacheDir,
327
+ execImpl: execImpl,
328
+ fetchImpl: mockFetch,
329
+ });
330
+ t.is(version, '0.133.0');
331
+ // Verify cached
332
+ const cachePath = path.join(cacheDir, 'codex-client-version.json');
333
+ t.true(fs.existsSync(cachePath));
334
+ const cached = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
335
+ t.is(cached.version, '0.133.0');
336
+ });
337
+ test('resolveCodexClientVersion uses cache if valid and less than one week old', async (t) => {
338
+ const cacheDir = path.join(TEST_DIR, 'cache-valid');
339
+ fs.mkdirSync(cacheDir, { recursive: true });
340
+ const cachePath = path.join(cacheDir, 'codex-client-version.json');
341
+ fs.writeFileSync(cachePath, JSON.stringify({
342
+ version: '9.9.9',
343
+ timestamp: Date.now() - 24 * 60 * 60 * 1000, // 1 day ago
344
+ }));
345
+ const execImpl = async () => {
346
+ t.fail('exec should not be called when valid cache exists');
347
+ return { stdout: '' };
348
+ };
349
+ const mockFetch = async () => {
350
+ t.fail('fetch should not be called when valid cache exists');
351
+ return new Response(JSON.stringify({}), { status: 200 });
352
+ };
353
+ const version = await resolveCodexClientVersion({
354
+ cacheDir,
355
+ execImpl: execImpl,
356
+ fetchImpl: mockFetch,
357
+ });
358
+ t.is(version, '9.9.9');
359
+ });
360
+ test('resolveCodexClientVersion ignores cache if older than one week', async (t) => {
361
+ const cacheDir = path.join(TEST_DIR, 'cache-expired');
362
+ fs.mkdirSync(cacheDir, { recursive: true });
363
+ const cachePath = path.join(cacheDir, 'codex-client-version.json');
364
+ fs.writeFileSync(cachePath, JSON.stringify({
365
+ version: '9.9.9',
366
+ timestamp: Date.now() - 8 * 24 * 60 * 60 * 1000, // 8 days ago (expired)
367
+ }));
368
+ const execImpl = async () => {
369
+ return { stdout: 'codex-cli 1.0.0' };
370
+ };
371
+ const version = await resolveCodexClientVersion({
372
+ cacheDir,
373
+ execImpl: execImpl,
374
+ });
375
+ t.is(version, '1.0.0');
376
+ // Verify cache updated
377
+ const cached = JSON.parse(fs.readFileSync(cachePath, 'utf8'));
378
+ t.is(cached.version, '1.0.0');
379
+ t.true(Date.now() - cached.timestamp < 1000);
380
+ });
381
+ test('Codex fetchModels appends correct client_version from cache/resolver', async (t) => {
382
+ const provider = getProvider('codex');
383
+ t.truthy(provider);
384
+ // Set cache dir to test dir
385
+ const origCacheDir = process.env.TERM2_CACHE_DIR;
386
+ process.env.TERM2_CACHE_DIR = path.join(TEST_DIR, 'fetch-models-cache');
387
+ fs.mkdirSync(process.env.TERM2_CACHE_DIR, { recursive: true });
388
+ // Pre-seed cache with a specific version
389
+ const cachePath = path.join(process.env.TERM2_CACHE_DIR, 'codex-client-version.json');
390
+ fs.writeFileSync(cachePath, JSON.stringify({
391
+ version: '1.2.3-test',
392
+ timestamp: Date.now(),
393
+ }));
394
+ const tokenPath = path.join(TEST_DIR, 'auth_models_version.json');
395
+ const validToken = createFakeJwt(3600);
396
+ fs.writeFileSync(tokenPath, JSON.stringify({
397
+ tokens: { access_token: validToken },
398
+ last_refresh: new Date().toISOString(),
399
+ }));
400
+ let fetchUrl = '';
401
+ const mockFetch = async (url) => {
402
+ fetchUrl = url;
403
+ return new Response(JSON.stringify({
404
+ data: [{ id: 'gpt-4o', name: 'GPT-4o' }],
405
+ }), { status: 200, headers: { 'Content-Type': 'application/json' } });
406
+ };
407
+ const deps = {
408
+ settingsService: { get: () => 'gpt-4o' },
409
+ loggingService: { info: () => { }, warn: () => { }, error: () => { }, debug: () => { } },
410
+ };
411
+ const origHome = process.env.CHATGPT_LOCAL_HOME;
412
+ process.env.CHATGPT_LOCAL_HOME = TEST_DIR;
413
+ fs.renameSync(tokenPath, path.join(TEST_DIR, 'auth.json'));
414
+ try {
415
+ await provider.fetchModels(deps, mockFetch);
416
+ t.is(fetchUrl, 'https://chatgpt.com/backend-api/codex/models?client_version=1.2.3-test');
417
+ }
418
+ finally {
419
+ process.env.CHATGPT_LOCAL_HOME = origHome;
420
+ process.env.TERM2_CACHE_DIR = origCacheDir;
421
+ try {
422
+ fs.unlinkSync(path.join(TEST_DIR, 'auth.json'));
423
+ }
424
+ catch { }
425
+ }
426
+ });
427
+ function createFakeJwtWithClaims(claims) {
428
+ const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url');
429
+ const payload = Buffer.from(JSON.stringify(claims)).toString('base64url');
430
+ return `${header}.${payload}.signature`;
431
+ }
432
+ test('extractAccountIdFromClaims resolves account ID with correct precedence', (t) => {
433
+ const claims1 = { chatgpt_account_id: 'acc_1' };
434
+ t.is(extractAccountIdFromClaims(claims1), 'acc_1');
435
+ const claims2 = {
436
+ 'https://api.openai.com/auth': { chatgpt_account_id: 'acc_2' },
437
+ };
438
+ t.is(extractAccountIdFromClaims(claims2), 'acc_2');
439
+ const claims3 = {
440
+ organizations: [{ id: 'org_3' }],
441
+ };
442
+ t.is(extractAccountIdFromClaims(claims3), 'org_3');
443
+ // Precedence test: chatgpt_account_id > https://api.openai.com/auth > organizations
444
+ const claimsAll = {
445
+ chatgpt_account_id: 'acc_1',
446
+ 'https://api.openai.com/auth': { chatgpt_account_id: 'acc_2' },
447
+ organizations: [{ id: 'org_3' }],
448
+ };
449
+ t.is(extractAccountIdFromClaims(claimsAll), 'acc_1');
450
+ const claimsNestedAndOrg = {
451
+ 'https://api.openai.com/auth': { chatgpt_account_id: 'acc_2' },
452
+ organizations: [{ id: 'org_3' }],
453
+ };
454
+ t.is(extractAccountIdFromClaims(claimsNestedAndOrg), 'acc_2');
455
+ });
456
+ test('extractAccountId prefers id_token claims over access_token claims', (t) => {
457
+ const idToken = createFakeJwtWithClaims({ chatgpt_account_id: 'acc_id_token' });
458
+ const accessToken = createFakeJwtWithClaims({ chatgpt_account_id: 'acc_access_token' });
459
+ // Preferred id_token
460
+ t.is(extractAccountId(idToken, accessToken), 'acc_id_token');
461
+ // Fallback to access_token if id_token is missing or has no relevant claim
462
+ t.is(extractAccountId(undefined, accessToken), 'acc_access_token');
463
+ t.is(extractAccountId('', accessToken), 'acc_access_token');
464
+ });
465
+ test.serial('CodexTokenManager extracts and stores accountId from file and refresh responses', async (t) => {
466
+ const tokenPath = path.join(TEST_DIR, 'auth_account_id.json');
467
+ const idToken = createFakeJwtWithClaims({ chatgpt_account_id: 'acc_from_id_token' });
468
+ const accessToken = createFakeJwt(3600);
469
+ const initialTokens = {
470
+ tokens: {
471
+ access_token: accessToken,
472
+ refresh_token: 'refresh-token',
473
+ id_token: idToken,
474
+ },
475
+ last_refresh: new Date().toISOString(),
476
+ };
477
+ fs.writeFileSync(tokenPath, JSON.stringify(initialTokens));
478
+ const manager = new CodexTokenManager({
479
+ tokenPathResolver: () => tokenPath,
480
+ });
481
+ // Ensure accountId is initially null before loading
482
+ t.is(manager.getAccountId(), null);
483
+ await manager.getOrRefreshAccessToken();
484
+ t.is(manager.getAccountId(), 'acc_from_id_token');
485
+ // Test update with fallback to file-level account_id if no claims found
486
+ const tokenPathFallback = path.join(TEST_DIR, 'auth_account_id_fallback.json');
487
+ const initialTokensFallback = {
488
+ tokens: {
489
+ access_token: accessToken,
490
+ refresh_token: 'refresh-token',
491
+ account_id: 'acc_from_file_direct',
492
+ },
493
+ last_refresh: new Date().toISOString(),
494
+ };
495
+ fs.writeFileSync(tokenPathFallback, JSON.stringify(initialTokensFallback));
496
+ const managerFallback = new CodexTokenManager({
497
+ tokenPathResolver: () => tokenPathFallback,
498
+ });
499
+ await managerFallback.getOrRefreshAccessToken();
500
+ t.is(managerFallback.getAccountId(), 'acc_from_file_direct');
501
+ });
502
+ test.serial('Codex fetchModels injects ChatGPT-Account-Id header if present', async (t) => {
503
+ const provider = getProvider('codex');
504
+ t.truthy(provider);
505
+ const tokenPath = path.join(TEST_DIR, 'auth_models_header.json');
506
+ const validToken = createFakeJwt(3600);
507
+ const idToken = createFakeJwtWithClaims({ chatgpt_account_id: 'acc_models_test' });
508
+ fs.writeFileSync(tokenPath, JSON.stringify({
509
+ tokens: { access_token: validToken, id_token: idToken },
510
+ last_refresh: new Date().toISOString(),
511
+ }));
512
+ let fetchHeaders = {};
513
+ const mockFetch = async (_url, init) => {
514
+ fetchHeaders = init?.headers || {};
515
+ return new Response(JSON.stringify({
516
+ models: [],
517
+ }), { status: 200, headers: { 'Content-Type': 'application/json' } });
518
+ };
519
+ const deps = {
520
+ settingsService: { get: () => 'gpt-5-codex' },
521
+ loggingService: { info: () => { }, warn: () => { }, error: () => { }, debug: () => { } },
522
+ };
523
+ const origHome = process.env.CHATGPT_LOCAL_HOME;
524
+ process.env.CHATGPT_LOCAL_HOME = TEST_DIR;
525
+ fs.renameSync(tokenPath, path.join(TEST_DIR, 'auth.json'));
526
+ try {
527
+ await provider.fetchModels(deps, mockFetch);
528
+ t.is(fetchHeaders['ChatGPT-Account-Id'], 'acc_models_test');
529
+ }
530
+ finally {
531
+ process.env.CHATGPT_LOCAL_HOME = origHome;
532
+ try {
533
+ fs.unlinkSync(path.join(TEST_DIR, 'auth.json'));
534
+ }
535
+ catch { }
536
+ }
537
+ });
538
+ test.serial('Codex provider createRunner custom fetch injects chatgpt-account-id header', async (t) => {
539
+ const provider = getProvider('codex');
540
+ t.truthy(provider);
541
+ if (!provider || !provider.createRunner) {
542
+ t.fail('createRunner is undefined');
543
+ return;
544
+ }
545
+ const validToken = createFakeJwt(3600);
546
+ const idToken = createFakeJwtWithClaims({ chatgpt_account_id: 'acc_runner_test' });
547
+ const origHome = process.env.CHATGPT_LOCAL_HOME;
548
+ process.env.CHATGPT_LOCAL_HOME = TEST_DIR;
549
+ fs.writeFileSync(path.join(TEST_DIR, 'auth.json'), JSON.stringify({
550
+ tokens: { access_token: validToken, id_token: idToken },
551
+ last_refresh: new Date().toISOString(),
552
+ }));
553
+ const mockLogging = {
554
+ info: () => { },
555
+ warn: () => { },
556
+ error: () => { },
557
+ debug: () => { },
558
+ };
559
+ const deps = {
560
+ settingsService: { get: (key) => (key === 'agent.model' ? 'gpt-5.3-codex' : undefined) },
561
+ loggingService: mockLogging,
562
+ };
563
+ let interceptorHeaders = {};
564
+ const mockFetch = async (_url, init) => {
565
+ interceptorHeaders = {};
566
+ if (init?.headers) {
567
+ if (typeof init.headers.forEach === 'function') {
568
+ init.headers.forEach((v, k) => {
569
+ interceptorHeaders[k.toLowerCase()] = String(v);
570
+ });
571
+ }
572
+ else {
573
+ for (const [k, v] of Object.entries(init.headers)) {
574
+ interceptorHeaders[k.toLowerCase()] = String(v);
575
+ }
576
+ }
577
+ }
578
+ return new Response(JSON.stringify({}), { status: 200, headers: { 'Content-Type': 'application/json' } });
579
+ };
580
+ const origFetch = globalThis.fetch;
581
+ globalThis.fetch = mockFetch;
582
+ try {
583
+ const runner = provider.createRunner(deps);
584
+ t.truthy(runner);
585
+ if (!runner)
586
+ return;
587
+ const modelProvider = runner.config.modelProvider;
588
+ const model = await modelProvider.getModel('gpt-5.3-codex');
589
+ const client = model._client;
590
+ await client.chat.completions
591
+ .create({
592
+ messages: [{ role: 'user', content: 'hi' }],
593
+ model: 'gpt-4o',
594
+ })
595
+ .catch(() => { });
596
+ t.is(interceptorHeaders['chatgpt-account-id'], 'acc_runner_test');
597
+ }
598
+ finally {
599
+ globalThis.fetch = origFetch;
600
+ process.env.CHATGPT_LOCAL_HOME = origHome;
601
+ try {
602
+ fs.unlinkSync(path.join(TEST_DIR, 'auth.json'));
603
+ }
604
+ catch { }
605
+ }
606
+ });
607
+ test.serial('Codex provider configures a shorter request timeout without SDK retries', async (t) => {
608
+ const provider = getProvider('codex');
609
+ t.truthy(provider);
610
+ if (!provider?.createRunner) {
611
+ t.fail('createRunner is undefined');
612
+ return;
613
+ }
614
+ const deps = {
615
+ settingsService: { get: (key) => (key === 'agent.model' ? 'gpt-5.3-codex' : undefined) },
616
+ loggingService: {
617
+ info: () => { },
618
+ warn: () => { },
619
+ error: () => { },
620
+ debug: () => { },
621
+ },
622
+ };
623
+ const runner = provider.createRunner(deps);
624
+ if (!runner) {
625
+ t.fail('runner is undefined');
626
+ return;
627
+ }
628
+ const modelProvider = runner.config.modelProvider;
629
+ const model = (await modelProvider.getModel('gpt-5.3-codex'));
630
+ const client = model._client;
631
+ t.is(client.timeout, CODEX_REQUEST_TIMEOUT_MS);
632
+ t.is(client.maxRetries, CODEX_MAX_RETRIES);
633
+ });
634
+ //# sourceMappingURL=codex.provider.test.js.map