@linxiraos/pi-ai 1.0.0

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