@openclaw/ai 2026.8.1-beta.1 → 2026.8.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/{anthropic-CE-7SkF6.mjs → anthropic-B6dLpq5L.mjs} +37 -22
  2. package/dist/{src-QkygScBs.mjs → anthropic-JsNA5KCu.mjs} +0 -1
  3. package/dist/{anthropic-usage-Ma4iX6uG.mjs → anthropic-compaction-replay-8lJNKXOE.mjs} +225 -102
  4. package/dist/anthropic-payload-policy-CiEuQS72.d.mts +49 -0
  5. package/dist/{api-registry-EpJoVwM1.d.mts → api-registry-k3zTz0cV.d.mts} +1 -1
  6. package/dist/{azure-openai-responses-Cc_kasye.mjs → azure-openai-responses-mxIOtUnn.mjs} +23 -29
  7. package/dist/diagnostics.d.mts +24 -1
  8. package/dist/diagnostics.mjs +2 -1
  9. package/dist/{event-stream-DRmDMRHH.d.mts → event-stream-BP6AWT8j.d.mts} +3 -1
  10. package/dist/{event-stream-CEV9t6da.mjs → event-stream-uSMZJ3FA.mjs} +16 -3
  11. package/dist/event-stream.d.mts +1 -1
  12. package/dist/event-stream.mjs +1 -1
  13. package/dist/{google-CGQu21aw.mjs → google-C7h2QzDX.mjs} +4 -4
  14. package/dist/{google-shared-Cq2UKx1l.mjs → google-shared-B0Qr9OR7.mjs} +9 -18
  15. package/dist/google-thinking-level-C-V3tecN.mjs +9 -0
  16. package/dist/{google-vertex-CusxWzvE.mjs → google-vertex-Bhc3TjDl.mjs} +5 -4
  17. package/dist/{llm-request-activity-BjtkplhG.mjs → headers-DdOQtGuU.mjs} +9 -1
  18. package/dist/{host-Bl7Kgddo.mjs → host-DTqNc7ad.mjs} +227 -168
  19. package/dist/{host-Dn4_SZI2.d.mts → host-vWgMMhiJ.d.mts} +9 -4
  20. package/dist/index.d.mts +6 -6
  21. package/dist/index.mjs +7 -5
  22. package/dist/internal/anthropic.d.mts +13 -5
  23. package/dist/internal/anthropic.mjs +5 -5
  24. package/dist/internal/openai-responses-payload-policy.d.mts +3 -0
  25. package/dist/internal/openai-responses-payload-policy.mjs +3 -0
  26. package/dist/internal/openai.d.mts +6 -6
  27. package/dist/internal/openai.mjs +8 -7
  28. package/dist/internal/runtime.d.mts +17 -5
  29. package/dist/internal/runtime.mjs +85 -73
  30. package/dist/internal/shared.d.mts +1 -6
  31. package/dist/internal/shared.mjs +3 -4
  32. package/dist/{json-parse-BvXNt1-7.mjs → json-parse-CDnesDM_.mjs} +4 -6
  33. package/dist/{mistral-Dx6VzNjf.mjs → mistral-CEWoQI_g.mjs} +26 -45
  34. package/dist/{number-coercion-1Miyb5MO.mjs → number-coercion-H9qHik3g.mjs} +7 -2
  35. package/dist/openai-chatgpt-jwt-KWcgd0d_.mjs +19 -0
  36. package/dist/{openai-chatgpt-responses-SC9m0FSf.mjs → openai-chatgpt-responses-CrPmqERt.mjs} +225 -144
  37. package/dist/{openai-completions-BaIpl93j.mjs → openai-completions-BPnt4Sml.mjs} +28 -43
  38. package/dist/openai-completions-compat-Dt3dcawL.d.mts +43 -0
  39. package/dist/{openai-responses-Bdx3V_Yu.mjs → openai-responses-DhIKtOup.mjs} +17 -24
  40. package/dist/openai-responses-contracts-CyfIkQi5.mjs +253 -0
  41. package/dist/openai-responses-contracts-XpZJxrRG.d.mts +68 -0
  42. package/dist/openai-responses-payload-policy-BDxV-W0c.mjs +206 -0
  43. package/dist/openai-responses-payload-policy-BSs371VM.d.mts +40 -0
  44. package/dist/{openai-responses-prompt-observer-internal-DDZPxRfr.mjs → openai-responses-prompt-observer-internal-DgNTYnRY.mjs} +3 -3
  45. package/dist/{openai-responses-stream-internal-Bl8YyVtF.mjs → openai-responses-shared-DXIt3iY5.mjs} +1211 -908
  46. package/dist/{openai-reasoning-compat-DKebnIBL.mjs → openai-stop-reason-BkFkqqK0.mjs} +198 -172
  47. package/dist/{openai-transport-shared-Cipt7egQ.mjs → openai-tool-projection-CY04OcvQ.mjs} +151 -128
  48. package/dist/provider-error-BUwEnjXq.mjs +429 -0
  49. package/dist/provider-error-CzNw4BWX.d.mts +12 -0
  50. package/dist/{provider-options-C5kYML7i.d.mts → provider-options-B96RdNpH.d.mts} +9 -3
  51. package/dist/provider-transcript-transform-ePx-Bbfr.mjs +155 -0
  52. package/dist/provider-types.d.mts +31 -0
  53. package/dist/provider-types.mjs +8 -0
  54. package/dist/providers.d.mts +1 -1
  55. package/dist/providers.mjs +17 -19
  56. package/dist/{reasoning-tag-text-partitioner-CGDyLWUR.mjs → reasoning-tag-text-partitioner-rnPwX2pg.mjs} +14 -8
  57. package/dist/record-coerce-DdXsgUd_.mjs +23 -0
  58. package/dist/{model-utils-Dau5dlgm.mjs → sanitize-unicode-BYqrYtC_.mjs} +28 -2
  59. package/dist/session-resources-CkR4WWy1.mjs +21 -0
  60. package/dist/simple-options-D58D5Kvw.mjs +117 -0
  61. package/dist/src-D2H6yKkH.mjs +2 -0
  62. package/dist/{stream-first-event-timeout-BIBomOGq.mjs → stream-first-event-timeout-MK28puvq.mjs} +2 -2
  63. package/dist/string-coerce-fsri9iCu.mjs +34 -0
  64. package/dist/{tool-schema-json-projection-B1b-XCn5.mjs → tool-schema-json-projection-q5d7QX5c.mjs} +5 -7
  65. package/dist/transport-utils-DJqkxbhC.mjs +138 -0
  66. package/dist/transports.d.mts +135 -178
  67. package/dist/transports.mjs +1732 -1294
  68. package/dist/types-BDdaOVi2.mjs +6 -0
  69. package/dist/{types-CH7ReIcU.d.mts → types-BHNrPS1l.d.mts} +19 -1
  70. package/dist/types.d.mts +4 -4
  71. package/dist/types.mjs +6 -4
  72. package/dist/utf16-slice-CvGodqok.mjs +29 -0
  73. package/dist/{validation-DAa_yFOM.mjs → validation-B61OhAio.mjs} +6 -6
  74. package/dist/{validation-CcPcuEbR.d.mts → validation-DT9SrFn3.d.mts} +1 -1
  75. package/dist/validation.d.mts +1 -1
  76. package/dist/validation.mjs +1 -1
  77. package/package.json +15 -1
  78. package/dist/headers-B_e4-1J0.mjs +0 -9
  79. package/dist/openai-chatgpt-jwt-DhAAzLkj.mjs +0 -39
  80. package/dist/openai-responses-contracts-BBKBfAqR.d.mts +0 -116
  81. package/dist/openai-responses-shared-CCyMvg7M.mjs +0 -403
  82. package/dist/provider-error-DI0Ts28U.mjs +0 -47
  83. package/dist/sanitize-unicode-DT5o51ur.mjs +0 -26
  84. package/dist/tool-result-text-Dvkp2Dus.mjs +0 -274
  85. package/dist/transform-messages-DfjpNXNQ.mjs +0 -2
  86. package/dist/transport-stream-shared-CPNv7A3r.mjs +0 -297
@@ -1,28 +1,36 @@
1
1
  import { n as getEnvApiKey } from "./env-api-keys-DrgeBuva.mjs";
2
- import { d as resolveClaudeSonnet5ModelIdentity, g as supportsClaudeNativeXhighEffort, p as supportsClaudeAdaptiveThinking, u as resolveClaudeOpus5ModelIdentity } from "./src-QkygScBs.mjs";
3
- import { r as createAssistantMessageEventStream } from "./event-stream-CEV9t6da.mjs";
4
- import { _ as normalizeLowercaseStringOrEmpty, a as ANTHROPIC_CLAUDE_CODE_BILLING_SYSTEM_BLOCK, b as isRecord, c as defaultsClaudeAdaptiveThinking, d as requiresClaudeAdaptiveThinking, f as resolveAnthropicThinkingEffort, g as hasNonEmptyString, h as usesClaudeStreamingRefusalContract, l as mapAnthropicStopReason, m as usesClaudeFable5MessagesContract, n as getAiTransportHost, o as ANTHROPIC_CLAUDE_CODE_VERSION, r as resolveAiTransportHeaderSentinels, s as applyClaudeRequestContract, u as prepareClaudeNoPrefillRequestContext, y as readStringValue } from "./host-Bl7Kgddo.mjs";
5
- import { n as calculateCost } from "./model-utils-Dau5dlgm.mjs";
2
+ import { d as resolveClaudeSonnet5ModelIdentity, g as supportsClaudeNativeXhighEffort, p as supportsClaudeAdaptiveThinking, u as resolveClaudeOpus5ModelIdentity } from "./anthropic-JsNA5KCu.mjs";
3
+ import { r as createAssistantMessageEventStream } from "./event-stream-uSMZJ3FA.mjs";
4
+ import { n as normalizeLowercaseStringOrEmpty, t as hasNonEmptyString } from "./string-coerce-fsri9iCu.mjs";
5
+ import { r as calculateCost } from "./sanitize-unicode-BYqrYtC_.mjs";
6
+ import { _ as resolveAnthropicThinkingEffort, a as describeToolResultMediaPlaceholder, b as usesClaudeStreamingRefusalContract, d as ANTHROPIC_CLAUDE_CODE_VERSION, f as applyClaudeRequestContract, g as requiresClaudeAdaptiveThinking, h as prepareClaudeNoPrefillRequestContext, l as isImageWithMediaPayload, m as mapAnthropicStopReason, n as getAiTransportHost, o as extractToolResultBlockText, p as defaultsClaudeAdaptiveThinking, r as resolveAiTransportHeaderSentinels, s as extractToolResultText, u as ANTHROPIC_CLAUDE_CODE_BILLING_SYSTEM_BLOCK, y as usesClaudeFable5MessagesContract } from "./host-DTqNc7ad.mjs";
7
+ import { a as isRecord } from "./record-coerce-DdXsgUd_.mjs";
8
+ import { i as canonicalizeBase64, n as projectProviderError, o as stableStringify } from "./provider-error-BUwEnjXq.mjs";
9
+ import { n as toErrorObject, t as createResponsesPromptEgressObserver } from "./openai-responses-prompt-observer-internal-DgNTYnRY.mjs";
10
+ import { r as asNonNegativeFiniteNumber } from "./number-coercion-H9qHik3g.mjs";
11
+ import { _ as isOpenAIGpt56Model, c as OpenAIResponsesWebSocketPostDispatchError, d as OpenAIResponsesWebSocketSafeRetryError, g as isOpenAIGpt55Model, h as isOpenAIGpt54MiniModel, i as OPENAI_RESPONSES_APIS, l as OpenAIResponsesWebSocketPreDispatchError, p as parseOpenAIResponsesWebSocketServerError, r as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, t as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, u as OpenAIResponsesWebSocketResponseFailedError, v as normalizeOpenAIReasoningEffort, w as uniqueStrings, y as resolveOpenAIReasoningEffortForModel } from "./openai-responses-contracts-CyfIkQi5.mjs";
6
12
  import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
7
13
  import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
8
- import { a as isImageWithMediaPayload, h as stripSystemPromptCacheBoundary, n as extractToolResultBlockText, r as extractToolResultText, s as adjustMaxTokensForThinking, t as describeToolResultMediaPlaceholder, v as sortPromptCacheToolsByName } from "./tool-result-text-Dvkp2Dus.mjs";
9
- import { a as resolveProviderEndpoint, c as transformTransportMessages, i as resolveOpenAIStrictToolSetting, n as buildGuardedModelFetch, r as resolveModelRequestTimeoutMs, s as resolveProviderRequestPolicyConfig } from "./tool-schema-json-projection-B1b-XCn5.mjs";
10
- import { A as applyAnthropicPayloadPolicyToParams, C as usesFoundryBearerAuth, D as canonicalizeBase64, E as resolveAnthropicImageMediaType, M as resolveAnthropicPayloadPolicy, O as applyAnthropicCacheControlToMessages, S as omitFoundryBearerCredentialHeaders, T as normalizeAnthropicInlineContent, b as resolveAnthropicFallbackServingModelCost, c as normalizeAnthropicToolChoice, d as resolveOriginalAnthropicToolName, f as toClaudeCodeToolName, g as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, h as ANTHROPIC_SERVER_SIDE_FALLBACKS, j as resolveAnthropicEphemeralCacheControl, k as applyAnthropicEphemeralCacheControlMarkers, l as projectAnthropicTools, m as findActiveAnthropicToolTurnAssistantIndex, n as applyAnthropicMessageStartUsage, p as ANTHROPIC_OMITTED_REASONING_TEXT, s as normalizeAnthropicToolCallId, t as applyAnthropicMessageDeltaUsage, u as reconcileAnthropicToolChoice, v as applyAnthropicFallbackBoundary, w as createAnthropicInlineImageBudget, x as applyAnthropicRefusal, y as readAnthropicFallbackBoundary } from "./anthropic-usage-Ma4iX6uG.mjs";
11
- import { n as toErrorObject, t as createResponsesPromptEgressObserver } from "./openai-responses-prompt-observer-internal-DDZPxRfr.mjs";
12
- import { a as convertMessages, c as resolveOpenAICompletionsResponseFormat, d as rememberPendingCommentaryTags, f as tagPendingCommentaryText, i as finalizeOpenAICompletionsToolCalls, l as shouldOmitOllamaCompatResponseFormat, n as mapOpenAIStopReason, o as detectOpenAICompletionsCompat, r as createOpenAICompletionsToolCallDeltaNormalizer, s as resolveOpenAICompletionsCompat, t as resolveOpenAIReasoningEffortMap, u as clearPendingCommentaryText } from "./openai-reasoning-compat-DKebnIBL.mjs";
14
+ import { f as sortPromptCacheToolsByName, l as stripSystemPromptCacheBoundary, t as adjustMaxTokensForThinking } from "./simple-options-D58D5Kvw.mjs";
15
+ import { a as resolveProviderEndpoint, c as transformTransportMessages, i as resolveOpenAIStrictToolSetting, n as buildGuardedModelFetch, r as resolveModelRequestTimeoutMs, s as resolveProviderRequestPolicyConfig } from "./tool-schema-json-projection-q5d7QX5c.mjs";
16
+ import { A as resolveAnthropicImageMediaType, C as readAnthropicFallbackBoundary, D as usesFoundryBearerAuth, E as omitFoundryBearerCredentialHeaders, F as resolveAnthropicPayloadPolicy, I as resolveAnthropicServerCompactionPlan, M as applyAnthropicEphemeralCacheControlMarkers, N as applyAnthropicPayloadPolicyToParams, O as createAnthropicInlineImageBudget, P as resolveAnthropicEphemeralCacheControl, S as applyAnthropicFallbackBoundary, T as applyAnthropicRefusal, _ as ANTHROPIC_OMITTED_REASONING_TEXT, a as applyAnthropicMessageDeltaUsage, b as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, d as normalizeAnthropicToolCallId, f as normalizeAnthropicToolChoice, g as toClaudeCodeToolName, h as resolveOriginalAnthropicToolName, i as suppressAnthropicCompaction, j as applyAnthropicCacheControlToMessages, k as normalizeAnthropicInlineContent, m as reconcileAnthropicToolChoice, n as createCompactionCapture, o as applyAnthropicMessageStartUsage, p as projectAnthropicTools, r as isAnthropicReplayRejection, t as buildAnthropicReplayPlan, v as findActiveAnthropicToolTurnAssistantIndex, w as resolveAnthropicFallbackServingModelCost, y as ANTHROPIC_SERVER_SIDE_FALLBACKS } from "./anthropic-compaction-replay-8lJNKXOE.mjs";
17
+ import { a as createOpenAICompletionsToolCallDeltaNormalizer, c as resolveOpenAICompletionsCompat, d as clearPendingCommentaryText, f as rememberPendingCommentaryTags, i as hasToolCallHistory, l as resolveOpenAICompletionsResponseFormat, n as resolveOpenAIReasoningEffortMap, o as finalizeOpenAICompletionsToolCalls, p as tagPendingCommentaryText, r as convertMessages, s as detectOpenAICompletionsCompat, t as mapOpenAIStopReason, u as shouldOmitOllamaCompatResponseFormat } from "./openai-stop-reason-BkFkqqK0.mjs";
13
18
  import { t as createDeferredEventBuffer } from "./deferred-event-buffer-DAvyP7qA.mjs";
14
- import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
15
- import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
16
- import { A as normalizeResponsesFailedEvent, C as prepareOpenAIResponsesReasoningItemForReplay, D as applyServiceTierPricing, E as tagOpenAIResponsesReasoningReplayItem, F as summarizeResponsesFailedNoDetailsObservation, G as normalizeOpenAIStrictToolParameters, I as summarizeResponsesPayload, L as summarizeResponsesTools, M as stringifyRedactedEvent, N as stringifyRedactedPayload, O as buildResponsesFailedNoDetailsObservation, P as summarizeOpenAITransportError, R as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, S as isInvalidEncryptedContentError, T as stripResponsesRequestEncryptedContent, U as findOpenAIStrictToolProjectionDiagnostics, V as resolveReplayableResponsesMessageId, _t as resolveModelSseDebugMode, b as convertResponsesMessages, bt as quoteUnsafeIntegerLiterals, ct as isOpenAIGpt56Model, ft as supportsOpenAIReasoningEffort, gt as resolveModelPayloadDebugMode, ht as emitModelTransportDebug, j as safeDebugValue, k as logResponsesFailedNoDetails, lt as normalizeOpenAIReasoningEffort, mt as uniqueStrings, n as processResponsesStream, o as observeResponsesStream, ot as isOpenAIGpt54MiniModel, q as resolveOpenAIProjectedToolsStrictToolFlag, st as isOpenAIGpt55Model, t as ResponsesStreamFailure, ut as resolveOpenAIReasoningEffortForModel, v as buildOpenAIResponsesReasoningReplayMetadata, vt as parseJsonObjectPreservingUnsafeIntegers, w as resolveAzureOpenAIApiVersion, x as createResponsesStreamWithEncryptedContentRetry, y as buildResponsesInputMessage, yt as parseJsonPreservingUnsafeIntegers, z as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS } from "./openai-responses-stream-internal-Bl8YyVtF.mjs";
17
- import { a as parseStrictPositiveInteger } from "./number-coercion-1Miyb5MO.mjs";
18
- import { C as resolveSecretSentinel, S as resolveModelHeaderSentinels$1, T as supportsModelTools, _ as isGoogleGemini3ProModel, a as failTransportStream, c as mergeTransportMetadata, d as transportAbortError, f as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE, g as isGoogleGemini3FlashModel, h as isCodeModeModelVisibleToolName, i as createWritableTransportEventStream, l as sanitizeNonEmptyTransportPayloadText, m as estimateStringChars, n as coerceTransportToolCallArguments, o as finalizeTransportStream, p as createAbortError$1, r as createEmptyTransportUsage, s as mergeTransportHeaders, t as assignTransportErrorDetails, u as sanitizeTransportPayloadText, v as parseRetryAfterSeconds, w as sha256Hex, y as readResponseTextSnippet } from "./transport-stream-shared-CPNv7A3r.mjs";
19
- import { a as parseOpenAICompletionsUsage, c as throwIfModelStreamAborted, d as reconcileOpenAIResponsesToolChoice, i as log, l as projectOpenAITools, n as createModelStreamCooperativeScheduler, o as readOpenAICompletionsContentDeltas, r as isOpenAICompletionsThinkingEnabled, s as resolvePromptCacheKey, t as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, u as reconcileOpenAICompletionsToolChoice } from "./openai-transport-shared-Cipt7egQ.mjs";
20
- import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-CGDyLWUR.mjs";
21
- import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-BIBomOGq.mjs";
19
+ import { n as parseStreamingJson } from "./json-parse-CDnesDM_.mjs";
20
+ import { n as notifyLlmRequestActivity } from "./headers-DdOQtGuU.mjs";
21
+ import { B as buildResponsesInputMessage, C as summarizeResponsesTools, G as suppressOpenAIResponsesCompaction, H as createOpenAIResponsesAssistantOutput, I as createResponsesStreamWithEncryptedContentRetry, K as resolveReplayableResponsesMessageId, L as isInvalidEncryptedContentError, R as resolveAzureOpenAIApiVersion, S as summarizeResponsesPayload, U as buildOpenAIResponsesReasoningReplayMetadata, V as convertResponsesMessages, W as captureOpenAIResponsesCompaction, Y as normalizeOpenAIStrictToolParameters, Z as resolveOpenAIProjectedToolsStrictToolFlag, _ as safeDebugValue, b as summarizeOpenAITransportError, c as processResponsesStream, dt as resolveModelPayloadDebugMode, f as observeResponsesStream, ft as resolveModelSseDebugMode, g as normalizeResponsesFailedEvent, h as logResponsesFailedNoDetails, ht as quoteUnsafeIntegerLiterals, m as buildResponsesFailedNoDetailsObservation, mt as parseJsonPreservingUnsafeIntegers, n as applyResponsesServiceTierPricing, p as ResponsesStreamFailure, pt as parseJsonObjectPreservingUnsafeIntegers, q as findOpenAIStrictToolProjectionDiagnostics, ut as emitModelTransportDebug, v as stringifyRedactedEvent, x as summarizeResponsesFailedNoDetailsObservation, y as stringifyRedactedPayload, z as resolveNextResponsesEncryptedContentAttempt } from "./openai-responses-shared-DXIt3iY5.mjs";
22
+ import { a as createWritableTransportEventStream, c as mergeTransportHeaders, d as sanitizeTransportPayloadText, f as transportAbortError, i as createEmptyTransportUsage, l as mergeTransportMetadata, n as assignTransportErrorDetails, o as failTransportStream, p as withProviderResponseHook, r as coerceTransportToolCallArguments, s as finalizeTransportStream, u as sanitizeNonEmptyTransportPayloadText } from "./provider-transcript-transform-ePx-Bbfr.mjs";
23
+ import { a as isGoogleGemini3FlashModel, c as readResponseTextSnippet, d as resolveModelHeaderSentinels$1, f as resolveSecretSentinel, i as isCodeModeModelVisibleToolName, l as redactIdentifier, m as supportsModelTools, n as createAbortError$1, o as isGoogleGemini3ProModel, p as sha256Hex, r as estimateStringChars, s as parseRetryAfterSeconds, t as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE } from "./transport-utils-DJqkxbhC.mjs";
24
+ import { t as googleFlashSupportsMinimalThinking } from "./google-thinking-level-C-V3tecN.mjs";
25
+ import { a as createModelStreamCooperativeScheduler, c as log, d as readOpenAICompletionsContentDeltas, f as resolvePromptCacheKey, i as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, l as measureUtf8AppendBytes, n as reconcileOpenAICompletionsToolChoice, o as createOpenAIResponseHook, p as throwIfModelStreamAborted, r as reconcileOpenAIResponsesToolChoice, s as isOpenAICompletionsThinkingEnabled, t as projectOpenAITools, u as parseOpenAICompletionsUsage } from "./openai-tool-projection-CY04OcvQ.mjs";
26
+ import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-MK28puvq.mjs";
27
+ import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-rnPwX2pg.mjs";
28
+ import { i as resolveOpenAIResponsesServerCompactionPlan, n as resolveOpenAIResponsesCompactEndpointPlan, r as resolveOpenAIResponsesPayloadPolicy, t as applyOpenAIResponsesPayloadPolicy } from "./openai-responses-payload-policy-BDxV-W0c.mjs";
22
29
  import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-C7K7QfUE.mjs";
23
- import { t as headersToRecord } from "./headers-B_e4-1J0.mjs";
30
+ import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
24
31
  import { randomUUID } from "node:crypto";
25
32
  import OpenAI, { AzureOpenAI } from "openai";
33
+ import { ResponsesWS } from "openai/resources/responses/ws.js";
26
34
  //#region packages/ai/src/transports/anthropic-transport-stream.ts
27
35
  /**
28
36
  * Native Anthropic Messages streaming transport.
@@ -169,7 +177,7 @@ async function convertAnthropicMessages(messages, model, isOAuthToken, options)
169
177
  continue;
170
178
  }
171
179
  if (msg.role === "assistant") {
172
- const blocks = [];
180
+ const blocks = i === 0 && options.compaction ? [options.compaction] : [];
173
181
  const reasoningContent = [];
174
182
  let omittedThinking = false;
175
183
  for (const block of msg.content) {
@@ -530,9 +538,15 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
530
538
  enableCacheControl: true
531
539
  });
532
540
  const cacheBreakpointOptOutMessageIndexes = /* @__PURE__ */ new Set();
533
- const messages = await convertAnthropicMessages(context.messages, model, isOAuthToken, {
541
+ const replayPlan = buildAnthropicReplayPlan(context.messages, model, {
542
+ enabled: !isOAuthToken && options?.anthropicServerCompaction === true,
543
+ authProfileId: options?.authProfileId,
544
+ sessionId: options?.sessionId
545
+ });
546
+ const messages = await convertAnthropicMessages(replayPlan.messages, model, isOAuthToken, {
534
547
  allowReasoningContentReplay: supportsReasoningContentReplay(model),
535
548
  cacheBreakpointOptOutMessageIndexes,
549
+ compaction: replayPlan.compaction,
536
550
  replayThinkingEnabled
537
551
  });
538
552
  const params = {
@@ -591,7 +605,8 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
591
605
  applyAnthropicPayloadPolicyToParams(params, payloadPolicy, cacheBreakpointOptOutMessageIndexes);
592
606
  return {
593
607
  params,
594
- toolProjection
608
+ toolProjection,
609
+ usedCompactionReplay: replayPlan.compaction !== void 0
595
610
  };
596
611
  }
597
612
  function resolveAnthropicTransportOptions(model, options, apiKey) {
@@ -620,7 +635,9 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
620
635
  interleavedThinking: options?.interleavedThinking,
621
636
  toolChoice: options?.toolChoice,
622
637
  thinkingBudgets: options?.thinkingBudgets,
623
- reasoning
638
+ reasoning,
639
+ ...options?.anthropicServerCompaction === true ? { anthropicServerCompaction: true } : {},
640
+ ...options?.authProfileId ? { authProfileId: options.authProfileId } : {}
624
641
  };
625
642
  if (reasoning === "off") {
626
643
  resolved.thinkingEnabled = false;
@@ -664,6 +681,7 @@ function createAnthropicMessagesTransportStreamFn() {
664
681
  const eventSink = refusalBuffer ?? stream;
665
682
  let costModel = model;
666
683
  let messageStartPromptUsage;
684
+ let usedCompactionReplay = false;
667
685
  try {
668
686
  const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? "";
669
687
  if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
@@ -676,6 +694,7 @@ function createAnthropicMessagesTransportStreamFn() {
676
694
  options: transportOptions
677
695
  });
678
696
  const builtParams = await buildAnthropicParams(model, requestContext, isOAuthToken, transportOptions);
697
+ usedCompactionReplay = builtParams.usedCompactionReplay;
679
698
  let params = builtParams.params;
680
699
  const toolProjection = builtParams.toolProjection;
681
700
  const nextParams = await transportOptions.onPayload?.(params, model);
@@ -687,6 +706,7 @@ function createAnthropicMessagesTransportStreamFn() {
687
706
  }, transportOptions.signal ? { signal: transportOptions.signal } : void 0);
688
707
  const blocks = output.content;
689
708
  const blockIndexes = /* @__PURE__ */ new Map();
709
+ const compactionCapture = createCompactionCapture(output, model, transportOptions);
690
710
  const pendingThinkingSignatures = /* @__PURE__ */ new Map();
691
711
  const allowReasoningContentReplay = supportsReasoningContentReplay(model);
692
712
  const reasoningContentThinkingBlocks = /* @__PURE__ */ new Map();
@@ -809,6 +829,7 @@ function createAnthropicMessagesTransportStreamFn() {
809
829
  if (event.type === "content_block_start") {
810
830
  const contentBlock = event.content_block;
811
831
  const index = typeof event.index === "number" ? event.index : -1;
832
+ if (transportOptions.anthropicServerCompaction === true && compactionCapture.begin(index, contentBlock, output.content.length)) continue;
812
833
  const fallbackBoundary = refusalBuffer ? readAnthropicFallbackBoundary(contentBlock) : null;
813
834
  if (fallbackBoundary) {
814
835
  refusalBuffer?.discard();
@@ -945,6 +966,7 @@ function createAnthropicMessagesTransportStreamFn() {
945
966
  if (event.type === "content_block_delta") {
946
967
  const delta = event.delta;
947
968
  const eventIndex = typeof event.index === "number" ? event.index : void 0;
969
+ if (eventIndex !== void 0 && compactionCapture.delta(eventIndex, delta)) continue;
948
970
  let index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
949
971
  let block = index === void 0 ? void 0 : blocks[index];
950
972
  if (allowReasoningContentReplay) {
@@ -1025,6 +1047,7 @@ function createAnthropicMessagesTransportStreamFn() {
1025
1047
  }
1026
1048
  if (event.type === "content_block_stop") {
1027
1049
  const eventIndex = typeof event.index === "number" ? event.index : void 0;
1050
+ if (eventIndex !== void 0 && compactionCapture.complete(eventIndex)) continue;
1028
1051
  const pendingSignature = eventIndex === void 0 ? void 0 : pendingThinkingSignatures.get(eventIndex);
1029
1052
  if (eventIndex !== void 0) pendingThinkingSignatures.delete(eventIndex);
1030
1053
  const index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
@@ -1079,7 +1102,7 @@ function createAnthropicMessagesTransportStreamFn() {
1079
1102
  flushPendingTextEnds();
1080
1103
  }
1081
1104
  }
1082
- if (refusalBuffer && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
1105
+ if (isDirectAnthropicModel(model) && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
1083
1106
  if (transportOptions.signal?.aborted) throw transportAbortError(transportOptions.signal);
1084
1107
  if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
1085
1108
  refusalBuffer?.flush();
@@ -1094,6 +1117,7 @@ function createAnthropicMessagesTransportStreamFn() {
1094
1117
  refusalBuffer.discard();
1095
1118
  output.content = [];
1096
1119
  }
1120
+ if (usedCompactionReplay && isAnthropicReplayRejection(error)) suppressAnthropicCompaction(output, model, options);
1097
1121
  failTransportStream({
1098
1122
  stream,
1099
1123
  output,
@@ -1210,15 +1234,11 @@ const MAX_TOKENS_PARAM_KEYS = [
1210
1234
  "max_completion_tokens",
1211
1235
  "max_tokens"
1212
1236
  ];
1213
- /** Return a finite non-negative max-token value, or undefined for invalid input. */
1214
- function resolveNonNegativeMaxTokensParam(value) {
1215
- return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1216
- }
1217
1237
  /** Resolve the first supported max-token parameter present in a params object. */
1218
1238
  function resolveMaxTokensParam(params) {
1219
1239
  if (!params) return;
1220
1240
  for (const key of MAX_TOKENS_PARAM_KEYS) {
1221
- const resolved = resolveNonNegativeMaxTokensParam(params[key]);
1241
+ const resolved = asNonNegativeFiniteNumber(params[key]);
1222
1242
  if (resolved !== void 0) return resolved;
1223
1243
  }
1224
1244
  }
@@ -1335,6 +1355,172 @@ function stripCompletionMessagesToRoleContent(messages) {
1335
1355
  });
1336
1356
  }
1337
1357
  //#endregion
1358
+ //#region packages/ai/src/transports/openai-completions-host.ts
1359
+ /**
1360
+ * Chat Completions accepts Azure AI Foundry hosts in addition to traditional
1361
+ * Azure OpenAI hosts. Do not replace this with isTraditionalAzureOpenAIHost,
1362
+ * which intentionally excludes the .services.ai.azure.com Foundry suffix.
1363
+ */
1364
+ function isAzureOpenAICompatibleHost(hostname) {
1365
+ return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
1366
+ }
1367
+ //#endregion
1368
+ //#region packages/ai/src/transports/openai-completions-replay.ts
1369
+ function isGoogleOpenAICompatModel(model) {
1370
+ const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
1371
+ return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
1372
+ }
1373
+ function requiresGoogleCompatToolCallThoughtSignature(model) {
1374
+ return isGoogleGemini3ProModel(model.id) || isGoogleGemini3FlashModel(model.id);
1375
+ }
1376
+ const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
1377
+ const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
1378
+ function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
1379
+ return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
1380
+ }
1381
+ function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
1382
+ if (!isGoogleOpenAICompatModel(model)) return;
1383
+ const sigById = /* @__PURE__ */ new Map();
1384
+ const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
1385
+ for (const msg of context.messages ?? []) {
1386
+ if (msg.role !== "assistant") continue;
1387
+ const source = msg;
1388
+ if (!Array.isArray(source.content)) continue;
1389
+ for (const block of source.content) {
1390
+ if (block.type !== "toolCall") continue;
1391
+ const id = block.id;
1392
+ const sig = block.thoughtSignature;
1393
+ if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
1394
+ const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
1395
+ if (!isSameRoute && !fallbackSig) continue;
1396
+ sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
1397
+ }
1398
+ }
1399
+ }
1400
+ if (sigById.size === 0 && !fallbackSig) return;
1401
+ for (const message of outgoingMessages) {
1402
+ const toolCalls = message.tool_calls;
1403
+ if (!Array.isArray(toolCalls)) continue;
1404
+ for (const toolCall of toolCalls) {
1405
+ const id = toolCall.id;
1406
+ if (typeof id !== "string") continue;
1407
+ let sig = sigById.get(id) ?? fallbackSig;
1408
+ if (typeof sig === "string" && sig.length > 0) {
1409
+ if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
1410
+ }
1411
+ if (typeof sig !== "string" || sig.length === 0) continue;
1412
+ const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
1413
+ toolCall.extra_content = extra;
1414
+ const google = extra.google && typeof extra.google === "object" ? extra.google : {};
1415
+ extra.google = google;
1416
+ google.thought_signature = sig;
1417
+ }
1418
+ }
1419
+ }
1420
+ const COMPLETIONS_REASONING_REPLAY_FIELDS = [
1421
+ "reasoning_details",
1422
+ "reasoning_content",
1423
+ "reasoning",
1424
+ "reasoning_text"
1425
+ ];
1426
+ function stripCompletionsReasoningReplayFields(record) {
1427
+ for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
1428
+ }
1429
+ function sanitizeOpenRouterReasoningReplayFields(record) {
1430
+ const reasoningDetails = record.reasoning_details;
1431
+ if (typeof reasoningDetails === "string") {
1432
+ if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
1433
+ delete record.reasoning_details;
1434
+ } else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
1435
+ if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
1436
+ if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
1437
+ const reasoningText = record.reasoning_text;
1438
+ if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
1439
+ if ("reasoning_text" in record) delete record.reasoning_text;
1440
+ }
1441
+ function sanitizeReasoningContentReplayFields(record) {
1442
+ if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
1443
+ delete record.reasoning_details;
1444
+ delete record.reasoning;
1445
+ delete record.reasoning_text;
1446
+ }
1447
+ const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
1448
+ "deepseek-v4-flash",
1449
+ "deepseek-v4-pro",
1450
+ "kimi-for-coding",
1451
+ "kimi-k2.5",
1452
+ "kimi-k2.6",
1453
+ "kimi-k2.7-code",
1454
+ "kimi-k2.7-code-highspeed",
1455
+ "kimi-k3",
1456
+ "kimi-k2-thinking",
1457
+ "kimi-k2-thinking-turbo",
1458
+ "mimo-v2-pro",
1459
+ "mimo-v2-omni",
1460
+ "mimo-v2.5",
1461
+ "mimo-v2.5-pro",
1462
+ "mimo-v2.6-pro"
1463
+ ]);
1464
+ const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
1465
+ "-free",
1466
+ "-paid",
1467
+ "-trial"
1468
+ ];
1469
+ function stripReasoningContentReplayTierSuffix(modelId) {
1470
+ for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
1471
+ return modelId;
1472
+ }
1473
+ function getReasoningContentReplayModelIdCandidates(modelId) {
1474
+ if (typeof modelId !== "string") return [];
1475
+ const normalized = modelId.trim().toLowerCase();
1476
+ if (!normalized) return [];
1477
+ const parts = normalized.split("/").filter(Boolean);
1478
+ const finalPart = parts[parts.length - 1] ?? normalized;
1479
+ const candidates = [finalPart];
1480
+ const colonParts = finalPart.split(":").filter(Boolean);
1481
+ if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
1482
+ const baseCount = candidates.length;
1483
+ for (let index = 0; index < baseCount; index += 1) {
1484
+ const candidate = candidates[index];
1485
+ if (typeof candidate !== "string") continue;
1486
+ const stripped = stripReasoningContentReplayTierSuffix(candidate);
1487
+ if (stripped !== candidate) candidates.push(stripped);
1488
+ }
1489
+ return uniqueStrings(candidates.filter(Boolean));
1490
+ }
1491
+ function shouldPreserveReasoningContentReplay(model, compat) {
1492
+ if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
1493
+ return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
1494
+ }
1495
+ function shouldPreserveOpenRouterReasoningReplay(model) {
1496
+ if (model.provider !== "openrouter") return true;
1497
+ const normalizedModelId = model.id.trim().toLowerCase();
1498
+ return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
1499
+ }
1500
+ function shouldTrustReasoningContentReplayMetadata(model) {
1501
+ if (!model.reasoning) return false;
1502
+ if (model.provider.trim().toLowerCase() === "openai") return false;
1503
+ return shouldPreserveOpenRouterReasoningReplay(model);
1504
+ }
1505
+ function sanitizeCompletionsReasoningReplayFields(messages, options) {
1506
+ if (!Array.isArray(messages)) return;
1507
+ for (const msg of messages) {
1508
+ if (!msg || typeof msg !== "object") continue;
1509
+ const record = msg;
1510
+ if (record.role !== "assistant") continue;
1511
+ if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
1512
+ else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
1513
+ else stripCompletionsReasoningReplayFields(record);
1514
+ }
1515
+ }
1516
+ function applyCompletionsReplay(outgoingMessages, context, model, compat) {
1517
+ injectToolCallThoughtSignatures(outgoingMessages, context, model);
1518
+ sanitizeCompletionsReasoningReplayFields(outgoingMessages, {
1519
+ preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
1520
+ preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
1521
+ });
1522
+ }
1523
+ //#endregion
1338
1524
  //#region packages/ai/src/transports/openai-transport-params.ts
1339
1525
  const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
1340
1526
  const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
@@ -1355,13 +1541,28 @@ function readCodeModePayloadToolName(tool) {
1355
1541
  const fnName = readToolPayloadField(fn, "name");
1356
1542
  return typeof fnName === "string" ? fnName : void 0;
1357
1543
  }
1358
- function filterCodeModePayloadTools(payload, visibleToolNames) {
1544
+ function readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes) {
1545
+ if (!isRecord(tool)) return;
1546
+ const type = readToolPayloadField(tool, "type");
1547
+ if (typeof type === "string" && allowedHostedToolTypes?.has(type)) {
1548
+ try {
1549
+ if (Object.hasOwn(tool, "name") || Object.hasOwn(tool, "function") || Object.hasOwn(tool, "functionDeclarations") || Object.hasOwn(tool, "function_declarations")) return false;
1550
+ } catch {
1551
+ return false;
1552
+ }
1553
+ return `hosted:${type}`;
1554
+ }
1555
+ const name = readCodeModePayloadToolName(tool);
1556
+ return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames) ? `client:${name}` : void 0;
1557
+ }
1558
+ function filterCodeModePayloadTools(payload, visibleToolNames, allowedHostedToolTypes) {
1359
1559
  if (!isRecord(payload)) return;
1360
1560
  const tools = readToolPayloadField(payload, "tools");
1361
1561
  if (!Array.isArray(tools)) return;
1362
1562
  payload.tools = tools.flatMap((tool) => {
1363
- const name = readCodeModePayloadToolName(tool);
1364
- if (typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames)) return [tool];
1563
+ const identity = readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes);
1564
+ if (identity) return [tool];
1565
+ if (identity === false) return [];
1365
1566
  if (!isRecord(tool)) return [];
1366
1567
  const filteredGroups = {};
1367
1568
  for (const key of ["functionDeclarations", "function_declarations"]) {
@@ -1379,20 +1580,18 @@ function filterCodeModePayloadTools(payload, visibleToolNames) {
1379
1580
  function resolveCodeModeResponsesVisibleToolNames(context) {
1380
1581
  return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
1381
1582
  }
1382
- function enforceCodeModeResponsesToolSurface(payload, visibleToolNames) {
1583
+ function enforceCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
1383
1584
  if (!isRecord(payload)) return;
1384
1585
  const tools = readToolPayloadField(payload, "tools");
1385
1586
  if (!Array.isArray(tools)) return;
1386
- payload.tools = tools.filter((tool) => {
1387
- const name = readCodeModePayloadToolName(tool);
1388
- return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames);
1389
- });
1587
+ payload.tools = tools.filter((tool) => Boolean(readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes)));
1390
1588
  }
1391
- function assertCodeModeResponsesToolSurface(payload, visibleToolNames) {
1589
+ function assertCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
1392
1590
  const tools = isRecord(payload) ? readToolPayloadField(payload, "tools") : void 0;
1393
1591
  if (!Array.isArray(tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
1394
- const names = tools.map(readCodeModePayloadToolName).filter((name) => typeof name === "string" && name.length > 0).toSorted((left, right) => left.localeCompare(right));
1395
- if (names.length >= 2 && names.length === tools.length && new Set(names).size === names.length && names.includes("exec") && names.includes("wait") && names.every((name) => isCodeModeModelVisibleToolName(name, visibleToolNames))) return;
1592
+ const identities = tools.map((tool) => readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes));
1593
+ const names = identities.flatMap((identity) => typeof identity === "string" && identity.startsWith("client:") ? [identity.slice(7)] : []).toSorted((left, right) => left.localeCompare(right));
1594
+ if (names.length >= 2 && identities.every((identity) => typeof identity === "string") && new Set(identities).size === identities.length && names.includes("exec") && names.includes("wait")) return;
1396
1595
  throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
1397
1596
  }
1398
1597
  function buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context) {
@@ -1514,64 +1713,7 @@ function getCompat(model) {
1514
1713
  };
1515
1714
  }
1516
1715
  //#endregion
1517
- //#region packages/ai/src/transports/openai-completions-transport.ts
1518
- function hasToolHistory(messages) {
1519
- return messages.some((message) => message.role === "toolResult" || message.role === "assistant" && Array.isArray(message.content) && message.content.some((block) => block.type === "toolCall"));
1520
- }
1521
- function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
1522
- const messages = params.messages;
1523
- if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
1524
- 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.`);
1525
- }
1526
- const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
1527
- const SSE_DONE_MAX_LINE_CHARS = 1024;
1528
- function createSseDoneDetector() {
1529
- const decoder = new TextDecoder();
1530
- let line = "";
1531
- let lineOverflowed = false;
1532
- let sawDone = false;
1533
- const finishLine = () => {
1534
- if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
1535
- line = "";
1536
- lineOverflowed = false;
1537
- };
1538
- const observeText = (text) => {
1539
- for (const char of text) {
1540
- if (char === "\n" || char === "\r") {
1541
- finishLine();
1542
- continue;
1543
- }
1544
- if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
1545
- else lineOverflowed = true;
1546
- }
1547
- };
1548
- return {
1549
- observe(chunk) {
1550
- if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
1551
- },
1552
- finish() {
1553
- if (sawDone) return;
1554
- observeText(decoder.decode());
1555
- if (line || lineOverflowed) finishLine();
1556
- },
1557
- sawDone: () => sawDone
1558
- };
1559
- }
1560
- function createOpenAICompletionsClient(model, context, apiKey, optionHeaders, opts) {
1561
- const clientConfig = buildOpenAICompletionsClientConfig(model, context, optionHeaders);
1562
- return new OpenAI({
1563
- apiKey,
1564
- baseURL: clientConfig.baseURL,
1565
- dangerouslyAllowBrowser: true,
1566
- defaultHeaders: clientConfig.defaultHeaders,
1567
- defaultQuery: clientConfig.defaultQuery,
1568
- fetch: opts?.fetch ?? buildGuardedModelFetch(model),
1569
- ...buildOpenAISdkClientOptions(model)
1570
- });
1571
- }
1572
- function isAzureOpenAICompatibleHost(hostname) {
1573
- return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
1574
- }
1716
+ //#region packages/ai/src/transports/openai-completions-params.ts
1575
1717
  function isKnownOpenAICompletionsEndpoint(model) {
1576
1718
  if (!model.baseUrl.trim()) return true;
1577
1719
  const endpointClass = resolveProviderEndpoint(model.baseUrl).endpointClass;
@@ -1582,490 +1724,232 @@ function isKnownOpenAICompletionsEndpoint(model) {
1582
1724
  return false;
1583
1725
  }
1584
1726
  }
1585
- function buildOpenAICompletionsClientConfig(model, context, optionHeaders) {
1586
- const headers = buildOpenAIClientHeaders(model, context, optionHeaders);
1587
- const defaultQuery = {};
1588
- let baseURL = model.baseUrl;
1589
- let isAzureHost = false;
1590
- try {
1591
- const parsed = new URL(model.baseUrl);
1592
- isAzureHost = isAzureOpenAICompatibleHost(parsed.hostname.toLowerCase());
1593
- parsed.searchParams.forEach((value, key) => {
1594
- if (value) defaultQuery[key] = value;
1595
- });
1596
- parsed.search = "";
1597
- baseURL = parsed.toString().replace(/\/$/, "");
1598
- } catch {}
1599
- if (isAzureHost) {
1600
- const apiVersionHeader = Object.keys(headers).find((key) => key.toLowerCase() === "api-version");
1601
- if (apiVersionHeader) {
1602
- const apiVersion = headers[apiVersionHeader]?.trim();
1603
- delete headers[apiVersionHeader];
1604
- if (apiVersion && !defaultQuery["api-version"]) defaultQuery["api-version"] = apiVersion;
1605
- }
1606
- }
1607
- return {
1608
- baseURL,
1609
- defaultHeaders: headers,
1610
- defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
1611
- };
1727
+ function resolveOpenAICompletionsReasoningEffort$1(options) {
1728
+ return options?.reasoningEffort ?? options?.reasoning ?? "high";
1612
1729
  }
1613
- function createOpenAICompletionsTransportStreamFn() {
1614
- return (model, context, options) => {
1615
- const eventStream = createAssistantMessageEventStream();
1616
- const stream = eventStream;
1617
- (async () => {
1618
- const output = {
1619
- role: "assistant",
1620
- content: [],
1621
- api: model.api,
1622
- provider: model.provider,
1623
- model: model.id,
1624
- usage: {
1625
- input: 0,
1626
- output: 0,
1627
- cacheRead: 0,
1628
- cacheWrite: 0,
1629
- totalTokens: 0,
1630
- cost: {
1631
- input: 0,
1632
- output: 0,
1633
- cacheRead: 0,
1634
- cacheWrite: 0,
1635
- total: 0
1636
- }
1637
- },
1638
- stopReason: "stop",
1639
- timestamp: Date.now()
1640
- };
1641
- let firstEventAbort;
1642
- try {
1643
- const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
1644
- const doneDetector = createSseDoneDetector();
1645
- const baseFetch = buildGuardedModelFetch(model);
1646
- const doneDetectingFetch = async (url, init) => {
1647
- const response = await baseFetch(url, init);
1648
- if (!response.body || !response.ok) return response;
1649
- if (typeof TransformStream === "undefined" || !response.body.pipeThrough) return response;
1650
- const transformed = response.body.pipeThrough(new TransformStream({
1651
- transform(chunk, controller) {
1652
- doneDetector.observe(chunk);
1653
- controller.enqueue(chunk);
1654
- },
1655
- flush() {
1656
- doneDetector.finish();
1657
- }
1658
- }));
1659
- return new Response(transformed, {
1660
- headers: response.headers,
1661
- status: response.status,
1662
- statusText: response.statusText
1663
- });
1664
- };
1665
- const client = createOpenAICompletionsClient(model, context, apiKey, options?.headers, { fetch: doneDetectingFetch });
1666
- let params = buildOpenAICompletionsParams(model, context, options);
1667
- const nextParams = await options?.onPayload?.(params, model);
1668
- if (nextParams !== void 0) params = nextParams;
1669
- if (options?.openclawCodeModeToolSurface === true) {
1670
- const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
1671
- enforceCodeModeResponsesToolSurface(params, visibleToolNames);
1672
- assertCodeModeResponsesToolSurface(params, visibleToolNames);
1673
- }
1674
- if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
1675
- const emitReasoning = shouldEmitOpenAICompletionsReasoning(model, options);
1676
- firstEventAbort = createFirstStreamEventAbortController(options?.signal);
1677
- const responseStream = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
1678
- timeoutMs: options?.timeoutMs,
1679
- maxRetries: options?.maxRetries
1680
- }));
1681
- stream.push({
1682
- type: "start",
1683
- partial: output
1684
- });
1685
- await processOpenAICompletionsStream(responseStream, output, model, stream, {
1686
- signal: options?.signal,
1687
- emitReasoning,
1688
- firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
1689
- abortFirstEventStream: firstEventAbort.abort,
1690
- onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
1691
- sawStreamDONE: doneDetector.sawDone
1692
- });
1693
- finalizeTransportStream({
1694
- stream,
1695
- output,
1696
- signal: options?.signal
1697
- });
1698
- } catch (error) {
1699
- failTransportStream({
1700
- stream,
1701
- output,
1702
- signal: options?.signal,
1703
- error,
1704
- cleanup: () => {
1705
- output.stopReason = options?.signal?.aborted ? "aborted" : "error";
1706
- finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
1707
- }
1708
- });
1709
- } finally {
1710
- firstEventAbort?.dispose();
1711
- }
1712
- })();
1713
- return eventStream;
1714
- };
1715
- }
1716
- async function processOpenAICompletionsStream(responseStream, output, model, stream, options) {
1717
- const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
1718
- const MAX_TOOL_CALL_ARGUMENT_BUFFER_BYTES = 256e3;
1719
- const emitReasoning = options?.emitReasoning ?? true;
1720
- const compat = getCompat(model);
1721
- const deepSeekTextFilter = shouldFilterDeepSeekDsmlText(compat) ? createDeepSeekTextFilter() : null;
1722
- const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText(compat) ? createDeepSeekDsmlToolCallRecoverer() : null;
1723
- const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
1724
- let currentBlock = null;
1725
- let pendingPostToolCallDeltas = [];
1726
- let pendingPostToolCallBytes = 0;
1727
- let isFlushingPendingPostToolCallDeltas = false;
1728
- const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
1729
- const toolCallBlocksById = /* @__PURE__ */ new Map();
1730
- const provisionalCommentaryTags = /* @__PURE__ */ new Map();
1731
- const toolCallBlockBytes = /* @__PURE__ */ new WeakMap();
1732
- const toolCallBlockIndices = /* @__PURE__ */ new WeakMap();
1733
- const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
1734
- let sawStopFinishReason = false;
1735
- let sawNativeToolCallDelta = false;
1736
- const blockIndex = () => output.content.length - 1;
1737
- const measureUtf8Bytes = (text) => Buffer.byteLength(text, "utf8");
1738
- let chunkPushedEvent = false;
1739
- const pushStreamEvent = (event) => {
1740
- chunkPushedEvent = true;
1741
- stream.push(event);
1742
- };
1743
- const queuePostToolCallDelta = (next) => {
1744
- const nextBytes = measureUtf8Bytes(next.text);
1745
- if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
1746
- pendingPostToolCallBytes += nextBytes;
1747
- const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
1748
- if (!previous || previous.kind !== next.kind) {
1749
- pendingPostToolCallDeltas.push(next);
1750
- return;
1751
- }
1752
- if (next.kind === "thinking" && previous.kind === "thinking") {
1753
- if (previous.signature !== next.signature) {
1754
- pendingPostToolCallDeltas.push(next);
1755
- return;
1756
- }
1757
- previous.text += next.text;
1758
- return;
1759
- }
1760
- previous.text += next.text;
1761
- };
1762
- const appendThinkingDeltaInternal = (reasoningDelta) => {
1763
- if (!currentBlock || currentBlock.type !== "thinking") {
1764
- currentBlock = {
1765
- type: "thinking",
1766
- thinking: "",
1767
- ...reasoningDelta.signature ? { thinkingSignature: reasoningDelta.signature } : {}
1768
- };
1769
- output.content.push(currentBlock);
1770
- pushStreamEvent({
1771
- type: "thinking_start",
1772
- contentIndex: blockIndex(),
1773
- partial: output
1774
- });
1775
- }
1776
- currentBlock.thinking += reasoningDelta.text;
1777
- pushStreamEvent({
1778
- type: "thinking_delta",
1779
- contentIndex: blockIndex(),
1780
- delta: reasoningDelta.text,
1781
- partial: output
1782
- });
1783
- };
1784
- const appendTextDeltaInternal = (text) => {
1785
- if (!currentBlock || currentBlock.type !== "text") {
1786
- currentBlock = {
1787
- type: "text",
1788
- text: ""
1789
- };
1790
- output.content.push(currentBlock);
1791
- pushStreamEvent({
1792
- type: "text_start",
1793
- contentIndex: blockIndex(),
1794
- partial: output
1795
- });
1796
- }
1797
- currentBlock.text += text;
1798
- pushStreamEvent({
1799
- type: "text_delta",
1800
- contentIndex: blockIndex(),
1801
- delta: text
1802
- });
1803
- };
1804
- const flushPendingPostToolCallDeltas = () => {
1805
- if (isFlushingPendingPostToolCallDeltas || currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
1806
- isFlushingPendingPostToolCallDeltas = true;
1807
- const bufferedDeltas = pendingPostToolCallDeltas;
1808
- pendingPostToolCallDeltas = [];
1809
- pendingPostToolCallBytes = 0;
1810
- for (const delta of bufferedDeltas) if (delta.kind === "text") appendTextDeltaInternal(delta.text);
1811
- else if (emitReasoning) appendThinkingDeltaInternal(delta);
1812
- isFlushingPendingPostToolCallDeltas = false;
1813
- };
1814
- const appendThinkingDelta = (reasoningDelta) => {
1815
- flushPendingPostToolCallDeltas();
1816
- appendThinkingDeltaInternal(reasoningDelta);
1817
- };
1818
- const appendTextDelta = (text) => {
1819
- flushPendingPostToolCallDeltas();
1820
- appendTextDeltaInternal(text);
1821
- };
1822
- const appendVisibleTextDelta = (text) => {
1823
- if (!text) return;
1824
- if (currentBlock?.type === "toolCall") queuePostToolCallDelta({
1825
- kind: "text",
1826
- text
1827
- });
1828
- else appendTextDelta(text);
1829
- };
1830
- const appendRecoveredToolCall = (toolCall) => {
1831
- if (currentBlock?.type === "toolCall") {
1832
- currentBlock = null;
1833
- flushPendingPostToolCallDeltas();
1834
- }
1835
- rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
1836
- const block = {
1837
- type: "toolCall",
1838
- id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
1839
- name: toolCall.name,
1840
- arguments: toolCall.arguments,
1841
- partialArgs: toolCall.partialArgs
1842
- };
1843
- currentBlock = block;
1844
- output.content.push(block);
1845
- toolCallBlockIndices.set(block, output.content.length - 1);
1846
- pushStreamEvent({
1847
- type: "toolcall_start",
1848
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
1849
- partial: output
1850
- });
1851
- pushStreamEvent({
1852
- type: "toolcall_delta",
1853
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
1854
- delta: toolCall.partialArgs,
1855
- partial: output
1856
- });
1857
- };
1858
- const appendFilteredVisibleTextDelta = (text) => {
1859
- const recoveredParts = deepSeekToolCallRecoverer?.push(text) ?? [{
1860
- kind: "text",
1861
- text
1862
- }];
1863
- for (const recoveredPart of recoveredParts) {
1864
- if (recoveredPart.kind === "toolCall") {
1865
- appendRecoveredToolCall(recoveredPart);
1866
- continue;
1867
- }
1868
- const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
1869
- for (const part of parts) appendVisibleTextDelta(part);
1870
- }
1730
+ function resolveOpenAICompletionsMaxTokens(model, options) {
1731
+ if (options?.maxTokens) return {
1732
+ maxTokens: options.maxTokens,
1733
+ clampToModelMaxTokens: true
1871
1734
  };
1872
- const flushDeepSeekToolCallRecovererAtEnd = () => {
1873
- const recoveredParts = deepSeekToolCallRecoverer?.flush();
1874
- if (!recoveredParts) return;
1875
- for (const recoveredPart of recoveredParts) {
1876
- if (recoveredPart.kind === "toolCall") {
1877
- appendRecoveredToolCall(recoveredPart);
1878
- continue;
1879
- }
1880
- const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
1881
- for (const part of parts) appendVisibleTextDelta(part);
1882
- }
1735
+ const paramsMaxTokens = resolveMaxTokensParam(model.params);
1736
+ if (paramsMaxTokens) return {
1737
+ maxTokens: paramsMaxTokens,
1738
+ clampToModelMaxTokens: false
1883
1739
  };
1884
- const flushDeepSeekTextFilterAtEnd = () => {
1885
- const parts = deepSeekTextFilter?.flush();
1886
- if (!parts) return;
1887
- for (const part of parts) appendVisibleTextDelta(part);
1740
+ return {
1741
+ maxTokens: model.maxTokens,
1742
+ clampToModelMaxTokens: false
1888
1743
  };
1889
- const appendRoutedContentDelta = (delta) => {
1890
- if (delta.kind === "text") {
1891
- appendFilteredVisibleTextDelta(delta.text);
1892
- return;
1744
+ }
1745
+ function resolveOpenAICompletionsModelMaxTokens(model) {
1746
+ return typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
1747
+ }
1748
+ const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
1749
+ const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
1750
+ function estimateOpenAICompletionsInputTokens(payload) {
1751
+ let adjustedChars = 0;
1752
+ adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
1753
+ if (Array.isArray(payload.tools) && payload.tools.length > 0) try {
1754
+ adjustedChars += estimateStringChars(JSON.stringify(payload.tools));
1755
+ } catch {
1756
+ adjustedChars += 1024;
1757
+ }
1758
+ if (payload.response_format !== void 0) try {
1759
+ adjustedChars += estimateStringChars(JSON.stringify(payload.response_format));
1760
+ } catch {
1761
+ adjustedChars += 256;
1762
+ }
1763
+ return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
1764
+ }
1765
+ function estimateOpenAICompletionsMessagesChars(messages) {
1766
+ if (!Array.isArray(messages)) return 0;
1767
+ let adjustedChars = 0;
1768
+ for (const message of messages) {
1769
+ if (!message || typeof message !== "object") continue;
1770
+ const record = message;
1771
+ adjustedChars += estimateOpenAICompletionsContentChars(record.content);
1772
+ for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
1773
+ if (record.tool_calls !== void 0) try {
1774
+ adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
1775
+ } catch {
1776
+ adjustedChars += 256;
1893
1777
  }
1894
- if (!emitReasoning) return;
1895
- if (currentBlock?.type === "toolCall") queuePostToolCallDelta(delta);
1896
- else appendThinkingDelta(delta);
1897
- };
1898
- const appendPartitionedVisibleDelta = (delta) => {
1899
- if (delta.kind === "text") appendFilteredVisibleTextDelta(delta.text);
1900
- };
1901
- const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
1902
- if (!hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
1903
- const latestBlock = output.content[output.content.length - 1];
1904
- if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
1905
- if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
1906
- appendThinkingDelta({ text: "" });
1907
- };
1908
- const flushReasoningTagTextPartitionerAtEnd = () => {
1909
- for (const delta of reasoningTagTextPartitioner.flush()) appendPartitionedVisibleDelta(delta);
1910
- };
1911
- const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
1912
- const guardedStream = withFirstStreamEventTimeout(responseStream, {
1913
- provider: model.provider,
1914
- api: model.api,
1915
- model: model.id,
1916
- timeoutMs: options?.firstEventTimeoutMs ?? 0,
1917
- stage: "completions",
1918
- abort: options?.abortFirstEventStream,
1919
- onTimeout: options?.onFirstEventTimeout,
1920
- 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."
1921
- });
1922
- for await (const rawChunk of guardedStream) {
1923
- throwIfModelStreamAborted(options?.signal);
1924
- chunkPushedEvent = false;
1925
- if (!rawChunk || typeof rawChunk !== "object") {
1926
- await cooperativeScheduler.afterEvent();
1778
+ }
1779
+ return adjustedChars;
1780
+ }
1781
+ function estimateOpenAICompletionsContentChars(value) {
1782
+ if (typeof value === "string") return estimateStringChars(value);
1783
+ if (!Array.isArray(value)) return 0;
1784
+ let adjustedChars = 0;
1785
+ for (const block of value) {
1786
+ if (!block || typeof block !== "object") continue;
1787
+ const record = block;
1788
+ if (record.type === "image_url" || record.type === "input_image") {
1789
+ adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
1927
1790
  continue;
1928
1791
  }
1929
- notifyLlmRequestActivity(options?.signal);
1930
- const chunk = rawChunk;
1931
- output.responseId ||= chunk.id;
1932
- let hasReasoningUsageActivity = false;
1933
- if (chunk.usage) {
1934
- output.usage = parseOpenAICompletionsUsage(chunk.usage, model);
1935
- hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(chunk.usage);
1936
- }
1937
- const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
1938
- if (!choice) {
1939
- emitReasoningUsageActivity(hasReasoningUsageActivity);
1940
- await cooperativeScheduler.afterEvent();
1792
+ const text = record.text;
1793
+ if (typeof text === "string") {
1794
+ adjustedChars += estimateStringChars(text);
1941
1795
  continue;
1942
1796
  }
1943
- const choiceUsage = choice.usage;
1944
- if (!chunk.usage && choiceUsage) {
1945
- output.usage = parseOpenAICompletionsUsage(choiceUsage, model);
1946
- hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
1947
- }
1948
- if (choice.finish_reason) {
1949
- const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
1950
- output.stopReason = finishReasonResult.stopReason;
1951
- if (finishReasonResult.stopReason === "stop") sawStopFinishReason = true;
1952
- if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
1953
- }
1954
- const rawChoiceDelta = choice.delta ?? choice.message;
1955
- if (!rawChoiceDelta) {
1956
- emitReasoningUsageActivity(hasReasoningUsageActivity);
1957
- await cooperativeScheduler.afterEvent();
1958
- continue;
1797
+ try {
1798
+ adjustedChars += estimateStringChars(JSON.stringify(block));
1799
+ } catch {
1800
+ adjustedChars += 256;
1959
1801
  }
1960
- for (const normalizedDelta of normalizeToolCallDeltas(rawChoiceDelta, choice.finish_reason)) {
1961
- const choiceDelta = normalizedDelta.delta;
1962
- const reasoningDeltas = getCompletionsReasoningDeltas(choiceDelta, compat.visibleReasoningDetailTypes);
1963
- const hasMirroredReasoning = reasoningDeltas.some((delta) => delta.kind === "thinking");
1964
- if (hasMirroredReasoning) reasoningTagTextPartitioner.markStrict();
1965
- const contentDeltas = readOpenAICompletionsContentDeltas(choiceDelta.content, choiceDelta.refusal, reasoningDeltas.filter((reasoningDelta) => reasoningDelta.kind === "thinking").map((reasoningDelta) => reasoningDelta.text));
1966
- const appendReasoningDeltas = () => {
1967
- for (const reasoningDelta of reasoningDeltas) {
1968
- if (reasoningDelta.kind === "thinking" && !emitReasoning) continue;
1969
- if (currentBlock?.type === "toolCall") {
1970
- queuePostToolCallDelta({ ...reasoningDelta });
1971
- continue;
1972
- }
1973
- if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text);
1974
- else if (emitReasoning) appendThinkingDelta(reasoningDelta);
1975
- }
1802
+ }
1803
+ return adjustedChars;
1804
+ }
1805
+ function resolveOpenAICompletionsEffectiveContextTokens(model) {
1806
+ const contextTokens = model.contextTokens;
1807
+ if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
1808
+ return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
1809
+ }
1810
+ function isQwenOpenAICompletionsThinkingFormat(format) {
1811
+ return format === "qwen" || format === "qwen-chat-template";
1812
+ }
1813
+ function setQwenChatTemplateThinking(params, enabled) {
1814
+ const existing = params.chat_template_kwargs;
1815
+ params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
1816
+ ...existing,
1817
+ enable_thinking: enabled
1818
+ } : { enable_thinking: enabled };
1819
+ }
1820
+ function applyQwenOpenAICompletionsThinkingParams(params) {
1821
+ if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
1822
+ const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
1823
+ if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
1824
+ else params.payload.enable_thinking = enabled;
1825
+ return true;
1826
+ }
1827
+ function applyTogetherOpenAICompletionsThinkingParams(params) {
1828
+ if (!params.modelReasoning || params.compatThinkingFormat !== "together") return;
1829
+ params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
1830
+ }
1831
+ function convertTools(tools, compat, model) {
1832
+ const projection = projectOpenAITools(tools);
1833
+ const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
1834
+ transport: "stream",
1835
+ supportsStrictMode: compat?.supportsStrictMode
1836
+ }), {
1837
+ transport: "completions",
1838
+ model
1839
+ });
1840
+ return {
1841
+ projection,
1842
+ tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
1843
+ const functionTool = {
1844
+ name: tool.name,
1845
+ description: tool.description,
1846
+ parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
1976
1847
  };
1977
- if (hasMirroredReasoning) appendReasoningDeltas();
1978
- for (const contentDelta of contentDeltas) if (contentDelta.kind === "text") {
1979
- const routedDeltas = hasMirroredReasoning ? reasoningTagTextPartitioner.push(contentDelta.text) : reasoningTagTextPartitioner.pushVisible(contentDelta.text);
1980
- for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
1981
- } else {
1982
- if (reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
1983
- appendRoutedContentDelta(contentDelta);
1984
- }
1985
- if (!hasMirroredReasoning) appendReasoningDeltas();
1986
- const toolCallDeltas = normalizedDelta.toolCalls;
1987
- if (toolCallDeltas.length > 0) {
1988
- sawNativeToolCallDelta = true;
1989
- flushReasoningTagTextPartitionerAtEnd();
1990
- rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
1991
- for (const toolCall of toolCallDeltas) {
1992
- const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
1993
- let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
1994
- if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
1995
- if (!block) {
1996
- if (currentBlock?.type === "toolCall") {
1997
- currentBlock = null;
1998
- flushPendingPostToolCallDeltas();
1999
- }
2000
- const initialSig = extractGoogleThoughtSignature(toolCall);
2001
- block = {
2002
- type: "toolCall",
2003
- id: toolCall.id || "",
2004
- name: toolCall.function?.name || "",
2005
- arguments: {},
2006
- partialArgs: "",
2007
- ...initialSig ? { thoughtSignature: initialSig } : {}
2008
- };
2009
- output.content.push(block);
2010
- toolCallBlockIndices.set(block, output.content.length - 1);
2011
- pushStreamEvent({
2012
- type: "toolcall_start",
2013
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
2014
- partial: output
2015
- });
2016
- }
2017
- if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
2018
- if (toolCall.id) {
2019
- block.id = toolCall.id;
2020
- toolCallBlocksById.set(toolCall.id, block);
2021
- }
2022
- currentBlock = block;
2023
- if (toolCall.function?.name) block.name = toolCall.function.name;
2024
- const deltaSig = extractGoogleThoughtSignature(toolCall);
2025
- if (deltaSig) block.thoughtSignature = deltaSig;
2026
- if (toolCall.function?.arguments) {
2027
- const nextArgumentBytes = measureUtf8Bytes(toolCall.function.arguments);
2028
- const currentBlockArgBytes = toolCallBlockBytes.get(block) ?? 0;
2029
- if (currentBlockArgBytes + nextArgumentBytes > MAX_TOOL_CALL_ARGUMENT_BUFFER_BYTES) throw new Error("Exceeded tool-call argument buffer limit");
2030
- toolCallBlockBytes.set(block, currentBlockArgBytes + nextArgumentBytes);
2031
- block.partialArgs += toolCall.function.arguments;
2032
- block.arguments = parseStreamingJson(block.partialArgs);
2033
- pushStreamEvent({
2034
- type: "toolcall_delta",
2035
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
2036
- delta: toolCall.function.arguments,
2037
- partial: output
2038
- });
2039
- }
2040
- }
2041
- }
1848
+ if (strict !== void 0) functionTool.strict = strict;
1849
+ return {
1850
+ type: "function",
1851
+ function: functionTool
1852
+ };
1853
+ })
1854
+ };
1855
+ }
1856
+ function buildOpenAICompletionsParams(model, context, options) {
1857
+ const compat = getCompat(model);
1858
+ const compatDetection = detectOpenAICompletionsCompat(model);
1859
+ let messages = convertMessages(model, context.systemPrompt ? {
1860
+ ...context,
1861
+ systemPrompt: stripSystemPromptCacheBoundary(context.systemPrompt)
1862
+ } : context, compat);
1863
+ applyCompletionsReplay(messages, context, model, compat);
1864
+ if (compat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
1865
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention);
1866
+ const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
1867
+ const params = {
1868
+ model: model.id,
1869
+ messages: compat.requiresStringContent ? flattenCompletionMessagesToStringContent(messages) : messages,
1870
+ stream: true
1871
+ };
1872
+ if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
1873
+ if (compat.supportsStore) params.store = false;
1874
+ if (compat.supportsPromptCacheKey && promptCacheKey) {
1875
+ params.prompt_cache_key = promptCacheKey;
1876
+ if (cacheRetention === "long" && compat.supportsLongCacheRetention) params.prompt_cache_retention = "24h";
1877
+ }
1878
+ if (options?.temperature !== void 0) params.temperature = options.temperature;
1879
+ if (options?.topP !== void 0) params.top_p = options.topP;
1880
+ const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
1881
+ provider: model.provider,
1882
+ baseUrl: model.baseUrl,
1883
+ hasTools: () => Boolean(context.tools?.length)
1884
+ }) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
1885
+ if (responseFormat !== void 0) params.response_format = responseFormat;
1886
+ if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
1887
+ if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
1888
+ if (options?.seed !== void 0) params.seed = options.seed;
1889
+ if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
1890
+ if (supportsModelTools(model)) {
1891
+ if (context.tools) {
1892
+ const converted = convertTools(context.tools, compat, model);
1893
+ if (converted.tools.length > 0 || converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
1894
+ else if (hasToolCallHistory(context.messages)) params.tools = [];
1895
+ if (options?.toolChoice) {
1896
+ const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
1897
+ if (toolChoice !== void 0) params.tool_choice = toolChoice;
1898
+ } else if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
1899
+ } else if (hasToolCallHistory(context.messages)) params.tools = [];
1900
+ if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
1901
+ delete params.tools;
1902
+ delete params.tool_choice;
2042
1903
  }
2043
- flushPendingPostToolCallDeltas();
2044
- emitReasoningUsageActivity(hasReasoningUsageActivity);
2045
- await cooperativeScheduler.afterEvent();
2046
1904
  }
2047
- flushReasoningTagTextPartitionerAtEnd();
2048
- flushDeepSeekToolCallRecovererAtEnd();
2049
- flushDeepSeekTextFilterAtEnd();
2050
- currentBlock = null;
2051
- flushPendingPostToolCallDeltas();
2052
- finalizeOpenAICompletionsToolCalls(output, {
2053
- allowSilentToolCallPromotion: sawStopFinishReason || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false),
2054
- onConfirmedToolCall(block, contentIndex) {
2055
- pushStreamEvent({
2056
- type: "toolcall_end",
2057
- contentIndex,
2058
- toolCall: block,
2059
- partial: output
2060
- });
1905
+ {
1906
+ const maxTokenBudget = resolveOpenAICompletionsMaxTokens(model, options);
1907
+ const effectiveMaxTokens = maxTokenBudget.maxTokens;
1908
+ const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
1909
+ let clampedMaxTokens = effectiveMaxTokens;
1910
+ const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
1911
+ if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
1912
+ clampedMaxTokens = modelMaxTokens;
1913
+ emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
1914
+ }
1915
+ if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
1916
+ const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
1917
+ const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
1918
+ if (clampedMaxTokens > remainingBudget) {
1919
+ clampedMaxTokens = remainingBudget;
1920
+ emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
1921
+ }
2061
1922
  }
1923
+ if (clampedMaxTokens) if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
1924
+ else params.max_completion_tokens = clampedMaxTokens;
1925
+ }
1926
+ const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort$1(options);
1927
+ const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
1928
+ model,
1929
+ effort: completionsReasoningEffort,
1930
+ fallbackMap: compat.reasoningEffortMap
1931
+ }) : void 0;
1932
+ const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
1933
+ const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
1934
+ const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
1935
+ compatThinkingFormat: compat.thinkingFormat,
1936
+ modelReasoning: model.reasoning,
1937
+ payload: params,
1938
+ requestedEffort: completionsReasoningEffort
2062
1939
  });
2063
- if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
2064
- if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
2065
- }
2066
- function shouldFilterDeepSeekDsmlText(compat) {
2067
- return compat.thinkingFormat === "deepseek";
1940
+ applyTogetherOpenAICompletionsThinkingParams({
1941
+ compatThinkingFormat: compat.thinkingFormat,
1942
+ modelReasoning: model.reasoning,
1943
+ payload: params,
1944
+ requestedEffort: completionsReasoningEffort
1945
+ });
1946
+ if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
1947
+ else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
1948
+ else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
1949
+ return params;
2068
1950
  }
1951
+ //#endregion
1952
+ //#region packages/ai/src/transports/openai-completions-dsml.ts
2069
1953
  const DEEPSEEK_DSML_BARS = ["|", "|"];
2070
1954
  const DEEPSEEK_DSML_TOOL_KINDS = [
2071
1955
  "tool_calls",
@@ -2080,7 +1964,7 @@ const DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPE
2080
1964
  const DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length), ...DEEPSEEK_DSML_TOOL_CLOSE_TOKENS.map((token) => token.length), ...DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES.map((token) => token.length), ...DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS.map((token) => token.length));
2081
1965
  const MAX_DSML_RECOVERY_BUFFER_BYTES = 256e3;
2082
1966
  const DEEPSEEK_DSML_SCAN_BATCH_CHARS = 64 * 1024;
2083
- function createDeepSeekDsmlToolCallRecoverer() {
1967
+ function createDsmlRecoverer() {
2084
1968
  let buffer = "";
2085
1969
  let bufferBytes = 0;
2086
1970
  let bufferEndsWithHighSurrogate = false;
@@ -2180,7 +2064,7 @@ function createDeepSeekDsmlToolCallRecoverer() {
2180
2064
  };
2181
2065
  return {
2182
2066
  push(chunk) {
2183
- const append = utf8ByteLengthForAppend(bufferEndsWithHighSurrogate, chunk);
2067
+ const append = measureUtf8AppendBytes(bufferEndsWithHighSurrogate, chunk);
2184
2068
  bufferBytes += append.bytes;
2185
2069
  bufferEndsWithHighSurrogate = append.endsWithHighSurrogate;
2186
2070
  buffer += chunk;
@@ -2336,20 +2220,6 @@ function scanDeepSeekDsmlToolBlock(text, closeToken, contentStartIndex, state) {
2336
2220
  }
2337
2221
  return { kind: "incomplete" };
2338
2222
  }
2339
- function utf8ByteLengthForAppend(bufferEndsWithHighSurrogate, chunk) {
2340
- let bytes = Buffer.byteLength(chunk, "utf8");
2341
- if (!chunk) return {
2342
- bytes,
2343
- endsWithHighSurrogate: bufferEndsWithHighSurrogate
2344
- };
2345
- const nextCodeUnit = chunk.charCodeAt(0);
2346
- if (bufferEndsWithHighSurrogate && nextCodeUnit >= 56320 && nextCodeUnit <= 57343) bytes -= 2;
2347
- const finalCodeUnit = chunk.charCodeAt(chunk.length - 1);
2348
- return {
2349
- bytes,
2350
- endsWithHighSurrogate: finalCodeUnit >= 55296 && finalCodeUnit <= 56319
2351
- };
2352
- }
2353
2223
  function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
2354
2224
  const maxLength = Math.min(text.length, DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN - 1);
2355
2225
  for (let length = maxLength; length > 0; length -= 1) {
@@ -2358,664 +2228,784 @@ function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
2358
2228
  }
2359
2229
  return 0;
2360
2230
  }
2361
- function getCompletionsReasoningDeltas(delta, visibleReasoningDetailTypes) {
2362
- const output = [];
2363
- const pushDelta = (next) => {
2364
- const previous = output[output.length - 1];
2231
+ //#endregion
2232
+ //#region packages/ai/src/transports/openai-completions-stream.ts
2233
+ function extractToolCallThoughtSignature(toolCall) {
2234
+ const tc = toolCall;
2235
+ if (!tc) return;
2236
+ const fromExtra = (tc.extra_content?.google)?.thought_signature;
2237
+ if (typeof fromExtra === "string" && fromExtra.length > 0) return fromExtra;
2238
+ const fromFunction = tc.function?.thought_signature;
2239
+ if (typeof fromFunction === "string" && fromFunction.length > 0) return fromFunction;
2240
+ const fromToolCall = tc.thought_signature;
2241
+ return typeof fromToolCall === "string" && fromToolCall.length > 0 ? fromToolCall : void 0;
2242
+ }
2243
+ async function processCompletionsStream(responseStream, output, model, stream, options) {
2244
+ const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
2245
+ const emitReasoning = options?.emitReasoning ?? true;
2246
+ const compat = getCompat(model);
2247
+ const shouldFilterDeepSeekDsmlText = compat.thinkingFormat === "deepseek";
2248
+ const deepSeekTextFilter = shouldFilterDeepSeekDsmlText ? createDeepSeekTextFilter() : null;
2249
+ const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText ? createDsmlRecoverer() : null;
2250
+ const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
2251
+ let currentBlock = null;
2252
+ let pendingPostToolCallDeltas = [];
2253
+ let pendingPostToolCallBytes = 0;
2254
+ let isFlushingPendingPostToolCallDeltas = false;
2255
+ const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
2256
+ const toolCallBlocksById = /* @__PURE__ */ new Map();
2257
+ const provisionalCommentaryTags = /* @__PURE__ */ new Map();
2258
+ const toolCallBlockIndices = /* @__PURE__ */ new WeakMap();
2259
+ const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
2260
+ let sawStopFinishReason = false;
2261
+ let sawNativeToolCallDelta = false;
2262
+ const blockIndex = () => output.content.length - 1;
2263
+ const measureUtf8Bytes = (text) => Buffer.byteLength(text, "utf8");
2264
+ let chunkPushedEvent = false;
2265
+ const pushStreamEvent = (event) => {
2266
+ chunkPushedEvent = true;
2267
+ stream.push(event);
2268
+ };
2269
+ const queuePostToolCallDelta = (next) => {
2270
+ const nextBytes = measureUtf8Bytes(next.text);
2271
+ if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
2272
+ pendingPostToolCallBytes += nextBytes;
2273
+ const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
2365
2274
  if (!previous || previous.kind !== next.kind) {
2366
- output.push(next);
2275
+ pendingPostToolCallDeltas.push(next);
2367
2276
  return;
2368
2277
  }
2369
2278
  if (next.kind === "thinking" && previous.kind === "thinking") {
2370
2279
  if (previous.signature !== next.signature) {
2371
- output.push(next);
2280
+ pendingPostToolCallDeltas.push(next);
2372
2281
  return;
2373
2282
  }
2374
- previous.text += next.text;
2375
- return;
2283
+ previous.text += next.text;
2284
+ return;
2285
+ }
2286
+ previous.text += next.text;
2287
+ };
2288
+ const appendThinkingDeltaInternal = (reasoningDelta) => {
2289
+ if (!currentBlock || currentBlock.type !== "thinking") {
2290
+ currentBlock = {
2291
+ type: "thinking",
2292
+ thinking: "",
2293
+ ...reasoningDelta.signature ? { thinkingSignature: reasoningDelta.signature } : {}
2294
+ };
2295
+ output.content.push(currentBlock);
2296
+ pushStreamEvent({
2297
+ type: "thinking_start",
2298
+ contentIndex: blockIndex(),
2299
+ partial: output
2300
+ });
2301
+ }
2302
+ currentBlock.thinking += reasoningDelta.text;
2303
+ pushStreamEvent({
2304
+ type: "thinking_delta",
2305
+ contentIndex: blockIndex(),
2306
+ delta: reasoningDelta.text,
2307
+ partial: output
2308
+ });
2309
+ };
2310
+ const appendTextDeltaInternal = (text) => {
2311
+ if (!currentBlock || currentBlock.type !== "text") {
2312
+ currentBlock = {
2313
+ type: "text",
2314
+ text: ""
2315
+ };
2316
+ output.content.push(currentBlock);
2317
+ pushStreamEvent({
2318
+ type: "text_start",
2319
+ contentIndex: blockIndex(),
2320
+ partial: output
2321
+ });
2322
+ }
2323
+ currentBlock.text += text;
2324
+ pushStreamEvent({
2325
+ type: "text_delta",
2326
+ contentIndex: blockIndex(),
2327
+ delta: text
2328
+ });
2329
+ };
2330
+ const flushPendingPostToolCallDeltas = () => {
2331
+ if (isFlushingPendingPostToolCallDeltas || currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
2332
+ isFlushingPendingPostToolCallDeltas = true;
2333
+ const bufferedDeltas = pendingPostToolCallDeltas;
2334
+ pendingPostToolCallDeltas = [];
2335
+ pendingPostToolCallBytes = 0;
2336
+ for (const delta of bufferedDeltas) if (delta.kind === "text") appendTextDeltaInternal(delta.text);
2337
+ else if (emitReasoning) appendThinkingDeltaInternal(delta);
2338
+ isFlushingPendingPostToolCallDeltas = false;
2339
+ };
2340
+ const appendThinkingDelta = (reasoningDelta) => {
2341
+ flushPendingPostToolCallDeltas();
2342
+ appendThinkingDeltaInternal(reasoningDelta);
2343
+ };
2344
+ const appendTextDelta = (text) => {
2345
+ flushPendingPostToolCallDeltas();
2346
+ appendTextDeltaInternal(text);
2347
+ };
2348
+ const appendVisibleTextDelta = (text) => {
2349
+ if (!text) return;
2350
+ if (currentBlock?.type === "toolCall") queuePostToolCallDelta({
2351
+ kind: "text",
2352
+ text
2353
+ });
2354
+ else appendTextDelta(text);
2355
+ };
2356
+ const appendRecoveredToolCall = (toolCall) => {
2357
+ if (currentBlock?.type === "toolCall") {
2358
+ currentBlock = null;
2359
+ flushPendingPostToolCallDeltas();
2360
+ }
2361
+ rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
2362
+ const block = {
2363
+ type: "toolCall",
2364
+ id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
2365
+ name: toolCall.name,
2366
+ arguments: toolCall.arguments,
2367
+ partialArgs: toolCall.partialArgs
2368
+ };
2369
+ currentBlock = block;
2370
+ output.content.push(block);
2371
+ toolCallBlockIndices.set(block, output.content.length - 1);
2372
+ pushStreamEvent({
2373
+ type: "toolcall_start",
2374
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2375
+ partial: output
2376
+ });
2377
+ pushStreamEvent({
2378
+ type: "toolcall_delta",
2379
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2380
+ delta: toolCall.partialArgs,
2381
+ partial: output
2382
+ });
2383
+ };
2384
+ const appendFilteredVisibleTextDelta = (text) => {
2385
+ const recoveredParts = deepSeekToolCallRecoverer?.push(text) ?? [{
2386
+ kind: "text",
2387
+ text
2388
+ }];
2389
+ for (const recoveredPart of recoveredParts) {
2390
+ if (recoveredPart.kind === "toolCall") {
2391
+ appendRecoveredToolCall(recoveredPart);
2392
+ continue;
2393
+ }
2394
+ const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
2395
+ for (const part of parts) appendVisibleTextDelta(part);
2376
2396
  }
2377
- previous.text += next.text;
2378
2397
  };
2379
- const reasoningDetails = delta.reasoning_details;
2380
- let usedReasoningThinkingDetails = false;
2381
- if (Array.isArray(reasoningDetails)) {
2382
- const visibleTypes = new Set(visibleReasoningDetailTypes);
2383
- for (const item of reasoningDetails) {
2384
- const detail = item;
2385
- if (typeof detail.text !== "string" || !detail.text) continue;
2386
- if (detail.type === "reasoning.text") {
2387
- usedReasoningThinkingDetails = true;
2388
- pushDelta({
2389
- kind: "thinking",
2390
- signature: "reasoning_details",
2391
- text: detail.text
2392
- });
2398
+ const flushDeepSeekToolCallRecovererAtEnd = () => {
2399
+ const recoveredParts = deepSeekToolCallRecoverer?.flush();
2400
+ if (!recoveredParts) return;
2401
+ for (const recoveredPart of recoveredParts) {
2402
+ if (recoveredPart.kind === "toolCall") {
2403
+ appendRecoveredToolCall(recoveredPart);
2393
2404
  continue;
2394
2405
  }
2395
- if (typeof detail.type === "string" && visibleTypes.has(detail.type)) pushDelta({
2396
- kind: "text",
2397
- text: detail.text
2398
- });
2406
+ const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
2407
+ for (const part of parts) appendVisibleTextDelta(part);
2399
2408
  }
2400
- }
2401
- if (!usedReasoningThinkingDetails) for (const field of [
2402
- "reasoning_content",
2403
- "reasoning",
2404
- "reasoning_text"
2405
- ]) {
2406
- const value = delta[field];
2407
- if (typeof value === "string" && value.length > 0) {
2408
- pushDelta({
2409
- kind: "thinking",
2410
- signature: field,
2411
- text: value
2412
- });
2413
- break;
2409
+ };
2410
+ const flushDeepSeekTextFilterAtEnd = () => {
2411
+ const parts = deepSeekTextFilter?.flush();
2412
+ if (!parts) return;
2413
+ for (const part of parts) appendVisibleTextDelta(part);
2414
+ };
2415
+ const appendRoutedContentDelta = (delta) => {
2416
+ if (delta.kind === "text") {
2417
+ appendFilteredVisibleTextDelta(delta.text);
2418
+ return;
2414
2419
  }
2415
- }
2416
- return output;
2417
- }
2418
- function resolveOpenAICompletionsReasoningEffort(options) {
2419
- return options?.reasoningEffort ?? options?.reasoning ?? "high";
2420
- }
2421
- function shouldEmitOpenAICompletionsReasoning(model, options) {
2422
- if (!model.reasoning) return false;
2423
- const effort = resolveOpenAICompletionsReasoningEffort(options);
2424
- if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
2425
- return true;
2426
- }
2427
- function shouldEmitOpenAICompletionsReasoningForModel(model, options) {
2428
- return shouldEmitOpenAICompletionsReasoning(model, options);
2429
- }
2430
- function resolveOpenAICompletionsMaxTokens(model, options) {
2431
- if (options?.maxTokens) return {
2432
- maxTokens: options.maxTokens,
2433
- clampToModelMaxTokens: true
2420
+ if (!emitReasoning) return;
2421
+ if (currentBlock?.type === "toolCall") queuePostToolCallDelta(delta);
2422
+ else appendThinkingDelta(delta);
2434
2423
  };
2435
- const paramsMaxTokens = resolveMaxTokensParam(model.params);
2436
- if (paramsMaxTokens) return {
2437
- maxTokens: paramsMaxTokens,
2438
- clampToModelMaxTokens: false
2424
+ const appendPartitionedVisibleDelta = (delta) => {
2425
+ if (delta.kind === "text") appendFilteredVisibleTextDelta(delta.text);
2439
2426
  };
2440
- return {
2441
- maxTokens: model.maxTokens,
2442
- clampToModelMaxTokens: false
2427
+ const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
2428
+ if (!hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
2429
+ const latestBlock = output.content[output.content.length - 1];
2430
+ if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
2431
+ if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
2432
+ appendThinkingDelta({ text: "" });
2443
2433
  };
2444
- }
2445
- function resolveOpenAICompletionsModelMaxTokens(model) {
2446
- return typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
2447
- }
2448
- const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
2449
- const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
2450
- function estimateOpenAICompletionsInputTokens(payload) {
2451
- let adjustedChars = 0;
2452
- adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
2453
- if (Array.isArray(payload.tools) && payload.tools.length > 0) try {
2454
- adjustedChars += estimateStringChars(JSON.stringify(payload.tools));
2455
- } catch {
2456
- adjustedChars += 1024;
2457
- }
2458
- if (payload.response_format !== void 0) try {
2459
- adjustedChars += estimateStringChars(JSON.stringify(payload.response_format));
2460
- } catch {
2461
- adjustedChars += 256;
2462
- }
2463
- return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
2464
- }
2465
- function estimateOpenAICompletionsMessagesChars(messages) {
2466
- if (!Array.isArray(messages)) return 0;
2467
- let adjustedChars = 0;
2468
- for (const message of messages) {
2469
- if (!message || typeof message !== "object") continue;
2470
- const record = message;
2471
- adjustedChars += estimateOpenAICompletionsContentChars(record.content);
2472
- for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
2473
- if (record.tool_calls !== void 0) try {
2474
- adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
2475
- } catch {
2476
- adjustedChars += 256;
2434
+ const flushReasoningTagTextPartitionerAtEnd = () => {
2435
+ for (const delta of reasoningTagTextPartitioner.flush()) appendPartitionedVisibleDelta(delta);
2436
+ };
2437
+ const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
2438
+ const guardedStream = withFirstStreamEventTimeout(responseStream, {
2439
+ provider: model.provider,
2440
+ api: model.api,
2441
+ model: model.id,
2442
+ timeoutMs: options?.firstEventTimeoutMs ?? 0,
2443
+ stage: "completions",
2444
+ abort: options?.abortFirstEventStream,
2445
+ onTimeout: options?.onFirstEventTimeout,
2446
+ 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."
2447
+ });
2448
+ for await (const rawChunk of guardedStream) {
2449
+ throwIfModelStreamAborted(options?.signal);
2450
+ chunkPushedEvent = false;
2451
+ if (!rawChunk || typeof rawChunk !== "object") {
2452
+ await cooperativeScheduler.afterEvent();
2453
+ continue;
2477
2454
  }
2478
- }
2479
- return adjustedChars;
2480
- }
2481
- function estimateOpenAICompletionsContentChars(value) {
2482
- if (typeof value === "string") return estimateStringChars(value);
2483
- if (!Array.isArray(value)) return 0;
2484
- let adjustedChars = 0;
2485
- for (const block of value) {
2486
- if (!block || typeof block !== "object") continue;
2487
- const record = block;
2488
- if (record.type === "image_url" || record.type === "input_image") {
2489
- adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
2455
+ notifyLlmRequestActivity(options?.signal);
2456
+ const chunk = rawChunk;
2457
+ output.responseId ||= chunk.id;
2458
+ let hasReasoningUsageActivity = false;
2459
+ if (chunk.usage) {
2460
+ output.usage = parseOpenAICompletionsUsage(chunk.usage, model);
2461
+ hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(chunk.usage);
2462
+ }
2463
+ const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
2464
+ if (!choice) {
2465
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
2466
+ await cooperativeScheduler.afterEvent();
2490
2467
  continue;
2491
2468
  }
2492
- const text = record.text;
2493
- if (typeof text === "string") {
2494
- adjustedChars += estimateStringChars(text);
2469
+ const choiceUsage = choice.usage;
2470
+ if (!chunk.usage && choiceUsage) {
2471
+ output.usage = parseOpenAICompletionsUsage(choiceUsage, model);
2472
+ hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
2473
+ }
2474
+ if (choice.finish_reason) {
2475
+ const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
2476
+ output.stopReason = finishReasonResult.stopReason;
2477
+ if (finishReasonResult.stopReason === "stop") sawStopFinishReason = true;
2478
+ if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
2479
+ }
2480
+ const rawChoiceDelta = choice.delta ?? choice.message;
2481
+ if (!rawChoiceDelta) {
2482
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
2483
+ await cooperativeScheduler.afterEvent();
2495
2484
  continue;
2496
2485
  }
2497
- try {
2498
- adjustedChars += estimateStringChars(JSON.stringify(block));
2499
- } catch {
2500
- adjustedChars += 256;
2486
+ for (const normalizedDelta of normalizeToolCallDeltas(rawChoiceDelta, choice.finish_reason)) {
2487
+ const choiceDelta = normalizedDelta.delta;
2488
+ const reasoningDeltas = getCompletionsReasoningDeltas(choiceDelta, compat.visibleReasoningDetailTypes);
2489
+ const hasMirroredReasoning = reasoningDeltas.some((delta) => delta.kind === "thinking");
2490
+ if (hasMirroredReasoning) reasoningTagTextPartitioner.markStrict();
2491
+ const contentDeltas = readOpenAICompletionsContentDeltas(choiceDelta.content, choiceDelta.refusal, reasoningDeltas.filter((reasoningDelta) => reasoningDelta.kind === "thinking").map((reasoningDelta) => reasoningDelta.text));
2492
+ const appendReasoningDeltas = () => {
2493
+ for (const reasoningDelta of reasoningDeltas) {
2494
+ if (reasoningDelta.kind === "thinking" && !emitReasoning) continue;
2495
+ if (currentBlock?.type === "toolCall") {
2496
+ queuePostToolCallDelta({ ...reasoningDelta });
2497
+ continue;
2498
+ }
2499
+ if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text);
2500
+ else if (emitReasoning) appendThinkingDelta(reasoningDelta);
2501
+ }
2502
+ };
2503
+ if (hasMirroredReasoning) appendReasoningDeltas();
2504
+ for (const contentDelta of contentDeltas) if (contentDelta.kind === "text") {
2505
+ const routedDeltas = hasMirroredReasoning ? reasoningTagTextPartitioner.push(contentDelta.text) : reasoningTagTextPartitioner.pushVisible(contentDelta.text);
2506
+ for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
2507
+ } else {
2508
+ if (reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
2509
+ appendRoutedContentDelta(contentDelta);
2510
+ }
2511
+ if (!hasMirroredReasoning) appendReasoningDeltas();
2512
+ const toolCallDeltas = normalizedDelta.toolCalls;
2513
+ if (toolCallDeltas.length > 0) {
2514
+ sawNativeToolCallDelta = true;
2515
+ flushReasoningTagTextPartitionerAtEnd();
2516
+ rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
2517
+ for (const toolCall of toolCallDeltas) {
2518
+ const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
2519
+ let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
2520
+ if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
2521
+ if (!block) {
2522
+ if (currentBlock?.type === "toolCall") {
2523
+ currentBlock = null;
2524
+ flushPendingPostToolCallDeltas();
2525
+ }
2526
+ const initialSig = extractToolCallThoughtSignature(toolCall);
2527
+ block = {
2528
+ type: "toolCall",
2529
+ id: toolCall.id || "",
2530
+ name: toolCall.function?.name || "",
2531
+ arguments: {},
2532
+ partialArgs: "",
2533
+ ...initialSig ? { thoughtSignature: initialSig } : {}
2534
+ };
2535
+ output.content.push(block);
2536
+ toolCallBlockIndices.set(block, output.content.length - 1);
2537
+ pushStreamEvent({
2538
+ type: "toolcall_start",
2539
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2540
+ partial: output
2541
+ });
2542
+ }
2543
+ if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
2544
+ if (toolCall.id) {
2545
+ block.id = toolCall.id;
2546
+ toolCallBlocksById.set(toolCall.id, block);
2547
+ }
2548
+ currentBlock = block;
2549
+ if (toolCall.function?.name) block.name = toolCall.function.name;
2550
+ const deltaSig = extractToolCallThoughtSignature(toolCall);
2551
+ if (deltaSig) block.thoughtSignature = deltaSig;
2552
+ if (toolCall.function?.arguments) {
2553
+ block.partialArgs += toolCall.function.arguments;
2554
+ block.arguments = parseStreamingJson(block.partialArgs);
2555
+ pushStreamEvent({
2556
+ type: "toolcall_delta",
2557
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2558
+ delta: toolCall.function.arguments,
2559
+ partial: output
2560
+ });
2561
+ }
2562
+ }
2563
+ }
2501
2564
  }
2565
+ flushPendingPostToolCallDeltas();
2566
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
2567
+ await cooperativeScheduler.afterEvent();
2502
2568
  }
2503
- return adjustedChars;
2504
- }
2505
- function resolveOpenAICompletionsEffectiveContextTokens(model) {
2506
- const contextTokens = model.contextTokens;
2507
- if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
2508
- return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
2509
- }
2510
- function isQwenOpenAICompletionsThinkingFormat(format) {
2511
- return format === "qwen" || format === "qwen-chat-template";
2512
- }
2513
- function setQwenChatTemplateThinking(params, enabled) {
2514
- const existing = params.chat_template_kwargs;
2515
- params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
2516
- ...existing,
2517
- enable_thinking: enabled
2518
- } : { enable_thinking: enabled };
2519
- }
2520
- function applyQwenOpenAICompletionsThinkingParams(params) {
2521
- if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
2522
- const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
2523
- if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
2524
- else params.payload.enable_thinking = enabled;
2525
- return true;
2526
- }
2527
- function applyTogetherOpenAICompletionsThinkingParams(params) {
2528
- if (!params.modelReasoning || params.compatThinkingFormat !== "together") return false;
2529
- params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
2530
- return true;
2531
- }
2532
- function convertTools(tools, compat, model) {
2533
- const projection = projectOpenAITools(tools);
2534
- const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
2535
- transport: "stream",
2536
- supportsStrictMode: compat?.supportsStrictMode
2537
- }), {
2538
- transport: "completions",
2539
- model
2569
+ flushReasoningTagTextPartitionerAtEnd();
2570
+ flushDeepSeekToolCallRecovererAtEnd();
2571
+ flushDeepSeekTextFilterAtEnd();
2572
+ currentBlock = null;
2573
+ flushPendingPostToolCallDeltas();
2574
+ finalizeOpenAICompletionsToolCalls(output, {
2575
+ allowSilentToolCallPromotion: sawStopFinishReason || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false),
2576
+ onConfirmedToolCall(block, contentIndex) {
2577
+ pushStreamEvent({
2578
+ type: "toolcall_end",
2579
+ contentIndex,
2580
+ toolCall: block,
2581
+ partial: output
2582
+ });
2583
+ }
2540
2584
  });
2541
- return {
2542
- projection,
2543
- tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
2544
- const functionTool = {
2545
- name: tool.name,
2546
- description: tool.description,
2547
- parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
2548
- };
2549
- if (strict !== void 0) functionTool.strict = strict;
2550
- return {
2551
- type: "function",
2552
- function: functionTool
2553
- };
2554
- })
2555
- };
2556
- }
2557
- function extractGoogleThoughtSignature(toolCall) {
2558
- const tc = toolCall;
2559
- if (!tc) return;
2560
- const fromExtra = (tc.extra_content?.google)?.thought_signature;
2561
- if (typeof fromExtra === "string" && fromExtra.length > 0) return fromExtra;
2562
- const fromFunction = tc.function?.thought_signature;
2563
- return typeof fromFunction === "string" && fromFunction.length > 0 ? fromFunction : void 0;
2564
- }
2565
- function isGoogleOpenAICompatModel(model) {
2566
- const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
2567
- return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
2568
- }
2569
- function requiresGoogleCompatToolCallThoughtSignature(model) {
2570
- return isGoogleGemini3ProModel(model.id) || isGoogleGemini3FlashModel(model.id);
2571
- }
2572
- const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
2573
- const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
2574
- function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
2575
- return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
2585
+ if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
2586
+ if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
2576
2587
  }
2577
- function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
2578
- if (!isGoogleOpenAICompatModel(model)) return;
2579
- const sigById = /* @__PURE__ */ new Map();
2580
- const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
2581
- for (const msg of context.messages ?? []) {
2582
- if (msg.role !== "assistant") continue;
2583
- const source = msg;
2584
- if (!Array.isArray(source.content)) continue;
2585
- for (const block of source.content) {
2586
- if (block.type !== "toolCall") continue;
2587
- const id = block.id;
2588
- const sig = block.thoughtSignature;
2589
- if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
2590
- const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
2591
- if (!isSameRoute && !fallbackSig) continue;
2592
- sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
2588
+ function getCompletionsReasoningDeltas(delta, visibleReasoningDetailTypes) {
2589
+ const output = [];
2590
+ const pushDelta = (next) => {
2591
+ const previous = output[output.length - 1];
2592
+ if (!previous || previous.kind !== next.kind) {
2593
+ output.push(next);
2594
+ return;
2595
+ }
2596
+ if (next.kind === "thinking" && previous.kind === "thinking") {
2597
+ if (previous.signature !== next.signature) {
2598
+ output.push(next);
2599
+ return;
2593
2600
  }
2601
+ previous.text += next.text;
2602
+ return;
2594
2603
  }
2595
- }
2596
- if (sigById.size === 0 && !fallbackSig) return;
2597
- for (const message of outgoingMessages) {
2598
- const toolCalls = message.tool_calls;
2599
- if (!Array.isArray(toolCalls)) continue;
2600
- for (const toolCall of toolCalls) {
2601
- const id = toolCall.id;
2602
- if (typeof id !== "string") continue;
2603
- let sig = sigById.get(id) ?? fallbackSig;
2604
- if (typeof sig === "string" && sig.length > 0) {
2605
- if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
2604
+ previous.text += next.text;
2605
+ };
2606
+ const reasoningDetails = delta.reasoning_details;
2607
+ let usedReasoningThinkingDetails = false;
2608
+ if (Array.isArray(reasoningDetails)) {
2609
+ const visibleTypes = new Set(visibleReasoningDetailTypes);
2610
+ for (const item of reasoningDetails) {
2611
+ const detail = item;
2612
+ if (typeof detail.text !== "string" || !detail.text) continue;
2613
+ if (detail.type === "reasoning.text") {
2614
+ usedReasoningThinkingDetails = true;
2615
+ pushDelta({
2616
+ kind: "thinking",
2617
+ signature: "reasoning_details",
2618
+ text: detail.text
2619
+ });
2620
+ continue;
2606
2621
  }
2607
- if (typeof sig !== "string" || sig.length === 0) continue;
2608
- const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
2609
- toolCall.extra_content = extra;
2610
- const google = extra.google && typeof extra.google === "object" ? extra.google : {};
2611
- extra.google = google;
2612
- google.thought_signature = sig;
2622
+ if (typeof detail.type === "string" && visibleTypes.has(detail.type)) pushDelta({
2623
+ kind: "text",
2624
+ text: detail.text
2625
+ });
2613
2626
  }
2614
2627
  }
2615
- }
2616
- const COMPLETIONS_REASONING_REPLAY_FIELDS = [
2617
- "reasoning_details",
2618
- "reasoning_content",
2619
- "reasoning",
2620
- "reasoning_text"
2621
- ];
2622
- function stripCompletionsReasoningReplayFields(record) {
2623
- for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
2624
- }
2625
- function sanitizeOpenRouterReasoningReplayFields(record) {
2626
- const reasoningDetails = record.reasoning_details;
2627
- if (typeof reasoningDetails === "string") {
2628
- if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
2629
- delete record.reasoning_details;
2630
- } else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
2631
- if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
2632
- if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
2633
- const reasoningText = record.reasoning_text;
2634
- if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
2635
- if ("reasoning_text" in record) delete record.reasoning_text;
2636
- }
2637
- function sanitizeReasoningContentReplayFields(record) {
2638
- if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
2639
- delete record.reasoning_details;
2640
- delete record.reasoning;
2641
- delete record.reasoning_text;
2642
- }
2643
- const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
2644
- "deepseek-v4-flash",
2645
- "deepseek-v4-pro",
2646
- "kimi-for-coding",
2647
- "kimi-k2.5",
2648
- "kimi-k2.6",
2649
- "kimi-k2.7-code",
2650
- "kimi-k2.7-code-highspeed",
2651
- "kimi-k3",
2652
- "kimi-k2-thinking",
2653
- "kimi-k2-thinking-turbo",
2654
- "mimo-v2-pro",
2655
- "mimo-v2-omni",
2656
- "mimo-v2.5",
2657
- "mimo-v2.5-pro",
2658
- "mimo-v2.6-pro"
2659
- ]);
2660
- const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
2661
- "-free",
2662
- "-paid",
2663
- "-trial"
2664
- ];
2665
- function stripReasoningContentReplayTierSuffix(modelId) {
2666
- for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
2667
- return modelId;
2668
- }
2669
- function getReasoningContentReplayModelIdCandidates(modelId) {
2670
- if (typeof modelId !== "string") return [];
2671
- const normalized = modelId.trim().toLowerCase();
2672
- if (!normalized) return [];
2673
- const parts = normalized.split("/").filter(Boolean);
2674
- const finalPart = parts[parts.length - 1] ?? normalized;
2675
- const candidates = [finalPart];
2676
- const colonParts = finalPart.split(":").filter(Boolean);
2677
- if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
2678
- const baseCount = candidates.length;
2679
- for (let index = 0; index < baseCount; index += 1) {
2680
- const candidate = candidates[index];
2681
- if (typeof candidate !== "string") continue;
2682
- const stripped = stripReasoningContentReplayTierSuffix(candidate);
2683
- if (stripped !== candidate) candidates.push(stripped);
2628
+ if (!usedReasoningThinkingDetails) for (const field of [
2629
+ "reasoning_content",
2630
+ "reasoning",
2631
+ "reasoning_text"
2632
+ ]) {
2633
+ const value = delta[field];
2634
+ if (typeof value === "string" && value.length > 0) {
2635
+ pushDelta({
2636
+ kind: "thinking",
2637
+ signature: field,
2638
+ text: value
2639
+ });
2640
+ break;
2641
+ }
2684
2642
  }
2685
- return uniqueStrings(candidates.filter(Boolean));
2686
- }
2687
- function shouldPreserveReasoningContentReplay(model, compat) {
2688
- if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
2689
- return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
2643
+ return output;
2690
2644
  }
2691
- function shouldPreserveOpenRouterReasoningReplay(model) {
2692
- if (model.provider !== "openrouter") return true;
2693
- const normalizedModelId = model.id.trim().toLowerCase();
2694
- return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
2645
+ function resolveOpenAICompletionsReasoningEffort(options) {
2646
+ return options?.reasoningEffort ?? options?.reasoning ?? "high";
2695
2647
  }
2696
- function shouldTrustReasoningContentReplayMetadata(model) {
2648
+ function shouldEmitOpenAICompletionsReasoning(model, options) {
2697
2649
  if (!model.reasoning) return false;
2698
- if (model.provider.trim().toLowerCase() === "openai") return false;
2699
- return shouldPreserveOpenRouterReasoningReplay(model);
2650
+ const effort = resolveOpenAICompletionsReasoningEffort(options);
2651
+ if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
2652
+ return true;
2700
2653
  }
2701
- function sanitizeCompletionsReasoningReplayFields(messages, options) {
2702
- if (!Array.isArray(messages)) return;
2703
- for (const msg of messages) {
2704
- if (!msg || typeof msg !== "object") continue;
2705
- const record = msg;
2706
- if (record.role !== "assistant") continue;
2707
- if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
2708
- else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
2709
- else stripCompletionsReasoningReplayFields(record);
2710
- }
2654
+ function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
2655
+ const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
2656
+ return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
2711
2657
  }
2712
- function buildOpenAICompletionsParams(model, context, options) {
2713
- const compat = getCompat(model);
2714
- const compatDetection = detectOpenAICompletionsCompat(model);
2715
- let messages = convertMessages(model, context.systemPrompt ? {
2716
- ...context,
2717
- systemPrompt: stripSystemPromptCacheBoundary(context.systemPrompt)
2718
- } : context, compat);
2719
- injectToolCallThoughtSignatures(messages, context, model);
2720
- sanitizeCompletionsReasoningReplayFields(messages, {
2721
- preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
2722
- preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
2723
- });
2724
- if (compat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
2725
- const cacheRetention = resolveCacheRetention(options?.cacheRetention);
2726
- const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
2727
- const params = {
2728
- model: model.id,
2729
- messages: compat.requiresStringContent ? flattenCompletionMessagesToStringContent(messages) : messages,
2730
- stream: true
2658
+ //#endregion
2659
+ //#region packages/ai/src/transports/openai-completions-transport.ts
2660
+ function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
2661
+ const messages = params.messages;
2662
+ if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
2663
+ 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.`);
2664
+ }
2665
+ const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
2666
+ const SSE_DONE_MAX_LINE_CHARS = 1024;
2667
+ function createSseDoneDetector() {
2668
+ const decoder = new TextDecoder();
2669
+ let line = "";
2670
+ let lineOverflowed = false;
2671
+ let sawDone = false;
2672
+ const finishLine = () => {
2673
+ if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
2674
+ line = "";
2675
+ lineOverflowed = false;
2731
2676
  };
2732
- if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
2733
- if (compat.supportsStore) params.store = false;
2734
- if (compat.supportsPromptCacheKey && promptCacheKey) {
2735
- params.prompt_cache_key = promptCacheKey;
2736
- if (cacheRetention === "long" && compat.supportsLongCacheRetention) params.prompt_cache_retention = "24h";
2737
- }
2738
- if (options?.temperature !== void 0) params.temperature = options.temperature;
2739
- if (options?.topP !== void 0) params.top_p = options.topP;
2740
- const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
2741
- provider: model.provider,
2742
- baseUrl: model.baseUrl,
2743
- hasTools: () => Boolean(context.tools?.length)
2744
- }) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
2745
- if (responseFormat !== void 0) params.response_format = responseFormat;
2746
- if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
2747
- if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
2748
- if (options?.seed !== void 0) params.seed = options.seed;
2749
- if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
2750
- if (supportsModelTools(model)) {
2751
- if (context.tools) {
2752
- const converted = convertTools(context.tools, compat, model);
2753
- if (converted.tools.length > 0 || converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
2754
- else if (hasToolHistory(context.messages)) params.tools = [];
2755
- if (options?.toolChoice) {
2756
- const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
2757
- if (toolChoice !== void 0) params.tool_choice = toolChoice;
2758
- } else if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
2759
- } else if (hasToolHistory(context.messages)) params.tools = [];
2760
- if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
2761
- delete params.tools;
2762
- delete params.tool_choice;
2677
+ const observeText = (text) => {
2678
+ for (const char of text) {
2679
+ if (char === "\n" || char === "\r") {
2680
+ finishLine();
2681
+ continue;
2682
+ }
2683
+ if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
2684
+ else lineOverflowed = true;
2763
2685
  }
2764
- }
2765
- {
2766
- const maxTokenBudget = resolveOpenAICompletionsMaxTokens(model, options);
2767
- const effectiveMaxTokens = maxTokenBudget.maxTokens;
2768
- const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
2769
- let clampedMaxTokens = effectiveMaxTokens;
2770
- const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
2771
- if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
2772
- clampedMaxTokens = modelMaxTokens;
2773
- emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
2686
+ };
2687
+ return {
2688
+ observe(chunk) {
2689
+ if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
2690
+ },
2691
+ finish() {
2692
+ if (sawDone) return;
2693
+ observeText(decoder.decode());
2694
+ if (line || lineOverflowed) finishLine();
2695
+ },
2696
+ sawDone: () => sawDone
2697
+ };
2698
+ }
2699
+ function createOpenAICompletionsClient(model, context, apiKey, optionHeaders, opts) {
2700
+ const clientConfig = buildOpenAICompletionsClientConfig(model, context, optionHeaders);
2701
+ return new OpenAI({
2702
+ apiKey,
2703
+ baseURL: clientConfig.baseURL,
2704
+ dangerouslyAllowBrowser: true,
2705
+ defaultHeaders: clientConfig.defaultHeaders,
2706
+ defaultQuery: clientConfig.defaultQuery,
2707
+ fetch: opts?.fetch ?? buildGuardedModelFetch(model),
2708
+ ...buildOpenAISdkClientOptions(model)
2709
+ });
2710
+ }
2711
+ function buildOpenAICompletionsClientConfig(model, context, optionHeaders) {
2712
+ const headers = buildOpenAIClientHeaders(model, context, optionHeaders);
2713
+ const defaultQuery = {};
2714
+ let baseURL = model.baseUrl;
2715
+ let isAzureHost = false;
2716
+ try {
2717
+ const parsed = new URL(model.baseUrl);
2718
+ isAzureHost = isAzureOpenAICompatibleHost(parsed.hostname.toLowerCase());
2719
+ parsed.searchParams.forEach((value, key) => {
2720
+ if (value) defaultQuery[key] = value;
2721
+ });
2722
+ parsed.search = "";
2723
+ baseURL = parsed.toString().replace(/\/$/, "");
2724
+ } catch {}
2725
+ if (isAzureHost) {
2726
+ const apiVersionHeader = Object.keys(headers).find((key) => key.toLowerCase() === "api-version");
2727
+ if (apiVersionHeader) {
2728
+ const apiVersion = headers[apiVersionHeader]?.trim();
2729
+ delete headers[apiVersionHeader];
2730
+ if (apiVersion && !defaultQuery["api-version"]) defaultQuery["api-version"] = apiVersion;
2774
2731
  }
2775
- if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
2776
- const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
2777
- const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
2778
- if (clampedMaxTokens > remainingBudget) {
2779
- clampedMaxTokens = remainingBudget;
2780
- emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
2732
+ }
2733
+ return {
2734
+ baseURL,
2735
+ defaultHeaders: headers,
2736
+ defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
2737
+ };
2738
+ }
2739
+ function createOpenAICompletionsTransportStreamFn() {
2740
+ return (model, context, options) => {
2741
+ const eventStream = createAssistantMessageEventStream();
2742
+ const stream = eventStream;
2743
+ (async () => {
2744
+ const output = {
2745
+ role: "assistant",
2746
+ content: [],
2747
+ api: model.api,
2748
+ provider: model.provider,
2749
+ model: model.id,
2750
+ usage: {
2751
+ input: 0,
2752
+ output: 0,
2753
+ cacheRead: 0,
2754
+ cacheWrite: 0,
2755
+ totalTokens: 0,
2756
+ cost: {
2757
+ input: 0,
2758
+ output: 0,
2759
+ cacheRead: 0,
2760
+ cacheWrite: 0,
2761
+ total: 0
2762
+ }
2763
+ },
2764
+ stopReason: "stop",
2765
+ timestamp: Date.now()
2766
+ };
2767
+ let firstEventAbort;
2768
+ try {
2769
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
2770
+ const doneDetector = createSseDoneDetector();
2771
+ const baseFetch = buildGuardedModelFetch(model);
2772
+ const doneDetectingFetch = async (url, init) => {
2773
+ const response = await baseFetch(url, init);
2774
+ if (!response.body || !response.ok) return response;
2775
+ if (typeof TransformStream === "undefined" || !response.body.pipeThrough) return response;
2776
+ const transformed = response.body.pipeThrough(new TransformStream({
2777
+ transform(chunk, controller) {
2778
+ doneDetector.observe(chunk);
2779
+ controller.enqueue(chunk);
2780
+ },
2781
+ flush() {
2782
+ doneDetector.finish();
2783
+ }
2784
+ }));
2785
+ return new Response(transformed, {
2786
+ headers: response.headers,
2787
+ status: response.status,
2788
+ statusText: response.statusText
2789
+ });
2790
+ };
2791
+ const client = createOpenAICompletionsClient(model, context, apiKey, options?.headers, { fetch: doneDetectingFetch });
2792
+ let params = buildOpenAICompletionsParams(model, context, options);
2793
+ const nextParams = await options?.onPayload?.(params, model);
2794
+ if (nextParams !== void 0) params = nextParams;
2795
+ if (options?.openclawCodeModeToolSurface === true) {
2796
+ const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
2797
+ enforceCodeModeResponsesToolSurface(params, visibleToolNames);
2798
+ assertCodeModeResponsesToolSurface(params, visibleToolNames);
2799
+ }
2800
+ if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
2801
+ const emitReasoning = shouldEmitOpenAICompletionsReasoning(model, options);
2802
+ firstEventAbort = createFirstStreamEventAbortController(options?.signal);
2803
+ const { data: responseStream, response } = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
2804
+ timeoutMs: options?.timeoutMs,
2805
+ maxRetries: options?.maxRetries
2806
+ })).withResponse();
2807
+ await processCompletionsStream(withProviderResponseHook({
2808
+ stream: responseStream,
2809
+ signal: firstEventAbort.signal,
2810
+ abort: firstEventAbort.abort,
2811
+ hook: createOpenAIResponseHook(options?.onResponse, response, model),
2812
+ onReady: () => stream.push({
2813
+ type: "start",
2814
+ partial: output
2815
+ })
2816
+ }), output, model, stream, {
2817
+ signal: options?.signal,
2818
+ emitReasoning,
2819
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
2820
+ abortFirstEventStream: firstEventAbort.abort,
2821
+ onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
2822
+ sawStreamDONE: doneDetector.sawDone
2823
+ });
2824
+ finalizeTransportStream({
2825
+ stream,
2826
+ output,
2827
+ signal: options?.signal
2828
+ });
2829
+ } catch (error) {
2830
+ failTransportStream({
2831
+ stream,
2832
+ output,
2833
+ signal: options?.signal,
2834
+ error,
2835
+ cleanup: () => {
2836
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
2837
+ finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
2838
+ }
2839
+ });
2840
+ } finally {
2841
+ firstEventAbort?.dispose();
2781
2842
  }
2782
- }
2783
- if (clampedMaxTokens) if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
2784
- else params.max_completion_tokens = clampedMaxTokens;
2785
- }
2786
- const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort(options);
2787
- const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
2788
- model,
2789
- effort: completionsReasoningEffort,
2790
- fallbackMap: compat.reasoningEffortMap
2791
- }) : void 0;
2792
- const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
2793
- const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
2794
- const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
2795
- compatThinkingFormat: compat.thinkingFormat,
2796
- modelReasoning: model.reasoning,
2797
- payload: params,
2798
- requestedEffort: completionsReasoningEffort
2799
- });
2800
- applyTogetherOpenAICompletionsThinkingParams({
2801
- compatThinkingFormat: compat.thinkingFormat,
2802
- modelReasoning: model.reasoning,
2803
- payload: params,
2804
- requestedEffort: completionsReasoningEffort
2805
- });
2806
- if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
2807
- else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
2808
- else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
2809
- return params;
2810
- }
2811
- function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
2812
- const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
2813
- return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
2843
+ })();
2844
+ return eventStream;
2845
+ };
2814
2846
  }
2815
- const completionsTesting = {
2816
- getCompat,
2817
- createSseDoneDetector,
2818
- createOpenAICompletionsClient,
2819
- buildOpenAICompletionsClientConfig,
2820
- parseTransportChunkUsage: parseOpenAICompletionsUsage,
2821
- processOpenAICompletionsStream,
2822
- shouldEmitOpenAICompletionsReasoningForModel
2823
- };
2824
- if (process.env.VITEST || false) globalThis.openclawOpenAICompletionsTransportTestApi = completionsTesting;
2825
2847
  //#endregion
2826
- //#region packages/ai/src/transports/openai-responses-payload-policy.ts
2827
- /**
2828
- * OpenAI Responses payload policy.
2829
- * Classifies endpoint capabilities and applies store, prompt-cache,
2830
- * server-compaction, service-tier, and reasoning payload rules.
2831
- */
2832
- const OPENAI_RESPONSES_APIS = /* @__PURE__ */ new Set([
2833
- "openai-responses",
2834
- "azure-openai-responses",
2835
- "openai-chatgpt-responses",
2836
- "openclaw-openai-responses-transport",
2837
- "openclaw-openai-chatgpt-responses-transport"
2838
- ]);
2839
- const OPENAI_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set([
2840
- "openai",
2841
- "azure-openai",
2842
- "azure-openai-responses"
2843
- ]);
2844
- const LOCAL_ENDPOINT_HOSTS = /* @__PURE__ */ new Set([
2845
- "localhost",
2846
- "127.0.0.1",
2847
- "::1",
2848
- "[::1]"
2849
- ]);
2850
- const MODELSTUDIO_NATIVE_BASE_URLS = /* @__PURE__ */ new Set([
2851
- "https://coding-intl.dashscope.aliyuncs.com/v1",
2852
- "https://coding.dashscope.aliyuncs.com/v1",
2853
- "https://dashscope.aliyuncs.com/compatible-mode/v1",
2854
- "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
2855
- ]);
2856
- const MOONSHOT_NATIVE_BASE_URLS = /* @__PURE__ */ new Set(["https://api.moonshot.ai/v1", "https://api.moonshot.cn/v1"]);
2857
- function normalizeLowercaseString(value) {
2858
- const stringValue = readStringValue(value)?.trim().toLowerCase();
2859
- return stringValue ? stringValue : void 0;
2860
- }
2861
- function normalizeComparableBaseUrl(value) {
2862
- const trimmed = readStringValue(value)?.trim();
2863
- if (!trimmed) return;
2864
- const parsedValue = /^[a-z0-9.[\]-]+(?::\d+)?(?:[/?#].*)?$/i.test(trimmed) ? `https://${trimmed}` : trimmed;
2865
- try {
2866
- const url = new URL(parsedValue);
2867
- if (url.protocol !== "http:" && url.protocol !== "https:") return;
2868
- url.hash = "";
2869
- url.search = "";
2870
- return url.toString().replace(/\/+$/, "").toLowerCase();
2871
- } catch {
2872
- return;
2848
+ //#region packages/ai/src/transports/openai-responses-compact-request.ts
2849
+ const COMPACT_REQUEST = Symbol("openaiResponsesCompactRequest");
2850
+ function claimResponsesCompactRequest(options) {
2851
+ const controller = options ? Reflect.get(options, COMPACT_REQUEST) : void 0;
2852
+ if (controller?.claimed === false) {
2853
+ controller.claimed = true;
2854
+ return controller;
2873
2855
  }
2874
2856
  }
2875
- function resolveUrlHostname(value) {
2876
- const trimmed = readStringValue(value)?.trim();
2877
- if (!trimmed) return;
2857
+ /** Run a compact-endpoint request through the session's prepared stream stack. */
2858
+ async function requestPreparedOpenAIResponsesCompaction(streamFn, model, context, options) {
2859
+ const preparedOptions = { ...options };
2860
+ let resolveResult;
2861
+ let rejectResult;
2862
+ const result = new Promise((resolve, reject) => {
2863
+ resolveResult = resolve;
2864
+ rejectResult = reject;
2865
+ });
2866
+ const controller = {
2867
+ claimed: false,
2868
+ resolve: resolveResult,
2869
+ reject: rejectResult
2870
+ };
2871
+ Reflect.set(preparedOptions, COMPACT_REQUEST, controller);
2872
+ const stream = await Promise.resolve(streamFn(model, context, preparedOptions));
2873
+ if (!controller.claimed) throw new Error("Prepared stream did not reach an OpenAI Responses transport");
2878
2874
  try {
2879
- return new URL(trimmed).hostname.toLowerCase();
2880
- } catch {
2881
- try {
2882
- return new URL(`https://${trimmed}`).hostname.toLowerCase();
2883
- } catch {
2884
- return;
2885
- }
2875
+ return await result;
2876
+ } finally {
2877
+ await stream.result().catch(() => void 0);
2886
2878
  }
2887
2879
  }
2888
- function hostMatchesSuffix(host, suffix) {
2889
- return suffix.startsWith(".") || suffix.startsWith("-") ? host.endsWith(suffix) : host === suffix || host.endsWith(`.${suffix}`);
2890
- }
2891
- function isLocalEndpointHost(host) {
2892
- return LOCAL_ENDPOINT_HOSTS.has(host) || host.endsWith(".localhost") || host.endsWith(".local") || host.endsWith(".internal");
2893
- }
2894
- function resolveBundledOpenAIResponsesEndpointClass(baseUrl) {
2895
- const trimmed = readStringValue(baseUrl)?.trim();
2896
- if (!trimmed) return "default";
2897
- const host = resolveUrlHostname(trimmed);
2898
- if (!host) return "invalid";
2899
- const comparableBaseUrl = normalizeComparableBaseUrl(trimmed);
2900
- switch (host) {
2901
- case "api.anthropic.com": return "anthropic-public";
2902
- case "api.cerebras.ai": return "cerebras-native";
2903
- case "llm.chutes.ai": return "chutes-native";
2904
- case "api.deepseek.com": return "deepseek-native";
2905
- case "api.groq.com": return "groq-native";
2906
- case "api.mistral.ai": return "mistral-public";
2907
- case "api.openai.com": return "openai-public";
2908
- case "chatgpt.com": return "openai";
2909
- case "generativelanguage.googleapis.com": return "google-generative-ai";
2910
- case "aiplatform.googleapis.com": return "google-vertex";
2911
- case "api.x.ai": return "xai-native";
2912
- case "api.z.ai": return "zai-native";
2913
- }
2914
- if (hostMatchesSuffix(host, ".githubcopilot.com")) return "github-copilot-native";
2915
- if (hostMatchesSuffix(host, ".openai.azure.com")) return "azure-openai";
2916
- if (hostMatchesSuffix(host, "openrouter.ai")) return "openrouter";
2917
- if (hostMatchesSuffix(host, "opencode.ai")) return "opencode-native";
2918
- if (hostMatchesSuffix(host, "-aiplatform.googleapis.com")) return "google-vertex";
2919
- if (comparableBaseUrl && MOONSHOT_NATIVE_BASE_URLS.has(comparableBaseUrl)) return "moonshot-native";
2920
- if (comparableBaseUrl && MODELSTUDIO_NATIVE_BASE_URLS.has(comparableBaseUrl)) return "modelstudio-native";
2921
- if (isLocalEndpointHost(host)) return "local";
2922
- return "custom";
2923
- }
2924
- function isOpenAIResponsesApi(api) {
2925
- return api !== void 0 && OPENAI_RESPONSES_APIS.has(api);
2926
- }
2927
- function readCompatPayloadBoolean(compat, key) {
2928
- if (!compat || typeof compat !== "object") return;
2929
- const value = compat[key];
2930
- return typeof value === "boolean" ? value : void 0;
2931
- }
2932
- function resolveOpenAIResponsesPayloadCapabilities(model) {
2933
- const provider = normalizeLowercaseString(model.provider);
2934
- const api = normalizeLowercaseString(model.api);
2935
- const isOpenAIProvider = provider === "openai";
2936
- const endpointClass = resolveBundledOpenAIResponsesEndpointClass(model.baseUrl);
2937
- const isResponsesApi = isOpenAIResponsesApi(api);
2938
- const usesConfiguredBaseUrl = endpointClass !== "default";
2939
- const usesKnownNativeOpenAIEndpoint = endpointClass === "openai-public" || endpointClass === "openai" || endpointClass === "azure-openai";
2940
- const usesKnownNativeOpenAIRoute = endpointClass === "default" ? provider === "openai" : usesKnownNativeOpenAIEndpoint;
2941
- const usesExplicitProxyLikeEndpoint = usesConfiguredBaseUrl && !usesKnownNativeOpenAIEndpoint;
2942
- const promptCacheKeySupport = readCompatPayloadBoolean(model.compat, "supportsPromptCacheKey");
2943
- const shouldStripResponsesPromptCache = promptCacheKeySupport === true ? false : promptCacheKeySupport === false ? isResponsesApi : isResponsesApi && usesExplicitProxyLikeEndpoint;
2944
- const supportsResponsesStoreField = readCompatPayloadBoolean(model.compat, "supportsStore") !== false && isResponsesApi;
2880
+ //#endregion
2881
+ //#region packages/ai/src/transports/openai-responses-continuation.ts
2882
+ const HTTP_CONTINUATION_IDLE_TTL_MS = 300 * 1e3;
2883
+ const TURN_HEADERS = /* @__PURE__ */ new Set([
2884
+ "traceparent",
2885
+ "x-openclaw-turn-id",
2886
+ "x-openclaw-turn-attempt"
2887
+ ]);
2888
+ function jsonValuesEqual(left, right) {
2889
+ return stableStringify(JSON.parse(JSON.stringify(left))) === stableStringify(JSON.parse(JSON.stringify(right)));
2890
+ }
2891
+ function requestWithoutInput(request) {
2892
+ const { input: _input, previous_response_id: _previousResponseId, ...rest } = request;
2893
+ if (!isRecord(rest.metadata)) return rest;
2894
+ const metadata = Object.fromEntries(Object.entries(rest.metadata).filter(([key]) => key !== "openclaw_turn_id" && key !== "openclaw_turn_attempt"));
2945
2895
  return {
2946
- allowsOpenAIServiceTier: provider === "openai" && (api === "openai-responses" || api === "openclaw-openai-responses-transport") && endpointClass === "openai-public" || isOpenAIProvider && (api === "openai-chatgpt-responses" || api === "openclaw-openai-chatgpt-responses-transport" || api === "openai-responses" || api === "openclaw-openai-responses-transport") && endpointClass === "openai",
2947
- allowsResponsesStore: supportsResponsesStoreField && api !== "openai-chatgpt-responses" && api !== "openclaw-openai-chatgpt-responses-transport" && provider !== void 0 && OPENAI_RESPONSES_PROVIDERS.has(provider) && usesKnownNativeOpenAIEndpoint,
2948
- shouldStripResponsesPromptCache,
2949
- supportsResponsesStoreField,
2950
- usesKnownNativeOpenAIRoute
2951
- };
2952
- }
2953
- function parsePositiveInteger(value) {
2954
- if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.floor(value);
2955
- if (typeof value === "string") return parseStrictPositiveInteger(value);
2956
- }
2957
- function resolveOpenAIResponsesCompactThreshold(model) {
2958
- const contextWindow = parsePositiveInteger(model.contextWindow);
2959
- if (contextWindow) return Math.max(1e3, Math.floor(contextWindow * .7));
2960
- return 8e4;
2961
- }
2962
- function shouldEnableOpenAIResponsesServerCompaction(explicitStore, provider, extraParams) {
2963
- const configured = extraParams?.responsesServerCompaction;
2964
- if (configured === false) return false;
2965
- if (explicitStore !== true) return false;
2966
- if (configured === true) return true;
2967
- return provider === "openai";
2968
- }
2969
- function stripDisabledOpenAIReasoningPayload(payloadObj) {
2970
- const reasoning = payloadObj.reasoning;
2971
- if (reasoning === "none") {
2972
- delete payloadObj.reasoning;
2973
- return;
2974
- }
2975
- if (!reasoning || typeof reasoning !== "object" || Array.isArray(reasoning)) return;
2976
- if (reasoning.effort === "none") delete payloadObj.reasoning;
2977
- }
2978
- /** Strip returned-item metadata rejected by strict Responses-compatible endpoints. */
2979
- function stripInputItemStatuses(input) {
2980
- if (!Array.isArray(input)) return;
2981
- for (const item of input) if (item && typeof item === "object" && !Array.isArray(item)) delete item.status;
2982
- }
2983
- /** Resolve payload mutation policy for one OpenAI Responses-style model endpoint. */
2984
- function resolveOpenAIResponsesPayloadPolicy(model, options = {}) {
2985
- const capabilities = resolveOpenAIResponsesPayloadCapabilities(model);
2986
- const storeMode = options.storeMode ?? "provider-policy";
2987
- const explicitStore = storeMode === "preserve" ? void 0 : storeMode === "disable" ? capabilities.supportsResponsesStoreField ? false : void 0 : capabilities.allowsResponsesStore ? true : void 0;
2988
- const isResponsesApi = isOpenAIResponsesApi(normalizeLowercaseString(model.api));
2989
- const shouldStripDisabledReasoningPayload = isResponsesApi && (!capabilities.usesKnownNativeOpenAIRoute || !supportsOpenAIReasoningEffort(model, "none"));
2990
- const shouldStripInputStatus = isResponsesApi && !capabilities.usesKnownNativeOpenAIRoute;
2896
+ ...rest,
2897
+ metadata
2898
+ };
2899
+ }
2900
+ function normalizeAssistantReplayInput(input) {
2901
+ return input.map((item) => {
2902
+ if (!isRecord(item)) return item;
2903
+ if (item.type === "reasoning") return { type: "reasoning" };
2904
+ if (item.type !== "function_call" && !(item.type === "message" && item.role === "assistant")) return item;
2905
+ const { id: _id, status: _status, ...stableItem } = item;
2906
+ if (item.type === "message" && Array.isArray(stableItem.content)) stableItem.content = stableItem.content.map((part) => {
2907
+ if (!isRecord(part) || part.type !== "output_text") return part;
2908
+ const { annotations: _annotations, logprobs: _logprobs, ...stablePart } = part;
2909
+ return stablePart;
2910
+ });
2911
+ return stableItem;
2912
+ });
2913
+ }
2914
+ function resolveResponsesContinuationRequest(continuation, request) {
2915
+ if (!continuation) return {
2916
+ request,
2917
+ continuationStatus: "no_previous_response"
2918
+ };
2919
+ if (request.previous_response_id) return {
2920
+ request,
2921
+ continuationStatus: "explicit_previous_response_id"
2922
+ };
2923
+ if (!jsonValuesEqual(requestWithoutInput(request), requestWithoutInput(continuation.lastRequest))) return {
2924
+ request,
2925
+ continuationStatus: "request_changed"
2926
+ };
2927
+ const currentInput = request.input ?? [];
2928
+ const previousInput = continuation.lastRequest.input ?? [];
2929
+ const baselineLength = previousInput.length + continuation.lastResponseItems.length;
2930
+ if (currentInput.length < baselineLength) return {
2931
+ request,
2932
+ continuationStatus: "history_shorter"
2933
+ };
2934
+ if (!jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(0, previousInput.length)), normalizeAssistantReplayInput(previousInput)) || !jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(previousInput.length, baselineLength)), normalizeAssistantReplayInput(continuation.lastResponseItems))) return {
2935
+ request,
2936
+ continuationStatus: "history_changed"
2937
+ };
2991
2938
  return {
2992
- allowsServiceTier: capabilities.allowsOpenAIServiceTier,
2993
- compactThreshold: parsePositiveInteger(options.extraParams?.responsesCompactThreshold) ?? resolveOpenAIResponsesCompactThreshold(model),
2994
- explicitStore,
2995
- shouldStripDisabledReasoningPayload,
2996
- shouldStripInputStatus,
2997
- shouldStripPromptCache: options.enablePromptCacheStripping === true && capabilities.shouldStripResponsesPromptCache,
2998
- shouldStripStore: explicitStore !== true && readCompatPayloadBoolean(model.compat, "supportsStore") === false && isResponsesApi,
2999
- useServerCompaction: options.enableServerCompaction === true && shouldEnableOpenAIResponsesServerCompaction(explicitStore, model.provider, options.extraParams)
3000
- };
3001
- }
3002
- /** Mutate a Responses request payload according to the resolved endpoint policy. */
3003
- function applyOpenAIResponsesPayloadPolicy(payloadObj, policy) {
3004
- if (policy.explicitStore !== void 0) payloadObj.store = policy.explicitStore;
3005
- if (policy.shouldStripStore) delete payloadObj.store;
3006
- if (policy.shouldStripPromptCache) {
3007
- delete payloadObj.prompt_cache_key;
3008
- delete payloadObj.prompt_cache_retention;
3009
- }
3010
- if (policy.useServerCompaction && payloadObj.context_management === void 0) payloadObj.context_management = [{
3011
- type: "compaction",
3012
- compact_threshold: policy.compactThreshold
3013
- }];
3014
- if (policy.shouldStripDisabledReasoningPayload) stripDisabledOpenAIReasoningPayload(payloadObj);
3015
- if (policy.shouldStripInputStatus) stripInputItemStatuses(payloadObj.input);
2939
+ request: {
2940
+ ...request,
2941
+ previous_response_id: continuation.lastResponseId,
2942
+ input: currentInput.slice(baselineLength)
2943
+ },
2944
+ continuationStatus: "continued"
2945
+ };
2946
+ }
2947
+ const httpContinuationEntries = /* @__PURE__ */ new Map();
2948
+ let nextHttpContinuationGeneration = 1;
2949
+ function connectionIdentity(params) {
2950
+ const headers = Object.entries(resolveAiTransportHeaderSentinels(params.headers) ?? {}).map(([name, value]) => [name.toLowerCase(), value]).filter(([name]) => !TURN_HEADERS.has(name)).toSorted(([a], [b]) => a.localeCompare(b));
2951
+ return sha256Hex(JSON.stringify([
2952
+ getAiTransportHost().resolveSecretSentinel(params.apiKey),
2953
+ params.baseUrl,
2954
+ headers
2955
+ ]));
2956
+ }
2957
+ function claimOpenAIResponsesHttpContinuation(params) {
2958
+ const key = `${params.sessionId}\0${connectionIdentity(params)}`;
2959
+ const previous = httpContinuationEntries.get(key);
2960
+ if (previous?.kind === "claimed") return;
2961
+ if (previous?.kind === "ready") clearTimeout(previous.idleTimer);
2962
+ const generation = nextHttpContinuationGeneration++;
2963
+ const claimed = {
2964
+ kind: "claimed",
2965
+ sessionId: params.sessionId,
2966
+ generation
2967
+ };
2968
+ httpContinuationEntries.set(key, claimed);
2969
+ return {
2970
+ request: resolveResponsesContinuationRequest(previous?.kind === "ready" ? previous.state : void 0, params.request).request,
2971
+ commit: (effectiveRequest, response) => {
2972
+ if (httpContinuationEntries.get(key) !== claimed) return;
2973
+ const idleTimer = setTimeout(() => {
2974
+ const current = httpContinuationEntries.get(key);
2975
+ if (current?.kind === "ready" && current.generation === generation) httpContinuationEntries.delete(key);
2976
+ }, HTTP_CONTINUATION_IDLE_TTL_MS);
2977
+ idleTimer.unref?.();
2978
+ const ready = {
2979
+ ...claimed,
2980
+ kind: "ready",
2981
+ state: {
2982
+ lastRequest: effectiveRequest,
2983
+ lastResponseId: response.id,
2984
+ lastResponseItems: response.output
2985
+ },
2986
+ idleTimer
2987
+ };
2988
+ httpContinuationEntries.set(key, ready);
2989
+ },
2990
+ release: () => {
2991
+ if (httpContinuationEntries.get(key) === claimed) httpContinuationEntries.delete(key);
2992
+ }
2993
+ };
3016
2994
  }
2995
+ registerSessionResourceCleanup((sessionId) => {
2996
+ for (const [key, entry] of httpContinuationEntries) if (!sessionId || entry.sessionId === sessionId) {
2997
+ if (entry.kind === "ready") clearTimeout(entry.idleTimer);
2998
+ httpContinuationEntries.delete(key);
2999
+ }
3000
+ });
3017
3001
  //#endregion
3018
3002
  //#region packages/ai/src/transports/openai-responses-params-internal.ts
3003
+ const OPENAI_RESPONSES_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set([
3004
+ "openai",
3005
+ "opencode",
3006
+ "azure-openai-responses",
3007
+ "github-copilot"
3008
+ ]);
3019
3009
  function convertResponsesTools(tools, model, options) {
3020
3010
  const projection = projectOpenAITools(tools);
3021
3011
  const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
@@ -3114,7 +3104,7 @@ function resolveOpenAIResponsesTextFormat(responseFormat) {
3114
3104
  };
3115
3105
  return responseFormat;
3116
3106
  }
3117
- function buildOpenAIResponsesParams(model, context, options, metadata) {
3107
+ function convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode) {
3118
3108
  const isCodexResponses = isOpenAICodexResponsesModel(model);
3119
3109
  const isNativeCodexResponses = usesNativeOpenAICodexResponsesBackend(model);
3120
3110
  const compat = getCompat(model);
@@ -3122,19 +3112,20 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
3122
3112
  const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
3123
3113
  const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
3124
3114
  const replayResponsesItemIds = !isNativeCodexResponses && (options?.replayResponsesItemIds ?? policyAllowsReplayIds);
3125
- const messages = convertResponsesMessages(model, context, /* @__PURE__ */ new Set([
3126
- "openai",
3127
- "opencode",
3128
- "azure-openai-responses",
3129
- "github-copilot"
3130
- ]), {
3115
+ return convertResponsesMessages(model, context, OPENAI_RESPONSES_TOOL_CALL_PROVIDERS, {
3131
3116
  includeSystemPrompt: !isCodexResponses,
3132
3117
  supportsDeveloperRole,
3133
3118
  replayReasoningItems: true,
3134
3119
  replayResponsesItemIds,
3135
3120
  authProfileId: options?.authProfileId,
3136
- sessionId: options?.sessionId
3121
+ sessionId: options?.sessionId,
3122
+ replayMode
3137
3123
  });
3124
+ }
3125
+ function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint") {
3126
+ const isCodexResponses = isOpenAICodexResponsesModel(model);
3127
+ const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
3128
+ const messages = convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode);
3138
3129
  if (isCodexResponses) ensureOpenAICodexResponsesInput(messages, context);
3139
3130
  const cacheRetention = resolveCacheRetention(options?.cacheRetention);
3140
3131
  const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
@@ -3194,6 +3185,262 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
3194
3185
  return sanitizeOpenAICodexResponsesParams(model, params);
3195
3186
  }
3196
3187
  //#endregion
3188
+ //#region packages/ai/src/transports/openai-responses-websocket.ts
3189
+ const SESSION_WEBSOCKET_CACHE_TTL_MS = 300 * 1e3;
3190
+ const SESSION_WEBSOCKET_MAX_AGE_MS = 3300 * 1e3;
3191
+ const WEBSOCKET_OPEN_STATE = 1;
3192
+ const websocketSessionCache = /* @__PURE__ */ new Map();
3193
+ const degradedWebSocketConnections = /* @__PURE__ */ new Map();
3194
+ function isOfficialOpenAIResponsesBaseUrl(baseUrl) {
3195
+ if (!baseUrl) return false;
3196
+ try {
3197
+ const url = new URL(baseUrl);
3198
+ return url.origin === "https://api.openai.com" && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && url.pathname.replace(/\/+$/, "") === "/v1";
3199
+ } catch {
3200
+ return false;
3201
+ }
3202
+ }
3203
+ function supportsNativeOpenAIResponsesEndpoint(params) {
3204
+ return params.provider.trim().toLowerCase() === "openai" && params.api === "openai-responses" && isOfficialOpenAIResponsesBaseUrl(params.baseUrl);
3205
+ }
3206
+ function closeWebSocketSilently(socket, reason = "done") {
3207
+ try {
3208
+ socket.close({
3209
+ code: 1e3,
3210
+ reason
3211
+ });
3212
+ } catch {}
3213
+ }
3214
+ function invalidateOwnedWebSocketSession(cacheKey, entry, reason = "done") {
3215
+ if (entry.idleTimer) {
3216
+ clearTimeout(entry.idleTimer);
3217
+ entry.idleTimer = void 0;
3218
+ }
3219
+ closeWebSocketSilently(entry.socket, reason);
3220
+ if (websocketSessionCache.get(cacheKey) === entry) websocketSessionCache.delete(cacheKey);
3221
+ }
3222
+ function scheduleSessionWebSocketExpiry(cacheKey, entry) {
3223
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
3224
+ entry.idleTimer = setTimeout(() => {
3225
+ if (entry.busy) return;
3226
+ invalidateOwnedWebSocketSession(cacheKey, entry, "idle_timeout");
3227
+ }, SESSION_WEBSOCKET_CACHE_TTL_MS);
3228
+ entry.idleTimer.unref?.();
3229
+ }
3230
+ function prepareWebSocketConnection(client, headers) {
3231
+ if (!isOfficialOpenAIResponsesBaseUrl(client.baseURL)) throw new Error("OpenAI Responses WebSocket requires the official API endpoint");
3232
+ if (typeof client.apiKey !== "string" || client.apiKey.length === 0) throw new Error("OpenAI Responses WebSocket requires an API key");
3233
+ const resolvedApiKey = getAiTransportHost().resolveSecretSentinel(client.apiKey);
3234
+ const resolvedHeaders = { ...resolveAiTransportHeaderSentinels(headers) };
3235
+ for (const key of Object.keys(resolvedHeaders)) {
3236
+ const normalizedKey = key.toLowerCase();
3237
+ if (normalizedKey === "authorization" || normalizedKey === "traceparent") delete resolvedHeaders[key];
3238
+ }
3239
+ if (!resolvedApiKey) throw new Error("OpenAI Responses WebSocket requires a resolved API key");
3240
+ return {
3241
+ client: client.withOptions({ apiKey: resolvedApiKey }),
3242
+ headers: resolvedHeaders,
3243
+ identity: sha256Hex(JSON.stringify([
3244
+ resolvedApiKey,
3245
+ client.baseURL,
3246
+ Object.entries(resolvedHeaders).toSorted(([a], [b]) => a.localeCompare(b))
3247
+ ]))
3248
+ };
3249
+ }
3250
+ function createWebSocket(connection, onError) {
3251
+ const socket = new ResponsesWS(connection.client, {
3252
+ headers: connection.headers,
3253
+ maxQueueSize: 1
3254
+ });
3255
+ socket.on("error", () => onError(socket));
3256
+ return socket;
3257
+ }
3258
+ function createTransientWebSocketLease(connection) {
3259
+ const socket = createWebSocket(connection, (failedSocket) => closeWebSocketSilently(failedSocket, "transport_error"));
3260
+ return {
3261
+ socket,
3262
+ iterator: socket.stream(),
3263
+ reusedConnection: false,
3264
+ release: () => closeWebSocketSilently(socket)
3265
+ };
3266
+ }
3267
+ function createCachedWebSocketLease(cacheKey, entry, reusedConnection) {
3268
+ entry.busy = true;
3269
+ return {
3270
+ socket: entry.socket,
3271
+ iterator: entry.socket.stream(),
3272
+ entry,
3273
+ reusedConnection,
3274
+ release: ({ keep } = {}) => {
3275
+ if (!keep || entry.socket.socket.readyState !== WEBSOCKET_OPEN_STATE) {
3276
+ invalidateOwnedWebSocketSession(cacheKey, entry);
3277
+ return;
3278
+ }
3279
+ entry.busy = false;
3280
+ scheduleSessionWebSocketExpiry(cacheKey, entry);
3281
+ }
3282
+ };
3283
+ }
3284
+ function acquireWebSocket(params, connection) {
3285
+ if (!(params.mode !== "websocket" && Boolean(params.sessionId)) || !params.sessionId) return createTransientWebSocketLease(connection);
3286
+ const cacheKey = `${params.sessionId}\0${connection.identity}`;
3287
+ const cached = websocketSessionCache.get(cacheKey);
3288
+ if (cached) {
3289
+ if (cached.idleTimer) {
3290
+ clearTimeout(cached.idleTimer);
3291
+ cached.idleTimer = void 0;
3292
+ }
3293
+ if (cached.busy) return createTransientWebSocketLease(connection);
3294
+ const expired = Date.now() - cached.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
3295
+ if (!expired && cached.socket.socket.readyState === WEBSOCKET_OPEN_STATE) return createCachedWebSocketLease(cacheKey, cached, true);
3296
+ invalidateOwnedWebSocketSession(cacheKey, cached, expired ? "connection_age_limit" : "done");
3297
+ }
3298
+ const entry = {
3299
+ socket: createWebSocket(connection, (failedSocket) => {
3300
+ const failedEntry = websocketSessionCache.get(cacheKey);
3301
+ if (failedEntry?.socket === failedSocket) invalidateOwnedWebSocketSession(cacheKey, failedEntry, "transport_error");
3302
+ else closeWebSocketSilently(failedSocket, "transport_error");
3303
+ }),
3304
+ sessionId: params.sessionId,
3305
+ busy: true,
3306
+ createdAt: Date.now()
3307
+ };
3308
+ websocketSessionCache.set(cacheKey, entry);
3309
+ return createCachedWebSocketLease(cacheKey, entry, false);
3310
+ }
3311
+ function sanitizeWebSocketRequest(request) {
3312
+ const { stream: _stream, background: _background, ...websocketRequest } = request;
3313
+ return websocketRequest;
3314
+ }
3315
+ async function nextWebSocketMessage(iterator, signal) {
3316
+ if (!signal) return iterator.next();
3317
+ if (signal.aborted) throw transportAbortError(signal);
3318
+ let onAbort;
3319
+ try {
3320
+ return await Promise.race([iterator.next(), new Promise((_resolve, reject) => {
3321
+ onAbort = () => reject(transportAbortError(signal));
3322
+ signal.addEventListener("abort", onAbort, { once: true });
3323
+ })]);
3324
+ } finally {
3325
+ if (onAbort) signal.removeEventListener("abort", onAbort);
3326
+ }
3327
+ }
3328
+ function readServerEvent(message) {
3329
+ if (message.type === "message") return message.message;
3330
+ if (message.type === "error") throw parseOpenAIResponsesWebSocketServerError(message.error) ?? new Error("OpenAI Responses WebSocket transport failed", { cause: message.error });
3331
+ if (message.type === "close") throw new Error(`OpenAI Responses WebSocket closed before completion (code ${message.code})`);
3332
+ }
3333
+ function createOpenAIResponsesWebSocketStream(params) {
3334
+ const connection = prepareWebSocketConnection(params.client, params.headers);
3335
+ const fullRequest = sanitizeWebSocketRequest(params.request);
3336
+ const requestModel = typeof fullRequest.model === "string" ? fullRequest.model : "";
3337
+ const degradationKey = `${params.sessionId ?? ""}\0${connection.identity}\0${requestModel}`;
3338
+ const degraded = degradedWebSocketConnections.get(degradationKey);
3339
+ if (degraded && degraded.retryAt > Date.now()) throw new OpenAIResponsesWebSocketPreDispatchError(/* @__PURE__ */ new Error("OpenAI Responses WebSocket is cooling down after a transport failure"));
3340
+ degradedWebSocketConnections.delete(degradationKey);
3341
+ const markDegraded = () => {
3342
+ const cooldownMs = params.degradeCooldownMs;
3343
+ if (cooldownMs === void 0 || !Number.isFinite(cooldownMs) || cooldownMs <= 0) return;
3344
+ degradedWebSocketConnections.set(degradationKey, {
3345
+ sessionId: params.sessionId,
3346
+ retryAt: Date.now() + cooldownMs
3347
+ });
3348
+ };
3349
+ let lease;
3350
+ try {
3351
+ lease = acquireWebSocket(params, connection);
3352
+ } catch (error) {
3353
+ markDegraded();
3354
+ throw new OpenAIResponsesWebSocketPreDispatchError(error);
3355
+ }
3356
+ let prepared;
3357
+ try {
3358
+ const continuation = lease.entry?.continuation;
3359
+ if (continuation && lease.entry) {
3360
+ lease.entry.continuation = void 0;
3361
+ prepared = resolveResponsesContinuationRequest(continuation, fullRequest);
3362
+ } else prepared = {
3363
+ request: fullRequest,
3364
+ continuationStatus: lease.entry ? "no_previous_response" : "socket_not_cached"
3365
+ };
3366
+ } catch (error) {
3367
+ lease.iterator.return?.().catch(() => void 0);
3368
+ lease.release({ keep: false });
3369
+ throw error;
3370
+ }
3371
+ let streamStarted = false;
3372
+ let terminalResponse;
3373
+ let terminalReceived = false;
3374
+ let released = false;
3375
+ const finish = ({ keep = true } = {}) => {
3376
+ if (released) return;
3377
+ released = true;
3378
+ if (keep && lease.entry && terminalResponse) lease.entry.continuation = {
3379
+ lastRequest: fullRequest,
3380
+ lastResponseId: terminalResponse.id,
3381
+ lastResponseItems: terminalResponse.output
3382
+ };
3383
+ lease.release({ keep });
3384
+ };
3385
+ return {
3386
+ stream: { async *[Symbol.asyncIterator]() {
3387
+ if (streamStarted) throw new Error("OpenAI Responses WebSocket stream can only be consumed once");
3388
+ streamStarted = true;
3389
+ const iterator = lease.iterator;
3390
+ let requestDispatched = false;
3391
+ try {
3392
+ if (params.signal?.aborted) throw transportAbortError(params.signal);
3393
+ for (;;) {
3394
+ const next = await nextWebSocketMessage(iterator, params.signal);
3395
+ if (next.done) throw new Error("OpenAI Responses WebSocket closed before a terminal response event");
3396
+ if (next.value.type === "open") {
3397
+ if (!requestDispatched) {
3398
+ requestDispatched = true;
3399
+ lease.socket.send({
3400
+ ...prepared.request,
3401
+ type: "response.create"
3402
+ });
3403
+ }
3404
+ continue;
3405
+ }
3406
+ const event = readServerEvent(next.value);
3407
+ if (!event) continue;
3408
+ if (event.type === "response.failed") throw new OpenAIResponsesWebSocketResponseFailedError(event.response.output.length > 0);
3409
+ if (event.type === "response.completed") terminalResponse = event.response;
3410
+ terminalReceived = event.type === "response.completed" || event.type === "response.incomplete";
3411
+ yield event;
3412
+ if (terminalReceived) {
3413
+ degradedWebSocketConnections.delete(degradationKey);
3414
+ return;
3415
+ }
3416
+ }
3417
+ } catch (error) {
3418
+ if (lease.entry) lease.entry.continuation = void 0;
3419
+ const safeRetry = error instanceof OpenAIResponsesWebSocketSafeRetryError;
3420
+ if (!params.callerSignal?.aborted && !safeRetry) markDegraded();
3421
+ if (!requestDispatched && !params.signal?.aborted) throw new OpenAIResponsesWebSocketPreDispatchError(error);
3422
+ if (!requestDispatched || params.callerSignal?.aborted || error instanceof OpenAIResponsesWebSocketResponseFailedError || safeRetry) throw error;
3423
+ throw new OpenAIResponsesWebSocketPostDispatchError(error);
3424
+ } finally {
3425
+ await iterator.return?.().catch(() => void 0);
3426
+ if (!terminalReceived) finish({ keep: false });
3427
+ }
3428
+ } },
3429
+ request: prepared.request,
3430
+ reusedConnection: lease.reusedConnection,
3431
+ continuationStatus: prepared.continuationStatus,
3432
+ finish
3433
+ };
3434
+ }
3435
+ function closeOpenAIResponsesWebSocketSessions(sessionId) {
3436
+ for (const [cacheKey, entry] of websocketSessionCache) {
3437
+ if (sessionId && entry.sessionId !== sessionId) continue;
3438
+ invalidateOwnedWebSocketSession(cacheKey, entry, "session_cleanup");
3439
+ }
3440
+ for (const [key, entry] of degradedWebSocketConnections) if (!sessionId || entry.sessionId === sessionId) degradedWebSocketConnections.delete(key);
3441
+ }
3442
+ registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
3443
+ //#endregion
3197
3444
  //#region packages/media-core/src/inline-image-data-url.ts
3198
3445
  /** Prefix used to distinguish inline data URLs from remote/local image references. */
3199
3446
  const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
@@ -3338,6 +3585,20 @@ function sanitizeResponsesImagePayload(params) {
3338
3585
  }
3339
3586
  //#endregion
3340
3587
  //#region packages/ai/src/transports/openai-responses-client.ts
3588
+ function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
3589
+ if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
3590
+ if (getAiTransportHost().requiresManagedTransport(model)) return;
3591
+ return supportsNativeOpenAIResponsesEndpoint({
3592
+ provider: model.provider,
3593
+ api: model.api,
3594
+ baseUrl: model.baseUrl
3595
+ }) ? transport : void 0;
3596
+ }
3597
+ function combineWebSocketTimeoutSignal(signal, model, timeoutMs) {
3598
+ const resolvedTimeoutMs = timeoutMs !== void 0 && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : getAiTransportHost().resolveModelRequestTimeoutMs(model);
3599
+ if (resolvedTimeoutMs === void 0 || !Number.isFinite(resolvedTimeoutMs)) return signal;
3600
+ return AbortSignal.any([signal, AbortSignal.timeout(Math.max(1, resolvedTimeoutMs))]);
3601
+ }
3341
3602
  function resolveProviderTransportTurnState(model, params) {
3342
3603
  const normalizedProvider = model.provider.trim().toLowerCase();
3343
3604
  const allowRuntimePluginLoad = normalizedProvider === "openai" || normalizedProvider === "azure-openai" || normalizedProvider === "azure-openai-responses";
@@ -3366,94 +3627,227 @@ function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turn
3366
3627
  ...buildOpenAISdkClientOptions(model)
3367
3628
  });
3368
3629
  }
3630
+ async function postOpenAIResponsesCompaction(params) {
3631
+ const response = await params.client.post("/responses/compact", {
3632
+ ...buildOpenAISdkRequestOptions(params.model, params.options?.signal, {
3633
+ timeoutMs: params.options?.timeoutMs,
3634
+ maxRetries: params.options?.maxRetries
3635
+ }),
3636
+ body: {
3637
+ model: params.request.model,
3638
+ input: params.request.input
3639
+ }
3640
+ });
3641
+ const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
3642
+ const item = output[0];
3643
+ const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
3644
+ if (!isRecord(response) || response.object !== "response.compaction" || output.length !== 1 || !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 exactly one compaction item");
3645
+ return {
3646
+ item,
3647
+ usage,
3648
+ model: params.model,
3649
+ replayMetadata: buildOpenAIResponsesReasoningReplayMetadata(params.model, {
3650
+ authProfileId: params.options?.authProfileId,
3651
+ sessionId: params.options?.sessionId
3652
+ })
3653
+ };
3654
+ }
3369
3655
  function createResponsesTransportExecutor(config) {
3370
3656
  return (model, context, options) => {
3371
3657
  const responsesOptions = options;
3658
+ const compactRequest = claimResponsesCompactRequest(responsesOptions);
3372
3659
  const eventStream = createAssistantMessageEventStream();
3373
3660
  const stream = eventStream;
3374
3661
  (async () => {
3375
- const output = {
3376
- role: "assistant",
3377
- content: [],
3378
- api: config.outputApi ?? model.api,
3379
- provider: model.provider,
3380
- model: model.id,
3381
- usage: {
3382
- input: 0,
3383
- output: 0,
3384
- cacheRead: 0,
3385
- cacheWrite: 0,
3386
- totalTokens: 0,
3387
- cost: {
3388
- input: 0,
3389
- output: 0,
3390
- cacheRead: 0,
3391
- cacheWrite: 0,
3392
- total: 0
3393
- }
3394
- },
3395
- stopReason: "stop",
3396
- timestamp: Date.now()
3397
- };
3662
+ const output = createOpenAIResponsesAssistantOutput(model, config.outputApi);
3398
3663
  let firstEventAbort;
3664
+ let continuationClaim;
3399
3665
  try {
3400
3666
  const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
3667
+ const websocketMode = resolveNativeOpenAIResponsesWebSocketMode(model, responsesOptions?.transport);
3401
3668
  const turnState = resolveProviderTransportTurnState(model, {
3402
3669
  sessionId: options?.sessionId,
3403
3670
  turnId: randomUUID(),
3404
3671
  attempt: 1,
3405
- transport: "stream"
3672
+ transport: websocketMode ? "websocket" : "stream"
3406
3673
  });
3674
+ const websocketSessionPolicy = websocketMode ? turnState?.websocket : void 0;
3675
+ const websocketHeaders = websocketMode ? buildOpenAIClientHeaders(model, context, options?.headers, websocketSessionPolicy?.headers, options?.sessionId) : void 0;
3407
3676
  const client = config.createClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId);
3408
- let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata);
3409
- const nextParams = await options?.onPayload?.(params, model);
3410
- if (nextParams !== void 0) params = nextParams;
3411
- if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
3412
- params = sanitizeOpenAICodexResponsesParams(model, params);
3413
- params = sanitizeResponsesImagePayload(params);
3414
- if (options?.openclawCodeModeToolSurface === true) {
3415
- const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
3416
- enforceCodeModeResponsesToolSurface(params, visibleToolNames);
3417
- assertCodeModeResponsesToolSurface(params, visibleToolNames);
3677
+ const buildRequest = async (replayMode) => {
3678
+ let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata, replayMode);
3679
+ const nextParams = await options?.onPayload?.(params, model);
3680
+ if (nextParams !== void 0) params = nextParams;
3681
+ if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
3682
+ params = sanitizeOpenAICodexResponsesParams(model, params);
3683
+ params = sanitizeResponsesImagePayload(params);
3684
+ if (options?.openclawCodeModeToolSurface === true) {
3685
+ const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
3686
+ const allowedHostedToolTypes = responsesOptions?.openclawCodeModeAllowedHostedToolTypes;
3687
+ enforceCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
3688
+ assertCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
3689
+ }
3690
+ return params;
3691
+ };
3692
+ const params = await buildRequest("checkpoint");
3693
+ if (compactRequest) {
3694
+ const compacted = await postOpenAIResponsesCompaction({
3695
+ client,
3696
+ model,
3697
+ request: params,
3698
+ options: responsesOptions
3699
+ });
3700
+ output.usage.input = compacted.usage.input_tokens;
3701
+ output.usage.output = compacted.usage.output_tokens;
3702
+ output.usage.totalTokens = compacted.usage.input_tokens + compacted.usage.output_tokens;
3703
+ compactRequest.resolve(compacted);
3704
+ stream.push({
3705
+ type: "done",
3706
+ reason: output.stopReason,
3707
+ message: output
3708
+ });
3709
+ stream.end();
3710
+ return;
3418
3711
  }
3712
+ const sessionId = options?.sessionId;
3713
+ if (config.httpContinuation && !websocketMode && !getAiTransportHost().requiresManagedTransport(model) && supportsNativeOpenAIResponsesEndpoint({
3714
+ provider: model.provider,
3715
+ api: model.api,
3716
+ baseUrl: model.baseUrl
3717
+ }) && sessionId && params.store === true && !params.previous_response_id) continuationClaim = claimOpenAIResponsesHttpContinuation({
3718
+ sessionId,
3719
+ apiKey,
3720
+ baseUrl: model.baseUrl,
3721
+ headers: buildOpenAIClientHeaders(model, context, options?.headers, turnState?.headers, sessionId),
3722
+ request: params
3723
+ });
3419
3724
  const observePrompt = createResponsesPromptEgressObserver(responsesOptions, context.systemPrompt);
3420
3725
  const requestStartedAt = Date.now();
3421
- firstEventAbort = createFirstStreamEventAbortController(options?.signal);
3422
- const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
3726
+ let started = false;
3727
+ const startStream = () => {
3728
+ if (!started) {
3729
+ started = true;
3730
+ stream.push({
3731
+ type: "start",
3732
+ partial: output
3733
+ });
3734
+ }
3735
+ };
3736
+ const firstEvent = createFirstStreamEventAbortController(options?.signal);
3737
+ firstEventAbort = firstEvent;
3738
+ const requestOptions = buildOpenAISdkRequestOptions(model, firstEvent.signal, {
3423
3739
  stream: config.streamRequest,
3424
3740
  timeoutMs: options?.timeoutMs,
3425
3741
  maxRetries: options?.maxRetries
3426
3742
  });
3427
- emitModelTransportDebug(log, `[responses] start provider=${model.provider} api=${model.api} model=${model.id} baseUrl=${formatModelTransportDebugBaseUrl(model.baseUrl)} timeoutMs=${safeDebugValue(requestOptions?.timeout)} apiKey=${apiKey ? "present" : "missing"} ${summarizeResponsesPayload(params)}`);
3428
- const { stream: responseStream, response } = await config.createResponseStream({
3429
- client,
3430
- request: params,
3431
- requestOptions,
3432
- model,
3433
- observePrompt
3434
- });
3435
- await options?.onResponse?.({
3436
- status: response.status,
3437
- headers: headersToRecord(response.headers)
3438
- }, model);
3439
- emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - requestStartedAt}`);
3440
- stream.push({
3441
- type: "start",
3442
- partial: output
3443
- });
3444
- await processResponsesStream(observeResponsesStream(responseStream, model), output, stream, model, {
3445
- ...config.pricingOptions?.(responsesOptions),
3446
- firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
3447
- abortFirstEventStream: firstEventAbort.abort,
3448
- onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
3449
- signal: options?.signal,
3450
- reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
3451
- authProfileId: responsesOptions?.authProfileId,
3452
- sessionId: options?.sessionId
3453
- })
3454
- });
3455
- if (options?.signal?.aborted) throw transportAbortError(options.signal);
3456
- if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
3743
+ const websocketSignal = combineWebSocketTimeoutSignal(firstEvent.signal, model, requestOptions?.timeout);
3744
+ 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)}`);
3745
+ let continuationBaseline;
3746
+ const createSseStream = async (initialRequest = continuationClaim?.request ?? params, initialAttemptKind = "initial", initialRejectedCompaction) => {
3747
+ const { stream: rawResponseStream, response, attempt } = await config.createResponseStream({
3748
+ client,
3749
+ request: initialRequest,
3750
+ requestOptions,
3751
+ model,
3752
+ observePrompt,
3753
+ initialAttemptKind,
3754
+ initialRejectedCompaction,
3755
+ buildFullHistoryRequest: () => buildRequest("full-history"),
3756
+ onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, responsesOptions, checkpoint)
3757
+ });
3758
+ if (continuationClaim) continuationBaseline = attempt.request.previous_response_id ? params : attempt.request;
3759
+ return withProviderResponseHook({
3760
+ stream: observeResponsesStream(rawResponseStream, model, requestStartedAt),
3761
+ signal: firstEvent.signal,
3762
+ abort: firstEvent.abort,
3763
+ hook: createOpenAIResponseHook(options?.onResponse, response, model),
3764
+ onReady: () => {
3765
+ emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} transport=sse elapsedMs=${Date.now() - requestStartedAt}`);
3766
+ startStream();
3767
+ }
3768
+ });
3769
+ };
3770
+ let responseStream;
3771
+ let finishWebSocket;
3772
+ let transport = "sse";
3773
+ const logWebSocketFallback = (reason) => emitModelTransportDebug(log, `[responses] websocket_fallback provider=${model.provider} api=${model.api} model=${model.id} reason=${reason}`);
3774
+ const closeWebSocketForFallback = (reason) => {
3775
+ finishWebSocket?.({ keep: false });
3776
+ finishWebSocket = void 0;
3777
+ transport = "sse";
3778
+ logWebSocketFallback(reason);
3779
+ };
3780
+ if (websocketMode) try {
3781
+ const websocket = createOpenAIResponsesWebSocketStream({
3782
+ client,
3783
+ request: params,
3784
+ mode: websocketMode,
3785
+ sessionId: options?.sessionId,
3786
+ headers: websocketHeaders,
3787
+ signal: websocketSignal,
3788
+ callerSignal: options?.signal,
3789
+ degradeCooldownMs: websocketSessionPolicy?.degradeCooldownMs
3790
+ });
3791
+ finishWebSocket = websocket.finish;
3792
+ observePrompt?.(websocket.request, {
3793
+ egress: "responses-websocket",
3794
+ payloadVariant: "initial"
3795
+ });
3796
+ transport = "websocket";
3797
+ 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))))}`);
3798
+ responseStream = { async *[Symbol.asyncIterator]() {
3799
+ try {
3800
+ for await (const event of websocket.stream) {
3801
+ startStream();
3802
+ yield event;
3803
+ }
3804
+ } catch (error) {
3805
+ if (error instanceof OpenAIResponsesWebSocketSafeRetryError) {
3806
+ const encryptedContentRejected = isInvalidEncryptedContentError(error);
3807
+ const recovery = encryptedContentRejected ? await resolveNextResponsesEncryptedContentAttempt({
3808
+ kind: "initial",
3809
+ request: {
3810
+ ...websocket.request,
3811
+ stream: true
3812
+ }
3813
+ }, error, { buildFullHistoryRequest: () => buildRequest("full-history") }) : void 0;
3814
+ if (encryptedContentRejected && !recovery) throw error;
3815
+ closeWebSocketForFallback(`safe_server_error code=${error.code} status=${safeDebugValue(error.status)} param=${safeDebugValue(error.param)}`);
3816
+ yield* await createSseStream(recovery?.request ?? await buildRequest("full-history"), recovery?.kind ?? "continuation-rejected", recovery?.rejectedCompaction);
3817
+ return;
3818
+ }
3819
+ if (websocketSignal.aborted || !(error instanceof OpenAIResponsesWebSocketPreDispatchError)) throw error;
3820
+ transport = "sse";
3821
+ logWebSocketFallback("before_first_event");
3822
+ yield* await createSseStream();
3823
+ }
3824
+ } };
3825
+ } catch {
3826
+ closeWebSocketForFallback("setup_failure");
3827
+ responseStream = await createSseStream();
3828
+ }
3829
+ else responseStream = await createSseStream();
3830
+ try {
3831
+ const terminal = await processResponsesStream(responseStream, output, stream, model, {
3832
+ ...config.pricingOptions?.(responsesOptions, model),
3833
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
3834
+ abortFirstEventStream: firstEvent.abort,
3835
+ onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
3836
+ signal: options?.signal,
3837
+ reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
3838
+ authProfileId: responsesOptions?.authProfileId,
3839
+ sessionId: options?.sessionId
3840
+ })
3841
+ });
3842
+ finishWebSocket?.();
3843
+ if (options?.signal?.aborted) throw transportAbortError(options.signal);
3844
+ if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
3845
+ if (continuationClaim && continuationBaseline && terminal) continuationClaim.commit(continuationBaseline, terminal);
3846
+ } catch (error) {
3847
+ finishWebSocket?.({ keep: false });
3848
+ throw error;
3849
+ }
3850
+ emitModelTransportDebug(log, `[responses] completed provider=${model.provider} api=${model.api} model=${model.id} transport=${transport} elapsedMs=${Date.now() - requestStartedAt}`);
3457
3851
  stream.push({
3458
3852
  type: "done",
3459
3853
  reason: output.stopReason,
@@ -3461,9 +3855,20 @@ function createResponsesTransportExecutor(config) {
3461
3855
  });
3462
3856
  stream.end();
3463
3857
  } catch (error) {
3858
+ if (compactRequest) {
3859
+ compactRequest.reject(error);
3860
+ Object.assign(output, projectProviderError(error, options?.signal));
3861
+ stream.push({
3862
+ type: "error",
3863
+ reason: output.stopReason,
3864
+ error: output
3865
+ });
3866
+ stream.end();
3867
+ return;
3868
+ }
3464
3869
  if (error instanceof ResponsesStreamFailure && error.observation) logResponsesFailedNoDetails(error.observation);
3465
3870
  log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
3466
- assignTransportErrorDetails(output, error, options?.signal);
3871
+ Object.assign(output, projectProviderError(error, options?.signal));
3467
3872
  stream.push({
3468
3873
  type: "error",
3469
3874
  reason: output.stopReason,
@@ -3471,6 +3876,7 @@ function createResponsesTransportExecutor(config) {
3471
3876
  });
3472
3877
  stream.end();
3473
3878
  } finally {
3879
+ continuationClaim?.release();
3474
3880
  firstEventAbort?.dispose();
3475
3881
  }
3476
3882
  })();
@@ -3480,12 +3886,13 @@ function createResponsesTransportExecutor(config) {
3480
3886
  function createOpenAIResponsesTransportStreamFn() {
3481
3887
  return createResponsesTransportExecutor({
3482
3888
  streamRequest: true,
3889
+ httpContinuation: true,
3483
3890
  createClient: createOpenAIResponsesClient,
3484
3891
  buildRequest: buildOpenAIResponsesParams,
3485
3892
  createResponseStream: createResponsesStreamWithEncryptedContentRetry,
3486
- pricingOptions: (options) => ({
3893
+ pricingOptions: (options, model) => ({
3487
3894
  serviceTier: options?.serviceTier,
3488
- applyServiceTierPricing
3895
+ applyServiceTierPricing: (usage, serviceTier) => applyResponsesServiceTierPricing(usage, serviceTier, model)
3489
3896
  })
3490
3897
  });
3491
3898
  }
@@ -3494,18 +3901,8 @@ function createAzureOpenAIResponsesTransportStreamFn() {
3494
3901
  outputApi: "azure-openai-responses",
3495
3902
  firstEventTimeoutMs: AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS,
3496
3903
  createClient: createAzureOpenAIClient,
3497
- buildRequest: (model, context, options, metadata) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata),
3498
- createResponseStream: async ({ client, request, requestOptions, observePrompt }) => {
3499
- observePrompt?.(request, {
3500
- egress: "responses-sdk",
3501
- payloadVariant: "initial"
3502
- });
3503
- const { data, response } = await client.responses.create(request, requestOptions).withResponse();
3504
- return {
3505
- stream: data,
3506
- response
3507
- };
3508
- }
3904
+ buildRequest: (model, context, options, metadata, replayMode) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata, replayMode),
3905
+ createResponseStream: createResponsesStreamWithEncryptedContentRetry
3509
3906
  });
3510
3907
  }
3511
3908
  function normalizeAzureBaseUrl(baseUrl) {
@@ -3533,8 +3930,8 @@ function createAzureOpenAIClient(model, context, apiKey, optionHeaders, turnHead
3533
3930
  apiVersion: resolveAzureOpenAIApiVersion()
3534
3931
  });
3535
3932
  }
3536
- function buildAzureOpenAIResponsesParams(model, context, options, deploymentName, metadata) {
3537
- const params = buildOpenAIResponsesParams(model, context, options, metadata);
3933
+ function buildAzureOpenAIResponsesParams(model, context, options, deploymentName, metadata, replayMode = "checkpoint") {
3934
+ const params = buildOpenAIResponsesParams(model, context, options, metadata, replayMode);
3538
3935
  params.model = deploymentName;
3539
3936
  delete params.store;
3540
3937
  return params;
@@ -3559,11 +3956,8 @@ const responsesTesting = {
3559
3956
  buildOpenAIResponsesReasoningReplayMetadata,
3560
3957
  isInvalidEncryptedContentError,
3561
3958
  normalizeResponsesFailedEvent,
3562
- prepareOpenAIResponsesReasoningItemForReplay,
3563
3959
  createResponsesStreamWithEncryptedContentRetry,
3564
3960
  resolveAzureOpenAIApiVersion,
3565
- stripResponsesRequestEncryptedContent,
3566
- tagOpenAIResponsesReasoningReplayItem,
3567
3961
  summarizeResponsesFailedNoDetailsObservation,
3568
3962
  summarizeResponsesPayload,
3569
3963
  summarizeResponsesTools,
@@ -3572,6 +3966,50 @@ const responsesTesting = {
3572
3966
  };
3573
3967
  if (process.env.VITEST || false) globalThis.openclawOpenAIResponsesTransportTestApi = responsesTesting;
3574
3968
  //#endregion
3969
+ //#region packages/ai/src/transports/provider-compaction-replay.ts
3970
+ /** Whether provider replay state is a prefix-bound server compaction checkpoint. */
3971
+ function isCompactionReplayCheckpoint(replay) {
3972
+ const type = replay && typeof replay === "object" ? replay.type : void 0;
3973
+ return type === "anthropic-compaction" || type === "openai-responses-compaction";
3974
+ }
3975
+ /** Strip prefix-bound checkpoints after local history rewrites. */
3976
+ function stripCompactionReplayCheckpoint(message) {
3977
+ if (!isCompactionReplayCheckpoint(message.providerReplay)) return message;
3978
+ const replaySafeMessage = { ...message };
3979
+ delete replaySafeMessage.providerReplay;
3980
+ return replaySafeMessage;
3981
+ }
3982
+ /** Strip prefix-bound checkpoint state from an in-place message rewrite. */
3983
+ function stripCompactionReplayCheckpointInPlace(message) {
3984
+ if (isCompactionReplayCheckpoint(message.providerReplay)) delete message.providerReplay;
3985
+ }
3986
+ /** Reindex a prefix-bound checkpoint after known content removals. */
3987
+ function replaceCompactionReplayOwnerContent(message, content) {
3988
+ const next = {
3989
+ ...message,
3990
+ content
3991
+ };
3992
+ const replay = message.providerReplay;
3993
+ if (!isCompactionReplayCheckpoint(replay)) return next;
3994
+ const replayIndex = replay.replayIndex ?? 0;
3995
+ if (content.length === 0 || replayIndex > message.content.length) return stripCompactionReplayCheckpoint(next);
3996
+ let sourceIndex = 0;
3997
+ let nextReplayIndex = 0;
3998
+ if (!content.every((block) => {
3999
+ const index = message.content.indexOf(block, sourceIndex);
4000
+ sourceIndex = index + 1;
4001
+ nextReplayIndex += index >= 0 && index < replayIndex ? 1 : 0;
4002
+ return index >= 0;
4003
+ })) return stripCompactionReplayCheckpoint(next);
4004
+ return nextReplayIndex === replayIndex ? next : {
4005
+ ...next,
4006
+ providerReplay: {
4007
+ ...replay,
4008
+ replayIndex: nextReplayIndex
4009
+ }
4010
+ };
4011
+ }
4012
+ //#endregion
3575
4013
  //#region packages/ai/src/transports/provider-transport-stream.ts
3576
4014
  const SUPPORTED_TRANSPORT_APIS = /* @__PURE__ */ new Set([
3577
4015
  "openai-responses",
@@ -3582,10 +4020,7 @@ const SUPPORTED_TRANSPORT_APIS = /* @__PURE__ */ new Set([
3582
4020
  "google-generative-ai"
3583
4021
  ]);
3584
4022
  const SIMPLE_TRANSPORT_API_ALIAS = {
3585
- "openai-responses": "openclaw-openai-responses-transport",
3586
- "openai-chatgpt-responses": "openclaw-openai-chatgpt-responses-transport",
3587
4023
  "openai-completions": "openclaw-openai-completions-transport",
3588
- "azure-openai-responses": "openclaw-azure-openai-responses-transport",
3589
4024
  "anthropic-messages": "openclaw-anthropic-messages-transport",
3590
4025
  "google-generative-ai": "openclaw-google-generative-ai-transport"
3591
4026
  };
@@ -3638,6 +4073,10 @@ function isTransportAwareApiSupported(api) {
3638
4073
  }
3639
4074
  /** Maps public model APIs to the internal transport API id used by simple runtime dispatch. */
3640
4075
  function resolveTransportAwareSimpleApi(api) {
4076
+ if (OPENAI_RESPONSES_APIS.has(api)) {
4077
+ const alias = `openclaw-${api}-transport`;
4078
+ return OPENAI_RESPONSES_APIS.has(alias) ? alias : void 0;
4079
+ }
3641
4080
  return SIMPLE_TRANSPORT_API_ALIAS[api];
3642
4081
  }
3643
4082
  /** Creates a managed transport stream only when request overrides require it. */
@@ -3734,12 +4173,12 @@ function resolveProviderStreamApi(model) {
3734
4173
  ];
3735
4174
  return `${PROVIDER_STREAM_API_PREFIX}${parts.map((part) => encodeURIComponent(part)).join(":")}`;
3736
4175
  }
3737
- function applyProviderSimpleCompletionWrapper(registry, model, cfg) {
4176
+ function applyProviderSimpleCompletionWrapper(registry, model, cfg, hookSourceApi = model.api) {
3738
4177
  if (model.api.startsWith(PROVIDER_SIMPLE_COMPLETION_API_PREFIX)) return model;
3739
4178
  const sourceProvider = registry.getApiProvider(model.api);
3740
4179
  if (!sourceProvider) return model;
3741
- const sourceApi = model.api;
3742
- const sourceStreamFn = (runtimeModel, context, options) => sourceProvider.streamSimple(projectModel(runtimeModel, { api: sourceApi }), context, options);
4180
+ const dispatchApi = model.api;
4181
+ const sourceStreamFn = (runtimeModel, context, options) => sourceProvider.streamSimple(projectModel(runtimeModel, { api: dispatchApi }), context, options);
3743
4182
  const streamFn = getAiTransportHost().plugin.wrapSimpleCompletionStream({
3744
4183
  provider: model.provider,
3745
4184
  config: cfg,
@@ -3748,6 +4187,7 @@ function applyProviderSimpleCompletionWrapper(registry, model, cfg) {
3748
4187
  provider: model.provider,
3749
4188
  modelId: model.id,
3750
4189
  model,
4190
+ sourceApi: hookSourceApi,
3751
4191
  streamFn: sourceStreamFn
3752
4192
  }
3753
4193
  });
@@ -3781,7 +4221,6 @@ function wrapPluginProviderStream(streamFn) {
3781
4221
  };
3782
4222
  }
3783
4223
  function prepareProviderStreamModel(params) {
3784
- if (params.model.api === "google-generative-ai") return;
3785
4224
  const pluginModel = resolveModelHeaderSentinels(params.model);
3786
4225
  const providerStreamFn = getAiTransportHost().plugin.resolveProviderStream({
3787
4226
  provider: params.model.provider,
@@ -3807,20 +4246,19 @@ function prepareModelForSimpleCompletion(params) {
3807
4246
  cfg,
3808
4247
  apiRegistry
3809
4248
  });
3810
- if (providerStreamModel) return applyProviderSimpleCompletionWrapper(apiRegistry, providerStreamModel, cfg);
4249
+ if (providerStreamModel) return applyProviderSimpleCompletionWrapper(apiRegistry, providerStreamModel, cfg, model.api);
3811
4250
  const codexTransportModel = prepareCodexSimpleTransportModel(apiRegistry, model, cfg);
3812
- if (codexTransportModel) return applyProviderSimpleCompletionWrapper(apiRegistry, codexTransportModel, cfg);
4251
+ if (codexTransportModel) return applyProviderSimpleCompletionWrapper(apiRegistry, codexTransportModel, cfg, model.api);
3813
4252
  const transportAwareModel = prepareTransportAwareSimpleModel(model, { cfg });
3814
4253
  if (transportAwareModel !== model) {
3815
4254
  const streamFn = buildTransportAwareSimpleStreamFn(model, { cfg });
3816
- if (streamFn && registerCustomApi(apiRegistry, transportAwareModel.api, streamFn)) return applyProviderSimpleCompletionWrapper(apiRegistry, transportAwareModel, cfg);
4255
+ if (streamFn && registerCustomApi(apiRegistry, transportAwareModel.api, streamFn)) return applyProviderSimpleCompletionWrapper(apiRegistry, transportAwareModel, cfg, model.api);
3817
4256
  }
3818
- if (model.api === "google-generative-ai") return applyProviderSimpleCompletionWrapper(apiRegistry, getAiTransportHost().prepareGoogleSimpleCompletionModel(apiRegistry, model), cfg);
3819
4257
  if (model.provider === "anthropic-vertex") {
3820
4258
  const api = resolveAnthropicVertexSimpleApi(model.baseUrl);
3821
- if (registerCustomApi(apiRegistry, api, getAiTransportHost().plugin.createAnthropicVertexStream(model))) return applyProviderSimpleCompletionWrapper(apiRegistry, projectModel(model, { api }), cfg);
4259
+ if (registerCustomApi(apiRegistry, api, getAiTransportHost().plugin.createAnthropicVertexStream(model))) return applyProviderSimpleCompletionWrapper(apiRegistry, projectModel(model, { api }), cfg, model.api);
3822
4260
  }
3823
4261
  return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
3824
4262
  }
3825
4263
  //#endregion
3826
- export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, applyAnthropicCacheControlToMessages, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, coerceTransportToolCallArguments, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, hasOpenAICompatibleConversationTurn, isCodeModeModelVisibleToolName, isOpenAICodexResponsesModel, isOpenAICompletionsThinkingEnabled, log, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, parseOpenAICompletionsUsage, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, readOpenAICompletionsContentDeltas, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveCodeModeResponsesVisibleToolNames, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend };
4264
+ export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, applyAnthropicCacheControlToMessages, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, captureOpenAIResponsesCompaction, coerceTransportToolCallArguments, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIResponseHook, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, googleFlashSupportsMinimalThinking, hasOpenAICompatibleConversationTurn, isCodeModeModelVisibleToolName, isCompactionReplayCheckpoint, isOpenAICodexResponsesModel, isOpenAICompletionsThinkingEnabled, log, measureUtf8AppendBytes, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, parseOpenAICompletionsUsage, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, readOpenAICompletionsContentDeltas, replaceCompactionReplayOwnerContent, requestPreparedOpenAIResponsesCompaction, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveAnthropicServerCompactionPlan, resolveCodeModeResponsesVisibleToolNames, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesCompactEndpointPlan, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIResponsesServerCompactionPlan, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompactionReplayCheckpoint, stripCompactionReplayCheckpointInPlace, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend, withProviderResponseHook };