@qwen-code/qwen-code 0.20.0 → 0.20.1-preview.7215

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 (169) hide show
  1. package/chunks/{MaxSizedBox-B5YGZSYE.js → MaxSizedBox-EH6G5AEH.js} +15 -12
  2. package/chunks/{StandaloneSessionPicker-CEGO2AZX.js → StandaloneSessionPicker-264A3TEA.js} +33 -30
  3. package/chunks/{acpAgent-YUR43TLG.js → acpAgent-6RBIJFDI.js} +79 -71
  4. package/chunks/{agent-H4JIDL7S.js → agent-QURXGAA3.js} +15 -12
  5. package/chunks/{agent-headless-TTBEREQV.js → agent-headless-CRGX4ICQ.js} +15 -12
  6. package/chunks/{anthropicContentGenerator-HVKLXOOU.js → anthropicContentGenerator-BWBAZGTW.js} +3 -2
  7. package/chunks/{artifact-tool-WBCAMD3O.js → artifact-tool-IUJ7QWCX.js} +1 -1
  8. package/chunks/{askUserQuestion-6DZC4UV5.js → askUserQuestion-IUMUUTDQ.js} +1 -1
  9. package/chunks/{bridge-VL7ITBU5.js → bridge-MNFVKPMI.js} +18 -15
  10. package/chunks/{chunk-FLSVD6BS.js → chunk-26LLB6FM.js} +1 -1
  11. package/chunks/{chunk-TKLLK6XQ.js → chunk-2DXOIVUI.js} +1 -1
  12. package/chunks/{chunk-HZPN55E5.js → chunk-3EUZWNWA.js} +1 -1
  13. package/chunks/{chunk-EZVDJFZ7.js → chunk-467G3SV5.js} +1 -1
  14. package/chunks/{chunk-4XNLIGVA.js → chunk-4SLLEAQP.js} +43 -26
  15. package/chunks/{chunk-O3OCNYY5.js → chunk-4WUAQ2DO.js} +1 -1
  16. package/chunks/{chunk-4ILMYHEI.js → chunk-5NWV4NFB.js} +3 -3
  17. package/chunks/{chunk-CV63PP6U.js → chunk-6GUTHAN7.js} +1 -1
  18. package/chunks/{chunk-KX6UK4VJ.js → chunk-6OH6A5EX.js} +117 -165
  19. package/chunks/{chunk-NDMQLV4Y.js → chunk-7KDI2IB2.js} +3 -3
  20. package/chunks/{chunk-6AFUCL3D.js → chunk-ANHT55MR.js} +1 -1
  21. package/chunks/{chunk-OYZWQNWG.js → chunk-AR27OPIU.js} +1 -1
  22. package/chunks/{chunk-IR46U7RB.js → chunk-B453NMZJ.js} +1 -1
  23. package/chunks/{chunk-4EP2PYYL.js → chunk-BAIEQKR5.js} +3 -3
  24. package/chunks/{chunk-AJSJ5TTU.js → chunk-BF32GYSY.js} +1 -1
  25. package/chunks/{chunk-O3Z73NBP.js → chunk-BRXIO5WY.js} +4 -4
  26. package/chunks/{chunk-D22SX43D.js → chunk-CVDW42JT.js} +1 -1
  27. package/chunks/{chunk-KG3XV6FH.js → chunk-DA7OOKVN.js} +1 -1
  28. package/chunks/{chunk-OEO2ERWK.js → chunk-E5GIFWE3.js} +3 -3
  29. package/chunks/{chunk-CPCVCKKF.js → chunk-ETD6K42I.js} +1 -1
  30. package/chunks/{chunk-AKX4WPPP.js → chunk-EVSTCA7S.js} +4 -4
  31. package/chunks/{chunk-DP5GXXNZ.js → chunk-FLTN6NRU.js} +1 -1
  32. package/chunks/{chunk-XVVH63JG.js → chunk-FNGK2Z5B.js} +2 -2
  33. package/chunks/{chunk-FEOZBUMT.js → chunk-FODK6UMO.js} +0 -27
  34. package/chunks/{chunk-PGEPSDJI.js → chunk-FVS747N7.js} +7 -7
  35. package/chunks/{chunk-333CXMNJ.js → chunk-GA7VRJVN.js} +4 -4
  36. package/chunks/chunk-GLNBDYUX.js +38 -0
  37. package/chunks/{chunk-SEKHYHMS.js → chunk-I5NLVPZJ.js} +1 -1
  38. package/chunks/{chunk-S2PD3EZD.js → chunk-IB35C3IL.js} +1 -1
  39. package/chunks/{chunk-DBE4QFZS.js → chunk-IE4JSQ5L.js} +10 -10
  40. package/chunks/{chunk-ANPINBOF.js → chunk-IKP5DXZY.js} +3 -3
  41. package/chunks/{chunk-OWCALFEG.js → chunk-IUSNI6IX.js} +1 -1
  42. package/chunks/{chunk-EAUYY7SS.js → chunk-JDI67JFB.js} +1 -1
  43. package/chunks/{chunk-MJ6S23WZ.js → chunk-JVTCARQ6.js} +4 -4
  44. package/chunks/{chunk-33XUAINQ.js → chunk-KPZNOZPC.js} +46 -6
  45. package/chunks/{chunk-CB2HFGRV.js → chunk-KXEGCGUG.js} +4 -4
  46. package/chunks/{chunk-S2W2LVDU.js → chunk-LZYWWWCZ.js} +2 -2
  47. package/chunks/{chunk-I2PYAYFM.js → chunk-MC2DOZS7.js} +1 -1
  48. package/chunks/{chunk-VZQUV7VK.js → chunk-MKLKHZAK.js} +1 -1
  49. package/chunks/{chunk-KOR3XKCA.js → chunk-N6YTEVLV.js} +3 -3
  50. package/chunks/{chunk-A5LYNIW7.js → chunk-NOWTNYC7.js} +4 -4
  51. package/chunks/{chunk-TIH2ZTOC.js → chunk-OF2BBLYQ.js} +1 -1
  52. package/chunks/chunk-OKWRU4NO.js +391 -0
  53. package/chunks/{chunk-ZFKNWQGQ.js → chunk-ONWW74X6.js} +7 -7
  54. package/chunks/{chunk-VEXFOTEO.js → chunk-P2YAOGJD.js} +2 -2
  55. package/chunks/chunk-PWVTCGBP.js +1768 -0
  56. package/chunks/{chunk-4N3KMAIL.js → chunk-Q3ZBFXK4.js} +13 -13
  57. package/chunks/{chunk-PF4CHQOJ.js → chunk-Q4RBU7WX.js} +4 -4
  58. package/chunks/{chunk-KDC6GNQR.js → chunk-QOK6RMIP.js} +4 -4
  59. package/chunks/{chunk-542SK7KV.js → chunk-RELYEO3R.js} +4 -4
  60. package/chunks/{chunk-OP4MOGXF.js → chunk-S35ZDXOS.js} +6 -0
  61. package/chunks/{chunk-KQNM4EO7.js → chunk-SDXIH6OA.js} +1 -1
  62. package/chunks/{chunk-7RMOXZTC.js → chunk-T7DDD7CZ.js} +4 -4
  63. package/chunks/{chunk-NGFEPAE6.js → chunk-TCKTNY66.js} +6 -4
  64. package/chunks/{chunk-5JBR5XIK.js → chunk-TTSKHF43.js} +2 -2
  65. package/chunks/{chunk-AQIEINAY.js → chunk-UJEZQ562.js} +25 -1763
  66. package/chunks/{chunk-BRRF37CO.js → chunk-UQES76OO.js} +1 -1
  67. package/chunks/{chunk-7SXZC7ID.js → chunk-V25Z5UUB.js} +4 -4
  68. package/chunks/{chunk-PBWEWI4N.js → chunk-VFAE4AGH.js} +29 -27
  69. package/chunks/{chunk-S2XVRHDN.js → chunk-WAX3ITOA.js} +1 -1
  70. package/chunks/chunk-WBTZGWQ6.js +121 -0
  71. package/chunks/{chunk-SJMV7ZBK.js → chunk-WKRXIHD6.js} +6 -6
  72. package/chunks/{chunk-2LL7TXB4.js → chunk-WOT64EP2.js} +2 -2
  73. package/chunks/{chunk-VDWBQ5AW.js → chunk-WXAXH7IJ.js} +1 -1
  74. package/chunks/{chunk-FQD7GFSV.js → chunk-X4CGDBKA.js} +3 -3
  75. package/chunks/{chunk-BMWMT5Y2.js → chunk-XNP57UDV.js} +2 -372
  76. package/chunks/{chunk-7PZ2ATFZ.js → chunk-Y4CRUWFZ.js} +13 -13
  77. package/chunks/{chunk-CZF3E5GF.js → chunk-Z5Q65RVH.js} +1 -1
  78. package/chunks/{chunk-IVPSS6EL.js → chunk-Z5R4JMOL.js} +3 -3
  79. package/chunks/{chunk-TMQ6XQCX.js → chunk-Z6UEAQTP.js} +36 -36
  80. package/chunks/{computer-use-NIWQNUZF.js → computer-use-R7KA6UJ6.js} +15 -12
  81. package/chunks/{config-utils-AHQ6YZ66.js → config-utils-6FXFJUYB.js} +1 -1
  82. package/chunks/{contextCommand-L2COEUHS.js → contextCommand-KAQQPHZ4.js} +17 -14
  83. package/chunks/{create-sub-session-MGNX52P3.js → create-sub-session-TDPDBU4K.js} +15 -12
  84. package/chunks/{create-sub-session-P26FE5GG.js → create-sub-session-X5RVIHCK.js} +1 -1
  85. package/chunks/{cron-create-JWFHBDRQ.js → cron-create-AC5FFLN7.js} +1 -1
  86. package/chunks/{cron-delete-GDX4E26G.js → cron-delete-JEB3FAHA.js} +1 -1
  87. package/chunks/{cron-list-USMAIWPT.js → cron-list-NBYPCNIB.js} +1 -1
  88. package/chunks/{daemon-status-provider-NL4IAG7B.js → daemon-status-provider-LVTVJGAD.js} +22 -19
  89. package/chunks/{earlyInputCapture-L6I2A22T.js → earlyInputCapture-WC7RI5HV.js} +15 -12
  90. package/chunks/{edit-3QV3JZJL.js → edit-6MHKCJCM.js} +16 -13
  91. package/chunks/{enter-worktree-4GMMF7EG.js → enter-worktree-O7VJ2E7A.js} +15 -12
  92. package/chunks/{enterPlanMode-L5UWBAUC.js → enterPlanMode-5XYAALIO.js} +15 -12
  93. package/chunks/{environment-3OV7UQOT.js → environment-LSKFNLBU.js} +17 -14
  94. package/chunks/{errors-LEOF5QG4.js → errors-ZFVCY6HG.js} +17 -14
  95. package/chunks/{exit-worktree-DDP57JLV.js → exit-worktree-MYIK66T7.js} +15 -12
  96. package/chunks/{exitPlanMode-CJBLGPIP.js → exitPlanMode-7MBNU76O.js} +15 -12
  97. package/chunks/{fast-path-M6RP2ING.js → fast-path-RNLIB64C.js} +2 -2
  98. package/chunks/{gemini-H4PHBJTL.js → gemini-WK5ISEAQ.js} +56 -53
  99. package/chunks/{geminiContentGenerator-VURJYPWP.js → geminiContentGenerator-NUJQBZBW.js} +1 -1
  100. package/chunks/{glob-2KUSRAVQ.js → glob-7P6RYY4L.js} +15 -12
  101. package/chunks/{grep-B5RXVESV.js → grep-V3QHNP2J.js} +15 -12
  102. package/chunks/{handleAutoUpdate-QQVIWXKT.js → handleAutoUpdate-AAQ6PL3E.js} +19 -16
  103. package/chunks/{i18n-N6OBDORT.js → i18n-B7IO7WQT.js} +16 -13
  104. package/chunks/{initializer-6WCFNDLL.js → initializer-E5TIC3CW.js} +20 -17
  105. package/chunks/{installationInfo-6J3WPXNS.js → installationInfo-XLPNAISK.js} +16 -13
  106. package/chunks/{list-HOWQSWP6.js → list-3CZR5I42.js} +23 -20
  107. package/chunks/{loadedSettingsAdapter-AO4YBRXZ.js → loadedSettingsAdapter-X5BSC6CE.js} +20 -17
  108. package/chunks/{loop-wakeup-5GLTUBHA.js → loop-wakeup-HMHCT5GU.js} +2 -2
  109. package/chunks/{ls-FHBY3Y7I.js → ls-IW5HYDG4.js} +1 -1
  110. package/chunks/{lsp-B6VMEW6Y.js → lsp-4RL52YIZ.js} +1 -1
  111. package/chunks/{mcp-QH7QNXOH.js → mcp-4C4HWXAR.js} +20 -17
  112. package/chunks/{monitor-GLJZDNPS.js → monitor-3CJDNZB2.js} +15 -12
  113. package/chunks/{nonInteractiveCli-TI7EMPXD.js → nonInteractiveCli-QTEHDDFH.js} +47 -44
  114. package/chunks/{notebook-edit-AVCIWS4F.js → notebook-edit-MX366HCT.js} +16 -13
  115. package/chunks/{openaiContentGenerator-B7W24G3D.js → openaiContentGenerator-KRPNNJGZ.js} +10 -7
  116. package/chunks/{pidfile-UEHAT6DH.js → pidfile-K7T2JLUJ.js} +15 -12
  117. package/chunks/{processUtils-XZPY2PBB.js → processUtils-YXL3VIBR.js} +2 -2
  118. package/chunks/{qwenContentGenerator-6GUTWJIC.js → qwenContentGenerator-GJVXFZ3U.js} +20 -15
  119. package/chunks/{qwenOAuth2-GASEEWGU.js → qwenOAuth2-UZTIBQLY.js} +6 -5
  120. package/chunks/{read-file-SQJONF6A.js → read-file-S5XQSYOW.js} +3 -3
  121. package/chunks/{read-mcp-resource-AKCL7I43.js → read-mcp-resource-OIBCNIX7.js} +1 -1
  122. package/chunks/{record-artifact-PEUB7727.js → record-artifact-HDMK4LHG.js} +1 -1
  123. package/chunks/{resumeHistoryUtils-PFQ2A23X.js → resumeHistoryUtils-IKR6XD2S.js} +19 -16
  124. package/chunks/{ripGrep-QCK22GDU.js → ripGrep-H65GRSQX.js} +15 -12
  125. package/chunks/{run-qwen-serve-OZGR3FQA.js → run-qwen-serve-7SKAQOAA.js} +24 -24
  126. package/chunks/{runtime-SNVAP6EH.js → runtime-I2STBGPR.js} +22 -19
  127. package/chunks/{scheduler-Z7QLMUWA.js → scheduler-6F7HETNO.js} +15 -12
  128. package/chunks/{send-message-3RHE7HSM.js → send-message-WXVVK4DB.js} +2 -2
  129. package/chunks/{serve-BT7KS6SC.js → serve-4ATEWSEA.js} +20 -17
  130. package/chunks/{server-SO64U3X5.js → server-YPUPFV2O.js} +70 -67
  131. package/chunks/{session-2KL2WQAH.js → session-AZFIERIY.js} +48 -45
  132. package/chunks/{settings-KITVOUSV.js → settings-EFQHLTM3.js} +19 -16
  133. package/chunks/{shell-BITUVH2R.js → shell-FWXWBJ4Q.js} +15 -12
  134. package/chunks/{skill-SOQHY455.js → skill-A634MNCD.js} +3 -3
  135. package/chunks/{spawnChannel-2BU3AULL.js → spawnChannel-KKBGGR47.js} +17 -14
  136. package/chunks/{src-43LM7JLB.js → src-BVROLD6N.js} +50 -46
  137. package/chunks/{standalone-update-YG5JUYCM.js → standalone-update-ERLA3BDC.js} +17 -14
  138. package/chunks/{startInteractiveUI-3RGTGEX5.js → startInteractiveUI-3VZ23WAY.js} +144 -140
  139. package/chunks/{syntheticOutput-FG5PQKIR.js → syntheticOutput-HCUFXSIA.js} +2 -2
  140. package/chunks/{task-create-HX32YXO7.js → task-create-RIY5JTFC.js} +3 -3
  141. package/chunks/{task-list-QWSY4ZIU.js → task-list-XBUCV3CO.js} +1 -1
  142. package/chunks/{task-stop-GWC6UWAL.js → task-stop-QDFCCYR5.js} +1 -1
  143. package/chunks/{task-update-NCTAB73Q.js → task-update-7PXKJQOI.js} +3 -3
  144. package/chunks/{team-create-FXN3PEBP.js → team-create-ZZO7QFQ7.js} +18 -15
  145. package/chunks/{team-delete-KK37HTOG.js → team-delete-TNSSE2S5.js} +1 -1
  146. package/chunks/{team-plan-approval-GEIZ356Q.js → team-plan-approval-3BZBJ46K.js} +15 -12
  147. package/chunks/{theme-manager-IAETOI25.js → theme-manager-FSFWNXFJ.js} +15 -12
  148. package/chunks/{todoWrite-ERCAMLFS.js → todoWrite-OMAIGB6V.js} +1 -1
  149. package/chunks/{tool-search-XKK7DNOK.js → tool-search-VYXAAIDW.js} +4 -4
  150. package/chunks/{total-session-admission-BJLDHKJN.js → total-session-admission-PSMLQICS.js} +18 -15
  151. package/chunks/{trustedFolders-5XCVNEUU.js → trustedFolders-QA3RK2NV.js} +16 -13
  152. package/chunks/{update-relaunch-FMVZK6XF.js → update-relaunch-LNLZFX3J.js} +5 -5
  153. package/chunks/{updateCheck-YZZB7YBU.js → updateCheck-JJEUQ54U.js} +18 -15
  154. package/chunks/{useAutoAcceptIndicator-ZSQX4I2H.js → useAutoAcceptIndicator-DIWN27LT.js} +23 -20
  155. package/chunks/{validateNonInterActiveAuth-PRVEB6GG.js → validateNonInterActiveAuth-LOEOFCAF.js} +44 -41
  156. package/chunks/{version-AZMG6KWL.js → version-HBF2VBLE.js} +1 -1
  157. package/chunks/{web-fetch-EPGJ5OQQ.js → web-fetch-BWGSD2BD.js} +5 -4
  158. package/chunks/web-search-OOHTCVR3.js +680 -0
  159. package/chunks/{workflow-UTNVPW22.js → workflow-LJAJBOR4.js} +16 -13
  160. package/chunks/{workspace-providers-status-EADV364W.js → workspace-providers-status-FYV7TTHA.js} +23 -20
  161. package/chunks/{workspace-registration-store-F3ZX3YHT.js → workspace-registration-store-4EHGQW4Y.js} +1 -1
  162. package/chunks/{workspace-registry-W6VMFYZU.js → workspace-registry-BKD7VC4Y.js} +18 -15
  163. package/chunks/{workspace-service-F3KIN2UN.js → workspace-service-75RHX6KR.js} +25 -22
  164. package/chunks/{workspace-skills-status-YVICUR5Y.js → workspace-skills-status-WBZBZI6A.js} +21 -18
  165. package/chunks/{write-file-CVGF2OKV.js → write-file-M2DTFILF.js} +16 -13
  166. package/cli.js +11 -11
  167. package/package.json +3 -3
  168. package/chunks/{chunk-B3KGZCQN.js → chunk-7YBZGNEC.js} +3 -3
  169. /package/chunks/{chunk-YWEW33JY.js → chunk-OMH3276Y.js} +0 -0
@@ -0,0 +1,1768 @@
1
+ // Force strict mode and setup for ESM
2
+ "use strict";
3
+ import {
4
+ DashScopeOpenAICompatibleProvider,
5
+ DefaultOpenAICompatibleProvider
6
+ } from "./chunk-UJEZQ562.js";
7
+ import {
8
+ InvalidStreamError,
9
+ OpenAIContentConverter,
10
+ ProtocolTagSanitizedEvent,
11
+ TaggedThinkingParser,
12
+ getRateLimitErrorDetails,
13
+ logProtocolTagSanitized,
14
+ openaiRequestCaptureContext
15
+ } from "./chunk-ONWW74X6.js";
16
+ import {
17
+ RequestTokenizer
18
+ } from "./chunk-26LLB6FM.js";
19
+ import {
20
+ DEFAULT_STREAM_IDLE_TIMEOUT_MS,
21
+ MAX_STREAM_IDLE_TIMEOUT_MS,
22
+ QWEN_STREAM_IDLE_TIMEOUT_MS_ENV
23
+ } from "./chunk-GLNBDYUX.js";
24
+ import {
25
+ createChildAbortController
26
+ } from "./chunk-DJ2GSRLV.js";
27
+ import {
28
+ redactProxyError
29
+ } from "./chunk-6EFTQYW7.js";
30
+ import {
31
+ getToolCallPreparations,
32
+ reconcileMaxTokens,
33
+ runtimeDiagnostics,
34
+ safeJsonParse
35
+ } from "./chunk-MAQQGNQ5.js";
36
+ import {
37
+ GenerateContentResponse
38
+ } from "./chunk-TYQMGACW.js";
39
+ import {
40
+ createDebugLogger,
41
+ getErrorMessage,
42
+ getErrorStatus,
43
+ getErrorType,
44
+ isAbortError
45
+ } from "./chunk-C27BYUX6.js";
46
+ import {
47
+ init_esbuild_shims
48
+ } from "./chunk-5O2XNYP6.js";
49
+ import {
50
+ __name
51
+ } from "./chunk-J2S4EL5Y.js";
52
+
53
+ // packages/core/src/core/openaiContentGenerator/index.ts
54
+ init_esbuild_shims();
55
+
56
+ // packages/core/src/core/openaiContentGenerator/openaiContentGenerator.ts
57
+ init_esbuild_shims();
58
+
59
+ // packages/core/src/core/openaiContentGenerator/provider/index.ts
60
+ init_esbuild_shims();
61
+
62
+ // packages/core/src/core/openaiContentGenerator/provider/modelscope.ts
63
+ init_esbuild_shims();
64
+ var ModelScopeOpenAICompatibleProvider = class extends DefaultOpenAICompatibleProvider {
65
+ static {
66
+ __name(this, "ModelScopeOpenAICompatibleProvider");
67
+ }
68
+ /**
69
+ * Checks if the configuration is for ModelScope.
70
+ */
71
+ static isModelScopeProvider(config) {
72
+ const baseUrl = config.baseUrl ?? "";
73
+ if (!baseUrl) return false;
74
+ try {
75
+ const hostname = new URL(baseUrl).hostname.toLowerCase();
76
+ return hostname === "modelscope.cn" || hostname.endsWith(".modelscope.cn");
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+ /**
82
+ * ModelScope does not support `stream_options` when `stream` is false.
83
+ * This method removes `stream_options` if `stream` is not true.
84
+ */
85
+ buildRequest(request, userPromptId) {
86
+ const newRequest = super.buildRequest(request, userPromptId);
87
+ if (!newRequest.stream) {
88
+ delete newRequest.stream_options;
89
+ }
90
+ return newRequest;
91
+ }
92
+ };
93
+
94
+ // packages/core/src/core/openaiContentGenerator/provider/deepseek.ts
95
+ init_esbuild_shims();
96
+
97
+ // packages/core/src/core/openaiContentGenerator/provider/utils.ts
98
+ init_esbuild_shims();
99
+ function ensureReasoningContentOnAssistantMessage(message) {
100
+ if (message.role !== "assistant") {
101
+ return message;
102
+ }
103
+ const assistant = message;
104
+ if (typeof assistant.reasoning_content === "string") {
105
+ return message;
106
+ }
107
+ return {
108
+ ...assistant,
109
+ reasoning_content: ""
110
+ };
111
+ }
112
+ __name(ensureReasoningContentOnAssistantMessage, "ensureReasoningContentOnAssistantMessage");
113
+
114
+ // packages/core/src/core/openaiContentGenerator/provider/deepseek.ts
115
+ function isDeepSeekHostname(contentGeneratorConfig) {
116
+ const baseUrl = contentGeneratorConfig.baseUrl ?? "";
117
+ if (!baseUrl) return false;
118
+ try {
119
+ const hostname = new URL(baseUrl).hostname.toLowerCase();
120
+ return hostname === "api.deepseek.com" || hostname.endsWith(".api.deepseek.com");
121
+ } catch {
122
+ return false;
123
+ }
124
+ }
125
+ __name(isDeepSeekHostname, "isDeepSeekHostname");
126
+ function isDeepSeekProvider(contentGeneratorConfig) {
127
+ if (isDeepSeekHostname(contentGeneratorConfig)) return true;
128
+ const model = contentGeneratorConfig.model ?? "";
129
+ return model.toLowerCase().includes("deepseek");
130
+ }
131
+ __name(isDeepSeekProvider, "isDeepSeekProvider");
132
+ var DeepSeekOpenAICompatibleProvider = class extends DefaultOpenAICompatibleProvider {
133
+ static {
134
+ __name(this, "DeepSeekOpenAICompatibleProvider");
135
+ }
136
+ constructor(contentGeneratorConfig, cliConfig) {
137
+ super(contentGeneratorConfig, cliConfig);
138
+ }
139
+ /**
140
+ * Backward-compatible static delegates for the free `isDeepSeek*`
141
+ * helpers. New call sites should import the free functions directly to
142
+ * avoid coupling to this class.
143
+ */
144
+ static isDeepSeekProvider = isDeepSeekProvider;
145
+ static isDeepSeekHostname = isDeepSeekHostname;
146
+ /**
147
+ * DeepSeek's API requires message content to be a plain string, not an
148
+ * array of content parts. Flatten any text-part arrays into joined
149
+ * strings; non-text parts (image_url, audio, …) are replaced with a
150
+ * `[Unsupported content type: <type>]` placeholder so the request still
151
+ * goes through with a textual breadcrumb rather than silently dropping
152
+ * the part or raising mid-conversation. Also translate the standard
153
+ * `reasoning.effort` config into DeepSeek's flat `reasoning_effort`
154
+ * body parameter — but only on actual DeepSeek hostnames, since the
155
+ * model-name fallback above can match self-hosted/strict OpenAI-compat
156
+ * backends that don't accept the DeepSeek extension.
157
+ */
158
+ buildRequest(request, userPromptId) {
159
+ const baseRequest = super.buildRequest(request, userPromptId);
160
+ const reshaped = isDeepSeekHostname(this.contentGeneratorConfig) ? translateReasoningEffort(baseRequest) : baseRequest;
161
+ if (!reshaped.messages?.length) {
162
+ return reshaped;
163
+ }
164
+ const messages = reshaped.messages.map((message) => {
165
+ const flattened = flattenContentParts(message);
166
+ return ensureReasoningContentOnAssistantMessage(flattened);
167
+ });
168
+ return {
169
+ ...reshaped,
170
+ messages
171
+ };
172
+ }
173
+ getDefaultGenerationConfig() {
174
+ return {
175
+ temperature: 0
176
+ };
177
+ }
178
+ };
179
+ function flattenContentParts(message) {
180
+ if (!("content" in message)) {
181
+ return message;
182
+ }
183
+ const { content } = message;
184
+ if (typeof content === "string" || content === null || content === void 0) {
185
+ return message;
186
+ }
187
+ if (!Array.isArray(content)) {
188
+ return message;
189
+ }
190
+ const text = content.map((part) => {
191
+ if (typeof part === "string") {
192
+ return part;
193
+ }
194
+ if (part.type === "text") {
195
+ return part.text ?? "";
196
+ }
197
+ return `[Unsupported content type: ${part.type}]`;
198
+ }).join("\n\n");
199
+ return {
200
+ ...message,
201
+ content: text
202
+ };
203
+ }
204
+ __name(flattenContentParts, "flattenContentParts");
205
+ function translateReasoningEffort(request) {
206
+ const r = request;
207
+ const nested = r["reasoning"];
208
+ const nestedEffort = nested?.effort;
209
+ if (typeof nestedEffort !== "string" || !nestedEffort) {
210
+ return request;
211
+ }
212
+ const next = { ...r };
213
+ if (typeof next["reasoning_effort"] !== "string" || !next["reasoning_effort"]) {
214
+ let normalized = nestedEffort;
215
+ if (normalized === "low" || normalized === "medium") normalized = "high";
216
+ else if (normalized === "xhigh") normalized = "max";
217
+ next["reasoning_effort"] = normalized;
218
+ }
219
+ if (nested && Object.keys(nested).length === 1) {
220
+ delete next["reasoning"];
221
+ } else if (nested) {
222
+ const { effort: _drop, ...rest } = nested;
223
+ next["reasoning"] = rest;
224
+ }
225
+ return next;
226
+ }
227
+ __name(translateReasoningEffort, "translateReasoningEffort");
228
+
229
+ // packages/core/src/core/openaiContentGenerator/provider/zai.ts
230
+ init_esbuild_shims();
231
+ var debugLogger = createDebugLogger("ZAI");
232
+ function isZaiHostname(contentGeneratorConfig) {
233
+ const baseUrl = contentGeneratorConfig.baseUrl;
234
+ if (!baseUrl) {
235
+ return false;
236
+ }
237
+ try {
238
+ const hostname = new URL(baseUrl).hostname.toLowerCase();
239
+ return hostname === "z.ai" || hostname.endsWith(".z.ai") || hostname === "bigmodel.cn" || hostname.endsWith(".bigmodel.cn");
240
+ } catch {
241
+ return false;
242
+ }
243
+ }
244
+ __name(isZaiHostname, "isZaiHostname");
245
+ function isZaiProvider(contentGeneratorConfig) {
246
+ if (isZaiHostname(contentGeneratorConfig)) {
247
+ return true;
248
+ }
249
+ const model = contentGeneratorConfig.model ?? "";
250
+ return model.toLowerCase().startsWith("glm-");
251
+ }
252
+ __name(isZaiProvider, "isZaiProvider");
253
+ var ZaiOpenAICompatibleProvider = class extends DefaultOpenAICompatibleProvider {
254
+ static {
255
+ __name(this, "ZaiOpenAICompatibleProvider");
256
+ }
257
+ static isZaiProvider = isZaiProvider;
258
+ static isZaiHostname = isZaiHostname;
259
+ // Latch so the skipped-flatten warning fires once per provider lifetime.
260
+ nonZaiHostnameFlattenWarned = false;
261
+ buildRequest(request, userPromptId) {
262
+ const baseRequest = super.buildRequest(request, userPromptId);
263
+ if (isZaiHostname(this.contentGeneratorConfig)) {
264
+ return flattenReasoningEffort(baseRequest);
265
+ }
266
+ const reasoning = baseRequest["reasoning"];
267
+ if (reasoning?.effort && !baseRequest["reasoning_effort"] && !this.nonZaiHostnameFlattenWarned) {
268
+ debugLogger.warn(
269
+ `GLM model '${this.contentGeneratorConfig.model ?? "unknown"}' on a non-Z.ai hostname; leaving nested reasoning.effort='${String(
270
+ reasoning.effort
271
+ )}' unflattened (reasoning_effort reshape is hostname-gated).`
272
+ );
273
+ this.nonZaiHostnameFlattenWarned = true;
274
+ }
275
+ return baseRequest;
276
+ }
277
+ };
278
+ function flattenReasoningEffort(request) {
279
+ const r = request;
280
+ const nested = r["reasoning"];
281
+ const effort = nested?.effort;
282
+ if (typeof effort !== "string" || !effort) {
283
+ return request;
284
+ }
285
+ const next = { ...r };
286
+ if (typeof next["reasoning_effort"] !== "string" || !next["reasoning_effort"]) {
287
+ next["reasoning_effort"] = effort;
288
+ }
289
+ if (nested && Object.keys(nested).length === 1) {
290
+ delete next["reasoning"];
291
+ } else if (nested) {
292
+ const { effort: _drop, ...rest } = nested;
293
+ next["reasoning"] = rest;
294
+ }
295
+ return next;
296
+ }
297
+ __name(flattenReasoningEffort, "flattenReasoningEffort");
298
+
299
+ // packages/core/src/core/openaiContentGenerator/provider/minimax.ts
300
+ init_esbuild_shims();
301
+ var MINIMAX_KNOWN_HOSTS = ["api.minimaxi.com", "api.minimax.io"];
302
+ var MINIMAX_HOST_SUFFIXES = [".minimaxi.com", ".minimax.io"];
303
+ var MiniMaxOpenAICompatibleProvider = class extends DefaultOpenAICompatibleProvider {
304
+ static {
305
+ __name(this, "MiniMaxOpenAICompatibleProvider");
306
+ }
307
+ static isMiniMaxProvider(config) {
308
+ if (!config.baseUrl) return false;
309
+ try {
310
+ const hostname = new URL(config.baseUrl).hostname.toLowerCase();
311
+ if (MINIMAX_KNOWN_HOSTS.includes(hostname)) {
312
+ return true;
313
+ }
314
+ return MINIMAX_HOST_SUFFIXES.some((suffix) => hostname.endsWith(suffix));
315
+ } catch {
316
+ return false;
317
+ }
318
+ }
319
+ getResponseParsingOptions() {
320
+ return { taggedThinkingTags: true };
321
+ }
322
+ };
323
+
324
+ // packages/core/src/core/openaiContentGenerator/provider/mistral.ts
325
+ init_esbuild_shims();
326
+ var MISTRAL_API_HOST = "api.mistral.ai";
327
+ var MISTRAL_MODEL_MARKERS = [
328
+ "mistral",
329
+ "mixtral",
330
+ "codestral",
331
+ "ministral",
332
+ "pixtral",
333
+ "magistral",
334
+ "devstral"
335
+ ];
336
+ function isMistralHostname(config) {
337
+ const baseUrl = config.baseUrl ?? "";
338
+ if (!baseUrl) return false;
339
+ try {
340
+ const hostname = new URL(baseUrl).hostname.toLowerCase();
341
+ return hostname === MISTRAL_API_HOST || hostname.endsWith(`.${MISTRAL_API_HOST}`);
342
+ } catch {
343
+ return false;
344
+ }
345
+ }
346
+ __name(isMistralHostname, "isMistralHostname");
347
+ function isMistralProvider(config) {
348
+ if (isMistralHostname(config)) return true;
349
+ const model = config.model?.toLowerCase() ?? "";
350
+ return MISTRAL_MODEL_MARKERS.some((marker) => model.includes(marker));
351
+ }
352
+ __name(isMistralProvider, "isMistralProvider");
353
+ var MistralOpenAICompatibleProvider = class extends DefaultOpenAICompatibleProvider {
354
+ static {
355
+ __name(this, "MistralOpenAICompatibleProvider");
356
+ }
357
+ static isMistralProvider = isMistralProvider;
358
+ buildRequest(request, userPromptId) {
359
+ const baseRequest = super.buildRequest(request, userPromptId);
360
+ return {
361
+ ...baseRequest,
362
+ messages: baseRequest.messages.map(stripReasoningContent)
363
+ };
364
+ }
365
+ };
366
+ function stripReasoningContent(message) {
367
+ if (!("reasoning_content" in message)) {
368
+ return message;
369
+ }
370
+ const next = { ...message };
371
+ delete next["reasoning_content"];
372
+ return next;
373
+ }
374
+ __name(stripReasoningContent, "stripReasoningContent");
375
+
376
+ // packages/core/src/core/openaiContentGenerator/provider/mimo.ts
377
+ init_esbuild_shims();
378
+ function isMiMoProvider(contentGeneratorConfig) {
379
+ const baseUrl = contentGeneratorConfig.baseUrl ?? "";
380
+ if (baseUrl) {
381
+ try {
382
+ const hostname = new URL(baseUrl).hostname.toLowerCase();
383
+ if (hostname === "xiaomimimo.com" || hostname.endsWith(".xiaomimimo.com")) {
384
+ return true;
385
+ }
386
+ } catch {
387
+ }
388
+ }
389
+ const model = contentGeneratorConfig.model ?? "";
390
+ return model.toLowerCase().startsWith("mimo-");
391
+ }
392
+ __name(isMiMoProvider, "isMiMoProvider");
393
+ var MiMoOpenAICompatibleProvider = class extends DefaultOpenAICompatibleProvider {
394
+ static {
395
+ __name(this, "MiMoOpenAICompatibleProvider");
396
+ }
397
+ constructor(contentGeneratorConfig, cliConfig) {
398
+ super(contentGeneratorConfig, cliConfig);
399
+ }
400
+ static isMiMoProvider = isMiMoProvider;
401
+ buildRequest(request, userPromptId) {
402
+ const baseRequest = super.buildRequest(request, userPromptId);
403
+ if (!baseRequest.messages?.length) {
404
+ return baseRequest;
405
+ }
406
+ return {
407
+ ...baseRequest,
408
+ messages: baseRequest.messages.map(
409
+ ensureReasoningContentOnAssistantMessage
410
+ )
411
+ };
412
+ }
413
+ getRequestContextOverrides() {
414
+ return {
415
+ splitToolMedia: this.contentGeneratorConfig.splitToolMedia ?? true
416
+ };
417
+ }
418
+ };
419
+
420
+ // packages/core/src/core/openaiContentGenerator/pipeline.ts
421
+ init_esbuild_shims();
422
+
423
+ // packages/core/src/core/openaiContentGenerator/streamingToolCallParser.ts
424
+ init_esbuild_shims();
425
+ var debugLogger2 = createDebugLogger("STREAMING_TOOL_CALL_PARSER");
426
+ var StreamingToolCallParser = class {
427
+ static {
428
+ __name(this, "StreamingToolCallParser");
429
+ }
430
+ /** Accumulated buffer containing all received chunks for each tool call index */
431
+ buffers = /* @__PURE__ */ new Map();
432
+ /** Current nesting depth in JSON structure for each tool call index */
433
+ depths = /* @__PURE__ */ new Map();
434
+ /** Whether we're currently inside a string literal for each tool call index */
435
+ inStrings = /* @__PURE__ */ new Map();
436
+ /** Whether the next character should be treated as escaped for each tool call index */
437
+ escapes = /* @__PURE__ */ new Map();
438
+ /** Metadata for each tool call index */
439
+ toolCallMeta = /* @__PURE__ */ new Map();
440
+ namelessToolCallIndices = /* @__PURE__ */ new Set();
441
+ /** Map from tool call ID to actual index used for storage */
442
+ idToIndexMap = /* @__PURE__ */ new Map();
443
+ /** Remapped slots awaiting a stable ID from a later chunk. */
444
+ pendingIndexRemaps = /* @__PURE__ */ new Map();
445
+ /** Counter for generating new indices when collisions occur */
446
+ nextAvailableIndex = 0;
447
+ conflictingToolCallIdentity = false;
448
+ invalidToolCallIndex = false;
449
+ /**
450
+ * Processes a new chunk of tool call data and attempts to parse complete JSON objects
451
+ *
452
+ * Handles the core problems of streaming tool call parsing:
453
+ * - Resolves index collisions when the same index is reused for different tool calls
454
+ * - Routes chunks without IDs to the correct incomplete tool call
455
+ * - Tracks JSON parsing state (depth, string boundaries, escapes) per tool call
456
+ * - Attempts parsing only when JSON structure is complete (depth = 0)
457
+ * - Repairs common issues like unclosed strings
458
+ *
459
+ * @param index - Tool call index from streaming response (may collide with existing calls)
460
+ * @param chunk - String chunk that may be empty, partial JSON, or complete data
461
+ * @param id - Optional tool call ID for collision detection and chunk routing
462
+ * @param name - Optional function name stored as metadata
463
+ * @returns ToolCallParseResult with completion status, parsed value, and repair info
464
+ */
465
+ addChunk(index, chunk, id, name) {
466
+ const validName = name?.trim() || void 0;
467
+ if (!Number.isSafeInteger(index) || index < 0) {
468
+ this.conflictingToolCallIdentity = true;
469
+ this.invalidToolCallIndex = true;
470
+ return {
471
+ complete: false,
472
+ error: new Error(`Invalid tool call index: ${index}`)
473
+ };
474
+ }
475
+ if (!id && !validName && !chunk.trim()) {
476
+ const depth2 = this.depths.get(index) ?? 0;
477
+ const inString2 = this.inStrings.get(index) ?? false;
478
+ if (!this.buffers.has(index) || depth2 === 0 && !inString2) {
479
+ return { complete: false };
480
+ }
481
+ }
482
+ let actualIndex = index;
483
+ const isKnownId = Boolean(id && this.idToIndexMap.has(id));
484
+ const existingName = this.toolCallMeta.get(index)?.name;
485
+ const isNameOnlyDelta = Boolean(
486
+ validName && chunk.length === 0 && (!existingName || existingName === validName)
487
+ );
488
+ if (id) {
489
+ if (this.idToIndexMap.has(id)) {
490
+ actualIndex = this.idToIndexMap.get(id);
491
+ } else if (this.pendingIndexRemaps.has(index)) {
492
+ actualIndex = this.pendingIndexRemaps.get(index);
493
+ this.pendingIndexRemaps.delete(index);
494
+ this.idToIndexMap.set(id, actualIndex);
495
+ } else {
496
+ if (this.buffers.has(index)) {
497
+ const existingBuffer = this.buffers.get(index);
498
+ const existingDepth = this.depths.get(index);
499
+ const existingMeta = this.toolCallMeta.get(index);
500
+ if (existingMeta?.id && existingMeta.id !== id) {
501
+ let existingComplete = existingDepth === 0;
502
+ if (existingComplete && existingBuffer.trim()) {
503
+ try {
504
+ JSON.parse(existingBuffer);
505
+ } catch {
506
+ existingComplete = false;
507
+ }
508
+ }
509
+ if (existingComplete) {
510
+ actualIndex = 0;
511
+ while (this.buffers.has(actualIndex)) actualIndex += 1;
512
+ if (!existingMeta.name) {
513
+ this.conflictingToolCallIdentity = true;
514
+ }
515
+ } else {
516
+ this.conflictingToolCallIdentity = true;
517
+ }
518
+ }
519
+ }
520
+ this.idToIndexMap.set(id, actualIndex);
521
+ }
522
+ } else if (!isNameOnlyDelta) {
523
+ if (this.pendingIndexRemaps.has(index)) {
524
+ actualIndex = this.pendingIndexRemaps.get(index);
525
+ const existingBuffer = this.buffers.get(actualIndex);
526
+ const existingDepth = this.depths.get(actualIndex);
527
+ if (existingDepth === 0 && existingBuffer.trim()) {
528
+ try {
529
+ JSON.parse(existingBuffer);
530
+ actualIndex = this.findMostRecentIncompleteIndex();
531
+ } catch {
532
+ }
533
+ }
534
+ } else if (this.buffers.has(index)) {
535
+ const existingBuffer = this.buffers.get(index);
536
+ const existingDepth = this.depths.get(index);
537
+ if (existingDepth > 0 || !existingBuffer.trim()) {
538
+ actualIndex = index;
539
+ } else {
540
+ try {
541
+ JSON.parse(existingBuffer);
542
+ actualIndex = this.findMostRecentIncompleteIndex();
543
+ } catch {
544
+ actualIndex = index;
545
+ }
546
+ }
547
+ }
548
+ }
549
+ if (!this.buffers.has(actualIndex)) {
550
+ this.buffers.set(actualIndex, "");
551
+ this.depths.set(actualIndex, 0);
552
+ this.inStrings.set(actualIndex, false);
553
+ this.escapes.set(actualIndex, false);
554
+ this.toolCallMeta.set(actualIndex, {});
555
+ }
556
+ const currentBuffer = this.buffers.get(actualIndex);
557
+ const currentDepth = this.depths.get(actualIndex);
558
+ const meta = this.toolCallMeta.get(actualIndex);
559
+ if (chunk.length === 0 && (id || validName)) {
560
+ if (id) meta.id = id;
561
+ if (validName && !meta.name) meta.name = validName;
562
+ if (!meta.name && meta.id) {
563
+ this.namelessToolCallIndices.add(actualIndex);
564
+ } else {
565
+ this.namelessToolCallIndices.delete(actualIndex);
566
+ }
567
+ if (!meta.id && actualIndex !== index) {
568
+ this.pendingIndexRemaps.set(index, actualIndex);
569
+ }
570
+ return { actualIndex, complete: false };
571
+ }
572
+ if (isKnownId && currentDepth === 0) {
573
+ if (currentBuffer.trim()) {
574
+ try {
575
+ JSON.parse(currentBuffer);
576
+ debugLogger2.debug(
577
+ `Ignoring replay chunk for completed toolCall id=${id}`
578
+ );
579
+ return { actualIndex, complete: false };
580
+ } catch {
581
+ }
582
+ }
583
+ }
584
+ const identityChanged = Boolean(id && meta.id && meta.id !== id);
585
+ if (id) meta.id = id;
586
+ if (validName) {
587
+ if (!identityChanged && meta.name && meta.name !== validName) {
588
+ this.conflictingToolCallIdentity = true;
589
+ } else {
590
+ meta.name = validName;
591
+ }
592
+ }
593
+ if (!meta.id && actualIndex !== index) {
594
+ this.pendingIndexRemaps.set(index, actualIndex);
595
+ }
596
+ const currentInString = this.inStrings.get(actualIndex);
597
+ const currentEscape = this.escapes.get(actualIndex);
598
+ const newBuffer = currentBuffer + chunk;
599
+ this.buffers.set(actualIndex, newBuffer);
600
+ if (!meta.name && (meta.id || /\S/.test(newBuffer))) {
601
+ this.namelessToolCallIndices.add(actualIndex);
602
+ } else {
603
+ this.namelessToolCallIndices.delete(actualIndex);
604
+ }
605
+ let depth = currentDepth;
606
+ let inString = currentInString;
607
+ let escape = currentEscape;
608
+ for (const char of chunk) {
609
+ if (!inString) {
610
+ if (char === "{" || char === "[") depth++;
611
+ else if (char === "}" || char === "]") depth--;
612
+ }
613
+ if (char === '"' && !escape) {
614
+ inString = !inString;
615
+ }
616
+ escape = char === "\\" && !escape;
617
+ }
618
+ this.depths.set(actualIndex, depth);
619
+ this.inStrings.set(actualIndex, inString);
620
+ this.escapes.set(actualIndex, escape);
621
+ if (depth === 0 && newBuffer.trim().length > 0) {
622
+ try {
623
+ const parsed = JSON.parse(newBuffer);
624
+ return { actualIndex, complete: true, value: parsed };
625
+ } catch (e) {
626
+ if (inString) {
627
+ try {
628
+ const repaired = JSON.parse(newBuffer + '"');
629
+ return {
630
+ actualIndex,
631
+ complete: true,
632
+ value: repaired,
633
+ repaired: true
634
+ };
635
+ } catch {
636
+ }
637
+ }
638
+ return {
639
+ actualIndex,
640
+ complete: false,
641
+ error: e instanceof Error ? e : new Error(String(e))
642
+ };
643
+ }
644
+ }
645
+ return { actualIndex, complete: false };
646
+ }
647
+ /**
648
+ * Gets the current tool call metadata for a specific index
649
+ *
650
+ * @param index - The tool call index
651
+ * @returns Object containing id and name if available
652
+ */
653
+ getToolCallMeta(index) {
654
+ return this.toolCallMeta.get(index) || {};
655
+ }
656
+ hasNamelessToolCall() {
657
+ return this.namelessToolCallIndices.size > 0;
658
+ }
659
+ hasConflictingToolCallIdentity() {
660
+ return this.conflictingToolCallIdentity;
661
+ }
662
+ hasInvalidToolCallIndex() {
663
+ return this.invalidToolCallIndex;
664
+ }
665
+ hasInvalidToolCallArguments() {
666
+ for (const [index, buffer] of this.buffers.entries()) {
667
+ if (!this.toolCallMeta.get(index)?.name || buffer.length === 0) continue;
668
+ try {
669
+ const args = JSON.parse(buffer);
670
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
671
+ return true;
672
+ }
673
+ } catch {
674
+ return true;
675
+ }
676
+ }
677
+ return false;
678
+ }
679
+ /**
680
+ * Gets all completed tool calls that are ready to be emitted
681
+ *
682
+ * Attempts to parse accumulated buffers using multiple strategies:
683
+ * 1. Standard JSON.parse()
684
+ * 2. Auto-close unclosed strings and retry
685
+ * 3. Fallback to safeJsonParse for malformed data
686
+ *
687
+ * Only returns tool calls with name metadata. An empty buffer yields
688
+ * empty arguments ({}), matching the non-streaming path for no-argument
689
+ * tools. Should be called when streaming is complete (finish_reason is
690
+ * present).
691
+ *
692
+ * @returns Array of completed tool calls with their metadata and parsed arguments
693
+ */
694
+ getCompletedToolCalls() {
695
+ const completed = [];
696
+ const emittedIds = /* @__PURE__ */ new Set();
697
+ for (const [index, buffer] of this.buffers.entries()) {
698
+ const meta = this.toolCallMeta.get(index);
699
+ if (meta?.name) {
700
+ if (meta.id) {
701
+ if (emittedIds.has(meta.id)) {
702
+ continue;
703
+ }
704
+ emittedIds.add(meta.id);
705
+ }
706
+ let args = {};
707
+ if (buffer.trim()) {
708
+ try {
709
+ args = JSON.parse(buffer);
710
+ } catch {
711
+ const inString = this.inStrings.get(index);
712
+ if (inString) {
713
+ try {
714
+ args = JSON.parse(buffer + '"');
715
+ } catch {
716
+ args = safeJsonParse(buffer, {});
717
+ }
718
+ } else {
719
+ args = safeJsonParse(buffer, {});
720
+ }
721
+ }
722
+ if (typeof args !== "object" || args === null || Array.isArray(args)) {
723
+ debugLogger2.debug(
724
+ `Collapsing non-object arguments for tool call ${meta.name} (id=${meta.id}) at index ${index}; buffer likely polluted by a misrouted fragment`
725
+ );
726
+ args = {};
727
+ }
728
+ } else {
729
+ debugLogger2.debug(
730
+ `Emitting no-argument tool call ${meta.name} (id=${meta.id}) at index ${index} with empty buffer`
731
+ );
732
+ }
733
+ completed.push({
734
+ id: meta.id,
735
+ name: meta.name,
736
+ args,
737
+ index
738
+ });
739
+ }
740
+ }
741
+ return completed;
742
+ }
743
+ /**
744
+ * Finds the next available index for a new tool call
745
+ *
746
+ * Scans indices starting from nextAvailableIndex to find one that's safe to use.
747
+ * Reuses indices never claimed by a named tool call or with incomplete
748
+ * parsing states. Skips indices with complete tool call data — including
749
+ * no-argument calls with empty buffers — to prevent overwriting.
750
+ *
751
+ * @returns The next available index safe for storing a new tool call
752
+ */
753
+ findNextAvailableIndex() {
754
+ while (this.buffers.has(this.nextAvailableIndex)) {
755
+ const buffer = this.buffers.get(this.nextAvailableIndex);
756
+ const depth = this.depths.get(this.nextAvailableIndex);
757
+ const meta = this.toolCallMeta.get(this.nextAvailableIndex);
758
+ if (!meta?.name || depth > 0 || !meta?.id) {
759
+ return this.nextAvailableIndex;
760
+ }
761
+ if (buffer.trim()) {
762
+ try {
763
+ JSON.parse(buffer);
764
+ } catch {
765
+ return this.nextAvailableIndex;
766
+ }
767
+ }
768
+ this.nextAvailableIndex++;
769
+ }
770
+ return this.nextAvailableIndex++;
771
+ }
772
+ /**
773
+ * Finds the most recent incomplete tool call index
774
+ *
775
+ * Used when continuation chunks arrive without IDs. Scans existing tool calls
776
+ * to find the highest index with incomplete parsing state (depth > 0, empty
777
+ * buffer with no name metadata yet, or unparseable JSON). Falls back to
778
+ * creating a new index if none found.
779
+ *
780
+ * @returns The index of the most recent incomplete tool call, or a new available index
781
+ */
782
+ findMostRecentIncompleteIndex() {
783
+ let maxIndex = -1;
784
+ for (const [index, buffer] of this.buffers.entries()) {
785
+ const depth = this.depths.get(index);
786
+ const meta = this.toolCallMeta.get(index);
787
+ if (meta?.id && (depth > 0 || !buffer.trim() && !meta?.name)) {
788
+ maxIndex = Math.max(maxIndex, index);
789
+ } else if (buffer.trim()) {
790
+ try {
791
+ JSON.parse(buffer);
792
+ } catch {
793
+ maxIndex = Math.max(maxIndex, index);
794
+ }
795
+ }
796
+ }
797
+ return maxIndex >= 0 ? maxIndex : this.findNextAvailableIndex();
798
+ }
799
+ /**
800
+ * Resets the parser state for a specific tool call index
801
+ *
802
+ * @param index - The tool call index to reset
803
+ */
804
+ resetIndex(index) {
805
+ this.buffers.set(index, "");
806
+ this.depths.set(index, 0);
807
+ this.inStrings.set(index, false);
808
+ this.escapes.set(index, false);
809
+ this.toolCallMeta.set(index, {});
810
+ this.namelessToolCallIndices.delete(index);
811
+ for (const [providerIndex, actualIndex] of this.pendingIndexRemaps) {
812
+ if (providerIndex === index || actualIndex === index) {
813
+ this.pendingIndexRemaps.delete(providerIndex);
814
+ }
815
+ }
816
+ }
817
+ /**
818
+ * Resets the entire parser state for processing a new stream
819
+ *
820
+ * Clears all accumulated buffers, parsing states, metadata, and counters.
821
+ * Allows the parser to be reused for multiple independent streams without
822
+ * data leakage between sessions.
823
+ */
824
+ reset() {
825
+ this.buffers.clear();
826
+ this.depths.clear();
827
+ this.inStrings.clear();
828
+ this.escapes.clear();
829
+ this.toolCallMeta.clear();
830
+ this.namelessToolCallIndices.clear();
831
+ this.idToIndexMap.clear();
832
+ this.pendingIndexRemaps.clear();
833
+ this.nextAvailableIndex = 0;
834
+ this.conflictingToolCallIdentity = false;
835
+ this.invalidToolCallIndex = false;
836
+ }
837
+ /**
838
+ * Gets the current accumulated buffer content for a specific index
839
+ *
840
+ * @param index - The tool call index to retrieve buffer for
841
+ * @returns The current buffer content for the specified index (empty string if not found)
842
+ */
843
+ getBuffer(index) {
844
+ return this.buffers.get(index) || "";
845
+ }
846
+ /**
847
+ * Gets the current parsing state information for a specific index
848
+ *
849
+ * @param index - The tool call index to get state information for
850
+ * @returns Object containing current parsing state (depth, inString, escape)
851
+ */
852
+ getState(index) {
853
+ return {
854
+ depth: this.depths.get(index) || 0,
855
+ inString: this.inStrings.get(index) || false,
856
+ escape: this.escapes.get(index) || false
857
+ };
858
+ }
859
+ /**
860
+ * Checks whether any buffered tool call has incomplete JSON at stream end.
861
+ *
862
+ * A tool call is considered incomplete when its JSON parsing state indicates
863
+ * the buffer was truncated mid-stream:
864
+ * - depth > 0: unclosed braces/brackets remain
865
+ * - inString === true: still inside a string literal
866
+ *
867
+ * This is critical for detecting output truncation that the LLM provider
868
+ * may not report correctly via finish_reason (e.g. reporting "stop" or
869
+ * "tool_calls" instead of "length" when output was actually cut off).
870
+ *
871
+ * @returns true if at least one tool call buffer has incomplete JSON
872
+ */
873
+ hasIncompleteToolCalls() {
874
+ for (const [index] of this.buffers.entries()) {
875
+ const meta = this.toolCallMeta.get(index);
876
+ if (!meta?.name) continue;
877
+ const depth = this.depths.get(index) || 0;
878
+ const inString = this.inStrings.get(index) || false;
879
+ if (depth > 0 || inString) {
880
+ return true;
881
+ }
882
+ }
883
+ return false;
884
+ }
885
+ };
886
+
887
+ // packages/core/src/core/openaiContentGenerator/pipeline.ts
888
+ var debugLogger3 = createDebugLogger("OPENAI_PIPELINE");
889
+ var StreamContentError = class extends Error {
890
+ static {
891
+ __name(this, "StreamContentError");
892
+ }
893
+ constructor(message) {
894
+ super(message);
895
+ this.name = "StreamContentError";
896
+ }
897
+ };
898
+ var StreamInactivityTimeoutError = class extends Error {
899
+ constructor(idleMs, chunksReceived, streamLifetimeMs) {
900
+ super(
901
+ `No stream activity for ${idleMs}ms after ${chunksReceived} chunks (stream lifetime: ${streamLifetimeMs}ms). Set ${QWEN_STREAM_IDLE_TIMEOUT_MS_ENV} to increase this window (or 0 to disable it).`
902
+ );
903
+ this.idleMs = idleMs;
904
+ this.chunksReceived = chunksReceived;
905
+ this.streamLifetimeMs = streamLifetimeMs;
906
+ this.name = "StreamInactivityTimeoutError";
907
+ }
908
+ static {
909
+ __name(this, "StreamInactivityTimeoutError");
910
+ }
911
+ code = "ETIMEDOUT";
912
+ };
913
+ var NON_SSE_BODY_PREFIX_LIMIT = 512;
914
+ function isSSECompatibleContentType(contentType) {
915
+ if (!contentType) return true;
916
+ const mediaType = (contentType.split(";")[0] ?? "").trim().toLowerCase();
917
+ return mediaType === "text/event-stream" || mediaType === "application/x-ndjson" || mediaType === "application/stream+json";
918
+ }
919
+ __name(isSSECompatibleContentType, "isSSECompatibleContentType");
920
+ var NonSSEResponseError = class extends Error {
921
+ constructor(contentType, httpStatus, bodyPrefix, requestId) {
922
+ const preview = bodyPrefix.length > 0 ? ` Body prefix: ${bodyPrefix}` : "";
923
+ super(
924
+ `Streaming request received a non-SSE response (HTTP ${httpStatus}, Content-Type: ${contentType || "unknown"}).${preview}`
925
+ );
926
+ this.contentType = contentType;
927
+ this.httpStatus = httpStatus;
928
+ this.bodyPrefix = bodyPrefix;
929
+ this.requestId = requestId;
930
+ this.name = "NonSSEResponseError";
931
+ this.status = httpStatus;
932
+ this.request_id = requestId;
933
+ }
934
+ static {
935
+ __name(this, "NonSSEResponseError");
936
+ }
937
+ status;
938
+ request_id;
939
+ };
940
+ var PROVIDER_OUTPUT_BUDGET_KEYS = ["max_completion_tokens", "max_new_tokens"];
941
+ function hasProviderOutputBudgetKey(samplingParams) {
942
+ return PROVIDER_OUTPUT_BUDGET_KEYS.some(
943
+ (key) => samplingParams[key] !== void 0
944
+ );
945
+ }
946
+ __name(hasProviderOutputBudgetKey, "hasProviderOutputBudgetKey");
947
+ function clampProviderOutputBudgetKeys(samplingParams, requestMaxTokens) {
948
+ if (typeof requestMaxTokens !== "number") return samplingParams;
949
+ for (const key of PROVIDER_OUTPUT_BUDGET_KEYS) {
950
+ const value = samplingParams[key];
951
+ if (typeof value === "number" && value > requestMaxTokens) {
952
+ samplingParams[key] = requestMaxTokens;
953
+ }
954
+ }
955
+ return samplingParams;
956
+ }
957
+ __name(clampProviderOutputBudgetKeys, "clampProviderOutputBudgetKeys");
958
+ function resolveStreamIdleTimeoutMs(config) {
959
+ const fromConfig = config.streamIdleTimeoutMs;
960
+ if (typeof fromConfig === "number") {
961
+ if (Number.isInteger(fromConfig) && fromConfig <= MAX_STREAM_IDLE_TIMEOUT_MS) {
962
+ return fromConfig;
963
+ }
964
+ console.warn(
965
+ `[qwen-code] Ignoring out-of-range streamIdleTimeoutMs=${fromConfig} (expected an integer in (-\u221E, ${MAX_STREAM_IDLE_TIMEOUT_MS}]); falling back to ${QWEN_STREAM_IDLE_TIMEOUT_MS_ENV}/default.`
966
+ );
967
+ }
968
+ const raw = process.env[QWEN_STREAM_IDLE_TIMEOUT_MS_ENV];
969
+ const trimmed = raw?.trim();
970
+ if (trimmed) {
971
+ if (/^\d+$/.test(trimmed)) {
972
+ const parsed = Number(trimmed);
973
+ if (parsed <= MAX_STREAM_IDLE_TIMEOUT_MS) {
974
+ return parsed;
975
+ }
976
+ }
977
+ console.warn(
978
+ `[qwen-code] Ignoring invalid ${QWEN_STREAM_IDLE_TIMEOUT_MS_ENV}="${raw}" (expected an integer of milliseconds in [0, ${MAX_STREAM_IDLE_TIMEOUT_MS}]); using default ${DEFAULT_STREAM_IDLE_TIMEOUT_MS}ms.`
979
+ );
980
+ }
981
+ return DEFAULT_STREAM_IDLE_TIMEOUT_MS;
982
+ }
983
+ __name(resolveStreamIdleTimeoutMs, "resolveStreamIdleTimeoutMs");
984
+ async function* withStreamInactivityTimeout(source, idleMs, abortRequest, parentSignal) {
985
+ const it = source[Symbol.asyncIterator]();
986
+ const streamStartedAt = Date.now();
987
+ let chunksReceived = 0;
988
+ try {
989
+ while (true) {
990
+ const nextPromise = it.next();
991
+ let timer;
992
+ const timeout = new Promise((_resolve, reject) => {
993
+ timer = setTimeout(() => {
994
+ if (parentSignal?.aborted) {
995
+ const abortErr = new Error("Aborted");
996
+ abortErr.name = "AbortError";
997
+ reject(abortErr);
998
+ } else {
999
+ abortRequest();
1000
+ reject(
1001
+ new StreamInactivityTimeoutError(
1002
+ idleMs,
1003
+ chunksReceived,
1004
+ Date.now() - streamStartedAt
1005
+ )
1006
+ );
1007
+ }
1008
+ }, idleMs);
1009
+ timer.unref?.();
1010
+ });
1011
+ let result;
1012
+ try {
1013
+ result = await Promise.race([nextPromise, timeout]);
1014
+ } catch (err) {
1015
+ void Promise.resolve(nextPromise).catch(() => {
1016
+ });
1017
+ throw err;
1018
+ } finally {
1019
+ if (timer !== void 0) clearTimeout(timer);
1020
+ }
1021
+ if (result.done) return;
1022
+ chunksReceived += 1;
1023
+ yield result.value;
1024
+ }
1025
+ } finally {
1026
+ abortRequest();
1027
+ try {
1028
+ await it.return?.();
1029
+ } catch {
1030
+ }
1031
+ }
1032
+ }
1033
+ __name(withStreamInactivityTimeout, "withStreamInactivityTimeout");
1034
+ var ContentGenerationPipeline = class {
1035
+ constructor(config) {
1036
+ this.config = config;
1037
+ this.contentGeneratorConfig = config.contentGeneratorConfig;
1038
+ this.client = this.config.provider.buildClient();
1039
+ this.streamIdleTimeoutMs = resolveStreamIdleTimeoutMs(
1040
+ this.contentGeneratorConfig
1041
+ );
1042
+ }
1043
+ static {
1044
+ __name(this, "ContentGenerationPipeline");
1045
+ }
1046
+ client;
1047
+ contentGeneratorConfig;
1048
+ // Resolved once (config field > env > default) so the env read + any
1049
+ // invalid-value warning happen per pipeline, not per streaming request.
1050
+ streamIdleTimeoutMs;
1051
+ async execute(request, userPromptId) {
1052
+ return this.executeWithErrorHandling(
1053
+ request,
1054
+ userPromptId,
1055
+ false,
1056
+ async (openaiRequest, context) => {
1057
+ const parentSignal = request.config?.abortSignal;
1058
+ const perRequestAc = parentSignal ? createChildAbortController(parentSignal) : void 0;
1059
+ try {
1060
+ const openaiResponse = await this.client.chat.completions.create(
1061
+ openaiRequest,
1062
+ {
1063
+ signal: perRequestAc?.signal
1064
+ }
1065
+ );
1066
+ const geminiResponse = OpenAIContentConverter.convertOpenAIResponseToGemini(
1067
+ openaiResponse,
1068
+ context
1069
+ );
1070
+ return geminiResponse;
1071
+ } finally {
1072
+ perRequestAc?.abort();
1073
+ }
1074
+ }
1075
+ );
1076
+ }
1077
+ async executeStream(request, userPromptId) {
1078
+ return this.executeWithErrorHandling(
1079
+ request,
1080
+ userPromptId,
1081
+ true,
1082
+ async (openaiRequest, context) => {
1083
+ const parentSignal = request.config?.abortSignal;
1084
+ const perRequestAc = createChildAbortController(parentSignal);
1085
+ let stream;
1086
+ try {
1087
+ const createPromise = this.client.chat.completions.create(
1088
+ openaiRequest,
1089
+ { signal: perRequestAc.signal }
1090
+ );
1091
+ if (typeof createPromise.withResponse === "function") {
1092
+ const {
1093
+ data,
1094
+ response: httpResponse,
1095
+ request_id
1096
+ } = await createPromise.withResponse();
1097
+ stream = data;
1098
+ const contentType = httpResponse.headers.get("content-type") ?? null;
1099
+ if (!isSSECompatibleContentType(contentType)) {
1100
+ let bodyPrefix = "";
1101
+ try {
1102
+ if (httpResponse.body) {
1103
+ const reader = httpResponse.body.getReader();
1104
+ const { value } = await reader.read();
1105
+ reader.releaseLock();
1106
+ if (value) {
1107
+ bodyPrefix = new TextDecoder().decode(value).slice(0, NON_SSE_BODY_PREFIX_LIMIT);
1108
+ }
1109
+ }
1110
+ } catch {
1111
+ }
1112
+ throw new NonSSEResponseError(
1113
+ contentType,
1114
+ httpResponse.status,
1115
+ bodyPrefix,
1116
+ request_id
1117
+ );
1118
+ }
1119
+ } else {
1120
+ stream = await createPromise;
1121
+ }
1122
+ } catch (e) {
1123
+ perRequestAc.abort();
1124
+ throw e;
1125
+ }
1126
+ const idleMs = this.streamIdleTimeoutMs;
1127
+ const guarded = idleMs > 0 ? withStreamInactivityTimeout(
1128
+ stream,
1129
+ idleMs,
1130
+ () => perRequestAc.abort(),
1131
+ parentSignal
1132
+ ) : stream;
1133
+ const innerStream = this.processStreamWithLogging(
1134
+ guarded,
1135
+ context,
1136
+ request,
1137
+ userPromptId
1138
+ );
1139
+ async function* drainThenCleanup() {
1140
+ try {
1141
+ yield* innerStream;
1142
+ } finally {
1143
+ perRequestAc.abort();
1144
+ }
1145
+ }
1146
+ __name(drainThenCleanup, "drainThenCleanup");
1147
+ return drainThenCleanup();
1148
+ }
1149
+ );
1150
+ }
1151
+ /**
1152
+ * Stage 2: Process OpenAI stream with conversion and logging
1153
+ * This method handles the complete stream processing pipeline:
1154
+ * 1. Convert OpenAI chunks to Gemini format while preserving original chunks
1155
+ * 2. Filter empty responses
1156
+ * 3. Handle chunk merging for providers that send finishReason and usageMetadata separately
1157
+ * 4. Handle success/error logging
1158
+ */
1159
+ async *processStreamWithLogging(stream, context, request, userPromptId) {
1160
+ let pendingFinishResponse = null;
1161
+ let finishYielded = false;
1162
+ let pendingFinishProtocolTagSanitized;
1163
+ const logPendingProtocolTagSanitized = /* @__PURE__ */ __name((response, sanitization) => {
1164
+ if (!sanitization) return;
1165
+ const event = new ProtocolTagSanitizedEvent({
1166
+ model: context.model,
1167
+ promptId: userPromptId,
1168
+ responseId: response.responseId,
1169
+ tagName: sanitization.tagName,
1170
+ toolCallCount: sanitization.toolCallCount
1171
+ });
1172
+ debugLogger3.warn("Sanitized a model protocol tag", {
1173
+ model: event.model,
1174
+ promptId: event.prompt_id,
1175
+ responseId: event.response_id,
1176
+ tagName: event.tag_name,
1177
+ toolCallCount: event.tool_call_count
1178
+ });
1179
+ logProtocolTagSanitized(this.config.cliConfig, event);
1180
+ }, "logPendingProtocolTagSanitized");
1181
+ try {
1182
+ for await (const chunk of stream) {
1183
+ if (chunk.choices?.[0]?.finish_reason === "error_finish") {
1184
+ const errorContent = chunk.choices?.[0]?.delta?.content?.trim() || "Unknown stream error";
1185
+ throw new StreamContentError(errorContent);
1186
+ }
1187
+ const response = OpenAIContentConverter.convertOpenAIChunkToGemini(
1188
+ chunk,
1189
+ context
1190
+ );
1191
+ const sanitization = context.protocolTagSanitized;
1192
+ if (sanitization) {
1193
+ context.protocolTagSanitized = void 0;
1194
+ }
1195
+ if (response.candidates?.[0]?.content?.parts?.length === 0 && !response.candidates?.[0]?.finishReason && !response.usageMetadata && // Preparation-only responses must reach ACP before arguments complete.
1196
+ getToolCallPreparations(response).length === 0) {
1197
+ continue;
1198
+ }
1199
+ if (pendingFinishProtocolTagSanitized && pendingFinishResponse && !response.candidates?.[0]?.finishReason && response.candidates?.some(
1200
+ (candidate) => (candidate.content?.parts?.length ?? 0) > 0
1201
+ )) {
1202
+ throw new InvalidStreamError(
1203
+ "Model response continued after a finish reason.",
1204
+ "PROTOCOL_TAG_LEAK"
1205
+ );
1206
+ }
1207
+ if (finishYielded) {
1208
+ if (response.usageMetadata) {
1209
+ const pending = pendingFinishResponse;
1210
+ if (pending) {
1211
+ pending.usageMetadata = response.usageMetadata;
1212
+ }
1213
+ }
1214
+ continue;
1215
+ }
1216
+ if (!pendingFinishResponse && response.candidates?.[0]?.finishReason && sanitization) {
1217
+ pendingFinishProtocolTagSanitized = sanitization;
1218
+ }
1219
+ const shouldYield = this.handleChunkMerging(
1220
+ response,
1221
+ pendingFinishResponse,
1222
+ (mergedResponse) => {
1223
+ pendingFinishResponse = mergedResponse;
1224
+ }
1225
+ );
1226
+ if (shouldYield) {
1227
+ if (pendingFinishResponse) {
1228
+ logPendingProtocolTagSanitized(
1229
+ pendingFinishResponse,
1230
+ pendingFinishProtocolTagSanitized
1231
+ );
1232
+ yield pendingFinishResponse;
1233
+ finishYielded = true;
1234
+ } else {
1235
+ logPendingProtocolTagSanitized(response, sanitization);
1236
+ yield response;
1237
+ }
1238
+ }
1239
+ }
1240
+ if (context.pendingThinkingTagCandidate && !context.pendingThinkingTagCandidate.closingTagName && !/\S/.test(context.pendingThinkingTagCandidate.text)) {
1241
+ const pendingParts = context.pendingUntrustedResponseParts;
1242
+ context.pendingThinkingTagCandidate = void 0;
1243
+ context.pendingUntrustedResponseParts = void 0;
1244
+ if (pendingParts?.length) {
1245
+ const response = new GenerateContentResponse();
1246
+ response.candidates = [
1247
+ {
1248
+ content: { parts: pendingParts, role: "model" },
1249
+ index: 0
1250
+ }
1251
+ ];
1252
+ yield response;
1253
+ }
1254
+ } else if (context.pendingThinkingTagCandidate) {
1255
+ throw new InvalidStreamError(
1256
+ "Model response leaked thinking tags.",
1257
+ "PROTOCOL_TAG_LEAK"
1258
+ );
1259
+ }
1260
+ if (pendingFinishResponse && !finishYielded) {
1261
+ logPendingProtocolTagSanitized(
1262
+ pendingFinishResponse,
1263
+ pendingFinishProtocolTagSanitized
1264
+ );
1265
+ yield pendingFinishResponse;
1266
+ }
1267
+ } catch (error) {
1268
+ if (error instanceof InvalidStreamError) {
1269
+ throw error;
1270
+ }
1271
+ if (error instanceof StreamContentError) {
1272
+ throw redactProxyError(error);
1273
+ }
1274
+ if (context.pendingThinkingTagCandidate?.closingTagName && request.config?.abortSignal?.aborted !== true) {
1275
+ context.pendingThinkingTagCandidate = void 0;
1276
+ context.pendingUntrustedResponseParts = void 0;
1277
+ throw new InvalidStreamError(
1278
+ "Model response leaked thinking tags.",
1279
+ "PROTOCOL_TAG_LEAK"
1280
+ );
1281
+ }
1282
+ if (error instanceof StreamInactivityTimeoutError) {
1283
+ debugLogger3.warn("OpenAI stream inactivity timeout", {
1284
+ idleMs: error.idleMs,
1285
+ chunksReceived: error.chunksReceived,
1286
+ streamLifetimeMs: error.streamLifetimeMs
1287
+ });
1288
+ throw redactProxyError(error);
1289
+ }
1290
+ await this.handleError(error, context, request);
1291
+ }
1292
+ }
1293
+ /**
1294
+ * Handle chunk merging for providers that send finishReason and usageMetadata separately.
1295
+ *
1296
+ * Strategy: When we encounter a finishReason chunk, we hold it and merge all subsequent
1297
+ * chunks into it until the stream ends. This ensures the final chunk contains both
1298
+ * finishReason and the most up-to-date usage information from any provider pattern.
1299
+ *
1300
+ * @param response Current Gemini response
1301
+ * @param pendingFinishResponse Finish response currently held for merging
1302
+ * @param setPendingFinish Callback to set pending finish response
1303
+ * @returns true if the response should be yielded, false if it should be held for merging
1304
+ */
1305
+ handleChunkMerging(response, pendingFinishResponse, setPendingFinish) {
1306
+ const isFinishChunk = response.candidates?.[0]?.finishReason;
1307
+ if (isFinishChunk) {
1308
+ if (pendingFinishResponse) {
1309
+ if (response.usageMetadata) {
1310
+ pendingFinishResponse.usageMetadata = response.usageMetadata;
1311
+ }
1312
+ setPendingFinish(pendingFinishResponse);
1313
+ } else {
1314
+ setPendingFinish(response);
1315
+ }
1316
+ return false;
1317
+ } else if (pendingFinishResponse) {
1318
+ const mergedResponse = new GenerateContentResponse();
1319
+ mergedResponse.candidates = pendingFinishResponse.candidates;
1320
+ if (response.usageMetadata) {
1321
+ mergedResponse.usageMetadata = response.usageMetadata;
1322
+ } else {
1323
+ mergedResponse.usageMetadata = pendingFinishResponse.usageMetadata;
1324
+ }
1325
+ mergedResponse.responseId = response.responseId;
1326
+ mergedResponse.createTime = response.createTime;
1327
+ mergedResponse.modelVersion = response.modelVersion;
1328
+ mergedResponse.promptFeedback = response.promptFeedback;
1329
+ setPendingFinish(mergedResponse);
1330
+ return true;
1331
+ }
1332
+ return true;
1333
+ }
1334
+ async buildRequest(request, userPromptId, context, isStreaming) {
1335
+ const messages = OpenAIContentConverter.convertGeminiRequestToOpenAI(
1336
+ request,
1337
+ context
1338
+ );
1339
+ const baseRequest = {
1340
+ model: context.model,
1341
+ messages,
1342
+ ...this.buildGenerateContentConfig(request)
1343
+ };
1344
+ if (isStreaming) {
1345
+ baseRequest.stream = true;
1346
+ baseRequest.stream_options = { include_usage: true };
1347
+ } else {
1348
+ baseRequest.stream = false;
1349
+ }
1350
+ if (request.config?.tools && request.config.tools.length > 0) {
1351
+ baseRequest.tools = await OpenAIContentConverter.convertGeminiToolsToOpenAI(
1352
+ request.config.tools,
1353
+ this.contentGeneratorConfig.schemaCompliance ?? "auto"
1354
+ );
1355
+ const fcMode = request.config?.toolConfig?.functionCallingConfig?.mode;
1356
+ if (fcMode === "ANY") {
1357
+ baseRequest["tool_choice"] = "required";
1358
+ } else if (fcMode === "NONE") {
1359
+ baseRequest["tool_choice"] = "none";
1360
+ }
1361
+ }
1362
+ const providerRequest = this.config.provider.buildRequest(
1363
+ baseRequest,
1364
+ userPromptId
1365
+ );
1366
+ const reasoningDisabled = request.config?.thinkingConfig?.includeThoughts === false || this.contentGeneratorConfig.reasoning === false;
1367
+ if (reasoningDisabled) {
1368
+ const typed = providerRequest;
1369
+ const model = (context.model ?? "").toLowerCase();
1370
+ if (model.startsWith("qwen") || model === "coder-model") {
1371
+ if (DashScopeOpenAICompatibleProvider.isDashScopeProvider(
1372
+ this.contentGeneratorConfig
1373
+ )) {
1374
+ typed["enable_thinking"] = false;
1375
+ } else {
1376
+ delete typed["enable_thinking"];
1377
+ const existing = typed["chat_template_kwargs"] ?? {};
1378
+ typed["chat_template_kwargs"] = {
1379
+ ...existing,
1380
+ enable_thinking: false
1381
+ };
1382
+ }
1383
+ }
1384
+ if ("reasoning" in typed) {
1385
+ delete typed["reasoning"];
1386
+ }
1387
+ if ("reasoning_effort" in typed) {
1388
+ delete typed["reasoning_effort"];
1389
+ }
1390
+ if (isDeepSeekHostname(this.contentGeneratorConfig)) {
1391
+ typed["thinking"] = { type: "disabled" };
1392
+ }
1393
+ }
1394
+ return providerRequest;
1395
+ }
1396
+ buildGenerateContentConfig(request) {
1397
+ const defaultSamplingParams = this.config.provider.getDefaultGenerationConfig();
1398
+ const configSamplingParams = this.contentGeneratorConfig.samplingParams;
1399
+ const getParameterValue = /* @__PURE__ */ __name((configKey, requestKey) => {
1400
+ const configValue = configSamplingParams?.[configKey];
1401
+ const requestValue = requestKey ? request.config?.[requestKey] : void 0;
1402
+ const defaultValue = requestKey ? defaultSamplingParams[requestKey] : void 0;
1403
+ if (configValue !== void 0) return configValue;
1404
+ if (requestValue !== void 0) return requestValue;
1405
+ return defaultValue;
1406
+ }, "getParameterValue");
1407
+ const addParameterIfDefined = /* @__PURE__ */ __name((key, configKey, requestKey) => {
1408
+ const value = getParameterValue(configKey, requestKey);
1409
+ return value !== void 0 ? { [key]: value } : {};
1410
+ }, "addParameterIfDefined");
1411
+ if (configSamplingParams !== void 0) {
1412
+ const requestMaxTokens = request.config?.maxOutputTokens;
1413
+ const maxTokens = reconcileMaxTokens(configSamplingParams.max_tokens, requestMaxTokens) ?? configSamplingParams.max_tokens ?? (hasProviderOutputBudgetKey(configSamplingParams) ? void 0 : requestMaxTokens);
1414
+ return clampProviderOutputBudgetKeys(
1415
+ maxTokens !== void 0 ? { ...configSamplingParams, max_tokens: maxTokens } : { ...configSamplingParams },
1416
+ requestMaxTokens
1417
+ );
1418
+ }
1419
+ const params = {
1420
+ // Parameters with request fallback but no defaults
1421
+ ...addParameterIfDefined("temperature", "temperature", "temperature"),
1422
+ ...addParameterIfDefined("top_p", "top_p", "topP"),
1423
+ // Max tokens (special case: different property names)
1424
+ ...addParameterIfDefined("max_tokens", "max_tokens", "maxOutputTokens"),
1425
+ // Config-only parameters (no request fallback)
1426
+ ...addParameterIfDefined("top_k", "top_k", "topK"),
1427
+ ...addParameterIfDefined("repetition_penalty", "repetition_penalty"),
1428
+ ...addParameterIfDefined(
1429
+ "presence_penalty",
1430
+ "presence_penalty",
1431
+ "presencePenalty"
1432
+ ),
1433
+ ...addParameterIfDefined(
1434
+ "frequency_penalty",
1435
+ "frequency_penalty",
1436
+ "frequencyPenalty"
1437
+ ),
1438
+ ...this.buildReasoningConfig(request)
1439
+ };
1440
+ return params;
1441
+ }
1442
+ buildReasoningConfig(request) {
1443
+ if (request.config?.thinkingConfig?.includeThoughts === false) {
1444
+ return {};
1445
+ }
1446
+ const reasoning = this.contentGeneratorConfig.reasoning;
1447
+ if (reasoning === false || reasoning === void 0) {
1448
+ return {};
1449
+ }
1450
+ return { reasoning };
1451
+ }
1452
+ /**
1453
+ * Common error handling wrapper for execute methods
1454
+ */
1455
+ async executeWithErrorHandling(request, userPromptId, isStreaming, executor) {
1456
+ const context = this.createRequestContext(request, isStreaming);
1457
+ try {
1458
+ const openaiRequest = await this.buildRequest(
1459
+ request,
1460
+ userPromptId,
1461
+ context,
1462
+ isStreaming
1463
+ );
1464
+ openaiRequestCaptureContext.getStore()?.(openaiRequest);
1465
+ runtimeDiagnostics.recordOpenAIWireRequest(openaiRequest);
1466
+ const result = await executor(openaiRequest, context);
1467
+ return result;
1468
+ } catch (error) {
1469
+ return await this.handleError(error, context, request);
1470
+ }
1471
+ }
1472
+ /**
1473
+ * Shared error handling logic for both executeWithErrorHandling and processStreamWithLogging
1474
+ * This centralizes the common error processing steps to avoid duplication
1475
+ */
1476
+ async handleError(error, context, request) {
1477
+ this.config.errorHandler.handle(redactProxyError(error), context, request);
1478
+ }
1479
+ /**
1480
+ * Create request context with common properties
1481
+ */
1482
+ createRequestContext(request, isStreaming) {
1483
+ const effectiveModel = request.model || this.contentGeneratorConfig.model;
1484
+ const providerOverrides = this.config.provider.getRequestContextOverrides?.() ?? {};
1485
+ const toolCallParser = isStreaming ? new StreamingToolCallParser() : void 0;
1486
+ const responseParsingOptions = this.config.provider.getResponseParsingOptions?.();
1487
+ const taggedThinkingParser = isStreaming && responseParsingOptions?.taggedThinkingTags ? new TaggedThinkingParser() : void 0;
1488
+ return {
1489
+ model: effectiveModel,
1490
+ modalities: this.contentGeneratorConfig.modalities ?? {},
1491
+ startTime: Date.now(),
1492
+ splitToolMedia: providerOverrides.splitToolMedia ?? this.contentGeneratorConfig.splitToolMedia ?? // Default true: the OpenAI Chat Completions spec only permits text on
1493
+ // `role: "tool"` messages, so tool-returned media (e.g. an image read
1494
+ // by read_file) embedded there is silently dropped or rejected by
1495
+ // strict providers (doubao / new-api / LM Studio) and the model never
1496
+ // sees it (QwenLM/qwen-code#4876). Splitting it into a follow-up user
1497
+ // message is spec-compliant and safe for permissive providers too.
1498
+ // Opt out via generationConfig.splitToolMedia = false.
1499
+ true,
1500
+ toolResultContentFormat: providerOverrides.toolResultContentFormat ?? this.contentGeneratorConfig.toolResultContentFormat ?? "parts",
1501
+ ...toolCallParser ? { toolCallParser } : {},
1502
+ ...responseParsingOptions ? { responseParsingOptions } : {},
1503
+ ...taggedThinkingParser ? { taggedThinkingParser } : {}
1504
+ };
1505
+ }
1506
+ };
1507
+
1508
+ // packages/core/src/core/openaiContentGenerator/errorHandler.ts
1509
+ init_esbuild_shims();
1510
+ var debugLogger4 = createDebugLogger("OPENAI_ERROR");
1511
+ var EnhancedErrorHandler = class {
1512
+ constructor(shouldSuppressLogging = () => false) {
1513
+ this.shouldSuppressLogging = shouldSuppressLogging;
1514
+ }
1515
+ static {
1516
+ __name(this, "EnhancedErrorHandler");
1517
+ }
1518
+ handle(error, context, request) {
1519
+ const redactedError = redactProxyError(error);
1520
+ const isTimeoutError = this.isTimeoutError(redactedError);
1521
+ const errorMessage = this.buildErrorMessage(
1522
+ redactedError,
1523
+ context,
1524
+ isTimeoutError
1525
+ );
1526
+ if (!this.shouldSuppressErrorLogging(redactedError, request)) {
1527
+ debugLogger4.error(
1528
+ "OpenAI API Error:",
1529
+ errorMessage,
1530
+ this.buildDiagnostics(redactedError, context)
1531
+ );
1532
+ }
1533
+ if (isTimeoutError) {
1534
+ throw new Error(
1535
+ `${errorMessage}
1536
+
1537
+ ${this.getTimeoutTroubleshootingTips()}`
1538
+ );
1539
+ }
1540
+ throw redactedError;
1541
+ }
1542
+ shouldSuppressErrorLogging(error, request) {
1543
+ return this.shouldSuppressLogging(error, request);
1544
+ }
1545
+ isTimeoutError(error) {
1546
+ if (!error) return false;
1547
+ const errorMessage = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
1548
+ const errorCode = error?.code;
1549
+ const errorType = error?.type;
1550
+ return errorMessage.includes("timeout") || errorMessage.includes("timed out") || errorMessage.includes("connection timeout") || errorMessage.includes("request timeout") || errorMessage.includes("read timeout") || errorMessage.includes("etimedout") || errorMessage.includes("esockettimedout") || errorCode === "ETIMEDOUT" || errorCode === "ESOCKETTIMEDOUT" || errorType === "timeout" || errorMessage.includes("request timed out") || errorMessage.includes("deadline exceeded");
1551
+ }
1552
+ buildErrorMessage(error, context, isTimeoutError) {
1553
+ const durationSeconds = Math.round((Date.now() - context.startTime) / 1e3);
1554
+ if (isTimeoutError) {
1555
+ return `Request timeout after ${durationSeconds}s. Try reducing input length or increasing timeout in config.`;
1556
+ }
1557
+ return error instanceof Error ? getErrorMessage(error) : String(error);
1558
+ }
1559
+ buildDiagnostics(error, context) {
1560
+ const details = getRateLimitErrorDetails(error);
1561
+ const requestId = this.getRequestId(error) ?? details.requestId;
1562
+ const statusCode = getErrorStatus(error);
1563
+ return {
1564
+ model: context.model,
1565
+ durationMs: Date.now() - context.startTime,
1566
+ errorType: getErrorType(error),
1567
+ ...statusCode !== void 0 ? { statusCode } : {},
1568
+ ...details.providerCode !== void 0 ? { providerCode: details.providerCode } : {},
1569
+ ...details.providerMessage !== void 0 ? { providerMessage: details.providerMessage } : {},
1570
+ ...requestId !== void 0 ? { requestId } : {},
1571
+ ...details.transport !== "unknown" ? { transport: details.transport } : {}
1572
+ };
1573
+ }
1574
+ getRequestId(error) {
1575
+ if (!error || typeof error !== "object") return void 0;
1576
+ const source = error;
1577
+ for (const value of [
1578
+ source.requestID,
1579
+ source.request_id,
1580
+ source.response_id
1581
+ ]) {
1582
+ if (typeof value === "string" && value) {
1583
+ return value;
1584
+ }
1585
+ }
1586
+ return void 0;
1587
+ }
1588
+ getTimeoutTroubleshootingTips() {
1589
+ const tips = [
1590
+ "- Reduce input length or complexity",
1591
+ "- Increase timeout in config: contentGenerator.timeout",
1592
+ "- Check network connectivity"
1593
+ ];
1594
+ return `Troubleshooting tips:
1595
+ ${tips.join("\n")}`;
1596
+ }
1597
+ };
1598
+
1599
+ // packages/core/src/core/openaiContentGenerator/openaiContentGenerator.ts
1600
+ var debugLogger5 = createDebugLogger("OPENAI");
1601
+ var OpenAIContentGenerator = class {
1602
+ static {
1603
+ __name(this, "OpenAIContentGenerator");
1604
+ }
1605
+ pipeline;
1606
+ constructor(contentGeneratorConfig, cliConfig, provider) {
1607
+ const pipelineConfig = {
1608
+ cliConfig,
1609
+ provider,
1610
+ contentGeneratorConfig,
1611
+ errorHandler: new EnhancedErrorHandler(
1612
+ (error, request) => this.shouldSuppressErrorLogging(error, request)
1613
+ )
1614
+ };
1615
+ this.pipeline = new ContentGenerationPipeline(pipelineConfig);
1616
+ }
1617
+ /**
1618
+ * Hook for subclasses to customize error handling behavior
1619
+ * @param error The error that occurred
1620
+ * @param request The original request
1621
+ * @returns true if error logging should be suppressed, false otherwise
1622
+ */
1623
+ shouldSuppressErrorLogging(error, request) {
1624
+ if (isAbortError(error) && request.config?.abortSignal?.aborted) {
1625
+ return true;
1626
+ }
1627
+ return false;
1628
+ }
1629
+ async generateContent(request, userPromptId) {
1630
+ return this.pipeline.execute(request, userPromptId);
1631
+ }
1632
+ async generateContentStream(request, userPromptId) {
1633
+ return this.pipeline.executeStream(request, userPromptId);
1634
+ }
1635
+ async countTokens(request) {
1636
+ try {
1637
+ const estimator = new RequestTokenizer();
1638
+ const result = await estimator.calculateTokens(request);
1639
+ return {
1640
+ totalTokens: result.totalTokens
1641
+ };
1642
+ } catch (error) {
1643
+ debugLogger5.warn(
1644
+ "Failed to calculate tokens with new tokenizer, falling back to simple method:",
1645
+ error
1646
+ );
1647
+ const content = JSON.stringify(request.contents);
1648
+ const totalTokens = Math.ceil(content.length / 4);
1649
+ return {
1650
+ totalTokens
1651
+ };
1652
+ }
1653
+ }
1654
+ async embedContent(request) {
1655
+ let text = "";
1656
+ if (Array.isArray(request.contents)) {
1657
+ text = request.contents.map((content) => {
1658
+ if (typeof content === "string") return content;
1659
+ if ("parts" in content && content.parts) {
1660
+ return content.parts.map(
1661
+ (part) => typeof part === "string" ? part : "text" in part ? part.text || "" : ""
1662
+ ).join(" ");
1663
+ }
1664
+ return "";
1665
+ }).join(" ");
1666
+ } else if (request.contents) {
1667
+ if (typeof request.contents === "string") {
1668
+ text = request.contents;
1669
+ } else if ("parts" in request.contents && request.contents.parts) {
1670
+ text = request.contents.parts.map(
1671
+ (part) => typeof part === "string" ? part : "text" in part ? part.text : ""
1672
+ ).join(" ");
1673
+ }
1674
+ }
1675
+ try {
1676
+ const embedding = await this.pipeline.client.embeddings.create({
1677
+ model: "text-embedding-ada-002",
1678
+ // Default embedding model
1679
+ input: text
1680
+ });
1681
+ return {
1682
+ embeddings: [
1683
+ {
1684
+ values: embedding.data[0].embedding
1685
+ }
1686
+ ]
1687
+ };
1688
+ } catch (error) {
1689
+ const redactedError = redactProxyError(error);
1690
+ debugLogger5.error("OpenAI API Embedding Error:", redactedError);
1691
+ throw new Error(
1692
+ `OpenAI API error: ${redactedError instanceof Error ? redactedError.message : String(redactedError)}`
1693
+ );
1694
+ }
1695
+ }
1696
+ useSummarizedThinking() {
1697
+ return false;
1698
+ }
1699
+ };
1700
+
1701
+ // packages/core/src/core/openaiContentGenerator/index.ts
1702
+ function createOpenAIContentGenerator(contentGeneratorConfig, cliConfig) {
1703
+ const provider = determineProvider(contentGeneratorConfig, cliConfig);
1704
+ return new OpenAIContentGenerator(
1705
+ contentGeneratorConfig,
1706
+ cliConfig,
1707
+ provider
1708
+ );
1709
+ }
1710
+ __name(createOpenAIContentGenerator, "createOpenAIContentGenerator");
1711
+ function determineProvider(contentGeneratorConfig, cliConfig) {
1712
+ const config = contentGeneratorConfig || cliConfig.getContentGeneratorConfig();
1713
+ if (DashScopeOpenAICompatibleProvider.isDashScopeProvider(config)) {
1714
+ return new DashScopeOpenAICompatibleProvider(
1715
+ contentGeneratorConfig,
1716
+ cliConfig
1717
+ );
1718
+ }
1719
+ if (DeepSeekOpenAICompatibleProvider.isDeepSeekProvider(config)) {
1720
+ return new DeepSeekOpenAICompatibleProvider(
1721
+ contentGeneratorConfig,
1722
+ cliConfig
1723
+ );
1724
+ }
1725
+ if (ZaiOpenAICompatibleProvider.isZaiProvider(config)) {
1726
+ return new ZaiOpenAICompatibleProvider(contentGeneratorConfig, cliConfig);
1727
+ }
1728
+ if (MiMoOpenAICompatibleProvider.isMiMoProvider(config)) {
1729
+ return new MiMoOpenAICompatibleProvider(contentGeneratorConfig, cliConfig);
1730
+ }
1731
+ if (ModelScopeOpenAICompatibleProvider.isModelScopeProvider(config)) {
1732
+ return new ModelScopeOpenAICompatibleProvider(
1733
+ contentGeneratorConfig,
1734
+ cliConfig
1735
+ );
1736
+ }
1737
+ if (MiniMaxOpenAICompatibleProvider.isMiniMaxProvider(config)) {
1738
+ return new MiniMaxOpenAICompatibleProvider(
1739
+ contentGeneratorConfig,
1740
+ cliConfig
1741
+ );
1742
+ }
1743
+ if (MistralOpenAICompatibleProvider.isMistralProvider(config)) {
1744
+ return new MistralOpenAICompatibleProvider(
1745
+ contentGeneratorConfig,
1746
+ cliConfig
1747
+ );
1748
+ }
1749
+ return new DefaultOpenAICompatibleProvider(contentGeneratorConfig, cliConfig);
1750
+ }
1751
+ __name(determineProvider, "determineProvider");
1752
+
1753
+ export {
1754
+ DeepSeekOpenAICompatibleProvider,
1755
+ MiniMaxOpenAICompatibleProvider,
1756
+ MistralOpenAICompatibleProvider,
1757
+ MiMoOpenAICompatibleProvider,
1758
+ ContentGenerationPipeline,
1759
+ EnhancedErrorHandler,
1760
+ OpenAIContentGenerator,
1761
+ createOpenAIContentGenerator,
1762
+ determineProvider
1763
+ };
1764
+ /**
1765
+ * @license
1766
+ * Copyright 2025 Qwen
1767
+ * SPDX-License-Identifier: Apache-2.0
1768
+ */