@jeffreycao/copilot-api 1.10.9 → 1.10.10

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 (33) hide show
  1. package/README.md +16 -13
  2. package/README.zh-CN.md +16 -13
  3. package/dist/auth-CeZtnQE_.js +116 -0
  4. package/dist/auth-CeZtnQE_.js.map +1 -0
  5. package/dist/{check-usage-BdXGp1Wr.js → check-usage-CTShKCvR.js} +3 -4
  6. package/dist/{check-usage-BdXGp1Wr.js.map → check-usage-CTShKCvR.js.map} +1 -1
  7. package/dist/{proxy-DvlF9a-7.js → config-ztdkLu9o.js} +83 -70
  8. package/dist/config-ztdkLu9o.js.map +1 -0
  9. package/dist/{debug-C_TBkyUw.js → debug-BVHmoCzY.js} +17 -7
  10. package/dist/debug-BVHmoCzY.js.map +1 -0
  11. package/dist/main.js +5 -5
  12. package/dist/{mcp-CTb-DbQH.js → mcp-DZgcvqQY.js} +2 -2
  13. package/dist/{mcp-CTb-DbQH.js.map → mcp-DZgcvqQY.js.map} +1 -1
  14. package/dist/{server-FPXzFkg9.js → server-D5O9IzAY.js} +1773 -1692
  15. package/dist/server-D5O9IzAY.js.map +1 -0
  16. package/dist/{start-CbKg_0bY.js → start-BhPxHgu8.js} +4 -6
  17. package/dist/{start-CbKg_0bY.js.map → start-BhPxHgu8.js.map} +1 -1
  18. package/dist/token-1SfgxCRm.js +1875 -0
  19. package/dist/token-1SfgxCRm.js.map +1 -0
  20. package/dist/{tool-search-D3SN0jX-.js → tool-search-wA-fLduL.js} +1 -1
  21. package/dist/{tool-search-D3SN0jX-.js.map → tool-search-wA-fLduL.js.map} +1 -1
  22. package/package.json +2 -2
  23. package/dist/auth-BHa2OHXf.js +0 -45
  24. package/dist/auth-BHa2OHXf.js.map +0 -1
  25. package/dist/debug-C_TBkyUw.js.map +0 -1
  26. package/dist/paths-DC-mqCY3.js +0 -30
  27. package/dist/paths-DC-mqCY3.js.map +0 -1
  28. package/dist/proxy-DvlF9a-7.js.map +0 -1
  29. package/dist/server-FPXzFkg9.js.map +0 -1
  30. package/dist/token-Dj8XsAxn.js +0 -170
  31. package/dist/token-Dj8XsAxn.js.map +0 -1
  32. package/dist/utils-jHLgqAq2.js +0 -657
  33. package/dist/utils-jHLgqAq2.js.map +0 -1
@@ -1,20 +1,18 @@
1
- import { t as PATHS } from "./paths-DC-mqCY3.js";
2
- import { D as generateTraceId, E as prepareMessageProxyHeaders, I as state, M as compactMessageSections, O as requestContext, P as compactSystemPromptStarts, T as prepareInteractionHeaders, _ as copilotWebSocketHeaders, c as getUUID, d as sleep, f as getCopilotUsage, g as copilotHeaders, h as copilotBaseUrl, j as compactAutoContinuePromptStarts, k as resolveTraceId$1, l as isNullish, m as forwardError, n as cacheModels, o as generateRequestIdFromPayload, p as HTTPError, s as getRootSessionId, u as parseUserIdMetadata, w as prepareForCompact } from "./utils-jHLgqAq2.js";
3
- import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, d as shouldEnableResponsesToolSearch, i as isBridgeToolSearchName, l as resolveBridgeToolSearchName, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, t as BRIDGE_TOOL_SEARCH_NAME, u as selectDeferredToolsByNames } from "./tool-search-D3SN0jX-.js";
4
- import { _ as setModelMappings, a as getConfig, c as getProviderConfig, d as isMessagesApiEnabled, f as isResponsesApiContextManagementModel, g as resolveMappedModel, i as getClaudeTokenMultiplier, l as getReasoningEffortForModel, m as isResponsesApiWebSocketEnabled, o as getExtraPromptForModel, p as isResponsesApiWebSearchEnabled, r as getAnthropicApiKey, s as getModelMappings, t as getProxyEnvDispatcher, u as getSmallModel } from "./proxy-DvlF9a-7.js";
1
+ import { A as compactSystemPromptStarts, B as createWebSocketUrl, C as prepareForCompact, D as compactAutoContinuePromptStarts, F as normalizeCodexResponsesEvent, H as state, I as generateTraceId, L as requestContext, N as createStandardizedCodexResponsesEventStream, O as compactMessageSections, P as forwardCodexResponses, R as resolveTraceId$1, S as copilotWebSocketHeaders, T as prepareMessageProxyHeaders, U as logCodexRateLimitsEvent, _ as getCopilotUsage, b as copilotBaseUrl, d as generateRequestIdFromPayload, f as getRootSessionId, g as sleep, h as parseUserIdMetadata, m as isNullish, p as getUUID, r as setupCodexToken, s as cacheModels, v as HTTPError, w as prepareInteractionHeaders, x as copilotHeaders, y as forwardError, z as createPooledWebSocketStream } from "./token-1SfgxCRm.js";
2
+ import { _ as setModelMappings, a as getModelMappings, c as getReasoningEffortForModel, d as isResponsesApiContextManagementModel, f as isResponsesApiWebSearchEnabled, g as resolveMappedModel, i as getExtraPromptForModel, l as getSmallModel, n as getClaudeTokenMultiplier, o as getProviderConfig, p as isResponsesApiWebSocketEnabled, r as getConfig, s as getRawProviderConfig, t as getAnthropicApiKey, u as isMessagesApiEnabled, y as PATHS } from "./config-ztdkLu9o.js";
3
+ import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, d as shouldEnableResponsesToolSearch, i as isBridgeToolSearchName, l as resolveBridgeToolSearchName, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, t as BRIDGE_TOOL_SEARCH_NAME, u as selectDeferredToolsByNames } from "./tool-search-wA-fLduL.js";
5
4
  import consola from "consola";
6
- import fs from "node:fs/promises";
7
- import path from "node:path";
8
5
  import { createHash } from "node:crypto";
6
+ import fs, { readFileSync } from "node:fs";
7
+ import fs$1 from "node:fs/promises";
8
+ import path from "node:path";
9
9
  import { z } from "zod";
10
10
  import { Hono } from "hono";
11
11
  import { cors } from "hono/cors";
12
12
  import { logger } from "hono/logger";
13
- import fs$1, { readFileSync } from "node:fs";
14
13
  import { streamSSE } from "hono/streaming";
15
14
  import util from "node:util";
16
15
  import { events } from "fetch-event-stream";
17
- import { WebSocket } from "undici";
18
16
  //#region src/lib/request-auth.ts
19
17
  function normalizeApiKeys(apiKeys) {
20
18
  if (!Array.isArray(apiKeys)) {
@@ -78,7 +76,7 @@ const traceIdMiddleware = async (c, next) => {
78
76
  traceId,
79
77
  startTime: Date.now(),
80
78
  userAgent: c.req.header("user-agent") || "",
81
- sessionAffinity: c.req.header("x-session-affinity"),
79
+ sessionAffinity: c.req.header("x-session-affinity") ?? c.req.header("x-client-request-id"),
82
80
  parentSessionId: c.req.header("x-parent-session-id")
83
81
  };
84
82
  await requestContext.run(context, async () => {
@@ -154,22 +152,22 @@ let runtimeInitialized = false;
154
152
  let flushInterval;
155
153
  let cleanupInterval;
156
154
  const ensureLogDirectory = () => {
157
- if (!fs$1.existsSync(LOG_DIR)) fs$1.mkdirSync(LOG_DIR, { recursive: true });
155
+ if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
158
156
  };
159
157
  const cleanupOldLogs = () => {
160
- if (!fs$1.existsSync(LOG_DIR)) return;
158
+ if (!fs.existsSync(LOG_DIR)) return;
161
159
  const now = Date.now();
162
- for (const entry of fs$1.readdirSync(LOG_DIR)) {
160
+ for (const entry of fs.readdirSync(LOG_DIR)) {
163
161
  const filePath = path.join(LOG_DIR, entry);
164
162
  let stats;
165
163
  try {
166
- stats = fs$1.statSync(filePath);
164
+ stats = fs.statSync(filePath);
167
165
  } catch {
168
166
  continue;
169
167
  }
170
168
  if (!stats.isFile()) continue;
171
169
  if (now - stats.mtimeMs > LOG_RETENTION_MS) try {
172
- fs$1.rmSync(filePath);
170
+ fs.rmSync(filePath);
173
171
  } catch {
174
172
  continue;
175
173
  }
@@ -228,7 +226,7 @@ const getLogStream = (filePath) => {
228
226
  initializeLoggerRuntime();
229
227
  let stream = logStreams.get(filePath);
230
228
  if (!stream || stream.destroyed) {
231
- stream = fs$1.createWriteStream(filePath, { flags: "a" });
229
+ stream = fs.createWriteStream(filePath, { flags: "a" });
232
230
  logStreams.set(filePath, stream);
233
231
  stream.on("error", (error) => {
234
232
  console.warn("Log stream error", error);
@@ -373,7 +371,7 @@ async function openNodeDatabase(dbPath) {
373
371
  }
374
372
  async function openSqliteDatabase(dbPath) {
375
373
  const dir = path.dirname(dbPath);
376
- if (dbPath !== ":memory:" && dir !== ".") await fs.mkdir(dir, { recursive: true });
374
+ if (dbPath !== ":memory:" && dir !== ".") await fs$1.mkdir(dir, { recursive: true });
377
375
  return isBunRuntime() ? openBunDatabase(dbPath) : openNodeDatabase(dbPath);
378
376
  }
379
377
  var SqliteDbStore = class {
@@ -1402,6 +1400,31 @@ const getTokenCount = async (payload, model) => {
1402
1400
  };
1403
1401
  };
1404
1402
  //#endregion
1403
+ //#region src/lib/provider-resolver.ts
1404
+ function isMissingCodexCredentialsError(error) {
1405
+ return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
1406
+ }
1407
+ async function resolveProviderConfig(providerName) {
1408
+ const normalizedProviderName = providerName.trim();
1409
+ if (!normalizedProviderName) return null;
1410
+ if (normalizedProviderName === "codex") {
1411
+ if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
1412
+ try {
1413
+ await setupCodexToken();
1414
+ } catch (error) {
1415
+ if (isMissingCodexCredentialsError(error)) return null;
1416
+ throw error;
1417
+ }
1418
+ const providerConfig = getProviderConfig(normalizedProviderName);
1419
+ if (!providerConfig) return null;
1420
+ return {
1421
+ ...providerConfig,
1422
+ apiKey: state.codexAccessToken ?? providerConfig.apiKey
1423
+ };
1424
+ }
1425
+ return getProviderConfig(normalizedProviderName);
1426
+ }
1427
+ //#endregion
1405
1428
  //#region src/routes/messages/utils.ts
1406
1429
  function mapOpenAIStopReasonToAnthropic(finishReason) {
1407
1430
  if (finishReason === null) return null;
@@ -1738,13 +1761,13 @@ async function handleProviderCountTokens(c) {
1738
1761
  async function handleProviderCountTokensForProvider(c, options) {
1739
1762
  const { payload: anthropicPayload, provider } = options;
1740
1763
  const modelId = anthropicPayload.model.trim();
1741
- const providerConfig = getProviderConfig(provider);
1764
+ const providerConfig = await resolveProviderConfig(provider);
1742
1765
  if (!providerConfig) return c.json({ error: {
1743
1766
  message: `Provider '${provider}' not found or disabled`,
1744
1767
  type: "invalid_request_error"
1745
1768
  } }, 404);
1746
1769
  const modelConfig = providerConfig.models?.[modelId];
1747
- const tokenCount = await getTokenCount(translateToOpenAI(anthropicPayload, providerConfig.type === "openai-compatible" ? {
1770
+ const tokenCount = await getTokenCount(translateToOpenAI(anthropicPayload, providerConfig.type === "openai-compatible" || providerConfig.type === "openai-responses" ? {
1748
1771
  supportPdf: modelConfig?.supportPdf,
1749
1772
  toolContentSupportType: modelConfig?.toolContentSupportType ?? []
1750
1773
  } : void 0), createFallbackModel(modelId));
@@ -2205,1847 +2228,1820 @@ function closeThinkingBlockIfOpen(state, events) {
2205
2228
  }
2206
2229
  }
2207
2230
  //#endregion
2208
- //#region src/services/providers/anthropic-proxy.ts
2209
- const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
2210
- const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
2211
- const STRIPPED_RESPONSE_HEADERS = [
2212
- "connection",
2213
- "content-encoding",
2214
- "content-length",
2215
- "keep-alive",
2216
- "proxy-authenticate",
2217
- "proxy-authorization",
2218
- "te",
2219
- "trailer",
2220
- "transfer-encoding",
2221
- "upgrade"
2222
- ];
2223
- function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
2224
- const authHeaders = {};
2225
- if (providerConfig.authType === "authorization") authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
2226
- else authHeaders["x-api-key"] = providerConfig.apiKey;
2231
+ //#region src/services/copilot/create-responses.ts
2232
+ const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
2233
+ if (!state.copilotToken) throw new Error("Copilot token not found");
2227
2234
  const headers = {
2228
- "content-type": "application/json",
2229
- accept: "application/json",
2230
- ...authHeaders
2235
+ ...copilotHeaders(state, requestId, vision),
2236
+ "x-initiator": initiator
2231
2237
  };
2232
- for (const headerName of SHARED_FORWARDABLE_HEADERS) {
2233
- const headerValue = requestHeaders.get(headerName);
2234
- if (headerValue) headers[headerName] = headerValue;
2235
- }
2236
- if (providerConfig.type !== "anthropic") return headers;
2237
- for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
2238
- const headerValue = requestHeaders.get(headerName);
2239
- if (headerValue) headers[headerName] = headerValue;
2238
+ prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
2239
+ prepareForCompact(headers, compactType);
2240
+ payload.service_tier = void 0;
2241
+ consola.log(`<-- model: ${payload.model}`);
2242
+ if ((compactType === 1 ? "http" : transport) === "websocket") {
2243
+ const stream = createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
2244
+ requestId,
2245
+ subagentMarker
2246
+ }));
2247
+ if (payload.stream) return stream;
2248
+ return await consumeResponsesWebSocketStream(stream);
2240
2249
  }
2241
- return headers;
2242
- }
2243
- function createProviderProxyResponse(upstreamResponse) {
2244
- const headers = new Headers(upstreamResponse.headers);
2245
- for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
2246
- return new Response(upstreamResponse.body, {
2247
- headers,
2248
- status: upstreamResponse.status,
2249
- statusText: upstreamResponse.statusText
2250
- });
2251
- }
2252
- async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
2253
- return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
2254
- method: "POST",
2255
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
2256
- body: JSON.stringify(payload)
2257
- });
2258
- }
2259
- async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
2260
- return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
2250
+ return await createHttpResponses(payload, headers);
2251
+ };
2252
+ const createHttpResponses = async (payload, headers) => {
2253
+ const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
2261
2254
  method: "POST",
2262
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
2255
+ headers,
2263
2256
  body: JSON.stringify(payload)
2264
2257
  });
2265
- }
2266
- async function forwardProviderModels(providerConfig, requestHeaders) {
2267
- return await fetch(`${providerConfig.baseUrl}/v1/models`, {
2268
- method: "GET",
2269
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
2270
- });
2271
- }
2272
- //#endregion
2273
- //#region src/routes/provider/messages/handler.ts
2274
- const logger$4 = createHandlerLogger("provider-messages-handler");
2275
- const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
2276
- const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
2277
- const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
2278
- "system",
2279
- "user",
2280
- "assistant",
2281
- "tool"
2282
- ]);
2283
- async function handleProviderMessages(c) {
2284
- const provider = c.req.param("provider");
2285
- return await handleProviderMessagesForProvider(c, {
2286
- payload: await c.req.json(),
2287
- provider
2288
- });
2289
- }
2290
- async function handleProviderMessagesForProvider(c, options) {
2291
- const { payload, provider } = options;
2292
- const providerConfig = getProviderConfig(provider);
2293
- if (!providerConfig) return c.json({ error: {
2294
- message: `Provider '${provider}' not found or disabled`,
2295
- type: "invalid_request_error"
2296
- } }, 404);
2297
- try {
2298
- const modelConfig = providerConfig.models?.[payload.model];
2299
- applyModelDefaults(payload, modelConfig);
2300
- debugJson(logger$4, "provider.messages.request", {
2301
- payload,
2302
- provider
2303
- });
2304
- if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
2305
- modelConfig,
2306
- payload,
2307
- provider,
2308
- providerConfig
2309
- });
2310
- applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
2311
- const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
2312
- if (!upstreamResponse.ok) {
2313
- logger$4.error("Failed to create responses", upstreamResponse);
2314
- throw new HTTPError("Failed to create responses", upstreamResponse);
2315
- }
2316
- const contentType = upstreamResponse.headers.get("content-type") ?? "";
2317
- if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderMessages({
2318
- c,
2319
- payload,
2320
- provider,
2321
- providerConfig,
2322
- upstreamResponse
2323
- });
2324
- return respondProviderMessagesJson(c, {
2325
- body: await upstreamResponse.json(),
2326
- payload,
2327
- provider,
2328
- providerConfig
2329
- });
2330
- } catch (error) {
2331
- logger$4.error("provider.messages.error", {
2332
- provider,
2333
- error
2334
- });
2335
- throw error;
2258
+ logCopilotRateLimits(response.headers);
2259
+ if (!response.ok) {
2260
+ consola.error("Failed to create responses", response);
2261
+ throw new HTTPError("Failed to create responses", response);
2336
2262
  }
2337
- }
2338
- const applyModelDefaults = (payload, modelConfig) => {
2339
- payload.temperature ??= modelConfig?.temperature;
2340
- payload.top_p ??= modelConfig?.topP;
2341
- payload.top_k ??= modelConfig?.topK;
2263
+ if (payload.stream) return events(response);
2264
+ return await response.json();
2342
2265
  };
2343
- const applyMissingExtraBody = (payload, options) => {
2344
- for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
2266
+ const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
2267
+ const initiator = getResponsesWebSocketInitiator(preparedHeaders);
2268
+ return {
2269
+ headers: copilotWebSocketHeaders(preparedHeaders),
2270
+ poolKey: buildResponsesWebSocketPoolKey(payload, options),
2271
+ payload: buildResponsesWebSocketPayload(payload, initiator),
2272
+ url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
2273
+ };
2345
2274
  };
2346
- const getRequestThinkingBudget = (payload) => {
2347
- const budget = payload.thinking?.budget_tokens;
2348
- if (typeof budget !== "number" || !Number.isFinite(budget)) return;
2349
- return budget;
2275
+ const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
2276
+ const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
2277
+ const subagentKey = subagentMarker ? [
2278
+ subagentMarker.session_id,
2279
+ subagentMarker.agent_id,
2280
+ subagentMarker.agent_type
2281
+ ].join(":") : "main";
2282
+ return [
2283
+ tokenFingerprint,
2284
+ payload.model,
2285
+ requestId,
2286
+ subagentKey
2287
+ ].map(encodePoolKeyPart).join("|");
2350
2288
  };
2351
- const applyOpenAICompatibleThinkingBudget = (payload, source) => {
2352
- const thinkingBudget = getRequestThinkingBudget(source);
2353
- if (thinkingBudget !== void 0) {
2354
- payload.thinking_budget = thinkingBudget;
2355
- return;
2356
- }
2357
- if (payload.thinking_budget === void 0) delete payload.thinking_budget;
2289
+ const getResponsesWebSocketInitiator = (preparedHeaders) => {
2290
+ return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
2358
2291
  };
2359
- const applyOpenAICompatibleExtraBodyThinkingBudget = (payload, options) => {
2360
- const { extraBody } = options;
2361
- if (!extraBody || !Object.hasOwn(extraBody, "thinking_budget")) return;
2362
- const rawPayload = payload;
2363
- rawPayload.thinking_budget = extraBody.thinking_budget;
2292
+ const createPooledResponsesWebSocketStream = (request) => createPooledWebSocketStream(request, {
2293
+ createChunk: createResponsesWebSocketStreamChunk,
2294
+ isTerminalChunk: isTerminalResponsesStreamChunk,
2295
+ openErrorMessage: "Failed to create responses websocket",
2296
+ streamErrorMessage: "Responses websocket stream error",
2297
+ terminalChunkMissingMessage: "Responses websocket ended without a terminal response"
2298
+ });
2299
+ const buildResponsesWebSocketPayload = (payload, initiator) => {
2300
+ const websocketPayload = {
2301
+ ...payload,
2302
+ type: "response.create",
2303
+ initiator
2304
+ };
2305
+ delete websocketPayload.stream;
2306
+ delete websocketPayload["background"];
2307
+ delete websocketPayload.service_tier;
2308
+ return websocketPayload;
2364
2309
  };
2365
- const handleOpenAICompatibleProviderMessages = async (c, options) => {
2366
- const { modelConfig, payload, provider, providerConfig } = options;
2367
- const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
2368
- debugJson(logger$4, "provider.messages.openai_compatible.request", {
2369
- payload: openAIPayload,
2370
- provider
2371
- });
2372
- const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
2373
- if (!upstreamResponse.ok) {
2374
- logger$4.error("Failed to create openai-compatible responses", upstreamResponse);
2375
- throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
2376
- }
2377
- const contentType = upstreamResponse.headers.get("content-type") ?? "";
2378
- if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
2379
- c,
2380
- payload,
2381
- provider,
2382
- upstreamResponse
2383
- });
2384
- return respondOpenAICompatibleProviderMessagesJson(c, {
2385
- body: await upstreamResponse.json(),
2386
- payload,
2387
- provider
2388
- });
2310
+ const buildResponsesWebSocketUrl = (baseUrl) => {
2311
+ return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
2389
2312
  };
2390
- const createOpenAICompatiblePayload = (payload, modelConfig) => {
2391
- const openAIPayload = translateToOpenAI(payload, {
2392
- supportPdf: modelConfig?.supportPdf,
2393
- toolContentSupportType: modelConfig?.toolContentSupportType ?? []
2394
- });
2395
- applyOpenAICompatibleThinkingBudget(openAIPayload, payload);
2396
- if (payload.top_k !== void 0) openAIPayload.top_k = payload.top_k;
2397
- if (openAIPayload.stream) openAIPayload.stream_options = { include_usage: true };
2398
- normalizeOpenAICompatibleReasoningContent(openAIPayload);
2399
- applyOpenAICompatibleRequestOverrides(openAIPayload, {
2400
- extraBody: modelConfig?.extraBody,
2401
- source: payload
2402
- });
2403
- applyMissingExtraBody(openAIPayload, { extraBody: modelConfig?.extraBody });
2404
- applyOpenAICompatibleExtraBodyThinkingBudget(openAIPayload, { extraBody: modelConfig?.extraBody });
2405
- if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
2406
- if (modelConfig?.contextCache !== false) applyOpenAICompatibleContextCache(openAIPayload);
2407
- return openAIPayload;
2313
+ const getHeaderValue = (headers, headerName) => {
2314
+ const normalizedHeaderName = headerName.toLowerCase();
2315
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
2408
2316
  };
2409
- const normalizeOpenAICompatibleReasoningContent = (payload) => {
2410
- for (const message of payload.messages) {
2411
- if (message.role !== "assistant") continue;
2412
- if (message.reasoning_content === void 0 && message.reasoning_text !== void 0) message.reasoning_content = message.reasoning_text;
2413
- delete message.reasoning_text;
2414
- delete message.reasoning_opaque;
2317
+ const encodePoolKeyPart = (value) => encodeURIComponent(value);
2318
+ const createResponsesWebSocketStreamChunk = (data) => {
2319
+ if (data === "[DONE]") return { data };
2320
+ try {
2321
+ const parsed = JSON.parse(data);
2322
+ if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
2323
+ return {
2324
+ data: JSON.stringify(parsed),
2325
+ event: typeof parsed.type === "string" ? parsed.type : void 0,
2326
+ id: typeof parsed.id === "string" ? parsed.id : void 0
2327
+ };
2328
+ } catch {
2329
+ return { data };
2415
2330
  }
2416
2331
  };
2417
- const applyOpenAICompatibleRequestOverrides = (payload, options) => {
2418
- const allowedKeys = new Set(Object.keys(options.extraBody ?? {}));
2419
- for (const key of allowedKeys) if (Object.hasOwn(options.source, key)) payload[key] = options.source[key];
2332
+ const isTerminalResponsesStreamChunk = (chunk) => {
2333
+ if (!chunk.data || chunk.data === "[DONE]") return false;
2334
+ try {
2335
+ const parsed = JSON.parse(chunk.data);
2336
+ return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
2337
+ } catch {
2338
+ return false;
2339
+ }
2420
2340
  };
2421
- const applyOpenAICompatibleContextCache = (payload) => {
2422
- const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
2423
- for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
2341
+ const consumeResponsesWebSocketStream = async (stream) => {
2342
+ for await (const chunk of stream) {
2343
+ if (!chunk.data || chunk.data === "[DONE]") continue;
2344
+ const event = JSON.parse(chunk.data);
2345
+ if (event.type === "error") throw new Error(event.message);
2346
+ if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return event.response;
2347
+ }
2348
+ throw new Error("Responses websocket ended without a terminal response");
2424
2349
  };
2425
- const selectContextCacheMessageIndexes = (messages) => {
2426
- const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
2427
- const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
2428
- const finalIndexes = cacheableIndexes.filter((index) => messages[index]?.role !== "system").slice(-2);
2429
- return uniqueIndexes$1([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
2350
+ //#endregion
2351
+ //#region src/routes/messages/responses-translation.ts
2352
+ const MESSAGE_TYPE = "message";
2353
+ const COMPACTION_SIGNATURE_PREFIX = "cm1#";
2354
+ const COMPACTION_SIGNATURE_SEPARATOR = "@";
2355
+ const THINKING_TEXT = "Thinking...";
2356
+ const buildPromptCacheKey = (basePromptCacheKey, subagentAgentId) => {
2357
+ if (!basePromptCacheKey) return null;
2358
+ const normalizedSubagentAgentId = subagentAgentId?.trim() || null;
2359
+ if (!normalizedSubagentAgentId) return basePromptCacheKey;
2360
+ return `${basePromptCacheKey}:agent:${normalizedSubagentAgentId}`;
2430
2361
  };
2431
- const uniqueIndexes$1 = (indexes) => [...new Set(indexes)].slice(0, OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT);
2432
- const isContextCacheMarkerEligible = (message) => {
2433
- if (!OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES.has(message.role)) return false;
2434
- if (typeof message.content === "string") return message.content.length > 0;
2435
- return Array.isArray(message.content) && message.content.length > 0;
2362
+ const translateAnthropicMessagesToResponsesPayload = (payload, subagentAgentId) => {
2363
+ const input = [];
2364
+ const applyPhase = shouldApplyPhase(payload.model);
2365
+ const toolSearchEnabled = shouldEnableResponsesToolSearch({
2366
+ model: payload.model,
2367
+ tools: payload.tools
2368
+ });
2369
+ const translationState = {
2370
+ originalTools: payload.tools ?? [],
2371
+ toolSearchEnabled,
2372
+ toolUseNameById: /* @__PURE__ */ new Map()
2373
+ };
2374
+ for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase, translationState));
2375
+ const hasOriginalTools = Array.isArray(payload.tools) && payload.tools.length > 0;
2376
+ const translatedTools = convertAnthropicTools(payload.tools, toolSearchEnabled);
2377
+ const toolChoice = convertAnthropicToolChoice(payload.tool_choice, toolSearchEnabled);
2378
+ const { sessionId: metadataPromptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
2379
+ const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
2380
+ const promptCacheKey = buildPromptCacheKey(metadataPromptCacheKey ?? sessionAffinity, subagentAgentId);
2381
+ const responsesPayload = {
2382
+ model: payload.model,
2383
+ input,
2384
+ instructions: translateSystemPrompt(payload.system, payload.model),
2385
+ temperature: 1,
2386
+ top_p: payload.top_p ?? null,
2387
+ max_output_tokens: Math.max(payload.max_tokens, 12800),
2388
+ tools: translatedTools,
2389
+ tool_choice: toolChoice,
2390
+ metadata: payload.metadata ? { ...payload.metadata } : null,
2391
+ stream: payload.stream ?? null,
2392
+ store: false,
2393
+ parallel_tool_calls: true,
2394
+ reasoning: {
2395
+ effort: getReasoningEffortForModel(payload.model),
2396
+ summary: "detailed"
2397
+ },
2398
+ include: ["reasoning.encrypted_content"]
2399
+ };
2400
+ if (hasOriginalTools) responsesPayload.prompt_cache_key = promptCacheKey;
2401
+ return responsesPayload;
2436
2402
  };
2437
- const applyContextCacheControl = (message) => {
2438
- if (!message) return;
2439
- if (typeof message.content === "string") {
2440
- message.content = [{
2441
- type: "text",
2442
- text: message.content,
2443
- cache_control: { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL }
2444
- }];
2445
- return;
2403
+ const encodeCompactionCarrierSignature = (compaction) => {
2404
+ return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
2405
+ };
2406
+ const decodeCompactionCarrierSignature = (signature) => {
2407
+ if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
2408
+ const raw = signature.slice(4);
2409
+ const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
2410
+ if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
2411
+ const encrypted_content = raw.slice(0, separatorIndex);
2412
+ const id = raw.slice(separatorIndex + 1);
2413
+ if (!encrypted_content) return;
2414
+ return {
2415
+ id,
2416
+ encrypted_content
2417
+ };
2446
2418
  }
2447
- if (!Array.isArray(message.content)) return;
2448
- const lastPart = message.content.at(-1);
2449
- if (!lastPart) return;
2450
- setContextCacheControl(lastPart);
2451
2419
  };
2452
- const setContextCacheControl = (part) => {
2453
- part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
2420
+ const translateMessage = (message, model, applyPhase, state) => {
2421
+ if (message.role === "user") return translateUserMessage(message, state);
2422
+ return translateAssistantMessage(message, model, applyPhase, state);
2454
2423
  };
2455
- const streamProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
2456
- logger$4.debug("provider.messages.streaming");
2457
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
2458
- return streamSSE(c, async (stream) => {
2459
- let usage = {};
2460
- for await (const chunk of events(upstreamResponse)) {
2461
- logger$4.debug("provider.messages.raw_stream_event:", chunk.data);
2462
- const eventName = chunk.event;
2463
- if (eventName === "ping") {
2464
- await stream.writeSSE({
2465
- event: "ping",
2466
- data: "{\"type\":\"ping\"}"
2467
- });
2468
- continue;
2469
- }
2470
- let data = chunk.data;
2471
- if (!data) continue;
2472
- if (chunk.data === "[DONE]") break;
2473
- const parsed = parseProviderStreamEvent(data, providerConfig);
2474
- if (parsed) {
2475
- usage = mergeAnthropicUsage(usage, parsed.usage);
2476
- data = parsed.data;
2477
- }
2478
- await stream.writeSSE({
2479
- event: eventName,
2480
- data
2481
- });
2424
+ const translateUserMessage = (message, state) => {
2425
+ if (typeof message.content === "string") return [createMessage("user", message.content)];
2426
+ if (!Array.isArray(message.content)) return [];
2427
+ const items = [];
2428
+ const pendingContent = [];
2429
+ for (const block of message.content) {
2430
+ if (block.type === "tool_result") {
2431
+ flushPendingContent(pendingContent, items, { role: "user" });
2432
+ items.push(createToolCallOutput(block, state));
2433
+ continue;
2482
2434
  }
2483
- recordUsage(usage);
2484
- });
2435
+ const converted = translateUserContentBlock(block);
2436
+ if (converted.length > 0) pendingContent.push(...converted);
2437
+ }
2438
+ flushPendingContent(pendingContent, items, { role: "user" });
2439
+ return items;
2485
2440
  };
2486
- const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstreamResponse }) => {
2487
- logger$4.debug("provider.messages.openai_compatible.streaming");
2488
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
2489
- return streamSSE(c, async (stream) => {
2490
- let usage = {};
2491
- const streamState = {
2492
- messageStartSent: false,
2493
- contentBlockIndex: 0,
2494
- contentBlockOpen: false,
2495
- toolCalls: {},
2496
- thinkingBlockOpen: false
2497
- };
2498
- for await (const chunk of events(upstreamResponse)) {
2499
- logger$4.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
2500
- if (chunk.event === "ping") {
2501
- await stream.writeSSE({
2502
- event: "ping",
2503
- data: "{\"type\":\"ping\"}"
2441
+ const translateAssistantMessage = (message, model, applyPhase, state) => {
2442
+ const assistantPhase = resolveAssistantPhase(model, message.content, applyPhase);
2443
+ if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
2444
+ if (!Array.isArray(message.content)) return [];
2445
+ const items = [];
2446
+ const pendingContent = [];
2447
+ for (const block of message.content) {
2448
+ if (block.type === "tool_use") {
2449
+ state.toolUseNameById.set(block.id, block.name);
2450
+ flushPendingContent(pendingContent, items, {
2451
+ role: "assistant",
2452
+ phase: assistantPhase
2453
+ });
2454
+ items.push(createToolCall(block, state));
2455
+ continue;
2456
+ }
2457
+ if (block.type === "thinking" && block.signature) {
2458
+ const compactionContent = createCompactionContent(block);
2459
+ if (compactionContent) {
2460
+ flushPendingContent(pendingContent, items, {
2461
+ role: "assistant",
2462
+ phase: assistantPhase
2504
2463
  });
2464
+ items.push(compactionContent);
2505
2465
  continue;
2506
2466
  }
2507
- if (!chunk.data || chunk.data === "[DONE]") {
2508
- if (chunk.data === "[DONE]") break;
2509
- continue;
2510
- }
2511
- const parsed = parseOpenAICompatibleStreamChunk(chunk.data);
2512
- if (!parsed) continue;
2513
- if (parsed.usage) usage = normalizeOpenAIUsage(parsed.usage);
2514
- const events = translateChunkToAnthropicEvents(parsed, streamState);
2515
- for (const event of events) {
2516
- const eventData = JSON.stringify(event);
2517
- debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
2518
- await stream.writeSSE({
2519
- event: event.type,
2520
- data: eventData
2467
+ if (block.signature.includes("@")) {
2468
+ flushPendingContent(pendingContent, items, {
2469
+ role: "assistant",
2470
+ phase: assistantPhase
2521
2471
  });
2472
+ items.push(createReasoningContent(block));
2473
+ continue;
2522
2474
  }
2523
2475
  }
2524
- for (const event of flushPendingAnthropicStreamEvents(streamState)) {
2525
- const eventData = JSON.stringify(event);
2526
- debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
2527
- await stream.writeSSE({
2528
- event: event.type,
2529
- data: eventData
2530
- });
2531
- }
2532
- recordUsage(usage);
2476
+ const converted = translateAssistantContentBlock(block);
2477
+ if (converted) pendingContent.push(converted);
2478
+ }
2479
+ flushPendingContent(pendingContent, items, {
2480
+ role: "assistant",
2481
+ phase: assistantPhase
2533
2482
  });
2483
+ return items;
2534
2484
  };
2535
- const parseOpenAICompatibleStreamChunk = (data) => {
2536
- try {
2537
- return JSON.parse(data);
2538
- } catch (error) {
2539
- logger$4.error("provider.messages.openai_compatible.parse_chunk_error", {
2540
- data,
2541
- error
2542
- });
2543
- return null;
2485
+ const translateUserContentBlock = (block) => {
2486
+ switch (block.type) {
2487
+ case "text": return [createTextContent(block.text)];
2488
+ case "image": return [createImageContent(block)];
2489
+ case "document": return [createFileContent(block)];
2490
+ default: return [];
2544
2491
  }
2545
2492
  };
2546
- const parseProviderStreamEvent = (data, providerConfig) => {
2547
- try {
2548
- const parsed = JSON.parse(data);
2549
- if (parsed.type === "message_start") {
2550
- adjustInputTokens(providerConfig, parsed.message.usage);
2551
- return {
2552
- data: JSON.stringify(parsed),
2553
- model: parsed.message.model,
2554
- usage: normalizeAnthropicUsage(parsed.message.usage)
2555
- };
2556
- }
2557
- if (parsed.type === "message_delta") {
2558
- adjustInputTokens(providerConfig, parsed.usage);
2559
- return {
2560
- data: JSON.stringify(parsed),
2561
- usage: normalizeAnthropicUsage(parsed.usage)
2562
- };
2563
- }
2564
- return {
2565
- data: JSON.stringify(parsed),
2566
- usage: {}
2567
- };
2568
- } catch (error) {
2569
- logger$4.error("provider.messages.streaming.adjust_tokens_error", {
2570
- error,
2571
- originalData: data
2572
- });
2573
- return null;
2493
+ const translateAssistantContentBlock = (block) => {
2494
+ switch (block.type) {
2495
+ case "text": return createOutPutTextContent(block.text);
2496
+ default: return;
2574
2497
  }
2575
2498
  };
2576
- const respondProviderMessagesJson = (c, options) => {
2577
- const { body, payload, provider, providerConfig } = options;
2578
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
2579
- adjustInputTokens(providerConfig, body.usage);
2580
- recordUsage(normalizeAnthropicUsage(body.usage));
2581
- debugJson(logger$4, "provider.messages.no_stream result:", body);
2582
- return c.json(body);
2583
- };
2584
- const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
2585
- const { body, payload, provider } = options;
2586
- createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
2587
- const anthropicResponse = translateToAnthropic(body);
2588
- debugJson(logger$4, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
2589
- return c.json(anthropicResponse);
2499
+ const flushPendingContent = (pendingContent, target, message) => {
2500
+ if (pendingContent.length === 0) return;
2501
+ const messageContent = [...pendingContent];
2502
+ target.push(createMessage(message.role, messageContent, message.phase));
2503
+ pendingContent.length = 0;
2590
2504
  };
2591
- const createProviderMessagesUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
2592
- endpoint: "provider_messages",
2593
- model: payload.model,
2594
- providerName: provider,
2595
- sessionId: parseUserIdMetadata(payload.metadata?.user_id).sessionId
2505
+ const createMessage = (role, content, phase) => ({
2506
+ type: MESSAGE_TYPE,
2507
+ role,
2508
+ content,
2509
+ ...role === "assistant" && phase ? { phase } : {}
2596
2510
  });
2597
- const adjustInputTokens = (providerConfig, usage) => {
2598
- if (!providerConfig.adjustInputTokens || !usage) return;
2599
- usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
2600
- debugJson(logger$4, "provider.messages.adjusted_usage:", usage);
2511
+ const resolveAssistantPhase = (_model, content, applyPhase) => {
2512
+ if (!applyPhase) return;
2513
+ if (typeof content === "string") return "final_answer";
2514
+ if (!Array.isArray(content)) return;
2515
+ if (!content.some((block) => block.type === "text")) return;
2516
+ return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
2601
2517
  };
2602
- const responsesUtilsDependencies = {
2603
- isResponsesApiContextManagementModel,
2604
- isResponsesApiWebSocketEnabled
2518
+ const shouldApplyPhase = (_model) => {
2519
+ return true;
2605
2520
  };
2606
- const getResponsesRequestOptions = (payload) => {
2521
+ const createTextContent = (text) => ({
2522
+ type: "input_text",
2523
+ text
2524
+ });
2525
+ const createOutPutTextContent = (text) => ({
2526
+ type: "output_text",
2527
+ text
2528
+ });
2529
+ const createImageContent = (block) => ({
2530
+ type: "input_image",
2531
+ image_url: `data:${block.source.media_type};base64,${block.source.data}`,
2532
+ detail: "auto"
2533
+ });
2534
+ const createFileContent = (block) => ({
2535
+ type: "input_file",
2536
+ file_data: `data:${block.source.media_type};base64,${block.source.data}`,
2537
+ filename: block.title ?? "document.pdf"
2538
+ });
2539
+ const createReasoningContent = (block) => {
2540
+ const { encryptedContent, id } = parseReasoningSignature(block.signature);
2541
+ const thinking = block.thinking === "Thinking..." ? "" : block.thinking;
2607
2542
  return {
2608
- vision: hasVisionInput(payload),
2609
- initiator: hasAgentInitiator(payload) ? "agent" : "user"
2543
+ id,
2544
+ type: "reasoning",
2545
+ summary: thinking ? [{
2546
+ type: "summary_text",
2547
+ text: thinking
2548
+ }] : [],
2549
+ encrypted_content: encryptedContent
2610
2550
  };
2611
2551
  };
2612
- const getResponsesTransportForModel = (selectedModel, options = {}) => {
2613
- const supportedEndpoints = selectedModel?.supported_endpoints ?? [];
2614
- const useWebSocket = responsesUtilsDependencies.isResponsesApiWebSocketEnabled();
2615
- if (options.compactType !== 1 && useWebSocket && supportedEndpoints.includes("ws:/responses")) return "websocket";
2616
- if (supportedEndpoints.includes("/responses")) return "http";
2617
- return null;
2618
- };
2619
- const hasAgentInitiator = (payload) => {
2620
- const lastItem = getPayloadItems(payload).at(-1);
2621
- if (!lastItem) return false;
2622
- if (!("role" in lastItem) || !lastItem.role) return true;
2623
- return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
2552
+ const createCompactionContent = (block) => {
2553
+ const compaction = decodeCompactionCarrierSignature(block.signature);
2554
+ if (!compaction) return;
2555
+ return {
2556
+ id: compaction.id,
2557
+ type: "compaction",
2558
+ encrypted_content: compaction.encrypted_content
2559
+ };
2624
2560
  };
2625
- const hasVisionInput = (payload) => {
2626
- return getPayloadItems(payload).some((item) => containsVisionContent(item));
2561
+ const parseReasoningSignature = (signature) => {
2562
+ const splitIndex = signature.lastIndexOf("@");
2563
+ if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
2564
+ encryptedContent: signature,
2565
+ id: ""
2566
+ };
2567
+ return {
2568
+ encryptedContent: signature.slice(0, splitIndex),
2569
+ id: signature.slice(splitIndex + 1)
2570
+ };
2627
2571
  };
2628
- const resolveResponsesCompactThreshold = (maxPromptTokens) => {
2629
- if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
2630
- return 5e4;
2572
+ const createFunctionToolCall = (block, state) => ({
2573
+ type: "function_call",
2574
+ call_id: block.id,
2575
+ name: block.name,
2576
+ arguments: JSON.stringify(block.input),
2577
+ status: "completed",
2578
+ ...state.toolSearchEnabled && isDeferredToolName(block.name) ? { namespace: block.name } : {}
2579
+ });
2580
+ const createToolSearchCall = (block) => ({
2581
+ type: "tool_search_call",
2582
+ call_id: block.id,
2583
+ arguments: normalizeToolSearchBridgeArguments(block.input),
2584
+ execution: "client",
2585
+ status: "completed"
2586
+ });
2587
+ const createToolCall = (block, state) => {
2588
+ if (state.toolSearchEnabled && isBridgeToolSearchName(block.name)) return createToolSearchCall(block);
2589
+ return createFunctionToolCall(block, state);
2631
2590
  };
2632
- const createCompactionContextManagement = (compactThreshold) => [{
2633
- type: "compaction",
2634
- compact_threshold: compactThreshold
2635
- }];
2636
- const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
2637
- if (payload.context_management !== void 0) return;
2638
- if (!responsesUtilsDependencies.isResponsesApiContextManagementModel(payload.model)) return;
2639
- payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
2591
+ const createFunctionCallOutput = (block) => ({
2592
+ type: "function_call_output",
2593
+ call_id: block.tool_use_id,
2594
+ output: convertToolResultContent(block.content),
2595
+ status: block.is_error ? "incomplete" : "completed"
2596
+ });
2597
+ const createToolCallOutput = (block, state) => {
2598
+ const toolUseName = state.toolUseNameById.get(block.tool_use_id);
2599
+ if (state.toolSearchEnabled && isBridgeToolSearchName(toolUseName ?? "")) return createToolSearchOutput(block, state.originalTools);
2600
+ return createFunctionCallOutput(block);
2640
2601
  };
2641
- const compactInputByLatestCompaction = (payload) => {
2642
- if (!Array.isArray(payload.input) || payload.input.length === 0) return;
2643
- const latestCompactionMessageIndex = getLatestCompactionMessageIndex(payload.input);
2644
- if (latestCompactionMessageIndex === void 0) return;
2645
- payload.input = payload.input.slice(latestCompactionMessageIndex);
2602
+ const createToolSearchOutput = (block, originalTools) => {
2603
+ const referencedToolNames = resolveToolSearchReferencedToolNames(block.content, originalTools);
2604
+ return {
2605
+ type: "tool_search_output",
2606
+ call_id: block.tool_use_id,
2607
+ tools: referencedToolNames.map((toolName) => convertDeferredToolToNamespace(resolveDeferredTool(toolName, originalTools))),
2608
+ execution: "client",
2609
+ status: block.is_error ? "incomplete" : "completed"
2610
+ };
2646
2611
  };
2647
- const getLatestCompactionMessageIndex = (input) => {
2648
- for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
2612
+ const resolveToolSearchReferencedToolNames = (content, originalTools) => {
2613
+ const explicitReferences = extractToolReferenceNames(content);
2614
+ if (explicitReferences.length > 0) return uniqueToolNames(explicitReferences);
2615
+ const sentinel = extractMcpToolSearchSentinel(content);
2616
+ if (sentinel) return selectDeferredToolsByNames(sentinel.names, originalTools).map((tool) => tool.name);
2617
+ return [];
2649
2618
  };
2650
- const isCompactionInputItem = (value) => {
2651
- return "type" in value && typeof value.type === "string" && value.type === "compaction";
2619
+ const extractToolReferenceNames = (content) => {
2620
+ if (!Array.isArray(content)) return [];
2621
+ return content.flatMap((block) => block.type === "tool_reference" ? [block.tool_name] : []);
2652
2622
  };
2653
- const getPayloadItems = (payload) => {
2654
- const result = [];
2655
- const { input } = payload;
2656
- if (Array.isArray(input)) result.push(...input);
2657
- return result;
2623
+ const extractMcpToolSearchSentinel = (content) => {
2624
+ if (typeof content === "string") return parseMcpToolSearchSentinel(content);
2625
+ for (const block of content) {
2626
+ if (block.type !== "text") continue;
2627
+ const sentinel = parseMcpToolSearchSentinel(block.text);
2628
+ if (sentinel) return sentinel;
2629
+ }
2630
+ return null;
2658
2631
  };
2659
- const containsVisionContent = (value) => {
2660
- if (!value) return false;
2661
- if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
2662
- if (typeof value !== "object") return false;
2663
- const record = value;
2664
- if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
2665
- if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
2666
- return false;
2632
+ const resolveDeferredTool = (toolName, originalTools) => {
2633
+ const tool = originalTools.find((candidate) => candidate.name === toolName);
2634
+ if (tool && isDeferredToolName(tool.name)) return tool;
2635
+ throw createInvalidRequestError(`Tool reference '${toolName}' has no corresponding deferred tool definition`);
2667
2636
  };
2668
- //#endregion
2669
- //#region src/services/copilot/create-responses.ts
2670
- const RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS = 6e4;
2671
- const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
2672
- if (!state.copilotToken) throw new Error("Copilot token not found");
2673
- const headers = {
2674
- ...copilotHeaders(state, requestId, vision),
2675
- "x-initiator": initiator
2676
- };
2677
- prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
2678
- prepareForCompact(headers, compactType);
2679
- payload.service_tier = void 0;
2680
- consola.log(`<-- model: ${payload.model}`);
2681
- if ((compactType === 1 ? "http" : transport) === "websocket") {
2682
- const stream = createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
2683
- requestId,
2684
- subagentMarker
2685
- }));
2686
- if (payload.stream) return stream;
2687
- return await consumeResponsesWebSocketStream(stream);
2637
+ const uniqueToolNames = (toolNames) => [...new Set(toolNames)];
2638
+ const createInvalidRequestError = (message) => new HTTPError(message, new Response(JSON.stringify({ error: {
2639
+ message,
2640
+ type: "invalid_request_error"
2641
+ } }), {
2642
+ status: 400,
2643
+ headers: { "content-type": "application/json" }
2644
+ }));
2645
+ const translateSystemPrompt = (system, model) => {
2646
+ if (!system) return null;
2647
+ const extraPrompt = getExtraPromptForModel(model);
2648
+ if (typeof system === "string") return system + extraPrompt;
2649
+ const text = system.map((block, index) => {
2650
+ if (index === 0) return block.text + "\n\n" + extraPrompt + "\n\n";
2651
+ return block.text;
2652
+ }).join(" ");
2653
+ return text.length > 0 ? text : null;
2654
+ };
2655
+ const convertAnthropicTools = (tools, toolSearchEnabled) => {
2656
+ if (!tools || tools.length === 0) return null;
2657
+ const converted = [];
2658
+ let addedToolSearch = false;
2659
+ const searchableToolNames = toolSearchEnabled ? listDeferredToolNames(tools) : [];
2660
+ for (const tool of tools) {
2661
+ if (isBridgeToolSearchName(tool.name)) {
2662
+ if (toolSearchEnabled && !addedToolSearch) {
2663
+ converted.push(createResponsesToolSearchDefinition(searchableToolNames));
2664
+ addedToolSearch = true;
2665
+ }
2666
+ continue;
2667
+ }
2668
+ if (toolSearchEnabled && isDeferredToolName(tool.name)) {
2669
+ converted.push(convertDeferredToolToNamespace(tool));
2670
+ continue;
2671
+ }
2672
+ converted.push(convertToolToFunction(tool));
2688
2673
  }
2689
- return await createHttpResponses(payload, headers);
2674
+ return converted;
2690
2675
  };
2691
- const createHttpResponses = async (payload, headers) => {
2692
- const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
2693
- method: "POST",
2694
- headers,
2695
- body: JSON.stringify(payload)
2696
- });
2697
- logCopilotRateLimits(response.headers);
2698
- if (!response.ok) {
2699
- consola.error("Failed to create responses", response);
2700
- throw new HTTPError("Failed to create responses", response);
2676
+ const createResponsesToolSearchDefinition = (searchableToolNames) => ({
2677
+ type: "tool_search",
2678
+ execution: "client",
2679
+ description: "Load deferred tools by exact name before using them. Return only the searchable tool names you need for the next step.",
2680
+ parameters: {
2681
+ type: "object",
2682
+ properties: { names: {
2683
+ type: "array",
2684
+ description: "Exact deferred tool names to load.",
2685
+ items: {
2686
+ type: "string",
2687
+ enum: searchableToolNames
2688
+ },
2689
+ minItems: 1
2690
+ } },
2691
+ required: ["names"],
2692
+ additionalProperties: false
2693
+ }
2694
+ });
2695
+ const convertToolToFunction = (tool) => ({
2696
+ type: "function",
2697
+ name: tool.name,
2698
+ parameters: normalizeToolSchema(tool.input_schema),
2699
+ strict: false,
2700
+ ...tool.description ? { description: tool.description } : {}
2701
+ });
2702
+ const convertDeferredToolToNamespace = (tool) => ({
2703
+ type: "namespace",
2704
+ name: tool.name,
2705
+ ...tool.description ? { description: tool.description } : {},
2706
+ tools: [{
2707
+ type: "function",
2708
+ name: tool.name,
2709
+ parameters: normalizeToolSchema(tool.input_schema),
2710
+ strict: false,
2711
+ defer_loading: true,
2712
+ ...tool.description ? { description: tool.description } : {}
2713
+ }]
2714
+ });
2715
+ const convertAnthropicToolChoice = (choice, toolSearchEnabled) => {
2716
+ if (!choice) return "auto";
2717
+ switch (choice.type) {
2718
+ case "auto": return "auto";
2719
+ case "any": return "required";
2720
+ case "tool":
2721
+ if (toolSearchEnabled && choice.name && isBridgeToolSearchName(choice.name)) return "auto";
2722
+ return choice.name ? {
2723
+ type: "function",
2724
+ name: choice.name
2725
+ } : "auto";
2726
+ case "none": return "none";
2727
+ default: return "auto";
2701
2728
  }
2702
- if (payload.stream) return events(response);
2703
- return await response.json();
2704
2729
  };
2705
- const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
2706
- const initiator = getResponsesWebSocketInitiator(preparedHeaders);
2730
+ const translateResponsesResultToAnthropic = (response, options) => {
2731
+ const contentBlocks = mapOutputToAnthropicContent(response.output, options);
2732
+ const usage = mapResponsesUsage(response);
2733
+ let anthropicContent = fallbackContentBlocks(response.output_text);
2734
+ if (contentBlocks.length > 0) anthropicContent = contentBlocks;
2735
+ const stopReason = mapResponsesStopReason(response, options);
2707
2736
  return {
2708
- headers: copilotWebSocketHeaders(preparedHeaders),
2709
- poolKey: buildResponsesWebSocketPoolKey(payload, options),
2710
- payload: buildResponsesWebSocketPayload(payload, initiator)
2737
+ id: response.id,
2738
+ type: "message",
2739
+ role: "assistant",
2740
+ content: anthropicContent,
2741
+ model: response.model,
2742
+ stop_reason: stopReason,
2743
+ stop_sequence: null,
2744
+ usage
2711
2745
  };
2712
2746
  };
2713
- const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
2714
- const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
2715
- const subagentKey = subagentMarker ? [
2716
- subagentMarker.session_id,
2717
- subagentMarker.agent_id,
2718
- subagentMarker.agent_type
2719
- ].join(":") : "main";
2720
- return [
2721
- tokenFingerprint,
2722
- payload.model,
2723
- requestId,
2724
- subagentKey
2725
- ].map(encodePoolKeyPart).join("|");
2726
- };
2727
- const getResponsesWebSocketInitiator = (preparedHeaders) => {
2728
- return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
2729
- };
2730
- const createPooledResponsesWebSocketStream = (request) => runResponsesWebSocketRequest(request);
2731
- const buildResponsesWebSocketPayload = (payload, initiator) => {
2732
- const websocketPayload = {
2733
- ...payload,
2734
- type: "response.create",
2735
- initiator
2736
- };
2737
- delete websocketPayload.stream;
2738
- delete websocketPayload["background"];
2739
- delete websocketPayload.service_tier;
2740
- return websocketPayload;
2747
+ const mapOutputToAnthropicContent = (output, options) => {
2748
+ const contentBlocks = [];
2749
+ for (const item of output) switch (item.type) {
2750
+ case "reasoning": {
2751
+ const thinkingText = extractReasoningText(item);
2752
+ if (thinkingText.length > 0) contentBlocks.push({
2753
+ type: "thinking",
2754
+ thinking: thinkingText,
2755
+ signature: (item.encrypted_content ?? "") + "@" + item.id
2756
+ });
2757
+ break;
2758
+ }
2759
+ case "function_call": {
2760
+ const toolUseBlock = createToolUseContentBlock(item);
2761
+ if (toolUseBlock) contentBlocks.push(toolUseBlock);
2762
+ break;
2763
+ }
2764
+ case "tool_search_call": {
2765
+ const toolUseBlock = createToolSearchUseContentBlock(item, options?.toolSearchName);
2766
+ if (toolUseBlock) contentBlocks.push(toolUseBlock);
2767
+ break;
2768
+ }
2769
+ case "tool_search_output": break;
2770
+ case "message": {
2771
+ const combinedText = combineMessageTextContent(item.content);
2772
+ if (combinedText.length > 0) contentBlocks.push({
2773
+ type: "text",
2774
+ text: combinedText
2775
+ });
2776
+ break;
2777
+ }
2778
+ case "compaction": {
2779
+ const compactionBlock = createCompactionThinkingBlock(item);
2780
+ if (compactionBlock) contentBlocks.push(compactionBlock);
2781
+ break;
2782
+ }
2783
+ default: {
2784
+ const combinedText = combineMessageTextContent(item.content);
2785
+ if (combinedText.length > 0) contentBlocks.push({
2786
+ type: "text",
2787
+ text: combinedText
2788
+ });
2789
+ }
2790
+ }
2791
+ return contentBlocks;
2741
2792
  };
2742
- const buildResponsesWebSocketUrl = (baseUrl) => {
2743
- const url = new URL(`${baseUrl.replace(/\/+$/u, "")}/responses`);
2744
- if (url.protocol === "https:") url.protocol = "wss:";
2745
- else if (url.protocol === "http:") url.protocol = "ws:";
2746
- return url.toString();
2747
- };
2748
- const responsesWebSocketPool = /* @__PURE__ */ new Map();
2749
- const responsesWebSocketActiveRequests = /* @__PURE__ */ new Map();
2750
- const runResponsesWebSocketRequest = async function* (request) {
2751
- const { entry, pooled } = getResponsesWebSocketRequestTarget(request);
2752
- const release = acquireResponsesWebSocketEntry(request.poolKey, entry, pooled);
2753
- try {
2754
- const websocket = await getReadyResponsesWebSocket(request.poolKey, entry, pooled);
2755
- websocket.send(JSON.stringify(request.payload));
2756
- for await (const data of createWebSocketMessageStream(websocket)) {
2757
- const chunk = createResponsesWebSocketStreamChunk(data);
2758
- yield chunk;
2759
- if (isTerminalResponsesStreamChunk(chunk)) return;
2793
+ const combineMessageTextContent = (content) => {
2794
+ if (!Array.isArray(content)) return "";
2795
+ let aggregated = "";
2796
+ for (const block of content) {
2797
+ if (isResponseOutputText(block)) {
2798
+ aggregated += block.text;
2799
+ continue;
2800
+ }
2801
+ if (isResponseOutputRefusal(block)) {
2802
+ aggregated += block.refusal;
2803
+ continue;
2804
+ }
2805
+ if (typeof block.text === "string") {
2806
+ aggregated += block.text;
2807
+ continue;
2808
+ }
2809
+ if (typeof block.reasoning === "string") {
2810
+ aggregated += block.reasoning;
2811
+ continue;
2760
2812
  }
2761
- removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2762
- throw new Error("Responses websocket ended without a terminal response");
2763
- } catch (error) {
2764
- removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2765
- throw toError(error);
2766
- } finally {
2767
- release();
2768
2813
  }
2814
+ return aggregated;
2769
2815
  };
2770
- const getResponsesWebSocketRequestTarget = (request) => {
2771
- if (getResponsesWebSocketActiveRequestCount(request.poolKey) > 0) return {
2772
- entry: createResponsesWebSocketEntry(request),
2773
- pooled: false
2816
+ const extractReasoningText = (item) => {
2817
+ const segments = [];
2818
+ const collectFromBlocks = (blocks) => {
2819
+ if (!Array.isArray(blocks)) return;
2820
+ for (const block of blocks) if (typeof block.text === "string") {
2821
+ segments.push(block.text);
2822
+ continue;
2823
+ }
2774
2824
  };
2775
- const existing = responsesWebSocketPool.get(request.poolKey);
2776
- if (existing && !existing.closed) {
2777
- clearResponsesWebSocketIdleTimer(existing);
2778
- return {
2779
- entry: existing,
2780
- pooled: true
2781
- };
2782
- }
2783
- const entry = createResponsesWebSocketEntry(request);
2784
- responsesWebSocketPool.set(request.poolKey, entry);
2825
+ if (!item.summary || item.summary.length === 0) return THINKING_TEXT;
2826
+ collectFromBlocks(item.summary);
2827
+ return segments.join("").trim();
2828
+ };
2829
+ const createToolUseContentBlock = (call) => {
2830
+ const toolId = call.call_id;
2831
+ const toolName = resolveToolUseName(call);
2832
+ if (!toolName || !toolId) return null;
2785
2833
  return {
2786
- entry,
2787
- pooled: true
2834
+ type: "tool_use",
2835
+ id: toolId,
2836
+ name: toolName,
2837
+ input: parseFunctionCallArguments(call.arguments)
2788
2838
  };
2789
2839
  };
2790
- const createResponsesWebSocketEntry = (request) => {
2791
- const entry = {
2792
- closed: false,
2793
- idleTimer: null,
2794
- requestCount: 0,
2795
- websocketPromise: openResponsesWebSocket({
2796
- headers: request.headers,
2797
- url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
2798
- })
2840
+ const createToolSearchUseContentBlock = (call, toolSearchName = BRIDGE_TOOL_SEARCH_NAME) => {
2841
+ const toolId = call.call_id;
2842
+ if (!toolId) return null;
2843
+ return {
2844
+ type: "tool_use",
2845
+ id: toolId,
2846
+ name: toolSearchName,
2847
+ input: parseToolSearchArguments(call.arguments)
2799
2848
  };
2800
- entry.websocketPromise.then((websocket) => {
2801
- websocket.addEventListener("close", () => {
2802
- removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2803
- });
2804
- websocket.addEventListener("error", () => {
2805
- removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2806
- });
2807
- }).catch(() => {
2808
- removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2809
- });
2810
- return entry;
2811
2849
  };
2812
- const acquireResponsesWebSocketEntry = (poolKey, entry, pooled) => {
2813
- clearResponsesWebSocketIdleTimer(entry);
2814
- incrementResponsesWebSocketActiveRequestCount(poolKey);
2815
- entry.requestCount += 1;
2816
- let released = false;
2817
- return () => {
2818
- if (released) return;
2819
- released = true;
2820
- entry.requestCount -= 1;
2821
- decrementResponsesWebSocketActiveRequestCount(poolKey);
2822
- if (entry.closed || entry.requestCount > 0) return;
2823
- if (pooled && responsesWebSocketPool.get(poolKey) === entry) {
2824
- scheduleResponsesWebSocketIdleClose(poolKey, entry);
2825
- return;
2826
- }
2827
- removeResponsesWebSocketPoolEntry(poolKey, entry);
2828
- };
2850
+ const resolveToolUseName = (call) => {
2851
+ if (typeof call.namespace === "string" && call.namespace.length > 0) return call.namespace;
2852
+ return call.name;
2829
2853
  };
2830
- const getReadyResponsesWebSocket = async (poolKey, entry, pooled) => {
2831
- if (entry.closed) throw new Error("Responses websocket became unavailable before the request started");
2832
- const websocket = await entry.websocketPromise;
2833
- if (entry.closed || pooled && responsesWebSocketPool.get(poolKey) !== entry) throw new Error("Responses websocket became unavailable before the request started");
2834
- if (websocket.readyState !== WebSocket.OPEN) {
2835
- removeResponsesWebSocketPoolEntry(poolKey, entry);
2836
- throw new Error("Responses websocket became unavailable before the request started");
2837
- }
2838
- return websocket;
2839
- };
2840
- const scheduleResponsesWebSocketIdleClose = (poolKey, entry) => {
2841
- clearResponsesWebSocketIdleTimer(entry);
2842
- entry.idleTimer = setTimeout(() => {
2843
- removeResponsesWebSocketPoolEntry(poolKey, entry);
2844
- }, RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS);
2845
- unrefTimer(entry.idleTimer);
2846
- };
2847
- const clearResponsesWebSocketIdleTimer = (entry) => {
2848
- if (entry.idleTimer) {
2849
- clearTimeout(entry.idleTimer);
2850
- entry.idleTimer = null;
2851
- }
2852
- };
2853
- const getResponsesWebSocketActiveRequestCount = (poolKey) => responsesWebSocketActiveRequests.get(poolKey) ?? 0;
2854
- const incrementResponsesWebSocketActiveRequestCount = (poolKey) => {
2855
- responsesWebSocketActiveRequests.set(poolKey, getResponsesWebSocketActiveRequestCount(poolKey) + 1);
2856
- };
2857
- const decrementResponsesWebSocketActiveRequestCount = (poolKey) => {
2858
- const nextCount = getResponsesWebSocketActiveRequestCount(poolKey) - 1;
2859
- if (nextCount <= 0) {
2860
- responsesWebSocketActiveRequests.delete(poolKey);
2861
- return;
2862
- }
2863
- responsesWebSocketActiveRequests.set(poolKey, nextCount);
2864
- };
2865
- const removeResponsesWebSocketPoolEntry = (poolKey, entry) => {
2866
- if (responsesWebSocketPool.get(poolKey) === entry) responsesWebSocketPool.delete(poolKey);
2867
- if (entry.closed) return;
2868
- entry.closed = true;
2869
- clearResponsesWebSocketIdleTimer(entry);
2870
- entry.websocketPromise.then(closeResponsesWebSocket).catch(() => {});
2871
- };
2872
- const unrefTimer = (timer) => {
2873
- if (typeof timer === "object" && "unref" in timer && typeof timer.unref === "function") timer.unref();
2874
- };
2875
- const createResponsesWebSocketError = (message, event) => {
2876
- const reason = event?.error ?? event?.message;
2877
- if (reason === void 0 || reason === "") return new Error(message);
2878
- const cause = toError(reason);
2879
- return new Error(`${message}: ${cause.message}`, { cause });
2880
- };
2881
- const openResponsesWebSocket = async ({ headers, url }) => await new Promise((resolve, reject) => {
2882
- const dispatcher = getProxyEnvDispatcher();
2883
- const websocket = new WebSocket(url, dispatcher ? {
2884
- dispatcher,
2885
- headers
2886
- } : { headers });
2887
- const cleanup = () => {
2888
- websocket.removeEventListener("open", onOpen);
2889
- websocket.removeEventListener("error", onError);
2890
- };
2891
- const onOpen = () => {
2892
- cleanup();
2893
- resolve(websocket);
2894
- };
2895
- const onError = (event) => {
2896
- cleanup();
2897
- reject(createResponsesWebSocketError("Failed to create responses websocket", event));
2898
- };
2899
- websocket.addEventListener("open", onOpen);
2900
- websocket.addEventListener("error", onError);
2901
- });
2902
- const createWebSocketMessageStream = async function* (websocket) {
2903
- const queue = [];
2904
- let closed = false;
2905
- let error = null;
2906
- let notify = null;
2907
- const wake = () => {
2908
- notify?.();
2909
- notify = null;
2910
- };
2911
- const onMessage = (event) => {
2912
- queue.push(normalizeWebSocketMessageData(event.data));
2913
- wake();
2914
- };
2915
- const onClose = () => {
2916
- closed = true;
2917
- wake();
2918
- };
2919
- const onError = (event) => {
2920
- error = createResponsesWebSocketError("Responses websocket stream error", event);
2921
- wake();
2854
+ const createCompactionThinkingBlock = (item) => {
2855
+ if (!item.id || !item.encrypted_content) return null;
2856
+ return {
2857
+ type: "thinking",
2858
+ thinking: THINKING_TEXT,
2859
+ signature: encodeCompactionCarrierSignature({
2860
+ id: item.id,
2861
+ encrypted_content: item.encrypted_content
2862
+ })
2922
2863
  };
2923
- websocket.addEventListener("message", onMessage);
2924
- websocket.addEventListener("close", onClose);
2925
- websocket.addEventListener("error", onError);
2926
- try {
2927
- while (true) {
2928
- const item = queue.shift();
2929
- if (item) {
2930
- yield await item;
2931
- continue;
2932
- }
2933
- if (error) throw toError(error);
2934
- if (closed) break;
2935
- await new Promise((resolve) => {
2936
- notify = resolve;
2937
- });
2938
- }
2939
- } finally {
2940
- websocket.removeEventListener("message", onMessage);
2941
- websocket.removeEventListener("close", onClose);
2942
- websocket.removeEventListener("error", onError);
2943
- }
2944
2864
  };
2945
- const normalizeWebSocketMessageData = async (data) => {
2946
- if (typeof data === "string") return data;
2947
- if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
2948
- if (ArrayBuffer.isView(data)) {
2949
- const view = data;
2950
- return new TextDecoder().decode(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
2865
+ const parseFunctionCallArguments = (rawArguments) => {
2866
+ if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
2867
+ try {
2868
+ const parsed = JSON.parse(rawArguments);
2869
+ if (Array.isArray(parsed)) return { arguments: parsed };
2870
+ if (parsed && typeof parsed === "object") return parsed;
2871
+ } catch (error) {
2872
+ consola.warn("Failed to parse function call arguments", {
2873
+ error,
2874
+ rawArguments
2875
+ });
2951
2876
  }
2952
- if (isTextReadable(data)) return await data.text();
2953
- return String(data);
2954
- };
2955
- const isTextReadable = (value) => {
2956
- if (!value || typeof value !== "object" || !("text" in value)) return false;
2957
- return typeof value.text === "function";
2958
- };
2959
- const toError = (value) => {
2960
- if (value instanceof Error) return value;
2961
- return new Error(String(value));
2877
+ return { raw_arguments: rawArguments };
2962
2878
  };
2963
- const getHeaderValue = (headers, headerName) => {
2964
- const normalizedHeaderName = headerName.toLowerCase();
2965
- return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
2879
+ const parseToolSearchArguments = (argumentsValue) => {
2880
+ return formatToolSearchBridgeArguments(argumentsValue);
2966
2881
  };
2967
- const encodePoolKeyPart = (value) => encodeURIComponent(value);
2968
- const createResponsesWebSocketStreamChunk = (data) => {
2969
- if (data === "[DONE]") return { data };
2970
- try {
2971
- const parsed = JSON.parse(data);
2972
- if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
2973
- return {
2974
- data: JSON.stringify(parsed),
2975
- event: typeof parsed.type === "string" ? parsed.type : void 0,
2976
- id: typeof parsed.id === "string" ? parsed.id : void 0
2977
- };
2978
- } catch {
2979
- return { data };
2980
- }
2882
+ const fallbackContentBlocks = (outputText) => {
2883
+ if (!outputText) return [];
2884
+ return [{
2885
+ type: "text",
2886
+ text: outputText
2887
+ }];
2981
2888
  };
2982
- const isTerminalResponsesStreamChunk = (chunk) => {
2983
- if (!chunk.data || chunk.data === "[DONE]") return false;
2984
- try {
2985
- const parsed = JSON.parse(chunk.data);
2986
- return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
2987
- } catch {
2988
- return false;
2889
+ const mapResponsesStopReason = (response, options) => {
2890
+ const { status, incomplete_details: incompleteDetails } = response;
2891
+ if (status === "completed") {
2892
+ if (response.output.length === 0) return options?.hasToolCall ? "tool_use" : "end_turn";
2893
+ if (response.output.some((item) => item.type === "function_call" || item.type === "tool_search_call")) return "tool_use";
2894
+ return "end_turn";
2989
2895
  }
2990
- };
2991
- const consumeResponsesWebSocketStream = async (stream) => {
2992
- for await (const chunk of stream) {
2993
- if (!chunk.data || chunk.data === "[DONE]") continue;
2994
- const event = JSON.parse(chunk.data);
2995
- if (event.type === "error") throw new Error(event.message);
2996
- if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return event.response;
2896
+ if (status === "incomplete") {
2897
+ if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
2898
+ if (incompleteDetails?.reason === "content_filter") return "end_turn";
2997
2899
  }
2998
- throw new Error("Responses websocket ended without a terminal response");
2999
- };
3000
- const closeResponsesWebSocket = (websocket) => {
3001
- if (websocket.readyState === WebSocket.CONNECTING || websocket.readyState === WebSocket.OPEN) websocket.close();
3002
- };
3003
- //#endregion
3004
- //#region src/routes/messages/responses-translation.ts
3005
- const MESSAGE_TYPE = "message";
3006
- const COMPACTION_SIGNATURE_PREFIX = "cm1#";
3007
- const COMPACTION_SIGNATURE_SEPARATOR = "@";
3008
- const THINKING_TEXT = "Thinking...";
3009
- const buildPromptCacheKey = (basePromptCacheKey, subagentAgentId) => {
3010
- if (!basePromptCacheKey) return null;
3011
- const normalizedSubagentAgentId = subagentAgentId?.trim() || null;
3012
- if (!normalizedSubagentAgentId) return basePromptCacheKey;
3013
- return `${basePromptCacheKey}:agent:${normalizedSubagentAgentId}`;
2900
+ return null;
3014
2901
  };
3015
- const translateAnthropicMessagesToResponsesPayload = (payload, subagentAgentId) => {
3016
- const input = [];
3017
- const applyPhase = shouldApplyPhase(payload.model);
3018
- const toolSearchEnabled = shouldEnableResponsesToolSearch({
3019
- model: payload.model,
3020
- tools: payload.tools
3021
- });
3022
- const translationState = {
3023
- originalTools: payload.tools ?? [],
3024
- toolSearchEnabled,
3025
- toolUseNameById: /* @__PURE__ */ new Map()
3026
- };
3027
- for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase, translationState));
3028
- const hasOriginalTools = Array.isArray(payload.tools) && payload.tools.length > 0;
3029
- const translatedTools = convertAnthropicTools(payload.tools, toolSearchEnabled);
3030
- const toolChoice = convertAnthropicToolChoice(payload.tool_choice, toolSearchEnabled);
3031
- const { sessionId: metadataPromptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
3032
- const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
3033
- const promptCacheKey = buildPromptCacheKey(metadataPromptCacheKey ?? sessionAffinity, subagentAgentId);
3034
- const responsesPayload = {
3035
- model: payload.model,
3036
- input,
3037
- instructions: translateSystemPrompt(payload.system, payload.model),
3038
- temperature: 1,
3039
- top_p: payload.top_p ?? null,
3040
- max_output_tokens: Math.max(payload.max_tokens, 12800),
3041
- tools: translatedTools,
3042
- tool_choice: toolChoice,
3043
- metadata: payload.metadata ? { ...payload.metadata } : null,
3044
- stream: payload.stream ?? null,
3045
- store: false,
3046
- parallel_tool_calls: true,
3047
- reasoning: {
3048
- effort: getReasoningEffortForModel(payload.model),
3049
- summary: "detailed"
3050
- },
3051
- include: ["reasoning.encrypted_content"]
2902
+ const mapResponsesUsage = (response) => {
2903
+ const inputTokens = response.usage?.input_tokens ?? 0;
2904
+ const outputTokens = response.usage?.output_tokens ?? 0;
2905
+ return {
2906
+ input_tokens: inputTokens - (response.usage?.input_tokens_details?.cached_tokens ?? 0),
2907
+ output_tokens: outputTokens,
2908
+ ...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
3052
2909
  };
3053
- if (hasOriginalTools) responsesPayload.prompt_cache_key = promptCacheKey;
3054
- return responsesPayload;
3055
- };
3056
- const encodeCompactionCarrierSignature = (compaction) => {
3057
- return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
3058
2910
  };
3059
- const decodeCompactionCarrierSignature = (signature) => {
3060
- if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
3061
- const raw = signature.slice(4);
3062
- const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
3063
- if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
3064
- const encrypted_content = raw.slice(0, separatorIndex);
3065
- const id = raw.slice(separatorIndex + 1);
3066
- if (!encrypted_content) return;
3067
- return {
3068
- id,
3069
- encrypted_content
3070
- };
2911
+ const isRecord = (value) => typeof value === "object" && value !== null;
2912
+ const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
2913
+ const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
2914
+ const convertToolResultContent = (content) => {
2915
+ if (typeof content === "string") return content;
2916
+ if (Array.isArray(content)) {
2917
+ const result = [];
2918
+ for (const block of content) switch (block.type) {
2919
+ case "text":
2920
+ result.push(createTextContent(block.text));
2921
+ break;
2922
+ case "image":
2923
+ result.push(createImageContent(block));
2924
+ break;
2925
+ case "document":
2926
+ result.push(createFileContent(block));
2927
+ break;
2928
+ case "tool_reference":
2929
+ result.push(createTextContent(`Tool ${block.tool_name} loaded`));
2930
+ break;
2931
+ default: break;
2932
+ }
2933
+ return result;
3071
2934
  }
2935
+ return "";
3072
2936
  };
3073
- const translateMessage = (message, model, applyPhase, state) => {
3074
- if (message.role === "user") return translateUserMessage(message, state);
3075
- return translateAssistantMessage(message, model, applyPhase, state);
2937
+ //#endregion
2938
+ //#region src/routes/messages/responses-stream-translation.ts
2939
+ const MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE = 20;
2940
+ var FunctionCallArgumentsValidationError = class extends Error {
2941
+ constructor(message) {
2942
+ super(message);
2943
+ this.name = "FunctionCallArgumentsValidationError";
2944
+ }
3076
2945
  };
3077
- const translateUserMessage = (message, state) => {
3078
- if (typeof message.content === "string") return [createMessage("user", message.content)];
3079
- if (!Array.isArray(message.content)) return [];
3080
- const items = [];
3081
- const pendingContent = [];
3082
- for (const block of message.content) {
3083
- if (block.type === "tool_result") {
3084
- flushPendingContent(pendingContent, items, { role: "user" });
3085
- items.push(createToolCallOutput(block, state));
2946
+ const updateWhitespaceRunState = (previousCount, chunk) => {
2947
+ let count = previousCount;
2948
+ for (const char of chunk) {
2949
+ if (char === "\r" || char === "\n" || char === " ") {
2950
+ count += 1;
2951
+ if (count > MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE) return {
2952
+ nextCount: count,
2953
+ exceeded: true
2954
+ };
3086
2955
  continue;
3087
2956
  }
3088
- const converted = translateUserContentBlock(block);
3089
- if (converted.length > 0) pendingContent.push(...converted);
2957
+ if (char !== " ") count = 0;
3090
2958
  }
3091
- flushPendingContent(pendingContent, items, { role: "user" });
3092
- return items;
2959
+ return {
2960
+ nextCount: count,
2961
+ exceeded: false
2962
+ };
3093
2963
  };
3094
- const translateAssistantMessage = (message, model, applyPhase, state) => {
3095
- const assistantPhase = resolveAssistantPhase(model, message.content, applyPhase);
3096
- if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
3097
- if (!Array.isArray(message.content)) return [];
3098
- const items = [];
3099
- const pendingContent = [];
3100
- for (const block of message.content) {
3101
- if (block.type === "tool_use") {
3102
- state.toolUseNameById.set(block.id, block.name);
3103
- flushPendingContent(pendingContent, items, {
3104
- role: "assistant",
3105
- phase: assistantPhase
3106
- });
3107
- items.push(createToolCall(block, state));
3108
- continue;
3109
- }
3110
- if (block.type === "thinking" && block.signature) {
3111
- const compactionContent = createCompactionContent(block);
3112
- if (compactionContent) {
3113
- flushPendingContent(pendingContent, items, {
3114
- role: "assistant",
3115
- phase: assistantPhase
3116
- });
3117
- items.push(compactionContent);
3118
- continue;
3119
- }
3120
- if (block.signature.includes("@")) {
3121
- flushPendingContent(pendingContent, items, {
3122
- role: "assistant",
3123
- phase: assistantPhase
3124
- });
3125
- items.push(createReasoningContent(block));
3126
- continue;
3127
- }
3128
- }
3129
- const converted = translateAssistantContentBlock(block);
3130
- if (converted) pendingContent.push(converted);
3131
- }
3132
- flushPendingContent(pendingContent, items, {
3133
- role: "assistant",
3134
- phase: assistantPhase
3135
- });
3136
- return items;
3137
- };
3138
- const translateUserContentBlock = (block) => {
3139
- switch (block.type) {
3140
- case "text": return [createTextContent(block.text)];
3141
- case "image": return [createImageContent(block)];
3142
- case "document": return [createFileContent(block)];
2964
+ const createResponsesStreamState = (options) => ({
2965
+ messageStartSent: false,
2966
+ messageCompleted: false,
2967
+ nextContentBlockIndex: 0,
2968
+ blockIndexByKey: /* @__PURE__ */ new Map(),
2969
+ openBlocks: /* @__PURE__ */ new Set(),
2970
+ blockHasDelta: /* @__PURE__ */ new Set(),
2971
+ functionCallStateByOutputIndex: /* @__PURE__ */ new Map(),
2972
+ toolSearchName: options?.toolSearchName ?? "mcp__tool_search__search",
2973
+ hasToolCall: false
2974
+ });
2975
+ const translateResponsesStreamEvent = (rawEvent, state) => {
2976
+ switch (rawEvent.type) {
2977
+ case "response.created": return handleResponseCreated(rawEvent, state);
2978
+ case "response.output_item.added": return handleOutputItemAdded$1(rawEvent, state);
2979
+ case "response.reasoning_summary_text.delta": return handleReasoningSummaryTextDelta(rawEvent, state);
2980
+ case "response.output_text.delta": return handleOutputTextDelta(rawEvent, state);
2981
+ case "response.reasoning_summary_text.done": return handleReasoningSummaryTextDone(rawEvent, state);
2982
+ case "response.output_text.done": return handleOutputTextDone(rawEvent, state);
2983
+ case "response.output_item.done": return handleOutputItemDone$1(rawEvent, state);
2984
+ case "response.function_call_arguments.delta": return handleFunctionCallArgumentsDelta(rawEvent, state);
2985
+ case "response.function_call_arguments.done": return handleFunctionCallArgumentsDone(rawEvent, state);
2986
+ case "response.completed":
2987
+ case "response.incomplete": return handleResponseCompleted(rawEvent, state);
2988
+ case "response.failed": return handleResponseFailed(rawEvent, state);
2989
+ case "error": return handleErrorEvent(rawEvent, state);
3143
2990
  default: return [];
3144
2991
  }
3145
2992
  };
3146
- const translateAssistantContentBlock = (block) => {
3147
- switch (block.type) {
3148
- case "text": return createOutPutTextContent(block.text);
3149
- default: return;
3150
- }
3151
- };
3152
- const flushPendingContent = (pendingContent, target, message) => {
3153
- if (pendingContent.length === 0) return;
3154
- const messageContent = [...pendingContent];
3155
- target.push(createMessage(message.role, messageContent, message.phase));
3156
- pendingContent.length = 0;
3157
- };
3158
- const createMessage = (role, content, phase) => ({
3159
- type: MESSAGE_TYPE,
3160
- role,
3161
- content,
3162
- ...role === "assistant" && phase ? { phase } : {}
3163
- });
3164
- const resolveAssistantPhase = (_model, content, applyPhase) => {
3165
- if (!applyPhase) return;
3166
- if (typeof content === "string") return "final_answer";
3167
- if (!Array.isArray(content)) return;
3168
- if (!content.some((block) => block.type === "text")) return;
3169
- return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
3170
- };
3171
- const shouldApplyPhase = (_model) => {
3172
- return true;
3173
- };
3174
- const createTextContent = (text) => ({
3175
- type: "input_text",
3176
- text
3177
- });
3178
- const createOutPutTextContent = (text) => ({
3179
- type: "output_text",
3180
- text
3181
- });
3182
- const createImageContent = (block) => ({
3183
- type: "input_image",
3184
- image_url: `data:${block.source.media_type};base64,${block.source.data}`,
3185
- detail: "auto"
3186
- });
3187
- const createFileContent = (block) => ({
3188
- type: "input_file",
3189
- file_data: `data:${block.source.media_type};base64,${block.source.data}`,
3190
- filename: block.title ?? "document.pdf"
3191
- });
3192
- const createReasoningContent = (block) => {
3193
- const { encryptedContent, id } = parseReasoningSignature(block.signature);
3194
- const thinking = block.thinking === "Thinking..." ? "" : block.thinking;
3195
- return {
3196
- id,
3197
- type: "reasoning",
3198
- summary: thinking ? [{
3199
- type: "summary_text",
3200
- text: thinking
3201
- }] : [],
3202
- encrypted_content: encryptedContent
3203
- };
3204
- };
3205
- const createCompactionContent = (block) => {
3206
- const compaction = decodeCompactionCarrierSignature(block.signature);
3207
- if (!compaction) return;
3208
- return {
3209
- id: compaction.id,
3210
- type: "compaction",
3211
- encrypted_content: compaction.encrypted_content
3212
- };
3213
- };
3214
- const parseReasoningSignature = (signature) => {
3215
- const splitIndex = signature.lastIndexOf("@");
3216
- if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
3217
- encryptedContent: signature,
3218
- id: ""
3219
- };
3220
- return {
3221
- encryptedContent: signature.slice(0, splitIndex),
3222
- id: signature.slice(splitIndex + 1)
3223
- };
3224
- };
3225
- const createFunctionToolCall = (block, state) => ({
3226
- type: "function_call",
3227
- call_id: block.id,
3228
- name: block.name,
3229
- arguments: JSON.stringify(block.input),
3230
- status: "completed",
3231
- ...state.toolSearchEnabled && isDeferredToolName(block.name) ? { namespace: block.name } : {}
3232
- });
3233
- const createToolSearchCall = (block) => ({
3234
- type: "tool_search_call",
3235
- call_id: block.id,
3236
- arguments: normalizeToolSearchBridgeArguments(block.input),
3237
- execution: "client",
3238
- status: "completed"
3239
- });
3240
- const createToolCall = (block, state) => {
3241
- if (state.toolSearchEnabled && isBridgeToolSearchName(block.name)) return createToolSearchCall(block);
3242
- return createFunctionToolCall(block, state);
3243
- };
3244
- const createFunctionCallOutput = (block) => ({
3245
- type: "function_call_output",
3246
- call_id: block.tool_use_id,
3247
- output: convertToolResultContent(block.content),
3248
- status: block.is_error ? "incomplete" : "completed"
3249
- });
3250
- const createToolCallOutput = (block, state) => {
3251
- const toolUseName = state.toolUseNameById.get(block.tool_use_id);
3252
- if (state.toolSearchEnabled && isBridgeToolSearchName(toolUseName ?? "")) return createToolSearchOutput(block, state.originalTools);
3253
- return createFunctionCallOutput(block);
3254
- };
3255
- const createToolSearchOutput = (block, originalTools) => {
3256
- const referencedToolNames = resolveToolSearchReferencedToolNames(block.content, originalTools);
3257
- return {
3258
- type: "tool_search_output",
3259
- call_id: block.tool_use_id,
3260
- tools: referencedToolNames.map((toolName) => convertDeferredToolToNamespace(resolveDeferredTool(toolName, originalTools))),
3261
- execution: "client",
3262
- status: block.is_error ? "incomplete" : "completed"
3263
- };
3264
- };
3265
- const resolveToolSearchReferencedToolNames = (content, originalTools) => {
3266
- const explicitReferences = extractToolReferenceNames(content);
3267
- if (explicitReferences.length > 0) return uniqueToolNames(explicitReferences);
3268
- const sentinel = extractMcpToolSearchSentinel(content);
3269
- if (sentinel) return selectDeferredToolsByNames(sentinel.names, originalTools).map((tool) => tool.name);
3270
- return [];
3271
- };
3272
- const extractToolReferenceNames = (content) => {
3273
- if (!Array.isArray(content)) return [];
3274
- return content.flatMap((block) => block.type === "tool_reference" ? [block.tool_name] : []);
2993
+ const handleResponseCreated = (rawEvent, state) => {
2994
+ return messageStart(state, rawEvent.response);
3275
2995
  };
3276
- const extractMcpToolSearchSentinel = (content) => {
3277
- if (typeof content === "string") return parseMcpToolSearchSentinel(content);
3278
- for (const block of content) {
3279
- if (block.type !== "text") continue;
3280
- const sentinel = parseMcpToolSearchSentinel(block.text);
3281
- if (sentinel) return sentinel;
2996
+ const handleOutputItemAdded$1 = (rawEvent, state) => {
2997
+ const events = new Array();
2998
+ const functionCallDetails = extractFunctionCallDetails(rawEvent, state);
2999
+ if (!functionCallDetails) return events;
3000
+ const { outputIndex, toolCallId, name, initialArguments } = functionCallDetails;
3001
+ const blockIndex = openFunctionCallBlock(state, {
3002
+ outputIndex,
3003
+ toolCallId,
3004
+ name,
3005
+ events
3006
+ });
3007
+ if (initialArguments !== void 0 && initialArguments.length > 0) {
3008
+ events.push({
3009
+ type: "content_block_delta",
3010
+ index: blockIndex,
3011
+ delta: {
3012
+ type: "input_json_delta",
3013
+ partial_json: initialArguments
3014
+ }
3015
+ });
3016
+ state.blockHasDelta.add(blockIndex);
3282
3017
  }
3283
- return null;
3284
- };
3285
- const resolveDeferredTool = (toolName, originalTools) => {
3286
- const tool = originalTools.find((candidate) => candidate.name === toolName);
3287
- if (tool && isDeferredToolName(tool.name)) return tool;
3288
- throw createInvalidRequestError(`Tool reference '${toolName}' has no corresponding deferred tool definition`);
3289
- };
3290
- const uniqueToolNames = (toolNames) => [...new Set(toolNames)];
3291
- const createInvalidRequestError = (message) => new HTTPError(message, new Response(JSON.stringify({ error: {
3292
- message,
3293
- type: "invalid_request_error"
3294
- } }), {
3295
- status: 400,
3296
- headers: { "content-type": "application/json" }
3297
- }));
3298
- const translateSystemPrompt = (system, model) => {
3299
- if (!system) return null;
3300
- const extraPrompt = getExtraPromptForModel(model);
3301
- if (typeof system === "string") return system + extraPrompt;
3302
- const text = system.map((block, index) => {
3303
- if (index === 0) return block.text + "\n\n" + extraPrompt + "\n\n";
3304
- return block.text;
3305
- }).join(" ");
3306
- return text.length > 0 ? text : null;
3018
+ return events;
3307
3019
  };
3308
- const convertAnthropicTools = (tools, toolSearchEnabled) => {
3309
- if (!tools || tools.length === 0) return null;
3310
- const converted = [];
3311
- let addedToolSearch = false;
3312
- const searchableToolNames = toolSearchEnabled ? listDeferredToolNames(tools) : [];
3313
- for (const tool of tools) {
3314
- if (isBridgeToolSearchName(tool.name)) {
3315
- if (toolSearchEnabled && !addedToolSearch) {
3316
- converted.push(createResponsesToolSearchDefinition(searchableToolNames));
3317
- addedToolSearch = true;
3318
- }
3319
- continue;
3320
- }
3321
- if (toolSearchEnabled && isDeferredToolName(tool.name)) {
3322
- converted.push(convertDeferredToolToNamespace(tool));
3323
- continue;
3020
+ const handleOutputItemDone$1 = (rawEvent, state) => {
3021
+ const events = new Array();
3022
+ const item = rawEvent.item;
3023
+ const itemType = item.type;
3024
+ const outputIndex = rawEvent.output_index;
3025
+ if (itemType === "tool_search_call") {
3026
+ const blockIndex = openFunctionCallBlock(state, {
3027
+ outputIndex,
3028
+ toolCallId: item.call_id,
3029
+ name: state.toolSearchName,
3030
+ events
3031
+ });
3032
+ const finalArguments = stringifyToolSearchArguments(item.arguments);
3033
+ if (!state.blockHasDelta.has(blockIndex) && finalArguments) {
3034
+ events.push({
3035
+ type: "content_block_delta",
3036
+ index: blockIndex,
3037
+ delta: {
3038
+ type: "input_json_delta",
3039
+ partial_json: finalArguments
3040
+ }
3041
+ });
3042
+ state.blockHasDelta.add(blockIndex);
3324
3043
  }
3325
- converted.push(convertToolToFunction(tool));
3044
+ state.functionCallStateByOutputIndex.delete(outputIndex);
3045
+ return events;
3326
3046
  }
3327
- return converted;
3328
- };
3329
- const createResponsesToolSearchDefinition = (searchableToolNames) => ({
3330
- type: "tool_search",
3331
- execution: "client",
3332
- description: "Load deferred tools by exact name before using them. Return only the searchable tool names you need for the next step.",
3333
- parameters: {
3334
- type: "object",
3335
- properties: { names: {
3336
- type: "array",
3337
- description: "Exact deferred tool names to load.",
3338
- items: {
3339
- type: "string",
3340
- enum: searchableToolNames
3341
- },
3342
- minItems: 1
3343
- } },
3344
- required: ["names"],
3345
- additionalProperties: false
3047
+ if (itemType === "compaction") {
3048
+ if (!item.id || !item.encrypted_content) return events;
3049
+ const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3050
+ if (!state.blockHasDelta.has(blockIndex)) events.push({
3051
+ type: "content_block_delta",
3052
+ index: blockIndex,
3053
+ delta: {
3054
+ type: "thinking_delta",
3055
+ thinking: THINKING_TEXT
3056
+ }
3057
+ });
3058
+ events.push({
3059
+ type: "content_block_delta",
3060
+ index: blockIndex,
3061
+ delta: {
3062
+ type: "signature_delta",
3063
+ signature: encodeCompactionCarrierSignature({
3064
+ id: item.id,
3065
+ encrypted_content: item.encrypted_content
3066
+ })
3067
+ }
3068
+ });
3069
+ state.blockHasDelta.add(blockIndex);
3070
+ return events;
3346
3071
  }
3347
- });
3348
- const convertToolToFunction = (tool) => ({
3349
- type: "function",
3350
- name: tool.name,
3351
- parameters: normalizeToolSchema(tool.input_schema),
3352
- strict: false,
3353
- ...tool.description ? { description: tool.description } : {}
3354
- });
3355
- const convertDeferredToolToNamespace = (tool) => ({
3356
- type: "namespace",
3357
- name: tool.name,
3358
- ...tool.description ? { description: tool.description } : {},
3359
- tools: [{
3360
- type: "function",
3361
- name: tool.name,
3362
- parameters: normalizeToolSchema(tool.input_schema),
3363
- strict: false,
3364
- defer_loading: true,
3365
- ...tool.description ? { description: tool.description } : {}
3366
- }]
3367
- });
3368
- const convertAnthropicToolChoice = (choice, toolSearchEnabled) => {
3369
- if (!choice) return "auto";
3370
- switch (choice.type) {
3371
- case "auto": return "auto";
3372
- case "any": return "required";
3373
- case "tool":
3374
- if (toolSearchEnabled && choice.name && isBridgeToolSearchName(choice.name)) return "auto";
3375
- return choice.name ? {
3376
- type: "function",
3377
- name: choice.name
3378
- } : "auto";
3379
- case "none": return "none";
3380
- default: return "auto";
3072
+ if (itemType !== "reasoning") return events;
3073
+ const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3074
+ const signature = (item.encrypted_content ?? "") + "@" + item.id;
3075
+ if (signature) {
3076
+ if (!item.summary || item.summary.length === 0) events.push({
3077
+ type: "content_block_delta",
3078
+ index: blockIndex,
3079
+ delta: {
3080
+ type: "thinking_delta",
3081
+ thinking: THINKING_TEXT
3082
+ }
3083
+ });
3084
+ events.push({
3085
+ type: "content_block_delta",
3086
+ index: blockIndex,
3087
+ delta: {
3088
+ type: "signature_delta",
3089
+ signature
3090
+ }
3091
+ });
3092
+ state.blockHasDelta.add(blockIndex);
3381
3093
  }
3094
+ return events;
3382
3095
  };
3383
- const translateResponsesResultToAnthropic = (response, options) => {
3384
- const contentBlocks = mapOutputToAnthropicContent(response.output, options);
3385
- const usage = mapResponsesUsage(response);
3386
- let anthropicContent = fallbackContentBlocks(response.output_text);
3387
- if (contentBlocks.length > 0) anthropicContent = contentBlocks;
3388
- const stopReason = mapResponsesStopReason(response);
3389
- return {
3390
- id: response.id,
3391
- type: "message",
3392
- role: "assistant",
3393
- content: anthropicContent,
3394
- model: response.model,
3395
- stop_reason: stopReason,
3396
- stop_sequence: null,
3397
- usage
3398
- };
3096
+ const handleFunctionCallArgumentsDelta = (rawEvent, state) => {
3097
+ const events = new Array();
3098
+ const outputIndex = rawEvent.output_index;
3099
+ const deltaText = rawEvent.delta;
3100
+ if (!deltaText) return events;
3101
+ const blockIndex = openFunctionCallBlock(state, {
3102
+ outputIndex,
3103
+ events
3104
+ });
3105
+ const functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
3106
+ if (!functionCallState) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta without an open tool call block."), state, events);
3107
+ const { nextCount, exceeded } = updateWhitespaceRunState(functionCallState.consecutiveWhitespaceCount, deltaText);
3108
+ if (exceeded) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta containing more than 20 consecutive whitespace characters."), state, events);
3109
+ functionCallState.consecutiveWhitespaceCount = nextCount;
3110
+ events.push({
3111
+ type: "content_block_delta",
3112
+ index: blockIndex,
3113
+ delta: {
3114
+ type: "input_json_delta",
3115
+ partial_json: deltaText
3116
+ }
3117
+ });
3118
+ state.blockHasDelta.add(blockIndex);
3119
+ return events;
3399
3120
  };
3400
- const mapOutputToAnthropicContent = (output, options) => {
3401
- const contentBlocks = [];
3402
- for (const item of output) switch (item.type) {
3403
- case "reasoning": {
3404
- const thinkingText = extractReasoningText(item);
3405
- if (thinkingText.length > 0) contentBlocks.push({
3406
- type: "thinking",
3407
- thinking: thinkingText,
3408
- signature: (item.encrypted_content ?? "") + "@" + item.id
3409
- });
3410
- break;
3121
+ const handleFunctionCallArgumentsDone = (rawEvent, state) => {
3122
+ const events = new Array();
3123
+ const outputIndex = rawEvent.output_index;
3124
+ const blockIndex = openFunctionCallBlock(state, {
3125
+ outputIndex,
3126
+ events
3127
+ });
3128
+ const finalArguments = typeof rawEvent.arguments === "string" ? rawEvent.arguments : void 0;
3129
+ if (!state.blockHasDelta.has(blockIndex) && finalArguments) {
3130
+ events.push({
3131
+ type: "content_block_delta",
3132
+ index: blockIndex,
3133
+ delta: {
3134
+ type: "input_json_delta",
3135
+ partial_json: finalArguments
3136
+ }
3137
+ });
3138
+ state.blockHasDelta.add(blockIndex);
3139
+ }
3140
+ state.functionCallStateByOutputIndex.delete(outputIndex);
3141
+ return events;
3142
+ };
3143
+ const handleOutputTextDelta = (rawEvent, state) => {
3144
+ const events = new Array();
3145
+ const outputIndex = rawEvent.output_index;
3146
+ const contentIndex = rawEvent.content_index;
3147
+ const deltaText = rawEvent.delta;
3148
+ if (!deltaText) return events;
3149
+ const blockIndex = openTextBlockIfNeeded(state, {
3150
+ outputIndex,
3151
+ contentIndex,
3152
+ events
3153
+ });
3154
+ events.push({
3155
+ type: "content_block_delta",
3156
+ index: blockIndex,
3157
+ delta: {
3158
+ type: "text_delta",
3159
+ text: deltaText
3411
3160
  }
3412
- case "function_call": {
3413
- const toolUseBlock = createToolUseContentBlock(item);
3414
- if (toolUseBlock) contentBlocks.push(toolUseBlock);
3415
- break;
3161
+ });
3162
+ state.blockHasDelta.add(blockIndex);
3163
+ return events;
3164
+ };
3165
+ const handleReasoningSummaryTextDelta = (rawEvent, state) => {
3166
+ const outputIndex = rawEvent.output_index;
3167
+ const deltaText = rawEvent.delta;
3168
+ const events = new Array();
3169
+ const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3170
+ events.push({
3171
+ type: "content_block_delta",
3172
+ index: blockIndex,
3173
+ delta: {
3174
+ type: "thinking_delta",
3175
+ thinking: deltaText
3416
3176
  }
3417
- case "tool_search_call": {
3418
- const toolUseBlock = createToolSearchUseContentBlock(item, options?.toolSearchName);
3419
- if (toolUseBlock) contentBlocks.push(toolUseBlock);
3420
- break;
3177
+ });
3178
+ state.blockHasDelta.add(blockIndex);
3179
+ return events;
3180
+ };
3181
+ const handleReasoningSummaryTextDone = (rawEvent, state) => {
3182
+ const outputIndex = rawEvent.output_index;
3183
+ const text = rawEvent.text;
3184
+ const events = new Array();
3185
+ const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3186
+ if (text && !state.blockHasDelta.has(blockIndex)) events.push({
3187
+ type: "content_block_delta",
3188
+ index: blockIndex,
3189
+ delta: {
3190
+ type: "thinking_delta",
3191
+ thinking: text
3421
3192
  }
3422
- case "tool_search_output": break;
3423
- case "message": {
3424
- const combinedText = combineMessageTextContent(item.content);
3425
- if (combinedText.length > 0) contentBlocks.push({
3426
- type: "text",
3427
- text: combinedText
3428
- });
3429
- break;
3193
+ });
3194
+ return events;
3195
+ };
3196
+ const handleOutputTextDone = (rawEvent, state) => {
3197
+ const events = new Array();
3198
+ const outputIndex = rawEvent.output_index;
3199
+ const contentIndex = rawEvent.content_index;
3200
+ const text = rawEvent.text;
3201
+ const blockIndex = openTextBlockIfNeeded(state, {
3202
+ outputIndex,
3203
+ contentIndex,
3204
+ events
3205
+ });
3206
+ if (text && !state.blockHasDelta.has(blockIndex)) events.push({
3207
+ type: "content_block_delta",
3208
+ index: blockIndex,
3209
+ delta: {
3210
+ type: "text_delta",
3211
+ text
3430
3212
  }
3431
- case "compaction": {
3432
- const compactionBlock = createCompactionThinkingBlock(item);
3433
- if (compactionBlock) contentBlocks.push(compactionBlock);
3434
- break;
3213
+ });
3214
+ return events;
3215
+ };
3216
+ const handleResponseCompleted = (rawEvent, state) => {
3217
+ const response = rawEvent.response;
3218
+ const events = new Array();
3219
+ closeAllOpenBlocks(state, events);
3220
+ const anthropic = translateResponsesResultToAnthropic(response, {
3221
+ hasToolCall: state.hasToolCall,
3222
+ toolSearchName: state.toolSearchName
3223
+ });
3224
+ events.push({
3225
+ type: "message_delta",
3226
+ delta: {
3227
+ stop_reason: anthropic.stop_reason,
3228
+ stop_sequence: anthropic.stop_sequence
3229
+ },
3230
+ usage: anthropic.usage
3231
+ }, { type: "message_stop" });
3232
+ state.messageCompleted = true;
3233
+ return events;
3234
+ };
3235
+ const handleResponseFailed = (rawEvent, state) => {
3236
+ const response = rawEvent.response;
3237
+ const events = new Array();
3238
+ closeAllOpenBlocks(state, events);
3239
+ const message = response.error?.message ?? "The response failed due to an unknown error.";
3240
+ events.push(buildErrorEvent(message));
3241
+ state.messageCompleted = true;
3242
+ return events;
3243
+ };
3244
+ const handleErrorEvent = (rawEvent, state) => {
3245
+ const message = typeof rawEvent.message === "string" ? rawEvent.message : "An unexpected error occurred during streaming.";
3246
+ state.messageCompleted = true;
3247
+ return [buildErrorEvent(message)];
3248
+ };
3249
+ const handleFunctionCallArgumentsValidationError = (error, state, events = []) => {
3250
+ const reason = error.message;
3251
+ closeAllOpenBlocks(state, events);
3252
+ state.messageCompleted = true;
3253
+ events.push(buildErrorEvent(reason));
3254
+ return events;
3255
+ };
3256
+ const messageStart = (state, response) => {
3257
+ state.messageStartSent = true;
3258
+ const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
3259
+ const inputTokens = (response.usage?.input_tokens ?? 0) - (inputCachedTokens ?? 0);
3260
+ return [{
3261
+ type: "message_start",
3262
+ message: {
3263
+ id: response.id,
3264
+ type: "message",
3265
+ role: "assistant",
3266
+ content: [],
3267
+ model: response.model,
3268
+ stop_reason: null,
3269
+ stop_sequence: null,
3270
+ usage: {
3271
+ input_tokens: inputTokens,
3272
+ output_tokens: 0,
3273
+ cache_read_input_tokens: inputCachedTokens ?? 0
3274
+ }
3435
3275
  }
3436
- default: {
3437
- const combinedText = combineMessageTextContent(item.content);
3438
- if (combinedText.length > 0) contentBlocks.push({
3276
+ }];
3277
+ };
3278
+ const openTextBlockIfNeeded = (state, params) => {
3279
+ const { outputIndex, contentIndex, events } = params;
3280
+ const key = getBlockKey(outputIndex, contentIndex);
3281
+ let blockIndex = state.blockIndexByKey.get(key);
3282
+ if (blockIndex === void 0) {
3283
+ blockIndex = state.nextContentBlockIndex;
3284
+ state.nextContentBlockIndex += 1;
3285
+ state.blockIndexByKey.set(key, blockIndex);
3286
+ }
3287
+ if (!state.openBlocks.has(blockIndex)) {
3288
+ closeOpenBlocks(state, events);
3289
+ events.push({
3290
+ type: "content_block_start",
3291
+ index: blockIndex,
3292
+ content_block: {
3439
3293
  type: "text",
3440
- text: combinedText
3441
- });
3442
- }
3294
+ text: ""
3295
+ }
3296
+ });
3297
+ state.openBlocks.add(blockIndex);
3443
3298
  }
3444
- return contentBlocks;
3299
+ return blockIndex;
3445
3300
  };
3446
- const combineMessageTextContent = (content) => {
3447
- if (!Array.isArray(content)) return "";
3448
- let aggregated = "";
3449
- for (const block of content) {
3450
- if (isResponseOutputText(block)) {
3451
- aggregated += block.text;
3452
- continue;
3453
- }
3454
- if (isResponseOutputRefusal(block)) {
3455
- aggregated += block.refusal;
3456
- continue;
3457
- }
3458
- if (typeof block.text === "string") {
3459
- aggregated += block.text;
3460
- continue;
3461
- }
3462
- if (typeof block.reasoning === "string") {
3463
- aggregated += block.reasoning;
3464
- continue;
3465
- }
3301
+ const openThinkingBlockIfNeeded = (state, outputIndex, events) => {
3302
+ const key = getBlockKey(outputIndex, 0);
3303
+ let blockIndex = state.blockIndexByKey.get(key);
3304
+ if (blockIndex === void 0) {
3305
+ blockIndex = state.nextContentBlockIndex;
3306
+ state.nextContentBlockIndex += 1;
3307
+ state.blockIndexByKey.set(key, blockIndex);
3466
3308
  }
3467
- return aggregated;
3309
+ if (!state.openBlocks.has(blockIndex)) {
3310
+ closeOpenBlocks(state, events);
3311
+ events.push({
3312
+ type: "content_block_start",
3313
+ index: blockIndex,
3314
+ content_block: {
3315
+ type: "thinking",
3316
+ thinking: ""
3317
+ }
3318
+ });
3319
+ state.openBlocks.add(blockIndex);
3320
+ }
3321
+ return blockIndex;
3468
3322
  };
3469
- const extractReasoningText = (item) => {
3470
- const segments = [];
3471
- const collectFromBlocks = (blocks) => {
3472
- if (!Array.isArray(blocks)) return;
3473
- for (const block of blocks) if (typeof block.text === "string") {
3474
- segments.push(block.text);
3475
- continue;
3476
- }
3477
- };
3478
- if (!item.summary || item.summary.length === 0) return THINKING_TEXT;
3479
- collectFromBlocks(item.summary);
3480
- return segments.join("").trim();
3323
+ const closeBlockIfOpen = (state, blockIndex, events) => {
3324
+ if (!state.openBlocks.has(blockIndex)) return;
3325
+ events.push({
3326
+ type: "content_block_stop",
3327
+ index: blockIndex
3328
+ });
3329
+ state.openBlocks.delete(blockIndex);
3330
+ state.blockHasDelta.delete(blockIndex);
3481
3331
  };
3482
- const createToolUseContentBlock = (call) => {
3483
- const toolId = call.call_id;
3484
- const toolName = resolveToolUseName(call);
3485
- if (!toolName || !toolId) return null;
3486
- return {
3487
- type: "tool_use",
3488
- id: toolId,
3489
- name: toolName,
3490
- input: parseFunctionCallArguments(call.arguments)
3491
- };
3332
+ const closeOpenBlocks = (state, events) => {
3333
+ for (const blockIndex of state.openBlocks) closeBlockIfOpen(state, blockIndex, events);
3492
3334
  };
3493
- const createToolSearchUseContentBlock = (call, toolSearchName = BRIDGE_TOOL_SEARCH_NAME) => {
3494
- const toolId = call.call_id;
3495
- if (!toolId) return null;
3496
- return {
3497
- type: "tool_use",
3498
- id: toolId,
3499
- name: toolSearchName,
3500
- input: parseToolSearchArguments(call.arguments)
3335
+ const closeAllOpenBlocks = (state, events) => {
3336
+ closeOpenBlocks(state, events);
3337
+ state.functionCallStateByOutputIndex.clear();
3338
+ };
3339
+ const buildErrorEvent = (message) => ({
3340
+ type: "error",
3341
+ error: {
3342
+ type: "api_error",
3343
+ message
3344
+ }
3345
+ });
3346
+ const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
3347
+ const openFunctionCallBlock = (state, params) => {
3348
+ const { outputIndex, toolCallId, name, events } = params;
3349
+ state.hasToolCall = true;
3350
+ let functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
3351
+ if (!functionCallState) {
3352
+ const blockIndex = state.nextContentBlockIndex;
3353
+ state.nextContentBlockIndex += 1;
3354
+ functionCallState = {
3355
+ blockIndex,
3356
+ toolCallId: toolCallId ?? `tool_call_${blockIndex}`,
3357
+ name: name ?? "function",
3358
+ consecutiveWhitespaceCount: 0
3359
+ };
3360
+ state.functionCallStateByOutputIndex.set(outputIndex, functionCallState);
3361
+ }
3362
+ const { blockIndex } = functionCallState;
3363
+ if (!state.openBlocks.has(blockIndex)) {
3364
+ closeOpenBlocks(state, events);
3365
+ events.push({
3366
+ type: "content_block_start",
3367
+ index: blockIndex,
3368
+ content_block: {
3369
+ type: "tool_use",
3370
+ id: functionCallState.toolCallId,
3371
+ name: functionCallState.name,
3372
+ input: {}
3373
+ }
3374
+ });
3375
+ state.openBlocks.add(blockIndex);
3376
+ }
3377
+ return blockIndex;
3378
+ };
3379
+ const extractFunctionCallDetails = (rawEvent, state) => {
3380
+ const item = rawEvent.item;
3381
+ const itemType = item.type;
3382
+ if (itemType === "tool_search_call") return {
3383
+ outputIndex: rawEvent.output_index,
3384
+ toolCallId: item.call_id,
3385
+ name: state.toolSearchName,
3386
+ initialArguments: ""
3501
3387
  };
3502
- };
3503
- const resolveToolUseName = (call) => {
3504
- if (typeof call.namespace === "string" && call.namespace.length > 0) return call.namespace;
3505
- return call.name;
3506
- };
3507
- const createCompactionThinkingBlock = (item) => {
3508
- if (!item.id || !item.encrypted_content) return null;
3388
+ if (itemType !== "function_call") return;
3509
3389
  return {
3510
- type: "thinking",
3511
- thinking: THINKING_TEXT,
3512
- signature: encodeCompactionCarrierSignature({
3513
- id: item.id,
3514
- encrypted_content: item.encrypted_content
3515
- })
3390
+ outputIndex: rawEvent.output_index,
3391
+ toolCallId: item.call_id,
3392
+ name: resolveToolUseName(item),
3393
+ initialArguments: item.arguments
3516
3394
  };
3517
3395
  };
3518
- const parseFunctionCallArguments = (rawArguments) => {
3519
- if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
3396
+ const stringifyToolSearchArguments = (argumentsValue) => {
3520
3397
  try {
3521
- const parsed = JSON.parse(rawArguments);
3522
- if (Array.isArray(parsed)) return { arguments: parsed };
3523
- if (parsed && typeof parsed === "object") return parsed;
3524
- } catch (error) {
3525
- consola.warn("Failed to parse function call arguments", {
3526
- error,
3527
- rawArguments
3528
- });
3398
+ return JSON.stringify(formatToolSearchBridgeArguments(argumentsValue));
3399
+ } catch {
3400
+ return;
3529
3401
  }
3530
- return { raw_arguments: rawArguments };
3531
3402
  };
3532
- const parseToolSearchArguments = (argumentsValue) => {
3533
- return formatToolSearchBridgeArguments(argumentsValue);
3403
+ const responsesUtilsDependencies = {
3404
+ isResponsesApiContextManagementModel,
3405
+ isResponsesApiWebSocketEnabled
3534
3406
  };
3535
- const fallbackContentBlocks = (outputText) => {
3536
- if (!outputText) return [];
3537
- return [{
3538
- type: "text",
3539
- text: outputText
3540
- }];
3407
+ const getResponsesRequestOptions = (payload) => {
3408
+ return {
3409
+ vision: hasVisionInput(payload),
3410
+ initiator: hasAgentInitiator(payload) ? "agent" : "user"
3411
+ };
3541
3412
  };
3542
- const mapResponsesStopReason = (response) => {
3543
- const { status, incomplete_details: incompleteDetails } = response;
3544
- if (status === "completed") {
3545
- if (response.output.some((item) => item.type === "function_call" || item.type === "tool_search_call")) return "tool_use";
3546
- return "end_turn";
3547
- }
3548
- if (status === "incomplete") {
3549
- if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
3550
- if (incompleteDetails?.reason === "content_filter") return "end_turn";
3551
- }
3413
+ const getResponsesTransportForModel = (selectedModel, options = {}) => {
3414
+ const supportedEndpoints = selectedModel?.supported_endpoints ?? [];
3415
+ const useWebSocket = responsesUtilsDependencies.isResponsesApiWebSocketEnabled();
3416
+ if (options.compactType !== 1 && useWebSocket && supportedEndpoints.includes("ws:/responses")) return "websocket";
3417
+ if (supportedEndpoints.includes("/responses")) return "http";
3552
3418
  return null;
3553
3419
  };
3554
- const mapResponsesUsage = (response) => {
3555
- const inputTokens = response.usage?.input_tokens ?? 0;
3556
- const outputTokens = response.usage?.output_tokens ?? 0;
3557
- return {
3558
- input_tokens: inputTokens - (response.usage?.input_tokens_details?.cached_tokens ?? 0),
3559
- output_tokens: outputTokens,
3560
- ...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
3561
- };
3420
+ const hasAgentInitiator = (payload) => {
3421
+ const lastItem = getPayloadItems(payload).at(-1);
3422
+ if (!lastItem) return false;
3423
+ if (!("role" in lastItem) || !lastItem.role) return true;
3424
+ return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
3562
3425
  };
3563
- const isRecord = (value) => typeof value === "object" && value !== null;
3564
- const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
3565
- const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
3566
- const convertToolResultContent = (content) => {
3567
- if (typeof content === "string") return content;
3568
- if (Array.isArray(content)) {
3569
- const result = [];
3570
- for (const block of content) switch (block.type) {
3571
- case "text":
3572
- result.push(createTextContent(block.text));
3573
- break;
3574
- case "image":
3575
- result.push(createImageContent(block));
3576
- break;
3577
- case "document":
3578
- result.push(createFileContent(block));
3579
- break;
3580
- case "tool_reference":
3581
- result.push(createTextContent(`Tool ${block.tool_name} loaded`));
3582
- break;
3583
- default: break;
3584
- }
3585
- return result;
3586
- }
3587
- return "";
3426
+ const hasVisionInput = (payload) => {
3427
+ return getPayloadItems(payload).some((item) => containsVisionContent(item));
3588
3428
  };
3589
- //#endregion
3590
- //#region src/routes/messages/responses-stream-translation.ts
3591
- const MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE = 20;
3592
- var FunctionCallArgumentsValidationError = class extends Error {
3593
- constructor(message) {
3594
- super(message);
3595
- this.name = "FunctionCallArgumentsValidationError";
3596
- }
3429
+ const resolveResponsesCompactThreshold = (maxPromptTokens) => {
3430
+ if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
3431
+ return 5e4;
3597
3432
  };
3598
- const updateWhitespaceRunState = (previousCount, chunk) => {
3599
- let count = previousCount;
3600
- for (const char of chunk) {
3601
- if (char === "\r" || char === "\n" || char === " ") {
3602
- count += 1;
3603
- if (count > MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE) return {
3604
- nextCount: count,
3605
- exceeded: true
3606
- };
3607
- continue;
3608
- }
3609
- if (char !== " ") count = 0;
3433
+ const createCompactionContextManagement = (compactThreshold) => [{
3434
+ type: "compaction",
3435
+ compact_threshold: compactThreshold
3436
+ }];
3437
+ const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
3438
+ if (payload.context_management !== void 0) return;
3439
+ if (!responsesUtilsDependencies.isResponsesApiContextManagementModel(payload.model)) return;
3440
+ payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
3441
+ };
3442
+ const compactInputByLatestCompaction = (payload) => {
3443
+ if (!Array.isArray(payload.input) || payload.input.length === 0) return;
3444
+ const latestCompactionMessageIndex = getLatestCompactionMessageIndex(payload.input);
3445
+ if (latestCompactionMessageIndex === void 0) return;
3446
+ payload.input = payload.input.slice(latestCompactionMessageIndex);
3447
+ };
3448
+ const getLatestCompactionMessageIndex = (input) => {
3449
+ for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
3450
+ };
3451
+ const isCompactionInputItem = (value) => {
3452
+ return "type" in value && typeof value.type === "string" && value.type === "compaction";
3453
+ };
3454
+ const getPayloadItems = (payload) => {
3455
+ const result = [];
3456
+ const { input } = payload;
3457
+ if (Array.isArray(input)) result.push(...input);
3458
+ return result;
3459
+ };
3460
+ const containsVisionContent = (value) => {
3461
+ if (!value) return false;
3462
+ if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
3463
+ if (typeof value !== "object") return false;
3464
+ const record = value;
3465
+ if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
3466
+ if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
3467
+ return false;
3468
+ };
3469
+ //#endregion
3470
+ //#region src/services/codex/get-models.ts
3471
+ const CODEX_MODELS = [
3472
+ {
3473
+ contextWindow: 272e3,
3474
+ id: "gpt-5.3-codex-spark",
3475
+ input: ["text"],
3476
+ maxTokens: 128e3,
3477
+ name: "GPT-5.3 Codex Spark"
3478
+ },
3479
+ {
3480
+ contextWindow: 1e6,
3481
+ id: "gpt-5.4",
3482
+ input: ["text", "image"],
3483
+ maxTokens: 128e3,
3484
+ name: "GPT-5.4"
3485
+ },
3486
+ {
3487
+ contextWindow: 1e6,
3488
+ id: "gpt-5.4-mini",
3489
+ input: ["text", "image"],
3490
+ maxTokens: 128e3,
3491
+ name: "GPT-5.4 mini"
3492
+ },
3493
+ {
3494
+ contextWindow: 272e3,
3495
+ id: "gpt-5.5",
3496
+ input: ["text", "image"],
3497
+ maxTokens: 128e3,
3498
+ name: "GPT-5.5"
3610
3499
  }
3500
+ ];
3501
+ function normalizeCodexModel(model) {
3502
+ const supportsVision = model.input.includes("image");
3611
3503
  return {
3612
- nextCount: count,
3613
- exceeded: false
3504
+ capabilities: {
3505
+ family: "gpt",
3506
+ limits: {
3507
+ max_context_window_tokens: model.contextWindow,
3508
+ max_output_tokens: model.maxTokens,
3509
+ max_prompt_tokens: model.contextWindow
3510
+ },
3511
+ object: "model_capabilities",
3512
+ supports: {
3513
+ adaptive_thinking: true,
3514
+ parallel_tool_calls: true,
3515
+ reasoning_effort: [
3516
+ "minimal",
3517
+ "low",
3518
+ "medium",
3519
+ "high",
3520
+ "xhigh"
3521
+ ],
3522
+ streaming: true,
3523
+ tool_calls: true,
3524
+ vision: supportsVision
3525
+ },
3526
+ tokenizer: "o200k_base",
3527
+ type: "chat"
3528
+ },
3529
+ id: model.id,
3530
+ model_picker_enabled: true,
3531
+ name: model.name,
3532
+ object: "model",
3533
+ preview: false,
3534
+ supported_endpoints: ["/v1/messages", "/v1/responses"],
3535
+ vendor: "openai",
3536
+ version: "chatgpt-codex"
3614
3537
  };
3615
- };
3616
- const createResponsesStreamState = (options) => ({
3617
- messageStartSent: false,
3618
- messageCompleted: false,
3619
- nextContentBlockIndex: 0,
3620
- blockIndexByKey: /* @__PURE__ */ new Map(),
3621
- openBlocks: /* @__PURE__ */ new Set(),
3622
- blockHasDelta: /* @__PURE__ */ new Set(),
3623
- functionCallStateByOutputIndex: /* @__PURE__ */ new Map(),
3624
- toolSearchName: options?.toolSearchName ?? "mcp__tool_search__search"
3625
- });
3626
- const translateResponsesStreamEvent = (rawEvent, state) => {
3627
- switch (rawEvent.type) {
3628
- case "response.created": return handleResponseCreated(rawEvent, state);
3629
- case "response.output_item.added": return handleOutputItemAdded$1(rawEvent, state);
3630
- case "response.reasoning_summary_text.delta": return handleReasoningSummaryTextDelta(rawEvent, state);
3631
- case "response.output_text.delta": return handleOutputTextDelta(rawEvent, state);
3632
- case "response.reasoning_summary_text.done": return handleReasoningSummaryTextDone(rawEvent, state);
3633
- case "response.output_text.done": return handleOutputTextDone(rawEvent, state);
3634
- case "response.output_item.done": return handleOutputItemDone$1(rawEvent, state);
3635
- case "response.function_call_arguments.delta": return handleFunctionCallArgumentsDelta(rawEvent, state);
3636
- case "response.function_call_arguments.done": return handleFunctionCallArgumentsDone(rawEvent, state);
3637
- case "response.completed":
3638
- case "response.incomplete": return handleResponseCompleted(rawEvent, state);
3639
- case "response.failed": return handleResponseFailed(rawEvent, state);
3640
- case "error": return handleErrorEvent(rawEvent, state);
3641
- default: return [];
3538
+ }
3539
+ function getModels() {
3540
+ return {
3541
+ object: "list",
3542
+ data: CODEX_MODELS.map((model) => normalizeCodexModel(model))
3543
+ };
3544
+ }
3545
+ //#endregion
3546
+ //#region src/services/providers/provider-proxy.ts
3547
+ const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
3548
+ const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
3549
+ const STRIPPED_RESPONSE_HEADERS = [
3550
+ "connection",
3551
+ "content-encoding",
3552
+ "content-length",
3553
+ "keep-alive",
3554
+ "proxy-authenticate",
3555
+ "proxy-authorization",
3556
+ "te",
3557
+ "trailer",
3558
+ "transfer-encoding",
3559
+ "upgrade"
3560
+ ];
3561
+ function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
3562
+ const authHeaders = {};
3563
+ if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
3564
+ else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
3565
+ const headers = {
3566
+ "content-type": "application/json",
3567
+ accept: "application/json",
3568
+ ...authHeaders
3569
+ };
3570
+ for (const headerName of SHARED_FORWARDABLE_HEADERS) {
3571
+ const headerValue = requestHeaders.get(headerName);
3572
+ if (headerValue) headers[headerName] = headerValue;
3642
3573
  }
3643
- };
3644
- const handleResponseCreated = (rawEvent, state) => {
3645
- return messageStart(state, rawEvent.response);
3646
- };
3647
- const handleOutputItemAdded$1 = (rawEvent, state) => {
3648
- const events = new Array();
3649
- const functionCallDetails = extractFunctionCallDetails(rawEvent, state);
3650
- if (!functionCallDetails) return events;
3651
- const { outputIndex, toolCallId, name, initialArguments } = functionCallDetails;
3652
- const blockIndex = openFunctionCallBlock(state, {
3653
- outputIndex,
3654
- toolCallId,
3655
- name,
3656
- events
3574
+ if (providerConfig.type !== "anthropic") return headers;
3575
+ for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
3576
+ const headerValue = requestHeaders.get(headerName);
3577
+ if (headerValue) headers[headerName] = headerValue;
3578
+ }
3579
+ return headers;
3580
+ }
3581
+ function createProviderProxyResponse(upstreamResponse, body) {
3582
+ const headers = new Headers(upstreamResponse.headers);
3583
+ for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
3584
+ return new Response(body ?? upstreamResponse.body, {
3585
+ headers,
3586
+ status: upstreamResponse.status,
3587
+ statusText: upstreamResponse.statusText
3588
+ });
3589
+ }
3590
+ async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
3591
+ return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
3592
+ method: "POST",
3593
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
3594
+ body: JSON.stringify(payload)
3657
3595
  });
3658
- if (initialArguments !== void 0 && initialArguments.length > 0) {
3659
- events.push({
3660
- type: "content_block_delta",
3661
- index: blockIndex,
3662
- delta: {
3663
- type: "input_json_delta",
3664
- partial_json: initialArguments
3665
- }
3596
+ }
3597
+ async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
3598
+ return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
3599
+ method: "POST",
3600
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
3601
+ body: JSON.stringify(payload)
3602
+ });
3603
+ }
3604
+ async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
3605
+ return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
3606
+ method: "POST",
3607
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
3608
+ body: JSON.stringify(payload)
3609
+ });
3610
+ }
3611
+ async function forwardProviderModels(providerConfig, requestHeaders) {
3612
+ return await fetch(`${providerConfig.baseUrl}/v1/models`, {
3613
+ method: "GET",
3614
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
3615
+ });
3616
+ }
3617
+ //#endregion
3618
+ //#region src/routes/provider/messages/handler.ts
3619
+ const logger$4 = createHandlerLogger("provider-messages-handler");
3620
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
3621
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
3622
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
3623
+ "system",
3624
+ "user",
3625
+ "assistant",
3626
+ "tool"
3627
+ ]);
3628
+ async function handleProviderMessages(c) {
3629
+ const provider = c.req.param("provider");
3630
+ return await handleProviderMessagesForProvider(c, {
3631
+ payload: await c.req.json(),
3632
+ provider
3633
+ });
3634
+ }
3635
+ async function handleProviderMessagesForProvider(c, options) {
3636
+ const { payload, provider } = options;
3637
+ const providerConfig = await resolveProviderConfig(provider);
3638
+ if (!providerConfig) return c.json({ error: {
3639
+ message: `Provider '${provider}' not found or disabled`,
3640
+ type: "invalid_request_error"
3641
+ } }, 404);
3642
+ try {
3643
+ const modelConfig = providerConfig.models?.[payload.model];
3644
+ applyModelDefaults(payload, modelConfig);
3645
+ debugJson(logger$4, "provider.messages.request", {
3646
+ payload,
3647
+ provider
3666
3648
  });
3667
- state.blockHasDelta.add(blockIndex);
3668
- }
3669
- return events;
3670
- };
3671
- const handleOutputItemDone$1 = (rawEvent, state) => {
3672
- const events = new Array();
3673
- const item = rawEvent.item;
3674
- const itemType = item.type;
3675
- const outputIndex = rawEvent.output_index;
3676
- if (itemType === "tool_search_call") {
3677
- const blockIndex = openFunctionCallBlock(state, {
3678
- outputIndex,
3679
- toolCallId: item.call_id,
3680
- name: state.toolSearchName,
3681
- events
3649
+ if (providerConfig.type === "openai-responses") return await handleOpenAIResponsesProviderMessages(c, {
3650
+ modelConfig,
3651
+ payload,
3652
+ provider,
3653
+ providerConfig
3682
3654
  });
3683
- const finalArguments = stringifyToolSearchArguments(item.arguments);
3684
- if (!state.blockHasDelta.has(blockIndex) && finalArguments) {
3685
- events.push({
3686
- type: "content_block_delta",
3687
- index: blockIndex,
3688
- delta: {
3689
- type: "input_json_delta",
3690
- partial_json: finalArguments
3691
- }
3692
- });
3693
- state.blockHasDelta.add(blockIndex);
3694
- }
3695
- state.functionCallStateByOutputIndex.delete(outputIndex);
3696
- return events;
3697
- }
3698
- if (itemType === "compaction") {
3699
- if (!item.id || !item.encrypted_content) return events;
3700
- const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3701
- if (!state.blockHasDelta.has(blockIndex)) events.push({
3702
- type: "content_block_delta",
3703
- index: blockIndex,
3704
- delta: {
3705
- type: "thinking_delta",
3706
- thinking: THINKING_TEXT
3707
- }
3655
+ if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
3656
+ modelConfig,
3657
+ payload,
3658
+ provider,
3659
+ providerConfig
3708
3660
  });
3709
- events.push({
3710
- type: "content_block_delta",
3711
- index: blockIndex,
3712
- delta: {
3713
- type: "signature_delta",
3714
- signature: encodeCompactionCarrierSignature({
3715
- id: item.id,
3716
- encrypted_content: item.encrypted_content
3717
- })
3718
- }
3661
+ applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
3662
+ const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
3663
+ if (!upstreamResponse.ok) {
3664
+ logger$4.error("Failed to create responses", upstreamResponse);
3665
+ throw new HTTPError("Failed to create responses", upstreamResponse);
3666
+ }
3667
+ const contentType = upstreamResponse.headers.get("content-type") ?? "";
3668
+ if (Boolean(payload.stream) && contentType.includes("text/event-stream")) return streamProviderMessages({
3669
+ c,
3670
+ payload,
3671
+ provider,
3672
+ providerConfig,
3673
+ upstreamResponse
3719
3674
  });
3720
- state.blockHasDelta.add(blockIndex);
3721
- return events;
3722
- }
3723
- if (itemType !== "reasoning") return events;
3724
- const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3725
- const signature = (item.encrypted_content ?? "") + "@" + item.id;
3726
- if (signature) {
3727
- if (!item.summary || item.summary.length === 0) events.push({
3728
- type: "content_block_delta",
3729
- index: blockIndex,
3730
- delta: {
3731
- type: "thinking_delta",
3732
- thinking: THINKING_TEXT
3733
- }
3675
+ return respondProviderMessagesJson(c, {
3676
+ body: await upstreamResponse.json(),
3677
+ payload,
3678
+ provider,
3679
+ providerConfig
3734
3680
  });
3735
- events.push({
3736
- type: "content_block_delta",
3737
- index: blockIndex,
3738
- delta: {
3739
- type: "signature_delta",
3740
- signature
3741
- }
3681
+ } catch (error) {
3682
+ logger$4.error("provider.messages.error", {
3683
+ provider,
3684
+ error
3742
3685
  });
3743
- state.blockHasDelta.add(blockIndex);
3686
+ throw error;
3744
3687
  }
3745
- return events;
3746
- };
3747
- const handleFunctionCallArgumentsDelta = (rawEvent, state) => {
3748
- const events = new Array();
3749
- const outputIndex = rawEvent.output_index;
3750
- const deltaText = rawEvent.delta;
3751
- if (!deltaText) return events;
3752
- const blockIndex = openFunctionCallBlock(state, {
3753
- outputIndex,
3754
- events
3688
+ }
3689
+ const handleOpenAIResponsesProviderMessages = async (c, options) => {
3690
+ const { payload, provider, providerConfig } = options;
3691
+ const selectedModel = providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0;
3692
+ const responsesPayload = translateAnthropicMessagesToResponsesPayload(payload);
3693
+ applyResponsesApiContextManagement(responsesPayload, selectedModel?.capabilities.limits.max_prompt_tokens);
3694
+ compactInputByLatestCompaction(responsesPayload);
3695
+ debugJson(logger$4, "provider.messages.responses.request", {
3696
+ payload: responsesPayload,
3697
+ provider
3755
3698
  });
3756
- const functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
3757
- if (!functionCallState) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta without an open tool call block."), state, events);
3758
- const { nextCount, exceeded } = updateWhitespaceRunState(functionCallState.consecutiveWhitespaceCount, deltaText);
3759
- if (exceeded) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta containing more than 20 consecutive whitespace characters."), state, events);
3760
- functionCallState.consecutiveWhitespaceCount = nextCount;
3761
- events.push({
3762
- type: "content_block_delta",
3763
- index: blockIndex,
3764
- delta: {
3765
- type: "input_json_delta",
3766
- partial_json: deltaText
3767
- }
3699
+ const upstreamResponse = providerConfig.name === "codex" ? await forwardCodexResponses(responsesPayload, c.req.raw.headers, providerConfig.baseUrl) : await forwardProviderResponses(providerConfig, responsesPayload, c.req.raw.headers);
3700
+ if (!upstreamResponse.ok) {
3701
+ logger$4.error("Failed to create provider responses", upstreamResponse);
3702
+ throw new HTTPError("Failed to create provider responses", upstreamResponse);
3703
+ }
3704
+ if (responsesPayload.stream) return streamResponsesProviderMessages({
3705
+ c,
3706
+ payload,
3707
+ provider,
3708
+ providerConfig,
3709
+ upstreamResponse
3710
+ });
3711
+ return respondResponsesProviderMessagesJson(c, {
3712
+ body: await upstreamResponse.json(),
3713
+ payload,
3714
+ provider,
3715
+ providerConfig
3768
3716
  });
3769
- state.blockHasDelta.add(blockIndex);
3770
- return events;
3771
3717
  };
3772
- const handleFunctionCallArgumentsDone = (rawEvent, state) => {
3773
- const events = new Array();
3774
- const outputIndex = rawEvent.output_index;
3775
- const blockIndex = openFunctionCallBlock(state, {
3776
- outputIndex,
3777
- events
3718
+ const applyModelDefaults = (payload, modelConfig) => {
3719
+ payload.temperature ??= modelConfig?.temperature;
3720
+ payload.top_p ??= modelConfig?.topP;
3721
+ payload.top_k ??= modelConfig?.topK;
3722
+ };
3723
+ const applyMissingExtraBody = (payload, options) => {
3724
+ for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
3725
+ };
3726
+ const getRequestThinkingBudget = (payload) => {
3727
+ const budget = payload.thinking?.budget_tokens;
3728
+ if (typeof budget !== "number" || !Number.isFinite(budget)) return;
3729
+ return budget;
3730
+ };
3731
+ const applyOpenAICompatibleThinkingBudget = (payload, source) => {
3732
+ const thinkingBudget = getRequestThinkingBudget(source);
3733
+ if (thinkingBudget !== void 0) {
3734
+ payload.thinking_budget = thinkingBudget;
3735
+ return;
3736
+ }
3737
+ if (payload.thinking_budget === void 0) delete payload.thinking_budget;
3738
+ };
3739
+ const applyOpenAICompatibleExtraBodyThinkingBudget = (payload, options) => {
3740
+ const { extraBody } = options;
3741
+ if (!extraBody || !Object.hasOwn(extraBody, "thinking_budget")) return;
3742
+ const rawPayload = payload;
3743
+ rawPayload.thinking_budget = extraBody.thinking_budget;
3744
+ };
3745
+ const handleOpenAICompatibleProviderMessages = async (c, options) => {
3746
+ const { modelConfig, payload, provider, providerConfig } = options;
3747
+ const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
3748
+ debugJson(logger$4, "provider.messages.openai_compatible.request", {
3749
+ payload: openAIPayload,
3750
+ provider
3778
3751
  });
3779
- const finalArguments = typeof rawEvent.arguments === "string" ? rawEvent.arguments : void 0;
3780
- if (!state.blockHasDelta.has(blockIndex) && finalArguments) {
3781
- events.push({
3782
- type: "content_block_delta",
3783
- index: blockIndex,
3784
- delta: {
3785
- type: "input_json_delta",
3786
- partial_json: finalArguments
3787
- }
3788
- });
3789
- state.blockHasDelta.add(blockIndex);
3752
+ const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
3753
+ if (!upstreamResponse.ok) {
3754
+ logger$4.error("Failed to create openai-compatible responses", upstreamResponse);
3755
+ throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
3790
3756
  }
3791
- state.functionCallStateByOutputIndex.delete(outputIndex);
3792
- return events;
3793
- };
3794
- const handleOutputTextDelta = (rawEvent, state) => {
3795
- const events = new Array();
3796
- const outputIndex = rawEvent.output_index;
3797
- const contentIndex = rawEvent.content_index;
3798
- const deltaText = rawEvent.delta;
3799
- if (!deltaText) return events;
3800
- const blockIndex = openTextBlockIfNeeded(state, {
3801
- outputIndex,
3802
- contentIndex,
3803
- events
3757
+ const contentType = upstreamResponse.headers.get("content-type") ?? "";
3758
+ if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
3759
+ c,
3760
+ payload,
3761
+ provider,
3762
+ upstreamResponse
3804
3763
  });
3805
- events.push({
3806
- type: "content_block_delta",
3807
- index: blockIndex,
3808
- delta: {
3809
- type: "text_delta",
3810
- text: deltaText
3811
- }
3764
+ return respondOpenAICompatibleProviderMessagesJson(c, {
3765
+ body: await upstreamResponse.json(),
3766
+ payload,
3767
+ provider
3812
3768
  });
3813
- state.blockHasDelta.add(blockIndex);
3814
- return events;
3815
3769
  };
3816
- const handleReasoningSummaryTextDelta = (rawEvent, state) => {
3817
- const outputIndex = rawEvent.output_index;
3818
- const deltaText = rawEvent.delta;
3819
- const events = new Array();
3820
- const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3821
- events.push({
3822
- type: "content_block_delta",
3823
- index: blockIndex,
3824
- delta: {
3825
- type: "thinking_delta",
3826
- thinking: deltaText
3827
- }
3770
+ const createOpenAICompatiblePayload = (payload, modelConfig) => {
3771
+ const openAIPayload = translateToOpenAI(payload, {
3772
+ supportPdf: modelConfig?.supportPdf,
3773
+ toolContentSupportType: modelConfig?.toolContentSupportType ?? []
3828
3774
  });
3829
- state.blockHasDelta.add(blockIndex);
3830
- return events;
3831
- };
3832
- const handleReasoningSummaryTextDone = (rawEvent, state) => {
3833
- const outputIndex = rawEvent.output_index;
3834
- const text = rawEvent.text;
3835
- const events = new Array();
3836
- const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3837
- if (text && !state.blockHasDelta.has(blockIndex)) events.push({
3838
- type: "content_block_delta",
3839
- index: blockIndex,
3840
- delta: {
3841
- type: "thinking_delta",
3842
- thinking: text
3843
- }
3775
+ applyOpenAICompatibleThinkingBudget(openAIPayload, payload);
3776
+ if (payload.top_k !== void 0) openAIPayload.top_k = payload.top_k;
3777
+ if (openAIPayload.stream) openAIPayload.stream_options = { include_usage: true };
3778
+ normalizeOpenAICompatibleReasoningContent(openAIPayload);
3779
+ applyOpenAICompatibleRequestOverrides(openAIPayload, {
3780
+ extraBody: modelConfig?.extraBody,
3781
+ source: payload
3844
3782
  });
3845
- return events;
3783
+ applyMissingExtraBody(openAIPayload, { extraBody: modelConfig?.extraBody });
3784
+ applyOpenAICompatibleExtraBodyThinkingBudget(openAIPayload, { extraBody: modelConfig?.extraBody });
3785
+ if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
3786
+ if (modelConfig?.contextCache !== false) applyOpenAICompatibleContextCache(openAIPayload);
3787
+ return openAIPayload;
3846
3788
  };
3847
- const handleOutputTextDone = (rawEvent, state) => {
3848
- const events = new Array();
3849
- const outputIndex = rawEvent.output_index;
3850
- const contentIndex = rawEvent.content_index;
3851
- const text = rawEvent.text;
3852
- const blockIndex = openTextBlockIfNeeded(state, {
3853
- outputIndex,
3854
- contentIndex,
3855
- events
3856
- });
3857
- if (text && !state.blockHasDelta.has(blockIndex)) events.push({
3858
- type: "content_block_delta",
3859
- index: blockIndex,
3860
- delta: {
3861
- type: "text_delta",
3862
- text
3863
- }
3864
- });
3865
- return events;
3789
+ const normalizeOpenAICompatibleReasoningContent = (payload) => {
3790
+ for (const message of payload.messages) {
3791
+ if (message.role !== "assistant") continue;
3792
+ if (message.reasoning_content === void 0 && message.reasoning_text !== void 0) message.reasoning_content = message.reasoning_text;
3793
+ delete message.reasoning_text;
3794
+ delete message.reasoning_opaque;
3795
+ }
3866
3796
  };
3867
- const handleResponseCompleted = (rawEvent, state) => {
3868
- const response = rawEvent.response;
3869
- const events = new Array();
3870
- closeAllOpenBlocks(state, events);
3871
- const anthropic = translateResponsesResultToAnthropic(response);
3872
- events.push({
3873
- type: "message_delta",
3874
- delta: {
3875
- stop_reason: anthropic.stop_reason,
3876
- stop_sequence: anthropic.stop_sequence
3877
- },
3878
- usage: anthropic.usage
3879
- }, { type: "message_stop" });
3880
- state.messageCompleted = true;
3881
- return events;
3797
+ const applyOpenAICompatibleRequestOverrides = (payload, options) => {
3798
+ const allowedKeys = new Set(Object.keys(options.extraBody ?? {}));
3799
+ for (const key of allowedKeys) if (Object.hasOwn(options.source, key)) payload[key] = options.source[key];
3882
3800
  };
3883
- const handleResponseFailed = (rawEvent, state) => {
3884
- const response = rawEvent.response;
3885
- const events = new Array();
3886
- closeAllOpenBlocks(state, events);
3887
- const message = response.error?.message ?? "The response failed due to an unknown error.";
3888
- events.push(buildErrorEvent(message));
3889
- state.messageCompleted = true;
3890
- return events;
3801
+ const applyOpenAICompatibleContextCache = (payload) => {
3802
+ const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
3803
+ for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
3891
3804
  };
3892
- const handleErrorEvent = (rawEvent, state) => {
3893
- const message = typeof rawEvent.message === "string" ? rawEvent.message : "An unexpected error occurred during streaming.";
3894
- state.messageCompleted = true;
3895
- return [buildErrorEvent(message)];
3805
+ const selectContextCacheMessageIndexes = (messages) => {
3806
+ const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
3807
+ const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
3808
+ const finalIndexes = cacheableIndexes.filter((index) => messages[index]?.role !== "system").slice(-2);
3809
+ return uniqueIndexes$1([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
3896
3810
  };
3897
- const handleFunctionCallArgumentsValidationError = (error, state, events = []) => {
3898
- const reason = error.message;
3899
- closeAllOpenBlocks(state, events);
3900
- state.messageCompleted = true;
3901
- events.push(buildErrorEvent(reason));
3902
- return events;
3811
+ const uniqueIndexes$1 = (indexes) => [...new Set(indexes)].slice(0, OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT);
3812
+ const isContextCacheMarkerEligible = (message) => {
3813
+ if (!OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES.has(message.role)) return false;
3814
+ if (typeof message.content === "string") return message.content.length > 0;
3815
+ return Array.isArray(message.content) && message.content.length > 0;
3903
3816
  };
3904
- const messageStart = (state, response) => {
3905
- state.messageStartSent = true;
3906
- const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
3907
- const inputTokens = (response.usage?.input_tokens ?? 0) - (inputCachedTokens ?? 0);
3908
- return [{
3909
- type: "message_start",
3910
- message: {
3911
- id: response.id,
3912
- type: "message",
3913
- role: "assistant",
3914
- content: [],
3915
- model: response.model,
3916
- stop_reason: null,
3917
- stop_sequence: null,
3918
- usage: {
3919
- input_tokens: inputTokens,
3920
- output_tokens: 0,
3921
- cache_read_input_tokens: inputCachedTokens ?? 0
3817
+ const applyContextCacheControl = (message) => {
3818
+ if (!message) return;
3819
+ if (typeof message.content === "string") {
3820
+ message.content = [{
3821
+ type: "text",
3822
+ text: message.content,
3823
+ cache_control: { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL }
3824
+ }];
3825
+ return;
3826
+ }
3827
+ if (!Array.isArray(message.content)) return;
3828
+ const lastPart = message.content.at(-1);
3829
+ if (!lastPart) return;
3830
+ setContextCacheControl(lastPart);
3831
+ };
3832
+ const setContextCacheControl = (part) => {
3833
+ part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
3834
+ };
3835
+ const streamProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
3836
+ logger$4.debug("provider.messages.streaming");
3837
+ const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
3838
+ return streamSSE(c, async (stream) => {
3839
+ let usage = {};
3840
+ for await (const chunk of events(upstreamResponse)) {
3841
+ logger$4.debug("provider.messages.raw_stream_event:", chunk.data);
3842
+ const eventName = chunk.event;
3843
+ if (eventName === "ping") {
3844
+ await stream.writeSSE({
3845
+ event: "ping",
3846
+ data: "{\"type\":\"ping\"}"
3847
+ });
3848
+ continue;
3849
+ }
3850
+ let data = chunk.data;
3851
+ if (!data) continue;
3852
+ if (chunk.data === "[DONE]") break;
3853
+ const parsed = parseProviderStreamEvent(data, providerConfig);
3854
+ if (parsed) {
3855
+ usage = mergeAnthropicUsage(usage, parsed.usage);
3856
+ data = parsed.data;
3857
+ }
3858
+ await stream.writeSSE({
3859
+ event: eventName,
3860
+ data
3861
+ });
3862
+ }
3863
+ recordUsage(usage);
3864
+ });
3865
+ };
3866
+ const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstreamResponse }) => {
3867
+ logger$4.debug("provider.messages.openai_compatible.streaming");
3868
+ const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
3869
+ return streamSSE(c, async (stream) => {
3870
+ let usage = {};
3871
+ const streamState = {
3872
+ messageStartSent: false,
3873
+ contentBlockIndex: 0,
3874
+ contentBlockOpen: false,
3875
+ toolCalls: {},
3876
+ thinkingBlockOpen: false
3877
+ };
3878
+ for await (const chunk of events(upstreamResponse)) {
3879
+ logger$4.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
3880
+ if (chunk.event === "ping") {
3881
+ await stream.writeSSE({
3882
+ event: "ping",
3883
+ data: "{\"type\":\"ping\"}"
3884
+ });
3885
+ continue;
3886
+ }
3887
+ if (!chunk.data || chunk.data === "[DONE]") {
3888
+ if (chunk.data === "[DONE]") break;
3889
+ continue;
3890
+ }
3891
+ const parsed = parseOpenAICompatibleStreamChunk(chunk.data);
3892
+ if (!parsed) continue;
3893
+ if (parsed.usage) usage = normalizeOpenAIUsage(parsed.usage);
3894
+ const events = translateChunkToAnthropicEvents(parsed, streamState);
3895
+ for (const event of events) {
3896
+ const eventData = JSON.stringify(event);
3897
+ debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
3898
+ await stream.writeSSE({
3899
+ event: event.type,
3900
+ data: eventData
3901
+ });
3922
3902
  }
3923
3903
  }
3924
- }];
3904
+ for (const event of flushPendingAnthropicStreamEvents(streamState)) {
3905
+ const eventData = JSON.stringify(event);
3906
+ debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
3907
+ await stream.writeSSE({
3908
+ event: event.type,
3909
+ data: eventData
3910
+ });
3911
+ }
3912
+ recordUsage(usage);
3913
+ });
3925
3914
  };
3926
- const openTextBlockIfNeeded = (state, params) => {
3927
- const { outputIndex, contentIndex, events } = params;
3928
- const key = getBlockKey(outputIndex, contentIndex);
3929
- let blockIndex = state.blockIndexByKey.get(key);
3930
- if (blockIndex === void 0) {
3931
- blockIndex = state.nextContentBlockIndex;
3932
- state.nextContentBlockIndex += 1;
3933
- state.blockIndexByKey.set(key, blockIndex);
3934
- }
3935
- if (!state.openBlocks.has(blockIndex)) {
3936
- closeOpenBlocks(state, events);
3937
- events.push({
3938
- type: "content_block_start",
3939
- index: blockIndex,
3940
- content_block: {
3941
- type: "text",
3942
- text: ""
3915
+ const streamResponsesProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
3916
+ logger$4.debug("provider.messages.responses.streaming", { provider });
3917
+ const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
3918
+ return streamSSE(c, async (stream) => {
3919
+ let usage = {};
3920
+ const streamState = createResponsesStreamState({ toolSearchName: resolveBridgeToolSearchName(payload.tools) });
3921
+ for await (const chunk of events(upstreamResponse)) {
3922
+ logger$4.debug("provider.messages.responses.raw_stream_event:", chunk.data);
3923
+ if (chunk.event === "ping") {
3924
+ await stream.writeSSE({
3925
+ event: "ping",
3926
+ data: "{\"type\":\"ping\"}"
3927
+ });
3928
+ continue;
3929
+ }
3930
+ if (!chunk.data || chunk.data === "[DONE]") {
3931
+ if (chunk.data === "[DONE]") break;
3932
+ continue;
3933
+ }
3934
+ const parsed = parseResponsesProviderStreamChunk(chunk.data, providerConfig);
3935
+ if (!parsed) continue;
3936
+ if (parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete") usage = normalizeResponsesUsage(parsed.response.usage);
3937
+ const events = translateResponsesStreamEvent(parsed, streamState);
3938
+ for (const event of events) {
3939
+ const eventData = JSON.stringify(event);
3940
+ debugLazy(logger$4, () => ["provider.messages.responses.translated_event:", eventData]);
3941
+ await stream.writeSSE({
3942
+ event: event.type,
3943
+ data: eventData
3944
+ });
3943
3945
  }
3946
+ }
3947
+ if (!streamState.messageCompleted) {
3948
+ const errorEvent = buildErrorEvent(`${provider} stream ended without a completion event`);
3949
+ await stream.writeSSE({
3950
+ event: errorEvent.type,
3951
+ data: JSON.stringify(errorEvent)
3952
+ });
3953
+ }
3954
+ recordUsage(usage);
3955
+ });
3956
+ };
3957
+ const parseOpenAICompatibleStreamChunk = (data) => {
3958
+ try {
3959
+ return JSON.parse(data);
3960
+ } catch (error) {
3961
+ logger$4.error("provider.messages.openai_compatible.parse_chunk_error", {
3962
+ data,
3963
+ error
3944
3964
  });
3945
- state.openBlocks.add(blockIndex);
3965
+ return null;
3946
3966
  }
3947
- return blockIndex;
3948
3967
  };
3949
- const openThinkingBlockIfNeeded = (state, outputIndex, events) => {
3950
- const key = getBlockKey(outputIndex, 0);
3951
- let blockIndex = state.blockIndexByKey.get(key);
3952
- if (blockIndex === void 0) {
3953
- blockIndex = state.nextContentBlockIndex;
3954
- state.nextContentBlockIndex += 1;
3955
- state.blockIndexByKey.set(key, blockIndex);
3956
- }
3957
- if (!state.openBlocks.has(blockIndex)) {
3958
- closeOpenBlocks(state, events);
3959
- events.push({
3960
- type: "content_block_start",
3961
- index: blockIndex,
3962
- content_block: {
3963
- type: "thinking",
3964
- thinking: ""
3965
- }
3968
+ const parseResponsesProviderStreamChunk = (data, providerConfig) => {
3969
+ try {
3970
+ const parsed = JSON.parse(data);
3971
+ if (providerConfig.name === "codex") logCodexRateLimitsEvent(parsed);
3972
+ return providerConfig.name === "codex" ? normalizeCodexResponsesEvent(parsed) : parsed;
3973
+ } catch (error) {
3974
+ logger$4.error("provider.messages.responses.parse_chunk_error", {
3975
+ provider: providerConfig.name,
3976
+ data,
3977
+ error
3966
3978
  });
3967
- state.openBlocks.add(blockIndex);
3979
+ return null;
3968
3980
  }
3969
- return blockIndex;
3970
- };
3971
- const closeBlockIfOpen = (state, blockIndex, events) => {
3972
- if (!state.openBlocks.has(blockIndex)) return;
3973
- events.push({
3974
- type: "content_block_stop",
3975
- index: blockIndex
3976
- });
3977
- state.openBlocks.delete(blockIndex);
3978
- state.blockHasDelta.delete(blockIndex);
3979
- };
3980
- const closeOpenBlocks = (state, events) => {
3981
- for (const blockIndex of state.openBlocks) closeBlockIfOpen(state, blockIndex, events);
3982
- };
3983
- const closeAllOpenBlocks = (state, events) => {
3984
- closeOpenBlocks(state, events);
3985
- state.functionCallStateByOutputIndex.clear();
3986
3981
  };
3987
- const buildErrorEvent = (message) => ({
3988
- type: "error",
3989
- error: {
3990
- type: "api_error",
3991
- message
3992
- }
3993
- });
3994
- const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
3995
- const openFunctionCallBlock = (state, params) => {
3996
- const { outputIndex, toolCallId, name, events } = params;
3997
- let functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
3998
- if (!functionCallState) {
3999
- const blockIndex = state.nextContentBlockIndex;
4000
- state.nextContentBlockIndex += 1;
4001
- functionCallState = {
4002
- blockIndex,
4003
- toolCallId: toolCallId ?? `tool_call_${blockIndex}`,
4004
- name: name ?? "function",
4005
- consecutiveWhitespaceCount: 0
3982
+ const parseProviderStreamEvent = (data, providerConfig) => {
3983
+ try {
3984
+ const parsed = JSON.parse(data);
3985
+ if (parsed.type === "message_start") {
3986
+ adjustInputTokens(providerConfig, parsed.message.usage);
3987
+ return {
3988
+ data: JSON.stringify(parsed),
3989
+ model: parsed.message.model,
3990
+ usage: normalizeAnthropicUsage(parsed.message.usage)
3991
+ };
3992
+ }
3993
+ if (parsed.type === "message_delta") {
3994
+ adjustInputTokens(providerConfig, parsed.usage);
3995
+ return {
3996
+ data: JSON.stringify(parsed),
3997
+ usage: normalizeAnthropicUsage(parsed.usage)
3998
+ };
3999
+ }
4000
+ return {
4001
+ data: JSON.stringify(parsed),
4002
+ usage: {}
4006
4003
  };
4007
- state.functionCallStateByOutputIndex.set(outputIndex, functionCallState);
4008
- }
4009
- const { blockIndex } = functionCallState;
4010
- if (!state.openBlocks.has(blockIndex)) {
4011
- closeOpenBlocks(state, events);
4012
- events.push({
4013
- type: "content_block_start",
4014
- index: blockIndex,
4015
- content_block: {
4016
- type: "tool_use",
4017
- id: functionCallState.toolCallId,
4018
- name: functionCallState.name,
4019
- input: {}
4020
- }
4004
+ } catch (error) {
4005
+ logger$4.error("provider.messages.streaming.adjust_tokens_error", {
4006
+ error,
4007
+ originalData: data
4021
4008
  });
4022
- state.openBlocks.add(blockIndex);
4009
+ return null;
4023
4010
  }
4024
- return blockIndex;
4025
4011
  };
4026
- const extractFunctionCallDetails = (rawEvent, state) => {
4027
- const item = rawEvent.item;
4028
- const itemType = item.type;
4029
- if (itemType === "tool_search_call") return {
4030
- outputIndex: rawEvent.output_index,
4031
- toolCallId: item.call_id,
4032
- name: state.toolSearchName,
4033
- initialArguments: ""
4034
- };
4035
- if (itemType !== "function_call") return;
4036
- return {
4037
- outputIndex: rawEvent.output_index,
4038
- toolCallId: item.call_id,
4039
- name: resolveToolUseName(item),
4040
- initialArguments: item.arguments
4041
- };
4012
+ const respondProviderMessagesJson = (c, options) => {
4013
+ const { body, payload, provider, providerConfig } = options;
4014
+ const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
4015
+ adjustInputTokens(providerConfig, body.usage);
4016
+ recordUsage(normalizeAnthropicUsage(body.usage));
4017
+ debugJson(logger$4, "provider.messages.no_stream result:", body);
4018
+ return c.json(body);
4042
4019
  };
4043
- const stringifyToolSearchArguments = (argumentsValue) => {
4044
- try {
4045
- return JSON.stringify(formatToolSearchBridgeArguments(argumentsValue));
4046
- } catch {
4047
- return;
4048
- }
4020
+ const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
4021
+ const { body, payload, provider } = options;
4022
+ createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
4023
+ const anthropicResponse = translateToAnthropic(body);
4024
+ debugJson(logger$4, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
4025
+ return c.json(anthropicResponse);
4026
+ };
4027
+ const respondResponsesProviderMessagesJson = (c, options) => {
4028
+ const { body, payload, provider, providerConfig } = options;
4029
+ createProviderMessagesUsageRecorder(payload, provider)(normalizeResponsesUsage(body.usage));
4030
+ const anthropicResponse = translateResponsesResultToAnthropic(body, { toolSearchName: resolveBridgeToolSearchName(payload.tools) });
4031
+ debugJson(logger$4, "provider.messages.responses.no_stream result:", anthropicResponse);
4032
+ if (providerConfig.name === "codex") logger$4.debug("provider.messages.codex.no_stream.result");
4033
+ return c.json(anthropicResponse);
4034
+ };
4035
+ const createProviderMessagesUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
4036
+ endpoint: "provider_messages",
4037
+ model: payload.model,
4038
+ providerName: provider,
4039
+ sessionId: parseUserIdMetadata(payload.metadata?.user_id).sessionId
4040
+ });
4041
+ const adjustInputTokens = (providerConfig, usage) => {
4042
+ if (!providerConfig.adjustInputTokens || !usage) return;
4043
+ usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
4044
+ debugJson(logger$4, "provider.messages.adjusted_usage:", usage);
4049
4045
  };
4050
4046
  //#endregion
4051
4047
  //#region src/services/copilot/create-messages.ts
@@ -4775,11 +4771,19 @@ const providerModelRoutes = new Hono();
4775
4771
  providerModelRoutes.get("/", async (c) => {
4776
4772
  const provider = c.req.param("provider") ?? "";
4777
4773
  try {
4778
- const providerConfig = getProviderConfig(provider);
4774
+ const providerConfig = await resolveProviderConfig(provider);
4779
4775
  if (!providerConfig) return c.json({ error: {
4780
4776
  message: `Provider '${provider}' not found or disabled`,
4781
4777
  type: "invalid_request_error"
4782
4778
  } }, 404);
4779
+ if (providerConfig.name === "codex") {
4780
+ const models = getModels();
4781
+ return c.json({
4782
+ object: "list",
4783
+ data: models.data,
4784
+ has_more: false
4785
+ });
4786
+ }
4783
4787
  const upstreamResponse = await forwardProviderModels(providerConfig, c.req.raw.headers);
4784
4788
  logger$2.debug("provider.models.response", {
4785
4789
  provider,
@@ -4839,9 +4843,17 @@ const responsesHandlerDependencies = {
4839
4843
  isResponsesApiWebSearchEnabled
4840
4844
  };
4841
4845
  const handleResponses = async (c) => {
4842
- await responsesHandlerDependencies.checkRateLimit(state);
4843
4846
  const payload = await c.req.json();
4844
4847
  debugJson(logger$1, "Responses request payload:", payload);
4848
+ const providerModelAlias = parseProviderModelAlias(payload.model);
4849
+ if (providerModelAlias) {
4850
+ payload.model = providerModelAlias.model;
4851
+ return await handleProviderResponsesForProvider(c, {
4852
+ payload,
4853
+ provider: providerModelAlias.provider
4854
+ });
4855
+ }
4856
+ await responsesHandlerDependencies.checkRateLimit(state);
4845
4857
  const requestId = generateRequestIdFromPayload({ messages: payload.input });
4846
4858
  logger$1.debug("Generated request ID:", requestId);
4847
4859
  const sessionId = getUUID(requestId);
@@ -4897,6 +4909,30 @@ const handleResponses = async (c) => {
4897
4909
  recordUsage(normalizeResponsesUsage(response.usage));
4898
4910
  return c.json(response);
4899
4911
  };
4912
+ const handleProviderResponsesForProvider = async (c, options) => {
4913
+ const { payload, provider } = options;
4914
+ const providerConfig = await resolveProviderConfig(provider);
4915
+ if (providerConfig?.type !== "openai-responses") return c.json({ error: {
4916
+ message: `Provider '${provider}' does not support the /v1/responses endpoint`,
4917
+ type: "invalid_request_error"
4918
+ } }, 400);
4919
+ const upstreamResponse = providerConfig.name === "codex" ? await forwardCodexResponses(payload, c.req.raw.headers, providerConfig.baseUrl) : await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
4920
+ if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${provider} responses`, upstreamResponse);
4921
+ const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
4922
+ if (payload.stream) recordProviderResponsesStreamUsage(upstreamResponse.clone(), {
4923
+ normalizeCodex: providerConfig.name === "codex",
4924
+ provider,
4925
+ recordUsage
4926
+ }).catch((error) => {
4927
+ logger$1.warn("provider.responses.usage_stream_error", {
4928
+ provider,
4929
+ error: getErrorMessage(error)
4930
+ });
4931
+ });
4932
+ else recordUsage(normalizeResponsesUsage((await upstreamResponse.clone().json()).usage));
4933
+ if (providerConfig.name === "codex" && payload.stream) return createProviderProxyResponse(upstreamResponse, createStandardizedCodexResponsesEventStream(getResponsesEvents(upstreamResponse)));
4934
+ return createProviderProxyResponse(upstreamResponse);
4935
+ };
4900
4936
  const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
4901
4937
  const isStreamingRequested = (payload) => Boolean(payload.stream);
4902
4938
  const parseResponsesStreamEvent = (chunk) => {
@@ -4908,6 +4944,51 @@ const parseResponsesStreamEvent = (chunk) => {
4908
4944
  return null;
4909
4945
  }
4910
4946
  };
4947
+ const getErrorMessage = (error) => {
4948
+ if (error instanceof Error && error.message) return error.message;
4949
+ return String(error);
4950
+ };
4951
+ const createProviderResponsesUsageRecorder = (payload, provider) => {
4952
+ const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
4953
+ return createProviderTokenUsageRecorder({
4954
+ endpoint: "responses",
4955
+ model: payload.model,
4956
+ providerName: provider,
4957
+ sessionId: sessionAffinity ?? ""
4958
+ });
4959
+ };
4960
+ const recordProviderResponsesStreamUsage = async (upstreamResponse, options) => {
4961
+ let usage = {};
4962
+ try {
4963
+ for await (const chunk of getResponsesEvents(upstreamResponse)) {
4964
+ debugJson(logger$1, "Responses stream chunk:", chunk);
4965
+ if (!chunk.data || chunk.data === "[DONE]") continue;
4966
+ const parsed = parseProviderResponsesStreamEvent(chunk.data, {
4967
+ normalizeCodex: options.normalizeCodex,
4968
+ provider: options.provider
4969
+ });
4970
+ if (parsed?.type === "response.completed" || parsed?.type === "response.failed" || parsed?.type === "response.incomplete") usage = normalizeResponsesUsage(parsed.response.usage);
4971
+ }
4972
+ } finally {
4973
+ options.recordUsage(usage);
4974
+ }
4975
+ };
4976
+ const parseProviderResponsesStreamEvent = (data, options) => {
4977
+ try {
4978
+ const parsed = JSON.parse(data);
4979
+ return options.normalizeCodex ? normalizeCodexResponsesEvent(parsed) : parsed;
4980
+ } catch (error) {
4981
+ logger$1.error("provider.responses.parse_chunk_error", {
4982
+ provider: options.provider,
4983
+ data,
4984
+ error
4985
+ });
4986
+ return null;
4987
+ }
4988
+ };
4989
+ const getResponsesEvents = (response) => {
4990
+ return events(response);
4991
+ };
4911
4992
  const removeWebSearchTool = (payload) => {
4912
4993
  if (!Array.isArray(payload.tools) || payload.tools.length === 0) return;
4913
4994
  payload.tools = payload.tools.filter((t) => {
@@ -5040,4 +5121,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
5040
5121
  //#endregion
5041
5122
  export { server };
5042
5123
 
5043
- //# sourceMappingURL=server-FPXzFkg9.js.map
5124
+ //# sourceMappingURL=server-D5O9IzAY.js.map