@openclaw/ai 0.0.0 → 2026.7.1-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +27 -3
  3. package/dist/anthropic-B5gZQM5X.mjs +1383 -0
  4. package/dist/api-registry-BXYnCOIR.d.mts +33 -0
  5. package/dist/azure-openai-responses-DNoSk8Uy.mjs +141 -0
  6. package/dist/azure-openai-responses-client-compat-a_O_GVQV.mjs +41 -0
  7. package/dist/diagnostics-BaTA9eVl.d.mts +25 -0
  8. package/dist/diagnostics-COpOtRwq.mjs +36 -0
  9. package/dist/diagnostics.d.mts +2 -0
  10. package/dist/diagnostics.mjs +2 -0
  11. package/dist/env-api-keys-CtMlqaQ4.mjs +171 -0
  12. package/dist/event-stream-0nZeBKl2.d.mts +26 -0
  13. package/dist/event-stream-ReMmOTzX.mjs +65 -0
  14. package/dist/event-stream.d.mts +2 -0
  15. package/dist/event-stream.mjs +2 -0
  16. package/dist/github-copilot-headers-BsH5cqGj.mjs +48 -0
  17. package/dist/google-D6sIQ1bL.mjs +55 -0
  18. package/dist/google-shared-ZPSl2qTi.mjs +548 -0
  19. package/dist/google-vertex-rDGwkoZK.mjs +111 -0
  20. package/dist/hash-CHgqbJmD.mjs +16 -0
  21. package/dist/headers-B_e4-1J0.mjs +9 -0
  22. package/dist/host-4t713IeR.mjs +37 -0
  23. package/dist/index-BoTnz8cv.d.mts +74 -0
  24. package/dist/index.d.mts +69 -0
  25. package/dist/index.mjs +7 -0
  26. package/dist/internal/anthropic.d.mts +234 -0
  27. package/dist/internal/anthropic.mjs +4 -0
  28. package/dist/internal/openai.d.mts +244 -0
  29. package/dist/internal/openai.mjs +7 -0
  30. package/dist/internal/runtime.d.mts +245 -0
  31. package/dist/internal/runtime.mjs +176 -0
  32. package/dist/internal/shared.d.mts +48 -0
  33. package/dist/internal/shared.mjs +3 -0
  34. package/dist/json-parse-DzNSIQBq.mjs +134 -0
  35. package/dist/llm-request-activity-CehVkZP-.mjs +35 -0
  36. package/dist/mistral-CePVNdws.mjs +563 -0
  37. package/dist/model-utils-DgmOla96.mjs +69 -0
  38. package/dist/openai-chatgpt-jwt-DhAAzLkj.mjs +39 -0
  39. package/dist/openai-chatgpt-responses-DVC4Bk_A.mjs +1068 -0
  40. package/dist/openai-completions-B9QLIq2U.mjs +844 -0
  41. package/dist/openai-responses-B6LylGxM.mjs +136 -0
  42. package/dist/openai-responses-shared-sj2YUPYc.mjs +1944 -0
  43. package/dist/openai-tool-projection-BknoV11q.mjs +195 -0
  44. package/dist/providers.d.mts +11 -0
  45. package/dist/providers.mjs +109 -0
  46. package/dist/reasoning-tag-text-partitioner-axhAdUwg.mjs +394 -0
  47. package/dist/sanitize-unicode-BZiVbGwK.d.mts +24 -0
  48. package/dist/sanitize-unicode-DT5o51ur.mjs +26 -0
  49. package/dist/src-CZ503MYJ.mjs +99 -0
  50. package/dist/stream-CREqxHgU.mjs +74 -0
  51. package/dist/stream-first-event-timeout-RjWszj8c.mjs +106 -0
  52. package/dist/streaming-byte-guard-BrbkbwUu.mjs +46 -0
  53. package/dist/tool-schema-json-projection-BXtBc_mD.mjs +74 -0
  54. package/dist/transform-messages-BhGF_fF4.mjs +507 -0
  55. package/dist/types-BVVgDSdq.d.mts +1 -0
  56. package/dist/types-DRgdPqaZ.d.mts +587 -0
  57. package/dist/types.d.mts +6 -0
  58. package/dist/types.mjs +5 -0
  59. package/dist/validation-BDMWOr8d.d.mts +9 -0
  60. package/dist/validation-FrchoOlv.mjs +199 -0
  61. package/dist/validation.d.mts +2 -0
  62. package/dist/validation.mjs +2 -0
  63. package/npm-shrinkwrap.json +645 -0
  64. package/package.json +74 -2
@@ -0,0 +1,563 @@
1
+ import { n as getEnvApiKey } from "./env-api-keys-CtMlqaQ4.mjs";
2
+ import { t as AssistantMessageEventStream } from "./event-stream-ReMmOTzX.mjs";
3
+ import { n as getAiTransportHost } from "./host-4t713IeR.mjs";
4
+ import { n as calculateCost, r as clampThinkingLevel } from "./model-utils-DgmOla96.mjs";
5
+ import { n as parseStreamingJson } from "./json-parse-DzNSIQBq.mjs";
6
+ import { t as sanitizeSurrogates } from "./sanitize-unicode-DT5o51ur.mjs";
7
+ import { b as stripSystemPromptCacheBoundary, i as extractToolResultText, n as describeToolResultMediaPlaceholder, o as buildBaseOptions, t as transformMessages } from "./transform-messages-BhGF_fF4.mjs";
8
+ import { t as shortHash } from "./hash-CHgqbJmD.mjs";
9
+ import { t as createSseByteGuard } from "./streaming-byte-guard-BrbkbwUu.mjs";
10
+ import { HTTPClient, Mistral } from "@mistralai/mistralai";
11
+ //#region packages/ai/src/providers/mistral.ts
12
+ const MISTRAL_TOOL_CALL_ID_LENGTH = 9;
13
+ const MAX_MISTRAL_ERROR_BODY_CHARS = 4e3;
14
+ const MISTRAL_STREAM_BODY_MAX_BYTES = 16 * 1024 * 1024;
15
+ /**
16
+ * Builds a `Fetcher` that wraps the default `fetch` with a 16 MiB byte cap
17
+ * on streamed response bodies. The wrapped `Response.body` exposes a
18
+ * `ReadableStream` whose chunks flow through `createSseByteGuard`, so the
19
+ * SDK's internal SSE parser (`EventStream` in
20
+ * `@mistralai/mistralai/lib/event-streams.ts`) reads exactly as it would on
21
+ * an unbounded body — but bounded.
22
+ *
23
+ * Bodyless responses (no `body` or no `getReader`) are returned unchanged so
24
+ * the SDK's error-path `res.arrayBuffer()` call still works.
25
+ */
26
+ function createBoundedMistralFetcher(maxBytes = MISTRAL_STREAM_BODY_MAX_BYTES, upstreamFetch = fetch) {
27
+ return async (input, init) => {
28
+ const response = init == null ? await upstreamFetch(input) : await upstreamFetch(input, init);
29
+ if (!response.body || typeof response.body.getReader !== "function") return response;
30
+ const guard = createSseByteGuard(response.body.getReader(), {
31
+ maxBytes,
32
+ onOverflow: ({ size, maxBytes: cap }) => /* @__PURE__ */ new Error(`mistral: stream body exceeds ${cap} bytes (got ${size})`)
33
+ });
34
+ const guardedStream = new ReadableStream({
35
+ async pull(controller) {
36
+ const { done, value } = await guard.read();
37
+ if (done) {
38
+ controller.close();
39
+ return;
40
+ }
41
+ controller.enqueue(value);
42
+ },
43
+ async cancel(reason) {
44
+ await guard.cancel(reason);
45
+ }
46
+ });
47
+ return new Response(guardedStream, {
48
+ status: response.status,
49
+ statusText: response.statusText,
50
+ headers: response.headers
51
+ });
52
+ };
53
+ }
54
+ /**
55
+ * Stream responses from Mistral using `chat.stream`.
56
+ */
57
+ const streamMistral = (model, context, options) => {
58
+ const stream = new AssistantMessageEventStream();
59
+ (async () => {
60
+ const output = createOutput(model);
61
+ try {
62
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider);
63
+ if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
64
+ const mistral = new Mistral({
65
+ apiKey,
66
+ serverURL: model.baseUrl,
67
+ httpClient: new HTTPClient({ fetcher: createBoundedMistralFetcher(MISTRAL_STREAM_BODY_MAX_BYTES, getAiTransportHost().buildModelFetch(model) ?? fetch) })
68
+ });
69
+ const normalizeMistralToolCallId = createMistralToolCallIdNormalizer();
70
+ let payload = buildChatPayload(model, context, transformMessages(context.messages, model, (id) => normalizeMistralToolCallId(id)), options);
71
+ const nextPayload = await options?.onPayload?.(payload, model);
72
+ if (nextPayload !== void 0) payload = nextPayload;
73
+ const mistralStream = await mistral.chat.stream(payload, buildRequestOptions(model, options));
74
+ stream.push({
75
+ type: "start",
76
+ partial: output
77
+ });
78
+ await consumeChatStream(model, output, stream, mistralStream);
79
+ if (options?.signal?.aborted) throw new Error("Request was aborted");
80
+ if (output.stopReason === "aborted" || output.stopReason === "error") throw new Error("An unknown error occurred");
81
+ stream.push({
82
+ type: "done",
83
+ reason: output.stopReason,
84
+ message: output
85
+ });
86
+ stream.end();
87
+ } catch (error) {
88
+ for (const block of output.content) delete block.partialArgs;
89
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
90
+ output.errorMessage = formatMistralError(error);
91
+ stream.push({
92
+ type: "error",
93
+ reason: output.stopReason,
94
+ error: output
95
+ });
96
+ stream.end();
97
+ }
98
+ })();
99
+ return stream;
100
+ };
101
+ /**
102
+ * Maps provider-agnostic `SimpleStreamOptions` to Mistral options.
103
+ */
104
+ const streamSimpleMistral = (model, context, options) => {
105
+ const apiKey = options?.apiKey || getEnvApiKey(model.provider);
106
+ if (!apiKey) throw new Error(`No API key for provider: ${model.provider}`);
107
+ const base = buildBaseOptions(model, options, apiKey);
108
+ const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : void 0;
109
+ const reasoning = clampedReasoning === "off" ? void 0 : clampedReasoning;
110
+ const shouldUseReasoning = model.reasoning && reasoning !== void 0;
111
+ return streamMistral(model, context, {
112
+ ...base,
113
+ promptMode: shouldUseReasoning && usesPromptModeReasoning(model) ? "reasoning" : void 0,
114
+ reasoningEffort: shouldUseReasoning && usesReasoningEffort(model) ? mapReasoningEffort(model, reasoning) : void 0
115
+ });
116
+ };
117
+ function createOutput(model) {
118
+ return {
119
+ role: "assistant",
120
+ content: [],
121
+ api: model.api,
122
+ provider: model.provider,
123
+ model: model.id,
124
+ usage: {
125
+ input: 0,
126
+ output: 0,
127
+ cacheRead: 0,
128
+ cacheWrite: 0,
129
+ totalTokens: 0,
130
+ cost: {
131
+ input: 0,
132
+ output: 0,
133
+ cacheRead: 0,
134
+ cacheWrite: 0,
135
+ total: 0
136
+ }
137
+ },
138
+ stopReason: "stop",
139
+ timestamp: Date.now()
140
+ };
141
+ }
142
+ function createMistralToolCallIdNormalizer() {
143
+ const idMap = /* @__PURE__ */ new Map();
144
+ const reverseMap = /* @__PURE__ */ new Map();
145
+ return (id) => {
146
+ const existing = idMap.get(id);
147
+ if (existing) return existing;
148
+ let attempt = 0;
149
+ while (true) {
150
+ const candidate = deriveMistralToolCallId(id, attempt);
151
+ const owner = reverseMap.get(candidate);
152
+ if (!owner || owner === id) {
153
+ idMap.set(id, candidate);
154
+ reverseMap.set(candidate, id);
155
+ return candidate;
156
+ }
157
+ attempt++;
158
+ }
159
+ };
160
+ }
161
+ function deriveMistralToolCallId(id, attempt) {
162
+ const normalized = id.replace(/[^a-zA-Z0-9]/g, "");
163
+ if (attempt === 0 && normalized.length === MISTRAL_TOOL_CALL_ID_LENGTH) return normalized;
164
+ const seedBase = normalized || id;
165
+ return shortHash(attempt === 0 ? seedBase : `${seedBase}:${attempt}`).replace(/[^a-zA-Z0-9]/g, "").slice(0, MISTRAL_TOOL_CALL_ID_LENGTH);
166
+ }
167
+ function formatMistralError(error) {
168
+ if (error instanceof Error) {
169
+ const sdkError = error;
170
+ const statusCode = typeof sdkError.statusCode === "number" ? sdkError.statusCode : void 0;
171
+ const bodyText = typeof sdkError.body === "string" ? sdkError.body.trim() : void 0;
172
+ if (statusCode !== void 0 && bodyText) return `Mistral API error (${statusCode}): ${truncateErrorText(bodyText, MAX_MISTRAL_ERROR_BODY_CHARS)}`;
173
+ if (statusCode !== void 0) return `Mistral API error (${statusCode}): ${error.message}`;
174
+ return error.message;
175
+ }
176
+ return safeJsonStringify(error);
177
+ }
178
+ function truncateErrorText(text, maxChars) {
179
+ if (text.length <= maxChars) return text;
180
+ return `${text.slice(0, maxChars)}... [truncated ${text.length - maxChars} chars]`;
181
+ }
182
+ function safeJsonStringify(value) {
183
+ try {
184
+ const serialized = JSON.stringify(value);
185
+ return serialized === void 0 ? String(value) : serialized;
186
+ } catch {
187
+ return String(value);
188
+ }
189
+ }
190
+ function buildRequestOptions(model, options) {
191
+ const requestOptions = { retries: { strategy: "none" } };
192
+ if (options?.signal) requestOptions.signal = options.signal;
193
+ const headers = {};
194
+ if (model.headers) Object.assign(headers, model.headers);
195
+ if (options?.headers) Object.assign(headers, options.headers);
196
+ if (options?.sessionId && !headers["x-affinity"]) headers["x-affinity"] = options.sessionId;
197
+ if (Object.keys(headers).length > 0) requestOptions.headers = headers;
198
+ return requestOptions;
199
+ }
200
+ function buildChatPayload(model, context, messages, options) {
201
+ const payload = {
202
+ model: model.id,
203
+ stream: true,
204
+ messages: toChatMessages(messages, model.input.includes("image"))
205
+ };
206
+ let convertedToolNames;
207
+ if (context.tools?.length) {
208
+ const tools = toFunctionTools(context.tools);
209
+ convertedToolNames = new Set(tools.map((tool) => tool.function.name));
210
+ if (tools.length > 0) payload.tools = tools;
211
+ }
212
+ if (options?.temperature !== void 0) payload.temperature = options.temperature;
213
+ if (options?.maxTokens !== void 0) payload.maxTokens = options.maxTokens;
214
+ if (options?.stop !== void 0 && options.stop.length > 0) payload.stop = options.stop;
215
+ if (options?.toolChoice) {
216
+ const toolChoice = mapToolChoice(options.toolChoice, convertedToolNames);
217
+ if (toolChoice) payload.toolChoice = toolChoice;
218
+ }
219
+ if (options?.promptMode) payload.promptMode = options.promptMode;
220
+ if (options?.reasoningEffort) payload.reasoningEffort = options.reasoningEffort;
221
+ if (context.systemPrompt) payload.messages.unshift({
222
+ role: "system",
223
+ content: sanitizeSurrogates(stripSystemPromptCacheBoundary(context.systemPrompt))
224
+ });
225
+ return payload;
226
+ }
227
+ async function consumeChatStream(model, output, stream, mistralStream) {
228
+ let currentBlock = null;
229
+ const blocks = output.content;
230
+ const blockIndex = () => blocks.length - 1;
231
+ const toolBlocksByKey = /* @__PURE__ */ new Map();
232
+ const finishCurrentBlock = (block) => {
233
+ if (!block) return;
234
+ if (block.type === "text") {
235
+ stream.push({
236
+ type: "text_end",
237
+ contentIndex: blockIndex(),
238
+ content: block.text,
239
+ partial: output
240
+ });
241
+ return;
242
+ }
243
+ if (block.type === "thinking") stream.push({
244
+ type: "thinking_end",
245
+ contentIndex: blockIndex(),
246
+ content: block.thinking,
247
+ partial: output
248
+ });
249
+ };
250
+ for await (const event of mistralStream) {
251
+ const chunk = event.data;
252
+ output.responseId ||= chunk.id;
253
+ if (chunk.usage) {
254
+ output.usage.input = chunk.usage.promptTokens || 0;
255
+ output.usage.output = chunk.usage.completionTokens || 0;
256
+ output.usage.cacheRead = 0;
257
+ output.usage.cacheWrite = 0;
258
+ output.usage.totalTokens = chunk.usage.totalTokens || output.usage.input + output.usage.output;
259
+ calculateCost(model, output.usage);
260
+ }
261
+ const choice = chunk.choices[0];
262
+ if (!choice) continue;
263
+ if (choice.finishReason) output.stopReason = mapChatStopReason(choice.finishReason);
264
+ const delta = choice.delta;
265
+ if (delta.content !== null && delta.content !== void 0) {
266
+ const contentItems = typeof delta.content === "string" ? [delta.content] : delta.content;
267
+ for (const item of contentItems) {
268
+ if (typeof item === "string") {
269
+ const textDelta = sanitizeSurrogates(item);
270
+ if (!currentBlock || currentBlock.type !== "text") {
271
+ finishCurrentBlock(currentBlock);
272
+ currentBlock = {
273
+ type: "text",
274
+ text: ""
275
+ };
276
+ output.content.push(currentBlock);
277
+ stream.push({
278
+ type: "text_start",
279
+ contentIndex: blockIndex(),
280
+ partial: output
281
+ });
282
+ }
283
+ currentBlock.text += textDelta;
284
+ stream.push({
285
+ type: "text_delta",
286
+ contentIndex: blockIndex(),
287
+ delta: textDelta,
288
+ partial: output
289
+ });
290
+ continue;
291
+ }
292
+ if (item.type === "thinking") {
293
+ const thinkingDelta = sanitizeSurrogates(item.thinking.map((part) => "text" in part ? part.text : "").filter((text) => text.length > 0).join(""));
294
+ if (!thinkingDelta) continue;
295
+ if (!currentBlock || currentBlock.type !== "thinking") {
296
+ finishCurrentBlock(currentBlock);
297
+ currentBlock = {
298
+ type: "thinking",
299
+ thinking: ""
300
+ };
301
+ output.content.push(currentBlock);
302
+ stream.push({
303
+ type: "thinking_start",
304
+ contentIndex: blockIndex(),
305
+ partial: output
306
+ });
307
+ }
308
+ currentBlock.thinking += thinkingDelta;
309
+ stream.push({
310
+ type: "thinking_delta",
311
+ contentIndex: blockIndex(),
312
+ delta: thinkingDelta,
313
+ partial: output
314
+ });
315
+ continue;
316
+ }
317
+ if (item.type === "text") {
318
+ const textDelta = sanitizeSurrogates(item.text);
319
+ if (!currentBlock || currentBlock.type !== "text") {
320
+ finishCurrentBlock(currentBlock);
321
+ currentBlock = {
322
+ type: "text",
323
+ text: ""
324
+ };
325
+ output.content.push(currentBlock);
326
+ stream.push({
327
+ type: "text_start",
328
+ contentIndex: blockIndex(),
329
+ partial: output
330
+ });
331
+ }
332
+ currentBlock.text += textDelta;
333
+ stream.push({
334
+ type: "text_delta",
335
+ contentIndex: blockIndex(),
336
+ delta: textDelta,
337
+ partial: output
338
+ });
339
+ }
340
+ }
341
+ }
342
+ const toolCalls = delta.toolCalls || [];
343
+ for (const toolCall of toolCalls) {
344
+ if (currentBlock) {
345
+ finishCurrentBlock(currentBlock);
346
+ currentBlock = null;
347
+ }
348
+ const callId = toolCall.id && toolCall.id !== "null" ? toolCall.id : deriveMistralToolCallId(`toolcall:${toolCall.index ?? 0}`, 0);
349
+ const key = `${callId}:${toolCall.index || 0}`;
350
+ const existingIndex = toolBlocksByKey.get(key);
351
+ let block;
352
+ if (existingIndex !== void 0) {
353
+ const existing = output.content[existingIndex];
354
+ if (existing?.type === "toolCall") block = existing;
355
+ }
356
+ if (!block) {
357
+ block = {
358
+ type: "toolCall",
359
+ id: callId,
360
+ name: toolCall.function.name,
361
+ arguments: {},
362
+ partialArgs: ""
363
+ };
364
+ output.content.push(block);
365
+ toolBlocksByKey.set(key, output.content.length - 1);
366
+ stream.push({
367
+ type: "toolcall_start",
368
+ contentIndex: output.content.length - 1,
369
+ partial: output
370
+ });
371
+ }
372
+ const argsDelta = typeof toolCall.function.arguments === "string" ? toolCall.function.arguments : JSON.stringify(toolCall.function.arguments || {});
373
+ block.partialArgs = (block.partialArgs || "") + argsDelta;
374
+ block.arguments = parseStreamingJson(block.partialArgs);
375
+ stream.push({
376
+ type: "toolcall_delta",
377
+ contentIndex: toolBlocksByKey.get(key),
378
+ delta: argsDelta,
379
+ partial: output
380
+ });
381
+ }
382
+ }
383
+ finishCurrentBlock(currentBlock);
384
+ for (const index of toolBlocksByKey.values()) {
385
+ const block = output.content[index];
386
+ if (block.type !== "toolCall") continue;
387
+ const toolBlock = block;
388
+ toolBlock.arguments = parseStreamingJson(toolBlock.partialArgs);
389
+ delete toolBlock.partialArgs;
390
+ stream.push({
391
+ type: "toolcall_end",
392
+ contentIndex: index,
393
+ toolCall: toolBlock,
394
+ partial: output
395
+ });
396
+ }
397
+ }
398
+ function toFunctionTools(tools) {
399
+ return tools.flatMap((tool) => {
400
+ try {
401
+ return {
402
+ type: "function",
403
+ function: {
404
+ name: tool.name,
405
+ description: tool.description,
406
+ parameters: stripSymbolKeys(tool.parameters),
407
+ strict: false
408
+ }
409
+ };
410
+ } catch {
411
+ return [];
412
+ }
413
+ });
414
+ }
415
+ function stripSymbolKeys(value) {
416
+ if (Array.isArray(value)) return value.map((item) => stripSymbolKeys(item));
417
+ if (value && typeof value === "object") {
418
+ const result = {};
419
+ for (const [key, entry] of Object.entries(value)) result[key] = stripSymbolKeys(entry);
420
+ return result;
421
+ }
422
+ return value;
423
+ }
424
+ function toChatMessages(messages, supportsImages) {
425
+ const result = [];
426
+ for (const msg of messages) {
427
+ if (msg.role === "user") {
428
+ if (typeof msg.content === "string") {
429
+ result.push({
430
+ role: "user",
431
+ content: sanitizeSurrogates(msg.content)
432
+ });
433
+ continue;
434
+ }
435
+ const hadImages = msg.content.some((item) => item.type === "image");
436
+ const content = msg.content.filter((item) => item.type === "text" || supportsImages).map((item) => {
437
+ if (item.type === "text") return {
438
+ type: "text",
439
+ text: sanitizeSurrogates(item.text)
440
+ };
441
+ return {
442
+ type: "image_url",
443
+ imageUrl: `data:${item.mimeType};base64,${item.data}`
444
+ };
445
+ });
446
+ if (content.length > 0) {
447
+ result.push({
448
+ role: "user",
449
+ content
450
+ });
451
+ continue;
452
+ }
453
+ if (hadImages && !supportsImages) result.push({
454
+ role: "user",
455
+ content: "(image omitted: model does not support images)"
456
+ });
457
+ continue;
458
+ }
459
+ if (msg.role === "assistant") {
460
+ const contentParts = [];
461
+ const toolCalls = [];
462
+ for (const block of msg.content) {
463
+ if (block.type === "text") {
464
+ if (block.text.trim().length > 0) contentParts.push({
465
+ type: "text",
466
+ text: sanitizeSurrogates(block.text)
467
+ });
468
+ continue;
469
+ }
470
+ if (block.type === "thinking") {
471
+ if (block.thinking.trim().length > 0) contentParts.push({
472
+ type: "thinking",
473
+ thinking: [{
474
+ type: "text",
475
+ text: sanitizeSurrogates(block.thinking)
476
+ }]
477
+ });
478
+ continue;
479
+ }
480
+ toolCalls.push({
481
+ id: block.id,
482
+ type: "function",
483
+ function: {
484
+ name: block.name,
485
+ arguments: JSON.stringify(block.arguments || {})
486
+ }
487
+ });
488
+ }
489
+ const assistantMessage = { role: "assistant" };
490
+ if (contentParts.length > 0) assistantMessage.content = contentParts;
491
+ if (toolCalls.length > 0) assistantMessage.toolCalls = toolCalls;
492
+ if (contentParts.length > 0 || toolCalls.length > 0) result.push(assistantMessage);
493
+ continue;
494
+ }
495
+ const toolContent = [];
496
+ const toolText = buildToolResultText(extractToolResultText(msg.content), describeToolResultMediaPlaceholder(msg.content), msg.content.some((part) => part.type === "image"), supportsImages, msg.isError);
497
+ toolContent.push({
498
+ type: "text",
499
+ text: toolText
500
+ });
501
+ for (const part of msg.content) {
502
+ if (!supportsImages) continue;
503
+ if (part.type !== "image") continue;
504
+ toolContent.push({
505
+ type: "image_url",
506
+ imageUrl: `data:${part.mimeType};base64,${part.data}`
507
+ });
508
+ }
509
+ result.push({
510
+ role: "tool",
511
+ toolCallId: msg.toolCallId,
512
+ name: msg.toolName,
513
+ content: toolContent
514
+ });
515
+ }
516
+ return result;
517
+ }
518
+ function buildToolResultText(text, mediaPlaceholder, hasImages, supportsImages, isError) {
519
+ const trimmed = text.trim();
520
+ const errorPrefix = isError ? "[tool error] " : "";
521
+ if (trimmed.length > 0) return `${errorPrefix}${trimmed}${hasImages && !supportsImages ? "\n[tool image omitted: model does not support images]" : ""}`;
522
+ if (mediaPlaceholder) {
523
+ if (!hasImages || supportsImages) return `${errorPrefix}${mediaPlaceholder}`;
524
+ return `${errorPrefix}${mediaPlaceholder === "(see attached media)" ? "(media omitted: model does not support images)" : "(image omitted: model does not support images)"}`;
525
+ }
526
+ return isError ? "[tool error] (no tool output)" : "(no tool output)";
527
+ }
528
+ function usesReasoningEffort(model) {
529
+ return model.id === "mistral-small-2603" || model.id === "mistral-small-latest" || model.id === "mistral-medium-3-5";
530
+ }
531
+ function usesPromptModeReasoning(model) {
532
+ return model.reasoning && !usesReasoningEffort(model);
533
+ }
534
+ function mapReasoningEffort(model, level) {
535
+ return model.thinkingLevelMap?.[level] ?? "high";
536
+ }
537
+ function mapToolChoice(choice, convertedToolNames) {
538
+ if (!choice) return;
539
+ if (convertedToolNames && convertedToolNames.size === 0) {
540
+ if (choice === "none" || choice === "auto") return choice === "none" ? "none" : void 0;
541
+ throw new Error("Mistral tool_choice requires a tool, but no tools survived schema conversion");
542
+ }
543
+ if (choice === "auto" || choice === "none" || choice === "any" || choice === "required") return choice;
544
+ const toolName = choice.function.name;
545
+ if (convertedToolNames && !convertedToolNames.has(toolName)) throw new Error(`Mistral tool_choice requested unavailable tool "${toolName}" after schema conversion`);
546
+ return {
547
+ type: "function",
548
+ function: { name: toolName }
549
+ };
550
+ }
551
+ function mapChatStopReason(reason) {
552
+ if (reason === null) return "stop";
553
+ switch (reason) {
554
+ case "stop": return "stop";
555
+ case "length":
556
+ case "model_length": return "length";
557
+ case "tool_calls": return "toolUse";
558
+ case "error": return "error";
559
+ default: return "stop";
560
+ }
561
+ }
562
+ //#endregion
563
+ export { createBoundedMistralFetcher, streamMistral, streamSimpleMistral };
@@ -0,0 +1,69 @@
1
+ import { c as resolveClaudeNativeThinkingLevelMap, i as requiresClaudeMandatoryAdaptiveThinking } from "./src-CZ503MYJ.mjs";
2
+ //#region packages/ai/src/model-utils.ts
3
+ /** Calculates and stores model cost fields from token usage and per-million pricing. */
4
+ function calculateCost(model, usage) {
5
+ usage.cost.input = model.cost.input / 1e6 * usage.input;
6
+ usage.cost.output = model.cost.output / 1e6 * usage.output;
7
+ usage.cost.cacheRead = model.cost.cacheRead / 1e6 * usage.cacheRead;
8
+ usage.cost.cacheWrite = model.cost.cacheWrite / 1e6 * usage.cacheWrite;
9
+ usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
10
+ return usage.cost;
11
+ }
12
+ /** Replaces the catalog estimate when the provider reports an authoritative billed total. */
13
+ function applyProviderReportedUsageCost(usage, reportedCost) {
14
+ if (typeof reportedCost !== "number" || !Number.isFinite(reportedCost) || reportedCost < 0) return;
15
+ usage.cost.total = reportedCost;
16
+ usage.cost.totalOrigin = "provider-billed";
17
+ }
18
+ const EXTENDED_THINKING_LEVELS = [
19
+ "off",
20
+ "minimal",
21
+ "low",
22
+ "medium",
23
+ "high",
24
+ "xhigh",
25
+ "max"
26
+ ];
27
+ function resolveThinkingLevelMap(model) {
28
+ return model.api === "anthropic-messages" ? resolveClaudeNativeThinkingLevelMap(model) ?? model.thinkingLevelMap : model.thinkingLevelMap;
29
+ }
30
+ /** Returns thinking levels exposed by a reasoning-capable model. */
31
+ function getSupportedThinkingLevels(model) {
32
+ const mandatoryAdaptiveContract = model.api === "anthropic-messages" && requiresClaudeMandatoryAdaptiveThinking(model);
33
+ if (!model.reasoning && !mandatoryAdaptiveContract) return ["off"];
34
+ const thinkingLevelMap = resolveThinkingLevelMap(model);
35
+ return EXTENDED_THINKING_LEVELS.filter((level) => {
36
+ const mapped = thinkingLevelMap?.[level];
37
+ if (mapped === null) return false;
38
+ if (level === "xhigh" || level === "max") return mapped !== void 0;
39
+ return true;
40
+ });
41
+ }
42
+ /** Clamps a requested thinking level to the closest supported level for a model. */
43
+ function clampThinkingLevel(model, level) {
44
+ const availableLevels = getSupportedThinkingLevels(model);
45
+ if (availableLevels.includes(level)) return level;
46
+ const requestedIndex = EXTENDED_THINKING_LEVELS.indexOf(level);
47
+ if (requestedIndex === -1) return availableLevels[0] ?? "off";
48
+ const thinkingLevelMap = resolveThinkingLevelMap(model);
49
+ if ((level === "xhigh" || level === "max") && thinkingLevelMap?.[level] === null) for (let i = requestedIndex - 1; i >= 0; i--) {
50
+ const candidate = EXTENDED_THINKING_LEVELS[i];
51
+ if (availableLevels.includes(candidate)) return candidate;
52
+ }
53
+ for (let i = requestedIndex; i < EXTENDED_THINKING_LEVELS.length; i++) {
54
+ const candidate = EXTENDED_THINKING_LEVELS[i];
55
+ if (availableLevels.includes(candidate)) return candidate;
56
+ }
57
+ for (let i = requestedIndex - 1; i >= 0; i--) {
58
+ const candidate = EXTENDED_THINKING_LEVELS[i];
59
+ if (availableLevels.includes(candidate)) return candidate;
60
+ }
61
+ return availableLevels[0] ?? "off";
62
+ }
63
+ /** Compares model identity by provider and id. */
64
+ function modelsAreEqual(a, b) {
65
+ if (!a || !b) return false;
66
+ return a.id === b.id && a.provider === b.provider;
67
+ }
68
+ //#endregion
69
+ export { modelsAreEqual as a, getSupportedThinkingLevels as i, calculateCost as n, clampThinkingLevel as r, applyProviderReportedUsageCost as t };
@@ -0,0 +1,39 @@
1
+ //#region packages/ai/src/session-resources.ts
2
+ const sessionResourceCleanups = /* @__PURE__ */ new Set();
3
+ /** Registers a session-resource cleanup hook and returns an unregister function. */
4
+ function registerSessionResourceCleanup(cleanup) {
5
+ sessionResourceCleanups.add(cleanup);
6
+ return () => {
7
+ sessionResourceCleanups.delete(cleanup);
8
+ };
9
+ }
10
+ /** Runs all registered cleanup hooks, aggregating failures after every hook has run. */
11
+ function cleanupSessionResources(sessionId) {
12
+ const errors = [];
13
+ for (const cleanup of sessionResourceCleanups) try {
14
+ cleanup(sessionId);
15
+ } catch (error) {
16
+ errors.push(error);
17
+ }
18
+ if (errors.length > 0) throw new AggregateError(errors, "Failed to cleanup session resources");
19
+ }
20
+ //#endregion
21
+ //#region packages/ai/src/utils/oauth/openai-chatgpt-jwt.ts
22
+ const OPENAI_CODEX_AUTH_CLAIM = "https://api.openai.com/auth";
23
+ function decodeOpenAICodexJwtPayload(token) {
24
+ const parts = token.split(".");
25
+ if (parts.length !== 3) return null;
26
+ try {
27
+ const decoded = Buffer.from(parts[1] ?? "", "base64url").toString("utf8");
28
+ const parsed = JSON.parse(decoded);
29
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+ function resolveOpenAICodexAccountId(token) {
35
+ const accountId = decodeOpenAICodexJwtPayload(token)?.[OPENAI_CODEX_AUTH_CLAIM]?.chatgpt_account_id;
36
+ return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
37
+ }
38
+ //#endregion
39
+ export { registerSessionResourceCleanup as i, resolveOpenAICodexAccountId as n, cleanupSessionResources as r, decodeOpenAICodexJwtPayload as t };