@openclaw/ai 2026.10.5-beta.3 → 2026.10.10-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +11 -0
  2. package/dist/{anthropic-BalhAzs-.mjs → anthropic-D6spfBri.mjs} +16 -18
  3. package/dist/{anthropic-payload-policy-DGblkHL5.d.mts → anthropic-payload-policy-Cr8xjazC.d.mts} +6 -3
  4. package/dist/{anthropic-stream-reducer-DkKeXXx4.mjs → anthropic-stream-reducer-CzPXOaLM.mjs} +59 -226
  5. package/dist/anthropic-tool-projection-BdAS3rtJ.mjs +165 -0
  6. package/dist/anthropic-tool-projection-DKH-aEWU.d.mts +51 -0
  7. package/dist/{api-registry-BhXXYdRA.d.mts → api-registry-gDt0faai.d.mts} +2 -2
  8. package/dist/{assistant-output-BuhRpLeD.mjs → assistant-output-6iFA1nbC.mjs} +2 -2
  9. package/dist/{azure-openai-responses-DfT9Ndd8.mjs → azure-openai-responses-0TqHwnlD.mjs} +8 -8
  10. package/dist/{cloudflare-5qSGvIUH.mjs → cloudflare-Dttf8jKq.mjs} +1 -1
  11. package/dist/{credential-redaction-BHxruEH6.d.mts → credential-redaction-XvsPf7Yi.d.mts} +1 -0
  12. package/dist/{diagnostics-tQasYct1.mjs → diagnostics-Cv7cW3pS.mjs} +3 -3
  13. package/dist/diagnostics.d.mts +6 -6
  14. package/dist/diagnostics.mjs +2 -2
  15. package/dist/{env-api-keys-CrxwdylR.mjs → env-api-keys-Xd8dm_aV.mjs} +10 -34
  16. package/dist/event-stream-CDpFza-M.d.mts +1 -0
  17. package/dist/{event-stream-BcqWLo_u.d.mts → event-stream-CzpE1g_o.d.mts} +1 -1
  18. package/dist/event-stream.d.mts +2 -2
  19. package/dist/{github-copilot-request-facts-DEE6gcB9.mjs → github-copilot-request-facts-F-EmvkpW.mjs} +1 -1
  20. package/dist/{google-interactions-9SSqsJg9.mjs → google-interactions-CTEBy9od.mjs} +19 -37
  21. package/dist/{google-messages-DsB7fmoQ.mjs → google-messages-DfpHVTaD.mjs} +18 -6
  22. package/dist/{google-provider-stream-CcEI2-MC.mjs → google-provider-stream-DJ8ANfsS.mjs} +6 -6
  23. package/dist/{google-shared-zGR0QUyS.mjs → google-shared-VTcCJLW3.mjs} +28 -41
  24. package/dist/{google-vertex-Dw_abY1w.mjs → google-vertex-Bkfizj7M.mjs} +3 -3
  25. package/dist/{google-sYI2wnY6.mjs → google-zWpkDSeN.mjs} +4 -4
  26. package/dist/{host-Bf3NjYNj.mjs → host-B2gk-d1H.mjs} +5 -11
  27. package/dist/{host-CTDz7LXD.d.mts → host-jJOffdlW.d.mts} +4 -6
  28. package/dist/{host-policy-BzZluO6_.mjs → host-policy-mpqGhbeM.mjs} +1 -1
  29. package/dist/{index-BYDvopUB.d.mts → index-DSprYWaz.d.mts} +7 -5
  30. package/dist/index.d.mts +7 -7
  31. package/dist/index.mjs +6 -6
  32. package/dist/internal/anthropic.d.mts +7 -53
  33. package/dist/internal/anthropic.mjs +6 -5
  34. package/dist/internal/google-model-family.d.mts +2 -0
  35. package/dist/internal/google-model-family.mjs +7 -3
  36. package/dist/internal/openai-completions-compat.d.mts +1 -1
  37. package/dist/internal/openai-completions-compat.mjs +1 -1
  38. package/dist/internal/openai-responses-payload-policy.d.mts +3 -3
  39. package/dist/internal/openai-responses-payload-policy.mjs +3 -3
  40. package/dist/internal/openai.d.mts +17 -26
  41. package/dist/internal/openai.mjs +10 -10
  42. package/dist/internal/retry-after.d.mts +1 -1
  43. package/dist/internal/retry-after.mjs +80 -55
  44. package/dist/internal/runtime.d.mts +9 -31
  45. package/dist/internal/runtime.mjs +10 -116
  46. package/dist/internal/shared.d.mts +9 -7
  47. package/dist/internal/shared.mjs +8 -6
  48. package/dist/internal/tool-schema.d.mts +83 -5
  49. package/dist/internal/tool-schema.mjs +4 -3
  50. package/dist/{mistral-dCDCR5ur.mjs → mistral-CATJrwne.mjs} +32 -35
  51. package/dist/model-request-body-BD5vy4rP.d.mts +17 -0
  52. package/dist/{number-coercion-C3hztWoY.mjs → number-coercion-37gmNkuA.mjs} +1 -1
  53. package/dist/{openai-chatgpt-responses-BHI3uCcA.mjs → openai-chatgpt-responses-CtdnpWwe.mjs} +95 -63
  54. package/dist/{openai-completions-CrVgnFrV.mjs → openai-completions-B6sET3oI.mjs} +10 -8
  55. package/dist/{openai-completions-compat-i-nLZxFy.d.mts → openai-completions-compat-1gsjP-KO.d.mts} +6 -3
  56. package/dist/{openai-completions-compat-CYX18uDk.mjs → openai-completions-compat-CFhQ3UVU.mjs} +7 -2
  57. package/dist/{openai-completions-transport-Dy4CZo9Q.mjs → openai-completions-transport-DviPEqw3.mjs} +370 -237
  58. package/dist/{openai-prompt-cache-r93BJhjm.mjs → openai-prompt-cache-DJ_P143P.mjs} +1 -1
  59. package/dist/{openai-prompt-cache-DIPMrMFy.d.mts → openai-prompt-cache-DgjD-UL5.d.mts} +2 -2
  60. package/dist/{openai-responses-D5oiAUOG.mjs → openai-responses-BXsJK-mD.mjs} +13 -15
  61. package/dist/{openai-responses-compaction-replay-B2FE4Nw8.d.mts → openai-responses-compaction-replay-Cvnl_nCq.d.mts} +5 -5
  62. package/dist/{openai-responses-contracts-DlKMMTxK.mjs → openai-responses-contracts-Bx-16mnf.mjs} +45 -6
  63. package/dist/{openai-responses-contracts-DKqMJ1Ct.d.mts → openai-responses-contracts-CQ4_AWjL.d.mts} +20 -8
  64. package/dist/{openai-responses-endpoint-CinGh4VZ.mjs → openai-responses-endpoint-D4v-eeYy.mjs} +55 -22
  65. package/dist/{openai-responses-payload-policy-C7GsA1y0.d.mts → openai-responses-payload-policy-BzM52TTH.d.mts} +9 -3
  66. package/dist/openai-responses-request-lifecycle-CTdAb2v1.mjs +246 -0
  67. package/dist/{openai-responses-shared-D-aDYAkJ.mjs → openai-responses-shared-Bv_1NtH9.mjs} +284 -160
  68. package/dist/{openai-responses-terminal-usage-BygjiAdi.d.mts → openai-responses-terminal-usage-CpVjkqn-.d.mts} +2 -2
  69. package/dist/{openai-tool-schema-DDPXsvJP.mjs → openai-tool-schema-Buv0GXFb.mjs} +170 -237
  70. package/dist/overflow-DJHXTNBQ.mjs +117 -0
  71. package/dist/{positive-integer-CqWjqSi9.mjs → positive-integer-Bb4Evc7L.mjs} +1 -1
  72. package/dist/prompt-cache-stability-D_MeCQFI.mjs +32 -0
  73. package/dist/{provider-error-xumb1V3F.d.mts → provider-error-CnaWYTRY.d.mts} +1 -0
  74. package/dist/{provider-error-CF9GWCs2.mjs → provider-error-qtZD2inE.mjs} +75 -13
  75. package/dist/{provider-options-CVrXZBtA.d.mts → provider-options-DeYeaflL.d.mts} +52 -6
  76. package/dist/{text-signature-Drb4Erb8.mjs → provider-replay-context-J6CfU3m1.mjs} +5 -14
  77. package/dist/{provider-transport-turn-state-BOpxgm-J.mjs → provider-transport-turn-state-Jxppzuc2.mjs} +5 -5
  78. package/dist/{provider-types-CHtRx0dI.d.mts → provider-types-ODq57rG_.d.mts} +3 -3
  79. package/dist/provider-types.d.mts +6 -6
  80. package/dist/providers.d.mts +2 -2
  81. package/dist/providers.mjs +11 -11
  82. package/dist/{reasoning-tag-text-partitioner-CpOy9-IQ.mjs → reasoning-tag-text-partitioner-DzlRubwU.mjs} +134 -47
  83. package/dist/{required-api-key-CFJvcFy_.mjs → required-api-key-D088S4Cd.mjs} +1 -1
  84. package/dist/{retry-after-CdCURCVg.d.mts → retry-after-sw-XX8aC.d.mts} +1 -1
  85. package/dist/sanitize-unicode-CRGuMONI.mjs +8 -0
  86. package/dist/sanitize-unicode-ZqVLLEKC.d.mts +27 -0
  87. package/dist/{session-resources-D1wgDQ8C.mjs → session-resources-vKvGIE3J.mjs} +1 -10
  88. package/dist/{simple-options-BAu4B54D.mjs → simple-options-BYgC6kse.mjs} +68 -79
  89. package/dist/{src-C44MKrW5.mjs → src-CyA0zJnt.mjs} +6 -2
  90. package/dist/{stream-Dd8DwRFL.mjs → stream-DLBirT0e.mjs} +8 -47
  91. package/dist/{stream-first-event-timeout-Dc42hKv6.mjs → stream-first-event-timeout-D_TfJHRJ.mjs} +1 -1
  92. package/dist/{string-coerce-CtgOVOSv.mjs → string-coerce-Cj8sqWHx.mjs} +26 -1
  93. package/dist/tool-schema-json-projection-B0hHDV7N.mjs +492 -0
  94. package/dist/{transport-stream-shared-BmpzDtdQ.d.mts → transport-stream-shared-8ru8eX4s.d.mts} +5 -5
  95. package/dist/{transport-stream-shared-CWGOuLcA.mjs → transport-stream-shared-CzQlAzJH.mjs} +27 -75
  96. package/dist/transports.d.mts +34 -28
  97. package/dist/transports.mjs +42 -48
  98. package/dist/{types-CQc6JNG4.mjs → types-BXd0kI8-.mjs} +1 -1
  99. package/dist/types-CBJthzJX.d.mts +1 -0
  100. package/dist/{types-Bd2T86VJ.d.mts → types-CVYzKrMl.d.mts} +20 -3
  101. package/dist/types.d.mts +6 -6
  102. package/dist/types.mjs +4 -4
  103. package/dist/{validation-ZX_sPVV0.mjs → validation-9FSkE8T-.mjs} +22 -3
  104. package/dist/{validation-DdGB1yy3.d.mts → validation-BQ5u20Ru.d.mts} +1 -1
  105. package/dist/validation.d.mts +1 -1
  106. package/dist/validation.mjs +1 -1
  107. package/package.json +3 -3
  108. package/dist/assistant-text-phase-Bn1hbvK9.mjs +0 -50
  109. package/dist/event-stream-6UNs-AhN.d.mts +0 -1
  110. package/dist/expect-lbe3Hgrh.mjs +0 -8
  111. package/dist/json-schema-Bp07Qui3.mjs +0 -22
  112. package/dist/openai-responses-request-lifecycle-NSIJo9jY.mjs +0 -67
  113. package/dist/openai-tool-projection-793cE3QX.d.mts +0 -37
  114. package/dist/openai-tool-schema-a5ysBBga.d.mts +0 -32
  115. package/dist/rolldown-runtime-BhDjJH2R.mjs +0 -15
  116. package/dist/sanitize-unicode-CjDG6jJx.d.mts +0 -5
  117. package/dist/string-normalization-I3ux50s9.mjs +0 -28
  118. package/dist/tool-schema-json-projection-B8sG1I9v.mjs +0 -180
  119. package/dist/types-Kdfw_04V.d.mts +0 -1
@@ -1,38 +1,40 @@
1
- import { _ as runtimeContextContentToText, f as hasRuntimeContextMarker, p as isRuntimeContextMessage } from "./types-CQc6JNG4.mjs";
1
+ import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-DzlRubwU.mjs";
2
+ import { _ as runtimeContextContentToText, f as hasRuntimeContextMarker, p as isRuntimeContextMessage } from "./types-BXd0kI8-.mjs";
3
+ import { t as appendAssistantMessageDiagnostic } from "./diagnostics-DzrF1h4J.mjs";
2
4
  import { r as appendAssistantThinking } from "./event-stream-X2gmsUWv.mjs";
3
5
  import { i as asOptionalRecord, o as isRecord, r as asOptionalObjectRecord } from "./record-coerce-CNDovvft.mjs";
4
- import { s as readNonEmptyStringPreservingWhitespace, t as hasNonEmptyString } from "./string-coerce-CtgOVOSv.mjs";
5
- import { n as asNonNegativeFiniteNumber, r as asPositiveFiniteNumber } from "./number-coercion-C3hztWoY.mjs";
6
- import { N as isImageWithMediaPayload, a as resolveAiTransportHeaderSentinels, r as getAiTransportHost } from "./host-Bf3NjYNj.mjs";
7
- import { M as resolveOpenAIThinkingApi, S as isOpenAIGpt6Model, _ as isResponsesServiceTierRejection, b as isOpenAIGpt55Model, d as OpenAIResponsesWebSocketSafeRetryError, g as responsesServiceTierObserver, k as supportsOpenAITemperature, l as OpenAIResponsesWebSocketPostDispatchError, m as parseOpenAIResponsesWebSocketServerError, r as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, t as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, u as OpenAIResponsesWebSocketPreDispatchError, v as nextResponsesServiceTier, x as isOpenAIGpt56Model, y as isOpenAIGpt54MiniModel } from "./openai-responses-contracts-DlKMMTxK.mjs";
8
- import { r as uniqueStrings } from "./string-normalization-I3ux50s9.mjs";
9
- import { n as getSessionResourceOwnerId, r as registerSessionResourceCleanup } from "./session-resources-D1wgDQ8C.mjs";
10
- import { c as stableStringify, s as truncateUtf16Safe } from "./provider-error-CF9GWCs2.mjs";
11
- import { r as resolveOpenAIPromptCacheParams } from "./openai-prompt-cache-r93BJhjm.mjs";
12
- import { d as hasOnlyResponsesFunctionTools, i as emitModelTransportError, r as emitModelTransportDebug, t as formatModelTransportDebugBaseUrl } from "./diagnostics-tQasYct1.mjs";
13
- import { f as supportsModelTools, i as providerReplayContextMatches, n as buildProviderReplayContext, o as estimateStringChars, r as isProviderReplayContext } from "./text-signature-Drb4Erb8.mjs";
14
- import { c as resolveOpenAIResponsesTextFormat, i as resolveOpenAICompletionsCompat, l as shouldOmitOllamaCompatResponseFormat, n as isNativeOpenAIEndpoint, o as usesNativeOpenAICodexResponsesBackend, r as isOpenAICodexResponsesModel, s as resolveOpenAICompletionsResponseFormat, t as detectOpenAICompletionsCompat } from "./openai-completions-compat-CYX18uDk.mjs";
15
- import { i as resolveProviderEndpoint, t as buildGuardedModelFetch } from "./host-policy-BzZluO6_.mjs";
16
- import { i as resolveCacheRetention, r as buildCopilotDynamicHeaders } from "./cloudflare-5qSGvIUH.mjs";
17
- import { E as createToolArgumentPreviewSchedule, M as racePromiseWithAbortSignal, O as parseStreamingJson, P as sanitizeSurrogates, S as withProviderResponseHook, b as transportAbortError, c as failTransportStream, d as iterateModelStream, h as notifyProviderStreamOpened, l as finalizeTerminalToolCallArguments, s as createWritableTransportEventStream, u as finalizeTransportStream, v as sanitizeTransportPayloadText, y as throwIfModelStreamAborted } from "./transport-stream-shared-CWGOuLcA.mjs";
18
- import { C as stripSystemPromptCacheBoundary, D as sortPromptCacheToolsByName, S as splitSystemPromptRelocatableBoundary, d as describeToolResultMediaPlaceholder, l as codeModeToolSurfaceObserver, m as extractToolResultText, s as prepareModelRequestBody, u as reasoningTagTextPolicy, w as stripSystemPromptRelocatableBoundary, x as splitSystemPromptCacheBoundary } from "./simple-options-BAu4B54D.mjs";
19
- import { $ as replayResponsesReasoningUpdates, A as summarizeResponsesPayload, At as measureUtf8AppendBytes, B as responsesInputFingerprint, C as readResponsesRecoveryEvent, Ct as toOpenAIResponsesToolChoice, D as logResponsesFailedNoDetails, Dt as createResponseModelTracker, E as ResponsesStreamFailure, Ft as resolvePromptCacheKey, G as responsesContinuationRequestFingerprint, H as recordResponsesContinuationState, I as convertResponsesMessages, It as redactIdentifier, Lt as sha256Hex, Mt as readOpenAICompletionsContentDeltas, Nt as readOpenAICompletionsReasoningBatch, O as safeDebugValue, Ot as isOpenAICompletionsThinkingEnabled, P as buildResponsesInputMessage, Pt as resolveOpenAIClientBaseUrl, Q as isConfigurationUpdate, R as readResponsesInputReplayState, S as isInvalidEncryptedContentError, St as reconcileOpenAIResponsesToolChoice, T as resolveNextResponsesEncryptedContentAttempt, Tt as createOpenAIProviderAcceptanceHook, U as resolveResponsesContinuationRequest, V as claimOpenAIResponsesHttpContinuation, W as responsesContinuationPrefixFingerprint, _t as resolveOpenAISimpleReasoningEffort, a as prepareResponsesTools, at as suppressOpenAIResponsesCompaction, bt as projectOpenAITools, c as observeResponsesStream, ct as buildOpenAISdkClientOptions, et as supportsResponsesReasoningUpdate, ft as getCompat, gt as resolveOpenAIRequestReasoning, ht as resolveOpenAIStrictToolFlagWithDiagnostics, j as recordResponsesContextUsage, jt as parseOpenAICompletionsUsage, k as summarizeOpenAITransportError, kt as log, lt as buildOpenAISdkRequestOptions, mt as resolveCodeModeResponsesVisibleToolNames, n as applyResponsesServiceTierPricing, nt as buildOpenAIResponsesReasoningReplayMetadata, ot as assertCodeModeResponsesToolSurface, s as processResponsesStream, st as buildOpenAIClientHeaders, ut as enforceCodeModeResponsesToolSurface, w as resolveAzureOpenAIApiVersion, wt as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, x as createResponsesStreamWithRecovery, xt as reconcileOpenAICompletionsToolChoice, yt as prepareOpenAITools, z as recordResponsesInputReplay } from "./openai-responses-shared-D-aDYAkJ.mjs";
20
- import { n as getEnvApiKey } from "./env-api-keys-CrxwdylR.mjs";
21
- import { n as transformProviderMessages, t as createAssistantOutput } from "./assistant-output-BuhRpLeD.mjs";
22
- import { a as tagUnresolvedTextAsCommentary, i as tagPendingCommentaryText, n as rememberPendingCommentaryTags, r as tagInterruptedTextPhases, t as clearPendingCommentaryText } from "./assistant-text-phase-Bn1hbvK9.mjs";
6
+ import { c as readNonEmptyStringPreservingWhitespace, t as hasNonEmptyString } from "./string-coerce-Cj8sqWHx.mjs";
7
+ import { n as asNonNegativeFiniteNumber, r as asPositiveFiniteNumber } from "./number-coercion-37gmNkuA.mjs";
8
+ import { N as isImageWithMediaPayload, a as resolveAiTransportHeaderSentinels, r as getAiTransportHost } from "./host-B2gk-d1H.mjs";
9
+ import { A as supportsOpenAITemperature, C as isOpenAIGpt6Model, L as resolveOpenAIThinkingApi, M as uniqueStrings, S as isOpenAIGpt56Model, _ as responsesServiceTierObserver, b as isOpenAIGpt54MiniModel, d as OpenAIResponsesWebSocketSafeRetryError, h as parseOpenAIResponsesWebSocketServerError, l as OpenAIResponsesWebSocketPostDispatchError, r as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS, t as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, u as OpenAIResponsesWebSocketPreDispatchError, v as isResponsesServiceTierRejection, x as isOpenAIGpt55Model, y as nextResponsesServiceTier } from "./openai-responses-contracts-Bx-16mnf.mjs";
10
+ import { n as getSessionResourceOwnerId, r as registerSessionResourceCleanup } from "./session-resources-vKvGIE3J.mjs";
11
+ import { c as stableStringify, s as truncateUtf16Safe } from "./provider-error-qtZD2inE.mjs";
12
+ import { r as resolveOpenAIPromptCacheParams } from "./openai-prompt-cache-DJ_P143P.mjs";
13
+ import { d as hasOnlyResponsesFunctionTools, i as emitModelTransportError, r as emitModelTransportDebug, t as formatModelTransportDebugBaseUrl } from "./diagnostics-Cv7cW3pS.mjs";
14
+ import { a as estimateStringChars, d as supportsModelTools, n as isProviderReplayContext, r as providerReplayContextMatches, t as buildProviderReplayContext } from "./provider-replay-context-J6CfU3m1.mjs";
15
+ import { c as resolveOpenAIResponsesTextFormat, i as resolveOpenAICompletionsCompat, l as shouldOmitOllamaCompatResponseFormat, n as isNativeOpenAIEndpoint, o as usesNativeOpenAICodexResponsesBackend, r as isOpenAICodexResponsesModel, s as resolveOpenAICompletionsResponseFormat, t as detectOpenAICompletionsCompat } from "./openai-completions-compat-CFhQ3UVU.mjs";
16
+ import { i as resolveProviderEndpoint, t as buildGuardedModelFetch } from "./host-policy-mpqGhbeM.mjs";
17
+ import { i as resolveCacheRetention, r as buildCopilotDynamicHeaders } from "./cloudflare-Dttf8jKq.mjs";
18
+ import { t as sanitizeSurrogates } from "./sanitize-unicode-CRGuMONI.mjs";
19
+ import { r as sortPromptCacheToolsByName } from "./prompt-cache-stability-D_MeCQFI.mjs";
20
+ import { C as splitSystemPromptRelocatableBoundary, S as splitSystemPromptCacheBoundary, T as stripSystemPromptRelocatableBoundary, f as extractToolResultText, g as reasoningTagTextPolicy, h as preparedRequestSuffix, l as describeToolResultMediaPlaceholder, m as codeModeToolSurfaceObserver, r as clampMaxTokensToModel, s as prepareModelRequestBody, w as stripSystemPromptCacheBoundary } from "./simple-options-BYgC6kse.mjs";
21
+ import { A as shortHash, E as createToolArgumentPreviewSchedule, M as racePromiseWithAbortSignal, O as parseStreamingJson, S as withProviderResponseHook, b as transportAbortError, c as failTransportStream, d as iterateModelStream, h as notifyProviderStreamOpened, l as finalizeTerminalToolCallArguments, s as createWritableTransportEventStream, u as finalizeTransportStream, v as sanitizeTransportPayloadText, y as throwIfModelStreamAborted } from "./transport-stream-shared-CzQlAzJH.mjs";
22
+ import { C as redactIdentifier, w as sha256Hex } from "./tool-schema-json-projection-B0hHDV7N.mjs";
23
+ import { n as getEnvApiKey } from "./env-api-keys-Xd8dm_aV.mjs";
24
+ import { n as transformProviderMessages, t as createAssistantOutput } from "./assistant-output-6iFA1nbC.mjs";
23
25
  import { t as notifyLlmRequestActivity } from "./llm-request-activity-CfMuh5cC.mjs";
24
26
  import { n as resolveOpencodeSessionHeaders } from "./session-affinity-DA4WHv1h.mjs";
27
+ import { isGoogleGemini3ThinkingLevelModel } from "./internal/google-model-family.mjs";
25
28
  import "./types.mjs";
26
- import { i as createOpenAIProviderClient, n as resolveProviderSimpleCompletionHeaders, r as resolveProviderTransportTurnState, t as filterProviderTurnHeadersForExplicitOpencodeSession } from "./provider-transport-turn-state-BOpxgm-J.mjs";
27
- import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-Dc42hKv6.mjs";
29
+ import { $ as isConfigurationUpdate, A as summarizeResponsesPayload, At as log, B as recordResponsesInputReplay, C as readResponsesRecoveryEvent, Ct as reconcileOpenAIResponsesToolChoice, D as logResponsesFailedNoDetails, E as ResponsesStreamFailure, Et as createOpenAIProviderAcceptanceHook, Ft as resolveOpenAIClientBaseUrl, G as responsesContinuationPrefixFingerprint, H as claimOpenAIResponsesHttpContinuation, I as convertResponsesMessages, It as resolvePromptCacheKey, K as responsesContinuationRequestFingerprint, Lt as withOpenAIErrorResponseHook, Mt as parseOpenAICompletionsUsage, Nt as readOpenAICompletionsContentDeltas, O as safeDebugValue, Ot as createResponseModelTracker, P as buildResponsesInputMessage, Pt as readOpenAICompletionsReasoningBatch, S as isInvalidEncryptedContentError, St as reconcileOpenAICompletionsToolChoice, T as resolveNextResponsesEncryptedContentAttempt, Tt as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, U as recordResponsesContinuationState, V as responsesInputFingerprint, W as resolveResponsesContinuationRequest, _t as resolveOpenAIRequestReasoning, a as prepareResponsesTools, bt as prepareOpenAITools, c as observeResponsesStream, ct as buildOpenAIClientHeaders, dt as enforceCodeModeResponsesToolSurface, et as replayResponsesReasoningUpdates, gt as resolveOpenAIStrictToolFlagWithDiagnostics, ht as resolveCodeModeResponsesVisibleToolNames, j as recordResponsesContextUsage, jt as measureUtf8AppendBytes, k as summarizeOpenAITransportError, kt as isOpenAICompletionsThinkingEnabled, lt as buildOpenAISdkClientOptions, n as applyResponsesServiceTierPricing, ot as suppressOpenAIResponsesCompaction, pt as getCompat, rt as buildOpenAIResponsesReasoningReplayMetadata, s as processResponsesStream, st as assertCodeModeResponsesToolSurface, tt as supportsResponsesReasoningUpdate, ut as buildOpenAISdkRequestOptions, vt as resolveOpenAISimpleReasoningEffort, w as resolveAzureOpenAIApiVersion, wt as toOpenAIResponsesToolChoice, x as createResponsesStreamWithRecovery, xt as projectOpenAITools, z as readResponsesInputReplayState } from "./openai-responses-shared-Bv_1NtH9.mjs";
30
+ import { i as createOpenAIProviderClient, n as resolveProviderSimpleCompletionHeaders, r as resolveProviderTransportTurnState, t as filterProviderTurnHeadersForExplicitOpencodeSession } from "./provider-transport-turn-state-Jxppzuc2.mjs";
31
+ import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-D_TfJHRJ.mjs";
28
32
  import { t as boundResponseBody } from "./streaming-byte-guard-DBXr1XYn.mjs";
29
- import { f as withPreparedToolSchemaNormalization, r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-DDPXsvJP.mjs";
30
- import { isGoogleGemini3ThinkingLevelModel } from "./internal/google-model-family.mjs";
33
+ import { b as prepareOpenAIFunctionTool, f as normalizeToolSchema, p as withPreparedToolSchemaNormalization, r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-Buv0GXFb.mjs";
31
34
  import { t as mapOpenAIStopReason } from "./openai-stop-reason-8s70vDXj.mjs";
32
- import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-CpOy9-IQ.mjs";
33
35
  import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-D0dvJMbZ.mjs";
34
- import { i as isOpenAIResponsesCompactionOutput, l as resolveOpenAIResponsesPayloadPolicy, n as supportsNativeOpenAIResponsesEndpoint, o as sanitizeResponsesImagePayload, r as createBoundedOpenAIResponsesCompactionFetch, s as applyOpenAIResponsesPayloadPolicy, t as isOfficialOpenAIResponsesBaseUrl } from "./openai-responses-endpoint-CinGh4VZ.mjs";
35
- import { n as withResponsesRequestAcceptance, r as createResponsesPromptEgressObserver, t as responsesRequestLifecycle } from "./openai-responses-request-lifecycle-NSIJo9jY.mjs";
36
+ import { a as isOpenAIResponsesCompactionOutput, c as applyOpenAIResponsesPayloadPolicy, d as resolveOpenAIResponsesPayloadPolicy, i as getOpenAIResponsesCompactionOutputError, n as supportsNativeOpenAIResponsesEndpoint, r as createBoundedOpenAIResponsesCompactionFetch, s as sanitizeResponsesImagePayload, t as isOfficialOpenAIResponsesBaseUrl } from "./openai-responses-endpoint-D4v-eeYy.mjs";
37
+ import { a as createResponsesV2CompactionCollector, i as createResponsesPromptEgressObserver, n as responsesRequestLifecycle, o as claimResponsesCompactRequest, r as withResponsesRequestAcceptance, t as independentResponsesRequest } from "./openai-responses-request-lifecycle-CTdAb2v1.mjs";
36
38
  import { randomUUID } from "node:crypto";
37
39
  import OpenAI, { AzureOpenAI } from "openai";
38
40
  import { ResponsesWS } from "openai/resources/responses/ws.js";
@@ -207,9 +209,21 @@ function applyCompletionsAnthropicCacheControl(payload, cacheControl = { type: "
207
209
  if (shapedPayloads.has(payload)) return;
208
210
  shapedPayloads.add(payload);
209
211
  const messages = Array.isArray(payload.messages) ? payload.messages : [];
210
- const tools = Array.isArray(payload.tools) ? payload.tools.filter(isRecord) : [];
212
+ const tools = Array.isArray(payload.tools) ? payload.tools : [];
213
+ let lastToolIndex = -1;
214
+ for (let index = 0; index < tools.length; index++) {
215
+ const tool = tools[index];
216
+ if (isRecord(tool)) {
217
+ lastToolIndex = index;
218
+ if (Object.hasOwn(tool, "cache_control")) {
219
+ const unmarked = { ...tool };
220
+ delete unmarked.cache_control;
221
+ tools[index] = unmarked;
222
+ }
223
+ }
224
+ }
211
225
  const blocks = messages.filter(isRecord).flatMap((message) => Array.isArray(message.content) ? message.content.filter(isRecord) : []);
212
- for (const block of [...tools, ...blocks]) delete block.cache_control;
226
+ for (const block of blocks) delete block.cache_control;
213
227
  if (!cacheControl) return;
214
228
  const markText = (message, splitBoundary) => {
215
229
  if (typeof message.content === "string" && message.content) message.content = [{
@@ -237,8 +251,10 @@ function applyCompletionsAnthropicCacheControl(payload, cacheControl = { type: "
237
251
  }
238
252
  return false;
239
253
  };
240
- const lastTool = tools.at(-1);
241
- if (markTools && lastTool) lastTool.cache_control = cacheControl;
254
+ if (markTools && lastToolIndex >= 0) tools[lastToolIndex] = {
255
+ ...tools[lastToolIndex],
256
+ cache_control: cacheControl
257
+ };
242
258
  if (!markMessages) return;
243
259
  const system = messages.find((message) => isRecord(message) && (message.role === "system" || message.role === "developer"));
244
260
  if (isRecord(system)) markText(system, true);
@@ -451,12 +467,12 @@ function finalizeOpenAICompletionsToolCalls(output, options = {}) {
451
467
  output.errorMessage = "Provider returned an incomplete or malformed tool call";
452
468
  output.content = output.content.filter((candidate) => !isToolCall(candidate));
453
469
  };
454
- if (toolCalls.some((call) => typeof call.name !== "string" || call.name.trim().length === 0 || typeof call.partialArgs !== "string" || call.partialArgs.trim().length === 0)) {
470
+ if (toolCalls.some((call) => typeof call.name !== "string" || call.name.trim().length === 0 || typeof call.partialJson !== "string" || call.partialJson.trim().length === 0)) {
455
471
  rejectToolCalls();
456
472
  return;
457
473
  }
458
474
  try {
459
- finalizeTerminalToolCallArguments(toolCalls, (call) => call.partialArgs);
475
+ finalizeTerminalToolCallArguments(toolCalls, (call) => call.partialJson);
460
476
  } catch {
461
477
  rejectToolCalls();
462
478
  return;
@@ -464,12 +480,75 @@ function finalizeOpenAICompletionsToolCalls(output, options = {}) {
464
480
  for (let contentIndex = 0; contentIndex < output.content.length; contentIndex += 1) {
465
481
  const block = output.content[contentIndex];
466
482
  if (!block || !isToolCall(block)) continue;
467
- delete block.partialArgs;
483
+ delete block.partialJson;
468
484
  delete block.streamIndex;
469
485
  options.onConfirmedToolCall?.(block, contentIndex);
470
486
  }
471
487
  }
472
488
  //#endregion
489
+ //#region packages/ai/src/transports/openai-completions-context-budget-buffer.ts
490
+ const MAX_CONTEXT_BUDGET_BUFFER_CHUNKS = 8192;
491
+ const MAX_CONTEXT_BUDGET_BUFFER_BYTES = 4194304;
492
+ async function bufferContextLimitedCompletions(source, signal) {
493
+ const iterator = source[Symbol.asyncIterator]();
494
+ const chunks = [];
495
+ let bufferedBytes = 0;
496
+ let overflow;
497
+ let finished = false;
498
+ let failed = false;
499
+ let sourceError;
500
+ let finishReason;
501
+ try {
502
+ for (;;) {
503
+ const next = await iterator.next();
504
+ if (next.done) {
505
+ finished = true;
506
+ break;
507
+ }
508
+ const chunk = next.value;
509
+ const chunkBytes = Buffer.byteLength(JSON.stringify(chunk) ?? "", "utf8");
510
+ if (chunks.length >= MAX_CONTEXT_BUDGET_BUFFER_CHUNKS || bufferedBytes + chunkBytes > MAX_CONTEXT_BUDGET_BUFFER_BYTES) {
511
+ overflow = next;
512
+ break;
513
+ }
514
+ chunks.push(chunk);
515
+ bufferedBytes += chunkBytes;
516
+ const chunkFinishReason = chunk?.choices?.[0]?.finish_reason;
517
+ if (chunkFinishReason) finishReason = chunkFinishReason;
518
+ signal?.throwIfAborted();
519
+ }
520
+ } catch (error) {
521
+ failed = true;
522
+ sourceError = error;
523
+ iterator.return?.().catch(() => void 0);
524
+ }
525
+ return {
526
+ bounded: overflow === void 0,
527
+ failed,
528
+ finishReason,
529
+ stream: { async *[Symbol.asyncIterator]() {
530
+ try {
531
+ yield* chunks;
532
+ if (failed) throw sourceError;
533
+ if (overflow !== void 0) {
534
+ yield overflow.value;
535
+ for (;;) {
536
+ const next = await iterator.next();
537
+ if (next.done) {
538
+ finished = true;
539
+ return;
540
+ }
541
+ yield next.value;
542
+ }
543
+ }
544
+ } finally {
545
+ chunks.length = 0;
546
+ if (!finished && !failed) iterator.return?.().catch(() => void 0);
547
+ }
548
+ } }
549
+ };
550
+ }
551
+ //#endregion
473
552
  //#region packages/ai/src/transports/openai-completions-host.ts
474
553
  /**
475
554
  * Chat Completions accepts Azure AI Foundry hosts in addition to traditional
@@ -488,6 +567,7 @@ function hasToolCallHistory(messages) {
488
567
  /** Convert a normalized transcript to OpenAI Chat Completions messages. */
489
568
  function convertMessages(model, context, compat, options = {}) {
490
569
  const params = [];
570
+ const supportsTools = options.supportsTools !== false;
491
571
  const normalizeToolCallId = (id) => {
492
572
  if (id.includes("|")) return id.slice(0, id.indexOf("|")).replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 40);
493
573
  if (model.provider === "openai") return id.length > 40 ? truncateUtf16Safe(id, 40) : id;
@@ -565,7 +645,10 @@ function convertMessages(model, context, compat, options = {}) {
565
645
  msg.content.forEach((block) => {
566
646
  if (block.type === "text" && block.text.trim().length > 0) assistantTexts.push(sanitizeSurrogates(block.text));
567
647
  else if (block.type === "thinking" && block.thinking.trim().length > 0) nonEmptyThinkingBlocks.push(block);
568
- else if (block.type === "toolCall") toolCalls.push(block);
648
+ else if (block.type === "toolCall") {
649
+ if (supportsTools) toolCalls.push(block);
650
+ else assistantTexts.push(sanitizeSurrogates(`[tool call id=${block.id} name=${block.name}] ${JSON.stringify(block.arguments)}`));
651
+ }
569
652
  });
570
653
  if (nonEmptyThinkingBlocks.length > 0 && compat.requiresThinkingAsText) assistantMsg.content = [{
571
654
  type: "text",
@@ -617,13 +700,28 @@ function convertMessages(model, context, compat, options = {}) {
617
700
  const textResult = extractToolResultText(toolMsg.content);
618
701
  const mediaPlaceholder = describeToolResultMediaPlaceholder(toolMsg.content);
619
702
  const images = toolMsg.content.filter(isImageWithMediaPayload);
620
- const toolResultMsg = {
621
- role: "tool",
622
- content: textResult.trim() ? textResult : mediaPlaceholder ?? "(no output)",
623
- tool_call_id: toolMsg.toolCallId
624
- };
625
- if (compat.requiresToolResultName && toolMsg.toolName) toolResultMsg.name = toolMsg.toolName;
626
- params.push(toolResultMsg);
703
+ const content = textResult.trim() ? textResult : mediaPlaceholder ?? "(no output)";
704
+ if (supportsTools) {
705
+ const toolResultMsg = {
706
+ role: "tool",
707
+ content,
708
+ tool_call_id: preparedRequestSuffix.has(context) ? preparedRequestSuffix.toolCallId(context, toolMsg.toolCallId, normalizeToolCallId(toolMsg.toolCallId)) : toolMsg.toolCallId
709
+ };
710
+ if (compat.requiresToolResultName && toolMsg.toolName) toolResultMsg.name = toolMsg.toolName;
711
+ params.push(toolResultMsg);
712
+ } else {
713
+ const text = sanitizeSurrogates(`[tool result id=${toolMsg.toolCallId} name=${toolMsg.toolName}] ${content}`);
714
+ const previous = params.at(-1);
715
+ if (previous?.role !== "assistant") params.push({
716
+ role: "assistant",
717
+ content: text
718
+ });
719
+ else if (Array.isArray(previous.content)) previous.content.push({
720
+ type: "text",
721
+ text
722
+ });
723
+ else previous.content = previous.content ? `${previous.content}\n${text}` : text;
724
+ }
627
725
  if (images.length > 0 && model.input.includes("image")) {
628
726
  const boundedToolName = sanitizeSurrogates(truncateUtf16Safe(toolMsg.toolName ?? "", 64));
629
727
  imageContentParts.push({
@@ -639,7 +737,7 @@ function convertMessages(model, context, compat, options = {}) {
639
737
  }
640
738
  i = j - 1;
641
739
  if (imageContentParts.length > 0) {
642
- if (compat.requiresAssistantAfterToolResult) params.push({
740
+ if (supportsTools && compat.requiresAssistantAfterToolResult) params.push({
643
741
  role: "assistant",
644
742
  content: "I have processed the tool results."
645
743
  });
@@ -648,7 +746,7 @@ function convertMessages(model, context, compat, options = {}) {
648
746
  content: imageContentParts
649
747
  });
650
748
  lastRole = "user";
651
- } else lastRole = "toolResult";
749
+ } else lastRole = supportsTools ? "toolResult" : "assistant";
652
750
  continue;
653
751
  }
654
752
  lastRole = msg.role;
@@ -685,7 +783,7 @@ function applyCompletionsReasoningAndRouting(params, model, reasoning, compat, m
685
783
  const direct = mode === "direct";
686
784
  const nativeEffort = compat.thinkingFormat === "openrouter" ? void 0 : reasoning.effort;
687
785
  const enabled = reasoning.thinkingEnabled ?? false;
688
- if (model.reasoning) {
786
+ if (model.reasoning && (!compat.thinkingOffControlUndeclared || reasoning.thinkingEnabled !== void 0 || !reasoning.thinkingExplicit)) {
689
787
  let allowScalarEffort = true;
690
788
  if (direct && compat.thinkingFormat === "zai") {
691
789
  params.thinking = enabled ? {
@@ -697,9 +795,11 @@ function applyCompletionsReasoningAndRouting(params, model, reasoning, compat, m
697
795
  params.enable_thinking = enabled;
698
796
  allowScalarEffort = false;
699
797
  } else if (compat.thinkingFormat === "qwen-chat-template") {
798
+ const chatTemplateReasoningEffort = enabled && reasoning.thinkingExplicit && model.compat?.supportedReasoningEfforts?.length ? reasoning.effort : void 0;
700
799
  params.chat_template_kwargs = {
701
800
  enable_thinking: enabled,
702
- ...direct ? { preserve_thinking: true } : {}
801
+ ...direct ? { preserve_thinking: true } : {},
802
+ ...chatTemplateReasoningEffort ? { reasoning_effort: chatTemplateReasoningEffort } : {}
703
803
  };
704
804
  allowScalarEffort = false;
705
805
  } else if (compat.thinkingFormat === "together" || direct && compat.thinkingFormat === "deepseek") {
@@ -878,10 +978,6 @@ function resolveOpenAICompletionsMaxTokens(model, options) {
878
978
  clampToModelMaxTokens: false
879
979
  };
880
980
  }
881
- function resolveOpenAICompletionsModelMaxTokens(model) {
882
- const maxTokens = asPositiveFiniteNumber(model.maxTokens);
883
- return maxTokens === void 0 ? void 0 : Math.floor(maxTokens);
884
- }
885
981
  const OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN = 1.25;
886
982
  const OPENAI_COMPLETIONS_IMAGE_CHAR_ESTIMATE = 8e3;
887
983
  const MIN_USEFUL_OUTPUT_TOKENS = 16;
@@ -893,8 +989,7 @@ function estimateJsonChars(value, fallback) {
893
989
  }
894
990
  }
895
991
  function estimateOpenAICompletionsInputTokens(payload) {
896
- let adjustedChars = 0;
897
- adjustedChars += estimateOpenAICompletionsMessagesChars(payload.messages);
992
+ let adjustedChars = estimateOpenAICompletionsMessagesChars(payload.messages);
898
993
  if (payload.tools?.length) adjustedChars += estimateJsonChars(payload.tools, 1024);
899
994
  if (payload.response_format !== void 0) adjustedChars += estimateJsonChars(payload.response_format, 256);
900
995
  return Math.ceil(adjustedChars / 4 * OPENAI_COMPLETIONS_INPUT_TOKEN_SAFETY_MARGIN);
@@ -944,16 +1039,8 @@ function convertTools(tools, compat, model, mode) {
944
1039
  return {
945
1040
  projection,
946
1041
  tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
947
- const functionTool = {
948
- name: tool.name,
949
- description: tool.description,
950
- parameters: mode === "direct" ? tool.parameters : normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
951
- };
952
- if (strict !== void 0) functionTool.strict = strict;
953
- return {
954
- type: "function",
955
- function: functionTool
956
- };
1042
+ const parameters = compat.requiresTypedUnionBranches ? normalizeToolSchema(tool.parameters, "moonshot") : tool.parameters;
1043
+ return prepareOpenAIFunctionTool(tool.name, tool.description, mode === "direct" ? parameters : normalizeOpenAIStrictToolParameters(parameters, strict === true, model.compat), strict).completions;
957
1044
  })
958
1045
  };
959
1046
  };
@@ -962,7 +1049,22 @@ function convertTools(tools, compat, model, mode) {
962
1049
  function buildOpenAICompletionsParams(model, context, options) {
963
1050
  return buildOpenAICompletionsRequest(model, context, options, { mode: "managed" });
964
1051
  }
1052
+ const contextOutputBudgets = /* @__PURE__ */ new WeakMap();
1053
+ function completionsInputHash(payload) {
1054
+ return shortHash(JSON.stringify([
1055
+ payload.messages,
1056
+ payload.tools,
1057
+ payload.response_format
1058
+ ]));
1059
+ }
1060
+ function resolveCompletionsContextOutputBudget(request, payload) {
1061
+ const budget = contextOutputBudgets.get(request);
1062
+ const limits = [payload.max_tokens, payload.max_completion_tokens].filter((value) => value !== void 0);
1063
+ if (!budget || payload.model !== budget.model || !Array.isArray(payload.messages) || payload.tools !== void 0 && !Array.isArray(payload.tools) || limits.length === 0 || limits.some((value) => value !== budget.cap) || completionsInputHash(payload) !== budget.inputHash) return;
1064
+ return budget.cap;
1065
+ }
965
1066
  function buildOpenAICompletionsRequest(model, context, options, policy) {
1067
+ let contextOutputCap;
966
1068
  const resolvedPolicy = policy.mode === "direct" ? policy : {
967
1069
  ...policy,
968
1070
  compat: getCompat(model)
@@ -980,8 +1082,10 @@ function buildOpenAICompletionsRequest(model, context, options, policy) {
980
1082
  "qwen"
981
1083
  ].includes(model.provider));
982
1084
  const cacheOptOutIndexes = /* @__PURE__ */ new Set();
1085
+ const supportsTools = policy.mode === "direct" || supportsModelTools(model);
983
1086
  const convertedMessages = convertMessages(model, context, compat, {
984
1087
  cacheOptOutIndexes,
1088
+ supportsTools,
985
1089
  preserveSystemPromptCacheBoundary: cacheControl !== void 0 && !managedCompat?.requiresStringContent
986
1090
  });
987
1091
  let messages = convertedMessages;
@@ -1014,7 +1118,7 @@ function buildOpenAICompletionsRequest(model, context, options, policy) {
1014
1118
  if (policy.mode === "managed" && options?.seed !== void 0) params.seed = options.seed;
1015
1119
  if (options?.stop !== void 0 && options.stop.length > 0) params.stop = options.stop;
1016
1120
  let directToolProjection;
1017
- if (policy.mode === "direct" || supportsModelTools(model)) {
1121
+ if (supportsTools) {
1018
1122
  if (context.tools) {
1019
1123
  const converted = convertTools(context.tools, compat, model, policy.mode);
1020
1124
  if (policy.mode === "direct") directToolProjection = converted.projection;
@@ -1043,7 +1147,11 @@ function buildOpenAICompletionsRequest(model, context, options, policy) {
1043
1147
  ...model,
1044
1148
  compat: model.compat ?? void 0
1045
1149
  }, simpleReasoning)) ?? (usesBinaryOpenRouterThinking ? void 0 : "high");
1046
- const reasoning = resolveOpenAIRequestReasoning(model, requestedEffort, compat.reasoningEffortForOff);
1150
+ const thinkingExplicit = options?.openclawThinkingExplicit ?? (options?.reasoningEffort !== void 0 || options?.reasoning !== void 0);
1151
+ const reasoning = {
1152
+ ...resolveOpenAIRequestReasoning(model, requestedEffort, compat.reasoningEffortForOff),
1153
+ thinkingExplicit
1154
+ };
1047
1155
  const { effort, thinkingEnabled } = reasoning;
1048
1156
  {
1049
1157
  const maxTokenBudget = policy.mode === "direct" ? {
@@ -1053,7 +1161,8 @@ function buildOpenAICompletionsRequest(model, context, options, policy) {
1053
1161
  const effectiveMaxTokens = maxTokenBudget.maxTokens;
1054
1162
  const effectiveContextTokens = asPositiveFiniteNumber(model.contextTokens) ?? asPositiveFiniteNumber(model.contextWindow);
1055
1163
  let clampedMaxTokens = effectiveMaxTokens;
1056
- const modelMaxTokens = resolveOpenAICompletionsModelMaxTokens(model);
1164
+ const modelOutputLimit = asPositiveFiniteNumber(model.maxTokens);
1165
+ const modelMaxTokens = modelOutputLimit === void 0 ? void 0 : Math.floor(modelOutputLimit);
1057
1166
  if (maxTokenBudget.clampToModelMaxTokens && clampedMaxTokens !== void 0 && modelMaxTokens !== void 0 && clampedMaxTokens > modelMaxTokens) {
1058
1167
  clampedMaxTokens = modelMaxTokens;
1059
1168
  if (policy.mode === "managed") emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} modelMaxTokens=${modelMaxTokens}`);
@@ -1062,8 +1171,9 @@ function buildOpenAICompletionsRequest(model, context, options, policy) {
1062
1171
  const estimatedInputTokens = estimateOpenAICompletionsInputTokens(params);
1063
1172
  const remainingBudget = Math.max(0, effectiveContextTokens - estimatedInputTokens - 1);
1064
1173
  if (clampedMaxTokens > remainingBudget) {
1065
- if (remainingBudget < MIN_USEFUL_OUTPUT_TOKENS) throw Object.assign(/* @__PURE__ */ new Error(`Context window exceeded: estimated input ${estimatedInputTokens} leaves only ${remainingBudget} output tokens within the ${effectiveContextTokens}-token context.`), { code: "context_length_exceeded" });
1174
+ if (remainingBudget < (params.tools?.length && params.tool_choice !== "none" ? Math.max(MIN_USEFUL_OUTPUT_TOKENS, Math.min(2048, Math.ceil((modelMaxTokens ?? 0) / 8))) : MIN_USEFUL_OUTPUT_TOKENS)) throw Object.assign(/* @__PURE__ */ new Error(`Context window exceeded: estimated input ${estimatedInputTokens} leaves only ${remainingBudget} output tokens within the ${effectiveContextTokens}-token context.`), { code: "context_length_exceeded" });
1066
1175
  clampedMaxTokens = remainingBudget;
1176
+ contextOutputCap = remainingBudget;
1067
1177
  emitModelTransportDebug(log, `[completions] clamp_max_tokens provider=${model.provider} api=${model.api} model=${model.id} requested=${effectiveMaxTokens} output=${clampedMaxTokens} effectiveContext=${effectiveContextTokens} estimatedInput=${estimatedInputTokens}`);
1068
1178
  }
1069
1179
  }
@@ -1082,6 +1192,11 @@ function buildOpenAICompletionsRequest(model, context, options, policy) {
1082
1192
  if (isOpenAIGpt56Model(model)) params.reasoning_effort = "none";
1083
1193
  else if (isOpenAIGpt54MiniModel(model) || isOpenAIGpt55Model(model)) delete params.reasoning_effort;
1084
1194
  }
1195
+ if (contextOutputCap !== void 0) contextOutputBudgets.set(params, {
1196
+ model: params.model,
1197
+ cap: contextOutputCap,
1198
+ inputHash: completionsInputHash(params)
1199
+ });
1085
1200
  return params;
1086
1201
  }
1087
1202
  //#endregion
@@ -1218,7 +1333,7 @@ function parseDeepSeekDsmlToolCallBlock(body) {
1218
1333
  kind: "toolCall",
1219
1334
  name: invokeName,
1220
1335
  arguments: parsedArguments,
1221
- partialArgs: JSON.stringify(parsedArguments)
1336
+ partialJson: JSON.stringify(parsedArguments)
1222
1337
  });
1223
1338
  }
1224
1339
  return toolCalls;
@@ -1379,7 +1494,7 @@ function recoverGemmaCalls(text) {
1379
1494
  kind: "toolCall",
1380
1495
  name,
1381
1496
  arguments: argumentsValue,
1382
- partialArgs: JSON.stringify(argumentsValue)
1497
+ partialJson: JSON.stringify(argumentsValue)
1383
1498
  });
1384
1499
  } catch {
1385
1500
  return;
@@ -1437,6 +1552,38 @@ function extractToolCallThoughtSignature(toolCall) {
1437
1552
  const extra = tc.extra_content?.google;
1438
1553
  return readNonEmptyStringPreservingWhitespace(extra?.thought_signature) ?? readNonEmptyStringPreservingWhitespace(tc.function?.thought_signature) ?? readNonEmptyStringPreservingWhitespace(tc.thought_signature);
1439
1554
  }
1555
+ async function* observeOpenAICompletionsProgress(responseStream, signal) {
1556
+ const maxUsageTokens = {
1557
+ prompt: 0,
1558
+ completion: 0,
1559
+ total: 0,
1560
+ reasoning: 0
1561
+ };
1562
+ for await (const rawChunk of responseStream) {
1563
+ throwIfModelStreamAborted(signal);
1564
+ if (rawChunk && typeof rawChunk === "object") {
1565
+ const chunk = rawChunk;
1566
+ const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
1567
+ const usage = chunk.usage || choice?.usage;
1568
+ let hasUsageProgress = false;
1569
+ for (const [counter, reported] of [
1570
+ ["prompt", usage?.prompt_tokens],
1571
+ ["completion", usage?.completion_tokens],
1572
+ ["total", usage?.total_tokens],
1573
+ ["reasoning", usage?.completion_tokens_details?.reasoning_tokens]
1574
+ ]) {
1575
+ const tokens = asPositiveFiniteNumber(reported) ?? 0;
1576
+ if (tokens > maxUsageTokens[counter]) {
1577
+ maxUsageTokens[counter] = tokens;
1578
+ hasUsageProgress = true;
1579
+ }
1580
+ }
1581
+ const rawChoiceDelta = choice?.delta ?? choice?.message;
1582
+ notifyLlmRequestActivity(signal, Boolean(hasUsageProgress || choice?.finish_reason || rawChoiceDelta && (rawChoiceDelta.tool_calls?.length || hasOpenAICompletionsDeltaContent(rawChoiceDelta))));
1583
+ }
1584
+ yield rawChunk;
1585
+ }
1586
+ }
1440
1587
  async function processCompletionsStream(responseStream, output, model, stream, options) {
1441
1588
  const MAX_POST_TOOL_CALL_BUFFER_BYTES = 256e3;
1442
1589
  const directMode = options?.mode === "direct";
@@ -1453,17 +1600,14 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1453
1600
  let currentBlock = null;
1454
1601
  const directContent = { block: null };
1455
1602
  let currentTextSource;
1456
- let pendingInterruptedTextBlock = null;
1457
- let confirmedInterruptedTextBlock = null;
1603
+ let sawReasoning = false;
1458
1604
  let pendingPostToolCallDeltas = [];
1459
1605
  let pendingPostToolCallBytes = 0;
1460
1606
  const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
1461
1607
  const toolCallBlocksById = /* @__PURE__ */ new Map();
1462
1608
  const encryptedReasoning = createOpenAIEncryptedToolCallReasoningTracker();
1463
1609
  const toolArgumentPreviewSchedules = /* @__PURE__ */ new WeakMap();
1464
- const provisionalCommentaryTags = directMode ? options.provisionalCommentaryTags : /* @__PURE__ */ new Map();
1465
1610
  const blockIndices = /* @__PURE__ */ new WeakMap();
1466
- let explicitVisibleTextBlocks;
1467
1611
  const normalizeToolCallDeltas = createOpenAICompletionsToolCallDeltaNormalizer();
1468
1612
  let finishReason;
1469
1613
  let sawNativeToolCallDelta = false;
@@ -1505,7 +1649,6 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1505
1649
  text: ""
1506
1650
  };
1507
1651
  currentTextSource = delta.source;
1508
- if (delta.source === "reasoning_detail") (explicitVisibleTextBlocks ??= /* @__PURE__ */ new Set()).add(currentBlock);
1509
1652
  } else currentBlock = {
1510
1653
  type: "thinking",
1511
1654
  thinking: "",
@@ -1521,13 +1664,7 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1521
1664
  });
1522
1665
  }
1523
1666
  if (currentBlock.type === "thinking") appendAssistantThinking(currentBlock, delta.text);
1524
- else {
1525
- currentBlock.text += delta.text;
1526
- if (pendingInterruptedTextBlock && delta.text.trim()) {
1527
- confirmedInterruptedTextBlock = pendingInterruptedTextBlock;
1528
- pendingInterruptedTextBlock = null;
1529
- }
1530
- }
1667
+ else currentBlock.text += delta.text;
1531
1668
  const event = {
1532
1669
  contentIndex: blockIndex(),
1533
1670
  delta: delta.text
@@ -1579,20 +1716,19 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1579
1716
  currentBlock = null;
1580
1717
  flushPendingPostToolCallDeltas();
1581
1718
  }
1582
- rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
1583
1719
  const block = {
1584
1720
  type: "toolCall",
1585
1721
  id: `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`,
1586
1722
  name: toolCall.name,
1587
1723
  arguments: toolCall.arguments,
1588
- partialArgs: toolCall.partialArgs
1724
+ partialJson: toolCall.partialJson
1589
1725
  };
1590
1726
  currentBlock = block;
1591
1727
  appendToolCallBlock(block);
1592
1728
  pushStreamEvent({
1593
1729
  type: "toolcall_delta",
1594
1730
  contentIndex: blockIndices.get(block) ?? -1,
1595
- delta: toolCall.partialArgs,
1731
+ delta: toolCall.partialJson,
1596
1732
  partial: output
1597
1733
  });
1598
1734
  };
@@ -1623,7 +1759,7 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1623
1759
  });
1624
1760
  };
1625
1761
  const flushReasoningTagTextPartitioner = (allowRecovery = true) => {
1626
- const recoverUnclosed = allowRecovery && output.stopReason !== "length" && output.stopReason !== "error" && output.stopReason !== "aborted";
1762
+ const recoverUnclosed = allowRecovery && !sawReasoning && output.stopReason !== "length" && output.stopReason !== "error" && output.stopReason !== "aborted";
1627
1763
  for (const delta of reasoningTagTextPartitioner.flush({ recoverUnclosed })) appendPartitionedVisibleDelta(delta);
1628
1764
  };
1629
1765
  const flushGemmaToolCallRecoverer = (allowRecovery = true) => {
@@ -1634,36 +1770,17 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1634
1770
  if (currentBlock?.type !== "text" && !reasoningTagTextPartitioner.hasPending()) return;
1635
1771
  flushReasoningTagTextPartitioner();
1636
1772
  if (currentBlock?.type !== "text") return;
1637
- if (currentTextSource !== "reasoning_detail" && currentBlock.text.trim()) pendingInterruptedTextBlock = currentBlock;
1638
1773
  currentBlock = null;
1639
1774
  if (directMode) directContent.block = null;
1640
1775
  currentTextSource = void 0;
1641
1776
  };
1642
- const beginReasoning = (hasFollowingVisibleText, forceStrict = false) => {
1643
- if (!output.openclawDelivery?.textPhaseRequiresTerminal) output.openclawDelivery = {
1644
- ...output.openclawDelivery,
1645
- textPhaseRequiresTerminal: true
1646
- };
1647
- if (forceStrict || reasoningTagTextPartitioner.hasPending()) reasoningTagTextPartitioner.markStrict();
1777
+ const beginReasoning = (hasFollowingVisibleText) => {
1778
+ sawReasoning = true;
1648
1779
  if (!hasFollowingVisibleText || !reasoningTagTextPartitioner.hasPendingSyntax()) sealTextBeforeReasoning();
1649
1780
  };
1650
- const guardedStream = withFirstStreamEventTimeout(responseStream, {
1651
- provider: model.provider,
1652
- api: model.api,
1653
- model: model.id,
1654
- timeoutMs: options?.firstEventTimeoutMs ?? 0,
1655
- stage: "completions",
1656
- abort: options?.abortFirstEventStream,
1657
- onTimeout: options?.onFirstEventTimeout,
1658
- 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."
1659
- });
1660
- const events = directMode ? guardedStream : iterateModelStream(guardedStream, options?.signal);
1661
- const maxUsageTokens = {
1662
- prompt: 0,
1663
- completion: 0,
1664
- total: 0,
1665
- reasoning: 0
1666
- };
1781
+ const events = directMode ? responseStream : iterateModelStream(responseStream, options?.signal);
1782
+ let maxReasoningTokens = 0;
1783
+ let providerProcessingMs;
1667
1784
  for await (const rawChunk of events) {
1668
1785
  throwIfModelStreamAborted(options?.signal);
1669
1786
  chunkPushedEvent = false;
@@ -1673,24 +1790,21 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1673
1790
  if (typeof chunk.model === "string" && chunk.model.length > 0 && chunk.model !== model.id) output.responseModel ||= chunk.model;
1674
1791
  const choice = Array.isArray(chunk.choices) ? chunk.choices[0] : void 0;
1675
1792
  const usage = chunk.usage || choice?.usage;
1676
- let hasUsageProgress = false;
1677
- let hasReasoningUsageActivity = false;
1678
- for (const [counter, reported] of [
1679
- ["prompt", usage?.prompt_tokens],
1680
- ["completion", usage?.completion_tokens],
1681
- ["total", usage?.total_tokens],
1682
- ["reasoning", usage?.completion_tokens_details?.reasoning_tokens]
1683
- ]) {
1684
- const tokens = asPositiveFiniteNumber(reported) ?? 0;
1685
- if (tokens > maxUsageTokens[counter]) {
1686
- maxUsageTokens[counter] = tokens;
1687
- hasUsageProgress = true;
1688
- hasReasoningUsageActivity ||= counter === "reasoning";
1689
- }
1793
+ if (chunk.timings !== void 0 && (choice?.finish_reason || Array.isArray(chunk.choices) && chunk.choices.length === 0 && chunk.usage)) {
1794
+ const promptMs = asNonNegativeFiniteNumber(chunk.timings?.prompt_ms);
1795
+ const predictedMs = asNonNegativeFiniteNumber(chunk.timings?.predicted_ms);
1796
+ providerProcessingMs = promptMs !== void 0 && predictedMs !== void 0 ? asNonNegativeFiniteNumber(promptMs + predictedMs) : void 0;
1797
+ }
1798
+ const reasoningTokens = asPositiveFiniteNumber(usage?.completion_tokens_details?.reasoning_tokens) ?? 0;
1799
+ const hasReasoningUsageActivity = reasoningTokens > maxReasoningTokens;
1800
+ maxReasoningTokens = Math.max(maxReasoningTokens, reasoningTokens);
1801
+ if (usage) {
1802
+ output.usage = parseOpenAICompletionsUsage(usage, model, { includeReasoningTokens: !directMode });
1803
+ const terminalUsage = Boolean(finishReason || choice?.finish_reason || chunk.choices?.length === 0);
1804
+ const completeCounts = [usage.prompt_tokens, usage.completion_tokens].every((value) => typeof value === "number" && Number.isFinite(value) && value >= 0);
1805
+ output.usage.usageReport = { state: terminalUsage && completeCounts ? "available" : "unavailable" };
1690
1806
  }
1691
- if (usage) output.usage = parseOpenAICompletionsUsage(usage, model, { includeReasoningTokens: !directMode });
1692
1807
  const rawChoiceDelta = choice?.delta ?? choice?.message;
1693
- notifyLlmRequestActivity(options?.signal, Boolean(hasUsageProgress || choice?.finish_reason || rawChoiceDelta && (rawChoiceDelta.tool_calls?.length || hasOpenAICompletionsDeltaContent(rawChoiceDelta))));
1694
1808
  if (!choice) {
1695
1809
  emitReasoningUsageActivity(hasReasoningUsageActivity);
1696
1810
  continue;
@@ -1715,15 +1829,12 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1715
1829
  const lastVisibleTextIndex = contentDeltas.findLastIndex((delta) => delta.kind === "text");
1716
1830
  const hasSameChunkVisibleText = reasoningBatch.hasVisibleText || lastVisibleTextIndex !== -1;
1717
1831
  if (hasReasoningThinking) {
1718
- beginReasoning(hasSameChunkVisibleText, true);
1832
+ beginReasoning(hasSameChunkVisibleText);
1719
1833
  appendReasoningDeltas(reasoningDeltas);
1720
1834
  }
1721
1835
  for (const [contentDeltaIndex, contentDelta] of contentDeltas.entries()) if (contentDelta.kind === "text") {
1722
1836
  const parts = gemmaToolCallRecoverer?.push(contentDelta.text) ?? [contentDelta];
1723
- for (const part of parts) {
1724
- const routedDeltas = hasReasoningThinking ? reasoningTagTextPartitioner.push(part.text) : reasoningTagTextPartitioner.pushVisible(part.text);
1725
- for (const routedDelta of routedDeltas) appendPartitionedVisibleDelta(routedDelta);
1726
- }
1837
+ for (const part of parts) for (const routedDelta of reasoningTagTextPartitioner.pushVisible(part.text)) appendPartitionedVisibleDelta(routedDelta);
1727
1838
  } else {
1728
1839
  beginReasoning(contentDeltaIndex < lastVisibleTextIndex);
1729
1840
  if (emitReasoning) {
@@ -1737,7 +1848,6 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1737
1848
  flushGemmaToolCallRecoverer(false);
1738
1849
  sawNativeToolCallDelta = true;
1739
1850
  flushReasoningTagTextPartitioner(false);
1740
- rememberPendingCommentaryTags(provisionalCommentaryTags, tagPendingCommentaryText(output.content));
1741
1851
  for (const toolCall of toolCallDeltas) {
1742
1852
  const streamIndex = typeof toolCall.index === "number" ? toolCall.index : void 0;
1743
1853
  let block = streamIndex !== void 0 ? toolCallBlocksByIndex.get(streamIndex) : void 0;
@@ -1755,7 +1865,7 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1755
1865
  id: toolCall.id || "",
1756
1866
  name: toolCall.function?.name || "",
1757
1867
  arguments: {},
1758
- partialArgs: "",
1868
+ partialJson: "",
1759
1869
  ...initialSig ? { thoughtSignature: initialSig } : {}
1760
1870
  };
1761
1871
  encryptedReasoning.rememberToolCall(block.id, block);
@@ -1776,8 +1886,8 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1776
1886
  if (deltaSig) block.thoughtSignature = deltaSig;
1777
1887
  const toolArgumentsDelta = toolCall.function?.arguments;
1778
1888
  if (toolArgumentsDelta) {
1779
- block.partialArgs += toolArgumentsDelta;
1780
- if (toolArgumentPreviewSchedules.get(block)?.(block.partialArgs.length)) block.arguments = parseStreamingJson(block.partialArgs);
1889
+ block.partialJson += toolArgumentsDelta;
1890
+ if (toolArgumentPreviewSchedules.get(block)?.(block.partialJson.length)) block.arguments = parseStreamingJson(block.partialJson);
1781
1891
  }
1782
1892
  if (toolArgumentsDelta || directMode) pushStreamEvent({
1783
1893
  type: "toolcall_delta",
@@ -1812,14 +1922,16 @@ async function processCompletionsStream(responseStream, output, model, stream, o
1812
1922
  });
1813
1923
  }
1814
1924
  });
1815
- if (confirmedInterruptedTextBlock && output.stopReason !== "toolUse" && output.stopReason !== "error" && output.stopReason !== "aborted") tagInterruptedTextPhases(output.content, confirmedInterruptedTextBlock, explicitVisibleTextBlocks);
1816
- if (output.stopReason !== "toolUse") clearPendingCommentaryText(provisionalCommentaryTags);
1817
- if (output.stopReason === "error" || output.stopReason === "aborted") tagUnresolvedTextAsCommentary(output);
1818
- if (output.stopReason === "toolUse") tagPendingCommentaryText(output.content);
1819
- if (!output.usage.contextUsage && !options?.signal?.aborted && output.stopReason !== "error" && output.stopReason !== "aborted") {
1925
+ output.usage.usageReport ??= { state: "unavailable" };
1926
+ if ((!output.usage.contextUsage || output.usage.usageReport.state === "unavailable") && !options?.signal?.aborted && output.stopReason !== "error" && output.stopReason !== "aborted") {
1820
1927
  output.usage.contextUsage = { state: "unavailable" };
1821
1928
  if (!compat.supportsUsageInStreaming) warnMissingStreamingUsage(openAIModel);
1822
1929
  }
1930
+ if (providerProcessingMs !== void 0) appendAssistantMessageDiagnostic(output, {
1931
+ type: "provider_timing",
1932
+ timestamp: Date.now(),
1933
+ details: { processingMs: providerProcessingMs }
1934
+ });
1823
1935
  }
1824
1936
  const MAX_MISSING_USAGE_HINT_KEYS = 256;
1825
1937
  const missingUsageHintKeys = /* @__PURE__ */ new Set();
@@ -1920,7 +2032,7 @@ function ensureOpenAIResponsesNonEmptyInput(messages, context) {
1920
2032
  text: " "
1921
2033
  }]));
1922
2034
  }
1923
- function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint") {
2035
+ function buildOpenAIResponsesParams(model, context, options, metadata, replayMode = "checkpoint", retainUserProvenance = false) {
1924
2036
  const payloadPolicy = resolveOpenAIResponsesPayloadPolicy(model, { storeMode: "transport-default" });
1925
2037
  const policyAllowsReplayIds = payloadPolicy.explicitStore !== false && !payloadPolicy.shouldStripStore;
1926
2038
  const replayResponsesItemIds = !usesNativeOpenAICodexResponsesBackend(model) && (options?.replayResponsesItemIds ?? policyAllowsReplayIds);
@@ -1930,7 +2042,8 @@ function buildOpenAIResponsesParams(model, context, options, metadata, replayMod
1930
2042
  replayResponsesItemIds,
1931
2043
  authProfileId: options?.authProfileId,
1932
2044
  sessionId: options?.sessionId,
1933
- replayMode
2045
+ replayMode,
2046
+ retainUserProvenance
1934
2047
  });
1935
2048
  ensureOpenAIResponsesNonEmptyInput(messages, context);
1936
2049
  const cacheRetention = resolveCacheRetention(options?.cacheRetention);
@@ -1947,7 +2060,7 @@ function buildOpenAIResponsesParams(model, context, options, metadata, replayMod
1947
2060
  ...metadata ? { metadata } : {}
1948
2061
  };
1949
2062
  const effectiveMaxTokens = options?.maxTokens || model.maxTokens;
1950
- if (effectiveMaxTokens) params.max_output_tokens = Math.max(effectiveMaxTokens, 16);
2063
+ if (effectiveMaxTokens) params.max_output_tokens = clampMaxTokensToModel(model, Math.max(effectiveMaxTokens, 16));
1951
2064
  if (options?.temperature !== void 0 && supportsOpenAITemperature(model)) params.temperature = options.temperature;
1952
2065
  if (options?.topP !== void 0 && (!isOpenAIGpt6Model(model) || resolveOpenAIThinkingApi(model.api) === "azure-openai-responses")) params.top_p = options.topP;
1953
2066
  if (options?.responseFormat !== void 0) params.text = {
@@ -1992,26 +2105,31 @@ function buildOpenAIResponsesParams(model, context, options, metadata, replayMod
1992
2105
  //#endregion
1993
2106
  //#region packages/ai/src/transports/openai-responses-compact-client.ts
1994
2107
  async function postOpenAIResponsesCompaction(params) {
1995
- const compactInput = typeof params.request.instructions === "string" && params.request.instructions.length > 0 ? [buildOpenAIResponsesCompactSystemMessage(params.model, params.request.instructions), ...params.request.input ?? []] : params.request.input;
2108
+ const instructions = typeof params.request.instructions === "string" && params.request.instructions.length > 0 ? params.request.instructions : void 0;
2109
+ const body = instructions && !supportsNativeOpenAIResponsesEndpoint(params.model) ? {
2110
+ model: params.request.model,
2111
+ input: [buildOpenAIResponsesCompactSystemMessage(params.model, instructions), ...params.request.input ?? []]
2112
+ } : {
2113
+ model: params.request.model,
2114
+ input: params.request.input,
2115
+ ...instructions ? { instructions } : {}
2116
+ };
1996
2117
  const response = await params.client.post("/responses/compact", {
1997
2118
  ...buildOpenAISdkRequestOptions(params.model, params.options?.signal, { timeoutMs: params.options?.timeoutMs }),
1998
- body: {
1999
- model: params.request.model,
2000
- input: compactInput
2001
- }
2119
+ body
2002
2120
  });
2003
- const output = isRecord(response) && Array.isArray(response.output) ? response.output : [];
2004
- const item = output.at(-1);
2005
- const retainedItems = output.slice(0, -1);
2006
- const retainedUserMessageCount = retainedItems.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user" && Array.isArray(candidate.content)).length;
2007
- const inputUserMessageCount = Array.isArray(params.request.input) ? params.request.input.filter((candidate) => isRecord(candidate) && candidate.type === "message" && candidate.role === "user").length : 0;
2008
- const retainedMessagePrefixSupported = supportsNativeOpenAIResponsesEndpoint(params.model);
2009
- const usage = isRecord(response) && isRecord(response.usage) ? response.usage : void 0;
2010
- if (!isRecord(response) || response.object !== "response.compaction" || !isOpenAIResponsesCompactionOutput(output, params.model) || retainedItems.length > 0 && (!retainedMessagePrefixSupported || retainedUserMessageCount !== inputUserMessageCount) || !isRecord(item) || item.type !== "compaction" || typeof item.encrypted_content !== "string" || item.encrypted_content.length === 0 || !usage || typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") throw new Error("Responses compact endpoint did not return one trailing compaction item");
2121
+ if (!isRecord(response) || response.object !== "response.compaction") throw new Error("Responses compact endpoint must return a response.compaction object");
2122
+ const output = response.output;
2123
+ if (!isOpenAIResponsesCompactionOutput(output, params.model)) throw new Error(`Responses compact endpoint: ${getOpenAIResponsesCompactionOutputError(output, params.model)}`);
2124
+ const window = output;
2125
+ const item = window.at(-1);
2126
+ if (window.length > 1 && !supportsNativeOpenAIResponsesEndpoint(params.model)) throw new Error("Responses compact endpoint: retained messages require native OpenAI");
2127
+ const usage = isRecord(response.usage) ? response.usage : void 0;
2128
+ if (!usage || typeof usage.input_tokens !== "number" || typeof usage.output_tokens !== "number") throw new Error("Responses compact endpoint: usage must include input_tokens and output_tokens");
2011
2129
  return {
2012
- output,
2130
+ output: window,
2013
2131
  item,
2014
- historyMode: retainedUserMessageCount > 0 ? "retained-users" : "compacted-prefix",
2132
+ historyMode: window.length > 1 ? "retained-users" : "compacted-prefix",
2015
2133
  usage,
2016
2134
  model: params.model,
2017
2135
  replayMetadata: buildOpenAIResponsesReasoningReplayMetadata(params.model, {
@@ -2021,39 +2139,6 @@ async function postOpenAIResponsesCompaction(params) {
2021
2139
  };
2022
2140
  }
2023
2141
  //#endregion
2024
- //#region packages/ai/src/transports/openai-responses-compact-request.ts
2025
- const COMPACT_REQUEST = Symbol("openaiResponsesCompactRequest");
2026
- function claimResponsesCompactRequest(options) {
2027
- const controller = options ? Reflect.get(options, COMPACT_REQUEST) : void 0;
2028
- if (controller?.claimed === false) {
2029
- controller.claimed = true;
2030
- return controller;
2031
- }
2032
- }
2033
- /** Run a compact-endpoint request through the session's prepared stream stack. */
2034
- async function requestPreparedOpenAIResponsesCompaction(streamFn, model, context, options) {
2035
- const preparedOptions = { ...options };
2036
- let resolveResult;
2037
- let rejectResult;
2038
- const result = new Promise((resolve, reject) => {
2039
- resolveResult = resolve;
2040
- rejectResult = reject;
2041
- });
2042
- const controller = {
2043
- claimed: false,
2044
- resolve: resolveResult,
2045
- reject: rejectResult
2046
- };
2047
- Reflect.set(preparedOptions, COMPACT_REQUEST, controller);
2048
- const stream = await Promise.resolve(streamFn(model, context, preparedOptions));
2049
- if (!controller.claimed) throw new Error("Prepared stream did not reach an OpenAI Responses transport");
2050
- try {
2051
- return await result;
2052
- } finally {
2053
- await stream.result().catch(() => void 0);
2054
- }
2055
- }
2056
- //#endregion
2057
2142
  //#region packages/ai/src/transports/openai-responses-reasoning-state.ts
2058
2143
  /** Save only admitted settings and hashes, never another copy of the conversation. */
2059
2144
  function recordResponsesReasoningState(message, model, identity, request, output) {
@@ -2314,6 +2399,16 @@ function createRecoverableResponsesWebSocketStream(params) {
2314
2399
  const SESSION_WEBSOCKET_CACHE_TTL_MS = 3e5;
2315
2400
  const SESSION_WEBSOCKET_MAX_AGE_MS = 33e5;
2316
2401
  const WEBSOCKET_OPEN_STATE = 1;
2402
+ function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
2403
+ if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
2404
+ if (getAiTransportHost().requiresManagedTransport(model)) return;
2405
+ return supportsNativeOpenAIResponsesEndpoint(model) ? transport : void 0;
2406
+ }
2407
+ function combineWebSocketTimeoutSignal(signal, model, timeoutMs) {
2408
+ const resolvedTimeoutMs = timeoutMs !== void 0 && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : getAiTransportHost().resolveModelRequestTimeoutMs(model);
2409
+ if (resolvedTimeoutMs === void 0 || !Number.isFinite(resolvedTimeoutMs)) return signal;
2410
+ return AbortSignal.any([signal, AbortSignal.timeout(Math.max(1, resolvedTimeoutMs))]);
2411
+ }
2317
2412
  const websocketSessionCache = /* @__PURE__ */ new Map();
2318
2413
  const degradedWebSocketConnections = /* @__PURE__ */ new Map();
2319
2414
  function closeWebSocketSilently(socket, reason = "done") {
@@ -2675,16 +2770,6 @@ function closeOpenAIResponsesWebSocketSessions(sessionId, owner) {
2675
2770
  registerSessionResourceCleanup(closeOpenAIResponsesWebSocketSessions);
2676
2771
  //#endregion
2677
2772
  //#region packages/ai/src/transports/openai-responses-client.ts
2678
- function resolveNativeOpenAIResponsesWebSocketMode(model, transport) {
2679
- if (transport !== "websocket" && transport !== "websocket-cached" && transport !== "auto") return;
2680
- if (getAiTransportHost().requiresManagedTransport(model)) return;
2681
- return supportsNativeOpenAIResponsesEndpoint(model) ? transport : void 0;
2682
- }
2683
- function combineWebSocketTimeoutSignal(signal, model, timeoutMs) {
2684
- const resolvedTimeoutMs = timeoutMs !== void 0 && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : getAiTransportHost().resolveModelRequestTimeoutMs(model);
2685
- if (resolvedTimeoutMs === void 0 || !Number.isFinite(resolvedTimeoutMs)) return signal;
2686
- return AbortSignal.any([signal, AbortSignal.timeout(Math.max(1, resolvedTimeoutMs))]);
2687
- }
2688
2773
  function createOpenAIResponsesClient(model, apiKey, defaultHeaders, fetchOverride) {
2689
2774
  return new OpenAI({
2690
2775
  apiKey,
@@ -2698,7 +2783,9 @@ function createOpenAIResponsesClient(model, apiKey, defaultHeaders, fetchOverrid
2698
2783
  function createResponsesTransportExecutor(config) {
2699
2784
  return (model, context, options) => {
2700
2785
  const responsesOptions = options;
2786
+ const independent = independentResponsesRequest.has(options);
2701
2787
  const compactRequest = claimResponsesCompactRequest(responsesOptions);
2788
+ const v2Compaction = compactRequest?.mode === "v2" ? createResponsesV2CompactionCollector(compactRequest.replayBudget) : void 0;
2702
2789
  const { eventStream, stream } = createWritableTransportEventStream();
2703
2790
  (async () => {
2704
2791
  const output = createAssistantOutput(model, config.outputApi);
@@ -2706,9 +2793,10 @@ function createResponsesTransportExecutor(config) {
2706
2793
  let continuationClaim;
2707
2794
  const requestLifecycle = responsesRequestLifecycle.get(options);
2708
2795
  try {
2796
+ if (compactRequest && requestLifecycle) throw new Error("Provider review continuation cannot replace its reviewed input with compaction");
2709
2797
  const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
2710
- const websocketMode = resolveNativeOpenAIResponsesWebSocketMode(model, responsesOptions?.transport);
2711
- const encodeBody = prepareModelRequestBody(websocketMode || compactRequest ? void 0 : options);
2798
+ const websocketMode = independent ? void 0 : resolveNativeOpenAIResponsesWebSocketMode(model, responsesOptions?.transport);
2799
+ const encodeBody = prepareModelRequestBody(websocketMode || compactRequest?.mode === "endpoint" ? void 0 : options);
2712
2800
  const turnState = resolveProviderTransportTurnState(model, {
2713
2801
  sessionId: options?.sessionId,
2714
2802
  turnId: randomUUID(),
@@ -2720,21 +2808,26 @@ function createResponsesTransportExecutor(config) {
2720
2808
  const websocketTurnHeaders = filterProviderTurnHeadersForExplicitOpencodeSession(model, options, websocketSessionPolicy?.headers);
2721
2809
  const websocketHeaders = websocketMode ? buildOpenAIClientHeaders(model, context, options?.headers, websocketTurnHeaders, options?.sessionId, options?.cacheRetention) : void 0;
2722
2810
  const httpHeaders = buildOpenAIClientHeaders(model, context, options?.headers, httpTurnHeaders, options?.sessionId, options?.cacheRetention);
2723
- const fetchOverride = createResponsesRequestFetch(model, {
2724
- compact: Boolean(compactRequest),
2811
+ const fetchOverride = withOpenAIErrorResponseHook(createResponsesRequestFetch(model, {
2812
+ compact: compactRequest?.mode === "endpoint",
2725
2813
  stream: config.streamRequest,
2726
2814
  lifecycle: requestLifecycle,
2727
2815
  onSseComment: () => notifyLlmRequestActivity(options?.signal, false)
2728
- });
2816
+ }) ?? buildGuardedModelFetch(model), options?.onResponse, model);
2729
2817
  const client = config.createClient(model, apiKey, httpHeaders, fetchOverride);
2730
2818
  const nativeAstra = model.id === "gpt-6-astra" && supportsNativeOpenAIResponsesEndpoint(model);
2731
2819
  const asyncToolExecutionEligible = nativeAstra && options?.asyncToolExecution === true && !responsesOptions?.openclawCodeModeToolSurface;
2732
2820
  const prepareRequest = async (request) => {
2733
2821
  let params = request;
2822
+ const preserveUsers = v2Compaction?.preserveUsers(request.input ?? []);
2734
2823
  const nextParams = await options?.onPayload?.(params, model);
2735
2824
  if (nextParams !== void 0) params = nextParams;
2825
+ if (preserveUsers) params = {
2826
+ ...params,
2827
+ input: preserveUsers(params.input ?? [])
2828
+ };
2736
2829
  params = sanitizeOpenAICodexResponsesParams(model, params);
2737
- params = sanitizeResponsesImagePayload(params);
2830
+ params = v2Compaction ? v2Compaction.sanitizeImages(params) : sanitizeResponsesImagePayload(params);
2738
2831
  if (responsesOptions?.openclawCodeModeToolSurface === true) {
2739
2832
  const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
2740
2833
  const allowedHostedToolTypes = responsesOptions?.openclawCodeModeAllowedHostedToolTypes;
@@ -2748,12 +2841,16 @@ function createResponsesTransportExecutor(config) {
2748
2841
  async: true
2749
2842
  } : tool);
2750
2843
  }
2844
+ if (v2Compaction) {
2845
+ params.input = [...params.input ?? [], { type: "compaction_trigger" }];
2846
+ v2Compaction.assertReplayFits(params.input, model);
2847
+ }
2751
2848
  return params;
2752
2849
  };
2753
- const buildRequest = (replayMode, requestContext = context) => prepareRequest(config.buildRequest(model, requestContext, responsesOptions, turnState?.metadata, replayMode));
2850
+ const buildRequest = (replayMode, requestContext = context) => prepareRequest(config.buildRequest(model, requestContext, responsesOptions, turnState?.metadata, replayMode, Boolean(v2Compaction)));
2754
2851
  let params = await buildRequest("checkpoint");
2755
- const asyncTools = asyncToolExecutionEligible && params.model === "gpt-6-astra" && params.multi_agent?.enabled !== true;
2756
- if (compactRequest) {
2852
+ const asyncTools = !compactRequest && asyncToolExecutionEligible && params.model === "gpt-6-astra" && params.multi_agent?.enabled !== true;
2853
+ if (compactRequest?.mode === "endpoint") {
2757
2854
  const compacted = await postOpenAIResponsesCompaction({
2758
2855
  client,
2759
2856
  model,
@@ -2763,6 +2860,7 @@ function createResponsesTransportExecutor(config) {
2763
2860
  output.usage.input = compacted.usage.input_tokens;
2764
2861
  output.usage.output = compacted.usage.output_tokens;
2765
2862
  output.usage.totalTokens = compacted.usage.input_tokens + compacted.usage.output_tokens;
2863
+ output.usage.usageReport = { state: "available" };
2766
2864
  compactRequest.resolve(compacted);
2767
2865
  finalizeTransportStream({
2768
2866
  stream,
@@ -2772,16 +2870,17 @@ function createResponsesTransportExecutor(config) {
2772
2870
  return;
2773
2871
  }
2774
2872
  const sessionId = options?.sessionId;
2775
- if (config.httpContinuation && !websocketMode && !getAiTransportHost().requiresManagedTransport(model) && (supportsNativeOpenAIResponsesEndpoint(model) || resolveOpenAIResponsesPayloadPolicy(model).explicitContinuationOptIn) && sessionId && (params.store === true || supportsResponsesReasoningUpdate(params)) && !params.previous_response_id) {
2873
+ if (config.httpContinuation && !compactRequest && !independent && !websocketMode && !getAiTransportHost().requiresManagedTransport(model) && (supportsNativeOpenAIResponsesEndpoint(model) || resolveOpenAIResponsesPayloadPolicy(model).explicitContinuationOptIn) && sessionId && (params.store === true || supportsResponsesReasoningUpdate(params)) && !params.previous_response_id) {
2874
+ const restoreRequest = () => restoreResponsesReasoningState(context, model, responsesOptions, params);
2776
2875
  continuationClaim = claimOpenAIResponsesHttpContinuation({
2777
2876
  sessionId,
2778
2877
  apiKey,
2779
2878
  baseUrl: model.baseUrl,
2780
2879
  headers: httpHeaders,
2781
2880
  request: params,
2782
- restoreRequest: () => restoreResponsesReasoningState(context, model, responsesOptions, params)
2881
+ restoreRequest
2783
2882
  });
2784
- if (continuationClaim) params = continuationClaim.fullRequest;
2883
+ params = continuationClaim?.fullRequest ?? restoreRequest();
2785
2884
  }
2786
2885
  const observePrompt = createResponsesPromptEgressObserver(responsesOptions, context.systemPrompt);
2787
2886
  const requestStartedAt = Date.now();
@@ -2829,7 +2928,7 @@ function createResponsesTransportExecutor(config) {
2829
2928
  continuationBaseline = attempt.request.previous_response_id ? params : attempt.request;
2830
2929
  const trackedResponseStream = responseModelTracker.track(response, rawResponseStream);
2831
2930
  return withProviderResponseHook({
2832
- stream: observeResponsesStream(trackedResponseStream, model, requestStartedAt),
2931
+ stream: observeResponsesStream(v2Compaction ? v2Compaction.observe(trackedResponseStream) : trackedResponseStream, model, requestStartedAt, attempt.request),
2833
2932
  signal: firstEvent.signal,
2834
2933
  abort: firstEvent.abort,
2835
2934
  hook: createOpenAIProviderAcceptanceHook(options, response, model),
@@ -2864,7 +2963,7 @@ function createResponsesTransportExecutor(config) {
2864
2963
  signal: websocketSignal,
2865
2964
  callerSignal: options?.signal,
2866
2965
  degradeCooldownMs: websocketSessionPolicy?.degradeCooldownMs,
2867
- onActiveResponse: nativeAstra && params.model === "gpt-6-astra" ? options?.onActiveResponse : void 0,
2966
+ onActiveResponse: !compactRequest && nativeAstra && params.model === "gpt-6-astra" ? options?.onActiveResponse : void 0,
2868
2967
  steeringInput: (messages) => {
2869
2968
  contextUsageEligible = false;
2870
2969
  return projectResponsesSteeringInput(params, () => buildRequest("checkpoint", {
@@ -2885,7 +2984,7 @@ function createResponsesTransportExecutor(config) {
2885
2984
  transport = "websocket";
2886
2985
  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))))}`);
2887
2986
  responseStream = createRecoverableResponsesWebSocketStream({
2888
- trackedWebSocketStream: responseModelTracker.track(void 0, websocket.stream),
2987
+ trackedWebSocketStream: observeResponsesStream(v2Compaction ? v2Compaction.observe(responseModelTracker.track(void 0, websocket.stream)) : responseModelTracker.track(void 0, websocket.stream), model, requestStartedAt, websocket.request, "websocket"),
2889
2988
  websocket,
2890
2989
  websocketSignal,
2891
2990
  options,
@@ -2911,7 +3010,7 @@ function createResponsesTransportExecutor(config) {
2911
3010
  canRetryIdentityConflict: () => hasOnlyResponsesFunctionTools(transport === "websocket" ? websocketBaseline : continuationBaseline),
2912
3011
  ...config.pricingOptions?.(responsesOptions, model),
2913
3012
  resolveServiceTier: (responseTier, originalTier) => responseTier ?? (requestedTier === "priority" || requestedTier === "default" ? requestedTier : originalTier),
2914
- onServiceTier: (responseTier) => responsesServiceTierObserver.observe(options, requestedTier, responseTier),
3013
+ onServiceTier: (tier, response) => responsesServiceTierObserver.observe(options, requestedTier, tier, response),
2915
3014
  firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
2916
3015
  abortFirstEventStream: firstEvent.abort,
2917
3016
  onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
@@ -2926,7 +3025,25 @@ function createResponsesTransportExecutor(config) {
2926
3025
  finishWebSocket?.();
2927
3026
  if (options?.signal?.aborted) throw transportAbortError(options.signal);
2928
3027
  if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
2929
- const admitted = transport === "websocket" ? websocketBaseline : continuationBaseline;
3028
+ const admitted = !independent && (transport === "websocket" ? websocketBaseline : continuationBaseline);
3029
+ if (v2Compaction && compactRequest) {
3030
+ const compacted = v2Compaction.finish({
3031
+ terminal,
3032
+ input: params.input ?? [],
3033
+ model,
3034
+ options: responsesOptions
3035
+ });
3036
+ compactRequest.resolve({
3037
+ ...compacted,
3038
+ modelUsage: output.usage
3039
+ });
3040
+ finalizeTransportStream({
3041
+ stream,
3042
+ output,
3043
+ signal: options?.signal
3044
+ });
3045
+ return;
3046
+ }
2930
3047
  if (terminal && admitted && supportsNativeOpenAIResponsesEndpoint(model)) recordResponsesReasoningState(output, model, responsesOptions, admitted, terminal.output);
2931
3048
  if (continuationClaim && continuationBaseline && terminal) continuationClaim.commit(continuationBaseline, terminal, dispatchedPreviousResponseId);
2932
3049
  if (terminal && admitted && contextUsageEligible) recordResponsesContextUsage(output, model, responsesOptions, admitted, terminal.output, "transport");
@@ -3158,7 +3275,7 @@ function streamOpenAICompletionsRequest(model, context, options, mode) {
3158
3275
  const { eventStream, stream } = createWritableTransportEventStream();
3159
3276
  (async () => {
3160
3277
  const output = createAssistantOutput(model);
3161
- const provisionalCommentaryTags = /* @__PURE__ */ new Map();
3278
+ let discardCandidate = false;
3162
3279
  let firstEventAbort;
3163
3280
  try {
3164
3281
  const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
@@ -3170,10 +3287,11 @@ function streamOpenAICompletionsRequest(model, context, options, mode) {
3170
3287
  cacheRetention
3171
3288
  } : { mode };
3172
3289
  const { client, sawStreamDONE } = policy.mode === "direct" ? {
3173
- client: createDirectCompletionsClient(model, context, apiKey, resolveProviderSimpleCompletionHeaders(model, options), cacheRetention === "none" ? void 0 : options?.sessionId, policy.compat),
3290
+ client: createDirectCompletionsClient(model, context, apiKey, options, cacheRetention === "none" ? void 0 : options?.sessionId, policy.compat),
3174
3291
  sawStreamDONE: void 0
3175
3292
  } : createManagedCompletionsClient(model, context, options, apiKey, cacheRetention);
3176
- let params = buildOpenAICompletionsRequest(model, context, options, policy);
3293
+ const builtParams = buildOpenAICompletionsRequest(model, context, options, policy);
3294
+ let params = builtParams;
3177
3295
  const encodeBody = prepareModelRequestBody(options);
3178
3296
  const nextParams = await options?.onPayload?.(params, model);
3179
3297
  if (nextParams !== void 0) params = nextParams;
@@ -3185,6 +3303,7 @@ function streamOpenAICompletionsRequest(model, context, options, mode) {
3185
3303
  }
3186
3304
  if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
3187
3305
  }
3306
+ const contextOutputBudget = resolveCompletionsContextOutputBudget(builtParams, params);
3188
3307
  const emitReasoning = mode === "direct" ? directEmitReasoning : shouldEmitOpenAICompletionsReasoning(model, options);
3189
3308
  firstEventAbort = createFirstStreamEventAbortController(options?.signal);
3190
3309
  const requestOptions = mode === "direct" ? {
@@ -3214,27 +3333,42 @@ function streamOpenAICompletionsRequest(model, context, options, mode) {
3214
3333
  signal: firstEventAbort.signal,
3215
3334
  abort: firstEventAbort.abort,
3216
3335
  hook: createOpenAIProviderAcceptanceHook(options, response, model),
3217
- onReady: () => stream.push({
3218
- type: "start",
3219
- partial: output
3220
- })
3336
+ onReady: () => {
3337
+ if (contextOutputBudget === void 0) stream.push({
3338
+ type: "start",
3339
+ partial: output
3340
+ });
3341
+ }
3342
+ });
3343
+ const providerStream = observeOpenAICompletionsProgress(withFirstStreamEventTimeout(hookedResponseStream, {
3344
+ provider: model.provider,
3345
+ api: model.api,
3346
+ model: model.id,
3347
+ timeoutMs: getFirstStreamEventTimeoutMs(options) ?? 0,
3348
+ stage: "completions",
3349
+ abort: firstEventAbort.abort,
3350
+ onTimeout: getFirstStreamEventTimeoutHandler(options),
3351
+ 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."
3352
+ }), options?.signal);
3353
+ const buffered = contextOutputBudget === void 0 ? void 0 : await bufferContextLimitedCompletions(providerStream, options?.signal);
3354
+ discardCandidate = Boolean(buffered?.bounded && (buffered.failed || buffered.finishReason === "length"));
3355
+ if (buffered && !discardCandidate) stream.push({
3356
+ type: "start",
3357
+ partial: output
3221
3358
  });
3222
3359
  const directEvents = mode === "direct" ? createDirectCompletionsEventStream(output, stream) : void 0;
3223
3360
  try {
3224
- await processCompletionsStream(hookedResponseStream, output, model, directEvents?.stream ?? stream, {
3361
+ await processCompletionsStream(buffered?.stream ?? providerStream, output, model, discardCandidate ? { push() {} } : directEvents?.stream ?? stream, {
3225
3362
  ...directEvents ? {
3226
3363
  mode: "direct",
3227
- beforeContentBlock: directEvents.beforeContentBlock,
3228
- provisionalCommentaryTags
3364
+ beforeContentBlock: directEvents.beforeContentBlock
3229
3365
  } : { mode: "managed" },
3230
- signal: options?.signal,
3366
+ signal: discardCandidate ? void 0 : options?.signal,
3231
3367
  emitReasoning,
3232
3368
  strictReasoningTags: reasoningTagTextPolicy.isStrict(options),
3233
- firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
3234
- abortFirstEventStream: firstEventAbort.abort,
3235
- onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
3236
3369
  sawStreamDONE
3237
3370
  });
3371
+ if (discardCandidate) throw new Error(`Context length exceeded: automatic output budget of ${contextOutputBudget} tokens exhausted before completion.`);
3238
3372
  if (directEvents) {
3239
3373
  if (options?.signal?.aborted) throw transportAbortError(options.signal);
3240
3374
  if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage || (output.stopReason === "aborted" ? "Request was aborted" : "Provider returned an invalid tool call"));
@@ -3250,6 +3384,7 @@ function streamOpenAICompletionsRequest(model, context, options, mode) {
3250
3384
  signal: options?.signal
3251
3385
  });
3252
3386
  } catch (error) {
3387
+ if (discardCandidate) output.content = [];
3253
3388
  failTransportStream({
3254
3389
  stream,
3255
3390
  output,
@@ -3257,11 +3392,9 @@ function streamOpenAICompletionsRequest(model, context, options, mode) {
3257
3392
  error,
3258
3393
  cleanup: () => {
3259
3394
  finalizeOpenAICompletionsToolCalls(output, { allowSilentToolCallPromotion: false });
3260
- clearPendingCommentaryText(provisionalCommentaryTags);
3261
- tagUnresolvedTextAsCommentary(output);
3262
3395
  if (mode === "direct") for (const block of output.content) {
3263
3396
  delete block.index;
3264
- delete block.partialArgs;
3397
+ if (block.type === "toolCall") delete block.partialJson;
3265
3398
  delete block.streamIndex;
3266
3399
  }
3267
3400
  }
@@ -3272,7 +3405,7 @@ function streamOpenAICompletionsRequest(model, context, options, mode) {
3272
3405
  })();
3273
3406
  return eventStream;
3274
3407
  }
3275
- function createDirectCompletionsClient(model, context, apiKey, optionsHeaders, sessionId, compat) {
3408
+ function createDirectCompletionsClient(model, context, apiKey, options, sessionId, compat) {
3276
3409
  if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
3277
3410
  const headers = { ...model.headers };
3278
3411
  if (model.provider === "github-copilot") Object.assign(headers, buildCopilotDynamicHeaders(context.messages));
@@ -3284,7 +3417,7 @@ function createDirectCompletionsClient(model, context, apiKey, optionsHeaders, s
3284
3417
  headers["x-session-affinity"] = sessionId;
3285
3418
  }
3286
3419
  }
3287
- return createOpenAIProviderClient(model, apiKey, headers, optionsHeaders);
3420
+ return createOpenAIProviderClient(model, apiKey, headers, resolveProviderSimpleCompletionHeaders(model, options), options?.onResponse);
3288
3421
  }
3289
3422
  function createManagedCompletionsClient(model, context, options, apiKey, cacheRetention) {
3290
3423
  const turnState = resolveProviderTransportTurnState(model, {
@@ -3296,7 +3429,7 @@ function createManagedCompletionsClient(model, context, options, apiKey, cacheRe
3296
3429
  const optionHeaders = resolveOpencodeSessionHeaders(model, options);
3297
3430
  const turnHeaders = filterProviderTurnHeadersForExplicitOpencodeSession(model, options, turnState?.headers);
3298
3431
  const doneDetector = createSseDoneDetector();
3299
- const baseFetch = buildGuardedModelFetch(model, void 0, { onSseComment: () => notifyLlmRequestActivity(options?.signal, false) });
3432
+ const baseFetch = withOpenAIErrorResponseHook(buildGuardedModelFetch(model, void 0, { onSseComment: () => notifyLlmRequestActivity(options?.signal, false) }), options?.onResponse, model);
3300
3433
  const doneDetectingFetch = async (url, init) => {
3301
3434
  const response = await baseFetch(url, init);
3302
3435
  if (!response.body || !response.ok) return response;
@@ -3330,4 +3463,4 @@ function createManagedCompletionsClient(model, context, options, apiKey, cacheRe
3330
3463
  };
3331
3464
  }
3332
3465
  //#endregion
3333
- export { requestPreparedOpenAIResponsesCompaction as a, convertMessages as c, applyCompletionsAnthropicCacheControl as d, hasOpenAICompatibleConversationTurn as f, createDeepSeekTextFilter as h, createOpenAIResponsesTransportStreamFn as i, flattenCompletionMessagesToStringContent as l, resolveMaxTokensParam as m, streamOpenAICompletionsRequest as n, hasResponsesWebSearchTool as o, canonicalizeMaxTokensParam as p, createAzureOpenAIResponsesTransportStreamFn as r, buildOpenAICompletionsParams as s, createOpenAICompletionsTransportStreamFn as t, stripCompletionMessagesToRoleContent as u };
3466
+ export { hasResponsesWebSearchTool as a, flattenCompletionMessagesToStringContent as c, hasOpenAICompatibleConversationTurn as d, canonicalizeMaxTokensParam as f, createOpenAIResponsesTransportStreamFn as i, stripCompletionMessagesToRoleContent as l, createDeepSeekTextFilter as m, streamOpenAICompletionsRequest as n, buildOpenAICompletionsParams as o, resolveMaxTokensParam as p, createAzureOpenAIResponsesTransportStreamFn as r, convertMessages as s, createOpenAICompletionsTransportStreamFn as t, applyCompletionsAnthropicCacheControl as u };