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

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 (88) hide show
  1. package/dist/{anthropic-CE-7SkF6.mjs → anthropic-B8KwTtlb.mjs} +62 -37
  2. package/dist/{anthropic-SrGtwsJu.d.mts → anthropic-BECQCNdF.d.mts} +0 -2
  3. package/dist/{src-QkygScBs.mjs → anthropic-BQXQudmA.mjs} +0 -2
  4. package/dist/{anthropic-usage-Ma4iX6uG.mjs → anthropic-compaction-replay-4Zx7-aCr.mjs} +225 -103
  5. package/dist/anthropic-payload-policy-CSx7MoKH.d.mts +50 -0
  6. package/dist/{api-registry-EpJoVwM1.d.mts → api-registry-DFxSnFqG.d.mts} +1 -1
  7. package/dist/{azure-openai-responses-Cc_kasye.mjs → azure-openai-responses-Aa4mVcYG.mjs} +23 -29
  8. package/dist/diagnostics.d.mts +24 -1
  9. package/dist/diagnostics.mjs +2 -1
  10. package/dist/event-stream-BVVgDSdq.d.mts +1 -0
  11. package/dist/{event-stream-DRmDMRHH.d.mts → event-stream-D91-ohD_.d.mts} +3 -1
  12. package/dist/{event-stream-CEV9t6da.mjs → event-stream-uSMZJ3FA.mjs} +16 -3
  13. package/dist/event-stream.d.mts +1 -1
  14. package/dist/event-stream.mjs +1 -1
  15. package/dist/{google-shared-Cq2UKx1l.mjs → google-shared-M1dl64nz.mjs} +17 -19
  16. package/dist/google-thinking-level-C-V3tecN.mjs +9 -0
  17. package/dist/{google-vertex-CusxWzvE.mjs → google-vertex-DCJHM_a5.mjs} +5 -4
  18. package/dist/{google-CGQu21aw.mjs → google-yDRnipeq.mjs} +4 -4
  19. package/dist/host-DF_yxOsA.mjs +477 -0
  20. package/dist/{host-Dn4_SZI2.d.mts → host-DJq_FGcz.d.mts} +13 -8
  21. package/dist/index.d.mts +7 -7
  22. package/dist/index.mjs +7 -5
  23. package/dist/internal/anthropic.d.mts +21 -7
  24. package/dist/internal/anthropic.mjs +5 -5
  25. package/dist/internal/openai-responses-payload-policy.d.mts +3 -0
  26. package/dist/internal/openai-responses-payload-policy.mjs +3 -0
  27. package/dist/internal/openai.d.mts +6 -6
  28. package/dist/internal/openai.mjs +9 -7
  29. package/dist/internal/runtime.d.mts +19 -5
  30. package/dist/internal/runtime.mjs +85 -72
  31. package/dist/internal/shared.d.mts +17 -8
  32. package/dist/internal/shared.mjs +4 -5
  33. package/dist/{json-parse-BvXNt1-7.mjs → json-parse-CDnesDM_.mjs} +4 -6
  34. package/dist/{mistral-Dx6VzNjf.mjs → mistral-D6hRXI7A.mjs} +67 -72
  35. package/dist/{number-coercion-1Miyb5MO.mjs → number-coercion-H9qHik3g.mjs} +7 -2
  36. package/dist/openai-chatgpt-jwt-KWcgd0d_.mjs +19 -0
  37. package/dist/{openai-chatgpt-responses-SC9m0FSf.mjs → openai-chatgpt-responses-C339VIfT.mjs} +235 -146
  38. package/dist/{openai-completions-BaIpl93j.mjs → openai-completions-D88orSJ9.mjs} +88 -67
  39. package/dist/openai-completions-compat-B0KTleXp.d.mts +43 -0
  40. package/dist/{openai-responses-Bdx3V_Yu.mjs → openai-responses-BXEcr-Tp.mjs} +17 -24
  41. package/dist/openai-responses-contracts-CD75pQ0m.d.mts +68 -0
  42. package/dist/openai-responses-contracts-DvILoqBk.mjs +247 -0
  43. package/dist/openai-responses-payload-policy-BDo8CQsx.mjs +208 -0
  44. package/dist/openai-responses-payload-policy-CNGFT9zr.d.mts +41 -0
  45. package/dist/{openai-responses-prompt-observer-internal-DDZPxRfr.mjs → openai-responses-prompt-observer-internal-BG-1AsBI.mjs} +2 -3
  46. package/dist/{openai-responses-stream-internal-Bl8YyVtF.mjs → openai-responses-shared-Cvt5MgEm.mjs} +1287 -1039
  47. package/dist/{openai-reasoning-compat-DKebnIBL.mjs → openai-stop-reason-9OONKyAm.mjs} +222 -180
  48. package/dist/{openai-transport-shared-Cipt7egQ.mjs → openai-tool-projection-CHGLt03m.mjs} +239 -128
  49. package/dist/provider-error-C8mBRJDl.mjs +424 -0
  50. package/dist/provider-error-CzNw4BWX.d.mts +12 -0
  51. package/dist/provider-options-AvldWZt8.mjs +21 -0
  52. package/dist/{provider-options-C5kYML7i.d.mts → provider-options-Cc5ZQ6Aa.d.mts} +19 -3
  53. package/dist/provider-transcript-transform--gtf0ZbR.mjs +32 -0
  54. package/dist/provider-types.d.mts +31 -0
  55. package/dist/provider-types.mjs +8 -0
  56. package/dist/providers.d.mts +1 -1
  57. package/dist/providers.mjs +17 -19
  58. package/dist/{reasoning-tag-text-partitioner-CGDyLWUR.mjs → reasoning-tag-text-partitioner-5ygO2rZc.mjs} +17 -8
  59. package/dist/record-coerce-DdXsgUd_.mjs +23 -0
  60. package/dist/{model-utils-Dau5dlgm.mjs → sanitize-unicode-BP7HLLZn.mjs} +28 -2
  61. package/dist/session-resources-CkR4WWy1.mjs +21 -0
  62. package/dist/simple-options-oqsXB6U3.mjs +450 -0
  63. package/dist/src-D2H6yKkH.mjs +2 -0
  64. package/dist/{stream-first-event-timeout-BIBomOGq.mjs → stream-first-event-timeout-MK28puvq.mjs} +2 -2
  65. package/dist/string-coerce-fsri9iCu.mjs +34 -0
  66. package/dist/{tool-schema-json-projection-B1b-XCn5.mjs → tool-schema-json-projection-FEr-jOoZ.mjs} +20 -13
  67. package/dist/transport-utils-Bv0FHkd0.mjs +138 -0
  68. package/dist/transports.d.mts +199 -209
  69. package/dist/transports.mjs +1842 -1364
  70. package/dist/types-BDdaOVi2.mjs +6 -0
  71. package/dist/{types-CH7ReIcU.d.mts → types-DTAP49HF.d.mts} +35 -2
  72. package/dist/types.d.mts +5 -5
  73. package/dist/types.mjs +6 -4
  74. package/dist/utf16-slice-CvGodqok.mjs +29 -0
  75. package/dist/{validation-DAa_yFOM.mjs → validation-B61OhAio.mjs} +6 -6
  76. package/dist/{validation-CcPcuEbR.d.mts → validation-DYNNrU9u.d.mts} +1 -1
  77. package/dist/validation.d.mts +1 -1
  78. package/dist/validation.mjs +1 -1
  79. package/package.json +15 -1
  80. package/dist/host-Bl7Kgddo.mjs +0 -407
  81. package/dist/openai-chatgpt-jwt-DhAAzLkj.mjs +0 -39
  82. package/dist/openai-responses-contracts-BBKBfAqR.d.mts +0 -116
  83. package/dist/openai-responses-shared-CCyMvg7M.mjs +0 -403
  84. package/dist/provider-error-DI0Ts28U.mjs +0 -47
  85. package/dist/sanitize-unicode-DT5o51ur.mjs +0 -26
  86. package/dist/tool-result-text-Dvkp2Dus.mjs +0 -274
  87. package/dist/transform-messages-DfjpNXNQ.mjs +0 -2
  88. package/dist/transport-stream-shared-CPNv7A3r.mjs +0 -297
@@ -1,28 +1,35 @@
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-BQXQudmA.mjs";
3
+ import { n as normalizeLowercaseStringOrEmpty, t as hasNonEmptyString } from "./string-coerce-fsri9iCu.mjs";
4
+ import { r as calculateCost } from "./sanitize-unicode-BP7HLLZn.mjs";
5
+ import { S as usesClaudeStreamingRefusalContract, _ as prepareClaudeNoPrefillRequestContext, a as describeToolResultMediaPlaceholder, c as extractToolResultText, d as isImageWithMediaPayload, f as ANTHROPIC_CLAUDE_CODE_BILLING_SYSTEM_BLOCK, g as mapAnthropicStopReason, h as defaultsClaudeAdaptiveThinking, m as applyClaudeRequestContract, n as getAiTransportHost, p as ANTHROPIC_CLAUDE_CODE_VERSION, r as resolveAiTransportHeaderSentinels, s as extractToolResultBlockText, v as requiresClaudeAdaptiveThinking, x as usesClaudeFable5MessagesContract, y as resolveAnthropicThinkingEffort } from "./host-DF_yxOsA.mjs";
6
+ import { a as isRecord } from "./record-coerce-DdXsgUd_.mjs";
7
+ import { i as canonicalizeBase64, o as stableStringify } from "./provider-error-C8mBRJDl.mjs";
8
+ import { n as toErrorObject, t as createResponsesPromptEgressObserver } from "./openai-responses-prompt-observer-internal-BG-1AsBI.mjs";
9
+ import { r as asNonNegativeFiniteNumber } from "./number-coercion-H9qHik3g.mjs";
10
+ import { _ as isOpenAIGpt56Model, d as OpenAIResponsesWebSocketSafeRetryError, g as isOpenAIGpt55Model, h as isOpenAIGpt54MiniModel, i as OPENAI_RESPONSES_APIS, l as OpenAIResponsesWebSocketPostDispatchError, p as parseOpenAIResponsesWebSocketServerError, r as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, t as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, u as OpenAIResponsesWebSocketPreDispatchError, v as normalizeOpenAIReasoningEffort, w as uniqueStrings, y as resolveOpenAIReasoningEffortForModel } from "./openai-responses-contracts-DvILoqBk.mjs";
6
11
  import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
7
12
  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";
13
+ import { C as withProviderResponseHook, E as quoteUnsafeIntegerLiterals, P as sortPromptCacheToolsByName, S as withProviderAcceptanceObserver, T as parseJsonPreservingUnsafeIntegers, _ as notifyProviderStreamOpened, a as assignTransportErrorDetails, b as sanitizeTransportPayloadText, c as createEmptyTransportUsage, d as finalizeTerminalToolCallArguments, f as finalizeTransportStream, g as notifyProviderHttpResponse, h as notifyProviderHttpMetadata, j as stripSystemPromptCacheBoundary, l as createWritableTransportEventStream, m as mergeTransportMetadata, o as coerceTransportToolCallArguments, p as mergeTransportHeaders, s as copyProviderAcceptanceObserver, t as adjustMaxTokensForThinking, u as failTransportStream, v as parseTerminalToolCallArguments, w as parseJsonObjectPreservingUnsafeIntegers, x as transportAbortError, y as sanitizeNonEmptyTransportPayloadText } from "./simple-options-oqsXB6U3.mjs";
14
+ import { a as resolveProviderEndpoint, c as transformTransportMessages, i as resolveOpenAIStrictToolSetting, n as buildGuardedModelFetch, r as resolveModelRequestTimeoutMs, s as resolveProviderRequestPolicyConfig } from "./tool-schema-json-projection-FEr-jOoZ.mjs";
15
+ 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-4Zx7-aCr.mjs";
16
+ import { a as createOpenAICompletionsToolCallDeltaNormalizer, c as resolveOpenAICompletionsCompat, d as clearPendingCommentaryText, f as rememberPendingCommentaryTags, h as tagUnresolvedTextAsCommentary, i as hasToolCallHistory, l as resolveOpenAICompletionsResponseFormat, m as tagPendingCommentaryText, n as resolveOpenAIReasoningEffortMap, o as finalizeOpenAICompletionsToolCalls, p as tagInterruptedTextPhases, r as convertMessages, s as detectOpenAICompletionsCompat, t as mapOpenAIStopReason, u as shouldOmitOllamaCompatResponseFormat } from "./openai-stop-reason-9OONKyAm.mjs";
13
17
  import { t as createDeferredEventBuffer } from "./deferred-event-buffer-DAvyP7qA.mjs";
14
- import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
18
+ import { n as parseStreamingJson } from "./json-parse-CDnesDM_.mjs";
15
19
  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";
20
+ 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-Bv0FHkd0.mjs";
21
+ import { t as googleFlashSupportsMinimalThinking } from "./google-thinking-level-C-V3tecN.mjs";
22
+ 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, m as buildResponsesFailedNoDetailsObservation, n as applyResponsesServiceTierPricing, p as ResponsesStreamFailure, q as findOpenAIStrictToolProjectionDiagnostics, ut as emitModelTransportDebug, v as stringifyRedactedEvent, x as summarizeResponsesFailedNoDetailsObservation, y as stringifyRedactedPayload, z as resolveNextResponsesEncryptedContentAttempt } from "./openai-responses-shared-Cvt5MgEm.mjs";
23
+ import { t as codeModeToolSurfaceObserver } from "./provider-options-AvldWZt8.mjs";
24
+ import { a as createModelStreamCooperativeScheduler, c as isOpenAICompletionsThinkingEnabled, d as parseOpenAICompletionsUsage, f as readOpenAICompletionsContentDeltas, h as throwIfModelStreamAborted, i as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, l as log, m as resolvePromptCacheKey, n as reconcileOpenAICompletionsToolChoice, o as createOpenAIProviderAcceptanceHook, p as readOpenAICompletionsReasoningBatch, r as reconcileOpenAIResponsesToolChoice, s as createOpenAIResponseHook, t as projectOpenAITools, u as measureUtf8AppendBytes } from "./openai-tool-projection-CHGLt03m.mjs";
25
+ import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-MK28puvq.mjs";
26
+ import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-5ygO2rZc.mjs";
27
+ import { i as resolveOpenAIResponsesServerCompactionPlan, n as resolveOpenAIResponsesCompactEndpointPlan, r as resolveOpenAIResponsesPayloadPolicy, t as applyOpenAIResponsesPayloadPolicy } from "./openai-responses-payload-policy-BDo8CQsx.mjs";
22
28
  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";
29
+ import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
24
30
  import { randomUUID } from "node:crypto";
25
31
  import OpenAI, { AzureOpenAI } from "openai";
32
+ import { ResponsesWS } from "openai/resources/responses/ws.js";
26
33
  //#region packages/ai/src/transports/anthropic-transport-stream.ts
27
34
  /**
28
35
  * Native Anthropic Messages streaming transport.
@@ -59,7 +66,7 @@ function isAnthropicOAuthToken(apiKey) {
59
66
  }
60
67
  function isDirectAnthropicModel(model) {
61
68
  if (normalizeLowercaseStringOrEmpty(model.provider) !== "anthropic") return false;
62
- const endpointClass = resolveProviderEndpoint(model.baseUrl).endpointClass;
69
+ const endpointClass = resolveProviderEndpoint(model).endpointClass;
63
70
  return endpointClass === "default" || endpointClass === "anthropic-public";
64
71
  }
65
72
  function isKimiAnthropicProvider(provider) {
@@ -74,7 +81,7 @@ function useAnthropicServerSideFallback(model) {
74
81
  return (usesClaudeFable5MessagesContract(model) || resolveClaudeOpus5ModelIdentity(model) !== void 0) && isDirectAnthropicModel(model);
75
82
  }
76
83
  function supportsReasoningContentReplay(model) {
77
- return resolveProviderEndpoint(model.baseUrl).endpointClass === "xiaomi-native";
84
+ return resolveProviderEndpoint(model).endpointClass === "xiaomi-native";
78
85
  }
79
86
  function buildAnthropicBetaHeader(model, betaFeatures, params) {
80
87
  if (!isDirectAnthropicModel(model)) return;
@@ -169,7 +176,7 @@ async function convertAnthropicMessages(messages, model, isOAuthToken, options)
169
176
  continue;
170
177
  }
171
178
  if (msg.role === "assistant") {
172
- const blocks = [];
179
+ const blocks = i === 0 && options.compaction ? [options.compaction] : [];
173
180
  const reasoningContent = [];
174
181
  let omittedThinking = false;
175
182
  for (const block of msg.content) {
@@ -296,7 +303,7 @@ function convertAnthropicTools(tools, isOAuthToken) {
296
303
  };
297
304
  }
298
305
  function parseAnthropicToolCallArguments(inputJson) {
299
- return parseJsonObjectPreservingUnsafeIntegers(inputJson) ?? parseStreamingJson(inputJson);
306
+ return coerceTransportToolCallArguments(parseJsonObjectPreservingUnsafeIntegers(inputJson) ?? parseStreamingJson(inputJson));
300
307
  }
301
308
  const DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com";
302
309
  /** Resolve the effective Anthropic API base URL from model or environment. */
@@ -399,7 +406,7 @@ async function* parseAnthropicSseBody(body, signal) {
399
406
  }
400
407
  function createAnthropicMessagesClient(params) {
401
408
  const url = resolveAnthropicMessagesUrl(params.baseURL);
402
- return { messages: { async *stream(body, options) {
409
+ return { messages: { async stream(body, options) {
403
410
  const headers = mergeTransportHeaders({
404
411
  "content-type": "application/json",
405
412
  "anthropic-version": "2023-06-01",
@@ -412,12 +419,10 @@ function createAnthropicMessagesClient(params) {
412
419
  body: JSON.stringify(body),
413
420
  signal: options?.signal
414
421
  });
415
- if (!response.ok) {
416
- const detail = await readAnthropicMessagesErrorBodySnippet(response);
417
- throw new Error(formatAnthropicMessagesHttpError(response, detail));
418
- }
419
- if (!response.body) return;
420
- yield* parseAnthropicSseBody(response.body, options?.signal);
422
+ return {
423
+ response,
424
+ stream: response.body ? parseAnthropicSseBody(response.body, options?.signal) : []
425
+ };
421
426
  } } };
422
427
  }
423
428
  function formatAnthropicMessagesHttpError(response, detail) {
@@ -528,11 +533,17 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
528
533
  baseUrl: model.baseUrl,
529
534
  cacheRetention: options?.cacheRetention,
530
535
  enableCacheControl: true
531
- });
536
+ }, model);
532
537
  const cacheBreakpointOptOutMessageIndexes = /* @__PURE__ */ new Set();
533
- const messages = await convertAnthropicMessages(context.messages, model, isOAuthToken, {
538
+ const replayPlan = buildAnthropicReplayPlan(context.messages, model, {
539
+ enabled: !isOAuthToken && options?.anthropicServerCompaction === true,
540
+ authProfileId: options?.authProfileId,
541
+ sessionId: options?.sessionId
542
+ });
543
+ const messages = await convertAnthropicMessages(replayPlan.messages, model, isOAuthToken, {
534
544
  allowReasoningContentReplay: supportsReasoningContentReplay(model),
535
545
  cacheBreakpointOptOutMessageIndexes,
546
+ compaction: replayPlan.compaction,
536
547
  replayThinkingEnabled
537
548
  });
538
549
  const params = {
@@ -591,7 +602,8 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
591
602
  applyAnthropicPayloadPolicyToParams(params, payloadPolicy, cacheBreakpointOptOutMessageIndexes);
592
603
  return {
593
604
  params,
594
- toolProjection
605
+ toolProjection,
606
+ usedCompactionReplay: replayPlan.compaction !== void 0
595
607
  };
596
608
  }
597
609
  function resolveAnthropicTransportOptions(model, options, apiKey) {
@@ -605,7 +617,7 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
605
617
  const reasoningModelMaxTokens = resolvePositiveAnthropicTokenLimit(model.maxTokens) ?? baseMaxTokens;
606
618
  const mandatoryAdaptiveThinking = requiresClaudeAdaptiveThinking(model);
607
619
  const reasoning = options?.reasoning === "off" && mandatoryAdaptiveThinking ? "low" : options?.reasoning;
608
- const resolved = {
620
+ const resolved = copyProviderAcceptanceObserver(options, {
609
621
  temperature: options?.temperature,
610
622
  stop: options?.stop,
611
623
  maxTokens: baseMaxTokens,
@@ -615,13 +627,16 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
615
627
  sessionId: options?.sessionId,
616
628
  headers: options?.headers,
617
629
  onPayload: options?.onPayload,
630
+ onResponse: options?.onResponse,
618
631
  maxRetryDelayMs: options?.maxRetryDelayMs,
619
632
  metadata: options?.metadata,
620
633
  interleavedThinking: options?.interleavedThinking,
621
634
  toolChoice: options?.toolChoice,
622
635
  thinkingBudgets: options?.thinkingBudgets,
623
- reasoning
624
- };
636
+ reasoning,
637
+ ...options?.anthropicServerCompaction === true ? { anthropicServerCompaction: true } : {},
638
+ ...options?.authProfileId ? { authProfileId: options.authProfileId } : {}
639
+ });
625
640
  if (reasoning === "off") {
626
641
  resolved.thinkingEnabled = false;
627
642
  return resolved;
@@ -664,6 +679,7 @@ function createAnthropicMessagesTransportStreamFn() {
664
679
  const eventSink = refusalBuffer ?? stream;
665
680
  let costModel = model;
666
681
  let messageStartPromptUsage;
682
+ let usedCompactionReplay = false;
667
683
  try {
668
684
  const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? "";
669
685
  if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
@@ -676,17 +692,29 @@ function createAnthropicMessagesTransportStreamFn() {
676
692
  options: transportOptions
677
693
  });
678
694
  const builtParams = await buildAnthropicParams(model, requestContext, isOAuthToken, transportOptions);
695
+ usedCompactionReplay = builtParams.usedCompactionReplay;
679
696
  let params = builtParams.params;
680
697
  const toolProjection = builtParams.toolProjection;
681
698
  const nextParams = await transportOptions.onPayload?.(params, model);
682
699
  if (nextParams !== void 0) params = nextParams;
683
700
  applyClaudeRequestContract(params, model);
684
- const anthropicStream = client.messages.stream({
701
+ const { response, stream: anthropicStream } = await client.messages.stream({
685
702
  ...params,
686
703
  stream: true
687
704
  }, transportOptions.signal ? { signal: transportOptions.signal } : void 0);
705
+ await notifyProviderHttpResponse({
706
+ options: transportOptions,
707
+ response,
708
+ model
709
+ });
710
+ if (!response.ok) {
711
+ const detail = await readAnthropicMessagesErrorBodySnippet(response);
712
+ throw new Error(formatAnthropicMessagesHttpError(response, detail));
713
+ }
688
714
  const blocks = output.content;
689
715
  const blockIndexes = /* @__PURE__ */ new Map();
716
+ const sealedToolCalls = [];
717
+ const compactionCapture = createCompactionCapture(output, model, transportOptions);
690
718
  const pendingThinkingSignatures = /* @__PURE__ */ new Map();
691
719
  const allowReasoningContentReplay = supportsReasoningContentReplay(model);
692
720
  const reasoningContentThinkingBlocks = /* @__PURE__ */ new Map();
@@ -720,6 +748,7 @@ function createAnthropicMessagesTransportStreamFn() {
720
748
  partial: output
721
749
  });
722
750
  }
751
+ if (contentIndex === void 0) return false;
723
752
  block.thinking += text;
724
753
  block.thinkingSignature = "reasoning_content";
725
754
  eventSink.push({
@@ -751,6 +780,7 @@ function createAnthropicMessagesTransportStreamFn() {
751
780
  partial: output
752
781
  });
753
782
  }
783
+ if (contentIndex === void 0) return false;
754
784
  block.text += text;
755
785
  eventSink.push({
756
786
  type: "text_delta",
@@ -809,9 +839,11 @@ function createAnthropicMessagesTransportStreamFn() {
809
839
  if (event.type === "content_block_start") {
810
840
  const contentBlock = event.content_block;
811
841
  const index = typeof event.index === "number" ? event.index : -1;
842
+ if (transportOptions.anthropicServerCompaction === true && compactionCapture.begin(index, contentBlock, output.content.length)) continue;
812
843
  const fallbackBoundary = refusalBuffer ? readAnthropicFallbackBoundary(contentBlock) : null;
813
844
  if (fallbackBoundary) {
814
845
  refusalBuffer?.discard();
846
+ sealedToolCalls.length = 0;
815
847
  pendingTextEnds.length = 0;
816
848
  blockIndexes.clear();
817
849
  pendingThinkingSignatures.clear();
@@ -945,6 +977,7 @@ function createAnthropicMessagesTransportStreamFn() {
945
977
  if (event.type === "content_block_delta") {
946
978
  const delta = event.delta;
947
979
  const eventIndex = typeof event.index === "number" ? event.index : void 0;
980
+ if (eventIndex !== void 0 && compactionCapture.delta(eventIndex, delta)) continue;
948
981
  let index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
949
982
  let block = index === void 0 ? void 0 : blocks[index];
950
983
  if (allowReasoningContentReplay) {
@@ -953,7 +986,7 @@ function createAnthropicMessagesTransportStreamFn() {
953
986
  let appendedContent = false;
954
987
  if (!hasNativeAnthropicDelta && typeof delta?.content === "string" && delta.content.length > 0) {
955
988
  const text = sanitizeTransportPayloadText(delta.content);
956
- if (text.length > 0) if (block?.type === "text") {
989
+ if (text.length > 0) if (block?.type === "text" && index !== void 0) {
957
990
  block.text += text;
958
991
  eventSink.push({
959
992
  type: "text_delta",
@@ -981,6 +1014,7 @@ function createAnthropicMessagesTransportStreamFn() {
981
1014
  partial: output
982
1015
  });
983
1016
  }
1017
+ if (index === void 0) continue;
984
1018
  if (block?.type === "text" && delta?.type === "text_delta" && typeof delta.text === "string") {
985
1019
  block.text += delta.text;
986
1020
  eventSink.push({
@@ -1025,6 +1059,7 @@ function createAnthropicMessagesTransportStreamFn() {
1025
1059
  }
1026
1060
  if (event.type === "content_block_stop") {
1027
1061
  const eventIndex = typeof event.index === "number" ? event.index : void 0;
1062
+ if (eventIndex !== void 0 && compactionCapture.complete(eventIndex)) continue;
1028
1063
  const pendingSignature = eventIndex === void 0 ? void 0 : pendingThinkingSignatures.get(eventIndex);
1029
1064
  if (eventIndex !== void 0) pendingThinkingSignatures.delete(eventIndex);
1030
1065
  const index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
@@ -1057,12 +1092,9 @@ function createAnthropicMessagesTransportStreamFn() {
1057
1092
  continue;
1058
1093
  }
1059
1094
  if (block.type === "toolCall") {
1060
- delete block.partialJson;
1061
- eventSink.push({
1062
- type: "toolcall_end",
1063
- contentIndex: index,
1064
- toolCall: block,
1065
- partial: output
1095
+ sealedToolCalls.push({
1096
+ block,
1097
+ contentIndex: index
1066
1098
  });
1067
1099
  finishReasoningContentSidecars(event.index);
1068
1100
  }
@@ -1079,9 +1111,20 @@ function createAnthropicMessagesTransportStreamFn() {
1079
1111
  flushPendingTextEnds();
1080
1112
  }
1081
1113
  }
1082
- if (refusalBuffer && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
1114
+ if (isDirectAnthropicModel(model) && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
1083
1115
  if (transportOptions.signal?.aborted) throw transportAbortError(transportOptions.signal);
1084
1116
  if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
1117
+ if ([...blockIndexes.values()].some((index) => blocks[index]?.type === "toolCall")) throw new Error("Provider completed stream with an incomplete tool call");
1118
+ finalizeTerminalToolCallArguments(sealedToolCalls.map(({ block }) => block), (block) => block.partialJson && block.partialJson.length > 0 ? block.partialJson : block.arguments);
1119
+ for (const sealed of sealedToolCalls) {
1120
+ delete sealed.block.partialJson;
1121
+ eventSink.push({
1122
+ type: "toolcall_end",
1123
+ contentIndex: sealed.contentIndex,
1124
+ toolCall: sealed.block,
1125
+ partial: output
1126
+ });
1127
+ }
1085
1128
  refusalBuffer?.flush();
1086
1129
  if (output.stopReason === "toolUse" || output.content.some((block) => block.type === "toolCall")) tagPendingCommentaryText(output.content);
1087
1130
  flushPendingTextEnds();
@@ -1093,7 +1136,8 @@ function createAnthropicMessagesTransportStreamFn() {
1093
1136
  if (refusalBuffer) {
1094
1137
  refusalBuffer.discard();
1095
1138
  output.content = [];
1096
- }
1139
+ } else output.content = output.content.filter((block) => block.type !== "toolCall");
1140
+ if (usedCompactionReplay && isAnthropicReplayRejection(error)) suppressAnthropicCompaction(output, model, options);
1097
1141
  failTransportStream({
1098
1142
  stream,
1099
1143
  output,
@@ -1210,15 +1254,11 @@ const MAX_TOKENS_PARAM_KEYS = [
1210
1254
  "max_completion_tokens",
1211
1255
  "max_tokens"
1212
1256
  ];
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
1257
  /** Resolve the first supported max-token parameter present in a params object. */
1218
1258
  function resolveMaxTokensParam(params) {
1219
1259
  if (!params) return;
1220
1260
  for (const key of MAX_TOKENS_PARAM_KEYS) {
1221
- const resolved = resolveNonNegativeMaxTokensParam(params[key]);
1261
+ const resolved = asNonNegativeFiniteNumber(params[key]);
1222
1262
  if (resolved !== void 0) return resolved;
1223
1263
  }
1224
1264
  }
@@ -1335,6 +1375,172 @@ function stripCompletionMessagesToRoleContent(messages) {
1335
1375
  });
1336
1376
  }
1337
1377
  //#endregion
1378
+ //#region packages/ai/src/transports/openai-completions-host.ts
1379
+ /**
1380
+ * Chat Completions accepts Azure AI Foundry hosts in addition to traditional
1381
+ * Azure OpenAI hosts. Do not replace this with isTraditionalAzureOpenAIHost,
1382
+ * which intentionally excludes the .services.ai.azure.com Foundry suffix.
1383
+ */
1384
+ function isAzureOpenAICompatibleHost(hostname) {
1385
+ return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
1386
+ }
1387
+ //#endregion
1388
+ //#region packages/ai/src/transports/openai-completions-replay.ts
1389
+ function isGoogleOpenAICompatModel(model) {
1390
+ const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
1391
+ return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
1392
+ }
1393
+ function requiresGoogleCompatToolCallThoughtSignature(model) {
1394
+ return isGoogleGemini3ProModel(model.id) || isGoogleGemini3FlashModel(model.id);
1395
+ }
1396
+ const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
1397
+ const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
1398
+ function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
1399
+ return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
1400
+ }
1401
+ function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
1402
+ if (!isGoogleOpenAICompatModel(model)) return;
1403
+ const sigById = /* @__PURE__ */ new Map();
1404
+ const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
1405
+ for (const msg of context.messages ?? []) {
1406
+ if (msg.role !== "assistant") continue;
1407
+ const source = msg;
1408
+ if (!Array.isArray(source.content)) continue;
1409
+ for (const block of source.content) {
1410
+ if (block.type !== "toolCall") continue;
1411
+ const id = block.id;
1412
+ const sig = block.thoughtSignature;
1413
+ if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
1414
+ const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
1415
+ if (!isSameRoute && !fallbackSig) continue;
1416
+ sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
1417
+ }
1418
+ }
1419
+ }
1420
+ if (sigById.size === 0 && !fallbackSig) return;
1421
+ for (const message of outgoingMessages) {
1422
+ const toolCalls = message.tool_calls;
1423
+ if (!Array.isArray(toolCalls)) continue;
1424
+ for (const toolCall of toolCalls) {
1425
+ const id = toolCall.id;
1426
+ if (typeof id !== "string") continue;
1427
+ let sig = sigById.get(id) ?? fallbackSig;
1428
+ if (typeof sig === "string" && sig.length > 0) {
1429
+ if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
1430
+ }
1431
+ if (typeof sig !== "string" || sig.length === 0) continue;
1432
+ const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
1433
+ toolCall.extra_content = extra;
1434
+ const google = extra.google && typeof extra.google === "object" ? extra.google : {};
1435
+ extra.google = google;
1436
+ google.thought_signature = sig;
1437
+ }
1438
+ }
1439
+ }
1440
+ const COMPLETIONS_REASONING_REPLAY_FIELDS = [
1441
+ "reasoning_details",
1442
+ "reasoning_content",
1443
+ "reasoning",
1444
+ "reasoning_text"
1445
+ ];
1446
+ function stripCompletionsReasoningReplayFields(record) {
1447
+ for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
1448
+ }
1449
+ function sanitizeOpenRouterReasoningReplayFields(record) {
1450
+ const reasoningDetails = record.reasoning_details;
1451
+ if (typeof reasoningDetails === "string") {
1452
+ if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
1453
+ delete record.reasoning_details;
1454
+ } else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
1455
+ if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
1456
+ if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
1457
+ const reasoningText = record.reasoning_text;
1458
+ if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
1459
+ if ("reasoning_text" in record) delete record.reasoning_text;
1460
+ }
1461
+ function sanitizeReasoningContentReplayFields(record) {
1462
+ if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
1463
+ delete record.reasoning_details;
1464
+ delete record.reasoning;
1465
+ delete record.reasoning_text;
1466
+ }
1467
+ const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
1468
+ "deepseek-v4-flash",
1469
+ "deepseek-v4-pro",
1470
+ "kimi-for-coding",
1471
+ "kimi-k2.5",
1472
+ "kimi-k2.6",
1473
+ "kimi-k2.7-code",
1474
+ "kimi-k2.7-code-highspeed",
1475
+ "kimi-k3",
1476
+ "kimi-k2-thinking",
1477
+ "kimi-k2-thinking-turbo",
1478
+ "mimo-v2-pro",
1479
+ "mimo-v2-omni",
1480
+ "mimo-v2.5",
1481
+ "mimo-v2.5-pro",
1482
+ "mimo-v2.6-pro"
1483
+ ]);
1484
+ const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
1485
+ "-free",
1486
+ "-paid",
1487
+ "-trial"
1488
+ ];
1489
+ function stripReasoningContentReplayTierSuffix(modelId) {
1490
+ for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
1491
+ return modelId;
1492
+ }
1493
+ function getReasoningContentReplayModelIdCandidates(modelId) {
1494
+ if (typeof modelId !== "string") return [];
1495
+ const normalized = modelId.trim().toLowerCase();
1496
+ if (!normalized) return [];
1497
+ const parts = normalized.split("/").filter(Boolean);
1498
+ const finalPart = parts[parts.length - 1] ?? normalized;
1499
+ const candidates = [finalPart];
1500
+ const colonParts = finalPart.split(":").filter(Boolean);
1501
+ if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
1502
+ const baseCount = candidates.length;
1503
+ for (let index = 0; index < baseCount; index += 1) {
1504
+ const candidate = candidates[index];
1505
+ if (typeof candidate !== "string") continue;
1506
+ const stripped = stripReasoningContentReplayTierSuffix(candidate);
1507
+ if (stripped !== candidate) candidates.push(stripped);
1508
+ }
1509
+ return uniqueStrings(candidates.filter(Boolean));
1510
+ }
1511
+ function shouldPreserveReasoningContentReplay(model, compat) {
1512
+ if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
1513
+ return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
1514
+ }
1515
+ function shouldPreserveOpenRouterReasoningReplay(model) {
1516
+ if (model.provider !== "openrouter") return true;
1517
+ const normalizedModelId = model.id.trim().toLowerCase();
1518
+ return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
1519
+ }
1520
+ function shouldTrustReasoningContentReplayMetadata(model) {
1521
+ if (!model.reasoning) return false;
1522
+ if (model.provider.trim().toLowerCase() === "openai") return false;
1523
+ return shouldPreserveOpenRouterReasoningReplay(model);
1524
+ }
1525
+ function sanitizeCompletionsReasoningReplayFields(messages, options) {
1526
+ if (!Array.isArray(messages)) return;
1527
+ for (const msg of messages) {
1528
+ if (!msg || typeof msg !== "object") continue;
1529
+ const record = msg;
1530
+ if (record.role !== "assistant") continue;
1531
+ if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
1532
+ else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
1533
+ else stripCompletionsReasoningReplayFields(record);
1534
+ }
1535
+ }
1536
+ function applyCompletionsReplay(outgoingMessages, context, model, compat) {
1537
+ injectToolCallThoughtSignatures(outgoingMessages, context, model);
1538
+ sanitizeCompletionsReasoningReplayFields(outgoingMessages, {
1539
+ preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
1540
+ preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
1541
+ });
1542
+ }
1543
+ //#endregion
1338
1544
  //#region packages/ai/src/transports/openai-transport-params.ts
1339
1545
  const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
1340
1546
  const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
@@ -1355,13 +1561,51 @@ function readCodeModePayloadToolName(tool) {
1355
1561
  const fnName = readToolPayloadField(fn, "name");
1356
1562
  return typeof fnName === "string" ? fnName : void 0;
1357
1563
  }
1358
- function filterCodeModePayloadTools(payload, visibleToolNames) {
1564
+ function readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes) {
1565
+ if (!isRecord(tool)) return;
1566
+ const type = readToolPayloadField(tool, "type");
1567
+ if (typeof type === "string" && allowedHostedToolTypes?.has(type)) {
1568
+ try {
1569
+ if (Object.hasOwn(tool, "name") || Object.hasOwn(tool, "function") || Object.hasOwn(tool, "functionDeclarations") || Object.hasOwn(tool, "function_declarations")) return false;
1570
+ } catch {
1571
+ return false;
1572
+ }
1573
+ return `hosted:${type}`;
1574
+ }
1575
+ const name = readCodeModePayloadToolName(tool);
1576
+ return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames) ? `client:${name}` : void 0;
1577
+ }
1578
+ function readCodeModePayloadToolIdentities(payload) {
1579
+ if (!isRecord(payload)) return [];
1580
+ const tools = readToolPayloadField(payload, "tools");
1581
+ if (!Array.isArray(tools)) return [];
1582
+ return tools.flatMap((tool) => {
1583
+ if (!isRecord(tool)) return [];
1584
+ const identities = [];
1585
+ const name = readCodeModePayloadToolName(tool);
1586
+ if (name) identities.push(`client:${name}`);
1587
+ for (const key of ["functionDeclarations", "function_declarations"]) {
1588
+ const declarations = readToolPayloadField(tool, key);
1589
+ if (!Array.isArray(declarations)) continue;
1590
+ for (const declaration of declarations) {
1591
+ const declarationName = readCodeModePayloadToolName(declaration);
1592
+ if (declarationName) identities.push(`client:${declarationName}`);
1593
+ }
1594
+ }
1595
+ const type = readToolPayloadField(tool, "type");
1596
+ if (typeof type === "string" && type !== "function") identities.push(`hosted:${type}`);
1597
+ return identities;
1598
+ });
1599
+ }
1600
+ function filterCodeModePayloadTools(payload, visibleToolNames, allowedHostedToolTypes, observer) {
1359
1601
  if (!isRecord(payload)) return;
1360
1602
  const tools = readToolPayloadField(payload, "tools");
1361
1603
  if (!Array.isArray(tools)) return;
1604
+ const beforeToolIdentities = observer ? readCodeModePayloadToolIdentities(payload) : void 0;
1362
1605
  payload.tools = tools.flatMap((tool) => {
1363
- const name = readCodeModePayloadToolName(tool);
1364
- if (typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames)) return [tool];
1606
+ const identity = readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes);
1607
+ if (identity) return [tool];
1608
+ if (identity === false) return [];
1365
1609
  if (!isRecord(tool)) return [];
1366
1610
  const filteredGroups = {};
1367
1611
  for (const key of ["functionDeclarations", "function_declarations"]) {
@@ -1375,24 +1619,31 @@ function filterCodeModePayloadTools(payload, visibleToolNames) {
1375
1619
  }
1376
1620
  return Object.keys(filteredGroups).length > 0 ? [filteredGroups] : [];
1377
1621
  });
1622
+ if (beforeToolIdentities) observer?.({
1623
+ beforeToolIdentities,
1624
+ afterToolIdentities: readCodeModePayloadToolIdentities(payload)
1625
+ });
1378
1626
  }
1379
1627
  function resolveCodeModeResponsesVisibleToolNames(context) {
1380
1628
  return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
1381
1629
  }
1382
- function enforceCodeModeResponsesToolSurface(payload, visibleToolNames) {
1630
+ function enforceCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes, observer) {
1383
1631
  if (!isRecord(payload)) return;
1384
1632
  const tools = readToolPayloadField(payload, "tools");
1385
1633
  if (!Array.isArray(tools)) return;
1386
- payload.tools = tools.filter((tool) => {
1387
- const name = readCodeModePayloadToolName(tool);
1388
- return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames);
1634
+ const beforeToolIdentities = observer ? readCodeModePayloadToolIdentities(payload) : void 0;
1635
+ payload.tools = tools.filter((tool) => Boolean(readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes)));
1636
+ if (beforeToolIdentities) observer?.({
1637
+ beforeToolIdentities,
1638
+ afterToolIdentities: readCodeModePayloadToolIdentities(payload)
1389
1639
  });
1390
1640
  }
1391
- function assertCodeModeResponsesToolSurface(payload, visibleToolNames) {
1641
+ function assertCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
1392
1642
  const tools = isRecord(payload) ? readToolPayloadField(payload, "tools") : void 0;
1393
1643
  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;
1644
+ const identities = tools.map((tool) => readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes));
1645
+ const names = identities.flatMap((identity) => typeof identity === "string" && identity.startsWith("client:") ? [identity.slice(7)] : []).toSorted((left, right) => left.localeCompare(right));
1646
+ if (names.length >= 2 && identities.every((identity) => typeof identity === "string") && new Set(identities).size === identities.length && names.includes("exec") && names.includes("wait")) return;
1396
1647
  throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
1397
1648
  }
1398
1649
  function buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context) {
@@ -1469,7 +1720,7 @@ function buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, se
1469
1720
  ...optionHeaders,
1470
1721
  ...turnHeaders
1471
1722
  };
1472
- const resolvedHeaders = resolveProviderRequestPolicyConfig({
1723
+ const resolvedHeaders = resolveProviderRequestPolicyConfig(model, {
1473
1724
  provider: model.provider,
1474
1725
  api: model.api,
1475
1726
  baseUrl: model.baseUrl,
@@ -1514,67 +1765,10 @@ function getCompat(model) {
1514
1765
  };
1515
1766
  }
1516
1767
  //#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
- }
1768
+ //#region packages/ai/src/transports/openai-completions-params.ts
1575
1769
  function isKnownOpenAICompletionsEndpoint(model) {
1576
1770
  if (!model.baseUrl.trim()) return true;
1577
- const endpointClass = resolveProviderEndpoint(model.baseUrl).endpointClass;
1771
+ const endpointClass = resolveProviderEndpoint(model).endpointClass;
1578
1772
  if (endpointClass === "openai-public" || endpointClass === "azure-openai") return true;
1579
1773
  try {
1580
1774
  return isAzureOpenAICompatibleHost(new URL(model.baseUrl).hostname.toLowerCase());
@@ -1582,523 +1776,265 @@ function isKnownOpenAICompletionsEndpoint(model) {
1582
1776
  return false;
1583
1777
  }
1584
1778
  }
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
- }
1779
+ function resolveOpenAICompletionsReasoningEffort$1(options) {
1780
+ return options?.reasoningEffort ?? options?.reasoning ?? "high";
1781
+ }
1782
+ function resolveOpenAICompletionsMaxTokens(model, options) {
1783
+ if (options?.maxTokens) return {
1784
+ maxTokens: options.maxTokens,
1785
+ clampToModelMaxTokens: true
1786
+ };
1787
+ const paramsMaxTokens = resolveMaxTokensParam(model.params);
1788
+ if (paramsMaxTokens) return {
1789
+ maxTokens: paramsMaxTokens,
1790
+ clampToModelMaxTokens: false
1791
+ };
1607
1792
  return {
1608
- baseURL,
1609
- defaultHeaders: headers,
1610
- defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
1793
+ maxTokens: model.maxTokens,
1794
+ clampToModelMaxTokens: false
1611
1795
  };
1612
1796
  }
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
- };
1797
+ function resolveOpenAICompletionsModelMaxTokens(model) {
1798
+ return typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
1715
1799
  }
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;
1800
+ const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
1801
+ const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
1802
+ function estimateOpenAICompletionsInputTokens(payload) {
1803
+ let adjustedChars = 0;
1804
+ adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
1805
+ if (Array.isArray(payload.tools) && payload.tools.length > 0) try {
1806
+ adjustedChars += estimateStringChars(JSON.stringify(payload.tools));
1807
+ } catch {
1808
+ adjustedChars += 1024;
1809
+ }
1810
+ if (payload.response_format !== void 0) try {
1811
+ adjustedChars += estimateStringChars(JSON.stringify(payload.response_format));
1812
+ } catch {
1813
+ adjustedChars += 256;
1814
+ }
1815
+ return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
1816
+ }
1817
+ function estimateOpenAICompletionsMessagesChars(messages) {
1818
+ if (!Array.isArray(messages)) return 0;
1819
+ let adjustedChars = 0;
1820
+ for (const message of messages) {
1821
+ if (!message || typeof message !== "object") continue;
1822
+ const record = message;
1823
+ adjustedChars += estimateOpenAICompletionsContentChars(record.content);
1824
+ for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
1825
+ if (record.tool_calls !== void 0) try {
1826
+ adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
1827
+ } catch {
1828
+ adjustedChars += 256;
1751
1829
  }
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;
1830
+ }
1831
+ return adjustedChars;
1832
+ }
1833
+ function estimateOpenAICompletionsContentChars(value) {
1834
+ if (typeof value === "string") return estimateStringChars(value);
1835
+ if (!Array.isArray(value)) return 0;
1836
+ let adjustedChars = 0;
1837
+ for (const block of value) {
1838
+ if (!block || typeof block !== "object") continue;
1839
+ const record = block;
1840
+ if (record.type === "image_url" || record.type === "input_image") {
1841
+ adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
1842
+ continue;
1759
1843
  }
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
- });
1844
+ const text = record.text;
1845
+ if (typeof text === "string") {
1846
+ adjustedChars += estimateStringChars(text);
1847
+ continue;
1775
1848
  }
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
- });
1849
+ try {
1850
+ adjustedChars += estimateStringChars(JSON.stringify(block));
1851
+ } catch {
1852
+ adjustedChars += 256;
1796
1853
  }
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);
1854
+ }
1855
+ return adjustedChars;
1856
+ }
1857
+ function resolveOpenAICompletionsEffectiveContextTokens(model) {
1858
+ const contextTokens = model.contextTokens;
1859
+ if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
1860
+ return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
1861
+ }
1862
+ function isQwenOpenAICompletionsThinkingFormat(format) {
1863
+ return format === "qwen" || format === "qwen-chat-template";
1864
+ }
1865
+ function setQwenChatTemplateThinking(params, enabled) {
1866
+ const existing = params.chat_template_kwargs;
1867
+ params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
1868
+ ...existing,
1869
+ enable_thinking: enabled
1870
+ } : { enable_thinking: enabled };
1871
+ }
1872
+ function applyQwenOpenAICompletionsThinkingParams(params) {
1873
+ if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
1874
+ const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
1875
+ if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
1876
+ else params.payload.enable_thinking = enabled;
1877
+ return true;
1878
+ }
1879
+ function applyTogetherOpenAICompletionsThinkingParams(params) {
1880
+ if (!params.modelReasoning || params.compatThinkingFormat !== "together") return;
1881
+ params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
1882
+ }
1883
+ function convertTools(tools, compat, model) {
1884
+ const projection = projectOpenAITools(tools);
1885
+ const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
1886
+ transport: "stream",
1887
+ supportsStrictMode: compat?.supportsStrictMode
1888
+ }), {
1889
+ transport: "completions",
1890
+ model
1891
+ });
1892
+ return {
1893
+ projection,
1894
+ tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
1895
+ const functionTool = {
1896
+ name: tool.name,
1897
+ description: tool.description,
1898
+ parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
1899
+ };
1900
+ if (strict !== void 0) functionTool.strict = strict;
1901
+ return {
1902
+ type: "function",
1903
+ function: functionTool
1904
+ };
1905
+ })
1821
1906
  };
1822
- const appendVisibleTextDelta = (text) => {
1823
- if (!text) return;
1824
- if (currentBlock?.type === "toolCall") queuePostToolCallDelta({
1825
- kind: "text",
1826
- text
1827
- });
1828
- else appendTextDelta(text);
1907
+ }
1908
+ function buildOpenAICompletionsParams(model, context, options) {
1909
+ const compat = getCompat(model);
1910
+ const compatDetection = detectOpenAICompletionsCompat(model);
1911
+ let messages = convertMessages(model, context.systemPrompt ? {
1912
+ ...context,
1913
+ systemPrompt: stripSystemPromptCacheBoundary(context.systemPrompt)
1914
+ } : context, compat);
1915
+ applyCompletionsReplay(messages, context, model, compat);
1916
+ if (compat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
1917
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention);
1918
+ const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
1919
+ const params = {
1920
+ model: model.id,
1921
+ messages: compat.requiresStringContent ? flattenCompletionMessagesToStringContent(messages) : messages,
1922
+ stream: true
1829
1923
  };
1830
- const appendRecoveredToolCall = (toolCall) => {
1831
- if (currentBlock?.type === "toolCall") {
1832
- currentBlock = null;
1833
- flushPendingPostToolCallDeltas();
1924
+ if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
1925
+ if (compat.supportsStore) params.store = false;
1926
+ if (compat.supportsPromptCacheKey && promptCacheKey) {
1927
+ params.prompt_cache_key = promptCacheKey;
1928
+ if (cacheRetention === "long" && compat.supportsLongCacheRetention) params.prompt_cache_retention = "24h";
1929
+ }
1930
+ if (options?.temperature !== void 0) params.temperature = options.temperature;
1931
+ if (options?.topP !== void 0) params.top_p = options.topP;
1932
+ const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
1933
+ provider: model.provider,
1934
+ baseUrl: model.baseUrl,
1935
+ hasTools: () => Boolean(context.tools?.length)
1936
+ }) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
1937
+ if (responseFormat !== void 0) params.response_format = responseFormat;
1938
+ if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
1939
+ if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
1940
+ if (options?.seed !== void 0) params.seed = options.seed;
1941
+ if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
1942
+ if (supportsModelTools(model)) {
1943
+ if (context.tools) {
1944
+ const converted = convertTools(context.tools, compat, model);
1945
+ if (converted.tools.length > 0 || converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
1946
+ else if (hasToolCallHistory(context.messages)) params.tools = [];
1947
+ if (options?.toolChoice) {
1948
+ const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
1949
+ if (toolChoice !== void 0) params.tool_choice = toolChoice;
1950
+ } else if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
1951
+ } else if (hasToolCallHistory(context.messages)) params.tools = [];
1952
+ if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
1953
+ delete params.tools;
1954
+ delete params.tool_choice;
1834
1955
  }
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);
1956
+ }
1957
+ {
1958
+ const maxTokenBudget = resolveOpenAICompletionsMaxTokens(model, options);
1959
+ const effectiveMaxTokens = maxTokenBudget.maxTokens;
1960
+ const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
1961
+ let clampedMaxTokens = effectiveMaxTokens;
1962
+ const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
1963
+ if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
1964
+ clampedMaxTokens = modelMaxTokens;
1965
+ emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
1870
1966
  }
1871
- };
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;
1967
+ if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
1968
+ const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
1969
+ const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
1970
+ if (clampedMaxTokens > remainingBudget) {
1971
+ clampedMaxTokens = remainingBudget;
1972
+ emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
1879
1973
  }
1880
- const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
1881
- for (const part of parts) appendVisibleTextDelta(part);
1882
1974
  }
1975
+ if (clampedMaxTokens) if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
1976
+ else params.max_completion_tokens = clampedMaxTokens;
1977
+ }
1978
+ const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort$1(options);
1979
+ const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
1980
+ model,
1981
+ effort: completionsReasoningEffort,
1982
+ fallbackMap: compat.reasoningEffortMap
1983
+ }) : void 0;
1984
+ const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
1985
+ const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
1986
+ const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
1987
+ compatThinkingFormat: compat.thinkingFormat,
1988
+ modelReasoning: model.reasoning,
1989
+ payload: params,
1990
+ requestedEffort: completionsReasoningEffort
1991
+ });
1992
+ applyTogetherOpenAICompletionsThinkingParams({
1993
+ compatThinkingFormat: compat.thinkingFormat,
1994
+ modelReasoning: model.reasoning,
1995
+ payload: params,
1996
+ requestedEffort: completionsReasoningEffort
1997
+ });
1998
+ if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
1999
+ else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
2000
+ else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
2001
+ return params;
2002
+ }
2003
+ //#endregion
2004
+ //#region packages/ai/src/transports/openai-completions-dsml.ts
2005
+ const DEEPSEEK_DSML_BARS = ["|", "|"];
2006
+ const DEEPSEEK_DSML_TOOL_KINDS = [
2007
+ "tool_calls",
2008
+ "tool_call",
2009
+ "function_calls"
2010
+ ];
2011
+ const DEEPSEEK_DSML_TOOL_OPEN_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `<${bar}DSML${bar}${kind}>`));
2012
+ const DEEPSEEK_DSML_TOOL_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `</${bar}DSML${bar}${kind}>`));
2013
+ const DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES = DEEPSEEK_DSML_BARS.map((bar) => `<${bar}DSML${bar}invoke`);
2014
+ const DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.map((bar) => `</${bar}DSML${bar}invoke>`);
2015
+ const DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length));
2016
+ 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));
2017
+ const MAX_DSML_RECOVERY_BUFFER_BYTES = 256e3;
2018
+ const DEEPSEEK_DSML_SCAN_BATCH_CHARS = 64 * 1024;
2019
+ function createDsmlRecoverer() {
2020
+ let buffer = "";
2021
+ let bufferBytes = 0;
2022
+ let bufferEndsWithHighSurrogate = false;
2023
+ let pendingScanChars = 0;
2024
+ let activeOpenToken = null;
2025
+ let blockScanState = {
2026
+ offset: 0,
2027
+ mode: "outer",
2028
+ invokeOpenStart: -1
1883
2029
  };
1884
- const flushDeepSeekTextFilterAtEnd = () => {
1885
- const parts = deepSeekTextFilter?.flush();
1886
- if (!parts) return;
1887
- for (const part of parts) appendVisibleTextDelta(part);
1888
- };
1889
- const appendRoutedContentDelta = (delta) => {
1890
- if (delta.kind === "text") {
1891
- appendFilteredVisibleTextDelta(delta.text);
1892
- return;
1893
- }
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();
1927
- continue;
1928
- }
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();
1941
- continue;
1942
- }
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;
1959
- }
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
- }
1976
- };
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
- }
2042
- }
2043
- flushPendingPostToolCallDeltas();
2044
- emitReasoningUsageActivity(hasReasoningUsageActivity);
2045
- await cooperativeScheduler.afterEvent();
2046
- }
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
- });
2061
- }
2062
- });
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";
2068
- }
2069
- const DEEPSEEK_DSML_BARS = ["|", "|"];
2070
- const DEEPSEEK_DSML_TOOL_KINDS = [
2071
- "tool_calls",
2072
- "tool_call",
2073
- "function_calls"
2074
- ];
2075
- const DEEPSEEK_DSML_TOOL_OPEN_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `<${bar}DSML${bar}${kind}>`));
2076
- const DEEPSEEK_DSML_TOOL_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `</${bar}DSML${bar}${kind}>`));
2077
- const DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES = DEEPSEEK_DSML_BARS.map((bar) => `<${bar}DSML${bar}invoke`);
2078
- const DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.map((bar) => `</${bar}DSML${bar}invoke>`);
2079
- const DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length));
2080
- 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
- const MAX_DSML_RECOVERY_BUFFER_BYTES = 256e3;
2082
- const DEEPSEEK_DSML_SCAN_BATCH_CHARS = 64 * 1024;
2083
- function createDeepSeekDsmlToolCallRecoverer() {
2084
- let buffer = "";
2085
- let bufferBytes = 0;
2086
- let bufferEndsWithHighSurrogate = false;
2087
- let pendingScanChars = 0;
2088
- let activeOpenToken = null;
2089
- let blockScanState = {
2090
- offset: 0,
2091
- mode: "outer",
2092
- invokeOpenStart: -1
2093
- };
2094
- const resetBlockScan = () => {
2095
- activeOpenToken = null;
2096
- pendingScanChars = 0;
2097
- blockScanState = {
2098
- offset: 0,
2099
- mode: "outer",
2100
- invokeOpenStart: -1
2101
- };
2030
+ const resetBlockScan = () => {
2031
+ activeOpenToken = null;
2032
+ pendingScanChars = 0;
2033
+ blockScanState = {
2034
+ offset: 0,
2035
+ mode: "outer",
2036
+ invokeOpenStart: -1
2037
+ };
2102
2038
  };
2103
2039
  const consume = (final) => {
2104
2040
  const output = [];
@@ -2180,7 +2116,7 @@ function createDeepSeekDsmlToolCallRecoverer() {
2180
2116
  };
2181
2117
  return {
2182
2118
  push(chunk) {
2183
- const append = utf8ByteLengthForAppend(bufferEndsWithHighSurrogate, chunk);
2119
+ const append = measureUtf8AppendBytes(bufferEndsWithHighSurrogate, chunk);
2184
2120
  bufferBytes += append.bytes;
2185
2121
  bufferEndsWithHighSurrogate = append.endsWithHighSurrogate;
2186
2122
  buffer += chunk;
@@ -2336,20 +2272,6 @@ function scanDeepSeekDsmlToolBlock(text, closeToken, contentStartIndex, state) {
2336
2272
  }
2337
2273
  return { kind: "incomplete" };
2338
2274
  }
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
2275
  function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
2354
2276
  const maxLength = Math.min(text.length, DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN - 1);
2355
2277
  for (let length = maxLength; length > 0; length -= 1) {
@@ -2358,17 +2280,61 @@ function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
2358
2280
  }
2359
2281
  return 0;
2360
2282
  }
2361
- function getCompletionsReasoningDeltas(delta, visibleReasoningDetailTypes) {
2362
- const output = [];
2363
- const pushDelta = (next) => {
2364
- const previous = output[output.length - 1];
2365
- if (!previous || previous.kind !== next.kind) {
2366
- output.push(next);
2283
+ //#endregion
2284
+ //#region packages/ai/src/transports/openai-completions-stream.ts
2285
+ function extractToolCallThoughtSignature(toolCall) {
2286
+ const tc = toolCall;
2287
+ if (!tc) return;
2288
+ const fromExtra = (tc.extra_content?.google)?.thought_signature;
2289
+ if (typeof fromExtra === "string" && fromExtra.length > 0) return fromExtra;
2290
+ const fromFunction = tc.function?.thought_signature;
2291
+ if (typeof fromFunction === "string" && fromFunction.length > 0) return fromFunction;
2292
+ const fromToolCall = tc.thought_signature;
2293
+ return typeof fromToolCall === "string" && fromToolCall.length > 0 ? fromToolCall : void 0;
2294
+ }
2295
+ async function processCompletionsStream(responseStream, output, model, stream, options) {
2296
+ const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
2297
+ const emitReasoning = options?.emitReasoning ?? true;
2298
+ const compat = getCompat(model);
2299
+ const visibleReasoningDetailTypes = new Set(compat.visibleReasoningDetailTypes);
2300
+ const shouldFilterDeepSeekDsmlText = compat.thinkingFormat === "deepseek";
2301
+ const deepSeekTextFilter = shouldFilterDeepSeekDsmlText ? createDeepSeekTextFilter() : null;
2302
+ const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText ? createDsmlRecoverer() : null;
2303
+ const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
2304
+ let currentBlock = null;
2305
+ let currentTextSource;
2306
+ let pendingInterruptedTextBlock = null;
2307
+ let confirmedInterruptedTextBlock = null;
2308
+ let pendingPostToolCallDeltas = [];
2309
+ let pendingPostToolCallBytes = 0;
2310
+ let isFlushingPendingPostToolCallDeltas = false;
2311
+ const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
2312
+ const toolCallBlocksById = /* @__PURE__ */ new Map();
2313
+ const provisionalCommentaryTags = /* @__PURE__ */ new Map();
2314
+ const toolCallBlockIndices = /* @__PURE__ */ new WeakMap();
2315
+ let explicitVisibleTextBlocks;
2316
+ const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
2317
+ let sawStopFinishReason = false;
2318
+ let sawNativeToolCallDelta = false;
2319
+ const blockIndex = () => output.content.length - 1;
2320
+ const measureUtf8Bytes = (text) => Buffer.byteLength(text, "utf8");
2321
+ let chunkPushedEvent = false;
2322
+ const pushStreamEvent = (event) => {
2323
+ chunkPushedEvent = true;
2324
+ stream.push(event);
2325
+ };
2326
+ const queuePostToolCallDelta = (next) => {
2327
+ const nextBytes = measureUtf8Bytes(next.text);
2328
+ if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
2329
+ pendingPostToolCallBytes += nextBytes;
2330
+ const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
2331
+ if (!previous || previous.kind !== next.kind || previous.kind === "text" && next.kind === "text" && previous.source !== next.source) {
2332
+ pendingPostToolCallDeltas.push(next);
2367
2333
  return;
2368
2334
  }
2369
2335
  if (next.kind === "thinking" && previous.kind === "thinking") {
2370
2336
  if (previous.signature !== next.signature) {
2371
- output.push(next);
2337
+ pendingPostToolCallDeltas.push(next);
2372
2338
  return;
2373
2339
  }
2374
2340
  previous.text += next.text;
@@ -2376,646 +2342,701 @@ function getCompletionsReasoningDeltas(delta, visibleReasoningDetailTypes) {
2376
2342
  }
2377
2343
  previous.text += next.text;
2378
2344
  };
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
- });
2393
- continue;
2394
- }
2395
- if (typeof detail.type === "string" && visibleTypes.has(detail.type)) pushDelta({
2396
- kind: "text",
2397
- text: detail.text
2345
+ const appendThinkingDeltaInternal = (reasoningDelta) => {
2346
+ if (!currentBlock || currentBlock.type !== "thinking") {
2347
+ currentBlock = {
2348
+ type: "thinking",
2349
+ thinking: "",
2350
+ ...reasoningDelta.signature ? { thinkingSignature: reasoningDelta.signature } : {}
2351
+ };
2352
+ output.content.push(currentBlock);
2353
+ pushStreamEvent({
2354
+ type: "thinking_start",
2355
+ contentIndex: blockIndex(),
2356
+ partial: output
2398
2357
  });
2399
2358
  }
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
2359
+ currentBlock.thinking += reasoningDelta.text;
2360
+ pushStreamEvent({
2361
+ type: "thinking_delta",
2362
+ contentIndex: blockIndex(),
2363
+ delta: reasoningDelta.text,
2364
+ partial: output
2365
+ });
2366
+ };
2367
+ const appendTextDeltaInternal = (text, source) => {
2368
+ if (currentBlock?.type === "text" && currentTextSource !== source) currentBlock = null;
2369
+ if (!currentBlock || currentBlock.type !== "text") {
2370
+ currentBlock = {
2371
+ type: "text",
2372
+ text: ""
2373
+ };
2374
+ currentTextSource = source;
2375
+ if (source === "reasoning_detail") (explicitVisibleTextBlocks ??= /* @__PURE__ */ new Set()).add(currentBlock);
2376
+ output.content.push(currentBlock);
2377
+ pushStreamEvent({
2378
+ type: "text_start",
2379
+ contentIndex: blockIndex(),
2380
+ partial: output
2412
2381
  });
2413
- break;
2414
2382
  }
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
2383
+ currentBlock.text += text;
2384
+ if (pendingInterruptedTextBlock && text.trim()) {
2385
+ confirmedInterruptedTextBlock = pendingInterruptedTextBlock;
2386
+ pendingInterruptedTextBlock = null;
2387
+ }
2388
+ pushStreamEvent({
2389
+ type: "text_delta",
2390
+ contentIndex: blockIndex(),
2391
+ delta: text
2392
+ });
2434
2393
  };
2435
- const paramsMaxTokens = resolveMaxTokensParam(model.params);
2436
- if (paramsMaxTokens) return {
2437
- maxTokens: paramsMaxTokens,
2438
- clampToModelMaxTokens: false
2394
+ const flushPendingPostToolCallDeltas = () => {
2395
+ if (isFlushingPendingPostToolCallDeltas || currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
2396
+ isFlushingPendingPostToolCallDeltas = true;
2397
+ const bufferedDeltas = pendingPostToolCallDeltas;
2398
+ pendingPostToolCallDeltas = [];
2399
+ pendingPostToolCallBytes = 0;
2400
+ for (const delta of bufferedDeltas) if (delta.kind === "text") appendTextDeltaInternal(delta.text, delta.source);
2401
+ else if (emitReasoning) appendThinkingDeltaInternal(delta);
2402
+ isFlushingPendingPostToolCallDeltas = false;
2439
2403
  };
2440
- return {
2441
- maxTokens: model.maxTokens,
2442
- clampToModelMaxTokens: false
2404
+ const appendThinkingDelta = (reasoningDelta) => {
2405
+ flushPendingPostToolCallDeltas();
2406
+ appendThinkingDeltaInternal(reasoningDelta);
2443
2407
  };
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;
2408
+ const appendTextDelta = (text, source) => {
2409
+ flushPendingPostToolCallDeltas();
2410
+ appendTextDeltaInternal(text, source);
2411
+ };
2412
+ const appendVisibleTextDelta = (text) => {
2413
+ if (!text) return;
2414
+ if (currentBlock?.type === "toolCall") queuePostToolCallDelta({
2415
+ kind: "text",
2416
+ text
2417
+ });
2418
+ else appendTextDelta(text);
2419
+ };
2420
+ const appendReasoningDeltas = (reasoningDeltas) => {
2421
+ for (const reasoningDelta of reasoningDeltas) {
2422
+ if (reasoningDelta.kind === "thinking" && !emitReasoning) continue;
2423
+ if (currentBlock?.type === "toolCall") {
2424
+ queuePostToolCallDelta({ ...reasoningDelta });
2425
+ continue;
2426
+ }
2427
+ if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text, reasoningDelta.source);
2428
+ else if (emitReasoning) appendThinkingDelta(reasoningDelta);
2477
2429
  }
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;
2430
+ };
2431
+ const appendRecoveredToolCall = (toolCall) => {
2432
+ if (currentBlock?.type === "toolCall") {
2433
+ currentBlock = null;
2434
+ flushPendingPostToolCallDeltas();
2435
+ }
2436
+ rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
2437
+ const block = {
2438
+ type: "toolCall",
2439
+ id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
2440
+ name: toolCall.name,
2441
+ arguments: toolCall.arguments,
2442
+ partialArgs: toolCall.partialArgs
2443
+ };
2444
+ currentBlock = block;
2445
+ output.content.push(block);
2446
+ toolCallBlockIndices.set(block, output.content.length - 1);
2447
+ pushStreamEvent({
2448
+ type: "toolcall_start",
2449
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2450
+ partial: output
2451
+ });
2452
+ pushStreamEvent({
2453
+ type: "toolcall_delta",
2454
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2455
+ delta: toolCall.partialArgs,
2456
+ partial: output
2457
+ });
2458
+ };
2459
+ const appendFilteredVisibleTextDelta = (text) => {
2460
+ const recoveredParts = deepSeekToolCallRecoverer?.push(text) ?? [{
2461
+ kind: "text",
2462
+ text
2463
+ }];
2464
+ for (const recoveredPart of recoveredParts) {
2465
+ if (recoveredPart.kind === "toolCall") {
2466
+ appendRecoveredToolCall(recoveredPart);
2467
+ continue;
2468
+ }
2469
+ const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
2470
+ for (const part of parts) appendVisibleTextDelta(part);
2471
+ }
2472
+ };
2473
+ const flushDeepSeekToolCallRecovererAtEnd = () => {
2474
+ const recoveredParts = deepSeekToolCallRecoverer?.flush();
2475
+ if (!recoveredParts) return;
2476
+ for (const recoveredPart of recoveredParts) {
2477
+ if (recoveredPart.kind === "toolCall") {
2478
+ appendRecoveredToolCall(recoveredPart);
2479
+ continue;
2480
+ }
2481
+ const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
2482
+ for (const part of parts) appendVisibleTextDelta(part);
2483
+ }
2484
+ };
2485
+ const flushDeepSeekTextFilterAtEnd = () => {
2486
+ const parts = deepSeekTextFilter?.flush();
2487
+ if (!parts) return;
2488
+ for (const part of parts) appendVisibleTextDelta(part);
2489
+ };
2490
+ const appendRoutedContentDelta = (delta) => {
2491
+ if (delta.kind === "text") {
2492
+ appendFilteredVisibleTextDelta(delta.text);
2493
+ return;
2494
+ }
2495
+ if (!emitReasoning) return;
2496
+ if (currentBlock?.type === "toolCall") queuePostToolCallDelta(delta);
2497
+ else appendThinkingDelta(delta);
2498
+ };
2499
+ const appendPartitionedVisibleDelta = (delta) => {
2500
+ if (delta.kind === "text") appendFilteredVisibleTextDelta(delta.text);
2501
+ };
2502
+ const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
2503
+ if (!hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
2504
+ const latestBlock = output.content[output.content.length - 1];
2505
+ if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
2506
+ if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
2507
+ appendThinkingDelta({ text: "" });
2508
+ };
2509
+ const flushReasoningTagTextPartitioner = () => {
2510
+ for (const delta of reasoningTagTextPartitioner.flush()) appendPartitionedVisibleDelta(delta);
2511
+ };
2512
+ const sealTextBeforeReasoning = () => {
2513
+ if (currentBlock?.type !== "text" && !reasoningTagTextPartitioner.hasPending()) return;
2514
+ flushReasoningTagTextPartitioner();
2515
+ if (currentBlock?.type !== "text") return;
2516
+ if (currentTextSource !== "reasoning_detail" && currentBlock.text.trim()) pendingInterruptedTextBlock = currentBlock;
2517
+ currentBlock = null;
2518
+ currentTextSource = void 0;
2519
+ };
2520
+ const beginReasoning = (hasFollowingVisibleText, forceStrict = false) => {
2521
+ if (!output.openclawDelivery?.textPhaseRequiresTerminal) output.openclawDelivery = {
2522
+ ...output.openclawDelivery,
2523
+ textPhaseRequiresTerminal: true
2524
+ };
2525
+ if (forceStrict || reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
2526
+ if (!hasFollowingVisibleText || !reasoningTagTextPartitioner.hasPendingSyntax()) sealTextBeforeReasoning();
2527
+ };
2528
+ const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
2529
+ const guardedStream = withFirstStreamEventTimeout(responseStream, {
2530
+ provider: model.provider,
2531
+ api: model.api,
2532
+ model: model.id,
2533
+ timeoutMs: options?.firstEventTimeoutMs ?? 0,
2534
+ stage: "completions",
2535
+ abort: options?.abortFirstEventStream,
2536
+ onTimeout: options?.onFirstEventTimeout,
2537
+ 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."
2538
+ });
2539
+ for await (const rawChunk of guardedStream) {
2540
+ throwIfModelStreamAborted(options?.signal);
2541
+ chunkPushedEvent = false;
2542
+ if (!rawChunk || typeof rawChunk !== "object") {
2543
+ await cooperativeScheduler.afterEvent();
2490
2544
  continue;
2491
2545
  }
2492
- const text = record.text;
2493
- if (typeof text === "string") {
2494
- adjustedChars += estimateStringChars(text);
2546
+ notifyLlmRequestActivity(options?.signal);
2547
+ const chunk = rawChunk;
2548
+ output.responseId ||= chunk.id;
2549
+ let hasReasoningUsageActivity = false;
2550
+ if (chunk.usage) {
2551
+ output.usage = parseOpenAICompletionsUsage(chunk.usage, model);
2552
+ hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(chunk.usage);
2553
+ }
2554
+ const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
2555
+ if (!choice) {
2556
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
2557
+ await cooperativeScheduler.afterEvent();
2495
2558
  continue;
2496
2559
  }
2497
- try {
2498
- adjustedChars += estimateStringChars(JSON.stringify(block));
2499
- } catch {
2500
- adjustedChars += 256;
2560
+ const choiceUsage = choice.usage;
2561
+ if (!chunk.usage && choiceUsage) {
2562
+ output.usage = parseOpenAICompletionsUsage(choiceUsage, model);
2563
+ hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
2501
2564
  }
2502
- }
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
2540
- });
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;
2576
- }
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);
2593
- }
2565
+ if (choice.finish_reason) {
2566
+ const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
2567
+ output.stopReason = finishReasonResult.stopReason;
2568
+ if (finishReasonResult.stopReason === "stop") sawStopFinishReason = true;
2569
+ if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
2594
2570
  }
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;
2571
+ const rawChoiceDelta = choice.delta ?? choice.message;
2572
+ if (!rawChoiceDelta) {
2573
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
2574
+ await cooperativeScheduler.afterEvent();
2575
+ continue;
2576
+ }
2577
+ for (const normalizedDelta of normalizeToolCallDeltas(rawChoiceDelta, choice.finish_reason)) {
2578
+ const choiceDelta = normalizedDelta.delta;
2579
+ const reasoningBatch = readOpenAICompletionsReasoningBatch(choiceDelta, visibleReasoningDetailTypes);
2580
+ const reasoningDeltas = reasoningBatch.deltas;
2581
+ const hasReasoningThinking = reasoningBatch.hasThinking;
2582
+ const contentDeltas = readOpenAICompletionsContentDeltas(choiceDelta.content, choiceDelta.refusal, reasoningBatch.mirroredThinking);
2583
+ const lastVisibleTextIndex = contentDeltas.findLastIndex((delta) => delta.kind === "text");
2584
+ const hasSameChunkVisibleText = reasoningBatch.hasVisibleText || lastVisibleTextIndex !== -1;
2585
+ if (hasReasoningThinking) {
2586
+ beginReasoning(hasSameChunkVisibleText, true);
2587
+ appendReasoningDeltas(reasoningDeltas);
2588
+ }
2589
+ for (const [contentDeltaIndex, contentDelta] of contentDeltas.entries()) if (contentDelta.kind === "text") {
2590
+ const routedDeltas = hasReasoningThinking ? reasoningTagTextPartitioner.push(contentDelta.text) : reasoningTagTextPartitioner.pushVisible(contentDelta.text);
2591
+ for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
2592
+ } else {
2593
+ beginReasoning(contentDeltaIndex < lastVisibleTextIndex);
2594
+ appendRoutedContentDelta(contentDelta);
2595
+ }
2596
+ if (!hasReasoningThinking) appendReasoningDeltas(reasoningDeltas);
2597
+ const toolCallDeltas = normalizedDelta.toolCalls;
2598
+ if (toolCallDeltas.length > 0) {
2599
+ sawNativeToolCallDelta = true;
2600
+ flushReasoningTagTextPartitioner();
2601
+ rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
2602
+ for (const toolCall of toolCallDeltas) {
2603
+ const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
2604
+ let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
2605
+ if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
2606
+ if (!block) {
2607
+ if (currentBlock?.type === "toolCall") {
2608
+ currentBlock = null;
2609
+ flushPendingPostToolCallDeltas();
2610
+ }
2611
+ const initialSig = extractToolCallThoughtSignature(toolCall);
2612
+ block = {
2613
+ type: "toolCall",
2614
+ id: toolCall.id || "",
2615
+ name: toolCall.function?.name || "",
2616
+ arguments: {},
2617
+ partialArgs: "",
2618
+ ...initialSig ? { thoughtSignature: initialSig } : {}
2619
+ };
2620
+ output.content.push(block);
2621
+ toolCallBlockIndices.set(block, output.content.length - 1);
2622
+ pushStreamEvent({
2623
+ type: "toolcall_start",
2624
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2625
+ partial: output
2626
+ });
2627
+ }
2628
+ if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
2629
+ if (toolCall.id) {
2630
+ block.id = toolCall.id;
2631
+ toolCallBlocksById.set(toolCall.id, block);
2632
+ }
2633
+ currentBlock = block;
2634
+ if (toolCall.function?.name) block.name = toolCall.function.name;
2635
+ const deltaSig = extractToolCallThoughtSignature(toolCall);
2636
+ if (deltaSig) block.thoughtSignature = deltaSig;
2637
+ if (toolCall.function?.arguments) {
2638
+ block.partialArgs += toolCall.function.arguments;
2639
+ block.arguments = parseStreamingJson(block.partialArgs);
2640
+ pushStreamEvent({
2641
+ type: "toolcall_delta",
2642
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2643
+ delta: toolCall.function.arguments,
2644
+ partial: output
2645
+ });
2646
+ }
2647
+ }
2606
2648
  }
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;
2613
2649
  }
2650
+ flushPendingPostToolCallDeltas();
2651
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
2652
+ await cooperativeScheduler.afterEvent();
2614
2653
  }
2654
+ flushReasoningTagTextPartitioner();
2655
+ flushDeepSeekToolCallRecovererAtEnd();
2656
+ flushDeepSeekTextFilterAtEnd();
2657
+ currentBlock = null;
2658
+ flushPendingPostToolCallDeltas();
2659
+ finalizeOpenAICompletionsToolCalls(output, {
2660
+ allowSilentToolCallPromotion: sawStopFinishReason || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false),
2661
+ onConfirmedToolCall(block, contentIndex) {
2662
+ if (block.type !== "toolCall") return;
2663
+ pushStreamEvent({
2664
+ type: "toolcall_end",
2665
+ contentIndex,
2666
+ toolCall: block,
2667
+ partial: output
2668
+ });
2669
+ }
2670
+ });
2671
+ if (confirmedInterruptedTextBlock && output.stopReason !== "toolUse" && output.stopReason !== "error" && output.stopReason !== "aborted") tagInterruptedTextPhases(output.content, confirmedInterruptedTextBlock, explicitVisibleTextBlocks);
2672
+ if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
2673
+ if (output.stopReason === "error" || output.stopReason === "aborted") tagUnresolvedTextAsCommentary(output);
2674
+ if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
2615
2675
  }
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);
2684
- }
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));
2690
- }
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/"));
2676
+ function resolveOpenAICompletionsReasoningEffort(options) {
2677
+ return options?.reasoningEffort ?? options?.reasoning ?? "high";
2695
2678
  }
2696
- function shouldTrustReasoningContentReplayMetadata(model) {
2679
+ function shouldEmitOpenAICompletionsReasoning(model, options) {
2697
2680
  if (!model.reasoning) return false;
2698
- if (model.provider.trim().toLowerCase() === "openai") return false;
2699
- return shouldPreserveOpenRouterReasoningReplay(model);
2681
+ const effort = resolveOpenAICompletionsReasoningEffort(options);
2682
+ if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
2683
+ return true;
2700
2684
  }
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
- }
2685
+ function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
2686
+ const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
2687
+ return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
2711
2688
  }
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
2689
+ //#endregion
2690
+ //#region packages/ai/src/transports/openai-completions-transport.ts
2691
+ function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
2692
+ const messages = params.messages;
2693
+ if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
2694
+ 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.`);
2695
+ }
2696
+ const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
2697
+ const SSE_DONE_MAX_LINE_CHARS = 1024;
2698
+ function createSseDoneDetector() {
2699
+ const decoder = new TextDecoder();
2700
+ let line = "";
2701
+ let lineOverflowed = false;
2702
+ let sawDone = false;
2703
+ const finishLine = () => {
2704
+ if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
2705
+ line = "";
2706
+ lineOverflowed = false;
2731
2707
  };
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;
2708
+ const observeText = (text) => {
2709
+ for (const char of text) {
2710
+ if (char === "\n" || char === "\r") {
2711
+ finishLine();
2712
+ continue;
2713
+ }
2714
+ if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
2715
+ else lineOverflowed = true;
2763
2716
  }
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}`);
2717
+ };
2718
+ return {
2719
+ observe(chunk) {
2720
+ if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
2721
+ },
2722
+ finish() {
2723
+ if (sawDone) return;
2724
+ observeText(decoder.decode());
2725
+ if (line || lineOverflowed) finishLine();
2726
+ },
2727
+ sawDone: () => sawDone
2728
+ };
2729
+ }
2730
+ function createOpenAICompletionsClient(model, context, apiKey, optionHeaders, opts) {
2731
+ const clientConfig = buildOpenAICompletionsClientConfig(model, context, optionHeaders);
2732
+ return new OpenAI({
2733
+ apiKey,
2734
+ baseURL: clientConfig.baseURL,
2735
+ dangerouslyAllowBrowser: true,
2736
+ defaultHeaders: clientConfig.defaultHeaders,
2737
+ defaultQuery: clientConfig.defaultQuery,
2738
+ fetch: opts?.fetch ?? buildGuardedModelFetch(model),
2739
+ ...buildOpenAISdkClientOptions(model)
2740
+ });
2741
+ }
2742
+ function buildOpenAICompletionsClientConfig(model, context, optionHeaders) {
2743
+ const headers = buildOpenAIClientHeaders(model, context, optionHeaders);
2744
+ const defaultQuery = {};
2745
+ let baseURL = model.baseUrl;
2746
+ let isAzureHost = false;
2747
+ try {
2748
+ const parsed = new URL(model.baseUrl);
2749
+ isAzureHost = isAzureOpenAICompatibleHost(parsed.hostname.toLowerCase());
2750
+ parsed.searchParams.forEach((value, key) => {
2751
+ if (value) defaultQuery[key] = value;
2752
+ });
2753
+ parsed.search = "";
2754
+ baseURL = parsed.toString().replace(/\/$/, "");
2755
+ } catch {}
2756
+ if (isAzureHost) {
2757
+ const apiVersionHeader = Object.keys(headers).find((key) => key.toLowerCase() === "api-version");
2758
+ if (apiVersionHeader) {
2759
+ const apiVersion = headers[apiVersionHeader]?.trim();
2760
+ delete headers[apiVersionHeader];
2761
+ if (apiVersion && !defaultQuery["api-version"]) defaultQuery["api-version"] = apiVersion;
2774
2762
  }
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}`);
2763
+ }
2764
+ return {
2765
+ baseURL,
2766
+ defaultHeaders: headers,
2767
+ defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
2768
+ };
2769
+ }
2770
+ function createOpenAICompletionsTransportStreamFn() {
2771
+ return (model, context, options) => {
2772
+ const { eventStream, stream } = createWritableTransportEventStream();
2773
+ (async () => {
2774
+ const output = {
2775
+ role: "assistant",
2776
+ content: [],
2777
+ api: model.api,
2778
+ provider: model.provider,
2779
+ model: model.id,
2780
+ usage: {
2781
+ input: 0,
2782
+ output: 0,
2783
+ cacheRead: 0,
2784
+ cacheWrite: 0,
2785
+ totalTokens: 0,
2786
+ cost: {
2787
+ input: 0,
2788
+ output: 0,
2789
+ cacheRead: 0,
2790
+ cacheWrite: 0,
2791
+ total: 0
2792
+ }
2793
+ },
2794
+ stopReason: "stop",
2795
+ timestamp: Date.now()
2796
+ };
2797
+ let firstEventAbort;
2798
+ try {
2799
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
2800
+ const doneDetector = createSseDoneDetector();
2801
+ const baseFetch = buildGuardedModelFetch(model);
2802
+ const doneDetectingFetch = async (url, init) => {
2803
+ const response = await baseFetch(url, init);
2804
+ if (!response.body || !response.ok) return response;
2805
+ if (typeof TransformStream === "undefined" || !response.body.pipeThrough) return response;
2806
+ const transformed = response.body.pipeThrough(new TransformStream({
2807
+ transform(chunk, controller) {
2808
+ doneDetector.observe(chunk);
2809
+ controller.enqueue(chunk);
2810
+ },
2811
+ flush() {
2812
+ doneDetector.finish();
2813
+ }
2814
+ }));
2815
+ return new Response(transformed, {
2816
+ headers: response.headers,
2817
+ status: response.status,
2818
+ statusText: response.statusText
2819
+ });
2820
+ };
2821
+ const client = createOpenAICompletionsClient(model, context, apiKey, options?.headers, { fetch: doneDetectingFetch });
2822
+ let params = buildOpenAICompletionsParams(model, context, options);
2823
+ const nextParams = await options?.onPayload?.(params, model);
2824
+ if (nextParams !== void 0) params = nextParams;
2825
+ if (options?.openclawCodeModeToolSurface === true) {
2826
+ const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
2827
+ enforceCodeModeResponsesToolSurface(params, visibleToolNames, void 0, codeModeToolSurfaceObserver.get(options));
2828
+ assertCodeModeResponsesToolSurface(params, visibleToolNames);
2829
+ }
2830
+ if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
2831
+ const emitReasoning = shouldEmitOpenAICompletionsReasoning(model, options);
2832
+ firstEventAbort = createFirstStreamEventAbortController(options?.signal);
2833
+ const { data: responseStream, response } = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
2834
+ timeoutMs: options?.timeoutMs,
2835
+ maxRetries: options?.maxRetries
2836
+ })).withResponse();
2837
+ await processCompletionsStream(withProviderResponseHook({
2838
+ stream: responseStream,
2839
+ signal: firstEventAbort.signal,
2840
+ abort: firstEventAbort.abort,
2841
+ hook: createOpenAIProviderAcceptanceHook(options, response, model),
2842
+ onReady: () => stream.push({
2843
+ type: "start",
2844
+ partial: output
2845
+ })
2846
+ }), output, model, stream, {
2847
+ signal: options?.signal,
2848
+ emitReasoning,
2849
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
2850
+ abortFirstEventStream: firstEventAbort.abort,
2851
+ onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
2852
+ sawStreamDONE: doneDetector.sawDone
2853
+ });
2854
+ finalizeTransportStream({
2855
+ stream,
2856
+ output,
2857
+ signal: options?.signal
2858
+ });
2859
+ } catch (error) {
2860
+ failTransportStream({
2861
+ stream,
2862
+ output,
2863
+ signal: options?.signal,
2864
+ error,
2865
+ cleanup: () => {
2866
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
2867
+ finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
2868
+ tagUnresolvedTextAsCommentary(output);
2869
+ }
2870
+ });
2871
+ } finally {
2872
+ firstEventAbort?.dispose();
2781
2873
  }
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;
2874
+ })();
2875
+ return eventStream;
2876
+ };
2814
2877
  }
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
2878
  //#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;
2879
+ //#region packages/ai/src/transports/openai-responses-compact-request.ts
2880
+ const COMPACT_REQUEST = Symbol("openaiResponsesCompactRequest");
2881
+ function claimResponsesCompactRequest(options) {
2882
+ const controller = options ? Reflect.get(options, COMPACT_REQUEST) : void 0;
2883
+ if (controller?.claimed === false) {
2884
+ controller.claimed = true;
2885
+ return controller;
2873
2886
  }
2874
2887
  }
2875
- function resolveUrlHostname(value) {
2876
- const trimmed = readStringValue(value)?.trim();
2877
- if (!trimmed) return;
2888
+ /** Run a compact-endpoint request through the session's prepared stream stack. */
2889
+ async function requestPreparedOpenAIResponsesCompaction(streamFn, model, context, options) {
2890
+ const preparedOptions = { ...options };
2891
+ let resolveResult;
2892
+ let rejectResult;
2893
+ const result = new Promise((resolve, reject) => {
2894
+ resolveResult = resolve;
2895
+ rejectResult = reject;
2896
+ });
2897
+ const controller = {
2898
+ claimed: false,
2899
+ resolve: resolveResult,
2900
+ reject: rejectResult
2901
+ };
2902
+ Reflect.set(preparedOptions, COMPACT_REQUEST, controller);
2903
+ const stream = await Promise.resolve(streamFn(model, context, preparedOptions));
2904
+ if (!controller.claimed) throw new Error("Prepared stream did not reach an OpenAI Responses transport");
2878
2905
  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
- }
2906
+ return await result;
2907
+ } finally {
2908
+ await stream.result().catch(() => void 0);
2886
2909
  }
2887
2910
  }
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;
2911
+ //#endregion
2912
+ //#region packages/ai/src/transports/openai-responses-continuation.ts
2913
+ const HTTP_CONTINUATION_IDLE_TTL_MS = 300 * 1e3;
2914
+ const TURN_HEADERS = /* @__PURE__ */ new Set([
2915
+ "traceparent",
2916
+ "x-openclaw-turn-id",
2917
+ "x-openclaw-turn-attempt"
2918
+ ]);
2919
+ function jsonValuesEqual(left, right) {
2920
+ return stableStringify(JSON.parse(JSON.stringify(left))) === stableStringify(JSON.parse(JSON.stringify(right)));
2921
+ }
2922
+ function requestWithoutInput(request) {
2923
+ const { input: _input, previous_response_id: _previousResponseId, ...rest } = request;
2924
+ if (!isRecord(rest.metadata)) return rest;
2925
+ const metadata = Object.fromEntries(Object.entries(rest.metadata).filter(([key]) => key !== "openclaw_turn_id" && key !== "openclaw_turn_attempt"));
2945
2926
  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;
2927
+ ...rest,
2928
+ metadata
2929
+ };
2930
+ }
2931
+ function normalizeAssistantReplayInput(input) {
2932
+ return input.map((item) => {
2933
+ if (!isRecord(item)) return item;
2934
+ if (item.type === "reasoning") return { type: "reasoning" };
2935
+ if (item.type !== "function_call" && !(item.type === "message" && item.role === "assistant")) return item;
2936
+ const { id: _id, status: _status, ...stableItem } = item;
2937
+ if (item.type === "message" && Array.isArray(stableItem.content)) stableItem.content = stableItem.content.map((part) => {
2938
+ if (!isRecord(part) || part.type !== "output_text") return part;
2939
+ const { annotations: _annotations, logprobs: _logprobs, ...stablePart } = part;
2940
+ return stablePart;
2941
+ });
2942
+ return stableItem;
2943
+ });
2944
+ }
2945
+ function resolveResponsesContinuationRequest(continuation, request) {
2946
+ if (!continuation) return {
2947
+ request,
2948
+ continuationStatus: "no_previous_response"
2949
+ };
2950
+ if (request.previous_response_id) return {
2951
+ request,
2952
+ continuationStatus: "explicit_previous_response_id"
2953
+ };
2954
+ if (!jsonValuesEqual(requestWithoutInput(request), requestWithoutInput(continuation.lastRequest))) return {
2955
+ request,
2956
+ continuationStatus: "request_changed"
2957
+ };
2958
+ const currentInput = request.input ?? [];
2959
+ const previousInput = continuation.lastRequest.input ?? [];
2960
+ const baselineLength = previousInput.length + continuation.lastResponseItems.length;
2961
+ if (currentInput.length < baselineLength) return {
2962
+ request,
2963
+ continuationStatus: "history_shorter"
2964
+ };
2965
+ if (!jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(0, previousInput.length)), normalizeAssistantReplayInput(previousInput)) || !jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(previousInput.length, baselineLength)), normalizeAssistantReplayInput(continuation.lastResponseItems))) return {
2966
+ request,
2967
+ continuationStatus: "history_changed"
2968
+ };
2991
2969
  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);
2970
+ request: {
2971
+ ...request,
2972
+ previous_response_id: continuation.lastResponseId,
2973
+ input: currentInput.slice(baselineLength)
2974
+ },
2975
+ continuationStatus: "continued"
2976
+ };
2977
+ }
2978
+ const httpContinuationEntries = /* @__PURE__ */ new Map();
2979
+ let nextHttpContinuationGeneration = 1;
2980
+ function connectionIdentity(params) {
2981
+ 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));
2982
+ return sha256Hex(JSON.stringify([
2983
+ getAiTransportHost().resolveSecretSentinel(params.apiKey),
2984
+ params.baseUrl,
2985
+ headers
2986
+ ]));
2987
+ }
2988
+ function claimOpenAIResponsesHttpContinuation(params) {
2989
+ const key = `${params.sessionId}\0${connectionIdentity(params)}`;
2990
+ const previous = httpContinuationEntries.get(key);
2991
+ if (previous?.kind === "claimed") return;
2992
+ if (previous?.kind === "ready") clearTimeout(previous.idleTimer);
2993
+ const generation = nextHttpContinuationGeneration++;
2994
+ const claimed = {
2995
+ kind: "claimed",
2996
+ sessionId: params.sessionId,
2997
+ generation
2998
+ };
2999
+ httpContinuationEntries.set(key, claimed);
3000
+ return {
3001
+ request: resolveResponsesContinuationRequest(previous?.kind === "ready" ? previous.state : void 0, params.request).request,
3002
+ commit: (effectiveRequest, response) => {
3003
+ if (httpContinuationEntries.get(key) !== claimed) return;
3004
+ const idleTimer = setTimeout(() => {
3005
+ const current = httpContinuationEntries.get(key);
3006
+ if (current?.kind === "ready" && current.generation === generation) httpContinuationEntries.delete(key);
3007
+ }, HTTP_CONTINUATION_IDLE_TTL_MS);
3008
+ idleTimer.unref?.();
3009
+ const ready = {
3010
+ ...claimed,
3011
+ kind: "ready",
3012
+ state: {
3013
+ lastRequest: effectiveRequest,
3014
+ lastResponseId: response.id,
3015
+ lastResponseItems: response.output
3016
+ },
3017
+ idleTimer
3018
+ };
3019
+ httpContinuationEntries.set(key, ready);
3020
+ },
3021
+ release: () => {
3022
+ if (httpContinuationEntries.get(key) === claimed) httpContinuationEntries.delete(key);
3023
+ }
3024
+ };
3016
3025
  }
3026
+ registerSessionResourceCleanup((sessionId) => {
3027
+ for (const [key, entry] of httpContinuationEntries) if (!sessionId || entry.sessionId === sessionId) {
3028
+ if (entry.kind === "ready") clearTimeout(entry.idleTimer);
3029
+ httpContinuationEntries.delete(key);
3030
+ }
3031
+ });
3017
3032
  //#endregion
3018
3033
  //#region packages/ai/src/transports/openai-responses-params-internal.ts
3034
+ const OPENAI_RESPONSES_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set([
3035
+ "openai",
3036
+ "opencode",
3037
+ "azure-openai-responses",
3038
+ "github-copilot"
3039
+ ]);
3019
3040
  function convertResponsesTools(tools, model, options) {
3020
3041
  const projection = projectOpenAITools(tools);
3021
3042
  const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
@@ -3114,7 +3135,7 @@ function resolveOpenAIResponsesTextFormat(responseFormat) {
3114
3135
  };
3115
3136
  return responseFormat;
3116
3137
  }
3117
- function buildOpenAIResponsesParams(model, context, options, metadata) {
3138
+ function convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode) {
3118
3139
  const isCodexResponses = isOpenAICodexResponsesModel(model);
3119
3140
  const isNativeCodexResponses = usesNativeOpenAICodexResponsesBackend(model);
3120
3141
  const compat = getCompat(model);
@@ -3122,19 +3143,20 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
3122
3143
  const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
3123
3144
  const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
3124
3145
  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
- ]), {
3146
+ return convertResponsesMessages(model, context, OPENAI_RESPONSES_TOOL_CALL_PROVIDERS, {
3131
3147
  includeSystemPrompt: !isCodexResponses,
3132
3148
  supportsDeveloperRole,
3133
3149
  replayReasoningItems: true,
3134
3150
  replayResponsesItemIds,
3135
3151
  authProfileId: options?.authProfileId,
3136
- sessionId: options?.sessionId
3152
+ sessionId: options?.sessionId,
3153
+ replayMode
3137
3154
  });
3155
+ }
3156
+ function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint") {
3157
+ const isCodexResponses = isOpenAICodexResponsesModel(model);
3158
+ const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
3159
+ const messages = convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode);
3138
3160
  if (isCodexResponses) ensureOpenAICodexResponsesInput(messages, context);
3139
3161
  const cacheRetention = resolveCacheRetention(options?.cacheRetention);
3140
3162
  const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
@@ -3194,6 +3216,261 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
3194
3216
  return sanitizeOpenAICodexResponsesParams(model, params);
3195
3217
  }
3196
3218
  //#endregion
3219
+ //#region packages/ai/src/transports/openai-responses-websocket.ts
3220
+ const SESSION_WEBSOCKET_CACHE_TTL_MS = 300 * 1e3;
3221
+ const SESSION_WEBSOCKET_MAX_AGE_MS = 3300 * 1e3;
3222
+ const WEBSOCKET_OPEN_STATE = 1;
3223
+ const websocketSessionCache = /* @__PURE__ */ new Map();
3224
+ const degradedWebSocketConnections = /* @__PURE__ */ new Map();
3225
+ function isOfficialOpenAIResponsesBaseUrl(baseUrl) {
3226
+ if (!baseUrl) return false;
3227
+ try {
3228
+ const url = new URL(baseUrl);
3229
+ return url.origin === "https://api.openai.com" && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && url.pathname.replace(/\/+$/, "") === "/v1";
3230
+ } catch {
3231
+ return false;
3232
+ }
3233
+ }
3234
+ function supportsNativeOpenAIResponsesEndpoint(params) {
3235
+ return params.provider.trim().toLowerCase() === "openai" && params.api === "openai-responses" && isOfficialOpenAIResponsesBaseUrl(params.baseUrl);
3236
+ }
3237
+ function closeWebSocketSilently(socket, reason = "done") {
3238
+ try {
3239
+ socket.close({
3240
+ code: 1e3,
3241
+ reason
3242
+ });
3243
+ } catch {}
3244
+ }
3245
+ function invalidateOwnedWebSocketSession(cacheKey, entry, reason = "done") {
3246
+ if (entry.idleTimer) {
3247
+ clearTimeout(entry.idleTimer);
3248
+ entry.idleTimer = void 0;
3249
+ }
3250
+ closeWebSocketSilently(entry.socket, reason);
3251
+ if (websocketSessionCache.get(cacheKey) === entry) websocketSessionCache.delete(cacheKey);
3252
+ }
3253
+ function scheduleSessionWebSocketExpiry(cacheKey, entry) {
3254
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
3255
+ entry.idleTimer = setTimeout(() => {
3256
+ if (entry.busy) return;
3257
+ invalidateOwnedWebSocketSession(cacheKey, entry, "idle_timeout");
3258
+ }, SESSION_WEBSOCKET_CACHE_TTL_MS);
3259
+ entry.idleTimer.unref?.();
3260
+ }
3261
+ function prepareWebSocketConnection(client, headers) {
3262
+ if (!isOfficialOpenAIResponsesBaseUrl(client.baseURL)) throw new Error("OpenAI Responses WebSocket requires the official API endpoint");
3263
+ if (typeof client.apiKey !== "string" || client.apiKey.length === 0) throw new Error("OpenAI Responses WebSocket requires an API key");
3264
+ const resolvedApiKey = getAiTransportHost().resolveSecretSentinel(client.apiKey);
3265
+ const resolvedHeaders = { ...resolveAiTransportHeaderSentinels(headers) };
3266
+ for (const key of Object.keys(resolvedHeaders)) {
3267
+ const normalizedKey = key.toLowerCase();
3268
+ if (normalizedKey === "authorization" || normalizedKey === "traceparent") delete resolvedHeaders[key];
3269
+ }
3270
+ if (!resolvedApiKey) throw new Error("OpenAI Responses WebSocket requires a resolved API key");
3271
+ return {
3272
+ client: client.withOptions({ apiKey: resolvedApiKey }),
3273
+ headers: resolvedHeaders,
3274
+ identity: sha256Hex(JSON.stringify([
3275
+ resolvedApiKey,
3276
+ client.baseURL,
3277
+ Object.entries(resolvedHeaders).toSorted(([a], [b]) => a.localeCompare(b))
3278
+ ]))
3279
+ };
3280
+ }
3281
+ function createWebSocket(connection, onError) {
3282
+ const socket = new ResponsesWS(connection.client, {
3283
+ headers: connection.headers,
3284
+ maxQueueSize: 1
3285
+ });
3286
+ socket.on("error", () => onError(socket));
3287
+ return socket;
3288
+ }
3289
+ function createTransientWebSocketLease(connection) {
3290
+ const socket = createWebSocket(connection, (failedSocket) => closeWebSocketSilently(failedSocket, "transport_error"));
3291
+ return {
3292
+ socket,
3293
+ iterator: socket.stream(),
3294
+ reusedConnection: false,
3295
+ release: () => closeWebSocketSilently(socket)
3296
+ };
3297
+ }
3298
+ function createCachedWebSocketLease(cacheKey, entry, reusedConnection) {
3299
+ entry.busy = true;
3300
+ return {
3301
+ socket: entry.socket,
3302
+ iterator: entry.socket.stream(),
3303
+ entry,
3304
+ reusedConnection,
3305
+ release: ({ keep } = {}) => {
3306
+ if (!keep || entry.socket.socket.readyState !== WEBSOCKET_OPEN_STATE) {
3307
+ invalidateOwnedWebSocketSession(cacheKey, entry);
3308
+ return;
3309
+ }
3310
+ entry.busy = false;
3311
+ scheduleSessionWebSocketExpiry(cacheKey, entry);
3312
+ }
3313
+ };
3314
+ }
3315
+ function acquireWebSocket(params, connection) {
3316
+ if (!(params.mode !== "websocket" && Boolean(params.sessionId)) || !params.sessionId) return createTransientWebSocketLease(connection);
3317
+ const cacheKey = `${params.sessionId}\0${connection.identity}`;
3318
+ const cached = websocketSessionCache.get(cacheKey);
3319
+ if (cached) {
3320
+ if (cached.idleTimer) {
3321
+ clearTimeout(cached.idleTimer);
3322
+ cached.idleTimer = void 0;
3323
+ }
3324
+ if (cached.busy) return createTransientWebSocketLease(connection);
3325
+ const expired = Date.now() - cached.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
3326
+ if (!expired && cached.socket.socket.readyState === WEBSOCKET_OPEN_STATE) return createCachedWebSocketLease(cacheKey, cached, true);
3327
+ invalidateOwnedWebSocketSession(cacheKey, cached, expired ? "connection_age_limit" : "done");
3328
+ }
3329
+ const entry = {
3330
+ socket: createWebSocket(connection, (failedSocket) => {
3331
+ const failedEntry = websocketSessionCache.get(cacheKey);
3332
+ if (failedEntry?.socket === failedSocket) invalidateOwnedWebSocketSession(cacheKey, failedEntry, "transport_error");
3333
+ else closeWebSocketSilently(failedSocket, "transport_error");
3334
+ }),
3335
+ sessionId: params.sessionId,
3336
+ busy: true,
3337
+ createdAt: Date.now()
3338
+ };
3339
+ websocketSessionCache.set(cacheKey, entry);
3340
+ return createCachedWebSocketLease(cacheKey, entry, false);
3341
+ }
3342
+ function sanitizeWebSocketRequest(request) {
3343
+ const { stream: _stream, background: _background, ...websocketRequest } = request;
3344
+ return websocketRequest;
3345
+ }
3346
+ async function nextWebSocketMessage(iterator, signal) {
3347
+ if (!signal) return iterator.next();
3348
+ if (signal.aborted) throw transportAbortError(signal);
3349
+ let onAbort;
3350
+ try {
3351
+ return await Promise.race([iterator.next(), new Promise((_resolve, reject) => {
3352
+ onAbort = () => reject(transportAbortError(signal));
3353
+ signal.addEventListener("abort", onAbort, { once: true });
3354
+ })]);
3355
+ } finally {
3356
+ if (onAbort) signal.removeEventListener("abort", onAbort);
3357
+ }
3358
+ }
3359
+ function readServerEvent(message) {
3360
+ if (message.type === "message") return message.message;
3361
+ if (message.type === "error") throw parseOpenAIResponsesWebSocketServerError(message.error) ?? new Error("OpenAI Responses WebSocket transport failed", { cause: message.error });
3362
+ if (message.type === "close") throw new Error(`OpenAI Responses WebSocket closed before completion (code ${message.code})`);
3363
+ }
3364
+ function createOpenAIResponsesWebSocketStream(params) {
3365
+ const connection = prepareWebSocketConnection(params.client, params.headers);
3366
+ const fullRequest = sanitizeWebSocketRequest(params.request);
3367
+ const requestModel = typeof fullRequest.model === "string" ? fullRequest.model : "";
3368
+ const degradationKey = `${params.sessionId ?? ""}\0${connection.identity}\0${requestModel}`;
3369
+ const degraded = degradedWebSocketConnections.get(degradationKey);
3370
+ if (degraded && degraded.retryAt > Date.now()) throw new OpenAIResponsesWebSocketPreDispatchError(/* @__PURE__ */ new Error("OpenAI Responses WebSocket is cooling down after a transport failure"));
3371
+ degradedWebSocketConnections.delete(degradationKey);
3372
+ const markDegraded = () => {
3373
+ const cooldownMs = params.degradeCooldownMs;
3374
+ if (cooldownMs === void 0 || !Number.isFinite(cooldownMs) || cooldownMs <= 0) return;
3375
+ degradedWebSocketConnections.set(degradationKey, {
3376
+ sessionId: params.sessionId,
3377
+ retryAt: Date.now() + cooldownMs
3378
+ });
3379
+ };
3380
+ let lease;
3381
+ try {
3382
+ lease = acquireWebSocket(params, connection);
3383
+ } catch (error) {
3384
+ markDegraded();
3385
+ throw new OpenAIResponsesWebSocketPreDispatchError(error);
3386
+ }
3387
+ let prepared;
3388
+ try {
3389
+ const continuation = lease.entry?.continuation;
3390
+ if (continuation && lease.entry) {
3391
+ lease.entry.continuation = void 0;
3392
+ prepared = resolveResponsesContinuationRequest(continuation, fullRequest);
3393
+ } else prepared = {
3394
+ request: fullRequest,
3395
+ continuationStatus: lease.entry ? "no_previous_response" : "socket_not_cached"
3396
+ };
3397
+ } catch (error) {
3398
+ lease.iterator.return?.().catch(() => void 0);
3399
+ lease.release({ keep: false });
3400
+ throw error;
3401
+ }
3402
+ let streamStarted = false;
3403
+ let terminalResponse;
3404
+ let terminalReceived = false;
3405
+ let released = false;
3406
+ const finish = ({ keep = true } = {}) => {
3407
+ if (released) return;
3408
+ released = true;
3409
+ if (keep && lease.entry && terminalResponse) lease.entry.continuation = {
3410
+ lastRequest: fullRequest,
3411
+ lastResponseId: terminalResponse.id,
3412
+ lastResponseItems: terminalResponse.output
3413
+ };
3414
+ lease.release({ keep });
3415
+ };
3416
+ return {
3417
+ stream: { async *[Symbol.asyncIterator]() {
3418
+ if (streamStarted) throw new Error("OpenAI Responses WebSocket stream can only be consumed once");
3419
+ streamStarted = true;
3420
+ const iterator = lease.iterator;
3421
+ let requestDispatched = false;
3422
+ try {
3423
+ if (params.signal?.aborted) throw transportAbortError(params.signal);
3424
+ for (;;) {
3425
+ const next = await nextWebSocketMessage(iterator, params.signal);
3426
+ if (next.done) throw new Error("OpenAI Responses WebSocket closed before a terminal response event");
3427
+ if (next.value.type === "open") {
3428
+ if (!requestDispatched) {
3429
+ requestDispatched = true;
3430
+ lease.socket.send({
3431
+ ...prepared.request,
3432
+ type: "response.create"
3433
+ });
3434
+ }
3435
+ continue;
3436
+ }
3437
+ const event = readServerEvent(next.value);
3438
+ if (!event) continue;
3439
+ if (event.type === "response.completed") terminalResponse = event.response;
3440
+ terminalReceived = event.type === "response.completed" || event.type === "response.incomplete" || event.type === "response.failed";
3441
+ yield event;
3442
+ if (terminalReceived) {
3443
+ degradedWebSocketConnections.delete(degradationKey);
3444
+ return;
3445
+ }
3446
+ }
3447
+ } catch (error) {
3448
+ if (lease.entry) lease.entry.continuation = void 0;
3449
+ const safeRetry = error instanceof OpenAIResponsesWebSocketSafeRetryError;
3450
+ if (!params.callerSignal?.aborted && !safeRetry) markDegraded();
3451
+ if (!requestDispatched && !params.signal?.aborted) throw new OpenAIResponsesWebSocketPreDispatchError(error);
3452
+ if (!requestDispatched || params.callerSignal?.aborted || safeRetry) throw error;
3453
+ throw new OpenAIResponsesWebSocketPostDispatchError(error);
3454
+ } finally {
3455
+ await iterator.return?.().catch(() => void 0);
3456
+ if (!terminalReceived) finish({ keep: false });
3457
+ }
3458
+ } },
3459
+ request: prepared.request,
3460
+ reusedConnection: lease.reusedConnection,
3461
+ continuationStatus: prepared.continuationStatus,
3462
+ finish
3463
+ };
3464
+ }
3465
+ function closeOpenAIResponsesWebSocketSessions(sessionId) {
3466
+ for (const [cacheKey, entry] of websocketSessionCache) {
3467
+ if (sessionId && entry.sessionId !== sessionId) continue;
3468
+ invalidateOwnedWebSocketSession(cacheKey, entry, "session_cleanup");
3469
+ }
3470
+ for (const [key, entry] of degradedWebSocketConnections) if (!sessionId || entry.sessionId === sessionId) degradedWebSocketConnections.delete(key);
3471
+ }
3472
+ registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
3473
+ //#endregion
3197
3474
  //#region packages/media-core/src/inline-image-data-url.ts
3198
3475
  /** Prefix used to distinguish inline data URLs from remote/local image references. */
3199
3476
  const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
@@ -3338,6 +3615,20 @@ function sanitizeResponsesImagePayload(params) {
3338
3615
  }
3339
3616
  //#endregion
3340
3617
  //#region packages/ai/src/transports/openai-responses-client.ts
3618
+ function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
3619
+ if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
3620
+ if (getAiTransportHost().requiresManagedTransport(model)) return;
3621
+ return supportsNativeOpenAIResponsesEndpoint({
3622
+ provider: model.provider,
3623
+ api: model.api,
3624
+ baseUrl: model.baseUrl
3625
+ }) ? transport : void 0;
3626
+ }
3627
+ function combineWebSocketTimeoutSignal(signal, model, timeoutMs) {
3628
+ const resolvedTimeoutMs = timeoutMs !== void 0 && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : getAiTransportHost().resolveModelRequestTimeoutMs(model);
3629
+ if (resolvedTimeoutMs === void 0 || !Number.isFinite(resolvedTimeoutMs)) return signal;
3630
+ return AbortSignal.any([signal, AbortSignal.timeout(Math.max(1, resolvedTimeoutMs))]);
3631
+ }
3341
3632
  function resolveProviderTransportTurnState(model, params) {
3342
3633
  const normalizedProvider = model.provider.trim().toLowerCase();
3343
3634
  const allowRuntimePluginLoad = normalizedProvider === "openai" || normalizedProvider === "azure-openai" || normalizedProvider === "azure-openai-responses";
@@ -3366,111 +3657,265 @@ function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turn
3366
3657
  ...buildOpenAISdkClientOptions(model)
3367
3658
  });
3368
3659
  }
3660
+ async function postOpenAIResponsesCompaction(params) {
3661
+ const response = await params.client.post("/responses/compact", {
3662
+ ...buildOpenAISdkRequestOptions(params.model, params.options?.signal, {
3663
+ timeoutMs: params.options?.timeoutMs,
3664
+ maxRetries: params.options?.maxRetries
3665
+ }),
3666
+ body: {
3667
+ model: params.request.model,
3668
+ input: params.request.input
3669
+ }
3670
+ });
3671
+ const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
3672
+ const item = output.at(-1);
3673
+ const retainedItems = output.slice(0, -1);
3674
+ const retainedMessagesAreValid = retainedItems.every((candidate) => isRecord(candidate) && candidate.type === "message" && (candidate.role === "user" || candidate.role === "developer" || candidate.role === "system") && Array.isArray(candidate.content));
3675
+ const retainedUserMessageCount = retainedItems.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user" && Array.isArray(candidate.content)).length;
3676
+ const inputUserMessageCount = Array.isArray(params.request.input) ? params.request.input.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user").length : 0;
3677
+ const retainedMessagePrefixSupported = supportsNativeOpenAIResponsesEndpoint(params.model);
3678
+ const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
3679
+ if (!isRecord(response) || response.object !== "response.compaction" || !retainedMessagesAreValid || retainedItems.length > 0 && (!retainedMessagePrefixSupported || retainedUserMessageCount !== inputUserMessageCount) || !isRecord(item) || item.type !== "compaction" || typeof item.encrypted_content !== "string" || item.encrypted_content.length === 0 || !usage || typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") throw new Error("Responses compact endpoint did not return one trailing compaction item");
3680
+ return {
3681
+ item,
3682
+ historyMode: retainedUserMessageCount > 0 ? "retained-users" : "compacted-prefix",
3683
+ usage,
3684
+ model: params.model,
3685
+ replayMetadata: buildOpenAIResponsesReasoningReplayMetadata(params.model, {
3686
+ authProfileId: params.options?.authProfileId,
3687
+ sessionId: params.options?.sessionId
3688
+ })
3689
+ };
3690
+ }
3369
3691
  function createResponsesTransportExecutor(config) {
3370
3692
  return (model, context, options) => {
3371
3693
  const responsesOptions = options;
3372
- const eventStream = createAssistantMessageEventStream();
3373
- const stream = eventStream;
3694
+ const compactRequest = claimResponsesCompactRequest(responsesOptions);
3695
+ const { eventStream, stream } = createWritableTransportEventStream();
3374
3696
  (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
- };
3697
+ const output = createOpenAIResponsesAssistantOutput(model, config.outputApi);
3398
3698
  let firstEventAbort;
3699
+ let continuationClaim;
3399
3700
  try {
3400
3701
  const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
3702
+ const websocketMode = resolveNativeOpenAIResponsesWebSocketMode(model, responsesOptions?.transport);
3401
3703
  const turnState = resolveProviderTransportTurnState(model, {
3402
3704
  sessionId: options?.sessionId,
3403
3705
  turnId: randomUUID(),
3404
3706
  attempt: 1,
3405
- transport: "stream"
3707
+ transport: websocketMode ? "websocket" : "stream"
3406
3708
  });
3709
+ const websocketSessionPolicy = websocketMode ? turnState?.websocket : void 0;
3710
+ const websocketHeaders = websocketMode ? buildOpenAIClientHeaders(model, context, options?.headers, websocketSessionPolicy?.headers, options?.sessionId) : void 0;
3407
3711
  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);
3712
+ const buildRequest = async (replayMode) => {
3713
+ let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata, replayMode);
3714
+ const nextParams = await options?.onPayload?.(params, model);
3715
+ if (nextParams !== void 0) params = nextParams;
3716
+ if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
3717
+ params = sanitizeOpenAICodexResponsesParams(model, params);
3718
+ params = sanitizeResponsesImagePayload(params);
3719
+ if (options?.openclawCodeModeToolSurface === true) {
3720
+ const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
3721
+ const allowedHostedToolTypes = responsesOptions?.openclawCodeModeAllowedHostedToolTypes;
3722
+ enforceCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes, codeModeToolSurfaceObserver.get(options));
3723
+ assertCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
3724
+ }
3725
+ return params;
3726
+ };
3727
+ const params = await buildRequest("checkpoint");
3728
+ if (compactRequest) {
3729
+ const compacted = await postOpenAIResponsesCompaction({
3730
+ client,
3731
+ model,
3732
+ request: params,
3733
+ options: responsesOptions
3734
+ });
3735
+ output.usage.input = compacted.usage.input_tokens;
3736
+ output.usage.output = compacted.usage.output_tokens;
3737
+ output.usage.totalTokens = compacted.usage.input_tokens + compacted.usage.output_tokens;
3738
+ compactRequest.resolve(compacted);
3739
+ finalizeTransportStream({
3740
+ stream,
3741
+ output,
3742
+ signal: options?.signal
3743
+ });
3744
+ return;
3418
3745
  }
3746
+ const sessionId = options?.sessionId;
3747
+ if (config.httpContinuation && !websocketMode && !getAiTransportHost().requiresManagedTransport(model) && supportsNativeOpenAIResponsesEndpoint({
3748
+ provider: model.provider,
3749
+ api: model.api,
3750
+ baseUrl: model.baseUrl
3751
+ }) && sessionId && params.store === true && !params.previous_response_id) continuationClaim = claimOpenAIResponsesHttpContinuation({
3752
+ sessionId,
3753
+ apiKey,
3754
+ baseUrl: model.baseUrl,
3755
+ headers: buildOpenAIClientHeaders(model, context, options?.headers, turnState?.headers, sessionId),
3756
+ request: params
3757
+ });
3419
3758
  const observePrompt = createResponsesPromptEgressObserver(responsesOptions, context.systemPrompt);
3420
3759
  const requestStartedAt = Date.now();
3421
- firstEventAbort = createFirstStreamEventAbortController(options?.signal);
3422
- const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
3760
+ let started = false;
3761
+ const startStream = () => {
3762
+ if (!started) {
3763
+ started = true;
3764
+ stream.push({
3765
+ type: "start",
3766
+ partial: output
3767
+ });
3768
+ }
3769
+ };
3770
+ const firstEvent = createFirstStreamEventAbortController(options?.signal);
3771
+ firstEventAbort = firstEvent;
3772
+ const requestOptions = buildOpenAISdkRequestOptions(model, firstEvent.signal, {
3423
3773
  stream: config.streamRequest,
3424
3774
  timeoutMs: options?.timeoutMs,
3425
3775
  maxRetries: options?.maxRetries
3426
3776
  });
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");
3457
- stream.push({
3458
- type: "done",
3459
- reason: output.stopReason,
3460
- message: output
3777
+ const websocketSignal = combineWebSocketTimeoutSignal(firstEvent.signal, model, requestOptions?.timeout);
3778
+ 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)}`);
3779
+ let continuationBaseline;
3780
+ const createSseStream = async (initialRequest = continuationClaim?.request ?? params, initialAttemptKind = "initial", initialRejectedCompaction) => {
3781
+ const { stream: rawResponseStream, response, attempt } = await config.createResponseStream({
3782
+ client,
3783
+ request: initialRequest,
3784
+ requestOptions,
3785
+ model,
3786
+ observePrompt,
3787
+ initialAttemptKind,
3788
+ initialRejectedCompaction,
3789
+ buildFullHistoryRequest: () => buildRequest("full-history"),
3790
+ onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, responsesOptions, checkpoint)
3791
+ });
3792
+ if (continuationClaim) continuationBaseline = attempt.request.previous_response_id ? params : attempt.request;
3793
+ return withProviderResponseHook({
3794
+ stream: observeResponsesStream(rawResponseStream, model, requestStartedAt),
3795
+ signal: firstEvent.signal,
3796
+ abort: firstEvent.abort,
3797
+ hook: createOpenAIProviderAcceptanceHook(options, response, model),
3798
+ onReady: () => {
3799
+ emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} transport=sse elapsedMs=${Date.now() - requestStartedAt}`);
3800
+ startStream();
3801
+ }
3802
+ });
3803
+ };
3804
+ let responseStream;
3805
+ let finishWebSocket;
3806
+ let transport = "sse";
3807
+ const logWebSocketFallback = (reason) => emitModelTransportDebug(log, `[responses] websocket_fallback provider=${model.provider} api=${model.api} model=${model.id} reason=${reason}`);
3808
+ const closeWebSocketForFallback = (reason) => {
3809
+ finishWebSocket?.({ keep: false });
3810
+ finishWebSocket = void 0;
3811
+ transport = "sse";
3812
+ logWebSocketFallback(reason);
3813
+ };
3814
+ if (websocketMode) try {
3815
+ const websocket = createOpenAIResponsesWebSocketStream({
3816
+ client,
3817
+ request: params,
3818
+ mode: websocketMode,
3819
+ sessionId: options?.sessionId,
3820
+ headers: websocketHeaders,
3821
+ signal: websocketSignal,
3822
+ callerSignal: options?.signal,
3823
+ degradeCooldownMs: websocketSessionPolicy?.degradeCooldownMs
3824
+ });
3825
+ finishWebSocket = websocket.finish;
3826
+ observePrompt?.(websocket.request, {
3827
+ egress: "responses-websocket",
3828
+ payloadVariant: "initial"
3829
+ });
3830
+ transport = "websocket";
3831
+ 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))))}`);
3832
+ responseStream = { async *[Symbol.asyncIterator]() {
3833
+ let providerAccepted = false;
3834
+ try {
3835
+ for await (const event of websocket.stream) {
3836
+ if (!providerAccepted) {
3837
+ providerAccepted = true;
3838
+ await notifyProviderStreamOpened({
3839
+ options,
3840
+ cancelStream: () => websocket.finish({ keep: false })
3841
+ });
3842
+ }
3843
+ startStream();
3844
+ yield event;
3845
+ }
3846
+ } catch (error) {
3847
+ if (error instanceof OpenAIResponsesWebSocketSafeRetryError) {
3848
+ const encryptedContentRejected = isInvalidEncryptedContentError(error);
3849
+ const recovery = encryptedContentRejected ? await resolveNextResponsesEncryptedContentAttempt({
3850
+ kind: "initial",
3851
+ request: {
3852
+ ...websocket.request,
3853
+ stream: true
3854
+ }
3855
+ }, error, { buildFullHistoryRequest: () => buildRequest("full-history") }) : void 0;
3856
+ if (encryptedContentRejected && !recovery) throw error;
3857
+ closeWebSocketForFallback(`safe_server_error code=${error.code} status=${safeDebugValue(error.status)} param=${safeDebugValue(error.param)}`);
3858
+ yield* await createSseStream(recovery?.request ?? await buildRequest("full-history"), recovery?.kind ?? "continuation-rejected", recovery?.rejectedCompaction);
3859
+ return;
3860
+ }
3861
+ if (websocketSignal.aborted || !(error instanceof OpenAIResponsesWebSocketPreDispatchError)) throw error;
3862
+ transport = "sse";
3863
+ logWebSocketFallback("before_first_event");
3864
+ yield* await createSseStream();
3865
+ }
3866
+ } };
3867
+ } catch {
3868
+ closeWebSocketForFallback("setup_failure");
3869
+ responseStream = await createSseStream();
3870
+ }
3871
+ else responseStream = await createSseStream();
3872
+ try {
3873
+ const terminal = await processResponsesStream(responseStream, output, stream, model, {
3874
+ ...config.pricingOptions?.(responsesOptions, model),
3875
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
3876
+ abortFirstEventStream: firstEvent.abort,
3877
+ onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
3878
+ signal: options?.signal,
3879
+ reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
3880
+ authProfileId: responsesOptions?.authProfileId,
3881
+ sessionId: options?.sessionId
3882
+ })
3883
+ });
3884
+ finishWebSocket?.();
3885
+ if (options?.signal?.aborted) throw transportAbortError(options.signal);
3886
+ if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
3887
+ if (continuationClaim && continuationBaseline && terminal) continuationClaim.commit(continuationBaseline, terminal);
3888
+ } catch (error) {
3889
+ finishWebSocket?.({ keep: false });
3890
+ throw error;
3891
+ }
3892
+ emitModelTransportDebug(log, `[responses] completed provider=${model.provider} api=${model.api} model=${model.id} transport=${transport} elapsedMs=${Date.now() - requestStartedAt}`);
3893
+ finalizeTransportStream({
3894
+ stream,
3895
+ output,
3896
+ signal: options?.signal
3461
3897
  });
3462
- stream.end();
3463
3898
  } catch (error) {
3899
+ if (compactRequest) {
3900
+ compactRequest.reject(error);
3901
+ failTransportStream({
3902
+ stream,
3903
+ output,
3904
+ signal: options?.signal,
3905
+ error
3906
+ });
3907
+ return;
3908
+ }
3464
3909
  if (error instanceof ResponsesStreamFailure && error.observation) logResponsesFailedNoDetails(error.observation);
3465
3910
  log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
3466
- assignTransportErrorDetails(output, error, options?.signal);
3467
- stream.push({
3468
- type: "error",
3469
- reason: output.stopReason,
3470
- error: output
3911
+ failTransportStream({
3912
+ stream,
3913
+ output,
3914
+ signal: options?.signal,
3915
+ error
3471
3916
  });
3472
- stream.end();
3473
3917
  } finally {
3918
+ continuationClaim?.release();
3474
3919
  firstEventAbort?.dispose();
3475
3920
  }
3476
3921
  })();
@@ -3480,12 +3925,13 @@ function createResponsesTransportExecutor(config) {
3480
3925
  function createOpenAIResponsesTransportStreamFn() {
3481
3926
  return createResponsesTransportExecutor({
3482
3927
  streamRequest: true,
3928
+ httpContinuation: true,
3483
3929
  createClient: createOpenAIResponsesClient,
3484
3930
  buildRequest: buildOpenAIResponsesParams,
3485
3931
  createResponseStream: createResponsesStreamWithEncryptedContentRetry,
3486
- pricingOptions: (options) => ({
3932
+ pricingOptions: (options, model) => ({
3487
3933
  serviceTier: options?.serviceTier,
3488
- applyServiceTierPricing
3934
+ applyServiceTierPricing: (usage, serviceTier) => applyResponsesServiceTierPricing(usage, serviceTier, model)
3489
3935
  })
3490
3936
  });
3491
3937
  }
@@ -3494,18 +3940,8 @@ function createAzureOpenAIResponsesTransportStreamFn() {
3494
3940
  outputApi: "azure-openai-responses",
3495
3941
  firstEventTimeoutMs: AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS,
3496
3942
  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
- }
3943
+ buildRequest: (model, context, options, metadata, replayMode) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata, replayMode),
3944
+ createResponseStream: createResponsesStreamWithEncryptedContentRetry
3509
3945
  });
3510
3946
  }
3511
3947
  function normalizeAzureBaseUrl(baseUrl) {
@@ -3533,8 +3969,8 @@ function createAzureOpenAIClient(model, context, apiKey, optionHeaders, turnHead
3533
3969
  apiVersion: resolveAzureOpenAIApiVersion()
3534
3970
  });
3535
3971
  }
3536
- function buildAzureOpenAIResponsesParams(model, context, options, deploymentName, metadata) {
3537
- const params = buildOpenAIResponsesParams(model, context, options, metadata);
3972
+ function buildAzureOpenAIResponsesParams(model, context, options, deploymentName, metadata, replayMode = "checkpoint") {
3973
+ const params = buildOpenAIResponsesParams(model, context, options, metadata, replayMode);
3538
3974
  params.model = deploymentName;
3539
3975
  delete params.store;
3540
3976
  return params;
@@ -3559,11 +3995,8 @@ const responsesTesting = {
3559
3995
  buildOpenAIResponsesReasoningReplayMetadata,
3560
3996
  isInvalidEncryptedContentError,
3561
3997
  normalizeResponsesFailedEvent,
3562
- prepareOpenAIResponsesReasoningItemForReplay,
3563
3998
  createResponsesStreamWithEncryptedContentRetry,
3564
3999
  resolveAzureOpenAIApiVersion,
3565
- stripResponsesRequestEncryptedContent,
3566
- tagOpenAIResponsesReasoningReplayItem,
3567
4000
  summarizeResponsesFailedNoDetailsObservation,
3568
4001
  summarizeResponsesPayload,
3569
4002
  summarizeResponsesTools,
@@ -3572,6 +4005,51 @@ const responsesTesting = {
3572
4005
  };
3573
4006
  if (process.env.VITEST || false) globalThis.openclawOpenAIResponsesTransportTestApi = responsesTesting;
3574
4007
  //#endregion
4008
+ //#region packages/ai/src/transports/provider-compaction-replay.ts
4009
+ /** Whether provider replay state is a prefix-bound server compaction checkpoint. */
4010
+ function isCompactionReplayCheckpoint(replay) {
4011
+ const type = replay && typeof replay === "object" ? replay.type : void 0;
4012
+ return type === "anthropic-compaction" || type === "openai-responses-compaction" || type === "openai-responses-retained-compaction";
4013
+ }
4014
+ /** Strip prefix-bound checkpoints after local history rewrites. */
4015
+ function stripCompactionReplayCheckpoint(message) {
4016
+ if (!isCompactionReplayCheckpoint(message.providerReplay)) return message;
4017
+ const replaySafeMessage = { ...message };
4018
+ delete replaySafeMessage.providerReplay;
4019
+ return replaySafeMessage;
4020
+ }
4021
+ /** Strip prefix-bound checkpoint state from an in-place message rewrite. */
4022
+ function stripCompactionReplayCheckpointInPlace(message) {
4023
+ if (isCompactionReplayCheckpoint(message.providerReplay)) delete message.providerReplay;
4024
+ }
4025
+ /** Reindex a prefix-bound checkpoint after known content removals. */
4026
+ function replaceCompactionReplayOwnerContent(message, content) {
4027
+ const next = {
4028
+ ...message,
4029
+ content
4030
+ };
4031
+ const replay = message.providerReplay;
4032
+ if (!isCompactionReplayCheckpoint(replay)) return next;
4033
+ if (replay.type === "openai-responses-retained-compaction") return next;
4034
+ const replayIndex = replay.replayIndex ?? 0;
4035
+ if (content.length === 0 && message.content.length > 0 || replayIndex > message.content.length) return stripCompactionReplayCheckpoint(next);
4036
+ let sourceIndex = 0;
4037
+ let nextReplayIndex = 0;
4038
+ if (!content.every((block) => {
4039
+ const index = message.content.indexOf(block, sourceIndex);
4040
+ sourceIndex = index + 1;
4041
+ nextReplayIndex += index >= 0 && index < replayIndex ? 1 : 0;
4042
+ return index >= 0;
4043
+ })) return stripCompactionReplayCheckpoint(next);
4044
+ return nextReplayIndex === replayIndex ? next : {
4045
+ ...next,
4046
+ providerReplay: {
4047
+ ...replay,
4048
+ replayIndex: nextReplayIndex
4049
+ }
4050
+ };
4051
+ }
4052
+ //#endregion
3575
4053
  //#region packages/ai/src/transports/provider-transport-stream.ts
3576
4054
  const SUPPORTED_TRANSPORT_APIS = /* @__PURE__ */ new Set([
3577
4055
  "openai-responses",
@@ -3582,10 +4060,7 @@ const SUPPORTED_TRANSPORT_APIS = /* @__PURE__ */ new Set([
3582
4060
  "google-generative-ai"
3583
4061
  ]);
3584
4062
  const SIMPLE_TRANSPORT_API_ALIAS = {
3585
- "openai-responses": "openclaw-openai-responses-transport",
3586
- "openai-chatgpt-responses": "openclaw-openai-chatgpt-responses-transport",
3587
4063
  "openai-completions": "openclaw-openai-completions-transport",
3588
- "azure-openai-responses": "openclaw-azure-openai-responses-transport",
3589
4064
  "anthropic-messages": "openclaw-anthropic-messages-transport",
3590
4065
  "google-generative-ai": "openclaw-google-generative-ai-transport"
3591
4066
  };
@@ -3638,6 +4113,10 @@ function isTransportAwareApiSupported(api) {
3638
4113
  }
3639
4114
  /** Maps public model APIs to the internal transport API id used by simple runtime dispatch. */
3640
4115
  function resolveTransportAwareSimpleApi(api) {
4116
+ if (OPENAI_RESPONSES_APIS.has(api)) {
4117
+ const alias = `openclaw-${api}-transport`;
4118
+ return OPENAI_RESPONSES_APIS.has(alias) ? alias : void 0;
4119
+ }
3641
4120
  return SIMPLE_TRANSPORT_API_ALIAS[api];
3642
4121
  }
3643
4122
  /** Creates a managed transport stream only when request overrides require it. */
@@ -3734,12 +4213,12 @@ function resolveProviderStreamApi(model) {
3734
4213
  ];
3735
4214
  return `${PROVIDER_STREAM_API_PREFIX}${parts.map((part) => encodeURIComponent(part)).join(":")}`;
3736
4215
  }
3737
- function applyProviderSimpleCompletionWrapper(registry, model, cfg) {
4216
+ function applyProviderSimpleCompletionWrapper(registry, model, cfg, hookSourceApi = model.api) {
3738
4217
  if (model.api.startsWith(PROVIDER_SIMPLE_COMPLETION_API_PREFIX)) return model;
3739
4218
  const sourceProvider = registry.getApiProvider(model.api);
3740
4219
  if (!sourceProvider) return model;
3741
- const sourceApi = model.api;
3742
- const sourceStreamFn = (runtimeModel, context, options) => sourceProvider.streamSimple(projectModel(runtimeModel, { api: sourceApi }), context, options);
4220
+ const dispatchApi = model.api;
4221
+ const sourceStreamFn = (runtimeModel, context, options) => sourceProvider.streamSimple(projectModel(runtimeModel, { api: dispatchApi }), context, options);
3743
4222
  const streamFn = getAiTransportHost().plugin.wrapSimpleCompletionStream({
3744
4223
  provider: model.provider,
3745
4224
  config: cfg,
@@ -3748,6 +4227,7 @@ function applyProviderSimpleCompletionWrapper(registry, model, cfg) {
3748
4227
  provider: model.provider,
3749
4228
  modelId: model.id,
3750
4229
  model,
4230
+ sourceApi: hookSourceApi,
3751
4231
  streamFn: sourceStreamFn
3752
4232
  }
3753
4233
  });
@@ -3781,7 +4261,6 @@ function wrapPluginProviderStream(streamFn) {
3781
4261
  };
3782
4262
  }
3783
4263
  function prepareProviderStreamModel(params) {
3784
- if (params.model.api === "google-generative-ai") return;
3785
4264
  const pluginModel = resolveModelHeaderSentinels(params.model);
3786
4265
  const providerStreamFn = getAiTransportHost().plugin.resolveProviderStream({
3787
4266
  provider: params.model.provider,
@@ -3807,20 +4286,19 @@ function prepareModelForSimpleCompletion(params) {
3807
4286
  cfg,
3808
4287
  apiRegistry
3809
4288
  });
3810
- if (providerStreamModel) return applyProviderSimpleCompletionWrapper(apiRegistry, providerStreamModel, cfg);
4289
+ if (providerStreamModel) return applyProviderSimpleCompletionWrapper(apiRegistry, providerStreamModel, cfg, model.api);
3811
4290
  const codexTransportModel = prepareCodexSimpleTransportModel(apiRegistry, model, cfg);
3812
- if (codexTransportModel) return applyProviderSimpleCompletionWrapper(apiRegistry, codexTransportModel, cfg);
4291
+ if (codexTransportModel) return applyProviderSimpleCompletionWrapper(apiRegistry, codexTransportModel, cfg, model.api);
3813
4292
  const transportAwareModel = prepareTransportAwareSimpleModel(model, { cfg });
3814
4293
  if (transportAwareModel !== model) {
3815
4294
  const streamFn = buildTransportAwareSimpleStreamFn(model, { cfg });
3816
- if (streamFn && registerCustomApi(apiRegistry, transportAwareModel.api, streamFn)) return applyProviderSimpleCompletionWrapper(apiRegistry, transportAwareModel, cfg);
4295
+ if (streamFn && registerCustomApi(apiRegistry, transportAwareModel.api, streamFn)) return applyProviderSimpleCompletionWrapper(apiRegistry, transportAwareModel, cfg, model.api);
3817
4296
  }
3818
- if (model.api === "google-generative-ai") return applyProviderSimpleCompletionWrapper(apiRegistry, getAiTransportHost().prepareGoogleSimpleCompletionModel(apiRegistry, model), cfg);
3819
4297
  if (model.provider === "anthropic-vertex") {
3820
4298
  const api = resolveAnthropicVertexSimpleApi(model.baseUrl);
3821
- if (registerCustomApi(apiRegistry, api, getAiTransportHost().plugin.createAnthropicVertexStream(model))) return applyProviderSimpleCompletionWrapper(apiRegistry, projectModel(model, { api }), cfg);
4299
+ if (registerCustomApi(apiRegistry, api, getAiTransportHost().plugin.createAnthropicVertexStream(model))) return applyProviderSimpleCompletionWrapper(apiRegistry, projectModel(model, { api }), cfg, model.api);
3822
4300
  }
3823
4301
  return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
3824
4302
  }
3825
4303
  //#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 };
4304
+ export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE, applyAnthropicCacheControlToMessages, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, captureOpenAIResponsesCompaction, coerceTransportToolCallArguments, copyProviderAcceptanceObserver, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIProviderAcceptanceHook, createOpenAIResponseHook, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTerminalToolCallArguments, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, googleFlashSupportsMinimalThinking, hasOpenAICompatibleConversationTurn, isCodeModeModelVisibleToolName, isCompactionReplayCheckpoint, isOpenAICodexResponsesModel, isOpenAICompletionsThinkingEnabled, log, measureUtf8AppendBytes, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, notifyProviderHttpMetadata, notifyProviderHttpResponse, notifyProviderStreamOpened, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, parseOpenAICompletionsUsage, parseTerminalToolCallArguments, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, readOpenAICompletionsContentDeltas, readOpenAICompletionsReasoningBatch, 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, withProviderAcceptanceObserver, withProviderResponseHook };