@linxiraos/pi-ai 1.0.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 (639) hide show
  1. package/CHANGELOG.md +5066 -0
  2. package/README.md +1195 -0
  3. package/dist/types/api-registry.d.ts +30 -0
  4. package/dist/types/auth/sqlite-credential-store.d.ts +117 -0
  5. package/dist/types/auth-broker/client.d.ts +93 -0
  6. package/dist/types/auth-broker/discover.d.ts +39 -0
  7. package/dist/types/auth-broker/index.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 +136 -0
  10. package/dist/types/auth-broker/server.d.ts +48 -0
  11. package/dist/types/auth-broker/snapshot-cache.d.ts +17 -0
  12. package/dist/types/auth-broker/types.d.ts +152 -0
  13. package/dist/types/auth-broker/wire-schema-resource.d.ts +53 -0
  14. package/dist/types/auth-broker/wire-schemas.d.ts +5 -0
  15. package/dist/types/auth-gateway/http.d.ts +56 -0
  16. package/dist/types/auth-gateway/index.d.ts +3 -0
  17. package/dist/types/auth-gateway/server.d.ts +36 -0
  18. package/dist/types/auth-gateway/types.d.ts +127 -0
  19. package/dist/types/auth-retry.d.ts +150 -0
  20. package/dist/types/auth-storage.d.ts +1258 -0
  21. package/dist/types/dialect/anthropic.d.ts +15 -0
  22. package/dist/types/dialect/catalog.d.ts +3 -0
  23. package/dist/types/dialect/coercion.d.ts +23 -0
  24. package/dist/types/dialect/deepseek.d.ts +14 -0
  25. package/dist/types/dialect/demotion.d.ts +30 -0
  26. package/dist/types/dialect/examples.d.ts +18 -0
  27. package/dist/types/dialect/factory.d.ts +3 -0
  28. package/dist/types/dialect/fenced-thinking.d.ts +53 -0
  29. package/dist/types/dialect/gemini.d.ts +17 -0
  30. package/dist/types/dialect/gemma.d.ts +15 -0
  31. package/dist/types/dialect/glm.d.ts +9 -0
  32. package/dist/types/dialect/harmony.d.ts +8 -0
  33. package/dist/types/dialect/hermes.d.ts +9 -0
  34. package/dist/types/dialect/history.d.ts +3 -0
  35. package/dist/types/dialect/index.d.ts +11 -0
  36. package/dist/types/dialect/inventory.d.ts +9 -0
  37. package/dist/types/dialect/kimi.d.ts +14 -0
  38. package/dist/types/dialect/minimax.d.ts +3 -0
  39. package/dist/types/dialect/owned-stream.d.ts +4 -0
  40. package/dist/types/dialect/qwen3.d.ts +9 -0
  41. package/dist/types/dialect/rendering.d.ts +54 -0
  42. package/dist/types/dialect/thinking.d.ts +6 -0
  43. package/dist/types/dialect/types.d.ts +68 -0
  44. package/dist/types/dialect/xml.d.ts +9 -0
  45. package/dist/types/error/abort.d.ts +14 -0
  46. package/dist/types/error/auth-classify.d.ts +20 -0
  47. package/dist/types/error/auth.d.ts +27 -0
  48. package/dist/types/error/aws.d.ts +27 -0
  49. package/dist/types/error/classes.d.ts +106 -0
  50. package/dist/types/error/finalize.d.ts +39 -0
  51. package/dist/types/error/flags.d.ts +94 -0
  52. package/dist/types/error/format.d.ts +20 -0
  53. package/dist/types/error/gateway.d.ts +20 -0
  54. package/dist/types/error/index.d.ts +14 -0
  55. package/dist/types/error/oauth.d.ts +43 -0
  56. package/dist/types/error/provider.d.ts +42 -0
  57. package/dist/types/error/rate-limit.d.ts +80 -0
  58. package/dist/types/error/retryable.d.ts +27 -0
  59. package/dist/types/error/validation.d.ts +32 -0
  60. package/dist/types/index.d.ts +51 -0
  61. package/dist/types/provider-details.d.ts +24 -0
  62. package/dist/types/providers/amazon-bedrock.d.ts +39 -0
  63. package/dist/types/providers/anthropic-client.d.ts +106 -0
  64. package/dist/types/providers/anthropic-messages-server-schema.d.ts +937 -0
  65. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  66. package/dist/types/providers/anthropic-wire.d.ts +345 -0
  67. package/dist/types/providers/anthropic.d.ts +261 -0
  68. package/dist/types/providers/aws-credentials.d.ts +48 -0
  69. package/dist/types/providers/aws-eventstream.d.ts +39 -0
  70. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  71. package/dist/types/providers/azure-openai-responses.d.ts +16 -0
  72. package/dist/types/providers/bedrock-mantle.d.ts +13 -0
  73. package/dist/types/providers/claude-code-fingerprint.d.ts +19 -0
  74. package/dist/types/providers/cowork-fetch.d.ts +3 -0
  75. package/dist/types/providers/cursor/exec-modern.d.ts +98 -0
  76. package/dist/types/providers/cursor-pi-args.d.ts +105 -0
  77. package/dist/types/providers/cursor.d.ts +221 -0
  78. package/dist/types/providers/devin.d.ts +12 -0
  79. package/dist/types/providers/error-message.d.ts +25 -0
  80. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  81. package/dist/types/providers/gitlab-duo-workflow.d.ts +254 -0
  82. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  83. package/dist/types/providers/google-auth.d.ts +24 -0
  84. package/dist/types/providers/google-gemini-cli.d.ts +120 -0
  85. package/dist/types/providers/google-shared.d.ts +203 -0
  86. package/dist/types/providers/google-types.d.ts +155 -0
  87. package/dist/types/providers/google-vertex.d.ts +7 -0
  88. package/dist/types/providers/google.d.ts +4 -0
  89. package/dist/types/providers/grammar.d.ts +1 -0
  90. package/dist/types/providers/kimi.d.ts +27 -0
  91. package/dist/types/providers/mock.d.ts +179 -0
  92. package/dist/types/providers/ollama.d.ts +8 -0
  93. package/dist/types/providers/openai-anthropic-shim.d.ts +35 -0
  94. package/dist/types/providers/openai-chat-server-schema.d.ts +1311 -0
  95. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  96. package/dist/types/providers/openai-chat-wire.d.ts +669 -0
  97. package/dist/types/providers/openai-codex/request-transformer.d.ts +109 -0
  98. package/dist/types/providers/openai-codex/response-handler.d.ts +26 -0
  99. package/dist/types/providers/openai-codex-responses.d.ts +238 -0
  100. package/dist/types/providers/openai-completions.d.ts +48 -0
  101. package/dist/types/providers/openai-reasoning-fallback.d.ts +25 -0
  102. package/dist/types/providers/openai-responses-server-schema.d.ts +1314 -0
  103. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  104. package/dist/types/providers/openai-responses-wire.d.ts +6099 -0
  105. package/dist/types/providers/openai-responses.d.ts +131 -0
  106. package/dist/types/providers/openai-shared.d.ts +624 -0
  107. package/dist/types/providers/pi-native-client.d.ts +13 -0
  108. package/dist/types/providers/pi-native-server.d.ts +69 -0
  109. package/dist/types/providers/register-builtins.d.ts +37 -0
  110. package/dist/types/providers/synthetic.d.ts +26 -0
  111. package/dist/types/providers/transform-messages.d.ts +32 -0
  112. package/dist/types/providers/vision-guard.d.ts +20 -0
  113. package/dist/types/registry/aiand.d.ts +7 -0
  114. package/dist/types/registry/aimlapi.d.ts +4 -0
  115. package/dist/types/registry/alibaba-coding-plan.d.ts +8 -0
  116. package/dist/types/registry/alibaba-token-plan.d.ts +18 -0
  117. package/dist/types/registry/amazon-bedrock.d.ts +5 -0
  118. package/dist/types/registry/anthropic.d.ts +10 -0
  119. package/dist/types/registry/api-key-login.d.ts +42 -0
  120. package/dist/types/registry/api-key-validation.d.ts +43 -0
  121. package/dist/types/registry/aws.d.ts +13 -0
  122. package/dist/types/registry/azure.d.ts +4 -0
  123. package/dist/types/registry/baseten.d.ts +7 -0
  124. package/dist/types/registry/bedrock-mantle.d.ts +22 -0
  125. package/dist/types/registry/cerebras.d.ts +7 -0
  126. package/dist/types/registry/cloudflare-ai-gateway.d.ts +13 -0
  127. package/dist/types/registry/coreweave.d.ts +7 -0
  128. package/dist/types/registry/cursor.d.ts +7 -0
  129. package/dist/types/registry/deepseek.d.ts +8 -0
  130. package/dist/types/registry/derived.d.ts +5 -0
  131. package/dist/types/registry/devin.d.ts +8 -0
  132. package/dist/types/registry/exa.d.ts +8 -0
  133. package/dist/types/registry/firepass.d.ts +16 -0
  134. package/dist/types/registry/fireworks.d.ts +7 -0
  135. package/dist/types/registry/github-copilot.d.ts +7 -0
  136. package/dist/types/registry/gitlab-duo-workflow.d.ts +10 -0
  137. package/dist/types/registry/gitlab-duo.d.ts +9 -0
  138. package/dist/types/registry/gmi-cloud.d.ts +7 -0
  139. package/dist/types/registry/google-antigravity.d.ts +9 -0
  140. package/dist/types/registry/google-gemini-cli.d.ts +9 -0
  141. package/dist/types/registry/google-vertex.d.ts +5 -0
  142. package/dist/types/registry/google.d.ts +4 -0
  143. package/dist/types/registry/groq.d.ts +4 -0
  144. package/dist/types/registry/huggingface.d.ts +7 -0
  145. package/dist/types/registry/index.d.ts +4 -0
  146. package/dist/types/registry/kagi.d.ts +14 -0
  147. package/dist/types/registry/kilo.d.ts +7 -0
  148. package/dist/types/registry/kimi-code.d.ts +7 -0
  149. package/dist/types/registry/litellm.d.ts +13 -0
  150. package/dist/types/registry/llama-cpp.d.ts +8 -0
  151. package/dist/types/registry/lm-studio.d.ts +8 -0
  152. package/dist/types/registry/meta.d.ts +7 -0
  153. package/dist/types/registry/minimax-code-cn.d.ts +6 -0
  154. package/dist/types/registry/minimax-code.d.ts +6 -0
  155. package/dist/types/registry/minimax.d.ts +4 -0
  156. package/dist/types/registry/mistral.d.ts +4 -0
  157. package/dist/types/registry/moonshot.d.ts +7 -0
  158. package/dist/types/registry/nanogpt.d.ts +7 -0
  159. package/dist/types/registry/novita.d.ts +6 -0
  160. package/dist/types/registry/nvidia.d.ts +7 -0
  161. package/dist/types/registry/oauth/anthropic-constants.d.ts +12 -0
  162. package/dist/types/registry/oauth/anthropic.d.ts +24 -0
  163. package/dist/types/registry/oauth/callback-server.d.ts +74 -0
  164. package/dist/types/registry/oauth/cursor.d.ts +16 -0
  165. package/dist/types/registry/oauth/device-code.d.ts +25 -0
  166. package/dist/types/registry/oauth/devin.d.ts +5 -0
  167. package/dist/types/registry/oauth/github-copilot.d.ts +30 -0
  168. package/dist/types/registry/oauth/gitlab-duo-workflow.d.ts +6 -0
  169. package/dist/types/registry/oauth/gitlab-duo.d.ts +3 -0
  170. package/dist/types/registry/oauth/google-antigravity.d.ts +11 -0
  171. package/dist/types/registry/oauth/google-gemini-cli.d.ts +22 -0
  172. package/dist/types/registry/oauth/google-oauth-shared.d.ts +56 -0
  173. package/dist/types/registry/oauth/index.d.ts +45 -0
  174. package/dist/types/registry/oauth/kimi.d.ts +21 -0
  175. package/dist/types/registry/oauth/minimax-code.d.ts +27 -0
  176. package/dist/types/registry/oauth/openai-codex.d.ts +33 -0
  177. package/dist/types/registry/oauth/opencode.d.ts +18 -0
  178. package/dist/types/registry/oauth/perplexity.d.ts +9 -0
  179. package/dist/types/registry/oauth/pkce.d.ts +8 -0
  180. package/dist/types/registry/oauth/types.d.ts +87 -0
  181. package/dist/types/registry/oauth/wafer.d.ts +1 -0
  182. package/dist/types/registry/oauth/xai-oauth.d.ts +46 -0
  183. package/dist/types/registry/oauth/xiaomi.d.ts +25 -0
  184. package/dist/types/registry/oauth/zai.d.ts +25 -0
  185. package/dist/types/registry/ollama-cloud.d.ts +7 -0
  186. package/dist/types/registry/ollama.d.ts +12 -0
  187. package/dist/types/registry/openai-codex-device.d.ts +8 -0
  188. package/dist/types/registry/openai-codex.d.ts +9 -0
  189. package/dist/types/registry/openai.d.ts +4 -0
  190. package/dist/types/registry/opencode-go.d.ts +6 -0
  191. package/dist/types/registry/opencode-zen.d.ts +6 -0
  192. package/dist/types/registry/openrouter.d.ts +13 -0
  193. package/dist/types/registry/parallel.d.ts +14 -0
  194. package/dist/types/registry/perplexity.d.ts +7 -0
  195. package/dist/types/registry/qianfan.d.ts +7 -0
  196. package/dist/types/registry/qwen-portal.d.ts +7 -0
  197. package/dist/types/registry/registry.d.ts +367 -0
  198. package/dist/types/registry/sakana.d.ts +7 -0
  199. package/dist/types/registry/siliconflow-cn.d.ts +7 -0
  200. package/dist/types/registry/siliconflow.d.ts +7 -0
  201. package/dist/types/registry/synthetic.d.ts +6 -0
  202. package/dist/types/registry/tavily.d.ts +14 -0
  203. package/dist/types/registry/together.d.ts +6 -0
  204. package/dist/types/registry/types.d.ts +75 -0
  205. package/dist/types/registry/umans.d.ts +7 -0
  206. package/dist/types/registry/venice.d.ts +13 -0
  207. package/dist/types/registry/vercel-ai-gateway.d.ts +7 -0
  208. package/dist/types/registry/vllm.d.ts +7 -0
  209. package/dist/types/registry/wafer-serverless.d.ts +6 -0
  210. package/dist/types/registry/xai-oauth.d.ts +7 -0
  211. package/dist/types/registry/xai.d.ts +7 -0
  212. package/dist/types/registry/xiaomi-token-plan-ams.d.ts +6 -0
  213. package/dist/types/registry/xiaomi-token-plan-cn.d.ts +6 -0
  214. package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +6 -0
  215. package/dist/types/registry/xiaomi.d.ts +6 -0
  216. package/dist/types/registry/zai.d.ts +15 -0
  217. package/dist/types/registry/zenmux.d.ts +7 -0
  218. package/dist/types/registry/zhipu-coding-plan.d.ts +7 -0
  219. package/dist/types/stream.d.ts +46 -0
  220. package/dist/types/types.d.ts +1064 -0
  221. package/dist/types/usage/alibaba-token-plan.d.ts +3 -0
  222. package/dist/types/usage/claude.d.ts +4 -0
  223. package/dist/types/usage/cursor.d.ts +4 -0
  224. package/dist/types/usage/gemini.d.ts +2 -0
  225. package/dist/types/usage/github-copilot.d.ts +7 -0
  226. package/dist/types/usage/google-antigravity.d.ts +15 -0
  227. package/dist/types/usage/kimi.d.ts +2 -0
  228. package/dist/types/usage/minimax-code.d.ts +3 -0
  229. package/dist/types/usage/ollama.d.ts +5 -0
  230. package/dist/types/usage/openai-codex-base-url.d.ts +18 -0
  231. package/dist/types/usage/openai-codex-reset.d.ts +88 -0
  232. package/dist/types/usage/openai-codex.d.ts +10 -0
  233. package/dist/types/usage/opencode-go.d.ts +2 -0
  234. package/dist/types/usage/shared.d.ts +1 -0
  235. package/dist/types/usage/synthetic.d.ts +2 -0
  236. package/dist/types/usage/umans.d.ts +2 -0
  237. package/dist/types/usage/xai-oauth.d.ts +12 -0
  238. package/dist/types/usage/zai.d.ts +3 -0
  239. package/dist/types/usage.d.ts +527 -0
  240. package/dist/types/utils/abort.d.ts +25 -0
  241. package/dist/types/utils/anthropic-auth.d.ts +35 -0
  242. package/dist/types/utils/aws-profile.d.ts +17 -0
  243. package/dist/types/utils/block-symbols.d.ts +62 -0
  244. package/dist/types/utils/deterministic-id.d.ts +16 -0
  245. package/dist/types/utils/empty-completion-retry.d.ts +21 -0
  246. package/dist/types/utils/event-stream.d.ts +39 -0
  247. package/dist/types/utils/foundry.d.ts +1 -0
  248. package/dist/types/utils/google-validation.d.ts +2 -0
  249. package/dist/types/utils/harmony-leak.d.ts +135 -0
  250. package/dist/types/utils/http-inspector.d.ts +49 -0
  251. package/dist/types/utils/idle-iterator.d.ts +149 -0
  252. package/dist/types/utils/leaked-thinking-stream.d.ts +33 -0
  253. package/dist/types/utils/openai-http.d.ts +48 -0
  254. package/dist/types/utils/openrouter-headers.d.ts +1 -0
  255. package/dist/types/utils/parse-bind.d.ts +23 -0
  256. package/dist/types/utils/provider-response.d.ts +3 -0
  257. package/dist/types/utils/proxy.d.ts +39 -0
  258. package/dist/types/utils/request-debug.d.ts +29 -0
  259. package/dist/types/utils/retry-after.d.ts +4 -0
  260. package/dist/types/utils/retry.d.ts +14 -0
  261. package/dist/types/utils/schema/adapt.d.ts +24 -0
  262. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  263. package/dist/types/utils/schema/dereference.d.ts +11 -0
  264. package/dist/types/utils/schema/draft.d.ts +10 -0
  265. package/dist/types/utils/schema/equality.d.ts +4 -0
  266. package/dist/types/utils/schema/fields.d.ts +54 -0
  267. package/dist/types/utils/schema/index.d.ts +14 -0
  268. package/dist/types/utils/schema/json-schema-validator.d.ts +20 -0
  269. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  270. package/dist/types/utils/schema/normalize.d.ts +153 -0
  271. package/dist/types/utils/schema/spill.d.ts +8 -0
  272. package/dist/types/utils/schema/stamps.d.ts +17 -0
  273. package/dist/types/utils/schema/strict-tool-validation.d.ts +16 -0
  274. package/dist/types/utils/schema/types.d.ts +4 -0
  275. package/dist/types/utils/schema/typescript.d.ts +24 -0
  276. package/dist/types/utils/schema/wire.d.ts +52 -0
  277. package/dist/types/utils/sdk-stream-timeout.d.ts +33 -0
  278. package/dist/types/utils/sse-debug.d.ts +5 -0
  279. package/dist/types/utils/stream-markup-healing.d.ts +87 -0
  280. package/dist/types/utils/thinking-loop.d.ts +102 -0
  281. package/dist/types/utils/tool-call-loop-guard.d.ts +26 -0
  282. package/dist/types/utils/tool-choice.d.ts +52 -0
  283. package/dist/types/utils/validation.d.ts +28 -0
  284. package/dist/types/utils.d.ts +57 -0
  285. package/package.json +138 -0
  286. package/src/api-registry.ts +109 -0
  287. package/src/auth/sqlite-credential-store.ts +2066 -0
  288. package/src/auth-broker/client.ts +471 -0
  289. package/src/auth-broker/discover.ts +310 -0
  290. package/src/auth-broker/index.ts +7 -0
  291. package/src/auth-broker/refresher.ts +117 -0
  292. package/src/auth-broker/remote-store.ts +1332 -0
  293. package/src/auth-broker/server.ts +898 -0
  294. package/src/auth-broker/snapshot-cache.ts +200 -0
  295. package/src/auth-broker/types.ts +193 -0
  296. package/src/auth-broker/wire-schema-resource.ts +487 -0
  297. package/src/auth-broker/wire-schemas.ts +43 -0
  298. package/src/auth-gateway/http.ts +227 -0
  299. package/src/auth-gateway/index.ts +3 -0
  300. package/src/auth-gateway/server.ts +836 -0
  301. package/src/auth-gateway/types.ts +153 -0
  302. package/src/auth-retry.ts +401 -0
  303. package/src/auth-storage.ts +6540 -0
  304. package/src/dialect/anthropic.md +31 -0
  305. package/src/dialect/anthropic.ts +608 -0
  306. package/src/dialect/catalog.ts +29 -0
  307. package/src/dialect/coercion.ts +136 -0
  308. package/src/dialect/deepseek.md +24 -0
  309. package/src/dialect/deepseek.ts +609 -0
  310. package/src/dialect/demotion.ts +40 -0
  311. package/src/dialect/examples.ts +71 -0
  312. package/src/dialect/factory.ts +34 -0
  313. package/src/dialect/fenced-thinking.ts +184 -0
  314. package/src/dialect/gemini.md +44 -0
  315. package/src/dialect/gemini.ts +583 -0
  316. package/src/dialect/gemma.md +33 -0
  317. package/src/dialect/gemma.ts +387 -0
  318. package/src/dialect/glm.md +32 -0
  319. package/src/dialect/glm.ts +579 -0
  320. package/src/dialect/harmony.md +31 -0
  321. package/src/dialect/harmony.ts +345 -0
  322. package/src/dialect/hermes.md +25 -0
  323. package/src/dialect/hermes.ts +206 -0
  324. package/src/dialect/history.ts +81 -0
  325. package/src/dialect/index.ts +15 -0
  326. package/src/dialect/inventory.ts +30 -0
  327. package/src/dialect/kimi.md +24 -0
  328. package/src/dialect/kimi.ts +340 -0
  329. package/src/dialect/minimax.md +31 -0
  330. package/src/dialect/minimax.ts +95 -0
  331. package/src/dialect/owned-stream.ts +481 -0
  332. package/src/dialect/prompt-template.md +12 -0
  333. package/src/dialect/qwen3.md +28 -0
  334. package/src/dialect/qwen3.ts +240 -0
  335. package/src/dialect/rendering.ts +304 -0
  336. package/src/dialect/thinking.ts +292 -0
  337. package/src/dialect/types.ts +56 -0
  338. package/src/dialect/xml.md +22 -0
  339. package/src/dialect/xml.ts +90 -0
  340. package/src/error/abort.ts +18 -0
  341. package/src/error/auth-classify.ts +47 -0
  342. package/src/error/auth.ts +48 -0
  343. package/src/error/aws.ts +35 -0
  344. package/src/error/classes.ts +281 -0
  345. package/src/error/finalize.ts +69 -0
  346. package/src/error/flags.ts +602 -0
  347. package/src/error/format.ts +45 -0
  348. package/src/error/gateway.ts +96 -0
  349. package/src/error/index.ts +14 -0
  350. package/src/error/oauth.ts +58 -0
  351. package/src/error/provider.ts +63 -0
  352. package/src/error/rate-limit.ts +303 -0
  353. package/src/error/retryable.ts +70 -0
  354. package/src/error/validation.ts +44 -0
  355. package/src/index.ts +51 -0
  356. package/src/provider-details.ts +90 -0
  357. package/src/providers/amazon-bedrock.ts +1064 -0
  358. package/src/providers/anthropic-client.ts +317 -0
  359. package/src/providers/anthropic-messages-server-schema.ts +252 -0
  360. package/src/providers/anthropic-messages-server.ts +818 -0
  361. package/src/providers/anthropic-wire.ts +359 -0
  362. package/src/providers/anthropic.ts +4539 -0
  363. package/src/providers/aws-credentials.ts +772 -0
  364. package/src/providers/aws-eventstream.ts +181 -0
  365. package/src/providers/aws-sigv4.ts +218 -0
  366. package/src/providers/azure-openai-responses.ts +438 -0
  367. package/src/providers/bedrock-mantle.ts +110 -0
  368. package/src/providers/claude-code-fingerprint.ts +20 -0
  369. package/src/providers/cowork-fetch.ts +201 -0
  370. package/src/providers/cursor/exec-modern.ts +496 -0
  371. package/src/providers/cursor/proto/agent.proto +4533 -0
  372. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  373. package/src/providers/cursor/proto/buf.yaml +17 -0
  374. package/src/providers/cursor-pi-args.ts +165 -0
  375. package/src/providers/cursor.ts +4689 -0
  376. package/src/providers/devin/proto/buf/validate/validate.proto +468 -0
  377. package/src/providers/devin/proto/buf.gen.yaml +33 -0
  378. package/src/providers/devin/proto/buf.yaml +17 -0
  379. package/src/providers/devin/proto/cel/expr/checked.proto +103 -0
  380. package/src/providers/devin/proto/cel/expr/eval.proto +38 -0
  381. package/src/providers/devin/proto/cel/expr/explain.proto +15 -0
  382. package/src/providers/devin/proto/cel/expr/syntax.proto +113 -0
  383. package/src/providers/devin/proto/cel/expr/value.proto +41 -0
  384. package/src/providers/devin/proto/connectext/grpc/status/v1/status.proto +11 -0
  385. package/src/providers/devin/proto/errorspb/errors.proto +56 -0
  386. package/src/providers/devin/proto/errorspb/hintdetail.proto +7 -0
  387. package/src/providers/devin/proto/errorspb/markers.proto +10 -0
  388. package/src/providers/devin/proto/errorspb/tags.proto +12 -0
  389. package/src/providers/devin/proto/errorspb/testing.proto +6 -0
  390. package/src/providers/devin/proto/exa/analytics_pb/analytics.proto +188 -0
  391. package/src/providers/devin/proto/exa/api_server_pb/api_server.proto +2461 -0
  392. package/src/providers/devin/proto/exa/auth_pb/auth.proto +19 -0
  393. package/src/providers/devin/proto/exa/auto_cascade_common_pb/auto_cascade_common.proto +79 -0
  394. package/src/providers/devin/proto/exa/browser_preview_pb/browser_preview.proto +32 -0
  395. package/src/providers/devin/proto/exa/bug_checker_pb/bug_checker.proto +22 -0
  396. package/src/providers/devin/proto/exa/cascade_plugins_pb/cascade_plugins.proto +262 -0
  397. package/src/providers/devin/proto/exa/chat_client_server_pb/chat_client_server.proto +57 -0
  398. package/src/providers/devin/proto/exa/chat_pb/chat.proto +449 -0
  399. package/src/providers/devin/proto/exa/code_edit/code_edit_pb/code_edit.proto +186 -0
  400. package/src/providers/devin/proto/exa/codeium_common_pb/codeium_common.proto +4157 -0
  401. package/src/providers/devin/proto/exa/context_module_pb/context_module.proto +175 -0
  402. package/src/providers/devin/proto/exa/cortex_pb/cortex.proto +3268 -0
  403. package/src/providers/devin/proto/exa/dev_pb/dev.proto +26 -0
  404. package/src/providers/devin/proto/exa/diff_action_pb/diff_action.proto +75 -0
  405. package/src/providers/devin/proto/exa/eval/pr_eval/datasets_pb/datasets.proto +103 -0
  406. package/src/providers/devin/proto/exa/eval_pb/eval.proto +1315 -0
  407. package/src/providers/devin/proto/exa/extension_server_pb/extension_server.proto +556 -0
  408. package/src/providers/devin/proto/exa/file_system_provider_pb/file_system_provider.proto +75 -0
  409. package/src/providers/devin/proto/exa/index_pb/index.proto +461 -0
  410. package/src/providers/devin/proto/exa/knowledge_base_pb/knowledge_base.proto +144 -0
  411. package/src/providers/devin/proto/exa/language_server_pb/language_server.proto +2385 -0
  412. package/src/providers/devin/proto/exa/model_management_pb/model_management.proto +186 -0
  413. package/src/providers/devin/proto/exa/opensearch_clients_pb/opensearch_clients.proto +503 -0
  414. package/src/providers/devin/proto/exa/product_analytics_pb/product_analytics.proto +37 -0
  415. package/src/providers/devin/proto/exa/prompt_pb/prompt.proto +92 -0
  416. package/src/providers/devin/proto/exa/reactive_component_pb/reactive_component.proto +96 -0
  417. package/src/providers/devin/proto/exa/seat_management_pb/seat_management.proto +2680 -0
  418. package/src/providers/devin/proto/exa/tokenizer_pb/tokenizer.proto +37 -0
  419. package/src/providers/devin/proto/exa/trainer_pb/config.proto +647 -0
  420. package/src/providers/devin/proto/exa/tree_sitter/language_data_pb/language_data.proto +14 -0
  421. package/src/providers/devin/proto/exa/trust_pb/trust.proto +157 -0
  422. package/src/providers/devin/proto/exa/user_analytics_pb/user_analytics.proto +519 -0
  423. package/src/providers/devin/proto/google.golang.org/appengine/internal/base/api_base.proto +28 -0
  424. package/src/providers/devin/proto/google.golang.org/appengine/internal/datastore/datastore_v3.proto +484 -0
  425. package/src/providers/devin/proto/google.golang.org/appengine/internal/log/log_service.proto +136 -0
  426. package/src/providers/devin/proto/google.golang.org/appengine/internal/remote_api/remote_api.proto +42 -0
  427. package/src/providers/devin/proto/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto +61 -0
  428. package/src/providers/devin/proto/grpc/binlog/v1/binarylog.proto +84 -0
  429. package/src/providers/devin/proto/io/prometheus/client/metrics.proto +98 -0
  430. package/src/providers/devin.ts +679 -0
  431. package/src/providers/error-message.ts +23 -0
  432. package/src/providers/github-copilot-headers.ts +141 -0
  433. package/src/providers/gitlab-duo-workflow-chatml-note.md +1 -0
  434. package/src/providers/gitlab-duo-workflow.ts +3135 -0
  435. package/src/providers/gitlab-duo.ts +399 -0
  436. package/src/providers/google-auth.ts +330 -0
  437. package/src/providers/google-gemini-cli.ts +1370 -0
  438. package/src/providers/google-shared.ts +1122 -0
  439. package/src/providers/google-types.ts +180 -0
  440. package/src/providers/google-vertex.ts +135 -0
  441. package/src/providers/google.ts +47 -0
  442. package/src/providers/grammar.ts +70 -0
  443. package/src/providers/kimi.ts +51 -0
  444. package/src/providers/mock.ts +514 -0
  445. package/src/providers/ollama.ts +776 -0
  446. package/src/providers/openai-anthropic-shim.ts +166 -0
  447. package/src/providers/openai-chat-server-schema.ts +243 -0
  448. package/src/providers/openai-chat-server.ts +752 -0
  449. package/src/providers/openai-chat-wire.ts +859 -0
  450. package/src/providers/openai-codex/request-transformer.ts +491 -0
  451. package/src/providers/openai-codex/response-handler.ts +102 -0
  452. package/src/providers/openai-codex-responses.ts +4716 -0
  453. package/src/providers/openai-completions.ts +2389 -0
  454. package/src/providers/openai-reasoning-fallback.ts +269 -0
  455. package/src/providers/openai-responses-server-schema.ts +397 -0
  456. package/src/providers/openai-responses-server.ts +1466 -0
  457. package/src/providers/openai-responses-wire.ts +6416 -0
  458. package/src/providers/openai-responses.ts +1393 -0
  459. package/src/providers/openai-shared.ts +3500 -0
  460. package/src/providers/pi-native-client.ts +275 -0
  461. package/src/providers/pi-native-server.ts +245 -0
  462. package/src/providers/register-builtins.ts +503 -0
  463. package/src/providers/synthetic.ts +50 -0
  464. package/src/providers/transform-messages.ts +1083 -0
  465. package/src/providers/vision-guard.ts +54 -0
  466. package/src/registry/aiand.ts +22 -0
  467. package/src/registry/aimlapi.ts +6 -0
  468. package/src/registry/alibaba-coding-plan.ts +104 -0
  469. package/src/registry/alibaba-token-plan.ts +125 -0
  470. package/src/registry/amazon-bedrock.ts +22 -0
  471. package/src/registry/anthropic.ts +26 -0
  472. package/src/registry/api-key-login.ts +115 -0
  473. package/src/registry/api-key-validation.ts +145 -0
  474. package/src/registry/aws.ts +57 -0
  475. package/src/registry/azure.ts +6 -0
  476. package/src/registry/baseten.ts +22 -0
  477. package/src/registry/bedrock-mantle.ts +34 -0
  478. package/src/registry/cerebras.ts +23 -0
  479. package/src/registry/cloudflare-ai-gateway.ts +45 -0
  480. package/src/registry/coreweave.ts +40 -0
  481. package/src/registry/cursor.ts +20 -0
  482. package/src/registry/deepseek.ts +46 -0
  483. package/src/registry/derived.ts +9 -0
  484. package/src/registry/devin.ts +15 -0
  485. package/src/registry/exa.ts +19 -0
  486. package/src/registry/firepass.ts +32 -0
  487. package/src/registry/fireworks.ts +28 -0
  488. package/src/registry/github-copilot.ts +22 -0
  489. package/src/registry/gitlab-duo-workflow.ts +20 -0
  490. package/src/registry/gitlab-duo.ts +19 -0
  491. package/src/registry/gmi-cloud.ts +22 -0
  492. package/src/registry/google-antigravity.ts +22 -0
  493. package/src/registry/google-gemini-cli.ts +22 -0
  494. package/src/registry/google-vertex.ts +38 -0
  495. package/src/registry/google.ts +6 -0
  496. package/src/registry/groq.ts +6 -0
  497. package/src/registry/huggingface.ts +29 -0
  498. package/src/registry/index.ts +4 -0
  499. package/src/registry/kagi.ts +46 -0
  500. package/src/registry/kilo.ts +114 -0
  501. package/src/registry/kimi-code.ts +17 -0
  502. package/src/registry/litellm.ts +45 -0
  503. package/src/registry/llama-cpp.ts +35 -0
  504. package/src/registry/lm-studio.ts +31 -0
  505. package/src/registry/meta.ts +22 -0
  506. package/src/registry/minimax-code-cn.ts +12 -0
  507. package/src/registry/minimax-code.ts +12 -0
  508. package/src/registry/minimax.ts +6 -0
  509. package/src/registry/mistral.ts +6 -0
  510. package/src/registry/moonshot.ts +28 -0
  511. package/src/registry/nanogpt.ts +22 -0
  512. package/src/registry/novita.ts +25 -0
  513. package/src/registry/nvidia.ts +61 -0
  514. package/src/registry/oauth/anthropic-constants.ts +12 -0
  515. package/src/registry/oauth/anthropic.ts +346 -0
  516. package/src/registry/oauth/callback-server.ts +438 -0
  517. package/src/registry/oauth/cursor.ts +187 -0
  518. package/src/registry/oauth/device-code.ts +92 -0
  519. package/src/registry/oauth/devin.ts +124 -0
  520. package/src/registry/oauth/github-copilot.ts +369 -0
  521. package/src/registry/oauth/gitlab-duo-workflow.ts +146 -0
  522. package/src/registry/oauth/gitlab-duo.ts +222 -0
  523. package/src/registry/oauth/google-antigravity.ts +225 -0
  524. package/src/registry/oauth/google-gemini-cli.ts +297 -0
  525. package/src/registry/oauth/google-oauth-shared.ts +211 -0
  526. package/src/registry/oauth/index.ts +187 -0
  527. package/src/registry/oauth/kimi.ts +297 -0
  528. package/src/registry/oauth/minimax-code.ts +53 -0
  529. package/src/registry/oauth/oauth.html +317 -0
  530. package/src/registry/oauth/openai-codex.ts +384 -0
  531. package/src/registry/oauth/opencode.ts +50 -0
  532. package/src/registry/oauth/perplexity.ts +228 -0
  533. package/src/registry/oauth/pkce.ts +18 -0
  534. package/src/registry/oauth/types.ts +96 -0
  535. package/src/registry/oauth/wafer.ts +24 -0
  536. package/src/registry/oauth/xai-oauth.ts +559 -0
  537. package/src/registry/oauth/xiaomi.ts +211 -0
  538. package/src/registry/oauth/zai.ts +285 -0
  539. package/src/registry/ollama-cloud.ts +36 -0
  540. package/src/registry/ollama.ts +43 -0
  541. package/src/registry/openai-codex-device.ts +18 -0
  542. package/src/registry/openai-codex.ts +19 -0
  543. package/src/registry/openai.ts +6 -0
  544. package/src/registry/opencode-go.ts +12 -0
  545. package/src/registry/opencode-zen.ts +12 -0
  546. package/src/registry/openrouter.ts +28 -0
  547. package/src/registry/parallel.ts +45 -0
  548. package/src/registry/perplexity.ts +13 -0
  549. package/src/registry/qianfan.ts +27 -0
  550. package/src/registry/qwen-portal.ts +50 -0
  551. package/src/registry/registry.ts +182 -0
  552. package/src/registry/sakana.ts +22 -0
  553. package/src/registry/siliconflow-cn.ts +22 -0
  554. package/src/registry/siliconflow.ts +22 -0
  555. package/src/registry/synthetic.ts +21 -0
  556. package/src/registry/tavily.ts +45 -0
  557. package/src/registry/together.ts +22 -0
  558. package/src/registry/types.ts +86 -0
  559. package/src/registry/umans.ts +23 -0
  560. package/src/registry/venice.ts +33 -0
  561. package/src/registry/vercel-ai-gateway.ts +38 -0
  562. package/src/registry/vllm.ts +34 -0
  563. package/src/registry/wafer-serverless.ts +12 -0
  564. package/src/registry/xai-oauth.ts +17 -0
  565. package/src/registry/xai.ts +22 -0
  566. package/src/registry/xiaomi-token-plan-ams.ts +12 -0
  567. package/src/registry/xiaomi-token-plan-cn.ts +12 -0
  568. package/src/registry/xiaomi-token-plan-sgp.ts +12 -0
  569. package/src/registry/xiaomi.ts +12 -0
  570. package/src/registry/zai.ts +41 -0
  571. package/src/registry/zenmux.ts +22 -0
  572. package/src/registry/zhipu-coding-plan.ts +27 -0
  573. package/src/stream.ts +1944 -0
  574. package/src/types.ts +1243 -0
  575. package/src/usage/alibaba-token-plan.ts +230 -0
  576. package/src/usage/claude.ts +830 -0
  577. package/src/usage/cursor.ts +335 -0
  578. package/src/usage/gemini.ts +258 -0
  579. package/src/usage/github-copilot.ts +424 -0
  580. package/src/usage/google-antigravity.ts +497 -0
  581. package/src/usage/kimi.ts +277 -0
  582. package/src/usage/minimax-code.ts +291 -0
  583. package/src/usage/ollama.ts +41 -0
  584. package/src/usage/openai-codex-base-url.ts +35 -0
  585. package/src/usage/openai-codex-reset.ts +205 -0
  586. package/src/usage/openai-codex.ts +627 -0
  587. package/src/usage/opencode-go.ts +89 -0
  588. package/src/usage/shared.ts +10 -0
  589. package/src/usage/synthetic.ts +180 -0
  590. package/src/usage/umans.ts +192 -0
  591. package/src/usage/xai-oauth.ts +414 -0
  592. package/src/usage/zai.ts +370 -0
  593. package/src/usage.ts +411 -0
  594. package/src/utils/abort.ts +67 -0
  595. package/src/utils/anthropic-auth.ts +93 -0
  596. package/src/utils/aws-profile.ts +88 -0
  597. package/src/utils/block-symbols.ts +78 -0
  598. package/src/utils/deterministic-id.ts +20 -0
  599. package/src/utils/empty-completion-retry.ts +161 -0
  600. package/src/utils/event-stream.ts +202 -0
  601. package/src/utils/foundry.ts +8 -0
  602. package/src/utils/google-validation.ts +25 -0
  603. package/src/utils/harmony-leak.ts +500 -0
  604. package/src/utils/http-inspector.ts +196 -0
  605. package/src/utils/idle-iterator.ts +531 -0
  606. package/src/utils/leaked-thinking-stream.ts +483 -0
  607. package/src/utils/openai-http.ts +119 -0
  608. package/src/utils/openrouter-headers.ts +12 -0
  609. package/src/utils/parse-bind.ts +56 -0
  610. package/src/utils/provider-response.ts +30 -0
  611. package/src/utils/proxy.ts +314 -0
  612. package/src/utils/request-debug.ts +351 -0
  613. package/src/utils/retry-after.ts +121 -0
  614. package/src/utils/retry.ts +77 -0
  615. package/src/utils/schema/CONSTRAINTS.md +168 -0
  616. package/src/utils/schema/adapt.ts +36 -0
  617. package/src/utils/schema/compatibility.ts +435 -0
  618. package/src/utils/schema/dereference.ts +98 -0
  619. package/src/utils/schema/draft.ts +341 -0
  620. package/src/utils/schema/equality.ts +97 -0
  621. package/src/utils/schema/fields.ts +210 -0
  622. package/src/utils/schema/index.ts +14 -0
  623. package/src/utils/schema/json-schema-validator.ts +595 -0
  624. package/src/utils/schema/meta-validator.ts +167 -0
  625. package/src/utils/schema/normalize.ts +2314 -0
  626. package/src/utils/schema/spill.ts +43 -0
  627. package/src/utils/schema/stamps.ts +109 -0
  628. package/src/utils/schema/strict-tool-validation.ts +117 -0
  629. package/src/utils/schema/types.ts +10 -0
  630. package/src/utils/schema/typescript.ts +212 -0
  631. package/src/utils/schema/wire.ts +662 -0
  632. package/src/utils/sdk-stream-timeout.ts +43 -0
  633. package/src/utils/sse-debug.ts +18 -0
  634. package/src/utils/stream-markup-healing.ts +247 -0
  635. package/src/utils/thinking-loop.ts +552 -0
  636. package/src/utils/tool-call-loop-guard.ts +107 -0
  637. package/src/utils/tool-choice.ts +101 -0
  638. package/src/utils/validation.ts +1932 -0
  639. package/src/utils.ts +492 -0
@@ -0,0 +1,1393 @@
1
+ import { scheduler } from "node:timers/promises";
2
+ import { hostMatchesUrl } from "@linxiraos/pi-catalog/hosts";
3
+ import { $flag, logger, structuredCloneJSON } from "@linxiraos/pi-utils";
4
+ import * as AIError from "../error";
5
+ import { getEnvApiKey } from "../stream";
6
+ import type {
7
+ AssistantMessage,
8
+ CacheRetention,
9
+ Context,
10
+ Model,
11
+ OpenAICompat,
12
+ ProviderSessionState,
13
+ RawSseEvent,
14
+ ServiceTier,
15
+ StreamFunction,
16
+ StreamOptions,
17
+ Tool,
18
+ ToolChoice,
19
+ } from "../types";
20
+ import {
21
+ createOpenAIResponsesHistoryPayload,
22
+ normalizeSystemPrompts,
23
+ resolveCacheRetention,
24
+ sanitizeOpenAIResponsesAssistantHistoryItemsForReplay,
25
+ } from "../utils";
26
+ import { createAbortSourceTracker } from "../utils/abort";
27
+ import { withEmptyCompletionRetry } from "../utils/empty-completion-retry";
28
+ import { AssistantMessageEventStream } from "../utils/event-stream";
29
+ import type { RawHttpRequestDump } from "../utils/http-inspector";
30
+ import {
31
+ getOpenAIStreamFirstEventTimeoutMs,
32
+ getOpenAIStreamIdleTimeoutMs,
33
+ iterateWithIdleTimeout,
34
+ } from "../utils/idle-iterator";
35
+ import { OpenAIHttpError, postOpenAIStream } from "../utils/openai-http";
36
+ import { notifyProviderResponse } from "../utils/provider-response";
37
+ import { callWithCopilotModelRetry } from "../utils/retry";
38
+ import {
39
+ adaptSchemaForStrict,
40
+ findStrictToolSchemaViolation,
41
+ NO_STRICT,
42
+ normalizeSchemaForMoonshot,
43
+ sanitizeSchemaForOpenAIResponses,
44
+ toolWireSchema,
45
+ } from "../utils/schema";
46
+ import {
47
+ isForcedToolChoice,
48
+ mapToOpenAIResponsesToolChoice,
49
+ type OpenAIResponsesToolChoice,
50
+ } from "../utils/tool-choice";
51
+ import { compactGrammarDefinition } from "./grammar";
52
+ import {
53
+ applyOpenAIReasoningEffortFallback,
54
+ clearOpenAIReasoningEffortFallbackState,
55
+ createOpenAIReasoningEffortFallbackKey,
56
+ createOpenAIReasoningEffortFallbackState,
57
+ getOpenAIReasoningEffortFallback,
58
+ type OpenAIReasoningEffortFallback,
59
+ type OpenAIReasoningEffortFallbackState,
60
+ rememberOpenAIReasoningEffortFallback,
61
+ resolveOpenAIReasoningEffortFallback,
62
+ } from "./openai-reasoning-fallback";
63
+ import type {
64
+ Tool as OpenAITool,
65
+ ResponseCreateParamsStreaming,
66
+ ResponseInput,
67
+ ResponseInputContent,
68
+ ResponseStreamEvent,
69
+ } from "./openai-responses-wire";
70
+ import {
71
+ applyCommonResponsesSamplingParams,
72
+ applyOpenAIExtraBody,
73
+ applyOpenAIGatewayRouting,
74
+ applyResponsesCompatPolicy,
75
+ applyVercelResponsesCacheControls,
76
+ applyWireModelIdTransform,
77
+ buildResponsesDeltaInput,
78
+ buildResponsesInput,
79
+ clearOpenAIStrictToolsState,
80
+ createInitialResponsesAssistantMessage,
81
+ createOpenAIStrictToolsState,
82
+ disableStrictToolsForScope,
83
+ getOpenAIPromptCacheKey,
84
+ getOpenAIResponsesRoutingSessionId,
85
+ getOpenAIStrictToolsScope,
86
+ getOpenRouterResponsesSessionId,
87
+ isCompiledGrammarTooLargeStrictError,
88
+ isOpenAIResponsesProgressEvent,
89
+ isOpenRouterAnthropicModel,
90
+ isStrictToolsDisabledForScope,
91
+ type OpenAIPromptCacheOptions,
92
+ type OpenAIStrictToolsScope,
93
+ type OpenAIStrictToolsState,
94
+ processResponsesStream,
95
+ resolveOpenAICompatPolicy,
96
+ resolveOpenAIOutputTokenParam,
97
+ resolveOpenAIRequestSetup,
98
+ resolveOpenAIResponsesOutputClamp,
99
+ shouldRetryWithoutStrictTools,
100
+ } from "./openai-shared";
101
+
102
+ // OpenAI Responses-specific options
103
+ export interface OpenAIResponsesOptions extends StreamOptions {
104
+ reasoning?: "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
105
+ reasoningSummary?: "auto" | "detailed" | "concise" | null;
106
+ serviceTier?: ServiceTier;
107
+ textVerbosity?: "low" | "medium" | "high";
108
+ toolChoice?: ToolChoice;
109
+ openrouterVariant?: string;
110
+ maxTokensExplicit?: boolean;
111
+ disableReasoning?: boolean;
112
+ /**
113
+ * Stateful turns: chain via `previous_response_id` + delta input instead of
114
+ * replaying the full transcript. Forces `store: true` (the platform only
115
+ * resolves stored responses). Defaults ON against the official OpenAI API
116
+ * and OFF for other Responses endpoints; `PI_OPENAI_STATEFUL` overrides the
117
+ * default, and `false` here vetoes everything. Requires `sessionId` +
118
+ * `providerSessionState`. Falls back to a full replay whenever history
119
+ * mutates or the server reports a stale id.
120
+ */
121
+ statefulResponses?: boolean;
122
+ /**
123
+ * Override catalog compat for strict tool call/result pairing when building
124
+ * Responses API inputs. Default behavior is catalog compat; this is only for
125
+ * debugging/adapter wrappers.
126
+ */
127
+ strictResponsesPairing?: boolean;
128
+ /**
129
+ * Override catalog compat for `include: ["reasoning.encrypted_content"]`.
130
+ * Default behavior is catalog compat; this is only for debugging/adapter wrappers.
131
+ */
132
+ includeEncryptedReasoning?: boolean;
133
+ /**
134
+ * Override catalog compat for stripping `type: "reasoning"` items from
135
+ * replayed conversation history before request encoding. Default behavior is
136
+ * catalog compat; this is only for debugging/adapter wrappers.
137
+ */
138
+ filterReasoningHistory?: boolean;
139
+ /**
140
+ * Override catalog compat for suppressing the `reasoning.effort` wire param.
141
+ * Default behavior is catalog compat; this is only for debugging/adapter wrappers.
142
+ */
143
+ omitReasoningEffort?: boolean;
144
+ /**
145
+ * Extra request headers merged onto the model/copilot defaults. Used by
146
+ * adapter wrappers to inject provider-specific
147
+ * routing or cache hints.
148
+ */
149
+ headers?: Record<string, string>;
150
+ /**
151
+ * Extra body fields merged into the Responses request payload. Used by
152
+ * adapter wrappers to inject provider-specific body keys (e.g.,
153
+ * prompt_cache_key for prompt-cache routing).
154
+ */
155
+ extraBody?: Record<string, unknown>;
156
+ /** Opt-in GPT-5.6+ prompt-cache policy. Unsupported explicit mode fails locally. */
157
+ promptCache?: OpenAIPromptCacheOptions;
158
+ }
159
+
160
+ const OPENAI_RESPONSES_PROVIDER_SESSION_STATE_PREFIX = "openai-responses:";
161
+ const OPENAI_RESPONSES_FIRST_EVENT_TIMEOUT_MESSAGE =
162
+ "OpenAI responses stream timed out while waiting for the first event";
163
+ /** Consecutive stale-previous-response failures before chaining is disabled for the session. */
164
+ const OPENAI_RESPONSES_CHAIN_STALE_FAILURE_LIMIT = 3;
165
+ const OPENAI_RESPONSES_MAX_TRANSIENT_STREAM_RETRIES = 1;
166
+ const OPENAI_RESPONSES_TRANSIENT_STREAM_RETRY_DELAY_MS = 500;
167
+
168
+ function isOpenAIResponsesReplayUnsafeEvent(event: ResponseStreamEvent): boolean {
169
+ switch (event.type) {
170
+ case "response.output_text.delta":
171
+ case "response.refusal.delta":
172
+ case "response.reasoning_summary_text.delta":
173
+ case "response.reasoning_text.delta":
174
+ case "response.function_call_arguments.delta":
175
+ case "response.custom_tool_call_input.delta":
176
+ return typeof event.delta === "string" && event.delta.length > 0;
177
+ case "response.reasoning_summary_part.done":
178
+ return true;
179
+ case "response.output_item.done":
180
+ return true;
181
+ default:
182
+ return false;
183
+ }
184
+ }
185
+
186
+ function isRetryableOpenAIResponsesStreamFailure(error: unknown): boolean {
187
+ return (
188
+ AIError.isTransientStreamParseError(error) ||
189
+ (error instanceof AIError.ProviderResponseError && error.kind === "incomplete-stream")
190
+ );
191
+ }
192
+
193
+ interface OpenAIResponsesProviderSessionState
194
+ extends ProviderSessionState,
195
+ OpenAIStrictToolsState,
196
+ OpenAIReasoningEffortFallbackState {
197
+ nativeHistoryReplayWarmed: boolean;
198
+ /** Stateful `previous_response_id` chain baselines, keyed by baseUrl/model/session. */
199
+ chains: Map<string, OpenAIResponsesChainState>;
200
+ }
201
+
202
+ interface OpenAIResponsesChainState {
203
+ /**
204
+ * Wire params of the last successful turn; never carries
205
+ * `previous_response_id`.
206
+ */
207
+ lastParams?: OpenAIResponsesSamplingParams;
208
+ lastPromptCacheBreakpointPolicy?: "latest-stable-message" | "none";
209
+ lastResponseId?: string;
210
+ /** Output items of the last response, in replay-sanitized form (matches next-turn input). */
211
+ lastResponseItems?: ResponseInput;
212
+ canAppend: boolean;
213
+ /** Consecutive stale-previous-response failures; reset on a successful chained completion. */
214
+ staleFailures: number;
215
+ /** Set once chaining is judged unsupported for this session (circuit breaker). */
216
+ disabled: boolean;
217
+ }
218
+
219
+ function createOpenAIResponsesProviderSessionState(): OpenAIResponsesProviderSessionState {
220
+ const strictToolsState = createOpenAIStrictToolsState();
221
+ const reasoningEffortFallbackState = createOpenAIReasoningEffortFallbackState();
222
+ const state: OpenAIResponsesProviderSessionState = {
223
+ ...strictToolsState,
224
+ ...reasoningEffortFallbackState,
225
+ nativeHistoryReplayWarmed: false,
226
+ chains: new Map(),
227
+ close: () => {
228
+ state.nativeHistoryReplayWarmed = false;
229
+ state.chains.clear();
230
+ clearOpenAIStrictToolsState(state);
231
+ clearOpenAIReasoningEffortFallbackState(state);
232
+ },
233
+ };
234
+ return state;
235
+ }
236
+
237
+ function getOpenAIResponsesProviderSessionState(
238
+ model: Model<"openai-responses">,
239
+ providerSessionState: Map<string, ProviderSessionState> | undefined,
240
+ ): OpenAIResponsesProviderSessionState | undefined {
241
+ if (!providerSessionState) return undefined;
242
+ const key = `${OPENAI_RESPONSES_PROVIDER_SESSION_STATE_PREFIX}${model.provider}`;
243
+ const existing = providerSessionState.get(key) as OpenAIResponsesProviderSessionState | undefined;
244
+ if (existing) return existing;
245
+ const created = createOpenAIResponsesProviderSessionState();
246
+ providerSessionState.set(key, created);
247
+ return created;
248
+ }
249
+
250
+ function isOpenAIResponsesStatefulEnabled(
251
+ options: OpenAIResponsesOptions | undefined,
252
+ baseUrl: string | undefined,
253
+ ): boolean {
254
+ if (options?.statefulResponses === false) return false;
255
+ if (options?.statefulResponses === true) return true;
256
+ // Default ON only against the official OpenAI API: chaining forces
257
+ // `store: true`, and third-party /v1/responses proxies routinely ignore or
258
+ // reject `previous_response_id`. An unset baseUrl means the default
259
+ // endpoint (api.openai.com).
260
+ return $flag("PI_OPENAI_STATEFUL", !baseUrl || hostMatchesUrl(baseUrl, "openai"));
261
+ }
262
+
263
+ function getOpenAIResponsesChainState(
264
+ providerSessionState: OpenAIResponsesProviderSessionState,
265
+ model: Model<"openai-responses">,
266
+ resolvedBaseUrl: string | undefined,
267
+ sessionId: string,
268
+ ): OpenAIResponsesChainState {
269
+ const key = `${resolvedBaseUrl ?? model.baseUrl ?? ""}\u0000${model.id}\u0000${sessionId}`;
270
+ const existing = providerSessionState.chains.get(key);
271
+ if (existing) return existing;
272
+ const created: OpenAIResponsesChainState = { canAppend: false, staleFailures: 0, disabled: false };
273
+ providerSessionState.chains.set(key, created);
274
+ return created;
275
+ }
276
+
277
+ function resetOpenAIResponsesChainState(state: OpenAIResponsesChainState): void {
278
+ state.canAppend = false;
279
+ state.lastParams = undefined;
280
+ state.lastResponseId = undefined;
281
+ state.lastResponseItems = undefined;
282
+ state.lastPromptCacheBreakpointPolicy = undefined;
283
+ }
284
+
285
+ interface OpenAIResponsesChainedParams {
286
+ params: OpenAIResponsesSamplingParams;
287
+ /** Set iff the params carry previous_response_id (delta request). */
288
+ previousResponseId?: string;
289
+ }
290
+
291
+ /**
292
+ * Shape the next turn's request: when the session's append baseline is intact
293
+ * (same options, strict history prefix), chain via `previous_response_id` +
294
+ * delta-only `input`; otherwise break the chain and replay the full transcript.
295
+ *
296
+ * The prefix check runs on the wire form of the conversation arguments, so
297
+ * history mutations or option changes force a full replay.
298
+ */
299
+ function buildOpenAIResponsesChainedParams(
300
+ params: OpenAIResponsesSamplingParams,
301
+ chain: OpenAIResponsesChainState,
302
+ ): OpenAIResponsesChainedParams {
303
+ const deltaInput = chain.canAppend
304
+ ? buildResponsesDeltaInput(chain.lastParams, chain.lastResponseItems, params)
305
+ : null;
306
+ if (deltaInput && deltaInput.length > 0 && chain.lastResponseId) {
307
+ return {
308
+ params: { ...params, previous_response_id: chain.lastResponseId, input: deltaInput },
309
+ previousResponseId: chain.lastResponseId,
310
+ };
311
+ }
312
+ if (chain.canAppend) {
313
+ // History mutated or options changed — break the chain and replay in full.
314
+ resetOpenAIResponsesChainState(chain);
315
+ }
316
+ return { params };
317
+ }
318
+
319
+ function isOpenAIResponsesStalePreviousResponseError(error: unknown): boolean {
320
+ if (!(error instanceof Error)) return false;
321
+ if ((error as { code?: string }).code === "previous_response_not_found") return true;
322
+ // "unsupported" covers endpoints that reject the parameter outright
323
+ // (e.g. "Unsupported parameter: previous_response_id").
324
+ return (
325
+ /previous[ _]?response/i.test(error.message) &&
326
+ /not[ _]?found|invalid|expired|stale|unsupported/i.test(error.message)
327
+ );
328
+ }
329
+
330
+ function registerOpenAIResponsesChainStaleFailure(chain: OpenAIResponsesChainState, error: unknown): void {
331
+ resetOpenAIResponsesChainState(chain);
332
+ chain.staleFailures += 1;
333
+ if (chain.staleFailures >= OPENAI_RESPONSES_CHAIN_STALE_FAILURE_LIMIT) {
334
+ chain.disabled = true;
335
+ }
336
+ logger.debug("OpenAI responses previous_response_id rejected; falling back to full context", {
337
+ error: error instanceof Error ? error.message : String(error),
338
+ consecutiveFailures: chain.staleFailures,
339
+ disabled: chain.disabled,
340
+ });
341
+ }
342
+
343
+ /**
344
+ * One-shot ZDR signal: the org will never resolve a stored response, so skip
345
+ * the staleFailures counter and disable chaining immediately for this session.
346
+ */
347
+ function markOpenAIResponsesChainZeroDataRetention(chain: OpenAIResponsesChainState, error: unknown): void {
348
+ resetOpenAIResponsesChainState(chain);
349
+ chain.disabled = true;
350
+ chain.staleFailures = OPENAI_RESPONSES_CHAIN_STALE_FAILURE_LIMIT;
351
+ logger.debug("OpenAI responses chaining disabled (Zero Data Retention)", {
352
+ error: error instanceof Error ? error.message : String(error),
353
+ });
354
+ }
355
+
356
+ type OpenRouterAnthropicCacheControl = { type: "ephemeral"; ttl?: "1h" };
357
+
358
+ type OpenAIResponsesSamplingParams = ResponseCreateParamsStreaming & {
359
+ top_p?: number;
360
+ top_k?: number;
361
+ min_p?: number;
362
+ presence_penalty?: number;
363
+ repetition_penalty?: number;
364
+ session_id?: string;
365
+ stream_options?: { include_obfuscation?: boolean };
366
+ provider?: OpenAICompat["openRouterRouting"];
367
+ reasoning?: { effort?: string } | { enabled: false };
368
+ cache_control?: OpenRouterAnthropicCacheControl;
369
+ caching?: "auto";
370
+ cache_anchor_items?: number;
371
+ cache_ttl?: "5m" | "1h";
372
+ };
373
+
374
+ function maybeAddOpenRouterAnthropicCacheControl(
375
+ params: OpenAIResponsesSamplingParams,
376
+ model: Model<"openai-responses">,
377
+ cacheRetention: CacheRetention,
378
+ ): void {
379
+ if (cacheRetention === "none" || !isOpenRouterAnthropicModel(model)) return;
380
+ if (params.cache_control != null) return;
381
+ params.cache_control = cacheRetention === "long" ? { type: "ephemeral", ttl: "1h" } : { type: "ephemeral" };
382
+ }
383
+
384
+ /**
385
+ * Generate function for OpenAI Responses API
386
+ */
387
+ const streamOpenAIResponsesOnce = (
388
+ model: Model<"openai-responses">,
389
+ context: Context,
390
+ options?: OpenAIResponsesOptions,
391
+ ): AssistantMessageEventStream => {
392
+ const stream = new AssistantMessageEventStream();
393
+
394
+ // Start async processing
395
+ (async () => {
396
+ const startTime = performance.now();
397
+ let firstTokenTime: number | undefined;
398
+
399
+ const output: AssistantMessage = createInitialResponsesAssistantMessage(model.api, model.provider, model.id);
400
+ let rawRequestDump: RawHttpRequestDump | undefined;
401
+ let chainState: OpenAIResponsesChainState | undefined;
402
+ let sentPreviousResponseId: string | undefined;
403
+ const abortTracker = createAbortSourceTracker(options?.signal);
404
+ const firstEventTimeoutAbortError = new AIError.StreamTimeoutError(OPENAI_RESPONSES_FIRST_EVENT_TIMEOUT_MESSAGE);
405
+ const { requestAbortController, requestSignal } = abortTracker;
406
+ const onSseEvent = options?.onSseEvent;
407
+ const rawSseObserver = onSseEvent
408
+ ? (event: RawSseEvent) => {
409
+ if (!event.event && event.data && event.data !== "[DONE]") {
410
+ try {
411
+ const parsed = JSON.parse(event.data);
412
+ const resolvedEvent =
413
+ typeof parsed.type === "string"
414
+ ? parsed.type
415
+ : typeof parsed.object === "string"
416
+ ? parsed.object
417
+ : null;
418
+ if (resolvedEvent) {
419
+ event.event = resolvedEvent;
420
+ event.raw = [`event: ${resolvedEvent}`, ...event.raw];
421
+ }
422
+ } catch {}
423
+ }
424
+ onSseEvent(event, model);
425
+ }
426
+ : undefined;
427
+
428
+ try {
429
+ // Keep request routing on `sessionId` while allowing callers to pin a
430
+ // stable prompt-cache key independently. Side-channel calls use this to
431
+ // avoid perturbing provider conversation state without cold-starting the cache.
432
+ const routingSessionId = getOpenAIResponsesRoutingSessionId(options);
433
+ const promptCacheSessionId = getOpenAIPromptCacheKey(options);
434
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
435
+ const { headers, copilotPremiumRequests, baseUrl } = resolveOpenAIRequestSetup(model, {
436
+ apiKey,
437
+ extraHeaders: options?.headers,
438
+ initiatorOverride: options?.initiatorOverride,
439
+ messages: context.messages,
440
+ openAISessionId: routingSessionId,
441
+ promptCacheSessionId,
442
+ });
443
+ const premiumRequestsTotal = copilotPremiumRequests;
444
+ const providerSessionState = getOpenAIResponsesProviderSessionState(model, options?.providerSessionState);
445
+ const strictToolsScope = getOpenAIStrictToolsScope(model, baseUrl);
446
+ const promptCacheBreakpointPolicy =
447
+ resolveCacheRetention(options?.cacheRetention) !== "none" && options?.promptCache?.mode === "explicit"
448
+ ? (options.promptCache.breakpoint ?? "latest-stable-message")
449
+ : undefined;
450
+ if (isOpenAIResponsesStatefulEnabled(options, baseUrl) && routingSessionId && providerSessionState) {
451
+ chainState = getOpenAIResponsesChainState(providerSessionState, model, baseUrl, routingSessionId);
452
+ if (chainState.canAppend && chainState.lastPromptCacheBreakpointPolicy !== promptCacheBreakpointPolicy) {
453
+ resetOpenAIResponsesChainState(chainState);
454
+ }
455
+ }
456
+ const builtParams = buildParams(
457
+ model,
458
+ context,
459
+ options,
460
+ providerSessionState,
461
+ strictToolsScope,
462
+ false,
463
+ chainState?.canAppend ? chainState.lastParams?.input : undefined,
464
+ );
465
+ const params = builtParams.params;
466
+ let activeParams = params;
467
+ const resolvedBaseUrl = (baseUrl ?? "https://api.openai.com/v1").replace(/\/+$/, "");
468
+ const requestReasoningEffortFallbacks = new Map<string, OpenAIReasoningEffortFallback>();
469
+ const attemptedReasoningEffortFallbacks = new Set<string>();
470
+ let pendingReasoningEffortFallback: { key: string; fallback: OpenAIReasoningEffortFallback } | undefined;
471
+ let activeReasoningEffortFallbackKey: string | undefined;
472
+ let activeRequestParams: OpenAIResponsesSamplingParams | undefined;
473
+ const applyReasoningEffortFallbackForRequest = (requestParams: OpenAIResponsesSamplingParams): string => {
474
+ const fallbackKey = createOpenAIReasoningEffortFallbackKey(
475
+ "responses",
476
+ resolvedBaseUrl,
477
+ typeof requestParams.model === "string" ? requestParams.model : model.id,
478
+ );
479
+ const requestReasoningEffortFallback = requestReasoningEffortFallbacks.has(fallbackKey)
480
+ ? requestReasoningEffortFallbacks.get(fallbackKey)
481
+ : getOpenAIReasoningEffortFallback(providerSessionState, fallbackKey);
482
+ if (requestReasoningEffortFallback !== undefined) {
483
+ applyOpenAIReasoningEffortFallback(requestParams, requestReasoningEffortFallback);
484
+ }
485
+ return fallbackKey;
486
+ };
487
+ if (chainState && !chainState.disabled) {
488
+ // Platform `previous_response_id` chaining only resolves stored responses.
489
+ params.store = true;
490
+ }
491
+ applyReasoningEffortFallbackForRequest(params);
492
+ let chained: OpenAIResponsesChainedParams =
493
+ chainState && !chainState.disabled ? buildOpenAIResponsesChainedParams(params, chainState) : { params };
494
+ sentPreviousResponseId = chained.previousResponseId;
495
+ const idleTimeoutMs =
496
+ options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs(model.compat.streamIdleTimeoutMs);
497
+ const firstEventTimeoutMs =
498
+ options?.streamFirstEventTimeoutMs ??
499
+ getOpenAIStreamFirstEventTimeoutMs(idleTimeoutMs, model.compat.streamFirstEventTimeoutMs);
500
+ const requestTimeoutMs =
501
+ firstEventTimeoutMs !== undefined && firstEventTimeoutMs > 0 ? firstEventTimeoutMs : undefined;
502
+ const requestUrl = `${resolvedBaseUrl}/responses`;
503
+ const applyPayloadReplacement = async (requestParams: OpenAIResponsesSamplingParams) => {
504
+ const replacementPayload = await options?.onPayload?.(requestParams, model);
505
+ const payload =
506
+ replacementPayload !== undefined ? (replacementPayload as OpenAIResponsesSamplingParams) : requestParams;
507
+ applyReasoningEffortFallbackForRequest(payload);
508
+ return payload;
509
+ };
510
+ chained = { ...chained, params: await applyPayloadReplacement(chained.params) };
511
+ const activeRawRequestDump: RawHttpRequestDump = {
512
+ provider: model.provider,
513
+ api: output.api,
514
+ model: model.id,
515
+ method: "POST",
516
+ url: requestUrl,
517
+ body: chained.params,
518
+ };
519
+ rawRequestDump = activeRawRequestDump;
520
+ const openResponsesStream = (requestParams: OpenAIResponsesSamplingParams) => {
521
+ activeReasoningEffortFallbackKey = createOpenAIReasoningEffortFallbackKey(
522
+ "responses",
523
+ resolvedBaseUrl,
524
+ typeof requestParams.model === "string" ? requestParams.model : model.id,
525
+ );
526
+ activeRequestParams = requestParams;
527
+ return callWithCopilotModelRetry(
528
+ async () => {
529
+ let requestTimeout: NodeJS.Timeout | undefined;
530
+ if (requestTimeoutMs !== undefined) {
531
+ requestTimeout = setTimeout(
532
+ () => abortTracker.abortLocally(firstEventTimeoutAbortError),
533
+ requestTimeoutMs,
534
+ );
535
+ }
536
+ try {
537
+ const headersWithTimeout = { ...headers };
538
+ if (requestTimeoutMs !== undefined) {
539
+ headersWithTimeout["X-Stainless-Timeout"] = Math.floor(requestTimeoutMs / 1000).toString();
540
+ }
541
+ const { events, response, requestId } = await postOpenAIStream<ResponseStreamEvent>({
542
+ url: requestUrl,
543
+ headers: headersWithTimeout,
544
+ body: requestParams,
545
+ signal: requestSignal,
546
+ fetch: options?.fetch,
547
+ // Transient 408/429/5xx get Retry-After-aware transport
548
+ // retries; the first-event watchdog aborts `requestSignal`,
549
+ // so retries cannot extend the caller's deadline.
550
+ onSseEvent: rawSseObserver,
551
+ });
552
+ // Disarm the first-event watchdog as soon as headers arrive — a slow
553
+ // onResponse callback must not abort an already-connected stream.
554
+ if (requestTimeout !== undefined) {
555
+ clearTimeout(requestTimeout);
556
+ requestTimeout = undefined;
557
+ }
558
+ await notifyProviderResponse(options, response, model, requestId);
559
+ return events;
560
+ } finally {
561
+ if (requestTimeout !== undefined) clearTimeout(requestTimeout);
562
+ }
563
+ },
564
+ { provider: model.provider, signal: requestSignal },
565
+ );
566
+ };
567
+ let strictRetryAvailable = true;
568
+ let activeStrictToolsApplied = builtParams.strictToolsApplied;
569
+ let forceDisableStrictTools = false;
570
+ const openResponsesStreamWithFallbacks = async (): Promise<AsyncIterable<ResponseStreamEvent>> => {
571
+ let openaiStream: AsyncIterable<ResponseStreamEvent>;
572
+ while (true) {
573
+ try {
574
+ openaiStream = await openResponsesStream(chained.params);
575
+ if (pendingReasoningEffortFallback) {
576
+ rememberOpenAIReasoningEffortFallback(
577
+ providerSessionState,
578
+ pendingReasoningEffortFallback.key,
579
+ pendingReasoningEffortFallback.fallback,
580
+ );
581
+ pendingReasoningEffortFallback = undefined;
582
+ }
583
+ break;
584
+ } catch (error) {
585
+ const capturedErrorResponse = error instanceof OpenAIHttpError ? error.captured : undefined;
586
+ const reasoningEffortFallback =
587
+ activeReasoningEffortFallbackKey && activeRequestParams && !requestSignal.aborted
588
+ ? resolveOpenAIReasoningEffortFallback(error, capturedErrorResponse, activeRequestParams, {
589
+ explicitDisable: options?.disableReasoning === true && options.reasoning === undefined,
590
+ })
591
+ : undefined;
592
+ if (reasoningEffortFallback !== undefined && activeReasoningEffortFallbackKey) {
593
+ const retryMarker = `${activeReasoningEffortFallbackKey}:${String(reasoningEffortFallback)}`;
594
+ if (attemptedReasoningEffortFallbacks.has(retryMarker)) throw error;
595
+ attemptedReasoningEffortFallbacks.add(retryMarker);
596
+ requestReasoningEffortFallbacks.set(activeReasoningEffortFallbackKey, reasoningEffortFallback);
597
+ applyOpenAIReasoningEffortFallback(chained.params, reasoningEffortFallback);
598
+ applyOpenAIReasoningEffortFallback(activeParams, reasoningEffortFallback);
599
+ activeRawRequestDump.body = chained.params;
600
+ pendingReasoningEffortFallback = {
601
+ key: activeReasoningEffortFallbackKey,
602
+ fallback: reasoningEffortFallback,
603
+ };
604
+ continue;
605
+ }
606
+ const compiledGrammarTooLarge =
607
+ isOpenRouterAnthropicModel(model) &&
608
+ isCompiledGrammarTooLargeStrictError(error, capturedErrorResponse);
609
+ const canRetryWithoutStrictTools =
610
+ strictRetryAvailable &&
611
+ !requestSignal.aborted &&
612
+ (compiledGrammarTooLarge ||
613
+ shouldRetryWithoutStrictTools(error, capturedErrorResponse, {
614
+ model,
615
+ strictToolsApplied: activeStrictToolsApplied,
616
+ tools: context.tools,
617
+ }));
618
+ if (canRetryWithoutStrictTools) {
619
+ strictRetryAvailable = false;
620
+ forceDisableStrictTools = true;
621
+ disableStrictToolsForScope(providerSessionState, strictToolsScope);
622
+ const fallbackBuilt = buildParams(
623
+ model,
624
+ context,
625
+ options,
626
+ providerSessionState,
627
+ strictToolsScope,
628
+ true,
629
+ chainState?.canAppend ? chainState.lastParams?.input : undefined,
630
+ );
631
+ const fallbackParams = fallbackBuilt.params;
632
+ if (chainState && !chainState.disabled) fallbackParams.store = true;
633
+ let fallbackChained: OpenAIResponsesChainedParams =
634
+ chainState && !chainState.disabled
635
+ ? buildOpenAIResponsesChainedParams(fallbackParams, chainState)
636
+ : { params: fallbackParams };
637
+ sentPreviousResponseId = fallbackChained.previousResponseId;
638
+ fallbackChained = {
639
+ ...fallbackChained,
640
+ params: await applyPayloadReplacement(fallbackChained.params),
641
+ };
642
+ chained = fallbackChained;
643
+ activeRawRequestDump.body = chained.params;
644
+ activeParams = fallbackParams;
645
+ activeStrictToolsApplied = fallbackBuilt.strictToolsApplied;
646
+ continue;
647
+ }
648
+ if (!chainState || !sentPreviousResponseId || requestSignal.aborted) {
649
+ throw error;
650
+ }
651
+ const zdrRejection =
652
+ error instanceof Error &&
653
+ /previous[ _]?response/i.test(error.message) &&
654
+ /zero[ _-]?data[ _-]?retention/i.test(error.message);
655
+ const isPromptBlocked =
656
+ error instanceof Error &&
657
+ ((error as { code?: string }).code === "invalid_prompt" ||
658
+ /invalid_prompt|Request blocked/i.test(error.message));
659
+ if (!zdrRejection && !isPromptBlocked && !isOpenAIResponsesStalePreviousResponseError(error)) {
660
+ throw error;
661
+ }
662
+ // Server rejected the chain baseline: reset, count the failure (or
663
+ // disable categorically on ZDR), and retry once with the full
664
+ // transcript. Structurally cannot loop — the retry carries no
665
+ // previous_response_id.
666
+ if (zdrRejection) {
667
+ markOpenAIResponsesChainZeroDataRetention(chainState, error);
668
+ // ZDR orgs cannot store responses; the retry uses `store: false`.
669
+ } else {
670
+ registerOpenAIResponsesChainStaleFailure(chainState, error);
671
+ }
672
+ sentPreviousResponseId = undefined;
673
+ const currentBuilt = buildParams(
674
+ model,
675
+ context,
676
+ options,
677
+ providerSessionState,
678
+ strictToolsScope,
679
+ forceDisableStrictTools,
680
+ );
681
+ const currentParams = currentBuilt.params;
682
+ // Only ZDR forces `store: false` (the org never persists responses). A
683
+ // non-ZDR stale baseline is transient, so keep storing: the full-context
684
+ // retry must be chainable next turn, and the consecutive stale-failure
685
+ // breaker only trips when each retry stores and the next turn re-chains.
686
+ currentParams.store = !zdrRejection;
687
+ const retryParams = await applyPayloadReplacement(currentParams);
688
+ chained = { params: retryParams };
689
+ activeRawRequestDump.body = retryParams;
690
+ activeParams = currentParams;
691
+ activeStrictToolsApplied = currentBuilt.strictToolsApplied;
692
+ }
693
+ }
694
+ return openaiStream;
695
+ };
696
+ let openaiStream = await openResponsesStreamWithFallbacks();
697
+ if (premiumRequestsTotal !== undefined) output.usage.premiumRequests = premiumRequestsTotal;
698
+ stream.push({ type: "start", partial: output });
699
+
700
+ const nativeOutputItems: Array<Record<string, unknown>> = [];
701
+ let transientStreamRetryAttempt = 0;
702
+ while (true) {
703
+ let sawReplayUnsafeOutput = false;
704
+ let sawTerminalResponseEvent = false;
705
+ const attemptStream = new AssistantMessageEventStream();
706
+ let forwardAttemptLive = false;
707
+ const forwardAttemptEvents = () => {
708
+ for (const event of attemptStream.queue) stream.push(event);
709
+ attemptStream.queue.length = 0;
710
+ };
711
+ nativeOutputItems.length = 0;
712
+ const timedOpenaiStream = iterateWithIdleTimeout(openaiStream, {
713
+ idleTimeoutMs,
714
+ firstItemTimeoutMs: firstEventTimeoutMs,
715
+ firstItemErrorMessage: OPENAI_RESPONSES_FIRST_EVENT_TIMEOUT_MESSAGE,
716
+ errorMessage: "OpenAI responses stream stalled while waiting for the next event",
717
+ onFirstItemTimeout: () => abortTracker.abortLocally(firstEventTimeoutAbortError),
718
+ onIdle: () => requestAbortController.abort(),
719
+ abortSignal: options?.signal,
720
+ isProgressItem: isOpenAIResponsesProgressEvent,
721
+ });
722
+ const observedOpenaiStream = (async function* (): AsyncGenerator<ResponseStreamEvent> {
723
+ for await (const event of timedOpenaiStream) {
724
+ if (isOpenAIResponsesReplayUnsafeEvent(event)) {
725
+ sawReplayUnsafeOutput = true;
726
+ if (!forwardAttemptLive) {
727
+ forwardAttemptEvents();
728
+ forwardAttemptLive = true;
729
+ }
730
+ }
731
+ yield event;
732
+ if (forwardAttemptLive) forwardAttemptEvents();
733
+ }
734
+ })();
735
+
736
+ try {
737
+ await processResponsesStream(observedOpenaiStream, output, attemptStream, model, {
738
+ onFirstToken: () => {
739
+ if (!firstTokenTime) firstTokenTime = performance.now();
740
+ },
741
+ onOutputItemDone: item => {
742
+ // `processResponsesStream` hands over a private clone already; no
743
+ // second deep copy needed (reasoning items carry multi-KB blobs).
744
+ nativeOutputItems.push(item as unknown as Record<string, unknown>);
745
+ },
746
+ onCompleted: () => {
747
+ sawTerminalResponseEvent = true;
748
+ },
749
+ requestServiceTier: options?.serviceTier,
750
+ });
751
+
752
+ const localAbortReason = abortTracker.getLocalAbortReason();
753
+ if (localAbortReason) throw localAbortReason;
754
+ if (abortTracker.wasCallerAbort()) throw new AIError.AbortError();
755
+
756
+ // Detect premature stream closure: the HTTP stream ended without the
757
+ // provider sending a recognized terminal response event.
758
+ if (!sawTerminalResponseEvent) {
759
+ throw new AIError.ProviderResponseError(
760
+ "OpenAI responses stream closed before a terminal response event was received",
761
+ { provider: model.provider, kind: "incomplete-stream" },
762
+ );
763
+ }
764
+
765
+ if (output.stopReason === "aborted" || output.stopReason === "error") {
766
+ throw new AIError.ProviderResponseError(output.errorMessage ?? "An unknown error occurred", {
767
+ provider: model.provider,
768
+ kind: "runtime",
769
+ });
770
+ }
771
+ forwardAttemptEvents();
772
+ break;
773
+ } catch (error) {
774
+ const streamFailure = abortTracker.getLocalAbortReason() ?? error;
775
+ const canRetry =
776
+ !sawReplayUnsafeOutput &&
777
+ !requestSignal.aborted &&
778
+ !abortTracker.wasCallerAbort() &&
779
+ transientStreamRetryAttempt < OPENAI_RESPONSES_MAX_TRANSIENT_STREAM_RETRIES &&
780
+ isRetryableOpenAIResponsesStreamFailure(streamFailure);
781
+ if (!canRetry) {
782
+ forwardAttemptEvents();
783
+ throw streamFailure;
784
+ }
785
+
786
+ transientStreamRetryAttempt++;
787
+ logger.debug("OpenAI responses stream ended before replay-unsafe output; retrying", {
788
+ provider: model.provider,
789
+ model: model.id,
790
+ attempt: transientStreamRetryAttempt,
791
+ error: streamFailure instanceof Error ? streamFailure.message : String(streamFailure),
792
+ });
793
+ const retryOutput = createInitialResponsesAssistantMessage(model.api, model.provider, model.id);
794
+ output.content.length = 0;
795
+ output.responseId = undefined;
796
+ output.upstreamProvider = undefined;
797
+ output.errorMessage = undefined;
798
+ output.errorStatus = undefined;
799
+ output.errorId = undefined;
800
+ output.stopDetails = undefined;
801
+ output.providerPayload = undefined;
802
+ output.usage = retryOutput.usage;
803
+ if (premiumRequestsTotal !== undefined) output.usage.premiumRequests = premiumRequestsTotal;
804
+ output.stopReason = "stop";
805
+ output.duration = undefined;
806
+ output.ttft = undefined;
807
+ firstTokenTime = undefined;
808
+ nativeOutputItems.length = 0;
809
+
810
+ if (options?.providerRetryWait) {
811
+ await options.providerRetryWait(OPENAI_RESPONSES_TRANSIENT_STREAM_RETRY_DELAY_MS, options.signal);
812
+ } else {
813
+ await scheduler.wait(OPENAI_RESPONSES_TRANSIENT_STREAM_RETRY_DELAY_MS, { signal: options?.signal });
814
+ }
815
+ if (abortTracker.wasCallerAbort()) throw new AIError.AbortError();
816
+ openaiStream = await openResponsesStreamWithFallbacks();
817
+ }
818
+ }
819
+
820
+ output.providerPayload = createOpenAIResponsesHistoryPayload(model.provider, nativeOutputItems);
821
+ const replayableResponseItems = sanitizeOpenAIResponsesAssistantHistoryItemsForReplay(
822
+ structuredCloneJSON(nativeOutputItems),
823
+ );
824
+ if (replayableResponseItems) {
825
+ if (providerSessionState) providerSessionState.nativeHistoryReplayWarmed = true;
826
+ if (chainState) {
827
+ chainState.lastParams = structuredCloneJSON(activeParams);
828
+ chainState.lastPromptCacheBreakpointPolicy = promptCacheBreakpointPolicy;
829
+ if (output.responseId) {
830
+ chainState.lastResponseId = output.responseId;
831
+ chainState.lastResponseItems = replayableResponseItems;
832
+ chainState.canAppend = true;
833
+ // Only a successful CHAINED completion clears the stale counter — a
834
+ // full-context success must not mask categorical rejection.
835
+ if (sentPreviousResponseId) chainState.staleFailures = 0;
836
+ } else {
837
+ // Without a response id the append baseline cannot be trusted.
838
+ chainState.canAppend = false;
839
+ }
840
+ }
841
+ } else if (chainState) {
842
+ // Hidden-empty / fully sanitized successes cannot be used as an append
843
+ // baseline, but `lastParams` still records the successful wire controls
844
+ // without re-enabling `previous_response_id` chaining.
845
+ chainState.canAppend = false;
846
+ chainState.lastParams = structuredCloneJSON(activeParams);
847
+ chainState.lastPromptCacheBreakpointPolicy = promptCacheBreakpointPolicy;
848
+ chainState.lastResponseId = undefined;
849
+ chainState.lastResponseItems = undefined;
850
+ }
851
+
852
+ output.duration = performance.now() - startTime;
853
+ if (firstTokenTime) output.ttft = firstTokenTime - startTime;
854
+ stream.push({ type: "done", reason: output.stopReason, message: output });
855
+ stream.end();
856
+ } catch (error) {
857
+ if (chainState) resetOpenAIResponsesChainState(chainState);
858
+ const capturedErrorResponse = error instanceof OpenAIHttpError ? error.captured : undefined;
859
+ const result = await AIError.finalize(error, {
860
+ api: model.api,
861
+ provider: model.provider,
862
+ abortTracker,
863
+ rawRequestDump,
864
+ capturedErrorResponse,
865
+ });
866
+ output.stopReason = result.stopReason;
867
+ output.errorStatus = result.status;
868
+ output.errorId = result.id;
869
+ output.errorMessage = result.message;
870
+ // Some providers via OpenRouter include extra details here.
871
+ const rawMetadata = (error as { error?: { metadata?: { raw?: string } } })?.error?.metadata?.raw;
872
+ if (rawMetadata) output.errorMessage += `\n${rawMetadata}`;
873
+ output.duration = performance.now() - startTime;
874
+ if (firstTokenTime) output.ttft = firstTokenTime - startTime;
875
+ stream.push({ type: "error", reason: output.stopReason, error: output });
876
+ stream.end();
877
+ }
878
+ })();
879
+
880
+ return stream;
881
+ };
882
+
883
+ /**
884
+ * Public entry: wrap the single-attempt Responses streamer with bounded
885
+ * empty-completion retries — a `response.completed` carrying no content/usage
886
+ * would otherwise stall the agent loop. Shared with the OpenAI-completions and
887
+ * Anthropic providers via `withEmptyCompletionRetry`.
888
+ */
889
+ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (model, context, options) =>
890
+ withEmptyCompletionRetry(model, context, options, streamOpenAIResponsesOnce);
891
+
892
+ function isOfficialOpenAIResponsesEndpoint(model: Model<"openai-responses">): boolean {
893
+ if (model.provider !== "openai") return false;
894
+ if (!model.baseUrl) return true;
895
+ try {
896
+ return new URL(model.baseUrl).hostname === "api.openai.com";
897
+ } catch {
898
+ return false;
899
+ }
900
+ }
901
+
902
+ function isResponsesPromptCacheableContentBlock(block: unknown): block is ResponseInputContent {
903
+ if (typeof block !== "object" || block === null || !("type" in block)) return false;
904
+ return block.type === "input_text" || block.type === "input_image" || block.type === "input_file";
905
+ }
906
+
907
+ type ResponsesPromptCacheableMessage = {
908
+ role: "assistant" | "developer" | "system" | "user";
909
+ content: ResponseInputContent[];
910
+ };
911
+
912
+ function isResponsesPromptCacheableMessage(item: unknown): item is ResponsesPromptCacheableMessage {
913
+ if (typeof item !== "object" || item === null || !("role" in item) || !("content" in item)) return false;
914
+ if (item.role !== "assistant" && item.role !== "developer" && item.role !== "system" && item.role !== "user")
915
+ return false;
916
+ return Array.isArray(item.content) && item.content.every(isResponsesPromptCacheableContentBlock);
917
+ }
918
+
919
+ type ResponsesStringInstruction = {
920
+ role: "developer" | "system";
921
+ content: string;
922
+ };
923
+
924
+ function isStableStringResponsesInstruction(item: unknown): item is ResponsesStringInstruction {
925
+ if (typeof item !== "object" || item === null || !("role" in item) || !("content" in item)) return false;
926
+ return (
927
+ (item.role === "developer" || item.role === "system") &&
928
+ typeof item.content === "string" &&
929
+ item.content.length > 0
930
+ );
931
+ }
932
+
933
+ function matchesResponsesCacheBaseline(
934
+ baseline: ResponsesPromptCacheableMessage,
935
+ current: ResponsesPromptCacheableMessage,
936
+ ): boolean {
937
+ if (baseline.role !== current.role || baseline.content.length !== current.content.length) return false;
938
+ for (let index = 0; index < baseline.content.length; index++) {
939
+ const baselineBlock = baseline.content[index];
940
+ const currentBlock = current.content[index];
941
+ if (!baselineBlock || !currentBlock) return false;
942
+ const breakpoint = baselineBlock.prompt_cache_breakpoint;
943
+ if (breakpoint) {
944
+ if (!Bun.deepEquals(baselineBlock, { ...currentBlock, prompt_cache_breakpoint: breakpoint })) return false;
945
+ } else if (!Bun.deepEquals(baselineBlock, currentBlock)) {
946
+ return false;
947
+ }
948
+ }
949
+ return true;
950
+ }
951
+
952
+ function restoreResponsesCacheBreakpointsFromBaseline(
953
+ input: ResponseInput | undefined,
954
+ baseline: ResponseInput | undefined,
955
+ ): boolean {
956
+ if (!input || !baseline) return false;
957
+ let restored = false;
958
+ for (let i = 0; i < baseline.length && i < input.length; i++) {
959
+ const baselineMessage = baseline[i];
960
+ const message = input[i];
961
+ if (!isResponsesPromptCacheableMessage(baselineMessage)) continue;
962
+
963
+ if (isStableStringResponsesInstruction(message)) {
964
+ const [baselineBlock] = baselineMessage.content;
965
+ if (
966
+ baselineMessage.role === message.role &&
967
+ baselineBlock?.type === "input_text" &&
968
+ baselineBlock.text === message.content &&
969
+ baselineBlock.prompt_cache_breakpoint
970
+ ) {
971
+ Object.assign(message, {
972
+ content: [
973
+ {
974
+ type: "input_text",
975
+ text: message.content,
976
+ prompt_cache_breakpoint: baselineBlock.prompt_cache_breakpoint,
977
+ },
978
+ ],
979
+ });
980
+ restored = true;
981
+ }
982
+ continue;
983
+ }
984
+
985
+ if (!isResponsesPromptCacheableMessage(message) || !matchesResponsesCacheBaseline(baselineMessage, message))
986
+ continue;
987
+ for (let j = 0; j < baselineMessage.content.length; j++) {
988
+ const baselineBlock = baselineMessage.content[j];
989
+ const block = message.content[j];
990
+ if (!baselineBlock?.prompt_cache_breakpoint || !block) continue;
991
+ Object.assign(block, { prompt_cache_breakpoint: baselineBlock.prompt_cache_breakpoint });
992
+ restored = true;
993
+ }
994
+ }
995
+ return restored;
996
+ }
997
+
998
+ function hasResponsesCacheBreakpoint(input: ResponseInput | undefined): boolean {
999
+ return (
1000
+ input?.some(
1001
+ message =>
1002
+ isResponsesPromptCacheableMessage(message) &&
1003
+ message.content.some(block => block.prompt_cache_breakpoint !== undefined),
1004
+ ) ?? false
1005
+ );
1006
+ }
1007
+
1008
+ function markLatestStableResponsesCacheBreakpoint(
1009
+ input: ResponseInput | undefined,
1010
+ statefulBaseline?: ResponseInput,
1011
+ ): boolean {
1012
+ if (!input) return false;
1013
+ // Stateful appends use a strict wire-prefix comparison. Retain the exact
1014
+ // marker from that prefix rather than recomputing a newer boundary.
1015
+ if (statefulBaseline) {
1016
+ if (restoreResponsesCacheBreakpointsFromBaseline(input, statefulBaseline)) return true;
1017
+ // A prior marker whose content no longer matches means chaining will
1018
+ // reset to a full replay. Recompute a fresh boundary for that replay.
1019
+ // Markerless baselines stay markerless so appends do not mutate them.
1020
+ if (!hasResponsesCacheBreakpoint(statefulBaseline)) return false;
1021
+ }
1022
+
1023
+ let latestInputMessage = -1;
1024
+ for (let i = input.length - 1; i >= 0; i--) {
1025
+ const message = input[i];
1026
+ if (!("role" in message)) continue;
1027
+ if (message.role === "user" || message.role === "developer") {
1028
+ latestInputMessage = i;
1029
+ break;
1030
+ }
1031
+ }
1032
+ if (latestInputMessage <= 0) return false;
1033
+
1034
+ for (let i = latestInputMessage - 1; i >= 0; i--) {
1035
+ const message = input[i];
1036
+ if (isStableStringResponsesInstruction(message)) {
1037
+ const text = message.content;
1038
+ Object.assign(message, {
1039
+ content: [
1040
+ {
1041
+ type: "input_text",
1042
+ text,
1043
+ prompt_cache_breakpoint: { mode: "explicit" },
1044
+ },
1045
+ ],
1046
+ });
1047
+ return true;
1048
+ }
1049
+ if (!isResponsesPromptCacheableMessage(message)) continue;
1050
+ for (let j = message.content.length - 1; j >= 0; j--) {
1051
+ const block = message.content[j];
1052
+ if (!isResponsesPromptCacheableContentBlock(block)) continue;
1053
+ Object.assign(block, { prompt_cache_breakpoint: { mode: "explicit" } });
1054
+ return true;
1055
+ }
1056
+ }
1057
+ return false;
1058
+ }
1059
+
1060
+ function applyOpenAIResponsesPromptCachePolicy(
1061
+ params: OpenAIResponsesSamplingParams,
1062
+ model: Model<"openai-responses">,
1063
+ options: OpenAIResponsesOptions | undefined,
1064
+ statefulCacheBaseline?: ResponseInput,
1065
+ ): void {
1066
+ const promptCache = options?.promptCache;
1067
+ if (!promptCache || resolveCacheRetention(options?.cacheRetention) === "none") return;
1068
+ if (!model.compat.supportsPromptCacheBreakpoints) {
1069
+ if (promptCache.mode === "explicit") {
1070
+ throw new AIError.ConfigurationError(
1071
+ `OpenAI explicit prompt caching is unsupported for ${model.provider}/${model.id}; enable compat.supportsPromptCacheBreakpoints only for a compatible endpoint.`,
1072
+ );
1073
+ }
1074
+ return;
1075
+ }
1076
+
1077
+ params.prompt_cache_options = {
1078
+ mode: promptCache.mode,
1079
+ ttl: promptCache.ttl ?? model.compat.promptCacheBreakpointTtl,
1080
+ };
1081
+ if (promptCache.mode === "explicit" && promptCache.breakpoint !== "none")
1082
+ markLatestStableResponsesCacheBreakpoint(params.input, statefulCacheBaseline);
1083
+ }
1084
+
1085
+ export function buildParams(
1086
+ model: Model<"openai-responses">,
1087
+ context: Context,
1088
+ options: OpenAIResponsesOptions | undefined,
1089
+ providerSessionState: OpenAIResponsesProviderSessionState | undefined,
1090
+ strictToolsScope?: OpenAIStrictToolsScope,
1091
+ disableStrictToolsOverride = false,
1092
+ statefulCacheBaseline?: ResponseInput,
1093
+ ): { params: OpenAIResponsesSamplingParams; strictToolsApplied: boolean } {
1094
+ const policy = resolveOpenAICompatPolicy(model, {
1095
+ endpoint: "responses",
1096
+ reasoning: options?.reasoning,
1097
+ disableReasoning: options?.disableReasoning,
1098
+ toolChoice: options?.toolChoice,
1099
+ strictResponsesPairing: options?.strictResponsesPairing,
1100
+ includeEncryptedReasoning: options?.includeEncryptedReasoning,
1101
+ filterReasoningHistory: options?.filterReasoningHistory,
1102
+ omitReasoningEffort: options?.omitReasoningEffort,
1103
+ });
1104
+ const strictResponsesPairing = policy.tools.strictResponsesPairing;
1105
+ const shouldReplayNativeHistory = providerSessionState?.nativeHistoryReplayWarmed ?? true;
1106
+ const messages = buildResponsesInput({
1107
+ model,
1108
+ context,
1109
+ strictResponsesPairing,
1110
+ supportsImageDetailOriginal: model.compat.supportsImageDetailOriginal,
1111
+ nativeHistory: {
1112
+ replay: shouldReplayNativeHistory,
1113
+ filterReasoning: policy.reasoning.filterReasoningHistory,
1114
+ },
1115
+ includeThinkingSignatures: shouldReplayNativeHistory && !policy.reasoning.filterReasoningHistory,
1116
+ repairOrphanOutputs: true,
1117
+ });
1118
+
1119
+ const systemPrompts = normalizeSystemPrompts(context.systemPrompt);
1120
+ let systemInstructions: string | undefined;
1121
+ if (systemPrompts.length > 0) {
1122
+ const needsDeveloperRole = policy.messages.systemRole === "developer";
1123
+ if (needsDeveloperRole) {
1124
+ // Reasoning models on known OpenAI-compatible endpoints require the
1125
+ // `developer` role. Send all system prompts inline in `input`.
1126
+ messages.unshift(
1127
+ ...systemPrompts.map(systemPrompt => ({ role: "developer" as const, content: systemPrompt })),
1128
+ );
1129
+ } else {
1130
+ // All other endpoints (including third-party /v1/responses proxies) use
1131
+ // the canonical top-level `instructions` field so that proxies that
1132
+ // reject `input[{role:"system"}]` work out of the box.
1133
+ systemInstructions = systemPrompts.join("\n\n");
1134
+ }
1135
+ }
1136
+
1137
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention);
1138
+ const promptCacheKey = getOpenAIPromptCacheKey(options);
1139
+ const modelId = applyWireModelIdTransform(
1140
+ model.requestModelId ?? model.id,
1141
+ model.compat.wireModelIdMode,
1142
+ options?.openrouterVariant,
1143
+ );
1144
+ const params: OpenAIResponsesSamplingParams = {
1145
+ model: modelId,
1146
+ input: messages,
1147
+ instructions: systemInstructions,
1148
+ stream: true,
1149
+ prompt_cache_key: promptCacheKey,
1150
+ prompt_cache_retention: promptCacheKey
1151
+ ? cacheRetention === "long" && model.compat.supportsLongPromptCacheRetention
1152
+ ? "24h"
1153
+ : undefined
1154
+ : undefined,
1155
+ // Gateway routing: OpenRouter-only Responses wire field for sticky upstream
1156
+ // routing + observability grouping; no equivalent on direct OpenAI.
1157
+ session_id: model.compat.isOpenRouterHost ? getOpenRouterResponsesSessionId(options) : undefined,
1158
+ store: false,
1159
+ stream_options: model.compat.supportsObfuscationOptOut ? { include_obfuscation: false } : undefined,
1160
+ };
1161
+ if (options?.include?.length) params.include = Array.from(new Set(options.include));
1162
+ maybeAddOpenRouterAnthropicCacheControl(params, model, cacheRetention);
1163
+ const outputToken = resolveOpenAIOutputTokenParam({
1164
+ field: "max_output_tokens",
1165
+ maxTokens: options?.maxTokens,
1166
+ maxTokensExplicit: options?.maxTokensExplicit ?? options?.maxTokens !== undefined,
1167
+ modelMaxTokens: model.maxTokens,
1168
+ omitMaxOutputTokens: model.omitMaxOutputTokens ?? false,
1169
+ isOpenRouterHost: model.compat.isOpenRouterHost,
1170
+ alwaysSendMaxTokens: model.compat.alwaysSendMaxTokens,
1171
+ providerOutputClamp: resolveOpenAIResponsesOutputClamp(model),
1172
+ });
1173
+
1174
+ applyCommonResponsesSamplingParams(params, { ...options, maxTokens: outputToken?.value }, model);
1175
+ if (options?.textVerbosity && isOfficialOpenAIResponsesEndpoint(model)) {
1176
+ params.text = { ...params.text, verbosity: options.textVerbosity };
1177
+ }
1178
+ // TODO: openai responses has no top-level `stop`/`stop_sequences`; surface via reasoning.stop?
1179
+ // `StreamOptions.stopSequences` is intentionally dropped for this provider.
1180
+ // TODO: openai responses has no top-level `frequency_penalty` field as of the current SDK;
1181
+ // `StreamOptions.frequencyPenalty` is intentionally dropped for this provider.
1182
+
1183
+ let strictToolsApplied = false;
1184
+ if (context.tools) {
1185
+ const disableStrictTools =
1186
+ disableStrictToolsOverride || isStrictToolsDisabledForScope(providerSessionState, strictToolsScope);
1187
+ const strictMode = !disableStrictTools && model.compat.supportsStrictMode !== false;
1188
+ params.tools = convertTools(context.tools, strictMode, model);
1189
+ strictToolsApplied = params.tools.some(t => (t as { strict?: boolean }).strict === true);
1190
+ if (options?.toolChoice) {
1191
+ // Map tool_choice against the tools that survived quarantine, not the
1192
+ // original list: a forced choice for a dropped tool — or "required" when
1193
+ // every tool was dropped — would otherwise send a tool_choice with no
1194
+ // matching tool, which the provider rejects just like the bad schema did (#2652).
1195
+ const emittedNames = new Set(
1196
+ params.tools.map(t => (t as { name?: string }).name).filter((n): n is string => n !== undefined),
1197
+ );
1198
+ const emittedComputer = params.tools.some(tool => tool.type === "computer");
1199
+ const survivingTools =
1200
+ params.tools.length === context.tools.length
1201
+ ? context.tools
1202
+ : context.tools.filter(
1203
+ t =>
1204
+ emittedNames.has(t.customWireName ?? t.name) ||
1205
+ (t.native?.type === "computer" && emittedComputer),
1206
+ );
1207
+ const toolChoice = mapOpenAIResponsesToolChoiceForTools(options.toolChoice, survivingTools, model);
1208
+ if (toolChoice !== undefined && params.tools.length > 0) {
1209
+ if (
1210
+ typeof toolChoice === "object" &&
1211
+ toolChoice.type === "function" &&
1212
+ !model.compat.supportsNamedToolChoice
1213
+ ) {
1214
+ // String-only hosts cannot receive the named object. Restrict the
1215
+ // catalogue first so "required" still forces the requested tool.
1216
+ params.tools = params.tools.filter(tool => tool.type === "function" && tool.name === toolChoice.name);
1217
+ params.tool_choice = "required";
1218
+ } else {
1219
+ params.tool_choice = toolChoice;
1220
+ }
1221
+ }
1222
+ }
1223
+ }
1224
+
1225
+ const reasoningPolicy = resolveOpenAICompatPolicy(model, {
1226
+ endpoint: "responses",
1227
+ reasoning: options?.reasoning,
1228
+ disableReasoning: options?.disableReasoning,
1229
+ toolChoice: params.tool_choice,
1230
+ strictResponsesPairing: options?.strictResponsesPairing,
1231
+ includeEncryptedReasoning: options?.includeEncryptedReasoning,
1232
+ filterReasoningHistory: options?.filterReasoningHistory,
1233
+ omitReasoningEffort: options?.omitReasoningEffort,
1234
+ });
1235
+ const reasoningSummary =
1236
+ model.provider === "xai-oauth"
1237
+ ? options?.reasoning === undefined
1238
+ ? undefined
1239
+ : null
1240
+ : options?.reasoningSummary;
1241
+ applyResponsesCompatPolicy(params, reasoningPolicy, {
1242
+ reasoningSummary,
1243
+ mapEffort: effort =>
1244
+ model.compat.reasoningEffortMap?.[effort as NonNullable<OpenAIResponsesOptions["reasoning"]>] ??
1245
+ model.thinking?.effortMap?.[effort as NonNullable<OpenAIResponsesOptions["reasoning"]>] ??
1246
+ effort,
1247
+ });
1248
+ // Catalog pro aliases (`gpt-5.6-*-pro`): merge AFTER the compat policy so the
1249
+ // mode survives every policy branch (disabled/omitted effort included) while
1250
+ // keeping whatever effort/summary the policy produced — mode and effort are
1251
+ // independent wire fields.
1252
+ if (model.reasoningMode) {
1253
+ params.reasoning = { ...params.reasoning, mode: model.reasoningMode };
1254
+ }
1255
+
1256
+ if (model.compat.isVercelGatewayHost) {
1257
+ applyVercelResponsesCacheControls(params, model.compat, cacheRetention);
1258
+ } else {
1259
+ applyOpenAIGatewayRouting(params, model.compat);
1260
+ }
1261
+
1262
+ applyOpenAIExtraBody(params, options?.extraBody);
1263
+ applyOpenAIResponsesPromptCachePolicy(params, model, options, statefulCacheBaseline);
1264
+
1265
+ return { params, strictToolsApplied };
1266
+ }
1267
+
1268
+ /**
1269
+ * Whether this model should get the OpenAI custom-tool grammar variant
1270
+ * for `apply_patch`. The generated model catalog sets
1271
+ * `model.applyPatchToolType` for first-party GPT-5 Responses models; this
1272
+ * runtime path only consumes that metadata.
1273
+ * @internal Exported for tests.
1274
+ */
1275
+ export function supportsFreeformApplyPatch(
1276
+ model: Model<"openai-responses" | "azure-openai-responses" | "openai-codex-responses">,
1277
+ ): boolean {
1278
+ return model.applyPatchToolType === "freeform";
1279
+ }
1280
+
1281
+ /** @internal Exported for tests. */
1282
+ export function mapOpenAIResponsesToolChoiceForTools(
1283
+ choice: ToolChoice | undefined,
1284
+ tools: Tool[],
1285
+ model: Model<"openai-responses">,
1286
+ ): OpenAIResponsesToolChoice {
1287
+ if (!model.compat.supportsToolChoice) return undefined;
1288
+ if (isForcedToolChoice(choice) && !model.compat.supportsForcedToolChoice) {
1289
+ return "auto";
1290
+ }
1291
+ if (typeof choice !== "string" && choice?.type === "computer") {
1292
+ const computer = tools.find(tool => tool.native?.type === "computer");
1293
+ if (!computer) return undefined;
1294
+ return model.supportsComputerUse === true ? { type: "computer" } : { type: "function", name: computer.name };
1295
+ }
1296
+ const mapped = mapToOpenAIResponsesToolChoice(choice);
1297
+ if (!mapped || typeof mapped === "string" || mapped.type !== "function") {
1298
+ return mapped;
1299
+ }
1300
+
1301
+ const directTool = tools.find(tool => tool.name === mapped.name);
1302
+ const customTool = supportsFreeformApplyPatch(model)
1303
+ ? tools.find(tool => tool.customFormat && (tool.name === mapped.name || tool.customWireName === mapped.name))
1304
+ : undefined;
1305
+ const offeredTool = customTool ?? directTool;
1306
+ if (offeredTool?.native?.type === "computer") {
1307
+ return model.supportsComputerUse === true ? { type: "computer" } : { type: "function", name: offeredTool.name };
1308
+ }
1309
+ if (!offeredTool) {
1310
+ return undefined;
1311
+ }
1312
+ return customTool ? { type: "custom", name: customTool.customWireName ?? customTool.name } : mapped;
1313
+ }
1314
+
1315
+ /** @internal Exported for tests. */
1316
+ export function convertTools(
1317
+ tools: Tool[],
1318
+ strictMode: boolean,
1319
+ model: Model<"openai-responses" | "azure-openai-responses" | "openai-codex-responses">,
1320
+ onQuarantine: (toolName: string, schemaPath: string) => void = (toolName, schemaPath) =>
1321
+ logger.warn(
1322
+ `Tool "${toolName}" omitted from the openai-responses request: its parameter schema is invalid for this provider at ${schemaPath} (an enum/const value cannot match its declared type). Other tools are unaffected.`,
1323
+ ),
1324
+ ): OpenAITool[] {
1325
+ const allowFreeform = supportsFreeformApplyPatch(model);
1326
+ const out: OpenAITool[] = [];
1327
+ for (const tool of tools) {
1328
+ if (tool.native?.type === "computer" && model.supportsComputerUse === true) {
1329
+ out.push({ type: "computer" });
1330
+ continue;
1331
+ }
1332
+ // Models without native computer support fall through and receive the
1333
+ // tool as a plain function tool (name/description/schema below), so
1334
+ // function-calling models can still drive the desktop.
1335
+ if (allowFreeform && tool.customFormat) {
1336
+ out.push({
1337
+ type: "custom",
1338
+ // Tool advertises its wire-level name (e.g. `apply_patch`) — the
1339
+ // agent-loop dispatcher will match incoming calls by either the
1340
+ // internal `name` or `customWireName`.
1341
+ name: tool.customWireName ?? tool.name,
1342
+ description: tool.description || "",
1343
+ format: {
1344
+ type: "grammar",
1345
+ syntax: tool.customFormat.syntax,
1346
+ definition: compactGrammarDefinition(tool.customFormat.syntax, tool.customFormat.definition),
1347
+ },
1348
+ } as unknown as OpenAITool);
1349
+ continue;
1350
+ }
1351
+ const strict = !NO_STRICT && strictMode && tool.strict !== false;
1352
+ const baseParameters = toolWireSchema(tool);
1353
+ // MFJS must run AFTER the Responses sanitizer: the sanitizer normalizes
1354
+ // `{}` → `true` (issue #1179), and Moonshot's validator rejects boolean
1355
+ // subschemas ("property schema … must be an object"), so the Moonshot
1356
+ // pass re-coerces them last.
1357
+ const sanitized = sanitizeSchemaForOpenAIResponses(baseParameters);
1358
+ const responseParameters =
1359
+ model.compat.toolSchemaFlavor === "moonshot-mfjs"
1360
+ ? (normalizeSchemaForMoonshot(sanitized) as Record<string, unknown>)
1361
+ : sanitized;
1362
+ const { schema: parameters, strict: effectiveStrict } = adaptSchemaForStrict(responseParameters, strict);
1363
+ // Quarantine a tool whose emitted schema carries a provider-rejecting
1364
+ // enum/const-vs-type contradiction: dropping just that tool keeps the rest
1365
+ // of the request valid instead of letting one bad MCP schema 400 the whole
1366
+ // turn (#2652). Other tools and built-ins are unaffected.
1367
+ const violation = findStrictToolSchemaViolation(parameters);
1368
+ if (violation) {
1369
+ onQuarantine(tool.name, violation);
1370
+ continue;
1371
+ }
1372
+ out.push({
1373
+ type: "function",
1374
+ name: tool.name,
1375
+ description: tool.description || "",
1376
+ parameters,
1377
+ // `strict: false` and an omitted `strict` are NOT equivalent for every
1378
+ // OpenAI-compat backend — some over-fill optional args when the flag is
1379
+ // absent (#4336). Preserve the author's explicit `false` unless the
1380
+ // provider is explicitly known not to understand the field
1381
+ // (`supportsStrictMode: false`) or the strict-schema fallback is
1382
+ // active — both paths rely on a uniformly absent wire flag. Mirrors the
1383
+ // `supportsStrictMode !== false` gate used by openai-completions
1384
+ // (#4527).
1385
+ ...(effectiveStrict
1386
+ ? { strict: true }
1387
+ : !NO_STRICT && strictMode && tool.strict === false
1388
+ ? { strict: false }
1389
+ : {}),
1390
+ } as OpenAITool);
1391
+ }
1392
+ return out;
1393
+ }