dominds 1.13.2 → 1.15.2

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 (200) hide show
  1. package/dist/bootstrap/global-dialog-event-broadcaster.d.ts +18 -0
  2. package/dist/bootstrap/global-dialog-event-broadcaster.js +81 -0
  3. package/dist/dialog-fork.js +13 -12
  4. package/dist/dialog.d.ts +61 -50
  5. package/dist/dialog.js +284 -78
  6. package/dist/docs/dialog-system.md +12 -0
  7. package/dist/docs/dialog-system.zh.md +12 -0
  8. package/dist/docs/dominds-terminology.md +17 -0
  9. package/dist/docs/issues/global-dialog-event-broadcaster-missing.md +128 -0
  10. package/dist/docs/llm-provider-isolation.md +35 -0
  11. package/dist/docs/llm-provider-isolation.zh.md +35 -0
  12. package/dist/llm/client.d.ts +2 -1
  13. package/dist/llm/defaults.yaml +118 -4
  14. package/dist/llm/gen/anthropic.js +2 -4
  15. package/dist/llm/gen/codex.d.ts +11 -0
  16. package/dist/llm/gen/codex.js +41 -31
  17. package/dist/llm/gen/failure-classifier.js +17 -0
  18. package/dist/llm/gen/mock.js +45 -21
  19. package/dist/llm/gen/openai-compatible.d.ts +2 -0
  20. package/dist/llm/gen/openai-compatible.js +43 -38
  21. package/dist/llm/gen/openai.d.ts +3 -1
  22. package/dist/llm/gen/openai.js +888 -71
  23. package/dist/llm/gen/tool-call-context.d.ts +7 -2
  24. package/dist/llm/gen/tool-call-context.js +55 -13
  25. package/dist/llm/gen.d.ts +60 -3
  26. package/dist/llm/kernel-driver/context.js +1 -1
  27. package/dist/llm/kernel-driver/drive.js +374 -348
  28. package/dist/llm/kernel-driver/flow.js +3 -3
  29. package/dist/llm/kernel-driver/guardrails.d.ts +1 -1
  30. package/dist/llm/kernel-driver/guardrails.js +4 -4
  31. package/dist/llm/kernel-driver/runtime.js +11 -29
  32. package/dist/llm/kernel-driver/subdialog.js +56 -5
  33. package/dist/llm/kernel-driver/tellask-special.d.ts +38 -12
  34. package/dist/llm/kernel-driver/tellask-special.js +489 -180
  35. package/dist/llm/kernel-driver/types.d.ts +1 -1
  36. package/dist/persistence.d.ts +30 -62
  37. package/dist/persistence.js +978 -986
  38. package/dist/priming.js +398 -365
  39. package/dist/recovery/reply-special.js +3 -3
  40. package/dist/runtime/inter-dialog-format.d.ts +1 -1
  41. package/dist/runtime/inter-dialog-format.js +1 -1
  42. package/dist/runtime/reply-prompt-copy.js +4 -4
  43. package/dist/server/setup-routes.js +26 -5
  44. package/dist/server/snippets-routes.d.ts +1 -0
  45. package/dist/server/snippets-routes.js +20 -9
  46. package/dist/server/websocket-handler.js +58 -25
  47. package/dist/shared/utils/fbr.js +12 -8
  48. package/dist/shared/utils/inter-dialog-format.js +6 -4
  49. package/dist/team.d.ts +24 -13
  50. package/dist/team.js +123 -32
  51. package/dist/tool.d.ts +26 -0
  52. package/dist/tool.js +97 -0
  53. package/dist/tools/team_mgmt.js +18 -0
  54. package/package.json +2 -2
  55. package/webapp/dist/assets/{_basePickBy-CBOtd63g.js → _basePickBy-DsirmCgI.js} +3 -3
  56. package/webapp/dist/assets/_basePickBy-DsirmCgI.js.map +1 -0
  57. package/webapp/dist/assets/{_baseUniq-mfoKz4Wm.js → _baseUniq-tR6G8loB.js} +2 -2
  58. package/webapp/dist/assets/_baseUniq-tR6G8loB.js.map +1 -0
  59. package/webapp/dist/assets/{arc-Dq0WZLyu.js → arc-CzxpASkZ.js} +2 -2
  60. package/webapp/dist/assets/arc-CzxpASkZ.js.map +1 -0
  61. package/webapp/dist/assets/{architectureDiagram-VXUJARFQ-CNmygmp3.js → architectureDiagram-2XIMDMQ5-BSH7H5oI.js} +26 -8
  62. package/webapp/dist/assets/architectureDiagram-2XIMDMQ5-BSH7H5oI.js.map +1 -0
  63. package/webapp/dist/assets/{blockDiagram-VD42YOAC-DvE0lybt.js → blockDiagram-WCTKOSBZ-DpLIr7yO.js} +187 -170
  64. package/webapp/dist/assets/blockDiagram-WCTKOSBZ-DpLIr7yO.js.map +1 -0
  65. package/webapp/dist/assets/{c4Diagram-YG6GDRKO-CR7zJ2_u.js → c4Diagram-IC4MRINW-WuYKgWfY.js} +4 -4
  66. package/webapp/dist/assets/c4Diagram-IC4MRINW-WuYKgWfY.js.map +1 -0
  67. package/webapp/dist/assets/{channel-DrTrnYx4.js → channel-B-v9dqLN.js} +2 -2
  68. package/webapp/dist/assets/channel-B-v9dqLN.js.map +1 -0
  69. package/webapp/dist/assets/{chunk-4BX2VUAB-CVuJEIeN.js → chunk-4BX2VUAB-MtFUfKZy.js} +2 -2
  70. package/webapp/dist/assets/chunk-4BX2VUAB-MtFUfKZy.js.map +1 -0
  71. package/webapp/dist/assets/{chunk-55IACEB6-BxUoXApB.js → chunk-55IACEB6-rY9AJdzj.js} +2 -2
  72. package/webapp/dist/assets/chunk-55IACEB6-rY9AJdzj.js.map +1 -0
  73. package/webapp/dist/assets/{chunk-FMBD7UC4-TX-LVAaV.js → chunk-FMBD7UC4-B-RtOs7e.js} +2 -2
  74. package/webapp/dist/assets/chunk-FMBD7UC4-B-RtOs7e.js.map +1 -0
  75. package/webapp/dist/assets/{chunk-TZMSLE5B-Cw689yRl.js → chunk-JSJVCQXG-Da1d3uS4.js} +14 -6
  76. package/webapp/dist/assets/chunk-JSJVCQXG-Da1d3uS4.js.map +1 -0
  77. package/webapp/dist/assets/{chunk-QN33PNHL-D1uiKlOO.js → chunk-KX2RTZJC-DH9UrpuG.js} +2 -2
  78. package/webapp/dist/assets/chunk-KX2RTZJC-DH9UrpuG.js.map +1 -0
  79. package/webapp/dist/assets/{chunk-DI55MBZ5-SAhxUTqQ.js → chunk-NQ4KR5QH-CK365lrr.js} +9 -7
  80. package/webapp/dist/assets/chunk-NQ4KR5QH-CK365lrr.js.map +1 -0
  81. package/webapp/dist/assets/{chunk-QZHKN3VN-BxuV0Oba.js → chunk-QZHKN3VN-BCaWPGDm.js} +2 -2
  82. package/webapp/dist/assets/chunk-QZHKN3VN-BCaWPGDm.js.map +1 -0
  83. package/webapp/dist/assets/{chunk-B4BG7PRW-DpMa3-9L.js → chunk-WL4C6EOR-DDCnEwft.js} +171 -121
  84. package/webapp/dist/assets/chunk-WL4C6EOR-DDCnEwft.js.map +1 -0
  85. package/webapp/dist/assets/{classDiagram-2ON5EDUG-BTTGianr.js → classDiagram-VBA2DB6C-CvMBU4WA.js} +7 -6
  86. package/webapp/dist/assets/classDiagram-VBA2DB6C-CvMBU4WA.js.map +1 -0
  87. package/webapp/dist/assets/{classDiagram-v2-WZHVMYZB-BTTGianr.js → classDiagram-v2-RAHNMMFH-CvMBU4WA.js} +7 -6
  88. package/webapp/dist/assets/classDiagram-v2-RAHNMMFH-CvMBU4WA.js.map +1 -0
  89. package/webapp/dist/assets/{clone-Dk8cAI3I.js → clone-r98jR0MC.js} +2 -2
  90. package/webapp/dist/assets/clone-r98jR0MC.js.map +1 -0
  91. package/webapp/dist/assets/{cose-bilkent-S5V4N54A-BjJnzB2N.js → cose-bilkent-S5V4N54A-t6J60Ogk.js} +2 -2
  92. package/webapp/dist/assets/cose-bilkent-S5V4N54A-t6J60Ogk.js.map +1 -0
  93. package/webapp/dist/assets/cytoscape.esm-Bm8DJGmZ.js.map +1 -1
  94. package/webapp/dist/assets/{dagre-6UL2VRFP-VF-xGhAf.js → dagre-KLK3FWXG-BlqmY2DV.js} +7 -7
  95. package/webapp/dist/assets/dagre-KLK3FWXG-BlqmY2DV.js.map +1 -0
  96. package/webapp/dist/assets/defaultLocale-B2RvLBDe.js.map +1 -1
  97. package/webapp/dist/assets/{diagram-PSM6KHXK-Ba5U0oRY.js → diagram-E7M64L7V-FwCHeIUD.js} +10 -10
  98. package/webapp/dist/assets/diagram-E7M64L7V-FwCHeIUD.js.map +1 -0
  99. package/webapp/dist/assets/{diagram-QEK2KX5R-DoYCnEw_.js → diagram-IFDJBPK2-NhtmkuZG.js} +9 -8
  100. package/webapp/dist/assets/diagram-IFDJBPK2-NhtmkuZG.js.map +1 -0
  101. package/webapp/dist/assets/{diagram-S2PKOQOG-CkK4SRyE.js → diagram-P4PSJMXO-B9FcmokX.js} +8 -8
  102. package/webapp/dist/assets/diagram-P4PSJMXO-B9FcmokX.js.map +1 -0
  103. package/webapp/dist/assets/{erDiagram-Q2GNP2WA-DkI5eYww.js → erDiagram-INFDFZHY-DHKmWvtB.js} +96 -75
  104. package/webapp/dist/assets/erDiagram-INFDFZHY-DHKmWvtB.js.map +1 -0
  105. package/webapp/dist/assets/{flowDiagram-NV44I4VS-wOdPUQ7Y.js → flowDiagram-PKNHOUZH-C7Zi8I7T.js} +98 -81
  106. package/webapp/dist/assets/flowDiagram-PKNHOUZH-C7Zi8I7T.js.map +1 -0
  107. package/webapp/dist/assets/{ganttDiagram-JELNMOA3-BtRWgkUH.js → ganttDiagram-A5KZAMGK-Cv2T8tz_.js} +28 -3
  108. package/webapp/dist/assets/ganttDiagram-A5KZAMGK-Cv2T8tz_.js.map +1 -0
  109. package/webapp/dist/assets/{gitGraphDiagram-V2S2FVAM-Bsz7u1vi.js → gitGraphDiagram-K3NZZRJ6-DztaipJU.js} +38 -46
  110. package/webapp/dist/assets/gitGraphDiagram-K3NZZRJ6-DztaipJU.js.map +1 -0
  111. package/webapp/dist/assets/graph-C5yf62Vs.js +782 -0
  112. package/webapp/dist/assets/graph-C5yf62Vs.js.map +1 -0
  113. package/webapp/dist/assets/{index-xvYYeHuy.css → index-YaxF76or.css} +1 -1
  114. package/webapp/dist/assets/{index-rYmIohM_.js → index-hve5MWPs.js} +1603 -1415
  115. package/webapp/dist/assets/index-hve5MWPs.js.map +1 -0
  116. package/webapp/dist/assets/{infoDiagram-HS3SLOUP-BMaxCvH5.js → infoDiagram-LFFYTUFH-VgsbBPZP.js} +7 -7
  117. package/webapp/dist/assets/infoDiagram-LFFYTUFH-VgsbBPZP.js.map +1 -0
  118. package/webapp/dist/assets/init-ZxktEp_H.js.map +1 -1
  119. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-C7j3YWdw.js +966 -0
  120. package/webapp/dist/assets/ishikawaDiagram-PHBUUO56-C7j3YWdw.js.map +1 -0
  121. package/webapp/dist/assets/{journeyDiagram-XKPGCS4Q-ejyerzmG.js → journeyDiagram-4ABVD52K-OO8sev-Y.js} +5 -5
  122. package/webapp/dist/assets/journeyDiagram-4ABVD52K-OO8sev-Y.js.map +1 -0
  123. package/webapp/dist/assets/{kanban-definition-3W4ZIXB7-CYj35TEs.js → kanban-definition-K7BYSVSG-DiYCC1Ig.js} +5 -3
  124. package/webapp/dist/assets/kanban-definition-K7BYSVSG-DiYCC1Ig.js.map +1 -0
  125. package/webapp/dist/assets/{layout-7Ql4zmuL.js → layout-DdZSgGdu.js} +5 -5
  126. package/webapp/dist/assets/layout-DdZSgGdu.js.map +1 -0
  127. package/webapp/dist/assets/{linear-CVmgVPuZ.js → linear-7-aHtaFi.js} +2 -2
  128. package/webapp/dist/assets/linear-7-aHtaFi.js.map +1 -0
  129. package/webapp/dist/assets/{mindmap-definition-VGOIOE7T-DOpxjGVo.js → mindmap-definition-YRQLILUH-IG3I-RdD.js} +7 -5
  130. package/webapp/dist/assets/mindmap-definition-YRQLILUH-IG3I-RdD.js.map +1 -0
  131. package/webapp/dist/assets/ordinal-CxptdPJm.js.map +1 -1
  132. package/webapp/dist/assets/{pieDiagram-ADFJNKIX-CLQjpmAG.js → pieDiagram-SKSYHLDU-z68KJT5r.js} +8 -8
  133. package/webapp/dist/assets/pieDiagram-SKSYHLDU-z68KJT5r.js.map +1 -0
  134. package/webapp/dist/assets/{quadrantDiagram-AYHSOK5B-ClD_bz7z.js → quadrantDiagram-337W2JSQ-DaENWdO6.js} +3 -3
  135. package/webapp/dist/assets/quadrantDiagram-337W2JSQ-DaENWdO6.js.map +1 -0
  136. package/webapp/dist/assets/{requirementDiagram-UZGBJVZJ-DOpb-TWH.js → requirementDiagram-Z7DCOOCP-ROTFv4sa.js} +16 -6
  137. package/webapp/dist/assets/requirementDiagram-Z7DCOOCP-ROTFv4sa.js.map +1 -0
  138. package/webapp/dist/assets/{sankeyDiagram-TZEHDZUN-D8Hsj3yx.js → sankeyDiagram-WA2Y5GQK-CK7qtpzw.js} +2 -2
  139. package/webapp/dist/assets/sankeyDiagram-WA2Y5GQK-CK7qtpzw.js.map +1 -0
  140. package/webapp/dist/assets/{sequenceDiagram-WL72ISMW-CFMNjBER.js → sequenceDiagram-2WXFIKYE-R5lDySeI.js} +601 -201
  141. package/webapp/dist/assets/sequenceDiagram-2WXFIKYE-R5lDySeI.js.map +1 -0
  142. package/webapp/dist/assets/{stateDiagram-FKZM4ZOC-BQeDlw0P.js → stateDiagram-RAJIS63D-sr7msF5U.js} +9 -9
  143. package/webapp/dist/assets/stateDiagram-RAJIS63D-sr7msF5U.js.map +1 -0
  144. package/webapp/dist/assets/{stateDiagram-v2-4FDKWEC3-DscX61Rs.js → stateDiagram-v2-FVOUBMTO-X663liwS.js} +5 -5
  145. package/webapp/dist/assets/stateDiagram-v2-FVOUBMTO-X663liwS.js.map +1 -0
  146. package/webapp/dist/assets/{timeline-definition-IT6M3QCI-BcXPSTiw.js → timeline-definition-YZTLITO2-Bw0TdG26.js} +3 -3
  147. package/webapp/dist/assets/timeline-definition-YZTLITO2-Bw0TdG26.js.map +1 -0
  148. package/webapp/dist/assets/{treemap-GDKQZRPO-BBr4UV0Z.js → treemap-KZPCXAKY-D_sjKwI7.js} +37 -24
  149. package/webapp/dist/assets/treemap-KZPCXAKY-D_sjKwI7.js.map +1 -0
  150. package/webapp/dist/assets/vennDiagram-LZ73GAT5-DhlHIHid.js +2487 -0
  151. package/webapp/dist/assets/vennDiagram-LZ73GAT5-DhlHIHid.js.map +1 -0
  152. package/webapp/dist/assets/{xychartDiagram-PRI3JC2R-CS5RAtQE.js → xychartDiagram-JWTSCODW-C65ESjTc.js} +4 -4
  153. package/webapp/dist/assets/xychartDiagram-JWTSCODW-C65ESjTc.js.map +1 -0
  154. package/webapp/dist/index.html +2 -2
  155. package/webapp/dist/assets/_basePickBy-CBOtd63g.js.map +0 -1
  156. package/webapp/dist/assets/_baseUniq-mfoKz4Wm.js.map +0 -1
  157. package/webapp/dist/assets/arc-Dq0WZLyu.js.map +0 -1
  158. package/webapp/dist/assets/architectureDiagram-VXUJARFQ-CNmygmp3.js.map +0 -1
  159. package/webapp/dist/assets/blockDiagram-VD42YOAC-DvE0lybt.js.map +0 -1
  160. package/webapp/dist/assets/c4Diagram-YG6GDRKO-CR7zJ2_u.js.map +0 -1
  161. package/webapp/dist/assets/channel-DrTrnYx4.js.map +0 -1
  162. package/webapp/dist/assets/chunk-4BX2VUAB-CVuJEIeN.js.map +0 -1
  163. package/webapp/dist/assets/chunk-55IACEB6-BxUoXApB.js.map +0 -1
  164. package/webapp/dist/assets/chunk-B4BG7PRW-DpMa3-9L.js.map +0 -1
  165. package/webapp/dist/assets/chunk-DI55MBZ5-SAhxUTqQ.js.map +0 -1
  166. package/webapp/dist/assets/chunk-FMBD7UC4-TX-LVAaV.js.map +0 -1
  167. package/webapp/dist/assets/chunk-QN33PNHL-D1uiKlOO.js.map +0 -1
  168. package/webapp/dist/assets/chunk-QZHKN3VN-BxuV0Oba.js.map +0 -1
  169. package/webapp/dist/assets/chunk-TZMSLE5B-Cw689yRl.js.map +0 -1
  170. package/webapp/dist/assets/classDiagram-2ON5EDUG-BTTGianr.js.map +0 -1
  171. package/webapp/dist/assets/classDiagram-v2-WZHVMYZB-BTTGianr.js.map +0 -1
  172. package/webapp/dist/assets/clone-Dk8cAI3I.js.map +0 -1
  173. package/webapp/dist/assets/cose-bilkent-S5V4N54A-BjJnzB2N.js.map +0 -1
  174. package/webapp/dist/assets/dagre-6UL2VRFP-VF-xGhAf.js.map +0 -1
  175. package/webapp/dist/assets/diagram-PSM6KHXK-Ba5U0oRY.js.map +0 -1
  176. package/webapp/dist/assets/diagram-QEK2KX5R-DoYCnEw_.js.map +0 -1
  177. package/webapp/dist/assets/diagram-S2PKOQOG-CkK4SRyE.js.map +0 -1
  178. package/webapp/dist/assets/erDiagram-Q2GNP2WA-DkI5eYww.js.map +0 -1
  179. package/webapp/dist/assets/flowDiagram-NV44I4VS-wOdPUQ7Y.js.map +0 -1
  180. package/webapp/dist/assets/ganttDiagram-JELNMOA3-BtRWgkUH.js.map +0 -1
  181. package/webapp/dist/assets/gitGraphDiagram-V2S2FVAM-Bsz7u1vi.js.map +0 -1
  182. package/webapp/dist/assets/graph-DAMkuTbn.js +0 -425
  183. package/webapp/dist/assets/graph-DAMkuTbn.js.map +0 -1
  184. package/webapp/dist/assets/index-rYmIohM_.js.map +0 -1
  185. package/webapp/dist/assets/infoDiagram-HS3SLOUP-BMaxCvH5.js.map +0 -1
  186. package/webapp/dist/assets/journeyDiagram-XKPGCS4Q-ejyerzmG.js.map +0 -1
  187. package/webapp/dist/assets/kanban-definition-3W4ZIXB7-CYj35TEs.js.map +0 -1
  188. package/webapp/dist/assets/layout-7Ql4zmuL.js.map +0 -1
  189. package/webapp/dist/assets/linear-CVmgVPuZ.js.map +0 -1
  190. package/webapp/dist/assets/mindmap-definition-VGOIOE7T-DOpxjGVo.js.map +0 -1
  191. package/webapp/dist/assets/pieDiagram-ADFJNKIX-CLQjpmAG.js.map +0 -1
  192. package/webapp/dist/assets/quadrantDiagram-AYHSOK5B-ClD_bz7z.js.map +0 -1
  193. package/webapp/dist/assets/requirementDiagram-UZGBJVZJ-DOpb-TWH.js.map +0 -1
  194. package/webapp/dist/assets/sankeyDiagram-TZEHDZUN-D8Hsj3yx.js.map +0 -1
  195. package/webapp/dist/assets/sequenceDiagram-WL72ISMW-CFMNjBER.js.map +0 -1
  196. package/webapp/dist/assets/stateDiagram-FKZM4ZOC-BQeDlw0P.js.map +0 -1
  197. package/webapp/dist/assets/stateDiagram-v2-4FDKWEC3-DscX61Rs.js.map +0 -1
  198. package/webapp/dist/assets/timeline-definition-IT6M3QCI-BcXPSTiw.js.map +0 -1
  199. package/webapp/dist/assets/treemap-GDKQZRPO-BBr4UV0Z.js.map +0 -1
  200. package/webapp/dist/assets/xychartDiagram-PRI3JC2R-CS5RAtQE.js.map +0 -1
@@ -2,7 +2,9 @@
2
2
  /**
3
3
  * Module: llm/gen/openai
4
4
  *
5
- * OpenAI-compatible Responses API integration implementing streaming and batch generation.
5
+ * OpenAI Responses API integration implementing streaming and batch generation.
6
+ * Isolation principle: this wrapper owns OpenAI Responses request/stream semantics and must not
7
+ * inherit Codex-specific abstractions or parameter aliases.
6
8
  */
7
9
  var __importDefault = (this && this.__importDefault) || function (mod) {
8
10
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -19,6 +21,19 @@ const failure_classifier_1 = require("./failure-classifier");
19
21
  const tool_call_context_1 = require("./tool-call-context");
20
22
  const tool_output_limit_1 = require("./tool-output-limit");
21
23
  const log = (0, log_1.createLogger)('llm/openai');
24
+ const OPENAI_API_QUIRK_VENDOR_HEARTBEAT_EVENT_TYPES = {
25
+ 'xcode.best': ['keepalive'],
26
+ };
27
+ const OPENAI_NATIVE_TOOL_ITEM_TYPES = new Set([
28
+ 'file_search_call',
29
+ 'code_interpreter_call',
30
+ 'image_generation_call',
31
+ 'mcp_call',
32
+ 'mcp_list_tools',
33
+ 'mcp_approval_request',
34
+ 'custom_tool_call',
35
+ ]);
36
+ const OPENAI_MALFORMED_BATCH_OUTPUT_ITEM_ERROR_CODE = 'OPENAI_MALFORMED_BATCH_OUTPUT_ITEM';
22
37
  function limitOpenAiToolOutputText(text, msg, limitChars) {
23
38
  const limited = (0, tool_output_limit_1.truncateProviderToolOutputText)(text, limitChars);
24
39
  if (limited.truncated) {
@@ -65,6 +80,59 @@ function limitOpenAiToolOutputItems(output, msg, limitChars) {
65
80
  function isRecord(value) {
66
81
  return typeof value === 'object' && value !== null && !Array.isArray(value);
67
82
  }
83
+ function readEventType(value) {
84
+ if (!isRecord(value) || typeof value.type !== 'string') {
85
+ return null;
86
+ }
87
+ return value.type;
88
+ }
89
+ function normalizeProviderApiQuirks(providerConfig) {
90
+ const raw = providerConfig.apiQuirks;
91
+ if (typeof raw === 'string') {
92
+ return raw.trim().length > 0 ? new Set([raw.trim()]) : new Set();
93
+ }
94
+ if (Array.isArray(raw)) {
95
+ return new Set(raw.filter((entry) => typeof entry === 'string' && entry.trim() !== ''));
96
+ }
97
+ return new Set();
98
+ }
99
+ function resolveOpenAiVendorHeartbeatEventTypes(providerConfig) {
100
+ const eventTypes = new Set();
101
+ for (const quirk of normalizeProviderApiQuirks(providerConfig)) {
102
+ const vendorEventTypes = OPENAI_API_QUIRK_VENDOR_HEARTBEAT_EVENT_TYPES[quirk];
103
+ if (!vendorEventTypes)
104
+ continue;
105
+ for (const eventType of vendorEventTypes) {
106
+ eventTypes.add(eventType);
107
+ }
108
+ }
109
+ return eventTypes;
110
+ }
111
+ function maybeAnnotateOpenAiQuirkFailure(providerConfig, error) {
112
+ const quirks = normalizeProviderApiQuirks(providerConfig);
113
+ const message = error instanceof Error
114
+ ? error.message
115
+ : typeof error === 'string'
116
+ ? error
117
+ : isRecord(error) && typeof error.message === 'string'
118
+ ? error.message
119
+ : '';
120
+ const lowerMessage = message.toLowerCase();
121
+ if (quirks.has('xcode.best') &&
122
+ lowerMessage.includes('stream error:') &&
123
+ lowerMessage.includes('internal_error') &&
124
+ lowerMessage.includes('received from peer')) {
125
+ if (error instanceof Error) {
126
+ const out = error;
127
+ out.code = 'XCODE_BEST_STREAM_INTERNAL_ERROR';
128
+ return out;
129
+ }
130
+ const out = new Error(message.length > 0 ? message : 'xcode.best stream internal error');
131
+ out.code = 'XCODE_BEST_STREAM_INTERNAL_ERROR';
132
+ return out;
133
+ }
134
+ return error;
135
+ }
68
136
  function tryExtractApiReturnedModel(value) {
69
137
  // NOTE: External API payload; a runtime check is unavoidable.
70
138
  if (!isRecord(value))
@@ -116,7 +184,6 @@ function chatMessageToOpenAiInputItem(msg) {
116
184
  };
117
185
  case 'transient_guide_msg':
118
186
  case 'saying_msg':
119
- case 'ui_only_markdown_msg':
120
187
  return {
121
188
  type: 'message',
122
189
  role: 'assistant',
@@ -125,7 +192,7 @@ function chatMessageToOpenAiInputItem(msg) {
125
192
  case 'thinking_msg':
126
193
  return thinkingMessageToOpenAiReasoningItem(msg);
127
194
  case 'tellask_result_msg':
128
- case 'tellask_carryover_result_msg':
195
+ case 'tellask_carryover_msg':
129
196
  return {
130
197
  type: 'message',
131
198
  role: 'user',
@@ -157,24 +224,15 @@ function buildReasoningPayloadFromText(text) {
157
224
  summary: [{ type: 'summary_text', text }],
158
225
  };
159
226
  }
160
- function buildReasoningItemId(genseq, text) {
161
- let hash = 0;
162
- for (let i = 0; i < text.length; i += 1) {
163
- hash = (hash * 31 + text.charCodeAt(i)) >>> 0;
164
- }
165
- return `dominds_reasoning_${genseq}_${hash.toString(16)}`;
166
- }
167
227
  function thinkingMessageToOpenAiReasoningItem(msg) {
168
228
  const reasoning = msg.reasoning ?? buildReasoningPayloadFromText(msg.content);
169
229
  if (!reasoning) {
170
- return {
171
- id: buildReasoningItemId(msg.genseq, msg.content),
172
- type: 'reasoning',
173
- summary: [],
174
- };
230
+ return { type: 'reasoning', summary: [] };
175
231
  }
232
+ // Some Responses-compatible gateways reject client-fabricated reasoning IDs and only accept
233
+ // provider-issued IDs (for example `rs_*`). We therefore omit `id` when replaying persisted
234
+ // reasoning and rely on encrypted_content when available.
176
235
  const out = {
177
- id: buildReasoningItemId(msg.genseq, `${msg.content}\n${reasoning.encrypted_content ?? ''}`),
178
236
  type: 'reasoning',
179
237
  summary: reasoning.summary.map((part) => ({ type: 'summary_text', text: part.text })),
180
238
  };
@@ -271,6 +329,11 @@ function mergeAdjacentOpenAiMessages(input) {
271
329
  }
272
330
  return merged;
273
331
  }
332
+ function shouldIncludeOpenAiEncryptedReasoning(input, reasoning) {
333
+ if (reasoning !== undefined)
334
+ return true;
335
+ return input.some((item) => isRecord(item) && item.type === 'reasoning');
336
+ }
274
337
  async function buildOpenAiRequestInput(context, providerConfig) {
275
338
  const normalized = (0, tool_call_context_1.normalizeToolCallPairs)(context);
276
339
  const violation = (0, tool_call_context_1.findFirstToolCallAdjacencyViolation)(normalized);
@@ -308,13 +371,40 @@ function parseOpenAiUsage(usage) {
308
371
  totalTokens: typeof totalTokens === 'number' ? totalTokens : inputTokens + outputTokens,
309
372
  };
310
373
  }
311
- function buildOpenAiTextConfig(openAiParams, jsonResponseEnabled) {
374
+ function buildOpenAiTextConfig(openAiParams) {
312
375
  const textConfig = {};
313
376
  if (openAiParams.verbosity !== undefined) {
314
377
  textConfig.verbosity = openAiParams.verbosity;
315
378
  }
316
- if (jsonResponseEnabled) {
317
- textConfig.format = { type: 'json_object' };
379
+ const textFormat = openAiParams.text_format;
380
+ if (textFormat === 'text' || textFormat === 'json_object') {
381
+ textConfig.format = { type: textFormat };
382
+ }
383
+ else if (textFormat === 'json_schema') {
384
+ const schemaName = openAiParams.text_format_json_schema_name?.trim();
385
+ const rawSchema = openAiParams.text_format_json_schema?.trim();
386
+ if (!schemaName || !rawSchema) {
387
+ throw new Error('Invalid openai text_format=json_schema: text_format_json_schema_name and text_format_json_schema are required.');
388
+ }
389
+ let parsedSchema;
390
+ try {
391
+ parsedSchema = JSON.parse(rawSchema);
392
+ }
393
+ catch (error) {
394
+ const message = error instanceof Error ? error.message : String(error);
395
+ throw new Error(`Invalid openai text_format_json_schema: ${message}`);
396
+ }
397
+ if (!isRecord(parsedSchema)) {
398
+ throw new Error('Invalid openai text_format_json_schema: expected a JSON object at the top level.');
399
+ }
400
+ textConfig.format = {
401
+ type: 'json_schema',
402
+ name: schemaName,
403
+ schema: parsedSchema,
404
+ ...(openAiParams.text_format_json_schema_strict !== undefined
405
+ ? { strict: openAiParams.text_format_json_schema_strict }
406
+ : {}),
407
+ };
318
408
  }
319
409
  return textConfig.verbosity !== undefined || textConfig.format !== undefined
320
410
  ? textConfig
@@ -332,10 +422,389 @@ function buildOpenAiReasoning(openAiParams) {
332
422
  ...(summary !== undefined ? { summary } : {}),
333
423
  };
334
424
  }
335
- function resolveOpenAiJsonResponseEnabled(agent, openAiParams) {
336
- if (openAiParams.json_response !== undefined)
337
- return openAiParams.json_response;
338
- return agent.model_params?.json_response === true;
425
+ function buildOpenAiNativeTools(openAiParams) {
426
+ const enabled = openAiParams.web_search_tool === true ||
427
+ openAiParams.web_search_context_size !== undefined ||
428
+ (Array.isArray(openAiParams.web_search_allowed_domains) &&
429
+ openAiParams.web_search_allowed_domains.length > 0);
430
+ if (!enabled)
431
+ return [];
432
+ const webSearchTool = {
433
+ type: 'web_search',
434
+ ...(openAiParams.web_search_context_size !== undefined
435
+ ? { search_context_size: openAiParams.web_search_context_size }
436
+ : {}),
437
+ ...(Array.isArray(openAiParams.web_search_allowed_domains) &&
438
+ openAiParams.web_search_allowed_domains.length > 0
439
+ ? { filters: { allowed_domains: openAiParams.web_search_allowed_domains } }
440
+ : {}),
441
+ };
442
+ return [webSearchTool];
443
+ }
444
+ function buildOpenAiInclude(input, reasoning, openAiParams) {
445
+ const include = new Set();
446
+ if (shouldIncludeOpenAiEncryptedReasoning(input, reasoning)) {
447
+ include.add('reasoning.encrypted_content');
448
+ }
449
+ if (openAiParams.web_search_include_sources === true) {
450
+ include.add('web_search_call.action.sources');
451
+ }
452
+ return include.size > 0 ? Array.from(include) : undefined;
453
+ }
454
+ function toOpenAiLlmWebSearchCall(item, itemId, phase) {
455
+ const rawAction = isRecord(item.action) ? item.action : null;
456
+ let action;
457
+ if (rawAction?.type === 'search') {
458
+ const queries = Array.isArray(rawAction.queries) &&
459
+ rawAction.queries.every((entry) => typeof entry === 'string')
460
+ ? rawAction.queries
461
+ : undefined;
462
+ action = {
463
+ type: 'search',
464
+ ...(typeof rawAction.query === 'string' ? { query: rawAction.query } : {}),
465
+ ...(queries && queries.length > 0 ? { queries } : {}),
466
+ };
467
+ }
468
+ else if (rawAction?.type === 'open_page') {
469
+ action = {
470
+ type: 'open_page',
471
+ ...(typeof rawAction.url === 'string' ? { url: rawAction.url } : {}),
472
+ };
473
+ }
474
+ else if (rawAction?.type === 'find_in_page') {
475
+ action = {
476
+ type: 'find_in_page',
477
+ ...(typeof rawAction.url === 'string' ? { url: rawAction.url } : {}),
478
+ ...(typeof rawAction.pattern === 'string' ? { pattern: rawAction.pattern } : {}),
479
+ };
480
+ }
481
+ else {
482
+ action = undefined;
483
+ }
484
+ return {
485
+ source: 'openai_responses',
486
+ phase,
487
+ itemId,
488
+ ...(typeof item.status === 'string' ? { status: item.status } : {}),
489
+ ...(action !== undefined ? { action } : {}),
490
+ };
491
+ }
492
+ function buildOpenAiWebSearchProgressCall(itemId, phase, status, action) {
493
+ return {
494
+ source: 'openai_responses',
495
+ phase,
496
+ itemId,
497
+ status,
498
+ ...(action !== undefined ? { action } : {}),
499
+ };
500
+ }
501
+ function throwOpenAiMalformedBatchOutputItem(itemType, detail) {
502
+ const message = `OPENAI malformed batch output item ${itemType}: ${detail}`;
503
+ const error = new Error(message);
504
+ error.code = OPENAI_MALFORMED_BATCH_OUTPUT_ITEM_ERROR_CODE;
505
+ log.error(message, error);
506
+ throw error;
507
+ }
508
+ async function throwOpenAiMalformedStreamEvent(receiver, eventType, detail) {
509
+ const message = `OPENAI malformed stream event ${eventType}: ${detail}`;
510
+ log.error(message, new Error('openai_malformed_stream_event'));
511
+ if (receiver.streamError) {
512
+ await receiver.streamError(message);
513
+ }
514
+ throw new Error(message);
515
+ }
516
+ function buildOpenAiNativeToolCallFromState(state, phase) {
517
+ if (state.itemType === 'custom_tool_call') {
518
+ return {
519
+ source: 'openai_responses',
520
+ itemType: state.itemType,
521
+ phase,
522
+ callId: state.callId,
523
+ ...(state.itemId !== undefined ? { itemId: state.itemId } : {}),
524
+ ...(state.status !== undefined ? { status: state.status } : {}),
525
+ ...(state.title !== undefined ? { title: state.title } : {}),
526
+ ...(state.summary !== undefined ? { summary: state.summary } : {}),
527
+ ...(state.detail !== undefined ? { detail: state.detail } : {}),
528
+ };
529
+ }
530
+ return {
531
+ source: 'openai_responses',
532
+ itemType: state.itemType,
533
+ phase,
534
+ itemId: state.itemId,
535
+ ...(state.status !== undefined ? { status: state.status } : {}),
536
+ ...(state.title !== undefined ? { title: state.title } : {}),
537
+ ...(state.summary !== undefined ? { summary: state.summary } : {}),
538
+ ...(state.detail !== undefined ? { detail: state.detail } : {}),
539
+ };
540
+ }
541
+ function mergeOpenAiNativeToolSeedIntoState(state, seed) {
542
+ if (state.itemType !== seed.itemType) {
543
+ throw new Error(`OPENAI native tool tracker invariant violation: itemType mismatch (${state.itemType} !== ${seed.itemType})`);
544
+ }
545
+ if (state.itemType === 'custom_tool_call' && seed.itemType === 'custom_tool_call') {
546
+ if (state.callId !== seed.callId) {
547
+ throw new Error(`OPENAI native tool tracker invariant violation: custom callId mismatch (${state.callId} !== ${seed.callId})`);
548
+ }
549
+ if (seed.itemId !== undefined && seed.itemId.trim() !== '') {
550
+ const normalizedItemId = seed.itemId.trim();
551
+ if (state.itemId !== undefined && state.itemId !== normalizedItemId) {
552
+ throw new Error(`OPENAI native tool tracker invariant violation: custom itemId mismatch (${state.itemId} !== ${normalizedItemId})`);
553
+ }
554
+ state.itemId = normalizedItemId;
555
+ }
556
+ }
557
+ else if (state.itemType !== 'custom_tool_call' && seed.itemType !== 'custom_tool_call') {
558
+ if (state.itemId !== seed.itemId) {
559
+ throw new Error(`OPENAI native tool tracker invariant violation: item tool itemId mismatch (${state.itemId} !== ${seed.itemId})`);
560
+ }
561
+ }
562
+ if (seed.status !== undefined) {
563
+ state.status = seed.status;
564
+ }
565
+ if (seed.title !== undefined) {
566
+ state.title = seed.title;
567
+ }
568
+ if (seed.summary !== undefined) {
569
+ state.summary = seed.summary;
570
+ }
571
+ if (seed.detail !== undefined) {
572
+ state.detail = seed.detail;
573
+ }
574
+ return state;
575
+ }
576
+ class OpenAiNativeToolTracker {
577
+ constructor() {
578
+ this.itemToolByItemId = new Map();
579
+ this.customToolByCallId = new Map();
580
+ this.customCallIdByItemId = new Map();
581
+ }
582
+ upsertItemTool(seed) {
583
+ const existing = this.itemToolByItemId.get(seed.itemId);
584
+ const state = existing ??
585
+ {
586
+ itemType: seed.itemType,
587
+ itemId: seed.itemId,
588
+ };
589
+ mergeOpenAiNativeToolSeedIntoState(state, seed);
590
+ if (!existing) {
591
+ this.itemToolByItemId.set(seed.itemId, state);
592
+ }
593
+ return state;
594
+ }
595
+ upsertCustomTool(seed) {
596
+ const existing = this.customToolByCallId.get(seed.callId);
597
+ const state = existing ??
598
+ {
599
+ itemType: 'custom_tool_call',
600
+ callId: seed.callId,
601
+ };
602
+ if (seed.itemId !== undefined && seed.itemId.trim() !== '') {
603
+ const normalizedItemId = seed.itemId.trim();
604
+ const existingCallId = this.customCallIdByItemId.get(normalizedItemId);
605
+ if (existingCallId !== undefined && existingCallId !== seed.callId) {
606
+ throw new Error(`OPENAI native tool tracker invariant violation: custom itemId ${normalizedItemId} already bound to a different callId`);
607
+ }
608
+ }
609
+ mergeOpenAiNativeToolSeedIntoState(state, seed);
610
+ if (!existing) {
611
+ this.customToolByCallId.set(seed.callId, state);
612
+ }
613
+ if (state.itemId !== undefined) {
614
+ this.customCallIdByItemId.set(state.itemId, state.callId);
615
+ }
616
+ return state;
617
+ }
618
+ claimCustomToolByItemId(itemId) {
619
+ const mappedCallId = this.customCallIdByItemId.get(itemId);
620
+ if (mappedCallId !== undefined) {
621
+ const existing = this.customToolByCallId.get(mappedCallId);
622
+ if (!existing) {
623
+ throw new Error(`OPENAI native tool tracker invariant violation: missing custom tool state for mapped itemId=${itemId}`);
624
+ }
625
+ return existing;
626
+ }
627
+ const unresolved = Array.from(this.customToolByCallId.values()).filter((state) => state.itemId === undefined);
628
+ if (unresolved.length === 1) {
629
+ const claimed = unresolved[0];
630
+ claimed.itemId = itemId;
631
+ this.customCallIdByItemId.set(itemId, claimed.callId);
632
+ return claimed;
633
+ }
634
+ if (unresolved.length === 0) {
635
+ throw new Error(`OPENAI native tool tracker invariant violation: missing unresolved custom tool state for itemId=${itemId}`);
636
+ }
637
+ throw new Error(`OPENAI native tool tracker invariant violation: ambiguous custom tool itemId correlation for itemId=${itemId}`);
638
+ }
639
+ upsert(seed) {
640
+ return seed.itemType === 'custom_tool_call'
641
+ ? this.upsertCustomTool(seed)
642
+ : this.upsertItemTool(seed);
643
+ }
644
+ }
645
+ function summarizeCodeInterpreterOutputs(value) {
646
+ if (!Array.isArray(value) || value.length === 0)
647
+ return undefined;
648
+ const parts = [];
649
+ let imageCount = 0;
650
+ for (const output of value) {
651
+ if (!isRecord(output) || typeof output.type !== 'string')
652
+ continue;
653
+ if (output.type === 'logs' && typeof output.logs === 'string' && output.logs.trim() !== '') {
654
+ parts.push(output.logs.trim());
655
+ continue;
656
+ }
657
+ if (output.type === 'image') {
658
+ imageCount += 1;
659
+ }
660
+ }
661
+ if (imageCount > 0) {
662
+ parts.push(`images=${String(imageCount)}`);
663
+ }
664
+ return parts.length > 0 ? parts.join('\n') : undefined;
665
+ }
666
+ function buildOpenAiNativeToolSeed(item, itemId) {
667
+ if (typeof item.type !== 'string' ||
668
+ !OPENAI_NATIVE_TOOL_ITEM_TYPES.has(item.type)) {
669
+ return null;
670
+ }
671
+ const itemType = item.type;
672
+ if (itemType === 'custom_tool_call') {
673
+ const callId = typeof item.call_id === 'string' ? item.call_id.trim() : '';
674
+ if (callId === '') {
675
+ throwOpenAiMalformedBatchOutputItem(itemType, 'missing call_id');
676
+ }
677
+ const base = {
678
+ itemType,
679
+ callId,
680
+ ...(typeof itemId === 'string' && itemId.trim() !== '' ? { itemId: itemId.trim() } : {}),
681
+ };
682
+ const namespace = typeof item.namespace === 'string' ? item.namespace.trim() : '';
683
+ const name = typeof item.name === 'string' ? item.name.trim() : '';
684
+ const summary = namespace !== '' && name !== ''
685
+ ? `${namespace}:${name}`
686
+ : namespace !== ''
687
+ ? namespace
688
+ : name;
689
+ return {
690
+ ...base,
691
+ title: 'Custom Tool Call',
692
+ ...(summary !== '' ? { summary } : {}),
693
+ ...(typeof item.input === 'string' && item.input.trim() !== ''
694
+ ? { detail: item.input.trim() }
695
+ : {}),
696
+ };
697
+ }
698
+ if (typeof itemId !== 'string' || itemId.trim() === '') {
699
+ throwOpenAiMalformedBatchOutputItem(itemType, 'missing itemId');
700
+ }
701
+ const base = {
702
+ itemType,
703
+ itemId: itemId.trim(),
704
+ };
705
+ switch (itemType) {
706
+ case 'file_search_call': {
707
+ const queries = Array.isArray(item.queries) && item.queries.every((entry) => typeof entry === 'string')
708
+ ? item.queries
709
+ : [];
710
+ const results = Array.isArray(item.results) ? item.results.length : 0;
711
+ return {
712
+ ...base,
713
+ ...(typeof item.status === 'string' ? { status: item.status } : {}),
714
+ title: 'File Search',
715
+ ...(queries.length > 0 ? { summary: queries.join('\n') } : {}),
716
+ ...(results > 0 ? { detail: `results=${String(results)}` } : {}),
717
+ };
718
+ }
719
+ case 'code_interpreter_call': {
720
+ return {
721
+ ...base,
722
+ ...(typeof item.status === 'string' ? { status: item.status } : {}),
723
+ title: 'Code Interpreter',
724
+ ...(typeof item.code === 'string' && item.code.trim() !== ''
725
+ ? { summary: item.code.trim() }
726
+ : {}),
727
+ ...(typeof item.container_id === 'string' && item.container_id.trim() !== ''
728
+ ? {
729
+ detail: `container=${item.container_id.trim()}${summarizeCodeInterpreterOutputs(item.outputs) ? `\n${summarizeCodeInterpreterOutputs(item.outputs)}` : ''}`,
730
+ }
731
+ : summarizeCodeInterpreterOutputs(item.outputs)
732
+ ? { detail: summarizeCodeInterpreterOutputs(item.outputs) }
733
+ : {}),
734
+ };
735
+ }
736
+ case 'image_generation_call': {
737
+ return {
738
+ ...base,
739
+ ...(typeof item.status === 'string' ? { status: item.status } : {}),
740
+ title: 'Image Generation',
741
+ summary: typeof item.result === 'string' && item.result.length > 0
742
+ ? 'image_ready'
743
+ : 'image_pending',
744
+ };
745
+ }
746
+ case 'mcp_call': {
747
+ const serverLabel = typeof item.server_label === 'string' ? item.server_label.trim() : '';
748
+ const name = typeof item.name === 'string' ? item.name.trim() : '';
749
+ const summary = serverLabel !== '' && name !== ''
750
+ ? `${serverLabel}: ${name}`
751
+ : serverLabel !== ''
752
+ ? serverLabel
753
+ : name;
754
+ const detailParts = [
755
+ typeof item.arguments === 'string' && item.arguments.trim() !== ''
756
+ ? item.arguments.trim()
757
+ : '',
758
+ typeof item.output === 'string' && item.output.trim() !== '' ? item.output.trim() : '',
759
+ typeof item.error === 'string' && item.error.trim() !== ''
760
+ ? `error=${item.error.trim()}`
761
+ : '',
762
+ ].filter((part) => part.length > 0);
763
+ return {
764
+ ...base,
765
+ ...(typeof item.status === 'string' ? { status: item.status } : {}),
766
+ title: 'MCP Tool Call',
767
+ ...(summary !== '' ? { summary } : {}),
768
+ ...(detailParts.length > 0 ? { detail: detailParts.join('\n') } : {}),
769
+ };
770
+ }
771
+ case 'mcp_list_tools': {
772
+ const serverLabel = typeof item.server_label === 'string' ? item.server_label.trim() : '';
773
+ const toolNames = Array.isArray(item.tools)
774
+ ? item.tools
775
+ .filter((entry) => isRecord(entry))
776
+ .map((entry) => (typeof entry.name === 'string' ? entry.name.trim() : ''))
777
+ .filter((entry) => entry.length > 0)
778
+ : [];
779
+ const error = typeof item.error === 'string' ? item.error.trim() : '';
780
+ return {
781
+ ...base,
782
+ title: 'MCP Tool Discovery',
783
+ ...(serverLabel !== '' ? { summary: serverLabel } : {}),
784
+ ...(toolNames.length > 0 || error !== ''
785
+ ? { detail: [...toolNames, ...(error !== '' ? [`error=${error}`] : [])].join('\n') }
786
+ : {}),
787
+ };
788
+ }
789
+ case 'mcp_approval_request': {
790
+ const serverLabel = typeof item.server_label === 'string' ? item.server_label.trim() : '';
791
+ const name = typeof item.name === 'string' ? item.name.trim() : '';
792
+ return {
793
+ ...base,
794
+ title: 'MCP Approval Request',
795
+ ...(serverLabel !== '' || name !== ''
796
+ ? { summary: [serverLabel, name].filter(Boolean).join(': ') }
797
+ : {}),
798
+ ...(typeof item.arguments === 'string' && item.arguments.trim() !== ''
799
+ ? { detail: item.arguments.trim() }
800
+ : {}),
801
+ };
802
+ }
803
+ default: {
804
+ const _exhaustive = itemType;
805
+ return _exhaustive;
806
+ }
807
+ }
339
808
  }
340
809
  async function buildOpenAiRequestInputWrapper(context, providerConfig) {
341
810
  return await buildOpenAiRequestInput(context, providerConfig);
@@ -347,16 +816,27 @@ function extractOutputMessageText(item) {
347
816
  if (!Array.isArray(content))
348
817
  return '';
349
818
  let text = '';
350
- for (const part of content) {
351
- if (!isRecord(part) || typeof part.type !== 'string')
819
+ let transcript = '';
820
+ let sawOutputText = false;
821
+ for (const rawPart of content) {
822
+ if (!isRecord(rawPart) || typeof rawPart.type !== 'string')
352
823
  continue;
353
- if (part.type === 'output_text' && typeof part.text === 'string') {
354
- text += part.text;
824
+ if (rawPart.type === 'output_text' && typeof rawPart.text === 'string') {
825
+ sawOutputText = true;
826
+ text += rawPart.text;
827
+ }
828
+ if (rawPart.type === 'output_audio' && typeof rawPart.transcript === 'string') {
829
+ transcript += rawPart.transcript;
355
830
  }
356
- if (part.type === 'refusal' && typeof part.refusal === 'string') {
357
- text += part.refusal;
831
+ if (rawPart.type === 'refusal' && typeof rawPart.refusal === 'string') {
832
+ text += rawPart.refusal;
358
833
  }
359
834
  }
835
+ // Keep batch-mode message extraction aligned with streaming semantics: transcript text is only a
836
+ // fallback when the response item did not already carry canonical output_text content.
837
+ if (!sawOutputText && transcript.length > 0) {
838
+ text += transcript;
839
+ }
360
840
  return text;
361
841
  }
362
842
  function extractReasoningText(item) {
@@ -407,11 +887,12 @@ function extractReasoningPayload(item) {
407
887
  out.encrypted_content = encrypted;
408
888
  return out;
409
889
  }
410
- function openAiResponseToChatMessages(response, genseq) {
411
- const messages = [];
890
+ function openAiResponseToBatchOutputs(response, genseq) {
891
+ const outputs = [];
412
892
  const output = response.output;
413
893
  if (!Array.isArray(output))
414
- return messages;
894
+ return outputs;
895
+ const nativeToolTracker = new OpenAiNativeToolTracker();
415
896
  for (const item of output) {
416
897
  if (!isRecord(item) || typeof item.type !== 'string')
417
898
  continue;
@@ -419,12 +900,15 @@ function openAiResponseToChatMessages(response, genseq) {
419
900
  const reasoning = extractReasoningPayload(item);
420
901
  const content = extractReasoningText(item);
421
902
  if (content.length > 0 || reasoning !== null) {
422
- messages.push({
423
- type: 'thinking_msg',
424
- role: 'assistant',
425
- genseq,
426
- content,
427
- reasoning: reasoning ?? undefined,
903
+ outputs.push({
904
+ kind: 'message',
905
+ message: {
906
+ type: 'thinking_msg',
907
+ role: 'assistant',
908
+ genseq,
909
+ content,
910
+ reasoning: reasoning ?? undefined,
911
+ },
428
912
  });
429
913
  }
430
914
  continue;
@@ -432,11 +916,14 @@ function openAiResponseToChatMessages(response, genseq) {
432
916
  if (item.type === 'message') {
433
917
  const content = extractOutputMessageText(item);
434
918
  if (content.length > 0) {
435
- messages.push({
436
- type: 'saying_msg',
437
- role: 'assistant',
438
- genseq,
439
- content,
919
+ outputs.push({
920
+ kind: 'message',
921
+ message: {
922
+ type: 'saying_msg',
923
+ role: 'assistant',
924
+ genseq,
925
+ content,
926
+ },
440
927
  });
441
928
  }
442
929
  continue;
@@ -446,19 +933,44 @@ function openAiResponseToChatMessages(response, genseq) {
446
933
  const name = typeof item.name === 'string' ? item.name : '';
447
934
  const args = typeof item.arguments === 'string' ? item.arguments : '';
448
935
  if (callId.length > 0 && name.length > 0) {
449
- messages.push({
450
- type: 'func_call_msg',
451
- role: 'assistant',
452
- genseq,
453
- id: callId,
454
- name,
455
- arguments: args,
936
+ outputs.push({
937
+ kind: 'message',
938
+ message: {
939
+ type: 'func_call_msg',
940
+ role: 'assistant',
941
+ genseq,
942
+ id: callId,
943
+ name,
944
+ arguments: args,
945
+ },
456
946
  });
457
947
  }
458
948
  continue;
459
949
  }
950
+ if (item.type === 'web_search_call') {
951
+ const itemId = typeof item.id === 'string' ? item.id.trim() : '';
952
+ if (itemId.length === 0) {
953
+ throwOpenAiMalformedBatchOutputItem(item.type, 'missing itemId');
954
+ }
955
+ outputs.push({
956
+ kind: 'web_search_call',
957
+ call: toOpenAiLlmWebSearchCall(item, itemId, 'done'),
958
+ });
959
+ continue;
960
+ }
961
+ if (OPENAI_NATIVE_TOOL_ITEM_TYPES.has(item.type)) {
962
+ const itemId = typeof item.id === 'string' ? item.id.trim() : undefined;
963
+ const seed = buildOpenAiNativeToolSeed(item, itemId);
964
+ if (seed) {
965
+ const state = nativeToolTracker.upsert(seed);
966
+ outputs.push({
967
+ kind: 'native_tool_call',
968
+ call: buildOpenAiNativeToolCallFromState(state, 'done'),
969
+ });
970
+ }
971
+ }
460
972
  }
461
- return messages;
973
+ return outputs;
462
974
  }
463
975
  class OpenAiGen {
464
976
  get apiType() {
@@ -478,13 +990,16 @@ class OpenAiGen {
478
990
  const requestInput = await buildOpenAiRequestInput(context, providerConfig);
479
991
  const openAiParams = agent.model_params?.openai || {};
480
992
  const maxTokens = agent.model_params?.max_tokens;
481
- const jsonResponseEnabled = resolveOpenAiJsonResponseEnabled(agent, openAiParams);
482
993
  const modelInfo = providerConfig.models[agent.model];
483
994
  const outputLength = modelInfo?.output_length;
484
995
  const maxOutputTokens = maxTokens ?? openAiParams.max_tokens ?? outputLength ?? 1024;
485
996
  const parallelToolCalls = openAiParams.parallel_tool_calls ?? true;
486
- const textConfig = buildOpenAiTextConfig(openAiParams, jsonResponseEnabled);
997
+ const textConfig = buildOpenAiTextConfig(openAiParams);
487
998
  const reasoning = buildOpenAiReasoning(openAiParams);
999
+ const nativeTools = buildOpenAiNativeTools(openAiParams);
1000
+ const include = buildOpenAiInclude(requestInput, reasoning, openAiParams);
1001
+ const tools = [...funcTools.map(funcToolToOpenAiTool), ...nativeTools];
1002
+ const vendorHeartbeatEventTypes = resolveOpenAiVendorHeartbeatEventTypes(providerConfig);
488
1003
  const payload = {
489
1004
  model: agent.model,
490
1005
  input: requestInput,
@@ -494,12 +1009,16 @@ class OpenAiGen {
494
1009
  store: false,
495
1010
  stream: true,
496
1011
  ...(openAiParams.service_tier !== undefined && { service_tier: openAiParams.service_tier }),
1012
+ ...(openAiParams.safety_identifier !== undefined && {
1013
+ safety_identifier: openAiParams.safety_identifier,
1014
+ }),
497
1015
  ...(openAiParams.temperature !== undefined && { temperature: openAiParams.temperature }),
498
1016
  ...(openAiParams.top_p !== undefined && { top_p: openAiParams.top_p }),
499
1017
  ...(reasoning !== undefined && { reasoning }),
1018
+ ...(include !== undefined ? { include } : {}),
500
1019
  ...(textConfig !== undefined && { text: textConfig }),
501
- ...(funcTools.length > 0
502
- ? { tools: funcTools.map(funcToolToOpenAiTool), tool_choice: 'auto' }
1020
+ ...(tools.length > 0
1021
+ ? { tools, tool_choice: 'auto' }
503
1022
  : { tool_choice: 'none' }),
504
1023
  };
505
1024
  let sayingStarted = false;
@@ -507,9 +1026,33 @@ class OpenAiGen {
507
1026
  let currentThinkingContent = '';
508
1027
  let finishedThinkingFromDelta = false;
509
1028
  let sawOutputText = false;
1029
+ let currentAudioTranscript = '';
510
1030
  let activeStream = 'idle';
511
1031
  let usage = { kind: 'unavailable' };
512
1032
  let returnedModel;
1033
+ const nativeToolTracker = new OpenAiNativeToolTracker();
1034
+ function claimNativeToolStateByEventItemId(itemId, itemType, title) {
1035
+ try {
1036
+ if (itemType === 'custom_tool_call') {
1037
+ const existing = nativeToolTracker.claimCustomToolByItemId(itemId);
1038
+ if (existing.title === undefined || existing.title.trim() === '') {
1039
+ existing.title = title;
1040
+ }
1041
+ return existing;
1042
+ }
1043
+ return nativeToolTracker.upsert({
1044
+ itemType,
1045
+ itemId,
1046
+ title,
1047
+ });
1048
+ }
1049
+ catch (error) {
1050
+ const detail = error instanceof Error && error.message.trim().length > 0
1051
+ ? error.message
1052
+ : `failed to resolve native tool state for ${itemType}`;
1053
+ return throwOpenAiMalformedStreamEvent(receiver, itemType, detail);
1054
+ }
1055
+ }
513
1056
  function applyArgsDelta(state, chunk) {
514
1057
  if (chunk.length === 0)
515
1058
  return;
@@ -538,7 +1081,28 @@ class OpenAiGen {
538
1081
  state.emitted = true;
539
1082
  await receiver_.funcCall(state.callId, state.name, args);
540
1083
  }
1084
+ async function emitNativeToolCall(state, phase) {
1085
+ if (!receiver.nativeToolCall)
1086
+ return;
1087
+ await receiver.nativeToolCall(buildOpenAiNativeToolCallFromState(state, phase));
1088
+ }
541
1089
  const activeFuncCallsByItemId = new Map();
1090
+ const activeWebSearchCallsByItemId = new Map();
1091
+ function readOptionalEventString(value) {
1092
+ return typeof value === 'string' ? value : '';
1093
+ }
1094
+ async function requireEventString(value, eventType, field) {
1095
+ if (typeof value === 'string')
1096
+ return value;
1097
+ return await throwOpenAiMalformedStreamEvent(receiver, eventType, `missing string ${field}`);
1098
+ }
1099
+ async function requireNonEmptyEventItemId(value, eventType) {
1100
+ const itemId = (await requireEventString(value, eventType, 'item_id')).trim();
1101
+ if (itemId.length === 0) {
1102
+ await throwOpenAiMalformedStreamEvent(receiver, eventType, 'empty item_id');
1103
+ }
1104
+ return itemId;
1105
+ }
542
1106
  try {
543
1107
  const stream = await client.responses.create(payload, {
544
1108
  ...(abortSignal ? { signal: abortSignal } : {}),
@@ -547,6 +1111,14 @@ class OpenAiGen {
547
1111
  if (abortSignal?.aborted) {
548
1112
  throw new Error('AbortError');
549
1113
  }
1114
+ const providerEvent = event;
1115
+ const providerEventType = readEventType(providerEvent);
1116
+ if (providerEventType !== null && vendorHeartbeatEventTypes.has(providerEventType)) {
1117
+ // Some Responses-compatible gateways emit out-of-band heartbeat frames to keep idle
1118
+ // streams alive. They are not part of the official OpenAI event taxonomy and carry no
1119
+ // model semantics, so the OpenAI wrapper ignores them locally.
1120
+ continue;
1121
+ }
550
1122
  switch (event.type) {
551
1123
  case 'response.created':
552
1124
  case 'response.in_progress':
@@ -556,6 +1128,15 @@ class OpenAiGen {
556
1128
  returnedModel = tryExtractApiReturnedModel(event.response);
557
1129
  }
558
1130
  if (event.type === 'response.completed') {
1131
+ if (!sawOutputText && currentAudioTranscript.trim().length > 0) {
1132
+ if (!sayingStarted) {
1133
+ await receiver.sayingStart();
1134
+ }
1135
+ await receiver.sayingChunk(currentAudioTranscript);
1136
+ await receiver.sayingFinish();
1137
+ sayingStarted = false;
1138
+ sawOutputText = true;
1139
+ }
559
1140
  if (thinkingStarted) {
560
1141
  await receiver.thinkingFinish(buildReasoningPayloadFromText(currentThinkingContent));
561
1142
  thinkingStarted = false;
@@ -598,7 +1179,7 @@ class OpenAiGen {
598
1179
  throw new Error(message);
599
1180
  }
600
1181
  case 'response.output_text.delta': {
601
- const delta = event.delta;
1182
+ const delta = await requireEventString(event.delta, event.type, 'delta');
602
1183
  if (delta.length > 0) {
603
1184
  if (activeStream === 'thinking') {
604
1185
  const detail = 'OPENAI stream overlap violation: received output_text while thinking stream still active';
@@ -624,7 +1205,8 @@ class OpenAiGen {
624
1205
  break;
625
1206
  }
626
1207
  case 'response.output_text.done': {
627
- if (!sawOutputText && event.text.length > 0) {
1208
+ const text = await requireEventString(event.text, event.type, 'text');
1209
+ if (!sawOutputText && text.length > 0) {
628
1210
  if (activeStream === 'thinking') {
629
1211
  const detail = 'OPENAI stream overlap violation: received output_text while thinking stream still active';
630
1212
  log.error(detail, new Error('openai_stream_overlap_violation'));
@@ -643,7 +1225,7 @@ class OpenAiGen {
643
1225
  await receiver.sayingStart();
644
1226
  activeStream = 'saying';
645
1227
  }
646
- await receiver.sayingChunk(event.text);
1228
+ await receiver.sayingChunk(text);
647
1229
  sawOutputText = true;
648
1230
  }
649
1231
  if (sayingStarted) {
@@ -656,7 +1238,7 @@ class OpenAiGen {
656
1238
  }
657
1239
  case 'response.reasoning_text.delta':
658
1240
  case 'response.reasoning_summary_text.delta': {
659
- const delta = event.delta;
1241
+ const delta = await requireEventString(event.delta, event.type, 'delta');
660
1242
  if (delta.length > 0) {
661
1243
  if (activeStream === 'saying') {
662
1244
  const detail = 'OPENAI stream overlap violation: received reasoning while saying stream still active';
@@ -715,6 +1297,15 @@ class OpenAiGen {
715
1297
  }
716
1298
  break;
717
1299
  }
1300
+ case 'response.audio.delta':
1301
+ case 'response.audio.done':
1302
+ break;
1303
+ case 'response.audio.transcript.delta': {
1304
+ currentAudioTranscript += await requireEventString(event.delta, event.type, 'delta');
1305
+ break;
1306
+ }
1307
+ case 'response.audio.transcript.done':
1308
+ break;
718
1309
  case 'response.output_item.done': {
719
1310
  const item = event.item;
720
1311
  if (isRecord(item) && item.type === 'function_call') {
@@ -747,6 +1338,37 @@ class OpenAiGen {
747
1338
  }
748
1339
  break;
749
1340
  }
1341
+ if (isRecord(item) && item.type === 'web_search_call' && receiver.webSearchCall) {
1342
+ const itemId = typeof item.id === 'string' ? item.id.trim() : '';
1343
+ if (itemId.length === 0) {
1344
+ await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing itemId');
1345
+ }
1346
+ const call = toOpenAiLlmWebSearchCall(item, itemId, 'done');
1347
+ activeWebSearchCallsByItemId.set(itemId, {
1348
+ itemId,
1349
+ action: call.action,
1350
+ });
1351
+ await receiver.webSearchCall(call);
1352
+ break;
1353
+ }
1354
+ if (isRecord(item) &&
1355
+ typeof item.type === 'string' &&
1356
+ OPENAI_NATIVE_TOOL_ITEM_TYPES.has(item.type)) {
1357
+ const itemId = typeof item.id === 'string' ? item.id.trim() : '';
1358
+ if (item.type !== 'custom_tool_call' && itemId.length === 0) {
1359
+ await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing itemId');
1360
+ }
1361
+ if (item.type === 'custom_tool_call' &&
1362
+ !(typeof item.call_id === 'string' && item.call_id.trim().length > 0)) {
1363
+ await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing call_id');
1364
+ }
1365
+ const seed = buildOpenAiNativeToolSeed(item, itemId.length > 0 ? itemId : undefined);
1366
+ if (seed) {
1367
+ const state = nativeToolTracker.upsert(seed);
1368
+ await emitNativeToolCall(state, 'done');
1369
+ }
1370
+ break;
1371
+ }
750
1372
  if (isRecord(item) && item.type === 'message' && !sawOutputText) {
751
1373
  const text = extractOutputMessageText(item);
752
1374
  if (text.length > 0) {
@@ -841,14 +1463,182 @@ class OpenAiGen {
841
1463
  activeFuncCallsByItemId.set(itemId, state);
842
1464
  }
843
1465
  }
1466
+ if (isRecord(item) && item.type === 'web_search_call' && receiver.webSearchCall) {
1467
+ const itemId = typeof item.id === 'string' ? item.id.trim() : '';
1468
+ if (itemId.length === 0) {
1469
+ await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing itemId');
1470
+ }
1471
+ const call = toOpenAiLlmWebSearchCall(item, itemId, 'added');
1472
+ activeWebSearchCallsByItemId.set(itemId, {
1473
+ itemId,
1474
+ action: call.action,
1475
+ });
1476
+ await receiver.webSearchCall(call);
1477
+ }
1478
+ if (isRecord(item) &&
1479
+ typeof item.type === 'string' &&
1480
+ OPENAI_NATIVE_TOOL_ITEM_TYPES.has(item.type)) {
1481
+ const itemId = typeof item.id === 'string' ? item.id.trim() : '';
1482
+ if (item.type !== 'custom_tool_call' && itemId.length === 0) {
1483
+ await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing itemId');
1484
+ }
1485
+ if (item.type === 'custom_tool_call' &&
1486
+ !(typeof item.call_id === 'string' && item.call_id.trim().length > 0)) {
1487
+ await throwOpenAiMalformedStreamEvent(receiver, item.type, 'missing call_id');
1488
+ }
1489
+ const seed = buildOpenAiNativeToolSeed(item, itemId.length > 0 ? itemId : undefined);
1490
+ if (seed) {
1491
+ const state = nativeToolTracker.upsert(seed);
1492
+ await emitNativeToolCall(state, 'added');
1493
+ }
1494
+ }
1495
+ break;
1496
+ }
1497
+ case 'response.web_search_call.in_progress':
1498
+ case 'response.web_search_call.searching':
1499
+ case 'response.web_search_call.completed': {
1500
+ if (!receiver.webSearchCall) {
1501
+ break;
1502
+ }
1503
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1504
+ const existing = activeWebSearchCallsByItemId.get(itemId);
1505
+ const status = event.type === 'response.web_search_call.in_progress'
1506
+ ? 'in_progress'
1507
+ : event.type === 'response.web_search_call.searching'
1508
+ ? 'searching'
1509
+ : 'completed';
1510
+ const phase = event.type === 'response.web_search_call.completed' ? 'done' : 'added';
1511
+ await receiver.webSearchCall(buildOpenAiWebSearchProgressCall(itemId, phase, status, existing?.action));
1512
+ break;
1513
+ }
1514
+ case 'response.file_search_call.in_progress':
1515
+ case 'response.file_search_call.searching':
1516
+ case 'response.file_search_call.completed': {
1517
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1518
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'file_search_call', 'File Search');
1519
+ existing.status =
1520
+ event.type === 'response.file_search_call.in_progress'
1521
+ ? 'in_progress'
1522
+ : event.type === 'response.file_search_call.searching'
1523
+ ? 'searching'
1524
+ : 'completed';
1525
+ await emitNativeToolCall(existing, event.type === 'response.file_search_call.completed' ? 'done' : 'added');
1526
+ break;
1527
+ }
1528
+ case 'response.code_interpreter_call.in_progress':
1529
+ case 'response.code_interpreter_call.interpreting':
1530
+ case 'response.code_interpreter_call.completed': {
1531
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1532
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'code_interpreter_call', 'Code Interpreter');
1533
+ existing.status =
1534
+ event.type === 'response.code_interpreter_call.in_progress'
1535
+ ? 'in_progress'
1536
+ : event.type === 'response.code_interpreter_call.interpreting'
1537
+ ? 'interpreting'
1538
+ : 'completed';
1539
+ await emitNativeToolCall(existing, event.type === 'response.code_interpreter_call.completed' ? 'done' : 'added');
1540
+ break;
1541
+ }
1542
+ case 'response.code_interpreter_call_code.delta': {
1543
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1544
+ const delta = await requireEventString(event.delta, event.type, 'delta');
1545
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'code_interpreter_call', 'Code Interpreter');
1546
+ existing.summary = `${existing.summary ?? ''}${delta}`;
1547
+ break;
1548
+ }
1549
+ case 'response.code_interpreter_call_code.done': {
1550
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1551
+ const code = await requireEventString(event.code, event.type, 'code');
1552
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'code_interpreter_call', 'Code Interpreter');
1553
+ existing.summary = code;
1554
+ await emitNativeToolCall(existing, 'added');
1555
+ break;
1556
+ }
1557
+ case 'response.image_generation_call.in_progress':
1558
+ case 'response.image_generation_call.generating':
1559
+ case 'response.image_generation_call.completed': {
1560
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1561
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'image_generation_call', 'Image Generation');
1562
+ existing.status =
1563
+ event.type === 'response.image_generation_call.in_progress'
1564
+ ? 'in_progress'
1565
+ : event.type === 'response.image_generation_call.generating'
1566
+ ? 'generating'
1567
+ : 'completed';
1568
+ await emitNativeToolCall(existing, event.type === 'response.image_generation_call.completed' ? 'done' : 'added');
1569
+ break;
1570
+ }
1571
+ case 'response.image_generation_call.partial_image': {
1572
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1573
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'image_generation_call', 'Image Generation');
1574
+ const currentIndex = typeof event.partial_image_index === 'number' ? event.partial_image_index + 1 : 1;
1575
+ existing.detail = `partial_images=${String(currentIndex)}`;
1576
+ break;
1577
+ }
1578
+ case 'response.mcp_call.in_progress':
1579
+ case 'response.mcp_call.failed':
1580
+ case 'response.mcp_call.completed': {
1581
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1582
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'mcp_call', 'MCP Tool Call');
1583
+ existing.status =
1584
+ event.type === 'response.mcp_call.in_progress'
1585
+ ? 'in_progress'
1586
+ : event.type === 'response.mcp_call.failed'
1587
+ ? 'failed'
1588
+ : 'completed';
1589
+ await emitNativeToolCall(existing, event.type === 'response.mcp_call.completed' ? 'done' : 'added');
1590
+ break;
1591
+ }
1592
+ case 'response.mcp_call_arguments.delta': {
1593
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1594
+ const delta = await requireEventString(event.delta, event.type, 'delta');
1595
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'mcp_call', 'MCP Tool Call');
1596
+ existing.detail = `${existing.detail ?? ''}${delta}`;
1597
+ break;
1598
+ }
1599
+ case 'response.mcp_call_arguments.done': {
1600
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1601
+ const args = await requireEventString(event.arguments, event.type, 'arguments');
1602
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'mcp_call', 'MCP Tool Call');
1603
+ existing.detail = args;
1604
+ await emitNativeToolCall(existing, 'added');
1605
+ break;
1606
+ }
1607
+ case 'response.mcp_list_tools.in_progress':
1608
+ case 'response.mcp_list_tools.failed':
1609
+ case 'response.mcp_list_tools.completed': {
1610
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1611
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'mcp_list_tools', 'MCP Tool Discovery');
1612
+ existing.status =
1613
+ event.type === 'response.mcp_list_tools.in_progress'
1614
+ ? 'in_progress'
1615
+ : event.type === 'response.mcp_list_tools.failed'
1616
+ ? 'failed'
1617
+ : 'completed';
1618
+ await emitNativeToolCall(existing, event.type === 'response.mcp_list_tools.completed' ? 'done' : 'added');
1619
+ break;
1620
+ }
1621
+ case 'response.custom_tool_call_input.delta': {
1622
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1623
+ const delta = await requireEventString(event.delta, event.type, 'delta');
1624
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'custom_tool_call', 'Custom Tool Call');
1625
+ existing.detail = `${existing.detail ?? ''}${delta}`;
1626
+ break;
1627
+ }
1628
+ case 'response.custom_tool_call_input.done': {
1629
+ const itemId = await requireNonEmptyEventItemId(event.item_id, event.type);
1630
+ const input = await requireEventString(event.input, event.type, 'input');
1631
+ const existing = await claimNativeToolStateByEventItemId(itemId, 'custom_tool_call', 'Custom Tool Call');
1632
+ existing.detail = input;
1633
+ await emitNativeToolCall(existing, 'added');
844
1634
  break;
845
1635
  }
846
1636
  case 'response.content_part.added':
847
1637
  case 'response.content_part.done':
848
1638
  break;
849
1639
  case 'response.function_call_arguments.delta': {
850
- const itemId = event.item_id;
851
- const delta = event.delta;
1640
+ const itemId = await requireEventString(event.item_id, event.type, 'item_id');
1641
+ const delta = await requireEventString(event.delta, event.type, 'delta');
852
1642
  if (itemId.length > 0 && delta.length > 0) {
853
1643
  const existing = activeFuncCallsByItemId.get(itemId);
854
1644
  const state = existing ??
@@ -865,9 +1655,9 @@ class OpenAiGen {
865
1655
  break;
866
1656
  }
867
1657
  case 'response.function_call_arguments.done': {
868
- const itemId = event.item_id;
869
- const name = event.name;
870
- const args = event.arguments;
1658
+ const itemId = await requireEventString(event.item_id, event.type, 'item_id');
1659
+ const name = readOptionalEventString(event.name);
1660
+ const args = readOptionalEventString(event.arguments);
871
1661
  if (itemId.length > 0) {
872
1662
  const existing = activeFuncCallsByItemId.get(itemId);
873
1663
  const state = existing ??
@@ -906,10 +1696,27 @@ class OpenAiGen {
906
1696
  }
907
1697
  }
908
1698
  }
1699
+ for (const state of activeFuncCallsByItemId.values()) {
1700
+ await maybeEmitFuncCall(state, receiver);
1701
+ }
1702
+ const unresolvedFuncCalls = Array.from(activeFuncCallsByItemId.values()).filter((state) => !state.emitted &&
1703
+ (state.callId.length > 0 || state.name.length > 0 || state.argsJson.length > 0));
1704
+ if (unresolvedFuncCalls.length > 0) {
1705
+ const detail = 'OPENAI incomplete function-call stream state: ' +
1706
+ unresolvedFuncCalls
1707
+ .map((state) => `itemId=${state.itemId},callId=${state.callId || '<missing>'},name=${state.name || '<missing>'}`)
1708
+ .join('; ');
1709
+ log.error(detail, new Error('openai_incomplete_function_call_stream_state'));
1710
+ if (receiver.streamError) {
1711
+ await receiver.streamError(detail);
1712
+ }
1713
+ throw new Error(detail);
1714
+ }
909
1715
  }
910
1716
  catch (error) {
911
- log.warn('OPENAI streaming error', error);
912
- throw error;
1717
+ const annotatedError = maybeAnnotateOpenAiQuirkFailure(providerConfig, error);
1718
+ log.warn('OPENAI streaming error', annotatedError);
1719
+ throw annotatedError;
913
1720
  }
914
1721
  finally {
915
1722
  if (thinkingStarted) {
@@ -932,13 +1739,15 @@ class OpenAiGen {
932
1739
  const requestInput = await buildOpenAiRequestInput(context, providerConfig);
933
1740
  const openAiParams = agent.model_params?.openai || {};
934
1741
  const maxTokens = agent.model_params?.max_tokens;
935
- const jsonResponseEnabled = resolveOpenAiJsonResponseEnabled(agent, openAiParams);
936
1742
  const modelInfo = providerConfig.models[agent.model];
937
1743
  const outputLength = modelInfo?.output_length;
938
1744
  const maxOutputTokens = maxTokens ?? openAiParams.max_tokens ?? outputLength ?? 1024;
939
1745
  const parallelToolCalls = openAiParams.parallel_tool_calls ?? true;
940
- const textConfig = buildOpenAiTextConfig(openAiParams, jsonResponseEnabled);
1746
+ const textConfig = buildOpenAiTextConfig(openAiParams);
941
1747
  const reasoning = buildOpenAiReasoning(openAiParams);
1748
+ const nativeTools = buildOpenAiNativeTools(openAiParams);
1749
+ const include = buildOpenAiInclude(requestInput, reasoning, openAiParams);
1750
+ const tools = [...funcTools.map(funcToolToOpenAiTool), ...nativeTools];
942
1751
  const payload = {
943
1752
  model: agent.model,
944
1753
  input: requestInput,
@@ -948,12 +1757,16 @@ class OpenAiGen {
948
1757
  store: false,
949
1758
  stream: false,
950
1759
  ...(openAiParams.service_tier !== undefined && { service_tier: openAiParams.service_tier }),
1760
+ ...(openAiParams.safety_identifier !== undefined && {
1761
+ safety_identifier: openAiParams.safety_identifier,
1762
+ }),
951
1763
  ...(openAiParams.temperature !== undefined && { temperature: openAiParams.temperature }),
952
1764
  ...(openAiParams.top_p !== undefined && { top_p: openAiParams.top_p }),
953
1765
  ...(reasoning !== undefined && { reasoning }),
1766
+ ...(include !== undefined ? { include } : {}),
954
1767
  ...(textConfig !== undefined && { text: textConfig }),
955
- ...(funcTools.length > 0
956
- ? { tools: funcTools.map(funcToolToOpenAiTool), tool_choice: 'auto' }
1768
+ ...(tools.length > 0
1769
+ ? { tools, tool_choice: 'auto' }
957
1770
  : { tool_choice: 'none' }),
958
1771
  };
959
1772
  const response = await client.responses.create(payload, {
@@ -964,8 +1777,12 @@ class OpenAiGen {
964
1777
  }
965
1778
  const returnedModel = typeof response.model === 'string' ? response.model : undefined;
966
1779
  const usage = parseOpenAiUsage(response.usage);
1780
+ const outputs = openAiResponseToBatchOutputs(response, genseq);
967
1781
  return {
968
- messages: openAiResponseToChatMessages(response, genseq),
1782
+ messages: outputs
1783
+ .filter((entry) => entry.kind === 'message')
1784
+ .map((entry) => entry.message),
1785
+ outputs,
969
1786
  usage,
970
1787
  llmGenModel: returnedModel,
971
1788
  };