@jeffreycao/copilot-api 1.10.8 → 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 +17 -14
  2. package/README.zh-CN.md +17 -14
  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-BRFLAT4z.js → server-D5O9IzAY.js} +1770 -1668
  15. package/dist/server-D5O9IzAY.js.map +1 -0
  16. package/dist/{start-Bzfnxzlh.js → start-BhPxHgu8.js} +4 -6
  17. package/dist/{start-Bzfnxzlh.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-BRFLAT4z.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,1826 +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;
2342
- };
2343
- const applyMissingExtraBody = (payload, options) => {
2344
- for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
2263
+ if (payload.stream) return events(response);
2264
+ return await response.json();
2345
2265
  };
2346
- const handleOpenAICompatibleProviderMessages = async (c, options) => {
2347
- const { modelConfig, payload, provider, providerConfig } = options;
2348
- const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
2349
- debugJson(logger$4, "provider.messages.openai_compatible.request", {
2350
- payload: openAIPayload,
2351
- provider
2352
- });
2353
- const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
2354
- if (!upstreamResponse.ok) {
2355
- logger$4.error("Failed to create openai-compatible responses", upstreamResponse);
2356
- throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
2357
- }
2358
- const contentType = upstreamResponse.headers.get("content-type") ?? "";
2359
- if (Boolean(openAIPayload.stream) && contentType.includes("text/event-stream")) return streamOpenAICompatibleProviderMessages({
2360
- c,
2361
- payload,
2362
- provider,
2363
- upstreamResponse
2364
- });
2365
- return respondOpenAICompatibleProviderMessagesJson(c, {
2366
- body: await upstreamResponse.json(),
2367
- payload,
2368
- provider
2369
- });
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
+ };
2370
2274
  };
2371
- const createOpenAICompatiblePayload = (payload, modelConfig) => {
2372
- const openAIPayload = translateToOpenAI(payload, {
2373
- supportPdf: modelConfig?.supportPdf,
2374
- toolContentSupportType: modelConfig?.toolContentSupportType ?? []
2375
- });
2376
- if (payload.top_k !== void 0) openAIPayload.top_k = payload.top_k;
2377
- if (openAIPayload.stream) openAIPayload.stream_options = { include_usage: true };
2378
- normalizeOpenAICompatibleReasoningContent(openAIPayload);
2379
- applyOpenAICompatibleRequestOverrides(openAIPayload, {
2380
- extraBody: modelConfig?.extraBody,
2381
- source: payload
2382
- });
2383
- applyMissingExtraBody(openAIPayload, { extraBody: modelConfig?.extraBody });
2384
- if (!Object.hasOwn(openAIPayload, "parallel_tool_calls")) openAIPayload.parallel_tool_calls = true;
2385
- if (modelConfig?.contextCache !== false) applyOpenAICompatibleContextCache(openAIPayload);
2386
- return openAIPayload;
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("|");
2387
2288
  };
2388
- const normalizeOpenAICompatibleReasoningContent = (payload) => {
2389
- for (const message of payload.messages) {
2390
- if (message.role !== "assistant") continue;
2391
- if (message.reasoning_content === void 0 && message.reasoning_text !== void 0) message.reasoning_content = message.reasoning_text;
2392
- delete message.reasoning_text;
2393
- delete message.reasoning_opaque;
2394
- }
2289
+ const getResponsesWebSocketInitiator = (preparedHeaders) => {
2290
+ return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
2395
2291
  };
2396
- const applyOpenAICompatibleRequestOverrides = (payload, options) => {
2397
- const allowedKeys = new Set(Object.keys(options.extraBody ?? {}));
2398
- for (const key of allowedKeys) if (Object.hasOwn(options.source, key)) payload[key] = options.source[key];
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;
2399
2309
  };
2400
- const applyOpenAICompatibleContextCache = (payload) => {
2401
- const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
2402
- for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
2310
+ const buildResponsesWebSocketUrl = (baseUrl) => {
2311
+ return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
2403
2312
  };
2404
- const selectContextCacheMessageIndexes = (messages) => {
2405
- const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
2406
- const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
2407
- const finalIndexes = cacheableIndexes.filter((index) => messages[index]?.role !== "system").slice(-2);
2408
- return uniqueIndexes$1([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
2313
+ const getHeaderValue = (headers, headerName) => {
2314
+ const normalizedHeaderName = headerName.toLowerCase();
2315
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
2409
2316
  };
2410
- const uniqueIndexes$1 = (indexes) => [...new Set(indexes)].slice(0, OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT);
2411
- const isContextCacheMarkerEligible = (message) => {
2412
- if (!OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES.has(message.role)) return false;
2413
- if (typeof message.content === "string") return message.content.length > 0;
2414
- return Array.isArray(message.content) && message.content.length > 0;
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 };
2330
+ }
2415
2331
  };
2416
- const applyContextCacheControl = (message) => {
2417
- if (!message) return;
2418
- if (typeof message.content === "string") {
2419
- message.content = [{
2420
- type: "text",
2421
- text: message.content,
2422
- cache_control: { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL }
2423
- }];
2424
- return;
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;
2425
2339
  }
2426
- if (!Array.isArray(message.content)) return;
2427
- const lastPart = message.content.at(-1);
2428
- if (!lastPart) return;
2429
- setContextCacheControl(lastPart);
2430
2340
  };
2431
- const setContextCacheControl = (part) => {
2432
- part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
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");
2433
2349
  };
2434
- const streamProviderMessages = ({ c, payload, provider, providerConfig, upstreamResponse }) => {
2435
- logger$4.debug("provider.messages.streaming");
2436
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
2437
- return streamSSE(c, async (stream) => {
2438
- let usage = {};
2439
- for await (const chunk of events(upstreamResponse)) {
2440
- logger$4.debug("provider.messages.raw_stream_event:", chunk.data);
2441
- const eventName = chunk.event;
2442
- if (eventName === "ping") {
2443
- await stream.writeSSE({
2444
- event: "ping",
2445
- data: "{\"type\":\"ping\"}"
2446
- });
2447
- continue;
2448
- }
2449
- let data = chunk.data;
2450
- if (!data) continue;
2451
- if (chunk.data === "[DONE]") break;
2452
- const parsed = parseProviderStreamEvent(data, providerConfig);
2453
- if (parsed) {
2454
- usage = mergeAnthropicUsage(usage, parsed.usage);
2455
- data = parsed.data;
2456
- }
2457
- await stream.writeSSE({
2458
- event: eventName,
2459
- data
2460
- });
2461
- }
2462
- recordUsage(usage);
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}`;
2361
+ };
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
2463
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;
2464
2402
  };
2465
- const streamOpenAICompatibleProviderMessages = ({ c, payload, provider, upstreamResponse }) => {
2466
- logger$4.debug("provider.messages.openai_compatible.streaming");
2467
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
2468
- return streamSSE(c, async (stream) => {
2469
- let usage = {};
2470
- const streamState = {
2471
- messageStartSent: false,
2472
- contentBlockIndex: 0,
2473
- contentBlockOpen: false,
2474
- toolCalls: {},
2475
- thinkingBlockOpen: false
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
2476
2417
  };
2477
- for await (const chunk of events(upstreamResponse)) {
2478
- logger$4.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
2479
- if (chunk.event === "ping") {
2480
- await stream.writeSSE({
2481
- event: "ping",
2482
- data: "{\"type\":\"ping\"}"
2418
+ }
2419
+ };
2420
+ const translateMessage = (message, model, applyPhase, state) => {
2421
+ if (message.role === "user") return translateUserMessage(message, state);
2422
+ return translateAssistantMessage(message, model, applyPhase, state);
2423
+ };
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;
2434
+ }
2435
+ const converted = translateUserContentBlock(block);
2436
+ if (converted.length > 0) pendingContent.push(...converted);
2437
+ }
2438
+ flushPendingContent(pendingContent, items, { role: "user" });
2439
+ return items;
2440
+ };
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
2483
2463
  });
2464
+ items.push(compactionContent);
2484
2465
  continue;
2485
2466
  }
2486
- if (!chunk.data || chunk.data === "[DONE]") {
2487
- if (chunk.data === "[DONE]") break;
2488
- continue;
2489
- }
2490
- const parsed = parseOpenAICompatibleStreamChunk(chunk.data);
2491
- if (!parsed) continue;
2492
- if (parsed.usage) usage = normalizeOpenAIUsage(parsed.usage);
2493
- const events = translateChunkToAnthropicEvents(parsed, streamState);
2494
- for (const event of events) {
2495
- const eventData = JSON.stringify(event);
2496
- debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
2497
- await stream.writeSSE({
2498
- event: event.type,
2499
- data: eventData
2467
+ if (block.signature.includes("@")) {
2468
+ flushPendingContent(pendingContent, items, {
2469
+ role: "assistant",
2470
+ phase: assistantPhase
2500
2471
  });
2472
+ items.push(createReasoningContent(block));
2473
+ continue;
2501
2474
  }
2502
2475
  }
2503
- for (const event of flushPendingAnthropicStreamEvents(streamState)) {
2504
- const eventData = JSON.stringify(event);
2505
- debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
2506
- await stream.writeSSE({
2507
- event: event.type,
2508
- data: eventData
2509
- });
2510
- }
2511
- recordUsage(usage);
2476
+ const converted = translateAssistantContentBlock(block);
2477
+ if (converted) pendingContent.push(converted);
2478
+ }
2479
+ flushPendingContent(pendingContent, items, {
2480
+ role: "assistant",
2481
+ phase: assistantPhase
2512
2482
  });
2483
+ return items;
2513
2484
  };
2514
- const parseOpenAICompatibleStreamChunk = (data) => {
2515
- try {
2516
- return JSON.parse(data);
2517
- } catch (error) {
2518
- logger$4.error("provider.messages.openai_compatible.parse_chunk_error", {
2519
- data,
2520
- error
2521
- });
2522
- 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 [];
2523
2491
  }
2524
2492
  };
2525
- const parseProviderStreamEvent = (data, providerConfig) => {
2526
- try {
2527
- const parsed = JSON.parse(data);
2528
- if (parsed.type === "message_start") {
2529
- adjustInputTokens(providerConfig, parsed.message.usage);
2530
- return {
2531
- data: JSON.stringify(parsed),
2532
- model: parsed.message.model,
2533
- usage: normalizeAnthropicUsage(parsed.message.usage)
2534
- };
2535
- }
2536
- if (parsed.type === "message_delta") {
2537
- adjustInputTokens(providerConfig, parsed.usage);
2538
- return {
2539
- data: JSON.stringify(parsed),
2540
- usage: normalizeAnthropicUsage(parsed.usage)
2541
- };
2542
- }
2543
- return {
2544
- data: JSON.stringify(parsed),
2545
- usage: {}
2546
- };
2547
- } catch (error) {
2548
- logger$4.error("provider.messages.streaming.adjust_tokens_error", {
2549
- error,
2550
- originalData: data
2551
- });
2552
- return null;
2493
+ const translateAssistantContentBlock = (block) => {
2494
+ switch (block.type) {
2495
+ case "text": return createOutPutTextContent(block.text);
2496
+ default: return;
2553
2497
  }
2554
2498
  };
2555
- const respondProviderMessagesJson = (c, options) => {
2556
- const { body, payload, provider, providerConfig } = options;
2557
- const recordUsage = createProviderMessagesUsageRecorder(payload, provider);
2558
- adjustInputTokens(providerConfig, body.usage);
2559
- recordUsage(normalizeAnthropicUsage(body.usage));
2560
- debugJson(logger$4, "provider.messages.no_stream result:", body);
2561
- return c.json(body);
2562
- };
2563
- const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
2564
- const { body, payload, provider } = options;
2565
- createProviderMessagesUsageRecorder(payload, provider)(normalizeOpenAIUsage(body.usage));
2566
- const anthropicResponse = translateToAnthropic(body);
2567
- debugJson(logger$4, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
2568
- 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;
2569
2504
  };
2570
- const createProviderMessagesUsageRecorder = (payload, provider) => createProviderTokenUsageRecorder({
2571
- endpoint: "provider_messages",
2572
- model: payload.model,
2573
- providerName: provider,
2574
- 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 } : {}
2575
2510
  });
2576
- const adjustInputTokens = (providerConfig, usage) => {
2577
- if (!providerConfig.adjustInputTokens || !usage) return;
2578
- usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
2579
- 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";
2580
2517
  };
2581
- const responsesUtilsDependencies = {
2582
- isResponsesApiContextManagementModel,
2583
- isResponsesApiWebSocketEnabled
2518
+ const shouldApplyPhase = (_model) => {
2519
+ return true;
2584
2520
  };
2585
- 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;
2586
2542
  return {
2587
- vision: hasVisionInput(payload),
2588
- 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
2589
2550
  };
2590
2551
  };
2591
- const getResponsesTransportForModel = (selectedModel, options = {}) => {
2592
- const supportedEndpoints = selectedModel?.supported_endpoints ?? [];
2593
- const useWebSocket = responsesUtilsDependencies.isResponsesApiWebSocketEnabled();
2594
- if (options.compactType !== 1 && useWebSocket && supportedEndpoints.includes("ws:/responses")) return "websocket";
2595
- if (supportedEndpoints.includes("/responses")) return "http";
2596
- return null;
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
+ };
2597
2560
  };
2598
- const hasAgentInitiator = (payload) => {
2599
- const lastItem = getPayloadItems(payload).at(-1);
2600
- if (!lastItem) return false;
2601
- if (!("role" in lastItem) || !lastItem.role) return true;
2602
- return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
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
+ };
2603
2571
  };
2604
- const hasVisionInput = (payload) => {
2605
- return getPayloadItems(payload).some((item) => containsVisionContent(item));
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);
2606
2590
  };
2607
- const resolveResponsesCompactThreshold = (maxPromptTokens) => {
2608
- if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
2609
- return 5e4;
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);
2610
2601
  };
2611
- const createCompactionContextManagement = (compactThreshold) => [{
2612
- type: "compaction",
2613
- compact_threshold: compactThreshold
2614
- }];
2615
- const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
2616
- if (payload.context_management !== void 0) return;
2617
- if (!responsesUtilsDependencies.isResponsesApiContextManagementModel(payload.model)) return;
2618
- payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
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
+ };
2619
2611
  };
2620
- const compactInputByLatestCompaction = (payload) => {
2621
- if (!Array.isArray(payload.input) || payload.input.length === 0) return;
2622
- const latestCompactionMessageIndex = getLatestCompactionMessageIndex(payload.input);
2623
- if (latestCompactionMessageIndex === void 0) return;
2624
- payload.input = payload.input.slice(latestCompactionMessageIndex);
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 [];
2625
2618
  };
2626
- const getLatestCompactionMessageIndex = (input) => {
2627
- for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
2619
+ const extractToolReferenceNames = (content) => {
2620
+ if (!Array.isArray(content)) return [];
2621
+ return content.flatMap((block) => block.type === "tool_reference" ? [block.tool_name] : []);
2628
2622
  };
2629
- const isCompactionInputItem = (value) => {
2630
- return "type" in value && typeof value.type === "string" && value.type === "compaction";
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;
2631
2631
  };
2632
- const getPayloadItems = (payload) => {
2633
- const result = [];
2634
- const { input } = payload;
2635
- if (Array.isArray(input)) result.push(...input);
2636
- return result;
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`);
2637
2636
  };
2638
- const containsVisionContent = (value) => {
2639
- if (!value) return false;
2640
- if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
2641
- if (typeof value !== "object") return false;
2642
- const record = value;
2643
- if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
2644
- if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
2645
- return false;
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;
2646
2654
  };
2647
- //#endregion
2648
- //#region src/services/copilot/create-responses.ts
2649
- const RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS = 6e4;
2650
- const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
2651
- if (!state.copilotToken) throw new Error("Copilot token not found");
2652
- const headers = {
2653
- ...copilotHeaders(state, requestId, vision),
2654
- "x-initiator": initiator
2655
- };
2656
- prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
2657
- prepareForCompact(headers, compactType);
2658
- payload.service_tier = void 0;
2659
- consola.log(`<-- model: ${payload.model}`);
2660
- if ((compactType === 1 ? "http" : transport) === "websocket") {
2661
- const stream = createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
2662
- requestId,
2663
- subagentMarker
2664
- }));
2665
- if (payload.stream) return stream;
2666
- return await consumeResponsesWebSocketStream(stream);
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));
2667
2673
  }
2668
- return await createHttpResponses(payload, headers);
2674
+ return converted;
2669
2675
  };
2670
- const createHttpResponses = async (payload, headers) => {
2671
- const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
2672
- method: "POST",
2673
- headers,
2674
- body: JSON.stringify(payload)
2675
- });
2676
- logCopilotRateLimits(response.headers);
2677
- if (!response.ok) {
2678
- consola.error("Failed to create responses", response);
2679
- 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";
2680
2728
  }
2681
- if (payload.stream) return events(response);
2682
- return await response.json();
2683
2729
  };
2684
- const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
2685
- 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);
2686
2736
  return {
2687
- headers: copilotWebSocketHeaders(preparedHeaders),
2688
- poolKey: buildResponsesWebSocketPoolKey(payload, options),
2689
- 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
2690
2745
  };
2691
2746
  };
2692
- const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
2693
- const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
2694
- const subagentKey = subagentMarker ? [
2695
- subagentMarker.session_id,
2696
- subagentMarker.agent_id,
2697
- subagentMarker.agent_type
2698
- ].join(":") : "main";
2699
- return [
2700
- tokenFingerprint,
2701
- payload.model,
2702
- requestId,
2703
- subagentKey
2704
- ].map(encodePoolKeyPart).join("|");
2705
- };
2706
- const getResponsesWebSocketInitiator = (preparedHeaders) => {
2707
- return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
2708
- };
2709
- const createPooledResponsesWebSocketStream = (request) => runResponsesWebSocketRequest(request);
2710
- const buildResponsesWebSocketPayload = (payload, initiator) => {
2711
- const websocketPayload = {
2712
- ...payload,
2713
- type: "response.create",
2714
- initiator
2715
- };
2716
- delete websocketPayload.stream;
2717
- delete websocketPayload["background"];
2718
- delete websocketPayload.service_tier;
2719
- 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;
2720
2792
  };
2721
- const buildResponsesWebSocketUrl = (baseUrl) => {
2722
- const url = new URL(`${baseUrl.replace(/\/+$/u, "")}/responses`);
2723
- if (url.protocol === "https:") url.protocol = "wss:";
2724
- else if (url.protocol === "http:") url.protocol = "ws:";
2725
- return url.toString();
2726
- };
2727
- const responsesWebSocketPool = /* @__PURE__ */ new Map();
2728
- const responsesWebSocketActiveRequests = /* @__PURE__ */ new Map();
2729
- const runResponsesWebSocketRequest = async function* (request) {
2730
- const { entry, pooled } = getResponsesWebSocketRequestTarget(request);
2731
- const release = acquireResponsesWebSocketEntry(request.poolKey, entry, pooled);
2732
- try {
2733
- const websocket = await getReadyResponsesWebSocket(request.poolKey, entry, pooled);
2734
- websocket.send(JSON.stringify(request.payload));
2735
- for await (const data of createWebSocketMessageStream(websocket)) {
2736
- const chunk = createResponsesWebSocketStreamChunk(data);
2737
- yield chunk;
2738
- 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;
2739
2812
  }
2740
- removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2741
- throw new Error("Responses websocket ended without a terminal response");
2742
- } catch (error) {
2743
- removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2744
- throw toError(error);
2745
- } finally {
2746
- release();
2747
2813
  }
2814
+ return aggregated;
2748
2815
  };
2749
- const getResponsesWebSocketRequestTarget = (request) => {
2750
- if (getResponsesWebSocketActiveRequestCount(request.poolKey) > 0) return {
2751
- entry: createResponsesWebSocketEntry(request),
2752
- 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
+ }
2753
2824
  };
2754
- const existing = responsesWebSocketPool.get(request.poolKey);
2755
- if (existing && !existing.closed) {
2756
- clearResponsesWebSocketIdleTimer(existing);
2757
- return {
2758
- entry: existing,
2759
- pooled: true
2760
- };
2761
- }
2762
- const entry = createResponsesWebSocketEntry(request);
2763
- 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;
2764
2833
  return {
2765
- entry,
2766
- pooled: true
2834
+ type: "tool_use",
2835
+ id: toolId,
2836
+ name: toolName,
2837
+ input: parseFunctionCallArguments(call.arguments)
2767
2838
  };
2768
2839
  };
2769
- const createResponsesWebSocketEntry = (request) => {
2770
- const entry = {
2771
- closed: false,
2772
- idleTimer: null,
2773
- requestCount: 0,
2774
- websocketPromise: openResponsesWebSocket({
2775
- headers: request.headers,
2776
- url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
2777
- })
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)
2778
2848
  };
2779
- entry.websocketPromise.then((websocket) => {
2780
- websocket.addEventListener("close", () => {
2781
- removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2782
- });
2783
- websocket.addEventListener("error", () => {
2784
- removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2785
- });
2786
- }).catch(() => {
2787
- removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2788
- });
2789
- return entry;
2790
2849
  };
2791
- const acquireResponsesWebSocketEntry = (poolKey, entry, pooled) => {
2792
- clearResponsesWebSocketIdleTimer(entry);
2793
- incrementResponsesWebSocketActiveRequestCount(poolKey);
2794
- entry.requestCount += 1;
2795
- let released = false;
2796
- return () => {
2797
- if (released) return;
2798
- released = true;
2799
- entry.requestCount -= 1;
2800
- decrementResponsesWebSocketActiveRequestCount(poolKey);
2801
- if (entry.closed || entry.requestCount > 0) return;
2802
- if (pooled && responsesWebSocketPool.get(poolKey) === entry) {
2803
- scheduleResponsesWebSocketIdleClose(poolKey, entry);
2804
- return;
2805
- }
2806
- removeResponsesWebSocketPoolEntry(poolKey, entry);
2807
- };
2850
+ const resolveToolUseName = (call) => {
2851
+ if (typeof call.namespace === "string" && call.namespace.length > 0) return call.namespace;
2852
+ return call.name;
2808
2853
  };
2809
- const getReadyResponsesWebSocket = async (poolKey, entry, pooled) => {
2810
- if (entry.closed) throw new Error("Responses websocket became unavailable before the request started");
2811
- const websocket = await entry.websocketPromise;
2812
- if (entry.closed || pooled && responsesWebSocketPool.get(poolKey) !== entry) throw new Error("Responses websocket became unavailable before the request started");
2813
- if (websocket.readyState !== WebSocket.OPEN) {
2814
- removeResponsesWebSocketPoolEntry(poolKey, entry);
2815
- throw new Error("Responses websocket became unavailable before the request started");
2816
- }
2817
- return websocket;
2818
- };
2819
- const scheduleResponsesWebSocketIdleClose = (poolKey, entry) => {
2820
- clearResponsesWebSocketIdleTimer(entry);
2821
- entry.idleTimer = setTimeout(() => {
2822
- removeResponsesWebSocketPoolEntry(poolKey, entry);
2823
- }, RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS);
2824
- unrefTimer(entry.idleTimer);
2825
- };
2826
- const clearResponsesWebSocketIdleTimer = (entry) => {
2827
- if (entry.idleTimer) {
2828
- clearTimeout(entry.idleTimer);
2829
- entry.idleTimer = null;
2830
- }
2831
- };
2832
- const getResponsesWebSocketActiveRequestCount = (poolKey) => responsesWebSocketActiveRequests.get(poolKey) ?? 0;
2833
- const incrementResponsesWebSocketActiveRequestCount = (poolKey) => {
2834
- responsesWebSocketActiveRequests.set(poolKey, getResponsesWebSocketActiveRequestCount(poolKey) + 1);
2835
- };
2836
- const decrementResponsesWebSocketActiveRequestCount = (poolKey) => {
2837
- const nextCount = getResponsesWebSocketActiveRequestCount(poolKey) - 1;
2838
- if (nextCount <= 0) {
2839
- responsesWebSocketActiveRequests.delete(poolKey);
2840
- return;
2841
- }
2842
- responsesWebSocketActiveRequests.set(poolKey, nextCount);
2843
- };
2844
- const removeResponsesWebSocketPoolEntry = (poolKey, entry) => {
2845
- if (responsesWebSocketPool.get(poolKey) === entry) responsesWebSocketPool.delete(poolKey);
2846
- if (entry.closed) return;
2847
- entry.closed = true;
2848
- clearResponsesWebSocketIdleTimer(entry);
2849
- entry.websocketPromise.then(closeResponsesWebSocket).catch(() => {});
2850
- };
2851
- const unrefTimer = (timer) => {
2852
- if (typeof timer === "object" && "unref" in timer && typeof timer.unref === "function") timer.unref();
2853
- };
2854
- const createResponsesWebSocketError = (message, event) => {
2855
- const reason = event?.error ?? event?.message;
2856
- if (reason === void 0 || reason === "") return new Error(message);
2857
- const cause = toError(reason);
2858
- return new Error(`${message}: ${cause.message}`, { cause });
2859
- };
2860
- const openResponsesWebSocket = async ({ headers, url }) => await new Promise((resolve, reject) => {
2861
- const dispatcher = getProxyEnvDispatcher();
2862
- const websocket = new WebSocket(url, dispatcher ? {
2863
- dispatcher,
2864
- headers
2865
- } : { headers });
2866
- const cleanup = () => {
2867
- websocket.removeEventListener("open", onOpen);
2868
- websocket.removeEventListener("error", onError);
2869
- };
2870
- const onOpen = () => {
2871
- cleanup();
2872
- resolve(websocket);
2873
- };
2874
- const onError = (event) => {
2875
- cleanup();
2876
- reject(createResponsesWebSocketError("Failed to create responses websocket", event));
2877
- };
2878
- websocket.addEventListener("open", onOpen);
2879
- websocket.addEventListener("error", onError);
2880
- });
2881
- const createWebSocketMessageStream = async function* (websocket) {
2882
- const queue = [];
2883
- let closed = false;
2884
- let error = null;
2885
- let notify = null;
2886
- const wake = () => {
2887
- notify?.();
2888
- notify = null;
2889
- };
2890
- const onMessage = (event) => {
2891
- queue.push(normalizeWebSocketMessageData(event.data));
2892
- wake();
2893
- };
2894
- const onClose = () => {
2895
- closed = true;
2896
- wake();
2897
- };
2898
- const onError = (event) => {
2899
- error = createResponsesWebSocketError("Responses websocket stream error", event);
2900
- 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
+ })
2901
2863
  };
2902
- websocket.addEventListener("message", onMessage);
2903
- websocket.addEventListener("close", onClose);
2904
- websocket.addEventListener("error", onError);
2864
+ };
2865
+ const parseFunctionCallArguments = (rawArguments) => {
2866
+ if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
2905
2867
  try {
2906
- while (true) {
2907
- const item = queue.shift();
2908
- if (item) {
2909
- yield await item;
2910
- continue;
2911
- }
2912
- if (error) throw toError(error);
2913
- if (closed) break;
2914
- await new Promise((resolve) => {
2915
- notify = resolve;
2916
- });
2917
- }
2918
- } finally {
2919
- websocket.removeEventListener("message", onMessage);
2920
- websocket.removeEventListener("close", onClose);
2921
- websocket.removeEventListener("error", onError);
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
+ });
2922
2876
  }
2877
+ return { raw_arguments: rawArguments };
2923
2878
  };
2924
- const normalizeWebSocketMessageData = async (data) => {
2925
- if (typeof data === "string") return data;
2926
- if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
2927
- if (ArrayBuffer.isView(data)) {
2928
- const view = data;
2929
- return new TextDecoder().decode(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
2930
- }
2931
- if (isTextReadable(data)) return await data.text();
2932
- return String(data);
2879
+ const parseToolSearchArguments = (argumentsValue) => {
2880
+ return formatToolSearchBridgeArguments(argumentsValue);
2933
2881
  };
2934
- const isTextReadable = (value) => {
2935
- if (!value || typeof value !== "object" || !("text" in value)) return false;
2936
- return typeof value.text === "function";
2882
+ const fallbackContentBlocks = (outputText) => {
2883
+ if (!outputText) return [];
2884
+ return [{
2885
+ type: "text",
2886
+ text: outputText
2887
+ }];
2937
2888
  };
2938
- const toError = (value) => {
2939
- if (value instanceof Error) return value;
2940
- return new Error(String(value));
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";
2895
+ }
2896
+ if (status === "incomplete") {
2897
+ if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
2898
+ if (incompleteDetails?.reason === "content_filter") return "end_turn";
2899
+ }
2900
+ return null;
2941
2901
  };
2942
- const getHeaderValue = (headers, headerName) => {
2943
- const normalizedHeaderName = headerName.toLowerCase();
2944
- return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
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 }
2909
+ };
2945
2910
  };
2946
- const encodePoolKeyPart = (value) => encodeURIComponent(value);
2947
- const createResponsesWebSocketStreamChunk = (data) => {
2948
- if (data === "[DONE]") return { data };
2949
- try {
2950
- const parsed = JSON.parse(data);
2951
- if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
2952
- return {
2953
- data: JSON.stringify(parsed),
2954
- event: typeof parsed.type === "string" ? parsed.type : void 0,
2955
- id: typeof parsed.id === "string" ? parsed.id : void 0
2956
- };
2957
- } catch {
2958
- return { data };
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;
2959
2934
  }
2935
+ return "";
2960
2936
  };
2961
- const isTerminalResponsesStreamChunk = (chunk) => {
2962
- if (!chunk.data || chunk.data === "[DONE]") return false;
2963
- try {
2964
- const parsed = JSON.parse(chunk.data);
2965
- return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
2966
- } catch {
2967
- return false;
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";
2968
2944
  }
2969
2945
  };
2970
- const consumeResponsesWebSocketStream = async (stream) => {
2971
- for await (const chunk of stream) {
2972
- if (!chunk.data || chunk.data === "[DONE]") continue;
2973
- const event = JSON.parse(chunk.data);
2974
- if (event.type === "error") throw new Error(event.message);
2975
- if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return event.response;
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
+ };
2955
+ continue;
2956
+ }
2957
+ if (char !== " ") count = 0;
2976
2958
  }
2977
- throw new Error("Responses websocket ended without a terminal response");
2959
+ return {
2960
+ nextCount: count,
2961
+ exceeded: false
2962
+ };
2978
2963
  };
2979
- const closeResponsesWebSocket = (websocket) => {
2980
- if (websocket.readyState === WebSocket.CONNECTING || websocket.readyState === WebSocket.OPEN) websocket.close();
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);
2990
+ default: return [];
2991
+ }
2981
2992
  };
2982
- //#endregion
2983
- //#region src/routes/messages/responses-translation.ts
2984
- const MESSAGE_TYPE = "message";
2985
- const COMPACTION_SIGNATURE_PREFIX = "cm1#";
2986
- const COMPACTION_SIGNATURE_SEPARATOR = "@";
2987
- const THINKING_TEXT = "Thinking...";
2988
- const buildPromptCacheKey = (basePromptCacheKey, subagentAgentId) => {
2989
- if (!basePromptCacheKey) return null;
2990
- const normalizedSubagentAgentId = subagentAgentId?.trim() || null;
2991
- if (!normalizedSubagentAgentId) return basePromptCacheKey;
2992
- return `${basePromptCacheKey}:agent:${normalizedSubagentAgentId}`;
2993
+ const handleResponseCreated = (rawEvent, state) => {
2994
+ return messageStart(state, rawEvent.response);
2993
2995
  };
2994
- const translateAnthropicMessagesToResponsesPayload = (payload, subagentAgentId) => {
2995
- const input = [];
2996
- const applyPhase = shouldApplyPhase(payload.model);
2997
- const toolSearchEnabled = shouldEnableResponsesToolSearch({
2998
- model: payload.model,
2999
- tools: payload.tools
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
3000
3006
  });
3001
- const translationState = {
3002
- originalTools: payload.tools ?? [],
3003
- toolSearchEnabled,
3004
- toolUseNameById: /* @__PURE__ */ new Map()
3005
- };
3006
- for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase, translationState));
3007
- const hasOriginalTools = Array.isArray(payload.tools) && payload.tools.length > 0;
3008
- const translatedTools = convertAnthropicTools(payload.tools, toolSearchEnabled);
3009
- const toolChoice = convertAnthropicToolChoice(payload.tool_choice, toolSearchEnabled);
3010
- const { sessionId: metadataPromptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
3011
- const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
3012
- const promptCacheKey = buildPromptCacheKey(metadataPromptCacheKey ?? sessionAffinity, subagentAgentId);
3013
- const responsesPayload = {
3014
- model: payload.model,
3015
- input,
3016
- instructions: translateSystemPrompt(payload.system, payload.model),
3017
- temperature: 1,
3018
- top_p: payload.top_p ?? null,
3019
- max_output_tokens: Math.max(payload.max_tokens, 12800),
3020
- tools: translatedTools,
3021
- tool_choice: toolChoice,
3022
- metadata: payload.metadata ? { ...payload.metadata } : null,
3023
- stream: payload.stream ?? null,
3024
- store: false,
3025
- parallel_tool_calls: true,
3026
- reasoning: {
3027
- effort: getReasoningEffortForModel(payload.model),
3028
- summary: "detailed"
3029
- },
3030
- include: ["reasoning.encrypted_content"]
3031
- };
3032
- if (hasOriginalTools) responsesPayload.prompt_cache_key = promptCacheKey;
3033
- return responsesPayload;
3034
- };
3035
- const encodeCompactionCarrierSignature = (compaction) => {
3036
- return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
3037
- };
3038
- const decodeCompactionCarrierSignature = (signature) => {
3039
- if (signature.startsWith(COMPACTION_SIGNATURE_PREFIX)) {
3040
- const raw = signature.slice(4);
3041
- const separatorIndex = raw.indexOf(COMPACTION_SIGNATURE_SEPARATOR);
3042
- if (separatorIndex <= 0 || separatorIndex === raw.length - 1) return;
3043
- const encrypted_content = raw.slice(0, separatorIndex);
3044
- const id = raw.slice(separatorIndex + 1);
3045
- if (!encrypted_content) return;
3046
- return {
3047
- id,
3048
- encrypted_content
3049
- };
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);
3050
3017
  }
3018
+ return events;
3051
3019
  };
3052
- const translateMessage = (message, model, applyPhase, state) => {
3053
- if (message.role === "user") return translateUserMessage(message, state);
3054
- return translateAssistantMessage(message, model, applyPhase, state);
3055
- };
3056
- const translateUserMessage = (message, state) => {
3057
- if (typeof message.content === "string") return [createMessage("user", message.content)];
3058
- if (!Array.isArray(message.content)) return [];
3059
- const items = [];
3060
- const pendingContent = [];
3061
- for (const block of message.content) {
3062
- if (block.type === "tool_result") {
3063
- flushPendingContent(pendingContent, items, { role: "user" });
3064
- items.push(createToolCallOutput(block, state));
3065
- continue;
3066
- }
3067
- const converted = translateUserContentBlock(block);
3068
- if (converted.length > 0) pendingContent.push(...converted);
3069
- }
3070
- flushPendingContent(pendingContent, items, { role: "user" });
3071
- return items;
3072
- };
3073
- const translateAssistantMessage = (message, model, applyPhase, state) => {
3074
- const assistantPhase = resolveAssistantPhase(model, message.content, applyPhase);
3075
- if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
3076
- if (!Array.isArray(message.content)) return [];
3077
- const items = [];
3078
- const pendingContent = [];
3079
- for (const block of message.content) {
3080
- if (block.type === "tool_use") {
3081
- state.toolUseNameById.set(block.id, block.name);
3082
- flushPendingContent(pendingContent, items, {
3083
- role: "assistant",
3084
- phase: assistantPhase
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
+ }
3085
3041
  });
3086
- items.push(createToolCall(block, state));
3087
- continue;
3042
+ state.blockHasDelta.add(blockIndex);
3088
3043
  }
3089
- if (block.type === "thinking" && block.signature) {
3090
- const compactionContent = createCompactionContent(block);
3091
- if (compactionContent) {
3092
- flushPendingContent(pendingContent, items, {
3093
- role: "assistant",
3094
- phase: assistantPhase
3095
- });
3096
- items.push(compactionContent);
3097
- continue;
3044
+ state.functionCallStateByOutputIndex.delete(outputIndex);
3045
+ return events;
3046
+ }
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
3098
3056
  }
3099
- if (block.signature.includes("@")) {
3100
- flushPendingContent(pendingContent, items, {
3101
- role: "assistant",
3102
- phase: assistantPhase
3103
- });
3104
- items.push(createReasoningContent(block));
3105
- continue;
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
+ })
3106
3067
  }
3107
- }
3108
- const converted = translateAssistantContentBlock(block);
3109
- if (converted) pendingContent.push(converted);
3068
+ });
3069
+ state.blockHasDelta.add(blockIndex);
3070
+ return events;
3110
3071
  }
3111
- flushPendingContent(pendingContent, items, {
3112
- role: "assistant",
3113
- phase: assistantPhase
3114
- });
3115
- return items;
3116
- };
3117
- const translateUserContentBlock = (block) => {
3118
- switch (block.type) {
3119
- case "text": return [createTextContent(block.text)];
3120
- case "image": return [createImageContent(block)];
3121
- case "document": return [createFileContent(block)];
3122
- default: return [];
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);
3123
3093
  }
3094
+ return events;
3124
3095
  };
3125
- const translateAssistantContentBlock = (block) => {
3126
- switch (block.type) {
3127
- case "text": return createOutPutTextContent(block.text);
3128
- default: return;
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;
3120
+ };
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);
3129
3139
  }
3140
+ state.functionCallStateByOutputIndex.delete(outputIndex);
3141
+ return events;
3130
3142
  };
3131
- const flushPendingContent = (pendingContent, target, message) => {
3132
- if (pendingContent.length === 0) return;
3133
- const messageContent = [...pendingContent];
3134
- target.push(createMessage(message.role, messageContent, message.phase));
3135
- pendingContent.length = 0;
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
3160
+ }
3161
+ });
3162
+ state.blockHasDelta.add(blockIndex);
3163
+ return events;
3136
3164
  };
3137
- const createMessage = (role, content, phase) => ({
3138
- type: MESSAGE_TYPE,
3139
- role,
3140
- content,
3141
- ...role === "assistant" && phase ? { phase } : {}
3142
- });
3143
- const resolveAssistantPhase = (_model, content, applyPhase) => {
3144
- if (!applyPhase) return;
3145
- if (typeof content === "string") return "final_answer";
3146
- if (!Array.isArray(content)) return;
3147
- if (!content.some((block) => block.type === "text")) return;
3148
- return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
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
3176
+ }
3177
+ });
3178
+ state.blockHasDelta.add(blockIndex);
3179
+ return events;
3149
3180
  };
3150
- const shouldApplyPhase = (_model) => {
3151
- return true;
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
3192
+ }
3193
+ });
3194
+ return events;
3152
3195
  };
3153
- const createTextContent = (text) => ({
3154
- type: "input_text",
3155
- text
3156
- });
3157
- const createOutPutTextContent = (text) => ({
3158
- type: "output_text",
3159
- text
3160
- });
3161
- const createImageContent = (block) => ({
3162
- type: "input_image",
3163
- image_url: `data:${block.source.media_type};base64,${block.source.data}`,
3164
- detail: "auto"
3165
- });
3166
- const createFileContent = (block) => ({
3167
- type: "input_file",
3168
- file_data: `data:${block.source.media_type};base64,${block.source.data}`,
3169
- filename: block.title ?? "document.pdf"
3170
- });
3171
- const createReasoningContent = (block) => {
3172
- const { encryptedContent, id } = parseReasoningSignature(block.signature);
3173
- const thinking = block.thinking === "Thinking..." ? "" : block.thinking;
3174
- return {
3175
- id,
3176
- type: "reasoning",
3177
- summary: thinking ? [{
3178
- type: "summary_text",
3179
- text: thinking
3180
- }] : [],
3181
- encrypted_content: encryptedContent
3182
- };
3183
- };
3184
- const createCompactionContent = (block) => {
3185
- const compaction = decodeCompactionCarrierSignature(block.signature);
3186
- if (!compaction) return;
3187
- return {
3188
- id: compaction.id,
3189
- type: "compaction",
3190
- encrypted_content: compaction.encrypted_content
3191
- };
3192
- };
3193
- const parseReasoningSignature = (signature) => {
3194
- const splitIndex = signature.lastIndexOf("@");
3195
- if (splitIndex <= 0 || splitIndex === signature.length - 1) return {
3196
- encryptedContent: signature,
3197
- id: ""
3198
- };
3199
- return {
3200
- encryptedContent: signature.slice(0, splitIndex),
3201
- id: signature.slice(splitIndex + 1)
3202
- };
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
3212
+ }
3213
+ });
3214
+ return events;
3203
3215
  };
3204
- const createFunctionToolCall = (block, state) => ({
3205
- type: "function_call",
3206
- call_id: block.id,
3207
- name: block.name,
3208
- arguments: JSON.stringify(block.input),
3209
- status: "completed",
3210
- ...state.toolSearchEnabled && isDeferredToolName(block.name) ? { namespace: block.name } : {}
3211
- });
3212
- const createToolSearchCall = (block) => ({
3213
- type: "tool_search_call",
3214
- call_id: block.id,
3215
- arguments: normalizeToolSearchBridgeArguments(block.input),
3216
- execution: "client",
3217
- status: "completed"
3218
- });
3219
- const createToolCall = (block, state) => {
3220
- if (state.toolSearchEnabled && isBridgeToolSearchName(block.name)) return createToolSearchCall(block);
3221
- return createFunctionToolCall(block, state);
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;
3222
3234
  };
3223
- const createFunctionCallOutput = (block) => ({
3224
- type: "function_call_output",
3225
- call_id: block.tool_use_id,
3226
- output: convertToolResultContent(block.content),
3227
- status: block.is_error ? "incomplete" : "completed"
3228
- });
3229
- const createToolCallOutput = (block, state) => {
3230
- const toolUseName = state.toolUseNameById.get(block.tool_use_id);
3231
- if (state.toolSearchEnabled && isBridgeToolSearchName(toolUseName ?? "")) return createToolSearchOutput(block, state.originalTools);
3232
- return createFunctionCallOutput(block);
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;
3233
3243
  };
3234
- const createToolSearchOutput = (block, originalTools) => {
3235
- const referencedToolNames = resolveToolSearchReferencedToolNames(block.content, originalTools);
3236
- return {
3237
- type: "tool_search_output",
3238
- call_id: block.tool_use_id,
3239
- tools: referencedToolNames.map((toolName) => convertDeferredToolToNamespace(resolveDeferredTool(toolName, originalTools))),
3240
- execution: "client",
3241
- status: block.is_error ? "incomplete" : "completed"
3242
- };
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)];
3243
3248
  };
3244
- const resolveToolSearchReferencedToolNames = (content, originalTools) => {
3245
- const explicitReferences = extractToolReferenceNames(content);
3246
- if (explicitReferences.length > 0) return uniqueToolNames(explicitReferences);
3247
- const sentinel = extractMcpToolSearchSentinel(content);
3248
- if (sentinel) return selectDeferredToolsByNames(sentinel.names, originalTools).map((tool) => tool.name);
3249
- return [];
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;
3250
3255
  };
3251
- const extractToolReferenceNames = (content) => {
3252
- if (!Array.isArray(content)) return [];
3253
- return content.flatMap((block) => block.type === "tool_reference" ? [block.tool_name] : []);
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
+ }
3275
+ }
3276
+ }];
3254
3277
  };
3255
- const extractMcpToolSearchSentinel = (content) => {
3256
- if (typeof content === "string") return parseMcpToolSearchSentinel(content);
3257
- for (const block of content) {
3258
- if (block.type !== "text") continue;
3259
- const sentinel = parseMcpToolSearchSentinel(block.text);
3260
- if (sentinel) return sentinel;
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);
3261
3286
  }
3262
- return null;
3263
- };
3264
- const resolveDeferredTool = (toolName, originalTools) => {
3265
- const tool = originalTools.find((candidate) => candidate.name === toolName);
3266
- if (tool && isDeferredToolName(tool.name)) return tool;
3267
- throw createInvalidRequestError(`Tool reference '${toolName}' has no corresponding deferred tool definition`);
3268
- };
3269
- const uniqueToolNames = (toolNames) => [...new Set(toolNames)];
3270
- const createInvalidRequestError = (message) => new HTTPError(message, new Response(JSON.stringify({ error: {
3271
- message,
3272
- type: "invalid_request_error"
3273
- } }), {
3274
- status: 400,
3275
- headers: { "content-type": "application/json" }
3276
- }));
3277
- const translateSystemPrompt = (system, model) => {
3278
- if (!system) return null;
3279
- const extraPrompt = getExtraPromptForModel(model);
3280
- if (typeof system === "string") return system + extraPrompt;
3281
- const text = system.map((block, index) => {
3282
- if (index === 0) return block.text + "\n\n" + extraPrompt + "\n\n";
3283
- return block.text;
3284
- }).join(" ");
3285
- return text.length > 0 ? text : null;
3286
- };
3287
- const convertAnthropicTools = (tools, toolSearchEnabled) => {
3288
- if (!tools || tools.length === 0) return null;
3289
- const converted = [];
3290
- let addedToolSearch = false;
3291
- const searchableToolNames = toolSearchEnabled ? listDeferredToolNames(tools) : [];
3292
- for (const tool of tools) {
3293
- if (isBridgeToolSearchName(tool.name)) {
3294
- if (toolSearchEnabled && !addedToolSearch) {
3295
- converted.push(createResponsesToolSearchDefinition(searchableToolNames));
3296
- addedToolSearch = true;
3287
+ if (!state.openBlocks.has(blockIndex)) {
3288
+ closeOpenBlocks(state, events);
3289
+ events.push({
3290
+ type: "content_block_start",
3291
+ index: blockIndex,
3292
+ content_block: {
3293
+ type: "text",
3294
+ text: ""
3297
3295
  }
3298
- continue;
3299
- }
3300
- if (toolSearchEnabled && isDeferredToolName(tool.name)) {
3301
- converted.push(convertDeferredToolToNamespace(tool));
3302
- continue;
3303
- }
3304
- converted.push(convertToolToFunction(tool));
3296
+ });
3297
+ state.openBlocks.add(blockIndex);
3305
3298
  }
3306
- return converted;
3299
+ return blockIndex;
3307
3300
  };
3308
- const createResponsesToolSearchDefinition = (searchableToolNames) => ({
3309
- type: "tool_search",
3310
- execution: "client",
3311
- description: "Load deferred tools by exact name before using them. Return only the searchable tool names you need for the next step.",
3312
- parameters: {
3313
- type: "object",
3314
- properties: { names: {
3315
- type: "array",
3316
- description: "Exact deferred tool names to load.",
3317
- items: {
3318
- type: "string",
3319
- enum: searchableToolNames
3320
- },
3321
- minItems: 1
3322
- } },
3323
- required: ["names"],
3324
- additionalProperties: false
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);
3325
3308
  }
3326
- });
3327
- const convertToolToFunction = (tool) => ({
3328
- type: "function",
3329
- name: tool.name,
3330
- parameters: normalizeToolSchema(tool.input_schema),
3331
- strict: false,
3332
- ...tool.description ? { description: tool.description } : {}
3333
- });
3334
- const convertDeferredToolToNamespace = (tool) => ({
3335
- type: "namespace",
3336
- name: tool.name,
3337
- ...tool.description ? { description: tool.description } : {},
3338
- tools: [{
3339
- type: "function",
3340
- name: tool.name,
3341
- parameters: normalizeToolSchema(tool.input_schema),
3342
- strict: false,
3343
- defer_loading: true,
3344
- ...tool.description ? { description: tool.description } : {}
3345
- }]
3346
- });
3347
- const convertAnthropicToolChoice = (choice, toolSearchEnabled) => {
3348
- if (!choice) return "auto";
3349
- switch (choice.type) {
3350
- case "auto": return "auto";
3351
- case "any": return "required";
3352
- case "tool":
3353
- if (toolSearchEnabled && choice.name && isBridgeToolSearchName(choice.name)) return "auto";
3354
- return choice.name ? {
3355
- type: "function",
3356
- name: choice.name
3357
- } : "auto";
3358
- case "none": return "none";
3359
- default: return "auto";
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);
3360
3320
  }
3321
+ return blockIndex;
3361
3322
  };
3362
- const translateResponsesResultToAnthropic = (response, options) => {
3363
- const contentBlocks = mapOutputToAnthropicContent(response.output, options);
3364
- const usage = mapResponsesUsage(response);
3365
- let anthropicContent = fallbackContentBlocks(response.output_text);
3366
- if (contentBlocks.length > 0) anthropicContent = contentBlocks;
3367
- const stopReason = mapResponsesStopReason(response);
3368
- return {
3369
- id: response.id,
3370
- type: "message",
3371
- role: "assistant",
3372
- content: anthropicContent,
3373
- model: response.model,
3374
- stop_reason: stopReason,
3375
- stop_sequence: null,
3376
- usage
3377
- };
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);
3378
3331
  };
3379
- const mapOutputToAnthropicContent = (output, options) => {
3380
- const contentBlocks = [];
3381
- for (const item of output) switch (item.type) {
3382
- case "reasoning": {
3383
- const thinkingText = extractReasoningText(item);
3384
- if (thinkingText.length > 0) contentBlocks.push({
3385
- type: "thinking",
3386
- thinking: thinkingText,
3387
- signature: (item.encrypted_content ?? "") + "@" + item.id
3388
- });
3389
- break;
3390
- }
3391
- case "function_call": {
3392
- const toolUseBlock = createToolUseContentBlock(item);
3393
- if (toolUseBlock) contentBlocks.push(toolUseBlock);
3394
- break;
3395
- }
3396
- case "tool_search_call": {
3397
- const toolUseBlock = createToolSearchUseContentBlock(item, options?.toolSearchName);
3398
- if (toolUseBlock) contentBlocks.push(toolUseBlock);
3399
- break;
3400
- }
3401
- case "tool_search_output": break;
3402
- case "message": {
3403
- const combinedText = combineMessageTextContent(item.content);
3404
- if (combinedText.length > 0) contentBlocks.push({
3405
- type: "text",
3406
- text: combinedText
3407
- });
3408
- break;
3409
- }
3410
- case "compaction": {
3411
- const compactionBlock = createCompactionThinkingBlock(item);
3412
- if (compactionBlock) contentBlocks.push(compactionBlock);
3413
- break;
3414
- }
3415
- default: {
3416
- const combinedText = combineMessageTextContent(item.content);
3417
- if (combinedText.length > 0) contentBlocks.push({
3418
- type: "text",
3419
- text: combinedText
3420
- });
3421
- }
3422
- }
3423
- return contentBlocks;
3332
+ const closeOpenBlocks = (state, events) => {
3333
+ for (const blockIndex of state.openBlocks) closeBlockIfOpen(state, blockIndex, events);
3424
3334
  };
3425
- const combineMessageTextContent = (content) => {
3426
- if (!Array.isArray(content)) return "";
3427
- let aggregated = "";
3428
- for (const block of content) {
3429
- if (isResponseOutputText(block)) {
3430
- aggregated += block.text;
3431
- continue;
3432
- }
3433
- if (isResponseOutputRefusal(block)) {
3434
- aggregated += block.refusal;
3435
- continue;
3436
- }
3437
- if (typeof block.text === "string") {
3438
- aggregated += block.text;
3439
- continue;
3440
- }
3441
- if (typeof block.reasoning === "string") {
3442
- aggregated += block.reasoning;
3443
- continue;
3444
- }
3445
- }
3446
- return aggregated;
3335
+ const closeAllOpenBlocks = (state, events) => {
3336
+ closeOpenBlocks(state, events);
3337
+ state.functionCallStateByOutputIndex.clear();
3447
3338
  };
3448
- const extractReasoningText = (item) => {
3449
- const segments = [];
3450
- const collectFromBlocks = (blocks) => {
3451
- if (!Array.isArray(blocks)) return;
3452
- for (const block of blocks) if (typeof block.text === "string") {
3453
- segments.push(block.text);
3454
- continue;
3455
- }
3456
- };
3457
- if (!item.summary || item.summary.length === 0) return THINKING_TEXT;
3458
- collectFromBlocks(item.summary);
3459
- return segments.join("").trim();
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;
3460
3378
  };
3461
- const createToolUseContentBlock = (call) => {
3462
- const toolId = call.call_id;
3463
- const toolName = resolveToolUseName(call);
3464
- if (!toolName || !toolId) return null;
3465
- return {
3466
- type: "tool_use",
3467
- id: toolId,
3468
- name: toolName,
3469
- input: parseFunctionCallArguments(call.arguments)
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: ""
3470
3387
  };
3471
- };
3472
- const createToolSearchUseContentBlock = (call, toolSearchName = BRIDGE_TOOL_SEARCH_NAME) => {
3473
- const toolId = call.call_id;
3474
- if (!toolId) return null;
3388
+ if (itemType !== "function_call") return;
3475
3389
  return {
3476
- type: "tool_use",
3477
- id: toolId,
3478
- name: toolSearchName,
3479
- input: parseToolSearchArguments(call.arguments)
3390
+ outputIndex: rawEvent.output_index,
3391
+ toolCallId: item.call_id,
3392
+ name: resolveToolUseName(item),
3393
+ initialArguments: item.arguments
3480
3394
  };
3481
3395
  };
3482
- const resolveToolUseName = (call) => {
3483
- if (typeof call.namespace === "string" && call.namespace.length > 0) return call.namespace;
3484
- return call.name;
3396
+ const stringifyToolSearchArguments = (argumentsValue) => {
3397
+ try {
3398
+ return JSON.stringify(formatToolSearchBridgeArguments(argumentsValue));
3399
+ } catch {
3400
+ return;
3401
+ }
3485
3402
  };
3486
- const createCompactionThinkingBlock = (item) => {
3487
- if (!item.id || !item.encrypted_content) return null;
3403
+ const responsesUtilsDependencies = {
3404
+ isResponsesApiContextManagementModel,
3405
+ isResponsesApiWebSocketEnabled
3406
+ };
3407
+ const getResponsesRequestOptions = (payload) => {
3488
3408
  return {
3489
- type: "thinking",
3490
- thinking: THINKING_TEXT,
3491
- signature: encodeCompactionCarrierSignature({
3492
- id: item.id,
3493
- encrypted_content: item.encrypted_content
3494
- })
3409
+ vision: hasVisionInput(payload),
3410
+ initiator: hasAgentInitiator(payload) ? "agent" : "user"
3495
3411
  };
3496
3412
  };
3497
- const parseFunctionCallArguments = (rawArguments) => {
3498
- if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
3499
- try {
3500
- const parsed = JSON.parse(rawArguments);
3501
- if (Array.isArray(parsed)) return { arguments: parsed };
3502
- if (parsed && typeof parsed === "object") return parsed;
3503
- } catch (error) {
3504
- consola.warn("Failed to parse function call arguments", {
3505
- error,
3506
- rawArguments
3507
- });
3508
- }
3509
- return { raw_arguments: rawArguments };
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";
3418
+ return null;
3510
3419
  };
3511
- const parseToolSearchArguments = (argumentsValue) => {
3512
- return formatToolSearchBridgeArguments(argumentsValue);
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";
3513
3425
  };
3514
- const fallbackContentBlocks = (outputText) => {
3515
- if (!outputText) return [];
3516
- return [{
3517
- type: "text",
3518
- text: outputText
3519
- }];
3426
+ const hasVisionInput = (payload) => {
3427
+ return getPayloadItems(payload).some((item) => containsVisionContent(item));
3520
3428
  };
3521
- const mapResponsesStopReason = (response) => {
3522
- const { status, incomplete_details: incompleteDetails } = response;
3523
- if (status === "completed") {
3524
- if (response.output.some((item) => item.type === "function_call" || item.type === "tool_search_call")) return "tool_use";
3525
- return "end_turn";
3526
- }
3527
- if (status === "incomplete") {
3528
- if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
3529
- if (incompleteDetails?.reason === "content_filter") return "end_turn";
3530
- }
3531
- return null;
3429
+ const resolveResponsesCompactThreshold = (maxPromptTokens) => {
3430
+ if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
3431
+ return 5e4;
3532
3432
  };
3533
- const mapResponsesUsage = (response) => {
3534
- const inputTokens = response.usage?.input_tokens ?? 0;
3535
- const outputTokens = response.usage?.output_tokens ?? 0;
3536
- return {
3537
- input_tokens: inputTokens - (response.usage?.input_tokens_details?.cached_tokens ?? 0),
3538
- output_tokens: outputTokens,
3539
- ...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
3540
- };
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));
3541
3441
  };
3542
- const isRecord = (value) => typeof value === "object" && value !== null;
3543
- const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
3544
- const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
3545
- const convertToolResultContent = (content) => {
3546
- if (typeof content === "string") return content;
3547
- if (Array.isArray(content)) {
3548
- const result = [];
3549
- for (const block of content) switch (block.type) {
3550
- case "text":
3551
- result.push(createTextContent(block.text));
3552
- break;
3553
- case "image":
3554
- result.push(createImageContent(block));
3555
- break;
3556
- case "document":
3557
- result.push(createFileContent(block));
3558
- break;
3559
- case "tool_reference":
3560
- result.push(createTextContent(`Tool ${block.tool_name} loaded`));
3561
- break;
3562
- default: break;
3563
- }
3564
- return result;
3565
- }
3566
- return "";
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);
3567
3447
  };
3568
- //#endregion
3569
- //#region src/routes/messages/responses-stream-translation.ts
3570
- const MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE = 20;
3571
- var FunctionCallArgumentsValidationError = class extends Error {
3572
- constructor(message) {
3573
- super(message);
3574
- this.name = "FunctionCallArgumentsValidationError";
3575
- }
3448
+ const getLatestCompactionMessageIndex = (input) => {
3449
+ for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
3576
3450
  };
3577
- const updateWhitespaceRunState = (previousCount, chunk) => {
3578
- let count = previousCount;
3579
- for (const char of chunk) {
3580
- if (char === "\r" || char === "\n" || char === " ") {
3581
- count += 1;
3582
- if (count > MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE) return {
3583
- nextCount: count,
3584
- exceeded: true
3585
- };
3586
- continue;
3587
- }
3588
- if (char !== " ") count = 0;
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"
3589
3499
  }
3500
+ ];
3501
+ function normalizeCodexModel(model) {
3502
+ const supportsVision = model.input.includes("image");
3590
3503
  return {
3591
- nextCount: count,
3592
- 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"
3593
3537
  };
3594
- };
3595
- const createResponsesStreamState = (options) => ({
3596
- messageStartSent: false,
3597
- messageCompleted: false,
3598
- nextContentBlockIndex: 0,
3599
- blockIndexByKey: /* @__PURE__ */ new Map(),
3600
- openBlocks: /* @__PURE__ */ new Set(),
3601
- blockHasDelta: /* @__PURE__ */ new Set(),
3602
- functionCallStateByOutputIndex: /* @__PURE__ */ new Map(),
3603
- toolSearchName: options?.toolSearchName ?? "mcp__tool_search__search"
3604
- });
3605
- const translateResponsesStreamEvent = (rawEvent, state) => {
3606
- switch (rawEvent.type) {
3607
- case "response.created": return handleResponseCreated(rawEvent, state);
3608
- case "response.output_item.added": return handleOutputItemAdded$1(rawEvent, state);
3609
- case "response.reasoning_summary_text.delta": return handleReasoningSummaryTextDelta(rawEvent, state);
3610
- case "response.output_text.delta": return handleOutputTextDelta(rawEvent, state);
3611
- case "response.reasoning_summary_text.done": return handleReasoningSummaryTextDone(rawEvent, state);
3612
- case "response.output_text.done": return handleOutputTextDone(rawEvent, state);
3613
- case "response.output_item.done": return handleOutputItemDone$1(rawEvent, state);
3614
- case "response.function_call_arguments.delta": return handleFunctionCallArgumentsDelta(rawEvent, state);
3615
- case "response.function_call_arguments.done": return handleFunctionCallArgumentsDone(rawEvent, state);
3616
- case "response.completed":
3617
- case "response.incomplete": return handleResponseCompleted(rawEvent, state);
3618
- case "response.failed": return handleResponseFailed(rawEvent, state);
3619
- case "error": return handleErrorEvent(rawEvent, state);
3620
- 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;
3621
3573
  }
3622
- };
3623
- const handleResponseCreated = (rawEvent, state) => {
3624
- return messageStart(state, rawEvent.response);
3625
- };
3626
- const handleOutputItemAdded$1 = (rawEvent, state) => {
3627
- const events = new Array();
3628
- const functionCallDetails = extractFunctionCallDetails(rawEvent, state);
3629
- if (!functionCallDetails) return events;
3630
- const { outputIndex, toolCallId, name, initialArguments } = functionCallDetails;
3631
- const blockIndex = openFunctionCallBlock(state, {
3632
- outputIndex,
3633
- toolCallId,
3634
- name,
3635
- 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
3636
3588
  });
3637
- if (initialArguments !== void 0 && initialArguments.length > 0) {
3638
- events.push({
3639
- type: "content_block_delta",
3640
- index: blockIndex,
3641
- delta: {
3642
- type: "input_json_delta",
3643
- partial_json: initialArguments
3644
- }
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)
3595
+ });
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
3645
3648
  });
3646
- state.blockHasDelta.add(blockIndex);
3647
- }
3648
- return events;
3649
- };
3650
- const handleOutputItemDone$1 = (rawEvent, state) => {
3651
- const events = new Array();
3652
- const item = rawEvent.item;
3653
- const itemType = item.type;
3654
- const outputIndex = rawEvent.output_index;
3655
- if (itemType === "tool_search_call") {
3656
- const blockIndex = openFunctionCallBlock(state, {
3657
- outputIndex,
3658
- toolCallId: item.call_id,
3659
- name: state.toolSearchName,
3660
- events
3649
+ if (providerConfig.type === "openai-responses") return await handleOpenAIResponsesProviderMessages(c, {
3650
+ modelConfig,
3651
+ payload,
3652
+ provider,
3653
+ providerConfig
3661
3654
  });
3662
- const finalArguments = stringifyToolSearchArguments(item.arguments);
3663
- if (!state.blockHasDelta.has(blockIndex) && finalArguments) {
3664
- events.push({
3665
- type: "content_block_delta",
3666
- index: blockIndex,
3667
- delta: {
3668
- type: "input_json_delta",
3669
- partial_json: finalArguments
3670
- }
3671
- });
3672
- state.blockHasDelta.add(blockIndex);
3673
- }
3674
- state.functionCallStateByOutputIndex.delete(outputIndex);
3675
- return events;
3676
- }
3677
- if (itemType === "compaction") {
3678
- if (!item.id || !item.encrypted_content) return events;
3679
- const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3680
- if (!state.blockHasDelta.has(blockIndex)) events.push({
3681
- type: "content_block_delta",
3682
- index: blockIndex,
3683
- delta: {
3684
- type: "thinking_delta",
3685
- thinking: THINKING_TEXT
3686
- }
3655
+ if (providerConfig.type === "openai-compatible") return await handleOpenAICompatibleProviderMessages(c, {
3656
+ modelConfig,
3657
+ payload,
3658
+ provider,
3659
+ providerConfig
3687
3660
  });
3688
- events.push({
3689
- type: "content_block_delta",
3690
- index: blockIndex,
3691
- delta: {
3692
- type: "signature_delta",
3693
- signature: encodeCompactionCarrierSignature({
3694
- id: item.id,
3695
- encrypted_content: item.encrypted_content
3696
- })
3697
- }
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
3698
3674
  });
3699
- state.blockHasDelta.add(blockIndex);
3700
- return events;
3701
- }
3702
- if (itemType !== "reasoning") return events;
3703
- const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3704
- const signature = (item.encrypted_content ?? "") + "@" + item.id;
3705
- if (signature) {
3706
- if (!item.summary || item.summary.length === 0) events.push({
3707
- type: "content_block_delta",
3708
- index: blockIndex,
3709
- delta: {
3710
- type: "thinking_delta",
3711
- thinking: THINKING_TEXT
3712
- }
3675
+ return respondProviderMessagesJson(c, {
3676
+ body: await upstreamResponse.json(),
3677
+ payload,
3678
+ provider,
3679
+ providerConfig
3713
3680
  });
3714
- events.push({
3715
- type: "content_block_delta",
3716
- index: blockIndex,
3717
- delta: {
3718
- type: "signature_delta",
3719
- signature
3720
- }
3681
+ } catch (error) {
3682
+ logger$4.error("provider.messages.error", {
3683
+ provider,
3684
+ error
3721
3685
  });
3722
- state.blockHasDelta.add(blockIndex);
3686
+ throw error;
3723
3687
  }
3724
- return events;
3725
- };
3726
- const handleFunctionCallArgumentsDelta = (rawEvent, state) => {
3727
- const events = new Array();
3728
- const outputIndex = rawEvent.output_index;
3729
- const deltaText = rawEvent.delta;
3730
- if (!deltaText) return events;
3731
- const blockIndex = openFunctionCallBlock(state, {
3732
- outputIndex,
3733
- events
3734
- });
3735
- const functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
3736
- if (!functionCallState) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta without an open tool call block."), state, events);
3737
- const { nextCount, exceeded } = updateWhitespaceRunState(functionCallState.consecutiveWhitespaceCount, deltaText);
3738
- if (exceeded) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta containing more than 20 consecutive whitespace characters."), state, events);
3739
- functionCallState.consecutiveWhitespaceCount = nextCount;
3740
- events.push({
3741
- type: "content_block_delta",
3742
- index: blockIndex,
3743
- delta: {
3744
- type: "input_json_delta",
3745
- partial_json: deltaText
3746
- }
3747
- });
3748
- state.blockHasDelta.add(blockIndex);
3749
- return events;
3750
- };
3751
- const handleFunctionCallArgumentsDone = (rawEvent, state) => {
3752
- const events = new Array();
3753
- const outputIndex = rawEvent.output_index;
3754
- const blockIndex = openFunctionCallBlock(state, {
3755
- outputIndex,
3756
- 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
3757
3698
  });
3758
- const finalArguments = typeof rawEvent.arguments === "string" ? rawEvent.arguments : void 0;
3759
- if (!state.blockHasDelta.has(blockIndex) && finalArguments) {
3760
- events.push({
3761
- type: "content_block_delta",
3762
- index: blockIndex,
3763
- delta: {
3764
- type: "input_json_delta",
3765
- partial_json: finalArguments
3766
- }
3767
- });
3768
- state.blockHasDelta.add(blockIndex);
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);
3769
3703
  }
3770
- state.functionCallStateByOutputIndex.delete(outputIndex);
3771
- return events;
3772
- };
3773
- const handleOutputTextDelta = (rawEvent, state) => {
3774
- const events = new Array();
3775
- const outputIndex = rawEvent.output_index;
3776
- const contentIndex = rawEvent.content_index;
3777
- const deltaText = rawEvent.delta;
3778
- if (!deltaText) return events;
3779
- const blockIndex = openTextBlockIfNeeded(state, {
3780
- outputIndex,
3781
- contentIndex,
3782
- events
3704
+ if (responsesPayload.stream) return streamResponsesProviderMessages({
3705
+ c,
3706
+ payload,
3707
+ provider,
3708
+ providerConfig,
3709
+ upstreamResponse
3783
3710
  });
3784
- events.push({
3785
- type: "content_block_delta",
3786
- index: blockIndex,
3787
- delta: {
3788
- type: "text_delta",
3789
- text: deltaText
3790
- }
3711
+ return respondResponsesProviderMessagesJson(c, {
3712
+ body: await upstreamResponse.json(),
3713
+ payload,
3714
+ provider,
3715
+ providerConfig
3791
3716
  });
3792
- state.blockHasDelta.add(blockIndex);
3793
- return events;
3794
3717
  };
3795
- const handleReasoningSummaryTextDelta = (rawEvent, state) => {
3796
- const outputIndex = rawEvent.output_index;
3797
- const deltaText = rawEvent.delta;
3798
- const events = new Array();
3799
- const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3800
- events.push({
3801
- type: "content_block_delta",
3802
- index: blockIndex,
3803
- delta: {
3804
- type: "thinking_delta",
3805
- thinking: deltaText
3806
- }
3807
- });
3808
- state.blockHasDelta.add(blockIndex);
3809
- return events;
3718
+ const applyModelDefaults = (payload, modelConfig) => {
3719
+ payload.temperature ??= modelConfig?.temperature;
3720
+ payload.top_p ??= modelConfig?.topP;
3721
+ payload.top_k ??= modelConfig?.topK;
3810
3722
  };
3811
- const handleReasoningSummaryTextDone = (rawEvent, state) => {
3812
- const outputIndex = rawEvent.output_index;
3813
- const text = rawEvent.text;
3814
- const events = new Array();
3815
- const blockIndex = openThinkingBlockIfNeeded(state, outputIndex, events);
3816
- if (text && !state.blockHasDelta.has(blockIndex)) events.push({
3817
- type: "content_block_delta",
3818
- index: blockIndex,
3819
- delta: {
3820
- type: "thinking_delta",
3821
- thinking: text
3822
- }
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
3751
+ });
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);
3756
+ }
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
3763
+ });
3764
+ return respondOpenAICompatibleProviderMessagesJson(c, {
3765
+ body: await upstreamResponse.json(),
3766
+ payload,
3767
+ provider
3823
3768
  });
3824
- return events;
3825
3769
  };
3826
- const handleOutputTextDone = (rawEvent, state) => {
3827
- const events = new Array();
3828
- const outputIndex = rawEvent.output_index;
3829
- const contentIndex = rawEvent.content_index;
3830
- const text = rawEvent.text;
3831
- const blockIndex = openTextBlockIfNeeded(state, {
3832
- outputIndex,
3833
- contentIndex,
3834
- events
3770
+ const createOpenAICompatiblePayload = (payload, modelConfig) => {
3771
+ const openAIPayload = translateToOpenAI(payload, {
3772
+ supportPdf: modelConfig?.supportPdf,
3773
+ toolContentSupportType: modelConfig?.toolContentSupportType ?? []
3835
3774
  });
3836
- if (text && !state.blockHasDelta.has(blockIndex)) events.push({
3837
- type: "content_block_delta",
3838
- index: blockIndex,
3839
- delta: {
3840
- type: "text_delta",
3841
- text
3842
- }
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
3843
3782
  });
3844
- 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;
3845
3788
  };
3846
- const handleResponseCompleted = (rawEvent, state) => {
3847
- const response = rawEvent.response;
3848
- const events = new Array();
3849
- closeAllOpenBlocks(state, events);
3850
- const anthropic = translateResponsesResultToAnthropic(response);
3851
- events.push({
3852
- type: "message_delta",
3853
- delta: {
3854
- stop_reason: anthropic.stop_reason,
3855
- stop_sequence: anthropic.stop_sequence
3856
- },
3857
- usage: anthropic.usage
3858
- }, { type: "message_stop" });
3859
- state.messageCompleted = true;
3860
- 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
+ }
3861
3796
  };
3862
- const handleResponseFailed = (rawEvent, state) => {
3863
- const response = rawEvent.response;
3864
- const events = new Array();
3865
- closeAllOpenBlocks(state, events);
3866
- const message = response.error?.message ?? "The response failed due to an unknown error.";
3867
- events.push(buildErrorEvent(message));
3868
- state.messageCompleted = true;
3869
- 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];
3870
3800
  };
3871
- const handleErrorEvent = (rawEvent, state) => {
3872
- const message = typeof rawEvent.message === "string" ? rawEvent.message : "An unexpected error occurred during streaming.";
3873
- state.messageCompleted = true;
3874
- return [buildErrorEvent(message)];
3801
+ const applyOpenAICompatibleContextCache = (payload) => {
3802
+ const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
3803
+ for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
3875
3804
  };
3876
- const handleFunctionCallArgumentsValidationError = (error, state, events = []) => {
3877
- const reason = error.message;
3878
- closeAllOpenBlocks(state, events);
3879
- state.messageCompleted = true;
3880
- events.push(buildErrorEvent(reason));
3881
- return events;
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);
3882
3810
  };
3883
- const messageStart = (state, response) => {
3884
- state.messageStartSent = true;
3885
- const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
3886
- const inputTokens = (response.usage?.input_tokens ?? 0) - (inputCachedTokens ?? 0);
3887
- return [{
3888
- type: "message_start",
3889
- message: {
3890
- id: response.id,
3891
- type: "message",
3892
- role: "assistant",
3893
- content: [],
3894
- model: response.model,
3895
- stop_reason: null,
3896
- stop_sequence: null,
3897
- usage: {
3898
- input_tokens: inputTokens,
3899
- output_tokens: 0,
3900
- cache_read_input_tokens: inputCachedTokens ?? 0
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;
3816
+ };
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
+ });
3901
3902
  }
3902
3903
  }
3903
- }];
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
+ });
3904
3914
  };
3905
- const openTextBlockIfNeeded = (state, params) => {
3906
- const { outputIndex, contentIndex, events } = params;
3907
- const key = getBlockKey(outputIndex, contentIndex);
3908
- let blockIndex = state.blockIndexByKey.get(key);
3909
- if (blockIndex === void 0) {
3910
- blockIndex = state.nextContentBlockIndex;
3911
- state.nextContentBlockIndex += 1;
3912
- state.blockIndexByKey.set(key, blockIndex);
3913
- }
3914
- if (!state.openBlocks.has(blockIndex)) {
3915
- closeOpenBlocks(state, events);
3916
- events.push({
3917
- type: "content_block_start",
3918
- index: blockIndex,
3919
- content_block: {
3920
- type: "text",
3921
- 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
+ });
3922
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
3923
3964
  });
3924
- state.openBlocks.add(blockIndex);
3965
+ return null;
3925
3966
  }
3926
- return blockIndex;
3927
3967
  };
3928
- const openThinkingBlockIfNeeded = (state, outputIndex, events) => {
3929
- const key = getBlockKey(outputIndex, 0);
3930
- let blockIndex = state.blockIndexByKey.get(key);
3931
- if (blockIndex === void 0) {
3932
- blockIndex = state.nextContentBlockIndex;
3933
- state.nextContentBlockIndex += 1;
3934
- state.blockIndexByKey.set(key, blockIndex);
3935
- }
3936
- if (!state.openBlocks.has(blockIndex)) {
3937
- closeOpenBlocks(state, events);
3938
- events.push({
3939
- type: "content_block_start",
3940
- index: blockIndex,
3941
- content_block: {
3942
- type: "thinking",
3943
- thinking: ""
3944
- }
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
3945
3978
  });
3946
- state.openBlocks.add(blockIndex);
3979
+ return null;
3947
3980
  }
3948
- return blockIndex;
3949
- };
3950
- const closeBlockIfOpen = (state, blockIndex, events) => {
3951
- if (!state.openBlocks.has(blockIndex)) return;
3952
- events.push({
3953
- type: "content_block_stop",
3954
- index: blockIndex
3955
- });
3956
- state.openBlocks.delete(blockIndex);
3957
- state.blockHasDelta.delete(blockIndex);
3958
- };
3959
- const closeOpenBlocks = (state, events) => {
3960
- for (const blockIndex of state.openBlocks) closeBlockIfOpen(state, blockIndex, events);
3961
- };
3962
- const closeAllOpenBlocks = (state, events) => {
3963
- closeOpenBlocks(state, events);
3964
- state.functionCallStateByOutputIndex.clear();
3965
3981
  };
3966
- const buildErrorEvent = (message) => ({
3967
- type: "error",
3968
- error: {
3969
- type: "api_error",
3970
- message
3971
- }
3972
- });
3973
- const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
3974
- const openFunctionCallBlock = (state, params) => {
3975
- const { outputIndex, toolCallId, name, events } = params;
3976
- let functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
3977
- if (!functionCallState) {
3978
- const blockIndex = state.nextContentBlockIndex;
3979
- state.nextContentBlockIndex += 1;
3980
- functionCallState = {
3981
- blockIndex,
3982
- toolCallId: toolCallId ?? `tool_call_${blockIndex}`,
3983
- name: name ?? "function",
3984
- 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: {}
3985
4003
  };
3986
- state.functionCallStateByOutputIndex.set(outputIndex, functionCallState);
3987
- }
3988
- const { blockIndex } = functionCallState;
3989
- if (!state.openBlocks.has(blockIndex)) {
3990
- closeOpenBlocks(state, events);
3991
- events.push({
3992
- type: "content_block_start",
3993
- index: blockIndex,
3994
- content_block: {
3995
- type: "tool_use",
3996
- id: functionCallState.toolCallId,
3997
- name: functionCallState.name,
3998
- input: {}
3999
- }
4004
+ } catch (error) {
4005
+ logger$4.error("provider.messages.streaming.adjust_tokens_error", {
4006
+ error,
4007
+ originalData: data
4000
4008
  });
4001
- state.openBlocks.add(blockIndex);
4009
+ return null;
4002
4010
  }
4003
- return blockIndex;
4004
4011
  };
4005
- const extractFunctionCallDetails = (rawEvent, state) => {
4006
- const item = rawEvent.item;
4007
- const itemType = item.type;
4008
- if (itemType === "tool_search_call") return {
4009
- outputIndex: rawEvent.output_index,
4010
- toolCallId: item.call_id,
4011
- name: state.toolSearchName,
4012
- initialArguments: ""
4013
- };
4014
- if (itemType !== "function_call") return;
4015
- return {
4016
- outputIndex: rawEvent.output_index,
4017
- toolCallId: item.call_id,
4018
- name: resolveToolUseName(item),
4019
- initialArguments: item.arguments
4020
- };
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);
4021
4019
  };
4022
- const stringifyToolSearchArguments = (argumentsValue) => {
4023
- try {
4024
- return JSON.stringify(formatToolSearchBridgeArguments(argumentsValue));
4025
- } catch {
4026
- return;
4027
- }
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);
4028
4045
  };
4029
4046
  //#endregion
4030
4047
  //#region src/services/copilot/create-messages.ts
@@ -4754,11 +4771,19 @@ const providerModelRoutes = new Hono();
4754
4771
  providerModelRoutes.get("/", async (c) => {
4755
4772
  const provider = c.req.param("provider") ?? "";
4756
4773
  try {
4757
- const providerConfig = getProviderConfig(provider);
4774
+ const providerConfig = await resolveProviderConfig(provider);
4758
4775
  if (!providerConfig) return c.json({ error: {
4759
4776
  message: `Provider '${provider}' not found or disabled`,
4760
4777
  type: "invalid_request_error"
4761
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
+ }
4762
4787
  const upstreamResponse = await forwardProviderModels(providerConfig, c.req.raw.headers);
4763
4788
  logger$2.debug("provider.models.response", {
4764
4789
  provider,
@@ -4818,9 +4843,17 @@ const responsesHandlerDependencies = {
4818
4843
  isResponsesApiWebSearchEnabled
4819
4844
  };
4820
4845
  const handleResponses = async (c) => {
4821
- await responsesHandlerDependencies.checkRateLimit(state);
4822
4846
  const payload = await c.req.json();
4823
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);
4824
4857
  const requestId = generateRequestIdFromPayload({ messages: payload.input });
4825
4858
  logger$1.debug("Generated request ID:", requestId);
4826
4859
  const sessionId = getUUID(requestId);
@@ -4876,6 +4909,30 @@ const handleResponses = async (c) => {
4876
4909
  recordUsage(normalizeResponsesUsage(response.usage));
4877
4910
  return c.json(response);
4878
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
+ };
4879
4936
  const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
4880
4937
  const isStreamingRequested = (payload) => Boolean(payload.stream);
4881
4938
  const parseResponsesStreamEvent = (chunk) => {
@@ -4887,6 +4944,51 @@ const parseResponsesStreamEvent = (chunk) => {
4887
4944
  return null;
4888
4945
  }
4889
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
+ };
4890
4992
  const removeWebSearchTool = (payload) => {
4891
4993
  if (!Array.isArray(payload.tools) || payload.tools.length === 0) return;
4892
4994
  payload.tools = payload.tools.filter((t) => {
@@ -5019,4 +5121,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
5019
5121
  //#endregion
5020
5122
  export { server };
5021
5123
 
5022
- //# sourceMappingURL=server-BRFLAT4z.js.map
5124
+ //# sourceMappingURL=server-D5O9IzAY.js.map