@jeffreycao/copilot-api 1.10.9 → 1.10.11

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