xiaodcs-copilot-api 2.4.1-public.1 → 2.5.1-public.2

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.
@@ -1,22 +1,26 @@
1
- import { A as isResponsesApiCompactionRecoveryEnabled, C as getClaudeTokenMultiplier, D as isAlphaSearchCodexPriorityEnabled, E as getResponsesTransportConfig, F as PATHS, M as isResponsesApiWebSocketEnabled, O as isCopilotFastTierEnabled, S as getClaudeAutoModel, T as getMessageApiWebSearchModel, _ as resolveMappedModel, b as getAlphaSearchModel, d as getModelResponsesApiCompactThreshold$1, f as getReasoningEffortForModel, g as isGpt56OrAbove, h as isContextManagementEnabledForResponses, i as listEnabledProviders, j as isResponsesApiWebSearchEnabled, k as isMessagesApiEnabled, l as getExtraPromptForModel, m as isContextManagementEnabledForMessages, n as getRawProviderConfig, o as resolveEffectiveProviderType, p as getSmallModel, s as resolveProviderAuthType, t as getProviderConfig, u as getModelMappings, v as setModelMappings, x as getAnthropicApiKey } from "./config-A1SyzmBJ.js";
2
- import { B as fetchResponsesWithLifecycle, C as compactAutoContinuePromptStarts, D as compactTextOnlyGuard, E as compactSystemPromptStarts, F as forwardCodexResponses, G as createWebSocketUrl, H as encodePoolKeyPart, I as generateTraceId, J as forwardError, K as state, L as requestContext, N as CODEX_API_BASE_URL, O as createAuthMiddleware, P as buildCodexRequestHeaders, R as resolveTraceId$1, U as isTerminalResponsesStreamChunk, V as createResponsesSafeStream, W as createPooledWebSocketStream, b as prepareInteractionHeaders, c as getUUID, d as isResponsesStream$1, f as parseUserIdMetadata, g as copilotHeaders, h as copilotBaseUrl, k as getConfiguredAdminApiKeys, l as isAsyncIterable, o as generateRequestIdFromPayload, p as getCopilotUsage, q as HTTPError, r as setupCodexToken, s as getRootSessionId, u as isNullish, v as copilotWebSocketHeaders, w as compactMessageSections, x as prepareMessageProxyHeaders, y as prepareForCompact, z as createResponsesHttpEventStream } from "./token-CQyKjtuM.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-Ds1vbmGG.js";
4
- import { i as toClientModelId, r as normalizeSdkModelId, t as findEndpointModel } from "./models-CmzVpYlg.js";
5
- import consola from "consola";
1
+ import { C as getAnthropicApiKey, D as getMessageApiWebSearchModel, F as isResponsesApiWebSocketEnabled, M as isMessagesApiEnabled, N as isResponsesApiCompactionRecoveryEnabled, O as getResponsesTransportConfig, P as isResponsesApiWebSearchEnabled, R as PATHS, S as getAlphaSearchModel, T as getClaudeTokenMultiplier, _ as isContextManagementEnabledForResponses, b as setModelMappings, d as getExtraPromptForModel, f as getModelMappings, g as isContextManagementEnabledForMessages, h as getSmallModel, i as listEnabledProviders, j as isCopilotFastTierEnabled, k as isAlphaSearchCodexPriorityEnabled, m as getReasoningEffortForModel, n as getRawProviderConfig, o as resolveEffectiveProviderType, p as getModelResponsesApiCompactThreshold$1, s as resolveProviderAuthType, t as getProviderConfig, u as isQoderSdkProvider, v as isGpt56OrAbove, w as getClaudeAutoModel, y as resolveMappedModel } from "./config-DnMKoKrg.js";
2
+ import { B as fetchResponsesWithLifecycle, C as compactAutoContinuePromptStarts, D as compactTextOnlyGuard, E as compactSystemPromptStarts, F as forwardCodexResponses, G as createWebSocketUrl, H as encodePoolKeyPart, I as generateTraceId, J as forwardError, K as state, L as requestContext, N as CODEX_API_BASE_URL, O as createAuthMiddleware, P as buildCodexRequestHeaders, R as resolveTraceId$1, U as isTerminalResponsesStreamChunk, V as createResponsesSafeStream, W as createPooledWebSocketStream, b as prepareInteractionHeaders, c as getUUID, d as isResponsesStream$1, f as parseUserIdMetadata, g as copilotHeaders, h as copilotBaseUrl, k as getConfiguredAdminApiKeys, l as isAsyncIterable, o as generateRequestIdFromPayload, p as getCopilotUsage, q as HTTPError, r as setupCodexToken, s as getRootSessionId, u as isNullish, v as copilotWebSocketHeaders, w as compactMessageSections, x as prepareMessageProxyHeaders, y as prepareForCompact, z as createResponsesHttpEventStream } from "./token-CQftvMU4.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-DCb3HbWj.js";
4
+ import { a as forwardProviderMessages, c as createTimeoutDispatcher, d as normalizeSdkModelId, f as toClientModelId, i as forwardProviderImages, l as findEndpointModel, n as forwardProviderAlphaSearch, o as forwardProviderModels, r as forwardProviderChatCompletions, s as forwardProviderResponses, t as createProviderProxyResponse } from "./provider-proxy-BbLwvtD_.js";
5
+ import { n as forwardQoder } from "./transport-DE8Q0wU-.js";
6
+ import fs, { mkdtemp, rm } from "node:fs/promises";
7
+ import { tmpdir } from "node:os";
8
+ import path, { join } from "node:path";
6
9
  import { createHash, randomUUID } from "node:crypto";
7
- import fs, { readFileSync } from "node:fs";
8
- import path from "node:path";
9
- import fs$1 from "node:fs/promises";
10
- import { fetch as fetch$1, getGlobalDispatcher } from "undici";
10
+ import { Readable, Transform } from "node:stream";
11
+ import consola from "consola";
12
+ import fs$1, { createWriteStream, readFileSync, rmSync } from "node:fs";
13
+ import { fetch as fetch$1 } from "undici";
11
14
  import { events } from "fetch-event-stream";
12
15
  import { z } from "zod";
13
16
  import { Hono } from "hono";
14
17
  import { cors } from "hono/cors";
15
18
  import { logger } from "hono/logger";
16
- import { decompress } from "fzstd";
17
19
  import util from "node:util";
18
20
  import { streamSSE } from "hono/streaming";
19
- import { bufferToFormData } from "hono/utils/buffer";
21
+ import { Busboy } from "@fastify/busboy";
22
+ import { pipeline } from "node:stream/promises";
23
+ import { decompress } from "fzstd";
20
24
  //#region src/lib/trace.ts
21
25
  const traceIdMiddleware = async (c, next) => {
22
26
  const traceId = resolveTraceId$1(c.req.header("x-trace-id"));
@@ -33,58 +37,6 @@ const traceIdMiddleware = async (c, next) => {
33
37
  });
34
38
  };
35
39
  //#endregion
36
- //#region src/lib/zstd-request.ts
37
- const ZSTD_CONTENT_ENCODING = "zstd";
38
- const INVALID_BODY_STATUS = 400;
39
- let nodeZlibPromise = null;
40
- const zstdDecompressionMiddleware = async (c, next) => {
41
- if (c.req.header("content-encoding")?.trim().toLowerCase() !== ZSTD_CONTENT_ENCODING) return next();
42
- try {
43
- const decompressedBody = await decompressZstd(new Uint8Array(await c.req.raw.arrayBuffer()));
44
- const headers = new Headers(c.req.raw.headers);
45
- headers.delete("content-encoding");
46
- headers.delete("content-length");
47
- c.req.raw = new Request(c.req.raw.url, {
48
- body: decompressedBody,
49
- headers,
50
- method: c.req.raw.method,
51
- signal: c.req.raw.signal
52
- });
53
- c.req.bodyCache = {};
54
- } catch {
55
- return c.json({ error: {
56
- message: "Failed to decompress zstd request body.",
57
- type: "invalid_request_error"
58
- } }, INVALID_BODY_STATUS);
59
- }
60
- return next();
61
- };
62
- const decompressZstd = async (input) => {
63
- const bun = getBunRuntime();
64
- if (bun?.zstdDecompress) return toUint8Array(await bun.zstdDecompress(input));
65
- const nodeZlib = await getNodeZlib();
66
- if (nodeZlib?.zstdDecompress) return new Promise((resolve, reject) => {
67
- nodeZlib.zstdDecompress?.(input, (error, result) => {
68
- if (error) {
69
- reject(error);
70
- return;
71
- }
72
- resolve(toUint8Array(result));
73
- });
74
- });
75
- return decompress(input);
76
- };
77
- const getBunRuntime = () => globalThis.Bun;
78
- const getNodeZlib = async () => {
79
- nodeZlibPromise ??= import("node:zlib").then((module) => module).catch(() => null);
80
- return nodeZlibPromise;
81
- };
82
- const toUint8Array = (data) => {
83
- if (data instanceof Uint8Array) return data;
84
- if (data instanceof ArrayBuffer) return new Uint8Array(data);
85
- return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
86
- };
87
- //#endregion
88
40
  //#region src/lib/process-cleanup.ts
89
41
  const cleanupHandlers = /* @__PURE__ */ new Set();
90
42
  let cleanupPromise = null;
@@ -152,7 +104,7 @@ let cleanupInterval;
152
104
  let currentLogDateKey;
153
105
  const ensureLogDirectory = () => {
154
106
  const logDir = getLogDir();
155
- if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
107
+ if (!fs$1.existsSync(logDir)) fs$1.mkdirSync(logDir, { recursive: true });
156
108
  };
157
109
  const formatArgs = (args) => args.map((arg) => typeof arg === "string" ? arg : util.inspect(arg, {
158
110
  depth: null,
@@ -204,13 +156,13 @@ const closePreviousDateLogFiles = (dateKey) => {
204
156
  };
205
157
  const cleanupOldLogs = async () => {
206
158
  const logDir = getLogDir();
207
- if (!fs.existsSync(logDir)) return;
159
+ if (!fs$1.existsSync(logDir)) return;
208
160
  const now = Date.now();
209
- for (const entry of fs.readdirSync(logDir)) {
161
+ for (const entry of fs$1.readdirSync(logDir)) {
210
162
  const filePath = path.join(logDir, entry);
211
163
  let stats;
212
164
  try {
213
- stats = fs.statSync(filePath);
165
+ stats = fs$1.statSync(filePath);
214
166
  } catch {
215
167
  continue;
216
168
  }
@@ -222,7 +174,7 @@ const cleanupOldLogs = async () => {
222
174
  continue;
223
175
  }
224
176
  try {
225
- fs.rmSync(filePath);
177
+ fs$1.rmSync(filePath);
226
178
  } catch (error) {
227
179
  console.warn(`Failed to remove old handler log: ${filePath}`, error);
228
180
  }
@@ -267,7 +219,7 @@ const getLogStream = (filePath) => {
267
219
  initializeLoggerRuntime();
268
220
  let stream = logStreams.get(filePath);
269
221
  if (!stream || stream.destroyed) {
270
- const createdStream = fs.createWriteStream(filePath, { flags: "a" });
222
+ const createdStream = fs$1.createWriteStream(filePath, { flags: "a" });
271
223
  stream = createdStream;
272
224
  logStreams.set(filePath, createdStream);
273
225
  createdStream.on("error", (error) => {
@@ -1063,7 +1015,7 @@ async function openNodeDatabase(dbPath) {
1063
1015
  }
1064
1016
  async function openSqliteDatabase(dbPath) {
1065
1017
  const dir = path.dirname(dbPath);
1066
- if (dbPath !== ":memory:" && dir !== ".") await fs$1.mkdir(dir, { recursive: true });
1018
+ if (dbPath !== ":memory:" && dir !== ".") await fs.mkdir(dir, { recursive: true });
1067
1019
  return isBunRuntime() ? openBunDatabase(dbPath) : openNodeDatabase(dbPath);
1068
1020
  }
1069
1021
  var SqliteDbStore = class {
@@ -2695,11 +2647,11 @@ var CompactionRecoveryStore = class {
2695
2647
  async prepareStorageDirectory() {
2696
2648
  if (this.dbPath === ":memory:" || this.storagePrepared) return;
2697
2649
  const directory = path.dirname(this.dbPath);
2698
- await fs$1.mkdir(directory, {
2650
+ await fs.mkdir(directory, {
2699
2651
  mode: this.secureDirectory ? 448 : void 0,
2700
2652
  recursive: true
2701
2653
  });
2702
- if (this.secureDirectory) await fs$1.chmod(directory, 448);
2654
+ if (this.secureDirectory) await fs.chmod(directory, 448);
2703
2655
  this.storagePrepared = true;
2704
2656
  }
2705
2657
  async secureDatabaseFiles() {
@@ -2710,7 +2662,7 @@ var CompactionRecoveryStore = class {
2710
2662
  ];
2711
2663
  await Promise.all(candidates.map(async (candidate) => {
2712
2664
  try {
2713
- await fs$1.chmod(candidate, 384);
2665
+ await fs.chmod(candidate, 384);
2714
2666
  } catch (error) {
2715
2667
  if (!isMissingFileError(error)) throw error;
2716
2668
  }
@@ -3521,160 +3473,8 @@ const createResponsesWebSocketStreamChunk = (data) => {
3521
3473
  }
3522
3474
  };
3523
3475
  //#endregion
3524
- //#region src/lib/timeout-dispatcher.ts
3525
- /**
3526
- * Node's global fetch keeps Undici's shorter default headers/body timeouts.
3527
- * Wrap the global dispatcher so long-running requests (e.g. image
3528
- * generation) get an explicit cap.
3529
- */
3530
- const createTimeoutDispatcher = (timeoutMs) => ({ dispatch(options, handler) {
3531
- return getGlobalDispatcher().dispatch({
3532
- ...options,
3533
- bodyTimeout: timeoutMs,
3534
- headersTimeout: timeoutMs
3535
- }, handler);
3536
- } });
3537
- //#endregion
3538
- //#region src/services/providers/provider-proxy.ts
3539
- const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
3540
- const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
3541
- const STRIPPED_RESPONSE_HEADERS = [
3542
- "connection",
3543
- "content-encoding",
3544
- "content-length",
3545
- "keep-alive",
3546
- "proxy-authenticate",
3547
- "proxy-authorization",
3548
- "te",
3549
- "trailer",
3550
- "transfer-encoding",
3551
- "upgrade"
3552
- ];
3553
- function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
3554
- const authHeaders = {};
3555
- if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
3556
- else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
3557
- const headers = {
3558
- "content-type": "application/json",
3559
- accept: "application/json",
3560
- ...authHeaders
3561
- };
3562
- for (const headerName of SHARED_FORWARDABLE_HEADERS) {
3563
- const headerValue = requestHeaders.get(headerName);
3564
- if (headerValue) headers[headerName] = headerValue;
3565
- }
3566
- if (providerConfig.type !== "anthropic") return headers;
3567
- for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
3568
- const headerValue = requestHeaders.get(headerName);
3569
- if (headerValue) headers[headerName] = headerValue;
3570
- }
3571
- return headers;
3572
- }
3573
- const OPENCODE_GO_PROVIDER_NAME = "opencode-go";
3574
- const OPENCODE_SESSION_HEADER = "x-opencode-session";
3575
- const resolveOpencodeMessagesSession = (payload) => {
3576
- const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim();
3577
- if (sessionAffinity) return sessionAffinity;
3578
- const userId = payload.metadata?.user_id;
3579
- if (!userId?.trim()) return;
3580
- const { sessionId } = parseUserIdMetadata(userId);
3581
- return sessionId ?? userId;
3582
- };
3583
- const applyOpencodeSessionHeader = (providerConfig, headers, session) => {
3584
- if (providerConfig.name !== OPENCODE_GO_PROVIDER_NAME || !session) return;
3585
- headers[OPENCODE_SESSION_HEADER] = session;
3586
- };
3587
- function createProviderProxyResponse(upstreamResponse, body) {
3588
- const headers = new Headers(upstreamResponse.headers);
3589
- for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
3590
- return new Response(body ?? upstreamResponse.body, {
3591
- headers,
3592
- status: upstreamResponse.status,
3593
- statusText: upstreamResponse.statusText
3594
- });
3595
- }
3596
- async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
3597
- consola.log(`<-- model: ${payload.model}`);
3598
- const headers = buildProviderUpstreamHeaders(providerConfig, requestHeaders);
3599
- applyOpencodeSessionHeader(providerConfig, headers, resolveOpencodeMessagesSession(payload));
3600
- return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
3601
- method: "POST",
3602
- headers,
3603
- body: JSON.stringify(payload)
3604
- });
3605
- }
3606
- async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
3607
- consola.log(`<-- model: ${payload.model}`);
3608
- const headers = buildProviderUpstreamHeaders(providerConfig, requestHeaders);
3609
- applyOpencodeSessionHeader(providerConfig, headers, payload.prompt_cache_key?.trim() || void 0);
3610
- return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
3611
- method: "POST",
3612
- headers,
3613
- body: JSON.stringify(payload)
3614
- });
3615
- }
3616
- async function forwardProviderResponses(providerConfig, payload, requestHeaders, options = {}) {
3617
- consola.log(`<-- model: ${payload.model}`);
3618
- const transportConfig = getResponsesTransportConfig();
3619
- const headers = buildProviderUpstreamHeaders(providerConfig, requestHeaders);
3620
- applyOpencodeSessionHeader(providerConfig, headers, payload.prompt_cache_key?.trim() || void 0);
3621
- return await fetchResponsesWithLifecycle(`${providerConfig.baseUrl}/v1/responses`, {
3622
- method: "POST",
3623
- headers,
3624
- body: JSON.stringify(payload)
3625
- }, {
3626
- headersTimeoutMs: transportConfig.headersTimeoutMs,
3627
- signal: options.signal,
3628
- streamInactivityTimeoutMs: transportConfig.streamInactivityTimeoutMs
3629
- });
3630
- }
3631
- const PROVIDER_MODELS_TIMEOUT_MS = 15e3;
3632
- async function forwardProviderModels(providerConfig, requestHeaders) {
3633
- return await fetch(`${providerConfig.baseUrl}/v1/models`, {
3634
- method: "GET",
3635
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
3636
- signal: AbortSignal.timeout(PROVIDER_MODELS_TIMEOUT_MS)
3637
- });
3638
- }
3639
- /** Align with Codex images: long-running generation/edits need a generous cap. */
3640
- const PROVIDER_IMAGES_TIMEOUT_MS = 900 * 1e3;
3641
- const providerImagesDispatcher = createTimeoutDispatcher(PROVIDER_IMAGES_TIMEOUT_MS);
3642
- function resolveProviderRequestUrl(providerConfig, requestUrl, path) {
3643
- const upstreamUrl = new URL(`${providerConfig.baseUrl}${path}`);
3644
- upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
3645
- return upstreamUrl.toString();
3646
- }
3647
- async function forwardProviderAlphaSearch(providerConfig, request) {
3648
- const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
3649
- const body = await request.arrayBuffer();
3650
- return await fetch(resolveProviderRequestUrl(providerConfig, request.url, "/v1/alpha/search"), {
3651
- method: "POST",
3652
- headers,
3653
- body
3654
- });
3655
- }
3656
- async function forwardProviderImages(providerConfig, request, operation) {
3657
- const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
3658
- const contentType = request.headers.get("content-type");
3659
- if (contentType) headers["content-type"] = contentType;
3660
- else if (operation === "edits") delete headers["content-type"];
3661
- const init = {
3662
- method: "POST",
3663
- headers,
3664
- body: request.body,
3665
- duplex: "half",
3666
- signal: AbortSignal.timeout(PROVIDER_IMAGES_TIMEOUT_MS)
3667
- };
3668
- const upstreamUrl = resolveProviderRequestUrl(providerConfig, request.url, `/v1/images/${operation}`);
3669
- if (typeof Bun !== "undefined") return await fetch(upstreamUrl, init);
3670
- return await fetch$1(upstreamUrl, {
3671
- ...init,
3672
- dispatcher: providerImagesDispatcher
3673
- });
3674
- }
3675
- //#endregion
3676
3476
  //#region src/routes/alpha-search/alpha-search-responses.ts
3677
- const logger$16 = createHandlerLogger("alpha-search-responses-handler");
3477
+ const logger$17 = createHandlerLogger("alpha-search-responses-handler");
3678
3478
  const SESSION_TTL_MS = 3600 * 1e3;
3679
3479
  const MAX_SESSIONS = 128;
3680
3480
  const MAX_URL_REFERENCES = 256;
@@ -4032,14 +3832,14 @@ function createRemoteResponsesPayload(request, model, instruction) {
4032
3832
  };
4033
3833
  }
4034
3834
  async function requestProviderSearch(c, providerConfig, payload, model, sessionId) {
4035
- debugJson(logger$16, "Alpha search provider Responses request:", {
3835
+ debugJson(logger$17, "Alpha search provider Responses request:", {
4036
3836
  payload,
4037
3837
  provider: providerConfig.name
4038
3838
  });
4039
3839
  const upstreamResponse = await forwardProviderResponses(providerConfig, payload, c.req.raw.headers, { signal: c.req.raw.signal });
4040
3840
  if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${providerConfig.name} responses for alpha search`, upstreamResponse);
4041
3841
  const result = await upstreamResponse.json();
4042
- debugJson(logger$16, "Alpha search provider Responses result:", {
3842
+ debugJson(logger$17, "Alpha search provider Responses result:", {
4043
3843
  provider: providerConfig.name,
4044
3844
  result
4045
3845
  });
@@ -4055,7 +3855,7 @@ async function requestProviderSearch(c, providerConfig, payload, model, sessionI
4055
3855
  return result;
4056
3856
  }
4057
3857
  async function requestCopilotSearch(payload, model, requestId, sessionId, signal) {
4058
- debugJson(logger$16, "Alpha search Copilot Responses request:", payload);
3858
+ debugJson(logger$17, "Alpha search Copilot Responses request:", payload);
4059
3859
  const result = await alphaSearchResponsesDependencies.createResponses(payload, {
4060
3860
  vision: false,
4061
3861
  initiator: "agent",
@@ -4064,7 +3864,7 @@ async function requestCopilotSearch(payload, model, requestId, sessionId, signal
4064
3864
  sessionId,
4065
3865
  signal
4066
3866
  });
4067
- debugJson(logger$16, "Alpha search Copilot Responses result:", result);
3867
+ debugJson(logger$17, "Alpha search Copilot Responses result:", result);
4068
3868
  alphaSearchResponsesDependencies.createUsageRecorder(model, sessionId)({
4069
3869
  ...normalizeResponsesUsage(result.usage),
4070
3870
  total_nano_aiu: normalizeOptionalToken(result.copilot_usage?.total_nano_aiu)
@@ -4179,7 +3979,7 @@ async function forwardCodexAlphaSearch(request) {
4179
3979
  }
4180
3980
  //#endregion
4181
3981
  //#region src/routes/alpha-search/route.ts
4182
- const logger$15 = createHandlerLogger("alpha-search-handler");
3982
+ const logger$16 = createHandlerLogger("alpha-search-handler");
4183
3983
  const alphaSearchRoutes = new Hono();
4184
3984
  const alphaSearchRouteDependencies = {
4185
3985
  findEndpointModel,
@@ -4199,9 +3999,9 @@ function parseDebugBody(body) {
4199
3999
  }
4200
4000
  }
4201
4001
  async function forwardCodexAlphaSearchRequest(request) {
4202
- await debugJsonAsync(logger$15, "alpha_search.codex.request", async () => ({ body: parseDebugBody(await request.clone().text()) }));
4002
+ await debugJsonAsync(logger$16, "alpha_search.codex.request", async () => ({ body: parseDebugBody(await request.clone().text()) }));
4203
4003
  const upstreamResponse = await alphaSearchRouteDependencies.forwardCodexAlphaSearch(request);
4204
- await debugJsonAsync(logger$15, "alpha_search.codex.response", async () => ({
4004
+ await debugJsonAsync(logger$16, "alpha_search.codex.response", async () => ({
4205
4005
  body: parseDebugBody(await upstreamResponse.clone().text()),
4206
4006
  statusCode: upstreamResponse.status
4207
4007
  }));
@@ -4312,7 +4112,7 @@ alphaSearchRoutes.post("/", async (c) => {
4312
4112
  try {
4313
4113
  return await handleAlphaSearchRequest(c);
4314
4114
  } catch (error) {
4315
- logger$15.error("alpha_search.error", { error });
4115
+ logger$16.error("alpha_search.error", { error });
4316
4116
  return await forwardError(c, error);
4317
4117
  }
4318
4118
  });
@@ -4485,7 +4285,7 @@ const normalizeProviderResponsesReasoningEffort = (payload, providerConfig) => {
4485
4285
  };
4486
4286
  //#endregion
4487
4287
  //#region src/routes/provider/chat-completions/handler.ts
4488
- const logger$14 = createHandlerLogger("provider-chat-completions-handler");
4288
+ const logger$15 = createHandlerLogger("provider-chat-completions-handler");
4489
4289
  async function handleProviderChatCompletionsForProvider(c, options) {
4490
4290
  const { payload, provider } = options;
4491
4291
  const providerConfig = await resolveProviderConfig(provider);
@@ -4499,13 +4299,13 @@ async function handleProviderChatCompletionsForProvider(c, options) {
4499
4299
  applyProviderStreamOptions(payload);
4500
4300
  applyDashScopePreserveThinkingDefault(payload, providerConfig);
4501
4301
  applyProviderContextCache(payload, modelConfig, providerConfig);
4502
- debugJson(logger$14, "provider.chat_completions.request", {
4302
+ debugJson(logger$15, "provider.chat_completions.request", {
4503
4303
  payload,
4504
4304
  provider
4505
4305
  });
4506
- const upstreamResponse = await forwardProviderChatCompletions(providerConfig, payload, c.req.raw.headers);
4306
+ const upstreamResponse = await forwardProviderChatCompletions(providerConfig, payload, c.req.raw.headers, { signal: c.req.raw.signal });
4507
4307
  if (!upstreamResponse.ok) {
4508
- logger$14.error("Failed to create provider chat completions", {
4308
+ logger$15.error("Failed to create provider chat completions", {
4509
4309
  provider,
4510
4310
  statusCode: upstreamResponse.status
4511
4311
  });
@@ -4519,7 +4319,7 @@ async function handleProviderChatCompletionsForProvider(c, options) {
4519
4319
  });
4520
4320
  const responseBody = await upstreamResponse.clone().json();
4521
4321
  recordUsage(normalizeOpenAIUsage(responseBody.usage));
4522
- debugJson(logger$14, "provider.chat_completions.response", responseBody);
4322
+ debugJson(logger$15, "provider.chat_completions.response", responseBody);
4523
4323
  return createProviderProxyResponse(upstreamResponse);
4524
4324
  }
4525
4325
  const applyProviderStreamOptions = (payload) => {
@@ -4541,12 +4341,12 @@ const createProviderChatCompletionsUsageRecorder = (payload, provider, modelConf
4541
4341
  providerName: provider
4542
4342
  });
4543
4343
  const streamProviderChatCompletions = (c, upstreamResponse, options) => {
4544
- logger$14.debug("provider.chat_completions.streaming", { provider: options.provider });
4344
+ logger$15.debug("provider.chat_completions.streaming", { provider: options.provider });
4545
4345
  return streamSSE(c, async (stream) => {
4546
4346
  let usage = {};
4547
4347
  try {
4548
4348
  for await (const chunk of events(upstreamResponse)) {
4549
- debugJson(logger$14, "provider.chat_completions.stream_chunk", chunk);
4349
+ debugJson(logger$15, "provider.chat_completions.stream_chunk", chunk);
4550
4350
  if (chunk.data && chunk.data !== "[DONE]") {
4551
4351
  const parsedChunk = parseChatCompletionChunkData(chunk.data);
4552
4352
  if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
@@ -4600,7 +4400,7 @@ const createChatCompletions = async (payload, options) => {
4600
4400
  };
4601
4401
  //#endregion
4602
4402
  //#region src/routes/chat-completions/handler.ts
4603
- const logger$13 = createHandlerLogger("chat-completions-handler");
4403
+ const logger$14 = createHandlerLogger("chat-completions-handler");
4604
4404
  async function handleCompletion$1(c) {
4605
4405
  let payload = await c.req.json();
4606
4406
  const requestedModel = payload.model;
@@ -4614,7 +4414,7 @@ async function handleCompletion$1(c) {
4614
4414
  provider: providerModelAlias.provider
4615
4415
  });
4616
4416
  }
4617
- debugJson(logger$13, "Request payload:", payload);
4417
+ debugJson(logger$14, "Request payload:", payload);
4618
4418
  const selectedModel = findEndpointModel(payload.model);
4619
4419
  payload.model = selectedModel?.id ?? payload.model;
4620
4420
  if (isNullish(payload.max_tokens) && isNullish(payload.max_completion_tokens)) {
@@ -4622,16 +4422,16 @@ async function handleCompletion$1(c) {
4622
4422
  ...payload,
4623
4423
  max_tokens: selectedModel?.capabilities.limits.max_output_tokens
4624
4424
  };
4625
- debugJson(logger$13, "Set max_tokens to:", payload.max_tokens);
4425
+ debugJson(logger$14, "Set max_tokens to:", payload.max_tokens);
4626
4426
  }
4627
4427
  if (payload.model.includes("gpt")) {
4628
4428
  if (isNullish(payload.max_completion_tokens)) payload.max_completion_tokens = payload.max_tokens;
4629
4429
  delete payload.max_tokens;
4630
4430
  }
4631
4431
  const requestId = generateRequestIdFromPayload(payload);
4632
- logger$13.debug("Generated request ID:", requestId);
4432
+ logger$14.debug("Generated request ID:", requestId);
4633
4433
  const sessionId = getUUID(requestId);
4634
- logger$13.debug("Extracted session ID:", sessionId);
4434
+ logger$14.debug("Extracted session ID:", sessionId);
4635
4435
  const recordUsage = createCopilotTokenUsageRecorder({
4636
4436
  endpoint: "chat_completions",
4637
4437
  fallbackSessionId: sessionId,
@@ -4642,18 +4442,18 @@ async function handleCompletion$1(c) {
4642
4442
  sessionId
4643
4443
  });
4644
4444
  if (isNonStreaming$1(response)) {
4645
- debugJson(logger$13, "Non-streaming response:", response);
4445
+ debugJson(logger$14, "Non-streaming response:", response);
4646
4446
  recordUsage({
4647
4447
  ...normalizeOpenAIUsage(response.usage),
4648
4448
  total_nano_aiu: normalizeOptionalToken(response.copilot_usage?.total_nano_aiu)
4649
4449
  });
4650
4450
  return c.json(response);
4651
4451
  }
4652
- logger$13.debug("Streaming response");
4452
+ logger$14.debug("Streaming response");
4653
4453
  return streamSSE(c, async (stream) => {
4654
4454
  let usage = {};
4655
4455
  for await (const chunk of response) {
4656
- debugJson(logger$13, "Streaming chunk:", chunk);
4456
+ debugJson(logger$14, "Streaming chunk:", chunk);
4657
4457
  const parsedChunk = parseChatCompletionChunk(chunk);
4658
4458
  if (parsedChunk?.usage || parsedChunk?.copilot_usage) usage = {
4659
4459
  ...normalizeOpenAIUsage(parsedChunk.usage),
@@ -4742,31 +4542,48 @@ embeddingRoutes.post("/", async (c) => {
4742
4542
  }
4743
4543
  });
4744
4544
  //#endregion
4745
- //#region src/services/codex/images.ts
4746
- const CODEX_IMAGES_TIMEOUT_MS = 900 * 1e3;
4747
- const codexImagesDispatcher = createTimeoutDispatcher(CODEX_IMAGES_TIMEOUT_MS);
4748
- function resolveCodexImagesUrl(requestUrl, operation) {
4749
- const upstreamUrl = new URL(`${CODEX_API_BASE_URL}/codex/images/${operation}`);
4750
- upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
4751
- return upstreamUrl.toString();
4545
+ //#region src/routes/images/body-limit.ts
4546
+ function createImagesBodyTooLargeError(maxBytes) {
4547
+ const message = `Image request body exceeds the ${maxBytes}-byte limit`;
4548
+ return new HTTPError(message, new Response(message, {
4549
+ headers: { "content-type": "text/plain; charset=utf-8" },
4550
+ status: 413
4551
+ }));
4752
4552
  }
4753
- async function forwardCodexImages(request, operation) {
4754
- const headers = buildCodexRequestHeaders(request.headers);
4755
- if (!headers.has("accept")) headers.set("accept", "application/json");
4756
- if (operation === "generations" && !headers.has("content-type")) headers.set("content-type", "application/json");
4757
- const init = {
4758
- method: "POST",
4759
- headers,
4760
- body: request.body,
4761
- duplex: "half",
4762
- signal: AbortSignal.timeout(CODEX_IMAGES_TIMEOUT_MS)
4763
- };
4764
- const upstreamUrl = resolveCodexImagesUrl(request.url, operation);
4765
- if (typeof Bun !== "undefined") return await fetch(upstreamUrl, init);
4766
- return await fetch$1(upstreamUrl, {
4767
- ...init,
4768
- dispatcher: codexImagesDispatcher
4769
- });
4553
+ async function readRequestBodyWithLimit(request, maxBytes) {
4554
+ const contentLength = request.headers.get("content-length");
4555
+ if (contentLength !== null) {
4556
+ const declaredBytes = Number(contentLength);
4557
+ if (Number.isFinite(declaredBytes) && declaredBytes > maxBytes) throw createImagesBodyTooLargeError(maxBytes);
4558
+ }
4559
+ if (!request.body) return /* @__PURE__ */ new ArrayBuffer(0);
4560
+ const reader = request.body.getReader();
4561
+ const chunks = [];
4562
+ let totalBytes = 0;
4563
+ try {
4564
+ while (true) {
4565
+ const { done, value } = await reader.read();
4566
+ if (done) break;
4567
+ if (!value || value.byteLength === 0) continue;
4568
+ totalBytes += value.byteLength;
4569
+ if (totalBytes > maxBytes) {
4570
+ try {
4571
+ await reader.cancel();
4572
+ } catch {}
4573
+ throw createImagesBodyTooLargeError(maxBytes);
4574
+ }
4575
+ chunks.push(value);
4576
+ }
4577
+ } finally {
4578
+ reader.releaseLock();
4579
+ }
4580
+ const body = new Uint8Array(totalBytes);
4581
+ let offset = 0;
4582
+ for (const chunk of chunks) {
4583
+ body.set(chunk, offset);
4584
+ offset += chunk.byteLength;
4585
+ }
4586
+ return body.buffer;
4770
4587
  }
4771
4588
  //#endregion
4772
4589
  //#region src/routes/images/forward-provider-images.ts
@@ -4795,13 +4612,38 @@ async function forwardProviderImagesWithLogging(providerConfig, request, operati
4795
4612
  return createProviderProxyResponse(upstreamResponse);
4796
4613
  }
4797
4614
  //#endregion
4798
- //#region src/routes/images/route.ts
4799
- const logger$12 = createHandlerLogger("images-handler");
4800
- const MAX_IMAGES_REQUEST_BODY_BYTES = 64 * 1024 * 1024;
4801
- const imageRoutes = new Hono();
4615
+ //#region src/services/codex/images.ts
4616
+ const CODEX_IMAGES_TIMEOUT_MS = 900 * 1e3;
4617
+ const codexImagesDispatcher = createTimeoutDispatcher(CODEX_IMAGES_TIMEOUT_MS);
4618
+ function resolveCodexImagesUrl(requestUrl, operation) {
4619
+ const upstreamUrl = new URL(`${CODEX_API_BASE_URL}/codex/images/${operation}`);
4620
+ upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
4621
+ return upstreamUrl.toString();
4622
+ }
4623
+ async function forwardCodexImages(request, operation) {
4624
+ const headers = buildCodexRequestHeaders(request.headers);
4625
+ if (!headers.has("accept")) headers.set("accept", "application/json");
4626
+ if (operation === "generations" && !headers.has("content-type")) headers.set("content-type", "application/json");
4627
+ const init = {
4628
+ method: "POST",
4629
+ headers,
4630
+ body: request.body,
4631
+ duplex: "half",
4632
+ signal: AbortSignal.timeout(CODEX_IMAGES_TIMEOUT_MS)
4633
+ };
4634
+ const upstreamUrl = resolveCodexImagesUrl(request.url, operation);
4635
+ if (typeof Bun !== "undefined") return await fetch(upstreamUrl, init);
4636
+ return await fetch$1(upstreamUrl, {
4637
+ ...init,
4638
+ dispatcher: codexImagesDispatcher
4639
+ });
4640
+ }
4641
+ //#endregion
4642
+ //#region src/routes/images/shared.ts
4643
+ const logger$13 = createHandlerLogger("images-handler");
4802
4644
  const imageRouteDependencies = {
4645
+ maxRequestBodyBytes: 64 * 1024 * 1024,
4803
4646
  debugJsonAsync,
4804
- maxRequestBodyBytes: MAX_IMAGES_REQUEST_BODY_BYTES,
4805
4647
  resolveMappedModel,
4806
4648
  resolveProviderConfig
4807
4649
  };
@@ -4811,114 +4653,33 @@ function getContentMetadata(headers) {
4811
4653
  contentLength: headers.get("content-length")
4812
4654
  };
4813
4655
  }
4814
- function createJsonImagesRequest(request, requestHeaders, payload, model) {
4815
- const headers = new Headers(requestHeaders);
4816
- headers.delete("content-length");
4817
- return new Request(request.url, {
4818
- body: JSON.stringify({
4819
- ...payload,
4820
- model
4821
- }),
4822
- headers,
4823
- method: request.method,
4824
- signal: request.signal
4825
- });
4656
+ /**
4657
+ * Snapshots request headers before the body is consumed: Bun drops
4658
+ * auto-generated body headers (like the multipart boundary) afterwards.
4659
+ */
4660
+ function snapshotRequestHeaders(request) {
4661
+ return new Headers(request.headers);
4826
4662
  }
4827
- function createBufferedImagesRequest(request, requestHeaders, body) {
4828
- return new Request(request.url, {
4663
+ /**
4664
+ * Rebuilds a request from primitives instead of cloning: under Node runtimes
4665
+ * srvx wraps incoming requests in a proxy class whose prototype chain
4666
+ * satisfies `instanceof Request` without the native internals, so
4667
+ * `new Request(request)` throws.
4668
+ */
4669
+ function createForwardRequest(request, requestHeaders, body) {
4670
+ const init = {
4829
4671
  body,
4672
+ duplex: "half",
4830
4673
  headers: requestHeaders,
4831
4674
  method: request.method,
4832
4675
  signal: request.signal
4833
- });
4834
- }
4835
- function createMultipartImagesRequest(request, requestHeaders, formData, model) {
4836
- formData.set("model", model);
4837
- const headers = new Headers(requestHeaders);
4838
- headers.delete("content-length");
4839
- headers.delete("content-type");
4840
- return new Request(request.url, {
4841
- body: formData,
4842
- headers,
4843
- method: request.method,
4844
- signal: request.signal
4845
- });
4846
- }
4847
- function createImagesBodyTooLargeError(maxBytes) {
4848
- const message = `Image request body exceeds the ${maxBytes}-byte limit`;
4849
- return new HTTPError(message, new Response(message, {
4850
- headers: { "content-type": "text/plain; charset=utf-8" },
4851
- status: 413
4852
- }));
4853
- }
4854
- async function readRequestBodyWithLimit(request, maxBytes) {
4855
- const contentLength = request.headers.get("content-length");
4856
- if (contentLength !== null) {
4857
- const declaredBytes = Number(contentLength);
4858
- if (Number.isFinite(declaredBytes) && declaredBytes > maxBytes) throw createImagesBodyTooLargeError(maxBytes);
4859
- }
4860
- if (!request.body) return /* @__PURE__ */ new ArrayBuffer(0);
4861
- const reader = request.body.getReader();
4862
- const chunks = [];
4863
- let totalBytes = 0;
4864
- try {
4865
- while (true) {
4866
- const { done, value } = await reader.read();
4867
- if (done) break;
4868
- if (!value || value.byteLength === 0) continue;
4869
- totalBytes += value.byteLength;
4870
- if (totalBytes > maxBytes) {
4871
- try {
4872
- await reader.cancel();
4873
- } catch {}
4874
- throw createImagesBodyTooLargeError(maxBytes);
4875
- }
4876
- chunks.push(value);
4877
- }
4878
- } finally {
4879
- reader.releaseLock();
4880
- }
4881
- const body = new Uint8Array(totalBytes);
4882
- let offset = 0;
4883
- for (const chunk of chunks) {
4884
- body.set(chunk, offset);
4885
- offset += chunk.byteLength;
4886
- }
4887
- return body.buffer;
4888
- }
4889
- async function parseImagesRequest(request) {
4890
- const requestHeaders = new Headers(request.headers);
4891
- const contentType = requestHeaders.get("content-type");
4892
- const body = await readRequestBodyWithLimit(request, imageRouteDependencies.maxRequestBodyBytes);
4893
- const originalRequest = createBufferedImagesRequest(request, requestHeaders, body);
4894
- if (contentType?.split(";", 1)[0]?.trim().toLowerCase() === "multipart/form-data" && contentType) try {
4895
- const formData = await bufferToFormData(body, contentType);
4896
- const model = formData.get("model");
4897
- if (typeof model !== "string") return originalRequest;
4898
- return {
4899
- createRequest: (mappedModel) => createMultipartImagesRequest(request, requestHeaders, formData, mappedModel),
4900
- model,
4901
- originalRequest
4902
- };
4903
- } catch {
4904
- return originalRequest;
4905
- }
4906
- try {
4907
- const payload = JSON.parse(new TextDecoder().decode(body));
4908
- if (payload === null || typeof payload !== "object" || Array.isArray(payload) || typeof payload.model !== "string") return originalRequest;
4909
- return {
4910
- createRequest: (mappedModel) => createJsonImagesRequest(request, requestHeaders, payload, mappedModel),
4911
- model: payload.model,
4912
- originalRequest
4913
- };
4914
- } catch {
4915
- return originalRequest;
4916
- }
4676
+ };
4677
+ return new Request(request.url, init);
4917
4678
  }
4918
4679
  /**
4919
- * Handles Codex images proxying. Pass `resolvedProviderConfig` when the
4680
+ * Handles Codex images proxying. Pass a resolvedProviderConfig when the
4920
4681
  * caller already resolved the codex provider to avoid a second resolve, and
4921
- * pass `request` when model mapping rebuilt the request body.
4682
+ * pass a request when model mapping rebuilt the request body.
4922
4683
  */
4923
4684
  async function handleCodexImages(c, operation, resolvedProviderConfig, request = c.req.raw) {
4924
4685
  try {
@@ -4926,48 +4687,386 @@ async function handleCodexImages(c, operation, resolvedProviderConfig, request =
4926
4687
  message: "Provider 'codex' not found or disabled",
4927
4688
  type: "invalid_request_error"
4928
4689
  } }, 404);
4929
- if (operation === "generations") await imageRouteDependencies.debugJsonAsync(logger$12, "images.generations.codex.request", async () => ({ body: await request.clone().text() }));
4930
- else debugJson(logger$12, "images.edits.codex.request", getContentMetadata(request.headers));
4690
+ if (operation === "generations") await imageRouteDependencies.debugJsonAsync(logger$13, "images.generations.codex.request", async () => ({ body: await request.clone().text() }));
4691
+ else debugJson(logger$13, "images.edits.codex.request", getContentMetadata(request.headers));
4931
4692
  const upstreamResponse = await forwardCodexImages(request, operation);
4932
- debugJson(logger$12, `images.${operation}.codex.response`, {
4693
+ debugJson(logger$13, `images.${operation}.codex.response`, {
4933
4694
  ...getContentMetadata(upstreamResponse.headers),
4934
4695
  statusCode: upstreamResponse.status
4935
4696
  });
4936
4697
  return createProviderProxyResponse(upstreamResponse);
4937
4698
  } catch (error) {
4938
- logger$12.error(`images.${operation}.codex.error`, { error });
4699
+ logger$13.error(`images.${operation}.codex.error`, { error });
4939
4700
  return await forwardError(c, error);
4940
4701
  }
4941
4702
  }
4942
- async function handleImagesRequest(c, operation) {
4703
+ /**
4704
+ * Routes a parsed images request: applies the model mapping, forwards to a
4705
+ * mapped provider when configured, and otherwise falls back to Codex.
4706
+ */
4707
+ async function routeImagesRequest(c, operation, parsed) {
4708
+ const requestedModel = parsed.model;
4709
+ const mappedModel = imageRouteDependencies.resolveMappedModel(requestedModel);
4710
+ if (mappedModel !== requestedModel) consola.debug(`Resolved model mapping: ${requestedModel} -> ${mappedModel}`);
4711
+ const providerModelAlias = parseProviderModelAlias(mappedModel);
4712
+ if (providerModelAlias) {
4713
+ const providerConfig = await imageRouteDependencies.resolveProviderConfig(providerModelAlias.provider);
4714
+ if (providerConfig) {
4715
+ const request = parsed.createRequest(providerModelAlias.model);
4716
+ if (providerConfig.name === "codex") return await handleCodexImages(c, operation, providerConfig, request);
4717
+ return await forwardProviderImagesWithLogging(providerConfig, request, operation, {
4718
+ logger: logger$13,
4719
+ provider: providerModelAlias.provider
4720
+ });
4721
+ }
4722
+ consola.debug(`Provider '${providerModelAlias.provider}' not found or disabled; forwarding the original model to Codex`);
4723
+ return await handleCodexImages(c, operation, void 0, parsed.originalRequest ?? parsed.createRequest(requestedModel));
4724
+ }
4725
+ if (parsed.originalRequest && mappedModel === requestedModel) return await handleCodexImages(c, operation, void 0, parsed.originalRequest);
4726
+ return await handleCodexImages(c, operation, void 0, parsed.createRequest(mappedModel));
4727
+ }
4728
+ //#endregion
4729
+ //#region src/routes/images/temp-form-data.ts
4730
+ const logger$12 = createHandlerLogger("images-temp-form-data");
4731
+ const multipartStagingDependencies = { createWriteStream };
4732
+ /**
4733
+ * Matches the images forwarding timeouts: an upstream upload may take that
4734
+ * long, so staged files must survive at least the whole forwarding window.
4735
+ */
4736
+ const STAGED_FILES_TTL_MS = 900 * 1e3;
4737
+ const DEFAULT_MULTIPART_STAGING_LIMITS = {
4738
+ maxBodySizeBytes: 128 * 1024 * 1024,
4739
+ maxFieldSizeBytes: 10 * 1024 * 1024,
4740
+ maxFields: 64,
4741
+ maxFileSizeBytes: 64 * 1024 * 1024,
4742
+ maxFiles: 16,
4743
+ maxParts: 80
4744
+ };
4745
+ var InvalidMultipartBodyError = class extends Error {
4746
+ constructor(options) {
4747
+ super("Invalid multipart form data body", options);
4748
+ this.name = "InvalidMultipartBodyError";
4749
+ }
4750
+ };
4751
+ var MultipartBodyTooLargeError = class extends Error {
4752
+ constructor() {
4753
+ super("Multipart form data body exceeds the configured upload limits");
4754
+ this.name = "MultipartBodyTooLargeError";
4755
+ }
4756
+ };
4757
+ function sanitizeFileName(name) {
4758
+ return name.split(/[\\/]/).pop()?.replaceAll("\0", "") || "file";
4759
+ }
4760
+ async function createDiskBackedBlob(filePath, type) {
4761
+ if (typeof Bun !== "undefined") return Bun.file(filePath, { type });
4762
+ const { openAsBlob } = await import("node:fs");
4763
+ return await openAsBlob(filePath, { type });
4764
+ }
4765
+ function toError(error) {
4766
+ return error instanceof Error ? error : new Error(String(error));
4767
+ }
4768
+ function parseMultipartBody(body, contentType, directory, entries, limits) {
4769
+ return new Promise((resolve, reject) => {
4770
+ let busboy;
4771
+ try {
4772
+ busboy = new Busboy({
4773
+ headers: { "content-type": contentType },
4774
+ limits: {
4775
+ fieldSize: limits.maxFieldSizeBytes,
4776
+ fields: limits.maxFields,
4777
+ fileSize: limits.maxFileSizeBytes,
4778
+ files: limits.maxFiles,
4779
+ parts: limits.maxParts
4780
+ }
4781
+ });
4782
+ } catch (error) {
4783
+ reject(new InvalidMultipartBodyError({ cause: toError(error) }));
4784
+ return;
4785
+ }
4786
+ const pendingWrites = [];
4787
+ const activeFileStreams = /* @__PURE__ */ new Set();
4788
+ const source = body === null ? Readable.from([]) : Readable.fromWeb(body);
4789
+ let bodySizeBytes = 0;
4790
+ const bodyLimiter = new Transform({ transform(chunk, _encoding, callback) {
4791
+ bodySizeBytes += chunk.byteLength;
4792
+ if (bodySizeBytes > limits.maxBodySizeBytes) {
4793
+ callback(new MultipartBodyTooLargeError());
4794
+ return;
4795
+ }
4796
+ callback(null, chunk);
4797
+ } });
4798
+ let fileIndex = 0;
4799
+ let failure = null;
4800
+ let inputPipeline = null;
4801
+ let settled = false;
4802
+ const settleStreams = () => Promise.allSettled([...inputPipeline ? [inputPipeline] : [], ...pendingWrites]);
4803
+ const fail = (error) => {
4804
+ if (failure || settled) return;
4805
+ const failureError = toError(error);
4806
+ failure = failureError;
4807
+ source.destroy(failureError);
4808
+ bodyLimiter.destroy(failureError);
4809
+ busboy.destroy(failureError);
4810
+ for (const stream of activeFileStreams) stream.destroy(failureError);
4811
+ settleStreams().then(() => {
4812
+ if (settled) return;
4813
+ settled = true;
4814
+ reject(failureError);
4815
+ });
4816
+ };
4817
+ const failLimit = () => fail(new MultipartBodyTooLargeError());
4818
+ busboy.on("field", (name, value, nameTruncated, valueTruncated) => {
4819
+ if (nameTruncated || valueTruncated) {
4820
+ failLimit();
4821
+ return;
4822
+ }
4823
+ entries.push({
4824
+ kind: "field",
4825
+ name,
4826
+ value
4827
+ });
4828
+ });
4829
+ busboy.on("file", (name, stream, fileName, _transferEncoding, mimeType) => {
4830
+ if (failure) {
4831
+ stream.resume();
4832
+ return;
4833
+ }
4834
+ const filePath = join(directory, `${fileIndex}.upload`);
4835
+ fileIndex += 1;
4836
+ entries.push({
4837
+ fileName: sanitizeFileName(fileName),
4838
+ filePath,
4839
+ kind: "file",
4840
+ mimeType,
4841
+ name
4842
+ });
4843
+ activeFileStreams.add(stream);
4844
+ stream.once("close", () => activeFileStreams.delete(stream));
4845
+ stream.once("limit", failLimit);
4846
+ const write = pipeline(stream, multipartStagingDependencies.createWriteStream(filePath));
4847
+ pendingWrites.push(write);
4848
+ write.catch(fail);
4849
+ });
4850
+ busboy.on("fieldsLimit", failLimit);
4851
+ busboy.on("filesLimit", failLimit);
4852
+ busboy.on("partsLimit", failLimit);
4853
+ inputPipeline = pipeline(source, bodyLimiter, busboy);
4854
+ inputPipeline.then(() => {
4855
+ Promise.allSettled(pendingWrites).then((results) => {
4856
+ if (failure || settled) return;
4857
+ const failed = results.find((result) => result.status === "rejected");
4858
+ if (failed) fail(failed.reason);
4859
+ else {
4860
+ settled = true;
4861
+ resolve();
4862
+ }
4863
+ });
4864
+ }, (error) => {
4865
+ fail(error instanceof MultipartBodyTooLargeError ? error : new InvalidMultipartBodyError({ cause: toError(error) }));
4866
+ });
4867
+ });
4868
+ }
4869
+ function resolveStagingLimits(overrides) {
4870
+ const limits = {
4871
+ ...DEFAULT_MULTIPART_STAGING_LIMITS,
4872
+ ...overrides
4873
+ };
4874
+ for (const [name, value] of Object.entries(limits)) if (!Number.isSafeInteger(value) || value <= 0) throw new RangeError(`${name} must be a positive safe integer`);
4875
+ return limits;
4876
+ }
4877
+ /**
4878
+ * Streams a multipart request body through the parser, writing every file
4879
+ * part to a temporary directory as it arrives and rebuilding the form data
4880
+ * with disk-backed blobs, so forwarding streams payloads from disk instead
4881
+ * of pinning the whole upload in memory.
4882
+ */
4883
+ async function stageMultipartBodyToDisk(body, contentType, limitOverrides = {}) {
4884
+ const limits = resolveStagingLimits(limitOverrides);
4885
+ const directory = await mkdtemp(join(tmpdir(), "copilot-api-images-"));
4886
+ const entries = [];
4943
4887
  try {
4944
- const parsedRequest = await parseImagesRequest(c.req.raw);
4945
- if (parsedRequest instanceof Request) return await handleCodexImages(c, operation, void 0, parsedRequest);
4946
- const requestedModel = parsedRequest.model;
4947
- const mappedModel = imageRouteDependencies.resolveMappedModel(requestedModel);
4948
- if (mappedModel !== requestedModel) consola.debug(`Resolved model mapping: ${requestedModel} -> ${mappedModel}`);
4949
- const providerModelAlias = parseProviderModelAlias(mappedModel);
4950
- if (providerModelAlias) {
4951
- const providerConfig = await imageRouteDependencies.resolveProviderConfig(providerModelAlias.provider);
4952
- if (providerConfig) {
4953
- const request = parsedRequest.createRequest(providerModelAlias.model);
4954
- if (providerConfig.name === "codex") return await handleCodexImages(c, operation, providerConfig, request);
4955
- return await forwardProviderImagesWithLogging(providerConfig, request, operation, {
4956
- logger: logger$12,
4957
- provider: providerModelAlias.provider
4888
+ await parseMultipartBody(body, contentType, directory, entries, limits);
4889
+ } catch (error) {
4890
+ await rm(directory, {
4891
+ force: true,
4892
+ recursive: true
4893
+ });
4894
+ throw error;
4895
+ }
4896
+ const stagedFormData = new FormData();
4897
+ try {
4898
+ for (const entry of entries) if (entry.kind === "field") stagedFormData.append(entry.name, entry.value);
4899
+ else stagedFormData.append(entry.name, await createDiskBackedBlob(entry.filePath, entry.mimeType), entry.fileName);
4900
+ } catch (error) {
4901
+ await rm(directory, {
4902
+ force: true,
4903
+ recursive: true
4904
+ });
4905
+ throw error;
4906
+ }
4907
+ let cleanupPromise = null;
4908
+ let cleanupTimer = null;
4909
+ let unregisterProcessCleanup = null;
4910
+ const cleanup = () => {
4911
+ cleanupPromise ??= (async () => {
4912
+ if (cleanupTimer) {
4913
+ clearTimeout(cleanupTimer);
4914
+ cleanupTimer = null;
4915
+ }
4916
+ try {
4917
+ await rm(directory, {
4918
+ force: true,
4919
+ recursive: true
4958
4920
  });
4921
+ } catch (error) {
4922
+ logger$12.warn("Failed to remove staged images temp directory", {
4923
+ directory,
4924
+ error
4925
+ });
4926
+ } finally {
4927
+ unregisterProcessCleanup?.();
4928
+ unregisterProcessCleanup = null;
4959
4929
  }
4960
- consola.debug(`Provider '${providerModelAlias.provider}' not found or disabled; forwarding the original model to Codex`);
4961
- return await handleCodexImages(c, operation, void 0, parsedRequest.originalRequest);
4930
+ })();
4931
+ return cleanupPromise;
4932
+ };
4933
+ unregisterProcessCleanup = registerProcessCleanup(() => {
4934
+ if (cleanupTimer) {
4935
+ clearTimeout(cleanupTimer);
4936
+ cleanupTimer = null;
4937
+ }
4938
+ try {
4939
+ rmSync(directory, {
4940
+ force: true,
4941
+ recursive: true
4942
+ });
4943
+ } catch (error) {
4944
+ logger$12.warn("Failed to remove staged images temp directory on exit", {
4945
+ directory,
4946
+ error
4947
+ });
4948
+ } finally {
4949
+ unregisterProcessCleanup?.();
4950
+ unregisterProcessCleanup = null;
4951
+ }
4952
+ });
4953
+ return {
4954
+ directory,
4955
+ formData: stagedFormData,
4956
+ cleanup,
4957
+ scheduleCleanup: () => {
4958
+ if (cleanupPromise || cleanupTimer) return;
4959
+ cleanupTimer = setTimeout(() => void cleanup(), STAGED_FILES_TTL_MS);
4960
+ cleanupTimer.unref();
4961
+ }
4962
+ };
4963
+ }
4964
+ //#endregion
4965
+ //#region src/routes/images/edits-handler.ts
4966
+ const imageEditsRouteDependencies = { stageMultipartBodyToDisk };
4967
+ function createStagedFormDataRequest(request, requestHeaders, formData) {
4968
+ const headers = new Headers(requestHeaders);
4969
+ headers.delete("content-length");
4970
+ headers.delete("content-type");
4971
+ return createForwardRequest(request, headers, formData);
4972
+ }
4973
+ function createMultipartImagesRequest(request, requestHeaders, formData, model) {
4974
+ formData.set("model", model);
4975
+ return createStagedFormDataRequest(request, requestHeaders, formData);
4976
+ }
4977
+ /**
4978
+ * Streams an edits multipart body to disk, staging uploaded files so
4979
+ * forwarding streams them from the filesystem instead of pinning them in
4980
+ * memory. Non-multipart bodies come back as an untouched request stream;
4981
+ * malformed multipart bodies throw a typed error because the consumed stream
4982
+ * can no longer be forwarded.
4983
+ */
4984
+ async function parseEditsRequest(request) {
4985
+ const requestHeaders = snapshotRequestHeaders(request);
4986
+ const contentType = requestHeaders.get("content-type");
4987
+ if (contentType?.split(";", 1)[0]?.trim().toLowerCase() !== "multipart/form-data" || !contentType) return createForwardRequest(request, requestHeaders, await readRequestBodyWithLimit(request, imageRouteDependencies.maxRequestBodyBytes));
4988
+ const staged = await imageEditsRouteDependencies.stageMultipartBodyToDisk(request.body, contentType, { maxBodySizeBytes: imageRouteDependencies.maxRequestBodyBytes });
4989
+ const model = staged.formData.get("model");
4990
+ return {
4991
+ model: typeof model === "string" ? model : void 0,
4992
+ requestHeaders,
4993
+ staged
4994
+ };
4995
+ }
4996
+ async function handleImagesEdits(c) {
4997
+ try {
4998
+ const parsed = await parseEditsRequest(c.req.raw);
4999
+ if (parsed instanceof Request) return await handleCodexImages(c, "edits", void 0, parsed);
5000
+ const { model, requestHeaders, staged } = parsed;
5001
+ try {
5002
+ const response = model === void 0 ? await handleCodexImages(c, "edits", void 0, createStagedFormDataRequest(c.req.raw, requestHeaders, staged.formData)) : await routeImagesRequest(c, "edits", {
5003
+ createRequest: (mappedModel) => createMultipartImagesRequest(c.req.raw, requestHeaders, staged.formData, mappedModel),
5004
+ model
5005
+ });
5006
+ staged.scheduleCleanup();
5007
+ return response;
5008
+ } catch (error) {
5009
+ await staged.cleanup();
5010
+ throw error;
4962
5011
  }
4963
- return await handleCodexImages(c, operation, void 0, mappedModel === requestedModel ? parsedRequest.originalRequest : parsedRequest.createRequest(mappedModel));
4964
5012
  } catch (error) {
4965
- logger$12.error(`images.${operation}.error`, { error });
5013
+ if (error instanceof InvalidMultipartBodyError || error instanceof MultipartBodyTooLargeError) return c.json({ error: {
5014
+ message: error.message,
5015
+ type: "invalid_request_error"
5016
+ } }, error instanceof MultipartBodyTooLargeError ? 413 : 400);
5017
+ logger$13.error("images.edits.error", { error });
4966
5018
  return await forwardError(c, error);
4967
5019
  }
4968
5020
  }
4969
- imageRoutes.post("/generations", (c) => handleImagesRequest(c, "generations"));
4970
- imageRoutes.post("/edits", (c) => handleImagesRequest(c, "edits"));
5021
+ //#endregion
5022
+ //#region src/routes/images/generations-handler.ts
5023
+ function createJsonImagesRequest(request, requestHeaders, payload, model) {
5024
+ const headers = new Headers(requestHeaders);
5025
+ headers.delete("content-length");
5026
+ return createForwardRequest(request, headers, new TextEncoder().encode(JSON.stringify({
5027
+ ...payload,
5028
+ model
5029
+ })));
5030
+ }
5031
+ /**
5032
+ * Parses a generations request body as JSON. Generation payloads are small
5033
+ * JSON documents, so one byte copy covers both routing on the model field
5034
+ * and forwarding an unchanged body to Codex. Rebuilt bodies stay binary so
5035
+ * Node does not synthesize a text/plain content type.
5036
+ */
5037
+ async function parseGenerationsRequest(request) {
5038
+ const requestHeaders = snapshotRequestHeaders(request);
5039
+ const body = new Uint8Array(await readRequestBodyWithLimit(request, imageRouteDependencies.maxRequestBodyBytes));
5040
+ const originalRequest = createForwardRequest(request, requestHeaders, body);
5041
+ let payload;
5042
+ try {
5043
+ const bodyText = new TextDecoder("utf-8", { fatal: true }).decode(body);
5044
+ payload = JSON.parse(bodyText);
5045
+ } catch {
5046
+ return originalRequest;
5047
+ }
5048
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload) || typeof payload.model !== "string") return originalRequest;
5049
+ return {
5050
+ createRequest: (mappedModel) => createJsonImagesRequest(request, requestHeaders, payload, mappedModel),
5051
+ model: payload.model,
5052
+ originalRequest
5053
+ };
5054
+ }
5055
+ async function handleImagesGenerations(c) {
5056
+ try {
5057
+ const parsed = await parseGenerationsRequest(c.req.raw);
5058
+ if (parsed instanceof Request) return await handleCodexImages(c, "generations", void 0, parsed);
5059
+ return await routeImagesRequest(c, "generations", parsed);
5060
+ } catch (error) {
5061
+ logger$13.error("images.generations.error", { error });
5062
+ return await forwardError(c, error);
5063
+ }
5064
+ }
5065
+ //#endregion
5066
+ //#region src/routes/images/route.ts
5067
+ const imageRoutes = new Hono();
5068
+ imageRoutes.post("/generations", handleImagesGenerations);
5069
+ imageRoutes.post("/edits", handleImagesEdits);
4971
5070
  //#endregion
4972
5071
  //#region src/lib/tokenizer.ts
4973
5072
  const ENCODING_MAP = {
@@ -6117,6 +6216,7 @@ async function handleCountTokens(c) {
6117
6216
  //#endregion
6118
6217
  //#region src/lib/codex-rate-limit.ts
6119
6218
  const codexRateLimitScopes = ["primary", "secondary"];
6219
+ const quotaWarningUsedPercent = 90;
6120
6220
  const formatCodexRateLimitResetAt = (resetAt) => {
6121
6221
  const date = /* @__PURE__ */ new Date(resetAt * 1e3);
6122
6222
  return Number.isNaN(date.getTime()) ? String(resetAt) : date.toLocaleString();
@@ -6131,16 +6231,21 @@ const logCodexRateLimitsEvent = (event) => {
6131
6231
  const rateLimitRecord = rateLimits;
6132
6232
  const allowed = typeof rateLimitRecord.allowed === "boolean" ? rateLimitRecord.allowed : null;
6133
6233
  const limitReached = typeof rateLimitRecord.limit_reached === "boolean" ? rateLimitRecord.limit_reached : null;
6234
+ const blocked = allowed === false || limitReached === true;
6235
+ let hasWindow = false;
6134
6236
  for (const scope of codexRateLimitScopes) {
6135
6237
  const window = rateLimitRecord[scope];
6136
6238
  if (!isCodexRateLimitWindow(window)) continue;
6239
+ hasWindow = true;
6137
6240
  const summary = [];
6138
6241
  if (allowed !== null) summary.push(`allowed=${allowed}`);
6139
6242
  if (limitReached !== null) summary.push(`limit_reached=${limitReached}`);
6140
6243
  summary.push(`used=${window.used_percent}%`, `reset_at=${formatCodexRateLimitResetAt(window.reset_at)}`);
6141
- const label = planType ? `Codex ${scope} rate limit (${planType})` : `Codex ${scope} rate limit`;
6142
- consola.log(`${label}: ${summary.join(", ")}`);
6244
+ const message = `${planType ? `Codex ${scope} quota (${planType})` : `Codex ${scope} quota`}: ${summary.join(", ")}`;
6245
+ if (blocked || window.used_percent >= quotaWarningUsedPercent) consola.warn(message);
6246
+ else consola.debug(message);
6143
6247
  }
6248
+ if (blocked && !hasWindow) consola.warn(`Codex quota restricted: allowed=${allowed}, limit_reached=${limitReached}`);
6144
6249
  };
6145
6250
  const isCodexRateLimitWindow = (value) => {
6146
6251
  if (!value || typeof value !== "object") return false;
@@ -9212,7 +9317,7 @@ const handleOpenAICompatibleProviderMessages = async (c, options) => {
9212
9317
  payload: openAIPayload,
9213
9318
  provider
9214
9319
  });
9215
- const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers);
9320
+ const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers, { signal: c.req.raw.signal });
9216
9321
  if (!upstreamResponse.ok) {
9217
9322
  logger$10.error("Failed to create openai-compatible responses", upstreamResponse);
9218
9323
  throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
@@ -11418,17 +11523,6 @@ function isDeepSeekModelId(modelId) {
11418
11523
  function shouldInjectMessagesToolCallTips(userAgent, targetModel) {
11419
11524
  return isCodexUserAgent(userAgent) && !isDeepSeekModelId(targetModel);
11420
11525
  }
11421
- async function logCodexModelsResponse(response) {
11422
- try {
11423
- const models = await response.clone().json();
11424
- debugJson(logger$8, "models.codex.response", {
11425
- statusCode: response.status,
11426
- models
11427
- });
11428
- } catch (error) {
11429
- logger$8.warn("models.codex.response_log_error", { error });
11430
- }
11431
- }
11432
11526
  /**
11433
11527
  * Proxies a models request to the fixed Codex upstream models endpoint.
11434
11528
  * Returns a 404 JSON response when the codex provider is unavailable.
@@ -11440,9 +11534,7 @@ async function handleCodexModelsProxy(c, resolvedProviderConfig) {
11440
11534
  message: "Provider 'codex' not found or disabled",
11441
11535
  type: "invalid_request_error"
11442
11536
  } }, 404);
11443
- const upstreamResponse = await forwardCodexModels(c.req.url, c.req.raw.headers);
11444
- await logCodexModelsResponse(upstreamResponse);
11445
- return createProviderProxyResponse(upstreamResponse);
11537
+ return createProviderProxyResponse(await forwardCodexModels(c.req.url, c.req.raw.headers));
11446
11538
  }
11447
11539
  async function handleMergedCodexModels(c, candidatesRequest, options = {}) {
11448
11540
  const [upstreamCatalog, candidates] = await Promise.all([tryGetCodexCatalog(c), Promise.resolve(candidatesRequest).catch((error) => {
@@ -11482,12 +11574,6 @@ async function handleMergedCodexModels(c, candidatesRequest, options = {}) {
11482
11574
  ...upstreamCatalog ?? {},
11483
11575
  models
11484
11576
  };
11485
- debugJson(logger$8, "models.codex.merged_response", {
11486
- upstreamCount: upstreamModels.length,
11487
- codexProviderAliasCount: codexProviderAliases.length,
11488
- syntheticCount: syntheticModels.length,
11489
- models: response
11490
- });
11491
11577
  return c.json(response);
11492
11578
  }
11493
11579
  function createCatalogAlias(model, slug, providerName) {
@@ -12034,6 +12120,74 @@ providerModelRoutes.get("/", async (c) => {
12034
12120
  }
12035
12121
  });
12036
12122
  //#endregion
12123
+ //#region src/lib/zstd-request.ts
12124
+ const ZSTD_CONTENT_ENCODING = "zstd";
12125
+ const INVALID_BODY_STATUS = 400;
12126
+ let nodeZlibPromise = null;
12127
+ const zstdDecompressionMiddleware = async (c, next) => {
12128
+ if (c.req.header("content-encoding")?.trim().toLowerCase() !== ZSTD_CONTENT_ENCODING) return next();
12129
+ try {
12130
+ const decompressedBody = await decompressZstd(new Uint8Array(await c.req.raw.arrayBuffer()));
12131
+ const headers = new Headers(c.req.raw.headers);
12132
+ headers.delete("content-encoding");
12133
+ headers.delete("content-length");
12134
+ c.req.raw = new Request(c.req.raw.url, {
12135
+ body: decompressedBody,
12136
+ headers,
12137
+ method: c.req.raw.method,
12138
+ signal: c.req.raw.signal
12139
+ });
12140
+ c.req.bodyCache = {};
12141
+ } catch {
12142
+ return c.json({ error: {
12143
+ message: "Failed to decompress zstd request body.",
12144
+ type: "invalid_request_error"
12145
+ } }, INVALID_BODY_STATUS);
12146
+ }
12147
+ return next();
12148
+ };
12149
+ const decompressZstd = async (input) => {
12150
+ const bun = getBunRuntime();
12151
+ if (bun?.zstdDecompress) return toUint8Array(await bun.zstdDecompress(input));
12152
+ const nodeZlib = await getNodeZlib();
12153
+ if (nodeZlib?.zstdDecompress) return new Promise((resolve, reject) => {
12154
+ nodeZlib.zstdDecompress?.(input, (error, result) => {
12155
+ if (error) {
12156
+ reject(error);
12157
+ return;
12158
+ }
12159
+ resolve(toUint8Array(result));
12160
+ });
12161
+ });
12162
+ return decompress(input);
12163
+ };
12164
+ const getBunRuntime = () => globalThis.Bun;
12165
+ const getNodeZlib = async () => {
12166
+ nodeZlibPromise ??= import("node:zlib").then((module) => module).catch(() => null);
12167
+ return nodeZlibPromise;
12168
+ };
12169
+ const toUint8Array = (data) => {
12170
+ if (data instanceof Uint8Array) return data;
12171
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
12172
+ return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
12173
+ };
12174
+ //#endregion
12175
+ //#region src/services/qoder/responses-compat.ts
12176
+ const NATIVE_SEARCH = new Set(["web_search", "web_search_preview"]);
12177
+ function qoderResponsesPayload(payload) {
12178
+ const tools = payload.tools ?? [];
12179
+ if (!tools.some((tool) => typeof tool.type === "string" && NATIVE_SEARCH.has(tool.type))) return payload;
12180
+ const choice = payload.tool_choice;
12181
+ if (typeof choice === "object" && choice !== null && NATIVE_SEARCH.has(choice.type)) throw new ResponsesMessagesTranslationError("Qoder does not support provider-native web search; use a client search tool");
12182
+ const supported = tools.filter((tool) => !(typeof tool.type === "string" && NATIVE_SEARCH.has(tool.type)));
12183
+ if (choice === "required" && !supported.length) throw new ResponsesMessagesTranslationError("Qoder cannot satisfy a required native web search tool");
12184
+ return {
12185
+ ...payload,
12186
+ tools: supported,
12187
+ instructions: [payload.instructions, "Provider-native web search is unavailable on this route. Do not claim to have searched the web unless an available client tool actually returns search results."].filter(Boolean).join("\n\n")
12188
+ };
12189
+ }
12190
+ //#endregion
12037
12191
  //#region src/routes/responses/custom-tool-input-stream-decoder.ts
12038
12192
  const CUSTOM_TOOL_INPUT_PREFIX_TOKENS = [
12039
12193
  "{",
@@ -12919,7 +13073,8 @@ function isAnthropicResponse(value) {
12919
13073
  const logger$2 = createHandlerLogger("provider-responses-handler");
12920
13074
  const providerResponsesHandlerDependencies = { resolveProviderConfig };
12921
13075
  async function handleProviderResponsesForProvider(c, options) {
12922
- const { payload, provider } = options;
13076
+ const { provider } = options;
13077
+ let { payload } = options;
12923
13078
  debugJson(logger$2, "Responses request payload:", {
12924
13079
  payload,
12925
13080
  provider
@@ -12929,6 +13084,7 @@ async function handleProviderResponsesForProvider(c, options) {
12929
13084
  message: `Provider '${provider}' does not support the /v1/responses endpoint`,
12930
13085
  type: "invalid_request_error"
12931
13086
  } }, 400);
13087
+ if (isQoderSdkProvider(providerConfig)) payload = qoderResponsesPayload(payload);
12932
13088
  const effectiveType = resolveEffectiveProviderType(providerConfig, payload.model);
12933
13089
  const normalizedReasoningEffort = normalizeProviderResponsesReasoningEffort(payload, providerConfig);
12934
13090
  if (normalizedReasoningEffort) logger$2.debug(`Normalized reasoning effort from ${normalizedReasoningEffort.from} to ${normalizedReasoningEffort.to} based on the provider model configuration`);
@@ -12945,7 +13101,8 @@ async function handleProviderResponsesForProvider(c, options) {
12945
13101
  type: "invalid_request_error"
12946
13102
  } }, 400);
12947
13103
  filterReasoningForTransport(payload, false);
12948
- if (applyResponsesApiContextManagement(payload, (providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0)?.capabilities.limits.max_prompt_tokens, {
13104
+ const model = providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0;
13105
+ if (applyResponsesApiContextManagement(payload, model?.capabilities.limits.max_prompt_tokens, {
12949
13106
  compactThresholdRatio: .8,
12950
13107
  source: "responses"
12951
13108
  })) compactInputByLatestCompaction(payload);
@@ -13073,6 +13230,7 @@ const getResponsesEvents = (response, signal) => createResponsesSafeStream(creat
13073
13230
  //#endregion
13074
13231
  //#region src/routes/provider/responses/route.ts
13075
13232
  const providerResponsesRoutes = new Hono();
13233
+ providerResponsesRoutes.use(zstdDecompressionMiddleware);
13076
13234
  providerResponsesRoutes.post("/", async (c) => {
13077
13235
  try {
13078
13236
  const provider = c.req.param("provider") ?? "";
@@ -13330,6 +13488,7 @@ const getTrimmedHeader = (c, name) => {
13330
13488
  //#endregion
13331
13489
  //#region src/routes/responses/route.ts
13332
13490
  const responsesRoutes = new Hono();
13491
+ responsesRoutes.use(zstdDecompressionMiddleware);
13333
13492
  responsesRoutes.post("/", async (c) => {
13334
13493
  try {
13335
13494
  return await handleResponses(c);
@@ -13380,6 +13539,14 @@ tokenUsageRoute.get("/events", async (c) => {
13380
13539
  return c.json(eventsPage);
13381
13540
  });
13382
13541
  //#endregion
13542
+ //#region src/routes/qoder/route.ts
13543
+ const qoderRoutes = new Hono();
13544
+ qoderRoutes.get("/usage", async (c) => {
13545
+ const provider = getProviderConfig("qoder");
13546
+ if (!provider || !isQoderSdkProvider(provider)) return c.json({ error: { message: "Builtin Qoder provider is not configured or is disabled" } }, 404);
13547
+ return forwardQoder(provider, "/usage", { signal: c.req.raw.signal });
13548
+ });
13549
+ //#endregion
13383
13550
  //#region src/routes/usage/route.ts
13384
13551
  const usageRoute = new Hono();
13385
13552
  usageRoute.get("/", async (c) => {
@@ -13393,54 +13560,69 @@ usageRoute.get("/", async (c) => {
13393
13560
  });
13394
13561
  //#endregion
13395
13562
  //#region src/server.ts
13396
- const server = new Hono();
13397
- server.use(traceIdMiddleware);
13398
- server.use(logger());
13399
- server.use(cors());
13400
- server.use("*", createAuthMiddleware({
13401
- allowUnauthenticatedPaths: [
13402
- "/",
13403
- "/usage-viewer",
13404
- "/usage-viewer/"
13405
- ],
13406
- shouldSkipPath: (path) => path.startsWith("/admin/")
13407
- }));
13408
- server.use("/admin/*", createAuthMiddleware({
13409
- getApiKeys: getConfiguredAdminApiKeys,
13410
- allowUnauthenticatedPaths: [],
13411
- allowWhenNoApiKeys: false
13412
- }));
13413
- server.use(zstdDecompressionMiddleware);
13414
- server.get("/", (c) => c.text("Server running"));
13415
- server.get("/usage-viewer", (c) => {
13416
- const usageViewerFileUrl = new URL("../pages/index.html", import.meta.url);
13417
- return c.html(readFileSync(usageViewerFileUrl, "utf8"));
13418
- });
13419
- server.get("/usage-viewer/", (c) => c.redirect("/usage-viewer", 301));
13420
- server.route("/chat/completions", completionRoutes);
13421
- server.route("/admin/config", configRoutes);
13422
- server.route("/models", modelRoutes);
13423
- server.route("/embeddings", embeddingRoutes);
13424
- server.route("/usage", usageRoute);
13425
- server.route("/token-usage", tokenUsageRoute);
13426
- server.route("/responses", responsesRoutes);
13427
- server.route("/alpha/search", alphaSearchRoutes);
13428
- server.route("/images", imageRoutes);
13429
- server.route("/v1/chat/completions", completionRoutes);
13430
- server.route("/v1/models", modelRoutes);
13431
- server.route("/v1/embeddings", embeddingRoutes);
13432
- server.route("/v1/responses", responsesRoutes);
13433
- server.route("/v1/alpha/search", alphaSearchRoutes);
13434
- server.route("/v1/images", imageRoutes);
13435
- server.route("/v1/messages", messageRoutes);
13436
- server.route("/:provider/v1/messages", providerMessageRoutes);
13437
- server.route("/:provider/v1/models", providerModelRoutes);
13438
- server.route("/:provider/v1/responses", providerResponsesRoutes);
13439
- server.route("/:provider/v1/alpha/search", providerAlphaSearchRoutes);
13440
- server.route("/:provider/v1/images", providerImageRoutes);
13441
- server.route("/:provider/models", providerModelRoutes);
13442
- server.route("/:provider/responses", providerResponsesRoutes);
13443
- server.route("/:provider/alpha/search", providerAlphaSearchRoutes);
13444
- server.route("/:provider/images", providerImageRoutes);
13563
+ function resolveSameOriginCorsOrigin(origin, context) {
13564
+ if (!origin) return null;
13565
+ try {
13566
+ return origin === new URL(context.req.url).origin ? origin : null;
13567
+ } catch {
13568
+ return null;
13569
+ }
13570
+ }
13571
+ function createServer(options = {}) {
13572
+ const server = new Hono();
13573
+ const networkExposed = options.networkExposed ?? false;
13574
+ server.use(traceIdMiddleware);
13575
+ server.use(logger());
13576
+ server.use(networkExposed ? cors({ origin: resolveSameOriginCorsOrigin }) : cors());
13577
+ server.use("*", createAuthMiddleware({
13578
+ getApiKeys: options.getApiKeys,
13579
+ allowUnauthenticatedPaths: [
13580
+ "/",
13581
+ "/usage-viewer",
13582
+ "/usage-viewer/"
13583
+ ],
13584
+ shouldSkipPath: (path) => path.startsWith("/admin/"),
13585
+ allowWhenNoApiKeys: !networkExposed
13586
+ }));
13587
+ server.use("/admin/*", createAuthMiddleware({
13588
+ getApiKeys: getConfiguredAdminApiKeys,
13589
+ allowUnauthenticatedPaths: [],
13590
+ allowWhenNoApiKeys: false
13591
+ }));
13592
+ server.get("/", (c) => c.text("Server running"));
13593
+ server.get("/usage-viewer", (c) => {
13594
+ const usageViewerFileUrl = new URL("../pages/index.html", import.meta.url);
13595
+ return c.html(readFileSync(usageViewerFileUrl, "utf8"));
13596
+ });
13597
+ server.get("/usage-viewer/", (c) => c.redirect("/usage-viewer", 301));
13598
+ server.route("/chat/completions", completionRoutes);
13599
+ server.route("/admin/config", configRoutes);
13600
+ server.route("/models", modelRoutes);
13601
+ server.route("/embeddings", embeddingRoutes);
13602
+ server.route("/usage", usageRoute);
13603
+ server.route("/qoder", qoderRoutes);
13604
+ server.route("/token-usage", tokenUsageRoute);
13605
+ server.route("/responses", responsesRoutes);
13606
+ server.route("/alpha/search", alphaSearchRoutes);
13607
+ server.route("/images", imageRoutes);
13608
+ server.route("/v1/chat/completions", completionRoutes);
13609
+ server.route("/v1/models", modelRoutes);
13610
+ server.route("/v1/embeddings", embeddingRoutes);
13611
+ server.route("/v1/responses", responsesRoutes);
13612
+ server.route("/v1/alpha/search", alphaSearchRoutes);
13613
+ server.route("/v1/images", imageRoutes);
13614
+ server.route("/v1/messages", messageRoutes);
13615
+ server.route("/:provider/v1/messages", providerMessageRoutes);
13616
+ server.route("/:provider/v1/models", providerModelRoutes);
13617
+ server.route("/:provider/v1/responses", providerResponsesRoutes);
13618
+ server.route("/:provider/v1/alpha/search", providerAlphaSearchRoutes);
13619
+ server.route("/:provider/v1/images", providerImageRoutes);
13620
+ server.route("/:provider/models", providerModelRoutes);
13621
+ server.route("/:provider/responses", providerResponsesRoutes);
13622
+ server.route("/:provider/alpha/search", providerAlphaSearchRoutes);
13623
+ server.route("/:provider/images", providerImageRoutes);
13624
+ return server;
13625
+ }
13626
+ createServer();
13445
13627
  //#endregion
13446
- export { server };
13628
+ export { createServer };