copilot-proxy-api
Version:
Self-hosted GitHub Copilot bridge for Claude Code and Codex CLI with OpenAI Responses, Anthropic Messages, streaming resilience, and context management.
1 lines • 241 kB
Source Map (JSON)
{"version":3,"file":"main.mjs","names":["process","handleCompletion","isNonStreaming","handleCompletion","CHARS_PER_TOKEN_ESTIMATE","TOKEN_RESERVE","IMAGE_STRIPPED_PLACEHOLDER"],"sources":["../src/lib/paths.ts","../src/lib/state.ts","../src/lib/api-config.ts","../src/services/github/get-copilot-token.ts","../src/services/github/get-device-code.ts","../src/services/github/get-user.ts","../src/services/copilot/get-models.ts","../src/services/get-vscode-version.ts","../src/lib/utils.ts","../src/services/github/poll-access-token.ts","../src/lib/token.ts","../src/auth.ts","../src/services/github/get-copilot-usage.ts","../src/check-usage.ts","../src/debug.ts","../src/lib/local-auth.ts","../src/lib/proxy.ts","../src/lib/shell.ts","../src/lib/request-trace.ts","../src/lib/approval.ts","../src/lib/rate-limit.ts","../src/lib/tokenizer.ts","../src/lib/copilot-fetch.ts","../src/lib/model-limits.ts","../src/lib/prompt-too-long.ts","../src/services/copilot/create-chat-completions.ts","../src/routes/chat-completions/handler.ts","../src/routes/chat-completions/route.ts","../src/routes/debug/route.ts","../src/services/copilot/create-embeddings.ts","../src/routes/embeddings/route.ts","../src/routes/messages/utils.ts","../src/routes/messages/non-stream-translation.ts","../src/routes/messages/count-tokens-handler.ts","../src/lib/context-manager.ts","../src/services/copilot/encrypted-output-recovery.ts","../src/services/copilot/create-responses.ts","../src/routes/messages/responses-bridge.ts","../src/routes/messages/stream-translation.ts","../src/routes/messages/handler.ts","../src/routes/messages/route.ts","../src/routes/models/route.ts","../src/routes/responses/handler.ts","../src/routes/responses/route.ts","../src/routes/token/route.ts","../src/routes/usage/route.ts","../src/server.ts","../src/start.ts","../src/main.ts"],"sourcesContent":["import fs from \"node:fs/promises\"\nimport os from \"node:os\"\nimport path from \"node:path\"\n\nconst APP_DIR = path.join(os.homedir(), \".local\", \"share\", \"copilot-proxy-api\")\n\nconst GITHUB_TOKEN_PATH = path.join(APP_DIR, \"github_token\")\n\nexport const PATHS = {\n APP_DIR,\n GITHUB_TOKEN_PATH,\n}\n\nexport async function ensurePaths(): Promise<void> {\n await fs.mkdir(PATHS.APP_DIR, { recursive: true })\n await ensureFile(PATHS.GITHUB_TOKEN_PATH)\n}\n\nasync function ensureFile(filePath: string): Promise<void> {\n try {\n await fs.access(filePath, fs.constants.W_OK)\n } catch {\n await fs.writeFile(filePath, \"\")\n await fs.chmod(filePath, 0o600)\n }\n}\n","import type { ModelsResponse } from \"~/services/copilot/get-models\"\n\nexport interface State {\n githubToken?: string\n copilotToken?: string\n\n accountType: string\n models?: ModelsResponse\n vsCodeVersion?: string\n\n manualApprove: boolean\n rateLimitWait: boolean\n showToken: boolean\n localApiKeys: Array<string>\n\n // Rate limiting configuration\n rateLimitSeconds?: number\n lastRequestTimestamp?: number\n}\n\nexport const state: State = {\n accountType: \"individual\",\n manualApprove: false,\n rateLimitWait: false,\n showToken: false,\n localApiKeys: [],\n}\n","import { randomUUID } from \"node:crypto\"\n\nimport type { State } from \"./state\"\n\nexport const standardHeaders = () => ({\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n})\n\nconst COPILOT_VERSION = \"0.53.0\"\nconst EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`\nconst USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`\n\nconst API_VERSION = \"2025-04-01\"\n\nexport const copilotBaseUrl = (state: State) =>\n state.accountType === \"individual\" ?\n \"https://api.githubcopilot.com\"\n : `https://api.${state.accountType}.githubcopilot.com`\nexport const copilotHeaders = (state: State, vision: boolean = false) => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${state.copilotToken}`,\n \"content-type\": standardHeaders()[\"content-type\"],\n \"copilot-integration-id\": \"vscode-chat\",\n \"editor-version\": `vscode/${state.vsCodeVersion}`,\n \"editor-plugin-version\": EDITOR_PLUGIN_VERSION,\n \"user-agent\": USER_AGENT,\n \"openai-intent\": \"conversation-panel\",\n \"x-github-api-version\": API_VERSION,\n \"x-request-id\": randomUUID(),\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n }\n\n if (vision) headers[\"copilot-vision-request\"] = \"true\"\n\n return headers\n}\n\nexport const GITHUB_API_BASE_URL = \"https://api.github.com\"\nexport const githubHeaders = (state: State) => ({\n ...standardHeaders(),\n authorization: `token ${state.githubToken}`,\n \"editor-version\": `vscode/${state.vsCodeVersion}`,\n \"editor-plugin-version\": EDITOR_PLUGIN_VERSION,\n \"user-agent\": USER_AGENT,\n \"x-github-api-version\": API_VERSION,\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n})\n\nexport const GITHUB_BASE_URL = \"https://github.com\"\nexport const GITHUB_CLIENT_ID = \"Iv1.b507a08c87ecfe98\"\nexport const GITHUB_APP_SCOPES = [\"read:user\"].join(\" \")\n","import { GITHUB_API_BASE_URL, githubHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport const getCopilotToken = async () => {\n const response = await fetch(\n `${GITHUB_API_BASE_URL}/copilot_internal/v2/token`,\n {\n headers: githubHeaders(state),\n },\n )\n\n if (!response.ok) throw new HTTPError(\"Failed to get Copilot token\", response)\n\n return (await response.json()) as GetCopilotTokenResponse\n}\n\n// Trimmed for the sake of simplicity\ninterface GetCopilotTokenResponse {\n expires_at: number\n refresh_in: number\n token: string\n}\n","import {\n GITHUB_APP_SCOPES,\n GITHUB_BASE_URL,\n GITHUB_CLIENT_ID,\n standardHeaders,\n} from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\n\nexport async function getDeviceCode(): Promise<DeviceCodeResponse> {\n const response = await fetch(`${GITHUB_BASE_URL}/login/device/code`, {\n method: \"POST\",\n headers: standardHeaders(),\n body: JSON.stringify({\n client_id: GITHUB_CLIENT_ID,\n scope: GITHUB_APP_SCOPES,\n }),\n })\n\n if (!response.ok) throw new HTTPError(\"Failed to get device code\", response)\n\n return (await response.json()) as DeviceCodeResponse\n}\n\nexport interface DeviceCodeResponse {\n device_code: string\n user_code: string\n verification_uri: string\n expires_in: number\n interval: number\n}\n","import { GITHUB_API_BASE_URL, standardHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport async function getGitHubUser() {\n const response = await fetch(`${GITHUB_API_BASE_URL}/user`, {\n headers: {\n authorization: `token ${state.githubToken}`,\n ...standardHeaders(),\n },\n })\n\n if (!response.ok) throw new HTTPError(\"Failed to get GitHub user\", response)\n\n return (await response.json()) as GithubUserResponse\n}\n\n// Trimmed for the sake of simplicity\ninterface GithubUserResponse {\n login: string\n}\n","import { copilotBaseUrl, copilotHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport const getModels = async () => {\n const response = await fetch(`${copilotBaseUrl(state)}/models`, {\n headers: copilotHeaders(state),\n })\n\n if (!response.ok) throw new HTTPError(\"Failed to get models\", response)\n\n return (await response.json()) as ModelsResponse\n}\n\nexport interface ModelsResponse {\n data: Array<Model>\n object: string\n}\n\ninterface ModelLimits {\n max_context_window_tokens?: number\n max_output_tokens?: number\n max_prompt_tokens?: number\n max_inputs?: number\n}\n\ninterface ModelSupports {\n tool_calls?: boolean\n parallel_tool_calls?: boolean\n dimensions?: boolean\n streaming?: boolean\n structured_outputs?: boolean\n vision?: boolean\n adaptive_thinking?: boolean\n reasoning_effort?: Array<string>\n}\n\ninterface ModelCapabilities {\n family: string\n limits: ModelLimits\n object: string\n supports: ModelSupports\n tokenizer: string\n type: string\n}\n\nexport interface Model {\n capabilities: ModelCapabilities\n id: string\n model_picker_enabled: boolean\n name: string\n object: string\n preview: boolean\n vendor: string\n version: string\n policy?: {\n state: string\n terms: string\n }\n supported_endpoints?: Array<string>\n}\n","// GitHub lists VS Code 1.128 as the minimum version for Claude Opus 5.\nconst FALLBACK = \"1.128.0\"\n\nexport async function getVSCodeVersion() {\n const controller = new AbortController()\n const timeout = setTimeout(() => {\n controller.abort()\n }, 5000)\n\n try {\n const response = await fetch(\n \"https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=visual-studio-code-bin\",\n {\n signal: controller.signal,\n },\n )\n\n const pkgbuild = await response.text()\n const pkgverRegex = /pkgver=([0-9.]+)/\n const match = pkgbuild.match(pkgverRegex)\n\n if (match) {\n return match[1]\n }\n\n return FALLBACK\n } catch {\n return FALLBACK\n } finally {\n clearTimeout(timeout)\n }\n}\n\nawait getVSCodeVersion()\n","import consola from \"consola\"\n\nimport { getModels } from \"~/services/copilot/get-models\"\nimport { getVSCodeVersion } from \"~/services/get-vscode-version\"\n\nimport { state } from \"./state\"\n\nexport const sleep = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms)\n })\n\nexport const isNullish = (value: unknown): value is null | undefined =>\n value === null || value === undefined\n\nexport async function cacheModels(): Promise<void> {\n const models = await getModels()\n state.models = models\n}\n\nexport const cacheVSCodeVersion = async () => {\n const response = await getVSCodeVersion()\n state.vsCodeVersion = response\n\n consola.info(`Using VSCode version: ${response}`)\n}\n","import consola from \"consola\"\n\nimport {\n GITHUB_BASE_URL,\n GITHUB_CLIENT_ID,\n standardHeaders,\n} from \"~/lib/api-config\"\nimport { sleep } from \"~/lib/utils\"\n\nimport type { DeviceCodeResponse } from \"./get-device-code\"\n\nexport async function pollAccessToken(\n deviceCode: DeviceCodeResponse,\n): Promise<string> {\n // Interval is in seconds, we need to multiply by 1000 to get milliseconds\n // I'm also adding another second, just to be safe\n const sleepDuration = (deviceCode.interval + 1) * 1000\n consola.debug(`Polling access token with interval of ${sleepDuration}ms`)\n\n while (true) {\n const response = await fetch(\n `${GITHUB_BASE_URL}/login/oauth/access_token`,\n {\n method: \"POST\",\n headers: standardHeaders(),\n body: JSON.stringify({\n client_id: GITHUB_CLIENT_ID,\n device_code: deviceCode.device_code,\n grant_type: \"urn:ietf:params:oauth:grant-type:device_code\",\n }),\n },\n )\n\n if (!response.ok) {\n await sleep(sleepDuration)\n consola.error(\"Failed to poll access token:\", await response.text())\n\n continue\n }\n\n const json = await response.json()\n consola.debug(\"Polling access token response:\", json)\n\n const { access_token } = json as AccessTokenResponse\n\n if (access_token) {\n return access_token\n } else {\n await sleep(sleepDuration)\n }\n }\n}\n\ninterface AccessTokenResponse {\n access_token: string\n token_type: string\n scope: string\n}\n","import consola from \"consola\"\nimport fs from \"node:fs/promises\"\n\nimport { PATHS } from \"~/lib/paths\"\nimport { getCopilotToken } from \"~/services/github/get-copilot-token\"\nimport { getDeviceCode } from \"~/services/github/get-device-code\"\nimport { getGitHubUser } from \"~/services/github/get-user\"\nimport { pollAccessToken } from \"~/services/github/poll-access-token\"\n\nimport { HTTPError } from \"./error\"\nimport { state } from \"./state\"\n\nconst readGithubToken = () => fs.readFile(PATHS.GITHUB_TOKEN_PATH, \"utf8\")\n\nconst writeGithubToken = (token: string) =>\n fs.writeFile(PATHS.GITHUB_TOKEN_PATH, token)\n\nlet copilotTokenRefresh: Promise<string> | undefined\n\nexport function refreshCopilotToken(staleToken?: string): Promise<string> {\n if (staleToken && state.copilotToken && state.copilotToken !== staleToken) {\n return Promise.resolve(state.copilotToken)\n }\n\n copilotTokenRefresh ??= getCopilotToken()\n .then(({ token }) => {\n state.copilotToken = token\n consola.debug(\"Copilot token refreshed\")\n if (state.showToken) {\n consola.info(\"Refreshed Copilot token:\", token)\n }\n return token\n })\n .finally(() => {\n copilotTokenRefresh = undefined\n })\n\n return copilotTokenRefresh\n}\n\nexport const setupCopilotToken = async () => {\n const { token, refresh_in } = await getCopilotToken()\n state.copilotToken = token\n\n // Display the Copilot token to the screen\n consola.debug(\"GitHub Copilot Token fetched successfully!\")\n if (state.showToken) {\n consola.info(\"Copilot token:\", token)\n }\n\n const refreshInterval = (refresh_in - 60) * 1000\n\n const refreshWithRetry = async (maxRetries = 5, baseDelay = 1000) => {\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n try {\n await refreshCopilotToken()\n return // Success, exit retry loop\n } catch (error) {\n consola.error(\n `Failed to refresh Copilot token (attempt ${attempt}/${maxRetries}):`,\n error,\n )\n\n if (attempt < maxRetries) {\n const delay = baseDelay * Math.pow(2, attempt - 1) // Exponential backoff\n consola.info(`Retrying in ${delay / 1000} seconds...`)\n await new Promise((resolve) => setTimeout(resolve, delay))\n } else {\n consola.error(\n \"Max retries reached. Token refresh failed, but keeping server alive.\",\n )\n // Don't throw - keep the server running with the old token\n }\n }\n }\n }\n\n setInterval(() => {\n consola.debug(\"Refreshing Copilot token\")\n void refreshWithRetry()\n }, refreshInterval)\n}\n\ninterface SetupGitHubTokenOptions {\n force?: boolean\n}\n\nexport async function setupGitHubToken(\n options?: SetupGitHubTokenOptions,\n): Promise<void> {\n try {\n const githubToken = await readGithubToken()\n\n if (githubToken && !options?.force) {\n state.githubToken = githubToken\n if (state.showToken) {\n consola.info(\"GitHub token:\", githubToken)\n }\n await logUser()\n\n return\n }\n\n consola.info(\"Not logged in, getting new access token\")\n const response = await getDeviceCode()\n consola.debug(\"Device code response:\", response)\n\n consola.info(\n `Please enter the code \"${response.user_code}\" in ${response.verification_uri}`,\n )\n\n const token = await pollAccessToken(response)\n await writeGithubToken(token)\n state.githubToken = token\n\n if (state.showToken) {\n consola.info(\"GitHub token:\", token)\n }\n await logUser()\n } catch (error) {\n if (error instanceof HTTPError) {\n consola.error(\"Failed to get GitHub token:\", await error.response.json())\n throw error\n }\n\n consola.error(\"Failed to get GitHub token:\", error)\n throw error\n }\n}\n\nasync function logUser() {\n const user = await getGitHubUser()\n consola.info(`Logged in as ${user.login}`)\n}\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { PATHS, ensurePaths } from \"./lib/paths\"\nimport { state } from \"./lib/state\"\nimport { setupGitHubToken } from \"./lib/token\"\n\ninterface RunAuthOptions {\n verbose: boolean\n showToken: boolean\n}\n\nexport async function runAuth(options: RunAuthOptions): Promise<void> {\n if (options.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n state.showToken = options.showToken\n\n await ensurePaths()\n await setupGitHubToken({ force: true })\n consola.success(\"GitHub token written to\", PATHS.GITHUB_TOKEN_PATH)\n}\n\nexport const auth = defineCommand({\n meta: {\n name: \"auth\",\n description: \"Run GitHub auth flow without running the server\",\n },\n args: {\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show GitHub token on auth\",\n },\n },\n run({ args }) {\n return runAuth({\n verbose: args.verbose,\n showToken: args[\"show-token\"],\n })\n },\n})\n","import { GITHUB_API_BASE_URL, githubHeaders } from \"~/lib/api-config\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport const getCopilotUsage = async (): Promise<CopilotUsageResponse> => {\n const response = await fetch(`${GITHUB_API_BASE_URL}/copilot_internal/user`, {\n headers: githubHeaders(state),\n })\n\n if (!response.ok) {\n throw new HTTPError(\"Failed to get Copilot usage\", response)\n }\n\n return (await response.json()) as CopilotUsageResponse\n}\n\nexport interface QuotaDetail {\n entitlement: number\n overage_count: number\n overage_permitted: boolean\n percent_remaining: number\n quota_id: string\n quota_remaining: number\n remaining: number\n unlimited: boolean\n}\n\ninterface QuotaSnapshots {\n chat: QuotaDetail\n completions: QuotaDetail\n premium_interactions: QuotaDetail\n}\n\ninterface CopilotUsageResponse {\n access_type_sku: string\n analytics_tracking_id: string\n assigned_date: string\n can_signup_for_limited: boolean\n chat_enabled: boolean\n copilot_plan: string\n organization_login_list: Array<unknown>\n organization_list: Array<unknown>\n quota_reset_date: string\n quota_snapshots: QuotaSnapshots\n}\n","import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { ensurePaths } from \"./lib/paths\"\nimport { setupGitHubToken } from \"./lib/token\"\nimport {\n getCopilotUsage,\n type QuotaDetail,\n} from \"./services/github/get-copilot-usage\"\n\nexport const checkUsage = defineCommand({\n meta: {\n name: \"check-usage\",\n description: \"Show current GitHub Copilot usage/quota information\",\n },\n async run() {\n await ensurePaths()\n await setupGitHubToken()\n try {\n const usage = await getCopilotUsage()\n const premium = usage.quota_snapshots.premium_interactions\n const premiumTotal = premium.entitlement\n const premiumUsed = premiumTotal - premium.remaining\n const premiumPercentUsed =\n premiumTotal > 0 ? (premiumUsed / premiumTotal) * 100 : 0\n const premiumPercentRemaining = premium.percent_remaining\n\n // Helper to summarize a quota snapshot\n function summarizeQuota(name: string, snap: QuotaDetail | undefined) {\n if (!snap) return `${name}: N/A`\n const total = snap.entitlement\n const used = total - snap.remaining\n const percentUsed = total > 0 ? (used / total) * 100 : 0\n const percentRemaining = snap.percent_remaining\n return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`\n }\n\n const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`\n const chatLine = summarizeQuota(\"Chat\", usage.quota_snapshots.chat)\n const completionsLine = summarizeQuota(\n \"Completions\",\n usage.quota_snapshots.completions,\n )\n\n consola.box(\n `Copilot Usage (plan: ${usage.copilot_plan})\\n`\n + `Quota resets: ${usage.quota_reset_date}\\n`\n + `\\nQuotas:\\n`\n + ` ${premiumLine}\\n`\n + ` ${chatLine}\\n`\n + ` ${completionsLine}`,\n )\n } catch (err) {\n consola.error(\"Failed to fetch Copilot usage:\", err)\n process.exit(1)\n }\n },\n})\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\nimport fs from \"node:fs/promises\"\nimport os from \"node:os\"\n\nimport { PATHS } from \"./lib/paths\"\n\ninterface DebugInfo {\n version: string\n runtime: {\n name: string\n version: string\n platform: string\n arch: string\n }\n paths: {\n APP_DIR: string\n GITHUB_TOKEN_PATH: string\n }\n tokenExists: boolean\n}\n\ninterface RunDebugOptions {\n json: boolean\n}\n\nasync function getPackageVersion(): Promise<string> {\n try {\n const packageJsonPath = new URL(\"../package.json\", import.meta.url).pathname\n // @ts-expect-error https://github.com/sindresorhus/eslint-plugin-unicorn/blob/v59.0.1/docs/rules/prefer-json-parse-buffer.md\n // JSON.parse() can actually parse buffers\n const packageJson = JSON.parse(await fs.readFile(packageJsonPath)) as {\n version: string\n }\n return packageJson.version\n } catch {\n return \"unknown\"\n }\n}\n\nfunction getRuntimeInfo() {\n const isBun = typeof Bun !== \"undefined\"\n\n return {\n name: isBun ? \"bun\" : \"node\",\n version: isBun ? Bun.version : process.version.slice(1),\n platform: os.platform(),\n arch: os.arch(),\n }\n}\n\nasync function checkTokenExists(): Promise<boolean> {\n try {\n const stats = await fs.stat(PATHS.GITHUB_TOKEN_PATH)\n if (!stats.isFile()) return false\n\n const content = await fs.readFile(PATHS.GITHUB_TOKEN_PATH, \"utf8\")\n return content.trim().length > 0\n } catch {\n return false\n }\n}\n\nasync function getDebugInfo(): Promise<DebugInfo> {\n const [version, tokenExists] = await Promise.all([\n getPackageVersion(),\n checkTokenExists(),\n ])\n\n return {\n version,\n runtime: getRuntimeInfo(),\n paths: {\n APP_DIR: PATHS.APP_DIR,\n GITHUB_TOKEN_PATH: PATHS.GITHUB_TOKEN_PATH,\n },\n tokenExists,\n }\n}\n\nfunction printDebugInfoPlain(info: DebugInfo): void {\n consola.info(`copilot-proxy-api debug\n\nVersion: ${info.version}\nRuntime: ${info.runtime.name} ${info.runtime.version} (${info.runtime.platform} ${info.runtime.arch})\n\nPaths:\n- APP_DIR: ${info.paths.APP_DIR}\n- GITHUB_TOKEN_PATH: ${info.paths.GITHUB_TOKEN_PATH}\n\nToken exists: ${info.tokenExists ? \"Yes\" : \"No\"}`)\n}\n\nfunction printDebugInfoJson(info: DebugInfo): void {\n console.log(JSON.stringify(info, null, 2))\n}\n\nexport async function runDebug(options: RunDebugOptions): Promise<void> {\n const debugInfo = await getDebugInfo()\n\n if (options.json) {\n printDebugInfoJson(debugInfo)\n } else {\n printDebugInfoPlain(debugInfo)\n }\n}\n\nexport const debug = defineCommand({\n meta: {\n name: \"debug\",\n description: \"Print debug information about the application\",\n },\n args: {\n json: {\n type: \"boolean\",\n default: false,\n description: \"Output debug information as JSON\",\n },\n },\n run({ args }) {\n return runDebug({\n json: args.json,\n })\n },\n})\n","import type { MiddlewareHandler } from \"hono\"\n\nimport { state } from \"./state\"\n\nexport function parseLocalApiKeys(value: string | undefined): Array<string> {\n if (!value) return []\n return value\n .split(\",\")\n .map((key) => key.trim())\n .filter((key) => key.length > 0)\n}\n\nexport const optionalLocalAuth: MiddlewareHandler = async (c, next) => {\n if (state.localApiKeys.length === 0 || c.req.method === \"OPTIONS\") {\n await next()\n return\n }\n\n const bearerToken = extractBearerToken(c.req.header(\"authorization\"))\n const apiKey = c.req.header(\"x-api-key\")?.trim()\n\n if (\n (bearerToken && state.localApiKeys.includes(bearerToken))\n || (apiKey && state.localApiKeys.includes(apiKey))\n ) {\n await next()\n return\n }\n\n return c.json(\n {\n error: {\n message: \"Missing or invalid API key\",\n type: \"authentication_error\",\n },\n },\n 401,\n )\n}\n\nfunction extractBearerToken(\n authorization: string | undefined,\n): string | undefined {\n if (!authorization) return undefined\n\n const prefix = \"bearer \"\n if (!authorization.toLowerCase().startsWith(prefix)) return undefined\n\n const token = authorization.slice(prefix.length).trim()\n return token.length > 0 ? token : undefined\n}\n","import consola from \"consola\"\nimport { getProxyForUrl } from \"proxy-from-env\"\nimport { Agent, ProxyAgent, setGlobalDispatcher, type Dispatcher } from \"undici\"\n\nexport function initProxyFromEnv(): void {\n if (typeof Bun !== \"undefined\") return\n\n try {\n const direct = new Agent()\n const proxies = new Map<string, ProxyAgent>()\n\n // We only need a minimal dispatcher that implements `dispatch` at runtime.\n // Typing the object as `Dispatcher` forces TypeScript to require many\n // additional methods. Instead, keep a plain object and cast when passing\n // to `setGlobalDispatcher`.\n const dispatcher = {\n dispatch(\n options: Dispatcher.DispatchOptions,\n handler: Dispatcher.DispatchHandler,\n ) {\n try {\n const origin =\n typeof options.origin === \"string\" ?\n new URL(options.origin)\n : (options.origin as URL)\n const get = getProxyForUrl as unknown as (\n u: string,\n ) => string | undefined\n const raw = get(origin.toString())\n const proxyUrl = raw && raw.length > 0 ? raw : undefined\n if (!proxyUrl) {\n consola.debug(`HTTP proxy bypass: ${origin.hostname}`)\n return (direct as unknown as Dispatcher).dispatch(options, handler)\n }\n let agent = proxies.get(proxyUrl)\n if (!agent) {\n agent = new ProxyAgent(proxyUrl)\n proxies.set(proxyUrl, agent)\n }\n let label = proxyUrl\n try {\n const u = new URL(proxyUrl)\n label = `${u.protocol}//${u.host}`\n } catch {\n /* noop */\n }\n consola.debug(`HTTP proxy route: ${origin.hostname} via ${label}`)\n return (agent as unknown as Dispatcher).dispatch(options, handler)\n } catch {\n return (direct as unknown as Dispatcher).dispatch(options, handler)\n }\n },\n close() {\n return direct.close()\n },\n destroy() {\n return direct.destroy()\n },\n }\n\n setGlobalDispatcher(dispatcher as unknown as Dispatcher)\n consola.debug(\"HTTP proxy configured from environment (per-URL)\")\n } catch (err) {\n consola.debug(\"Proxy setup skipped:\", err)\n }\n}\n","import { execSync } from \"node:child_process\"\nimport process from \"node:process\"\n\ntype ShellName = \"bash\" | \"zsh\" | \"fish\" | \"powershell\" | \"cmd\" | \"sh\"\ntype EnvVars = Record<string, string | undefined>\n\nfunction getShell(): ShellName {\n const { platform, ppid, env } = process\n\n if (platform === \"win32\") {\n try {\n const command = `wmic process get ParentProcessId,Name | findstr \"${ppid}\"`\n const parentProcess = execSync(command, { stdio: \"pipe\" }).toString()\n\n if (parentProcess.toLowerCase().includes(\"powershell.exe\")) {\n return \"powershell\"\n }\n } catch {\n return \"cmd\"\n }\n\n return \"cmd\"\n } else {\n const shellPath = env.SHELL\n if (shellPath) {\n if (shellPath.endsWith(\"zsh\")) return \"zsh\"\n if (shellPath.endsWith(\"fish\")) return \"fish\"\n if (shellPath.endsWith(\"bash\")) return \"bash\"\n }\n\n return \"sh\"\n }\n}\n\n/**\n * Generates a copy-pasteable script to set multiple environment variables\n * and run a subsequent command.\n * @param {EnvVars} envVars - An object of environment variables to set.\n * @param {string} commandToRun - The command to run after setting the variables.\n * @returns {string} The formatted script string.\n */\nexport function generateEnvScript(\n envVars: EnvVars,\n commandToRun: string = \"\",\n): string {\n const shell = getShell()\n const filteredEnvVars = Object.entries(envVars).filter(\n ([, value]) => value !== undefined,\n ) as Array<[string, string]>\n\n let commandBlock: string\n\n switch (shell) {\n case \"powershell\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `$env:${key} = ${value}`)\n .join(\"; \")\n break\n }\n case \"cmd\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `set ${key}=${value}`)\n .join(\" & \")\n break\n }\n case \"fish\": {\n commandBlock = filteredEnvVars\n .map(([key, value]) => `set -gx ${key} ${value}`)\n .join(\"; \")\n break\n }\n default: {\n // bash, zsh, sh\n const assignments = filteredEnvVars\n .map(([key, value]) => `${key}=${value}`)\n .join(\" \")\n commandBlock = filteredEnvVars.length > 0 ? `export ${assignments}` : \"\"\n break\n }\n }\n\n if (commandBlock && commandToRun) {\n const separator = shell === \"cmd\" ? \" & \" : \" && \"\n return `${commandBlock}${separator}${commandToRun}`\n }\n\n return commandBlock || commandToRun\n}\n","import type { MiddlewareHandler } from \"hono\"\n\nimport { randomUUID } from \"node:crypto\"\n\nconst MAX_REQUEST_TRACES = 100\n\nexport interface RequestTrace {\n id: string\n method: string\n path: string\n status: number\n ok: boolean\n durationMs: number\n startedAt: string\n error?: string\n}\n\nconst requestTraces: Array<RequestTrace> = []\n\nfunction createRequestId(): string {\n return `req_${randomUUID().replaceAll(\"-\", \"\")}`\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : \"Unknown error\"\n}\n\nfunction addRequestTrace(trace: RequestTrace): void {\n requestTraces.unshift(trace)\n if (requestTraces.length > MAX_REQUEST_TRACES) {\n requestTraces.length = MAX_REQUEST_TRACES\n }\n}\n\nexport function getRequestTraces(): Array<RequestTrace> {\n return [...requestTraces]\n}\n\nexport function clearRequestTraces(): void {\n requestTraces.length = 0\n}\n\nexport const requestTraceMiddleware: MiddlewareHandler = async (c, next) => {\n const startedAtMs = Date.now()\n const startedAt = new Date(startedAtMs).toISOString()\n const requestId =\n c.req.header(\"x-client-request-id\")\n ?? c.req.header(\"x-request-id\")\n ?? createRequestId()\n\n c.header(\"request-id\", requestId)\n c.header(\"x-request-id\", requestId)\n\n let error: string | undefined\n try {\n await next()\n } catch (caught) {\n error = errorMessage(caught)\n throw caught\n } finally {\n const finalRequestId =\n c.res.headers.get(\"x-request-id\")\n ?? c.res.headers.get(\"request-id\")\n ?? requestId\n const status = error ? 500 : c.res.status\n addRequestTrace({\n id: finalRequestId,\n method: c.req.method,\n path: c.req.path,\n status,\n ok: !error && status < 400,\n durationMs: Date.now() - startedAtMs,\n startedAt,\n error,\n })\n }\n}\n","import consola from \"consola\"\n\nimport { HTTPError } from \"./error\"\n\nexport const awaitApproval = async () => {\n const response = await consola.prompt(`Accept incoming request?`, {\n type: \"confirm\",\n })\n\n if (!response)\n throw new HTTPError(\n \"Request rejected\",\n Response.json({ message: \"Request rejected\" }, { status: 403 }),\n )\n}\n","import consola from \"consola\"\n\nimport type { State } from \"./state\"\n\nimport { HTTPError } from \"./error\"\nimport { sleep } from \"./utils\"\n\nexport async function checkRateLimit(state: State) {\n if (state.rateLimitSeconds === undefined) return\n\n const now = Date.now()\n\n if (!state.lastRequestTimestamp) {\n state.lastRequestTimestamp = now\n return\n }\n\n const elapsedSeconds = (now - state.lastRequestTimestamp) / 1000\n\n if (elapsedSeconds > state.rateLimitSeconds) {\n state.lastRequestTimestamp = now\n return\n }\n\n const waitTimeSeconds = Math.ceil(state.rateLimitSeconds - elapsedSeconds)\n\n if (!state.rateLimitWait) {\n consola.warn(\n `Rate limit exceeded. Need to wait ${waitTimeSeconds} more seconds.`,\n )\n throw new HTTPError(\n \"Rate limit exceeded\",\n Response.json({ message: \"Rate limit exceeded\" }, { status: 429 }),\n )\n }\n\n const waitTimeMs = waitTimeSeconds * 1000\n consola.warn(\n `Rate limit reached. Waiting ${waitTimeSeconds} seconds before proceeding...`,\n )\n await sleep(waitTimeMs)\n // eslint-disable-next-line require-atomic-updates\n state.lastRequestTimestamp = now\n consola.info(\"Rate limit wait completed, proceeding with request\")\n return\n}\n","import type {\n ChatCompletionsPayload,\n ContentPart,\n Message,\n Tool,\n ToolCall,\n} from \"~/services/copilot/create-chat-completions\"\nimport type { Model } from \"~/services/copilot/get-models\"\n\n// Encoder type mapping\nconst ENCODING_MAP = {\n o200k_base: () => import(\"gpt-tokenizer/encoding/o200k_base\"),\n cl100k_base: () => import(\"gpt-tokenizer/encoding/cl100k_base\"),\n p50k_base: () => import(\"gpt-tokenizer/encoding/p50k_base\"),\n p50k_edit: () => import(\"gpt-tokenizer/encoding/p50k_edit\"),\n r50k_base: () => import(\"gpt-tokenizer/encoding/r50k_base\"),\n} as const\n\ntype SupportedEncoding = keyof typeof ENCODING_MAP\n\n// Define encoder interface\ninterface Encoder {\n encode: (text: string) => Array<number>\n}\n\n// Cache loaded encoders to avoid repeated imports\nconst encodingCache = new Map<string, Encoder>()\n\n/**\n * Calculate tokens for tool calls\n */\nconst calculateToolCallsTokens = (\n toolCalls: Array<ToolCall>,\n encoder: Encoder,\n constants: ReturnType<typeof getModelConstants>,\n): number => {\n let tokens = 0\n for (const toolCall of toolCalls) {\n tokens += constants.funcInit\n tokens += encoder.encode(JSON.stringify(toolCall)).length\n }\n tokens += constants.funcEnd\n return tokens\n}\n\n/**\n * Calculate tokens for content parts\n */\nconst calculateContentPartsTokens = (\n contentParts: Array<ContentPart>,\n encoder: Encoder,\n): number => {\n let tokens = 0\n for (const part of contentParts) {\n if (part.type === \"image_url\") {\n tokens += encoder.encode(part.image_url.url).length + 85\n } else if (part.text) {\n tokens += encoder.encode(part.text).length\n }\n }\n return tokens\n}\n\n/**\n * Calculate tokens for a single message\n */\nconst calculateMessageTokens = (\n message: Message,\n encoder: Encoder,\n constants: ReturnType<typeof getModelConstants>,\n): number => {\n const tokensPerMessage = 3\n const tokensPerName = 1\n let tokens = tokensPerMessage\n for (const [key, value] of Object.entries(message)) {\n if (typeof value === \"string\") {\n tokens += encoder.encode(value).length\n }\n if (key === \"name\") {\n tokens += tokensPerName\n }\n if (key === \"tool_calls\") {\n tokens += calculateToolCallsTokens(\n value as Array<ToolCall>,\n encoder,\n constants,\n )\n }\n if (key === \"content\" && Array.isArray(value)) {\n tokens += calculateContentPartsTokens(\n value as Array<ContentPart>,\n encoder,\n )\n }\n }\n return tokens\n}\n\n/**\n * Calculate tokens using custom algorithm\n */\nconst calculateTokens = (\n messages: Array<Message>,\n encoder: Encoder,\n constants: ReturnType<typeof getModelConstants>,\n): number => {\n if (messages.length === 0) {\n return 0\n }\n let numTokens = 0\n for (const message of messages) {\n numTokens += calculateMessageTokens(message, encoder, constants)\n }\n // every reply is primed with <|start|>assistant<|message|>\n numTokens += 3\n return numTokens\n}\n\n/**\n * Get the corresponding encoder module based on encoding type\n */\nconst getEncodeChatFunction = async (encoding: string): Promise<Encoder> => {\n if (encodingCache.has(encoding)) {\n const cached = encodingCache.get(encoding)\n if (cached) {\n return cached\n }\n }\n\n const supportedEncoding = encoding as SupportedEncoding\n if (!(supportedEncoding in ENCODING_MAP)) {\n const fallbackModule = (await ENCODING_MAP.o200k_base()) as Encoder\n encodingCache.set(encoding, fallbackModule)\n return fallbackModule\n }\n\n const encodingModule = (await ENCODING_MAP[supportedEncoding]()) as Encoder\n encodingCache.set(encoding, encodingModule)\n return encodingModule\n}\n\n/**\n * Get tokenizer type from model information\n */\nexport const getTokenizerFromModel = (model: Model): string => {\n return model.capabilities.tokenizer || \"o200k_base\"\n}\n\n/**\n * Get model-specific constants for token calculation\n */\nconst getModelConstants = (model: Model) => {\n return model.id === \"gpt-3.5-turbo\" || model.id === \"gpt-4\" ?\n {\n funcInit: 10,\n propInit: 3,\n propKey: 3,\n enumInit: -3,\n enumItem: 3,\n funcEnd: 12,\n }\n : {\n funcInit: 7,\n propInit: 3,\n propKey: 3,\n enumInit: -3,\n enumItem: 3,\n funcEnd: 12,\n }\n}\n\n/**\n * Calculate tokens for a single parameter\n */\nconst calculateParameterTokens = (\n key: string,\n prop: unknown,\n context: {\n encoder: Encoder\n constants: ReturnType<typeof getModelConstants>\n },\n): number => {\n const { encoder, constants } = context\n let tokens = constants.propKey\n\n // Early return if prop is not an object\n if (typeof prop !== \"object\" || prop === null) {\n return tokens\n }\n\n // Type assertion for parameter properties\n const param = prop as {\n type?: string\n description?: string\n enum?: Array<unknown>\n [key: string]: unknown\n }\n\n const paramName = key\n const paramType = param.type || \"string\"\n let paramDesc = param.description || \"\"\n\n // Handle enum values\n if (param.enum && Array.isArray(param.enum)) {\n tokens += constants.enumInit\n for (const item of param.enum) {\n tokens += constants.enumItem\n tokens += encoder.encode(String(item)).length\n }\n }\n\n // Clean up description\n if (paramDesc.endsWith(\".\")) {\n paramDesc = paramDesc.slice(0, -1)\n }\n\n // Encode the main parameter line\n const line = `${paramName}:${paramType}:${paramDesc}`\n tokens += encoder.encode(line).length\n\n // Handle additional properties (excluding standard ones)\n const excludedKeys = new Set([\"type\", \"description\", \"enum\"])\n for (const propertyName of Object.keys(param)) {\n if (!excludedKeys.has(propertyName)) {\n const propertyValue = param[propertyName]\n const propertyText =\n typeof propertyValue === \"string\" ? propertyValue : (\n JSON.stringify(propertyValue)\n )\n tokens += encoder.encode(`${propertyName}:${propertyText}`).length\n }\n }\n\n return tokens\n}\n\n/**\n * Calculate tokens for function parameters\n */\nconst calculateParametersTokens = (\n parameters: unknown,\n encoder: Encoder,\n constants: ReturnType<typeof getModelConstants>,\n): number => {\n if (!parameters || typeof parameters !== \"object\") {\n return 0\n }\n\n const params = parameters as Record<string, unknown>\n let tokens = 0\n\n for (const [key, value] of Object.entries(params)) {\n if (key === \"properties\") {\n const properties = value as Record<string, unknown>\n if (Object.keys(properties).length > 0) {\n tokens += constants.propInit\n for (const propKey of Object.keys(properties)) {\n tokens += calculateParameterTokens(propKey, properties[propKey], {\n encoder,\n constants,\n })\n }\n }\n } else {\n const paramText =\n typeof value === \"string\" ? value : JSON.stringify(value)\n tokens += encoder.encode(`${key}:${paramText}`).length\n }\n }\n\n return tokens\n}\n\n/**\n * Calculate tokens for a single tool\n */\nconst calculateToolTokens = (\n tool: Tool,\n encoder: Encoder,\n constants: ReturnType<typeof getModelConstants>,\n): number => {\n let tokens = constants.funcInit\n const func = tool.function\n const fName = func.name\n let fDesc = func.description || \"\"\n if (fDesc.endsWith(\".\")) {\n fDesc = fDesc.slice(0, -1)\n }\n const line = fName + \":\" + fDesc\n tokens += encoder.encode(line).length\n if (\n typeof func.parameters === \"object\" // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n && func.parameters !== null\n ) {\n tokens += calculateParametersTokens(func.parameters, encoder, constants)\n }\n return tokens\n}\n\n/**\n * Calculate token count for tools based on model\n */\nexport const numTokensForTools = (\n tools: Array<Tool>,\n encoder: Encoder,\n constants: ReturnType<typeof getModelConstants>,\n): number => {\n let funcTokenCount = 0\n for (const tool of tools) {\n funcTokenCount += calculateToolTokens(tool, encoder, constants)\n }\n funcTokenCount += constants.funcEnd\n return funcTokenCount\n}\n\n/**\n * Calculate the token count of messages, supporting multiple GPT encoders\n */\nexport const getTokenCount = async (\n payload: ChatCompletionsPayload,\n model: Model,\n): Promise<{ input: number; output: number }> => {\n // Get tokenizer string\n const tokenizer = getTokenizerFromModel(model)\n\n // Get corresponding encoder module\n const encoder = await getEncodeChatFunction(tokenizer)\n\n const simplifiedMessages = payload.messages\n const inputMessages = simplifiedMessages.filter(\n (msg) => msg.role !== \"assistant\",\n )\n const outputMessages = simplifiedMessages.filter(\n (msg) => msg.role === \"assistant\",\n )\n\n const constants = getModelConstants(model)\n let inputTokens = calculateTokens(inputMessages, encoder, constants)\n if (payload.tools && payload.tools.length > 0) {\n inputTokens += numTokensForTools(payload.tools, encoder, constants)\n }\n const outputTokens = calculateTokens(outputMessages, encoder, constants)\n\n return {\n input: inputTokens,\n output: outputTokens,\n }\n}\n","import consola from \"consola\"\n\nimport { refreshCopilotToken } from \"./token\"\nimport { sleep } from \"./utils\"\n\nconst DEFAULT_ATTEMPTS = 3\nconst RETRY_DELAY_MS = 250\nconst LARGE_PAYLOAD_CONTEXT_OVERFLOW_BYTES = 2_000_000\n\nexport interface CopilotFetchOptions extends RequestInit {\n attempts?: number\n retryDelayMs?: number\n}\n\nexport async function copilotFetch(\n url: string,\n options: CopilotFetchOptions,\n): Promise<Response> {\n const {\n attempts = DEFAULT_ATTEMPTS,\n retryDelayMs = RETRY_DELAY_MS,\n ...init\n } = options\n const bodyLength = typeof init.body === \"string\" ? init.body.length : 0\n const requestLabel = formatRequestLabel(url, init.method)\n let canRefreshAuth = true\n\n let lastError: unknown\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n let response = await fetch(url, init)\n if (canRefreshAuth && isAuthFailure(response)) {\n canRefreshAuth = false\n response = await retryWithRefreshedToken(url, init, response)\n }\n if (!shouldRetryResponse(response, bodyLength) || attempt === attempts) {\n return response\n }\n\n consola.warn(\n `Copilot ${requestLabel} returned ${response.status}; retrying (${attempt}/${attempts})`,\n )\n } catch (error) {\n lastError = error\n if (\n attempt === attempts\n || isLikelyContextOverflowTimeout(error, bodyLength)\n ) {\n throw error\n }\n consola.warn(\n `Copilot ${requestLabel} failed (${formatErrorMessage(error)}); retrying (${attempt}/${attempts})`,\n )\n }\n\n await sleep(retryDelayMs * attempt)\n }\n\n throw lastError instanceof Error ? lastError : (\n new Error(\"Copilot request failed\")\n )\n}\n\nfunction isAuthFailure(response: Response): boolean {\n return response.status === 401 || response.status === 403\n}\n\nasync function retryWithRefreshedToken(\n url: string,\n init: RequestInit,\n response: Response,\n): Promise<Response> {\n const staleToken = getBearerToken(init.headers)\n const requestLabel = formatRequestLabel(url, init.method)\n\n try {\n const token = await refreshCopilotToken(staleToken)\n const headers = new Headers(init.headers)\n headers.set(\"Authorization\", `Bearer ${token}`)\n init.headers = headers\n consola.warn(\n `Copilot ${requestLabel} returned ${response.status}; refreshed token and retrying`,\n )\n return await fetch(url, init)\n } catch (error) {\n consola.warn(\n `Copilot token refresh failed after ${requestLabel} returned ${response.status}: ${formatErrorMessage(error)}`,\n )\n return response\n }\n}\n\nfunction getBearerToken(headers: RequestInit[\"headers\"]): string | undefined {\n const authorization = new Headers(headers).get(\"Authorization\")\n if (!authorization?.toLowerCase().startsWith(\"bearer \")) return undefined\n return authorization.slice(7)\n}\n\nfunction formatRequestLabel(url: string, method: string | undefined): string {\n const verb = method?.toUpperCase() ?? \"GET\"\n\n try {\n const parsed = new URL(url)\n return `${verb} ${parsed.pathname}`\n } catch {\n return `${verb} ${url}`\n }\n}\n\nfunction formatErrorMessage(error: unknown): string {\n if (error instanceof Error && error.message) return error.message\n return \"unknown error\"\n}\n\nfunction shouldRetryResponse(response: Response, bodyLength: number): boolean {\n if (\n response.status >= 500\n && bodyLength > LARGE_PAYLOAD_CONTEXT_OVERFLOW_BYTES\n ) {\n return false\n }\n\n return (\n response.status === 408\n || response.status === 409\n || response.status === 425\n || response.status === 429\n || response.status === 499\n || response.status === 500\n || response.status === 502\n || response.status === 503\n || response.status === 504\n )\n}\n\nexport function isLikelyContextOverflowTimeout(\n error: unknown,\n bodyLength: number,\n): boolean {\n return (\n bodyLength > LARGE_PAYLOAD_CONTEXT_OVERFLOW_BYTES\n && error instanceof Error\n && /operation timed out|timed out/i.test(error.message)\n )\n}\n","import type { Model } from \"~/services/copilot/get-models\"\n\nexport const DEFAULT_PROMPT_TOKENS = 200_000\nexport const ONE_M_PROMPT_TOKENS = 936_000\nexport const ONE_M_CONTEXT_TOKENS = 1_000_000\n\nexport function getModelPromptLimit(\n modelId: string,\n limits?: Model[\"capabilities\"][\"limits\"],\n): number {\n return getKnownModelPromptLimit(modelId, limits) ?? DEFAULT_PROMPT_TOKENS\n}\n\nexport function getKnownModelPromptLimit(\n modelId: string,\n limits?: Model[\"capabilities\"][\"limits\"],\n): number | undefined {\n if (limits?.max_prompt_tokens) return limits.max_prompt_tokens\n if (isOneMillionContextModel(modelId)) return ONE_M_PROMPT_TOKENS\n return limits?.max_context_window_tokens\n}\n\nexport function getModelContextLimit(\n modelId: string,\n limits?: Model[\"capabilities\"][\"limits\"],\n): number {\n if (limits?.max_context_window_tokens) return limits.max_context_window_tokens\n if (isOneMillionContextModel(modelId)) return ONE_M_CONTEXT_TOKENS\n return DEFAULT_PROMPT_TOKENS\n}\n\nfunction isOneMillionContextModel(modelId: string): boolean {\n return /(?:^|[-_.])1m(?:$|[-_.])/i.test(modelId)\n}\n","import consola from \"consola\"\n\nimport type { ChatCompletionsPayload } from \"~/services/copilot/create-chat-completions\"\n\nimport { HTTPError } from \"./error\"\nimport { getModelPromptLimit } from \"./model-limits\"\nimport { state } from \"./state\"\n\nexport function createPromptTooLongError(\n payload: ChatCompletionsPayload,\n bodyLength: number,\n): HTTPError {\n const estimatedTokens = Math.ceil(bodyLength / 4)\n const modelCaps = state.models?.data.find((m) => m.id === payload.model)\n ?.capabilities.limits\n const modelLimit = getModelPromptLimit(payload.model, modelCaps)\n const maxOutputTokens = payload.max_tokens ?? 0\n\n consola.warn(\n `Context overflow -> returning 400 prompt-too-long (~${estimatedTokens} + ${maxOutputTokens} > ${modelLimit}) to trigger Claude Code reactive compaction`,\n )\n\n return new HTTPError(\n \"Prompt too long\",\n new Response(\n JSON.stringify({\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message: `prompt is too long: input length and \\`max_tokens\\` exceed context limit: ${estimatedTokens} + ${maxOutputTokens} > ${modelLimit} tokens`,\n },\n }),\n {\n status: 400,\n statusText: \"Bad Request\",\n headers: { \"content-type\": \"application/json\" },\n },\n ),\n )\n}\n","import consola from \"consola\"\nimport { events } from \"fetch-event-stream\"\n\nimport { copilotHeaders, copilotBaseUrl } from \"~/lib/api-config\"\nimport {\n copilotFetch,\n isLikelyContextOverflowTimeout,\n} from \"~/lib/copilot-fetch\"\nimport { HTTPError } from \"~/lib/error\"\nimport { createPromptTooLongError } from \"~/lib/prompt-too-long\"\nimport { state } from \"~/lib/state\"\n\n/* eslint-disable complexity */\nexport const createChatCompletions = async (\n payload: ChatCompletionsPayload,\n) => {\n if (!state.copilotToken) throw new Error(\"Copilot token not found\")\n\n const enableVision = payload.messages.some(\n (x) =>\n typeof x.content !== \"string\"\n && x.content?.some((x) => x.type === \"image_url\"),\n )\n\n // Agent/user check for X-Initiator header\n const isAgentCall = payload.messages.some((msg) =>\n [\"assistant\", \"tool\"].includes(msg.role),\n )\n\n const headers: Record<string, string> = {\n ...copilotHeaders(state, enableVision),\n \"X-Initiator\": isAgentCall ? \"agent\" : \"user\",\n }\n\n const body = JSON.stringify(payload)\n consola.info(\n `Sending payload: ${body.length} bytes, ${payload.messages.length} messages, model: ${payload.model}`,\n )\n\n let response: Response\n try {\n response = await copilotFetch(`${copilotBaseUrl(state)}/chat/completions`, {\n method: \"POST\",\n headers,\n body,\n })\n } catch (error) {\n if (isLikelyContextOverflowTimeout(error, body.length)) {\n throw createPromptTooLongError(payload, body.length)\n }\n throw error\n }\n\n if (!response.ok) {\n const errorBody = await response.text()\n consola.error(\n `Failed to create chat completions - Status: ${response.status} ${response.statusText}`,\n )\n consola.error(`Response body: ${errorBody}`)\n consola.error(`Request payload size: ${body.length} bytes`)\n\n // Detect context overflow errors. Includes:\n // - HTTP 413 from Azure Front Door (>~5.4 MB)\n // - Backend \"operation timed out\" / 500 in the 2.5 MiB - 5.3 MB dead\n // zone (Copilot backend hangs >90s instead of cleanly rejecting;\n // Bun fetch eventually surfaces this as a 500 or upstream timeout)\n // - Various worded variants Copilot has emitted in the past\n const isContextOverflow =\n response.status === 413\n || /request entity too large/i.test(errorBody)\n || /exceeds the limit of \\d+/i.test(errorBody)\n || /context_length_exceeded/i.test(errorBody)\n || /operation timed out/i.test(errorBody)\n || /payload too large/i.test(errorBody)\n || /maximum context length/i.test(errorBody)\n || (response.status >= 500\n && response.status < 600\n && body.length > 2_000_000)\n\n if (isContextOverflow) {\n // Return HTTP 400 with \"prompt is too long\" so Claude Code's reactive\n // compaction kicks in (verified from Claude Code source: services/api/\n // errors.ts triggers tryReactiveCompact on this exact phrase). Returning\n // 529/overloaded_error would only trigger retry-with-backoff (3 attempts\n // then fatal \"Repeated 529 Overloaded errors\").\n //\n // Token estimate: bytes/4 is a rough but conservative approximation.\n // Claude Code uses the gap (estimatedTokens - modelLimit) to decide how\n // many message groups to peel in a single compaction pass.\n //\n // Use max_prompt_tokens (the field Copilot actually enforces — e.g.\n // 168K for opus-4.7) rather than max_context_window_tokens (the\n // total window incl. output, ≥200K). Falling back to the larger field\n // and finally 200K preserves behavior for models lacking metadata.\n throw createPromptTooLongError(payload, body.length)\n }\n\n throw new HTTPError(\n \"Failed to create chat completions\",\n new Response(errorBody, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n }),\n )\n }\n\n if (payload.stream) {\n return events(response)\n }\n\n return (await response.json()) as ChatCompletionResponse\n}\n\n// Streaming types\n\nexport interface ChatCompletionChunk {\n id: string\n object: \"chat.completion.chunk\"\n created: number\n model: string\n choices: Array<Choice>\n system_fingerprint?: string\n usage?: {\n prompt_tokens: number\n completion_tokens: number\n total_tokens: number\n prompt_tokens_details?: {\n cached_tokens: number\n }\n completion_tokens_details?: {\n accepted_prediction_tokens: number\n rejected_prediction_tokens: number\n }\n }\n}\n\ninterface Delta {\n content?: string | null\n role?: \"user\" | \"assistant\" | \"system\" | \"tool\"\n tool_calls?: Array<{\n index: number\n id?: string\n type?: \"function\"\n function?: {\n name?: string\n arguments?: string\n }\n }>\n}\n\ninterface Choice {\n index: number\n delta: Delta\n finish_reason: \"stop\" | \"length\" | \"tool_calls\" | \"content_filter\" | null\n logprobs: object | null\n}\n\n// Non-streaming types\n\nexport interface ChatCompletionResponse {\n id: string\n object: \"chat.completion\"\n created: number\n model: string\n choices: Array<ChoiceNonStreaming>\n system_fingerprint?: string\n usage?: {\n prompt_tokens: number\n completion_tokens: number\n total_tokens: number\n prompt_tokens_details?: {\n cached_tokens: number\n }\n }\n}\n\ninterface ResponseMessage {\n role: \"assistant\"\n content: string | null\n tool_calls?: Array<ToolCall>\n}\n\ninterface ChoiceNonStreaming {\n index: number\n message: ResponseMessage\n logprobs: object | null\n finish_reason: \"stop\" | \"length\" | \"tool_calls\" | \"content_filter\"\n}\n\n// Payload types\n\nexport interface ChatCompletionsPayload {\n messages: Array<Message>\n model: string\n temperature?: number | null\n top_p?: number | null\n max_tokens?: number | null\n stop?: string | Array<string> | null\n n?: number | null\n stream?: boolean | null\n\n frequency_penalty?: number | null\n presence_penalty?: number | null\n logit_bias?: Record<string, number> | null\n logprobs?: boolean | null\n response_format?: { type: \"json_object\" } | null\n seed?: number | null\n thinking?: { type: \"enabled\" | \"disabled\" } | null\n reasoning_effort?: \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\" | null\n tools?: Array<Tool> | null\n tool_choice?:\n | \"none\"\n | \"auto\"\n | \"required\"\n | { type: \"function\"; function: { name: string } }\n | null\n user?: string | null\n}\n\nexport interface Tool {\n type: \"function\"\n function: {\n name: string\n description?: string\n parameters: Record<string, unknown>\n }\n}\n\nexport interface Message {\n role: \"user\" | \"assistant\" | \"system\" | \"tool\" | \"developer\"\n content: string | Array<ContentPart> | null\n\n name?: string\n tool_calls?: Array<ToolCall>\n tool_call_id?: string\n}\n\nexport interface ToolCall {\n id: string\n type: \"function\"\n function: {\n name: string\n arguments: string\n }\n}\n\nexport type ContentPart = TextPart | ImagePart\n\nexport interface TextPart {\n type: \"text\"\n text: string\n}\n\nexport interface ImagePart {\n type: \"image_url\"\n image_url: {\n url: string\n detail?: \"low\" | \"high\" | \"auto\"\n }\n}\n","import type { Context } from \"hono\"\n\nimport consola from \"consola\"\nimport { streamSSE, type SSEMessage } from \"hono/streaming\"\n\nimport { awaitApproval } from \"~/lib/approval\"\nimport { checkRateLimit } from \"~/lib/rate-limit\"\nimport { state } from \"~/lib/state\"\nimport { getTokenCount } from \"~/lib/tokenizer\"\nimport { isNullish } from \"~/lib/utils\"\nimport {\n createChatCompletions,\n type ChatCompletionResponse,\n type ChatCompletionsPayload,\n} from \"~/services/copilot/create-chat-completions\"\n\nexport async function handleCompletion(c: Context) {\n await checkRateLimit(state)\n\n let payload = await c.req.json<ChatCompletionsPayload>()\n consola.debug(\"Request payload:\", JSON.stringify(payload).slice(-400))\n\n // Find the selected model\n const selectedModel = state.models?.data.find(\n (model) => model.id === payload.model,\n )\n\n // Calculate and display token count\n try {\n if (selectedModel) {\n const tokenCount = await getTokenCount(payload, selectedModel)\n consola.info(\"Current token count:\", tokenCount)\n } else {\n consola.warn(\"No model selected, skipping token count calculation\")\n }\n } catch (error) {\n consola.warn(\"Failed to calculate token count:\", error)\n }\n\n if (state.manualApprove) await awaitApproval()\n\n if (isNullish(payload.max_tokens)) {\n payload = {\n ...payload,\n max_tokens: selectedModel?.capabilities.limits.max_output_tokens,\n }\n consola.debug(\"Set max_tokens to:\", JSON.stringify(payload.max_tokens))\n }\n\n const response = await createChatCompletions(payload)\n\n if (isNonStreaming(response)) {\n consola.debug(\"Non-streaming response:\", JSON.stringify(response))\n return c.json(response)\n }\n\n consola.debug(\"Streaming response\")\n return streamSSE(c, async (stream) => {\n try {\n for await (const chunk of response) {\n consola.debug(\"Streaming chunk:\", JSON.stringify(chunk))\n await stream.writeSSE(chunk as SSEMessage)\n }\n } catch (error) {\n consola.error(\"Streaming error:\", error)\n await stream.writeSSE({\n data: JSON.stringify({\n error: {\n message:\n error instanceof Error ? error.message : \"Stream interrupted\",\n type: \"server_error\",\n },\n }),\n })\n }\n })\n}\n\nconst isNonStreaming = (\n response: Awaited<ReturnType<typeof createChatCompletions>>,\n): response is ChatCompletionResponse => Object.hasOwn(response, \"choices\")\n","import { Hono } from \"hono\"\n\nimport { forwardError } from \"~/lib/error\"\n\nimport { handleCompletion } from \"./handler\"\n\nexport const completionRoutes = new Hono()\n\ncompletionRoutes.post(\"/\", async (c) => {\n try {\n return await handleCompletion(c)\n } catch (error) {\n return await forwardError(c, error)\n }\n})\n","import { Hono } from \"hono\"\n\nimport { getRequestTraces } from \"~/lib/request-trace\"\n\nexport const debugRoutes = new Hono()\n\ndebugRoutes.get(\"/requests\", (c) =>\n c.json({\n object: \"list\",\n data: getRequestTraces(),\n }),\n)\n","import { copilotHeaders, copilotBaseUrl } from \"~/lib/api-config\"\nimport { copilotFetch } from \"~/lib/copilot-fetch\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport const createEmbeddings = async (payload: EmbeddingRequest) => {\n if (!state.copilotToken) throw new Error(\"Copilot token not found\")\n\n const response = await copilotFetch(`${copilotBaseUrl(state)}/embeddings`, {\n method: \"POST\",\n headers: copilotHeaders(state),\n body: JSON.stringify(payload),\n })\n\n if (!response.ok) throw new HTTPError(\"Failed to create embeddings\", response)\n\n return (await response.json()) as EmbeddingResponse\n}\n\nexport interface EmbeddingRequest {\n input: string | Array<string>\n model: string\n}\n\nexport interface Embedding {\n object: string\n embedding: Array<number>\n index: number\n}\n\nexport interface EmbeddingResponse {\n object: string\n data: Array<Embedding>\n model: string\n usage: {\n prompt_tokens: number\n total_tokens: number\n }\n}\n","import { Hono } from \"hono\"\n\nimport { forwardError } from \"~/lib/error\"\nimport {\n createEmbeddings,\n type EmbeddingRequest,\n} from \"~/services/copilot/create-embeddings\"\n\nexport const embeddingRoutes = new Hono()\n\nembeddingRoutes.post(\"/\", async (c) => {\n try {\n const payload = await c.req.json<EmbeddingRequest>()\n const response = await createEmbeddings(payload)\n\n return c.json(response)\n } catch (error) {\n return await forwardError(c, error)\n }\n})\n","import { type AnthropicResponse } from \"./anthropic-types\"\n\nexport function mapOpenAIStopReasonToAnthropic(\n finishReason: \"stop\" | \"length\" | \"tool_calls\" | \"content_filter\" | null,\n): AnthropicResponse[\"stop_reason\"] {\n if (finishReason === null) {\n return null\n }\n const stopReasonMap = {\n stop: \"end_turn\",\n length: \"max_tokens\",\n tool_calls: \"tool_use\",\n // Anthropic's spec uses \"refusal\" when the model declines to respond\n // due to safety/content policy. Map Copilot's \"content_filter\" to that\n // so Claude Code can surface a meaningful UX (it has a special render\n // path for refusal blocks).\n content_filter: \"refusal\",\n } as const\n return stopReasonMap[finishReason]\n}\n","import consola from \"consola\"\n\nimport { state } from \"~/lib/state\"\nimport {\n type ChatCompletionResponse,\n type ChatCompletionsPayload,\n type ContentPart,\n type Message,\n type TextPart,\n type Tool,\n type ToolCall,\n} from \"~/services/copilot/create-chat-completions\"\n\nimport {\n type AnthropicAssistantContentBlock,\n type AnthropicAssistantMessage,\n type AnthropicDocumentBlock,\n type AnthropicMessage,\n type AnthropicMessagesPayload,\n type AnthropicResponse,\n type AnthropicTextBlock,\n type AnthropicTool,\n type AnthropicToolResultBlock,\n type AnthropicToolUseBlock,\n type AnthropicUserContentBlock,\n type AnthropicUserMessage,\n} from \"./anthropic-types\"\nimport { mapOpenAIStopReasonToAnthropic } from \"./utils\"\n\n// Payload translation\n\nexport function translateToOpenAI(\n payload: AnthropicMessagesPayload,\n): ChatCompletionsPayload {\n const messages = stripTrailingAssistantPrefill(\n translateAnthropicMessagesToOpenAI(payload.messages, payload.system),\n )\n const model = translateModelName(payload.model)\n\n return {\n model,\n messages,\n max_tokens: payload.max_tokens,\n stop: payload.stop_sequences,\n stream: payload.stream,\n temperature: payload.temperature,\n top_p: payload.top_p,\n user: payload.metadata?.user_id,\n tools: translateAnthropicToolsToOpenAI(payload.tools),\n tool_choice: translateAnthropicToolChoiceToOpenAI(payload.tool_choice),\n thinking: translateAnthropicThinkingToOpenAI(payload.thinking),\n reasoning_effort: translateReasoningEffort(\n model,\n payload.output_config?.effort,\n ),\n }\n}\n\nfunction translateReasoningEffort(\n model: string,\n effort: ChatCompletionsPayload[\"reasoning_effort\"],\n): ChatCompletionsPayload[\"reasoning_effort\"] {\n if (!effort) return undefined\n\n const modelMetadata = state.models?.data.find((m) => m.id === model)\n if (!modelMetadata) return effort\n\n const supportedEfforts = modelMetadata.capabilities.supports.reasoning_effort\n if (!supportedEfforts?.length) return undefined\n\n return supportedEfforts.includes(effort) ? effort : undefined\n}\n\nfunction stripTrailingAssistantPrefill(\n messages: Array<Message>,\n): Array<Message> {\n let end = messages.length\n while (end > 0 && messages[end - 1].role === \"assistant\") {\n end--\n }\n\n if (end === messages.length) {\n return messages\n }\n\n consola.warn(\n `Stripped ${messages.length - end} trailing assistant prefill message(s) unsupported by Copilot chat completions`,\n )\n return messages.slice(0, end)\n}\n\n/**\n * Pre-process the Anthropic payload to handle async / proxy-side concerns\n * (currently: PDF document block extraction). Returns a new payload with\n * `document` blocks replaced by extracted-text blocks.\n *\n * Throws HTTPError (Anthropic-shaped) on PDF extraction failure.\n */\nexport async function preprocessAnthropicPayload(\n payload: AnthropicMessagesPayload,\n): Promise<AnthropicMessagesPayload> {\n // Fast path: no document blocks anywhere.\n const hasDocuments = payload.messages.some(\n (m) =>\n Array.isArray(m.content) && m.content.some((b) => b.type === \"document\"),\n )\n if (!hasDocuments) return payload\n\n const newMessages: Array<AnthropicMessage> = []\n for (const msg of payload.messages) {\n if (msg.role !== \"user\" || !Array.isArray(msg.content)) {\n newMessages.push(msg)\n continue\n }\n const newContent: Array<AnthropicUserContentBlock> = []\n for (const block of msg.content) {\n if (block.type === \"document\") {\n newContent.push(...(await extractDocumentBlock(block)))\n } else {\n newContent.push(block)\n }\n }\n newMessages.push({ role: \"user\", content: newContent })\n }\n return { ...payload, messages: newMessages }\n}\n\nasync function extractDocumentBlock(\n block: AnthropicDocumentBlock,\n): Promise<Array<AnthropicTextBlock>> {\n // Plain-text document source — just inline.\n if (block.source.type === \"text\") {\n return [\n {\n type: \"text\",\n text: documentHeader(block) + block.source.data,\n },\n ]\n }\n\n // URL document source — Copilot can't fetch URLs; skip with note.\n if (block.source.type === \"url\") {\n return [\n {\n type: \"text\",\n text:\n documentHeader(block)\n + `[document at ${block.source.url} not inlined — proxy does not fetch external URLs]`,\n },\n ]\n }\n\n // base64 PDF — extract via pdf-parse.\n const { extractPdfText } = await import(\"~/lib/pdf\")\n const extracted = await extractPdfText(block.source.data)\n return [\n {\n type: \"text\",\n text:\n documentHeader(block)\n + `[Extracted text from PDF (${extracted.pageCount} page${extracted.pageCount === 1 ? \"\" : \"s\"}${extracted.truncated ? \", truncated\" : \"\"}):]\\n\\n`\n + extracted.text,\n },\n ]\n}\n\nfunction documentHeader(block: AnthropicDocumentBlock): string {\n const parts: Array<string> = []\n if (block.title) parts.push(`Document: ${block.title}`)\n if (block.context) parts.push(`Context: ${block.context}`)\n return parts.length > 0 ? parts.join(\"\\n\") + \"\\n\\n\" : \"\"\n}\n\n// Static fallback mappings, used when state.models hasn't loaded or the\n// dynamic resolver can't find a family match.\nconst MODEL_NAME_FALLBACK: Record<string, string> = {\n haiku: \"claude-haiku-4.5\",\n sonnet: \"claude-sonnet-5\",\n opus: \"claude-opus-5\",\n}\n\nconst LEGACY_OPUS_FALLBACK = \"claude-opus-4.8\"\n\nconst CLAUDE_FAMILIES = [\"opus\", \"sonnet\", \"haiku\"] as const\ntype ClaudeFamily = (typeof CLAUDE_FAMILIES)[number]\n\n/** Detect which Claude family a requested model name belongs to.\n * Handles all the shapes Claude Code emits: bare (`claude-opus-4-5`),\n * dated (`claude-opus-4-5-20251101`), AWS (`claude-opus-4-5@20251101`),\n * Bedrock (`claude-opus-4-5-20251101-v1:0`), short alias (`opus`), etc. */\nfunction detectFamily(model: string): ClaudeFamily | null {\n const lower = model.toLowerCase()\n for (const family of CLAUDE_FAMILIES) {\n if (lower.includes(family)) return family\n }\n return null\n}\n\n/** Parse a numeric version tuple from a Copilot model id like\n * `claude-opus-4.7` → [4, 7] or `claude-sonnet-5` → [5, 0].\n * Returns [0, 0] if no version found. */\nfunction parseCopilotVersion(id: string): [number, number] {\n const match =\n /(?:opus|sonnet|haiku)-(\\d{1,2})(?:[.-](\\d{1,2}))?(?:$|[-_.])/i.exec(id)\n if (!match) return [0, 0]\n return [Number(match[1]), match[2] ? Number(match[2]) : 0]\n}\n\n/** Pick the best Copilot model in a family (and optionally a major version).\n * Highest version wins; among equal versions, prefer the `-1m` variant. */\nfunction pickLatestCopilotModel(\n family: ClaudeFamily,\n majorVersion?: number,\n): string | null {\n const candidates =\n state.models?.data.filter(\n (m) =>\n m.id.toLowerCase().includes(family)\n && (majorVersion === undefined\n || parseCopilotVersion(m.id)[0] === majorVersion),\n ) ?? []\n if (candidates.length === 0) return null\n\n let best = candidates[0]\n let bestVersion = parseCopilotVersion(best.id)\n let bestIs1m = best.id.includes(\"-1m\")\n\n for (const m of candidates.slice(1)) {\n const v = parseCopilotVersion(m.id)\n const is1m = m.id.includes(\"-1m\")\n const newer =\n v[0] > bestVersion[0]\n || (v[0] === bestVersion[0] && v[1] > bestVersion[1])\n const sameVersionPrefer1m =\n v[0] === bestVersion[0] && v[1] === bestVersion[1] && is1m && !bestIs1m\n if (newer || sameVersionPrefer1m) {\n best = m\n bestVersion = v\n bestIs1m = is1m\n }\n }\n return best.id\n}\n\nfunction parseRequestedClaudeVersion(\n model: string,\n family: ClaudeFamily,\n): [number, number] | null {\n const match = new RegExp(\n `${family}-(\\\\d{1,2})(?:[.-](\\\\d{1,2}))?(?:$|[-@:.])`,\n ).exec(model.toLowerCase())\n if (!match) return null\n return [Number(match[1]), match[2] ? Number(match[2]) : 0]\n}\n\nfunction isCopilotClaudeModelId(model: string): boolean {\n const match =\n /^claude-(?:opus|sonnet|haiku)-(\\d{1,2})(?:\\.(\\d{1,2})(?:-.+)?)?$/i.exec(\n model,\n )\n if (!match) return false\n return Boolean(match[2]) || Number(match[1]) >= 5\n}\n\nfunction unadvertisedClaudeModelFallback(\n model: string,\n family: ClaudeFamily,\n version: [number, number],\n): string | null {\n if (family === \"opus\" && version[0] === 5 && version[1] === 0) {\n return MODEL_NAME_FALLBACK.opus\n }\n if (family === \"opus\" && version[0] === 4) return LEGACY_OPUS_FALLBACK\n if (!state.models && isCopilotClaudeModelId(model)) return model\n return null\n}\n\n/** Pick the Copilot model matching the user-requested Claude version.\n * Among equal versions, prefer the `-1m` variant. */\nfunction pickMatchingCopilotModel(\n family: ClaudeFamily,\n version: [number, number],\n): string | null {\n const candidates =\n state.models?.data.filter((m) => {\n const candidateVersion = parseCopilotVersion(m.id)\n return (\n m.id.toLowerCase().includes(family)\n && candidateVersion[0] === version[0]\n && candidateVersion[1] === version[1]\n )\n }) ?? []\n if (candidates.length === 0) return null\n\n let best = candidates[0]\n let bestIs1m = best.id.includes(\"-1m\")\n\n for (const m of candidates.slice(1)) {\n const is1m = m.id.includes(\"-1m\")\n if (is1m && !bestIs1m) {\n best = m\n bestIs1m = is1m\n }\n }\n return best.id\n}\n\nfunction translateModelName(model: string): string {\n // Claude Code uses Anthropic model IDs (e.g. claude-opus-4-5-20251101);\n // Copilot uses version-only names (e.g. claude-opus-4.7). Preserve the\n // version Claude Code requested instead of upgrading a family to latest.\n\n const family = detectFamily(model)\n if (family) {\n const override = process.env[`COPILOT_OVERRIDE_${family.toUpperCase()}`]\n if (override) return override\n }\n\n // 1. Already a valid Copilot model id → pass through unchanged.\n if (state.models?.data.some((m) => m.id === model)) {\n return model\n }\n\n // 2. Exact short aliases use pinned defaults. These come from user/tool\n // settings, so do not reinterpret them as \"latest family member\".\n if (MODEL_NAME_FALLBACK[model]) {\n return MODEL_NAME_FALLBACK[model]\n }\n\n // 3. For explicit Claude version names, use the matching Copilot version\n // when available. If the input is already a Copilot-shaped dotted id,\n // preserve it even when state.models is not loaded yet.\n if (family) {\n const requestedVersion = parseRequestedClaudeVersion(model, family)\n if (requestedVersion) {\n const matching = pickMatchingCopilotModel(family, requestedVersion)\n if (matching) return matching\n const sameMajor = pickLatestCopilotModel(family, requestedVersion[0])\n if (sameMajor) return sameMajor\n const fallback = unadvertisedClaudeModelFallback(\n model,\n family,\n requestedVersion,\n )\n if (fallback) return fallback\n }\n\n const latest = pickLatestCopilotModel(family)\n if (latest) return latest\n\n return MODEL_NAME_FALLBACK[family]\n }\n\n return model\n}\n\nfunction translateAnthropicMessagesToOpenAI(\n anthropicMessages: Array<AnthropicMessage>,\n system: string | Array<AnthropicTextBlock> | undefined,\n): Array<Message> {\n const systemMessages = handleSystemPrompt(system)\n\n const otherMessages = anthropicMessages.flatMap((message) =>\n message.role === \"user\" ?\n handleUserMessage(message)\n : handleAssistantMessage(message),\n )\n\n return [...systemMessages, ...otherMessages]\n}\n\n// Reserved keywords that GitHub Copilot API blocks in system prompts\n// These will be completely removed from system prompts (case-insensitive)\nconst RESERVED_KEYWORD_PATTERNS = [\n /x-anthropic-billing-header/gi,\n /anthropic-billing-header/gi,\n /x-anthropic-/gi,\n]\n\nfunction sanitizeSystemPrompt(text: string): string {\n let sanitized = text\n for (const pattern of RESERVED_KEYWORD_PATTERNS) {\n sanitized = sanitized.replaceAll(pattern, \"\")\n }\n return sanitized\n}\n\nfunction handleSystemPrompt(\n system: string | Array<AnthropicTextBlock> | undefined,\n): Array<Message> {\n if (!system) {\n return []\n }\n\n if (typeof system === \"string\") {\n return [{ role: \"system\", content: sanitizeSystemPrompt(system) }]\n } else {\n const systemText = system.map((block) => block.text).join(\"\\n\\n\")\n return [{ role: \"system\", content: sanitizeSystemPrompt(systemText) }]\n }\n}\n\nfunction handleUserMessage(message: AnthropicUserMessage): Array<Message> {\n const newMessages: Array<Message> = []\n\n if (Array.isArray(message.content)) {\n const toolResultBlocks = message.content.filter(\n (block): block is AnthropicToolResultBlock =>\n block.type === \"tool_result\",\n )\n const otherBlocks = message.content.filter(\n (block) => block.type !== \"tool_result\",\n )\n\n // Tool results must come first to maintain protocol: tool_use -> tool_result -> user\n for (const block of toolResultBlocks) {\n newMessages.push({\n role: \"tool\",\n tool_call_id: block.tool_use_id,\n content: mapContent(block.content),\n })\n }\n\n if (otherBlocks.length > 0) {\n newMessages.push({\n role: \"user\",\n content: mapContent(otherBlocks),\n })\n }\n } else {\n newMessages.push({\n role: \"user\",\n content: mapContent(message.content),\n })\n }\n\n return newMessages\n}\n\nfunction handleAssistantMessage(\n message: AnthropicAssistantMessage,\n): Array<Message> {\n if (!Array.isArray(message.content)) {\n return [\n {\n role: \"assistant\",\n content: mapContent(message.content),\n },\n ]\n }\n\n const toolUseBlocks = message.content.filter(\n (block): block is AnthropicToolUseBlock => block.type === \"tool_use\",\n )\n\n const textBlocks = message.content.filter(\n (block): block is AnthropicTextBlock => block.type === \"text\",\n )\n\n // Thinking blocks have signed-by-Anthropic semantics that Copilot can't\n // verify or replay. Promoting their content to plain text would destroy\n // the assistant turn semantics (the model would treat its own private\n // reasoning as visible text). Drop them cleanly on the request side.\n const allTextContent = textBlocks.map((b) => b.text).join(\"\\n\\n\")\n\n return toolUseBlocks.length > 0 ?\n [\n {\n role: \"assistant\",\n content: allTextContent || null,\n tool_calls: toolUseBlocks.map((toolUse) => ({\n id: toolUse.id,\n type: \"function\",\n function: {\n name: toolUse.name,\n arguments: JSON.stringify(toolUse.input),\n },\n })),\n },\n ]\n : [\n {\n role: \"assistant\",\n content: mapContent(message.content),\n },\n ]\n}\n\nfunction mapContent(\n content:\n | string\n | Array<AnthropicUserContentBlock | AnthropicAssistantContentBlock>,\n): string | Array<ContentPart> | null {\n if (typeof content === \"string\") {\n return content\n }\n if (!Array.isArray(content)) {\n return null\n }\n\n const hasImage = content.some((block) => block.type === \"image\")\n if (!hasImage) {\n return content\n .filter((block): block is AnthropicTextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\\n\\n\")\n }\n\n const contentParts: Array<ContentPart> = []\n for (const block of content) {\n switch (block.type) {\n case \"text\": {\n contentParts.push({ type: \"text\", text: block.text })\n\n break\n }\n // Thinking blocks dropped — see handleAssistantMessage rationale.\n case \"image\": {\n contentParts.push({\n type: \"image_url\",\n image_url: {\n url: `data:${block.source.media_type};base64,${block.source.data}`,\n },\n })\n\n break\n }\n // No default\n }\n }\n return contentParts\n}\n\nfunction translateAnthropicToolsToOpenAI(\n anthropicTools: Array<AnthropicTool> | undefined,\n): Array<Tool> | undefined {\n if (!anthropicTools) {\n return undefined\n }\n return anthropicTools.map((tool) => ({\n type: \"function\",\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.input_schema,\n },\n }))\n}\n\n/**\n * Map Anthropic `thinking` to the Copilot-compatible format.\n * Anthropic uses `'enabled'` and `'adaptive'`; Copilot only accepts\n * `'enabled'` or `'disabled'`. Both `'enabled'` and `'adaptive'` map to\n * `'enabled'` since they both request extended thinking.\n */\nfunction translateAnthropicThinkingToOpenAI(\n thinking: AnthropicMessagesPayload[\"thinking\"],\n): ChatCompletionsPayload[\"thinking\"] {\n if (!thinking) return undefined\n // Copilot only supports 'enabled' | 'disabled' — map 'adaptive' to 'enabled'\n return { type: \"enabled\" }\n}\n\nfunction translateAnthropicToolChoiceToOpenAI(\n anthropicToolChoice: AnthropicMessagesPayload[\"tool_choice\"],\n): ChatCompletionsPayload[\"tool_choice\"] {\n if (!anthropicToolChoice) {\n return undefined\n }\n\n switch (anthropicToolChoice.type) {\n case \"auto\": {\n return \"auto\"\n }\n case \"any\": {\n return \"required\"\n }\n case \"tool\": {\n if (anthropicToolChoice.name) {\n return {\n type: \"function\",\n function: { name: anthropicToolChoice.name },\n }\n }\n return undefined\n }\n case \"none\": {\n return \"none\"\n }\n default: {\n return undefined\n }\n }\n}\n\n// Response translation\n\n// eslint-disable-next-line complexity\nexport function translateToAnthropic(\n response: ChatCompletionResponse,\n /** Original client-requested model id, echoed back so Claude Code displays\n * the model the user asked for rather than Copilot's internal id. */\n clientModel?: string,\n): AnthropicResponse {\n // Merge content from all choices\n const allTextBlocks: Array<AnthropicTextBlock> = []\n const allToolUseBlocks: Array<AnthropicToolUseBlock> = []\n let stopReason: \"stop\" | \"length\" | \"tool_calls\" | \"content_filter\" | null =\n null // default\n stopReason = response.choices[0]?.finish_reason ?? stopReason\n\n // Process all choices to extract text and tool use blocks\n for (const choice of response.choices) {\n const textBlocks = getAnthropicTextBlocks(choice.message.content)\n const toolUseBlocks = getAnthropicToolUseBlocks(choice.message.tool_calls)\n\n allTextBlocks.push(...textBlocks)\n allToolUseBlocks.push(...toolUseBlocks)\n\n // Use the finish_reason from the first choice, or prioritize tool_calls\n if (choice.finish_reason === \"tool_calls\" || stopReason === \"stop\") {\n stopReason = choice.finish_reason\n }\n }\n\n // Note: GitHub Copilot doesn't generate thinking blocks, so we don't include them in responses\n\n return {\n id: response.id,\n type: \"message\",\n role: \"assistant\",\n model: clientModel ?? response.model,\n content: [...allTextBlocks, ...allToolUseBlocks],\n stop_reason: mapOpenAIStopReasonToAnthropic(stopReason),\n stop_sequence: null,\n usage: {\n input_tokens:\n (response.usage?.prompt_tokens ?? 0)\n - (response.usage?.prompt_tokens_details?.cached_tokens ?? 0),\n output_tokens: response.usage?.completion_tokens ?? 0,\n ...(response.usage?.prompt_tokens_details?.cached_tokens\n !== undefined && {\n cache_read_input_tokens:\n response.usage.prompt_tokens_details.cached_tokens,\n }),\n },\n }\n}\n\nfunction getAnthropicTextBlocks(\n messageContent: Message[\"content\"],\n): Array<AnthropicTextBlock> {\n if (typeof messageContent === \"string\") {\n return [{ type: \"text\", text: messageContent }]\n }\n\n if (Array.isArray(messageContent)) {\n return messageContent\n .filter((part): part is TextPart => part.type === \"text\")\n .map((part) => ({ type: \"text\", text: part.text }))\n }\n\n return []\n}\n\nfunction getAnthropicToolUseBlocks(\n toolCalls: Array<ToolCall> | undefined,\n): Array<AnthropicToolUseBlock> {\n if (!toolCalls) {\n return []\n }\n return toolCalls.map((toolCall) => {\n let input: Record<string, unknown> = {}\n try {\n const parsed = JSON.parse(toolCall.function.arguments) as unknown\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n input = parsed as Record<string, unknown>\n }\n } catch {\n // Copilot can emit malformed JSON for tool args (especially mid-stream\n // truncation or partial outputs). Fall back to an empty object so the\n // assistant turn is at least well-formed; Claude Code will surface the\n // missing args via tool execution failure rather than a hard crash.\n }\n return {\n type: \"tool_use\",\n id: toolCall.id,\n name: toolCall.function.name,\n input,\n }\n })\n}\n","import type { Context } from \"hono\"\n\nimport consola from \"consola\"\n\nimport { state } from \"~/lib/state\"\nimport { getTokenCount } from \"~/lib/tokenizer\"\n\nimport { type AnthropicMessagesPayload } from \"./anthropic-types\"\nimport { translateToOpenAI } from \"./non-stream-translation\"\n\n/**\n * Handles token counting for Anthropic messages\n */\nexport async function handleCountTokens(c: Context) {\n try {\n const anthropicBeta = c.req.header(\"anthropic-beta\")\n\n const anthropicPayload = await c.req.json<AnthropicMessagesPayload>()\n\n const openAIPayload = translateToOpenAI(anthropicPayload)\n\n const selectedModel = state.models?.data.find(\n (model) => model.id === openAIPayload.model,\n )\n\n if (!selectedModel) {\n consola.warn(\"Model not found, returning default token count\")\n return c.json(\n {\n type: \"error\",\n error: {\n type: \"not_found_error\",\n message: `Model '${openAIPayload.model}' not found`,\n },\n },\n 404,\n )\n }\n\n const tokenCount = await getTokenCount(openAIPayload, selectedModel)\n\n if (anthropicPayload.tools && anthropicPayload.tools.length > 0) {\n let mcpToolExist = false\n if (anthropicBeta?.startsWith(\"claude-code\")) {\n mcpToolExist = anthropicPayload.tools.some((tool) =>\n tool.name.startsWith(\"mcp__\"),\n )\n }\n if (!mcpToolExist) {\n if (anthropicPayload.model.startsWith(\"claude\")) {\n // https://docs.anthropic.com/en/docs/agents-and-tools/tool-use/overview#pricing\n tokenCount.input = tokenCount.input + 346\n } else if (anthropicPayload.model.startsWith(\"grok\")) {\n tokenCount.input = tokenCount.input + 480\n }\n }\n }\n\n // Anthropic's /v1/messages/count_tokens returns INPUT tokens only\n // (https://docs.anthropic.com/en/api/messages-count-tokens). Including\n // an estimated output count would mislead Claude Code's auto-compact\n // accounting (autoCompact.ts uses this to gate when to compact).\n let finalTokenCount = tokenCount.input\n if (anthropicPayload.model.startsWith(\"claude\")) {\n finalTokenCount = Math.round(finalTokenCount * 1.15)\n } else if (anthropicPayload.model.startsWith(\"grok\")) {\n finalTokenCount = Math.round(finalTokenCount * 1.03)\n }\n\n consola.info(\"Token count:\", finalTokenCount)\n\n return c.json({\n input_tokens: finalTokenCount,\n })\n } catch (error) {\n consola.error(\"Error counting tokens:\", error)\n return c.json(\n {\n type: \"error\" as const,\n error: {\n type: \"api_error\",\n message:\n error instanceof Error ? error.message : \"Failed to count tokens\",\n },\n },\n 500,\n )\n }\n}\n","/**\n * Context management for Copilot API payloads.\n *\n * Enforces TWO ceilings, whichever is smaller for a given model:\n * 1. Hard byte ceiling (~5 MB) — Copilot's Azure Front Door cliff\n * 2. Token-derived byte ceiling — model's max_prompt_tokens × 3.5 chars\n *\n * The token-derived ceiling prevents `model_max_prompt_tokens_exceeded`\n * rejections on models with stricter caps (e.g. opus-4.7 = 168K tokens),\n * which a 1 MB payload can easily breach.\n *\n * Fast path (~99% of Claude Code requests): byte size check → return.\n * Slow path: prune tool outputs → strip images → drop oldest messages →\n * byte-based final backstop.\n */\n\nimport consola from \"consola\"\n\nimport type {\n ChatCompletionsPayload,\n Message,\n} from \"~/services/copilot/create-chat-completions\"\nimport type { Model } from \"~/services/copilot/get-models\"\n\nimport { getKnownModelPromptLimit } from \"./model-limits\"\n\n// ── Configuration ───────────────────────────────────────────────────────────\n\n/**\n * Hard byte ceiling for Copilot. Empirically the cliff is at ~5.4 MB for\n * Azure Front Door; we set the proxy ceiling at 5,000,000 to leave ~400 KB\n * headroom for headers, tool definitions, and downstream JSON framing.\n *\n * Why 5 MB and not 2.5 MB (v0.9.1-v0.10.2 value):\n * The 2.5 MB ceiling fired aggressively on normal Claude Code sessions\n * (typical 6 MB → 0.9 MB prune per turn). The pruned payload causes\n * Copilot to report a small prompt_tokens count, which Claude Code uses\n * to compute its local context estimate via tokenCountWithEstimation.\n * Because Claude Code's local message array still holds the full unpruned\n * conversation, its local count climbs by the rough delta each turn until\n * it hits the blocking limit (window - 3K) and Claude Code emits\n * \"Context limit reached\" via the preempt path in query.ts:641 — without\n * ever calling our proxy, so auto-compact never fires.\n *\n * Raising the ceiling to 5 MB lets normal payloads pass through unmodified.\n * Copilot then returns honest prompt_tokens, and Claude Code's auto-compact\n * threshold (window - 13K) trips on schedule. Pruning still kicks in as a\n * last-resort safety net for genuinely oversized payloads (≥5 MB), which\n * is rare in normal Claude Code use.\n *\n * This restores the v0.8.0 behavior (no proxy-side context manager) which\n * the user reports worked reliably for auto-compact.\n */\nconst MAX_PAYLOAD_BYTES = 5_000_000\n\n/** Conservative ceiling for custom Claude Code aliases / effort modes that do\n * not appear in Copilot model metadata. Copilot can hang for minutes in the\n * 2.5-5 MB range, so unknown models stay below that dead zone. */\nexport const UNKNOWN_MODEL_PAYLOAD_BYTES = 1_900_000\n\n/** Minimum protected byte budget for recent tool outputs before pruning. */\nconst PRUNE_PROTECT_BYTES = 200_000\n\n/** Don't prune unless we'd reclaim at least this many bytes. */\nconst PRUNE_MINIMUM_BYTES = 50_000\n\n/** Per-message hard cap for tool output content even when \"recent\". A single\n * giant Read of a 1MB file shouldn't blow the budget by itself. */\nconst TOOL_OUTPUT_HARD_CAP = 100_000\n\n/**\n * Empirical chars/token ratio for Claude Code payloads. Used to derive a\n * byte ceiling from the model's `max_prompt_tokens` without paying for a\n * real tokenizer pass. Real ratios on typical code-heavy Claude Code traffic\n * sit in the 3.0-3.8 range (English prose ~4.0, dense code ~3.0). We pick\n * 3.5 as a slightly conservative midpoint — it errs toward pruning a touch\n * earlier rather than letting Copilot reject. Cost: one multiplication per\n * request vs. ~300-1000ms for a real tokenizer pass on the hot path.\n */\nconst CHARS_PER_TOKEN_ESTIMATE = 3.5\n\n/**\n * Reserve this many tokens under max_prompt_tokens for max_tokens output and\n * Copilot framing overhead (function-call encoding, message-wrapping tokens).\n * Copilot's reported prompt_token count typically runs a few thousand higher\n * than ours due to encoding differences; this buffer absorbs that skew.\n */\nconst TOKEN_RESERVE = 8_000\n\nconst PRUNED_PLACEHOLDER = \"[content pruned — tool output was too old]\"\nconst TOOL_TRUNCATED_SUFFIX = \"\\n\\n[…tool output truncated to fit context]\"\nconst IMAGE_STRIPPED_PLACEHOLDER = \"[image removed to save context]\"\n\n// ── Public API ──────────────────────────────────────────────────────────────\n\n/**\n * Compute the effective byte ceiling for a given model.\n *\n * The proxy enforces TWO ceilings and uses whichever is smaller:\n *\n * 1. The hard byte ceiling MAX_PAYLOAD_BYTES (~5 MB) — Copilot's Azure\n * Front Door rejects payloads >5.4 MB regardless of token count.\n *\n * 2. A token-derived byte ceiling — Copilot ALSO rejects when the\n * tokenized prompt exceeds the model's `max_prompt_tokens` cap, which\n * varies per model (e.g. claude-opus-4.7 = 168K, others = 200K). A 1 MB\n * payload on opus-4.7 can easily contain 169K tokens and get rejected\n * with `model_max_prompt_tokens_exceeded`. To prevent that, we estimate\n * a byte budget from the token cap using a fixed chars/token ratio.\n *\n * If `max_prompt_tokens` is missing from model metadata, we fall back to\n * MAX_PAYLOAD_BYTES alone (existing behavior, no regression).\n */\nfunction computeEffectiveCeiling(model: Model): number {\n const maxPromptTokens = getKnownModelPromptLimit(\n model.id,\n model.capabilities.limits,\n )\n if (!maxPromptTokens) return MAX_PAYLOAD_BYTES\n const tokenDerivedBytes = Math.floor(\n (maxPromptTokens - TOKEN_RESERVE) * CHARS_PER_TOKEN_ESTIMATE,\n )\n return Math.min(MAX_PAYLOAD_BYTES, tokenDerivedBytes)\n}\n\n/**\n * Fit a payload within the model's effective byte ceiling. Returns the input\n * payload unmodified in the common case (cheap byte size check).\n */\nexport function fitContext(\n payload: ChatCompletionsPayload,\n model: Model,\n): ChatCompletionsPayload {\n const ceiling = computeEffectiveCeiling(model)\n return fitPayloadToCeiling(payload, ceiling)\n}\n\nexport function fitUnknownModelContext(\n payload: ChatCompletionsPayload,\n): ChatCompletionsPayload {\n return fitPayloadToCeiling(payload, UNKNOWN_MODEL_PAYLOAD_BYTES)\n}\n\nexport function isPayloadOverUnknownModelCeiling(\n payload: ChatCompletionsPayload,\n): boolean {\n return estimatePayloadBytes(payload) > UNKNOWN_MODEL_PAYLOAD_BYTES\n}\n\nfunction fitPayloadToCeiling(\n payload: ChatCompletionsPayload,\n ceiling: number,\n): ChatCompletionsPayload {\n const initialBytes = estimatePayloadBytes(payload)\n\n // Fast path: well under the effective ceiling — just send.\n if (initialBytes <= ceiling) {\n return payload\n }\n\n consola.info(\n `Context fit: payload ${formatBytes(initialBytes)} exceeds ${formatBytes(ceiling)} ceiling — reducing`,\n )\n\n // Slow path: progressive byte-based reduction.\n let current = pruneToolOutputs(payload)\n let bytes = estimatePayloadBytes(current)\n if (bytes <= ceiling) {\n consola.info(\n `Context fit: ${formatBytes(initialBytes)} → ${formatBytes(bytes)} after tool pruning`,\n )\n return current\n }\n\n current = stripOldImages(current)\n bytes = estimatePayloadBytes(current)\n if (bytes <= ceiling) {\n consola.info(\n `Context fit: ${formatBytes(initialBytes)} → ${formatBytes(bytes)} after stripping old images`,\n )\n return current\n }\n\n // Aggressive: strip ALL images regardless of age.\n current = stripAllImages(current)\n bytes = estimatePayloadBytes(current)\n if (bytes <= ceiling) {\n consola.warn(\n `Context fit: ${formatBytes(initialBytes)} → ${formatBytes(bytes)} after stripping ALL images`,\n )\n return current\n }\n\n // Truncate any oversized tool outputs that survived pruning (e.g. recent ones).\n current = truncateOversizedToolOutputs(current)\n bytes = estimatePayloadBytes(current)\n if (bytes <= ceiling) {\n consola.warn(\n `Context fit: ${formatBytes(initialBytes)} → ${formatBytes(bytes)} after truncating oversized tool outputs`,\n )\n return current\n }\n\n current = dropOldMessages(current, ceiling)\n bytes = estimatePayloadBytes(current)\n consola.warn(\n `Context fit: ${formatBytes(initialBytes)} → ${formatBytes(bytes)} after dropping old messages`,\n )\n return current\n}\n\nfunction formatBytes(b: number): string {\n if (b < 1024) return `${b}B`\n if (b < 1024 * 1024) return `${(b / 1024).toFixed(1)}KB`\n return `${(b / 1024 / 1024).toFixed(2)}MB`\n}\n\n// ── Byte estimation ─────────────────────────────────────────────────────────\n\n/**\n * Cheap byte-size estimate for a payload. Uses content lengths directly\n * instead of JSON.stringify, which is ~10x faster on large payloads. The\n * estimate is a lower bound (ignores JSON framing overhead) but accurate\n * to within a few percent for realistic Claude Code payloads.\n */\nfunction estimatePayloadBytes(payload: ChatCompletionsPayload): number {\n let bytes = 200 // baseline for top-level JSON framing\n for (const msg of payload.messages) {\n bytes += estimateMessageBytes(msg)\n }\n if (payload.tools) {\n for (const tool of payload.tools) {\n // Tool definitions are dense JSON; conservatively double-count via stringify.\n bytes += JSON.stringify(tool).length + 20\n }\n }\n return bytes\n}\n\nfunction estimateMessageBytes(msg: Message): number {\n let bytes = 40 // role + framing\n if (typeof msg.content === \"string\") {\n bytes += msg.content.length\n } else if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n if (\"text\" in part && typeof part.text === \"string\") {\n bytes += part.text.length + 20\n } else if (\"type\" in part && part.type === \"image_url\") {\n bytes += part.image_url.url.length + 40\n }\n }\n }\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n bytes += tc.function.name.length + tc.function.arguments.length + 80\n }\n }\n if (msg.tool_call_id) bytes += msg.tool_call_id.length + 20\n return bytes\n}\n\n// ── Reduction step 1: prune old tool outputs ────────────────────────────────\n\nfunction pruneToolOutputs(\n payload: ChatCompletionsPayload,\n): ChatCompletionsPayload {\n const messages = payload.messages\n let protectedBytes = 0\n let prunedBytes = 0\n const prunedIndices: Array<number> = []\n\n // Walk newest → oldest, protecting recent tool outputs.\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.role !== \"tool\") continue\n\n const msgBytes = estimateMessageBytes(msg)\n if (protectedBytes < PRUNE_PROTECT_BYTES) {\n protectedBytes += msgBytes\n } else {\n prunedBytes += msgBytes\n prunedIndices.push(i)\n }\n }\n\n if (prunedBytes < PRUNE_MINIMUM_BYTES) {\n return payload\n }\n\n const newMessages = [...messages]\n for (const idx of prunedIndices) {\n newMessages[idx] = { ...newMessages[idx], content: PRUNED_PLACEHOLDER }\n }\n\n consola.info(\n `Pruned ${prunedIndices.length} old tool outputs (~${prunedBytes} bytes reclaimed)`,\n )\n return { ...payload, messages: newMessages }\n}\n\n// ── Reduction step 2: strip base64 images from older messages ───────────────\n\nfunction stripOldImages(\n payload: ChatCompletionsPayload,\n): ChatCompletionsPayload {\n const messages = payload.messages\n const newMessages = [...messages]\n let strippedCount = 0\n let strippedBytes = 0\n\n // Keep images only in the last 4 messages (recent visual context).\n const protectFromEnd = 4\n const protectBoundary = Math.max(0, messages.length - protectFromEnd)\n\n for (let i = 0; i < protectBoundary; i++) {\n const msg = messages[i]\n if (!Array.isArray(msg.content)) continue\n\n const beforeStripped = strippedCount\n const newContent = msg.content.map((part) => {\n if (\"type\" in part && part.type === \"image_url\") {\n strippedCount++\n strippedBytes += part.image_url.url.length\n return { type: \"text\" as const, text: IMAGE_STRIPPED_PLACEHOLDER }\n }\n return part\n })\n\n if (strippedCount > beforeStripped) {\n newMessages[i] = { ...msg, content: newContent }\n }\n }\n\n if (strippedCount > 0) {\n consola.info(\n `Stripped ${strippedCount} base64 images (~${strippedBytes} bytes reclaimed)`,\n )\n }\n return { ...payload, messages: newMessages }\n}\n\n// ── Reduction step 2b: strip ALL images regardless of recency ───────────────\n\nfunction stripAllImages(\n payload: ChatCompletionsPayload,\n): ChatCompletionsPayload {\n let strippedCount = 0\n let strippedBytes = 0\n const newMessages = payload.messages.map((msg) => {\n if (!Array.isArray(msg.content)) return msg\n const before = strippedCount\n const newContent = msg.content.map((part) => {\n if (\"type\" in part && part.type === \"image_url\") {\n strippedCount++\n strippedBytes += part.image_url.url.length\n return { type: \"text\" as const, text: IMAGE_STRIPPED_PLACEHOLDER }\n }\n return part\n })\n return strippedCount > before ? { ...msg, content: newContent } : msg\n })\n if (strippedCount > 0) {\n consola.warn(\n `Stripped ${strippedCount} ALL images (~${strippedBytes} bytes reclaimed)`,\n )\n }\n return { ...payload, messages: newMessages }\n}\n\n// ── Reduction step 2c: truncate oversized tool outputs ──────────────────────\n\nfunction truncateOversizedToolOutputs(\n payload: ChatCompletionsPayload,\n): ChatCompletionsPayload {\n let truncatedCount = 0\n let reclaimedBytes = 0\n const newMessages = payload.messages.map((msg) => {\n if (msg.role !== \"tool\") return msg\n if (typeof msg.content !== \"string\") return msg\n if (msg.content.length <= TOOL_OUTPUT_HARD_CAP) return msg\n const before = msg.content.length\n const truncated =\n msg.content.slice(0, TOOL_OUTPUT_HARD_CAP) + TOOL_TRUNCATED_SUFFIX\n truncatedCount++\n reclaimedBytes += before - truncated.length\n return { ...msg, content: truncated }\n })\n if (truncatedCount > 0) {\n consola.warn(\n `Truncated ${truncatedCount} oversized tool outputs (~${reclaimedBytes} bytes reclaimed)`,\n )\n }\n return { ...payload, messages: newMessages }\n}\n\n// ── Reduction step 3: drop oldest conversation messages ─────────────────────\n\nfunction dropOldMessages(\n payload: ChatCompletionsPayload,\n ceiling: number,\n): ChatCompletionsPayload {\n const systemMessages = payload.messages.filter(\n (m) => m.role === \"system\" || m.role === \"developer\",\n )\n const conversationMessages = payload.messages.filter(\n (m) => m.role !== \"system\" && m.role !== \"developer\",\n )\n\n // Compute reusable system byte total.\n let baseBytes = 200\n for (const m of systemMessages) baseBytes += estimateMessageBytes(m)\n if (payload.tools) {\n for (const tool of payload.tools) {\n baseBytes += JSON.stringify(tool).length + 20\n }\n }\n\n // Compute per-message byte sizes once for the conversation tail.\n const convBytes = conversationMessages.map((m) => estimateMessageBytes(m))\n let totalBytes = baseBytes + convBytes.reduce((a, b) => a + b, 0)\n\n let dropped = 0\n while (conversationMessages.length > 2 && totalBytes > ceiling) {\n const removedBytes = convBytes.shift() ?? 0\n conversationMessages.shift()\n totalBytes -= removedBytes\n dropped++\n }\n\n // Avoid stranding a tool_result whose corresponding tool_use was just\n // dropped. Copilot/Vertex rejects with \"unexpected tool_use_id ... Each\n // tool_result block must have a corresponding tool_use block in the\n // previous message.\" Keep dropping from the front while the new head is\n // either a `tool` role message OR a user/assistant message whose content\n // begins with a tool_result/tool_use_id reference. Cheapest heuristic:\n // drop while head.role === \"tool\".\n let orphansDropped = 0\n while (\n conversationMessages.length > 2\n && conversationMessages[0].role === \"tool\"\n ) {\n const removedBytes = convBytes.shift() ?? 0\n conversationMessages.shift()\n totalBytes -= removedBytes\n orphansDropped++\n }\n\n if (dropped > 0 || orphansDropped > 0) {\n consola.warn(\n `Dropped ${dropped} oldest conversation messages + ${orphansDropped} orphaned tool results (~${totalBytes} bytes remaining, limit ${ceiling})`,\n )\n }\n\n if (totalBytes > ceiling) {\n consola.error(\n `Payload still over ${ceiling}B after all reductions (${totalBytes}B). Forwarding anyway — Copilot will likely return 413 → 400 prompt-too-long for reactive compaction.`,\n )\n }\n\n return {\n ...payload,\n messages: [...systemMessages, ...conversationMessages],\n }\n}\n","import consola from \"consola\"\nimport { createHash } from \"node:crypto\"\n\nimport type {\n ResponsesApiRequest,\n ResponsesInputItem,\n} from \"~/routes/responses/types\"\n\nconst ENCRYPTED_OUTPUT_ERROR =\n \"Encrypted function output content could not be decrypted or decoded.\"\nconst MAX_REJECTED_ENCRYPTED_OUTPUTS = 256\nconst REJECTED_ENCRYPTED_OUTPUT_TTL_MS = 30 * 60 * 1000\nconst STREAM_FAILURE_INSPECTION_TIMEOUT_MS = 100\nconst MAX_STREAM_FAILURE_INSPECTION_BYTES = 64 * 1024\nconst MAX_STREAM_FAILURE_INSPECTION_EVENTS = 8\nconst rejectedEncryptedOutputs = new Map<string, number>()\n\nexport function createEncryptedOutputScope(\n url: string,\n model: string,\n accountIdentity: string,\n): string {\n return fingerprintEncryptedOutput(`${url}\\0${model}\\0${accountIdentity}`)\n}\n\nexport function isEncryptedOutputError(\n response: Response,\n errorBody: string,\n): boolean {\n return response.status === 400 && errorBody.includes(ENCRYPTED_OUTPUT_ERROR)\n}\n\nexport async function isImmediateEncryptedOutputStreamFailure(\n response: Response,\n): Promise<boolean> {\n if (!response.body) return false\n if (!response.headers.get(\"content-type\")?.includes(\"text/event-stream\")) {\n return false\n }\n\n const reader = response.clone().body?.getReader()\n if (!reader) return false\n\n try {\n return await readImmediateEncryptedOutputStreamFailure(reader)\n } catch (error) {\n consola.debug(\"Failed to inspect Copilot Responses stream:\", error)\n } finally {\n void reader.cancel().catch(() => undefined)\n }\n\n return false\n}\n\nexport function stripEncryptedOutputParts(\n payload: ResponsesApiRequest,\n selected?: (fingerprint: string) => boolean,\n): {\n count: number\n fingerprints: Array<string>\n payload: ResponsesApiRequest\n} {\n if (typeof payload.input === \"string\") {\n return { count: 0, fingerprints: [], payload }\n }\n\n let count = 0\n const fingerprints: Array<string> = []\n let input: Array<ResponsesInputItem> | undefined\n for (const [index, item] of payload.input.entries()) {\n if (!Array.isArray(item.content)) continue\n const hasReadableText = item.content.some(\n (part) =>\n (part.type === \"input_text\" || part.type === \"output_text\")\n && typeof part.text === \"string\",\n )\n if (!hasReadableText) continue\n\n const content = item.content.filter((part) => {\n if (\n part.type !== \"encrypted_content\"\n || typeof part.encrypted_content !== \"string\"\n ) {\n return true\n }\n const fingerprint = fingerprintEncryptedOutput(part.encrypted_content)\n if (selected && !selected(fingerprint)) return true\n fingerprints.push(fingerprint)\n return false\n })\n if (content.length === item.content.length || content.length === 0) continue\n\n count += item.content.length - content.length\n input ??= [...payload.input]\n input[index] = { ...item, content }\n }\n\n return {\n count,\n fingerprints,\n payload: input ? { ...payload, input } : payload,\n }\n}\n\nexport function isRejectedEncryptedOutput(\n scope: string,\n fingerprint: string,\n): boolean {\n const key = rejectedEncryptedOutputKey(scope, fingerprint)\n const expiresAt = rejectedEncryptedOutputs.get(key)\n if (!expiresAt) return false\n if (expiresAt > Date.now()) return true\n\n rejectedEncryptedOutputs.delete(key)\n return false\n}\n\nexport function rememberRejectedEncryptedOutputs(\n scope: string,\n fingerprints: Array<string>,\n): void {\n if (fingerprints.length !== 1) return\n\n const key = rejectedEncryptedOutputKey(scope, fingerprints[0])\n rejectedEncryptedOutputs.delete(key)\n rejectedEncryptedOutputs.set(\n key,\n Date.now() + REJECTED_ENCRYPTED_OUTPUT_TTL_MS,\n )\n while (rejectedEncryptedOutputs.size > MAX_REJECTED_ENCRYPTED_OUTPUTS) {\n const oldest = rejectedEncryptedOutputs.keys().next().value\n if (!oldest) break\n rejectedEncryptedOutputs.delete(oldest)\n }\n}\n\nasync function readImmediateEncryptedOutputStreamFailure(\n reader: Pick<ReadableStreamDefaultReader<Uint8Array>, \"read\">,\n): Promise<boolean> {\n const decoder = new TextDecoder()\n const deadline = performance.now() + STREAM_FAILURE_INSPECTION_TIMEOUT_MS\n let buffer = \"\"\n let bytesRead = 0\n let eventsRead = 0\n\n while (true) {\n const remainingMs = deadline - performance.now()\n if (remainingMs <= 0) return false\n\n const result = await readStreamChunkWithTimeout(reader, remainingMs)\n if (!result) return false\n\n const { done, value } = result\n if (value) {\n bytesRead += value.byteLength\n if (bytesRead > MAX_STREAM_FAILURE_INSPECTION_BYTES) return false\n buffer += decoder.decode(value, { stream: !done })\n } else if (done) {\n buffer += decoder.decode()\n }\n\n while (true) {\n const next = takeNextSseBlock(buffer)\n if (!next) break\n buffer = next.remaining\n const event = parseResponseEvent(next.block)\n if (!event) continue\n\n eventsRead++\n if (isEncryptedOutputFailureEvent(event)) return true\n if (!isProvisionalResponseEvent(event)) return false\n if (eventsRead >= MAX_STREAM_FAILURE_INSPECTION_EVENTS) return false\n }\n\n if (done) {\n const event = parseResponseEvent(buffer)\n return event ? isEncryptedOutputFailureEvent(event) : false\n }\n }\n}\n\nasync function readStreamChunkWithTimeout(\n reader: Pick<ReadableStreamDefaultReader<Uint8Array>, \"read\">,\n timeoutMs: number,\n): Promise<{ done: boolean; value?: Uint8Array } | undefined> {\n return await Promise.race([\n reader.read(),\n new Promise<undefined>((resolve) => {\n setTimeout(resolve, timeoutMs)\n }),\n ])\n}\n\nfunction takeNextSseBlock(\n buffer: string,\n): { block: string; remaining: string } | undefined {\n const boundary = /\\r\\n\\r\\n|\\r\\r|\\n\\n/.exec(buffer)\n if (boundary?.index === undefined) return undefined\n\n return {\n block: buffer.slice(0, boundary.index),\n remaining: buffer.slice(boundary.index + boundary[0].length),\n }\n}\n\ninterface ResponseStreamEvent {\n type?: unknown\n [key: string]: unknown\n}\n\nfunction parseResponseEvent(block: string): ResponseStreamEvent | undefined {\n const data = block\n .split(/\\r\\n|\\r|\\n/)\n .filter((line) => line.startsWith(\"data:\"))\n .map((line) => line.slice(5).trimStart())\n .join(\"\\n\")\n if (!data || data === \"[DONE]\") return undefined\n\n try {\n const event = JSON.parse(data) as unknown\n return event && typeof event === \"object\" ?\n (event as ResponseStreamEvent)\n : undefined\n } catch {\n return undefined\n }\n}\n\nfunction isEncryptedOutputFailureEvent(event: ResponseStreamEvent): boolean {\n return (\n event.type === \"response.failed\"\n && JSON.stringify(event).includes(ENCRYPTED_OUTPUT_ERROR)\n )\n}\n\nfunction isProvisionalResponseEvent(event: ResponseStreamEvent): boolean {\n return (\n event.type === \"response.created\" || event.type === \"response.in_progress\"\n )\n}\n\nfunction fingerprintEncryptedOutput(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"base64url\")\n}\n\nfunction rejectedEncryptedOutputKey(\n scope: string,\n fingerprint: string,\n): string {\n return `${scope}:${fingerprint}`\n}\n","import consola from \"consola\"\n\nimport type {\n ResponsesApiRequest,\n ResponsesContentPart,\n ResponsesInputItem,\n} from \"~/routes/responses/types\"\n\nimport { copilotBaseUrl, copilotHeaders } from \"~/lib/api-config\"\nimport { copilotFetch } from \"~/lib/copilot-fetch\"\nimport { HTTPError } from \"~/lib/error\"\nimport {\n getKnownModelPromptLimit,\n getModelPromptLimit,\n} from \"~/lib/model-limits\"\nimport { state } from \"~/lib/state\"\nimport {\n createEncryptedOutputScope,\n isEncryptedOutputError,\n isImmediateEncryptedOutputStreamFailure,\n isRejectedEncryptedOutput,\n rememberRejectedEncryptedOutputs,\n stripEncryptedOutputParts,\n} from \"~/services/copilot/encrypted-output-recovery\"\n\nconst MAX_RESPONSES_PAYLOAD_BYTES = 5_000_000\nconst CHARS_PER_TOKEN_ESTIMATE = 3.5\nconst TOKEN_RESERVE = 8_000\nconst IMAGE_STRIPPED_PLACEHOLDER =\n \"[image removed to stay under upstream payload limit]\"\nconst INVALID_OUTPUT_IMAGE_PLACEHOLDER =\n \"[image output removed because its URL is not valid for Copilot Responses]\"\nconst INPUT_DROPPED_PLACEHOLDER =\n \"[older response input omitted to stay under context limit]\"\nconst INPUT_TRUNCATED_PREFIX =\n \"[older response input truncated to stay under context limit]\\n\\n\"\nconst PRESERVED_INPUT_STRING_KEYS = new Set([\n \"call_id\",\n \"id\",\n \"model\",\n \"name\",\n \"previous_response_id\",\n \"role\",\n \"status\",\n \"tool_call_id\",\n \"type\",\n])\n\nexport async function createResponses(\n payload: ResponsesApiRequest,\n): Promise<Response> {\n if (!state.copilotToken) throw new Error(\"Copilot token not found\")\n\n const url = `${copilotBaseUrl(state)}/responses`\n const encryptedOutputScope = createEncryptedOutputScope(\n url,\n payload.model,\n state.githubToken ?? state.copilotToken,\n )\n const knownRejected = stripEncryptedOutputParts(\n sanitizeResponsesPayload(payload),\n (fingerprint) =>\n isRejectedEncryptedOutput(encryptedOutputScope, fingerprint),\n )\n const upstreamPayload = fitResponsesPayload(\n knownRejected.payload,\n computeResponsesPayloadCeiling(payload.model),\n )\n const headers: Record<string, string> = {\n ...copilotHeaders(state, responsesPayloadHasImages(upstreamPayload)),\n accept: upstreamPayload.stream ? \"text/event-stream\" : \"application/json\",\n \"X-Initiator\": \"agent\",\n }\n const bodyLength = JSON.stringify(upstreamPayload).length\n\n consola.info(\n `Sending responses payload: ${bodyLength} bytes, model: ${payload.model}`,\n )\n\n const result = await fetchResponsesWithEncryptedOutputFallback(\n { url, headers, encryptedOutputScope },\n upstreamPayload,\n )\n const { response } = result\n\n if (!response.ok) {\n const errorBody = result.errorBody ?? (await response.text())\n consola.error(\n `Failed to create responses - Status: ${response.status} ${response.statusText}`,\n )\n consola.error(`Response body: ${errorBody}`)\n consola.error(`Request payload size: ${result.body.length} bytes`)\n\n if (isContextOverflow(response, errorBody, result.body.length)) {\n const estimatedTokens = Math.ceil(result.body.length / 4)\n const modelCaps = state.models?.data.find((m) => m.id === payload.model)\n ?.capabilities.limits\n const modelLimit = getModelPromptLimit(payload.model, modelCaps)\n const maxOutputTokens = payload.max_output_tokens ?? 0\n\n consola.warn(\n `Responses context overflow -> returning 400 prompt-too-long (~${estimatedTokens} + ${maxOutputTokens} > ${modelLimit})`,\n )\n\n throw new HTTPError(\n \"Prompt too long\",\n new Response(\n JSON.stringify({\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message: `prompt is too long: input length and \\`max_tokens\\` exceed context limit: ${estimatedTokens} + ${maxOutputTokens} > ${modelLimit} tokens`,\n },\n }),\n {\n status: 400,\n statusText: \"Bad Request\",\n headers: { \"content-type\": \"application/json\" },\n },\n ),\n )\n }\n\n throw new HTTPError(\n \"Failed to create responses\",\n new Response(errorBody, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n }),\n )\n }\n\n return response\n}\n\ninterface ResponsesFetchResult {\n body: string\n errorBody?: string\n response: Response\n}\n\ninterface ResponsesFetchContext {\n encryptedOutputScope: string\n headers: Record<string, string>\n url: string\n}\n\nasync function fetchResponsesWithEncryptedOutputFallback(\n context: ResponsesFetchContext,\n payload: ResponsesApiRequest,\n): Promise<ResponsesFetchResult> {\n const { encryptedOutputScope, headers, url } = context\n const fallback = stripEncryptedOutputParts(payload)\n const initial = await sendResponsesRequest(url, headers, payload)\n if (fallback.count === 0) return initial\n\n if (\n !initial.response.ok\n && initial.errorBody\n && isEncryptedOutputError(initial.response, initial.errorBody)\n ) {\n consola.warn(\n `Copilot could not decrypt ${fallback.count} encrypted output part(s); retrying without them`,\n )\n rememberRejectedEncryptedOutputs(\n encryptedOutputScope,\n fallback.fingerprints,\n )\n return sendResponsesRequest(url, headers, fallback.payload)\n }\n\n if (\n initial.response.ok\n && payload.stream\n && (await isImmediateEncryptedOutputStreamFailure(initial.response))\n ) {\n void initial.response.body?.cancel().catch(() => undefined)\n consola.warn(\n `Copilot stream failed before output; retrying without ${fallback.count} encrypted output part(s)`,\n )\n rememberRejectedEncryptedOutputs(\n encryptedOutputScope,\n fallback.fingerprints,\n )\n return sendResponsesRequest(url, headers, fallback.payload)\n }\n\n return initial\n}\n\nasync function sendResponsesRequest(\n url: string,\n headers: Record<string, string>,\n payload: ResponsesApiRequest,\n): Promise<ResponsesFetchResult> {\n const body = JSON.stringify(payload)\n const response = await copilotFetch(url, { method: \"POST\", headers, body })\n const errorBody = response.ok ? undefined : await response.text()\n return { body, errorBody, response }\n}\n\nfunction fitResponsesPayload(\n payload: ResponsesApiRequest,\n ceiling: number,\n): ResponsesApiRequest {\n const initialBody = JSON.stringify(payload)\n if (initialBody.length <= ceiling) return payload\n\n consola.info(\n `Responses context fit: payload ${initialBody.length} bytes exceeds ${ceiling} byte ceiling - reducing`,\n )\n\n if (typeof payload.input === \"string\") {\n const current = truncateStringInput(payload, ceiling)\n const currentBodyLength = JSON.stringify(current).length\n consola.warn(\n `Responses context fit: truncated string input (${initialBody.length} -> ${currentBodyLength} bytes)`,\n )\n return current\n }\n\n let current = payload\n let currentBodyLength = initialBody.length\n\n const imageLocations = collectImageLocations(payload)\n let strippedCount = 0\n\n for (const location of imageLocations) {\n current = replaceImageWithPlaceholder(current, location)\n strippedCount++\n currentBodyLength = JSON.stringify(current).length\n if (currentBodyLength <= ceiling) break\n }\n\n if (strippedCount > 0) {\n consola.warn(\n `Responses context fit: stripped ${strippedCount} images (${initialBody.length} -> ${currentBodyLength} bytes)`,\n )\n }\n\n if (currentBodyLength <= ceiling) return current\n\n const dropped = dropOldInputItems(current, ceiling)\n current = dropOrphanedToolCallOutputs(dropped.payload)\n currentBodyLength = JSON.stringify(current).length\n if (dropped.count > 0) {\n consola.warn(\n `Responses context fit: dropped ${dropped.count} old input items (${initialBody.length} -> ${currentBodyLength} bytes)`,\n )\n }\n\n if (currentBodyLength <= ceiling) return current\n\n current = truncateLargestInputContent(current, ceiling)\n currentBodyLength = JSON.stringify(current).length\n consola.warn(\n `Responses context fit: truncated input content (${initialBody.length} -> ${currentBodyLength} bytes)`,\n )\n\n if (currentBodyLength <= ceiling) return current\n\n current = minimizeResponsesInput(current, ceiling)\n currentBodyLength = JSON.stringify(current).length\n consola.warn(\n `Responses context fit: minimized input history (${initialBody.length} -> ${currentBodyLength} bytes)`,\n )\n\n if (currentBodyLength <= ceiling) return current\n\n current = truncateLargestPayloadStrings(current, ceiling)\n currentBodyLength = JSON.stringify(current).length\n consola.warn(\n `Responses context fit: truncated payload strings (${initialBody.length} -> ${currentBodyLength} bytes)`,\n )\n\n return current\n}\n\nfunction computeResponsesPayloadCeiling(modelId: string): number {\n const limits = state.models?.data.find((m) => m.id === modelId)?.capabilities\n .limits\n const maxPromptTokens = getKnownModelPromptLimit(modelId, limits)\n if (!maxPromptTokens) return MAX_RESPONSES_PAYLOAD_BYTES\n\n const tokenDerivedBytes = Math.floor(\n (maxPromptTokens - TOKEN_RESERVE) * CHARS_PER_TOKEN_ESTIMATE,\n )\n return Math.min(MAX_RESPONSES_PAYLOAD_BYTES, tokenDerivedBytes)\n}\n\nfunction truncateStringInput(\n payload: ResponsesApiRequest,\n ceiling: number,\n): ResponsesApiRequest {\n if (typeof payload.input !== \"string\") return payload\n\n const overhead = JSON.stringify({ ...payload, input: \"\" }).length\n const maxInputLength = Math.max(\n INPUT_TRUNCATED_PREFIX.length,\n ceiling - overhead,\n )\n const tailLength = Math.max(0, maxInputLength - INPUT_TRUNCATED_PREFIX.length)\n return {\n ...payload,\n input: INPUT_TRUNCATED_PREFIX + payload.input.slice(-tailLength),\n }\n}\n\ninterface ImageLocation {\n contentIndex: number\n inputIndex: number\n}\n\nfunction collectImageLocations(\n payload: ResponsesApiRequest,\n): Array<ImageLocation> {\n if (typeof payload.input === \"string\") return []\n\n const locations: Array<ImageLocation> = []\n for (const [inputIndex, item] of payload.input.entries()) {\n if (!Array.isArray(item.content)) continue\n\n for (const [contentIndex, part] of item.content.entries()) {\n if (part.type === \"input_image\") {\n locations.push({ contentIndex, inputIndex })\n }\n }\n }\n return locations\n}\n\nfunction replaceImageWithPlaceholder(\n payload: ResponsesApiRequest,\n location: ImageLocation,\n): ResponsesApiRequest {\n if (typeof payload.input === \"string\") return payload\n\n const input = [...payload.input]\n const item = input[location.inputIndex]\n if (!Array.isArray(item.content)) return payload\n\n const content = [...item.content]\n content[location.contentIndex] = {\n type: \"input_text\",\n text: IMAGE_STRIPPED_PLACEHOLDER,\n }\n\n input[location.inputIndex] = { ...item, content }\n return { ...payload, input }\n}\n\nfunction dropOldInputItems(\n payload: ResponsesApiRequest,\n ceiling: number,\n): { bodyLength: number; count: number; payload: ResponsesApiRequest } {\n if (typeof payload.input === \"string\") {\n return { bodyLength: JSON.stringify(payload).length, count: 0, payload }\n }\n\n const input = [...payload.input]\n let bodyLength = JSON.stringify({ ...payload, input }).length\n let count = 0\n\n for (let index = 0; index < input.length && bodyLength > ceiling; index++) {\n const item = input[index]\n if (item.role === \"system\" || item.role === \"developer\") continue\n if (countRecentDroppableItems(input, index) <= 2) continue\n\n input[index] = createDroppedInputItem(item)\n count++\n bodyLength = JSON.stringify({ ...payload, input }).length\n }\n\n return { bodyLength, count, payload: { ...payload, input } }\n}\n\nfunction createDroppedInputItem(item: ResponsesInputItem): ResponsesInputItem {\n return {\n role: item.role === \"assistant\" ? \"assistant\" : \"user\",\n content: INPUT_DROPPED_PLACEHOLDER,\n }\n}\n\nfunction dropOrphanedToolCallOutputs(\n payload: ResponsesApiRequest,\n): ResponsesApiRequest {\n if (typeof payload.input === \"string\") return payload\n\n const callIds = new Set(\n payload.input\n .filter((item) => isToolCallItem(item) && item.call_id)\n .map((item) => item.call_id),\n )\n let droppedCount = 0\n const input = payload.input.map((item) => {\n if (\n !isToolCallOutputItem(item)\n || !item.call_id\n || callIds.has(item.call_id)\n ) {\n return item\n }\n\n droppedCount++\n return createDroppedInputItem(item)\n })\n\n if (droppedCount === 0) return payload\n consola.warn(\n `Responses context fit: dropped ${droppedCount} orphaned tool call outputs`,\n )\n return { ...payload, input }\n}\n\nfunction isToolCallItem(item: ResponsesInputItem): boolean {\n return typeof item.type === \"string\" && item.type.endsWith(\"_call\")\n}\n\nfunction isToolCallOutputItem(item: ResponsesInputItem): boolean {\n return typeof item.type === \"string\" && item.type.endsWith(\"_call_output\")\n}\n\nfunction countRecentDroppableItems(\n input: Array<ResponsesInputItem>,\n index: number,\n): number {\n let count = 0\n for (let i = index; i < input.length; i++) {\n if (input[i].role !== \"system\" && input[i].role !== \"developer\") count++\n }\n return count\n}\n\nfunction truncateLargestInputContent(\n payload: ResponsesApiRequest,\n ceiling: number,\n): ResponsesApiRequest {\n if (typeof payload.input === \"string\") {\n return truncateStringInput(payload, ceiling)\n }\n\n let current = payload\n let bodyLength = JSON.stringify(current).length\n\n while (bodyLength > ceiling) {\n const target = findLargestInputString(current)\n if (!target) return current\n\n current = truncateStringAtLocation(current, target, bodyLength - ceiling)\n const nextBodyLength = JSON.stringify(current).length\n if (nextBodyLength >= bodyLength) return current\n bodyLength = nextBodyLength\n }\n\n return current\n}\n\ninterface StringLocation {\n inputIndex: number\n length: number\n path: Array<number | string>\n}\n\nfunction findLargestInputString(\n payload: ResponsesApiRequest,\n): StringLocation | null {\n if (typeof payload.input === \"string\") return null\n\n let largest: StringLocation | null = null\n for (const [inputIndex, item] of payload.input.entries()) {\n if (item.role === \"system\" || item.role === \"developer\") continue\n\n const candidate = findLargestStringInValue(item, [])\n if (candidate && (!largest || candidate.length > largest.length)) {\n largest = { inputIndex, ...candidate }\n }\n }\n\n return largest\n}\n\nfunction truncateStringAtLocation(\n payload: ResponsesApiRequest,\n location: StringLocation,\n excessBytes: number,\n): ResponsesApiRequest {\n if (typeof payload.input === \"string\") return payload\n\n const input = [...payload.input]\n const item = input[location.inputIndex]\n const keepLength = Math.max(\n 0,\n location.length - excessBytes - INPUT_TRUNCATED_PREFIX.length - 10_000,\n )\n\n input[location.inputIndex] = truncateStringAtPath(\n item,\n location.path,\n keepLength,\n ) as ResponsesInputItem\n return { ...payload, input }\n}\n\ninterface StringCandidate {\n length: number\n path: Array<number | string>\n}\n\nfunction findLargestStringInValue(\n value: unknown,\n path: Array<number | string>,\n): StringCandidate | null {\n if (typeof value === \"string\") {\n const key = path.at(-1)\n if (typeof key === \"string\" && PRESERVED_INPUT_STRING_KEYS.has(key)) {\n return null\n }\n return { length: value.length, path }\n }\n\n if (Array.isArray(value)) {\n return value.reduce<StringCandidate | null>((largest, item, index) => {\n const candidate = findLargestStringInValue(item, [...path, index])\n if (!candidate) return largest\n return !largest || candidate.length > largest.length ? candidate : largest\n }, null)\n }\n\n if (!isRecord(value)) return null\n\n let largest: StringCandidate | null = null\n for (const [key, nested] of Object.entries(value)) {\n const candidate = findLargestStringInValue(nested, [...path, key])\n if (candidate && (!largest || candidate.length > largest.length)) {\n largest = candidate\n }\n }\n return largest\n}\n\nfunction truncateStringAtPath(\n value: unknown,\n path: Array<number | string>,\n keepLength: number,\n): unknown {\n if (path.length === 0) {\n if (typeof value !== \"string\") return value\n return INPUT_TRUNCATED_PREFIX + value.slice(-keepLength)\n }\n\n const [head, ...tail] = path\n if (Array.isArray(value) && typeof head === \"number\") {\n const arrayValue = value as Array<unknown>\n const next = [...arrayValue]\n next[head] = truncateStringAtPath(next[head], tail, keepLength)\n return next\n }\n\n if (isRecord(value) && typeof head === \"string\") {\n return {\n ...value,\n [head]: truncateStringAtPath(value[head], tail, keepLength),\n }\n }\n\n return value\n}\n\nfunction minimizeResponsesInput(\n payload: ResponsesApiRequest,\n ceiling: number,\n): ResponsesApiRequest {\n if (typeof payload.input === \"string\") {\n return truncateStringInput(payload, ceiling)\n }\n\n const protectedItems = payload.input.filter(\n (item) => item.role === \"system\" || item.role === \"developer\",\n )\n const latest = [...payload.input]\n .reverse()\n .find((item) => item.role !== \"system\" && item.role !== \"developer\")\n\n return {\n ...payload,\n input: [\n ...protectedItems,\n {\n role: latest?.role === \"assistant\" ? \"assistant\" : \"user\",\n content: INPUT_DROPPED_PLACEHOLDER,\n },\n ],\n }\n}\n\nfunction truncateLargestPayloadStrings(\n payload: ResponsesApiRequest,\n ceiling: number,\n): ResponsesApiRequest {\n let current = payload\n let bodyLength = JSON.stringify(current).length\n\n while (bodyLength > ceiling) {\n const target = findLargestStringInValue(current, [])\n if (!target) return current\n\n current = truncateStringAtPath(\n current,\n target.path,\n bodyLength - ceiling,\n ) as ResponsesApiRequest\n const nextBodyLength = JSON.stringify(current).length\n if (nextBodyLength >= bodyLength) return current\n bodyLength = nextBodyLength\n }\n\n return current\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null\n}\n\nfunction isContextOverflow(\n response: Response,\n errorBody: string,\n bodyLength: number,\n): boolean {\n return (\n response.status === 413\n || /request entity too large/i.test(errorBody)\n || /exceeds the limit of \\d+/i.test(errorBody)\n || /context_length_exceeded/i.test(errorBody)\n || /operation timed out/i.test(errorBody)\n || /payload too large/i.test(errorBody)\n || /maximum context length/i.test(errorBody)\n || (response.status >= 500\n && response.status < 600\n && bodyLength > 2_000_000)\n )\n}\n\nfunction sanitizeResponsesPayload(\n payload: ResponsesApiRequest,\n): ResponsesApiRequest {\n const sanitized = normalizeToolDescriptions(\n sanitizeInvalidOutputImages(payload),\n )\n\n // Codex sends ChatGPT-only fast mode metadata; Copilot Responses rejects it.\n delete (sanitized as ResponsesApiRequest & { service_tier?: unknown })\n .service_tier\n\n if (!sanitized.tools?.some((tool) => tool.type === \"image_generation\")) {\n return sanitized\n }\n\n return {\n ...sanitized,\n tools: sanitized.tools.filter((tool) => tool.type !== \"image_generation\"),\n }\n}\n\nfunction normalizeToolDescriptions(\n payload: ResponsesApiRequest,\n): ResponsesApiRequest {\n const tools = normalizeToolList(payload.tools)\n let sanitized = tools === payload.tools ? payload : { ...payload, tools }\n\n if (typeof payload.input === \"string\") return sanitized\n\n let input: Array<ResponsesInputItem> | undefined\n for (const [index, item] of payload.input.entries()) {\n const itemTools = normalizeToolList(item.tools, true)\n if (itemTools === item.tools) continue\n\n input ??= [...payload.input]\n input[index] = { ...item, tools: itemTools }\n }\n\n if (input) sanitized = { ...sanitized, input }\n return sanitized\n}\n\nfunction normalizeToolList(\n tools: ResponsesApiRequest[\"tools\"],\n descriptionRequired = false,\n): ResponsesApiRequest[\"tools\"] {\n if (\n !Array.isArray(tools)\n || !tools.some(\n (tool) =>\n (descriptionRequired && !isNonBlankString(tool.description))\n || (tool.description !== undefined\n && !isNonBlankString(tool.description))\n || (tool.function?.description !== undefined\n && !isNonBlankString(tool.function.description)),\n )\n ) {\n return tools\n }\n\n return tools.map((tool) => {\n const sanitizedTool = { ...tool }\n if (descriptionRequired && !isNonBlankString(sanitizedTool.description)) {\n const nestedDescription = sanitizedTool.function?.description\n let name: string | undefined\n if (isNonBlankString(sanitizedTool.name)) {\n name = sanitizedTool.name\n } else if (isNonBlankString(sanitizedTool.function?.name)) {\n name = sanitizedTool.function.name\n }\n sanitizedTool.description = name ? `Tool ${name}` : \"Tool\"\n if (isNonBlankString(nestedDescription)) {\n sanitizedTool.description = nestedDescription\n }\n } else if (\n sanitizedTool.description !== undefined\n && !isNonBlankString(sanitizedTool.description)\n ) {\n delete sanitizedTool.description\n }\n if (\n sanitizedTool.function?.description !== undefined\n && !isNonBlankString(sanitizedTool.function.description)\n ) {\n sanitizedTool.function = { ...sanitizedTool.function }\n delete sanitizedTool.function.description\n }\n return sanitizedTool\n })\n}\n\nfunction isNonBlankString(value: unknown): value is string {\n return typeof value === \"string\" && value.trim().length > 0\n}\n\nfunction sanitizeInvalidOutputImages(\n payload: ResponsesApiRequest,\n): ResponsesApiRequest {\n if (typeof payload.input === \"string\") return payload\n\n let input: Array<ResponsesInputItem> | undefined\n for (const [index, item] of payload.input.entries()) {\n if (!Array.isArray(item.output)) continue\n\n const sanitizedOutput = sanitizeOutputParts(item.output)\n if (sanitizedOutput === item.output) continue\n\n input ??= [...payload.input]\n input[index] = { ...item, output: sanitizedOutput }\n }\n\n return input ? { ...payload, input } : payload\n}\n\nfunction sanitizeOutputParts(\n output: NonNullable<ResponsesInputItem[\"output\"]>,\n): ResponsesInputItem[\"output\"] {\n if (typeof output === \"string\") return output\n\n const sanitizedOutput = output.map((part) => sanitizeOutputPart(part))\n const changed = sanitizedOutput.some((part, index) => part !== output[index])\n\n return changed ? sanitizedOutput : output\n}\n\nfunction sanitizeOutputPart(part: ResponsesContentPart): ResponsesContentPart {\n if (hasInvalidImageUrl(part)) {\n return {\n type: \"input_text\",\n text: INVALID_OUTPUT_IMAGE_PLACEHOLDER,\n }\n }\n\n if (typeof part.image_url === \"string\" && part.type !== \"input_image\") {\n return {\n type: \"input_image\",\n image_url: part.image_url,\n detail: part.detail,\n }\n }\n\n if (part.type === \"output_text\") {\n return {\n type: \"input_text\",\n text: part.text ?? \"\",\n }\n }\n\n return part\n}\n\nfunction hasInvalidImageUrl(part: { image_url?: unknown }): boolean {\n if (\n !(\"image_url\" in part)\n || part.image_url === null\n || part.image_url === undefined\n ) {\n return false\n }\n return typeof part.image_url !== \"string\" || !isValidHttpUrl(part.image_url)\n}\n\nfunction isValidHttpUrl(value: string): boolean {\n try {\n const url = new URL(value)\n return url.protocol === \"http:\" || url.protocol === \"https:\"\n } catch {\n return false\n }\n}\n\nfunction responsesPayloadHasImages(payload: ResponsesApiRequest): boolean {\n if (typeof payload.input === \"string\") return false\n\n return payload.input.some(\n (item) =>\n Array.isArray(item.content)\n && item.content.some((part) => part.type === \"input_image\"),\n )\n}\n","import type {\n ResponsesApiRequest,\n ResponsesApiResponse,\n ResponsesContentPart,\n ResponsesInputItem,\n ResponsesOutputItem,\n} from \"~/routes/responses/types\"\n\nimport type {\n AnthropicAssistantContentBlock,\n AnthropicImageBlock,\n AnthropicMessagesPayload,\n AnthropicResponse,\n AnthropicTextBlock,\n AnthropicToolResultBlock,\n AnthropicToolUseBlock,\n AnthropicUserContentBlock,\n} from \"./anthropic-types\"\n\ninterface SSEStream {\n writeSSE(event: { data: string; event: string }): Promise<void>\n}\n\nexport function translateAnthropicMessagesToResponses(\n payload: AnthropicMessagesPayload,\n model: string,\n): ResponsesApiRequest {\n return {\n model,\n input: payload.messages.flatMap((message) => translateMessage(message)),\n instructions: translateSystem(payload.system),\n max_output_tokens: payload.max_tokens,\n temperature: payload.temperature,\n top_p: payload.top_p,\n stream: false,\n tools: payload.tools?.map((tool) => ({\n type: \"function\",\n name: tool.name,\n description: tool.description,\n parameters: tool.input_schema,\n })),\n tool_choice: translateToolChoice(payload.tool_choice),\n }\n}\n\nexport function translateResponsesToAnthropicMessage(\n response: ResponsesApiResponse,\n clientModel: string,\n): AnthropicResponse {\n const content = response.output.flatMap((item) => translateOutputItem(item))\n const hasToolUse = content.some((block) => block.type === \"tool_use\")\n\n return {\n id: response.id,\n type: \"message\",\n role: \"assistant\",\n content,\n model: clientModel,\n stop_reason: hasToolUse ? \"tool_use\" : \"end_turn\",\n stop_sequence: null,\n usage: {\n input_tokens: response.usage?.input_tokens ?? 0,\n output_tokens: response.usage?.output_tokens ?? 0,\n },\n }\n}\n\nexport async function writeResponsesAsAnthropicStream(\n stream: SSEStream,\n response: ResponsesApiResponse,\n clientModel: string,\n) {\n const message = translateResponsesToAnthropicMessage(response, clientModel)\n\n await stream.writeSSE({\n event: \"message_start\",\n data: JSON.stringify({\n type: \"message_start\",\n message: {\n ...message,\n content: [],\n stop_reason: null,\n stop_sequence: null,\n usage: { ...message.usage, output_tokens: 0 },\n },\n }),\n })\n\n for (const [index, block] of message.content.entries()) {\n if (block.type === \"text\") {\n await writeTextBlock(stream, index, block.text)\n } else if (block.type === \"tool_use\") {\n await writeToolUseBlock(stream, index, block)\n }\n }\n\n await stream.writeSSE({\n event: \"message_delta\",\n data: JSON.stringify({\n type: \"message_delta\",\n delta: {\n stop_reason: message.stop_reason,\n stop_sequence: null,\n },\n usage: { output_tokens: message.usage.output_tokens },\n }),\n })\n await stream.writeSSE({\n event: \"message_stop\",\n data: JSON.stringify({ type: \"message_stop\" }),\n })\n}\n\nfunction translateSystem(\n system: AnthropicMessagesPayload[\"system\"],\n): string | undefined {\n if (!system) return undefined\n if (typeof system === \"string\") return system\n return system.map((block) => block.text).join(\"\\n\\n\")\n}\n\nfunction translateMessage(\n message: AnthropicMessagesPayload[\"messages\"][number],\n): Array<ResponsesInputItem> {\n if (message.role === \"user\") return translateUserMessage(message.content)\n return translateAssistantMessage(message.content)\n}\n\nfunction translateUserMessage(\n content: string | Array<AnthropicUserContentBlock>,\n): Array<ResponsesInputItem> {\n if (typeof content === \"string\") return [{ role: \"user\", content }]\n\n const items: Array<ResponsesInputItem> = []\n const messageParts: Array<\n Exclude<AnthropicUserContentBlock, AnthropicToolResultBlock>\n > = []\n\n for (const block of content) {\n if (block.type === \"tool_result\") {\n items.push({\n type: \"function_call_output\",\n call_id: block.tool_use_id,\n output: stringifyToolResult(block.content),\n })\n } else {\n messageParts.push(block)\n }\n }\n\n const messageContent = translateInputContent(messageParts)\n if (messageContent) items.push({ role: \"user\", content: messageContent })\n return items\n}\n\nfunction translateAssistantMessage(\n content: string | Array<AnthropicAssistantContentBlock>,\n): Array<ResponsesInputItem> {\n if (typeof content === \"string\") return [{ role: \"assistant\", content }]\n\n const items: Array<ResponsesInputItem> = []\n const text = content\n .filter((block): block is AnthropicTextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\\n\\n\")\n if (text) items.push({ role: \"assistant\", content: text })\n\n for (const block of content) {\n if (block.type !== \"tool_use\") continue\n items.push({\n type: \"function_call\",\n call_id: block.id,\n name: block.name,\n arguments: JSON.stringify(block.input),\n })\n }\n\n return items\n}\n\nfunction translateInputContent(\n content: Array<Exclude<AnthropicUserContentBlock, AnthropicToolResultBlock>>,\n): string | Array<ResponsesContentPart> | null {\n const hasImage = content.some((block) => block.type === \"image\")\n if (!hasImage) {\n const text = content\n .filter((block): block is AnthropicTextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\\n\\n\")\n return text || null\n }\n\n const parts: Array<ResponsesContentPart> = []\n for (const block of content) {\n if (block.type === \"text\") {\n parts.push({ type: \"input_text\", text: block.text })\n } else if (block.type === \"image\") {\n parts.push(translateImageBlock(block))\n }\n }\n return parts.length > 0 ? parts : null\n}\n\nfunction translateImageBlock(block: AnthropicImageBlock): ResponsesContentPart {\n return {\n type: \"input_image\",\n image_url: `data:${block.source.media_type};base64,${block.source.data}`,\n }\n}\n\nfunction translateToolChoice(\n toolChoice: AnthropicMessagesPayload[\"tool_choice\"],\n): ResponsesApiRequest[\"tool_choice\"] {\n if (!toolChoice) return undefined\n if (toolChoice.type === \"auto\" || toolChoice.type === \"none\") {\n return toolChoice.type\n }\n if (toolChoice.type === \"any\") return \"required\"\n if (toolChoice.name) {\n return { type: \"function\", name: toolChoice.name }\n }\n return undefined\n}\n\nfunction stringifyToolResult(\n content: AnthropicToolResultBlock[\"content\"],\n): string {\n return typeof content === \"string\" ? content : JSON.stringify(content)\n}\n\nfunction translateOutputItem(\n item: ResponsesOutputItem,\n): Array<AnthropicAssistantContentBlock> {\n if (item.type === \"function_call\" && item.name && item.call_id) {\n return [\n {\n type: \"tool_use\",\n id: item.call_id,\n name: item.name,\n input: parseToolInput(item.arguments),\n },\n ]\n }\n\n if (item.type !== \"message\" || !item.content) return []\n return item.content.flatMap((part) => {\n if (part.type !== \"output_text\" || !part.text) return []\n return [{ type: \"text\", text: part.text }]\n })\n}\n\nfunction parseToolInput(\n argumentsText: string | undefined,\n): Record<string, unknown> {\n if (!argumentsText) return {}\n try {\n const parsed = JSON.parse(argumentsText) as unknown\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>\n }\n } catch {\n return {}\n }\n return {}\n}\n\nasync function writeTextBlock(stream: SSEStream, index: number, text: string) {\n await stream.writeSSE({\n event: \"content_block_start\",\n data: JSON.stringify({\n type: \"content_block_start\",\n index,\n content_block: { type: \"text\", text: \"\" },\n }),\n })\n await stream.writeSSE({\n event: \"content_block_delta\",\n data: JSON.stringify({\n type: \"content_block_delta\",\n index,\n delta: { type: \"text_delta\", text },\n }),\n })\n await stream.writeSSE({\n event: \"content_block_stop\",\n data: JSON.stringify({ type: \"content_block_stop\", index }),\n })\n}\n\nasync function writeToolUseBlock(\n stream: SSEStream,\n index: number,\n block: AnthropicToolUseBlock,\n) {\n await stream.writeSSE({\n event: \"content_block_start\",\n data: JSON.stringify({\n type: \"content_block_start\",\n index,\n content_block: {\n type: \"tool_use\",\n id: block.id,\n name: block.name,\n input: {},\n },\n }),\n })\n await stream.writeSSE({\n event: \"content_block_delta\",\n data: JSON.stringify({\n type: \"content_block_delta\",\n index,\n delta: {\n type: \"input_json_delta\",\n partial_json: JSON.stringify(block.input),\n },\n }),\n })\n await stream.writeSSE({\n event: \"content_block_stop\",\n data: JSON.stringify({ type: \"content_block_stop\", index }),\n })\n}\n","import { type ChatCompletionChunk } from \"~/services/copilot/create-chat-completions\"\n\nimport {\n type AnthropicStreamEventData,\n type AnthropicStreamState,\n} from \"./anthropic-types\"\nimport { mapOpenAIStopReasonToAnthropic } from \"./utils\"\n\nfunction isToolBlockOpen(state: AnthropicStreamState): boolean {\n if (!state.contentBlockOpen) {\n return false\n }\n // Check if the current block index corresponds to any known tool call\n return Object.values(state.toolCalls).some(\n (tc) => tc.anthropicBlockIndex === state.contentBlockIndex,\n )\n}\n\n// eslint-disable-next-line max-lines-per-function, complexity\nexport function translateChunkToAnthropicEvents(\n chunk: ChatCompletionChunk,\n state: AnthropicStreamState,\n): Array<AnthropicStreamEventData> {\n const events: Array<AnthropicStreamEventData> = []\n\n if (chunk.choices.length === 0) {\n return events\n }\n\n const choice = chunk.choices[0]\n const { delta } = choice\n\n if (!state.messageStartSent) {\n events.push({\n type: \"message_start\",\n message: {\n id: chunk.id,\n type: \"message\",\n role: \"assistant\",\n content: [],\n model: chunk.model,\n stop_reason: null,\n stop_sequence: null,\n usage: {\n input_tokens:\n (chunk.usage?.prompt_tokens ?? 0)\n - (chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0),\n output_tokens: 0, // Will be updated in message_delta when finished\n ...(chunk.usage?.prompt_tokens_details?.cached_tokens\n !== undefined && {\n cache_read_input_tokens:\n chunk.usage.prompt_tokens_details.cached_tokens,\n }),\n },\n },\n })\n state.messageStartSent = true\n }\n\n if (delta.content) {\n if (isToolBlockOpen(state)) {\n // A tool block was open, so close it before starting a text block.\n events.push({\n type: \"content_block_stop\",\n index: state.contentBlockIndex,\n })\n state.contentBlockIndex++\n state.contentBlockOpen = false\n }\n\n if (!state.contentBlockOpen) {\n events.push({\n type: \"content_block_start\",\n index: state.contentBlockIndex,\n content_block: {\n type: \"text\",\n text: \"\",\n },\n })\n state.contentBlockOpen = true\n }\n\n events.push({\n type: \"content_block_delta\",\n index: state.contentBlockIndex,\n delta: {\n type: \"text_delta\",\n text: delta.content,\n },\n })\n }\n\n if (delta.tool_calls) {\n for (const toolCall of delta.tool_calls) {\n if (toolCall.id && toolCall.function?.name) {\n // New tool call starting.\n if (state.contentBlockOpen) {\n // Close any previously open block.\n events.push({\n type: \"content_block_stop\",\n index: state.contentBlockIndex,\n })\n state.contentBlockIndex++\n state.contentBlockOpen = false\n }\n\n const anthropicBlockIndex = state.contentBlockIndex\n state.toolCalls[toolCall.index] = {\n id: toolCall.id,\n name: toolCall.function.name,\n anthropicBlockIndex,\n }\n\n events.push({\n type: \"content_block_start\",\n index: anthropicBlockIndex,\n content_block: {\n type: \"tool_use\",\n id: toolCall.id,\n name: toolCall.function.name,\n input: {},\n },\n })\n state.contentBlockOpen = true\n }\n\n if (toolCall.function?.arguments) {\n const toolCallInfo = state.toolCalls[toolCall.index]\n // Tool call can still be empty\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (toolCallInfo) {\n events.push({\n type: \"content_block_delta\",\n index: toolCallInfo.anthropicBlockIndex,\n delta: {\n type: \"input_json_delta\",\n partial_json: toolCall.function.arguments,\n },\n })\n }\n }\n }\n }\n\n if (choice.finish_reason) {\n if (state.contentBlockOpen) {\n events.push({\n type: \"content_block_stop\",\n index: state.contentBlockIndex,\n })\n state.contentBlockOpen = false\n }\n\n events.push(\n {\n type: \"message_delta\",\n delta: {\n stop_reason: mapOpenAIStopReasonToAnthropic(choice.finish_reason),\n stop_sequence: null,\n },\n usage: {\n input_tokens:\n (chunk.usage?.prompt_tokens ?? 0)\n - (chunk.usage?.prompt_tokens_details?.cached_tokens ?? 0),\n output_tokens: chunk.usage?.completion_tokens ?? 0,\n ...(chunk.usage?.prompt_tokens_details?.cached_tokens\n !== undefined && {\n cache_read_input_tokens:\n chunk.usage.prompt_tokens_details.cached_tokens,\n }),\n },\n },\n {\n type: \"message_stop\",\n },\n )\n }\n\n return events\n}\n\nexport function translateErrorToAnthropicErrorEvent(\n message?: string,\n): AnthropicStreamEventData {\n if (isContextOverflowMessage(message)) {\n return {\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message:\n \"prompt is too long: upstream operation timed out while streaming the response\",\n },\n }\n }\n\n return {\n type: \"error\",\n error: {\n type: \"api_error\",\n message: message ?? \"An unexpected error occurred during streaming.\",\n },\n }\n}\n\nfunction isContextOverflowMessage(message: string | undefined): boolean {\n return (\n message !== undefined\n && (/prompt is too long/i.test(message)\n || /operation timed out/i.test(message)\n || /request entity too large/i.test(message)\n || /context_length_exceeded/i.test(message)\n || /payload too large/i.test(message)\n || /maximum context length/i.test(message))\n )\n}\n","import type { Context } from \"hono\"\n\nimport consola from \"consola\"\nimport { streamSSE } from \"hono/streaming\"\n\nimport type { ResponsesApiResponse } from \"~/routes/responses/types\"\nimport type { Model } from \"~/services/copilot/get-models\"\n\nimport { awaitApproval } from \"~/lib/approval\"\nimport {\n fitContext,\n fitUnknownModelContext,\n isPayloadOverUnknownModelCeiling,\n} from \"~/lib/context-manager\"\nimport { HTTPError } from \"~/lib/error\"\nimport { createPromptTooLongError } from \"~/lib/prompt-too-long\"\nimport { checkRateLimit } from \"~/lib/rate-limit\"\nimport { state } from \"~/lib/state\"\nimport {\n createChatCompletions,\n type ChatCompletionChunk,\n type ChatCompletionsPayload,\n type ChatCompletionResponse,\n} from \"~/services/copilot/create-chat-completions\"\nimport { createResponses } from \"~/services/copilot/create-responses\"\n\nimport {\n type AnthropicMessagesPayload,\n type AnthropicStreamState,\n} from \"./anthropic-types\"\nimport {\n preprocessAnthropicPayload,\n translateToAnthropic,\n translateToOpenAI,\n} from \"./non-stream-translation\"\nimport {\n translateAnthropicMessagesToResponses,\n translateResponsesToAnthropicMessage,\n writeResponsesAsAnthropicStream,\n} from \"./responses-bridge\"\nimport {\n translateChunkToAnthropicEvents,\n translateErrorToAnthropicErrorEvent,\n} from \"./stream-translation\"\n\n/** Heartbeat interval for SSE keepalive. Claude Code's idle timeout is 90s\n * (CLAUDE_STREAM_IDLE_TIMEOUT_MS); 15s gives a 6× safety margin. */\nconst PING_INTERVAL_MS = 15_000\nconst CHAT_COMPLETIONS_ENDPOINT = \"/chat/completions\"\nconst RESPONSES_ENDPOINT = \"/responses\"\n\ntype SSEStream = Parameters<Parameters<typeof streamSSE>[1]>[0]\ntype ChatCompletionResult = Awaited<ReturnType<typeof createChatCompletions>>\ntype CreateChatCompletionResponse = () => Promise<ChatCompletionResult>\n\ninterface ChatCompletionFlowOptions {\n clientModel: string\n requestId: string\n}\n\ninterface ResponsesFlowOptions extends ChatCompletionFlowOptions {\n model: string\n}\n\ninterface ChatCompletionStreamOptions extends ChatCompletionFlowOptions {\n createResponse: CreateChatCompletionResponse\n streamState: AnthropicStreamState\n}\n\ninterface StreamCleanupOptions {\n error: unknown\n requestId: string\n streamState: AnthropicStreamState\n}\n\nfunction generateRequestId(): string {\n // RFC 4122 v4-ish; sufficient for response correlation.\n return `req_${crypto.randomUUID().replaceAll(\"-\", \"\")}`\n}\n\nexport async function handleCompletion(c: Context) {\n await checkRateLimit(state)\n\n const requestId = c.req.header(\"x-client-request-id\") ?? generateRequestId()\n c.header(\"request-id\", requestId)\n c.header(\"x-request-id\", requestId)\n\n const anthropicPayload = await c.req.json<AnthropicMessagesPayload>()\n // Lazy debug logging — JSON.stringify on a large payload is 5-30ms even\n // when debug output is suppressed. Guard with level check (debug = 4).\n if (consola.level >= 4) {\n consola.debug(\n `[${requestId}] Anthropic request payload:`,\n JSON.stringify(anthropicPayload).slice(0, 2000),\n )\n }\n\n // Preserve the client-requested model name so we can echo it back in the\n // response (Claude Code reads `response.model` for display/telemetry).\n const clientModel = anthropicPayload.model\n\n // Async preprocessing: PDF document block extraction, etc.\n const preprocessed = await preprocessAnthropicPayload(anthropicPayload)\n const openAIPayload = translateToOpenAI(preprocessed)\n const model = state.models?.data.find((m) => m.id === openAIPayload.model)\n\n if (shouldUseResponsesForMessages(model)) {\n return await handleResponsesMessages(c, preprocessed, {\n clientModel,\n model: openAIPayload.model,\n requestId,\n })\n }\n\n return await handleChatCompletions(c, openAIPayload, {\n clientModel,\n requestId,\n })\n}\n\nasync function handleChatCompletions(\n c: Context,\n openAIPayload: ChatCompletionsPayload,\n options: ChatCompletionFlowOptions,\n) {\n if (consola.level >= 4) {\n consola.debug(\n `[${options.requestId}] Translated OpenAI request payload:`,\n JSON.stringify(openAIPayload),\n )\n }\n\n // Byte-based context management — fast path returns input unchanged.\n const model = state.models?.data.find((m) => m.id === openAIPayload.model)\n const fittedPayload =\n model ?\n fitContext(openAIPayload, model)\n : fitUnknownModelContext(openAIPayload)\n\n if (!model && isPayloadOverUnknownModelCeiling(fittedPayload)) {\n throw createPromptTooLongError(\n fittedPayload,\n JSON.stringify(fittedPayload).length,\n )\n }\n\n if (fittedPayload.messages.length !== openAIPayload.messages.length) {\n consola.info(\n `[${options.requestId}] Context management: ${openAIPayload.messages.length} → ${fittedPayload.messages.length} messages`,\n )\n }\n\n if (state.manualApprove) {\n await awaitApproval()\n }\n\n if (fittedPayload.stream) {\n consola.debug(`[${options.requestId}] Streaming response from Copilot`)\n return handleStreamingChatCompletion(\n c,\n () => createChatCompletions(fittedPayload),\n options,\n )\n }\n\n const response = await createChatCompletions(fittedPayload)\n if (!isNonStreaming(response)) {\n throw new Error(\"Expected non-streaming response from Copilot\")\n }\n return handleNonStreamingChatCompletion(c, response, options)\n}\n\nasync function handleResponsesMessages(\n c: Context,\n payload: AnthropicMessagesPayload,\n options: ResponsesFlowOptions,\n) {\n if (state.manualApprove) {\n await awaitApproval()\n }\n\n const responsesPayload = translateAnthropicMessagesToResponses(\n payload,\n options.model,\n )\n\n if (payload.stream) {\n return streamSSE(c, async (stream) => {\n const stopPings = startPings(stream)\n try {\n const response = await createResponses(responsesPayload)\n const body = (await response.json()) as ResponsesApiResponse\n await writeResponsesAsAnthropicStream(stream, body, options.clientModel)\n } catch (error) {\n await stream.writeSSE({\n event: \"error\",\n data: JSON.stringify(\n translateErrorToAnthropicErrorEvent(\n error instanceof Error ? error.message : undefined,\n ),\n ),\n })\n } finally {\n stopPings()\n }\n })\n }\n\n const response = await createResponses(responsesPayload)\n const body = (await response.json()) as ResponsesApiResponse\n return c.json(translateResponsesToAnthropicMessage(body, options.clientModel))\n}\n\nfunction shouldUseResponsesForMessages(model: Model | undefined): boolean {\n if (!model?.supported_endpoints) return false\n return (\n model.supported_endpoints.includes(RESPONSES_ENDPOINT)\n && !model.supported_endpoints.includes(CHAT_COMPLETIONS_ENDPOINT)\n )\n}\n\nfunction handleNonStreamingChatCompletion(\n c: Context,\n response: ChatCompletionResponse,\n options: ChatCompletionFlowOptions,\n) {\n if (consola.level >= 4) {\n consola.debug(\n `[${options.requestId}] Non-streaming response from Copilot:`,\n JSON.stringify(response).slice(-400),\n )\n }\n\n try {\n const anthropicResponse = translateToAnthropic(\n response,\n options.clientModel,\n )\n if (consola.level >= 4) {\n consola.debug(\n `[${options.requestId}] Translated Anthropic response:`,\n JSON.stringify(anthropicResponse),\n )\n }\n return c.json(anthropicResponse)\n } catch (error) {\n return c.json(createTranslationErrorBody(error, options.requestId), 500)\n }\n}\n\nfunction createTranslationErrorBody(error: unknown, requestId: string) {\n consola.error(\n `[${requestId}] Failed to translate non-streaming response:`,\n error,\n )\n return {\n type: \"error\" as const,\n error: {\n type: \"api_error\",\n message:\n error instanceof Error ?\n `Translation failed: ${error.message}`\n : \"Translation failed\",\n },\n }\n}\n\nfunction handleStreamingChatCompletion(\n c: Context,\n createResponse: CreateChatCompletionResponse,\n options: ChatCompletionFlowOptions,\n) {\n return streamSSE(c, async (stream) => {\n const streamState: AnthropicStreamState = {\n messageStartSent: false,\n contentBlockIndex: 0,\n contentBlockOpen: false,\n toolCalls: {},\n }\n\n const stopPings = startPings(stream)\n\n try {\n await writePing(stream)\n await forwardChatCompletionStream(stream, {\n ...options,\n createResponse,\n streamState,\n })\n } catch (error) {\n await emitStreamErrorCleanup(stream, {\n error,\n requestId: options.requestId,\n streamState,\n })\n } finally {\n stopPings()\n }\n })\n}\n\nfunction startPings(stream: SSEStream): () => void {\n const pingTimer = setInterval(() => {\n void writePing(stream).catch(() => {})\n }, PING_INTERVAL_MS)\n\n return () => clearInterval(pingTimer)\n}\n\nasync function writePing(stream: SSEStream): Promise<void> {\n await stream.writeSSE({\n event: \"ping\",\n data: JSON.stringify({ type: \"ping\" }),\n })\n}\n\nasync function forwardChatCompletionStream(\n stream: SSEStream,\n options: ChatCompletionStreamOptions,\n) {\n const response = await options.createResponse()\n if (isNonStreaming(response)) {\n throw new Error(\"Expected streaming response from Copilot\")\n }\n\n for await (const rawEvent of response) {\n if (rawEvent.data === \"[DONE]\") break\n if (!rawEvent.data) continue\n\n const chunk = parseChatCompletionChunk(rawEvent.data, options.requestId)\n if (!chunk) continue\n\n if (!options.streamState.messageStartSent) chunk.model = options.clientModel\n\n for (const event of translateChunkToAnthropicEvents(\n chunk,\n options.streamState,\n )) {\n await stream.writeSSE({ event: event.type, data: JSON.stringify(event) })\n }\n }\n}\n\nfunction parseChatCompletionChunk(\n data: string,\n requestId: string,\n): ChatCompletionChunk | null {\n try {\n return JSON.parse(data) as ChatCompletionChunk\n } catch (parseError) {\n consola.warn(\n `[${requestId}] Skipping unparseable Copilot chunk:`,\n parseError,\n )\n return null\n }\n}\n\nasync function emitStreamErrorCleanup(\n stream: SSEStream,\n options: StreamCleanupOptions,\n) {\n consola.error(`[${options.requestId}] Streaming error:`, options.error)\n\n try {\n const errorMessage = await extractStreamErrorMessage(options.error)\n await closeOpenContentBlock(stream, options.streamState)\n await closeStartedMessage(stream, options.streamState)\n await stream.writeSSE({\n event: \"error\",\n data: JSON.stringify(translateErrorToAnthropicErrorEvent(errorMessage)),\n })\n } catch (cleanupError) {\n consola.error(\n `[${options.requestId}] Failed to emit stream cleanup events:`,\n cleanupError,\n )\n }\n}\n\nasync function extractStreamErrorMessage(\n error: unknown,\n): Promise<string | undefined> {\n if (error instanceof HTTPError) {\n try {\n return extractMessageFromErrorText(await error.response.text())\n } catch {\n return error.message\n }\n }\n\n return error instanceof Error ? error.message : undefined\n}\n\nfunction extractMessageFromErrorText(errorText: string): string {\n try {\n const parsed = JSON.parse(errorText) as unknown\n const extracted = extractMessageFromParsedError(parsed)\n if (extracted) return extracted\n } catch {\n return errorText\n }\n\n return errorText\n}\n\nfunction extractMessageFromParsedError(parsed: unknown): string | undefined {\n if (typeof parsed === \"string\") return parsed\n if (typeof parsed !== \"object\" || parsed === null) return undefined\n\n const obj = parsed as Record<string, unknown>\n const error = obj.error\n if (typeof error === \"string\") return error\n if (typeof error === \"object\" && error !== null) {\n const nested = error as Record<string, unknown>\n if (typeof nested.message === \"string\") return nested.message\n }\n if (typeof obj.message === \"string\") return obj.message\n\n return undefined\n}\n\nasync function closeOpenContentBlock(\n stream: SSEStream,\n streamState: AnthropicStreamState,\n) {\n if (!streamState.contentBlockOpen) return\n\n await stream.writeSSE({\n event: \"content_block_stop\",\n data: JSON.stringify({\n type: \"content_block_stop\",\n index: streamState.contentBlockIndex,\n }),\n })\n}\n\nasync function closeStartedMessage(\n stream: SSEStream,\n streamState: AnthropicStreamState,\n) {\n if (!streamState.messageStartSent) return\n\n await stream.writeSSE({\n event: \"message_delta\",\n data: JSON.stringify({\n type: \"message_delta\",\n delta: { stop_reason: \"end_turn\", stop_sequence: null },\n usage: { output_tokens: 0 },\n }),\n })\n await stream.writeSSE({\n event: \"message_stop\",\n data: JSON.stringify({ type: \"message_stop\" }),\n })\n}\n\nconst isNonStreaming = (\n response: Awaited<ReturnType<typeof createChatCompletions>>,\n): response is ChatCompletionResponse => Object.hasOwn(response, \"choices\")\n","import { Hono } from \"hono\"\n\nimport { forwardError } from \"~/lib/error\"\n\nimport { handleCountTokens } from \"./count-tokens-handler\"\nimport { handleCompletion } from \"./handler\"\n\nexport const messageRoutes = new Hono()\n\nmessageRoutes.post(\"/\", async (c) => {\n try {\n return await handleCompletion(c)\n } catch (error) {\n return await forwardError(c, error)\n }\n})\n\nmessageRoutes.post(\"/count_tokens\", async (c) => {\n try {\n return await handleCountTokens(c)\n } catch (error) {\n return await forwardError(c, error)\n }\n})\n","import { Hono } from \"hono\"\n\nimport { forwardError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\nimport { cacheModels } from \"~/lib/utils\"\n\nexport const modelRoutes = new Hono()\n\nmodelRoutes.get(\"/\", async (c) => {\n try {\n // Codex appends client_version and expects its own model-catalog schema.\n // An empty catalog keeps Codex's bundled metadata and instructions; partial\n // remote entries would replace them instead of merging individual fields.\n if (c.req.query(\"client_version\") !== undefined) {\n return c.json({ models: [] })\n }\n\n if (!state.models) {\n // This should be handled by startup logic, but as a fallback.\n await cacheModels()\n }\n\n const models = state.models?.data.map((model) => ({\n id: model.id,\n object: \"model\",\n type: \"model\",\n created: 0, // No date available from source\n created_at: new Date(0).toISOString(), // No date available from source\n owned_by: model.vendor,\n display_name: model.name,\n capabilities: model.capabilities,\n supported_capabilities: inferClaudeCodeCapabilities(model),\n }))\n\n return c.json({\n object: \"list\",\n data: models,\n has_more: false,\n })\n } catch (error) {\n return await forwardError(c, error)\n }\n})\n\nfunction inferClaudeCodeCapabilities(\n model: NonNullable<typeof state.models>[\"data\"][number],\n): Array<string> {\n const capabilities = new Set<string>()\n const supports = model.capabilities.supports\n\n if (supports.reasoning_effort?.length) {\n capabilities.add(\"effort\")\n if (supports.reasoning_effort.includes(\"xhigh\")) {\n capabilities.add(\"xhigh_effort\")\n }\n if (supports.reasoning_effort.includes(\"max\")) {\n capabilities.add(\"max_effort\")\n }\n }\n\n if (supports.adaptive_thinking) {\n capabilities.add(\"thinking\")\n capabilities.add(\"adaptive_thinking\")\n capabilities.add(\"interleaved_thinking\")\n }\n\n return [...capabilities]\n}\n","import type { Context } from \"hono\"\n\nimport consola from \"consola\"\n\nimport { awaitApproval } from \"~/lib/approval\"\nimport { checkRateLimit } from \"~/lib/rate-limit\"\nimport { state } from \"~/lib/state\"\nimport { createResponses } from \"~/services/copilot/create-responses\"\n\nimport type { ResponsesApiRequest } from \"./types\"\n\nexport async function handleResponses(c: Context): Promise<Response> {\n await checkRateLimit(state)\n\n const request = await c.req.json<ResponsesApiRequest>()\n consola.debug(\"Responses API request:\", JSON.stringify(request).slice(-400))\n\n if (state.manualApprove) await awaitApproval()\n\n return createResponses(request)\n}\n","import consola from \"consola\"\nimport { Hono } from \"hono\"\n\nimport { forwardError, HTTPError } from \"~/lib/error\"\n\nimport { handleResponses } from \"./handler\"\n\nexport const responsesRoutes = new Hono()\n\nresponsesRoutes.post(\"/\", async (c) => {\n try {\n return await handleResponses(c)\n } catch (error) {\n if (error instanceof HTTPError) {\n const errorText = await error.response.text()\n let parsed: unknown\n try {\n parsed = JSON.parse(errorText)\n } catch {\n parsed = errorText\n }\n consola.error(\"Responses HTTP error body:\", parsed)\n\n return new Response(errorText, {\n status: error.response.status,\n statusText: error.response.statusText,\n headers: {\n \"content-type\":\n error.response.headers.get(\"content-type\") ?? \"application/json\",\n },\n })\n }\n\n return await forwardError(c, error)\n }\n})\n","import consola from \"consola\"\nimport { Hono } from \"hono\"\n\nimport { state } from \"~/lib/state\"\n\nexport const tokenRoute = new Hono()\n\ntokenRoute.get(\"/\", (c) => {\n try {\n return c.json({\n token: state.copilotToken,\n })\n } catch (error) {\n consola.error(\"Error fetching token:\", error)\n return c.json({ error: \"Failed to fetch token\", token: null }, 500)\n }\n})\n","import consola from \"consola\"\nimport { Hono } from \"hono\"\n\nimport { getCopilotUsage } from \"~/services/github/get-copilot-usage\"\n\nexport const usageRoute = new Hono()\n\nusageRoute.get(\"/\", async (c) => {\n try {\n const usage = await getCopilotUsage()\n return c.json(usage)\n } catch (error) {\n consola.error(\"Error fetching Copilot usage:\", error)\n return c.json({ error: \"Failed to fetch Copilot usage\" }, 500)\n }\n})\n","import { Hono } from \"hono\"\nimport { cors } from \"hono/cors\"\nimport { logger } from \"hono/logger\"\n\nimport { optionalLocalAuth } from \"./lib/local-auth\"\nimport { requestTraceMiddleware } from \"./lib/request-trace\"\nimport { completionRoutes } from \"./routes/chat-completions/route\"\nimport { debugRoutes } from \"./routes/debug/route\"\nimport { embeddingRoutes } from \"./routes/embeddings/route\"\nimport { messageRoutes } from \"./routes/messages/route\"\nimport { modelRoutes } from \"./routes/models/route\"\nimport { responsesRoutes } from \"./routes/responses/route\"\nimport { tokenRoute } from \"./routes/token/route\"\nimport { usageRoute } from \"./routes/usage/route\"\n\nexport const server = new Hono()\n\nserver.use(logger())\nserver.use(cors())\nserver.use(requestTraceMiddleware)\n\nserver.get(\"/\", (c) => c.text(\"Server running\"))\n\nserver.use(optionalLocalAuth)\n\nserver.route(\"/chat/completions\", completionRoutes)\nserver.route(\"/debug\", debugRoutes)\nserver.route(\"/models\", modelRoutes)\nserver.route(\"/embeddings\", embeddingRoutes)\nserver.route(\"/usage\", usageRoute)\nserver.route(\"/token\", tokenRoute)\n\n// Compatibility with tools that expect v1/ prefix\nserver.route(\"/v1/chat/completions\", completionRoutes)\nserver.route(\"/v1/models\", modelRoutes)\nserver.route(\"/v1/embeddings\", embeddingRoutes)\n\n// OpenAI Responses API endpoint\nserver.route(\"/v1/responses\", responsesRoutes)\n\n// Anthropic compatible endpoints\nserver.route(\"/v1/messages\", messageRoutes)\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport clipboard from \"clipboardy\"\nimport consola from \"consola\"\nimport { serve, type ServerHandler } from \"srvx\"\nimport invariant from \"tiny-invariant\"\n\nimport { parseLocalApiKeys } from \"./lib/local-auth\"\nimport { ensurePaths } from \"./lib/paths\"\nimport { initProxyFromEnv } from \"./lib/proxy\"\nimport { generateEnvScript } from \"./lib/shell\"\nimport { state } from \"./lib/state\"\nimport { setupCopilotToken, setupGitHubToken } from \"./lib/token\"\nimport { cacheModels, cacheVSCodeVersion } from \"./lib/utils\"\nimport { server } from \"./server\"\n\ninterface RunServerOptions {\n port: number\n verbose: boolean\n accountType: string\n manual: boolean\n rateLimit?: number\n rateLimitWait: boolean\n githubToken?: string\n claudeCode: boolean\n showToken: boolean\n proxyEnv: boolean\n apiKey?: string\n}\n\nexport async function runServer(options: RunServerOptions): Promise<void> {\n if (options.proxyEnv) {\n initProxyFromEnv()\n }\n\n if (options.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n state.accountType = options.accountType\n if (options.accountType !== \"individual\") {\n consola.info(`Using ${options.accountType} plan GitHub account`)\n }\n\n state.manualApprove = options.manual\n state.rateLimitSeconds = options.rateLimit\n state.rateLimitWait = options.rateLimitWait\n state.showToken = options.showToken\n state.localApiKeys = parseLocalApiKeys(options.apiKey)\n if (state.localApiKeys.length > 0) {\n consola.info(\"Local API-key auth enabled\")\n }\n\n if (options.githubToken) {\n state.githubToken = options.githubToken\n consola.info(\"Using provided GitHub token\")\n }\n\n await ensurePaths()\n await cacheVSCodeVersion()\n\n if (!options.githubToken) {\n await setupGitHubToken()\n }\n\n await setupCopilotToken()\n await cacheModels()\n\n consola.info(\n `Available models: \\n${state.models?.data.map((model) => `- ${model.id}`).join(\"\\n\")}`,\n )\n\n const serverUrl = `http://localhost:${options.port}`\n\n if (options.claudeCode) {\n invariant(state.models, \"Models should be loaded by now\")\n\n const selectedModel = await consola.prompt(\n \"Select a model to use with Claude Code\",\n {\n type: \"select\",\n options: state.models.data.map((model) => model.id),\n },\n )\n\n const selectedSmallModel = await consola.prompt(\n \"Select a small model to use with Claude Code\",\n {\n type: \"select\",\n options: state.models.data.map((model) => model.id),\n },\n )\n\n const command = generateEnvScript(\n {\n ANTHROPIC_BASE_URL: serverUrl,\n ANTHROPIC_AUTH_TOKEN: \"dummy\",\n ANTHROPIC_MODEL: selectedModel,\n ANTHROPIC_DEFAULT_SONNET_MODEL: selectedModel,\n ANTHROPIC_SMALL_FAST_MODEL: selectedSmallModel,\n ANTHROPIC_DEFAULT_HAIKU_MODEL: selectedSmallModel,\n DISABLE_NON_ESSENTIAL_MODEL_CALLS: \"1\",\n CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: \"1\",\n },\n \"claude\",\n )\n\n try {\n clipboard.writeSync(command)\n consola.success(\"Copied Claude Code command to clipboard!\")\n } catch {\n consola.warn(\n \"Failed to copy to clipboard. Here is the Claude Code command:\",\n )\n consola.log(command)\n }\n }\n\n consola.box(\n `🌐 Usage Viewer: https://voidsteed.github.io/copilot-proxy-api/dashboard.html?endpoint=${serverUrl}/usage`,\n )\n\n serve({\n fetch: server.fetch as ServerHandler,\n port: options.port,\n // Bun's HTTP server defaults to a 10s idleTimeout, which closes the\n // client socket while we're still waiting on Copilot's upstream\n // response (Copilot can take 10-60s for large opus requests). Setting\n // to 0 disables the idle timeout entirely. Claude Code reports this\n // as \"API Error: The socket connection was closed unexpectedly\".\n bun: { idleTimeout: 0 },\n })\n}\n\nexport const start = defineCommand({\n meta: {\n name: \"start\",\n description: \"Start the Copilot API server\",\n },\n args: {\n port: {\n alias: \"p\",\n type: \"string\",\n default: \"4141\",\n description: \"Port to listen on\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n \"account-type\": {\n alias: \"a\",\n type: \"string\",\n default: \"individual\",\n description: \"Account type to use (individual, business, enterprise)\",\n },\n manual: {\n type: \"boolean\",\n default: false,\n description: \"Enable manual request approval\",\n },\n \"rate-limit\": {\n alias: \"r\",\n type: \"string\",\n description: \"Rate limit in seconds between requests\",\n },\n wait: {\n alias: \"w\",\n type: \"boolean\",\n default: false,\n description:\n \"Wait instead of error when rate limit is hit. Has no effect if rate limit is not set\",\n },\n \"github-token\": {\n alias: \"g\",\n type: \"string\",\n description:\n \"Provide GitHub token directly (must be generated using the `auth` subcommand)\",\n },\n \"claude-code\": {\n alias: \"c\",\n type: \"boolean\",\n default: false,\n description:\n \"Generate a command to launch Claude Code with Copilot API config\",\n },\n \"show-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show GitHub and Copilot tokens on fetch and refresh\",\n },\n \"proxy-env\": {\n type: \"boolean\",\n default: false,\n description: \"Initialize proxy from environment variables\",\n },\n \"api-key\": {\n type: \"string\",\n description:\n \"Require one of these comma-separated local API keys for proxy routes\",\n },\n },\n run({ args }) {\n const rateLimitRaw = args[\"rate-limit\"]\n const rateLimit =\n rateLimitRaw === undefined ? undefined : Number.parseInt(rateLimitRaw, 10)\n\n return runServer({\n port: Number.parseInt(args.port, 10),\n verbose: args.verbose,\n accountType: args[\"account-type\"],\n manual: args.manual,\n rateLimit,\n rateLimitWait: args.wait,\n githubToken: args[\"github-token\"],\n claudeCode: args[\"claude-code\"],\n showToken: args[\"show-token\"],\n proxyEnv: args[\"proxy-env\"],\n apiKey: args[\"api-key\"],\n })\n },\n})\n","#!/usr/bin/env node\n\nimport { defineCommand, runMain } from \"citty\"\n\nimport { auth } from \"./auth\"\nimport { checkUsage } from \"./check-usage\"\nimport { debug } from \"./debug\"\nimport { start } from \"./start\"\n\nconst main = defineCommand({\n meta: {\n name: \"copilot-proxy-api\",\n description:\n \"A wrapper around GitHub Copilot API to make it OpenAI compatible, making it usable for other tools.\",\n },\n subCommands: { auth, start, \"check-usage\": checkUsage, debug },\n})\n\nawait runMain(main)\n"],"mappings":"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"}