@nick3/copilot-api 1.10.9 → 1.10.30

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 (57) hide show
  1. package/README.md +140 -45
  2. package/README.zh-CN.md +140 -45
  3. package/dist/{account-COtMmvzU.js → account-DpW8RaT6.js} +3 -3
  4. package/dist/{account-COtMmvzU.js.map → account-DpW8RaT6.js.map} +1 -1
  5. package/dist/admin/AGENTS.md +19 -0
  6. package/dist/admin/assets/{index-BAh4eOwM.js → index-BGmG-ckX.js} +34 -34
  7. package/dist/admin/index.html +1 -1
  8. package/dist/auth-CnwhQOu6.js +327 -0
  9. package/dist/auth-CnwhQOu6.js.map +1 -0
  10. package/dist/{check-usage-DdevqHE5.js → check-usage-CseltoFJ.js} +4 -42
  11. package/dist/check-usage-CseltoFJ.js.map +1 -0
  12. package/dist/config-XZv75uoU.js +591 -0
  13. package/dist/config-XZv75uoU.js.map +1 -0
  14. package/dist/{debug-BMo6ltbp.js → debug-D8xHblDV.js} +18 -7
  15. package/dist/debug-D8xHblDV.js.map +1 -0
  16. package/dist/main.js +5 -10
  17. package/dist/main.js.map +1 -1
  18. package/dist/mcp-http-BhELuvog.js +2 -0
  19. package/dist/mcp-http-DI4Vz01p.js +82 -0
  20. package/dist/mcp-http-DI4Vz01p.js.map +1 -0
  21. package/dist/mcp-http-config-DMdUDz1D.js +39 -0
  22. package/dist/mcp-http-config-DMdUDz1D.js.map +1 -0
  23. package/dist/mcp-pLTPS0tO.js +79 -0
  24. package/dist/mcp-pLTPS0tO.js.map +1 -0
  25. package/dist/{tool-search-BrN7M0Dd.js → mcp-server-DEqHrXFq.js} +25 -2
  26. package/dist/mcp-server-DEqHrXFq.js.map +1 -0
  27. package/dist/{paths-CclKwouX.js → paths-Bpsb62LK.js} +3 -1
  28. package/dist/paths-Bpsb62LK.js.map +1 -0
  29. package/dist/{poll-access-token-BAgM2-7k.js → poll-access-token-GzVkiTH8.js} +71 -4
  30. package/dist/poll-access-token-GzVkiTH8.js.map +1 -0
  31. package/dist/{request-outbound-BJjWS_jF.js → request-outbound-BkEA8Wgb.js} +1 -1
  32. package/dist/{request-outbound-Pu1kp2x8.js → request-outbound-DZTxxtcx.js} +3 -3
  33. package/dist/{request-outbound-Pu1kp2x8.js.map → request-outbound-DZTxxtcx.js.map} +1 -1
  34. package/dist/{proxy-_U-hgwIn.js → responses-bridge-registry-JjUvTCST.js} +127 -581
  35. package/dist/responses-bridge-registry-JjUvTCST.js.map +1 -0
  36. package/dist/{server-GxNB5Syq.js → server-BYKxAFro.js} +678 -249
  37. package/dist/server-BYKxAFro.js.map +1 -0
  38. package/dist/start-DVlCJ0Ma.js +526 -0
  39. package/dist/start-DVlCJ0Ma.js.map +1 -0
  40. package/dist/token-9O2KJcF5.js +946 -0
  41. package/dist/token-9O2KJcF5.js.map +1 -0
  42. package/package.json +2 -2
  43. package/dist/auth-B0y-2njL.js +0 -226
  44. package/dist/auth-B0y-2njL.js.map +0 -1
  45. package/dist/check-usage-DdevqHE5.js.map +0 -1
  46. package/dist/debug-BMo6ltbp.js.map +0 -1
  47. package/dist/get-copilot-token-8Rm-rVsp.js +0 -17
  48. package/dist/get-copilot-token-8Rm-rVsp.js.map +0 -1
  49. package/dist/mcp-9Hgepkc5.js +0 -37
  50. package/dist/mcp-9Hgepkc5.js.map +0 -1
  51. package/dist/paths-CclKwouX.js.map +0 -1
  52. package/dist/poll-access-token-BAgM2-7k.js.map +0 -1
  53. package/dist/proxy-_U-hgwIn.js.map +0 -1
  54. package/dist/server-GxNB5Syq.js.map +0 -1
  55. package/dist/start-DdrurmQ3.js +0 -274
  56. package/dist/start-DdrurmQ3.js.map +0 -1
  57. package/dist/tool-search-BrN7M0Dd.js.map +0 -1
@@ -1,109 +1,27 @@
1
- import { A as state, D as prepareInteractionHeaders, E as prepareForCompact, I as compactAutoContinuePromptStarts, L as compactMessageSections, N as requestContext, O as prepareMessageProxyHeaders, P as resolveTraceId, S as copilotWebSocketHeaders, T as normalizeDomain, _ as HTTPError, b as copilotHeaders, c as getUUID, d as parseUserIdMetadata, f as resolveAffinityKey, g as getCopilotUsage, h as getDeviceCode, j as captureOutboundHeadersSnapshot, k as accountFromState, l as isNullish, m as getGitHubUser, o as generateRequestIdFromPayload, p as sleep, s as getRootSessionId, t as pollAccessToken, u as normalizeStableSessionId, v as forwardError, y as copilotBaseUrl, z as compactSystemPromptStarts } from "./poll-access-token-BAgM2-7k.js";
2
- import { a as getAccountClientIdentityByLoginAndApp, b as getCurrentIdentityEnvironment, d as loadRegistry, g as saveRegistry, h as saveAccountToken, l as listAccountsFromRegistry, m as removeAccountToken, p as removeAccountFromRegistry, r as addAccountToRegistry, t as isAccountType } from "./account-COtMmvzU.js";
3
- import { r as ensurePaths, t as PATHS } from "./paths-CclKwouX.js";
4
- import { i as getRequestOutboundStore, r as getRedactedHeaderKeys } from "./request-outbound-Pu1kp2x8.js";
5
- import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, i as isBridgeToolSearchName, l as selectDeferredToolsByNames, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, t as BRIDGE_TOOL_SEARCH_NAME, u as shouldEnableResponsesToolSearch } from "./tool-search-BrN7M0Dd.js";
6
- import { A as getModelRefreshIntervalMs, B as isResponsesApiWebSocketEnabled, C as getAnthropicApiKey, D as getLogLevel, E as getExtraPromptForModel, F as isForceAgentEnabled, H as resolveModelAlias, I as isMessageStartInputTokensFallbackEnabled, L as isMessagesApiEnabled, M as getReasoningEffortForModel, N as getSmallModel, O as getModelAliases, P as isAccountAffinityEnabled, R as isResponsesApiContextManagementModel, S as getAliasTargetSet, T as getConfig, U as shouldCompactUseSmallModel, V as mergeConfigWithDefaults, _ as toLocalDateString, b as isDevModeEnabled, c as applySharedSessionAffinityRetention, d as getClientIpInfo, f as getRequestHistoryStore, g as normalizeMessagesUsage, h as normalizeEmbeddingsUsage, j as getProviderConfig, k as getModelAliasesInfo, l as extractResponsesUsageFromResult, m as normalizeChatCompletionsUsage, o as updateQuotaRefreshSchedulerFromConfig, p as getStatsStore, s as accountsManager, t as getProxyEnvDispatcher, u as extractResponsesUsageFromStreamEvent, v as copilotFetch, w as getClaudeTokenMultiplier, x as PROVIDER_TYPE_ANTHROPIC, y as flushPendingCapture, z as isResponsesApiWebSearchEnabled } from "./proxy-_U-hgwIn.js";
1
+ import { A as accountFromState, B as compactSystemPromptStarts, C as copilotHeaders, D as prepareForCompact, E as normalizeDomain, F as resolveTraceId, L as compactAutoContinuePromptStarts, M as captureOutboundHeadersSnapshot, O as prepareInteractionHeaders, P as requestContext, R as compactMessageSections, S as copilotBaseUrl, T as copilotWebSocketHeaders, b as HTTPError, c as getUUID, d as parseUserIdMetadata, f as resolveAffinityKey, g as getCopilotUsage, h as getDeviceCode, j as state, k as prepareMessageProxyHeaders, l as isNullish, m as getGitHubUser, o as generateRequestIdFromPayload, p as sleep, s as getRootSessionId, t as pollAccessToken, u as normalizeStableSessionId, v as getProxyEnvDispatcher, x as forwardError } from "./poll-access-token-GzVkiTH8.js";
2
+ import { a as getAccountClientIdentityByLoginAndApp, b as getCurrentIdentityEnvironment, d as loadRegistry, g as saveRegistry, h as saveAccountToken, l as listAccountsFromRegistry, m as removeAccountToken, p as removeAccountFromRegistry, r as addAccountToRegistry, t as isAccountType } from "./account-DpW8RaT6.js";
3
+ import { r as ensurePaths, t as PATHS } from "./paths-Bpsb62LK.js";
4
+ import { C as isResponsesApiWebSocketEnabled, D as resolveModelAlias, E as resolveMappedModel, S as isResponsesApiWebSearchEnabled, T as mergeConfigWithDefaults, _ as isAccountAffinityEnabled, a as getConfig, b as isMessagesApiEnabled, c as getModelAliases, d as getProviderConfig, g as getSmallModel, i as getClaudeTokenMultiplier, k as shouldCompactUseSmallModel, l as getModelAliasesInfo, m as getReasoningEffortForModel, n as getAliasTargetSet, o as getExtraPromptForModel, p as getRawProviderConfig, r as getAnthropicApiKey, s as getLogLevel, t as PROVIDER_TYPE_ANTHROPIC, u as getModelRefreshIntervalMs, v as isForceAgentEnabled, x as isResponsesApiContextManagementEnabled, y as isMessageStartInputTokensFallbackEnabled } from "./config-XZv75uoU.js";
5
+ import { a as forwardCodexResponses, n as setupCodexToken } from "./token-9O2KJcF5.js";
6
+ import { i as getRequestOutboundStore, r as getRedactedHeaderKeys } from "./request-outbound-DZTxxtcx.js";
7
+ import { i as isMcpHttpEnabledFromEnv, n as DEFAULT_MCP_HTTP_PATH } from "./mcp-http-config-DMdUDz1D.js";
8
+ import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, i as isBridgeToolSearchName, l as selectDeferredToolsByNames, n as BRIDGE_TOOL_SEARCH_NAME, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, u as shouldEnableResponsesToolSearch } from "./mcp-server-DEqHrXFq.js";
9
+ import { n as handleStreamableHttpMcpRequest, r as mcpHttpCorsOptions } from "./mcp-http-DI4Vz01p.js";
10
+ import { C as createAuthMiddleware, S as isDevModeEnabled, _ as normalizeMessagesUsage, b as copilotFetch, c as accountsManager, d as extractResponsesUsageFromStreamEvent, f as getClientIpInfo, g as normalizeEmbeddingsUsage, h as normalizeChatCompletionsUsage, l as applySharedSessionAffinityRetention, m as getStatsStore, p as getRequestHistoryStore, s as updateQuotaRefreshSchedulerFromConfig, t as closeResponsesBridge, u as extractResponsesUsageFromResult, v as normalizeResponsesUsage, x as flushPendingCapture, y as toLocalDateString } from "./responses-bridge-registry-JjUvTCST.js";
7
11
  import consola from "consola";
8
12
  import fs, { readFile } from "node:fs/promises";
9
- import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
13
+ import { createHash, randomUUID } from "node:crypto";
10
14
  import * as path$1 from "node:path";
11
15
  import path from "node:path";
12
- import { fileURLToPath } from "node:url";
16
+ import { events } from "fetch-event-stream";
13
17
  import fs$1, { existsSync } from "node:fs";
18
+ import { WebSocket } from "undici";
19
+ import { fileURLToPath } from "node:url";
14
20
  import { Hono } from "hono";
15
21
  import { cors } from "hono/cors";
16
22
  import { logger } from "hono/logger";
17
23
  import { streamSSE } from "hono/streaming";
18
- import { events } from "fetch-event-stream";
19
24
  import util from "node:util";
20
- import { WebSocket } from "undici";
21
- //#region src/lib/request-auth.ts
22
- const LEGACY_API_KEY_ENV_VAR = "COPILOT_API_KEY";
23
- let warnedLegacyEnvFallback = false;
24
- let warnedLegacyConfigFallback = false;
25
- function normalizeApiKeys(apiKeys) {
26
- if (!Array.isArray(apiKeys)) {
27
- if (apiKeys !== void 0) consola.warn("Invalid auth.apiKeys config. Expected an array of strings.");
28
- return [];
29
- }
30
- const normalizedKeys = apiKeys.filter((key) => typeof key === "string").map((key) => key.trim()).filter((key) => key.length > 0);
31
- if (normalizedKeys.length !== apiKeys.length) consola.warn("Invalid auth.apiKeys entries found. Only non-empty strings are allowed.");
32
- return [...new Set(normalizedKeys)];
33
- }
34
- function getConfiguredApiKeys() {
35
- const config = getConfig();
36
- const configuredApiKeys = normalizeApiKeys(config.auth?.apiKeys);
37
- if (configuredApiKeys.length > 0) return configuredApiKeys;
38
- const envApiKey = process.env[LEGACY_API_KEY_ENV_VAR]?.trim();
39
- if (envApiKey) {
40
- if (!warnedLegacyEnvFallback) {
41
- warnedLegacyEnvFallback = true;
42
- consola.warn(`Using legacy ${LEGACY_API_KEY_ENV_VAR}. Please migrate to config.auth.apiKeys.`);
43
- }
44
- return [envApiKey];
45
- }
46
- const legacyConfigApiKey = config.apiKey?.trim();
47
- if (legacyConfigApiKey) {
48
- if (!warnedLegacyConfigFallback) {
49
- warnedLegacyConfigFallback = true;
50
- consola.warn("Using deprecated config.apiKey. Please migrate to config.auth.apiKeys.");
51
- }
52
- return [legacyConfigApiKey];
53
- }
54
- return configuredApiKeys;
55
- }
56
- function extractRequestApiKey(c) {
57
- const xApiKey = c.req.header("x-api-key")?.trim();
58
- if (xApiKey) return xApiKey;
59
- const authorization = c.req.header("authorization");
60
- if (!authorization) return null;
61
- const [scheme, ...rest] = authorization.trim().split(/\s+/);
62
- if (scheme.toLowerCase() !== "bearer") return null;
63
- return rest.join(" ").trim() || null;
64
- }
65
- function createUnauthorizedResponse(c) {
66
- c.header("WWW-Authenticate", "Bearer realm=\"copilot-api\"");
67
- return c.json({ error: {
68
- message: "Unauthorized. Provide Authorization: Bearer <key> or x-api-key.",
69
- type: "unauthorized"
70
- } }, 401);
71
- }
72
- function normalizePathname(pathname) {
73
- if (pathname.length > 1 && pathname.endsWith("/")) return pathname.slice(0, -1);
74
- return pathname;
75
- }
76
- function hasPrefixBoundary(pathname, prefix) {
77
- return pathname === prefix || pathname.startsWith(`${prefix}/`);
78
- }
79
- function timingSafeKeyCompare(a, b) {
80
- try {
81
- const aBuf = Buffer.from(a);
82
- const bBuf = Buffer.from(b);
83
- if (aBuf.length !== bBuf.length) return false;
84
- return timingSafeEqual(aBuf, bBuf);
85
- } catch {
86
- return false;
87
- }
88
- }
89
- function createAuthMiddleware(options = {}) {
90
- const getApiKeys = options.getApiKeys ?? getConfiguredApiKeys;
91
- const allowUnauthenticatedPaths = new Set((options.allowUnauthenticatedPaths ?? ["/"]).map((path) => normalizePathname(path)));
92
- const allowUnauthenticatedPathPrefixes = (options.allowUnauthenticatedPathPrefixes ?? []).map((path) => normalizePathname(path));
93
- const allowOptionsBypass = options.allowOptionsBypass ?? true;
94
- return async (c, next) => {
95
- if (allowOptionsBypass && c.req.method === "OPTIONS") return next();
96
- const pathname = normalizePathname(new URL(c.req.url, "http://local").pathname);
97
- if (allowUnauthenticatedPaths.has(pathname)) return next();
98
- if (allowUnauthenticatedPathPrefixes.some((prefix) => hasPrefixBoundary(pathname, prefix))) return next();
99
- const apiKeys = getApiKeys();
100
- if (apiKeys.length === 0) return next();
101
- const requestApiKey = extractRequestApiKey(c);
102
- if (!(requestApiKey ? apiKeys.some((apiKey) => timingSafeKeyCompare(requestApiKey, apiKey)) : false)) return createUnauthorizedResponse(c);
103
- return next();
104
- };
105
- }
106
- //#endregion
107
25
  //#region src/lib/trace.ts
108
26
  const traceIdMiddleware = async (c, next) => {
109
27
  const traceId = resolveTraceId(c.req.header("x-trace-id"));
@@ -112,7 +30,7 @@ const traceIdMiddleware = async (c, next) => {
112
30
  traceId,
113
31
  startTime: Date.now(),
114
32
  userAgent: c.req.header("user-agent") || "",
115
- sessionAffinity: c.req.header("x-session-affinity"),
33
+ sessionAffinity: c.req.header("x-session-affinity") ?? c.req.header("x-client-request-id"),
116
34
  parentSessionId: c.req.header("x-parent-session-id")
117
35
  };
118
36
  await requestContext.run(context, async () => {
@@ -302,7 +220,7 @@ const findEndpointModel = (sdkModelId) => {
302
220
  const models = getAvailableModels();
303
221
  const exactMatch = models.find((m) => m.id === sdkModelId);
304
222
  if (exactMatch) return exactMatch;
305
- const normalized = _normalizeSdkModelId(sdkModelId);
223
+ const normalized = normalizeSdkModelId(sdkModelId);
306
224
  if (!normalized) return;
307
225
  const modelName = `claude-${normalized.family}-${normalized.version}`;
308
226
  return models.find((m) => m.id === modelName);
@@ -317,22 +235,22 @@ const findEndpointModel = (sdkModelId) => {
317
235
  * - "claude-haiku-3-5-20250514" -> { family: "haiku", version: "3.5" }
318
236
  * - "claude-haiku-4.5" -> { family: "haiku", version: "4.5" }
319
237
  */
320
- const _normalizeSdkModelId = (sdkModelId) => {
238
+ const normalizeSdkModelId = (sdkModelId) => {
321
239
  const withoutDate = sdkModelId.toLowerCase().replace(/-\d{8}$/, "");
322
- const pattern1 = withoutDate.match(/^claude-(\w+)-(\d+)-(\d+)$/);
240
+ const pattern1 = withoutDate.match(/^claude-(\w+)-(\d+)\.(\d+)$/);
323
241
  if (pattern1) return {
324
242
  family: pattern1[1],
325
243
  version: `${pattern1[2]}.${pattern1[3]}`
326
244
  };
327
- const pattern2 = withoutDate.match(/^claude-(\d+)-(\d+)-(\w+)$/);
245
+ const pattern2 = withoutDate.match(/^claude-(\w+)-(\d+)-(\d+)$/);
328
246
  if (pattern2) return {
329
- family: pattern2[3],
330
- version: `${pattern2[1]}.${pattern2[2]}`
247
+ family: pattern2[1],
248
+ version: `${pattern2[2]}.${pattern2[3]}`
331
249
  };
332
- const pattern3 = withoutDate.match(/^claude-(\w+)-(\d+)\.(\d+)$/);
250
+ const pattern3 = withoutDate.match(/^claude-(\d+)-(\d+)-(\w+)$/);
333
251
  if (pattern3) return {
334
- family: pattern3[1],
335
- version: `${pattern3[2]}.${pattern3[3]}`
252
+ family: pattern3[3],
253
+ version: `${pattern3[1]}.${pattern3[2]}`
336
254
  };
337
255
  const pattern4 = withoutDate.match(/^claude-(\w+)-(\d+)$/);
338
256
  if (pattern4) return {
@@ -411,7 +329,7 @@ const logCopilotQuotaSnapshots = (snapshots) => {
411
329
  const logCopilotRateLimitUsage = (usage) => {
412
330
  const d = new Date(usage.resetAt);
413
331
  const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
414
- consola.info(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
332
+ consola.log(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
415
333
  };
416
334
  const isCopilotQuotaSnapshot = (value) => {
417
335
  if (!value || typeof value !== "object") return false;
@@ -693,10 +611,95 @@ const getTokenCount = async (payload, model) => {
693
611
  output: outputTokens
694
612
  };
695
613
  };
614
+ const SYSTEM_REMINDER_START = "<system-reminder>";
615
+ const SYSTEM_REMINDER_END = "</system-reminder>";
616
+ const SUBAGENT_START_HOOK_ADDITIONAL_PREFIX = "SubagentStart hook additional";
696
617
  const IDE_EXECUTE_CODE_TOOL = "mcp__ide__executeCode";
697
618
  const IDE_GET_DIAGNOSTICS_TOOL = "mcp__ide__getDiagnostics";
698
619
  const IDE_GET_DIAGNOSTICS_DESCRIPTION = "Get language diagnostics from VS Code. Returns errors, warnings, information, and hints for files in the workspace.";
699
620
  const PDF_FILE_READ_PREFIX = "PDF file read:";
621
+ const createTextBlock = (text) => ({
622
+ type: "text",
623
+ text
624
+ });
625
+ const appendTextSegment = (base, addition) => {
626
+ if (base.length === 0) return addition;
627
+ if (addition.length === 0) return base;
628
+ return `${base}\n\n${addition}`;
629
+ };
630
+ const ensureSystemReminderText = (text) => {
631
+ if (text.startsWith(SYSTEM_REMINDER_START)) return text;
632
+ return `${SYSTEM_REMINDER_START}\n${text.trim()}\n${SYSTEM_REMINDER_END}`;
633
+ };
634
+ const normalizeSystemStringForMerge = (text) => {
635
+ if (!text.startsWith(SUBAGENT_START_HOOK_ADDITIONAL_PREFIX)) return ensureSystemReminderText(text);
636
+ const lineBreakMatch = /\r?\n/.exec(text);
637
+ if (!lineBreakMatch) return [createTextBlock(ensureSystemReminderText(text))];
638
+ const firstLine = text.slice(0, lineBreakMatch.index);
639
+ const rest = text.slice(lineBreakMatch.index + lineBreakMatch[0].length);
640
+ return [createTextBlock(ensureSystemReminderText(firstLine)), ...rest.length > 0 ? [createTextBlock(ensureSystemReminderText(rest))] : []];
641
+ };
642
+ const normalizeSystemContentForMerge = (content) => {
643
+ if (typeof content === "string") return normalizeSystemStringForMerge(content);
644
+ return content.map((block) => block.text.startsWith(SYSTEM_REMINDER_START) ? block : {
645
+ ...block,
646
+ text: ensureSystemReminderText(block.text)
647
+ });
648
+ };
649
+ const toSystemTextBlocks = (content) => {
650
+ return typeof content === "string" ? [createTextBlock(content)] : [...content];
651
+ };
652
+ const mergeSystemPromptContent = (current, addition) => {
653
+ if (current === void 0) return typeof addition === "string" ? addition : [...addition];
654
+ if (typeof current === "string" && typeof addition === "string") return appendTextSegment(current, addition);
655
+ return [...toSystemTextBlocks(current), ...toSystemTextBlocks(addition)];
656
+ };
657
+ const prependSystemContentToUserMessage = (message, addition) => {
658
+ if (typeof message.content === "string" && typeof addition === "string") {
659
+ message.content = appendTextSegment(addition, message.content);
660
+ return;
661
+ }
662
+ if (Array.isArray(message.content)) {
663
+ const lastToolResultIndex = message.content.findLastIndex((block) => block.type === "tool_result");
664
+ if (lastToolResultIndex >= 0) {
665
+ message.content = [
666
+ ...message.content.slice(0, lastToolResultIndex + 1),
667
+ ...toSystemTextBlocks(addition),
668
+ ...message.content.slice(lastToolResultIndex + 1)
669
+ ];
670
+ return;
671
+ }
672
+ }
673
+ message.content = [...toSystemTextBlocks(addition), ...typeof message.content === "string" ? [createTextBlock(message.content)] : message.content];
674
+ };
675
+ const normalizeSystemMessages = (payload) => {
676
+ if (!Array.isArray(payload.messages) || !payload.messages.some((msg) => msg.role === "system")) return;
677
+ const normalizedMessages = [];
678
+ let system = payload.system;
679
+ for (const message of payload.messages) {
680
+ if (message.role === "system") {
681
+ const normalizedContent = normalizeSystemContentForMerge(message.content);
682
+ const previousMessage = normalizedMessages.at(-1);
683
+ if (previousMessage?.role === "user") prependSystemContentToUserMessage(previousMessage, normalizedContent);
684
+ else if (!previousMessage) system = mergeSystemPromptContent(system, normalizedContent);
685
+ continue;
686
+ }
687
+ normalizedMessages.push(message);
688
+ }
689
+ payload.messages = normalizedMessages;
690
+ payload.system = system;
691
+ };
692
+ const isVersionAtLeast = (version, minimumMajor, minimumMinor) => {
693
+ const [majorPart, minorPart = "0"] = version.split(".");
694
+ const major = Number.parseInt(majorPart, 10);
695
+ const minor = Number.parseInt(minorPart, 10);
696
+ if (!Number.isInteger(major) || !Number.isInteger(minor)) return false;
697
+ return major > minimumMajor || major === minimumMajor && minor >= minimumMinor;
698
+ };
699
+ const shouldSummarizeThinkingDisplayForModel = (model) => {
700
+ const normalized = normalizeSdkModelId(model);
701
+ return Boolean(normalized && isVersionAtLeast(normalized.version, 4, 7));
702
+ };
700
703
  const getBlockCacheControl = (block) => {
701
704
  if (!block || block.type === "thinking") return;
702
705
  const cacheControl = block.cache_control;
@@ -966,7 +969,7 @@ const prepareMessagesApiPayload = (payload, selectedModel) => {
966
969
  if (selectedModel?.capabilities.supports.adaptive_thinking && !disableThink) {
967
970
  payload.thinking = { type: "adaptive" };
968
971
  if (!hasThinking) payload.thinking.display = "summarized";
969
- if (payload.model === "claude-opus-4.7") payload.thinking.display = "summarized";
972
+ if (shouldSummarizeThinkingDisplayForModel(payload.model)) payload.thinking.display = "summarized";
970
973
  let effort = getReasoningEffortForModel(payload.model);
971
974
  if (effort === "none" || effort === "minimal") effort = "low";
972
975
  const reasoningEffort = selectedModel.capabilities.supports.reasoning_effort;
@@ -1884,7 +1887,11 @@ const PROVIDER_CONFIG_FIELDS = [
1884
1887
  "models",
1885
1888
  "adjustInputTokens"
1886
1889
  ];
1887
- const PROVIDER_AUTH_TYPES = ["authorization", "x-api-key"];
1890
+ const PROVIDER_AUTH_TYPES = [
1891
+ "authorization",
1892
+ "oauth2",
1893
+ "x-api-key"
1894
+ ];
1888
1895
  const PROVIDER_CONFIG_KEYS = new Set(PROVIDER_CONFIG_FIELDS);
1889
1896
  function isProviderAuthType(value) {
1890
1897
  return PROVIDER_AUTH_TYPES.includes(value);
@@ -2493,13 +2500,22 @@ adminApiRoutes.get("/accounts", async (c) => {
2493
2500
  unlimited: s.unlimited,
2494
2501
  failed: s.failed,
2495
2502
  failureReason: s.failureReason,
2496
- enabled: s.enabled
2503
+ enabled: s.enabled,
2504
+ lastModelsFetch: s.lastModelsFetch,
2505
+ isRefreshingModels: s.isRefreshingModels
2497
2506
  },
2498
2507
  stats
2499
2508
  };
2500
2509
  });
2501
2510
  return c.json({ items });
2502
2511
  });
2512
+ adminApiRoutes.post("/accounts/models/refresh", async (c) => {
2513
+ const { failedCount } = await accountsManager.refreshAllModelsNow();
2514
+ return c.json({
2515
+ ok: true,
2516
+ failedCount
2517
+ });
2518
+ });
2503
2519
  adminApiRoutes.get("/requests", (c) => {
2504
2520
  const p = new URL(c.req.url, "http://local").searchParams;
2505
2521
  const limit = parseFiniteNumber(p.get("limit")) ?? 50;
@@ -3864,7 +3880,7 @@ function selectionFailureResponse$2(c, params) {
3864
3880
  }
3865
3881
  //#endregion
3866
3882
  //#region src/routes/chat-completions/handler.ts
3867
- const logger$6 = createHandlerLogger("chat-completions-handler");
3883
+ const logger$7 = createHandlerLogger("chat-completions-handler");
3868
3884
  function buildChatCompletionCandidates(clientModel) {
3869
3885
  return [{
3870
3886
  modelId: clientModel,
@@ -4001,12 +4017,12 @@ async function writeChatCompletionsStreamError(stream, message) {
4001
4017
  } }) });
4002
4018
  await stream.writeSSE({ data: "[DONE]" });
4003
4019
  } catch (streamError) {
4004
- logger$6.warn("Failed to write chat completions stream error event:", streamError);
4020
+ logger$7.warn("Failed to write chat completions stream error event:", streamError);
4005
4021
  }
4006
4022
  }
4007
4023
  async function selectChatCompletionAccount(params) {
4008
4024
  const { c, store, request, payload, clientModel, streamRequested, normalizedPromptCacheKey } = params;
4009
- debugJsonTail(logger$6, "Request payload:", {
4025
+ debugJsonTail(logger$7, "Request payload:", {
4010
4026
  value: payload,
4011
4027
  tailLength: 400
4012
4028
  });
@@ -4041,9 +4057,9 @@ async function selectChatCompletionAccount(params) {
4041
4057
  async function logTokenCountForRequest(params) {
4042
4058
  try {
4043
4059
  const tokenCount = await getTokenCount(params.payload, params.selectedModel);
4044
- logger$6.info("Current token count:", tokenCount);
4060
+ logger$7.info("Current token count:", tokenCount);
4045
4061
  } catch (error) {
4046
- logger$6.warn("Failed to calculate token count:", error);
4062
+ logger$7.warn("Failed to calculate token count:", error);
4047
4063
  }
4048
4064
  }
4049
4065
  function applyDefaultMaxTokens(payload, selectedModel) {
@@ -4052,7 +4068,7 @@ function applyDefaultMaxTokens(payload, selectedModel) {
4052
4068
  ...payload,
4053
4069
  max_tokens: selectedModel.capabilities.limits.max_output_tokens
4054
4070
  };
4055
- debugJson(logger$6, "Set max_tokens to:", updated.max_tokens);
4071
+ debugJson(logger$7, "Set max_tokens to:", updated.max_tokens);
4056
4072
  return updated;
4057
4073
  }
4058
4074
  async function handleStreamingRequest(params) {
@@ -4086,7 +4102,7 @@ async function handleStreamingRequest(params) {
4086
4102
  premiumUnlimitedBefore,
4087
4103
  response
4088
4104
  });
4089
- logger$6.debug("Streaming response");
4105
+ logger$7.debug("Streaming response");
4090
4106
  return streamSSE(c, (stream) => streamChatCompletionsAndLog$1({
4091
4107
  stream,
4092
4108
  response,
@@ -4141,7 +4157,7 @@ async function handleNonStreamingUpstreamResponse(params) {
4141
4157
  let upstreamErrorMessageRaw;
4142
4158
  const finishedAtMs = Date.now();
4143
4159
  try {
4144
- debugJson(logger$6, "Non-streaming response:", response);
4160
+ debugJson(logger$7, "Non-streaming response:", response);
4145
4161
  return c.json(response);
4146
4162
  } catch (error) {
4147
4163
  const details = await extractErrorObservability(error);
@@ -4194,7 +4210,7 @@ async function streamChatCompletionsAndLog$1(params) {
4194
4210
  if (ttfbMs === void 0) ttfbMs = Date.now() - request.startedAtMs;
4195
4211
  const usage = await extractUsageFromChunk(chunk);
4196
4212
  if (usage) lastUsage = usage;
4197
- debugJson(logger$6, "Streaming chunk:", chunk);
4213
+ debugJson(logger$7, "Streaming chunk:", chunk);
4198
4214
  await stream.writeSSE(chunk);
4199
4215
  }
4200
4216
  } catch (error) {
@@ -4203,7 +4219,7 @@ async function streamChatCompletionsAndLog$1(params) {
4203
4219
  errorStatus = details.errorStatus;
4204
4220
  errorMessage = details.errorMessage;
4205
4221
  upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
4206
- logger$6.warn("Streaming error:", error);
4222
+ logger$7.warn("Streaming error:", error);
4207
4223
  if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(account.id, "Unauthorized (401)");
4208
4224
  await writeChatCompletionsStreamError(stream, getUserVisibleErrorMessage(details));
4209
4225
  } finally {
@@ -4241,7 +4257,7 @@ async function extractUsageFromChunk(chunk) {
4241
4257
  try {
4242
4258
  data = typeof chunk.data === "string" ? chunk.data : await chunk.data;
4243
4259
  } catch (error) {
4244
- logger$6.warn("Failed to read chat completions usage chunk:", error);
4260
+ logger$7.warn("Failed to read chat completions usage chunk:", error);
4245
4261
  return;
4246
4262
  }
4247
4263
  if (!data || data === "[DONE]") return;
@@ -4250,7 +4266,7 @@ async function extractUsageFromChunk(chunk) {
4250
4266
  if (!parsed.usage) return void 0;
4251
4267
  return normalizeChatCompletionsUsage(parsed.usage);
4252
4268
  } catch (error) {
4253
- logger$6.warn("Failed to parse chat completions usage chunk:", {
4269
+ logger$7.warn("Failed to parse chat completions usage chunk:", {
4254
4270
  error,
4255
4271
  data
4256
4272
  });
@@ -4277,7 +4293,7 @@ async function handleNonStreamingRequest(params) {
4277
4293
  selection.confirmAffinity?.();
4278
4294
  finishedAtMs = Date.now();
4279
4295
  usage = normalizeChatCompletionsUsage(response.usage);
4280
- debugJson(logger$6, "Non-streaming response:", response);
4296
+ debugJson(logger$7, "Non-streaming response:", response);
4281
4297
  return c.json(response);
4282
4298
  } catch (error) {
4283
4299
  finishedAtMs = Date.now();
@@ -4535,8 +4551,8 @@ const createFallbackModel = (modelId) => ({
4535
4551
  });
4536
4552
  //#endregion
4537
4553
  //#region src/routes/provider/messages/count-tokens-handler.ts
4538
- const logger$5 = createHandlerLogger("provider-count-tokens-handler");
4539
- const resolveProviderConfig$2 = (c, provider) => {
4554
+ const logger$6 = createHandlerLogger("provider-count-tokens-handler");
4555
+ const resolveProviderConfig$3 = (c, provider) => {
4540
4556
  return (c.get("providerConfigResolver") ?? getProviderConfig)(provider);
4541
4557
  };
4542
4558
  async function handleProviderCountTokens(c) {
@@ -4548,7 +4564,8 @@ async function handleProviderCountTokens(c) {
4548
4564
  }
4549
4565
  async function handleProviderCountTokensForProvider(c, options) {
4550
4566
  const { payload: anthropicPayload, provider } = options;
4551
- const providerConfig = resolveProviderConfig$2(c, provider);
4567
+ normalizeSystemMessages(anthropicPayload);
4568
+ const providerConfig = resolveProviderConfig$3(c, provider);
4552
4569
  if (!providerConfig) return c.json({ error: {
4553
4570
  message: `Provider '${provider}' not found or disabled`,
4554
4571
  type: "invalid_request_error"
@@ -4559,21 +4576,21 @@ async function handleProviderCountTokensForProvider(c, options) {
4559
4576
  } }, 400);
4560
4577
  const modelId = anthropicPayload.model.trim();
4561
4578
  const modelConfig = providerConfig.models?.[modelId];
4562
- const translationOptions = providerConfig.type === "openai-compatible" ? {
4579
+ const translationOptions = providerConfig.type === "openai-compatible" || providerConfig.type === "openai-responses" ? {
4563
4580
  supportPdf: modelConfig?.supportPdf,
4564
4581
  toolContentSupportType: modelConfig?.toolContentSupportType ?? []
4565
4582
  } : void 0;
4566
4583
  try {
4567
4584
  const tokenCount = await getTokenCount(translateToOpenAI(anthropicPayload, translationOptions), findEndpointModel(modelId) ?? createFallbackModel(modelId));
4568
4585
  const finalTokenCount = tokenCount.input + tokenCount.output;
4569
- logger$5.debug("provider.count_tokens.success", {
4586
+ logger$6.debug("provider.count_tokens.success", {
4570
4587
  provider,
4571
4588
  model: anthropicPayload.model,
4572
4589
  input_tokens: finalTokenCount
4573
4590
  });
4574
4591
  return c.json({ input_tokens: finalTokenCount });
4575
4592
  } catch (error) {
4576
- logger$5.error("provider.count_tokens.error", {
4593
+ logger$6.error("provider.count_tokens.error", {
4577
4594
  provider,
4578
4595
  error
4579
4596
  });
@@ -4639,6 +4656,7 @@ async function countTokensViaAnthropic(c, payload) {
4639
4656
  async function handleCountTokens(c) {
4640
4657
  const anthropicPayload = await c.req.json();
4641
4658
  anthropicPayload.model = resolveModelAlias(anthropicPayload.model);
4659
+ normalizeSystemMessages(anthropicPayload);
4642
4660
  const providerModelAlias = resolveExistingProviderModelAlias(anthropicPayload.model, getProviderConfigResolver$1(c));
4643
4661
  if (providerModelAlias) {
4644
4662
  anthropicPayload.model = providerModelAlias.model;
@@ -4676,7 +4694,7 @@ async function handleCountTokens(c) {
4676
4694
  //#endregion
4677
4695
  //#region src/services/copilot/create-responses.ts
4678
4696
  const RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS = 6e4;
4679
- const createResponses = async (payload, { vision, initiator, upstreamRequestId, subagentMarker, sessionId, compactType, requestId, fetchImpl, transport = "http" }, account) => {
4697
+ const createResponses = async (payload, { vision, initiator, upstreamRequestId, subagentMarker, sessionId, compactType, requestId, fetchImpl, transport = "http", bridgeId }, account) => {
4680
4698
  const ctx = account ?? accountFromState();
4681
4699
  if (!ctx.copilotToken) throw new Error("Copilot token not found");
4682
4700
  const effectiveInitiator = resolveEffectiveInitiator(initiator, {
@@ -4696,7 +4714,9 @@ const createResponses = async (payload, { vision, initiator, upstreamRequestId,
4696
4714
  const stream = createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
4697
4715
  copilotToken: ctx.copilotToken,
4698
4716
  requestId: requestId ?? upstreamRequestId ?? "missing-request-id",
4699
- subagentMarker
4717
+ sessionId,
4718
+ subagentMarker,
4719
+ bridgeId
4700
4720
  }), copilotBaseUrl(ctx));
4701
4721
  if (payload.stream) return stream;
4702
4722
  return await consumeResponsesWebSocketStream(stream);
@@ -4729,20 +4749,22 @@ const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) =>
4729
4749
  return {
4730
4750
  headers: copilotWebSocketHeaders(preparedHeaders),
4731
4751
  poolKey: buildResponsesWebSocketPoolKey(payload, options),
4732
- payload: buildResponsesWebSocketPayload(payload, initiator)
4752
+ payload: buildResponsesWebSocketPayload(payload, initiator),
4753
+ bridgeId: options.bridgeId
4733
4754
  };
4734
4755
  };
4735
- const buildResponsesWebSocketPoolKey = (payload, { copilotToken, requestId, subagentMarker }) => {
4756
+ const buildResponsesWebSocketPoolKey = (payload, { copilotToken, requestId, sessionId, subagentMarker }) => {
4736
4757
  const tokenFingerprint = copilotToken ? createHash("sha256").update(copilotToken).digest("hex").slice(0, 16) : "missing-token";
4737
4758
  const subagentKey = subagentMarker ? [
4738
4759
  subagentMarker.session_id,
4739
4760
  subagentMarker.agent_id,
4740
4761
  subagentMarker.agent_type
4741
4762
  ].join(":") : "main";
4763
+ const connectionAffinityKey = sessionId ?? requestId;
4742
4764
  return [
4743
4765
  tokenFingerprint,
4744
4766
  payload.model,
4745
- requestId,
4767
+ connectionAffinityKey,
4746
4768
  subagentKey
4747
4769
  ].map(encodePoolKeyPart).join("|");
4748
4770
  };
@@ -4797,6 +4819,7 @@ const getResponsesWebSocketRequestTarget = (request, baseUrl) => {
4797
4819
  const existing = responsesWebSocketPool.get(request.poolKey);
4798
4820
  if (existing && !existing.closed) {
4799
4821
  clearResponsesWebSocketIdleTimer(existing);
4822
+ existing.bridgeId = request.bridgeId;
4800
4823
  return {
4801
4824
  entry: existing,
4802
4825
  pooled: true
@@ -4817,13 +4840,16 @@ const createResponsesWebSocketEntry = (request, baseUrl) => {
4817
4840
  websocketPromise: openResponsesWebSocket({
4818
4841
  headers: request.headers,
4819
4842
  url: buildResponsesWebSocketUrl(baseUrl)
4820
- })
4843
+ }),
4844
+ bridgeId: request.bridgeId
4821
4845
  };
4822
4846
  entry.websocketPromise.then((websocket) => {
4823
4847
  websocket.addEventListener("close", () => {
4848
+ maybeCloseResponsesBridgeForReapedEntry(request.poolKey, entry);
4824
4849
  removeResponsesWebSocketPoolEntry(request.poolKey, entry);
4825
4850
  });
4826
4851
  websocket.addEventListener("error", () => {
4852
+ maybeCloseResponsesBridgeForReapedEntry(request.poolKey, entry);
4827
4853
  removeResponsesWebSocketPoolEntry(request.poolKey, entry);
4828
4854
  });
4829
4855
  }).catch(() => {
@@ -4831,6 +4857,10 @@ const createResponsesWebSocketEntry = (request, baseUrl) => {
4831
4857
  });
4832
4858
  return entry;
4833
4859
  };
4860
+ const maybeCloseResponsesBridgeForReapedEntry = (poolKey, entry) => {
4861
+ if (entry.bridgeId === void 0 || entry.requestCount > 0 || responsesWebSocketPool.get(poolKey) !== entry) return;
4862
+ closeResponsesBridge(entry.bridgeId);
4863
+ };
4834
4864
  const acquireResponsesWebSocketEntry = (poolKey, entry, pooled) => {
4835
4865
  clearResponsesWebSocketIdleTimer(entry);
4836
4866
  incrementResponsesWebSocketActiveRequestCount(poolKey);
@@ -4862,6 +4892,7 @@ const getReadyResponsesWebSocket = async (poolKey, entry, pooled) => {
4862
4892
  const scheduleResponsesWebSocketIdleClose = (poolKey, entry) => {
4863
4893
  clearResponsesWebSocketIdleTimer(entry);
4864
4894
  entry.idleTimer = setTimeout(() => {
4895
+ maybeCloseResponsesBridgeForReapedEntry(poolKey, entry);
4865
4896
  removeResponsesWebSocketPoolEntry(poolKey, entry);
4866
4897
  }, RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS);
4867
4898
  unrefTimer(entry.idleTimer);
@@ -5408,7 +5439,7 @@ const translateResponsesResultToAnthropic = (response, options) => {
5408
5439
  const usage = mapResponsesUsage(response);
5409
5440
  let anthropicContent = fallbackContentBlocks(response.output_text);
5410
5441
  if (contentBlocks.length > 0) anthropicContent = contentBlocks;
5411
- const stopReason = mapResponsesStopReason(response);
5442
+ const stopReason = mapResponsesStopReason(response, options);
5412
5443
  return {
5413
5444
  id: response.id,
5414
5445
  type: "message",
@@ -5422,6 +5453,7 @@ const translateResponsesResultToAnthropic = (response, options) => {
5422
5453
  };
5423
5454
  const mapOutputToAnthropicContent = (output, options) => {
5424
5455
  const contentBlocks = [];
5456
+ if (!output) output = [];
5425
5457
  for (const item of output) switch (item.type) {
5426
5458
  case "reasoning": {
5427
5459
  const thinkingText = extractReasoningText(item);
@@ -5562,9 +5594,10 @@ const fallbackContentBlocks = (outputText) => {
5562
5594
  text: outputText
5563
5595
  }];
5564
5596
  };
5565
- const mapResponsesStopReason = (response) => {
5597
+ const mapResponsesStopReason = (response, options) => {
5566
5598
  const { status, incomplete_details: incompleteDetails } = response;
5567
5599
  if (status === "completed") {
5600
+ if (!response.output || response.output.length === 0) return options?.hasToolCall ? "tool_use" : "end_turn";
5568
5601
  if (response.output.some((item) => item.type === "function_call" || item.type === "tool_search_call")) return "tool_use";
5569
5602
  return "end_turn";
5570
5603
  }
@@ -5992,7 +6025,7 @@ function closeThinkingBlockIfOpen(state, events) {
5992
6025
  }
5993
6026
  }
5994
6027
  //#endregion
5995
- //#region src/services/providers/anthropic-proxy.ts
6028
+ //#region src/services/providers/provider-proxy.ts
5996
6029
  const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
5997
6030
  const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
5998
6031
  const STRIPPED_RESPONSE_HEADERS = [
@@ -6009,8 +6042,8 @@ const STRIPPED_RESPONSE_HEADERS = [
6009
6042
  ];
6010
6043
  function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
6011
6044
  const authHeaders = {};
6012
- if (providerConfig.authType === "authorization") authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
6013
- else authHeaders["x-api-key"] = providerConfig.apiKey;
6045
+ if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
6046
+ else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
6014
6047
  const headers = {
6015
6048
  "content-type": "application/json",
6016
6049
  accept: "application/json",
@@ -6027,10 +6060,10 @@ function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
6027
6060
  }
6028
6061
  return headers;
6029
6062
  }
6030
- function createProviderProxyResponse(upstreamResponse) {
6063
+ function createProviderProxyResponse(upstreamResponse, body) {
6031
6064
  const headers = new Headers(upstreamResponse.headers);
6032
6065
  for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
6033
- return new Response(upstreamResponse.body, {
6066
+ return new Response(body ?? upstreamResponse.body, {
6034
6067
  headers,
6035
6068
  status: upstreamResponse.status,
6036
6069
  statusText: upstreamResponse.statusText
@@ -6050,6 +6083,13 @@ async function forwardProviderChatCompletions(providerConfig, payload, requestHe
6050
6083
  body: JSON.stringify(payload)
6051
6084
  });
6052
6085
  }
6086
+ async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
6087
+ return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
6088
+ method: "POST",
6089
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
6090
+ body: JSON.stringify(payload)
6091
+ });
6092
+ }
6053
6093
  async function forwardProviderModels(providerConfig, requestHeaders, fetchImpl = fetch) {
6054
6094
  return await fetchImpl(`${providerConfig.baseUrl}/v1/models`, {
6055
6095
  method: "GET",
@@ -6058,9 +6098,9 @@ async function forwardProviderModels(providerConfig, requestHeaders, fetchImpl =
6058
6098
  }
6059
6099
  //#endregion
6060
6100
  //#region src/routes/provider/messages/handler.ts
6061
- const logger$4 = createHandlerLogger("provider-messages-handler");
6101
+ const logger$5 = createHandlerLogger("provider-messages-handler");
6062
6102
  const getProviderFetch$1 = (c) => c.get("providerFetch") ?? fetch;
6063
- const resolveProviderConfig$1 = (c, provider) => {
6103
+ const resolveProviderConfig$2 = (c, provider) => {
6064
6104
  return (c.get("providerConfigResolver") ?? getProviderConfig)(provider);
6065
6105
  };
6066
6106
  const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
@@ -6084,7 +6124,7 @@ const writeProviderStreamError = async (stream, message) => {
6084
6124
  })
6085
6125
  });
6086
6126
  } catch (error) {
6087
- logger$4.warn("Failed to write provider stream error event", error);
6127
+ logger$5.warn("Failed to write provider stream error event", error);
6088
6128
  }
6089
6129
  };
6090
6130
  async function handleProviderMessages(c) {
@@ -6096,7 +6136,7 @@ async function handleProviderMessages(c) {
6096
6136
  }
6097
6137
  async function handleProviderMessagesForProvider(c, options) {
6098
6138
  const { instrumentation, payload, provider } = options;
6099
- const providerConfig = resolveProviderConfig$1(c, provider);
6139
+ const providerConfig = resolveProviderConfig$2(c, provider);
6100
6140
  if (!providerConfig) {
6101
6141
  const message = `Provider '${provider}' not found or disabled`;
6102
6142
  instrumentation?.onError?.({
@@ -6113,7 +6153,7 @@ async function handleProviderMessagesForProvider(c, options) {
6113
6153
  try {
6114
6154
  const modelConfig = providerConfig.models?.[payload.model];
6115
6155
  applyModelDefaults(payload, modelConfig);
6116
- debugJson(logger$4, "provider.messages.request", {
6156
+ debugJson(logger$5, "provider.messages.request", {
6117
6157
  payload,
6118
6158
  provider
6119
6159
  });
@@ -6127,7 +6167,7 @@ async function handleProviderMessagesForProvider(c, options) {
6127
6167
  applyMissingExtraBody(payload, { extraBody: modelConfig?.extraBody });
6128
6168
  const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers, getProviderFetch$1(c));
6129
6169
  if (!upstreamResponse.ok) {
6130
- logger$4.error("Failed to create responses", upstreamResponse);
6170
+ logger$5.error("Failed to create responses", upstreamResponse);
6131
6171
  throw new HTTPError("Failed to create responses", upstreamResponse);
6132
6172
  }
6133
6173
  const contentType = upstreamResponse.headers.get("content-type") ?? "";
@@ -6147,7 +6187,7 @@ async function handleProviderMessagesForProvider(c, options) {
6147
6187
  providerConfig
6148
6188
  });
6149
6189
  } catch (error) {
6150
- logger$4.error("provider.messages.error", {
6190
+ logger$5.error("provider.messages.error", {
6151
6191
  provider,
6152
6192
  error
6153
6193
  });
@@ -6184,13 +6224,13 @@ const applyOpenAICompatibleExtraBodyThinkingBudget = (payload, options) => {
6184
6224
  const handleOpenAICompatibleProviderMessages = async (c, options) => {
6185
6225
  const { instrumentation, modelConfig, payload, provider, providerConfig } = options;
6186
6226
  const openAIPayload = createOpenAICompatiblePayload(payload, modelConfig);
6187
- debugJson(logger$4, "provider.messages.openai_compatible.request", {
6227
+ debugJson(logger$5, "provider.messages.openai_compatible.request", {
6188
6228
  payload: openAIPayload,
6189
6229
  provider
6190
6230
  });
6191
6231
  const upstreamResponse = await forwardProviderChatCompletions(providerConfig, openAIPayload, c.req.raw.headers, getProviderFetch$1(c));
6192
6232
  if (!upstreamResponse.ok) {
6193
- logger$4.error("Failed to create openai-compatible responses", upstreamResponse);
6233
+ logger$5.error("Failed to create openai-compatible responses", upstreamResponse);
6194
6234
  throw new HTTPError("Failed to create openai-compatible responses", upstreamResponse);
6195
6235
  }
6196
6236
  const contentType = upstreamResponse.headers.get("content-type") ?? "";
@@ -6274,13 +6314,13 @@ const setContextCacheControl = (part) => {
6274
6314
  part.cache_control = { ...OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL };
6275
6315
  };
6276
6316
  const streamProviderMessages = ({ c, instrumentation, providerConfig, upstreamResponse }) => {
6277
- logger$4.debug("provider.messages.streaming");
6317
+ logger$5.debug("provider.messages.streaming");
6278
6318
  return streamSSE(c, async (stream) => {
6279
6319
  let usage = {};
6280
6320
  try {
6281
6321
  let completed = false;
6282
6322
  for await (const chunk of events(upstreamResponse)) {
6283
- logger$4.debug("provider.messages.raw_stream_event:", chunk.data);
6323
+ logger$5.debug("provider.messages.raw_stream_event:", chunk.data);
6284
6324
  const eventName = chunk.event;
6285
6325
  if (eventName === "ping") {
6286
6326
  await stream.writeSSE({
@@ -6316,14 +6356,14 @@ const streamProviderMessages = ({ c, instrumentation, providerConfig, upstreamRe
6316
6356
  instrumentation?.onComplete?.(usage);
6317
6357
  } catch (error) {
6318
6358
  const details = await extractErrorObservability(error);
6319
- logger$4.warn("provider.messages.streaming.error", error);
6359
+ logger$5.warn("provider.messages.streaming.error", error);
6320
6360
  instrumentation?.onError?.(details);
6321
6361
  await writeProviderStreamError(stream, getUserVisibleErrorMessage(details));
6322
6362
  }
6323
6363
  });
6324
6364
  };
6325
6365
  const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamResponse }) => {
6326
- logger$4.debug("provider.messages.openai_compatible.streaming");
6366
+ logger$5.debug("provider.messages.openai_compatible.streaming");
6327
6367
  return streamSSE(c, async (stream) => {
6328
6368
  let usage = {};
6329
6369
  const streamState = {
@@ -6336,7 +6376,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
6336
6376
  try {
6337
6377
  let completed = false;
6338
6378
  for await (const chunk of events(upstreamResponse)) {
6339
- logger$4.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
6379
+ logger$5.debug("provider.messages.openai_compatible.raw_stream_event:", chunk.data);
6340
6380
  if (chunk.event === "ping") {
6341
6381
  await stream.writeSSE({
6342
6382
  event: "ping",
@@ -6354,7 +6394,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
6354
6394
  const events = translateChunkToAnthropicEvents(parsed, streamState);
6355
6395
  for (const event of events) {
6356
6396
  const eventData = JSON.stringify(event);
6357
- debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
6397
+ debugLazy(logger$5, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
6358
6398
  await stream.writeSSE({
6359
6399
  event: event.type,
6360
6400
  data: eventData
@@ -6364,7 +6404,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
6364
6404
  }
6365
6405
  for (const event of flushPendingAnthropicStreamEvents(streamState)) {
6366
6406
  const eventData = JSON.stringify(event);
6367
- debugLazy(logger$4, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
6407
+ debugLazy(logger$5, () => ["provider.messages.openai_compatible.translated_event:", eventData]);
6368
6408
  await stream.writeSSE({
6369
6409
  event: event.type,
6370
6410
  data: eventData
@@ -6375,7 +6415,7 @@ const streamOpenAICompatibleProviderMessages = ({ c, instrumentation, upstreamRe
6375
6415
  instrumentation?.onComplete?.(usage);
6376
6416
  } catch (error) {
6377
6417
  const details = await extractErrorObservability(error);
6378
- logger$4.warn("provider.messages.openai_compatible.streaming.error", error);
6418
+ logger$5.warn("provider.messages.openai_compatible.streaming.error", error);
6379
6419
  instrumentation?.onError?.(details);
6380
6420
  await writeProviderStreamError(stream, getUserVisibleErrorMessage(details));
6381
6421
  }
@@ -6386,7 +6426,7 @@ const parseOpenAICompatibleStreamChunk = (data) => {
6386
6426
  try {
6387
6427
  parsed = JSON.parse(data);
6388
6428
  } catch (error) {
6389
- logger$4.error("provider.messages.openai_compatible.parse_chunk_error", {
6429
+ logger$5.error("provider.messages.openai_compatible.parse_chunk_error", {
6390
6430
  data,
6391
6431
  error
6392
6432
  });
@@ -6443,7 +6483,7 @@ const parseProviderStreamEvent = (data, providerConfig) => {
6443
6483
  usage: {}
6444
6484
  };
6445
6485
  } catch (error) {
6446
- logger$4.error("provider.messages.streaming.adjust_tokens_error", {
6486
+ logger$5.error("provider.messages.streaming.adjust_tokens_error", {
6447
6487
  error,
6448
6488
  originalData: data
6449
6489
  });
@@ -6453,7 +6493,7 @@ const parseProviderStreamEvent = (data, providerConfig) => {
6453
6493
  const respondProviderMessagesJson = (c, options) => {
6454
6494
  const { body, instrumentation, providerConfig } = options;
6455
6495
  adjustInputTokens(providerConfig, body.usage);
6456
- debugJson(logger$4, "provider.messages.no_stream result:", body);
6496
+ debugJson(logger$5, "provider.messages.no_stream result:", body);
6457
6497
  const response = c.json(body);
6458
6498
  instrumentation?.onComplete?.(normalizeAnthropicUsage(body.usage));
6459
6499
  return response;
@@ -6461,7 +6501,7 @@ const respondProviderMessagesJson = (c, options) => {
6461
6501
  const respondOpenAICompatibleProviderMessagesJson = (c, options) => {
6462
6502
  const { body, instrumentation } = options;
6463
6503
  const anthropicResponse = translateToAnthropic(body);
6464
- debugJson(logger$4, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
6504
+ debugJson(logger$5, "provider.messages.openai_compatible.no_stream result:", anthropicResponse);
6465
6505
  const response = c.json(anthropicResponse);
6466
6506
  instrumentation?.onComplete?.(normalizeOpenAIUsage(body.usage));
6467
6507
  return response;
@@ -6491,7 +6531,7 @@ const mergeAnthropicUsage = (current, next) => ({
6491
6531
  const adjustInputTokens = (providerConfig, usage) => {
6492
6532
  if (!providerConfig.adjustInputTokens || !usage) return;
6493
6533
  usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
6494
- debugJson(logger$4, "provider.messages.adjusted_usage:", usage);
6534
+ debugJson(logger$5, "provider.messages.adjusted_usage:", usage);
6495
6535
  };
6496
6536
  //#endregion
6497
6537
  //#region src/routes/messages/responses-stream-translation.ts
@@ -6528,7 +6568,8 @@ const createResponsesStreamState = (options) => ({
6528
6568
  openBlocks: /* @__PURE__ */ new Set(),
6529
6569
  blockHasDelta: /* @__PURE__ */ new Set(),
6530
6570
  functionCallStateByOutputIndex: /* @__PURE__ */ new Map(),
6531
- toolSearchName: options?.toolSearchName ?? "mcp__tool_search__search"
6571
+ toolSearchName: options?.toolSearchName ?? "mcp__tool_search__search",
6572
+ hasToolCall: false
6532
6573
  });
6533
6574
  const translateResponsesStreamEvent = (rawEvent, state) => {
6534
6575
  switch (rawEvent.type) {
@@ -6776,7 +6817,10 @@ const handleResponseCompleted = (rawEvent, state) => {
6776
6817
  const events = new Array();
6777
6818
  closeAllOpenBlocks(state, events);
6778
6819
  state.responseStatus = response.status;
6779
- const anthropic = translateResponsesResultToAnthropic(response);
6820
+ const anthropic = translateResponsesResultToAnthropic(response, {
6821
+ hasToolCall: state.hasToolCall,
6822
+ toolSearchName: state.toolSearchName
6823
+ });
6780
6824
  events.push({
6781
6825
  type: "message_delta",
6782
6826
  delta: {
@@ -6908,6 +6952,7 @@ const buildErrorEvent = (message) => ({
6908
6952
  const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
6909
6953
  const openFunctionCallBlock = (state, params) => {
6910
6954
  const { outputIndex, toolCallId, name, events } = params;
6955
+ state.hasToolCall = true;
6911
6956
  let functionCallState = state.functionCallStateByOutputIndex.get(outputIndex);
6912
6957
  if (!functionCallState) {
6913
6958
  const blockIndex = state.nextContentBlockIndex;
@@ -6961,8 +7006,9 @@ const stringifyToolSearchArguments = (argumentsValue) => {
6961
7006
  return;
6962
7007
  }
6963
7008
  };
7009
+ const DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO = .9;
6964
7010
  const responsesUtilsDependencies = {
6965
- isResponsesApiContextManagementModel,
7011
+ isResponsesApiContextManagementEnabled,
6966
7012
  isResponsesApiWebSocketEnabled
6967
7013
  };
6968
7014
  const getResponsesRequestOptions = (payload) => {
@@ -6992,18 +7038,92 @@ const isAgentRole = (item) => {
6992
7038
  const hasVisionInput$1 = (payload) => {
6993
7039
  return getPayloadItems(payload).some((item) => containsVisionContent(item));
6994
7040
  };
6995
- const resolveResponsesCompactThreshold = (maxPromptTokens) => {
6996
- if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
6997
- return 5e4;
7041
+ const DATA_URL_PREFIX = "data:";
7042
+ const REDACTED_IMAGE_PLACEHOLDER_DATA_URL = "data:image/png;base64," + [
7043
+ "iVBORw0KGgoAAAANSUhEUgAAAGAAAAAgCAMAAADaHo1mAAADAFBMVEX///8fKTfR1dsAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7044
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7045
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7046
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7047
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7048
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7049
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7050
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7051
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7052
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7053
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
7054
+ "AAAAAAAAAAAAAAAAAAAAAACae8QWAAAAvElEQVR42u1WixKAIAhj/f9Hdz2BXJiVed3pVSYtpgwsGSo3GaRq6wSd4F8EyIJx",
7055
+ "ydSUAMB8il51sHT2fiVQu8czguQwXWAyFvswIJhmoS9gmzYlcFiHj1aAgzcJVgCyguYhAhNZmMhYQZs1EJnnIAqKiuHjSrZT",
7056
+ "ucSQ4s8JkKDDIYr3IuR8vEWgqroKP9b1bYKk2wfgeVmqATQLXdXamsXdEKkz3QXEEeTTuWWImMhW6qci94/+hwSVf99HqVoD",
7057
+ "OAuj2SEAAAAASUVORK5CYII="
7058
+ ].join("");
7059
+ const sanitizeOversizedInputImages = (payload, maxPromptImageSize) => {
7060
+ const limit = typeof maxPromptImageSize === "number" && maxPromptImageSize > 0 ? maxPromptImageSize : void 0;
7061
+ if (limit === void 0 || !Array.isArray(payload.input)) return 0;
7062
+ return sanitizeInputImages(payload.input, (image) => image.decodedBytes > limit);
7063
+ };
7064
+ const sanitizeInputImages = (input, shouldReplace) => {
7065
+ let count = 0;
7066
+ for (const image of collectInputImageDataUrls(input)) {
7067
+ if (!shouldReplace(image)) continue;
7068
+ replaceInputImageWithPlaceholder(image);
7069
+ count += 1;
7070
+ }
7071
+ return count;
7072
+ };
7073
+ const collectInputImageDataUrls = (input, images = []) => {
7074
+ for (const item of input) collectInputItemImageDataUrls(item, images);
7075
+ return images;
7076
+ };
7077
+ const collectInputItemImageDataUrls = (item, images) => {
7078
+ if (isResponseInputMessage(item)) collectContentImageDataUrls(item.content, images);
7079
+ else if (isResponseFunctionCallOutputItem(item)) collectContentImageDataUrls(item.output, images);
7080
+ };
7081
+ const collectContentImageDataUrls = (content, images) => {
7082
+ if (!Array.isArray(content)) return;
7083
+ for (const block of content) {
7084
+ const image = getInputImageDataUrl(block);
7085
+ if (image) images.push(image);
7086
+ }
7087
+ };
7088
+ const getInputImageDataUrl = (content) => {
7089
+ if (!isResponseInputImage(content) || typeof content.image_url !== "string") return null;
7090
+ const imageUrl = content.image_url;
7091
+ if (!imageUrl.startsWith(DATA_URL_PREFIX)) return null;
7092
+ return {
7093
+ decodedBytes: estimateDataUrlByteLength(imageUrl),
7094
+ record: content
7095
+ };
7096
+ };
7097
+ const estimateDataUrlByteLength = (value) => {
7098
+ return Math.max(0, Math.floor(value.length * 3 / 4));
7099
+ };
7100
+ const replaceInputImageWithPlaceholder = (image) => {
7101
+ image.record.type = "input_image";
7102
+ image.record.image_url = REDACTED_IMAGE_PLACEHOLDER_DATA_URL;
7103
+ image.record.detail = "low";
7104
+ delete image.record.file_id;
7105
+ };
7106
+ const isResponseInputMessage = (item) => {
7107
+ return typeof item === "object" && item !== null && "role" in item && typeof item.role === "string";
7108
+ };
7109
+ const isResponseFunctionCallOutputItem = (item) => {
7110
+ return typeof item === "object" && item !== null && "type" in item && item.type === "function_call_output";
7111
+ };
7112
+ const isResponseInputImage = (content) => {
7113
+ return typeof content === "object" && content !== null && "type" in content && content.type === "input_image";
7114
+ };
7115
+ const resolveResponsesCompactThreshold = (maxPromptTokens, compactThresholdRatio = DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO) => {
7116
+ if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * compactThresholdRatio);
7117
+ return 2e5 * compactThresholdRatio;
6998
7118
  };
6999
7119
  const createCompactionContextManagement = (compactThreshold) => [{
7000
7120
  type: "compaction",
7001
7121
  compact_threshold: compactThreshold
7002
7122
  }];
7003
- const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
7123
+ const applyResponsesApiContextManagement = (payload, maxPromptTokens, compactThresholdRatio = DEFAULT_RESPONSES_COMPACT_THRESHOLD_RATIO) => {
7004
7124
  if (payload.context_management !== void 0) return;
7005
- if (!responsesUtilsDependencies.isResponsesApiContextManagementModel(payload.model)) return;
7006
- payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
7125
+ if (!responsesUtilsDependencies.isResponsesApiContextManagementEnabled()) return;
7126
+ payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens, compactThresholdRatio));
7007
7127
  };
7008
7128
  const compactInputByLatestCompaction = (payload) => {
7009
7129
  if (!Array.isArray(payload.input) || payload.input.length === 0) return;
@@ -7106,7 +7226,7 @@ const buildMessagesHeaders = ({ ctx, enableVision, initiator, options, payload }
7106
7226
  };
7107
7227
  prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
7108
7228
  prepareForCompact(headers, options?.compactType);
7109
- if (shouldUseMessageProxyHeaders(payload)) prepareMessageProxyHeaders(headers);
7229
+ if (shouldUseMessageProxyHeaders(payload) && payload.model !== "claude-opus-4.8") prepareMessageProxyHeaders(headers);
7110
7230
  const anthropicBeta = buildAnthropicBetaHeader(options?.anthropicBetaHeader, payload.thinking, payload.model);
7111
7231
  if (anthropicBeta) headers["anthropic-beta"] = anthropicBeta;
7112
7232
  return headers;
@@ -7245,7 +7365,7 @@ const extractBalancedJson = (text) => {
7245
7365
  };
7246
7366
  //#endregion
7247
7367
  //#region src/routes/messages/handler.ts
7248
- const logger$3 = createHandlerLogger("messages-handler");
7368
+ const logger$4 = createHandlerLogger("messages-handler");
7249
7369
  const CHAT_COMPLETIONS_ENDPOINT = "/chat/completions";
7250
7370
  const RESPONSES_ENDPOINT$1 = "/responses";
7251
7371
  const MESSAGES_ENDPOINT = "/v1/messages";
@@ -7291,7 +7411,7 @@ async function handleProviderAliasCompletion(c, options) {
7291
7411
  let requestRecorded = false;
7292
7412
  const insertProviderAliasLog = (record) => {
7293
7413
  if (requestRecorded) {
7294
- logger$3.warn("provider alias request already recorded", { requestId });
7414
+ logger$4.warn("provider alias request already recorded", { requestId });
7295
7415
  return;
7296
7416
  }
7297
7417
  requestRecorded = true;
@@ -7364,15 +7484,16 @@ async function handleCompletion(c) {
7364
7484
  const path = new URL(c.req.url, "http://local").pathname;
7365
7485
  const { ip: clientIp, source: clientIpSource } = getClientIpInfo(c);
7366
7486
  const userAgent = c.req.header("user-agent") ?? void 0;
7487
+ normalizeSystemMessages(anthropicPayload);
7367
7488
  sanitizeIdeTools(anthropicPayload);
7368
- debugJson(logger$3, "Anthropic request payload:", anthropicPayload);
7489
+ debugJson(logger$4, "Anthropic request payload:", anthropicPayload);
7369
7490
  const markerInspection = inspectSubagentMarkerFromFirstUser(anthropicPayload);
7370
7491
  const subagentMarker = markerInspection.kind === "valid" ? markerInspection.marker : null;
7371
7492
  const isSubagentRequest = subagentMarker !== null;
7372
7493
  const invalidSubagentMarkerSelectionReason = markerInspection.kind === "invalid" ? "subagent_marker_invalid_fallback" : void 0;
7373
- if (subagentMarker) debugJson(logger$3, "Detected Subagent marker:", subagentMarker);
7494
+ if (subagentMarker) debugJson(logger$4, "Detected Subagent marker:", subagentMarker);
7374
7495
  const sessionId = getRootSessionId(anthropicPayload, c);
7375
- logger$3.debug("Extracted session ID:", sessionId);
7496
+ logger$4.debug("Extracted session ID:", sessionId);
7376
7497
  const ownershipLookupSessionId = markerInspection.kind === "valid" ? normalizeStableSessionId(markerInspection.marker.session_id) : void 0;
7377
7498
  const ownershipWriteSessionId = markerInspection.kind === "none" ? sessionId : void 0;
7378
7499
  const anthropicBeta = c.req.header("anthropic-beta");
@@ -7380,7 +7501,7 @@ async function handleCompletion(c) {
7380
7501
  const isCompact = compactType !== 0;
7381
7502
  const originalRequestModel = anthropicPayload.model;
7382
7503
  if (anthropicBeta && isWarmupProbeRequest(anthropicPayload)) anthropicPayload.model = getSmallModel();
7383
- if (compactType !== 0) logger$3.debug("Compact request type:", compactType);
7504
+ if (compactType !== 0) logger$4.debug("Compact request type:", compactType);
7384
7505
  const lastMessageCacheControl = getLastMessageContentCacheControl(anthropicPayload.messages.at(-1));
7385
7506
  if (compactType === 1 && shouldCompactUseSmallModel()) anthropicPayload.model = getSmallModel();
7386
7507
  if (compactType === 0) {
@@ -7389,7 +7510,7 @@ async function handleCompletion(c) {
7389
7510
  }
7390
7511
  applyLastMessageCacheControl(anthropicPayload, lastMessageCacheControl);
7391
7512
  const upstreamRequestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
7392
- logger$3.debug("Generated request ID:", upstreamRequestId);
7513
+ logger$4.debug("Generated request ID:", upstreamRequestId);
7393
7514
  const clientModel = anthropicPayload.model;
7394
7515
  anthropicPayload.model = resolveModelAlias(anthropicPayload.model);
7395
7516
  const routingModel = anthropicPayload.model;
@@ -7546,7 +7667,7 @@ async function handleCompletion(c) {
7546
7667
  }
7547
7668
  const handleWithChatCompletions = async (params) => {
7548
7669
  const { c, openAIPayload, subagentMarker, sessionId, selectedModel, instr, compactType } = params;
7549
- debugJson(logger$3, "Translated OpenAI request payload:", openAIPayload);
7670
+ debugJson(logger$4, "Translated OpenAI request payload:", openAIPayload);
7550
7671
  const ctx = toAccountContext(instr.account);
7551
7672
  const effectiveInitiator = resolveEffectiveInitiator(getChatInitiator(openAIPayload.messages), {
7552
7673
  isCompact: compactType !== 0,
@@ -7577,9 +7698,9 @@ const handleWithChatCompletions = async (params) => {
7577
7698
  response,
7578
7699
  instr
7579
7700
  });
7580
- logger$3.debug("Streaming response from Copilot");
7701
+ logger$4.debug("Streaming response from Copilot");
7581
7702
  const fallbackEnabled = isMessageStartInputTokensFallbackEnabled();
7582
- const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$3) : void 0;
7703
+ const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$4) : void 0;
7583
7704
  const historicalUsage = fallbackEnabled && instr.promptCacheKey && instr.safetyIdentifier ? instr.store.getLastCompletedUsageBySession({
7584
7705
  promptCacheKey: instr.promptCacheKey,
7585
7706
  safetyIdentifier: instr.safetyIdentifier,
@@ -7601,7 +7722,7 @@ const handleWithResponsesApi = async (params) => {
7601
7722
  });
7602
7723
  applyResponsesApiContextManagement(responsesPayload, selectedModel.capabilities.limits.max_prompt_tokens);
7603
7724
  compactInputByLatestCompaction(responsesPayload);
7604
- debugJson(logger$3, "Translated Responses payload:", responsesPayload);
7725
+ debugJson(logger$4, "Translated Responses payload:", responsesPayload);
7605
7726
  const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
7606
7727
  const transport = getResponsesTransportForModel(selectedModel, { compactType }) ?? "http";
7607
7728
  const effectiveInitiator = resolveEffectiveInitiator(initiator, {
@@ -7632,9 +7753,9 @@ const handleWithResponsesApi = async (params) => {
7632
7753
  });
7633
7754
  }
7634
7755
  if (responsesPayload.stream && isAsyncIterable(response)) {
7635
- logger$3.debug("Streaming response from Copilot (Responses API)");
7756
+ logger$4.debug("Streaming response from Copilot (Responses API)");
7636
7757
  const fallbackEnabled = isMessageStartInputTokensFallbackEnabled();
7637
- const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$3) : void 0;
7758
+ const estimatedInputTokens = fallbackEnabled ? await estimateInputTokens(openAIPayload, selectedModel, logger$4) : void 0;
7638
7759
  const historicalUsage = fallbackEnabled && instr.promptCacheKey && instr.safetyIdentifier ? instr.store.getLastCompletedUsageBySession({
7639
7760
  promptCacheKey: instr.promptCacheKey,
7640
7761
  safetyIdentifier: instr.safetyIdentifier,
@@ -7732,9 +7853,9 @@ async function handleChatCompletionsNonStreaming(params) {
7732
7853
  let upstreamErrorMessageRaw;
7733
7854
  const finishedAtMs = Date.now();
7734
7855
  try {
7735
- logger$3.debug("Non-streaming response from Copilot:", JSON.stringify(response));
7856
+ logger$4.debug("Non-streaming response from Copilot:", JSON.stringify(response));
7736
7857
  const anthropicResponse = translateToAnthropic(response);
7737
- debugJson(logger$3, "Translated Anthropic response:", anthropicResponse);
7858
+ debugJson(logger$4, "Translated Anthropic response:", anthropicResponse);
7738
7859
  return c.json(anthropicResponse);
7739
7860
  } catch (error) {
7740
7861
  const details = await extractErrorObservability(error);
@@ -7785,7 +7906,7 @@ async function streamChatCompletionsAndLog(params) {
7785
7906
  try {
7786
7907
  for await (const rawEvent of response) {
7787
7908
  if (ttfbMs === void 0) ttfbMs = Date.now() - instr.startedAtMs;
7788
- logger$3.debug("Copilot raw stream event:", JSON.stringify(rawEvent));
7909
+ logger$4.debug("Copilot raw stream event:", JSON.stringify(rawEvent));
7789
7910
  const { data: rawData } = rawEvent;
7790
7911
  const data = typeof rawData === "string" ? rawData : await rawData;
7791
7912
  if (data === "[DONE]") break;
@@ -7794,7 +7915,7 @@ async function streamChatCompletionsAndLog(params) {
7794
7915
  if (chunk.usage) lastUsage = normalizeChatCompletionsUsage(chunk.usage);
7795
7916
  const events = translateChunkToAnthropicEvents(chunk, streamState);
7796
7917
  for (const event of events) {
7797
- logger$3.debug("Translated Anthropic event:", JSON.stringify(event));
7918
+ logger$4.debug("Translated Anthropic event:", JSON.stringify(event));
7798
7919
  await stream.writeSSE({
7799
7920
  event: event.type,
7800
7921
  data: JSON.stringify(event)
@@ -7807,7 +7928,7 @@ async function streamChatCompletionsAndLog(params) {
7807
7928
  errorStatus = details.errorStatus;
7808
7929
  errorMessage = details.errorMessage;
7809
7930
  upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
7810
- logger$3.warn("Streaming error:", error);
7931
+ logger$4.warn("Streaming error:", error);
7811
7932
  if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
7812
7933
  await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
7813
7934
  } finally {
@@ -7868,9 +7989,9 @@ async function handleResponsesNonStreaming(params) {
7868
7989
  usage = extractResponsesUsageFromResult(result);
7869
7990
  const responseOwnerKeys = extractResponsesResultOwnerKeys(result);
7870
7991
  instr.responsesItemOwnerRecordedKeys = responseOwnerKeys;
7871
- logger$3.debug("Non-streaming Responses result:", JSON.stringify(result).slice(-400));
7992
+ logger$4.debug("Non-streaming Responses result:", JSON.stringify(result).slice(-400));
7872
7993
  const anthropicResponse = translateResponsesResultToAnthropic(result);
7873
- debugJson(logger$3, "Translated Anthropic response:", anthropicResponse);
7994
+ debugJson(logger$4, "Translated Anthropic response:", anthropicResponse);
7874
7995
  const response = c.json(anthropicResponse);
7875
7996
  if (result.status === "completed") accountsManager.recordResponsesItemOwnership(responseOwnerKeys, instr.account.id);
7876
7997
  return response;
@@ -7904,7 +8025,7 @@ async function handleResponsesNonStreaming(params) {
7904
8025
  async function ensureResponsesStreamCompleted(params) {
7905
8026
  const { stream, streamState, setStreamError } = params;
7906
8027
  if (streamState.messageCompleted) return;
7907
- logger$3.warn("Responses stream ended without completion; sending error event");
8028
+ logger$4.warn("Responses stream ended without completion; sending error event");
7908
8029
  const msg = "Responses stream ended without completion";
7909
8030
  const errorEvent = buildErrorEvent(msg);
7910
8031
  setStreamError("StreamIncomplete", msg);
@@ -7921,7 +8042,7 @@ async function writeAnthropicStreamError(stream, message) {
7921
8042
  data: JSON.stringify(errorEvent)
7922
8043
  });
7923
8044
  } catch (streamError) {
7924
- logger$3.warn("Failed to write Anthropic stream error event:", streamError);
8045
+ logger$4.warn("Failed to write Anthropic stream error event:", streamError);
7925
8046
  }
7926
8047
  }
7927
8048
  function collectResponsesStreamOwnerKeys(event, responseOwnerKeys) {
@@ -7964,7 +8085,7 @@ function getResponsesStreamEventError(event) {
7964
8085
  async function writeTranslatedAnthropicStreamEvents(stream, events) {
7965
8086
  for (const event of events) {
7966
8087
  const eventData = JSON.stringify(event);
7967
- logger$3.debug("Translated Anthropic event:", eventData);
8088
+ logger$4.debug("Translated Anthropic event:", eventData);
7968
8089
  await stream.writeSSE({
7969
8090
  event: event.type,
7970
8091
  data: eventData
@@ -7993,7 +8114,7 @@ async function streamResponsesAndLog$1(params) {
7993
8114
  }
7994
8115
  const data = chunk.data;
7995
8116
  if (!data) continue;
7996
- logger$3.debug("Responses raw stream event:", data);
8117
+ logger$4.debug("Responses raw stream event:", data);
7997
8118
  const parsed = JSON.parse(data);
7998
8119
  const streamEventError = getResponsesStreamEventError(parsed);
7999
8120
  if (streamEventError) {
@@ -8007,7 +8128,7 @@ async function streamResponsesAndLog$1(params) {
8007
8128
  if (u.usageJson) lastUsage = u;
8008
8129
  await writeTranslatedAnthropicStreamEvents(stream, translateResponsesStreamEvent(parsed, streamState));
8009
8130
  if (streamState.messageCompleted) {
8010
- logger$3.debug("Message completed, ending stream");
8131
+ logger$4.debug("Message completed, ending stream");
8011
8132
  break;
8012
8133
  }
8013
8134
  }
@@ -8026,7 +8147,7 @@ async function streamResponsesAndLog$1(params) {
8026
8147
  errorStatus = details.errorStatus;
8027
8148
  errorMessage = details.errorMessage;
8028
8149
  upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
8029
- logger$3.warn("Streaming error:", error);
8150
+ logger$4.warn("Streaming error:", error);
8030
8151
  invalidateAffinityOnOwnershipMismatch(details.ownershipMismatch, instr);
8031
8152
  if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
8032
8153
  await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
@@ -8081,7 +8202,7 @@ async function handleMessagesNonStreaming(params) {
8081
8202
  let upstreamErrorMessageRaw;
8082
8203
  const finishedAtMs = Date.now();
8083
8204
  try {
8084
- logger$3.debug("Non-streaming Messages result:", JSON.stringify(response).slice(-400));
8205
+ logger$4.debug("Non-streaming Messages result:", JSON.stringify(response).slice(-400));
8085
8206
  return c.json(response);
8086
8207
  } catch (error) {
8087
8208
  const details = await extractErrorObservability(error);
@@ -8118,7 +8239,7 @@ const parseMessagesStreamUsage = (data) => {
8118
8239
  if (parsed.type !== "message_delta" || !parsed.usage) return null;
8119
8240
  return normalizeMessagesUsage(parsed.usage);
8120
8241
  } catch (error) {
8121
- logger$3.warn("Failed to parse messages stream event", error);
8242
+ logger$4.warn("Failed to parse messages stream event", error);
8122
8243
  throw new Error("Failed to parse messages stream event", { cause: error });
8123
8244
  }
8124
8245
  };
@@ -8136,7 +8257,7 @@ async function streamMessagesAndLog(params) {
8136
8257
  const eventNameRaw = rawEvent.event;
8137
8258
  const eventName = typeof eventNameRaw === "string" && eventNameRaw.length > 0 ? eventNameRaw : "message";
8138
8259
  const data = rawEvent.data ?? "";
8139
- logger$3.debug("Messages raw stream event:", data);
8260
+ logger$4.debug("Messages raw stream event:", data);
8140
8261
  const usage = parseMessagesStreamUsage(data);
8141
8262
  if (usage) lastUsage = usage;
8142
8263
  await stream.writeSSE({
@@ -8150,7 +8271,7 @@ async function streamMessagesAndLog(params) {
8150
8271
  errorStatus = details.errorStatus;
8151
8272
  errorMessage = details.errorMessage;
8152
8273
  upstreamErrorMessageRaw = details.upstreamErrorMessageRaw;
8153
- logger$3.warn("Streaming error:", error);
8274
+ logger$4.warn("Streaming error:", error);
8154
8275
  if (shouldMarkAccountFailed(details)) accountsManager.markAccountFailed(instr.account.id, "Unauthorized (401)");
8155
8276
  await writeAnthropicStreamError(stream, getUserVisibleErrorMessage(details));
8156
8277
  } finally {
@@ -8176,7 +8297,7 @@ async function streamMessagesAndLog(params) {
8176
8297
  const handleWithMessagesApi = async (params) => {
8177
8298
  const { c, anthropicPayload, anthropicBetaHeader, subagentMarker, sessionId, instr, selectedModel, compactType } = params;
8178
8299
  prepareMessagesApiPayload(anthropicPayload, selectedModel);
8179
- debugJson(logger$3, "Translated Messages payload:", anthropicPayload);
8300
+ debugJson(logger$4, "Translated Messages payload:", anthropicPayload);
8180
8301
  const ctx = toAccountContext(instr.account);
8181
8302
  const effectiveInitiator = resolveEffectiveInitiator(getMessagesInitiator(anthropicPayload), {
8182
8303
  isCompact: compactType !== 0,
@@ -8204,7 +8325,7 @@ const handleWithMessagesApi = async (params) => {
8204
8325
  });
8205
8326
  }
8206
8327
  if (isAsyncIterable(response)) {
8207
- logger$3.debug("Streaming response from Copilot (Messages API)");
8328
+ logger$4.debug("Streaming response from Copilot (Messages API)");
8208
8329
  return streamSSE(c, (stream) => streamMessagesAndLog({
8209
8330
  stream,
8210
8331
  response,
@@ -8292,29 +8413,113 @@ providerMessageRoutes.post("/count_tokens", async (c) => {
8292
8413
  }
8293
8414
  });
8294
8415
  //#endregion
8416
+ //#region src/services/codex/get-models.ts
8417
+ const CODEX_MODELS = [
8418
+ {
8419
+ contextWindow: 1e5,
8420
+ id: "gpt-5.3-codex-spark",
8421
+ input: ["text"],
8422
+ maxTokens: 32e3,
8423
+ name: "GPT-5.3 Codex Spark"
8424
+ },
8425
+ {
8426
+ contextWindow: 4e5,
8427
+ id: "gpt-5.4",
8428
+ input: ["text", "image"],
8429
+ maxTokens: 128e3,
8430
+ name: "GPT-5.4"
8431
+ },
8432
+ {
8433
+ contextWindow: 4e5,
8434
+ id: "gpt-5.4-mini",
8435
+ input: ["text", "image"],
8436
+ maxTokens: 128e3,
8437
+ name: "GPT-5.4 mini"
8438
+ },
8439
+ {
8440
+ contextWindow: 272e3,
8441
+ id: "gpt-5.5",
8442
+ input: ["text", "image"],
8443
+ maxTokens: 128e3,
8444
+ name: "GPT-5.5"
8445
+ }
8446
+ ];
8447
+ function normalizeCodexModel(model) {
8448
+ const supportsVision = model.input.includes("image");
8449
+ return {
8450
+ capabilities: {
8451
+ family: "gpt",
8452
+ limits: {
8453
+ max_context_window_tokens: model.contextWindow,
8454
+ max_output_tokens: model.maxTokens,
8455
+ max_prompt_tokens: model.contextWindow
8456
+ },
8457
+ object: "model_capabilities",
8458
+ supports: {
8459
+ adaptive_thinking: true,
8460
+ parallel_tool_calls: true,
8461
+ reasoning_effort: [
8462
+ "minimal",
8463
+ "low",
8464
+ "medium",
8465
+ "high",
8466
+ "xhigh"
8467
+ ],
8468
+ streaming: true,
8469
+ tool_calls: true,
8470
+ vision: supportsVision
8471
+ },
8472
+ tokenizer: "o200k_base",
8473
+ type: "chat"
8474
+ },
8475
+ id: model.id,
8476
+ model_picker_enabled: true,
8477
+ name: model.name,
8478
+ object: "model",
8479
+ preview: false,
8480
+ supported_endpoints: ["/v1/messages", "/v1/responses"],
8481
+ vendor: "openai",
8482
+ version: "chatgpt-codex"
8483
+ };
8484
+ }
8485
+ function getModels() {
8486
+ return {
8487
+ object: "list",
8488
+ data: CODEX_MODELS.map((model) => normalizeCodexModel(model))
8489
+ };
8490
+ }
8491
+ //#endregion
8295
8492
  //#region src/routes/provider/models/route.ts
8296
- const logger$2 = createHandlerLogger("provider-models-handler");
8493
+ const logger$3 = createHandlerLogger("provider-models-handler");
8297
8494
  const getProviderFetch = (c) => c.get("providerFetch") ?? fetch;
8298
- const resolveProviderConfig = (c, provider) => {
8495
+ const resolveProviderConfig$1 = (c, provider) => {
8299
8496
  return (c.get("providerConfigResolver") ?? getProviderConfig)(provider);
8300
8497
  };
8301
8498
  const providerModelRoutes = new Hono();
8302
8499
  providerModelRoutes.get("/", async (c) => {
8303
8500
  const provider = c.req.param("provider") ?? "";
8304
8501
  try {
8305
- const providerConfig = resolveProviderConfig(c, provider);
8502
+ const providerConfig = resolveProviderConfig$1(c, provider);
8306
8503
  if (!providerConfig) return c.json({ error: {
8307
8504
  message: `Provider '${provider}' not found or disabled`,
8308
8505
  type: "invalid_request_error"
8309
8506
  } }, 404);
8507
+ if (providerConfig.name === "codex") {
8508
+ const models = getModels();
8509
+ return c.json({
8510
+ object: "list",
8511
+ data: models.data,
8512
+ has_more: false
8513
+ });
8514
+ }
8310
8515
  const upstreamResponse = await forwardProviderModels(providerConfig, c.req.raw.headers, getProviderFetch(c));
8311
- logger$2.debug("provider.models.response", {
8516
+ logger$3.debug("provider.models.response", {
8312
8517
  provider,
8313
8518
  statusCode: upstreamResponse.status
8314
8519
  });
8315
8520
  return createProviderProxyResponse(upstreamResponse);
8316
8521
  } catch (error) {
8317
- logger$2.error("provider.models.error", {
8522
+ logger$3.error("provider.models.error", {
8318
8523
  provider,
8319
8524
  error
8320
8525
  });
@@ -8322,6 +8527,166 @@ providerModelRoutes.get("/", async (c) => {
8322
8527
  }
8323
8528
  });
8324
8529
  //#endregion
8530
+ //#region src/lib/codex-rate-limit.ts
8531
+ const codexRateLimitScopes = ["primary", "secondary"];
8532
+ const formatCodexRateLimitResetAt = (resetAt) => {
8533
+ const date = /* @__PURE__ */ new Date(resetAt * 1e3);
8534
+ return Number.isNaN(date.getTime()) ? String(resetAt) : date.toLocaleString();
8535
+ };
8536
+ const logCodexRateLimitsEvent = (event) => {
8537
+ if (!event || typeof event !== "object") return;
8538
+ const eventRecord = event;
8539
+ if (eventRecord.type !== "codex.rate_limits") return;
8540
+ const rateLimits = eventRecord.rate_limits;
8541
+ if (!rateLimits || typeof rateLimits !== "object") return;
8542
+ const planType = typeof eventRecord.plan_type === "string" ? eventRecord.plan_type : null;
8543
+ const rateLimitRecord = rateLimits;
8544
+ const allowed = typeof rateLimitRecord.allowed === "boolean" ? rateLimitRecord.allowed : null;
8545
+ const limitReached = typeof rateLimitRecord.limit_reached === "boolean" ? rateLimitRecord.limit_reached : null;
8546
+ for (const scope of codexRateLimitScopes) {
8547
+ const window = rateLimitRecord[scope];
8548
+ if (!isCodexRateLimitWindow(window)) continue;
8549
+ const summary = [];
8550
+ if (allowed !== null) summary.push(`allowed=${allowed}`);
8551
+ if (limitReached !== null) summary.push(`limit_reached=${limitReached}`);
8552
+ summary.push(`used=${window.used_percent}%`, `reset_at=${formatCodexRateLimitResetAt(window.reset_at)}`);
8553
+ const label = planType ? `Codex ${scope} rate limit (${planType})` : `Codex ${scope} rate limit`;
8554
+ consola.log(`${label}: ${summary.join(", ")}`);
8555
+ }
8556
+ };
8557
+ const isCodexRateLimitWindow = (value) => {
8558
+ if (!value || typeof value !== "object") return false;
8559
+ const record = value;
8560
+ return typeof record.reset_after_seconds === "number" && typeof record.reset_at === "number" && typeof record.used_percent === "number" && typeof record.window_minutes === "number";
8561
+ };
8562
+ //#endregion
8563
+ //#region src/lib/provider-resolver.ts
8564
+ function isMissingCodexCredentialsError(error) {
8565
+ return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
8566
+ }
8567
+ async function resolveProviderConfig(providerName) {
8568
+ const normalizedProviderName = providerName.trim();
8569
+ if (!normalizedProviderName) return null;
8570
+ if (normalizedProviderName === "codex") {
8571
+ if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
8572
+ try {
8573
+ await setupCodexToken();
8574
+ } catch (error) {
8575
+ if (isMissingCodexCredentialsError(error)) return null;
8576
+ throw error;
8577
+ }
8578
+ const providerConfig = getProviderConfig(normalizedProviderName);
8579
+ if (!providerConfig) return null;
8580
+ return {
8581
+ ...providerConfig,
8582
+ apiKey: state.codexAccessToken ?? providerConfig.apiKey
8583
+ };
8584
+ }
8585
+ return getProviderConfig(normalizedProviderName);
8586
+ }
8587
+ //#endregion
8588
+ //#region src/routes/provider/responses/handler.ts
8589
+ const logger$2 = createHandlerLogger("provider-responses-handler");
8590
+ async function handleProviderResponsesForProvider(c, options) {
8591
+ const { payload, provider } = options;
8592
+ debugJson(logger$2, "Responses request payload:", {
8593
+ payload,
8594
+ provider
8595
+ });
8596
+ const providerConfig = await resolveProviderConfig(provider);
8597
+ if (providerConfig?.type !== "openai-responses") return c.json({ error: {
8598
+ message: `Provider '${provider}' does not support the /v1/responses endpoint`,
8599
+ type: "invalid_request_error"
8600
+ } }, 400);
8601
+ applyResponsesApiContextManagement(payload, (providerConfig.name === "codex" ? getModels().data.find((model) => model.id === payload.model) : void 0)?.capabilities.limits.max_prompt_tokens ?? 0, .8);
8602
+ const contextManagement = payload.context_management;
8603
+ debugJson(logger$2, "Translated Responses request payload:", {
8604
+ contextManagement,
8605
+ provider
8606
+ });
8607
+ compactInputByLatestCompaction(payload);
8608
+ if (providerConfig.name === "codex") {
8609
+ const upstreamResponse = await forwardCodexResponses(payload, c.req.raw.headers, providerConfig.baseUrl);
8610
+ const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
8611
+ if (payload.stream && isResponsesStream(upstreamResponse)) return streamProviderResponses(c, upstreamResponse, {
8612
+ normalizeCodex: true,
8613
+ provider,
8614
+ recordUsage
8615
+ });
8616
+ const responseBody = upstreamResponse;
8617
+ recordUsage(normalizeResponsesUsage(responseBody.usage));
8618
+ return c.json(responseBody);
8619
+ }
8620
+ const upstreamResponse = await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
8621
+ if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${provider} responses`, upstreamResponse);
8622
+ const recordUsage = createProviderResponsesUsageRecorder(payload, provider);
8623
+ if (payload.stream) return streamProviderResponses(c, getResponsesEvents(upstreamResponse), {
8624
+ normalizeCodex: false,
8625
+ provider,
8626
+ recordUsage
8627
+ });
8628
+ recordUsage(normalizeResponsesUsage((await upstreamResponse.clone().json()).usage));
8629
+ return createProviderProxyResponse(upstreamResponse);
8630
+ }
8631
+ const createProviderResponsesUsageRecorder = (_payload, _provider) => {
8632
+ return (_usage) => {};
8633
+ };
8634
+ const streamProviderResponses = (c, upstreamResponse, options) => {
8635
+ return streamSSE(c, async (stream) => {
8636
+ let usage = {};
8637
+ try {
8638
+ for await (const chunk of upstreamResponse) {
8639
+ debugJson(logger$2, "Responses stream chunk:", chunk);
8640
+ let responseChunk = chunk;
8641
+ let event = null;
8642
+ if (chunk.data && chunk.data !== "[DONE]") {
8643
+ event = parseProviderResponsesStreamEvent(chunk.data, {
8644
+ normalizeCodex: options.normalizeCodex,
8645
+ provider: options.provider
8646
+ });
8647
+ if (event && options.normalizeCodex) responseChunk = {
8648
+ ...chunk,
8649
+ data: JSON.stringify(event),
8650
+ event: event.type
8651
+ };
8652
+ }
8653
+ if (event) {
8654
+ const nextUsage = getResponsesStreamEventUsage(event);
8655
+ if (nextUsage) usage = nextUsage;
8656
+ }
8657
+ await stream.writeSSE({
8658
+ data: responseChunk.data ?? "",
8659
+ event: responseChunk.event
8660
+ });
8661
+ }
8662
+ } finally {
8663
+ options.recordUsage(usage);
8664
+ }
8665
+ });
8666
+ };
8667
+ const parseProviderResponsesStreamEvent = (data, options) => {
8668
+ try {
8669
+ const parsed = JSON.parse(data);
8670
+ if (options.normalizeCodex) logCodexRateLimitsEvent(parsed);
8671
+ return parsed;
8672
+ } catch (error) {
8673
+ logger$2.error("provider.responses.parse_chunk_error", {
8674
+ provider: options.provider,
8675
+ data,
8676
+ error
8677
+ });
8678
+ return null;
8679
+ }
8680
+ };
8681
+ const getResponsesStreamEventUsage = (event) => {
8682
+ if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return normalizeResponsesUsage(event.response.usage);
8683
+ return null;
8684
+ };
8685
+ const getResponsesEvents = (response) => events(response);
8686
+ const isResponsesStream = (value) => {
8687
+ return Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
8688
+ };
8689
+ //#endregion
8325
8690
  //#region src/routes/responses/stream-id-sync.ts
8326
8691
  const createStreamIdTracker = () => ({ outputItems: /* @__PURE__ */ new Map() });
8327
8692
  const fixStreamIds = (data, event, tracker) => {
@@ -8362,16 +8727,31 @@ const handleItemId = (parsed, tracker) => {
8362
8727
  const logger$1 = createHandlerLogger("responses-handler");
8363
8728
  const RESPONSES_ENDPOINT = "/responses";
8364
8729
  const handleResponses = async (c) => {
8730
+ const payload = await c.req.json();
8731
+ debugJson(logger$1, "Responses request payload:", payload);
8732
+ const requestedModel = payload.model;
8733
+ payload.model = resolveMappedModel(payload.model);
8734
+ if (payload.model !== requestedModel) logger$1.debug(`Resolved model mapping: ${requestedModel} -> ${payload.model}`);
8735
+ const providerModelAlias = parseProviderModelAlias(payload.model);
8736
+ if (providerModelAlias) {
8737
+ payload.model = providerModelAlias.model;
8738
+ return await handleProviderResponsesForProvider(c, {
8739
+ payload,
8740
+ provider: providerModelAlias.provider
8741
+ });
8742
+ }
8743
+ const subagentMarker = getCodexResponsesSubagentMarker(c);
8744
+ if (subagentMarker) debugJson(logger$1, "Detected Codex subagent headers:", subagentMarker);
8745
+ const incomingSessionId = subagentMarker ? getIncomingResponsesSessionId(c) : void 0;
8365
8746
  await checkRateLimit(state);
8366
8747
  const store = getRequestHistoryStore();
8367
8748
  const request = buildRequestContext(c);
8368
- const payload = await c.req.json();
8369
8749
  const clientModel = payload.model;
8370
- debugJson(logger$1, "Responses request payload:", payload);
8371
8750
  if (!isResponsesApiWebSearchEnabled()) removeWebSearchTool(payload);
8372
8751
  compactInputByLatestCompaction(payload);
8373
8752
  const streamRequested = Boolean(payload.stream);
8374
- const { initiator: initialInitiator } = getResponsesRequestOptions(payload);
8753
+ const { initiator: inferredInitiator } = getResponsesRequestOptions(payload);
8754
+ const initialInitiator = subagentMarker ? "agent" : inferredInitiator;
8375
8755
  const userId = payload.metadata?.user_id;
8376
8756
  const requestBodyPromptCacheKey = typeof payload.prompt_cache_key === "string" ? payload.prompt_cache_key : null;
8377
8757
  const { safetyIdentifier, sessionId: metadataSessionId } = parseUserIdMetadata(userId);
@@ -8393,8 +8773,8 @@ const handleResponses = async (c) => {
8393
8773
  message: "This model is only available via an alias. Please use the alias model name."
8394
8774
  });
8395
8775
  }
8396
- const upstreamRequestId = generateRequestIdFromPayload({ messages: payload.input }, normalizedPromptCacheKey);
8397
8776
  const headerSessionId = c.req.header("x-session-id") ?? null;
8777
+ const upstreamRequestId = generateRequestIdFromPayload({ messages: payload.input }, incomingSessionId ?? normalizedPromptCacheKey);
8398
8778
  const affinityKey = resolveAffinityKey({
8399
8779
  promptCacheKey: requestBodyPromptCacheKey,
8400
8780
  metadataSessionId,
@@ -8425,18 +8805,22 @@ const handleResponses = async (c) => {
8425
8805
  model: selectedModel.id
8426
8806
  };
8427
8807
  removeUnsupportedTools(upstreamPayload);
8808
+ const sanitizedImageCount = sanitizeOversizedInputImages(upstreamPayload, selectedModel.capabilities.limits.vision?.max_prompt_image_size);
8809
+ if (sanitizedImageCount > 0) logger$1.warn(`Omitted ${sanitizedImageCount} oversized input image(s) before forwarding to Copilot Responses`);
8428
8810
  applyResponsesApiContextManagement(upstreamPayload, selectedModel.capabilities.limits.max_prompt_tokens);
8429
8811
  compactInputByLatestCompaction(upstreamPayload);
8430
8812
  const premiumRemainingBefore = account.premiumRemaining;
8431
8813
  const premiumUnlimitedBefore = account.unlimited;
8432
8814
  const transport = getResponsesTransportForModel(selectedModel) ?? "http";
8433
- const { vision, initiator } = getResponsesRequestOptions(upstreamPayload);
8815
+ const { vision, initiator: inferredUpstreamInitiator } = getResponsesRequestOptions(upstreamPayload);
8816
+ const initiator = subagentMarker ? "agent" : inferredUpstreamInitiator;
8434
8817
  request.initiator = initiator;
8435
8818
  if (state.manualApprove) await awaitApproval();
8436
8819
  const accountCtx = toAccountContext(account);
8437
- const upstreamSessionId = getUUID(normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
8820
+ const upstreamSessionId = getUUID(incomingSessionId ?? normalizedPromptCacheKey ?? headerSessionId ?? upstreamRequestId);
8438
8821
  request.upstreamRequestId = upstreamRequestId;
8439
8822
  request.upstreamSessionId = upstreamSessionId;
8823
+ const bridgeId = c.req.header("x-responses-bridge-id") ?? void 0;
8440
8824
  if (streamRequested) return handleStreamingResponses({
8441
8825
  c,
8442
8826
  store,
@@ -8447,9 +8831,11 @@ const handleResponses = async (c) => {
8447
8831
  accountCtx,
8448
8832
  vision,
8449
8833
  initiator,
8834
+ subagentMarker,
8450
8835
  premiumRemainingBefore,
8451
8836
  premiumUnlimitedBefore,
8452
- transport
8837
+ transport,
8838
+ bridgeId
8453
8839
  });
8454
8840
  return handleNonStreamingResponses({
8455
8841
  c,
@@ -8461,9 +8847,11 @@ const handleResponses = async (c) => {
8461
8847
  accountCtx,
8462
8848
  vision,
8463
8849
  initiator,
8850
+ subagentMarker,
8464
8851
  premiumRemainingBefore,
8465
8852
  premiumUnlimitedBefore,
8466
- transport
8853
+ transport,
8854
+ bridgeId
8467
8855
  });
8468
8856
  };
8469
8857
  async function observeRequestError(accountId, error, affinity) {
@@ -8571,16 +8959,18 @@ function extractUsageFromChunkData(data) {
8571
8959
  }
8572
8960
  }
8573
8961
  async function handleStreamingResponses(params) {
8574
- const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore, transport } = params;
8962
+ const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, subagentMarker, premiumRemainingBefore, premiumUnlimitedBefore, transport, bridgeId } = params;
8575
8963
  let response;
8576
8964
  try {
8577
8965
  response = await createResponses(payload, {
8578
8966
  vision,
8579
8967
  initiator,
8968
+ subagentMarker,
8580
8969
  upstreamRequestId: request.upstreamRequestId,
8581
8970
  sessionId: request.upstreamSessionId,
8582
8971
  requestId: request.requestId,
8583
- transport
8972
+ transport,
8973
+ bridgeId
8584
8974
  }, accountCtx);
8585
8975
  selection.confirmAffinity?.();
8586
8976
  } catch (error) {
@@ -8768,7 +9158,7 @@ async function streamResponsesAndLog(params) {
8768
9158
  }
8769
9159
  }
8770
9160
  async function handleNonStreamingResponses(params) {
8771
- const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, premiumRemainingBefore, premiumUnlimitedBefore, transport } = params;
9161
+ const { c, store, request, payload, selection, clientModel, accountCtx, vision, initiator, subagentMarker, premiumRemainingBefore, premiumUnlimitedBefore, transport, bridgeId } = params;
8772
9162
  const { account, reservation, selectedModel, endpoint, costUnits } = selection;
8773
9163
  let usage = {};
8774
9164
  let errorState = { httpStatus: 200 };
@@ -8777,10 +9167,12 @@ async function handleNonStreamingResponses(params) {
8777
9167
  const response = await createResponses(payload, {
8778
9168
  vision,
8779
9169
  initiator,
9170
+ subagentMarker,
8780
9171
  upstreamRequestId: request.upstreamRequestId,
8781
9172
  sessionId: request.upstreamSessionId,
8782
9173
  requestId: request.requestId,
8783
- transport
9174
+ transport,
9175
+ bridgeId
8784
9176
  }, accountCtx);
8785
9177
  if (isAsyncIterable$1(response)) throw new Error("Upstream returned a stream unexpectedly");
8786
9178
  selection.confirmAffinity?.();
@@ -8842,6 +9234,31 @@ const removeUnsupportedTools = (payload) => {
8842
9234
  });
8843
9235
  if (dropped.length > 0) logger$1.debug("Removed unsupported tools:", dropped);
8844
9236
  };
9237
+ const getTrimmedHeader = (c, name) => {
9238
+ const value = c.req.header(name)?.trim();
9239
+ return value ? value : void 0;
9240
+ };
9241
+ const getIncomingResponsesSessionId = (c) => getTrimmedHeader(c, "session-id") ?? getTrimmedHeader(c, "x-session-id");
9242
+ const codexSubagentHeaderValues = new Set([
9243
+ "collab_spawn",
9244
+ "compact",
9245
+ "memory_consolidation",
9246
+ "review"
9247
+ ]);
9248
+ const getCodexResponsesSubagentMarker = (c) => {
9249
+ const agentType = getTrimmedHeader(c, "x-openai-subagent");
9250
+ if (!agentType || !codexSubagentHeaderValues.has(agentType)) return null;
9251
+ const threadId = getTrimmedHeader(c, "thread-id");
9252
+ const rootSessionId = getIncomingResponsesSessionId(c);
9253
+ const parentThreadId = getTrimmedHeader(c, "x-codex-parent-thread-id");
9254
+ if (!threadId && !rootSessionId && !parentThreadId) return null;
9255
+ const agentId = threadId ?? parentThreadId ?? rootSessionId;
9256
+ return {
9257
+ agent_id: agentId,
9258
+ agent_type: agentType,
9259
+ session_id: threadId ?? rootSessionId ?? agentId
9260
+ };
9261
+ };
8845
9262
  //#endregion
8846
9263
  //#region src/routes/responses/route.ts
8847
9264
  const responsesRoutes = new Hono();
@@ -8904,31 +9321,43 @@ usageRoute.get("/:accountIndex", async (c) => {
8904
9321
  });
8905
9322
  //#endregion
8906
9323
  //#region src/server.ts
8907
- const server = new Hono();
8908
- server.use(traceIdMiddleware);
8909
- server.use(logger());
8910
- server.use(cors());
8911
- server.use("*", createAuthMiddleware({
8912
- allowUnauthenticatedPaths: ["/"],
8913
- allowUnauthenticatedPathPrefixes: ["/admin", "/api/admin"]
8914
- }));
8915
- server.get("/", (c) => c.text("Server running"));
8916
- server.route("/chat/completions", completionRoutes);
8917
- server.route("/models", modelRoutes);
8918
- server.route("/embeddings", embeddingRoutes);
8919
- server.route("/usage", usageRoute);
8920
- server.route("/token", tokenRoute);
8921
- server.route("/responses", responsesRoutes);
8922
- server.route("/admin", adminRoutes);
8923
- server.route("/api/admin", adminApiRoutes);
8924
- server.route("/v1/chat/completions", completionRoutes);
8925
- server.route("/v1/models", modelRoutes);
8926
- server.route("/v1/embeddings", embeddingRoutes);
8927
- server.route("/v1/responses", responsesRoutes);
8928
- server.route("/v1/messages", messageRoutes);
8929
- server.route("/:provider/v1/messages", providerMessageRoutes);
8930
- server.route("/:provider/v1/models", providerModelRoutes);
9324
+ function createServer(options = {}) {
9325
+ const app = new Hono();
9326
+ const enableMcpHttp = options.enableMcpHttp ?? isMcpHttpEnabledFromEnv();
9327
+ app.use(traceIdMiddleware);
9328
+ app.use(logger());
9329
+ if (enableMcpHttp) {
9330
+ app.use(DEFAULT_MCP_HTTP_PATH, cors(mcpHttpCorsOptions));
9331
+ app.all(DEFAULT_MCP_HTTP_PATH, (c) => handleStreamableHttpMcpRequest(c.req.raw));
9332
+ } else app.all(DEFAULT_MCP_HTTP_PATH, (c) => c.json({ error: {
9333
+ type: "mcp_http_disabled",
9334
+ message: "MCP Streamable HTTP is disabled. Start with --enable-mcp-http to expose /mcp."
9335
+ } }, 404));
9336
+ app.use(cors());
9337
+ app.use("*", createAuthMiddleware({
9338
+ allowUnauthenticatedPaths: ["/", DEFAULT_MCP_HTTP_PATH],
9339
+ allowUnauthenticatedPathPrefixes: ["/admin", "/api/admin"]
9340
+ }));
9341
+ app.get("/", (c) => c.text("Server running"));
9342
+ app.route("/chat/completions", completionRoutes);
9343
+ app.route("/models", modelRoutes);
9344
+ app.route("/embeddings", embeddingRoutes);
9345
+ app.route("/usage", usageRoute);
9346
+ app.route("/token", tokenRoute);
9347
+ app.route("/responses", responsesRoutes);
9348
+ app.route("/admin", adminRoutes);
9349
+ app.route("/api/admin", adminApiRoutes);
9350
+ app.route("/v1/chat/completions", completionRoutes);
9351
+ app.route("/v1/models", modelRoutes);
9352
+ app.route("/v1/embeddings", embeddingRoutes);
9353
+ app.route("/v1/responses", responsesRoutes);
9354
+ app.route("/v1/messages", messageRoutes);
9355
+ app.route("/:provider/v1/messages", providerMessageRoutes);
9356
+ app.route("/:provider/v1/models", providerModelRoutes);
9357
+ return app;
9358
+ }
9359
+ createServer();
8931
9360
  //#endregion
8932
- export { server };
9361
+ export { createServer };
8933
9362
 
8934
- //# sourceMappingURL=server-GxNB5Syq.js.map
9363
+ //# sourceMappingURL=server-BYKxAFro.js.map