@openclaw/ai 2026.7.2-beta.4 → 2026.7.2-beta.6

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 (68) hide show
  1. package/dist/{anthropic-CrUDIBpM.mjs → anthropic-CH4UUnZr.mjs} +26 -441
  2. package/dist/{anthropic-BoTnz8cv.d.mts → anthropic-SrGtwsJu.d.mts} +27 -1
  3. package/dist/anthropic-usage-DWU-x8MI.mjs +459 -0
  4. package/dist/{api-registry-BMphkFf1.d.mts → api-registry-DlMgPR39.d.mts} +1 -1
  5. package/dist/{azure-openai-responses-CbrpVeEv.mjs → azure-openai-responses-CImcwB83.mjs} +4 -4
  6. package/dist/cache-retention-0x979a5V.mjs +12 -0
  7. package/dist/deferred-event-buffer-DAvyP7qA.mjs +19 -0
  8. package/dist/error-coercion-DgxlWC0n.mjs +15 -0
  9. package/dist/{event-stream-Douf9dob.d.mts → event-stream-YjaPW20U.d.mts} +1 -1
  10. package/dist/event-stream.d.mts +1 -1
  11. package/dist/{github-copilot-headers-BsH5cqGj.mjs → github-copilot-headers-NCJtz9i0.mjs} +1 -12
  12. package/dist/{google-Cs62KA7t.mjs → google-CtSg0iTS.mjs} +3 -3
  13. package/dist/{google-shared-CMLI-tCZ.mjs → google-shared-DNBz5rcD.mjs} +7 -4
  14. package/dist/{google-vertex-B4SD3U0f.mjs → google-vertex-31f1uS9L.mjs} +3 -3
  15. package/dist/{host-WvWBo4h8.d.mts → host-B9GUmcra.d.mts} +2 -2
  16. package/dist/host-Dog2WQiR.mjs +369 -0
  17. package/dist/index.d.mts +7 -7
  18. package/dist/index.mjs +3 -3
  19. package/dist/internal/anthropic.d.mts +16 -62
  20. package/dist/internal/anthropic.mjs +5 -4
  21. package/dist/internal/openai.d.mts +260 -2
  22. package/dist/internal/openai.mjs +7 -5
  23. package/dist/internal/runtime.d.mts +8 -32
  24. package/dist/internal/runtime.mjs +7 -6
  25. package/dist/internal/shared.d.mts +14 -9
  26. package/dist/internal/shared.mjs +6 -2
  27. package/dist/{llm-request-activity-CehVkZP-.mjs → llm-request-activity-BjtkplhG.mjs} +1 -19
  28. package/dist/{mistral-D5Ps7Led.mjs → mistral-CWmpvWYh.mjs} +7 -4
  29. package/dist/number-coercion-DvG7SNMg.mjs +129 -0
  30. package/dist/{openai-chatgpt-responses-h0o5yV8Y.mjs → openai-chatgpt-responses-B84Ibtrd.mjs} +33 -29
  31. package/dist/{openai-completions-exO8NFQf.mjs → openai-completions-DsOxhOD1.mjs} +28 -482
  32. package/dist/openai-completions-compat-DBWjXoMZ.d.mts +43 -0
  33. package/dist/openai-prompt-cache-2uo_1OR1.d.mts +7 -0
  34. package/dist/openai-prompt-cache-mZTCdRPo.mjs +12 -0
  35. package/dist/openai-reasoning-compat-YgeLncHw.mjs +396 -0
  36. package/dist/{openai-responses-BHpmtUKo.mjs → openai-responses-BT7A3sLu.mjs} +7 -5
  37. package/dist/openai-responses-shared-pXl6Wd8S.mjs +392 -0
  38. package/dist/{openai-responses-shared-CzCurmY1.mjs → openai-responses-stream-internal-Cw5txaGW.mjs} +1609 -1037
  39. package/dist/{openai-tool-projection-ITOU9bG1.mjs → openai-tool-projection-OhX64DoP.mjs} +26 -11
  40. package/dist/prompt-cache-stability-Cwcjv_fx.d.mts +13 -0
  41. package/dist/{provider-error-C4VvV_3t.mjs → provider-error-CAEvRjry.mjs} +8 -2
  42. package/dist/provider-options-D8bB3z9b.d.mts +144 -0
  43. package/dist/providers.d.mts +3 -2
  44. package/dist/providers.mjs +10 -9
  45. package/dist/reasoning-tag-text-partitioner-CGDyLWUR.mjs +12209 -0
  46. package/dist/simple-options-9lhRrN73.mjs +50 -0
  47. package/dist/{src-CXno1H5g.mjs → src-QkygScBs.mjs} +23 -4
  48. package/dist/stream-first-event-timeout-BBys9hSb.mjs +86 -0
  49. package/dist/stream-first-event-timeout-DvDeSucC.d.mts +29 -0
  50. package/dist/tls-certificate-errors-DXSpluKI.mjs +93 -0
  51. package/dist/tool-result-text-CTpIRbYd.mjs +225 -0
  52. package/dist/transform-messages-C8mBqZxF.mjs +2 -0
  53. package/dist/transport-stream-shared-D81p90xq.mjs +297 -0
  54. package/dist/transports.d.mts +115 -132
  55. package/dist/transports.mjs +400 -1991
  56. package/dist/{types-AFwwWium.d.mts → types-bzp5k29J.d.mts} +7 -0
  57. package/dist/types.d.mts +5 -5
  58. package/dist/types.mjs +2 -2
  59. package/dist/{validation-sxvxC8J-.d.mts → validation-B-j7cOYp.d.mts} +1 -1
  60. package/dist/validation.d.mts +1 -1
  61. package/package.json +4 -5
  62. package/dist/host-XYGZcgO8.mjs +0 -98
  63. package/dist/model-utils-1GiZ2_rr.mjs +0 -64
  64. package/dist/openai-CoGicoDt.d.mts +0 -332
  65. package/dist/reasoning-tag-text-partitioner-BlVe67g6.mjs +0 -400
  66. package/dist/stream-first-event-timeout-DP4xEyBY.mjs +0 -150
  67. package/dist/system-prompt-cache-boundary-CbHeV4_l.mjs +0 -526
  68. package/npm-shrinkwrap.json +0 -638
@@ -1,52 +1,25 @@
1
1
  import { n as getEnvApiKey } from "./env-api-keys-DrgeBuva.mjs";
2
- import { c as resolveClaudeNativeThinkingLevelMap, d as supportsClaudeNativeMaxEffort, f as supportsClaudeNativeXhighEffort, l as resolveClaudeSonnet5ModelIdentity, u as supportsClaudeAdaptiveThinking } from "./src-CXno1H5g.mjs";
2
+ import { d as resolveClaudeSonnet5ModelIdentity, g as supportsClaudeNativeXhighEffort, h as supportsClaudeNativeMaxEffort, l as resolveClaudeNativeThinkingLevelMap, p as supportsClaudeAdaptiveThinking, u as resolveClaudeOpus5ModelIdentity } from "./src-QkygScBs.mjs";
3
3
  import { r as createAssistantMessageEventStream } from "./event-stream-D8n2uFee.mjs";
4
- import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-XYGZcgO8.mjs";
5
- import { C as isImageWithMediaPayload, T as isRecord, _ as normalizeOptionalString, a as stripSystemPromptCacheBoundary, b as extractToolResultBlockText, d as prepareClaudeSonnet5RequestContext, f as requiresClaudeAdaptiveThinking, g as normalizeLowercaseStringOrEmpty, h as usesClaudeStreamingRefusalContract, i as splitSystemPromptCacheBoundary, l as applyClaudeRequestContract, m as usesClaudeFable5MessagesContract, u as defaultsClaudeAdaptiveThinking, v as readStringValue, w as truncateUtf16Safe, x as extractToolResultText, y as describeToolResultMediaPlaceholder } from "./system-prompt-cache-boundary-CbHeV4_l.mjs";
6
- import { t as sanitizeSurrogates } from "./sanitize-unicode-DT5o51ur.mjs";
7
- import "./internal/shared.mjs";
8
- import { C as normalizeAnthropicInlineContent, S as createAnthropicInlineImageBudget, T as canonicalizeBase64, _ as buildAnthropicServerSideFallbacks, a as readAnthropicPromptUsageSnapshot, b as omitFoundryBearerCredentialHeaders, c as projectAnthropicTools, d as ANTHROPIC_OMITTED_REASONING_TEXT, f as findActiveAnthropicToolTurnAssistantIndex, g as applyAnthropicFallbackBoundary, h as CLAUDE_FABLE_5_FALLBACK_MODEL_COST, l as reconcileAnthropicToolChoice, o as readAnthropicUsageTokenCount, p as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, s as readLastAnthropicIterationUsage, u as resolveOriginalAnthropicToolName, v as readAnthropicFallbackBoundary, w as resolveAnthropicImageMediaType, x as usesFoundryBearerAuth, y as applyAnthropicRefusal } from "./anthropic-CrUDIBpM.mjs";
9
- import { n as calculateCost, r as clampThinkingLevel, t as applyProviderReportedUsageCost } from "./model-utils-1GiZ2_rr.mjs";
10
- import { r as createDeferredEventBuffer, t as notifyLlmRequestActivity } from "./llm-request-activity-CehVkZP-.mjs";
4
+ import { a as applyClaudeRequestContract, c as requiresClaudeAdaptiveThinking, d as usesClaudeStreamingRefusalContract, f as hasNonEmptyString, g as isRecord, h as readStringValue, n as getAiTransportHost, o as defaultsClaudeAdaptiveThinking, p as normalizeLowercaseStringOrEmpty, r as resolveAiTransportHeaderSentinels, s as prepareClaudeNoPrefillRequestContext, u as usesClaudeFable5MessagesContract } from "./host-Dog2WQiR.mjs";
5
+ import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-mZTCdRPo.mjs";
6
+ import { t as resolveCacheRetention } from "./cache-retention-0x979a5V.mjs";
7
+ import { a as isImageWithMediaPayload, d as stripSystemPromptCacheBoundary, m as sortPromptCacheToolsByName, n as extractToolResultBlockText, r as extractToolResultText, t as describeToolResultMediaPlaceholder, u as splitSystemPromptCacheBoundary } from "./tool-result-text-CTpIRbYd.mjs";
8
+ import { a as resolveModelRequestTimeoutMs, c as resolveProviderRequestCapabilities, i as buildGuardedModelFetch, l as resolveProviderRequestPolicyConfig, n as reconcileOpenAICompletionsToolChoice, o as resolveOpenAIStrictToolSetting, r as reconcileOpenAIResponsesToolChoice, s as resolveProviderEndpoint, t as projectOpenAITools, u as transformTransportMessages } from "./openai-tool-projection-OhX64DoP.mjs";
9
+ import { t as toErrorObject } from "./error-coercion-DgxlWC0n.mjs";
10
+ import { S as canonicalizeBase64, _ as omitFoundryBearerCredentialHeaders, a as projectAnthropicTools, b as normalizeAnthropicInlineContent, c as ANTHROPIC_OMITTED_REASONING_TEXT, d as ANTHROPIC_SERVER_SIDE_FALLBACK_BETA, g as applyAnthropicRefusal, h as resolveAnthropicFallbackServingModelCost, i as readLastAnthropicIterationUsage, l as findActiveAnthropicToolTurnAssistantIndex, m as readAnthropicFallbackBoundary, n as readAnthropicPromptUsageSnapshot, o as reconcileAnthropicToolChoice, p as applyAnthropicFallbackBoundary, r as readAnthropicUsageTokenCount, s as resolveOriginalAnthropicToolName, u as ANTHROPIC_SERVER_SIDE_FALLBACKS, v as usesFoundryBearerAuth, x as resolveAnthropicImageMediaType, y as createAnthropicInlineImageBudget } from "./anthropic-usage-DWU-x8MI.mjs";
11
+ import { a as parseStrictPositiveInteger, c as calculateCost, l as clampThinkingLevel, s as applyProviderReportedUsageCost } from "./number-coercion-DvG7SNMg.mjs";
12
+ import { a as resolveOpenAICompletionsCompat, c as clearPendingCommentaryText, i as detectOpenAICompletionsCompat, l as rememberPendingCommentaryTags, n as mapOpenAIStopReason, o as resolveOpenAICompletionsResponseFormat, r as convertMessages, s as shouldOmitOllamaCompatResponseFormat, t as resolveOpenAIReasoningEffortMap, u as tagPendingCommentaryText } from "./openai-reasoning-compat-YgeLncHw.mjs";
13
+ import { t as createDeferredEventBuffer } from "./deferred-event-buffer-DAvyP7qA.mjs";
11
14
  import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
12
- import "./internal/anthropic.mjs";
13
- import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-BlVe67g6.mjs";
14
- import { a as withFirstStreamEventTimeout, c as parseStrictFiniteNumber, i as getFirstStreamEventTimeoutMs, l as parseStrictNonNegativeInteger, o as asFiniteNumberInRange, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController, u as parseStrictPositiveInteger } from "./stream-first-event-timeout-DP4xEyBY.mjs";
15
- import "./internal/runtime.mjs";
16
- import { a as mapOpenAIStopReason, c as rememberPendingCommentaryTags, l as tagPendingCommentaryText, o as resolveOpenAIReasoningEffortMap, s as clearPendingCommentaryText, t as convertMessages } from "./openai-completions-exO8NFQf.mjs";
17
- import { parseRetryAfterHttpDateMs } from "./internal/retry-after.mjs";
18
- import { A as isOpenAIGpt54MiniModel, D as isResponsesTextContentPartType, G as uniqueStrings, I as supportsOpenAIReasoningEffort, M as isOpenAIGpt56Model, N as normalizeOpenAIReasoningEffort, O as isResponsesTextDeltaEventType, P as resolveOpenAIReasoningEffortForModel, _ as readResponsesToolCallItemIdentity, b as resolveResponsesTerminalStopReason, d as normalizeOpenAIStrictToolParameters, g as createResponsesToolCallTracker, j as isOpenAIGpt55Model, k as resolveResponsesMessageSnapshotCollapse, l as findOpenAIStrictToolProjectionDiagnostics, p as resolveOpenAIProjectedToolsStrictToolFlag, v as mapResponsesTerminalUsage, y as readResponsesReasoningTokens } from "./openai-responses-shared-CzCurmY1.mjs";
15
+ import { t as notifyLlmRequestActivity } from "./llm-request-activity-BjtkplhG.mjs";
16
+ import { C as resolveSecretSentinel, S as resolveModelHeaderSentinels$1, T as supportsModelTools, _ as isGoogleGemini3ProModel, a as failTransportStream, c as mergeTransportMetadata, d as transportAbortError, f as MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE, g as isGoogleGemini3FlashModel, h as isCodeModeModelVisibleToolName, i as createWritableTransportEventStream, l as sanitizeNonEmptyTransportPayloadText, m as estimateStringChars, n as coerceTransportToolCallArguments, o as finalizeTransportStream, p as createAbortError$1, r as createEmptyTransportUsage, s as mergeTransportHeaders, t as assignTransportErrorDetails, u as sanitizeTransportPayloadText, v as parseRetryAfterSeconds, w as sha256Hex, y as readResponseTextSnippet } from "./transport-stream-shared-D81p90xq.mjs";
17
+ import { A as normalizeResponsesFailedEvent, B as resolveReplayableResponsesMessageId, C as prepareOpenAIResponsesReasoningItemForReplay, D as applyServiceTierPricing, E as tagOpenAIResponsesReasoningReplayItem, F as summarizeResponsesFailedNoDetailsObservation, G as throwIfModelStreamAborted, H as createModelStreamCooperativeScheduler, I as summarizeResponsesPayload, L as summarizeResponsesTools, M as stringifyRedactedEvent, N as stringifyRedactedPayload, O as buildResponsesFailedNoDetailsObservation, P as summarizeOpenAITransportError, R as AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS, S as isInvalidEncryptedContentError, T as stripResponsesRequestEncryptedContent, U as log, V as GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, W as resolvePromptCacheKey, Y as normalizeOpenAIStrictToolParameters, Z as resolveOpenAIProjectedToolsStrictToolFlag, b as convertResponsesMessages, bt as resolveModelPayloadDebugMode, dt as isOpenAIGpt55Model, ft as isOpenAIGpt56Model, gt as supportsOpenAIReasoningEffort, j as safeDebugValue, k as logResponsesFailedNoDetails, mt as resolveOpenAIReasoningEffortForModel, n as processResponsesStream, o as observeResponsesStream, pt as normalizeOpenAIReasoningEffort, q as findOpenAIStrictToolProjectionDiagnostics, t as ResponsesStreamFailure, ut as isOpenAIGpt54MiniModel, v as buildOpenAIResponsesReasoningReplayMetadata, vt as uniqueStrings, w as resolveAzureOpenAIApiVersion, x as createResponsesStreamWithEncryptedContentRetry, xt as resolveModelSseDebugMode, y as buildResponsesInputMessage, yt as emitModelTransportDebug, z as OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS } from "./openai-responses-stream-internal-Cw5txaGW.mjs";
18
+ import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-CGDyLWUR.mjs";
19
+ import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-BBys9hSb.mjs";
19
20
  import { i as resolveAzureDeploymentNameFromMap, t as isOpenAICompatibleAzureResponsesBaseUrl } from "./azure-openai-responses-client-compat-C7K7QfUE.mjs";
20
- import { a as clampOpenAIPromptCacheKey, n as reconcileOpenAICompletionsToolChoice, r as reconcileOpenAIResponsesToolChoice, t as projectOpenAITools } from "./openai-tool-projection-ITOU9bG1.mjs";
21
- import "./internal/openai.mjs";
22
- import { createHash, randomUUID } from "node:crypto";
21
+ import { randomUUID } from "node:crypto";
23
22
  import OpenAI, { AzureOpenAI } from "openai";
24
- //#region packages/ai/src/transports/host-policy.ts
25
- function buildGuardedModelFetch(model, timeoutMs, options) {
26
- const host = getAiTransportHost();
27
- if (options !== void 0) return host.buildModelFetch(model, timeoutMs, options) ?? globalThis.fetch;
28
- if (timeoutMs !== void 0) return host.buildModelFetch(model, timeoutMs) ?? globalThis.fetch;
29
- return host.buildModelFetch(model) ?? globalThis.fetch;
30
- }
31
- function resolveProviderEndpoint(baseUrl) {
32
- return { endpointClass: getAiTransportHost().resolveProviderEndpointClass(baseUrl) };
33
- }
34
- function resolveProviderRequestCapabilities(input) {
35
- return getAiTransportHost().resolveProviderRequestCapabilities(input);
36
- }
37
- function resolveProviderRequestPolicyConfig(input) {
38
- return { headers: getAiTransportHost().resolveProviderRequestHeaders(input) };
39
- }
40
- function resolveModelRequestTimeoutMs(model, timeoutMs) {
41
- return timeoutMs ?? getAiTransportHost().resolveModelRequestTimeoutMs(model);
42
- }
43
- function resolveOpenAIStrictToolSetting(model, options) {
44
- return getAiTransportHost().resolveOpenAIStrictToolSetting(model, options);
45
- }
46
- function transformTransportMessages(messages, model, normalizeToolCallId, options) {
47
- return getAiTransportHost().transformTransportMessages(messages, model, normalizeToolCallId, options);
48
- }
49
- //#endregion
50
23
  //#region packages/ai/src/transports/anthropic-payload-policy.ts
51
24
  /**
52
25
  * Anthropic-family request payload policy helpers.
@@ -69,7 +42,7 @@ function isLongTtlEligibleEndpoint(baseUrl) {
69
42
  }
70
43
  /** Resolve Anthropic cache-control marker retention for a request endpoint. */
71
44
  function resolveAnthropicEphemeralCacheControl(baseUrl, cacheRetention) {
72
- const retention = cacheRetention ?? (process.env.OPENCLAW_CACHE_RETENTION === "long" ? "long" : "short");
45
+ const retention = resolveCacheRetention(cacheRetention);
73
46
  if (retention === "none") return;
74
47
  const ttl = retention === "long" && (cacheRetention === "long" || isLongTtlEligibleEndpoint(baseUrl)) ? "1h" : void 0;
75
48
  return {
@@ -219,20 +192,6 @@ function applyAnthropicEphemeralCacheControlMarkers(payloadObj, cacheControl = {
219
192
  }
220
193
  }
221
194
  //#endregion
222
- //#region packages/normalization-core/src/error-coercion.ts
223
- /**
224
- * Normalizes an unknown thrown value into an Error. Non-Error objects become
225
- * the `cause` and have their enumerable fields copied so structured details
226
- * (codes, statuses) survive the coercion.
227
- */
228
- function toErrorObject(value, fallbackMessage) {
229
- if (value instanceof Error) return value;
230
- if (typeof value === "string") return new Error(value);
231
- const error = new Error(fallbackMessage, { cause: value });
232
- if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
233
- return error;
234
- }
235
- //#endregion
236
195
  //#region packages/ai/src/transports/json-unsafe-integers.ts
237
196
  /**
238
197
  * JSON parsing helpers that preserve integer literals larger than
@@ -330,270 +289,6 @@ function parseJsonObjectPreservingUnsafeIntegers(value) {
330
289
  return null;
331
290
  }
332
291
  //#endregion
333
- //#region packages/ai/src/transports/transport-utils.ts
334
- const MALFORMED_STREAMING_FRAGMENT_ERROR_MESSAGE = "OpenClaw transport error: malformed_streaming_fragment";
335
- const NON_LATIN_RE = /[\u2E80-\u9FFF\uA000-\uA4FF\uAC00-\uD7AF\uF900-\uFAFF\uFF01-\uFF60\uFFE0-\uFFE6\u{20000}-\u{2FA1F}]/gu;
336
- const CJK_SURROGATE_HIGH_RE = /[\uD840-\uD87E][\uDC00-\uDFFF]/g;
337
- function sha256Hex(value) {
338
- return createHash("sha256").update(value).digest("hex");
339
- }
340
- function sha256HexPrefix(value, length) {
341
- return sha256Hex(value).slice(0, length);
342
- }
343
- function redactIdentifier(value, opts) {
344
- const trimmed = normalizeOptionalString(value);
345
- if (!trimmed) return "-";
346
- return `sha256:${sha256HexPrefix(trimmed, Number.isFinite(opts?.len) ? Math.max(1, Math.floor(opts?.len ?? 12)) : 12)}`;
347
- }
348
- function redactSensitiveText(text, _options) {
349
- return getAiTransportHost().redactToolPayloadText(text);
350
- }
351
- function resolveSecretSentinel(value) {
352
- return getAiTransportHost().resolveSecretSentinel(value);
353
- }
354
- function resolveModelHeaderSentinels$1(model) {
355
- if (!model.headers) return model;
356
- let headers;
357
- for (const [name, value] of Object.entries(model.headers)) {
358
- const resolved = resolveSecretSentinel(value);
359
- if (resolved !== value) {
360
- headers ??= { ...model.headers };
361
- headers[name] = resolved;
362
- }
363
- }
364
- return headers ? {
365
- ...model,
366
- headers
367
- } : model;
368
- }
369
- function createAbortError$1(message, options) {
370
- const error = new Error(message, options);
371
- error.name = "AbortError";
372
- return error;
373
- }
374
- function estimateStringChars(text) {
375
- if (!text) return 0;
376
- const nonLatinCount = (text.match(NON_LATIN_RE) ?? []).length;
377
- return (nonLatinCount === 0 ? text.length : text.length - (text.match(CJK_SURROGATE_HIGH_RE) ?? []).length) + nonLatinCount * 3;
378
- }
379
- function supportsModelTools(model) {
380
- return (model.compat && typeof model.compat === "object" ? model.compat : void 0)?.supportsTools !== false;
381
- }
382
- function isCodeModeModelVisibleToolName(name) {
383
- return name === "exec" || name === "wait" || name === "computer" || name === "image";
384
- }
385
- function isGoogleGemini3Model(modelId, family) {
386
- const normalized = modelId.trim().toLowerCase();
387
- const suffix = family === "pro" ? "pro" : "flash";
388
- return new RegExp(`(?:^|/)gemini-(?:3(?:\\.\\d+)?-${suffix}|${suffix}${family === "flash" ? "(?:-lite)?" : ""}-latest)(?:-|$)`).test(normalized);
389
- }
390
- function isGoogleGemini3ProModel(modelId) {
391
- return isGoogleGemini3Model(modelId, "pro");
392
- }
393
- function isGoogleGemini3FlashModel(modelId) {
394
- return isGoogleGemini3Model(modelId, "flash");
395
- }
396
- function parseRetryAfterSeconds(headers) {
397
- const retryAfterMs = headers.get("retry-after-ms");
398
- if (retryAfterMs) {
399
- const trimmed = retryAfterMs.trim();
400
- if (/^\d+(?:\.\d+)?$/.test(trimmed)) {
401
- const milliseconds = asFiniteNumberInRange(parseStrictFiniteNumber(trimmed), {
402
- min: 0,
403
- max: Number.MAX_SAFE_INTEGER
404
- });
405
- return milliseconds === void 0 ? Number.POSITIVE_INFINITY : milliseconds / 1e3;
406
- }
407
- }
408
- const retryAfter = headers.get("retry-after")?.trim();
409
- if (!retryAfter) return;
410
- if (/^\d+$/.test(retryAfter)) return parseStrictNonNegativeInteger(retryAfter) ?? Number.POSITIVE_INFINITY;
411
- const retryAt = parseRetryAfterHttpDateMs(retryAfter);
412
- return retryAt === void 0 ? void 0 : Math.max(0, (retryAt - Date.now()) / 1e3);
413
- }
414
- async function readChunkWithIdleTimeout(reader, timeoutMs, onIdleTimeout) {
415
- let timer;
416
- try {
417
- return await Promise.race([reader.read(), new Promise((_resolve, reject) => {
418
- timer = setTimeout(() => reject(onIdleTimeout?.({ chunkTimeoutMs: timeoutMs }) ?? /* @__PURE__ */ new Error("Read timed out")), timeoutMs);
419
- })]);
420
- } finally {
421
- if (timer) clearTimeout(timer);
422
- }
423
- }
424
- async function readResponseTextSnippet(response, options) {
425
- const maxBytes = options?.maxBytes ?? 8 * 1024;
426
- const maxChars = options?.maxChars ?? 200;
427
- const reader = response.body?.getReader();
428
- if (!reader) return;
429
- const chunks = [];
430
- let bytes = 0;
431
- let truncated = false;
432
- try {
433
- while (bytes < maxBytes) {
434
- const result = options?.chunkTimeoutMs ? await readChunkWithIdleTimeout(reader, options.chunkTimeoutMs, options.onIdleTimeout) : await reader.read();
435
- if (result.done) break;
436
- if (!result.value?.length) continue;
437
- const remaining = maxBytes - bytes;
438
- chunks.push(result.value.subarray(0, remaining));
439
- bytes += Math.min(result.value.length, remaining);
440
- if (result.value.length >= remaining) {
441
- truncated = true;
442
- await reader.cancel().catch(() => void 0);
443
- break;
444
- }
445
- }
446
- } catch (error) {
447
- await reader.cancel(error).catch(() => void 0);
448
- throw error;
449
- } finally {
450
- reader.releaseLock();
451
- }
452
- const merged = new Uint8Array(bytes);
453
- let offset = 0;
454
- for (const chunk of chunks) {
455
- merged.set(chunk, offset);
456
- offset += chunk.length;
457
- }
458
- const collapsed = new TextDecoder().decode(merged).replace(/\s+/g, " ").trim();
459
- if (!collapsed) return;
460
- if (collapsed.length > maxChars) return `${truncateUtf16Safe(collapsed, maxChars)}…`;
461
- return truncated ? `${collapsed}…` : collapsed;
462
- }
463
- //#endregion
464
- //#region packages/ai/src/transports/transport-stream-shared.ts
465
- const EMPTY_TOOL_RESULT_TEXT = "(no output)";
466
- function sanitizeTransportPayloadText(text) {
467
- if (typeof text !== "string") return "";
468
- return sanitizeSurrogates(text);
469
- }
470
- function sanitizeNonEmptyTransportPayloadText(text, fallback = EMPTY_TOOL_RESULT_TEXT) {
471
- const sanitized = sanitizeTransportPayloadText(text);
472
- return sanitized.trim().length > 0 ? sanitized : fallback;
473
- }
474
- function coerceTransportToolCallArguments(argumentsValue) {
475
- if (argumentsValue && typeof argumentsValue === "object" && !Array.isArray(argumentsValue)) return argumentsValue;
476
- if (typeof argumentsValue === "string") try {
477
- const parsed = JSON.parse(argumentsValue);
478
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
479
- } catch {}
480
- return {};
481
- }
482
- function mergeTransportHeaders(...headerSources) {
483
- const merged = {};
484
- for (const headers of headerSources) if (headers) Object.assign(merged, headers);
485
- return Object.keys(merged).length > 0 ? merged : void 0;
486
- }
487
- function mergeTransportMetadata(payload, metadata) {
488
- if (!metadata || Object.keys(metadata).length === 0) return payload;
489
- const existingMetadata = payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata) ? payload.metadata : void 0;
490
- return {
491
- ...payload,
492
- metadata: {
493
- ...existingMetadata,
494
- ...metadata
495
- }
496
- };
497
- }
498
- function createEmptyTransportUsage() {
499
- return {
500
- input: 0,
501
- output: 0,
502
- cacheRead: 0,
503
- cacheWrite: 0,
504
- totalTokens: 0,
505
- cost: {
506
- input: 0,
507
- output: 0,
508
- cacheRead: 0,
509
- cacheWrite: 0,
510
- total: 0
511
- }
512
- };
513
- }
514
- function createWritableTransportEventStream() {
515
- const eventStream = createAssistantMessageEventStream();
516
- return {
517
- eventStream,
518
- stream: eventStream
519
- };
520
- }
521
- function finalizeTransportStream(params) {
522
- const { stream, output, signal } = params;
523
- if (signal?.aborted) throw new Error("Request was aborted");
524
- if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
525
- stream.push({
526
- type: "done",
527
- reason: output.stopReason,
528
- message: output
529
- });
530
- stream.end();
531
- }
532
- function readStringLikeProperty(value, key) {
533
- if (!value || typeof value !== "object") return;
534
- const raw = value[key];
535
- if (typeof raw === "string") return raw.trim() || void 0;
536
- if (typeof raw === "number" && Number.isFinite(raw)) return String(raw);
537
- }
538
- function readObjectProperty(value, key) {
539
- if (!value || typeof value !== "object") return;
540
- const raw = value[key];
541
- return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : void 0;
542
- }
543
- function stringifyErrorBody(value) {
544
- if (typeof value === "string") return value;
545
- if (value === void 0 || value === null) return;
546
- try {
547
- return JSON.stringify(value);
548
- } catch {
549
- return;
550
- }
551
- }
552
- function stringifyTransportErrorMessage(value) {
553
- if (value instanceof Error) return value.message;
554
- const encoded = stringifyErrorBody(value);
555
- if (encoded !== void 0) return encoded;
556
- try {
557
- return String(value);
558
- } catch {
559
- return;
560
- }
561
- }
562
- function normalizeTransportErrorBody(value) {
563
- const text = stringifyErrorBody(value);
564
- if (!text?.trim()) return;
565
- const redacted = redactSensitiveText(text);
566
- return redacted.length > 500 ? `${truncateUtf16Safe(redacted, 499)}…` : redacted;
567
- }
568
- function extractTransportErrorDetails(error) {
569
- const errorObject = error && typeof error === "object" ? error : void 0;
570
- const nestedError = readObjectProperty(errorObject, "error");
571
- const errorCode = readStringLikeProperty(errorObject, "errorCode") ?? readStringLikeProperty(errorObject, "code") ?? readStringLikeProperty(nestedError, "code");
572
- const errorType = readStringLikeProperty(errorObject, "errorType") ?? readStringLikeProperty(errorObject, "type") ?? readStringLikeProperty(nestedError, "type");
573
- const errorBody = normalizeTransportErrorBody(readStringLikeProperty(errorObject, "errorBody")) ?? normalizeTransportErrorBody(readStringLikeProperty(errorObject, "body")) ?? normalizeTransportErrorBody(readObjectProperty(errorObject, "body")) ?? normalizeTransportErrorBody(nestedError);
574
- return {
575
- ...errorCode ? { errorCode } : {},
576
- ...errorType ? { errorType } : {},
577
- ...errorBody ? { errorBody } : {}
578
- };
579
- }
580
- function assignTransportErrorDetails(output, error, signal) {
581
- output.stopReason = signal?.aborted ? "aborted" : "error";
582
- output.errorMessage = stringifyTransportErrorMessage(error);
583
- Object.assign(output, extractTransportErrorDetails(error));
584
- }
585
- function failTransportStream(params) {
586
- const { stream, output, signal, error, cleanup } = params;
587
- cleanup?.();
588
- assignTransportErrorDetails(output, error, signal);
589
- stream.push({
590
- type: "error",
591
- reason: output.stopReason,
592
- error: output
593
- });
594
- stream.end();
595
- }
596
- //#endregion
597
292
  //#region packages/ai/src/transports/anthropic-transport-stream.ts
598
293
  /**
599
294
  * Native Anthropic Messages streaming transport.
@@ -712,7 +407,7 @@ function isKimiAnthropicProvider(provider) {
712
407
  * identity) requests are excluded until the beta is verified there.
713
408
  */
714
409
  function useAnthropicServerSideFallback(model) {
715
- return usesClaudeFable5MessagesContract(model) && isDirectAnthropicModel(model);
410
+ return (usesClaudeFable5MessagesContract(model) || resolveClaudeOpus5ModelIdentity(model) !== void 0) && isDirectAnthropicModel(model);
716
411
  }
717
412
  function supportsReasoningContentReplay(model) {
718
413
  return resolveProviderEndpoint(model.baseUrl).endpointClass === "xiaomi-native";
@@ -1200,7 +895,7 @@ async function buildAnthropicParams(model, context, isOAuthToken, options) {
1200
895
  max_tokens: maxTokens,
1201
896
  stream: true
1202
897
  };
1203
- if (!isOAuthToken && useAnthropicServerSideFallback(model)) params.fallbacks = buildAnthropicServerSideFallbacks();
898
+ if (!isOAuthToken && useAnthropicServerSideFallback(model)) params.fallbacks = ANTHROPIC_SERVER_SIDE_FALLBACKS;
1204
899
  if (isOAuthToken) params.system = [
1205
900
  {
1206
901
  type: "text",
@@ -1258,7 +953,7 @@ function resolveAnthropicTransportOptions(model, options, apiKey) {
1258
953
  modelContextWindow: model.contextWindow,
1259
954
  modelMaxTokens: model.maxTokens,
1260
955
  requestedMaxTokens: options?.maxTokens,
1261
- useModelDefault: resolveClaudeSonnet5ModelIdentity(model) !== void 0
956
+ useModelDefault: resolveClaudeSonnet5ModelIdentity(model) !== void 0 || resolveClaudeOpus5ModelIdentity(model) !== void 0
1262
957
  });
1263
958
  if (baseMaxTokens === void 0) throw new Error(`Anthropic Messages transport requires a positive maxTokens value for ${model.provider}/${model.id}`);
1264
959
  const reasoningModelMaxTokens = resolvePositiveAnthropicTokenLimit(model.maxTokens) ?? baseMaxTokens;
@@ -1332,7 +1027,7 @@ function createAnthropicMessagesTransportStreamFn() {
1332
1027
  const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? "";
1333
1028
  if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
1334
1029
  const transportOptions = resolveAnthropicTransportOptions(model, options, apiKey);
1335
- const requestContext = prepareClaudeSonnet5RequestContext(model, context);
1030
+ const requestContext = prepareClaudeNoPrefillRequestContext(model, context);
1336
1031
  const { client, isOAuthToken } = createAnthropicTransportClient({
1337
1032
  model,
1338
1033
  context: requestContext,
@@ -1502,7 +1197,11 @@ function createAnthropicMessagesTransportStreamFn() {
1502
1197
  });
1503
1198
  costModel = {
1504
1199
  ...model,
1505
- cost: CLAUDE_FABLE_5_FALLBACK_MODEL_COST
1200
+ cost: resolveAnthropicFallbackServingModelCost({
1201
+ requestedModelId: model.id,
1202
+ servingModelId: fallbackBoundary.toModel,
1203
+ requestedCost: model.cost
1204
+ })
1506
1205
  };
1507
1206
  calculateCost(costModel, output.usage);
1508
1207
  eventSink.push({
@@ -1779,7 +1478,7 @@ function createAnthropicMessagesTransportStreamFn() {
1779
1478
  }
1780
1479
  }
1781
1480
  if (refusalBuffer && !sawMessageStop) throw new Error("Anthropic stream ended before message_stop");
1782
- if (transportOptions.signal?.aborted) throw new Error("Request was aborted");
1481
+ if (transportOptions.signal?.aborted) throw transportAbortError(transportOptions.signal);
1783
1482
  if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error(output.errorMessage ?? "An unknown error occurred");
1784
1483
  refusalBuffer?.flush();
1785
1484
  if (output.stopReason === "toolUse" || output.content.some((block) => block.type === "toolCall")) tagPendingCommentaryText(output.content);
@@ -1936,44 +1635,6 @@ function canonicalizeMaxTokensParam(params) {
1936
1635
  params.merged.maxTokens = resolved;
1937
1636
  }
1938
1637
  //#endregion
1939
- //#region packages/ai/src/transports/model-transport-debug.ts
1940
- function normalizeEnv(value) {
1941
- return typeof value === "string" ? value.trim().toLowerCase() : "";
1942
- }
1943
- function isTruthyEnv(value) {
1944
- const normalized = normalizeEnv(value);
1945
- return normalized.length > 0 && normalized !== "0" && normalized !== "false" && normalized !== "off" && normalized !== "no";
1946
- }
1947
- /** Resolves model payload debug verbosity from `OPENCLAW_DEBUG_MODEL_PAYLOAD`. */
1948
- function resolveModelPayloadDebugMode(env = process.env) {
1949
- const normalized = normalizeEnv(env.OPENCLAW_DEBUG_MODEL_PAYLOAD);
1950
- if (normalized === "tools" || normalized === "full-redacted") return normalized;
1951
- if (normalized === "summary") return "summary";
1952
- return "off";
1953
- }
1954
- /** Resolves SSE stream debug verbosity from `OPENCLAW_DEBUG_SSE`. */
1955
- function resolveModelSseDebugMode(env = process.env) {
1956
- const normalized = normalizeEnv(env.OPENCLAW_DEBUG_SSE);
1957
- if (normalized === "peek") return "peek";
1958
- if (normalized === "events" || isTruthyEnv(normalized)) return "events";
1959
- return "off";
1960
- }
1961
- /** Returns whether any model transport debug channel is enabled. */
1962
- function isModelTransportDebugEnabled(env = process.env) {
1963
- return isTruthyEnv(env.OPENCLAW_DEBUG_MODEL_TRANSPORT) || resolveModelPayloadDebugMode(env) !== "off" || resolveModelSseDebugMode(env) !== "off" || isTruthyEnv(env.OPENCLAW_DEBUG_CODE_MODE);
1964
- }
1965
- function isModelFetchMetadataMessage(message) {
1966
- return message.startsWith("[model-fetch]");
1967
- }
1968
- /** Emits model-fetch metadata at info level by default; other diagnostics require debug env. */
1969
- function emitModelTransportDebug(log, message) {
1970
- if (isModelFetchMetadataMessage(message) || isModelTransportDebugEnabled()) {
1971
- log.info(message);
1972
- return;
1973
- }
1974
- log.debug(message);
1975
- }
1976
- //#endregion
1977
1638
  //#region packages/ai/src/transports/model-transport-url.ts
1978
1639
  /**
1979
1640
  * Debug formatting helpers for model transport endpoints.
@@ -2004,9 +1665,6 @@ function formatModelTransportDebugBaseUrl(rawUrl) {
2004
1665
  * Some providers reject requests without a non-empty user/assistant turn; this
2005
1666
  * helper checks the loose message payload shape before transport submission.
2006
1667
  */
2007
- function hasNonEmptyString(value) {
2008
- return typeof value === "string" && value.trim().length > 0;
2009
- }
2010
1668
  function hasNonEmptyContentPart(part) {
2011
1669
  if (!part || typeof part !== "object") return false;
2012
1670
  const record = part;
@@ -2036,62 +1694,6 @@ function hasOpenAICompatibleConversationTurn(messages) {
2036
1694
  });
2037
1695
  }
2038
1696
  //#endregion
2039
- //#region packages/ai/src/transports/openai-completions-compat.ts
2040
- function isDefaultRouteProvider(provider, ...ids) {
2041
- return provider !== void 0 && ids.includes(provider);
2042
- }
2043
- /** Resolves default request flags for an OpenAI-compatible completions endpoint. */
2044
- function resolveOpenAICompletionsCompatDefaults(input) {
2045
- const { provider, endpointClass, knownProviderFamily, supportsNativeStreamingUsageCompat = false, supportsOpenAICompletionsStreamingUsageCompat = false, usesExplicitProxyLikeEndpoint = false } = input;
2046
- const isDefaultRoute = endpointClass === "default";
2047
- const usesConfiguredNonOpenAIEndpoint = endpointClass !== "default" && endpointClass !== "openai-public";
2048
- const isMoonshotLike = knownProviderFamily === "moonshot" || knownProviderFamily === "modelstudio" || endpointClass === "moonshot-native" || endpointClass === "modelstudio-native";
2049
- const isModelStudioLike = knownProviderFamily === "modelstudio" || endpointClass === "modelstudio-native" || isDefaultRoute && isDefaultRouteProvider(provider, "dashscope", "modelstudio", "qwen");
2050
- const isZai = endpointClass === "zai-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "zai");
2051
- const isDeepSeek = endpointClass === "deepseek-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "deepseek");
2052
- const isTogether = knownProviderFamily === "together" || isDefaultRoute && isDefaultRouteProvider(input.provider, "together");
2053
- const isXiaomi = endpointClass === "xiaomi-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "xiaomi");
2054
- const isNonStandard = endpointClass === "cerebras-native" || endpointClass === "chutes-native" || endpointClass === "deepseek-native" || endpointClass === "mistral-public" || endpointClass === "opencode-native" || endpointClass === "xai-native" || isXiaomi || isZai || isDefaultRoute && isDefaultRouteProvider(input.provider, "cerebras", "chutes", "deepseek", "opencode", "xai");
2055
- const isOpenRouterLike = input.provider === "openrouter" || endpointClass === "openrouter";
2056
- const isLocalEndpoint = endpointClass === "local";
2057
- const usesMaxTokens = endpointClass === "chutes-native" || endpointClass === "mistral-public" || knownProviderFamily === "mistral" || isZai || isTogether || isDefaultRoute && isDefaultRouteProvider(provider, "chutes");
2058
- return {
2059
- supportsStore: !isNonStandard && knownProviderFamily !== "mistral" && !usesExplicitProxyLikeEndpoint,
2060
- supportsDeveloperRole: !isNonStandard && !isMoonshotLike && !usesConfiguredNonOpenAIEndpoint,
2061
- supportsReasoningEffort: !isZai && !isTogether && knownProviderFamily !== "mistral" && endpointClass !== "xai-native" && !usesExplicitProxyLikeEndpoint,
2062
- supportsUsageInStreaming: supportsOpenAICompletionsStreamingUsageCompat || !isNonStandard && (isLocalEndpoint || !usesConfiguredNonOpenAIEndpoint || supportsNativeStreamingUsageCompat),
2063
- maxTokensField: usesMaxTokens ? "max_tokens" : "max_completion_tokens",
2064
- thinkingFormat: isDeepSeek || isXiaomi ? "deepseek" : isZai ? "zai" : isTogether ? "together" : isOpenRouterLike ? "openrouter" : "openai",
2065
- visibleReasoningDetailTypes: isOpenRouterLike ? ["response.output_text", "response.text"] : [],
2066
- supportsStrictMode: !isZai && !usesConfiguredNonOpenAIEndpoint,
2067
- requiresReasoningContentOnAssistantMessages: isDeepSeek || isXiaomi,
2068
- requiresNonEmptyUserOrAssistantMessage: isModelStudioLike
2069
- };
2070
- }
2071
- function resolveOpenAICompletionsCompatDefaultsFromCapabilities(input) {
2072
- return resolveOpenAICompletionsCompatDefaults(input);
2073
- }
2074
- /** Detects endpoint capabilities and defaults for an OpenAI-completions model. */
2075
- function detectOpenAICompletionsCompat(model, resolveCapabilities = resolveProviderRequestCapabilities) {
2076
- const capabilities = resolveCapabilities({
2077
- provider: model.provider,
2078
- api: "openai-completions",
2079
- baseUrl: model.baseUrl,
2080
- capability: "llm",
2081
- transport: "stream",
2082
- modelId: model.id,
2083
- compat: model.compat && typeof model.compat === "object" ? model.compat : void 0
2084
- });
2085
- return {
2086
- capabilities,
2087
- defaults: resolveOpenAICompletionsCompatDefaultsFromCapabilities({
2088
- provider: model.provider,
2089
- ...capabilities
2090
- })
2091
- };
2092
- }
2093
- if (process.env.VITEST || false) globalThis[Symbol.for("openclaw.openAICompletionsCompatTestApi")] = { resolveOpenAICompletionsCompatDefaults };
2094
- //#endregion
2095
1697
  //#region packages/ai/src/transports/openai-completions-string-content.ts
2096
1698
  /**
2097
1699
  * OpenAI Chat Completions compatibility helpers. Some providers only accept
@@ -2135,33 +1737,60 @@ function stripCompletionMessagesToRoleContent(messages) {
2135
1737
  const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
2136
1738
  const OPENAI_CODEX_RESPONSES_PROVIDERS = /* @__PURE__ */ new Set(["openai"]);
2137
1739
  const loggedOpenAIStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
2138
- function readToolPayloadField$1(record, field) {
1740
+ function readToolPayloadField(record, field) {
2139
1741
  try {
2140
- return record[field];
1742
+ return Object.hasOwn(record, field) ? record[field] : void 0;
2141
1743
  } catch {
2142
1744
  return;
2143
1745
  }
2144
1746
  }
2145
- function transportPayloadToolName(tool) {
1747
+ function readCodeModePayloadToolName(tool) {
2146
1748
  if (!isRecord(tool)) return;
2147
- const name = readToolPayloadField$1(tool, "name");
1749
+ const name = readToolPayloadField(tool, "name");
2148
1750
  if (typeof name === "string") return name;
2149
- const fn = readToolPayloadField$1(tool, "function");
1751
+ const fn = readToolPayloadField(tool, "function");
2150
1752
  if (!isRecord(fn)) return;
2151
- const fnName = readToolPayloadField$1(fn, "name");
1753
+ const fnName = readToolPayloadField(fn, "name");
2152
1754
  return typeof fnName === "string" ? fnName : void 0;
2153
1755
  }
2154
- function enforceCodeModeResponsesToolSurface(payload) {
2155
- if (!isRecord(payload) || !Array.isArray(payload.tools)) return;
2156
- payload.tools = payload.tools.filter((tool) => {
2157
- const name = transportPayloadToolName(tool);
2158
- return typeof name === "string" && isCodeModeModelVisibleToolName(name);
1756
+ function filterCodeModePayloadTools(payload, visibleToolNames) {
1757
+ if (!isRecord(payload)) return;
1758
+ const tools = readToolPayloadField(payload, "tools");
1759
+ if (!Array.isArray(tools)) return;
1760
+ payload.tools = tools.flatMap((tool) => {
1761
+ const name = readCodeModePayloadToolName(tool);
1762
+ if (typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames)) return [tool];
1763
+ if (!isRecord(tool)) return [];
1764
+ const filteredGroups = {};
1765
+ for (const key of ["functionDeclarations", "function_declarations"]) {
1766
+ const declarations = readToolPayloadField(tool, key);
1767
+ if (!Array.isArray(declarations)) continue;
1768
+ const filtered = declarations.filter((declaration) => {
1769
+ const declarationName = readCodeModePayloadToolName(declaration);
1770
+ return typeof declarationName === "string" && isCodeModeModelVisibleToolName(declarationName, visibleToolNames);
1771
+ });
1772
+ if (filtered.length > 0) filteredGroups[key] = filtered;
1773
+ }
1774
+ return Object.keys(filteredGroups).length > 0 ? [filteredGroups] : [];
1775
+ });
1776
+ }
1777
+ function resolveCodeModeResponsesVisibleToolNames(context) {
1778
+ return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
1779
+ }
1780
+ function enforceCodeModeResponsesToolSurface(payload, visibleToolNames) {
1781
+ if (!isRecord(payload)) return;
1782
+ const tools = readToolPayloadField(payload, "tools");
1783
+ if (!Array.isArray(tools)) return;
1784
+ payload.tools = tools.filter((tool) => {
1785
+ const name = readCodeModePayloadToolName(tool);
1786
+ return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames);
2159
1787
  });
2160
1788
  }
2161
- function assertCodeModeResponsesToolSurface(payload) {
2162
- if (!isRecord(payload) || !Array.isArray(payload.tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
2163
- const names = payload.tools.map(transportPayloadToolName).filter((name) => typeof name === "string" && name.length > 0).toSorted((left, right) => left.localeCompare(right));
2164
- if (names.length >= 2 && new Set(names).size === names.length && names.filter((name) => name === "exec").length === 1 && names.filter((name) => name === "wait").length === 1 && names.every(isCodeModeModelVisibleToolName)) return;
1789
+ function assertCodeModeResponsesToolSurface(payload, visibleToolNames) {
1790
+ const tools = isRecord(payload) ? readToolPayloadField(payload, "tools") : void 0;
1791
+ if (!Array.isArray(tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
1792
+ const names = tools.map(readCodeModePayloadToolName).filter((name) => typeof name === "string" && name.length > 0).toSorted((left, right) => left.localeCompare(right));
1793
+ if (names.length >= 2 && names.length === tools.length && new Set(names).size === names.length && names.includes("exec") && names.includes("wait") && names.every((name) => isCodeModeModelVisibleToolName(name, visibleToolNames))) return;
2165
1794
  throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
2166
1795
  }
2167
1796
  function buildOpenAIStrictToolDowngradeDiagnosticKey(diagnostics, context) {
@@ -2248,134 +1877,41 @@ function buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, se
2248
1877
  callerHeaders: Object.keys(callerHeaders).length > 0 ? callerHeaders : void 0,
2249
1878
  precedence: "caller-wins"
2250
1879
  }).headers ?? {};
2251
- if (sessionId && !Object.keys(resolvedHeaders).some((key) => normalizeLowercaseStringOrEmpty(key) === "session_id") && usesNativeOpenAICodexResponsesBackend(model)) resolvedHeaders.session_id = sessionId;
1880
+ if (sessionId && !Object.keys(resolvedHeaders).some((key) => normalizeLowercaseStringOrEmpty(key) === "session_id") && usesNativeOpenAICodexResponsesBackend(model)) resolvedHeaders.session_id = clampOpenAIPromptCacheKey(sessionId) ?? sessionId;
2252
1881
  return resolvedHeaders;
2253
1882
  }
2254
- function resolveOpenAISdkTimeoutMs(model) {
2255
- return resolveModelRequestTimeoutMs(model, void 0);
1883
+ function resolveOpenAISdkTimeoutMs(model, timeoutMs) {
1884
+ return resolveModelRequestTimeoutMs(model, timeoutMs);
2256
1885
  }
2257
1886
  function buildOpenAISdkClientOptions(model) {
2258
1887
  const timeout = resolveOpenAISdkTimeoutMs(model);
2259
1888
  return timeout === void 0 ? {} : { timeout };
2260
1889
  }
2261
1890
  function buildOpenAISdkRequestOptions(model, signal, options) {
2262
- const timeout = resolveOpenAISdkTimeoutMs(model);
1891
+ const timeout = resolveOpenAISdkTimeoutMs(model, options?.timeoutMs);
2263
1892
  const headers = options?.stream === true && usesNativeOpenAICodexResponsesBackend(model) ? { Accept: "text/event-stream" } : void 0;
2264
- if (timeout === void 0 && !signal && !headers) return;
1893
+ if (timeout === void 0 && options?.maxRetries === void 0 && !signal && !headers) return;
2265
1894
  return {
2266
1895
  ...headers ? { headers } : {},
2267
1896
  ...signal ? { signal } : {},
2268
- ...timeout !== void 0 ? { timeout } : {}
2269
- };
2270
- }
2271
- function detectCompat(model) {
2272
- const { defaults } = detectOpenAICompletionsCompat(model);
2273
- return {
2274
- supportsStore: defaults.supportsStore,
2275
- supportsDeveloperRole: defaults.supportsDeveloperRole,
2276
- supportsReasoningEffort: defaults.supportsReasoningEffort,
2277
- reasoningEffortMap: {},
2278
- supportsUsageInStreaming: defaults.supportsUsageInStreaming,
2279
- maxTokensField: defaults.maxTokensField,
2280
- requiresToolResultName: false,
2281
- requiresAssistantAfterToolResult: false,
2282
- requiresThinkingAsText: false,
2283
- thinkingFormat: defaults.thinkingFormat,
2284
- visibleReasoningDetailTypes: defaults.visibleReasoningDetailTypes,
2285
- openRouterRouting: {},
2286
- vercelGatewayRouting: {},
2287
- supportsStrictMode: defaults.supportsStrictMode,
2288
- requiresReasoningContentOnAssistantMessages: defaults.requiresReasoningContentOnAssistantMessages,
2289
- requiresNonEmptyUserOrAssistantMessage: defaults.requiresNonEmptyUserOrAssistantMessage
1897
+ ...timeout !== void 0 ? { timeout } : {},
1898
+ ...options?.maxRetries !== void 0 ? { maxRetries: options.maxRetries } : {}
2290
1899
  };
2291
1900
  }
2292
1901
  function getCompat(model) {
2293
- const detected = detectCompat(model);
1902
+ const resolved = resolveOpenAICompletionsCompat(model);
2294
1903
  const compat = model.compat ?? {};
2295
- const supportsStore = typeof compat.supportsStore === "boolean" ? compat.supportsStore : detected.supportsStore;
2296
- const supportsReasoningEffort = typeof compat.supportsReasoningEffort === "boolean" ? compat.supportsReasoningEffort : detected.supportsReasoningEffort;
2297
1904
  return {
2298
- supportsStore,
2299
- supportsDeveloperRole: compat.supportsDeveloperRole ?? detected.supportsDeveloperRole,
2300
- supportsReasoningEffort,
2301
- reasoningEffortMap: resolveOpenAIReasoningEffortMap(model, detected.reasoningEffortMap),
2302
- supportsUsageInStreaming: compat.supportsUsageInStreaming ?? detected.supportsUsageInStreaming,
2303
- maxTokensField: compat.maxTokensField ?? detected.maxTokensField,
2304
- requiresToolResultName: compat.requiresToolResultName ?? detected.requiresToolResultName,
2305
- requiresAssistantAfterToolResult: compat.requiresAssistantAfterToolResult ?? detected.requiresAssistantAfterToolResult,
2306
- requiresThinkingAsText: compat.requiresThinkingAsText ?? detected.requiresThinkingAsText,
2307
- thinkingFormat: compat.thinkingFormat ?? detected.thinkingFormat,
2308
- openRouterRouting: compat.openRouterRouting ?? {},
2309
- vercelGatewayRouting: compat.vercelGatewayRouting ?? detected.vercelGatewayRouting,
2310
- supportsStrictMode: compat.supportsStrictMode ?? detected.supportsStrictMode,
2311
- supportsPromptCacheKey: compat.supportsPromptCacheKey === true,
2312
- supportsLongCacheRetention: compat.supportsLongCacheRetention !== false,
1905
+ ...resolved,
1906
+ cacheControlFormat: resolved.cacheControlFormat,
1907
+ reasoningEffortMap: resolveOpenAIReasoningEffortMap(model, {}),
1908
+ openRouterRouting: resolved.openRouterRouting ?? {},
1909
+ vercelGatewayRouting: resolved.vercelGatewayRouting,
2313
1910
  requiresStringContent: compat.requiresStringContent ?? false,
2314
- strictMessageKeys: compat.strictMessageKeys === true,
2315
- visibleReasoningDetailTypes: compat.visibleReasoningDetailTypes ?? detected.visibleReasoningDetailTypes,
2316
- requiresReasoningContentOnAssistantMessages: compat.requiresReasoningContentOnAssistantMessages ?? detected.requiresReasoningContentOnAssistantMessages,
2317
- requiresNonEmptyUserOrAssistantMessage: detected.requiresNonEmptyUserOrAssistantMessage
1911
+ strictMessageKeys: compat.strictMessageKeys === true
2318
1912
  };
2319
1913
  }
2320
1914
  //#endregion
2321
- //#region packages/ai/src/transports/openai-transport-shared.ts
2322
- /** Shared options, usage shape, cache identity, ordering, and stream scheduling for OpenAI APIs. */
2323
- const MODEL_STREAM_COOPERATIVE_YIELD_INTERVAL_MS = 12;
2324
- const MODEL_STREAM_COOPERATIVE_YIELD_MAX_EVENTS = 64;
2325
- const GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP = "skip_thought_signature_validator";
2326
- const log = {
2327
- debug(message, data) {
2328
- getAiTransportHost().logDebug("openai-transport", () => ({
2329
- message,
2330
- data
2331
- }));
2332
- },
2333
- info(message, data) {
2334
- getAiTransportHost().logInfo("openai-transport", message, data);
2335
- },
2336
- warn(message, data) {
2337
- getAiTransportHost().logWarn("openai-transport", message, data);
2338
- }
2339
- };
2340
- function throwIfModelStreamAborted(signal) {
2341
- if (signal?.aborted) throw new Error("Request was aborted");
2342
- }
2343
- function createModelStreamCooperativeScheduler(signal) {
2344
- let lastYieldedAt = Date.now();
2345
- let eventsSinceYield = 0;
2346
- return { async afterEvent() {
2347
- throwIfModelStreamAborted(signal);
2348
- eventsSinceYield += 1;
2349
- const now = Date.now();
2350
- if (eventsSinceYield < MODEL_STREAM_COOPERATIVE_YIELD_MAX_EVENTS && now - lastYieldedAt < MODEL_STREAM_COOPERATIVE_YIELD_INTERVAL_MS) return;
2351
- eventsSinceYield = 0;
2352
- lastYieldedAt = now;
2353
- await new Promise((resolve) => {
2354
- setTimeout(resolve, 0);
2355
- });
2356
- throwIfModelStreamAborted(signal);
2357
- } };
2358
- }
2359
- function resolveCacheRetention(cacheRetention) {
2360
- if (cacheRetention === "short" || cacheRetention === "long" || cacheRetention === "none") return cacheRetention;
2361
- if (typeof process !== "undefined" && process.env.OPENCLAW_CACHE_RETENTION === "long") return "long";
2362
- return "short";
2363
- }
2364
- function resolvePromptCacheKey(options, cacheRetention) {
2365
- if (cacheRetention === "none") return;
2366
- return clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId);
2367
- }
2368
- function compareTransportToolText(left, right) {
2369
- const leftText = left ?? "";
2370
- const rightText = right ?? "";
2371
- if (leftText < rightText) return -1;
2372
- if (leftText > rightText) return 1;
2373
- return 0;
2374
- }
2375
- function sortTransportToolsByName(tools) {
2376
- return tools.toSorted((left, right) => compareTransportToolText(left.name, right.name) || compareTransportToolText(left.description, right.description));
2377
- }
2378
- //#endregion
2379
1915
  //#region packages/ai/src/transports/openai-completions-transport.ts
2380
1916
  function hasToolHistory(messages) {
2381
1917
  return messages.some((message) => message.role === "toolResult" || message.role === "assistant" && Array.isArray(message.content) && message.content.some((block) => block.type === "toolCall"));
@@ -2529,13 +2065,17 @@ function createOpenAICompletionsTransportStreamFn() {
2529
2065
  const nextParams = await options?.onPayload?.(params, model);
2530
2066
  if (nextParams !== void 0) params = nextParams;
2531
2067
  if (options?.openclawCodeModeToolSurface === true) {
2532
- enforceCodeModeResponsesToolSurface(params);
2533
- assertCodeModeResponsesToolSurface(params);
2068
+ const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
2069
+ enforceCodeModeResponsesToolSurface(params, visibleToolNames);
2070
+ assertCodeModeResponsesToolSurface(params, visibleToolNames);
2534
2071
  }
2535
2072
  if (getCompat(model).requiresNonEmptyUserOrAssistantMessage) assertOpenAICompletionsPayloadHasConversationTurn(params, model);
2536
2073
  const emitReasoning = shouldEmitOpenAICompletionsReasoning(model, options);
2537
2074
  firstEventAbort = createFirstStreamEventAbortController(options?.signal);
2538
- const responseStream = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal));
2075
+ const responseStream = await client.chat.completions.create(params, buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
2076
+ timeoutMs: options?.timeoutMs,
2077
+ maxRetries: options?.maxRetries
2078
+ }));
2539
2079
  stream.push({
2540
2080
  type: "start",
2541
2081
  partial: output
@@ -2782,6 +2322,7 @@ async function processOpenAICompletionsStream(responseStream, output, model, str
2782
2322
  await cooperativeScheduler.afterEvent();
2783
2323
  continue;
2784
2324
  }
2325
+ notifyLlmRequestActivity(options?.signal);
2785
2326
  const chunk = rawChunk;
2786
2327
  output.responseId ||= chunk.id;
2787
2328
  let hasReasoningUsageActivity = false;
@@ -3288,7 +2829,7 @@ function convertTools(tools, compat, model) {
3288
2829
  });
3289
2830
  return {
3290
2831
  projection,
3291
- tools: sortTransportToolsByName(projection.tools).map((tool) => {
2832
+ tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
3292
2833
  const functionTool = {
3293
2834
  name: tool.name,
3294
2835
  description: tool.description,
@@ -3485,7 +3026,12 @@ function buildOpenAICompletionsParams(model, context, options) {
3485
3026
  }
3486
3027
  if (options?.temperature !== void 0) params.temperature = options.temperature;
3487
3028
  if (options?.topP !== void 0) params.top_p = options.topP;
3488
- if (options?.responseFormat !== void 0) params.response_format = options.responseFormat;
3029
+ const responseFormat = resolveOpenAICompletionsResponseFormat(shouldOmitOllamaCompatResponseFormat({
3030
+ provider: model.provider,
3031
+ baseUrl: model.baseUrl,
3032
+ hasTools: () => Boolean(context.tools?.length)
3033
+ }) ? void 0 : options?.responseFormat, compat.supportsJsonSchemaResponseFormat);
3034
+ if (responseFormat !== void 0) params.response_format = responseFormat;
3489
3035
  if (options?.frequencyPenalty !== void 0) params.frequency_penalty = options.frequencyPenalty;
3490
3036
  if (options?.presencePenalty !== void 0) params.presence_penalty = options.presencePenalty;
3491
3037
  if (options?.seed !== void 0) params.seed = options.seed;
@@ -3784,1443 +3330,96 @@ function applyOpenAIResponsesPayloadPolicy(payloadObj, policy) {
3784
3330
  if (policy.shouldStripInputStatus) stripInputItemStatuses(payloadObj.input);
3785
3331
  }
3786
3332
  //#endregion
3787
- //#region packages/ai/src/transports/openai-responses-replay.ts
3788
- /** Resolves the assistant message id that can be replayed to OpenAI Responses. */
3789
- function resolveReplayableResponsesMessageId(params) {
3790
- if (!params.replayResponsesItemIds) return;
3791
- if (!params.textSignatureId) return params.fallbackOrdinal === 0 ? params.fallbackId : `${params.fallbackId}_${params.fallbackOrdinal}`;
3792
- return params.previousReplayItemWasReasoning ? params.textSignatureId : void 0;
3333
+ //#region packages/ai/src/transports/openai-responses-params-internal.ts
3334
+ function convertResponsesTools(tools, model, options) {
3335
+ const projection = projectOpenAITools(tools);
3336
+ const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
3337
+ transport: "responses",
3338
+ model
3339
+ });
3340
+ return {
3341
+ projection,
3342
+ tools: sortPromptCacheToolsByName(projection.tools).map((tool) => {
3343
+ const result = {
3344
+ type: "function",
3345
+ name: tool.name,
3346
+ description: tool.description,
3347
+ parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
3348
+ };
3349
+ if (strict !== void 0) result.strict = strict;
3350
+ return result;
3351
+ })
3352
+ };
3793
3353
  }
3794
- //#endregion
3795
- //#region packages/ai/src/transports/openai-responses-terminal-outcome.ts
3796
- function readIncompleteReason(response) {
3797
- const details = response?.incomplete_details;
3798
- if (!isRecord(details)) return;
3799
- return typeof details.reason === "string" ? details.reason : void 0;
3800
- }
3801
- /** Record usage, cost and stop reason from a terminal Responses event onto the output. */
3802
- function recordResponsesTerminalOutcome(params) {
3803
- const { response, output, model } = params;
3804
- const usage = response?.usage;
3805
- const mappedUsage = mapResponsesTerminalUsage(usage);
3806
- if (mappedUsage) {
3807
- const reasoningTokens = readResponsesReasoningTokens(usage);
3808
- output.usage = {
3809
- ...mappedUsage,
3810
- ...reasoningTokens === void 0 ? {} : { reasoningTokens },
3811
- cost: {
3812
- input: 0,
3813
- output: 0,
3814
- cacheRead: 0,
3815
- cacheWrite: 0,
3816
- total: 0
3817
- }
3818
- };
3819
- }
3820
- calculateCost(model, output.usage);
3821
- if (params.applyServiceTierPricing) params.applyServiceTierPricing(output.usage, response?.service_tier ?? params.serviceTier);
3822
- const terminal = resolveResponsesTerminalStopReason({
3823
- status: response?.status,
3824
- incompleteReason: readIncompleteReason(response),
3825
- hasToolCall: output.content.some((block) => block.type === "toolCall")
3354
+ function getPromptCacheRetention(baseUrl, cacheRetention) {
3355
+ if (cacheRetention !== "long") return;
3356
+ return baseUrl?.includes("api.openai.com") ? "24h" : void 0;
3357
+ }
3358
+ function resolveOpenAIReasoningEffort(options) {
3359
+ return normalizeOpenAIReasoningEffort(options?.reasoningEffort ?? options?.reasoning ?? "high");
3360
+ }
3361
+ function hasResponsesWebSearchTool(tools) {
3362
+ if (!Array.isArray(tools)) return false;
3363
+ return tools.some((tool) => {
3364
+ if (!isRecord(tool)) return false;
3365
+ if (tool.type === "web_search") return true;
3366
+ if (tool.type === "function" && tool.name === "web_search") return true;
3367
+ const fn = tool.function;
3368
+ return isRecord(fn) && fn.name === "web_search";
3826
3369
  });
3827
- output.stopReason = terminal.stopReason;
3828
- if (terminal.errorMessage) output.errorMessage = terminal.errorMessage;
3829
3370
  }
3830
- //#endregion
3831
- //#region packages/media-core/src/inline-image-data-url.ts
3832
- /** Prefix used to distinguish inline data URLs from remote/local image references. */
3833
- const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
3834
- const IMAGE_SIGNATURES = [
3835
- {
3836
- mime: "image/png",
3837
- matches: (buffer) => buffer.length >= 8 && buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71 && buffer[4] === 13 && buffer[5] === 10 && buffer[6] === 26 && buffer[7] === 10
3838
- },
3839
- {
3840
- mime: "image/jpeg",
3841
- matches: (buffer) => buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255
3842
- },
3843
- {
3844
- mime: "image/webp",
3845
- matches: (buffer) => buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP"
3846
- },
3847
- {
3848
- mime: "image/gif",
3849
- matches: (buffer) => buffer.length >= 6 && (buffer.subarray(0, 6).toString("ascii") === "GIF87a" || buffer.subarray(0, 6).toString("ascii") === "GIF89a")
3850
- },
3851
- {
3852
- mime: "image/bmp",
3853
- matches: (buffer) => buffer.length >= 2 && buffer[0] === 66 && buffer[1] === 77
3371
+ function raiseMinimalReasoningForResponsesWebSearch(params) {
3372
+ if (params.effort !== "minimal" || !hasResponsesWebSearchTool(params.tools)) return params.effort;
3373
+ for (const effort of [
3374
+ "low",
3375
+ "medium",
3376
+ "high"
3377
+ ]) {
3378
+ const resolved = resolveOpenAIReasoningEffortForModel({
3379
+ model: params.model,
3380
+ effort
3381
+ });
3382
+ if (resolved && resolved !== "none" && resolved !== "minimal") return resolved;
3854
3383
  }
3384
+ return params.effort;
3385
+ }
3386
+ const OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS = [
3387
+ "max_output_tokens",
3388
+ "metadata",
3389
+ "prompt_cache_retention",
3390
+ "service_tier",
3391
+ "temperature",
3392
+ "top_p"
3855
3393
  ];
3856
- const HEIC_BRANDS = /* @__PURE__ */ new Set([
3857
- "heic",
3858
- "heix",
3859
- "hevc",
3860
- "hevx",
3861
- "heis",
3862
- "heim",
3863
- "hevm",
3864
- "hevs"
3865
- ]);
3866
- const HEIF_BRANDS = /* @__PURE__ */ new Set(["mif1", "msf1"]);
3867
- const IMAGE_SIGNATURE_PREFIX_BASE64_CHARS = 128;
3868
- const INLINE_IMAGE_DATA_URL_MIMES = /* @__PURE__ */ new Set([
3869
- "image/png",
3870
- "image/jpeg",
3871
- "image/webp",
3872
- "image/gif"
3873
- ]);
3874
- function startsWithDataUrl(value) {
3875
- return value.slice(0, 5).toLowerCase() === INLINE_IMAGE_DATA_URL_PREFIX;
3394
+ function stripOpenAICodexResponsesUnsupportedTextFields(params) {
3395
+ const text = params.text;
3396
+ if (!text || typeof text !== "object" || Array.isArray(text)) return;
3397
+ const sanitizedText = { ...text };
3398
+ delete sanitizedText.format;
3399
+ if (Object.keys(sanitizedText).length > 0) params.text = sanitizedText;
3400
+ else delete params.text;
3876
3401
  }
3877
- function sniffIsoBmffImageMime(buffer) {
3878
- if (buffer.length < 12 || buffer.subarray(4, 8).toString("ascii") !== "ftyp") return;
3879
- const brands = [buffer.subarray(8, 12).toString("ascii")];
3880
- for (let offset = 16; offset + 4 <= buffer.length; offset += 4) brands.push(buffer.subarray(offset, offset + 4).toString("ascii"));
3881
- if (brands.some((brand) => HEIC_BRANDS.has(brand))) return "image/heic";
3882
- if (brands.some((brand) => HEIF_BRANDS.has(brand))) return "image/heif";
3402
+ function sanitizeOpenAICodexResponsesParams(model, params) {
3403
+ if (!usesNativeOpenAICodexResponsesBackend(model)) return params;
3404
+ for (const key of OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS) delete params[key];
3405
+ stripOpenAICodexResponsesUnsupportedTextFields(params);
3406
+ return params;
3883
3407
  }
3884
- /** Sniffs supported inline image formats from decoded bytes. */
3885
- function sniffInlineImageMime(buffer) {
3886
- return IMAGE_SIGNATURES.find((signature) => signature.matches(buffer))?.mime ?? sniffIsoBmffImageMime(buffer);
3408
+ function buildOpenAICodexResponsesInstructions(context) {
3409
+ if (!context.systemPrompt) return;
3410
+ return sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt));
3887
3411
  }
3888
- function isImageMimeType(value) {
3889
- return value.trim().toLowerCase().startsWith("image/");
3890
- }
3891
- /** Canonicalizes trusted inline image base64 and rejects malformed or non-image payloads. */
3892
- function sanitizeInlineImageBase64(params) {
3893
- if (!isImageMimeType(params.mimeType)) return;
3894
- const canonicalPayload = canonicalizeBase64(params.base64);
3895
- if (!canonicalPayload) return;
3896
- const sniffedMimeType = sniffInlineImageMime(Buffer.from(canonicalPayload.slice(0, IMAGE_SIGNATURE_PREFIX_BASE64_CHARS), "base64"));
3897
- if (!sniffedMimeType) return;
3898
- return {
3899
- mimeType: sniffedMimeType,
3900
- base64: canonicalPayload
3901
- };
3902
- }
3903
- function parseInlineImageDataUrl(value) {
3904
- if (!startsWithDataUrl(value)) return {
3905
- metadata: [],
3906
- payload: value
3907
- };
3908
- const commaIndex = value.indexOf(",");
3909
- if (commaIndex < 0) return;
3910
- return {
3911
- metadata: value.slice(5, commaIndex).split(";").map((part) => part.trim()),
3912
- payload: value.slice(commaIndex + 1)
3913
- };
3914
- }
3915
- function metadataAllowsImageBase64(metadata) {
3916
- const [mimeType, ...options] = metadata;
3917
- return mimeType !== void 0 && isImageMimeType(mimeType) && options.some((part) => part.toLowerCase() === "base64");
3918
- }
3919
- function sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, allowedMimes) {
3920
- const parsed = parseInlineImageDataUrl(imageUrl);
3921
- if (!parsed) return;
3922
- if (parsed.metadata.length === 0) return imageUrl;
3923
- if (!metadataAllowsImageBase64(parsed.metadata)) return;
3924
- const [mimeType] = parsed.metadata;
3925
- const sanitized = sanitizeInlineImageBase64({
3926
- mimeType: mimeType ?? "",
3927
- base64: parsed.payload
3928
- });
3929
- if (!sanitized) return;
3930
- if (allowedMimes && !allowedMimes.has(sanitized.mimeType)) return;
3931
- return `data:${sanitized.mimeType};base64,${sanitized.base64}`;
3932
- }
3933
- /** Canonicalizes provider-safe inline image data URLs and rejects unsupported formats. */
3934
- function sanitizeInlineImageDataUrl(imageUrl) {
3935
- return sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, INLINE_IMAGE_DATA_URL_MIMES);
3936
- }
3937
- //#endregion
3938
- //#region packages/ai/src/transports/responses-image-payload-sanitizer.ts
3939
- /**
3940
- * Sanitizes OpenAI Responses payloads before transport. Invalid inline images
3941
- * are replaced with text placeholders so the request remains valid and
3942
- * auditable.
3943
- */
3944
- const IMAGE_OMITTED_TEXT = "omitted image payload: invalid inline image data";
3945
- function invalidSnakeImage() {
3946
- return {
3947
- type: "input_text",
3948
- text: `[${IMAGE_OMITTED_TEXT}]`
3949
- };
3950
- }
3951
- function sanitizeValue(value) {
3952
- if (Array.isArray(value)) return value.map(sanitizeValue);
3953
- if (!isRecord(value)) return value;
3954
- if (value.type === "input_image" && typeof value.image_url === "string") {
3955
- const imageUrl = sanitizeInlineImageDataUrl(value.image_url);
3956
- return imageUrl ? {
3957
- ...value,
3958
- image_url: imageUrl
3959
- } : invalidSnakeImage();
3960
- }
3961
- const next = {};
3962
- for (const [key, child] of Object.entries(value)) next[key] = sanitizeValue(child);
3963
- return next;
3964
- }
3965
- /** Sanitize inline image fields inside a Responses API payload. */
3966
- function sanitizeResponsesImagePayload(params) {
3967
- if (!Array.isArray(params.input)) return params;
3968
- return {
3969
- ...params,
3970
- input: sanitizeValue(params.input)
3971
- };
3972
- }
3973
- //#endregion
3974
- //#region packages/ai/src/transports/openai-responses-transport.ts
3975
- /**
3976
- * OpenAI Responses transport for Azure variants, replay, reasoning, and payload policy.
3977
- */
3978
- const DEFAULT_AZURE_OPENAI_API_VERSION = "preview";
3979
- const OPENAI_CODEX_RESPONSES_EMPTY_INPUT_TEXT = " ";
3980
- const OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS = "Follow the user request.";
3981
- const AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS = 3e4;
3982
- const RESPONSE_FAILED_NO_DETAILS_MESSAGE = "Unknown error (no error details in response)";
3983
- const OPENAI_RESPONSES_REASONING_REPLAY_META_KEY = "__openclaw_replay";
3984
- const OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY = "openclawReasoningReplay";
3985
- const OPENAI_RESPONSES_REPLAY_ITEM_ID_MAX_LENGTH = 64;
3986
- function stringifyUnknown(value, fallback = "") {
3987
- if (typeof value === "string") return value;
3988
- if (typeof value === "number" || typeof value === "boolean") return String(value);
3989
- return fallback;
3990
- }
3991
- function stringifyJsonLike(value, fallback = "") {
3992
- if (typeof value === "string") return value;
3993
- if (value && typeof value === "object") return JSON.stringify(value);
3994
- if (typeof value === "number" || typeof value === "boolean") return String(value);
3995
- return fallback;
3996
- }
3997
- function getServiceTierCostMultiplier(serviceTier) {
3998
- switch (serviceTier) {
3999
- case "flex": return .5;
4000
- case "priority": return 2;
4001
- default: return 1;
4002
- }
4003
- }
4004
- function applyServiceTierPricing(usage, serviceTier) {
4005
- const multiplier = getServiceTierCostMultiplier(serviceTier);
4006
- if (multiplier === 1) return;
4007
- usage.cost.input *= multiplier;
4008
- usage.cost.output *= multiplier;
4009
- usage.cost.cacheRead *= multiplier;
4010
- usage.cost.cacheWrite *= multiplier;
4011
- usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
4012
- }
4013
- function safeDebugValue(value) {
4014
- if (typeof value === "string") return value;
4015
- if (typeof value === "number" || typeof value === "boolean") return String(value);
4016
- if (value === null) return "null";
4017
- if (value === void 0) return "undefined";
4018
- return Array.isArray(value) ? "array" : typeof value;
4019
- }
4020
- function responseInputTextChars(input) {
4021
- if (typeof input === "string") return input.length;
4022
- if (Array.isArray(input)) return input.reduce((total, item) => total + responseInputTextChars(item), 0);
4023
- if (!input || typeof input !== "object") return 0;
4024
- const record = input;
4025
- let total = 0;
4026
- if (typeof record.text === "string") total += record.text.length;
4027
- if (typeof record.content === "string") total += record.content.length;
4028
- else if (Array.isArray(record.content)) total += responseInputTextChars(record.content);
4029
- return total;
4030
- }
4031
- function responseInputRoles(input) {
4032
- if (!Array.isArray(input)) return "";
4033
- const roles = /* @__PURE__ */ new Set();
4034
- for (const item of input) if (item && typeof item === "object") {
4035
- const role = item.role;
4036
- if (typeof role === "string" && role.trim()) roles.add(role.trim());
4037
- }
4038
- return [...roles].toSorted().join(",");
4039
- }
4040
- function readToolPayloadField(record, field) {
4041
- try {
4042
- return record[field];
4043
- } catch {
4044
- return;
4045
- }
4046
- }
4047
- function readResponsesToolDisplayName(tool) {
4048
- if (!tool || typeof tool !== "object") return "";
4049
- const record = tool;
4050
- const name = readToolPayloadField(record, "name");
4051
- if (typeof name === "string") return name;
4052
- const fn = readToolPayloadField(record, "function");
4053
- if (fn && typeof fn === "object") {
4054
- const fnName = readToolPayloadField(fn, "name");
4055
- if (typeof fnName === "string") return fnName;
4056
- }
4057
- const type = readToolPayloadField(record, "type");
4058
- return typeof type === "string" && type !== "function" ? type : "";
4059
- }
4060
- function summarizeResponsesTools(tools) {
4061
- if (!Array.isArray(tools)) return "count=0";
4062
- const names = tools.map(readResponsesToolDisplayName).filter(Boolean);
4063
- const mode = resolveModelPayloadDebugMode();
4064
- const maxNames = mode === "tools" || mode === "full-redacted" ? names.length : 12;
4065
- const label = maxNames >= names.length ? "names" : "sample";
4066
- const shown = names.slice(0, maxNames).join(",");
4067
- return `count=${tools.length}${shown ? ` ${label}=${shown}` : ""}`;
4068
- }
4069
- function stringifyRedactedPayload(value) {
4070
- try {
4071
- const encoded = JSON.stringify(value);
4072
- if (!encoded) return "<empty>";
4073
- const redacted = redactSensitiveText(encoded, { mode: "tools" });
4074
- return redacted.length > 8e3 ? `${truncateUtf16Safe(redacted, 8e3)}…<truncated>` : redacted;
4075
- } catch {
4076
- return "<unserializable>";
4077
- }
4078
- }
4079
- function stringifyRedactedEvent(value) {
4080
- const redacted = stringifyRedactedPayload(value);
4081
- return redacted.length > 2e3 ? `${truncateUtf16Safe(redacted, 2e3)}…<truncated>` : redacted;
4082
- }
4083
- const RESPONSE_FAILED_FAILURE_FIELD_KEYS = [
4084
- "error",
4085
- "incomplete_details",
4086
- "status_details",
4087
- "failure_reason",
4088
- "last_error",
4089
- "provider_error",
4090
- "error_details"
4091
- ];
4092
- function readResponseFailedString(record, key) {
4093
- return stringifyUnknown(record?.[key]);
4094
- }
4095
- function buildResponsesFailedEventSummary(message, responseId, observation) {
4096
- const summary = { message };
4097
- if (responseId) summary.responseId = responseId;
4098
- if (observation) summary.observation = observation;
4099
- return summary;
4100
- }
4101
- function isResponseFailedIdentifierKey(key) {
4102
- const normalized = key.replace(/[-_\s]/g, "").toLowerCase();
4103
- return normalized === "requestid" || normalized === "xrequestid" || normalized === "providerrequestid" || normalized === "providerresponseid" || normalized === "litellmrequestid" || normalized.includes("request") && normalized.endsWith("id") || normalized.includes("provider") && normalized.endsWith("id");
4104
- }
4105
- function collectResponseFailedIdentifierHashes(value, opts = {}) {
4106
- const path = opts.path ?? "";
4107
- const depth = opts.depth ?? 0;
4108
- const identifierKey = opts.identifierKey ?? "";
4109
- const out = opts.out ?? [];
4110
- const seen = opts.seen ?? /* @__PURE__ */ new WeakSet();
4111
- if (out.length >= 12 || depth > 4 || !value || typeof value !== "object") return out;
4112
- if (seen.has(value)) return out;
4113
- seen.add(value);
4114
- if (Array.isArray(value)) {
4115
- for (const [index, item] of value.entries()) {
4116
- if (index >= 8 || out.length >= 12) break;
4117
- const itemString = typeof item === "string" || typeof item === "number" ? String(item).trim() : "";
4118
- if (identifierKey && isResponseFailedIdentifierKey(identifierKey) && itemString) {
4119
- out.push(`${path}[${index}]=${redactIdentifier(itemString, { len: 12 })}`);
4120
- continue;
4121
- }
4122
- collectResponseFailedIdentifierHashes(item, {
4123
- path: `${path}[${index}]`,
4124
- depth: depth + 1,
4125
- identifierKey,
4126
- out,
4127
- seen
4128
- });
4129
- }
4130
- return out;
4131
- }
4132
- for (const [key, child] of Object.entries(value)) {
4133
- if (out.length >= 12) break;
4134
- const childPath = path ? `${path}.${key}` : key;
4135
- const childString = typeof child === "string" || typeof child === "number" ? String(child).trim() : "";
4136
- if (isResponseFailedIdentifierKey(key) && childString) {
4137
- out.push(`${childPath}=${redactIdentifier(childString, { len: 12 })}`);
4138
- continue;
4139
- }
4140
- collectResponseFailedIdentifierHashes(child, {
4141
- path: childPath,
4142
- depth: depth + 1,
4143
- identifierKey: isResponseFailedIdentifierKey(key) ? key : void 0,
4144
- out,
4145
- seen
4146
- });
4147
- }
4148
- return out;
4149
- }
4150
- function redactResponseFailedDiagnosticValue(value, opts = {}) {
4151
- const key = opts.key ?? "";
4152
- const depth = opts.depth ?? 0;
4153
- if (typeof value === "string" || typeof value === "number") return key && isResponseFailedIdentifierKey(key) ? redactIdentifier(String(value), { len: 12 }) : value;
4154
- if (depth > 6 || !value || typeof value !== "object") return value;
4155
- const seen = opts.seen ?? /* @__PURE__ */ new WeakSet();
4156
- if (seen.has(value)) return "<circular>";
4157
- seen.add(value);
4158
- if (Array.isArray(value)) return value.slice(0, 16).map((item) => redactResponseFailedDiagnosticValue(item, {
4159
- key,
4160
- depth: depth + 1,
4161
- seen
4162
- }));
4163
- const out = {};
4164
- for (const [childKey, child] of Object.entries(value)) out[childKey] = redactResponseFailedDiagnosticValue(child, {
4165
- key: childKey,
4166
- depth: depth + 1,
4167
- seen
4168
- });
4169
- return out;
4170
- }
4171
- function buildResponsesFailedFailureFields(response) {
4172
- if (!response) return {};
4173
- const fields = {};
4174
- for (const key of RESPONSE_FAILED_FAILURE_FIELD_KEYS) if (response[key] !== void 0 && response[key] !== null) fields[key] = response[key];
4175
- return fields;
4176
- }
4177
- function buildResponsesFailedNoDetailsObservation(event, model, response = isRecord(event.response) ? event.response : void 0) {
4178
- const failureFields = redactResponseFailedDiagnosticValue(buildResponsesFailedFailureFields(response));
4179
- const metadataKeys = isRecord(response?.metadata) ? Object.keys(response.metadata).toSorted() : [];
4180
- const responsePreview = {
4181
- id: readResponseFailedString(response, "id"),
4182
- status: readResponseFailedString(response, "status"),
4183
- model: readResponseFailedString(response, "model"),
4184
- object: readResponseFailedString(response, "object"),
4185
- failureFields,
4186
- metadataKeys
4187
- };
4188
- return {
4189
- event: "openai_responses_response_failed_without_details",
4190
- provider: model.provider,
4191
- api: model.api,
4192
- transportModel: model.id,
4193
- providerRuntimeFailureKind: "no_error_details",
4194
- responseId: responsePreview.id,
4195
- responseStatus: responsePreview.status,
4196
- responseModel: responsePreview.model,
4197
- responseObject: responsePreview.object,
4198
- metadataKeys,
4199
- requestIdHashes: collectResponseFailedIdentifierHashes(event),
4200
- failureFieldsPreview: stringifyRedactedEvent(failureFields),
4201
- responsePreview: stringifyRedactedEvent(responsePreview)
4202
- };
4203
- }
4204
- function summarizeResponsesFailedNoDetailsObservation(observation) {
4205
- const requestIds = observation.requestIdHashes.join(",");
4206
- const metadataKeys = observation.metadataKeys.join(",");
4207
- return `responseId=${safeDebugValue(observation.responseId || void 0)} responseStatus=${safeDebugValue(observation.responseStatus || void 0)} responseModel=${safeDebugValue(observation.responseModel || void 0)} requestIds=${requestIds || "none"} metadataKeys=${metadataKeys || "none"} failureFields=${observation.failureFieldsPreview}`;
4208
- }
4209
- function normalizeResponsesFailedEvent(event, model) {
4210
- const response = isRecord(event.response) ? event.response : void 0;
4211
- const responseId = readResponseFailedString(response, "id") || void 0;
4212
- const error = isRecord(response?.error) ? response.error : void 0;
4213
- if (error) {
4214
- const code = readResponseFailedString(error, "code").trim();
4215
- const message = readResponseFailedString(error, "message").trim();
4216
- if (code || message) return buildResponsesFailedEventSummary(`${code || "unknown"}: ${message || "no message"}`, responseId);
4217
- }
4218
- const incompleteReason = readResponseFailedString(isRecord(response?.incomplete_details) ? response.incomplete_details : void 0, "reason");
4219
- if (incompleteReason) return buildResponsesFailedEventSummary(`incomplete: ${incompleteReason}`, responseId);
4220
- return buildResponsesFailedEventSummary(RESPONSE_FAILED_NO_DETAILS_MESSAGE, responseId, buildResponsesFailedNoDetailsObservation(event, model, response));
4221
- }
4222
- function logResponsesFailedNoDetails(observation) {
4223
- log.warn(`[responses] response.failed missing error details provider=${observation.provider} api=${observation.api} model=${observation.transportModel} ` + summarizeResponsesFailedNoDetailsObservation(observation), observation);
4224
- }
4225
- function summarizeResponsesPayload(params) {
4226
- if (!params || typeof params !== "object") return "payload=non-object";
4227
- const record = params;
4228
- const input = record.input;
4229
- const reasoning = record.reasoning && typeof record.reasoning === "object" ? record.reasoning : void 0;
4230
- const text = record.text && typeof record.text === "object" ? record.text : void 0;
4231
- const parts = [
4232
- `fields=${Object.keys(record).toSorted().join(",")}`,
4233
- `model=${safeDebugValue(record.model)}`,
4234
- `stream=${safeDebugValue(record.stream)}`,
4235
- `inputItems=${Array.isArray(input) ? input.length : typeof input}`,
4236
- `inputRoles=${responseInputRoles(input) || "none"}`,
4237
- `inputTextChars=${responseInputTextChars(input)}`,
4238
- `tools=${summarizeResponsesTools(record.tools)}`,
4239
- `reasoningEffort=${safeDebugValue(reasoning?.effort)}`,
4240
- `reasoningSummary=${safeDebugValue(reasoning?.summary)}`,
4241
- `textVerbosity=${safeDebugValue(text?.verbosity)}`,
4242
- `serviceTier=${safeDebugValue(record.service_tier)}`,
4243
- `store=${safeDebugValue(record.store)}`,
4244
- `promptCacheKey=${record.prompt_cache_key === void 0 ? "absent" : "present"}`,
4245
- `metadataKeys=${record.metadata && typeof record.metadata === "object" ? Object.keys(record.metadata).toSorted().join(",") : "none"}`
4246
- ];
4247
- if (resolveModelPayloadDebugMode() === "full-redacted") parts.push(`payload=${stringifyRedactedPayload(record)}`);
4248
- return parts.join(" ");
4249
- }
4250
- function summarizeOpenAITransportError(error) {
4251
- if (!error || typeof error !== "object") return `type=${typeof error} message=${safeDebugValue(error)}`;
4252
- const record = error;
4253
- const cause = record.cause && typeof record.cause === "object" ? record.cause : void 0;
4254
- return [
4255
- `name=${safeDebugValue(record.name)}`,
4256
- `status=${safeDebugValue(record.status)}`,
4257
- `code=${safeDebugValue(record.code)}`,
4258
- `type=${safeDebugValue(record.type)}`,
4259
- `causeName=${safeDebugValue(cause?.name)}`,
4260
- `causeCode=${safeDebugValue(cause?.code)}`,
4261
- `message=${error instanceof Error ? error.message : safeDebugValue(error)}`
4262
- ].join(" ");
4263
- }
4264
- function isInvalidEncryptedContentError(error) {
4265
- if (!error || typeof error !== "object") return false;
4266
- const record = error;
4267
- if (record.code === "invalid_encrypted_content" || record.code === "thinking_signature_invalid") return true;
4268
- const message = typeof record.message === "string" ? record.message : "";
4269
- return message.includes("invalid_encrypted_content") || message.includes("thinking_signature_invalid") || record.status === 400 && message.toLowerCase().includes("could not decrypt the provided encrypted_content");
4270
- }
4271
- function stripEncryptedContentFields(value) {
4272
- if (!value || typeof value !== "object") return {
4273
- value,
4274
- changed: false
4275
- };
4276
- if (Array.isArray(value)) {
4277
- let changed = false;
4278
- const next = value.map((item) => {
4279
- const stripped = stripEncryptedContentFields(item);
4280
- changed ||= stripped.changed;
4281
- return stripped.value;
4282
- });
4283
- return changed ? {
4284
- value: next,
4285
- changed: true
4286
- } : {
4287
- value,
4288
- changed: false
4289
- };
4290
- }
4291
- let changed = false;
4292
- const next = {};
4293
- for (const [key, child] of Object.entries(value)) {
4294
- if (key === "encrypted_content") {
4295
- changed = true;
4296
- continue;
4297
- }
4298
- const stripped = stripEncryptedContentFields(child);
4299
- changed ||= stripped.changed;
4300
- next[key] = stripped.value;
4301
- }
4302
- return changed ? {
4303
- value: next,
4304
- changed: true
4305
- } : {
4306
- value,
4307
- changed: false
4308
- };
4309
- }
4310
- function stripResponsesRequestEncryptedContent(params) {
4311
- const stripped = stripEncryptedContentFields(params.input);
4312
- if (!stripped.changed) return params;
4313
- return {
4314
- ...params,
4315
- input: stripped.value
4316
- };
4317
- }
4318
- function hashOptionalReplayContextValue(value) {
4319
- const normalized = value?.trim();
4320
- return normalized ? shortHash(normalized) : void 0;
4321
- }
4322
- function buildOpenAIResponsesReplayContext(model, options) {
4323
- return {
4324
- provider: model.provider,
4325
- api: model.api,
4326
- model: model.id,
4327
- baseUrlHash: hashOptionalReplayContextValue(model.baseUrl),
4328
- sessionHash: hashOptionalReplayContextValue(options?.sessionId),
4329
- authProfileHash: hashOptionalReplayContextValue(options?.authProfileId)
4330
- };
4331
- }
4332
- function buildOpenAIResponsesReasoningReplayMetadata(model, options) {
4333
- return {
4334
- v: 1,
4335
- source: "openai-responses",
4336
- ...buildOpenAIResponsesReplayContext(model, options)
4337
- };
4338
- }
4339
- function tagOpenAIResponsesReasoningReplayItem(item, model, options) {
4340
- if (!("encrypted_content" in item)) return item;
4341
- return {
4342
- ...item,
4343
- [OPENAI_RESPONSES_REASONING_REPLAY_META_KEY]: buildOpenAIResponsesReasoningReplayMetadata(model, options)
4344
- };
4345
- }
4346
- function isOpenAIResponsesReasoningReplayMetadata(value) {
4347
- if (!value || typeof value !== "object") return false;
4348
- const record = value;
4349
- return record.v === 1 && record.source === "openai-responses" && typeof record.provider === "string" && typeof record.api === "string" && typeof record.model === "string" && (record.baseUrlHash === void 0 || typeof record.baseUrlHash === "string") && (record.sessionHash === void 0 || typeof record.sessionHash === "string") && (record.authProfileHash === void 0 || typeof record.authProfileHash === "string");
4350
- }
4351
- function encryptedReasoningReplayMetadataMatches(metadata, context) {
4352
- if (!metadata) return false;
4353
- return metadata.provider === context.provider && metadata.api === context.api && metadata.model === context.model && metadata.baseUrlHash === context.baseUrlHash && metadata.sessionHash === context.sessionHash && metadata.authProfileHash === context.authProfileHash;
4354
- }
4355
- function readOpenAIResponsesReasoningReplayBlockMetadata(block) {
4356
- const value = block[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY];
4357
- return isOpenAIResponsesReasoningReplayMetadata(value) ? value : void 0;
4358
- }
4359
- function normalizeOpenAIResponsesReasoningReplayItem(item) {
4360
- const record = item;
4361
- if (record.type !== "reasoning" || Array.isArray(record.summary)) return item;
4362
- return {
4363
- ...record,
4364
- summary: []
4365
- };
4366
- }
4367
- function prepareOpenAIResponsesReasoningItemForReplay(item, context, blockMetadata) {
4368
- const { [OPENAI_RESPONSES_REASONING_REPLAY_META_KEY]: rawMetadata, ...rest } = item;
4369
- if (!("encrypted_content" in rest)) return normalizeOpenAIResponsesReasoningReplayItem(rest);
4370
- if (encryptedReasoningReplayMetadataMatches(blockMetadata ?? (isOpenAIResponsesReasoningReplayMetadata(rawMetadata) ? rawMetadata : void 0), context)) return normalizeOpenAIResponsesReasoningReplayItem(rest);
4371
- return normalizeOpenAIResponsesReasoningReplayItem(stripEncryptedContentFields(rest).value);
4372
- }
4373
- async function createResponsesStreamWithEncryptedContentRetry(params) {
4374
- try {
4375
- return await params.client.responses.create(params.request, params.requestOptions);
4376
- } catch (error) {
4377
- const retryRequest = stripResponsesRequestEncryptedContent(params.request);
4378
- if (!isInvalidEncryptedContentError(error) || retryRequest === params.request) throw error;
4379
- log.warn(`[responses] retrying without encrypted reasoning content provider=${params.model.provider} api=${params.model.api} model=${params.model.id}`);
4380
- return await params.client.responses.create(retryRequest, params.requestOptions);
4381
- }
4382
- }
4383
- function resolveAzureOpenAIApiVersion(env = process.env) {
4384
- return env.AZURE_OPENAI_API_VERSION?.trim() || DEFAULT_AZURE_OPENAI_API_VERSION;
4385
- }
4386
- function shortHash(value) {
4387
- return sha256HexPrefix(value, 16);
4388
- }
4389
- function normalizeResponsesReplayItemId(id, prefix) {
4390
- if (!id) return;
4391
- if (id.length <= OPENAI_RESPONSES_REPLAY_ITEM_ID_MAX_LENGTH) return id;
4392
- return `${prefix}_${shortHash(id)}`;
4393
- }
4394
- function isSafeResponsesReplayItemId(id) {
4395
- return typeof id === "string" && id.length > 0 && id.length <= OPENAI_RESPONSES_REPLAY_ITEM_ID_MAX_LENGTH;
4396
- }
4397
- function encodeTextSignatureV1(id, phase) {
4398
- return JSON.stringify({
4399
- v: 1,
4400
- id,
4401
- ...phase ? { phase } : {}
4402
- });
4403
- }
4404
- function parseTextSignature(signature) {
4405
- if (!signature) return;
4406
- if (signature.startsWith("{")) try {
4407
- const parsed = JSON.parse(signature);
4408
- if (parsed.v === 1) {
4409
- const id = typeof parsed.id === "string" ? parsed.id : void 0;
4410
- const phase = parsed.phase === "commentary" || parsed.phase === "final_answer" ? parsed.phase : void 0;
4411
- if (id !== void 0 || phase !== void 0) return {
4412
- id,
4413
- phase
4414
- };
4415
- return;
4416
- }
4417
- } catch {}
4418
- return { id: signature };
4419
- }
4420
- function buildResponsesInputMessage(role, content) {
4421
- return {
4422
- type: "message",
4423
- role,
4424
- content
4425
- };
4426
- }
4427
- function convertResponsesMessages(model, context, allowedToolCallProviders, options) {
4428
- const messages = [];
4429
- const shouldReplayReasoningItems = options?.replayReasoningItems ?? true;
4430
- const shouldReplayResponsesItemIds = options?.replayResponsesItemIds ?? true;
4431
- const replayContext = buildOpenAIResponsesReplayContext(model, {
4432
- sessionId: options?.sessionId,
4433
- authProfileId: options?.authProfileId
4434
- });
4435
- const shouldNormalizeSameModelToolCallIds = model.provider === "github-copilot";
4436
- const sanitizeIdPart = (part) => part.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+$/, "");
4437
- const normalizeIdPart = (part) => {
4438
- const sanitized = sanitizeIdPart(part);
4439
- return (sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized).replace(/_+$/, "");
4440
- };
4441
- const buildForeignResponsesItemId = (itemId) => {
4442
- const normalized = `fc_${shortHash(itemId)}`;
4443
- return normalized.length > 64 ? normalized.slice(0, 64) : normalized;
4444
- };
4445
- const buildSameProviderCopilotResponsesItemId = (itemId) => {
4446
- const sanitized = sanitizeIdPart(itemId);
4447
- const candidate = sanitized.startsWith("fc_") ? sanitized : `fc_${sanitized}`;
4448
- return candidate.length > 64 ? buildForeignResponsesItemId(itemId) : candidate;
4449
- };
4450
- const normalizeToolCallId = (id, _targetModel, source) => {
4451
- if (!allowedToolCallProviders.has(model.provider)) return normalizeIdPart(id);
4452
- if (!id.includes("|")) return normalizeIdPart(id);
4453
- const separatorIndex = id.indexOf("|");
4454
- const callId = id.slice(0, separatorIndex);
4455
- const itemId = id.slice(separatorIndex + 1);
4456
- const normalizedCallId = normalizeIdPart(callId);
4457
- let normalizedItemId = source.provider !== model.provider || source.api !== model.api ? buildForeignResponsesItemId(itemId) : model.provider === "github-copilot" ? buildSameProviderCopilotResponsesItemId(itemId) : normalizeIdPart(itemId);
4458
- if (!normalizedItemId.startsWith("fc_")) normalizedItemId = normalizeIdPart(`fc_${normalizedItemId}`);
4459
- return `${normalizedCallId}|${normalizedItemId}`;
4460
- };
4461
- const transformedMessages = transformTransportMessages(context.messages, model, normalizeToolCallId, { normalizeSameModelToolCallIds: shouldNormalizeSameModelToolCallIds });
4462
- if ((options?.includeSystemPrompt ?? true) && context.systemPrompt) messages.push(buildResponsesInputMessage(model.reasoning && options?.supportsDeveloperRole !== false ? "developer" : "system", [{
4463
- type: "input_text",
4464
- text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt))
4465
- }]));
4466
- let msgIndex = 0;
4467
- for (const msg of transformedMessages) {
4468
- if (msg.role === "user") if (typeof msg.content === "string") messages.push(buildResponsesInputMessage("user", [{
4469
- type: "input_text",
4470
- text: sanitizeTransportPayloadText(msg.content)
4471
- }]));
4472
- else {
4473
- const content = msg.content.map((item) => item.type === "text" ? {
4474
- type: "input_text",
4475
- text: sanitizeTransportPayloadText(item.text)
4476
- } : {
4477
- type: "input_image",
4478
- detail: "auto",
4479
- image_url: `data:${item.mimeType};base64,${item.data}`
4480
- }).filter((item) => model.input.includes("image") || item.type !== "input_image");
4481
- if (content.length > 0) messages.push(buildResponsesInputMessage("user", content));
4482
- }
4483
- else if (msg.role === "assistant") {
4484
- const output = [];
4485
- let textFallbackOrdinal = 0;
4486
- let previousReplayItemWasReasoning = false;
4487
- const isDifferentModel = msg.model !== model.id && msg.provider === model.provider && msg.api === model.api;
4488
- for (const block of msg.content) if (block.type === "thinking") {
4489
- if (shouldReplayReasoningItems && block.thinkingSignature && block.thinkingSignature.startsWith("{")) {
4490
- const replayableReasoningItem = prepareOpenAIResponsesReasoningItemForReplay(JSON.parse(block.thinkingSignature), replayContext, readOpenAIResponsesReasoningReplayBlockMetadata(block));
4491
- if (!shouldReplayResponsesItemIds) delete replayableReasoningItem.id;
4492
- if (shouldReplayResponsesItemIds && model.provider === "github-copilot" && !isSafeResponsesReplayItemId(replayableReasoningItem.id)) continue;
4493
- output.push(replayableReasoningItem);
4494
- previousReplayItemWasReasoning = true;
4495
- }
4496
- } else if (block.type === "text") {
4497
- const textSignature = parseTextSignature(block.textSignature);
4498
- let msgId = resolveReplayableResponsesMessageId({
4499
- replayResponsesItemIds: shouldReplayResponsesItemIds,
4500
- textSignatureId: textSignature?.id,
4501
- fallbackId: `msg_${msgIndex}`,
4502
- fallbackOrdinal: textFallbackOrdinal,
4503
- previousReplayItemWasReasoning
4504
- });
4505
- if (!textSignature?.id) textFallbackOrdinal += 1;
4506
- msgId = normalizeResponsesReplayItemId(msgId, "msg");
4507
- const messageItem = {
4508
- type: "message",
4509
- role: "assistant",
4510
- content: [{
4511
- type: "output_text",
4512
- text: sanitizeTransportPayloadText(block.text),
4513
- annotations: []
4514
- }],
4515
- status: "completed",
4516
- ...msgId ? { id: msgId } : {},
4517
- phase: textSignature?.phase
4518
- };
4519
- output.push(messageItem);
4520
- previousReplayItemWasReasoning = false;
4521
- } else if (block.type === "toolCall") {
4522
- const separatorIndex = block.id.indexOf("|");
4523
- const callId = separatorIndex === -1 ? block.id : block.id.slice(0, separatorIndex);
4524
- const itemIdRaw = separatorIndex === -1 ? void 0 : block.id.slice(separatorIndex + 1);
4525
- const itemId = shouldReplayResponsesItemIds && !(isDifferentModel && itemIdRaw?.startsWith("fc_")) ? itemIdRaw : void 0;
4526
- output.push({
4527
- type: "function_call",
4528
- ...itemId ? { id: itemId } : {},
4529
- call_id: callId,
4530
- name: block.name,
4531
- arguments: typeof block.arguments === "string" ? block.arguments : JSON.stringify(block.arguments ?? {})
4532
- });
4533
- previousReplayItemWasReasoning = false;
4534
- }
4535
- if (output.length > 0) messages.push(...output);
4536
- } else if (msg.role === "toolResult") {
4537
- const textResult = extractToolResultText(msg.content);
4538
- const sanitizedTextResult = sanitizeTransportPayloadText(textResult);
4539
- const hasText = sanitizedTextResult.trim().length > 0;
4540
- const mediaPlaceholder = describeToolResultMediaPlaceholder(msg.content);
4541
- const hasImages = msg.content.some(isImageWithMediaPayload);
4542
- const separatorIndex = msg.toolCallId.indexOf("|");
4543
- const callId = separatorIndex === -1 ? msg.toolCallId : msg.toolCallId.slice(0, separatorIndex);
4544
- messages.push({
4545
- type: "function_call_output",
4546
- call_id: callId,
4547
- output: hasImages && model.input.includes("image") ? [...hasText ? [{
4548
- type: "input_text",
4549
- text: sanitizedTextResult
4550
- }] : mediaPlaceholder === "(see attached media)" ? [{
4551
- type: "input_text",
4552
- text: mediaPlaceholder
4553
- }] : [], ...msg.content.filter(isImageWithMediaPayload).map((item) => ({
4554
- type: "input_image",
4555
- detail: "auto",
4556
- image_url: `data:${item.mimeType};base64,${item.data}`
4557
- }))] : sanitizeNonEmptyTransportPayloadText(textResult, mediaPlaceholder ?? "(no output)")
4558
- });
4559
- }
4560
- msgIndex += 1;
4561
- }
4562
- return messages;
4563
- }
4564
- function convertResponsesTools(tools, model, options) {
4565
- const projection = projectOpenAITools(tools);
4566
- const strict = resolveOpenAIStrictToolFlagWithDiagnostics(projection, options?.strict, {
4567
- transport: "responses",
4568
- model
4569
- });
4570
- return {
4571
- projection,
4572
- tools: sortTransportToolsByName(projection.tools).map((tool) => {
4573
- const result = {
4574
- type: "function",
4575
- name: tool.name,
4576
- description: tool.description,
4577
- parameters: normalizeOpenAIStrictToolParameters(tool.parameters, strict === true, model.compat)
4578
- };
4579
- if (strict !== void 0) result.strict = strict;
4580
- return result;
4581
- })
4582
- };
4583
- }
4584
- async function processResponsesStream(openaiStream, output, stream, model, options) {
4585
- const resolveToolCallId = (item, fallbackId) => {
4586
- const callId = stringifyUnknown(item.call_id).trim();
4587
- const itemId = stringifyUnknown(item.id).trim();
4588
- const [fallbackCallId = "", fallbackItemId = ""] = (fallbackId ?? "").split("|");
4589
- const resolvedCallId = callId || fallbackCallId;
4590
- const resolvedItemId = itemId || fallbackItemId;
4591
- if (resolvedCallId) return resolvedItemId ? `${resolvedCallId}|${resolvedItemId}` : resolvedCallId;
4592
- const generatedCallId = `call_${randomUUID().replaceAll("-", "").slice(0, 24)}`;
4593
- return resolvedItemId ? `${generatedCallId}|${resolvedItemId}` : generatedCallId;
4594
- };
4595
- let currentItem = null;
4596
- let currentBlock = null;
4597
- const streamingToolCalls = createResponsesToolCallTracker();
4598
- let lastTextBlock = null;
4599
- let pendingMessageText = null;
4600
- const streamStartedAt = Date.now();
4601
- let eventCount = 0;
4602
- const eventTypes = /* @__PURE__ */ new Map();
4603
- const sseDebugMode = resolveModelSseDebugMode();
4604
- const blockIndex = () => output.content.length - 1;
4605
- const readIdentityValue = (value) => {
4606
- return (typeof value === "string" ? value.trim() : "") || void 0;
4607
- };
4608
- const resolveCompletedToolCallName = (toolCall, value) => {
4609
- const streamedName = readIdentityValue(toolCall?.block.name);
4610
- const completedName = readIdentityValue(value);
4611
- if (streamedName && completedName && streamedName !== completedName) throw new Error(`Responses stream changed tool-call function name from ${streamedName} to ${completedName}`);
4612
- const name = completedName ?? streamedName;
4613
- if (!name) throw new Error("Responses stream completed tool call without a function name");
4614
- return name;
4615
- };
4616
- const appendPendingMessageDelta = (delta) => {
4617
- pendingMessageText = `${pendingMessageText ?? ""}${delta}`;
4618
- const priorText = stringifyUnknown(lastTextBlock?.block.text);
4619
- if (priorText.startsWith(pendingMessageText) || pendingMessageText.startsWith(priorText)) return;
4620
- const phase = currentItem?.type === "message" ? currentItem.phase ?? void 0 : void 0;
4621
- currentBlock = {
4622
- type: "text",
4623
- text: pendingMessageText,
4624
- ...currentItem?.type === "message" && phase ? { textSignature: encodeTextSignatureV1(stringifyUnknown(currentItem.id), phase) } : {}
4625
- };
4626
- output.content.push(currentBlock);
4627
- stream.push({
4628
- type: "text_start",
4629
- contentIndex: blockIndex(),
4630
- partial: output
4631
- });
4632
- stream.push({
4633
- type: "text_delta",
4634
- contentIndex: blockIndex(),
4635
- delta: pendingMessageText
4636
- });
4637
- pendingMessageText = null;
4638
- };
4639
- const appendTerminalResponseTextItem = (item) => {
4640
- const text = readResponsesOutputMessageText(item);
4641
- if (!text) return;
4642
- const phase = item.phase ?? void 0;
4643
- const collapse = resolveResponsesMessageSnapshotCollapse({
4644
- prior: lastTextBlock && {
4645
- text: stringifyUnknown(lastTextBlock.block.text),
4646
- phase: lastTextBlock.phase
4647
- },
4648
- nextText: text,
4649
- nextPhase: phase
4650
- });
4651
- if (collapse.kind === "extend" && lastTextBlock) {
4652
- lastTextBlock.block.text = collapse.text;
4653
- lastTextBlock.block.textSignature = encodeTextSignatureV1(stringifyUnknown(item.id), phase);
4654
- stream.push({
4655
- type: "text_end",
4656
- contentIndex: lastTextBlock.index,
4657
- content: collapse.text,
4658
- partial: output
4659
- });
4660
- return;
4661
- }
4662
- const block = {
4663
- type: "text",
4664
- text,
4665
- textSignature: encodeTextSignatureV1(stringifyUnknown(item.id), phase)
4666
- };
4667
- output.content.push(block);
4668
- lastTextBlock = {
4669
- block,
4670
- index: blockIndex(),
4671
- phase
4672
- };
4673
- stream.push({
4674
- type: "text_start",
4675
- contentIndex: blockIndex(),
4676
- partial: output
4677
- });
4678
- stream.push({
4679
- type: "text_end",
4680
- contentIndex: blockIndex(),
4681
- content: text,
4682
- partial: output
4683
- });
4684
- };
4685
- const appendCompletedResponseToolCallItem = (item) => {
4686
- const args = parseStreamingJson(stringifyJsonLike(item.arguments, "{}"));
4687
- const name = resolveCompletedToolCallName(void 0, item.name);
4688
- const block = {
4689
- type: "toolCall",
4690
- id: resolveToolCallId(item),
4691
- name,
4692
- arguments: args,
4693
- partialJson: stringifyJsonLike(item.arguments, "{}")
4694
- };
4695
- output.content.push(block);
4696
- stream.push({
4697
- type: "toolcall_start",
4698
- contentIndex: blockIndex(),
4699
- partial: output
4700
- });
4701
- stream.push({
4702
- type: "toolcall_end",
4703
- contentIndex: blockIndex(),
4704
- toolCall: {
4705
- type: "toolCall",
4706
- id: block.id,
4707
- name: block.name,
4708
- arguments: args
4709
- },
4710
- partial: output
4711
- });
4712
- };
4713
- const backfillTerminalResponseOutput = (response, backfillOptions) => {
4714
- if (output.content.length > 0 || !Array.isArray(response?.output)) return;
4715
- for (const rawItem of response.output) {
4716
- if (!isRecord(rawItem)) continue;
4717
- if (rawItem.type === "message") {
4718
- appendTerminalResponseTextItem(rawItem);
4719
- continue;
4720
- }
4721
- lastTextBlock = null;
4722
- if (backfillOptions.includeToolCalls && rawItem.type === "function_call") appendCompletedResponseToolCallItem(rawItem);
4723
- }
4724
- };
4725
- const guardedStream = withFirstStreamEventTimeout(openaiStream, {
4726
- provider: model.provider,
4727
- api: model.api,
4728
- model: model.id,
4729
- timeoutMs: options?.firstEventTimeoutMs ?? 0,
4730
- stage: "responses",
4731
- abort: options?.abortFirstEventStream,
4732
- onTimeout: options?.onFirstEventTimeout,
4733
- 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."
4734
- });
4735
- const cooperativeScheduler = createModelStreamCooperativeScheduler(options?.signal);
4736
- for await (const rawEvent of guardedStream) {
4737
- throwIfModelStreamAborted(options?.signal);
4738
- const event = rawEvent;
4739
- const type = stringifyUnknown(event.type);
4740
- eventCount += 1;
4741
- eventTypes.set(type, (eventTypes.get(type) ?? 0) + 1);
4742
- if (eventCount === 1) emitModelTransportDebug(log, `[responses] first_event provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - streamStartedAt} type=${type}`);
4743
- if (sseDebugMode === "peek" && eventCount <= 5) emitModelTransportDebug(log, `[responses] event_peek provider=${model.provider} api=${model.api} model=${model.id} index=${eventCount} type=${type} event=${stringifyRedactedEvent(event)}`);
4744
- if (type === "response.created") output.responseId = stringifyUnknown(event.response?.id);
4745
- else if (type === "response.output_item.added") {
4746
- const item = event.item;
4747
- if (item.type !== "message") {
4748
- lastTextBlock = null;
4749
- pendingMessageText = null;
4750
- }
4751
- if (item.type === "reasoning") {
4752
- currentItem = item;
4753
- currentBlock = {
4754
- type: "thinking",
4755
- thinking: ""
4756
- };
4757
- output.content.push(currentBlock);
4758
- stream.push({
4759
- type: "thinking_start",
4760
- contentIndex: blockIndex(),
4761
- partial: output
4762
- });
4763
- } else if (item.type === "message") {
4764
- currentItem = item;
4765
- if (lastTextBlock) {
4766
- currentBlock = null;
4767
- pendingMessageText = "";
4768
- } else {
4769
- const phase = item.phase ?? void 0;
4770
- currentBlock = {
4771
- type: "text",
4772
- text: "",
4773
- ...phase ? { textSignature: encodeTextSignatureV1(stringifyUnknown(item.id), phase) } : {}
4774
- };
4775
- output.content.push(currentBlock);
4776
- stream.push({
4777
- type: "text_start",
4778
- contentIndex: blockIndex(),
4779
- partial: output
4780
- });
4781
- }
4782
- } else if (item.type === "function_call") {
4783
- const toolCallBlock = {
4784
- type: "toolCall",
4785
- id: resolveToolCallId(item),
4786
- name: readIdentityValue(item.name) ?? "",
4787
- arguments: {},
4788
- partialJson: stringifyJsonLike(item.arguments)
4789
- };
4790
- const contentIndex = output.content.length;
4791
- const toolCallState = {
4792
- block: toolCallBlock,
4793
- contentIndex,
4794
- argumentStreamReliable: true,
4795
- ...readResponsesToolCallItemIdentity(item)
4796
- };
4797
- streamingToolCalls.register(event, toolCallState);
4798
- currentItem = item;
4799
- currentBlock = toolCallBlock;
4800
- output.content.push(toolCallBlock);
4801
- stream.push({
4802
- type: "toolcall_start",
4803
- contentIndex,
4804
- partial: output
4805
- });
4806
- }
4807
- } else if (type === "response.reasoning_summary_text.delta") {
4808
- if (currentItem?.type === "reasoning" && currentBlock?.type === "thinking") {
4809
- currentBlock.thinking = `${stringifyUnknown(currentBlock.thinking)}${stringifyUnknown(event.delta)}`;
4810
- stream.push({
4811
- type: "thinking_delta",
4812
- contentIndex: blockIndex(),
4813
- delta: stringifyUnknown(event.delta),
4814
- partial: output
4815
- });
4816
- }
4817
- } else if (isResponsesTextDeltaEventType(type) || type === "response.refusal.delta") {
4818
- if (currentItem?.type === "message") {
4819
- if (pendingMessageText !== null) appendPendingMessageDelta(stringifyUnknown(event.delta));
4820
- else if (currentBlock?.type === "text") {
4821
- currentBlock.text = `${stringifyUnknown(currentBlock.text)}${stringifyUnknown(event.delta)}`;
4822
- stream.push({
4823
- type: "text_delta",
4824
- contentIndex: blockIndex(),
4825
- delta: stringifyUnknown(event.delta)
4826
- });
4827
- }
4828
- }
4829
- } else if (type === "response.function_call_arguments.delta") {
4830
- const toolCall = streamingToolCalls.resolve(event);
4831
- if (toolCall) {
4832
- toolCall.block.partialJson = `${stringifyJsonLike(toolCall.block.partialJson)}${stringifyJsonLike(event.delta)}`;
4833
- toolCall.block.arguments = parseStreamingJson(stringifyJsonLike(toolCall.block.partialJson));
4834
- stream.push({
4835
- type: "toolcall_delta",
4836
- contentIndex: toolCall.contentIndex,
4837
- delta: stringifyJsonLike(event.delta),
4838
- partial: output
4839
- });
4840
- } else if (streamingToolCalls.hasActive()) streamingToolCalls.markArgumentsUnreliable();
4841
- } else if (type === "response.function_call_arguments.done") {
4842
- const toolCall = streamingToolCalls.resolve(event);
4843
- if (toolCall) {
4844
- const previousPartialJson = stringifyJsonLike(toolCall.block.partialJson);
4845
- const doneArguments = typeof event.arguments === "string" ? event.arguments : void 0;
4846
- if (doneArguments !== void 0 && (doneArguments.length > 0 || previousPartialJson === "")) {
4847
- toolCall.block.partialJson = doneArguments;
4848
- toolCall.block.arguments = parseStreamingJson(doneArguments);
4849
- toolCall.argumentStreamReliable = true;
4850
- }
4851
- if (doneArguments?.startsWith(previousPartialJson)) {
4852
- const delta = doneArguments.slice(previousPartialJson.length);
4853
- if (delta.length > 0) stream.push({
4854
- type: "toolcall_delta",
4855
- contentIndex: toolCall.contentIndex,
4856
- delta,
4857
- partial: output
4858
- });
4859
- }
4860
- } else if (streamingToolCalls.hasActive()) streamingToolCalls.markArgumentsUnreliable();
4861
- } else if (type === "response.output_item.done") {
4862
- const item = event.item;
4863
- if (item.type !== "message") {
4864
- lastTextBlock = null;
4865
- pendingMessageText = null;
4866
- }
4867
- if (item.type === "reasoning" && currentBlock?.type === "thinking") {
4868
- const summary = Array.isArray(item.summary) ? item.summary.map((part) => {
4869
- return part.text ?? "";
4870
- }).join("\n\n") : "";
4871
- currentBlock.thinking = summary;
4872
- currentBlock.thinkingSignature = JSON.stringify(item);
4873
- if ("encrypted_content" in item) currentBlock[OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY] = buildOpenAIResponsesReasoningReplayMetadata(model, {
4874
- authProfileId: options?.authProfileId,
4875
- sessionId: options?.sessionId
4876
- });
4877
- stream.push({
4878
- type: "thinking_end",
4879
- contentIndex: blockIndex(),
4880
- content: stringifyUnknown(currentBlock.thinking),
4881
- partial: output
4882
- });
4883
- currentBlock = null;
4884
- } else if (item.type === "message" && (currentBlock?.type === "text" || pendingMessageText !== null)) {
4885
- const finalText = (Array.isArray(item.content) ? item.content : []).map((part) => {
4886
- const contentPart = part;
4887
- return isResponsesTextContentPartType(contentPart.type) ? contentPart.text ?? "" : contentPart.refusal ?? "";
4888
- }).join("");
4889
- const phase = item.phase ?? void 0;
4890
- const collapse = pendingMessageText !== null ? resolveResponsesMessageSnapshotCollapse({
4891
- prior: lastTextBlock && {
4892
- text: stringifyUnknown(lastTextBlock.block.text),
4893
- phase: lastTextBlock.phase
4894
- },
4895
- nextText: finalText,
4896
- nextPhase: phase
4897
- }) : { kind: "keep" };
4898
- pendingMessageText = null;
4899
- if (collapse.kind === "extend" && lastTextBlock) {
4900
- lastTextBlock.block.text = collapse.text;
4901
- lastTextBlock.block.textSignature = encodeTextSignatureV1(stringifyUnknown(item.id), phase);
4902
- stream.push({
4903
- type: "text_end",
4904
- contentIndex: lastTextBlock.index,
4905
- content: collapse.text,
4906
- partial: output
4907
- });
4908
- } else {
4909
- if (currentBlock?.type !== "text") {
4910
- currentBlock = {
4911
- type: "text",
4912
- text: "",
4913
- ...phase ? { textSignature: encodeTextSignatureV1(stringifyUnknown(item.id), phase) } : {}
4914
- };
4915
- output.content.push(currentBlock);
4916
- stream.push({
4917
- type: "text_start",
4918
- contentIndex: blockIndex(),
4919
- partial: output
4920
- });
4921
- }
4922
- currentBlock.text = finalText;
4923
- currentBlock.textSignature = encodeTextSignatureV1(stringifyUnknown(item.id), phase);
4924
- lastTextBlock = {
4925
- block: currentBlock,
4926
- index: blockIndex(),
4927
- phase
4928
- };
4929
- stream.push({
4930
- type: "text_end",
4931
- contentIndex: blockIndex(),
4932
- content: stringifyUnknown(currentBlock.text),
4933
- partial: output
4934
- });
4935
- }
4936
- currentBlock = null;
4937
- } else if (item.type === "function_call") {
4938
- const streamingToolCall = streamingToolCalls.resolve(event, readResponsesToolCallItemIdentity(item));
4939
- if (!streamingToolCall && streamingToolCalls.hasActive()) {
4940
- await cooperativeScheduler.afterEvent();
4941
- continue;
4942
- }
4943
- const completedName = resolveCompletedToolCallName(streamingToolCall, item.name);
4944
- const streamedPartialJson = streamingToolCall ? stringifyJsonLike(streamingToolCall.block.partialJson) : "";
4945
- const completedArguments = typeof item.arguments === "string" ? item.arguments : void 0;
4946
- if (streamingToolCall && !streamingToolCall.argumentStreamReliable && !completedArguments) {
4947
- await cooperativeScheduler.afterEvent();
4948
- continue;
4949
- }
4950
- const finalPartialJson = completedArguments !== void 0 && (completedArguments.length > 0 || !streamedPartialJson) ? completedArguments : streamedPartialJson || "{}";
4951
- const args = parseStreamingJson(finalPartialJson);
4952
- let toolCallBlock;
4953
- let contentIndex;
4954
- if (streamingToolCall) {
4955
- toolCallBlock = streamingToolCall.block;
4956
- contentIndex = streamingToolCall.contentIndex;
4957
- } else {
4958
- toolCallBlock = {
4959
- type: "toolCall",
4960
- id: resolveToolCallId(item),
4961
- name: completedName,
4962
- arguments: args,
4963
- partialJson: finalPartialJson
4964
- };
4965
- output.content.push(toolCallBlock);
4966
- contentIndex = blockIndex();
4967
- stream.push({
4968
- type: "toolcall_start",
4969
- contentIndex,
4970
- partial: output
4971
- });
4972
- }
4973
- const currentToolCallId = resolveToolCallId(item, typeof toolCallBlock.id === "string" ? toolCallBlock.id : void 0);
4974
- toolCallBlock.id = currentToolCallId;
4975
- toolCallBlock.name = completedName;
4976
- toolCallBlock.arguments = args;
4977
- toolCallBlock.partialJson = finalPartialJson;
4978
- stream.push({
4979
- type: "toolcall_end",
4980
- contentIndex,
4981
- toolCall: {
4982
- type: "toolCall",
4983
- id: currentToolCallId,
4984
- name: completedName,
4985
- arguments: args
4986
- },
4987
- partial: output
4988
- });
4989
- if (streamingToolCall) streamingToolCalls.forget(streamingToolCall);
4990
- if (currentBlock === toolCallBlock) {
4991
- currentBlock = null;
4992
- currentItem = null;
4993
- }
4994
- }
4995
- } else if (type === "response.completed" || type === "response.incomplete") {
4996
- if (streamingToolCalls.hasActive()) throw new Error("Responses stream completed with unresolved tool calls");
4997
- const response = event.response;
4998
- if (typeof response?.id === "string") output.responseId = response.id;
4999
- if (type === "response.completed") backfillTerminalResponseOutput(response, { includeToolCalls: true });
5000
- recordResponsesTerminalOutcome({
5001
- response,
5002
- output,
5003
- model,
5004
- serviceTier: options?.serviceTier,
5005
- applyServiceTierPricing: options?.applyServiceTierPricing
5006
- });
5007
- if (type === "response.incomplete" && output.stopReason === "length") backfillTerminalResponseOutput(response, { includeToolCalls: false });
5008
- } else if (type === "error") throw new Error(`Error Code ${stringifyUnknown(event.code, "unknown")}: ${stringifyUnknown(event.message, "Unknown error")}`);
5009
- else if (type === "response.failed") {
5010
- const failure = normalizeResponsesFailedEvent(event, model);
5011
- if (failure.responseId) output.responseId = failure.responseId;
5012
- if (failure.observation) logResponsesFailedNoDetails(failure.observation);
5013
- throw new Error(failure.message);
5014
- }
5015
- await cooperativeScheduler.afterEvent();
5016
- }
5017
- if (streamingToolCalls.hasActive()) throw new Error("Responses stream ended with unresolved tool calls");
5018
- const eventTypeSummary = [...eventTypes.entries()].slice(0, 12).map(([eventType, count]) => `${eventType}:${count}`).join(",");
5019
- emitModelTransportDebug(log, `[responses] stream_done provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - streamStartedAt} events=${eventCount} types=${eventTypeSummary} stopReason=${output.stopReason ?? "unset"} contentBlocks=${output.content.length}`);
5020
- }
5021
- function readResponsesOutputMessageText(item) {
5022
- return (Array.isArray(item.content) ? item.content : []).map((part) => {
5023
- if (!isRecord(part)) return "";
5024
- if (part.type === "output_text" || part.type === "text") return stringifyUnknown(part.text);
5025
- if (part.type === "refusal") return stringifyUnknown(part.refusal);
5026
- return "";
5027
- }).join("");
5028
- }
5029
- function resolveProviderTransportTurnState(model, params) {
5030
- const normalizedProvider = model.provider.trim().toLowerCase();
5031
- const allowRuntimePluginLoad = normalizedProvider === "openai" || normalizedProvider === "azure-openai" || normalizedProvider === "azure-openai-responses";
5032
- return getAiTransportHost().plugin.resolveTransportTurnState({
5033
- provider: model.provider,
5034
- modelId: model.id,
5035
- allowRuntimePluginLoad,
5036
- context: {
5037
- provider: model.provider,
5038
- modelId: model.id,
5039
- model,
5040
- sessionId: params.sessionId,
5041
- turnId: params.turnId,
5042
- attempt: params.attempt,
5043
- transport: params.transport
5044
- }
5045
- });
5046
- }
5047
- function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turnHeaders, sessionId) {
5048
- return new OpenAI({
5049
- apiKey,
5050
- baseURL: model.baseUrl,
5051
- dangerouslyAllowBrowser: true,
5052
- defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, sessionId),
5053
- fetch: buildGuardedModelFetch(model),
5054
- ...buildOpenAISdkClientOptions(model)
5055
- });
5056
- }
5057
- function createOpenAIResponsesTransportStreamFn() {
5058
- return (model, context, options) => {
5059
- const responsesOptions = options;
5060
- const eventStream = createAssistantMessageEventStream();
5061
- const stream = eventStream;
5062
- (async () => {
5063
- const output = {
5064
- role: "assistant",
5065
- content: [],
5066
- api: model.api,
5067
- provider: model.provider,
5068
- model: model.id,
5069
- usage: {
5070
- input: 0,
5071
- output: 0,
5072
- cacheRead: 0,
5073
- cacheWrite: 0,
5074
- totalTokens: 0,
5075
- cost: {
5076
- input: 0,
5077
- output: 0,
5078
- cacheRead: 0,
5079
- cacheWrite: 0,
5080
- total: 0
5081
- }
5082
- },
5083
- stopReason: "stop",
5084
- timestamp: Date.now()
5085
- };
5086
- let firstEventAbort;
5087
- try {
5088
- const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
5089
- const turnState = resolveProviderTransportTurnState(model, {
5090
- sessionId: options?.sessionId,
5091
- turnId: randomUUID(),
5092
- attempt: 1,
5093
- transport: "stream"
5094
- });
5095
- const client = createOpenAIResponsesClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId);
5096
- let params = buildOpenAIResponsesParams(model, context, responsesOptions, turnState?.metadata);
5097
- const nextParams = await options?.onPayload?.(params, model);
5098
- if (nextParams !== void 0) params = nextParams;
5099
- if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
5100
- params = sanitizeOpenAICodexResponsesParams(model, params);
5101
- params = sanitizeResponsesImagePayload(params);
5102
- if (options?.openclawCodeModeToolSurface === true) {
5103
- enforceCodeModeResponsesToolSurface(params);
5104
- assertCodeModeResponsesToolSurface(params);
5105
- }
5106
- const requestStartedAt = Date.now();
5107
- firstEventAbort = createFirstStreamEventAbortController(options?.signal);
5108
- const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal, { stream: true });
5109
- emitModelTransportDebug(log, `[responses] start provider=${model.provider} api=${model.api} model=${model.id} baseUrl=${formatModelTransportDebugBaseUrl(model.baseUrl)} timeoutMs=${safeDebugValue(requestOptions?.timeout)} apiKey=${apiKey ? "present" : "missing"} ${summarizeResponsesPayload(params)}`);
5110
- const responseStream = await createResponsesStreamWithEncryptedContentRetry({
5111
- client,
5112
- request: params,
5113
- requestOptions,
5114
- model
5115
- });
5116
- emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - requestStartedAt}`);
5117
- stream.push({
5118
- type: "start",
5119
- partial: output
5120
- });
5121
- await processResponsesStream(responseStream, output, stream, model, {
5122
- serviceTier: responsesOptions?.serviceTier,
5123
- applyServiceTierPricing,
5124
- firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options),
5125
- abortFirstEventStream: firstEventAbort.abort,
5126
- onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
5127
- signal: options?.signal,
5128
- authProfileId: responsesOptions?.authProfileId,
5129
- sessionId: options?.sessionId
5130
- });
5131
- if (options?.signal?.aborted) throw new Error("Request was aborted");
5132
- if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
5133
- stream.push({
5134
- type: "done",
5135
- reason: output.stopReason,
5136
- message: output
5137
- });
5138
- stream.end();
5139
- } catch (error) {
5140
- log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
5141
- assignTransportErrorDetails(output, error, options?.signal);
5142
- stream.push({
5143
- type: "error",
5144
- reason: output.stopReason,
5145
- error: output
5146
- });
5147
- stream.end();
5148
- } finally {
5149
- firstEventAbort?.dispose();
5150
- }
5151
- })();
5152
- return eventStream;
5153
- };
5154
- }
5155
- function getPromptCacheRetention(baseUrl, cacheRetention) {
5156
- if (cacheRetention !== "long") return;
5157
- return baseUrl?.includes("api.openai.com") ? "24h" : void 0;
5158
- }
5159
- function resolveOpenAIReasoningEffort(options) {
5160
- return normalizeOpenAIReasoningEffort(options?.reasoningEffort ?? options?.reasoning ?? "high");
5161
- }
5162
- function hasResponsesWebSearchTool(tools) {
5163
- if (!Array.isArray(tools)) return false;
5164
- return tools.some((tool) => {
5165
- if (!isRecord(tool)) return false;
5166
- if (tool.type === "web_search") return true;
5167
- if (tool.type === "function" && tool.name === "web_search") return true;
5168
- const fn = tool.function;
5169
- return isRecord(fn) && fn.name === "web_search";
5170
- });
5171
- }
5172
- function raiseMinimalReasoningForResponsesWebSearch(params) {
5173
- if (params.effort !== "minimal" || !hasResponsesWebSearchTool(params.tools)) return params.effort;
5174
- for (const effort of [
5175
- "low",
5176
- "medium",
5177
- "high"
5178
- ]) {
5179
- const resolved = resolveOpenAIReasoningEffortForModel({
5180
- model: params.model,
5181
- effort
5182
- });
5183
- if (resolved && resolved !== "none" && resolved !== "minimal") return resolved;
5184
- }
5185
- return params.effort;
5186
- }
5187
- const OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS = [
5188
- "max_output_tokens",
5189
- "metadata",
5190
- "prompt_cache_retention",
5191
- "service_tier",
5192
- "temperature",
5193
- "top_p"
5194
- ];
5195
- function stripOpenAICodexResponsesUnsupportedTextFields(params) {
5196
- const text = params.text;
5197
- if (!text || typeof text !== "object" || Array.isArray(text)) return;
5198
- const sanitizedText = { ...text };
5199
- delete sanitizedText.format;
5200
- if (Object.keys(sanitizedText).length > 0) params.text = sanitizedText;
5201
- else delete params.text;
5202
- }
5203
- function sanitizeOpenAICodexResponsesParams(model, params) {
5204
- if (!usesNativeOpenAICodexResponsesBackend(model)) return params;
5205
- for (const key of OPENAI_CODEX_RESPONSES_UNSUPPORTED_PARAMS) delete params[key];
5206
- stripOpenAICodexResponsesUnsupportedTextFields(params);
5207
- return params;
5208
- }
5209
- function buildOpenAICodexResponsesInstructions(context) {
5210
- if (!context.systemPrompt) return;
5211
- return sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt));
5212
- }
5213
- function resolveOpenAICodexResponsesInstructions(model, context) {
5214
- const instructions = buildOpenAICodexResponsesInstructions(context);
5215
- if (instructions && instructions.trim().length > 0) return instructions;
5216
- return usesNativeOpenAICodexResponsesBackend(model) ? OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS : void 0;
3412
+ function resolveOpenAICodexResponsesInstructions(model, context) {
3413
+ const instructions = buildOpenAICodexResponsesInstructions(context);
3414
+ if (instructions && instructions.trim().length > 0) return instructions;
3415
+ return usesNativeOpenAICodexResponsesBackend(model) ? OPENAI_CODEX_RESPONSES_DEFAULT_INSTRUCTIONS : void 0;
5217
3416
  }
5218
3417
  function ensureOpenAICodexResponsesInput(messages, context) {
5219
3418
  if (messages.length > 0 || !context.systemPrompt) return;
5220
3419
  if (!buildOpenAICodexResponsesInstructions(context)) throw new Error("OpenAI Codex Responses requires non-empty input when only systemPrompt is provided.");
5221
3420
  messages.push(buildResponsesInputMessage("user", [{
5222
3421
  type: "input_text",
5223
- text: OPENAI_CODEX_RESPONSES_EMPTY_INPUT_TEXT
3422
+ text: " "
5224
3423
  }]));
5225
3424
  }
5226
3425
  function resolveOpenAIResponsesTextFormat(responseFormat) {
@@ -5309,7 +3508,180 @@ function buildOpenAIResponsesParams(model, context, options, metadata) {
5309
3508
  applyOpenAIResponsesPayloadPolicy(params, payloadPolicy);
5310
3509
  return sanitizeOpenAICodexResponsesParams(model, params);
5311
3510
  }
5312
- function createAzureOpenAIResponsesTransportStreamFn() {
3511
+ //#endregion
3512
+ //#region packages/media-core/src/inline-image-data-url.ts
3513
+ /** Prefix used to distinguish inline data URLs from remote/local image references. */
3514
+ const INLINE_IMAGE_DATA_URL_PREFIX = "data:";
3515
+ const IMAGE_SIGNATURES = [
3516
+ {
3517
+ mime: "image/png",
3518
+ matches: (buffer) => buffer.length >= 8 && buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71 && buffer[4] === 13 && buffer[5] === 10 && buffer[6] === 26 && buffer[7] === 10
3519
+ },
3520
+ {
3521
+ mime: "image/jpeg",
3522
+ matches: (buffer) => buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255
3523
+ },
3524
+ {
3525
+ mime: "image/webp",
3526
+ matches: (buffer) => buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP"
3527
+ },
3528
+ {
3529
+ mime: "image/gif",
3530
+ matches: (buffer) => buffer.length >= 6 && (buffer.subarray(0, 6).toString("ascii") === "GIF87a" || buffer.subarray(0, 6).toString("ascii") === "GIF89a")
3531
+ },
3532
+ {
3533
+ mime: "image/bmp",
3534
+ matches: (buffer) => buffer.length >= 2 && buffer[0] === 66 && buffer[1] === 77
3535
+ }
3536
+ ];
3537
+ const HEIC_BRANDS = /* @__PURE__ */ new Set([
3538
+ "heic",
3539
+ "heix",
3540
+ "hevc",
3541
+ "hevx",
3542
+ "heis",
3543
+ "heim",
3544
+ "hevm",
3545
+ "hevs"
3546
+ ]);
3547
+ const HEIF_BRANDS = /* @__PURE__ */ new Set(["mif1", "msf1"]);
3548
+ const IMAGE_SIGNATURE_PREFIX_BASE64_CHARS = 128;
3549
+ const INLINE_IMAGE_DATA_URL_MIMES = /* @__PURE__ */ new Set([
3550
+ "image/png",
3551
+ "image/jpeg",
3552
+ "image/webp",
3553
+ "image/gif"
3554
+ ]);
3555
+ function startsWithDataUrl(value) {
3556
+ return value.slice(0, 5).toLowerCase() === INLINE_IMAGE_DATA_URL_PREFIX;
3557
+ }
3558
+ function sniffIsoBmffImageMime(buffer) {
3559
+ if (buffer.length < 12 || buffer.subarray(4, 8).toString("ascii") !== "ftyp") return;
3560
+ const brands = [buffer.subarray(8, 12).toString("ascii")];
3561
+ for (let offset = 16; offset + 4 <= buffer.length; offset += 4) brands.push(buffer.subarray(offset, offset + 4).toString("ascii"));
3562
+ if (brands.some((brand) => HEIC_BRANDS.has(brand))) return "image/heic";
3563
+ if (brands.some((brand) => HEIF_BRANDS.has(brand))) return "image/heif";
3564
+ }
3565
+ /** Sniffs supported inline image formats from decoded bytes. */
3566
+ function sniffInlineImageMime(buffer) {
3567
+ return IMAGE_SIGNATURES.find((signature) => signature.matches(buffer))?.mime ?? sniffIsoBmffImageMime(buffer);
3568
+ }
3569
+ function isImageMimeType(value) {
3570
+ return value.trim().toLowerCase().startsWith("image/");
3571
+ }
3572
+ /** Canonicalizes trusted inline image base64 and rejects malformed or non-image payloads. */
3573
+ function sanitizeInlineImageBase64(params) {
3574
+ if (!isImageMimeType(params.mimeType)) return;
3575
+ const canonicalPayload = canonicalizeBase64(params.base64);
3576
+ if (!canonicalPayload) return;
3577
+ const sniffedMimeType = sniffInlineImageMime(Buffer.from(canonicalPayload.slice(0, IMAGE_SIGNATURE_PREFIX_BASE64_CHARS), "base64"));
3578
+ if (!sniffedMimeType) return;
3579
+ return {
3580
+ mimeType: sniffedMimeType,
3581
+ base64: canonicalPayload
3582
+ };
3583
+ }
3584
+ function parseInlineImageDataUrl(value) {
3585
+ if (!startsWithDataUrl(value)) return {
3586
+ metadata: [],
3587
+ payload: value
3588
+ };
3589
+ const commaIndex = value.indexOf(",");
3590
+ if (commaIndex < 0) return;
3591
+ return {
3592
+ metadata: value.slice(5, commaIndex).split(";").map((part) => part.trim()),
3593
+ payload: value.slice(commaIndex + 1)
3594
+ };
3595
+ }
3596
+ function metadataAllowsImageBase64(metadata) {
3597
+ const [mimeType, ...options] = metadata;
3598
+ return mimeType !== void 0 && isImageMimeType(mimeType) && options.some((part) => part.toLowerCase() === "base64");
3599
+ }
3600
+ function sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, allowedMimes) {
3601
+ const parsed = parseInlineImageDataUrl(imageUrl);
3602
+ if (!parsed) return;
3603
+ if (parsed.metadata.length === 0) return imageUrl;
3604
+ if (!metadataAllowsImageBase64(parsed.metadata)) return;
3605
+ const [mimeType] = parsed.metadata;
3606
+ const sanitized = sanitizeInlineImageBase64({
3607
+ mimeType: mimeType ?? "",
3608
+ base64: parsed.payload
3609
+ });
3610
+ if (!sanitized) return;
3611
+ if (allowedMimes && !allowedMimes.has(sanitized.mimeType)) return;
3612
+ return `data:${sanitized.mimeType};base64,${sanitized.base64}`;
3613
+ }
3614
+ /** Canonicalizes provider-safe inline image data URLs and rejects unsupported formats. */
3615
+ function sanitizeInlineImageDataUrl(imageUrl) {
3616
+ return sanitizeInlineImageDataUrlWithAllowedMimes(imageUrl, INLINE_IMAGE_DATA_URL_MIMES);
3617
+ }
3618
+ //#endregion
3619
+ //#region packages/ai/src/transports/responses-image-payload-sanitizer.ts
3620
+ /**
3621
+ * Sanitizes OpenAI Responses payloads before transport. Invalid inline images
3622
+ * are replaced with text placeholders so the request remains valid and
3623
+ * auditable.
3624
+ */
3625
+ const IMAGE_OMITTED_TEXT = "omitted image payload: invalid inline image data";
3626
+ function invalidSnakeImage() {
3627
+ return {
3628
+ type: "input_text",
3629
+ text: `[${IMAGE_OMITTED_TEXT}]`
3630
+ };
3631
+ }
3632
+ function sanitizeValue(value) {
3633
+ if (Array.isArray(value)) return value.map(sanitizeValue);
3634
+ if (!isRecord(value)) return value;
3635
+ if (value.type === "input_image" && typeof value.image_url === "string") {
3636
+ const imageUrl = sanitizeInlineImageDataUrl(value.image_url);
3637
+ return imageUrl ? {
3638
+ ...value,
3639
+ image_url: imageUrl
3640
+ } : invalidSnakeImage();
3641
+ }
3642
+ const next = {};
3643
+ for (const [key, child] of Object.entries(value)) next[key] = sanitizeValue(child);
3644
+ return next;
3645
+ }
3646
+ /** Sanitize inline image fields inside a Responses API payload. */
3647
+ function sanitizeResponsesImagePayload(params) {
3648
+ if (!Array.isArray(params.input)) return params;
3649
+ return {
3650
+ ...params,
3651
+ input: sanitizeValue(params.input)
3652
+ };
3653
+ }
3654
+ //#endregion
3655
+ //#region packages/ai/src/transports/openai-responses-client.ts
3656
+ function resolveProviderTransportTurnState(model, params) {
3657
+ const normalizedProvider = model.provider.trim().toLowerCase();
3658
+ const allowRuntimePluginLoad = normalizedProvider === "openai" || normalizedProvider === "azure-openai" || normalizedProvider === "azure-openai-responses";
3659
+ return getAiTransportHost().plugin.resolveTransportTurnState({
3660
+ provider: model.provider,
3661
+ modelId: model.id,
3662
+ allowRuntimePluginLoad,
3663
+ context: {
3664
+ provider: model.provider,
3665
+ modelId: model.id,
3666
+ model,
3667
+ sessionId: params.sessionId,
3668
+ turnId: params.turnId,
3669
+ attempt: params.attempt,
3670
+ transport: params.transport
3671
+ }
3672
+ });
3673
+ }
3674
+ function createOpenAIResponsesClient(model, context, apiKey, optionHeaders, turnHeaders, sessionId) {
3675
+ return new OpenAI({
3676
+ apiKey,
3677
+ baseURL: model.baseUrl,
3678
+ dangerouslyAllowBrowser: true,
3679
+ defaultHeaders: buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, sessionId),
3680
+ fetch: buildGuardedModelFetch(model),
3681
+ ...buildOpenAISdkClientOptions(model)
3682
+ });
3683
+ }
3684
+ function createResponsesTransportExecutor(config) {
5313
3685
  return (model, context, options) => {
5314
3686
  const responsesOptions = options;
5315
3687
  const eventStream = createAssistantMessageEventStream();
@@ -5318,7 +3690,7 @@ function createAzureOpenAIResponsesTransportStreamFn() {
5318
3690
  const output = {
5319
3691
  role: "assistant",
5320
3692
  content: [],
5321
- api: "azure-openai-responses",
3693
+ api: config.outputApi ?? model.api,
5322
3694
  provider: model.provider,
5323
3695
  model: model.id,
5324
3696
  usage: {
@@ -5347,37 +3719,49 @@ function createAzureOpenAIResponsesTransportStreamFn() {
5347
3719
  attempt: 1,
5348
3720
  transport: "stream"
5349
3721
  });
5350
- const client = createAzureOpenAIClient(model, context, apiKey, options?.headers, turnState?.headers);
5351
- const deploymentName = resolveAzureDeploymentName(model);
5352
- let params = buildAzureOpenAIResponsesParams(model, context, responsesOptions, deploymentName, turnState?.metadata);
3722
+ const client = config.createClient(model, context, apiKey, options?.headers, turnState?.headers, options?.sessionId);
3723
+ let params = config.buildRequest(model, context, responsesOptions, turnState?.metadata);
5353
3724
  const nextParams = await options?.onPayload?.(params, model);
5354
3725
  if (nextParams !== void 0) params = nextParams;
5355
3726
  if (!isOpenAICodexResponsesModel(model)) params = mergeTransportMetadata(params, turnState?.metadata);
5356
3727
  params = sanitizeOpenAICodexResponsesParams(model, params);
5357
3728
  params = sanitizeResponsesImagePayload(params);
5358
3729
  if (options?.openclawCodeModeToolSurface === true) {
5359
- enforceCodeModeResponsesToolSurface(params);
5360
- assertCodeModeResponsesToolSurface(params);
3730
+ const visibleToolNames = resolveCodeModeResponsesVisibleToolNames(context);
3731
+ enforceCodeModeResponsesToolSurface(params, visibleToolNames);
3732
+ assertCodeModeResponsesToolSurface(params, visibleToolNames);
5361
3733
  }
5362
3734
  const requestStartedAt = Date.now();
5363
3735
  firstEventAbort = createFirstStreamEventAbortController(options?.signal);
5364
- const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal);
3736
+ const requestOptions = buildOpenAISdkRequestOptions(model, firstEventAbort.signal, {
3737
+ stream: config.streamRequest,
3738
+ timeoutMs: options?.timeoutMs,
3739
+ maxRetries: options?.maxRetries
3740
+ });
5365
3741
  emitModelTransportDebug(log, `[responses] start provider=${model.provider} api=${model.api} model=${model.id} baseUrl=${formatModelTransportDebugBaseUrl(model.baseUrl)} timeoutMs=${safeDebugValue(requestOptions?.timeout)} apiKey=${apiKey ? "present" : "missing"} ${summarizeResponsesPayload(params)}`);
5366
- const responseStream = await client.responses.create(params, requestOptions);
3742
+ const responseStream = await config.createResponseStream({
3743
+ client,
3744
+ request: params,
3745
+ requestOptions,
3746
+ model
3747
+ });
5367
3748
  emitModelTransportDebug(log, `[responses] headers provider=${model.provider} api=${model.api} model=${model.id} elapsedMs=${Date.now() - requestStartedAt}`);
5368
3749
  stream.push({
5369
3750
  type: "start",
5370
3751
  partial: output
5371
3752
  });
5372
- await processResponsesStream(responseStream, output, stream, model, {
5373
- firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS,
3753
+ await processResponsesStream(observeResponsesStream(responseStream, model), output, stream, model, {
3754
+ ...config.pricingOptions?.(responsesOptions),
3755
+ firstEventTimeoutMs: getFirstStreamEventTimeoutMs(options) ?? config.firstEventTimeoutMs,
5374
3756
  abortFirstEventStream: firstEventAbort.abort,
5375
3757
  onFirstEventTimeout: getFirstStreamEventTimeoutHandler(options),
5376
3758
  signal: options?.signal,
5377
- authProfileId: responsesOptions?.authProfileId,
5378
- sessionId: options?.sessionId
3759
+ reasoningReplayMetadata: buildOpenAIResponsesReasoningReplayMetadata(model, {
3760
+ authProfileId: responsesOptions?.authProfileId,
3761
+ sessionId: options?.sessionId
3762
+ })
5379
3763
  });
5380
- if (options?.signal?.aborted) throw new Error("Request was aborted");
3764
+ if (options?.signal?.aborted) throw transportAbortError(options.signal);
5381
3765
  if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
5382
3766
  stream.push({
5383
3767
  type: "done",
@@ -5386,6 +3770,7 @@ function createAzureOpenAIResponsesTransportStreamFn() {
5386
3770
  });
5387
3771
  stream.end();
5388
3772
  } catch (error) {
3773
+ if (error instanceof ResponsesStreamFailure && error.observation) logResponsesFailedNoDetails(error.observation);
5389
3774
  log.warn(`[responses] error provider=${model.provider} api=${model.api} model=${model.id} ` + summarizeOpenAITransportError(error));
5390
3775
  assignTransportErrorDetails(output, error, options?.signal);
5391
3776
  stream.push({
@@ -5401,6 +3786,27 @@ function createAzureOpenAIResponsesTransportStreamFn() {
5401
3786
  return eventStream;
5402
3787
  };
5403
3788
  }
3789
+ function createOpenAIResponsesTransportStreamFn() {
3790
+ return createResponsesTransportExecutor({
3791
+ streamRequest: true,
3792
+ createClient: createOpenAIResponsesClient,
3793
+ buildRequest: buildOpenAIResponsesParams,
3794
+ createResponseStream: createResponsesStreamWithEncryptedContentRetry,
3795
+ pricingOptions: (options) => ({
3796
+ serviceTier: options?.serviceTier,
3797
+ applyServiceTierPricing
3798
+ })
3799
+ });
3800
+ }
3801
+ function createAzureOpenAIResponsesTransportStreamFn() {
3802
+ return createResponsesTransportExecutor({
3803
+ outputApi: "azure-openai-responses",
3804
+ firstEventTimeoutMs: AZURE_RESPONSES_FIRST_EVENT_TIMEOUT_MS,
3805
+ createClient: createAzureOpenAIClient,
3806
+ buildRequest: (model, context, options, metadata) => buildAzureOpenAIResponsesParams(model, context, options, resolveAzureDeploymentName(model), metadata),
3807
+ createResponseStream: async ({ client, request, requestOptions }) => await client.responses.create(request, requestOptions)
3808
+ });
3809
+ }
5404
3810
  function normalizeAzureBaseUrl(baseUrl) {
5405
3811
  return baseUrl.replace(/\/+$/, "");
5406
3812
  }
@@ -5432,6 +3838,9 @@ function buildAzureOpenAIResponsesParams(model, context, options, deploymentName
5432
3838
  delete params.store;
5433
3839
  return params;
5434
3840
  }
3841
+ //#endregion
3842
+ //#region packages/ai/src/transports/openai-responses-transport.ts
3843
+ /** OpenAI Responses transport facade. */
5435
3844
  const responsesTesting = {
5436
3845
  getCompat,
5437
3846
  assertCodeModeResponsesToolSurface,
@@ -5698,4 +4107,4 @@ function prepareModelForSimpleCompletion(params) {
5698
4107
  return applyProviderSimpleCompletionWrapper(apiRegistry, model, cfg);
5699
4108
  }
5700
4109
  //#endregion
5701
- export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, coerceTransportToolCallArguments, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, hasOpenAICompatibleConversationTurn, isOpenAICodexResponsesModel, log, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveCacheRetention, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortTransportToolsByName, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, usesNativeOpenAICodexResponsesBackend };
4110
+ export { GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP, applyAnthropicEphemeralCacheControlMarkers, applyAnthropicPayloadPolicyToParams, applyOpenAIResponsesPayloadPolicy, assertCodeModeResponsesToolSurface, assignTransportErrorDetails, buildOpenAIClientHeaders, buildOpenAICompletionsParams, buildOpenAISdkClientOptions, buildOpenAISdkRequestOptions, buildTransportAwareSimpleStreamFn, canonicalizeMaxTokensParam, coerceTransportToolCallArguments, createAnthropicMessagesTransportStreamFn, createAzureOpenAIResponsesTransportStreamFn, createBoundaryAwareStreamFnForModel, createDeepSeekTextFilter, createEmptyTransportUsage, createModelStreamCooperativeScheduler, createOpenAICompletionsTransportStreamFn, createOpenAIResponsesTransportStreamFn, createOpenClawTransportStreamFnForModel, createTransportAwareStreamFnForModel, createWritableTransportEventStream, detectOpenAICompletionsCompat, emitModelTransportDebug, enforceCodeModeResponsesToolSurface, failTransportStream, filterCodeModePayloadTools, finalizeTransportStream, flattenCompletionMessagesToStringContent, formatModelTransportDebugBaseUrl, formatModelTransportDebugUrl, getCompat, hasOpenAICompatibleConversationTurn, isCodeModeModelVisibleToolName, isOpenAICodexResponsesModel, log, mergeTransportHeaders, mergeTransportMetadata, normalizeCodexResponsesBaseUrlForOpenAISdk, parseJsonObjectPreservingUnsafeIntegers, parseJsonPreservingUnsafeIntegers, prepareModelForSimpleCompletion, prepareTransportAwareSimpleModel, quoteUnsafeIntegerLiterals, readCodeModePayloadToolName, resolveAnthropicEphemeralCacheControl, resolveAnthropicMessagesUrl, resolveAnthropicPayloadPolicy, resolveCodeModeResponsesVisibleToolNames, resolveMaxTokensParam, resolveModelPayloadDebugMode, resolveModelSseDebugMode, resolveOpenAICompletionsCompat, resolveOpenAIReasoningEffortMap, resolveOpenAIResponsesPayloadPolicy, resolveOpenAIStrictToolFlagWithDiagnostics, resolvePromptCacheKey, resolveReplayableResponsesMessageId, resolveTransportAwareSimpleApi, sanitizeNonEmptyTransportPayloadText, sanitizeResponsesImagePayload, sanitizeTransportPayloadText, sortPromptCacheToolsByName as sortTransportToolsByName, stripCompletionMessagesToRoleContent, throwIfModelStreamAborted, transportAbortError, usesNativeOpenAICodexResponsesBackend };