@fgv/ts-extras 5.1.0-3 → 5.1.0-30

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 (334) hide show
  1. package/dist/index.browser.js +4 -2
  2. package/dist/index.browser.js.map +1 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/packlets/ai-assist/apiClient.js +958 -131
  5. package/dist/packlets/ai-assist/apiClient.js.map +1 -0
  6. package/dist/packlets/ai-assist/chatRequestBuilders.js +186 -0
  7. package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -0
  8. package/dist/packlets/ai-assist/converters.js +2 -1
  9. package/dist/packlets/ai-assist/converters.js.map +1 -0
  10. package/dist/packlets/ai-assist/endpoint.js +78 -0
  11. package/dist/packlets/ai-assist/endpoint.js.map +1 -0
  12. package/dist/packlets/ai-assist/imageOptionsResolver.js +212 -0
  13. package/dist/packlets/ai-assist/imageOptionsResolver.js.map +1 -0
  14. package/dist/packlets/ai-assist/index.js +7 -3
  15. package/dist/packlets/ai-assist/index.js.map +1 -0
  16. package/dist/packlets/ai-assist/jsonCompletion.js +95 -0
  17. package/dist/packlets/ai-assist/jsonCompletion.js.map +1 -0
  18. package/dist/packlets/ai-assist/jsonResponse.js +149 -0
  19. package/dist/packlets/ai-assist/jsonResponse.js.map +1 -0
  20. package/dist/packlets/ai-assist/model.js +21 -4
  21. package/dist/packlets/ai-assist/model.js.map +1 -0
  22. package/dist/packlets/ai-assist/registry.js +235 -10
  23. package/dist/packlets/ai-assist/registry.js.map +1 -0
  24. package/dist/packlets/ai-assist/sseParser.js +123 -0
  25. package/dist/packlets/ai-assist/sseParser.js.map +1 -0
  26. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +197 -0
  27. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -0
  28. package/dist/packlets/ai-assist/streamingAdapters/common.js +79 -0
  29. package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -0
  30. package/dist/packlets/ai-assist/streamingAdapters/gemini.js +172 -0
  31. package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -0
  32. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +165 -0
  33. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -0
  34. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +179 -0
  35. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -0
  36. package/dist/packlets/ai-assist/streamingAdapters/proxy.js +163 -0
  37. package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -0
  38. package/dist/packlets/ai-assist/streamingClient.js +116 -0
  39. package/dist/packlets/ai-assist/streamingClient.js.map +1 -0
  40. package/dist/packlets/ai-assist/thinkingOptionsResolver.js +265 -0
  41. package/dist/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -0
  42. package/dist/packlets/ai-assist/toolFormats.js.map +1 -0
  43. package/dist/packlets/conversion/converters.js +35 -1
  44. package/dist/packlets/conversion/converters.js.map +1 -0
  45. package/dist/packlets/conversion/index.js.map +1 -0
  46. package/dist/packlets/crypto-utils/constants.js.map +1 -0
  47. package/dist/packlets/crypto-utils/converters.js +24 -4
  48. package/dist/packlets/crypto-utils/converters.js.map +1 -0
  49. package/dist/packlets/crypto-utils/directEncryptionProvider.js.map +1 -0
  50. package/dist/packlets/crypto-utils/encryptedFile.js.map +1 -0
  51. package/dist/packlets/crypto-utils/hpkeProvider.js +333 -0
  52. package/dist/packlets/crypto-utils/hpkeProvider.js.map +1 -0
  53. package/dist/packlets/crypto-utils/index.browser.js +7 -0
  54. package/dist/packlets/crypto-utils/index.browser.js.map +1 -0
  55. package/dist/packlets/crypto-utils/index.js +6 -0
  56. package/dist/packlets/crypto-utils/index.js.map +1 -0
  57. package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js +71 -0
  58. package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js.map +1 -0
  59. package/dist/packlets/crypto-utils/keystore/converters.js +103 -11
  60. package/dist/packlets/crypto-utils/keystore/converters.js.map +1 -0
  61. package/dist/packlets/crypto-utils/keystore/index.js +1 -0
  62. package/dist/packlets/crypto-utils/keystore/index.js.map +1 -0
  63. package/dist/packlets/crypto-utils/keystore/keyStore.js +618 -118
  64. package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -0
  65. package/dist/packlets/crypto-utils/keystore/model.js +22 -1
  66. package/dist/packlets/crypto-utils/keystore/model.js.map +1 -0
  67. package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js +21 -0
  68. package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -0
  69. package/dist/packlets/crypto-utils/model.js +32 -0
  70. package/dist/packlets/crypto-utils/model.js.map +1 -0
  71. package/dist/packlets/crypto-utils/nodeCryptoProvider.js +270 -1
  72. package/dist/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -0
  73. package/dist/packlets/crypto-utils/spkiHelpers.js +130 -0
  74. package/dist/packlets/crypto-utils/spkiHelpers.js.map +1 -0
  75. package/dist/packlets/csv/csvFileHelpers.js +0 -14
  76. package/dist/packlets/csv/csvFileHelpers.js.map +1 -0
  77. package/dist/packlets/csv/csvHelpers.js +14 -0
  78. package/dist/packlets/csv/csvHelpers.js.map +1 -0
  79. package/dist/packlets/csv/index.browser.js +1 -3
  80. package/dist/packlets/csv/index.browser.js.map +1 -0
  81. package/dist/packlets/csv/index.js.map +1 -0
  82. package/dist/packlets/experimental/extendedArray.js.map +1 -0
  83. package/dist/packlets/experimental/formatter.js.map +1 -0
  84. package/dist/packlets/experimental/index.js.map +1 -0
  85. package/dist/packlets/experimental/rangeOf.js.map +1 -0
  86. package/dist/packlets/hash/index.browser.js.map +1 -0
  87. package/dist/packlets/hash/index.js.map +1 -0
  88. package/dist/packlets/hash/index.node.js.map +1 -0
  89. package/dist/packlets/hash/md5Normalizer.browser.js.map +1 -0
  90. package/dist/packlets/hash/md5Normalizer.js.map +1 -0
  91. package/dist/packlets/mustache/index.js.map +1 -0
  92. package/dist/packlets/mustache/interfaces.js.map +1 -0
  93. package/dist/packlets/mustache/mustacheTemplate.js +42 -4
  94. package/dist/packlets/mustache/mustacheTemplate.js.map +1 -0
  95. package/dist/packlets/record-jar/index.browser.js +1 -3
  96. package/dist/packlets/record-jar/index.browser.js.map +1 -0
  97. package/dist/packlets/record-jar/index.js.map +1 -0
  98. package/dist/packlets/record-jar/recordJarFileHelpers.js +0 -18
  99. package/dist/packlets/record-jar/recordJarFileHelpers.js.map +1 -0
  100. package/dist/packlets/record-jar/recordJarHelpers.js +18 -0
  101. package/dist/packlets/record-jar/recordJarHelpers.js.map +1 -0
  102. package/dist/packlets/yaml/converters.js.map +1 -0
  103. package/dist/packlets/yaml/index.js +1 -0
  104. package/dist/packlets/yaml/index.js.map +1 -0
  105. package/dist/packlets/yaml/serializers.js +48 -0
  106. package/dist/packlets/yaml/serializers.js.map +1 -0
  107. package/dist/packlets/zip-file-tree/index.js.map +1 -0
  108. package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js +2 -2
  109. package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -0
  110. package/dist/packlets/zip-file-tree/zipFileTreeWriter.js.map +1 -0
  111. package/dist/ts-extras.d.ts +2869 -154
  112. package/dist/tsdoc-metadata.json +1 -1
  113. package/lib/index.browser.d.ts +4 -2
  114. package/lib/index.browser.d.ts.map +1 -0
  115. package/lib/index.browser.js +8 -3
  116. package/lib/index.browser.js.map +1 -0
  117. package/lib/index.d.ts.map +1 -0
  118. package/lib/index.js.map +1 -0
  119. package/lib/packlets/ai-assist/apiClient.d.ts +99 -16
  120. package/lib/packlets/ai-assist/apiClient.d.ts.map +1 -0
  121. package/lib/packlets/ai-assist/apiClient.js +961 -130
  122. package/lib/packlets/ai-assist/apiClient.js.map +1 -0
  123. package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +89 -0
  124. package/lib/packlets/ai-assist/chatRequestBuilders.d.ts.map +1 -0
  125. package/lib/packlets/ai-assist/chatRequestBuilders.js +195 -0
  126. package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -0
  127. package/lib/packlets/ai-assist/converters.d.ts.map +1 -0
  128. package/lib/packlets/ai-assist/converters.js +2 -1
  129. package/lib/packlets/ai-assist/converters.js.map +1 -0
  130. package/lib/packlets/ai-assist/endpoint.d.ts +28 -0
  131. package/lib/packlets/ai-assist/endpoint.d.ts.map +1 -0
  132. package/lib/packlets/ai-assist/endpoint.js +82 -0
  133. package/lib/packlets/ai-assist/endpoint.js.map +1 -0
  134. package/lib/packlets/ai-assist/imageOptionsResolver.d.ts +74 -0
  135. package/lib/packlets/ai-assist/imageOptionsResolver.d.ts.map +1 -0
  136. package/lib/packlets/ai-assist/imageOptionsResolver.js +216 -0
  137. package/lib/packlets/ai-assist/imageOptionsResolver.js.map +1 -0
  138. package/lib/packlets/ai-assist/index.d.ts +7 -3
  139. package/lib/packlets/ai-assist/index.d.ts.map +1 -0
  140. package/lib/packlets/ai-assist/index.js +21 -1
  141. package/lib/packlets/ai-assist/index.js.map +1 -0
  142. package/lib/packlets/ai-assist/jsonCompletion.d.ts +93 -0
  143. package/lib/packlets/ai-assist/jsonCompletion.d.ts.map +1 -0
  144. package/lib/packlets/ai-assist/jsonCompletion.js +99 -0
  145. package/lib/packlets/ai-assist/jsonCompletion.js.map +1 -0
  146. package/lib/packlets/ai-assist/jsonResponse.d.ts +91 -0
  147. package/lib/packlets/ai-assist/jsonResponse.d.ts.map +1 -0
  148. package/lib/packlets/ai-assist/jsonResponse.js +154 -0
  149. package/lib/packlets/ai-assist/jsonResponse.js.map +1 -0
  150. package/lib/packlets/ai-assist/model.d.ts +720 -7
  151. package/lib/packlets/ai-assist/model.d.ts.map +1 -0
  152. package/lib/packlets/ai-assist/model.js +22 -4
  153. package/lib/packlets/ai-assist/model.js.map +1 -0
  154. package/lib/packlets/ai-assist/registry.d.ts +34 -1
  155. package/lib/packlets/ai-assist/registry.d.ts.map +1 -0
  156. package/lib/packlets/ai-assist/registry.js +238 -11
  157. package/lib/packlets/ai-assist/registry.js.map +1 -0
  158. package/lib/packlets/ai-assist/sseParser.d.ts +45 -0
  159. package/lib/packlets/ai-assist/sseParser.d.ts.map +1 -0
  160. package/lib/packlets/ai-assist/sseParser.js +128 -0
  161. package/lib/packlets/ai-assist/sseParser.js.map +1 -0
  162. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +19 -0
  163. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -0
  164. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +200 -0
  165. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -0
  166. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +83 -0
  167. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -0
  168. package/lib/packlets/ai-assist/streamingAdapters/common.js +83 -0
  169. package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -0
  170. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +20 -0
  171. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -0
  172. package/lib/packlets/ai-assist/streamingAdapters/gemini.js +175 -0
  173. package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -0
  174. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +19 -0
  175. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -0
  176. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +168 -0
  177. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -0
  178. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +20 -0
  179. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -0
  180. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +182 -0
  181. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -0
  182. package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts +34 -0
  183. package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -0
  184. package/lib/packlets/ai-assist/streamingAdapters/proxy.js +166 -0
  185. package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -0
  186. package/lib/packlets/ai-assist/streamingClient.d.ts +33 -0
  187. package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -0
  188. package/lib/packlets/ai-assist/streamingClient.js +121 -0
  189. package/lib/packlets/ai-assist/streamingClient.js.map +1 -0
  190. package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts +71 -0
  191. package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts.map +1 -0
  192. package/lib/packlets/ai-assist/thinkingOptionsResolver.js +270 -0
  193. package/lib/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -0
  194. package/lib/packlets/ai-assist/toolFormats.d.ts.map +1 -0
  195. package/lib/packlets/ai-assist/toolFormats.js.map +1 -0
  196. package/lib/packlets/conversion/converters.d.ts +8 -1
  197. package/lib/packlets/conversion/converters.d.ts.map +1 -0
  198. package/lib/packlets/conversion/converters.js +36 -2
  199. package/lib/packlets/conversion/converters.js.map +1 -0
  200. package/lib/packlets/conversion/index.d.ts.map +1 -0
  201. package/lib/packlets/conversion/index.js.map +1 -0
  202. package/lib/packlets/crypto-utils/constants.d.ts.map +1 -0
  203. package/lib/packlets/crypto-utils/constants.js.map +1 -0
  204. package/lib/packlets/crypto-utils/converters.d.ts +12 -1
  205. package/lib/packlets/crypto-utils/converters.d.ts.map +1 -0
  206. package/lib/packlets/crypto-utils/converters.js +25 -5
  207. package/lib/packlets/crypto-utils/converters.js.map +1 -0
  208. package/lib/packlets/crypto-utils/directEncryptionProvider.d.ts.map +1 -0
  209. package/lib/packlets/crypto-utils/directEncryptionProvider.js.map +1 -0
  210. package/lib/packlets/crypto-utils/encryptedFile.d.ts.map +1 -0
  211. package/lib/packlets/crypto-utils/encryptedFile.js.map +1 -0
  212. package/lib/packlets/crypto-utils/hpkeProvider.d.ts +142 -0
  213. package/lib/packlets/crypto-utils/hpkeProvider.d.ts.map +1 -0
  214. package/lib/packlets/crypto-utils/hpkeProvider.js +337 -0
  215. package/lib/packlets/crypto-utils/hpkeProvider.js.map +1 -0
  216. package/lib/packlets/crypto-utils/index.browser.d.ts +3 -0
  217. package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -0
  218. package/lib/packlets/crypto-utils/index.browser.js +14 -1
  219. package/lib/packlets/crypto-utils/index.browser.js.map +1 -0
  220. package/lib/packlets/crypto-utils/index.d.ts +3 -0
  221. package/lib/packlets/crypto-utils/index.d.ts.map +1 -0
  222. package/lib/packlets/crypto-utils/index.js +13 -1
  223. package/lib/packlets/crypto-utils/index.js.map +1 -0
  224. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts +54 -0
  225. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts.map +1 -0
  226. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js +74 -0
  227. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js.map +1 -0
  228. package/lib/packlets/crypto-utils/keystore/converters.d.ts +68 -6
  229. package/lib/packlets/crypto-utils/keystore/converters.d.ts.map +1 -0
  230. package/lib/packlets/crypto-utils/keystore/converters.js +101 -9
  231. package/lib/packlets/crypto-utils/keystore/converters.js.map +1 -0
  232. package/lib/packlets/crypto-utils/keystore/index.d.ts +1 -0
  233. package/lib/packlets/crypto-utils/keystore/index.d.ts.map +1 -0
  234. package/lib/packlets/crypto-utils/keystore/index.js +1 -0
  235. package/lib/packlets/crypto-utils/keystore/index.js.map +1 -0
  236. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +198 -13
  237. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -0
  238. package/lib/packlets/crypto-utils/keystore/keyStore.js +624 -124
  239. package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -0
  240. package/lib/packlets/crypto-utils/keystore/model.d.ts +268 -19
  241. package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -0
  242. package/lib/packlets/crypto-utils/keystore/model.js +24 -2
  243. package/lib/packlets/crypto-utils/keystore/model.js.map +1 -0
  244. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts +50 -0
  245. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts.map +1 -0
  246. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js +22 -0
  247. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -0
  248. package/lib/packlets/crypto-utils/model.d.ts +338 -10
  249. package/lib/packlets/crypto-utils/model.d.ts.map +1 -0
  250. package/lib/packlets/crypto-utils/model.js +33 -1
  251. package/lib/packlets/crypto-utils/model.js.map +1 -0
  252. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +110 -2
  253. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts.map +1 -0
  254. package/lib/packlets/crypto-utils/nodeCryptoProvider.js +269 -0
  255. package/lib/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -0
  256. package/lib/packlets/crypto-utils/spkiHelpers.d.ts +53 -0
  257. package/lib/packlets/crypto-utils/spkiHelpers.d.ts.map +1 -0
  258. package/lib/packlets/crypto-utils/spkiHelpers.js +136 -0
  259. package/lib/packlets/crypto-utils/spkiHelpers.js.map +1 -0
  260. package/lib/packlets/csv/csvFileHelpers.d.ts +0 -10
  261. package/lib/packlets/csv/csvFileHelpers.d.ts.map +1 -0
  262. package/lib/packlets/csv/csvFileHelpers.js +0 -15
  263. package/lib/packlets/csv/csvFileHelpers.js.map +1 -0
  264. package/lib/packlets/csv/csvHelpers.d.ts +10 -0
  265. package/lib/packlets/csv/csvHelpers.d.ts.map +1 -0
  266. package/lib/packlets/csv/csvHelpers.js +15 -0
  267. package/lib/packlets/csv/csvHelpers.js.map +1 -0
  268. package/lib/packlets/csv/index.browser.d.ts +0 -1
  269. package/lib/packlets/csv/index.browser.d.ts.map +1 -0
  270. package/lib/packlets/csv/index.browser.js +1 -5
  271. package/lib/packlets/csv/index.browser.js.map +1 -0
  272. package/lib/packlets/csv/index.d.ts.map +1 -0
  273. package/lib/packlets/csv/index.js.map +1 -0
  274. package/lib/packlets/experimental/extendedArray.d.ts.map +1 -0
  275. package/lib/packlets/experimental/extendedArray.js.map +1 -0
  276. package/lib/packlets/experimental/formatter.d.ts.map +1 -0
  277. package/lib/packlets/experimental/formatter.js.map +1 -0
  278. package/lib/packlets/experimental/index.d.ts.map +1 -0
  279. package/lib/packlets/experimental/index.js.map +1 -0
  280. package/lib/packlets/experimental/rangeOf.d.ts.map +1 -0
  281. package/lib/packlets/experimental/rangeOf.js.map +1 -0
  282. package/lib/packlets/hash/index.browser.d.ts.map +1 -0
  283. package/lib/packlets/hash/index.browser.js.map +1 -0
  284. package/lib/packlets/hash/index.d.ts.map +1 -0
  285. package/lib/packlets/hash/index.js.map +1 -0
  286. package/lib/packlets/hash/index.node.d.ts.map +1 -0
  287. package/lib/packlets/hash/index.node.js.map +1 -0
  288. package/lib/packlets/hash/md5Normalizer.browser.d.ts.map +1 -0
  289. package/lib/packlets/hash/md5Normalizer.browser.js.map +1 -0
  290. package/lib/packlets/hash/md5Normalizer.d.ts.map +1 -0
  291. package/lib/packlets/hash/md5Normalizer.js.map +1 -0
  292. package/lib/packlets/mustache/index.d.ts +1 -1
  293. package/lib/packlets/mustache/index.d.ts.map +1 -0
  294. package/lib/packlets/mustache/index.js.map +1 -0
  295. package/lib/packlets/mustache/interfaces.d.ts +34 -0
  296. package/lib/packlets/mustache/interfaces.d.ts.map +1 -0
  297. package/lib/packlets/mustache/interfaces.js.map +1 -0
  298. package/lib/packlets/mustache/mustacheTemplate.d.ts +2 -0
  299. package/lib/packlets/mustache/mustacheTemplate.d.ts.map +1 -0
  300. package/lib/packlets/mustache/mustacheTemplate.js +42 -4
  301. package/lib/packlets/mustache/mustacheTemplate.js.map +1 -0
  302. package/lib/packlets/record-jar/index.browser.d.ts +0 -1
  303. package/lib/packlets/record-jar/index.browser.d.ts.map +1 -0
  304. package/lib/packlets/record-jar/index.browser.js +1 -5
  305. package/lib/packlets/record-jar/index.browser.js.map +1 -0
  306. package/lib/packlets/record-jar/index.d.ts.map +1 -0
  307. package/lib/packlets/record-jar/index.js.map +1 -0
  308. package/lib/packlets/record-jar/recordJarFileHelpers.d.ts +0 -11
  309. package/lib/packlets/record-jar/recordJarFileHelpers.d.ts.map +1 -0
  310. package/lib/packlets/record-jar/recordJarFileHelpers.js +0 -19
  311. package/lib/packlets/record-jar/recordJarFileHelpers.js.map +1 -0
  312. package/lib/packlets/record-jar/recordJarHelpers.d.ts +11 -0
  313. package/lib/packlets/record-jar/recordJarHelpers.d.ts.map +1 -0
  314. package/lib/packlets/record-jar/recordJarHelpers.js +19 -0
  315. package/lib/packlets/record-jar/recordJarHelpers.js.map +1 -0
  316. package/lib/packlets/yaml/converters.d.ts.map +1 -0
  317. package/lib/packlets/yaml/converters.js.map +1 -0
  318. package/lib/packlets/yaml/index.d.ts +1 -0
  319. package/lib/packlets/yaml/index.d.ts.map +1 -0
  320. package/lib/packlets/yaml/index.js +1 -0
  321. package/lib/packlets/yaml/index.js.map +1 -0
  322. package/lib/packlets/yaml/serializers.d.ts +45 -0
  323. package/lib/packlets/yaml/serializers.d.ts.map +1 -0
  324. package/lib/packlets/yaml/serializers.js +84 -0
  325. package/lib/packlets/yaml/serializers.js.map +1 -0
  326. package/lib/packlets/zip-file-tree/index.d.ts.map +1 -0
  327. package/lib/packlets/zip-file-tree/index.js.map +1 -0
  328. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts +2 -2
  329. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts.map +1 -0
  330. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js +2 -2
  331. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -0
  332. package/lib/packlets/zip-file-tree/zipFileTreeWriter.d.ts.map +1 -0
  333. package/lib/packlets/zip-file-tree/zipFileTreeWriter.js.map +1 -0
  334. package/package.json +16 -15
@@ -20,7 +20,11 @@
20
20
  // SOFTWARE.
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.callProviderCompletion = callProviderCompletion;
23
+ exports.callProviderImageGeneration = callProviderImageGeneration;
24
+ exports.callProviderListModels = callProviderListModels;
25
+ exports.callProxiedListModels = callProxiedListModels;
23
26
  exports.callProxiedCompletion = callProxiedCompletion;
27
+ exports.callProxiedImageGeneration = callProxiedImageGeneration;
24
28
  /**
25
29
  * Chat completion client for AI assist with support for multiple provider APIs.
26
30
  *
@@ -36,31 +40,20 @@ exports.callProxiedCompletion = callProxiedCompletion;
36
40
  const ts_json_base_1 = require("@fgv/ts-json-base");
37
41
  const ts_utils_1 = require("@fgv/ts-utils");
38
42
  const model_1 = require("./model");
43
+ const thinkingOptionsResolver_1 = require("./thinkingOptionsResolver");
44
+ const chatRequestBuilders_1 = require("./chatRequestBuilders");
45
+ const endpoint_1 = require("./endpoint");
46
+ const registry_1 = require("./registry");
47
+ const imageOptionsResolver_1 = require("./imageOptionsResolver");
39
48
  const toolFormats_1 = require("./toolFormats");
40
49
  // ============================================================================
41
50
  // Shared helpers
42
51
  // ============================================================================
43
- /**
44
- * Builds the messages array from prompt + optional correction messages.
45
- * @internal
46
- */
47
- function buildMessages(prompt, additionalMessages) {
48
- const messages = [
49
- { role: 'system', content: prompt.system },
50
- { role: 'user', content: prompt.user }
51
- ];
52
- if (additionalMessages) {
53
- for (const msg of additionalMessages) {
54
- messages.push({ role: msg.role, content: msg.content });
55
- }
56
- }
57
- return messages;
58
- }
59
52
  /**
60
53
  * Makes an HTTP request and returns the parsed JSON, or a failure.
61
54
  * @internal
62
55
  */
63
- async function fetchJson(url, headers, body, logger) {
56
+ async function fetchJson(url, headers, body, logger, signal) {
64
57
  /* c8 ignore next 1 - optional logger */
65
58
  logger === null || logger === void 0 ? void 0 : logger.detail(`AI API request: POST ${url}`);
66
59
  let response;
@@ -68,7 +61,8 @@ async function fetchJson(url, headers, body, logger) {
68
61
  response = await fetch(url, {
69
62
  method: 'POST',
70
63
  headers: Object.assign({ 'Content-Type': 'application/json' }, headers),
71
- body: JSON.stringify(body)
64
+ body: JSON.stringify(body),
65
+ signal
72
66
  });
73
67
  }
74
68
  catch (err) {
@@ -101,6 +95,143 @@ async function fetchJson(url, headers, body, logger) {
101
95
  }
102
96
  return (0, ts_utils_1.succeed)(json);
103
97
  }
98
+ /**
99
+ * Makes a multipart/form-data POST request and returns the parsed JSON, or a
100
+ * failure. The Content-Type header (with boundary) is set automatically by
101
+ * `fetch` from the `FormData` body — callers must NOT pass it explicitly.
102
+ * @internal
103
+ */
104
+ async function fetchMultipart(url, headers, body, logger, signal) {
105
+ /* c8 ignore next 1 - optional logger */
106
+ logger === null || logger === void 0 ? void 0 : logger.detail(`AI API request: POST ${url} (multipart)`);
107
+ let response;
108
+ try {
109
+ response = await fetch(url, {
110
+ method: 'POST',
111
+ headers,
112
+ body,
113
+ signal
114
+ });
115
+ }
116
+ catch (err) {
117
+ /* c8 ignore next 1 - defensive: fetch errors are always Error instances in practice */
118
+ const detail = err instanceof Error ? err.message : String(err);
119
+ /* c8 ignore next 1 - optional logger */
120
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI API request failed: ${detail}`);
121
+ return (0, ts_utils_1.fail)(`AI API request failed: ${detail}`);
122
+ }
123
+ if (!response.ok) {
124
+ const errorText = await response.text().catch(() => 'unknown error');
125
+ /* c8 ignore next 1 - optional logger */
126
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI API returned ${response.status}: ${errorText}`);
127
+ return (0, ts_utils_1.fail)(`AI API returned ${response.status}: ${errorText}`);
128
+ }
129
+ /* c8 ignore next 1 - optional logger */
130
+ logger === null || logger === void 0 ? void 0 : logger.detail(`AI API response: ${response.status}`);
131
+ let json;
132
+ try {
133
+ json = await response.json();
134
+ }
135
+ catch /* c8 ignore start - defensive: response.json() failure on a 2xx */ (_a) {
136
+ logger === null || logger === void 0 ? void 0 : logger.error('AI API returned invalid JSON response');
137
+ return (0, ts_utils_1.fail)('AI API returned invalid JSON response');
138
+ } /* c8 ignore stop */
139
+ /* c8 ignore next 5 - defensive: provider returning non-object JSON on a 2xx */
140
+ if (!(0, ts_json_base_1.isJsonObject)(json)) {
141
+ logger === null || logger === void 0 ? void 0 : logger.error('AI API returned non-object JSON response');
142
+ return (0, ts_utils_1.fail)('AI API returned non-object JSON response');
143
+ }
144
+ return (0, ts_utils_1.succeed)(json);
145
+ }
146
+ /**
147
+ * Decodes a base64-encoded image attachment into a `Blob` suitable for use as
148
+ * a multipart file field. On Node hands the `Buffer` straight to `Blob`
149
+ * (Buffer extends Uint8Array) to skip an intermediate copy; falls back to
150
+ * `atob` in browsers. Inputs come from `FileReader` or prior provider
151
+ * responses, which are trusted to be valid. Note that Node's
152
+ * `Buffer.from(..., 'base64')` silently strips invalid characters rather
153
+ * than throwing, so failures are only observable in the browser path.
154
+ * @internal
155
+ */
156
+ function attachmentToBlob(attachment) {
157
+ if (typeof Buffer !== 'undefined') {
158
+ return (0, ts_utils_1.succeed)(new Blob([Buffer.from(attachment.base64, 'base64')], { type: attachment.mimeType }));
159
+ }
160
+ /* c8 ignore start - Browser-only fallback cannot be tested in Node.js environment */
161
+ try {
162
+ const binary = atob(attachment.base64);
163
+ const bytes = new Uint8Array(binary.length);
164
+ for (let i = 0; i < binary.length; i++) {
165
+ bytes[i] = binary.charCodeAt(i);
166
+ }
167
+ return (0, ts_utils_1.succeed)(new Blob([bytes], { type: attachment.mimeType }));
168
+ }
169
+ catch (e) {
170
+ const message = e instanceof Error ? e.message : String(e);
171
+ return (0, ts_utils_1.fail)(`Invalid base64: ${message}`);
172
+ }
173
+ /* c8 ignore stop */
174
+ }
175
+ /**
176
+ * Maps a MIME type to a sensible file extension for multipart filenames.
177
+ * @internal
178
+ */
179
+ function extensionForMimeType(mimeType) {
180
+ switch (mimeType) {
181
+ case 'image/png':
182
+ return 'png';
183
+ case 'image/jpeg':
184
+ case 'image/jpg':
185
+ return 'jpg';
186
+ case 'image/webp':
187
+ return 'webp';
188
+ case 'image/gif':
189
+ return 'gif';
190
+ default:
191
+ return 'bin';
192
+ }
193
+ }
194
+ /**
195
+ * Makes an HTTP GET request and returns the parsed JSON, or a failure.
196
+ * @internal
197
+ */
198
+ async function fetchGetJson(url, headers, logger, signal) {
199
+ /* c8 ignore next 1 - optional logger */
200
+ logger === null || logger === void 0 ? void 0 : logger.detail(`AI API request: GET ${url}`);
201
+ let response;
202
+ try {
203
+ response = await fetch(url, { method: 'GET', headers, signal });
204
+ }
205
+ catch (err) {
206
+ /* c8 ignore next 1 - defensive: fetch errors are always Error instances in practice */
207
+ const detail = err instanceof Error ? err.message : String(err);
208
+ /* c8 ignore next 1 - optional logger */
209
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI API request failed: ${detail}`);
210
+ return (0, ts_utils_1.fail)(`AI API request failed: ${detail}`);
211
+ }
212
+ if (!response.ok) {
213
+ const errorText = await response.text().catch(() => 'unknown error');
214
+ /* c8 ignore next 1 - optional logger */
215
+ logger === null || logger === void 0 ? void 0 : logger.error(`AI API returned ${response.status}: ${errorText}`);
216
+ return (0, ts_utils_1.fail)(`AI API returned ${response.status}: ${errorText}`);
217
+ }
218
+ /* c8 ignore next 1 - optional logger */
219
+ logger === null || logger === void 0 ? void 0 : logger.detail(`AI API response: ${response.status}`);
220
+ let json;
221
+ try {
222
+ json = await response.json();
223
+ }
224
+ catch /* c8 ignore start - defensive: response.json() failure on a 2xx */ (_a) {
225
+ logger === null || logger === void 0 ? void 0 : logger.error('AI API returned invalid JSON response');
226
+ return (0, ts_utils_1.fail)('AI API returned invalid JSON response');
227
+ } /* c8 ignore stop */
228
+ /* c8 ignore next 5 - defensive: provider returning non-object JSON on a 2xx */
229
+ if (!(0, ts_json_base_1.isJsonObject)(json)) {
230
+ logger === null || logger === void 0 ? void 0 : logger.error('AI API returned non-object JSON response');
231
+ return (0, ts_utils_1.fail)('AI API returned non-object JSON response');
232
+ }
233
+ return (0, ts_utils_1.succeed)(json);
234
+ }
104
235
  const openAiMessage = ts_utils_1.Validators.object({
105
236
  content: ts_utils_1.Validators.string
106
237
  });
@@ -125,13 +256,6 @@ const responsesApiResponse = ts_utils_1.Validators.object({
125
256
  output: ts_utils_1.Validators.arrayOf(responsesApiOutputItem).withConstraint((arr) => arr.length > 0),
126
257
  status: ts_utils_1.Validators.string
127
258
  });
128
- const anthropicContentBlock = ts_utils_1.Validators.object({
129
- text: ts_utils_1.Validators.string
130
- });
131
- const anthropicResponse = ts_utils_1.Validators.object({
132
- content: ts_utils_1.Validators.arrayOf(anthropicContentBlock).withConstraint((arr) => arr.length > 0),
133
- stop_reason: ts_utils_1.Validators.string
134
- });
135
259
  const geminiPart = ts_utils_1.Validators.object({
136
260
  text: ts_utils_1.Validators.string
137
261
  });
@@ -153,16 +277,21 @@ const geminiResponse = ts_utils_1.Validators.object({
153
277
  * Works for xAI Grok, OpenAI, Groq, and Mistral.
154
278
  * @internal
155
279
  */
156
- async function callOpenAiCompletion(config, prompt, additionalMessages, temperature = 0.7, logger) {
280
+ async function callOpenAiCompletion(config, prompt, additionalMessages, temperature = 0.7, logger, signal, resolvedThinking) {
281
+ var _a;
157
282
  const url = `${config.baseUrl}/chat/completions`;
158
- const messages = buildMessages(prompt, additionalMessages);
159
- const body = { model: config.model, messages, temperature };
160
- const headers = {
161
- Authorization: `Bearer ${config.apiKey}`
162
- };
283
+ const messages = (0, chatRequestBuilders_1.buildMessages)(prompt.system, (0, chatRequestBuilders_1.buildOpenAiChatUserContent)(prompt), {
284
+ tail: additionalMessages
285
+ });
286
+ const effort = (_a = resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.openAiEffort) !== null && _a !== void 0 ? _a : resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.xaiEffort;
287
+ const body = Object.assign(Object.assign({ model: config.model, messages }, (effort === undefined || effort === 'none' ? { temperature } : {})), (effort !== undefined && config.model !== 'grok-4' ? { reasoning_effort: effort } : {}));
288
+ if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.otherParams) !== undefined) {
289
+ Object.assign(body, resolvedThinking.otherParams);
290
+ }
291
+ const headers = (0, endpoint_1.bearerAuthHeader)(config.apiKey);
163
292
  /* c8 ignore next 1 - optional logger */
164
293
  logger === null || logger === void 0 ? void 0 : logger.info(`OpenAI completion: model=${config.model}`);
165
- const jsonResult = await fetchJson(url, headers, body, logger);
294
+ const jsonResult = await fetchJson(url, headers, body, logger, signal);
166
295
  if (jsonResult.isFailure()) {
167
296
  return (0, ts_utils_1.fail)(jsonResult.message);
168
297
  }
@@ -201,21 +330,21 @@ function extractResponsesApiText(output) {
201
330
  * Used when tools are configured for an openai-format provider.
202
331
  * @internal
203
332
  */
204
- async function callOpenAiResponsesCompletion(config, prompt, tools, additionalMessages, temperature = 0.7, logger) {
333
+ async function callOpenAiResponsesCompletion(config, prompt, tools, additionalMessages, temperature = 0.7, logger, signal, resolvedThinking) {
334
+ var _a;
205
335
  const url = `${config.baseUrl}/responses`;
206
- const input = buildMessages(prompt, additionalMessages);
207
- const body = {
208
- model: config.model,
209
- input,
210
- tools: (0, toolFormats_1.toResponsesApiTools)(tools),
211
- temperature
212
- };
213
- const headers = {
214
- Authorization: `Bearer ${config.apiKey}`
215
- };
336
+ const input = (0, chatRequestBuilders_1.buildMessages)(prompt.system, (0, chatRequestBuilders_1.buildOpenAiResponsesUserContent)(prompt), {
337
+ tail: additionalMessages
338
+ });
339
+ const effort = (_a = resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.openAiEffort) !== null && _a !== void 0 ? _a : resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.xaiEffort;
340
+ const body = Object.assign(Object.assign({ model: config.model, input, tools: (0, toolFormats_1.toResponsesApiTools)(tools) }, (effort === undefined || effort === 'none' ? { temperature } : {})), (effort !== undefined && config.model !== 'grok-4' ? { reasoning: { effort } } : {}));
341
+ if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.otherParams) !== undefined) {
342
+ Object.assign(body, resolvedThinking.otherParams);
343
+ }
344
+ const headers = (0, endpoint_1.bearerAuthHeader)(config.apiKey);
216
345
  /* c8 ignore next 1 - optional logger */
217
346
  logger === null || logger === void 0 ? void 0 : logger.info(`OpenAI Responses API: model=${config.model}, tools=${tools.map((t) => t.type).join(',')}`);
218
- const jsonResult = await fetchJson(url, headers, body, logger);
347
+ const jsonResult = await fetchJson(url, headers, body, logger, signal);
219
348
  if (jsonResult.isFailure()) {
220
349
  return (0, ts_utils_1.fail)(jsonResult.message);
221
350
  }
@@ -254,31 +383,18 @@ function extractAnthropicText(content) {
254
383
  }
255
384
  return (0, ts_utils_1.succeed)(textParts.join(''));
256
385
  }
257
- /**
258
- * Calls the Anthropic Messages API.
259
- * When tools are configured, includes them in the request and handles
260
- * mixed content block responses.
261
- * @internal
262
- */
263
- async function callAnthropicCompletion(config, prompt, additionalMessages, temperature = 0.7, logger, tools) {
386
+ /** Calls the Anthropic Messages API with optional tool support. @internal */
387
+ async function callAnthropicCompletion(config, prompt, additionalMessages, temperature = 0.7, logger, tools, signal, resolvedThinking) {
264
388
  const url = `${config.baseUrl}/messages`;
265
- // Anthropic uses system as a top-level field, not in messages
266
- const messages = [{ role: 'user', content: prompt.user }];
267
- if (additionalMessages) {
268
- for (const msg of additionalMessages) {
269
- // Anthropic doesn't have a system role in messages
270
- if (msg.role !== 'system') {
271
- messages.push({ role: msg.role, content: msg.content });
272
- }
273
- }
389
+ const messages = (0, chatRequestBuilders_1.buildAnthropicMessages)(prompt, { tail: additionalMessages });
390
+ const body = Object.assign({ model: config.model, system: prompt.system, messages, max_tokens: 4096 }, ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.anthropicEffort) === undefined ? { temperature } : {}));
391
+ if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.anthropicEffort) !== undefined) {
392
+ body.thinking = { type: 'enabled' };
393
+ body.output_config = { effort: resolvedThinking.anthropicEffort };
394
+ }
395
+ if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.otherParams) !== undefined) {
396
+ Object.assign(body, resolvedThinking.otherParams);
274
397
  }
275
- const body = {
276
- model: config.model,
277
- system: prompt.system,
278
- messages,
279
- max_tokens: 4096,
280
- temperature
281
- };
282
398
  if (tools && tools.length > 0) {
283
399
  body.tools = (0, toolFormats_1.toAnthropicTools)(tools);
284
400
  /* c8 ignore next 3 - optional logger diagnostic output */
@@ -293,32 +409,22 @@ async function callAnthropicCompletion(config, prompt, additionalMessages, tempe
293
409
  'anthropic-version': '2023-06-01',
294
410
  'anthropic-dangerous-direct-browser-access': 'true'
295
411
  };
296
- const jsonResult = await fetchJson(url, headers, body, logger);
412
+ const jsonResult = await fetchJson(url, headers, body, logger, signal);
297
413
  if (jsonResult.isFailure()) {
298
414
  return (0, ts_utils_1.fail)(jsonResult.message);
299
415
  }
300
- // When tools are used, the response content is a mixed array of block types.
301
- // We need to extract text from all text blocks.
302
- if (tools && tools.length > 0) {
303
- const rawContent = jsonResult.value.content;
304
- const stopReason = jsonResult.value.stop_reason;
305
- if (!Array.isArray(rawContent)) {
306
- return (0, ts_utils_1.fail)('Anthropic API response: content is not an array');
307
- }
308
- return extractAnthropicText(rawContent).onSuccess((text) => (0, ts_utils_1.succeed)({
309
- content: text,
310
- truncated: stopReason === 'max_tokens'
311
- }));
416
+ const rawContent = jsonResult.value.content;
417
+ const stopReason = jsonResult.value.stop_reason;
418
+ if (!Array.isArray(rawContent)) {
419
+ return (0, ts_utils_1.fail)('Anthropic API response: content is not an array');
312
420
  }
313
- return anthropicResponse
314
- .validate(jsonResult.value)
315
- .withErrorFormat((msg) => `Anthropic API response: ${msg}`)
316
- .onSuccess((response) => {
317
- return (0, ts_utils_1.succeed)({
318
- content: response.content[0].text,
319
- truncated: response.stop_reason === 'max_tokens'
320
- });
321
- });
421
+ if (typeof stopReason !== 'string') {
422
+ return (0, ts_utils_1.fail)('Anthropic API response: stop_reason is missing or not a string');
423
+ }
424
+ return extractAnthropicText(rawContent).onSuccess((text) => (0, ts_utils_1.succeed)({
425
+ content: text,
426
+ truncated: stopReason === 'max_tokens'
427
+ }));
322
428
  }
323
429
  // ============================================================================
324
430
  // Google Gemini adapter
@@ -328,26 +434,20 @@ async function callAnthropicCompletion(config, prompt, additionalMessages, tempe
328
434
  * When tools are configured, includes Google Search grounding.
329
435
  * @internal
330
436
  */
331
- async function callGeminiCompletion(config, prompt, additionalMessages, temperature = 0.7, logger, tools) {
437
+ async function callGeminiCompletion(config, prompt, additionalMessages, temperature = 0.7, logger, tools, signal, resolvedThinking) {
332
438
  const url = `${config.baseUrl}/models/${config.model}:generateContent`;
333
- // Gemini uses 'contents' with 'parts', and 'model' role instead of 'assistant'
334
- const contents = [
335
- { role: 'user', parts: [{ text: prompt.user }] }
336
- ];
337
- if (additionalMessages) {
338
- for (const msg of additionalMessages) {
339
- if (msg.role !== 'system') {
340
- contents.push({
341
- role: msg.role === 'assistant' ? 'model' : msg.role,
342
- parts: [{ text: msg.content }]
343
- });
344
- }
345
- }
439
+ const contents = (0, chatRequestBuilders_1.buildGeminiContents)(prompt, { tail: additionalMessages });
440
+ const generationConfig = { temperature };
441
+ if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.geminiThinkingBudget) !== undefined) {
442
+ generationConfig.thinkingConfig = { thinkingBudget: resolvedThinking.geminiThinkingBudget };
443
+ }
444
+ if ((resolvedThinking === null || resolvedThinking === void 0 ? void 0 : resolvedThinking.otherParams) !== undefined) {
445
+ Object.assign(generationConfig, resolvedThinking.otherParams);
346
446
  }
347
447
  const body = {
348
448
  systemInstruction: { parts: [{ text: prompt.system }] },
349
449
  contents,
350
- generationConfig: { temperature }
450
+ generationConfig
351
451
  };
352
452
  if (tools && tools.length > 0) {
353
453
  body.tools = (0, toolFormats_1.toGeminiTools)(tools);
@@ -361,7 +461,7 @@ async function callGeminiCompletion(config, prompt, additionalMessages, temperat
361
461
  const headers = {
362
462
  'x-goog-api-key': config.apiKey
363
463
  };
364
- const jsonResult = await fetchJson(url, headers, body, logger);
464
+ const jsonResult = await fetchJson(url, headers, body, logger, signal);
365
465
  if (jsonResult.isFailure()) {
366
466
  return (0, ts_utils_1.fail)(jsonResult.message);
367
467
  }
@@ -381,32 +481,52 @@ async function callGeminiCompletion(config, prompt, additionalMessages, temperat
381
481
  // ============================================================================
382
482
  /**
383
483
  * Calls the appropriate chat completion API for a given provider.
384
- *
385
- * Routes based on the provider descriptor's `apiFormat` field:
386
- * - `'openai'` for xAI, OpenAI, Groq, Mistral
387
- * - `'anthropic'` for Anthropic Claude
388
- * - `'gemini'` for Google Gemini
389
- *
390
- * When tools are provided and the provider supports them:
391
- * - OpenAI-format providers switch to the Responses API
392
- * - Anthropic includes tools in the Messages API request
393
- * - Gemini includes Google Search grounding
394
- *
484
+ * Routes by `apiFormat`: `'openai'` (xAI/OpenAI/Groq/Mistral — switches to Responses API when
485
+ * tools are set), `'anthropic'`, or `'gemini'`.
395
486
  * @param params - Request parameters including descriptor, API key, prompt, and optional tools
396
487
  * @returns The completion response with content and truncation status, or a failure
397
488
  * @public
398
489
  */
399
490
  async function callProviderCompletion(params) {
400
- const { descriptor, apiKey, prompt, additionalMessages, temperature = 0.7, modelOverride, logger, tools } = params;
401
- if (!descriptor.baseUrl) {
402
- return (0, ts_utils_1.fail)(`provider "${descriptor.id}" has no API endpoint configured`);
491
+ var _a;
492
+ const { descriptor, apiKey, prompt, additionalMessages, temperature, modelOverride, logger, tools, signal, endpoint, thinking } = params;
493
+ const baseUrlResult = (0, endpoint_1.resolveEffectiveBaseUrl)(descriptor, endpoint);
494
+ if (baseUrlResult.isFailure()) {
495
+ return (0, ts_utils_1.fail)(baseUrlResult.message);
496
+ }
497
+ if (prompt.attachments.length > 0 && !descriptor.acceptsImageInput) {
498
+ return (0, ts_utils_1.fail)(`provider "${descriptor.id}" does not accept image input`);
403
499
  }
404
500
  const hasTools = tools !== undefined && tools.length > 0;
405
- const modelContext = hasTools ? 'tools' : undefined;
501
+ const discriminator = (0, thinkingOptionsResolver_1.providerDiscriminatorForId)(descriptor.id);
502
+ const hasThinkingConfig = discriminator !== undefined &&
503
+ ((thinking === null || thinking === void 0 ? void 0 : thinking.effort) !== undefined ||
504
+ ((_a = thinking === null || thinking === void 0 ? void 0 : thinking.providers) === null || _a === void 0 ? void 0 : _a.some((b) => b.provider === 'other' || b.provider === discriminator)) === true);
505
+ const modelContext = hasThinkingConfig ? 'thinking' : hasTools ? 'tools' : undefined;
506
+ const model = (0, model_1.resolveModel)(modelOverride !== null && modelOverride !== void 0 ? modelOverride : descriptor.defaultModel, modelContext);
507
+ if (model.length === 0) {
508
+ return (0, ts_utils_1.fail)(`provider "${descriptor.id}": no model resolved; pass modelOverride or set descriptor.defaultModel`);
509
+ }
510
+ let resolvedThinking;
511
+ if (thinking !== undefined) {
512
+ if (discriminator !== undefined) {
513
+ const mergeResult = (0, thinkingOptionsResolver_1.mergeThinkingConfig)(thinking, model, discriminator);
514
+ /* c8 ignore next 3 - mergeThinkingConfig always succeeds; defensive guard */
515
+ if (mergeResult.isFailure()) {
516
+ return (0, ts_utils_1.fail)(mergeResult.message);
517
+ }
518
+ resolvedThinking = mergeResult.value;
519
+ const conflictResult = (0, thinkingOptionsResolver_1.checkTemperatureConflict)(resolvedThinking, discriminator, temperature);
520
+ if (conflictResult.isFailure()) {
521
+ return (0, ts_utils_1.fail)(conflictResult.message);
522
+ }
523
+ }
524
+ }
525
+ const effectiveTemperature = temperature !== null && temperature !== void 0 ? temperature : 0.7;
406
526
  const config = {
407
- baseUrl: descriptor.baseUrl,
527
+ baseUrl: baseUrlResult.value,
408
528
  apiKey,
409
- model: (0, model_1.resolveModel)(modelOverride !== null && modelOverride !== void 0 ? modelOverride : descriptor.defaultModel, modelContext)
529
+ model
410
530
  };
411
531
  /* c8 ignore next 8 - optional logger diagnostic output */
412
532
  if (logger) {
@@ -418,19 +538,683 @@ async function callProviderCompletion(params) {
418
538
  switch (descriptor.apiFormat) {
419
539
  case 'openai':
420
540
  if (hasTools) {
421
- return callOpenAiResponsesCompletion(config, prompt, tools, additionalMessages, temperature, logger);
541
+ return callOpenAiResponsesCompletion(config, prompt, tools, additionalMessages, effectiveTemperature, logger, signal, resolvedThinking);
542
+ }
543
+ return callOpenAiCompletion(config, prompt, additionalMessages, effectiveTemperature, logger, signal, resolvedThinking);
544
+ case 'anthropic':
545
+ return callAnthropicCompletion(config, prompt, additionalMessages, effectiveTemperature, logger, tools, signal, resolvedThinking);
546
+ case 'gemini':
547
+ return callGeminiCompletion(config, prompt, additionalMessages, effectiveTemperature, logger, tools, signal, resolvedThinking);
548
+ /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */
549
+ default: {
550
+ const _exhaustive = descriptor.apiFormat;
551
+ return (0, ts_utils_1.fail)(`unsupported API format: ${String(_exhaustive)}`);
552
+ }
553
+ }
554
+ }
555
+ const openAiImageItem = ts_utils_1.Validators.object({
556
+ b64_json: ts_utils_1.Validators.string,
557
+ revised_prompt: ts_utils_1.Validators.string.optional()
558
+ });
559
+ const openAiImageResponse = ts_utils_1.Validators.object({
560
+ data: ts_utils_1.Validators.arrayOf(openAiImageItem).withConstraint((arr) => arr.length > 0)
561
+ });
562
+ const imagenPrediction = ts_utils_1.Validators.object({
563
+ bytesBase64Encoded: ts_utils_1.Validators.string,
564
+ mimeType: ts_utils_1.Validators.string.optional()
565
+ });
566
+ const imagenResponse = ts_utils_1.Validators.object({
567
+ predictions: ts_utils_1.Validators.arrayOf(imagenPrediction).withConstraint((arr) => arr.length > 0)
568
+ });
569
+ const geminiImageInlineData = ts_utils_1.Validators.object({
570
+ mimeType: ts_utils_1.Validators.string,
571
+ data: ts_utils_1.Validators.string
572
+ });
573
+ const geminiImageOutPart = ts_utils_1.Validators.object({
574
+ text: ts_utils_1.Validators.string.optional(),
575
+ inlineData: geminiImageInlineData.optional()
576
+ });
577
+ const geminiImageOutContent = ts_utils_1.Validators.object({
578
+ parts: ts_utils_1.Validators.arrayOf(geminiImageOutPart).withConstraint((arr) => arr.length > 0)
579
+ });
580
+ const geminiImageOutCandidate = ts_utils_1.Validators.object({
581
+ content: geminiImageOutContent,
582
+ finishReason: ts_utils_1.Validators.string.optional()
583
+ });
584
+ const geminiImageOutResponse = ts_utils_1.Validators.object({
585
+ candidates: ts_utils_1.Validators.arrayOf(geminiImageOutCandidate).withConstraint((arr) => arr.length > 0)
586
+ });
587
+ // ---- Proxied image generation response ----
588
+ const proxiedGeneratedImage = ts_utils_1.Validators.object({
589
+ mimeType: ts_utils_1.Validators.string,
590
+ base64: ts_utils_1.Validators.string,
591
+ revisedPrompt: ts_utils_1.Validators.string.optional()
592
+ });
593
+ const proxiedImageGenerationResponse = ts_utils_1.Validators.object({
594
+ images: ts_utils_1.Validators.arrayOf(proxiedGeneratedImage).withConstraint((arr) => arr.length > 0)
595
+ });
596
+ const proxiedListModelsEntry = ts_utils_1.Validators.object({
597
+ id: ts_utils_1.Validators.string,
598
+ capabilities: ts_utils_1.Validators.arrayOf(ts_utils_1.Validators.enumeratedValue([
599
+ 'chat',
600
+ 'tools',
601
+ 'vision',
602
+ 'image-generation',
603
+ 'thinking'
604
+ ])),
605
+ displayName: ts_utils_1.Validators.string.optional()
606
+ });
607
+ const proxiedListModelsResponse = ts_utils_1.Validators.object({
608
+ models: ts_utils_1.Validators.arrayOf(proxiedListModelsEntry)
609
+ });
610
+ // ============================================================================
611
+ // Image generation — adapters
612
+ // ============================================================================
613
+ /** Routes to /images/generations or /images/edits; handles outputParamStyle. @internal */
614
+ async function callOpenAiImageGeneration(config, request, capability, resolved, logger, signal) {
615
+ var _a, _b;
616
+ const refs = (_a = request.referenceImages) !== null && _a !== void 0 ? _a : [];
617
+ const headers = (0, endpoint_1.bearerAuthHeader)(config.apiKey);
618
+ const effectiveMimeType = resolved.outputFormat !== undefined
619
+ ? `image/${resolved.outputFormat}`
620
+ : (_b = capability.defaultOutputMimeType) !== null && _b !== void 0 ? _b : 'image/png';
621
+ const fetched = refs.length > 0
622
+ ? await callOpenAiImagesEdits(config, capability, request, headers, resolved, logger, signal)
623
+ : await callOpenAiImagesGenerations(config, request, headers, resolved, capability, logger, signal);
624
+ return fetched.onSuccess((json) => openAiImageResponse
625
+ .validate(json)
626
+ .withErrorFormat((msg) => `OpenAI images API response: ${msg}`)
627
+ .onSuccess((response) => (0, ts_utils_1.succeed)({
628
+ images: response.data.map((item) => (Object.assign({ mimeType: effectiveMimeType, base64: item.b64_json }, (item.revised_prompt !== undefined ? { revisedPrompt: item.revised_prompt } : {}))))
629
+ })));
630
+ }
631
+ /** Builds the JSON /images/generations request; handles outputParamStyle. @internal */
632
+ function callOpenAiImagesGenerations(config, request, headers, resolved, capability, logger, signal) {
633
+ var _a;
634
+ const body = {
635
+ model: config.model,
636
+ prompt: request.prompt,
637
+ n: resolved.n
638
+ };
639
+ // Output format param — conditional on model capability
640
+ if (capability.outputParamStyle === 'response-format') {
641
+ body.response_format = 'b64_json';
642
+ }
643
+ else if (capability.outputParamStyle === 'output-format') {
644
+ body.output_format = (_a = resolved.outputFormat) !== null && _a !== void 0 ? _a : 'png';
645
+ }
646
+ if (resolved.size !== undefined) {
647
+ body.size = resolved.size;
648
+ }
649
+ if (capability.supportsQualityParam && resolved.quality !== undefined) {
650
+ body.quality = resolved.quality;
651
+ }
652
+ if (resolved.seed !== undefined) {
653
+ body.seed = resolved.seed;
654
+ }
655
+ if (resolved.style !== undefined) {
656
+ body.style = resolved.style;
657
+ }
658
+ if (resolved.background !== undefined) {
659
+ body.background = resolved.background;
660
+ }
661
+ if (resolved.moderation !== undefined) {
662
+ body.moderation = resolved.moderation;
663
+ }
664
+ if (resolved.outputCompression !== undefined) {
665
+ body.output_compression = resolved.outputCompression;
666
+ }
667
+ if (resolved.otherParams !== undefined) {
668
+ Object.assign(body, resolved.otherParams);
669
+ }
670
+ /* c8 ignore next 1 - optional logger */
671
+ logger === null || logger === void 0 ? void 0 : logger.info(`Image generation: model=${config.model}, n=${resolved.n}`);
672
+ return fetchJson(`${config.baseUrl}/images/generations`, headers, body, logger, signal);
673
+ }
674
+ /** Builds the multipart /images/edits request with ref images. @internal */
675
+ async function callOpenAiImagesEdits(config, capability, request, headers, resolved, logger, signal) {
676
+ const refs = request.referenceImages; // callers verify refs.length > 0 before calling this function
677
+ const blobsResult = (0, ts_utils_1.mapResults)(refs.map((ref, i) => attachmentToBlob(ref).withErrorFormat((msg) => `reference image ${i}: ${msg}`)));
678
+ /* c8 ignore next 3 - decode failure unreachable via Node's Buffer.from (silently strips invalid input) */
679
+ if (blobsResult.isFailure()) {
680
+ return (0, ts_utils_1.fail)(blobsResult.message);
681
+ }
682
+ const form = new FormData();
683
+ form.append('model', config.model);
684
+ form.append('prompt', request.prompt);
685
+ form.append('n', String(resolved.n));
686
+ if (capability.outputParamStyle !== 'output-format') {
687
+ form.append('response_format', 'b64_json');
688
+ }
689
+ if (resolved.size !== undefined) {
690
+ form.append('size', resolved.size);
691
+ }
692
+ blobsResult.value.forEach((blob, i) => {
693
+ form.append('image[]', blob, `ref-${i}.${extensionForMimeType(refs[i].mimeType)}`);
694
+ });
695
+ /* c8 ignore next 1 - optional logger */
696
+ logger === null || logger === void 0 ? void 0 : logger.info(`Image edit: model=${config.model}, n=${resolved.n}, refs=${refs.length}`);
697
+ return fetchMultipart(`${config.baseUrl}/images/edits`, headers, form, logger, signal);
698
+ }
699
+ /** Calls xAI /images/edits with JSON body (not multipart); up to 3 source images. @internal */
700
+ async function callXaiImagesEdits(config, request, resolved, logger, signal) {
701
+ var _a;
702
+ /* c8 ignore next 1 - defensive: referenceImages always defined when this function is called */
703
+ const refs = (_a = request.referenceImages) !== null && _a !== void 0 ? _a : [];
704
+ if (refs.length > 3) {
705
+ return (0, ts_utils_1.fail)(`xAI image edits supports at most 3 reference images; got ${refs.length}`);
706
+ }
707
+ const images = refs.map((ref) => ({
708
+ type: 'image_url',
709
+ url: `data:${ref.mimeType};base64,${ref.base64}`
710
+ }));
711
+ const body = {
712
+ model: config.model,
713
+ prompt: request.prompt,
714
+ n: resolved.n,
715
+ response_format: 'b64_json',
716
+ image: images
717
+ };
718
+ if (resolved.aspectRatio !== undefined) {
719
+ body.aspect_ratio = resolved.aspectRatio;
720
+ }
721
+ if (resolved.resolution !== undefined) {
722
+ body.resolution = resolved.resolution;
723
+ }
724
+ if (resolved.otherParams !== undefined) {
725
+ Object.assign(body, resolved.otherParams);
726
+ }
727
+ /* c8 ignore next 1 - optional logger */
728
+ logger === null || logger === void 0 ? void 0 : logger.info(`xAI image edit: model=${config.model}, n=${resolved.n}, refs=${refs.length}`);
729
+ return fetchJson(`${config.baseUrl}/images/edits`, (0, endpoint_1.bearerAuthHeader)(config.apiKey), body, logger, signal);
730
+ }
731
+ /** Calls xAI /images/generations; uses aspect_ratio instead of size. @internal */
732
+ async function callXaiImageGeneration(config, request, capability, resolved, logger, signal) {
733
+ const headers = (0, endpoint_1.bearerAuthHeader)(config.apiKey);
734
+ const body = {
735
+ model: config.model,
736
+ prompt: request.prompt,
737
+ n: resolved.n,
738
+ response_format: 'b64_json'
739
+ };
740
+ if (resolved.aspectRatio !== undefined) {
741
+ body.aspect_ratio = resolved.aspectRatio;
742
+ }
743
+ if (resolved.resolution !== undefined) {
744
+ body.resolution = resolved.resolution;
745
+ }
746
+ if (resolved.otherParams !== undefined) {
747
+ Object.assign(body, resolved.otherParams);
748
+ }
749
+ /* c8 ignore next 1 - optional logger */
750
+ logger === null || logger === void 0 ? void 0 : logger.info(`xAI image generation: model=${config.model}, n=${resolved.n}`);
751
+ const fetched = await fetchJson(`${config.baseUrl}/images/generations`, headers, body, logger, signal);
752
+ return fetched.onSuccess((json) => openAiImageResponse
753
+ .validate(json)
754
+ .withErrorFormat((msg) => `xAI images API response: ${msg}`)
755
+ .onSuccess((response) => (0, ts_utils_1.succeed)({
756
+ images: response.data.map((item) => {
757
+ var _a;
758
+ return ({
759
+ mimeType: (_a = capability.defaultOutputMimeType) !== null && _a !== void 0 ? _a : 'image/jpeg',
760
+ base64: item.b64_json
761
+ });
762
+ })
763
+ })));
764
+ }
765
+ /** Calls Gemini :generateContent for image output; accepts ref images as inlineData. @internal */
766
+ async function callGeminiImageOutGeneration(config, request, resolved, logger, signal) {
767
+ var _a;
768
+ const url = `${config.baseUrl}/models/${config.model}:generateContent`;
769
+ const refs = (_a = request.referenceImages) !== null && _a !== void 0 ? _a : [];
770
+ const parts = [{ text: request.prompt }];
771
+ for (const ref of refs) {
772
+ parts.push({ inlineData: { mimeType: ref.mimeType, data: ref.base64 } });
773
+ }
774
+ const generationConfig = {};
775
+ if (resolved.geminiAspectRatio !== undefined) {
776
+ generationConfig.imageConfig = { aspectRatio: resolved.geminiAspectRatio };
777
+ }
778
+ if (resolved.otherParams !== undefined) {
779
+ Object.assign(generationConfig, resolved.otherParams);
780
+ }
781
+ const body = { contents: [{ role: 'user', parts }] };
782
+ if (Object.keys(generationConfig).length > 0) {
783
+ body.generationConfig = generationConfig;
784
+ }
785
+ const headers = {
786
+ 'x-goog-api-key': config.apiKey
787
+ };
788
+ /* c8 ignore next 1 - optional logger */
789
+ logger === null || logger === void 0 ? void 0 : logger.info(`Gemini image-out: model=${config.model}, refs=${refs.length}`);
790
+ return (await fetchJson(url, headers, body, logger, signal)).onSuccess((json) => geminiImageOutResponse
791
+ .validate(json)
792
+ .withErrorFormat((msg) => `Gemini image API response: ${msg}`)
793
+ .onSuccess((response) => {
794
+ const images = [];
795
+ for (const candidate of response.candidates) {
796
+ for (const part of candidate.content.parts) {
797
+ if (part.inlineData) {
798
+ images.push({
799
+ mimeType: part.inlineData.mimeType,
800
+ base64: part.inlineData.data
801
+ });
802
+ }
803
+ }
804
+ }
805
+ if (images.length === 0) {
806
+ return (0, ts_utils_1.fail)('Gemini image API response: no image parts in response');
807
+ }
808
+ return (0, ts_utils_1.succeed)({ images });
809
+ }));
810
+ }
811
+ /** Calls the Gemini Imagen :predict endpoint with Imagen 4 params. @internal */
812
+ async function callImagenGeneration(config, request, resolved, logger, signal) {
813
+ const url = `${config.baseUrl}/models/${config.model}:predict`;
814
+ const parameters = {
815
+ sampleCount: resolved.n
816
+ };
817
+ if (resolved.imagenAspectRatio !== undefined) {
818
+ parameters.aspectRatio = resolved.imagenAspectRatio;
819
+ }
820
+ if (resolved.imageSize !== undefined) {
821
+ parameters.imageSize = resolved.imageSize;
822
+ }
823
+ if (resolved.addWatermark !== undefined) {
824
+ parameters.addWatermark = resolved.addWatermark;
825
+ }
826
+ if (resolved.enhancePrompt !== undefined) {
827
+ parameters.enhancePrompt = resolved.enhancePrompt;
828
+ }
829
+ if (resolved.imagenOutputMimeType !== undefined || resolved.imagenOutputCompressionQuality !== undefined) {
830
+ const outputOptions = {};
831
+ if (resolved.imagenOutputMimeType !== undefined) {
832
+ outputOptions.mimeType = resolved.imagenOutputMimeType;
833
+ }
834
+ if (resolved.imagenOutputCompressionQuality !== undefined) {
835
+ outputOptions.compressionQuality = resolved.imagenOutputCompressionQuality;
836
+ }
837
+ parameters.outputOptions = outputOptions;
838
+ }
839
+ if (resolved.personGeneration !== undefined) {
840
+ parameters.personGeneration = resolved.personGeneration;
841
+ }
842
+ if (resolved.seed !== undefined) {
843
+ parameters.seed = resolved.seed;
844
+ }
845
+ if (resolved.otherParams !== undefined) {
846
+ Object.assign(parameters, resolved.otherParams);
847
+ }
848
+ const body = {
849
+ instances: [{ prompt: request.prompt }],
850
+ parameters
851
+ };
852
+ const headers = { 'x-goog-api-key': config.apiKey };
853
+ /* c8 ignore next 1 - optional logger */
854
+ logger === null || logger === void 0 ? void 0 : logger.info(`Imagen generation: model=${config.model}, n=${parameters.sampleCount}`);
855
+ const jsonResult = await fetchJson(url, headers, body, logger, signal);
856
+ if (jsonResult.isFailure()) {
857
+ return (0, ts_utils_1.fail)(jsonResult.message);
858
+ }
859
+ return imagenResponse
860
+ .validate(jsonResult.value)
861
+ .withErrorFormat((msg) => `Imagen API response: ${msg}`)
862
+ .onSuccess((response) => {
863
+ const images = response.predictions.map((p) => {
864
+ var _a;
865
+ return ({
866
+ mimeType: (_a = p.mimeType) !== null && _a !== void 0 ? _a : 'image/png',
867
+ base64: p.bytesBase64Encoded
868
+ });
869
+ });
870
+ return (0, ts_utils_1.succeed)({ images });
871
+ });
872
+ }
873
+ // ============================================================================
874
+ // Image generation — dispatcher
875
+ // ============================================================================
876
+ /**
877
+ * Calls the appropriate image-generation API for a given provider.
878
+ * Routes by the `format` field of the resolved {@link IAiImageModelCapability}:
879
+ * `'openai-images'`, `'xai-images'`, `'xai-images-edits'`, `'gemini-imagen'`,
880
+ * or `'gemini-image-out'`. Rejects up front if `referenceImages` is set but the
881
+ * capability does not declare `acceptsImageReferenceInput`.
882
+ * @param params - Request parameters including descriptor, API key, and prompt
883
+ * @returns The generated images, or a failure
884
+ * @public
885
+ */
886
+ async function callProviderImageGeneration(params) {
887
+ var _a, _b, _c;
888
+ const { descriptor, apiKey, params: request, modelOverride, logger, signal, endpoint } = params;
889
+ if (!(0, registry_1.supportsImageGeneration)(descriptor)) {
890
+ return (0, ts_utils_1.fail)(`provider "${descriptor.id}" does not support image generation`);
891
+ }
892
+ const baseUrlResult = (0, endpoint_1.resolveEffectiveBaseUrl)(descriptor, endpoint);
893
+ if (baseUrlResult.isFailure()) {
894
+ return (0, ts_utils_1.fail)(baseUrlResult.message);
895
+ }
896
+ const model = (0, model_1.resolveModel)(modelOverride !== null && modelOverride !== void 0 ? modelOverride : descriptor.defaultModel, 'image');
897
+ if (model.length === 0) {
898
+ return (0, ts_utils_1.fail)(`provider "${descriptor.id}": no image model resolved; ` +
899
+ `pass modelOverride or set descriptor.defaultModel ` +
900
+ `(a plain string, or an object with an "image" entry)`);
901
+ }
902
+ const capability = (0, registry_1.resolveImageCapability)(descriptor, model);
903
+ if (capability === undefined) {
904
+ return (0, ts_utils_1.fail)(`provider "${descriptor.id}" does not support image generation for model "${model}"`);
905
+ }
906
+ if (((_b = (_a = request.referenceImages) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0 && !capability.acceptsImageReferenceInput) {
907
+ return (0, ts_utils_1.fail)(`model "${model}" does not support reference images`);
908
+ }
909
+ const resolved = (0, imageOptionsResolver_1.resolveImageOptions)(model, capability, request.options);
910
+ const validationResult = (0, imageOptionsResolver_1.validateResolvedOptions)(model, capability, resolved);
911
+ if (validationResult.isFailure()) {
912
+ return (0, ts_utils_1.fail)(validationResult.message);
913
+ }
914
+ const config = {
915
+ baseUrl: baseUrlResult.value,
916
+ apiKey,
917
+ model
918
+ };
919
+ /* c8 ignore next 6 - optional logger diagnostic output */
920
+ if (logger) {
921
+ logger.info(`AI image generation: provider=${descriptor.id}, format=${capability.format}, ` +
922
+ `model=${config.model}`);
923
+ }
924
+ switch (capability.format) {
925
+ case 'openai-images':
926
+ return callOpenAiImageGeneration(config, request, capability, resolved, logger, signal);
927
+ case 'xai-images':
928
+ return callXaiImageGeneration(config, request, capability, resolved, logger, signal);
929
+ case 'xai-images-edits': {
930
+ const refs = (_c = request.referenceImages) !== null && _c !== void 0 ? _c : [];
931
+ if (refs.length > 0) {
932
+ const editsResult = await callXaiImagesEdits(config, request, resolved, logger, signal);
933
+ return editsResult.onSuccess((json) => openAiImageResponse
934
+ .validate(json)
935
+ .withErrorFormat((msg) => `xAI images API response: ${msg}`)
936
+ .onSuccess((response) => (0, ts_utils_1.succeed)({
937
+ images: response.data.map((item) => {
938
+ var _a;
939
+ return ({
940
+ mimeType: (_a = capability.defaultOutputMimeType) !== null && _a !== void 0 ? _a : 'image/jpeg',
941
+ base64: item.b64_json
942
+ });
943
+ })
944
+ })));
422
945
  }
423
- return callOpenAiCompletion(config, prompt, additionalMessages, temperature, logger);
946
+ return callXaiImageGeneration(config, request, capability, resolved, logger, signal);
947
+ }
948
+ case 'gemini-imagen':
949
+ return callImagenGeneration(config, request, resolved, logger, signal);
950
+ case 'gemini-image-out':
951
+ return callGeminiImageOutGeneration(config, request, resolved, logger, signal);
952
+ /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */
953
+ default: {
954
+ const _exhaustive = capability.format;
955
+ return (0, ts_utils_1.fail)(`unsupported image API format: ${String(_exhaustive)}`);
956
+ }
957
+ }
958
+ }
959
+ const openAiListEntry = ts_utils_1.Validators.object({
960
+ id: ts_utils_1.Validators.string
961
+ });
962
+ const openAiListResponse = ts_utils_1.Validators.object({
963
+ data: ts_utils_1.Validators.arrayOf(openAiListEntry)
964
+ });
965
+ const anthropicListEntry = ts_utils_1.Validators.object({
966
+ id: ts_utils_1.Validators.string,
967
+ display_name: ts_utils_1.Validators.string.optional()
968
+ });
969
+ const anthropicListResponse = ts_utils_1.Validators.object({
970
+ data: ts_utils_1.Validators.arrayOf(anthropicListEntry)
971
+ });
972
+ const geminiListEntry = ts_utils_1.Validators.object({
973
+ name: ts_utils_1.Validators.string,
974
+ displayName: ts_utils_1.Validators.string.optional(),
975
+ supportedGenerationMethods: ts_utils_1.Validators.arrayOf(ts_utils_1.Validators.string).optional()
976
+ });
977
+ const geminiListResponse = ts_utils_1.Validators.object({
978
+ models: ts_utils_1.Validators.arrayOf(geminiListEntry)
979
+ });
980
+ // ============================================================================
981
+ // List models — capability resolution
982
+ // ============================================================================
983
+ /**
984
+ * Translates Gemini's `supportedGenerationMethods` strings into our abstract
985
+ * capability vocabulary. Methods without a mapping are ignored.
986
+ * @internal
987
+ */
988
+ function geminiMethodsToCapabilities(methods) {
989
+ const out = [];
990
+ for (const m of methods) {
991
+ if (m === 'generateContent') {
992
+ out.push('chat');
993
+ }
994
+ else if (m === 'predict') {
995
+ out.push('image-generation');
996
+ }
997
+ }
998
+ return out;
999
+ }
1000
+ /**
1001
+ * Strips the `models/` prefix Gemini includes on listed model names.
1002
+ * @internal
1003
+ */
1004
+ function geminiBareId(name) {
1005
+ /* c8 ignore next 1 - defensive: Gemini API always returns names prefixed with 'models/' */
1006
+ return name.startsWith('models/') ? name.substring('models/'.length) : name;
1007
+ }
1008
+ /**
1009
+ * Applies a capability config to a model id. Walks per-provider rules then
1010
+ * global rules; unions all matching rules' capabilities. Returns the union
1011
+ * and the first matching `displayName` (if any).
1012
+ * @internal
1013
+ */
1014
+ function applyCapabilityConfig(config, providerId, modelId) {
1015
+ var _a, _b, _c;
1016
+ const caps = new Set();
1017
+ let displayName;
1018
+ const rulesets = [
1019
+ (_b = (_a = config.perProvider) === null || _a === void 0 ? void 0 : _a[providerId]) !== null && _b !== void 0 ? _b : [],
1020
+ (_c = config.global) !== null && _c !== void 0 ? _c : []
1021
+ ];
1022
+ for (const rules of rulesets) {
1023
+ for (const rule of rules) {
1024
+ rule.idPattern.lastIndex = 0;
1025
+ if (rule.idPattern.test(modelId)) {
1026
+ for (const cap of rule.capabilities) {
1027
+ caps.add(cap);
1028
+ }
1029
+ if (displayName === undefined && rule.displayName !== undefined) {
1030
+ displayName = typeof rule.displayName === 'function' ? rule.displayName(modelId) : rule.displayName;
1031
+ }
1032
+ }
1033
+ }
1034
+ }
1035
+ return { capabilities: Array.from(caps), displayName };
1036
+ }
1037
+ /**
1038
+ * Combines provider-native capability info (when supplied) and config-derived
1039
+ * capability info into a final {@link IAiModelInfo}.
1040
+ * @internal
1041
+ */
1042
+ function buildModelInfo(providerId, id, nativeCapabilities, nativeDisplayName, config) {
1043
+ const fromConfig = applyCapabilityConfig(config, providerId, id);
1044
+ const all = new Set([...nativeCapabilities, ...fromConfig.capabilities]);
1045
+ return Object.assign({ id, capabilities: all }, (nativeDisplayName !== undefined
1046
+ ? { displayName: nativeDisplayName }
1047
+ : fromConfig.displayName !== undefined
1048
+ ? { displayName: fromConfig.displayName }
1049
+ : {}));
1050
+ }
1051
+ // ============================================================================
1052
+ // List models — adapters
1053
+ // ============================================================================
1054
+ /**
1055
+ * Calls the OpenAI-style `GET /models` endpoint. Used by openai, xai-grok,
1056
+ * groq, and mistral. Provider supplies no capability info — capabilities are
1057
+ * derived entirely from the config.
1058
+ * @internal
1059
+ */
1060
+ async function callOpenAiListModels(config, providerId, capabilityConfig, logger, signal) {
1061
+ const url = `${config.baseUrl}/models`;
1062
+ const headers = (0, endpoint_1.bearerAuthHeader)(config.apiKey);
1063
+ /* c8 ignore next 1 - optional logger */
1064
+ logger === null || logger === void 0 ? void 0 : logger.info(`List models: provider=${providerId}, format=openai`);
1065
+ const jsonResult = await fetchGetJson(url, headers, logger, signal);
1066
+ if (jsonResult.isFailure()) {
1067
+ return (0, ts_utils_1.fail)(jsonResult.message);
1068
+ }
1069
+ return openAiListResponse
1070
+ .validate(jsonResult.value)
1071
+ .withErrorFormat((msg) => `OpenAI models API response: ${msg}`)
1072
+ .onSuccess((response) => {
1073
+ const models = response.data.map((entry) => buildModelInfo(providerId, entry.id, [], undefined, capabilityConfig));
1074
+ return (0, ts_utils_1.succeed)(models);
1075
+ });
1076
+ }
1077
+ /**
1078
+ * Calls the Anthropic `GET /models` endpoint. Provider supplies a
1079
+ * `display_name` but no native capability info.
1080
+ * @internal
1081
+ */
1082
+ async function callAnthropicListModels(config, providerId, capabilityConfig, logger, signal) {
1083
+ const url = `${config.baseUrl}/models`;
1084
+ const headers = {
1085
+ 'x-api-key': config.apiKey,
1086
+ 'anthropic-version': '2023-06-01',
1087
+ 'anthropic-dangerous-direct-browser-access': 'true'
1088
+ };
1089
+ /* c8 ignore next 1 - optional logger */
1090
+ logger === null || logger === void 0 ? void 0 : logger.info(`List models: provider=${providerId}, format=anthropic`);
1091
+ const jsonResult = await fetchGetJson(url, headers, logger, signal);
1092
+ if (jsonResult.isFailure()) {
1093
+ return (0, ts_utils_1.fail)(jsonResult.message);
1094
+ }
1095
+ return anthropicListResponse
1096
+ .validate(jsonResult.value)
1097
+ .withErrorFormat((msg) => `Anthropic models API response: ${msg}`)
1098
+ .onSuccess((response) => {
1099
+ const models = response.data.map((entry) => buildModelInfo(providerId, entry.id, [], entry.display_name, capabilityConfig));
1100
+ return (0, ts_utils_1.succeed)(models);
1101
+ });
1102
+ }
1103
+ /**
1104
+ * Calls the Gemini `GET /models` endpoint. Provider supplies both a
1105
+ * `displayName` and `supportedGenerationMethods` — translated to native
1106
+ * capabilities and unioned with config-derived capabilities.
1107
+ * @internal
1108
+ */
1109
+ async function callGeminiListModels(config, providerId, capabilityConfig, logger, signal) {
1110
+ const url = `${config.baseUrl}/models`;
1111
+ const headers = {
1112
+ 'x-goog-api-key': config.apiKey
1113
+ };
1114
+ /* c8 ignore next 1 - optional logger */
1115
+ logger === null || logger === void 0 ? void 0 : logger.info(`List models: provider=${providerId}, format=gemini`);
1116
+ const jsonResult = await fetchGetJson(url, headers, logger, signal);
1117
+ if (jsonResult.isFailure()) {
1118
+ return (0, ts_utils_1.fail)(jsonResult.message);
1119
+ }
1120
+ return geminiListResponse
1121
+ .validate(jsonResult.value)
1122
+ .withErrorFormat((msg) => `Gemini models API response: ${msg}`)
1123
+ .onSuccess((response) => {
1124
+ const models = response.models.map((entry) => {
1125
+ const id = geminiBareId(entry.name);
1126
+ const native = entry.supportedGenerationMethods
1127
+ ? geminiMethodsToCapabilities(entry.supportedGenerationMethods)
1128
+ : [];
1129
+ return buildModelInfo(providerId, id, native, entry.displayName, capabilityConfig);
1130
+ });
1131
+ return (0, ts_utils_1.succeed)(models);
1132
+ });
1133
+ }
1134
+ // ============================================================================
1135
+ // List models — dispatcher
1136
+ // ============================================================================
1137
+ /**
1138
+ * Lists models available from a provider, routing by `descriptor.apiFormat`.
1139
+ * Capabilities are resolved from native provider info and a configurable rule set.
1140
+ * @param params - Request parameters including descriptor, API key, and optional capability filter
1141
+ * @returns The resolved model list, or a failure
1142
+ * @public
1143
+ */
1144
+ async function callProviderListModels(params) {
1145
+ const { descriptor, apiKey, capability, capabilityConfig, logger, signal, endpoint } = params;
1146
+ const baseUrlResult = (0, endpoint_1.resolveEffectiveBaseUrl)(descriptor, endpoint);
1147
+ if (baseUrlResult.isFailure()) {
1148
+ return (0, ts_utils_1.fail)(baseUrlResult.message);
1149
+ }
1150
+ const config = {
1151
+ baseUrl: baseUrlResult.value,
1152
+ apiKey,
1153
+ model: '' // unused by listing
1154
+ };
1155
+ const effectiveConfig = capabilityConfig !== null && capabilityConfig !== void 0 ? capabilityConfig : registry_1.DEFAULT_MODEL_CAPABILITY_CONFIG;
1156
+ let listResult;
1157
+ switch (descriptor.apiFormat) {
1158
+ case 'openai':
1159
+ listResult = await callOpenAiListModels(config, descriptor.id, effectiveConfig, logger, signal);
1160
+ break;
424
1161
  case 'anthropic':
425
- return callAnthropicCompletion(config, prompt, additionalMessages, temperature, logger, tools);
1162
+ listResult = await callAnthropicListModels(config, descriptor.id, effectiveConfig, logger, signal);
1163
+ break;
426
1164
  case 'gemini':
427
- return callGeminiCompletion(config, prompt, additionalMessages, temperature, logger, tools);
1165
+ listResult = await callGeminiListModels(config, descriptor.id, effectiveConfig, logger, signal);
1166
+ break;
428
1167
  /* c8 ignore next 4 - defensive coding: exhaustive switch guaranteed by TypeScript */
429
1168
  default: {
430
1169
  const _exhaustive = descriptor.apiFormat;
431
1170
  return (0, ts_utils_1.fail)(`unsupported API format: ${String(_exhaustive)}`);
432
1171
  }
433
1172
  }
1173
+ if (listResult.isFailure()) {
1174
+ return listResult;
1175
+ }
1176
+ if (capability === undefined) {
1177
+ return listResult;
1178
+ }
1179
+ return (0, ts_utils_1.succeed)(listResult.value.filter((m) => m.capabilities.has(capability)));
1180
+ }
1181
+ // ============================================================================
1182
+ // Proxied list models
1183
+ // ============================================================================
1184
+ /**
1185
+ * Calls the model-listing endpoint on a proxy server.
1186
+ * Endpoint: `POST ${proxyUrl}/api/ai/list-models`. Capability config is not
1187
+ * forwarded. `capabilities` is serialized as a string array. Error body
1188
+ * `{error: string}` is surfaced as `proxy: ${error}`.
1189
+ * @public
1190
+ */
1191
+ async function callProxiedListModels(proxyUrl, params) {
1192
+ const { descriptor, apiKey, capability, logger, signal } = params;
1193
+ const body = {
1194
+ providerId: descriptor.id,
1195
+ apiKey
1196
+ };
1197
+ if (capability !== undefined) {
1198
+ body.capability = capability;
1199
+ }
1200
+ /* c8 ignore next 1 - optional logger */
1201
+ logger === null || logger === void 0 ? void 0 : logger.info(`AI list-models proxy request: provider=${descriptor.id}, proxy=${proxyUrl}`);
1202
+ const url = `${proxyUrl}/api/ai/list-models`;
1203
+ const jsonResult = await fetchJson(url, {}, body, logger, signal);
1204
+ if (jsonResult.isFailure()) {
1205
+ return (0, ts_utils_1.fail)(jsonResult.message);
1206
+ }
1207
+ const response = jsonResult.value;
1208
+ if (typeof response.error === 'string') {
1209
+ return (0, ts_utils_1.fail)(`proxy: ${response.error}`);
1210
+ }
1211
+ return proxiedListModelsResponse
1212
+ .validate(response)
1213
+ .withErrorFormat((msg) => `proxy returned invalid response: ${msg}`)
1214
+ .onSuccess((parsed) => {
1215
+ const models = parsed.models.map((m) => (Object.assign({ id: m.id, capabilities: new Set(m.capabilities) }, (m.displayName !== undefined ? { displayName: m.displayName } : {}))));
1216
+ return (0, ts_utils_1.succeed)(models);
1217
+ });
434
1218
  }
435
1219
  // ============================================================================
436
1220
  // Proxied completion (routes through a backend server)
@@ -449,11 +1233,15 @@ async function callProviderCompletion(params) {
449
1233
  * @public
450
1234
  */
451
1235
  async function callProxiedCompletion(proxyUrl, params) {
452
- const { descriptor, apiKey, prompt, additionalMessages, temperature, modelOverride, logger, tools } = params;
1236
+ const { descriptor, apiKey, prompt, additionalMessages, temperature, modelOverride, logger, tools, signal, thinking } = params;
1237
+ const promptBody = { system: prompt.system, user: prompt.user };
1238
+ if (prompt.attachments.length > 0) {
1239
+ promptBody.attachments = prompt.attachments;
1240
+ }
453
1241
  const body = {
454
1242
  providerId: descriptor.id,
455
1243
  apiKey,
456
- prompt: { system: prompt.system, user: prompt.user },
1244
+ prompt: promptBody,
457
1245
  temperature: temperature !== null && temperature !== void 0 ? temperature : 0.7
458
1246
  };
459
1247
  if (additionalMessages && additionalMessages.length > 0) {
@@ -465,14 +1253,16 @@ async function callProxiedCompletion(proxyUrl, params) {
465
1253
  if (tools && tools.length > 0) {
466
1254
  body.tools = tools;
467
1255
  }
1256
+ if (thinking !== undefined) {
1257
+ body.thinking = thinking;
1258
+ }
468
1259
  /* c8 ignore next 1 - optional logger */
469
1260
  logger === null || logger === void 0 ? void 0 : logger.info(`AI proxy request: provider=${descriptor.id}, proxy=${proxyUrl}`);
470
1261
  const url = `${proxyUrl}/api/ai/completion`;
471
- const jsonResult = await fetchJson(url, {}, body, logger);
1262
+ const jsonResult = await fetchJson(url, {}, body, logger, signal);
472
1263
  if (jsonResult.isFailure()) {
473
1264
  return (0, ts_utils_1.fail)(jsonResult.message);
474
1265
  }
475
- // Check for error response from proxy
476
1266
  const response = jsonResult.value;
477
1267
  if (typeof response.error === 'string') {
478
1268
  return (0, ts_utils_1.fail)(`proxy: ${response.error}`);
@@ -485,4 +1275,45 @@ async function callProxiedCompletion(proxyUrl, params) {
485
1275
  truncated: response.truncated === true
486
1276
  });
487
1277
  }
1278
+ // ============================================================================
1279
+ // Proxied image generation
1280
+ // ============================================================================
1281
+ /**
1282
+ * Calls the image-generation endpoint on a proxy server instead of calling
1283
+ * the provider API directly from the browser.
1284
+ * Endpoint: `POST ${proxyUrl}/api/ai/image-generation`. Request body:
1285
+ * `{providerId, apiKey, params, modelOverride?}`. The proxy handles descriptor
1286
+ * lookup, model resolution, provider dispatch, and response normalization
1287
+ * (including repackaging `referenceImages` for the upstream wire format).
1288
+ * Error body `{error: string}` is surfaced as `proxy: ${error}`.
1289
+ * @param proxyUrl - Base URL of the proxy server (e.g. `http://localhost:3001`)
1290
+ * @param params - Same parameters as {@link callProviderImageGeneration}
1291
+ * @returns The generated images, or a failure
1292
+ * @public
1293
+ */
1294
+ async function callProxiedImageGeneration(proxyUrl, params) {
1295
+ const { descriptor, apiKey, params: request, modelOverride, logger, signal } = params;
1296
+ const body = {
1297
+ providerId: descriptor.id,
1298
+ apiKey,
1299
+ params: request
1300
+ };
1301
+ if (modelOverride !== undefined) {
1302
+ body.modelOverride = modelOverride;
1303
+ }
1304
+ /* c8 ignore next 1 - optional logger */
1305
+ logger === null || logger === void 0 ? void 0 : logger.info(`AI image proxy request: provider=${descriptor.id}, proxy=${proxyUrl}`);
1306
+ const url = `${proxyUrl}/api/ai/image-generation`;
1307
+ const jsonResult = await fetchJson(url, {}, body, logger, signal);
1308
+ if (jsonResult.isFailure()) {
1309
+ return (0, ts_utils_1.fail)(jsonResult.message);
1310
+ }
1311
+ const response = jsonResult.value;
1312
+ if (typeof response.error === 'string') {
1313
+ return (0, ts_utils_1.fail)(`proxy: ${response.error}`);
1314
+ }
1315
+ return proxiedImageGenerationResponse
1316
+ .validate(response)
1317
+ .withErrorFormat((msg) => `proxy returned invalid response: ${msg}`);
1318
+ }
488
1319
  //# sourceMappingURL=apiClient.js.map