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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/{anthropic-Cvm71xlk.mjs → anthropic-D7SyZQ5v.mjs} +58 -39
  2. package/dist/{api-registry-Byvoz8Ha.d.mts → api-registry-DXBIXISf.d.mts} +1 -1
  3. package/dist/{azure-openai-responses-b4lfJxYS.mjs → azure-openai-responses-BkZgyZ3I.mjs} +5 -4
  4. package/dist/{event-stream-uQspnL8S.d.mts → event-stream-BPemFrFW.d.mts} +1 -1
  5. package/dist/event-stream.d.mts +1 -1
  6. package/dist/{google-C7PoDjpW.mjs → google-C_biGbV7.mjs} +2 -2
  7. package/dist/{google-shared-C7ohrmOI.mjs → google-shared-BK-NoHl8.mjs} +5 -4
  8. package/dist/{google-vertex-L_ABazIA.mjs → google-vertex-CvdzF1U7.mjs} +2 -2
  9. package/dist/index.d.mts +4 -4
  10. package/dist/internal/anthropic.d.mts +8 -2
  11. package/dist/internal/anthropic.mjs +3 -3
  12. package/dist/internal/openai.d.mts +27 -2
  13. package/dist/internal/openai.mjs +5 -5
  14. package/dist/internal/runtime.d.mts +2 -2
  15. package/dist/internal/runtime.mjs +3 -2
  16. package/dist/internal/shared.d.mts +13 -2
  17. package/dist/internal/shared.mjs +2 -2
  18. package/dist/{mistral-D-39Trax.mjs → mistral-BdrQmDWp.mjs} +25 -9
  19. package/dist/{model-utils-Q1LSRIdo.mjs → model-utils-1GiZ2_rr.mjs} +3 -1
  20. package/dist/{openai-chatgpt-responses-DCA0K85L.mjs → openai-chatgpt-responses-DP63Qt01.mjs} +11 -9
  21. package/dist/{openai-completions-B2jr3vtJ.mjs → openai-completions-zLDfebnN.mjs} +42 -21
  22. package/dist/{openai-responses-DTpD8YVy.mjs → openai-responses-ClspWt4I.mjs} +3 -3
  23. package/dist/{openai-responses-shared-B9mtGvXq.mjs → openai-responses-shared-CwnGU0KW.mjs} +412 -284
  24. package/dist/{openai-tool-projection-cTLC4F65.mjs → openai-tool-projection-WHf2nuOI.mjs} +1 -1
  25. package/dist/provider-error-LMfTEkfO.mjs +40 -0
  26. package/dist/providers.d.mts +1 -1
  27. package/dist/providers.mjs +8 -8
  28. package/dist/{transform-messages-DNWpwvXA.mjs → transform-messages-eb6lxDLB.mjs} +18 -3
  29. package/dist/{types-Cx2zJtyz.d.mts → types-CYORveZ7.d.mts} +6 -0
  30. package/dist/types.d.mts +3 -3
  31. package/dist/{validation-Cej7htKc.d.mts → validation-BX8euiDv.d.mts} +1 -1
  32. package/dist/validation.d.mts +1 -1
  33. package/npm-shrinkwrap.json +2 -2
  34. package/package.json +1 -1
@@ -1,10 +1,10 @@
1
1
  import { n as getEnvApiKey } from "./env-api-keys-DnhYpc27.mjs";
2
2
  import { t as AssistantMessageEventStream } from "./event-stream-D8n2uFee.mjs";
3
3
  import { n as getAiTransportHost } from "./host-4t713IeR.mjs";
4
- import { n as calculateCost, r as clampThinkingLevel } from "./model-utils-Q1LSRIdo.mjs";
4
+ import { n as calculateCost, r as clampThinkingLevel } from "./model-utils-1GiZ2_rr.mjs";
5
5
  import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
6
6
  import { t as sanitizeSurrogates } from "./sanitize-unicode-DT5o51ur.mjs";
7
- import { a as truncateUtf16Safe, i as extractToolResultText, n as describeToolResultMediaPlaceholder, s as buildBaseOptions, t as transformMessages, x as stripSystemPromptCacheBoundary } from "./transform-messages-DNWpwvXA.mjs";
7
+ import { i as extractToolResultText, l as buildBaseOptions, n as describeToolResultMediaPlaceholder, o as isImageWithMediaPayload, s as truncateUtf16Safe, t as transformMessages, u as clampMaxTokensToModel, w as stripSystemPromptCacheBoundary } from "./transform-messages-eb6lxDLB.mjs";
8
8
  import { t as shortHash } from "./hash-CHgqbJmD.mjs";
9
9
  import { t as createSseByteGuard } from "./streaming-byte-guard-BrbkbwUu.mjs";
10
10
  import { randomUUID } from "node:crypto";
@@ -75,7 +75,7 @@ const streamMistral = (model, context, options) => {
75
75
  ...model.headers,
76
76
  ...options?.headers
77
77
  };
78
- if (options?.sessionId) headers["x-affinity"] ||= options.sessionId;
78
+ if (resolveMistralPromptCacheKey(options) && options?.sessionId) headers["x-affinity"] ||= options.sessionId;
79
79
  const mistralStream = await mistral.chat.stream(payload, {
80
80
  headers,
81
81
  signal: options?.signal
@@ -113,7 +113,10 @@ const streamMistral = (model, context, options) => {
113
113
  const streamSimpleMistral = (model, context, options) => {
114
114
  const apiKey = options?.apiKey || getEnvApiKey(model.provider);
115
115
  if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
116
- const base = buildBaseOptions(model, options, apiKey);
116
+ const base = {
117
+ ...buildBaseOptions(model, options, apiKey),
118
+ maxTokens: clampMaxTokensToModel(model, options?.maxTokens)
119
+ };
117
120
  const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : void 0;
118
121
  const reasoning = clampedReasoning === "off" ? void 0 : clampedReasoning;
119
122
  const shouldUseReasoning = model.reasoning && reasoning !== void 0;
@@ -218,12 +221,23 @@ function buildChatPayload(model, context, messages, options) {
218
221
  }
219
222
  if (options?.promptMode) payload.promptMode = options.promptMode;
220
223
  if (options?.reasoningEffort) payload.reasoningEffort = options.reasoningEffort;
224
+ const promptCacheKey = resolveMistralPromptCacheKey(options);
225
+ if (promptCacheKey) payload.promptCacheKey = promptCacheKey;
221
226
  if (context.systemPrompt) payload.messages.unshift({
222
227
  role: "system",
223
228
  content: sanitizeSurrogates(stripSystemPromptCacheBoundary(context.systemPrompt))
224
229
  });
225
230
  return payload;
226
231
  }
232
+ function resolveMistralPromptCacheKey(options) {
233
+ if (options?.cacheRetention === "none") return;
234
+ return options?.promptCacheKey?.trim() || options?.sessionId?.trim() || void 0;
235
+ }
236
+ function readMistralCachedPromptTokens(usage, promptTokens) {
237
+ const record = usage;
238
+ const rawCachedTokens = record.promptTokensDetails?.cachedTokens ?? record.prompt_tokens_details?.cached_tokens ?? record.cachedTokens ?? record.cached_tokens;
239
+ return Math.min(promptTokens, Math.max(0, typeof rawCachedTokens === "number" && Number.isFinite(rawCachedTokens) ? rawCachedTokens : 0));
240
+ }
227
241
  async function consumeChatStream(model, output, stream, mistralStream) {
228
242
  let currentBlock = null;
229
243
  const blocks = output.content;
@@ -309,11 +323,13 @@ async function consumeChatStream(model, output, stream, mistralStream) {
309
323
  const chunk = event.data;
310
324
  output.responseId ||= chunk.id;
311
325
  if (chunk.usage) {
312
- output.usage.input = chunk.usage.promptTokens || 0;
326
+ const promptTokens = chunk.usage.promptTokens || 0;
327
+ const cachedPromptTokens = readMistralCachedPromptTokens(chunk.usage, promptTokens);
328
+ output.usage.input = Math.max(0, promptTokens - cachedPromptTokens);
313
329
  output.usage.output = chunk.usage.completionTokens || 0;
314
- output.usage.cacheRead = 0;
330
+ output.usage.cacheRead = cachedPromptTokens;
315
331
  output.usage.cacheWrite = 0;
316
- output.usage.totalTokens = chunk.usage.totalTokens || output.usage.input + output.usage.output;
332
+ output.usage.totalTokens = chunk.usage.totalTokens || output.usage.input + output.usage.output + output.usage.cacheRead;
317
333
  calculateCost(model, output.usage);
318
334
  }
319
335
  const choice = chunk.choices[0];
@@ -576,14 +592,14 @@ function toChatMessages(messages, supportsImages) {
576
592
  continue;
577
593
  }
578
594
  const toolContent = [];
579
- const toolText = buildToolResultText(extractToolResultText(msg.content), describeToolResultMediaPlaceholder(msg.content), msg.content.some((part) => part.type === "image"), supportsImages, msg.isError);
595
+ const toolText = buildToolResultText(extractToolResultText(msg.content), describeToolResultMediaPlaceholder(msg.content), msg.content.some(isImageWithMediaPayload), supportsImages, msg.isError);
580
596
  toolContent.push({
581
597
  type: "text",
582
598
  text: toolText
583
599
  });
584
600
  for (const part of msg.content) {
585
601
  if (!supportsImages) continue;
586
- if (part.type !== "image") continue;
602
+ if (!isImageWithMediaPayload(part)) continue;
587
603
  toolContent.push({
588
604
  type: "image_url",
589
605
  imageUrl: `data:${part.mimeType};base64,${part.data}`
@@ -2,10 +2,12 @@ import { c as resolveClaudeNativeThinkingLevelMap, i as requiresClaudeMandatoryA
2
2
  //#region packages/ai/src/model-utils.ts
3
3
  /** Calculates and stores model cost fields from token usage and per-million pricing. */
4
4
  function calculateCost(model, usage) {
5
+ const cacheWrite1h = Math.min(usage.cacheWrite, Math.max(0, usage.cacheWrite1h ?? 0));
6
+ const cacheWrite5m = usage.cacheWrite - cacheWrite1h;
5
7
  usage.cost.input = model.cost.input / 1e6 * usage.input;
6
8
  usage.cost.output = model.cost.output / 1e6 * usage.output;
7
9
  usage.cost.cacheRead = model.cost.cacheRead / 1e6 * usage.cacheRead;
8
- usage.cost.cacheWrite = model.cost.cacheWrite / 1e6 * usage.cacheWrite;
10
+ usage.cost.cacheWrite = (model.cost.cacheWrite * cacheWrite5m + model.cost.input * 2 * cacheWrite1h) / 1e6;
9
11
  usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
10
12
  return usage.cost;
11
13
  }
@@ -3,10 +3,10 @@ import { i as formatThrownValue, n as createAssistantMessageDiagnostic, t as app
3
3
  import { t as AssistantMessageEventStream } from "./event-stream-D8n2uFee.mjs";
4
4
  import { n as getAiTransportHost, r as resolveAiTransportHeaderSentinels } from "./host-4t713IeR.mjs";
5
5
  import { t as headersToRecord } from "./headers-B_e4-1J0.mjs";
6
- import { s as buildBaseOptions, x as stripSystemPromptCacheBoundary } from "./transform-messages-DNWpwvXA.mjs";
7
- import { A as supportsOpenAITemperature, a as resolveResponsesReasoningEffort, i as processResponsesStream, n as convertResponsesMessages, s as convertResponsesToolPayload } from "./openai-responses-shared-B9mtGvXq.mjs";
6
+ import { l as buildBaseOptions, w as stripSystemPromptCacheBoundary } from "./transform-messages-eb6lxDLB.mjs";
7
+ import { M as supportsOpenAITemperature, a as resolveResponsesReasoningEffort, i as processResponsesStream, n as convertResponsesMessages, s as convertResponsesToolPayload } from "./openai-responses-shared-CwnGU0KW.mjs";
8
8
  import { i as getFirstStreamEventTimeoutMs, o as clampTimerTimeoutMs, r as getFirstStreamEventTimeoutHandler, s as resolveTimerTimeoutMs, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-RjWszj8c.mjs";
9
- import { a as clampOpenAIPromptCacheKey } from "./openai-tool-projection-cTLC4F65.mjs";
9
+ import { a as clampOpenAIPromptCacheKey } from "./openai-tool-projection-WHf2nuOI.mjs";
10
10
  import { parseRetryAfterHttpDateMs } from "./internal/retry-after.mjs";
11
11
  import { i as registerSessionResourceCleanup, n as resolveOpenAICodexAccountId } from "./openai-chatgpt-jwt-DhAAzLkj.mjs";
12
12
  import { t as createSseByteGuard } from "./streaming-byte-guard-BrbkbwUu.mjs";
@@ -38,7 +38,7 @@ function loadNodeOs() {
38
38
  }
39
39
  const os = loadNodeOs();
40
40
  const DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
41
- const MAX_RETRIES = 3;
41
+ const DEFAULT_MAX_RETRIES = 3;
42
42
  const BASE_DELAY_MS = 1e3;
43
43
  const REQUEST_COMPRESSION_ZSTD_LEVEL = 3;
44
44
  const CODEX_TOOL_CALL_PROVIDERS = /* @__PURE__ */ new Set(["openai", "opencode"]);
@@ -127,8 +127,9 @@ const streamOpenAICodexResponses = (model, context, options) => {
127
127
  let body = buildRequestBody(model, context, options);
128
128
  const nextBody = await options?.onPayload?.(body, model);
129
129
  if (nextBody !== void 0) body = nextBody;
130
- const websocketRequestId = options?.sessionId || createCodexRequestId();
131
- const sseHeaders = buildSSEHeaders(modelHeaders, optionHeaders, accountId, apiKey, options?.sessionId);
130
+ const sessionId = clampOpenAIPromptCacheKey(options?.sessionId);
131
+ const websocketRequestId = sessionId || createCodexRequestId();
132
+ const sseHeaders = buildSSEHeaders(modelHeaders, optionHeaders, accountId, apiKey, sessionId);
132
133
  const websocketHeaders = buildWebSocketHeaders(modelHeaders, optionHeaders, accountId, apiKey, websocketRequestId);
133
134
  const bodyJson = JSON.stringify(body);
134
135
  requestTimeoutMs = resolveRequestTimeoutMs(options);
@@ -184,7 +185,8 @@ const streamOpenAICodexResponses = (model, context, options) => {
184
185
  const sseBody = compressedBody ?? bodyJson;
185
186
  let response;
186
187
  let lastError;
187
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
188
+ const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
189
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
188
190
  if (activeSignal?.aborted) throw new Error("Request was aborted");
189
191
  try {
190
192
  response = await fetch(resolveCodexUrl(model.baseUrl), {
@@ -199,7 +201,7 @@ const streamOpenAICodexResponses = (model, context, options) => {
199
201
  }, model);
200
202
  if (response.ok) break;
201
203
  const errorText = await readChatGptResponsesErrorTextLimited(response);
202
- if (attempt < MAX_RETRIES && isRetryableError(response.status, errorText)) {
204
+ if (attempt < maxRetries && isRetryableError(response.status, errorText)) {
203
205
  let delayMs = BASE_DELAY_MS * 2 ** attempt;
204
206
  const retryAfterMs = response.headers.get("retry-after-ms");
205
207
  if (retryAfterMs !== null) {
@@ -233,7 +235,7 @@ const streamOpenAICodexResponses = (model, context, options) => {
233
235
  if (error.name === "TimeoutError" && requestTimeoutMs !== void 0) throw new Error(`Request timed out after ${requestTimeoutMs}ms`, { cause: error });
234
236
  }
235
237
  lastError = error instanceof Error ? error : new Error(String(error));
236
- if (attempt < MAX_RETRIES && !lastError.message.includes("usage limit")) {
238
+ if (attempt < maxRetries && !lastError.message.includes("usage limit")) {
237
239
  await sleepWithAbort(BASE_DELAY_MS * 2 ** attempt, activeSignal);
238
240
  continue;
239
241
  }
@@ -1,15 +1,16 @@
1
1
  import { n as getEnvApiKey, r as __exportAll } from "./env-api-keys-DnhYpc27.mjs";
2
2
  import { t as AssistantMessageEventStream } from "./event-stream-D8n2uFee.mjs";
3
3
  import { n as getAiTransportHost } from "./host-4t713IeR.mjs";
4
- import { n as calculateCost, r as clampThinkingLevel, t as applyProviderReportedUsageCost } from "./model-utils-Q1LSRIdo.mjs";
4
+ import { n as calculateCost, r as clampThinkingLevel, t as applyProviderReportedUsageCost } from "./model-utils-1GiZ2_rr.mjs";
5
5
  import { t as headersToRecord } from "./headers-B_e4-1J0.mjs";
6
6
  import { n as parseStreamingJson } from "./json-parse-BvXNt1-7.mjs";
7
7
  import { t as sanitizeSurrogates } from "./sanitize-unicode-DT5o51ur.mjs";
8
- import { b as splitSystemPromptCacheBoundary, i as extractToolResultText, n as describeToolResultMediaPlaceholder, s as buildBaseOptions, t as transformMessages, x as stripSystemPromptCacheBoundary } from "./transform-messages-DNWpwvXA.mjs";
8
+ import { C as splitSystemPromptCacheBoundary, i as extractToolResultText, l as buildBaseOptions, n as describeToolResultMediaPlaceholder, o as isImageWithMediaPayload, t as transformMessages, w as stripSystemPromptCacheBoundary } from "./transform-messages-eb6lxDLB.mjs";
9
9
  import { a as resolveCacheRetention, i as resolveCloudflareBaseUrl, n as hasCopilotVisionInput, r as isCloudflareProvider, t as buildCopilotDynamicHeaders } from "./github-copilot-headers-BsH5cqGj.mjs";
10
+ import { t as formatProviderError } from "./provider-error-LMfTEkfO.mjs";
10
11
  import { t as createReasoningTagTextPartitioner } from "./reasoning-tag-text-partitioner-BlVe67g6.mjs";
11
12
  import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-RjWszj8c.mjs";
12
- import { a as clampOpenAIPromptCacheKey, n as reconcileOpenAICompletionsToolChoice, t as projectOpenAITools } from "./openai-tool-projection-cTLC4F65.mjs";
13
+ import { a as clampOpenAIPromptCacheKey, n as reconcileOpenAICompletionsToolChoice, t as projectOpenAITools } from "./openai-tool-projection-WHf2nuOI.mjs";
13
14
  import OpenAI from "openai";
14
15
  //#region packages/ai/src/providers/openai-stop-reason.ts
15
16
  function mapOpenAIStopReason(reason, options) {
@@ -67,14 +68,16 @@ function isThinkingContentBlock(block) {
67
68
  function isToolCallBlock(block) {
68
69
  return block.type === "toolCall";
69
70
  }
70
- function isImageContentBlock(block) {
71
- return block.type === "image";
72
- }
73
71
  const EMPTY_TOOL_RESULT_TEXT = "(no output)";
74
72
  function sanitizeToolResultText(text, fallback) {
75
73
  const sanitized = sanitizeSurrogates(text);
76
74
  return sanitized.trim().length > 0 ? sanitized : fallback;
77
75
  }
76
+ function isEncryptedReasoningDetail(detail) {
77
+ if (typeof detail !== "object" || detail === null) return false;
78
+ const candidate = detail;
79
+ return candidate.type === "reasoning.encrypted" && typeof candidate.id === "string" && candidate.id.length > 0 && typeof candidate.data === "string" && candidate.data.length > 0;
80
+ }
78
81
  const streamOpenAICompletions = (model, context, options) => {
79
82
  const stream = new AssistantMessageEventStream();
80
83
  (async () => {
@@ -115,7 +118,7 @@ const streamOpenAICompletions = (model, context, options) => {
115
118
  const requestOptions = {
116
119
  signal: firstEventAbort.signal,
117
120
  ...options?.timeoutMs !== void 0 ? { timeout: options.timeoutMs } : {},
118
- ...options?.maxRetries !== void 0 ? { maxRetries: options.maxRetries } : {}
121
+ maxRetries: options?.maxRetries ?? 0
119
122
  };
120
123
  const { data: openaiStream, response } = await client.chat.completions.create(params, requestOptions).withResponse();
121
124
  await options?.onResponse?.({
@@ -132,6 +135,7 @@ const streamOpenAICompletions = (model, context, options) => {
132
135
  const toolCallBlocksByIndex = /* @__PURE__ */ new Map();
133
136
  const toolCallBlocksById = /* @__PURE__ */ new Map();
134
137
  const toolCallBlocksByFirstId = /* @__PURE__ */ new Map();
138
+ const pendingReasoningDetailsByToolCallId = /* @__PURE__ */ new Map();
135
139
  const blocks = output.content;
136
140
  const finishedBlocks = /* @__PURE__ */ new Set();
137
141
  const contentIndices = /* @__PURE__ */ new WeakMap();
@@ -141,7 +145,13 @@ const streamOpenAICompletions = (model, context, options) => {
141
145
  };
142
146
  const getContentIndex = (block) => contentIndices.get(block) ?? -1;
143
147
  const rememberFirstToolCallById = (id, block) => {
144
- if (!toolCallBlocksByFirstId.has(id)) toolCallBlocksByFirstId.set(id, block);
148
+ if (toolCallBlocksByFirstId.has(id)) return;
149
+ toolCallBlocksByFirstId.set(id, block);
150
+ const pendingDetail = pendingReasoningDetailsByToolCallId.get(id);
151
+ if (pendingDetail) {
152
+ block.thoughtSignature = pendingDetail;
153
+ pendingReasoningDetailsByToolCallId.delete(id);
154
+ }
145
155
  };
146
156
  const finishBlock = (block) => {
147
157
  const contentIndex = getContentIndex(block);
@@ -344,10 +354,12 @@ const streamOpenAICompletions = (model, context, options) => {
344
354
  }
345
355
  }
346
356
  const reasoningDetails = choiceDelta.reasoning_details;
347
- if (reasoningDetails && Array.isArray(reasoningDetails)) {
348
- for (const detail of reasoningDetails) if (detail.type === "reasoning.encrypted" && detail.id && detail.data) {
357
+ if (Array.isArray(reasoningDetails)) {
358
+ for (const detail of reasoningDetails) if (isEncryptedReasoningDetail(detail)) {
359
+ const serializedDetail = JSON.stringify(detail);
349
360
  const matchingToolCall = toolCallBlocksByFirstId.get(detail.id);
350
- if (matchingToolCall) matchingToolCall.thoughtSignature = JSON.stringify(detail);
361
+ if (matchingToolCall) matchingToolCall.thoughtSignature = serializedDetail;
362
+ else pendingReasoningDetailsByToolCallId.set(detail.id, serializedDetail);
351
363
  }
352
364
  }
353
365
  }
@@ -376,9 +388,9 @@ const streamOpenAICompletions = (model, context, options) => {
376
388
  delete block.streamIndex;
377
389
  }
378
390
  output.stopReason = options?.signal?.aborted ? "aborted" : "error";
379
- output.errorMessage = error instanceof Error ? error.message : JSON.stringify(error);
391
+ output.errorMessage = formatProviderError(error);
380
392
  const rawMetadata = error?.error?.metadata?.raw;
381
- if (rawMetadata) output.errorMessage += `\n${rawMetadata}`;
393
+ if (rawMetadata && !output.errorMessage.includes(rawMetadata)) output.errorMessage += `\n${rawMetadata}`;
382
394
  stream.push({
383
395
  type: "error",
384
396
  reason: output.stopReason,
@@ -415,7 +427,8 @@ function createClient(model, context, apiKey, optionsHeaders, sessionId, compat
415
427
  });
416
428
  Object.assign(headers, copilotHeaders);
417
429
  }
418
- if (sessionId && compat.sendSessionAffinityHeaders) {
430
+ if (sessionId && compat.sendSessionAffinityHeaders) if (compat.sessionAffinityFormat === "openrouter") headers["x-session-id"] = sessionId;
431
+ else {
419
432
  headers.session_id = sessionId;
420
433
  headers["x-client-request-id"] = sessionId;
421
434
  headers["x-session-affinity"] = sessionId;
@@ -472,7 +485,10 @@ function buildParams(model, context, options, compat = getCompat(model), cacheRe
472
485
  const toolChoice = reconcileOpenAICompletionsToolChoice(options.toolChoice, toolProjection ?? projectOpenAITools([]));
473
486
  if (toolChoice !== void 0) params.tool_choice = toolChoice;
474
487
  }
475
- if (compat.thinkingFormat === "zai" && model.reasoning) params.enable_thinking = Boolean(options?.reasoningEffort);
488
+ if (compat.thinkingFormat === "zai" && model.reasoning) params.thinking = options?.reasoningEffort ? {
489
+ type: "enabled",
490
+ clear_thinking: false
491
+ } : { type: "disabled" };
476
492
  else if (compat.thinkingFormat === "qwen" && model.reasoning) params.enable_thinking = Boolean(options?.reasoningEffort);
477
493
  else if (compat.thinkingFormat === "qwen-chat-template" && model.reasoning) params.chat_template_kwargs = {
478
494
  enable_thinking: Boolean(options?.reasoningEffort),
@@ -494,7 +510,7 @@ function buildParams(model, context, options, compat = getCompat(model), cacheRe
494
510
  const offValue = model.thinkingLevelMap?.off;
495
511
  if (typeof offValue === "string") params.reasoning_effort = offValue;
496
512
  }
497
- if (model.baseUrl.includes("openrouter.ai") && model.compat?.openRouterRouting) params.provider = model.compat.openRouterRouting;
513
+ if (model.compat?.openRouterRouting) params.provider = model.compat.openRouterRouting;
498
514
  if (model.baseUrl.includes("ai-gateway.vercel.sh") && model.compat?.vercelGatewayRouting) {
499
515
  const routing = model.compat.vercelGatewayRouting;
500
516
  if (routing.only || routing.order) {
@@ -699,7 +715,7 @@ function convertMessages(model, context, compat, options = {}) {
699
715
  if (toolMsg?.role !== "toolResult") break;
700
716
  const textResult = extractToolResultText(toolMsg.content);
701
717
  const mediaPlaceholder = describeToolResultMediaPlaceholder(toolMsg.content);
702
- const hasImages = toolMsg.content.some((c) => c.type === "image");
718
+ const hasImages = toolMsg.content.some(isImageWithMediaPayload);
703
719
  const toolResultMsg = {
704
720
  role: "tool",
705
721
  content: sanitizeToolResultText(textResult, mediaPlaceholder ?? EMPTY_TOOL_RESULT_TEXT),
@@ -708,7 +724,7 @@ function convertMessages(model, context, compat, options = {}) {
708
724
  if (compat.requiresToolResultName && toolMsg.toolName) toolResultMsg.name = toolMsg.toolName;
709
725
  params.push(toolResultMsg);
710
726
  if (hasImages && model.input.includes("image")) {
711
- for (const block of toolMsg.content) if (isImageContentBlock(block)) imageBlocks.push({
727
+ for (const block of toolMsg.content) if (isImageWithMediaPayload(block)) imageBlocks.push({
712
728
  type: "image_url",
713
729
  image_url: { url: `data:${block.mimeType};base64,${block.data}` }
714
730
  });
@@ -788,15 +804,18 @@ function detectCompat(model) {
788
804
  const isMoonshot = provider === "moonshotai" || provider === "moonshotai-cn" || baseUrl.includes("api.moonshot.");
789
805
  const isCloudflareWorkersAI = provider === "cloudflare-workers-ai" || baseUrl.includes("api.cloudflare.com");
790
806
  const isCloudflareAiGateway = provider === "cloudflare-ai-gateway" || baseUrl.includes("gateway.ai.cloudflare.com");
807
+ const isOpenRouter = provider === "openrouter" || baseUrl.includes("openrouter.ai");
791
808
  const isNonStandard = provider === "cerebras" || baseUrl.includes("cerebras.ai") || provider === "xai" || baseUrl.includes("api.x.ai") || isTogether || baseUrl.includes("chutes.ai") || baseUrl.includes("deepseek.com") || isZai || isMoonshot || provider === "opencode" || baseUrl.includes("opencode.ai") || isCloudflareWorkersAI || isCloudflareAiGateway;
792
- const useMaxTokens = baseUrl.includes("chutes.ai") || isMoonshot || isCloudflareAiGateway || isTogether;
809
+ const useMaxTokens = baseUrl.includes("chutes.ai") || isMoonshot || isCloudflareAiGateway || isTogether || isZai;
793
810
  const isGrok = provider === "xai" || baseUrl.includes("api.x.ai");
794
811
  const isDeepSeek = provider === "deepseek" || baseUrl.includes("deepseek.com");
795
812
  const isXiaomi = provider === "xiaomi" || baseUrl.includes("xiaomimimo.com");
813
+ const supportsOpenRouterDeveloperRole = isOpenRouter && (model.id.startsWith("anthropic/") || model.id.startsWith("openai/"));
814
+ const usesOpenRouterSessionAffinity = isOpenRouter || model.compat?.thinkingFormat === "openrouter" || model.compat?.openRouterRouting !== void 0;
796
815
  const cacheControlFormat = provider === "openrouter" && model.id.startsWith("anthropic/") ? "anthropic" : void 0;
797
816
  return {
798
817
  supportsStore: !isNonStandard,
799
- supportsDeveloperRole: !isNonStandard,
818
+ supportsDeveloperRole: supportsOpenRouterDeveloperRole || !isNonStandard && !isOpenRouter,
800
819
  supportsReasoningEffort: !isGrok && !isZai && !isMoonshot && !isTogether && !isCloudflareAiGateway,
801
820
  supportsUsageInStreaming: true,
802
821
  maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
@@ -804,13 +823,14 @@ function detectCompat(model) {
804
823
  requiresAssistantAfterToolResult: false,
805
824
  requiresThinkingAsText: false,
806
825
  requiresReasoningContentOnAssistantMessages: isDeepSeek || isXiaomi,
807
- thinkingFormat: isDeepSeek ? "deepseek" : isXiaomi ? "deepseek" : isZai ? "zai" : isTogether ? "together" : provider === "openrouter" || baseUrl.includes("openrouter.ai") ? "openrouter" : "openai",
826
+ thinkingFormat: isDeepSeek ? "deepseek" : isXiaomi ? "deepseek" : isZai ? "zai" : isTogether ? "together" : isOpenRouter ? "openrouter" : "openai",
808
827
  openRouterRouting: {},
809
828
  vercelGatewayRouting: {},
810
829
  zaiToolStream: false,
811
830
  supportsStrictMode: !isMoonshot && !isTogether && !isCloudflareAiGateway,
812
831
  cacheControlFormat,
813
832
  sendSessionAffinityHeaders: false,
833
+ sessionAffinityFormat: usesOpenRouterSessionAffinity ? "openrouter" : "openai",
814
834
  supportsPromptCacheKey: false,
815
835
  supportsLongCacheRetention: !(isTogether || isCloudflareWorkersAI || isCloudflareAiGateway)
816
836
  };
@@ -839,6 +859,7 @@ function getCompat(model) {
839
859
  supportsStrictMode: model.compat.supportsStrictMode ?? detected.supportsStrictMode,
840
860
  cacheControlFormat: model.compat.cacheControlFormat ?? detected.cacheControlFormat,
841
861
  sendSessionAffinityHeaders: model.compat.sendSessionAffinityHeaders ?? detected.sendSessionAffinityHeaders,
862
+ sessionAffinityFormat: detected.sessionAffinityFormat,
842
863
  supportsPromptCacheKey: model.compat.supportsPromptCacheKey ?? detected.supportsPromptCacheKey,
843
864
  supportsLongCacheRetention: model.compat.supportsLongCacheRetention ?? detected.supportsLongCacheRetention
844
865
  };
@@ -1,10 +1,10 @@
1
1
  import { n as getEnvApiKey, r as __exportAll } from "./env-api-keys-DnhYpc27.mjs";
2
2
  import { t as AssistantMessageEventStream } from "./event-stream-D8n2uFee.mjs";
3
3
  import { n as getAiTransportHost } from "./host-4t713IeR.mjs";
4
- import { s as buildBaseOptions } from "./transform-messages-DNWpwvXA.mjs";
4
+ import { l as buildBaseOptions } from "./transform-messages-eb6lxDLB.mjs";
5
5
  import { a as resolveCacheRetention, i as resolveCloudflareBaseUrl, n as hasCopilotVisionInput, r as isCloudflareProvider, t as buildCopilotDynamicHeaders } from "./github-copilot-headers-BsH5cqGj.mjs";
6
- import { A as supportsOpenAITemperature, a as resolveResponsesReasoningEffort, n as convertResponsesMessages, o as runResponsesStreamLifecycle, r as createResponsesAssistantOutput, t as applyCommonResponsesParams } from "./openai-responses-shared-B9mtGvXq.mjs";
7
- import { a as clampOpenAIPromptCacheKey } from "./openai-tool-projection-cTLC4F65.mjs";
6
+ import { M as supportsOpenAITemperature, a as resolveResponsesReasoningEffort, n as convertResponsesMessages, o as runResponsesStreamLifecycle, r as createResponsesAssistantOutput, t as applyCommonResponsesParams } from "./openai-responses-shared-CwnGU0KW.mjs";
7
+ import { a as clampOpenAIPromptCacheKey } from "./openai-tool-projection-WHf2nuOI.mjs";
8
8
  import OpenAI from "openai";
9
9
  //#region packages/ai/src/providers/openai-responses.ts
10
10
  var openai_responses_exports = /* @__PURE__ */ __exportAll({