@openclaw/ai 2026.7.2-beta.7 → 2026.8.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/{anthropic-CH4UUnZr.mjs → anthropic-B6dLpq5L.mjs} +55 -203
  2. package/dist/{src-QkygScBs.mjs → anthropic-JsNA5KCu.mjs} +0 -1
  3. package/dist/anthropic-compaction-replay-8lJNKXOE.mjs +840 -0
  4. package/dist/anthropic-payload-policy-CiEuQS72.d.mts +49 -0
  5. package/dist/{api-registry-DlMgPR39.d.mts → api-registry-k3zTz0cV.d.mts} +1 -1
  6. package/dist/{azure-openai-responses-CImcwB83.mjs → azure-openai-responses-mxIOtUnn.mjs} +23 -29
  7. package/dist/diagnostics.d.mts +24 -1
  8. package/dist/diagnostics.mjs +2 -1
  9. package/dist/{event-stream-YjaPW20U.d.mts → event-stream-BP6AWT8j.d.mts} +4 -1
  10. package/dist/{event-stream-D8n2uFee.mjs → event-stream-uSMZJ3FA.mjs} +26 -5
  11. package/dist/event-stream.d.mts +1 -1
  12. package/dist/event-stream.mjs +1 -1
  13. package/dist/{google-CtSg0iTS.mjs → google-C7h2QzDX.mjs} +5 -5
  14. package/dist/{google-shared-DNBz5rcD.mjs → google-shared-B0Qr9OR7.mjs} +146 -89
  15. package/dist/google-thinking-level-C-V3tecN.mjs +9 -0
  16. package/dist/{google-vertex-31f1uS9L.mjs → google-vertex-Bhc3TjDl.mjs} +11 -7
  17. package/dist/{llm-request-activity-BjtkplhG.mjs → headers-DdOQtGuU.mjs} +9 -1
  18. package/dist/host-DTqNc7ad.mjs +466 -0
  19. package/dist/{host-B9GUmcra.d.mts → host-vWgMMhiJ.d.mts} +9 -4
  20. package/dist/index.d.mts +6 -6
  21. package/dist/index.mjs +7 -5
  22. package/dist/internal/anthropic.d.mts +29 -5
  23. package/dist/internal/anthropic.mjs +5 -5
  24. package/dist/internal/openai-responses-payload-policy.d.mts +3 -0
  25. package/dist/internal/openai-responses-payload-policy.mjs +3 -0
  26. package/dist/internal/openai.d.mts +6 -6
  27. package/dist/internal/openai.mjs +8 -7
  28. package/dist/internal/runtime.d.mts +17 -5
  29. package/dist/internal/runtime.mjs +85 -73
  30. package/dist/internal/shared.d.mts +1 -6
  31. package/dist/internal/shared.mjs +3 -5
  32. package/dist/{json-parse-BvXNt1-7.mjs → json-parse-CDnesDM_.mjs} +4 -6
  33. package/dist/{mistral-CWmpvWYh.mjs → mistral-CEWoQI_g.mjs} +45 -48
  34. package/dist/number-coercion-H9qHik3g.mjs +71 -0
  35. package/dist/openai-chatgpt-jwt-KWcgd0d_.mjs +19 -0
  36. package/dist/{openai-chatgpt-responses-B84Ibtrd.mjs → openai-chatgpt-responses-CrPmqERt.mjs} +284 -240
  37. package/dist/{openai-completions-DsOxhOD1.mjs → openai-completions-BPnt4Sml.mjs} +65 -99
  38. package/dist/{openai-completions-compat-DBWjXoMZ.d.mts → openai-completions-compat-Dt3dcawL.d.mts} +2 -2
  39. package/dist/{openai-responses-BT7A3sLu.mjs → openai-responses-DhIKtOup.mjs} +18 -41
  40. package/dist/openai-responses-contracts-CyfIkQi5.mjs +253 -0
  41. package/dist/openai-responses-contracts-XpZJxrRG.d.mts +68 -0
  42. package/dist/openai-responses-payload-policy-BDxV-W0c.mjs +206 -0
  43. package/dist/openai-responses-payload-policy-BSs371VM.d.mts +40 -0
  44. package/dist/openai-responses-prompt-observer-internal-DgNTYnRY.mjs +46 -0
  45. package/dist/{openai-responses-stream-internal-Cw5txaGW.mjs → openai-responses-shared-DXIt3iY5.mjs} +1406 -940
  46. package/dist/{openai-reasoning-compat-YgeLncHw.mjs → openai-stop-reason-BkFkqqK0.mjs} +204 -37
  47. package/dist/openai-tool-projection-CY04OcvQ.mjs +338 -0
  48. package/dist/provider-error-BUwEnjXq.mjs +429 -0
  49. package/dist/provider-error-CzNw4BWX.d.mts +12 -0
  50. package/dist/{provider-options-D8bB3z9b.d.mts → provider-options-B96RdNpH.d.mts} +9 -3
  51. package/dist/provider-transcript-transform-ePx-Bbfr.mjs +155 -0
  52. package/dist/provider-types.d.mts +31 -0
  53. package/dist/provider-types.mjs +8 -0
  54. package/dist/providers.d.mts +1 -1
  55. package/dist/providers.mjs +17 -19
  56. package/dist/{reasoning-tag-text-partitioner-CGDyLWUR.mjs → reasoning-tag-text-partitioner-rnPwX2pg.mjs} +14 -8
  57. package/dist/record-coerce-DdXsgUd_.mjs +23 -0
  58. package/dist/sanitize-unicode-BYqrYtC_.mjs +90 -0
  59. package/dist/session-resources-CkR4WWy1.mjs +21 -0
  60. package/dist/simple-options-D58D5Kvw.mjs +117 -0
  61. package/dist/src-D2H6yKkH.mjs +2 -0
  62. package/dist/{stream-first-event-timeout-BBys9hSb.mjs → stream-first-event-timeout-MK28puvq.mjs} +2 -2
  63. package/dist/string-coerce-fsri9iCu.mjs +34 -0
  64. package/dist/{tool-schema-json-projection-BwNu3nDi.mjs → tool-schema-json-projection-q5d7QX5c.mjs} +32 -7
  65. package/dist/transport-utils-DJqkxbhC.mjs +138 -0
  66. package/dist/transports.d.mts +166 -241
  67. package/dist/transports.mjs +1979 -1825
  68. package/dist/types-BDdaOVi2.mjs +6 -0
  69. package/dist/{types-bzp5k29J.d.mts → types-BHNrPS1l.d.mts} +23 -1
  70. package/dist/types.d.mts +4 -4
  71. package/dist/types.mjs +6 -4
  72. package/dist/utf16-slice-CvGodqok.mjs +29 -0
  73. package/dist/{validation-DAa_yFOM.mjs → validation-B61OhAio.mjs} +6 -6
  74. package/dist/{validation-B-j7cOYp.d.mts → validation-DT9SrFn3.d.mts} +1 -1
  75. package/dist/validation.d.mts +1 -1
  76. package/dist/validation.mjs +1 -1
  77. package/package.json +15 -1
  78. package/dist/anthropic-usage-DWU-x8MI.mjs +0 -459
  79. package/dist/error-coercion-DgxlWC0n.mjs +0 -15
  80. package/dist/headers-B_e4-1J0.mjs +0 -9
  81. package/dist/host-Dog2WQiR.mjs +0 -369
  82. package/dist/number-coercion-DvG7SNMg.mjs +0 -129
  83. package/dist/openai-chatgpt-jwt-DhAAzLkj.mjs +0 -39
  84. package/dist/openai-responses-shared-pXl6Wd8S.mjs +0 -392
  85. package/dist/openai-tool-projection-OhX64DoP.mjs +0 -215
  86. package/dist/provider-error-CAEvRjry.mjs +0 -47
  87. package/dist/sanitize-unicode-DT5o51ur.mjs +0 -26
  88. package/dist/simple-options-9lhRrN73.mjs +0 -50
  89. package/dist/tool-result-text-CTpIRbYd.mjs +0 -225
  90. package/dist/transform-messages-C8mBqZxF.mjs +0 -2
  91. package/dist/transport-stream-shared-D81p90xq.mjs +0 -297
@@ -1,364 +1,53 @@
1
1
  import { n as getEnvApiKey } from "./env-api-keys-DrgeBuva.mjs";
2
- import { d as resolveClaudeSonnet5ModelIdentity, g as supportsClaudeNativeXhighEffort, h as supportsClaudeNativeMaxEffort, l as resolveClaudeNativeThinkingLevelMap, p as supportsClaudeAdaptiveThinking, u as resolveClaudeOpus5ModelIdentity } from "./src-QkygScBs.mjs";
3
- import { r as createAssistantMessageEventStream } from "./event-stream-D8n2uFee.mjs";
4
- import { a as applyClaudeRequestContract, c as requiresClaudeAdaptiveThinking, d as usesClaudeStreamingRefusalContract, f as hasNonEmptyString, g as isRecord, h as readStringValue, n as getAiTransportHost, o as defaultsClaudeAdaptiveThinking, p as normalizeLowercaseStringOrEmpty, r as resolveAiTransportHeaderSentinels, s as prepareClaudeNoPrefillRequestContext, u as usesClaudeFable5MessagesContract } from "./host-Dog2WQiR.mjs";
2
+ import { d as resolveClaudeSonnet5ModelIdentity, g as supportsClaudeNativeXhighEffort, p as supportsClaudeAdaptiveThinking, u as resolveClaudeOpus5ModelIdentity } from "./anthropic-JsNA5KCu.mjs";
3
+ import { r as createAssistantMessageEventStream } from "./event-stream-uSMZJ3FA.mjs";
4
+ import { n as normalizeLowercaseStringOrEmpty, t as hasNonEmptyString } from "./string-coerce-fsri9iCu.mjs";
5
+ import { r as calculateCost } from "./sanitize-unicode-BYqrYtC_.mjs";
6
+ import { _ as resolveAnthropicThinkingEffort, a as describeToolResultMediaPlaceholder, b as usesClaudeStreamingRefusalContract, d as ANTHROPIC_CLAUDE_CODE_VERSION, f as applyClaudeRequestContract, g as requiresClaudeAdaptiveThinking, h as prepareClaudeNoPrefillRequestContext, l as isImageWithMediaPayload, m as mapAnthropicStopReason, n as getAiTransportHost, o as extractToolResultBlockText, p as defaultsClaudeAdaptiveThinking, r as resolveAiTransportHeaderSentinels, s as extractToolResultText, u as ANTHROPIC_CLAUDE_CODE_BILLING_SYSTEM_BLOCK, y as usesClaudeFable5MessagesContract } from "./host-DTqNc7ad.mjs";
7
+ import { a as isRecord } from "./record-coerce-DdXsgUd_.mjs";
8
+ import { i as canonicalizeBase64, n as projectProviderError, o as stableStringify } from "./provider-error-BUwEnjXq.mjs";
9
+ import { n as toErrorObject, t as createResponsesPromptEgressObserver } from "./openai-responses-prompt-observer-internal-DgNTYnRY.mjs";
10
+ import { r as asNonNegativeFiniteNumber } from "./number-coercion-H9qHik3g.mjs";
11
+ import { _ as isOpenAIGpt56Model, c as OpenAIResponsesWebSocketPostDispatchError, d as OpenAIResponsesWebSocketSafeRetryError, g as isOpenAIGpt55Model, h as isOpenAIGpt54MiniModel, i as OPENAI_RESPONSES_APIS, l as OpenAIResponsesWebSocketPreDispatchError, p as parseOpenAIResponsesWebSocketServerError, r as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, t as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, u as OpenAIResponsesWebSocketResponseFailedError, v as normalizeOpenAIReasoningEffort, w as uniqueStrings, y as resolveOpenAIReasoningEffortForModel } from "./openai-responses-contracts-CyfIkQi5.mjs";
5
12
  import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
6
13
  import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
7
- import { a as isImageWithMediaPayload, d as stripSystemPromptCacheBoundary, m as sortPromptCacheToolsByName, n as extractToolResultBlockText, r as extractToolResultText, t as describeToolResultMediaPlaceholder, u as splitSystemPromptCacheBoundary } from "./tool-result-text-CTpIRbYd.mjs";
8
- import { a as resolveModelRequestTimeoutMs, c as resolveProviderRequestCapabilities, i as buildGuardedModelFetch, l as resolveProviderRequestPolicyConfig, n as reconcileOpenAICompletionsToolChoice, o as resolveOpenAIStrictToolSetting, r as reconcileOpenAIResponsesToolChoice, s as resolveProviderEndpoint, t as projectOpenAITools, u as transformTransportMessages } from "./openai-tool-projection-OhX64DoP.mjs";
9
- import { t as toErrorObject } from "./error-coercion-DgxlWC0n.mjs";
10
- import { S as canonicalizeBase64, _ as omitFoundryBearerCredentialHeaders, a as projectAnthropicTools, b as normalizeAnthropicInlineContent, c as ANTHROPIC_OMITTED_REASONING_TEXT, d as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, g as applyAnthropicRefusal, h as resolveAnthropicFallbackServingModelCost, i as readLastAnthropicIterationUsage, l as findActiveAnthropicToolTurnAssistantIndex, m as readAnthropicFallbackBoundary, n as readAnthropicPromptUsageSnapshot, o as reconcileAnthropicToolChoice, p as applyAnthropicFallbackBoundary, r as readAnthropicUsageTokenCount, s as resolveOriginalAnthropicToolName, u as ANTHROPIC_SERVER_SIDE_FALLBACKS, v as usesFoundryBearerAuth, x as resolveAnthropicImageMediaType, y as createAnthropicInlineImageBudget } from "./anthropic-usage-DWU-x8MI.mjs";
11
- import { a as parseStrictPositiveInteger, c as calculateCost, l as clampThinkingLevel, s as applyProviderReportedUsageCost } from "./number-coercion-DvG7SNMg.mjs";
12
- import { a as resolveOpenAICompletionsCompat, c as clearPendingCommentaryText, i as detectOpenAICompletionsCompat, l as rememberPendingCommentaryTags, n as mapOpenAIStopReason, o as resolveOpenAICompletionsResponseFormat, r as convertMessages, s as shouldOmitOllamaCompatResponseFormat, t as resolveOpenAIReasoningEffortMap, u as tagPendingCommentaryText } from "./openai-reasoning-compat-YgeLncHw.mjs";
14
+ import { f as sortPromptCacheToolsByName, l as stripSystemPromptCacheBoundary, t as adjustMaxTokensForThinking } from "./simple-options-D58D5Kvw.mjs";
15
+ import { a as resolveProviderEndpoint, c as transformTransportMessages, i as resolveOpenAIStrictToolSetting, n as buildGuardedModelFetch, r as resolveModelRequestTimeoutMs, s as resolveProviderRequestPolicyConfig } from "./tool-schema-json-projection-q5d7QX5c.mjs";
16
+ import { A as resolveAnthropicImageMediaType, C as readAnthropicFallbackBoundary, D as usesFoundryBearerAuth, E as omitFoundryBearerCredentialHeaders, F as resolveAnthropicPayloadPolicy, I as resolveAnthropicServerCompactionPlan, M as applyAnthropicEphemeralCacheControlMarkers, N as applyAnthropicPayloadPolicyToParams, O as createAnthropicInlineImageBudget, P as resolveAnthropicEphemeralCacheControl, S as applyAnthropicFallbackBoundary, T as applyAnthropicRefusal, _ as ANTHROPIC_OMITTED_REASONING_TEXT, a as applyAnthropicMessageDeltaUsage, b as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, d as normalizeAnthropicToolCallId, f as normalizeAnthropicToolChoice, g as toClaudeCodeToolName, h as resolveOriginalAnthropicToolName, i as suppressAnthropicCompaction, j as applyAnthropicCacheControlToMessages, k as normalizeAnthropicInlineContent, m as reconcileAnthropicToolChoice, n as createCompactionCapture, o as applyAnthropicMessageStartUsage, p as projectAnthropicTools, r as isAnthropicReplayRejection, t as buildAnthropicReplayPlan, v as findActiveAnthropicToolTurnAssistantIndex, w as resolveAnthropicFallbackServingModelCost, y as ANTHROPIC_SERVER_SIDE_FALLBACKS } from "./anthropic-compaction-replay-8lJNKXOE.mjs";
17
+ import { a as createOpenAICompletionsToolCallDeltaNormalizer, c as resolveOpenAICompletionsCompat, d as clearPendingCommentaryText, f as rememberPendingCommentaryTags, i as hasToolCallHistory, l as resolveOpenAICompletionsResponseFormat, n as resolveOpenAIReasoningEffortMap, o as finalizeOpenAICompletionsToolCalls, p as tagPendingCommentaryText, r as convertMessages, s as detectOpenAICompletionsCompat, t as mapOpenAIStopReason, u as shouldOmitOllamaCompatResponseFormat } from "./openai-stop-reason-BkFkqqK0.mjs";
13
18
  import { t as createDeferredEventBuffer } from "./deferred-event-buffer-DAvyP7qA.mjs";
14
- import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
15
- import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
16
- import { 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-D81p90xq.mjs";
17
- import { A as normalizeResponsesFailedEvent, B as resolveReplayableResponsesMessageId, C as prepareOpenAIResponsesReasoningItemForReplay, D as applyServiceTierPricing, E as tagOpenAIResponsesReasoningReplayItem, F as summarizeResponsesFailedNoDetailsObservation, G as throwIfModelStreamAborted, H as createModelStreamCooperativeScheduler, 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 log, V as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, W as resolvePromptCacheKey, Y as normalizeOpenAIStrictToolParameters, Z as resolveOpenAIProjectedToolsStrictToolFlag, b as convertResponsesMessages, bt as resolveModelPayloadDebugMode, dt as isOpenAIGpt55Model, ft as isOpenAIGpt56Model, gt as supportsOpenAIReasoningEffort, j as safeDebugValue, k as logResponsesFailedNoDetails, mt as resolveOpenAIReasoningEffortForModel, n as processResponsesStream, o as observeResponsesStream, pt as normalizeOpenAIReasoningEffort, q as findOpenAIStrictToolProjectionDiagnostics, t as ResponsesStreamFailure, ut as isOpenAIGpt54MiniModel, v as buildOpenAIResponsesReasoningReplayMetadata, vt as uniqueStrings, w as resolveAzureOpenAIApiVersion, x as createResponsesStreamWithEncryptedContentRetry, xt as resolveModelSseDebugMode, y as buildResponsesInputMessage, yt as emitModelTransportDebug, z as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS } from "./openai-responses-stream-internal-Cw5txaGW.mjs";
18
- import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-CGDyLWUR.mjs";
19
- import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-BBys9hSb.mjs";
19
+ import { n as parseStreamingJson } from "./json-parse-CDnesDM_.mjs";
20
+ import { n as notifyLlmRequestActivity } from "./headers-DdOQtGuU.mjs";
21
+ import { B as buildResponsesInputMessage, C as summarizeResponsesTools, G as suppressOpenAIResponsesCompaction, H as createOpenAIResponsesAssistantOutput, I as createResponsesStreamWithEncryptedContentRetry, K as resolveReplayableResponsesMessageId, L as isInvalidEncryptedContentError, R as resolveAzureOpenAIApiVersion, S as summarizeResponsesPayload, U as buildOpenAIResponsesReasoningReplayMetadata, V as convertResponsesMessages, W as captureOpenAIResponsesCompaction, Y as normalizeOpenAIStrictToolParameters, Z as resolveOpenAIProjectedToolsStrictToolFlag, _ as safeDebugValue, b as summarizeOpenAITransportError, c as processResponsesStream, dt as resolveModelPayloadDebugMode, f as observeResponsesStream, ft as resolveModelSseDebugMode, g as normalizeResponsesFailedEvent, h as logResponsesFailedNoDetails, ht as quoteUnsafeIntegerLiterals, m as buildResponsesFailedNoDetailsObservation, mt as parseJsonPreservingUnsafeIntegers, n as applyResponsesServiceTierPricing, p as ResponsesStreamFailure, pt as parseJsonObjectPreservingUnsafeIntegers, q as findOpenAIStrictToolProjectionDiagnostics, ut as emitModelTransportDebug, v as stringifyRedactedEvent, x as summarizeResponsesFailedNoDetailsObservation, y as stringifyRedactedPayload, z as resolveNextResponsesEncryptedContentAttempt } from "./openai-responses-shared-DXIt3iY5.mjs";
22
+ import { a as createWritableTransportEventStream, c as mergeTransportHeaders, d as sanitizeTransportPayloadText, f as transportAbortError, i as createEmptyTransportUsage, l as mergeTransportMetadata, n as assignTransportErrorDetails, o as failTransportStream, p as withProviderResponseHook, r as coerceTransportToolCallArguments, s as finalizeTransportStream, u as sanitizeNonEmptyTransportPayloadText } from "./provider-transcript-transform-ePx-Bbfr.mjs";
23
+ import { a as isGoogleGemini3FlashModel, c as readResponseTextSnippet, d as resolveModelHeaderSentinels$1, f as resolveSecretSentinel, i as isCodeModeModelVisibleToolName, l as redactIdentifier, m as supportsModelTools, n as createAbortError$1, o as isGoogleGemini3ProModel, p as sha256Hex, r as estimateStringChars, s as parseRetryAfterSeconds, t as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE } from "./transport-utils-DJqkxbhC.mjs";
24
+ import { t as googleFlashSupportsMinimalThinking } from "./google-thinking-level-C-V3tecN.mjs";
25
+ import { a as createModelStreamCooperativeScheduler, c as log, d as readOpenAICompletionsContentDeltas, f as resolvePromptCacheKey, i as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, l as measureUtf8AppendBytes, n as reconcileOpenAICompletionsToolChoice, o as createOpenAIResponseHook, p as throwIfModelStreamAborted, r as reconcileOpenAIResponsesToolChoice, s as isOpenAICompletionsThinkingEnabled, t as projectOpenAITools, u as parseOpenAICompletionsUsage } from "./openai-tool-projection-CY04OcvQ.mjs";
26
+ import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-MK28puvq.mjs";
27
+ import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-rnPwX2pg.mjs";
28
+ import { i as resolveOpenAIResponsesServerCompactionPlan, n as resolveOpenAIResponsesCompactEndpointPlan, r as resolveOpenAIResponsesPayloadPolicy, t as applyOpenAIResponsesPayloadPolicy } from "./openai-responses-payload-policy-BDxV-W0c.mjs";
20
29
  import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-C7K7QfUE.mjs";
30
+ import { n as registerSessionResourceCleanup } from "./session-resources-CkR4WWy1.mjs";
21
31
  import { randomUUID } from "node:crypto";
22
32
  import OpenAI, { AzureOpenAI } from "openai";
23
- //#region packages/ai/src/transports/anthropic-payload-policy.ts
24
- /**
25
- * Anthropic-family request payload policy helpers.
26
- * Applies service-tier and cache-control markers only when provider endpoint
27
- * capabilities allow them.
28
- */
29
- const ANTHROPIC_CACHE_CONTROL_LIMIT = 4;
30
- function resolveBaseUrlHostname(baseUrl) {
31
- try {
32
- return new URL(baseUrl).hostname;
33
- } catch {
34
- return;
35
- }
36
- }
37
- function isLongTtlEligibleEndpoint(baseUrl) {
38
- if (typeof baseUrl !== "string") return false;
39
- const hostname = resolveBaseUrlHostname(baseUrl);
40
- if (!hostname) return false;
41
- return hostname === "api.anthropic.com" || hostname === "aiplatform.googleapis.com" || hostname.endsWith("-aiplatform.googleapis.com");
42
- }
43
- /** Resolve Anthropic cache-control marker retention for a request endpoint. */
44
- function resolveAnthropicEphemeralCacheControl(baseUrl, cacheRetention) {
45
- const retention = resolveCacheRetention(cacheRetention);
46
- if (retention === "none") return;
47
- const ttl = retention === "long" && (cacheRetention === "long" || isLongTtlEligibleEndpoint(baseUrl)) ? "1h" : void 0;
48
- return {
49
- type: "ephemeral",
50
- ...ttl ? { ttl } : {}
51
- };
52
- }
53
- function applyAnthropicCacheControlToSystem(system, cacheControl) {
54
- if (!Array.isArray(system)) return;
55
- const normalizedBlocks = [];
56
- for (const block of system) {
57
- if (!block || typeof block !== "object") {
58
- normalizedBlocks.push(block);
59
- continue;
60
- }
61
- const record = block;
62
- if (record.type !== "text" || typeof record.text !== "string") {
63
- normalizedBlocks.push(block);
64
- continue;
65
- }
66
- const split = splitSystemPromptCacheBoundary(record.text);
67
- if (!split) {
68
- if (record.cache_control === void 0) record.cache_control = cacheControl;
69
- normalizedBlocks.push(record);
70
- continue;
71
- }
72
- const { cache_control: existingCacheControl, ...rest } = record;
73
- if (split.stablePrefix) normalizedBlocks.push({
74
- ...rest,
75
- text: split.stablePrefix,
76
- cache_control: existingCacheControl ?? cacheControl
77
- });
78
- if (split.dynamicSuffix) normalizedBlocks.push({
79
- ...rest,
80
- text: split.dynamicSuffix
81
- });
82
- }
83
- system.splice(0, system.length, ...normalizedBlocks);
84
- }
85
- function stripAnthropicSystemPromptBoundary(system) {
86
- if (!Array.isArray(system)) return;
87
- for (const block of system) {
88
- if (!block || typeof block !== "object") continue;
89
- const record = block;
90
- if (record.type === "text" && typeof record.text === "string") record.text = stripSystemPromptCacheBoundary(record.text);
91
- }
92
- }
93
- function applyAnthropicCacheControlToMessages(messages, cacheControl, markerLimit, cacheBreakpointOptOutMessageIndexes) {
94
- if (!Array.isArray(messages) || messages.length === 0 || markerLimit <= 0) return;
95
- let fallbackToolResult;
96
- for (let i = messages.length - 1; i >= 0; i--) {
97
- const message = messages[i];
98
- if (!message || typeof message !== "object") continue;
99
- const record = message;
100
- if (record.role !== "user" || cacheBreakpointOptOutMessageIndexes.has(i)) continue;
101
- const content = record.content;
102
- if (typeof content === "string") {
103
- if (fallbackToolResult && markerLimit === 1) {
104
- fallbackToolResult.cache_control = cacheControl;
105
- return;
106
- }
107
- record.content = [{
108
- type: "text",
109
- text: content,
110
- cache_control: cacheControl
111
- }];
112
- if (fallbackToolResult && markerLimit > 1) fallbackToolResult.cache_control = cacheControl;
113
- return;
114
- }
115
- if (!Array.isArray(content)) continue;
116
- for (let j = content.length - 1; j >= 0; j--) {
117
- const block = content[j];
118
- if (!block || typeof block !== "object") continue;
119
- const blockRecord = block;
120
- if (blockRecord.type === "text" || blockRecord.type === "image") {
121
- if (fallbackToolResult && markerLimit === 1) {
122
- fallbackToolResult.cache_control = cacheControl;
123
- return;
124
- }
125
- blockRecord.cache_control = cacheControl;
126
- if (fallbackToolResult && markerLimit > 1) fallbackToolResult.cache_control = cacheControl;
127
- return;
128
- }
129
- if (blockRecord.type === "tool_result" && fallbackToolResult === void 0) fallbackToolResult = blockRecord;
130
- }
131
- }
132
- if (fallbackToolResult) fallbackToolResult.cache_control = cacheControl;
133
- }
134
- function countAnthropicCacheControlMarkers(blocks) {
135
- if (!Array.isArray(blocks)) return 0;
136
- let count = 0;
137
- for (const block of blocks) if (block && typeof block === "object" && "cache_control" in block) count += 1;
138
- return count;
139
- }
140
- /** @deprecated Anthropic-family provider payload helper; do not use from third-party plugins. */
141
- function resolveAnthropicPayloadPolicy(input) {
142
- return {
143
- allowsServiceTier: resolveProviderRequestCapabilities({
144
- provider: input.provider,
145
- api: input.api,
146
- baseUrl: input.baseUrl,
147
- capability: "llm",
148
- transport: "stream"
149
- }).allowsAnthropicServiceTier,
150
- cacheControl: input.enableCacheControl === true ? resolveAnthropicEphemeralCacheControl(input.baseUrl, input.cacheRetention) : void 0,
151
- serviceTier: input.serviceTier
152
- };
153
- }
154
- /** @deprecated Anthropic-family provider payload helper; do not use from third-party plugins. */
155
- function applyAnthropicPayloadPolicyToParams(payloadObj, policy, cacheBreakpointOptOutMessageIndexes) {
156
- if (policy.allowsServiceTier && policy.serviceTier !== void 0 && payloadObj.service_tier === void 0) payloadObj.service_tier = policy.serviceTier;
157
- if (policy.cacheControl) applyAnthropicCacheControlToSystem(payloadObj.system, policy.cacheControl);
158
- else stripAnthropicSystemPromptBoundary(payloadObj.system);
159
- if (!policy.cacheControl) return;
160
- const usedMarkers = countAnthropicCacheControlMarkers(payloadObj.system) + countAnthropicCacheControlMarkers(payloadObj.tools);
161
- applyAnthropicCacheControlToMessages(payloadObj.messages, policy.cacheControl, ANTHROPIC_CACHE_CONTROL_LIMIT - usedMarkers, cacheBreakpointOptOutMessageIndexes);
162
- }
163
- /** @deprecated Anthropic-family provider payload helper; do not use from third-party plugins. */
164
- function applyAnthropicEphemeralCacheControlMarkers(payloadObj, cacheControl = { type: "ephemeral" }) {
165
- const messages = payloadObj.messages;
166
- if (!Array.isArray(messages)) return;
167
- for (const message of messages) {
168
- if (message.role === "system" || message.role === "developer") {
169
- if (!cacheControl) continue;
170
- if (typeof message.content === "string") {
171
- message.content = [{
172
- type: "text",
173
- text: message.content,
174
- cache_control: cacheControl
175
- }];
176
- continue;
177
- }
178
- if (Array.isArray(message.content) && message.content.length > 0) {
179
- const last = message.content[message.content.length - 1];
180
- if (last && typeof last === "object") {
181
- const record = last;
182
- if (record.type !== "thinking" && record.type !== "redacted_thinking") record.cache_control = cacheControl;
183
- }
184
- }
185
- continue;
186
- }
187
- if (message.role === "assistant" && Array.isArray(message.content)) for (const block of message.content) {
188
- if (!block || typeof block !== "object") continue;
189
- const record = block;
190
- if (record.type === "thinking" || record.type === "redacted_thinking") delete record.cache_control;
191
- }
192
- }
193
- }
194
- //#endregion
195
- //#region packages/ai/src/transports/json-unsafe-integers.ts
196
- /**
197
- * JSON parsing helpers that preserve integer literals larger than
198
- * Number.MAX_SAFE_INTEGER as strings before JSON.parse can round them.
199
- */
200
- const MAX_SAFE_INTEGER_ABS_STR = String(Number.MAX_SAFE_INTEGER);
201
- function isAsciiDigit(ch) {
202
- return ch !== void 0 && ch >= "0" && ch <= "9";
203
- }
204
- function parseJsonNumberToken(input, start) {
205
- let idx = start;
206
- if (input[idx] === "-") idx += 1;
207
- if (idx >= input.length) return null;
208
- if (input[idx] === "0") idx += 1;
209
- else if (isAsciiDigit(input[idx]) && input[idx] !== "0") while (isAsciiDigit(input[idx])) idx += 1;
210
- else return null;
211
- let isInteger = true;
212
- if (input[idx] === ".") {
213
- isInteger = false;
214
- idx += 1;
215
- if (!isAsciiDigit(input[idx])) return null;
216
- while (isAsciiDigit(input[idx])) idx += 1;
217
- }
218
- if (input[idx] === "e" || input[idx] === "E") {
219
- isInteger = false;
220
- idx += 1;
221
- if (input[idx] === "+" || input[idx] === "-") idx += 1;
222
- if (!isAsciiDigit(input[idx])) return null;
223
- while (isAsciiDigit(input[idx])) idx += 1;
224
- }
225
- return {
226
- token: input.slice(start, idx),
227
- end: idx,
228
- isInteger
229
- };
230
- }
231
- function isUnsafeIntegerLiteral(token) {
232
- const digits = token[0] === "-" ? token.slice(1) : token;
233
- if (digits.length < MAX_SAFE_INTEGER_ABS_STR.length) return false;
234
- if (digits.length > MAX_SAFE_INTEGER_ABS_STR.length) return true;
235
- return digits > MAX_SAFE_INTEGER_ABS_STR;
236
- }
237
- /** Quotes integer literals above Number.MAX_SAFE_INTEGER before JSON.parse. */
238
- function quoteUnsafeIntegerLiterals(input) {
239
- let out = "";
240
- let inString = false;
241
- let escaped = false;
242
- let idx = 0;
243
- while (idx < input.length) {
244
- const ch = input[idx] ?? "";
245
- if (inString) {
246
- out += ch;
247
- if (escaped) escaped = false;
248
- else if (ch === "\\") escaped = true;
249
- else if (ch === "\"") inString = false;
250
- idx += 1;
251
- continue;
252
- }
253
- if (ch === "\"") {
254
- inString = true;
255
- out += ch;
256
- idx += 1;
257
- continue;
258
- }
259
- if (ch === "-" || isAsciiDigit(ch)) {
260
- const parsed = parseJsonNumberToken(input, idx);
261
- if (parsed) {
262
- if (parsed.isInteger && isUnsafeIntegerLiteral(parsed.token)) out += `"${parsed.token}"`;
263
- else out += parsed.token;
264
- idx = parsed.end;
265
- continue;
266
- }
267
- }
268
- out += ch;
269
- idx += 1;
270
- }
271
- return out;
272
- }
273
- /** Parses JSON while preserving unsafe integer literals as strings. */
274
- function parseJsonPreservingUnsafeIntegers(input) {
275
- return JSON.parse(quoteUnsafeIntegerLiterals(input));
276
- }
277
- /** Parses or accepts an object while preserving unsafe integer literals in string input. */
278
- function parseJsonObjectPreservingUnsafeIntegers(value) {
279
- if (typeof value === "string") {
280
- try {
281
- const parsed = parseJsonPreservingUnsafeIntegers(value);
282
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
283
- } catch {
284
- return null;
285
- }
286
- return null;
287
- }
288
- if (value && typeof value === "object" && !Array.isArray(value)) return value;
289
- return null;
290
- }
291
- //#endregion
33
+ import { ResponsesWS } from "openai/resources/responses/ws.js";
292
34
  //#region packages/ai/src/transports/anthropic-transport-stream.ts
293
35
  /**
294
36
  * Native Anthropic Messages streaming transport.
295
37
  * Converts OpenClaw contexts/tools into Anthropic payloads, streams SSE events
296
38
  * back into runtime output blocks, and applies provider request policy.
297
39
  */
298
- const CLAUDE_CODE_VERSION = "2.1.75";
299
- const CLAUDE_CODE_BILLING_SYSTEM_BLOCK = `x-anthropic-billing-header: cc_version=${CLAUDE_CODE_VERSION}; cc_entrypoint=sdk-cli;`;
300
40
  const ANTHROPIC_MESSAGES_ERROR_BODY_MAX_BYTES = 8 * 1024;
301
41
  const ANTHROPIC_MESSAGES_ERROR_BODY_MAX_CHARS = 400;
302
42
  const ANTHROPIC_MESSAGES_ERROR_BODY_READ_IDLE_TIMEOUT_MS = 1e4;
303
43
  const ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS = 4096;
304
44
  const ANTHROPIC_MESSAGES_FALLBACK_CONTEXT_DIVISOR = 4;
305
45
  const ANTHROPIC_MESSAGES_SSE_PENDING_BUFFER_MAX_CHARS = 16 * 1024 * 1024;
306
- const CLAUDE_CODE_TOOL_LOOKUP = new Map([
307
- "Read",
308
- "Write",
309
- "Edit",
310
- "Bash",
311
- "Grep",
312
- "Glob",
313
- "AskUserQuestion",
314
- "EnterPlanMode",
315
- "ExitPlanMode",
316
- "KillShell",
317
- "NotebookEdit",
318
- "Skill",
319
- "Task",
320
- "TaskOutput",
321
- "TodoWrite",
322
- "WebFetch",
323
- "WebSearch"
324
- ].map((tool) => [normalizeLowercaseStringOrEmpty(tool), tool]));
325
46
  function resolveAnthropicRequestModelId(model) {
326
47
  if (isDirectAnthropicModel(model) && /^anthropic\//i.test(model.id)) return model.id.replace(/^anthropic\//i, "");
327
48
  return model.id;
328
49
  }
329
50
  const EMPTY_ANTHROPIC_MESSAGES_FALLBACK_TEXT = ".";
330
- function normalizeAnthropicToolChoice(thinkingEnabled, toolChoice) {
331
- if (thinkingEnabled && (toolChoice === "any" || typeof toolChoice === "object" && toolChoice.type === "tool")) return { type: "auto" };
332
- return typeof toolChoice === "string" ? { type: toolChoice } : toolChoice;
333
- }
334
- function supportsNativeXhighEffort(model) {
335
- return supportsClaudeNativeXhighEffort(model);
336
- }
337
- function supportsAdaptiveThinking(model) {
338
- return supportsClaudeAdaptiveThinking(model);
339
- }
340
- function mapThinkingLevelToEffort(level, model) {
341
- const thinkingLevelMap = resolveClaudeNativeThinkingLevelMap(model);
342
- const resolvedLevel = clampThinkingLevel({
343
- ...model,
344
- ...typeof model.params?.canonicalModelId === "string" ? { reasoning: true } : {},
345
- ...thinkingLevelMap ? { thinkingLevelMap } : {}
346
- }, level);
347
- const mapped = thinkingLevelMap?.[resolvedLevel];
348
- if (typeof mapped === "string") return mapped;
349
- switch (resolvedLevel) {
350
- case "off":
351
- case "minimal":
352
- case "low": return "low";
353
- case "medium": return "medium";
354
- case "xhigh": return supportsNativeXhighEffort(model) ? "xhigh" : "high";
355
- case "max": return supportsClaudeNativeMaxEffort(model) ? "max" : "high";
356
- default: return "high";
357
- }
358
- }
359
- function clampReasoningLevel(level) {
360
- return level === "xhigh" || level === "max" ? "high" : level;
361
- }
362
51
  function resolvePositiveAnthropicTokenLimit(value) {
363
52
  if (typeof value !== "number" || !Number.isFinite(value)) return;
364
53
  const floored = Math.floor(value);
@@ -373,23 +62,6 @@ function resolveAnthropicMessagesMaxTokens(params) {
373
62
  const contextWindow = resolvePositiveAnthropicTokenLimit(params.modelContextWindow);
374
63
  return contextWindow === void 0 ? ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS : Math.max(1, Math.min(ANTHROPIC_MESSAGES_DEFAULT_MAX_TOKENS, Math.floor(contextWindow / ANTHROPIC_MESSAGES_FALLBACK_CONTEXT_DIVISOR)));
375
64
  }
376
- function adjustMaxTokensForThinking(params) {
377
- const budgets = {
378
- minimal: 1024,
379
- low: 2048,
380
- medium: 8192,
381
- high: 16384,
382
- ...params.customBudgets
383
- };
384
- const minOutputTokens = 1024;
385
- let thinkingBudget = budgets[clampReasoningLevel(params.reasoningLevel)];
386
- const maxTokens = Math.min(params.baseMaxTokens + thinkingBudget, params.modelMaxTokens);
387
- if (maxTokens <= thinkingBudget) thinkingBudget = Math.max(0, maxTokens - minOutputTokens);
388
- return {
389
- maxTokens,
390
- thinkingBudget
391
- };
392
- }
393
65
  function isAnthropicOAuthToken(apiKey) {
394
66
  return (resolveSecretSentinel(apiKey) ?? apiKey).includes("sk-ant-oat");
395
67
  }
@@ -416,9 +88,6 @@ function buildAnthropicBetaHeader(model, betaFeatures, params) {
416
88
  if (!isDirectAnthropicModel(model)) return;
417
89
  return params.oauth ? `claude-code-20250219,oauth-2025-04-20,${betaFeatures.join(",")}` : betaFeatures.join(",");
418
90
  }
419
- function toClaudeCodeName(name) {
420
- return CLAUDE_CODE_TOOL_LOOKUP.get(normalizeLowercaseStringOrEmpty(name)) ?? name;
421
- }
422
91
  const NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
423
92
  async function convertContentBlocks(content, model, imageBudget) {
424
93
  const text = extractToolResultText(content);
@@ -459,15 +128,12 @@ async function convertContentBlocks(content, model, imageBudget) {
459
128
  });
460
129
  return blocks;
461
130
  }
462
- function normalizeToolCallId(id) {
463
- return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
464
- }
465
131
  async function convertAnthropicMessages(messages, model, isOAuthToken, options) {
466
132
  const params = [];
467
133
  const imageBudget = createAnthropicInlineImageBudget();
468
134
  const allowReasoningContentReplay = options.allowReasoningContentReplay === true;
469
135
  const replayThinkingEnabled = options.replayThinkingEnabled !== false;
470
- const transformedMessages = transformTransportMessages(messages, model, normalizeToolCallId);
136
+ const transformedMessages = transformTransportMessages(messages, model, normalizeAnthropicToolCallId);
471
137
  const activeToolTurnAssistantIndex = replayThinkingEnabled ? -1 : findActiveAnthropicToolTurnAssistantIndex(transformedMessages);
472
138
  for (let i = 0; i < transformedMessages.length; i += 1) {
473
139
  const msg = transformedMessages[i];
@@ -511,7 +177,7 @@ async function convertAnthropicMessages(messages, model, isOAuthToken, options)
511
177
  continue;
512
178
  }
513
179
  if (msg.role === "assistant") {
514
- const blocks = [];
180
+ const blocks = i === 0 && options.compaction ? [options.compaction] : [];
515
181
  const reasoningContent = [];
516
182
  let omittedThinking = false;
517
183
  for (const block of msg.content) {
@@ -566,7 +232,7 @@ async function convertAnthropicMessages(messages, model, isOAuthToken, options)
566
232
  if (block.type === "toolCall") blocks.push({
567
233
  type: "tool_use",
568
234
  id: block.id,
569
- name: isOAuthToken ? toClaudeCodeName(block.name) : block.name,
235
+ name: isOAuthToken ? toClaudeCodeToolName(block.name) : block.name,
570
236
  input: coerceTransportToolCallArguments(block.arguments)
571
237
  });
572
238
  }
@@ -625,7 +291,7 @@ function ensureNonEmptyAnthropicMessages(messages) {
625
291
  }];
626
292
  }
627
293
  function convertAnthropicTools(tools, isOAuthToken) {
628
- const projection = projectAnthropicTools(tools ?? [], (name) => isOAuthToken ? toClaudeCodeName(name) : name);
294
+ const projection = projectAnthropicTools(tools ?? [], (name) => isOAuthToken ? toClaudeCodeToolName(name) : name);
629
295
  const converted = [];
630
296
  for (const tool of projection.tools) converted.push({
631
297
  name: tool.wireName,
@@ -640,18 +306,6 @@ function convertAnthropicTools(tools, isOAuthToken) {
640
306
  function parseAnthropicToolCallArguments(inputJson) {
641
307
  return parseJsonObjectPreservingUnsafeIntegers(inputJson) ?? parseStreamingJson(inputJson);
642
308
  }
643
- function mapStopReason(reason) {
644
- switch (reason) {
645
- case "end_turn": return "stop";
646
- case "max_tokens": return "length";
647
- case "tool_use": return "toolUse";
648
- case "pause_turn": return "stop";
649
- case "refusal":
650
- case "sensitive": return "error";
651
- case "stop_sequence": return "stop";
652
- default: throw new Error(`Unhandled stop reason: ${String(reason)}`);
653
- }
654
- }
655
309
  const DEFAULT_ANTHROPIC_BASE_URL = "https://api.anthropic.com";
656
310
  /** Resolve the effective Anthropic API base URL from model or environment. */
657
311
  function resolveAnthropicBaseUrl(baseUrl) {
@@ -794,7 +448,7 @@ async function readAnthropicMessagesErrorBodySnippet(response) {
794
448
  }
795
449
  function createAnthropicTransportClient(params) {
796
450
  const { model, context, apiKey, options } = params;
797
- const needsInterleavedBeta = (options?.interleavedThinking ?? true) && !supportsAdaptiveThinking(model);
451
+ const needsInterleavedBeta = (options?.interleavedThinking ?? true) && !supportsClaudeAdaptiveThinking(model);
798
452
  const fetch = isKimiAnthropicProvider(model.provider) && options?.thinkingEnabled === true ? buildGuardedModelFetch(model, void 0, { sanitizeSse: false }) : buildGuardedModelFetch(model);
799
453
  if (model.provider === "github-copilot") {
800
454
  const betaFeatures = needsInterleavedBeta ? ["interleaved-thinking-2025-05-14"] : [];
@@ -843,7 +497,7 @@ function createAnthropicTransportClient(params) {
843
497
  accept: "application/json",
844
498
  "anthropic-dangerous-direct-browser-access": "true",
845
499
  ...betaHeader ? { "anthropic-beta": betaHeader } : {},
846
- "user-agent": `claude-cli/${CLAUDE_CODE_VERSION}`,
500
+ "user-agent": `claude-cli/${ANTHROPIC_CLAUDE_CODE_VERSION}`,
847
501
  "x-app": "cli"
848
502
  }, model.headers, options?.headers),
849
503
  fetch
@@ -884,9 +538,15 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
884
538
  enableCacheControl: true
885
539
  });
886
540
  const cacheBreakpointOptOutMessageIndexes = /* @__PURE__ */ new Set();
887
- const messages = await convertAnthropicMessages(context.messages, model, isOAuthToken, {
541
+ const replayPlan = buildAnthropicReplayPlan(context.messages, model, {
542
+ enabled: !isOAuthToken && options?.anthropicServerCompaction === true,
543
+ authProfileId: options?.authProfileId,
544
+ sessionId: options?.sessionId
545
+ });
546
+ const messages = await convertAnthropicMessages(replayPlan.messages, model, isOAuthToken, {
888
547
  allowReasoningContentReplay: supportsReasoningContentReplay(model),
889
548
  cacheBreakpointOptOutMessageIndexes,
549
+ compaction: replayPlan.compaction,
890
550
  replayThinkingEnabled
891
551
  });
892
552
  const params = {
@@ -899,7 +559,7 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
899
559
  if (isOAuthToken) params.system = [
900
560
  {
901
561
  type: "text",
902
- text: CLAUDE_CODE_BILLING_SYSTEM_BLOCK
562
+ text: ANTHROPIC_CLAUDE_CODE_BILLING_SYSTEM_BLOCK
903
563
  },
904
564
  {
905
565
  type: "text",
@@ -914,7 +574,7 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
914
574
  type: "text",
915
575
  text: sanitizeTransportPayloadText(context.systemPrompt)
916
576
  }];
917
- if (options?.temperature !== void 0 && !options.thinkingEnabled && !supportsNativeXhighEffort(model)) params.temperature = options.temperature;
577
+ if (options?.temperature !== void 0 && !options.thinkingEnabled && !supportsClaudeNativeXhighEffort(model)) params.temperature = options.temperature;
918
578
  if (options?.stop !== void 0 && options.stop.length > 0) params.stop_sequences = options.stop;
919
579
  let toolProjection;
920
580
  if (context.tools) {
@@ -922,8 +582,8 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
922
582
  toolProjection = convertedTools.projection;
923
583
  if (convertedTools.tools.length > 0) params.tools = convertedTools.tools;
924
584
  }
925
- if (mandatoryAdaptiveThinking || model.reasoning || supportsAdaptiveThinking(model)) {
926
- if (mandatoryAdaptiveThinking || options?.thinkingEnabled) if (supportsAdaptiveThinking(model)) {
585
+ if (mandatoryAdaptiveThinking || model.reasoning || supportsClaudeAdaptiveThinking(model)) {
586
+ if (mandatoryAdaptiveThinking || options?.thinkingEnabled) if (supportsClaudeAdaptiveThinking(model)) {
927
587
  params.thinking = {
928
588
  type: "adaptive",
929
589
  display: options?.thinkingDisplay ?? "summarized"
@@ -945,7 +605,8 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
945
605
  applyAnthropicPayloadPolicyToParams(params, payloadPolicy, cacheBreakpointOptOutMessageIndexes);
946
606
  return {
947
607
  params,
948
- toolProjection
608
+ toolProjection,
609
+ usedCompactionReplay: replayPlan.compaction !== void 0
949
610
  };
950
611
  }
951
612
  function resolveAnthropicTransportOptions(model, options, apiKey) {
@@ -974,7 +635,9 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
974
635
  interleavedThinking: options?.interleavedThinking,
975
636
  toolChoice: options?.toolChoice,
976
637
  thinkingBudgets: options?.thinkingBudgets,
977
- reasoning
638
+ reasoning,
639
+ ...options?.anthropicServerCompaction === true ? { anthropicServerCompaction: true } : {},
640
+ ...options?.authProfileId ? { authProfileId: options.authProfileId } : {}
978
641
  };
979
642
  if (reasoning === "off") {
980
643
  resolved.thinkingEnabled = false;
@@ -985,17 +648,12 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
985
648
  if (resolved.thinkingEnabled) resolved.effort = "high";
986
649
  return resolved;
987
650
  }
988
- if (supportsAdaptiveThinking(model)) {
651
+ if (supportsClaudeAdaptiveThinking(model)) {
989
652
  resolved.thinkingEnabled = true;
990
- resolved.effort = mapThinkingLevelToEffort(reasoning, model);
653
+ resolved.effort = resolveAnthropicThinkingEffort(model, reasoning);
991
654
  return resolved;
992
655
  }
993
- const adjusted = adjustMaxTokensForThinking({
994
- baseMaxTokens,
995
- modelMaxTokens: reasoningModelMaxTokens,
996
- reasoningLevel: reasoning,
997
- customBudgets: options?.thinkingBudgets
998
- });
656
+ const adjusted = adjustMaxTokensForThinking(baseMaxTokens, reasoningModelMaxTokens, reasoning === "max" ? "high" : reasoning, options?.thinkingBudgets);
999
657
  const thinkingEnabled = adjusted.thinkingBudget >= 1024;
1000
658
  resolved.maxTokens = adjusted.maxTokens;
1001
659
  resolved.thinkingEnabled = thinkingEnabled;
@@ -1023,6 +681,7 @@ function createAnthropicMessagesTransportStreamFn() {
1023
681
  const eventSink = refusalBuffer ?? stream;
1024
682
  let costModel = model;
1025
683
  let messageStartPromptUsage;
684
+ let usedCompactionReplay = false;
1026
685
  try {
1027
686
  const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? "";
1028
687
  if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
@@ -1035,6 +694,7 @@ function createAnthropicMessagesTransportStreamFn() {
1035
694
  options: transportOptions
1036
695
  });
1037
696
  const builtParams = await buildAnthropicParams(model, requestContext, isOAuthToken, transportOptions);
697
+ usedCompactionReplay = builtParams.usedCompactionReplay;
1038
698
  let params = builtParams.params;
1039
699
  const toolProjection = builtParams.toolProjection;
1040
700
  const nextParams = await transportOptions.onPayload?.(params, model);
@@ -1046,6 +706,7 @@ function createAnthropicMessagesTransportStreamFn() {
1046
706
  }, transportOptions.signal ? { signal: transportOptions.signal } : void 0);
1047
707
  const blocks = output.content;
1048
708
  const blockIndexes = /* @__PURE__ */ new Map();
709
+ const compactionCapture = createCompactionCapture(output, model, transportOptions);
1049
710
  const pendingThinkingSignatures = /* @__PURE__ */ new Map();
1050
711
  const allowReasoningContentReplay = supportsReasoningContentReplay(model);
1051
712
  const reasoningContentThinkingBlocks = /* @__PURE__ */ new Map();
@@ -1153,23 +814,7 @@ function createAnthropicMessagesTransportStreamFn() {
1153
814
  const usage = message?.usage ?? {};
1154
815
  output.responseId = typeof message?.id === "string" ? message.id : void 0;
1155
816
  output.responseModel = typeof message?.model === "string" ? message.model : void 0;
1156
- const promptUsage = readAnthropicPromptUsageSnapshot(usage);
1157
- const messageStartPromptTokens = promptUsage ? promptUsage.input + promptUsage.cacheRead + promptUsage.cacheWrite : 0;
1158
- messageStartPromptUsage = messageStartPromptTokens > 0 ? promptUsage : void 0;
1159
- const inputTokens = readAnthropicUsageTokenCount(usage.input_tokens);
1160
- if (inputTokens !== void 0) output.usage.input = inputTokens;
1161
- const outputTokens = readAnthropicUsageTokenCount(usage.output_tokens);
1162
- if (outputTokens !== void 0) output.usage.output = outputTokens;
1163
- const cacheReadTokens = usage.cache_read_input_tokens == null ? 0 : readAnthropicUsageTokenCount(usage.cache_read_input_tokens);
1164
- if (cacheReadTokens !== void 0) output.usage.cacheRead = cacheReadTokens;
1165
- const cacheWriteTokens = usage.cache_creation_input_tokens == null ? 0 : readAnthropicUsageTokenCount(usage.cache_creation_input_tokens);
1166
- if (cacheWriteTokens !== void 0) output.usage.cacheWrite = cacheWriteTokens;
1167
- output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
1168
- if (messageStartPromptUsage && outputTokens !== void 0) output.usage.contextUsage = {
1169
- state: "available",
1170
- promptTokens: messageStartPromptTokens,
1171
- totalTokens: messageStartPromptTokens + output.usage.output
1172
- };
817
+ messageStartPromptUsage = applyAnthropicMessageStartUsage(output.usage, usage);
1173
818
  calculateCost(costModel, output.usage);
1174
819
  eventSink.push({
1175
820
  type: "start",
@@ -1184,6 +829,7 @@ function createAnthropicMessagesTransportStreamFn() {
1184
829
  if (event.type === "content_block_start") {
1185
830
  const contentBlock = event.content_block;
1186
831
  const index = typeof event.index === "number" ? event.index : -1;
832
+ if (transportOptions.anthropicServerCompaction === true && compactionCapture.begin(index, contentBlock, output.content.length)) continue;
1187
833
  const fallbackBoundary = refusalBuffer ? readAnthropicFallbackBoundary(contentBlock) : null;
1188
834
  if (fallbackBoundary) {
1189
835
  refusalBuffer?.discard();
@@ -1320,6 +966,7 @@ function createAnthropicMessagesTransportStreamFn() {
1320
966
  if (event.type === "content_block_delta") {
1321
967
  const delta = event.delta;
1322
968
  const eventIndex = typeof event.index === "number" ? event.index : void 0;
969
+ if (eventIndex !== void 0 && compactionCapture.delta(eventIndex, delta)) continue;
1323
970
  let index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
1324
971
  let block = index === void 0 ? void 0 : blocks[index];
1325
972
  if (allowReasoningContentReplay) {
@@ -1400,6 +1047,7 @@ function createAnthropicMessagesTransportStreamFn() {
1400
1047
  }
1401
1048
  if (event.type === "content_block_stop") {
1402
1049
  const eventIndex = typeof event.index === "number" ? event.index : void 0;
1050
+ if (eventIndex !== void 0 && compactionCapture.complete(eventIndex)) continue;
1403
1051
  const pendingSignature = eventIndex === void 0 ? void 0 : pendingThinkingSignatures.get(eventIndex);
1404
1052
  if (eventIndex !== void 0) pendingThinkingSignatures.delete(eventIndex);
1405
1053
  const index = eventIndex === void 0 ? void 0 : blockIndexes.get(eventIndex);
@@ -1447,37 +1095,14 @@ function createAnthropicMessagesTransportStreamFn() {
1447
1095
  const delta = event.delta;
1448
1096
  const usage = event.usage;
1449
1097
  if (delta?.stop_reason) if (delta.stop_reason === "refusal") applyAnthropicRefusal(output, delta.stop_details, model.provider);
1450
- else output.stopReason = mapStopReason(delta.stop_reason);
1451
- const inputTokens = readAnthropicUsageTokenCount(usage?.input_tokens);
1452
- if (inputTokens !== void 0) output.usage.input = inputTokens;
1453
- const outputTokens = readAnthropicUsageTokenCount(usage?.output_tokens);
1454
- if (outputTokens !== void 0) output.usage.output = outputTokens;
1455
- const cacheReadTokens = readAnthropicUsageTokenCount(usage?.cache_read_input_tokens);
1456
- if (cacheReadTokens !== void 0) output.usage.cacheRead = cacheReadTokens;
1457
- const cacheWriteTokens = readAnthropicUsageTokenCount(usage?.cache_creation_input_tokens);
1458
- if (cacheWriteTokens !== void 0) output.usage.cacheWrite = cacheWriteTokens;
1459
- output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
1460
- const iterationUsage = readLastAnthropicIterationUsage(usage ?? {});
1461
- if (iterationUsage.state === "valid") output.usage.contextUsage = {
1462
- state: "available",
1463
- promptTokens: iterationUsage.usage.contextPromptTokens,
1464
- totalTokens: iterationUsage.usage.totalTokens
1465
- };
1466
- else if (iterationUsage.state === "invalid") output.usage.contextUsage = { state: "unavailable" };
1467
- else if (outputTokens !== void 0 && (messageStartPromptUsage !== void 0 || inputTokens !== void 0 && cacheReadTokens !== void 0 && cacheWriteTokens !== void 0)) {
1468
- const promptTokens = output.usage.input + output.usage.cacheRead + output.usage.cacheWrite;
1469
- output.usage.contextUsage = {
1470
- state: "available",
1471
- promptTokens,
1472
- totalTokens: promptTokens + output.usage.output
1473
- };
1474
- } else output.usage.contextUsage = { state: "unavailable" };
1098
+ else output.stopReason = mapAnthropicStopReason(delta.stop_reason);
1099
+ applyAnthropicMessageDeltaUsage(output.usage, usage, messageStartPromptUsage);
1475
1100
  calculateCost(costModel, output.usage);
1476
1101
  if (output.stopReason === "toolUse" || output.content.some((block) => block.type === "toolCall")) tagPendingCommentaryText(output.content);
1477
1102
  flushPendingTextEnds();
1478
1103
  }
1479
1104
  }
1480
- if (refusalBuffer && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
1105
+ if (isDirectAnthropicModel(model) && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
1481
1106
  if (transportOptions.signal?.aborted) throw transportAbortError(transportOptions.signal);
1482
1107
  if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
1483
1108
  refusalBuffer?.flush();
@@ -1492,6 +1117,7 @@ function createAnthropicMessagesTransportStreamFn() {
1492
1117
  refusalBuffer.discard();
1493
1118
  output.content = [];
1494
1119
  }
1120
+ if (usedCompactionReplay && isAnthropicReplayRejection(error)) suppressAnthropicCompaction(output, model, options);
1495
1121
  failTransportStream({
1496
1122
  stream,
1497
1123
  output,
@@ -1608,15 +1234,11 @@ const MAX_TOKENS_PARAM_KEYS = [
1608
1234
  "max_completion_tokens",
1609
1235
  "max_tokens"
1610
1236
  ];
1611
- /** Return a finite non-negative max-token value, or undefined for invalid input. */
1612
- function resolveNonNegativeMaxTokensParam(value) {
1613
- return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1614
- }
1615
1237
  /** Resolve the first supported max-token parameter present in a params object. */
1616
1238
  function resolveMaxTokensParam(params) {
1617
1239
  if (!params) return;
1618
1240
  for (const key of MAX_TOKENS_PARAM_KEYS) {
1619
- const resolved = resolveNonNegativeMaxTokensParam(params[key]);
1241
+ const resolved = asNonNegativeFiniteNumber(params[key]);
1620
1242
  if (resolved !== void 0) return resolved;
1621
1243
  }
1622
1244
  }
@@ -1733,100 +1355,279 @@ function stripCompletionMessagesToRoleContent(messages) {
1733
1355
  });
1734
1356
  }
1735
1357
  //#endregion
1736
- //#region packages/ai/src/transports/openai-transport-params.ts
1737
- const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
1738
- const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
1739
- const loggedOpenAIStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
1740
- function readToolPayloadField(record, field) {
1741
- try {
1742
- return Object.hasOwn(record, field) ? record[field] : void 0;
1743
- } catch {
1744
- return;
1745
- }
1358
+ //#region packages/ai/src/transports/openai-completions-host.ts
1359
+ /**
1360
+ * Chat Completions accepts Azure AI Foundry hosts in addition to traditional
1361
+ * Azure OpenAI hosts. Do not replace this with isTraditionalAzureOpenAIHost,
1362
+ * which intentionally excludes the .services.ai.azure.com Foundry suffix.
1363
+ */
1364
+ function isAzureOpenAICompatibleHost(hostname) {
1365
+ return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
1746
1366
  }
1747
- function readCodeModePayloadToolName(tool) {
1748
- if (!isRecord(tool)) return;
1749
- const name = readToolPayloadField(tool, "name");
1750
- if (typeof name === "string") return name;
1751
- const fn = readToolPayloadField(tool, "function");
1752
- if (!isRecord(fn)) return;
1753
- const fnName = readToolPayloadField(fn, "name");
1754
- return typeof fnName === "string" ? fnName : void 0;
1367
+ //#endregion
1368
+ //#region packages/ai/src/transports/openai-completions-replay.ts
1369
+ function isGoogleOpenAICompatModel(model) {
1370
+ const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
1371
+ return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
1755
1372
  }
1756
- function filterCodeModePayloadTools(payload, visibleToolNames) {
1757
- if (!isRecord(payload)) return;
1758
- const tools = readToolPayloadField(payload, "tools");
1759
- if (!Array.isArray(tools)) return;
1760
- payload.tools = tools.flatMap((tool) => {
1761
- const name = readCodeModePayloadToolName(tool);
1762
- if (typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames)) return [tool];
1763
- if (!isRecord(tool)) return [];
1764
- const filteredGroups = {};
1765
- for (const key of ["functionDeclarations", "function_declarations"]) {
1766
- const declarations = readToolPayloadField(tool, key);
1767
- if (!Array.isArray(declarations)) continue;
1768
- const filtered = declarations.filter((declaration) => {
1769
- const declarationName = readCodeModePayloadToolName(declaration);
1770
- return typeof declarationName === "string" && isCodeModeModelVisibleToolName(declarationName, visibleToolNames);
1771
- });
1772
- if (filtered.length > 0) filteredGroups[key] = filtered;
1773
- }
1774
- return Object.keys(filteredGroups).length > 0 ? [filteredGroups] : [];
1775
- });
1373
+ function requiresGoogleCompatToolCallThoughtSignature(model) {
1374
+ return isGoogleGemini3ProModel(model.id) || isGoogleGemini3FlashModel(model.id);
1776
1375
  }
1777
- function resolveCodeModeResponsesVisibleToolNames(context) {
1778
- return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
1376
+ const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
1377
+ const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
1378
+ function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
1379
+ return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
1779
1380
  }
1780
- function enforceCodeModeResponsesToolSurface(payload, visibleToolNames) {
1781
- if (!isRecord(payload)) return;
1782
- const tools = readToolPayloadField(payload, "tools");
1783
- if (!Array.isArray(tools)) return;
1784
- payload.tools = tools.filter((tool) => {
1785
- const name = readCodeModePayloadToolName(tool);
1786
- return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames);
1787
- });
1381
+ function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
1382
+ if (!isGoogleOpenAICompatModel(model)) return;
1383
+ const sigById = /* @__PURE__ */ new Map();
1384
+ const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
1385
+ for (const msg of context.messages ?? []) {
1386
+ if (msg.role !== "assistant") continue;
1387
+ const source = msg;
1388
+ if (!Array.isArray(source.content)) continue;
1389
+ for (const block of source.content) {
1390
+ if (block.type !== "toolCall") continue;
1391
+ const id = block.id;
1392
+ const sig = block.thoughtSignature;
1393
+ if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
1394
+ const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
1395
+ if (!isSameRoute && !fallbackSig) continue;
1396
+ sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
1397
+ }
1398
+ }
1399
+ }
1400
+ if (sigById.size === 0 && !fallbackSig) return;
1401
+ for (const message of outgoingMessages) {
1402
+ const toolCalls = message.tool_calls;
1403
+ if (!Array.isArray(toolCalls)) continue;
1404
+ for (const toolCall of toolCalls) {
1405
+ const id = toolCall.id;
1406
+ if (typeof id !== "string") continue;
1407
+ let sig = sigById.get(id) ?? fallbackSig;
1408
+ if (typeof sig === "string" && sig.length > 0) {
1409
+ if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
1410
+ }
1411
+ if (typeof sig !== "string" || sig.length === 0) continue;
1412
+ const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
1413
+ toolCall.extra_content = extra;
1414
+ const google = extra.google && typeof extra.google === "object" ? extra.google : {};
1415
+ extra.google = google;
1416
+ google.thought_signature = sig;
1417
+ }
1418
+ }
1788
1419
  }
1789
- function assertCodeModeResponsesToolSurface(payload, visibleToolNames) {
1790
- const tools = isRecord(payload) ? readToolPayloadField(payload, "tools") : void 0;
1791
- if (!Array.isArray(tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
1792
- const names = tools.map(readCodeModePayloadToolName).filter((name) => typeof name === "string" && name.length > 0).toSorted((left, right) => left.localeCompare(right));
1793
- 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;
1794
- throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
1420
+ const COMPLETIONS_REASONING_REPLAY_FIELDS = [
1421
+ "reasoning_details",
1422
+ "reasoning_content",
1423
+ "reasoning",
1424
+ "reasoning_text"
1425
+ ];
1426
+ function stripCompletionsReasoningReplayFields(record) {
1427
+ for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
1795
1428
  }
1796
- function buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context) {
1797
- return sha256Hex(JSON.stringify({
1798
- transport: context.transport,
1799
- provider: context.model.provider ?? null,
1800
- model: context.model.id ?? null,
1801
- diagnostics: diagnostics.map((entry) => ({
1802
- toolIndex: entry.toolIndex,
1803
- toolName: entry.toolName ?? null,
1804
- violations: entry.violations
1805
- }))
1806
- }));
1429
+ function sanitizeOpenRouterReasoningReplayFields(record) {
1430
+ const reasoningDetails = record.reasoning_details;
1431
+ if (typeof reasoningDetails === "string") {
1432
+ if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
1433
+ delete record.reasoning_details;
1434
+ } else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
1435
+ if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
1436
+ if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
1437
+ const reasoningText = record.reasoning_text;
1438
+ if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
1439
+ if ("reasoning_text" in record) delete record.reasoning_text;
1807
1440
  }
1808
- function shouldLogOpenAIStrictToolDowngradeDiagnostic(diagnostics, context) {
1809
- const key = buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context);
1810
- if (loggedOpenAIStrictToolDowngradeDiagnosticKeys.has(key)) return false;
1811
- if (loggedOpenAIStrictToolDowngradeDiagnosticKeys.size >= MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS) loggedOpenAIStrictToolDowngradeDiagnosticKeys.clear();
1812
- loggedOpenAIStrictToolDowngradeDiagnosticKeys.add(key);
1813
- return true;
1441
+ function sanitizeReasoningContentReplayFields(record) {
1442
+ if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
1443
+ delete record.reasoning_details;
1444
+ delete record.reasoning;
1445
+ delete record.reasoning_text;
1814
1446
  }
1815
- function resolveOpenAIStrictToolFlagWithDiagnostics(projection, strictSetting, context) {
1816
- const strict = resolveOpenAIProjectedToolsStrictToolFlag(projection, strictSetting);
1817
- if (strictSetting === true && strict === false) {
1818
- const diagnostics = findOpenAIStrictToolProjectionDiagnostics(projection);
1819
- getAiTransportHost().logDebug("openai-transport", () => {
1820
- if (!shouldLogOpenAIStrictToolDowngradeDiagnostic(diagnostics, context)) return null;
1821
- const sample = diagnostics.slice(0, 5).map((entry) => ({
1822
- tool: entry.toolName ?? `tool[${entry.toolIndex}]`,
1823
- violations: entry.violations.slice(0, 8)
1824
- }));
1825
- return {
1826
- message: `OpenAI ${context.transport} tool schema strict mode downgraded to strict=false for ${context.model.provider ?? "unknown"}/${context.model.id ?? "unknown"} because ${diagnostics.length} tool schema(s) are not strict-compatible`,
1827
- data: {
1828
- transport: context.transport,
1829
- provider: context.model.provider,
1447
+ const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
1448
+ "deepseek-v4-flash",
1449
+ "deepseek-v4-pro",
1450
+ "kimi-for-coding",
1451
+ "kimi-k2.5",
1452
+ "kimi-k2.6",
1453
+ "kimi-k2.7-code",
1454
+ "kimi-k2.7-code-highspeed",
1455
+ "kimi-k3",
1456
+ "kimi-k2-thinking",
1457
+ "kimi-k2-thinking-turbo",
1458
+ "mimo-v2-pro",
1459
+ "mimo-v2-omni",
1460
+ "mimo-v2.5",
1461
+ "mimo-v2.5-pro",
1462
+ "mimo-v2.6-pro"
1463
+ ]);
1464
+ const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
1465
+ "-free",
1466
+ "-paid",
1467
+ "-trial"
1468
+ ];
1469
+ function stripReasoningContentReplayTierSuffix(modelId) {
1470
+ for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
1471
+ return modelId;
1472
+ }
1473
+ function getReasoningContentReplayModelIdCandidates(modelId) {
1474
+ if (typeof modelId !== "string") return [];
1475
+ const normalized = modelId.trim().toLowerCase();
1476
+ if (!normalized) return [];
1477
+ const parts = normalized.split("/").filter(Boolean);
1478
+ const finalPart = parts[parts.length - 1] ?? normalized;
1479
+ const candidates = [finalPart];
1480
+ const colonParts = finalPart.split(":").filter(Boolean);
1481
+ if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
1482
+ const baseCount = candidates.length;
1483
+ for (let index = 0; index < baseCount; index += 1) {
1484
+ const candidate = candidates[index];
1485
+ if (typeof candidate !== "string") continue;
1486
+ const stripped = stripReasoningContentReplayTierSuffix(candidate);
1487
+ if (stripped !== candidate) candidates.push(stripped);
1488
+ }
1489
+ return uniqueStrings(candidates.filter(Boolean));
1490
+ }
1491
+ function shouldPreserveReasoningContentReplay(model, compat) {
1492
+ if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
1493
+ return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
1494
+ }
1495
+ function shouldPreserveOpenRouterReasoningReplay(model) {
1496
+ if (model.provider !== "openrouter") return true;
1497
+ const normalizedModelId = model.id.trim().toLowerCase();
1498
+ return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
1499
+ }
1500
+ function shouldTrustReasoningContentReplayMetadata(model) {
1501
+ if (!model.reasoning) return false;
1502
+ if (model.provider.trim().toLowerCase() === "openai") return false;
1503
+ return shouldPreserveOpenRouterReasoningReplay(model);
1504
+ }
1505
+ function sanitizeCompletionsReasoningReplayFields(messages, options) {
1506
+ if (!Array.isArray(messages)) return;
1507
+ for (const msg of messages) {
1508
+ if (!msg || typeof msg !== "object") continue;
1509
+ const record = msg;
1510
+ if (record.role !== "assistant") continue;
1511
+ if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
1512
+ else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
1513
+ else stripCompletionsReasoningReplayFields(record);
1514
+ }
1515
+ }
1516
+ function applyCompletionsReplay(outgoingMessages, context, model, compat) {
1517
+ injectToolCallThoughtSignatures(outgoingMessages, context, model);
1518
+ sanitizeCompletionsReasoningReplayFields(outgoingMessages, {
1519
+ preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
1520
+ preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
1521
+ });
1522
+ }
1523
+ //#endregion
1524
+ //#region packages/ai/src/transports/openai-transport-params.ts
1525
+ const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
1526
+ const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
1527
+ const loggedOpenAIStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
1528
+ function readToolPayloadField(record, field) {
1529
+ try {
1530
+ return Object.hasOwn(record, field) ? record[field] : void 0;
1531
+ } catch {
1532
+ return;
1533
+ }
1534
+ }
1535
+ function readCodeModePayloadToolName(tool) {
1536
+ if (!isRecord(tool)) return;
1537
+ const name = readToolPayloadField(tool, "name");
1538
+ if (typeof name === "string") return name;
1539
+ const fn = readToolPayloadField(tool, "function");
1540
+ if (!isRecord(fn)) return;
1541
+ const fnName = readToolPayloadField(fn, "name");
1542
+ return typeof fnName === "string" ? fnName : void 0;
1543
+ }
1544
+ function readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes) {
1545
+ if (!isRecord(tool)) return;
1546
+ const type = readToolPayloadField(tool, "type");
1547
+ if (typeof type === "string" && allowedHostedToolTypes?.has(type)) {
1548
+ try {
1549
+ if (Object.hasOwn(tool, "name") || Object.hasOwn(tool, "function") || Object.hasOwn(tool, "functionDeclarations") || Object.hasOwn(tool, "function_declarations")) return false;
1550
+ } catch {
1551
+ return false;
1552
+ }
1553
+ return `hosted:${type}`;
1554
+ }
1555
+ const name = readCodeModePayloadToolName(tool);
1556
+ return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames) ? `client:${name}` : void 0;
1557
+ }
1558
+ function filterCodeModePayloadTools(payload, visibleToolNames, allowedHostedToolTypes) {
1559
+ if (!isRecord(payload)) return;
1560
+ const tools = readToolPayloadField(payload, "tools");
1561
+ if (!Array.isArray(tools)) return;
1562
+ payload.tools = tools.flatMap((tool) => {
1563
+ const identity = readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes);
1564
+ if (identity) return [tool];
1565
+ if (identity === false) return [];
1566
+ if (!isRecord(tool)) return [];
1567
+ const filteredGroups = {};
1568
+ for (const key of ["functionDeclarations", "function_declarations"]) {
1569
+ const declarations = readToolPayloadField(tool, key);
1570
+ if (!Array.isArray(declarations)) continue;
1571
+ const filtered = declarations.filter((declaration) => {
1572
+ const declarationName = readCodeModePayloadToolName(declaration);
1573
+ return typeof declarationName === "string" && isCodeModeModelVisibleToolName(declarationName, visibleToolNames);
1574
+ });
1575
+ if (filtered.length > 0) filteredGroups[key] = filtered;
1576
+ }
1577
+ return Object.keys(filteredGroups).length > 0 ? [filteredGroups] : [];
1578
+ });
1579
+ }
1580
+ function resolveCodeModeResponsesVisibleToolNames(context) {
1581
+ return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
1582
+ }
1583
+ function enforceCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
1584
+ if (!isRecord(payload)) return;
1585
+ const tools = readToolPayloadField(payload, "tools");
1586
+ if (!Array.isArray(tools)) return;
1587
+ payload.tools = tools.filter((tool) => Boolean(readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes)));
1588
+ }
1589
+ function assertCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
1590
+ const tools = isRecord(payload) ? readToolPayloadField(payload, "tools") : void 0;
1591
+ if (!Array.isArray(tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
1592
+ const identities = tools.map((tool) => readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes));
1593
+ const names = identities.flatMap((identity) => typeof identity === "string" && identity.startsWith("client:") ? [identity.slice(7)] : []).toSorted((left, right) => left.localeCompare(right));
1594
+ if (names.length >= 2 && identities.every((identity) => typeof identity === "string") && new Set(identities).size === identities.length && names.includes("exec") && names.includes("wait")) return;
1595
+ throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
1596
+ }
1597
+ function buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context) {
1598
+ return sha256Hex(JSON.stringify({
1599
+ transport: context.transport,
1600
+ provider: context.model.provider ?? null,
1601
+ model: context.model.id ?? null,
1602
+ diagnostics: diagnostics.map((entry) => ({
1603
+ toolIndex: entry.toolIndex,
1604
+ toolName: entry.toolName ?? null,
1605
+ violations: entry.violations
1606
+ }))
1607
+ }));
1608
+ }
1609
+ function shouldLogOpenAIStrictToolDowngradeDiagnostic(diagnostics, context) {
1610
+ const key = buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context);
1611
+ if (loggedOpenAIStrictToolDowngradeDiagnosticKeys.has(key)) return false;
1612
+ if (loggedOpenAIStrictToolDowngradeDiagnosticKeys.size >= MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS) loggedOpenAIStrictToolDowngradeDiagnosticKeys.clear();
1613
+ loggedOpenAIStrictToolDowngradeDiagnosticKeys.add(key);
1614
+ return true;
1615
+ }
1616
+ function resolveOpenAIStrictToolFlagWithDiagnostics(projection, strictSetting, context) {
1617
+ const strict = resolveOpenAIProjectedToolsStrictToolFlag(projection, strictSetting);
1618
+ if (strictSetting === true && strict === false) {
1619
+ const diagnostics = findOpenAIStrictToolProjectionDiagnostics(projection);
1620
+ getAiTransportHost().logDebug("openai-transport", () => {
1621
+ if (!shouldLogOpenAIStrictToolDowngradeDiagnostic(diagnostics, context)) return null;
1622
+ const sample = diagnostics.slice(0, 5).map((entry) => ({
1623
+ tool: entry.toolName ?? `tool[${entry.toolIndex}]`,
1624
+ violations: entry.violations.slice(0, 8)
1625
+ }));
1626
+ return {
1627
+ message: `OpenAI ${context.transport} tool schema strict mode downgraded to strict=false for ${context.model.provider ?? "unknown"}/${context.model.id ?? "unknown"} because ${diagnostics.length} tool schema(s) are not strict-compatible`,
1628
+ data: {
1629
+ transport: context.transport,
1630
+ provider: context.model.provider,
1830
1631
  model: context.model.id,
1831
1632
  incompatibleToolCount: diagnostics.length,
1832
1633
  sample
@@ -1912,64 +1713,7 @@ function getCompat(model) {
1912
1713
  };
1913
1714
  }
1914
1715
  //#endregion
1915
- //#region packages/ai/src/transports/openai-completions-transport.ts
1916
- function hasToolHistory(messages) {
1917
- return messages.some((message) => message.role === "toolResult" || message.role === "assistant" && Array.isArray(message.content) && message.content.some((block) => block.type === "toolCall"));
1918
- }
1919
- function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
1920
- const messages = params.messages;
1921
- if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
1922
- 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.`);
1923
- }
1924
- const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
1925
- const SSE_DONE_MAX_LINE_CHARS = 1024;
1926
- function createSseDoneDetector() {
1927
- const decoder = new TextDecoder();
1928
- let line = "";
1929
- let lineOverflowed = false;
1930
- let sawDone = false;
1931
- const finishLine = () => {
1932
- if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
1933
- line = "";
1934
- lineOverflowed = false;
1935
- };
1936
- const observeText = (text) => {
1937
- for (const char of text) {
1938
- if (char === "\n" || char === "\r") {
1939
- finishLine();
1940
- continue;
1941
- }
1942
- if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
1943
- else lineOverflowed = true;
1944
- }
1945
- };
1946
- return {
1947
- observe(chunk) {
1948
- if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
1949
- },
1950
- finish() {
1951
- if (sawDone) return;
1952
- observeText(decoder.decode());
1953
- if (line || lineOverflowed) finishLine();
1954
- },
1955
- sawDone: () => sawDone
1956
- };
1957
- }
1958
- function createOpenAICompletionsClient(model, context, apiKey, optionHeaders, opts) {
1959
- const clientConfig = buildOpenAICompletionsClientConfig(model, context, optionHeaders);
1960
- return new OpenAI({
1961
- apiKey,
1962
- baseURL: clientConfig.baseURL,
1963
- dangerouslyAllowBrowser: true,
1964
- defaultHeaders: clientConfig.defaultHeaders,
1965
- defaultQuery: clientConfig.defaultQuery,
1966
- fetch: opts?.fetch ?? buildGuardedModelFetch(model),
1967
- ...buildOpenAISdkClientOptions(model)
1968
- });
1969
- }
1970
- function isAzureOpenAICompatibleHost(hostname) {
1971
- return hostname.endsWith(".openai.azure.com") || hostname.endsWith(".services.ai.azure.com") || hostname.endsWith(".cognitiveservices.azure.com");
1972
- }
1716
+ //#region packages/ai/src/transports/openai-completions-params.ts
1973
1717
  function isKnownOpenAICompletionsEndpoint(model) {
1974
1718
  if (!model.baseUrl.trim()) return true;
1975
1719
  const endpointClass = resolveProviderEndpoint(model.baseUrl).endpointClass;
@@ -1980,481 +1724,232 @@ function isKnownOpenAICompletionsEndpoint(model) {
1980
1724
  return false;
1981
1725
  }
1982
1726
  }
1983
- function buildOpenAICompletionsClientConfig(model, context, optionHeaders) {
1984
- const headers = buildOpenAIClientHeaders(model, context, optionHeaders);
1985
- const defaultQuery = {};
1986
- let baseURL = model.baseUrl;
1987
- let isAzureHost = false;
1988
- try {
1989
- const parsed = new URL(model.baseUrl);
1990
- isAzureHost = isAzureOpenAICompatibleHost(parsed.hostname.toLowerCase());
1991
- parsed.searchParams.forEach((value, key) => {
1992
- if (value) defaultQuery[key] = value;
1993
- });
1994
- parsed.search = "";
1995
- baseURL = parsed.toString().replace(/\/$/, "");
1996
- } catch {}
1997
- if (isAzureHost) {
1998
- const apiVersionHeader = Object.keys(headers).find((key) => key.toLowerCase() === "api-version");
1999
- if (apiVersionHeader) {
2000
- const apiVersion = headers[apiVersionHeader]?.trim();
2001
- delete headers[apiVersionHeader];
2002
- if (apiVersion && !defaultQuery["api-version"]) defaultQuery["api-version"] = apiVersion;
2003
- }
2004
- }
2005
- return {
2006
- baseURL,
2007
- defaultHeaders: headers,
2008
- defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
2009
- };
1727
+ function resolveOpenAICompletionsReasoningEffort$1(options) {
1728
+ return options?.reasoningEffort ?? options?.reasoning ?? "high";
2010
1729
  }
2011
- function createOpenAICompletionsTransportStreamFn() {
2012
- return (model, context, options) => {
2013
- const eventStream = createAssistantMessageEventStream();
2014
- const stream = eventStream;
2015
- (async () => {
2016
- const output = {
2017
- role: "assistant",
2018
- content: [],
2019
- api: model.api,
2020
- provider: model.provider,
2021
- model: model.id,
2022
- usage: {
2023
- input: 0,
2024
- output: 0,
2025
- cacheRead: 0,
2026
- cacheWrite: 0,
2027
- totalTokens: 0,
2028
- cost: {
2029
- input: 0,
2030
- output: 0,
2031
- cacheRead: 0,
2032
- cacheWrite: 0,
2033
- total: 0
2034
- }
2035
- },
2036
- stopReason: "stop",
2037
- timestamp: Date.now()
2038
- };
2039
- let firstEventAbort;
2040
- try {
2041
- const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
2042
- const doneDetector = createSseDoneDetector();
2043
- const baseFetch = buildGuardedModelFetch(model);
2044
- const doneDetectingFetch = async (url, init) => {
2045
- const response = await baseFetch(url, init);
2046
- if (!response.body || !response.ok) return response;
2047
- if (typeof TransformStream === "undefined" || !response.body.pipeThrough) return response;
2048
- const transformed = response.body.pipeThrough(new TransformStream({
2049
- transform(chunk, controller) {
2050
- doneDetector.observe(chunk);
2051
- controller.enqueue(chunk);
2052
- },
2053
- flush() {
2054
- doneDetector.finish();
2055
- }
2056
- }));
2057
- return new Response(transformed, {
2058
- headers: response.headers,
2059
- status: response.status,
2060
- statusText: response.statusText
2061
- });
2062
- };
2063
- const client = createOpenAICompletionsClient(model, context, apiKey, options?.headers, { fetch: doneDetectingFetch });
2064
- let params = buildOpenAICompletionsParams(model, context, options);
2065
- const nextParams = await options?.onPayload?.(params, model);
2066
- if (nextParams !== void 0) params = nextParams;
2067
- if (options?.openclawCodeModeToolSurface === true) {
2068
- const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
2069
- enforceCodeModeResponsesToolSurface(params, visibleToolNames);
2070
- assertCodeModeResponsesToolSurface(params, visibleToolNames);
2071
- }
2072
- if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
2073
- const emitReasoning = shouldEmitOpenAICompletionsReasoning(model, options);
2074
- firstEventAbort = createFirstStreamEventAbortController(options?.signal);
2075
- const responseStream = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
2076
- timeoutMs: options?.timeoutMs,
2077
- maxRetries: options?.maxRetries
2078
- }));
2079
- stream.push({
2080
- type: "start",
2081
- partial: output
2082
- });
2083
- await processOpenAICompletionsStream(responseStream, output, model, stream, {
2084
- signal: options?.signal,
2085
- emitReasoning,
2086
- firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
2087
- abortFirstEventStream: firstEventAbort.abort,
2088
- onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
2089
- sawStreamDONE: doneDetector.sawDone
2090
- });
2091
- finalizeTransportStream({
2092
- stream,
2093
- output,
2094
- signal: options?.signal
2095
- });
2096
- } catch (error) {
2097
- failTransportStream({
2098
- stream,
2099
- output,
2100
- signal: options?.signal,
2101
- error
2102
- });
2103
- } finally {
2104
- firstEventAbort?.dispose();
2105
- }
2106
- })();
2107
- return eventStream;
2108
- };
2109
- }
2110
- async function processOpenAICompletionsStream(responseStream, output, model, stream, options) {
2111
- const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
2112
- const MAX_TOOL_CALL_ARGUMENT_BUFFER_BYTES = 256e3;
2113
- const emitReasoning = options?.emitReasoning ?? true;
2114
- const compat = getCompat(model);
2115
- const deepSeekTextFilter = shouldFilterDeepSeekDsmlText(compat) ? createDeepSeekTextFilter() : null;
2116
- const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText(compat) ? createDeepSeekDsmlToolCallRecoverer() : null;
2117
- const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
2118
- let currentBlock = null;
2119
- let pendingPostToolCallDeltas = [];
2120
- let pendingPostToolCallBytes = 0;
2121
- let isFlushingPendingPostToolCallDeltas = false;
2122
- const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
2123
- const toolCallBlocksById = /* @__PURE__ */ new Map();
2124
- const provisionalCommentaryTags = /* @__PURE__ */ new Map();
2125
- const toolCallBlockBytes = /* @__PURE__ */ new WeakMap();
2126
- const toolCallBlockIndices = /* @__PURE__ */ new WeakMap();
2127
- let sawStopFinishReason = false;
2128
- let sawNativeToolCallDelta = false;
2129
- const blockIndex = () => output.content.length - 1;
2130
- const measureUtf8Bytes = (text) => Buffer.byteLength(text, "utf8");
2131
- let chunkPushedEvent = false;
2132
- const pushStreamEvent = (event) => {
2133
- chunkPushedEvent = true;
2134
- stream.push(event);
2135
- };
2136
- const queuePostToolCallDelta = (next) => {
2137
- const nextBytes = measureUtf8Bytes(next.text);
2138
- if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
2139
- pendingPostToolCallBytes += nextBytes;
2140
- const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
2141
- if (!previous || previous.kind !== next.kind) {
2142
- pendingPostToolCallDeltas.push(next);
2143
- return;
2144
- }
2145
- if (next.kind === "thinking" && previous.kind === "thinking") {
2146
- if (previous.signature !== next.signature) {
2147
- pendingPostToolCallDeltas.push(next);
2148
- return;
2149
- }
2150
- previous.text += next.text;
2151
- return;
2152
- }
2153
- previous.text += next.text;
2154
- };
2155
- const appendThinkingDeltaInternal = (reasoningDelta) => {
2156
- if (!currentBlock || currentBlock.type !== "thinking") {
2157
- currentBlock = {
2158
- type: "thinking",
2159
- thinking: "",
2160
- ...reasoningDelta.signature ? { thinkingSignature: reasoningDelta.signature } : {}
2161
- };
2162
- output.content.push(currentBlock);
2163
- pushStreamEvent({
2164
- type: "thinking_start",
2165
- contentIndex: blockIndex(),
2166
- partial: output
2167
- });
2168
- }
2169
- currentBlock.thinking += reasoningDelta.text;
2170
- pushStreamEvent({
2171
- type: "thinking_delta",
2172
- contentIndex: blockIndex(),
2173
- delta: reasoningDelta.text,
2174
- partial: output
2175
- });
2176
- };
2177
- const appendTextDeltaInternal = (text) => {
2178
- if (!currentBlock || currentBlock.type !== "text") {
2179
- currentBlock = {
2180
- type: "text",
2181
- text: ""
2182
- };
2183
- output.content.push(currentBlock);
2184
- pushStreamEvent({
2185
- type: "text_start",
2186
- contentIndex: blockIndex(),
2187
- partial: output
2188
- });
2189
- }
2190
- currentBlock.text += text;
2191
- pushStreamEvent({
2192
- type: "text_delta",
2193
- contentIndex: blockIndex(),
2194
- delta: text
2195
- });
2196
- };
2197
- const flushPendingPostToolCallDeltas = () => {
2198
- if (isFlushingPendingPostToolCallDeltas || currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
2199
- isFlushingPendingPostToolCallDeltas = true;
2200
- const bufferedDeltas = pendingPostToolCallDeltas;
2201
- pendingPostToolCallDeltas = [];
2202
- pendingPostToolCallBytes = 0;
2203
- for (const delta of bufferedDeltas) if (delta.kind === "text") appendTextDeltaInternal(delta.text);
2204
- else if (emitReasoning) appendThinkingDeltaInternal(delta);
2205
- isFlushingPendingPostToolCallDeltas = false;
2206
- };
2207
- const appendThinkingDelta = (reasoningDelta) => {
2208
- flushPendingPostToolCallDeltas();
2209
- appendThinkingDeltaInternal(reasoningDelta);
2210
- };
2211
- const appendTextDelta = (text) => {
2212
- flushPendingPostToolCallDeltas();
2213
- appendTextDeltaInternal(text);
2214
- };
2215
- const appendVisibleTextDelta = (text) => {
2216
- if (!text) return;
2217
- if (currentBlock?.type === "toolCall") queuePostToolCallDelta({
2218
- kind: "text",
2219
- text
2220
- });
2221
- else appendTextDelta(text);
2222
- };
2223
- const appendRecoveredToolCall = (toolCall) => {
2224
- if (currentBlock?.type === "toolCall") {
2225
- currentBlock = null;
2226
- flushPendingPostToolCallDeltas();
2227
- }
2228
- rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
2229
- const block = {
2230
- type: "toolCall",
2231
- id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
2232
- name: toolCall.name,
2233
- arguments: toolCall.arguments,
2234
- partialArgs: toolCall.partialArgs
2235
- };
2236
- currentBlock = block;
2237
- output.content.push(block);
2238
- toolCallBlockIndices.set(block, output.content.length - 1);
2239
- pushStreamEvent({
2240
- type: "toolcall_start",
2241
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
2242
- partial: output
2243
- });
2244
- pushStreamEvent({
2245
- type: "toolcall_delta",
2246
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
2247
- delta: toolCall.partialArgs,
2248
- partial: output
2249
- });
2250
- };
2251
- const appendFilteredVisibleTextDelta = (text) => {
2252
- const recoveredParts = deepSeekToolCallRecoverer?.push(text) ?? [{
2253
- kind: "text",
2254
- text
2255
- }];
2256
- for (const recoveredPart of recoveredParts) {
2257
- if (recoveredPart.kind === "toolCall") {
2258
- appendRecoveredToolCall(recoveredPart);
2259
- continue;
2260
- }
2261
- const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
2262
- for (const part of parts) appendVisibleTextDelta(part);
2263
- }
2264
- };
2265
- const flushDeepSeekToolCallRecovererAtEnd = () => {
2266
- const recoveredParts = deepSeekToolCallRecoverer?.flush();
2267
- if (!recoveredParts) return;
2268
- for (const recoveredPart of recoveredParts) {
2269
- if (recoveredPart.kind === "toolCall") {
2270
- appendRecoveredToolCall(recoveredPart);
2271
- continue;
2272
- }
2273
- const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
2274
- for (const part of parts) appendVisibleTextDelta(part);
2275
- }
2276
- };
2277
- const flushDeepSeekTextFilterAtEnd = () => {
2278
- const parts = deepSeekTextFilter?.flush();
2279
- if (!parts) return;
2280
- for (const part of parts) appendVisibleTextDelta(part);
2281
- };
2282
- const appendRoutedContentDelta = (delta) => {
2283
- if (delta.kind === "text") {
2284
- appendFilteredVisibleTextDelta(delta.text);
2285
- return;
2286
- }
2287
- if (!emitReasoning) return;
2288
- if (currentBlock?.type === "toolCall") queuePostToolCallDelta(delta);
2289
- else appendThinkingDelta(delta);
2290
- };
2291
- const appendPartitionedVisibleDelta = (delta) => {
2292
- if (delta.kind === "text") appendFilteredVisibleTextDelta(delta.text);
1730
+ function resolveOpenAICompletionsMaxTokens(model, options) {
1731
+ if (options?.maxTokens) return {
1732
+ maxTokens: options.maxTokens,
1733
+ clampToModelMaxTokens: true
2293
1734
  };
2294
- const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
2295
- if (!hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
2296
- const latestBlock = output.content[output.content.length - 1];
2297
- if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
2298
- if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
2299
- appendThinkingDelta({
2300
- signature: "",
2301
- text: ""
2302
- });
1735
+ const paramsMaxTokens = resolveMaxTokensParam(model.params);
1736
+ if (paramsMaxTokens) return {
1737
+ maxTokens: paramsMaxTokens,
1738
+ clampToModelMaxTokens: false
2303
1739
  };
2304
- const flushReasoningTagTextPartitionerAtEnd = () => {
2305
- for (const delta of reasoningTagTextPartitioner.flush()) appendPartitionedVisibleDelta(delta);
1740
+ return {
1741
+ maxTokens: model.maxTokens,
1742
+ clampToModelMaxTokens: false
2306
1743
  };
2307
- const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
2308
- const guardedStream = withFirstStreamEventTimeout(responseStream, {
2309
- provider: model.provider,
2310
- api: model.api,
2311
- model: model.id,
2312
- timeoutMs: options?.firstEventTimeoutMs ?? 0,
2313
- stage: "completions",
2314
- abort: options?.abortFirstEventStream,
2315
- onTimeout: options?.onFirstEventTimeout,
2316
- 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."
2317
- });
2318
- for await (const rawChunk of guardedStream) {
2319
- throwIfModelStreamAborted(options?.signal);
2320
- chunkPushedEvent = false;
2321
- if (!rawChunk || typeof rawChunk !== "object") {
2322
- await cooperativeScheduler.afterEvent();
2323
- continue;
2324
- }
2325
- notifyLlmRequestActivity(options?.signal);
2326
- const chunk = rawChunk;
2327
- output.responseId ||= chunk.id;
2328
- let hasReasoningUsageActivity = false;
2329
- if (chunk.usage) {
2330
- output.usage = parseTransportChunkUsage(chunk.usage, model);
2331
- hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(chunk.usage);
1744
+ }
1745
+ function resolveOpenAICompletionsModelMaxTokens(model) {
1746
+ return typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
1747
+ }
1748
+ const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
1749
+ const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
1750
+ function estimateOpenAICompletionsInputTokens(payload) {
1751
+ let adjustedChars = 0;
1752
+ adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
1753
+ if (Array.isArray(payload.tools) && payload.tools.length > 0) try {
1754
+ adjustedChars += estimateStringChars(JSON.stringify(payload.tools));
1755
+ } catch {
1756
+ adjustedChars += 1024;
1757
+ }
1758
+ if (payload.response_format !== void 0) try {
1759
+ adjustedChars += estimateStringChars(JSON.stringify(payload.response_format));
1760
+ } catch {
1761
+ adjustedChars += 256;
1762
+ }
1763
+ return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
1764
+ }
1765
+ function estimateOpenAICompletionsMessagesChars(messages) {
1766
+ if (!Array.isArray(messages)) return 0;
1767
+ let adjustedChars = 0;
1768
+ for (const message of messages) {
1769
+ if (!message || typeof message !== "object") continue;
1770
+ const record = message;
1771
+ adjustedChars += estimateOpenAICompletionsContentChars(record.content);
1772
+ for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
1773
+ if (record.tool_calls !== void 0) try {
1774
+ adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
1775
+ } catch {
1776
+ adjustedChars += 256;
2332
1777
  }
2333
- const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
2334
- if (!choice) {
2335
- emitReasoningUsageActivity(hasReasoningUsageActivity);
2336
- await cooperativeScheduler.afterEvent();
1778
+ }
1779
+ return adjustedChars;
1780
+ }
1781
+ function estimateOpenAICompletionsContentChars(value) {
1782
+ if (typeof value === "string") return estimateStringChars(value);
1783
+ if (!Array.isArray(value)) return 0;
1784
+ let adjustedChars = 0;
1785
+ for (const block of value) {
1786
+ if (!block || typeof block !== "object") continue;
1787
+ const record = block;
1788
+ if (record.type === "image_url" || record.type === "input_image") {
1789
+ adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
2337
1790
  continue;
2338
1791
  }
2339
- const choiceUsage = choice.usage;
2340
- if (!chunk.usage && choiceUsage) {
2341
- output.usage = parseTransportChunkUsage(choiceUsage, model);
2342
- hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
2343
- }
2344
- if (choice.finish_reason) {
2345
- const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
2346
- output.stopReason = finishReasonResult.stopReason;
2347
- if (finishReasonResult.stopReason === "stop") sawStopFinishReason = true;
2348
- if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
2349
- }
2350
- const choiceDelta = choice.delta ?? choice.message;
2351
- if (!choiceDelta) {
2352
- emitReasoningUsageActivity(hasReasoningUsageActivity);
2353
- await cooperativeScheduler.afterEvent();
1792
+ const text = record.text;
1793
+ if (typeof text === "string") {
1794
+ adjustedChars += estimateStringChars(text);
2354
1795
  continue;
2355
1796
  }
2356
- const reasoningDeltas = getCompletionsReasoningDeltas(choiceDelta, compat.visibleReasoningDetailTypes);
2357
- const hasMirroredReasoning = reasoningDeltas.some((delta) => delta.kind === "thinking");
2358
- if (hasMirroredReasoning) reasoningTagTextPartitioner.markStrict();
2359
- if (choiceDelta.content) {
2360
- const contentDeltas = getCompletionsContentDeltas(choiceDelta.content);
2361
- for (const contentDelta of contentDeltas) if (contentDelta.kind === "text") {
2362
- const routedDeltas = hasMirroredReasoning ? reasoningTagTextPartitioner.push(contentDelta.text) : reasoningTagTextPartitioner.pushVisible(contentDelta.text);
2363
- for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
2364
- } else {
2365
- reasoningTagTextPartitioner.markStrict();
2366
- appendRoutedContentDelta(contentDelta);
2367
- }
1797
+ try {
1798
+ adjustedChars += estimateStringChars(JSON.stringify(block));
1799
+ } catch {
1800
+ adjustedChars += 256;
2368
1801
  }
2369
- const refusalText = typeof choiceDelta.refusal === "string" ? choiceDelta.refusal : "";
2370
- if (refusalText) {
2371
- const routedDeltas = hasMirroredReasoning ? reasoningTagTextPartitioner.push(refusalText) : reasoningTagTextPartitioner.pushVisible(refusalText);
2372
- for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
1802
+ }
1803
+ return adjustedChars;
1804
+ }
1805
+ function resolveOpenAICompletionsEffectiveContextTokens(model) {
1806
+ const contextTokens = model.contextTokens;
1807
+ if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
1808
+ return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
1809
+ }
1810
+ function isQwenOpenAICompletionsThinkingFormat(format) {
1811
+ return format === "qwen" || format === "qwen-chat-template";
1812
+ }
1813
+ function setQwenChatTemplateThinking(params, enabled) {
1814
+ const existing = params.chat_template_kwargs;
1815
+ params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
1816
+ ...existing,
1817
+ enable_thinking: enabled
1818
+ } : { enable_thinking: enabled };
1819
+ }
1820
+ function applyQwenOpenAICompletionsThinkingParams(params) {
1821
+ if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
1822
+ const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
1823
+ if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
1824
+ else params.payload.enable_thinking = enabled;
1825
+ return true;
1826
+ }
1827
+ function applyTogetherOpenAICompletionsThinkingParams(params) {
1828
+ if (!params.modelReasoning || params.compatThinkingFormat !== "together") return;
1829
+ params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
1830
+ }
1831
+ function convertTools(tools, compat, model) {
1832
+ const projection = projectOpenAITools(tools);
1833
+ const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
1834
+ transport: "stream",
1835
+ supportsStrictMode: compat?.supportsStrictMode
1836
+ }), {
1837
+ transport: "completions",
1838
+ model
1839
+ });
1840
+ return {
1841
+ projection,
1842
+ tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
1843
+ const functionTool = {
1844
+ name: tool.name,
1845
+ description: tool.description,
1846
+ parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
1847
+ };
1848
+ if (strict !== void 0) functionTool.strict = strict;
1849
+ return {
1850
+ type: "function",
1851
+ function: functionTool
1852
+ };
1853
+ })
1854
+ };
1855
+ }
1856
+ function buildOpenAICompletionsParams(model, context, options) {
1857
+ const compat = getCompat(model);
1858
+ const compatDetection = detectOpenAICompletionsCompat(model);
1859
+ let messages = convertMessages(model, context.systemPrompt ? {
1860
+ ...context,
1861
+ systemPrompt: stripSystemPromptCacheBoundary(context.systemPrompt)
1862
+ } : context, compat);
1863
+ applyCompletionsReplay(messages, context, model, compat);
1864
+ if (compat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
1865
+ const cacheRetention = resolveCacheRetention(options?.cacheRetention);
1866
+ const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
1867
+ const params = {
1868
+ model: model.id,
1869
+ messages: compat.requiresStringContent ? flattenCompletionMessagesToStringContent(messages) : messages,
1870
+ stream: true
1871
+ };
1872
+ if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
1873
+ if (compat.supportsStore) params.store = false;
1874
+ if (compat.supportsPromptCacheKey && promptCacheKey) {
1875
+ params.prompt_cache_key = promptCacheKey;
1876
+ if (cacheRetention === "long" && compat.supportsLongCacheRetention) params.prompt_cache_retention = "24h";
1877
+ }
1878
+ if (options?.temperature !== void 0) params.temperature = options.temperature;
1879
+ if (options?.topP !== void 0) params.top_p = options.topP;
1880
+ const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
1881
+ provider: model.provider,
1882
+ baseUrl: model.baseUrl,
1883
+ hasTools: () => Boolean(context.tools?.length)
1884
+ }) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
1885
+ if (responseFormat !== void 0) params.response_format = responseFormat;
1886
+ if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
1887
+ if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
1888
+ if (options?.seed !== void 0) params.seed = options.seed;
1889
+ if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
1890
+ if (supportsModelTools(model)) {
1891
+ if (context.tools) {
1892
+ const converted = convertTools(context.tools, compat, model);
1893
+ if (converted.tools.length > 0 || converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
1894
+ else if (hasToolCallHistory(context.messages)) params.tools = [];
1895
+ if (options?.toolChoice) {
1896
+ const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
1897
+ if (toolChoice !== void 0) params.tool_choice = toolChoice;
1898
+ } else if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
1899
+ } else if (hasToolCallHistory(context.messages)) params.tools = [];
1900
+ if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
1901
+ delete params.tools;
1902
+ delete params.tool_choice;
2373
1903
  }
2374
- for (const reasoningDelta of reasoningDeltas) {
2375
- if (reasoningDelta.kind === "thinking" && !emitReasoning) continue;
2376
- if (currentBlock?.type === "toolCall") {
2377
- queuePostToolCallDelta({ ...reasoningDelta });
2378
- continue;
2379
- }
2380
- if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text);
2381
- else if (emitReasoning) appendThinkingDelta(reasoningDelta);
1904
+ }
1905
+ {
1906
+ const maxTokenBudget = resolveOpenAICompletionsMaxTokens(model, options);
1907
+ const effectiveMaxTokens = maxTokenBudget.maxTokens;
1908
+ const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
1909
+ let clampedMaxTokens = effectiveMaxTokens;
1910
+ const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
1911
+ if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
1912
+ clampedMaxTokens = modelMaxTokens;
1913
+ emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
2382
1914
  }
2383
- if (choiceDelta.tool_calls && choiceDelta.tool_calls.length > 0) {
2384
- sawNativeToolCallDelta = true;
2385
- flushReasoningTagTextPartitionerAtEnd();
2386
- rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
2387
- for (const toolCall of choiceDelta.tool_calls) {
2388
- const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
2389
- let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
2390
- if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
2391
- if (!block) {
2392
- if (currentBlock?.type === "toolCall") {
2393
- currentBlock = null;
2394
- flushPendingPostToolCallDeltas();
2395
- }
2396
- const initialSig = extractGoogleThoughtSignature(toolCall);
2397
- block = {
2398
- type: "toolCall",
2399
- id: toolCall.id || "",
2400
- name: toolCall.function?.name || "",
2401
- arguments: {},
2402
- partialArgs: "",
2403
- ...initialSig ? { thoughtSignature: initialSig } : {}
2404
- };
2405
- output.content.push(block);
2406
- toolCallBlockIndices.set(block, output.content.length - 1);
2407
- pushStreamEvent({
2408
- type: "toolcall_start",
2409
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
2410
- partial: output
2411
- });
2412
- }
2413
- if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
2414
- if (toolCall.id) {
2415
- block.id = toolCall.id;
2416
- toolCallBlocksById.set(toolCall.id, block);
2417
- }
2418
- currentBlock = block;
2419
- if (toolCall.function?.name) block.name = toolCall.function.name;
2420
- const deltaSig = extractGoogleThoughtSignature(toolCall);
2421
- if (deltaSig) block.thoughtSignature = deltaSig;
2422
- if (toolCall.function?.arguments) {
2423
- const nextArgumentBytes = measureUtf8Bytes(toolCall.function.arguments);
2424
- const currentBlockArgBytes = toolCallBlockBytes.get(block) ?? 0;
2425
- if (currentBlockArgBytes + nextArgumentBytes > MAX_TOOL_CALL_ARGUMENT_BUFFER_BYTES) throw new Error("Exceeded tool-call argument buffer limit");
2426
- toolCallBlockBytes.set(block, currentBlockArgBytes + nextArgumentBytes);
2427
- block.partialArgs += toolCall.function.arguments;
2428
- block.arguments = parseStreamingJson(block.partialArgs);
2429
- pushStreamEvent({
2430
- type: "toolcall_delta",
2431
- contentIndex: toolCallBlockIndices.get(block) ?? -1,
2432
- delta: toolCall.function.arguments,
2433
- partial: output
2434
- });
2435
- }
1915
+ if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
1916
+ const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
1917
+ const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
1918
+ if (clampedMaxTokens > remainingBudget) {
1919
+ clampedMaxTokens = remainingBudget;
1920
+ emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
2436
1921
  }
2437
1922
  }
2438
- flushPendingPostToolCallDeltas();
2439
- emitReasoningUsageActivity(hasReasoningUsageActivity);
2440
- await cooperativeScheduler.afterEvent();
1923
+ if (clampedMaxTokens) if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
1924
+ else params.max_completion_tokens = clampedMaxTokens;
2441
1925
  }
2442
- flushReasoningTagTextPartitionerAtEnd();
2443
- flushDeepSeekToolCallRecovererAtEnd();
2444
- flushDeepSeekTextFilterAtEnd();
2445
- currentBlock = null;
2446
- flushPendingPostToolCallDeltas();
2447
- const hasToolCalls = output.content.some((block) => block.type === "toolCall");
2448
- const hasVisibleText = output.content.some((block) => block.type === "text" && typeof block.text === "string" && block.text.trim().length > 0);
2449
- if (output.stopReason === "toolUse" && !hasToolCalls) output.stopReason = "stop";
2450
- if (output.stopReason === "stop" && hasToolCalls && !hasVisibleText && (sawStopFinishReason || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false))) output.stopReason = "toolUse";
2451
- if (hasToolCalls && output.stopReason !== "toolUse") output.content = output.content.filter((block) => block.type !== "toolCall");
2452
- if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
2453
- if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
2454
- }
2455
- function shouldFilterDeepSeekDsmlText(compat) {
2456
- return compat.thinkingFormat === "deepseek";
1926
+ const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort$1(options);
1927
+ const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
1928
+ model,
1929
+ effort: completionsReasoningEffort,
1930
+ fallbackMap: compat.reasoningEffortMap
1931
+ }) : void 0;
1932
+ const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
1933
+ const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
1934
+ const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
1935
+ compatThinkingFormat: compat.thinkingFormat,
1936
+ modelReasoning: model.reasoning,
1937
+ payload: params,
1938
+ requestedEffort: completionsReasoningEffort
1939
+ });
1940
+ applyTogetherOpenAICompletionsThinkingParams({
1941
+ compatThinkingFormat: compat.thinkingFormat,
1942
+ modelReasoning: model.reasoning,
1943
+ payload: params,
1944
+ requestedEffort: completionsReasoningEffort
1945
+ });
1946
+ if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
1947
+ else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
1948
+ else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
1949
+ return params;
2457
1950
  }
1951
+ //#endregion
1952
+ //#region packages/ai/src/transports/openai-completions-dsml.ts
2458
1953
  const DEEPSEEK_DSML_BARS = ["|", "|"];
2459
1954
  const DEEPSEEK_DSML_TOOL_KINDS = [
2460
1955
  "tool_calls",
@@ -2463,42 +1958,80 @@ const DEEPSEEK_DSML_TOOL_KINDS = [
2463
1958
  ];
2464
1959
  const DEEPSEEK_DSML_TOOL_OPEN_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `<${bar}DSML${bar}${kind}>`));
2465
1960
  const DEEPSEEK_DSML_TOOL_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.flatMap((bar) => DEEPSEEK_DSML_TOOL_KINDS.map((kind) => `</${bar}DSML${bar}${kind}>`));
1961
+ const DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES = DEEPSEEK_DSML_BARS.map((bar) => `<${bar}DSML${bar}invoke`);
1962
+ const DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS = DEEPSEEK_DSML_BARS.map((bar) => `</${bar}DSML${bar}invoke>`);
2466
1963
  const DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length));
2467
- function createDeepSeekDsmlToolCallRecoverer() {
1964
+ const DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN = Math.max(...DEEPSEEK_DSML_TOOL_OPEN_TOKENS.map((token) => token.length), ...DEEPSEEK_DSML_TOOL_CLOSE_TOKENS.map((token) => token.length), ...DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES.map((token) => token.length), ...DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS.map((token) => token.length));
1965
+ const MAX_DSML_RECOVERY_BUFFER_BYTES = 256e3;
1966
+ const DEEPSEEK_DSML_SCAN_BATCH_CHARS = 64 * 1024;
1967
+ function createDsmlRecoverer() {
2468
1968
  let buffer = "";
1969
+ let bufferBytes = 0;
1970
+ let bufferEndsWithHighSurrogate = false;
1971
+ let pendingScanChars = 0;
1972
+ let activeOpenToken = null;
1973
+ let blockScanState = {
1974
+ offset: 0,
1975
+ mode: "outer",
1976
+ invokeOpenStart: -1
1977
+ };
1978
+ const resetBlockScan = () => {
1979
+ activeOpenToken = null;
1980
+ pendingScanChars = 0;
1981
+ blockScanState = {
1982
+ offset: 0,
1983
+ mode: "outer",
1984
+ invokeOpenStart: -1
1985
+ };
1986
+ };
2469
1987
  const consume = (final) => {
2470
1988
  const output = [];
2471
1989
  while (buffer) {
2472
- const open = findEarliestStringToken(buffer, DEEPSEEK_DSML_TOOL_OPEN_TOKENS);
1990
+ const open = activeOpenToken ? {
1991
+ index: 0,
1992
+ token: activeOpenToken
1993
+ } : findEarliestStringToken(buffer, DEEPSEEK_DSML_TOOL_OPEN_TOKENS);
2473
1994
  if (!open) {
1995
+ resetBlockScan();
2474
1996
  if (final) {
2475
1997
  output.push({
2476
1998
  kind: "text",
2477
1999
  text: buffer
2478
2000
  });
2479
2001
  buffer = "";
2002
+ bufferBytes = 0;
2003
+ bufferEndsWithHighSurrogate = false;
2480
2004
  return output;
2481
2005
  }
2482
2006
  const keep = longestDeepSeekDsmlToolOpenPrefixSuffixLength(buffer);
2483
2007
  const emitLength = buffer.length - keep;
2484
2008
  if (emitLength > 0) {
2009
+ const emitted = buffer.slice(0, emitLength);
2485
2010
  output.push({
2486
2011
  kind: "text",
2487
- text: buffer.slice(0, emitLength)
2012
+ text: emitted
2488
2013
  });
2489
- buffer = buffer.slice(emitLength);
2014
+ bufferBytes -= Buffer.byteLength(emitted, "utf8");
2015
+ buffer = buffer.slice(emitted.length);
2016
+ if (!buffer) bufferEndsWithHighSurrogate = false;
2490
2017
  }
2491
2018
  return output;
2492
2019
  }
2493
2020
  if (open.index > 0) {
2021
+ const prefix = buffer.slice(0, open.index);
2494
2022
  output.push({
2495
2023
  kind: "text",
2496
- text: buffer.slice(0, open.index)
2024
+ text: prefix
2497
2025
  });
2498
- buffer = buffer.slice(open.index);
2026
+ bufferBytes -= Buffer.byteLength(prefix, "utf8");
2027
+ buffer = buffer.slice(prefix.length);
2028
+ resetBlockScan();
2499
2029
  }
2500
- const afterOpen = buffer.slice(open.token.length);
2501
- const close = findEarliestStringToken(afterOpen, DEEPSEEK_DSML_TOOL_CLOSE_TOKENS);
2030
+ activeOpenToken = open.token;
2031
+ if (blockScanState.offset === 0) blockScanState.offset = open.token.length;
2032
+ const blockScan = scanDeepSeekDsmlToolBlock(buffer, open.token.replace("<", "</"), open.token.length, blockScanState);
2033
+ if (blockScan.kind === "nested-open") throw new Error("Nested DeepSeek DSML recovery wrappers are not supported");
2034
+ const close = blockScan.kind === "close" ? blockScan : null;
2502
2035
  if (!close) {
2503
2036
  if (final) {
2504
2037
  output.push({
@@ -2506,24 +2039,38 @@ function createDeepSeekDsmlToolCallRecoverer() {
2506
2039
  text: buffer
2507
2040
  });
2508
2041
  buffer = "";
2042
+ bufferBytes = 0;
2043
+ bufferEndsWithHighSurrogate = false;
2044
+ return output;
2509
2045
  }
2046
+ if (bufferBytes > MAX_DSML_RECOVERY_BUFFER_BYTES) throw new Error("Exceeded DeepSeek DSML recovery buffer limit");
2510
2047
  return output;
2511
2048
  }
2512
- const body = afterOpen.slice(0, close.index);
2513
- const blockLength = open.token.length + close.index + close.token.length;
2049
+ resetBlockScan();
2050
+ const body = buffer.slice(open.token.length, close.index);
2051
+ const blockText = buffer.slice(0, close.index + close.token.length);
2052
+ if (Buffer.byteLength(blockText, "utf8") > MAX_DSML_RECOVERY_BUFFER_BYTES) throw new Error("Exceeded DeepSeek DSML recovery buffer limit");
2514
2053
  const recoveredToolCalls = parseDeepSeekDsmlToolCallBlock(body);
2515
2054
  if (recoveredToolCalls.length > 0) output.push(...recoveredToolCalls);
2516
2055
  else output.push({
2517
2056
  kind: "text",
2518
- text: buffer.slice(0, blockLength)
2057
+ text: blockText
2519
2058
  });
2520
- buffer = buffer.slice(blockLength);
2059
+ bufferBytes -= Buffer.byteLength(blockText, "utf8");
2060
+ buffer = buffer.slice(blockText.length);
2061
+ if (!buffer) bufferEndsWithHighSurrogate = false;
2521
2062
  }
2522
2063
  return output;
2523
2064
  };
2524
2065
  return {
2525
2066
  push(chunk) {
2067
+ const append = measureUtf8AppendBytes(bufferEndsWithHighSurrogate, chunk);
2068
+ bufferBytes += append.bytes;
2069
+ bufferEndsWithHighSurrogate = append.endsWithHighSurrogate;
2526
2070
  buffer += chunk;
2071
+ pendingScanChars += chunk.length;
2072
+ if (activeOpenToken && pendingScanChars < DEEPSEEK_DSML_SCAN_BATCH_CHARS && !chunk.includes("<") && !chunk.includes(">") && bufferBytes <= MAX_DSML_RECOVERY_BUFFER_BYTES) return [];
2073
+ pendingScanChars = 0;
2527
2074
  return consume(false);
2528
2075
  },
2529
2076
  flush() {
@@ -2533,16 +2080,16 @@ function createDeepSeekDsmlToolCallRecoverer() {
2533
2080
  }
2534
2081
  function parseDeepSeekDsmlToolCallBlock(body) {
2535
2082
  const toolCalls = [];
2536
- const invokeOpenRegex = /<[||]DSML[||]invoke\b([^>]*)>/g;
2083
+ const invokeOpenRegex = /<[||]DSML[||]invoke\b([^<>]*)>/g;
2537
2084
  let openMatch;
2538
2085
  while ((openMatch = invokeOpenRegex.exec(body)) !== null) {
2539
- const invokeName = parseXmlAttribute(openMatch[1] ?? "", "name");
2540
- if (!invokeName) continue;
2541
2086
  const invokeBodyStart = openMatch.index + openMatch[0].length;
2542
2087
  const invokeClose = findEarliestStringToken(body.slice(invokeBodyStart), ["</|DSML|invoke>", "</|DSML|invoke>"]);
2543
- if (!invokeClose) continue;
2088
+ if (!invokeClose) break;
2544
2089
  const invokeBody = body.slice(invokeBodyStart, invokeBodyStart + invokeClose.index);
2545
2090
  invokeOpenRegex.lastIndex = invokeBodyStart + invokeClose.index + invokeClose.token.length;
2091
+ const invokeName = parseXmlAttribute(openMatch[1] ?? "", "name");
2092
+ if (!invokeName) continue;
2546
2093
  const parsedArguments = parseDeepSeekDsmlInvokeArguments(invokeBody);
2547
2094
  if (!parsedArguments) continue;
2548
2095
  toolCalls.push({
@@ -2593,10 +2140,10 @@ function parseXmlAttribute(attributes, name) {
2593
2140
  function decodeDeepSeekDsmlText(value) {
2594
2141
  return value.replaceAll("&quot;", "\"").replaceAll("&apos;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
2595
2142
  }
2596
- function findEarliestStringToken(text, tokens) {
2143
+ function findEarliestStringToken(text, tokens, fromIndex = 0) {
2597
2144
  let best = null;
2598
2145
  for (const token of tokens) {
2599
- const index = text.indexOf(token);
2146
+ const index = text.indexOf(token, fromIndex);
2600
2147
  if (index !== -1 && (!best || index < best.index)) best = {
2601
2148
  index,
2602
2149
  token
@@ -2604,6 +2151,75 @@ function findEarliestStringToken(text, tokens) {
2604
2151
  }
2605
2152
  return best;
2606
2153
  }
2154
+ function scanDeepSeekDsmlToolBlock(text, closeToken, contentStartIndex, state) {
2155
+ while (state.offset < text.length) {
2156
+ if (state.mode === "invoke-open") {
2157
+ const nextOpen = text.indexOf("<", state.offset);
2158
+ const nextClose = text.indexOf(">", state.offset);
2159
+ if (nextClose === -1 && nextOpen === -1) {
2160
+ state.offset = text.length;
2161
+ return { kind: "incomplete" };
2162
+ }
2163
+ if (nextOpen !== -1 && (nextClose === -1 || nextOpen < nextClose)) {
2164
+ state.mode = "outer";
2165
+ state.offset = nextOpen;
2166
+ state.invokeOpenStart = -1;
2167
+ continue;
2168
+ }
2169
+ const invokeOpenTag = text.slice(state.invokeOpenStart, nextClose + 1);
2170
+ if (!/^<[||]DSML[||]invoke\b[^<>]*>$/.test(invokeOpenTag)) {
2171
+ state.mode = "outer";
2172
+ state.offset = state.invokeOpenStart + 1;
2173
+ state.invokeOpenStart = -1;
2174
+ continue;
2175
+ }
2176
+ state.mode = "invoke-body";
2177
+ state.offset = nextClose + 1;
2178
+ state.invokeOpenStart = -1;
2179
+ continue;
2180
+ }
2181
+ if (state.mode === "invoke-body") {
2182
+ const invokeClose = findEarliestStringToken(text, DEEPSEEK_DSML_INVOKE_CLOSE_TOKENS, state.offset);
2183
+ if (!invokeClose) {
2184
+ state.offset = Math.max(0, text.length - DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN + 1);
2185
+ return { kind: "incomplete" };
2186
+ }
2187
+ state.mode = "outer";
2188
+ state.offset = invokeClose.index + invokeClose.token.length;
2189
+ continue;
2190
+ }
2191
+ const toolOpen = findEarliestStringToken(text, DEEPSEEK_DSML_TOOL_OPEN_TOKENS, state.offset);
2192
+ const toolCloseIndex = text.indexOf(closeToken, state.offset);
2193
+ const invokeOpen = findEarliestStringToken(text, DEEPSEEK_DSML_INVOKE_OPEN_PREFIXES, state.offset);
2194
+ const next = [
2195
+ toolOpen ? {
2196
+ kind: "nested-open",
2197
+ ...toolOpen
2198
+ } : null,
2199
+ toolCloseIndex === -1 ? null : {
2200
+ kind: "close",
2201
+ index: toolCloseIndex,
2202
+ token: closeToken
2203
+ },
2204
+ invokeOpen ? {
2205
+ kind: "invoke-open",
2206
+ ...invokeOpen
2207
+ } : null
2208
+ ].filter((candidate) => candidate !== null).toSorted((left, right) => left.index - right.index)[0];
2209
+ if (!next) {
2210
+ state.offset = Math.max(contentStartIndex, text.length - DEEPSEEK_DSML_RECOVERY_MAX_BOUNDARY_LEN + 1);
2211
+ return { kind: "incomplete" };
2212
+ }
2213
+ if (next.kind === "invoke-open") {
2214
+ state.mode = "invoke-open";
2215
+ state.invokeOpenStart = next.index;
2216
+ state.offset = next.index + next.token.length;
2217
+ continue;
2218
+ }
2219
+ return next;
2220
+ }
2221
+ return { kind: "incomplete" };
2222
+ }
2607
2223
  function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
2608
2224
  const maxLength = Math.min(text.length, DEEPSEEK_DSML_TOOL_MAX_OPEN_TOKEN_LEN - 1);
2609
2225
  for (let length = maxLength; length > 0; length -= 1) {
@@ -2612,725 +2228,784 @@ function longestDeepSeekDsmlToolOpenPrefixSuffixLength(text) {
2612
2228
  }
2613
2229
  return 0;
2614
2230
  }
2615
- function getCompletionsContentDeltas(content) {
2616
- if (typeof content === "string") return content ? [{
2617
- kind: "text",
2618
- text: content
2619
- }] : [];
2620
- if (Array.isArray(content)) return content.flatMap((item) => getCompletionsContentDeltas(item));
2621
- if (!content || typeof content !== "object") return [];
2622
- const record = content;
2623
- const type = typeof record.type === "string" ? record.type.toLowerCase() : "";
2624
- const extractText = (value) => {
2625
- if (typeof value === "string") return value;
2626
- if (Array.isArray(value)) return value.map((item) => extractText(item)).join("");
2627
- if (value && typeof value === "object") {
2628
- const nested = value;
2629
- return extractText(nested.text ?? nested.content ?? nested.thinking);
2231
+ //#endregion
2232
+ //#region packages/ai/src/transports/openai-completions-stream.ts
2233
+ function extractToolCallThoughtSignature(toolCall) {
2234
+ const tc = toolCall;
2235
+ if (!tc) return;
2236
+ const fromExtra = (tc.extra_content?.google)?.thought_signature;
2237
+ if (typeof fromExtra === "string" && fromExtra.length > 0) return fromExtra;
2238
+ const fromFunction = tc.function?.thought_signature;
2239
+ if (typeof fromFunction === "string" && fromFunction.length > 0) return fromFunction;
2240
+ const fromToolCall = tc.thought_signature;
2241
+ return typeof fromToolCall === "string" && fromToolCall.length > 0 ? fromToolCall : void 0;
2242
+ }
2243
+ async function processCompletionsStream(responseStream, output, model, stream, options) {
2244
+ const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
2245
+ const emitReasoning = options?.emitReasoning ?? true;
2246
+ const compat = getCompat(model);
2247
+ const shouldFilterDeepSeekDsmlText = compat.thinkingFormat === "deepseek";
2248
+ const deepSeekTextFilter = shouldFilterDeepSeekDsmlText ? createDeepSeekTextFilter() : null;
2249
+ const deepSeekToolCallRecoverer = shouldFilterDeepSeekDsmlText ? createDsmlRecoverer() : null;
2250
+ const reasoningTagTextPartitioner = createReasoningTagTextPartitioner();
2251
+ let currentBlock = null;
2252
+ let pendingPostToolCallDeltas = [];
2253
+ let pendingPostToolCallBytes = 0;
2254
+ let isFlushingPendingPostToolCallDeltas = false;
2255
+ const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
2256
+ const toolCallBlocksById = /* @__PURE__ */ new Map();
2257
+ const provisionalCommentaryTags = /* @__PURE__ */ new Map();
2258
+ const toolCallBlockIndices = /* @__PURE__ */ new WeakMap();
2259
+ const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
2260
+ let sawStopFinishReason = false;
2261
+ let sawNativeToolCallDelta = false;
2262
+ const blockIndex = () => output.content.length - 1;
2263
+ const measureUtf8Bytes = (text) => Buffer.byteLength(text, "utf8");
2264
+ let chunkPushedEvent = false;
2265
+ const pushStreamEvent = (event) => {
2266
+ chunkPushedEvent = true;
2267
+ stream.push(event);
2268
+ };
2269
+ const queuePostToolCallDelta = (next) => {
2270
+ const nextBytes = measureUtf8Bytes(next.text);
2271
+ if (pendingPostToolCallBytes + nextBytes > MAX_POST_TOOL_CALL_BUFFER_BYTES) throw new Error("Exceeded post-tool-call delta buffer limit");
2272
+ pendingPostToolCallBytes += nextBytes;
2273
+ const previous = pendingPostToolCallDeltas[pendingPostToolCallDeltas.length - 1];
2274
+ if (!previous || previous.kind !== next.kind) {
2275
+ pendingPostToolCallDeltas.push(next);
2276
+ return;
2630
2277
  }
2631
- return "";
2278
+ if (next.kind === "thinking" && previous.kind === "thinking") {
2279
+ if (previous.signature !== next.signature) {
2280
+ pendingPostToolCallDeltas.push(next);
2281
+ return;
2282
+ }
2283
+ previous.text += next.text;
2284
+ return;
2285
+ }
2286
+ previous.text += next.text;
2287
+ };
2288
+ const appendThinkingDeltaInternal = (reasoningDelta) => {
2289
+ if (!currentBlock || currentBlock.type !== "thinking") {
2290
+ currentBlock = {
2291
+ type: "thinking",
2292
+ thinking: "",
2293
+ ...reasoningDelta.signature ? { thinkingSignature: reasoningDelta.signature } : {}
2294
+ };
2295
+ output.content.push(currentBlock);
2296
+ pushStreamEvent({
2297
+ type: "thinking_start",
2298
+ contentIndex: blockIndex(),
2299
+ partial: output
2300
+ });
2301
+ }
2302
+ currentBlock.thinking += reasoningDelta.text;
2303
+ pushStreamEvent({
2304
+ type: "thinking_delta",
2305
+ contentIndex: blockIndex(),
2306
+ delta: reasoningDelta.text,
2307
+ partial: output
2308
+ });
2309
+ };
2310
+ const appendTextDeltaInternal = (text) => {
2311
+ if (!currentBlock || currentBlock.type !== "text") {
2312
+ currentBlock = {
2313
+ type: "text",
2314
+ text: ""
2315
+ };
2316
+ output.content.push(currentBlock);
2317
+ pushStreamEvent({
2318
+ type: "text_start",
2319
+ contentIndex: blockIndex(),
2320
+ partial: output
2321
+ });
2322
+ }
2323
+ currentBlock.text += text;
2324
+ pushStreamEvent({
2325
+ type: "text_delta",
2326
+ contentIndex: blockIndex(),
2327
+ delta: text
2328
+ });
2329
+ };
2330
+ const flushPendingPostToolCallDeltas = () => {
2331
+ if (isFlushingPendingPostToolCallDeltas || currentBlock?.type === "toolCall" || pendingPostToolCallDeltas.length === 0) return;
2332
+ isFlushingPendingPostToolCallDeltas = true;
2333
+ const bufferedDeltas = pendingPostToolCallDeltas;
2334
+ pendingPostToolCallDeltas = [];
2335
+ pendingPostToolCallBytes = 0;
2336
+ for (const delta of bufferedDeltas) if (delta.kind === "text") appendTextDeltaInternal(delta.text);
2337
+ else if (emitReasoning) appendThinkingDeltaInternal(delta);
2338
+ isFlushingPendingPostToolCallDeltas = false;
2339
+ };
2340
+ const appendThinkingDelta = (reasoningDelta) => {
2341
+ flushPendingPostToolCallDeltas();
2342
+ appendThinkingDeltaInternal(reasoningDelta);
2632
2343
  };
2633
- const text = extractText(record.text ?? record.content ?? record.thinking);
2634
- if (!text) return [];
2635
- if (type.includes("thinking") || type.includes("reasoning")) return [{
2636
- kind: "thinking",
2637
- signature: "content",
2638
- text
2639
- }];
2640
- if (type === "text" || type === "output_text" || type.endsWith(".output_text")) return [{
2641
- kind: "text",
2642
- text
2643
- }];
2644
- return [];
2645
- }
2646
- function getCompletionsReasoningDeltas(delta, visibleReasoningDetailTypes) {
2647
- const output = [];
2648
- const pushDelta = (next) => {
2649
- const previous = output[output.length - 1];
2650
- if (!previous || previous.kind !== next.kind) {
2651
- output.push(next);
2652
- return;
2344
+ const appendTextDelta = (text) => {
2345
+ flushPendingPostToolCallDeltas();
2346
+ appendTextDeltaInternal(text);
2347
+ };
2348
+ const appendVisibleTextDelta = (text) => {
2349
+ if (!text) return;
2350
+ if (currentBlock?.type === "toolCall") queuePostToolCallDelta({
2351
+ kind: "text",
2352
+ text
2353
+ });
2354
+ else appendTextDelta(text);
2355
+ };
2356
+ const appendRecoveredToolCall = (toolCall) => {
2357
+ if (currentBlock?.type === "toolCall") {
2358
+ currentBlock = null;
2359
+ flushPendingPostToolCallDeltas();
2653
2360
  }
2654
- if (next.kind === "thinking" && previous.kind === "thinking") {
2655
- if (previous.signature !== next.signature) {
2656
- output.push(next);
2657
- return;
2361
+ rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
2362
+ const block = {
2363
+ type: "toolCall",
2364
+ id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
2365
+ name: toolCall.name,
2366
+ arguments: toolCall.arguments,
2367
+ partialArgs: toolCall.partialArgs
2368
+ };
2369
+ currentBlock = block;
2370
+ output.content.push(block);
2371
+ toolCallBlockIndices.set(block, output.content.length - 1);
2372
+ pushStreamEvent({
2373
+ type: "toolcall_start",
2374
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2375
+ partial: output
2376
+ });
2377
+ pushStreamEvent({
2378
+ type: "toolcall_delta",
2379
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2380
+ delta: toolCall.partialArgs,
2381
+ partial: output
2382
+ });
2383
+ };
2384
+ const appendFilteredVisibleTextDelta = (text) => {
2385
+ const recoveredParts = deepSeekToolCallRecoverer?.push(text) ?? [{
2386
+ kind: "text",
2387
+ text
2388
+ }];
2389
+ for (const recoveredPart of recoveredParts) {
2390
+ if (recoveredPart.kind === "toolCall") {
2391
+ appendRecoveredToolCall(recoveredPart);
2392
+ continue;
2658
2393
  }
2659
- previous.text += next.text;
2660
- return;
2394
+ const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
2395
+ for (const part of parts) appendVisibleTextDelta(part);
2661
2396
  }
2662
- previous.text += next.text;
2663
2397
  };
2664
- const reasoningDetails = delta.reasoning_details;
2665
- let usedReasoningThinkingDetails = false;
2666
- if (Array.isArray(reasoningDetails)) {
2667
- const visibleTypes = new Set(visibleReasoningDetailTypes);
2668
- for (const item of reasoningDetails) {
2669
- const detail = item;
2670
- if (typeof detail.text !== "string" || !detail.text) continue;
2671
- if (detail.type === "reasoning.text") {
2672
- usedReasoningThinkingDetails = true;
2673
- pushDelta({
2674
- kind: "thinking",
2675
- signature: "reasoning_details",
2676
- text: detail.text
2677
- });
2398
+ const flushDeepSeekToolCallRecovererAtEnd = () => {
2399
+ const recoveredParts = deepSeekToolCallRecoverer?.flush();
2400
+ if (!recoveredParts) return;
2401
+ for (const recoveredPart of recoveredParts) {
2402
+ if (recoveredPart.kind === "toolCall") {
2403
+ appendRecoveredToolCall(recoveredPart);
2678
2404
  continue;
2679
2405
  }
2680
- if (typeof detail.type === "string" && visibleTypes.has(detail.type)) pushDelta({
2681
- kind: "text",
2682
- text: detail.text
2683
- });
2406
+ const parts = deepSeekTextFilter?.push(recoveredPart.text) ?? [recoveredPart.text];
2407
+ for (const part of parts) appendVisibleTextDelta(part);
2684
2408
  }
2685
- }
2686
- if (!usedReasoningThinkingDetails) for (const field of [
2687
- "reasoning_content",
2688
- "reasoning",
2689
- "reasoning_text"
2690
- ]) {
2691
- const value = delta[field];
2692
- if (typeof value === "string" && value.length > 0) {
2693
- pushDelta({
2694
- kind: "thinking",
2695
- signature: field,
2696
- text: value
2697
- });
2698
- break;
2409
+ };
2410
+ const flushDeepSeekTextFilterAtEnd = () => {
2411
+ const parts = deepSeekTextFilter?.flush();
2412
+ if (!parts) return;
2413
+ for (const part of parts) appendVisibleTextDelta(part);
2414
+ };
2415
+ const appendRoutedContentDelta = (delta) => {
2416
+ if (delta.kind === "text") {
2417
+ appendFilteredVisibleTextDelta(delta.text);
2418
+ return;
2699
2419
  }
2700
- }
2701
- return output;
2702
- }
2703
- function resolveOpenAICompletionsReasoningEffort(options) {
2704
- return options?.reasoningEffort ?? options?.reasoning ?? "high";
2705
- }
2706
- function shouldEmitOpenAICompletionsReasoning(model, options) {
2707
- if (!model.reasoning) return false;
2708
- const effort = resolveOpenAICompletionsReasoningEffort(options);
2709
- if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
2710
- return true;
2711
- }
2712
- function shouldEmitOpenAICompletionsReasoningForModel(model, options) {
2713
- return shouldEmitOpenAICompletionsReasoning(model, options);
2714
- }
2715
- function resolveOpenAICompletionsMaxTokens(model, options) {
2716
- if (options?.maxTokens) return {
2717
- maxTokens: options.maxTokens,
2718
- clampToModelMaxTokens: true
2420
+ if (!emitReasoning) return;
2421
+ if (currentBlock?.type === "toolCall") queuePostToolCallDelta(delta);
2422
+ else appendThinkingDelta(delta);
2719
2423
  };
2720
- const paramsMaxTokens = resolveMaxTokensParam(model.params);
2721
- if (paramsMaxTokens) return {
2722
- maxTokens: paramsMaxTokens,
2723
- clampToModelMaxTokens: false
2424
+ const appendPartitionedVisibleDelta = (delta) => {
2425
+ if (delta.kind === "text") appendFilteredVisibleTextDelta(delta.text);
2724
2426
  };
2725
- return {
2726
- maxTokens: model.maxTokens,
2727
- clampToModelMaxTokens: false
2427
+ const emitReasoningUsageActivity = (hasReasoningUsageActivity) => {
2428
+ if (!hasReasoningUsageActivity || chunkPushedEvent || !emitReasoning) return;
2429
+ const latestBlock = output.content[output.content.length - 1];
2430
+ if (currentBlock?.type === "text" || currentBlock?.type === "toolCall") return;
2431
+ if (latestBlock?.type === "text" || latestBlock?.type === "toolCall") return;
2432
+ appendThinkingDelta({ text: "" });
2728
2433
  };
2729
- }
2730
- function resolveOpenAICompletionsModelMaxTokens(model) {
2731
- return typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) && model.maxTokens > 0 ? Math.floor(model.maxTokens) : void 0;
2732
- }
2733
- const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
2734
- const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
2735
- function estimateOpenAICompletionsInputTokens(payload) {
2736
- let adjustedChars = 0;
2737
- adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
2738
- if (Array.isArray(payload.tools) && payload.tools.length > 0) try {
2739
- adjustedChars += estimateStringChars(JSON.stringify(payload.tools));
2740
- } catch {
2741
- adjustedChars += 1024;
2742
- }
2743
- if (payload.response_format !== void 0) try {
2744
- adjustedChars += estimateStringChars(JSON.stringify(payload.response_format));
2745
- } catch {
2746
- adjustedChars += 256;
2747
- }
2748
- return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
2749
- }
2750
- function estimateOpenAICompletionsMessagesChars(messages) {
2751
- if (!Array.isArray(messages)) return 0;
2752
- let adjustedChars = 0;
2753
- for (const message of messages) {
2754
- if (!message || typeof message !== "object") continue;
2755
- const record = message;
2756
- adjustedChars += estimateOpenAICompletionsContentChars(record.content);
2757
- for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) adjustedChars += estimateOpenAICompletionsContentChars(record[field]);
2758
- if (record.tool_calls !== void 0) try {
2759
- adjustedChars += estimateStringChars(JSON.stringify(record.tool_calls));
2760
- } catch {
2761
- adjustedChars += 256;
2434
+ const flushReasoningTagTextPartitionerAtEnd = () => {
2435
+ for (const delta of reasoningTagTextPartitioner.flush()) appendPartitionedVisibleDelta(delta);
2436
+ };
2437
+ const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
2438
+ const guardedStream = withFirstStreamEventTimeout(responseStream, {
2439
+ provider: model.provider,
2440
+ api: model.api,
2441
+ model: model.id,
2442
+ timeoutMs: options?.firstEventTimeoutMs ?? 0,
2443
+ stage: "completions",
2444
+ abort: options?.abortFirstEventStream,
2445
+ onTimeout: options?.onFirstEventTimeout,
2446
+ hint: "The provider may be stalled while parsing the tool payload; retry with a smaller tool surface or enable OPENCLAW_DEBUG_MODEL_PAYLOAD=tools to inspect exposed tools."
2447
+ });
2448
+ for await (const rawChunk of guardedStream) {
2449
+ throwIfModelStreamAborted(options?.signal);
2450
+ chunkPushedEvent = false;
2451
+ if (!rawChunk || typeof rawChunk !== "object") {
2452
+ await cooperativeScheduler.afterEvent();
2453
+ continue;
2454
+ }
2455
+ notifyLlmRequestActivity(options?.signal);
2456
+ const chunk = rawChunk;
2457
+ output.responseId ||= chunk.id;
2458
+ let hasReasoningUsageActivity = false;
2459
+ if (chunk.usage) {
2460
+ output.usage = parseOpenAICompletionsUsage(chunk.usage, model);
2461
+ hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(chunk.usage);
2462
+ }
2463
+ const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
2464
+ if (!choice) {
2465
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
2466
+ await cooperativeScheduler.afterEvent();
2467
+ continue;
2468
+ }
2469
+ const choiceUsage = choice.usage;
2470
+ if (!chunk.usage && choiceUsage) {
2471
+ output.usage = parseOpenAICompletionsUsage(choiceUsage, model);
2472
+ hasReasoningUsageActivity = hasOpenAICompletionsReasoningUsageActivity(choiceUsage);
2473
+ }
2474
+ if (choice.finish_reason) {
2475
+ const finishReasonResult = mapOpenAIStopReason(choice.finish_reason, { allowSingularToolCall: true });
2476
+ output.stopReason = finishReasonResult.stopReason;
2477
+ if (finishReasonResult.stopReason === "stop") sawStopFinishReason = true;
2478
+ if (finishReasonResult.errorMessage) output.errorMessage = finishReasonResult.errorMessage;
2479
+ }
2480
+ const rawChoiceDelta = choice.delta ?? choice.message;
2481
+ if (!rawChoiceDelta) {
2482
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
2483
+ await cooperativeScheduler.afterEvent();
2484
+ continue;
2485
+ }
2486
+ for (const normalizedDelta of normalizeToolCallDeltas(rawChoiceDelta, choice.finish_reason)) {
2487
+ const choiceDelta = normalizedDelta.delta;
2488
+ const reasoningDeltas = getCompletionsReasoningDeltas(choiceDelta, compat.visibleReasoningDetailTypes);
2489
+ const hasMirroredReasoning = reasoningDeltas.some((delta) => delta.kind === "thinking");
2490
+ if (hasMirroredReasoning) reasoningTagTextPartitioner.markStrict();
2491
+ const contentDeltas = readOpenAICompletionsContentDeltas(choiceDelta.content, choiceDelta.refusal, reasoningDeltas.filter((reasoningDelta) => reasoningDelta.kind === "thinking").map((reasoningDelta) => reasoningDelta.text));
2492
+ const appendReasoningDeltas = () => {
2493
+ for (const reasoningDelta of reasoningDeltas) {
2494
+ if (reasoningDelta.kind === "thinking" && !emitReasoning) continue;
2495
+ if (currentBlock?.type === "toolCall") {
2496
+ queuePostToolCallDelta({ ...reasoningDelta });
2497
+ continue;
2498
+ }
2499
+ if (reasoningDelta.kind === "text") appendTextDelta(reasoningDelta.text);
2500
+ else if (emitReasoning) appendThinkingDelta(reasoningDelta);
2501
+ }
2502
+ };
2503
+ if (hasMirroredReasoning) appendReasoningDeltas();
2504
+ for (const contentDelta of contentDeltas) if (contentDelta.kind === "text") {
2505
+ const routedDeltas = hasMirroredReasoning ? reasoningTagTextPartitioner.push(contentDelta.text) : reasoningTagTextPartitioner.pushVisible(contentDelta.text);
2506
+ for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
2507
+ } else {
2508
+ if (reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
2509
+ appendRoutedContentDelta(contentDelta);
2510
+ }
2511
+ if (!hasMirroredReasoning) appendReasoningDeltas();
2512
+ const toolCallDeltas = normalizedDelta.toolCalls;
2513
+ if (toolCallDeltas.length > 0) {
2514
+ sawNativeToolCallDelta = true;
2515
+ flushReasoningTagTextPartitionerAtEnd();
2516
+ rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
2517
+ for (const toolCall of toolCallDeltas) {
2518
+ const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
2519
+ let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
2520
+ if (!block && toolCall.id) block = toolCallBlocksById.get(toolCall.id);
2521
+ if (!block) {
2522
+ if (currentBlock?.type === "toolCall") {
2523
+ currentBlock = null;
2524
+ flushPendingPostToolCallDeltas();
2525
+ }
2526
+ const initialSig = extractToolCallThoughtSignature(toolCall);
2527
+ block = {
2528
+ type: "toolCall",
2529
+ id: toolCall.id || "",
2530
+ name: toolCall.function?.name || "",
2531
+ arguments: {},
2532
+ partialArgs: "",
2533
+ ...initialSig ? { thoughtSignature: initialSig } : {}
2534
+ };
2535
+ output.content.push(block);
2536
+ toolCallBlockIndices.set(block, output.content.length - 1);
2537
+ pushStreamEvent({
2538
+ type: "toolcall_start",
2539
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2540
+ partial: output
2541
+ });
2542
+ }
2543
+ if (streamIndex !== void 0 && !toolCallBlocksByIndex.has(streamIndex)) toolCallBlocksByIndex.set(streamIndex, block);
2544
+ if (toolCall.id) {
2545
+ block.id = toolCall.id;
2546
+ toolCallBlocksById.set(toolCall.id, block);
2547
+ }
2548
+ currentBlock = block;
2549
+ if (toolCall.function?.name) block.name = toolCall.function.name;
2550
+ const deltaSig = extractToolCallThoughtSignature(toolCall);
2551
+ if (deltaSig) block.thoughtSignature = deltaSig;
2552
+ if (toolCall.function?.arguments) {
2553
+ block.partialArgs += toolCall.function.arguments;
2554
+ block.arguments = parseStreamingJson(block.partialArgs);
2555
+ pushStreamEvent({
2556
+ type: "toolcall_delta",
2557
+ contentIndex: toolCallBlockIndices.get(block) ?? -1,
2558
+ delta: toolCall.function.arguments,
2559
+ partial: output
2560
+ });
2561
+ }
2562
+ }
2563
+ }
2762
2564
  }
2565
+ flushPendingPostToolCallDeltas();
2566
+ emitReasoningUsageActivity(hasReasoningUsageActivity);
2567
+ await cooperativeScheduler.afterEvent();
2763
2568
  }
2764
- return adjustedChars;
2765
- }
2766
- function estimateOpenAICompletionsContentChars(value) {
2767
- if (typeof value === "string") return estimateStringChars(value);
2768
- if (!Array.isArray(value)) return 0;
2769
- let adjustedChars = 0;
2770
- for (const block of value) {
2771
- if (!block || typeof block !== "object") continue;
2772
- const record = block;
2773
- if (record.type === "image_url" || record.type === "input_image") {
2774
- adjustedChars += OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE;
2775
- continue;
2776
- }
2777
- const text = record.text;
2778
- if (typeof text === "string") {
2779
- adjustedChars += estimateStringChars(text);
2780
- continue;
2781
- }
2782
- try {
2783
- adjustedChars += estimateStringChars(JSON.stringify(block));
2784
- } catch {
2785
- adjustedChars += 256;
2569
+ flushReasoningTagTextPartitionerAtEnd();
2570
+ flushDeepSeekToolCallRecovererAtEnd();
2571
+ flushDeepSeekTextFilterAtEnd();
2572
+ currentBlock = null;
2573
+ flushPendingPostToolCallDeltas();
2574
+ finalizeOpenAICompletionsToolCalls(output, {
2575
+ allowSilentToolCallPromotion: sawStopFinishReason || sawNativeToolCallDelta && (options?.sawStreamDONE?.() ?? false),
2576
+ onConfirmedToolCall(block, contentIndex) {
2577
+ pushStreamEvent({
2578
+ type: "toolcall_end",
2579
+ contentIndex,
2580
+ toolCall: block,
2581
+ partial: output
2582
+ });
2786
2583
  }
2787
- }
2788
- return adjustedChars;
2789
- }
2790
- function resolveOpenAICompletionsEffectiveContextTokens(model) {
2791
- const contextTokens = model.contextTokens;
2792
- if (typeof contextTokens === "number" && Number.isFinite(contextTokens) && contextTokens > 0) return contextTokens;
2793
- return typeof model.contextWindow === "number" && Number.isFinite(model.contextWindow) && model.contextWindow > 0 ? model.contextWindow : void 0;
2794
- }
2795
- function isQwenOpenAICompletionsThinkingFormat(format) {
2796
- return format === "qwen" || format === "qwen-chat-template";
2797
- }
2798
- function isOpenAICompletionsThinkingEnabled(effort) {
2799
- const normalized = effort.trim().toLowerCase();
2800
- return normalized !== "off" && normalized !== "none";
2801
- }
2802
- function setQwenChatTemplateThinking(params, enabled) {
2803
- const existing = params.chat_template_kwargs;
2804
- params.chat_template_kwargs = existing && typeof existing === "object" && !Array.isArray(existing) ? {
2805
- ...existing,
2806
- enable_thinking: enabled
2807
- } : { enable_thinking: enabled };
2808
- }
2809
- function applyQwenOpenAICompletionsThinkingParams(params) {
2810
- if (!params.modelReasoning || !isQwenOpenAICompletionsThinkingFormat(params.compatThinkingFormat)) return false;
2811
- const enabled = isOpenAICompletionsThinkingEnabled(params.requestedEffort);
2812
- if (params.compatThinkingFormat === "qwen-chat-template") setQwenChatTemplateThinking(params.payload, enabled);
2813
- else params.payload.enable_thinking = enabled;
2814
- return true;
2815
- }
2816
- function applyTogetherOpenAICompletionsThinkingParams(params) {
2817
- if (!params.modelReasoning || params.compatThinkingFormat !== "together") return false;
2818
- params.payload.reasoning = { enabled: isOpenAICompletionsThinkingEnabled(params.requestedEffort) };
2819
- return true;
2820
- }
2821
- function convertTools(tools, compat, model) {
2822
- const projection = projectOpenAITools(tools);
2823
- const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, resolveOpenAIStrictToolSetting(model, {
2824
- transport: "stream",
2825
- supportsStrictMode: compat?.supportsStrictMode
2826
- }), {
2827
- transport: "completions",
2828
- model
2829
2584
  });
2830
- return {
2831
- projection,
2832
- tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
2833
- const functionTool = {
2834
- name: tool.name,
2835
- description: tool.description,
2836
- parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
2837
- };
2838
- if (strict !== void 0) functionTool.strict = strict;
2839
- return {
2840
- type: "function",
2841
- function: functionTool
2842
- };
2843
- })
2844
- };
2845
- }
2846
- function extractGoogleThoughtSignature(toolCall) {
2847
- const tc = toolCall;
2848
- if (!tc) return;
2849
- const fromExtra = (tc.extra_content?.google)?.thought_signature;
2850
- if (typeof fromExtra === "string" && fromExtra.length > 0) return fromExtra;
2851
- const fromFunction = tc.function?.thought_signature;
2852
- return typeof fromFunction === "string" && fromFunction.length > 0 ? fromFunction : void 0;
2853
- }
2854
- function isGoogleOpenAICompatModel(model) {
2855
- const endpointClass = detectOpenAICompletionsCompat(model).capabilities.endpointClass;
2856
- return model.provider === "google" || endpointClass === "google-generative-ai" || endpointClass === "google-vertex";
2857
- }
2858
- function requiresGoogleCompatToolCallThoughtSignature(model) {
2859
- return isGoogleGemini3ProModel(model.id) || isGoogleGemini3FlashModel(model.id);
2860
- }
2861
- const GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE = /[\u2026]|\.\.\./;
2862
- const GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE = /^[A-Za-z0-9+/=]+$/;
2863
- function hasGoogleCompatThoughtSignatureTruncationFootprint(value) {
2864
- return GOOGLE_COMPAT_THOUGHT_SIGNATURE_ELLIPSIS_RE.test(value) || GOOGLE_COMPAT_THOUGHT_SIGNATURE_BASE64_RE.test(value) && value.length % 4 !== 0;
2585
+ if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
2586
+ if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
2865
2587
  }
2866
- function injectToolCallThoughtSignatures(outgoingMessages, context, model) {
2867
- if (!isGoogleOpenAICompatModel(model)) return;
2868
- const sigById = /* @__PURE__ */ new Map();
2869
- const fallbackSig = requiresGoogleCompatToolCallThoughtSignature(model) ? GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP : void 0;
2870
- for (const msg of context.messages ?? []) {
2871
- if (msg.role !== "assistant") continue;
2872
- const source = msg;
2873
- if (!Array.isArray(source.content)) continue;
2874
- for (const block of source.content) {
2875
- if (block.type !== "toolCall") continue;
2876
- const id = block.id;
2877
- const sig = block.thoughtSignature;
2878
- if (typeof id === "string" && typeof sig === "string" && sig.length > 0) {
2879
- const isSameRoute = source.api === model.api && source.provider === model.provider && source.model === model.id;
2880
- if (!isSameRoute && !fallbackSig) continue;
2881
- sigById.set(id, isSameRoute ? sig : fallbackSig ?? sig);
2588
+ function getCompletionsReasoningDeltas(delta, visibleReasoningDetailTypes) {
2589
+ const output = [];
2590
+ const pushDelta = (next) => {
2591
+ const previous = output[output.length - 1];
2592
+ if (!previous || previous.kind !== next.kind) {
2593
+ output.push(next);
2594
+ return;
2595
+ }
2596
+ if (next.kind === "thinking" && previous.kind === "thinking") {
2597
+ if (previous.signature !== next.signature) {
2598
+ output.push(next);
2599
+ return;
2882
2600
  }
2601
+ previous.text += next.text;
2602
+ return;
2883
2603
  }
2884
- }
2885
- if (sigById.size === 0 && !fallbackSig) return;
2886
- for (const message of outgoingMessages) {
2887
- const toolCalls = message.tool_calls;
2888
- if (!Array.isArray(toolCalls)) continue;
2889
- for (const toolCall of toolCalls) {
2890
- const id = toolCall.id;
2891
- if (typeof id !== "string") continue;
2892
- let sig = sigById.get(id) ?? fallbackSig;
2893
- if (typeof sig === "string" && sig.length > 0) {
2894
- if (hasGoogleCompatThoughtSignatureTruncationFootprint(sig.trim())) sig = fallbackSig;
2604
+ previous.text += next.text;
2605
+ };
2606
+ const reasoningDetails = delta.reasoning_details;
2607
+ let usedReasoningThinkingDetails = false;
2608
+ if (Array.isArray(reasoningDetails)) {
2609
+ const visibleTypes = new Set(visibleReasoningDetailTypes);
2610
+ for (const item of reasoningDetails) {
2611
+ const detail = item;
2612
+ if (typeof detail.text !== "string" || !detail.text) continue;
2613
+ if (detail.type === "reasoning.text") {
2614
+ usedReasoningThinkingDetails = true;
2615
+ pushDelta({
2616
+ kind: "thinking",
2617
+ signature: "reasoning_details",
2618
+ text: detail.text
2619
+ });
2620
+ continue;
2895
2621
  }
2896
- if (typeof sig !== "string" || sig.length === 0) continue;
2897
- const extra = toolCall.extra_content && typeof toolCall.extra_content === "object" ? toolCall.extra_content : {};
2898
- toolCall.extra_content = extra;
2899
- const google = extra.google && typeof extra.google === "object" ? extra.google : {};
2900
- extra.google = google;
2901
- google.thought_signature = sig;
2622
+ if (typeof detail.type === "string" && visibleTypes.has(detail.type)) pushDelta({
2623
+ kind: "text",
2624
+ text: detail.text
2625
+ });
2902
2626
  }
2903
2627
  }
2904
- }
2905
- const COMPLETIONS_REASONING_REPLAY_FIELDS = [
2906
- "reasoning_details",
2907
- "reasoning_content",
2908
- "reasoning",
2909
- "reasoning_text"
2910
- ];
2911
- function stripCompletionsReasoningReplayFields(record) {
2912
- for (const field of COMPLETIONS_REASONING_REPLAY_FIELDS) if (field in record) delete record[field];
2913
- }
2914
- function sanitizeOpenRouterReasoningReplayFields(record) {
2915
- const reasoningDetails = record.reasoning_details;
2916
- if (typeof reasoningDetails === "string") {
2917
- if (reasoningDetails.length > 0 && typeof record.reasoning !== "string") record.reasoning = reasoningDetails;
2918
- delete record.reasoning_details;
2919
- } else if (reasoningDetails !== void 0 && !Array.isArray(reasoningDetails)) delete record.reasoning_details;
2920
- if ("reasoning" in record && (typeof record.reasoning !== "string" || record.reasoning === "")) delete record.reasoning;
2921
- if ("reasoning_content" in record && (typeof record.reasoning_content !== "string" || record.reasoning_content === "")) delete record.reasoning_content;
2922
- const reasoningText = record.reasoning_text;
2923
- if (typeof reasoningText === "string" && reasoningText.length > 0 && typeof record.reasoning !== "string" && typeof record.reasoning_content !== "string") record.reasoning = reasoningText;
2924
- if ("reasoning_text" in record) delete record.reasoning_text;
2925
- }
2926
- function sanitizeReasoningContentReplayFields(record) {
2927
- if ("reasoning_content" in record && typeof record.reasoning_content !== "string") delete record.reasoning_content;
2928
- delete record.reasoning_details;
2929
- delete record.reasoning;
2930
- delete record.reasoning_text;
2931
- }
2932
- const REASONING_CONTENT_REPLAY_MODEL_IDS = /* @__PURE__ */ new Set([
2933
- "deepseek-v4-flash",
2934
- "deepseek-v4-pro",
2935
- "kimi-for-coding",
2936
- "kimi-k2.5",
2937
- "kimi-k2.6",
2938
- "kimi-k2.7-code",
2939
- "kimi-k2.7-code-highspeed",
2940
- "kimi-k3",
2941
- "kimi-k2-thinking",
2942
- "kimi-k2-thinking-turbo",
2943
- "mimo-v2-pro",
2944
- "mimo-v2-omni",
2945
- "mimo-v2.5",
2946
- "mimo-v2.5-pro",
2947
- "mimo-v2.6-pro"
2948
- ]);
2949
- const REASONING_CONTENT_REPLAY_TIER_SUFFIXES = [
2950
- "-free",
2951
- "-paid",
2952
- "-trial"
2953
- ];
2954
- function stripReasoningContentReplayTierSuffix(modelId) {
2955
- for (const suffix of REASONING_CONTENT_REPLAY_TIER_SUFFIXES) if (modelId.length > suffix.length && modelId.endsWith(suffix)) return modelId.slice(0, -suffix.length);
2956
- return modelId;
2957
- }
2958
- function getReasoningContentReplayModelIdCandidates(modelId) {
2959
- if (typeof modelId !== "string") return [];
2960
- const normalized = modelId.trim().toLowerCase();
2961
- if (!normalized) return [];
2962
- const parts = normalized.split("/").filter(Boolean);
2963
- const finalPart = parts[parts.length - 1] ?? normalized;
2964
- const candidates = [finalPart];
2965
- const colonParts = finalPart.split(":").filter(Boolean);
2966
- if (colonParts.length > 1) candidates.push(colonParts[0] ?? "", colonParts[colonParts.length - 1] ?? "");
2967
- const baseCount = candidates.length;
2968
- for (let index = 0; index < baseCount; index += 1) {
2969
- const candidate = candidates[index];
2970
- if (typeof candidate !== "string") continue;
2971
- const stripped = stripReasoningContentReplayTierSuffix(candidate);
2972
- if (stripped !== candidate) candidates.push(stripped);
2628
+ if (!usedReasoningThinkingDetails) for (const field of [
2629
+ "reasoning_content",
2630
+ "reasoning",
2631
+ "reasoning_text"
2632
+ ]) {
2633
+ const value = delta[field];
2634
+ if (typeof value === "string" && value.length > 0) {
2635
+ pushDelta({
2636
+ kind: "thinking",
2637
+ signature: field,
2638
+ text: value
2639
+ });
2640
+ break;
2641
+ }
2973
2642
  }
2974
- return uniqueStrings(candidates.filter(Boolean));
2975
- }
2976
- function shouldPreserveReasoningContentReplay(model, compat) {
2977
- if (compat.requiresReasoningContentOnAssistantMessages || compat.thinkingFormat === "deepseek" || compat.thinkingFormat === "zai" || shouldTrustReasoningContentReplayMetadata(model)) return true;
2978
- return getReasoningContentReplayModelIdCandidates(model.id).some((modelId) => REASONING_CONTENT_REPLAY_MODEL_IDS.has(modelId));
2643
+ return output;
2979
2644
  }
2980
- function shouldPreserveOpenRouterReasoningReplay(model) {
2981
- if (model.provider !== "openrouter") return true;
2982
- const normalizedModelId = model.id.trim().toLowerCase();
2983
- return !(normalizedModelId.startsWith("anthropic/") || normalizedModelId.startsWith("x-ai/"));
2645
+ function resolveOpenAICompletionsReasoningEffort(options) {
2646
+ return options?.reasoningEffort ?? options?.reasoning ?? "high";
2984
2647
  }
2985
- function shouldTrustReasoningContentReplayMetadata(model) {
2648
+ function shouldEmitOpenAICompletionsReasoning(model, options) {
2986
2649
  if (!model.reasoning) return false;
2987
- if (model.provider.trim().toLowerCase() === "openai") return false;
2988
- return shouldPreserveOpenRouterReasoningReplay(model);
2650
+ const effort = resolveOpenAICompletionsReasoningEffort(options);
2651
+ if (!effort || !isOpenAICompletionsThinkingEnabled(effort)) return false;
2652
+ return true;
2989
2653
  }
2990
- function sanitizeCompletionsReasoningReplayFields(messages, options) {
2991
- if (!Array.isArray(messages)) return;
2992
- for (const msg of messages) {
2993
- if (!msg || typeof msg !== "object") continue;
2994
- const record = msg;
2995
- if (record.role !== "assistant") continue;
2996
- if (options.preserveOpenRouterReasoning) sanitizeOpenRouterReasoningReplayFields(record);
2997
- else if (options.preserveReasoningContent) sanitizeReasoningContentReplayFields(record);
2998
- else stripCompletionsReasoningReplayFields(record);
2999
- }
2654
+ function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
2655
+ const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
2656
+ return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
3000
2657
  }
3001
- function buildOpenAICompletionsParams(model, context, options) {
3002
- const compat = getCompat(model);
3003
- const compatDetection = detectOpenAICompletionsCompat(model);
3004
- let messages = convertMessages(model, context.systemPrompt ? {
3005
- ...context,
3006
- systemPrompt: stripSystemPromptCacheBoundary(context.systemPrompt)
3007
- } : context, compat);
3008
- injectToolCallThoughtSignatures(messages, context, model);
3009
- sanitizeCompletionsReasoningReplayFields(messages, {
3010
- preserveOpenRouterReasoning: compat.thinkingFormat === "openrouter" && shouldPreserveOpenRouterReasoningReplay(model),
3011
- preserveReasoningContent: shouldPreserveReasoningContentReplay(model, compat)
3012
- });
3013
- if (compat.strictMessageKeys) messages = stripCompletionMessagesToRoleContent(messages);
3014
- const cacheRetention = resolveCacheRetention(options?.cacheRetention);
3015
- const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
3016
- const params = {
3017
- model: model.id,
3018
- messages: compat.requiresStringContent ? flattenCompletionMessagesToStringContent(messages) : messages,
3019
- stream: true
2658
+ //#endregion
2659
+ //#region packages/ai/src/transports/openai-completions-transport.ts
2660
+ function assertOpenAICompletionsPayloadHasConversationTurn(params, model) {
2661
+ const messages = params.messages;
2662
+ if (!Array.isArray(messages) || hasOpenAICompatibleConversationTurn(messages)) return;
2663
+ throw new Error(`OpenAI-compatible chat payload for ${model.provider}/${model.id} contains no non-empty user or assistant messages after compaction and transport transforms; refusing to send a system/tool-only request. Start a new user turn or repair the compacted session history.`);
2664
+ }
2665
+ const SSE_DONE_LINE_RE = /^data:[ \t]*\[DONE\][ \t]*$/i;
2666
+ const SSE_DONE_MAX_LINE_CHARS = 1024;
2667
+ function createSseDoneDetector() {
2668
+ const decoder = new TextDecoder();
2669
+ let line = "";
2670
+ let lineOverflowed = false;
2671
+ let sawDone = false;
2672
+ const finishLine = () => {
2673
+ if (!lineOverflowed && SSE_DONE_LINE_RE.test(line)) sawDone = true;
2674
+ line = "";
2675
+ lineOverflowed = false;
3020
2676
  };
3021
- if (compat.supportsUsageInStreaming) params.stream_options = { include_usage: true };
3022
- if (compat.supportsStore) params.store = false;
3023
- if (compat.supportsPromptCacheKey && promptCacheKey) {
3024
- params.prompt_cache_key = promptCacheKey;
3025
- if (cacheRetention === "long" && compat.supportsLongCacheRetention) params.prompt_cache_retention = "24h";
3026
- }
3027
- if (options?.temperature !== void 0) params.temperature = options.temperature;
3028
- if (options?.topP !== void 0) params.top_p = options.topP;
3029
- const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
3030
- provider: model.provider,
3031
- baseUrl: model.baseUrl,
3032
- hasTools: () => Boolean(context.tools?.length)
3033
- }) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
3034
- if (responseFormat !== void 0) params.response_format = responseFormat;
3035
- if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
3036
- if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
3037
- if (options?.seed !== void 0) params.seed = options.seed;
3038
- if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
3039
- if (supportsModelTools(model)) {
3040
- if (context.tools) {
3041
- const converted = convertTools(context.tools, compat, model);
3042
- if (converted.tools.length > 0 || converted.projection.inputToolCount === 0 && converted.projection.diagnostics.length === 0) params.tools = converted.tools;
3043
- else if (hasToolHistory(context.messages)) params.tools = [];
3044
- if (options?.toolChoice) {
3045
- const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, converted.projection);
3046
- if (toolChoice !== void 0) params.tool_choice = toolChoice;
3047
- } else if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length > 0) params.tool_choice = "auto";
3048
- } else if (hasToolHistory(context.messages)) params.tools = [];
3049
- if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && Array.isArray(params.tools) && params.tools.length === 0) {
3050
- delete params.tools;
3051
- delete params.tool_choice;
2677
+ const observeText = (text) => {
2678
+ for (const char of text) {
2679
+ if (char === "\n" || char === "\r") {
2680
+ finishLine();
2681
+ continue;
2682
+ }
2683
+ if (!lineOverflowed && line.length < SSE_DONE_MAX_LINE_CHARS) line += char;
2684
+ else lineOverflowed = true;
3052
2685
  }
3053
- }
3054
- {
3055
- const maxTokenBudget = resolveOpenAICompletionsMaxTokens(model, options);
3056
- const effectiveMaxTokens = maxTokenBudget.maxTokens;
3057
- const effectiveContextTokens = resolveOpenAICompletionsEffectiveContextTokens(model);
3058
- let clampedMaxTokens = effectiveMaxTokens;
3059
- const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
3060
- if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
3061
- clampedMaxTokens = modelMaxTokens;
3062
- emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
2686
+ };
2687
+ return {
2688
+ observe(chunk) {
2689
+ if (!sawDone) observeText(decoder.decode(chunk, { stream: true }));
2690
+ },
2691
+ finish() {
2692
+ if (sawDone) return;
2693
+ observeText(decoder.decode());
2694
+ if (line || lineOverflowed) finishLine();
2695
+ },
2696
+ sawDone: () => sawDone
2697
+ };
2698
+ }
2699
+ function createOpenAICompletionsClient(model, context, apiKey, optionHeaders, opts) {
2700
+ const clientConfig = buildOpenAICompletionsClientConfig(model, context, optionHeaders);
2701
+ return new OpenAI({
2702
+ apiKey,
2703
+ baseURL: clientConfig.baseURL,
2704
+ dangerouslyAllowBrowser: true,
2705
+ defaultHeaders: clientConfig.defaultHeaders,
2706
+ defaultQuery: clientConfig.defaultQuery,
2707
+ fetch: opts?.fetch ?? buildGuardedModelFetch(model),
2708
+ ...buildOpenAISdkClientOptions(model)
2709
+ });
2710
+ }
2711
+ function buildOpenAICompletionsClientConfig(model, context, optionHeaders) {
2712
+ const headers = buildOpenAIClientHeaders(model, context, optionHeaders);
2713
+ const defaultQuery = {};
2714
+ let baseURL = model.baseUrl;
2715
+ let isAzureHost = false;
2716
+ try {
2717
+ const parsed = new URL(model.baseUrl);
2718
+ isAzureHost = isAzureOpenAICompatibleHost(parsed.hostname.toLowerCase());
2719
+ parsed.searchParams.forEach((value, key) => {
2720
+ if (value) defaultQuery[key] = value;
2721
+ });
2722
+ parsed.search = "";
2723
+ baseURL = parsed.toString().replace(/\/$/, "");
2724
+ } catch {}
2725
+ if (isAzureHost) {
2726
+ const apiVersionHeader = Object.keys(headers).find((key) => key.toLowerCase() === "api-version");
2727
+ if (apiVersionHeader) {
2728
+ const apiVersion = headers[apiVersionHeader]?.trim();
2729
+ delete headers[apiVersionHeader];
2730
+ if (apiVersion && !defaultQuery["api-version"]) defaultQuery["api-version"] = apiVersion;
3063
2731
  }
3064
- if (compatDetection.capabilities.usesExplicitProxyLikeEndpoint && clampedMaxTokens !== void 0 && effectiveContextTokens !== void 0) {
3065
- const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
3066
- const remainingBudget = Math.max(1, effectiveContextTokens - estimatedInputTokens - 1);
3067
- if (clampedMaxTokens > remainingBudget) {
3068
- clampedMaxTokens = remainingBudget;
3069
- emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
2732
+ }
2733
+ return {
2734
+ baseURL,
2735
+ defaultHeaders: headers,
2736
+ defaultQuery: Object.keys(defaultQuery).length > 0 ? defaultQuery : void 0
2737
+ };
2738
+ }
2739
+ function createOpenAICompletionsTransportStreamFn() {
2740
+ return (model, context, options) => {
2741
+ const eventStream = createAssistantMessageEventStream();
2742
+ const stream = eventStream;
2743
+ (async () => {
2744
+ const output = {
2745
+ role: "assistant",
2746
+ content: [],
2747
+ api: model.api,
2748
+ provider: model.provider,
2749
+ model: model.id,
2750
+ usage: {
2751
+ input: 0,
2752
+ output: 0,
2753
+ cacheRead: 0,
2754
+ cacheWrite: 0,
2755
+ totalTokens: 0,
2756
+ cost: {
2757
+ input: 0,
2758
+ output: 0,
2759
+ cacheRead: 0,
2760
+ cacheWrite: 0,
2761
+ total: 0
2762
+ }
2763
+ },
2764
+ stopReason: "stop",
2765
+ timestamp: Date.now()
2766
+ };
2767
+ let firstEventAbort;
2768
+ try {
2769
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
2770
+ const doneDetector = createSseDoneDetector();
2771
+ const baseFetch = buildGuardedModelFetch(model);
2772
+ const doneDetectingFetch = async (url, init) => {
2773
+ const response = await baseFetch(url, init);
2774
+ if (!response.body || !response.ok) return response;
2775
+ if (typeof TransformStream === "undefined" || !response.body.pipeThrough) return response;
2776
+ const transformed = response.body.pipeThrough(new TransformStream({
2777
+ transform(chunk, controller) {
2778
+ doneDetector.observe(chunk);
2779
+ controller.enqueue(chunk);
2780
+ },
2781
+ flush() {
2782
+ doneDetector.finish();
2783
+ }
2784
+ }));
2785
+ return new Response(transformed, {
2786
+ headers: response.headers,
2787
+ status: response.status,
2788
+ statusText: response.statusText
2789
+ });
2790
+ };
2791
+ const client = createOpenAICompletionsClient(model, context, apiKey, options?.headers, { fetch: doneDetectingFetch });
2792
+ let params = buildOpenAICompletionsParams(model, context, options);
2793
+ const nextParams = await options?.onPayload?.(params, model);
2794
+ if (nextParams !== void 0) params = nextParams;
2795
+ if (options?.openclawCodeModeToolSurface === true) {
2796
+ const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
2797
+ enforceCodeModeResponsesToolSurface(params, visibleToolNames);
2798
+ assertCodeModeResponsesToolSurface(params, visibleToolNames);
2799
+ }
2800
+ if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
2801
+ const emitReasoning = shouldEmitOpenAICompletionsReasoning(model, options);
2802
+ firstEventAbort = createFirstStreamEventAbortController(options?.signal);
2803
+ const { data: responseStream, response } = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
2804
+ timeoutMs: options?.timeoutMs,
2805
+ maxRetries: options?.maxRetries
2806
+ })).withResponse();
2807
+ await processCompletionsStream(withProviderResponseHook({
2808
+ stream: responseStream,
2809
+ signal: firstEventAbort.signal,
2810
+ abort: firstEventAbort.abort,
2811
+ hook: createOpenAIResponseHook(options?.onResponse, response, model),
2812
+ onReady: () => stream.push({
2813
+ type: "start",
2814
+ partial: output
2815
+ })
2816
+ }), output, model, stream, {
2817
+ signal: options?.signal,
2818
+ emitReasoning,
2819
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
2820
+ abortFirstEventStream: firstEventAbort.abort,
2821
+ onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
2822
+ sawStreamDONE: doneDetector.sawDone
2823
+ });
2824
+ finalizeTransportStream({
2825
+ stream,
2826
+ output,
2827
+ signal: options?.signal
2828
+ });
2829
+ } catch (error) {
2830
+ failTransportStream({
2831
+ stream,
2832
+ output,
2833
+ signal: options?.signal,
2834
+ error,
2835
+ cleanup: () => {
2836
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
2837
+ finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
2838
+ }
2839
+ });
2840
+ } finally {
2841
+ firstEventAbort?.dispose();
3070
2842
  }
3071
- }
3072
- if (clampedMaxTokens) if (compat.maxTokensField === "max_tokens") params.max_tokens = clampedMaxTokens;
3073
- else params.max_completion_tokens = clampedMaxTokens;
3074
- }
3075
- const completionsReasoningEffort = resolveOpenAICompletionsReasoningEffort(options);
3076
- const resolvedCompletionsReasoningEffort = completionsReasoningEffort ? resolveOpenAIReasoningEffortForModel({
3077
- model,
3078
- effort: completionsReasoningEffort,
3079
- fallbackMap: compat.reasoningEffortMap
3080
- }) : void 0;
3081
- const omitChatCompletionsToolReasoningEffort = Array.isArray(params.tools) && params.tools.length > 0 && (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model) && isKnownOpenAICompletionsEndpoint(model));
3082
- const disableChatCompletionsToolReasoning = Array.isArray(params.tools) && params.tools.length > 0 && isOpenAIGpt56Model(model) && isKnownOpenAICompletionsEndpoint(model);
3083
- const handledQwenThinkingFormat = applyQwenOpenAICompletionsThinkingParams({
3084
- compatThinkingFormat: compat.thinkingFormat,
3085
- modelReasoning: model.reasoning,
3086
- payload: params,
3087
- requestedEffort: completionsReasoningEffort
3088
- });
3089
- applyTogetherOpenAICompletionsThinkingParams({
3090
- compatThinkingFormat: compat.thinkingFormat,
3091
- modelReasoning: model.reasoning,
3092
- payload: params,
3093
- requestedEffort: completionsReasoningEffort
3094
- });
3095
- if (disableChatCompletionsToolReasoning) params.reasoning_effort = "none";
3096
- else if (compat.thinkingFormat === "openrouter" && model.reasoning && resolvedCompletionsReasoningEffort) params.reasoning = { effort: resolvedCompletionsReasoningEffort };
3097
- else if (resolvedCompletionsReasoningEffort && model.reasoning && compat.supportsReasoningEffort && !handledQwenThinkingFormat && !omitChatCompletionsToolReasoningEffort) params.reasoning_effort = resolvedCompletionsReasoningEffort;
3098
- return params;
3099
- }
3100
- function parseTransportChunkUsage(rawUsage, model) {
3101
- const cacheWriteTokens = rawUsage.prompt_tokens_details?.cache_write_tokens || 0;
3102
- const cachedTokens = rawUsage.prompt_tokens_details?.cached_tokens || 0;
3103
- const promptTokens = rawUsage.prompt_tokens || 0;
3104
- const input = Math.max(0, promptTokens - cachedTokens - cacheWriteTokens);
3105
- const outputTokens = rawUsage.completion_tokens || 0;
3106
- const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
3107
- const usage = {
3108
- input,
3109
- output: outputTokens,
3110
- cacheRead: cachedTokens,
3111
- cacheWrite: cacheWriteTokens,
3112
- ...typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) ? { reasoningTokens } : {},
3113
- totalTokens: input + outputTokens + cachedTokens + cacheWriteTokens,
3114
- cost: {
3115
- input: 0,
3116
- output: 0,
3117
- cacheRead: 0,
3118
- cacheWrite: 0,
3119
- total: 0
3120
- }
2843
+ })();
2844
+ return eventStream;
3121
2845
  };
3122
- calculateCost(model, usage);
3123
- applyProviderReportedUsageCost(usage, rawUsage.cost);
3124
- return usage;
3125
- }
3126
- function hasOpenAICompletionsReasoningUsageActivity(rawUsage) {
3127
- const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
3128
- return typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) && reasoningTokens > 0;
3129
2846
  }
3130
- const completionsTesting = {
3131
- getCompat,
3132
- createSseDoneDetector,
3133
- createOpenAICompletionsClient,
3134
- buildOpenAICompletionsClientConfig,
3135
- parseTransportChunkUsage,
3136
- processOpenAICompletionsStream,
3137
- shouldEmitOpenAICompletionsReasoningForModel
3138
- };
3139
- if (process.env.VITEST || false) globalThis.openclawOpenAICompletionsTransportTestApi = completionsTesting;
3140
2847
  //#endregion
3141
- //#region packages/ai/src/transports/openai-responses-payload-policy.ts
3142
- /**
3143
- * OpenAI Responses payload policy.
3144
- * Classifies endpoint capabilities and applies store, prompt-cache,
3145
- * server-compaction, service-tier, and reasoning payload rules.
3146
- */
3147
- const OPENAI_RESPONSES_APIS = /* @__PURE__ */ new Set([
3148
- "openai-responses",
3149
- "azure-openai-responses",
3150
- "openai-chatgpt-responses",
3151
- "openclaw-openai-responses-transport",
3152
- "openclaw-openai-chatgpt-responses-transport"
3153
- ]);
3154
- const OPENAI_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set([
3155
- "openai",
3156
- "azure-openai",
3157
- "azure-openai-responses"
3158
- ]);
3159
- const LOCAL_ENDPOINT_HOSTS = /* @__PURE__ */ new Set([
3160
- "localhost",
3161
- "127.0.0.1",
3162
- "::1",
3163
- "[::1]"
3164
- ]);
3165
- const MODELSTUDIO_NATIVE_BASE_URLS = /* @__PURE__ */ new Set([
3166
- "https://coding-intl.dashscope.aliyuncs.com/v1",
3167
- "https://coding.dashscope.aliyuncs.com/v1",
3168
- "https://dashscope.aliyuncs.com/compatible-mode/v1",
3169
- "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
3170
- ]);
3171
- const MOONSHOT_NATIVE_BASE_URLS = /* @__PURE__ */ new Set(["https://api.moonshot.ai/v1", "https://api.moonshot.cn/v1"]);
3172
- function normalizeLowercaseString(value) {
3173
- const stringValue = readStringValue(value)?.trim().toLowerCase();
3174
- return stringValue ? stringValue : void 0;
3175
- }
3176
- function normalizeComparableBaseUrl(value) {
3177
- const trimmed = readStringValue(value)?.trim();
3178
- if (!trimmed) return;
3179
- const parsedValue = /^[a-z0-9.[\]-]+(?::\d+)?(?:[/?#].*)?$/i.test(trimmed) ? `https://${trimmed}` : trimmed;
3180
- try {
3181
- const url = new URL(parsedValue);
3182
- if (url.protocol !== "http:" && url.protocol !== "https:") return;
3183
- url.hash = "";
3184
- url.search = "";
3185
- return url.toString().replace(/\/+$/, "").toLowerCase();
3186
- } catch {
3187
- return;
2848
+ //#region packages/ai/src/transports/openai-responses-compact-request.ts
2849
+ const COMPACT_REQUEST = Symbol("openaiResponsesCompactRequest");
2850
+ function claimResponsesCompactRequest(options) {
2851
+ const controller = options ? Reflect.get(options, COMPACT_REQUEST) : void 0;
2852
+ if (controller?.claimed === false) {
2853
+ controller.claimed = true;
2854
+ return controller;
3188
2855
  }
3189
2856
  }
3190
- function resolveUrlHostname(value) {
3191
- const trimmed = readStringValue(value)?.trim();
3192
- if (!trimmed) return;
2857
+ /** Run a compact-endpoint request through the session's prepared stream stack. */
2858
+ async function requestPreparedOpenAIResponsesCompaction(streamFn, model, context, options) {
2859
+ const preparedOptions = { ...options };
2860
+ let resolveResult;
2861
+ let rejectResult;
2862
+ const result = new Promise((resolve, reject) => {
2863
+ resolveResult = resolve;
2864
+ rejectResult = reject;
2865
+ });
2866
+ const controller = {
2867
+ claimed: false,
2868
+ resolve: resolveResult,
2869
+ reject: rejectResult
2870
+ };
2871
+ Reflect.set(preparedOptions, COMPACT_REQUEST, controller);
2872
+ const stream = await Promise.resolve(streamFn(model, context, preparedOptions));
2873
+ if (!controller.claimed) throw new Error("Prepared stream did not reach an OpenAI Responses transport");
3193
2874
  try {
3194
- return new URL(trimmed).hostname.toLowerCase();
3195
- } catch {
3196
- try {
3197
- return new URL(`https://${trimmed}`).hostname.toLowerCase();
3198
- } catch {
3199
- return;
3200
- }
2875
+ return await result;
2876
+ } finally {
2877
+ await stream.result().catch(() => void 0);
3201
2878
  }
3202
2879
  }
3203
- function hostMatchesSuffix(host, suffix) {
3204
- return suffix.startsWith(".") || suffix.startsWith("-") ? host.endsWith(suffix) : host === suffix || host.endsWith(`.${suffix}`);
3205
- }
3206
- function isLocalEndpointHost(host) {
3207
- return LOCAL_ENDPOINT_HOSTS.has(host) || host.endsWith(".localhost") || host.endsWith(".local") || host.endsWith(".internal");
3208
- }
3209
- function resolveBundledOpenAIResponsesEndpointClass(baseUrl) {
3210
- const trimmed = readStringValue(baseUrl)?.trim();
3211
- if (!trimmed) return "default";
3212
- const host = resolveUrlHostname(trimmed);
3213
- if (!host) return "invalid";
3214
- const comparableBaseUrl = normalizeComparableBaseUrl(trimmed);
3215
- switch (host) {
3216
- case "api.anthropic.com": return "anthropic-public";
3217
- case "api.cerebras.ai": return "cerebras-native";
3218
- case "llm.chutes.ai": return "chutes-native";
3219
- case "api.deepseek.com": return "deepseek-native";
3220
- case "api.groq.com": return "groq-native";
3221
- case "api.mistral.ai": return "mistral-public";
3222
- case "api.openai.com": return "openai-public";
3223
- case "chatgpt.com": return "openai";
3224
- case "generativelanguage.googleapis.com": return "google-generative-ai";
3225
- case "aiplatform.googleapis.com": return "google-vertex";
3226
- case "api.x.ai": return "xai-native";
3227
- case "api.z.ai": return "zai-native";
3228
- }
3229
- if (hostMatchesSuffix(host, ".githubcopilot.com")) return "github-copilot-native";
3230
- if (hostMatchesSuffix(host, ".openai.azure.com")) return "azure-openai";
3231
- if (hostMatchesSuffix(host, "openrouter.ai")) return "openrouter";
3232
- if (hostMatchesSuffix(host, "opencode.ai")) return "opencode-native";
3233
- if (hostMatchesSuffix(host, "-aiplatform.googleapis.com")) return "google-vertex";
3234
- if (comparableBaseUrl && MOONSHOT_NATIVE_BASE_URLS.has(comparableBaseUrl)) return "moonshot-native";
3235
- if (comparableBaseUrl && MODELSTUDIO_NATIVE_BASE_URLS.has(comparableBaseUrl)) return "modelstudio-native";
3236
- if (isLocalEndpointHost(host)) return "local";
3237
- return "custom";
3238
- }
3239
- function isOpenAIResponsesApi(api) {
3240
- return api !== void 0 && OPENAI_RESPONSES_APIS.has(api);
3241
- }
3242
- function readCompatPayloadBoolean(compat, key) {
3243
- if (!compat || typeof compat !== "object") return;
3244
- const value = compat[key];
3245
- return typeof value === "boolean" ? value : void 0;
3246
- }
3247
- function resolveOpenAIResponsesPayloadCapabilities(model) {
3248
- const provider = normalizeLowercaseString(model.provider);
3249
- const api = normalizeLowercaseString(model.api);
3250
- const isOpenAIProvider = provider === "openai";
3251
- const endpointClass = resolveBundledOpenAIResponsesEndpointClass(model.baseUrl);
3252
- const isResponsesApi = isOpenAIResponsesApi(api);
3253
- const usesConfiguredBaseUrl = endpointClass !== "default";
3254
- const usesKnownNativeOpenAIEndpoint = endpointClass === "openai-public" || endpointClass === "openai" || endpointClass === "azure-openai";
3255
- const usesKnownNativeOpenAIRoute = endpointClass === "default" ? provider === "openai" : usesKnownNativeOpenAIEndpoint;
3256
- const usesExplicitProxyLikeEndpoint = usesConfiguredBaseUrl && !usesKnownNativeOpenAIEndpoint;
3257
- const promptCacheKeySupport = readCompatPayloadBoolean(model.compat, "supportsPromptCacheKey");
3258
- const shouldStripResponsesPromptCache = promptCacheKeySupport === true ? false : promptCacheKeySupport === false ? isResponsesApi : isResponsesApi && usesExplicitProxyLikeEndpoint;
3259
- const supportsResponsesStoreField = readCompatPayloadBoolean(model.compat, "supportsStore") !== false && isResponsesApi;
2880
+ //#endregion
2881
+ //#region packages/ai/src/transports/openai-responses-continuation.ts
2882
+ const HTTP_CONTINUATION_IDLE_TTL_MS = 300 * 1e3;
2883
+ const TURN_HEADERS = /* @__PURE__ */ new Set([
2884
+ "traceparent",
2885
+ "x-openclaw-turn-id",
2886
+ "x-openclaw-turn-attempt"
2887
+ ]);
2888
+ function jsonValuesEqual(left, right) {
2889
+ return stableStringify(JSON.parse(JSON.stringify(left))) === stableStringify(JSON.parse(JSON.stringify(right)));
2890
+ }
2891
+ function requestWithoutInput(request) {
2892
+ const { input: _input, previous_response_id: _previousResponseId, ...rest } = request;
2893
+ if (!isRecord(rest.metadata)) return rest;
2894
+ const metadata = Object.fromEntries(Object.entries(rest.metadata).filter(([key]) => key !== "openclaw_turn_id" && key !== "openclaw_turn_attempt"));
3260
2895
  return {
3261
- 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",
3262
- allowsResponsesStore: supportsResponsesStoreField && api !== "openai-chatgpt-responses" && api !== "openclaw-openai-chatgpt-responses-transport" && provider !== void 0 && OPENAI_RESPONSES_PROVIDERS.has(provider) && usesKnownNativeOpenAIEndpoint,
3263
- shouldStripResponsesPromptCache,
3264
- supportsResponsesStoreField,
3265
- usesKnownNativeOpenAIRoute
2896
+ ...rest,
2897
+ metadata
3266
2898
  };
3267
2899
  }
3268
- function parsePositiveInteger(value) {
3269
- if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.floor(value);
3270
- if (typeof value === "string") return parseStrictPositiveInteger(value);
3271
- }
3272
- function resolveOpenAIResponsesCompactThreshold(model) {
3273
- const contextWindow = parsePositiveInteger(model.contextWindow);
3274
- if (contextWindow) return Math.max(1e3, Math.floor(contextWindow * .7));
3275
- return 8e4;
3276
- }
3277
- function shouldEnableOpenAIResponsesServerCompaction(explicitStore, provider, extraParams) {
3278
- const configured = extraParams?.responsesServerCompaction;
3279
- if (configured === false) return false;
3280
- if (explicitStore !== true) return false;
3281
- if (configured === true) return true;
3282
- return provider === "openai";
3283
- }
3284
- function stripDisabledOpenAIReasoningPayload(payloadObj) {
3285
- const reasoning = payloadObj.reasoning;
3286
- if (reasoning === "none") {
3287
- delete payloadObj.reasoning;
3288
- return;
3289
- }
3290
- if (!reasoning || typeof reasoning !== "object" || Array.isArray(reasoning)) return;
3291
- if (reasoning.effort === "none") delete payloadObj.reasoning;
3292
- }
3293
- /** Strip returned-item metadata rejected by strict Responses-compatible endpoints. */
3294
- function stripInputItemStatuses(input) {
3295
- if (!Array.isArray(input)) return;
3296
- for (const item of input) if (item && typeof item === "object" && !Array.isArray(item)) delete item.status;
3297
- }
3298
- /** Resolve payload mutation policy for one OpenAI Responses-style model endpoint. */
3299
- function resolveOpenAIResponsesPayloadPolicy(model, options = {}) {
3300
- const capabilities = resolveOpenAIResponsesPayloadCapabilities(model);
3301
- const storeMode = options.storeMode ?? "provider-policy";
3302
- const explicitStore = storeMode === "preserve" ? void 0 : storeMode === "disable" ? capabilities.supportsResponsesStoreField ? false : void 0 : capabilities.allowsResponsesStore ? true : void 0;
3303
- const isResponsesApi = isOpenAIResponsesApi(normalizeLowercaseString(model.api));
3304
- const shouldStripDisabledReasoningPayload = isResponsesApi && (!capabilities.usesKnownNativeOpenAIRoute || !supportsOpenAIReasoningEffort(model, "none"));
3305
- const shouldStripInputStatus = isResponsesApi && !capabilities.usesKnownNativeOpenAIRoute;
2900
+ function normalizeAssistantReplayInput(input) {
2901
+ return input.map((item) => {
2902
+ if (!isRecord(item)) return item;
2903
+ if (item.type === "reasoning") return { type: "reasoning" };
2904
+ if (item.type !== "function_call" && !(item.type === "message" && item.role === "assistant")) return item;
2905
+ const { id: _id, status: _status, ...stableItem } = item;
2906
+ if (item.type === "message" && Array.isArray(stableItem.content)) stableItem.content = stableItem.content.map((part) => {
2907
+ if (!isRecord(part) || part.type !== "output_text") return part;
2908
+ const { annotations: _annotations, logprobs: _logprobs, ...stablePart } = part;
2909
+ return stablePart;
2910
+ });
2911
+ return stableItem;
2912
+ });
2913
+ }
2914
+ function resolveResponsesContinuationRequest(continuation, request) {
2915
+ if (!continuation) return {
2916
+ request,
2917
+ continuationStatus: "no_previous_response"
2918
+ };
2919
+ if (request.previous_response_id) return {
2920
+ request,
2921
+ continuationStatus: "explicit_previous_response_id"
2922
+ };
2923
+ if (!jsonValuesEqual(requestWithoutInput(request), requestWithoutInput(continuation.lastRequest))) return {
2924
+ request,
2925
+ continuationStatus: "request_changed"
2926
+ };
2927
+ const currentInput = request.input ?? [];
2928
+ const previousInput = continuation.lastRequest.input ?? [];
2929
+ const baselineLength = previousInput.length + continuation.lastResponseItems.length;
2930
+ if (currentInput.length < baselineLength) return {
2931
+ request,
2932
+ continuationStatus: "history_shorter"
2933
+ };
2934
+ if (!jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(0, previousInput.length)), normalizeAssistantReplayInput(previousInput)) || !jsonValuesEqual(normalizeAssistantReplayInput(currentInput.slice(previousInput.length, baselineLength)), normalizeAssistantReplayInput(continuation.lastResponseItems))) return {
2935
+ request,
2936
+ continuationStatus: "history_changed"
2937
+ };
3306
2938
  return {
3307
- allowsServiceTier: capabilities.allowsOpenAIServiceTier,
3308
- compactThreshold: parsePositiveInteger(options.extraParams?.responsesCompactThreshold) ?? resolveOpenAIResponsesCompactThreshold(model),
3309
- explicitStore,
3310
- shouldStripDisabledReasoningPayload,
3311
- shouldStripInputStatus,
3312
- shouldStripPromptCache: options.enablePromptCacheStripping === true && capabilities.shouldStripResponsesPromptCache,
3313
- shouldStripStore: explicitStore !== true && readCompatPayloadBoolean(model.compat, "supportsStore") === false && isResponsesApi,
3314
- useServerCompaction: options.enableServerCompaction === true && shouldEnableOpenAIResponsesServerCompaction(explicitStore, model.provider, options.extraParams)
2939
+ request: {
2940
+ ...request,
2941
+ previous_response_id: continuation.lastResponseId,
2942
+ input: currentInput.slice(baselineLength)
2943
+ },
2944
+ continuationStatus: "continued"
3315
2945
  };
3316
2946
  }
3317
- /** Mutate a Responses request payload according to the resolved endpoint policy. */
3318
- function applyOpenAIResponsesPayloadPolicy(payloadObj, policy) {
3319
- if (policy.explicitStore !== void 0) payloadObj.store = policy.explicitStore;
3320
- if (policy.shouldStripStore) delete payloadObj.store;
3321
- if (policy.shouldStripPromptCache) {
3322
- delete payloadObj.prompt_cache_key;
3323
- delete payloadObj.prompt_cache_retention;
3324
- }
3325
- if (policy.useServerCompaction && payloadObj.context_management === void 0) payloadObj.context_management = [{
3326
- type: "compaction",
3327
- compact_threshold: policy.compactThreshold
3328
- }];
3329
- if (policy.shouldStripDisabledReasoningPayload) stripDisabledOpenAIReasoningPayload(payloadObj);
3330
- if (policy.shouldStripInputStatus) stripInputItemStatuses(payloadObj.input);
2947
+ const httpContinuationEntries = /* @__PURE__ */ new Map();
2948
+ let nextHttpContinuationGeneration = 1;
2949
+ function connectionIdentity(params) {
2950
+ const headers = Object.entries(resolveAiTransportHeaderSentinels(params.headers) ?? {}).map(([name, value]) => [name.toLowerCase(), value]).filter(([name]) => !TURN_HEADERS.has(name)).toSorted(([a], [b]) => a.localeCompare(b));
2951
+ return sha256Hex(JSON.stringify([
2952
+ getAiTransportHost().resolveSecretSentinel(params.apiKey),
2953
+ params.baseUrl,
2954
+ headers
2955
+ ]));
2956
+ }
2957
+ function claimOpenAIResponsesHttpContinuation(params) {
2958
+ const key = `${params.sessionId}\0${connectionIdentity(params)}`;
2959
+ const previous = httpContinuationEntries.get(key);
2960
+ if (previous?.kind === "claimed") return;
2961
+ if (previous?.kind === "ready") clearTimeout(previous.idleTimer);
2962
+ const generation = nextHttpContinuationGeneration++;
2963
+ const claimed = {
2964
+ kind: "claimed",
2965
+ sessionId: params.sessionId,
2966
+ generation
2967
+ };
2968
+ httpContinuationEntries.set(key, claimed);
2969
+ return {
2970
+ request: resolveResponsesContinuationRequest(previous?.kind === "ready" ? previous.state : void 0, params.request).request,
2971
+ commit: (effectiveRequest, response) => {
2972
+ if (httpContinuationEntries.get(key) !== claimed) return;
2973
+ const idleTimer = setTimeout(() => {
2974
+ const current = httpContinuationEntries.get(key);
2975
+ if (current?.kind === "ready" && current.generation === generation) httpContinuationEntries.delete(key);
2976
+ }, HTTP_CONTINUATION_IDLE_TTL_MS);
2977
+ idleTimer.unref?.();
2978
+ const ready = {
2979
+ ...claimed,
2980
+ kind: "ready",
2981
+ state: {
2982
+ lastRequest: effectiveRequest,
2983
+ lastResponseId: response.id,
2984
+ lastResponseItems: response.output
2985
+ },
2986
+ idleTimer
2987
+ };
2988
+ httpContinuationEntries.set(key, ready);
2989
+ },
2990
+ release: () => {
2991
+ if (httpContinuationEntries.get(key) === claimed) httpContinuationEntries.delete(key);
2992
+ }
2993
+ };
3331
2994
  }
2995
+ registerSessionResourceCleanup((sessionId) => {
2996
+ for (const [key, entry] of httpContinuationEntries) if (!sessionId || entry.sessionId === sessionId) {
2997
+ if (entry.kind === "ready") clearTimeout(entry.idleTimer);
2998
+ httpContinuationEntries.delete(key);
2999
+ }
3000
+ });
3332
3001
  //#endregion
3333
3002
  //#region packages/ai/src/transports/openai-responses-params-internal.ts
3003
+ const OPENAI_RESPONSES_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set([
3004
+ "openai",
3005
+ "opencode",
3006
+ "azure-openai-responses",
3007
+ "github-copilot"
3008
+ ]);
3334
3009
  function convertResponsesTools(tools, model, options) {
3335
3010
  const projection = projectOpenAITools(tools);
3336
3011
  const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
@@ -3429,7 +3104,7 @@ function resolveOpenAIResponsesTextFormat(responseFormat) {
3429
3104
  };
3430
3105
  return responseFormat;
3431
3106
  }
3432
- function buildOpenAIResponsesParams(model, context, options, metadata) {
3107
+ function convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode) {
3433
3108
  const isCodexResponses = isOpenAICodexResponsesModel(model);
3434
3109
  const isNativeCodexResponses = usesNativeOpenAICodexResponsesBackend(model);
3435
3110
  const compat = getCompat(model);
@@ -3437,19 +3112,20 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
3437
3112
  const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
3438
3113
  const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
3439
3114
  const replayResponsesItemIds = !isNativeCodexResponses && (options?.replayResponsesItemIds ?? policyAllowsReplayIds);
3440
- const messages = convertResponsesMessages(model, context, /* @__PURE__ */ new Set([
3441
- "openai",
3442
- "opencode",
3443
- "azure-openai-responses",
3444
- "github-copilot"
3445
- ]), {
3115
+ return convertResponsesMessages(model, context, OPENAI_RESPONSES_TOOL_CALL_PROVIDERS, {
3446
3116
  includeSystemPrompt: !isCodexResponses,
3447
3117
  supportsDeveloperRole,
3448
3118
  replayReasoningItems: true,
3449
3119
  replayResponsesItemIds,
3450
3120
  authProfileId: options?.authProfileId,
3451
- sessionId: options?.sessionId
3121
+ sessionId: options?.sessionId,
3122
+ replayMode
3452
3123
  });
3124
+ }
3125
+ function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint") {
3126
+ const isCodexResponses = isOpenAICodexResponsesModel(model);
3127
+ const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "disable" });
3128
+ const messages = convertOpenAIResponsesMessagesForRequest(model, context, options, replayMode);
3453
3129
  if (isCodexResponses) ensureOpenAICodexResponsesInput(messages, context);
3454
3130
  const cacheRetention = resolveCacheRetention(options?.cacheRetention);
3455
3131
  const promptCacheKey = resolvePromptCacheKey(options, cacheRetention);
@@ -3509,6 +3185,262 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
3509
3185
  return sanitizeOpenAICodexResponsesParams(model, params);
3510
3186
  }
3511
3187
  //#endregion
3188
+ //#region packages/ai/src/transports/openai-responses-websocket.ts
3189
+ const SESSION_WEBSOCKET_CACHE_TTL_MS = 300 * 1e3;
3190
+ const SESSION_WEBSOCKET_MAX_AGE_MS = 3300 * 1e3;
3191
+ const WEBSOCKET_OPEN_STATE = 1;
3192
+ const websocketSessionCache = /* @__PURE__ */ new Map();
3193
+ const degradedWebSocketConnections = /* @__PURE__ */ new Map();
3194
+ function isOfficialOpenAIResponsesBaseUrl(baseUrl) {
3195
+ if (!baseUrl) return false;
3196
+ try {
3197
+ const url = new URL(baseUrl);
3198
+ return url.origin === "https://api.openai.com" && url.username === "" && url.password === "" && url.search === "" && url.hash === "" && url.pathname.replace(/\/+$/, "") === "/v1";
3199
+ } catch {
3200
+ return false;
3201
+ }
3202
+ }
3203
+ function supportsNativeOpenAIResponsesEndpoint(params) {
3204
+ return params.provider.trim().toLowerCase() === "openai" && params.api === "openai-responses" && isOfficialOpenAIResponsesBaseUrl(params.baseUrl);
3205
+ }
3206
+ function closeWebSocketSilently(socket, reason = "done") {
3207
+ try {
3208
+ socket.close({
3209
+ code: 1e3,
3210
+ reason
3211
+ });
3212
+ } catch {}
3213
+ }
3214
+ function invalidateOwnedWebSocketSession(cacheKey, entry, reason = "done") {
3215
+ if (entry.idleTimer) {
3216
+ clearTimeout(entry.idleTimer);
3217
+ entry.idleTimer = void 0;
3218
+ }
3219
+ closeWebSocketSilently(entry.socket, reason);
3220
+ if (websocketSessionCache.get(cacheKey) === entry) websocketSessionCache.delete(cacheKey);
3221
+ }
3222
+ function scheduleSessionWebSocketExpiry(cacheKey, entry) {
3223
+ if (entry.idleTimer) clearTimeout(entry.idleTimer);
3224
+ entry.idleTimer = setTimeout(() => {
3225
+ if (entry.busy) return;
3226
+ invalidateOwnedWebSocketSession(cacheKey, entry, "idle_timeout");
3227
+ }, SESSION_WEBSOCKET_CACHE_TTL_MS);
3228
+ entry.idleTimer.unref?.();
3229
+ }
3230
+ function prepareWebSocketConnection(client, headers) {
3231
+ if (!isOfficialOpenAIResponsesBaseUrl(client.baseURL)) throw new Error("OpenAI Responses WebSocket requires the official API endpoint");
3232
+ if (typeof client.apiKey !== "string" || client.apiKey.length === 0) throw new Error("OpenAI Responses WebSocket requires an API key");
3233
+ const resolvedApiKey = getAiTransportHost().resolveSecretSentinel(client.apiKey);
3234
+ const resolvedHeaders = { ...resolveAiTransportHeaderSentinels(headers) };
3235
+ for (const key of Object.keys(resolvedHeaders)) {
3236
+ const normalizedKey = key.toLowerCase();
3237
+ if (normalizedKey === "authorization" || normalizedKey === "traceparent") delete resolvedHeaders[key];
3238
+ }
3239
+ if (!resolvedApiKey) throw new Error("OpenAI Responses WebSocket requires a resolved API key");
3240
+ return {
3241
+ client: client.withOptions({ apiKey: resolvedApiKey }),
3242
+ headers: resolvedHeaders,
3243
+ identity: sha256Hex(JSON.stringify([
3244
+ resolvedApiKey,
3245
+ client.baseURL,
3246
+ Object.entries(resolvedHeaders).toSorted(([a], [b]) => a.localeCompare(b))
3247
+ ]))
3248
+ };
3249
+ }
3250
+ function createWebSocket(connection, onError) {
3251
+ const socket = new ResponsesWS(connection.client, {
3252
+ headers: connection.headers,
3253
+ maxQueueSize: 1
3254
+ });
3255
+ socket.on("error", () => onError(socket));
3256
+ return socket;
3257
+ }
3258
+ function createTransientWebSocketLease(connection) {
3259
+ const socket = createWebSocket(connection, (failedSocket) => closeWebSocketSilently(failedSocket, "transport_error"));
3260
+ return {
3261
+ socket,
3262
+ iterator: socket.stream(),
3263
+ reusedConnection: false,
3264
+ release: () => closeWebSocketSilently(socket)
3265
+ };
3266
+ }
3267
+ function createCachedWebSocketLease(cacheKey, entry, reusedConnection) {
3268
+ entry.busy = true;
3269
+ return {
3270
+ socket: entry.socket,
3271
+ iterator: entry.socket.stream(),
3272
+ entry,
3273
+ reusedConnection,
3274
+ release: ({ keep } = {}) => {
3275
+ if (!keep || entry.socket.socket.readyState !== WEBSOCKET_OPEN_STATE) {
3276
+ invalidateOwnedWebSocketSession(cacheKey, entry);
3277
+ return;
3278
+ }
3279
+ entry.busy = false;
3280
+ scheduleSessionWebSocketExpiry(cacheKey, entry);
3281
+ }
3282
+ };
3283
+ }
3284
+ function acquireWebSocket(params, connection) {
3285
+ if (!(params.mode !== "websocket" && Boolean(params.sessionId)) || !params.sessionId) return createTransientWebSocketLease(connection);
3286
+ const cacheKey = `${params.sessionId}\0${connection.identity}`;
3287
+ const cached = websocketSessionCache.get(cacheKey);
3288
+ if (cached) {
3289
+ if (cached.idleTimer) {
3290
+ clearTimeout(cached.idleTimer);
3291
+ cached.idleTimer = void 0;
3292
+ }
3293
+ if (cached.busy) return createTransientWebSocketLease(connection);
3294
+ const expired = Date.now() - cached.createdAt >= SESSION_WEBSOCKET_MAX_AGE_MS;
3295
+ if (!expired && cached.socket.socket.readyState === WEBSOCKET_OPEN_STATE) return createCachedWebSocketLease(cacheKey, cached, true);
3296
+ invalidateOwnedWebSocketSession(cacheKey, cached, expired ? "connection_age_limit" : "done");
3297
+ }
3298
+ const entry = {
3299
+ socket: createWebSocket(connection, (failedSocket) => {
3300
+ const failedEntry = websocketSessionCache.get(cacheKey);
3301
+ if (failedEntry?.socket === failedSocket) invalidateOwnedWebSocketSession(cacheKey, failedEntry, "transport_error");
3302
+ else closeWebSocketSilently(failedSocket, "transport_error");
3303
+ }),
3304
+ sessionId: params.sessionId,
3305
+ busy: true,
3306
+ createdAt: Date.now()
3307
+ };
3308
+ websocketSessionCache.set(cacheKey, entry);
3309
+ return createCachedWebSocketLease(cacheKey, entry, false);
3310
+ }
3311
+ function sanitizeWebSocketRequest(request) {
3312
+ const { stream: _stream, background: _background, ...websocketRequest } = request;
3313
+ return websocketRequest;
3314
+ }
3315
+ async function nextWebSocketMessage(iterator, signal) {
3316
+ if (!signal) return iterator.next();
3317
+ if (signal.aborted) throw transportAbortError(signal);
3318
+ let onAbort;
3319
+ try {
3320
+ return await Promise.race([iterator.next(), new Promise((_resolve, reject) => {
3321
+ onAbort = () => reject(transportAbortError(signal));
3322
+ signal.addEventListener("abort", onAbort, { once: true });
3323
+ })]);
3324
+ } finally {
3325
+ if (onAbort) signal.removeEventListener("abort", onAbort);
3326
+ }
3327
+ }
3328
+ function readServerEvent(message) {
3329
+ if (message.type === "message") return message.message;
3330
+ if (message.type === "error") throw parseOpenAIResponsesWebSocketServerError(message.error) ?? new Error("OpenAI Responses WebSocket transport failed", { cause: message.error });
3331
+ if (message.type === "close") throw new Error(`OpenAI Responses WebSocket closed before completion (code ${message.code})`);
3332
+ }
3333
+ function createOpenAIResponsesWebSocketStream(params) {
3334
+ const connection = prepareWebSocketConnection(params.client, params.headers);
3335
+ const fullRequest = sanitizeWebSocketRequest(params.request);
3336
+ const requestModel = typeof fullRequest.model === "string" ? fullRequest.model : "";
3337
+ const degradationKey = `${params.sessionId ?? ""}\0${connection.identity}\0${requestModel}`;
3338
+ const degraded = degradedWebSocketConnections.get(degradationKey);
3339
+ if (degraded && degraded.retryAt > Date.now()) throw new OpenAIResponsesWebSocketPreDispatchError(/* @__PURE__ */ new Error("OpenAI Responses WebSocket is cooling down after a transport failure"));
3340
+ degradedWebSocketConnections.delete(degradationKey);
3341
+ const markDegraded = () => {
3342
+ const cooldownMs = params.degradeCooldownMs;
3343
+ if (cooldownMs === void 0 || !Number.isFinite(cooldownMs) || cooldownMs <= 0) return;
3344
+ degradedWebSocketConnections.set(degradationKey, {
3345
+ sessionId: params.sessionId,
3346
+ retryAt: Date.now() + cooldownMs
3347
+ });
3348
+ };
3349
+ let lease;
3350
+ try {
3351
+ lease = acquireWebSocket(params, connection);
3352
+ } catch (error) {
3353
+ markDegraded();
3354
+ throw new OpenAIResponsesWebSocketPreDispatchError(error);
3355
+ }
3356
+ let prepared;
3357
+ try {
3358
+ const continuation = lease.entry?.continuation;
3359
+ if (continuation && lease.entry) {
3360
+ lease.entry.continuation = void 0;
3361
+ prepared = resolveResponsesContinuationRequest(continuation, fullRequest);
3362
+ } else prepared = {
3363
+ request: fullRequest,
3364
+ continuationStatus: lease.entry ? "no_previous_response" : "socket_not_cached"
3365
+ };
3366
+ } catch (error) {
3367
+ lease.iterator.return?.().catch(() => void 0);
3368
+ lease.release({ keep: false });
3369
+ throw error;
3370
+ }
3371
+ let streamStarted = false;
3372
+ let terminalResponse;
3373
+ let terminalReceived = false;
3374
+ let released = false;
3375
+ const finish = ({ keep = true } = {}) => {
3376
+ if (released) return;
3377
+ released = true;
3378
+ if (keep && lease.entry && terminalResponse) lease.entry.continuation = {
3379
+ lastRequest: fullRequest,
3380
+ lastResponseId: terminalResponse.id,
3381
+ lastResponseItems: terminalResponse.output
3382
+ };
3383
+ lease.release({ keep });
3384
+ };
3385
+ return {
3386
+ stream: { async *[Symbol.asyncIterator]() {
3387
+ if (streamStarted) throw new Error("OpenAI Responses WebSocket stream can only be consumed once");
3388
+ streamStarted = true;
3389
+ const iterator = lease.iterator;
3390
+ let requestDispatched = false;
3391
+ try {
3392
+ if (params.signal?.aborted) throw transportAbortError(params.signal);
3393
+ for (;;) {
3394
+ const next = await nextWebSocketMessage(iterator, params.signal);
3395
+ if (next.done) throw new Error("OpenAI Responses WebSocket closed before a terminal response event");
3396
+ if (next.value.type === "open") {
3397
+ if (!requestDispatched) {
3398
+ requestDispatched = true;
3399
+ lease.socket.send({
3400
+ ...prepared.request,
3401
+ type: "response.create"
3402
+ });
3403
+ }
3404
+ continue;
3405
+ }
3406
+ const event = readServerEvent(next.value);
3407
+ if (!event) continue;
3408
+ if (event.type === "response.failed") throw new OpenAIResponsesWebSocketResponseFailedError(event.response.output.length > 0);
3409
+ if (event.type === "response.completed") terminalResponse = event.response;
3410
+ terminalReceived = event.type === "response.completed" || event.type === "response.incomplete";
3411
+ yield event;
3412
+ if (terminalReceived) {
3413
+ degradedWebSocketConnections.delete(degradationKey);
3414
+ return;
3415
+ }
3416
+ }
3417
+ } catch (error) {
3418
+ if (lease.entry) lease.entry.continuation = void 0;
3419
+ const safeRetry = error instanceof OpenAIResponsesWebSocketSafeRetryError;
3420
+ if (!params.callerSignal?.aborted && !safeRetry) markDegraded();
3421
+ if (!requestDispatched && !params.signal?.aborted) throw new OpenAIResponsesWebSocketPreDispatchError(error);
3422
+ if (!requestDispatched || params.callerSignal?.aborted || error instanceof OpenAIResponsesWebSocketResponseFailedError || safeRetry) throw error;
3423
+ throw new OpenAIResponsesWebSocketPostDispatchError(error);
3424
+ } finally {
3425
+ await iterator.return?.().catch(() => void 0);
3426
+ if (!terminalReceived) finish({ keep: false });
3427
+ }
3428
+ } },
3429
+ request: prepared.request,
3430
+ reusedConnection: lease.reusedConnection,
3431
+ continuationStatus: prepared.continuationStatus,
3432
+ finish
3433
+ };
3434
+ }
3435
+ function closeOpenAIResponsesWebSocketSessions(sessionId) {
3436
+ for (const [cacheKey, entry] of websocketSessionCache) {
3437
+ if (sessionId && entry.sessionId !== sessionId) continue;
3438
+ invalidateOwnedWebSocketSession(cacheKey, entry, "session_cleanup");
3439
+ }
3440
+ for (const [key, entry] of degradedWebSocketConnections) if (!sessionId || entry.sessionId === sessionId) degradedWebSocketConnections.delete(key);
3441
+ }
3442
+ registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
3443
+ //#endregion
3512
3444
  //#region packages/media-core/src/inline-image-data-url.ts
3513
3445
  /** Prefix used to distinguish inline data URLs from remote/local image references. */
3514
3446
  const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
@@ -3653,6 +3585,20 @@ function sanitizeResponsesImagePayload(params) {
3653
3585
  }
3654
3586
  //#endregion
3655
3587
  //#region packages/ai/src/transports/openai-responses-client.ts
3588
+ function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
3589
+ if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
3590
+ if (getAiTransportHost().requiresManagedTransport(model)) return;
3591
+ return supportsNativeOpenAIResponsesEndpoint({
3592
+ provider: model.provider,
3593
+ api: model.api,
3594
+ baseUrl: model.baseUrl
3595
+ }) ? transport : void 0;
3596
+ }
3597
+ function combineWebSocketTimeoutSignal(signal, model, timeoutMs) {
3598
+ const resolvedTimeoutMs = timeoutMs !== void 0 && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : getAiTransportHost().resolveModelRequestTimeoutMs(model);
3599
+ if (resolvedTimeoutMs === void 0 || !Number.isFinite(resolvedTimeoutMs)) return signal;
3600
+ return AbortSignal.any([signal, AbortSignal.timeout(Math.max(1, resolvedTimeoutMs))]);
3601
+ }
3656
3602
  function resolveProviderTransportTurnState(model, params) {
3657
3603
  const normalizedProvider = model.provider.trim().toLowerCase();
3658
3604
  const allowRuntimePluginLoad = normalizedProvider === "openai" || normalizedProvider === "azure-openai" || normalizedProvider === "azure-openai-responses";
@@ -3681,88 +3627,227 @@ function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turn
3681
3627
  ...buildOpenAISdkClientOptions(model)
3682
3628
  });
3683
3629
  }
3630
+ async function postOpenAIResponsesCompaction(params) {
3631
+ const response = await params.client.post("/responses/compact", {
3632
+ ...buildOpenAISdkRequestOptions(params.model, params.options?.signal, {
3633
+ timeoutMs: params.options?.timeoutMs,
3634
+ maxRetries: params.options?.maxRetries
3635
+ }),
3636
+ body: {
3637
+ model: params.request.model,
3638
+ input: params.request.input
3639
+ }
3640
+ });
3641
+ const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
3642
+ const item = output[0];
3643
+ const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
3644
+ if (!isRecord(response) || response.object !== "response.compaction" || output.length !== 1 || !isRecord(item) || item.type !== "compaction" || typeof item.encrypted_content !== "string" || item.encrypted_content.length === 0 || !usage || typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") throw new Error("Responses compact endpoint did not return exactly one compaction item");
3645
+ return {
3646
+ item,
3647
+ usage,
3648
+ model: params.model,
3649
+ replayMetadata: buildOpenAIResponsesReasoningReplayMetadata(params.model, {
3650
+ authProfileId: params.options?.authProfileId,
3651
+ sessionId: params.options?.sessionId
3652
+ })
3653
+ };
3654
+ }
3684
3655
  function createResponsesTransportExecutor(config) {
3685
3656
  return (model, context, options) => {
3686
3657
  const responsesOptions = options;
3658
+ const compactRequest = claimResponsesCompactRequest(responsesOptions);
3687
3659
  const eventStream = createAssistantMessageEventStream();
3688
3660
  const stream = eventStream;
3689
3661
  (async () => {
3690
- const output = {
3691
- role: "assistant",
3692
- content: [],
3693
- api: config.outputApi ?? model.api,
3694
- provider: model.provider,
3695
- model: model.id,
3696
- usage: {
3697
- input: 0,
3698
- output: 0,
3699
- cacheRead: 0,
3700
- cacheWrite: 0,
3701
- totalTokens: 0,
3702
- cost: {
3703
- input: 0,
3704
- output: 0,
3705
- cacheRead: 0,
3706
- cacheWrite: 0,
3707
- total: 0
3708
- }
3709
- },
3710
- stopReason: "stop",
3711
- timestamp: Date.now()
3712
- };
3662
+ const output = createOpenAIResponsesAssistantOutput(model, config.outputApi);
3713
3663
  let firstEventAbort;
3664
+ let continuationClaim;
3714
3665
  try {
3715
3666
  const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
3667
+ const websocketMode = resolveNativeOpenAIResponsesWebSocketMode(model, responsesOptions?.transport);
3716
3668
  const turnState = resolveProviderTransportTurnState(model, {
3717
3669
  sessionId: options?.sessionId,
3718
3670
  turnId: randomUUID(),
3719
3671
  attempt: 1,
3720
- transport: "stream"
3672
+ transport: websocketMode ? "websocket" : "stream"
3721
3673
  });
3674
+ const websocketSessionPolicy = websocketMode ? turnState?.websocket : void 0;
3675
+ const websocketHeaders = websocketMode ? buildOpenAIClientHeaders(model, context, options?.headers, websocketSessionPolicy?.headers, options?.sessionId) : void 0;
3722
3676
  const client = config.createClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId);
3723
- let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata);
3724
- const nextParams = await options?.onPayload?.(params, model);
3725
- if (nextParams !== void 0) params = nextParams;
3726
- if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
3727
- params = sanitizeOpenAICodexResponsesParams(model, params);
3728
- params = sanitizeResponsesImagePayload(params);
3729
- if (options?.openclawCodeModeToolSurface === true) {
3730
- const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
3731
- enforceCodeModeResponsesToolSurface(params, visibleToolNames);
3732
- assertCodeModeResponsesToolSurface(params, visibleToolNames);
3677
+ const buildRequest = async (replayMode) => {
3678
+ let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata, replayMode);
3679
+ const nextParams = await options?.onPayload?.(params, model);
3680
+ if (nextParams !== void 0) params = nextParams;
3681
+ if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
3682
+ params = sanitizeOpenAICodexResponsesParams(model, params);
3683
+ params = sanitizeResponsesImagePayload(params);
3684
+ if (options?.openclawCodeModeToolSurface === true) {
3685
+ const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
3686
+ const allowedHostedToolTypes = responsesOptions?.openclawCodeModeAllowedHostedToolTypes;
3687
+ enforceCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
3688
+ assertCodeModeResponsesToolSurface(params, visibleToolNames, allowedHostedToolTypes);
3689
+ }
3690
+ return params;
3691
+ };
3692
+ const params = await buildRequest("checkpoint");
3693
+ if (compactRequest) {
3694
+ const compacted = await postOpenAIResponsesCompaction({
3695
+ client,
3696
+ model,
3697
+ request: params,
3698
+ options: responsesOptions
3699
+ });
3700
+ output.usage.input = compacted.usage.input_tokens;
3701
+ output.usage.output = compacted.usage.output_tokens;
3702
+ output.usage.totalTokens = compacted.usage.input_tokens + compacted.usage.output_tokens;
3703
+ compactRequest.resolve(compacted);
3704
+ stream.push({
3705
+ type: "done",
3706
+ reason: output.stopReason,
3707
+ message: output
3708
+ });
3709
+ stream.end();
3710
+ return;
3733
3711
  }
3712
+ const sessionId = options?.sessionId;
3713
+ if (config.httpContinuation && !websocketMode && !getAiTransportHost().requiresManagedTransport(model) && supportsNativeOpenAIResponsesEndpoint({
3714
+ provider: model.provider,
3715
+ api: model.api,
3716
+ baseUrl: model.baseUrl
3717
+ }) && sessionId && params.store === true && !params.previous_response_id) continuationClaim = claimOpenAIResponsesHttpContinuation({
3718
+ sessionId,
3719
+ apiKey,
3720
+ baseUrl: model.baseUrl,
3721
+ headers: buildOpenAIClientHeaders(model, context, options?.headers, turnState?.headers, sessionId),
3722
+ request: params
3723
+ });
3724
+ const observePrompt = createResponsesPromptEgressObserver(responsesOptions, context.systemPrompt);
3734
3725
  const requestStartedAt = Date.now();
3735
- firstEventAbort = createFirstStreamEventAbortController(options?.signal);
3736
- const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
3726
+ let started = false;
3727
+ const startStream = () => {
3728
+ if (!started) {
3729
+ started = true;
3730
+ stream.push({
3731
+ type: "start",
3732
+ partial: output
3733
+ });
3734
+ }
3735
+ };
3736
+ const firstEvent = createFirstStreamEventAbortController(options?.signal);
3737
+ firstEventAbort = firstEvent;
3738
+ const requestOptions = buildOpenAISdkRequestOptions(model, firstEvent.signal, {
3737
3739
  stream: config.streamRequest,
3738
3740
  timeoutMs: options?.timeoutMs,
3739
3741
  maxRetries: options?.maxRetries
3740
3742
  });
3741
- 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)}`);
3742
- const responseStream = await config.createResponseStream({
3743
- client,
3744
- request: params,
3745
- requestOptions,
3746
- model
3747
- });
3748
- emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - requestStartedAt}`);
3749
- stream.push({
3750
- type: "start",
3751
- partial: output
3752
- });
3753
- await processResponsesStream(observeResponsesStream(responseStream, model), output, stream, model, {
3754
- ...config.pricingOptions?.(responsesOptions),
3755
- firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
3756
- abortFirstEventStream: firstEventAbort.abort,
3757
- onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
3758
- signal: options?.signal,
3759
- reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
3760
- authProfileId: responsesOptions?.authProfileId,
3761
- sessionId: options?.sessionId
3762
- })
3763
- });
3764
- if (options?.signal?.aborted) throw transportAbortError(options.signal);
3765
- if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
3743
+ const websocketSignal = combineWebSocketTimeoutSignal(firstEvent.signal, model, requestOptions?.timeout);
3744
+ emitModelTransportDebug(log, `[responses] start provider=${model.provider} api=${model.api} model=${model.id} requestIdHash=${redactIdentifier(options?.requestId, { len: 64 })} baseUrl=${formatModelTransportDebugBaseUrl(model.baseUrl)} timeoutMs=${safeDebugValue(requestOptions?.timeout)} apiKey=${apiKey ? "present" : "missing"} ${summarizeResponsesPayload(params)}`);
3745
+ let continuationBaseline;
3746
+ const createSseStream = async (initialRequest = continuationClaim?.request ?? params, initialAttemptKind = "initial", initialRejectedCompaction) => {
3747
+ const { stream: rawResponseStream, response, attempt } = await config.createResponseStream({
3748
+ client,
3749
+ request: initialRequest,
3750
+ requestOptions,
3751
+ model,
3752
+ observePrompt,
3753
+ initialAttemptKind,
3754
+ initialRejectedCompaction,
3755
+ buildFullHistoryRequest: () => buildRequest("full-history"),
3756
+ onCompactionRejected: (checkpoint) => suppressOpenAIResponsesCompaction(output, model, responsesOptions, checkpoint)
3757
+ });
3758
+ if (continuationClaim) continuationBaseline = attempt.request.previous_response_id ? params : attempt.request;
3759
+ return withProviderResponseHook({
3760
+ stream: observeResponsesStream(rawResponseStream, model, requestStartedAt),
3761
+ signal: firstEvent.signal,
3762
+ abort: firstEvent.abort,
3763
+ hook: createOpenAIResponseHook(options?.onResponse, response, model),
3764
+ onReady: () => {
3765
+ emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} transport=sse elapsedMs=${Date.now() - requestStartedAt}`);
3766
+ startStream();
3767
+ }
3768
+ });
3769
+ };
3770
+ let responseStream;
3771
+ let finishWebSocket;
3772
+ let transport = "sse";
3773
+ const logWebSocketFallback = (reason) => emitModelTransportDebug(log, `[responses] websocket_fallback provider=${model.provider} api=${model.api} model=${model.id} reason=${reason}`);
3774
+ const closeWebSocketForFallback = (reason) => {
3775
+ finishWebSocket?.({ keep: false });
3776
+ finishWebSocket = void 0;
3777
+ transport = "sse";
3778
+ logWebSocketFallback(reason);
3779
+ };
3780
+ if (websocketMode) try {
3781
+ const websocket = createOpenAIResponsesWebSocketStream({
3782
+ client,
3783
+ request: params,
3784
+ mode: websocketMode,
3785
+ sessionId: options?.sessionId,
3786
+ headers: websocketHeaders,
3787
+ signal: websocketSignal,
3788
+ callerSignal: options?.signal,
3789
+ degradeCooldownMs: websocketSessionPolicy?.degradeCooldownMs
3790
+ });
3791
+ finishWebSocket = websocket.finish;
3792
+ observePrompt?.(websocket.request, {
3793
+ egress: "responses-websocket",
3794
+ payloadVariant: "initial"
3795
+ });
3796
+ transport = "websocket";
3797
+ emitModelTransportDebug(log, `[responses] websocket_selected provider=${model.provider} api=${model.api} model=${model.id} mode=${websocketMode} reused=${websocket.reusedConnection} continuation=${websocket.continuationStatus === "continued"} continuationStatus=${websocket.continuationStatus} sessionIdHash=${redactIdentifier(options?.sessionId)} headersHash=${redactIdentifier(JSON.stringify(Object.entries(websocketHeaders ?? {}).toSorted(([a], [b]) => a.localeCompare(b))))}`);
3798
+ responseStream = { async *[Symbol.asyncIterator]() {
3799
+ try {
3800
+ for await (const event of websocket.stream) {
3801
+ startStream();
3802
+ yield event;
3803
+ }
3804
+ } catch (error) {
3805
+ if (error instanceof OpenAIResponsesWebSocketSafeRetryError) {
3806
+ const encryptedContentRejected = isInvalidEncryptedContentError(error);
3807
+ const recovery = encryptedContentRejected ? await resolveNextResponsesEncryptedContentAttempt({
3808
+ kind: "initial",
3809
+ request: {
3810
+ ...websocket.request,
3811
+ stream: true
3812
+ }
3813
+ }, error, { buildFullHistoryRequest: () => buildRequest("full-history") }) : void 0;
3814
+ if (encryptedContentRejected && !recovery) throw error;
3815
+ closeWebSocketForFallback(`safe_server_error code=${error.code} status=${safeDebugValue(error.status)} param=${safeDebugValue(error.param)}`);
3816
+ yield* await createSseStream(recovery?.request ?? await buildRequest("full-history"), recovery?.kind ?? "continuation-rejected", recovery?.rejectedCompaction);
3817
+ return;
3818
+ }
3819
+ if (websocketSignal.aborted || !(error instanceof OpenAIResponsesWebSocketPreDispatchError)) throw error;
3820
+ transport = "sse";
3821
+ logWebSocketFallback("before_first_event");
3822
+ yield* await createSseStream();
3823
+ }
3824
+ } };
3825
+ } catch {
3826
+ closeWebSocketForFallback("setup_failure");
3827
+ responseStream = await createSseStream();
3828
+ }
3829
+ else responseStream = await createSseStream();
3830
+ try {
3831
+ const terminal = await processResponsesStream(responseStream, output, stream, model, {
3832
+ ...config.pricingOptions?.(responsesOptions, model),
3833
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
3834
+ abortFirstEventStream: firstEvent.abort,
3835
+ onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
3836
+ signal: options?.signal,
3837
+ reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
3838
+ authProfileId: responsesOptions?.authProfileId,
3839
+ sessionId: options?.sessionId
3840
+ })
3841
+ });
3842
+ finishWebSocket?.();
3843
+ if (options?.signal?.aborted) throw transportAbortError(options.signal);
3844
+ if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
3845
+ if (continuationClaim && continuationBaseline && terminal) continuationClaim.commit(continuationBaseline, terminal);
3846
+ } catch (error) {
3847
+ finishWebSocket?.({ keep: false });
3848
+ throw error;
3849
+ }
3850
+ emitModelTransportDebug(log, `[responses] completed provider=${model.provider} api=${model.api} model=${model.id} transport=${transport} elapsedMs=${Date.now() - requestStartedAt}`);
3766
3851
  stream.push({
3767
3852
  type: "done",
3768
3853
  reason: output.stopReason,
@@ -3770,9 +3855,20 @@ function createResponsesTransportExecutor(config) {
3770
3855
  });
3771
3856
  stream.end();
3772
3857
  } catch (error) {
3858
+ if (compactRequest) {
3859
+ compactRequest.reject(error);
3860
+ Object.assign(output, projectProviderError(error, options?.signal));
3861
+ stream.push({
3862
+ type: "error",
3863
+ reason: output.stopReason,
3864
+ error: output
3865
+ });
3866
+ stream.end();
3867
+ return;
3868
+ }
3773
3869
  if (error instanceof ResponsesStreamFailure && error.observation) logResponsesFailedNoDetails(error.observation);
3774
3870
  log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
3775
- assignTransportErrorDetails(output, error, options?.signal);
3871
+ Object.assign(output, projectProviderError(error, options?.signal));
3776
3872
  stream.push({
3777
3873
  type: "error",
3778
3874
  reason: output.stopReason,
@@ -3780,6 +3876,7 @@ function createResponsesTransportExecutor(config) {
3780
3876
  });
3781
3877
  stream.end();
3782
3878
  } finally {
3879
+ continuationClaim?.release();
3783
3880
  firstEventAbort?.dispose();
3784
3881
  }
3785
3882
  })();
@@ -3789,12 +3886,13 @@ function createResponsesTransportExecutor(config) {
3789
3886
  function createOpenAIResponsesTransportStreamFn() {
3790
3887
  return createResponsesTransportExecutor({
3791
3888
  streamRequest: true,
3889
+ httpContinuation: true,
3792
3890
  createClient: createOpenAIResponsesClient,
3793
3891
  buildRequest: buildOpenAIResponsesParams,
3794
3892
  createResponseStream: createResponsesStreamWithEncryptedContentRetry,
3795
- pricingOptions: (options) => ({
3893
+ pricingOptions: (options, model) => ({
3796
3894
  serviceTier: options?.serviceTier,
3797
- applyServiceTierPricing
3895
+ applyServiceTierPricing: (usage, serviceTier) => applyResponsesServiceTierPricing(usage, serviceTier, model)
3798
3896
  })
3799
3897
  });
3800
3898
  }
@@ -3803,8 +3901,8 @@ function createAzureOpenAIResponsesTransportStreamFn() {
3803
3901
  outputApi: "azure-openai-responses",
3804
3902
  firstEventTimeoutMs: AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS,
3805
3903
  createClient: createAzureOpenAIClient,
3806
- buildRequest: (model, context, options, metadata) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata),
3807
- createResponseStream: async ({ client, request, requestOptions }) => await client.responses.create(request, requestOptions)
3904
+ buildRequest: (model, context, options, metadata, replayMode) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata, replayMode),
3905
+ createResponseStream: createResponsesStreamWithEncryptedContentRetry
3808
3906
  });
3809
3907
  }
3810
3908
  function normalizeAzureBaseUrl(baseUrl) {
@@ -3832,8 +3930,8 @@ function createAzureOpenAIClient(model, context, apiKey, optionHeaders, turnHead
3832
3930
  apiVersion: resolveAzureOpenAIApiVersion()
3833
3931
  });
3834
3932
  }
3835
- function buildAzureOpenAIResponsesParams(model, context, options, deploymentName, metadata) {
3836
- const params = buildOpenAIResponsesParams(model, context, options, metadata);
3933
+ function buildAzureOpenAIResponsesParams(model, context, options, deploymentName, metadata, replayMode = "checkpoint") {
3934
+ const params = buildOpenAIResponsesParams(model, context, options, metadata, replayMode);
3837
3935
  params.model = deploymentName;
3838
3936
  delete params.store;
3839
3937
  return params;
@@ -3858,11 +3956,8 @@ const responsesTesting = {
3858
3956
  buildOpenAIResponsesReasoningReplayMetadata,
3859
3957
  isInvalidEncryptedContentError,
3860
3958
  normalizeResponsesFailedEvent,
3861
- prepareOpenAIResponsesReasoningItemForReplay,
3862
3959
  createResponsesStreamWithEncryptedContentRetry,
3863
3960
  resolveAzureOpenAIApiVersion,
3864
- stripResponsesRequestEncryptedContent,
3865
- tagOpenAIResponsesReasoningReplayItem,
3866
3961
  summarizeResponsesFailedNoDetailsObservation,
3867
3962
  summarizeResponsesPayload,
3868
3963
  summarizeResponsesTools,
@@ -3871,6 +3966,50 @@ const responsesTesting = {
3871
3966
  };
3872
3967
  if (process.env.VITEST || false) globalThis.openclawOpenAIResponsesTransportTestApi = responsesTesting;
3873
3968
  //#endregion
3969
+ //#region packages/ai/src/transports/provider-compaction-replay.ts
3970
+ /** Whether provider replay state is a prefix-bound server compaction checkpoint. */
3971
+ function isCompactionReplayCheckpoint(replay) {
3972
+ const type = replay && typeof replay === "object" ? replay.type : void 0;
3973
+ return type === "anthropic-compaction" || type === "openai-responses-compaction";
3974
+ }
3975
+ /** Strip prefix-bound checkpoints after local history rewrites. */
3976
+ function stripCompactionReplayCheckpoint(message) {
3977
+ if (!isCompactionReplayCheckpoint(message.providerReplay)) return message;
3978
+ const replaySafeMessage = { ...message };
3979
+ delete replaySafeMessage.providerReplay;
3980
+ return replaySafeMessage;
3981
+ }
3982
+ /** Strip prefix-bound checkpoint state from an in-place message rewrite. */
3983
+ function stripCompactionReplayCheckpointInPlace(message) {
3984
+ if (isCompactionReplayCheckpoint(message.providerReplay)) delete message.providerReplay;
3985
+ }
3986
+ /** Reindex a prefix-bound checkpoint after known content removals. */
3987
+ function replaceCompactionReplayOwnerContent(message, content) {
3988
+ const next = {
3989
+ ...message,
3990
+ content
3991
+ };
3992
+ const replay = message.providerReplay;
3993
+ if (!isCompactionReplayCheckpoint(replay)) return next;
3994
+ const replayIndex = replay.replayIndex ?? 0;
3995
+ if (content.length === 0 || replayIndex > message.content.length) return stripCompactionReplayCheckpoint(next);
3996
+ let sourceIndex = 0;
3997
+ let nextReplayIndex = 0;
3998
+ if (!content.every((block) => {
3999
+ const index = message.content.indexOf(block, sourceIndex);
4000
+ sourceIndex = index + 1;
4001
+ nextReplayIndex += index >= 0 && index < replayIndex ? 1 : 0;
4002
+ return index >= 0;
4003
+ })) return stripCompactionReplayCheckpoint(next);
4004
+ return nextReplayIndex === replayIndex ? next : {
4005
+ ...next,
4006
+ providerReplay: {
4007
+ ...replay,
4008
+ replayIndex: nextReplayIndex
4009
+ }
4010
+ };
4011
+ }
4012
+ //#endregion
3874
4013
  //#region packages/ai/src/transports/provider-transport-stream.ts
3875
4014
  const SUPPORTED_TRANSPORT_APIS = /* @__PURE__ */ new Set([
3876
4015
  "openai-responses",
@@ -3881,10 +4020,7 @@ const SUPPORTED_TRANSPORT_APIS = /* @__PURE__ */ new Set([
3881
4020
  "google-generative-ai"
3882
4021
  ]);
3883
4022
  const SIMPLE_TRANSPORT_API_ALIAS = {
3884
- "openai-responses": "openclaw-openai-responses-transport",
3885
- "openai-chatgpt-responses": "openclaw-openai-chatgpt-responses-transport",
3886
4023
  "openai-completions": "openclaw-openai-completions-transport",
3887
- "azure-openai-responses": "openclaw-azure-openai-responses-transport",
3888
4024
  "anthropic-messages": "openclaw-anthropic-messages-transport",
3889
4025
  "google-generative-ai": "openclaw-google-generative-ai-transport"
3890
4026
  };
@@ -3937,6 +4073,10 @@ function isTransportAwareApiSupported(api) {
3937
4073
  }
3938
4074
  /** Maps public model APIs to the internal transport API id used by simple runtime dispatch. */
3939
4075
  function resolveTransportAwareSimpleApi(api) {
4076
+ if (OPENAI_RESPONSES_APIS.has(api)) {
4077
+ const alias = `openclaw-${api}-transport`;
4078
+ return OPENAI_RESPONSES_APIS.has(alias) ? alias : void 0;
4079
+ }
3940
4080
  return SIMPLE_TRANSPORT_API_ALIAS[api];
3941
4081
  }
3942
4082
  /** Creates a managed transport stream only when request overrides require it. */
@@ -3971,6 +4111,7 @@ function buildTransportAwareSimpleStreamFn(model, ctx) {
3971
4111
  //#endregion
3972
4112
  //#region packages/ai/src/transports/simple-completion-transport.ts
3973
4113
  const PROVIDER_SIMPLE_COMPLETION_API_PREFIX = "openclaw-provider-simple:";
4114
+ const PROVIDER_STREAM_API_PREFIX = "openclaw-provider-stream:";
3974
4115
  const INVALID_CODEX_BASE_URL_MESSAGE = "OpenAI Codex Responses baseUrl must not include query parameters or fragments";
3975
4116
  function registerCustomApi(registry, api, streamFn) {
3976
4117
  getAiTransportHost().registerCustomApi(registry, api, streamFn);
@@ -4023,12 +4164,21 @@ function resolveProviderSimpleCompletionApi(model) {
4023
4164
  ];
4024
4165
  return `${PROVIDER_SIMPLE_COMPLETION_API_PREFIX}${parts.map((part) => encodeURIComponent(part)).join(":")}`;
4025
4166
  }
4026
- function applyProviderSimpleCompletionWrapper(registry, model, cfg) {
4167
+ function resolveProviderStreamApi(model) {
4168
+ const parts = [
4169
+ model.provider,
4170
+ model.id,
4171
+ model.api,
4172
+ model.baseUrl || "default"
4173
+ ];
4174
+ return `${PROVIDER_STREAM_API_PREFIX}${parts.map((part) => encodeURIComponent(part)).join(":")}`;
4175
+ }
4176
+ function applyProviderSimpleCompletionWrapper(registry, model, cfg, hookSourceApi = model.api) {
4027
4177
  if (model.api.startsWith(PROVIDER_SIMPLE_COMPLETION_API_PREFIX)) return model;
4028
4178
  const sourceProvider = registry.getApiProvider(model.api);
4029
4179
  if (!sourceProvider) return model;
4030
- const sourceApi = model.api;
4031
- const sourceStreamFn = (runtimeModel, context, options) => sourceProvider.streamSimple(projectModel(runtimeModel, { api: sourceApi }), context, options);
4180
+ const dispatchApi = model.api;
4181
+ const sourceStreamFn = (runtimeModel, context, options) => sourceProvider.streamSimple(projectModel(runtimeModel, { api: dispatchApi }), context, options);
4032
4182
  const streamFn = getAiTransportHost().plugin.wrapSimpleCompletionStream({
4033
4183
  provider: model.provider,
4034
4184
  config: cfg,
@@ -4037,6 +4187,7 @@ function applyProviderSimpleCompletionWrapper(registry, model, cfg) {
4037
4187
  provider: model.provider,
4038
4188
  modelId: model.id,
4039
4189
  model,
4190
+ sourceApi: hookSourceApi,
4040
4191
  streamFn: sourceStreamFn
4041
4192
  }
4042
4193
  });
@@ -4069,7 +4220,7 @@ function wrapPluginProviderStream(streamFn) {
4069
4220
  });
4070
4221
  };
4071
4222
  }
4072
- function registerProviderStreamForModel(params) {
4223
+ function prepareProviderStreamModel(params) {
4073
4224
  const pluginModel = resolveModelHeaderSentinels(params.model);
4074
4225
  const providerStreamFn = getAiTransportHost().plugin.resolveProviderStream({
4075
4226
  provider: params.model.provider,
@@ -4083,28 +4234,31 @@ function registerProviderStreamForModel(params) {
4083
4234
  });
4084
4235
  const transportFallback = providerStreamFn ? void 0 : createTransportAwareStreamFnForModel(params.model.api === "google-generative-ai" ? pluginModel : params.model, { cfg: params.cfg });
4085
4236
  const streamFn = providerStreamFn ? wrapPluginProviderStream(providerStreamFn) : transportFallback && params.model.api === "google-generative-ai" ? wrapPluginProviderStream(transportFallback) : transportFallback;
4086
- return streamFn && registerCustomApi(params.apiRegistry, params.model.api, streamFn) ? streamFn : void 0;
4237
+ if (!streamFn) return;
4238
+ const api = params.apiRegistry.getApiProvider(params.model.api) ? resolveProviderStreamApi(params.model) : params.model.api;
4239
+ if (!registerCustomApi(params.apiRegistry, api, streamFn)) return;
4240
+ return api === params.model.api ? params.model : projectModel(params.model, { api });
4087
4241
  }
4088
4242
  function prepareModelForSimpleCompletion(params) {
4089
4243
  const { apiRegistry, model, cfg } = params;
4090
- if (!apiRegistry.getApiProvider(model.api) && registerProviderStreamForModel({
4244
+ const providerStreamModel = prepareProviderStreamModel({
4091
4245
  model,
4092
4246
  cfg,
4093
4247
  apiRegistry
4094
- })) return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
4248
+ });
4249
+ if (providerStreamModel) return applyProviderSimpleCompletionWrapper(apiRegistry, providerStreamModel, cfg, model.api);
4095
4250
  const codexTransportModel = prepareCodexSimpleTransportModel(apiRegistry, model, cfg);
4096
- if (codexTransportModel) return applyProviderSimpleCompletionWrapper(apiRegistry, codexTransportModel, cfg);
4251
+ if (codexTransportModel) return applyProviderSimpleCompletionWrapper(apiRegistry, codexTransportModel, cfg, model.api);
4097
4252
  const transportAwareModel = prepareTransportAwareSimpleModel(model, { cfg });
4098
4253
  if (transportAwareModel !== model) {
4099
4254
  const streamFn = buildTransportAwareSimpleStreamFn(model, { cfg });
4100
- if (streamFn && registerCustomApi(apiRegistry, transportAwareModel.api, streamFn)) return applyProviderSimpleCompletionWrapper(apiRegistry, transportAwareModel, cfg);
4255
+ if (streamFn && registerCustomApi(apiRegistry, transportAwareModel.api, streamFn)) return applyProviderSimpleCompletionWrapper(apiRegistry, transportAwareModel, cfg, model.api);
4101
4256
  }
4102
- if (model.api === "google-generative-ai") return applyProviderSimpleCompletionWrapper(apiRegistry, getAiTransportHost().prepareGoogleSimpleCompletionModel(apiRegistry, model), cfg);
4103
4257
  if (model.provider === "anthropic-vertex") {
4104
4258
  const api = resolveAnthropicVertexSimpleApi(model.baseUrl);
4105
- if (registerCustomApi(apiRegistry, api, getAiTransportHost().plugin.createAnthropicVertexStream(model))) return applyProviderSimpleCompletionWrapper(apiRegistry, projectModel(model, { api }), cfg);
4259
+ if (registerCustomApi(apiRegistry, api, getAiTransportHost().plugin.createAnthropicVertexStream(model))) return applyProviderSimpleCompletionWrapper(apiRegistry, projectModel(model, { api }), cfg, model.api);
4106
4260
  }
4107
4261
  return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
4108
4262
  }
4109
4263
  //#endregion
4110
- export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, 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, log, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveCodeModeResponsesVisibleToolNames, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend };
4264
+ export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, applyAnthropicCacheControlToMessages, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, captureOpenAIResponsesCompaction, coerceTransportToolCallArguments, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIResponseHook, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, googleFlashSupportsMinimalThinking, hasOpenAICompatibleConversationTurn, isCodeModeModelVisibleToolName, isCompactionReplayCheckpoint, isOpenAICodexResponsesModel, isOpenAICompletionsThinkingEnabled, log, measureUtf8AppendBytes, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, parseOpenAICompletionsUsage, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, readOpenAICompletionsContentDeltas, replaceCompactionReplayOwnerContent, requestPreparedOpenAIResponsesCompaction, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveAnthropicServerCompactionPlan, resolveCodeModeResponsesVisibleToolNames, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesCompactEndpointPlan, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIResponsesServerCompactionPlan, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompactionReplayCheckpoint, stripCompactionReplayCheckpointInPlace, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend, withProviderResponseHook };