@fractaal/pi-ai 0.86.9 → 1.6.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 (411) hide show
  1. package/README.md +154 -78
  2. package/dist/api/anthropic-messages.d.ts.map +1 -1
  3. package/dist/api/anthropic-messages.js +380 -144
  4. package/dist/api/anthropic-messages.js.map +1 -1
  5. package/dist/api/azure-openai-responses.d.ts.map +1 -1
  6. package/dist/api/azure-openai-responses.js +9 -11
  7. package/dist/api/azure-openai-responses.js.map +1 -1
  8. package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  9. package/dist/api/bedrock-converse-stream.js +184 -121
  10. package/dist/api/bedrock-converse-stream.js.map +1 -1
  11. package/dist/api/bedrock-converse-stream.lazy.d.ts +7 -0
  12. package/dist/api/bedrock-converse-stream.lazy.d.ts.map +1 -1
  13. package/dist/api/bedrock-converse-stream.lazy.js +18 -5
  14. package/dist/api/bedrock-converse-stream.lazy.js.map +1 -1
  15. package/dist/api/classifier-shared.d.ts +21 -0
  16. package/dist/api/classifier-shared.d.ts.map +1 -0
  17. package/dist/api/classifier-shared.js +99 -0
  18. package/dist/api/classifier-shared.js.map +1 -0
  19. package/dist/api/cloudflare-workers-ai-system-one.d.ts.map +1 -1
  20. package/dist/api/cloudflare-workers-ai-system-one.js +10 -1
  21. package/dist/api/cloudflare-workers-ai-system-one.js.map +1 -1
  22. package/dist/api/constrained-sampling.d.ts +15 -1
  23. package/dist/api/constrained-sampling.d.ts.map +1 -1
  24. package/dist/api/constrained-sampling.js +51 -18
  25. package/dist/api/constrained-sampling.js.map +1 -1
  26. package/dist/api/google-generative-ai.d.ts.map +1 -1
  27. package/dist/api/google-generative-ai.js +4 -10
  28. package/dist/api/google-generative-ai.js.map +1 -1
  29. package/dist/api/google-shared.d.ts +2 -1
  30. package/dist/api/google-shared.d.ts.map +1 -1
  31. package/dist/api/google-shared.js +60 -3
  32. package/dist/api/google-shared.js.map +1 -1
  33. package/dist/api/google-vertex.d.ts.map +1 -1
  34. package/dist/api/google-vertex.js +9 -15
  35. package/dist/api/google-vertex.js.map +1 -1
  36. package/dist/api/lazy.d.ts +7 -2
  37. package/dist/api/lazy.d.ts.map +1 -1
  38. package/dist/api/lazy.js +33 -12
  39. package/dist/api/lazy.js.map +1 -1
  40. package/dist/api/llama-cpp-classify.d.ts.map +1 -1
  41. package/dist/api/llama-cpp-classify.js +4 -1
  42. package/dist/api/llama-cpp-classify.js.map +1 -1
  43. package/dist/api/mistral-conversations.d.ts.map +1 -1
  44. package/dist/api/mistral-conversations.js +50 -60
  45. package/dist/api/mistral-conversations.js.map +1 -1
  46. package/dist/api/openai-codex-responses.d.ts.map +1 -1
  47. package/dist/api/openai-codex-responses.js +30 -17
  48. package/dist/api/openai-codex-responses.js.map +1 -1
  49. package/dist/api/openai-completions.d.ts +2 -1
  50. package/dist/api/openai-completions.d.ts.map +1 -1
  51. package/dist/api/openai-completions.js +93 -90
  52. package/dist/api/openai-completions.js.map +1 -1
  53. package/dist/api/openai-decisions.d.ts +4 -0
  54. package/dist/api/openai-decisions.d.ts.map +1 -0
  55. package/dist/api/openai-decisions.js +162 -0
  56. package/dist/api/openai-decisions.js.map +1 -0
  57. package/dist/api/openai-decisions.lazy.d.ts +3 -0
  58. package/dist/api/openai-decisions.lazy.d.ts.map +1 -0
  59. package/dist/api/openai-decisions.lazy.js +4 -0
  60. package/dist/api/openai-decisions.lazy.js.map +1 -0
  61. package/dist/api/openai-responses-shared.d.ts +26 -0
  62. package/dist/api/openai-responses-shared.d.ts.map +1 -1
  63. package/dist/api/openai-responses-shared.js +132 -66
  64. package/dist/api/openai-responses-shared.js.map +1 -1
  65. package/dist/api/openai-responses.d.ts.map +1 -1
  66. package/dist/api/openai-responses.js +42 -21
  67. package/dist/api/openai-responses.js.map +1 -1
  68. package/dist/api/openrouter-images.d.ts.map +1 -1
  69. package/dist/api/openrouter-images.js +5 -3
  70. package/dist/api/openrouter-images.js.map +1 -1
  71. package/dist/api/pi-messages.d.ts +2 -0
  72. package/dist/api/pi-messages.d.ts.map +1 -1
  73. package/dist/api/pi-messages.js +27 -12
  74. package/dist/api/pi-messages.js.map +1 -1
  75. package/dist/api/simple-options.d.ts +1 -0
  76. package/dist/api/simple-options.d.ts.map +1 -1
  77. package/dist/api/simple-options.js +18 -3
  78. package/dist/api/simple-options.js.map +1 -1
  79. package/dist/api/system-one-shared.d.ts +29 -3
  80. package/dist/api/system-one-shared.d.ts.map +1 -1
  81. package/dist/api/system-one-shared.js +54 -86
  82. package/dist/api/system-one-shared.js.map +1 -1
  83. package/dist/api/transform-messages.d.ts +16 -1
  84. package/dist/api/transform-messages.d.ts.map +1 -1
  85. package/dist/api/transform-messages.js +28 -15
  86. package/dist/api/transform-messages.js.map +1 -1
  87. package/dist/api/typesafe-system-one.d.ts.map +1 -1
  88. package/dist/api/typesafe-system-one.js +12 -1
  89. package/dist/api/typesafe-system-one.js.map +1 -1
  90. package/dist/auth/oauth/anthropic.d.ts.map +1 -1
  91. package/dist/auth/oauth/anthropic.js +14 -6
  92. package/dist/auth/oauth/anthropic.js.map +1 -1
  93. package/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  94. package/dist/auth/oauth/github-copilot.js +4 -11
  95. package/dist/auth/oauth/github-copilot.js.map +1 -1
  96. package/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
  97. package/dist/auth/oauth/kimi-coding.js +4 -22
  98. package/dist/auth/oauth/kimi-coding.js.map +1 -1
  99. package/dist/auth/oauth/load.d.ts.map +1 -1
  100. package/dist/auth/oauth/load.js +5 -2
  101. package/dist/auth/oauth/load.js.map +1 -1
  102. package/dist/auth/oauth/meta.d.ts.map +1 -1
  103. package/dist/auth/oauth/meta.js +1 -14
  104. package/dist/auth/oauth/meta.js.map +1 -1
  105. package/dist/auth/oauth/openai-chatgpt.js +1 -1
  106. package/dist/auth/oauth/openai-chatgpt.js.map +1 -1
  107. package/dist/auth/oauth/openai-codex.d.ts.map +1 -1
  108. package/dist/auth/oauth/openai-codex.js +4 -4
  109. package/dist/auth/oauth/openai-codex.js.map +1 -1
  110. package/dist/auth/oauth/radius.d.ts.map +1 -1
  111. package/dist/auth/oauth/radius.js +9 -3
  112. package/dist/auth/oauth/radius.js.map +1 -1
  113. package/dist/auth/oauth/trusted-url.d.ts +7 -0
  114. package/dist/auth/oauth/trusted-url.d.ts.map +1 -0
  115. package/dist/auth/oauth/trusted-url.js +12 -0
  116. package/dist/auth/oauth/trusted-url.js.map +1 -0
  117. package/dist/auth/types.d.ts +9 -0
  118. package/dist/auth/types.d.ts.map +1 -1
  119. package/dist/auth/types.js.map +1 -1
  120. package/dist/cli.js +6 -2
  121. package/dist/cli.js.map +1 -1
  122. package/dist/compat/extension-oauth-types.d.ts +4 -0
  123. package/dist/compat/extension-oauth-types.d.ts.map +1 -1
  124. package/dist/compat/extension-oauth-types.js.map +1 -1
  125. package/dist/env-api-keys.d.ts.map +1 -1
  126. package/dist/env-api-keys.js +1 -0
  127. package/dist/env-api-keys.js.map +1 -1
  128. package/dist/index.d.ts +4 -4
  129. package/dist/index.d.ts.map +1 -1
  130. package/dist/index.js +4 -3
  131. package/dist/index.js.map +1 -1
  132. package/dist/lazy-bedrock-provider.d.ts +10 -0
  133. package/dist/lazy-bedrock-provider.d.ts.map +1 -0
  134. package/dist/lazy-bedrock-provider.js +13 -0
  135. package/dist/lazy-bedrock-provider.js.map +1 -0
  136. package/dist/lazy-oauth.d.ts +10 -0
  137. package/dist/lazy-oauth.d.ts.map +1 -0
  138. package/dist/lazy-oauth.js +23 -0
  139. package/dist/lazy-oauth.js.map +1 -0
  140. package/dist/models.d.ts +9 -0
  141. package/dist/models.d.ts.map +1 -1
  142. package/dist/models.generated.d.ts +4 -0
  143. package/dist/models.generated.d.ts.map +1 -1
  144. package/dist/models.generated.js +5 -1
  145. package/dist/models.generated.js.map +1 -1
  146. package/dist/models.js +28 -12
  147. package/dist/models.js.map +1 -1
  148. package/dist/providers/all.d.ts.map +1 -1
  149. package/dist/providers/all.js +2 -0
  150. package/dist/providers/all.js.map +1 -1
  151. package/dist/providers/amazon-bedrock.models.js +1 -1
  152. package/dist/providers/amazon-bedrock.models.js.map +1 -1
  153. package/dist/providers/ant-ling.models.js +1 -1
  154. package/dist/providers/ant-ling.models.js.map +1 -1
  155. package/dist/providers/anthropic.models.js +1 -1
  156. package/dist/providers/anthropic.models.js.map +1 -1
  157. package/dist/providers/azure.models.js +1 -1
  158. package/dist/providers/azure.models.js.map +1 -1
  159. package/dist/providers/baseten.models.js +1 -1
  160. package/dist/providers/baseten.models.js.map +1 -1
  161. package/dist/providers/cerebras.models.js +1 -1
  162. package/dist/providers/cerebras.models.js.map +1 -1
  163. package/dist/providers/cloudflare-ai-gateway.models.js +1 -1
  164. package/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  165. package/dist/providers/cloudflare-workers-ai.models.js +1 -1
  166. package/dist/providers/cloudflare-workers-ai.models.js.map +1 -1
  167. package/dist/providers/compat-schema.d.ts +494 -0
  168. package/dist/providers/compat-schema.d.ts.map +1 -0
  169. package/dist/providers/compat-schema.js +447 -0
  170. package/dist/providers/compat-schema.js.map +1 -0
  171. package/dist/providers/data/.manifest.json +1 -1
  172. package/dist/providers/data/amazon-bedrock.json +1 -1
  173. package/dist/providers/data/anthropic.json +1 -1
  174. package/dist/providers/data/azure.json +1 -1
  175. package/dist/providers/data/baseten.json +1 -1
  176. package/dist/providers/data/cerebras.json +1 -1
  177. package/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  178. package/dist/providers/data/cloudflare-workers-ai.json +1 -1
  179. package/dist/providers/data/fireworks.json +1 -1
  180. package/dist/providers/data/github-copilot.json +1 -1
  181. package/dist/providers/data/google-vertex.json +1 -1
  182. package/dist/providers/data/google.json +1 -1
  183. package/dist/providers/data/groq.json +1 -1
  184. package/dist/providers/data/huggingface.json +1 -1
  185. package/dist/providers/data/kimi-coding.json +1 -1
  186. package/dist/providers/data/meta.json +1 -1
  187. package/dist/providers/data/minimax-cn.json +1 -1
  188. package/dist/providers/data/minimax.json +1 -1
  189. package/dist/providers/data/mistral.json +1 -1
  190. package/dist/providers/data/moonshotai-cn.json +1 -1
  191. package/dist/providers/data/moonshotai.json +1 -1
  192. package/dist/providers/data/nvidia.json +1 -1
  193. package/dist/providers/data/openai-codex.json +1 -1
  194. package/dist/providers/data/openai.json +1 -1
  195. package/dist/providers/data/opencode-go.json +1 -1
  196. package/dist/providers/data/opencode.json +1 -1
  197. package/dist/providers/data/openrouter.json +1 -1
  198. package/dist/providers/data/qwen-token-plan-cn.json +1 -1
  199. package/dist/providers/data/qwen-token-plan-individual-cn.json +1 -0
  200. package/dist/providers/data/qwen-token-plan-individual.json +1 -1
  201. package/dist/providers/data/qwen-token-plan.json +1 -1
  202. package/dist/providers/data/radius.json +1 -1
  203. package/dist/providers/data/together.json +1 -1
  204. package/dist/providers/data/typesafe.json +1 -1
  205. package/dist/providers/data/vercel-ai-gateway.json +1 -1
  206. package/dist/providers/data/xai.json +1 -1
  207. package/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
  208. package/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
  209. package/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
  210. package/dist/providers/data/xiaomi.json +1 -1
  211. package/dist/providers/data/zai-coding-cn.json +1 -1
  212. package/dist/providers/data/zai.json +1 -1
  213. package/dist/providers/deepseek.models.js +1 -1
  214. package/dist/providers/deepseek.models.js.map +1 -1
  215. package/dist/providers/faux.d.ts.map +1 -1
  216. package/dist/providers/faux.js +31 -9
  217. package/dist/providers/faux.js.map +1 -1
  218. package/dist/providers/fireworks.models.js +1 -1
  219. package/dist/providers/fireworks.models.js.map +1 -1
  220. package/dist/providers/github-copilot.d.ts +3 -1
  221. package/dist/providers/github-copilot.d.ts.map +1 -1
  222. package/dist/providers/github-copilot.js +13 -2
  223. package/dist/providers/github-copilot.js.map +1 -1
  224. package/dist/providers/github-copilot.models.js +1 -1
  225. package/dist/providers/github-copilot.models.js.map +1 -1
  226. package/dist/providers/google-vertex.models.js +1 -1
  227. package/dist/providers/google-vertex.models.js.map +1 -1
  228. package/dist/providers/google.models.js +1 -1
  229. package/dist/providers/google.models.js.map +1 -1
  230. package/dist/providers/groq.models.js +1 -1
  231. package/dist/providers/groq.models.js.map +1 -1
  232. package/dist/providers/huggingface.models.js +1 -1
  233. package/dist/providers/huggingface.models.js.map +1 -1
  234. package/dist/providers/kimi-coding.d.ts +1 -1
  235. package/dist/providers/kimi-coding.js +3 -3
  236. package/dist/providers/kimi-coding.js.map +1 -1
  237. package/dist/providers/kimi-coding.models.js +1 -1
  238. package/dist/providers/kimi-coding.models.js.map +1 -1
  239. package/dist/providers/meta.models.js +1 -1
  240. package/dist/providers/meta.models.js.map +1 -1
  241. package/dist/providers/minimax-cn.models.js +1 -1
  242. package/dist/providers/minimax-cn.models.js.map +1 -1
  243. package/dist/providers/minimax.models.js +1 -1
  244. package/dist/providers/minimax.models.js.map +1 -1
  245. package/dist/providers/mistral.models.js +1 -1
  246. package/dist/providers/mistral.models.js.map +1 -1
  247. package/dist/providers/model-schema.d.ts +91 -0
  248. package/dist/providers/model-schema.d.ts.map +1 -0
  249. package/dist/providers/model-schema.js +68 -0
  250. package/dist/providers/model-schema.js.map +1 -0
  251. package/dist/providers/moonshotai-cn.models.js +1 -1
  252. package/dist/providers/moonshotai-cn.models.js.map +1 -1
  253. package/dist/providers/moonshotai.models.js +1 -1
  254. package/dist/providers/moonshotai.models.js.map +1 -1
  255. package/dist/providers/nvidia.models.js +1 -1
  256. package/dist/providers/nvidia.models.js.map +1 -1
  257. package/dist/providers/openai-codex-catalog.d.ts.map +1 -1
  258. package/dist/providers/openai-codex-catalog.js +44 -44
  259. package/dist/providers/openai-codex-catalog.js.map +1 -1
  260. package/dist/providers/openai-codex.models.js +1 -1
  261. package/dist/providers/openai-codex.models.js.map +1 -1
  262. package/dist/providers/openai.d.ts.map +1 -1
  263. package/dist/providers/openai.js +12 -3
  264. package/dist/providers/openai.js.map +1 -1
  265. package/dist/providers/openai.models.js +1 -1
  266. package/dist/providers/openai.models.js.map +1 -1
  267. package/dist/providers/opencode-go.d.ts +3 -1
  268. package/dist/providers/opencode-go.d.ts.map +1 -1
  269. package/dist/providers/opencode-go.js +2 -0
  270. package/dist/providers/opencode-go.js.map +1 -1
  271. package/dist/providers/opencode-go.models.js +1 -1
  272. package/dist/providers/opencode-go.models.js.map +1 -1
  273. package/dist/providers/opencode.js +1 -1
  274. package/dist/providers/opencode.js.map +1 -1
  275. package/dist/providers/opencode.models.js +1 -1
  276. package/dist/providers/opencode.models.js.map +1 -1
  277. package/dist/providers/openrouter-api.d.ts +40 -0
  278. package/dist/providers/openrouter-api.d.ts.map +1 -0
  279. package/dist/providers/openrouter-api.js +41 -0
  280. package/dist/providers/openrouter-api.js.map +1 -0
  281. package/dist/providers/openrouter.d.ts +11 -1
  282. package/dist/providers/openrouter.d.ts.map +1 -1
  283. package/dist/providers/openrouter.js +52 -1
  284. package/dist/providers/openrouter.js.map +1 -1
  285. package/dist/providers/openrouter.models.js +1 -1
  286. package/dist/providers/openrouter.models.js.map +1 -1
  287. package/dist/providers/qwen-token-plan-cn.models.js +1 -1
  288. package/dist/providers/qwen-token-plan-cn.models.js.map +1 -1
  289. package/dist/providers/qwen-token-plan-individual-cn.d.ts +3 -0
  290. package/dist/providers/qwen-token-plan-individual-cn.d.ts.map +1 -0
  291. package/dist/providers/qwen-token-plan-individual-cn.js +15 -0
  292. package/dist/providers/qwen-token-plan-individual-cn.js.map +1 -0
  293. package/dist/providers/qwen-token-plan-individual-cn.models.d.ts +6 -0
  294. package/dist/providers/qwen-token-plan-individual-cn.models.d.ts.map +1 -0
  295. package/dist/providers/qwen-token-plan-individual-cn.models.js +8 -0
  296. package/dist/providers/qwen-token-plan-individual-cn.models.js.map +1 -0
  297. package/dist/providers/qwen-token-plan-individual.models.js +1 -1
  298. package/dist/providers/qwen-token-plan-individual.models.js.map +1 -1
  299. package/dist/providers/qwen-token-plan.models.js +1 -1
  300. package/dist/providers/qwen-token-plan.models.js.map +1 -1
  301. package/dist/providers/radius-config.d.ts.map +1 -1
  302. package/dist/providers/radius-config.js +2 -1
  303. package/dist/providers/radius-config.js.map +1 -1
  304. package/dist/providers/radius.d.ts.map +1 -1
  305. package/dist/providers/radius.js +4 -12
  306. package/dist/providers/radius.js.map +1 -1
  307. package/dist/providers/radius.models.js +1 -1
  308. package/dist/providers/radius.models.js.map +1 -1
  309. package/dist/providers/schema-builders.d.ts +10 -0
  310. package/dist/providers/schema-builders.d.ts.map +1 -0
  311. package/dist/providers/schema-builders.js +18 -0
  312. package/dist/providers/schema-builders.js.map +1 -0
  313. package/dist/providers/together.models.js +1 -1
  314. package/dist/providers/together.models.js.map +1 -1
  315. package/dist/providers/typesafe.models.js +1 -1
  316. package/dist/providers/typesafe.models.js.map +1 -1
  317. package/dist/providers/vercel-ai-gateway.models.js +1 -1
  318. package/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  319. package/dist/providers/xai.models.js +1 -1
  320. package/dist/providers/xai.models.js.map +1 -1
  321. package/dist/providers/xiaomi-token-plan-ams.models.js +1 -1
  322. package/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
  323. package/dist/providers/xiaomi-token-plan-cn.models.js +1 -1
  324. package/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
  325. package/dist/providers/xiaomi-token-plan-sgp.models.js +1 -1
  326. package/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
  327. package/dist/providers/xiaomi.models.js +1 -1
  328. package/dist/providers/xiaomi.models.js.map +1 -1
  329. package/dist/providers/zai-coding-cn.models.js +1 -1
  330. package/dist/providers/zai-coding-cn.models.js.map +1 -1
  331. package/dist/providers/zai.models.js +1 -1
  332. package/dist/providers/zai.models.js.map +1 -1
  333. package/dist/telemetry.d.ts +288 -0
  334. package/dist/telemetry.d.ts.map +1 -0
  335. package/dist/telemetry.js +192 -0
  336. package/dist/telemetry.js.map +1 -0
  337. package/dist/types.d.ts +57 -310
  338. package/dist/types.d.ts.map +1 -1
  339. package/dist/types.js.map +1 -1
  340. package/dist/utils/error-body.d.ts +8 -0
  341. package/dist/utils/error-body.d.ts.map +1 -1
  342. package/dist/utils/error-body.js +30 -2
  343. package/dist/utils/error-body.js.map +1 -1
  344. package/dist/utils/estimate.js +1 -1
  345. package/dist/utils/estimate.js.map +1 -1
  346. package/dist/utils/event-stream.d.ts +10 -1
  347. package/dist/utils/event-stream.d.ts.map +1 -1
  348. package/dist/utils/event-stream.js +25 -0
  349. package/dist/utils/event-stream.js.map +1 -1
  350. package/dist/utils/identity.d.ts +65 -0
  351. package/dist/utils/identity.d.ts.map +1 -0
  352. package/dist/utils/identity.js +141 -0
  353. package/dist/utils/identity.js.map +1 -0
  354. package/dist/utils/json-parse.d.ts +10 -0
  355. package/dist/utils/json-parse.d.ts.map +1 -1
  356. package/dist/utils/json-parse.js +26 -1
  357. package/dist/utils/json-parse.js.map +1 -1
  358. package/dist/utils/model-operations.d.ts +3 -1
  359. package/dist/utils/model-operations.d.ts.map +1 -1
  360. package/dist/utils/model-operations.js +6 -0
  361. package/dist/utils/model-operations.js.map +1 -1
  362. package/dist/utils/openrouter-cost.d.ts +24 -0
  363. package/dist/utils/openrouter-cost.d.ts.map +1 -0
  364. package/dist/utils/openrouter-cost.js +34 -0
  365. package/dist/utils/openrouter-cost.js.map +1 -0
  366. package/dist/utils/overflow.d.ts +1 -0
  367. package/dist/utils/overflow.d.ts.map +1 -1
  368. package/dist/utils/overflow.js +8 -1
  369. package/dist/utils/overflow.js.map +1 -1
  370. package/dist/utils/provider-retry.d.ts +2 -0
  371. package/dist/utils/provider-retry.d.ts.map +1 -1
  372. package/dist/utils/provider-retry.js +2 -0
  373. package/dist/utils/provider-retry.js.map +1 -1
  374. package/dist/utils/retry.d.ts.map +1 -1
  375. package/dist/utils/retry.js +29 -10
  376. package/dist/utils/retry.js.map +1 -1
  377. package/dist/utils/streaming-json.d.ts +84 -0
  378. package/dist/utils/streaming-json.d.ts.map +1 -0
  379. package/dist/utils/streaming-json.js +513 -0
  380. package/dist/utils/streaming-json.js.map +1 -0
  381. package/dist/utils/{typebox-helpers.d.ts → string-enum.d.ts} +5 -5
  382. package/dist/utils/string-enum.d.ts.map +1 -0
  383. package/dist/utils/{typebox-helpers.js → string-enum.js} +5 -6
  384. package/dist/utils/string-enum.js.map +1 -0
  385. package/dist/utils/text.d.ts +7 -0
  386. package/dist/utils/text.d.ts.map +1 -1
  387. package/dist/utils/text.js +9 -0
  388. package/dist/utils/text.js.map +1 -1
  389. package/dist/utils/transcript.d.ts +23 -9
  390. package/dist/utils/transcript.d.ts.map +1 -1
  391. package/dist/utils/transcript.js +30 -19
  392. package/dist/utils/transcript.js.map +1 -1
  393. package/dist/utils/validation.d.ts +5 -3
  394. package/dist/utils/validation.d.ts.map +1 -1
  395. package/dist/utils/validation.js +27 -445
  396. package/dist/utils/validation.js.map +1 -1
  397. package/dist/utils/version.d.ts +2 -0
  398. package/dist/utils/version.d.ts.map +1 -0
  399. package/dist/utils/version.js +3 -0
  400. package/dist/utils/version.js.map +1 -0
  401. package/package.json +19 -10
  402. package/dist/utils/assistant-message-frame.d.ts +0 -77
  403. package/dist/utils/assistant-message-frame.d.ts.map +0 -1
  404. package/dist/utils/assistant-message-frame.js +0 -425
  405. package/dist/utils/assistant-message-frame.js.map +0 -1
  406. package/dist/utils/pi-user-agent.d.ts +0 -2
  407. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  408. package/dist/utils/pi-user-agent.js +0 -12
  409. package/dist/utils/pi-user-agent.js.map +0 -1
  410. package/dist/utils/typebox-helpers.d.ts.map +0 -1
  411. package/dist/utils/typebox-helpers.js.map +0 -1
@@ -0,0 +1,162 @@
1
+ import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
2
+ import { isRecord, parseClassifierUsage, postClassifierRequest, requiredNumber, } from "./classifier-shared.js";
3
+ /**
4
+ * OpenAI's Decisions API: `POST /v1/decisions` with `{ model, input, questions }`.
5
+ * https://developers.openai.com/api/docs/guides/decisions
6
+ *
7
+ * The state is sent as JSON text. With images, the input becomes one user message with the state
8
+ * as `input_text` followed by `input_image` data URLs. Questions map to Decisions types:
9
+ * `choice` to `choice`, `score` to `score`, and `bool` to `predicate`. Predicates have no
10
+ * criteria field, so the meanings of true and false are appended to the instructions.
11
+ *
12
+ * Only OpenAI API keys work: Sign in with ChatGPT tokens are rejected on this route.
13
+ */
14
+ const LABEL = "OpenAI Decisions";
15
+ /** The endpoint accepts at most this many image parts per request. */
16
+ const MAX_IMAGES = 128;
17
+ function predicateInstructions(question) {
18
+ const meanings = [
19
+ question.criteria.true ? `True means: ${question.criteria.true}` : "",
20
+ question.criteria.false ? `False means: ${question.criteria.false}` : "",
21
+ ].filter(Boolean);
22
+ return meanings.length > 0 ? `${question.instructions}\n\n${meanings.join("\n")}` : question.instructions;
23
+ }
24
+ function wireQuestion(name, question) {
25
+ if (question.type === "choice") {
26
+ return {
27
+ type: "choice",
28
+ name,
29
+ instructions: question.instructions,
30
+ choices: Object.entries(question.criteria).map(([value, description]) => description ? { value, description } : { value }),
31
+ };
32
+ }
33
+ if (question.type === "score") {
34
+ return {
35
+ type: "score",
36
+ name,
37
+ instructions: question.instructions,
38
+ levels: question.criteria.map((label) => ({ label })),
39
+ };
40
+ }
41
+ return { type: "predicate", name, instructions: predicateInstructions(question) };
42
+ }
43
+ function wireInput(context) {
44
+ const state = JSON.stringify(context.state);
45
+ const images = context.images ?? [];
46
+ if (images.length === 0)
47
+ return state;
48
+ if (images.length > MAX_IMAGES) {
49
+ throw new Error(`${LABEL} accepts at most ${MAX_IMAGES} images, got ${images.length}`);
50
+ }
51
+ return [
52
+ {
53
+ role: "user",
54
+ content: [
55
+ { type: "input_text", text: state },
56
+ ...images.map((image) => ({
57
+ type: "input_image",
58
+ image_url: `data:${image.mimeType};base64,${image.data}`,
59
+ })),
60
+ ],
61
+ },
62
+ ];
63
+ }
64
+ function choiceProbabilities(value, id) {
65
+ if (!Array.isArray(value))
66
+ throw new Error(`${LABEL} returned invalid probabilities for ${id}`);
67
+ return Object.fromEntries(value.map((entry) => {
68
+ if (!isRecord(entry) || typeof entry.value !== "string") {
69
+ throw new Error(`${LABEL} returned invalid probabilities for ${id}`);
70
+ }
71
+ return [entry.value, requiredNumber(LABEL, entry.probability, `probability for ${id}.${entry.value}`)];
72
+ }));
73
+ }
74
+ function parseAnswer(id, question, answer) {
75
+ if (answer.type === "refusal")
76
+ throw new Error(`${LABEL} refused to answer ${id}`);
77
+ if (question.type === "choice") {
78
+ if (answer.type !== "choice" || typeof answer.choice !== "string") {
79
+ throw new Error(`${LABEL} did not return a choice answer for ${id}`);
80
+ }
81
+ return {
82
+ type: "choice",
83
+ choice: answer.choice,
84
+ probabilities: choiceProbabilities(answer.probabilities, id),
85
+ confidence: requiredNumber(LABEL, answer.confidence, `confidence for ${id}`),
86
+ };
87
+ }
88
+ if (question.type === "score") {
89
+ if (answer.type !== "score")
90
+ throw new Error(`${LABEL} did not return a score answer for ${id}`);
91
+ return {
92
+ type: "score",
93
+ score: requiredNumber(LABEL, answer.score, `score for ${id}`),
94
+ confidence: requiredNumber(LABEL, answer.confidence, `confidence for ${id}`),
95
+ };
96
+ }
97
+ if (answer.type !== "predicate")
98
+ throw new Error(`${LABEL} did not return a predicate answer for ${id}`);
99
+ return { type: "bool", probability: requiredNumber(LABEL, answer.probability, `probability for ${id}`) };
100
+ }
101
+ function parseAnswers(value, context) {
102
+ if (!Array.isArray(value))
103
+ throw new Error(`${LABEL} returned an unexpected response`);
104
+ const byName = new Map();
105
+ for (const answer of value) {
106
+ if (isRecord(answer) && typeof answer.name === "string")
107
+ byName.set(answer.name, answer);
108
+ }
109
+ return Object.fromEntries(Object.entries(context.questions).map(([id, question]) => {
110
+ const answer = byName.get(id);
111
+ if (!answer)
112
+ throw new Error(`${LABEL} did not return an answer for ${id}`);
113
+ return [id, parseAnswer(id, question, answer)];
114
+ }));
115
+ }
116
+ /**
117
+ * Cloudflare in front of api.openai.com answers 504 with an HTML page when a request runs longer
118
+ * than about five seconds. Large inputs, currently above roughly 600K tokens, hit this limit, and
119
+ * retrying the same input runs into it again, so 504 is not retried.
120
+ */
121
+ const NO_RETRY_STATUSES = [504];
122
+ function errorMessage(error) {
123
+ if (error.status === 504) {
124
+ return `${LABEL} error (504): the request timed out at the gateway. Very large inputs (above roughly 600K tokens) currently exceed its time limit.`;
125
+ }
126
+ return formatProviderError(normalizeProviderError(error), `${LABEL} error`);
127
+ }
128
+ /** Classification through OpenAI's Decisions API. */
129
+ export const classify = async (model, context, options) => {
130
+ const output = {
131
+ api: model.api,
132
+ provider: model.provider,
133
+ model: model.id,
134
+ answers: {},
135
+ stopReason: "stop",
136
+ timestamp: Date.now(),
137
+ };
138
+ try {
139
+ if (model.api !== "openai-decisions")
140
+ throw new Error(`Unsupported classifier API: ${model.api}`);
141
+ const body = await postClassifierRequest(LABEL, new URL("decisions", `${model.baseUrl.replace(/\/+$/u, "")}/`), model, {
142
+ model: model.id,
143
+ input: wireInput(context),
144
+ questions: Object.entries(context.questions).map(([id, question]) => wireQuestion(id, question)),
145
+ }, options, NO_RETRY_STATUSES);
146
+ if (!isRecord(body))
147
+ throw new Error(`${LABEL} returned an unexpected response`);
148
+ // Set before parsing answers: a request with malformed or refused answers was still billed.
149
+ const usage = parseClassifierUsage(body.usage, model);
150
+ if (usage)
151
+ output.usage = usage;
152
+ output.answers = parseAnswers(body.answers, context);
153
+ return output;
154
+ }
155
+ catch (error) {
156
+ output.answers = {};
157
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
158
+ output.errorMessage = errorMessage(error);
159
+ return output;
160
+ }
161
+ };
162
+ //# sourceMappingURL=openai-decisions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-decisions.js","sourceRoot":"","sources":["../../src/api/openai-decisions.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tClassifierAnswer,\n\tClassifierContext,\n\tClassifierFunction,\n\tClassifierOptions,\n\tClassifierQuestion,\n\tClassifierResult,\n} from \"../types.ts\";\nimport { formatProviderError, normalizeProviderError } from \"../utils/error-body.ts\";\nimport {\n\ttype ClassifierHttpError,\n\tisRecord,\n\tparseClassifierUsage,\n\tpostClassifierRequest,\n\trequiredNumber,\n} from \"./classifier-shared.ts\";\n\n/**\n * OpenAI's Decisions API: `POST /v1/decisions` with `{ model, input, questions }`.\n * https://developers.openai.com/api/docs/guides/decisions\n *\n * The state is sent as JSON text. With images, the input becomes one user message with the state\n * as `input_text` followed by `input_image` data URLs. Questions map to Decisions types:\n * `choice` to `choice`, `score` to `score`, and `bool` to `predicate`. Predicates have no\n * criteria field, so the meanings of true and false are appended to the instructions.\n *\n * Only OpenAI API keys work: Sign in with ChatGPT tokens are rejected on this route.\n */\n\nconst LABEL = \"OpenAI Decisions\";\n\n/** The endpoint accepts at most this many image parts per request. */\nconst MAX_IMAGES = 128;\n\ninterface DecisionQuestion {\n\ttype: \"predicate\" | \"choice\" | \"score\";\n\tname: string;\n\tinstructions: string;\n\tchoices?: Array<{ value: string; description?: string }>;\n\tlevels?: Array<{ label: string }>;\n}\n\nfunction predicateInstructions(question: Extract<ClassifierQuestion, { type: \"bool\" }>): string {\n\tconst meanings = [\n\t\tquestion.criteria.true ? `True means: ${question.criteria.true}` : \"\",\n\t\tquestion.criteria.false ? `False means: ${question.criteria.false}` : \"\",\n\t].filter(Boolean);\n\treturn meanings.length > 0 ? `${question.instructions}\\n\\n${meanings.join(\"\\n\")}` : question.instructions;\n}\n\nfunction wireQuestion(name: string, question: ClassifierQuestion): DecisionQuestion {\n\tif (question.type === \"choice\") {\n\t\treturn {\n\t\t\ttype: \"choice\",\n\t\t\tname,\n\t\t\tinstructions: question.instructions,\n\t\t\tchoices: Object.entries(question.criteria).map(([value, description]) =>\n\t\t\t\tdescription ? { value, description } : { value },\n\t\t\t),\n\t\t};\n\t}\n\tif (question.type === \"score\") {\n\t\treturn {\n\t\t\ttype: \"score\",\n\t\t\tname,\n\t\t\tinstructions: question.instructions,\n\t\t\tlevels: question.criteria.map((label) => ({ label })),\n\t\t};\n\t}\n\treturn { type: \"predicate\", name, instructions: predicateInstructions(question) };\n}\n\nfunction wireInput(context: ClassifierContext): unknown {\n\tconst state = JSON.stringify(context.state);\n\tconst images = context.images ?? [];\n\tif (images.length === 0) return state;\n\tif (images.length > MAX_IMAGES) {\n\t\tthrow new Error(`${LABEL} accepts at most ${MAX_IMAGES} images, got ${images.length}`);\n\t}\n\treturn [\n\t\t{\n\t\t\trole: \"user\",\n\t\t\tcontent: [\n\t\t\t\t{ type: \"input_text\", text: state },\n\t\t\t\t...images.map((image) => ({\n\t\t\t\t\ttype: \"input_image\",\n\t\t\t\t\timage_url: `data:${image.mimeType};base64,${image.data}`,\n\t\t\t\t})),\n\t\t\t],\n\t\t},\n\t];\n}\n\nfunction choiceProbabilities(value: unknown, id: string): Record<string, number> {\n\tif (!Array.isArray(value)) throw new Error(`${LABEL} returned invalid probabilities for ${id}`);\n\treturn Object.fromEntries(\n\t\tvalue.map((entry) => {\n\t\t\tif (!isRecord(entry) || typeof entry.value !== \"string\") {\n\t\t\t\tthrow new Error(`${LABEL} returned invalid probabilities for ${id}`);\n\t\t\t}\n\t\t\treturn [entry.value, requiredNumber(LABEL, entry.probability, `probability for ${id}.${entry.value}`)];\n\t\t}),\n\t);\n}\n\nfunction parseAnswer(id: string, question: ClassifierQuestion, answer: Record<string, unknown>): ClassifierAnswer {\n\tif (answer.type === \"refusal\") throw new Error(`${LABEL} refused to answer ${id}`);\n\tif (question.type === \"choice\") {\n\t\tif (answer.type !== \"choice\" || typeof answer.choice !== \"string\") {\n\t\t\tthrow new Error(`${LABEL} did not return a choice answer for ${id}`);\n\t\t}\n\t\treturn {\n\t\t\ttype: \"choice\",\n\t\t\tchoice: answer.choice,\n\t\t\tprobabilities: choiceProbabilities(answer.probabilities, id),\n\t\t\tconfidence: requiredNumber(LABEL, answer.confidence, `confidence for ${id}`),\n\t\t};\n\t}\n\tif (question.type === \"score\") {\n\t\tif (answer.type !== \"score\") throw new Error(`${LABEL} did not return a score answer for ${id}`);\n\t\treturn {\n\t\t\ttype: \"score\",\n\t\t\tscore: requiredNumber(LABEL, answer.score, `score for ${id}`),\n\t\t\tconfidence: requiredNumber(LABEL, answer.confidence, `confidence for ${id}`),\n\t\t};\n\t}\n\tif (answer.type !== \"predicate\") throw new Error(`${LABEL} did not return a predicate answer for ${id}`);\n\treturn { type: \"bool\", probability: requiredNumber(LABEL, answer.probability, `probability for ${id}`) };\n}\n\nfunction parseAnswers(value: unknown, context: ClassifierContext): Record<string, ClassifierAnswer> {\n\tif (!Array.isArray(value)) throw new Error(`${LABEL} returned an unexpected response`);\n\tconst byName = new Map<string, Record<string, unknown>>();\n\tfor (const answer of value) {\n\t\tif (isRecord(answer) && typeof answer.name === \"string\") byName.set(answer.name, answer);\n\t}\n\treturn Object.fromEntries(\n\t\tObject.entries(context.questions).map(([id, question]) => {\n\t\t\tconst answer = byName.get(id);\n\t\t\tif (!answer) throw new Error(`${LABEL} did not return an answer for ${id}`);\n\t\t\treturn [id, parseAnswer(id, question, answer)];\n\t\t}),\n\t);\n}\n\n/**\n * Cloudflare in front of api.openai.com answers 504 with an HTML page when a request runs longer\n * than about five seconds. Large inputs, currently above roughly 600K tokens, hit this limit, and\n * retrying the same input runs into it again, so 504 is not retried.\n */\nconst NO_RETRY_STATUSES = [504];\n\nfunction errorMessage(error: unknown): string {\n\tif ((error as Partial<ClassifierHttpError>).status === 504) {\n\t\treturn `${LABEL} error (504): the request timed out at the gateway. Very large inputs (above roughly 600K tokens) currently exceed its time limit.`;\n\t}\n\treturn formatProviderError(normalizeProviderError(error), `${LABEL} error`);\n}\n\n/** Classification through OpenAI's Decisions API. */\nexport const classify: ClassifierFunction<ClassifierOptions> = async (model, context, options) => {\n\tconst output: ClassifierResult = {\n\t\tapi: model.api,\n\t\tprovider: model.provider,\n\t\tmodel: model.id,\n\t\tanswers: {},\n\t\tstopReason: \"stop\",\n\t\ttimestamp: Date.now(),\n\t};\n\n\ttry {\n\t\tif (model.api !== \"openai-decisions\") throw new Error(`Unsupported classifier API: ${model.api}`);\n\t\tconst body = await postClassifierRequest(\n\t\t\tLABEL,\n\t\t\tnew URL(\"decisions\", `${model.baseUrl.replace(/\\/+$/u, \"\")}/`),\n\t\t\tmodel,\n\t\t\t{\n\t\t\t\tmodel: model.id,\n\t\t\t\tinput: wireInput(context),\n\t\t\t\tquestions: Object.entries(context.questions).map(([id, question]) => wireQuestion(id, question)),\n\t\t\t},\n\t\t\toptions,\n\t\t\tNO_RETRY_STATUSES,\n\t\t);\n\t\tif (!isRecord(body)) throw new Error(`${LABEL} returned an unexpected response`);\n\t\t// Set before parsing answers: a request with malformed or refused answers was still billed.\n\t\tconst usage = parseClassifierUsage(body.usage, model);\n\t\tif (usage) output.usage = usage;\n\t\toutput.answers = parseAnswers(body.answers, context);\n\t\treturn output;\n\t} catch (error) {\n\t\toutput.answers = {};\n\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\toutput.errorMessage = errorMessage(error);\n\t\treturn output;\n\t}\n};\n"]}
@@ -0,0 +1,3 @@
1
+ import type { ProviderClassifier } from "../types.ts";
2
+ export declare const openAIDecisionsApi: () => ProviderClassifier;
3
+ //# sourceMappingURL=openai-decisions.lazy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-decisions.lazy.d.ts","sourceRoot":"","sources":["../../src/api/openai-decisions.lazy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,eAAO,MAAM,kBAAkB,QAAO,kBAGpC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export const openAIDecisionsApi = () => ({
2
+ classify: async (model, context, options) => (await import("./openai-decisions.js")).classify(model, context, options),
3
+ });
4
+ //# sourceMappingURL=openai-decisions.lazy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-decisions.lazy.js","sourceRoot":"","sources":["../../src/api/openai-decisions.lazy.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAuB,EAAE,CAAC,CAAC;IAC5D,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAC3C,CAAC,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC;CAC1E,CAAC,CAAC","sourcesContent":["import type { ProviderClassifier } from \"../types.ts\";\n\nexport const openAIDecisionsApi = (): ProviderClassifier => ({\n\tclassify: async (model, context, options) =>\n\t\t(await import(\"./openai-decisions.ts\")).classify(model, context, options),\n});\n"]}
@@ -1,6 +1,7 @@
1
1
  import type { Tool as OpenAITool, ResponseCreateParamsStreaming, ResponseInput, ResponseStreamEvent } from "openai/resources/responses/responses.js";
2
2
  import type { Api, AssistantMessage, Model, StreamOptions, Tool, TranscriptContext, Usage } from "../types.ts";
3
3
  import type { AssistantMessageEventStream } from "../utils/event-stream.ts";
4
+ import { type TranscriptToolSupport } from "../utils/transcript.ts";
4
5
  export interface OpenAIResponsesStreamOptions {
5
6
  onProviderStreamEvent?: StreamOptions["onProviderStreamEvent"];
6
7
  serviceTier?: ResponseCreateParamsStreaming["service_tier"];
@@ -16,6 +17,16 @@ export interface ConvertResponsesMessagesOptions {
16
17
  supportsAdditionalTools?: boolean;
17
18
  supportsToolSearch?: boolean;
18
19
  toolOptions?: ConvertResponsesToolsOptions;
20
+ /**
21
+ * Effort for the reply being generated. When set, the thinking level is sent as
22
+ * configuration_update items instead of a changing reasoning.effort, which would invalidate the
23
+ * prompt cache. Each item sets the effort from that point on: one goes before every earlier
24
+ * reply with the level recorded on it (`providerThinkingLevel`), and one with this effort goes
25
+ * at the end. Earlier input therefore never changes. OpenAI documents the item before user
26
+ * messages only. After tool results it was verified on gpt-6-luna (October 2026) to be accepted
27
+ * and to apply to the next reply.
28
+ */
29
+ configurationUpdateEffort?: string;
19
30
  }
20
31
  export interface ConvertResponsesToolsOptions {
21
32
  strict?: boolean | null;
@@ -23,6 +34,21 @@ export interface ConvertResponsesToolsOptions {
23
34
  supportsOpenAIGrammarTools?: boolean;
24
35
  toolSearchResult?: boolean;
25
36
  }
37
+ /**
38
+ * How a Responses transport expresses tool changes.
39
+ *
40
+ * `additional_tools` items accumulate: a later item adds tools and replaces same-name
41
+ * definitions from earlier items, but it cannot replace a top-level tool and cannot remove any
42
+ * tool (verified with strict schemas on the OpenAI, Codex, and Copilot backends, October 2026).
43
+ * Like Codex, every tool is therefore declared in `additional_tools` items, starting with one
44
+ * at the head of the input, and removals are announced as text. Synthetic tool search can
45
+ * only add tools. Without mid-conversation system messages there is nothing to anchor.
46
+ */
47
+ export declare function getResponsesToolSupport(options: {
48
+ supportsMidConvoSystemMessages?: boolean;
49
+ supportsAdditionalTools?: boolean;
50
+ supportsToolSearch?: boolean;
51
+ }): TranscriptToolSupport;
26
52
  export declare function convertResponsesMessages<TApi extends Api>(model: Model<TApi>, context: TranscriptContext, allowedToolCallProviders: ReadonlySet<string>, options?: ConvertResponsesMessagesOptions): ResponseInput;
27
53
  export declare function convertResponsesTools(tools: readonly Tool[], options?: ConvertResponsesToolsOptions): OpenAITool[];
28
54
  export declare function processResponsesStream<TApi extends Api>(openaiStream: AsyncIterable<ResponseStreamEvent>, output: AssistantMessage, stream: AssistantMessageEventStream, model: Model<TApi>, options?: OpenAIResponsesStreamOptions): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"openai-responses-shared.d.ts","sourceRoot":"","sources":["../../src/api/openai-responses-shared.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,IAAI,IAAI,UAAU,EAClB,6BAA6B,EAC7B,aAAa,EAQb,mBAAmB,EAEnB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EACX,GAAG,EACH,gBAAgB,EAEhB,KAAK,EAEL,aAAa,EAKb,IAAI,EAEJ,iBAAiB,EACjB,KAAK,EACL,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAoF5E,MAAM,WAAW,4BAA4B;IAC5C,qBAAqB,CAAC,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IAC/D,WAAW,CAAC,EAAE,6BAA6B,CAAC,cAAc,CAAC,CAAC;IAC5D,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,kBAAkB,CAAC,EAAE,CACpB,mBAAmB,EAAE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,EAC9E,kBAAkB,EAAE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,KACzE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAC/D,uBAAuB,CAAC,EAAE,CACzB,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,KAClE,IAAI,CAAC;CACV;AAED,MAAM,WAAW,+BAA+B;IAC/C,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,0GAA0G;IAC1G,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,4BAA4B,CAAC;CAC3C;AAED,MAAM,WAAW,4BAA4B;IAC5C,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B;AAMD,wBAAgB,wBAAwB,CAAC,IAAI,SAAS,GAAG,EACxD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,iBAAiB,EAC1B,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAAC,EAC7C,OAAO,CAAC,EAAE,+BAA+B,GACvC,aAAa,CA4Mf;AAMD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,4BAA4B,GAAG,UAAU,EAAE,CAqClH;AAoCD,wBAAsB,sBAAsB,CAAC,IAAI,SAAS,GAAG,EAC5D,YAAY,EAAE,aAAa,CAAC,mBAAmB,CAAC,EAChD,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,2BAA2B,EACnC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,CAAC,EAAE,4BAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAmWf"}
1
+ {"version":3,"file":"openai-responses-shared.d.ts","sourceRoot":"","sources":["../../src/api/openai-responses-shared.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,IAAI,IAAI,UAAU,EAElB,6BAA6B,EAC7B,aAAa,EAQb,mBAAmB,EAEnB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,KAAK,EACX,GAAG,EACH,gBAAgB,EAEhB,KAAK,EAEL,aAAa,EAKb,IAAI,EAEJ,iBAAiB,EACjB,KAAK,EACL,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAK5E,OAAO,EAA6C,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAgF/G,MAAM,WAAW,4BAA4B;IAC5C,qBAAqB,CAAC,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAC;IAC/D,WAAW,CAAC,EAAE,6BAA6B,CAAC,cAAc,CAAC,CAAC;IAC5D,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,kBAAkB,CAAC,EAAE,CACpB,mBAAmB,EAAE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,EAC9E,kBAAkB,EAAE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,KACzE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAC/D,uBAAuB,CAAC,EAAE,CACzB,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,6BAA6B,CAAC,cAAc,CAAC,GAAG,SAAS,KAClE,IAAI,CAAC;CACV;AAED,MAAM,WAAW,+BAA+B;IAC/C,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzD,0GAA0G;IAC1G,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,4BAA4B,CAAC;IAC3C;;;;;;;;OAQG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,4BAA4B;IAC5C,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC3B;AAMD;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAChD,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC7B,GAAG,qBAAqB,CAGxB;AAkBD,wBAAgB,wBAAwB,CAAC,IAAI,SAAS,GAAG,EACxD,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,EAAE,iBAAiB,EAC1B,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAAC,EAC7C,OAAO,CAAC,EAAE,+BAA+B,GACvC,aAAa,CA4Of;AAMD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,4BAA4B,GAAG,UAAU,EAAE,CAqClH;AA2BD,wBAAsB,sBAAsB,CAAC,IAAI,SAAS,GAAG,EAC5D,YAAY,EAAE,aAAa,CAAC,mBAAmB,CAAC,EAChD,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,2BAA2B,EACnC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,OAAO,CAAC,EAAE,4BAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAgVf"}
@@ -1,10 +1,10 @@
1
1
  import { calculateCost } from "../models.js";
2
2
  import { shortHash } from "../utils/hash.js";
3
- import { parseStreamingJson } from "../utils/json-parse.js";
4
3
  import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
4
+ import { ToolCallArgumentsStream } from "../utils/streaming-json.js";
5
5
  import { getSystemMessageText, renderSystemMessageUpdate } from "../utils/text.js";
6
6
  import { resolveTranscript, resolveTranscriptTools } from "../utils/transcript.js";
7
- import { appendGrammarToolInputJsonDelta, getGrammarToolInput, getJsonSchemaToolParameters, resolveGrammarConstrainedSampling, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
7
+ import { appendGrammarToolInputJsonDelta, closeGrammarToolInputJson, getGrammarToolInput, getJsonSchemaToolParameters, resolveGrammarConstrainedSampling, resolveJsonSchemaStrictSampling, } from "./constrained-sampling.js";
8
8
  import { transformMessages } from "./transform-messages.js";
9
9
  // =============================================================================
10
10
  // Utilities
@@ -46,7 +46,7 @@ function convertToolResultOutput(model, content) {
46
46
  .map((c) => c.text)
47
47
  .join("\n");
48
48
  const images = content.filter((c) => c.type === "image");
49
- const hasText = textResult.length > 0;
49
+ const hasText = textResult.trim().length > 0;
50
50
  if (images.length === 0 || !model.input.includes("image")) {
51
51
  return sanitizeSurrogates(hasText ? textResult : images.length > 0 ? "(see attached image)" : "(no tool output)");
52
52
  }
@@ -66,6 +66,34 @@ function convertToolResultOutput(model, content) {
66
66
  // =============================================================================
67
67
  // Message conversion
68
68
  // =============================================================================
69
+ /**
70
+ * How a Responses transport expresses tool changes.
71
+ *
72
+ * `additional_tools` items accumulate: a later item adds tools and replaces same-name
73
+ * definitions from earlier items, but it cannot replace a top-level tool and cannot remove any
74
+ * tool (verified with strict schemas on the OpenAI, Codex, and Copilot backends, October 2026).
75
+ * Like Codex, every tool is therefore declared in `additional_tools` items, starting with one
76
+ * at the head of the input, and removals are announced as text. Synthetic tool search can
77
+ * only add tools. Without mid-conversation system messages there is nothing to anchor.
78
+ */
79
+ export function getResponsesToolSupport(options) {
80
+ if (options.supportsMidConvoSystemMessages && options.supportsAdditionalTools)
81
+ return "changes";
82
+ return options.supportsToolSearch ? "additions" : "none";
83
+ }
84
+ /**
85
+ * Precedes every additional_tools item after the first. Without it, models read the latest item as
86
+ * the complete tool set once a removal was announced; Codex precedes its incremental tool batches
87
+ * with the same guidance. Restating the tools that are still unavailable keeps smaller models
88
+ * (gpt-5.6-luna, gpt-6-luna) from calling them again after later loads (verified October 2026).
89
+ */
90
+ function renderIncrementalToolsNotice(unavailable) {
91
+ const notice = "This is an incremental tools update. Previously declared tools remain available unless explicitly marked unavailable. A tool declared again here is available again, and its latest definition replaces the earlier one.";
92
+ return unavailable.size > 0 ? `${notice}\nStill unavailable: ${[...unavailable].join(", ")}.` : notice;
93
+ }
94
+ function renderToolRemovalNotice(names) {
95
+ return `The following tools are no longer available. Do not call them:\n${names.map((name) => `- ${name}`).join("\n")}`;
96
+ }
69
97
  export function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
70
98
  const normalizedContext = resolveTranscript(context, options?.supportsMidConvoSystemMessages);
71
99
  const messages = [];
@@ -94,20 +122,30 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
94
122
  return `${normalizedCallId}|${normalizedItemId}`;
95
123
  };
96
124
  const transformedMessages = transformMessages(normalizedContext.messages, model, normalizeToolCallId);
97
- const transcriptTools = resolveTranscriptTools(normalizedContext.messages, (options?.supportsAdditionalTools ?? false) || (options?.supportsToolSearch ?? false));
125
+ const toolSupport = getResponsesToolSupport(options ?? {});
126
+ const transcriptTools = resolveTranscriptTools(normalizedContext.messages, toolSupport);
127
+ let declaredAdditionalTools = false;
128
+ /** Tools announced as removed and not declared again since. */
129
+ const unavailableTools = new Set();
98
130
  const appendSystemToolAdditions = (message, seed) => {
99
- const tools = transcriptTools.anchorsAdditions ? (message.toolsAdded ?? []) : [];
131
+ const tools = message.toolsAdded ?? [];
132
+ for (const tool of tools)
133
+ unavailableTools.delete(tool.name);
100
134
  if (tools.length === 0)
101
135
  return;
102
- if (options?.supportsAdditionalTools) {
136
+ if (toolSupport === "changes") {
137
+ if (declaredAdditionalTools) {
138
+ messages.push({ role: instructionRole, content: renderIncrementalToolsNotice(unavailableTools) });
139
+ }
140
+ declaredAdditionalTools = true;
103
141
  messages.push({
104
142
  type: "additional_tools",
105
143
  role: "developer",
106
- tools: convertResponsesTools(tools, options.toolOptions),
144
+ tools: convertResponsesTools(tools, options?.toolOptions),
107
145
  });
108
146
  return;
109
147
  }
110
- if (!options?.supportsToolSearch)
148
+ if (toolSupport !== "additions")
111
149
  return;
112
150
  const names = tools.map((tool) => tool.name);
113
151
  const callId = `pi_tool_load_${shortHash(`${seed}:${names.join(",")}`)}`;
@@ -123,21 +161,41 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
123
161
  call_id: callId,
124
162
  execution: "client",
125
163
  status: "completed",
126
- tools: convertResponsesTools(tools, { ...options.toolOptions, toolSearchResult: true }),
164
+ tools: convertResponsesTools(tools, { ...options?.toolOptions, toolSearchResult: true }),
127
165
  });
128
166
  };
129
167
  const includeInitialSystemMessage = options?.includeSystemPrompt ?? true;
168
+ const pushConfigurationUpdate = (effort) => {
169
+ messages.push({
170
+ type: "configuration_update",
171
+ reasoning: { effort: effort },
172
+ });
173
+ };
130
174
  const compat = model.compat;
131
175
  const instructionRole = model.reasoning && compat?.supportsDeveloperRole !== false ? "developer" : "system";
132
176
  let msgIndex = 0;
133
177
  let sourceIndex = 0;
178
+ let systemIndex = 0;
134
179
  for (const msg of transformedMessages) {
135
180
  const isLeadingSystemMessage = sourceIndex++ === 0 && msg.role === "system";
136
181
  if (msg.role === "system") {
137
- if (!isLeadingSystemMessage)
182
+ // transformMessages keeps system messages in order, so their position matches the transcript.
183
+ const anchorsToolChanges = systemIndex++ >= transcriptTools.foldedSystemMessages;
184
+ if (anchorsToolChanges)
138
185
  appendSystemToolAdditions(msg, `system:${msgIndex}`);
139
186
  if (!isLeadingSystemMessage || includeInitialSystemMessage) {
140
- const text = isLeadingSystemMessage ? getSystemMessageText(msg) : renderSystemMessageUpdate(msg);
187
+ const parts = [isLeadingSystemMessage ? getSystemMessageText(msg) : renderSystemMessageUpdate(msg)];
188
+ if (anchorsToolChanges) {
189
+ // A redefinition is a removal plus an addition of the same name; only true removals are announced.
190
+ const added = new Set((msg.toolsAdded ?? []).map((tool) => tool.name));
191
+ const removed = (msg.toolsRemoved ?? []).map((tool) => tool.name).filter((name) => !added.has(name));
192
+ if (removed.length > 0)
193
+ parts.push(renderToolRemovalNotice(removed));
194
+ // Recorded after this message's additions, whose notice precedes this removal notice.
195
+ for (const name of removed)
196
+ unavailableTools.add(name);
197
+ }
198
+ const text = parts.filter((part) => part.length > 0).join("\n\n");
141
199
  if (text.length > 0) {
142
200
  messages.push({ role: instructionRole, content: sanitizeSurrogates(text) });
143
201
  }
@@ -247,6 +305,11 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
247
305
  }
248
306
  if (output.length === 0)
249
307
  continue;
308
+ if (options?.configurationUpdateEffort !== undefined &&
309
+ isSameModel &&
310
+ msg.providerThinkingLevel !== undefined) {
311
+ pushConfigurationUpdate(msg.providerThinkingLevel);
312
+ }
250
313
  messages.push(...output);
251
314
  }
252
315
  else if (msg.role === "toolResult") {
@@ -270,6 +333,8 @@ export function convertResponsesMessages(model, context, allowedToolCallProvider
270
333
  if (!isLeadingSystemMessage)
271
334
  msgIndex++;
272
335
  }
336
+ if (options?.configurationUpdateEffort !== undefined)
337
+ pushConfigurationUpdate(options.configurationUpdateEffort);
273
338
  return messages;
274
339
  }
275
340
  // =============================================================================
@@ -309,24 +374,11 @@ export function convertResponsesTools(tools, options) {
309
374
  return functionTool;
310
375
  });
311
376
  }
312
- function getCustomToolCallInput(block) {
313
- const property = block.customInput?.property;
314
- if (property === undefined)
315
- return "";
316
- const value = block.arguments[property];
317
- return typeof value === "string" ? value : "";
318
- }
319
- function appendCustomToolCallInput(block, nextInput, close) {
320
- const customInput = block.customInput;
321
- if (!customInput)
322
- return undefined;
323
- const delta = appendGrammarToolInputJsonDelta(customInput.jsonBuffer, customInput.property, nextInput, close);
324
- block.arguments = { [customInput.property]: nextInput };
325
- return delta;
326
- }
327
377
  export async function processResponsesStream(openaiStream, output, stream, model, options) {
328
378
  let sawTerminalResponseEvent = false;
329
379
  const outputSlots = new Map();
380
+ // Tool calls whose output_item.done has not arrived yet.
381
+ const unfinishedToolCalls = new Set();
330
382
  const reasoningBlocksById = new Map();
331
383
  const applyMessagePhaseStopReason = (item) => {
332
384
  if (item.type === "message" && item.phase === "final_answer") {
@@ -371,19 +423,21 @@ export async function processResponsesStream(openaiStream, output, stream, model
371
423
  return slot;
372
424
  }
373
425
  if (item.type === "function_call") {
426
+ const args = new ToolCallArgumentsStream();
374
427
  const block = {
375
428
  type: "toolCall",
376
429
  id: `${item.call_id}|${item.id}`,
377
430
  name: item.name,
378
- arguments: {},
431
+ arguments: item.arguments ? args.append(item.arguments) : {},
379
432
  ...(item.namespace !== undefined ? { namespace: item.namespace } : {}),
380
- partialJson: item.arguments || "",
381
433
  };
382
434
  output.content.push(block);
435
+ unfinishedToolCalls.add(block);
383
436
  const slot = {
384
437
  type: "toolCall",
385
438
  block,
386
439
  contentIndex: output.content.length - 1,
440
+ input: { kind: "json", arguments: args },
387
441
  };
388
442
  outputSlots.set(outputIndex, slot);
389
443
  stream.push({ type: "toolcall_start", contentIndex: slot.contentIndex, partial: output });
@@ -398,16 +452,14 @@ export async function processResponsesStream(openaiStream, output, stream, model
398
452
  name: item.name,
399
453
  arguments: { [inputProperty]: input },
400
454
  ...(item.namespace !== undefined ? { namespace: item.namespace } : {}),
401
- customInput: {
402
- property: inputProperty,
403
- jsonBuffer: { input: "", started: false, closed: false },
404
- },
405
455
  };
406
456
  output.content.push(block);
457
+ unfinishedToolCalls.add(block);
407
458
  const slot = {
408
459
  type: "toolCall",
409
460
  block,
410
461
  contentIndex: output.content.length - 1,
462
+ input: customInput(inputProperty, input),
411
463
  };
412
464
  outputSlots.set(outputIndex, slot);
413
465
  stream.push({ type: "toolcall_start", contentIndex: slot.contentIndex, partial: output });
@@ -567,36 +619,31 @@ export async function processResponsesStream(openaiStream, output, stream, model
567
619
  }
568
620
  else if (event.type === "response.function_call_arguments.delta") {
569
621
  const slot = getSlot(event.output_index, "toolCall");
570
- if (!slot || slot.block.partialJson === undefined)
622
+ if (slot?.input.kind !== "json")
571
623
  continue;
572
- slot.block.partialJson += event.delta;
573
- slot.block.arguments = parseStreamingJson(slot.block.partialJson);
624
+ slot.block.arguments = slot.input.arguments.append(event.delta);
574
625
  pushToolCallDelta(slot, event.delta);
575
626
  }
576
627
  else if (event.type === "response.function_call_arguments.done") {
577
628
  const slot = getSlot(event.output_index, "toolCall");
578
- if (!slot || slot.block.partialJson === undefined)
629
+ if (slot?.input.kind !== "json")
579
630
  continue;
580
- const previousPartialJson = slot.block.partialJson;
581
- slot.block.partialJson = event.arguments;
582
- slot.block.arguments = parseStreamingJson(slot.block.partialJson);
583
- if (event.arguments.startsWith(previousPartialJson)) {
584
- const delta = event.arguments.slice(previousPartialJson.length);
585
- if (delta.length > 0)
586
- pushToolCallDelta(slot, delta);
587
- }
631
+ const replaced = slot.input.arguments.replace(event.arguments);
632
+ slot.block.arguments = replaced.arguments;
633
+ if (replaced.delta)
634
+ pushToolCallDelta(slot, replaced.delta);
588
635
  }
589
636
  else if (event.type === "response.custom_tool_call_input.delta") {
590
637
  const slot = getSlot(event.output_index, "toolCall");
591
- if (!slot || !slot.block.customInput)
638
+ if (slot?.input.kind !== "custom")
592
639
  continue;
593
- pushToolCallDelta(slot, appendCustomToolCallInput(slot.block, getCustomToolCallInput(slot.block) + event.delta, false));
640
+ pushToolCallDelta(slot, appendCustomInput(slot.block, slot.input, event.delta));
594
641
  }
595
642
  else if (event.type === "response.custom_tool_call_input.done") {
596
643
  const slot = getSlot(event.output_index, "toolCall");
597
- if (!slot || !slot.block.customInput)
644
+ if (slot?.input.kind !== "custom")
598
645
  continue;
599
- pushToolCallDelta(slot, appendCustomToolCallInput(slot.block, event.input, true));
646
+ pushToolCallDelta(slot, closeCustomInput(slot.block, slot.input, event.input));
600
647
  }
601
648
  else if (event.type === "response.output_item.done") {
602
649
  const item = event.item;
@@ -628,15 +675,13 @@ export async function processResponsesStream(openaiStream, output, stream, model
628
675
  });
629
676
  outputSlots.delete(event.output_index);
630
677
  }
631
- else if (item.type === "function_call" &&
632
- slot?.type === "toolCall" &&
633
- slot.block.partialJson !== undefined) {
634
- slot.block.arguments = parseStreamingJson(item.arguments || slot.block.partialJson || "{}");
678
+ else if (item.type === "function_call" && slot?.type === "toolCall" && slot.input.kind === "json") {
679
+ if (item.arguments)
680
+ slot.input.arguments.replace(item.arguments);
681
+ Object.assign(slot.block, slot.input.arguments.finish());
635
682
  if (item.namespace !== undefined)
636
683
  slot.block.namespace = item.namespace;
637
- // Finalize in-place and strip the scratch buffer so replay only
638
- // carries parsed arguments.
639
- delete slot.block.partialJson;
684
+ unfinishedToolCalls.delete(slot.block);
640
685
  stream.push({
641
686
  type: "toolcall_end",
642
687
  contentIndex: slot.contentIndex,
@@ -645,11 +690,11 @@ export async function processResponsesStream(openaiStream, output, stream, model
645
690
  });
646
691
  outputSlots.delete(event.output_index);
647
692
  }
648
- else if (item.type === "custom_tool_call" && slot?.type === "toolCall" && slot.block.customInput) {
649
- pushToolCallDelta(slot, appendCustomToolCallInput(slot.block, item.input ?? getCustomToolCallInput(slot.block), true));
693
+ else if (item.type === "custom_tool_call" && slot?.type === "toolCall" && slot.input.kind === "custom") {
694
+ pushToolCallDelta(slot, closeCustomInput(slot.block, slot.input, item.input));
650
695
  if (item.namespace !== undefined)
651
696
  slot.block.namespace = item.namespace;
652
- delete slot.block.customInput;
697
+ unfinishedToolCalls.delete(slot.block);
653
698
  stream.push({
654
699
  type: "toolcall_end",
655
700
  contentIndex: slot.contentIndex,
@@ -683,18 +728,39 @@ export async function processResponsesStream(openaiStream, output, stream, model
683
728
  }
684
729
  // The agent runs every tool call in the final message. Refuse to hand over calls whose
685
730
  // output_item.done never arrived: their arguments may be cut off or mixed up, e.g. when a
686
- // non-compliant server omits output_index. Finished calls have their scratch buffers removed.
731
+ // non-compliant server omits output_index.
687
732
  if (output.stopReason === "toolUse") {
688
- for (const block of output.content) {
689
- if (block.type !== "toolCall")
690
- continue;
691
- const toolCall = block;
692
- if (toolCall.partialJson !== undefined || toolCall.customInput !== undefined) {
693
- throw new Error(`OpenAI Responses stream completed with an unfinished tool call: ${toolCall.name} (${toolCall.id})`);
694
- }
733
+ for (const toolCall of unfinishedToolCalls) {
734
+ throw new Error(`OpenAI Responses stream completed with an unfinished tool call: ${toolCall.name} (${toolCall.id})`);
695
735
  }
696
736
  }
697
737
  }
738
+ /** Stream state of a custom tool call that starts with `initialInput`. */
739
+ function customInput(property, initialInput) {
740
+ const buffer = { input: "", started: false, closed: false };
741
+ return {
742
+ kind: "custom",
743
+ property,
744
+ buffer,
745
+ unsent: appendGrammarToolInputJsonDelta(buffer, property, initialInput) ?? "",
746
+ };
747
+ }
748
+ /** Append to a streaming custom tool input; returns the matching piece of its JSON arguments text. */
749
+ function appendCustomInput(block, input, delta) {
750
+ return sendCustomInput(block, input, appendGrammarToolInputJsonDelta(input.buffer, input.property, delta));
751
+ }
752
+ /** Close a custom tool input with the provider's complete input, if it sent one. */
753
+ function closeCustomInput(block, input, finalInput) {
754
+ return sendCustomInput(block, input, closeGrammarToolInputJson(input.buffer, input.property, finalInput));
755
+ }
756
+ function sendCustomInput(block, input, jsonDelta) {
757
+ block.arguments = { [input.property]: input.buffer.input };
758
+ if (input.unsent === "")
759
+ return jsonDelta;
760
+ const sent = input.unsent + (jsonDelta ?? "");
761
+ input.unsent = "";
762
+ return sent;
763
+ }
698
764
  function mapStopReason(status, incompleteReason) {
699
765
  if (!status)
700
766
  return { stopReason: "stop" };