@prometheus-ai/ai 0.5.4 → 0.5.8

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 (377) hide show
  1. package/dist/types/auth-broker/remote-store.d.ts +2 -1
  2. package/dist/types/auth-broker/wire-schemas.d.ts +4 -1
  3. package/dist/types/auth-gateway/server.d.ts +19 -0
  4. package/dist/types/auth-gateway/types.d.ts +9 -3
  5. package/dist/types/auth-retry.d.ts +119 -0
  6. package/dist/types/auth-storage.d.ts +217 -8
  7. package/dist/types/errors.d.ts +24 -0
  8. package/dist/types/index.d.ts +5 -9
  9. package/dist/types/provider-details.d.ts +1 -1
  10. package/dist/types/providers/amazon-bedrock.d.ts +12 -6
  11. package/dist/types/providers/anthropic-client.d.ts +10 -3
  12. package/dist/types/providers/anthropic-messages-server-schema.d.ts +2 -2
  13. package/dist/types/providers/anthropic-messages-server.d.ts +3 -3
  14. package/dist/types/providers/anthropic-wire.d.ts +3 -3
  15. package/dist/types/providers/anthropic.d.ts +41 -34
  16. package/dist/types/providers/aws-credentials.d.ts +8 -0
  17. package/dist/types/providers/azure-openai-responses.d.ts +1 -0
  18. package/dist/types/providers/google-gemini-cli.d.ts +22 -1
  19. package/dist/types/providers/google-shared.d.ts +22 -0
  20. package/dist/types/providers/google-types.d.ts +13 -1
  21. package/dist/types/providers/mock.d.ts +8 -3
  22. package/dist/types/providers/ollama.d.ts +6 -0
  23. package/dist/types/providers/openai-chat-server-schema.d.ts +6 -3
  24. package/dist/types/providers/openai-chat-server.d.ts +3 -3
  25. package/dist/types/providers/openai-chat-wire.d.ts +644 -0
  26. package/dist/types/providers/openai-codex/request-transformer.d.ts +8 -0
  27. package/dist/types/providers/openai-codex/response-handler.d.ts +9 -0
  28. package/dist/types/providers/openai-codex-responses.d.ts +31 -2
  29. package/dist/types/providers/openai-completions-compat.d.ts +2 -25
  30. package/dist/types/providers/openai-completions.d.ts +2 -10
  31. package/dist/types/providers/openai-responses-server-schema.d.ts +4 -4
  32. package/dist/types/providers/openai-responses-server.d.ts +2 -2
  33. package/dist/types/providers/openai-responses-shared.d.ts +49 -9
  34. package/dist/types/providers/openai-responses-wire.d.ts +6065 -0
  35. package/dist/types/providers/openai-responses.d.ts +13 -4
  36. package/dist/types/providers/prometheus-native-client.d.ts +9 -0
  37. package/dist/types/providers/prometheus-native-server.d.ts +4 -3
  38. package/dist/types/providers/transform-messages.d.ts +1 -2
  39. package/dist/types/rate-limit-utils.d.ts +3 -2
  40. package/dist/types/registry/aimlapi.d.ts +4 -0
  41. package/dist/types/registry/alibaba-coding-plan.d.ts +7 -0
  42. package/dist/types/registry/amazon-bedrock.d.ts +5 -0
  43. package/dist/types/registry/anthropic.d.ts +10 -0
  44. package/dist/types/{utils/oauth → registry}/api-key-login.d.ts +8 -2
  45. package/dist/types/{utils/oauth → registry}/api-key-validation.d.ts +15 -0
  46. package/dist/types/registry/cerebras.d.ts +7 -0
  47. package/dist/types/registry/cloudflare-ai-gateway.d.ts +13 -0
  48. package/dist/types/registry/cursor.d.ts +7 -0
  49. package/dist/types/registry/deepseek.d.ts +8 -0
  50. package/dist/types/registry/derived.d.ts +5 -0
  51. package/dist/types/registry/firepass.d.ts +16 -0
  52. package/dist/types/registry/fireworks.d.ts +7 -0
  53. package/dist/types/registry/github-copilot.d.ts +7 -0
  54. package/dist/types/registry/gitlab-duo.d.ts +9 -0
  55. package/dist/types/registry/google-antigravity.d.ts +9 -0
  56. package/dist/types/registry/google-gemini-cli.d.ts +9 -0
  57. package/dist/types/registry/google-vertex.d.ts +5 -0
  58. package/dist/types/registry/google.d.ts +4 -0
  59. package/dist/types/registry/groq.d.ts +4 -0
  60. package/dist/types/registry/huggingface.d.ts +7 -0
  61. package/dist/types/registry/index.d.ts +4 -0
  62. package/dist/types/registry/kagi.d.ts +14 -0
  63. package/dist/types/registry/kilo.d.ts +7 -0
  64. package/dist/types/registry/kimi-code.d.ts +7 -0
  65. package/dist/types/registry/litellm.d.ts +13 -0
  66. package/dist/types/registry/lm-studio.d.ts +8 -0
  67. package/dist/types/registry/minimax-code-cn.d.ts +6 -0
  68. package/dist/types/registry/minimax-code.d.ts +6 -0
  69. package/dist/types/registry/minimax.d.ts +4 -0
  70. package/dist/types/registry/mistral.d.ts +4 -0
  71. package/dist/types/registry/moonshot.d.ts +7 -0
  72. package/dist/types/registry/nanogpt.d.ts +7 -0
  73. package/dist/types/registry/nvidia.d.ts +7 -0
  74. package/dist/types/registry/oauth/__tests__/xai-oauth.test.d.ts +1 -0
  75. package/dist/types/{utils → registry}/oauth/anthropic.d.ts +2 -1
  76. package/dist/types/{utils → registry}/oauth/github-copilot.d.ts +15 -23
  77. package/dist/types/{utils → registry}/oauth/index.d.ts +1 -0
  78. package/dist/types/{utils → registry}/oauth/minimax-code.d.ts +5 -5
  79. package/dist/types/{utils → registry}/oauth/types.d.ts +6 -1
  80. package/dist/types/{utils → registry}/oauth/xai-oauth.d.ts +2 -1
  81. package/dist/types/registry/ollama-cloud.d.ts +7 -0
  82. package/dist/types/registry/ollama.d.ts +12 -0
  83. package/dist/types/registry/openai-codex-device.d.ts +8 -0
  84. package/dist/types/registry/openai-codex.d.ts +9 -0
  85. package/dist/types/registry/openai.d.ts +4 -0
  86. package/dist/types/registry/opencode-go.d.ts +6 -0
  87. package/dist/types/registry/opencode-zen.d.ts +6 -0
  88. package/dist/types/registry/openrouter.d.ts +13 -0
  89. package/dist/types/registry/parallel.d.ts +14 -0
  90. package/dist/types/registry/perplexity.d.ts +7 -0
  91. package/dist/types/registry/qianfan.d.ts +7 -0
  92. package/dist/types/registry/qwen-portal.d.ts +7 -0
  93. package/dist/types/registry/registry.d.ts +272 -0
  94. package/dist/types/registry/synthetic.d.ts +6 -0
  95. package/dist/types/registry/tavily.d.ts +14 -0
  96. package/dist/types/registry/together.d.ts +6 -0
  97. package/dist/types/registry/types.d.ts +51 -0
  98. package/dist/types/registry/venice.d.ts +13 -0
  99. package/dist/types/registry/vercel-ai-gateway.d.ts +7 -0
  100. package/dist/types/registry/vllm.d.ts +7 -0
  101. package/dist/types/registry/wafer-pass.d.ts +6 -0
  102. package/dist/types/registry/wafer-serverless.d.ts +6 -0
  103. package/dist/types/registry/xai-oauth.d.ts +7 -0
  104. package/dist/types/registry/xai.d.ts +4 -0
  105. package/dist/types/registry/xiaomi-token-plan-ams.d.ts +6 -0
  106. package/dist/types/registry/xiaomi-token-plan-cn.d.ts +6 -0
  107. package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +6 -0
  108. package/dist/types/registry/xiaomi.d.ts +6 -0
  109. package/dist/types/registry/zai.d.ts +7 -0
  110. package/dist/types/registry/zenmux.d.ts +7 -0
  111. package/dist/types/registry/zhipu-coding-plan.d.ts +7 -0
  112. package/dist/types/stream.d.ts +9 -1
  113. package/dist/types/types.d.ts +56 -295
  114. package/dist/types/usage/google-antigravity.d.ts +15 -1
  115. package/dist/types/usage/openai-codex-reset.d.ts +79 -0
  116. package/dist/types/usage/openai-codex.d.ts +1 -0
  117. package/dist/types/usage.d.ts +77 -4
  118. package/dist/types/utils/abort.d.ts +6 -0
  119. package/dist/types/utils/event-stream.d.ts +2 -0
  120. package/dist/types/utils/http-inspector.d.ts +0 -1
  121. package/dist/types/utils/idle-iterator.d.ts +35 -0
  122. package/dist/types/utils/openai-http.d.ts +58 -0
  123. package/dist/types/utils/request-debug.d.ts +3 -0
  124. package/dist/types/utils/retry-after.d.ts +1 -0
  125. package/dist/types/utils/schema/fields.d.ts +5 -0
  126. package/dist/types/utils/schema/json-schema-validator.d.ts +8 -0
  127. package/dist/types/utils/schema/stamps.d.ts +7 -15
  128. package/dist/types/utils/sse-debug.d.ts +0 -5
  129. package/dist/types/utils/stream-markup-healing.d.ts +2 -0
  130. package/dist/types/utils.d.ts +1 -5
  131. package/package.json +17 -29
  132. package/src/auth-broker/remote-store.ts +10 -1
  133. package/src/auth-broker/snapshot-cache.ts +1 -1
  134. package/src/auth-broker/wire-schemas.ts +1 -1
  135. package/src/auth-gateway/http.ts +1 -1
  136. package/src/auth-gateway/server.ts +95 -30
  137. package/src/auth-gateway/types.ts +10 -2
  138. package/src/auth-retry.ts +238 -0
  139. package/src/auth-storage.ts +935 -430
  140. package/src/errors.ts +32 -0
  141. package/src/index.ts +9 -14
  142. package/src/provider-details.ts +1 -1
  143. package/src/providers/__tests__/google-auth.test.ts +144 -0
  144. package/src/providers/amazon-bedrock.ts +70 -40
  145. package/src/providers/anthropic-client.ts +15 -13
  146. package/src/providers/anthropic-messages-server-schema.ts +17 -7
  147. package/src/providers/anthropic-messages-server.ts +88 -20
  148. package/src/providers/anthropic-wire.ts +4 -3
  149. package/src/providers/anthropic.ts +1234 -621
  150. package/src/providers/aws-credentials.ts +47 -5
  151. package/src/providers/aws-eventstream.ts +5 -0
  152. package/src/providers/azure-openai-responses.ts +117 -67
  153. package/src/providers/cursor.ts +30 -30
  154. package/src/providers/github-copilot-headers.ts +1 -1
  155. package/src/providers/gitlab-duo.ts +36 -29
  156. package/src/providers/google-auth.ts +71 -8
  157. package/src/providers/google-gemini-cli.ts +118 -22
  158. package/src/providers/google-shared.ts +163 -43
  159. package/src/providers/google-types.ts +10 -1
  160. package/src/providers/kimi.ts +1 -1
  161. package/src/providers/mock.ts +11 -3
  162. package/src/providers/ollama.ts +64 -7
  163. package/src/providers/openai-anthropic-shim.ts +17 -8
  164. package/src/providers/openai-chat-server-schema.ts +9 -3
  165. package/src/providers/openai-chat-server.ts +82 -16
  166. package/src/providers/openai-chat-wire.ts +847 -0
  167. package/src/providers/openai-codex/request-transformer.ts +129 -34
  168. package/src/providers/openai-codex/response-handler.ts +22 -1
  169. package/src/providers/openai-codex-responses.ts +699 -247
  170. package/src/providers/openai-completions-compat.ts +8 -308
  171. package/src/providers/openai-completions.ts +416 -267
  172. package/src/providers/openai-responses-server-schema.ts +15 -9
  173. package/src/providers/openai-responses-server.ts +162 -114
  174. package/src/providers/openai-responses-shared.ts +320 -82
  175. package/src/providers/openai-responses-wire.ts +6391 -0
  176. package/src/providers/openai-responses.ts +382 -176
  177. package/src/providers/prometheus-native-client.ts +27 -11
  178. package/src/providers/prometheus-native-server.ts +44 -17
  179. package/src/providers/transform-messages.ts +311 -120
  180. package/src/providers/vision-guard.ts +5 -3
  181. package/src/rate-limit-utils.ts +13 -3
  182. package/src/registry/aimlapi.ts +6 -0
  183. package/src/{utils/oauth → registry}/alibaba-coding-plan.ts +8 -18
  184. package/src/registry/amazon-bedrock.ts +22 -0
  185. package/src/registry/anthropic.ts +26 -0
  186. package/src/{utils/oauth → registry}/api-key-login.ts +25 -3
  187. package/src/{utils/oauth → registry}/api-key-validation.ts +62 -2
  188. package/src/{utils/oauth → registry}/cerebras.ts +8 -1
  189. package/src/{utils/oauth → registry}/cloudflare-ai-gateway.ts +8 -12
  190. package/src/registry/cursor.ts +20 -0
  191. package/src/{utils/oauth → registry}/deepseek.ts +9 -17
  192. package/src/registry/derived.ts +9 -0
  193. package/src/{utils/oauth → registry}/firepass.ts +10 -2
  194. package/src/{utils/oauth → registry}/fireworks.ts +8 -1
  195. package/src/registry/github-copilot.ts +22 -0
  196. package/src/registry/gitlab-duo.ts +19 -0
  197. package/src/registry/google-antigravity.ts +21 -0
  198. package/src/registry/google-gemini-cli.ts +21 -0
  199. package/src/registry/google-vertex.ts +38 -0
  200. package/src/registry/google.ts +6 -0
  201. package/src/registry/groq.ts +6 -0
  202. package/src/{utils/oauth → registry}/huggingface.ts +8 -19
  203. package/src/registry/index.ts +4 -0
  204. package/src/{utils/oauth → registry}/kagi.ts +9 -11
  205. package/src/{utils/oauth → registry}/kilo.ts +11 -6
  206. package/src/registry/kimi-code.ts +17 -0
  207. package/src/{utils/oauth → registry}/litellm.ts +8 -12
  208. package/src/{utils/oauth → registry}/lm-studio.ts +9 -17
  209. package/src/registry/minimax-code-cn.ts +12 -0
  210. package/src/registry/minimax-code.ts +12 -0
  211. package/src/registry/minimax.ts +6 -0
  212. package/src/registry/mistral.ts +6 -0
  213. package/src/{utils/oauth → registry}/moonshot.ts +8 -9
  214. package/src/{utils/oauth → registry}/nanogpt.ts +8 -1
  215. package/src/{utils/oauth → registry}/nvidia.ts +8 -18
  216. package/src/{utils → registry}/oauth/__tests__/xai-oauth.test.ts +4 -7
  217. package/src/{utils → registry}/oauth/anthropic.ts +38 -17
  218. package/src/{utils → registry}/oauth/github-copilot.ts +79 -115
  219. package/src/registry/oauth/gitlab-duo.ts +198 -0
  220. package/src/{utils → registry}/oauth/google-antigravity.ts +1 -4
  221. package/src/{utils → registry}/oauth/google-gemini-cli.ts +1 -4
  222. package/src/registry/oauth/index.ts +164 -0
  223. package/src/{utils → registry}/oauth/minimax-code.ts +16 -14
  224. package/src/{utils → registry}/oauth/types.ts +7 -51
  225. package/src/{utils → registry}/oauth/wafer.ts +1 -1
  226. package/src/{utils → registry}/oauth/xai-oauth.ts +16 -8
  227. package/src/{utils → registry}/oauth/xiaomi.ts +9 -4
  228. package/src/{utils/oauth → registry}/ollama-cloud.ts +8 -1
  229. package/src/{utils/oauth → registry}/ollama.ts +8 -13
  230. package/src/registry/openai-codex-device.ts +18 -0
  231. package/src/registry/openai-codex.ts +19 -0
  232. package/src/registry/openai.ts +6 -0
  233. package/src/registry/opencode-go.ts +12 -0
  234. package/src/registry/opencode-zen.ts +12 -0
  235. package/src/{utils/oauth → registry}/openrouter.ts +10 -2
  236. package/src/{utils/oauth → registry}/parallel.ts +9 -11
  237. package/src/registry/perplexity.ts +13 -0
  238. package/src/{utils/oauth → registry}/qianfan.ts +8 -17
  239. package/src/{utils/oauth → registry}/qwen-portal.ts +8 -19
  240. package/src/registry/registry.ts +149 -0
  241. package/src/{utils/oauth → registry}/synthetic.ts +7 -1
  242. package/src/{utils/oauth → registry}/tavily.ts +10 -12
  243. package/src/{utils/oauth → registry}/together.ts +7 -1
  244. package/src/registry/types.ts +56 -0
  245. package/src/{utils/oauth → registry}/venice.ts +8 -12
  246. package/src/{utils/oauth → registry}/vercel-ai-gateway.ts +8 -18
  247. package/src/{utils/oauth → registry}/vllm.ts +9 -16
  248. package/src/registry/wafer-pass.ts +12 -0
  249. package/src/registry/wafer-serverless.ts +12 -0
  250. package/src/registry/xai-oauth.ts +17 -0
  251. package/src/registry/xai.ts +6 -0
  252. package/src/registry/xiaomi-token-plan-ams.ts +12 -0
  253. package/src/registry/xiaomi-token-plan-cn.ts +12 -0
  254. package/src/registry/xiaomi-token-plan-sgp.ts +12 -0
  255. package/src/registry/xiaomi.ts +12 -0
  256. package/src/{utils/oauth → registry}/zai.ts +10 -22
  257. package/src/{utils/oauth → registry}/zenmux.ts +8 -1
  258. package/src/{utils/oauth/zhipu.ts → registry/zhipu-coding-plan.ts} +9 -21
  259. package/src/stream.ts +229 -199
  260. package/src/types.ts +63 -384
  261. package/src/usage/claude.ts +4 -2
  262. package/src/usage/github-copilot.ts +4 -2
  263. package/src/usage/google-antigravity.ts +196 -28
  264. package/src/usage/kimi.ts +1 -1
  265. package/src/usage/minimax-code.ts +5 -6
  266. package/src/usage/openai-codex-reset.ts +174 -0
  267. package/src/usage/openai-codex.ts +19 -2
  268. package/src/usage/zai.ts +2 -1
  269. package/src/usage.ts +93 -4
  270. package/src/utils/abort.ts +14 -0
  271. package/src/utils/event-stream.ts +17 -0
  272. package/src/utils/http-inspector.ts +4 -12
  273. package/src/utils/idle-iterator.ts +250 -79
  274. package/src/utils/openai-http.ts +157 -0
  275. package/src/utils/request-debug.ts +67 -19
  276. package/src/utils/retry-after.ts +1 -1
  277. package/src/utils/retry.ts +23 -2
  278. package/src/utils/schema/CONSTRAINTS.md +4 -2
  279. package/src/utils/schema/fields.ts +16 -0
  280. package/src/utils/schema/json-schema-validator.ts +19 -1
  281. package/src/utils/schema/normalize.ts +80 -8
  282. package/src/utils/schema/stamps.ts +22 -10
  283. package/src/utils/schema/wire.ts +2 -2
  284. package/src/utils/sse-debug.ts +0 -271
  285. package/src/utils/stream-markup-healing.ts +50 -8
  286. package/src/utils/validation.ts +49 -13
  287. package/src/utils.ts +2 -26
  288. package/dist/types/model-cache.d.ts +0 -17
  289. package/dist/types/model-manager.d.ts +0 -64
  290. package/dist/types/model-thinking.d.ts +0 -100
  291. package/dist/types/models.d.ts +0 -12
  292. package/dist/types/provider-models/bundled-references.d.ts +0 -4
  293. package/dist/types/provider-models/descriptors.d.ts +0 -50
  294. package/dist/types/provider-models/google.d.ts +0 -24
  295. package/dist/types/provider-models/index.d.ts +0 -5
  296. package/dist/types/provider-models/ollama.d.ts +0 -7
  297. package/dist/types/provider-models/openai-compat.d.ts +0 -323
  298. package/dist/types/provider-models/special.d.ts +0 -16
  299. package/dist/types/utils/discovery/antigravity.d.ts +0 -61
  300. package/dist/types/utils/discovery/codex.d.ts +0 -38
  301. package/dist/types/utils/discovery/cursor.d.ts +0 -23
  302. package/dist/types/utils/discovery/gemini.d.ts +0 -25
  303. package/dist/types/utils/discovery/index.d.ts +0 -4
  304. package/dist/types/utils/discovery/openai-compatible.d.ts +0 -72
  305. package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +0 -18
  306. package/dist/types/utils/oauth/cerebras.d.ts +0 -1
  307. package/dist/types/utils/oauth/cloudflare-ai-gateway.d.ts +0 -18
  308. package/dist/types/utils/oauth/deepseek.d.ts +0 -10
  309. package/dist/types/utils/oauth/firepass.d.ts +0 -1
  310. package/dist/types/utils/oauth/fireworks.d.ts +0 -1
  311. package/dist/types/utils/oauth/huggingface.d.ts +0 -19
  312. package/dist/types/utils/oauth/kagi.d.ts +0 -17
  313. package/dist/types/utils/oauth/kilo.d.ts +0 -5
  314. package/dist/types/utils/oauth/litellm.d.ts +0 -18
  315. package/dist/types/utils/oauth/lm-studio.d.ts +0 -17
  316. package/dist/types/utils/oauth/moonshot.d.ts +0 -1
  317. package/dist/types/utils/oauth/nanogpt.d.ts +0 -1
  318. package/dist/types/utils/oauth/nvidia.d.ts +0 -18
  319. package/dist/types/utils/oauth/ollama-cloud.d.ts +0 -2
  320. package/dist/types/utils/oauth/ollama.d.ts +0 -18
  321. package/dist/types/utils/oauth/openrouter.d.ts +0 -1
  322. package/dist/types/utils/oauth/parallel.d.ts +0 -17
  323. package/dist/types/utils/oauth/qianfan.d.ts +0 -17
  324. package/dist/types/utils/oauth/qwen-portal.d.ts +0 -19
  325. package/dist/types/utils/oauth/synthetic.d.ts +0 -1
  326. package/dist/types/utils/oauth/tavily.d.ts +0 -17
  327. package/dist/types/utils/oauth/together.d.ts +0 -1
  328. package/dist/types/utils/oauth/venice.d.ts +0 -18
  329. package/dist/types/utils/oauth/vercel-ai-gateway.d.ts +0 -18
  330. package/dist/types/utils/oauth/vllm.d.ts +0 -16
  331. package/dist/types/utils/oauth/zai.d.ts +0 -18
  332. package/dist/types/utils/oauth/zenmux.d.ts +0 -1
  333. package/dist/types/utils/oauth/zhipu.d.ts +0 -18
  334. package/src/model-cache.ts +0 -129
  335. package/src/model-manager.ts +0 -469
  336. package/src/model-thinking.ts +0 -756
  337. package/src/models.json +0 -60287
  338. package/src/models.json.d.ts +0 -9
  339. package/src/models.ts +0 -56
  340. package/src/provider-models/bundled-references.ts +0 -38
  341. package/src/provider-models/descriptors.ts +0 -364
  342. package/src/provider-models/google.ts +0 -88
  343. package/src/provider-models/index.ts +0 -5
  344. package/src/provider-models/ollama.ts +0 -153
  345. package/src/provider-models/openai-compat.ts +0 -2904
  346. package/src/provider-models/special.ts +0 -67
  347. package/src/utils/discovery/antigravity.ts +0 -261
  348. package/src/utils/discovery/codex.ts +0 -371
  349. package/src/utils/discovery/cursor.ts +0 -306
  350. package/src/utils/discovery/gemini.ts +0 -248
  351. package/src/utils/discovery/index.ts +0 -4
  352. package/src/utils/discovery/openai-compatible.ts +0 -224
  353. package/src/utils/oauth/gitlab-duo.ts +0 -123
  354. package/src/utils/oauth/index.ts +0 -502
  355. /package/dist/types/{utils/oauth/__tests__/xai-oauth.test.d.ts → providers/__tests__/google-auth.test.d.ts} +0 -0
  356. /package/dist/types/{utils → registry}/oauth/callback-server.d.ts +0 -0
  357. /package/dist/types/{utils → registry}/oauth/cursor.d.ts +0 -0
  358. /package/dist/types/{utils → registry}/oauth/gitlab-duo.d.ts +0 -0
  359. /package/dist/types/{utils → registry}/oauth/google-antigravity.d.ts +0 -0
  360. /package/dist/types/{utils → registry}/oauth/google-gemini-cli.d.ts +0 -0
  361. /package/dist/types/{utils → registry}/oauth/google-oauth-shared.d.ts +0 -0
  362. /package/dist/types/{utils → registry}/oauth/kimi.d.ts +0 -0
  363. /package/dist/types/{utils → registry}/oauth/openai-codex.d.ts +0 -0
  364. /package/dist/types/{utils → registry}/oauth/opencode.d.ts +0 -0
  365. /package/dist/types/{utils → registry}/oauth/perplexity.d.ts +0 -0
  366. /package/dist/types/{utils → registry}/oauth/pkce.d.ts +0 -0
  367. /package/dist/types/{utils → registry}/oauth/wafer.d.ts +0 -0
  368. /package/dist/types/{utils → registry}/oauth/xiaomi.d.ts +0 -0
  369. /package/src/{utils → registry}/oauth/callback-server.ts +0 -0
  370. /package/src/{utils → registry}/oauth/cursor.ts +0 -0
  371. /package/src/{utils → registry}/oauth/google-oauth-shared.ts +0 -0
  372. /package/src/{utils → registry}/oauth/kimi.ts +0 -0
  373. /package/src/{utils → registry}/oauth/oauth.html +0 -0
  374. /package/src/{utils → registry}/oauth/openai-codex.ts +0 -0
  375. /package/src/{utils → registry}/oauth/opencode.ts +0 -0
  376. /package/src/{utils → registry}/oauth/perplexity.ts +0 -0
  377. /package/src/{utils → registry}/oauth/pkce.ts +0 -0
@@ -0,0 +1,157 @@
1
+ /**
2
+ * JSON-POST → SSE transport for OpenAI-wire streaming endpoints (chat
3
+ * completions, responses, azure responses). Replaces the `openai` SDK client:
4
+ *
5
+ * - Retries: `fetchWithRetry` (Retry-After/quota-hint aware; 5xx/408/429 and
6
+ * transient network errors). Default 6 total attempts — parity with the
7
+ * SDK's former `maxRetries: 5`.
8
+ * - SSE decode: `readSseJson` (spec-compliant framing, `[DONE]`-aware).
9
+ * `onSseEvent` observers now receive real wire frames instead of events
10
+ * re-synthesized from decoded SDK objects.
11
+ * - Errors: {@link OpenAIHttpError} exposes `status`/`headers`/`code`
12
+ * structurally (ProviderHttpError contract — `extractHttpStatusFromError`,
13
+ * retry-after extraction, copilot transient classification) and carries the
14
+ * captured response body for the strict-tools fallback and the responses
15
+ * chain-state detectors, which regex over `error.message`.
16
+ */
17
+ import { fetchWithRetry, readSseJson, type SseEventObserver } from "@prometheus-ai/utils";
18
+ import { ProviderHttpError } from "../errors";
19
+ import type { FetchImpl } from "../types";
20
+ import type { CapturedHttpErrorResponse } from "./http-inspector";
21
+
22
+ /**
23
+ * Total attempts when the caller has no first-event deadline armed. The
24
+ * removed SDK clients ran `maxRetries: 5`, i.e. 6 requests.
25
+ */
26
+ const DEFAULT_MAX_ATTEMPTS = 6;
27
+
28
+ /** Bound the `Error.message` allocation for proxy HTML error pages and the like. */
29
+ const MAX_DETAIL_CHARS = 4096;
30
+
31
+ /** Non-2xx response from an OpenAI-wire endpoint, with the decoded body attached. */
32
+ export class OpenAIHttpError extends ProviderHttpError {
33
+ readonly captured: CapturedHttpErrorResponse;
34
+
35
+ constructor(message: string, captured: CapturedHttpErrorResponse, code: string | undefined) {
36
+ super(message, captured.status, { headers: captured.headers, code });
37
+ this.name = "OpenAIHttpError";
38
+ this.captured = captured;
39
+ }
40
+ }
41
+
42
+ export interface OpenAIStreamRequestInit {
43
+ url: string;
44
+ headers: Record<string, string>;
45
+ /** JSON request body; serialized once per call (retries resend the same bytes). */
46
+ body: unknown;
47
+ signal: AbortSignal;
48
+ fetch?: FetchImpl;
49
+ /**
50
+ * Total attempts (initial + retries). Defaults to {@link DEFAULT_MAX_ATTEMPTS}.
51
+ * Pass `1` when a first-event watchdog is armed so retries cannot silently
52
+ * extend the caller's deadline (mirrors the old `maxRetries: 0` hint).
53
+ */
54
+ maxAttempts?: number;
55
+ /** Raw wire-frame observer (`onSseEvent` debug pipeline). */
56
+ onSseEvent?: SseEventObserver;
57
+ }
58
+
59
+ export interface OpenAIStreamHandle<TEvent> {
60
+ /** Decoded `data:` payloads; terminates on `[DONE]` or stream end. */
61
+ events: AsyncGenerator<TEvent>;
62
+ response: Response;
63
+ /** `x-request-id` response header (the SDK's former `request_id`). */
64
+ requestId: string | null;
65
+ }
66
+
67
+ /**
68
+ * POST a JSON body and stream back decoded SSE events.
69
+ *
70
+ * Throws {@link OpenAIHttpError} on a non-2xx terminal response. Aborts on
71
+ * `signal` propagate from `fetchWithRetry`/`readSseJson`; callers own the
72
+ * watchdog timers and abort-reason bookkeeping.
73
+ */
74
+ export async function postOpenAIStream<TEvent>(init: OpenAIStreamRequestInit): Promise<OpenAIStreamHandle<TEvent>> {
75
+ const response = await fetchWithRetry(init.url, {
76
+ method: "POST",
77
+ headers: { "Content-Type": "application/json", Accept: "text/event-stream", ...init.headers },
78
+ body: JSON.stringify(init.body),
79
+ signal: init.signal,
80
+ fetch: init.fetch,
81
+ maxAttempts: init.maxAttempts ?? DEFAULT_MAX_ATTEMPTS,
82
+ // Bun's native fetch enforces a hard ~300s pre-response timeout (issue #2422).
83
+ // Cold large-context streams legitimately exceed it; the caller's
84
+ // `firstEventTimeoutMs`/`AbortSignal` already govern stuck requests.
85
+ timeout: false,
86
+ });
87
+ if (!response.ok) {
88
+ throw await captureOpenAIHttpError(response);
89
+ }
90
+ if (!response.body) {
91
+ throw new Error(`OpenAI stream response has no body (status ${response.status})`);
92
+ }
93
+ return {
94
+ events: readSseJson<TEvent>(response.body, init.signal, init.onSseEvent),
95
+ response,
96
+ requestId: response.headers.get("x-request-id"),
97
+ };
98
+ }
99
+
100
+ /** Decode a non-2xx response into an {@link OpenAIHttpError} without consuming it twice. */
101
+ export async function captureOpenAIHttpError(response: Response): Promise<OpenAIHttpError> {
102
+ let bodyText: string | undefined;
103
+ let bodyJson: unknown;
104
+ try {
105
+ bodyText = await response.text();
106
+ if (bodyText.trim().length > 0) {
107
+ try {
108
+ bodyJson = JSON.parse(bodyText);
109
+ } catch {}
110
+ } else {
111
+ bodyText = undefined;
112
+ }
113
+ } catch {}
114
+ const captured: CapturedHttpErrorResponse = {
115
+ status: response.status,
116
+ headers: response.headers,
117
+ bodyText,
118
+ bodyJson,
119
+ };
120
+ const { detail, code } = extractErrorDetail(bodyJson, bodyText);
121
+ // "status code (no body)" matches the SDK's former APIError phrasing;
122
+ // `finalizeErrorMessage` keys a repair path on that exact wording.
123
+ const message = detail
124
+ ? `${response.status} ${detail.length > MAX_DETAIL_CHARS ? detail.slice(0, MAX_DETAIL_CHARS) : detail}`
125
+ : `${response.status} status code (no body)`;
126
+ return new OpenAIHttpError(message, captured, code);
127
+ }
128
+
129
+ /**
130
+ * Pull a human-readable message and machine code out of an OpenAI-style error
131
+ * envelope (`{ error: { message, code, type } }`), tolerating the flat shapes
132
+ * compat hosts return (`{ error: "..." }`, `{ message: "..." }`) and falling
133
+ * back to the raw body text.
134
+ */
135
+ function extractErrorDetail(
136
+ bodyJson: unknown,
137
+ bodyText: string | undefined,
138
+ ): { detail: string | undefined; code: string | undefined } {
139
+ if (typeof bodyJson === "object" && bodyJson !== null) {
140
+ const envelope = bodyJson as { error?: unknown; message?: unknown };
141
+ const error = envelope.error;
142
+ if (typeof error === "object" && error !== null) {
143
+ const { message, code, type } = error as { message?: unknown; code?: unknown; type?: unknown };
144
+ return {
145
+ detail: typeof message === "string" && message.length > 0 ? message : bodyText,
146
+ code: typeof code === "string" ? code : typeof type === "string" ? type : undefined,
147
+ };
148
+ }
149
+ if (typeof error === "string" && error.length > 0) {
150
+ return { detail: error, code: undefined };
151
+ }
152
+ if (typeof envelope.message === "string" && envelope.message.length > 0) {
153
+ return { detail: envelope.message, code: undefined };
154
+ }
155
+ }
156
+ return { detail: bodyText, code: undefined };
157
+ }
@@ -1,5 +1,6 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import * as fs from "node:fs/promises";
3
+ import * as path from "node:path";
3
4
  import type { FetchImpl } from "../types";
4
5
 
5
6
  const REQUEST_DEBUG_ENV = "PROMETHEUS_REQ_DEBUG";
@@ -8,6 +9,7 @@ const textEncoder = new TextEncoder();
8
9
  const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
9
10
 
10
11
  let nextSessionId = 1;
12
+ let nextRequestDebugPath: string | undefined;
11
13
 
12
14
  type DebugFetch = FetchImpl & { [DEBUG_FETCH_MARKER]?: true };
13
15
  type RequestBodyInit = NonNullable<RequestInit["body"]>;
@@ -27,6 +29,14 @@ export interface RequestDebugPayload {
27
29
  protocol?: string;
28
30
  }
29
31
 
32
+ interface ReservedRequestDebugFile {
33
+ id: number;
34
+ requestPath: string;
35
+ responsePath: string;
36
+ handle: fs.FileHandle;
37
+ overwrite: boolean;
38
+ }
39
+
30
40
  export interface RequestDebugResponseLog {
31
41
  write(chunk: Uint8Array | string): void;
32
42
  close(): Promise<void>;
@@ -40,10 +50,32 @@ export interface RequestDebugSession {
40
50
  wrapResponse(response: Response): Promise<Response>;
41
51
  }
42
52
 
43
- export function isRequestDebugEnabled(): boolean {
53
+ function isRequestDebugEnvEnabled(): boolean {
44
54
  return Bun.env[REQUEST_DEBUG_ENV] === "1";
45
55
  }
46
56
 
57
+ export function isRequestDebugEnabled(): boolean {
58
+ return isRequestDebugEnvEnabled() || nextRequestDebugPath !== undefined;
59
+ }
60
+
61
+ export function setNextRequestDebugPath(requestPath: string): void {
62
+ nextRequestDebugPath = requestPath;
63
+ }
64
+
65
+ export function clearNextRequestDebugPath(): void {
66
+ nextRequestDebugPath = undefined;
67
+ }
68
+
69
+ export function getNextRequestDebugPath(): string | undefined {
70
+ return nextRequestDebugPath;
71
+ }
72
+
73
+ function consumeNextRequestDebugPath(): string | undefined {
74
+ const requestPath = nextRequestDebugPath;
75
+ nextRequestDebugPath = undefined;
76
+ return requestPath;
77
+ }
78
+
47
79
  export function wrapFetchForRequestDebug(fetchImpl: FetchImpl): FetchImpl {
48
80
  if (!isRequestDebugEnabled()) return fetchImpl;
49
81
  const maybeWrapped = fetchImpl as DebugFetch;
@@ -51,6 +83,7 @@ export function wrapFetchForRequestDebug(fetchImpl: FetchImpl): FetchImpl {
51
83
 
52
84
  const wrapped = Object.assign(
53
85
  async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
86
+ if (!isRequestDebugEnabled()) return fetchImpl(input, init);
54
87
  const session = await createFetchRequestDebugSession(input, init);
55
88
  const response = await fetchImpl(input, init);
56
89
  return session.wrapResponse(response);
@@ -69,7 +102,7 @@ export function withRequestDebugFetch<T extends { fetch?: FetchImpl } | undefine
69
102
  }
70
103
 
71
104
  export async function createRequestDebugSession(payload: RequestDebugPayload): Promise<RequestDebugSession> {
72
- const { id, requestPath, responsePath, handle } = await reserveRequestDebugFile();
105
+ const { id, requestPath, responsePath, handle, overwrite } = await reserveRequestDebugFile();
73
106
  const requestDump: Record<string, unknown> = {
74
107
  id,
75
108
  protocol: payload.protocol ?? "http",
@@ -89,7 +122,7 @@ export async function createRequestDebugSession(payload: RequestDebugPayload): P
89
122
  await handle.close();
90
123
  }
91
124
 
92
- return new FileRequestDebugSession(id, requestPath, responsePath);
125
+ return new FileRequestDebugSession(id, requestPath, responsePath, overwrite);
93
126
  }
94
127
 
95
128
  async function createFetchRequestDebugSession(
@@ -110,15 +143,17 @@ class FileRequestDebugSession implements RequestDebugSession {
110
143
  readonly id: number;
111
144
  readonly requestPath: string;
112
145
  readonly responsePath: string;
146
+ readonly #overwriteResponseLog: boolean;
113
147
 
114
- constructor(id: number, requestPath: string, responsePath: string) {
148
+ constructor(id: number, requestPath: string, responsePath: string, overwriteResponseLog: boolean) {
115
149
  this.id = id;
116
150
  this.requestPath = requestPath;
117
151
  this.responsePath = responsePath;
152
+ this.#overwriteResponseLog = overwriteResponseLog;
118
153
  }
119
154
 
120
155
  async openResponseLog(statusLine: string, headers?: RequestDebugHeaders): Promise<RequestDebugResponseLog> {
121
- const handle = await fs.open(this.responsePath, "wx");
156
+ const handle = await fs.open(this.responsePath, this.#overwriteResponseLog ? "w" : "wx");
122
157
  const headerBlock = formatResponseHeaderBlock(statusLine, headers);
123
158
  await handle.write(textEncoder.encode(headerBlock));
124
159
  return new FileRequestDebugResponseLog(handle);
@@ -170,6 +205,7 @@ class FileRequestDebugSession implements RequestDebugSession {
170
205
  class FileRequestDebugResponseLog implements RequestDebugResponseLog {
171
206
  #handle: fs.FileHandle | undefined;
172
207
  #pending: Promise<void> = Promise.resolve();
208
+ #closed: Promise<void> | undefined;
173
209
 
174
210
  constructor(handle: fs.FileHandle) {
175
211
  this.#handle = handle;
@@ -184,15 +220,19 @@ class FileRequestDebugResponseLog implements RequestDebugResponseLog {
184
220
  });
185
221
  }
186
222
 
187
- async close(): Promise<void> {
223
+ close(): Promise<void> {
224
+ if (this.#closed) return this.#closed;
188
225
  const handle = this.#handle;
189
- if (!handle) return;
226
+ if (!handle) return Promise.resolve();
190
227
  this.#handle = undefined;
191
- try {
192
- await this.#pending;
193
- } finally {
194
- await handle.close();
195
- }
228
+ this.#closed = (async () => {
229
+ try {
230
+ await this.#pending;
231
+ } finally {
232
+ await handle.close();
233
+ }
234
+ })();
235
+ return this.#closed;
196
236
  }
197
237
  }
198
238
 
@@ -208,18 +248,26 @@ function copyResponseMetadata(target: Response, source: Response): void {
208
248
  }
209
249
  }
210
250
 
211
- async function reserveRequestDebugFile(): Promise<{
212
- id: number;
213
- requestPath: string;
214
- responsePath: string;
215
- handle: fs.FileHandle;
216
- }> {
251
+ async function reserveRequestDebugFile(): Promise<ReservedRequestDebugFile> {
252
+ const explicitPath = consumeNextRequestDebugPath();
253
+ if (explicitPath) {
254
+ await fs.mkdir(path.dirname(explicitPath), { recursive: true });
255
+ const handle = await fs.open(explicitPath, "w");
256
+ return {
257
+ id: nextSessionId++,
258
+ requestPath: explicitPath,
259
+ responsePath: `${explicitPath}.res.log`,
260
+ handle,
261
+ overwrite: true,
262
+ };
263
+ }
264
+
217
265
  for (;;) {
218
266
  const id = nextSessionId++;
219
267
  const requestPath = `rr-session-${id}.json`;
220
268
  try {
221
269
  const handle = await fs.open(requestPath, "wx");
222
- return { id, requestPath, responsePath: `rr-session-${id}.res.log`, handle };
270
+ return { id, requestPath, responsePath: `rr-session-${id}.res.log`, handle, overwrite: false };
223
271
  } catch (error) {
224
272
  if (isFileExistsError(error)) continue;
225
273
  throw error;
@@ -28,7 +28,7 @@ export function getRetryAfterMsFromHeaders(headers: HeadersLike): number | undef
28
28
  return Math.max(...candidates);
29
29
  }
30
30
 
31
- function getHeadersFromError(error: unknown): HeadersLike {
31
+ export function getHeadersFromError(error: unknown): HeadersLike {
32
32
  if (!error || typeof error !== "object") return undefined;
33
33
  const record = error as { headers?: unknown; response?: { headers?: unknown }; cause?: unknown };
34
34
  const direct = extractHeaders(record.headers) ?? extractHeaders(record.response?.headers);
@@ -1,5 +1,6 @@
1
1
  import { scheduler } from "node:timers/promises";
2
2
  import { extractHttpStatusFromError, isRetryableError } from "@prometheus-ai/utils";
3
+ import { getHeadersFromError, getRetryAfterMsFromHeaders } from "./retry-after";
3
4
 
4
5
  /**
5
6
  * GitHub Copilot intermittently rejects preview models (gpt-5.3-codex,
@@ -24,6 +25,8 @@ export function isCopilotTransientModelError(error: unknown): boolean {
24
25
 
25
26
  const COPILOT_MODEL_RETRY_MAX_ATTEMPTS = 3;
26
27
  const COPILOT_MODEL_RETRY_BASE_DELAY_MS = 400;
28
+ /** Longest server-requested backoff we are willing to sit out before giving up. */
29
+ const COPILOT_RETRY_AFTER_MAX_WAIT_MS = 30_000;
27
30
 
28
31
  /**
29
32
  * Wrap an initial Copilot request so transient `model_not_supported` 400s are
@@ -45,9 +48,27 @@ export async function callWithCopilotModelRetry<T>(
45
48
  return await fn();
46
49
  } catch (error) {
47
50
  lastError = error;
48
- if (!isCopilotTransientModelError(error) && !isRetryableError(error)) throw error;
51
+ // A latched abort (caller cancel or local watchdog) makes any retry a
52
+ // guaranteed-dead attempt — surface the original error, not the
53
+ // scheduler's AbortError.
54
+ if (options.signal?.aborted) throw error;
55
+ const transientModelError = isCopilotTransientModelError(error);
56
+ if (!transientModelError && !isRetryableError(error)) throw error;
49
57
  if (attempt === COPILOT_MODEL_RETRY_MAX_ATTEMPTS - 1) break;
50
- await scheduler.wait(retryBaseDelayMs * (attempt + 1), { signal: options.signal });
58
+ let delayMs = retryBaseDelayMs * (attempt + 1);
59
+ if (!transientModelError) {
60
+ const status = extractHttpStatusFromError(error);
61
+ if (status !== undefined) {
62
+ // Status-bearing retryable errors (429/5xx) are only re-sent when
63
+ // the server told us when to come back — a blind fixed-delay retry
64
+ // of a rate limit just burns the remaining attempts. Status-less
65
+ // transport blips (socket close, h2 reset) keep the linear backoff.
66
+ const retryAfterMs = getRetryAfterMsFromHeaders(getHeadersFromError(error));
67
+ if (retryAfterMs === undefined || retryAfterMs > COPILOT_RETRY_AFTER_MAX_WAIT_MS) throw error;
68
+ delayMs = Math.max(delayMs, retryAfterMs);
69
+ }
70
+ }
71
+ await scheduler.wait(delayMs, { signal: options.signal });
51
72
  }
52
73
  }
53
74
  throw lastError;
@@ -37,8 +37,10 @@ When strict mode is requested (`strict=true` at call site), the schema MUST sati
37
37
  3. **Object and tuple strictness is enforced recursively**
38
38
  - Every object node gets `additionalProperties: false`.
39
39
  - Every property key is included in `required`.
40
- - Optional properties are wrapped as nullable unions:
41
- - `anyOf: [<original schema>, { "type": "null" }]`.
40
+ - Optional properties are made nullable:
41
+ - Pure union nodes (only `anyOf` plus optional `description`) get a `{ "type": "null" }` branch appended in place — never a nested wrapper.
42
+ - All other nodes are wrapped as `anyOf: [<original schema>, { "type": "null" }]`. Nodes with constraining siblings next to `anyOf` MUST keep the wrapper: sibling keywords are conjunctive with `anyOf`, so appending a null branch would not make the node nullable.
43
+ - Nested pure unions are spliced into the parent `anyOf` (`(A ∨ B) ∨ C` → `A ∨ B ∨ C`); an inner `description` is hoisted to the parent when the parent has none. Strict output MUST NOT contain an `anyOf` branch that is itself a pure union — some upstream validators (OpenRouter DeepSeek) reject branches without `type`.
42
44
  - Tuple entries in `prefixItems` are strictified recursively.
43
45
 
44
46
  4. **Schema nodes must be representable in strict mode**
@@ -154,6 +154,22 @@ export const CLOUD_CODE_ASSIST_TYPE_SPECIFIC_KEYS: Record<string, Record<string,
154
154
  null: {},
155
155
  };
156
156
 
157
+ /**
158
+ * Flat set of every type-specific key across all CCA types.
159
+ * Used to identify sibling keys that need filtering during mixed-type collapse.
160
+ */
161
+ export const ALL_CCA_TYPE_SPECIFIC_KEYS: Record<string, true> = buildAllCcaTypeSpecificKeys();
162
+
163
+ function buildAllCcaTypeSpecificKeys(): Record<string, true> {
164
+ const all: Record<string, true> = {};
165
+ for (const typeKeys of Object.values(CLOUD_CODE_ASSIST_TYPE_SPECIFIC_KEYS)) {
166
+ for (const key in typeKeys) {
167
+ all[key] = true;
168
+ }
169
+ }
170
+ return all;
171
+ }
172
+
157
173
  /**
158
174
  * Cloud Code Assist shared schema keys allowed on any type.
159
175
  * Used alongside CLOUD_CODE_ASSIST_TYPE_SPECIFIC_KEYS for CCA combiner collapsing.
@@ -20,6 +20,14 @@ export interface JsonSchemaValidationIssue {
20
20
  message: string;
21
21
  expectedTypes?: string[];
22
22
  keyword?: string;
23
+ /**
24
+ * Marks issues that originate inside a failed `anyOf` / `oneOf` branch.
25
+ * Consumers such as the tool-argument coercion layer use this to avoid
26
+ * applying type repairs (e.g. singleton-array wrapping) that would be
27
+ * authoritative outside of a combinator but are only one candidate
28
+ * branch's expectation here.
29
+ */
30
+ fromUnionBranch?: boolean;
23
31
  }
24
32
 
25
33
  export interface JsonSchemaValidationResult {
@@ -242,7 +250,17 @@ function validateSchemaNode(
242
250
  const branchValid = keyword === "anyOf" ? matches > 0 : matches === 1;
243
251
  if (!branchValid) {
244
252
  if (matches === 0 && firstIssues && firstIssues.length > 0) {
245
- issues.push(...firstIssues);
253
+ // Only tag issues that sit at the combinator's own path as
254
+ // union-branch; deeper issues describe a specific field within
255
+ // the failed branch and should remain individually repairable.
256
+ const unionDepth = path.length;
257
+ for (const branchIssue of firstIssues) {
258
+ if (branchIssue.path.length === unionDepth) {
259
+ issues.push({ ...branchIssue, fromUnionBranch: true });
260
+ } else {
261
+ issues.push(branchIssue);
262
+ }
263
+ }
246
264
  } else {
247
265
  pushIssue(
248
266
  issues,
@@ -11,6 +11,7 @@ import { dereferenceJsonSchema } from "./dereference";
11
11
  import { upgradeJsonSchemaTo202012 } from "./draft";
12
12
  import { areJsonValuesEqual, mergePropertySchemas } from "./equality";
13
13
  import {
14
+ ALL_CCA_TYPE_SPECIFIC_KEYS,
14
15
  CLOUD_CODE_ASSIST_SHARED_SCHEMA_KEYS,
15
16
  CLOUD_CODE_ASSIST_TYPE_SPECIFIC_KEYS,
16
17
  COMBINATOR_KEYS,
@@ -51,7 +52,6 @@ export interface NormalizeSchemaOptions {
51
52
 
52
53
  interface NormalizeSchemaWalkOptions extends NormalizeSchemaOptions {
53
54
  insideProperties: boolean;
54
- epoch: number;
55
55
  }
56
56
 
57
57
  interface ResidualIncompatibilityChecks {
@@ -218,13 +218,27 @@ function applyDescriptionSpill(
218
218
 
219
219
  function normalizeSchemaNode(value: unknown, options: NormalizeSchemaWalkOptions): unknown {
220
220
  if (Array.isArray(value)) {
221
- if (!once(value, options.epoch)) return [];
222
- return value.map(entry => normalizeSchemaNode(entry, options));
221
+ if (!enter(value)) return [];
222
+ try {
223
+ return value.map(entry => normalizeSchemaNode(entry, options));
224
+ } finally {
225
+ exit(value);
226
+ }
223
227
  }
224
228
  if (!isJsonObject(value)) {
225
229
  return value;
226
230
  }
227
- if (!once(value, options.epoch)) return {};
231
+ // `enter`/`exit` path-tracking (not a visited-set): DAG-shared subtrees are
232
+ // normalized at every occurrence; only true cycles short-circuit to `{}`.
233
+ if (!enter(value)) return {};
234
+ try {
235
+ return normalizeSchemaObjectNode(value, options);
236
+ } finally {
237
+ exit(value);
238
+ }
239
+ }
240
+
241
+ function normalizeSchemaObjectNode(value: JsonObject, options: NormalizeSchemaWalkOptions): unknown {
228
242
  let obj = options.normalizeFieldNames && !options.insideProperties ? applySnakeCaseRenames(value) : value;
229
243
  if (options.collapseNullFields && !options.insideProperties) {
230
244
  obj = preHandleNullFields(obj);
@@ -501,12 +515,32 @@ function collapseMixedTypeCombinerVariants(schema: JsonObject, combiner: "anyOf"
501
515
  if (variantTypes.length < 2 || variantTypes.every(type => type === "object")) {
502
516
  return schema;
503
517
  }
504
-
505
518
  const nextSchema = copySchemaWithout(schema, combiner);
506
519
  const nonNullTypes = variantTypes.filter(t => t !== "null");
507
- nextSchema.type = nonNullTypes[0] ?? variantTypes[0];
520
+ const chosenType: string = nonNullTypes[0] ?? variantTypes[0];
521
+ nextSchema.type = chosenType;
522
+ const chosenTypeAllowedKeys = CLOUD_CODE_ASSIST_TYPE_SPECIFIC_KEYS[chosenType] ?? {};
523
+
524
+ // Strip sibling keys that were copied from the parent and belong to a
525
+ // different type (e.g. `items` sibling on a now-string-typed schema).
526
+ for (const key in nextSchema) {
527
+ if (!Object.hasOwn(nextSchema, key)) continue;
528
+ if (key === "type") continue;
529
+ if (
530
+ Object.hasOwn(ALL_CCA_TYPE_SPECIFIC_KEYS, key) &&
531
+ !Object.hasOwn(chosenTypeAllowedKeys, key) &&
532
+ !Object.hasOwn(CLOUD_CODE_ASSIST_SHARED_SCHEMA_KEYS, key)
533
+ ) {
534
+ delete nextSchema[key];
535
+ }
536
+ }
537
+
508
538
  for (const key in mergedVariantFields) {
509
539
  if (!Object.hasOwn(mergedVariantFields, key)) continue;
540
+ // Drop type-specific keys that don't belong to the chosen type
541
+ if (!Object.hasOwn(chosenTypeAllowedKeys, key) && !Object.hasOwn(CLOUD_CODE_ASSIST_SHARED_SCHEMA_KEYS, key)) {
542
+ continue;
543
+ }
510
544
  const value = mergedVariantFields[key];
511
545
  const existingValue = nextSchema[key];
512
546
  if (existingValue !== undefined && !areJsonValuesEqual(existingValue, value)) {
@@ -774,7 +808,6 @@ export function normalizeSchema(value: unknown, options: NormalizeSchemaOptions)
774
808
  let normalized = normalizeSchemaNode(dereferenced, {
775
809
  ...options,
776
810
  insideProperties: false,
777
- epoch: epochNext(),
778
811
  });
779
812
  if (options.stripResidualCombinersFixpoint) {
780
813
  normalized = stripResidualCombiners(normalized);
@@ -1069,7 +1102,7 @@ function inferStrictPrimitiveTypeFromEnumOrConst(node: Record<string, unknown>):
1069
1102
  * so repeated calls (different providers, retries, batching) reuse the same
1070
1103
  * computed pair without re-walking the tree.
1071
1104
  */
1072
- const kStrictSchema = Symbol("pi.schema.strict");
1105
+ const kStrictSchema = Symbol("prometheus.schema.strict");
1073
1106
 
1074
1107
  /**
1075
1108
  * Detect schemas that strict mode *cannot* represent.
@@ -1404,6 +1437,21 @@ export function sanitizeSchemaForStrictMode(
1404
1437
  return sanitized;
1405
1438
  }
1406
1439
 
1440
+ /**
1441
+ * A node whose only constraining keyword is `anyOf` (annotations like
1442
+ * `description` aside). Only such nodes can be merged into an enclosing
1443
+ * union without changing semantics: sibling keywords (`type`, `enum`,
1444
+ * `properties`, …) apply conjunctively with `anyOf`, so spreading the
1445
+ * branches of a non-pure node would drop those constraints.
1446
+ */
1447
+ function isPureAnyOfNode(value: unknown): value is Record<string, unknown> & { anyOf: unknown[] } {
1448
+ if (!isJsonObject(value) || !Array.isArray(value.anyOf)) return false;
1449
+ for (const key in value) {
1450
+ if (key !== "anyOf" && key !== "description") return false;
1451
+ }
1452
+ return true;
1453
+ }
1454
+
1407
1455
  /**
1408
1456
  * Recursively enforces JSON Schema constraints required by OpenAI/Codex strict mode:
1409
1457
  * - `additionalProperties: false` on every object node
@@ -1472,6 +1520,10 @@ function enforceStrictSchemaBody(
1472
1520
  strictProperties[key] = processed;
1473
1521
  continue;
1474
1522
  }
1523
+ if (isPureAnyOfNode(processed)) {
1524
+ strictProperties[key] = { ...processed, anyOf: [...processed.anyOf, { type: "null" }] };
1525
+ continue;
1526
+ }
1475
1527
  if (isJsonObject(processed) && typeof processed.description === "string") {
1476
1528
  const { description, ...withoutDescription } = processed;
1477
1529
  strictProperties[key] = { anyOf: [withoutDescription, { type: "null" }], description };
@@ -1512,6 +1564,26 @@ function enforceStrictSchemaBody(
1512
1564
  );
1513
1565
  }
1514
1566
  }
1567
+ // Splice nested pure unions into the parent `anyOf`: `(A ∨ B) ∨ C` ≡ `A ∨ B ∨ C`.
1568
+ // Some strict-mode validators (e.g. DeepSeek behind OpenRouter) reject anyOf
1569
+ // branches that carry no `type`, which is exactly what a nested combinator
1570
+ // node looks like (#2270). Branch recursion above already flattened deeper
1571
+ // levels bottom-up, so a single pass suffices.
1572
+ if (Array.isArray(result.anyOf) && result.anyOf.some(isPureAnyOfNode)) {
1573
+ const flattened: unknown[] = [];
1574
+ for (const branch of result.anyOf) {
1575
+ if (!isPureAnyOfNode(branch)) {
1576
+ flattened.push(branch);
1577
+ continue;
1578
+ }
1579
+ flattened.push(...branch.anyOf);
1580
+ // Keep the inner annotation when the parent has none.
1581
+ if (typeof branch.description === "string" && result.description === undefined) {
1582
+ result.description = branch.description;
1583
+ }
1584
+ }
1585
+ result.anyOf = flattened;
1586
+ }
1515
1587
  for (const defsKey of ["$defs", "definitions"] as const) {
1516
1588
  if (result[defsKey] != null && typeof result[defsKey] === "object" && !Array.isArray(result[defsKey])) {
1517
1589
  const defs = result[defsKey] as Record<string, unknown>;
@@ -9,11 +9,13 @@
9
9
  *
10
10
  * Caveats: the stamp lives as long as the host object, even after callers
11
11
  * release their references to the cached value — only use this for caches
12
- * whose lifetime should match the host. Frozen hosts will throw on write in
13
- * strict mode; callers that may receive frozen input must handle that.
12
+ * whose lifetime should match the host. Frozen hosts cannot be stamped;
13
+ * `define` silently skips them, so memoization/visit-tracking degrades to
14
+ * best-effort (recompute on every call, no cycle protection) instead of
15
+ * throwing.
14
16
  */
15
-
16
17
  function define<T extends object>(target: T, key: symbol, value: unknown): void {
18
+ if (Object.isFrozen(target)) return;
17
19
  Object.defineProperty(target, key, { value, writable: true, configurable: true });
18
20
  }
19
21
 
@@ -38,7 +40,7 @@ export function stamp<T extends object, V>(target: T, key: symbol, compute: (tar
38
40
  * for (const child of node.children) walk(child, epoch);
39
41
  * }
40
42
  */
41
- const kEpoch = Symbol("pi.schema.epoch");
43
+ const kEpoch = Symbol("prometheus.schema.epoch");
42
44
  let __epoch = 0;
43
45
 
44
46
  export function epochNext(): number {
@@ -77,9 +79,15 @@ export function once<T extends object>(target: T, epoch: number): boolean {
77
79
  * finally { exit(node); }
78
80
  * }
79
81
  */
80
- const kDepth = Symbol("pi.schema.depth");
82
+ const kDepth = Symbol("prometheus.schema.depth");
81
83
 
82
- /** Returns `true` on first entry, `false` if `target` is already on the current path. */
84
+ /**
85
+ * Returns `true` on first entry, `false` if `target` is already on the
86
+ * current path. A `false` return does NOT deepen the counter — callers pair
87
+ * `exit` only with successful enters (`if (!enter(n)) bail; try {…} finally
88
+ * { exit(n); }`), so incrementing on the cycle branch would leak depth and
89
+ * make every later top-level walk of the same object misreport a cycle.
90
+ */
83
91
  export function enter<T extends object>(target: T): boolean {
84
92
  const slot = target as Record<symbol, number | undefined>;
85
93
  const cur = slot[kDepth];
@@ -87,11 +95,15 @@ export function enter<T extends object>(target: T): boolean {
87
95
  define(target, kDepth, 1);
88
96
  return true;
89
97
  }
90
- slot[kDepth] = cur + 1;
91
- return cur === 0;
98
+ if (cur !== 0) return false;
99
+ slot[kDepth] = 1;
100
+ return true;
92
101
  }
93
102
 
94
103
  export function exit<T extends object>(target: T): void {
95
- const slot = target as Record<symbol, number>;
96
- slot[kDepth]--;
104
+ const slot = target as Record<symbol, number | undefined>;
105
+ const cur = slot[kDepth];
106
+ // Frozen targets never received the kDepth stamp in `enter` — nothing to unwind.
107
+ if (cur === undefined) return;
108
+ slot[kDepth] = cur - 1;
97
109
  }