@openclaw/ai 2026.7.2-beta.5 → 2026.7.2-beta.7

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 (47) hide show
  1. package/dist/{anthropic-CQVj3le6.mjs → anthropic-CH4UUnZr.mjs} +14 -463
  2. package/dist/anthropic-usage-DWU-x8MI.mjs +459 -0
  3. package/dist/{azure-openai-responses-DIYgsqFM.mjs → azure-openai-responses-CImcwB83.mjs} +4 -3
  4. package/dist/azure-openai-responses-client-compat-C7K7QfUE.mjs +62 -0
  5. package/dist/cache-retention-0x979a5V.mjs +12 -0
  6. package/dist/deferred-event-buffer-DAvyP7qA.mjs +19 -0
  7. package/dist/github-copilot-headers-NCJtz9i0.mjs +37 -0
  8. package/dist/{google-f-A8xrae.mjs → google-CtSg0iTS.mjs} +3 -3
  9. package/dist/{google-shared-J6qvYINH.mjs → google-shared-DNBz5rcD.mjs} +5 -3
  10. package/dist/{google-vertex-D3yMVXIY.mjs → google-vertex-31f1uS9L.mjs} +3 -3
  11. package/dist/host-Dog2WQiR.mjs +369 -0
  12. package/dist/index.mjs +1 -1
  13. package/dist/internal/anthropic.d.mts +3 -53
  14. package/dist/internal/anthropic.mjs +3 -2
  15. package/dist/internal/openai.d.mts +259 -2
  16. package/dist/internal/openai.mjs +8 -4
  17. package/dist/internal/runtime.mjs +6 -4
  18. package/dist/internal/shared.d.mts +1 -1
  19. package/dist/internal/shared.mjs +5 -1
  20. package/dist/{llm-request-activity-CehVkZP-.mjs → llm-request-activity-BjtkplhG.mjs} +1 -19
  21. package/dist/{mistral-CKV-TOQj.mjs → mistral-CWmpvWYh.mjs} +5 -3
  22. package/dist/{openai-chatgpt-responses-CedIj0hk.mjs → openai-chatgpt-responses-B84Ibtrd.mjs} +18 -13
  23. package/dist/openai-completions-DsOxhOD1.mjs +630 -0
  24. package/dist/openai-completions-compat-DBWjXoMZ.d.mts +43 -0
  25. package/dist/openai-reasoning-compat-YgeLncHw.mjs +396 -0
  26. package/dist/openai-responses-BT7A3sLu.mjs +138 -0
  27. package/dist/openai-responses-shared-pXl6Wd8S.mjs +392 -0
  28. package/dist/{openai-D3PD6PE-.mjs → openai-responses-stream-internal-Cw5txaGW.mjs} +1409 -1863
  29. package/dist/openai-tool-projection-OhX64DoP.mjs +215 -0
  30. package/dist/{provider-error-apVOZI6G.mjs → provider-error-CAEvRjry.mjs} +1 -1
  31. package/dist/provider-options-D8bB3z9b.d.mts +144 -0
  32. package/dist/providers.mjs +8 -8
  33. package/dist/{stream-first-event-timeout-C3OgBjIk.mjs → reasoning-tag-text-partitioner-CGDyLWUR.mjs} +1 -86
  34. package/dist/simple-options-9lhRrN73.mjs +50 -0
  35. package/dist/stream-first-event-timeout-BBys9hSb.mjs +86 -0
  36. package/dist/tls-certificate-errors-DXSpluKI.mjs +93 -0
  37. package/dist/tool-result-text-CTpIRbYd.mjs +225 -0
  38. package/dist/{github-copilot-headers-BCoBNmL7.mjs → tool-schema-json-projection-BwNu3nDi.mjs} +1 -48
  39. package/dist/transform-messages-C8mBqZxF.mjs +2 -0
  40. package/dist/{transport-stream-shared-BbMELSI4.mjs → transport-stream-shared-D81p90xq.mjs} +4 -4
  41. package/dist/transports.d.mts +13 -33
  42. package/dist/transports.mjs +75 -34
  43. package/package.json +4 -4
  44. package/dist/host-XYGZcgO8.mjs +0 -98
  45. package/dist/openai-BPor_3WI.d.mts +0 -358
  46. package/dist/openai-completions-CiSutyu0.mjs +0 -1223
  47. package/dist/shared-CdjNZd35.mjs +0 -634
@@ -1,634 +0,0 @@
1
- import { a as requiresClaudeMandatoryAdaptiveThinking, c as resolveClaudeMythos5ModelIdentity, d as resolveClaudeSonnet5ModelIdentity, i as requiresClaudeDefaultSampling, o as resolveClaudeFable5ModelIdentity, u as resolveClaudeOpus5ModelIdentity } from "./src-QkygScBs.mjs";
2
- import { n as getAiTransportHost } from "./host-XYGZcgO8.mjs";
3
- import { t as sanitizeSurrogates } from "./sanitize-unicode-DT5o51ur.mjs";
4
- //#region packages/ai/src/providers/simple-options.ts
5
- function buildBaseOptions(model, options, apiKey) {
6
- const firstEventOptions = options;
7
- return {
8
- temperature: options?.temperature,
9
- maxTokens: options?.maxTokens,
10
- stop: options?.stop,
11
- signal: options?.signal,
12
- apiKey: apiKey || options?.apiKey,
13
- transport: options?.transport,
14
- cacheRetention: options?.cacheRetention,
15
- sessionId: options?.sessionId,
16
- promptCacheKey: options?.promptCacheKey,
17
- headers: options?.headers,
18
- onPayload: options?.onPayload,
19
- onResponse: options?.onResponse,
20
- timeoutMs: options?.timeoutMs,
21
- firstEventTimeoutMs: firstEventOptions?.firstEventTimeoutMs,
22
- onFirstEventTimeout: firstEventOptions?.onFirstEventTimeout,
23
- maxRetries: options?.maxRetries,
24
- maxRetryDelayMs: options?.maxRetryDelayMs,
25
- metadata: options?.metadata
26
- };
27
- }
28
- function clampMaxTokensToModel(model, requestedMaxTokens) {
29
- return requestedMaxTokens === void 0 ? void 0 : Math.max(1, Math.min(requestedMaxTokens, model.maxTokens));
30
- }
31
- function clampReasoning(effort) {
32
- return effort === "xhigh" ? "high" : effort;
33
- }
34
- function adjustMaxTokensForThinking(baseMaxTokens, modelMaxTokens, reasoningLevel, customBudgets) {
35
- const budgets = {
36
- minimal: 1024,
37
- low: 2048,
38
- medium: 8192,
39
- high: 16384,
40
- max: 32768,
41
- ...customBudgets
42
- };
43
- const minOutputTokens = 1024;
44
- let thinkingBudget = budgets[clampReasoning(reasoningLevel)];
45
- const maxTokens = baseMaxTokens === void 0 ? modelMaxTokens : Math.min(baseMaxTokens + thinkingBudget, modelMaxTokens);
46
- if (maxTokens <= thinkingBudget) thinkingBudget = Math.max(0, maxTokens - minOutputTokens);
47
- return {
48
- maxTokens,
49
- thinkingBudget
50
- };
51
- }
52
- //#endregion
53
- //#region packages/normalization-core/src/record-coerce.ts
54
- /** Type guard for non-array object records at browser-safe boundaries. */
55
- function isRecord(value) {
56
- return value !== null && typeof value === "object" && !Array.isArray(value);
57
- }
58
- //#endregion
59
- //#region packages/normalization-core/src/utf16-slice.ts
60
- function isHighSurrogate(codeUnit) {
61
- return codeUnit >= 55296 && codeUnit <= 56319;
62
- }
63
- function isLowSurrogate(codeUnit) {
64
- return codeUnit >= 56320 && codeUnit <= 57343;
65
- }
66
- /** Slices a UTF-16 string without returning dangling surrogate halves at either edge. */
67
- function sliceUtf16Safe(input, start, end) {
68
- const len = input.length;
69
- let from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
70
- let to = end === void 0 ? len : end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
71
- if (to <= from) return "";
72
- if (from > 0 && from < len) {
73
- if (isLowSurrogate(input.charCodeAt(from)) && isHighSurrogate(input.charCodeAt(from - 1))) from += 1;
74
- }
75
- if (to > 0 && to < len) {
76
- if (isHighSurrogate(input.charCodeAt(to - 1)) && isLowSurrogate(input.charCodeAt(to))) to -= 1;
77
- }
78
- return input.slice(from, to);
79
- }
80
- /** Truncates a UTF-16 string without cutting a surrogate pair in half. */
81
- function truncateUtf16Safe(input, maxLen) {
82
- const limit = Math.max(0, Math.floor(maxLen));
83
- if (input.length <= limit) return input;
84
- return sliceUtf16Safe(input, 0, limit);
85
- }
86
- //#endregion
87
- //#region packages/ai/src/providers/tool-result-text.ts
88
- const PROVIDER_TOOL_RESULT_MAX_CHARS = 8e3;
89
- const IMAGE_TOOL_RESULT_TYPES = /* @__PURE__ */ new Set([
90
- "image",
91
- "image_url",
92
- "input_image"
93
- ]);
94
- const AUDIO_TOOL_RESULT_TYPES = /* @__PURE__ */ new Set([
95
- "audio",
96
- "input_audio",
97
- "output_audio"
98
- ]);
99
- const MEDIA_ONLY_TOOL_RESULT_TYPES = /* @__PURE__ */ new Set([...IMAGE_TOOL_RESULT_TYPES, ...AUDIO_TOOL_RESULT_TYPES]);
100
- const INLINE_DATA_URI_PATTERN = /(^|[^A-Za-z0-9_])data:([a-z][a-z0-9.+-]*\/[a-z0-9.+-]+(?:;[a-z0-9.+-]+=[^,;"'\s]+|;base64)*,[^\s"'<>)]+)/gi;
101
- const MIME_KEY_CANDIDATES = [
102
- "mimeType",
103
- "mime_type",
104
- "mediaType",
105
- "media_type",
106
- "contentType",
107
- "content_type"
108
- ];
109
- const TEXTUAL_MIME_PATTERN = /^(?:text\/|application\/(?:json|ld\+json|x-ndjson|xml|javascript|x-www-form-urlencoded)|[^/]+\/[^+]+\+(?:json|xml)$)/i;
110
- const OPAQUE_OR_BINARY_FIELD_RE = /^(?:blob|buffer|bytes|encrypted_content|encrypted_stdout)$/i;
111
- function readMimeType(value) {
112
- if (!isRecord(value)) return;
113
- for (const key of MIME_KEY_CANDIDATES) {
114
- const mimeType = value[key];
115
- if (typeof mimeType === "string" && mimeType.trim().length > 0) return mimeType;
116
- }
117
- }
118
- function isBinaryMimeType(mimeType) {
119
- const normalized = mimeType.split(";", 1)[0]?.trim().toLowerCase();
120
- return normalized ? !TEXTUAL_MIME_PATTERN.test(normalized) : false;
121
- }
122
- function describeOmittedValue(value, label) {
123
- const length = typeof value === "string" ? value.length : JSON.stringify(value)?.length;
124
- return length ? `[${label} omitted: ${length} chars]` : `[${label} omitted]`;
125
- }
126
- function redactInlineDataUris(value) {
127
- return value.replace(INLINE_DATA_URI_PATTERN, (_match, prefix, uri) => `${prefix}[inline data URI: ${uri.length} chars]`);
128
- }
129
- function redactStructuredTextValue(value) {
130
- const host = getAiTransportHost();
131
- const redacted = host.redactToolPayloadText(value);
132
- const trimmed = redacted.trim();
133
- if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return redacted;
134
- try {
135
- const redactedWrapper = host.redactSecrets({ structuredTextValue: JSON.parse(redacted) });
136
- return JSON.stringify(redactedWrapper.structuredTextValue);
137
- } catch {
138
- return redacted;
139
- }
140
- }
141
- function stringifyStructuredBlock(block) {
142
- const seen = /* @__PURE__ */ new WeakSet();
143
- try {
144
- const redactedBlock = getAiTransportHost().redactSecrets({ structuredToolResult: block }).structuredToolResult;
145
- const serialized = JSON.stringify(redactedBlock, function structuredToolResultReplacer(key, value) {
146
- if (OPAQUE_OR_BINARY_FIELD_RE.test(key)) return `[omitted ${key}]`;
147
- if (key === "data") {
148
- const mimeType = readMimeType(this);
149
- if (mimeType && isBinaryMimeType(mimeType)) return describeOmittedValue(value, "binary data");
150
- }
151
- if (typeof value === "bigint") return value.toString();
152
- if (typeof value === "string") return redactInlineDataUris(redactStructuredTextValue(value));
153
- if (typeof value === "function" || typeof value === "symbol" || value === void 0) return;
154
- if (!value || typeof value !== "object") return value;
155
- if (seen.has(value)) return "[Circular]";
156
- seen.add(value);
157
- return value;
158
- });
159
- if (!serialized || serialized === "{}") return;
160
- return serialized;
161
- } catch {
162
- return;
163
- }
164
- }
165
- function truncateProviderToolText(text) {
166
- if (text.length <= PROVIDER_TOOL_RESULT_MAX_CHARS) return text;
167
- return `${truncateUtf16Safe(text, PROVIDER_TOOL_RESULT_MAX_CHARS)}\n…(truncated)…`;
168
- }
169
- /** Media metadata alone is not an attachment; provider emitters need inline bytes. */
170
- function hasMediaPayload(block) {
171
- return isRecord(block) && typeof block.data === "string" && block.data.trim().length > 0;
172
- }
173
- /** Image metadata alone is not an attachment; provider emitters need inline bytes. */
174
- function isImageWithMediaPayload(block) {
175
- return isRecord(block) && block.type === "image" && hasMediaPayload(block);
176
- }
177
- function describeToolResultMediaPlaceholder(blocks) {
178
- let hasImage = false;
179
- let hasAudio = false;
180
- for (const block of blocks) {
181
- if (!hasMediaPayload(block)) continue;
182
- const record = block;
183
- const type = typeof record.type === "string" ? record.type : void 0;
184
- const mimeType = readMimeType(record);
185
- if (type && IMAGE_TOOL_RESULT_TYPES.has(type) || mimeType?.toLowerCase().startsWith("image/")) hasImage = true;
186
- if (type && AUDIO_TOOL_RESULT_TYPES.has(type) || mimeType?.toLowerCase().startsWith("audio/")) hasAudio = true;
187
- }
188
- if (hasImage && hasAudio) return "(see attached media)";
189
- if (hasAudio) return "(see attached audio)";
190
- if (hasImage) return "(see attached image)";
191
- }
192
- function extractToolResultBlockText(block) {
193
- if (!block || typeof block !== "object") return;
194
- const record = block;
195
- if (typeof record.type === "string" && MEDIA_ONLY_TOOL_RESULT_TYPES.has(record.type)) return;
196
- if (record.type === "text") {
197
- const text = typeof record.text === "string" ? record.text : "";
198
- return text ? sanitizeSurrogates(text) : void 0;
199
- }
200
- const structured = stringifyStructuredBlock(record);
201
- return structured ? sanitizeSurrogates(truncateProviderToolText(structured)) : void 0;
202
- }
203
- function extractToolResultText(blocks) {
204
- const explicitTexts = [];
205
- const structuredTexts = [];
206
- for (const block of blocks) {
207
- const text = extractToolResultBlockText(block);
208
- if (!text) continue;
209
- if (block.type === "text") explicitTexts.push(text);
210
- else structuredTexts.push(text);
211
- }
212
- if (explicitTexts.length > 0) return sanitizeSurrogates(explicitTexts.join("\n"));
213
- return sanitizeSurrogates(truncateProviderToolText(structuredTexts.join("\n")));
214
- }
215
- //#endregion
216
- //#region packages/normalization-core/src/string-coerce.ts
217
- /** Reads a value only when it is already a string, preserving whitespace. */
218
- function readStringValue(value) {
219
- return typeof value === "string" ? value : void 0;
220
- }
221
- /** Trims string input and returns null for non-strings or empty strings. */
222
- function normalizeNullableString(value) {
223
- if (typeof value !== "string") return null;
224
- const trimmed = value.trim();
225
- return trimmed ? trimmed : null;
226
- }
227
- /** Trims string input and returns undefined for non-strings or empty strings. */
228
- function normalizeOptionalString(value) {
229
- return normalizeNullableString(value) ?? void 0;
230
- }
231
- /** Lowercases a normalized optional string. */
232
- function normalizeOptionalLowercaseString(value) {
233
- return normalizeOptionalString(value)?.toLowerCase();
234
- }
235
- /** Lowercases a normalized string or returns an empty string when absent. */
236
- function normalizeLowercaseStringOrEmpty(value) {
237
- return normalizeOptionalLowercaseString(value) ?? "";
238
- }
239
- /** Type guard for strings that remain non-empty after trimming. */
240
- function hasNonEmptyString(value) {
241
- return normalizeOptionalString(value) !== void 0;
242
- }
243
- //#endregion
244
- //#region packages/ai/src/providers/anthropic-model-contract.ts
245
- function normalizeModelId(modelId) {
246
- const normalized = normalizeLowercaseStringOrEmpty(modelId);
247
- return (normalized.startsWith("anthropic/") ? normalized.slice(10) : normalized).replace(/[._\s]+/g, "-");
248
- }
249
- function normalizeApi(api) {
250
- const normalized = normalizeLowercaseStringOrEmpty(api);
251
- return normalized === "openclaw-anthropic-messages-transport" ? "anthropic-messages" : normalized;
252
- }
253
- function hasConcreteResponseModel(ref) {
254
- const responseModelId = normalizeModelId(ref.responseModelId);
255
- return responseModelId.length > 0 && responseModelId !== normalizeModelId(ref.modelId);
256
- }
257
- function usesClaudeFable5MessagesContract(model) {
258
- return normalizeApi(model.api) === "anthropic-messages" && resolveClaudeFable5ModelIdentity(model) !== void 0;
259
- }
260
- /** Return whether streamed output must wait for the terminal refusal decision. */
261
- function usesClaudeStreamingRefusalContract(model) {
262
- if (normalizeApi(model.api) !== "anthropic-messages") return false;
263
- return resolveClaudeFable5ModelIdentity(model) !== void 0 || resolveClaudeMythos5ModelIdentity(model) !== void 0 || resolveClaudeOpus5ModelIdentity(model) !== void 0 || resolveClaudeSonnet5ModelIdentity(model) !== void 0;
264
- }
265
- function requiresClaudeAdaptiveThinking(model) {
266
- if (normalizeApi(model.api) !== "anthropic-messages") return false;
267
- return requiresClaudeMandatoryAdaptiveThinking(model);
268
- }
269
- /** Return whether omitted thinking should default to adaptive/high. */
270
- function defaultsClaudeAdaptiveThinking(model) {
271
- return requiresClaudeAdaptiveThinking(model) || normalizeApi(model.api) === "anthropic-messages" && (resolveClaudeOpus5ModelIdentity(model) !== void 0 || resolveClaudeSonnet5ModelIdentity(model) !== void 0);
272
- }
273
- /** Remove unsupported assistant prefills while preserving completed tool-use turns. */
274
- function prepareClaudeNoPrefillRequestContext(model, context) {
275
- if (!resolveClaudeOpus5ModelIdentity(model) && !resolveClaudeSonnet5ModelIdentity(model)) return context;
276
- let end = context.messages.length;
277
- while (end > 0) {
278
- const message = context.messages[end - 1];
279
- if (message?.role !== "assistant" || Array.isArray(message.content) && message.content.some((block) => block.type === "toolCall")) break;
280
- end -= 1;
281
- }
282
- return end === context.messages.length ? context : {
283
- ...context,
284
- messages: context.messages.slice(0, end)
285
- };
286
- }
287
- function applyClaudeRequestContract(params, model) {
288
- if (normalizeApi(model.api) !== "anthropic-messages") return;
289
- const opus5 = resolveClaudeOpus5ModelIdentity(model) !== void 0;
290
- const sonnet5 = resolveClaudeSonnet5ModelIdentity(model) !== void 0;
291
- if (!requiresClaudeDefaultSampling(model) && !opus5 && !sonnet5) return;
292
- delete params.temperature;
293
- delete params.top_p;
294
- delete params.top_k;
295
- if (opus5 || sonnet5) delete params.service_tier;
296
- }
297
- function resolveReplayModelBoundIdentity(ref) {
298
- if (normalizeApi(ref.api) !== "anthropic-messages") return;
299
- const modelRef = hasConcreteResponseModel(ref) ? { id: ref.responseModelId } : {
300
- id: ref.modelId,
301
- params: ref.modelParams
302
- };
303
- const fableIdentity = resolveClaudeFable5ModelIdentity(modelRef);
304
- if (fableIdentity) return `fable:${fableIdentity}`;
305
- const mythosIdentity = resolveClaudeMythos5ModelIdentity(modelRef);
306
- if (mythosIdentity) return `mythos:${mythosIdentity}`;
307
- const opusIdentity = resolveClaudeOpus5ModelIdentity(modelRef);
308
- if (opusIdentity) return `opus:${opusIdentity}`;
309
- const sonnetIdentity = resolveClaudeSonnet5ModelIdentity(modelRef);
310
- return sonnetIdentity ? `sonnet:${sonnetIdentity}` : void 0;
311
- }
312
- function resolveModelBoundThinkingReplayMode(params) {
313
- const sourceApi = normalizeApi(params.source.api);
314
- const targetApi = normalizeApi(params.target.api);
315
- const sourceIdentity = resolveReplayModelBoundIdentity(params.source);
316
- const targetIdentity = resolveReplayModelBoundIdentity(params.target);
317
- const sameRoute = normalizeLowercaseStringOrEmpty(params.source.provider) === normalizeLowercaseStringOrEmpty(params.target.provider) && sourceApi === targetApi && normalizeModelId(params.source.modelId) === normalizeModelId(params.target.modelId);
318
- if (!sourceIdentity && !targetIdentity) return "default";
319
- if (!sourceIdentity && !hasConcreteResponseModel(params.source) && targetIdentity && sameRoute) return "preserve";
320
- return sourceApi === targetApi && sourceIdentity === targetIdentity ? "preserve" : "drop";
321
- }
322
- //#endregion
323
- //#region packages/ai/src/providers/transform-messages.ts
324
- const NON_VISION_USER_IMAGE_PLACEHOLDER = "(image omitted: model does not support images)";
325
- const NON_VISION_TOOL_IMAGE_PLACEHOLDER = "(tool image omitted: model does not support images)";
326
- function replaceImagesWithPlaceholder(content, placeholder) {
327
- const result = [];
328
- let previousWasPlaceholder = false;
329
- for (const block of content) {
330
- if (block.type === "image") {
331
- if (!isImageWithMediaPayload(block)) continue;
332
- if (!previousWasPlaceholder) result.push({
333
- type: "text",
334
- text: placeholder
335
- });
336
- previousWasPlaceholder = true;
337
- continue;
338
- }
339
- result.push(block);
340
- previousWasPlaceholder = block.text === placeholder;
341
- }
342
- return result;
343
- }
344
- function downgradeUnsupportedImages(messages, model) {
345
- if (model.input.includes("image")) return messages;
346
- return messages.map((msg) => {
347
- if (msg.role === "user" && Array.isArray(msg.content)) return {
348
- ...msg,
349
- content: replaceImagesWithPlaceholder(msg.content, NON_VISION_USER_IMAGE_PLACEHOLDER)
350
- };
351
- if (msg.role === "toolResult") return {
352
- ...msg,
353
- content: replaceImagesWithPlaceholder(msg.content, NON_VISION_TOOL_IMAGE_PLACEHOLDER)
354
- };
355
- return msg;
356
- });
357
- }
358
- /**
359
- * Normalize tool call ID for cross-provider compatibility.
360
- * OpenAI Responses API generates IDs that are 450+ chars with special characters like `|`.
361
- * Anthropic APIs require IDs matching ^[a-zA-Z0-9_-]+$ (max 64 chars).
362
- */
363
- function transformMessages(messages, model, normalizeToolCallId) {
364
- const toolCallIdMap = /* @__PURE__ */ new Map();
365
- const transformed = downgradeUnsupportedImages(messages.map((msg) => msg.content == null ? {
366
- ...msg,
367
- content: []
368
- } : msg), model).map((msg) => {
369
- if (msg.role === "user") return msg;
370
- if (msg.role === "toolResult") {
371
- const normalizedId = toolCallIdMap.get(msg.toolCallId);
372
- if (normalizedId && normalizedId !== msg.toolCallId) return Object.assign({}, msg, { toolCallId: normalizedId });
373
- return msg;
374
- }
375
- if (msg.role === "assistant") {
376
- const assistantMsg = msg;
377
- const modelBoundThinkingReplayMode = resolveModelBoundThinkingReplayMode({
378
- source: {
379
- provider: assistantMsg.provider,
380
- api: assistantMsg.api,
381
- modelId: assistantMsg.model,
382
- responseModelId: assistantMsg.responseModel
383
- },
384
- target: {
385
- provider: model.provider,
386
- api: model.api,
387
- modelId: model.id,
388
- modelParams: model.params
389
- }
390
- });
391
- const isSameModel = modelBoundThinkingReplayMode === "preserve" || assistantMsg.provider === model.provider && assistantMsg.api === model.api && assistantMsg.model === model.id;
392
- const transformedContent = (typeof assistantMsg.content === "string" ? [{
393
- type: "text",
394
- text: assistantMsg.content
395
- }] : assistantMsg.content).flatMap((block) => {
396
- if (block.type === "thinking") {
397
- if (modelBoundThinkingReplayMode === "drop") return [];
398
- if (block.redacted) return isSameModel ? block : [];
399
- if (isSameModel && block.thinkingSignature) return block;
400
- if (!block.thinking || block.thinking.trim() === "") return [];
401
- if (isSameModel) return block;
402
- return {
403
- type: "text",
404
- text: block.thinking
405
- };
406
- }
407
- if (block.type === "text") {
408
- if (isSameModel) return block;
409
- return {
410
- type: "text",
411
- text: block.text
412
- };
413
- }
414
- if (block.type === "toolCall") {
415
- const toolCall = block;
416
- let normalizedToolCall = toolCall;
417
- if (!isSameModel && toolCall.thoughtSignature) {
418
- normalizedToolCall = Object.assign({}, toolCall);
419
- delete normalizedToolCall.thoughtSignature;
420
- }
421
- if (!isSameModel && normalizeToolCallId) {
422
- const normalizedId = normalizeToolCallId(toolCall.id, model, assistantMsg);
423
- if (normalizedId !== toolCall.id) {
424
- toolCallIdMap.set(toolCall.id, normalizedId);
425
- normalizedToolCall = Object.assign({}, normalizedToolCall, { id: normalizedId });
426
- }
427
- }
428
- return normalizedToolCall;
429
- }
430
- return block;
431
- });
432
- return Object.assign({}, assistantMsg, { content: transformedContent });
433
- }
434
- return msg;
435
- });
436
- const result = [];
437
- let pendingToolCalls = [];
438
- let existingToolResultIds = /* @__PURE__ */ new Set();
439
- const insertSyntheticToolResults = () => {
440
- if (pendingToolCalls.length > 0) {
441
- for (const tc of pendingToolCalls) if (!existingToolResultIds.has(tc.id)) result.push({
442
- role: "toolResult",
443
- toolCallId: tc.id,
444
- toolName: tc.name,
445
- content: [{
446
- type: "text",
447
- text: "No result provided"
448
- }],
449
- isError: true,
450
- timestamp: Date.now()
451
- });
452
- pendingToolCalls = [];
453
- existingToolResultIds = /* @__PURE__ */ new Set();
454
- }
455
- };
456
- for (const msg of transformed) if (msg.role === "assistant") {
457
- insertSyntheticToolResults();
458
- const assistantMsg = msg;
459
- if (assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") continue;
460
- const toolCalls = assistantMsg.content.filter((b) => b.type === "toolCall");
461
- if (toolCalls.length > 0) {
462
- pendingToolCalls = toolCalls;
463
- existingToolResultIds = /* @__PURE__ */ new Set();
464
- }
465
- result.push(msg);
466
- } else if (msg.role === "toolResult") {
467
- existingToolResultIds.add(msg.toolCallId);
468
- result.push(msg);
469
- } else if (msg.role === "user") {
470
- insertSyntheticToolResults();
471
- result.push(msg);
472
- } else result.push(msg);
473
- insertSyntheticToolResults();
474
- return result;
475
- }
476
- //#endregion
477
- //#region packages/ai/src/utils/prompt-cache-stability.ts
478
- /**
479
- * Prompt-cache normalization helpers. They keep generated prompt sections
480
- * deterministic across platform newlines, trailing whitespace, and input
481
- * ordering.
482
- */
483
- /** Canonicalizes provider tool order without relying on host locale settings. */
484
- function sortPromptCacheToolsByName(tools) {
485
- const compareText = (left, right) => {
486
- const leftText = left ?? "";
487
- const rightText = right ?? "";
488
- return leftText < rightText ? -1 : leftText > rightText ? 1 : 0;
489
- };
490
- return tools.toSorted((left, right) => compareText(left.wireName ?? left.name, right.wireName ?? right.name) || compareText(left.description, right.description));
491
- }
492
- /** Normalize structured prompt text before hashing or snapshot comparison. */
493
- function normalizeStructuredPromptSection(text) {
494
- return sanitizeSurrogates(text).replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "").trim();
495
- }
496
- /** Normalize, de-dupe, and sort capability ids for stable prompt payloads. */
497
- function normalizePromptCapabilityIds(capabilities) {
498
- const seen = /* @__PURE__ */ new Set();
499
- const normalized = [];
500
- for (const capability of capabilities) {
501
- const value = normalizeLowercaseStringOrEmpty(normalizeStructuredPromptSection(capability));
502
- if (!value || seen.has(value)) continue;
503
- seen.add(value);
504
- normalized.push(value);
505
- }
506
- return normalized.toSorted((left, right) => left.localeCompare(right));
507
- }
508
- //#endregion
509
- //#region packages/ai/src/utils/system-prompt-cache-boundary.ts
510
- /**
511
- * System prompt cache-boundary helpers.
512
- *
513
- * Keeps stable prompt prefixes separate from dynamic runtime additions for provider prompt caching.
514
- */
515
- const SYSTEM_PROMPT_CACHE_BOUNDARY = "\n<!-- OPENCLAW_CACHE_BOUNDARY -->\n";
516
- function stripSystemPromptCacheBoundary(text) {
517
- return text.replaceAll(SYSTEM_PROMPT_CACHE_BOUNDARY, "\n");
518
- }
519
- function ensureSystemPromptCacheBoundary(systemPrompt) {
520
- if (systemPrompt.trim().length === 0) return systemPrompt;
521
- return systemPrompt.includes("\n<!-- OPENCLAW_CACHE_BOUNDARY -->\n") ? systemPrompt : `${systemPrompt}${SYSTEM_PROMPT_CACHE_BOUNDARY}`;
522
- }
523
- function splitSystemPromptCacheBoundary(text) {
524
- const boundaryIndex = text.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
525
- if (boundaryIndex === -1) return;
526
- return {
527
- stablePrefix: text.slice(0, boundaryIndex).trimEnd(),
528
- dynamicSuffix: text.slice(boundaryIndex + 34).trimStart()
529
- };
530
- }
531
- function prependSystemPromptAdditionAfterCacheBoundary(params) {
532
- const systemPromptAddition = typeof params.systemPromptAddition === "string" ? normalizeStructuredPromptSection(params.systemPromptAddition) : "";
533
- if (!systemPromptAddition) return params.systemPrompt;
534
- if (params.systemPrompt.trim().length === 0) return systemPromptAddition;
535
- const split = splitSystemPromptCacheBoundary(params.systemPrompt);
536
- if (!split) return `${systemPromptAddition}\n\n${params.systemPrompt}`;
537
- const dynamicSuffix = split.dynamicSuffix ? normalizeStructuredPromptSection(split.dynamicSuffix) : "";
538
- if (!dynamicSuffix) return `${split.stablePrefix}${SYSTEM_PROMPT_CACHE_BOUNDARY}${systemPromptAddition}`;
539
- return `${split.stablePrefix}${SYSTEM_PROMPT_CACHE_BOUNDARY}${systemPromptAddition}\n\n${dynamicSuffix}`;
540
- }
541
- //#endregion
542
- //#region packages/ai/src/utils/tls-certificate-errors.ts
543
- const HOSTNAME_MISMATCH_CODES = /* @__PURE__ */ new Set(["ERR_TLS_CERT_ALTNAME_INVALID", "HOSTNAME_MISMATCH"]);
544
- const CERTIFICATE_INVALID_CODES = /* @__PURE__ */ new Set([
545
- "CERT_CHAIN_TOO_LONG",
546
- "CERT_HAS_EXPIRED",
547
- "CERT_NOT_YET_VALID",
548
- "CERT_REJECTED",
549
- "CERT_REVOKED",
550
- "CERT_SIGNATURE_FAILURE",
551
- "CERT_UNTRUSTED",
552
- "CRL_HAS_EXPIRED",
553
- "CRL_NOT_YET_VALID",
554
- "CRL_SIGNATURE_FAILURE",
555
- "DEPTH_ZERO_SELF_SIGNED_CERT",
556
- "ERROR_IN_CERT_NOT_AFTER_FIELD",
557
- "ERROR_IN_CERT_NOT_BEFORE_FIELD",
558
- "ERROR_IN_CRL_LAST_UPDATE_FIELD",
559
- "ERROR_IN_CRL_NEXT_UPDATE_FIELD",
560
- "INVALID_CA",
561
- "INVALID_PURPOSE",
562
- "PATH_LENGTH_EXCEEDED",
563
- "SELF_SIGNED_CERT_IN_CHAIN",
564
- "UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",
565
- "UNABLE_TO_DECRYPT_CERT_SIGNATURE",
566
- "UNABLE_TO_DECRYPT_CRL_SIGNATURE",
567
- "UNABLE_TO_GET_CRL",
568
- "UNABLE_TO_GET_ISSUER_CERT",
569
- "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
570
- "UNABLE_TO_VERIFY_LEAF_SIGNATURE"
571
- ]);
572
- const HOSTNAME_MISMATCH_PATTERNS = [
573
- /hostname\/ip does not match certificate(?:'s)? altnames/i,
574
- /hostname mismatch/i,
575
- /host: .+ is not in the cert(?:ificate)?(?:'s)? altnames/i
576
- ];
577
- const CERTIFICATE_INVALID_PATTERNS = [
578
- /certificate has expired/i,
579
- /certificate is not yet valid/i,
580
- /self[- ]signed certificate/i,
581
- /unable to get local issuer certificate/i,
582
- /unable to verify the first certificate/i
583
- ];
584
- const MAX_TLS_ERROR_DEPTH = 8;
585
- function classifyCode(code) {
586
- if (HOSTNAME_MISMATCH_CODES.has(code)) return "hostname_mismatch";
587
- return CERTIFICATE_INVALID_CODES.has(code) ? "certificate_invalid" : null;
588
- }
589
- function classifyMessage(message) {
590
- if (HOSTNAME_MISMATCH_PATTERNS.some((pattern) => pattern.test(message))) return "hostname_mismatch";
591
- return CERTIFICATE_INVALID_PATTERNS.some((pattern) => pattern.test(message)) ? "certificate_invalid" : null;
592
- }
593
- function inspectTlsCertificateErrorInternal(error, seen, depth) {
594
- if (depth > MAX_TLS_ERROR_DEPTH) return null;
595
- if (typeof error === "string") {
596
- const kind = classifyMessage(error);
597
- return kind ? {
598
- kind,
599
- message: error
600
- } : null;
601
- }
602
- if (!error || typeof error !== "object" || seen.has(error)) return null;
603
- seen.add(error);
604
- const candidate = error;
605
- const code = typeof candidate.code === "string" ? candidate.code.trim().toUpperCase() : void 0;
606
- const message = typeof candidate.message === "string" && candidate.message.trim() ? candidate.message : void 0;
607
- const codeKind = code ? classifyCode(code) : null;
608
- if (code && codeKind) return {
609
- kind: codeKind,
610
- code,
611
- message: message ?? code
612
- };
613
- const nestedErrors = Array.isArray(candidate.errors) ? candidate.errors : [];
614
- for (const nested of [
615
- candidate.cause,
616
- candidate.error,
617
- ...nestedErrors
618
- ]) {
619
- if (nested === void 0 || nested === error) continue;
620
- const details = inspectTlsCertificateErrorInternal(nested, seen, depth + 1);
621
- if (details) return details;
622
- }
623
- const messageKind = message ? classifyMessage(message) : null;
624
- return messageKind && message ? {
625
- kind: messageKind,
626
- message
627
- } : null;
628
- }
629
- /** Classify deterministic Node/OpenSSL certificate validation failures. */
630
- function inspectTlsCertificateError(error) {
631
- return inspectTlsCertificateErrorInternal(error, /* @__PURE__ */ new Set(), 0);
632
- }
633
- //#endregion
634
- export { buildBaseOptions as A, extractToolResultBlockText as C, truncateUtf16Safe as D, isImageWithMediaPayload as E, clampReasoning as M, isRecord as O, describeToolResultMediaPlaceholder as S, hasMediaPayload as T, usesClaudeStreamingRefusalContract as _, splitSystemPromptCacheBoundary as a, normalizeOptionalString as b, normalizeStructuredPromptSection as c, applyClaudeRequestContract as d, defaultsClaudeAdaptiveThinking as f, usesClaudeFable5MessagesContract as g, resolveModelBoundThinkingReplayMode as h, prependSystemPromptAdditionAfterCacheBoundary as i, clampMaxTokensToModel as j, adjustMaxTokensForThinking as k, sortPromptCacheToolsByName as l, requiresClaudeAdaptiveThinking as m, SYSTEM_PROMPT_CACHE_BOUNDARY as n, stripSystemPromptCacheBoundary as o, prepareClaudeNoPrefillRequestContext as p, ensureSystemPromptCacheBoundary as r, normalizePromptCapabilityIds as s, inspectTlsCertificateError as t, transformMessages as u, hasNonEmptyString as v, extractToolResultText as w, readStringValue as x, normalizeLowercaseStringOrEmpty as y };