@vib-rato/ai 0.16.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 (525) hide show
  1. package/CHANGELOG.md +3354 -0
  2. package/README.md +1194 -0
  3. package/dist/types/adapter-internals/provider-safety-stop.d.ts +45 -0
  4. package/dist/types/api-registry.d.ts +30 -0
  5. package/dist/types/auth-broker/client.d.ts +80 -0
  6. package/dist/types/auth-broker/index.d.ts +5 -0
  7. package/dist/types/auth-broker/redact.d.ts +7 -0
  8. package/dist/types/auth-broker/refresher.d.ts +25 -0
  9. package/dist/types/auth-broker/remote-store.d.ts +153 -0
  10. package/dist/types/auth-broker/server.d.ts +32 -0
  11. package/dist/types/auth-broker/types.d.ts +132 -0
  12. package/dist/types/auth-broker/wire-schemas.d.ts +555 -0
  13. package/dist/types/auth-gateway/http.d.ts +40 -0
  14. package/dist/types/auth-gateway/index.d.ts +3 -0
  15. package/dist/types/auth-gateway/server.d.ts +70 -0
  16. package/dist/types/auth-gateway/types.d.ts +129 -0
  17. package/dist/types/auth-storage.d.ts +1074 -0
  18. package/dist/types/cli.d.ts +2 -0
  19. package/dist/types/codex-tools.d.ts +4 -0
  20. package/dist/types/context-cap-policy.d.ts +68 -0
  21. package/dist/types/core.d.ts +35 -0
  22. package/dist/types/index.d.ts +55 -0
  23. package/dist/types/model-cache.d.ts +24 -0
  24. package/dist/types/model-manager.d.ts +77 -0
  25. package/dist/types/model-pricing.d.ts +3 -0
  26. package/dist/types/model-retirements.d.ts +6 -0
  27. package/dist/types/model-thinking.d.ts +100 -0
  28. package/dist/types/models.d.ts +21 -0
  29. package/dist/types/openai-completions-compat.d.ts +34 -0
  30. package/dist/types/provider-details.d.ts +24 -0
  31. package/dist/types/provider-models/bundled-references.d.ts +4 -0
  32. package/dist/types/provider-models/descriptors.d.ts +48 -0
  33. package/dist/types/provider-models/google.d.ts +20 -0
  34. package/dist/types/provider-models/index.d.ts +5 -0
  35. package/dist/types/provider-models/ollama.d.ts +7 -0
  36. package/dist/types/provider-models/openai-compat.d.ts +293 -0
  37. package/dist/types/provider-models/special.d.ts +29 -0
  38. package/dist/types/providers/amazon-bedrock.d.ts +60 -0
  39. package/dist/types/providers/anthropic-messages-server-schema.d.ts +450 -0
  40. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  41. package/dist/types/providers/anthropic.d.ts +280 -0
  42. package/dist/types/providers/aws-credential-config.d.ts +19 -0
  43. package/dist/types/providers/aws-credentials.d.ts +43 -0
  44. package/dist/types/providers/aws-eventstream.d.ts +38 -0
  45. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  46. package/dist/types/providers/azure-openai-responses.d.ts +22 -0
  47. package/dist/types/providers/composer-discipline.d.ts +32 -0
  48. package/dist/types/providers/cursor/client-version.d.ts +10 -0
  49. package/dist/types/providers/cursor/exec-modern.d.ts +98 -0
  50. package/dist/types/providers/cursor/gen/agent_pb.d.ts +16769 -0
  51. package/dist/types/providers/cursor-pi-args.d.ts +119 -0
  52. package/dist/types/providers/cursor.d.ts +72 -0
  53. package/dist/types/providers/dashscope-token-plan-headers.d.ts +57 -0
  54. package/dist/types/providers/error-message.d.ts +27 -0
  55. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  56. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  57. package/dist/types/providers/google-auth.d.ts +26 -0
  58. package/dist/types/providers/google-gemini-cli.d.ts +75 -0
  59. package/dist/types/providers/google-gemini-headers.d.ts +43 -0
  60. package/dist/types/providers/google-shared.d.ts +183 -0
  61. package/dist/types/providers/google-types.d.ts +138 -0
  62. package/dist/types/providers/google-vertex.d.ts +11 -0
  63. package/dist/types/providers/google.d.ts +4 -0
  64. package/dist/types/providers/grammar.d.ts +1 -0
  65. package/dist/types/providers/kimi.d.ts +27 -0
  66. package/dist/types/providers/kiro-api-key.d.ts +50 -0
  67. package/dist/types/providers/kiro-codewhisperer.d.ts +11 -0
  68. package/dist/types/providers/mock.d.ts +189 -0
  69. package/dist/types/providers/ollama.d.ts +41 -0
  70. package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
  71. package/dist/types/providers/openai-bounded-rate-limits.d.ts +3 -0
  72. package/dist/types/providers/openai-chat-server-schema.d.ts +1733 -0
  73. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  74. package/dist/types/providers/openai-codex/constants.d.ts +26 -0
  75. package/dist/types/providers/openai-codex/request-transformer.d.ts +50 -0
  76. package/dist/types/providers/openai-codex/response-handler.d.ts +18 -0
  77. package/dist/types/providers/openai-codex-responses.d.ts +71 -0
  78. package/dist/types/providers/openai-completions-compat.d.ts +6 -0
  79. package/dist/types/providers/openai-completions.d.ts +35 -0
  80. package/dist/types/providers/openai-opencodex-responses.d.ts +10 -0
  81. package/dist/types/providers/openai-request-transform.d.ts +4 -0
  82. package/dist/types/providers/openai-responses-server-schema.d.ts +392 -0
  83. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  84. package/dist/types/providers/openai-responses-shared.d.ts +106 -0
  85. package/dist/types/providers/openai-responses.d.ts +37 -0
  86. package/dist/types/providers/pi-native-client.d.ts +29 -0
  87. package/dist/types/providers/pi-native-server.d.ts +60 -0
  88. package/dist/types/providers/register-builtins.d.ts +59 -0
  89. package/dist/types/providers/synthetic.d.ts +26 -0
  90. package/dist/types/providers/transform-messages.d.ts +33 -0
  91. package/dist/types/providers/vision-guard.d.ts +8 -0
  92. package/dist/types/rate-limit-utils.d.ts +19 -0
  93. package/dist/types/stream.d.ts +45 -0
  94. package/dist/types/types.d.ts +1073 -0
  95. package/dist/types/usage/claude.d.ts +3 -0
  96. package/dist/types/usage/gemini.d.ts +2 -0
  97. package/dist/types/usage/github-copilot.d.ts +7 -0
  98. package/dist/types/usage/google-antigravity.d.ts +2 -0
  99. package/dist/types/usage/grok-cli.d.ts +17 -0
  100. package/dist/types/usage/kimi.d.ts +4 -0
  101. package/dist/types/usage/minimax-code.d.ts +2 -0
  102. package/dist/types/usage/openai-codex.d.ts +3 -0
  103. package/dist/types/usage/shared.d.ts +1 -0
  104. package/dist/types/usage/zai.d.ts +2 -0
  105. package/dist/types/usage.d.ts +264 -0
  106. package/dist/types/utils/abort.d.ts +19 -0
  107. package/dist/types/utils/anthropic-auth.d.ts +39 -0
  108. package/dist/types/utils/block-symbols.d.ts +6 -0
  109. package/dist/types/utils/discovery/antigravity.d.ts +67 -0
  110. package/dist/types/utils/discovery/codex.d.ts +38 -0
  111. package/dist/types/utils/discovery/cursor.d.ts +49 -0
  112. package/dist/types/utils/discovery/gemini.d.ts +25 -0
  113. package/dist/types/utils/discovery/index.d.ts +4 -0
  114. package/dist/types/utils/discovery/openai-compatible.d.ts +81 -0
  115. package/dist/types/utils/event-stream.d.ts +41 -0
  116. package/dist/types/utils/fallback-transport.d.ts +110 -0
  117. package/dist/types/utils/fireworks-model-id.d.ts +10 -0
  118. package/dist/types/utils/foundry.d.ts +11 -0
  119. package/dist/types/utils/h2-fetch.d.ts +22 -0
  120. package/dist/types/utils/http-inspector.d.ts +59 -0
  121. package/dist/types/utils/idle-iterator.d.ts +122 -0
  122. package/dist/types/utils/json-parse.d.ts +98 -0
  123. package/dist/types/utils/oauth/alibaba-token-plan.d.ts +19 -0
  124. package/dist/types/utils/oauth/anthropic.d.ts +41 -0
  125. package/dist/types/utils/oauth/api-key-login.d.ts +38 -0
  126. package/dist/types/utils/oauth/api-key-validation.d.ts +39 -0
  127. package/dist/types/utils/oauth/bizrouter.d.ts +1 -0
  128. package/dist/types/utils/oauth/callback-server.d.ts +80 -0
  129. package/dist/types/utils/oauth/cerebras.d.ts +1 -0
  130. package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +18 -0
  131. package/dist/types/utils/oauth/commandcode.d.ts +1 -0
  132. package/dist/types/utils/oauth/cursor.d.ts +15 -0
  133. package/dist/types/utils/oauth/deepinfra.d.ts +1 -0
  134. package/dist/types/utils/oauth/deepseek.d.ts +10 -0
  135. package/dist/types/utils/oauth/firepass.d.ts +1 -0
  136. package/dist/types/utils/oauth/fireworks.d.ts +1 -0
  137. package/dist/types/utils/oauth/fugu.d.ts +1 -0
  138. package/dist/types/utils/oauth/github-copilot.d.ts +38 -0
  139. package/dist/types/utils/oauth/gitlab-duo.d.ts +3 -0
  140. package/dist/types/utils/oauth/glm-zcode.d.ts +71 -0
  141. package/dist/types/utils/oauth/google-antigravity.d.ts +11 -0
  142. package/dist/types/utils/oauth/google-gemini-cli.d.ts +10 -0
  143. package/dist/types/utils/oauth/google-oauth-shared.d.ts +28 -0
  144. package/dist/types/utils/oauth/huggingface.d.ts +19 -0
  145. package/dist/types/utils/oauth/index.d.ts +39 -0
  146. package/dist/types/utils/oauth/kagi.d.ts +17 -0
  147. package/dist/types/utils/oauth/kilo.d.ts +5 -0
  148. package/dist/types/utils/oauth/kimi.d.ts +17 -0
  149. package/dist/types/utils/oauth/kiro.d.ts +71 -0
  150. package/dist/types/utils/oauth/litellm.d.ts +18 -0
  151. package/dist/types/utils/oauth/lm-studio.d.ts +17 -0
  152. package/dist/types/utils/oauth/mara.d.ts +1 -0
  153. package/dist/types/utils/oauth/minimax-code.d.ts +28 -0
  154. package/dist/types/utils/oauth/moonshot.d.ts +1 -0
  155. package/dist/types/utils/oauth/nanogpt.d.ts +1 -0
  156. package/dist/types/utils/oauth/nvidia.d.ts +18 -0
  157. package/dist/types/utils/oauth/ollama-cloud.d.ts +2 -0
  158. package/dist/types/utils/oauth/ollama.d.ts +18 -0
  159. package/dist/types/utils/oauth/openai-codex.d.ts +21 -0
  160. package/dist/types/utils/oauth/opencode.d.ts +18 -0
  161. package/dist/types/utils/oauth/opengateway.d.ts +1 -0
  162. package/dist/types/utils/oauth/openrouter.d.ts +1 -0
  163. package/dist/types/utils/oauth/parallel.d.ts +17 -0
  164. package/dist/types/utils/oauth/perplexity.d.ts +4 -0
  165. package/dist/types/utils/oauth/pkce.d.ts +8 -0
  166. package/dist/types/utils/oauth/qianfan.d.ts +17 -0
  167. package/dist/types/utils/oauth/qwen-portal.d.ts +19 -0
  168. package/dist/types/utils/oauth/sglang.d.ts +16 -0
  169. package/dist/types/utils/oauth/synthetic.d.ts +1 -0
  170. package/dist/types/utils/oauth/tavily.d.ts +17 -0
  171. package/dist/types/utils/oauth/together.d.ts +1 -0
  172. package/dist/types/utils/oauth/types.d.ts +56 -0
  173. package/dist/types/utils/oauth/venice.d.ts +18 -0
  174. package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +18 -0
  175. package/dist/types/utils/oauth/vllm.d.ts +16 -0
  176. package/dist/types/utils/oauth/xai.d.ts +30 -0
  177. package/dist/types/utils/oauth/xiaomi.d.ts +25 -0
  178. package/dist/types/utils/oauth/zai.d.ts +18 -0
  179. package/dist/types/utils/oauth/zenmux.d.ts +1 -0
  180. package/dist/types/utils/overflow.d.ts +14 -0
  181. package/dist/types/utils/parse-bind.d.ts +26 -0
  182. package/dist/types/utils/provider-response.d.ts +6 -0
  183. package/dist/types/utils/provider-safety-stop.d.ts +8 -0
  184. package/dist/types/utils/proxy.d.ts +7 -0
  185. package/dist/types/utils/retry-after.d.ts +3 -0
  186. package/dist/types/utils/retry-budget.d.ts +1 -0
  187. package/dist/types/utils/retry.d.ts +29 -0
  188. package/dist/types/utils/schema/adapt.d.ts +24 -0
  189. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  190. package/dist/types/utils/schema/dereference.d.ts +11 -0
  191. package/dist/types/utils/schema/draft.d.ts +10 -0
  192. package/dist/types/utils/schema/equality.d.ts +4 -0
  193. package/dist/types/utils/schema/fields.d.ts +49 -0
  194. package/dist/types/utils/schema/index.d.ts +14 -0
  195. package/dist/types/utils/schema/json-schema-validator.d.ts +12 -0
  196. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  197. package/dist/types/utils/schema/normalize.d.ts +93 -0
  198. package/dist/types/utils/schema/root-combinator.d.ts +12 -0
  199. package/dist/types/utils/schema/spill.d.ts +8 -0
  200. package/dist/types/utils/schema/stamps.d.ts +25 -0
  201. package/dist/types/utils/schema/types.d.ts +4 -0
  202. package/dist/types/utils/schema/wire.d.ts +54 -0
  203. package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
  204. package/dist/types/utils/sse-debug.d.ts +10 -0
  205. package/dist/types/utils/tool-call-healing.d.ts +80 -0
  206. package/dist/types/utils/tool-choice-capability.d.ts +57 -0
  207. package/dist/types/utils/tool-choice.d.ts +50 -0
  208. package/dist/types/utils/validation.d.ts +17 -0
  209. package/dist/types/utils.d.ts +117 -0
  210. package/package.json +152 -0
  211. package/src/adapter-internals/provider-safety-stop.d.ts +45 -0
  212. package/src/adapter-internals/provider-safety-stop.ts +156 -0
  213. package/src/api-registry.d.ts +30 -0
  214. package/src/api-registry.ts +96 -0
  215. package/src/auth-broker/client.ts +444 -0
  216. package/src/auth-broker/index.ts +5 -0
  217. package/src/auth-broker/redact.ts +39 -0
  218. package/src/auth-broker/refresher.ts +130 -0
  219. package/src/auth-broker/remote-store.ts +1576 -0
  220. package/src/auth-broker/server.ts +764 -0
  221. package/src/auth-broker/types.ts +164 -0
  222. package/src/auth-broker/wire-schemas.ts +261 -0
  223. package/src/auth-gateway/http.ts +198 -0
  224. package/src/auth-gateway/index.ts +3 -0
  225. package/src/auth-gateway/server.ts +1315 -0
  226. package/src/auth-gateway/types.ts +160 -0
  227. package/src/auth-storage.ts +7312 -0
  228. package/src/cli.ts +269 -0
  229. package/src/codex-tools.d.ts +4 -0
  230. package/src/codex-tools.ts +24 -0
  231. package/src/context-cap-policy.d.ts +68 -0
  232. package/src/context-cap-policy.ts +123 -0
  233. package/src/core.ts +44 -0
  234. package/src/index.ts +61 -0
  235. package/src/model-cache.ts +236 -0
  236. package/src/model-manager.ts +744 -0
  237. package/src/model-pricing.d.ts +3 -0
  238. package/src/model-pricing.ts +68 -0
  239. package/src/model-retirements.d.ts +6 -0
  240. package/src/model-retirements.ts +19 -0
  241. package/src/model-thinking.d.ts +100 -0
  242. package/src/model-thinking.ts +1054 -0
  243. package/src/models.d.ts +21 -0
  244. package/src/models.json +94672 -0
  245. package/src/models.json.d.ts +9 -0
  246. package/src/models.ts +126 -0
  247. package/src/openai-completions-compat.d.ts +34 -0
  248. package/src/openai-completions-compat.ts +383 -0
  249. package/src/prompts/composer-bash-policy-recovery.md +1 -0
  250. package/src/prompts/cursor-composer-bash-policy-recovery.md +1 -0
  251. package/src/prompts/cursor-composer-edit-discipline.md +7 -0
  252. package/src/prompts/turn-aborted-guidance.md +4 -0
  253. package/src/provider-details.ts +90 -0
  254. package/src/provider-models/bundled-references.ts +38 -0
  255. package/src/provider-models/descriptors.ts +392 -0
  256. package/src/provider-models/google.ts +92 -0
  257. package/src/provider-models/index.ts +5 -0
  258. package/src/provider-models/ollama.ts +159 -0
  259. package/src/provider-models/openai-compat.ts +2920 -0
  260. package/src/provider-models/special.ts +185 -0
  261. package/src/providers/amazon-bedrock.d.ts +60 -0
  262. package/src/providers/amazon-bedrock.ts +939 -0
  263. package/src/providers/anthropic-messages-server-schema.ts +229 -0
  264. package/src/providers/anthropic-messages-server.ts +839 -0
  265. package/src/providers/anthropic.d.ts +280 -0
  266. package/src/providers/anthropic.ts +4421 -0
  267. package/src/providers/aws-credential-config.d.ts +19 -0
  268. package/src/providers/aws-credential-config.ts +179 -0
  269. package/src/providers/aws-credentials.d.ts +43 -0
  270. package/src/providers/aws-credentials.ts +457 -0
  271. package/src/providers/aws-eventstream.d.ts +38 -0
  272. package/src/providers/aws-eventstream.ts +185 -0
  273. package/src/providers/aws-sigv4.d.ts +55 -0
  274. package/src/providers/aws-sigv4.ts +218 -0
  275. package/src/providers/azure-openai-responses.d.ts +22 -0
  276. package/src/providers/azure-openai-responses.ts +447 -0
  277. package/src/providers/composer-discipline.d.ts +32 -0
  278. package/src/providers/composer-discipline.ts +95 -0
  279. package/src/providers/cursor/client-version.d.ts +10 -0
  280. package/src/providers/cursor/client-version.ts +10 -0
  281. package/src/providers/cursor/exec-modern.d.ts +98 -0
  282. package/src/providers/cursor/exec-modern.ts +497 -0
  283. package/src/providers/cursor/gen/agent_pb.d.ts +16769 -0
  284. package/src/providers/cursor/gen/agent_pb.ts +19780 -0
  285. package/src/providers/cursor/proto/agent.proto +4533 -0
  286. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  287. package/src/providers/cursor/proto/buf.yaml +17 -0
  288. package/src/providers/cursor-pi-args.d.ts +119 -0
  289. package/src/providers/cursor-pi-args.ts +187 -0
  290. package/src/providers/cursor.d.ts +72 -0
  291. package/src/providers/cursor.ts +3396 -0
  292. package/src/providers/dashscope-token-plan-headers.d.ts +57 -0
  293. package/src/providers/dashscope-token-plan-headers.ts +84 -0
  294. package/src/providers/error-message.d.ts +27 -0
  295. package/src/providers/error-message.ts +21 -0
  296. package/src/providers/github-copilot-headers.d.ts +40 -0
  297. package/src/providers/github-copilot-headers.ts +140 -0
  298. package/src/providers/gitlab-duo.d.ts +27 -0
  299. package/src/providers/gitlab-duo.ts +393 -0
  300. package/src/providers/google-auth.d.ts +26 -0
  301. package/src/providers/google-auth.ts +262 -0
  302. package/src/providers/google-gemini-cli.d.ts +75 -0
  303. package/src/providers/google-gemini-cli.ts +969 -0
  304. package/src/providers/google-gemini-headers.d.ts +43 -0
  305. package/src/providers/google-gemini-headers.ts +100 -0
  306. package/src/providers/google-shared.d.ts +183 -0
  307. package/src/providers/google-shared.ts +1105 -0
  308. package/src/providers/google-types.d.ts +138 -0
  309. package/src/providers/google-types.ts +167 -0
  310. package/src/providers/google-vertex.d.ts +11 -0
  311. package/src/providers/google-vertex.ts +124 -0
  312. package/src/providers/google.d.ts +4 -0
  313. package/src/providers/google.ts +41 -0
  314. package/src/providers/grammar.d.ts +1 -0
  315. package/src/providers/grammar.ts +70 -0
  316. package/src/providers/kimi.d.ts +27 -0
  317. package/src/providers/kimi.ts +52 -0
  318. package/src/providers/kiro-api-key.d.ts +50 -0
  319. package/src/providers/kiro-api-key.ts +786 -0
  320. package/src/providers/kiro-codewhisperer.d.ts +11 -0
  321. package/src/providers/kiro-codewhisperer.ts +600 -0
  322. package/src/providers/mock.ts +526 -0
  323. package/src/providers/ollama.d.ts +41 -0
  324. package/src/providers/ollama.ts +645 -0
  325. package/src/providers/openai-anthropic-shim.d.ts +31 -0
  326. package/src/providers/openai-anthropic-shim.ts +156 -0
  327. package/src/providers/openai-bounded-rate-limits.d.ts +3 -0
  328. package/src/providers/openai-bounded-rate-limits.ts +57 -0
  329. package/src/providers/openai-chat-server-schema.ts +254 -0
  330. package/src/providers/openai-chat-server.ts +724 -0
  331. package/src/providers/openai-codex/constants.d.ts +26 -0
  332. package/src/providers/openai-codex/constants.ts +43 -0
  333. package/src/providers/openai-codex/request-transformer.d.ts +50 -0
  334. package/src/providers/openai-codex/request-transformer.ts +219 -0
  335. package/src/providers/openai-codex/response-handler.d.ts +18 -0
  336. package/src/providers/openai-codex/response-handler.ts +111 -0
  337. package/src/providers/openai-codex-responses.d.ts +71 -0
  338. package/src/providers/openai-codex-responses.ts +3288 -0
  339. package/src/providers/openai-completions-compat.d.ts +6 -0
  340. package/src/providers/openai-completions-compat.ts +6 -0
  341. package/src/providers/openai-completions.d.ts +35 -0
  342. package/src/providers/openai-completions.ts +2294 -0
  343. package/src/providers/openai-opencodex-responses.ts +174 -0
  344. package/src/providers/openai-request-transform.d.ts +4 -0
  345. package/src/providers/openai-request-transform.ts +136 -0
  346. package/src/providers/openai-responses-server-schema.ts +290 -0
  347. package/src/providers/openai-responses-server.ts +1268 -0
  348. package/src/providers/openai-responses-shared.d.ts +106 -0
  349. package/src/providers/openai-responses-shared.ts +1253 -0
  350. package/src/providers/openai-responses.d.ts +37 -0
  351. package/src/providers/openai-responses.ts +989 -0
  352. package/src/providers/pi-native-client.d.ts +29 -0
  353. package/src/providers/pi-native-client.ts +243 -0
  354. package/src/providers/pi-native-server.ts +488 -0
  355. package/src/providers/register-builtins.d.ts +59 -0
  356. package/src/providers/register-builtins.ts +544 -0
  357. package/src/providers/synthetic.d.ts +26 -0
  358. package/src/providers/synthetic.ts +50 -0
  359. package/src/providers/transform-messages.d.ts +33 -0
  360. package/src/providers/transform-messages.ts +408 -0
  361. package/src/providers/vision-guard.d.ts +8 -0
  362. package/src/providers/vision-guard.ts +31 -0
  363. package/src/rate-limit-utils.d.ts +19 -0
  364. package/src/rate-limit-utils.ts +102 -0
  365. package/src/stream.d.ts +45 -0
  366. package/src/stream.ts +1306 -0
  367. package/src/types.d.ts +1073 -0
  368. package/src/types.ts +1305 -0
  369. package/src/usage/claude.ts +449 -0
  370. package/src/usage/gemini.ts +250 -0
  371. package/src/usage/github-copilot.ts +421 -0
  372. package/src/usage/google-antigravity.ts +201 -0
  373. package/src/usage/grok-cli.ts +259 -0
  374. package/src/usage/kimi.ts +285 -0
  375. package/src/usage/minimax-code.ts +31 -0
  376. package/src/usage/openai-codex.ts +503 -0
  377. package/src/usage/shared.ts +10 -0
  378. package/src/usage/zai.ts +247 -0
  379. package/src/usage.ts +190 -0
  380. package/src/utils/abort.d.ts +19 -0
  381. package/src/utils/abort.ts +51 -0
  382. package/src/utils/anthropic-auth.ts +95 -0
  383. package/src/utils/block-symbols.d.ts +6 -0
  384. package/src/utils/block-symbols.ts +11 -0
  385. package/src/utils/discovery/antigravity.ts +275 -0
  386. package/src/utils/discovery/codex.ts +362 -0
  387. package/src/utils/discovery/cursor.ts +388 -0
  388. package/src/utils/discovery/gemini.ts +248 -0
  389. package/src/utils/discovery/index.ts +4 -0
  390. package/src/utils/discovery/openai-compatible.ts +379 -0
  391. package/src/utils/event-stream.d.ts +41 -0
  392. package/src/utils/event-stream.ts +269 -0
  393. package/src/utils/fallback-transport.d.ts +110 -0
  394. package/src/utils/fallback-transport.ts +411 -0
  395. package/src/utils/fireworks-model-id.d.ts +10 -0
  396. package/src/utils/fireworks-model-id.ts +30 -0
  397. package/src/utils/foundry.d.ts +11 -0
  398. package/src/utils/foundry.ts +18 -0
  399. package/src/utils/h2-fetch.ts +60 -0
  400. package/src/utils/http-inspector.d.ts +59 -0
  401. package/src/utils/http-inspector.ts +380 -0
  402. package/src/utils/idle-iterator.d.ts +122 -0
  403. package/src/utils/idle-iterator.ts +410 -0
  404. package/src/utils/json-parse.d.ts +98 -0
  405. package/src/utils/json-parse.ts +607 -0
  406. package/src/utils/oauth/alibaba-token-plan.ts +60 -0
  407. package/src/utils/oauth/anthropic.ts +233 -0
  408. package/src/utils/oauth/api-key-login.ts +98 -0
  409. package/src/utils/oauth/api-key-validation.ts +344 -0
  410. package/src/utils/oauth/bizrouter.ts +15 -0
  411. package/src/utils/oauth/callback-server.d.ts +80 -0
  412. package/src/utils/oauth/callback-server.ts +359 -0
  413. package/src/utils/oauth/cerebras.ts +16 -0
  414. package/src/utils/oauth/cloudflare-ai-gateway.ts +48 -0
  415. package/src/utils/oauth/commandcode.ts +17 -0
  416. package/src/utils/oauth/cursor.ts +157 -0
  417. package/src/utils/oauth/deepinfra.ts +15 -0
  418. package/src/utils/oauth/deepseek.ts +53 -0
  419. package/src/utils/oauth/firepass.ts +24 -0
  420. package/src/utils/oauth/fireworks.ts +15 -0
  421. package/src/utils/oauth/fugu.ts +15 -0
  422. package/src/utils/oauth/github-copilot.d.ts +38 -0
  423. package/src/utils/oauth/github-copilot.ts +362 -0
  424. package/src/utils/oauth/gitlab-duo.ts +123 -0
  425. package/src/utils/oauth/glm-zcode.d.ts +71 -0
  426. package/src/utils/oauth/glm-zcode.ts +433 -0
  427. package/src/utils/oauth/google-antigravity.ts +200 -0
  428. package/src/utils/oauth/google-gemini-cli.ts +256 -0
  429. package/src/utils/oauth/google-oauth-shared.ts +110 -0
  430. package/src/utils/oauth/huggingface.ts +62 -0
  431. package/src/utils/oauth/index.ts +558 -0
  432. package/src/utils/oauth/kagi.ts +47 -0
  433. package/src/utils/oauth/kilo.ts +87 -0
  434. package/src/utils/oauth/kimi.d.ts +17 -0
  435. package/src/utils/oauth/kimi.ts +275 -0
  436. package/src/utils/oauth/kiro.ts +448 -0
  437. package/src/utils/oauth/litellm.ts +47 -0
  438. package/src/utils/oauth/lm-studio.ts +38 -0
  439. package/src/utils/oauth/mara.ts +16 -0
  440. package/src/utils/oauth/minimax-code.ts +78 -0
  441. package/src/utils/oauth/moonshot.ts +16 -0
  442. package/src/utils/oauth/nanogpt.ts +15 -0
  443. package/src/utils/oauth/nvidia.ts +70 -0
  444. package/src/utils/oauth/oauth.html +199 -0
  445. package/src/utils/oauth/ollama-cloud.ts +28 -0
  446. package/src/utils/oauth/ollama.ts +47 -0
  447. package/src/utils/oauth/openai-codex.ts +299 -0
  448. package/src/utils/oauth/opencode.ts +49 -0
  449. package/src/utils/oauth/opengateway.ts +15 -0
  450. package/src/utils/oauth/openrouter.ts +16 -0
  451. package/src/utils/oauth/parallel.ts +46 -0
  452. package/src/utils/oauth/perplexity.ts +225 -0
  453. package/src/utils/oauth/pkce.ts +18 -0
  454. package/src/utils/oauth/qianfan.ts +58 -0
  455. package/src/utils/oauth/qwen-portal.ts +60 -0
  456. package/src/utils/oauth/sglang.ts +42 -0
  457. package/src/utils/oauth/synthetic.ts +15 -0
  458. package/src/utils/oauth/tavily.ts +46 -0
  459. package/src/utils/oauth/together.ts +16 -0
  460. package/src/utils/oauth/types.d.ts +56 -0
  461. package/src/utils/oauth/types.ts +122 -0
  462. package/src/utils/oauth/venice.ts +59 -0
  463. package/src/utils/oauth/vercel-ai-gateway.ts +47 -0
  464. package/src/utils/oauth/vllm.ts +42 -0
  465. package/src/utils/oauth/xai.ts +246 -0
  466. package/src/utils/oauth/xiaomi.ts +199 -0
  467. package/src/utils/oauth/zai.ts +60 -0
  468. package/src/utils/oauth/zenmux.ts +15 -0
  469. package/src/utils/overflow.ts +275 -0
  470. package/src/utils/parse-bind.ts +81 -0
  471. package/src/utils/provider-response.d.ts +6 -0
  472. package/src/utils/provider-response.ts +30 -0
  473. package/src/utils/provider-safety-stop.ts +8 -0
  474. package/src/utils/proxy.d.ts +7 -0
  475. package/src/utils/proxy.ts +652 -0
  476. package/src/utils/retry-after.d.ts +3 -0
  477. package/src/utils/retry-after.ts +110 -0
  478. package/src/utils/retry-budget.d.ts +1 -0
  479. package/src/utils/retry-budget.ts +4 -0
  480. package/src/utils/retry.d.ts +29 -0
  481. package/src/utils/retry.ts +67 -0
  482. package/src/utils/schema/CONSTRAINTS.md +164 -0
  483. package/src/utils/schema/adapt.d.ts +24 -0
  484. package/src/utils/schema/adapt.ts +36 -0
  485. package/src/utils/schema/compatibility.d.ts +30 -0
  486. package/src/utils/schema/compatibility.ts +435 -0
  487. package/src/utils/schema/dereference.d.ts +11 -0
  488. package/src/utils/schema/dereference.ts +98 -0
  489. package/src/utils/schema/draft.d.ts +10 -0
  490. package/src/utils/schema/draft.ts +341 -0
  491. package/src/utils/schema/equality.d.ts +4 -0
  492. package/src/utils/schema/equality.ts +97 -0
  493. package/src/utils/schema/fields.d.ts +49 -0
  494. package/src/utils/schema/fields.ts +190 -0
  495. package/src/utils/schema/index.d.ts +14 -0
  496. package/src/utils/schema/index.ts +14 -0
  497. package/src/utils/schema/json-schema-validator.d.ts +12 -0
  498. package/src/utils/schema/json-schema-validator.ts +577 -0
  499. package/src/utils/schema/meta-validator.d.ts +2 -0
  500. package/src/utils/schema/meta-validator.ts +167 -0
  501. package/src/utils/schema/normalize.d.ts +93 -0
  502. package/src/utils/schema/normalize.ts +1588 -0
  503. package/src/utils/schema/root-combinator.d.ts +12 -0
  504. package/src/utils/schema/root-combinator.ts +143 -0
  505. package/src/utils/schema/spill.d.ts +8 -0
  506. package/src/utils/schema/spill.ts +43 -0
  507. package/src/utils/schema/stamps.d.ts +25 -0
  508. package/src/utils/schema/stamps.ts +97 -0
  509. package/src/utils/schema/types.d.ts +4 -0
  510. package/src/utils/schema/types.ts +11 -0
  511. package/src/utils/schema/wire.d.ts +54 -0
  512. package/src/utils/schema/wire.ts +213 -0
  513. package/src/utils/schema/zod-decontaminate.d.ts +31 -0
  514. package/src/utils/schema/zod-decontaminate.ts +331 -0
  515. package/src/utils/sse-debug.d.ts +10 -0
  516. package/src/utils/sse-debug.ts +289 -0
  517. package/src/utils/tool-call-healing.d.ts +80 -0
  518. package/src/utils/tool-call-healing.ts +298 -0
  519. package/src/utils/tool-choice-capability.d.ts +57 -0
  520. package/src/utils/tool-choice-capability.ts +633 -0
  521. package/src/utils/tool-choice.d.ts +50 -0
  522. package/src/utils/tool-choice.ts +99 -0
  523. package/src/utils/validation.ts +1080 -0
  524. package/src/utils.d.ts +117 -0
  525. package/src/utils.ts +523 -0
@@ -0,0 +1,989 @@
1
+ import { $credentialEnv, extractHttpStatusFromError, logger, structuredCloneJSON } from "@vib-rato/utils";
2
+ import OpenAI, { APIConnectionTimeoutError } from "openai";
3
+ import type {
4
+ Tool as OpenAITool,
5
+ ResponseCreateParamsStreaming,
6
+ ResponseInput,
7
+ } from "openai/resources/responses/responses";
8
+ import packageJson from "../../package.json" with { type: "json" };
9
+ import { getEnvApiKey } from "../stream";
10
+ import {
11
+ type AssistantMessage,
12
+ type CacheRetention,
13
+ type Context,
14
+ type FetchImpl,
15
+ isKnownProvider,
16
+ type MessageAttribution,
17
+ type Model,
18
+ type OpenAICompat,
19
+ type ProviderSessionState,
20
+ type ServiceTier,
21
+ type StreamFunction,
22
+ type StreamOptions,
23
+ type Tool,
24
+ type ToolChoice,
25
+ type ToolResultMessage,
26
+ } from "../types";
27
+ import {
28
+ createOpenAIResponsesHistoryPayload,
29
+ getOpenAIResponsesHistoryItems,
30
+ getOpenAIResponsesHistoryPayload,
31
+ isInvalidPromptError,
32
+ neutralizeReservedControlTokens,
33
+ neutralizeResponsesInputControlTokens,
34
+ normalizeSystemPrompts,
35
+ resolveCacheRetention,
36
+ sanitizeOpenAIResponsesHistoryItemsForReplay,
37
+ } from "../utils";
38
+ import { createAbortSourceTracker } from "../utils/abort";
39
+ import { AssistantMessageEventStream } from "../utils/event-stream";
40
+ import { EMPTY_RESPONSE_PROVIDER_CODE, transportFailureFacts } from "../utils/fallback-transport";
41
+ import { finalizeErrorMessage, type RawHttpRequestDump, rewriteCopilotError } from "../utils/http-inspector";
42
+ import {
43
+ FirstEventTimeoutError,
44
+ getOpenAIStreamIdleTimeoutMs,
45
+ getProviderFirstEventTimeoutFallbackMs,
46
+ getStreamFirstEventTimeoutMs,
47
+ iterateWithIdleTimeout,
48
+ resolveOpenAISdkRequestTimeoutMs,
49
+ } from "../utils/idle-iterator";
50
+ import { parseGitHubCopilotApiKey } from "../utils/oauth/github-copilot";
51
+ import { notifyProviderResponse } from "../utils/provider-response";
52
+ import { callWithCopilotModelRetry } from "../utils/retry";
53
+ import { resolveRetryBudget } from "../utils/retry-budget";
54
+ import {
55
+ adaptSchemaForStrict,
56
+ flattenToolRootCombinators,
57
+ NO_STRICT,
58
+ sanitizeSchemaForOpenAIResponses,
59
+ toolWireSchema,
60
+ } from "../utils/schema";
61
+ import { wrapFetchForSseDebug } from "../utils/sse-debug";
62
+ import { mapToOpenAIResponsesToolChoice, type OpenAIResponsesToolChoice } from "../utils/tool-choice";
63
+ import {
64
+ isForcedToolChoiceUnsupportedError,
65
+ markToolChoiceIncapability,
66
+ resolveToolChoice,
67
+ } from "../utils/tool-choice-capability";
68
+ import { COMPOSER_EDIT_DISCIPLINE_PROMPT, isComposerHarnessModel } from "./composer-discipline";
69
+ import { mergeDashScopeTokenPlanHeaders } from "./dashscope-token-plan-headers";
70
+ import {
71
+ buildCopilotDynamicHeaders,
72
+ hasCopilotVisionInput,
73
+ resolveGitHubCopilotBaseUrl,
74
+ } from "./github-copilot-headers";
75
+ import { compactGrammarDefinition } from "./grammar";
76
+ import { wrapOpenAIFetchForBoundedRateLimits } from "./openai-bounded-rate-limits";
77
+ import {
78
+ applyOpenAIRequestTransformBody,
79
+ applyOpenAIRequestTransformHeaders,
80
+ wrapFetchForOpenAIRequestTransform,
81
+ } from "./openai-request-transform";
82
+ import {
83
+ appendResponsesToolResultMessages,
84
+ applyCommonResponsesSamplingParams,
85
+ applyResponsesReasoningParams,
86
+ collectCustomCallIds,
87
+ collectKnownCallIds,
88
+ convertResponsesAssistantMessage,
89
+ convertResponsesInputContent,
90
+ createInitialResponsesAssistantMessage,
91
+ isOpenAIResponsesProgressEvent,
92
+ normalizeResponsesToolCallIdForTransform,
93
+ processResponsesStream,
94
+ repairOrphanResponsesToolOutputs,
95
+ } from "./openai-responses-shared";
96
+ import { transformMessages } from "./transform-messages";
97
+
98
+ /**
99
+ * Get prompt cache retention based on cacheRetention and base URL.
100
+ * Only applies to direct OpenAI API calls (api.openai.com).
101
+ */
102
+ function getPromptCacheRetention(baseUrl: string | undefined, cacheRetention: CacheRetention): "24h" | undefined {
103
+ if (cacheRetention !== "long") {
104
+ return undefined;
105
+ }
106
+ if (baseUrl && isDefaultOpenAIBaseUrl(baseUrl)) {
107
+ return "24h";
108
+ }
109
+ return undefined;
110
+ }
111
+
112
+ export function normalizeOpenAIResponsesPromptCacheKey(sessionId: string | undefined): string | undefined {
113
+ if (!sessionId || sessionId.length === 0) return undefined;
114
+ const wellFormed = sessionId.toWellFormed();
115
+ if (wellFormed.length <= 64) return wellFormed;
116
+ return `pc_${Bun.hash(wellFormed).toString(36)}`;
117
+ }
118
+
119
+ // OpenAI Responses-specific options
120
+ export interface OpenAIResponsesOptions extends StreamOptions {
121
+ reasoning?: "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
122
+ reasoningSummary?: "auto" | "detailed" | "concise" | null;
123
+ serviceTier?: ServiceTier;
124
+ toolChoice?: ToolChoice;
125
+ /**
126
+ * Enforce strict tool call/result pairing when building Responses API inputs.
127
+ * Azure OpenAI and GitHub Copilot Responses paths require tool results to match prior tool calls.
128
+ */
129
+ strictResponsesPairing?: boolean;
130
+ }
131
+
132
+ const OPENAI_RESPONSES_PROVIDER_SESSION_STATE_PREFIX = "openai-responses:";
133
+ const OPENAI_RESPONSES_FIRST_EVENT_TIMEOUT_MESSAGE =
134
+ "OpenAI responses stream timed out while waiting for the first event";
135
+ const OPENAI_DEFAULT_BASE_URL = "https://api.openai.com/v1";
136
+ const OPENAI_DEFAULT_BASE_URL_HOST = "api.openai.com";
137
+
138
+ function isDefaultOpenAIBaseUrl(baseUrl: string): boolean {
139
+ try {
140
+ const url = new URL(baseUrl);
141
+ return url.hostname === OPENAI_DEFAULT_BASE_URL_HOST && (url.pathname === "" || url.pathname === "/v1");
142
+ } catch {
143
+ return baseUrl === OPENAI_DEFAULT_BASE_URL;
144
+ }
145
+ }
146
+
147
+ function isCanonicalOpenAIAffinityOrigin(baseUrl: string | undefined): boolean {
148
+ if (!baseUrl) return false;
149
+ try {
150
+ const url = new URL(baseUrl);
151
+ return (
152
+ url.origin === "https://api.openai.com" &&
153
+ url.username === "" &&
154
+ url.password === "" &&
155
+ (url.pathname === "" || url.pathname === "/" || url.pathname === "/v1") &&
156
+ url.search === "" &&
157
+ url.hash === ""
158
+ );
159
+ } catch {
160
+ return false;
161
+ }
162
+ }
163
+ /**
164
+ * Official OpenAI keeps its existing session-routing behavior even when prompt
165
+ * caching is disabled. Relay affinity is opt-in, cache-enabled, and limited to
166
+ * explicitly supported openai or unknown provider ids so known non-target
167
+ * transports cannot inherit the headers.
168
+ */
169
+
170
+ function shouldSendOpenAIResponsesSessionHeaders(
171
+ model: Model<"openai-responses">,
172
+ baseUrl: string | undefined,
173
+ cacheRetention: CacheRetention,
174
+ ): boolean {
175
+ if (model.provider === "openai") {
176
+ if (isCanonicalOpenAIAffinityOrigin(baseUrl)) return true;
177
+ return cacheRetention !== "none" && model.compat?.supportsResponsesSessionAffinity === true;
178
+ }
179
+ if (cacheRetention === "none" || isKnownProvider(model.provider)) {
180
+ return false;
181
+ }
182
+ return (
183
+ Boolean(baseUrl?.trim()) &&
184
+ model.compat?.supportsResponsesSessionAffinity === true &&
185
+ !isCanonicalOpenAIAffinityOrigin(baseUrl)
186
+ );
187
+ }
188
+
189
+ function isOpenAIHostBaseUrl(baseUrl: string): boolean {
190
+ try {
191
+ const url = new URL(baseUrl);
192
+ return url.hostname === OPENAI_DEFAULT_BASE_URL_HOST;
193
+ } catch {
194
+ return baseUrl.toLowerCase().startsWith(OPENAI_DEFAULT_BASE_URL);
195
+ }
196
+ }
197
+
198
+ function resolveOpenAIProviderBaseUrl(
199
+ baseUrl: string | undefined,
200
+ authCredentialType: "api_key" | "oauth" | undefined,
201
+ ): string {
202
+ if (authCredentialType === "oauth") return OPENAI_DEFAULT_BASE_URL;
203
+ // Trusted sources only: this base URL becomes the request endpoint that carries
204
+ // the OpenAI credential, and `$env` merges the caller's `cwd/.env`, so reading it
205
+ // there would let repository content redirect authenticated traffic.
206
+ const envBaseUrl = $credentialEnv("OPENAI_BASE_URL");
207
+ const configuredBaseUrl = baseUrl?.trim();
208
+ if (envBaseUrl && (!configuredBaseUrl || isDefaultOpenAIBaseUrl(configuredBaseUrl))) {
209
+ return envBaseUrl;
210
+ }
211
+ return configuredBaseUrl || envBaseUrl || OPENAI_DEFAULT_BASE_URL;
212
+ }
213
+
214
+ /** Test seam: the provider base URL as resolved from trusted env. */
215
+ export function resolveOpenAIProviderBaseUrlForTest(
216
+ baseUrl: string | undefined,
217
+ authCredentialType: "api_key" | "oauth" | undefined,
218
+ ): string {
219
+ return resolveOpenAIProviderBaseUrl(baseUrl, authCredentialType);
220
+ }
221
+
222
+ function appendUrlPath(baseUrl: string | undefined, path: string): string | undefined {
223
+ if (!baseUrl) return undefined;
224
+ const normalizedPath = path.replace(/^\/+/g, "");
225
+ try {
226
+ const parsed = new URL(baseUrl);
227
+ parsed.pathname = `${parsed.pathname.replace(/\/+$/g, "")}/${normalizedPath}`;
228
+ return parsed.toString();
229
+ } catch {
230
+ return `${baseUrl.replace(/\/+$/g, "")}/${normalizedPath}`;
231
+ }
232
+ }
233
+
234
+ type OpenAIResponsesQuery = string;
235
+
236
+ function splitBaseUrlQuery(baseUrl: string | undefined): {
237
+ baseUrl: string | undefined;
238
+ query?: OpenAIResponsesQuery;
239
+ } {
240
+ if (!baseUrl) return { baseUrl };
241
+ try {
242
+ const parsed = new URL(baseUrl);
243
+ if (!parsed.search) return { baseUrl };
244
+ const queryStart = baseUrl.indexOf("?");
245
+ const fragmentStart = baseUrl.indexOf("#", queryStart);
246
+ const query = baseUrl.slice(queryStart + 1, fragmentStart === -1 ? undefined : fragmentStart);
247
+ if (!query) return { baseUrl };
248
+ parsed.search = "";
249
+ return {
250
+ baseUrl: parsed.toString(),
251
+ query,
252
+ };
253
+ } catch {
254
+ return { baseUrl };
255
+ }
256
+ }
257
+
258
+ function appendRawQuery(url: string, query: OpenAIResponsesQuery | undefined): string {
259
+ if (!query) return url;
260
+ const fragmentStart = url.indexOf("#");
261
+ const beforeFragment = fragmentStart === -1 ? url : url.slice(0, fragmentStart);
262
+ const fragment = fragmentStart === -1 ? "" : url.slice(fragmentStart);
263
+ return `${beforeFragment}${beforeFragment.includes("?") ? "&" : "?"}${query}${fragment}`;
264
+ }
265
+
266
+ function buildRequestUrl(baseUrl: string | undefined, path: string, query?: OpenAIResponsesQuery): string | undefined {
267
+ const url = appendUrlPath(baseUrl, path);
268
+ return url ? appendRawQuery(url, query) : undefined;
269
+ }
270
+
271
+ function appendQueryToRequest(input: string | URL | Request, query?: OpenAIResponsesQuery): string | URL | Request {
272
+ if (!query) return input;
273
+ const url = appendRawQuery(input instanceof Request ? input.url : String(input), query);
274
+ if (input instanceof Request) return new Request(url, input as unknown as RequestInit);
275
+ return url;
276
+ }
277
+
278
+ interface OpenAIResponsesProviderSessionState extends ProviderSessionState {
279
+ nativeHistoryReplayWarmed: boolean;
280
+ }
281
+
282
+ function createOpenAIResponsesProviderSessionState(): OpenAIResponsesProviderSessionState {
283
+ const state: OpenAIResponsesProviderSessionState = {
284
+ nativeHistoryReplayWarmed: false,
285
+ close: () => {
286
+ state.nativeHistoryReplayWarmed = false;
287
+ },
288
+ };
289
+ return state;
290
+ }
291
+
292
+ function getOpenAIResponsesProviderSessionStateKey(model: Model<"openai-responses">): string {
293
+ return `${OPENAI_RESPONSES_PROVIDER_SESSION_STATE_PREFIX}${model.provider}`;
294
+ }
295
+
296
+ function getOpenAIResponsesProviderSessionState(
297
+ model: Model<"openai-responses">,
298
+ providerSessionState: Map<string, ProviderSessionState> | undefined,
299
+ ): OpenAIResponsesProviderSessionState | undefined {
300
+ if (!providerSessionState) return undefined;
301
+ const key = getOpenAIResponsesProviderSessionStateKey(model);
302
+ const existing = providerSessionState.get(key) as OpenAIResponsesProviderSessionState | undefined;
303
+ if (existing) return existing;
304
+ const created = createOpenAIResponsesProviderSessionState();
305
+ providerSessionState.set(key, created);
306
+ return created;
307
+ }
308
+
309
+ function canReplayOpenAIResponsesNativeHistory(
310
+ providerSessionState: OpenAIResponsesProviderSessionState | undefined,
311
+ ): boolean {
312
+ return providerSessionState?.nativeHistoryReplayWarmed ?? true;
313
+ }
314
+
315
+ type OpenAIResponsesSamplingParams = ResponseCreateParamsStreaming & {
316
+ top_p?: number;
317
+ top_k?: number;
318
+ min_p?: number;
319
+ presence_penalty?: number;
320
+ repetition_penalty?: number;
321
+ stream_options?: { include_obfuscation?: boolean };
322
+ };
323
+
324
+ export function isOpenCodeGoEmptyCompletedResponse(
325
+ model: Model<"openai-responses">,
326
+ output: AssistantMessage,
327
+ nativeOutputItemCount: number,
328
+ ): boolean {
329
+ return (
330
+ model.provider === "opencode-go" &&
331
+ typeof output.responseId === "string" &&
332
+ output.responseId.length > 0 &&
333
+ output.stopReason === "stop" &&
334
+ output.content.length === 0 &&
335
+ nativeOutputItemCount === 0 &&
336
+ output.usage.input === 0 &&
337
+ output.usage.output === 0 &&
338
+ output.usage.cacheRead === 0 &&
339
+ output.usage.cacheWrite === 0 &&
340
+ output.usage.totalTokens === 0
341
+ );
342
+ }
343
+
344
+ /**
345
+ * Generate function for OpenAI Responses API
346
+ */
347
+ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
348
+ model: Model<"openai-responses">,
349
+ context: Context,
350
+ options?: OpenAIResponsesOptions,
351
+ ): AssistantMessageEventStream => {
352
+ const stream = new AssistantMessageEventStream();
353
+
354
+ // Start async processing
355
+ (async () => {
356
+ const startTime = Date.now();
357
+ let firstTokenTime: number | undefined;
358
+ let streamConnected = false;
359
+
360
+ const output: AssistantMessage = createInitialResponsesAssistantMessage(
361
+ "openai-responses",
362
+ model.provider,
363
+ model.id,
364
+ );
365
+ let rawRequestDump: RawHttpRequestDump | undefined;
366
+ const abortTracker = createAbortSourceTracker(options?.signal);
367
+ const { requestAbortController, requestSignal } = abortTracker;
368
+
369
+ try {
370
+ // Keep request headers and prompt-cache routing on the same session-derived value.
371
+ const cacheSessionId = getOpenAIResponsesCacheSessionId(options);
372
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention ?? model.cacheRetention);
373
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
374
+ const { client, copilotPremiumRequests, baseUrl, requestBaseUrl, requestQuery } = createClient(
375
+ model,
376
+ context,
377
+ apiKey,
378
+ options?.headers,
379
+ options?.initiatorOverride,
380
+ cacheSessionId,
381
+ cacheRetention,
382
+ options?.onSseEvent,
383
+ options?.fetch,
384
+ options?.authCredentialType,
385
+ options?.requestMaxRetries,
386
+ options?.maxRetryDelayMs,
387
+ options?.attemptScope,
388
+ options?.streamFirstEventTimeoutMs,
389
+ );
390
+ const premiumRequestsTotal = copilotPremiumRequests;
391
+ const providerSessionState = getOpenAIResponsesProviderSessionState(model, options?.providerSessionState);
392
+ const { params } = buildParams(model, context, options, providerSessionState, cacheRetention, baseUrl);
393
+ const idleTimeoutMs = options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs(model.provider, model.id);
394
+ options?.onPayload?.(params, undefined, options?.attemptScope);
395
+ rawRequestDump = {
396
+ provider: model.provider,
397
+ api: output.api,
398
+ model: model.id,
399
+ method: "POST",
400
+ url: buildRequestUrl(requestBaseUrl, "responses", requestQuery),
401
+ body: params,
402
+ };
403
+ const openaiStream = await callWithCopilotModelRetry(
404
+ async () => {
405
+ options?.onStreamCreated?.();
406
+ const { data, response, request_id } = await client.responses
407
+ .create(params, { signal: requestSignal })
408
+ .withResponse();
409
+ await notifyProviderResponse(options, response, model, request_id);
410
+ return data;
411
+ },
412
+ {
413
+ provider: model.provider,
414
+ signal: requestSignal,
415
+ fallbackManaged: options?.fallbackManaged,
416
+ requestMaxRetries: options?.requestMaxRetries,
417
+ disableProviderRetries: options?.disableProviderRetries,
418
+ },
419
+ ).catch(async error => {
420
+ if (
421
+ options?.fallbackManaged ||
422
+ options?.disableProviderRetries ||
423
+ !isForcedToolChoiceUnsupportedError(error, isForcedOpenAIResponsesToolChoice(params.tool_choice))
424
+ ) {
425
+ throw error;
426
+ }
427
+ const reason = await finalizeErrorMessage(error, rawRequestDump);
428
+ markToolChoiceIncapability(model, "auto", reason);
429
+ const resolvedToolChoice = resolveToolChoice(model, options?.toolChoice);
430
+ stream.push({
431
+ type: "toolChoiceIncapability",
432
+ api: model.api,
433
+ provider: model.provider,
434
+ model: model.id,
435
+ requestedLevel: resolvedToolChoice.requestedLevel,
436
+ resolvedLevel: "auto",
437
+ reason,
438
+ registryKey: resolvedToolChoice.registryKey,
439
+ });
440
+ delete params.tool_choice;
441
+ if (rawRequestDump) rawRequestDump.body = params;
442
+ options?.onStreamCreated?.();
443
+ const { data, response, request_id } = await client.responses
444
+ .create(params, { signal: requestSignal })
445
+ .withResponse();
446
+ await notifyProviderResponse(options, response, model, request_id);
447
+ return data;
448
+ });
449
+ streamConnected = true;
450
+ const firstEventFallbackMs = getProviderFirstEventTimeoutFallbackMs(model.provider);
451
+ const firstEventTimeoutMs =
452
+ options?.streamFirstEventTimeoutMs ?? getStreamFirstEventTimeoutMs(idleTimeoutMs, firstEventFallbackMs);
453
+ if (premiumRequestsTotal !== undefined) output.usage.premiumRequests = premiumRequestsTotal;
454
+ stream.push({ type: "start", partial: output });
455
+
456
+ const nativeOutputItems: Array<Record<string, unknown>> = [];
457
+ await processResponsesStream(
458
+ iterateWithIdleTimeout(openaiStream, {
459
+ idleTimeoutMs,
460
+ firstItemTimeoutMs: firstEventTimeoutMs,
461
+ firstItemErrorMessage: OPENAI_RESPONSES_FIRST_EVENT_TIMEOUT_MESSAGE,
462
+ errorMessage: "OpenAI responses stream stalled while waiting for the next event",
463
+ onIdle: () => requestAbortController.abort(),
464
+ onFirstItemTimeout: () => requestAbortController.abort(),
465
+ abortSignal: options?.signal,
466
+ isProgressItem: isOpenAIResponsesProgressEvent,
467
+ }),
468
+ output,
469
+ stream,
470
+ model,
471
+ {
472
+ onFirstToken: () => {
473
+ if (!firstTokenTime) firstTokenTime = Date.now();
474
+ },
475
+ onOutputItemDone: item => {
476
+ nativeOutputItems.push(structuredCloneJSON<unknown>(item) as unknown as Record<string, unknown>);
477
+ },
478
+ },
479
+ );
480
+ if (premiumRequestsTotal !== undefined) output.usage.premiumRequests = premiumRequestsTotal;
481
+
482
+ const firstEventTimeoutError = abortTracker.getLocalAbortReason();
483
+ if (firstEventTimeoutError) {
484
+ throw firstEventTimeoutError;
485
+ }
486
+ if (abortTracker.wasCallerAbort()) {
487
+ throw new Error("Request was aborted");
488
+ }
489
+
490
+ if (output.stopReason === "aborted" || output.stopReason === "error") {
491
+ throw new Error(output.errorMessage ?? "An unknown error occurred");
492
+ }
493
+
494
+ output.providerPayload = createOpenAIResponsesHistoryPayload(model.provider, nativeOutputItems);
495
+ if (isOpenCodeGoEmptyCompletedResponse(model, output, nativeOutputItems.length)) {
496
+ output.stopReason = "error";
497
+ output.errorMessage = "Provider returned an empty response with zero token usage";
498
+ output.transportFailure = {
499
+ kind: "transport",
500
+ providerCode: EMPTY_RESPONSE_PROVIDER_CODE,
501
+ };
502
+ }
503
+ if (providerSessionState) providerSessionState.nativeHistoryReplayWarmed = true;
504
+
505
+ output.duration = Date.now() - startTime;
506
+ if (firstTokenTime) output.ttft = firstTokenTime - startTime;
507
+ if (output.stopReason === "error") {
508
+ stream.push({ type: "error", reason: "error", error: output });
509
+ } else {
510
+ stream.push({ type: "done", reason: output.stopReason, message: output });
511
+ }
512
+ stream.end();
513
+ } catch (error) {
514
+ for (const block of output.content) delete (block as { index?: number }).index;
515
+ const localAbortReason = abortTracker.getLocalAbortReason();
516
+ const normalizedError =
517
+ !streamConnected && model.provider === "alibaba-token-plan" && error instanceof APIConnectionTimeoutError
518
+ ? new FirstEventTimeoutError(OPENAI_RESPONSES_FIRST_EVENT_TIMEOUT_MESSAGE)
519
+ : error;
520
+ output.stopReason = abortTracker.wasCallerAbort() ? "aborted" : "error";
521
+ output.errorStatus = extractHttpStatusFromError(localAbortReason ?? normalizedError);
522
+ output.transportFailure = transportFailureFacts(localAbortReason ?? normalizedError);
523
+ output.errorMessage =
524
+ localAbortReason?.message ?? (await finalizeErrorMessage(normalizedError, rawRequestDump));
525
+ output.errorMessage = rewriteCopilotError(output.errorMessage, normalizedError, model.provider);
526
+ // Explicitly mark the poisoned-history rejection so the shared
527
+ // `invalid_prompt` contract is present even when the SDK error surfaces
528
+ // only a message (no structured code). This keeps the responses
529
+ // transport's classification uniform with the codex transport's
530
+ // non-retryable event set and lets the session-level circuit breaker
531
+ // key on one durable marker instead of per-transport string matching.
532
+ if (
533
+ output.stopReason === "error" &&
534
+ !output.transportFailure?.providerCode &&
535
+ (isInvalidPromptError(error) || isInvalidPromptError(output.errorMessage))
536
+ ) {
537
+ output.transportFailure = {
538
+ ...(output.transportFailure ?? { kind: "transport" }),
539
+ providerCode: "invalid_prompt",
540
+ };
541
+ }
542
+ output.duration = Date.now() - startTime;
543
+ if (firstTokenTime) output.ttft = firstTokenTime - startTime;
544
+ stream.push({ type: "error", reason: output.stopReason, error: output });
545
+ stream.end();
546
+ }
547
+ })();
548
+
549
+ return stream;
550
+ };
551
+
552
+ function createClient(
553
+ model: Model<"openai-responses">,
554
+ context: Context,
555
+ apiKey?: string,
556
+ extraHeaders?: Record<string, string>,
557
+ initiatorOverride?: MessageAttribution,
558
+ sessionId?: string,
559
+ cacheRetention?: CacheRetention,
560
+ onSseEvent?: OpenAIResponsesOptions["onSseEvent"],
561
+ fetchOverride?: FetchImpl,
562
+ authCredentialType?: OpenAIResponsesOptions["authCredentialType"],
563
+ requestMaxRetries?: number,
564
+ maxRetryDelayMs?: number,
565
+ attemptScope?: import("../types.js").AttemptScopeRef,
566
+ streamFirstEventTimeoutOverride?: number,
567
+ ): {
568
+ client: OpenAI;
569
+ copilotPremiumRequests: number | undefined;
570
+ baseUrl: string | undefined;
571
+ requestBaseUrl: string | undefined;
572
+ requestQuery: OpenAIResponsesQuery | undefined;
573
+ } {
574
+ if (!apiKey) {
575
+ apiKey = $credentialEnv("OPENAI_API_KEY");
576
+ if (!apiKey) {
577
+ throw new Error(
578
+ "OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass it as an argument.",
579
+ );
580
+ }
581
+ }
582
+ const rawApiKey = apiKey;
583
+
584
+ const baseHeaders =
585
+ model.provider === "alibaba-token-plan"
586
+ ? // Emit Qwen Code's canonical DashScope request fingerprint (User-Agent /
587
+ // X-DashScope-CacheControl / X-DashScope-UserAgent / X-DashScope-AuthType)
588
+ // so DashScope treats the caller identically to upstream QwenLM/qwen-code.
589
+ // Canonical identity is the base; caller headers win per key (upstream
590
+ // `{...default, ...customHeaders}`). #3557.
591
+ mergeDashScopeTokenPlanHeaders({ ...(model.headers ?? {}), ...(extraHeaders ?? {}) })
592
+ : { ...(model.headers ?? {}), ...(extraHeaders ?? {}) };
593
+ let copilotPremiumRequests: number | undefined;
594
+
595
+ let baseUrl =
596
+ model.provider === "openai" ? resolveOpenAIProviderBaseUrl(model.baseUrl, authCredentialType) : model.baseUrl;
597
+ if (model.provider === "openai" && !baseUrl) {
598
+ baseUrl = OPENAI_DEFAULT_BASE_URL;
599
+ }
600
+ let headers = baseHeaders;
601
+ if (model.provider === "github-copilot") {
602
+ apiKey = parseGitHubCopilotApiKey(rawApiKey).accessToken;
603
+ const hasImages = hasCopilotVisionInput(context.messages);
604
+ const copilot = buildCopilotDynamicHeaders({
605
+ messages: context.messages,
606
+ hasImages,
607
+ premiumMultiplier: model.premiumMultiplier,
608
+ headers,
609
+ initiatorOverride,
610
+ });
611
+ Object.assign(headers, copilot.headers);
612
+ copilotPremiumRequests = copilot.premiumRequests;
613
+ baseUrl = resolveGitHubCopilotBaseUrl(model.baseUrl, rawApiKey) ?? model.baseUrl;
614
+ }
615
+ if (sessionId && shouldSendOpenAIResponsesSessionHeaders(model, baseUrl, cacheRetention ?? "short")) {
616
+ headers.session_id ??= sessionId;
617
+ headers["x-client-request-id"] ??= sessionId;
618
+ }
619
+ headers = applyOpenAIRequestTransformHeaders(headers, model.requestTransform, `Vibrato/${packageJson.version}`);
620
+ const { baseUrl: clientBaseUrl, query: endpointQuery } = splitBaseUrlQuery(baseUrl);
621
+ const baseFetch = fetchOverride ?? fetch;
622
+ const queryFetch = Object.assign(
623
+ async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
624
+ return baseFetch(appendQueryToRequest(input, endpointQuery), init);
625
+ },
626
+ baseFetch.preconnect ? { preconnect: baseFetch.preconnect } : {},
627
+ );
628
+ const boundedFetch = wrapOpenAIFetchForBoundedRateLimits(queryFetch, maxRetryDelayMs);
629
+ const transformedFetch = wrapFetchForOpenAIRequestTransform(
630
+ boundedFetch,
631
+ model.requestTransform,
632
+ `Vibrato/${packageJson.version}`,
633
+ );
634
+ // Bound HTTP request timeout to the first-event window so a stalled-before-headers
635
+ // fetch cannot wait the SDK's 10-minute default before the transport watchdog arms.
636
+ const sdkTimeoutMs = resolveOpenAISdkRequestTimeoutMs(model.provider, streamFirstEventTimeoutOverride, model.id);
637
+ return {
638
+ client: new OpenAI({
639
+ apiKey,
640
+ baseURL: clientBaseUrl,
641
+ dangerouslyAllowBrowser: true,
642
+ maxRetries: resolveRetryBudget(requestMaxRetries, 5),
643
+ defaultHeaders: headers,
644
+ fetch: onSseEvent
645
+ ? wrapFetchForSseDebug(transformedFetch, event => onSseEvent(event, model, attemptScope))
646
+ : transformedFetch,
647
+ ...(sdkTimeoutMs !== undefined ? { timeout: sdkTimeoutMs } : {}),
648
+ }),
649
+ copilotPremiumRequests,
650
+ baseUrl,
651
+ requestBaseUrl: clientBaseUrl,
652
+ requestQuery: endpointQuery,
653
+ };
654
+ }
655
+
656
+ function getOpenAIResponsesCacheSessionId(
657
+ options: Pick<OpenAIResponsesOptions, "sessionId"> | undefined,
658
+ ): string | undefined {
659
+ return normalizeOpenAIResponsesPromptCacheKey(options?.sessionId);
660
+ }
661
+
662
+ function buildParams(
663
+ model: Model<"openai-responses">,
664
+ context: Context,
665
+ options: OpenAIResponsesOptions | undefined,
666
+ providerSessionState: OpenAIResponsesProviderSessionState | undefined,
667
+ cacheRetention: CacheRetention,
668
+ resolvedBaseUrl?: string,
669
+ ): { conversationMessages: ResponseInput; params: OpenAIResponsesSamplingParams } {
670
+ const strictResponsesPairing =
671
+ options?.strictResponsesPairing ??
672
+ (isAzureOpenAIBaseUrl(model.baseUrl ?? "") || model.provider === "github-copilot");
673
+ const conversationMessages = convertConversationMessages(
674
+ model,
675
+ context,
676
+ strictResponsesPairing,
677
+ providerSessionState,
678
+ );
679
+ const messages: ResponseInput = neutralizeResponsesInputControlTokens(conversationMessages);
680
+
681
+ // Neutralize leaked Harmony control tokens in the system prompt too: the
682
+ // `instructions` field and developer-role messages bypass the `input`
683
+ // request-boundary sanitizer above, and a poisoned system prompt (e.g.
684
+ // injected project context quoting `<|channel|>` markers) rejects EVERY
685
+ // turn with `Request blocked (code=invalid_prompt)` — unrepairable by the
686
+ // history circuit breaker.
687
+ const systemPrompts = normalizeSystemPrompts(context.systemPrompt).map(neutralizeReservedControlTokens);
688
+ if (isComposerHarnessModel(model.id)) {
689
+ systemPrompts.unshift(COMPOSER_EDIT_DISCIPLINE_PROMPT);
690
+ }
691
+ let systemInstructions: string | undefined;
692
+ if (systemPrompts.length > 0) {
693
+ const needsDeveloperRole =
694
+ model.reasoning &&
695
+ ((model.provider === "openai" && !model.baseUrl) || supportsDeveloperRole(resolvedBaseUrl || model));
696
+ if (needsDeveloperRole) {
697
+ // Reasoning models on known OpenAI-compatible endpoints require the
698
+ // `developer` role. Send all system prompts inline in `input`.
699
+ messages.unshift(
700
+ ...systemPrompts.map(systemPrompt => ({ role: "developer" as const, content: systemPrompt })),
701
+ );
702
+ } else {
703
+ // All other endpoints (including third-party /v1/responses proxies) use
704
+ // the canonical top-level `instructions` field so that proxies that
705
+ // reject `input[{role:"system"}]` work out of the box.
706
+ systemInstructions = systemPrompts.join("\n\n");
707
+ }
708
+ }
709
+
710
+ const promptCacheKey = getOpenAIResponsesCacheSessionId(options);
711
+ const params: OpenAIResponsesSamplingParams = {
712
+ model: model.wireModelId ?? model.id,
713
+ input: messages,
714
+ instructions: systemInstructions,
715
+ stream: true,
716
+ prompt_cache_key: promptCacheKey,
717
+ prompt_cache_retention: getPromptCacheRetention(resolvedBaseUrl || model.baseUrl, cacheRetention),
718
+ store: false,
719
+ stream_options: model.provider === "openai" ? { include_obfuscation: false } : undefined,
720
+ };
721
+
722
+ applyCommonResponsesSamplingParams(params, options, model.provider, model.compat?.supportsServiceTier === true);
723
+ // TODO: openai responses has no top-level `stop`/`stop_sequences`; surface via reasoning.stop?
724
+ // `StreamOptions.stopSequences` is intentionally dropped for this provider.
725
+ // TODO: openai responses has no top-level `frequency_penalty` field as of the current SDK;
726
+ // `StreamOptions.frequencyPenalty` is intentionally dropped for this provider.
727
+
728
+ if (context.tools) {
729
+ params.tools = convertTools(context.tools, supportsStrictMode(model), model);
730
+ if (options?.toolChoice) {
731
+ const toolChoice = resolveToolChoice(model, options.toolChoice);
732
+ if (toolChoice.degraded && toolChoice.supportSource === "runtime") {
733
+ logger.debug("openai-responses: degraded tool_choice after runtime capability discovery", {
734
+ model: model.id,
735
+ requestedLevel: toolChoice.requestedLevel,
736
+ resolvedLevel: toolChoice.resolvedLevel,
737
+ reason: toolChoice.reason,
738
+ });
739
+ }
740
+ params.tool_choice = mapOpenAIResponsesToolChoiceForTools(toolChoice.resolvedChoice, context.tools, model);
741
+ }
742
+ // The apply_patch spec §1 marks only `apply_patch` itself as
743
+ // `supports_parallel_tool_calls = false`. OpenAI's Responses API
744
+ // exposes `parallel_tool_calls` as a request-scoped flag, not a
745
+ // per-tool one, so when a custom grammar tool is in the list we
746
+ // disable parallelism for the whole turn. Slightly coarser than
747
+ // the spec requires — but the platform API offers no finer knob.
748
+ if (params.tools.some(t => (t as { type?: string }).type === "custom")) {
749
+ params.parallel_tool_calls = false;
750
+ }
751
+ }
752
+
753
+ applyResponsesReasoningParams(params, model, options, messages, effort =>
754
+ mapReasoningEffort(effort as NonNullable<OpenAIResponsesOptions["reasoning"]>, model.compat?.reasoningEffortMap),
755
+ );
756
+ applyOpenAIRequestTransformBody(params, model.requestTransform);
757
+
758
+ return { conversationMessages, params };
759
+ }
760
+
761
+ function mapReasoningEffort(
762
+ effort: NonNullable<OpenAIResponsesOptions["reasoning"]>,
763
+ reasoningEffortMap: OpenAICompat["reasoningEffortMap"] | undefined,
764
+ ): string {
765
+ return reasoningEffortMap?.[effort] ?? effort;
766
+ }
767
+
768
+ function isAzureOpenAIBaseUrl(baseUrl: string): boolean {
769
+ return baseUrl.includes(".openai.azure.com") || baseUrl.includes("azure.com/openai");
770
+ }
771
+
772
+ function supportsStrictMode(model: Model<"openai-responses">): boolean {
773
+ if (model.provider === "openai" || model.provider === "azure" || model.provider === "github-copilot") return true;
774
+
775
+ const baseUrl = model.baseUrl;
776
+ const lowerBaseUrl = baseUrl.toLowerCase();
777
+ return (
778
+ isDefaultOpenAIBaseUrl(baseUrl) ||
779
+ lowerBaseUrl.includes(".openai.azure.com") ||
780
+ lowerBaseUrl.includes("models.inference.ai.azure.com")
781
+ );
782
+ }
783
+
784
+ export function supportsDeveloperRole(modelOrBaseUrl: Pick<Model, "provider" | "baseUrl"> | string): boolean {
785
+ const baseUrl = typeof modelOrBaseUrl === "string" ? modelOrBaseUrl : (modelOrBaseUrl.baseUrl ?? "");
786
+ if (typeof modelOrBaseUrl !== "string" && modelOrBaseUrl.provider === "openai" && !baseUrl) {
787
+ return true;
788
+ }
789
+ const lowerBaseUrl = baseUrl.toLowerCase();
790
+ return (
791
+ isOpenAIHostBaseUrl(baseUrl) ||
792
+ lowerBaseUrl.includes(".openai.azure.com") ||
793
+ lowerBaseUrl.includes("azure.com/openai") ||
794
+ lowerBaseUrl.includes("models.inference.ai.azure.com") ||
795
+ lowerBaseUrl.includes("githubcopilot.com") ||
796
+ lowerBaseUrl.includes("copilot-api.")
797
+ );
798
+ }
799
+
800
+ function convertConversationMessages(
801
+ model: Model<"openai-responses">,
802
+ context: Context,
803
+ strictResponsesPairing: boolean,
804
+ providerSessionState: OpenAIResponsesProviderSessionState | undefined,
805
+ ): ResponseInput {
806
+ const messages: ResponseInput = [];
807
+ let knownCallIds = new Set<string>();
808
+ const customCallIds = new Set<string>();
809
+ const shouldReplayNativeHistory = canReplayOpenAIResponsesNativeHistory(providerSessionState);
810
+ const transformedMessages = transformMessages(context.messages, model, normalizeResponsesToolCallIdForTransform);
811
+
812
+ let msgIndex = 0;
813
+ // Consecutive tool results are batched into one append call so every output
814
+ // of the turn stays contiguous before the collected image user message;
815
+ // per-result image user messages interleave with sibling outputs and break
816
+ // tool_use→tool_result adjacency through Anthropic-translating proxies (#4807).
817
+ let pendingToolResults: ToolResultMessage[] = [];
818
+ const flushPendingToolResults = (): void => {
819
+ if (pendingToolResults.length === 0) return;
820
+ appendResponsesToolResultMessages(
821
+ messages,
822
+ pendingToolResults,
823
+ model,
824
+ strictResponsesPairing,
825
+ knownCallIds,
826
+ customCallIds,
827
+ );
828
+ pendingToolResults = [];
829
+ };
830
+ for (const msg of transformedMessages) {
831
+ if (msg.role === "toolResult") {
832
+ pendingToolResults.push(msg);
833
+ msgIndex++;
834
+ continue;
835
+ }
836
+ flushPendingToolResults();
837
+ if (msg.role === "user" || msg.role === "developer") {
838
+ const providerPayload = (msg as { providerPayload?: AssistantMessage["providerPayload"] }).providerPayload;
839
+ const historyItems = getOpenAIResponsesHistoryItems(providerPayload, model.provider);
840
+ const shouldReplayPayloadItems =
841
+ shouldReplayNativeHistory ||
842
+ (historyItems?.some(item => {
843
+ if (!item || typeof item !== "object") return false;
844
+ const candidate = item as { type?: unknown };
845
+ return candidate.type === "compaction" || candidate.type === "compaction_summary";
846
+ }) ??
847
+ false);
848
+ if (historyItems && shouldReplayPayloadItems) {
849
+ messages.push(...sanitizeOpenAIResponsesHistoryItemsForReplay(historyItems));
850
+ knownCallIds = collectKnownCallIds(messages);
851
+ for (const id of collectCustomCallIds(messages)) customCallIds.add(id);
852
+ msgIndex++;
853
+ continue;
854
+ }
855
+ const content = convertResponsesInputContent(msg.content, model.input.includes("image"));
856
+ if (!content) continue;
857
+ messages.push({ role: "user", content });
858
+ } else if (msg.role === "assistant") {
859
+ const assistantMsg = msg as AssistantMessage;
860
+ const providerPayload = shouldReplayNativeHistory
861
+ ? getOpenAIResponsesHistoryPayload(assistantMsg.providerPayload, model.provider, assistantMsg.provider)
862
+ : undefined;
863
+ const historyItems = providerPayload?.items;
864
+ if (historyItems) {
865
+ const sanitizedHistoryItems = sanitizeOpenAIResponsesHistoryItemsForReplay(historyItems);
866
+ if (providerPayload?.dt) {
867
+ messages.push(...sanitizedHistoryItems);
868
+ } else {
869
+ messages.splice(0, messages.length, ...sanitizedHistoryItems);
870
+ }
871
+ knownCallIds = collectKnownCallIds(messages);
872
+ for (const id of collectCustomCallIds(messages)) customCallIds.add(id);
873
+ msgIndex++;
874
+ continue;
875
+ }
876
+
877
+ const outputItems = convertResponsesAssistantMessage(
878
+ assistantMsg,
879
+ model,
880
+ msgIndex,
881
+ knownCallIds,
882
+ shouldReplayNativeHistory,
883
+ customCallIds,
884
+ );
885
+ if (outputItems.length === 0) continue;
886
+ messages.push(...outputItems);
887
+ }
888
+ msgIndex++;
889
+ }
890
+ flushPendingToolResults();
891
+
892
+ return repairOrphanResponsesToolOutputs(messages);
893
+ }
894
+
895
+ /**
896
+ * Whether this model should get the OpenAI custom-tool grammar variant
897
+ * for `apply_patch`. The generated model catalog sets
898
+ * `model.applyPatchToolType` for first-party GPT-5 Responses models; this
899
+ * runtime path only consumes that metadata.
900
+ * @internal Exported for tests.
901
+ */
902
+ export function supportsFreeformApplyPatch(model: Model<"openai-responses">): boolean {
903
+ return model.applyPatchToolType === "freeform";
904
+ }
905
+
906
+ /** @internal Exported for tests. */
907
+ export function mapOpenAIResponsesToolChoiceForTools(
908
+ choice: ToolChoice | undefined,
909
+ tools: Tool[],
910
+ model: Model<"openai-responses">,
911
+ ): OpenAIResponsesToolChoice {
912
+ const mapped = mapToOpenAIResponsesToolChoice(choice);
913
+ if (!mapped || typeof mapped === "string" || mapped.type !== "function" || !supportsFreeformApplyPatch(model)) {
914
+ return mapped;
915
+ }
916
+
917
+ const customTool = tools.find(
918
+ tool => tool.customFormat && (tool.name === mapped.name || tool.customWireName === mapped.name),
919
+ );
920
+ return customTool ? { type: "custom", name: customTool.customWireName ?? customTool.name } : mapped;
921
+ }
922
+
923
+ function isForcedOpenAIResponsesToolChoice(choice: unknown): boolean {
924
+ return !!choice && choice !== "none" && choice !== "auto";
925
+ }
926
+
927
+ /**
928
+ * Tool names an OpenAI-compatible endpoint reserves for its own built-ins and
929
+ * refuses as custom function declarations.
930
+ *
931
+ * OpenCode Zen/Go reject `web_search` with
932
+ * `invalid tools in request: custom function name "web_search" is reserved`.
933
+ * The rejection is request-scoped: one colliding declaration fails the WHOLE
934
+ * tools array before any token streams, so every agent carrying that tool is
935
+ * permanently broken on the provider rather than losing a single capability.
936
+ *
937
+ * The collision is dropped rather than renamed. A renamed function tool would
938
+ * come back as a `function_call` under the wire alias, and that path does not
939
+ * populate `Tool.customWireName`, so the agent-loop dispatcher could not route
940
+ * it — trading a loud 400 for a silent unresolvable call. Dropping leaves the
941
+ * agent in the same state as any provider that simply has no web search.
942
+ */
943
+ const PROVIDER_RESERVED_TOOL_NAMES: Record<string, readonly string[]> = {
944
+ "opencode-go": ["web_search"],
945
+ "opencode-zen": ["web_search"],
946
+ };
947
+
948
+ /** @internal Exported for tests. */
949
+ export function resolveReservedToolNames(model: Model<"openai-responses">): readonly string[] {
950
+ return model.compat?.reservedToolNames ?? PROVIDER_RESERVED_TOOL_NAMES[model.provider] ?? [];
951
+ }
952
+ /** @internal Exported for tests. */
953
+ export function convertTools(tools: Tool[], strictMode: boolean, model: Model<"openai-responses">): OpenAITool[] {
954
+ const allowFreeform = supportsFreeformApplyPatch(model);
955
+ const reserved = resolveReservedToolNames(model);
956
+ const declarable = reserved.length === 0 ? tools : tools.filter(tool => !reserved.includes(tool.name));
957
+ const payloads = declarable.map(tool => {
958
+ if (allowFreeform && tool.customFormat) {
959
+ return {
960
+ type: "custom",
961
+ // Tool advertises its wire-level name (e.g. `apply_patch`) — the
962
+ // agent-loop dispatcher will match incoming calls by either the
963
+ // internal `name` or `customWireName`.
964
+ name: tool.customWireName ?? tool.name,
965
+ description: tool.description || "",
966
+ format: {
967
+ type: "grammar",
968
+ syntax: tool.customFormat.syntax,
969
+ definition: compactGrammarDefinition(tool.customFormat.syntax, tool.customFormat.definition),
970
+ },
971
+ } as unknown as OpenAITool;
972
+ }
973
+ const strict = !NO_STRICT && strictMode && tool.strict !== false;
974
+ const baseParameters = flattenToolRootCombinators(toolWireSchema(tool));
975
+ const responseParameters = sanitizeSchemaForOpenAIResponses(baseParameters);
976
+ const { schema: parameters, strict: effectiveStrict } = adaptSchemaForStrict(responseParameters, strict);
977
+ return {
978
+ type: "function",
979
+ name: tool.name,
980
+ description: tool.description || "",
981
+ parameters,
982
+ ...(effectiveStrict && { strict: true }),
983
+ } as OpenAITool;
984
+ });
985
+ // Tool definitions bypass the `input`/`instructions` sanitizers, so a
986
+ // leaked Harmony marker in an MCP/skill tool description or schema string
987
+ // rejects every gpt-5.x request (`Request blocked`).
988
+ return neutralizeResponsesInputControlTokens(payloads);
989
+ }