jeopi-ai 16.2.13

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 (598) hide show
  1. package/CHANGELOG.md +4347 -0
  2. package/README.md +1193 -0
  3. package/dist/types/api-registry.d.ts +30 -0
  4. package/dist/types/auth-broker/client.d.ts +73 -0
  5. package/dist/types/auth-broker/discover.d.ts +35 -0
  6. package/dist/types/auth-broker/index.d.ts +7 -0
  7. package/dist/types/auth-broker/refresher.d.ts +25 -0
  8. package/dist/types/auth-broker/remote-store.d.ts +102 -0
  9. package/dist/types/auth-broker/server.d.ts +43 -0
  10. package/dist/types/auth-broker/snapshot-cache.d.ts +17 -0
  11. package/dist/types/auth-broker/types.d.ts +107 -0
  12. package/dist/types/auth-broker/wire-schemas.d.ts +411 -0
  13. package/dist/types/auth-gateway/http.d.ts +39 -0
  14. package/dist/types/auth-gateway/index.d.ts +3 -0
  15. package/dist/types/auth-gateway/server.d.ts +36 -0
  16. package/dist/types/auth-gateway/types.d.ts +123 -0
  17. package/dist/types/auth-retry.d.ts +124 -0
  18. package/dist/types/auth-storage.d.ts +1026 -0
  19. package/dist/types/dialect/anthropic.d.ts +15 -0
  20. package/dist/types/dialect/catalog.d.ts +3 -0
  21. package/dist/types/dialect/coercion.d.ts +23 -0
  22. package/dist/types/dialect/deepseek.d.ts +14 -0
  23. package/dist/types/dialect/demotion.d.ts +23 -0
  24. package/dist/types/dialect/examples.d.ts +2 -0
  25. package/dist/types/dialect/factory.d.ts +3 -0
  26. package/dist/types/dialect/fenced-thinking.d.ts +53 -0
  27. package/dist/types/dialect/gemini.d.ts +17 -0
  28. package/dist/types/dialect/gemma.d.ts +15 -0
  29. package/dist/types/dialect/glm.d.ts +9 -0
  30. package/dist/types/dialect/harmony.d.ts +8 -0
  31. package/dist/types/dialect/hermes.d.ts +9 -0
  32. package/dist/types/dialect/history.d.ts +3 -0
  33. package/dist/types/dialect/index.d.ts +11 -0
  34. package/dist/types/dialect/inventory.d.ts +12 -0
  35. package/dist/types/dialect/kimi.d.ts +14 -0
  36. package/dist/types/dialect/minimax.d.ts +3 -0
  37. package/dist/types/dialect/owned-stream.d.ts +4 -0
  38. package/dist/types/dialect/qwen3.d.ts +9 -0
  39. package/dist/types/dialect/rendering.d.ts +45 -0
  40. package/dist/types/dialect/thinking.d.ts +6 -0
  41. package/dist/types/dialect/types.d.ts +69 -0
  42. package/dist/types/dialect/xml.d.ts +9 -0
  43. package/dist/types/error/abort.d.ts +14 -0
  44. package/dist/types/error/auth-classify.d.ts +16 -0
  45. package/dist/types/error/auth.d.ts +27 -0
  46. package/dist/types/error/aws.d.ts +23 -0
  47. package/dist/types/error/classes.d.ts +102 -0
  48. package/dist/types/error/finalize.d.ts +39 -0
  49. package/dist/types/error/flags.d.ts +79 -0
  50. package/dist/types/error/format.d.ts +20 -0
  51. package/dist/types/error/gateway.d.ts +20 -0
  52. package/dist/types/error/index.d.ts +13 -0
  53. package/dist/types/error/oauth.d.ts +43 -0
  54. package/dist/types/error/provider.d.ts +42 -0
  55. package/dist/types/error/rate-limit.d.ts +59 -0
  56. package/dist/types/error/retryable.d.ts +27 -0
  57. package/dist/types/error/validation.d.ts +32 -0
  58. package/dist/types/index.d.ts +49 -0
  59. package/dist/types/provider-details.d.ts +24 -0
  60. package/dist/types/providers/__tests__/google-auth.test.d.ts +1 -0
  61. package/dist/types/providers/__tests__/kimi-code-thinking.test.d.ts +1 -0
  62. package/dist/types/providers/__tests__/openai-codex-error.test.d.ts +1 -0
  63. package/dist/types/providers/amazon-bedrock.d.ts +39 -0
  64. package/dist/types/providers/anthropic-client.d.ts +94 -0
  65. package/dist/types/providers/anthropic-messages-server-schema.d.ts +497 -0
  66. package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
  67. package/dist/types/providers/anthropic-wire.d.ts +318 -0
  68. package/dist/types/providers/anthropic.d.ts +248 -0
  69. package/dist/types/providers/aws-credentials.d.ts +53 -0
  70. package/dist/types/providers/aws-eventstream.d.ts +39 -0
  71. package/dist/types/providers/aws-sigv4.d.ts +55 -0
  72. package/dist/types/providers/azure-openai-responses.d.ts +16 -0
  73. package/dist/types/providers/cursor.d.ts +91 -0
  74. package/dist/types/providers/devin.d.ts +12 -0
  75. package/dist/types/providers/error-message.d.ts +27 -0
  76. package/dist/types/providers/github-copilot-headers.d.ts +40 -0
  77. package/dist/types/providers/gitlab-duo-workflow.d.ts +254 -0
  78. package/dist/types/providers/gitlab-duo.d.ts +27 -0
  79. package/dist/types/providers/google-auth.d.ts +32 -0
  80. package/dist/types/providers/google-gemini-cli.d.ts +118 -0
  81. package/dist/types/providers/google-interactions.d.ts +65 -0
  82. package/dist/types/providers/google-shared.d.ts +203 -0
  83. package/dist/types/providers/google-types.d.ts +155 -0
  84. package/dist/types/providers/google-vertex.d.ts +7 -0
  85. package/dist/types/providers/google.d.ts +4 -0
  86. package/dist/types/providers/grammar.d.ts +1 -0
  87. package/dist/types/providers/kimi.d.ts +27 -0
  88. package/dist/types/providers/mock.d.ts +178 -0
  89. package/dist/types/providers/ollama.d.ts +7 -0
  90. package/dist/types/providers/openai-anthropic-shim.d.ts +31 -0
  91. package/dist/types/providers/openai-chat-server-schema.d.ts +695 -0
  92. package/dist/types/providers/openai-chat-server.d.ts +16 -0
  93. package/dist/types/providers/openai-chat-wire.d.ts +644 -0
  94. package/dist/types/providers/openai-codex/request-transformer.d.ts +54 -0
  95. package/dist/types/providers/openai-codex/response-handler.d.ts +26 -0
  96. package/dist/types/providers/openai-codex-responses.d.ts +108 -0
  97. package/dist/types/providers/openai-completions.d.ts +45 -0
  98. package/dist/types/providers/openai-reasoning-fallback.d.ts +25 -0
  99. package/dist/types/providers/openai-responses-server-schema.d.ts +349 -0
  100. package/dist/types/providers/openai-responses-server.d.ts +17 -0
  101. package/dist/types/providers/openai-responses-wire.d.ts +6065 -0
  102. package/dist/types/providers/openai-responses.d.ts +126 -0
  103. package/dist/types/providers/openai-shared.d.ts +506 -0
  104. package/dist/types/providers/pi-native-client.d.ts +13 -0
  105. package/dist/types/providers/pi-native-server.d.ts +69 -0
  106. package/dist/types/providers/register-builtins.d.ts +32 -0
  107. package/dist/types/providers/synthetic.d.ts +26 -0
  108. package/dist/types/providers/transform-messages.d.ts +11 -0
  109. package/dist/types/providers/vision-guard.d.ts +20 -0
  110. package/dist/types/registry/aimlapi.d.ts +4 -0
  111. package/dist/types/registry/alibaba-coding-plan.d.ts +8 -0
  112. package/dist/types/registry/amazon-bedrock.d.ts +5 -0
  113. package/dist/types/registry/anthropic.d.ts +10 -0
  114. package/dist/types/registry/api-key-login.d.ts +42 -0
  115. package/dist/types/registry/api-key-validation.d.ts +43 -0
  116. package/dist/types/registry/azure.d.ts +4 -0
  117. package/dist/types/registry/cerebras.d.ts +7 -0
  118. package/dist/types/registry/cloudflare-ai-gateway.d.ts +13 -0
  119. package/dist/types/registry/coreweave.d.ts +7 -0
  120. package/dist/types/registry/cursor.d.ts +7 -0
  121. package/dist/types/registry/deepseek.d.ts +8 -0
  122. package/dist/types/registry/derived.d.ts +5 -0
  123. package/dist/types/registry/devin.d.ts +8 -0
  124. package/dist/types/registry/firepass.d.ts +16 -0
  125. package/dist/types/registry/fireworks.d.ts +7 -0
  126. package/dist/types/registry/github-copilot.d.ts +7 -0
  127. package/dist/types/registry/gitlab-duo-workflow.d.ts +10 -0
  128. package/dist/types/registry/gitlab-duo.d.ts +9 -0
  129. package/dist/types/registry/google-antigravity.d.ts +9 -0
  130. package/dist/types/registry/google-gemini-cli.d.ts +9 -0
  131. package/dist/types/registry/google-vertex.d.ts +5 -0
  132. package/dist/types/registry/google.d.ts +4 -0
  133. package/dist/types/registry/groq.d.ts +4 -0
  134. package/dist/types/registry/huggingface.d.ts +7 -0
  135. package/dist/types/registry/index.d.ts +4 -0
  136. package/dist/types/registry/kagi.d.ts +14 -0
  137. package/dist/types/registry/kilo.d.ts +7 -0
  138. package/dist/types/registry/kimi-code.d.ts +7 -0
  139. package/dist/types/registry/litellm.d.ts +13 -0
  140. package/dist/types/registry/llama-cpp.d.ts +8 -0
  141. package/dist/types/registry/lm-studio.d.ts +8 -0
  142. package/dist/types/registry/minimax-code-cn.d.ts +6 -0
  143. package/dist/types/registry/minimax-code.d.ts +6 -0
  144. package/dist/types/registry/minimax.d.ts +4 -0
  145. package/dist/types/registry/mistral.d.ts +4 -0
  146. package/dist/types/registry/moonshot.d.ts +7 -0
  147. package/dist/types/registry/nanogpt.d.ts +7 -0
  148. package/dist/types/registry/nvidia.d.ts +7 -0
  149. package/dist/types/registry/oauth/__tests__/xai-oauth.test.d.ts +1 -0
  150. package/dist/types/registry/oauth/anthropic.d.ts +23 -0
  151. package/dist/types/registry/oauth/callback-server.d.ts +72 -0
  152. package/dist/types/registry/oauth/cursor.d.ts +15 -0
  153. package/dist/types/registry/oauth/devin.d.ts +5 -0
  154. package/dist/types/registry/oauth/github-copilot.d.ts +30 -0
  155. package/dist/types/registry/oauth/gitlab-duo-workflow.d.ts +6 -0
  156. package/dist/types/registry/oauth/gitlab-duo.d.ts +3 -0
  157. package/dist/types/registry/oauth/google-antigravity.d.ts +11 -0
  158. package/dist/types/registry/oauth/google-gemini-cli.d.ts +10 -0
  159. package/dist/types/registry/oauth/google-oauth-shared.d.ts +22 -0
  160. package/dist/types/registry/oauth/index.d.ts +64 -0
  161. package/dist/types/registry/oauth/kimi.d.ts +21 -0
  162. package/dist/types/registry/oauth/minimax-code.d.ts +27 -0
  163. package/dist/types/registry/oauth/openai-codex.d.ts +33 -0
  164. package/dist/types/registry/oauth/opencode.d.ts +18 -0
  165. package/dist/types/registry/oauth/perplexity.d.ts +9 -0
  166. package/dist/types/registry/oauth/pkce.d.ts +8 -0
  167. package/dist/types/registry/oauth/types.d.ts +56 -0
  168. package/dist/types/registry/oauth/wafer.d.ts +1 -0
  169. package/dist/types/registry/oauth/xai-oauth.d.ts +52 -0
  170. package/dist/types/registry/oauth/xiaomi.d.ts +25 -0
  171. package/dist/types/registry/ollama-cloud.d.ts +7 -0
  172. package/dist/types/registry/ollama.d.ts +12 -0
  173. package/dist/types/registry/openai-codex-device.d.ts +8 -0
  174. package/dist/types/registry/openai-codex.d.ts +9 -0
  175. package/dist/types/registry/openai.d.ts +4 -0
  176. package/dist/types/registry/opencode-go.d.ts +6 -0
  177. package/dist/types/registry/opencode-zen.d.ts +6 -0
  178. package/dist/types/registry/openrouter.d.ts +13 -0
  179. package/dist/types/registry/parallel.d.ts +14 -0
  180. package/dist/types/registry/perplexity.d.ts +7 -0
  181. package/dist/types/registry/qianfan.d.ts +7 -0
  182. package/dist/types/registry/qwen-portal.d.ts +7 -0
  183. package/dist/types/registry/registry.d.ts +303 -0
  184. package/dist/types/registry/sakana.d.ts +7 -0
  185. package/dist/types/registry/synthetic.d.ts +6 -0
  186. package/dist/types/registry/tavily.d.ts +14 -0
  187. package/dist/types/registry/together.d.ts +6 -0
  188. package/dist/types/registry/types.d.ts +51 -0
  189. package/dist/types/registry/umans.d.ts +7 -0
  190. package/dist/types/registry/venice.d.ts +13 -0
  191. package/dist/types/registry/vercel-ai-gateway.d.ts +7 -0
  192. package/dist/types/registry/vllm.d.ts +7 -0
  193. package/dist/types/registry/wafer-serverless.d.ts +6 -0
  194. package/dist/types/registry/xai-oauth.d.ts +7 -0
  195. package/dist/types/registry/xai.d.ts +4 -0
  196. package/dist/types/registry/xiaomi-token-plan-ams.d.ts +6 -0
  197. package/dist/types/registry/xiaomi-token-plan-cn.d.ts +6 -0
  198. package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +6 -0
  199. package/dist/types/registry/xiaomi.d.ts +6 -0
  200. package/dist/types/registry/zai.d.ts +7 -0
  201. package/dist/types/registry/zenmux.d.ts +7 -0
  202. package/dist/types/registry/zhipu-coding-plan.d.ts +7 -0
  203. package/dist/types/stream.d.ts +44 -0
  204. package/dist/types/types.d.ts +715 -0
  205. package/dist/types/usage/claude.d.ts +4 -0
  206. package/dist/types/usage/gemini.d.ts +2 -0
  207. package/dist/types/usage/github-copilot.d.ts +7 -0
  208. package/dist/types/usage/google-antigravity.d.ts +15 -0
  209. package/dist/types/usage/kimi.d.ts +2 -0
  210. package/dist/types/usage/minimax-code.d.ts +2 -0
  211. package/dist/types/usage/ollama.d.ts +5 -0
  212. package/dist/types/usage/openai-codex-base-url.d.ts +18 -0
  213. package/dist/types/usage/openai-codex-reset.d.ts +79 -0
  214. package/dist/types/usage/openai-codex.d.ts +3 -0
  215. package/dist/types/usage/opencode-go.d.ts +2 -0
  216. package/dist/types/usage/shared.d.ts +1 -0
  217. package/dist/types/usage/zai.d.ts +2 -0
  218. package/dist/types/usage.d.ts +346 -0
  219. package/dist/types/utils/abort.d.ts +25 -0
  220. package/dist/types/utils/anthropic-auth.d.ts +35 -0
  221. package/dist/types/utils/block-symbols.d.ts +20 -0
  222. package/dist/types/utils/deterministic-id.d.ts +16 -0
  223. package/dist/types/utils/empty-completion-retry.d.ts +21 -0
  224. package/dist/types/utils/event-stream.d.ts +30 -0
  225. package/dist/types/utils/foundry.d.ts +1 -0
  226. package/dist/types/utils/google-validation.d.ts +2 -0
  227. package/dist/types/utils/harmony-leak.d.ts +118 -0
  228. package/dist/types/utils/http-inspector.d.ts +30 -0
  229. package/dist/types/utils/idle-iterator.d.ts +137 -0
  230. package/dist/types/utils/leaked-thinking-stream.d.ts +29 -0
  231. package/dist/types/utils/openai-http.d.ts +54 -0
  232. package/dist/types/utils/openrouter-headers.d.ts +1 -0
  233. package/dist/types/utils/parse-bind.d.ts +23 -0
  234. package/dist/types/utils/provider-response.d.ts +3 -0
  235. package/dist/types/utils/proxy.d.ts +29 -0
  236. package/dist/types/utils/request-debug.d.ts +29 -0
  237. package/dist/types/utils/retry-after.d.ts +4 -0
  238. package/dist/types/utils/retry.d.ts +14 -0
  239. package/dist/types/utils/schema/adapt.d.ts +24 -0
  240. package/dist/types/utils/schema/compatibility.d.ts +30 -0
  241. package/dist/types/utils/schema/dereference.d.ts +11 -0
  242. package/dist/types/utils/schema/draft.d.ts +10 -0
  243. package/dist/types/utils/schema/equality.d.ts +4 -0
  244. package/dist/types/utils/schema/fields.d.ts +54 -0
  245. package/dist/types/utils/schema/index.d.ts +15 -0
  246. package/dist/types/utils/schema/json-schema-validator.d.ts +20 -0
  247. package/dist/types/utils/schema/meta-validator.d.ts +2 -0
  248. package/dist/types/utils/schema/normalize.d.ts +124 -0
  249. package/dist/types/utils/schema/spill.d.ts +8 -0
  250. package/dist/types/utils/schema/stamps.d.ts +17 -0
  251. package/dist/types/utils/schema/strict-tool-validation.d.ts +16 -0
  252. package/dist/types/utils/schema/types.d.ts +4 -0
  253. package/dist/types/utils/schema/typescript.d.ts +18 -0
  254. package/dist/types/utils/schema/wire.d.ts +92 -0
  255. package/dist/types/utils/schema/zod-decontaminate.d.ts +31 -0
  256. package/dist/types/utils/sdk-stream-timeout.d.ts +33 -0
  257. package/dist/types/utils/sse-debug.d.ts +5 -0
  258. package/dist/types/utils/stream-markup-healing.d.ts +87 -0
  259. package/dist/types/utils/thinking-loop.d.ts +102 -0
  260. package/dist/types/utils/tool-call-loop-guard.d.ts +26 -0
  261. package/dist/types/utils/tool-choice.d.ts +50 -0
  262. package/dist/types/utils/validation.d.ts +42 -0
  263. package/dist/types/utils.d.ts +24 -0
  264. package/package.json +139 -0
  265. package/src/api-registry.ts +109 -0
  266. package/src/auth-broker/client.ts +359 -0
  267. package/src/auth-broker/discover.ts +222 -0
  268. package/src/auth-broker/index.ts +7 -0
  269. package/src/auth-broker/refresher.ts +117 -0
  270. package/src/auth-broker/remote-store.ts +657 -0
  271. package/src/auth-broker/server.ts +646 -0
  272. package/src/auth-broker/snapshot-cache.ts +191 -0
  273. package/src/auth-broker/types.ts +130 -0
  274. package/src/auth-broker/wire-schemas.ts +249 -0
  275. package/src/auth-gateway/http.ts +194 -0
  276. package/src/auth-gateway/index.ts +3 -0
  277. package/src/auth-gateway/server.ts +802 -0
  278. package/src/auth-gateway/types.ts +151 -0
  279. package/src/auth-retry.ts +250 -0
  280. package/src/auth-storage.ts +5576 -0
  281. package/src/dialect/anthropic.md +31 -0
  282. package/src/dialect/anthropic.ts +608 -0
  283. package/src/dialect/catalog.ts +29 -0
  284. package/src/dialect/coercion.ts +136 -0
  285. package/src/dialect/deepseek.md +24 -0
  286. package/src/dialect/deepseek.ts +609 -0
  287. package/src/dialect/demotion.ts +36 -0
  288. package/src/dialect/examples.ts +33 -0
  289. package/src/dialect/factory.ts +34 -0
  290. package/src/dialect/fenced-thinking.ts +184 -0
  291. package/src/dialect/gemini.md +44 -0
  292. package/src/dialect/gemini.ts +597 -0
  293. package/src/dialect/gemma.md +33 -0
  294. package/src/dialect/gemma.ts +387 -0
  295. package/src/dialect/glm.md +32 -0
  296. package/src/dialect/glm.ts +456 -0
  297. package/src/dialect/harmony.md +31 -0
  298. package/src/dialect/harmony.ts +346 -0
  299. package/src/dialect/hermes.md +25 -0
  300. package/src/dialect/hermes.ts +206 -0
  301. package/src/dialect/history.ts +81 -0
  302. package/src/dialect/index.ts +15 -0
  303. package/src/dialect/inventory.ts +73 -0
  304. package/src/dialect/kimi.md +24 -0
  305. package/src/dialect/kimi.ts +340 -0
  306. package/src/dialect/minimax.md +31 -0
  307. package/src/dialect/minimax.ts +95 -0
  308. package/src/dialect/owned-stream.ts +470 -0
  309. package/src/dialect/prompt-template.md +12 -0
  310. package/src/dialect/qwen3.md +28 -0
  311. package/src/dialect/qwen3.ts +240 -0
  312. package/src/dialect/rendering.ts +249 -0
  313. package/src/dialect/thinking.ts +122 -0
  314. package/src/dialect/types.ts +57 -0
  315. package/src/dialect/xml.md +22 -0
  316. package/src/dialect/xml.ts +90 -0
  317. package/src/error/abort.ts +18 -0
  318. package/src/error/auth-classify.ts +30 -0
  319. package/src/error/auth.ts +48 -0
  320. package/src/error/aws.ts +31 -0
  321. package/src/error/classes.ts +186 -0
  322. package/src/error/finalize.ts +69 -0
  323. package/src/error/flags.ts +506 -0
  324. package/src/error/format.ts +45 -0
  325. package/src/error/gateway.ts +96 -0
  326. package/src/error/index.ts +13 -0
  327. package/src/error/oauth.ts +58 -0
  328. package/src/error/provider.ts +62 -0
  329. package/src/error/rate-limit.ts +161 -0
  330. package/src/error/retryable.ts +70 -0
  331. package/src/error/validation.ts +44 -0
  332. package/src/index.ts +49 -0
  333. package/src/provider-details.ts +90 -0
  334. package/src/providers/__tests__/google-auth.test.ts +144 -0
  335. package/src/providers/__tests__/kimi-code-thinking.test.ts +112 -0
  336. package/src/providers/__tests__/openai-codex-error.test.ts +84 -0
  337. package/src/providers/amazon-bedrock.ts +1042 -0
  338. package/src/providers/anthropic-client.ts +295 -0
  339. package/src/providers/anthropic-messages-server-schema.ts +252 -0
  340. package/src/providers/anthropic-messages-server.ts +756 -0
  341. package/src/providers/anthropic-wire.ts +318 -0
  342. package/src/providers/anthropic.ts +4078 -0
  343. package/src/providers/aws-credentials.ts +586 -0
  344. package/src/providers/aws-eventstream.ts +181 -0
  345. package/src/providers/aws-sigv4.ts +218 -0
  346. package/src/providers/azure-openai-responses.ts +382 -0
  347. package/src/providers/cursor/proto/agent.proto +3526 -0
  348. package/src/providers/cursor/proto/buf.gen.yaml +6 -0
  349. package/src/providers/cursor/proto/buf.yaml +17 -0
  350. package/src/providers/cursor.ts +2695 -0
  351. package/src/providers/devin/proto/buf/validate/validate.proto +468 -0
  352. package/src/providers/devin/proto/buf.gen.yaml +33 -0
  353. package/src/providers/devin/proto/buf.yaml +17 -0
  354. package/src/providers/devin/proto/cel/expr/checked.proto +103 -0
  355. package/src/providers/devin/proto/cel/expr/eval.proto +38 -0
  356. package/src/providers/devin/proto/cel/expr/explain.proto +15 -0
  357. package/src/providers/devin/proto/cel/expr/syntax.proto +113 -0
  358. package/src/providers/devin/proto/cel/expr/value.proto +41 -0
  359. package/src/providers/devin/proto/connectext/grpc/status/v1/status.proto +11 -0
  360. package/src/providers/devin/proto/errorspb/errors.proto +56 -0
  361. package/src/providers/devin/proto/errorspb/hintdetail.proto +7 -0
  362. package/src/providers/devin/proto/errorspb/markers.proto +10 -0
  363. package/src/providers/devin/proto/errorspb/tags.proto +12 -0
  364. package/src/providers/devin/proto/errorspb/testing.proto +6 -0
  365. package/src/providers/devin/proto/exa/analytics_pb/analytics.proto +188 -0
  366. package/src/providers/devin/proto/exa/api_server_pb/api_server.proto +2461 -0
  367. package/src/providers/devin/proto/exa/auth_pb/auth.proto +19 -0
  368. package/src/providers/devin/proto/exa/auto_cascade_common_pb/auto_cascade_common.proto +79 -0
  369. package/src/providers/devin/proto/exa/browser_preview_pb/browser_preview.proto +32 -0
  370. package/src/providers/devin/proto/exa/bug_checker_pb/bug_checker.proto +22 -0
  371. package/src/providers/devin/proto/exa/cascade_plugins_pb/cascade_plugins.proto +262 -0
  372. package/src/providers/devin/proto/exa/chat_client_server_pb/chat_client_server.proto +57 -0
  373. package/src/providers/devin/proto/exa/chat_pb/chat.proto +449 -0
  374. package/src/providers/devin/proto/exa/code_edit/code_edit_pb/code_edit.proto +186 -0
  375. package/src/providers/devin/proto/exa/codeium_common_pb/codeium_common.proto +4157 -0
  376. package/src/providers/devin/proto/exa/context_module_pb/context_module.proto +175 -0
  377. package/src/providers/devin/proto/exa/cortex_pb/cortex.proto +3268 -0
  378. package/src/providers/devin/proto/exa/dev_pb/dev.proto +26 -0
  379. package/src/providers/devin/proto/exa/diff_action_pb/diff_action.proto +75 -0
  380. package/src/providers/devin/proto/exa/eval/pr_eval/datasets_pb/datasets.proto +103 -0
  381. package/src/providers/devin/proto/exa/eval_pb/eval.proto +1315 -0
  382. package/src/providers/devin/proto/exa/extension_server_pb/extension_server.proto +556 -0
  383. package/src/providers/devin/proto/exa/file_system_provider_pb/file_system_provider.proto +75 -0
  384. package/src/providers/devin/proto/exa/index_pb/index.proto +461 -0
  385. package/src/providers/devin/proto/exa/knowledge_base_pb/knowledge_base.proto +144 -0
  386. package/src/providers/devin/proto/exa/language_server_pb/language_server.proto +2385 -0
  387. package/src/providers/devin/proto/exa/model_management_pb/model_management.proto +186 -0
  388. package/src/providers/devin/proto/exa/opensearch_clients_pb/opensearch_clients.proto +503 -0
  389. package/src/providers/devin/proto/exa/product_analytics_pb/product_analytics.proto +37 -0
  390. package/src/providers/devin/proto/exa/prompt_pb/prompt.proto +92 -0
  391. package/src/providers/devin/proto/exa/reactive_component_pb/reactive_component.proto +96 -0
  392. package/src/providers/devin/proto/exa/seat_management_pb/seat_management.proto +2680 -0
  393. package/src/providers/devin/proto/exa/tokenizer_pb/tokenizer.proto +37 -0
  394. package/src/providers/devin/proto/exa/trainer_pb/config.proto +647 -0
  395. package/src/providers/devin/proto/exa/tree_sitter/language_data_pb/language_data.proto +14 -0
  396. package/src/providers/devin/proto/exa/trust_pb/trust.proto +157 -0
  397. package/src/providers/devin/proto/exa/user_analytics_pb/user_analytics.proto +519 -0
  398. package/src/providers/devin/proto/google.golang.org/appengine/internal/base/api_base.proto +28 -0
  399. package/src/providers/devin/proto/google.golang.org/appengine/internal/datastore/datastore_v3.proto +484 -0
  400. package/src/providers/devin/proto/google.golang.org/appengine/internal/log/log_service.proto +136 -0
  401. package/src/providers/devin/proto/google.golang.org/appengine/internal/remote_api/remote_api.proto +42 -0
  402. package/src/providers/devin/proto/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto +61 -0
  403. package/src/providers/devin/proto/grpc/binlog/v1/binarylog.proto +84 -0
  404. package/src/providers/devin/proto/io/prometheus/client/metrics.proto +98 -0
  405. package/src/providers/devin.ts +577 -0
  406. package/src/providers/error-message.ts +21 -0
  407. package/src/providers/github-copilot-headers.ts +141 -0
  408. package/src/providers/gitlab-duo-workflow-chatml-note.md +1 -0
  409. package/src/providers/gitlab-duo-workflow.ts +3058 -0
  410. package/src/providers/gitlab-duo.ts +395 -0
  411. package/src/providers/google-auth.ts +350 -0
  412. package/src/providers/google-gemini-cli.ts +1362 -0
  413. package/src/providers/google-interactions.ts +753 -0
  414. package/src/providers/google-shared.ts +1103 -0
  415. package/src/providers/google-types.ts +180 -0
  416. package/src/providers/google-vertex.ts +183 -0
  417. package/src/providers/google.ts +87 -0
  418. package/src/providers/grammar.ts +70 -0
  419. package/src/providers/kimi.ts +52 -0
  420. package/src/providers/mock.ts +507 -0
  421. package/src/providers/ollama.ts +773 -0
  422. package/src/providers/openai-anthropic-shim.ts +152 -0
  423. package/src/providers/openai-chat-server-schema.ts +242 -0
  424. package/src/providers/openai-chat-server.ts +715 -0
  425. package/src/providers/openai-chat-wire.ts +847 -0
  426. package/src/providers/openai-codex/request-transformer.ts +295 -0
  427. package/src/providers/openai-codex/response-handler.ts +102 -0
  428. package/src/providers/openai-codex-responses.ts +3468 -0
  429. package/src/providers/openai-completions.ts +2173 -0
  430. package/src/providers/openai-reasoning-fallback.ts +269 -0
  431. package/src/providers/openai-responses-reasoning-suppression.md +1 -0
  432. package/src/providers/openai-responses-server-schema.ts +282 -0
  433. package/src/providers/openai-responses-server.ts +1280 -0
  434. package/src/providers/openai-responses-wire.ts +6391 -0
  435. package/src/providers/openai-responses.ts +1022 -0
  436. package/src/providers/openai-shared.ts +2648 -0
  437. package/src/providers/pi-native-client.ts +266 -0
  438. package/src/providers/pi-native-server.ts +242 -0
  439. package/src/providers/register-builtins.ts +475 -0
  440. package/src/providers/synthetic.ts +50 -0
  441. package/src/providers/transform-messages.ts +787 -0
  442. package/src/providers/vision-guard.ts +54 -0
  443. package/src/registry/aimlapi.ts +6 -0
  444. package/src/registry/alibaba-coding-plan.ts +95 -0
  445. package/src/registry/amazon-bedrock.ts +22 -0
  446. package/src/registry/anthropic.ts +26 -0
  447. package/src/registry/api-key-login.ts +112 -0
  448. package/src/registry/api-key-validation.ts +161 -0
  449. package/src/registry/azure.ts +6 -0
  450. package/src/registry/cerebras.ts +23 -0
  451. package/src/registry/cloudflare-ai-gateway.ts +45 -0
  452. package/src/registry/coreweave.ts +40 -0
  453. package/src/registry/cursor.ts +20 -0
  454. package/src/registry/deepseek.ts +46 -0
  455. package/src/registry/derived.ts +9 -0
  456. package/src/registry/devin.ts +15 -0
  457. package/src/registry/firepass.ts +32 -0
  458. package/src/registry/fireworks.ts +28 -0
  459. package/src/registry/github-copilot.ts +22 -0
  460. package/src/registry/gitlab-duo-workflow.ts +20 -0
  461. package/src/registry/gitlab-duo.ts +19 -0
  462. package/src/registry/google-antigravity.ts +22 -0
  463. package/src/registry/google-gemini-cli.ts +22 -0
  464. package/src/registry/google-vertex.ts +38 -0
  465. package/src/registry/google.ts +6 -0
  466. package/src/registry/groq.ts +6 -0
  467. package/src/registry/huggingface.ts +29 -0
  468. package/src/registry/index.ts +4 -0
  469. package/src/registry/kagi.ts +46 -0
  470. package/src/registry/kilo.ts +114 -0
  471. package/src/registry/kimi-code.ts +17 -0
  472. package/src/registry/litellm.ts +45 -0
  473. package/src/registry/llama-cpp.ts +35 -0
  474. package/src/registry/lm-studio.ts +31 -0
  475. package/src/registry/minimax-code-cn.ts +12 -0
  476. package/src/registry/minimax-code.ts +12 -0
  477. package/src/registry/minimax.ts +6 -0
  478. package/src/registry/mistral.ts +6 -0
  479. package/src/registry/moonshot.ts +22 -0
  480. package/src/registry/nanogpt.ts +22 -0
  481. package/src/registry/nvidia.ts +61 -0
  482. package/src/registry/oauth/__tests__/xai-oauth.test.ts +104 -0
  483. package/src/registry/oauth/anthropic.ts +311 -0
  484. package/src/registry/oauth/callback-server.ts +315 -0
  485. package/src/registry/oauth/cursor.ts +171 -0
  486. package/src/registry/oauth/devin.ts +124 -0
  487. package/src/registry/oauth/github-copilot.ts +369 -0
  488. package/src/registry/oauth/gitlab-duo-workflow.ts +146 -0
  489. package/src/registry/oauth/gitlab-duo.ts +222 -0
  490. package/src/registry/oauth/google-antigravity.ts +209 -0
  491. package/src/registry/oauth/google-gemini-cli.ts +273 -0
  492. package/src/registry/oauth/google-oauth-shared.ts +125 -0
  493. package/src/registry/oauth/index.ts +269 -0
  494. package/src/registry/oauth/kimi.ts +289 -0
  495. package/src/registry/oauth/minimax-code.ts +53 -0
  496. package/src/registry/oauth/oauth.html +311 -0
  497. package/src/registry/oauth/openai-codex.ts +364 -0
  498. package/src/registry/oauth/opencode.ts +50 -0
  499. package/src/registry/oauth/perplexity.ts +228 -0
  500. package/src/registry/oauth/pkce.ts +18 -0
  501. package/src/registry/oauth/types.ts +65 -0
  502. package/src/registry/oauth/wafer.ts +24 -0
  503. package/src/registry/oauth/xai-oauth.ts +394 -0
  504. package/src/registry/oauth/xiaomi.ts +211 -0
  505. package/src/registry/ollama-cloud.ts +36 -0
  506. package/src/registry/ollama.ts +43 -0
  507. package/src/registry/openai-codex-device.ts +18 -0
  508. package/src/registry/openai-codex.ts +19 -0
  509. package/src/registry/openai.ts +6 -0
  510. package/src/registry/opencode-go.ts +12 -0
  511. package/src/registry/opencode-zen.ts +12 -0
  512. package/src/registry/openrouter.ts +28 -0
  513. package/src/registry/parallel.ts +45 -0
  514. package/src/registry/perplexity.ts +13 -0
  515. package/src/registry/qianfan.ts +27 -0
  516. package/src/registry/qwen-portal.ts +50 -0
  517. package/src/registry/registry.ts +161 -0
  518. package/src/registry/sakana.ts +22 -0
  519. package/src/registry/synthetic.ts +21 -0
  520. package/src/registry/tavily.ts +45 -0
  521. package/src/registry/together.ts +22 -0
  522. package/src/registry/types.ts +56 -0
  523. package/src/registry/umans.ts +23 -0
  524. package/src/registry/venice.ts +33 -0
  525. package/src/registry/vercel-ai-gateway.ts +38 -0
  526. package/src/registry/vllm.ts +34 -0
  527. package/src/registry/wafer-serverless.ts +12 -0
  528. package/src/registry/xai-oauth.ts +17 -0
  529. package/src/registry/xai.ts +6 -0
  530. package/src/registry/xiaomi-token-plan-ams.ts +12 -0
  531. package/src/registry/xiaomi-token-plan-cn.ts +12 -0
  532. package/src/registry/xiaomi-token-plan-sgp.ts +12 -0
  533. package/src/registry/xiaomi.ts +12 -0
  534. package/src/registry/zai.ts +27 -0
  535. package/src/registry/zenmux.ts +22 -0
  536. package/src/registry/zhipu-coding-plan.ts +27 -0
  537. package/src/stream.ts +1778 -0
  538. package/src/types.ts +856 -0
  539. package/src/usage/claude.ts +485 -0
  540. package/src/usage/gemini.ts +258 -0
  541. package/src/usage/github-copilot.ts +424 -0
  542. package/src/usage/google-antigravity.ts +497 -0
  543. package/src/usage/kimi.ts +271 -0
  544. package/src/usage/minimax-code.ts +30 -0
  545. package/src/usage/ollama.ts +41 -0
  546. package/src/usage/openai-codex-base-url.ts +35 -0
  547. package/src/usage/openai-codex-reset.ts +174 -0
  548. package/src/usage/openai-codex.ts +535 -0
  549. package/src/usage/opencode-go.ts +89 -0
  550. package/src/usage/shared.ts +10 -0
  551. package/src/usage/zai.ts +321 -0
  552. package/src/usage.ts +333 -0
  553. package/src/utils/abort.ts +67 -0
  554. package/src/utils/anthropic-auth.ts +93 -0
  555. package/src/utils/block-symbols.ts +32 -0
  556. package/src/utils/deterministic-id.ts +20 -0
  557. package/src/utils/empty-completion-retry.ts +159 -0
  558. package/src/utils/event-stream.ts +171 -0
  559. package/src/utils/foundry.ts +8 -0
  560. package/src/utils/google-validation.ts +25 -0
  561. package/src/utils/harmony-leak.ts +456 -0
  562. package/src/utils/http-inspector.ts +168 -0
  563. package/src/utils/idle-iterator.ts +473 -0
  564. package/src/utils/leaked-thinking-stream.ts +294 -0
  565. package/src/utils/openai-http.ts +122 -0
  566. package/src/utils/openrouter-headers.ts +12 -0
  567. package/src/utils/parse-bind.ts +56 -0
  568. package/src/utils/provider-response.ts +30 -0
  569. package/src/utils/proxy.ts +240 -0
  570. package/src/utils/request-debug.ts +351 -0
  571. package/src/utils/retry-after.ts +110 -0
  572. package/src/utils/retry.ts +59 -0
  573. package/src/utils/schema/CONSTRAINTS.md +166 -0
  574. package/src/utils/schema/adapt.ts +36 -0
  575. package/src/utils/schema/compatibility.ts +435 -0
  576. package/src/utils/schema/dereference.ts +98 -0
  577. package/src/utils/schema/draft.ts +341 -0
  578. package/src/utils/schema/equality.ts +97 -0
  579. package/src/utils/schema/fields.ts +207 -0
  580. package/src/utils/schema/index.ts +15 -0
  581. package/src/utils/schema/json-schema-validator.ts +595 -0
  582. package/src/utils/schema/meta-validator.ts +167 -0
  583. package/src/utils/schema/normalize.ts +1901 -0
  584. package/src/utils/schema/spill.ts +43 -0
  585. package/src/utils/schema/stamps.ts +109 -0
  586. package/src/utils/schema/strict-tool-validation.ts +117 -0
  587. package/src/utils/schema/types.ts +10 -0
  588. package/src/utils/schema/typescript.ts +198 -0
  589. package/src/utils/schema/wire.ts +789 -0
  590. package/src/utils/schema/zod-decontaminate.ts +331 -0
  591. package/src/utils/sdk-stream-timeout.ts +43 -0
  592. package/src/utils/sse-debug.ts +18 -0
  593. package/src/utils/stream-markup-healing.ts +247 -0
  594. package/src/utils/thinking-loop.ts +552 -0
  595. package/src/utils/tool-call-loop-guard.ts +107 -0
  596. package/src/utils/tool-choice.ts +99 -0
  597. package/src/utils/validation.ts +1507 -0
  598. package/src/utils.ts +171 -0
@@ -0,0 +1,1280 @@
1
+ /**
2
+ * OpenAI Responses HTTP wire-format ↔ omp Context bridge for the auth-gateway.
3
+ *
4
+ * Inbound: parses `POST /v1/responses` request bodies into a {@link ParsedRequest}.
5
+ * Outbound: encodes omp's {@link AssistantMessage} (and event stream) back into
6
+ * the documented `response.*` SSE taxonomy or the non-streaming JSON shape.
7
+ *
8
+ * Spec: https://platform.openai.com/docs/api-reference/responses
9
+ * Inverse direction (source-of-truth for item shapes): ../../providers/openai-responses.ts
10
+ */
11
+
12
+ import { type } from "arktype";
13
+ import { logger } from "jeopi-utils";
14
+ import { resolvePromptCacheKey } from "../auth-gateway/http";
15
+ import type { AuthGatewayStreamControl, AuthGatewayParsedRequest as ParsedRequest } from "../auth-gateway/types";
16
+ import * as AIError from "../error";
17
+ import type {
18
+ AssistantMessage,
19
+ AssistantMessageEventStream,
20
+ Context,
21
+ Message,
22
+ TextContent,
23
+ ThinkingContent,
24
+ Tool,
25
+ ToolCall,
26
+ } from "../types";
27
+ import {
28
+ type OpenAIResponsesFunctionCallItem,
29
+ type OpenAIResponsesFunctionCallOutputItem,
30
+ type OpenAIResponsesInputContent,
31
+ type OpenAIResponsesOutputContent,
32
+ type OpenAIResponsesReasoningItem,
33
+ type OpenAIResponsesTool,
34
+ openaiResponsesRequestSchema,
35
+ } from "./openai-responses-server-schema";
36
+ import { encodeTextSignatureV1, parseTextSignature } from "./openai-shared";
37
+
38
+ export type { ParsedRequest };
39
+
40
+ // ─── narrow guards ──────────────────────────────────────────────────────────
41
+
42
+ function isReasoningEffort(value: unknown): value is NonNullable<ParsedRequest["options"]["reasoning"]> {
43
+ return value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh";
44
+ }
45
+
46
+ function isServiceTier(value: unknown): value is NonNullable<ParsedRequest["options"]["serviceTier"]> {
47
+ return value === "auto" || value === "default" || value === "flex" || value === "scale" || value === "priority";
48
+ }
49
+
50
+ function isObj(v: unknown): v is Record<string, unknown> {
51
+ return typeof v === "object" && v !== null && !Array.isArray(v);
52
+ }
53
+
54
+ function asString(v: unknown): string | undefined {
55
+ return typeof v === "string" ? v : undefined;
56
+ }
57
+
58
+ type AssistantItemPhase = "commentary" | "final_answer";
59
+ type MessageSignature = { id: string; phase?: AssistantItemPhase };
60
+
61
+ function parseAssistantItemPhase(value: unknown): AssistantItemPhase | undefined {
62
+ return value === "commentary" || value === "final_answer" ? value : undefined;
63
+ }
64
+
65
+ function messageTextSignature(id: unknown, phase: unknown): string | undefined {
66
+ const parsedPhase = parseAssistantItemPhase(phase);
67
+ if (typeof id === "string" && id.length > 0) return encodeTextSignatureV1(id, parsedPhase);
68
+ if (!parsedPhase) return undefined;
69
+ return encodeTextSignatureV1(makeMsgId(), parsedPhase);
70
+ }
71
+
72
+ // ─── id helpers ─────────────────────────────────────────────────────────────
73
+
74
+ function uuidNoDashes(): string {
75
+ return crypto.randomUUID().replace(/-/g, "");
76
+ }
77
+
78
+ function makeRespId(): string {
79
+ return `resp_${uuidNoDashes()}`;
80
+ }
81
+
82
+ function makeMsgId(): string {
83
+ return `msg_${uuidNoDashes()}`;
84
+ }
85
+
86
+ function makeReasoningId(): string {
87
+ return `rs_${uuidNoDashes()}`;
88
+ }
89
+
90
+ function makeFuncCallId(): string {
91
+ return `fc_${uuidNoDashes()}`;
92
+ }
93
+
94
+ function makeCustomCallId(): string {
95
+ return `ctc_${uuidNoDashes()}`;
96
+ }
97
+
98
+ // ─── once-only warnings ─────────────────────────────────────────────────────
99
+ // Module-scoped so we don't spam logs once per turn.
100
+
101
+ let warnedImageNotSupported = false;
102
+ let warnedFileNotSupported = false;
103
+ let warnedReasoningSummaryLevel = false;
104
+
105
+ // ─── inbound parser helpers ─────────────────────────────────────────────────
106
+
107
+ function extractReasoningTextFromItem(item: OpenAIResponsesReasoningItem): string {
108
+ // Prefer `summary[]` — mirrors real OpenAI and the openai-responses provider
109
+ // which writes the surfaced reasoning summary into `summary[].text`.
110
+ const fromSummary = (item.summary ?? []).map(c => c.text).join("");
111
+ if (fromSummary) return fromSummary;
112
+ return (item.content ?? []).map(c => c.text).join("");
113
+ }
114
+
115
+ type InputBlockUnion =
116
+ | { type: "input_text"; text: string }
117
+ | { type: "text"; text: string }
118
+ | { type: "input_image"; detail?: "auto" | "low" | "high"; image_url?: string; file_id?: string }
119
+ | { type: "input_file"; file_id?: string; filename?: string; file_data?: string };
120
+
121
+ /**
122
+ * Walk an input message's content array and produce pi-ai's `TextContent[]`.
123
+ * `input_image`/`input_file` blocks become bracketed text placeholders since
124
+ * pi-ai's `ImageContent` only carries inline base64 data and we have no
125
+ * resolver for OpenAI `image_url` / `file_id` references. Logs once per kind.
126
+ */
127
+ function inputContentParts(blocks: OpenAIResponsesInputContent[] | string | undefined): string | TextContent[] {
128
+ if (typeof blocks === "string") return blocks;
129
+ if (!blocks) return [];
130
+ const parts: TextContent[] = [];
131
+ for (const raw of blocks) {
132
+ const block = raw as InputBlockUnion;
133
+ if (block.type === "input_text" || block.type === "text") {
134
+ parts.push({ type: "text", text: block.text });
135
+ } else if (block.type === "input_image") {
136
+ if (!warnedImageNotSupported) {
137
+ warnedImageNotSupported = true;
138
+ logger.warn("openai-responses-server: input_image dropped (no pi-ai bridge for image_url/file_id)", {
139
+ hasUrl: typeof block.image_url === "string",
140
+ hasFileId: typeof block.file_id === "string",
141
+ });
142
+ }
143
+ const ref = block.image_url ?? block.file_id ?? "?";
144
+ parts.push({ type: "text", text: `[image: ${ref}]` });
145
+ } else if (block.type === "input_file") {
146
+ if (!warnedFileNotSupported) {
147
+ warnedFileNotSupported = true;
148
+ logger.warn("openai-responses-server: input_file dropped (no pi-ai bridge for file_id/file_data)", {
149
+ hasFileId: typeof block.file_id === "string",
150
+ hasFileData: typeof block.file_data === "string",
151
+ });
152
+ }
153
+ const ref = block.file_id ?? block.filename ?? "?";
154
+ parts.push({ type: "text", text: `[file: ${ref}]` });
155
+ }
156
+ }
157
+ return parts.length === 1 ? parts[0].text : parts;
158
+ }
159
+
160
+ type OutputBlockUnion =
161
+ | { type: "output_text"; text: string }
162
+ | { type: "text"; text: string }
163
+ | { type: "refusal"; refusal: string };
164
+
165
+ function outputTextOf(
166
+ blocks: OpenAIResponsesOutputContent[] | string | undefined,
167
+ message?: { id?: unknown; phase?: unknown },
168
+ ): TextContent[] {
169
+ const textSignature = messageTextSignature(message?.id, message?.phase);
170
+ const textContent = (text: string): TextContent =>
171
+ textSignature ? { type: "text", text, textSignature } : { type: "text", text };
172
+ if (typeof blocks === "string") return blocks.length > 0 ? [textContent(blocks)] : [];
173
+ if (!blocks) return [];
174
+ const parts: string[] = [];
175
+ for (const raw of blocks) {
176
+ const block = raw as OutputBlockUnion;
177
+ if (block.type === "output_text" || block.type === "text") {
178
+ parts.push(block.text);
179
+ } else if (block.type === "refusal") {
180
+ // Preserve the refusal reason so history replay still carries it.
181
+ parts.push(`[refusal: ${block.refusal}]`);
182
+ }
183
+ }
184
+ const text = parts.join("");
185
+ return text.length > 0 ? [textContent(text)] : [];
186
+ }
187
+
188
+ // The schema accepts a much wider tool_choice union than the SDK type so the
189
+ // walker narrows against the local schema shape.
190
+ type ParsedToolChoice =
191
+ | "auto"
192
+ | "none"
193
+ | "required"
194
+ | { type: "function"; name: string }
195
+ | { type: "custom"; name: string }
196
+ | {
197
+ type:
198
+ | "web_search_preview"
199
+ | "file_search"
200
+ | "computer_use_preview"
201
+ | "code_interpreter"
202
+ | "image_generation"
203
+ | "mcp";
204
+ }
205
+ | { type: "allowed_tools"; mode: "auto" | "required"; tools: Array<{ type: string; name?: string }> };
206
+
207
+ function mapToolChoice(value: ParsedToolChoice | undefined): ParsedRequest["options"]["toolChoice"] {
208
+ if (value === undefined) return undefined;
209
+ if (value === "auto" || value === "none" || value === "required") return value;
210
+ if ("type" in value) {
211
+ // `custom` (codex apply_patch) and `function` both resolve to the same
212
+ // pi-ai shape: pi-ai's dispatcher matches `Tool.name` AND `customWireName`,
213
+ // so passing the wire name works for either.
214
+ if (value.type === "function" || value.type === "custom") return { name: value.name };
215
+ // Hosted tools + allowed_tools — we don't surface these to pi-ai; fall
216
+ // back to letting the model pick a tool freely.
217
+ return "auto";
218
+ }
219
+ return undefined;
220
+ }
221
+
222
+ function buildTools(tools: Array<OpenAIResponsesTool | { type: string }> | undefined): Tool[] | undefined {
223
+ if (!tools) return undefined;
224
+ const out: Tool[] = [];
225
+ for (const t of tools) {
226
+ // Skip non-function tools (web_search, file_search, …).
227
+ if (t.type !== "function") continue;
228
+ const fn = t as Extract<OpenAIResponsesTool, { type: "function" }>;
229
+ const tool: Tool = {
230
+ name: fn.name,
231
+ description: fn.description ?? "",
232
+ parameters: (fn.parameters ?? {}) as Tool["parameters"],
233
+ };
234
+ if (fn.strict !== undefined && fn.strict !== null) tool.strict = fn.strict;
235
+ out.push(tool);
236
+ }
237
+ return out.length > 0 ? out : undefined;
238
+ }
239
+
240
+ function ensureAssistantPlaceholder(messages: Message[], modelId: string, now: number): AssistantMessage {
241
+ const last = messages[messages.length - 1];
242
+ if (last && last.role === "assistant") return last;
243
+ const placeholder: AssistantMessage = {
244
+ role: "assistant",
245
+ content: [],
246
+ api: "openai-responses",
247
+ provider: "openai",
248
+ model: modelId,
249
+ usage: {
250
+ input: 0,
251
+ output: 0,
252
+ cacheRead: 0,
253
+ cacheWrite: 0,
254
+ totalTokens: 0,
255
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
256
+ },
257
+ stopReason: "stop",
258
+ timestamp: now,
259
+ };
260
+ messages.push(placeholder);
261
+ return placeholder;
262
+ }
263
+
264
+ /** Flatten a function_call_output array form (text + refusal) into a single string. */
265
+ function flattenFunctionOutputArray(blocks: readonly unknown[]): string {
266
+ const parts: string[] = [];
267
+ for (const raw of blocks) {
268
+ if (!isObj(raw)) continue;
269
+ const t = raw.type;
270
+ if (t === "output_text" || t === "text") {
271
+ const text = asString(raw.text);
272
+ if (text) parts.push(text);
273
+ } else if (t === "refusal") {
274
+ const refusal = asString(raw.refusal);
275
+ if (refusal) parts.push(`[refusal: ${refusal}]`);
276
+ }
277
+ }
278
+ return parts.join("");
279
+ }
280
+
281
+ // ─── parseRequest ───────────────────────────────────────────────────────────
282
+
283
+ export function parseRequest(body: unknown, headers?: Headers): ParsedRequest {
284
+ // Header capture is centralized in `auth-gateway/server.ts` (the
285
+ // allow-listed set lands on `options.headers` automatically). We also
286
+ // consult `headers` here to populate `options.promptCacheKey` when the
287
+ // client signals a cache identity outside the body — see the
288
+ // `resolvePromptCacheKey` call further down.
289
+
290
+ const data = openaiResponsesRequestSchema(body);
291
+ if (data instanceof type.errors) {
292
+ throw new AIError.ValidationError(`openai-responses: ${data.summary}`);
293
+ }
294
+
295
+ const now = Date.now();
296
+ const messages: Message[] = [];
297
+ const systemPrompt: string[] = [];
298
+
299
+ if (typeof data.instructions === "string" && data.instructions.length > 0) {
300
+ systemPrompt.push(data.instructions);
301
+ }
302
+
303
+ if (typeof data.input === "string") {
304
+ messages.push({ role: "user", content: data.input, timestamp: now });
305
+ } else if (data.input) {
306
+ for (const item of data.input) {
307
+ // Items may omit `type` and rely on `role` (the convenience shape).
308
+ const effectiveType = item.type ?? ("role" in item ? "message" : undefined);
309
+ if (effectiveType === "message") {
310
+ const msg = item as {
311
+ role?: string;
312
+ content?: OpenAIResponsesInputContent[] | OpenAIResponsesOutputContent[] | string;
313
+ id?: unknown;
314
+ phase?: unknown;
315
+ };
316
+ switch (msg.role) {
317
+ case "system": {
318
+ const text = inputContentParts(msg.content as OpenAIResponsesInputContent[] | string | undefined);
319
+ const flat = typeof text === "string" ? text : text.map(p => p.text).join("");
320
+ if (flat.length > 0) systemPrompt.push(flat);
321
+ break;
322
+ }
323
+ case "user":
324
+ case "developer": {
325
+ const content = inputContentParts(msg.content as OpenAIResponsesInputContent[] | string | undefined);
326
+ messages.push({ role: msg.role, content, timestamp: now });
327
+ break;
328
+ }
329
+ case "assistant": {
330
+ const parts = outputTextOf(msg.content as OpenAIResponsesOutputContent[] | string | undefined, {
331
+ id: msg.id,
332
+ phase: msg.phase,
333
+ });
334
+ messages.push({
335
+ role: "assistant",
336
+ content: parts,
337
+ api: "openai-responses",
338
+ provider: "openai",
339
+ model: data.model,
340
+ usage: {
341
+ input: 0,
342
+ output: 0,
343
+ cacheRead: 0,
344
+ cacheWrite: 0,
345
+ totalTokens: 0,
346
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
347
+ },
348
+ stopReason: "stop",
349
+ timestamp: now,
350
+ });
351
+ break;
352
+ }
353
+ }
354
+ continue;
355
+ }
356
+ if (effectiveType === "reasoning") {
357
+ const reasoning = item as OpenAIResponsesReasoningItem;
358
+ const text = extractReasoningTextFromItem(reasoning);
359
+ const thinking: ThinkingContent = {
360
+ type: "thinking",
361
+ thinking: text,
362
+ thinkingSignature: JSON.stringify(reasoning),
363
+ ...(reasoning.id ? { itemId: reasoning.id } : {}),
364
+ };
365
+ ensureAssistantPlaceholder(messages, data.model, now).content.push(thinking);
366
+ continue;
367
+ }
368
+ if (effectiveType === "function_call") {
369
+ const call = item as OpenAIResponsesFunctionCallItem;
370
+ const argsRaw = call.arguments ?? "{}";
371
+ let args: Record<string, unknown>;
372
+ try {
373
+ const parsedArgs: unknown = JSON.parse(argsRaw);
374
+ args = isObj(parsedArgs) ? parsedArgs : {};
375
+ } catch {
376
+ throw new AIError.ValidationError(
377
+ `openai-responses: function_call ${call.call_id} has invalid JSON arguments`,
378
+ );
379
+ }
380
+ const toolCall: ToolCall = {
381
+ type: "toolCall",
382
+ id: call.call_id,
383
+ name: call.name,
384
+ arguments: args,
385
+ ...(call.id ? { thoughtSignature: call.id } : {}),
386
+ };
387
+ ensureAssistantPlaceholder(messages, data.model, now).content.push(toolCall);
388
+ continue;
389
+ }
390
+ if (effectiveType === "custom_tool_call") {
391
+ const call = item as { id?: string; call_id: string; name: string; input: string };
392
+ // Custom tools carry a raw input string. We stash it in `arguments.input`
393
+ // matching pi-ai's openai-shared convention, and tag the call
394
+ // with `customWireName` so encoders re-emit it as `custom_tool_call`.
395
+ const toolCall: ToolCall = {
396
+ type: "toolCall",
397
+ id: call.call_id,
398
+ name: call.name,
399
+ arguments: { input: call.input ?? "" },
400
+ customWireName: call.name,
401
+ ...(call.id ? { thoughtSignature: call.id } : {}),
402
+ };
403
+ ensureAssistantPlaceholder(messages, data.model, now).content.push(toolCall);
404
+ continue;
405
+ }
406
+ if (effectiveType === "function_call_output") {
407
+ const output = item as OpenAIResponsesFunctionCallOutputItem;
408
+ const toolName = findToolNameById(messages, output.call_id);
409
+ const text =
410
+ typeof output.output === "string"
411
+ ? output.output
412
+ : Array.isArray(output.output)
413
+ ? flattenFunctionOutputArray(output.output)
414
+ : "";
415
+ messages.push({
416
+ role: "toolResult",
417
+ toolCallId: output.call_id,
418
+ toolName,
419
+ content: [{ type: "text", text }],
420
+ isError: false,
421
+ timestamp: now,
422
+ });
423
+ continue;
424
+ }
425
+ if (effectiveType === "custom_tool_call_output") {
426
+ const output = item as { call_id: string; output: string };
427
+ const toolName = findToolNameById(messages, output.call_id);
428
+ messages.push({
429
+ role: "toolResult",
430
+ toolCallId: output.call_id,
431
+ toolName,
432
+ content: [{ type: "text", text: output.output ?? "" }],
433
+ isError: false,
434
+ timestamp: now,
435
+ });
436
+ }
437
+ // Other item types are tolerated but not bridged.
438
+ }
439
+ }
440
+
441
+ const tools = buildTools(data.tools);
442
+ const context: Context = {
443
+ ...(systemPrompt.length > 0 ? { systemPrompt } : {}),
444
+ messages,
445
+ ...(tools ? { tools } : {}),
446
+ };
447
+
448
+ const options: ParsedRequest["options"] = {};
449
+ if (data.max_output_tokens !== undefined) options.maxOutputTokens = data.max_output_tokens;
450
+ if (data.temperature !== undefined) options.temperature = data.temperature;
451
+ if (data.top_p !== undefined) options.topP = data.top_p;
452
+ if (data.stop !== undefined && data.stop !== null) {
453
+ options.stopSequences = typeof data.stop === "string" ? [data.stop] : data.stop;
454
+ }
455
+ const toolChoice = mapToolChoice(data.tool_choice as ParsedToolChoice | undefined);
456
+ if (toolChoice !== undefined) options.toolChoice = toolChoice;
457
+ if (data.reasoning?.effort && isReasoningEffort(data.reasoning.effort)) {
458
+ options.reasoning = data.reasoning.effort;
459
+ }
460
+ // OpenAI summary: `none` → suppress; `auto`/`concise`/`detailed` → request
461
+ // visible summary. pi-ai has no per-level plumbing — log once and let the
462
+ // provider default kick in.
463
+ if (data.reasoning?.summary === "none") {
464
+ options.hideThinkingSummary = true;
465
+ } else if (
466
+ data.reasoning?.summary === "auto" ||
467
+ data.reasoning?.summary === "concise" ||
468
+ data.reasoning?.summary === "detailed"
469
+ ) {
470
+ if (!warnedReasoningSummaryLevel) {
471
+ warnedReasoningSummaryLevel = true;
472
+ logger.debug("openai-responses-server: reasoning.summary level not differentiated", {
473
+ level: data.reasoning.summary,
474
+ });
475
+ }
476
+ }
477
+ if (data.service_tier !== undefined && isServiceTier(data.service_tier)) {
478
+ options.serviceTier = data.service_tier;
479
+ }
480
+ if (data.presence_penalty !== undefined) options.presencePenalty = data.presence_penalty;
481
+ if (data.frequency_penalty !== undefined) options.frequencyPenalty = data.frequency_penalty;
482
+ if (data.parallel_tool_calls !== undefined) options.parallelToolCalls = data.parallel_tool_calls;
483
+ const cacheKey = resolvePromptCacheKey(body, headers);
484
+ if (cacheKey !== undefined) options.promptCacheKey = cacheKey;
485
+ if (data.previous_response_id !== undefined) options.previousResponseId = data.previous_response_id;
486
+ if (data.user !== undefined) options.user = data.user;
487
+ if (isObj(data.metadata)) options.metadata = data.metadata;
488
+ // `store` is a stateful-storage hint that omp's gateway doesn't honour;
489
+ // silently accepted by the schema. No typed slot — drop.
490
+
491
+ return {
492
+ modelId: data.model,
493
+ context,
494
+ stream: data.stream === true,
495
+ options,
496
+ };
497
+ }
498
+
499
+ function findToolNameById(messages: Message[], callId: string): string {
500
+ for (let i = messages.length - 1; i >= 0; i--) {
501
+ const m = messages[i];
502
+ if (m.role !== "assistant") continue;
503
+ for (const c of m.content) {
504
+ if (c.type === "toolCall" && c.id === callId) return c.name;
505
+ }
506
+ }
507
+ return "";
508
+ }
509
+
510
+ // ─── formatError ────────────────────────────────────────────────────────────
511
+
512
+ export function formatError(status: number, type: string, message: string): Response {
513
+ return new Response(JSON.stringify({ error: { message, type } }), {
514
+ status,
515
+ headers: { "Content-Type": "application/json" },
516
+ });
517
+ }
518
+
519
+ // ─── output item builders (shared by streaming + non-streaming encoders) ────
520
+
521
+ type ReasoningOutputItem = {
522
+ type: "reasoning";
523
+ id: string;
524
+ summary: Array<{ type: "summary_text"; text: string }>;
525
+ } & Record<string, unknown>;
526
+
527
+ type MessageOutputItem = {
528
+ type: "message";
529
+ id: string;
530
+ role: "assistant";
531
+ status: "completed";
532
+ content: Array<{ type: "output_text"; text: string; annotations: never[] }>;
533
+ phase?: AssistantItemPhase;
534
+ };
535
+
536
+ type FunctionCallOutputItem = {
537
+ type: "function_call";
538
+ id: string;
539
+ call_id: string;
540
+ name: string;
541
+ arguments: string;
542
+ status: "completed";
543
+ };
544
+
545
+ type CustomToolCallOutputItem = {
546
+ type: "custom_tool_call";
547
+ id: string;
548
+ call_id: string;
549
+ name: string;
550
+ input: string;
551
+ status: "completed";
552
+ };
553
+
554
+ type OutputItem = ReasoningOutputItem | MessageOutputItem | FunctionCallOutputItem | CustomToolCallOutputItem;
555
+
556
+ type ResponseStatus = "completed" | "in_progress" | "failed" | "incomplete";
557
+
558
+ function responseStatusForStopReason(message: AssistantMessage): ResponseStatus {
559
+ if (message.stopReason === "length") return "incomplete";
560
+ if (message.stopReason === "error" || message.stopReason === "aborted") return "failed";
561
+ return "completed";
562
+ }
563
+
564
+ function buildReasoningItem(part: ThinkingContent): ReasoningOutputItem {
565
+ const baseId = part.itemId ?? makeReasoningId();
566
+ if (part.thinkingSignature) {
567
+ try {
568
+ const sigParsed: unknown = JSON.parse(part.thinkingSignature);
569
+ if (isObj(sigParsed) && sigParsed.type === "reasoning") {
570
+ const id = part.itemId ?? asString(sigParsed.id) ?? makeReasoningId();
571
+ // Preserve any extra fields (encrypted_content, …) the original carried,
572
+ // but normalize the summary into the canonical `{type, text}[]` shape.
573
+ const merged: Record<string, unknown> = { ...sigParsed, type: "reasoning", id };
574
+ merged.summary = [{ type: "summary_text", text: part.thinking }];
575
+ // `content[]` is the encrypted/raw side-channel; leave whatever was
576
+ // already there. If absent, omit — real OpenAI only emits `content[]`
577
+ // when `include=['reasoning.encrypted_content']` is set.
578
+ return merged as ReasoningOutputItem;
579
+ }
580
+ } catch {
581
+ // Not a serialized Responses reasoning item; fall through to fresh build.
582
+ }
583
+ }
584
+ return {
585
+ type: "reasoning",
586
+ id: baseId,
587
+ summary: [{ type: "summary_text", text: part.thinking }],
588
+ };
589
+ }
590
+
591
+ function reasoningItemId(part: ThinkingContent): string {
592
+ if (part.itemId) return part.itemId;
593
+ if (part.thinkingSignature) {
594
+ try {
595
+ const sigParsed: unknown = JSON.parse(part.thinkingSignature);
596
+ if (isObj(sigParsed)) {
597
+ const id = asString(sigParsed.id);
598
+ if (id) return id;
599
+ }
600
+ } catch {
601
+ // Not a serialized Responses reasoning item.
602
+ }
603
+ }
604
+ return makeReasoningId();
605
+ }
606
+
607
+ /**
608
+ * pi-ai responses providers mint composite `"{call_id}|{item_id}"` tool-call
609
+ * ids ({@link encodeResponsesToolCallId}). Only the call_id half belongs on
610
+ * the wire: third-party clients validate the `call_id` charset
611
+ * (`^[a-zA-Z0-9_-]+$`) or echo it to other backends, and `|` fails both.
612
+ */
613
+ function wireCallId(id: string): string {
614
+ const sep = id.indexOf("|");
615
+ return sep >= 0 ? id.slice(0, sep) : id;
616
+ }
617
+
618
+ /**
619
+ * Walk the assistant content array and group consecutive TextContent into a
620
+ * single message item; each ThinkingContent / ToolCall is its own item.
621
+ */
622
+ function buildOutputItems(message: AssistantMessage): OutputItem[] {
623
+ const out: OutputItem[] = [];
624
+ let pendingMessage: MessageOutputItem | null = null;
625
+ let pendingMessageSignature: { id: string; phase?: AssistantItemPhase } | undefined;
626
+ const flushMessage = () => {
627
+ if (pendingMessage) {
628
+ out.push(pendingMessage);
629
+ pendingMessage = null;
630
+ pendingMessageSignature = undefined;
631
+ }
632
+ };
633
+
634
+ for (const part of message.content) {
635
+ if (part.type === "text") {
636
+ const signature = parseTextSignature(part.textSignature);
637
+ const sameSignature =
638
+ !pendingMessage ||
639
+ (pendingMessageSignature?.id === signature?.id && pendingMessageSignature?.phase === signature?.phase);
640
+ if (!sameSignature) flushMessage();
641
+ if (!pendingMessage) {
642
+ pendingMessage = {
643
+ type: "message",
644
+ id: signature?.id ?? makeMsgId(),
645
+ role: "assistant",
646
+ status: "completed",
647
+ content: [],
648
+ ...(signature?.phase ? { phase: signature.phase } : {}),
649
+ };
650
+ pendingMessageSignature = signature;
651
+ }
652
+ pendingMessage.content.push({ type: "output_text", text: part.text, annotations: [] });
653
+ } else if (part.type === "thinking") {
654
+ flushMessage();
655
+ out.push(buildReasoningItem(part));
656
+ } else if (part.type === "toolCall") {
657
+ flushMessage();
658
+ if (part.customWireName) {
659
+ const input = part.arguments?.input;
660
+ const rawInput = typeof input === "string" ? input : "";
661
+ out.push({
662
+ type: "custom_tool_call",
663
+ id: part.thoughtSignature ?? makeCustomCallId(),
664
+ call_id: wireCallId(part.id),
665
+ name: part.customWireName,
666
+ input: rawInput,
667
+ status: "completed",
668
+ });
669
+ } else {
670
+ out.push({
671
+ type: "function_call",
672
+ id: part.thoughtSignature ?? makeFuncCallId(),
673
+ call_id: wireCallId(part.id),
674
+ name: part.name,
675
+ arguments: JSON.stringify(part.arguments ?? {}),
676
+ status: "completed",
677
+ });
678
+ }
679
+ }
680
+ // RedactedThinking / Image are silently dropped — no direct Responses wire representation.
681
+ }
682
+ flushMessage();
683
+ return out;
684
+ }
685
+
686
+ function buildUsage(message: AssistantMessage): Record<string, unknown> {
687
+ const u = message.usage;
688
+ const inputTokens = u.input + u.cacheRead + u.cacheWrite;
689
+ return {
690
+ input_tokens: inputTokens,
691
+ input_tokens_details: { cached_tokens: u.cacheRead },
692
+ output_tokens: u.output,
693
+ output_tokens_details: { reasoning_tokens: u.reasoningTokens ?? 0 },
694
+ total_tokens: inputTokens + u.output,
695
+ };
696
+ }
697
+
698
+ function buildResponseEnvelope(
699
+ message: AssistantMessage,
700
+ requestedModelId: string,
701
+ id: string,
702
+ status: ResponseStatus,
703
+ items: OutputItem[] | [],
704
+ usage: Record<string, unknown> | null,
705
+ ): Record<string, unknown> {
706
+ return {
707
+ id,
708
+ object: "response",
709
+ created_at: Math.floor(message.timestamp / 1000),
710
+ status,
711
+ model: requestedModelId,
712
+ output: items,
713
+ usage,
714
+ ...(status === "incomplete" ? { incomplete_details: { reason: "max_output_tokens" } } : {}),
715
+ ...(status === "failed" ? { error: { message: message.errorMessage ?? "response failed" } } : {}),
716
+ };
717
+ }
718
+
719
+ // ─── encodeResponse (non-streaming) ─────────────────────────────────────────
720
+
721
+ export function encodeResponse(message: AssistantMessage, requestedModelId: string): Record<string, unknown> {
722
+ const items = buildOutputItems(message);
723
+ return buildResponseEnvelope(
724
+ message,
725
+ requestedModelId,
726
+ makeRespId(),
727
+ responseStatusForStopReason(message),
728
+ items,
729
+ buildUsage(message),
730
+ );
731
+ }
732
+
733
+ // ─── encodeStream ───────────────────────────────────────────────────────────
734
+
735
+ interface OpenMessage {
736
+ kind: "message";
737
+ itemId: string;
738
+ outputIndex: number;
739
+ contentIndex: number;
740
+ currentPartText: string;
741
+ content: Array<{ type: "output_text"; text: string; annotations: never[] }>;
742
+ signature?: MessageSignature;
743
+ }
744
+ interface OpenReasoning {
745
+ kind: "reasoning";
746
+ itemId: string;
747
+ outputIndex: number;
748
+ reasoningText: string;
749
+ }
750
+ interface OpenFunctionCall {
751
+ kind: "function_call";
752
+ itemId: string;
753
+ outputIndex: number;
754
+ contentIndex: number;
755
+ callId: string;
756
+ name: string;
757
+ argsText: string;
758
+ /** Set when the underlying ToolCall is a custom-tool emission. */
759
+ customWireName?: string;
760
+ }
761
+ type OpenItem = OpenMessage | OpenReasoning | OpenFunctionCall;
762
+
763
+ function sseEvent(name: string, data: unknown): string {
764
+ return `event: ${name}\ndata: ${JSON.stringify(data)}\n\n`;
765
+ }
766
+
767
+ export function encodeStream(
768
+ events: AssistantMessageEventStream,
769
+ requestedModelId: string,
770
+ _options?: ParsedRequest["options"],
771
+ control?: AuthGatewayStreamControl,
772
+ ): ReadableStream<Uint8Array> {
773
+ const encoder = new TextEncoder();
774
+ const responseId = makeRespId();
775
+ let sequenceNumber = 0;
776
+ let cancelled = control?.signal?.aborted === true;
777
+ const markCancelled = () => {
778
+ cancelled = true;
779
+ };
780
+ control?.signal?.addEventListener("abort", markCancelled, { once: true });
781
+ const seq = () => sequenceNumber++;
782
+
783
+ return new ReadableStream<Uint8Array>({
784
+ async start(controller) {
785
+ const emit = (name: string, data: Record<string, unknown>) => {
786
+ if (!cancelled)
787
+ controller.enqueue(encoder.encode(sseEvent(name, { type: name, sequence_number: seq(), ...data })));
788
+ };
789
+ const emitDone = () => {
790
+ if (!cancelled) controller.enqueue(encoder.encode("data: [DONE]\n\n"));
791
+ };
792
+
793
+ let createdAt = Math.floor(Date.now() / 1000);
794
+ let outputIndex = 0;
795
+ const state: { open: OpenItem | null } = { open: null };
796
+ const openFunctionCalls = new Map<number, OpenFunctionCall>();
797
+ const finishedItems: OutputItem[] = [];
798
+ const allocateOutputIndex = (): number => outputIndex++;
799
+
800
+ const responseSnapshot = (status: ResponseStatus, output: OutputItem[] | []) => ({
801
+ id: responseId,
802
+ object: "response",
803
+ created_at: createdAt,
804
+ status,
805
+ model: requestedModelId,
806
+ output,
807
+ usage: null,
808
+ });
809
+
810
+ const openMessage = (signature?: MessageSignature): OpenMessage => {
811
+ const itemOutputIndex = allocateOutputIndex();
812
+ const itemId = signature?.id ?? makeMsgId();
813
+ const item = {
814
+ type: "message" as const,
815
+ id: itemId,
816
+ status: "in_progress" as const,
817
+ role: "assistant" as const,
818
+ content: [] as Array<{ type: "output_text"; text: string; annotations: never[] }>,
819
+ ...(signature?.phase ? { phase: signature.phase } : {}),
820
+ };
821
+ emit("response.output_item.added", { output_index: itemOutputIndex, item });
822
+ const next: OpenMessage = {
823
+ kind: "message",
824
+ itemId,
825
+ outputIndex: itemOutputIndex,
826
+ contentIndex: 0,
827
+ currentPartText: "",
828
+ content: [],
829
+ ...(signature ? { signature } : {}),
830
+ };
831
+ state.open = next;
832
+ return next;
833
+ };
834
+
835
+ const openReasoning = (partial: AssistantMessage, contentIndex: number): OpenReasoning => {
836
+ const itemOutputIndex = allocateOutputIndex();
837
+ const part = partial.content[contentIndex];
838
+ const itemId = part && part.type === "thinking" ? reasoningItemId(part) : makeReasoningId();
839
+ const item = {
840
+ type: "reasoning" as const,
841
+ id: itemId,
842
+ summary: [] as Array<{ type: "summary_text"; text: string }>,
843
+ };
844
+ emit("response.output_item.added", { output_index: itemOutputIndex, item });
845
+ // Open the summary part. Real OpenAI streams summary text in the
846
+ // canonical `reasoning_summary_*` lifecycle; pi-ai's own decoder
847
+ // reads `summary[].text` from the eventual `output_item.done`.
848
+ emit("response.reasoning_summary_part.added", {
849
+ item_id: itemId,
850
+ output_index: itemOutputIndex,
851
+ summary_index: 0,
852
+ part: { type: "summary_text", text: "" },
853
+ });
854
+ const next: OpenReasoning = { kind: "reasoning", itemId, outputIndex: itemOutputIndex, reasoningText: "" };
855
+ state.open = next;
856
+ return next;
857
+ };
858
+
859
+ const openToolCall = (partial: AssistantMessage, contentIndex: number): OpenFunctionCall => {
860
+ const itemOutputIndex = allocateOutputIndex();
861
+ const part = partial.content[contentIndex];
862
+ const tc = part && part.type === "toolCall" ? part : undefined;
863
+ const customWireName: string | undefined =
864
+ tc && typeof tc.customWireName === "string" && tc.customWireName.length > 0
865
+ ? tc.customWireName
866
+ : undefined;
867
+ const isCustom = customWireName !== undefined;
868
+ const itemId = tc?.thoughtSignature ?? (isCustom ? makeCustomCallId() : makeFuncCallId());
869
+ const callId = wireCallId(tc?.id ?? "");
870
+ const name = customWireName ?? tc?.name ?? "";
871
+ const item = isCustom
872
+ ? {
873
+ type: "custom_tool_call" as const,
874
+ id: itemId,
875
+ call_id: callId,
876
+ name,
877
+ input: "",
878
+ status: "in_progress",
879
+ }
880
+ : {
881
+ type: "function_call" as const,
882
+ id: itemId,
883
+ call_id: callId,
884
+ name,
885
+ arguments: "",
886
+ status: "in_progress",
887
+ };
888
+ emit("response.output_item.added", { output_index: itemOutputIndex, item });
889
+ const next: OpenFunctionCall = {
890
+ kind: "function_call",
891
+ itemId,
892
+ outputIndex: itemOutputIndex,
893
+ contentIndex,
894
+ callId,
895
+ name,
896
+ argsText: "",
897
+ ...(isCustom ? { customWireName } : {}),
898
+ };
899
+ openFunctionCalls.set(contentIndex, next);
900
+ state.open = next;
901
+ return next;
902
+ };
903
+
904
+ const closeFunctionCall = (call: OpenFunctionCall): void => {
905
+ const text = call.argsText ?? "";
906
+ if (call.customWireName) {
907
+ const item = {
908
+ type: "custom_tool_call",
909
+ id: call.itemId,
910
+ call_id: call.callId ?? "",
911
+ name: call.customWireName,
912
+ input: text,
913
+ status: "completed",
914
+ };
915
+ emit("response.output_item.done", { output_index: call.outputIndex, item });
916
+ finishedItems.push({
917
+ type: "custom_tool_call",
918
+ id: call.itemId,
919
+ call_id: call.callId ?? "",
920
+ name: call.customWireName,
921
+ input: text,
922
+ status: "completed",
923
+ });
924
+ } else {
925
+ const item = {
926
+ type: "function_call",
927
+ id: call.itemId,
928
+ call_id: call.callId ?? "",
929
+ name: call.name ?? "",
930
+ arguments: text,
931
+ status: "completed",
932
+ };
933
+ emit("response.output_item.done", { output_index: call.outputIndex, item });
934
+ finishedItems.push({
935
+ type: "function_call",
936
+ id: call.itemId,
937
+ call_id: call.callId ?? "",
938
+ name: call.name ?? "",
939
+ arguments: text,
940
+ status: "completed",
941
+ });
942
+ }
943
+ openFunctionCalls.delete(call.contentIndex);
944
+ if (state.open === call) state.open = null;
945
+ };
946
+
947
+ const closeOpen = () => {
948
+ if (!state.open) return;
949
+ if (state.open.kind === "message") {
950
+ const item = {
951
+ type: "message" as const,
952
+ id: state.open.itemId,
953
+ status: "completed" as const,
954
+ role: "assistant" as const,
955
+ content: state.open.content,
956
+ ...(state.open.signature?.phase ? { phase: state.open.signature.phase } : {}),
957
+ };
958
+ emit("response.output_item.done", { output_index: state.open.outputIndex, item });
959
+ finishedItems.push(item);
960
+ state.open = null;
961
+ } else if (state.open.kind === "reasoning") {
962
+ const summary = [{ type: "summary_text" as const, text: state.open.reasoningText ?? "" }];
963
+ const item = {
964
+ type: "reasoning",
965
+ id: state.open.itemId,
966
+ summary,
967
+ };
968
+ emit("response.output_item.done", { output_index: state.open.outputIndex, item });
969
+ finishedItems.push({
970
+ type: "reasoning",
971
+ id: state.open.itemId,
972
+ summary,
973
+ });
974
+ state.open = null;
975
+ } else {
976
+ closeFunctionCall(state.open);
977
+ }
978
+ };
979
+
980
+ const closeOpenFunctionCalls = (): void => {
981
+ for (const call of [...openFunctionCalls.values()]) {
982
+ closeFunctionCall(call);
983
+ }
984
+ };
985
+
986
+ const functionCallForEvent = (contentIndex: number): OpenFunctionCall | undefined => {
987
+ const byIndex = openFunctionCalls.get(contentIndex);
988
+ if (byIndex) return byIndex;
989
+ return state.open?.kind === "function_call" ? state.open : undefined;
990
+ };
991
+ let finalMessage: AssistantMessage | undefined;
992
+ let failureMessage: AssistantMessage | undefined;
993
+ try {
994
+ if (cancelled) {
995
+ controller.close();
996
+ return;
997
+ }
998
+ for await (const ev of events) {
999
+ if (cancelled) return;
1000
+ switch (ev.type) {
1001
+ case "start": {
1002
+ createdAt = Math.floor((ev.partial.timestamp || Date.now()) / 1000);
1003
+ // response.created — initial envelope.
1004
+ emit("response.created", { response: responseSnapshot("in_progress", []) });
1005
+ // response.in_progress — mirrors real OpenAI; some clients gate
1006
+ // on it before reading items.
1007
+ emit("response.in_progress", { response: responseSnapshot("in_progress", []) });
1008
+ break;
1009
+ }
1010
+ case "text_start": {
1011
+ let cur: OpenMessage;
1012
+ const textBlock = ev.partial.content[ev.contentIndex];
1013
+ const signature =
1014
+ textBlock?.type === "text" ? parseTextSignature(textBlock.textSignature) : undefined;
1015
+ if (state.open && state.open.kind === "message") {
1016
+ const sameSignature =
1017
+ (!signature && !state.open.signature) ||
1018
+ (signature !== undefined &&
1019
+ state.open.signature?.id === signature.id &&
1020
+ state.open.signature.phase === signature.phase);
1021
+ if (sameSignature) {
1022
+ // Continue same message item, new content part.
1023
+ cur = state.open;
1024
+ cur.currentPartText = "";
1025
+ } else {
1026
+ closeOpen();
1027
+ cur = openMessage(signature);
1028
+ }
1029
+ } else {
1030
+ if (state.open && state.open.kind !== "function_call") closeOpen();
1031
+ cur = openMessage(signature);
1032
+ }
1033
+ const contentPart = { type: "output_text", text: "", annotations: [] as never[] };
1034
+ emit("response.content_part.added", {
1035
+ item_id: cur.itemId,
1036
+ output_index: cur.outputIndex,
1037
+ content_index: cur.contentIndex,
1038
+ part: contentPart,
1039
+ });
1040
+ break;
1041
+ }
1042
+ case "text_delta": {
1043
+ if (state.open?.kind !== "message") break;
1044
+ const cur: OpenMessage = state.open;
1045
+ cur.currentPartText += ev.delta;
1046
+ emit("response.output_text.delta", {
1047
+ item_id: cur.itemId,
1048
+ output_index: cur.outputIndex,
1049
+ content_index: cur.contentIndex,
1050
+ delta: ev.delta,
1051
+ logprobs: [],
1052
+ });
1053
+ // TODO: when pi-ai surfaces output_text annotations
1054
+ // (web_search citations, …), emit
1055
+ // `response.output_text.annotation.added` here.
1056
+ break;
1057
+ }
1058
+ case "text_end": {
1059
+ if (state.open?.kind !== "message") break;
1060
+ const cur: OpenMessage = state.open;
1061
+ const text = ev.content ?? cur.currentPartText;
1062
+ emit("response.output_text.done", {
1063
+ item_id: cur.itemId,
1064
+ output_index: cur.outputIndex,
1065
+ content_index: cur.contentIndex,
1066
+ text,
1067
+ logprobs: [],
1068
+ });
1069
+ cur.content.push({ type: "output_text", text, annotations: [] });
1070
+ emit("response.content_part.done", {
1071
+ item_id: cur.itemId,
1072
+ output_index: cur.outputIndex,
1073
+ content_index: cur.contentIndex,
1074
+ part: { type: "output_text", text, annotations: [] },
1075
+ });
1076
+ cur.contentIndex += 1;
1077
+ cur.currentPartText = "";
1078
+ break;
1079
+ }
1080
+ case "thinking_start": {
1081
+ if (state.open && state.open.kind !== "function_call") closeOpen();
1082
+ openReasoning(ev.partial, ev.contentIndex);
1083
+ break;
1084
+ }
1085
+ case "thinking_delta": {
1086
+ if (state.open?.kind !== "reasoning") break;
1087
+ const cur: OpenReasoning = state.open;
1088
+ cur.reasoningText += ev.delta;
1089
+ emit("response.reasoning_summary_text.delta", {
1090
+ item_id: cur.itemId,
1091
+ output_index: cur.outputIndex,
1092
+ summary_index: 0,
1093
+ delta: ev.delta,
1094
+ });
1095
+ break;
1096
+ }
1097
+ case "thinking_end": {
1098
+ if (state.open?.kind !== "reasoning") break;
1099
+ const cur: OpenReasoning = state.open;
1100
+ const text = ev.content ?? cur.reasoningText;
1101
+ cur.reasoningText = text;
1102
+ emit("response.reasoning_summary_text.done", {
1103
+ item_id: cur.itemId,
1104
+ output_index: cur.outputIndex,
1105
+ summary_index: 0,
1106
+ text,
1107
+ });
1108
+ emit("response.reasoning_summary_part.done", {
1109
+ item_id: cur.itemId,
1110
+ output_index: cur.outputIndex,
1111
+ summary_index: 0,
1112
+ part: { type: "summary_text", text },
1113
+ });
1114
+ closeOpen();
1115
+ break;
1116
+ }
1117
+ case "toolcall_start": {
1118
+ if (state.open && state.open.kind !== "function_call") closeOpen();
1119
+ openToolCall(ev.partial, ev.contentIndex);
1120
+ break;
1121
+ }
1122
+ case "toolcall_delta": {
1123
+ const cur = functionCallForEvent(ev.contentIndex);
1124
+ if (!cur) break;
1125
+ cur.argsText += ev.delta;
1126
+ if (cur.customWireName) {
1127
+ emit("response.custom_tool_call_input.delta", {
1128
+ item_id: cur.itemId,
1129
+ output_index: cur.outputIndex,
1130
+ delta: ev.delta,
1131
+ });
1132
+ } else {
1133
+ emit("response.function_call_arguments.delta", {
1134
+ item_id: cur.itemId,
1135
+ output_index: cur.outputIndex,
1136
+ delta: ev.delta,
1137
+ });
1138
+ }
1139
+ break;
1140
+ }
1141
+ case "toolcall_end": {
1142
+ const cur = functionCallForEvent(ev.contentIndex);
1143
+ if (!cur) break;
1144
+ // Promote possibly-late info from the canonical ToolCall.
1145
+ const tc = ev.toolCall;
1146
+ if (tc.customWireName && !cur.customWireName) cur.customWireName = tc.customWireName;
1147
+ if (tc.thoughtSignature) cur.itemId = tc.thoughtSignature;
1148
+ cur.callId = tc.id;
1149
+ cur.name = cur.customWireName ?? tc.name;
1150
+ if (cur.customWireName) {
1151
+ // Custom tool: raw input string. Streamed deltas accumulated
1152
+ // the wire-level body; fall back to `arguments.input` from
1153
+ // the finalized ToolCall when nothing streamed (rare).
1154
+ const rawInput =
1155
+ cur.argsText ||
1156
+ (typeof tc.arguments?.input === "string" ? (tc.arguments.input as string) : "");
1157
+ cur.argsText = rawInput;
1158
+ emit("response.custom_tool_call_input.done", {
1159
+ item_id: cur.itemId,
1160
+ output_index: cur.outputIndex,
1161
+ input: rawInput,
1162
+ name: cur.name,
1163
+ });
1164
+ } else {
1165
+ // Standard JSON tool: arguments object on the omp side, the
1166
+ // wire wants the JSON string the model emitted (= streamed deltas).
1167
+ const argsJson = cur.argsText || JSON.stringify(tc.arguments ?? {});
1168
+ cur.argsText = argsJson;
1169
+ emit("response.function_call_arguments.done", {
1170
+ item_id: cur.itemId,
1171
+ output_index: cur.outputIndex,
1172
+ arguments: argsJson,
1173
+ name: cur.name,
1174
+ });
1175
+ }
1176
+ closeFunctionCall(cur);
1177
+ break;
1178
+ }
1179
+ case "done": {
1180
+ finalMessage = ev.message;
1181
+ break;
1182
+ }
1183
+ case "error": {
1184
+ failureMessage = ev.error;
1185
+ break;
1186
+ }
1187
+ }
1188
+ }
1189
+
1190
+ if (failureMessage) {
1191
+ closeOpenFunctionCalls();
1192
+ if (state.open) closeOpen();
1193
+ controller.enqueue(
1194
+ encoder.encode(
1195
+ sseEvent("response.failed", {
1196
+ type: "response.failed",
1197
+ sequence_number: seq(),
1198
+ response: {
1199
+ ...responseSnapshot("failed", finishedItems),
1200
+ error: { message: failureMessage.errorMessage ?? "stream failed" },
1201
+ },
1202
+ }),
1203
+ ),
1204
+ );
1205
+ emitDone();
1206
+ controller.close();
1207
+ return;
1208
+ }
1209
+
1210
+ closeOpenFunctionCalls();
1211
+ if (state.open) closeOpen();
1212
+ const message = finalMessage ?? ((await events.result().catch(() => null)) as AssistantMessage | null);
1213
+
1214
+ // Build the canonical output from the final message so non-streaming
1215
+ // readers see the exact same shape they'd get from encodeResponse().
1216
+ const items = message ? buildOutputItems(message) : finishedItems;
1217
+ const usage = message ? buildUsage(message) : null;
1218
+ const status = message ? responseStatusForStopReason(message) : "completed";
1219
+ const terminalEvent =
1220
+ status === "incomplete"
1221
+ ? "response.incomplete"
1222
+ : status === "failed"
1223
+ ? "response.failed"
1224
+ : "response.completed";
1225
+ controller.enqueue(
1226
+ encoder.encode(
1227
+ sseEvent(terminalEvent, {
1228
+ type: terminalEvent,
1229
+ sequence_number: seq(),
1230
+ response: {
1231
+ id: responseId,
1232
+ object: "response",
1233
+ created_at: createdAt,
1234
+ status,
1235
+ model: requestedModelId,
1236
+ output: items,
1237
+ usage,
1238
+ ...(status === "incomplete" ? { incomplete_details: { reason: "max_output_tokens" } } : {}),
1239
+ ...(status === "failed"
1240
+ ? { error: { message: message?.errorMessage ?? "response failed" } }
1241
+ : {}),
1242
+ },
1243
+ }),
1244
+ ),
1245
+ );
1246
+ emitDone();
1247
+ controller.close();
1248
+ } catch (err) {
1249
+ if (!cancelled) {
1250
+ controller.enqueue(
1251
+ encoder.encode(
1252
+ sseEvent("response.failed", {
1253
+ type: "response.failed",
1254
+ sequence_number: seq(),
1255
+ response: {
1256
+ id: responseId,
1257
+ object: "response",
1258
+ created_at: Math.floor(Date.now() / 1000),
1259
+ status: "failed",
1260
+ model: requestedModelId,
1261
+ output: [],
1262
+ error: { message: err instanceof Error ? err.message : String(err) },
1263
+ },
1264
+ }),
1265
+ ),
1266
+ );
1267
+ emitDone();
1268
+ controller.close();
1269
+ }
1270
+ } finally {
1271
+ control?.signal?.removeEventListener("abort", markCancelled);
1272
+ }
1273
+ },
1274
+ cancel(reason) {
1275
+ cancelled = true;
1276
+ control?.signal?.removeEventListener("abort", markCancelled);
1277
+ control?.onCancel?.(reason);
1278
+ },
1279
+ });
1280
+ }