@openclaw/ai 2026.10.1-beta.2 → 2026.10.5-beta.1

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 (104) hide show
  1. package/README.md +6 -0
  2. package/dist/{anthropic-CZv6XFER.mjs → anthropic-BAHKgWam.mjs} +30 -59
  3. package/dist/{anthropic-payload-policy-BhCcgNdm.d.mts → anthropic-payload-policy-DGblkHL5.d.mts} +19 -16
  4. package/dist/{anthropic-stream-reducer-BQ1tjd7x.mjs → anthropic-stream-reducer-g6w1YVii.mjs} +197 -285
  5. package/dist/{api-registry-DpU0Rj-I.d.mts → api-registry-BhXXYdRA.d.mts} +2 -2
  6. package/dist/assistant-message-events-CHMJgbhs.mjs +18 -0
  7. package/dist/{assistant-output-OLm-Oln0.mjs → assistant-output-CUEeSotf.mjs} +2 -2
  8. package/dist/{assistant-text-phase-Bl1aDCB7.mjs → assistant-text-phase-BrV8KdFO.mjs} +7 -6
  9. package/dist/{azure-openai-responses-DH7bd3QT.mjs → azure-openai-responses-C7qBuR23.mjs} +8 -9
  10. package/dist/{credential-redaction-BKv49aiv.d.mts → credential-redaction-BHxruEH6.d.mts} +3 -1
  11. package/dist/{diagnostics-Caj-4RBA.mjs → diagnostics-tQasYct1.mjs} +10 -3
  12. package/dist/diagnostics.d.mts +7 -7
  13. package/dist/diagnostics.mjs +3 -3
  14. package/dist/{env-api-keys-CyX4VKGO.mjs → env-api-keys-CrxwdylR.mjs} +24 -35
  15. package/dist/event-stream-6UNs-AhN.d.mts +1 -0
  16. package/dist/{event-stream-DrRM_90c.d.mts → event-stream-BcqWLo_u.d.mts} +1 -1
  17. package/dist/{event-stream-C5AbCAaZ.mjs → event-stream-X2gmsUWv.mjs} +5 -8
  18. package/dist/event-stream.d.mts +2 -2
  19. package/dist/event-stream.mjs +1 -1
  20. package/dist/google-Boz2QcpK.mjs +20 -0
  21. package/dist/{google-interactions-wrnE6MLw.mjs → google-interactions-tjAb1mc5.mjs} +52 -84
  22. package/dist/{google-messages-BwPPycw6.mjs → google-messages-C-RZzloH.mjs} +25 -35
  23. package/dist/google-provider-stream-CMImyxbX.mjs +58 -0
  24. package/dist/{google-shared-Eql1OMPU.mjs → google-shared-OyAtMTZv.mjs} +6 -15
  25. package/dist/google-vertex-hCxTJea7.mjs +40 -0
  26. package/dist/{host-BinBeGcQ.d.mts → host-CTDz7LXD.d.mts} +6 -3
  27. package/dist/{host-DCW52iHe.mjs → host-JWXZWa-c.mjs} +12 -8
  28. package/dist/{host-policy-B3jc4Rpn.mjs → host-policy-q4Azym9F.mjs} +1 -1
  29. package/dist/{index-a8e_oKnu.d.mts → index-BYDvopUB.d.mts} +27 -6
  30. package/dist/index.d.mts +7 -7
  31. package/dist/index.mjs +7 -6
  32. package/dist/internal/anthropic.d.mts +6 -6
  33. package/dist/internal/anthropic.mjs +5 -5
  34. package/dist/internal/openai-completions-compat.d.mts +1 -1
  35. package/dist/internal/openai-completions-compat.mjs +1 -1
  36. package/dist/internal/openai-responses-payload-policy.d.mts +8 -1
  37. package/dist/internal/openai-responses-payload-policy.mjs +3 -3
  38. package/dist/internal/openai.d.mts +9 -9
  39. package/dist/internal/openai.mjs +9 -11
  40. package/dist/internal/runtime.d.mts +14 -11
  41. package/dist/internal/runtime.mjs +10 -13
  42. package/dist/internal/shared.d.mts +5 -5
  43. package/dist/internal/shared.mjs +6 -7
  44. package/dist/internal/tool-schema.mjs +2 -2
  45. package/dist/{llm-request-activity-D8f_oxHj.mjs → llm-request-activity-CfMuh5cC.mjs} +2 -2
  46. package/dist/{mistral-DSt4vQAO.mjs → mistral-DUveP4hF.mjs} +71 -146
  47. package/dist/{model-transport-url-C5f59L3Y.d.mts → model-transport-url-BrsjjdjR.d.mts} +5 -1
  48. package/dist/{number-coercion-V8-BhME4.mjs → number-coercion-C3hztWoY.mjs} +5 -4
  49. package/dist/{openai-chatgpt-responses-BC2x0Oqx.mjs → openai-chatgpt-responses-OCwckGhY.mjs} +173 -242
  50. package/dist/{openai-completions-DNyHnXKg.mjs → openai-completions-DOltBUe9.mjs} +7 -6
  51. package/dist/{openai-completions-compat-BYdYpXhU.d.mts → openai-completions-compat-i-nLZxFy.d.mts} +10 -11
  52. package/dist/{openai-completions-compat-BdmlhD4u.mjs → openai-completions-compat-pg1q4uNG.mjs} +10 -6
  53. package/dist/openai-completions-transport-OMrn7gnZ.mjs +3319 -0
  54. package/dist/{openai-prompt-cache-I8YQTzf8.mjs → openai-prompt-cache-Cr2xKVrS.mjs} +1 -1
  55. package/dist/{openai-prompt-cache-45-AH_xv.d.mts → openai-prompt-cache-DIPMrMFy.d.mts} +2 -2
  56. package/dist/{openai-responses-uZ5oWSp7.mjs → openai-responses-BSBJUA19.mjs} +9 -10
  57. package/dist/{openai-responses-compaction-replay-BClApUIf.d.mts → openai-responses-compaction-replay-B2FE4Nw8.d.mts} +4 -4
  58. package/dist/{openai-responses-contracts-Br5IIw_h.d.mts → openai-responses-contracts-DKqMJ1Ct.d.mts} +9 -4
  59. package/dist/{openai-responses-contracts-BKu8O7l4.mjs → openai-responses-contracts-DlKMMTxK.mjs} +26 -6
  60. package/dist/{openai-responses-compaction-window-m878aVpU.mjs → openai-responses-endpoint-CinGh4VZ.mjs} +14 -4
  61. package/dist/{openai-responses-prompt-observer-internal-CCg7B6l4.mjs → openai-responses-request-lifecycle-D2IFiI0-.mjs} +39 -4
  62. package/dist/{openai-responses-shared-C7oVyjED.mjs → openai-responses-shared-DVGhq5xF.mjs} +1036 -327
  63. package/dist/{openai-responses-terminal-usage-DgImcyJu.d.mts → openai-responses-terminal-usage-BygjiAdi.d.mts} +2 -2
  64. package/dist/{openai-tool-schema-DYNePiF8.mjs → openai-tool-schema-DDPXsvJP.mjs} +236 -399
  65. package/dist/{positive-integer-CwLi8LUL.mjs → positive-integer-CqWjqSi9.mjs} +1 -1
  66. package/dist/{provider-error-DQBE1bUQ.mjs → provider-error-CF9GWCs2.mjs} +5 -3
  67. package/dist/{provider-options-D5ABCV_l.d.mts → provider-options-CVrXZBtA.d.mts} +3 -2
  68. package/dist/{provider-transport-turn-state-_M2kLdUE.mjs → provider-transport-turn-state-LuCaTsSO.mjs} +2 -2
  69. package/dist/{provider-types-F8Mqc9Ma.d.mts → provider-types-CHtRx0dI.d.mts} +3 -3
  70. package/dist/provider-types.d.mts +6 -6
  71. package/dist/providers.d.mts +2 -2
  72. package/dist/providers.mjs +12 -12
  73. package/dist/{reasoning-tag-text-partitioner-Ju9VpUGl.mjs → reasoning-tag-text-partitioner-0-Rsezog.mjs} +13 -12
  74. package/dist/{required-api-key-D4kn3252.mjs → required-api-key-CFJvcFy_.mjs} +1 -1
  75. package/dist/{simple-options-BkKTXxhp.mjs → simple-options-BARFAzk9.mjs} +9 -11
  76. package/dist/{src-B0NIt2UK.mjs → src-C_wfIUmV.mjs} +35 -10
  77. package/dist/{stream-DSdzKsDK.mjs → stream-DwrETW0_.mjs} +12 -21
  78. package/dist/{stream-first-event-timeout-D9ZmL_s9.mjs → stream-first-event-timeout-Dc42hKv6.mjs} +8 -21
  79. package/dist/{streaming-byte-guard-CC-HMn_u.mjs → streaming-byte-guard-DBXr1XYn.mjs} +16 -1
  80. package/dist/{string-normalization-PN10hf6d.mjs → string-normalization-I3ux50s9.mjs} +1 -1
  81. package/dist/{text-signature-MLwfi6fq.mjs → text-signature-B_MB6U_9.mjs} +32 -5
  82. package/dist/{tool-schema-json-projection-Ct8SeBeJ.mjs → tool-schema-json-projection-B8sG1I9v.mjs} +29 -23
  83. package/dist/{transport-stream-shared-BcAKh2XH.d.mts → transport-stream-shared-BmpzDtdQ.d.mts} +4 -4
  84. package/dist/{transport-stream-shared-CxXXz_Dd.mjs → transport-stream-shared-DH08b7Il.mjs} +31 -20
  85. package/dist/transports.d.mts +33 -22
  86. package/dist/transports.mjs +73 -1234
  87. package/dist/{types-DvzmWW8J.d.mts → types-Bd2T86VJ.d.mts} +5 -23
  88. package/dist/types-Kdfw_04V.d.mts +1 -0
  89. package/dist/types.d.mts +6 -6
  90. package/dist/types.mjs +5 -4
  91. package/dist/{validation-B3lQt_VE.d.mts → validation-DdGB1yy3.d.mts} +1 -1
  92. package/dist/{validation-DbqlIfDn.mjs → validation-opC2pjk9.mjs} +2 -3
  93. package/dist/validation.d.mts +1 -1
  94. package/dist/validation.mjs +1 -1
  95. package/package.json +4 -4
  96. package/dist/event-stream-DqDQWqiy.d.mts +0 -1
  97. package/dist/google-1WLOtb8w.mjs +0 -54
  98. package/dist/google-vertex-0LrlRoO3.mjs +0 -88
  99. package/dist/openai-completions-transport-DWM2mQDB.mjs +0 -2081
  100. package/dist/openai-responses-request-lifecycle-Fsag-IGZ.mjs +0 -36
  101. package/dist/openai-transport-params-JFsnIyto.mjs +0 -735
  102. package/dist/openai-web-search-tools-Cfu0uLyN.mjs +0 -14
  103. package/dist/provider-replay-context-C7Bmi84a.mjs +0 -25
  104. package/dist/types-BuiEBM3u.d.mts +0 -1
@@ -0,0 +1,3319 @@
1
+ import { _ as runtimeContextContentToText, f as hasRuntimeContextMarker, p as isRuntimeContextMessage } from "./types-CQc6JNG4.mjs";
2
+ import { r as appendAssistantThinking } from "./event-stream-X2gmsUWv.mjs";
3
+ import { i as asOptionalRecord, o as isRecord, r as asOptionalObjectRecord } from "./record-coerce-CNDovvft.mjs";
4
+ import { s as readNonEmptyStringPreservingWhitespace, t as hasNonEmptyString } from "./string-coerce-CtgOVOSv.mjs";
5
+ import { n as asNonNegativeFiniteNumber, r as asPositiveFiniteNumber } from "./number-coercion-C3hztWoY.mjs";
6
+ import { N as isImageWithMediaPayload, a as resolveAiTransportHeaderSentinels, r as getAiTransportHost } from "./host-JWXZWa-c.mjs";
7
+ import { M as resolveOpenAIThinkingApi, S as isOpenAIGpt6Model, _ as isResponsesServiceTierRejection, b as isOpenAIGpt55Model, d as OpenAIResponsesWebSocketSafeRetryError, g as responsesServiceTierObserver, k as supportsOpenAITemperature, l as OpenAIResponsesWebSocketPostDispatchError, m as parseOpenAIResponsesWebSocketServerError, r as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, t as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, u as OpenAIResponsesWebSocketPreDispatchError, v as nextResponsesServiceTier, x as isOpenAIGpt56Model, y as isOpenAIGpt54MiniModel } from "./openai-responses-contracts-DlKMMTxK.mjs";
8
+ import { r as uniqueStrings } from "./string-normalization-I3ux50s9.mjs";
9
+ import { n as getSessionResourceOwnerId, r as registerSessionResourceCleanup } from "./session-resources-D1wgDQ8C.mjs";
10
+ import { c as stableStringify, s as truncateUtf16Safe } from "./provider-error-CF9GWCs2.mjs";
11
+ import { r as resolveOpenAIPromptCacheParams } from "./openai-prompt-cache-Cr2xKVrS.mjs";
12
+ import { d as hasOnlyResponsesFunctionTools, i as emitModelTransportError, r as emitModelTransportDebug, t as formatModelTransportDebugBaseUrl } from "./diagnostics-tQasYct1.mjs";
13
+ import { f as supportsModelTools, i as providerReplayContextMatches, n as buildProviderReplayContext, o as estimateStringChars, r as isProviderReplayContext } from "./text-signature-B_MB6U_9.mjs";
14
+ import { c as resolveOpenAIResponsesTextFormat, i as resolveOpenAICompletionsCompat, l as shouldOmitOllamaCompatResponseFormat, n as isNativeOpenAIEndpoint, o as usesNativeOpenAICodexResponsesBackend, r as isOpenAICodexResponsesModel, s as resolveOpenAICompletionsResponseFormat, t as detectOpenAICompletionsCompat } from "./openai-completions-compat-pg1q4uNG.mjs";
15
+ import { i as resolveProviderEndpoint, t as buildGuardedModelFetch } from "./host-policy-q4Azym9F.mjs";
16
+ import { i as resolveCacheRetention, r as buildCopilotDynamicHeaders } from "./cloudflare-5qSGvIUH.mjs";
17
+ import { E as createToolArgumentPreviewSchedule, M as racePromiseWithAbortSignal, O as parseStreamingJson, P as sanitizeSurrogates, S as withProviderResponseHook, b as transportAbortError, c as failTransportStream, d as iterateModelStream, h as notifyProviderStreamOpened, l as finalizeTerminalToolCallArguments, s as createWritableTransportEventStream, u as finalizeTransportStream, v as sanitizeTransportPayloadText, y as throwIfModelStreamAborted } from "./transport-stream-shared-DH08b7Il.mjs";
18
+ import { C as stripSystemPromptCacheBoundary, D as sortPromptCacheToolsByName, S as splitSystemPromptRelocatableBoundary, d as describeToolResultMediaPlaceholder, l as codeModeToolSurfaceObserver, m as extractToolResultText, s as prepareModelRequestBody, u as reasoningTagTextPolicy, w as stripSystemPromptRelocatableBoundary, x as splitSystemPromptCacheBoundary } from "./simple-options-BARFAzk9.mjs";
19
+ import { $ as replayResponsesReasoningUpdates, A as summarizeResponsesPayload, At as measureUtf8AppendBytes, B as responsesInputFingerprint, C as readResponsesRecoveryEvent, Ct as toOpenAIResponsesToolChoice, D as logResponsesFailedNoDetails, Dt as createResponseModelTracker, E as ResponsesStreamFailure, Ft as resolvePromptCacheKey, G as responsesContinuationRequestFingerprint, H as recordResponsesContinuationState, I as convertResponsesMessages, It as redactIdentifier, Lt as sha256Hex, Mt as readOpenAICompletionsContentDeltas, Nt as readOpenAICompletionsReasoningBatch, O as safeDebugValue, Ot as isOpenAICompletionsThinkingEnabled, P as buildResponsesInputMessage, Pt as resolveOpenAIClientBaseUrl, Q as isConfigurationUpdate, R as readResponsesInputReplayState, S as isInvalidEncryptedContentError, St as reconcileOpenAIResponsesToolChoice, T as resolveNextResponsesEncryptedContentAttempt, Tt as createOpenAIProviderAcceptanceHook, U as resolveResponsesContinuationRequest, V as claimOpenAIResponsesHttpContinuation, W as responsesContinuationPrefixFingerprint, _t as resolveOpenAISimpleReasoningEffort, a as prepareResponsesTools, at as suppressOpenAIResponsesCompaction, bt as projectOpenAITools, c as observeResponsesStream, ct as buildOpenAISdkClientOptions, et as supportsResponsesReasoningUpdate, ft as getCompat, gt as resolveOpenAIRequestReasoning, ht as resolveOpenAIStrictToolFlagWithDiagnostics, j as recordResponsesContextUsage, jt as parseOpenAICompletionsUsage, k as summarizeOpenAITransportError, kt as log, lt as buildOpenAISdkRequestOptions, mt as resolveCodeModeResponsesVisibleToolNames, n as applyResponsesServiceTierPricing, nt as buildOpenAIResponsesReasoningReplayMetadata, ot as assertCodeModeResponsesToolSurface, s as processResponsesStream, st as buildOpenAIClientHeaders, ut as enforceCodeModeResponsesToolSurface, w as resolveAzureOpenAIApiVersion, wt as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, x as createResponsesStreamWithRecovery, xt as reconcileOpenAICompletionsToolChoice, yt as prepareOpenAITools, z as recordResponsesInputReplay } from "./openai-responses-shared-DVGhq5xF.mjs";
20
+ import { n as getEnvApiKey } from "./env-api-keys-CrxwdylR.mjs";
21
+ import { n as transformProviderMessages, t as createAssistantOutput } from "./assistant-output-CUEeSotf.mjs";
22
+ import { a as tagUnresolvedTextAsCommentary, i as tagPendingCommentaryText, n as rememberPendingCommentaryTags, r as tagInterruptedTextPhases, t as clearPendingCommentaryText } from "./assistant-text-phase-BrV8KdFO.mjs";
23
+ import { t as notifyLlmRequestActivity } from "./llm-request-activity-CfMuh5cC.mjs";
24
+ import { n as resolveOpencodeSessionHeaders } from "./session-affinity-DA4WHv1h.mjs";
25
+ import "./types.mjs";
26
+ import { i as createOpenAIProviderClient, n as resolveProviderSimpleCompletionHeaders, r as resolveProviderTransportTurnState, t as filterProviderTurnHeadersForExplicitOpencodeSession } from "./provider-transport-turn-state-LuCaTsSO.mjs";
27
+ import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-Dc42hKv6.mjs";
28
+ import { t as boundResponseBody } from "./streaming-byte-guard-DBXr1XYn.mjs";
29
+ import { f as withPreparedToolSchemaNormalization, r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-DDPXsvJP.mjs";
30
+ import { isGoogleGemini3ThinkingLevelModel } from "./internal/google-model-family.mjs";
31
+ import { t as mapOpenAIStopReason } from "./openai-stop-reason-8s70vDXj.mjs";
32
+ import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-0-Rsezog.mjs";
33
+ import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-D0dvJMbZ.mjs";
34
+ import { i as isOpenAIResponsesCompactionOutput, l as resolveOpenAIResponsesPayloadPolicy, n as supportsNativeOpenAIResponsesEndpoint, o as sanitizeResponsesImagePayload, r as createBoundedOpenAIResponsesCompactionFetch, s as applyOpenAIResponsesPayloadPolicy, t as isOfficialOpenAIResponsesBaseUrl } from "./openai-responses-endpoint-CinGh4VZ.mjs";
35
+ import { n as withResponsesRequestAcceptance, r as createResponsesPromptEgressObserver, t as responsesRequestLifecycle } from "./openai-responses-request-lifecycle-D2IFiI0-.mjs";
36
+ import { randomUUID } from "node:crypto";
37
+ import OpenAI, { AzureOpenAI } from "openai";
38
+ import { ResponsesWS } from "openai/resources/responses/ws.js";
39
+ //#region packages/ai/src/transports/deepseek-dsml-grammar.ts
40
+ const DEEPSEEK_DSML_MARKERS = [
41
+ "|",
42
+ "|",
43
+ "||"
44
+ ].map((bar) => `${bar}DSML${bar}`);
45
+ const DEEPSEEK_DSML_MARKER_PATTERN = `(${DEEPSEEK_DSML_MARKERS.map((marker) => marker.replaceAll("|", "\\|")).join("|")})`;
46
+ function findEarliestDsmlToken(text, tokens, fromIndex = 0) {
47
+ let best = null;
48
+ for (const token of tokens) {
49
+ const index = text.indexOf(token, fromIndex);
50
+ if (index !== -1 && (!best || index < best.index)) best = {
51
+ index,
52
+ token
53
+ };
54
+ }
55
+ return best;
56
+ }
57
+ function longestDsmlTokenPrefixSuffixLength(text, tokens, maxTokenLength) {
58
+ const maxLength = Math.min(text.length, maxTokenLength - 1);
59
+ for (let length = maxLength; length > 0; length--) {
60
+ const suffix = text.slice(text.length - length);
61
+ if (tokens.some((token) => token.startsWith(suffix))) return length;
62
+ }
63
+ return 0;
64
+ }
65
+ //#endregion
66
+ //#region packages/ai/src/transports/deepseek-text-filter.ts
67
+ /**
68
+ * DeepSeek DSML streaming text filter.
69
+ * Removes provider-emitted DSML tool markup while buffering split tag prefixes
70
+ * across streamed chunks.
71
+ */
72
+ const DSML_KINDS = [
73
+ "tool_use_error",
74
+ "tool_calls",
75
+ "tool_call",
76
+ "function_calls"
77
+ ];
78
+ const DSML_OPEN_TOKENS = DEEPSEEK_DSML_MARKERS.flatMap((marker) => DSML_KINDS.map((kind) => `<${marker}${kind}>`));
79
+ const MAX_OPEN_TOKEN_LEN = Math.max(...DSML_OPEN_TOKENS.map((token) => token.length));
80
+ /** Create an incremental text filter that strips DeepSeek DSML tool blocks. */
81
+ function createDeepSeekTextFilter() {
82
+ let buffer = "";
83
+ let closeToken;
84
+ const consume = (final) => {
85
+ const output = [];
86
+ const emit = (text) => {
87
+ if (text) output.push(text);
88
+ };
89
+ while (buffer) {
90
+ if (closeToken) {
91
+ const closeIndex = buffer.indexOf(closeToken);
92
+ if (closeIndex !== -1) {
93
+ buffer = buffer.slice(closeIndex + closeToken.length);
94
+ closeToken = void 0;
95
+ continue;
96
+ }
97
+ const keep = final ? 0 : Math.min(buffer.length, closeToken.length - 1);
98
+ buffer = buffer.slice(buffer.length - keep);
99
+ if (final) closeToken = void 0;
100
+ return output;
101
+ }
102
+ const open = findEarliestDsmlToken(buffer, DSML_OPEN_TOKENS);
103
+ if (open) {
104
+ emit(buffer.slice(0, open.index));
105
+ buffer = buffer.slice(open.index + open.token.length);
106
+ closeToken = open.token.replace("<", "</");
107
+ continue;
108
+ }
109
+ if (final) {
110
+ emit(buffer);
111
+ buffer = "";
112
+ return output;
113
+ }
114
+ const keep = longestDsmlTokenPrefixSuffixLength(buffer, DSML_OPEN_TOKENS, MAX_OPEN_TOKEN_LEN);
115
+ const emitLength = buffer.length - keep;
116
+ if (emitLength <= 0) return output;
117
+ emit(buffer.slice(0, emitLength));
118
+ buffer = buffer.slice(emitLength);
119
+ return output;
120
+ }
121
+ return output;
122
+ };
123
+ return {
124
+ push(chunk) {
125
+ buffer += chunk;
126
+ return consume(false);
127
+ },
128
+ flush() {
129
+ return consume(true);
130
+ }
131
+ };
132
+ }
133
+ //#endregion
134
+ //#region packages/ai/src/transports/model-max-tokens-params.ts
135
+ /**
136
+ * Max-token parameter normalization across provider/native naming variants.
137
+ * Callers canonicalize aliases before dispatch so payloads cannot carry
138
+ * conflicting limits.
139
+ */
140
+ const MAX_TOKENS_PARAM_KEYS = [
141
+ "maxTokens",
142
+ "max_completion_tokens",
143
+ "max_tokens"
144
+ ];
145
+ /** Resolve the first supported max-token parameter present in a params object. */
146
+ function resolveMaxTokensParam(params) {
147
+ if (!params) return;
148
+ for (const key of MAX_TOKENS_PARAM_KEYS) {
149
+ const resolved = asNonNegativeFiniteNumber(params[key]);
150
+ if (resolved !== void 0) return resolved;
151
+ }
152
+ }
153
+ /**
154
+ * Canonicalize merged params to `maxTokens`, preserving source precedence from
155
+ * left to right across the provided source objects.
156
+ */
157
+ function canonicalizeMaxTokensParam(params) {
158
+ let resolved;
159
+ for (const source of params.sources) {
160
+ const sourceValue = resolveMaxTokensParam(source);
161
+ if (sourceValue !== void 0) resolved = sourceValue;
162
+ }
163
+ if (resolved === void 0) return;
164
+ for (const key of MAX_TOKENS_PARAM_KEYS) delete params.merged[key];
165
+ params.merged.maxTokens = resolved;
166
+ }
167
+ //#endregion
168
+ //#region packages/ai/src/transports/openai-compatible-conversation-turn.ts
169
+ function hasNonEmptyContentPart(part) {
170
+ if (!part || typeof part !== "object") return false;
171
+ const record = part;
172
+ if (record.type === "text") return hasNonEmptyString(record.text);
173
+ return true;
174
+ }
175
+ function hasNonEmptyMessageContent(content) {
176
+ if (hasNonEmptyString(content)) return true;
177
+ if (!Array.isArray(content)) return false;
178
+ return content.some(hasNonEmptyContentPart);
179
+ }
180
+ /** Returns whether an OpenAI-compatible messages payload contains a usable turn. */
181
+ function hasOpenAICompatibleConversationTurn(messages) {
182
+ if (!Array.isArray(messages)) return false;
183
+ return messages.some((message) => {
184
+ if (!message || typeof message !== "object") return false;
185
+ const record = message;
186
+ if (record.role === "user") return hasNonEmptyMessageContent(record.content);
187
+ if (record.role === "assistant") {
188
+ if (hasNonEmptyMessageContent(record.content)) return true;
189
+ const toolCalls = record.tool_calls;
190
+ return Array.isArray(toolCalls) && toolCalls.some((toolCall) => toolCall && typeof toolCall === "object");
191
+ }
192
+ return false;
193
+ });
194
+ }
195
+ //#endregion
196
+ //#region packages/ai/src/transports/openai-completions-cache-control.ts
197
+ const shapedPayloads = /* @__PURE__ */ new WeakSet();
198
+ function resolveCompletionsCacheControl(compat, retention, openRouterRoute) {
199
+ if (compat.cacheControlFormat !== "anthropic" || retention === "none") return;
200
+ return {
201
+ type: "ephemeral",
202
+ ...retention === "long" && compat.supportsLongCacheRetention && (openRouterRoute || compat.configuredSupportsLongCacheRetention === true) ? { ttl: "1h" } : {}
203
+ };
204
+ }
205
+ /** Shared Chat Completions policy; repeated wrapper application preserves existing checkpoints. */
206
+ function applyCompletionsAnthropicCacheControl(payload, cacheControl = { type: "ephemeral" }, cacheOptOutIndexes = /* @__PURE__ */ new Set(), markTools = true, markMessages = true) {
207
+ if (shapedPayloads.has(payload)) return;
208
+ shapedPayloads.add(payload);
209
+ const messages = Array.isArray(payload.messages) ? payload.messages : [];
210
+ const tools = Array.isArray(payload.tools) ? payload.tools.filter(isRecord) : [];
211
+ const blocks = messages.filter(isRecord).flatMap((message) => Array.isArray(message.content) ? message.content.filter(isRecord) : []);
212
+ for (const block of [...tools, ...blocks]) delete block.cache_control;
213
+ if (!cacheControl) return;
214
+ const markText = (message, splitBoundary) => {
215
+ if (typeof message.content === "string" && message.content) message.content = [{
216
+ type: "text",
217
+ text: message.content
218
+ }];
219
+ const content = message.content;
220
+ if (!Array.isArray(content)) return false;
221
+ for (let i = content.length - 1; i >= 0; i--) {
222
+ const block = content[i];
223
+ if (!isRecord(block) || block.type !== "text" || typeof block.text !== "string" || !block.text) continue;
224
+ const split = splitBoundary ? splitSystemPromptCacheBoundary(block.text) : void 0;
225
+ if (split) {
226
+ content.splice(i, 1, ...split.stablePrefix ? [{
227
+ type: "text",
228
+ text: split.stablePrefix,
229
+ cache_control: cacheControl
230
+ }] : [], ...split.dynamicSuffix ? [{
231
+ type: "text",
232
+ text: split.dynamicSuffix
233
+ }] : []);
234
+ if (!split.stablePrefix) return false;
235
+ } else block.cache_control = cacheControl;
236
+ return true;
237
+ }
238
+ return false;
239
+ };
240
+ const lastTool = tools.at(-1);
241
+ if (markTools && lastTool) lastTool.cache_control = cacheControl;
242
+ if (!markMessages) return;
243
+ const system = messages.find((message) => isRecord(message) && (message.role === "system" || message.role === "developer"));
244
+ if (isRecord(system)) markText(system, true);
245
+ for (let i = messages.length - 1; i >= 0; i--) {
246
+ const message = messages[i];
247
+ if (!cacheOptOutIndexes.has(i) && isRecord(message) && (message.role === "user" || message.role === "tool") && markText(message, false)) return;
248
+ }
249
+ }
250
+ //#endregion
251
+ //#region packages/ai/src/transports/openai-completions-string-content.ts
252
+ /**
253
+ * OpenAI Chat Completions compatibility helpers. Some providers only accept
254
+ * role/content messages with plain string content instead of text block arrays.
255
+ */
256
+ /** Flatten string-only text block content arrays into newline-joined strings. */
257
+ function flattenCompletionMessagesToStringContent(messages) {
258
+ return messages.map((message) => {
259
+ const record = asOptionalObjectRecord(message);
260
+ const content = record?.content;
261
+ if (!Array.isArray(content)) return message;
262
+ const textParts = [];
263
+ for (const item of content) {
264
+ const part = asOptionalObjectRecord(item);
265
+ if (part?.type !== "text" || typeof part.text !== "string") return message;
266
+ textParts.push(part.text);
267
+ }
268
+ return {
269
+ ...record,
270
+ content: textParts.join("\n")
271
+ };
272
+ });
273
+ }
274
+ /** Strip completion messages to role/content fields for strict providers. */
275
+ function stripCompletionMessagesToRoleContent(messages) {
276
+ return messages.map((message) => {
277
+ const record = asOptionalRecord(message);
278
+ if (!record) return message;
279
+ return {
280
+ ...Object.hasOwn(record, "role") ? { role: record.role } : {},
281
+ ...Object.hasOwn(record, "content") ? { content: record.content } : {}
282
+ };
283
+ });
284
+ }
285
+ //#endregion
286
+ //#region packages/ai/src/providers/openai-completions-tool-calls.ts
287
+ const MAX_BUFFERED_TOOL_CALL_ARGUMENT_BYTES = 256e3;
288
+ const MAX_BUFFERED_LEGACY_FOLLOWING_DELTA_BYTES = 256e3;
289
+ const MAX_BUFFERED_LEGACY_FOLLOWING_DELTAS = 1024;
290
+ /** Keep encrypted provider reasoning attached to the first matching tool call. */
291
+ function createOpenAIEncryptedToolCallReasoningTracker() {
292
+ const firstBlocks = /* @__PURE__ */ new Map();
293
+ const pendingDetails = /* @__PURE__ */ new Map();
294
+ return {
295
+ rememberToolCall(id, block, previousId = id) {
296
+ const previous = firstBlocks.get(previousId);
297
+ if (previousId !== id && previous?.block === block) {
298
+ firstBlocks.delete(previousId);
299
+ if (previous.signature && block.thoughtSignature === previous.signature) delete block.thoughtSignature;
300
+ }
301
+ if (!id || firstBlocks.has(id)) return;
302
+ const pendingDetail = pendingDetails.get(id);
303
+ firstBlocks.set(id, {
304
+ block,
305
+ signature: pendingDetail
306
+ });
307
+ if (pendingDetail) {
308
+ block.thoughtSignature = pendingDetail;
309
+ pendingDetails.delete(id);
310
+ }
311
+ },
312
+ consumeDetails(details) {
313
+ if (!Array.isArray(details)) return;
314
+ for (const detail of details) {
315
+ if (!isRecord(detail) || detail.type !== "reasoning.encrypted" || typeof detail.id !== "string" || detail.id.length === 0 || typeof detail.data !== "string" || detail.data.length === 0) continue;
316
+ const serializedDetail = JSON.stringify(detail);
317
+ const matchingBlock = firstBlocks.get(detail.id);
318
+ if (matchingBlock) {
319
+ matchingBlock.signature = serializedDetail;
320
+ matchingBlock.block.thoughtSignature = serializedDetail;
321
+ } else pendingDetails.set(detail.id, serializedDetail);
322
+ }
323
+ }
324
+ };
325
+ }
326
+ function hasObservableContent(value) {
327
+ if (typeof value === "string") return value.length > 0;
328
+ if (Array.isArray(value)) return value.some(hasObservableContent);
329
+ if (isRecord(value)) return Object.entries(value).some(([field, nestedValue]) => field !== "type" && field !== "id" && field !== "index" && hasObservableContent(nestedValue));
330
+ return false;
331
+ }
332
+ function hasOpenAICompletionsDeltaContent(delta) {
333
+ return Object.entries(delta).some(([field, value]) => field !== "role" && hasObservableContent(value));
334
+ }
335
+ /** Normalize the SDK's legacy single-function lane into its modern tool-call shape. */
336
+ function createOpenAICompletionsToolCallDeltaNormalizer() {
337
+ let sawModernToolCall = false;
338
+ let pendingLegacyArgumentBytes = 0;
339
+ let pendingFollowingDeltaBytes = 0;
340
+ let pendingLegacyToolCall;
341
+ const modernArguments = /* @__PURE__ */ new Map();
342
+ const pendingFollowingDeltas = [];
343
+ const takePendingFollowingDeltas = () => {
344
+ pendingFollowingDeltaBytes = 0;
345
+ return pendingFollowingDeltas.splice(0).map((delta) => ({
346
+ delta,
347
+ toolCalls: []
348
+ }));
349
+ };
350
+ const bufferFollowingDelta = (delta) => {
351
+ const nextDeltaBytes = Buffer.byteLength(JSON.stringify(delta), "utf8");
352
+ if (pendingFollowingDeltaBytes + nextDeltaBytes > MAX_BUFFERED_LEGACY_FOLLOWING_DELTA_BYTES || pendingFollowingDeltas.length >= MAX_BUFFERED_LEGACY_FOLLOWING_DELTAS) throw new Error("Exceeded legacy tool-call content buffer limit");
353
+ pendingFollowingDeltaBytes += nextDeltaBytes;
354
+ pendingFollowingDeltas.push(delta);
355
+ };
356
+ return (delta, finishReason) => {
357
+ const ordinaryDelta = { ...delta };
358
+ delete ordinaryDelta.function_call;
359
+ delete ordinaryDelta.tool_calls;
360
+ if (delta.tool_calls && delta.tool_calls.length > 0) {
361
+ for (const toolCall of delta.tool_calls) {
362
+ const index = typeof toolCall.index === "number" ? toolCall.index : void 0;
363
+ const state = (index === void 0 ? void 0 : modernArguments.get(index)) ?? (toolCall.id ? modernArguments.get(toolCall.id) : void 0) ?? {
364
+ bytes: 0,
365
+ pendingHighSurrogate: false
366
+ };
367
+ if (index !== void 0) modernArguments.set(index, state);
368
+ if (toolCall.id) modernArguments.set(toolCall.id, state);
369
+ const argumentDelta = toolCall.function?.arguments ?? "";
370
+ const append = measureUtf8AppendBytes(state.pendingHighSurrogate, argumentDelta);
371
+ state.bytes += append.bytes;
372
+ if (state.bytes > MAX_BUFFERED_TOOL_CALL_ARGUMENT_BYTES) throw new Error("Exceeded tool-call argument buffer limit");
373
+ state.pendingHighSurrogate = append.endsWithHighSurrogate;
374
+ }
375
+ const precedingDeltas = takePendingFollowingDeltas();
376
+ sawModernToolCall = true;
377
+ pendingLegacyArgumentBytes = 0;
378
+ pendingLegacyToolCall = void 0;
379
+ return [...precedingDeltas, {
380
+ delta: ordinaryDelta,
381
+ toolCalls: delta.tool_calls
382
+ }];
383
+ }
384
+ const functionCall = delta.function_call;
385
+ if (sawModernToolCall || !functionCall && !pendingLegacyToolCall) return [{
386
+ delta: ordinaryDelta,
387
+ toolCalls: []
388
+ }];
389
+ const hadPendingLegacyCall = pendingLegacyToolCall !== void 0;
390
+ const leadingDeltas = [];
391
+ if (hasOpenAICompletionsDeltaContent(ordinaryDelta)) {
392
+ if (hadPendingLegacyCall) bufferFollowingDelta(ordinaryDelta);
393
+ else leadingDeltas.push({
394
+ delta: ordinaryDelta,
395
+ toolCalls: []
396
+ });
397
+ }
398
+ if (functionCall && (functionCall.name || functionCall.arguments)) {
399
+ const nextFunctionName = pendingLegacyToolCall?.function?.name ? void 0 : functionCall.name;
400
+ const nextArgumentBytes = Buffer.byteLength(functionCall.arguments ?? "", "utf8") + Buffer.byteLength(nextFunctionName ?? "", "utf8");
401
+ if (pendingLegacyArgumentBytes + nextArgumentBytes > MAX_BUFFERED_TOOL_CALL_ARGUMENT_BYTES) throw new Error("Exceeded tool-call argument buffer limit");
402
+ pendingLegacyArgumentBytes += nextArgumentBytes;
403
+ pendingLegacyToolCall ??= {
404
+ index: 0,
405
+ id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
406
+ type: "function",
407
+ function: {}
408
+ };
409
+ const pendingFunction = pendingLegacyToolCall.function ??= {};
410
+ if (nextFunctionName) pendingFunction.name = nextFunctionName;
411
+ if (functionCall.arguments) pendingFunction.arguments = (pendingFunction.arguments ?? "") + functionCall.arguments;
412
+ }
413
+ if (!pendingLegacyToolCall) return leadingDeltas.length > 0 ? leadingDeltas : [{
414
+ delta: ordinaryDelta,
415
+ toolCalls: []
416
+ }];
417
+ if (finishReason === "function_call") {
418
+ const confirmedLegacyToolCall = pendingLegacyToolCall;
419
+ pendingLegacyToolCall = void 0;
420
+ pendingLegacyArgumentBytes = 0;
421
+ return [
422
+ ...leadingDeltas,
423
+ {
424
+ delta: {},
425
+ toolCalls: [confirmedLegacyToolCall]
426
+ },
427
+ ...takePendingFollowingDeltas()
428
+ ];
429
+ }
430
+ if (finishReason) {
431
+ pendingLegacyArgumentBytes = 0;
432
+ pendingLegacyToolCall = void 0;
433
+ return [...leadingDeltas, ...takePendingFollowingDeltas()];
434
+ }
435
+ return leadingDeltas;
436
+ };
437
+ }
438
+ /** Publish only executable calls; streaming scratch state never belongs in replay. */
439
+ function finalizeOpenAICompletionsToolCalls(output, options = {}) {
440
+ const isToolCall = (block) => block.type === "toolCall";
441
+ const hasToolCalls = output.content.some(isToolCall);
442
+ if (output.stopReason === "toolUse" && !hasToolCalls) output.stopReason = "stop";
443
+ if (output.stopReason === "stop" && hasToolCalls && options.allowSilentToolCallPromotion !== false && !output.content.some((block) => block.type === "text" && block.text.trim().length > 0)) output.stopReason = "toolUse";
444
+ if (output.stopReason !== "toolUse") {
445
+ if (hasToolCalls) output.content = output.content.filter((block) => !isToolCall(block));
446
+ return;
447
+ }
448
+ const toolCalls = output.content.filter(isToolCall);
449
+ const rejectToolCalls = () => {
450
+ output.stopReason = "error";
451
+ output.errorMessage = "Provider returned an incomplete or malformed tool call";
452
+ output.content = output.content.filter((candidate) => !isToolCall(candidate));
453
+ };
454
+ if (toolCalls.some((call) => typeof call.name !== "string" || call.name.trim().length === 0 || typeof call.partialArgs !== "string" || call.partialArgs.trim().length === 0)) {
455
+ rejectToolCalls();
456
+ return;
457
+ }
458
+ try {
459
+ finalizeTerminalToolCallArguments(toolCalls, (call) => call.partialArgs);
460
+ } catch {
461
+ rejectToolCalls();
462
+ return;
463
+ }
464
+ for (let contentIndex = 0; contentIndex < output.content.length; contentIndex += 1) {
465
+ const block = output.content[contentIndex];
466
+ if (!block || !isToolCall(block)) continue;
467
+ delete block.partialArgs;
468
+ delete block.streamIndex;
469
+ options.onConfirmedToolCall?.(block, contentIndex);
470
+ }
471
+ }
472
+ //#endregion
473
+ //#region packages/ai/src/transports/openai-completions-host.ts
474
+ /**
475
+ * Chat Completions accepts Azure AI Foundry hosts in addition to traditional
476
+ * Azure OpenAI hosts. Do not replace this with isTraditionalAzureOpenAIHost,
477
+ * which intentionally excludes the .services.ai.azure.com Foundry suffix.
478
+ */
479
+ function isAzureOpenAICompatibleHost(hostname) {
480
+ return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
481
+ }
482
+ //#endregion
483
+ //#region packages/ai/src/openai-completions-messages.ts
484
+ /** Whether replayed messages require a tools marker for proxy compatibility. */
485
+ function hasToolCallHistory(messages) {
486
+ return messages.some((message) => message.role === "toolResult" || message.role === "assistant" && Array.isArray(message.content) && message.content.some((block) => block.type === "toolCall"));
487
+ }
488
+ /** Convert a normalized transcript to OpenAI Chat Completions messages. */
489
+ function convertMessages(model, context, compat, options = {}) {
490
+ const params = [];
491
+ const normalizeToolCallId = (id) => {
492
+ if (id.includes("|")) return id.slice(0, id.indexOf("|")).replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
493
+ if (model.provider === "openai") return id.length > 40 ? truncateUtf16Safe(id, 40) : id;
494
+ return id;
495
+ };
496
+ const transformedMessages = transformProviderMessages(context.messages, model, normalizeToolCallId);
497
+ let relocatableSplit;
498
+ let systemParamIndex;
499
+ if (context.systemPrompt) {
500
+ const role = model.reasoning && compat.supportsDeveloperRole ? "developer" : "system";
501
+ let systemPrompt;
502
+ if (options.preserveSystemPromptCacheBoundary) systemPrompt = stripSystemPromptRelocatableBoundary(context.systemPrompt);
503
+ else {
504
+ const split = splitSystemPromptRelocatableBoundary(context.systemPrompt);
505
+ if (split && split.relocatable.length > 0) relocatableSplit = split;
506
+ systemPrompt = stripSystemPromptCacheBoundary(context.systemPrompt);
507
+ }
508
+ systemParamIndex = params.length;
509
+ params.push({
510
+ role,
511
+ content: sanitizeSurrogates(systemPrompt)
512
+ });
513
+ }
514
+ let lastRole = null;
515
+ for (let i = 0; i < transformedMessages.length; i++) {
516
+ const msg = transformedMessages[i];
517
+ if (!msg) continue;
518
+ if (compat.requiresAssistantAfterToolResult && lastRole === "toolResult" && msg.role === "user") params.push({
519
+ role: "assistant",
520
+ content: "I have processed the tool results."
521
+ });
522
+ if (isRuntimeContextMessage(msg)) {
523
+ params.push({
524
+ role: compat.supportsDeveloperRole ? "developer" : "user",
525
+ content: sanitizeSurrogates(runtimeContextContentToText(msg.content))
526
+ });
527
+ options.cacheOptOutIndexes?.add(params.length - 1);
528
+ } else if (msg.role === "user") {
529
+ let userParam;
530
+ if (typeof msg.content === "string") userParam = {
531
+ role: "user",
532
+ content: sanitizeSurrogates(msg.content)
533
+ };
534
+ else {
535
+ const content = msg.content.map((item) => {
536
+ if (item.type === "text") return {
537
+ type: "text",
538
+ text: sanitizeSurrogates(item.text)
539
+ };
540
+ if (item.type === "video") return {
541
+ type: "video_url",
542
+ video_url: { url: `data:${item.mimeType};base64,${item.data}` }
543
+ };
544
+ return {
545
+ type: "image_url",
546
+ image_url: { url: `data:${item.mimeType};base64,${item.data}` }
547
+ };
548
+ });
549
+ if (content.length === 0) continue;
550
+ userParam = {
551
+ role: "user",
552
+ content
553
+ };
554
+ }
555
+ if (hasRuntimeContextMarker(msg)) options.cacheOptOutIndexes?.add(params.length);
556
+ params.push(userParam);
557
+ } else if (msg.role === "assistant") {
558
+ const assistantMsg = {
559
+ role: "assistant",
560
+ content: compat.requiresAssistantAfterToolResult ? "" : null
561
+ };
562
+ const assistantTexts = [];
563
+ const nonEmptyThinkingBlocks = [];
564
+ const toolCalls = [];
565
+ msg.content.forEach((block) => {
566
+ if (block.type === "text" && block.text.trim().length > 0) assistantTexts.push(sanitizeSurrogates(block.text));
567
+ else if (block.type === "thinking" && block.thinking.trim().length > 0) nonEmptyThinkingBlocks.push(block);
568
+ else if (block.type === "toolCall") toolCalls.push(block);
569
+ });
570
+ if (nonEmptyThinkingBlocks.length > 0 && compat.requiresThinkingAsText) assistantMsg.content = [{
571
+ type: "text",
572
+ text: nonEmptyThinkingBlocks.map((block) => sanitizeSurrogates(block.thinking)).join("\n\n")
573
+ }, ...assistantTexts.map((text) => ({
574
+ type: "text",
575
+ text
576
+ }))];
577
+ else {
578
+ const assistantText = assistantTexts.join("\n");
579
+ if (assistantText.length > 0) assistantMsg.content = assistantText;
580
+ if (nonEmptyThinkingBlocks.length > 0) {
581
+ let signature = nonEmptyThinkingBlocks.at(0)?.thinkingSignature;
582
+ if (model.provider === "opencode-go" && signature === "reasoning") signature = "reasoning_content";
583
+ if (signature && signature.length > 0) assistantMsg[signature] = nonEmptyThinkingBlocks.map((block) => block.thinking).join("\n");
584
+ }
585
+ }
586
+ if (toolCalls.length > 0) {
587
+ assistantMsg.tool_calls = toolCalls.map((toolCall) => ({
588
+ id: toolCall.id,
589
+ type: "function",
590
+ function: {
591
+ name: toolCall.name,
592
+ arguments: JSON.stringify(toolCall.arguments)
593
+ }
594
+ }));
595
+ const reasoningDetails = toolCalls.flatMap((toolCall) => {
596
+ const signature = toolCall.thoughtSignature;
597
+ if (!signature) return [];
598
+ try {
599
+ const parsed = JSON.parse(signature);
600
+ return parsed ? [parsed] : [];
601
+ } catch {
602
+ return [];
603
+ }
604
+ });
605
+ if (reasoningDetails.length > 0) assistantMsg.reasoning_details = reasoningDetails;
606
+ }
607
+ if (compat.requiresReasoningContentOnAssistantMessages && model.reasoning && assistantMsg.reasoning_content === void 0) assistantMsg.reasoning_content = "";
608
+ const content = assistantMsg.content;
609
+ if (!(content !== null && content !== void 0 && content.length > 0) && !assistantMsg.tool_calls) continue;
610
+ params.push(assistantMsg);
611
+ } else if (msg.role === "toolResult") {
612
+ const imageContentParts = [];
613
+ let j = i;
614
+ while (j < transformedMessages.length) {
615
+ const toolMsg = transformedMessages.at(j);
616
+ if (toolMsg?.role !== "toolResult") break;
617
+ const textResult = extractToolResultText(toolMsg.content);
618
+ const mediaPlaceholder = describeToolResultMediaPlaceholder(toolMsg.content);
619
+ const images = toolMsg.content.filter(isImageWithMediaPayload);
620
+ const toolResultMsg = {
621
+ role: "tool",
622
+ content: textResult.trim() ? textResult : mediaPlaceholder ?? "(no output)",
623
+ tool_call_id: toolMsg.toolCallId
624
+ };
625
+ if (compat.requiresToolResultName && toolMsg.toolName) toolResultMsg.name = toolMsg.toolName;
626
+ params.push(toolResultMsg);
627
+ if (images.length > 0 && model.input.includes("image")) {
628
+ const boundedToolName = sanitizeSurrogates(truncateUtf16Safe(toolMsg.toolName ?? "", 64));
629
+ imageContentParts.push({
630
+ type: "text",
631
+ text: `Image(s) from tool result #${j - i + 1}${boundedToolName ? ` (${boundedToolName})` : ""}:`
632
+ });
633
+ for (const block of images) imageContentParts.push({
634
+ type: "image_url",
635
+ image_url: { url: `data:${block.mimeType};base64,${block.data}` }
636
+ });
637
+ }
638
+ j += 1;
639
+ }
640
+ i = j - 1;
641
+ if (imageContentParts.length > 0) {
642
+ if (compat.requiresAssistantAfterToolResult) params.push({
643
+ role: "assistant",
644
+ content: "I have processed the tool results."
645
+ });
646
+ params.push({
647
+ role: "user",
648
+ content: imageContentParts
649
+ });
650
+ lastRole = "user";
651
+ } else lastRole = "toolResult";
652
+ continue;
653
+ }
654
+ lastRole = msg.role;
655
+ }
656
+ if (relocatableSplit !== void 0 && systemParamIndex !== void 0) relocateNonBehavioralRegion({
657
+ params,
658
+ systemParamIndex,
659
+ split: relocatableSplit,
660
+ cacheOptOutIndexes: options.cacheOptOutIndexes
661
+ });
662
+ return params;
663
+ }
664
+ /** Commit relocation only after finding an emitted carrier. */
665
+ function relocateNonBehavioralRegion(args) {
666
+ const text = sanitizeSurrogates(stripSystemPromptCacheBoundary(args.split.relocatable));
667
+ for (let index = args.systemParamIndex + 1; index < args.params.length; index++) {
668
+ const param = args.params[index];
669
+ if (!param || param.role !== "user") continue;
670
+ if (typeof param.content === "string") param.content = `${param.content}\n\n${text}`;
671
+ else if (Array.isArray(param.content)) param.content = [...param.content, {
672
+ type: "text",
673
+ text
674
+ }];
675
+ else continue;
676
+ const systemParam = args.params[args.systemParamIndex];
677
+ if (systemParam) systemParam.content = sanitizeSurrogates(stripSystemPromptCacheBoundary(args.split.remainingPrompt));
678
+ args.cacheOptOutIndexes?.add(index);
679
+ return;
680
+ }
681
+ }
682
+ //#endregion
683
+ //#region packages/ai/src/transports/openai-completions-policy.ts
684
+ function applyCompletionsReasoningAndRouting(params, model, reasoning, compat, mode) {
685
+ const direct = mode === "direct";
686
+ const nativeEffort = compat.thinkingFormat === "openrouter" ? void 0 : reasoning.effort;
687
+ const enabled = reasoning.thinkingEnabled ?? false;
688
+ if (model.reasoning) {
689
+ let allowScalarEffort = true;
690
+ if (direct && compat.thinkingFormat === "zai") {
691
+ params.thinking = enabled ? {
692
+ type: "enabled",
693
+ clear_thinking: false
694
+ } : { type: "disabled" };
695
+ allowScalarEffort = false;
696
+ } else if (compat.thinkingFormat === "qwen") {
697
+ params.enable_thinking = enabled;
698
+ allowScalarEffort = false;
699
+ } else if (compat.thinkingFormat === "qwen-chat-template") {
700
+ params.chat_template_kwargs = {
701
+ enable_thinking: enabled,
702
+ ...direct ? { preserve_thinking: true } : {}
703
+ };
704
+ allowScalarEffort = false;
705
+ } else if (compat.thinkingFormat === "together" || direct && compat.thinkingFormat === "deepseek") {
706
+ if (compat.thinkingFormat === "deepseek") params.thinking = { type: enabled ? "enabled" : "disabled" };
707
+ else params.reasoning = { enabled };
708
+ allowScalarEffort = enabled;
709
+ }
710
+ if (allowScalarEffort && compat.supportsReasoningEffort && (direct ? nativeEffort !== void 0 : nativeEffort)) params.reasoning_effort = nativeEffort;
711
+ }
712
+ if (!direct) return;
713
+ if (compat.openRouterRouting) params.provider = compat.openRouterRouting;
714
+ if (model.baseUrl.includes("ai-gateway.vercel.sh") && model.compat?.vercelGatewayRouting) {
715
+ const routing = model.compat.vercelGatewayRouting;
716
+ if (routing.only || routing.order) {
717
+ const gatewayOptions = {};
718
+ if (routing.only) gatewayOptions.only = routing.only;
719
+ if (routing.order) gatewayOptions.order = routing.order;
720
+ params.providerOptions = { gateway: gatewayOptions };
721
+ }
722
+ }
723
+ }
724
+ //#endregion
725
+ //#region packages/ai/src/transports/openai-completions-replay.ts
726
+ function isGoogleOpenAICompatModel(model) {
727
+ const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
728
+ return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
729
+ }
730
+ const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
731
+ const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
732
+ function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
733
+ return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
734
+ }
735
+ function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
736
+ if (!isGoogleOpenAICompatModel(model)) return;
737
+ const sigById = /* @__PURE__ */ new Map();
738
+ const fallbackSig = isGoogleGemini3ThinkingLevelModel(model.id) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
739
+ for (const msg of context.messages ?? []) {
740
+ if (msg.role !== "assistant") continue;
741
+ if (!Array.isArray(msg.content)) continue;
742
+ for (const block of msg.content) {
743
+ if (block.type !== "toolCall") continue;
744
+ const id = block.id;
745
+ const sig = block.thoughtSignature;
746
+ if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
747
+ const isSameRoute = msg.api === model.api && msg.provider === model.provider && msg.model === model.id;
748
+ if (!isSameRoute && !fallbackSig) continue;
749
+ sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
750
+ }
751
+ }
752
+ }
753
+ if (sigById.size === 0 && !fallbackSig) return;
754
+ for (const message of outgoingMessages) {
755
+ const toolCalls = "tool_calls" in message ? message.tool_calls : void 0;
756
+ if (!Array.isArray(toolCalls)) continue;
757
+ for (const toolCall of toolCalls) {
758
+ let sig = sigById.get(toolCall.id) ?? fallbackSig;
759
+ if (sig) {
760
+ if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
761
+ }
762
+ if (!sig) continue;
763
+ Object.assign(toolCall, { extra_content: { google: { thought_signature: sig } } });
764
+ }
765
+ }
766
+ }
767
+ const COMPLETIONS_REASONING_REPLAY_FIELDS = [
768
+ "reasoning_details",
769
+ "reasoning_content",
770
+ "reasoning",
771
+ "reasoning_text"
772
+ ];
773
+ function sanitizeOpenRouterReasoningReplayFields(record) {
774
+ const reasoningDetails = record.reasoning_details;
775
+ if (typeof reasoningDetails === "string") {
776
+ if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
777
+ delete record.reasoning_details;
778
+ } else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
779
+ for (const field of ["reasoning", "reasoning_content"]) if (field in record && (typeof record[field] !== "string" || record[field] === "")) delete record[field];
780
+ const reasoningText = record.reasoning_text;
781
+ if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
782
+ delete record.reasoning_text;
783
+ }
784
+ const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
785
+ "deepseek-v4-flash",
786
+ "deepseek-v4-pro",
787
+ "kimi-for-coding",
788
+ "kimi-k2.5",
789
+ "kimi-k2.6",
790
+ "kimi-k2.7-code",
791
+ "kimi-k2.7-code-highspeed",
792
+ "kimi-k3",
793
+ "kimi-k2-thinking",
794
+ "kimi-k2-thinking-turbo",
795
+ "mimo-v2-pro",
796
+ "mimo-v2-omni",
797
+ "mimo-v2.5",
798
+ "mimo-v2.5-pro",
799
+ "mimo-v2.6-flash",
800
+ "mimo-v2.6-pro",
801
+ "mimo-v2.6-pro-ultraspeed"
802
+ ]);
803
+ const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
804
+ "-free",
805
+ "-paid",
806
+ "-trial"
807
+ ];
808
+ function stripReasoningContentReplayTierSuffix(modelId) {
809
+ for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
810
+ return modelId;
811
+ }
812
+ function getReasoningContentReplayModelIdCandidates(modelId) {
813
+ if (typeof modelId !== "string") return [];
814
+ const normalized = modelId.trim().toLowerCase();
815
+ if (!normalized) return [];
816
+ const parts = normalized.split("/").filter(Boolean);
817
+ const finalPart = parts[parts.length - 1] ?? normalized;
818
+ const candidates = [finalPart];
819
+ const colonParts = finalPart.split(":").filter(Boolean);
820
+ if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
821
+ for (const candidate of candidates.slice()) {
822
+ const stripped = stripReasoningContentReplayTierSuffix(candidate);
823
+ if (stripped !== candidate) candidates.push(stripped);
824
+ }
825
+ return uniqueStrings(candidates.filter(Boolean));
826
+ }
827
+ function shouldPreserveReasoningContentReplay(model, compat) {
828
+ if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
829
+ return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
830
+ }
831
+ function shouldPreserveOpenRouterReasoningReplay(model) {
832
+ if (model.provider !== "openrouter") return true;
833
+ const normalizedModelId = model.id.trim().toLowerCase();
834
+ return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
835
+ }
836
+ function shouldTrustReasoningContentReplayMetadata(model) {
837
+ if (!model.reasoning) return false;
838
+ if (model.provider.trim().toLowerCase() === "openai") return false;
839
+ return shouldPreserveOpenRouterReasoningReplay(model);
840
+ }
841
+ function applyCompletionsReplay(outgoingMessages, context, model, compat) {
842
+ injectToolCallThoughtSignatures(outgoingMessages, context, model);
843
+ const preserveOpenRouterReasoning = compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model);
844
+ const preserveReasoningContent = shouldPreserveReasoningContentReplay(model, compat);
845
+ for (const message of outgoingMessages) {
846
+ if (message.role !== "assistant") continue;
847
+ if (preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(message);
848
+ else {
849
+ const record = message;
850
+ for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) {
851
+ if (preserveReasoningContent && field === "reasoning_content" && typeof record[field] === "string") continue;
852
+ delete record[field];
853
+ }
854
+ }
855
+ }
856
+ }
857
+ //#endregion
858
+ //#region packages/ai/src/transports/openai-completions-params.ts
859
+ function isKnownOpenAICompletionsEndpoint(model) {
860
+ if (!model.baseUrl.trim() || isNativeOpenAIEndpoint(model)) return true;
861
+ const endpointClass = resolveProviderEndpoint(model).endpointClass;
862
+ if (endpointClass === "openai-public" || endpointClass === "azure-openai") return true;
863
+ const endpoint = URL.parse(model.baseUrl);
864
+ return endpoint !== null && isAzureOpenAICompatibleHost(endpoint.hostname.toLowerCase());
865
+ }
866
+ function resolveOpenAICompletionsMaxTokens(model, options) {
867
+ if (options?.maxTokens) return {
868
+ maxTokens: options.maxTokens,
869
+ clampToModelMaxTokens: true
870
+ };
871
+ const paramsMaxTokens = resolveMaxTokensParam(model.params);
872
+ if (paramsMaxTokens) return {
873
+ maxTokens: paramsMaxTokens,
874
+ clampToModelMaxTokens: false
875
+ };
876
+ return {
877
+ maxTokens: model.maxTokens,
878
+ clampToModelMaxTokens: false
879
+ };
880
+ }
881
+ function resolveOpenAICompletionsModelMaxTokens(model) {
882
+ const maxTokens = asPositiveFiniteNumber(model.maxTokens);
883
+ return maxTokens === void 0 ? void 0 : Math.floor(maxTokens);
884
+ }
885
+ const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
886
+ const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
887
+ const MIN_USEFUL_OUTPUT_TOKENS = 16;
888
+ function estimateJsonChars(value, fallback) {
889
+ try {
890
+ return estimateStringChars(JSON.stringify(value));
891
+ } catch {
892
+ return fallback;
893
+ }
894
+ }
895
+ function estimateOpenAICompletionsInputTokens(payload) {
896
+ let adjustedChars = 0;
897
+ adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
898
+ if (payload.tools?.length) adjustedChars += estimateJsonChars(payload.tools, 1024);
899
+ if (payload.response_format !== void 0) adjustedChars += estimateJsonChars(payload.response_format, 256);
900
+ return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
901
+ }
902
+ function estimateOpenAICompletionsMessagesChars(messages) {
903
+ let adjustedChars = 0;
904
+ for (const message of messages) {
905
+ if (!message || typeof message !== "object") continue;
906
+ const record = message;
907
+ adjustedChars += estimateOpenAICompletionsContentChars(record.content);
908
+ for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
909
+ if (record.tool_calls !== void 0) adjustedChars += estimateJsonChars(record.tool_calls, 256);
910
+ }
911
+ return adjustedChars;
912
+ }
913
+ function estimateOpenAICompletionsContentChars(value) {
914
+ if (typeof value === "string") return estimateStringChars(value);
915
+ if (!Array.isArray(value)) return 0;
916
+ let adjustedChars = 0;
917
+ for (const block of value) {
918
+ if (!block || typeof block !== "object") continue;
919
+ const record = block;
920
+ if (record.type === "image_url" || record.type === "input_image") {
921
+ adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
922
+ continue;
923
+ }
924
+ const text = record.text;
925
+ if (typeof text === "string") {
926
+ adjustedChars += estimateStringChars(text);
927
+ continue;
928
+ }
929
+ adjustedChars += estimateJsonChars(block, 256);
930
+ }
931
+ return adjustedChars;
932
+ }
933
+ function convertTools(tools, compat, model, mode) {
934
+ const prepared = mode === "managed" ? prepareOpenAITools(tools) : void 0;
935
+ const projection = prepared?.projection ?? projectOpenAITools(tools);
936
+ const convert = () => {
937
+ const strict = mode === "direct" ? compat.supportsStrictMode ? false : void 0 : resolveOpenAIStrictToolFlagWithDiagnostics(projection, getAiTransportHost().resolveOpenAIStrictToolSetting(model, {
938
+ transport: "stream",
939
+ supportsStrictMode: compat?.supportsStrictMode
940
+ }), {
941
+ transport: "completions",
942
+ model
943
+ });
944
+ return {
945
+ projection,
946
+ tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
947
+ const functionTool = {
948
+ name: tool.name,
949
+ description: tool.description,
950
+ parameters: mode === "direct" ? tool.parameters : normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
951
+ };
952
+ if (strict !== void 0) functionTool.strict = strict;
953
+ return {
954
+ type: "function",
955
+ function: functionTool
956
+ };
957
+ })
958
+ };
959
+ };
960
+ return prepared ? withPreparedToolSchemaNormalization(prepared.schemas, convert) : convert();
961
+ }
962
+ function buildOpenAICompletionsParams(model, context, options) {
963
+ return buildOpenAICompletionsRequest(model, context, options, { mode: "managed" });
964
+ }
965
+ function buildOpenAICompletionsRequest(model, context, options, policy) {
966
+ const resolvedPolicy = policy.mode === "direct" ? policy : {
967
+ ...policy,
968
+ compat: getCompat(model)
969
+ };
970
+ const compat = resolvedPolicy.compat;
971
+ const managedCompat = resolvedPolicy.mode === "managed" ? resolvedPolicy.compat : void 0;
972
+ const endpointDetection = detectOpenAICompletionsCompat(model);
973
+ const compatDetection = policy.mode === "managed" ? endpointDetection : void 0;
974
+ const { endpointClass } = endpointDetection.capabilities;
975
+ const cacheRetention = policy.mode === "direct" ? policy.cacheRetention : resolveCacheRetention(options?.cacheRetention);
976
+ const cacheControl = resolveCompletionsCacheControl(compat, cacheRetention, endpointClass === "openrouter" || endpointClass === "default" && model.provider === "openrouter");
977
+ const markTools = endpointClass !== "modelstudio-native" && !(endpointClass === "default" && [
978
+ "modelstudio",
979
+ "dashscope",
980
+ "qwen"
981
+ ].includes(model.provider));
982
+ const cacheOptOutIndexes = /* @__PURE__ */ new Set();
983
+ const convertedMessages = convertMessages(model, context, compat, {
984
+ cacheOptOutIndexes,
985
+ preserveSystemPromptCacheBoundary: cacheControl !== void 0 && !managedCompat?.requiresStringContent
986
+ });
987
+ let messages = convertedMessages;
988
+ if (managedCompat) {
989
+ applyCompletionsReplay(convertedMessages, context, model, managedCompat);
990
+ if (managedCompat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
991
+ if (managedCompat.requiresStringContent) messages = flattenCompletionMessagesToStringContent(messages);
992
+ }
993
+ const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
994
+ const params = {
995
+ model: model.id,
996
+ messages,
997
+ stream: (options?.streaming ?? model.params?.streaming) !== false,
998
+ ...resolveOpenAIPromptCacheParams(model, cacheRetention, compat)
999
+ };
1000
+ if (params.stream && compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
1001
+ if (compat.supportsStore) params.store = false;
1002
+ if (policy.mode === "direct" || compat.supportsPromptCacheKey && promptCacheKey) params.prompt_cache_key = compat.supportsPromptCacheKey ? promptCacheKey : void 0;
1003
+ if (options?.temperature !== void 0) params.temperature = options.temperature;
1004
+ if (policy.mode === "managed" && options?.topP !== void 0) params.top_p = options.topP;
1005
+ const requestedResponseFormat = options?.responseFormat;
1006
+ const responseFormat = policy.mode === "direct" && requestedResponseFormat === void 0 ? void 0 : resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
1007
+ provider: model.provider,
1008
+ baseUrl: model.baseUrl,
1009
+ hasTools: () => Boolean(context.tools?.length)
1010
+ }) ? void 0 : requestedResponseFormat, compat.supportsJsonSchemaResponseFormat);
1011
+ if (responseFormat !== void 0) params.response_format = responseFormat;
1012
+ if (policy.mode === "managed" && options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
1013
+ if (policy.mode === "managed" && options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
1014
+ if (policy.mode === "managed" && options?.seed !== void 0) params.seed = options.seed;
1015
+ if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
1016
+ let directToolProjection;
1017
+ if (policy.mode === "direct" || supportsModelTools(model)) {
1018
+ if (context.tools) {
1019
+ const converted = convertTools(context.tools, compat, model, policy.mode);
1020
+ if (policy.mode === "direct") directToolProjection = converted.projection;
1021
+ if (converted.tools.length > 0 || policy.mode === "managed" && converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
1022
+ else if (hasToolCallHistory(context.messages)) params.tools = [];
1023
+ if (policy.mode === "direct" && params.stream && compat.zaiToolStream && converted.tools.length > 0) params.tool_stream = true;
1024
+ if (policy.mode === "managed" && options?.toolChoice) {
1025
+ const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
1026
+ if (toolChoice !== void 0) params.tool_choice = toolChoice;
1027
+ } else if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && params.tools?.length) params.tool_choice = "auto";
1028
+ } else if (hasToolCallHistory(context.messages)) params.tools = [];
1029
+ if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && params.tools?.length === 0) {
1030
+ delete params.tools;
1031
+ delete params.tool_choice;
1032
+ }
1033
+ }
1034
+ if (policy.mode === "direct" && compat.cacheControlFormat === "anthropic") applyCompletionsAnthropicCacheControl(params, cacheControl ?? null, cacheOptOutIndexes, markTools);
1035
+ if (policy.mode === "direct" && options?.toolChoice) {
1036
+ const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, directToolProjection ?? projectOpenAITools([]));
1037
+ if (toolChoice !== void 0) params.tool_choice = toolChoice;
1038
+ }
1039
+ const isOpenRouter = compat.thinkingFormat === "openrouter";
1040
+ const usesBinaryOpenRouterThinking = isOpenRouter && (model.compat?.supportsReasoningEffort === false || model.compat?.supportedReasoningEfforts?.length === 0);
1041
+ const simpleReasoning = options?.reasoning;
1042
+ const requestedEffort = policy.mode === "direct" ? options?.reasoningEffort : options?.reasoningEffort ?? (simpleReasoning === "none" ? "none" : resolveOpenAISimpleReasoningEffort({
1043
+ ...model,
1044
+ compat: model.compat ?? void 0
1045
+ }, simpleReasoning)) ?? (usesBinaryOpenRouterThinking ? void 0 : "high");
1046
+ const reasoning = resolveOpenAIRequestReasoning(model, requestedEffort, compat.reasoningEffortForOff);
1047
+ const { effort, thinkingEnabled } = reasoning;
1048
+ {
1049
+ const maxTokenBudget = policy.mode === "direct" ? {
1050
+ maxTokens: options?.maxTokens,
1051
+ clampToModelMaxTokens: true
1052
+ } : resolveOpenAICompletionsMaxTokens(model, options);
1053
+ const effectiveMaxTokens = maxTokenBudget.maxTokens;
1054
+ const effectiveContextTokens = asPositiveFiniteNumber(model.contextTokens) ?? asPositiveFiniteNumber(model.contextWindow);
1055
+ let clampedMaxTokens = effectiveMaxTokens;
1056
+ const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
1057
+ if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
1058
+ clampedMaxTokens = modelMaxTokens;
1059
+ if (policy.mode === "managed") emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
1060
+ }
1061
+ if (compatDetection?.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
1062
+ const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
1063
+ const remainingBudget = Math.max(0, effectiveContextTokens - estimatedInputTokens - 1);
1064
+ if (clampedMaxTokens > remainingBudget) {
1065
+ if (remainingBudget < MIN_USEFUL_OUTPUT_TOKENS) throw Object.assign(/* @__PURE__ */ new Error(`Context window exceeded: estimated input ${estimatedInputTokens} leaves only ${remainingBudget} output tokens within the ${effectiveContextTokens}-token context.`), { code: "context_length_exceeded" });
1066
+ clampedMaxTokens = remainingBudget;
1067
+ emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
1068
+ }
1069
+ }
1070
+ if (policy.mode === "direct" ? options?.maxTokens : clampedMaxTokens) {
1071
+ if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
1072
+ else params.max_completion_tokens = clampedMaxTokens;
1073
+ }
1074
+ }
1075
+ if (isOpenRouter && model.reasoning) {
1076
+ if (usesBinaryOpenRouterThinking && thinkingEnabled !== void 0) params.reasoning = { enabled: thinkingEnabled };
1077
+ else if (effort !== void 0) params.reasoning = { effort };
1078
+ }
1079
+ applyCompletionsReasoningAndRouting(params, model, reasoning, compat, policy.mode);
1080
+ if (policy.mode === "managed" && compat.cacheControlFormat === "anthropic") applyCompletionsAnthropicCacheControl(params, cacheControl ?? null, cacheOptOutIndexes, markTools, !managedCompat?.requiresStringContent);
1081
+ if (params.tools?.length && isKnownOpenAICompletionsEndpoint(model)) {
1082
+ if (isOpenAIGpt56Model(model)) params.reasoning_effort = "none";
1083
+ else if (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model)) delete params.reasoning_effort;
1084
+ }
1085
+ return params;
1086
+ }
1087
+ //#endregion
1088
+ //#region packages/ai/src/transports/openai-completions-dsml.ts
1089
+ const DEEPSEEK_DSML_TOOL_KINDS = [
1090
+ "tool_calls",
1091
+ "tool_call",
1092
+ "function_calls"
1093
+ ];
1094
+ const DEEPSEEK_DSML_TOOL_OPEN_TOKENS = DEEPSEEK_DSML_MARKERS.flatMap((marker) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `<${marker}${kind}>`));
1095
+ const DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES = DEEPSEEK_DSML_MARKERS.map((marker) => `<${marker}invoke`);
1096
+ const DEEPSEEK_DSML_INVOKE_OPEN_TAG = new RegExp(`^<${DEEPSEEK_DSML_MARKER_PATTERN}invoke\\b[^<>]*>$`);
1097
+ const DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length));
1098
+ const DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length + 1), ...DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES.map((token) => token.length + 2));
1099
+ const MAX_DSML_RECOVERY_BUFFER_BYTES = 256e3;
1100
+ const DEEPSEEK_DSML_SCAN_BATCH_CHARS = 65536;
1101
+ const DSML_NAME_ATTRIBUTE_RE = /\bname=("([^"]*)"|'([^']*)'|([^\s>]+))/;
1102
+ function createDsmlRecoverer() {
1103
+ let buffer = "";
1104
+ let bufferBytes = 0;
1105
+ let bufferEndsWithHighSurrogate = false;
1106
+ let pendingScanChars = 0;
1107
+ let activeOpenToken = null;
1108
+ let blockScanState = { offset: 0 };
1109
+ const resetBlockScan = () => {
1110
+ activeOpenToken = null;
1111
+ pendingScanChars = 0;
1112
+ blockScanState = { offset: 0 };
1113
+ };
1114
+ const consume = (final) => {
1115
+ const output = [];
1116
+ while (buffer) {
1117
+ const open = activeOpenToken ? {
1118
+ index: 0,
1119
+ token: activeOpenToken
1120
+ } : findEarliestDsmlToken(buffer, DEEPSEEK_DSML_TOOL_OPEN_TOKENS);
1121
+ if (!open) {
1122
+ resetBlockScan();
1123
+ if (final) {
1124
+ output.push({
1125
+ kind: "text",
1126
+ text: buffer
1127
+ });
1128
+ buffer = "";
1129
+ bufferBytes = 0;
1130
+ bufferEndsWithHighSurrogate = false;
1131
+ return output;
1132
+ }
1133
+ const keep = longestDsmlTokenPrefixSuffixLength(buffer, DEEPSEEK_DSML_TOOL_OPEN_TOKENS, DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN);
1134
+ const emitLength = buffer.length - keep;
1135
+ if (emitLength > 0) {
1136
+ const emitted = buffer.slice(0, emitLength);
1137
+ output.push({
1138
+ kind: "text",
1139
+ text: emitted
1140
+ });
1141
+ bufferBytes -= Buffer.byteLength(emitted, "utf8");
1142
+ buffer = buffer.slice(emitted.length);
1143
+ if (!buffer) bufferEndsWithHighSurrogate = false;
1144
+ }
1145
+ return output;
1146
+ }
1147
+ if (open.index > 0) {
1148
+ const prefix = buffer.slice(0, open.index);
1149
+ output.push({
1150
+ kind: "text",
1151
+ text: prefix
1152
+ });
1153
+ bufferBytes -= Buffer.byteLength(prefix, "utf8");
1154
+ buffer = buffer.slice(prefix.length);
1155
+ resetBlockScan();
1156
+ }
1157
+ activeOpenToken = open.token;
1158
+ if (blockScanState.offset === 0) blockScanState.offset = open.token.length;
1159
+ const blockScan = scanDeepSeekDsmlToolBlock(buffer, open.token.replace("<", "</"), open.token.length, blockScanState);
1160
+ if (blockScan.kind === "nested-open") throw new Error("Nested DeepSeek DSML recovery wrappers are not supported");
1161
+ const close = blockScan.kind === "close" ? blockScan : null;
1162
+ if (!close) {
1163
+ if (final) {
1164
+ output.push({
1165
+ kind: "text",
1166
+ text: buffer
1167
+ });
1168
+ buffer = "";
1169
+ bufferBytes = 0;
1170
+ bufferEndsWithHighSurrogate = false;
1171
+ return output;
1172
+ }
1173
+ if (bufferBytes > MAX_DSML_RECOVERY_BUFFER_BYTES) throw new Error("Exceeded DeepSeek DSML recovery buffer limit");
1174
+ return output;
1175
+ }
1176
+ resetBlockScan();
1177
+ const body = buffer.slice(open.token.length, close.index);
1178
+ const blockText = buffer.slice(0, close.index + close.token.length);
1179
+ if (Buffer.byteLength(blockText, "utf8") > MAX_DSML_RECOVERY_BUFFER_BYTES) throw new Error("Exceeded DeepSeek DSML recovery buffer limit");
1180
+ const recoveredToolCalls = parseDeepSeekDsmlToolCallBlock(body);
1181
+ if (recoveredToolCalls.length > 0) output.push(...recoveredToolCalls);
1182
+ else output.push({
1183
+ kind: "text",
1184
+ text: blockText
1185
+ });
1186
+ bufferBytes -= Buffer.byteLength(blockText, "utf8");
1187
+ buffer = buffer.slice(blockText.length);
1188
+ if (!buffer) bufferEndsWithHighSurrogate = false;
1189
+ }
1190
+ return output;
1191
+ };
1192
+ return {
1193
+ push(chunk) {
1194
+ const append = measureUtf8AppendBytes(bufferEndsWithHighSurrogate, chunk);
1195
+ bufferBytes += append.bytes;
1196
+ bufferEndsWithHighSurrogate = append.endsWithHighSurrogate;
1197
+ buffer += chunk;
1198
+ pendingScanChars += chunk.length;
1199
+ if (activeOpenToken && pendingScanChars < DEEPSEEK_DSML_SCAN_BATCH_CHARS && !chunk.includes("<") && !chunk.includes(">") && bufferBytes <= MAX_DSML_RECOVERY_BUFFER_BYTES) return [];
1200
+ pendingScanChars = 0;
1201
+ return consume(false);
1202
+ },
1203
+ flush() {
1204
+ return consume(true);
1205
+ }
1206
+ };
1207
+ }
1208
+ function parseDeepSeekDsmlToolCallBlock(body) {
1209
+ const toolCalls = [];
1210
+ const invokeOpenRegex = new RegExp(`<${DEEPSEEK_DSML_MARKER_PATTERN}invoke\\b([^<>]*)>`, "g");
1211
+ let openMatch;
1212
+ while ((openMatch = invokeOpenRegex.exec(body)) !== null) {
1213
+ const invokeBodyStart = openMatch.index + openMatch[0].length;
1214
+ const invokeCloseToken = `</${openMatch[1]}invoke>`;
1215
+ const invokeCloseIndex = body.indexOf(invokeCloseToken, invokeBodyStart);
1216
+ if (invokeCloseIndex === -1) break;
1217
+ const invokeBody = body.slice(invokeBodyStart, invokeCloseIndex);
1218
+ invokeOpenRegex.lastIndex = invokeCloseIndex + invokeCloseToken.length;
1219
+ const invokeName = parseDsmlNameAttribute(openMatch[2] ?? "");
1220
+ if (!invokeName) continue;
1221
+ const parsedArguments = parseDeepSeekDsmlInvokeArguments(invokeBody);
1222
+ if (!parsedArguments) continue;
1223
+ toolCalls.push({
1224
+ kind: "toolCall",
1225
+ name: invokeName,
1226
+ arguments: parsedArguments,
1227
+ partialArgs: JSON.stringify(parsedArguments)
1228
+ });
1229
+ }
1230
+ return toolCalls;
1231
+ }
1232
+ function parseDeepSeekDsmlInvokeArguments(body) {
1233
+ const args = {};
1234
+ const parameterRegex = new RegExp(`<${DEEPSEEK_DSML_MARKER_PATTERN}parameter\\b([^>]*)>([\\s\\S]*?)</\\1parameter>`, "g");
1235
+ let parameterMatch;
1236
+ while ((parameterMatch = parameterRegex.exec(body)) !== null) {
1237
+ const name = parseDsmlNameAttribute(parameterMatch[2] ?? "");
1238
+ if (!name) continue;
1239
+ const rawValue = parameterMatch[3] ?? "";
1240
+ if (rawValue.length === 0) {
1241
+ const stringAttribute = Array.from((parameterMatch[2] ?? "").matchAll(/(?:^|\s)([^\s=]+)=(?:"([^"]*)"|'([^']*)'|([^\s"']+))(?=\s|$)/g)).find((attribute) => attribute[1] === "string");
1242
+ if ((stringAttribute?.[2] ?? stringAttribute?.[3] ?? stringAttribute?.[4]) !== "true") continue;
1243
+ }
1244
+ Object.defineProperty(args, name, {
1245
+ value: decodeDeepSeekDsmlText(rawValue),
1246
+ enumerable: true,
1247
+ configurable: true,
1248
+ writable: true
1249
+ });
1250
+ }
1251
+ if (Object.keys(args).length > 0) return args;
1252
+ const trimmed = body.trim();
1253
+ if (!trimmed.startsWith("{")) return null;
1254
+ try {
1255
+ const parsed = JSON.parse(trimmed);
1256
+ if (isRecord(parsed) && Object.keys(parsed).length > 0) return parsed;
1257
+ } catch {
1258
+ return null;
1259
+ }
1260
+ return null;
1261
+ }
1262
+ function parseDsmlNameAttribute(attributes) {
1263
+ const match = DSML_NAME_ATTRIBUTE_RE.exec(attributes);
1264
+ const value = match?.[2] ?? match?.[3] ?? match?.[4];
1265
+ return value ? decodeDeepSeekDsmlText(value) : null;
1266
+ }
1267
+ function decodeDeepSeekDsmlText(value) {
1268
+ return value.replaceAll("&quot;", "\"").replaceAll("&apos;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
1269
+ }
1270
+ function scanDeepSeekDsmlToolBlock(text, closeToken, contentStartIndex, state) {
1271
+ while (state.offset < text.length) {
1272
+ if (state.invoke?.kind === "open") {
1273
+ const nextOpen = text.indexOf("<", state.offset);
1274
+ const nextClose = text.indexOf(">", state.offset);
1275
+ if (nextClose === -1 && nextOpen === -1) {
1276
+ state.offset = text.length;
1277
+ return { kind: "incomplete" };
1278
+ }
1279
+ if (nextOpen !== -1 && (nextClose === -1 || nextOpen < nextClose)) {
1280
+ state.invoke = void 0;
1281
+ state.offset = nextOpen;
1282
+ continue;
1283
+ }
1284
+ const invokeOpenTag = text.slice(state.invoke.start, nextClose + 1);
1285
+ const open = DEEPSEEK_DSML_INVOKE_OPEN_TAG.exec(invokeOpenTag);
1286
+ if (!open) {
1287
+ state.offset = state.invoke.start + 1;
1288
+ state.invoke = void 0;
1289
+ continue;
1290
+ }
1291
+ state.invoke = {
1292
+ kind: "body",
1293
+ closeToken: `</${open[1]}invoke>`
1294
+ };
1295
+ state.offset = nextClose + 1;
1296
+ continue;
1297
+ }
1298
+ if (state.invoke?.kind === "body") {
1299
+ const invokeCloseIndex = text.indexOf(state.invoke.closeToken, state.offset);
1300
+ if (invokeCloseIndex === -1) {
1301
+ state.offset = Math.max(0, text.length - DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN + 1);
1302
+ return { kind: "incomplete" };
1303
+ }
1304
+ state.offset = invokeCloseIndex + state.invoke.closeToken.length;
1305
+ state.invoke = void 0;
1306
+ continue;
1307
+ }
1308
+ const toolOpen = findEarliestDsmlToken(text, DEEPSEEK_DSML_TOOL_OPEN_TOKENS, state.offset);
1309
+ const toolCloseIndex = text.indexOf(closeToken, state.offset);
1310
+ const invokeOpen = findEarliestDsmlToken(text, DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES, state.offset);
1311
+ const next = [
1312
+ toolOpen ? {
1313
+ kind: "nested-open",
1314
+ ...toolOpen
1315
+ } : null,
1316
+ toolCloseIndex === -1 ? null : {
1317
+ kind: "close",
1318
+ index: toolCloseIndex,
1319
+ token: closeToken
1320
+ },
1321
+ invokeOpen ? {
1322
+ kind: "invoke-open",
1323
+ ...invokeOpen
1324
+ } : null
1325
+ ].filter((candidate) => candidate !== null).toSorted((left, right) => left.index - right.index)[0];
1326
+ if (!next) {
1327
+ state.offset = Math.max(contentStartIndex, text.length - DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN + 1);
1328
+ return { kind: "incomplete" };
1329
+ }
1330
+ if (next.kind === "invoke-open") {
1331
+ state.invoke = {
1332
+ kind: "open",
1333
+ start: next.index
1334
+ };
1335
+ state.offset = next.index + next.token.length;
1336
+ continue;
1337
+ }
1338
+ return next;
1339
+ }
1340
+ return { kind: "incomplete" };
1341
+ }
1342
+ //#endregion
1343
+ //#region packages/ai/src/transports/openai-completions-gemma.ts
1344
+ const OPEN = "<|tool_call>";
1345
+ const CLOSE = "<tool_call|>";
1346
+ const QUOTE = "<|\"|>";
1347
+ const MAX_RECOVERY_BYTES = 256e3;
1348
+ function recoverGemmaCalls(text) {
1349
+ const parts = [];
1350
+ const opener = /\s*<\|tool_call>\s*call:([\w.-]+)\s*/y;
1351
+ const token = /\s*(<\|"\|>[\s\S]*?<\|"\|>|[\w.+-]+|[{}[\],:])\s*/y;
1352
+ let offset = 0;
1353
+ while (offset < text.length) {
1354
+ if (!text.slice(offset).trim()) {
1355
+ parts.push({
1356
+ kind: "text",
1357
+ text: text.slice(offset)
1358
+ });
1359
+ break;
1360
+ }
1361
+ opener.lastIndex = offset;
1362
+ const head = opener.exec(text);
1363
+ const name = head?.[1];
1364
+ if (!head || !name) return;
1365
+ const leading = /^\s*/.exec(head[0])?.[0] ?? "";
1366
+ if (leading) parts.push({
1367
+ kind: "text",
1368
+ text: leading
1369
+ });
1370
+ token.lastIndex = opener.lastIndex;
1371
+ let depth = 0;
1372
+ const json = [];
1373
+ do {
1374
+ const value = token.exec(text)?.[1];
1375
+ if (!value || json.length === 0 && value !== "{") return;
1376
+ if (value === "{") depth += 1;
1377
+ else if (value === "}") depth -= 1;
1378
+ json.push(value.startsWith(QUOTE) ? JSON.stringify(value.slice(5, -5)) : text[token.lastIndex] === ":" && /^[A-Za-z_][\w.-]*$/.test(value) ? JSON.stringify(value) : value);
1379
+ } while (depth > 0);
1380
+ if (!text.startsWith(CLOSE, token.lastIndex)) return;
1381
+ try {
1382
+ const argumentsValue = JSON.parse(json.join(" "));
1383
+ if (!isRecord(argumentsValue)) return;
1384
+ parts.push({
1385
+ kind: "toolCall",
1386
+ name,
1387
+ arguments: argumentsValue,
1388
+ partialArgs: JSON.stringify(argumentsValue)
1389
+ });
1390
+ } catch {
1391
+ return;
1392
+ }
1393
+ offset = token.lastIndex + 12;
1394
+ }
1395
+ return parts;
1396
+ }
1397
+ /** Buffer only a possible standalone call batch; prose and examples stay text. */
1398
+ function createGemmaToolCallRecoverer() {
1399
+ let buffer = "";
1400
+ let bytes = 0;
1401
+ let highSurrogate = false;
1402
+ let passthrough = false;
1403
+ return {
1404
+ push(text) {
1405
+ if (passthrough) return [{
1406
+ kind: "text",
1407
+ text
1408
+ }];
1409
+ const append = measureUtf8AppendBytes(highSurrogate, text);
1410
+ bytes += append.bytes;
1411
+ highSurrogate = append.endsWithHighSurrogate;
1412
+ buffer += text;
1413
+ const candidate = buffer.trimStart();
1414
+ if (OPEN.startsWith(candidate) || candidate.startsWith(OPEN)) {
1415
+ if (bytes > MAX_RECOVERY_BYTES) throw new Error("Exceeded Gemma tool-call recovery buffer limit");
1416
+ return [];
1417
+ }
1418
+ passthrough = true;
1419
+ const result = [{
1420
+ kind: "text",
1421
+ text: buffer
1422
+ }];
1423
+ buffer = "";
1424
+ return result;
1425
+ },
1426
+ flush(allowRecovery = true) {
1427
+ const text = buffer;
1428
+ buffer = "";
1429
+ passthrough ||= !allowRecovery;
1430
+ const recovered = allowRecovery ? recoverGemmaCalls(text) : void 0;
1431
+ return text ? recovered ?? [{
1432
+ kind: "text",
1433
+ text
1434
+ }] : [];
1435
+ }
1436
+ };
1437
+ }
1438
+ //#endregion
1439
+ //#region packages/ai/src/transports/openai-completions-stream.ts
1440
+ function extractToolCallThoughtSignature(toolCall) {
1441
+ const tc = toolCall;
1442
+ if (!tc) return;
1443
+ const extra = tc.extra_content?.google;
1444
+ return readNonEmptyStringPreservingWhitespace(extra?.thought_signature) ?? readNonEmptyStringPreservingWhitespace(tc.function?.thought_signature) ?? readNonEmptyStringPreservingWhitespace(tc.thought_signature);
1445
+ }
1446
+ async function processCompletionsStream(responseStream, output, model, stream, options) {
1447
+ const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
1448
+ const directMode = options?.mode === "direct";
1449
+ const emitReasoning = options?.emitReasoning ?? true;
1450
+ const openAIModel = model;
1451
+ const compat = getCompat(openAIModel);
1452
+ const visibleReasoningDetailTypes = new Set(compat.visibleReasoningDetailTypes);
1453
+ const shouldFilterDeepSeekDsmlText = !directMode && compat.thinkingFormat === "deepseek";
1454
+ const deepSeekTextFilter = shouldFilterDeepSeekDsmlText ? createDeepSeekTextFilter() : null;
1455
+ const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText ? createDsmlRecoverer() : null;
1456
+ const gemmaToolCallRecoverer = !directMode && /gemma-?4/i.test(model.id) ? createGemmaToolCallRecoverer() : null;
1457
+ const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
1458
+ if (options?.strictReasoningTags) reasoningTagTextPartitioner.markStrict();
1459
+ let currentBlock = null;
1460
+ const directContent = { block: null };
1461
+ let currentTextSource;
1462
+ let pendingInterruptedTextBlock = null;
1463
+ let confirmedInterruptedTextBlock = null;
1464
+ let pendingPostToolCallDeltas = [];
1465
+ let pendingPostToolCallBytes = 0;
1466
+ const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
1467
+ const toolCallBlocksById = /* @__PURE__ */ new Map();
1468
+ const encryptedReasoning = createOpenAIEncryptedToolCallReasoningTracker();
1469
+ const toolArgumentPreviewSchedules = /* @__PURE__ */ new WeakMap();
1470
+ const provisionalCommentaryTags = directMode ? options.provisionalCommentaryTags : /* @__PURE__ */ new Map();
1471
+ const blockIndices = /* @__PURE__ */ new WeakMap();
1472
+ let explicitVisibleTextBlocks;
1473
+ const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
1474
+ let finishReason;
1475
+ let sawNativeToolCallDelta = false;
1476
+ const blockIndex = () => directMode && currentBlock && currentBlock.type !== "toolCall" ? blockIndices.get(currentBlock) ?? output.content.length - 1 : output.content.length - 1;
1477
+ let chunkPushedEvent = false;
1478
+ const pushStreamEvent = (event) => {
1479
+ chunkPushedEvent = true;
1480
+ stream.push(event);
1481
+ };
1482
+ const appendToolCallBlock = (block) => {
1483
+ output.content.push(block);
1484
+ blockIndices.set(block, output.content.length - 1);
1485
+ pushStreamEvent({
1486
+ type: "toolcall_start",
1487
+ contentIndex: blockIndices.get(block) ?? -1,
1488
+ partial: output
1489
+ });
1490
+ };
1491
+ const queuePostToolCallDelta = (next) => {
1492
+ const nextBytes = Buffer.byteLength(next.text, "utf8");
1493
+ if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
1494
+ pendingPostToolCallBytes += nextBytes;
1495
+ const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
1496
+ if (!previous || previous.kind !== next.kind || previous.kind === "text" && next.kind === "text" && previous.source !== next.source || previous.kind === "thinking" && next.kind === "thinking" && previous.signature !== next.signature) {
1497
+ pendingPostToolCallDeltas.push(next);
1498
+ return;
1499
+ }
1500
+ previous.text += next.text;
1501
+ };
1502
+ const appendContentDelta = (delta) => {
1503
+ flushPendingPostToolCallDeltas();
1504
+ if (directMode && directContent.block?.type === delta.kind) currentBlock = directContent.block;
1505
+ if (delta.kind === "text" && currentBlock?.type === "text" && currentTextSource !== delta.source) currentBlock = null;
1506
+ if (!currentBlock || currentBlock.type !== delta.kind) {
1507
+ options?.beforeContentBlock?.(delta.kind);
1508
+ if (delta.kind === "text") {
1509
+ currentBlock = {
1510
+ type: "text",
1511
+ text: ""
1512
+ };
1513
+ currentTextSource = delta.source;
1514
+ if (delta.source === "reasoning_detail") (explicitVisibleTextBlocks ??= /* @__PURE__ */ new Set()).add(currentBlock);
1515
+ } else currentBlock = {
1516
+ type: "thinking",
1517
+ thinking: "",
1518
+ ...delta.signature ? { thinkingSignature: delta.signature } : {}
1519
+ };
1520
+ if (directMode) directContent.block = currentBlock;
1521
+ output.content.push(currentBlock);
1522
+ blockIndices.set(currentBlock, output.content.length - 1);
1523
+ pushStreamEvent({
1524
+ type: `${delta.kind}_start`,
1525
+ contentIndex: blockIndex(),
1526
+ partial: output
1527
+ });
1528
+ }
1529
+ if (currentBlock.type === "thinking") appendAssistantThinking(currentBlock, delta.text);
1530
+ else {
1531
+ currentBlock.text += delta.text;
1532
+ if (pendingInterruptedTextBlock && delta.text.trim()) {
1533
+ confirmedInterruptedTextBlock = pendingInterruptedTextBlock;
1534
+ pendingInterruptedTextBlock = null;
1535
+ }
1536
+ }
1537
+ const event = {
1538
+ contentIndex: blockIndex(),
1539
+ delta: delta.text
1540
+ };
1541
+ if (delta.kind === "thinking") pushStreamEvent({
1542
+ type: "thinking_delta",
1543
+ ...event,
1544
+ partial: output
1545
+ });
1546
+ else pushStreamEvent({
1547
+ type: "text_delta",
1548
+ ...event,
1549
+ ...directMode ? { partial: output } : {}
1550
+ });
1551
+ };
1552
+ const flushPendingPostToolCallDeltas = () => {
1553
+ if (currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
1554
+ const bufferedDeltas = pendingPostToolCallDeltas;
1555
+ pendingPostToolCallDeltas = [];
1556
+ pendingPostToolCallBytes = 0;
1557
+ for (const delta of bufferedDeltas) if (delta.kind === "text" || emitReasoning) appendContentDelta(delta);
1558
+ };
1559
+ const appendVisibleTextDelta = (text) => {
1560
+ if (!text) return;
1561
+ if (currentBlock?.type === "toolCall" && !directMode) queuePostToolCallDelta({
1562
+ kind: "text",
1563
+ text
1564
+ });
1565
+ else appendContentDelta({
1566
+ kind: "text",
1567
+ text
1568
+ });
1569
+ };
1570
+ const appendReasoningDeltas = (reasoningDeltas) => {
1571
+ for (const delta of reasoningDeltas) {
1572
+ if (delta.kind === "thinking" && !emitReasoning) continue;
1573
+ if (currentBlock?.type === "toolCall" && !directMode) {
1574
+ queuePostToolCallDelta({ ...delta });
1575
+ continue;
1576
+ }
1577
+ appendContentDelta(delta.kind === "thinking" && directMode && model.provider === "opencode-go" && delta.signature === "reasoning" ? {
1578
+ ...delta,
1579
+ signature: "reasoning_content"
1580
+ } : delta);
1581
+ }
1582
+ };
1583
+ const appendRecoveredToolCall = (toolCall) => {
1584
+ if (currentBlock?.type === "toolCall") {
1585
+ currentBlock = null;
1586
+ flushPendingPostToolCallDeltas();
1587
+ }
1588
+ rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
1589
+ const block = {
1590
+ type: "toolCall",
1591
+ id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
1592
+ name: toolCall.name,
1593
+ arguments: toolCall.arguments,
1594
+ partialArgs: toolCall.partialArgs
1595
+ };
1596
+ currentBlock = block;
1597
+ appendToolCallBlock(block);
1598
+ pushStreamEvent({
1599
+ type: "toolcall_delta",
1600
+ contentIndex: blockIndices.get(block) ?? -1,
1601
+ delta: toolCall.partialArgs,
1602
+ partial: output
1603
+ });
1604
+ };
1605
+ const appendRecoveredParts = (recoveredParts) => {
1606
+ for (const recoveredPart of recoveredParts) {
1607
+ if (recoveredPart.kind === "toolCall") {
1608
+ appendRecoveredToolCall(recoveredPart);
1609
+ continue;
1610
+ }
1611
+ const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
1612
+ for (const part of parts) appendVisibleTextDelta(part);
1613
+ }
1614
+ };
1615
+ const appendPartitionedVisibleDelta = (delta) => {
1616
+ if (delta.kind === "text") appendRecoveredParts(deepSeekToolCallRecoverer?.push(delta.text) ?? [{
1617
+ kind: "text",
1618
+ text: delta.text
1619
+ }]);
1620
+ };
1621
+ const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
1622
+ if (directMode || !hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
1623
+ const latestBlock = output.content[output.content.length - 1];
1624
+ if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
1625
+ if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
1626
+ appendContentDelta({
1627
+ kind: "thinking",
1628
+ text: ""
1629
+ });
1630
+ };
1631
+ const flushReasoningTagTextPartitioner = () => {
1632
+ const recoverUnclosed = output.stopReason !== "length" && output.stopReason !== "error" && output.stopReason !== "aborted";
1633
+ for (const delta of reasoningTagTextPartitioner.flush({ recoverUnclosed })) appendPartitionedVisibleDelta(delta);
1634
+ };
1635
+ const flushGemmaToolCallRecoverer = (allowRecovery = true) => {
1636
+ for (const part of gemmaToolCallRecoverer?.flush(allowRecovery) ?? []) if (part.kind === "toolCall") appendRecoveredToolCall(part);
1637
+ else for (const delta of reasoningTagTextPartitioner.pushVisible(part.text)) appendPartitionedVisibleDelta(delta);
1638
+ };
1639
+ const sealTextBeforeReasoning = () => {
1640
+ if (currentBlock?.type !== "text" && !reasoningTagTextPartitioner.hasPending()) return;
1641
+ flushReasoningTagTextPartitioner();
1642
+ if (currentBlock?.type !== "text") return;
1643
+ if (currentTextSource !== "reasoning_detail" && currentBlock.text.trim()) pendingInterruptedTextBlock = currentBlock;
1644
+ currentBlock = null;
1645
+ if (directMode) directContent.block = null;
1646
+ currentTextSource = void 0;
1647
+ };
1648
+ const beginReasoning = (hasFollowingVisibleText, forceStrict = false) => {
1649
+ if (!output.openclawDelivery?.textPhaseRequiresTerminal) output.openclawDelivery = {
1650
+ ...output.openclawDelivery,
1651
+ textPhaseRequiresTerminal: true
1652
+ };
1653
+ if (forceStrict || reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
1654
+ if (!hasFollowingVisibleText || !reasoningTagTextPartitioner.hasPendingSyntax()) sealTextBeforeReasoning();
1655
+ };
1656
+ const guardedStream = withFirstStreamEventTimeout(responseStream, {
1657
+ provider: model.provider,
1658
+ api: model.api,
1659
+ model: model.id,
1660
+ timeoutMs: options?.firstEventTimeoutMs ?? 0,
1661
+ stage: "completions",
1662
+ abort: options?.abortFirstEventStream,
1663
+ onTimeout: options?.onFirstEventTimeout,
1664
+ hint: "The provider may be stalled while parsing the tool payload; retry with a smaller tool surface or enable OPENCLAW_DEBUG_MODEL_PAYLOAD=tools to inspect exposed tools."
1665
+ });
1666
+ const events = directMode ? guardedStream : iterateModelStream(guardedStream, options?.signal);
1667
+ for await (const rawChunk of events) {
1668
+ throwIfModelStreamAborted(options?.signal);
1669
+ chunkPushedEvent = false;
1670
+ if (!rawChunk || typeof rawChunk !== "object") continue;
1671
+ const chunk = rawChunk;
1672
+ output.responseId ||= chunk.id;
1673
+ if (typeof chunk.model === "string" && chunk.model.length > 0 && chunk.model !== model.id) output.responseModel ||= chunk.model;
1674
+ const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
1675
+ const usage = chunk.usage || choice?.usage;
1676
+ const hasReasoningUsageActivity = Boolean(asPositiveFiniteNumber(usage?.completion_tokens_details?.reasoning_tokens));
1677
+ if (usage) output.usage = parseOpenAICompletionsUsage(usage, model, { includeReasoningTokens: !directMode });
1678
+ const rawChoiceDelta = choice?.delta ?? choice?.message;
1679
+ notifyLlmRequestActivity(options?.signal, Boolean(usage || choice?.finish_reason || rawChoiceDelta && (rawChoiceDelta.tool_calls?.length || hasOpenAICompletionsDeltaContent(rawChoiceDelta))));
1680
+ if (!choice) {
1681
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
1682
+ continue;
1683
+ }
1684
+ if (choice.finish_reason) {
1685
+ const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
1686
+ output.stopReason = finishReasonResult.stopReason;
1687
+ finishReason = finishReasonResult.stopReason;
1688
+ if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
1689
+ }
1690
+ if (!rawChoiceDelta) {
1691
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
1692
+ continue;
1693
+ }
1694
+ for (const normalizedDelta of normalizeToolCallDeltas(rawChoiceDelta, choice.finish_reason)) {
1695
+ const choiceDelta = normalizedDelta.delta;
1696
+ const deltaFields = choiceDelta;
1697
+ const reasoningBatch = readOpenAICompletionsReasoningBatch(deltaFields, visibleReasoningDetailTypes);
1698
+ const reasoningDeltas = reasoningBatch.deltas;
1699
+ const hasReasoningThinking = reasoningBatch.hasThinking;
1700
+ const contentDeltas = readOpenAICompletionsContentDeltas(choiceDelta.content, choiceDelta.refusal, reasoningBatch.mirroredThinking);
1701
+ const lastVisibleTextIndex = contentDeltas.findLastIndex((delta) => delta.kind === "text");
1702
+ const hasSameChunkVisibleText = reasoningBatch.hasVisibleText || lastVisibleTextIndex !== -1;
1703
+ if (hasReasoningThinking) {
1704
+ beginReasoning(hasSameChunkVisibleText, true);
1705
+ appendReasoningDeltas(reasoningDeltas);
1706
+ }
1707
+ for (const [contentDeltaIndex, contentDelta] of contentDeltas.entries()) if (contentDelta.kind === "text") {
1708
+ const parts = gemmaToolCallRecoverer?.push(contentDelta.text) ?? [contentDelta];
1709
+ for (const part of parts) {
1710
+ const routedDeltas = hasReasoningThinking ? reasoningTagTextPartitioner.push(part.text) : reasoningTagTextPartitioner.pushVisible(part.text);
1711
+ for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
1712
+ }
1713
+ } else {
1714
+ beginReasoning(contentDeltaIndex < lastVisibleTextIndex);
1715
+ if (emitReasoning) {
1716
+ if (currentBlock?.type === "toolCall" && !directMode) queuePostToolCallDelta(contentDelta);
1717
+ else appendContentDelta(contentDelta);
1718
+ }
1719
+ }
1720
+ if (!hasReasoningThinking) appendReasoningDeltas(reasoningDeltas);
1721
+ const toolCallDeltas = normalizedDelta.toolCalls;
1722
+ if (toolCallDeltas.length > 0) {
1723
+ flushGemmaToolCallRecoverer(false);
1724
+ sawNativeToolCallDelta = true;
1725
+ flushReasoningTagTextPartitioner();
1726
+ rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
1727
+ for (const toolCall of toolCallDeltas) {
1728
+ const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
1729
+ let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
1730
+ if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
1731
+ if (!block) {
1732
+ if (currentBlock?.type === "toolCall") {
1733
+ currentBlock = null;
1734
+ flushPendingPostToolCallDeltas();
1735
+ }
1736
+ const initialSig = directMode ? void 0 : extractToolCallThoughtSignature(toolCall);
1737
+ options?.beforeContentBlock?.("toolCall");
1738
+ if (directMode && directContent.block?.type === "thinking") directContent.block = null;
1739
+ block = {
1740
+ type: "toolCall",
1741
+ id: toolCall.id || "",
1742
+ name: toolCall.function?.name || "",
1743
+ arguments: {},
1744
+ partialArgs: "",
1745
+ ...initialSig ? { thoughtSignature: initialSig } : {}
1746
+ };
1747
+ encryptedReasoning.rememberToolCall(block.id, block);
1748
+ toolArgumentPreviewSchedules.set(block, createToolArgumentPreviewSchedule());
1749
+ appendToolCallBlock(block);
1750
+ }
1751
+ if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
1752
+ if (toolCall.id) {
1753
+ const previousId = block.id;
1754
+ if (!directMode || !block.id) block.id = toolCall.id;
1755
+ toolCallBlocksById.set(toolCall.id, block);
1756
+ if (block.id === toolCall.id) encryptedReasoning.rememberToolCall(toolCall.id, block, previousId);
1757
+ }
1758
+ currentBlock = block;
1759
+ const conflictingId = directMode && block.id && toolCall.id && block.id !== toolCall.id;
1760
+ if (toolCall.function?.name && !conflictingId) block.name = toolCall.function.name;
1761
+ const deltaSig = directMode ? void 0 : extractToolCallThoughtSignature(toolCall);
1762
+ if (deltaSig) block.thoughtSignature = deltaSig;
1763
+ const toolArgumentsDelta = toolCall.function?.arguments;
1764
+ if (toolArgumentsDelta) {
1765
+ block.partialArgs += toolArgumentsDelta;
1766
+ if (toolArgumentPreviewSchedules.get(block)?.(block.partialArgs.length)) block.arguments = parseStreamingJson(block.partialArgs);
1767
+ }
1768
+ if (toolArgumentsDelta || directMode) pushStreamEvent({
1769
+ type: "toolcall_delta",
1770
+ contentIndex: blockIndices.get(block) ?? -1,
1771
+ delta: toolArgumentsDelta ?? "",
1772
+ partial: output
1773
+ });
1774
+ }
1775
+ }
1776
+ encryptedReasoning.consumeDetails(deltaFields.reasoning_details);
1777
+ }
1778
+ flushPendingPostToolCallDeltas();
1779
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
1780
+ }
1781
+ throwIfModelStreamAborted(options?.signal);
1782
+ if (!finishReason && (directMode || options?.sawStreamDONE?.() === false)) throw new Error("Stream ended without finish_reason");
1783
+ flushGemmaToolCallRecoverer();
1784
+ flushReasoningTagTextPartitioner();
1785
+ appendRecoveredParts(deepSeekToolCallRecoverer?.flush() ?? []);
1786
+ for (const part of deepSeekTextFilter?.flush() ?? []) appendVisibleTextDelta(part);
1787
+ currentBlock = null;
1788
+ flushPendingPostToolCallDeltas();
1789
+ finalizeOpenAICompletionsToolCalls(output, {
1790
+ allowSilentToolCallPromotion: finishReason === "stop" || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false),
1791
+ onConfirmedToolCall(block, contentIndex) {
1792
+ if (directMode) return;
1793
+ pushStreamEvent({
1794
+ type: "toolcall_end",
1795
+ contentIndex,
1796
+ toolCall: block,
1797
+ partial: output
1798
+ });
1799
+ }
1800
+ });
1801
+ if (confirmedInterruptedTextBlock && output.stopReason !== "toolUse" && output.stopReason !== "error" && output.stopReason !== "aborted") tagInterruptedTextPhases(output.content, confirmedInterruptedTextBlock, explicitVisibleTextBlocks);
1802
+ if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
1803
+ if (output.stopReason === "error" || output.stopReason === "aborted") tagUnresolvedTextAsCommentary(output);
1804
+ if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
1805
+ if (!output.usage.contextUsage && !options?.signal?.aborted && output.stopReason !== "error" && output.stopReason !== "aborted") {
1806
+ output.usage.contextUsage = { state: "unavailable" };
1807
+ if (!compat.supportsUsageInStreaming) warnMissingStreamingUsage(openAIModel);
1808
+ }
1809
+ }
1810
+ const MAX_MISSING_USAGE_HINT_KEYS = 256;
1811
+ const missingUsageHintKeys = /* @__PURE__ */ new Set();
1812
+ function warnMissingStreamingUsage(model) {
1813
+ if (detectOpenAICompletionsCompat(model).capabilities.endpointClass !== "custom") return;
1814
+ const key = `${model.provider}/${model.id}`;
1815
+ if (missingUsageHintKeys.has(key) || missingUsageHintKeys.size >= MAX_MISSING_USAGE_HINT_KEYS) return;
1816
+ missingUsageHintKeys.add(key);
1817
+ log.warn(`${key} returned no token usage; context size is estimated and token accounting is unavailable. If this endpoint supports stream_options.include_usage, set compat.supportsUsageInStreaming: true on the model in models.providers.${model.provider}.models.`, {
1818
+ provider: model.provider,
1819
+ model: model.id
1820
+ });
1821
+ }
1822
+ function shouldEmitOpenAICompletionsReasoning(model, options) {
1823
+ if (!model.reasoning) return false;
1824
+ const effort = options?.reasoningEffort ?? options?.reasoning ?? "high";
1825
+ return Boolean(effort) && isOpenAICompletionsThinkingEnabled(effort);
1826
+ }
1827
+ //#endregion
1828
+ //#region packages/ai/src/providers/openai-web-search-tools.ts
1829
+ function hasResponsesWebSearchTool(tools) {
1830
+ if (!Array.isArray(tools)) return false;
1831
+ return tools.some((tool) => {
1832
+ if (!isRecord(tool)) return false;
1833
+ if (tool.type === "web_search") return true;
1834
+ if (tool.type === "function" && tool.name === "web_search") return true;
1835
+ const fn = tool.function;
1836
+ return isRecord(fn) && fn.name === "web_search";
1837
+ });
1838
+ }
1839
+ //#endregion
1840
+ //#region packages/ai/src/transports/openai-responses-params-internal.ts
1841
+ const OPENAI_RESPONSES_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set([
1842
+ "openai",
1843
+ "opencode",
1844
+ "azure-openai-responses",
1845
+ "github-copilot"
1846
+ ]);
1847
+ function raiseMinimalReasoningForResponsesWebSearch(params) {
1848
+ if (params.effort !== "minimal" || !hasResponsesWebSearchTool(params.tools)) return params.effort;
1849
+ for (const effort of [
1850
+ "low",
1851
+ "medium",
1852
+ "high"
1853
+ ]) {
1854
+ const resolved = resolveOpenAIRequestReasoning(params.model, effort).effort;
1855
+ if (resolved && resolved !== "none" && resolved !== "minimal") return resolved;
1856
+ }
1857
+ return params.effort;
1858
+ }
1859
+ const OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS = [
1860
+ "max_output_tokens",
1861
+ "metadata",
1862
+ "prompt_cache_retention",
1863
+ "prompt_cache_options",
1864
+ "service_tier",
1865
+ "temperature",
1866
+ "top_p"
1867
+ ];
1868
+ function stripOpenAICodexResponsesUnsupportedTextFields(params) {
1869
+ const text = params.text;
1870
+ if (!text || typeof text !== "object" || Array.isArray(text)) return;
1871
+ const sanitizedText = { ...text };
1872
+ delete sanitizedText.format;
1873
+ if (Object.keys(sanitizedText).length > 0) params.text = sanitizedText;
1874
+ else delete params.text;
1875
+ }
1876
+ function sanitizeOpenAICodexResponsesParams(model, params) {
1877
+ if (!usesNativeOpenAICodexResponsesBackend(model)) return params;
1878
+ for (const key of OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS) delete params[key];
1879
+ Object.assign(params, { store: false });
1880
+ stripOpenAICodexResponsesUnsupportedTextFields(params);
1881
+ return params;
1882
+ }
1883
+ function buildOpenAIResponsesInstructionsText(context) {
1884
+ if (!context.systemPrompt) return;
1885
+ return sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt));
1886
+ }
1887
+ function resolveOpenAIResponsesInstructions(model, context, usesInstructionsField) {
1888
+ if (!usesInstructionsField) return;
1889
+ const instructions = buildOpenAIResponsesInstructionsText(context);
1890
+ if (instructions && instructions.trim().length > 0) return instructions;
1891
+ return usesNativeOpenAICodexResponsesBackend(model) ? OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS : void 0;
1892
+ }
1893
+ function buildOpenAIResponsesCompactSystemMessage(model, instructions) {
1894
+ const compat = getCompat(model);
1895
+ const role = model.reasoning && compat.supportsDeveloperRole ? "developer" : "system";
1896
+ return buildResponsesInputMessage(role, [{
1897
+ type: "input_text",
1898
+ text: instructions
1899
+ }]);
1900
+ }
1901
+ function ensureOpenAIResponsesNonEmptyInput(messages, context) {
1902
+ if (messages.length > 0 || !context.systemPrompt) return;
1903
+ if (!buildOpenAIResponsesInstructionsText(context)) throw new Error("OpenAI Responses requires non-empty input when only systemPrompt is provided.");
1904
+ messages.push(buildResponsesInputMessage("user", [{
1905
+ type: "input_text",
1906
+ text: " "
1907
+ }]));
1908
+ }
1909
+ function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint") {
1910
+ const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "transport-default" });
1911
+ const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
1912
+ const replayResponsesItemIds = !usesNativeOpenAICodexResponsesBackend(model) && (options?.replayResponsesItemIds ?? policyAllowsReplayIds);
1913
+ const messages = convertResponsesMessages(model, context, OPENAI_RESPONSES_TOOL_CALL_PROVIDERS, {
1914
+ includeSystemPrompt: !payloadPolicy.usesInstructionsField,
1915
+ replayReasoningItems: true,
1916
+ replayResponsesItemIds,
1917
+ authProfileId: options?.authProfileId,
1918
+ sessionId: options?.sessionId,
1919
+ replayMode
1920
+ });
1921
+ ensureOpenAIResponsesNonEmptyInput(messages, context);
1922
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention);
1923
+ const compat = getCompat(model);
1924
+ const promptCacheKey = compat.supportsPromptCacheKey ? resolvePromptCacheKey(options, cacheRetention) : void 0;
1925
+ const instructions = resolveOpenAIResponsesInstructions(model, context, payloadPolicy.usesInstructionsField);
1926
+ const params = {
1927
+ model: model.id,
1928
+ input: messages,
1929
+ stream: true,
1930
+ prompt_cache_key: promptCacheKey,
1931
+ ...resolveOpenAIPromptCacheParams(model, cacheRetention, compat),
1932
+ ...instructions ? { instructions } : {},
1933
+ ...metadata ? { metadata } : {}
1934
+ };
1935
+ const effectiveMaxTokens = options?.maxTokens || model.maxTokens;
1936
+ if (effectiveMaxTokens) params.max_output_tokens = Math.max(effectiveMaxTokens, 16);
1937
+ if (options?.temperature !== void 0 && supportsOpenAITemperature(model)) params.temperature = options.temperature;
1938
+ if (options?.topP !== void 0 && (!isOpenAIGpt6Model(model) || resolveOpenAIThinkingApi(model.api) === "azure-openai-responses")) params.top_p = options.topP;
1939
+ if (options?.responseFormat !== void 0) params.text = {
1940
+ ...params.text,
1941
+ format: resolveOpenAIResponsesTextFormat(options.responseFormat)
1942
+ };
1943
+ if (options?.serviceTier !== void 0 && payloadPolicy.allowsServiceTier) params.service_tier = options.serviceTier;
1944
+ if (context.tools) {
1945
+ const tools = context.tools;
1946
+ const strict = getAiTransportHost().resolveOpenAIStrictToolSetting(model, {
1947
+ transport: "stream",
1948
+ supportsStrictMode: compat.supportsStrictMode
1949
+ });
1950
+ const { projection, tools: converted } = prepareResponsesTools(tools, strict, model);
1951
+ if (converted.length > 0 || projection.inputToolCount === 0 && projection.diagnostics.length === 0) params.tools = converted;
1952
+ if (options?.toolChoice) {
1953
+ const toolChoice = reconcileOpenAIResponsesToolChoice(options.toolChoice, projection);
1954
+ if (toolChoice !== void 0) params.tool_choice = toolChoice;
1955
+ }
1956
+ }
1957
+ if (model.reasoning) {
1958
+ const reasoning = options?.reasoning;
1959
+ const requestedEffort = options?.reasoningEffort ?? (reasoning === "none" ? "none" : resolveOpenAISimpleReasoningEffort(model, reasoning)) ?? (options?.reasoningSummary ? "high" : payloadPolicy.defaultManagedReasoningEffort);
1960
+ const resolvedEffort = requestedEffort === void 0 ? void 0 : resolveOpenAIRequestReasoning(model, requestedEffort).effort;
1961
+ if (resolvedEffort !== void 0) {
1962
+ const effort = raiseMinimalReasoningForResponsesWebSearch({
1963
+ model,
1964
+ effort: resolvedEffort,
1965
+ tools: params.tools
1966
+ });
1967
+ const summary = effort !== "none" && (options?.reasoningEffort || options?.reasoning || options?.reasoningSummary) ? options.reasoningSummary || "auto" : void 0;
1968
+ params.reasoning = {
1969
+ effort,
1970
+ ...summary ? { summary } : {}
1971
+ };
1972
+ if (summary) params.include = ["reasoning.encrypted_content"];
1973
+ }
1974
+ }
1975
+ applyOpenAIResponsesPayloadPolicy(params, payloadPolicy);
1976
+ return sanitizeOpenAICodexResponsesParams(model, params);
1977
+ }
1978
+ //#endregion
1979
+ //#region packages/ai/src/transports/openai-responses-compact-client.ts
1980
+ async function postOpenAIResponsesCompaction(params) {
1981
+ const compactInput = typeof params.request.instructions === "string" && params.request.instructions.length > 0 ? [buildOpenAIResponsesCompactSystemMessage(params.model, params.request.instructions), ...params.request.input ?? []] : params.request.input;
1982
+ const response = await params.client.post("/responses/compact", {
1983
+ ...buildOpenAISdkRequestOptions(params.model, params.options?.signal, { timeoutMs: params.options?.timeoutMs }),
1984
+ body: {
1985
+ model: params.request.model,
1986
+ input: compactInput
1987
+ }
1988
+ });
1989
+ const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
1990
+ const item = output.at(-1);
1991
+ const retainedItems = output.slice(0, -1);
1992
+ const retainedUserMessageCount = retainedItems.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user" && Array.isArray(candidate.content)).length;
1993
+ const inputUserMessageCount = Array.isArray(params.request.input) ? params.request.input.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user").length : 0;
1994
+ const retainedMessagePrefixSupported = supportsNativeOpenAIResponsesEndpoint(params.model);
1995
+ const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
1996
+ if (!isRecord(response) || response.object !== "response.compaction" || !isOpenAIResponsesCompactionOutput(output, params.model) || retainedItems.length > 0 && (!retainedMessagePrefixSupported || retainedUserMessageCount !== inputUserMessageCount) || !isRecord(item) || item.type !== "compaction" || typeof item.encrypted_content !== "string" || item.encrypted_content.length === 0 || !usage || typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") throw new Error("Responses compact endpoint did not return one trailing compaction item");
1997
+ return {
1998
+ output,
1999
+ item,
2000
+ historyMode: retainedUserMessageCount > 0 ? "retained-users" : "compacted-prefix",
2001
+ usage,
2002
+ model: params.model,
2003
+ replayMetadata: buildOpenAIResponsesReasoningReplayMetadata(params.model, {
2004
+ authProfileId: params.options?.authProfileId,
2005
+ sessionId: params.options?.sessionId
2006
+ })
2007
+ };
2008
+ }
2009
+ //#endregion
2010
+ //#region packages/ai/src/transports/openai-responses-compact-request.ts
2011
+ const COMPACT_REQUEST = Symbol("openaiResponsesCompactRequest");
2012
+ function claimResponsesCompactRequest(options) {
2013
+ const controller = options ? Reflect.get(options, COMPACT_REQUEST) : void 0;
2014
+ if (controller?.claimed === false) {
2015
+ controller.claimed = true;
2016
+ return controller;
2017
+ }
2018
+ }
2019
+ /** Run a compact-endpoint request through the session's prepared stream stack. */
2020
+ async function requestPreparedOpenAIResponsesCompaction(streamFn, model, context, options) {
2021
+ const preparedOptions = { ...options };
2022
+ let resolveResult;
2023
+ let rejectResult;
2024
+ const result = new Promise((resolve, reject) => {
2025
+ resolveResult = resolve;
2026
+ rejectResult = reject;
2027
+ });
2028
+ const controller = {
2029
+ claimed: false,
2030
+ resolve: resolveResult,
2031
+ reject: rejectResult
2032
+ };
2033
+ Reflect.set(preparedOptions, COMPACT_REQUEST, controller);
2034
+ const stream = await Promise.resolve(streamFn(model, context, preparedOptions));
2035
+ if (!controller.claimed) throw new Error("Prepared stream did not reach an OpenAI Responses transport");
2036
+ try {
2037
+ return await result;
2038
+ } finally {
2039
+ await stream.result().catch(() => void 0);
2040
+ }
2041
+ }
2042
+ //#endregion
2043
+ //#region packages/ai/src/transports/openai-responses-reasoning-state.ts
2044
+ /** Save only admitted settings and hashes, never another copy of the conversation. */
2045
+ function recordResponsesReasoningState(message, model, identity, request, output) {
2046
+ if (!supportsResponsesReasoningUpdate(request) || !isRecord(request.reasoning) || !request.input || request.previous_response_id || message.providerReplay || output.some((item) => isRecord(item) && item.type === "compaction")) return;
2047
+ const reasoning = {
2048
+ ...buildProviderReplayContext(model, identity),
2049
+ effort: request.reasoning.effort,
2050
+ controls: request.input.flatMap((item, index) => isConfigurationUpdate(item) ? [{
2051
+ index,
2052
+ item
2053
+ }] : []),
2054
+ inputLength: request.input.length,
2055
+ outputLength: output.length,
2056
+ prefixHash: responsesContinuationPrefixFingerprint(request.input, output),
2057
+ requestHash: responsesContinuationRequestFingerprint(request)
2058
+ };
2059
+ Object.assign(message, { openclawResponsesInputReplay: {
2060
+ ...readResponsesInputReplayState(message),
2061
+ reasoning
2062
+ } });
2063
+ }
2064
+ /** A cold transport can replay controls, but cannot resurrect a server response handle. */
2065
+ function restoreResponsesReasoningState(context, model, identity, request) {
2066
+ const latest = context.messages.findLast((message) => message.role === "assistant");
2067
+ const state = latest ? readResponsesInputReplayState(latest)?.reasoning : void 0;
2068
+ if (!isRecord(state)) return request;
2069
+ const { effort, inputLength, outputLength, controls, prefixHash, requestHash } = state;
2070
+ if (!isProviderReplayContext(state) || !providerReplayContextMatches(state, buildProviderReplayContext(model, identity)) || latest?.providerReplay || !supportsResponsesReasoningUpdate(request) || !isRecord(request.reasoning) || !request.input || request.previous_response_id || request.input.some(isConfigurationUpdate) || typeof effort !== "string" || typeof inputLength !== "number" || !Number.isSafeInteger(inputLength) || inputLength < 0 || typeof outputLength !== "number" || !Number.isSafeInteger(outputLength) || outputLength < 0 || !Array.isArray(controls) || controls.length > inputLength || inputLength + outputLength > request.input.length + controls.length) return request;
2071
+ const previousInput = request.input.slice(0, inputLength - controls.length);
2072
+ let lastIndex = -1;
2073
+ for (const control of controls) {
2074
+ if (!isRecord(control) || typeof control.index !== "number" || !Number.isSafeInteger(control.index) || control.index <= lastIndex || control.index > previousInput.length || !isConfigurationUpdate(control.item)) return request;
2075
+ previousInput.splice(control.index, 0, control.item);
2076
+ lastIndex = control.index;
2077
+ }
2078
+ const previous = {
2079
+ ...request,
2080
+ reasoning: {
2081
+ ...request.reasoning,
2082
+ effort
2083
+ },
2084
+ input: previousInput
2085
+ };
2086
+ if (responsesContinuationRequestFingerprint(previous) !== requestHash) return request;
2087
+ const prepared = replayResponsesReasoningUpdates(previous, request, outputLength);
2088
+ if (responsesContinuationPrefixFingerprint((prepared.input ?? []).slice(0, inputLength + outputLength)) !== prefixHash) return request;
2089
+ return prepared;
2090
+ }
2091
+ //#endregion
2092
+ //#region packages/ai/src/transports/openai-responses-request-fetch.ts
2093
+ function withDefaultResponsesStreamEncoding(fetch) {
2094
+ return (input, init) => {
2095
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
2096
+ if (headers.has("accept-encoding")) return fetch(input, init);
2097
+ headers.set("accept-encoding", "identity");
2098
+ return fetch(input, {
2099
+ ...init,
2100
+ headers
2101
+ });
2102
+ };
2103
+ }
2104
+ /** Preserve the existing fetch policy while binding explicit continuation dispatch to its signal. */
2105
+ function createResponsesRequestFetch(model, options) {
2106
+ const lifecycle = options.lifecycle;
2107
+ if (lifecycle && options.compact) throw new Error("Provider review continuation cannot replace its reviewed input with compaction");
2108
+ let fetchOverride = options.compact ? createBoundedOpenAIResponsesCompactionFetch(buildGuardedModelFetch(model)) : options.stream ? withDefaultResponsesStreamEncoding(buildGuardedModelFetch(model, void 0, { onSseComment: options.onSseComment })) : void 0;
2109
+ if (lifecycle) {
2110
+ const dispatchFetch = fetchOverride ?? buildGuardedModelFetch(model);
2111
+ fetchOverride = async (input, init) => {
2112
+ const signal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
2113
+ await lifecycle.beforeDispatch(signal ?? void 0);
2114
+ signal?.throwIfAborted();
2115
+ lifecycle.assertCurrent();
2116
+ return dispatchFetch(input, init);
2117
+ };
2118
+ }
2119
+ return fetchOverride;
2120
+ }
2121
+ //#endregion
2122
+ //#region packages/ai/src/transports/openai-responses-steering.ts
2123
+ function cloneWireRequest(request) {
2124
+ const serialized = JSON.stringify(request);
2125
+ return JSON.parse(serialized);
2126
+ }
2127
+ async function projectResponsesSteeringInput(request, project) {
2128
+ const { input: activeInput, ...activeSettings } = cloneWireRequest(request);
2129
+ if (!Array.isArray(activeInput)) return [];
2130
+ const { input, ...settings } = cloneWireRequest(await project());
2131
+ if (!Array.isArray(input) || stableStringify(settings) !== stableStringify(activeSettings) || stableStringify(input.slice(0, activeInput.length)) !== stableStringify(activeInput)) return [];
2132
+ const projected = input.slice(activeInput.length);
2133
+ return projected.every((item) => isRecord(item) && item.role === "user") ? projected : [];
2134
+ }
2135
+ /** One response owns admission; accepted input stays on this connection until continuation. */
2136
+ function createResponsesSteering(params) {
2137
+ let responseId;
2138
+ let sealed = false;
2139
+ let unsubscribe;
2140
+ const pending = [];
2141
+ const accepted = /* @__PURE__ */ new Map();
2142
+ const acknowledged = /* @__PURE__ */ new Set();
2143
+ const seal = () => {
2144
+ sealed = true;
2145
+ const cleanup = unsubscribe;
2146
+ unsubscribe = void 0;
2147
+ cleanup?.();
2148
+ };
2149
+ return {
2150
+ get responseId() {
2151
+ return responseId;
2152
+ },
2153
+ get pending() {
2154
+ return pending.length > 0;
2155
+ },
2156
+ get acceptedInput() {
2157
+ return [...accepted.values()].flat();
2158
+ },
2159
+ seal,
2160
+ close(error) {
2161
+ seal();
2162
+ for (const submission of pending.splice(0)) submission.reject(error);
2163
+ },
2164
+ handle(event) {
2165
+ if (!isRecord(event)) return false;
2166
+ if (event.type === "response.created" && !responseId && !sealed) {
2167
+ const response = isRecord(event.response) ? event.response : void 0;
2168
+ if (typeof response?.id !== "string" || !response.id.trim()) throw new Error("Responses steering requires a response identity");
2169
+ const activeResponseId = response.id;
2170
+ responseId = activeResponseId;
2171
+ const cleanup = params.onActiveResponse({
2172
+ needsContinuation: params.needsContinuation,
2173
+ steer(messages) {
2174
+ if (sealed || messages.length === 0) return Promise.resolve(false);
2175
+ params.assertActive();
2176
+ const converted = params.toInput(messages);
2177
+ const submit = (input) => {
2178
+ if (sealed || input.length === 0) return Promise.resolve(false);
2179
+ params.assertActive();
2180
+ return new Promise((resolve, reject) => {
2181
+ pending.push({
2182
+ input,
2183
+ resolve,
2184
+ reject
2185
+ });
2186
+ params.send({
2187
+ type: "response.steer",
2188
+ previous_response_id: activeResponseId,
2189
+ input
2190
+ });
2191
+ });
2192
+ };
2193
+ return Array.isArray(converted) ? submit(converted) : converted.then(submit);
2194
+ }
2195
+ });
2196
+ if (sealed) cleanup?.();
2197
+ else unsubscribe = cleanup;
2198
+ return false;
2199
+ }
2200
+ if (event.type !== "response.steer.accepted" && event.type !== "response.steer.failed" && event.type !== "response.steer.pending") return false;
2201
+ const steer = isRecord(event.steer) ? event.steer : void 0;
2202
+ if (!responseId || steer?.previous_response_id !== responseId) throw new Error("Responses steering acknowledgement has an unexpected identity");
2203
+ if (event.type === "response.steer.failed" && steer.id === void 0) {
2204
+ const index = pending.findIndex((submission) => stableStringify(submission.input) === stableStringify(steer.input));
2205
+ const submission = pending[index];
2206
+ if (!submission) throw new Error("Responses steering acknowledgement has no pending submission");
2207
+ pending.splice(index, 1);
2208
+ submission.resolve(false);
2209
+ return true;
2210
+ }
2211
+ if (typeof steer.id !== "string" || !steer.id.trim()) throw new Error("Responses steering acknowledgement has an unexpected identity");
2212
+ if (event.type === "response.steer.pending") {
2213
+ if (!accepted.has(steer.id)) throw new Error("Responses steering pending event has no accepted submission");
2214
+ return true;
2215
+ }
2216
+ if (event.type === "response.steer.failed" && accepted.has(steer.id)) throw new Error("OpenAI could not apply accepted steering; the queued message remains in the conversation");
2217
+ if (acknowledged.has(steer.id)) throw new Error("Responses steering acknowledgement was already applied");
2218
+ const submission = pending.shift();
2219
+ if (!submission) throw new Error("Responses steering acknowledgement has no pending submission");
2220
+ acknowledged.add(steer.id);
2221
+ if (event.type === "response.steer.accepted") {
2222
+ accepted.set(steer.id, submission.input);
2223
+ submission.resolve(true);
2224
+ } else submission.resolve(false);
2225
+ return true;
2226
+ }
2227
+ };
2228
+ }
2229
+ /** Accepted steering is prepended by the server, so never repeat it in response.create. */
2230
+ function omitAcceptedSteering(input, accepted) {
2231
+ const remaining = [...input];
2232
+ for (const message of accepted) {
2233
+ const index = remaining.findIndex((item) => stableStringify(item) === stableStringify(message));
2234
+ if (index < 0) throw new Error("Responses steering continuation no longer contains the accepted user input");
2235
+ remaining.splice(index, 1);
2236
+ }
2237
+ return remaining;
2238
+ }
2239
+ //#endregion
2240
+ //#region packages/ai/src/transports/openai-responses-websocket-recovery.ts
2241
+ /** Owns safe WebSocket rejection recovery; transport selection and session leases stay with the caller. */
2242
+ function createRecoverableResponsesWebSocketStream(params) {
2243
+ const { trackedWebSocketStream, websocket, websocketSignal, options, output, buildRequest, createSseStream, closeWebSocketForFallback, logWebSocketFallback, startStream } = params;
2244
+ return { async *[Symbol.asyncIterator]() {
2245
+ let acceptance = "pending";
2246
+ let outputObserved = false;
2247
+ try {
2248
+ for await (const event of trackedWebSocketStream) {
2249
+ const { failure, hasResponseOutput, isPrelude } = readResponsesRecoveryEvent(event);
2250
+ outputObserved ||= hasResponseOutput;
2251
+ if (isResponsesServiceTierRejection(failure) && !websocket.hasActiveResponse) throw new OpenAIResponsesWebSocketSafeRetryError("invalid_request_error", void 0, "service_tier", "Invalid service_tier argument", failure);
2252
+ outputObserved ||= !isPrelude;
2253
+ if (acceptance === "pending") {
2254
+ acceptance = "observing";
2255
+ await notifyProviderStreamOpened({
2256
+ options,
2257
+ cancelStream: () => websocket.finish({ keep: false })
2258
+ });
2259
+ acceptance = "accepted";
2260
+ }
2261
+ startStream();
2262
+ yield event;
2263
+ }
2264
+ } catch (error) {
2265
+ if (error instanceof OpenAIResponsesWebSocketSafeRetryError) {
2266
+ if (isResponsesServiceTierRejection(error)) {
2267
+ const nextTier = nextResponsesServiceTier(websocket.request.service_tier, error);
2268
+ if (!nextTier || outputObserved || acceptance === "observing" || output.content.length || websocketSignal.aborted || websocket.hasActiveResponse) throw error;
2269
+ closeWebSocketForFallback("service_tier_rejected");
2270
+ const rejectedTier = websocket.request.service_tier;
2271
+ if (rejectedTier === "ultrafast" || rejectedTier === "priority") responsesServiceTierObserver.reject(options, rejectedTier);
2272
+ yield* await createSseStream({
2273
+ ...await buildRequest("full-history"),
2274
+ service_tier: nextTier
2275
+ });
2276
+ return;
2277
+ }
2278
+ const encryptedContentRejected = isInvalidEncryptedContentError(error);
2279
+ const recovery = encryptedContentRejected ? await resolveNextResponsesEncryptedContentAttempt({
2280
+ kind: "initial",
2281
+ request: {
2282
+ ...websocket.request,
2283
+ stream: true
2284
+ }
2285
+ }, error, { buildFullHistoryRequest: () => buildRequest("full-history") }) : void 0;
2286
+ if (encryptedContentRejected && !recovery) throw error;
2287
+ closeWebSocketForFallback(`safe_server_error code=${error.code} status=${safeDebugValue(error.status)} param=${safeDebugValue(error.param)}`);
2288
+ yield* await createSseStream(recovery?.request ?? await buildRequest("full-history"), recovery?.kind ?? "continuation-rejected", recovery?.rejectedCompaction);
2289
+ return;
2290
+ }
2291
+ if (websocketSignal.aborted || !(error instanceof OpenAIResponsesWebSocketPreDispatchError)) throw error;
2292
+ params.setTransportToSse();
2293
+ logWebSocketFallback("before_first_event");
2294
+ yield* await createSseStream();
2295
+ }
2296
+ } };
2297
+ }
2298
+ //#endregion
2299
+ //#region packages/ai/src/transports/openai-responses-websocket.ts
2300
+ const SESSION_WEBSOCKET_CACHE_TTL_MS = 3e5;
2301
+ const SESSION_WEBSOCKET_MAX_AGE_MS = 33e5;
2302
+ const WEBSOCKET_OPEN_STATE = 1;
2303
+ const websocketSessionCache = /* @__PURE__ */ new Map();
2304
+ const degradedWebSocketConnections = /* @__PURE__ */ new Map();
2305
+ function closeWebSocketSilently(socket, reason = "done") {
2306
+ try {
2307
+ socket.close({
2308
+ code: 1e3,
2309
+ reason
2310
+ });
2311
+ } catch {}
2312
+ }
2313
+ function invalidateOwnedWebSocketSession(cacheKey, entry, reason = "done") {
2314
+ if (entry.idleTimer) {
2315
+ clearTimeout(entry.idleTimer);
2316
+ entry.idleTimer = void 0;
2317
+ }
2318
+ closeWebSocketSilently(entry.socket, reason);
2319
+ entry.steeringContinuation?.steering.close(/* @__PURE__ */ new Error("Responses steering connection closed"));
2320
+ entry.steeringContinuation?.iterator.return?.().catch(() => void 0);
2321
+ entry.steeringContinuation = void 0;
2322
+ if (websocketSessionCache.get(cacheKey) === entry) websocketSessionCache.delete(cacheKey);
2323
+ }
2324
+ function scheduleSessionWebSocketExpiry(cacheKey, entry) {
2325
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
2326
+ entry.idleTimer = setTimeout(() => {
2327
+ if (entry.busy) return;
2328
+ invalidateOwnedWebSocketSession(cacheKey, entry, "idle_timeout");
2329
+ }, SESSION_WEBSOCKET_CACHE_TTL_MS);
2330
+ entry.idleTimer.unref?.();
2331
+ }
2332
+ function prepareWebSocketConnection(client, headers) {
2333
+ if (!isOfficialOpenAIResponsesBaseUrl(client.baseURL)) throw new Error("OpenAI Responses WebSocket requires the official API endpoint");
2334
+ if (typeof client.apiKey !== "string" || client.apiKey.length === 0) throw new Error("OpenAI Responses WebSocket requires an API key");
2335
+ const owner = getAiTransportHost();
2336
+ const resolvedApiKey = owner.resolveSecretSentinel(client.apiKey);
2337
+ const resolvedHeaders = { ...resolveAiTransportHeaderSentinels(headers) };
2338
+ for (const key of Object.keys(resolvedHeaders)) {
2339
+ const normalizedKey = key.toLowerCase();
2340
+ if (normalizedKey === "authorization" || normalizedKey === "traceparent") delete resolvedHeaders[key];
2341
+ }
2342
+ if (!resolvedApiKey) throw new Error("OpenAI Responses WebSocket requires a resolved API key");
2343
+ return {
2344
+ client: client.withOptions({ apiKey: resolvedApiKey }),
2345
+ headers: resolvedHeaders,
2346
+ owner,
2347
+ ownerId: getSessionResourceOwnerId(owner),
2348
+ identity: sha256Hex(JSON.stringify([
2349
+ resolvedApiKey,
2350
+ client.baseURL,
2351
+ Object.entries(resolvedHeaders).toSorted(([a], [b]) => a.localeCompare(b))
2352
+ ]))
2353
+ };
2354
+ }
2355
+ function createWebSocket(connection, onError) {
2356
+ const socket = new ResponsesWS(connection.client, {
2357
+ headers: connection.headers,
2358
+ maxQueueSize: 1
2359
+ });
2360
+ socket.on("error", () => onError(socket));
2361
+ return socket;
2362
+ }
2363
+ function createTransientWebSocketLease(connection) {
2364
+ const socket = createWebSocket(connection, (failedSocket) => closeWebSocketSilently(failedSocket, "transport_error"));
2365
+ return {
2366
+ socket,
2367
+ iterator: socket.stream(),
2368
+ reusedConnection: false,
2369
+ release: () => closeWebSocketSilently(socket)
2370
+ };
2371
+ }
2372
+ function createCachedWebSocketLease(cacheKey, entry, reusedConnection) {
2373
+ entry.busy = true;
2374
+ const steeringContinuation = entry.steeringContinuation;
2375
+ entry.steeringContinuation = void 0;
2376
+ return {
2377
+ socket: entry.socket,
2378
+ iterator: steeringContinuation?.iterator ?? entry.socket.stream(),
2379
+ entry,
2380
+ reusedConnection,
2381
+ steeringContinuation,
2382
+ release: ({ keep } = {}) => {
2383
+ if (!keep || entry.socket.socket.readyState !== WEBSOCKET_OPEN_STATE) {
2384
+ invalidateOwnedWebSocketSession(cacheKey, entry);
2385
+ return;
2386
+ }
2387
+ entry.busy = false;
2388
+ scheduleSessionWebSocketExpiry(cacheKey, entry);
2389
+ }
2390
+ };
2391
+ }
2392
+ function acquireWebSocket(params, connection) {
2393
+ if (params.mode === "websocket" || !params.sessionId) return createTransientWebSocketLease(connection);
2394
+ const cacheKey = `${connection.ownerId}\0${params.sessionId}\0${connection.identity}`;
2395
+ const cached = websocketSessionCache.get(cacheKey);
2396
+ if (cached) {
2397
+ if (cached.idleTimer) {
2398
+ clearTimeout(cached.idleTimer);
2399
+ cached.idleTimer = void 0;
2400
+ }
2401
+ if (cached.busy) return createTransientWebSocketLease(connection);
2402
+ const expired = Date.now() - cached.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
2403
+ if (!expired && cached.socket.socket.readyState === WEBSOCKET_OPEN_STATE) return createCachedWebSocketLease(cacheKey, cached, true);
2404
+ invalidateOwnedWebSocketSession(cacheKey, cached, expired ? "connection_age_limit" : "done");
2405
+ }
2406
+ const entry = {
2407
+ socket: createWebSocket(connection, (failedSocket) => {
2408
+ const failedEntry = websocketSessionCache.get(cacheKey);
2409
+ if (failedEntry?.socket === failedSocket) invalidateOwnedWebSocketSession(cacheKey, failedEntry, "transport_error");
2410
+ else closeWebSocketSilently(failedSocket, "transport_error");
2411
+ }),
2412
+ sessionId: params.sessionId,
2413
+ owner: connection.owner,
2414
+ busy: true,
2415
+ createdAt: Date.now()
2416
+ };
2417
+ websocketSessionCache.set(cacheKey, entry);
2418
+ return createCachedWebSocketLease(cacheKey, entry, false);
2419
+ }
2420
+ function sanitizeWebSocketRequest(request) {
2421
+ const { stream: _stream, background: _background, ...websocketRequest } = request;
2422
+ return websocketRequest;
2423
+ }
2424
+ async function nextWebSocketMessage(iterator, signal) {
2425
+ if (signal?.aborted) throw transportAbortError(signal);
2426
+ return await racePromiseWithAbortSignal(iterator.next(), signal, transportAbortError);
2427
+ }
2428
+ function readServerEvent(message) {
2429
+ if (message.type === "message") return message.message;
2430
+ if (message.type === "error") throw parseOpenAIResponsesWebSocketServerError(message.error) ?? new Error("OpenAI Responses WebSocket transport failed", { cause: message.error });
2431
+ if (message.type === "close") throw new Error(`OpenAI Responses WebSocket closed before completion (code ${message.code})`);
2432
+ }
2433
+ function createOpenAIResponsesWebSocketStream(params) {
2434
+ const connection = prepareWebSocketConnection(params.client, params.headers);
2435
+ let fullRequest = sanitizeWebSocketRequest(params.request);
2436
+ const requestModel = typeof fullRequest.model === "string" ? fullRequest.model : "";
2437
+ const degradationKey = `${connection.ownerId}\0${params.sessionId ?? ""}\0${connection.identity}\0${requestModel}`;
2438
+ const degraded = degradedWebSocketConnections.get(degradationKey);
2439
+ if (degraded && degraded.retryAt > Date.now()) throw new OpenAIResponsesWebSocketPreDispatchError(/* @__PURE__ */ new Error("OpenAI Responses WebSocket is cooling down after a transport failure"));
2440
+ if (degraded) clearTimeout(degraded.expiryTimer);
2441
+ degradedWebSocketConnections.delete(degradationKey);
2442
+ const markDegraded = () => {
2443
+ const cooldownMs = params.degradeCooldownMs;
2444
+ if (cooldownMs === void 0 || !Number.isFinite(cooldownMs) || cooldownMs <= 0) return;
2445
+ const entry = {
2446
+ sessionId: params.sessionId,
2447
+ owner: connection.owner,
2448
+ retryAt: Date.now() + cooldownMs,
2449
+ expiryTimer: setTimeout(() => {
2450
+ if (degradedWebSocketConnections.get(degradationKey) === entry) degradedWebSocketConnections.delete(degradationKey);
2451
+ }, cooldownMs)
2452
+ };
2453
+ entry.expiryTimer.unref?.();
2454
+ degradedWebSocketConnections.set(degradationKey, entry);
2455
+ };
2456
+ let lease;
2457
+ try {
2458
+ lease = acquireWebSocket(params, connection);
2459
+ } catch (error) {
2460
+ markDegraded();
2461
+ throw new OpenAIResponsesWebSocketPreDispatchError(error);
2462
+ }
2463
+ let prepared;
2464
+ let previousContinuation;
2465
+ const resumedSteering = lease.steeringContinuation;
2466
+ const steeringMode = resumedSteering ? resumedSteering.requiresInput ? "required-input" : "automatic" : void 0;
2467
+ try {
2468
+ const continuation = previousContinuation = lease.entry?.continuation;
2469
+ if (continuation && lease.entry) {
2470
+ lease.entry.continuation = void 0;
2471
+ prepared = resolveResponsesContinuationRequest(continuation, fullRequest, steeringMode);
2472
+ } else {
2473
+ fullRequest = params.restoreRequest?.(fullRequest) ?? fullRequest;
2474
+ prepared = {
2475
+ request: fullRequest,
2476
+ continuationStatus: lease.entry ? "no_previous_response" : "socket_not_cached"
2477
+ };
2478
+ }
2479
+ } catch (error) {
2480
+ lease.iterator.return?.().catch(() => void 0);
2481
+ lease.release({ keep: false });
2482
+ throw error;
2483
+ }
2484
+ let streamStarted = false;
2485
+ let terminalResponse;
2486
+ let terminalReceived = false;
2487
+ let released = false;
2488
+ let retainIterator = false;
2489
+ let deferredInput = false;
2490
+ let inputReplay;
2491
+ if (resumedSteering) try {
2492
+ if (prepared.continuationStatus !== "continued" || !prepared.request.previous_response_id) throw new Error("Responses steering continuation changed its request or history");
2493
+ const input = omitAcceptedSteering(prepared.request.input ?? [], resumedSteering.acceptedInput);
2494
+ if (!resumedSteering.requiresInput && (stableStringify(fullRequest.instructions) !== stableStringify(resumedSteering.instructions) || stableStringify(fullRequest.tools) !== stableStringify(resumedSteering.tools))) throw new Error("Responses automatic steering continuation cannot change instructions or tools");
2495
+ const baseline = prepared.fullRequest ?? fullRequest;
2496
+ const priorLength = (baseline.input?.length ?? 0) - (prepared.request.input?.length ?? 0);
2497
+ const fingerprints = input.map(responsesInputFingerprint);
2498
+ if (fingerprints.length > 0) inputReplay = {
2499
+ afterResponseId: prepared.request.previous_response_id,
2500
+ before: resumedSteering.requiresInput ? fingerprints : [],
2501
+ after: resumedSteering.requiresInput ? [] : fingerprints
2502
+ };
2503
+ const delivered = resumedSteering.requiresInput ? input : [];
2504
+ prepared.fullRequest = {
2505
+ ...baseline,
2506
+ input: [
2507
+ ...(baseline.input ?? []).slice(0, priorLength),
2508
+ ...resumedSteering.acceptedInput,
2509
+ ...delivered
2510
+ ]
2511
+ };
2512
+ prepared.request = {
2513
+ ...prepared.request,
2514
+ input: delivered
2515
+ };
2516
+ deferredInput = !resumedSteering.requiresInput && input.length > 0;
2517
+ } catch (error) {
2518
+ lease.iterator.return?.().catch(() => void 0);
2519
+ lease.release({ keep: false });
2520
+ throw new OpenAIResponsesWebSocketPostDispatchError(error);
2521
+ }
2522
+ const steering = lease.entry && params.onActiveResponse && params.steeringInput ? createResponsesSteering({
2523
+ onActiveResponse: params.onActiveResponse,
2524
+ toInput: params.steeringInput,
2525
+ send: (event) => lease.socket.sendRaw(JSON.stringify(event)),
2526
+ needsContinuation: () => deferredInput,
2527
+ assertActive: () => {
2528
+ if (released || terminalReceived || params.signal?.aborted) throw new Error("Responses steering is no longer active");
2529
+ }
2530
+ }) : void 0;
2531
+ const finish = ({ keep = true } = {}) => {
2532
+ if (released) return;
2533
+ released = true;
2534
+ if (keep && lease.entry && terminalResponse) lease.entry.continuation = recordResponsesContinuationState(previousContinuation, prepared.fullRequest ?? fullRequest, terminalResponse, previousContinuation?.lastResponseId === prepared.request.previous_response_id);
2535
+ lease.release({ keep });
2536
+ };
2537
+ return {
2538
+ stream: { async *[Symbol.asyncIterator]() {
2539
+ if (streamStarted) throw new Error("OpenAI Responses WebSocket stream can only be consumed once");
2540
+ streamStarted = true;
2541
+ const iterator = lease.iterator;
2542
+ let requestDispatched = false;
2543
+ try {
2544
+ if (params.signal?.aborted) throw transportAbortError(params.signal);
2545
+ if (resumedSteering) {
2546
+ requestDispatched = true;
2547
+ if (resumedSteering.requiresInput) lease.socket.send({
2548
+ ...prepared.request,
2549
+ type: "response.create"
2550
+ });
2551
+ }
2552
+ for (;;) {
2553
+ const buffered = resumedSteering?.buffered.shift();
2554
+ const next = buffered ? {
2555
+ value: buffered,
2556
+ done: false
2557
+ } : await nextWebSocketMessage(iterator, params.signal);
2558
+ if (next.done) throw new Error("OpenAI Responses WebSocket closed before a terminal response event");
2559
+ if (next.value.type === "open") {
2560
+ if (!requestDispatched) {
2561
+ requestDispatched = true;
2562
+ lease.socket.send({
2563
+ ...prepared.request,
2564
+ type: "response.create"
2565
+ });
2566
+ }
2567
+ continue;
2568
+ }
2569
+ const event = readServerEvent(next.value);
2570
+ if (!event) continue;
2571
+ if (isRecord(event) && typeof event.type === "string" && event.type.startsWith("response.steer.")) {
2572
+ if ((isRecord(event.steer) && event.steer.previous_response_id === steering?.responseId ? steering : resumedSteering?.steering ?? steering)?.handle(event)) continue;
2573
+ }
2574
+ steering?.handle(event);
2575
+ if (event.type === "response.completed") terminalResponse = event.response;
2576
+ terminalReceived = event.type === "response.completed" || event.type === "response.incomplete" || event.type === "response.failed";
2577
+ if (terminalReceived) {
2578
+ steering?.seal();
2579
+ if (event.type === "response.failed") steering?.close(/* @__PURE__ */ new Error("Responses failed before steering could be applied"));
2580
+ const continuationBuffer = [];
2581
+ for (;;) {
2582
+ if (!steering?.pending) break;
2583
+ const acknowledgement = await nextWebSocketMessage(iterator, params.signal);
2584
+ if (acknowledgement.done) throw new Error("Responses closed before acknowledging steering");
2585
+ const acknowledgedEvent = readServerEvent(acknowledgement.value);
2586
+ if (!steering.handle(acknowledgedEvent)) continuationBuffer.push(acknowledgement.value);
2587
+ }
2588
+ const acceptedInput = steering?.acceptedInput ?? [];
2589
+ const isSteered = event.type === "response.incomplete" && isRecord(event.response.incomplete_details) && event.response.incomplete_details.reason === "steered";
2590
+ if (lease.entry && steering && acceptedInput.length > 0 && (event.type === "response.completed" || isSteered)) {
2591
+ if (event.type === "response.incomplete") terminalResponse = event.response;
2592
+ lease.entry.steeringContinuation = {
2593
+ iterator,
2594
+ buffered: continuationBuffer,
2595
+ acceptedInput,
2596
+ requiresInput: (terminalResponse?.output ?? []).some((item) => (item.type === "function_call" || item.type === "custom_tool_call") && !(isRecord(item) && item.async === true) || item.type === "mcp_approval_request"),
2597
+ steering,
2598
+ instructions: fullRequest.instructions,
2599
+ tools: fullRequest.tools
2600
+ };
2601
+ retainIterator = true;
2602
+ if (isSteered) {
2603
+ yield {
2604
+ ...event,
2605
+ type: "response.completed",
2606
+ response: {
2607
+ ...event.response,
2608
+ status: "completed",
2609
+ incomplete_details: null
2610
+ }
2611
+ };
2612
+ return;
2613
+ }
2614
+ }
2615
+ }
2616
+ yield event;
2617
+ if (terminalReceived) {
2618
+ degradedWebSocketConnections.delete(degradationKey);
2619
+ return;
2620
+ }
2621
+ }
2622
+ } catch (error) {
2623
+ const steeringDispatched = Boolean(steering?.pending || steering?.acceptedInput.length || resumedSteering);
2624
+ steering?.close(error instanceof Error ? error : /* @__PURE__ */ new Error("Responses steering failed"));
2625
+ if (lease.entry) lease.entry.continuation = void 0;
2626
+ const safeRetry = error instanceof OpenAIResponsesWebSocketSafeRetryError && !steeringDispatched;
2627
+ if (!params.callerSignal?.aborted && !safeRetry) markDegraded();
2628
+ if (!requestDispatched && !params.signal?.aborted) throw new OpenAIResponsesWebSocketPreDispatchError(error);
2629
+ if (!requestDispatched || params.callerSignal?.aborted || safeRetry) throw error;
2630
+ throw new OpenAIResponsesWebSocketPostDispatchError(error);
2631
+ } finally {
2632
+ steering?.seal();
2633
+ if (!retainIterator) {
2634
+ steering?.close(/* @__PURE__ */ new Error("Responses stream ended before steering was confirmed"));
2635
+ await iterator.return?.().catch(() => void 0);
2636
+ }
2637
+ if (!terminalReceived) finish({ keep: false });
2638
+ }
2639
+ } },
2640
+ request: prepared.request,
2641
+ fullRequest: prepared.fullRequest ?? fullRequest,
2642
+ reusedConnection: lease.reusedConnection,
2643
+ continuationStatus: prepared.continuationStatus,
2644
+ inputReplay,
2645
+ get hasActiveResponse() {
2646
+ return Boolean(steering?.responseId || resumedSteering);
2647
+ },
2648
+ finish
2649
+ };
2650
+ }
2651
+ function closeOpenAIResponsesWebSocketSessions(sessionId, owner) {
2652
+ for (const [cacheKey, entry] of websocketSessionCache) {
2653
+ if (owner && entry.owner !== owner || sessionId && entry.sessionId !== sessionId) continue;
2654
+ invalidateOwnedWebSocketSession(cacheKey, entry, "session_cleanup");
2655
+ }
2656
+ for (const [key, entry] of degradedWebSocketConnections) if ((!owner || entry.owner === owner) && (!sessionId || entry.sessionId === sessionId)) {
2657
+ clearTimeout(entry.expiryTimer);
2658
+ degradedWebSocketConnections.delete(key);
2659
+ }
2660
+ }
2661
+ registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
2662
+ //#endregion
2663
+ //#region packages/ai/src/transports/openai-responses-client.ts
2664
+ function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
2665
+ if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
2666
+ if (getAiTransportHost().requiresManagedTransport(model)) return;
2667
+ return supportsNativeOpenAIResponsesEndpoint(model) ? transport : void 0;
2668
+ }
2669
+ function combineWebSocketTimeoutSignal(signal, model, timeoutMs) {
2670
+ const resolvedTimeoutMs = timeoutMs !== void 0 && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : getAiTransportHost().resolveModelRequestTimeoutMs(model);
2671
+ if (resolvedTimeoutMs === void 0 || !Number.isFinite(resolvedTimeoutMs)) return signal;
2672
+ return AbortSignal.any([signal, AbortSignal.timeout(Math.max(1, resolvedTimeoutMs))]);
2673
+ }
2674
+ function createOpenAIResponsesClient(model, apiKey, defaultHeaders, fetchOverride) {
2675
+ return new OpenAI({
2676
+ apiKey,
2677
+ baseURL: resolveOpenAIClientBaseUrl(model),
2678
+ dangerouslyAllowBrowser: true,
2679
+ defaultHeaders,
2680
+ fetch: fetchOverride ?? buildGuardedModelFetch(model),
2681
+ ...buildOpenAISdkClientOptions(model)
2682
+ });
2683
+ }
2684
+ function createResponsesTransportExecutor(config) {
2685
+ return (model, context, options) => {
2686
+ const responsesOptions = options;
2687
+ const compactRequest = claimResponsesCompactRequest(responsesOptions);
2688
+ const { eventStream, stream } = createWritableTransportEventStream();
2689
+ (async () => {
2690
+ const output = createAssistantOutput(model, config.outputApi);
2691
+ let firstEventAbort;
2692
+ let continuationClaim;
2693
+ const requestLifecycle = responsesRequestLifecycle.get(options);
2694
+ try {
2695
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
2696
+ const websocketMode = resolveNativeOpenAIResponsesWebSocketMode(model, responsesOptions?.transport);
2697
+ const encodeBody = prepareModelRequestBody(websocketMode || compactRequest ? void 0 : options);
2698
+ const turnState = resolveProviderTransportTurnState(model, {
2699
+ sessionId: options?.sessionId,
2700
+ turnId: randomUUID(),
2701
+ attempt: 1,
2702
+ transport: websocketMode ? "websocket" : "stream"
2703
+ });
2704
+ const websocketSessionPolicy = websocketMode ? turnState?.websocket : void 0;
2705
+ const httpTurnHeaders = filterProviderTurnHeadersForExplicitOpencodeSession(model, options, turnState?.headers);
2706
+ const websocketTurnHeaders = filterProviderTurnHeadersForExplicitOpencodeSession(model, options, websocketSessionPolicy?.headers);
2707
+ const websocketHeaders = websocketMode ? buildOpenAIClientHeaders(model, context, options?.headers, websocketTurnHeaders, options?.sessionId, options?.cacheRetention) : void 0;
2708
+ const httpHeaders = buildOpenAIClientHeaders(model, context, options?.headers, httpTurnHeaders, options?.sessionId, options?.cacheRetention);
2709
+ const fetchOverride = createResponsesRequestFetch(model, {
2710
+ compact: Boolean(compactRequest),
2711
+ stream: config.streamRequest,
2712
+ lifecycle: requestLifecycle,
2713
+ onSseComment: () => notifyLlmRequestActivity(options?.signal, false)
2714
+ });
2715
+ const client = config.createClient(model, apiKey, httpHeaders, fetchOverride);
2716
+ const nativeAstra = model.id === "gpt-6-astra" && supportsNativeOpenAIResponsesEndpoint(model);
2717
+ const asyncToolExecutionEligible = nativeAstra && options?.asyncToolExecution === true && !responsesOptions?.openclawCodeModeToolSurface;
2718
+ const prepareRequest = async (request) => {
2719
+ let params = request;
2720
+ const nextParams = await options?.onPayload?.(params, model);
2721
+ if (nextParams !== void 0) params = nextParams;
2722
+ params = sanitizeOpenAICodexResponsesParams(model, params);
2723
+ params = sanitizeResponsesImagePayload(params);
2724
+ if (responsesOptions?.openclawCodeModeToolSurface === true) {
2725
+ const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
2726
+ const allowedHostedToolTypes = responsesOptions?.openclawCodeModeAllowedHostedToolTypes;
2727
+ enforceCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes, codeModeToolSurfaceObserver.get(options));
2728
+ assertCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
2729
+ }
2730
+ if (asyncToolExecutionEligible && params.model === "gpt-6-astra" && params.multi_agent?.enabled !== true && params.tools) {
2731
+ const synchronousTools = new Set(context.tools?.flatMap((tool) => tool.async === false ? [tool.name] : []));
2732
+ params.tools = params.tools.map((tool) => tool.type === "function" && !synchronousTools.has(tool.name) ? {
2733
+ ...tool,
2734
+ async: true
2735
+ } : tool);
2736
+ }
2737
+ return params;
2738
+ };
2739
+ const buildRequest = (replayMode, requestContext = context) => prepareRequest(config.buildRequest(model, requestContext, responsesOptions, turnState?.metadata, replayMode));
2740
+ let params = await buildRequest("checkpoint");
2741
+ const asyncTools = asyncToolExecutionEligible && params.model === "gpt-6-astra" && params.multi_agent?.enabled !== true;
2742
+ if (compactRequest) {
2743
+ const compacted = await postOpenAIResponsesCompaction({
2744
+ client,
2745
+ model,
2746
+ request: params,
2747
+ options: responsesOptions
2748
+ });
2749
+ output.usage.input = compacted.usage.input_tokens;
2750
+ output.usage.output = compacted.usage.output_tokens;
2751
+ output.usage.totalTokens = compacted.usage.input_tokens + compacted.usage.output_tokens;
2752
+ compactRequest.resolve(compacted);
2753
+ finalizeTransportStream({
2754
+ stream,
2755
+ output,
2756
+ signal: options?.signal
2757
+ });
2758
+ return;
2759
+ }
2760
+ const sessionId = options?.sessionId;
2761
+ if (config.httpContinuation && !websocketMode && !getAiTransportHost().requiresManagedTransport(model) && (supportsNativeOpenAIResponsesEndpoint(model) || resolveOpenAIResponsesPayloadPolicy(model).explicitContinuationOptIn) && sessionId && (params.store === true || supportsResponsesReasoningUpdate(params)) && !params.previous_response_id) {
2762
+ continuationClaim = claimOpenAIResponsesHttpContinuation({
2763
+ sessionId,
2764
+ apiKey,
2765
+ baseUrl: model.baseUrl,
2766
+ headers: httpHeaders,
2767
+ request: params,
2768
+ restoreRequest: () => restoreResponsesReasoningState(context, model, responsesOptions, params)
2769
+ });
2770
+ if (continuationClaim) params = continuationClaim.fullRequest;
2771
+ }
2772
+ const observePrompt = createResponsesPromptEgressObserver(responsesOptions, context.systemPrompt);
2773
+ const requestStartedAt = Date.now();
2774
+ let started = false;
2775
+ const startStream = () => {
2776
+ if (!started) {
2777
+ started = true;
2778
+ stream.push({
2779
+ type: "start",
2780
+ partial: output
2781
+ });
2782
+ }
2783
+ };
2784
+ const firstEvent = createFirstStreamEventAbortController(options?.signal);
2785
+ firstEventAbort = firstEvent;
2786
+ const requestOptions = buildOpenAISdkRequestOptions(model, firstEvent.signal, {
2787
+ stream: config.streamRequest,
2788
+ timeoutMs: options?.timeoutMs
2789
+ });
2790
+ const websocketSignal = combineWebSocketTimeoutSignal(firstEvent.signal, model, requestOptions?.timeout);
2791
+ emitModelTransportDebug(log, `[responses] start provider=${model.provider} api=${model.api} model=${model.id} requestIdHash=${redactIdentifier(options?.requestId, { len: 64 })} baseUrl=${formatModelTransportDebugBaseUrl(model.baseUrl)} timeoutMs=${safeDebugValue(requestOptions?.timeout)} apiKey=${apiKey ? "present" : "missing"} ${summarizeResponsesPayload(params)}`);
2792
+ const responseModelTracker = createResponseModelTracker(isOpenAICodexResponsesModel(model));
2793
+ let continuationBaseline;
2794
+ let dispatchedPreviousResponseId;
2795
+ let contextUsageEligible = true;
2796
+ let requestedTier;
2797
+ const createSseStream = async (initialRequest = continuationClaim?.request ?? params, initialAttemptKind = "initial", initialRejectedCompaction) => {
2798
+ const { stream: responseStream } = await createResponsesStreamWithRecovery({
2799
+ client,
2800
+ request: initialRequest,
2801
+ requestOptions,
2802
+ model,
2803
+ encodeBody,
2804
+ observePrompt,
2805
+ initialAttemptKind,
2806
+ initialRejectedCompaction,
2807
+ buildFullHistoryRequest: () => buildRequest("full-history"),
2808
+ onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, responsesOptions, checkpoint),
2809
+ canRetryStream: () => output.content.length === 0,
2810
+ onServiceTierRejected: (tier) => responsesServiceTierObserver.reject(options, tier),
2811
+ wrapStream: ({ stream: rawResponseStream, response, attempt }) => {
2812
+ requestedTier = attempt.request.service_tier;
2813
+ contextUsageEligible &&= attempt.kind === "initial";
2814
+ dispatchedPreviousResponseId = attempt.request.previous_response_id;
2815
+ continuationBaseline = attempt.request.previous_response_id ? params : attempt.request;
2816
+ const trackedResponseStream = responseModelTracker.track(response, rawResponseStream);
2817
+ return withProviderResponseHook({
2818
+ stream: observeResponsesStream(trackedResponseStream, model, requestStartedAt),
2819
+ signal: firstEvent.signal,
2820
+ abort: firstEvent.abort,
2821
+ hook: createOpenAIProviderAcceptanceHook(options, response, model),
2822
+ onReady: () => {
2823
+ emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} transport=sse elapsedMs=${Date.now() - requestStartedAt}`);
2824
+ startStream();
2825
+ }
2826
+ });
2827
+ }
2828
+ });
2829
+ return responseStream;
2830
+ };
2831
+ let responseStream;
2832
+ let websocketBaseline;
2833
+ let finishWebSocket;
2834
+ let transport = "sse";
2835
+ const logWebSocketFallback = (reason) => emitModelTransportDebug(log, `[responses] websocket_fallback provider=${model.provider} api=${model.api} model=${model.id} reason=${reason}`);
2836
+ const closeWebSocketForFallback = (reason) => {
2837
+ finishWebSocket?.({ keep: false });
2838
+ finishWebSocket = void 0;
2839
+ transport = "sse";
2840
+ logWebSocketFallback(reason);
2841
+ };
2842
+ if (websocketMode) try {
2843
+ const websocket = createOpenAIResponsesWebSocketStream({
2844
+ client,
2845
+ request: params,
2846
+ restoreRequest: (request) => restoreResponsesReasoningState(context, model, responsesOptions, request),
2847
+ mode: websocketMode,
2848
+ sessionId: options?.sessionId,
2849
+ headers: websocketHeaders,
2850
+ signal: websocketSignal,
2851
+ callerSignal: options?.signal,
2852
+ degradeCooldownMs: websocketSessionPolicy?.degradeCooldownMs,
2853
+ onActiveResponse: nativeAstra && params.model === "gpt-6-astra" ? options?.onActiveResponse : void 0,
2854
+ steeringInput: (messages) => {
2855
+ contextUsageEligible = false;
2856
+ return projectResponsesSteeringInput(params, () => buildRequest("checkpoint", {
2857
+ ...context,
2858
+ messages: [...context.messages, ...messages]
2859
+ }));
2860
+ }
2861
+ });
2862
+ finishWebSocket = websocket.finish;
2863
+ requestedTier = websocket.request.service_tier;
2864
+ websocketBaseline = websocket.fullRequest;
2865
+ recordResponsesInputReplay(output, websocket.inputReplay);
2866
+ contextUsageEligible &&= websocket.inputReplay === void 0;
2867
+ observePrompt?.(websocket.request, {
2868
+ egress: "responses-websocket",
2869
+ payloadVariant: "initial"
2870
+ });
2871
+ transport = "websocket";
2872
+ emitModelTransportDebug(log, `[responses] websocket_selected provider=${model.provider} api=${model.api} model=${model.id} mode=${websocketMode} reused=${websocket.reusedConnection} continuation=${websocket.continuationStatus === "continued"} continuationStatus=${websocket.continuationStatus} sessionIdHash=${redactIdentifier(options?.sessionId)} headersHash=${redactIdentifier(JSON.stringify(Object.entries(websocketHeaders ?? {}).toSorted(([a], [b]) => a.localeCompare(b))))}`);
2873
+ responseStream = createRecoverableResponsesWebSocketStream({
2874
+ trackedWebSocketStream: responseModelTracker.track(void 0, websocket.stream),
2875
+ websocket,
2876
+ websocketSignal,
2877
+ options,
2878
+ output,
2879
+ buildRequest,
2880
+ createSseStream,
2881
+ closeWebSocketForFallback,
2882
+ logWebSocketFallback,
2883
+ startStream,
2884
+ setTransportToSse: () => {
2885
+ transport = "sse";
2886
+ }
2887
+ });
2888
+ } catch (error) {
2889
+ if (error instanceof OpenAIResponsesWebSocketPostDispatchError) throw error;
2890
+ closeWebSocketForFallback("setup_failure");
2891
+ responseStream = await createSseStream();
2892
+ }
2893
+ else responseStream = await createSseStream();
2894
+ try {
2895
+ const acceptedResponseStream = withResponsesRequestAcceptance(responseStream, requestLifecycle, transport === "websocket" ? websocketSignal : firstEvent.signal);
2896
+ const terminal = await processResponsesStream(acceptedResponseStream, output, stream, model, {
2897
+ canRetryIdentityConflict: () => hasOnlyResponsesFunctionTools(transport === "websocket" ? websocketBaseline : continuationBaseline),
2898
+ ...config.pricingOptions?.(responsesOptions, model),
2899
+ resolveServiceTier: (responseTier, originalTier) => responseTier ?? (requestedTier === "priority" || requestedTier === "default" ? requestedTier : originalTier),
2900
+ onServiceTier: (responseTier) => responsesServiceTierObserver.observe(options, requestedTier, responseTier),
2901
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
2902
+ abortFirstEventStream: firstEvent.abort,
2903
+ onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
2904
+ signal: options?.signal,
2905
+ reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
2906
+ authProfileId: responsesOptions?.authProfileId,
2907
+ sessionId: options?.sessionId
2908
+ }),
2909
+ asyncToolExecution: asyncTools,
2910
+ ...responseModelTracker.terminalOptions
2911
+ });
2912
+ finishWebSocket?.();
2913
+ if (options?.signal?.aborted) throw transportAbortError(options.signal);
2914
+ if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
2915
+ const admitted = transport === "websocket" ? websocketBaseline : continuationBaseline;
2916
+ if (terminal && admitted && supportsNativeOpenAIResponsesEndpoint(model)) recordResponsesReasoningState(output, model, responsesOptions, admitted, terminal.output);
2917
+ if (continuationClaim && continuationBaseline && terminal) continuationClaim.commit(continuationBaseline, terminal, dispatchedPreviousResponseId);
2918
+ if (terminal && admitted && contextUsageEligible) recordResponsesContextUsage(output, model, responsesOptions, admitted, terminal.output, "transport");
2919
+ } catch (error) {
2920
+ finishWebSocket?.({ keep: false });
2921
+ throw error;
2922
+ }
2923
+ emitModelTransportDebug(log, `[responses] completed provider=${model.provider} api=${model.api} model=${model.id} transport=${transport} elapsedMs=${Date.now() - requestStartedAt}`);
2924
+ finalizeTransportStream({
2925
+ stream,
2926
+ output,
2927
+ signal: options?.signal
2928
+ });
2929
+ } catch (error) {
2930
+ if (requestLifecycle) await requestLifecycle.settle().catch(() => void 0);
2931
+ if (compactRequest) {
2932
+ compactRequest.reject(error);
2933
+ failTransportStream({
2934
+ stream,
2935
+ output,
2936
+ signal: options?.signal,
2937
+ error
2938
+ });
2939
+ return;
2940
+ }
2941
+ if (error instanceof ResponsesStreamFailure && error.observation) logResponsesFailedNoDetails(error.observation);
2942
+ const incompleteReason = output.diagnostics?.find(({ type }) => type === "openai_responses_terminal")?.details?.incompleteReason;
2943
+ emitModelTransportError(log, "responses", `provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error) + (typeof incompleteReason === "string" ? ` incompleteReason=${incompleteReason}` : ""), options?.signal);
2944
+ failTransportStream({
2945
+ stream,
2946
+ output,
2947
+ signal: options?.signal,
2948
+ error
2949
+ });
2950
+ } finally {
2951
+ continuationClaim?.release();
2952
+ firstEventAbort?.dispose();
2953
+ }
2954
+ })();
2955
+ return eventStream;
2956
+ };
2957
+ }
2958
+ function createOpenAIResponsesTransportStreamFn() {
2959
+ return createResponsesTransportExecutor({
2960
+ streamRequest: true,
2961
+ httpContinuation: true,
2962
+ createClient: createOpenAIResponsesClient,
2963
+ buildRequest: buildOpenAIResponsesParams,
2964
+ pricingOptions: (options, model) => ({
2965
+ serviceTier: options?.serviceTier,
2966
+ applyServiceTierPricing: (usage, serviceTier) => applyResponsesServiceTierPricing(usage, serviceTier, model)
2967
+ })
2968
+ });
2969
+ }
2970
+ function createAzureOpenAIResponsesTransportStreamFn() {
2971
+ return createResponsesTransportExecutor({
2972
+ outputApi: "azure-openai-responses",
2973
+ firstEventTimeoutMs: AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS,
2974
+ createClient: createAzureOpenAIClient,
2975
+ buildRequest: (model, context, options, metadata, replayMode) => {
2976
+ const deploymentName = resolveAzureDeploymentNameFromMap({
2977
+ modelId: model.id,
2978
+ deploymentMap: process.env.AZURE_OPENAI_DEPLOYMENT_NAME_MAP
2979
+ });
2980
+ const params = buildOpenAIResponsesParams(model, context, options, metadata, replayMode);
2981
+ params.model = deploymentName;
2982
+ delete params.store;
2983
+ return params;
2984
+ }
2985
+ });
2986
+ }
2987
+ function createAzureOpenAIClient(model, apiKey, defaultHeaders, fetchOverride) {
2988
+ const baseURL = model.baseUrl.replace(/\/+$/, "");
2989
+ const clientOptions = {
2990
+ apiKey,
2991
+ dangerouslyAllowBrowser: true,
2992
+ defaultHeaders,
2993
+ baseURL,
2994
+ fetch: fetchOverride ?? buildGuardedModelFetch(model),
2995
+ ...buildOpenAISdkClientOptions(model)
2996
+ };
2997
+ if (isOpenAICompatibleAzureResponsesBaseUrl(baseURL)) return new OpenAI(clientOptions);
2998
+ return new AzureOpenAI({
2999
+ ...clientOptions,
3000
+ apiVersion: resolveAzureOpenAIApiVersion()
3001
+ });
3002
+ }
3003
+ //#endregion
3004
+ //#region packages/ai/src/transports/openai-completions-transport.ts
3005
+ function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
3006
+ const messages = params.messages;
3007
+ if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
3008
+ throw new Error(`OpenAI-compatible chat payload for ${model.provider}/${model.id} contains no non-empty user or assistant messages after compaction and transport transforms; refusing to send a system/tool-only request. Start a new user turn or repair the compacted session history.`);
3009
+ }
3010
+ const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
3011
+ const SSE_DONE_MAX_LINE_CHARS = 1024;
3012
+ function createSseDoneDetector() {
3013
+ const decoder = new TextDecoder();
3014
+ let line = "";
3015
+ let lineOverflowed = false;
3016
+ let sawDone = false;
3017
+ const finishLine = () => {
3018
+ if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
3019
+ line = "";
3020
+ lineOverflowed = false;
3021
+ };
3022
+ const observeText = (text) => {
3023
+ for (const char of text) {
3024
+ if (char === "\n" || char === "\r") {
3025
+ finishLine();
3026
+ continue;
3027
+ }
3028
+ if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
3029
+ else lineOverflowed = true;
3030
+ }
3031
+ };
3032
+ return {
3033
+ observe(chunk) {
3034
+ if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
3035
+ },
3036
+ finish() {
3037
+ if (sawDone) return;
3038
+ observeText(decoder.decode());
3039
+ if (line || lineOverflowed) finishLine();
3040
+ },
3041
+ sawDone: () => sawDone
3042
+ };
3043
+ }
3044
+ function buildOpenAICompletionsClientConfig(model, headers) {
3045
+ const defaultQuery = {};
3046
+ let baseURL = model.baseUrl;
3047
+ let isAzureHost = false;
3048
+ const parsed = URL.parse(model.baseUrl);
3049
+ if (parsed) {
3050
+ isAzureHost = isAzureOpenAICompatibleHost(parsed.hostname.toLowerCase());
3051
+ parsed.searchParams.forEach((value, key) => {
3052
+ if (value) defaultQuery[key] = value;
3053
+ });
3054
+ parsed.search = "";
3055
+ baseURL = parsed.toString().replace(/\/$/, "");
3056
+ }
3057
+ if (isAzureHost) {
3058
+ const apiVersionHeader = Object.keys(headers).find((key) => key.toLowerCase() === "api-version");
3059
+ if (apiVersionHeader) {
3060
+ const apiVersion = headers[apiVersionHeader]?.trim();
3061
+ delete headers[apiVersionHeader];
3062
+ if (apiVersion && !defaultQuery["api-version"]) defaultQuery["api-version"] = apiVersion;
3063
+ }
3064
+ }
3065
+ return {
3066
+ baseURL: resolveOpenAIClientBaseUrl(model, baseURL),
3067
+ defaultHeaders: headers,
3068
+ defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
3069
+ };
3070
+ }
3071
+ function createDirectCompletionsEventStream(output, stream) {
3072
+ const finishedBlocks = /* @__PURE__ */ new Set();
3073
+ const contentIndices = /* @__PURE__ */ new WeakMap();
3074
+ let openTextBlock;
3075
+ let openThinkingBlock;
3076
+ const finishBlock = (block) => {
3077
+ const contentIndex = contentIndices.get(block);
3078
+ if (contentIndex === void 0 || finishedBlocks.has(block)) return;
3079
+ finishedBlocks.add(block);
3080
+ if (block.type === "text") {
3081
+ openTextBlock = void 0;
3082
+ stream.push({
3083
+ type: "text_end",
3084
+ contentIndex,
3085
+ content: block.text,
3086
+ partial: output
3087
+ });
3088
+ } else if (block.type === "thinking") {
3089
+ openThinkingBlock = void 0;
3090
+ stream.push({
3091
+ type: "thinking_end",
3092
+ contentIndex,
3093
+ content: block.thinking,
3094
+ partial: output
3095
+ });
3096
+ } else if (block.type === "toolCall") stream.push({
3097
+ type: "toolcall_end",
3098
+ contentIndex,
3099
+ toolCall: block,
3100
+ partial: output
3101
+ });
3102
+ };
3103
+ return {
3104
+ stream: { push(event) {
3105
+ if (event.type === "text_start" || event.type === "thinking_start" || event.type === "toolcall_start") {
3106
+ const block = output.content[event.contentIndex];
3107
+ if (block) {
3108
+ contentIndices.set(block, event.contentIndex);
3109
+ if (block.type === "text") openTextBlock = block;
3110
+ else if (block.type === "thinking") openThinkingBlock = block;
3111
+ }
3112
+ }
3113
+ stream.push(event);
3114
+ } },
3115
+ beforeContentBlock: (nextType) => {
3116
+ if (openThinkingBlock) finishBlock(openThinkingBlock);
3117
+ if (openTextBlock && nextType !== "toolCall") finishBlock(openTextBlock);
3118
+ },
3119
+ finish(includeToolCalls) {
3120
+ for (const block of output.content) if (block.type !== "toolCall" || includeToolCalls) finishBlock(block);
3121
+ }
3122
+ };
3123
+ }
3124
+ function createOpenAICompletionsTransportStreamFn() {
3125
+ const streamResponses = createOpenAIResponsesTransportStreamFn();
3126
+ return (model, context, options) => {
3127
+ const completionsOptions = options;
3128
+ if (model.reasoning && context.tools?.length && supportsModelTools(model) && isNativeOpenAIEndpoint(model)) {
3129
+ const toolChoice = completionsOptions?.toolChoice;
3130
+ const responsesOptions = {
3131
+ ...options,
3132
+ ...completionsOptions?.reasoning === void 0 && completionsOptions?.reasoningEffort === void 0 ? { reasoningEffort: "high" } : {},
3133
+ ...toolChoice && typeof toolChoice === "object" ? { toolChoice: toOpenAIResponsesToolChoice(toolChoice) } : {}
3134
+ };
3135
+ return streamResponses(getAiTransportHost().inheritManagedTransport(model, {
3136
+ ...model,
3137
+ api: "openai-responses"
3138
+ }), context, responsesOptions);
3139
+ }
3140
+ return streamOpenAICompletionsRequest(model, context, completionsOptions, "managed");
3141
+ };
3142
+ }
3143
+ function streamOpenAICompletionsRequest(model, context, options, mode) {
3144
+ const { eventStream, stream } = createWritableTransportEventStream();
3145
+ (async () => {
3146
+ const output = createAssistantOutput(model);
3147
+ const provisionalCommentaryTags = /* @__PURE__ */ new Map();
3148
+ let firstEventAbort;
3149
+ try {
3150
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
3151
+ const directEmitReasoning = Boolean(mode === "direct" && model.reasoning && options?.reasoningEffort && isOpenAICompletionsThinkingEnabled(options.reasoningEffort));
3152
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention);
3153
+ const policy = mode === "direct" ? {
3154
+ mode,
3155
+ compat: resolveOpenAICompletionsCompat(model),
3156
+ cacheRetention
3157
+ } : { mode };
3158
+ const { client, sawStreamDONE } = policy.mode === "direct" ? {
3159
+ client: createDirectCompletionsClient(model, context, apiKey, resolveProviderSimpleCompletionHeaders(model, options), cacheRetention === "none" ? void 0 : options?.sessionId, policy.compat),
3160
+ sawStreamDONE: void 0
3161
+ } : createManagedCompletionsClient(model, context, options, apiKey, cacheRetention);
3162
+ let params = buildOpenAICompletionsRequest(model, context, options, policy);
3163
+ const encodeBody = prepareModelRequestBody(options);
3164
+ const nextParams = await options?.onPayload?.(params, model);
3165
+ if (nextParams !== void 0) params = nextParams;
3166
+ if (mode === "managed") {
3167
+ if (options?.openclawCodeModeToolSurface === true) {
3168
+ const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
3169
+ enforceCodeModeResponsesToolSurface(params, visibleToolNames, void 0, codeModeToolSurfaceObserver.get(options));
3170
+ assertCodeModeResponsesToolSurface(params, visibleToolNames);
3171
+ }
3172
+ if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
3173
+ }
3174
+ const emitReasoning = mode === "direct" ? directEmitReasoning : shouldEmitOpenAICompletionsReasoning(model, options);
3175
+ firstEventAbort = createFirstStreamEventAbortController(options?.signal);
3176
+ const requestOptions = mode === "direct" ? {
3177
+ ...await encodeBody(params),
3178
+ signal: firstEventAbort.signal,
3179
+ ...options?.timeoutMs !== void 0 ? { timeout: options.timeoutMs } : {},
3180
+ maxRetries: 0
3181
+ } : {
3182
+ ...buildOpenAISdkRequestOptions(model, firstEventAbort.signal, { timeoutMs: options?.timeoutMs }),
3183
+ ...await encodeBody(params)
3184
+ };
3185
+ const request = client.chat.completions.create(params, requestOptions);
3186
+ const response = await request.asResponse();
3187
+ const hookedResponseStream = withProviderResponseHook({
3188
+ stream: { async *[Symbol.asyncIterator]() {
3189
+ if (!params.stream) {
3190
+ yield await boundResponseBody(response, {
3191
+ maxBytes: 16777216,
3192
+ onOverflow: ({ maxBytes }) => /* @__PURE__ */ new Error(`Chat Completions JSON response exceeds ${maxBytes} bytes`)
3193
+ }).json();
3194
+ return;
3195
+ }
3196
+ const data = await request;
3197
+ if (Symbol.asyncIterator in data) yield* data;
3198
+ else yield data;
3199
+ } },
3200
+ signal: firstEventAbort.signal,
3201
+ abort: firstEventAbort.abort,
3202
+ hook: createOpenAIProviderAcceptanceHook(options, response, model),
3203
+ onReady: () => stream.push({
3204
+ type: "start",
3205
+ partial: output
3206
+ })
3207
+ });
3208
+ const directEvents = mode === "direct" ? createDirectCompletionsEventStream(output, stream) : void 0;
3209
+ try {
3210
+ await processCompletionsStream(hookedResponseStream, output, model, directEvents?.stream ?? stream, {
3211
+ ...directEvents ? {
3212
+ mode: "direct",
3213
+ beforeContentBlock: directEvents.beforeContentBlock,
3214
+ provisionalCommentaryTags
3215
+ } : { mode: "managed" },
3216
+ signal: options?.signal,
3217
+ emitReasoning,
3218
+ strictReasoningTags: reasoningTagTextPolicy.isStrict(options),
3219
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
3220
+ abortFirstEventStream: firstEventAbort.abort,
3221
+ onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
3222
+ sawStreamDONE
3223
+ });
3224
+ if (directEvents) {
3225
+ if (options?.signal?.aborted) throw transportAbortError(options.signal);
3226
+ if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage || (output.stopReason === "aborted" ? "Request was aborted" : "Provider returned an invalid tool call"));
3227
+ }
3228
+ } catch (error) {
3229
+ directEvents?.finish(false);
3230
+ throw error;
3231
+ }
3232
+ directEvents?.finish(output.stopReason === "toolUse");
3233
+ finalizeTransportStream({
3234
+ stream,
3235
+ output,
3236
+ signal: options?.signal
3237
+ });
3238
+ } catch (error) {
3239
+ failTransportStream({
3240
+ stream,
3241
+ output,
3242
+ signal: options?.signal,
3243
+ error,
3244
+ cleanup: () => {
3245
+ finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
3246
+ clearPendingCommentaryText(provisionalCommentaryTags);
3247
+ tagUnresolvedTextAsCommentary(output);
3248
+ if (mode === "direct") for (const block of output.content) {
3249
+ delete block.index;
3250
+ delete block.partialArgs;
3251
+ delete block.streamIndex;
3252
+ }
3253
+ }
3254
+ });
3255
+ } finally {
3256
+ firstEventAbort?.dispose();
3257
+ }
3258
+ })();
3259
+ return eventStream;
3260
+ }
3261
+ function createDirectCompletionsClient(model, context, apiKey, optionsHeaders, sessionId, compat) {
3262
+ if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
3263
+ const headers = { ...model.headers };
3264
+ if (model.provider === "github-copilot") Object.assign(headers, buildCopilotDynamicHeaders(context.messages));
3265
+ if (sessionId && compat.sessionAffinity !== "none") {
3266
+ if (compat.sessionAffinity === "openrouter") headers["x-session-id"] = sessionId;
3267
+ else {
3268
+ headers.session_id = sessionId;
3269
+ headers["x-client-request-id"] = sessionId;
3270
+ headers["x-session-affinity"] = sessionId;
3271
+ }
3272
+ }
3273
+ return createOpenAIProviderClient(model, apiKey, headers, optionsHeaders);
3274
+ }
3275
+ function createManagedCompletionsClient(model, context, options, apiKey, cacheRetention) {
3276
+ const turnState = resolveProviderTransportTurnState(model, {
3277
+ sessionId: options?.sessionId,
3278
+ turnId: randomUUID(),
3279
+ attempt: 1,
3280
+ transport: "stream"
3281
+ });
3282
+ const optionHeaders = resolveOpencodeSessionHeaders(model, options);
3283
+ const turnHeaders = filterProviderTurnHeadersForExplicitOpencodeSession(model, options, turnState?.headers);
3284
+ const doneDetector = createSseDoneDetector();
3285
+ const baseFetch = buildGuardedModelFetch(model, void 0, { onSseComment: () => notifyLlmRequestActivity(options?.signal, false) });
3286
+ const doneDetectingFetch = async (url, init) => {
3287
+ const response = await baseFetch(url, init);
3288
+ if (!response.body || !response.ok) return response;
3289
+ if (typeof TransformStream === "undefined" || !response.body.pipeThrough) return response;
3290
+ const transformed = response.body.pipeThrough(new TransformStream({
3291
+ transform(chunk, controller) {
3292
+ doneDetector.observe(chunk);
3293
+ controller.enqueue(chunk);
3294
+ },
3295
+ flush() {
3296
+ doneDetector.finish();
3297
+ }
3298
+ }));
3299
+ return new Response(transformed, response);
3300
+ };
3301
+ const clientConfig = buildOpenAICompletionsClientConfig(model, buildOpenAIClientHeaders(model, context, {
3302
+ ...turnHeaders,
3303
+ ...optionHeaders
3304
+ }, void 0, resolvePromptCacheKey(options, cacheRetention), cacheRetention));
3305
+ return {
3306
+ client: new OpenAI({
3307
+ apiKey,
3308
+ baseURL: clientConfig.baseURL,
3309
+ dangerouslyAllowBrowser: true,
3310
+ defaultHeaders: clientConfig.defaultHeaders,
3311
+ defaultQuery: clientConfig.defaultQuery,
3312
+ fetch: doneDetectingFetch,
3313
+ ...buildOpenAISdkClientOptions(model)
3314
+ }),
3315
+ sawStreamDONE: doneDetector.sawDone
3316
+ };
3317
+ }
3318
+ //#endregion
3319
+ export { requestPreparedOpenAIResponsesCompaction as a, convertMessages as c, applyCompletionsAnthropicCacheControl as d, hasOpenAICompatibleConversationTurn as f, createDeepSeekTextFilter as h, createOpenAIResponsesTransportStreamFn as i, flattenCompletionMessagesToStringContent as l, resolveMaxTokensParam as m, streamOpenAICompletionsRequest as n, hasResponsesWebSearchTool as o, canonicalizeMaxTokensParam as p, createAzureOpenAIResponsesTransportStreamFn as r, buildOpenAICompletionsParams as s, createOpenAICompletionsTransportStreamFn as t, stripCompletionMessagesToRoleContent as u };