@openclaw/ai 2026.10.2 → 2026.10.4

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 (21) hide show
  1. package/dist/{anthropic-C4qygbtf.mjs → anthropic-DloU96D6.mjs} +2 -2
  2. package/dist/{anthropic-stream-reducer-CWyWE21W.mjs → anthropic-stream-reducer-Djl4yqrc.mjs} +2 -3
  3. package/dist/{assistant-text-phase-DVZOxbuR.mjs → assistant-text-phase-DzvV7Oso.mjs} +1 -1
  4. package/dist/{azure-openai-responses-DO-Fv8ha.mjs → azure-openai-responses-CAyTrcy6.mjs} +1 -2
  5. package/dist/internal/anthropic.mjs +2 -2
  6. package/dist/internal/openai.mjs +5 -7
  7. package/dist/{openai-chatgpt-responses-CHdn8YJ_.mjs → openai-chatgpt-responses-vk4XdUjX.mjs} +6 -7
  8. package/dist/{openai-completions-BZfHIaZ7.mjs → openai-completions-XvUwdvJc.mjs} +1 -1
  9. package/dist/{openai-completions-transport-CuoQfnKV.mjs → openai-completions-transport-QYABn8Uz.mjs} +1180 -29
  10. package/dist/{openai-responses-BhAZFsL9.mjs → openai-responses-DNWY56mK.mjs} +2 -3
  11. package/dist/{openai-responses-prompt-observer-internal-CefUzh8M.mjs → openai-responses-request-lifecycle-Dskvtz_Z.mjs} +36 -1
  12. package/dist/{openai-responses-shared-CKNmC3vC.mjs → openai-responses-shared-BMx2NAS-.mjs} +805 -67
  13. package/dist/{provider-transport-turn-state-BX6TfCyW.mjs → provider-transport-turn-state-1T3vwTvn.mjs} +1 -1
  14. package/dist/providers.mjs +5 -5
  15. package/dist/{text-signature-D_vRrx-z.mjs → text-signature-CbqxIB8B.mjs} +25 -2
  16. package/dist/transports.mjs +11 -1148
  17. package/package.json +1 -1
  18. package/dist/openai-responses-request-lifecycle-Fsag-IGZ.mjs +0 -36
  19. package/dist/openai-transport-params-D7JMUPjf.mjs +0 -735
  20. package/dist/openai-web-search-tools-Cfu0uLyN.mjs +0 -14
  21. package/dist/provider-replay-context-CO_Eplnc.mjs +0 -25
@@ -2,90 +2,761 @@ import "./src-VWy9E7DZ.mjs";
2
2
  import { _ as runtimeContextContentToText, f as hasRuntimeContextMarker, p as isRuntimeContextMessage } from "./types-CQc6JNG4.mjs";
3
3
  import { o as readProviderRefusalReview, t as appendAssistantMessageDiagnostic } from "./diagnostics-DzrF1h4J.mjs";
4
4
  import { r as appendAssistantThinking } from "./event-stream-C5AbCAaZ.mjs";
5
- import { o as isRecord } from "./record-coerce-CNDovvft.mjs";
6
- import { a as normalizeOptionalString, c as readStringValue, o as normalizeStringifiedOptionalString } from "./string-coerce-CtgOVOSv.mjs";
7
- import { N as isImageWithMediaPayload, O as calculateCost, a as resolveAiTransportHeaderSentinels, r as getAiTransportHost } from "./host-u3o8XiAp.mjs";
8
- import { D as supportsOpenAITemperature, a as OPENAI_RESPONSES_COMPACTION_REPLAY_TYPE, c as OPENAI_RESPONSES_RETAINED_COMPACTION_REPLAY_TYPE, f as RESPONSE_FAILED_NO_DETAILS_MESSAGE, g as responsesServiceTierObserver, o as OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY, p as isPreviousResponseRejection, s as OPENAI_RESPONSES_REASONING_REPLAY_META_KEY } from "./openai-responses-contracts-BKu8O7l4.mjs";
5
+ import { o as isRecord, r as asOptionalObjectRecord } from "./record-coerce-CNDovvft.mjs";
6
+ import { a as normalizeOptionalString, c as readStringValue, n as normalizeLowercaseStringOrEmpty, o as normalizeStringifiedOptionalString } from "./string-coerce-CtgOVOSv.mjs";
7
+ import { D as applyProviderReportedUsageCost, E as headersToRecord, N as isImageWithMediaPayload, O as calculateCost, a as resolveAiTransportHeaderSentinels, k as clampThinkingLevel, r as getAiTransportHost } from "./host-u3o8XiAp.mjs";
8
+ import { A as resolveOpenAIThinkingApi, C as resolveOpenAIReasoningEffortForModel, D as supportsOpenAITemperature, E as supportsOpenAIReasoningEffort, O as MODEL_CATALOG_THINKING_LEVELS, S as resolveOpenAIModelReasoningEfforts, a as OPENAI_RESPONSES_COMPACTION_REPLAY_TYPE, c as OPENAI_RESPONSES_RETAINED_COMPACTION_REPLAY_TYPE, f as RESPONSE_FAILED_NO_DETAILS_MESSAGE, g as responsesServiceTierObserver, i as OPENAI_RESPONSES_APIS, o as OPENAI_RESPONSES_REASONING_REPLAY_BLOCK_META_KEY, p as isPreviousResponseRejection, s as OPENAI_RESPONSES_REASONING_REPLAY_META_KEY, w as resolveOpenAIReasoningEffortMapping, x as normalizeOpenAIReasoningEffort } from "./openai-responses-contracts-BKu8O7l4.mjs";
9
9
  import { n as getSessionResourceOwnerId, r as registerSessionResourceCleanup } from "./session-resources-D1wgDQ8C.mjs";
10
10
  import { o as truncateUtf16Safe, s as stableStringify } from "./provider-error-DQBE1bUQ.mjs";
11
+ import { n as clampOpenAIPromptCacheKey } from "./openai-prompt-cache-BK6E2g4Q.mjs";
11
12
  import { a as resolveModelSseDebugMode, d as isResponsesProviderTool, g as resolveResponsesTerminalStopReason, h as readResponsesReasoningTokens, i as resolveModelPayloadDebugMode, l as ResponsesOutputIdentityError, m as mapResponsesTerminalUsage, r as emitModelTransportDebug, u as hasOnlyResponsesFunctionTools } from "./diagnostics-Caj-4RBA.mjs";
12
- import { o as redactSensitiveText, t as encodeTextSignatureV1 } from "./text-signature-D_vRrx-z.mjs";
13
- import { c as resolveOpenAIResponsesTextFormat, i as resolveOpenAICompletionsCompat } from "./openai-completions-compat-GMN8A0fm.mjs";
14
- import { A as shortHash, C as parseJsonObjectPreservingUnsafeIntegers, E as createToolArgumentPreviewSchedule, O as parseStreamingJson, S as withProviderResponseHook, _ as sanitizeNonEmptyTransportPayloadText, b as transportAbortError, c as failTransportStream, d as iterateModelStream, g as parseTerminalToolCallArguments, t as IncompleteToolCallError, u as finalizeTransportStream, v as sanitizeTransportPayloadText } from "./transport-stream-shared-CCR7hSW0.mjs";
13
+ import { i as providerReplayContextMatches, l as redactSensitiveText, n as buildProviderReplayContext, r as isProviderReplayContext, s as isCodeModeModelVisibleToolName, t as encodeTextSignatureV1 } from "./text-signature-CbqxIB8B.mjs";
14
+ import { c as resolveOpenAIResponsesTextFormat, i as resolveOpenAICompletionsCompat, o as usesNativeOpenAICodexResponsesBackend } from "./openai-completions-compat-GMN8A0fm.mjs";
15
+ import { r as resolveModelRequestTimeoutMs } from "./host-policy-BwhNNGQR.mjs";
16
+ import { A as shortHash, C as parseJsonObjectPreservingUnsafeIntegers, E as createToolArgumentPreviewSchedule, O as parseStreamingJson, S as withProviderResponseHook, _ as sanitizeNonEmptyTransportPayloadText, b as transportAbortError, c as failTransportStream, d as iterateModelStream, g as parseTerminalToolCallArguments, m as notifyProviderHttpResponse, t as IncompleteToolCallError, u as finalizeTransportStream, v as sanitizeTransportPayloadText } from "./transport-stream-shared-CCR7hSW0.mjs";
15
17
  import { C as stripSystemPromptCacheBoundary, D as sortPromptCacheToolsByName, d as describeToolResultMediaPlaceholder, m as extractToolResultText, s as prepareModelRequestBody } from "./simple-options-7QuSKriJ.mjs";
16
- import { A as redactIdentifier, C as log, d as prepareOpenAITools, h as resolveOpenAIRequestReasoning, j as sha256Hex, u as resolveOpenAIStrictToolFlagWithDiagnostics, y as createOpenAIProviderAcceptanceHook } from "./openai-transport-params-D7JMUPjf.mjs";
17
- import { n as isProviderReplayContext, r as providerReplayContextMatches, t as buildProviderReplayContext } from "./provider-replay-context-CO_Eplnc.mjs";
18
18
  import { n as transformProviderMessages } from "./assistant-output-D5762GPH.mjs";
19
+ import { n as projectRuntimeToolInputSchema, t as prepareRuntimeToolInputSchema } from "./tool-schema-json-projection-Ct8SeBeJ.mjs";
19
20
  import { t as notifyLlmRequestActivity } from "./llm-request-activity-D8f_oxHj.mjs";
21
+ import { n as resolveOpencodeSessionHeaders } from "./session-affinity-DA4WHv1h.mjs";
20
22
  import { a as withFirstStreamEventTimeout, i as getFirstStreamEventTimeoutMs, r as getFirstStreamEventTimeoutHandler, t as createFirstStreamEventAbortController } from "./stream-first-event-timeout-D9ZmL_s9.mjs";
21
- import { a as resolveOpenAIProjectedToolsStrictToolFlag, f as withPreparedToolSchemaNormalization, r as normalizeOpenAIStrictToolParameters } from "./openai-tool-schema-BIrI0mrA.mjs";
23
+ import { a as resolveOpenAIProjectedToolsStrictToolFlag, f as withPreparedToolSchemaNormalization, r as normalizeOpenAIStrictToolParameters, t as findOpenAIStrictToolProjectionDiagnostics } from "./openai-tool-schema-BIrI0mrA.mjs";
22
24
  import { n as isOpenAIResponsesCompactionOutput, r as readOpenAIResponsesCompactionWindow } from "./openai-responses-compaction-window-tA4QkU0o.mjs";
23
- import { randomUUID } from "node:crypto";
24
- //#region packages/ai/src/transports/openai-responses-replay.ts
25
- /** Resolves the assistant message id that can be replayed to OpenAI Responses. */
26
- function resolveReplayableResponsesMessageId(params) {
27
- if (!params.replayResponsesItemIds) return;
28
- if (!params.textSignatureId) return params.fallbackOrdinal === 0 ? params.fallbackId : `${params.fallbackId}_${params.fallbackOrdinal}`;
29
- return params.previousReplayItemWasReasoning ? params.textSignatureId : void 0;
25
+ import { hash, randomUUID } from "node:crypto";
26
+ //#region packages/normalization-core/src/node-crypto.ts
27
+ function sha256Hex(input) {
28
+ return hash("sha256", input, "hex");
29
+ }
30
+ function sha256HexPrefixCore(input, length) {
31
+ return sha256Hex(input).slice(0, length);
32
+ }
33
+ /** Redacts an identifier to a stable hash label, or "-" for missing values. */
34
+ function redactIdentifier(value, opts) {
35
+ const trimmed = normalizeOptionalString(value);
36
+ if (!trimmed) return "-";
37
+ const len = opts?.len ?? 12;
38
+ return `sha256:${sha256HexPrefixCore(trimmed, Number.isFinite(len) ? Math.max(1, Math.floor(len)) : 12)}`;
30
39
  }
31
40
  //#endregion
32
- //#region packages/ai/src/transports/openai-responses-tool-call-id-shape.ts
33
- /**
34
- * Pure OpenAI Responses `call_*`/`fc_*` tool-call-id shape helpers.
35
- *
36
- * Moved out of embedded-agent-helpers/openai.ts (the AgentMessage[]-walking
37
- * caller lives in src/agents and imports from packages/ai, so the reverse
38
- * import direction is not available) so the continuation transport can
39
- * recognize its own cached raw ids after this same reshaping and restore
40
- * them on a replayed wire request -- see openai-responses-continuation.ts.
41
- */
42
- const OPENAI_RESPONSES_ID_MAX_LENGTH = 64;
43
- const OPENAI_RESPONSES_CALL_ID_RE = /^call_[A-Za-z0-9_-]{1,59}$/;
44
- const OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE = /^fc_[A-Za-z0-9_-]{1,61}$/;
45
- function splitOpenAIFunctionCallPairing(id) {
46
- const separator = id.indexOf("|");
47
- if (separator <= 0 || separator >= id.length - 1) return { callId: id };
41
+ //#region packages/ai/src/transports/openai-transport-shared.ts
42
+ const OPENAI_RESPONSE_MODEL_HEADER_NAMES = /* @__PURE__ */ new Set(["openai-model", "x-openai-model"]);
43
+ const OPENAI_RESPONSE_MODEL_EVENT_TYPES = /* @__PURE__ */ new Set([
44
+ "response.created",
45
+ "response.in_progress",
46
+ "response.completed",
47
+ "response.done",
48
+ "response.incomplete",
49
+ "response.failed"
50
+ ]);
51
+ const OPENAI_DATED_MODEL_SUFFIX = /-(?:\d{8}|\d{4}-\d{2}-\d{2})$/;
52
+ const GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP = "skip_thought_signature_validator";
53
+ const log = {
54
+ debug(message, data) {
55
+ getAiTransportHost().logDebug("openai-transport", () => ({
56
+ message,
57
+ data
58
+ }));
59
+ },
60
+ info(message, data) {
61
+ getAiTransportHost().logInfo("openai-transport", message, data);
62
+ },
63
+ warn(message, data) {
64
+ getAiTransportHost().logWarn("openai-transport", message, data);
65
+ }
66
+ };
67
+ const OPENAI_RESPONSE_MODEL_CONFLICT = "Conflicting OpenAI response model attestations";
68
+ function splitOpenAIResponseModelHeader(value) {
69
+ return value ? value.split(",").map((part) => part.trim()).filter(Boolean) : [];
70
+ }
71
+ function readOpenAIResponseModelHeaders(headers) {
72
+ return [...OPENAI_RESPONSE_MODEL_HEADER_NAMES].flatMap((name) => splitOpenAIResponseModelHeader(headers.get(name)));
73
+ }
74
+ function readOpenAIResponseModelHeaderRecord(headers) {
75
+ if (!isRecord(headers)) return [];
76
+ return Object.entries(headers).filter(([name]) => OPENAI_RESPONSE_MODEL_HEADER_NAMES.has(name.toLowerCase())).flatMap(([, value]) => typeof value === "string" ? splitOpenAIResponseModelHeader(value) : []);
77
+ }
78
+ function readOpenAIResponseModelEvent(event) {
79
+ if (!isRecord(event)) return [];
80
+ const response = isRecord(event.response) ? event.response : void 0;
81
+ return [
82
+ ...OPENAI_RESPONSE_MODEL_EVENT_TYPES.has(String(event.type)) && typeof response?.model === "string" && response.model.trim() ? [response.model.trim()] : [],
83
+ ...readOpenAIResponseModelHeaderRecord(response?.headers),
84
+ ...readOpenAIResponseModelHeaderRecord(event.headers)
85
+ ];
86
+ }
87
+ function reconcileOpenAIResponseModels(current, observed) {
88
+ if (!current || current === observed) return observed;
89
+ const currentBase = current.replace(OPENAI_DATED_MODEL_SUFFIX, "");
90
+ const observedBase = observed.replace(OPENAI_DATED_MODEL_SUFFIX, "");
91
+ if (currentBase !== observedBase) return;
92
+ if (currentBase === current && observedBase !== observed) return observed;
93
+ if (observedBase === observed && currentBase !== current) return current;
94
+ }
95
+ function createResponseModelTracker(enabled = true) {
96
+ let responseModel;
97
+ const observe = (models) => {
98
+ for (const model of models) {
99
+ const reconciled = reconcileOpenAIResponseModels(responseModel, model);
100
+ if (!reconciled) throw new Error(OPENAI_RESPONSE_MODEL_CONFLICT);
101
+ responseModel = reconciled;
102
+ }
103
+ };
104
+ const begin = (headers) => {
105
+ responseModel = void 0;
106
+ if (enabled && headers) observe(readOpenAIResponseModelHeaders(headers));
107
+ };
108
+ const observeEvent = (event) => {
109
+ if (enabled) observe(readOpenAIResponseModelEvent(event));
110
+ return responseModel;
111
+ };
112
+ const resolve = () => responseModel;
48
113
  return {
49
- callId: id.slice(0, separator),
50
- itemId: id.slice(separator + 1)
114
+ begin,
115
+ observeEvent,
116
+ resolve,
117
+ track(response, stream) {
118
+ if (!enabled) return stream;
119
+ return (async function* () {
120
+ begin(response?.headers);
121
+ for await (const event of stream) {
122
+ observeEvent(event);
123
+ yield event;
124
+ }
125
+ })();
126
+ },
127
+ terminalOptions: enabled ? { resolveResponseModel: resolve } : {}
51
128
  };
52
129
  }
53
- function shortOpenAIResponsesIdHash(id) {
54
- return sha256Hex(id).slice(0, 10);
130
+ function resolveOpenAIClientBaseUrl(model, baseUrl = model.baseUrl) {
131
+ if (baseUrl?.trim()) return baseUrl;
132
+ if (model.provider.trim().toLowerCase() === "openai") return;
133
+ throw new Error(`Provider "${model.provider}" requires an explicit base URL before using an OpenAI-compatible API. Reload provider metadata or configure an endpoint.`);
55
134
  }
56
- function sanitizeOpenAIResponsesIdTail(value) {
57
- return value.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
135
+ const EMPTY_OPENAI_COMPLETIONS_REASONING_BATCH = createOpenAICompletionsReasoningBatch();
136
+ const OPENAI_COMPLETIONS_REASONING_FIELDS = [
137
+ "reasoning_content",
138
+ "reasoning",
139
+ "reasoning_text"
140
+ ];
141
+ function appendOpenAICompletionsReasoningDelta(batch, next) {
142
+ if (next.kind === "thinking") batch.hasThinking = true;
143
+ else batch.hasVisibleText = true;
144
+ const previous = batch.deltas[batch.deltas.length - 1];
145
+ if (!previous || previous.kind !== next.kind || next.kind === "thinking" && previous.kind === "thinking" && previous.signature !== next.signature) {
146
+ batch.deltas.push(next);
147
+ if (next.kind === "thinking") batch.mirroredThinking.push(next.text);
148
+ return;
149
+ }
150
+ previous.text += next.text;
151
+ if (next.kind === "thinking") batch.mirroredThinking[batch.mirroredThinking.length - 1] += next.text;
58
152
  }
59
- function normalizeOpenAIResponsesIdPart(params) {
60
- const trimmed = params.value.trim();
61
- if (params.isValid(trimmed)) return trimmed;
62
- const rawTail = trimmed.startsWith(params.prefix) ? trimmed.slice(params.prefix.length) : trimmed;
63
- const hash = shortOpenAIResponsesIdHash(trimmed || params.prefix);
64
- const maxTailLength = 64 - params.prefix.length;
65
- const hashSuffix = `_${hash}`;
66
- const tail = `${sanitizeOpenAIResponsesIdTail(rawTail).slice(0, Math.max(1, maxTailLength - hashSuffix.length)) || "id"}${hashSuffix}`.slice(0, maxTailLength);
67
- return `${params.prefix}${tail}`;
153
+ function createOpenAICompletionsReasoningBatch() {
154
+ return {
155
+ deltas: [],
156
+ mirroredThinking: [],
157
+ hasThinking: false,
158
+ hasVisibleText: false
159
+ };
160
+ }
161
+ function readOpenAICompletionsReasoningBatch(delta, visibleReasoningDetailTypes) {
162
+ let batch;
163
+ const reasoningDetails = delta.reasoning_details;
164
+ let usedReasoningThinkingDetails = false;
165
+ if (Array.isArray(reasoningDetails)) for (const item of reasoningDetails) {
166
+ if (!isRecord(item)) continue;
167
+ const detail = item;
168
+ if (typeof detail.text !== "string" || !detail.text) continue;
169
+ if (detail.type === "reasoning.text") {
170
+ usedReasoningThinkingDetails = true;
171
+ batch ??= createOpenAICompletionsReasoningBatch();
172
+ appendOpenAICompletionsReasoningDelta(batch, {
173
+ kind: "thinking",
174
+ signature: "reasoning_details",
175
+ text: detail.text
176
+ });
177
+ continue;
178
+ }
179
+ if (typeof detail.type === "string" && visibleReasoningDetailTypes.has(detail.type)) {
180
+ batch ??= createOpenAICompletionsReasoningBatch();
181
+ appendOpenAICompletionsReasoningDelta(batch, {
182
+ kind: "text",
183
+ text: detail.text,
184
+ source: "reasoning_detail"
185
+ });
186
+ }
187
+ }
188
+ if (!usedReasoningThinkingDetails) for (const field of OPENAI_COMPLETIONS_REASONING_FIELDS) {
189
+ const value = delta[field];
190
+ if (typeof value === "string" && value.length > 0) {
191
+ batch ??= createOpenAICompletionsReasoningBatch();
192
+ appendOpenAICompletionsReasoningDelta(batch, {
193
+ kind: "thinking",
194
+ signature: field,
195
+ text: value
196
+ });
197
+ break;
198
+ }
199
+ }
200
+ return batch ?? EMPTY_OPENAI_COMPLETIONS_REASONING_BATCH;
201
+ }
202
+ function parseOpenAICompletionsUsage(rawUsage, model, options) {
203
+ const cacheRead = rawUsage.prompt_tokens_details?.cached_tokens ?? rawUsage.prompt_cache_hit_tokens ?? 0;
204
+ const cacheWrite = rawUsage.prompt_tokens_details?.cache_write_tokens ?? rawUsage.prompt_tokens_details?.cache_creation_input_tokens ?? rawUsage.cache_creation_input_tokens ?? 0;
205
+ const input = Math.max(0, (rawUsage.prompt_tokens || 0) - cacheRead - cacheWrite);
206
+ const output = rawUsage.completion_tokens || 0;
207
+ const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens;
208
+ const hasCoherentContext = [
209
+ rawUsage.prompt_tokens,
210
+ rawUsage.completion_tokens,
211
+ rawUsage.total_tokens,
212
+ cacheRead,
213
+ cacheWrite
214
+ ].every((value) => typeof value === "number" && Number.isFinite(value) && value >= 0) && rawUsage.prompt_tokens >= cacheRead + cacheWrite && rawUsage.total_tokens >= rawUsage.prompt_tokens + rawUsage.completion_tokens;
215
+ const usage = {
216
+ input,
217
+ output,
218
+ cacheRead,
219
+ cacheWrite,
220
+ ...options?.includeReasoningTokens !== false && typeof reasoningTokens === "number" && Number.isFinite(reasoningTokens) ? { reasoningTokens } : {},
221
+ contextUsage: hasCoherentContext ? {
222
+ state: "available",
223
+ promptTokens: rawUsage.prompt_tokens,
224
+ totalTokens: Math.max(input + output + cacheRead + cacheWrite, rawUsage.total_tokens)
225
+ } : { state: "unavailable" },
226
+ totalTokens: input + output + cacheRead + cacheWrite,
227
+ cost: {
228
+ input: 0,
229
+ output: 0,
230
+ cacheRead: 0,
231
+ cacheWrite: 0,
232
+ total: 0
233
+ }
234
+ };
235
+ calculateCost(model, usage);
236
+ applyProviderReportedUsageCost(usage, rawUsage.cost);
237
+ return usage;
68
238
  }
69
- /** Same shaping `normalizeOpenAIResponsesToolCallIds` applies to a replayed `call_id`/`call_id|fc_id` pair. */
70
- function normalizeOpenAIResponsesFunctionCallId(id) {
71
- const { callId, itemId } = splitOpenAIFunctionCallPairing(id);
72
- const normalizedCallId = normalizeOpenAIResponsesIdPart({
73
- value: itemId ? `${callId}|${itemId}` : callId,
74
- prefix: "call_",
75
- isValid: (value) => OPENAI_RESPONSES_CALL_ID_RE.test(value)
239
+ function createOpenAIResponseHook(onResponse, response, model) {
240
+ return onResponse ? () => onResponse({
241
+ status: response.status,
242
+ headers: headersToRecord(response.headers)
243
+ }, model) : void 0;
244
+ }
245
+ function createOpenAIProviderAcceptanceHook(options, response, model) {
246
+ return () => notifyProviderHttpResponse({
247
+ options,
248
+ response,
249
+ model
76
250
  });
77
- if (!itemId) return normalizedCallId;
78
- return `${normalizedCallId}|${normalizeOpenAIResponsesIdPart({
79
- value: itemId,
80
- prefix: "fc_",
81
- isValid: (value) => OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE.test(value)
82
- })}`;
83
251
  }
84
- function shouldNormalizeOpenAIResponsesToolCallId(id) {
85
- const pairing = splitOpenAIFunctionCallPairing(id);
86
- if (!OPENAI_RESPONSES_CALL_ID_RE.test(pairing.callId)) return true;
87
- if (pairing.itemId === void 0) return false;
88
- return !OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE.test(pairing.itemId);
252
+ /** Measure one UTF-8 append without double-counting a surrogate pair split across chunks. */
253
+ function measureUtf8AppendBytes(bufferEndsWithHighSurrogate, chunk) {
254
+ let bytes = Buffer.byteLength(chunk, "utf8");
255
+ if (!chunk) return {
256
+ bytes,
257
+ endsWithHighSurrogate: bufferEndsWithHighSurrogate
258
+ };
259
+ const nextCodeUnit = chunk.charCodeAt(0);
260
+ if (bufferEndsWithHighSurrogate && nextCodeUnit >= 56320 && nextCodeUnit <= 57343) bytes -= 2;
261
+ const finalCodeUnit = chunk.charCodeAt(chunk.length - 1);
262
+ return {
263
+ bytes,
264
+ endsWithHighSurrogate: finalCodeUnit >= 55296 && finalCodeUnit <= 56319
265
+ };
266
+ }
267
+ function resolvePromptCacheKey(options, cacheRetention) {
268
+ if (cacheRetention === "none") return;
269
+ return clampOpenAIPromptCacheKey(options?.promptCacheKey ?? options?.sessionId);
270
+ }
271
+ function isOpenAICompletionsThinkingEnabled(effort) {
272
+ const normalized = effort.trim().toLowerCase();
273
+ return normalized !== "off" && normalized !== "none";
274
+ }
275
+ function readOpenAICompletionsContentDeltas(content, topLevelRefusal, mirroredThinking = []) {
276
+ let deltas = readOpenAICompletionsContentPartDeltas(content);
277
+ if (mirroredThinking.length > 0) {
278
+ const structuredThinking = deltas.filter((delta) => delta.kind === "thinking").map((delta) => delta.text);
279
+ const mirrorsCombinedThinking = structuredThinking.length > 1 && mirroredThinking.includes(structuredThinking.join(""));
280
+ deltas = deltas.filter((delta) => delta.kind !== "thinking" || !mirrorsCombinedThinking && !mirroredThinking.includes(delta.text));
281
+ }
282
+ if (typeof topLevelRefusal !== "string" || !topLevelRefusal) return deltas;
283
+ const structuredRefusals = deltas.filter((delta) => delta.kind === "text" && delta.source === "refusal").map((delta) => delta.text);
284
+ if (structuredRefusals.some((refusal) => refusal === topLevelRefusal) || structuredRefusals.length > 1 && structuredRefusals.join("") === topLevelRefusal) return deltas;
285
+ return [...deltas, {
286
+ kind: "text",
287
+ text: topLevelRefusal,
288
+ source: "refusal"
289
+ }];
290
+ }
291
+ function readOpenAICompletionsContentPartDeltas(content) {
292
+ if (typeof content === "string") return content ? [{
293
+ kind: "text",
294
+ text: content
295
+ }] : [];
296
+ if (Array.isArray(content)) return content.flatMap(readOpenAICompletionsContentPartDeltas);
297
+ if (!content || typeof content !== "object") return [];
298
+ const record = content;
299
+ const type = typeof record.type === "string" ? record.type.toLowerCase() : "";
300
+ const extractText = (value) => {
301
+ if (typeof value === "string") return value;
302
+ if (Array.isArray(value)) return value.map(extractText).join("");
303
+ if (value && typeof value === "object") {
304
+ const nested = value;
305
+ return extractText(nested.text ?? nested.content ?? nested.thinking ?? nested.refusal);
306
+ }
307
+ return "";
308
+ };
309
+ const text = extractText(record.text ?? record.content ?? record.thinking ?? record.refusal);
310
+ if (!text) return [];
311
+ if (type.includes("thinking") || type.includes("reasoning")) return [{
312
+ kind: "thinking",
313
+ text
314
+ }];
315
+ if (type === "refusal") return [{
316
+ kind: "text",
317
+ text,
318
+ source: "refusal"
319
+ }];
320
+ if (["text", "output_text"].includes(type) || type.endsWith(".output_text")) return [{
321
+ kind: "text",
322
+ text
323
+ }];
324
+ return [];
325
+ }
326
+ //#endregion
327
+ //#region packages/ai/src/providers/openai-tool-projection.ts
328
+ function unreadableToolDiagnostic(toolIndex) {
329
+ return {
330
+ toolIndex,
331
+ violations: [`tool[${toolIndex}] is unreadable`]
332
+ };
333
+ }
334
+ /** Snapshots direct/custom tool descriptors before OpenAI payload construction. */
335
+ function projectOpenAITools(tools) {
336
+ return projectOpenAIToolDescriptors(tools);
337
+ }
338
+ /** Package-private facts are consumed before the projection or payload can escape. */
339
+ function prepareOpenAITools(tools) {
340
+ const schemas = /* @__PURE__ */ new Map();
341
+ return {
342
+ projection: projectOpenAIToolDescriptors(tools, schemas),
343
+ schemas
344
+ };
345
+ }
346
+ function projectOpenAIToolDescriptors(tools, schemas) {
347
+ let inputToolCount;
348
+ try {
349
+ inputToolCount = tools.length;
350
+ } catch {
351
+ return {
352
+ inputToolCount: 0,
353
+ tools: [],
354
+ diagnostics: [unreadableToolDiagnostic(0)]
355
+ };
356
+ }
357
+ const projectedTools = [];
358
+ const diagnostics = [];
359
+ for (let toolIndex = 0; toolIndex < inputToolCount; toolIndex += 1) {
360
+ let tool;
361
+ try {
362
+ const candidate = tools[toolIndex];
363
+ if (!candidate) {
364
+ diagnostics.push(unreadableToolDiagnostic(toolIndex));
365
+ continue;
366
+ }
367
+ tool = candidate;
368
+ } catch {
369
+ diagnostics.push(unreadableToolDiagnostic(toolIndex));
370
+ continue;
371
+ }
372
+ let name;
373
+ try {
374
+ name = tool.name;
375
+ } catch {
376
+ diagnostics.push({
377
+ toolIndex,
378
+ violations: [`tool[${toolIndex}].name is unreadable`]
379
+ });
380
+ continue;
381
+ }
382
+ if (typeof name !== "string" || !name) {
383
+ diagnostics.push({
384
+ toolIndex,
385
+ violations: [`tool[${toolIndex}].name is empty`]
386
+ });
387
+ continue;
388
+ }
389
+ let parameters;
390
+ try {
391
+ parameters = tool.parameters;
392
+ } catch {
393
+ diagnostics.push({
394
+ toolIndex,
395
+ toolName: name,
396
+ violations: [`${name}.parameters is unreadable`]
397
+ });
398
+ continue;
399
+ }
400
+ const prepared = schemas ? prepareRuntimeToolInputSchema(parameters ?? {}, `${name}.parameters`) : void 0;
401
+ const schemaProjection = prepared?.projection ?? projectRuntimeToolInputSchema(parameters ?? {}, `${name}.parameters`);
402
+ if (!isRecord(schemaProjection.schema) || schemaProjection.violations.length > 0) {
403
+ diagnostics.push({
404
+ toolIndex,
405
+ toolName: name,
406
+ violations: schemaProjection.violations.length > 0 ? schemaProjection.violations : [`${name}.parameters must be a JSON object schema`]
407
+ });
408
+ continue;
409
+ }
410
+ if (prepared?.normalization) schemas?.set(schemaProjection.schema, prepared.normalization);
411
+ let descriptionValue;
412
+ try {
413
+ descriptionValue = tool.description;
414
+ } catch {}
415
+ const description = typeof descriptionValue === "string" ? descriptionValue : void 0;
416
+ projectedTools.push({
417
+ toolIndex,
418
+ name,
419
+ ...description !== void 0 ? { description } : {},
420
+ parameters: schemaProjection.schema
421
+ });
422
+ }
423
+ return {
424
+ inputToolCount,
425
+ tools: projectedTools,
426
+ diagnostics
427
+ };
428
+ }
429
+ function reconcileToolChoice(choice, projection, responses) {
430
+ const label = responses ? "OpenAI Responses" : "OpenAI Chat Completions";
431
+ if (choice === "auto") return projection.tools.length > 0 ? choice : void 0;
432
+ if (choice === "required") {
433
+ if (projection.tools.length === 0) throw new Error(`${label} tool_choice requires a tool, but no tools survived schema conversion`);
434
+ return choice;
435
+ }
436
+ if (choice === "none" || !isRecord(choice)) return choice;
437
+ const choiceType = choice.type;
438
+ if (!responses && choiceType === "custom") throw new Error("OpenAI Chat Completions custom tool_choice is unsupported because this adapter emits function tools only");
439
+ if (choiceType === "function") {
440
+ const functionChoice = responses ? choice : choice.function;
441
+ const functionName = isRecord(functionChoice) ? functionChoice.name : void 0;
442
+ if (typeof functionName !== "string") return choice;
443
+ if (!projection.tools.some((tool) => tool.name === functionName)) throw new Error(`${label} tool_choice requested unavailable tool "${functionName}" after schema conversion`);
444
+ return responses ? {
445
+ type: "function",
446
+ name: functionName
447
+ } : {
448
+ type: "function",
449
+ function: { name: functionName }
450
+ };
451
+ }
452
+ if (choiceType !== "allowed_tools") return choice;
453
+ const allowedConfig = responses ? choice : choice.allowed_tools;
454
+ if (!isRecord(allowedConfig)) return choice;
455
+ const { mode, tools } = allowedConfig;
456
+ if (mode !== "auto" && mode !== "required" || !Array.isArray(tools)) return choice;
457
+ const responseTools = [];
458
+ const completionTools = [];
459
+ for (const tool of tools) {
460
+ if (!isRecord(tool) || tool.type !== "function") {
461
+ if (responses) responseTools.push(tool);
462
+ continue;
463
+ }
464
+ const functionChoice = responses ? tool : tool.function;
465
+ const functionName = isRecord(functionChoice) ? functionChoice.name : void 0;
466
+ if (typeof functionName === "string" && projection.tools.some((projectedTool) => projectedTool.name === functionName)) {
467
+ if (responses) responseTools.push({
468
+ type: "function",
469
+ name: functionName
470
+ });
471
+ else completionTools.push({
472
+ type: "function",
473
+ function: { name: functionName }
474
+ });
475
+ }
476
+ }
477
+ if (responseTools.length === 0 && completionTools.length === 0) {
478
+ if (mode === "auto") return "none";
479
+ throw new Error(`${label} tool_choice requires a tool, but no allowed tools survived schema conversion`);
480
+ }
481
+ return responses ? {
482
+ type: "allowed_tools",
483
+ mode,
484
+ tools: responseTools
485
+ } : {
486
+ type: "allowed_tools",
487
+ allowed_tools: {
488
+ mode,
489
+ tools: completionTools
490
+ }
491
+ };
492
+ }
493
+ /** Keeps Responses tool choices aligned with surviving function schemas. */
494
+ function reconcileOpenAIResponsesToolChoice(choice, projection) {
495
+ return reconcileToolChoice(choice, projection, true);
496
+ }
497
+ /** Keeps Chat Completions tool choices aligned with surviving function schemas. */
498
+ function reconcileOpenAICompletionsToolChoice(choice, projection) {
499
+ return reconcileToolChoice(choice, projection, false);
500
+ }
501
+ /** Re-expresses an object Chat Completions tool choice in the Responses wire shape. */
502
+ function toOpenAIResponsesToolChoice(choice) {
503
+ if (choice.type === "function") return {
504
+ type: "function",
505
+ name: choice.function.name
506
+ };
507
+ return {
508
+ type: "allowed_tools",
509
+ mode: choice.allowed_tools.mode,
510
+ tools: choice.allowed_tools.tools.map((tool) => tool.type === "function" && isRecord(tool.function) && typeof tool.function.name === "string" ? {
511
+ type: "function",
512
+ name: tool.function.name
513
+ } : tool)
514
+ };
515
+ }
516
+ //#endregion
517
+ //#region packages/ai/src/transports/openai-reasoning-compat.ts
518
+ /** Explicit model reasoning-effort compatibility metadata. */
519
+ function readCompatReasoningEffortMap(compat) {
520
+ const rawMap = asOptionalObjectRecord(asOptionalObjectRecord(compat)?.reasoningEffortMap);
521
+ if (!rawMap) return {};
522
+ return Object.fromEntries(Object.entries(rawMap).filter((entry) => typeof entry[1] === "string"));
523
+ }
524
+ /** Resolves the reasoning effort remap for an OpenAI-compatible model. */
525
+ function resolveOpenAIReasoningEffortMap(model, fallbackMap = {}) {
526
+ return {
527
+ ...fallbackMap,
528
+ ...readCompatReasoningEffortMap(model.compat)
529
+ };
530
+ }
531
+ //#endregion
532
+ //#region packages/ai/src/providers/openai-request-reasoning.ts
533
+ /** Preserve explicit off until the selected route maps it to its native value. */
534
+ function resolveOpenAISimpleReasoningEffort(model, reasoning) {
535
+ return reasoning === void 0 ? void 0 : clampThinkingLevel(model, reasoning);
536
+ }
537
+ function resolveOpenAIRequestReasoning(model, reasoning) {
538
+ const requested = normalizeOpenAIReasoningEffort(reasoning ?? "off");
539
+ const modelLevel = MODEL_CATALOG_THINKING_LEVELS.find((candidate) => candidate === requested);
540
+ const modelMapped = modelLevel ? model.thinkingLevelMap?.[modelLevel] : void 0;
541
+ const mapped = modelMapped === null ? null : resolveOpenAIReasoningEffortMapping(requested, resolveOpenAIReasoningEffortMap(model)) ?? modelMapped;
542
+ const intent = mapped !== void 0 ? mapped?.trim() : reasoning === void 0 ? void 0 : requested;
543
+ const normalizedIntent = normalizeOpenAIReasoningEffort(intent ?? "off");
544
+ const supported = resolveOpenAIModelReasoningEfforts(model);
545
+ const effort = !model.reasoning || supported?.length === 0 || intent === void 0 ? void 0 : supported === void 0 ? mapped !== void 0 ? intent : requested === "off" ? "none" : requested : resolveOpenAIReasoningEffortForModel({
546
+ model,
547
+ effort: requested,
548
+ fallbackMap: { [requested]: intent }
549
+ });
550
+ return {
551
+ effort: effort === "none" && resolveOpenAIThinkingApi(model.api) === "openai-chatgpt-responses" && !supportsOpenAIReasoningEffort(model.id === "gpt-6-sol" || model.id === "gpt-6-luna" ? model : { compat: model.compat }, "none") ? void 0 : effort,
552
+ thinkingEnabled: intent === void 0 ? void 0 : model.reasoning && normalizedIntent !== "off" && normalizedIntent !== "none"
553
+ };
554
+ }
555
+ //#endregion
556
+ //#region packages/ai/src/transports/openai-transport-params.ts
557
+ const MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS = 256;
558
+ const loggedOpenAIStrictToolDowngradeDiagnosticKeys = /* @__PURE__ */ new Set();
559
+ const OPENAI_COMPLETIONS_APIS = /* @__PURE__ */ new Set(["openai-completions", "openclaw-openai-completions-transport"]);
560
+ function readToolPayloadField$1(record, field) {
561
+ try {
562
+ return Object.hasOwn(record, field) ? record[field] : void 0;
563
+ } catch {
564
+ return;
565
+ }
566
+ }
567
+ function readCodeModePayloadToolName(tool) {
568
+ if (!isRecord(tool)) return;
569
+ const name = readToolPayloadField$1(tool, "name");
570
+ if (typeof name === "string") return name;
571
+ const fn = readToolPayloadField$1(tool, "function");
572
+ if (!isRecord(fn)) return;
573
+ const fnName = readToolPayloadField$1(fn, "name");
574
+ return typeof fnName === "string" ? fnName : void 0;
575
+ }
576
+ function readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes) {
577
+ if (!isRecord(tool)) return;
578
+ const type = readToolPayloadField$1(tool, "type");
579
+ if (typeof type === "string" && allowedHostedToolTypes?.has(type)) {
580
+ try {
581
+ if (Object.hasOwn(tool, "name") || Object.hasOwn(tool, "function") || Object.hasOwn(tool, "functionDeclarations") || Object.hasOwn(tool, "function_declarations")) return false;
582
+ } catch {
583
+ return false;
584
+ }
585
+ return `hosted:${type}`;
586
+ }
587
+ const name = readCodeModePayloadToolName(tool);
588
+ return typeof name === "string" && isCodeModeModelVisibleToolName(name, visibleToolNames) ? `client:${name}` : void 0;
589
+ }
590
+ function readCodeModePayloadToolIdentities(payload) {
591
+ if (!isRecord(payload)) return [];
592
+ const tools = readToolPayloadField$1(payload, "tools");
593
+ if (!Array.isArray(tools)) return [];
594
+ return tools.flatMap((tool) => {
595
+ if (!isRecord(tool)) return [];
596
+ const identities = [];
597
+ const name = readCodeModePayloadToolName(tool);
598
+ if (name) identities.push(`client:${name}`);
599
+ for (const key of ["functionDeclarations", "function_declarations"]) {
600
+ const declarations = readToolPayloadField$1(tool, key);
601
+ if (!Array.isArray(declarations)) continue;
602
+ for (const declaration of declarations) {
603
+ const declarationName = readCodeModePayloadToolName(declaration);
604
+ if (declarationName) identities.push(`client:${declarationName}`);
605
+ }
606
+ }
607
+ const type = readToolPayloadField$1(tool, "type");
608
+ if (typeof type === "string" && type !== "function") identities.push(`hosted:${type}`);
609
+ return identities;
610
+ });
611
+ }
612
+ function filterCodeModePayloadTools(payload, visibleToolNames, allowedHostedToolTypes, observer) {
613
+ if (!isRecord(payload)) return;
614
+ const tools = readToolPayloadField$1(payload, "tools");
615
+ if (!Array.isArray(tools)) return;
616
+ const beforeToolIdentities = observer ? readCodeModePayloadToolIdentities(payload) : void 0;
617
+ payload.tools = tools.flatMap((tool) => {
618
+ const identity = readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes);
619
+ if (identity) return [tool];
620
+ if (identity === false) return [];
621
+ if (!isRecord(tool)) return [];
622
+ const filteredGroups = {};
623
+ for (const key of ["functionDeclarations", "function_declarations"]) {
624
+ const declarations = readToolPayloadField$1(tool, key);
625
+ if (!Array.isArray(declarations)) continue;
626
+ const filtered = declarations.filter((declaration) => {
627
+ const declarationName = readCodeModePayloadToolName(declaration);
628
+ return typeof declarationName === "string" && isCodeModeModelVisibleToolName(declarationName, visibleToolNames);
629
+ });
630
+ if (filtered.length > 0) filteredGroups[key] = filtered;
631
+ }
632
+ return Object.keys(filteredGroups).length > 0 ? [filteredGroups] : [];
633
+ });
634
+ if (beforeToolIdentities) observer?.({
635
+ beforeToolIdentities,
636
+ afterToolIdentities: readCodeModePayloadToolIdentities(payload)
637
+ });
638
+ }
639
+ function resolveCodeModeResponsesVisibleToolNames(context) {
640
+ return new Set((context.tools ?? []).map(readCodeModePayloadToolName).filter((name) => typeof name === "string"));
641
+ }
642
+ function enforceCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes, observer) {
643
+ if (!isRecord(payload)) return;
644
+ const tools = readToolPayloadField$1(payload, "tools");
645
+ if (!Array.isArray(tools)) return;
646
+ const beforeToolIdentities = observer ? readCodeModePayloadToolIdentities(payload) : void 0;
647
+ payload.tools = tools.filter((tool) => Boolean(readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes)));
648
+ if (beforeToolIdentities) observer?.({
649
+ beforeToolIdentities,
650
+ afterToolIdentities: readCodeModePayloadToolIdentities(payload)
651
+ });
652
+ }
653
+ function assertCodeModeResponsesToolSurface(payload, visibleToolNames, allowedHostedToolTypes) {
654
+ const tools = isRecord(payload) ? readToolPayloadField$1(payload, "tools") : void 0;
655
+ if (!Array.isArray(tools)) throw new Error("Code mode payload tool surface violation: expected exec,wait; got no tools");
656
+ const identities = tools.map((tool) => readCodeModePayloadToolIdentity(tool, visibleToolNames, allowedHostedToolTypes));
657
+ const names = identities.flatMap((identity) => typeof identity === "string" && identity.startsWith("client:") ? [identity.slice(7)] : []).toSorted((left, right) => left.localeCompare(right));
658
+ if (names.length >= 2 && identities.every((identity) => typeof identity === "string") && new Set(identities).size === identities.length && names.includes("exec") && names.includes("wait")) return;
659
+ throw new Error(`Code mode payload tool surface violation: expected exec,wait plus direct-only tools; got ${names.length > 0 ? names.join(",") : "none"}`);
660
+ }
661
+ function resolveOpenAIStrictToolFlagWithDiagnostics(projection, strictSetting, context) {
662
+ const strict = resolveOpenAIProjectedToolsStrictToolFlag(projection, strictSetting);
663
+ if (strictSetting === true && strict === false) getAiTransportHost().logDebug("openai-transport", () => {
664
+ const diagnostics = findOpenAIStrictToolProjectionDiagnostics(projection);
665
+ const key = sha256Hex(JSON.stringify({
666
+ transport: context.transport,
667
+ provider: context.model.provider ?? null,
668
+ model: context.model.id ?? null,
669
+ diagnostics: diagnostics.map((entry) => ({
670
+ toolIndex: entry.toolIndex,
671
+ toolName: entry.toolName ?? null,
672
+ violations: entry.violations
673
+ }))
674
+ }));
675
+ if (loggedOpenAIStrictToolDowngradeDiagnosticKeys.has(key)) return null;
676
+ if (loggedOpenAIStrictToolDowngradeDiagnosticKeys.size >= MAX_OPENAI_STRICT_TOOL_DOWNGRADE_DIAGNOSTIC_KEYS) loggedOpenAIStrictToolDowngradeDiagnosticKeys.clear();
677
+ loggedOpenAIStrictToolDowngradeDiagnosticKeys.add(key);
678
+ const sample = diagnostics.slice(0, 5).map((entry) => ({
679
+ tool: entry.toolName ?? `tool[${entry.toolIndex}]`,
680
+ violations: entry.violations.slice(0, 8)
681
+ }));
682
+ return {
683
+ message: `OpenAI ${context.transport} tool schema strict mode downgraded to strict=false for ${context.model.provider ?? "unknown"}/${context.model.id ?? "unknown"} because ${diagnostics.length} tool schema(s) are not strict-compatible`,
684
+ data: {
685
+ transport: context.transport,
686
+ provider: context.model.provider,
687
+ model: context.model.id,
688
+ incompatibleToolCount: diagnostics.length,
689
+ sample
690
+ }
691
+ };
692
+ });
693
+ return strict;
694
+ }
695
+ function buildOpenAIClientHeaders(model, context, optionHeaders, turnHeaders, sessionId, cacheRetention) {
696
+ const providerHeaders = { ...model.headers };
697
+ if (model.provider === "github-copilot") Object.assign(providerHeaders, getAiTransportHost().buildCopilotDynamicHeaders(context.messages));
698
+ if (OPENAI_COMPLETIONS_APIS.has(model.api) && sessionId && cacheRetention !== "none") {
699
+ const { sessionAffinity } = resolveOpenAICompletionsCompat(model);
700
+ if (sessionAffinity !== "none") {
701
+ const affinityValue = clampOpenAIPromptCacheKey(sessionId) ?? sessionId;
702
+ const affinityHeaders = sessionAffinity === "openrouter" ? ["x-session-id"] : [
703
+ "session_id",
704
+ "x-client-request-id",
705
+ "x-session-affinity"
706
+ ];
707
+ const configuredHeaders = new Set(Object.keys(providerHeaders).map((key) => key.toLowerCase()));
708
+ for (const name of affinityHeaders) if (!configuredHeaders.has(name)) providerHeaders[name] = affinityValue;
709
+ }
710
+ }
711
+ const callerHeaders = {
712
+ ...optionHeaders,
713
+ ...turnHeaders
714
+ };
715
+ const resolvedHeaders = getAiTransportHost().resolveProviderRequestHeaders({
716
+ provider: model.provider,
717
+ api: model.api,
718
+ baseUrl: model.baseUrl,
719
+ providerHeaders,
720
+ callerHeaders: Object.keys(callerHeaders).length > 0 ? callerHeaders : void 0,
721
+ precedence: "caller-wins",
722
+ model
723
+ }) ?? {};
724
+ const sendSessionIdHeader = (OPENAI_RESPONSES_APIS.has(model.api) && model.compat && "sendSessionIdHeader" in model.compat ? model.compat?.sendSessionIdHeader : void 0) ?? usesNativeOpenAICodexResponsesBackend(model);
725
+ if (sessionId && cacheRetention !== "none" && sendSessionIdHeader && !Object.keys(resolvedHeaders).some((key) => normalizeLowercaseStringOrEmpty(key) === "session_id")) resolvedHeaders.session_id = clampOpenAIPromptCacheKey(sessionId) ?? sessionId;
726
+ return resolveOpencodeSessionHeaders(model, {
727
+ sessionId,
728
+ headers: resolvedHeaders
729
+ }) ?? resolvedHeaders;
730
+ }
731
+ function buildOpenAISdkClientOptions(model) {
732
+ const timeout = resolveModelRequestTimeoutMs(model);
733
+ return {
734
+ ...timeout === void 0 ? {} : { timeout },
735
+ maxRetries: 0
736
+ };
737
+ }
738
+ function buildOpenAISdkRequestOptions(model, signal, options) {
739
+ const timeout = resolveModelRequestTimeoutMs(model, options?.timeoutMs);
740
+ const headers = options?.stream === true && usesNativeOpenAICodexResponsesBackend(model) ? { Accept: "text/event-stream" } : void 0;
741
+ if (timeout === void 0 && !signal && !headers) return;
742
+ return {
743
+ ...headers ? { headers } : {},
744
+ ...signal ? { signal } : {},
745
+ ...timeout !== void 0 ? { timeout } : {},
746
+ maxRetries: 0
747
+ };
748
+ }
749
+ function getCompat(model) {
750
+ const resolved = resolveOpenAICompletionsCompat(model);
751
+ const compat = model.compat ?? {};
752
+ return {
753
+ ...resolved,
754
+ reasoningEffortMap: resolveOpenAIReasoningEffortMap(model, {}),
755
+ openRouterRouting: resolved.openRouterRouting ?? {},
756
+ vercelGatewayRouting: resolved.vercelGatewayRouting,
757
+ requiresStringContent: compat.requiresStringContent ?? false,
758
+ strictMessageKeys: compat.strictMessageKeys === true
759
+ };
89
760
  }
90
761
  //#endregion
91
762
  //#region packages/ai/src/transports/openai-responses-compaction-replay.ts
@@ -289,6 +960,65 @@ function replayResponsesReasoningUpdates(previous, request, previousOutputLength
289
960
  };
290
961
  }
291
962
  //#endregion
963
+ //#region packages/ai/src/transports/openai-responses-tool-call-id-shape.ts
964
+ /**
965
+ * Pure OpenAI Responses `call_*`/`fc_*` tool-call-id shape helpers.
966
+ *
967
+ * Moved out of embedded-agent-helpers/openai.ts (the AgentMessage[]-walking
968
+ * caller lives in src/agents and imports from packages/ai, so the reverse
969
+ * import direction is not available) so the continuation transport can
970
+ * recognize its own cached raw ids after this same reshaping and restore
971
+ * them on a replayed wire request -- see openai-responses-continuation.ts.
972
+ */
973
+ const OPENAI_RESPONSES_ID_MAX_LENGTH = 64;
974
+ const OPENAI_RESPONSES_CALL_ID_RE = /^call_[A-Za-z0-9_-]{1,59}$/;
975
+ const OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE = /^fc_[A-Za-z0-9_-]{1,61}$/;
976
+ function splitOpenAIFunctionCallPairing(id) {
977
+ const separator = id.indexOf("|");
978
+ if (separator <= 0 || separator >= id.length - 1) return { callId: id };
979
+ return {
980
+ callId: id.slice(0, separator),
981
+ itemId: id.slice(separator + 1)
982
+ };
983
+ }
984
+ function shortOpenAIResponsesIdHash(id) {
985
+ return sha256Hex(id).slice(0, 10);
986
+ }
987
+ function sanitizeOpenAIResponsesIdTail(value) {
988
+ return value.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
989
+ }
990
+ function normalizeOpenAIResponsesIdPart(params) {
991
+ const trimmed = params.value.trim();
992
+ if (params.isValid(trimmed)) return trimmed;
993
+ const rawTail = trimmed.startsWith(params.prefix) ? trimmed.slice(params.prefix.length) : trimmed;
994
+ const hash = shortOpenAIResponsesIdHash(trimmed || params.prefix);
995
+ const maxTailLength = 64 - params.prefix.length;
996
+ const hashSuffix = `_${hash}`;
997
+ const tail = `${sanitizeOpenAIResponsesIdTail(rawTail).slice(0, Math.max(1, maxTailLength - hashSuffix.length)) || "id"}${hashSuffix}`.slice(0, maxTailLength);
998
+ return `${params.prefix}${tail}`;
999
+ }
1000
+ /** Same shaping `normalizeOpenAIResponsesToolCallIds` applies to a replayed `call_id`/`call_id|fc_id` pair. */
1001
+ function normalizeOpenAIResponsesFunctionCallId(id) {
1002
+ const { callId, itemId } = splitOpenAIFunctionCallPairing(id);
1003
+ const normalizedCallId = normalizeOpenAIResponsesIdPart({
1004
+ value: itemId ? `${callId}|${itemId}` : callId,
1005
+ prefix: "call_",
1006
+ isValid: (value) => OPENAI_RESPONSES_CALL_ID_RE.test(value)
1007
+ });
1008
+ if (!itemId) return normalizedCallId;
1009
+ return `${normalizedCallId}|${normalizeOpenAIResponsesIdPart({
1010
+ value: itemId,
1011
+ prefix: "fc_",
1012
+ isValid: (value) => OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE.test(value)
1013
+ })}`;
1014
+ }
1015
+ function shouldNormalizeOpenAIResponsesToolCallId(id) {
1016
+ const pairing = splitOpenAIFunctionCallPairing(id);
1017
+ if (!OPENAI_RESPONSES_CALL_ID_RE.test(pairing.callId)) return true;
1018
+ if (pairing.itemId === void 0) return false;
1019
+ return !OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE.test(pairing.itemId);
1020
+ }
1021
+ //#endregion
292
1022
  //#region packages/ai/src/transports/openai-responses-continuation.ts
293
1023
  const HTTP_CONTINUATION_IDLE_TTL_MS = 54e5;
294
1024
  const MAX_HTTP_CONTINUATION_READY_ENTRIES = 1e3;
@@ -681,6 +1411,14 @@ function createResponsesInputReplay(model) {
681
1411
  };
682
1412
  }
683
1413
  //#endregion
1414
+ //#region packages/ai/src/transports/openai-responses-replay.ts
1415
+ /** Resolves the assistant message id that can be replayed to OpenAI Responses. */
1416
+ function resolveReplayableResponsesMessageId(params) {
1417
+ if (!params.replayResponsesItemIds) return;
1418
+ if (!params.textSignatureId) return params.fallbackOrdinal === 0 ? params.fallbackId : `${params.fallbackId}_${params.fallbackOrdinal}`;
1419
+ return params.previousReplayItemWasReasoning ? params.textSignatureId : void 0;
1420
+ }
1421
+ //#endregion
684
1422
  //#region packages/ai/src/transports/openai-responses-replay-messages-internal.ts
685
1423
  function resolveResponsesInstructionRole(model) {
686
1424
  const supportsDeveloperRole = !isRecord(model.compat) || model.compat.supportsDeveloperRole !== false;
@@ -2671,4 +3409,4 @@ async function runResponsesStreamLifecycle(params) {
2671
3409
  }
2672
3410
  }
2673
3411
  //#endregion
2674
- export { OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE as $, recordResponsesContextUsage as A, recordResponsesContinuationState as B, resolveAzureOpenAIApiVersion as C, safeDebugValue as D, logResponsesFailedNoDetails as E, convertResponsesMessages as F, replayResponsesReasoningUpdates as G, responsesContinuationPrefixFingerprint as H, readResponsesInputReplayState as I, buildOpenAIResponsesReasoningReplayMetadata as J, supportsResponsesReasoningUpdate as K, recordResponsesInputReplay as L, bindResponsesInputMessage as M, buildResponsesInputMessage as N, summarizeOpenAITransportError as O, convertProviderResponsesMessages as P, OPENAI_RESPONSES_CALL_ID_RE as Q, responsesInputFingerprint as R, isInvalidEncryptedContentError as S, ResponsesStreamFailure as T, responsesContinuationRequestFingerprint as U, resolveResponsesContinuationRequest as V, isConfigurationUpdate as W, resolveNewestOpenAIResponsesCompactionReplay as X, captureOpenAIResponsesCompaction as Y, suppressOpenAIResponsesCompaction as Z, isResponsesTextContentPartType as _, prepareResponsesTools as a, commitResponsesEncryptedContentAttempt as b, observeResponsesStream as c, AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE as d, OPENAI_RESPONSES_ID_MAX_LENGTH as et, AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE as f, isAzureResponsesTextDeltaEventType as g, isAzureResponsesTextDeltaEvent as h, convertResponsesToolPayload as i, resolveReplayableResponsesMessageId as it, resolveResponsesContextUsageBoundary as j, summarizeResponsesPayload as k, createResponsesToolCallTracker as l, OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE as m, applyResponsesServiceTierPricing as n, shouldNormalizeOpenAIResponsesToolCallId as nt, readOpenAIMisalignmentReview as o, OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE as p, CompactionReplayRefreshRequiredError as q, runResponsesStreamLifecycle as r, splitOpenAIFunctionCallPairing as rt, processResponsesStream as s, applyCommonResponsesParams as t, normalizeOpenAIResponsesFunctionCallId as tt, readResponsesToolCallItemIdentity as u, isResponsesTextDeltaEventType as v, resolveNextResponsesEncryptedContentAttempt as w, createResponsesStreamWithEncryptedContentRetry as x, resolveResponsesMessageSnapshotCollapse as y, claimOpenAIResponsesHttpContinuation as z };
3412
+ export { supportsResponsesReasoningUpdate as $, recordResponsesContextUsage as A, parseOpenAICompletionsUsage as At, claimOpenAIResponsesHttpContinuation as B, resolveAzureOpenAIApiVersion as C, GEMINI_THOUGHT_SIGNATURE_VALIDATOR_SKIP as Ct, safeDebugValue as D, isOpenAICompletionsThinkingEnabled as Dt, logResponsesFailedNoDetails as E, createResponseModelTracker as Et, convertResponsesMessages as F, redactIdentifier as Ft, OPENAI_RESPONSES_CALL_ID_RE as G, resolveResponsesContinuationRequest as H, resolveReplayableResponsesMessageId as I, sha256Hex as It, normalizeOpenAIResponsesFunctionCallId as J, OPENAI_RESPONSES_FUNCTION_CALL_ITEM_ID_RE as K, readResponsesInputReplayState as L, bindResponsesInputMessage as M, readOpenAICompletionsReasoningBatch as Mt, buildResponsesInputMessage as N, resolveOpenAIClientBaseUrl as Nt, summarizeOpenAITransportError as O, log as Ot, convertProviderResponsesMessages as P, resolvePromptCacheKey as Pt, replayResponsesReasoningUpdates as Q, recordResponsesInputReplay as R, isInvalidEncryptedContentError as S, toOpenAIResponsesToolChoice as St, ResponsesStreamFailure as T, createOpenAIResponseHook as Tt, responsesContinuationPrefixFingerprint as U, recordResponsesContinuationState as V, responsesContinuationRequestFingerprint as W, splitOpenAIFunctionCallPairing as X, shouldNormalizeOpenAIResponsesToolCallId as Y, isConfigurationUpdate as Z, isResponsesTextContentPartType as _, resolveOpenAIReasoningEffortMap as _t, prepareResponsesTools as a, assertCodeModeResponsesToolSurface as at, commitResponsesEncryptedContentAttempt as b, reconcileOpenAICompletionsToolChoice as bt, observeResponsesStream as c, buildOpenAISdkRequestOptions as ct, AZURE_RESPONSES_TEXT_CONTENT_PART_TYPE as d, getCompat as dt, CompactionReplayRefreshRequiredError as et, AZURE_RESPONSES_TEXT_DELTA_EVENT_TYPE as f, readCodeModePayloadToolName as ft, isAzureResponsesTextDeltaEventType as g, resolveOpenAISimpleReasoningEffort as gt, isAzureResponsesTextDeltaEvent as h, resolveOpenAIRequestReasoning as ht, convertResponsesToolPayload as i, suppressOpenAIResponsesCompaction as it, resolveResponsesContextUsageBoundary as j, readOpenAICompletionsContentDeltas as jt, summarizeResponsesPayload as k, measureUtf8AppendBytes as kt, createResponsesToolCallTracker as l, enforceCodeModeResponsesToolSurface as lt, OPENAI_RESPONSES_OUTPUT_TEXT_DELTA_EVENT_TYPE as m, resolveOpenAIStrictToolFlagWithDiagnostics as mt, applyResponsesServiceTierPricing as n, captureOpenAIResponsesCompaction as nt, readOpenAIMisalignmentReview as o, buildOpenAIClientHeaders as ot, OPENAI_RESPONSES_OUTPUT_TEXT_CONTENT_PART_TYPE as p, resolveCodeModeResponsesVisibleToolNames as pt, OPENAI_RESPONSES_ID_MAX_LENGTH as q, runResponsesStreamLifecycle as r, resolveNewestOpenAIResponsesCompactionReplay as rt, processResponsesStream as s, buildOpenAISdkClientOptions as st, applyCommonResponsesParams as t, buildOpenAIResponsesReasoningReplayMetadata as tt, readResponsesToolCallItemIdentity as u, filterCodeModePayloadTools as ut, isResponsesTextDeltaEventType as v, prepareOpenAITools as vt, resolveNextResponsesEncryptedContentAttempt as w, createOpenAIProviderAcceptanceHook as wt, createResponsesStreamWithEncryptedContentRetry as x, reconcileOpenAIResponsesToolChoice as xt, resolveResponsesMessageSnapshotCollapse as y, projectOpenAITools as yt, responsesInputFingerprint as z };