@hsupu/copilot-api 0.8.0 → 0.8.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -110
- package/README.zh.md +39 -0
- package/config.example.yaml +341 -0
- package/dist/dist-B3gFwWti.mjs +428 -0
- package/dist/dist-B3gFwWti.mjs.map +1 -0
- package/dist/main.mjs +11129 -0
- package/dist/main.mjs.map +1 -0
- package/package.json +65 -27
- package/ui/history-v3/dist/assets/BaseSelect-CttLMFCN.js +1 -0
- package/ui/history-v3/dist/assets/BaseSelect-N-W6HPTu.css +1 -0
- package/ui/history-v3/dist/assets/DashboardPage-BYXNxjXb.css +1 -0
- package/ui/history-v3/dist/assets/DashboardPage-CgXivoWS.js +1 -0
- package/ui/history-v3/dist/assets/DetailPanel-CGpPxDDa.css +1 -0
- package/ui/history-v3/dist/assets/DetailPanel-EOQma5fZ.js +3 -0
- package/ui/history-v3/dist/assets/HistoryPage-PA0Yh3n3.css +1 -0
- package/ui/history-v3/dist/assets/HistoryPage-rhEb_UZG.js +1 -0
- package/ui/history-v3/dist/assets/LogsPage-BuPou1cg.css +1 -0
- package/ui/history-v3/dist/assets/LogsPage-CNXQuqMj.js +1 -0
- package/ui/history-v3/dist/assets/ModelsPage-Bpi7Y9GS.js +1 -0
- package/ui/history-v3/dist/assets/ModelsPage-F2KTxq2i.css +1 -0
- package/ui/history-v3/dist/assets/ProgressBar-6xzx-ZSc.js +1 -0
- package/ui/history-v3/dist/assets/ProgressBar-CtfiTXLy.css +1 -0
- package/ui/history-v3/dist/assets/UsagePage-COyq-DOU.css +1 -0
- package/ui/history-v3/dist/assets/UsagePage-CZfgTYCP.js +1 -0
- package/ui/history-v3/dist/assets/VChip-9UyCCNyg.js +1 -0
- package/ui/history-v3/dist/assets/VChip-B_fbAfwz.css +1 -0
- package/ui/history-v3/dist/assets/VDashboardPage-DXtj4agW.js +1 -0
- package/ui/history-v3/dist/assets/VDashboardPage-axfQtTiR.css +1 -0
- package/ui/history-v3/dist/assets/VDivider-D8zdArq0.js +1 -0
- package/ui/history-v3/dist/assets/VDivider-DITF6qCr.css +1 -0
- package/ui/history-v3/dist/assets/VHistoryPage-Dj0qUmWz.js +1 -0
- package/ui/history-v3/dist/assets/VHistoryPage-DqpLWYXo.css +1 -0
- package/ui/history-v3/dist/assets/VList-Bf0AJT_N.css +1 -0
- package/ui/history-v3/dist/assets/VList-Ct6gdZ-F.js +1 -0
- package/ui/history-v3/dist/assets/VLogsPage-BOA_17HS.js +1 -0
- package/ui/history-v3/dist/assets/VLogsPage-Dr3my9y3.css +1 -0
- package/ui/history-v3/dist/assets/VModelsPage-Bkon7sFs.css +1 -0
- package/ui/history-v3/dist/assets/VModelsPage-D-IbiiwR.js +1 -0
- package/ui/history-v3/dist/assets/VSpacer-DfbUir7X.css +1 -0
- package/ui/history-v3/dist/assets/VSpacer-F4vloCsf.js +1 -0
- package/ui/history-v3/dist/assets/VTable-B4qROCQu.js +1 -0
- package/ui/history-v3/dist/assets/VTable-BTui1tPX.css +1 -0
- package/ui/history-v3/dist/assets/VTooltip-9iBP-JhF.js +1 -0
- package/ui/history-v3/dist/assets/VTooltip-C1DKovoh.css +1 -0
- package/ui/history-v3/dist/assets/VUsagePage-B8WkBKET.js +1 -0
- package/ui/history-v3/dist/assets/VUsagePage-Se7R-H-Y.css +1 -0
- package/ui/history-v3/dist/assets/index-B8CP-fZd.css +1 -0
- package/ui/history-v3/dist/assets/index-cupXJxSz.js +18 -0
- package/ui/history-v3/dist/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
- package/ui/history-v3/dist/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
- package/ui/history-v3/dist/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
- package/ui/history-v3/dist/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
- package/ui/history-v3/dist/assets/useFormatters-BBIZmSf2.js +1 -0
- package/ui/history-v3/dist/assets/useInjectedHistoryStore-Dx7UlhLw.js +1 -0
- package/ui/history-v3/dist/assets/useLogs-Bz9naVOB.js +1 -0
- package/ui/history-v3/dist/assets/usePolling-CRd-nhvF.js +1 -0
- package/ui/history-v3/dist/assets/vendor-CmGvxZwr.js +125 -0
- package/ui/history-v3/dist/assets/vue-Bmo88J5t.js +1 -0
- package/ui/history-v3/dist/index.html +14 -0
- package/dist/main.js +0 -4571
- package/dist/main.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.mjs","names":["DEFAULT_CONFIG","fsPromises","path","fs","estimateMessageTokens","cleanupMessages","smartCompressToolResults","findOptimalPreserveIndex","generateRemovedMessagesSummary","addCompressionNotice","createTruncationSystemContext","createTruncationMarker","calculateTokenLimit","smartCompressToolResults","addCompressionNotice","findOptimalPreserveIndex","estimateMessageTokens","cleanupMessages","generateRemovedMessagesSummary","createTruncationSystemContext","createTruncationMarker","createTruncationMarker","createTruncationMarker","packageJson.version","packageJson.version"],"sources":["../src/lib/state.ts","../src/lib/utils.ts","../src/lib/ws/broadcast.ts","../src/lib/ws/adapter.ts","../src/lib/history/state.ts","../src/lib/history/stats.ts","../src/lib/history/entries.ts","../src/lib/history/queries.ts","../src/lib/history/sessions.ts","../src/lib/history/memory-pressure.ts","../src/lib/config/paths.ts","../src/lib/config/config.ts","../src/lib/proxy.ts","../src/lib/error/http-error.ts","../src/lib/error/parsing.ts","../src/lib/error/utils.ts","../src/lib/error/classify.ts","../src/lib/error/forward.ts","../src/lib/copilot-api.ts","../src/lib/token/copilot-client.ts","../src/lib/token/copilot-token-manager.ts","../src/lib/token/github-client.ts","../src/lib/token/providers/base.ts","../src/lib/token/providers/cli.ts","../src/lib/token/providers/file.ts","../src/lib/token/providers/device-auth.ts","../src/lib/token/providers/env.ts","../src/lib/token/github-token-manager.ts","../src/lib/token/index.ts","../src/auth.ts","../src/check-usage.ts","../src/lib/fetch-utils.ts","../src/lib/models/client.ts","../src/debug.ts","../src/lib/adaptive-rate-limiter.ts","../src/lib/models/resolver.ts","../src/lib/context/request.ts","../src/lib/context/manager.ts","../src/lib/shutdown.ts","../src/lib/tui/tracker.ts","../src/lib/tui/middleware.ts","../src/lib/tui/format.ts","../src/lib/tui/console-renderer.ts","../src/lib/tui/index.ts","../src/list-claude-code.ts","../src/logout.ts","../src/setup-claude-code.ts","../src/lib/serve.ts","../package.json","../src/lib/system-prompt/override.ts","../src/lib/system-prompt/reminder.ts","../src/lib/auto-truncate/engine.ts","../src/lib/context/error-persistence.ts","../src/lib/context/consumers.ts","../src/lib/models/endpoint.ts","../src/lib/openai/responses-conversion.ts","../src/lib/openai/responses-stream-accumulator.ts","../src/lib/request/pipeline.ts","../src/lib/request/response.ts","../src/lib/request/recording.ts","../src/lib/stream.ts","../src/lib/openai/request-preparation.ts","../src/lib/openai/responses-client.ts","../src/lib/request/strategies/network-retry.ts","../src/lib/request/strategies/token-refresh.ts","../src/routes/responses/pipeline.ts","../src/routes/responses/ws.ts","../src/lib/models/tokenizer.ts","../src/lib/openai/orphan-filter.ts","../src/lib/openai/auto-truncate/token-counting.ts","../src/lib/openai/auto-truncate/truncation.ts","../src/lib/openai/auto-truncate.ts","../src/lib/openai/chat-completions-client.ts","../src/lib/openai/sanitize.ts","../src/lib/openai/stream-accumulator.ts","../src/lib/request/payload.ts","../src/lib/request/strategies/auto-truncate.ts","../src/lib/request/truncation.ts","../src/routes/chat-completions/handler.ts","../src/routes/chat-completions/route.ts","../src/routes/config/route.ts","../src/lib/openai/embeddings.ts","../src/routes/embeddings/route.ts","../src/routes/event-logging/route.ts","../src/routes/history/handler.ts","../src/routes/history/route.ts","../src/routes/logs/route.ts","../src/types/api/anthropic.ts","../src/lib/anthropic/thinking-immutability.ts","../src/lib/anthropic/auto-truncate/tool-utils.ts","../src/lib/anthropic/auto-truncate/token-counting.ts","../src/lib/anthropic/sanitize/content-blocks.ts","../src/lib/anthropic/sanitize/deduplicate-tool-calls.ts","../src/lib/anthropic/sanitize/read-tool-result-tags.ts","../src/lib/anthropic/sanitize/result.ts","../src/lib/anthropic/sanitize/text-blocks.ts","../src/lib/anthropic/sanitize/system-reminders.ts","../src/lib/anthropic/sanitize/system-prompt.ts","../src/lib/anthropic/sanitize/tool-blocks.ts","../src/lib/anthropic/sanitize.ts","../src/lib/anthropic/auto-truncate/truncation.ts","../src/lib/anthropic/auto-truncate.ts","../src/routes/messages/count-tokens-handler.ts","../src/lib/anthropic/feature-negotiation.ts","../src/lib/anthropic/features.ts","../src/lib/anthropic/message-tools.ts","../src/lib/anthropic/request-preparation.ts","../src/lib/anthropic/client.ts","../src/lib/anthropic/message-mapping.ts","../src/lib/anthropic/server-tool-filter.ts","../src/lib/anthropic/stream-accumulator.ts","../src/lib/anthropic/sse.ts","../src/lib/repetition-detector.ts","../src/lib/request/strategies/context-management-retry.ts","../src/lib/request/strategies/deferred-tool-retry.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/status/route.ts","../src/routes/token/route.ts","../src/routes/history/assets.ts","../src/routes/ui/route.ts","../src/routes/index.ts","../src/server.ts","../src/start.ts","../src/main.ts"],"sourcesContent":["import type { Model, ModelsResponse } from \"~/lib/models/client\"\n\nimport type { AdaptiveRateLimiterConfig } from \"./adaptive-rate-limiter\"\nimport type { CopilotTokenInfo, TokenInfo } from \"./token/types\"\n\n/**\n * Server-side context editing mode.\n * Controls how Anthropic's context_management trims older context when input grows large.\n * Mirrors VSCode Copilot Chat's `chat.anthropic.contextEditing.mode` setting.\n */\nexport type ContextEditingMode = \"off\" | \"clear-thinking\" | \"clear-tooluse\" | \"clear-both\"\n\n/** A compiled rewrite rule (regex pre-compiled from config string) */\nexport interface CompiledRewriteRule {\n /** Pattern to match (regex in regex mode, string in line mode) */\n from: RegExp | string\n /** Replacement string (supports $0, $1, etc. in regex mode) */\n to: string\n /** Match method: \"regex\" (default) or \"line\" */\n method?: \"regex\" | \"line\"\n /** Compiled regex for model name filtering. undefined = apply to all models. */\n modelPattern?: RegExp\n}\n\nexport interface State {\n readonly githubToken?: string\n readonly copilotToken?: string\n\n /** Token metadata (new token system) */\n readonly tokenInfo?: TokenInfo\n readonly copilotTokenInfo?: CopilotTokenInfo\n\n readonly accountType: \"individual\" | \"business\" | \"enterprise\"\n readonly models?: ModelsResponse\n /** O(1) lookup index: model ID → Model object. Rebuilt on cacheModels(). */\n readonly modelIndex: Map<string, Model>\n /** O(1) membership check: set of available model IDs. Rebuilt on cacheModels(). */\n readonly modelIds: Set<string>\n readonly vsCodeVersion?: string\n\n /** Show GitHub token in logs */\n readonly showGitHubToken: boolean\n readonly verbose: boolean\n\n /** Adaptive rate limiting configuration */\n readonly adaptiveRateLimitConfig?: Partial<AdaptiveRateLimiterConfig>\n\n /**\n * Auto-truncate: reactively truncate on limit errors and pre-check for known limits.\n * Enabled by default; disable with --no-auto-truncate.\n */\n readonly autoTruncate: boolean\n\n /**\n * Compress old tool results before truncating messages.\n * When enabled, large tool_result content is compressed to reduce context size.\n */\n readonly compressToolResultsBeforeTruncate: boolean\n\n /** Strip Anthropic server-side tools from requests when upstream doesn't support them */\n readonly stripServerTools: boolean\n\n /**\n * Treat any assistant message containing `thinking` or `redacted_thinking`\n * as fully immutable during client-side rewrites.\n *\n * Disabled by default. When enabled, sanitization / dedup / auto-truncate\n * keep those assistant messages byte-for-byte intact instead of editing\n * adjacent text or tool blocks.\n */\n readonly immutableThinkingMessages: boolean\n\n /**\n * Model name overrides: request model → target model.\n *\n * Override values can be full model names or short aliases (opus, sonnet, haiku).\n * If the target is not in available models, it's resolved as an alias.\n * Defaults to DEFAULT_MODEL_OVERRIDES; config.yaml `model.model_overrides` replaces entirely.\n */\n readonly modelOverrides: Record<string, string>\n\n /**\n * Deduplicate repeated tool calls: remove duplicate tool_use/tool_result pairs,\n * keeping only the last occurrence of each matching combination.\n *\n * - `false` — disabled (default)\n * - `\"input\"` — match by (tool_name, input); different results are still deduped\n * - `\"result\"` — match by (tool_name, input, result); only dedup when result is identical\n */\n readonly dedupToolCalls: false | \"input\" | \"result\"\n\n /**\n * Rewrite `<system-reminder>` tags in messages.\n *\n * - `false` — disabled, keep all tags unchanged (default)\n * - `true` — remove ALL system-reminder tags\n * - `Array<CompiledRewriteRule>` — rewrite rules evaluated top-down, first match wins:\n * - If replacement produces the original content → keep tag unchanged\n * - If replacement produces an empty string → remove the tag\n * - Otherwise → replace tag content with the result\n */\n readonly rewriteSystemReminders: boolean | Array<CompiledRewriteRule>\n\n /**\n * Strip injected `<system-reminder>` tags from Read tool results.\n * Reduces context bloat from repeated system reminders in file content.\n * Disabled by default; enable with config anthropic.strip_read_tool_result_tags.\n */\n readonly stripReadToolResultTags: boolean\n\n /**\n * Server-side context editing mode.\n * Controls how Anthropic's context_management trims older context when input grows large.\n *\n * - `\"off\"` — disabled (default). No context_management sent, no beta header added.\n * - `\"clear-thinking\"` — clear old thinking blocks, keeping the last N thinking turns.\n * - `\"clear-tooluse\"` — clear old tool_use/tool_result pairs when input_tokens exceed threshold.\n * - `\"clear-both\"` — apply both clear-thinking and clear-tooluse edits.\n */\n readonly contextEditingMode: ContextEditingMode\n\n /** Pre-compiled system prompt override rules from config.yaml */\n readonly systemPromptOverrides: Array<CompiledRewriteRule>\n\n /**\n * Maximum number of history entries to keep in memory.\n * 0 = unlimited. Default: 200.\n */\n readonly historyLimit: number\n\n /**\n * Minimum number of history entries to keep even under memory pressure.\n * The memory pressure monitor will never evict below this floor.\n * Default: 50.\n */\n readonly historyMinEntries: number\n\n /**\n * Fetch timeout in seconds.\n * Time from request start to receiving HTTP response headers.\n * Applies to both streaming and non-streaming requests.\n * 0 = no timeout (rely on upstream gateway timeout).\n */\n readonly fetchTimeout: number\n\n /**\n * Stream idle timeout in seconds.\n * Maximum time to wait between consecutive SSE events during streaming.\n * Aborts the stream if no event arrives within this window.\n * Applies to all streaming paths (Anthropic, Chat Completions, Responses).\n * 0 = no idle timeout. Default: 300.\n */\n readonly streamIdleTimeout: number\n\n /**\n * Shutdown Phase 2 timeout in seconds.\n * Wait for in-flight requests to complete naturally before sending abort signal.\n * Default: 60.\n */\n readonly shutdownGracefulWait: number\n\n /**\n * Shutdown Phase 3 timeout in seconds.\n * After abort signal, wait for handlers to wrap up before force-closing.\n * Default: 120.\n */\n readonly shutdownAbortWait: number\n\n /**\n * Maximum age of an active request before the stale reaper forces it to fail (seconds).\n * Requests exceeding this age are assumed stuck and cleaned up.\n * 0 = disabled. Default: 600 (10 minutes).\n */\n readonly staleRequestMaxAge: number\n\n /**\n * Normalize function call IDs in Responses API input.\n * Converts `call_` prefixed IDs (Chat Completions format) to `fc_` prefixed IDs\n * (Responses API format) before forwarding to upstream.\n *\n * Useful when clients send conversation history containing tool call IDs\n * generated by Chat Completions API to the Responses API endpoint.\n *\n * Enabled by default; disable with config openai-responses.normalize_call_ids: false.\n */\n readonly normalizeResponsesCallIds: boolean\n}\n\ntype MutableState = {\n -readonly [K in keyof State]: State[K]\n}\n\nexport type StateSnapshot = MutableState\n\n/** Epoch ms when the server started (set once in runServer) */\nexport let serverStartTime = 0\n\n/** Set the server start time (called once from runServer) */\nexport function setServerStartTime(ts: number): void {\n serverStartTime = ts\n}\n\nfunction updateState(patch: Partial<MutableState>): void {\n Object.assign(mutableState, patch)\n}\n\nfunction cloneModels(models: ModelsResponse | undefined): ModelsResponse | undefined {\n return models ? { ...models, data: [...models.data] } : undefined\n}\n\nfunction cloneRewriteRules(\n rules: boolean | Array<CompiledRewriteRule>,\n): boolean | Array<CompiledRewriteRule> {\n return Array.isArray(rules) ? [...rules] : rules\n}\n\nfunction cloneState(source: MutableState): MutableState {\n return {\n ...source,\n adaptiveRateLimitConfig: source.adaptiveRateLimitConfig ? { ...source.adaptiveRateLimitConfig } : undefined,\n copilotTokenInfo: source.copilotTokenInfo ? { ...source.copilotTokenInfo } : undefined,\n modelIds: new Set(source.modelIds),\n modelIndex: new Map(source.modelIndex),\n modelOverrides: { ...source.modelOverrides },\n models: cloneModels(source.models),\n rewriteSystemReminders: cloneRewriteRules(source.rewriteSystemReminders),\n systemPromptOverrides: [...source.systemPromptOverrides],\n tokenInfo: source.tokenInfo ? { ...source.tokenInfo } : undefined,\n }\n}\n\nfunction cloneStatePatch(patch: Partial<MutableState>): Partial<MutableState> {\n const cloned: Partial<MutableState> = { ...patch }\n\n if (\"adaptiveRateLimitConfig\" in patch) {\n cloned.adaptiveRateLimitConfig = patch.adaptiveRateLimitConfig ? { ...patch.adaptiveRateLimitConfig } : undefined\n }\n if (\"copilotTokenInfo\" in patch) {\n cloned.copilotTokenInfo = patch.copilotTokenInfo ? { ...patch.copilotTokenInfo } : undefined\n }\n if (\"modelIds\" in patch) {\n cloned.modelIds = patch.modelIds ? new Set(patch.modelIds) : undefined\n }\n if (\"modelIndex\" in patch) {\n cloned.modelIndex = patch.modelIndex ? new Map(patch.modelIndex) : undefined\n }\n if (\"modelOverrides\" in patch) {\n cloned.modelOverrides = patch.modelOverrides ? { ...patch.modelOverrides } : undefined\n }\n if (\"models\" in patch) {\n cloned.models = cloneModels(patch.models)\n }\n if (\"rewriteSystemReminders\" in patch) {\n cloned.rewriteSystemReminders =\n patch.rewriteSystemReminders === undefined ? undefined : cloneRewriteRules(patch.rewriteSystemReminders)\n }\n if (\"systemPromptOverrides\" in patch) {\n cloned.systemPromptOverrides = patch.systemPromptOverrides ? [...patch.systemPromptOverrides] : undefined\n }\n if (\"tokenInfo\" in patch) {\n cloned.tokenInfo = patch.tokenInfo ? { ...patch.tokenInfo } : undefined\n }\n\n return cloned\n}\n\nexport function setGitHubToken(githubToken: string | undefined): void {\n updateState({ githubToken })\n}\n\nexport function setCopilotToken(copilotToken: string | undefined): void {\n updateState({ copilotToken })\n}\n\nexport function setTokenState(patch: Partial<Pick<MutableState, \"tokenInfo\" | \"copilotTokenInfo\">>): void {\n updateState(patch)\n}\n\nexport function setCliState(\n patch: Partial<Pick<MutableState, \"accountType\" | \"showGitHubToken\" | \"autoTruncate\" | \"verbose\">>,\n): void {\n updateState(patch)\n}\n\nexport function setVSCodeVersion(vsCodeVersion: string | undefined): void {\n updateState({ vsCodeVersion })\n}\n\nexport function setModels(models: ModelsResponse | undefined): void {\n updateState({ models })\n rebuildModelIndex()\n}\n\nexport function setAnthropicBehavior(\n patch: Partial<\n Pick<\n MutableState,\n | \"stripServerTools\"\n | \"immutableThinkingMessages\"\n | \"dedupToolCalls\"\n | \"stripReadToolResultTags\"\n | \"contextEditingMode\"\n | \"rewriteSystemReminders\"\n | \"systemPromptOverrides\"\n | \"compressToolResultsBeforeTruncate\"\n >\n >,\n): void {\n updateState(patch)\n}\n\nexport function setModelOverrides(modelOverrides: Record<string, string>): void {\n updateState({ modelOverrides })\n}\n\nexport function setHistoryConfig(patch: Partial<Pick<MutableState, \"historyLimit\" | \"historyMinEntries\">>): void {\n updateState(patch)\n}\n\nexport function setShutdownConfig(\n patch: Partial<Pick<MutableState, \"shutdownGracefulWait\" | \"shutdownAbortWait\">>,\n): void {\n updateState(patch)\n}\n\nexport function setTimeoutConfig(\n patch: Partial<Pick<MutableState, \"fetchTimeout\" | \"streamIdleTimeout\" | \"staleRequestMaxAge\">>,\n): void {\n updateState(patch)\n}\n\nexport function setResponsesConfig(patch: Partial<Pick<MutableState, \"normalizeResponsesCallIds\">>): void {\n updateState(patch)\n}\n\n/**\n * Capture a deep-enough clone of state for test restoration.\n * Tests should prefer this over direct mutation snapshots so State can stay readonly.\n */\nexport function snapshotStateForTests(): StateSnapshot {\n return cloneState(mutableState)\n}\n\n/**\n * Controlled test-only mutation path.\n * Keeps readonly State in application code while allowing tests to set fixtures.\n */\nexport function setStateForTests(patch: Partial<MutableState>): void {\n updateState(cloneStatePatch(patch))\n if (\"models\" in patch && !(\"modelIndex\" in patch) && !(\"modelIds\" in patch)) {\n rebuildModelIndex()\n }\n}\n\n/** Restore state from a snapshot captured by snapshotStateForTests(). */\nexport function restoreStateForTests(snapshot: StateSnapshot): void {\n updateState(cloneState(snapshot))\n}\n\n/**\n * Rebuild model lookup indexes from state.models.\n * Called by cacheModels() in production; call directly in tests after setting state.models.\n */\nexport function rebuildModelIndex(): void {\n const data = mutableState.models?.data ?? []\n updateState({\n modelIndex: new Map(data.map((m) => [m.id, m])),\n modelIds: new Set(data.map((m) => m.id)),\n })\n}\nexport const DEFAULT_MODEL_OVERRIDES: Record<string, string> = {\n opus: \"claude-opus-4.6\",\n sonnet: \"claude-sonnet-4.6\",\n haiku: \"claude-haiku-4.5\",\n}\n\nconst mutableState: MutableState = {\n accountType: \"individual\",\n autoTruncate: true,\n compressToolResultsBeforeTruncate: true,\n contextEditingMode: \"off\",\n stripServerTools: false,\n immutableThinkingMessages: false,\n dedupToolCalls: false,\n fetchTimeout: 300,\n historyLimit: 200,\n historyMinEntries: 50,\n modelIds: new Set(),\n modelIndex: new Map(),\n modelOverrides: { ...DEFAULT_MODEL_OVERRIDES },\n rewriteSystemReminders: false,\n showGitHubToken: false,\n shutdownAbortWait: 120,\n shutdownGracefulWait: 60,\n staleRequestMaxAge: 600,\n streamIdleTimeout: 300,\n systemPromptOverrides: [],\n stripReadToolResultTags: false,\n normalizeResponsesCallIds: true,\n verbose: false,\n}\n\nexport const state: State = mutableState\n","export const sleep = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms)\n })\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined\n\n/** Convert bytes to KB with rounding */\nexport function bytesToKB(bytes: number): number {\n return Math.round(bytes / 1024)\n}\n\n/** Generate unique ID (timestamp + random) */\nexport function generateId(randomLength = 7): string {\n return (\n Date.now().toString(36)\n + Math.random()\n .toString(36)\n .slice(2, 2 + randomLength)\n )\n}\n","/**\n * Topic-aware WebSocket broadcast system.\n *\n * Clients connect to `/ws` and optionally subscribe to topics via:\n * `{ type: \"subscribe\", topics: [\"history\", \"requests\", \"status\"] }`\n *\n * Clients with no subscriptions (empty topics set) receive ALL broadcasts.\n * Clients with subscriptions only receive messages for their subscribed topics.\n *\n * The `broadcastAlways` function ignores topics entirely (used for `connected`).\n */\n\nimport type { Hono } from \"hono\"\nimport type { UpgradeWebSocket } from \"hono/ws\"\n\nimport consola from \"consola\"\n\nimport type { EntrySummary, HistoryStats } from \"../history/store\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Known broadcast topics */\nexport type WSTopic = \"history\" | \"requests\" | \"status\"\n\n/** All WebSocket message types (history events + new event types) */\nexport type WSMessageType =\n | \"entry_added\"\n | \"entry_updated\"\n | \"stats_updated\"\n | \"history_cleared\"\n | \"session_deleted\"\n | \"connected\"\n | \"active_request_changed\"\n | \"rate_limiter_changed\"\n | \"shutdown_phase_changed\"\n\n/** A WebSocket message sent to connected clients */\nexport interface WSMessage {\n type: WSMessageType\n data: unknown\n timestamp: number\n}\n\n/** Client subscription message from the frontend */\ninterface SubscribeMessage {\n type: \"subscribe\"\n topics: Array<string>\n}\n\n/** Internal representation of a connected WebSocket client */\ninterface WSClient {\n ws: WebSocket\n /** Topics this client is subscribed to. Empty = receive all broadcasts. */\n topics: Set<string>\n}\n\n// ============================================================================\n// Client management\n// ============================================================================\n\n/** Connected clients indexed by their raw WebSocket instance */\nconst clients = new Map<WebSocket, WSClient>()\n\n/**\n * Factory for building the `connected` message data.\n * Set by start.ts after RequestContextManager is initialized.\n * Returns active requests snapshot for the connected event.\n */\nlet connectedDataFactory: (() => Array<unknown>) | null = null\n\n/** Set the factory that provides active requests snapshot for connected events */\nexport function setConnectedDataFactory(factory: () => Array<unknown>): void {\n connectedDataFactory = factory\n}\n\n/** Register a new WebSocket client (starts with no topic subscriptions = receive all) */\nexport function addClient(ws: WebSocket): void {\n clients.set(ws, { ws, topics: new Set() })\n\n const activeRequests = connectedDataFactory?.() ?? []\n\n // Send connected confirmation to the newly connected client only\n const msg: WSMessage = {\n type: \"connected\",\n data: { clientCount: clients.size, activeRequests },\n timestamp: Date.now(),\n }\n ws.send(JSON.stringify(msg))\n}\n\n/** Unregister a WebSocket client */\nexport function removeClient(ws: WebSocket): void {\n clients.delete(ws)\n}\n\n/** Get the number of currently connected WebSocket clients */\nexport function getClientCount(): number {\n return clients.size\n}\n\n/** Close all connected WebSocket clients */\nexport function closeAllClients(): void {\n for (const { ws } of clients.values()) {\n try {\n ws.close(1001, \"Server shutting down\")\n } catch {\n // Ignore errors during shutdown\n }\n }\n clients.clear()\n}\n\n/** Handle an incoming message from a client (topic subscription) */\nexport function handleClientMessage(ws: WebSocket, data: string): void {\n try {\n const parsed = JSON.parse(data) as unknown\n if (!isSubscribeMessage(parsed)) return\n\n const client = clients.get(ws)\n if (!client) return\n\n // Replace topics entirely — immutable update of the Set\n client.topics = new Set(parsed.topics)\n consola.debug(`[WS] Client subscribed to topics: [${[...client.topics].join(\", \")}]`)\n } catch {\n // Ignore malformed messages\n }\n}\n\n// ============================================================================\n// Broadcast\n// ============================================================================\n\n/**\n * Broadcast a message to clients subscribed to a specific topic.\n *\n * - Clients with no subscriptions (empty topics) receive the message (wildcard).\n * - Clients subscribed to the given topic receive the message.\n * - Clients subscribed to other topics (but not this one) are skipped.\n */\nexport function broadcast(message: WSMessage, topic: WSTopic): void {\n if (clients.size === 0) return\n\n const data = JSON.stringify(message)\n for (const [rawWs, client] of clients) {\n // Skip clients that have explicit subscriptions but not this topic\n if (client.topics.size > 0 && !client.topics.has(topic)) continue\n\n try {\n if (rawWs.readyState === WebSocket.OPEN) {\n rawWs.send(data)\n } else {\n // Remove clients that are no longer open (CLOSING, CLOSED)\n clients.delete(rawWs)\n }\n } catch (error) {\n consola.debug(\"WebSocket send failed, removing client:\", error)\n clients.delete(rawWs)\n }\n }\n}\n\n/**\n * Broadcast a message to ALL clients regardless of their topic subscriptions.\n * Used for connection-level messages like `connected`.\n */\nexport function broadcastAlways(message: WSMessage): void {\n if (clients.size === 0) return\n\n const data = JSON.stringify(message)\n for (const [rawWs] of clients) {\n try {\n if (rawWs.readyState === WebSocket.OPEN) {\n rawWs.send(data)\n } else {\n clients.delete(rawWs)\n }\n } catch (error) {\n consola.debug(\"WebSocket send failed, removing client:\", error)\n clients.delete(rawWs)\n }\n }\n}\n\n// ============================================================================\n// History notify functions (topic: \"history\")\n// ============================================================================\n\n/** Called when a new entry is recorded */\nexport function notifyEntryAdded(summary: EntrySummary): void {\n if (clients.size === 0) return\n\n broadcast(\n {\n type: \"entry_added\",\n data: summary,\n timestamp: Date.now(),\n },\n \"history\",\n )\n}\n\n/** Called when an entry is updated (e.g., response received) */\nexport function notifyEntryUpdated(summary: EntrySummary): void {\n if (clients.size === 0) return\n\n broadcast(\n {\n type: \"entry_updated\",\n data: summary,\n timestamp: Date.now(),\n },\n \"history\",\n )\n}\n\n/** Called when stats change */\nexport function notifyStatsUpdated(stats: HistoryStats): void {\n if (clients.size === 0) return\n\n broadcast(\n {\n type: \"stats_updated\",\n data: stats,\n timestamp: Date.now(),\n },\n \"history\",\n )\n}\n\n/** Called when all history is cleared */\nexport function notifyHistoryCleared(): void {\n if (clients.size === 0) return\n\n broadcast(\n {\n type: \"history_cleared\",\n data: null,\n timestamp: Date.now(),\n },\n \"history\",\n )\n}\n\n/** Called when a session is deleted */\nexport function notifySessionDeleted(sessionId: string): void {\n if (clients.size === 0) return\n\n broadcast(\n {\n type: \"session_deleted\",\n data: { sessionId },\n timestamp: Date.now(),\n },\n \"history\",\n )\n}\n\n// ============================================================================\n// New notify functions (exported but not yet called from trigger points)\n// ============================================================================\n\n/** Called when active request state changes (topic: \"requests\") */\nexport function notifyActiveRequestChanged(data: unknown): void {\n if (clients.size === 0) return\n\n broadcast(\n {\n type: \"active_request_changed\",\n data,\n timestamp: Date.now(),\n },\n \"requests\",\n )\n}\n\n/** Called when rate limiter state changes (topic: \"status\") */\nexport function notifyRateLimiterChanged(data: unknown): void {\n if (clients.size === 0) return\n\n broadcast(\n {\n type: \"rate_limiter_changed\",\n data,\n timestamp: Date.now(),\n },\n \"status\",\n )\n}\n\n/** Called when shutdown phase changes (topic: \"status\") */\nexport function notifyShutdownPhaseChanged(data: unknown): void {\n if (clients.size === 0) return\n\n broadcast(\n {\n type: \"shutdown_phase_changed\",\n data,\n timestamp: Date.now(),\n },\n \"status\",\n )\n}\n\n// ============================================================================\n// WebSocket route registration\n// ============================================================================\n\n/**\n * Initialize the global WebSocket endpoint at `/ws`.\n * Registers the route on the root Hono app using the shared WebSocket adapter.\n *\n * @param rootApp - The root Hono app instance\n * @param upgradeWs - Shared WebSocket upgrade function from createWebSocketAdapter\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function initWebSocket(rootApp: Hono, upgradeWs: UpgradeWebSocket<any>): void {\n rootApp.get(\n \"/ws\",\n upgradeWs(() => ({\n onOpen(_event, ws) {\n addClient(ws.raw as unknown as WebSocket)\n },\n onClose(_event, ws) {\n removeClient(ws.raw as unknown as WebSocket)\n },\n onMessage(event, ws) {\n const raw = typeof event.data === \"string\" ? event.data : String(event.data)\n handleClientMessage(ws.raw as unknown as WebSocket, raw)\n },\n onError(event, ws) {\n consola.debug(\"WebSocket error:\", event)\n removeClient(ws.raw as unknown as WebSocket)\n },\n })),\n )\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** Type guard for subscribe messages from the client */\nfunction isSubscribeMessage(value: unknown): value is SubscribeMessage {\n if (typeof value !== \"object\" || value === null) return false\n const msg = value as Record<string, unknown>\n return msg.type === \"subscribe\" && Array.isArray(msg.topics)\n}\n","/**\n * Shared WebSocket adapter for Node.js and Bun runtimes.\n *\n * Creates a single `@hono/node-ws` instance that all WebSocket routes share.\n * This prevents multiple `upgrade` listeners on the Node HTTP server, which\n * would cause ERR_STREAM_WRITE_AFTER_END when one handler consumes the socket\n * and others try to reject with `socket.end()`.\n */\n\nimport type { Hono } from \"hono\"\nimport type { UpgradeWebSocket } from \"hono/ws\"\nimport type { Server as NodeHttpServer } from \"node:http\"\n\nexport interface WebSocketAdapter {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n upgradeWebSocket: UpgradeWebSocket<any>\n /** Inject the single upgrade handler into a Node.js HTTP server (no-op for Bun) */\n injectWebSocket?: (server: NodeHttpServer) => void\n}\n\n/** Create a shared WebSocket adapter for the given Hono app */\nexport async function createWebSocketAdapter(app: Hono): Promise<WebSocketAdapter> {\n if (typeof globalThis.Bun !== \"undefined\") {\n const { upgradeWebSocket } = await import(\"hono/bun\")\n return { upgradeWebSocket }\n }\n\n const { createNodeWebSocket } = await import(\"@hono/node-ws\")\n const nodeWs = createNodeWebSocket({ app })\n return {\n upgradeWebSocket: nodeWs.upgradeWebSocket,\n injectWebSocket: (server) => nodeWs.injectWebSocket(server),\n }\n}\n","import { generateId } from \"../utils\"\nimport type { EntrySummary, HistoryEntry, HistoryState, HistoryStats, Session } from \"./types\"\n\nexport const historyState: HistoryState = {\n enabled: false,\n entries: [],\n sessions: new Map(),\n currentSessionId: \"\",\n maxEntries: 200,\n}\n\nexport const historyIndexes = {\n entryIndex: new Map<string, HistoryEntry>(),\n summaryIndex: new Map<string, EntrySummary>(),\n sessionEntryCount: new Map<string, number>(),\n sessionModelsSet: new Map<string, Set<string>>(),\n sessionToolsSet: new Map<string, Set<string>>(),\n}\n\nexport const historyStatsCache: {\n dirty: boolean\n stats: HistoryStats | null\n} = {\n dirty: true,\n stats: null,\n}\n\nexport function resetHistoryIndexes(): void {\n historyIndexes.entryIndex.clear()\n historyIndexes.summaryIndex.clear()\n historyIndexes.sessionEntryCount.clear()\n historyIndexes.sessionModelsSet.clear()\n historyIndexes.sessionToolsSet.clear()\n}\n\nexport function invalidateHistoryStats(): void {\n historyStatsCache.dirty = true\n historyStatsCache.stats = null\n}\n\nfunction buildSearchText(entry: HistoryEntry): string {\n const parts: Array<string> = []\n\n if (entry.request.model) parts.push(entry.request.model)\n if (entry.response?.model) parts.push(entry.response.model)\n if (entry.response?.error) parts.push(entry.response.error)\n\n if (entry.request.system) {\n if (typeof entry.request.system === \"string\") {\n parts.push(entry.request.system.slice(0, 500))\n } else {\n for (const block of entry.request.system) {\n parts.push(block.text.slice(0, 200))\n }\n }\n }\n\n if (entry.request.messages) {\n for (const msg of entry.request.messages) {\n if (typeof msg.content === \"string\") {\n parts.push(msg.content.slice(0, 200))\n } else if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (block.type === \"text\" && block.text) {\n parts.push((block.text as string).slice(0, 200))\n } else if (block.type === \"tool_use\") {\n if (block.name) parts.push(block.name as string)\n if (block.input) {\n const inputStr = typeof block.input === \"string\" ? block.input : JSON.stringify(block.input)\n parts.push(inputStr.slice(0, 500))\n }\n } else if (block.type === \"tool_result\" && block.content) {\n const contentStr = typeof block.content === \"string\" ? block.content : JSON.stringify(block.content)\n parts.push(contentStr.slice(0, 500))\n } else if (block.type === \"thinking\" && block.thinking) {\n parts.push((block.thinking as string).slice(0, 200))\n }\n }\n }\n\n if (msg.tool_calls) {\n for (const toolCall of msg.tool_calls) {\n if (toolCall.function.name) parts.push(toolCall.function.name)\n if (toolCall.function.arguments) parts.push(toolCall.function.arguments.slice(0, 500))\n }\n }\n }\n }\n\n if (entry.response?.content) {\n const responseContent = entry.response.content\n if (typeof responseContent.content === \"string\") {\n parts.push(responseContent.content.slice(0, 200))\n } else if (Array.isArray(responseContent.content)) {\n for (const block of responseContent.content) {\n if (block.type === \"text\" && block.text) {\n parts.push((block.text as string).slice(0, 200))\n } else if (block.type === \"tool_use\" && block.name) {\n parts.push(block.name as string)\n }\n }\n }\n }\n\n return parts.join(\" \").toLowerCase()\n}\n\nexport function ensureSearchText(id: string): string {\n const summary = historyIndexes.summaryIndex.get(id)\n if (!summary) return \"\"\n if (summary.searchText === \"\") {\n const entry = historyIndexes.entryIndex.get(id)\n if (entry) {\n summary.searchText = buildSearchText(entry)\n }\n }\n return summary.searchText\n}\n\nexport function initHistory(enabled: boolean, maxEntries: number): void {\n historyState.enabled = enabled\n historyState.maxEntries = maxEntries\n historyState.entries = []\n historyState.sessions = new Map()\n historyState.currentSessionId = enabled ? generateId() : \"\"\n resetHistoryIndexes()\n invalidateHistoryStats()\n}\n\nexport function setHistoryMaxEntries(limit: number): void {\n historyState.maxEntries = limit\n}\n\nexport function isHistoryEnabled(): boolean {\n return historyState.enabled\n}\n\nexport function resetHistoryStateForClear(): void {\n historyState.entries = []\n historyState.sessions = new Map<string, Session>()\n historyState.currentSessionId = generateId()\n resetHistoryIndexes()\n invalidateHistoryStats()\n}\n","import { historyStatsCache, historyState } from \"./state\"\nimport type { HistoryStats } from \"./types\"\n\nfunction formatLocalTimestamp(ts: number): string {\n const date = new Date(ts)\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, \"0\")\n const day = String(date.getDate()).padStart(2, \"0\")\n const hour = String(date.getHours()).padStart(2, \"0\")\n const minute = String(date.getMinutes()).padStart(2, \"0\")\n const second = String(date.getSeconds()).padStart(2, \"0\")\n return `${year}-${month}-${day} ${hour}:${minute}:${second}`\n}\n\nfunction escapeCsvValue(value: unknown): string {\n if (value === null || value === undefined) return \"\"\n const str = typeof value === \"string\" ? value : JSON.stringify(value)\n if (str.includes(\",\") || str.includes('\"') || str.includes(\"\\n\")) {\n return `\"${str.replaceAll('\"', '\"\"')}\"`\n }\n return str\n}\n\nexport function getStats(): HistoryStats {\n if (!historyStatsCache.dirty && historyStatsCache.stats) return historyStatsCache.stats\n\n const entries = historyState.entries\n const modelDist: Record<string, number> = {}\n const endpointDist: Record<string, number> = {}\n const hourlyActivity: Record<string, number> = {}\n\n let totalInput = 0\n let totalOutput = 0\n let totalDuration = 0\n let durationCount = 0\n let successCount = 0\n let failCount = 0\n\n for (const entry of entries) {\n const model = entry.response?.model || entry.request.model || \"unknown\"\n modelDist[model] = (modelDist[model] || 0) + 1\n endpointDist[entry.endpoint] = (endpointDist[entry.endpoint] || 0) + 1\n\n const date = new Date(entry.timestamp)\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, \"0\")\n const day = String(date.getDate()).padStart(2, \"0\")\n const hour = String(date.getHours()).padStart(2, \"0\")\n const hourKey = `${year}-${month}-${day}T${hour}`\n hourlyActivity[hourKey] = (hourlyActivity[hourKey] || 0) + 1\n\n if (entry.response) {\n if (entry.response.success) {\n successCount++\n } else {\n failCount++\n }\n totalInput += entry.response.usage.input_tokens\n totalOutput += entry.response.usage.output_tokens\n }\n\n if (entry.durationMs) {\n totalDuration += entry.durationMs\n durationCount++\n }\n }\n\n const recentActivity = Object.entries(hourlyActivity)\n .sort(([a], [b]) => a.localeCompare(b))\n .slice(-24)\n .map(([hour, count]) => ({ hour, count }))\n\n const stats: HistoryStats = {\n totalRequests: entries.length,\n successfulRequests: successCount,\n failedRequests: failCount,\n totalInputTokens: totalInput,\n totalOutputTokens: totalOutput,\n averageDurationMs: durationCount > 0 ? totalDuration / durationCount : 0,\n modelDistribution: modelDist,\n endpointDistribution: endpointDist,\n recentActivity,\n activeSessions: historyState.sessions.size,\n }\n\n historyStatsCache.dirty = false\n historyStatsCache.stats = stats\n return stats\n}\n\nexport function exportHistory(format: \"json\" | \"csv\" = \"json\"): string {\n if (format === \"json\") {\n return JSON.stringify(\n {\n sessions: Array.from(historyState.sessions.values()),\n entries: historyState.entries,\n },\n null,\n 2,\n )\n }\n\n const headers = [\n \"id\",\n \"session_id\",\n \"timestamp\",\n \"endpoint\",\n \"request_model\",\n \"message_count\",\n \"stream\",\n \"success\",\n \"response_model\",\n \"input_tokens\",\n \"output_tokens\",\n \"duration_ms\",\n \"stop_reason\",\n \"error\",\n ]\n\n const rows = historyState.entries.map((entry) => [\n entry.id,\n entry.sessionId,\n formatLocalTimestamp(entry.timestamp),\n entry.endpoint,\n entry.request.model,\n entry.request.messages?.length,\n entry.request.stream,\n entry.response?.success,\n entry.response?.model,\n entry.response?.usage.input_tokens,\n entry.response?.usage.output_tokens,\n entry.durationMs,\n entry.response?.stop_reason,\n entry.response?.error,\n ])\n\n return [headers.join(\",\"), ...rows.map((row) => row.map((value) => escapeCsvValue(value)).join(\",\"))].join(\"\\n\")\n}\n","import { generateId } from \"../utils\"\nimport { notifyEntryAdded, notifyEntryUpdated, notifyHistoryCleared, notifyStatsUpdated } from \"../ws\"\nimport { historyIndexes, historyState, invalidateHistoryStats, resetHistoryIndexes } from \"./state\"\nimport { getStats } from \"./stats\"\nimport type { EntrySummary, HistoryEntry } from \"./types\"\n\n/** Extract a preview from the last user message (first 100 chars) */\nfunction extractPreviewText(entry: HistoryEntry): string {\n const messages = entry.request.messages\n if (!messages || messages.length === 0) return \"\"\n\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.role === \"tool\") continue\n if (msg.role !== \"user\") continue\n\n if (typeof msg.content === \"string\") {\n return msg.content.slice(0, 100)\n }\n if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (block.type === \"text\" && block.text) {\n return (block.text as string).slice(0, 100)\n }\n if (block.type === \"tool_result\") {\n break\n }\n }\n continue\n }\n break\n }\n\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.role === \"assistant\" && msg.tool_calls && msg.tool_calls.length > 0) {\n const names = msg.tool_calls.map((toolCall) => toolCall.function.name).join(\", \")\n return `[tool_call: ${names}]`.slice(0, 100)\n }\n if (msg.role === \"tool\") {\n return `[tool_result: ${msg.tool_call_id ?? msg.name ?? \"unknown\"}]`.slice(0, 100)\n }\n break\n }\n\n return \"\"\n}\n\nfunction toSummary(entry: HistoryEntry): EntrySummary {\n return {\n id: entry.id,\n sessionId: entry.sessionId,\n timestamp: entry.timestamp,\n endpoint: entry.endpoint,\n requestModel: entry.request.model,\n stream: entry.request.stream,\n messageCount: entry.request.messages?.length ?? 0,\n responseModel: entry.response?.model,\n responseSuccess: entry.response?.success,\n responseError: entry.response?.error,\n usage: entry.response?.usage,\n durationMs: entry.durationMs,\n previewText: extractPreviewText(entry),\n searchText: \"\",\n }\n}\n\nfunction updateSessionMetadata(entry: HistoryEntry): void {\n const session = historyState.sessions.get(entry.sessionId)\n if (!session) return\n\n const model = entry.request.model\n if (model) {\n const modelsSet = historyIndexes.sessionModelsSet.get(entry.sessionId)\n if (modelsSet && !modelsSet.has(model)) {\n modelsSet.add(model)\n session.models.push(model)\n }\n }\n\n if (entry.request.tools && entry.request.tools.length > 0) {\n if (!session.toolsUsed) {\n session.toolsUsed = []\n }\n let toolsSet = historyIndexes.sessionToolsSet.get(entry.sessionId)\n if (!toolsSet) {\n toolsSet = new Set(session.toolsUsed)\n historyIndexes.sessionToolsSet.set(entry.sessionId, toolsSet)\n }\n for (const tool of entry.request.tools) {\n if (!toolsSet.has(tool.name)) {\n toolsSet.add(tool.name)\n session.toolsUsed.push(tool.name)\n }\n }\n }\n}\n\nfunction removeOldestEntries(count: number): number {\n if (count <= 0 || historyState.entries.length === 0) return 0\n\n const actualCount = Math.min(count, historyState.entries.length)\n const removed = historyState.entries.splice(0, actualCount)\n for (const entry of removed) {\n historyIndexes.entryIndex.delete(entry.id)\n historyIndexes.summaryIndex.delete(entry.id)\n const sessionCount = (historyIndexes.sessionEntryCount.get(entry.sessionId) ?? 1) - 1\n if (sessionCount <= 0) {\n historyIndexes.sessionEntryCount.delete(entry.sessionId)\n historyIndexes.sessionModelsSet.delete(entry.sessionId)\n historyIndexes.sessionToolsSet.delete(entry.sessionId)\n historyState.sessions.delete(entry.sessionId)\n } else {\n historyIndexes.sessionEntryCount.set(entry.sessionId, sessionCount)\n }\n }\n\n if (removed.length > 0) {\n invalidateHistoryStats()\n }\n\n return removed.length\n}\n\nexport function evictOldestEntries(count: number): number {\n const evicted = removeOldestEntries(count)\n if (evicted > 0) {\n notifyStatsUpdated(getStats())\n }\n return evicted\n}\n\nexport function insertEntry(entry: HistoryEntry): void {\n if (!historyState.enabled) return\n\n const session = historyState.sessions.get(entry.sessionId)\n if (!session) return\n\n historyState.entries.push(entry)\n historyIndexes.entryIndex.set(entry.id, entry)\n session.requestCount++\n historyIndexes.sessionEntryCount.set(entry.sessionId, (historyIndexes.sessionEntryCount.get(entry.sessionId) ?? 0) + 1)\n\n updateSessionMetadata(entry)\n\n const summary = toSummary(entry)\n historyIndexes.summaryIndex.set(entry.id, summary)\n\n if (historyState.maxEntries > 0 && historyState.entries.length > historyState.maxEntries) {\n removeOldestEntries(historyState.entries.length - historyState.maxEntries)\n }\n\n invalidateHistoryStats()\n notifyEntryAdded(summary)\n notifyStatsUpdated(getStats())\n}\n\nexport function updateEntry(\n id: string,\n update: Partial<\n Pick<\n HistoryEntry,\n \"request\" | \"response\" | \"pipelineInfo\" | \"sseEvents\" | \"durationMs\" | \"effectiveRequest\" | \"wireRequest\" | \"attempts\"\n >\n >,\n): void {\n if (!historyState.enabled) return\n\n const entry = historyIndexes.entryIndex.get(id)\n if (!entry) return\n\n if (update.request) {\n entry.request = update.request\n updateSessionMetadata(entry)\n }\n if (update.response) entry.response = update.response\n if (update.pipelineInfo) entry.pipelineInfo = update.pipelineInfo\n if (update.durationMs !== undefined) entry.durationMs = update.durationMs\n if (update.sseEvents) entry.sseEvents = update.sseEvents\n if (update.effectiveRequest) entry.effectiveRequest = update.effectiveRequest\n if (update.wireRequest) entry.wireRequest = update.wireRequest\n if (update.attempts) entry.attempts = update.attempts\n\n if (update.response) {\n const session = historyState.sessions.get(entry.sessionId)\n if (session) {\n session.totalInputTokens += update.response.usage.input_tokens\n session.totalOutputTokens += update.response.usage.output_tokens\n session.lastActivity = Date.now()\n }\n }\n\n invalidateHistoryStats()\n const summary = toSummary(entry)\n historyIndexes.summaryIndex.set(entry.id, summary)\n notifyEntryUpdated(summary)\n notifyStatsUpdated(getStats())\n}\n\nexport function clearHistory(): void {\n historyState.entries = []\n historyState.sessions = new Map()\n historyState.currentSessionId = generateId()\n resetHistoryIndexes()\n invalidateHistoryStats()\n notifyHistoryCleared()\n notifyStatsUpdated(getStats())\n}\n","import { ensureSearchText, historyIndexes, historyState } from \"./state\"\nimport type { CursorResult, EntrySummary, HistoryEntry, HistoryResult, QueryOptions } from \"./types\"\n\nexport function getHistory(options: QueryOptions = {}): HistoryResult {\n const { cursor, limit = 50, model, endpoint, success, from, to, search, sessionId } = options\n\n let filtered = [...historyState.entries]\n\n if (sessionId) {\n filtered = filtered.filter((entry) => entry.sessionId === sessionId)\n }\n if (model) {\n const modelLower = model.toLowerCase()\n filtered = filtered.filter(\n (entry) =>\n entry.request.model?.toLowerCase().includes(modelLower)\n || entry.response?.model.toLowerCase().includes(modelLower),\n )\n }\n if (endpoint) {\n filtered = filtered.filter((entry) => entry.endpoint === endpoint)\n }\n if (success !== undefined) {\n filtered = filtered.filter((entry) => entry.response?.success === success)\n }\n if (from) {\n filtered = filtered.filter((entry) => entry.timestamp >= from)\n }\n if (to) {\n filtered = filtered.filter((entry) => entry.timestamp <= to)\n }\n if (search) {\n const searchLower = search.toLowerCase()\n filtered = filtered.filter((entry) => ensureSearchText(entry.id).includes(searchLower))\n }\n\n filtered.sort((a, b) => b.timestamp - a.timestamp)\n\n const total = filtered.length\n let startIdx = 0\n if (cursor) {\n const cursorIdx = filtered.findIndex((entry) => entry.id === cursor)\n if (cursorIdx !== -1) startIdx = cursorIdx + 1\n }\n\n const entries = filtered.slice(startIdx, startIdx + limit)\n return {\n entries,\n total,\n page: 1,\n limit,\n totalPages: Math.ceil(total / limit),\n }\n}\n\nexport function getEntry(id: string): HistoryEntry | undefined {\n return historyIndexes.entryIndex.get(id) ?? historyState.entries.find((entry) => entry.id === id)\n}\n\nexport function getSummary(id: string): EntrySummary | undefined {\n return historyIndexes.summaryIndex.get(id)\n}\n\nexport function getHistorySummaries(options: QueryOptions = {}): CursorResult<EntrySummary> {\n const { cursor, limit = 50, direction = \"older\", model, endpoint, success, from, to, search, sessionId } = options\n\n let summaries = Array.from(historyIndexes.summaryIndex.values())\n\n if (sessionId) summaries = summaries.filter((summary) => summary.sessionId === sessionId)\n if (model) {\n const modelLower = model.toLowerCase()\n summaries = summaries.filter(\n (summary) =>\n summary.requestModel?.toLowerCase().includes(modelLower)\n || summary.responseModel?.toLowerCase().includes(modelLower),\n )\n }\n if (endpoint) summaries = summaries.filter((summary) => summary.endpoint === endpoint)\n if (success !== undefined) summaries = summaries.filter((summary) => summary.responseSuccess === success)\n if (from) summaries = summaries.filter((summary) => summary.timestamp >= from)\n if (to) summaries = summaries.filter((summary) => summary.timestamp <= to)\n\n if (search) {\n const needle = search.toLowerCase()\n summaries = summaries.filter((summary) => ensureSearchText(summary.id).includes(needle))\n }\n\n summaries.sort((a, b) => b.timestamp - a.timestamp || b.id.localeCompare(a.id))\n\n const total = summaries.length\n let startIdx = 0\n if (cursor) {\n const cursorIdx = summaries.findIndex((summary) => summary.id === cursor)\n if (cursorIdx !== -1) {\n startIdx = direction === \"older\" ? cursorIdx + 1 : Math.max(0, cursorIdx - limit)\n }\n }\n\n const entries = summaries.slice(startIdx, startIdx + limit)\n const nextCursor = startIdx + limit < total ? (entries.at(-1)?.id ?? null) : null\n const prevCursor = startIdx > 0 ? (entries[0]?.id ?? null) : null\n\n return { entries, total, nextCursor, prevCursor }\n}\n","import { generateId } from \"../utils\"\nimport { notifySessionDeleted, notifyStatsUpdated } from \"../ws\"\nimport { historyIndexes, historyState, invalidateHistoryStats } from \"./state\"\nimport { getStats } from \"./stats\"\nimport type { CursorResult, EndpointType, HistoryEntry, Session, SessionResult } from \"./types\"\n\n/**\n * Get or create current session.\n * Currently treats all requests as belonging to one session per server lifetime,\n * since clients don't provide session identifiers yet.\n * TODO: When clients support session headers, use that to group requests.\n */\nexport function getCurrentSession(endpoint: EndpointType): string {\n if (historyState.currentSessionId) {\n const session = historyState.sessions.get(historyState.currentSessionId)\n if (session) {\n session.lastActivity = Date.now()\n if (!session.endpoints.includes(endpoint)) {\n session.endpoints.push(endpoint)\n }\n return historyState.currentSessionId\n }\n }\n\n const now = Date.now()\n const sessionId = generateId()\n historyState.currentSessionId = sessionId\n historyIndexes.sessionModelsSet.set(sessionId, new Set())\n historyIndexes.sessionToolsSet.set(sessionId, new Set())\n historyState.sessions.set(sessionId, {\n id: sessionId,\n startTime: now,\n lastActivity: now,\n requestCount: 0,\n totalInputTokens: 0,\n totalOutputTokens: 0,\n models: [],\n endpoints: [endpoint],\n })\n\n return sessionId\n}\n\nexport function getSessions(): SessionResult {\n const sessions = Array.from(historyState.sessions.values()).sort((a, b) => b.lastActivity - a.lastActivity)\n return {\n sessions,\n total: sessions.length,\n }\n}\n\nexport function getSession(id: string): Session | undefined {\n return historyState.sessions.get(id)\n}\n\nexport function getSessionEntries(sessionId: string, options: { cursor?: string; limit?: number } = {}): CursorResult<HistoryEntry> {\n const { cursor, limit = 50 } = options\n const all = historyState.entries.filter((entry) => entry.sessionId === sessionId).sort((a, b) => a.timestamp - b.timestamp)\n\n const total = all.length\n let startIdx = 0\n if (cursor) {\n const cursorIdx = all.findIndex((entry) => entry.id === cursor)\n if (cursorIdx !== -1) startIdx = cursorIdx + 1\n }\n\n const entries = all.slice(startIdx, startIdx + limit)\n const nextCursor = startIdx + limit < total ? (entries.at(-1)?.id ?? null) : null\n const prevCursor = startIdx > 0 ? (entries[0]?.id ?? null) : null\n\n return { entries, total, nextCursor, prevCursor }\n}\n\nexport function deleteSession(sessionId: string): boolean {\n if (!historyState.sessions.has(sessionId)) {\n return false\n }\n\n const remaining: Array<HistoryEntry> = []\n for (const entry of historyState.entries) {\n if (entry.sessionId === sessionId) {\n historyIndexes.entryIndex.delete(entry.id)\n historyIndexes.summaryIndex.delete(entry.id)\n } else {\n remaining.push(entry)\n }\n }\n\n historyState.entries = remaining\n historyState.sessions.delete(sessionId)\n historyIndexes.sessionEntryCount.delete(sessionId)\n historyIndexes.sessionModelsSet.delete(sessionId)\n historyIndexes.sessionToolsSet.delete(sessionId)\n invalidateHistoryStats()\n\n if (historyState.currentSessionId === sessionId) {\n historyState.currentSessionId = generateId()\n }\n\n notifySessionDeleted(sessionId)\n notifyStatsUpdated(getStats())\n return true\n}\n","/**\n * Memory pressure monitor — proactively evicts old history entries\n * when heap usage approaches the V8 heap limit, preventing OOM crashes.\n *\n * Graduated response:\n * 75–80% Warning: log only, no eviction\n * 80–90% High: evict entries, reduce maxEntries by 25%\n * 90%+ Critical: aggressive eviction, reduce maxEntries by 50%\n *\n * The monitor reduces maxEntries on each eviction event to prevent\n * re-accumulation. Reductions compound across successive events\n * (e.g. 200 → 150 → 112 → ...) until the floor is reached.\n */\n\nimport consola from \"consola\"\n\nimport { state } from \"~/lib/state\"\n\nimport { evictOldestEntries, historyState, setHistoryMaxEntries } from \"./store\"\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\n/** Polling interval in milliseconds */\nconst CHECK_INTERVAL_MS = 30_000\n\n/** Heap usage ratio thresholds */\nconst WARN_THRESHOLD = 0.75\nconst EVICT_THRESHOLD = 0.8\nconst CRITICAL_THRESHOLD = 0.9\n\n/** Minimum interval between warning logs (ms) */\nconst WARN_LOG_COOLDOWN_MS = 300_000\n\n// ============================================================================\n// Heap limit detection\n// ============================================================================\n\nlet resolvedHeapLimit: number | null = null\n\n/**\n * Get the V8 heap size limit.\n * Falls back to a conservative 512MB for non-V8 runtimes (e.g. Bun).\n */\nasync function resolveHeapLimit(): Promise<number> {\n if (resolvedHeapLimit !== null) return resolvedHeapLimit\n\n try {\n const v8 = await import(\"node:v8\")\n resolvedHeapLimit = v8.getHeapStatistics().heap_size_limit\n } catch {\n // Bun or other runtime without V8 internals\n resolvedHeapLimit = 512 * 1024 * 1024\n }\n\n return resolvedHeapLimit\n}\n\n// ============================================================================\n// Module state\n// ============================================================================\n\nlet timer: ReturnType<typeof setInterval> | null = null\nlet lastWarningTime = 0\nlet totalEvictedCount = 0\n\n// ============================================================================\n// Formatting helpers\n// ============================================================================\n\nfunction formatMB(bytes: number): string {\n return `${Math.round(bytes / 1024 / 1024)}MB`\n}\n\nfunction formatPct(ratio: number): string {\n return `${Math.round(ratio * 100)}%`\n}\n\n// ============================================================================\n// Monitor logic\n// ============================================================================\n\nasync function checkMemoryPressure(): Promise<void> {\n const heapLimit = await resolveHeapLimit()\n const { heapUsed } = process.memoryUsage()\n const ratio = heapUsed / heapLimit\n\n // Below warning threshold — all clear\n if (ratio < WARN_THRESHOLD) return\n\n const currentEntries = historyState.entries.length\n\n // Nothing to evict — only warn if pressure is severe\n if (currentEntries <= state.historyMinEntries) {\n if (ratio >= EVICT_THRESHOLD && Date.now() - lastWarningTime > WARN_LOG_COOLDOWN_MS) {\n lastWarningTime = Date.now()\n consola.warn(\n `[memory] Heap ${formatMB(heapUsed)}/${formatMB(heapLimit)} (${formatPct(ratio)}) — `\n + `only ${currentEntries} history entries remain. `\n + `Consider increasing --max-old-space-size`,\n )\n }\n return\n }\n\n // Warning zone (75–80%): log warning, no eviction\n if (ratio < EVICT_THRESHOLD) {\n if (Date.now() - lastWarningTime > WARN_LOG_COOLDOWN_MS) {\n lastWarningTime = Date.now()\n consola.warn(\n `[memory] Heap ${formatMB(heapUsed)}/${formatMB(heapLimit)} (${formatPct(ratio)}) — `\n + `approaching limit, ${currentEntries} history entries in memory`,\n )\n }\n return\n }\n\n // ── Eviction zone (80%+) ──\n\n lastWarningTime = Date.now()\n\n // Compute new maxEntries based on current max (compounds across events)\n const currentMax = historyState.maxEntries > 0 ? historyState.maxEntries : currentEntries\n let newMaxEntries: number\n\n if (ratio >= CRITICAL_THRESHOLD) {\n // Critical (90%+): halve the limit\n newMaxEntries = Math.max(state.historyMinEntries, Math.floor(currentMax * 0.5))\n } else {\n // High (80–90%): reduce by 25%\n newMaxEntries = Math.max(state.historyMinEntries, Math.floor(currentMax * 0.75))\n }\n\n const evictCount = Math.max(0, currentEntries - newMaxEntries)\n if (evictCount <= 0) return\n\n const evicted = evictOldestEntries(evictCount)\n totalEvictedCount += evicted\n\n if (newMaxEntries < (historyState.maxEntries > 0 ? historyState.maxEntries : Infinity)) {\n setHistoryMaxEntries(newMaxEntries)\n }\n\n const afterHeapUsed = process.memoryUsage().heapUsed\n\n consola.warn(\n `[memory] Evicted ${evicted} history entries due to memory pressure `\n + `(heap: ${formatMB(heapUsed)} → ${formatMB(afterHeapUsed)}/${formatMB(heapLimit)}, `\n + `entries: ${currentEntries} → ${currentEntries - evicted}, `\n + `max: ${newMaxEntries}). `\n + `Consider increasing --max-old-space-size or reducing history_limit in config.yaml`,\n )\n\n // Hint GC if exposed via --expose-gc (no-op otherwise)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ;(globalThis as any).gc?.()\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/** Start the memory pressure monitor (idempotent) */\nexport function startMemoryPressureMonitor(): void {\n if (timer) return\n\n timer = setInterval(() => {\n checkMemoryPressure().catch((error: unknown) => {\n consola.error(\"[memory] Error in memory pressure check:\", error)\n })\n }, CHECK_INTERVAL_MS)\n\n // Don't prevent process exit\n if (\"unref\" in timer) {\n timer.unref()\n }\n}\n\n/** Stop the memory pressure monitor */\nexport function stopMemoryPressureMonitor(): void {\n if (timer) {\n clearInterval(timer)\n timer = null\n }\n}\n\n/** Get memory pressure diagnostics */\nexport function getMemoryPressureStats(): {\n totalEvictedCount: number\n currentMaxEntries: number\n heapUsedMB: number\n heapLimitMB: number | null\n} {\n const { heapUsed } = process.memoryUsage()\n return {\n totalEvictedCount,\n currentMaxEntries: historyState.maxEntries,\n heapUsedMB: Math.round(heapUsed / 1024 / 1024),\n heapLimitMB: resolvedHeapLimit ? Math.round(resolvedHeapLimit / 1024 / 1024) : null,\n }\n}\n","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-api\")\n\nconst GITHUB_TOKEN_PATH = path.join(APP_DIR, \"github_token\")\n\nexport const PATHS = {\n APP_DIR,\n GITHUB_TOKEN_PATH,\n CONFIG_YAML: path.join(APP_DIR, \"config.yaml\"),\n LEARNED_LIMITS: path.join(APP_DIR, \"learned-limits.json\"),\n ERROR_DIR: path.join(APP_DIR, \"errmsgs\"),\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 const isWindows = process.platform === \"win32\"\n try {\n await fs.access(filePath, fs.constants.W_OK)\n // File exists — on Unix, ensure secure permissions (owner read/write only).\n // Windows NTFS doesn't support Unix permission bits; chmod is a no-op and\n // stat.mode returns a synthetic value (e.g. 0o666), so skip the check entirely.\n if (!isWindows) {\n const stats = await fs.stat(filePath)\n const currentMode = stats.mode & 0o777\n if (currentMode !== 0o600) {\n await fs.chmod(filePath, 0o600)\n }\n }\n } catch {\n await fs.writeFile(filePath, \"\")\n if (!isWindows) {\n await fs.chmod(filePath, 0o600)\n }\n }\n}\n","/**\n * Application configuration: types, YAML loading, and state application.\n *\n * All config types live here as the single source of truth.\n * config.yaml is loaded with mtime-based caching.\n */\n\nimport consola from \"consola\"\nimport fs from \"node:fs/promises\"\n\nimport { setHistoryMaxEntries } from \"~/lib/history\"\nimport {\n type CompiledRewriteRule,\n type ContextEditingMode,\n DEFAULT_MODEL_OVERRIDES,\n setAnthropicBehavior,\n setHistoryConfig,\n setModelOverrides,\n setResponsesConfig,\n setShutdownConfig,\n setTimeoutConfig,\n} from \"~/lib/state\"\n\nimport { PATHS } from \"./paths\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Raw rewrite rule from config.yaml (shared by system_prompt_overrides and rewrite_system_reminders) */\nexport interface RewriteRule {\n from: string\n to: string\n /** Match method: \"line\" = exact line match, \"regex\" = regex on full text. Default: \"regex\". */\n method?: \"line\" | \"regex\"\n /** Resolved model name regex pattern (case-insensitive). When set, this rule only applies to matching models. */\n model?: string\n}\n\n// ============================================================================\n// Rule Compilation\n// ============================================================================\n\n/** Compile a raw rewrite rule into a CompiledRewriteRule. Returns null for invalid regex. */\nexport function compileRewriteRule(raw: RewriteRule): CompiledRewriteRule | null {\n const method = raw.method ?? \"regex\"\n\n // Compile model filter regex (shared by both line and regex methods)\n let modelPattern: RegExp | undefined\n if (raw.model) {\n try {\n modelPattern = new RegExp(raw.model, \"i\")\n } catch (err) {\n consola.warn(`[config] Invalid model regex in rewrite rule: \"${raw.model}\"`, err)\n return null\n }\n }\n\n if (method === \"line\") return { from: raw.from, to: raw.to, method, modelPattern }\n try {\n // Strip leading inline flags (?flags) — merge with base gms flags\n // e.g. \"(?i)pattern\" → pattern \"pattern\", flags \"gmsi\"\n // e.g. \"(?s).*\" → pattern \".*\", flags \"gms\" (s already present)\n let pattern = raw.from\n let flags = \"gms\"\n const inlineMatch = pattern.match(/^\\(\\?([a-z]+)\\)/i)\n if (inlineMatch) {\n pattern = pattern.slice(inlineMatch[0].length)\n // Merge unique flags\n for (const f of inlineMatch[1]) {\n if (!flags.includes(f)) flags += f\n }\n }\n return { from: new RegExp(pattern, flags), to: raw.to, method, modelPattern }\n } catch (err) {\n consola.warn(`[config] Invalid regex in rewrite rule: \"${raw.from}\"`, err)\n return null\n }\n}\n\n/** Compile an array of raw rewrite rules, skipping invalid ones */\nexport function compileRewriteRules(raws: Array<RewriteRule>): Array<CompiledRewriteRule> {\n return raws.map((r) => compileRewriteRule(r)).filter((r): r is CompiledRewriteRule => r !== null)\n}\n\n/** Rate limiter configuration section */\nexport interface RateLimiterConfig {\n /** Seconds to wait before retrying after rate limit error (default: 10) */\n retry_interval?: number\n /** Seconds between requests in rate-limited mode (default: 10) */\n request_interval?: number\n /** Minutes before attempting recovery from rate-limited mode (default: 10) */\n recovery_timeout?: number\n /** Number of consecutive successes needed to recover (default: 5) */\n consecutive_successes?: number\n}\n\n/** Anthropic-specific configuration section */\nexport interface AnthropicConfig {\n /** Strip server-side tools (web_search, etc.) from requests (default: false) */\n strip_server_tools?: boolean\n /**\n * Treat assistant messages containing `thinking` / `redacted_thinking`\n * as fully immutable during client-side rewrites.\n *\n * Default: false. When enabled, sanitization / dedup / auto-truncate will\n * not modify those assistant messages in place.\n */\n immutable_thinking_messages?: boolean\n /**\n * Remove duplicate tool_use/tool_result pairs (keep last occurrence).\n * - `false` — disabled (default)\n * - `true` or `\"input\"` — match by (tool_name, input)\n * - `\"result\"` — match by (tool_name, input, result)\n */\n dedup_tool_calls?: boolean | \"input\" | \"result\"\n /** Strip injected system-reminder tags from Read tool results */\n strip_read_tool_result_tags?: boolean\n /**\n * Rewrite system-reminder tags in messages.\n * - `false` — keep all tags unchanged (default)\n * - `true` — remove all system-reminder tags\n * - Array of rewrite rules — first matching rule wins (top-down):\n * - `from`: pattern to match against tag content\n * - `to`: replacement string (supports $0, $1, etc. in regex mode)\n * Empty string = remove the tag. `$0` = keep unchanged.\n * - `method`: `\"regex\"` (default) or `\"line\"`\n */\n rewrite_system_reminders?: boolean | Array<RewriteRule>\n /**\n * Server-side context editing mode.\n * Controls how Anthropic's context_management trims older context when input grows large.\n * Requires the `context-management-2025-06-27` beta header (added automatically when not 'off').\n *\n * - `\"off\"` — disabled (default). No context_management sent, no beta header added.\n * - `\"clear-thinking\"` — clear old thinking blocks, keeping the last N thinking turns.\n * - `\"clear-tooluse\"` — clear old tool_use/tool_result pairs when input_tokens exceed threshold.\n * - `\"clear-both\"` — apply both clear-thinking and clear-tooluse edits.\n *\n * Mirrors VSCode Copilot Chat's `chat.anthropic.contextEditing.mode` setting.\n * Only effective for models that support context editing (Haiku 4.5, Sonnet 4/4.5/4.6, Opus 4/4.1/4.5/4.6).\n */\n context_editing?: ContextEditingMode\n}\n\n/** Shutdown timing configuration section */\nexport interface ShutdownConfig {\n /** Phase 2 timeout in seconds: wait for in-flight requests to complete naturally (default: 60) */\n graceful_wait?: number\n /** Phase 3 timeout in seconds: wait after abort signal for handlers to wrap up (default: 120) */\n abort_wait?: number\n}\n\n/** Responses API configuration section */\nexport interface ResponsesConfig {\n /**\n * Normalize function call IDs: convert `call_` prefix to `fc_` prefix.\n * Required when clients send conversation history with Chat Completions-format\n * tool call IDs (`call_xxx`) to the Responses API endpoint (which requires `fc_xxx`).\n * Default: true.\n */\n normalize_call_ids?: boolean\n}\n\n/** History storage configuration section */\nexport interface HistoryConfig {\n /** Maximum number of entries to keep in memory (0 = unlimited, default: 200) */\n limit?: number\n /** Minimum entries to keep even under memory pressure (default: 50) */\n min_entries?: number\n}\n\n/** Application configuration loaded from config.yaml */\nexport interface Config {\n /**\n * Proxy URL for all outgoing requests.\n * Supports http://, https://, socks5://, socks5h:// schemes.\n * Authentication via URL credentials: socks5h://user:pass@host:port\n * Takes precedence over HTTP_PROXY/HTTPS_PROXY environment variables.\n * Not hot-reloadable (requires restart).\n */\n proxy?: string\n system_prompt_overrides?: Array<RewriteRule>\n system_prompt_prepend?: string\n system_prompt_append?: string\n rate_limiter?: RateLimiterConfig\n anthropic?: AnthropicConfig\n /** Responses API configuration */\n \"openai-responses\"?: ResponsesConfig\n /** Model name overrides: request model → target model */\n model_overrides?: Record<string, string>\n /** Compress old tool_result content before truncating (default: true) */\n compress_tool_results_before_truncate?: boolean\n /** History storage configuration */\n history?: HistoryConfig\n /** Shutdown timing configuration */\n shutdown?: ShutdownConfig\n /** Stream idle timeout in seconds for all paths (default: 300, 0 = no timeout) */\n stream_idle_timeout?: number\n /** Fetch timeout in seconds: request start → HTTP response headers (default: 0 = no timeout) */\n fetch_timeout?: number\n /** Maximum age (seconds) of an active request before stale reaper forces fail (0 = disabled, default: 600) */\n stale_request_max_age?: number\n}\n\n// ============================================================================\n// Config Loading (mtime-cached)\n// ============================================================================\n\nlet cachedConfig: Config | null = null\nlet configLastMtimeMs: number = 0\n/** Time-based debounce: skip stat() if checked recently */\nlet lastStatTimeMs: number = 0\nconst STAT_DEBOUNCE_MS = 2000\n\nexport async function loadConfig(): Promise<Config> {\n try {\n // Debounce: if we already have a cached config and checked recently, skip stat()\n const now = Date.now()\n if (cachedConfig && now - lastStatTimeMs < STAT_DEBOUNCE_MS) {\n return cachedConfig\n }\n\n const stat = await fs.stat(PATHS.CONFIG_YAML)\n lastStatTimeMs = now\n if (cachedConfig && stat.mtimeMs === configLastMtimeMs) {\n return cachedConfig\n }\n const content = await fs.readFile(PATHS.CONFIG_YAML, \"utf8\")\n const { parse } = await import(\"yaml\")\n const parsed = parse(content)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- yaml.parse returns null for empty files\n cachedConfig = (parsed as Config) ?? {}\n configLastMtimeMs = stat.mtimeMs\n return cachedConfig\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return {}\n }\n // Cache the failed mtime to avoid re-parsing the same broken file every request.\n // The user sees one warning per config change, not one per request.\n try {\n const stat = await fs.stat(PATHS.CONFIG_YAML)\n configLastMtimeMs = stat.mtimeMs\n } catch {\n // File disappeared between first stat and this one — ignore\n }\n consola.warn(\"[config] Failed to load config.yaml:\", err)\n return {}\n }\n}\n\n/** Get the mtime of the currently cached config (0 if not loaded) */\nexport function getConfigMtimeMs(): number {\n return configLastMtimeMs\n}\n\n/** Exposed for testing: reset the mtime cache */\nexport function resetConfigCache(): void {\n cachedConfig = null\n configLastMtimeMs = 0\n lastStatTimeMs = 0\n}\n\n// ============================================================================\n// Config → State Application (hot-reloadable)\n// ============================================================================\n\nlet hasApplied = false\nlet lastAppliedMtimeMs = 0\n\n/**\n * Load config.yaml and apply all hot-reloadable settings to global state.\n *\n * Scalar fields: only overridden when explicitly present in config (deleted keys keep current runtime value).\n * Collection fields (model_overrides, rewrite_system_reminders array): entire replacement when present.\n *\n * Safe to call per-request — loadConfig() is mtime-cached, so unchanged config\n * only costs one stat() syscall.\n *\n * NOT hot-reloaded: rate_limiter (stateful singleton initialized at startup).\n */\nexport async function applyConfigToState(): Promise<Config> {\n const config = await loadConfig()\n\n // Anthropic settings (scalar: override only when present)\n if (config.anthropic) {\n const a = config.anthropic\n if (a.strip_server_tools !== undefined) setAnthropicBehavior({ stripServerTools: a.strip_server_tools })\n if (a.immutable_thinking_messages !== undefined)\n setAnthropicBehavior({ immutableThinkingMessages: a.immutable_thinking_messages })\n if (a.dedup_tool_calls !== undefined) {\n // Normalize: true → \"input\" for backward compatibility, false → false\n setAnthropicBehavior({ dedupToolCalls: a.dedup_tool_calls === true ? \"input\" : a.dedup_tool_calls })\n }\n if (a.strip_read_tool_result_tags !== undefined)\n setAnthropicBehavior({ stripReadToolResultTags: a.strip_read_tool_result_tags })\n if (a.context_editing !== undefined) setAnthropicBehavior({ contextEditingMode: a.context_editing })\n if (a.rewrite_system_reminders !== undefined) {\n // Collection: entire replacement — deleted rules disappear\n if (typeof a.rewrite_system_reminders === \"boolean\") {\n setAnthropicBehavior({ rewriteSystemReminders: a.rewrite_system_reminders })\n } else if (Array.isArray(a.rewrite_system_reminders)) {\n setAnthropicBehavior({ rewriteSystemReminders: compileRewriteRules(a.rewrite_system_reminders) })\n }\n }\n }\n\n // System prompt overrides (collection: entire replacement)\n // Use Array.isArray to guard against YAML null (which passes !== undefined but crashes on .length)\n if (Array.isArray(config.system_prompt_overrides)) {\n setAnthropicBehavior({\n systemPromptOverrides:\n config.system_prompt_overrides.length > 0 ? compileRewriteRules(config.system_prompt_overrides) : [],\n })\n }\n\n // Model overrides (collection: entire replacement from defaults + config)\n // User deletes a key → it reverts to default; user adds a key → it overrides default\n if (config.model_overrides) {\n setModelOverrides({ ...DEFAULT_MODEL_OVERRIDES, ...config.model_overrides })\n }\n\n // Other settings (scalar: override only when present)\n if (config.compress_tool_results_before_truncate !== undefined)\n setAnthropicBehavior({ compressToolResultsBeforeTruncate: config.compress_tool_results_before_truncate })\n\n // History settings (nested: override only when present)\n if (config.history) {\n const h = config.history\n if (h.limit !== undefined) {\n setHistoryConfig({ historyLimit: h.limit })\n setHistoryMaxEntries(h.limit)\n }\n if (h.min_entries !== undefined) setHistoryConfig({ historyMinEntries: h.min_entries })\n }\n\n // Shutdown timing (scalar: override only when present)\n if (config.shutdown) {\n const s = config.shutdown\n if (s.graceful_wait !== undefined) setShutdownConfig({ shutdownGracefulWait: s.graceful_wait })\n if (s.abort_wait !== undefined) setShutdownConfig({ shutdownAbortWait: s.abort_wait })\n }\n\n // Top-level timeouts\n if (config.fetch_timeout !== undefined) setTimeoutConfig({ fetchTimeout: config.fetch_timeout })\n if (config.stream_idle_timeout !== undefined) setTimeoutConfig({ streamIdleTimeout: config.stream_idle_timeout })\n\n // Stale request reaper max age (scalar: override only when present)\n if (config.stale_request_max_age !== undefined) setTimeoutConfig({ staleRequestMaxAge: config.stale_request_max_age })\n\n // Responses API settings (scalar: override only when present)\n const responsesConfig = config[\"openai-responses\"]\n if (responsesConfig && responsesConfig.normalize_call_ids !== undefined)\n setResponsesConfig({ normalizeResponsesCallIds: responsesConfig.normalize_call_ids })\n\n // Log when config actually changes (skip initial startup load)\n const currentMtime = getConfigMtimeMs()\n if (hasApplied && currentMtime !== lastAppliedMtimeMs) {\n consola.info(\"[config] Reloaded config.yaml\")\n }\n hasApplied = true\n lastAppliedMtimeMs = currentMtime\n\n return config\n}\n\n/** Exposed for testing: reset the apply-tracking state */\nexport function resetApplyState(): void {\n hasApplied = false\n lastAppliedMtimeMs = 0\n}\n","/**\n * Proxy configuration: HTTP/HTTPS and SOCKS5/5h proxy support.\n *\n * Priority: explicit proxy URL (CLI --proxy or config.yaml) > env vars (--http-proxy-from-env).\n * On Node.js, proxying works via undici's global dispatcher.\n * On Bun, HTTP proxies are set via env vars (Bun handles them natively); SOCKS5 is not supported.\n */\n\nimport consola from \"consola\"\nimport tls from \"node:tls\"\nimport { getProxyForUrl } from \"proxy-from-env\"\nimport { SocksClient, type SocksProxy } from \"socks\"\nimport { Agent, ProxyAgent, setGlobalDispatcher, type Dispatcher } from \"undici\"\n\n// ============================================================================\n// Public API\n// ============================================================================\n\nexport interface ProxyOptions {\n /** Explicit proxy URL (from CLI --proxy or config.yaml proxy) */\n url?: string\n /** Fall back to HTTP_PROXY/HTTPS_PROXY environment variables */\n fromEnv: boolean\n}\n\n/**\n * Initialize proxy for all outgoing fetch requests.\n *\n * On Node.js: sets undici's global dispatcher.\n * On Bun: sets process.env.HTTP_PROXY/HTTPS_PROXY for HTTP proxies (Bun handles natively).\n *\n * Must be called before any network requests.\n */\nexport function initProxy(options: ProxyOptions): void {\n if (typeof Bun !== \"undefined\") {\n initProxyBun(options)\n return\n }\n\n initProxyNode(options)\n}\n\n/** Format a proxy URL for display (strip credentials) */\nexport function formatProxyDisplay(proxyUrl: string): string {\n try {\n const u = new URL(proxyUrl)\n const auth = u.username ? `${u.username}:***@` : \"\"\n return `${u.protocol}//${auth}${u.host}`\n } catch {\n return proxyUrl\n }\n}\n\n// ============================================================================\n// Node.js implementation (undici dispatchers)\n// ============================================================================\n\nfunction initProxyNode(options: ProxyOptions): void {\n try {\n if (options.url) {\n const dispatcher = createDispatcherForUrl(options.url)\n setGlobalDispatcher(dispatcher)\n consola.debug(`Proxy configured: ${formatProxyDisplay(options.url)}`)\n return\n }\n\n if (options.fromEnv) {\n const dispatcher = new EnvProxyDispatcher()\n setGlobalDispatcher(dispatcher)\n consola.debug(\"HTTP proxy configured from environment (per-URL)\")\n }\n } catch (err) {\n consola.error(\"Proxy setup failed:\", err)\n throw err\n }\n}\n\n/** Create the appropriate undici dispatcher for a proxy URL scheme */\nexport function createDispatcherForUrl(proxyUrl: string): Dispatcher {\n const url = new URL(proxyUrl)\n const protocol = url.protocol.toLowerCase()\n\n if (protocol === \"http:\" || protocol === \"https:\") {\n return new ProxyAgent(proxyUrl)\n }\n\n if (protocol === \"socks5:\" || protocol === \"socks5h:\") {\n return createSocksAgent(url)\n }\n\n throw new Error(`Unsupported proxy protocol: ${protocol}. Supported: http, https, socks5, socks5h`)\n}\n\n// ============================================================================\n// SOCKS5/5h agent\n// ============================================================================\n\n/**\n * Create an undici Agent that routes connections through a SOCKS5/5h proxy.\n *\n * For socks5h:// the proxy performs DNS resolution (hostname passed as-is).\n * For socks5:// the hostname is also passed to the proxy (proxy resolves).\n * Both protocols support username/password authentication via URL credentials.\n */\nfunction createSocksAgent(proxyUrl: URL): Agent {\n const proxy: SocksProxy = {\n host: proxyUrl.hostname,\n port: Number(proxyUrl.port) || 1080,\n type: 5,\n }\n\n // Support username/password authentication\n if (proxyUrl.username) {\n proxy.userId = decodeURIComponent(proxyUrl.username)\n proxy.password = proxyUrl.password ? decodeURIComponent(proxyUrl.password) : undefined\n }\n\n return new Agent({\n connect(opts, callback) {\n const destPort = Number(opts.port) || (opts.protocol === \"https:\" ? 443 : 80)\n\n SocksClient.createConnection({\n proxy,\n command: \"connect\",\n destination: {\n host: opts.hostname,\n port: destPort,\n },\n })\n .then(({ socket }) => {\n if (opts.protocol === \"https:\") {\n // Upgrade to TLS for HTTPS destinations\n const tlsSocket = tls.connect({\n socket,\n servername: opts.servername ?? opts.hostname,\n })\n callback(null, tlsSocket)\n } else {\n callback(null, socket)\n }\n })\n .catch((err: unknown) => {\n callback(err instanceof Error ? err : new Error(String(err)), null)\n })\n },\n })\n}\n\n// ============================================================================\n// Environment variable proxy dispatcher (existing behavior)\n// ============================================================================\n\n/**\n * Custom dispatcher that routes requests through proxies based on environment variables.\n * Uses proxy-from-env to resolve HTTP_PROXY/HTTPS_PROXY/NO_PROXY per-URL.\n */\nclass EnvProxyDispatcher extends Agent {\n private proxies = new Map<string, ProxyAgent>()\n\n dispatch(options: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandler): boolean {\n try {\n const origin = this.getOriginUrl(options.origin)\n const proxyUrl = this.getProxyUrl(origin)\n\n if (!proxyUrl) {\n consola.debug(`HTTP proxy bypass: ${origin.hostname}`)\n return super.dispatch(options, handler)\n }\n\n const agent = this.getOrCreateProxyAgent(proxyUrl)\n consola.debug(`HTTP proxy route: ${origin.hostname} via ${formatProxyDisplay(proxyUrl)}`)\n return agent.dispatch(options, handler)\n } catch {\n return super.dispatch(options, handler)\n }\n }\n\n private getOriginUrl(origin: Dispatcher.DispatchOptions[\"origin\"]): URL {\n return typeof origin === \"string\" ? new URL(origin) : (origin as URL)\n }\n\n private getProxyUrl(origin: URL): string | undefined {\n const raw = getProxyForUrl(origin.toString())\n return raw && raw.length > 0 ? raw : undefined\n }\n\n private getOrCreateProxyAgent(proxyUrl: string): ProxyAgent {\n let agent = this.proxies.get(proxyUrl)\n if (!agent) {\n agent = new ProxyAgent(proxyUrl)\n this.proxies.set(proxyUrl, agent)\n }\n return agent\n }\n\n override async close(): Promise<void> {\n await super.close()\n await Promise.all([...this.proxies.values()].map((p) => p.close()))\n this.proxies.clear()\n }\n\n override destroy(err?: Error | null): Promise<void>\n override destroy(callback: () => void): void\n override destroy(err: Error | null, callback: () => void): void\n override destroy(errOrCallback?: Error | null | (() => void), callback?: () => void): Promise<void> | void {\n // Clean up proxy agents (fire-and-forget, errors are ignored)\n for (const agent of this.proxies.values()) {\n if (typeof errOrCallback === \"function\") {\n agent.destroy(errOrCallback)\n } else if (callback) {\n agent.destroy(errOrCallback ?? null, callback)\n } else {\n agent.destroy(errOrCallback ?? null).catch(() => {\n // Ignore cleanup errors\n })\n }\n }\n this.proxies.clear()\n\n // Call super with appropriate overload\n if (typeof errOrCallback === \"function\") {\n super.destroy(errOrCallback)\n return\n } else if (callback) {\n super.destroy(errOrCallback ?? null, callback)\n return\n } else {\n return super.destroy(errOrCallback ?? null)\n }\n }\n}\n\n// ============================================================================\n// Bun implementation\n// ============================================================================\n\n/**\n * Initialize proxy for Bun runtime.\n * Bun handles HTTP_PROXY/HTTPS_PROXY env vars natively.\n * SOCKS5 proxies are not supported on Bun.\n */\nfunction initProxyBun(options: ProxyOptions): void {\n if (!options.url) return\n\n const url = new URL(options.url)\n const protocol = url.protocol.toLowerCase()\n\n if (protocol === \"socks5:\" || protocol === \"socks5h:\") {\n throw new Error(\"SOCKS5 proxy is not supported on Bun runtime. Use Node.js or an HTTP proxy instead.\")\n }\n\n // Set env vars for Bun's native HTTP proxy support\n process.env.HTTP_PROXY = options.url\n process.env.HTTPS_PROXY = options.url\n consola.debug(`Proxy configured (Bun env): ${formatProxyDisplay(options.url)}`)\n}\n","export class HTTPError extends Error {\n status: number\n responseText: string\n /** Model ID that caused the error (if known) */\n modelId?: string\n /** Original response headers (for Retry-After, quota snapshots, etc.) */\n responseHeaders?: Headers\n\n constructor(message: string, status: number, responseText: string, modelId?: string, responseHeaders?: Headers) {\n super(message)\n this.status = status\n this.responseText = responseText\n this.modelId = modelId\n this.responseHeaders = responseHeaders\n }\n\n static async fromResponse(message: string, response: Response, modelId?: string): Promise<HTTPError> {\n const text = await response.text()\n return new HTTPError(message, response.status, text, modelId, response.headers)\n }\n}\n","/** Parse token limit info from error message text. */\nexport function parseTokenLimitError(message: string): {\n current: number\n limit: number\n} | null {\n const openaiMatch = message.match(/prompt token count of (\\d+) exceeds the limit of (\\d+)/)\n if (openaiMatch) {\n return {\n current: Number.parseInt(openaiMatch[1], 10),\n limit: Number.parseInt(openaiMatch[2], 10),\n }\n }\n\n const anthropicMatch = message.match(/prompt is too long: (\\d+) tokens > (\\d+) maximum/)\n if (anthropicMatch) {\n return {\n current: Number.parseInt(anthropicMatch[1], 10),\n limit: Number.parseInt(anthropicMatch[2], 10),\n }\n }\n\n return null\n}\n\n/** Extract retry_after from JSON response body. */\nexport function extractRetryAfterFromBody(responseText: string): number | undefined {\n try {\n const parsed: unknown = JSON.parse(responseText)\n if (parsed && typeof parsed === \"object\") {\n if (\"retry_after\" in parsed && typeof (parsed as Record<string, unknown>).retry_after === \"number\") {\n return (parsed as { retry_after: number }).retry_after\n }\n\n if (\"error\" in parsed) {\n const err = (parsed as { error: unknown }).error\n if (\n err\n && typeof err === \"object\"\n && \"retry_after\" in err\n && typeof (err as Record<string, unknown>).retry_after === \"number\"\n ) {\n return (err as { retry_after: number }).retry_after\n }\n }\n }\n } catch {\n // Not JSON\n }\n\n return undefined\n}\n\n/** Check if a 503 response body indicates upstream provider rate limiting. */\nexport function isUpstreamRateLimited(responseText: string): boolean {\n try {\n const parsed: unknown = JSON.parse(responseText)\n if (parsed && typeof parsed === \"object\" && \"error\" in parsed) {\n const err = (parsed as { error: unknown }).error\n if (err && typeof err === \"object\") {\n const errObj = err as Record<string, unknown>\n if (typeof errObj.code === \"string\" && errObj.code.includes(\"rate\")) return true\n if (typeof errObj.message === \"string\") {\n const msg = errObj.message.toLowerCase()\n if (msg.includes(\"rate limit\") || msg.includes(\"too many requests\") || msg.includes(\"quota\")) return true\n }\n }\n }\n } catch {\n const lower = responseText.toLowerCase()\n if (lower.includes(\"rate limit\") || lower.includes(\"too many requests\")) return true\n }\n\n return false\n}\n\n/** Try to extract token limit info from a JSON error response body. */\nexport function extractTokenLimitFromResponseText(responseText: string): {\n current: number\n limit: number\n} | null {\n try {\n const parsed: unknown = JSON.parse(responseText)\n if (parsed && typeof parsed === \"object\" && \"error\" in parsed) {\n const err = (parsed as { error: unknown }).error\n if (\n err\n && typeof err === \"object\"\n && \"message\" in err\n && typeof (err as Record<string, unknown>).message === \"string\"\n ) {\n return parseTokenLimitError((err as { message: string }).message)\n }\n }\n } catch {\n // Not JSON\n }\n\n return null\n}\n","/**\n * Parse the `Retry-After` HTTP response header.\n * Supports both formats per RFC 7231:\n * - Seconds: `Retry-After: 120` → 120\n * - HTTP-date: `Retry-After: Fri, 31 Dec 2025 23:59:59 GMT` → seconds from now\n * Returns undefined if header is missing or unparseable.\n */\nexport function parseRetryAfterHeader(headers: Headers | undefined): number | undefined {\n if (!headers) return undefined\n\n const value = headers.get(\"retry-after\")\n if (!value) return undefined\n\n // Try as seconds (integer)\n const seconds = Number.parseInt(value, 10)\n if (!Number.isNaN(seconds) && String(seconds) === value.trim()) {\n return seconds > 0 ? seconds : undefined\n }\n\n // Try as HTTP-date\n const date = new Date(value)\n if (!Number.isNaN(date.getTime())) {\n const deltaMs = date.getTime() - Date.now()\n const deltaSec = Math.ceil(deltaMs / 1000)\n return deltaSec > 0 ? deltaSec : undefined\n }\n\n return undefined\n}\n\n/**\n * Strip Bun's unhelpful verbose hint from error messages.\n * Bun appends \"For more information, pass `verbose: true` in the second argument to fetch()\"\n * to socket/network errors — this is an implementation detail, not useful to the user.\n */\nfunction stripBunVerboseHint(message: string): string {\n return message.replace(/\\s*For more information, pass `verbose: true`.*$/i, \"\")\n}\n\n/**\n * Format error message including cause chain, with Bun noise stripped.\n * Surfaces error.cause details (e.g. underlying socket/TLS reason) inline.\n */\nexport function formatErrorWithCause(error: Error): string {\n let msg = stripBunVerboseHint(error.message)\n if (error.cause instanceof Error && error.cause.message && error.cause.message !== error.message) {\n msg += ` (cause: ${stripBunVerboseHint(error.cause.message)})`\n }\n return msg\n}\n\n/** Extract error message with fallback. For HTTPError, extracts the actual API error response. */\nexport function getErrorMessage(error: unknown, fallback = \"Unknown error\"): string {\n if (error instanceof Error) {\n if (\"responseText\" in error && typeof (error as { responseText: unknown }).responseText === \"string\") {\n const responseText = (error as { responseText: string }).responseText\n const status = \"status\" in error ? (error as { status: number }).status : undefined\n try {\n const parsed = JSON.parse(responseText) as { error?: { message?: string; type?: string } }\n if (parsed.error?.message) {\n return status ? `HTTP ${status}: ${parsed.error.message}` : parsed.error.message\n }\n } catch {\n if (responseText.length > 0 && responseText.length < 500) {\n return status ? `HTTP ${status}: ${responseText}` : responseText\n }\n }\n return status ? `HTTP ${status}: ${error.message}` : error.message\n }\n\n return formatErrorWithCause(error)\n }\n\n return fallback\n}\n","import { HTTPError } from \"./http-error\"\nimport { extractRetryAfterFromBody, extractTokenLimitFromResponseText, isUpstreamRateLimited } from \"./parsing\"\nimport { formatErrorWithCause, parseRetryAfterHeader } from \"./utils\"\n\n/** Structured error types for pipeline retry decisions */\nexport type ApiErrorType =\n | \"rate_limited\" // 429\n | \"payload_too_large\" // 413\n | \"token_limit\" // 200/400 but body contains token limit error\n | \"content_filtered\" // 422 — Responsible AI Service filtering\n | \"quota_exceeded\" // 402 — free tier / premium quota exceeded\n | \"auth_expired\" // Token expired\n | \"network_error\" // Connection failure\n | \"server_error\" // 5xx (non-503-upstream)\n | \"upstream_rate_limited\" // 503 — upstream provider rate limited\n | \"bad_request\" // 400 (non-token-limit)\n\n/** Classified API error with structured metadata */\nexport interface ApiError {\n type: ApiErrorType\n status: number\n message: string\n /** Retry-After seconds (rate_limited / quota_exceeded / upstream_rate_limited) */\n retryAfter?: number\n /** Token limit from error response (token_limit) */\n tokenLimit?: number\n /** Current token count from error response (token_limit) */\n tokenCurrent?: number\n /** Original response headers (for quota snapshots, etc.) */\n responseHeaders?: Headers\n /** Original error object */\n raw: unknown\n}\n\n/**\n * Classify a raw error into a structured ApiError.\n * Used by the pipeline to route errors to appropriate RetryStrategies.\n */\nexport function classifyError(error: unknown): ApiError {\n if (error instanceof HTTPError) {\n return classifyHTTPError(error)\n }\n\n // Network errors: fetch failures, socket closures, connection resets, timeouts, DNS failures\n // Bun throws TypeError for some fetch failures, and plain Error for socket closures.\n // Match broadly on error message patterns to catch all network-level failures.\n if (error instanceof Error && isNetworkError(error)) {\n return {\n type: \"network_error\",\n status: 0,\n message: formatErrorWithCause(error),\n raw: error,\n }\n }\n\n if (error instanceof Error) {\n return {\n type: \"bad_request\",\n status: 0,\n message: formatErrorWithCause(error),\n raw: error,\n }\n }\n\n return {\n type: \"bad_request\",\n status: 0,\n message: String(error),\n raw: error,\n }\n}\n\nfunction classifyHTTPError(error: HTTPError): ApiError {\n const { status, responseText, message } = error\n\n if (status === 422) {\n return {\n type: \"content_filtered\",\n status,\n message,\n responseHeaders: error.responseHeaders,\n raw: error,\n }\n }\n\n if (status === 402) {\n const retryAfter = extractRetryAfterFromBody(responseText) ?? parseRetryAfterHeader(error.responseHeaders)\n return {\n type: \"quota_exceeded\",\n status,\n message,\n retryAfter,\n responseHeaders: error.responseHeaders,\n raw: error,\n }\n }\n\n if (status === 429) {\n const retryAfter = extractRetryAfterFromBody(responseText) ?? parseRetryAfterHeader(error.responseHeaders)\n return {\n type: \"rate_limited\",\n status,\n message,\n retryAfter,\n responseHeaders: error.responseHeaders,\n raw: error,\n }\n }\n\n if (status === 413) {\n return {\n type: \"payload_too_large\",\n status,\n message,\n raw: error,\n }\n }\n\n if (status === 503) {\n const retryAfter = extractRetryAfterFromBody(responseText) ?? parseRetryAfterHeader(error.responseHeaders)\n if (isUpstreamRateLimited(responseText)) {\n return {\n type: \"upstream_rate_limited\",\n status,\n message,\n retryAfter,\n responseHeaders: error.responseHeaders,\n raw: error,\n }\n }\n return {\n type: \"server_error\",\n status,\n message,\n retryAfter,\n raw: error,\n }\n }\n\n if (status >= 500) {\n return {\n type: \"server_error\",\n status,\n message,\n raw: error,\n }\n }\n\n if (status === 401 || status === 403) {\n return {\n type: \"auth_expired\",\n status,\n message,\n raw: error,\n }\n }\n\n if (status === 400) {\n const tokenLimit = tryExtractTokenLimit(responseText)\n if (tokenLimit) {\n return {\n type: \"token_limit\",\n status,\n message,\n tokenLimit: tokenLimit.limit,\n tokenCurrent: tokenLimit.current,\n raw: error,\n }\n }\n\n if (isRateLimitedInBody(responseText)) {\n const retryAfter = extractRetryAfterFromBody(responseText) ?? parseRetryAfterHeader(error.responseHeaders)\n return {\n type: \"rate_limited\",\n status,\n message,\n retryAfter,\n raw: error,\n }\n }\n }\n\n return {\n type: \"bad_request\",\n status,\n message,\n raw: error,\n }\n}\n\n/** Check if response body contains rate_limited code */\nfunction isRateLimitedInBody(responseText: string): boolean {\n try {\n const parsed: unknown = JSON.parse(responseText)\n if (parsed && typeof parsed === \"object\" && \"error\" in parsed) {\n const err = (parsed as { error: unknown }).error\n if (err && typeof err === \"object\" && \"code\" in err) {\n return (err as { code: unknown }).code === \"rate_limited\"\n }\n }\n } catch {\n // Not JSON\n }\n return false\n}\n\n/** Try to extract token limit info from response body */\nfunction tryExtractTokenLimit(responseText: string): { current: number; limit: number } | null {\n return extractTokenLimitFromResponseText(responseText)\n}\n\n/** Known network/socket error message patterns from Bun and Node.js fetch */\nconst NETWORK_ERROR_PATTERNS = [\n \"socket\",\n \"ECONNRESET\",\n \"ECONNREFUSED\",\n \"ETIMEDOUT\",\n \"ENETUNREACH\",\n \"EHOSTUNREACH\",\n \"EAI_AGAIN\",\n \"UND_ERR_SOCKET\",\n \"fetch failed\",\n \"network\",\n \"TLS\",\n \"CERT\",\n \"abort\",\n]\n\n/** Check if an error is a network-level failure (socket, DNS, TLS, connection errors) */\nfunction isNetworkError(error: Error): boolean {\n const msg = error.message.toLowerCase()\n if (NETWORK_ERROR_PATTERNS.some((pattern) => msg.includes(pattern.toLowerCase()))) return true\n\n if (error.cause instanceof Error) return isNetworkError(error.cause)\n\n return false\n}\n","import type { Context } from \"hono\"\nimport type { ContentfulStatusCode } from \"hono/utils/http-status\"\n\nimport consola from \"consola\"\n\nimport { HTTPError } from \"./http-error\"\nimport { extractTokenLimitFromResponseText, isUpstreamRateLimited } from \"./parsing\"\nimport { formatErrorWithCause, parseRetryAfterHeader } from \"./utils\"\n\n/** Copilot error structure */\ninterface CopilotError {\n error?: {\n message?: string\n code?: string\n }\n}\n\n/** Anthropic error structure */\ninterface AnthropicError {\n type?: string\n error?: {\n type?: string\n message?: string\n }\n}\n\n/** Format Anthropic-compatible error for token limit exceeded */\nfunction formatTokenLimitError(current: number, limit: number) {\n const excess = current - limit\n const percentage = Math.round((excess / limit) * 100)\n\n return {\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message:\n `prompt is too long: ${current} tokens > ${limit} maximum ` + `(${excess} tokens over, ${percentage}% excess)`,\n },\n }\n}\n\n/** Format Anthropic-compatible error for request too large (413) */\nfunction formatRequestTooLargeError() {\n return {\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message:\n \"Request body too large. The HTTP request exceeds the server's size limit. \"\n + \"Try reducing the conversation history or removing large content like images.\",\n },\n }\n}\n\n/** Format Anthropic-compatible error for rate limit exceeded (429) */\nfunction formatRateLimitError(copilotMessage?: string) {\n return {\n type: \"error\",\n error: {\n type: \"rate_limit_error\",\n message: copilotMessage ?? \"You have exceeded your rate limit. Please try again later.\",\n },\n }\n}\n\n/** Format Anthropic-compatible error for quota exceeded (402) */\nfunction formatQuotaExceededError(retryAfter?: number) {\n const retryInfo = retryAfter ? ` Quota resets in approximately ${retryAfter} seconds.` : \"\"\n return {\n type: \"error\",\n error: {\n type: \"rate_limit_error\",\n message: `You have exceeded your usage quota. Please try again later.${retryInfo}`,\n },\n ...(retryAfter !== undefined && { retry_after: retryAfter }),\n }\n}\n\n/** Format Anthropic-compatible error for content filtered (422) */\nfunction formatContentFilteredError(responseText: string) {\n let detail = \"\"\n try {\n const parsed = JSON.parse(responseText) as { error?: { message?: string } }\n if (parsed.error?.message) detail = `: ${parsed.error.message}`\n } catch {\n // Not JSON — use generic message\n }\n return {\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message: `Content filtered by safety system${detail}`,\n },\n }\n}\n\nexport function forwardError(c: Context, error: unknown) {\n // Error file persistence is handled by the error-persistence consumer\n // (subscribes to \"failed\" events on RequestContext) — no inline writing here.\n if (error instanceof HTTPError) {\n const limitInfo = error.status === 400 ? extractTokenLimitFromResponseText(error.responseText) : null\n\n if (error.status === 413) {\n const formattedError = formatRequestTooLargeError()\n consola.warn(\"HTTP 413: Request too large\")\n return c.json(formattedError, 413 as ContentfulStatusCode)\n }\n\n if (limitInfo?.current && limitInfo.limit) {\n const formattedError = formatTokenLimitError(limitInfo.current, limitInfo.limit)\n const excess = limitInfo.current - limitInfo.limit\n const percentage = Math.round((excess / limitInfo.limit) * 100)\n consola.warn(\n `HTTP ${error.status}: Token limit exceeded for ${error.modelId ?? \"unknown\"} `\n + `(${limitInfo.current.toLocaleString()} > ${limitInfo.limit.toLocaleString()}, `\n + `${excess.toLocaleString()} over, ${percentage}% excess)`,\n )\n return c.json(formattedError, 400 as ContentfulStatusCode)\n }\n\n if (error.status === 402) {\n const retryAfter = parseRetryAfterHeader(error.responseHeaders)\n const formattedError = formatQuotaExceededError(retryAfter)\n consola.warn(`HTTP 402: Quota exceeded${retryAfter ? ` (retry after ${retryAfter}s)` : \"\"}`)\n return c.json(formattedError, 402 as ContentfulStatusCode)\n }\n\n if (error.status === 422) {\n const formattedError = formatContentFilteredError(error.responseText)\n consola.warn(\"HTTP 422: Content filtered by safety system\")\n return c.json(formattedError, 422 as ContentfulStatusCode)\n }\n\n let errorJson: unknown\n try {\n errorJson = JSON.parse(error.responseText)\n } catch {\n errorJson = error.responseText\n }\n\n if (typeof errorJson === \"object\" && errorJson !== null) {\n const errorObj = errorJson as CopilotError & AnthropicError\n\n if (error.status === 429 || errorObj.error?.code === \"rate_limited\") {\n const formattedError = formatRateLimitError(errorObj.error?.message)\n consola.warn(\"HTTP 429: Rate limit exceeded\")\n return c.json(formattedError, 429 as ContentfulStatusCode)\n }\n\n if (error.status === 503 && isUpstreamRateLimited(error.responseText)) {\n const retryAfter = parseRetryAfterHeader(error.responseHeaders)\n const formattedError = formatRateLimitError(\n errorObj.error?.message ?? \"Upstream provider rate limited. Please try again later.\",\n )\n if (retryAfter) {\n ;(formattedError as Record<string, unknown>).retry_after = retryAfter\n }\n consola.warn(`HTTP 503: Upstream provider rate limited${retryAfter ? ` (retry after ${retryAfter}s)` : \"\"}`)\n return c.json(formattedError, 503 as ContentfulStatusCode)\n }\n } else if (error.status === 429) {\n const formattedError = formatRateLimitError()\n consola.warn(\"HTTP 429: Rate limit exceeded\")\n return c.json(formattedError, 429 as ContentfulStatusCode)\n }\n\n if (typeof errorJson === \"string\") {\n const isHtml = errorJson.trimStart().startsWith(\"<\")\n const preview = isHtml ? `[HTML ${errorJson.length} bytes]` : truncateForLog(errorJson, 200)\n consola.error(`HTTP ${error.status}: ${preview}`)\n } else {\n consola.error(`HTTP ${error.status}:`, errorJson)\n }\n\n return c.json(\n {\n error: {\n message: error.responseText,\n type: \"error\",\n },\n },\n error.status as ContentfulStatusCode,\n )\n }\n\n const errorMessage = error instanceof Error ? formatErrorWithCause(error) : String(error)\n consola.error(`Unexpected non-HTTP error in ${c.req.method} ${c.req.path}:`, errorMessage)\n\n return c.json(\n {\n error: {\n message: errorMessage,\n type: \"error\",\n },\n },\n 500,\n )\n}\n\n/** Truncate a string for log display, adding ellipsis if truncated */\nfunction truncateForLog(text: string, maxLen: number): string {\n if (text.length <= maxLen) return text\n return `${text.slice(0, maxLen)}… (${text.length} bytes total)`\n}\n","import { randomUUID } from \"node:crypto\"\n\nimport type { State } from \"./state\"\n\nimport { setVSCodeVersion } from \"./state\"\n\nexport const standardHeaders = () => ({\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n})\n\nconst COPILOT_VERSION = \"0.38.0\"\nconst EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`\nconst USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`\n\n/** Copilot Chat API version (for chat/completions requests) */\nconst COPILOT_API_VERSION = \"2025-05-01\"\n\n/** Copilot internal API version (for token & usage endpoints) */\nexport const COPILOT_INTERNAL_API_VERSION = \"2025-04-01\"\n\n/** GitHub public API version (for /user, repos, etc.) */\nconst GITHUB_API_VERSION = \"2022-11-28\"\n\n/**\n * Session-level interaction ID.\n * Used to correlate all requests within a single server session.\n * Unlike x-request-id (per-request UUID), this stays constant for the server lifetime.\n */\nconst INTERACTION_ID = randomUUID()\n\nexport const copilotBaseUrl = (state: State) =>\n state.accountType === \"individual\" ?\n \"https://api.githubcopilot.com\"\n : `https://api.${state.accountType}.githubcopilot.com`\n\nexport interface CopilotHeaderOptions {\n /** Whether to set the Copilot-Vision-Request header */\n vision?: boolean\n /** Model-specific request headers from CAPI to forward upstream */\n modelRequestHeaders?: Record<string, string>\n /** OpenAI intent value (default: \"conversation-panel\") */\n intent?: string\n}\n\nexport const copilotHeaders = (state: State, opts?: CopilotHeaderOptions) => {\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\": opts?.intent ?? \"conversation-panel\",\n \"x-github-api-version\": COPILOT_API_VERSION,\n \"x-request-id\": randomUUID(),\n \"X-Interaction-Id\": INTERACTION_ID,\n \"x-vscode-user-agent-library-version\": \"electron-fetch\",\n }\n\n if (opts?.vision) headers[\"copilot-vision-request\"] = \"true\"\n\n // Forward model-specific request headers from CAPI (lowest priority — don't override core headers)\n if (opts?.modelRequestHeaders) {\n const coreKeysLower = new Set(Object.keys(headers).map((k) => k.toLowerCase()))\n for (const [key, value] of Object.entries(opts.modelRequestHeaders)) {\n if (!coreKeysLower.has(key.toLowerCase())) headers[key] = value\n }\n }\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\": GITHUB_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\n// ============================================================================\n// VSCode version detection\n// ============================================================================\n\n/** Fallback VSCode version when GitHub API is unavailable */\nconst VSCODE_VERSION_FALLBACK = \"1.104.3\"\n\n/** GitHub API endpoint for latest VSCode release */\nconst VSCODE_RELEASE_URL = \"https://api.github.com/repos/microsoft/vscode/releases/latest\"\n\n/** GitHub release response shape */\ninterface GitHubRelease {\n tag_name: string\n}\n\n/** Fetch the latest VSCode version and cache in global state */\nexport async function cacheVSCodeVersion(): Promise<void> {\n const response = await getVSCodeVersion()\n setVSCodeVersion(response)\n}\n\n/** Fetch the latest VSCode version from GitHub releases, falling back to a hardcoded version */\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(VSCODE_RELEASE_URL, {\n signal: controller.signal,\n headers: {\n Accept: \"application/vnd.github.v3+json\",\n \"User-Agent\": \"copilot-api\",\n },\n })\n\n if (!response.ok) {\n return VSCODE_VERSION_FALLBACK\n }\n\n const release = (await response.json()) as GitHubRelease\n // tag_name is in format \"1.107.1\"\n const version = release.tag_name\n if (version && /^\\d+\\.\\d+\\.\\d+$/.test(version)) {\n return version\n }\n\n return VSCODE_VERSION_FALLBACK\n } catch {\n return VSCODE_VERSION_FALLBACK\n } finally {\n clearTimeout(timeout)\n }\n}\n","/** Copilot API client — token and usage */\n\nimport { COPILOT_INTERNAL_API_VERSION, GITHUB_API_BASE_URL, githubHeaders } from \"~/lib/copilot-api\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\n// ============================================================================\n// Token\n// ============================================================================\n\nexport const getCopilotToken = async (): Promise<CopilotTokenResponse> => {\n const response = await fetch(`${GITHUB_API_BASE_URL}/copilot_internal/v2/token`, {\n headers: { ...githubHeaders(state), \"x-github-api-version\": COPILOT_INTERNAL_API_VERSION },\n signal: AbortSignal.timeout(15_000),\n })\n\n if (!response.ok) throw await HTTPError.fromResponse(\"Failed to get Copilot token\", response)\n\n return (await response.json()) as CopilotTokenResponse\n}\n\n/**\n * Copilot token API response.\n * Only the fields we actively use are typed; the full response is\n * preserved as-is in CopilotTokenInfo.raw for future consumers.\n */\nexport interface CopilotTokenResponse {\n expires_at: number\n refresh_in: number\n token: string\n [key: string]: unknown\n}\n\n// ============================================================================\n// Usage\n// ============================================================================\n\nexport const getCopilotUsage = async (): Promise<CopilotUsageResponse> => {\n const response = await fetch(`${GITHUB_API_BASE_URL}/copilot_internal/user`, {\n headers: { ...githubHeaders(state), \"x-github-api-version\": COPILOT_INTERNAL_API_VERSION },\n signal: AbortSignal.timeout(15_000),\n })\n\n if (!response.ok) {\n throw await HTTPError.fromResponse(\"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 consola from \"consola\"\n\nimport { formatErrorWithCause } from \"~/lib/error\"\nimport { setCopilotToken, setGitHubToken } from \"~/lib/state\"\n\nimport type { GitHubTokenManager } from \"./github-token-manager\"\nimport type { CopilotTokenInfo } from \"./types\"\n\nimport { getCopilotToken } from \"./copilot-client\"\n\nexport interface CopilotTokenManagerOptions {\n /** GitHub token manager instance */\n githubTokenManager: GitHubTokenManager\n /** Minimum refresh interval in seconds (default: 60) */\n minRefreshIntervalSeconds?: number\n /** Maximum retries for token refresh (default: 3) */\n maxRetries?: number\n}\n\n/**\n * Manages Copilot token lifecycle including automatic refresh.\n * Depends on GitHubTokenManager for authentication.\n *\n * All refresh paths (scheduled + on-demand via 401) go through `refresh()`,\n * which deduplicates concurrent callers and reschedules the next refresh based\n * on the server's `refresh_in` value.\n */\nexport class CopilotTokenManager {\n private githubTokenManager: GitHubTokenManager\n private currentToken: CopilotTokenInfo | null = null\n private refreshTimeout: ReturnType<typeof setTimeout> | null = null\n private minRefreshIntervalMs: number\n private maxRetries: number\n /** Shared promise to prevent concurrent refresh attempts */\n private refreshInFlight: Promise<CopilotTokenInfo | null> | null = null\n /** Set when a refresh attempt fails; cleared on next success */\n private _refreshNeeded = false\n\n constructor(options: CopilotTokenManagerOptions) {\n this.githubTokenManager = options.githubTokenManager\n this.minRefreshIntervalMs = (options.minRefreshIntervalSeconds ?? 60) * 1000\n this.maxRetries = options.maxRetries ?? 3\n }\n\n /**\n * Get the current Copilot token info.\n */\n getCurrentToken(): CopilotTokenInfo | null {\n return this.currentToken\n }\n\n /**\n * Initialize the Copilot token and start automatic refresh.\n */\n async initialize(): Promise<CopilotTokenInfo> {\n const tokenInfo = await this.fetchCopilotToken()\n\n // Update global state\n setCopilotToken(tokenInfo.token)\n\n // Show token in verbose mode\n consola.debug(\"GitHub Copilot Token fetched successfully!\")\n\n // Schedule first refresh based on server's refresh_in\n this.scheduleRefresh(tokenInfo.refreshIn)\n\n return tokenInfo\n }\n\n /**\n * Fetch a new Copilot token from the API.\n */\n private async fetchCopilotToken(): Promise<CopilotTokenInfo> {\n const response = await getCopilotToken()\n\n const tokenInfo: CopilotTokenInfo = {\n token: response.token,\n expiresAt: response.expires_at,\n refreshIn: response.refresh_in,\n raw: response,\n }\n\n this.currentToken = tokenInfo\n return tokenInfo\n }\n\n /**\n * Fetch a new Copilot token with exponential backoff retry.\n * Pure acquisition logic — does not update global state or reschedule timers.\n */\n private async fetchTokenWithRetry(): Promise<CopilotTokenInfo | null> {\n let lastError: unknown = null\n\n for (let attempt = 0; attempt < this.maxRetries; attempt++) {\n try {\n return await this.fetchCopilotToken()\n } catch (error) {\n lastError = error\n\n // Check if this is a 401 error - might need to refresh GitHub token\n if (this.isUnauthorizedError(error)) {\n consola.warn(\"Copilot token refresh got 401, trying to refresh GitHub token...\")\n const newGithubToken = await this.githubTokenManager.refresh()\n if (newGithubToken) {\n // Update state and retry\n setGitHubToken(newGithubToken.token)\n continue\n }\n }\n\n const delay = Math.min(1000 * 2 ** attempt, 30000) // Max 30s delay\n const reason = error instanceof Error ? formatErrorWithCause(error) : String(error)\n consola.warn(\n `Token refresh attempt ${attempt + 1}/${this.maxRetries} failed: ${reason}, retrying in ${delay}ms`,\n )\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n }\n\n const reason = lastError instanceof Error ? formatErrorWithCause(lastError) : String(lastError)\n consola.error(`All token refresh attempts failed: ${reason}`)\n return null\n }\n\n /**\n * Check if an error is a 401 Unauthorized error.\n */\n private isUnauthorizedError(error: unknown): boolean {\n if (error && typeof error === \"object\" && \"status\" in error) {\n return (error as { status: number }).status === 401\n }\n return false\n }\n\n /**\n * Schedule the next refresh using setTimeout.\n *\n * Uses the server-provided `refresh_in` value each time, adapting to\n * changing token lifetimes. After each refresh, reschedules based on\n * the new token's `refresh_in`.\n */\n private scheduleRefresh(refreshInSeconds: number): void {\n // Sanity check: refresh_in should be positive and reasonable\n let effectiveRefreshIn = refreshInSeconds\n if (refreshInSeconds <= 0) {\n consola.warn(`[CopilotToken] Invalid refresh_in=${refreshInSeconds}s, using default 30 minutes`)\n effectiveRefreshIn = 1800 // 30 minutes\n }\n\n // Calculate delay (refresh a bit before expiration)\n const delayMs = Math.max((effectiveRefreshIn - 60) * 1000, this.minRefreshIntervalMs)\n\n consola.debug(\n `[CopilotToken] refresh_in=${effectiveRefreshIn}s, scheduling next refresh in ${Math.round(delayMs / 1000)}s`,\n )\n\n // Clear any existing timer\n this.cancelScheduledRefresh()\n\n this.refreshTimeout = setTimeout(() => {\n this.refresh().catch((error: unknown) => {\n consola.error(\"Unexpected error during scheduled token refresh:\", error)\n })\n }, delayMs)\n }\n\n /**\n * Cancel the currently scheduled refresh.\n */\n private cancelScheduledRefresh(): void {\n if (this.refreshTimeout) {\n clearTimeout(this.refreshTimeout)\n this.refreshTimeout = null\n }\n }\n\n /**\n * Stop automatic token refresh.\n * Call this during cleanup/shutdown.\n */\n stopAutoRefresh(): void {\n this.cancelScheduledRefresh()\n }\n\n /**\n * Refresh the Copilot token.\n *\n * Single entry point for all refreshes — both scheduled and on-demand\n * (e.g. after a 401). Concurrent callers share the same in-flight refresh.\n * On success, updates global state and reschedules the next refresh based\n * on the new token's `refresh_in`.\n */\n async refresh(): Promise<CopilotTokenInfo | null> {\n // If a refresh is already in progress, piggyback on it\n if (this.refreshInFlight) {\n consola.debug(\"[CopilotToken] Refresh already in progress, waiting...\")\n return this.refreshInFlight\n }\n\n this.refreshInFlight = this.fetchTokenWithRetry()\n .then((tokenInfo) => {\n if (tokenInfo) {\n this._refreshNeeded = false\n setCopilotToken(tokenInfo.token)\n // Reschedule based on new token's refresh_in\n this.scheduleRefresh(tokenInfo.refreshIn)\n consola.verbose(`[CopilotToken] Token refreshed (next refresh_in=${tokenInfo.refreshIn}s)`)\n } else {\n this._refreshNeeded = true\n consola.error(\"[CopilotToken] Token refresh failed, keeping existing token\")\n // Still reschedule with a fallback to avoid stopping the refresh loop entirely\n this.scheduleRefresh(300) // retry in 5 minutes\n }\n return tokenInfo\n })\n .finally(() => {\n this.refreshInFlight = null\n })\n\n return this.refreshInFlight\n }\n\n /**\n * Proactively ensure the token is valid before sending a request.\n * Triggers a refresh if the token is expired/expiring or the last\n * refresh attempt failed. Concurrent callers share the same in-flight\n * refresh via `refresh()`.\n */\n async ensureValidToken(): Promise<void> {\n if (!this.isExpiredOrExpiring() && !this._refreshNeeded) return\n await this.refresh()\n }\n\n /**\n * Check if the current token is expired or about to expire.\n */\n isExpiredOrExpiring(marginSeconds = 60): boolean {\n if (!this.currentToken) {\n return true\n }\n\n const now = Date.now() / 1000\n return this.currentToken.expiresAt - marginSeconds <= now\n }\n}\n","/** GitHub OAuth API client — device code flow and user info */\n\nimport consola from \"consola\"\n\nimport {\n GITHUB_API_BASE_URL,\n GITHUB_BASE_URL,\n GITHUB_CLIENT_ID,\n githubHeaders,\n standardHeaders,\n} from \"~/lib/copilot-api\"\nimport { HTTPError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\nimport { sleep } from \"~/lib/utils\"\n\n// ============================================================================\n// User\n// ============================================================================\n\nexport interface GitHubUser {\n login: string\n id: number\n name: string | null\n email: string | null\n created_at: string\n updated_at: string\n two_factor_authentication: boolean\n}\n\nexport const getGitHubUser = async (): Promise<GitHubUser> => {\n const response = await fetch(`${GITHUB_API_BASE_URL}/user`, {\n headers: githubHeaders(state),\n signal: AbortSignal.timeout(15_000),\n })\n\n if (!response.ok) throw await HTTPError.fromResponse(\"Failed to get GitHub user\", response)\n\n return (await response.json()) as GitHubUser\n}\n\n// ============================================================================\n// Device code flow\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\nexport const getDeviceCode = async (): 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: \"read:user\",\n }),\n signal: AbortSignal.timeout(15_000),\n })\n\n if (!response.ok) throw await HTTPError.fromResponse(\"Failed to get device code\", response)\n\n return (await response.json()) as DeviceCodeResponse\n}\n\nexport async function pollAccessToken(deviceCode: DeviceCodeResponse): 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 // Calculate expiration time based on expires_in from device code response\n const expiresAt = Date.now() + deviceCode.expires_in * 1000\n\n while (Date.now() < expiresAt) {\n const response = await fetch(`${GITHUB_BASE_URL}/login/oauth/access_token`, {\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 signal: AbortSignal.timeout(15_000),\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()) as AccessTokenResponse\n consola.debug(\"Polling access token response:\", json)\n\n const { access_token } = json\n\n if (access_token) {\n return access_token\n } else {\n await sleep(sleepDuration)\n }\n }\n\n throw new Error(\"Device code expired. Please run the authentication flow again.\")\n}\n\ninterface AccessTokenResponse {\n access_token: string\n token_type: string\n scope: string\n}\n","import { setGitHubToken, state } from \"~/lib/state\"\nimport { getGitHubUser } from \"~/lib/token/github-client\"\n\nimport type { TokenInfo, TokenValidationResult } from \"../types\"\n\n/**\n * Abstract base class for GitHub token providers.\n * Each provider represents a different source of GitHub tokens.\n */\nexport abstract class GitHubTokenProvider {\n /** Human-readable name of the provider */\n abstract readonly name: string\n\n /** Priority (lower = higher priority, tried first) */\n abstract readonly priority: number\n\n /** Whether this provider can refresh tokens */\n abstract readonly refreshable: boolean\n\n /**\n * Check if this provider is available (has required configuration).\n * For example, CLI provider is only available if token was passed via args.\n */\n abstract isAvailable(): boolean | Promise<boolean>\n\n /**\n * Get the token from this provider.\n * Returns null if not available or token cannot be obtained.\n */\n abstract getToken(): Promise<TokenInfo | null>\n\n /**\n * Refresh the token (if supported).\n * Default implementation returns null (not supported).\n */\n // eslint-disable-next-line @typescript-eslint/require-await\n async refresh(): Promise<TokenInfo | null> {\n return null\n }\n\n /**\n * Validate the token by calling GitHub API.\n * Returns validation result with username if successful.\n */\n async validate(token: string): Promise<TokenValidationResult> {\n // Temporarily set the token to validate\n const originalToken = state.githubToken\n\n try {\n setGitHubToken(token)\n const user = await getGitHubUser()\n return {\n valid: true,\n username: user.login,\n }\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : String(error),\n }\n } finally {\n // Restore original token\n setGitHubToken(originalToken)\n }\n }\n}\n","import type { TokenInfo } from \"../types\"\n\nimport { GitHubTokenProvider } from \"./base\"\n\n/**\n * Provider for tokens passed via CLI --github-token argument.\n * Highest priority (1) - if user explicitly provides a token, use it.\n */\nexport class CLITokenProvider extends GitHubTokenProvider {\n readonly name = \"CLI\"\n readonly priority = 1\n readonly refreshable = false\n\n private token: string | undefined\n\n constructor(token?: string) {\n super()\n this.token = token\n }\n\n isAvailable(): boolean {\n return Boolean(this.token && this.token.trim())\n }\n\n getToken(): Promise<TokenInfo | null> {\n if (!this.isAvailable() || !this.token) {\n return Promise.resolve(null)\n }\n\n return Promise.resolve({\n token: this.token.trim(),\n source: \"cli\",\n refreshable: false,\n })\n }\n}\n","import fs from \"node:fs/promises\"\n\nimport { PATHS } from \"~/lib/config/paths\"\n\nimport type { TokenInfo } from \"../types\"\n\nimport { GitHubTokenProvider } from \"./base\"\n\n/**\n * Provider for tokens stored in file system.\n * Priority 3 - checked after CLI and environment variables.\n */\nexport class FileTokenProvider extends GitHubTokenProvider {\n readonly name = \"File\"\n readonly priority = 3\n readonly refreshable = false\n\n async isAvailable(): Promise<boolean> {\n try {\n const token = await this.readTokenFile()\n return Boolean(token && token.trim())\n } catch {\n return false\n }\n }\n\n async getToken(): Promise<TokenInfo | null> {\n try {\n const token = await this.readTokenFile()\n if (!token || !token.trim()) {\n return null\n }\n\n return {\n token: token.trim(),\n source: \"file\",\n refreshable: false,\n }\n } catch {\n return null\n }\n }\n\n /**\n * Save a token to the file.\n * This is used by device auth provider to persist tokens.\n */\n async saveToken(token: string): Promise<void> {\n await fs.writeFile(PATHS.GITHUB_TOKEN_PATH, token.trim())\n }\n\n /**\n * Clear the stored token.\n */\n async clearToken(): Promise<void> {\n try {\n await fs.writeFile(PATHS.GITHUB_TOKEN_PATH, \"\")\n } catch {\n // Ignore errors when clearing\n }\n }\n\n private async readTokenFile(): Promise<string> {\n return fs.readFile(PATHS.GITHUB_TOKEN_PATH, \"utf8\")\n }\n}\n","import consola from \"consola\"\n\nimport { state } from \"~/lib/state\"\nimport { getDeviceCode, pollAccessToken } from \"~/lib/token/github-client\"\n\nimport type { TokenInfo } from \"../types\"\n\nimport { GitHubTokenProvider } from \"./base\"\nimport { FileTokenProvider } from \"./file\"\n\n/**\n * Provider for tokens obtained via GitHub device authorization flow.\n * Priority 4 (lowest) - only used when no other token source is available.\n * This is the interactive fallback that prompts the user to authenticate.\n */\nexport class DeviceAuthProvider extends GitHubTokenProvider {\n readonly name = \"DeviceAuth\"\n readonly priority = 4\n readonly refreshable = true\n\n private fileProvider: FileTokenProvider\n\n constructor() {\n super()\n this.fileProvider = new FileTokenProvider()\n }\n\n /**\n * Device auth is always \"available\" as a fallback.\n * It will prompt the user to authenticate interactively.\n */\n isAvailable(): boolean {\n return true\n }\n\n /**\n * Run the device authorization flow to get a new token.\n * This will prompt the user to visit a URL and enter a code.\n */\n async getToken(): Promise<TokenInfo | null> {\n try {\n consola.info(\"Not logged in, starting device authorization flow...\")\n\n const response = await getDeviceCode()\n consola.debug(\"Device code response:\", response)\n\n consola.info(`Please enter the code \"${response.user_code}\" at ${response.verification_uri}`)\n\n const token = await pollAccessToken(response)\n\n // Save to file for future sessions\n await this.fileProvider.saveToken(token)\n\n // Show token if configured\n if (state.showGitHubToken) {\n consola.info(\"GitHub token:\", token)\n }\n\n return {\n token,\n source: \"device-auth\",\n refreshable: true,\n }\n } catch (error) {\n // Node.js undici wraps the real error in TypeError.cause — surface it for diagnostics\n const cause = error instanceof TypeError && error.cause ? error.cause : undefined\n consola.error(\"Device authorization failed:\", error)\n if (cause) {\n consola.error(\"Caused by:\", cause)\n }\n return null\n }\n }\n\n /**\n * Refresh by running the device auth flow again.\n */\n async refresh(): Promise<TokenInfo | null> {\n return this.getToken()\n }\n}\n","import type { TokenInfo } from \"../types\"\n\nimport { GitHubTokenProvider } from \"./base\"\n\n/**\n * Environment variable names to check for GitHub token.\n * Checked in order - first found wins.\n */\nconst ENV_VARS = [\n \"COPILOT_API_GITHUB_TOKEN\", // Our dedicated variable\n \"GH_TOKEN\", // GitHub CLI compatible\n \"GITHUB_TOKEN\", // Common convention\n]\n\n/**\n * Provider for tokens from environment variables.\n * Priority 2 - checked after CLI but before file storage.\n */\nexport class EnvTokenProvider extends GitHubTokenProvider {\n readonly name = \"Environment\"\n readonly priority = 2\n readonly refreshable = false\n\n /** The env var name where the token was found */\n private foundEnvVar: string | undefined\n\n isAvailable(): boolean {\n return this.findEnvVar() !== undefined\n }\n\n getToken(): Promise<TokenInfo | null> {\n const envVar = this.findEnvVar()\n if (!envVar) {\n return Promise.resolve(null)\n }\n\n const token = process.env[envVar]\n if (!token) {\n return Promise.resolve(null)\n }\n\n this.foundEnvVar = envVar\n\n return Promise.resolve({\n token: token.trim(),\n source: \"env\",\n refreshable: false,\n })\n }\n\n /**\n * Find the first environment variable that contains a token.\n */\n private findEnvVar(): string | undefined {\n for (const envVar of ENV_VARS) {\n const value = process.env[envVar]\n if (value && value.trim()) {\n return envVar\n }\n }\n return undefined\n }\n\n /**\n * Get the name of the environment variable that provided the token.\n */\n getFoundEnvVar(): string | undefined {\n return this.foundEnvVar\n }\n}\n","import consola from \"consola\"\n\nimport type { GitHubTokenProvider } from \"./providers/base\"\nimport type { TokenInfo, TokenValidationResult } from \"./types\"\n\nimport { CLITokenProvider } from \"./providers/cli\"\nimport { DeviceAuthProvider } from \"./providers/device-auth\"\nimport { EnvTokenProvider } from \"./providers/env\"\nimport { FileTokenProvider } from \"./providers/file\"\n\nexport interface GitHubTokenManagerOptions {\n /** Token provided via CLI --github-token argument */\n cliToken?: string\n /** Whether to validate tokens before use */\n validateOnInit?: boolean\n /** Callback when token expires and cannot be refreshed */\n onTokenExpired?: () => void\n}\n\n/**\n * Manages GitHub token acquisition from multiple providers.\n * Providers are tried in priority order until one succeeds.\n */\nexport class GitHubTokenManager {\n private providers: Array<GitHubTokenProvider> = []\n private currentToken: TokenInfo | null = null\n private onTokenExpired?: () => void\n private validateOnInit: boolean\n\n constructor(options: GitHubTokenManagerOptions = {}) {\n this.validateOnInit = options.validateOnInit ?? false\n this.onTokenExpired = options.onTokenExpired\n\n // Initialize providers in priority order\n // Note: GhCliTokenProvider is NOT included because GitHub CLI tokens\n // are obtained via a different OAuth app and cannot access Copilot internal APIs.\n this.providers = [\n new CLITokenProvider(options.cliToken),\n new EnvTokenProvider(),\n new FileTokenProvider(),\n new DeviceAuthProvider(),\n ]\n\n // Sort by priority (lower = higher priority)\n this.providers.sort((a, b) => a.priority - b.priority)\n }\n\n /**\n * Get the current token info (without fetching a new one).\n */\n getCurrentToken(): TokenInfo | null {\n return this.currentToken\n }\n\n /**\n * Get a GitHub token, trying providers in priority order.\n * Caches the result for subsequent calls.\n */\n async getToken(): Promise<TokenInfo> {\n // Return cached token if available\n if (this.currentToken) {\n return this.currentToken\n }\n\n for (const provider of this.providers) {\n if (!(await provider.isAvailable())) {\n continue\n }\n\n consola.debug(`Trying ${provider.name} token provider...`)\n\n const tokenInfo = await provider.getToken()\n if (!tokenInfo) {\n continue\n }\n\n // Optionally validate the token\n if (this.validateOnInit) {\n const validation = await this.validateToken(tokenInfo.token, provider)\n if (!validation.valid) {\n consola.warn(`Token from ${provider.name} provider is invalid: ${validation.error}`)\n continue\n }\n consola.info(`Logged in as ${validation.username}`)\n }\n\n consola.debug(`Using token from ${provider.name} provider`)\n this.currentToken = tokenInfo\n return tokenInfo\n }\n\n throw new Error(\"No valid GitHub token available from any provider\")\n }\n\n /**\n * Validate a token using a provider's validate method.\n */\n async validateToken(token: string, provider?: GitHubTokenProvider): Promise<TokenValidationResult> {\n const p = provider ?? this.providers[0]\n return p.validate(token)\n }\n\n /**\n * Force refresh the current token.\n * Only works if the current token source supports refresh.\n * For non-refreshable sources (CLI, env), this will call onTokenExpired.\n */\n async refresh(): Promise<TokenInfo | null> {\n if (!this.currentToken) {\n // No current token, get a new one\n return this.getToken()\n }\n\n // Check if current token source is refreshable\n if (!this.currentToken.refreshable) {\n consola.warn(`Current token from ${this.currentToken.source} cannot be refreshed`)\n this.onTokenExpired?.()\n return null\n }\n\n // Find the device auth provider for refresh\n const deviceAuthProvider = this.providers.find((p) => p instanceof DeviceAuthProvider)\n if (!deviceAuthProvider) {\n consola.warn(\"[GitHubToken] No provider supports token refresh, triggering re-authentication\")\n this.onTokenExpired?.()\n return null\n }\n\n const newToken = await deviceAuthProvider.refresh()\n if (newToken) {\n this.currentToken = newToken\n return newToken\n }\n\n consola.error(\"[GitHubToken] Failed to refresh token via DeviceAuthProvider\")\n this.onTokenExpired?.()\n return null\n }\n\n /**\n * Clear the current token cache.\n * Does not delete persisted tokens.\n */\n clearCache(): void {\n this.currentToken = null\n }\n\n /**\n * Clear all tokens (including persisted ones).\n */\n async clearAll(): Promise<void> {\n this.currentToken = null\n\n // Clear file-based token\n const fileProvider = this.providers.find((p) => p instanceof FileTokenProvider)\n if (fileProvider) {\n await fileProvider.clearToken()\n }\n }\n\n /**\n * Get all available providers for debugging.\n */\n async getProviders(): Promise<\n Array<{\n name: string\n priority: number\n available: boolean\n }>\n > {\n return Promise.all(\n this.providers.map(async (p) => ({\n name: p.name,\n priority: p.priority,\n available: await p.isAvailable(),\n })),\n )\n }\n}\n","export { CopilotTokenManager, type CopilotTokenManagerOptions } from \"./copilot-token-manager\"\n\n// Managers\nexport { GitHubTokenManager, type GitHubTokenManagerOptions } from \"./github-token-manager\"\n// Providers\nexport { GitHubTokenProvider } from \"./providers/base\"\nexport { CLITokenProvider } from \"./providers/cli\"\nexport { DeviceAuthProvider } from \"./providers/device-auth\"\nexport { EnvTokenProvider } from \"./providers/env\"\nexport { FileTokenProvider } from \"./providers/file\"\n// Types\nexport type { CopilotTokenInfo, TokenInfo, TokenSource, TokenValidationResult } from \"./types\"\n\nimport consola from \"consola\"\n\nimport { setGitHubToken, setTokenState, state } from \"~/lib/state\"\nimport { getGitHubUser } from \"~/lib/token/github-client\"\n\nimport { CopilotTokenManager } from \"./copilot-token-manager\"\nimport { GitHubTokenManager } from \"./github-token-manager\"\n\n/** Global manager instances */\nlet githubTokenManager: GitHubTokenManager | null = null\nlet copilotTokenManager: CopilotTokenManager | null = null\n\nexport interface InitTokenManagersOptions {\n /** Token provided via CLI --github-token argument */\n cliToken?: string\n}\n\n/**\n * Initialize the token management system.\n * This sets up both GitHub and Copilot token managers.\n */\nexport async function initTokenManagers(options: InitTokenManagersOptions = {}): Promise<{\n githubTokenManager: GitHubTokenManager\n copilotTokenManager: CopilotTokenManager\n}> {\n // Create GitHub token manager\n githubTokenManager = new GitHubTokenManager({\n cliToken: options.cliToken,\n validateOnInit: false, // We'll validate manually to show login info\n onTokenExpired: () => {\n consola.error(\"GitHub token has expired. Please run `copilot-api auth` to re-authenticate.\")\n },\n })\n\n // Get GitHub token\n const tokenInfo = await githubTokenManager.getToken()\n setGitHubToken(tokenInfo.token)\n setTokenState({ tokenInfo })\n\n // Log token source\n const isExplicitToken = tokenInfo.source === \"cli\" || tokenInfo.source === \"env\"\n switch (tokenInfo.source) {\n case \"cli\": {\n consola.info(\"Using provided GitHub token (from CLI)\")\n\n break\n }\n case \"env\": {\n consola.info(\"Using GitHub token from environment variable\")\n\n break\n }\n case \"file\": {\n // File is the default, no need to log\n\n break\n }\n // No default\n }\n\n // Show token if configured\n if (state.showGitHubToken) {\n consola.info(\"GitHub token:\", tokenInfo.token)\n }\n\n // Validate and show user info\n // If the token was explicitly provided (CLI or env), give a clear error and abort on failure\n try {\n const user = await getGitHubUser()\n consola.info(`Logged in as ${user.login}`)\n } catch (error) {\n if (isExplicitToken) {\n const source = tokenInfo.source === \"cli\" ? \"--github-token\" : \"environment variable\"\n consola.error(\n `The GitHub token provided via ${source} is invalid or expired.`,\n error instanceof Error ? error.message : error,\n )\n process.exit(1)\n }\n throw error\n }\n\n // Create Copilot token manager\n copilotTokenManager = new CopilotTokenManager({\n githubTokenManager,\n })\n\n // Initialize Copilot token\n // If the token was explicitly provided and Copilot rejects it, abort with clear error\n try {\n const copilotTokenInfo = await copilotTokenManager.initialize()\n setTokenState({ copilotTokenInfo })\n } catch (error) {\n if (isExplicitToken) {\n const source = tokenInfo.source === \"cli\" ? \"--github-token\" : \"environment variable\"\n consola.error(\n `The GitHub token provided via ${source} does not have Copilot access.`,\n error instanceof Error ? error.message : error,\n )\n process.exit(1)\n }\n throw error\n }\n\n return { githubTokenManager, copilotTokenManager }\n}\n\n/**\n * Get the global GitHub token manager instance.\n */\nexport function getGitHubTokenManager(): GitHubTokenManager | null {\n return githubTokenManager\n}\n\n/**\n * Get the global Copilot token manager instance.\n */\nexport function getCopilotTokenManager(): CopilotTokenManager | null {\n return copilotTokenManager\n}\n\n/**\n * Stop all token refresh timers.\n * Call this during cleanup/shutdown.\n */\nexport function stopTokenRefresh(): void {\n copilotTokenManager?.stopAutoRefresh()\n}\n\n/**\n * Proactively ensure the Copilot token is valid.\n * Triggers a refresh if the token is expired/expiring or the last\n * background refresh failed. No-op if the manager is not initialized.\n */\nexport async function ensureValidCopilotToken(): Promise<void> {\n await copilotTokenManager?.ensureValidToken()\n}\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { applyConfigToState } from \"./lib/config/config\"\nimport { PATHS, ensurePaths } from \"./lib/config/paths\"\nimport { initProxy } from \"./lib/proxy\"\nimport { setCliState } from \"./lib/state\"\nimport { DeviceAuthProvider, FileTokenProvider } from \"./lib/token\"\n\ninterface RunAuthOptions {\n verbose: boolean\n showGitHubToken: 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 setCliState({ showGitHubToken: options.showGitHubToken })\n\n await ensurePaths()\n\n // Load config and initialize proxy before any network requests\n const config = await applyConfigToState()\n if (config.proxy) {\n initProxy({ url: config.proxy, fromEnv: false })\n } else {\n initProxy({ url: undefined, fromEnv: true })\n }\n\n // Use DeviceAuthProvider directly for force authentication\n const deviceAuthProvider = new DeviceAuthProvider()\n const tokenInfo = await deviceAuthProvider.getToken()\n\n if (!tokenInfo) {\n throw new Error(\"Failed to obtain GitHub token via device authorization\")\n }\n\n // Validate and show user info\n const validation = await deviceAuthProvider.validate(tokenInfo.token)\n if (validation.valid) {\n consola.info(`Logged in as ${validation.username}`)\n }\n\n // File provider will have already saved the token during device auth\n // But we can verify the file exists\n const fileProvider = new FileTokenProvider()\n if (await fileProvider.isAvailable()) {\n consola.success(\"GitHub token written to\", PATHS.GITHUB_TOKEN_PATH)\n }\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-github-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 showGitHubToken: args[\"show-github-token\"],\n })\n },\n})\n","import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { applyConfigToState } from \"./lib/config/config\"\nimport { ensurePaths } from \"./lib/config/paths\"\nimport { initProxy } from \"./lib/proxy\"\nimport { setGitHubToken } from \"./lib/state\"\nimport { GitHubTokenManager } from \"./lib/token\"\nimport { getCopilotUsage, type QuotaDetail } from \"./lib/token/copilot-client\"\nimport { getGitHubUser } from \"./lib/token/github-client\"\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\n // Load config and initialize proxy before any network requests\n const config = await applyConfigToState()\n if (config.proxy) {\n initProxy({ url: config.proxy, fromEnv: false })\n } else {\n initProxy({ url: undefined, fromEnv: true })\n }\n\n // Use GitHubTokenManager to get token\n const tokenManager = new GitHubTokenManager()\n const tokenInfo = await tokenManager.getToken()\n setGitHubToken(tokenInfo.token)\n\n // Show logged in user\n const user = await getGitHubUser()\n consola.info(`Logged in as ${user.login}`)\n\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 = 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(\"Completions\", usage.quota_snapshots.completions)\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","import type { HeadersCapture } from \"~/lib/context/request\"\n\nimport { state } from \"~/lib/state\"\n\nconst SENSITIVE_HEADER_NAMES = new Set([\"authorization\", \"proxy-authorization\", \"x-api-key\", \"api-key\"])\n\n/**\n * Create an AbortSignal for fetch timeout if configured.\n * Controls the time from request start to receiving response headers.\n * Returns undefined if fetchTimeout is 0 (disabled).\n */\nexport function createFetchSignal(): AbortSignal | undefined {\n return state.fetchTimeout > 0 ? AbortSignal.timeout(state.fetchTimeout * 1000) : undefined\n}\n\n/**\n * Populate a HeadersCapture object with request and response headers.\n * Should be called immediately after fetch(), before !response.ok check,\n * so headers are captured even for error responses.\n */\nexport function captureHttpHeaders(\n capture: HeadersCapture,\n requestHeaders: Record<string, string>,\n response: Response,\n): void {\n capture.request = sanitizeHeadersForHistory(requestHeaders)\n capture.response = Object.fromEntries(response.headers.entries())\n}\n\n/** Return a copy of headers safe to persist in history/error artifacts. */\nexport function sanitizeHeadersForHistory(headers: Record<string, string>): Record<string, string> {\n return Object.fromEntries(\n Object.entries(headers).map(([name, value]) => [\n name,\n SENSITIVE_HEADER_NAMES.has(name.toLowerCase()) ? \"***\" : value,\n ]),\n )\n}\n","import { copilotBaseUrl, copilotHeaders } from \"~/lib/copilot-api\"\nimport { HTTPError } from \"~/lib/error\"\nimport { createFetchSignal } from \"~/lib/fetch-utils\"\nimport { state, setModels } from \"~/lib/state\"\n\n/** Fetch models from Copilot API and cache in global state */\nexport async function cacheModels(): Promise<void> {\n const models = await getModels()\n setModels(models)\n}\n\nexport const getModels = async () => {\n const response = await fetch(`${copilotBaseUrl(state)}/models`, {\n headers: copilotHeaders(state),\n signal: createFetchSignal(),\n })\n\n if (!response.ok) throw await HTTPError.fromResponse(\"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 VisionLimits {\n max_prompt_image_size?: number\n max_prompt_images?: number\n supported_media_types?: Array<string>\n}\n\ninterface ModelLimits {\n max_context_window_tokens?: number\n max_output_tokens?: number\n max_prompt_tokens?: number\n max_non_streaming_output_tokens?: number\n max_inputs?: number\n vision?: VisionLimits\n}\n\ninterface ModelSupports {\n [key: string]: boolean | number | undefined\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 billing?: {\n is_premium?: boolean\n multiplier?: number\n restricted_to?: Array<string>\n }\n capabilities?: ModelCapabilities\n id: string\n model_picker_category?: string\n model_picker_enabled: boolean\n name: string\n object: string\n preview: boolean\n /** Model-specific request headers from CAPI (forwarded to upstream API requests) */\n request_headers?: Record<string, string>\n supported_endpoints?: Array<string>\n vendor: string\n version: string\n policy?: {\n state: string\n terms: string\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 { ensurePaths, PATHS } from \"./lib/config/paths\"\nimport { getModels } from \"./lib/models/client\"\nimport { setCliState, setCopilotToken, setGitHubToken, state } from \"./lib/state\"\nimport { GitHubTokenManager } from \"./lib/token\"\nimport { getCopilotToken, getCopilotUsage } from \"./lib/token/copilot-client\"\nimport { getGitHubUser } from \"./lib/token/github-client\"\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 account?: {\n user: unknown\n copilot: unknown\n }\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 getAccountInfo(): Promise<{\n user: unknown\n copilot: unknown\n} | null> {\n try {\n await ensurePaths()\n\n // Use GitHubTokenManager to get token\n const tokenManager = new GitHubTokenManager()\n const tokenInfo = await tokenManager.getToken()\n setGitHubToken(tokenInfo.token)\n\n if (!state.githubToken) return null\n\n const [user, copilot] = await Promise.all([getGitHubUser(), getCopilotUsage()])\n\n return { user, copilot }\n } catch {\n return null\n }\n}\n\nasync function getDebugInfo(includeAccount: boolean): Promise<DebugInfo> {\n const [version, tokenExists] = await Promise.all([getPackageVersion(), checkTokenExists()])\n\n const info: DebugInfo = {\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 if (includeAccount && tokenExists) {\n const account = await getAccountInfo()\n if (account) {\n info.account = account\n }\n }\n\n return info\n}\n\nfunction printDebugInfoPlain(info: DebugInfo): void {\n let output = `copilot-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 if (info.account) {\n output += `\n\nAccount Info:\n${JSON.stringify(info.account, null, 2)}`\n }\n\n consola.info(output)\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(true)\n\n if (options.json) {\n printDebugInfoJson(debugInfo)\n } else {\n printDebugInfoPlain(debugInfo)\n }\n}\n\n/** Subcommand: debug info (default behavior) */\nconst debugInfo = defineCommand({\n meta: {\n name: \"info\",\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({ json: args.json })\n },\n})\n\n/** Subcommand: debug models */\nconst debugModels = defineCommand({\n meta: {\n name: \"models\",\n description: \"Fetch and display raw model data from Copilot API\",\n },\n args: {\n \"account-type\": {\n type: \"string\",\n alias: \"a\",\n default: \"individual\",\n description: \"The type of GitHub account (individual, business, enterprise)\",\n },\n \"github-token\": {\n type: \"string\",\n alias: \"g\",\n description: \"GitHub token to use (skips interactive auth)\",\n },\n },\n async run({ args }) {\n setCliState({ accountType: args[\"account-type\"] as \"individual\" | \"business\" | \"enterprise\" })\n\n await ensurePaths()\n\n if (args[\"github-token\"]) {\n setGitHubToken(args[\"github-token\"])\n consola.info(\"Using provided GitHub token\")\n } else {\n // Use GitHubTokenManager to get token\n const tokenManager = new GitHubTokenManager()\n const tokenInfo = await tokenManager.getToken()\n setGitHubToken(tokenInfo.token)\n }\n\n // Get Copilot token without setting up refresh interval\n const { token } = await getCopilotToken()\n setCopilotToken(token)\n\n const models = await getModels()\n\n console.log(JSON.stringify(models, null, 2))\n },\n})\n\nexport const debug = defineCommand({\n meta: {\n name: \"debug\",\n description: \"Debug commands for troubleshooting\",\n },\n subCommands: {\n info: debugInfo,\n models: debugModels,\n },\n})\n","import consola from \"consola\"\n\nimport { notifyRateLimiterChanged } from \"~/lib/ws\"\n\n/**\n * Adaptive Rate Limiter\n *\n * Normal mode: Full speed, no delay between requests\n * Rate-limited mode: Queue requests and process with exponential backoff\n * Gradual recovery: After recovery, slowly ramp up speed before full speed\n *\n * Mode transitions:\n * - Normal → Rate-limited: When a 429 error is detected\n * - Rate-limited → Recovering: After recovery timeout OR consecutive successes\n * - Recovering → Normal: After gradual speedup completes\n *\n * Features:\n * - Exponential backoff: Retry delays double each time (10s → 20s → 40s...)\n * - Retry-After support: Uses server-provided wait time if available\n * - Gradual recovery: Slowly ramps up speed after leaving rate-limited mode\n */\n\nexport interface AdaptiveRateLimiterConfig {\n /** Base interval for retries, doubles with each retry (default: 10s) */\n baseRetryIntervalSeconds: number\n /** Maximum retry interval cap (default: 120s) */\n maxRetryIntervalSeconds: number\n /** Interval between requests in rate-limited mode (default: 10s) */\n requestIntervalSeconds: number\n /** Time after which to attempt recovery to normal mode (default: 10 minutes) */\n recoveryTimeoutMinutes: number\n /** Number of consecutive successes needed to recover (default: 5) */\n consecutiveSuccessesForRecovery: number\n /** Gradual recovery steps: intervals to use before full speed (default: [5, 2, 1, 0]) */\n gradualRecoverySteps: Array<number>\n}\n\nconst DEFAULT_CONFIG: AdaptiveRateLimiterConfig = {\n baseRetryIntervalSeconds: 10,\n maxRetryIntervalSeconds: 120,\n requestIntervalSeconds: 10,\n recoveryTimeoutMinutes: 10,\n consecutiveSuccessesForRecovery: 5,\n gradualRecoverySteps: [5, 2, 1, 0], // 5s → 2s → 1s → full speed\n}\n\ninterface QueuedRequest<T> {\n execute: () => Promise<T>\n resolve: (value: T) => void\n reject: (error: unknown) => void\n retryCount: number\n /** Server-provided retry delay from Retry-After header */\n retryAfterSeconds?: number\n /** Timestamp when request was enqueued */\n enqueuedAt: number\n}\n\n/** Result wrapper that includes queue wait time */\nexport interface RateLimitedResult<T> {\n result: T\n /** Time spent waiting in queue (ms), 0 if not queued */\n queueWaitMs: number\n}\n\ntype RateLimiterMode = \"normal\" | \"rate-limited\" | \"recovering\"\n\n/**\n * Adaptive rate limiter that switches between normal, rate-limited, and recovering modes\n * based on API responses.\n */\nexport class AdaptiveRateLimiter {\n private config: AdaptiveRateLimiterConfig\n private mode: RateLimiterMode = \"normal\"\n private queue: Array<QueuedRequest<unknown>> = []\n private processing = false\n private rateLimitedAt: number | null = null\n private consecutiveSuccesses = 0\n private lastRequestTime = 0\n /** Current step in gradual recovery (index into gradualRecoverySteps) */\n private recoveryStepIndex = 0\n /** Abort controller for cancelling pending sleeps during shutdown */\n private sleepAbortController = new AbortController()\n\n constructor(config: Partial<AdaptiveRateLimiterConfig> = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config }\n }\n\n /**\n * Execute a request with adaptive rate limiting.\n * Returns a promise that resolves when the request succeeds.\n * The request will be retried automatically on 429 errors.\n */\n async execute<T>(fn: () => Promise<T>): Promise<RateLimitedResult<T>> {\n if (this.mode === \"normal\") {\n return this.executeInNormalMode(fn)\n }\n if (this.mode === \"recovering\") {\n return this.executeInRecoveringMode(fn)\n }\n return this.enqueue(fn)\n }\n\n /**\n * Check if an error is a rate limit error (429) and extract Retry-After if available\n */\n isRateLimitError(error: unknown): {\n isRateLimit: boolean\n retryAfter?: number\n } {\n if (error && typeof error === \"object\") {\n // Check HTTPError\n if (\"status\" in error && error.status === 429) {\n // Try to extract Retry-After from response headers or body\n const retryAfter = this.extractRetryAfter(error)\n return { isRateLimit: true, retryAfter }\n }\n // Check nested error structure from Copilot\n if (\"responseText\" in error && typeof error.responseText === \"string\") {\n try {\n const parsed: unknown = JSON.parse(error.responseText)\n if (\n parsed\n && typeof parsed === \"object\"\n && \"error\" in parsed\n && parsed.error\n && typeof parsed.error === \"object\"\n && \"code\" in parsed.error\n && parsed.error.code === \"rate_limited\"\n ) {\n return { isRateLimit: true }\n }\n } catch {\n // Not JSON, ignore\n }\n }\n }\n return { isRateLimit: false }\n }\n\n /**\n * Extract Retry-After value from error response\n */\n private extractRetryAfter(error: unknown): number | undefined {\n if (!error || typeof error !== \"object\") return undefined\n\n // Check responseText for JSON with retry_after field\n if (\"responseText\" in error && typeof error.responseText === \"string\") {\n try {\n const parsed: unknown = JSON.parse(error.responseText)\n if (parsed && typeof parsed === \"object\" && \"retry_after\" in parsed && typeof parsed.retry_after === \"number\") {\n return parsed.retry_after\n }\n // Also check nested error.retry_after\n if (\n parsed\n && typeof parsed === \"object\"\n && \"error\" in parsed\n && parsed.error\n && typeof parsed.error === \"object\"\n && \"retry_after\" in parsed.error\n && typeof parsed.error.retry_after === \"number\"\n ) {\n return parsed.error.retry_after\n }\n } catch {\n // Not JSON, ignore\n }\n }\n\n return undefined\n }\n\n /**\n * Execute in normal mode - full speed\n */\n private async executeInNormalMode<T>(fn: () => Promise<T>): Promise<RateLimitedResult<T>> {\n try {\n const result = await fn()\n return { result, queueWaitMs: 0 }\n } catch (error) {\n const { isRateLimit, retryAfter } = this.isRateLimitError(error)\n if (isRateLimit) {\n this.enterRateLimitedMode()\n // Queue this request for retry instead of failing\n return this.enqueue(fn, retryAfter)\n }\n throw error\n }\n }\n\n /**\n * Execute in recovering mode - gradual speedup\n */\n private async executeInRecoveringMode<T>(fn: () => Promise<T>): Promise<RateLimitedResult<T>> {\n const startTime = Date.now()\n const currentInterval = this.config.gradualRecoverySteps[this.recoveryStepIndex] ?? 0\n\n // Wait for the current recovery interval\n if (currentInterval > 0) {\n const now = Date.now()\n const elapsedMs = now - this.lastRequestTime\n const requiredMs = currentInterval * 1000\n\n if (this.lastRequestTime > 0 && elapsedMs < requiredMs) {\n const waitMs = requiredMs - elapsedMs\n await this.sleep(waitMs)\n }\n }\n\n this.lastRequestTime = Date.now()\n\n try {\n const result = await fn()\n\n // Success - advance recovery step\n this.recoveryStepIndex++\n if (this.recoveryStepIndex >= this.config.gradualRecoverySteps.length) {\n this.completeRecovery()\n } else {\n const nextInterval = this.config.gradualRecoverySteps[this.recoveryStepIndex] ?? 0\n consola.info(\n `[RateLimiter] Ramp-up step ${this.recoveryStepIndex}/${this.config.gradualRecoverySteps.length} `\n + `(next interval: ${nextInterval}s)`,\n )\n }\n\n const queueWaitMs = Date.now() - startTime\n return { result, queueWaitMs }\n } catch (error) {\n const { isRateLimit, retryAfter } = this.isRateLimitError(error)\n if (isRateLimit) {\n // Back to rate-limited mode\n consola.warn(\"[RateLimiter] Hit rate limit during ramp-up, returning to rate-limited mode\")\n this.enterRateLimitedMode()\n return this.enqueue(fn, retryAfter)\n }\n throw error\n }\n }\n\n /**\n * Enter rate-limited mode\n */\n private enterRateLimitedMode(): void {\n if (this.mode === \"rate-limited\") return\n\n const previousMode = this.mode\n this.mode = \"rate-limited\"\n this.rateLimitedAt = Date.now()\n this.consecutiveSuccesses = 0\n\n consola.warn(\n `[RateLimiter] Entering rate-limited mode. `\n + `Requests will be queued with exponential backoff (base: ${this.config.baseRetryIntervalSeconds}s).`,\n )\n notifyRateLimiterChanged({\n mode: this.mode,\n previousMode,\n queueLength: this.queue.length,\n consecutiveSuccesses: this.consecutiveSuccesses,\n rateLimitedAt: this.rateLimitedAt,\n })\n }\n\n /**\n * Check if we should try to recover to normal mode\n */\n private shouldAttemptRecovery(): boolean {\n // Check consecutive successes\n if (this.consecutiveSuccesses >= this.config.consecutiveSuccessesForRecovery) {\n consola.info(`[RateLimiter] ${this.consecutiveSuccesses} consecutive successes. Starting ramp-up.`)\n return true\n }\n\n // Check timeout\n if (this.rateLimitedAt) {\n const elapsed = Date.now() - this.rateLimitedAt\n const timeout = this.config.recoveryTimeoutMinutes * 60 * 1000\n if (elapsed >= timeout) {\n consola.info(`[RateLimiter] ${this.config.recoveryTimeoutMinutes} minutes elapsed. Starting ramp-up.`)\n return true\n }\n }\n\n return false\n }\n\n /**\n * Start gradual recovery mode\n */\n private startGradualRecovery(): void {\n const previousMode = this.mode\n this.mode = \"recovering\"\n this.recoveryStepIndex = 0\n this.rateLimitedAt = null\n this.consecutiveSuccesses = 0\n\n const firstInterval = this.config.gradualRecoverySteps[0] ?? 0\n consola.info(\n `[RateLimiter] Starting ramp-up (${this.config.gradualRecoverySteps.length} steps, `\n + `first interval: ${firstInterval}s)`,\n )\n notifyRateLimiterChanged({\n mode: this.mode,\n previousMode,\n queueLength: this.queue.length,\n consecutiveSuccesses: this.consecutiveSuccesses,\n rateLimitedAt: this.rateLimitedAt,\n })\n }\n\n /**\n * Complete recovery to normal mode\n */\n private completeRecovery(): void {\n const previousMode = this.mode\n this.mode = \"normal\"\n this.recoveryStepIndex = 0\n\n consola.success(\"[RateLimiter] Exiting rate-limited mode.\")\n notifyRateLimiterChanged({\n mode: this.mode,\n previousMode,\n queueLength: this.queue.length,\n consecutiveSuccesses: this.consecutiveSuccesses,\n rateLimitedAt: this.rateLimitedAt,\n })\n }\n\n /**\n * Enqueue a request for later execution\n */\n private enqueue<T>(fn: () => Promise<T>, retryAfterSeconds?: number): Promise<RateLimitedResult<T>> {\n return new Promise<RateLimitedResult<T>>((resolve, reject) => {\n const request: QueuedRequest<unknown> = {\n execute: fn as () => Promise<unknown>,\n resolve: resolve as (value: unknown) => void,\n reject,\n retryCount: 0,\n retryAfterSeconds,\n enqueuedAt: Date.now(),\n }\n\n this.queue.push(request)\n\n if (this.queue.length > 1) {\n const position = this.queue.length\n const estimatedWait = (position - 1) * this.config.requestIntervalSeconds\n consola.info(`[RateLimiter] Request queued (position ${position}, ~${estimatedWait}s wait)`)\n }\n\n void this.processQueue()\n })\n }\n\n /**\n * Calculate retry interval with exponential backoff\n */\n private calculateRetryInterval(request: QueuedRequest<unknown>): number {\n // Use server-provided Retry-After if available\n if (request.retryAfterSeconds !== undefined && request.retryAfterSeconds > 0) {\n return request.retryAfterSeconds\n }\n\n // Exponential backoff: base * 2^(retryCount-1), capped at max\n const backoff = this.config.baseRetryIntervalSeconds * Math.pow(2, request.retryCount)\n return Math.min(backoff, this.config.maxRetryIntervalSeconds)\n }\n\n /**\n * Process the queue\n */\n private async processQueue(): Promise<void> {\n if (this.processing) return\n this.processing = true\n\n while (this.queue.length > 0) {\n const request = this.queue[0]\n\n // Check if we should try recovery before processing\n if (this.shouldAttemptRecovery()) {\n this.startGradualRecovery()\n // Continue processing remaining queue items in recovering mode\n // But first, let the current queue drain\n }\n\n // Calculate wait time based on whether this is a retry or new request\n const now = Date.now()\n const elapsedMs = now - this.lastRequestTime\n const intervalSeconds =\n request.retryCount > 0 ? this.calculateRetryInterval(request) : this.config.requestIntervalSeconds\n const requiredMs = intervalSeconds * 1000\n\n if (this.lastRequestTime > 0 && elapsedMs < requiredMs) {\n const waitMs = requiredMs - elapsedMs\n const waitSec = Math.ceil(waitMs / 1000)\n consola.info(`[RateLimiter] Waiting ${waitSec}s before next request...`)\n await this.sleep(waitMs)\n }\n\n this.lastRequestTime = Date.now()\n\n try {\n const result = await request.execute()\n\n // Success!\n this.queue.shift()\n this.consecutiveSuccesses++\n // Clear retry-after on success\n request.retryAfterSeconds = undefined\n // Calculate queue wait time\n const queueWaitMs = Date.now() - request.enqueuedAt\n request.resolve({ result, queueWaitMs })\n\n if (this.mode === \"rate-limited\") {\n consola.info(\n `[RateLimiter] Request succeeded (${this.consecutiveSuccesses}/${this.config.consecutiveSuccessesForRecovery} for ramp-up)`,\n )\n }\n } catch (error) {\n const { isRateLimit, retryAfter } = this.isRateLimitError(error)\n if (isRateLimit) {\n // Still rate limited, retry with exponential backoff\n request.retryCount++\n request.retryAfterSeconds = retryAfter\n this.consecutiveSuccesses = 0\n this.rateLimitedAt = Date.now() // Reset timeout\n\n const nextInterval = this.calculateRetryInterval(request)\n const source = retryAfter ? \"server Retry-After\" : \"exponential backoff\"\n consola.warn(\n `[RateLimiter] Request failed with 429 (retry #${request.retryCount}). `\n + `Retrying in ${nextInterval}s (${source})...`,\n )\n } else {\n // Other error, fail this request and continue with queue\n this.queue.shift()\n request.reject(error)\n }\n }\n }\n\n this.processing = false\n\n // If queue is empty and we're in rate-limited mode, stay in that mode\n // until recovery conditions are met on next request\n }\n\n /**\n * Reject all queued requests immediately.\n * Called during shutdown Phase 1 to drain the queue so queued requests\n * don't waste drain time. Returns the number of rejected requests.\n */\n rejectQueued(): number {\n const count = this.queue.length\n while (this.queue.length > 0) {\n const request = this.queue.shift()\n if (!request) break\n request.reject(new Error(\"Server shutting down\"))\n }\n this.processing = false\n\n // Cancel any pending sleep (processQueue or executeInRecoveringMode)\n this.sleepAbortController.abort()\n this.sleepAbortController = new AbortController()\n\n return count\n }\n\n private sleep(ms: number): Promise<void> {\n const signal = this.sleepAbortController.signal\n if (signal.aborted) return Promise.resolve()\n\n return new Promise((resolve) => {\n const timer = setTimeout(resolve, ms)\n signal.addEventListener(\n \"abort\",\n () => {\n clearTimeout(timer)\n resolve()\n },\n { once: true },\n )\n })\n }\n\n /**\n * Get current status for debugging/monitoring\n */\n getStatus(): {\n mode: RateLimiterMode\n queueLength: number\n consecutiveSuccesses: number\n rateLimitedAt: number | null\n } {\n return {\n mode: this.mode,\n queueLength: this.queue.length,\n consecutiveSuccesses: this.consecutiveSuccesses,\n rateLimitedAt: this.rateLimitedAt,\n }\n }\n\n /** Get the effective configuration */\n getConfig(): AdaptiveRateLimiterConfig {\n return { ...this.config }\n }\n}\n\n/** Singleton instance */\nlet rateLimiterInstance: AdaptiveRateLimiter | null = null\n\n/**\n * Initialize the adaptive rate limiter with configuration\n */\nexport function initAdaptiveRateLimiter(config: Partial<AdaptiveRateLimiterConfig> = {}): void {\n rateLimiterInstance = new AdaptiveRateLimiter(config)\n\n const baseRetry = config.baseRetryIntervalSeconds ?? DEFAULT_CONFIG.baseRetryIntervalSeconds\n const maxRetry = config.maxRetryIntervalSeconds ?? DEFAULT_CONFIG.maxRetryIntervalSeconds\n const interval = config.requestIntervalSeconds ?? DEFAULT_CONFIG.requestIntervalSeconds\n const recovery = config.recoveryTimeoutMinutes ?? DEFAULT_CONFIG.recoveryTimeoutMinutes\n const successes = config.consecutiveSuccessesForRecovery ?? DEFAULT_CONFIG.consecutiveSuccessesForRecovery\n const steps = config.gradualRecoverySteps ?? DEFAULT_CONFIG.gradualRecoverySteps\n\n consola.info(\n `[RateLimiter] Initialized (backoff: ${baseRetry}s-${maxRetry}s, `\n + `interval: ${interval}s, recovery: ${recovery}min or ${successes} successes, `\n + `gradual: [${steps.join(\"s, \")}s])`,\n )\n}\n\n/**\n * Get the rate limiter instance\n */\nexport function getAdaptiveRateLimiter(): AdaptiveRateLimiter | null {\n return rateLimiterInstance\n}\n\n/**\n * Reset the rate limiter singleton (for testing only).\n */\nexport function resetAdaptiveRateLimiter(): void {\n rateLimiterInstance = null\n}\n\n/**\n * Execute a request with adaptive rate limiting.\n * If rate limiter is not initialized, executes immediately.\n * Returns the result along with queue wait time.\n */\nexport async function executeWithAdaptiveRateLimit<T>(fn: () => Promise<T>): Promise<RateLimitedResult<T>> {\n if (!rateLimiterInstance) {\n const result = await fn()\n return { result, queueWaitMs: 0 }\n }\n return rateLimiterInstance.execute(fn)\n}\n","/**\n * Unified model name resolution and normalization.\n *\n * Handles short aliases (opus/sonnet/haiku), versioned names with date suffixes,\n * hyphenated versions (claude-opus-4-6 → claude-opus-4.6), model overrides,\n * and family-level fallbacks.\n */\n\nimport { DEFAULT_MODEL_OVERRIDES, state } from \"~/lib/state\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type ModelFamily = \"opus\" | \"sonnet\" | \"haiku\"\n\n// ============================================================================\n// Model Preference Lists\n// ============================================================================\n\n/** Preferred model order per family, highest priority first. */\nexport const MODEL_PREFERENCE: Record<ModelFamily, Array<string>> = {\n opus: [\n \"claude-opus-4.6\",\n \"claude-opus-4.5\",\n \"claude-opus-41\", // 4.1\n // \"claude-opus-4\",\n ],\n sonnet: [\n \"claude-sonnet-4.6\",\n \"claude-sonnet-4.5\",\n \"claude-sonnet-4\",\n // \"claude-sonnet-3.5\",\n ],\n haiku: [\n \"claude-haiku-4.5\",\n // \"claude-haiku-3.5\",\n ],\n}\n\n// ============================================================================\n// Normalization and Detection\n// ============================================================================\n\n/** Pre-compiled regex: claude-{family}-{major}-{minor}[-YYYYMMDD] */\nconst VERSIONED_RE = /^(claude-(?:opus|sonnet|haiku))-(\\d+)-(\\d{1,2})(?:-\\d{8,})?$/\n\n/** Pre-compiled regex: claude-{family}-{major}-YYYYMMDD (date-only suffix) */\nconst DATE_ONLY_RE = /^(claude-(opus|sonnet|haiku)-\\d+)-\\d{8,}$/\n\n/**\n * Normalize model ID for matching: lowercase and replace dots with dashes.\n * e.g. \"claude-sonnet-4.5\" → \"claude-sonnet-4-5\"\n *\n * Used for feature detection (startsWith matching), NOT for API calls.\n */\nexport function normalizeForMatching(modelId: string): string {\n return modelId.toLowerCase().replaceAll(\".\", \"-\")\n}\n\n/**\n * Normalize a model ID to canonical dot-version form.\n * e.g. \"claude-opus-4-6\" → \"claude-opus-4.6\", \"claude-opus-4-6-1m\" → \"claude-opus-4.6-1m\"\n *\n * Handles modifier suffixes (-fast, -1m) and strips date suffixes (-YYYYMMDD).\n * Non-Claude models or unrecognized patterns are returned as-is.\n *\n * Used for normalizing API response model names to match `/models` endpoint IDs.\n */\nexport function normalizeModelId(modelId: string): string {\n const { base, suffix } = extractModifierSuffix(modelId)\n const versionedMatch = base.match(VERSIONED_RE)\n if (versionedMatch) {\n return `${versionedMatch[1]}-${versionedMatch[2]}.${versionedMatch[3]}${suffix}`\n }\n return modelId\n}\n\n/** Extract the model family from a model ID. */\nexport function getModelFamily(modelId: string): ModelFamily | undefined {\n const normalized = normalizeForMatching(modelId)\n if (normalized.includes(\"opus\")) return \"opus\"\n if (normalized.includes(\"sonnet\")) return \"sonnet\"\n if (normalized.includes(\"haiku\")) return \"haiku\"\n return undefined\n}\n\n/** Check if a model ID belongs to the Sonnet family. */\nexport function isSonnetModel(modelId: string): boolean {\n return getModelFamily(modelId) === \"sonnet\"\n}\n\n/** Check if a model ID belongs to the Opus family. */\nexport function isOpusModel(modelId: string): boolean {\n return getModelFamily(modelId) === \"opus\"\n}\n\n// ============================================================================\n// Model Resolution\n// ============================================================================\n\n/**\n * Find the best available model for a family by checking the preference list\n * against actually available models. Returns the first match, or the top\n * preference as fallback when state.models is unavailable.\n */\nexport function findPreferredModel(family: string): string {\n const preference = MODEL_PREFERENCE[family as ModelFamily]\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive for arbitrary family strings\n if (!preference) return family\n\n if (state.modelIds.size === 0) {\n return preference[0]\n }\n\n for (const candidate of preference) {\n if (state.modelIds.has(candidate)) {\n return candidate\n }\n }\n\n return preference[0]\n}\n\n/** Known model modifier suffixes (e.g., \"-fast\" for fast output mode, \"-1m\" for 1M context). */\nconst KNOWN_MODIFIERS = [\"-fast\", \"-1m\"]\n\n/**\n * Extract known modifier suffix from a model name.\n * e.g. \"claude-opus-4-6-fast\" → { base: \"claude-opus-4-6\", suffix: \"-fast\" }\n */\nfunction extractModifierSuffix(model: string): { base: string; suffix: string } {\n const lower = model.toLowerCase()\n for (const modifier of KNOWN_MODIFIERS) {\n if (lower.endsWith(modifier)) {\n return { base: model.slice(0, -modifier.length), suffix: modifier }\n }\n }\n return { base: model, suffix: \"\" }\n}\n\n/**\n * Normalize bracket notation to hyphen suffix.\n * Claude Code CLI sends model keys like \"opus[1m]\" or \"claude-opus-4.6[1m]\".\n * This converts them to the standard hyphen form: \"opus-1m\", \"claude-opus-4.6-1m\".\n */\nfunction normalizeBracketNotation(model: string): string {\n const match = model.match(/^([^[]+)\\[([^\\]]+)\\]$/)\n if (!match) return model\n return `${match[1]}-${match[2].toLowerCase()}`\n}\n\n/**\n * Resolve a model name to its canonical form, then apply overrides.\n *\n * Override matching order:\n * 1. Check the raw (original) model name against state.modelOverrides\n * 2. Resolve via alias/normalization (resolveModelNameCore)\n * 3. If resolved name differs from raw, check resolved name against overrides\n * 4. Check if the model's family (opus/sonnet/haiku) has an override\n *\n * This is the main entry point for route handlers.\n */\nexport function resolveModelName(model: string): string {\n // 0. Normalize bracket notation: \"opus[1m]\" → \"opus-1m\"\n const normalized = normalizeBracketNotation(model)\n\n // 1. Check raw model name against overrides first\n const rawOverride = state.modelOverrides[normalized]\n if (rawOverride) {\n return resolveOverrideTarget(normalized, rawOverride)\n }\n\n // 2. Normal alias/normalization resolution\n const resolved = resolveModelNameCore(normalized)\n\n // 3. If resolved name is different, check it against overrides too\n if (resolved !== normalized) {\n const resolvedOverride = state.modelOverrides[resolved]\n if (resolvedOverride) {\n return resolveOverrideTarget(resolved, resolvedOverride)\n }\n }\n\n // 4. Check if the model's family has a user-customized override\n // Last-resort fallback: only applies when steps 1-3 didn't match.\n // Propagates to ALL family members regardless of target family.\n // e.g., opus → claude-opus-4.6-1m: claude-opus-4-6 also redirects\n // e.g., sonnet → opus: claude-sonnet-4 also redirects (cross-family)\n // Only skipped when override equals the built-in default (pure alias, not redirection).\n const family = getModelFamily(resolved)\n if (family) {\n const familyOverride = state.modelOverrides[family]\n if (familyOverride && familyOverride !== DEFAULT_MODEL_OVERRIDES[family]) {\n const familyResolved = resolveOverrideTarget(family, familyOverride)\n if (familyResolved !== resolved) {\n return familyResolved\n }\n }\n }\n\n return resolved\n}\n\n/**\n * Resolve override target: if target is directly available, use it;\n * otherwise check for chained overrides, then treat as alias.\n * If still unavailable, fall back to the best available model in the same family.\n *\n * Uses `seen` set to prevent circular override chains.\n */\nfunction resolveOverrideTarget(source: string, target: string, seen?: Set<string>): string {\n if (state.modelIds.size === 0 || state.modelIds.has(target)) {\n return target\n }\n\n // Check if target itself has an override (chained overrides: sonnet → opus → claude-opus-4.6-1m)\n const visited = seen ?? new Set([source])\n const targetOverride = state.modelOverrides[target]\n if (targetOverride && !visited.has(target)) {\n visited.add(target)\n return resolveOverrideTarget(target, targetOverride, visited)\n }\n\n // Target not directly available — might be an alias, resolve it\n const resolved = resolveModelNameCore(target)\n if (resolved !== target) {\n return resolved\n }\n\n // Still not resolved — check if target belongs to a known family and find best available\n const family = getModelFamily(target)\n if (family) {\n const preferred = findPreferredModel(family)\n if (preferred !== target) {\n return preferred\n }\n }\n\n // Can't resolve further — use target as-is\n return target\n}\n\n/**\n * Core model name resolution (without overrides).\n *\n * Handles:\n * 1. Modifier suffixes: \"claude-opus-4-6-fast\" → \"claude-opus-4.6-fast\"\n * 2. Short aliases: \"opus\" → best available opus\n * 3. Hyphenated versions: \"claude-opus-4-6\" → \"claude-opus-4.6\"\n * 4. Date suffixes: \"claude-opus-4-20250514\" → best opus\n */\nfunction resolveModelNameCore(model: string): string {\n // Extract modifier suffix (e.g., \"-fast\") before resolution\n const { base, suffix } = extractModifierSuffix(model)\n\n // Resolve the base model name\n const resolvedBase = resolveBase(base)\n\n // Re-attach suffix and validate availability\n if (suffix) {\n const withSuffix = resolvedBase + suffix\n if (state.modelIds.size === 0 || state.modelIds.has(withSuffix)) {\n return withSuffix\n }\n // Suffixed variant not available, fall back to base\n return resolvedBase\n }\n\n return resolvedBase\n}\n\n/** Resolve a base model name (without modifier suffix) to its canonical form. */\nfunction resolveBase(model: string): string {\n // 1. Short alias: \"opus\" → best opus\n if (model in MODEL_PREFERENCE) {\n return findPreferredModel(model)\n }\n\n // 2. Hyphenated: claude-opus-4-6 or claude-opus-4-6-20250514 → claude-opus-4.6\n // Pattern: claude-{family}-{major}-{minor}[-YYYYMMDD]\n // Minor version is 1-2 digits; date suffix is 8+ digits\n const versionedMatch = model.match(VERSIONED_RE)\n if (versionedMatch) {\n const dotModel = `${versionedMatch[1]}-${versionedMatch[2]}.${versionedMatch[3]}`\n if (state.modelIds.size === 0 || state.modelIds.has(dotModel)) {\n return dotModel\n }\n }\n\n // 3. Date-only suffix: claude-{family}-{major}-YYYYMMDD → base model or best family\n const dateOnlyMatch = model.match(DATE_ONLY_RE)\n if (dateOnlyMatch) {\n const baseModel = dateOnlyMatch[1]\n const family = dateOnlyMatch[2]\n if (state.modelIds.has(baseModel)) {\n return baseModel\n }\n return findPreferredModel(family)\n }\n\n return model\n}\n","/**\n * RequestContext — Complete active representation of a request\n *\n * Holds all data from request entry to completion. Independent of the history\n * system — history is a consumer of RequestContext through events.\n * Each retry creates a new Attempt in the attempts array.\n */\n\nimport type {\n ApiError,\n} from \"~/lib/error\"\nimport type {\n Attempt,\n EffectiveRequest,\n HeadersCapture,\n HistoryEntryData,\n OriginalRequest,\n RequestContext,\n RequestContextEventCallback,\n RequestContextEventData,\n RequestState,\n ResponseData,\n WireRequest,\n} from \"./types\"\nimport type { EndpointType, PipelineInfo, SanitizationInfo, SseEventRecord, TruncationInfo } from \"~/lib/history/store\"\n\nimport { getErrorMessage } from \"~/lib/error\"\nimport { normalizeModelId } from \"~/lib/models/resolver\"\nexport type {\n Attempt,\n EffectiveRequest,\n HeadersCapture,\n HistoryEntryData,\n OriginalRequest,\n RequestContext,\n RequestContextEventCallback,\n RequestContextEventData,\n RequestState,\n ResponseData,\n WireRequest,\n} from \"./types\"\n\n// ─── Implementation ───\n\nlet idCounter = 0\n\nexport function createRequestContext(opts: {\n endpoint: EndpointType\n tuiLogId?: string\n onEvent: RequestContextEventCallback\n}): RequestContext {\n const id = `req_${Date.now()}_${++idCounter}`\n const startTime = Date.now()\n const onEvent = opts.onEvent\n\n // Mutable internal state\n let _state: RequestState = \"pending\"\n let _originalRequest: OriginalRequest | null = null\n let _response: ResponseData | null = null\n let _pipelineInfo: PipelineInfo | null = null\n let _sseEvents: Array<SseEventRecord> | null = null\n let _httpHeaders: { request: Record<string, string>; response: Record<string, string> } | null = null\n let _queueWaitMs = 0\n const _attempts: Array<Attempt> = []\n /** Guard: once complete() or fail() is called, subsequent calls are no-ops */\n let settled = false\n\n function emit(event: RequestContextEventData) {\n try {\n onEvent(event)\n } catch {\n // Swallow event handler errors\n }\n }\n\n const ctx: RequestContext = {\n id,\n tuiLogId: opts.tuiLogId,\n startTime,\n endpoint: opts.endpoint,\n\n get state() {\n return _state\n },\n get durationMs() {\n return Date.now() - startTime\n },\n get settled() {\n return settled\n },\n get originalRequest() {\n return _originalRequest\n },\n get response() {\n return _response\n },\n get pipelineInfo() {\n return _pipelineInfo\n },\n get httpHeaders() {\n return _httpHeaders\n },\n get attempts() {\n return _attempts\n },\n get currentAttempt() {\n return _attempts.at(-1) ?? null\n },\n get queueWaitMs() {\n return _queueWaitMs\n },\n\n setOriginalRequest(req: OriginalRequest) {\n _originalRequest = req\n emit({ type: \"updated\", context: ctx, field: \"originalRequest\" })\n },\n\n setPipelineInfo(info: PipelineInfo) {\n // Direct assignment — caller assembles the complete PipelineInfo\n _pipelineInfo = info\n emit({ type: \"updated\", context: ctx, field: \"pipelineInfo\" })\n },\n\n setSseEvents(events: Array<SseEventRecord>) {\n _sseEvents = events.length > 0 ? events : null\n },\n\n setHttpHeaders(capture: HeadersCapture) {\n if (capture.request && capture.response) {\n _httpHeaders = { request: capture.request, response: capture.response }\n }\n },\n\n beginAttempt(attemptOpts: { strategy?: string; waitMs?: number; truncation?: TruncationInfo }) {\n const attempt: Attempt = {\n index: _attempts.length,\n effectiveRequest: null, // Set later via setAttemptEffectiveRequest\n wireRequest: null, // Set later via setAttemptWireRequest\n response: null,\n error: null,\n strategy: attemptOpts.strategy,\n truncation: attemptOpts.truncation,\n waitMs: attemptOpts.waitMs,\n startTime: Date.now(),\n durationMs: 0,\n }\n _attempts.push(attempt)\n emit({ type: \"updated\", context: ctx, field: \"attempts\" })\n },\n\n setAttemptSanitization(info: SanitizationInfo) {\n const attempt = ctx.currentAttempt\n if (attempt) {\n attempt.sanitization = info\n }\n },\n\n setAttemptEffectiveRequest(req: EffectiveRequest) {\n const attempt = ctx.currentAttempt\n if (attempt) {\n attempt.effectiveRequest = req\n }\n },\n\n setAttemptWireRequest(req: WireRequest) {\n const attempt = ctx.currentAttempt\n if (attempt) {\n attempt.wireRequest = req\n }\n },\n\n setAttemptResponse(response: ResponseData) {\n const attempt = ctx.currentAttempt\n if (attempt) {\n attempt.response = response\n attempt.durationMs = Date.now() - attempt.startTime\n }\n },\n\n setAttemptError(error: ApiError) {\n const attempt = ctx.currentAttempt\n if (attempt) {\n attempt.error = error\n attempt.durationMs = Date.now() - attempt.startTime\n }\n },\n\n addQueueWaitMs(ms: number) {\n _queueWaitMs += ms\n },\n\n transition(newState: RequestState, meta?: Record<string, unknown>) {\n const previousState = _state\n _state = newState\n emit({ type: \"state_changed\", context: ctx, previousState, meta })\n },\n\n complete(response: ResponseData) {\n if (settled) return\n settled = true\n\n // Normalize response model to canonical dot-version form\n // (API may return \"claude-opus-4-6\" instead of \"claude-opus-4.6\")\n if (response.model) response.model = normalizeModelId(response.model)\n _response = response\n ctx.setAttemptResponse(response)\n _state = \"completed\"\n const entry = ctx.toHistoryEntry()\n emit({ type: \"completed\", context: ctx, entry })\n },\n\n fail(model: string, error: unknown) {\n if (settled) return\n settled = true\n\n const errorMsg = getErrorMessage(error)\n _response = {\n success: false,\n model: normalizeModelId(model),\n usage: { input_tokens: 0, output_tokens: 0 },\n error: errorMsg,\n content: null,\n }\n\n // Preserve upstream HTTP error details as structured fields\n if (\n error instanceof Error\n && \"responseText\" in error\n && typeof (error as { responseText: unknown }).responseText === \"string\"\n ) {\n const responseText = (error as { responseText: string }).responseText\n if (responseText) {\n _response.responseText = responseText\n }\n }\n if (error instanceof Error && \"status\" in error && typeof (error as { status: unknown }).status === \"number\") {\n _response.status = (error as { status: number }).status\n }\n\n _state = \"failed\"\n const entry = ctx.toHistoryEntry()\n emit({ type: \"failed\", context: ctx, entry })\n },\n\n toHistoryEntry(): HistoryEntryData {\n // Extract request metadata from the original payload\n const p = _originalRequest?.payload as Record<string, unknown> | undefined\n const entry: HistoryEntryData = {\n id,\n endpoint: opts.endpoint,\n timestamp: startTime,\n durationMs: Date.now() - startTime,\n request: {\n model: _originalRequest?.model,\n messages: _originalRequest?.messages,\n stream: _originalRequest?.stream,\n tools: _originalRequest?.tools,\n system: _originalRequest?.system,\n // Auto-extract metadata from payload (no handler changes needed)\n max_tokens: typeof p?.max_tokens === \"number\" ? p.max_tokens : undefined,\n temperature: typeof p?.temperature === \"number\" ? p.temperature : undefined,\n thinking: p?.thinking ?? undefined,\n },\n }\n\n if (_response) {\n entry.response = _response\n }\n\n // Find truncation from the last attempt that had one\n const lastTruncation = _attempts.findLast((a) => a.truncation)?.truncation\n if (lastTruncation) {\n entry.truncation = lastTruncation\n }\n\n if (_pipelineInfo) {\n entry.pipelineInfo = _pipelineInfo\n }\n\n if (_sseEvents) {\n entry.sseEvents = _sseEvents\n }\n\n if (_httpHeaders) {\n entry.httpHeaders = _httpHeaders\n }\n\n // Extract effective request from the final attempt\n const finalAttempt = _attempts.at(-1)\n if (finalAttempt?.effectiveRequest) {\n const ep = finalAttempt.effectiveRequest\n entry.effectiveRequest = {\n model: ep.model,\n format: ep.format,\n messageCount: ep.messages.length,\n messages: ep.messages,\n system: (ep.payload as Record<string, unknown>)?.system,\n payload: ep.payload,\n }\n }\n\n if (finalAttempt?.wireRequest) {\n const wp = finalAttempt.wireRequest\n entry.wireRequest = {\n model: wp.model,\n format: wp.format,\n messageCount: wp.messages.length,\n messages: wp.messages,\n system: (wp.payload as Record<string, unknown>)?.system,\n payload: wp.payload,\n headers: wp.headers,\n }\n }\n\n // Always include attempt details (even for single attempts)\n if (_attempts.length > 0) {\n entry.attempts = _attempts.map((a) => ({\n index: a.index,\n strategy: a.strategy,\n durationMs: a.durationMs,\n error: a.error?.message,\n truncation: a.truncation,\n sanitization: a.sanitization,\n effectiveMessageCount: a.effectiveRequest?.messages?.length,\n }))\n }\n\n return entry\n },\n }\n\n return ctx\n}\n","/**\n * RequestContextManager — Active request management\n *\n * Manages all in-flight RequestContext instances. Publishes events for\n * WebSocket push and history persistence. The \"active layer\" complementing\n * the history store (persistence layer).\n *\n * Data flow:\n * Handler creates RequestContext → manager.create() registers + emits \"created\"\n * → pipeline processes request, calls ctx.transition()/setPipelineInfo()/etc\n * → each change → manager emits events\n * → ws receives events → pushes to browser\n * → ctx.complete()/fail() → ctx.toHistoryEntry() → store.insert()\n * → manager emits \"completed\"/\"failed\" → removes active context\n */\n\nimport { consola } from \"consola\"\n\nimport type { EndpointType } from \"~/lib/history/store\"\n\nimport { state } from \"~/lib/state\"\n\nimport { notifyActiveRequestChanged } from \"~/lib/ws\"\n\nimport type { HistoryEntryData, RequestContext, RequestContextEventData, RequestState } from \"./request\"\n\nimport { createRequestContext } from \"./request\"\n\n// ─── Event Types ───\n\nexport type RequestContextEvent =\n | { type: \"created\"; context: RequestContext }\n | { type: \"state_changed\"; context: RequestContext; previousState: RequestState; meta?: Record<string, unknown> }\n | { type: \"updated\"; context: RequestContext; field: string }\n | { type: \"completed\"; context: RequestContext; entry: HistoryEntryData }\n | { type: \"failed\"; context: RequestContext; entry: HistoryEntryData }\n\n// ─── Manager Interface ───\n\nexport interface RequestContextManager {\n /** Create and register a new active request context */\n create(opts: { endpoint: EndpointType; tuiLogId?: string }): RequestContext\n\n /** Get an active request by ID */\n get(id: string): RequestContext | undefined\n\n /** Get all active requests (for history UI real-time view) */\n getAll(): Array<RequestContext>\n\n /** Number of active requests */\n readonly activeCount: number\n\n /** Subscribe to context events */\n on(event: \"change\", listener: (event: RequestContextEvent) => void): void\n\n /** Unsubscribe from context events */\n off(event: \"change\", listener: (event: RequestContextEvent) => void): void\n\n /** Start periodic cleanup of stale active contexts */\n startReaper(): void\n\n /** Stop the reaper (for shutdown/cleanup) */\n stopReaper(): void\n\n /** Run a single reaper scan (exposed for testing) */\n _runReaperOnce(): void\n}\n\n// ─── Implementation ───\n\n// ─── Module-level Singleton ───\n\nlet _manager: RequestContextManager | null = null\n\nexport function initRequestContextManager(): RequestContextManager {\n _manager = createRequestContextManager()\n return _manager\n}\n\nexport function getRequestContextManager(): RequestContextManager {\n if (!_manager) throw new Error(\"RequestContextManager not initialized — call initRequestContextManager() first\")\n return _manager\n}\n\n// ─── Factory ───\n\nexport function createRequestContextManager(): RequestContextManager {\n const activeContexts = new Map<string, RequestContext>()\n const listeners = new Set<(event: RequestContextEvent) => void>()\n\n // ─── Stale Request Reaper ───\n\n const REAPER_INTERVAL_MS = 60_000\n let reaperTimer: ReturnType<typeof setInterval> | null = null\n\n /** Single reaper scan — force-fail contexts exceeding maxAge */\n function runReaperOnce() {\n const maxAgeMs = state.staleRequestMaxAge * 1000\n if (maxAgeMs <= 0) return // disabled\n\n for (const [id, ctx] of activeContexts) {\n if (ctx.durationMs > maxAgeMs) {\n consola.warn(\n `[context] Force-failing stale request ${id}`\n + ` (endpoint: ${ctx.endpoint}`\n + `, model: ${ctx.originalRequest?.model ?? \"unknown\"}`\n + `, stream: ${ctx.originalRequest?.stream ?? \"?\"}`\n + `, state: ${ctx.state}`\n + `, age: ${Math.round(ctx.durationMs / 1000)}s`\n + `, max: ${state.staleRequestMaxAge}s)`,\n )\n ctx.fail(\n ctx.originalRequest?.model ?? \"unknown\",\n new Error(`Request exceeded maximum age of ${state.staleRequestMaxAge}s (stale context reaper)`),\n )\n }\n }\n }\n\n function startReaper() {\n if (reaperTimer) return // idempotent\n reaperTimer = setInterval(runReaperOnce, REAPER_INTERVAL_MS)\n }\n\n function stopReaper() {\n if (reaperTimer) {\n clearInterval(reaperTimer)\n reaperTimer = null\n }\n }\n\n function emit(event: RequestContextEvent) {\n for (const listener of listeners) {\n try {\n listener(event)\n } catch {\n // Swallow listener errors\n }\n }\n }\n\n function handleContextEvent(rawEvent: RequestContextEventData) {\n const { type, context } = rawEvent\n\n switch (type) {\n case \"state_changed\": {\n if (rawEvent.previousState) {\n emit({\n type: \"state_changed\",\n context,\n previousState: rawEvent.previousState,\n meta: rawEvent.meta,\n })\n notifyActiveRequestChanged({\n action: \"state_changed\",\n request: summarizeContext(context),\n activeCount: activeContexts.size,\n })\n }\n break\n }\n case \"updated\": {\n if (rawEvent.field) {\n emit({\n type: \"updated\",\n context,\n field: rawEvent.field,\n })\n }\n break\n }\n case \"completed\": {\n if (rawEvent.entry) {\n emit({\n type: \"completed\",\n context,\n entry: rawEvent.entry,\n })\n }\n activeContexts.delete(context.id)\n notifyActiveRequestChanged({\n action: \"completed\",\n requestId: context.id,\n activeCount: activeContexts.size,\n })\n break\n }\n case \"failed\": {\n if (rawEvent.entry) {\n emit({\n type: \"failed\",\n context,\n entry: rawEvent.entry,\n })\n }\n activeContexts.delete(context.id)\n notifyActiveRequestChanged({\n action: \"failed\",\n requestId: context.id,\n activeCount: activeContexts.size,\n })\n break\n }\n default: {\n break\n }\n }\n }\n\n /** Build a lightweight summary of a context for WS broadcast */\n function summarizeContext(ctx: RequestContext) {\n return {\n id: ctx.id,\n endpoint: ctx.endpoint,\n state: ctx.state,\n startTime: ctx.startTime,\n durationMs: ctx.durationMs,\n model: ctx.originalRequest?.model,\n stream: ctx.originalRequest?.stream,\n attemptCount: ctx.attempts.length,\n currentStrategy: ctx.currentAttempt?.strategy,\n queueWaitMs: ctx.queueWaitMs,\n }\n }\n\n return {\n create(opts) {\n const ctx = createRequestContext({\n endpoint: opts.endpoint,\n tuiLogId: opts.tuiLogId,\n onEvent: handleContextEvent,\n })\n activeContexts.set(ctx.id, ctx)\n emit({ type: \"created\", context: ctx })\n notifyActiveRequestChanged({\n action: \"created\",\n request: summarizeContext(ctx),\n activeCount: activeContexts.size,\n })\n return ctx\n },\n\n get(id) {\n return activeContexts.get(id)\n },\n\n getAll() {\n return Array.from(activeContexts.values())\n },\n\n get activeCount() {\n return activeContexts.size\n },\n\n on(_event: \"change\", listener: (event: RequestContextEvent) => void) {\n listeners.add(listener)\n },\n\n off(_event: \"change\", listener: (event: RequestContextEvent) => void) {\n listeners.delete(listener)\n },\n\n startReaper,\n stopReaper,\n _runReaperOnce: runReaperOnce,\n }\n}\n","/**\n * Centralized graceful shutdown management.\n *\n * Coordinates a 4-phase shutdown sequence:\n * Phase 1 (0s): Stop accepting new requests, drain rate limiter queue\n * Phase 2 (0–Ns): Wait for in-flight requests to complete naturally\n * Phase 3 (N–N+Ms): Fire abort signal, wait for handlers to wrap up\n * Phase 4: Force-close all connections, clean up\n *\n * Phase 2/3 timeouts are configurable via state.shutdownGracefulWait and\n * state.shutdownAbortWait (seconds), set from config.yaml `shutdown` section.\n *\n * Handlers integrate via getShutdownSignal() to detect Phase 3 abort.\n */\n\nimport type { ServerInstance } from \"./serve\"\n\nimport consola from \"consola\"\n\nimport type { AdaptiveRateLimiter } from \"./adaptive-rate-limiter\"\nimport type { TuiLogEntry } from \"./tui\"\n\nimport { getAdaptiveRateLimiter } from \"./adaptive-rate-limiter\"\nimport { getRequestContextManager } from \"./context/manager\"\nimport { closeAllClients, getClientCount, stopMemoryPressureMonitor } from \"./history\"\nimport { state } from \"./state\"\nimport { stopTokenRefresh } from \"./token\"\nimport { tuiLogger } from \"./tui\"\nimport { notifyShutdownPhaseChanged } from \"./ws\"\n\n// ============================================================================\n// Configuration constants\n// ============================================================================\n\n/** Polling interval during drain */\nexport const DRAIN_POLL_INTERVAL_MS = 500\n/** Progress log interval during drain */\nexport const DRAIN_PROGRESS_INTERVAL_MS = 5_000\n\n// ============================================================================\n// Module state\n// ============================================================================\n\nlet serverInstance: ServerInstance | null = null\nlet _isShuttingDown = false\nlet shutdownResolve: (() => void) | null = null\nlet shutdownAbortController: AbortController | null = null\nlet shutdownDrainAbortController: AbortController | null = null\nlet shutdownPhase: \"idle\" | \"phase1\" | \"phase2\" | \"phase3\" | \"phase4\" | \"finalized\" = \"idle\"\nlet shutdownPromise: Promise<void> | null = null\n\n/** Transition shutdown phase and broadcast via WebSocket */\nfunction setPhase(phase: typeof shutdownPhase): void {\n const prev = shutdownPhase\n shutdownPhase = phase\n if (prev !== phase) {\n notifyShutdownPhaseChanged({ phase, previousPhase: prev })\n }\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/** Check if the server is in shutdown state (used by middleware to reject new requests) */\nexport function getIsShuttingDown(): boolean {\n return _isShuttingDown\n}\n\n/** Get the current shutdown phase */\nexport function getShutdownPhase(): typeof shutdownPhase {\n return shutdownPhase\n}\n\n/**\n * Get the shutdown abort signal.\n * Returns undefined before shutdown starts. During Phase 1–2 the signal is\n * not aborted; it fires at Phase 3 to tell handlers to wrap up.\n */\nexport function getShutdownSignal(): AbortSignal | undefined {\n return shutdownAbortController?.signal\n}\n\n/**\n * Returns a promise that resolves when the server is shut down via signal.\n * Used by runServer() to keep the async function alive until shutdown.\n */\nexport function waitForShutdown(): Promise<void> {\n return new Promise((resolve) => {\n shutdownResolve = resolve\n })\n}\n\n/** Store the server instance for shutdown */\nexport function setServerInstance(server: ServerInstance): void {\n serverInstance = server\n}\n\n// ============================================================================\n// Dependency injection for testing\n// ============================================================================\n\n/** Dependencies that can be injected for testing */\nexport interface ShutdownDeps {\n tracker?: {\n getActiveRequests: () => Array<TuiLogEntry>\n destroy: () => void\n }\n server?: {\n close: (force?: boolean) => Promise<void>\n }\n rateLimiter?: AdaptiveRateLimiter | null\n stopTokenRefreshFn?: () => void\n closeAllClientsFn?: () => void\n getClientCountFn?: () => number\n /** Request context manager (for stopping stale reaper during shutdown) */\n contextManager?: { stopReaper: () => void }\n /** Timing overrides (for testing — avoids real 20s/120s waits) */\n gracefulWaitMs?: number\n abortWaitMs?: number\n drainPollIntervalMs?: number\n drainProgressIntervalMs?: number\n}\n\n// ============================================================================\n// Drain logic\n// ============================================================================\n\n/** Format a summary of active requests for logging */\nexport function formatActiveRequestsSummary(requests: Array<TuiLogEntry>): string {\n const now = Date.now()\n const lines = requests.map((req) => {\n const age = Math.round((now - req.startTime) / 1000)\n const model = req.model || \"unknown\"\n const tags = req.tags?.length ? ` [${req.tags.join(\", \")}]` : \"\"\n return ` ${req.method} ${req.path} ${model} (${req.status}, ${age}s)${tags}`\n })\n return `Waiting for ${requests.length} active request(s):\\n${lines.join(\"\\n\")}`\n}\n\n/**\n * Wait for all active requests to complete, with periodic progress logging.\n * Returns \"drained\" when all requests finish, \"timeout\" if deadline is reached.\n */\nexport async function drainActiveRequests(\n timeoutMs: number,\n tracker: { getActiveRequests: () => Array<TuiLogEntry> },\n opts?: { pollIntervalMs?: number; progressIntervalMs?: number; abortSignal?: AbortSignal },\n): Promise<\"drained\" | \"timeout\" | \"aborted\"> {\n const pollInterval = opts?.pollIntervalMs ?? DRAIN_POLL_INTERVAL_MS\n const progressInterval = opts?.progressIntervalMs ?? DRAIN_PROGRESS_INTERVAL_MS\n const abortSignal = opts?.abortSignal\n const deadline = Date.now() + timeoutMs\n let lastProgressLog = 0\n\n while (Date.now() < deadline) {\n if (abortSignal?.aborted) return \"aborted\"\n\n const active = tracker.getActiveRequests()\n if (active.length === 0) return \"drained\"\n\n // Log progress periodically\n const now = Date.now()\n if (now - lastProgressLog >= progressInterval) {\n lastProgressLog = now\n consola.info(formatActiveRequestsSummary(active))\n }\n\n const waitResult = await new Promise<\"timer\" | \"aborted\">((resolve) => {\n let settled = false\n let onAbort: (() => void) | undefined\n\n const finish = (value: \"timer\" | \"aborted\") => {\n if (settled) return\n settled = true\n if (abortSignal && onAbort) {\n abortSignal.removeEventListener(\"abort\", onAbort)\n }\n resolve(value)\n }\n\n const timeoutId = setTimeout(() => finish(\"timer\"), pollInterval)\n if (!abortSignal) return\n\n onAbort = () => {\n clearTimeout(timeoutId)\n finish(\"aborted\")\n }\n\n abortSignal.addEventListener(\"abort\", onAbort, { once: true })\n })\n\n if (waitResult === \"aborted\") return \"aborted\"\n }\n\n return \"timeout\"\n}\n\n// ============================================================================\n// Graceful shutdown (4 phases)\n// ============================================================================\n\n/**\n * Perform graceful shutdown in 4 phases.\n *\n * @param signal - The signal that triggered shutdown (e.g. \"SIGINT\")\n * @param deps - Optional dependency injection for testing\n */\nexport async function gracefulShutdown(signal: string, deps?: ShutdownDeps): Promise<void> {\n const tracker = deps?.tracker ?? tuiLogger\n const server = deps?.server ?? serverInstance\n const rateLimiter = deps?.rateLimiter !== undefined ? deps.rateLimiter : getAdaptiveRateLimiter()\n const stopRefresh = deps?.stopTokenRefreshFn ?? stopTokenRefresh\n const closeWsClients = deps?.closeAllClientsFn ?? closeAllClients\n const getWsClientCount = deps?.getClientCountFn ?? getClientCount\n\n // Timing (defaults to state values from config, overridable for testing)\n const gracefulWaitMs = deps?.gracefulWaitMs ?? state.shutdownGracefulWait * 1000\n const abortWaitMs = deps?.abortWaitMs ?? state.shutdownAbortWait * 1000\n const drainOpts = {\n pollIntervalMs: deps?.drainPollIntervalMs ?? DRAIN_POLL_INTERVAL_MS,\n progressIntervalMs: deps?.drainProgressIntervalMs ?? DRAIN_PROGRESS_INTERVAL_MS,\n }\n\n // ── Phase 1: Stop accepting new requests ──────────────────────────────\n _isShuttingDown = true\n shutdownAbortController = new AbortController()\n setPhase(\"phase1\")\n\n consola.info(`Received ${signal}, shutting down gracefully...`)\n\n // Stop stale context reaper before drain (avoid racing with drain logic)\n try {\n const ctxMgr = deps?.contextManager ?? getRequestContextManager()\n ctxMgr.stopReaper()\n } catch {\n // Context manager may not be initialized in tests or early shutdown\n }\n\n // Stop background services\n stopRefresh()\n stopMemoryPressureMonitor()\n\n const wsClients = getWsClientCount()\n if (wsClients > 0) {\n closeWsClients()\n consola.info(`Disconnected ${wsClients} WebSocket client(s)`)\n }\n\n // Drain rate limiter queue immediately\n if (rateLimiter) {\n const rejected = rateLimiter.rejectQueued()\n if (rejected > 0) {\n consola.info(`Rejected ${rejected} queued request(s) from rate limiter`)\n }\n }\n\n // Stop listening for new connections (but keep existing ones alive).\n // Do NOT await — server.close(false) stops accepting new connections immediately,\n // but the returned promise won't resolve until all existing connections end.\n // Upgraded WebSocket connections (even after close handshake) keep the HTTP\n // server open indefinitely, which would block the entire shutdown sequence.\n if (server) {\n server.close(false).catch((error: unknown) => {\n consola.error(\"Error stopping listener:\", error)\n })\n consola.info(\"Stopped accepting new connections\")\n }\n\n // ── Phase 2: Wait for natural completion ──────────────────────────────\n const activeCount = tracker.getActiveRequests().length\n if (activeCount > 0) {\n consola.info(`Phase 2: Waiting up to ${gracefulWaitMs / 1000}s for ${activeCount} active request(s)...`)\n setPhase(\"phase2\")\n shutdownDrainAbortController = new AbortController()\n\n try {\n const phase2Result = await drainActiveRequests(gracefulWaitMs, tracker, {\n ...drainOpts,\n abortSignal: shutdownDrainAbortController.signal,\n })\n if (phase2Result === \"drained\") {\n consola.info(\"All requests completed naturally\")\n finalize(tracker)\n return\n }\n } catch (error) {\n consola.error(\"Error during Phase 2 drain:\", error)\n }\n\n // ── Phase 3: Abort signal + extended wait ─────────────────────────────\n const remaining = tracker.getActiveRequests().length\n consola.info(\n `Phase 3: Sending abort signal to ${remaining} remaining request(s), `\n + `waiting up to ${abortWaitMs / 1000}s...`,\n )\n\n setPhase(\"phase3\")\n shutdownDrainAbortController = new AbortController()\n shutdownAbortController.abort()\n\n try {\n const phase3Result = await drainActiveRequests(abortWaitMs, tracker, {\n ...drainOpts,\n abortSignal: shutdownDrainAbortController.signal,\n })\n if (phase3Result === \"drained\") {\n consola.info(\"All requests completed after abort signal\")\n finalize(tracker)\n return\n }\n } catch (error) {\n consola.error(\"Error during Phase 3 drain:\", error)\n }\n\n // ── Phase 4: Force close ────────────────────────────────────────────\n setPhase(\"phase4\")\n const forceRemaining = tracker.getActiveRequests().length\n consola.warn(`Phase 4: Force-closing ${forceRemaining} remaining request(s)`)\n\n if (server) {\n try {\n await server.close(true)\n } catch (error) {\n consola.error(\"Error force-closing server:\", error)\n }\n }\n }\n\n finalize(tracker)\n}\n\n/** Final cleanup after drain/force-close */\nfunction finalize(tracker: { destroy: () => void }): void {\n setPhase(\"finalized\")\n shutdownDrainAbortController = null\n tracker.destroy()\n consola.info(\"Shutdown complete\")\n shutdownResolve?.()\n}\n\n// ============================================================================\n// Signal handlers\n// ============================================================================\n\ninterface HandleShutdownSignalOptions {\n gracefulShutdownFn?: (signal: string) => Promise<void>\n exitFn?: (code: number) => void\n}\n\nexport function handleShutdownSignal(signal: string, opts?: HandleShutdownSignalOptions): Promise<void> | undefined {\n const shutdownFn = opts?.gracefulShutdownFn ?? ((shutdownSignal: string) => gracefulShutdown(shutdownSignal))\n const exitFn = opts?.exitFn ?? ((code: number) => process.exit(code))\n\n if (_isShuttingDown) {\n if (shutdownPhase === \"phase2\") {\n consola.warn(\"Second signal received, escalating shutdown to abort active requests\")\n shutdownDrainAbortController?.abort()\n return shutdownPromise ?? undefined\n }\n\n if (shutdownPhase === \"phase3\") {\n consola.warn(\"Additional signal received, escalating shutdown to force-close remaining requests\")\n shutdownDrainAbortController?.abort()\n return shutdownPromise ?? undefined\n }\n\n consola.warn(\"Additional signal received during forced shutdown, exiting immediately\")\n exitFn(1)\n return shutdownPromise ?? undefined\n }\n\n shutdownPromise = shutdownFn(signal).catch((error: unknown) => {\n consola.error(\"Fatal error during shutdown:\", error)\n shutdownResolve?.() // Ensure waitForShutdown resolves even on error\n exitFn(1)\n })\n return shutdownPromise\n}\n\n/** Setup process signal handlers for graceful shutdown */\nexport function setupShutdownHandlers(): void {\n const handler = (signal: string) => {\n handleShutdownSignal(signal)\n }\n process.on(\"SIGINT\", () => handler(\"SIGINT\"))\n process.on(\"SIGTERM\", () => handler(\"SIGTERM\"))\n}\n\n// ============================================================================\n// Testing utilities\n// ============================================================================\n\n/** Reset module state (for tests only) */\nexport function _resetShutdownState(): void {\n _isShuttingDown = false\n shutdownResolve = null\n shutdownAbortController = null\n shutdownDrainAbortController = null\n shutdownPhase = \"idle\"\n shutdownPromise = null\n serverInstance = null\n}\n","/** TUI logger - manages request log entries independently of rendering */\n\nimport { state } from \"~/lib/state\"\nimport { generateId } from \"~/lib/utils\"\n\nimport type { RequestUpdate, TuiLogEntry, TuiRenderer } from \"./types\"\n\ninterface StartRequestOptions {\n method: string\n path: string\n model?: string\n isHistoryAccess?: boolean\n requestBodySize?: number\n}\n\n/** Outcome passed to finishRequest to mark a request as completed or failed */\nexport interface RequestOutcome {\n statusCode?: number\n error?: string\n usage?: { inputTokens: number; outputTokens: number }\n}\n\nexport class TuiLogger {\n private entries: Map<string, TuiLogEntry> = new Map()\n private renderer: TuiRenderer | null = null\n private completedQueue: Array<TuiLogEntry> = []\n private completedTimeouts: Map<string, ReturnType<typeof setTimeout>> = new Map()\n private historySize = 5\n private completedDisplayMs = 2000\n\n setRenderer(renderer: TuiRenderer | null): void {\n this.renderer = renderer\n }\n\n setOptions(options: { historySize?: number; completedDisplayMs?: number }): void {\n if (options.historySize !== undefined) {\n this.historySize = options.historySize\n }\n if (options.completedDisplayMs !== undefined) {\n this.completedDisplayMs = options.completedDisplayMs\n }\n }\n\n /**\n * Start tracking a new request\n * Returns the log entry ID\n */\n startRequest(options: StartRequestOptions): string {\n const id = generateId()\n const entry: TuiLogEntry = {\n id,\n method: options.method,\n path: options.path,\n model: options.model,\n startTime: Date.now(),\n status: \"executing\",\n isHistoryAccess: options.isHistoryAccess,\n requestBodySize: options.requestBodySize,\n }\n\n this.entries.set(id, entry)\n this.renderer?.onRequestStart(entry)\n\n return id\n }\n\n /**\n * Update request status\n */\n updateRequest(id: string, update: RequestUpdate): void {\n const entry = this.entries.get(id)\n if (!entry) return\n\n if (update.model !== undefined) {\n entry.model = update.model\n const multiplier = state.modelIndex.get(update.model)?.billing?.multiplier\n if (multiplier !== undefined) entry.multiplier = multiplier\n }\n if (update.clientModel !== undefined) entry.clientModel = update.clientModel\n if (update.status !== undefined) entry.status = update.status\n if (update.statusCode !== undefined) entry.statusCode = update.statusCode\n if (update.durationMs !== undefined) entry.durationMs = update.durationMs\n if (update.inputTokens !== undefined) entry.inputTokens = update.inputTokens\n if (update.outputTokens !== undefined) entry.outputTokens = update.outputTokens\n if (update.cacheReadInputTokens !== undefined) entry.cacheReadInputTokens = update.cacheReadInputTokens\n if (update.cacheCreationInputTokens !== undefined) entry.cacheCreationInputTokens = update.cacheCreationInputTokens\n if (update.estimatedTokens !== undefined) entry.estimatedTokens = update.estimatedTokens\n if (update.error !== undefined) entry.error = update.error\n if (update.queuePosition !== undefined) entry.queuePosition = update.queuePosition\n if (update.queueWaitMs !== undefined) entry.queueWaitMs = update.queueWaitMs\n if (update.streamBytesIn !== undefined) entry.streamBytesIn = update.streamBytesIn\n if (update.streamEventsIn !== undefined) entry.streamEventsIn = update.streamEventsIn\n if (update.streamBlockType !== undefined) entry.streamBlockType = update.streamBlockType\n if (update.tags) {\n entry.tags ??= []\n for (const tag of update.tags) {\n if (!entry.tags.includes(tag)) entry.tags.push(tag)\n }\n }\n\n this.renderer?.onRequestUpdate(id, update)\n }\n\n /**\n * Mark a request as finished (completed or failed).\n *\n * Determines final status from the outcome:\n * - `error` present → \"error\"\n * - `statusCode` in success range (101, 2xx, 3xx) → \"completed\"\n * - `statusCode` outside success range → \"error\"\n * - Neither → \"completed\" (e.g. streaming success with no HTTP status)\n *\n * Safe to call multiple times for the same ID — second call is a no-op.\n * This eliminates the dual-path race between middleware and context consumer.\n */\n finishRequest(id: string, outcome: RequestOutcome): void {\n const entry = this.entries.get(id)\n if (!entry) return\n\n // Determine final status\n if (outcome.error) {\n entry.status = \"error\"\n entry.error = outcome.error\n } else if (outcome.statusCode !== undefined) {\n const sc = outcome.statusCode\n entry.status = sc === 101 || (sc >= 200 && sc < 400) ? \"completed\" : \"error\"\n } else {\n entry.status = \"completed\"\n }\n\n if (outcome.statusCode !== undefined) entry.statusCode = outcome.statusCode\n if (outcome.usage) {\n entry.inputTokens = outcome.usage.inputTokens\n entry.outputTokens = outcome.usage.outputTokens\n }\n entry.durationMs = Date.now() - entry.startTime\n\n this.renderer?.onRequestComplete(entry)\n this.moveToCompleted(id, entry)\n }\n\n // ─── Completed queue management ───\n\n /** Move entry from active to completed queue with auto-cleanup */\n private moveToCompleted(id: string, entry: TuiLogEntry): void {\n this.entries.delete(id)\n this.completedQueue.push(entry)\n\n // Trim queue to max history size\n while (this.completedQueue.length > this.historySize) {\n const removed = this.completedQueue.shift()\n if (removed) {\n const timeoutId = this.completedTimeouts.get(removed.id)\n if (timeoutId) {\n clearTimeout(timeoutId)\n this.completedTimeouts.delete(removed.id)\n }\n }\n }\n\n // Schedule removal from display after delay\n const timeoutId = setTimeout(() => {\n const idx = this.completedQueue.indexOf(entry)\n if (idx !== -1) {\n this.completedQueue.splice(idx, 1)\n }\n this.completedTimeouts.delete(id)\n }, this.completedDisplayMs)\n this.completedTimeouts.set(id, timeoutId)\n }\n\n // ─── Queries ───\n\n /**\n * Get all active entries\n */\n getActiveRequests(): Array<TuiLogEntry> {\n return Array.from(this.entries.values())\n }\n\n /**\n * Get recently completed entries\n */\n getCompletedRequests(): Array<TuiLogEntry> {\n return [...this.completedQueue]\n }\n\n /**\n * Get entry by ID (only active/in-flight entries)\n */\n getRequest(id: string): TuiLogEntry | undefined {\n return this.entries.get(id)\n }\n\n /**\n * Clear all entries and pending timeouts\n */\n clear(): void {\n this.entries.clear()\n this.completedQueue = []\n // Clear all pending timeouts\n for (const timeoutId of this.completedTimeouts.values()) {\n clearTimeout(timeoutId)\n }\n this.completedTimeouts.clear()\n }\n\n /**\n * Destroy the logger and its renderer.\n * Called during graceful shutdown to clean up terminal state (e.g. footer).\n */\n destroy(): void {\n this.clear()\n this.renderer?.destroy()\n this.renderer = null\n }\n}\n\n/** Singleton instance */\nexport const tuiLogger = new TuiLogger()\n","/**\n * TUI logger middleware — tracks every HTTP request in the TUI.\n *\n * Lifecycle:\n * startRequest() → [handler runs] → finishRequest()\n *\n * Completion ownership:\n * - Streaming API requests (SSE): consumer calls finishRequest asynchronously\n * when the stream ends, with correct duration and full usage data.\n * Middleware detects SSE content-type and skips finishRequest.\n * - Non-streaming API requests: consumer calls finishRequest synchronously\n * during await next(). Middleware's subsequent finishRequest is a no-op.\n * - Simple routes (/models, /history, etc.): no consumer — middleware calls\n * finishRequest after await next() with c.res.status.\n * - WebSocket upgrades: middleware calls finishRequest with status 101.\n *\n * finishRequest is idempotent — second call for the same ID is a no-op.\n */\n\nimport type { Context, MiddlewareHandler, Next } from \"hono\"\n\nimport { getErrorMessage, HTTPError } from \"~/lib/error\"\nimport { getIsShuttingDown } from \"~/lib/shutdown\"\n\nimport { tuiLogger } from \"./tracker\"\n\nexport function tuiMiddleware(): MiddlewareHandler {\n return async (c: Context, next: Next) => {\n // Reject new requests during shutdown\n if (getIsShuttingDown()) {\n return c.json({ type: \"error\", error: { type: \"server_error\", message: \"Server is shutting down\" } }, 503)\n }\n\n const method = c.req.method\n const path = c.req.path\n\n // Capture request body size from Content-Length header\n const contentLength = c.req.header(\"content-length\")\n const requestBodySize = contentLength ? Number.parseInt(contentLength, 10) : undefined\n\n const tuiLogId = tuiLogger.startRequest({\n method,\n path,\n model: \"\",\n isHistoryAccess: path.startsWith(\"/history\"),\n requestBodySize,\n })\n\n // Store tracking ID in context for handlers/consumers to use\n c.set(\"tuiLogId\", tuiLogId)\n\n // Detect WebSocket upgrade before calling next() — after the upgrade,\n // c.res may not have a meaningful status.\n const isWebSocketUpgrade = c.req.header(\"upgrade\")?.toLowerCase() === \"websocket\"\n\n try {\n await next()\n\n // WebSocket: treat as 101 regardless of c.res.status\n // (Bun returns 200, Node.js handles upgrade outside Hono)\n if (isWebSocketUpgrade) {\n tuiLogger.finishRequest(tuiLogId, { statusCode: 101 })\n return\n }\n\n // Streaming (SSE): the consumer handles completion asynchronously when\n // the stream finishes — with correct duration and full usage data.\n // Calling finishRequest here would finish prematurely (before the stream\n // ends, without usage).\n const contentType = c.res.headers.get(\"content-type\")\n if (contentType?.includes(\"text/event-stream\")) return\n\n // Non-streaming: finish the request with the actual HTTP status.\n // If a consumer already finished it synchronously during next(), this is a no-op.\n tuiLogger.finishRequest(tuiLogId, { statusCode: c.res.status })\n } catch (error) {\n tuiLogger.finishRequest(tuiLogId, {\n error: getErrorMessage(error),\n statusCode: error instanceof HTTPError ? error.status : undefined,\n })\n throw error\n }\n }\n}\n","/** Pure formatting functions for TUI display */\n\nimport pc from \"picocolors\"\n\nimport type { TuiLogEntry } from \"./types\"\n\nexport function formatTime(date: Date = new Date()): string {\n const h = String(date.getHours()).padStart(2, \"0\")\n const m = String(date.getMinutes()).padStart(2, \"0\")\n const s = String(date.getSeconds()).padStart(2, \"0\")\n return `${h}:${m}:${s}`\n}\n\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`\n return `${(ms / 1000).toFixed(1)}s`\n}\n\nexport function formatNumber(n: number): string {\n if (n >= 1000000) return `${(n / 1000000).toFixed(1)}M`\n if (n >= 1000) return `${(n / 1000).toFixed(1)}K`\n return String(n)\n}\n\nexport function formatBytes(n: number): string {\n if (n >= 1048576) return `${(n / 1048576).toFixed(1)}MB`\n if (n >= 1024) return `${(n / 1024).toFixed(1)}KB`\n return `${n}B`\n}\n\n/** Format streaming metrics for footer display: ↓12.3KB 42ev [thinking] */\nexport function formatStreamInfo(req: TuiLogEntry): string {\n if (req.streamBytesIn === undefined) return \"\"\n const bytes = formatBytes(req.streamBytesIn)\n const events = req.streamEventsIn ?? 0\n const blockType = req.streamBlockType ? ` [${req.streamBlockType}]` : \"\"\n return ` ↓${bytes} ${events}ev${blockType}`\n}\n\n/** Format token counts with colors: dim for cache read, cyan for cache creation */\nexport function formatTokens(input?: number, output?: number, cacheRead?: number, cacheCreation?: number): string {\n if (input === undefined && output === undefined) return \"-\"\n let result = `↑${formatNumber(input ?? 0)}`\n if (cacheRead) result += pc.dim(`+${formatNumber(cacheRead)}`)\n if (cacheCreation) result += pc.cyan(`+${formatNumber(cacheCreation)}`)\n result += ` ↓${formatNumber(output ?? 0)}`\n return result\n}\n","/**\n * Console renderer - simple single-line output for each completed request.\n * Replaces Hono's default logger with cleaner, more informative output.\n */\n\nimport consola from \"consola\"\nimport pc from \"picocolors\"\n\nimport type { RequestUpdate, TuiLogEntry, TuiRenderer } from \"./types\"\n\nimport { formatBytes, formatDuration, formatStreamInfo, formatTime, formatTokens } from \"./format\"\n\n// ANSI escape codes for cursor control\nconst CLEAR_LINE = \"\\x1b[2K\\r\"\n\n/**\n * Console renderer that shows request lifecycle with apt-get style footer\n *\n * Log format:\n * - Start: [....] HH:MM:SS METHOD /path model-name (debug only, dim)\n * - Streaming: [<-->] HH:MM:SS METHOD /path model-name streaming... (dim)\n * - Complete: [ OK ] HH:MM:SS 200 POST /path model-name (3x) 1.2s ↑12.3KB ↓45.6KB ↑1.5K+300 ↓500 (colored)\n * - Error: [FAIL] HH:MM:SS 500 POST /path model-name (3x) 1.2s: error message (red)\n *\n * Color scheme for completed requests:\n * - Prefix: green (success) / red (error)\n * - Time: dim\n * - Method: white\n * - Path: white\n * - Model: magenta\n * - Status: green (success) / red (error)\n * - Duration: yellow\n * - Tokens: cyan (req/res info)\n *\n * Features:\n * - Start lines only shown in debug mode (--verbose)\n * - Streaming lines are dim (less important)\n * - /history API requests are always dim\n * - Sticky footer shows active requests with model and elapsed time\n * - Footer auto-refreshes every second while requests are in-flight\n * - Intercepts consola output to properly handle footer\n */\nexport class ConsoleRenderer implements TuiRenderer {\n private activeRequests: Map<string, TuiLogEntry> = new Map()\n private showActive: boolean\n private footerVisible = false\n private isTTY: boolean\n private originalReporters: Array<unknown> = []\n private footerTimer: ReturnType<typeof setInterval> | null = null\n\n constructor(options?: { showActive?: boolean }) {\n this.showActive = options?.showActive ?? true\n this.isTTY = process.stdout.isTTY\n\n // Install consola reporter that coordinates with footer\n this.installConsolaReporter()\n }\n\n /**\n * Install a custom consola reporter that coordinates with footer\n */\n private installConsolaReporter(): void {\n // Save original reporters\n this.originalReporters = [...consola.options.reporters]\n\n // Create a wrapper reporter that handles footer\n const footerAwareReporter = {\n log: (logObj: { args: Array<unknown>; type: string }) => {\n // Clear footer before any consola output\n this.clearFooterForLog()\n\n // Format and print the log message\n // Trim trailing whitespace/newlines to prevent blank lines\n // (e.g. citty's runMain passes \"\\n\" as a separate arg on errors)\n const message = logObj.args\n .map((arg) => {\n if (typeof arg === \"string\") return arg\n // Error objects have non-enumerable properties, JSON.stringify gives \"{}\"\n if (arg instanceof Error) {\n return arg.stack ?? arg.message\n }\n return JSON.stringify(arg)\n })\n .join(\" \")\n .trimEnd()\n\n // Use appropriate formatting based on log type\n const prefix = this.getLogPrefix(logObj.type)\n if (prefix) {\n process.stdout.write(`${prefix} ${message}\\n`)\n } else {\n process.stdout.write(`${message}\\n`)\n }\n\n // Re-render footer after log\n this.renderFooter()\n },\n }\n\n consola.setReporters([footerAwareReporter])\n }\n\n /**\n * Get log prefix based on log type (includes timestamp)\n */\n private getLogPrefix(type: string): string {\n const time = pc.dim(formatTime())\n\n switch (type) {\n case \"error\":\n case \"fatal\": {\n return `${pc.red(\"[ERR ]\")} ${time}`\n }\n case \"warn\": {\n return `${pc.yellow(\"[WARN]\")} ${time}`\n }\n case \"info\": {\n return `${pc.cyan(\"[INFO]\")} ${time}`\n }\n case \"success\": {\n return `${pc.green(\"[SUCC]\")} ${time}`\n }\n case \"debug\": {\n return `${pc.gray(\"[DBG ]\")} ${time}`\n }\n default: {\n return time\n }\n }\n }\n\n // ─── Footer (active request status line) ───\n\n /**\n * Build footer text showing per-request model and elapsed time.\n *\n * Single: [<-->] POST /v1/messages claude-sonnet-4 3.2s ↓12.3KB 42ev [thinking]\n * Multi: [<-->] claude-sonnet-4 5.2s ↓456KB 120ev [thinking] | claude-haiku-3 0.3s\n */\n private getFooterText(): string {\n const activeCount = this.activeRequests.size\n if (activeCount === 0) return \"\"\n\n const now = Date.now()\n\n if (activeCount === 1) {\n const req = this.activeRequests.values().next().value\n if (!req) return \"\" // should never happen when activeCount === 1\n const elapsed = formatDuration(now - req.startTime)\n const model = req.model ? ` ${req.model}` : \"\"\n const streamInfo = formatStreamInfo(req)\n return pc.dim(`[<-->] ${req.method} ${req.path}${model} ${elapsed}${streamInfo}`)\n }\n\n // Multiple requests — compact: model elapsed stream-info | model elapsed stream-info\n const items = Array.from(this.activeRequests.values()).map((req) => {\n const elapsed = formatDuration(now - req.startTime)\n const label = req.model || `${req.method} ${req.path}`\n const streamInfo = formatStreamInfo(req)\n return `${label} ${elapsed}${streamInfo}`\n })\n return pc.dim(`[<-->] ${items.join(\" | \")}`)\n }\n\n /**\n * Render footer in-place on current line (no newline)\n * Only works on TTY terminals\n */\n private renderFooter(): void {\n if (!this.isTTY) return\n\n const footerText = this.getFooterText()\n if (footerText) {\n process.stdout.write(CLEAR_LINE + footerText)\n this.footerVisible = true\n } else if (this.footerVisible) {\n process.stdout.write(CLEAR_LINE)\n this.footerVisible = false\n }\n }\n\n /**\n * Clear footer and prepare for log output\n */\n private clearFooterForLog(): void {\n if (this.footerVisible && this.isTTY) {\n process.stdout.write(CLEAR_LINE)\n this.footerVisible = false\n }\n }\n\n /** Start periodic footer refresh (every 100ms) to keep elapsed time current */\n private startFooterTimer(): void {\n if (this.footerTimer || !this.isTTY) return\n this.footerTimer = setInterval(() => {\n if (this.activeRequests.size > 0) {\n this.renderFooter()\n } else {\n this.stopFooterTimer()\n }\n }, 100)\n // Don't prevent process exit\n this.footerTimer.unref()\n }\n\n /** Stop periodic footer refresh */\n private stopFooterTimer(): void {\n if (this.footerTimer) {\n clearInterval(this.footerTimer)\n this.footerTimer = null\n }\n }\n\n /**\n * Format a complete log line with colored parts\n *\n * Format: [xxxx] HH:mm:ss <status> <method> <path> <model> (<multiplier>x) <duration> ↑<reqSize> ↓<respSize> ↑<inTokens>+<cache> ↓<outTokens>\n */\n private formatLogLine(parts: {\n prefix: string\n time: string\n method: string\n path: string\n model?: string\n /** Original model name from client (shown when different from resolved model) */\n clientModel?: string\n multiplier?: number\n status?: number\n duration?: string\n requestBodySize?: number\n responseBodySize?: number\n inputTokens?: number\n outputTokens?: number\n cacheReadInputTokens?: number\n cacheCreationInputTokens?: number\n queueWait?: string\n extra?: string\n isError?: boolean\n isDim?: boolean\n }): string {\n const {\n prefix,\n time,\n method,\n path,\n model,\n clientModel,\n multiplier,\n status,\n duration,\n requestBodySize,\n responseBodySize,\n inputTokens,\n outputTokens,\n cacheReadInputTokens,\n cacheCreationInputTokens,\n queueWait,\n extra,\n isError,\n isDim,\n } = parts\n\n if (isDim) {\n const modelPart = model ? ` ${model}` : \"\"\n const extraPart = extra ? ` ${extra}` : \"\"\n return pc.dim(`${prefix} ${time} ${method} ${path}${modelPart}${extraPart}`)\n }\n\n // Colored lines: each part has its own color\n const coloredPrefix = isError ? pc.red(prefix) : pc.green(prefix)\n const coloredTime = pc.dim(time)\n let coloredStatus: string | undefined\n if (status !== undefined) {\n coloredStatus = isError ? pc.red(String(status)) : pc.green(String(status))\n }\n const coloredMethod = pc.white(method)\n const coloredPath = pc.white(path)\n\n // Show \"clientModel → model\" when client requested a different model name\n let coloredModel = \"\"\n if (model) {\n coloredModel =\n clientModel && clientModel !== model ?\n ` ${pc.dim(clientModel)} → ${pc.magenta(model)}`\n : pc.magenta(` ${model}`)\n }\n const coloredMultiplier = multiplier !== undefined ? pc.dim(` (${multiplier}x)`) : \"\"\n const coloredDuration = duration ? ` ${pc.yellow(duration)}` : \"\"\n const coloredQueueWait = queueWait ? ` ${pc.dim(`(queued ${queueWait})`)}` : \"\"\n\n // req/resp body sizes with ↑↓ arrows\n let sizeInfo = \"\"\n if (model) {\n const reqSize = requestBodySize !== undefined ? `↑${formatBytes(requestBodySize)}` : \"\"\n const respSize = responseBodySize !== undefined ? `↓${formatBytes(responseBodySize)}` : \"\"\n const parts = [reqSize, respSize].filter(Boolean).join(\" \")\n if (parts) sizeInfo = ` ${pc.dim(parts)}`\n }\n\n // in-tokens/out-tokens (with cache breakdown)\n let tokenInfo = \"\"\n if (model && (inputTokens !== undefined || outputTokens !== undefined)) {\n tokenInfo = ` ${formatTokens(inputTokens, outputTokens, cacheReadInputTokens, cacheCreationInputTokens)}`\n }\n\n let extraPart = \"\"\n if (extra) {\n extraPart = isError ? pc.red(extra) : extra\n }\n\n const statusAndMethod = coloredStatus ? `${coloredStatus} ${coloredMethod}` : coloredMethod\n\n return `${coloredPrefix} ${coloredTime} ${statusAndMethod} ${coloredPath}${coloredModel}${coloredMultiplier}${coloredDuration}${coloredQueueWait}${sizeInfo}${tokenInfo}${extraPart}`\n }\n\n /**\n * Print a log line with proper footer handling\n */\n private printLog(message: string): void {\n this.clearFooterForLog()\n process.stdout.write(message + \"\\n\")\n this.renderFooter()\n }\n\n onRequestStart(request: TuiLogEntry): void {\n this.activeRequests.set(request.id, request)\n this.startFooterTimer()\n\n // Only show start line in debug mode (consola.level >= 5)\n if (this.showActive && consola.level >= 5) {\n const message = this.formatLogLine({\n prefix: \"[....]\",\n time: formatTime(),\n method: request.method,\n path: request.path,\n model: request.model,\n extra:\n request.queuePosition !== undefined && request.queuePosition > 0 ? `[q#${request.queuePosition}]` : undefined,\n isDim: true,\n })\n this.printLog(message)\n }\n }\n\n onRequestUpdate(id: string, update: RequestUpdate): void {\n const request = this.activeRequests.get(id)\n if (!request) return\n\n Object.assign(request, update)\n }\n\n onRequestComplete(request: TuiLogEntry): void {\n this.activeRequests.delete(request.id)\n\n // Stop timer when no more active requests\n if (this.activeRequests.size === 0) {\n this.stopFooterTimer()\n }\n\n // Skip completed log line for history access (only errors are shown)\n if (request.isHistoryAccess && request.status !== \"error\") {\n this.renderFooter()\n return\n }\n\n const status = request.statusCode\n const isError = request.status === \"error\" || (status !== undefined && status >= 400)\n\n // Only show queue wait if it's significant (> 100ms)\n const queueWait = request.queueWaitMs && request.queueWaitMs > 100 ? formatDuration(request.queueWaitMs) : undefined\n\n // Build extra text from tags and error\n // Tags are supplementary metadata — dim the entire group\n const tagStr = !isError && request.tags?.length ? pc.dim(` (${request.tags.join(\", \")})`) : \"\"\n const errorStr = isError && request.error ? `: ${request.error}` : \"\"\n const extra = tagStr + errorStr || undefined\n\n const message = this.formatLogLine({\n prefix: isError ? \"[FAIL]\" : \"[ OK ]\",\n time: formatTime(),\n method: request.method,\n path: request.path,\n model: request.model,\n clientModel: request.clientModel,\n multiplier: request.multiplier,\n status,\n duration: formatDuration(request.durationMs ?? 0),\n queueWait,\n requestBodySize: request.requestBodySize,\n responseBodySize: request.streamBytesIn,\n inputTokens: request.inputTokens,\n outputTokens: request.outputTokens,\n cacheReadInputTokens: request.cacheReadInputTokens,\n cacheCreationInputTokens: request.cacheCreationInputTokens,\n extra,\n isError,\n })\n this.printLog(message)\n }\n\n destroy(): void {\n this.stopFooterTimer()\n if (this.footerVisible && this.isTTY) {\n process.stdout.write(CLEAR_LINE)\n this.footerVisible = false\n }\n this.activeRequests.clear()\n\n // Restore original reporters\n if (this.originalReporters.length > 0) {\n consola.setReporters(this.originalReporters as Parameters<typeof consola.setReporters>[0])\n }\n }\n}\n","/** TUI module exports */\n\nexport { tuiMiddleware } from \"./middleware\"\nexport { tuiLogger } from \"./tracker\"\nexport type { RequestOutcome } from \"./tracker\"\nexport type { RequestStatus, RequestUpdate, TuiLogEntry, TuiOptions, TuiRenderer } from \"./types\"\n\nimport type { TuiOptions } from \"./types\"\n\nimport { ConsoleRenderer } from \"./console-renderer\"\nimport { tuiLogger } from \"./tracker\"\n\n/** Singleton renderer instance (created once, used for both logging and request tracking) */\nlet renderer: ConsoleRenderer | null = null\n\n/**\n * Initialize the consola reporter for unified log formatting.\n * This should be called as early as possible to capture all logs.\n * Does NOT set up request tracking - call initTuiLogger() for that.\n *\n * @param forceEnable - Force enable even if not TTY (useful for consistent log format)\n */\nexport function initConsolaReporter(forceEnable = true): void {\n if (!renderer && (forceEnable || process.stdout.isTTY)) {\n renderer = new ConsoleRenderer()\n }\n}\n\n/**\n * Initialize request tracking with the TUI renderer.\n * Should be called after initConsolaReporter() and before handling requests.\n */\nexport function initTuiLogger(options?: TuiOptions): void {\n if (renderer) {\n tuiLogger.setRenderer(renderer)\n }\n\n if (options?.historySize !== undefined || options?.completedDisplayMs !== undefined) {\n tuiLogger.setOptions({\n historySize: options.historySize,\n completedDisplayMs: options.completedDisplayMs,\n })\n }\n}\n","import { defineCommand } from \"citty\"\nimport consola from \"consola\"\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\"\nimport { dirname, join } from \"node:path\"\n\n/**\n * Get Claude Code version from package.json\n */\nfunction getClaudeCodeVersion(cliPath: string): string | null {\n try {\n const packageJsonPath = join(dirname(cliPath), \"package.json\")\n if (!existsSync(packageJsonPath)) return null\n\n const packageJson: unknown = JSON.parse(readFileSync(packageJsonPath, \"utf8\"))\n if (\n typeof packageJson === \"object\"\n && packageJson !== null\n && \"version\" in packageJson\n && typeof packageJson.version === \"string\"\n ) {\n return packageJson.version\n }\n return null\n } catch {\n return null\n }\n}\n\n/**\n * Search volta tools directory for Claude Code\n */\nfunction findInVoltaTools(voltaHome: string): Array<string> {\n const paths: Array<string> = []\n\n // Check volta packages directory (npm install -g @anthropic-ai/claude-code)\n const packagesPath = join(\n voltaHome,\n \"tools\",\n \"image\",\n \"packages\",\n \"@anthropic-ai\",\n \"claude-code\",\n \"lib\",\n \"node_modules\",\n \"@anthropic-ai\",\n \"claude-code\",\n \"cli.js\",\n )\n if (existsSync(packagesPath)) {\n paths.push(packagesPath)\n }\n\n // Check volta node tools directory (older installation method)\n const toolsDir = join(voltaHome, \"tools\", \"image\", \"node\")\n if (existsSync(toolsDir)) {\n try {\n for (const version of readdirSync(toolsDir)) {\n const claudePath = join(toolsDir, version, \"lib\", \"node_modules\", \"@anthropic-ai\", \"claude-code\", \"cli.js\")\n if (existsSync(claudePath)) {\n paths.push(claudePath)\n }\n }\n } catch {\n // Ignore errors reading directory\n }\n }\n\n return paths\n}\n\n/**\n * Find all Claude Code CLI paths by checking common locations\n */\nfunction findAllClaudeCodePaths(): Array<string> {\n const possiblePaths: Array<string> = []\n const home = process.env.HOME || \"\"\n\n // Check volta installation\n const voltaHome = process.env.VOLTA_HOME || join(home, \".volta\")\n if (existsSync(voltaHome)) {\n possiblePaths.push(...findInVoltaTools(voltaHome))\n }\n\n // Check npm global installation\n const npmPrefix = process.env.npm_config_prefix\n if (npmPrefix) {\n possiblePaths.push(join(npmPrefix, \"lib\", \"node_modules\", \"@anthropic-ai\", \"claude-code\", \"cli.js\"))\n }\n\n // Check common global npm paths\n const globalPaths = [\n join(home, \".npm-global\", \"lib\", \"node_modules\"),\n \"/usr/local/lib/node_modules\",\n \"/usr/lib/node_modules\",\n ]\n\n for (const base of globalPaths) {\n possiblePaths.push(join(base, \"@anthropic-ai\", \"claude-code\", \"cli.js\"))\n }\n\n // Check bun global installation\n const bunGlobal = join(home, \".bun\", \"install\", \"global\")\n if (existsSync(bunGlobal)) {\n possiblePaths.push(join(bunGlobal, \"node_modules\", \"@anthropic-ai\", \"claude-code\", \"cli.js\"))\n }\n\n // Return all existing paths (deduplicated)\n return [...new Set(possiblePaths.filter((p) => existsSync(p)))]\n}\n\nexport const listClaudeCode = defineCommand({\n meta: {\n name: \"list-claude-code\",\n description: \"List all locally installed Claude Code versions\",\n },\n run() {\n const installations = findAllClaudeCodePaths()\n\n if (installations.length === 0) {\n consola.info(\"No Claude Code installations found\")\n consola.info(\"Searched in: volta, npm global, bun global\")\n return\n }\n\n consola.info(`Found ${installations.length} Claude Code installation(s):`)\n\n for (const [i, path] of installations.entries()) {\n const version = getClaudeCodeVersion(path) ?? \"unknown\"\n consola.info(` ${i + 1}. v${version} ${path}`)\n }\n },\n})\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\nimport fs from \"node:fs/promises\"\n\nimport { PATHS } from \"./lib/config/paths\"\n\nexport async function runLogout(): Promise<void> {\n try {\n await fs.unlink(PATHS.GITHUB_TOKEN_PATH)\n consola.success(\"Logged out successfully. GitHub token removed.\")\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n consola.info(\"No token found. Already logged out.\")\n } else {\n consola.error(\"Failed to remove token:\", error)\n throw error\n }\n }\n}\n\nexport const logout = defineCommand({\n meta: {\n name: \"logout\",\n description: \"Remove stored GitHub token and log out\",\n },\n run() {\n return runLogout()\n },\n})\n","import { defineCommand } from \"citty\"\nimport consola from \"consola\"\nimport { existsSync, promises as fsPromises } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\nimport invariant from \"tiny-invariant\"\n\nimport { applyConfigToState } from \"./lib/config/config\"\nimport { ensurePaths } from \"./lib/config/paths\"\nimport { cacheVSCodeVersion } from \"./lib/copilot-api\"\nimport { cacheModels } from \"./lib/models/client\"\nimport { initProxy } from \"./lib/proxy\"\nimport { setCliState, state } from \"./lib/state\"\nimport { initTokenManagers } from \"./lib/token\"\n\n/**\n * Write Claude Code configuration files for use with Copilot API.\n * Creates/updates:\n * - $HOME/.claude.json - Sets hasCompletedOnboarding: true\n * - $HOME/.claude/settings.json - Sets env variables for Copilot API\n */\nexport async function writeClaudeCodeConfig(serverUrl: string, model: string, smallModel: string): Promise<void> {\n const home = homedir()\n const claudeJsonPath = join(home, \".claude.json\")\n const claudeDir = join(home, \".claude\")\n const settingsPath = join(claudeDir, \"settings.json\")\n\n // Ensure .claude directory exists\n if (!existsSync(claudeDir)) {\n await fsPromises.mkdir(claudeDir, { recursive: true })\n consola.info(`Created directory: ${claudeDir}`)\n }\n\n // Update $HOME/.claude.json\n let claudeJson: Record<string, unknown> = {}\n if (existsSync(claudeJsonPath)) {\n try {\n const buffer = await fsPromises.readFile(claudeJsonPath)\n claudeJson = JSON.parse(buffer.toString()) as Record<string, unknown>\n } catch {\n consola.warn(`Failed to parse ${claudeJsonPath}, creating new file`)\n }\n }\n claudeJson.hasCompletedOnboarding = true\n await fsPromises.writeFile(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + \"\\n\")\n consola.success(`Updated ${claudeJsonPath}`)\n\n // Update $HOME/.claude/settings.json\n let settings: Record<string, unknown> = {}\n if (existsSync(settingsPath)) {\n try {\n const buffer = await fsPromises.readFile(settingsPath)\n settings = JSON.parse(buffer.toString()) as Record<string, unknown>\n } catch {\n consola.warn(`Failed to parse ${settingsPath}, creating new file`)\n }\n }\n\n // Set env configuration\n settings.env = {\n ...(settings.env as Record<string, string> | undefined),\n ANTHROPIC_BASE_URL: serverUrl,\n ANTHROPIC_AUTH_TOKEN: \"copilot-api\",\n ANTHROPIC_MODEL: model,\n ANTHROPIC_DEFAULT_SONNET_MODEL: model,\n ANTHROPIC_SMALL_FAST_MODEL: smallModel,\n ANTHROPIC_DEFAULT_HAIKU_MODEL: smallModel,\n DISABLE_NON_ESSENTIAL_MODEL_CALLS: \"1\",\n CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: \"1\",\n CLAUDE_CODE_ENABLE_TELEMETRY: \"0\",\n }\n\n await fsPromises.writeFile(settingsPath, JSON.stringify(settings, null, 2) + \"\\n\")\n consola.success(`Updated ${settingsPath}`)\n\n consola.box(\n `Claude Code configured!\\n\\n`\n + `Model: ${model}\\n`\n + `Small Model: ${smallModel}\\n`\n + `API URL: ${serverUrl}\\n\\n`\n + `Run 'claude' to start Claude Code.`,\n )\n}\n\ninterface SetupClaudeCodeOptions {\n port: number\n host?: string\n model?: string\n smallModel?: string\n accountType: \"individual\" | \"business\" | \"enterprise\"\n githubToken?: string\n verbose: boolean\n}\n\nexport async function runSetupClaudeCode(options: SetupClaudeCodeOptions): Promise<void> {\n if (options.verbose) {\n consola.level = 5\n consola.info(\"Verbose logging enabled\")\n }\n\n setCliState({ accountType: options.accountType })\n\n // Load config and initialize proxy before any network requests\n await ensurePaths()\n const config = await applyConfigToState()\n if (config.proxy) {\n initProxy({ url: config.proxy, fromEnv: false })\n } else {\n initProxy({ url: undefined, fromEnv: true })\n }\n\n // Authenticate and fetch models\n await cacheVSCodeVersion()\n await initTokenManagers({ cliToken: options.githubToken })\n await cacheModels()\n\n invariant(state.models, \"Models should be loaded by now\")\n const availableModelIds = state.models.data.map((m) => m.id)\n\n let selectedModel: string\n let selectedSmallModel: string\n\n if (options.model && options.smallModel) {\n // Validate the provided models exist\n if (!availableModelIds.includes(options.model)) {\n consola.error(`Invalid model: ${options.model}\\nAvailable models: ${availableModelIds.join(\", \")}`)\n process.exit(1)\n }\n if (!availableModelIds.includes(options.smallModel)) {\n consola.error(`Invalid small model: ${options.smallModel}\\nAvailable models: ${availableModelIds.join(\", \")}`)\n process.exit(1)\n }\n selectedModel = options.model\n selectedSmallModel = options.smallModel\n } else if (options.model || options.smallModel) {\n consola.error(\"Both --model and --small-model must be provided together, or neither for interactive selection\")\n process.exit(1)\n } else {\n // Interactive selection\n selectedModel = await consola.prompt(\"Select a model to use with Claude Code\", {\n type: \"select\",\n options: availableModelIds,\n })\n\n selectedSmallModel = await consola.prompt(\"Select a small model to use with Claude Code\", {\n type: \"select\",\n options: availableModelIds,\n })\n }\n\n const displayHost = options.host ?? \"localhost\"\n const serverUrl = `http://${displayHost}:${options.port}`\n\n await writeClaudeCodeConfig(serverUrl, selectedModel, selectedSmallModel)\n}\n\nexport const setupClaudeCode = defineCommand({\n meta: {\n name: \"setup-claude-code\",\n description: \"Setup Claude Code configuration files to use Copilot API as backend\",\n },\n args: {\n port: {\n alias: \"p\",\n type: \"string\",\n default: \"4141\",\n description: \"Port the Copilot API server will run on\",\n },\n host: {\n alias: \"H\",\n type: \"string\",\n description: \"Host the Copilot API server will bind to (default: localhost)\",\n },\n model: {\n alias: \"m\",\n type: \"string\",\n description: \"Model to use with Claude Code (skips interactive selection, requires --small-model)\",\n },\n \"small-model\": {\n alias: \"s\",\n type: \"string\",\n description: \"Small/fast model to use with Claude Code (skips interactive selection, requires --model)\",\n },\n \"account-type\": {\n alias: \"a\",\n type: \"string\",\n default: \"individual\",\n description: \"Account type to use (individual, business, enterprise)\",\n },\n \"github-token\": {\n alias: \"g\",\n type: \"string\",\n description: \"Provide GitHub token directly (must be generated using the `auth` subcommand)\",\n },\n verbose: {\n alias: \"v\",\n type: \"boolean\",\n default: false,\n description: \"Enable verbose logging\",\n },\n },\n run({ args }) {\n return runSetupClaudeCode({\n port: Number.parseInt(args.port, 10),\n host: args.host,\n model: args.model,\n smallModel: args[\"small-model\"],\n accountType: args[\"account-type\"] as \"individual\" | \"business\" | \"enterprise\",\n githubToken: args[\"github-token\"],\n verbose: args.verbose,\n })\n },\n})\n","/**\n * Cross-runtime HTTP server creation.\n *\n * Replaces srvx with direct @hono/node-server (Node.js) and Bun.serve() (Bun)\n * to give full control over server behavior and logging output.\n */\n\nimport type { Server as NodeHttpServer } from \"node:http\"\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Minimal server interface shared with shutdown.ts */\nexport interface ServerInstance {\n /** Close the server. force=true terminates all active connections immediately. */\n close(force?: boolean): Promise<void>\n /** Node.js HTTP server instance (undefined under Bun). Used for WebSocket injection. */\n nodeServer?: NodeHttpServer\n}\n\nexport interface StartServerOptions {\n /** Hono app's fetch handler */\n fetch: (request: Request, env?: Record<string, unknown>) => Response | Promise<Response>\n port: number\n hostname?: string\n /** hono/bun websocket handler object (Bun only) */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n bunWebSocket?: any\n}\n\n// ============================================================================\n// Server creation\n// ============================================================================\n\n/** Start the HTTP server and return a ServerInstance. */\nexport async function startServer(options: StartServerOptions): Promise<ServerInstance> {\n if (typeof globalThis.Bun !== \"undefined\") {\n return startBunServer(options)\n }\n return startNodeServer(options)\n}\n\n// ============================================================================\n// Node.js\n// ============================================================================\n\nasync function startNodeServer(options: StartServerOptions): Promise<ServerInstance> {\n const { createAdaptorServer } = await import(\"@hono/node-server\")\n\n const nodeServer = createAdaptorServer({ fetch: options.fetch })\n\n // Manual listen for full control over options (reusePort via exclusive: false)\n await new Promise<void>((resolve, reject) => {\n nodeServer.once(\"error\", reject)\n nodeServer.listen(\n {\n port: options.port,\n host: options.hostname,\n exclusive: false,\n },\n () => {\n nodeServer.removeListener(\"error\", reject)\n resolve()\n },\n )\n })\n\n return {\n nodeServer: nodeServer as NodeHttpServer,\n close(force?: boolean): Promise<void> {\n return new Promise((resolve, reject) => {\n if (force && \"closeAllConnections\" in nodeServer) {\n ;(nodeServer as NodeHttpServer).closeAllConnections()\n }\n nodeServer.close((err) => (err ? reject(err) : resolve()))\n })\n },\n }\n}\n\n// ============================================================================\n// Bun\n// ============================================================================\n\nasync function startBunServer(options: StartServerOptions): Promise<ServerInstance> {\n // Bun.serve() passes the server instance as 2nd arg to fetch.\n // Forward it to Hono's env so hono/bun's upgradeWebSocket can call server.upgrade().\n const bunServer = Bun.serve({\n fetch(request: Request, server: unknown) {\n return options.fetch(request, { server })\n },\n port: options.port,\n hostname: options.hostname,\n idleTimeout: 255, // seconds (Bun max — default 10s is too short for LLM streaming)\n ...(options.bunWebSocket ? { websocket: options.bunWebSocket } : {}),\n })\n\n return {\n close(force?: boolean): Promise<void> {\n bunServer.stop(force ?? false)\n return Promise.resolve()\n },\n }\n}\n","","/**\n * System prompt override application.\n *\n * Applies config-driven override / prepend / append rules for Anthropic,\n * OpenAI Chat Completions, and Responses instructions.\n */\n\nimport type { TextBlockParam } from \"~/types/api/anthropic\"\nimport type { ContentPart, Message } from \"~/types/api/openai-chat-completions\"\n\nimport { applyConfigToState } from \"../config/config\"\nimport { state, type CompiledRewriteRule } from \"../state\"\n\n/**\n * Apply overrides to a text block.\n * - line: split by newlines, if a trimmed line matches trimmed `from`, replace that line with `to`\n * - regex: apply regex on the entire text with gms flags (multiline: ^$ match line boundaries, dotAll: . matches \\n)\n */\nexport function applyOverrides(text: string, rules: Array<CompiledRewriteRule>, model?: string): string {\n let result = text\n for (const rule of rules) {\n // Skip rule if it has a model filter and the model doesn't match\n if (rule.modelPattern && (!model || !rule.modelPattern.test(model))) continue\n if (rule.method === \"line\") {\n const lines = result.split(\"\\n\")\n result = lines.map((line) => (line.trim() === (rule.from as string).trim() ? rule.to : line)).join(\"\\n\")\n } else {\n result = result.replace(rule.from as RegExp, rule.to)\n }\n }\n return result\n}\n\n/**\n * Process a plain-text system prompt: apply overrides, prepend, and append.\n *\n * Shared core logic used by all format-specific processors (Anthropic, OpenAI, Responses).\n */\nexport async function processSystemPromptText(text: string, model?: string): Promise<string> {\n const config = await applyConfigToState()\n\n let result = text\n if (state.systemPromptOverrides.length > 0) {\n result = applyOverrides(result, state.systemPromptOverrides, model)\n }\n if (config.system_prompt_prepend) {\n result = config.system_prompt_prepend + \"\\n\\n\" + result\n }\n if (config.system_prompt_append) {\n result = result + \"\\n\\n\" + config.system_prompt_append\n }\n return result\n}\n\nexport async function processAnthropicSystem(\n system: string | Array<TextBlockParam> | undefined,\n model?: string,\n): Promise<string | Array<TextBlockParam> | undefined> {\n if (!system) return system\n\n if (typeof system === \"string\") {\n return processSystemPromptText(system, model)\n }\n\n const config = await applyConfigToState()\n const prepend = config.system_prompt_prepend\n const append = config.system_prompt_append\n\n let result: Array<TextBlockParam> = system\n if (state.systemPromptOverrides.length > 0) {\n result = result.map((block) => ({\n ...block,\n text: applyOverrides(block.text, state.systemPromptOverrides, model),\n }))\n }\n\n if (prepend) {\n result = [{ type: \"text\" as const, text: prepend }, ...result]\n }\n\n if (append) {\n result = [...result, { type: \"text\" as const, text: append }]\n }\n\n return result\n}\n\nexport async function processOpenAIMessages(messages: Array<Message>, model?: string): Promise<Array<Message>> {\n const systemMessages = messages.filter((m) => m.role === \"system\" || m.role === \"developer\")\n if (systemMessages.length === 0) {\n const config = await applyConfigToState()\n let result = messages\n if (config.system_prompt_prepend) {\n result = [{ role: \"system\" as const, content: config.system_prompt_prepend }, ...result]\n }\n if (config.system_prompt_append) {\n result = [...result, { role: \"system\" as const, content: config.system_prompt_append }]\n }\n return result\n }\n\n const config = await applyConfigToState()\n const prepend = config.system_prompt_prepend\n const append = config.system_prompt_append\n\n let result =\n state.systemPromptOverrides.length > 0 ?\n messages.map((msg) => {\n if (msg.role !== \"system\" && msg.role !== \"developer\") return msg\n\n if (typeof msg.content === \"string\") {\n return { ...msg, content: applyOverrides(msg.content, state.systemPromptOverrides, model) }\n }\n\n if (Array.isArray(msg.content)) {\n return {\n ...msg,\n content: msg.content.map((part: ContentPart) => {\n if (part.type === \"text\") {\n return { ...part, text: applyOverrides(part.text, state.systemPromptOverrides, model) }\n }\n return part\n }),\n }\n }\n\n return msg\n })\n : messages\n\n if (prepend) {\n result = [{ role: \"system\" as const, content: prepend }, ...result]\n }\n\n if (append) {\n result = [...result, { role: \"system\" as const, content: append }]\n }\n\n return result\n}\n\n/**\n * Process Responses API `instructions` field (system prompt equivalent).\n *\n * Applies the same overrides, prepend, and append as Anthropic/OpenAI system prompts.\n * Preserves null/undefined pass-through — only processes non-empty strings.\n */\nexport async function processResponsesInstructions(\n instructions: string | null | undefined,\n model?: string,\n): Promise<string | null | undefined> {\n if (!instructions) return instructions\n return processSystemPromptText(instructions, model)\n}\n","/**\n * System-reminder tag detection, rewriting, and removal.\n *\n * Claude Code injects `<system-reminder>` tags into tool results and user\n * messages. Each tag always occupies its own line:\n * \\n<system-reminder>\\n...content...\\n</system-reminder>\n *\n * This module:\n * - Defines all known system-reminder content types\n * - Provides configurable rewriting (transform, keep, or remove tags)\n * - Processes tags at the start/end of text content\n *\n * Rewriting is controlled by `state.rewriteSystemReminders`:\n * - `false` — keep all tags unchanged (default)\n * - `true` — remove all tags\n * - `Array<CompiledRewriteRule>` — rewrite rules evaluated top-down,\n * first match wins. If replacement produces the original content, tag is\n * kept unchanged. If replacement produces an empty string, tag is removed.\n * Otherwise, tag content is replaced with the result.\n */\n\nimport { state } from \"../state\"\n\n// ============================================================================\n// Tag Constants\n// ============================================================================\n\n/** Opening tag — always appears on its own line */\nexport const OPEN_TAG = \"<system-reminder>\"\n\n/** Closing tag — always appears on its own line */\nexport const CLOSE_TAG = \"</system-reminder>\"\n\n// ============================================================================\n// Tag Parsing Types\n// ============================================================================\n\n/** A parsed system-reminder tag found at a text boundary. */\nexport interface ParsedSystemReminderTag {\n /** The inner content between `<system-reminder>` and `</system-reminder>` */\n content: string\n /** Start position of the tag in the original text (the `\\n` before `<system-reminder>`) */\n tagStart: number\n /** End position of the tag (exclusive), i.e. the range is [tagStart, tagEnd) */\n tagEnd: number\n}\n\n/**\n * Extract trailing `<system-reminder>` tags from text.\n *\n * Scans backwards from the end, collecting each tag that sits on its own\n * lines at the text boundary. Returns them outermost-first and the position\n * where main (non-tag) content ends.\n *\n * Used by both:\n * - `removeSystemReminderTags` (filter by content, then rebuild)\n * - `compressToolResultContent` (extract all, generate summaries)\n */\nexport function extractTrailingSystemReminderTags(text: string): {\n mainContentEnd: number\n tags: Array<ParsedSystemReminderTag>\n} {\n const tags: Array<ParsedSystemReminderTag> = []\n let scanEnd = text.length\n\n while (true) {\n const currentTagEnd = scanEnd\n\n // Skip trailing whitespace/newlines (charCode: \\n=10, space=32, \\t=9, \\r=13)\n let end = scanEnd\n while (end > 0) {\n const c = text.codePointAt(end - 1)\n if (c !== 10 && c !== 32 && c !== 9 && c !== 13) break\n end--\n }\n\n // Must end with </system-reminder>\n if (end < CLOSE_TAG.length) break\n if (text.slice(end - CLOSE_TAG.length, end) !== CLOSE_TAG) break\n\n const closeTagStart = end - CLOSE_TAG.length\n\n // </system-reminder> must be at line start (preceded by \\n)\n if (closeTagStart === 0 || text[closeTagStart - 1] !== \"\\n\") break\n\n // Find matching \\n<system-reminder>\\n before it\n const openSearch = \"\\n\" + OPEN_TAG + \"\\n\"\n const openPos = text.lastIndexOf(openSearch, closeTagStart)\n if (openPos === -1) break\n\n // Extract inner content\n const innerStart = openPos + openSearch.length\n const innerEnd = closeTagStart - 1 // the \\n before </system-reminder>\n if (innerStart > innerEnd) break\n\n const content = text.slice(innerStart, innerEnd)\n tags.push({ content, tagStart: openPos, tagEnd: currentTagEnd })\n\n scanEnd = openPos\n }\n\n return { mainContentEnd: scanEnd, tags }\n}\n\n/**\n * Extract leading `<system-reminder>` tags from text.\n *\n * Scans forward from the start, collecting each tag that begins at the text\n * boundary (possibly preceded by whitespace). Returns tags in order and the\n * position where main (non-tag) content starts.\n *\n * Leading tags use the format:\n * [whitespace]<system-reminder>\\n...content...\\n</system-reminder>[\\n|EOF]\n *\n * Note: The first tag may start without a preceding `\\n` (beginning of text).\n */\nexport function extractLeadingSystemReminderTags(text: string): {\n mainContentStart: number\n tags: Array<ParsedSystemReminderTag>\n} {\n const tags: Array<ParsedSystemReminderTag> = []\n let scanStart = 0\n\n while (true) {\n const currentTagStart = scanStart\n\n // Skip leading whitespace (charCode: space=32, \\t=9, \\r=13)\n let start = scanStart\n while (start < text.length) {\n const c = text.codePointAt(start)\n if (c !== 32 && c !== 9 && c !== 13) break\n start++\n }\n\n // Must start with <system-reminder>\n if (start + OPEN_TAG.length > text.length) break\n if (text.slice(start, start + OPEN_TAG.length) !== OPEN_TAG) break\n\n const afterOpen = start + OPEN_TAG.length\n if (afterOpen >= text.length || text[afterOpen] !== \"\\n\") break\n\n // Find closing tag: \\n</system-reminder> followed by \\n or EOF\n const closeNeedle = \"\\n\" + CLOSE_TAG\n let searchFrom = afterOpen\n let closePos = -1\n while (true) {\n const pos = text.indexOf(closeNeedle, searchFrom)\n if (pos === -1) break\n const afterClose = pos + closeNeedle.length\n if (afterClose >= text.length || text[afterClose] === \"\\n\") {\n closePos = pos\n break\n }\n searchFrom = pos + 1\n }\n if (closePos === -1) break\n\n const content = text.slice(afterOpen + 1, closePos)\n\n // tagEnd: skip past \\n</system-reminder> and any trailing newlines\n let endPos = closePos + closeNeedle.length\n while (endPos < text.length && text[endPos] === \"\\n\") endPos++\n\n tags.push({ content, tagStart: currentTagStart, tagEnd: endPos })\n scanStart = endPos\n }\n\n return { mainContentStart: scanStart, tags }\n}\n\n// ============================================================================\n// Rewrite Configuration\n// ============================================================================\n\n/**\n * Determine how to rewrite a system-reminder tag's content.\n *\n * Reads from `state.rewriteSystemReminders`:\n * - `true` → return `\"\"` (remove all tags)\n * - `false` → return `null` (keep all tags unchanged)\n * - `Array<CompiledRewriteRule>` → first matching rule wins (top-down):\n * - `to: \"\"` → return `\"\"` (remove the tag entirely)\n * - `to: \"$0\"` (regex mode) → return `null` (keep unchanged, fast path)\n * - Otherwise → apply replacement:\n * - regex mode: `content.replace(from, to)` with capture group support\n * - line mode: replace exact `from` substring with `to`\n * - If result === original → return `null` (keep)\n * - Otherwise → return the new content\n * - If no rule matches → return `null` (keep)\n *\n * @returns `null` to keep original, `\"\"` to remove, or a new content string\n */\nfunction rewriteReminder(content: string): string | null {\n const rewrite = state.rewriteSystemReminders\n if (rewrite === true) return \"\"\n if (rewrite === false) return null\n\n for (const rule of rewrite) {\n const matched = rule.method === \"line\" ? content.includes(rule.from as string) : (rule.from as RegExp).test(content)\n\n // Reset lastIndex after test() in case of global flag\n if (rule.method !== \"line\") (rule.from as RegExp).lastIndex = 0\n\n if (!matched) continue\n\n // Empty replacement = remove the entire tag\n if (rule.to === \"\") return \"\"\n\n // $0 replacement in regex mode = keep tag unchanged (identity)\n if (rule.method !== \"line\" && rule.to === \"$0\") return null\n\n const result =\n rule.method === \"line\" ?\n content.replaceAll(rule.from as string, rule.to)\n : content.replace(rule.from as RegExp, rule.to)\n\n if (result === content) return null // replacement produced no change → keep\n return result\n }\n\n return null // no rule matched → keep\n}\n\n// ============================================================================\n// Tag Removal\n// ============================================================================\n\n/**\n * Rewrite, remove, or keep `<system-reminder>` tags in text content.\n *\n * Only processes reminders that:\n * 1. Appear at the START or END of content (not embedded in code)\n * 2. Are separated from main content by newlines (indicating injection points)\n *\n * For each tag, `rewriteReminder(content)` decides the action:\n * - `null` → keep the tag unchanged\n * - `\"\"` → remove the tag entirely\n * - new string → replace the tag's inner content\n *\n * This prevents accidental modification of system-reminder tags that appear\n * in tool_result content (e.g., when reading source files that contain\n * these tags as string literals or documentation).\n */\nexport function removeSystemReminderTags(text: string): string {\n let result = text\n let modified = false\n\n // Process trailing tags\n const trailing = extractTrailingSystemReminderTags(result)\n if (trailing.tags.length > 0) {\n let tail = \"\"\n for (const tag of trailing.tags) {\n const rewritten = rewriteReminder(tag.content)\n if (rewritten === null) {\n // Keep original\n tail += result.slice(tag.tagStart, tag.tagEnd)\n } else if (rewritten === \"\") {\n // Remove — don't append anything\n modified = true\n } else {\n // Replace content\n tail += `\\n${OPEN_TAG}\\n${rewritten}\\n${CLOSE_TAG}`\n modified = true\n }\n }\n if (modified) {\n result = result.slice(0, trailing.mainContentEnd) + tail\n }\n }\n\n // Process leading tags\n const leading = extractLeadingSystemReminderTags(result)\n if (leading.tags.length > 0) {\n let head = \"\"\n let leadingModified = false\n for (const tag of leading.tags) {\n const rewritten = rewriteReminder(tag.content)\n if (rewritten === null) {\n // Keep original\n head += result.slice(tag.tagStart, tag.tagEnd)\n } else if (rewritten === \"\") {\n // Remove — don't append anything\n leadingModified = true\n } else {\n // Replace content\n head += `${OPEN_TAG}\\n${rewritten}\\n${CLOSE_TAG}\\n`\n leadingModified = true\n }\n }\n if (leadingModified) {\n result = head + result.slice(leading.mainContentStart)\n modified = true\n }\n }\n\n if (!modified) return text\n\n // Only strip trailing newlines left behind by tag removal — never touch\n // leading whitespace (e.g. indentation in tool_result content like\n // \" 1→const x = 1\") to avoid false \"rewritten\" diffs.\n let end = result.length\n while (end > 0 && result[end - 1] === \"\\n\") end--\n return end < result.length ? result.slice(0, end) : result\n}\n","/**\n * Common types and configuration for auto-truncate modules.\n * Shared between OpenAI and Anthropic format handlers.\n */\n\nimport consola from \"consola\"\nimport fs from \"node:fs/promises\"\n\nimport { PATHS } from \"~/lib/config/paths\"\nimport { HTTPError } from \"~/lib/error\"\nimport { parseTokenLimitError } from \"~/lib/error/parsing\"\nimport {\n CLOSE_TAG,\n extractLeadingSystemReminderTags,\n extractTrailingSystemReminderTags,\n OPEN_TAG,\n} from \"~/lib/system-prompt\"\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\n/** Configuration for auto-truncate behavior */\nexport interface AutoTruncateConfig {\n /** Safety margin percentage to account for token counting differences (default: 2) */\n safetyMarginPercent: number\n /** Percentage of context to preserve uncompressed from the end (default: 0.7 = 70%) */\n preserveRecentPercent: number\n /** Whether to enforce token limit (default: true) */\n checkTokenLimit: boolean\n /** Explicit token limit override (used in reactive retry — caller has already applied margin) */\n targetTokenLimit?: number\n}\n\n/** Maximum number of reactive auto-truncate retries per request */\nexport const MAX_AUTO_TRUNCATE_RETRIES = 5\n\n/** Factor to apply to error-reported limit when retrying (90% of limit) */\nexport const AUTO_TRUNCATE_RETRY_FACTOR = 0.9\n\nexport const DEFAULT_AUTO_TRUNCATE_CONFIG: AutoTruncateConfig = {\n safetyMarginPercent: 2,\n preserveRecentPercent: 0.7,\n checkTokenLimit: true,\n}\n\n// ============================================================================\n// Learned Limits (per-model, with calibration)\n// ============================================================================\n\n/** Per-model learned limits with tokenizer calibration */\nexport interface ModelLimits {\n /** Token upper bound (from error response's reported limit) */\n tokenLimit: number\n /** Calibration factor: actualTokens / gptEstimatedTokens.\n * > 1.0 means GPT tokenizer underestimates (Claude tokenizer produces more tokens).\n * < 1.0 means GPT tokenizer overestimates. */\n calibrationFactor: number\n /** Number of calibration samples (higher = more reliable factor) */\n sampleCount: number\n /** Last updated timestamp (ms since epoch) */\n updatedAt: number\n}\n\nconst learnedLimits = new Map<string, ModelLimits>()\n\n/** Get learned limits for a model (including calibration data) */\nexport function getLearnedLimits(modelId: string): ModelLimits | undefined {\n return learnedLimits.get(modelId)\n}\n\n/**\n * Check whether a model has known limits from previous failures.\n * Used to decide whether to pre-check requests before sending.\n */\nexport function hasKnownLimits(modelId: string): boolean {\n return learnedLimits.has(modelId)\n}\n\n// ============================================================================\n// Token Limit Learning\n// ============================================================================\n\n/**\n * Called when a token limit error (400) occurs.\n * Records the learned limit and optionally updates calibration.\n */\nexport function onTokenLimitExceeded(\n modelId: string,\n reportedLimit: number,\n reportedCurrent?: number,\n estimatedTokens?: number,\n): void {\n // Update learned limits (with calibration data for future pre-checks)\n const existing = learnedLimits.get(modelId)\n\n // Only update if this is the first time or the new limit is lower (more restrictive)\n if (!existing || reportedLimit < existing.tokenLimit) {\n learnedLimits.set(modelId, {\n tokenLimit: reportedLimit,\n calibrationFactor: existing?.calibrationFactor ?? 1.0,\n sampleCount: existing?.sampleCount ?? 0,\n updatedAt: Date.now(),\n })\n consola.info(`[AutoTruncate] Learned token limit for ${modelId}: ${reportedLimit}`)\n }\n\n // Calibrate tokenizer if we have both actual and estimated token counts\n if (reportedCurrent !== undefined && estimatedTokens !== undefined && estimatedTokens > 0) {\n updateCalibration(modelId, reportedCurrent, estimatedTokens)\n const lim = learnedLimits.get(modelId)\n if (lim) {\n consola.info(\n `[AutoTruncate] Calibration for ${modelId}: actual=${reportedCurrent} vs estimated=${estimatedTokens}`\n + ` → factor=${lim.calibrationFactor.toFixed(3)} (${lim.sampleCount} samples)`,\n )\n }\n }\n\n schedulePersist()\n}\n\n/** Reset all dynamic limits (for testing) */\nexport function resetAllLimitsForTesting(): void {\n learnedLimits.clear()\n if (persistTimer) {\n clearTimeout(persistTimer)\n persistTimer = null\n }\n}\n\n// ============================================================================\n// Calibration (EWMA)\n// ============================================================================\n\nconst CALIBRATION_ALPHA = 0.3\nconst CALIBRATION_MIN = 0.5\nconst CALIBRATION_MAX = 3.0\n\n/**\n * Update the per-model calibration factor using EWMA.\n *\n * Called after a token limit error when we know both the GPT tokenizer estimate\n * and the actual token count (from the error response). The ratio between them\n * tells us how much the GPT tokenizer over/under-estimates for this model.\n */\nexport function updateCalibration(modelId: string, actualTokens: number, estimatedTokens: number): void {\n if (estimatedTokens <= 0) return\n const limits = learnedLimits.get(modelId)\n if (!limits) return\n\n const rawFactor = actualTokens / estimatedTokens\n const clamped = Math.max(CALIBRATION_MIN, Math.min(CALIBRATION_MAX, rawFactor))\n\n if (limits.sampleCount === 0) {\n limits.calibrationFactor = clamped\n } else {\n limits.calibrationFactor = CALIBRATION_ALPHA * clamped + (1 - CALIBRATION_ALPHA) * limits.calibrationFactor\n }\n limits.sampleCount++\n limits.updatedAt = Date.now()\n}\n\n/** Apply calibration factor to a GPT tokenizer estimate */\nexport function calibrate(modelId: string, gptEstimate: number): number {\n const limits = learnedLimits.get(modelId)\n if (!limits || limits.sampleCount === 0) return gptEstimate\n return Math.ceil(gptEstimate * limits.calibrationFactor)\n}\n\n// ============================================================================\n// Dynamic Safety Margin\n// ============================================================================\n\nconst BASE_MARGIN = 0.03\nconst BONUS_MARGIN_PER_SAMPLE = 0.07\n\n/**\n * Compute dynamic safety margin based on calibration confidence.\n * Fewer samples → wider margin (conservative). More samples → narrower margin.\n *\n * - 0 samples: 10% (0.03 + 0.07)\n * - 1 sample: 10%\n * - 10 samples: ~3.7%\n * - ∞ samples: 3%\n */\nexport function computeSafetyMargin(sampleCount: number): number {\n if (sampleCount <= 0) return BASE_MARGIN + BONUS_MARGIN_PER_SAMPLE\n return BASE_MARGIN + BONUS_MARGIN_PER_SAMPLE / sampleCount\n}\n\n// ============================================================================\n// Limit Persistence\n// ============================================================================\n\ninterface LearnedLimitsFile {\n version: 1\n limits: Record<string, ModelLimits>\n}\n\nlet persistTimer: ReturnType<typeof setTimeout> | null = null\nconst PERSIST_DEBOUNCE_MS = 5000\n\n/** Schedule an async write of learned limits (debounced) */\nexport function schedulePersist(): void {\n if (persistTimer) return\n persistTimer = setTimeout(() => {\n persistTimer = null\n void persistLimits()\n }, PERSIST_DEBOUNCE_MS)\n}\n\n/** Write learned limits to disk */\nexport async function persistLimits(): Promise<void> {\n if (learnedLimits.size === 0) return\n const data: LearnedLimitsFile = { version: 1, limits: Object.fromEntries(learnedLimits) }\n try {\n await fs.writeFile(PATHS.LEARNED_LIMITS, JSON.stringify(data, null, 2), \"utf8\")\n } catch {\n // Write failure is non-critical — limits will be re-learned on next error\n }\n}\n\n/** Load previously persisted limits from disk (called at startup) */\nexport async function loadPersistedLimits(): Promise<void> {\n try {\n const raw = await fs.readFile(PATHS.LEARNED_LIMITS, \"utf8\")\n const data = JSON.parse(raw) as LearnedLimitsFile\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive: persisted file may have unexpected version\n if (data.version !== 1) return\n for (const [modelId, lim] of Object.entries(data.limits)) {\n if (lim.tokenLimit > 0 && lim.calibrationFactor >= CALIBRATION_MIN && lim.calibrationFactor <= CALIBRATION_MAX) {\n learnedLimits.set(modelId, lim)\n }\n }\n if (learnedLimits.size > 0) {\n consola.info(`[AutoTruncate] Loaded learned limits for ${learnedLimits.size} model(s)`)\n }\n } catch {\n // File doesn't exist or is corrupted — start fresh\n }\n}\n\n// ============================================================================\n// Reactive Auto-Truncate Helpers\n// ============================================================================\n\n/** Copilot error structure for JSON parsing */\ninterface CopilotErrorBody {\n error?: {\n message?: string\n code?: string\n type?: string\n }\n}\n\n/** Result from tryParseAndLearnLimit */\nexport interface LimitErrorInfo {\n type: \"token_limit\"\n /** The reported limit (tokens) */\n limit?: number\n /** The current usage that exceeded the limit */\n current?: number\n}\n\n/**\n * Parse an HTTPError to detect token limit errors,\n * and record the learned limit for future pre-checks.\n *\n * When `estimatedTokens` is provided (the GPT tokenizer estimate at the time\n * of the error), also updates the per-model calibration factor.\n *\n * Returns error info if the error is a retryable token limit error, null otherwise.\n */\nexport function tryParseAndLearnLimit(\n error: HTTPError,\n modelId: string,\n learn = true,\n estimatedTokens?: number,\n): LimitErrorInfo | null {\n // 400 → try to parse token limit\n if (error.status === 400) {\n let errorJson: CopilotErrorBody | undefined\n try {\n errorJson = JSON.parse(error.responseText) as CopilotErrorBody\n } catch {\n return null\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- errorJson.error may be undefined at runtime\n if (!errorJson?.error?.message) return null\n\n // Check OpenAI format (code: \"model_max_prompt_tokens_exceeded\")\n // or Anthropic format (type: \"invalid_request_error\")\n const isTokenError =\n errorJson.error.code === \"model_max_prompt_tokens_exceeded\" || errorJson.error.type === \"invalid_request_error\"\n\n if (!isTokenError) return null\n\n const tokenInfo = parseTokenLimitError(errorJson.error.message)\n if (!tokenInfo) return null\n\n // Record the learned limit (only when auto-truncate is enabled)\n if (learn) {\n onTokenLimitExceeded(modelId, tokenInfo.limit, tokenInfo.current, estimatedTokens)\n }\n\n return {\n type: \"token_limit\",\n limit: tokenInfo.limit,\n current: tokenInfo.current,\n }\n }\n\n return null\n}\n\n// ============================================================================\n// Tool Result Compression\n// ============================================================================\n\n/** Threshold for large tool_result content (bytes) */\nexport const LARGE_TOOL_RESULT_THRESHOLD = 10000 // 10KB\n\n/** Maximum length for compressed tool_result summary */\nconst COMPRESSED_SUMMARY_LENGTH = 500\n\n/**\n * Compress a large tool_result content to a summary.\n * Keeps the first and last portions with a note about truncation.\n *\n * Preserves `<system-reminder>` tag wrappers (injected by Claude Code)\n * with a truncated summary of their content, instead of letting them\n * get sliced into broken XML fragments by character-level truncation.\n */\nexport function compressToolResultContent(content: string): string {\n if (content.length <= LARGE_TOOL_RESULT_THRESHOLD) {\n return content\n }\n\n // Extract trailing <system-reminder> tags before compression.\n // These are preserved as truncated summaries instead of being sliced\n // into broken XML fragments by character-level truncation.\n const { mainContentEnd, tags } = extractTrailingSystemReminderTags(content)\n const reminders = tags.map((tag) => {\n const summary = tag.content.trim().split(\"\\n\")[0].slice(0, 80)\n return `${OPEN_TAG}\\n[Truncated] ${summary}\\n${CLOSE_TAG}`\n })\n\n const mainContent = content.slice(0, mainContentEnd)\n\n // Compress the main content (without trailing system-reminder tags)\n const halfLen = Math.floor(COMPRESSED_SUMMARY_LENGTH / 2)\n const start = mainContent.slice(0, halfLen)\n const end = mainContent.slice(-halfLen)\n const removedChars = mainContent.length - COMPRESSED_SUMMARY_LENGTH\n\n let result = `${start}\\n\\n[... ${removedChars.toLocaleString()} characters omitted for brevity ...]\\n\\n${end}`\n\n // Re-append preserved system-reminder tags\n if (reminders.length > 0) {\n result += \"\\n\" + reminders.join(\"\\n\")\n }\n\n return result\n}\n\n// ============================================================================\n// Compacted Text Block Compression\n// ============================================================================\n\n/** Prefix that identifies a compacted tool result in a system-reminder tag */\nconst COMPACTED_RESULT_PREFIX = \"Result of calling the \"\n\n/**\n * Compress a compacted tool result text block.\n *\n * Claude Code compacts tool_result blocks into text blocks wrapped in\n * `<system-reminder>` tags during conversation summarization. Format:\n *\n * <system-reminder>\n * Result of calling the Read tool: \" 1→...file content...\"\n * </system-reminder>\n *\n * These blocks can be very large (entire file contents) but are low-value\n * since the file can be re-read. This replaces the full content with a\n * compressed summary preserving the tool name and a short preview.\n *\n * Returns the compressed text, or `null` if the text doesn't match\n * the expected compacted format.\n */\nexport function compressCompactedReadResult(text: string): string | null {\n const { mainContentStart, tags } = extractLeadingSystemReminderTags(text)\n\n // Must be exactly one system-reminder tag covering the entire text\n if (tags.length !== 1) return null\n // Allow trailing whitespace/newlines after the tag\n if (mainContentStart < text.length && text.slice(mainContentStart).trim() !== \"\") return null\n\n const content = tags[0].content\n if (!content.startsWith(COMPACTED_RESULT_PREFIX)) return null\n\n // Extract tool name: \"Result of calling the Read tool: \"...\"\n const colonPos = content.indexOf(\": \", COMPACTED_RESULT_PREFIX.length)\n if (colonPos === -1) return null\n\n const toolName = content.slice(COMPACTED_RESULT_PREFIX.length, colonPos).replace(/ tool$/, \"\")\n\n // Extract the quoted content after \": \"\n const afterColon = content.slice(colonPos + 2)\n if (!afterColon.startsWith('\"')) return null\n\n // Get the inner content (between quotes) — may use \\\" escapes\n const innerContent = afterColon.slice(1, afterColon.endsWith('\"') ? -1 : undefined)\n\n // Build a short preview from the first meaningful line\n const firstLines = innerContent.split(String.raw`\\n`).slice(0, 3)\n const preview = firstLines.join(\" | \").slice(0, 150)\n\n return (\n `${OPEN_TAG}\\n`\n + `[Compressed] ${toolName} tool result (${innerContent.length.toLocaleString()} chars). `\n + `Preview: ${preview}\\n`\n + CLOSE_TAG\n )\n}\n","/**\n * Error persistence consumer.\n *\n * Subscribes to \"failed\" events on RequestContext and writes structured\n * error files to disk for post-mortem debugging. All data comes from\n * RequestContext (via HistoryEntryData on the event), not from Hono\n * Context — ensuring reliability regardless of whether the HTTP body\n * has been consumed.\n *\n * Output directory: PATHS.ERROR_DIR/{timestamp}_{id}/\n * Files:\n * - meta.json: structured metadata (timestamp, endpoint, model, error, attempts)\n * - request.json: full request payload (messages capped at 50 for size)\n * - effective-request.json: logical request after sanitize/truncate/retry\n * - wire-request.json: final outbound HTTP payload + headers sent upstream\n * - response.txt: raw upstream response body (if available)\n * - sse-events.json: recorded SSE events (if streaming request failed mid-stream)\n */\n\nimport consola from \"consola\"\nimport { randomBytes } from \"node:crypto\"\nimport * as fs from \"node:fs/promises\"\nimport * as path from \"node:path\"\n\nimport { PATHS } from \"~/lib/config/paths\"\n\nimport type { RequestContextEvent } from \"./manager\"\nimport type { HistoryEntryData } from \"./request\"\n\n// ============================================================================\n// Consumer entry point\n// ============================================================================\n\n/** Handle context events — only acts on \"failed\" */\nexport function handleErrorPersistence(event: RequestContextEvent): void {\n if (event.type !== \"failed\") return\n\n writeErrorEntry(event.entry).catch((err: unknown) => {\n consola.debug(`[ErrorPersistence] Failed to write error file: ${String(err)}`)\n })\n}\n\n// ============================================================================\n// File writing\n// ============================================================================\n\n/** Max number of messages to include in request.json (to avoid huge files) */\nconst MAX_MESSAGES_IN_DUMP = 50\n\nasync function writeErrorEntry(entry: HistoryEntryData): Promise<void> {\n // Build compact meta (focused on debugging, not full replay)\n const meta = {\n timestamp: new Date(entry.timestamp).toISOString(),\n id: entry.id,\n endpoint: entry.endpoint,\n durationMs: entry.durationMs,\n request: {\n model: entry.request.model,\n stream: entry.request.stream,\n messageCount: entry.request.messages?.length,\n toolCount: entry.request.tools?.length,\n },\n effective: entry.effectiveRequest\n ? { model: entry.effectiveRequest.model, messageCount: entry.effectiveRequest.messageCount }\n : undefined,\n wire: entry.wireRequest\n ? { model: entry.wireRequest.model, messageCount: entry.wireRequest.messageCount }\n : undefined,\n response:\n entry.response ?\n {\n success: entry.response.success,\n model: entry.response.model,\n error: entry.response.error,\n status: entry.response.status,\n }\n : undefined,\n truncation: entry.truncation,\n attempts: entry.attempts,\n }\n\n // Collect file entries: [filename, content] pairs\n const files: Array<[string, string]> = [[\"meta.json\", JSON.stringify(meta, null, 2)]]\n\n // Full request payload (from RequestContext — always available)\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive: request may not be set in all failure paths\n if (entry.request) {\n const { messages, ...requestWithoutMessages } = entry.request\n const requestData = {\n ...requestWithoutMessages,\n messageCount: messages?.length,\n // Include messages only for small payloads to avoid huge files\n ...(messages && messages.length <= MAX_MESSAGES_IN_DUMP && { messages }),\n }\n files.push([\"request.json\", JSON.stringify(requestData, null, 2)])\n }\n\n // Raw upstream response body (from HTTPError.responseText, preserved in ResponseData)\n if (entry.response?.responseText) {\n files.push([\"response.txt\", entry.response.responseText])\n }\n\n // SSE events (useful for diagnosing mid-stream failures)\n if (entry.sseEvents?.length) {\n files.push([\"sse-events.json\", JSON.stringify(entry.sseEvents, null, 2)])\n }\n\n // Effective request: logical payload after sanitize/truncate/retry, before\n // client-specific final wire mutations (beta headers, context_management injection, etc.).\n if (entry.effectiveRequest) {\n files.push([\n \"effective-request.json\",\n JSON.stringify(entry.effectiveRequest.payload ?? entry.effectiveRequest, null, 2),\n ])\n }\n\n // Wire request: final outbound HTTP payload and headers. This is the\n // authoritative source for post-mortems when the client mutates the payload\n // after the pipeline has already recorded effectiveRequest.\n if (entry.wireRequest) {\n files.push([\"wire-request.json\", JSON.stringify(entry.wireRequest, null, 2)])\n }\n\n // Create directory and write all files (only after all data is collected)\n const id = randomBytes(4).toString(\"hex\")\n const dirPath = path.join(PATHS.ERROR_DIR, `${formatTimestamp()}_${id}`)\n\n await fs.mkdir(dirPath, { recursive: true })\n await Promise.all(files.map(([name, content]) => fs.writeFile(path.join(dirPath, name), content)))\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** Format timestamp as YYMMDD_HHmmss for error directory names */\nfunction formatTimestamp(): string {\n const now = new Date()\n const pad = (n: number) => String(n).padStart(2, \"0\")\n const YY = String(now.getFullYear()).slice(2)\n return `${YY}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`\n}\n","/**\n * Context event consumers — bridge between RequestContext events and subsystems.\n *\n * Three consumers subscribe to RequestContextManager \"change\" events:\n * 1. History consumer → inserts/updates HistoryEntry in the store\n * 2. TUI consumer → updates tuiLogger for terminal display\n * 3. (WebSocket is handled implicitly via store's notifyEntryAdded/Updated)\n */\n\nimport type { HistoryEntry, MessageContent } from \"~/lib/history\"\n\nimport { getCurrentSession, insertEntry, isHistoryEnabled, updateEntry } from \"~/lib/history/store\"\nimport { tuiLogger } from \"~/lib/tui\"\n\nimport type { RequestContextEvent, RequestContextManager } from \"./manager\"\nimport type { HistoryEntryData, ResponseData } from \"./request\"\n\n// ─── History Consumer ───\n\nfunction handleHistoryEvent(event: RequestContextEvent): void {\n if (!isHistoryEnabled()) return\n\n switch (event.type) {\n case \"created\": {\n // Don't insert yet — wait for originalRequest to be available\n // (setOriginalRequest fires \"updated\" event immediately after create)\n break\n }\n\n case \"updated\": {\n // Insert entry on first originalRequest (delayed from \"created\")\n if (event.field === \"originalRequest\") {\n const orig = event.context.originalRequest\n if (!orig) break\n const ctx = event.context\n const sessionId = getCurrentSession(ctx.endpoint)\n\n const entry: HistoryEntry = {\n id: ctx.id,\n sessionId,\n timestamp: ctx.startTime,\n endpoint: ctx.endpoint,\n request: {\n model: orig.model,\n messages: orig.messages as Array<MessageContent> | undefined,\n stream: orig.stream,\n tools: orig.tools as HistoryEntry[\"request\"][\"tools\"],\n system: orig.system as HistoryEntry[\"request\"][\"system\"],\n },\n }\n\n insertEntry(entry)\n }\n if (event.field === \"pipelineInfo\" && event.context.pipelineInfo) {\n updateEntry(event.context.id, { pipelineInfo: event.context.pipelineInfo })\n }\n break\n }\n\n case \"completed\":\n case \"failed\": {\n const entryData = event.entry\n const response = toHistoryResponse(entryData)\n\n updateEntry(entryData.id, {\n response,\n durationMs: entryData.durationMs,\n sseEvents: entryData.sseEvents,\n ...(entryData.effectiveRequest && {\n effectiveRequest: {\n model: entryData.effectiveRequest.model,\n format: entryData.effectiveRequest.format,\n messageCount: entryData.effectiveRequest.messageCount,\n messages: entryData.effectiveRequest.messages as NonNullable<HistoryEntry[\"effectiveRequest\"]>[\"messages\"],\n system: entryData.effectiveRequest.system as NonNullable<HistoryEntry[\"effectiveRequest\"]>[\"system\"],\n payload: entryData.effectiveRequest.payload,\n },\n }),\n ...(entryData.wireRequest && {\n wireRequest: {\n model: entryData.wireRequest.model,\n format: entryData.wireRequest.format,\n messageCount: entryData.wireRequest.messageCount,\n messages: entryData.wireRequest.messages as NonNullable<HistoryEntry[\"wireRequest\"]>[\"messages\"],\n system: entryData.wireRequest.system as NonNullable<HistoryEntry[\"wireRequest\"]>[\"system\"],\n payload: entryData.wireRequest.payload,\n headers: entryData.wireRequest.headers ?? entryData.httpHeaders?.request,\n },\n }),\n ...(entryData.attempts && {\n attempts: entryData.attempts as HistoryEntry[\"attempts\"],\n }),\n })\n break\n }\n\n default: {\n break\n }\n }\n}\n\n// ─── TUI Consumer ───\n\nfunction handleTuiEvent(event: RequestContextEvent): void {\n switch (event.type) {\n case \"state_changed\": {\n const tuiLogId = event.context.tuiLogId\n if (!tuiLogId) return\n\n const newState = event.context.state\n if (newState === \"streaming\") {\n tuiLogger.updateRequest(tuiLogId, { status: \"streaming\" })\n } else if (newState === \"executing\") {\n tuiLogger.updateRequest(tuiLogId, { status: \"executing\" })\n }\n break\n }\n\n case \"updated\": {\n const tuiLogId = event.context.tuiLogId\n if (!tuiLogId) return\n\n // When attempts are updated, add retry tags\n if (event.field === \"attempts\" && event.context.attempts.length > 1) {\n const attempt = event.context.currentAttempt\n if (attempt?.strategy) {\n tuiLogger.updateRequest(tuiLogId, { tags: [attempt.strategy] })\n }\n }\n break\n }\n\n case \"completed\": {\n const ctx = event.context\n const tuiLogId = ctx.tuiLogId\n if (!tuiLogId) return\n\n const response = ctx.response\n if (response) {\n tuiLogger.updateRequest(tuiLogId, {\n inputTokens: response.usage.input_tokens,\n outputTokens: response.usage.output_tokens,\n cacheReadInputTokens: response.usage.cache_read_input_tokens ?? undefined,\n cacheCreationInputTokens: response.usage.cache_creation_input_tokens ?? undefined,\n queueWaitMs: ctx.queueWaitMs || undefined,\n })\n // \"completed\" event implies upstream returned 200\n tuiLogger.finishRequest(tuiLogId, { statusCode: 200 })\n }\n break\n }\n\n case \"failed\": {\n const ctx = event.context\n const tuiLogId = ctx.tuiLogId\n if (!tuiLogId) return\n\n tuiLogger.finishRequest(tuiLogId, {\n error: ctx.response?.error ?? \"Unknown error\",\n // HTTP status from the last attempt's classified error (if available)\n statusCode: ctx.currentAttempt?.error?.status || undefined,\n })\n break\n }\n\n default: {\n break\n }\n }\n}\n\n// ─── Helpers ───\n\nfunction toHistoryResponse(entryData: HistoryEntryData): HistoryEntry[\"response\"] | undefined {\n if (!entryData.response) return undefined\n\n const r: ResponseData = entryData.response\n return {\n success: r.success,\n model: r.model,\n usage: {\n input_tokens: r.usage.input_tokens,\n output_tokens: r.usage.output_tokens,\n cache_read_input_tokens: r.usage.cache_read_input_tokens,\n cache_creation_input_tokens: r.usage.cache_creation_input_tokens,\n output_tokens_details: r.usage.output_tokens_details,\n },\n stop_reason: r.stop_reason,\n error: r.error,\n status: r.status,\n content: r.content as MessageContent | null,\n rawBody: r.responseText,\n headers: entryData.httpHeaders?.response,\n }\n}\n\n// ─── Registration ───\n\nimport { handleErrorPersistence } from \"./error-persistence\"\n\nexport function registerContextConsumers(manager: RequestContextManager): void {\n manager.on(\"change\", handleHistoryEvent)\n manager.on(\"change\", handleTuiEvent)\n manager.on(\"change\", handleErrorPersistence)\n}\n","import type { Model } from \"./client\"\n\n// ============================================================================\n// Copilot API endpoint identifiers\n// ============================================================================\n\nexport const ENDPOINT = {\n MESSAGES: \"/v1/messages\",\n CHAT_COMPLETIONS: \"/chat/completions\",\n RESPONSES: \"/responses\",\n /** WebSocket transport for Responses API (client↔proxy only; proxy↔upstream is always HTTP) */\n WS_RESPONSES: \"ws:/responses\",\n EMBEDDINGS: \"/v1/embeddings\",\n} as const\n\n/** Capability type → default endpoints for legacy models without `supported_endpoints` */\nconst LEGACY_ENDPOINTS: Record<string, Array<string>> = {\n chat: [ENDPOINT.CHAT_COMPLETIONS],\n completion: [ENDPOINT.CHAT_COMPLETIONS],\n embeddings: [ENDPOINT.EMBEDDINGS],\n}\n\n/**\n * Get the effective endpoint list for a model.\n *\n * Returns `supported_endpoints` when present, otherwise infers from\n * `capabilities.type` for legacy models that predate the field.\n */\nexport function getEffectiveEndpoints(model: Model): Array<string> | undefined {\n if (model.supported_endpoints) return model.supported_endpoints\n const type = model.capabilities?.type\n if (type) return LEGACY_ENDPOINTS[type]\n return undefined\n}\n\n// ============================================================================\n// Endpoint support checks\n// ============================================================================\n\n/**\n * Check if a model supports a given API endpoint.\n *\n * When `supported_endpoints` is absent (legacy models like gpt-4, gemini),\n * we assume all endpoints are supported — these models predate the field\n * and rely on /chat/completions as a universal fallback.\n */\nexport function isEndpointSupported(model: Model | undefined, endpoint: string): boolean {\n if (!model?.supported_endpoints) return true\n return model.supported_endpoints.includes(endpoint)\n}\n\n/**\n * Check if a model supports the Responses API via either transport:\n * HTTP (`/responses`) or WebSocket (`ws:/responses`).\n */\nexport function isResponsesSupported(model: Model | undefined): boolean {\n return isEndpointSupported(model, ENDPOINT.RESPONSES) || isEndpointSupported(model, ENDPOINT.WS_RESPONSES)\n}\n\n/**\n * Assert that a model supports a given endpoint, throwing a descriptive error if not.\n * Returns the validated model for chaining.\n */\nexport function assertEndpointSupported(model: Model | undefined, endpoint: string): void {\n if (isEndpointSupported(model, endpoint)) return\n\n const modelId = model?.id ?? \"unknown\"\n const supported = model?.supported_endpoints?.join(\", \") ?? \"none\"\n const msg = `Model \"${modelId}\" does not support ${endpoint}. Supported endpoints: ${supported}`\n throw new Error(msg)\n}\n","/**\n * Conversion utilities for Responses API data structures.\n *\n * Converts between OpenAI Responses API input/output formats and the\n * unified MessageContent format used for history storage and display.\n */\n\nimport type { MessageContent } from \"~/lib/history\"\nimport type { ResponsesInputItem, ResponsesOutputItem } from \"~/types/api/openai-responses\"\n\n// ============================================================================\n// Input conversion\n// ============================================================================\n\n/**\n * Convert Responses API input items to MessageContent format for history storage.\n * Maps Responses-specific item types to the unified MessageContent structure\n * that the history UI understands.\n */\nexport function responsesInputToMessages(input: string | Array<ResponsesInputItem>): Array<MessageContent> {\n if (typeof input === \"string\") {\n return [{ role: \"user\", content: input }]\n }\n\n const messages: Array<MessageContent> = []\n for (const item of input) {\n switch (item.type) {\n case \"message\":\n case undefined: {\n // Regular message — convert content parts to Anthropic-style blocks\n const role = item.role ?? \"user\"\n let content: string | Array<unknown> | null\n\n if (typeof item.content === \"string\") {\n content = item.content\n } else if (Array.isArray(item.content)) {\n content = item.content.map((part) => {\n switch (part.type) {\n case \"input_text\": {\n return { type: \"text\", text: part.text }\n }\n case \"output_text\": {\n return { type: \"text\", text: part.text }\n }\n case \"input_image\": {\n return { type: \"image\", source: { type: \"url\", url: part.image_url } }\n }\n case \"input_file\": {\n return { type: \"file\", file_id: part.file_id, filename: part.filename }\n }\n default: {\n return part\n }\n }\n })\n } else {\n content = null\n }\n\n messages.push({ role, content })\n break\n }\n\n case \"function_call\": {\n // Function call from assistant — convert to OpenAI tool_calls format\n messages.push({\n role: \"assistant\",\n content: null,\n tool_calls: [\n {\n id: item.call_id ?? item.id ?? \"\",\n type: \"function\",\n function: { name: item.name ?? \"\", arguments: item.arguments ?? \"\" },\n },\n ],\n })\n break\n }\n\n case \"function_call_output\": {\n // Function output — convert to OpenAI tool response format\n messages.push({\n role: \"tool\",\n content: item.output ?? \"\",\n tool_call_id: item.call_id ?? \"\",\n })\n break\n }\n\n case \"item_reference\": {\n // Item reference — store as informational marker\n messages.push({\n role: \"system\",\n content: `[item_reference: ${item.id ?? \"unknown\"}]`,\n })\n break\n }\n\n case \"reasoning\": {\n // Reasoning item — store as assistant marker for history display\n messages.push({\n role: \"assistant\",\n content: `[reasoning: ${item.id ?? \"unknown\"}]`,\n })\n break\n }\n\n default: {\n // Unknown/custom item types (e.g. compaction) — store as system marker\n if (item.type && item.id) {\n messages.push({\n role: \"system\",\n content: `[${item.type}: ${item.id}]`,\n })\n }\n break\n }\n }\n }\n\n return messages\n}\n\n// ============================================================================\n// Output conversion\n// ============================================================================\n\n/**\n * Convert Responses API output items to a unified MessageContent for history storage.\n * Extracts text content and function calls from the output array.\n */\nexport function responsesOutputToContent(output: Array<ResponsesOutputItem>): MessageContent | null {\n const textParts: Array<string> = []\n const toolCalls: Array<{ id: string; type: string; function: { name: string; arguments: string } }> = []\n\n for (const item of output) {\n if (item.type === \"message\") {\n for (const part of item.content) {\n if (part.type === \"output_text\") textParts.push(part.text)\n if (part.type === \"refusal\") textParts.push(`[Refusal: ${part.refusal}]`)\n }\n }\n if (item.type === \"function_call\") {\n toolCalls.push({\n id: item.call_id,\n type: \"function\",\n function: { name: item.name, arguments: item.arguments },\n })\n }\n if (item.type === \"reasoning\") {\n const summaryText = item.summary\n .map((s) => s.text)\n .filter(Boolean)\n .join(\"\\n\")\n if (summaryText) textParts.push(`[Reasoning: ${summaryText}]`)\n }\n }\n\n if (textParts.length === 0 && toolCalls.length === 0) return null\n\n return {\n role: \"assistant\",\n content: textParts.join(\"\") || null,\n ...(toolCalls.length > 0 && { tool_calls: toolCalls }),\n }\n}\n","/**\n * Stream accumulator for Responses API format.\n * Accumulates semantic SSE events into a final state for history/tracking.\n */\n\nimport type { BaseStreamAccumulator } from \"~/lib/anthropic/stream-accumulator\"\nimport type { ResponsesStreamEvent } from \"~/types/api/openai-responses\"\n\n/** Internal tool call accumulator using string array to avoid O(n²) concatenation */\ninterface ToolCallAccumulator {\n id: string\n callId: string\n name: string\n argumentParts: Array<string>\n}\n\n/** Stream accumulator for Responses API format */\nexport interface ResponsesStreamAccumulator extends BaseStreamAccumulator {\n status: string\n responseId: string\n toolCalls: Array<{ id: string; callId: string; name: string; arguments: string }>\n /** Tool call accumulators indexed by output_index */\n toolCallMap: Map<number, ToolCallAccumulator>\n /** Text content parts for O(1) accumulation, joined on read via finalContent() */\n contentParts: Array<string>\n /** Reasoning output tokens (from output_tokens_details) */\n reasoningTokens: number\n /** Cached input tokens (from input_tokens_details) */\n cachedInputTokens: number\n}\n\nexport function createResponsesStreamAccumulator(): ResponsesStreamAccumulator {\n return {\n model: \"\",\n inputTokens: 0,\n outputTokens: 0,\n rawContent: \"\",\n status: \"\",\n responseId: \"\",\n toolCalls: [],\n toolCallMap: new Map(),\n contentParts: [],\n reasoningTokens: 0,\n cachedInputTokens: 0,\n }\n}\n\n/** Get the final accumulated content string */\nexport function finalizeResponsesContent(acc: ResponsesStreamAccumulator): string {\n if (acc.contentParts.length > 0) {\n acc.rawContent = acc.contentParts.join(\"\")\n acc.contentParts = []\n }\n return acc.rawContent\n}\n\n/** Accumulate a single parsed Responses API event into the accumulator */\nexport function accumulateResponsesStreamEvent(event: ResponsesStreamEvent, acc: ResponsesStreamAccumulator) {\n switch (event.type) {\n case \"response.created\":\n case \"response.in_progress\": {\n if (event.response.model) acc.model = event.response.model\n if (event.response.id) acc.responseId = event.response.id\n break\n }\n\n case \"response.completed\": {\n acc.status = event.response.status\n if (event.response.model) acc.model = event.response.model\n if (event.response.usage) {\n acc.inputTokens = event.response.usage.input_tokens\n acc.outputTokens = event.response.usage.output_tokens\n acc.reasoningTokens = event.response.usage.output_tokens_details?.reasoning_tokens ?? 0\n acc.cachedInputTokens = event.response.usage.input_tokens_details?.cached_tokens ?? 0\n }\n break\n }\n\n case \"response.failed\":\n case \"response.incomplete\": {\n acc.status = event.response.status\n break\n }\n\n case \"response.output_item.added\": {\n if (event.item.type === \"function_call\") {\n acc.toolCallMap.set(event.output_index, {\n id: event.item.id,\n callId: \"call_id\" in event.item ? event.item.call_id : \"\",\n name: \"name\" in event.item ? event.item.name : \"\",\n argumentParts: [],\n })\n }\n break\n }\n\n case \"response.output_text.delta\": {\n acc.contentParts.push(event.delta)\n break\n }\n\n case \"response.function_call_arguments.delta\": {\n const tcAcc = acc.toolCallMap.get(event.output_index)\n if (tcAcc) {\n tcAcc.argumentParts.push(event.delta)\n }\n break\n }\n\n case \"response.function_call_arguments.done\": {\n const tcAcc = acc.toolCallMap.get(event.output_index)\n if (tcAcc) {\n acc.toolCalls.push({\n id: tcAcc.id,\n callId: tcAcc.callId,\n name: tcAcc.name,\n arguments: tcAcc.argumentParts.join(\"\"),\n })\n }\n break\n }\n\n case \"response.output_item.done\": {\n // Final output item — if it's a function call that wasn't already finalized\n // via arguments.done, finalize it now\n if (event.item.type === \"function_call\") {\n const existing = acc.toolCalls.find((tc) => tc.id === event.item.id)\n if (!existing) {\n acc.toolCalls.push({\n id: event.item.id,\n callId: \"call_id\" in event.item ? event.item.call_id : \"\",\n name: \"name\" in event.item ? event.item.name : \"\",\n arguments: \"arguments\" in event.item ? event.item.arguments : \"\",\n })\n }\n }\n break\n }\n\n // Other events don't need accumulation\n default: {\n break\n }\n }\n}\n","/**\n * Request execution pipeline with pluggable retry strategies.\n *\n * Unifies the retry loop pattern shared by all API handlers:\n * messages, chat-completions, and responses.\n */\n\nimport consola from \"consola\"\n\nimport type { RequestContext } from \"~/lib/context/request\"\nimport type { ApiError } from \"~/lib/error\"\nimport type { EndpointType, SanitizationInfo } from \"~/lib/history/store\"\nimport type { Model } from \"~/lib/models/client\"\n\nimport { classifyError } from \"~/lib/error\"\n\n// --- FormatAdapter ---\n\nexport interface SanitizeResult<TPayload> {\n payload: TPayload\n /** Convenience: total blocks removed (sum of orphans + empty text) */\n blocksRemoved: number\n /** Convenience: number of system reminder tags removed */\n systemReminderRemovals: number\n /** Structured breakdown of what was removed/modified — format-specific detail */\n stats?: Record<string, number>\n}\n\nexport interface FormatAdapter<TPayload> {\n readonly format: EndpointType\n sanitize(payload: TPayload): SanitizeResult<TPayload>\n /** Execute API call — raw execution without rate limiting wrapper */\n execute(payload: TPayload): Promise<{ result: unknown; queueWaitMs: number }>\n logPayloadSize(payload: TPayload): void | Promise<void>\n}\n\n// --- RetryStrategy ---\n\nexport interface RetryContext<TPayload> {\n attempt: number\n originalPayload: TPayload\n model: Model | undefined\n maxRetries: number\n}\n\nexport type RetryAction<TPayload> =\n | { action: \"retry\"; payload: TPayload; waitMs?: number; meta?: Record<string, unknown> }\n | { action: \"abort\"; error: ApiError }\n\nexport interface RetryStrategy<TPayload> {\n readonly name: string\n /** Check if this strategy can handle the given error */\n canHandle(error: ApiError): boolean\n /** Handle the error and decide whether to retry or abort */\n handle(error: ApiError, payload: TPayload, context: RetryContext<TPayload>): Promise<RetryAction<TPayload>>\n}\n\n// --- Pipeline ---\n\nexport interface PipelineResult {\n response: unknown\n effectivePayload: unknown\n queueWaitMs: number\n totalRetries: number\n}\n\nexport interface PipelineOptions<TPayload> {\n adapter: FormatAdapter<TPayload>\n strategies: Array<RetryStrategy<TPayload>>\n payload: TPayload\n originalPayload: TPayload\n model: Model | undefined\n maxRetries?: number\n /** Optional request context for lifecycle tracking */\n requestContext?: RequestContext\n /** Called before each attempt (for tracking tags, etc.) */\n onBeforeAttempt?: (attempt: number, payload: TPayload) => void\n /** Called after successful truncation retry (for recording rewrites, etc.) */\n onRetry?: (attempt: number, strategyName: string, newPayload: TPayload, meta?: Record<string, unknown>) => void\n}\n\n/**\n * Execute a request through the pipeline with retry strategies.\n *\n * Flow:\n * 1. Execute API call with the current payload\n * 2. On success → return response\n * 3. On failure → classify error → find first matching strategy → handle\n * - retry → use new payload, loop back to step 1\n * - abort or no strategy → throw error\n */\nexport async function executeRequestPipeline<TPayload>(opts: PipelineOptions<TPayload>): Promise<PipelineResult> {\n const { adapter, strategies, originalPayload, model, maxRetries = 3, requestContext, onBeforeAttempt, onRetry } = opts\n\n let effectivePayload = opts.payload\n let lastError: unknown = null\n let totalQueueWaitMs = 0\n let lastStrategyName: string | undefined\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n // 1. Create attempt first (ensures currentAttempt is available for subsequent calls)\n requestContext?.beginAttempt({\n strategy: attempt > 0 ? lastStrategyName : undefined,\n })\n lastStrategyName = undefined\n\n // 2. Auto-record effective payload on each attempt (covers all handlers)\n if (requestContext) {\n const p = effectivePayload as Record<string, unknown>\n requestContext.setAttemptEffectiveRequest({\n model: typeof p.model === \"string\" ? p.model : \"\",\n resolvedModel: model,\n messages: Array.isArray(p.messages) ? p.messages : [],\n payload: effectivePayload,\n format: adapter.format,\n })\n }\n\n // 3. External callback (currentAttempt now exists)\n onBeforeAttempt?.(attempt, effectivePayload)\n requestContext?.transition(\"executing\")\n\n try {\n const { result: response, queueWaitMs } = await adapter.execute(effectivePayload)\n totalQueueWaitMs += queueWaitMs\n requestContext?.addQueueWaitMs(queueWaitMs)\n\n return {\n response,\n effectivePayload,\n queueWaitMs: totalQueueWaitMs,\n totalRetries: attempt,\n }\n } catch (error) {\n lastError = error\n\n // Classify and record the error on the current attempt (always, including final attempt)\n const apiError = classifyError(error)\n requestContext?.setAttemptError(apiError)\n\n // Don't retry if we've exhausted attempts\n if (attempt >= maxRetries) break\n\n // Find first strategy that can handle this error\n let handled = false\n for (const strategy of strategies) {\n if (!strategy.canHandle(apiError)) continue\n\n const retryContext: RetryContext<TPayload> = {\n attempt,\n originalPayload,\n model,\n maxRetries,\n }\n\n try {\n const action = await strategy.handle(apiError, effectivePayload, retryContext)\n\n if (action.action === \"retry\") {\n consola.debug(\n `[Pipeline] Strategy \"${strategy.name}\" requests retry ` + `(attempt ${attempt + 1}/${maxRetries + 1})`,\n )\n\n if (action.waitMs && action.waitMs > 0) {\n totalQueueWaitMs += action.waitMs\n requestContext?.addQueueWaitMs(action.waitMs)\n }\n\n // Auto-record sanitization from strategy meta (e.g. auto-truncate provides this)\n if (action.meta?.sanitization && requestContext) {\n requestContext.setAttemptSanitization(action.meta.sanitization as SanitizationInfo)\n }\n\n lastStrategyName = strategy.name\n effectivePayload = action.payload\n onRetry?.(attempt, strategy.name, action.payload, action.meta)\n handled = true\n break\n }\n\n // action === \"abort\": fall through to break\n break\n } catch (strategyError) {\n consola.warn(\n `[Pipeline] Strategy \"${strategy.name}\" failed on attempt ${attempt + 1}:`,\n strategyError instanceof Error ? strategyError.message : strategyError,\n )\n // Strategy itself failed, break out to throw original error\n break\n }\n }\n\n if (!handled) break\n }\n }\n\n // If we exit the loop, it means all retries failed or no strategy handled the error\n if (lastError) {\n // Log payload size info for 413 errors\n const apiError = classifyError(lastError)\n if (apiError.type === \"payload_too_large\") {\n await adapter.logPayloadSize(effectivePayload)\n }\n\n throw lastError instanceof Error ? lastError : new Error(\"Unknown error\")\n }\n\n // Should not reach here\n throw new Error(\"Unexpected state in pipeline retry loop\")\n}\n","/**\n * Response utilities for request handlers.\n */\n\nimport type { ChatCompletionResponse } from \"~/types/api/openai-chat-completions\"\n\n/** Type guard for non-streaming responses */\nexport function isNonStreaming(\n response: ChatCompletionResponse | AsyncIterable<unknown>,\n): response is ChatCompletionResponse {\n return Object.hasOwn(response, \"choices\")\n}\n\n/** Parse a JSON string to object, returning the value as-is if already an object */\nexport function safeParseJson(input: string | Record<string, unknown>): Record<string, unknown> {\n if (typeof input !== \"string\") return input\n try {\n return JSON.parse(input) as Record<string, unknown>\n } catch {\n return {}\n }\n}\n\n/** Prepend a marker string to the first text content block of an Anthropic-format response */\nexport function prependMarkerToResponse<T extends { content: Array<{ type: string; text?: string }> }>(\n response: T,\n marker: string,\n): T {\n if (!marker) return response\n\n // Find first text block and prepend, or add new text block at start\n const content = [...response.content]\n const firstTextIndex = content.findIndex((block) => block.type === \"text\")\n\n if (firstTextIndex !== -1) {\n const textBlock = content[firstTextIndex]\n if (textBlock.type === \"text\") {\n content[firstTextIndex] = {\n ...textBlock,\n text: marker + (textBlock.text ?? \"\"),\n }\n }\n } else {\n // No text block found, add one at the beginning\n content.unshift({ type: \"text\", text: marker } as (typeof content)[number])\n }\n\n return { ...response, content }\n}\n","/** Shared recording utilities for streaming responses */\n\nimport consola from \"consola\"\n\nimport type { AnthropicStreamAccumulator } from \"~/lib/anthropic/stream-accumulator\"\nimport type { ResponseData } from \"~/lib/context/request\"\nimport type { ResponsesStreamAccumulator } from \"~/lib/openai/responses-stream-accumulator\"\nimport type { OpenAIStreamAccumulator } from \"~/lib/openai/stream-accumulator\"\n\nimport { finalizeResponsesContent } from \"~/lib/openai/responses-stream-accumulator\"\n\nimport { safeParseJson } from \"./response\"\n\n/**\n * Map Anthropic content blocks to history-friendly format.\n * Filters out empty/whitespace-only text blocks (non-thinking) before mapping.\n */\nfunction mapAnthropicContentBlocks(acc: AnthropicStreamAccumulator): Array<unknown> {\n return acc.contentBlocks\n .filter((block) => {\n // Keep all non-text blocks (thinking, redacted_thinking, tool_use, etc.)\n if (block.type !== \"text\") return true\n // Filter out empty/whitespace-only text blocks\n return \"text\" in block && (block as { text: string }).text.trim() !== \"\"\n })\n .map((block) => {\n // Generic (unknown) blocks are passed through as-is\n if (\"_generic\" in block) {\n const { _generic: _, ...rest } = block\n return rest\n }\n\n // Server tool result blocks (web_search_tool_result, tool_search_tool_result, etc.)\n // Check before the type switch because _brand blocks have `type: string` which\n // would overlap with literal type cases.\n if (\"_brand\" in block) {\n return {\n type: block.type,\n tool_use_id: block.tool_use_id,\n content: block.content,\n }\n }\n\n // After the _generic and _brand checks, only known block types remain.\n // Use a type assertion to narrow — TypeScript can't infer this from the\n // _brand / _generic guards since they aren't shared discriminant properties.\n type KnownBlock =\n | { type: \"text\"; text: string }\n | { type: \"thinking\"; thinking: string; signature?: string }\n | { type: \"redacted_thinking\"; data: string }\n | { type: \"tool_use\"; id: string; name: string; input: string }\n | { type: \"server_tool_use\"; id: string; name: string; input: string }\n const narrowed = block as KnownBlock\n\n switch (narrowed.type) {\n case \"text\": {\n return { type: \"text\" as const, text: narrowed.text }\n }\n case \"thinking\": {\n return { type: \"thinking\" as const, thinking: narrowed.thinking }\n }\n case \"redacted_thinking\": {\n return { type: \"redacted_thinking\" as const }\n }\n case \"tool_use\":\n case \"server_tool_use\": {\n return {\n type: narrowed.type as string,\n id: narrowed.id,\n name: narrowed.name,\n input: safeParseJson(narrowed.input),\n }\n }\n default: {\n const unknown = narrowed as { type: string }\n consola.warn(`[recording] Unhandled content block type in stream result: ${unknown.type}`)\n return { type: unknown.type }\n }\n }\n })\n}\n\n/**\n * Build a ResponseData from a completed Anthropic stream accumulator.\n * Does not include durationMs or queueWaitMs — those are tracked by RequestContext.\n */\nexport function buildAnthropicResponseData(acc: AnthropicStreamAccumulator, fallbackModel: string): ResponseData {\n const contentBlocks = mapAnthropicContentBlocks(acc)\n\n return {\n success: true,\n model: acc.model || fallbackModel,\n usage: {\n input_tokens: acc.inputTokens,\n output_tokens: acc.outputTokens,\n ...(acc.cacheReadTokens > 0 && { cache_read_input_tokens: acc.cacheReadTokens }),\n ...(acc.cacheCreationTokens > 0 && { cache_creation_input_tokens: acc.cacheCreationTokens }),\n },\n stop_reason: acc.stopReason || undefined,\n content: contentBlocks.length > 0 ? { role: \"assistant\", content: contentBlocks } : null,\n }\n}\n\n/**\n * Build a ResponseData from a completed OpenAI stream accumulator.\n * Does not include durationMs or queueWaitMs — those are tracked by RequestContext.\n */\nexport function buildOpenAIResponseData(acc: OpenAIStreamAccumulator, fallbackModel: string): ResponseData {\n // Collect tool calls from map, joining accumulated argument parts\n for (const tc of acc.toolCallMap.values()) {\n if (tc.id && tc.name) acc.toolCalls.push({ id: tc.id, name: tc.name, arguments: tc.argumentParts.join(\"\") })\n }\n\n const toolCalls = acc.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n }))\n\n return {\n success: true,\n model: acc.model || fallbackModel,\n usage: {\n input_tokens: acc.inputTokens,\n output_tokens: acc.outputTokens,\n ...(acc.reasoningTokens > 0 && {\n output_tokens_details: { reasoning_tokens: acc.reasoningTokens },\n }),\n ...(acc.cachedTokens > 0 && { cache_read_input_tokens: acc.cachedTokens }),\n },\n stop_reason: acc.finishReason || undefined,\n content: {\n role: \"assistant\",\n content: acc.rawContent,\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n },\n }\n}\n\n/**\n * Build a ResponseData from a completed Responses API stream accumulator.\n * Converts tool calls from Responses format (callId) to OpenAI Chat Completions format (id).\n */\nexport function buildResponsesResponseData(acc: ResponsesStreamAccumulator, fallbackModel: string): ResponseData {\n // Finalize tool calls from the accumulator map\n for (const tcAcc of acc.toolCallMap.values()) {\n const existing = acc.toolCalls.find((tc) => tc.id === tcAcc.id)\n if (!existing && tcAcc.id && tcAcc.name) {\n acc.toolCalls.push({\n id: tcAcc.id,\n callId: tcAcc.callId,\n name: tcAcc.name,\n arguments: tcAcc.argumentParts.join(\"\"),\n })\n }\n }\n\n const finalContent = finalizeResponsesContent(acc)\n\n const toolCalls = acc.toolCalls.map((tc) => ({\n id: tc.callId || tc.id,\n type: \"function\" as const,\n function: { name: tc.name, arguments: tc.arguments },\n }))\n\n return {\n success: true,\n model: acc.model || fallbackModel,\n usage: {\n input_tokens: acc.inputTokens,\n output_tokens: acc.outputTokens,\n ...(acc.reasoningTokens > 0 && {\n output_tokens_details: { reasoning_tokens: acc.reasoningTokens },\n }),\n ...(acc.cachedInputTokens > 0 && { cache_read_input_tokens: acc.cachedInputTokens }),\n },\n stop_reason: acc.status || undefined,\n content:\n finalContent || toolCalls.length > 0 ?\n {\n role: \"assistant\",\n content: finalContent || null,\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n }\n : null,\n }\n}\n","/**\n * Generic stream utilities for SSE-based streaming proxying.\n *\n * These utilities are protocol-agnostic — they work with any async iterator\n * and are used by Anthropic, OpenAI Chat Completions, and Responses handlers.\n */\n\n// ============================================================================\n// Stream idle timeout\n// ============================================================================\n\n/** Error thrown when no SSE event arrives within the configured idle timeout window */\nexport class StreamIdleTimeoutError extends Error {\n constructor(timeoutMs: number) {\n super(`Stream idle timeout: no event received within ${timeoutMs / 1000}s`)\n this.name = \"StreamIdleTimeoutError\"\n }\n}\n\n// ============================================================================\n// Abort signal utilities\n// ============================================================================\n\n/** Sentinel value returned when shutdown abort signal fires during iterator.next() */\nexport const STREAM_ABORTED = Symbol(\"STREAM_ABORTED\")\n\n/**\n * Combine multiple abort signals into one.\n * Returns undefined if no valid signals provided. Returns the single signal\n * if only one is valid. Otherwise uses AbortSignal.any() to merge.\n */\nexport function combineAbortSignals(...signals: Array<AbortSignal | undefined>): AbortSignal | undefined {\n const valid = signals.filter((s): s is AbortSignal => s !== undefined)\n if (valid.length === 0) return undefined\n if (valid.length === 1) return valid[0]\n return AbortSignal.any(valid)\n}\n\n// ============================================================================\n// Iterator racing\n// ============================================================================\n\n/**\n * Race `iterator.next()` against idle timeout and/or shutdown abort signal.\n *\n * Without this, `await iterator.next()` blocks indefinitely when the upstream\n * connection is alive but sends no data — the shutdown signal check at the top\n * of the loop never gets reached. This function ensures the abort signal can\n * interrupt the wait.\n *\n * Returns `STREAM_ABORTED` when the abort signal fires (caller should break).\n * Rejects with `StreamIdleTimeoutError` if idle timeout fires first.\n */\nexport function raceIteratorNext<T>(\n promise: Promise<IteratorResult<T>>,\n opts: { idleTimeoutMs: number; abortSignal?: AbortSignal },\n): Promise<IteratorResult<T> | typeof STREAM_ABORTED> {\n const { idleTimeoutMs, abortSignal } = opts\n\n // Fast path: already aborted\n if (abortSignal?.aborted) return Promise.resolve(STREAM_ABORTED)\n\n // Build the set of racing promises\n const racers: Array<Promise<IteratorResult<T> | typeof STREAM_ABORTED>> = [promise]\n const cleanups: Array<() => void> = []\n\n // Idle timeout racer\n if (idleTimeoutMs > 0) {\n let timeoutId: ReturnType<typeof setTimeout>\n racers.push(\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => reject(new StreamIdleTimeoutError(idleTimeoutMs)), idleTimeoutMs)\n }),\n )\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n cleanups.push(() => clearTimeout(timeoutId!))\n }\n\n // Abort signal racer — resolves (not rejects) with sentinel so the caller\n // can distinguish shutdown from errors and complete gracefully\n if (abortSignal && !abortSignal.aborted) {\n let onAbort: () => void\n racers.push(\n new Promise<typeof STREAM_ABORTED>((resolve) => {\n onAbort = () => resolve(STREAM_ABORTED)\n abortSignal.addEventListener(\"abort\", onAbort, { once: true })\n }),\n )\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n cleanups.push(() => abortSignal.removeEventListener(\"abort\", onAbort!))\n }\n\n return Promise.race(racers).finally(() => {\n for (const cleanup of cleanups) cleanup()\n })\n}\n","import type { Model } from \"~/lib/models/client\"\nimport type { ChatCompletionsPayload } from \"~/types/api/openai-chat-completions\"\nimport type { ResponsesInputItem, ResponsesPayload } from \"~/types/api/openai-responses\"\n\nimport { copilotHeaders } from \"~/lib/copilot-api\"\nimport { state } from \"~/lib/state\"\n\nexport interface PreparedOpenAIRequest<TPayload> {\n wire: TPayload\n headers: Record<string, string>\n}\n\ninterface PrepareOpenAIRequestOptions {\n resolvedModel?: Model\n}\n\nexport function prepareChatCompletionsRequest(\n payload: ChatCompletionsPayload,\n opts?: PrepareOpenAIRequestOptions,\n): PreparedOpenAIRequest<ChatCompletionsPayload> {\n const wire = payload\n\n const enableVision = wire.messages.some(\n (message) => typeof message.content !== \"string\" && message.content?.some((part) => part.type === \"image_url\"),\n )\n\n const isAgentCall = wire.messages.some((message) => [\"assistant\", \"tool\"].includes(message.role))\n const modelSupportsVision = opts?.resolvedModel?.capabilities?.supports?.vision !== false\n\n const headers: Record<string, string> = {\n ...copilotHeaders(state, {\n vision: enableVision && modelSupportsVision,\n modelRequestHeaders: opts?.resolvedModel?.request_headers,\n intent: isAgentCall ? \"conversation-agent\" : \"conversation-panel\",\n }),\n \"X-Initiator\": isAgentCall ? \"agent\" : \"user\",\n }\n\n return { wire, headers }\n}\n\nexport function prepareResponsesRequest(\n payload: ResponsesPayload,\n opts?: PrepareOpenAIRequestOptions,\n): PreparedOpenAIRequest<ResponsesPayload> {\n const wire = payload\n const enableVision = hasVisionContent(wire.input)\n const isAgentCall =\n Array.isArray(wire.input)\n && wire.input.some(\n (item) => item.role === \"assistant\" || item.type === \"function_call\" || item.type === \"function_call_output\",\n )\n const modelSupportsVision = opts?.resolvedModel?.capabilities?.supports?.vision !== false\n\n const headers: Record<string, string> = {\n ...copilotHeaders(state, {\n vision: enableVision && modelSupportsVision,\n modelRequestHeaders: opts?.resolvedModel?.request_headers,\n intent: isAgentCall ? \"conversation-agent\" : \"conversation-panel\",\n }),\n \"X-Initiator\": isAgentCall ? \"agent\" : \"user\",\n }\n\n return { wire, headers }\n}\n\nfunction hasVisionContent(input: string | Array<ResponsesInputItem>): boolean {\n if (typeof input === \"string\") return false\n return input.some(\n (item) => Array.isArray(item.content) && item.content.some((part) => \"type\" in part && part.type === \"input_image\"),\n )\n}\n","/**\n * Responses API client for Copilot /responses endpoint.\n * Follows the same pattern as chat-completions-client.ts but targets the /responses endpoint.\n */\n\nimport type { ServerSentEventMessage } from \"fetch-event-stream\"\n\nimport consola from \"consola\"\nimport { events } from \"fetch-event-stream\"\n\nimport type { HeadersCapture } from \"~/lib/context/request\"\nimport type { Model } from \"~/lib/models/client\"\nimport type { ResponsesPayload, ResponsesResponse } from \"~/types/api/openai-responses\"\n\nimport { copilotBaseUrl } from \"~/lib/copilot-api\"\nimport { HTTPError } from \"~/lib/error\"\nimport { createFetchSignal, captureHttpHeaders, sanitizeHeadersForHistory } from \"~/lib/fetch-utils\"\nimport { state } from \"~/lib/state\"\nimport { prepareResponsesRequest, type PreparedOpenAIRequest } from \"./request-preparation\"\n\ninterface CreateResponsesOptions {\n resolvedModel?: Model\n headersCapture?: HeadersCapture\n onPrepared?: (request: PreparedOpenAIRequest<ResponsesPayload>) => void\n}\n\nexport { prepareResponsesRequest, type PreparedOpenAIRequest } from \"./request-preparation\"\n\n/** Call Copilot /responses endpoint */\nexport const createResponses = async (\n payload: ResponsesPayload,\n opts?: CreateResponsesOptions,\n): Promise<ResponsesResponse | AsyncGenerator<ServerSentEventMessage>> => {\n if (!state.copilotToken) throw new Error(\"Copilot token not found\")\n\n const prepared = prepareResponsesRequest(payload, opts)\n opts?.onPrepared?.({\n wire: prepared.wire,\n headers: sanitizeHeadersForHistory(prepared.headers),\n })\n const { wire, headers } = prepared\n\n // Apply fetch timeout if configured (connection + response headers)\n const fetchSignal = createFetchSignal()\n\n const response = await fetch(`${copilotBaseUrl(state)}/responses`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(wire),\n signal: fetchSignal,\n })\n\n // Capture HTTP headers for history (before error check — capture even on failure)\n if (opts?.headersCapture) {\n captureHttpHeaders(opts.headersCapture, headers, response)\n }\n\n if (!response.ok) {\n consola.error(\"Failed to create responses\", response)\n throw await HTTPError.fromResponse(\"Failed to create responses\", response, wire.model)\n }\n\n if (wire.stream) {\n return events(response)\n }\n\n return (await response.json()) as ResponsesResponse\n}\n","/**\n * Network error retry strategy.\n *\n * Handles transient network errors (ECONNRESET, ETIMEDOUT, socket closures, etc.)\n * by retrying once after a brief delay. These errors are typically caused by\n * connection pool issues, transient network glitches, or upstream resets,\n * and a single retry usually succeeds.\n */\n\nimport consola from \"consola\"\n\nimport type { ApiError } from \"~/lib/error\"\n\nimport type { RetryAction, RetryContext, RetryStrategy } from \"../pipeline\"\n\n/** Default delay before network retry (ms) */\nconst NETWORK_RETRY_DELAY_MS = 1000\n\n/**\n * Create a network error retry strategy.\n *\n * On `network_error` (ECONNRESET, ETIMEDOUT, socket closures, DNS timeouts, etc.),\n * waits briefly then retries with the same payload.\n * Only retries once per pipeline execution to avoid prolonged retry loops\n * on persistent network failures.\n */\nexport function createNetworkRetryStrategy<TPayload>(): RetryStrategy<TPayload> {\n // Track whether we've already attempted a network retry.\n // A second network error after retry means the problem is persistent.\n let hasRetried = false\n\n return {\n name: \"network-retry\",\n\n canHandle(error: ApiError): boolean {\n return error.type === \"network_error\" && !hasRetried\n },\n\n handle(error: ApiError, currentPayload: TPayload, context: RetryContext<TPayload>): Promise<RetryAction<TPayload>> {\n consola.info(\n `[NetworkRetry] Attempt ${context.attempt + 1}/${context.maxRetries + 1}: `\n + `Network error \"${error.message}\", retrying in ${NETWORK_RETRY_DELAY_MS}ms...`,\n )\n\n hasRetried = true\n\n return Promise.resolve({\n action: \"retry\",\n payload: currentPayload,\n waitMs: NETWORK_RETRY_DELAY_MS,\n meta: { networkRetry: true },\n })\n },\n }\n}\n","/**\n * Token refresh retry strategy.\n *\n * Handles 401/403 errors by refreshing the Copilot token and retrying.\n * When the Copilot token expires between scheduled refreshes, this strategy\n * triggers an immediate refresh so the request can be retried transparently.\n */\n\nimport consola from \"consola\"\n\nimport type { ApiError } from \"~/lib/error\"\n\nimport { getCopilotTokenManager } from \"~/lib/token\"\n\nimport type { RetryAction, RetryContext, RetryStrategy } from \"../pipeline\"\n\n/**\n * Refresh the Copilot token via the global manager.\n * Returns true on success, false on failure.\n */\nasync function refreshCopilotToken(): Promise<boolean> {\n const manager = getCopilotTokenManager()\n if (!manager) return false\n const result = await manager.refresh()\n return result !== null\n}\n\n/**\n * Create a token refresh retry strategy.\n *\n * On `auth_expired` errors (401/403), refreshes the Copilot token via\n * `CopilotTokenManager.refresh()`, then retries with the same payload.\n * Only retries once per pipeline execution to avoid infinite refresh loops.\n */\nexport function createTokenRefreshStrategy<TPayload>(): RetryStrategy<TPayload> {\n // Track whether we've already attempted a refresh in this pipeline execution.\n // A second 401 after refresh means the problem isn't a stale token.\n let hasRefreshed = false\n\n return {\n name: \"token-refresh\",\n\n canHandle(error: ApiError): boolean {\n return error.type === \"auth_expired\" && !hasRefreshed\n },\n\n async handle(\n error: ApiError,\n currentPayload: TPayload,\n context: RetryContext<TPayload>,\n ): Promise<RetryAction<TPayload>> {\n consola.info(\n `[TokenRefresh] Attempt ${context.attempt + 1}/${context.maxRetries + 1}: `\n + `Got ${error.status}, refreshing Copilot token...`,\n )\n\n const success = await refreshCopilotToken()\n hasRefreshed = true\n\n if (!success) {\n consola.error(\"[TokenRefresh] Token refresh failed, aborting request\")\n return { action: \"abort\", error }\n }\n\n consola.info(\"[TokenRefresh] Token refreshed, retrying request\")\n\n // Retry with the same payload — the new token is in global state,\n // which the adapter reads when constructing the Authorization header\n return {\n action: \"retry\",\n payload: currentPayload,\n meta: { tokenRefreshed: true },\n }\n },\n }\n}\n","/**\n * Shared pipeline configuration for the Responses API.\n *\n * Both the HTTP handler (handler.ts) and WebSocket handler (ws.ts)\n * use identical adapter and strategy configuration. This module\n * centralizes that configuration to avoid duplication.\n */\n\nimport consola from \"consola\"\n\nimport type { HeadersCapture, WireRequest } from \"~/lib/context/request\"\nimport type { Model } from \"~/lib/models/client\"\nimport type { FormatAdapter } from \"~/lib/request/pipeline\"\nimport type { ResponsesInputItem, ResponsesPayload } from \"~/types/api/openai-responses\"\n\nimport { executeWithAdaptiveRateLimit } from \"~/lib/adaptive-rate-limiter\"\nimport { createResponses } from \"~/lib/openai/responses-client\"\nimport { createNetworkRetryStrategy } from \"~/lib/request/strategies/network-retry\"\nimport { createTokenRefreshStrategy } from \"~/lib/request/strategies/token-refresh\"\n\n/** Create the FormatAdapter for Responses API pipeline execution */\nexport function createResponsesAdapter(\n selectedModel?: Model,\n headersCapture?: HeadersCapture,\n onPrepared?: (request: WireRequest) => void,\n): FormatAdapter<ResponsesPayload> {\n return {\n format: \"openai-responses\",\n sanitize: (p) => ({ payload: p, blocksRemoved: 0, systemReminderRemovals: 0 }),\n execute: (p) =>\n executeWithAdaptiveRateLimit(() =>\n createResponses(p, {\n resolvedModel: selectedModel,\n headersCapture,\n onPrepared: ({ wire, headers }) => {\n onPrepared?.({\n model: typeof wire.model === \"string\" ? wire.model : p.model,\n messages: [],\n payload: wire,\n headers,\n format: \"openai-responses\",\n })\n },\n })),\n logPayloadSize: (p) => {\n const count = typeof p.input === \"string\" ? 1 : p.input.length\n consola.debug(`Responses payload: ${count} input item(s), model: ${p.model}`)\n },\n }\n}\n\n/** Create the retry strategies for Responses API pipeline execution */\nexport function createResponsesStrategies() {\n return [createNetworkRetryStrategy<ResponsesPayload>(), createTokenRefreshStrategy<ResponsesPayload>()]\n}\n\n// ============================================================================\n// Call ID normalization\n// ============================================================================\n\nconst CALL_PREFIX = \"call_\"\nconst FC_PREFIX = \"fc_\"\n\n/**\n * Normalize function call IDs in Responses API input.\n * Converts Chat Completions format `call_xxx` IDs to Responses format `fc_xxx` IDs\n * on `function_call` and `function_call_output` items.\n */\nexport function normalizeCallIds(payload: ResponsesPayload): ResponsesPayload {\n if (typeof payload.input === \"string\") return payload\n\n let count = 0\n const normalizedInput = payload.input.map((item): ResponsesInputItem => {\n if (item.type !== \"function_call\" && item.type !== \"function_call_output\") return item\n\n const newItem = { ...item }\n if (newItem.id?.startsWith(CALL_PREFIX)) {\n newItem.id = FC_PREFIX + newItem.id.slice(CALL_PREFIX.length)\n count++\n }\n if (newItem.call_id?.startsWith(CALL_PREFIX)) {\n newItem.call_id = FC_PREFIX + newItem.call_id.slice(CALL_PREFIX.length)\n count++\n }\n return newItem\n })\n\n if (count === 0) return payload\n consola.debug(`[responses] Normalized ${count} call ID(s) (call_ → fc_)`)\n return { ...payload, input: normalizedInput }\n}\n","/**\n * WebSocket transport for the Responses API.\n *\n * Accepts WebSocket connections on GET /v1/responses (and /responses).\n * Clients send `{ type: \"response.create\", response: { model, input, ... } }`\n * and receive streaming events as JSON frames (same data as SSE events).\n *\n * This bridges the WebSocket transport to our existing HTTP pipeline:\n * WebSocket message → extract payload → pipeline → SSE events → WS JSON frames.\n */\n\nimport type { Hono } from \"hono\"\nimport type { UpgradeWebSocket, WSContext } from \"hono/ws\"\n\nimport consola from \"consola\"\n\nimport type { HeadersCapture } from \"~/lib/context/request\"\nimport type { ResponsesPayload, ResponsesStreamEvent } from \"~/types/api/openai-responses\"\n\nimport { getRequestContextManager } from \"~/lib/context/manager\"\nimport { isResponsesSupported } from \"~/lib/models/endpoint\"\nimport { resolveModelName } from \"~/lib/models/resolver\"\nimport { responsesInputToMessages } from \"~/lib/openai/responses-conversion\"\nimport {\n accumulateResponsesStreamEvent,\n createResponsesStreamAccumulator,\n} from \"~/lib/openai/responses-stream-accumulator\"\nimport { executeRequestPipeline } from \"~/lib/request/pipeline\"\nimport { buildResponsesResponseData } from \"~/lib/request/recording\"\nimport { getShutdownSignal } from \"~/lib/shutdown\"\nimport { state } from \"~/lib/state\"\nimport { STREAM_ABORTED, raceIteratorNext } from \"~/lib/stream\"\nimport { processResponsesInstructions } from \"~/lib/system-prompt\"\nimport { tuiLogger } from \"~/lib/tui\"\n\nimport { createResponsesAdapter, createResponsesStrategies, normalizeCallIds } from \"./pipeline\"\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Terminal event types that signal the end of a response */\nconst TERMINAL_EVENTS = new Set([\"response.completed\", \"response.failed\", \"response.incomplete\", \"error\"])\n\n// ============================================================================\n// Payload extraction\n// ============================================================================\n\n/**\n * Extract ResponsesPayload from a response.create WebSocket message.\n *\n * Supports two formats:\n * - OpenAI SDK style: `{ type: \"response.create\", response: { model, input, ... } }`\n * - Flat style: `{ type: \"response.create\", model, input, ... }`\n *\n * WebSocket transport always streams, so `stream` is forced to `true`.\n */\nfunction extractPayload(message: unknown): ResponsesPayload | null {\n if (typeof message !== \"object\" || message === null) return null\n const msg = message as Record<string, unknown>\n\n if (msg.type !== \"response.create\") return null\n\n // OpenAI SDK wraps payload in \"response\" key\n let payload: Record<string, unknown>\n if (msg.response && typeof msg.response === \"object\") {\n payload = msg.response as Record<string, unknown>\n } else {\n const { type: _type, ...rest } = msg\n payload = rest\n }\n\n // WebSocket transport always streams\n payload.stream = true\n\n if (!payload.model || typeof payload.model !== \"string\") return null\n if (!payload.input) return null\n\n return payload as unknown as ResponsesPayload\n}\n\n// ============================================================================\n// Error helpers\n// ============================================================================\n\n/** Send an error frame and close the WebSocket */\nfunction sendErrorAndClose(ws: WSContext, message: string, code?: string): void {\n try {\n ws.send(\n JSON.stringify({\n type: \"error\",\n error: { type: code ?? \"server_error\", message },\n }),\n )\n } catch {\n // WebSocket might already be closed\n }\n try {\n ws.close(1011, message.slice(0, 123)) // WS close reason max 123 bytes\n } catch {\n // Already closed\n }\n}\n\n// ============================================================================\n// Core handler\n// ============================================================================\n\n/** Handle a response.create message over WebSocket */\nasync function handleResponseCreate(ws: WSContext, rawPayload: ResponsesPayload): Promise<void> {\n let payload = rawPayload\n const requestedModel = payload.model\n const resolvedModel = resolveModelName(requestedModel)\n payload.model = resolvedModel\n\n // Check endpoint support\n const selectedModel = state.modelIndex.get(resolvedModel)\n if (!isResponsesSupported(selectedModel)) {\n sendErrorAndClose(ws, `Model \"${resolvedModel}\" does not support the Responses API`, \"invalid_request_error\")\n return\n }\n\n // Process system prompt (overrides, prepend, append from config)\n payload.instructions = await processResponsesInstructions(payload.instructions, payload.model)\n\n // Normalize call IDs before pipeline (call_ → fc_)\n if (state.normalizeResponsesCallIds) {\n payload = normalizeCallIds(payload)\n }\n\n // TUI logging — use \"WS\" as method indicator\n const tuiLogId = tuiLogger.startRequest({\n method: \"WS\",\n path: \"/v1/responses\",\n model: resolvedModel,\n })\n\n // Create request context for tracking\n const reqCtx = getRequestContextManager().create({ endpoint: \"openai-responses\", tuiLogId })\n\n reqCtx.setOriginalRequest({\n model: requestedModel,\n messages: responsesInputToMessages(payload.input),\n stream: true,\n tools: payload.tools,\n system: payload.instructions ?? undefined,\n payload,\n })\n\n // Update TUI with resolved model (if different from requested)\n if (requestedModel !== resolvedModel) {\n tuiLogger.updateRequest(tuiLogId, {\n model: resolvedModel,\n clientModel: requestedModel,\n })\n }\n\n // Build pipeline adapter and strategies (shared with HTTP handler)\n const headersCapture: HeadersCapture = {}\n const adapter = createResponsesAdapter(selectedModel, headersCapture, (wireRequest) => {\n reqCtx.setAttemptWireRequest(wireRequest)\n })\n const strategies = createResponsesStrategies()\n\n try {\n // Execute pipeline (model resolution, token refresh, rate limiting)\n const pipelineResult = await executeRequestPipeline({\n adapter,\n strategies,\n payload,\n originalPayload: payload,\n model: selectedModel,\n maxRetries: 1,\n requestContext: reqCtx,\n })\n\n // Capture HTTP headers from the final attempt for history recording\n reqCtx.setHttpHeaders(headersCapture)\n\n const response = pipelineResult.response\n\n // Stream SSE events → WebSocket JSON frames\n // The pipeline returns an AsyncIterable<ServerSentEventMessage> for streaming\n const iterator = (response as AsyncIterable<{ data?: string; event?: string }>)[Symbol.asyncIterator]()\n const acc = createResponsesStreamAccumulator()\n const idleTimeoutMs = state.streamIdleTimeout > 0 ? state.streamIdleTimeout * 1000 : 0\n let eventsReceived = 0\n\n while (true) {\n const shutdownSignal = getShutdownSignal()\n const result = await raceIteratorNext(iterator.next(), {\n idleTimeoutMs,\n abortSignal: shutdownSignal ?? undefined,\n })\n\n if (result === STREAM_ABORTED || result.done) break\n\n const sseEvent = result.value\n if (!sseEvent.data || sseEvent.data === \"[DONE]\") continue\n\n try {\n const parsed = JSON.parse(sseEvent.data) as ResponsesStreamEvent\n accumulateResponsesStreamEvent(parsed, acc)\n\n // Forward event as WebSocket JSON frame\n ws.send(sseEvent.data)\n eventsReceived++\n\n // Update TUI with stream progress\n tuiLogger.updateRequest(tuiLogId, { streamEventsIn: eventsReceived })\n\n // Check for terminal events\n if (TERMINAL_EVENTS.has(parsed.type)) break\n } catch {\n consola.debug(\"[WS] Skipping unparseable SSE event\")\n }\n }\n\n // Record to history\n const responseData = buildResponsesResponseData(acc, resolvedModel)\n reqCtx.complete(responseData)\n\n // Close WebSocket gracefully\n ws.close(1000, \"done\")\n } catch (error) {\n reqCtx.setHttpHeaders(headersCapture)\n reqCtx.fail(resolvedModel, error)\n\n const message = error instanceof Error ? error.message : String(error)\n consola.error(`[WS] Responses API error: ${message}`)\n sendErrorAndClose(ws, message)\n }\n}\n\n// ============================================================================\n// WebSocket route registration\n// ============================================================================\n\n/**\n * Initialize WebSocket routes for the Responses API.\n *\n * Registers GET /v1/responses and GET /responses on the root Hono app\n * with WebSocket upgrade handling. Uses the shared WebSocket adapter\n * to avoid multiple upgrade listeners on the same HTTP server.\n *\n * @param rootApp - The root Hono app instance\n * @param upgradeWs - Shared WebSocket upgrade function from createWebSocketAdapter\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function initResponsesWebSocket(rootApp: Hono, upgradeWs: UpgradeWebSocket<any>): void {\n // Create the WebSocket handler\n const wsHandler = upgradeWs(() => ({\n onOpen(_event: Event, _ws: WSContext) {\n consola.debug(\"[WS] Responses API WebSocket connected\")\n },\n\n onClose(_event: Event, _ws: WSContext) {\n consola.debug(\"[WS] Responses API WebSocket disconnected\")\n },\n\n async onMessage(event: MessageEvent, ws: WSContext) {\n // Parse the incoming message\n let message: unknown\n try {\n const raw = typeof event.data === \"string\" ? event.data : String(event.data)\n message = JSON.parse(raw)\n } catch {\n sendErrorAndClose(ws, \"Invalid JSON message\", \"invalid_request_error\")\n return\n }\n\n // Extract and validate payload\n const payload = extractPayload(message)\n if (!payload) {\n sendErrorAndClose(\n ws,\n 'Invalid message: expected { type: \"response.create\", response: { model, input, ... } }',\n \"invalid_request_error\",\n )\n return\n }\n\n // Handle the response creation\n await handleResponseCreate(ws, payload)\n },\n\n onError(event: Event, ws: WSContext) {\n consola.error(\"[WS] Responses API WebSocket error:\", event)\n try {\n ws.close(1011, \"Internal error\")\n } catch {\n // Already closed\n }\n },\n }))\n\n // Register on both paths (GET for WebSocket upgrade, coexists with POST for HTTP)\n rootApp.get(\"/v1/responses\", wsHandler)\n rootApp.get(\"/responses\", wsHandler)\n\n consola.debug(\"[WS] Responses API WebSocket routes registered\")\n}\n","import type { ChatCompletionsPayload, ContentPart, Message, Tool, ToolCall } from \"~/types/api/openai-chat-completions\"\n\nimport type { Model } from \"./client\"\n\n// ============================================================================\n// GPT Encoder Support\n// ============================================================================\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/** Encoder interface for tokenization */\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 = (contentParts: Array<ContentPart>, encoder: Encoder): number => {\n let tokens = 0\n for (const part of contentParts) {\n if (part.type === \"image_url\") {\n // Image URLs incur ~85 tokens overhead for the image processing metadata\n // This is an approximation based on OpenAI's image token calculation\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 // Each message incurs 3 tokens overhead for role/metadata framing\n // Based on OpenAI's token counting methodology\n const tokensPerMessage = 3\n // Additional token when a \"name\" field is present\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(value as Array<ToolCall>, encoder, constants)\n }\n if (key === \"content\" && Array.isArray(value)) {\n tokens += calculateContentPartsTokens(value as Array<ContentPart>, encoder)\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|> (3 tokens)\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 const rawModule =\n supportedEncoding in ENCODING_MAP ? await ENCODING_MAP[supportedEncoding]() : await ENCODING_MAP.o200k_base()\n\n // Wrap encode to disable special token checks.\n // gpt-tokenizer defaults to disallowedSpecial='all', which throws on\n // tokens like <|im_start|> that appear in tool_result content.\n const encoder: Encoder = {\n encode: (text: string) => rawModule.encode(text, { disallowedSpecial: new Set() }),\n }\n\n encodingCache.set(encoding, encoder)\n return encoder\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 * Count tokens in a text string using the model's tokenizer.\n * This is a simple wrapper for counting tokens in plain text.\n */\nexport const countTextTokens = async (text: string, model: Model): Promise<number> => {\n const tokenizer = getTokenizerFromModel(model)\n const encoder = await getEncodeChatFunction(tokenizer)\n return encoder.encode(text).length\n}\n\n/**\n * Get model-specific constants for token calculation.\n * These values are empirically determined based on OpenAI's function calling token overhead.\n * - funcInit: Tokens for initializing a function definition\n * - propInit: Tokens for initializing the properties section\n * - propKey: Tokens per property key\n * - enumInit: Token adjustment when enum is present (negative because type info is replaced)\n * - enumItem: Tokens per enum value\n * - funcEnd: Tokens for closing the function definition\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 = typeof propertyValue === \"string\" ? propertyValue : JSON.stringify(propertyValue)\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 = 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 = (tool: Tool, encoder: Encoder, constants: ReturnType<typeof getModelConstants>): 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 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- typeof null === \"object\" in JavaScript\n if (typeof func.parameters === \"object\" && func.parameters !== null) {\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// Main Token Count API\n// ============================================================================\n\n/**\n * Calculate the token count of messages.\n * Uses the tokenizer specified by the GitHub Copilot API model info.\n * All models (including Claude) use GPT tokenizers (o200k_base or cl100k_base).\n */\nexport const getTokenCount = async (\n payload: ChatCompletionsPayload,\n model: Model,\n): Promise<{ input: number; output: number }> => {\n // Use the tokenizer specified by the API (defaults to o200k_base)\n const tokenizer = getTokenizerFromModel(model)\n const encoder = await getEncodeChatFunction(tokenizer)\n\n const simplifiedMessages = payload.messages\n const inputMessages = simplifiedMessages.filter((msg) => msg.role !== \"assistant\")\n const outputMessages = simplifiedMessages.filter((msg) => msg.role === \"assistant\")\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","/**\n * OpenAI orphaned tool block filtering.\n *\n * Filters orphaned tool messages and tool_calls from OpenAI messages\n * to ensure API compatibility.\n */\n\nimport consola from \"consola\"\n\nimport type { Message, ToolCall } from \"~/types/api/openai-chat-completions\"\n\n/**\n * Get tool_call IDs from an OpenAI assistant message.\n */\nexport function getOpenAIToolCallIds(msg: Message): Array<string> {\n if (msg.role === \"assistant\" && msg.tool_calls) {\n return msg.tool_calls.map((tc: ToolCall) => tc.id)\n }\n return []\n}\n\n/**\n * Get tool_result IDs from OpenAI tool messages.\n */\nexport function getOpenAIToolResultIds(messages: Array<Message>): Set<string> {\n const ids = new Set<string>()\n for (const msg of messages) {\n if (msg.role === \"tool\" && msg.tool_call_id) {\n ids.add(msg.tool_call_id)\n }\n }\n return ids\n}\n\n/**\n * Filter orphaned tool messages from OpenAI messages.\n */\nexport function filterOpenAIOrphanedToolResults(messages: Array<Message>): Array<Message> {\n // Collect all available tool_call IDs\n const toolCallIds = new Set<string>()\n for (const msg of messages) {\n for (const id of getOpenAIToolCallIds(msg)) {\n toolCallIds.add(id)\n }\n }\n\n // Filter out orphaned tool messages\n let removedCount = 0\n const filtered = messages.filter((msg) => {\n if (msg.role === \"tool\" && msg.tool_call_id && !toolCallIds.has(msg.tool_call_id)) {\n removedCount++\n return false\n }\n return true\n })\n\n if (removedCount > 0) {\n consola.debug(`[Sanitizer:OpenAI] Filtered ${removedCount} orphaned tool_result`)\n }\n\n return filtered\n}\n\n/**\n * Filter orphaned tool_calls from OpenAI assistant messages.\n */\nexport function filterOpenAIOrphanedToolUse(messages: Array<Message>): Array<Message> {\n const toolResultIds = getOpenAIToolResultIds(messages)\n\n // Filter out orphaned tool_calls from assistant messages\n const result: Array<Message> = []\n let removedCount = 0\n\n for (const msg of messages) {\n if (msg.role === \"assistant\" && msg.tool_calls) {\n const filteredToolCalls = msg.tool_calls.filter((tc: ToolCall) => {\n if (!toolResultIds.has(tc.id)) {\n removedCount++\n return false\n }\n return true\n })\n\n // If all tool_calls were removed but there's still content, keep the message\n if (filteredToolCalls.length === 0) {\n if (msg.content) {\n result.push({ ...msg, tool_calls: undefined })\n }\n // Skip message entirely if no content and no tool_calls\n continue\n }\n\n result.push({ ...msg, tool_calls: filteredToolCalls })\n continue\n }\n\n result.push(msg)\n }\n\n if (removedCount > 0) {\n consola.debug(`[Sanitizer:OpenAI] Filtered ${removedCount} orphaned tool_use`)\n }\n\n return result\n}\n\n/**\n * Ensure OpenAI messages start with a user message.\n */\nexport function ensureOpenAIStartsWithUser(messages: Array<Message>): Array<Message> {\n let startIndex = 0\n while (startIndex < messages.length && messages[startIndex].role !== \"user\") {\n startIndex++\n }\n\n if (startIndex > 0) {\n consola.debug(`[Sanitizer:OpenAI] Skipped ${startIndex} leading non-user messages`)\n }\n\n return messages.slice(startIndex)\n}\n\n/**\n * Extract system/developer messages from the beginning of OpenAI messages.\n */\nexport function extractOpenAISystemMessages(messages: Array<Message>): {\n systemMessages: Array<Message>\n conversationMessages: Array<Message>\n} {\n let splitIndex = 0\n while (splitIndex < messages.length) {\n const role = messages[splitIndex].role\n if (role !== \"system\" && role !== \"developer\") break\n splitIndex++\n }\n\n return {\n systemMessages: messages.slice(0, splitIndex),\n conversationMessages: messages.slice(splitIndex),\n }\n}\n","import type { Message } from \"~/types/api/openai-chat-completions\"\n\n/** Estimate tokens for a single message (fast approximation) */\nexport function estimateMessageTokens(msg: Message): number {\n let charCount = 0\n\n if (typeof msg.content === \"string\") {\n charCount = msg.content.length\n } else if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n if (part.type === \"text\") {\n charCount += part.text.length\n } else if (\"image_url\" in part) {\n charCount += Math.min(part.image_url.url.length, 10000)\n }\n }\n }\n\n if (msg.tool_calls) {\n charCount += JSON.stringify(msg.tool_calls).length\n }\n\n return Math.ceil(charCount / 4) + 10\n}\n\n/** Calculate cumulative token sums from the end of the message array */\nexport function calculateCumulativeSums(messages: Array<Message>): { cumTokens: Array<number> } {\n const n = messages.length\n const cumTokens = Array.from<number>({ length: n + 1 }).fill(0)\n for (let i = n - 1; i >= 0; i--) {\n cumTokens[i] = cumTokens[i + 1] + estimateMessageTokens(messages[i])\n }\n return { cumTokens }\n}\n","import type { ChatCompletionsPayload, Message } from \"~/types/api/openai-chat-completions\"\n\nimport { LARGE_TOOL_RESULT_THRESHOLD, compressToolResultContent } from \"../../auto-truncate\"\nimport {\n ensureOpenAIStartsWithUser,\n extractOpenAISystemMessages,\n filterOpenAIOrphanedToolResults,\n filterOpenAIOrphanedToolUse,\n} from \"../orphan-filter\"\nimport { calculateCumulativeSums, estimateMessageTokens } from \"./token-counting\"\n\n/**\n * Clean up orphaned tool messages and ensure valid conversation start.\n * Loops until stable since each pass may create new orphans.\n */\nexport function cleanupMessages(messages: Array<Message>): Array<Message> {\n let result = messages\n let prevLength: number\n do {\n prevLength = result.length\n result = filterOpenAIOrphanedToolResults(result)\n result = filterOpenAIOrphanedToolUse(result)\n result = ensureOpenAIStartsWithUser(result)\n } while (result.length !== prevLength)\n return result\n}\n\n/**\n * Smart compression strategy for OpenAI format.\n */\nexport function smartCompressToolResults(\n messages: Array<Message>,\n tokenLimit: number,\n preservePercent: number,\n): {\n messages: Array<Message>\n compressedCount: number\n compressThresholdIndex: number\n} {\n const n = messages.length\n const { cumTokens } = calculateCumulativeSums(messages)\n const preserveTokenLimit = Math.floor(tokenLimit * preservePercent)\n\n let thresholdIndex = n\n for (let i = n - 1; i >= 0; i--) {\n if (cumTokens[i] > preserveTokenLimit) {\n thresholdIndex = i + 1\n break\n }\n thresholdIndex = i\n }\n\n if (thresholdIndex >= n) {\n return { messages, compressedCount: 0, compressThresholdIndex: n }\n }\n\n const result: Array<Message> = []\n let compressedCount = 0\n\n for (const [i, msg] of messages.entries()) {\n if (\n i < thresholdIndex\n && msg.role === \"tool\"\n && typeof msg.content === \"string\"\n && msg.content.length > LARGE_TOOL_RESULT_THRESHOLD\n ) {\n compressedCount++\n result.push({\n ...msg,\n content: compressToolResultContent(msg.content),\n })\n continue\n }\n result.push(msg)\n }\n\n return {\n messages: result,\n compressedCount,\n compressThresholdIndex: thresholdIndex,\n }\n}\n\ninterface PreserveSearchParams {\n messages: Array<Message>\n systemTokens: number\n tokenLimit: number\n}\n\n/**\n * Find the optimal index from which to preserve messages.\n * Uses binary search with pre-calculated cumulative sums.\n */\nexport function findOptimalPreserveIndex(params: PreserveSearchParams): number {\n const { messages, systemTokens, tokenLimit } = params\n\n if (messages.length === 0) return 0\n\n const markerTokens = 50\n const availableTokens = tokenLimit - systemTokens - markerTokens\n\n if (availableTokens <= 0) {\n return messages.length\n }\n\n const n = messages.length\n const { cumTokens } = calculateCumulativeSums(messages)\n\n let left = 0\n let right = n\n\n while (left < right) {\n const mid = (left + right) >>> 1\n if (cumTokens[mid] <= availableTokens) {\n right = mid\n } else {\n left = mid + 1\n }\n }\n\n return left\n}\n\n/**\n * Generate a summary of removed messages for context.\n */\nexport function generateRemovedMessagesSummary(removedMessages: Array<Message>): string {\n const toolCalls: Array<string> = []\n let userMessageCount = 0\n let assistantMessageCount = 0\n\n for (const msg of removedMessages) {\n if (msg.role === \"user\") {\n userMessageCount++\n } else if (msg.role === \"assistant\") {\n assistantMessageCount++\n }\n\n if (msg.tool_calls) {\n for (const toolCall of msg.tool_calls) {\n if (toolCall.function.name) {\n toolCalls.push(toolCall.function.name)\n }\n }\n }\n }\n\n const parts: Array<string> = []\n if (userMessageCount > 0 || assistantMessageCount > 0) {\n const breakdown = []\n if (userMessageCount > 0) breakdown.push(`${userMessageCount} user`)\n if (assistantMessageCount > 0) breakdown.push(`${assistantMessageCount} assistant`)\n parts.push(`Messages: ${breakdown.join(\", \")}`)\n }\n\n if (toolCalls.length > 0) {\n const uniqueTools = [...new Set(toolCalls)]\n const displayTools =\n uniqueTools.length > 5 ? [...uniqueTools.slice(0, 5), `+${uniqueTools.length - 5} more`] : uniqueTools\n parts.push(`Tools used: ${displayTools.join(\", \")}`)\n }\n\n return parts.join(\". \")\n}\n\n/**\n * Add a compression notice to the system message.\n */\nexport function addCompressionNotice(\n payload: ChatCompletionsPayload,\n compressedCount: number,\n): ChatCompletionsPayload {\n const notice =\n `\\n\\n[CONTEXT NOTE]\\n`\n + `${compressedCount} large tool results have been compressed to reduce context size.\\n`\n + `The compressed results show the beginning and end of the content with an omission marker.\\n`\n + `If you need the full content, you can re-read the file or re-run the tool.\\n`\n + `[END NOTE]`\n\n const messages = [...payload.messages]\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.role === \"system\" || msg.role === \"developer\") {\n if (typeof msg.content === \"string\") {\n messages[i] = { ...msg, content: msg.content + notice }\n }\n break\n }\n }\n\n return { ...payload, messages }\n}\n\n/**\n * Create truncation context to append to system messages.\n */\nexport function createTruncationSystemContext(\n removedCount: number,\n compressedCount: number,\n summary: string,\n): string {\n let context = `\\n\\n[CONVERSATION CONTEXT]\\n`\n\n if (removedCount > 0) {\n context += `${removedCount} earlier messages have been removed due to context window limits.\\n`\n }\n if (compressedCount > 0) {\n context += `${compressedCount} large tool results have been compressed.\\n`\n }\n if (summary) {\n context += `Summary of removed content: ${summary}\\n`\n }\n\n context +=\n `If you need earlier context, ask the user or check available tools for conversation history access.\\n`\n + `[END CONTEXT]`\n\n return context\n}\n\n/** Create a truncation marker message (fallback when no system message) */\nexport function createTruncationMarker(\n removedCount: number,\n compressedCount: number,\n summary: string,\n): Message {\n const parts: Array<string> = []\n\n if (removedCount > 0) {\n parts.push(`${removedCount} earlier messages removed`)\n }\n if (compressedCount > 0) {\n parts.push(`${compressedCount} tool results compressed`)\n }\n\n let content = `[CONTEXT MODIFIED: ${parts.join(\", \")} to fit context limits]`\n if (summary) {\n content += `\\n[Summary: ${summary}]`\n }\n return {\n role: \"user\",\n content,\n }\n}\n\n/**\n * Extract system token approximation from OpenAI messages.\n */\nexport function estimateSystemTokens(messages: Array<Message>): number {\n const { systemMessages } = extractOpenAISystemMessages(messages)\n return systemMessages.reduce((sum, message) => sum + estimateMessageTokens(message), 0)\n}\n","/**\n * Auto-truncate module: Automatically truncates conversation history\n * when it exceeds token limits (OpenAI format).\n *\n * Key features:\n * - Binary search for optimal truncation point\n * - Token limit enforcement with learned calibration\n * - Preserves system messages\n * - Filters orphaned tool_result and tool_use messages\n * - Optional smart compression of old tool_result content\n */\n\nimport consola from \"consola\"\n\nimport type { Model } from \"~/lib/models/client\"\nimport type { ChatCompletionsPayload, Message } from \"~/types/api/openai-chat-completions\"\n\nimport { getTokenCount } from \"~/lib/models/tokenizer\"\nimport { bytesToKB } from \"~/lib/utils\"\n\nimport type { AutoTruncateConfig } from \"../auto-truncate\"\n\nimport {\n DEFAULT_AUTO_TRUNCATE_CONFIG,\n calibrate,\n computeSafetyMargin,\n getLearnedLimits,\n} from \"../auto-truncate\"\nimport { state } from \"~/lib/state\"\nimport { extractOpenAISystemMessages } from \"./orphan-filter\"\nimport {\n addCompressionNotice,\n cleanupMessages,\n createTruncationMarker,\n createTruncationSystemContext,\n findOptimalPreserveIndex,\n generateRemovedMessagesSummary,\n smartCompressToolResults,\n} from \"./auto-truncate/truncation\"\nimport { estimateMessageTokens } from \"./auto-truncate/token-counting\"\n\n// ============================================================================\n// Result Types\n// ============================================================================\n\n/** Result of auto-truncate operation */\nexport interface OpenAIAutoTruncateResult {\n payload: ChatCompletionsPayload\n wasTruncated: boolean\n originalTokens: number\n compactedTokens: number\n removedMessageCount: number\n /** Processing time in milliseconds */\n processingTimeMs: number\n}\n\n/** Result of needs-compaction check */\nexport interface OpenAICompactionCheckResult {\n needed: boolean\n currentTokens: number\n tokenLimit: number\n reason?: \"tokens\"\n}\n\n// ============================================================================\n// Limit Calculation\n// ============================================================================\n\n/**\n * Calculate the effective token limit for auto-truncate.\n * Uses explicit target if provided, otherwise learned limits with calibration,\n * otherwise model capabilities with safety margin.\n *\n * Returns undefined when no limit information is available — the caller\n * should skip truncation rather than guess with a hardcoded default.\n */\nfunction calculateTokenLimit(model: Model, config: AutoTruncateConfig): number | undefined {\n // Use explicit target if provided (reactive retry — caller already applied margin)\n if (config.targetTokenLimit !== undefined) {\n return config.targetTokenLimit\n }\n\n // Check for learned limits (adjusted based on previous errors)\n const learned = getLearnedLimits(model.id)\n if (learned) {\n const margin = computeSafetyMargin(learned.sampleCount)\n return Math.floor(learned.tokenLimit * (1 - margin))\n }\n\n // Use model capabilities with static safety margin\n const rawTokenLimit =\n model.capabilities?.limits?.max_context_window_tokens ?? model.capabilities?.limits?.max_prompt_tokens\n\n if (rawTokenLimit === undefined) return undefined\n\n return Math.floor(rawTokenLimit * (1 - config.safetyMarginPercent / 100))\n}\n\n// ============================================================================\n// Main API\n// ============================================================================\n\n/**\n * Check if payload needs compaction based on learned model limits.\n * Returns early with `needed: false` when no limits are known for the model.\n */\nexport async function checkNeedsCompactionOpenAI(\n payload: ChatCompletionsPayload,\n model: Model,\n config: Partial<AutoTruncateConfig> = {},\n): Promise<OpenAICompactionCheckResult> {\n const cfg = { ...DEFAULT_AUTO_TRUNCATE_CONFIG, ...config }\n\n // If no learned limits and no explicit target, skip pre-check\n // (we don't know the real limit yet — let the server tell us)\n const learned = getLearnedLimits(model.id)\n if (!learned && cfg.targetTokenLimit === undefined) {\n return {\n needed: false,\n currentTokens: 0,\n tokenLimit: 0,\n }\n }\n\n const tokenLimit = calculateTokenLimit(model, cfg)\n\n // Defensive: should not reach here without a resolvable limit (guarded by early return above),\n // but satisfy the type system\n if (tokenLimit === undefined) {\n return {\n needed: false,\n currentTokens: 0,\n tokenLimit: 0,\n }\n }\n\n const tokenCount = await getTokenCount(payload, model)\n const rawTokens = tokenCount.input\n\n // Apply calibration to adjust the GPT tokenizer estimate\n const currentTokens = learned && learned.sampleCount > 0 ? calibrate(model.id, rawTokens) : rawTokens\n\n const exceedsTokens = cfg.checkTokenLimit && currentTokens > tokenLimit\n\n return {\n needed: exceedsTokens,\n currentTokens,\n tokenLimit,\n reason: exceedsTokens ? \"tokens\" : undefined,\n }\n}\n\n// ============================================================================\n// Truncation Steps\n// ============================================================================\n\n/** Shared context for truncation operations */\ninterface TruncationContext {\n payload: ChatCompletionsPayload\n model: Model\n cfg: AutoTruncateConfig\n tokenLimit: number\n originalTokens: number\n originalBytes: number\n startTime: number\n}\n\nfunction buildTimedResult(\n ctx: TruncationContext,\n result: Omit<OpenAIAutoTruncateResult, \"processingTimeMs\">,\n): OpenAIAutoTruncateResult {\n return { ...result, processingTimeMs: Math.round(performance.now() - ctx.startTime) }\n}\n\n/**\n * Step 1: Try compressing tool results to fit within limits.\n * First compresses old tool results, then all if needed.\n * Returns early result if compression alone is sufficient.\n */\nasync function tryCompressToolResults(\n ctx: TruncationContext,\n): Promise<{ workingMessages: Array<Message>; compressedCount: number; earlyResult?: OpenAIAutoTruncateResult }> {\n if (!state.compressToolResultsBeforeTruncate) {\n return { workingMessages: ctx.payload.messages, compressedCount: 0 }\n }\n\n // Step 1a: Compress old tool messages\n const compressionResult = smartCompressToolResults(\n ctx.payload.messages,\n ctx.tokenLimit,\n ctx.cfg.preserveRecentPercent,\n )\n let workingMessages = compressionResult.messages\n let compressedCount = compressionResult.compressedCount\n\n // Check if compression alone was enough\n const compressedPayload = { ...ctx.payload, messages: workingMessages }\n const compressedBytes = JSON.stringify(compressedPayload).length\n const compressedTokenCount = await getTokenCount(compressedPayload, ctx.model)\n\n if (compressedTokenCount.input <= ctx.tokenLimit) {\n const elapsedMs = Math.round(performance.now() - ctx.startTime)\n consola.info(\n `[AutoTruncate:OpenAI] tokens: ${ctx.originalTokens}→${compressedTokenCount.input}, `\n + `${bytesToKB(ctx.originalBytes)}→${bytesToKB(compressedBytes)}KB `\n + `(compressed ${compressedCount} tool_results) [${elapsedMs}ms]`,\n )\n\n const noticePayload = addCompressionNotice(compressedPayload, compressedCount)\n // Estimate notice token overhead instead of full recount (~150 chars / 4 + framing)\n const noticeTokenOverhead = Math.ceil(150 / 4) + 10\n\n return {\n workingMessages,\n compressedCount,\n earlyResult: buildTimedResult(ctx, {\n payload: noticePayload,\n wasTruncated: true,\n originalTokens: ctx.originalTokens,\n compactedTokens: compressedTokenCount.input + noticeTokenOverhead,\n removedMessageCount: 0,\n }),\n }\n }\n\n // Step 1b: Compress ALL tool messages (including recent ones)\n const allCompression = smartCompressToolResults(\n workingMessages,\n ctx.tokenLimit,\n 0.0, // preservePercent=0 means compress all messages\n )\n if (allCompression.compressedCount > 0) {\n workingMessages = allCompression.messages\n compressedCount += allCompression.compressedCount\n\n // Check if compressing all was enough\n const allCompressedPayload = { ...ctx.payload, messages: workingMessages }\n const allCompressedBytes = JSON.stringify(allCompressedPayload).length\n const allCompressedTokenCount = await getTokenCount(allCompressedPayload, ctx.model)\n\n if (allCompressedTokenCount.input <= ctx.tokenLimit) {\n const elapsedMs = Math.round(performance.now() - ctx.startTime)\n consola.info(\n `[AutoTruncate:OpenAI] tokens: ${ctx.originalTokens}→${allCompressedTokenCount.input}, `\n + `${bytesToKB(ctx.originalBytes)}→${bytesToKB(allCompressedBytes)}KB `\n + `(compressed ${compressedCount} tool_results, including recent) [${elapsedMs}ms]`,\n )\n\n const noticePayload = addCompressionNotice(allCompressedPayload, compressedCount)\n // Estimate notice token overhead instead of full recount\n const noticeTokenOverhead = Math.ceil(150 / 4) + 10\n\n return {\n workingMessages,\n compressedCount,\n earlyResult: buildTimedResult(ctx, {\n payload: noticePayload,\n wasTruncated: true,\n originalTokens: ctx.originalTokens,\n compactedTokens: allCompressedTokenCount.input + noticeTokenOverhead,\n removedMessageCount: 0,\n }),\n }\n }\n }\n\n return { workingMessages, compressedCount }\n}\n\n/**\n * Step 2: Remove messages to fit within limits using binary search.\n * Handles orphan cleanup, summary generation, and result assembly.\n */\nasync function truncateByMessageRemoval(\n ctx: TruncationContext,\n workingMessages: Array<Message>,\n compressedCount: number,\n): Promise<OpenAIAutoTruncateResult> {\n // Extract system messages from working messages\n const { systemMessages, conversationMessages } = extractOpenAISystemMessages(workingMessages)\n\n // Calculate system message token sizes\n const systemTokens = systemMessages.reduce((sum, m) => sum + estimateMessageTokens(m), 0)\n\n // Find optimal preserve index\n const preserveIndex = findOptimalPreserveIndex({\n messages: conversationMessages,\n systemTokens,\n tokenLimit: ctx.tokenLimit,\n })\n\n // Check if we can compact\n if (preserveIndex >= conversationMessages.length) {\n consola.warn(\"[AutoTruncate:OpenAI] Would need to remove all messages\")\n return buildTimedResult(ctx, {\n payload: ctx.payload,\n wasTruncated: false,\n originalTokens: ctx.originalTokens,\n compactedTokens: ctx.originalTokens,\n removedMessageCount: 0,\n })\n }\n\n // Build preserved messages and clean up orphans\n let preserved = conversationMessages.slice(preserveIndex)\n preserved = cleanupMessages(preserved)\n\n if (preserved.length === 0) {\n consola.warn(\"[AutoTruncate:OpenAI] All messages filtered out after cleanup\")\n return buildTimedResult(ctx, {\n payload: ctx.payload,\n wasTruncated: false,\n originalTokens: ctx.originalTokens,\n compactedTokens: ctx.originalTokens,\n removedMessageCount: 0,\n })\n }\n\n // Calculate removed messages and generate summary\n const removedMessages = conversationMessages.slice(0, preserveIndex)\n const removedCount = conversationMessages.length - preserved.length\n const summary = generateRemovedMessagesSummary(removedMessages)\n\n // Build new payload with truncation context\n let newSystemMessages = systemMessages\n let newMessages = preserved\n\n // Prefer adding context to last system message (cleaner for the model)\n if (systemMessages.length > 0) {\n const truncationContext = createTruncationSystemContext(removedCount, compressedCount, summary)\n const lastSystemIdx = systemMessages.length - 1\n const lastSystem = systemMessages[lastSystemIdx]\n\n // Append context to last system message\n const updatedSystem: Message = {\n ...lastSystem,\n\n content: typeof lastSystem.content === \"string\" ? lastSystem.content + truncationContext : lastSystem.content, // Can't append to array content\n }\n newSystemMessages = [...systemMessages.slice(0, lastSystemIdx), updatedSystem]\n } else {\n // No system messages, use marker message\n const marker = createTruncationMarker(removedCount, compressedCount, summary)\n newMessages = [marker, ...preserved]\n }\n\n const newPayload: ChatCompletionsPayload = {\n ...ctx.payload,\n messages: [...newSystemMessages, ...newMessages],\n }\n\n // Verify the result\n const newBytes = JSON.stringify(newPayload).length\n const newTokenCount = await getTokenCount(newPayload, ctx.model)\n\n // Log single line summary\n const actions: Array<string> = []\n if (removedCount > 0) actions.push(`removed ${removedCount} msgs`)\n if (compressedCount > 0) actions.push(`compressed ${compressedCount} tool_results`)\n const actionInfo = actions.length > 0 ? ` (${actions.join(\", \")})` : \"\"\n\n const elapsedMs = Math.round(performance.now() - ctx.startTime)\n consola.info(\n `[AutoTruncate:OpenAI] tokens: ${ctx.originalTokens}→${newTokenCount.input}, `\n + `${bytesToKB(ctx.originalBytes)}→${bytesToKB(newBytes)}KB${actionInfo} [${elapsedMs}ms]`,\n )\n\n // Warn if still over token limit\n if (newTokenCount.input > ctx.tokenLimit) {\n consola.warn(`[AutoTruncate:OpenAI] Result still over token limit (${newTokenCount.input} > ${ctx.tokenLimit})`)\n }\n\n return buildTimedResult(ctx, {\n payload: newPayload,\n wasTruncated: true,\n originalTokens: ctx.originalTokens,\n compactedTokens: newTokenCount.input,\n removedMessageCount: removedCount,\n })\n}\n\n// ============================================================================\n// Public Entry Points\n// ============================================================================\n\n/**\n * Perform auto-truncation on a payload that exceeds limits.\n * Uses binary search to find the optimal truncation point.\n *\n * Pipeline:\n * 1. Check if compaction is needed\n * 2. Try compressing tool results (old first, then all)\n * 3. If still over limit, remove messages via binary search\n */\nexport async function autoTruncateOpenAI(\n payload: ChatCompletionsPayload,\n model: Model,\n config: Partial<AutoTruncateConfig> = {},\n): Promise<OpenAIAutoTruncateResult> {\n const startTime = performance.now()\n const cfg = { ...DEFAULT_AUTO_TRUNCATE_CONFIG, ...config }\n const tokenLimit = calculateTokenLimit(model, cfg)\n\n // No limit information available — skip truncation and let the server decide\n if (tokenLimit === undefined) {\n return {\n payload,\n wasTruncated: false,\n originalTokens: 0,\n compactedTokens: 0,\n removedMessageCount: 0,\n processingTimeMs: Math.round(performance.now() - startTime),\n }\n }\n\n // Measure original size\n const originalBytes = JSON.stringify(payload).length\n const originalTokens = (await getTokenCount(payload, model)).input\n\n const ctx: TruncationContext = {\n payload,\n model,\n cfg,\n tokenLimit,\n originalTokens,\n originalBytes,\n startTime,\n }\n\n // Check if compaction is needed\n if (originalTokens <= tokenLimit) {\n return buildTimedResult(ctx, {\n payload,\n wasTruncated: false,\n originalTokens,\n compactedTokens: originalTokens,\n removedMessageCount: 0,\n })\n }\n\n // Step 1: Try tool result compression\n const { workingMessages, compressedCount, earlyResult } = await tryCompressToolResults(ctx)\n if (earlyResult) return earlyResult\n\n // Step 2: Message removal via binary search\n return await truncateByMessageRemoval(ctx, workingMessages, compressedCount)\n}\n\n/**\n * Create a marker to prepend to responses indicating auto-truncation occurred.\n */\nexport function createTruncationResponseMarkerOpenAI(result: OpenAIAutoTruncateResult): string {\n if (!result.wasTruncated) return \"\"\n\n const reduction = result.originalTokens - result.compactedTokens\n const percentage = Math.round((reduction / result.originalTokens) * 100)\n\n return (\n `\\n\\n---\\n[Auto-truncated: ${result.removedMessageCount} messages removed, `\n + `${result.originalTokens} → ${result.compactedTokens} tokens (${percentage}% reduction)]`\n )\n}\n","import type { ServerSentEventMessage } from \"fetch-event-stream\"\n\nimport consola from \"consola\"\nimport { events } from \"fetch-event-stream\"\n\nimport type { HeadersCapture } from \"~/lib/context/request\"\nimport type { Model } from \"~/lib/models/client\"\nimport type { ChatCompletionsPayload, ChatCompletionResponse } from \"~/types/api/openai-chat-completions\"\n\nimport { copilotBaseUrl } from \"~/lib/copilot-api\"\nimport { HTTPError } from \"~/lib/error\"\nimport { createFetchSignal, captureHttpHeaders, sanitizeHeadersForHistory } from \"~/lib/fetch-utils\"\nimport { state } from \"~/lib/state\"\nimport { prepareChatCompletionsRequest, type PreparedOpenAIRequest } from \"./request-preparation\"\n\ninterface CreateChatCompletionsOptions {\n resolvedModel?: Model\n headersCapture?: HeadersCapture\n onPrepared?: (request: PreparedOpenAIRequest<ChatCompletionsPayload>) => void\n}\n\nexport { prepareChatCompletionsRequest, type PreparedOpenAIRequest } from \"./request-preparation\"\n\nexport const createChatCompletions = async (\n payload: ChatCompletionsPayload,\n opts?: CreateChatCompletionsOptions,\n): Promise<ChatCompletionResponse | AsyncGenerator<ServerSentEventMessage>> => {\n if (!state.copilotToken) throw new Error(\"Copilot token not found\")\n\n const prepared = prepareChatCompletionsRequest(payload, opts)\n opts?.onPrepared?.({\n wire: prepared.wire,\n headers: sanitizeHeadersForHistory(prepared.headers),\n })\n const { wire, headers } = prepared\n\n // Apply fetch timeout if configured (connection + response headers)\n const fetchSignal = createFetchSignal()\n\n const response = await fetch(`${copilotBaseUrl(state)}/chat/completions`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(wire),\n signal: fetchSignal,\n })\n\n // Capture HTTP headers for history (before error check — capture even on failure)\n if (opts?.headersCapture) {\n captureHttpHeaders(opts.headersCapture, headers, response)\n }\n\n if (!response.ok) {\n consola.error(\"Failed to create chat completions\", response)\n throw await HTTPError.fromResponse(\"Failed to create chat completions\", response, wire.model)\n }\n\n if (wire.stream) {\n return events(response)\n }\n\n return (await response.json()) as ChatCompletionResponse\n}\n","/**\n * OpenAI message sanitization orchestrator.\n *\n * Combines system-reminder removal, orphan filtering, and empty block cleanup\n * into a single sanitization pipeline for OpenAI messages.\n */\n\nimport consola from \"consola\"\n\nimport type { SanitizeResult } from \"~/lib/request/pipeline\"\nimport type { ChatCompletionsPayload, Message } from \"~/types/api/openai-chat-completions\"\n\nimport { removeSystemReminderTags } from \"~/lib/system-prompt\"\n\nimport {\n extractOpenAISystemMessages,\n filterOpenAIOrphanedToolResults,\n filterOpenAIOrphanedToolUse,\n} from \"./orphan-filter\"\n\n// ============================================================================\n// Message Content Sanitization\n// ============================================================================\n\n/**\n * Remove system-reminder tags from OpenAI message content.\n * Handles both string content and array of content parts.\n *\n * NOTE: System prompt overrides are handled by\n * system-prompt.ts via config.yaml.\n */\nfunction sanitizeOpenAIMessageContent(msg: Message): Message {\n if (typeof msg.content === \"string\") {\n const sanitized = removeSystemReminderTags(msg.content)\n if (sanitized !== msg.content) {\n // Don't return empty content — keep original if sanitized is empty\n return sanitized ? { ...msg, content: sanitized } : msg\n }\n return msg\n }\n\n // Handle array of content parts (TextPart | ImagePart)\n if (Array.isArray(msg.content)) {\n const result = msg.content.reduce<{\n parts: Array<\n | { type: \"text\"; text: string }\n | {\n type: \"image_url\"\n image_url: { url: string; detail?: \"low\" | \"high\" | \"auto\" }\n }\n >\n modified: boolean\n }>(\n (acc, part) => {\n if (part.type === \"text\" && typeof part.text === \"string\") {\n const sanitized = removeSystemReminderTags(part.text)\n if (sanitized !== part.text) {\n if (sanitized) {\n acc.parts.push({ ...part, text: sanitized })\n }\n acc.modified = true\n return acc\n }\n }\n acc.parts.push(part)\n return acc\n },\n { parts: [], modified: false },\n )\n\n if (result.modified) {\n return { ...msg, content: result.parts }\n }\n }\n\n return msg\n}\n\n/**\n * Remove system-reminder tags from all OpenAI messages.\n */\nexport function removeOpenAISystemReminders(messages: Array<Message>): {\n messages: Array<Message>\n modifiedCount: number\n} {\n let modifiedCount = 0\n const result = messages.map((msg) => {\n const sanitized = sanitizeOpenAIMessageContent(msg)\n if (sanitized !== msg) modifiedCount++\n return sanitized\n })\n return { messages: result, modifiedCount }\n}\n\n// ============================================================================\n// Main Orchestrator\n// ============================================================================\n\n/**\n * Sanitize OpenAI messages by filtering orphaned tool messages and system reminders.\n *\n * @returns Sanitized payload and count of removed items\n */\nexport function sanitizeOpenAIMessages(payload: ChatCompletionsPayload): SanitizeResult<ChatCompletionsPayload> {\n const { systemMessages, conversationMessages } = extractOpenAISystemMessages(payload.messages)\n\n // Remove system-reminder tags from all messages\n const convResult = removeOpenAISystemReminders(conversationMessages)\n let messages = convResult.messages\n const sysResult = removeOpenAISystemReminders(systemMessages)\n const sanitizedSystemMessages = sysResult.messages\n const systemReminderRemovals = convResult.modifiedCount + sysResult.modifiedCount\n\n const originalCount = messages.length\n\n // Filter orphaned tool_result and tool_use messages\n messages = filterOpenAIOrphanedToolResults(messages)\n messages = filterOpenAIOrphanedToolUse(messages)\n\n // Final safety net: remove empty/whitespace-only text parts from array content\n const allMessages = [...sanitizedSystemMessages, ...messages].map((msg) => {\n if (!Array.isArray(msg.content)) return msg\n const filtered = msg.content.filter((part) => {\n if (part.type === \"text\") return part.text.trim() !== \"\"\n return true\n })\n if (filtered.length === msg.content.length) return msg\n return { ...msg, content: filtered }\n })\n\n const blocksRemoved = originalCount - messages.length\n\n if (blocksRemoved > 0) {\n consola.info(`[Sanitizer:OpenAI] Filtered ${blocksRemoved} orphaned tool messages`)\n }\n\n return {\n payload: {\n ...payload,\n messages: allMessages,\n },\n blocksRemoved,\n systemReminderRemovals,\n }\n}\n","/**\n * Stream accumulator for OpenAI format responses.\n * Handles accumulating ChatCompletionChunk events for history recording and tracking.\n */\n\nimport type { BaseStreamAccumulator } from \"~/lib/anthropic/stream-accumulator\"\nimport type { ChatCompletionChunk } from \"~/types/api/openai-chat-completions\"\n\n/** Internal tool call accumulator using string array to avoid O(n²) concatenation */\ninterface ToolCallAccumulator {\n id: string\n name: string\n argumentParts: Array<string>\n}\n\n/** Stream accumulator for OpenAI format */\nexport interface OpenAIStreamAccumulator extends BaseStreamAccumulator {\n cachedTokens: number\n reasoningTokens: number\n finishReason: string\n toolCalls: Array<{ id: string; name: string; arguments: string }>\n toolCallMap: Map<number, ToolCallAccumulator>\n}\n\nexport function createOpenAIStreamAccumulator(): OpenAIStreamAccumulator {\n return {\n model: \"\",\n inputTokens: 0,\n outputTokens: 0,\n cachedTokens: 0,\n reasoningTokens: 0,\n finishReason: \"\",\n rawContent: \"\",\n toolCalls: [],\n toolCallMap: new Map(),\n }\n}\n\n/** Accumulate a single parsed OpenAI chunk into the accumulator */\nexport function accumulateOpenAIStreamEvent(parsed: ChatCompletionChunk, acc: OpenAIStreamAccumulator) {\n if (parsed.model && !acc.model) acc.model = parsed.model\n\n if (parsed.usage) {\n acc.inputTokens = parsed.usage.prompt_tokens\n acc.outputTokens = parsed.usage.completion_tokens\n if (parsed.usage.prompt_tokens_details?.cached_tokens !== undefined) {\n acc.cachedTokens = parsed.usage.prompt_tokens_details.cached_tokens\n }\n if (parsed.usage.completion_tokens_details?.reasoning_tokens !== undefined) {\n acc.reasoningTokens = parsed.usage.completion_tokens_details.reasoning_tokens\n }\n }\n\n const choice = parsed.choices[0] as (typeof parsed.choices)[0] | undefined\n if (choice) {\n if (choice.delta.content) acc.rawContent += choice.delta.content\n if (choice.delta.tool_calls) {\n for (const tc of choice.delta.tool_calls) {\n const idx = tc.index\n if (!acc.toolCallMap.has(idx)) {\n acc.toolCallMap.set(idx, {\n id: tc.id ?? \"\",\n name: tc.function?.name ?? \"\",\n argumentParts: [],\n })\n }\n const item = acc.toolCallMap.get(idx)\n if (item) {\n if (tc.id) item.id = tc.id\n if (tc.function?.name) item.name = tc.function.name\n if (tc.function?.arguments) item.argumentParts.push(tc.function.arguments)\n }\n }\n }\n if (choice.finish_reason) acc.finishReason = choice.finish_reason\n }\n}\n","/**\n * Payload utilities for request handlers.\n */\n\nimport consola from \"consola\"\n\nimport type { Model } from \"~/lib/models/client\"\nimport type { MessagesPayload } from \"~/types/api/anthropic\"\nimport type { ChatCompletionsPayload } from \"~/types/api/openai-chat-completions\"\n\nimport { getTokenCount } from \"~/lib/models/tokenizer\"\nimport { bytesToKB } from \"~/lib/utils\"\n\n/**\n * Log helpful debugging information when a 413 error occurs.\n *\n * @param precomputedBytes - Optional pre-computed payload byte size to avoid redundant JSON.stringify\n */\nexport async function logPayloadSizeInfo(\n payload: ChatCompletionsPayload,\n model: Model | undefined,\n precomputedBytes?: number,\n) {\n const messageCount = payload.messages.length\n const bodySize = precomputedBytes ?? JSON.stringify(payload).length\n const bodySizeKB = bytesToKB(bodySize)\n\n // Count images and large messages\n let imageCount = 0\n let largeMessages = 0\n let totalImageSize = 0\n\n for (const msg of payload.messages) {\n if (Array.isArray(msg.content)) {\n for (const part of msg.content) {\n if (part.type === \"image_url\") {\n imageCount++\n if (part.image_url.url.startsWith(\"data:\")) {\n totalImageSize += part.image_url.url.length\n }\n }\n }\n }\n\n const msgSize = typeof msg.content === \"string\" ? msg.content.length : JSON.stringify(msg.content).length\n if (msgSize > 50000) largeMessages++\n }\n\n consola.info(\"\")\n consola.info(\"╭─────────────────────────────────────────────────────────╮\")\n consola.info(\"│ 413 Request Entity Too Large │\")\n consola.info(\"╰─────────────────────────────────────────────────────────╯\")\n consola.info(\"\")\n consola.info(` Request body size: ${bodySizeKB} KB (${bodySize.toLocaleString()} bytes)`)\n consola.info(` Message count: ${messageCount}`)\n\n if (model) {\n try {\n const tokenCount = await getTokenCount(payload, model)\n const limit = model.capabilities?.limits?.max_prompt_tokens ?? 128000\n consola.info(` Estimated tokens: ${tokenCount.input.toLocaleString()} / ${limit.toLocaleString()}`)\n } catch (error) {\n consola.debug(\"Token count estimation failed:\", error)\n }\n }\n\n if (imageCount > 0) {\n const imageSizeKB = bytesToKB(totalImageSize)\n consola.info(` Images: ${imageCount} (${imageSizeKB} KB base64 data)`)\n }\n if (largeMessages > 0) {\n consola.info(` Large messages (>50KB): ${largeMessages}`)\n }\n\n consola.info(\"\")\n consola.info(\" Suggestions:\")\n if (imageCount > 0) {\n consola.info(\" • Remove or resize large images in the conversation\")\n }\n consola.info(\" • Start a new conversation with /clear or /reset\")\n consola.info(\" • Reduce conversation history by deleting old messages\")\n consola.info(\"\")\n}\n\n/** Log payload size info for Anthropic format when a 413 error occurs */\nexport function logPayloadSizeInfoAnthropic(payload: MessagesPayload, model: Model | undefined) {\n const payloadSize = JSON.stringify(payload).length\n const messageCount = payload.messages.length\n const toolCount = payload.tools?.length ?? 0\n const systemSize = payload.system ? JSON.stringify(payload.system).length : 0\n\n consola.info(\n `[Anthropic 413] Payload size: ${bytesToKB(payloadSize)}KB, `\n + `messages: ${messageCount}, tools: ${toolCount}, system: ${bytesToKB(systemSize)}KB`,\n )\n\n if (model?.capabilities?.limits) {\n const limits = model.capabilities.limits\n consola.info(\n `[Anthropic 413] Model limits: context=${limits.max_context_window_tokens}, `\n + `prompt=${limits.max_prompt_tokens}, output=${limits.max_output_tokens}`,\n )\n }\n}\n","/**\n * Auto-truncate retry strategy.\n *\n * Handles token limit errors by truncating the message payload and retrying.\n */\n\nimport consola from \"consola\"\n\nimport type { ApiError } from \"~/lib/error\"\nimport type { Model } from \"~/lib/models/client\"\n\nimport { AUTO_TRUNCATE_RETRY_FACTOR, tryParseAndLearnLimit } from \"~/lib/auto-truncate\"\nimport { HTTPError } from \"~/lib/error\"\n\nimport type { RetryAction, RetryContext, RetryStrategy, SanitizeResult } from \"../pipeline\"\n\n/** Result from a truncation operation */\nexport interface TruncateResult<TPayload> {\n wasTruncated: boolean\n payload: TPayload\n removedMessageCount: number\n originalTokens: number\n compactedTokens: number\n processingTimeMs: number\n}\n\n/** Options passed to the truncation function */\nexport interface TruncateOptions {\n checkTokenLimit: boolean\n targetTokenLimit?: number\n}\n\n/**\n * Create an auto-truncate retry strategy.\n *\n * @param truncate - Format-specific truncation function\n * @param resanitize - Format-specific re-sanitization after truncation\n * @param isEnabled - Check if auto-truncate is enabled (typically reads state.autoTruncate)\n */\nexport function createAutoTruncateStrategy<TPayload>(opts: {\n truncate: (payload: TPayload, model: Model, options: TruncateOptions) => Promise<TruncateResult<TPayload>>\n resanitize: (payload: TPayload) => SanitizeResult<TPayload>\n isEnabled: () => boolean\n label: string\n}): RetryStrategy<TPayload> {\n const { truncate, resanitize, isEnabled, label } = opts\n\n return {\n name: \"auto-truncate\",\n\n canHandle(error: ApiError): boolean {\n if (!isEnabled()) return false\n return error.type === \"payload_too_large\" || error.type === \"token_limit\"\n },\n\n async handle(\n error: ApiError,\n currentPayload: TPayload,\n context: RetryContext<TPayload>,\n ): Promise<RetryAction<TPayload>> {\n const { attempt, originalPayload, model, maxRetries } = context\n\n if (!model) {\n return { action: \"abort\", error }\n }\n\n // Extract the raw error to get HTTP details for tryParseAndLearnLimit\n const rawError = error.raw\n if (!(rawError instanceof HTTPError)) {\n return { action: \"abort\", error }\n }\n\n // Estimate tokens using GPT tokenizer for calibration feedback\n const payloadJson = JSON.stringify(currentPayload)\n const estimatedTokens = Math.ceil(payloadJson.length / 4)\n\n const parsed = tryParseAndLearnLimit(rawError, model.id, true, estimatedTokens)\n\n if (!parsed) {\n // For 413 errors without parseable limit info, still retry with truncation\n if (rawError.status === 413) {\n consola.info(\n `[${label}] Attempt ${attempt + 1}/${maxRetries + 1}: ` + `413 Body too large, retrying with truncation...`,\n )\n\n const truncateResult = await truncate(originalPayload, model, {\n checkTokenLimit: true,\n })\n\n if (!truncateResult.wasTruncated) {\n return { action: \"abort\", error }\n }\n\n const sanitizeResult = resanitize(truncateResult.payload)\n return {\n action: \"retry\",\n payload: sanitizeResult.payload,\n meta: {\n truncateResult,\n sanitization: sanitizeResult.stats ?? {\n totalBlocksRemoved: sanitizeResult.blocksRemoved,\n systemReminderRemovals: sanitizeResult.systemReminderRemovals,\n },\n attempt: attempt + 1,\n },\n }\n }\n\n return { action: \"abort\", error }\n }\n\n // Calculate target token limit based on error info\n let targetTokenLimit: number | undefined\n\n if (parsed.limit) {\n targetTokenLimit = Math.floor(parsed.limit * AUTO_TRUNCATE_RETRY_FACTOR)\n consola.info(\n `[${label}] Attempt ${attempt + 1}/${maxRetries + 1}: `\n + `Token limit error (${parsed.current}>${parsed.limit}), `\n + `retrying with limit ${targetTokenLimit}...`,\n )\n }\n\n // Truncate from original payload (not from already-truncated)\n const truncateResult = await truncate(originalPayload, model, {\n checkTokenLimit: true,\n targetTokenLimit,\n })\n\n if (!truncateResult.wasTruncated) {\n // Truncation didn't help\n return { action: \"abort\", error }\n }\n\n // Re-sanitize the truncated payload\n const sanitizeResult = resanitize(truncateResult.payload)\n\n return {\n action: \"retry\",\n payload: sanitizeResult.payload,\n meta: {\n truncateResult,\n sanitization: sanitizeResult.stats ?? {\n totalBlocksRemoved: sanitizeResult.blocksRemoved,\n systemReminderRemovals: sanitizeResult.systemReminderRemovals,\n },\n attempt: attempt + 1,\n },\n }\n },\n }\n}\n","/**\n * Truncation marker utilities.\n */\n\n/** Minimal truncate result info needed for usage adjustment and markers */\nexport interface TruncateResultInfo {\n wasTruncated: boolean\n originalTokens?: number\n compactedTokens?: number\n removedMessageCount?: number\n}\n\n/**\n * Create a marker to prepend to responses indicating auto-truncation occurred.\n * Works with both OpenAI and Anthropic truncate results.\n */\nexport function createTruncationMarker(result: TruncateResultInfo): string {\n if (!result.wasTruncated) return \"\"\n\n const { originalTokens, compactedTokens, removedMessageCount } = result\n\n if (originalTokens === undefined || compactedTokens === undefined || removedMessageCount === undefined) {\n return `\\n\\n---\\n[Auto-truncated: conversation history was reduced to fit context limits]`\n }\n\n const reduction = originalTokens - compactedTokens\n const percentage = Math.round((reduction / originalTokens) * 100)\n\n return (\n `\\n\\n---\\n[Auto-truncated: ${removedMessageCount} messages removed, `\n + `${originalTokens} → ${compactedTokens} tokens (${percentage}% reduction)]`\n )\n}\n","import type { ServerSentEventMessage } from \"fetch-event-stream\"\nimport type { Context } from \"hono\"\n\nimport consola from \"consola\"\nimport { SSEStreamingApi, streamSSE } from \"hono/streaming\"\n\nimport type { RequestContext } from \"~/lib/context/request\"\nimport type { HeadersCapture } from \"~/lib/context/request\"\nimport type { MessageContent } from \"~/lib/history\"\nimport type { Model } from \"~/lib/models/client\"\nimport type { FormatAdapter } from \"~/lib/request/pipeline\"\nimport type {\n ChatCompletionChunk,\n ChatCompletionResponse,\n ChatCompletionsPayload,\n} from \"~/types/api/openai-chat-completions\"\n\nimport { executeWithAdaptiveRateLimit } from \"~/lib/adaptive-rate-limiter\"\nimport { MAX_AUTO_TRUNCATE_RETRIES } from \"~/lib/auto-truncate\"\nimport { getRequestContextManager } from \"~/lib/context/manager\"\nimport { HTTPError } from \"~/lib/error\"\nimport { ENDPOINT, isEndpointSupported } from \"~/lib/models/endpoint\"\nimport { resolveModelName } from \"~/lib/models/resolver\"\nimport {\n autoTruncateOpenAI,\n createTruncationResponseMarkerOpenAI,\n type OpenAIAutoTruncateResult,\n} from \"~/lib/openai/auto-truncate\"\nimport { createChatCompletions } from \"~/lib/openai/chat-completions-client\"\nimport { sanitizeOpenAIMessages } from \"~/lib/openai/sanitize\"\nimport { createOpenAIStreamAccumulator, accumulateOpenAIStreamEvent } from \"~/lib/openai/stream-accumulator\"\nimport { buildOpenAIResponseData, isNonStreaming, logPayloadSizeInfo } from \"~/lib/request\"\nimport { executeRequestPipeline } from \"~/lib/request/pipeline\"\nimport { createAutoTruncateStrategy, type TruncateResult } from \"~/lib/request/strategies/auto-truncate\"\nimport { createNetworkRetryStrategy } from \"~/lib/request/strategies/network-retry\"\nimport { createTokenRefreshStrategy } from \"~/lib/request/strategies/token-refresh\"\nimport { getShutdownSignal } from \"~/lib/shutdown\"\nimport { state } from \"~/lib/state\"\nimport { STREAM_ABORTED, StreamIdleTimeoutError, combineAbortSignals, raceIteratorNext } from \"~/lib/stream\"\nimport { processOpenAIMessages } from \"~/lib/system-prompt\"\nimport { tuiLogger } from \"~/lib/tui\"\nimport { isNullish } from \"~/lib/utils\"\n\nexport async function handleChatCompletion(c: Context) {\n const originalPayload = await c.req.json<ChatCompletionsPayload>()\n\n // Resolve model name aliases and date-suffixed versions\n const clientModel = originalPayload.model\n const resolvedModel = resolveModelName(clientModel)\n if (resolvedModel !== clientModel) {\n consola.debug(`Model name resolved: ${clientModel} → ${resolvedModel}`)\n originalPayload.model = resolvedModel\n }\n\n // Find the selected model and validate endpoint support\n const selectedModel = state.modelIndex.get(originalPayload.model)\n if (!isEndpointSupported(selectedModel, ENDPOINT.CHAT_COMPLETIONS)) {\n const msg = `Model \"${originalPayload.model}\" does not support the ${ENDPOINT.CHAT_COMPLETIONS} endpoint`\n throw new HTTPError(msg, 400, msg)\n }\n\n // System prompt collection + config-based overrides (always active)\n originalPayload.messages = await processOpenAIMessages(originalPayload.messages, originalPayload.model)\n\n // Get tracking ID\n const tuiLogId = c.get(\"tuiLogId\") as string | undefined\n\n // Create request context — triggers \"created\" event → history consumer inserts entry\n const manager = getRequestContextManager()\n const reqCtx = manager.create({ endpoint: \"openai-chat-completions\", tuiLogId })\n reqCtx.setOriginalRequest({\n // Use client's original model name (before resolution/overrides)\n model: clientModel,\n messages: originalPayload.messages as unknown as Array<MessageContent>,\n stream: originalPayload.stream ?? false,\n tools: originalPayload.tools?.map((t) => ({\n name: t.function.name,\n description: t.function.description,\n })),\n payload: originalPayload,\n })\n\n // Update TUI tracker with model info (immediate feedback)\n if (tuiLogId) {\n tuiLogger.updateRequest(tuiLogId, {\n model: originalPayload.model,\n ...(clientModel !== originalPayload.model && { clientModel }),\n })\n }\n\n // Sanitize messages (filter orphaned tool blocks, system-reminders)\n const { payload: sanitizedPayload } = sanitizeOpenAIMessages(originalPayload)\n\n const finalPayload =\n isNullish(sanitizedPayload.max_tokens) ?\n {\n ...sanitizedPayload,\n max_tokens: selectedModel?.capabilities?.limits?.max_output_tokens,\n }\n : sanitizedPayload\n\n if (isNullish(originalPayload.max_tokens)) {\n consola.debug(\"Set max_tokens to:\", JSON.stringify(finalPayload.max_tokens))\n }\n\n // Execute request with reactive retry pipeline\n return executeRequest({\n c,\n payload: finalPayload,\n originalPayload,\n selectedModel,\n reqCtx,\n })\n}\n\n/** Options for executeRequest */\ninterface ExecuteRequestOptions {\n c: Context\n payload: ChatCompletionsPayload\n originalPayload: ChatCompletionsPayload\n selectedModel: Model | undefined\n reqCtx: RequestContext\n}\n\n/**\n * Execute the API call with reactive retry pipeline.\n * Handles 413 and token limit errors with auto-truncation.\n */\nasync function executeRequest(opts: ExecuteRequestOptions) {\n const { c, payload, originalPayload, selectedModel, reqCtx } = opts\n\n // Build adapter and strategy for the pipeline\n const headersCapture: HeadersCapture = {}\n const adapter: FormatAdapter<ChatCompletionsPayload> = {\n format: \"openai-chat-completions\",\n sanitize: (p) => sanitizeOpenAIMessages(p),\n execute: (p) =>\n executeWithAdaptiveRateLimit(() =>\n createChatCompletions(p, {\n resolvedModel: selectedModel,\n headersCapture,\n onPrepared: ({ wire, headers }) => {\n reqCtx.setAttemptWireRequest({\n model: typeof wire.model === \"string\" ? wire.model : payload.model,\n messages: Array.isArray(wire.messages) ? wire.messages : [],\n payload: wire,\n headers,\n format: \"openai-chat-completions\",\n })\n },\n })),\n logPayloadSize: (p) => logPayloadSizeInfo(p, selectedModel),\n }\n\n const strategies = [\n createNetworkRetryStrategy<ChatCompletionsPayload>(),\n createTokenRefreshStrategy<ChatCompletionsPayload>(),\n createAutoTruncateStrategy<ChatCompletionsPayload>({\n truncate: (p, model, truncOpts) =>\n autoTruncateOpenAI(p, model, truncOpts) as Promise<TruncateResult<ChatCompletionsPayload>>,\n resanitize: (p) => sanitizeOpenAIMessages(p),\n isEnabled: () => state.autoTruncate,\n label: \"Completions\",\n }),\n ]\n\n // Track truncation result for non-streaming response marker\n let truncateResult: OpenAIAutoTruncateResult | undefined\n\n try {\n const result = await executeRequestPipeline({\n adapter,\n strategies,\n payload,\n originalPayload,\n model: selectedModel,\n maxRetries: MAX_AUTO_TRUNCATE_RETRIES,\n requestContext: reqCtx,\n onRetry: (attempt, _strategyName, _newPayload, meta) => {\n // Capture truncation result for response marker\n const retryTruncateResult = meta?.truncateResult as OpenAIAutoTruncateResult | undefined\n if (retryTruncateResult) {\n truncateResult = retryTruncateResult\n }\n\n // Update tracking tags\n if (reqCtx.tuiLogId) {\n tuiLogger.updateRequest(reqCtx.tuiLogId, { tags: [\"truncated\", `retry-${attempt + 1}`] })\n }\n },\n })\n\n // Capture HTTP headers from the final attempt for history recording\n reqCtx.setHttpHeaders(headersCapture)\n\n const response = result.response\n\n if (isNonStreaming(response as ChatCompletionResponse | AsyncIterable<unknown>)) {\n return handleNonStreamingResponse(c, response as ChatCompletionResponse, reqCtx, truncateResult)\n }\n\n consola.debug(\"Streaming response\")\n reqCtx.transition(\"streaming\")\n\n return streamSSE(c, async (stream) => {\n const clientAbort = new AbortController()\n stream.onAbort(() => clientAbort.abort())\n\n await handleStreamingResponse({\n stream,\n response: response as AsyncIterable<ServerSentEventMessage>,\n payload,\n reqCtx,\n truncateResult,\n clientAbortSignal: clientAbort.signal,\n })\n })\n } catch (error) {\n reqCtx.setHttpHeaders(headersCapture)\n reqCtx.fail(payload.model, error)\n throw error\n }\n}\n\n// Handle non-streaming response\nfunction handleNonStreamingResponse(\n c: Context,\n originalResponse: ChatCompletionResponse,\n reqCtx: RequestContext,\n truncateResult: OpenAIAutoTruncateResult | undefined,\n) {\n // Prepend truncation marker if auto-truncate was performed (only in verbose mode)\n let response = originalResponse\n if (state.verbose && truncateResult?.wasTruncated && response.choices[0]?.message.content) {\n const marker = createTruncationResponseMarkerOpenAI(truncateResult)\n const firstChoice = response.choices[0]\n response = {\n ...response,\n choices: [\n { ...firstChoice, message: { ...firstChoice.message, content: `${marker}${firstChoice.message.content}` } },\n ...response.choices.slice(1),\n ],\n }\n }\n\n const choice = response.choices[0]\n const usage = response.usage\n\n reqCtx.complete({\n success: true,\n model: response.model,\n usage: {\n input_tokens: usage?.prompt_tokens ?? 0,\n output_tokens: usage?.completion_tokens ?? 0,\n ...(usage?.prompt_tokens_details?.cached_tokens !== undefined && {\n cache_read_input_tokens: usage.prompt_tokens_details.cached_tokens,\n }),\n },\n stop_reason: choice.finish_reason ?? undefined,\n content: choice.message,\n })\n\n return c.json(response)\n}\n\n/** Options for handleStreamingResponse */\ninterface StreamingOptions {\n stream: SSEStreamingApi\n response: AsyncIterable<ServerSentEventMessage>\n payload: ChatCompletionsPayload\n reqCtx: RequestContext\n truncateResult: OpenAIAutoTruncateResult | undefined\n /** Abort signal that fires when the downstream client disconnects */\n clientAbortSignal?: AbortSignal\n}\n\n// Handle streaming response\nasync function handleStreamingResponse(opts: StreamingOptions) {\n const { stream, response, payload, reqCtx, truncateResult, clientAbortSignal } = opts\n const acc = createOpenAIStreamAccumulator()\n const idleTimeoutMs = state.streamIdleTimeout * 1000\n\n // Streaming metrics for TUI footer\n let bytesIn = 0\n let eventsIn = 0\n\n try {\n // Prepend truncation marker as first chunk if auto-truncate was performed (only in verbose mode)\n if (state.verbose && truncateResult?.wasTruncated) {\n const marker = createTruncationResponseMarkerOpenAI(truncateResult)\n const markerChunk: ChatCompletionChunk = {\n id: `truncation-marker-${Date.now()}`,\n object: \"chat.completion.chunk\",\n created: Math.floor(Date.now() / 1000),\n model: payload.model,\n choices: [\n {\n index: 0,\n delta: { content: marker },\n finish_reason: null,\n logprobs: null,\n },\n ],\n }\n await stream.writeSSE({\n data: JSON.stringify(markerChunk),\n event: \"message\",\n })\n acc.rawContent += marker\n }\n\n const iterator = response[Symbol.asyncIterator]()\n\n for (;;) {\n const abortSignal = combineAbortSignals(getShutdownSignal(), clientAbortSignal)\n const result = await raceIteratorNext(iterator.next(), { idleTimeoutMs, abortSignal })\n\n if (result === STREAM_ABORTED) break\n if (result.done) break\n\n const rawEvent = result.value\n\n bytesIn += rawEvent.data?.length ?? 0\n eventsIn++\n\n // Update TUI footer with streaming progress\n if (reqCtx.tuiLogId) {\n tuiLogger.updateRequest(reqCtx.tuiLogId, {\n streamBytesIn: bytesIn,\n streamEventsIn: eventsIn,\n })\n }\n\n // Parse and accumulate for history/tracking (skip [DONE] and empty data)\n if (rawEvent.data && rawEvent.data !== \"[DONE]\") {\n try {\n const chunk = JSON.parse(rawEvent.data) as ChatCompletionChunk\n accumulateOpenAIStreamEvent(chunk, acc)\n } catch {\n // Ignore parse errors\n }\n }\n\n // Forward every event to client — proxy preserves upstream data\n await stream.writeSSE({\n data: rawEvent.data ?? \"\",\n event: rawEvent.event,\n id: rawEvent.id !== undefined ? String(rawEvent.id) : undefined,\n retry: rawEvent.retry,\n })\n }\n\n const responseData = buildOpenAIResponseData(acc, payload.model)\n reqCtx.complete(responseData)\n } catch (error) {\n consola.error(\"[ChatCompletions] Stream error:\", error)\n reqCtx.fail(acc.model || payload.model, error)\n\n // Send error to client as final SSE event (consistent with Anthropic path)\n const errorMessage = error instanceof Error ? error.message : String(error)\n await stream.writeSSE({\n data: JSON.stringify({\n error: {\n message: errorMessage,\n type: error instanceof StreamIdleTimeoutError ? \"timeout_error\" : \"server_error\",\n },\n }),\n event: \"error\",\n })\n }\n}\n","import { Hono } from \"hono\"\n\nimport { forwardError } from \"~/lib/error\"\n\nimport { handleChatCompletion } from \"./handler\"\n\nexport const chatCompletionRoutes = new Hono()\n\nchatCompletionRoutes.post(\"/\", async (c) => {\n try {\n return await handleChatCompletion(c)\n } catch (error) {\n return forwardError(c, error)\n }\n})\n","/** Current effective runtime configuration (read-only, sanitized) */\n\nimport { Hono } from \"hono\"\n\nimport { state } from \"~/lib/state\"\n\nexport const configRoutes = new Hono()\n\nconfigRoutes.get(\"/\", (c) => {\n return c.json({\n // ─── General ───\n verbose: state.verbose,\n\n // ─── Anthropic pipeline ───\n autoTruncate: state.autoTruncate,\n compressToolResultsBeforeTruncate: state.compressToolResultsBeforeTruncate,\n stripServerTools: state.stripServerTools,\n immutableThinkingMessages: state.immutableThinkingMessages,\n dedupToolCalls: state.dedupToolCalls,\n contextEditingMode: state.contextEditingMode,\n rewriteSystemReminders: serializeRewriteSystemReminders(state.rewriteSystemReminders),\n stripReadToolResultTags: state.stripReadToolResultTags,\n systemPromptOverridesCount: state.systemPromptOverrides.length,\n\n // ─── OpenAI Responses ───\n normalizeResponsesCallIds: state.normalizeResponsesCallIds,\n\n // ─── Timeouts ───\n fetchTimeout: state.fetchTimeout,\n streamIdleTimeout: state.streamIdleTimeout,\n staleRequestMaxAge: state.staleRequestMaxAge,\n\n // ─── Shutdown ───\n shutdownGracefulWait: state.shutdownGracefulWait,\n shutdownAbortWait: state.shutdownAbortWait,\n\n // ─── History ───\n historyLimit: state.historyLimit,\n historyMinEntries: state.historyMinEntries,\n\n // ─── Model overrides ───\n modelOverrides: state.modelOverrides,\n\n // ─── Rate limiter (config snapshot, not live state) ───\n rateLimiter: state.adaptiveRateLimitConfig ?? null,\n })\n})\n\n/**\n * Serialize rewriteSystemReminders for API output.\n * CompiledRewriteRule contains RegExp objects which don't serialize well —\n * convert back to a human-readable form.\n */\nfunction serializeRewriteSystemReminders(\n value: typeof state.rewriteSystemReminders,\n): boolean | Array<{ from: string; to: string; method?: string; model?: string }> {\n if (typeof value === \"boolean\") return value\n return value.map((rule) => ({\n from: rule.from instanceof RegExp ? rule.from.source : rule.from,\n to: rule.to,\n ...(rule.method ? { method: rule.method } : {}),\n ...(rule.modelPattern ? { model: rule.modelPattern.source } : {}),\n }))\n}\n","import { copilotHeaders, copilotBaseUrl } from \"~/lib/copilot-api\"\nimport { HTTPError } from \"~/lib/error\"\nimport { createFetchSignal } from \"~/lib/fetch-utils\"\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 // Normalize input to array — some API providers reject bare string input\n const normalizedPayload = {\n ...payload,\n input: typeof payload.input === \"string\" ? [payload.input] : payload.input,\n }\n\n const response = await fetch(`${copilotBaseUrl(state)}/embeddings`, {\n method: \"POST\",\n headers: copilotHeaders(state),\n body: JSON.stringify(normalizedPayload),\n signal: createFetchSignal(),\n })\n\n if (!response.ok) throw await HTTPError.fromResponse(\"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 encoding_format?: \"float\" | \"base64\"\n dimensions?: number\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 { createEmbeddings, type EmbeddingRequest } from \"~/lib/openai/embeddings\"\n\nexport const embeddingsRoutes = new Hono()\n\nembeddingsRoutes.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 forwardError(c, error)\n }\n})\n","import { Hono } from \"hono\"\n\nexport const eventLoggingRoutes = new Hono()\n\n// Anthropic SDK sends telemetry to this endpoint\n// Return 200 OK to prevent errors in the SDK\neventLoggingRoutes.post(\"/batch\", (c) => {\n return c.text(\"OK\", 200)\n})\n","import type { Context } from \"hono\"\n\nimport {\n clearHistory,\n deleteSession,\n exportHistory,\n getEntry,\n getHistorySummaries,\n getSession,\n getSessionEntries,\n getSessions,\n getStats,\n isHistoryEnabled,\n type EndpointType,\n type QueryOptions,\n} from \"~/lib/history\"\n\nexport function handleGetEntries(c: Context) {\n if (!isHistoryEnabled()) {\n return c.json({ error: \"History recording is not enabled\" }, 400)\n }\n\n const query = c.req.query()\n const options: QueryOptions = {\n cursor: query.cursor || undefined,\n limit: query.limit ? Number.parseInt(query.limit, 10) : undefined,\n direction: (query.direction as \"older\" | \"newer\") || undefined,\n model: query.model || undefined,\n endpoint: query.endpoint as EndpointType | undefined,\n success: query.success ? query.success === \"true\" : undefined,\n from: query.from ? Number.parseInt(query.from, 10) : undefined,\n to: query.to ? Number.parseInt(query.to, 10) : undefined,\n search: query.search || undefined,\n sessionId: query.sessionId || undefined,\n }\n\n const result = getHistorySummaries(options)\n return c.json(result)\n}\n\nexport function handleGetEntry(c: Context) {\n if (!isHistoryEnabled()) {\n return c.json({ error: \"History recording is not enabled\" }, 400)\n }\n\n const id = c.req.param(\"id\")\n const entry = getEntry(id)\n\n if (!entry) {\n return c.json({ error: \"Entry not found\" }, 404)\n }\n\n return c.json(entry)\n}\n\nexport function handleDeleteEntries(c: Context) {\n if (!isHistoryEnabled()) {\n return c.json({ error: \"History recording is not enabled\" }, 400)\n }\n\n clearHistory()\n return c.json({ success: true, message: \"History cleared\" })\n}\n\nexport function handleGetStats(c: Context) {\n if (!isHistoryEnabled()) {\n return c.json({ error: \"History recording is not enabled\" }, 400)\n }\n\n const stats = getStats()\n return c.json(stats)\n}\n\nexport function handleExport(c: Context) {\n if (!isHistoryEnabled()) {\n return c.json({ error: \"History recording is not enabled\" }, 400)\n }\n\n const format = (c.req.query(\"format\") || \"json\") as \"json\" | \"csv\"\n const data = exportHistory(format)\n\n if (format === \"csv\") {\n c.header(\"Content-Type\", \"text/csv\")\n c.header(\"Content-Disposition\", \"attachment; filename=history.csv\")\n } else {\n c.header(\"Content-Type\", \"application/json\")\n c.header(\"Content-Disposition\", \"attachment; filename=history.json\")\n }\n\n return c.body(data)\n}\n\n/** Session management endpoints */\nexport function handleGetSessions(c: Context) {\n if (!isHistoryEnabled()) {\n return c.json({ error: \"History recording is not enabled\" }, 400)\n }\n\n const result = getSessions()\n return c.json(result)\n}\n\nexport function handleGetSession(c: Context) {\n if (!isHistoryEnabled()) {\n return c.json({ error: \"History recording is not enabled\" }, 400)\n }\n\n const id = c.req.param(\"id\")\n const session = getSession(id)\n\n if (!session) {\n return c.json({ error: \"Session not found\" }, 404)\n }\n\n // Include paginated entries in the session response\n const query = c.req.query()\n const result = getSessionEntries(id, {\n cursor: query.cursor || undefined,\n limit: query.limit ? Number.parseInt(query.limit, 10) : undefined,\n })\n\n return c.json({\n ...session,\n ...result,\n })\n}\n\nexport function handleDeleteSession(c: Context) {\n if (!isHistoryEnabled()) {\n return c.json({ error: \"History recording is not enabled\" }, 400)\n }\n\n const id = c.req.param(\"id\")\n const success = deleteSession(id)\n\n if (!success) {\n return c.json({ error: \"Session not found\" }, 404)\n }\n\n return c.json({ success: true, message: \"Session deleted\" })\n}\n","import { Hono } from \"hono\"\n\nimport {\n handleDeleteEntries,\n handleDeleteSession,\n handleExport,\n handleGetEntries,\n handleGetEntry,\n handleGetSession,\n handleGetSessions,\n handleGetStats,\n} from \"./handler\"\n\nexport const historyRoutes = new Hono()\n\nhistoryRoutes.all(\"/\", (c) => c.json({ error: \"Not Found\" }, 404))\n\n/** API endpoints */\nhistoryRoutes.get(\"/api/entries\", handleGetEntries)\nhistoryRoutes.get(\"/api/entries/:id\", handleGetEntry)\nhistoryRoutes.delete(\"/api/entries\", handleDeleteEntries)\nhistoryRoutes.get(\"/api/stats\", handleGetStats)\nhistoryRoutes.get(\"/api/export\", handleExport)\n\n/** Session endpoints */\nhistoryRoutes.get(\"/api/sessions\", handleGetSessions)\nhistoryRoutes.get(\"/api/sessions/:id\", handleGetSession)\nhistoryRoutes.delete(\"/api/sessions/:id\", handleDeleteSession)\n","/**\n * Live log endpoint — recent EntrySummary snapshot for the log viewer page.\n *\n * Returns the most recent entries (newest first, capped at `limit`).\n * After initial load, the web client subscribes to /ws\n * WebSocket for real-time `entry_added` / `entry_updated` events.\n */\n\nimport { Hono } from \"hono\"\n\nimport { getHistorySummaries, isHistoryEnabled } from \"~/lib/history\"\n\nexport const logsRoutes = new Hono()\n\nlogsRoutes.get(\"/\", (c) => {\n if (!isHistoryEnabled()) {\n return c.json({ error: \"History recording is not enabled\" }, 400)\n }\n\n const limit = Math.min(Number(c.req.query(\"limit\")) || 100, 500)\n const result = getHistorySummaries({ limit })\n\n return c.json({\n entries: result.entries,\n total: result.total,\n })\n})\n","/**\n * Anthropic API Types\n *\n * Content block types, stream events, and response types are imported from\n * the `@anthropic-ai/sdk`. Request payload and tool types remain our own\n * definitions since Copilot proxies arbitrary model names (not SDK's literal\n * union) and adds extensions (context_management, copilot_annotations).\n */\n\n// ============================================================================\n// Re-export SDK types\n// ============================================================================\n\n// Response content blocks\nexport type {\n ContentBlock,\n RedactedThinkingBlock,\n ServerToolUseBlock,\n TextBlock,\n ThinkingBlock,\n ToolUseBlock,\n WebSearchToolResultBlock,\n} from \"@anthropic-ai/sdk/resources/messages\"\n\n// Request content blocks\nexport type {\n ContentBlockParam,\n ImageBlockParam,\n TextBlockParam,\n ToolResultBlockParam,\n ToolUseBlockParam,\n} from \"@anthropic-ai/sdk/resources/messages\"\n\n// Messages\nexport type { Message, MessageParam } from \"@anthropic-ai/sdk/resources/messages\"\n\n// Thinking & cache\nexport type { CacheControlEphemeral, ThinkingConfigParam } from \"@anthropic-ai/sdk/resources/messages\"\n\n// Stream events\nexport type {\n RawContentBlockDelta,\n RawContentBlockStartEvent,\n RawContentBlockStopEvent,\n RawMessageDeltaEvent,\n RawMessageStartEvent,\n RawMessageStopEvent,\n} from \"@anthropic-ai/sdk/resources/messages\"\n\n// Internal-only SDK imports (not re-exported)\nimport type {\n ContentBlock,\n ContentBlockParam,\n TextBlockParam,\n MessageParam,\n ThinkingConfigParam,\n CacheControlEphemeral,\n WebSearchToolResultBlock,\n ToolResultBlockParam,\n RawContentBlockDeltaEvent,\n RawMessageStartEvent,\n RawMessageStopEvent,\n RawMessageDeltaEvent,\n RawContentBlockStartEvent,\n RawContentBlockStopEvent,\n} from \"@anthropic-ai/sdk/resources/messages\"\n\n// ============================================================================\n// Request payload (our own — SDK uses Model literal union, we proxy strings)\n// ============================================================================\n\nexport interface MessagesPayload {\n model: string\n max_tokens: number\n messages: Array<MessageParam>\n system?: string | Array<TextBlockParam>\n temperature?: number\n top_p?: number\n top_k?: number\n stop_sequences?: Array<string>\n stream?: boolean\n tools?: Array<Tool>\n tool_choice?: ToolChoice\n thinking?: ThinkingConfigParam\n metadata?: { user_id?: string }\n /** `null` is an internal sentinel meaning \"do not auto-inject context_management on retry\". */\n context_management?: Record<string, unknown> | null\n}\n\nexport interface Tool {\n name: string\n description?: string\n input_schema?: Record<string, unknown>\n cache_control?: CacheControlEphemeral\n type?: string\n defer_loading?: boolean\n}\n\nexport type ToolChoice = { type: \"auto\" } | { type: \"any\" } | { type: \"none\" } | { type: \"tool\"; name: string }\n\n// ============================================================================\n// Message subtypes (narrow role for cast convenience)\n// ============================================================================\n\nexport interface UserMessage {\n role: \"user\"\n content: string | Array<ContentBlockParam>\n}\n\nexport interface AssistantMessage {\n role: \"assistant\"\n content: string | Array<ContentBlockParam>\n}\n\n// ============================================================================\n// Copilot Extensions (not part of the Anthropic API)\n// ============================================================================\n\nexport interface CopilotIPCodeCitation {\n url: string\n license: string\n repository: string\n start_line: number\n end_line: number\n}\n\n/** Copilot-specific annotations attached to SSE content block deltas */\nexport interface CopilotAnnotations {\n ip_code_citations?: Array<CopilotIPCodeCitation>\n}\n\n/** Content block delta event with Copilot annotations extension */\ntype CopilotContentBlockDeltaEvent = RawContentBlockDeltaEvent & {\n copilot_annotations?: CopilotAnnotations\n}\n\nexport interface StreamPingEvent {\n type: \"ping\"\n}\n\nexport interface StreamErrorEvent {\n type: \"error\"\n error: { type: string; message: string }\n}\n\n/** Stream event union — replaces SDK's delta event with our Copilot-extended version */\nexport type StreamEvent =\n | RawMessageStartEvent\n | RawMessageStopEvent\n | RawMessageDeltaEvent\n | RawContentBlockStartEvent\n | RawContentBlockStopEvent\n | CopilotContentBlockDeltaEvent\n | StreamPingEvent\n | StreamErrorEvent\n\n// ============================================================================\n// Type guards\n// ============================================================================\n\n/** Type guard for ToolResultBlockParam */\nexport function isToolResultBlock(block: ContentBlockParam): block is ToolResultBlockParam {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- type guard pattern requires cast\n return (block as ToolResultBlockParam).type === \"tool_result\"\n}\n\n/** Type guard for server-side tool result blocks (web_search, tool_search, code_execution, etc.) */\nexport function isServerToolResultBlock(block: ContentBlockParam | ContentBlock): block is WebSearchToolResultBlock {\n // Cast to string to allow matching beyond the SDK's narrow literal type union.\n // Server tool results include: web_search_tool_result, tool_search_tool_result,\n // code_execution_tool_result, etc. They all end with \"_tool_result\" and carry a tool_use_id.\n // Exclude plain \"tool_result\" which is the standard user-side tool result.\n const type = (block as unknown as Record<string, unknown>).type as string | undefined\n if (!type) return false\n return type !== \"tool_result\" && type.endsWith(\"_tool_result\") && \"tool_use_id\" in block\n}\n","import type { MessageParam } from \"~/types/api/anthropic\"\n\nimport { state } from \"~/lib/state\"\n\n/**\n * Whether an assistant message contains signature-bound thinking content.\n *\n * Anthropic returns `thinking` / `redacted_thinking` blocks in assistant\n * messages. These blocks may need stronger preservation guarantees depending\n * on the configured rewrite policy.\n */\nexport function hasThinkingSignatureBlocks(msg: MessageParam): boolean {\n return (\n msg.role === \"assistant\"\n && Array.isArray(msg.content)\n && msg.content.some((block) => block.type === \"thinking\" || block.type === \"redacted_thinking\")\n )\n}\n\n/**\n * Strong preservation mode for assistant messages that contain thinking blocks.\n *\n * When enabled, the entire assistant message is treated as immutable by\n * client-side rewrite passes.\n */\nexport function isImmutableThinkingAssistantMessage(msg: MessageParam): boolean {\n return state.immutableThinkingMessages && hasThinkingSignatureBlocks(msg)\n}\n","import consola from \"consola\"\n\nimport type { MessageParam } from \"~/types/api/anthropic\"\n\nimport { isServerToolResultBlock, isToolResultBlock } from \"~/types/api/anthropic\"\n\nimport { isImmutableThinkingAssistantMessage } from \"../thinking-immutability\"\n\n/**\n * Get tool_use IDs from an Anthropic assistant message.\n */\nexport function getAnthropicToolUseIds(msg: MessageParam): Array<string> {\n if (msg.role !== \"assistant\") return []\n if (typeof msg.content === \"string\") return []\n\n const ids: Array<string> = []\n for (const block of msg.content) {\n if ((block.type === \"tool_use\" || block.type === \"server_tool_use\") && block.id) {\n ids.push(block.id)\n }\n }\n return ids\n}\n\n/**\n * Get tool_result IDs from an Anthropic message.\n * Checks both user messages (regular tool_result) and assistant messages\n * (server tool results like tool_search_tool_result which appear inline).\n */\nexport function getAnthropicToolResultIds(msg: MessageParam): Array<string> {\n if (typeof msg.content === \"string\") return []\n\n const ids: Array<string> = []\n for (const block of msg.content) {\n if (isToolResultBlock(block)) {\n ids.push(block.tool_use_id)\n } else if (isServerToolResultBlock(block)) {\n ids.push(block.tool_use_id)\n }\n }\n return ids\n}\n\n/**\n * Ensure Anthropic messages start with a user message.\n * Drops leading non-user messages (e.g., orphaned assistant messages after truncation).\n */\nexport function ensureAnthropicStartsWithUser(messages: Array<MessageParam>): Array<MessageParam> {\n let startIndex = 0\n while (startIndex < messages.length && messages[startIndex].role !== \"user\") {\n startIndex++\n }\n\n if (startIndex > 0) {\n consola.debug(`[AutoTruncate:Anthropic] Skipped ${startIndex} leading non-user messages`)\n }\n\n return messages.slice(startIndex)\n}\n\n/**\n * Filter orphaned tool_result blocks (no matching tool_use).\n */\nexport function filterAnthropicOrphanedToolResults(messages: Array<MessageParam>): Array<MessageParam> {\n const toolUseIds = new Set<string>()\n for (const msg of messages) {\n for (const id of getAnthropicToolUseIds(msg)) {\n toolUseIds.add(id)\n }\n }\n\n let removed = 0\n const result: Array<MessageParam> = []\n\n for (const msg of messages) {\n if (typeof msg.content === \"string\") {\n result.push(msg)\n continue\n }\n\n const filtered = msg.content.filter((block) => {\n if (isToolResultBlock(block) && !toolUseIds.has(block.tool_use_id)) {\n removed++\n return false\n }\n if (isServerToolResultBlock(block) && !toolUseIds.has(block.tool_use_id)) {\n removed++\n return false\n }\n return true\n })\n\n if (filtered.length === 0) continue\n if (filtered.length === msg.content.length) {\n result.push(msg)\n } else {\n result.push({ ...msg, content: filtered } as MessageParam)\n }\n }\n\n if (removed > 0) {\n consola.debug(`[AutoTruncate:Anthropic] Filtered ${removed} orphaned tool results`)\n }\n\n return result\n}\n\n/**\n * Filter orphaned tool_use blocks (no matching tool_result).\n */\nexport function filterAnthropicOrphanedToolUse(messages: Array<MessageParam>): Array<MessageParam> {\n const toolResultIds = new Set<string>()\n for (const msg of messages) {\n for (const id of getAnthropicToolResultIds(msg)) {\n toolResultIds.add(id)\n }\n }\n\n let removed = 0\n const result: Array<MessageParam> = []\n\n for (const msg of messages) {\n if (msg.role !== \"assistant\" || typeof msg.content === \"string\") {\n result.push(msg)\n continue\n }\n\n if (isImmutableThinkingAssistantMessage(msg)) {\n result.push(msg)\n continue\n }\n\n const survivingIds = new Set<string>()\n for (const block of msg.content) {\n if ((block.type === \"tool_use\" || block.type === \"server_tool_use\") && toolResultIds.has(block.id)) {\n survivingIds.add(block.id)\n }\n }\n\n const filtered = msg.content.filter((block) => {\n if ((block.type === \"tool_use\" || block.type === \"server_tool_use\") && !toolResultIds.has(block.id)) {\n removed++\n return false\n }\n if (isServerToolResultBlock(block) && !survivingIds.has(block.tool_use_id)) {\n removed++\n return false\n }\n return true\n })\n\n if (filtered.length === 0) continue\n if (filtered.length === msg.content.length) {\n result.push(msg)\n } else {\n result.push({ ...msg, content: filtered } as MessageParam)\n }\n }\n\n if (removed > 0) {\n consola.debug(`[AutoTruncate:Anthropic] Filtered ${removed} orphaned tool blocks`)\n }\n\n return result\n}\n","import type { Model } from \"~/lib/models/client\"\nimport type { MessageParam, MessagesPayload } from \"~/types/api/anthropic\"\n\nimport { countTextTokens } from \"~/lib/models/tokenizer\"\n\n/**\n * Convert Anthropic message content to text for token counting.\n * @param options.includeThinking Whether to include thinking blocks (default: true)\n */\nexport function contentToText(content: MessageParam[\"content\"], options?: { includeThinking?: boolean }): string {\n if (typeof content === \"string\") {\n return content\n }\n\n const includeThinking = options?.includeThinking ?? true\n const parts: Array<string> = []\n for (const block of content) {\n switch (block.type) {\n case \"text\": {\n parts.push(block.text)\n break\n }\n case \"tool_use\": {\n parts.push(`[tool_use: ${block.name}]`, JSON.stringify(block.input))\n break\n }\n case \"tool_result\": {\n if (typeof block.content === \"string\") {\n parts.push(block.content)\n } else if (Array.isArray(block.content)) {\n for (const inner of block.content) {\n if (inner.type === \"text\") {\n parts.push(inner.text)\n }\n }\n }\n break\n }\n case \"thinking\": {\n if (includeThinking) {\n parts.push(block.thinking)\n }\n break\n }\n case \"redacted_thinking\": {\n break\n }\n case \"server_tool_use\": {\n parts.push(`[server_tool_use: ${block.name}]`, JSON.stringify(block.input))\n break\n }\n default: {\n const genericBlock = block as unknown as Record<string, unknown>\n if (\"tool_use_id\" in genericBlock && genericBlock.type !== \"image\") {\n parts.push(`[${String(genericBlock.type)}]`)\n break\n }\n break\n }\n }\n }\n\n return parts.join(\"\\n\")\n}\n\n/**\n * Estimate tokens for a message (fast, synchronous).\n * Uses ~4 chars per token approximation for internal calculations.\n */\nexport function estimateMessageTokens(msg: MessageParam): number {\n const text = contentToText(msg.content)\n return Math.ceil(text.length / 4) + 4\n}\n\n/**\n * Count tokens for an Anthropic message using the model's tokenizer.\n */\nexport async function countMessageTokens(\n msg: MessageParam,\n model: Model,\n options?: { includeThinking?: boolean },\n): Promise<number> {\n const text = contentToText(msg.content, options)\n return (await countTextTokens(text, model)) + 4\n}\n\n/**\n * Count tokens for system prompt.\n */\nexport async function countSystemTokens(system: MessagesPayload[\"system\"], model: Model): Promise<number> {\n if (!system) return 0\n if (typeof system === \"string\") {\n return (await countTextTokens(system, model)) + 4\n }\n const text = system.map((block) => block.text).join(\"\\n\")\n return (await countTextTokens(text, model)) + 4\n}\n\n/**\n * Count tokens for just the messages array.\n * Used internally to avoid re-counting system/tools tokens that don't change.\n */\nexport async function countMessagesTokens(messages: Array<MessageParam>, model: Model): Promise<number> {\n let total = 0\n for (const msg of messages) {\n total += await countMessageTokens(msg, model)\n }\n return total\n}\n\n/**\n * Count tokens for system + tools (the parts that don't change during truncation).\n * Returns the combined fixed overhead token count.\n */\nexport async function countFixedTokens(payload: MessagesPayload, model: Model): Promise<number> {\n let total = await countSystemTokens(payload.system, model)\n if (payload.tools) {\n const toolsText = JSON.stringify(payload.tools)\n total += await countTextTokens(toolsText, model)\n }\n return total\n}\n\n/**\n * Count total tokens for the payload using the model's tokenizer.\n * Includes thinking blocks — used by auto-truncate decisions.\n */\nexport async function countTotalTokens(payload: MessagesPayload, model: Model): Promise<number> {\n const fixed = await countFixedTokens(payload, model)\n const messages = await countMessagesTokens(payload.messages, model)\n return fixed + messages\n}\n\n/**\n * Count total input tokens for the payload, excluding thinking blocks\n * from assistant messages per Anthropic token counting spec.\n */\nexport async function countTotalInputTokens(payload: MessagesPayload, model: Model): Promise<number> {\n let total = await countSystemTokens(payload.system, model)\n for (const msg of payload.messages) {\n const skipThinking = msg.role === \"assistant\"\n total += await countMessageTokens(msg, model, {\n includeThinking: !skipThinking,\n })\n }\n if (payload.tools) {\n const toolsText = JSON.stringify(payload.tools)\n total += await countTextTokens(toolsText, model)\n }\n return total\n}\n","import type { MessageParam, MessagesPayload } from \"~/types/api/anthropic\"\n\nimport { hasThinkingSignatureBlocks } from \"../thinking-immutability\"\n\n/**\n * Final pass: remove any empty/whitespace-only text content blocks from Anthropic messages.\n * This is a safety net that catches empty blocks regardless of how they were produced.\n */\nexport function filterEmptyAnthropicTextBlocks(messages: Array<MessageParam>): Array<MessageParam> {\n return messages.map((msg) => {\n if (typeof msg.content === \"string\") return msg\n\n if (msg.role === \"assistant\" && hasThinkingSignatureBlocks(msg)) {\n return msg\n }\n\n const filtered = msg.content.filter((block) => {\n if (block.type === \"text\" && \"text\" in block) {\n return block.text.trim() !== \"\"\n }\n return true\n })\n\n if (filtered.length === msg.content.length) return msg\n return { ...msg, content: filtered } as MessageParam\n })\n}\n\n/**\n * Final pass: remove any empty/whitespace-only text blocks from Anthropic system prompt.\n */\nexport function filterEmptySystemTextBlocks(system: MessagesPayload[\"system\"]): MessagesPayload[\"system\"] {\n if (!system || typeof system === \"string\") return system\n return system.filter((block) => block.text.trim() !== \"\")\n}\n\n/**\n * Count total content blocks in Anthropic messages.\n */\nexport function countAnthropicContentBlocks(messages: Array<MessageParam>): number {\n let count = 0\n for (const msg of messages) {\n count += typeof msg.content === \"string\" ? 1 : msg.content.length\n }\n return count\n}\n","import type { MessageParam } from \"~/types/api/anthropic\"\n\nimport { hasThinkingSignatureBlocks, isImmutableThinkingAssistantMessage } from \"../thinking-immutability\"\n\n/**\n * Remove duplicate tool_use/tool_result pairs, keeping only the last occurrence\n * of each matching combination.\n */\nexport function deduplicateToolCalls(\n messages: Array<MessageParam>,\n mode: \"input\" | \"result\" = \"input\",\n): {\n messages: Array<MessageParam>\n dedupedCount: number\n dedupedByTool: Record<string, number>\n} {\n const toolUseKeys = new Map<string, string>()\n\n for (const msg of messages) {\n if (msg.role !== \"assistant\" || typeof msg.content === \"string\") continue\n for (const block of msg.content) {\n if (block.type === \"tool_use\") {\n const key = `${block.name}:${JSON.stringify(block.input)}`\n toolUseKeys.set(block.id, key)\n }\n }\n }\n\n if (mode === \"result\") {\n const resultContentById = new Map<string, string>()\n for (const msg of messages) {\n if (msg.role !== \"user\" || typeof msg.content === \"string\") continue\n for (const block of msg.content) {\n if (block.type === \"tool_result\" && toolUseKeys.has(block.tool_use_id)) {\n const resultStr = typeof block.content === \"string\" ? block.content : JSON.stringify(block.content)\n resultContentById.set(block.tool_use_id, resultStr)\n }\n }\n }\n\n for (const [id, baseKey] of toolUseKeys) {\n const resultContent = resultContentById.get(id)\n if (resultContent !== undefined) {\n toolUseKeys.set(id, `${baseKey}::${resultContent}`)\n }\n }\n }\n\n const keeperIds = new Set<string>()\n const seenKeys = new Set<string>()\n\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]\n if (msg.role !== \"assistant\" || typeof msg.content === \"string\") continue\n for (let j = msg.content.length - 1; j >= 0; j--) {\n const block = msg.content[j]\n if (block.type === \"tool_use\") {\n const key = toolUseKeys.get(block.id)\n if (!key) continue\n if (!seenKeys.has(key)) {\n seenKeys.add(key)\n keeperIds.add(block.id)\n }\n }\n }\n }\n\n const protectedIds = new Set<string>()\n for (const msg of messages) {\n if (msg.role !== \"assistant\" || typeof msg.content === \"string\") continue\n if (!hasThinkingSignatureBlocks(msg)) continue\n for (const block of msg.content) {\n if (block.type === \"tool_use\") {\n protectedIds.add(block.id)\n }\n }\n }\n\n const removedIds = new Set<string>()\n for (const [id, key] of toolUseKeys) {\n if (seenKeys.has(key) && !keeperIds.has(id) && !protectedIds.has(id)) {\n removedIds.add(id)\n }\n }\n\n if (removedIds.size === 0) {\n return { messages, dedupedCount: 0, dedupedByTool: {} }\n }\n\n const dedupedByTool: Record<string, number> = {}\n for (const id of removedIds) {\n const key = toolUseKeys.get(id)\n if (key) {\n const toolName = key.slice(0, key.indexOf(\":\"))\n dedupedByTool[toolName] = (dedupedByTool[toolName] ?? 0) + 1\n }\n }\n\n const filtered: Array<MessageParam> = []\n for (const msg of messages) {\n if (typeof msg.content === \"string\") {\n filtered.push(msg)\n continue\n }\n\n if (msg.role === \"assistant\") {\n const newContent = msg.content.filter((block) => block.type !== \"tool_use\" || !removedIds.has(block.id))\n if (newContent.length > 0) {\n if (newContent.length === msg.content.length) {\n filtered.push(msg)\n } else {\n filtered.push({ ...msg, content: newContent } as MessageParam)\n }\n }\n } else {\n const newContent = msg.content.filter(\n (block) => block.type !== \"tool_result\" || !removedIds.has(block.tool_use_id),\n )\n if (newContent.length > 0) {\n if (newContent.length === msg.content.length) {\n filtered.push(msg)\n } else {\n filtered.push({ ...msg, content: newContent } as MessageParam)\n }\n }\n }\n }\n\n const merged: Array<MessageParam> = []\n for (const msg of filtered) {\n const prev = merged.at(-1)\n if (prev && prev.role === msg.role) {\n if (prev.role === \"assistant\" && (isImmutableThinkingAssistantMessage(prev) || isImmutableThinkingAssistantMessage(msg))) {\n merged.push(msg)\n continue\n }\n\n const prevContent =\n typeof prev.content === \"string\" ? [{ type: \"text\" as const, text: prev.content }] : prev.content\n const currContent = typeof msg.content === \"string\" ? [{ type: \"text\" as const, text: msg.content }] : msg.content\n merged[merged.length - 1] = {\n ...prev,\n content: [...prevContent, ...currContent],\n } as MessageParam\n } else {\n merged.push(msg)\n }\n }\n\n return { messages: merged, dedupedCount: removedIds.size, dedupedByTool }\n}\n","import type { ContentBlockParam, MessageParam, UserMessage } from \"~/types/api/anthropic\"\n\nimport { extractLeadingSystemReminderTags, extractTrailingSystemReminderTags } from \"~/lib/system-prompt\"\n\n/**\n * Strip ALL `<system-reminder>` tags from Read tool results.\n */\nexport function stripReadToolResultTags(messages: Array<MessageParam>): {\n messages: Array<MessageParam>\n strippedCount: number\n tagPreviews: Array<string>\n} {\n const readToolUseIds = new Set<string>()\n for (const msg of messages) {\n if (msg.role !== \"assistant\" || typeof msg.content === \"string\") continue\n for (const block of msg.content) {\n if (block.type === \"tool_use\" && block.name === \"Read\") {\n readToolUseIds.add(block.id)\n }\n }\n }\n\n if (readToolUseIds.size === 0) {\n return { messages, strippedCount: 0, tagPreviews: [] }\n }\n\n let strippedCount = 0\n const allPreviews: Array<string> = []\n const result = messages.map((msg) => {\n if (msg.role !== \"user\" || typeof msg.content === \"string\") return msg\n\n let modified = false\n const newContent = msg.content.map((block) => {\n if (block.type !== \"tool_result\" || !readToolUseIds.has(block.tool_use_id)) {\n return block\n }\n\n const stripped = stripAllSystemReminderTags(block.content as string | Array<{ type: string; text?: string }>)\n if (stripped.modified) {\n modified = true\n strippedCount += stripped.tagCount\n allPreviews.push(...stripped.tagPreviews)\n return { ...block, content: stripped.content } as ContentBlockParam\n }\n return block\n })\n\n return modified ? ({ ...msg, content: newContent } as UserMessage) : msg\n })\n\n return { messages: strippedCount > 0 ? result : messages, strippedCount, tagPreviews: allPreviews }\n}\n\n/**\n * Strip ALL system-reminder tags from tool_result content (string or array form).\n * Returns the cleaned content and whether anything was modified.\n */\nfunction stripAllSystemReminderTags(content: string | Array<{ type: string; text?: string }>): {\n content: typeof content\n modified: boolean\n tagCount: number\n tagPreviews: Array<string>\n} {\n if (typeof content === \"string\") {\n return stripAllTagsFromString(content)\n }\n\n let totalTagCount = 0\n const allPreviews: Array<string> = []\n let modified = false\n const result = content.map((block) => {\n if (block.type !== \"text\" || !block.text) return block\n const stripped = stripAllTagsFromString(block.text)\n if (stripped.modified) {\n modified = true\n totalTagCount += stripped.tagCount\n allPreviews.push(...stripped.tagPreviews)\n return { ...block, text: stripped.content }\n }\n return block\n })\n\n return { content: modified ? result : content, modified, tagCount: totalTagCount, tagPreviews: allPreviews }\n}\n\n/**\n * Remove ALL system-reminder tags from a string, keeping only the main content.\n */\nfunction stripAllTagsFromString(text: string): {\n content: string\n modified: boolean\n tagCount: number\n tagPreviews: Array<string>\n} {\n let tagCount = 0\n const tagPreviews: Array<string> = []\n\n const trailing = extractTrailingSystemReminderTags(text)\n tagCount += trailing.tags.length\n for (const tag of trailing.tags) {\n tagPreviews.push(tag.content.slice(0, 80))\n }\n\n const mainSlice = text.slice(0, trailing.mainContentEnd)\n const leading = extractLeadingSystemReminderTags(mainSlice)\n tagCount += leading.tags.length\n for (const tag of leading.tags) {\n tagPreviews.push(tag.content.slice(0, 80))\n }\n\n if (tagCount === 0) {\n return { content: text, modified: false, tagCount: 0, tagPreviews: [] }\n }\n\n const content = mainSlice.slice(leading.mainContentStart)\n return { content, modified: true, tagCount, tagPreviews }\n}\n","import consola from \"consola\"\n\nimport type { SanitizeResult } from \"~/lib/request/pipeline\"\nimport type { MessagesPayload } from \"~/types/api/anthropic\"\n\nimport {\n countAnthropicContentBlocks,\n filterEmptyAnthropicTextBlocks,\n filterEmptySystemTextBlocks,\n} from \"./content-blocks\"\n\nexport interface SanitizationStats {\n orphanedToolUseCount: number\n orphanedToolResultCount: number\n fixedNameCount: number\n emptyTextBlocksRemoved: number\n systemReminderRemovals: number\n totalBlocksRemoved: number\n}\n\nexport function finalizeAnthropicSanitization(\n payload: MessagesPayload,\n messages: MessagesPayload[\"messages\"],\n system: MessagesPayload[\"system\"],\n originalBlockCount: number,\n toolStats: Pick<SanitizationStats, \"fixedNameCount\" | \"orphanedToolUseCount\" | \"orphanedToolResultCount\">,\n systemReminderRemovals: number,\n): SanitizeResult<MessagesPayload> & { stats: SanitizationStats } {\n const finalMessages = filterEmptyAnthropicTextBlocks(messages)\n const finalSystem = filterEmptySystemTextBlocks(system)\n const totalBlocksRemoved = Math.max(0, originalBlockCount - countAnthropicContentBlocks(finalMessages))\n const emptyTextBlocksRemoved = Math.max(\n 0,\n totalBlocksRemoved - toolStats.orphanedToolUseCount - toolStats.orphanedToolResultCount,\n )\n\n if (toolStats.fixedNameCount > 0) {\n consola.debug(`[Sanitizer:Anthropic] Fixed ${toolStats.fixedNameCount} tool name casing mismatches`)\n }\n\n if (totalBlocksRemoved > 0 && (toolStats.orphanedToolUseCount > 0 || toolStats.orphanedToolResultCount > 0)) {\n const parts: Array<string> = []\n if (toolStats.orphanedToolUseCount > 0) parts.push(`${toolStats.orphanedToolUseCount} orphaned tool_use`)\n if (toolStats.orphanedToolResultCount > 0) parts.push(`${toolStats.orphanedToolResultCount} orphaned tool_result`)\n if (emptyTextBlocksRemoved > 0) parts.push(`${emptyTextBlocksRemoved} empty text blocks`)\n consola.info(`[Sanitizer:Anthropic] Removed ${totalBlocksRemoved} content blocks (${parts.join(\", \")})`)\n }\n\n return {\n payload: { ...payload, system: finalSystem, messages: finalMessages },\n blocksRemoved: totalBlocksRemoved,\n systemReminderRemovals,\n stats: {\n ...toolStats,\n emptyTextBlocksRemoved,\n systemReminderRemovals,\n totalBlocksRemoved,\n },\n }\n}\n","import { removeSystemReminderTags } from \"~/lib/system-prompt\"\n\n/**\n * Remove system-reminder tags from text blocks in an array.\n * Drops blocks whose text becomes empty after sanitization.\n * Returns the original array reference if nothing changed.\n */\nexport function sanitizeTextBlocksInArray<T extends { type: string }>(\n blocks: Array<T>,\n getText: (block: T) => string | undefined,\n setText: (block: T, text: string) => T,\n): { blocks: Array<T>; modified: boolean } {\n let modified = false\n const result: Array<T> = []\n\n for (const block of blocks) {\n const text = getText(block)\n if (text !== undefined) {\n const sanitized = removeSystemReminderTags(text)\n if (sanitized !== text) {\n modified = true\n if (sanitized) {\n result.push(setText(block, sanitized))\n }\n continue\n }\n }\n result.push(block)\n }\n\n return { blocks: modified ? result : blocks, modified }\n}\n","import type {\n AssistantMessage,\n ContentBlock,\n ContentBlockParam,\n MessageParam,\n UserMessage,\n} from \"~/types/api/anthropic\"\n\nimport { removeSystemReminderTags } from \"~/lib/system-prompt\"\n\nimport { sanitizeTextBlocksInArray } from \"./text-blocks\"\nimport { isImmutableThinkingAssistantMessage } from \"../thinking-immutability\"\n\n/**\n * Sanitize tool_result content (can be string or array of text/image blocks).\n * Returns the sanitized content and whether it was modified.\n */\nfunction sanitizeToolResultContent(\n content: string | Array<{ type: \"text\"; text: string } | { type: \"image\"; source: unknown }>,\n): { content: typeof content; modified: boolean } {\n if (typeof content === \"string\") {\n const sanitized = removeSystemReminderTags(content)\n if (!sanitized && sanitized !== content) {\n return { content, modified: false }\n }\n return { content: sanitized, modified: sanitized !== content }\n }\n\n const { blocks, modified } = sanitizeTextBlocksInArray(\n content,\n (block) => (block.type === \"text\" ? block.text : undefined),\n (block, text) => ({ ...block, text }),\n )\n return { content: modified ? blocks : content, modified }\n}\n\n/**\n * Remove system-reminder tags from Anthropic message content.\n */\nfunction sanitizeMessageParamContent(msg: MessageParam): MessageParam {\n if (typeof msg.content === \"string\") {\n const sanitized = removeSystemReminderTags(msg.content)\n if (sanitized !== msg.content) {\n return sanitized ? { ...msg, content: sanitized } : msg\n }\n return msg\n }\n\n if (msg.role === \"user\") {\n let modified = false\n const blocks: Array<ContentBlockParam> = []\n\n for (const block of msg.content) {\n if (block.type === \"text\" && typeof block.text === \"string\") {\n const sanitized = removeSystemReminderTags(block.text)\n if (sanitized !== block.text) {\n modified = true\n if (sanitized) blocks.push({ ...block, text: sanitized })\n continue\n }\n } else if (block.type === \"tool_result\" && block.content) {\n const sanitizedResult = sanitizeToolResultContent(\n block.content as Parameters<typeof sanitizeToolResultContent>[0],\n )\n if (sanitizedResult.modified) {\n modified = true\n blocks.push({ ...block, content: sanitizedResult.content } as ContentBlockParam)\n continue\n }\n }\n blocks.push(block)\n }\n\n return modified ? ({ role: \"user\", content: blocks } as UserMessage) : msg\n }\n\n if (isImmutableThinkingAssistantMessage(msg)) {\n return msg\n }\n\n const { blocks, modified } = sanitizeTextBlocksInArray(\n msg.content,\n (block) => (block.type === \"text\" && \"text\" in block ? (block as { text: string }).text : undefined),\n (block, text) => ({ ...block, text }) as ContentBlock,\n )\n return modified ? ({ role: \"assistant\", content: blocks } as AssistantMessage) : msg\n}\n\n/**\n * Remove system-reminder tags from all Anthropic messages.\n */\nexport function removeAnthropicSystemReminders(messages: Array<MessageParam>): {\n messages: Array<MessageParam>\n modifiedCount: number\n} {\n let modifiedCount = 0\n const result = messages.map((msg) => {\n const sanitized = sanitizeMessageParamContent(msg)\n if (sanitized !== msg) modifiedCount++\n return sanitized\n })\n return { messages: modifiedCount === 0 ? messages : result, modifiedCount }\n}\n","import type { MessagesPayload } from \"~/types/api/anthropic\"\n\nimport { removeSystemReminderTags } from \"~/lib/system-prompt\"\n\nimport { sanitizeTextBlocksInArray } from \"./text-blocks\"\n\n/**\n * Sanitize Anthropic system prompt (can be string or array of text blocks).\n * Only removes system-reminder tags here.\n *\n * NOTE: Restrictive statement filtering is handled separately by:\n * - system-prompt.ts (via config.yaml overrides)\n * This avoids duplicate processing of the system prompt.\n */\nexport function sanitizeAnthropicSystemPrompt(system: MessagesPayload[\"system\"]): {\n system: MessagesPayload[\"system\"]\n modified: boolean\n} {\n if (!system) {\n return { system, modified: false }\n }\n\n if (typeof system === \"string\") {\n const sanitized = removeSystemReminderTags(system)\n return { system: sanitized, modified: sanitized !== system }\n }\n\n const { blocks, modified } = sanitizeTextBlocksInArray(\n system,\n (block) => block.text,\n (block, text) => ({ ...block, text }),\n )\n return { system: modified ? blocks : system, modified }\n}\n","import type { ContentBlockParam, MessageParam } from \"~/types/api/anthropic\"\n\nimport { isServerToolResultBlock } from \"~/types/api/anthropic\"\n\nimport { isImmutableThinkingAssistantMessage } from \"../thinking-immutability\"\n\n/**\n * Parse a potentially stringified JSON input into a proper object.\n * Handles double-serialized strings (e.g., \"\\\"{ ... }\\\"\") by parsing iteratively.\n */\nfunction parseStringifiedInput(input: unknown): Record<string, unknown> {\n if (typeof input !== \"string\") return input as Record<string, unknown>\n try {\n let parsed: unknown = input\n while (typeof parsed === \"string\") {\n parsed = JSON.parse(parsed)\n }\n return (typeof parsed === \"object\" && parsed !== null ? parsed : {}) as Record<string, unknown>\n } catch {\n return {}\n }\n}\n\n/**\n * Process all tool-related operations in a single pass:\n * 1. Fix tool_use name casing\n * 2. Filter orphaned tool_result blocks\n * 3. Filter orphaned tool_use blocks\n */\nexport function processToolBlocks(\n messages: Array<MessageParam>,\n tools: Array<{ name: string }> | undefined,\n): {\n messages: Array<MessageParam>\n fixedNameCount: number\n orphanedToolUseCount: number\n orphanedToolResultCount: number\n} {\n const nameMap = new Map<string, string>()\n if (tools && tools.length > 0) {\n for (const tool of tools) {\n nameMap.set(tool.name.toLowerCase(), tool.name)\n }\n }\n\n const toolUseIds = new Set<string>()\n const toolResultIds = new Set<string>()\n\n for (const msg of messages) {\n if (typeof msg.content === \"string\") continue\n\n if (msg.role === \"assistant\") {\n for (const block of msg.content) {\n if ((block.type === \"tool_use\" || block.type === \"server_tool_use\") && block.id) {\n toolUseIds.add(block.id)\n }\n if (isServerToolResultBlock(block)) {\n toolResultIds.add(block.tool_use_id)\n }\n }\n } else {\n for (const block of msg.content) {\n if (block.type === \"tool_result\" && block.tool_use_id) {\n toolResultIds.add(block.tool_use_id)\n } else if (isServerToolResultBlock(block)) {\n toolResultIds.add(block.tool_use_id)\n }\n }\n }\n }\n\n const result: Array<MessageParam> = []\n let fixedNameCount = 0\n let orphanedToolUseCount = 0\n let orphanedToolResultCount = 0\n const filteredToolUseIds = new Set<string>()\n\n for (const msg of messages) {\n if (typeof msg.content === \"string\") {\n result.push(msg)\n continue\n }\n\n if (msg.role === \"assistant\") {\n if (isImmutableThinkingAssistantMessage(msg)) {\n result.push(msg)\n continue\n }\n\n const newContent: Array<ContentBlockParam> = []\n let modified = false\n\n for (const block of msg.content) {\n if (block.type === \"tool_use\") {\n if (!toolResultIds.has(block.id)) {\n orphanedToolUseCount++\n filteredToolUseIds.add(block.id)\n modified = true\n continue\n }\n\n const correctName = nameMap.get(block.name.toLowerCase())\n const needsNameFix = correctName !== undefined && correctName !== block.name\n const needsInputFix = typeof block.input === \"string\"\n\n if (needsNameFix || needsInputFix) {\n modified = true\n const fixed = { ...block } as typeof block\n if (needsNameFix) {\n fixedNameCount++\n ;(fixed as { name: string }).name = correctName\n }\n if (needsInputFix) {\n ;(fixed as { input: Record<string, unknown> }).input = parseStringifiedInput(block.input)\n }\n newContent.push(fixed)\n } else {\n newContent.push(block)\n }\n } else if (block.type === \"server_tool_use\") {\n if (!toolResultIds.has(block.id)) {\n orphanedToolUseCount++\n filteredToolUseIds.add(block.id)\n modified = true\n continue\n }\n\n if (typeof block.input === \"string\") {\n modified = true\n newContent.push({ ...block, input: parseStringifiedInput(block.input) })\n } else {\n newContent.push(block)\n }\n } else {\n if (\n isServerToolResultBlock(block)\n && (!toolUseIds.has(block.tool_use_id) || filteredToolUseIds.has(block.tool_use_id))\n ) {\n orphanedToolResultCount++\n modified = true\n continue\n }\n newContent.push(block as ContentBlockParam)\n }\n }\n\n if (newContent.length === 0) continue\n result.push(modified ? { ...msg, content: newContent } : msg)\n } else {\n const newContent: Array<ContentBlockParam> = []\n\n for (const block of msg.content) {\n if (block.type === \"tool_result\") {\n if (!toolUseIds.has(block.tool_use_id) || filteredToolUseIds.has(block.tool_use_id)) {\n orphanedToolResultCount++\n continue\n }\n } else if (isServerToolResultBlock(block)) {\n if (!toolUseIds.has(block.tool_use_id) || filteredToolUseIds.has(block.tool_use_id)) {\n orphanedToolResultCount++\n continue\n }\n } else if (\n (block as unknown as Record<string, unknown>).type !== \"text\"\n && (block as unknown as Record<string, unknown>).type !== \"image\"\n ) {\n orphanedToolResultCount++\n continue\n }\n newContent.push(block)\n }\n\n if (newContent.length === 0) continue\n result.push({ ...msg, content: newContent })\n }\n }\n\n return {\n messages: result,\n fixedNameCount,\n orphanedToolUseCount,\n orphanedToolResultCount,\n }\n}\n","/**\n * Anthropic message sanitization orchestrator.\n *\n * Keeps the public import surface stable while the concrete sanitizers live in\n * focused submodules under `anthropic/sanitize/`.\n */\n\nimport consola from \"consola\"\n\nimport type { MessageParam, MessagesPayload } from \"~/types/api/anthropic\"\n\nimport { state } from \"~/lib/state\"\n\nimport { countAnthropicContentBlocks } from \"./sanitize/content-blocks\"\nimport { deduplicateToolCalls } from \"./sanitize/deduplicate-tool-calls\"\nimport { stripReadToolResultTags } from \"./sanitize/read-tool-result-tags\"\nimport { finalizeAnthropicSanitization, type SanitizationStats } from \"./sanitize/result\"\nimport { removeAnthropicSystemReminders } from \"./sanitize/system-reminders\"\nimport { sanitizeAnthropicSystemPrompt } from \"./sanitize/system-prompt\"\nimport { processToolBlocks } from \"./sanitize/tool-blocks\"\n\nexport {\n deduplicateToolCalls,\n processToolBlocks,\n removeAnthropicSystemReminders,\n type SanitizationStats,\n stripReadToolResultTags,\n}\n\n/**\n * One-time preprocessing of Anthropic messages.\n *\n * Runs idempotent operations that reduce context noise before the request\n * enters the routing / retry pipeline.\n */\nexport function preprocessAnthropicMessages(messages: Array<MessageParam>): {\n messages: Array<MessageParam>\n strippedReadTagCount: number\n dedupedToolCallCount: number\n} {\n let result = messages\n let strippedReadTagCount = 0\n let dedupedToolCallCount = 0\n\n if (state.stripReadToolResultTags) {\n const strip = stripReadToolResultTags(result)\n result = strip.messages\n strippedReadTagCount = strip.strippedCount\n if (strippedReadTagCount > 0) {\n consola.info(\n `[Preprocess] Stripped ${strippedReadTagCount} system-reminder tags from Read results:\\n`\n + strip.tagPreviews.map((preview) => ` - \"${preview}${preview.length >= 80 ? \"…\" : \"\"}\"`).join(\"\\n\"),\n )\n }\n }\n\n if (state.dedupToolCalls) {\n const dedup = deduplicateToolCalls(result, state.dedupToolCalls)\n result = dedup.messages\n dedupedToolCallCount = dedup.dedupedCount\n if (dedupedToolCallCount > 0) {\n const breakdown = Object.entries(dedup.dedupedByTool)\n .map(([name, count]) => `${name}×${count}`)\n .join(\", \")\n consola.info(`[Preprocess] Deduped ${dedupedToolCallCount} tool calls [${state.dedupToolCalls}] (${breakdown})`)\n }\n }\n\n return { messages: result, strippedReadTagCount, dedupedToolCallCount }\n}\n/**\n * Sanitize Anthropic messages by filtering orphaned tool blocks and system reminders.\n */\nexport function sanitizeAnthropicMessages(\n payload: MessagesPayload,\n): ReturnType<typeof finalizeAnthropicSanitization> {\n let messages = payload.messages\n const originalBlocks = countAnthropicContentBlocks(messages)\n\n const { system: sanitizedSystem } = sanitizeAnthropicSystemPrompt(payload.system)\n\n const reminderResult = removeAnthropicSystemReminders(messages)\n messages = reminderResult.messages\n const systemReminderRemovals = reminderResult.modifiedCount\n\n const toolResult = processToolBlocks(messages, payload.tools)\n messages = toolResult.messages\n return finalizeAnthropicSanitization(payload, messages, sanitizedSystem, originalBlocks, toolResult, systemReminderRemovals)\n}\n","import type { Model } from \"~/lib/models/client\"\nimport type { ContentBlock, ContentBlockParam, MessageParam, MessagesPayload } from \"~/types/api/anthropic\"\n\nimport type { AutoTruncateConfig } from \"../../auto-truncate\"\n\nimport {\n LARGE_TOOL_RESULT_THRESHOLD,\n compressCompactedReadResult,\n compressToolResultContent,\n computeSafetyMargin,\n getLearnedLimits,\n} from \"../../auto-truncate\"\nimport { processToolBlocks } from \"../sanitize\"\nimport { ensureAnthropicStartsWithUser } from \"./tool-utils\"\nimport { estimateMessageTokens } from \"./token-counting\"\nimport { isImmutableThinkingAssistantMessage } from \"../thinking-immutability\"\n\n/**\n * Strip thinking/redacted_thinking blocks from old assistant messages.\n */\nexport function stripThinkingBlocks(\n messages: Array<MessageParam>,\n preserveRecentCount: number,\n): { messages: Array<MessageParam>; strippedCount: number } {\n const n = messages.length\n const stripBefore = Math.max(0, n - preserveRecentCount)\n let strippedCount = 0\n\n const result = messages.map((msg, i) => {\n if (i >= stripBefore || msg.role !== \"assistant\" || !Array.isArray(msg.content)) {\n return msg\n }\n\n if (isImmutableThinkingAssistantMessage(msg)) {\n return msg\n }\n\n const hasThinking = msg.content.some((block) => block.type === \"thinking\" || block.type === \"redacted_thinking\")\n if (!hasThinking) return msg\n\n const filtered = msg.content.filter((block): block is ContentBlock => {\n if (block.type === \"thinking\" || block.type === \"redacted_thinking\") {\n strippedCount++\n return false\n }\n return true\n })\n\n if (filtered.length === 0) {\n return { ...msg, content: [{ type: \"text\" as const, text: \"\" }] }\n }\n\n return { ...msg, content: filtered }\n })\n\n return { messages: result, strippedCount }\n}\n\nfunction compressToolResultBlock(block: ContentBlockParam): ContentBlockParam {\n if (\n block.type === \"tool_result\"\n && typeof block.content === \"string\"\n && block.content.length > LARGE_TOOL_RESULT_THRESHOLD\n ) {\n return {\n ...block,\n content: compressToolResultContent(block.content),\n }\n }\n return block\n}\n\n/**\n * Smart compression strategy for Anthropic format.\n */\nexport function smartCompressToolResults(\n messages: Array<MessageParam>,\n tokenLimit: number,\n preservePercent: number,\n): {\n messages: Array<MessageParam>\n compressedCount: number\n compressThresholdIndex: number\n} {\n const n = messages.length\n const cumTokens: Array<number> = Array.from({ length: n + 1 }, () => 0)\n\n for (let i = n - 1; i >= 0; i--) {\n cumTokens[i] = cumTokens[i + 1] + estimateMessageTokens(messages[i])\n }\n\n const preserveTokenLimit = Math.floor(tokenLimit * preservePercent)\n\n let thresholdIndex = n\n for (let i = n - 1; i >= 0; i--) {\n if (cumTokens[i] > preserveTokenLimit) {\n thresholdIndex = i + 1\n break\n }\n thresholdIndex = i\n }\n\n if (thresholdIndex >= n) {\n return { messages, compressedCount: 0, compressThresholdIndex: n }\n }\n\n const result: Array<MessageParam> = []\n let compressedCount = 0\n\n for (const [i, msg] of messages.entries()) {\n if (i < thresholdIndex && msg.role === \"user\" && Array.isArray(msg.content)) {\n let hadCompression = false\n const compressedContent = msg.content.map((block) => {\n if (\n block.type === \"tool_result\"\n && typeof block.content === \"string\"\n && block.content.length > LARGE_TOOL_RESULT_THRESHOLD\n ) {\n compressedCount++\n hadCompression = true\n return compressToolResultBlock(block)\n }\n if (block.type === \"text\" && block.text.length > LARGE_TOOL_RESULT_THRESHOLD) {\n const compressed = compressCompactedReadResult(block.text)\n if (compressed) {\n compressedCount++\n hadCompression = true\n return { ...block, text: compressed }\n }\n }\n return block\n })\n if (hadCompression) {\n result.push({ ...msg, content: compressedContent })\n continue\n }\n }\n result.push(msg)\n }\n\n return {\n messages: result,\n compressedCount,\n compressThresholdIndex: thresholdIndex,\n }\n}\n\n/**\n * Calculate the effective token limit for auto-truncate.\n */\nexport function calculateTokenLimit(model: Model, config: AutoTruncateConfig): number | undefined {\n if (config.targetTokenLimit !== undefined) {\n return config.targetTokenLimit\n }\n\n const learned = getLearnedLimits(model.id)\n if (learned) {\n const margin = computeSafetyMargin(learned.sampleCount)\n return Math.floor(learned.tokenLimit * (1 - margin))\n }\n\n const rawTokenLimit =\n model.capabilities?.limits?.max_context_window_tokens ?? model.capabilities?.limits?.max_prompt_tokens\n\n if (rawTokenLimit === undefined) return undefined\n\n return Math.floor(rawTokenLimit * (1 - config.safetyMarginPercent / 100))\n}\n\ninterface PreserveSearchParams {\n messages: Array<MessageParam>\n systemTokens: number\n tokenLimit: number\n}\n\nexport function findOptimalPreserveIndex(params: PreserveSearchParams): number {\n const { messages, systemTokens, tokenLimit } = params\n\n if (messages.length === 0) return 0\n\n const markerTokens = 50\n const availableTokens = tokenLimit - systemTokens - markerTokens\n\n if (availableTokens <= 0) {\n return messages.length\n }\n\n const n = messages.length\n const cumTokens: Array<number> = Array.from({ length: n + 1 }, () => 0)\n\n for (let i = n - 1; i >= 0; i--) {\n cumTokens[i] = cumTokens[i + 1] + estimateMessageTokens(messages[i])\n }\n\n let left = 0\n let right = n\n\n while (left < right) {\n const mid = (left + right) >>> 1\n if (cumTokens[mid] <= availableTokens) {\n right = mid\n } else {\n left = mid + 1\n }\n }\n\n return left\n}\n\nexport function generateRemovedMessagesSummary(removedMessages: Array<MessageParam>): string {\n const toolCalls: Array<string> = []\n let userMessageCount = 0\n let assistantMessageCount = 0\n\n for (const msg of removedMessages) {\n if (msg.role === \"user\") {\n userMessageCount++\n } else {\n assistantMessageCount++\n }\n\n if (Array.isArray(msg.content)) {\n for (const block of msg.content) {\n if (block.type === \"tool_use\") {\n toolCalls.push(block.name)\n }\n if (block.type === \"server_tool_use\") {\n toolCalls.push(block.name)\n }\n }\n }\n }\n\n const parts: Array<string> = []\n\n if (userMessageCount > 0 || assistantMessageCount > 0) {\n const breakdown = []\n if (userMessageCount > 0) breakdown.push(`${userMessageCount} user`)\n if (assistantMessageCount > 0) breakdown.push(`${assistantMessageCount} assistant`)\n parts.push(`Messages: ${breakdown.join(\", \")}`)\n }\n\n if (toolCalls.length > 0) {\n const uniqueTools = [...new Set(toolCalls)]\n const displayTools =\n uniqueTools.length > 5 ? [...uniqueTools.slice(0, 5), `+${uniqueTools.length - 5} more`] : uniqueTools\n parts.push(`Tools used: ${displayTools.join(\", \")}`)\n }\n\n return parts.join(\". \")\n}\n\nexport function addCompressionNotice(payload: MessagesPayload, compressedCount: number): MessagesPayload {\n const notice =\n `[CONTEXT NOTE]\\n`\n + `${compressedCount} large tool_result blocks have been compressed to reduce context size.\\n`\n + `The compressed results show the beginning and end of the content with an omission marker.\\n`\n + `If you need the full content, you can re-read the file or re-run the tool.\\n`\n + `[END NOTE]\\n\\n`\n\n let newSystem: MessagesPayload[\"system\"]\n if (typeof payload.system === \"string\") {\n newSystem = notice + payload.system\n } else if (Array.isArray(payload.system)) {\n newSystem = [{ type: \"text\" as const, text: notice }, ...payload.system]\n } else {\n newSystem = notice\n }\n\n return { ...payload, system: newSystem }\n}\n\nexport function createTruncationSystemContext(\n removedCount: number,\n compressedCount: number,\n summary: string,\n): string {\n let context = `[CONVERSATION CONTEXT]\\n`\n\n if (removedCount > 0) {\n context += `${removedCount} earlier messages have been removed due to context window limits.\\n`\n }\n\n if (compressedCount > 0) {\n context += `${compressedCount} large tool_result blocks have been compressed.\\n`\n }\n\n if (summary) {\n context += `Summary of removed content: ${summary}\\n`\n }\n\n context +=\n `If you need earlier context, ask the user or check available tools for conversation history access.\\n`\n + `[END CONTEXT]\\n\\n`\n\n return context\n}\n\nexport function createTruncationMarker(\n removedCount: number,\n compressedCount: number,\n summary: string,\n): MessageParam {\n const parts: Array<string> = []\n\n if (removedCount > 0) {\n parts.push(`${removedCount} earlier messages removed`)\n }\n if (compressedCount > 0) {\n parts.push(`${compressedCount} tool_result blocks compressed`)\n }\n\n let content = `[CONTEXT MODIFIED: ${parts.join(\", \")} to fit context limits]`\n if (summary) {\n content += `\\n[Summary: ${summary}]`\n }\n return {\n role: \"user\",\n content,\n }\n}\n\n/**\n * Clean up truncated messages after preserve slicing.\n */\nexport function cleanupMessages(messages: Array<MessageParam>): Array<MessageParam> {\n let cleanedMessages = messages\n let pass = processToolBlocks(cleanedMessages, undefined)\n cleanedMessages = pass.messages\n cleanedMessages = ensureAnthropicStartsWithUser(cleanedMessages)\n pass = processToolBlocks(cleanedMessages, undefined)\n return pass.messages\n}\n","/**\n * Auto-truncate module for Anthropic-style messages.\n *\n * This module handles automatic truncation of Anthropic message format\n * when it exceeds token limits.\n *\n * Key features:\n * - Binary search for optimal truncation point\n * - Token limit enforcement with learned calibration\n * - Preserves system messages\n * - Filters orphaned tool_result and tool_use messages\n * - Smart compression of old tool_result content (e.g., Read tool results)\n */\n\nimport consola from \"consola\"\n\nimport type { Model } from \"~/lib/models/client\"\nimport type { MessagesPayload } from \"~/types/api/anthropic\"\n\nimport { state } from \"~/lib/state\"\nimport { bytesToKB } from \"~/lib/utils\"\n\nimport type { AutoTruncateConfig } from \"../auto-truncate\"\n\nimport {\n DEFAULT_AUTO_TRUNCATE_CONFIG,\n calibrate,\n getLearnedLimits,\n} from \"../auto-truncate\"\nimport {\n ensureAnthropicStartsWithUser,\n filterAnthropicOrphanedToolResults,\n filterAnthropicOrphanedToolUse,\n getAnthropicToolResultIds,\n getAnthropicToolUseIds,\n} from \"./auto-truncate/tool-utils\"\nimport {\n contentToText,\n countFixedTokens,\n countMessageTokens,\n countMessagesTokens,\n countSystemTokens,\n countTotalInputTokens,\n countTotalTokens,\n} from \"./auto-truncate/token-counting\"\nimport {\n addCompressionNotice,\n calculateTokenLimit,\n cleanupMessages,\n createTruncationMarker,\n createTruncationSystemContext,\n findOptimalPreserveIndex,\n generateRemovedMessagesSummary,\n smartCompressToolResults,\n stripThinkingBlocks,\n} from \"./auto-truncate/truncation\"\nexport {\n ensureAnthropicStartsWithUser,\n filterAnthropicOrphanedToolResults,\n filterAnthropicOrphanedToolUse,\n getAnthropicToolResultIds,\n getAnthropicToolUseIds,\n}\nexport {\n contentToText,\n countFixedTokens,\n countMessageTokens,\n countMessagesTokens,\n countSystemTokens,\n countTotalInputTokens,\n countTotalTokens,\n}\n\n// ============================================================================\n// Result Types\n// ============================================================================\n\nexport interface AnthropicAutoTruncateResult {\n payload: MessagesPayload\n wasTruncated: boolean\n originalTokens: number\n compactedTokens: number\n removedMessageCount: number\n /** Processing time in milliseconds */\n processingTimeMs: number\n}\n\n/**\n * Perform auto-truncation on an Anthropic payload that exceeds limits.\n */\nexport async function autoTruncateAnthropic(\n payload: MessagesPayload,\n model: Model,\n config: Partial<AutoTruncateConfig> = {},\n): Promise<AnthropicAutoTruncateResult> {\n const startTime = performance.now()\n\n // Helper to build result with timing\n const buildResult = (result: Omit<AnthropicAutoTruncateResult, \"processingTimeMs\">): AnthropicAutoTruncateResult => ({\n ...result,\n processingTimeMs: Math.round(performance.now() - startTime),\n })\n\n const cfg = { ...DEFAULT_AUTO_TRUNCATE_CONFIG, ...config }\n const tokenLimit = calculateTokenLimit(model, cfg)\n\n // No limit information available — skip truncation and let the server decide\n if (tokenLimit === undefined) {\n return buildResult({\n payload,\n wasTruncated: false,\n originalTokens: 0,\n compactedTokens: 0,\n removedMessageCount: 0,\n })\n }\n\n // Compute fixed overhead tokens (system + tools) once — these don't change during truncation\n const fixedTokens = await countFixedTokens(payload, model)\n\n // Measure original size\n const originalMsgTokens = await countMessagesTokens(payload.messages, model)\n const originalTokens = fixedTokens + originalMsgTokens\n\n // Check if compaction is needed\n if (originalTokens <= tokenLimit) {\n return buildResult({\n payload,\n wasTruncated: false,\n originalTokens,\n compactedTokens: originalTokens,\n removedMessageCount: 0,\n })\n }\n\n // Step 1: Strip thinking blocks from old assistant messages\n // These don't count as input tokens per Anthropic docs, but they consume request body space.\n // Preserve thinking in the last 4 messages (2 exchanges) for context continuity.\n const { messages: thinkingStripped, strippedCount: thinkingStrippedCount } = stripThinkingBlocks(payload.messages, 4)\n let workingMessages = thinkingStripped\n\n // Check if stripping alone was enough\n if (thinkingStrippedCount > 0) {\n const strippedMsgTokens = await countMessagesTokens(workingMessages, model)\n const strippedTokens = fixedTokens + strippedMsgTokens\n\n if (strippedTokens <= tokenLimit) {\n const elapsedMs = Math.round(performance.now() - startTime)\n consola.info(\n `[AutoTruncate:Anthropic] tokens: ${originalTokens}→${strippedTokens} `\n + `(stripped ${thinkingStrippedCount} thinking blocks) [${elapsedMs}ms]`,\n )\n\n return buildResult({\n payload: { ...payload, messages: workingMessages },\n wasTruncated: true,\n originalTokens,\n compactedTokens: strippedTokens,\n removedMessageCount: 0,\n })\n }\n }\n\n // Step 2: Smart compress old tool_results (if enabled)\n // Compress tool_results in messages that are beyond the preserve threshold\n let compressedCount = 0\n\n if (state.compressToolResultsBeforeTruncate) {\n const compressionResult = smartCompressToolResults(workingMessages, tokenLimit, cfg.preserveRecentPercent)\n workingMessages = compressionResult.messages\n compressedCount = compressionResult.compressedCount\n\n // Check if compression alone was enough\n const compressedMsgTokens = await countMessagesTokens(workingMessages, model)\n const compressedTokens = fixedTokens + compressedMsgTokens\n\n if (compressedTokens <= tokenLimit) {\n const elapsedMs = Math.round(performance.now() - startTime)\n consola.info(\n `[AutoTruncate:Anthropic] tokens: ${originalTokens}→${compressedTokens} `\n + `(compressed ${compressedCount} tool_results) [${elapsedMs}ms]`,\n )\n\n // Add compression notice to system prompt\n const compressedPayload = { ...payload, messages: workingMessages }\n const noticePayload = addCompressionNotice(compressedPayload, compressedCount)\n\n // Estimate notice token overhead instead of full recount\n const noticeTokenOverhead = Math.ceil(150 / 4) + 4 // ~150 chars in notice text\n return buildResult({\n payload: noticePayload,\n wasTruncated: true,\n originalTokens,\n compactedTokens: compressedTokens + noticeTokenOverhead,\n removedMessageCount: 0,\n })\n }\n\n // Step 2.5: Compress ALL tool_results (including recent ones)\n // If compressing only old tool_results wasn't enough, try compressing all of them\n // before resorting to message removal\n const allCompression = smartCompressToolResults(\n workingMessages,\n tokenLimit,\n 0.0, // preservePercent=0 means compress all messages\n )\n if (allCompression.compressedCount > 0) {\n workingMessages = allCompression.messages\n compressedCount += allCompression.compressedCount\n\n // Check if compressing all was enough\n const allCompressedMsgTokens = await countMessagesTokens(workingMessages, model)\n const allCompressedTokens = fixedTokens + allCompressedMsgTokens\n\n if (allCompressedTokens <= tokenLimit) {\n const elapsedMs = Math.round(performance.now() - startTime)\n consola.info(\n `[AutoTruncate:Anthropic] tokens: ${originalTokens}→${allCompressedTokens} `\n + `(compressed ${compressedCount} tool_results, including recent) [${elapsedMs}ms]`,\n )\n\n const allCompressedPayload = { ...payload, messages: workingMessages }\n const noticePayload = addCompressionNotice(allCompressedPayload, compressedCount)\n\n // Estimate notice token overhead instead of full recount\n const noticeTokenOverhead = Math.ceil(150 / 4) + 4\n return buildResult({\n payload: noticePayload,\n wasTruncated: true,\n originalTokens,\n compactedTokens: allCompressedTokens + noticeTokenOverhead,\n removedMessageCount: 0,\n })\n }\n }\n }\n\n // Step 3: Compression wasn't enough (or disabled), proceed with message removal\n // Use working messages (compressed if enabled, original otherwise)\n\n // Calculate system tokens for the binary search\n const systemTokens = await countSystemTokens(payload.system, model)\n\n // Find optimal preserve index on working messages\n const preserveIndex = findOptimalPreserveIndex({\n messages: workingMessages,\n systemTokens,\n tokenLimit,\n })\n\n // Check if we can compact\n if (preserveIndex >= workingMessages.length) {\n consola.warn(\"[AutoTruncate:Anthropic] Would need to remove all messages\")\n return buildResult({\n payload,\n wasTruncated: false,\n originalTokens,\n compactedTokens: originalTokens,\n removedMessageCount: 0,\n })\n }\n\n // Build preserved messages from working (compressed) messages\n let preserved = workingMessages.slice(preserveIndex)\n\n // Clean up the message list - filter orphaned tool blocks in two passes\n // (one pass to collect IDs, one to filter), then ensure starts with user\n preserved = cleanupMessages(preserved)\n\n if (preserved.length === 0) {\n consola.warn(\"[AutoTruncate:Anthropic] All messages filtered out after cleanup\")\n return buildResult({\n payload,\n wasTruncated: false,\n originalTokens,\n compactedTokens: originalTokens,\n removedMessageCount: 0,\n })\n }\n\n // Calculate removed messages and generate summary\n // Use original messages for summary (uncompressed content is more informative)\n const removedMessages = payload.messages.slice(0, preserveIndex)\n const removedCount = workingMessages.length - preserved.length\n const summary = generateRemovedMessagesSummary(removedMessages)\n\n // Build new payload with truncation context\n let newSystem = payload.system\n let newMessages = preserved\n\n // Prefer adding context to system prompt (cleaner for the model)\n if (payload.system !== undefined) {\n const truncationContext = createTruncationSystemContext(removedCount, compressedCount, summary)\n if (typeof payload.system === \"string\") {\n newSystem = truncationContext + payload.system\n } else if (Array.isArray(payload.system)) {\n // Prepend as first text block\n newSystem = [{ type: \"text\" as const, text: truncationContext }, ...payload.system]\n }\n } else {\n // No system prompt, use marker message\n const marker = createTruncationMarker(removedCount, compressedCount, summary)\n newMessages = [marker, ...preserved]\n }\n\n const newPayload: MessagesPayload = {\n ...payload,\n system: newSystem,\n messages: newMessages,\n }\n\n // Verify the result — only count messages (reuse cached fixed tokens)\n const newBytes = JSON.stringify(newPayload).length\n const newMsgTokens = await countMessagesTokens(newMessages, model)\n // Re-count system tokens if system was modified (truncation context added)\n const newSystemTokens = newSystem !== payload.system ? await countSystemTokens(newSystem, model) : systemTokens\n const toolsTokens = fixedTokens - (await countSystemTokens(payload.system, model))\n const newTokens = newSystemTokens + toolsTokens + newMsgTokens\n\n // Log single line summary\n const actions: Array<string> = []\n if (removedCount > 0) actions.push(`removed ${removedCount} msgs`)\n if (thinkingStrippedCount > 0) actions.push(`stripped ${thinkingStrippedCount} thinking blocks`)\n if (compressedCount > 0) actions.push(`compressed ${compressedCount} tool_results`)\n const actionInfo = actions.length > 0 ? ` (${actions.join(\", \")})` : \"\"\n\n const elapsedMs = Math.round(performance.now() - startTime)\n consola.info(\n `[AutoTruncate:Anthropic] tokens: ${originalTokens}→${newTokens}, `\n + `${bytesToKB(newBytes)}KB${actionInfo} [${elapsedMs}ms]`,\n )\n\n // Warn if still over token limit\n if (newTokens > tokenLimit) {\n consola.warn(`[AutoTruncate:Anthropic] Result still over token limit (${newTokens} > ${tokenLimit})`)\n }\n\n return buildResult({\n payload: newPayload,\n wasTruncated: true,\n originalTokens,\n compactedTokens: newTokens,\n removedMessageCount: removedCount,\n })\n}\n\n/**\n * Create a marker to prepend to responses indicating auto-truncation occurred.\n */\nexport function createTruncationResponseMarkerAnthropic(result: AnthropicAutoTruncateResult): string {\n if (!result.wasTruncated) return \"\"\n\n const reduction = result.originalTokens - result.compactedTokens\n const percentage = Math.round((reduction / result.originalTokens) * 100)\n\n return (\n `\\n\\n---\\n[Auto-truncated: ${result.removedMessageCount} messages removed, `\n + `${result.originalTokens} → ${result.compactedTokens} tokens (${percentage}% reduction)]`\n )\n}\n\n/**\n * Check if payload needs compaction based on learned model limits.\n * Returns early with `needed: false` when no limits are known for the model.\n */\nexport async function checkNeedsCompactionAnthropic(\n payload: MessagesPayload,\n model: Model,\n config: Partial<AutoTruncateConfig> = {},\n): Promise<{\n needed: boolean\n currentTokens: number\n tokenLimit: number\n reason?: \"tokens\"\n}> {\n const cfg = { ...DEFAULT_AUTO_TRUNCATE_CONFIG, ...config }\n\n // If no learned limits and no explicit target, skip pre-check\n // (we don't know the real limit yet — let the server tell us)\n const learned = getLearnedLimits(model.id)\n if (!learned && cfg.targetTokenLimit === undefined) {\n return {\n needed: false,\n currentTokens: 0,\n tokenLimit: 0,\n }\n }\n\n const tokenLimit = calculateTokenLimit(model, cfg)\n\n // Defensive: should not reach here without a resolvable limit (guarded by early return above),\n // but satisfy the type system\n if (tokenLimit === undefined) {\n return {\n needed: false,\n currentTokens: 0,\n tokenLimit: 0,\n }\n }\n\n const rawTokens = await countTotalTokens(payload, model)\n\n // Apply calibration to adjust the GPT tokenizer estimate\n const currentTokens = learned && learned.sampleCount > 0 ? calibrate(model.id, rawTokens) : rawTokens\n\n const exceedsTokens = cfg.checkTokenLimit && currentTokens > tokenLimit\n\n return {\n needed: exceedsTokens,\n currentTokens,\n tokenLimit,\n reason: exceedsTokens ? \"tokens\" : undefined,\n }\n}\n","import type { Context } from \"hono\"\n\nimport consola from \"consola\"\n\nimport { checkNeedsCompactionAnthropic, countTotalInputTokens } from \"~/lib/anthropic/auto-truncate\"\nimport { hasKnownLimits } from \"~/lib/auto-truncate\"\nimport { resolveModelName } from \"~/lib/models/resolver\"\nimport { state } from \"~/lib/state\"\nimport { tuiLogger } from \"~/lib/tui\"\nimport { type MessagesPayload } from \"~/types/api/anthropic\"\n\n/**\n * Handles token counting for Anthropic /v1/messages/count_tokens endpoint.\n *\n * Counts tokens directly on the Anthropic payload using native counting functions.\n *\n * Per Anthropic docs:\n * - Returns { input_tokens: N } where N is the total input tokens\n * - Thinking blocks from previous assistant turns don't count as input tokens\n * - The count is an estimate\n */\nexport async function handleCountTokens(c: Context) {\n const tuiLogId = c.get(\"tuiLogId\") as string | undefined\n\n try {\n const anthropicPayload = await c.req.json<MessagesPayload>()\n\n // Resolve model name aliases and date-suffixed versions\n anthropicPayload.model = resolveModelName(anthropicPayload.model)\n\n // Update tracker with model name\n if (tuiLogId) {\n tuiLogger.updateRequest(tuiLogId, { model: anthropicPayload.model })\n }\n\n const selectedModel = state.modelIndex.get(anthropicPayload.model)\n\n if (!selectedModel) {\n consola.warn(`[count_tokens] Model \"${anthropicPayload.model}\" not found, returning input_tokens=1`)\n return c.json({ input_tokens: 1 })\n }\n\n // Check if auto-truncate would be triggered (only for models with known limits)\n // If so, return an inflated token count to encourage Claude Code auto-compact\n if (state.autoTruncate && hasKnownLimits(selectedModel.id)) {\n const truncateCheck = await checkNeedsCompactionAnthropic(anthropicPayload, selectedModel, {\n checkTokenLimit: true,\n })\n\n if (truncateCheck.needed) {\n const contextWindow = selectedModel.capabilities?.limits?.max_context_window_tokens ?? 200000\n const inflatedTokens = Math.floor(contextWindow * 0.95)\n\n consola.info(\n `[count_tokens] Prompt too long: `\n + `${truncateCheck.currentTokens} tokens > ${truncateCheck.tokenLimit} limit, `\n + `returning inflated count ${inflatedTokens} to trigger client-side compaction`,\n )\n\n if (tuiLogId) {\n tuiLogger.updateRequest(tuiLogId, { inputTokens: inflatedTokens })\n }\n\n return c.json({ input_tokens: inflatedTokens })\n }\n }\n\n // Count tokens directly on Anthropic payload\n // Excludes thinking blocks from assistant messages per Anthropic spec\n const inputTokens = await countTotalInputTokens(anthropicPayload, selectedModel)\n\n consola.debug(\n `[count_tokens] ${inputTokens} tokens (native Anthropic) `\n + `(tokenizer: ${selectedModel.capabilities?.tokenizer ?? \"o200k_base\"})`,\n )\n\n if (tuiLogId) {\n tuiLogger.updateRequest(tuiLogId, { inputTokens })\n }\n\n return c.json({ input_tokens: inputTokens })\n } catch (error) {\n consola.error(\"[count_tokens] Error counting tokens:\", error)\n return c.json({ input_tokens: 1 })\n }\n}\n","import { copilotBaseUrl } from \"~/lib/copilot-api\"\nimport { normalizeForMatching } from \"~/lib/models/resolver\"\nimport { state } from \"~/lib/state\"\n\nexport type AnthropicNegotiatedFeature = \"context_management\"\n\nconst NEGOTIATION_TTL_MS = 10 * 60 * 1000\nconst unsupportedFeatures = new Map<string, number>()\n\nfunction makeKey(modelId: string, feature: AnthropicNegotiatedFeature): string {\n return `${copilotBaseUrl(state)}|anthropic-messages|${normalizeForMatching(modelId)}|${feature}`\n}\n\nfunction isFresh(expiresAt: number): boolean {\n return expiresAt > Date.now()\n}\n\nexport function markAnthropicFeatureUnsupported(modelId: string, feature: AnthropicNegotiatedFeature): void {\n unsupportedFeatures.set(makeKey(modelId, feature), Date.now() + NEGOTIATION_TTL_MS)\n}\n\nexport function isAnthropicFeatureUnsupported(modelId: string, feature: AnthropicNegotiatedFeature): boolean {\n const key = makeKey(modelId, feature)\n const expiresAt = unsupportedFeatures.get(key)\n if (!expiresAt) return false\n if (isFresh(expiresAt)) return true\n unsupportedFeatures.delete(key)\n return false\n}\n\nexport function resetAnthropicFeatureNegotiationForTesting(): void {\n unsupportedFeatures.clear()\n}\n","/**\n * Anthropic model feature detection and request header construction.\n *\n * Mirrors VSCode Copilot Chat's feature detection logic from:\n * - anthropic.ts: modelSupportsInterleavedThinking, modelSupportsContextEditing, modelSupportsToolSearch\n * - chatEndpoint.ts: getExtraHeaders (anthropic-beta headers)\n * - anthropic.ts: buildContextManagement\n */\n\nimport type { Model } from \"~/lib/models/client\"\nimport type { ContextEditingMode } from \"~/lib/state\"\n\nimport { normalizeForMatching } from \"~/lib/models/resolver\"\nimport { state } from \"~/lib/state\"\n\n// ============================================================================\n// Model Feature Detection\n// ============================================================================\n\n/**\n * Interleaved thinking is supported by:\n * - Claude Sonnet 4/4.5\n * - Claude Haiku 4.5\n * - Claude Opus 4.5/4.6\n *\n * Notably, claude-opus-4 and claude-opus-4-1 do NOT support interleaved thinking.\n */\nexport function modelSupportsInterleavedThinking(modelId: string): boolean {\n const normalized = normalizeForMatching(modelId)\n return (\n normalized.startsWith(\"claude-sonnet-4-5\")\n || normalized.startsWith(\"claude-sonnet-4\")\n || normalized.startsWith(\"claude-haiku-4-5\")\n || normalized.startsWith(\"claude-opus-4-5\")\n || normalized.startsWith(\"claude-opus-4-6\")\n )\n}\n\n/**\n * Context editing is supported by a broader set of models:\n * - Claude Haiku 4.5\n * - Claude Sonnet 4/4.5\n * - Claude Opus 4/4.1/4.5/4.6\n */\nexport function modelSupportsContextEditing(modelId: string): boolean {\n const normalized = normalizeForMatching(modelId)\n return (\n normalized.startsWith(\"claude-haiku-4-5\")\n || normalized.startsWith(\"claude-sonnet-4-5\")\n || normalized.startsWith(\"claude-sonnet-4\")\n || normalized.startsWith(\"claude-opus-4-5\")\n || normalized.startsWith(\"claude-opus-4-6\")\n || normalized.startsWith(\"claude-opus-4-1\")\n || normalized.startsWith(\"claude-opus-4\")\n )\n}\n\n/**\n * Check if context editing is enabled for a model.\n * Requires both model support AND config mode != 'off'.\n * Mirrors VSCode Copilot Chat's isAnthropicContextEditingEnabled().\n */\nexport function isContextEditingEnabled(modelId: string): boolean {\n return modelSupportsContextEditing(modelId) && state.contextEditingMode !== \"off\"\n}\n\n/**\n * Tool search is supported by:\n * - Claude Opus 4.5/4.6\n */\nexport function modelSupportsToolSearch(modelId: string): boolean {\n const normalized = normalizeForMatching(modelId)\n return normalized.startsWith(\"claude-opus-4-5\") || normalized.startsWith(\"claude-opus-4-6\")\n}\n\n// ============================================================================\n// Anthropic Beta Headers\n// ============================================================================\n\nexport interface AnthropicBetaHeaders {\n /** Comma-separated beta feature identifiers */\n \"anthropic-beta\"?: string\n}\n\nexport interface AnthropicBetaHeaderOptions {\n disableContextManagement?: boolean\n}\n\n/**\n * Check if a model supports adaptive thinking (from model metadata).\n *\n * Models with adaptive thinking (e.g. opus 4.6) use `thinking: { type: 'adaptive' }`\n * and do NOT need the interleaved-thinking beta header. Models without adaptive\n * thinking still need the beta header to enable interleaved thinking.\n *\n * Uses model metadata `capabilities.supports.adaptive_thinking` field.\n * Falls back to false when metadata is unavailable, which is safe because\n * adding the interleaved-thinking beta to an adaptive model is harmless\n * (the server ignores unknown betas), while omitting it from a non-adaptive\n * model that needs it would break thinking.\n */\nfunction modelHasAdaptiveThinking(resolvedModel?: Model): boolean {\n return resolvedModel?.capabilities?.supports?.adaptive_thinking === true\n}\n\n/**\n * Build anthropic-beta headers based on model capabilities.\n *\n * Logic from chatEndpoint.ts:getExtraHeaders:\n * - If model does NOT support adaptive thinking → add \"interleaved-thinking-2025-05-14\"\n * - If model supports context editing → add \"context-management-2025-06-27\"\n * - If model supports tool search → add \"advanced-tool-use-2025-11-20\"\n *\n * The resolvedModel parameter provides model metadata for capability-based\n * decisions. When unavailable, falls back to name-based detection.\n */\nexport function buildAnthropicBetaHeaders(\n modelId: string,\n resolvedModel?: Model,\n opts?: AnthropicBetaHeaderOptions,\n): AnthropicBetaHeaders {\n const headers: AnthropicBetaHeaders = {}\n const betaFeatures: Array<string> = []\n\n // Adaptive thinking models (e.g. opus 4.6) don't need the interleaved-thinking beta.\n // All other models that support interleaved thinking need it explicitly enabled.\n if (!modelHasAdaptiveThinking(resolvedModel)) {\n betaFeatures.push(\"interleaved-thinking-2025-05-14\")\n }\n\n if (!opts?.disableContextManagement && isContextEditingEnabled(modelId)) {\n betaFeatures.push(\"context-management-2025-06-27\")\n }\n\n if (modelSupportsToolSearch(modelId)) {\n betaFeatures.push(\"advanced-tool-use-2025-11-20\")\n }\n\n if (betaFeatures.length > 0) {\n headers[\"anthropic-beta\"] = betaFeatures.join(\",\")\n }\n\n return headers\n}\n\n// ============================================================================\n// Context Management\n// ============================================================================\n\ninterface ContextManagementEdit {\n type: string\n trigger?: { type: string; value: number }\n keep?: { type: string; value: number }\n clear_at_least?: { type: string; value: number }\n exclude_tools?: Array<string>\n clear_tool_inputs?: boolean\n}\n\nexport interface ContextManagement {\n edits: Array<ContextManagementEdit>\n}\n\n/**\n * Build context_management config for the request body.\n *\n * From anthropic.ts:270-329 (buildContextManagement + getContextManagementFromConfig):\n * - clear_thinking: keep last N thinking turns\n * - clear_tool_uses: triggered by input_tokens threshold, keep last N tool uses\n *\n * Only builds edits matching the requested mode:\n * - \"off\" → undefined (no context management)\n * - \"clear-thinking\" → clear_thinking only (if thinking is enabled)\n * - \"clear-tooluse\" → clear_tool_uses only\n * - \"clear-both\" → both edits\n */\nexport function buildContextManagement(mode: ContextEditingMode, hasThinking: boolean): ContextManagement | undefined {\n if (mode === \"off\") {\n return undefined\n }\n\n // Default config from getContextManagementFromConfig\n const triggerType = \"input_tokens\"\n const triggerValue = 100_000\n const keepCount = 3\n const thinkingKeepTurns = 1\n\n const edits: Array<ContextManagementEdit> = []\n\n // Add clear_thinking when mode is \"clear-thinking\" or \"clear-both\", and thinking is enabled\n if ((mode === \"clear-thinking\" || mode === \"clear-both\") && hasThinking) {\n edits.push({\n type: \"clear_thinking_20251015\",\n keep: { type: \"thinking_turns\", value: Math.max(1, thinkingKeepTurns) },\n })\n }\n\n // Add clear_tool_uses when mode is \"clear-tooluse\" or \"clear-both\"\n if (mode === \"clear-tooluse\" || mode === \"clear-both\") {\n edits.push({\n type: \"clear_tool_uses_20250919\",\n trigger: { type: triggerType, value: triggerValue },\n keep: { type: \"tool_uses\", value: keepCount },\n })\n }\n\n return edits.length > 0 ? { edits } : undefined\n}\n","/**\n * Anthropic Messages API tool preprocessing pipeline.\n *\n * Prepares the tools array before sending to Copilot's /v1/messages endpoint:\n * - Injects Claude Code official tool stubs for missing tools\n * - Applies tool_search / defer_loading based on model capabilities\n * - Ensures all custom tools have input_schema\n * - Injects stubs for tools referenced in message history\n * - Strips server-side tools (web_search, etc.) when configured\n *\n * Must be called BEFORE sanitize — processToolBlocks (in sanitize) uses\n * the tools array to validate tool_use references in messages.\n */\n\nimport consola from \"consola\"\n\nimport type { MessageParam, MessagesPayload, Tool } from \"~/types/api/anthropic\"\n\nimport { state } from \"~/lib/state\"\n\nimport { modelSupportsToolSearch } from \"./features\"\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * Claude Code official tool names that must always be present in the tools array.\n * If any of these are missing from the request, they will be injected as stub definitions.\n */\nconst CLAUDE_CODE_OFFICIAL_TOOLS = [\n \"Task\",\n \"TaskOutput\",\n \"Bash\",\n \"Glob\",\n \"Grep\",\n \"Read\",\n \"Edit\",\n \"Write\",\n \"NotebookEdit\",\n \"WebFetch\",\n \"TodoWrite\",\n \"KillShell\",\n \"AskUserQuestion\",\n \"Skill\",\n \"EnterPlanMode\",\n \"ExitPlanMode\",\n]\n\n/** Tool names that should NOT be deferred (core tools always available) */\nconst NON_DEFERRED_TOOL_NAMES = new Set([\n // VSCode Copilot Chat original tool names (snake_case)\n \"read_file\",\n \"list_dir\",\n \"grep_search\",\n \"semantic_search\",\n \"file_search\",\n \"replace_string_in_file\",\n \"multi_replace_string_in_file\",\n \"insert_edit_into_file\",\n \"apply_patch\",\n \"create_file\",\n \"run_in_terminal\",\n \"get_terminal_output\",\n \"get_errors\",\n \"manage_todo_list\",\n \"runSubagent\",\n \"search_subagent\",\n \"runTests\",\n \"ask_questions\",\n \"switch_agent\",\n // Claude Code official tool names (PascalCase)\n ...CLAUDE_CODE_OFFICIAL_TOOLS,\n])\n\nconst TOOL_SEARCH_TOOL_NAME = \"tool_search_tool_regex\"\nconst TOOL_SEARCH_TOOL_TYPE = \"tool_search_tool_regex_20251119\"\n\nconst EMPTY_INPUT_SCHEMA = { type: \"object\", properties: {}, required: [] } as const\n\n// ============================================================================\n// Internal helpers\n// ============================================================================\n\n/** Ensure a tool has input_schema — required by Anthropic API for custom tools. */\nfunction ensureInputSchema(tool: Tool): Tool {\n return tool.input_schema ? tool : { ...tool, input_schema: EMPTY_INPUT_SCHEMA }\n}\n\n/**\n * Collect tool names referenced in message history via tool_use blocks.\n *\n * When tool_search is enabled, deferred tools must be \"loaded\" via\n * tool_search_tool_regex before they can be called. But in multi-turn\n * conversations, message history may already contain tool_use blocks\n * referencing tools that were loaded in earlier turns. If we mark those\n * tools as deferred again, the API rejects the request because the\n * historical tool_use references a tool that isn't \"loaded\" in this turn.\n *\n * By collecting all tool names from history, we ensure those tools remain\n * non-deferred (immediately available) — preserving the tool_use/tool_result\n * pairing that the API requires.\n */\nfunction collectHistoryToolNames(messages: Array<MessageParam>): Set<string> {\n const names = new Set<string>()\n for (const msg of messages) {\n if (msg.role !== \"assistant\" || typeof msg.content === \"string\") continue\n for (const block of msg.content) {\n if (block.type === \"tool_use\") {\n names.add(block.name)\n }\n }\n }\n return names\n}\n\n/**\n * Build minimal tool stubs for tools referenced in message history.\n *\n * Used when the request has no tools but messages contain tool_use blocks.\n * Only needed when tool search is enabled (advanced-tool-use beta),\n * which enforces tool reference validation.\n */\nfunction buildHistoryToolStubs(historyToolNames: Set<string>): Array<Tool> {\n return Array.from(historyToolNames).map((name) => ({\n name,\n description: `Stub for tool referenced in conversation history`,\n input_schema: EMPTY_INPUT_SCHEMA,\n }))\n}\n\n// ============================================================================\n// Tool pipeline\n// ============================================================================\n\n/**\n * Process tools through the full pipeline:\n * 1. Inject missing Claude Code official tool stubs\n * 2. If model supports tool search: prepend search tool, mark non-core as deferred\n * 3. Ensure all custom tools have input_schema (skip API-defined typed tools)\n *\n * Returns a new array — never mutates the input.\n */\nfunction processToolPipeline(tools: Array<Tool>, modelId: string, messages: Array<MessageParam>): Array<Tool> {\n // Case-insensitive set for Claude Code stub injection — prevents injecting \"Read\" when\n // the client already has \"read\" (different casing). Without this, the model sees two\n // similar tools: the client's (with proper schema) and the stub (with empty schema).\n const existingNamesLower = new Set(tools.map((t) => t.name.toLowerCase()))\n const toolSearchEnabled = modelSupportsToolSearch(modelId)\n\n // Collect tool names already referenced in message history — these must\n // stay non-deferred to avoid \"Tool reference not found\" errors\n const historyToolNames = toolSearchEnabled ? collectHistoryToolNames(messages) : undefined\n\n const result: Array<Tool> = []\n\n // Prepend tool_search_tool_regex if model supports it\n if (toolSearchEnabled) {\n result.push({\n name: TOOL_SEARCH_TOOL_NAME,\n type: TOOL_SEARCH_TOOL_TYPE,\n defer_loading: false,\n })\n }\n\n // Process existing tools: ensure input_schema, apply defer_loading\n for (const tool of tools) {\n // Tools with a `type` field are API-defined (tool_search, memory, web_search) —\n // schema is managed server-side, don't touch input_schema\n const normalized = tool.type ? tool : ensureInputSchema(tool)\n\n // Respect explicit defer_loading: false from retry strategies (deferred-tool-retry\n // sets this when a tool was rejected as \"not found in available tools\")\n const shouldDefer =\n toolSearchEnabled\n && tool.defer_loading !== false\n && !NON_DEFERRED_TOOL_NAMES.has(tool.name)\n && !historyToolNames?.has(tool.name)\n\n result.push(shouldDefer ? { ...normalized, defer_loading: true } : normalized)\n }\n\n // Inject stubs for any missing Claude Code official tools\n for (const name of CLAUDE_CODE_OFFICIAL_TOOLS) {\n if (!existingNamesLower.has(name.toLowerCase())) {\n const stub: Tool = {\n name,\n description: `Claude Code ${name} tool`,\n input_schema: EMPTY_INPUT_SCHEMA,\n }\n // Official tools are always non-deferred, no defer_loading needed\n result.push(stub)\n }\n }\n\n // Inject minimal stubs for tools referenced in message history but missing\n // from the tools array. This happens when MCP tools were available in earlier\n // turns but not included in the current request. Without these stubs, the API\n // rejects the request because the historical tool_use references a tool that\n // doesn't exist in the tools list at all.\n if (historyToolNames) {\n const allResultNames = new Set(result.map((t) => t.name))\n for (const name of historyToolNames) {\n if (!allResultNames.has(name)) {\n consola.debug(`[ToolPipeline] Injecting stub for history-referenced tool: ${name}`)\n result.push({\n name,\n description: `Stub for tool referenced in conversation history`,\n input_schema: EMPTY_INPUT_SCHEMA,\n })\n }\n }\n }\n\n const deferredCount = result.filter((t) => t.defer_loading === true).length\n const injectedCount = result.length - tools.length\n if (deferredCount > 0 || injectedCount > 0) {\n consola.debug(\n `[ToolPipeline] ${result.length} tools`\n + ` (${deferredCount} deferred, ${injectedCount} injected, tool_search: ${toolSearchEnabled})`,\n )\n }\n\n return result\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Preprocess tools for an Anthropic request.\n *\n * Must be called BEFORE sanitize — because processToolBlocks (in sanitize)\n * uses the tools array to validate tool_use references. If we run sanitize\n * first with the original (incomplete) tools, it may incorrectly filter\n * tool_use blocks that processToolPipeline would have later provided stubs for.\n *\n * Handles two scenarios:\n * 1. Request has tools → run through processToolPipeline (defer_loading, stubs, tool_search)\n * 2. Request has NO tools but messages have tool_use → inject minimal stubs\n *\n * Returns a new payload (never mutates input).\n */\nexport function preprocessTools(payload: MessagesPayload): MessagesPayload {\n const tools = payload.tools\n const model = payload.model\n const messages = payload.messages\n\n if (tools && tools.length > 0) {\n return { ...payload, tools: processToolPipeline(tools, model, messages) }\n }\n\n // No tools in request — but if tool search is enabled and history has tool_use\n // references, we need stubs to satisfy API validation\n if (modelSupportsToolSearch(model)) {\n const historyToolNames = collectHistoryToolNames(messages)\n if (historyToolNames.size > 0) {\n consola.debug(\n `[ToolPipeline] Injecting ${historyToolNames.size} tool stubs for`\n + ` history references (no tools in request): ${[...historyToolNames].join(\", \")}`,\n )\n return { ...payload, tools: buildHistoryToolStubs(historyToolNames) }\n }\n }\n\n return payload\n}\n\n// ============================================================================\n// Server Tool Stripping\n// ============================================================================\n\n/**\n * Server tool type prefixes recognized by the Anthropic API.\n *\n * Server tools use a `type` field with a dated suffix (e.g., \"web_search_20250305\")\n * instead of `input_schema`. These are executed by Anthropic's servers, not by the client.\n *\n * Source: @anthropic-ai/sdk ContentBlock / Tool union types.\n */\n// prettier-ignore\nconst SERVER_TOOL_TYPE_PREFIXES = [\n \"web_search_\",\n \"web_fetch_\",\n \"code_execution_\",\n \"text_editor_\",\n \"computer_\",\n \"bash_\",\n]\n\n/** Check if a tool's type field matches a known server tool prefix. */\nfunction isServerToolType(type: string | undefined): boolean {\n if (!type) return false\n return SERVER_TOOL_TYPE_PREFIXES.some((prefix) => type.startsWith(prefix))\n}\n\n/**\n * Strip server-side tools from the tools array, or pass them through unchanged.\n *\n * When state.stripServerTools is enabled, server tools (web_search,\n * code_execution, etc.) are removed from the request because Copilot's /v1/messages\n * endpoint may not support executing them server-side. Without this, the tools\n * would be silently ignored or cause errors.\n *\n * When disabled (default), server tools are passed through unchanged — the proxy\n * transparently forwards them per the Anthropic protocol.\n */\nexport function stripServerTools(tools: Array<Tool> | undefined): Array<Tool> | undefined {\n if (!tools) return undefined\n\n // When stripping is disabled, pass all tools through unchanged\n if (!state.stripServerTools) return tools\n\n const result: Array<Tool> = []\n\n for (const tool of tools) {\n if (isServerToolType(tool.type)) {\n consola.warn(`[DirectAnthropic] Stripping server tool: ${tool.name} (type: ${tool.type})`)\n continue\n }\n result.push(tool)\n }\n\n return result.length > 0 ? result : undefined\n}\n","import consola from \"consola\"\n\nimport type { Model } from \"~/lib/models/client\"\nimport type { MessagesPayload, Tool } from \"~/types/api/anthropic\"\n\nimport { copilotHeaders } from \"~/lib/copilot-api\"\nimport { state } from \"~/lib/state\"\n\nimport { isAnthropicFeatureUnsupported } from \"./feature-negotiation\"\nimport { buildAnthropicBetaHeaders, buildContextManagement, isContextEditingEnabled } from \"./features\"\nimport { stripServerTools } from \"./message-tools\"\n\nexport interface PreparedAnthropicRequest {\n wire: Record<string, unknown>\n headers: Record<string, string>\n}\n\ninterface PrepareAnthropicRequestOptions {\n resolvedModel?: Model\n}\n\nconst COPILOT_REJECTED_FIELDS = new Set([\"output_config\", \"inference_geo\"])\n\nexport function prepareAnthropicRequest(\n payload: MessagesPayload,\n opts?: PrepareAnthropicRequestOptions,\n): PreparedAnthropicRequest {\n const wire = buildWirePayload(payload)\n adjustThinkingBudget(wire)\n\n const model = wire.model as string\n const messages = wire.messages as MessagesPayload[\"messages\"]\n const thinking = wire.thinking as MessagesPayload[\"thinking\"]\n\n const enableVision = messages.some((msg) => {\n if (typeof msg.content === \"string\") return false\n return msg.content.some((block) => block.type === \"image\")\n })\n\n const isAgentCall = messages.some((msg) => msg.role === \"assistant\")\n const modelSupportsVision = opts?.resolvedModel?.capabilities?.supports?.vision !== false\n const contextManagementDisabled = wire.context_management === null\n || isAnthropicFeatureUnsupported(model, \"context_management\")\n\n if (contextManagementDisabled) {\n delete wire.context_management\n }\n\n const headers: Record<string, string> = {\n ...copilotHeaders(state, {\n vision: enableVision && modelSupportsVision,\n modelRequestHeaders: opts?.resolvedModel?.request_headers,\n intent: isAgentCall ? \"conversation-agent\" : \"conversation-panel\",\n }),\n \"X-Initiator\": isAgentCall ? \"agent\" : \"user\",\n \"anthropic-version\": \"2023-06-01\",\n ...buildAnthropicBetaHeaders(model, opts?.resolvedModel, {\n disableContextManagement: contextManagementDisabled,\n }),\n }\n\n if (!contextManagementDisabled && !(\"context_management\" in wire) && isContextEditingEnabled(model)) {\n const hasThinking = Boolean(thinking && thinking.type !== \"disabled\")\n const contextManagement = buildContextManagement(state.contextEditingMode, hasThinking)\n if (contextManagement) {\n wire.context_management = contextManagement\n consola.debug(\"[DirectAnthropic] Added context_management:\", JSON.stringify(contextManagement))\n }\n }\n\n return { wire, headers }\n}\n\nfunction buildWirePayload(payload: MessagesPayload): Record<string, unknown> {\n const wire: Record<string, unknown> = {}\n const rejectedFields: Array<string> = []\n\n for (const [key, value] of Object.entries(payload)) {\n if (COPILOT_REJECTED_FIELDS.has(key)) {\n rejectedFields.push(key)\n } else {\n wire[key] = value\n }\n }\n\n if (rejectedFields.length > 0) {\n consola.debug(`[DirectAnthropic] Stripped rejected fields: ${rejectedFields.join(\", \")}`)\n }\n\n if (wire.tools) {\n wire.tools = stripServerTools(wire.tools as Array<Tool>)\n }\n\n return wire\n}\n\nfunction adjustThinkingBudget(wire: Record<string, unknown>): void {\n const thinking = wire.thinking as MessagesPayload[\"thinking\"]\n if (!thinking || thinking.type === \"disabled\" || thinking.type === \"adaptive\") return\n\n const budgetTokens = thinking.budget_tokens\n if (!budgetTokens) return\n\n const maxTokens = wire.max_tokens as number\n if (budgetTokens >= maxTokens) {\n const adjusted = maxTokens - 1\n ;(wire.thinking as { budget_tokens: number }).budget_tokens = adjusted\n consola.debug(\n `[DirectAnthropic] Capped thinking.budget_tokens: ${budgetTokens} → ${adjusted} (max_tokens=${maxTokens})`,\n )\n }\n}\n","/**\n * Direct Anthropic-style message API for Copilot.\n *\n * Owns the HTTP request lifecycle: wire payload construction, header building,\n * model-aware request enrichment (beta headers, context management),\n * and HTTP execution against Copilot's /v1/messages endpoint.\n *\n * Tool preprocessing lives in ./message-tools.ts and must be called\n * before createAnthropicMessages().\n */\n\nimport type { ServerSentEventMessage } from \"fetch-event-stream\"\n\nimport consola from \"consola\"\nimport { events } from \"fetch-event-stream\"\n\nimport type { HeadersCapture } from \"~/lib/context/request\"\nimport type { Model } from \"~/lib/models/client\"\nimport type { MessagesPayload, Message as AnthropicResponse, Tool } from \"~/types/api/anthropic\"\n\nimport { copilotBaseUrl } from \"~/lib/copilot-api\"\nimport { HTTPError } from \"~/lib/error\"\nimport { createFetchSignal, captureHttpHeaders, sanitizeHeadersForHistory } from \"~/lib/fetch-utils\"\nimport { state } from \"~/lib/state\"\n\nimport { prepareAnthropicRequest, type PreparedAnthropicRequest } from \"./request-preparation\"\n\n/** Re-export the response type for consumers */\nexport type AnthropicMessageResponse = AnthropicResponse\nexport { prepareAnthropicRequest, type PreparedAnthropicRequest } from \"./request-preparation\"\n\ninterface CreateAnthropicMessagesOptions {\n resolvedModel?: Model\n headersCapture?: HeadersCapture\n onPrepared?: (request: PreparedAnthropicRequest) => void\n}\n\n// ============================================================================\n// Main entry point — createAnthropicMessages\n// ============================================================================\n\n/**\n * Create messages using Anthropic-style API directly.\n * Calls Copilot's native Anthropic endpoint for Anthropic-vendor models.\n */\nexport async function createAnthropicMessages(\n payload: MessagesPayload,\n opts?: CreateAnthropicMessagesOptions,\n): Promise<AnthropicMessageResponse | AsyncGenerator<ServerSentEventMessage>> {\n if (!state.copilotToken) throw new Error(\"Copilot token not found\")\n\n const prepared = prepareAnthropicRequest(payload, opts)\n opts?.onPrepared?.({\n wire: prepared.wire,\n headers: sanitizeHeadersForHistory(prepared.headers),\n })\n\n const { wire, headers } = prepared\n\n // Destructure known fields for typed access\n const model = wire.model as string\n const messages = wire.messages as MessagesPayload[\"messages\"]\n const tools = wire.tools as Array<Tool> | undefined\n const thinking = wire.thinking as MessagesPayload[\"thinking\"]\n\n consola.debug(\"Sending direct Anthropic request to Copilot /v1/messages\")\n\n // Apply fetch timeout if configured (connection + response headers)\n const fetchSignal = createFetchSignal()\n\n const response = await fetch(`${copilotBaseUrl(state)}/v1/messages`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(wire),\n signal: fetchSignal,\n })\n\n // Capture HTTP headers for history (before error check — capture even on failure)\n if (opts?.headersCapture) {\n captureHttpHeaders(opts.headersCapture, headers, response)\n }\n\n if (!response.ok) {\n consola.debug(\"Request failed:\", {\n model,\n max_tokens: wire.max_tokens,\n stream: wire.stream,\n toolCount: tools?.length ?? 0,\n thinking,\n messageCount: messages.length,\n })\n throw await HTTPError.fromResponse(\"Failed to create Anthropic messages\", response, model)\n }\n\n if (payload.stream) {\n return events(response)\n }\n\n return (await response.json()) as AnthropicMessageResponse\n}\n","/**\n * Message mapping utilities for correlating original and rewritten message arrays.\n *\n * Used by Anthropic handlers to track which rewritten messages correspond\n * to which original messages.\n */\n\nimport type { MessageParam } from \"~/types/api/anthropic\"\n\n/**\n * Check if two messages likely correspond to the same original message.\n * Used by buildMessageMapping to handle cases where sanitization removes\n * content blocks within a message (changing its shape) or removes entire messages.\n */\nexport function messagesMatch(orig: MessageParam, rewritten: MessageParam): boolean {\n if (orig.role !== rewritten.role) return false\n\n // String content: compare prefix\n if (typeof orig.content === \"string\" && typeof rewritten.content === \"string\")\n return (\n rewritten.content.startsWith(orig.content.slice(0, 100))\n || orig.content.startsWith(rewritten.content.slice(0, 100))\n )\n\n // Array content: compare first block's type and id\n const origBlocks = Array.isArray(orig.content) ? orig.content : []\n const rwBlocks = Array.isArray(rewritten.content) ? rewritten.content : []\n\n if (origBlocks.length === 0 || rwBlocks.length === 0) return true\n\n const ob = origBlocks[0]\n const rb = rwBlocks[0]\n if (ob.type !== rb.type) return false\n if (ob.type === \"tool_use\" && rb.type === \"tool_use\") return ob.id === rb.id\n if (ob.type === \"tool_result\" && rb.type === \"tool_result\") return ob.tool_use_id === rb.tool_use_id\n return true\n}\n\n/**\n * Build messageMapping (rwIdx → origIdx) for the direct Anthropic path.\n * Uses a two-pointer approach since rewritten messages maintain the same relative\n * order as originals (all transformations are deletions, never reorderings).\n */\nexport function buildMessageMapping(original: Array<MessageParam>, rewritten: Array<MessageParam>): Array<number> {\n const mapping: Array<number> = []\n let origIdx = 0\n\n for (const element of rewritten) {\n while (origIdx < original.length) {\n if (messagesMatch(original[origIdx], element)) {\n mapping.push(origIdx)\n origIdx++\n break\n }\n origIdx++\n }\n }\n\n // If matching missed some (shouldn't happen), fill with -1\n while (mapping.length < rewritten.length) {\n mapping.push(-1)\n }\n\n return mapping\n}\n","/**\n * Server tool block filter for Anthropic SSE streams and non-streaming responses.\n *\n * Always active — matching vscode-copilot-chat behavior, which intercepts\n * server_tool_use and *_tool_result blocks unconditionally. These are server-side\n * artifacts (e.g. tool_search injected by copilot-api, web_search) that clients\n * don't expect and most SDKs can't validate.\n *\n * Also provides logging for server tool blocks (called before filtering,\n * so information is never lost even when blocks are stripped).\n */\n\nimport consola from \"consola\"\n\nimport type { StreamEvent } from \"~/types/api/anthropic\"\n\nimport type { AnthropicMessageResponse } from \"./client\"\n\n// ============================================================================\n// Server tool type detection\n// ============================================================================\n\n/** Check if a block type is a server-side tool result (ends with _tool_result, but not plain tool_result) */\nexport function isServerToolResultType(type: string): boolean {\n return type !== \"tool_result\" && type.endsWith(\"_tool_result\")\n}\n\n/**\n * Check if a content block is a server-side tool block.\n * Matches `server_tool_use` (any name) and all server tool result types\n * (web_search_tool_result, tool_search_tool_result, code_execution_tool_result, etc.).\n */\nexport function isServerToolBlock(block: { type: string }): boolean {\n if (block.type === \"server_tool_use\") return true\n return isServerToolResultType(block.type)\n}\n\n// ============================================================================\n// Server tool logging\n// ============================================================================\n\n/**\n * Log a single server tool block (server_tool_use or *_tool_result).\n * No-op for non-server-tool blocks — safe to call unconditionally.\n *\n * Called before filtering, so information is never lost even when blocks are stripped.\n */\nexport function logServerToolBlock(block: Record<string, unknown> & { type: string }) {\n if (block.type === \"server_tool_use\") {\n consola.debug(`[ServerTool] server_tool_use: ${block.name as string}`)\n return\n }\n\n if (!isServerToolResultType(block.type)) return\n\n const content = block.content as Record<string, unknown> | undefined\n if (!content) return\n\n const contentType = content.type as string | undefined\n\n // tool_search results: log discovered tool count and names\n if (contentType === \"tool_search_tool_search_result\") {\n const refs = content.tool_references as Array<{ tool_name?: string }> | undefined\n const toolNames = refs?.map((r) => r.tool_name).filter(Boolean) ?? []\n consola.debug(\n `[ServerTool] tool_search result: discovered ${toolNames.length} tools${toolNames.length > 0 ? ` [${toolNames.join(\", \")}]` : \"\"}`,\n )\n } else if (contentType === \"tool_search_tool_result_error\") {\n consola.warn(`[ServerTool] tool_search error: ${content.error_code as string}`)\n } else {\n // Generic server tool result (web_search, code_execution, etc.)\n consola.debug(`[ServerTool] ${block.type}: ${contentType ?? \"unknown\"}`)\n }\n}\n\n/**\n * Log all server tool blocks from a non-streaming response content array.\n * Must be called before filterServerToolBlocksFromResponse() to preserve info.\n */\nexport function logServerToolBlocks(content: Array<Record<string, unknown> & { type: string }>) {\n for (const block of content) {\n logServerToolBlock(block)\n }\n}\n\n// ============================================================================\n// Stream filter (SSE)\n// ============================================================================\n\n/**\n * Filters server tool blocks from the SSE stream before forwarding to the client.\n * Handles index remapping so block indices remain dense/sequential after filtering.\n *\n * Always active — matching vscode-copilot-chat behavior, which intercepts\n * server_tool_use and *_tool_result blocks unconditionally. These are server-side\n * artifacts (e.g. tool_search injected by copilot-api, web_search) that clients\n * don't expect and most SDKs can't validate.\n */\nexport function createServerToolBlockFilter() {\n const filteredIndices = new Set<number>()\n const clientIndexMap = new Map<number, number>()\n let nextClientIndex = 0\n\n function getClientIndex(apiIndex: number): number {\n let idx = clientIndexMap.get(apiIndex)\n if (idx === undefined) {\n idx = nextClientIndex++\n clientIndexMap.set(apiIndex, idx)\n }\n return idx\n }\n\n return {\n /** Returns rewritten data to forward, or null to suppress the event */\n rewriteEvent(parsed: StreamEvent | undefined, rawData: string): string | null {\n if (!parsed) return rawData\n\n if (parsed.type === \"content_block_start\") {\n const block = parsed.content_block as { type: string }\n if (isServerToolBlock(block)) {\n filteredIndices.add(parsed.index)\n return null\n }\n if (filteredIndices.size === 0) {\n getClientIndex(parsed.index)\n return rawData\n }\n const clientIndex = getClientIndex(parsed.index)\n if (clientIndex === parsed.index) return rawData\n const obj = JSON.parse(rawData) as Record<string, unknown>\n obj.index = clientIndex\n return JSON.stringify(obj)\n }\n\n if (parsed.type === \"content_block_delta\" || parsed.type === \"content_block_stop\") {\n if (filteredIndices.has(parsed.index)) return null\n if (filteredIndices.size === 0) return rawData\n const clientIndex = getClientIndex(parsed.index)\n if (clientIndex === parsed.index) return rawData\n const obj = JSON.parse(rawData) as Record<string, unknown>\n obj.index = clientIndex\n return JSON.stringify(obj)\n }\n\n return rawData\n },\n }\n}\n\n// ============================================================================\n// Non-streaming filter\n// ============================================================================\n\n/** Filter server tool blocks from a non-streaming response */\nexport function filterServerToolBlocksFromResponse(response: AnthropicMessageResponse): AnthropicMessageResponse {\n const filtered = response.content.filter((block: { type: string }) => !isServerToolBlock(block))\n\n if (filtered.length === response.content.length) return response\n return { ...response, content: filtered }\n}\n","/**\n * Stream accumulator for Anthropic format responses.\n * Handles accumulating stream events for history recording and tracking.\n */\n\nimport consola from \"consola\"\n\nimport type { CopilotAnnotations, StreamEvent, RawMessageStartEvent } from \"~/types/api/anthropic\"\n\nimport { isServerToolResultType } from \"./server-tool-filter\"\n\n// ============================================================================\n// Accumulated content block types\n// ============================================================================\n\n/**\n * A single content block accumulated from the stream, preserving original order.\n * Known block types have typed variants; unknown types are stored via\n * AccumulatedGenericBlock with all original fields preserved.\n */\nexport type AccumulatedContentBlock =\n | AccumulatedTextBlock\n | AccumulatedThinkingBlock\n | AccumulatedRedactedThinkingBlock\n | AccumulatedToolUseBlock\n | AccumulatedServerToolUseBlock\n | AccumulatedServerToolResultBlock\n | AccumulatedGenericBlock\n\nexport interface AccumulatedTextBlock {\n type: \"text\"\n text: string\n}\nexport interface AccumulatedThinkingBlock {\n type: \"thinking\"\n thinking: string\n signature?: string\n}\nexport interface AccumulatedRedactedThinkingBlock {\n type: \"redacted_thinking\"\n data: string\n}\nexport interface AccumulatedToolUseBlock {\n type: \"tool_use\"\n id: string\n name: string\n input: string\n}\nexport interface AccumulatedServerToolUseBlock {\n type: \"server_tool_use\"\n id: string\n name: string\n input: string\n}\n\n/**\n * Server-side tool result block (web_search_tool_result, tool_search_tool_result,\n * code_execution_tool_result, etc.). Branded with `_serverToolResult` to\n * distinguish from AccumulatedGenericBlock in type checks.\n *\n * Uses `_brand` discriminant to enable TypeScript union narrowing against\n * known literal-typed block variants.\n */\nexport interface AccumulatedServerToolResultBlock {\n _brand: \"server_tool_result\"\n type: string\n tool_use_id: string\n content: unknown\n}\n\n/**\n * Generic block for unknown/future content block types.\n * Branded with `_generic` to distinguish from known types in discriminated unions.\n */\nexport interface AccumulatedGenericBlock {\n type: string\n _generic: true\n [key: string]: unknown\n}\n\n// ============================================================================\n// Base accumulator interface (shared with OpenAI accumulator)\n// ============================================================================\n\n/** Minimal accumulator contract for tracking and error recording */\nexport interface BaseStreamAccumulator {\n model: string\n inputTokens: number\n outputTokens: number\n /** Plain text content accumulated from text deltas (error recording fallback) */\n rawContent: string\n}\n\n// ============================================================================\n// Anthropic stream accumulator\n// ============================================================================\n\n/** Stream accumulator for Anthropic format */\nexport interface AnthropicStreamAccumulator extends BaseStreamAccumulator {\n cacheReadTokens: number\n cacheCreationTokens: number\n stopReason: string\n /** Content blocks in stream order, indexed by the event's `index` field. */\n contentBlocks: Array<AccumulatedContentBlock>\n /** Copilot-specific: IP code citations collected from stream events */\n copilotAnnotations: Array<CopilotAnnotations>\n /** Error received from stream, if any */\n streamError?: { type: string; message: string }\n /** Server-side tool search request count from usage.server_tool_use */\n toolSearchRequests: number\n}\n\n/** Create a fresh Anthropic stream accumulator */\nexport function createAnthropicStreamAccumulator(): AnthropicStreamAccumulator {\n return {\n model: \"\",\n inputTokens: 0,\n outputTokens: 0,\n cacheReadTokens: 0,\n cacheCreationTokens: 0,\n stopReason: \"\",\n rawContent: \"\",\n contentBlocks: [],\n copilotAnnotations: [],\n toolSearchRequests: 0,\n }\n}\n\n// ============================================================================\n// Event processing\n// ============================================================================\n\n/** Accumulate a single Anthropic stream event into the accumulator */\nexport function accumulateAnthropicStreamEvent(event: StreamEvent, acc: AnthropicStreamAccumulator) {\n switch (event.type) {\n case \"message_start\": {\n handleMessageStart(event.message, acc)\n break\n }\n case \"content_block_start\": {\n handleContentBlockStart(event.index, event.content_block as unknown as AccContentBlock, acc)\n break\n }\n case \"content_block_delta\": {\n handleContentBlockDelta(event.index, event.delta as AccDelta, acc, event.copilot_annotations)\n break\n }\n case \"content_block_stop\": {\n // Nothing to do — block is already stored by index, no state to clear\n break\n }\n case \"message_delta\": {\n handleMessageDelta(event.delta as MessageDelta, event.usage as MessageDeltaUsage, acc)\n break\n }\n case \"message_stop\": {\n // Nothing to do — stop_reason is provided in message_delta, no state to clear\n break\n }\n case \"ping\": {\n // No accumulation needed for ping events, but could track last ping time if desired\n break\n }\n case \"error\": {\n const err = (event as { error?: { type?: string; message?: string } }).error\n acc.streamError = {\n type: err?.type ?? \"unknown_error\",\n message: err?.message ?? \"Unknown stream error\",\n }\n break\n }\n default: {\n consola.warn(`[stream-accumulator] Unknown event type: ${(event as { type: string }).type}`)\n break\n }\n }\n}\n\n// ============================================================================\n// message_start handler\n// ============================================================================\n\n/**\n * Handle message_start event.\n * This is where input_tokens, model, and cache stats are first reported.\n */\nfunction handleMessageStart(message: RawMessageStartEvent[\"message\"], acc: AnthropicStreamAccumulator) {\n if (message.model) acc.model = message.model\n acc.inputTokens = message.usage.input_tokens\n acc.outputTokens = message.usage.output_tokens\n if (message.usage.cache_read_input_tokens) {\n acc.cacheReadTokens = message.usage.cache_read_input_tokens\n }\n if (message.usage.cache_creation_input_tokens) {\n acc.cacheCreationTokens = message.usage.cache_creation_input_tokens\n }\n // Server-side tool search usage\n const serverToolUse = (message.usage as unknown as Record<string, unknown>).server_tool_use as\n | { tool_search_requests?: number }\n | undefined\n if (serverToolUse?.tool_search_requests) {\n acc.toolSearchRequests = serverToolUse.tool_search_requests\n }\n}\n\n// ============================================================================\n// content_block handlers\n// ============================================================================\n\n/** Content block delta types (local — looser than SDK's RawContentBlockDelta for proxy use) */\ntype AccDelta =\n | { type: \"text_delta\"; text: string }\n | { type: \"input_json_delta\"; partial_json: string }\n | { type: \"thinking_delta\"; thinking: string }\n | { type: \"signature_delta\"; signature: string }\n\n/**\n * Content block start — accepts any record from the SSE stream.\n * Known types are narrowed inside handleContentBlockStart; unknown types\n * are stored via AccumulatedGenericBlock or AccumulatedServerToolResultBlock.\n */\ntype AccContentBlock = Record<string, unknown> & { type: string }\n\nfunction handleContentBlockStart(index: number, block: AccContentBlock, acc: AnthropicStreamAccumulator) {\n let newBlock: AccumulatedContentBlock\n\n switch (block.type) {\n case \"text\": {\n newBlock = { type: \"text\", text: \"\" }\n break\n }\n case \"thinking\": {\n newBlock = { type: \"thinking\", thinking: \"\", signature: undefined }\n break\n }\n case \"redacted_thinking\": {\n // Complete at block_start, no subsequent deltas\n newBlock = { type: \"redacted_thinking\", data: block.data as string }\n break\n }\n case \"tool_use\": {\n newBlock = { type: \"tool_use\", id: block.id as string, name: block.name as string, input: \"\" }\n break\n }\n case \"server_tool_use\": {\n newBlock = { type: \"server_tool_use\", id: block.id as string, name: block.name as string, input: \"\" }\n break\n }\n default: {\n // Server tool result blocks (web_search_tool_result, tool_search_tool_result,\n // code_execution_tool_result, etc.) — complete at block_start, no subsequent deltas.\n if (isServerToolResultType(block.type) && \"tool_use_id\" in block) {\n newBlock = {\n _brand: \"server_tool_result\",\n type: block.type,\n tool_use_id: block.tool_use_id as string,\n content: block.content,\n }\n break\n }\n\n // Truly unknown block type — store all fields as-is for forward compatibility.\n consola.warn(`[stream-accumulator] Unknown content block type: ${block.type}`)\n newBlock = { ...block, _generic: true } as AccumulatedGenericBlock\n break\n }\n }\n\n acc.contentBlocks[index] = newBlock\n}\n\nfunction handleContentBlockDelta(\n index: number,\n delta: AccDelta,\n acc: AnthropicStreamAccumulator,\n copilotAnnotations?: CopilotAnnotations,\n) {\n const block = acc.contentBlocks[index]\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive: index from untrusted SSE data\n if (!block) return\n\n switch (delta.type) {\n case \"text_delta\": {\n const b = block as { text: string }\n b.text += delta.text\n acc.rawContent += delta.text // Sync BaseStreamAccumulator.rawContent for error fallback\n break\n }\n case \"thinking_delta\": {\n const b = block as { thinking: string }\n b.thinking += delta.thinking\n break\n }\n case \"input_json_delta\": {\n const b = block as { input: string }\n b.input += delta.partial_json\n break\n }\n case \"signature_delta\": {\n // signature_delta is part of the thinking block integrity, it's not accumulated actually (it, not content)\n const b = block as { signature?: string }\n if (b.signature) {\n consola.error(\n \"[stream-accumulator] Received unexpected signature_delta for a block that already has a signature. Overwriting existing signature.\",\n )\n }\n b.signature = delta.signature\n break\n }\n default: {\n consola.warn(`[stream-accumulator] Unknown delta type: ${(delta as { type: string }).type}`)\n break\n }\n }\n\n // Collect Copilot-specific IP code citations\n if (copilotAnnotations?.ip_code_citations?.length) {\n acc.copilotAnnotations.push(copilotAnnotations)\n }\n}\n\n// ============================================================================\n// message_delta handler\n// ============================================================================\n\n// Message delta types\ninterface MessageDelta {\n stop_reason?: string | null\n stop_sequence?: string | null\n}\n\ninterface MessageDeltaUsage {\n input_tokens?: number\n output_tokens: number\n cache_creation_input_tokens?: number\n cache_read_input_tokens?: number\n server_tool_use?: { tool_search_requests?: number }\n}\n\n/**\n * Handle message_delta event.\n * output_tokens is the final count here (replaces message_start's 0).\n * input_tokens may or may not be present — only update if provided.\n */\nfunction handleMessageDelta(\n delta: MessageDelta,\n usage: MessageDeltaUsage | undefined,\n acc: AnthropicStreamAccumulator,\n) {\n if (delta.stop_reason) acc.stopReason = delta.stop_reason\n if (usage) {\n // output_tokens in message_delta is the final output count\n acc.outputTokens = usage.output_tokens\n // input_tokens in message_delta is optional; only override if explicitly present\n if (usage.input_tokens !== undefined) {\n acc.inputTokens = usage.input_tokens\n }\n // Accumulate cache stats if present (may complement message_start values)\n if (usage.cache_read_input_tokens !== undefined) {\n acc.cacheReadTokens = usage.cache_read_input_tokens\n }\n if (usage.cache_creation_input_tokens !== undefined) {\n acc.cacheCreationTokens = usage.cache_creation_input_tokens\n }\n // Server-side tool search usage\n if (usage.server_tool_use?.tool_search_requests) {\n acc.toolSearchRequests = usage.server_tool_use.tool_search_requests\n }\n }\n}\n\n// ============================================================================\n// Convenience extractors\n// ============================================================================\n\n/** Get concatenated text content from all text blocks */\nexport function getTextContent(acc: AnthropicStreamAccumulator): string {\n return acc.contentBlocks\n .filter((b): b is { type: \"text\"; text: string } => b.type === \"text\")\n .map((b) => b.text)\n .join(\"\")\n}\n\n/** Get concatenated thinking content from all thinking blocks */\nexport function getThinkingContent(acc: AnthropicStreamAccumulator): string {\n return acc.contentBlocks\n .filter((b): b is { type: \"thinking\"; thinking: string } => b.type === \"thinking\")\n .map((b) => b.thinking)\n .join(\"\")\n}\n\n/** Get count of redacted_thinking blocks */\nexport function getRedactedThinkingCount(acc: AnthropicStreamAccumulator): number {\n return acc.contentBlocks.filter((b) => b.type === \"redacted_thinking\").length\n}\n","/**\n * Anthropic SSE stream processing and API routing.\n *\n * Reusable components shared across route handlers and tests:\n * - API routing decisions (vendor/endpoint validation)\n * - SSE stream processing (parse, accumulate, shutdown-aware iteration)\n */\n\nimport type { ServerSentEventMessage } from \"fetch-event-stream\"\n\nimport consola from \"consola\"\n\nimport type { StreamEvent } from \"~/types/api/anthropic\"\n\nimport { ENDPOINT, isEndpointSupported } from \"~/lib/models/endpoint\"\nimport { getShutdownSignal } from \"~/lib/shutdown\"\nimport { state } from \"~/lib/state\"\nimport { combineAbortSignals, raceIteratorNext, STREAM_ABORTED } from \"~/lib/stream\"\n\nimport { type AnthropicStreamAccumulator, accumulateAnthropicStreamEvent } from \"./stream-accumulator\"\n\n// ============================================================================\n// API routing\n// ============================================================================\n\nexport interface ApiRoutingDecision {\n supported: boolean\n reason: string\n}\n\n/**\n * Check if a model supports direct Anthropic API.\n * Returns a decision with reason so callers can log/display the routing rationale.\n */\nexport function supportsDirectAnthropicApi(modelId: string): ApiRoutingDecision {\n const model = state.modelIndex.get(modelId)\n if (model?.vendor !== \"Anthropic\") {\n return { supported: false, reason: `vendor is \"${model?.vendor ?? \"unknown\"}\", not Anthropic` }\n }\n\n if (!isEndpointSupported(model, ENDPOINT.MESSAGES)) {\n return { supported: false, reason: \"model does not support /v1/messages endpoint\" }\n }\n\n return { supported: true, reason: \"Anthropic vendor with /v1/messages support\" }\n}\n\n// ============================================================================\n// Stream processing\n// ============================================================================\n\n/** Processed event from the Anthropic stream */\nexport interface ProcessedAnthropicEvent {\n /** Original SSE message for forwarding */\n raw: ServerSentEventMessage\n /** Parsed event for accumulation (undefined for keepalives / [DONE]) */\n parsed?: StreamEvent\n}\n\n/**\n * Process raw Anthropic SSE stream: parse events, accumulate, check shutdown.\n * Yields each event for the caller to forward to the client.\n *\n * Each iteration races `iterator.next()` against idle timeout (if configured)\n * and the shutdown abort signal — so a stalled upstream connection can be\n * interrupted by either mechanism without waiting for the next event.\n */\nexport async function* processAnthropicStream(\n response: AsyncIterable<ServerSentEventMessage>,\n acc: AnthropicStreamAccumulator,\n clientAbortSignal?: AbortSignal,\n shutdownSignalProvider: () => AbortSignal | undefined = getShutdownSignal,\n): AsyncGenerator<ProcessedAnthropicEvent> {\n const idleTimeoutMs = state.streamIdleTimeout * 1000\n const iterator = response[Symbol.asyncIterator]()\n\n for (;;) {\n // Resolve shutdown signal lazily on each iteration so streams that started\n // before shutdown still observe the Phase 3 abort once shutdown begins.\n const abortSignal = combineAbortSignals(shutdownSignalProvider(), clientAbortSignal)\n const result = await raceIteratorNext(iterator.next(), { idleTimeoutMs, abortSignal })\n\n // Shutdown abort signal fired while waiting for the next event\n if (result === STREAM_ABORTED) break\n\n if (result.done) break\n\n const rawEvent = result.value\n\n // No data — keepalive, nothing to accumulate\n if (!rawEvent.data) {\n consola.debug(\"SSE event with no data (keepalive):\", rawEvent.event ?? \"(no event type)\")\n yield { raw: rawEvent }\n continue\n }\n\n // [DONE] is not part of the SSE spec - it's an OpenAI convention.\n // Copilot's gateway injects it at the end of all streams, including Anthropic.\n // see refs/vscode-copilot-chat/src/platform/endpoint/node/messagesApi.ts:326\n if (rawEvent.data === \"[DONE]\") break\n\n // Try to parse and accumulate for history/tracking\n let parsed: StreamEvent | undefined\n try {\n parsed = JSON.parse(rawEvent.data) as StreamEvent\n accumulateAnthropicStreamEvent(parsed, acc)\n } catch (parseError) {\n consola.error(\"Failed to parse Anthropic stream event:\", parseError, rawEvent.data)\n }\n\n yield { raw: rawEvent, parsed }\n\n // Error event is terminal — Anthropic sends no more events after this\n if (parsed?.type === \"error\") break\n }\n}\n\n// ============================================================================\n// Re-exports\n// ============================================================================\n\n// Stream accumulator — re-exported for convenience\n\nexport { type AnthropicStreamAccumulator, createAnthropicStreamAccumulator } from \"./stream-accumulator\"\n","/**\n * Stream repetition detector.\n *\n * Uses the KMP failure function (prefix function) to detect repeated patterns\n * in streaming text output. When a model gets stuck in a repetitive loop,\n * it wastes tokens producing the same content over and over. This detector\n * identifies such loops early so the caller can take action (log warning,\n * abort stream, etc.).\n *\n * The algorithm works by maintaining a sliding buffer of recent text and\n * computing the longest proper prefix that is also a suffix — if this\n * length exceeds `(text.length - period) >= minRepetitions * period`,\n * it means a pattern of length `period` has repeated enough times.\n */\n\nimport consola from \"consola\"\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nexport interface RepetitionDetectorConfig {\n /** Minimum pattern length in characters to consider as repetition (default: 10) */\n minPatternLength: number\n /** Minimum number of full repetitions to trigger detection (default: 3) */\n minRepetitions: number\n /** Maximum buffer size in characters; older text is discarded (default: 5000) */\n maxBufferSize: number\n}\n\nconst DEFAULT_CONFIG: RepetitionDetectorConfig = {\n minPatternLength: 10,\n minRepetitions: 3,\n maxBufferSize: 5000,\n}\n\n// ============================================================================\n// Repetition Detector\n// ============================================================================\n\nexport class RepetitionDetector {\n private buffer = \"\"\n private readonly config: RepetitionDetectorConfig\n private detected = false\n\n constructor(config?: Partial<RepetitionDetectorConfig>) {\n this.config = { ...DEFAULT_CONFIG, ...config }\n }\n\n /**\n * Feed a text chunk into the detector.\n * Returns `true` if repetition has been detected (now or previously).\n * Once detected, subsequent calls return `true` without further analysis.\n */\n feed(text: string): boolean {\n if (this.detected) return true\n if (!text) return false\n\n this.buffer += text\n\n // Trim buffer to maxBufferSize (keep the tail)\n if (this.buffer.length > this.config.maxBufferSize) {\n this.buffer = this.buffer.slice(-this.config.maxBufferSize)\n }\n\n // Only check when we have enough data for at least minRepetitions of the minimum pattern\n const minRequired = this.config.minPatternLength * this.config.minRepetitions\n if (this.buffer.length < minRequired) return false\n\n // Check for repetition in the buffer\n this.detected = detectRepetition(this.buffer, this.config.minPatternLength, this.config.minRepetitions)\n return this.detected\n }\n\n /** Reset detector state for a new stream */\n reset(): void {\n this.buffer = \"\"\n this.detected = false\n }\n\n /** Whether repetition has been detected */\n get isDetected(): boolean {\n return this.detected\n }\n}\n\n// ============================================================================\n// Core detection algorithm\n// ============================================================================\n\n/**\n * Detect if the tail of `text` contains a repeating pattern.\n *\n * Uses the KMP prefix function: for a string S, the prefix function π[i]\n * gives the length of the longest proper prefix of S[0..i] that is also\n * a suffix. If π[n-1] ≥ (n - period) where period = n - π[n-1], then\n * the string is composed of a repeating unit of length `period`.\n *\n * We check the suffix of the buffer (last `checkLength` chars) to detect\n * if a pattern of at least `minPatternLength` chars repeats at least\n * `minRepetitions` times.\n */\nfunction detectRepetition(text: string, minPatternLength: number, minRepetitions: number): boolean {\n // We check progressively larger windows to find repetitions\n // Start from the minimum detectable size\n const minWindow = minPatternLength * minRepetitions\n const maxWindow = Math.min(text.length, 2000) // Cap analysis window for performance\n\n // Check the tail of the text with increasing window sizes\n // Use a few strategic window sizes rather than checking every size\n const windowSizes = [minWindow, Math.floor(maxWindow * 0.5), maxWindow].filter(\n (w) => w >= minWindow && w <= text.length,\n )\n\n for (const windowSize of windowSizes) {\n const window = text.slice(-windowSize)\n const period = findRepeatingPeriod(window)\n\n if (period >= minPatternLength) {\n const repetitions = Math.floor(window.length / period)\n if (repetitions >= minRepetitions) {\n return true\n }\n }\n }\n\n return false\n}\n\n/**\n * Find the shortest repeating period in a string using KMP prefix function.\n * Returns the period length, or the string length if no repetition found.\n */\nfunction findRepeatingPeriod(s: string): number {\n const n = s.length\n if (n === 0) return 0\n\n // Compute KMP prefix function (failure function)\n const pi = new Int32Array(n)\n // pi[0] = 0 implicitly\n\n for (let i = 1; i < n; i++) {\n let j = pi[i - 1] ?? 0\n while (j > 0 && s[i] !== s[j]) {\n j = pi[j - 1] ?? 0\n }\n if (s[i] === s[j]) {\n j++\n }\n pi[i] = j\n }\n\n // The period of the string is n - pi[n-1]\n // If n is divisible by the period, the string is fully periodic\n const period = n - pi[n - 1]\n if (period < n && n % period === 0) {\n return period\n }\n\n // Check if the string is approximately periodic\n // (allows for a partial final repetition)\n if (period < n && pi[n - 1] >= period) {\n return period\n }\n\n return n // No repetition found\n}\n\n// ============================================================================\n// Convenience: Stream integration helper\n// ============================================================================\n\n/**\n * Create a repetition detector callback for use in stream processing.\n * Returns a function that accepts text deltas and logs a warning on first detection.\n */\nexport function createStreamRepetitionChecker(\n label: string,\n config?: Partial<RepetitionDetectorConfig>,\n): (textDelta: string) => boolean {\n const detector = new RepetitionDetector(config)\n let warned = false\n\n return (textDelta: string): boolean => {\n const isRepetitive = detector.feed(textDelta)\n if (isRepetitive && !warned) {\n warned = true\n consola.warn(`[RepetitionDetector] ${label}: Repetitive output detected in stream`)\n }\n return isRepetitive\n }\n}\n","/**\n * Context management retry strategy.\n *\n * Handles 400 errors where the upstream proxy rejects the Anthropic\n * `context_management` request field as an unknown/unsupported extra input.\n *\n * Some upstreams lag behind Anthropic feature rollout or support the beta\n * header but not the body field. In that case we retry once with\n * `context_management` explicitly disabled for that payload only.\n */\n\nimport type { ApiError } from \"~/lib/error\"\n\nimport { markAnthropicFeatureUnsupported } from \"~/lib/anthropic/feature-negotiation\"\n\nimport type { RetryAction, RetryContext, RetryStrategy } from \"../pipeline\"\n\nconst EXTRA_INPUTS_PATTERN = /context_management:\\s*Extra inputs are not permitted/i\n\nexport function parseContextManagementExtraInputsError(message: string): boolean {\n return EXTRA_INPUTS_PATTERN.test(message)\n}\n\nfunction extractErrorMessage(error: ApiError): string | null {\n if (parseContextManagementExtraInputsError(error.message)) {\n return error.message\n }\n\n const raw = error.raw\n if (!raw || typeof raw !== \"object\" || !(\"responseText\" in raw) || typeof raw.responseText !== \"string\") {\n return null\n }\n\n try {\n const parsed = JSON.parse(raw.responseText) as { error?: { message?: string } }\n return parsed.error?.message ?? raw.responseText\n } catch {\n return raw.responseText\n }\n}\n\nexport function createContextManagementRetryStrategy<\n TPayload extends {\n model: string\n context_management?: Record<string, unknown> | null\n },\n>(): RetryStrategy<TPayload> {\n return {\n name: \"context-management-retry\",\n\n canHandle(error: ApiError): boolean {\n if (error.type !== \"bad_request\" || error.status !== 400) return false\n const message = extractErrorMessage(error)\n return message ? parseContextManagementExtraInputsError(message) : false\n },\n\n handle(error: ApiError, currentPayload: TPayload, _context: RetryContext<TPayload>): Promise<RetryAction<TPayload>> {\n markAnthropicFeatureUnsupported(currentPayload.model, \"context_management\")\n\n if (currentPayload.context_management === null) {\n return Promise.resolve({ action: \"abort\", error })\n }\n\n return Promise.resolve({\n action: \"retry\",\n payload: {\n ...currentPayload,\n context_management: null,\n },\n meta: {\n disabledContextManagement: true,\n },\n })\n },\n }\n}\n","/**\n * Deferred tool retry strategy.\n *\n * Handles 400 errors caused by deferred tools being referenced in the request\n * (e.g., in message history) before they've been loaded via tool_search.\n *\n * When context_management clears older tool_search activations but keeps\n * tool_use/tool_result pairs, or when the client compacts history, a deferred\n * tool may appear in the conversation without its tool_search \"load\" record.\n * The API then rejects the request with:\n * \"Tool reference 'X' not found in available tools\"\n *\n * This strategy parses the tool name from the error, marks it as non-deferred\n * (defer_loading: false) in the payload's tools array, and retries.\n */\n\nimport consola from \"consola\"\n\nimport type { ApiError } from \"~/lib/error\"\nimport type { Tool } from \"~/types/api/anthropic\"\n\nimport type { RetryAction, RetryContext, RetryStrategy } from \"../pipeline\"\n\n// ============================================================================\n// Error parsing\n// ============================================================================\n\n/** Pattern: \"Tool reference 'tool_name' not found in available tools\" */\nconst TOOL_REFERENCE_NOT_FOUND_PATTERN = /Tool reference '([^']+)' not found in available tools/\n\n/**\n * Extract tool name from a \"Tool reference not found\" error.\n * Returns the tool name or null if the error doesn't match.\n */\nexport function parseToolReferenceError(message: string): string | null {\n const match = TOOL_REFERENCE_NOT_FOUND_PATTERN.exec(message)\n return match?.[1] ?? null\n}\n\n// ============================================================================\n// Strategy\n// ============================================================================\n\n/**\n * Create a deferred tool retry strategy.\n *\n * When the API rejects a request because a deferred tool is referenced\n * in the message history, this strategy un-defers that tool and retries.\n */\nexport function createDeferredToolRetryStrategy<TPayload extends { tools?: Array<Tool> }>(): RetryStrategy<TPayload> {\n // Track tool names that have already been un-deferred across retries\n // to avoid infinite retry loops on the same tool\n const undeferredTools = new Set<string>()\n\n return {\n name: \"deferred-tool-retry\",\n\n canHandle(error: ApiError): boolean {\n if (error.type !== \"bad_request\" || error.status !== 400) return false\n\n const raw = error.raw\n if (!raw || typeof raw !== \"object\" || !(\"responseText\" in raw)) return false\n\n const responseText = (raw as { responseText: string }).responseText\n const toolName = parseToolReferenceFromResponse(responseText)\n if (!toolName) return false\n\n // Only handle if we haven't already retried for this tool\n return !undeferredTools.has(toolName)\n },\n\n handle(error: ApiError, currentPayload: TPayload, context: RetryContext<TPayload>): Promise<RetryAction<TPayload>> {\n const raw = error.raw as { responseText: string }\n const toolName = parseToolReferenceFromResponse(raw.responseText)\n\n if (!toolName || !currentPayload.tools) {\n return Promise.resolve({ action: \"abort\", error })\n }\n\n consola.debug(\n `[DeferredToolRetry] Tool \"${toolName}\" error.`\n + ` Payload has ${currentPayload.tools.length} tools: [${currentPayload.tools.map((t) => t.name).join(\", \")}]`,\n )\n\n // Find the tool in the payload\n const toolIndex = currentPayload.tools.findIndex((t) => t.name === toolName)\n\n if (toolIndex === -1) {\n // Safety net: tool may not be in the pipeline-visible payload\n // (e.g. preprocessTools wasn't called, or sanitize removed it).\n // Inject a minimal stub with no defer_loading so the API accepts\n // the tool_use reference on retry.\n consola.debug(`[DeferredToolRetry] Tool \"${toolName}\" not in payload, injecting non-deferred stub`)\n undeferredTools.add(toolName)\n\n const newTools = [\n ...currentPayload.tools,\n {\n name: toolName,\n description: \"Tool referenced in conversation history\",\n input_schema: { type: \"object\" as const, properties: {} },\n },\n ]\n\n return Promise.resolve({\n action: \"retry\",\n payload: { ...currentPayload, tools: newTools } as TPayload,\n })\n }\n\n // Mark as un-deferred and track it\n undeferredTools.add(toolName)\n\n const newTools = [...currentPayload.tools]\n newTools[toolIndex] = { ...newTools[toolIndex], defer_loading: false }\n\n consola.info(\n `[DeferredToolRetry] Attempt ${context.attempt + 1}/${context.maxRetries + 1}: `\n + `Un-deferring tool \"${toolName}\" and retrying`,\n )\n\n return Promise.resolve({\n action: \"retry\",\n payload: { ...currentPayload, tools: newTools },\n meta: { undeferredTool: toolName },\n })\n },\n }\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** Parse tool name from the error response JSON */\nfunction parseToolReferenceFromResponse(responseText: string): string | null {\n try {\n const parsed = JSON.parse(responseText) as { error?: { message?: string } }\n const message = parsed.error?.message\n if (!message) return null\n return parseToolReferenceError(message)\n } catch {\n // Try raw text match as fallback\n return parseToolReferenceError(responseText)\n }\n}\n","/**\n * Anthropic /v1/messages route handler.\n * Parses payload, resolves model, processes system prompt,\n * and orchestrates completion (streaming / non-streaming).\n */\n\nimport type { ServerSentEventMessage } from \"fetch-event-stream\"\nimport type { Context } from \"hono\"\n\nimport consola from \"consola\"\nimport { SSEStreamingApi, streamSSE } from \"hono/streaming\"\n\nimport type { HeadersCapture, RequestContext } from \"~/lib/context/request\"\nimport type { MessageContent, ToolDefinition } from \"~/lib/history\"\nimport type { PreprocessInfo, SseEventRecord } from \"~/lib/history/store\"\nimport type { MessagesPayload } from \"~/types/api/anthropic\"\n\nimport { executeWithAdaptiveRateLimit } from \"~/lib/adaptive-rate-limiter\"\nimport { type AnthropicAutoTruncateResult, autoTruncateAnthropic } from \"~/lib/anthropic/auto-truncate\"\nimport { createAnthropicMessages, type AnthropicMessageResponse } from \"~/lib/anthropic/client\"\nimport { buildMessageMapping } from \"~/lib/anthropic/message-mapping\"\nimport { preprocessTools } from \"~/lib/anthropic/message-tools\"\nimport {\n preprocessAnthropicMessages,\n sanitizeAnthropicMessages,\n type SanitizationStats,\n} from \"~/lib/anthropic/sanitize\"\nimport {\n createServerToolBlockFilter,\n filterServerToolBlocksFromResponse,\n logServerToolBlock,\n logServerToolBlocks,\n} from \"~/lib/anthropic/server-tool-filter\"\nimport { processAnthropicStream, supportsDirectAnthropicApi } from \"~/lib/anthropic/sse\"\nimport { createAnthropicStreamAccumulator } from \"~/lib/anthropic/stream-accumulator\"\nimport { MAX_AUTO_TRUNCATE_RETRIES } from \"~/lib/auto-truncate\"\nimport { getRequestContextManager } from \"~/lib/context/manager\"\nimport { HTTPError } from \"~/lib/error\"\nimport { resolveModelName } from \"~/lib/models/resolver\"\nimport { createStreamRepetitionChecker } from \"~/lib/repetition-detector\"\nimport { buildAnthropicResponseData, createTruncationMarker, prependMarkerToResponse } from \"~/lib/request\"\nimport { logPayloadSizeInfoAnthropic } from \"~/lib/request/payload\"\nimport { executeRequestPipeline, type FormatAdapter } from \"~/lib/request/pipeline\"\nimport { createAutoTruncateStrategy, type TruncateResult } from \"~/lib/request/strategies/auto-truncate\"\nimport { createContextManagementRetryStrategy } from \"~/lib/request/strategies/context-management-retry\"\nimport { createDeferredToolRetryStrategy } from \"~/lib/request/strategies/deferred-tool-retry\"\nimport { createNetworkRetryStrategy } from \"~/lib/request/strategies/network-retry\"\nimport { createTokenRefreshStrategy } from \"~/lib/request/strategies/token-refresh\"\nimport { state } from \"~/lib/state\"\nimport { StreamIdleTimeoutError } from \"~/lib/stream\"\nimport { processAnthropicSystem } from \"~/lib/system-prompt\"\nimport { tuiLogger } from \"~/lib/tui\"\n\n// ============================================================================\n// Main entry point — Anthropic /v1/messages completion\n// ============================================================================\n\n/**\n * Handle an Anthropic /v1/messages request.\n * Parses payload, resolves model name, processes system prompt,\n * creates RequestContext, and routes to direct Anthropic API.\n */\nexport async function handleMessages(c: Context) {\n const anthropicPayload = await c.req.json<MessagesPayload>()\n\n // Resolve model name aliases and date-suffixed versions\n // e.g., \"haiku\" → \"claude-haiku-4.5\", \"claude-sonnet-4-20250514\" → \"claude-sonnet-4\"\n const clientModel = anthropicPayload.model\n const resolvedModel = resolveModelName(clientModel)\n if (resolvedModel !== clientModel) {\n consola.debug(`Model name resolved: ${clientModel} → ${resolvedModel}`)\n anthropicPayload.model = resolvedModel\n }\n const clientModelName = clientModel !== resolvedModel ? clientModel : undefined\n\n // System prompt collection + config-based overrides (always active)\n if (anthropicPayload.system) {\n anthropicPayload.system = await processAnthropicSystem(anthropicPayload.system, anthropicPayload.model)\n }\n\n // Get tracking ID\n const tuiLogId = c.get(\"tuiLogId\") as string | undefined\n\n // Route validation BEFORE creating RequestContext — prevents dangling history entries\n // when routing fails (reqCtx.create() triggers history insertion, and a subsequent throw\n // without reqCtx.fail() would leave an entry with no response)\n const routingDecision = supportsDirectAnthropicApi(anthropicPayload.model)\n if (!routingDecision.supported) {\n const msg = `Model \"${anthropicPayload.model}\" does not support /v1/messages: ${routingDecision.reason}`\n throw new HTTPError(msg, 400, msg)\n }\n consola.debug(`[AnthropicRouting] ${anthropicPayload.model}: ${routingDecision.reason}`)\n\n // Create request context — this triggers the \"created\" event → history consumer inserts entry\n const manager = getRequestContextManager()\n const reqCtx = manager.create({ endpoint: \"anthropic-messages\", tuiLogId })\n reqCtx.setOriginalRequest({\n // Use client's original model name (before resolution/overrides)\n model: clientModelName ?? anthropicPayload.model,\n messages: anthropicPayload.messages as unknown as Array<MessageContent>,\n stream: anthropicPayload.stream ?? false,\n tools: anthropicPayload.tools as Array<ToolDefinition> | undefined,\n system: anthropicPayload.system,\n payload: anthropicPayload,\n })\n\n // Update TUI tracker with model info (immediate feedback, don't wait for event loop)\n if (tuiLogId) {\n tuiLogger.updateRequest(tuiLogId, {\n model: anthropicPayload.model,\n ...(clientModelName && { clientModel: clientModelName }),\n })\n }\n\n // Phase 1: One-time preprocessing (idempotent, before routing)\n const preprocessed = preprocessAnthropicMessages(anthropicPayload.messages)\n anthropicPayload.messages = preprocessed.messages\n const preprocessInfo = {\n strippedReadTagCount: preprocessed.strippedReadTagCount,\n dedupedToolCallCount: preprocessed.dedupedToolCallCount,\n }\n\n return handleDirectAnthropicCompletion(c, anthropicPayload, reqCtx, preprocessInfo)\n}\n\n// ============================================================================\n// Direct Anthropic completion orchestration\n// ============================================================================\n\n// Handle completion using direct Anthropic API (no translation needed)\nasync function handleDirectAnthropicCompletion(c: Context, anthropicPayload: MessagesPayload, reqCtx: RequestContext, preprocessInfo: PreprocessInfo) {\n consola.debug(\"Using direct Anthropic API path for model:\", anthropicPayload.model)\n\n // Find model for auto-truncate and usage adjustment\n const selectedModel = state.modelIndex.get(anthropicPayload.model)\n\n // Preprocess tools: inject stubs for history-referenced tools, set defer_loading,\n // add tool_search. Must run BEFORE sanitize — processToolBlocks (in sanitize) uses\n // the tools array to validate tool_use references in messages.\n const toolPreprocessed = preprocessTools(anthropicPayload)\n\n // Always sanitize messages to filter orphaned tool_result/tool_use blocks\n const { payload: initialSanitized, stats: sanitizationStats } = sanitizeAnthropicMessages(toolPreprocessed)\n const initialSanitizationInfo = toSanitizationInfo(sanitizationStats)\n\n // Record sanitization/preprocessing if anything was modified\n const hasPreprocessing = preprocessInfo.dedupedToolCallCount > 0 || preprocessInfo.strippedReadTagCount > 0\n if (\n sanitizationStats.totalBlocksRemoved > 0\n || sanitizationStats.systemReminderRemovals > 0\n || sanitizationStats.fixedNameCount > 0\n || hasPreprocessing\n ) {\n const messageMapping = buildMessageMapping(anthropicPayload.messages, initialSanitized.messages)\n reqCtx.setPipelineInfo({\n preprocessing: preprocessInfo,\n sanitization: [initialSanitizationInfo],\n messageMapping,\n })\n }\n\n // Set initial tracking tags for log display\n if (reqCtx.tuiLogId) {\n const tags: Array<string> = []\n if (initialSanitized.thinking && initialSanitized.thinking.type !== \"disabled\")\n tags.push(`thinking:${initialSanitized.thinking.type}`)\n if (tags.length > 0) tuiLogger.updateRequest(reqCtx.tuiLogId, { tags })\n }\n\n // Build adapter and strategy for the pipeline\n const headersCapture: HeadersCapture = {}\n const adapter: FormatAdapter<MessagesPayload> = {\n format: \"anthropic-messages\",\n sanitize: (p) => sanitizeAnthropicMessages(preprocessTools(p)),\n execute: (p) =>\n executeWithAdaptiveRateLimit(() =>\n createAnthropicMessages(p, {\n resolvedModel: selectedModel,\n headersCapture,\n onPrepared: ({ wire, headers }) => {\n reqCtx.setAttemptWireRequest({\n model: typeof wire.model === \"string\" ? wire.model : anthropicPayload.model,\n messages: Array.isArray(wire.messages) ? wire.messages : [],\n payload: wire,\n headers,\n format: \"anthropic-messages\",\n })\n },\n })),\n logPayloadSize: (p) => logPayloadSizeInfoAnthropic(p, selectedModel),\n }\n\n const strategies = [\n createNetworkRetryStrategy<MessagesPayload>(),\n createTokenRefreshStrategy<MessagesPayload>(),\n createContextManagementRetryStrategy<MessagesPayload>(),\n createDeferredToolRetryStrategy<MessagesPayload>(),\n createAutoTruncateStrategy<MessagesPayload>({\n truncate: (p, model, opts) => autoTruncateAnthropic(p, model, opts) as Promise<TruncateResult<MessagesPayload>>,\n resanitize: (p) => sanitizeAnthropicMessages(preprocessTools(p)),\n isEnabled: () => state.autoTruncate,\n label: \"Anthropic\",\n }),\n ]\n\n // Track truncation result for non-streaming response marker\n let truncateResult: AnthropicAutoTruncateResult | undefined\n\n try {\n const result = await executeRequestPipeline({\n adapter,\n strategies,\n payload: initialSanitized,\n originalPayload: anthropicPayload,\n model: selectedModel,\n maxRetries: MAX_AUTO_TRUNCATE_RETRIES,\n requestContext: reqCtx,\n onRetry: (_attempt, _strategyName, newPayload, meta) => {\n // Capture truncation result for response marker\n const retryTruncateResult = meta?.truncateResult as AnthropicAutoTruncateResult | undefined\n if (retryTruncateResult) {\n truncateResult = retryTruncateResult\n }\n\n // Record rewrites for the retried payload\n const retrySanitization = meta?.sanitization as SanitizationStats | undefined\n const allSanitization = [\n initialSanitizationInfo,\n ...(retrySanitization ? [toSanitizationInfo(retrySanitization)] : []),\n ]\n const retryMessageMapping = buildMessageMapping(anthropicPayload.messages, newPayload.messages)\n reqCtx.setPipelineInfo({\n preprocessing: preprocessInfo,\n sanitization: allSanitization,\n truncation:\n retryTruncateResult ?\n {\n wasTruncated: true,\n removedMessageCount: retryTruncateResult.removedMessageCount,\n originalTokens: retryTruncateResult.originalTokens,\n compactedTokens: retryTruncateResult.compactedTokens,\n processingTimeMs: retryTruncateResult.processingTimeMs,\n }\n : undefined,\n messageMapping: retryMessageMapping,\n })\n\n // Update tracking tags\n if (reqCtx.tuiLogId) {\n const retryAttempt = (meta?.attempt as number | undefined) ?? 1\n const retryTags = [\"truncated\", `retry-${retryAttempt}`]\n if (newPayload.thinking && newPayload.thinking.type !== \"disabled\")\n retryTags.push(`thinking:${newPayload.thinking.type}`)\n tuiLogger.updateRequest(reqCtx.tuiLogId, { tags: retryTags })\n }\n },\n })\n\n // Capture HTTP headers from the final attempt for history recording\n reqCtx.setHttpHeaders(headersCapture)\n\n const response = result.response\n const effectivePayload = result.effectivePayload as MessagesPayload\n\n // Check if response is streaming (AsyncIterable)\n if (Symbol.asyncIterator in (response as object)) {\n consola.debug(\"Streaming response from Copilot (direct Anthropic)\")\n reqCtx.transition(\"streaming\")\n\n return streamSSE(c, async (stream) => {\n const clientAbort = new AbortController()\n stream.onAbort(() => clientAbort.abort())\n\n await handleDirectAnthropicStreamingResponse({\n stream,\n response: response as AsyncIterable<ServerSentEventMessage>,\n anthropicPayload: effectivePayload,\n reqCtx,\n clientAbortSignal: clientAbort.signal,\n })\n })\n }\n\n // Non-streaming response\n return handleDirectAnthropicNonStreamingResponse(c, response as AnthropicMessageResponse, reqCtx, truncateResult)\n } catch (error) {\n reqCtx.setHttpHeaders(headersCapture)\n reqCtx.fail(anthropicPayload.model, error)\n throw error\n }\n}\n\n// ============================================================================\n// Response handlers (streaming / non-streaming)\n// ============================================================================\n\n/** Options for handleDirectAnthropicStreamingResponse */\ninterface DirectAnthropicStreamHandlerOptions {\n stream: SSEStreamingApi\n response: AsyncIterable<ServerSentEventMessage>\n anthropicPayload: MessagesPayload\n reqCtx: RequestContext\n /** Abort signal that fires when the downstream client disconnects */\n clientAbortSignal?: AbortSignal\n}\n\n/** Handle streaming direct Anthropic response (passthrough SSE events) */\nasync function handleDirectAnthropicStreamingResponse(opts: DirectAnthropicStreamHandlerOptions) {\n const { stream, response, anthropicPayload, reqCtx, clientAbortSignal } = opts\n const acc = createAnthropicStreamAccumulator()\n\n // Repetition detection — feed text deltas and log warning on first detection\n const checkRepetition = createStreamRepetitionChecker(anthropicPayload.model)\n\n // SSE event recording for debugging (excludes high-volume content_block_delta and ping)\n const sseEvents: Array<SseEventRecord> = []\n\n // Streaming metrics for TUI footer and debug timing\n const streamStartMs = Date.now()\n let bytesIn = 0\n let eventsIn = 0\n let currentBlockType = \"\"\n let firstEventLogged = false\n\n // Server tool block filter — always active, matching vscode-copilot-chat behavior.\n // Server tool blocks (server_tool_use, tool_search_tool_result, etc.) are server-side\n // artifacts that clients don't expect. The reference implementation (vscode-copilot-chat)\n // intercepts these unconditionally and never forwards raw blocks to the consumer.\n const serverToolFilter = createServerToolBlockFilter()\n\n try {\n for await (const { raw: rawEvent, parsed } of processAnthropicStream(response, acc, clientAbortSignal)) {\n const dataLen = rawEvent.data?.length ?? 0\n bytesIn += dataLen\n eventsIn++\n\n // Record non-delta SSE events for history debugging\n if (parsed && parsed.type !== \"content_block_delta\" && parsed.type !== \"ping\") {\n sseEvents.push({\n offsetMs: Date.now() - streamStartMs,\n type: parsed.type,\n data: parsed,\n })\n }\n\n // Debug: log first event arrival (measures TTFB from stream perspective)\n if (!firstEventLogged) {\n const eventType = parsed?.type ?? \"keepalive\"\n consola.debug(`[Stream] First event at +${Date.now() - streamStartMs}ms (${eventType})`)\n firstEventLogged = true\n }\n\n // Debug: log content block boundaries with timing\n if (parsed?.type === \"content_block_start\") {\n currentBlockType = (parsed.content_block as { type: string }).type\n consola.debug(`[Stream] Block #${parsed.index} start: ${currentBlockType} at +${Date.now() - streamStartMs}ms`)\n\n // Log server tool information (before filtering, so info is never lost)\n const block = parsed.content_block as unknown as Record<string, unknown> & { type: string }\n logServerToolBlock(block)\n } else if (parsed?.type === \"content_block_stop\") {\n const offset = Date.now() - streamStartMs\n consola.debug(\n `[Stream] Block #${parsed.index} stop (${currentBlockType}) at +${offset}ms, cumulative ↓${bytesIn}B ${eventsIn}ev`,\n )\n currentBlockType = \"\"\n }\n\n // Update TUI footer with streaming progress\n if (reqCtx.tuiLogId) {\n tuiLogger.updateRequest(reqCtx.tuiLogId, {\n streamBytesIn: bytesIn,\n streamEventsIn: eventsIn,\n streamBlockType: currentBlockType,\n })\n }\n\n // Check for repetitive output in text deltas\n if (parsed?.type === \"content_block_delta\") {\n const delta = parsed.delta as { type: string; text?: string }\n if (delta.type === \"text_delta\" && delta.text) {\n checkRepetition(delta.text)\n }\n }\n\n // Forward event to client, filtering server tool blocks\n const forwardData = serverToolFilter.rewriteEvent(parsed, rawEvent.data ?? \"\")\n if (forwardData === null) continue\n\n await stream.writeSSE({\n data: forwardData,\n event: rawEvent.event,\n id: rawEvent.id !== undefined ? String(rawEvent.id) : undefined,\n retry: rawEvent.retry,\n })\n }\n\n // Debug: stream completion summary\n const summaryParts = [`↓${bytesIn}B ${eventsIn}ev in ${Date.now() - streamStartMs}ms`]\n if (acc.toolSearchRequests > 0) summaryParts.push(`tool_search:${acc.toolSearchRequests}`)\n consola.debug(`[Stream] Completed: ${summaryParts.join(\" \")}`)\n\n // Record SSE events for history debugging (must be before complete/fail which calls toHistoryEntry)\n reqCtx.setSseEvents(sseEvents)\n\n if (acc.streamError) {\n reqCtx.fail(acc.model || anthropicPayload.model, new Error(`${acc.streamError.type}: ${acc.streamError.message}`))\n } else {\n const responseData = buildAnthropicResponseData(acc, anthropicPayload.model)\n reqCtx.complete(responseData)\n }\n } catch (error) {\n consola.error(\"Direct Anthropic stream error:\", error)\n reqCtx.fail(acc.model || anthropicPayload.model, error)\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n const errorType = error instanceof StreamIdleTimeoutError ? \"timeout_error\" : \"api_error\"\n await stream.writeSSE({\n event: \"error\",\n data: JSON.stringify({\n type: \"error\",\n error: { type: errorType, message: errorMessage },\n }),\n })\n }\n}\n\n/** Handle non-streaming direct Anthropic response */\nfunction handleDirectAnthropicNonStreamingResponse(\n c: Context,\n response: AnthropicMessageResponse,\n reqCtx: RequestContext,\n truncateResult: AnthropicAutoTruncateResult | undefined,\n) {\n reqCtx.complete({\n success: true,\n model: response.model,\n usage: {\n input_tokens: response.usage.input_tokens,\n output_tokens: response.usage.output_tokens,\n cache_read_input_tokens: response.usage.cache_read_input_tokens ?? undefined,\n cache_creation_input_tokens: response.usage.cache_creation_input_tokens ?? undefined,\n },\n stop_reason: response.stop_reason ?? undefined,\n content: { role: \"assistant\", content: response.content },\n })\n\n // Add truncation marker to response if verbose mode and truncation occurred\n let finalResponse = response\n if (state.verbose && truncateResult?.wasTruncated) {\n const marker = createTruncationMarker(truncateResult)\n finalResponse = prependMarkerToResponse(response, marker)\n }\n\n // Filter server tool blocks from non-streaming response (always active)\n logServerToolBlocks(finalResponse.content as unknown as Array<Record<string, unknown> & { type: string }>)\n finalResponse = filterServerToolBlocksFromResponse(finalResponse)\n\n return c.json(finalResponse)\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** Convert SanitizationStats to the format expected by rewrites */\nfunction toSanitizationInfo(stats: SanitizationStats) {\n return {\n totalBlocksRemoved: stats.totalBlocksRemoved,\n orphanedToolUseCount: stats.orphanedToolUseCount,\n orphanedToolResultCount: stats.orphanedToolResultCount,\n fixedNameCount: stats.fixedNameCount,\n emptyTextBlocksRemoved: stats.emptyTextBlocksRemoved,\n systemReminderRemovals: stats.systemReminderRemovals,\n }\n}\n","import { Hono } from \"hono\"\n\nimport { forwardError } from \"~/lib/error\"\n\nimport { handleCountTokens } from \"./count-tokens-handler\"\nimport { handleMessages } from \"./handler\"\n\nexport const messagesRoutes = new Hono()\n\nmessagesRoutes.post(\"/\", async (c) => {\n try {\n return await handleMessages(c)\n } catch (error) {\n return forwardError(c, error)\n }\n})\n\nmessagesRoutes.post(\"/count_tokens\", async (c) => {\n try {\n return await handleCountTokens(c)\n } catch (error) {\n return forwardError(c, error)\n }\n})\n","import { Hono } from \"hono\"\n\nimport type { Model } from \"~/lib/models/client\"\n\nimport { forwardError } from \"~/lib/error\"\nimport { cacheModels } from \"~/lib/models/client\"\nimport { state } from \"~/lib/state\"\n\nexport const modelsRoutes = new Hono()\n\nconst EPOCH_ISO = new Date(0).toISOString()\n\nfunction formatModel(model: Model) {\n return {\n id: model.id,\n object: \"model\" as const,\n type: \"model\" as const,\n created: 0, // No date available from source\n created_at: EPOCH_ISO, // No date available from source\n owned_by: model.vendor,\n display_name: model.name,\n capabilities: model.capabilities,\n }\n}\n\nfunction formatModelDetail(model: Model) {\n return {\n ...formatModel(model),\n version: model.version,\n preview: model.preview,\n model_picker_enabled: model.model_picker_enabled,\n model_picker_category: model.model_picker_category,\n supported_endpoints: model.supported_endpoints,\n billing: model.billing,\n }\n}\n\nmodelsRoutes.get(\"/\", async (c) => {\n try {\n if (!state.models) {\n // This should be handled by startup logic, but as a fallback.\n await cacheModels()\n }\n\n const detail = c.req.query(\"detail\") === \"true\"\n const formatter = detail ? formatModelDetail : formatModel\n const models = state.models?.data.map((m) => formatter(m))\n\n return c.json({\n object: \"list\",\n data: models,\n has_more: false,\n })\n } catch (error) {\n return forwardError(c, error)\n }\n})\n\nmodelsRoutes.get(\"/:model\", async (c) => {\n try {\n if (!state.models) {\n await cacheModels()\n }\n\n const modelId = c.req.param(\"model\")\n const model = state.modelIndex.get(modelId)\n\n if (!model) {\n return c.json(\n {\n error: {\n message: `The model '${modelId}' does not exist`,\n type: \"invalid_request_error\",\n param: \"model\",\n code: \"model_not_found\",\n },\n },\n 404,\n )\n }\n\n return c.json(formatModelDetail(model))\n } catch (error) {\n return forwardError(c, error)\n }\n})\n","/**\n * Handler for inbound OpenAI Responses API requests.\n * Routes directly to Copilot /responses endpoint.\n * Models that do not support /responses get a 400 error.\n */\n\nimport type { ServerSentEventMessage } from \"fetch-event-stream\"\nimport type { Context } from \"hono\"\n\nimport consola from \"consola\"\nimport { streamSSE } from \"hono/streaming\"\n\nimport type { HeadersCapture, RequestContext } from \"~/lib/context/request\"\nimport type { ResponsesPayload, ResponsesResponse, ResponsesStreamEvent } from \"~/types/api/openai-responses\"\n\nimport { getRequestContextManager } from \"~/lib/context/manager\"\nimport { HTTPError } from \"~/lib/error\"\nimport { ENDPOINT, isResponsesSupported } from \"~/lib/models/endpoint\"\nimport { resolveModelName } from \"~/lib/models/resolver\"\nimport { responsesInputToMessages, responsesOutputToContent } from \"~/lib/openai/responses-conversion\"\nimport {\n accumulateResponsesStreamEvent,\n createResponsesStreamAccumulator,\n} from \"~/lib/openai/responses-stream-accumulator\"\nimport { executeRequestPipeline } from \"~/lib/request/pipeline\"\nimport { buildResponsesResponseData } from \"~/lib/request/recording\"\nimport { getShutdownSignal } from \"~/lib/shutdown\"\nimport { state } from \"~/lib/state\"\nimport { STREAM_ABORTED, StreamIdleTimeoutError, combineAbortSignals, raceIteratorNext } from \"~/lib/stream\"\nimport { processResponsesInstructions } from \"~/lib/system-prompt\"\nimport { tuiLogger } from \"~/lib/tui\"\n\nimport { createResponsesAdapter, createResponsesStrategies, normalizeCallIds } from \"./pipeline\"\n\n// Re-export conversion functions (other modules may import from ./handler)\n\n/** Handle an inbound Responses API request */\nexport async function handleResponses(c: Context) {\n let payload = await c.req.json<ResponsesPayload>()\n\n // Resolve model name aliases\n const clientModel = payload.model\n const resolvedModel = resolveModelName(clientModel)\n if (resolvedModel !== clientModel) {\n consola.debug(`Model name resolved: ${clientModel} → ${resolvedModel}`)\n payload.model = resolvedModel\n }\n\n // Validate that the model supports /responses endpoint\n const selectedModel = state.modelIndex.get(payload.model)\n if (!isResponsesSupported(selectedModel)) {\n const msg = `Model \"${payload.model}\" does not support the ${ENDPOINT.RESPONSES} endpoint`\n throw new HTTPError(msg, 400, msg)\n }\n\n // Process system prompt (overrides, prepend, append from config)\n payload.instructions = await processResponsesInstructions(payload.instructions, payload.model)\n\n // Normalize call IDs before pipeline (call_ → fc_)\n if (state.normalizeResponsesCallIds) {\n payload = normalizeCallIds(payload)\n }\n\n // Get tracking ID\n const tuiLogId = c.get(\"tuiLogId\") as string | undefined\n\n // Create request context (Responses API is a distinct OpenAI-format endpoint)\n const manager = getRequestContextManager()\n const reqCtx = manager.create({ endpoint: \"openai-responses\", tuiLogId })\n\n // Record original request for history\n reqCtx.setOriginalRequest({\n model: clientModel,\n messages: responsesInputToMessages(payload.input),\n stream: payload.stream ?? false,\n tools: payload.tools,\n system: payload.instructions ?? undefined,\n payload,\n })\n\n // Update TUI tracker with model info\n if (tuiLogId) {\n tuiLogger.updateRequest(tuiLogId, {\n model: payload.model,\n ...(clientModel !== payload.model && { clientModel }),\n })\n }\n\n return handleDirectResponses({ c, payload, reqCtx })\n}\n\n// ============================================================================\n// Direct passthrough to /responses endpoint\n// ============================================================================\n\ninterface ResponsesHandlerOptions {\n c: Context\n payload: ResponsesPayload\n reqCtx: RequestContext\n}\n\n/** Pass through to Copilot /responses endpoint directly */\nasync function handleDirectResponses(opts: ResponsesHandlerOptions) {\n const { c, payload, reqCtx } = opts\n\n const selectedModel = state.modelIndex.get(payload.model)\n const headersCapture: HeadersCapture = {}\n const adapter = createResponsesAdapter(selectedModel, headersCapture, (wireRequest) => {\n reqCtx.setAttemptWireRequest(wireRequest)\n })\n const strategies = createResponsesStrategies()\n\n try {\n const pipelineResult = await executeRequestPipeline({\n adapter,\n strategies,\n payload,\n originalPayload: payload,\n model: selectedModel,\n maxRetries: 1,\n requestContext: reqCtx,\n })\n\n // Capture HTTP headers from the final attempt for history recording\n reqCtx.setHttpHeaders(headersCapture)\n\n const response = pipelineResult.response\n // Note: queueWaitMs is already accumulated by the pipeline via requestContext.addQueueWaitMs()\n\n // Determine streaming vs non-streaming based on the request payload,\n // not by inspecting the response shape (isNonStreaming checks for \"choices\"\n // which only exists in Chat Completions format, not Responses format)\n if (!payload.stream) {\n // Non-streaming response — build content from output items\n const responsesResponse = response as ResponsesResponse\n const content = responsesOutputToContent(responsesResponse.output)\n\n reqCtx.complete({\n success: true,\n model: responsesResponse.model,\n usage: {\n input_tokens: responsesResponse.usage?.input_tokens ?? 0,\n output_tokens: responsesResponse.usage?.output_tokens ?? 0,\n ...(responsesResponse.usage?.input_tokens_details?.cached_tokens && {\n cache_read_input_tokens: responsesResponse.usage.input_tokens_details.cached_tokens,\n }),\n ...(responsesResponse.usage?.output_tokens_details?.reasoning_tokens && {\n output_tokens_details: {\n reasoning_tokens: responsesResponse.usage.output_tokens_details.reasoning_tokens,\n },\n }),\n },\n stop_reason: responsesResponse.status,\n content,\n })\n return c.json(responsesResponse)\n }\n\n // Streaming response — forward Responses SSE events directly\n consola.debug(\"Streaming response (/responses)\")\n reqCtx.transition(\"streaming\")\n\n return streamSSE(c, async (stream) => {\n const clientAbort = new AbortController()\n stream.onAbort(() => clientAbort.abort())\n\n const acc = createResponsesStreamAccumulator()\n const idleTimeoutMs = state.streamIdleTimeout * 1000\n\n // Streaming metrics for TUI footer\n let bytesIn = 0\n let eventsIn = 0\n\n try {\n const iterator = (response as AsyncIterable<ServerSentEventMessage>)[Symbol.asyncIterator]()\n\n for (;;) {\n const abortSignal = combineAbortSignals(getShutdownSignal(), clientAbort.signal)\n const result = await raceIteratorNext(iterator.next(), { idleTimeoutMs, abortSignal })\n\n if (result === STREAM_ABORTED) break\n if (result.done) break\n\n const rawEvent = result.value\n\n if (rawEvent.data && rawEvent.data !== \"[DONE]\") {\n bytesIn += rawEvent.data.length\n eventsIn++\n\n // Update TUI footer with streaming progress\n if (reqCtx.tuiLogId) {\n tuiLogger.updateRequest(reqCtx.tuiLogId, {\n streamBytesIn: bytesIn,\n streamEventsIn: eventsIn,\n })\n }\n\n try {\n const event = JSON.parse(rawEvent.data) as ResponsesStreamEvent\n accumulateResponsesStreamEvent(event, acc)\n\n // Forward the event as-is (including SSE event type field)\n await stream.writeSSE({ event: rawEvent.event ?? event.type, data: rawEvent.data })\n } catch {\n // Ignore parse errors\n }\n }\n }\n\n // Use shared recording utility for consistent response data\n const responseData = buildResponsesResponseData(acc, payload.model)\n reqCtx.complete(responseData)\n } catch (error) {\n consola.error(\"[Responses] Stream error:\", error)\n reqCtx.fail(acc.model || payload.model, error)\n\n // Send error to client as final SSE event\n const errorMessage = error instanceof Error ? error.message : String(error)\n await stream.writeSSE({\n event: \"error\",\n data: JSON.stringify({\n error: {\n message: errorMessage,\n type: error instanceof StreamIdleTimeoutError ? \"timeout_error\" : \"server_error\",\n },\n }),\n })\n }\n })\n } catch (error) {\n reqCtx.setHttpHeaders(headersCapture)\n reqCtx.fail(payload.model, error)\n throw error\n }\n}\n\nexport { responsesInputToMessages, responsesOutputToContent } from \"~/lib/openai/responses-conversion\"\n","/**\n * OpenAI Responses API route definition.\n * Handles POST /responses and POST /v1/responses.\n */\n\nimport { Hono } from \"hono\"\n\nimport { forwardError } 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 return forwardError(c, error)\n }\n})\n","/**\n * Aggregated server status endpoint.\n * Returns health, auth, quota, rate limiter, memory, shutdown, and model counts\n * in a single request.\n */\n\nimport { Hono } from \"hono\"\n\nimport packageJson from \"../../../package.json\"\nimport { getAdaptiveRateLimiter } from \"~/lib/adaptive-rate-limiter\"\nimport { getRequestContextManager } from \"~/lib/context/manager\"\nimport { historyState } from \"~/lib/history/store\"\nimport { getMemoryPressureStats } from \"~/lib/history/memory-pressure\"\nimport { getIsShuttingDown, getShutdownPhase } from \"~/lib/shutdown\"\nimport { serverStartTime, state } from \"~/lib/state\"\nimport { getCopilotUsage, type QuotaDetail } from \"~/lib/token/copilot-client\"\n\nexport const statusRoutes = new Hono()\n\nstatusRoutes.get(\"/\", async (c) => {\n const now = Date.now()\n\n // Rate limiter status + config\n const limiter = getAdaptiveRateLimiter()\n const limiterStatus = limiter?.getStatus()\n\n // Memory pressure\n const memStats = getMemoryPressureStats()\n\n // Active request count (safe — returns 0 if manager not initialized)\n let activeCount = 0\n try {\n activeCount = getRequestContextManager().activeCount\n } catch {\n // Manager not initialized yet\n }\n\n // Copilot quota (non-blocking — null on failure)\n let quota: {\n plan: string\n resetDate: string\n chat: QuotaDetail\n completions: QuotaDetail\n premiumInteractions: QuotaDetail\n } | null = null\n try {\n const usage = await getCopilotUsage()\n quota = {\n plan: usage.copilot_plan,\n resetDate: usage.quota_reset_date,\n chat: usage.quota_snapshots.chat,\n completions: usage.quota_snapshots.completions,\n premiumInteractions: usage.quota_snapshots.premium_interactions,\n }\n } catch {\n // Quota query failed — return null, don't block the entire status response\n }\n\n return c.json({\n status: getIsShuttingDown() ? \"shutting_down\" : (state.copilotToken && state.githubToken ? \"healthy\" : \"unhealthy\"),\n uptime: serverStartTime > 0 ? Math.floor((now - serverStartTime) / 1000) : 0,\n version: packageJson.version,\n vsCodeVersion: state.vsCodeVersion ?? null,\n\n auth: {\n accountType: state.accountType,\n tokenSource: state.tokenInfo?.source ?? null,\n tokenExpiresAt: state.tokenInfo?.expiresAt ?? null,\n copilotTokenExpiresAt: state.copilotTokenInfo ? state.copilotTokenInfo.expiresAt * 1000 : null,\n },\n\n quota,\n\n activeRequests: {\n count: activeCount,\n },\n\n rateLimiter: limiterStatus\n ? {\n enabled: true,\n ...limiterStatus,\n config: limiter!.getConfig(),\n }\n : { enabled: false },\n\n memory: {\n heapUsedMB: memStats.heapUsedMB,\n heapLimitMB: memStats.heapLimitMB,\n historyEntryCount: historyState.entries.length,\n historyMaxEntries: memStats.currentMaxEntries,\n totalEvictedCount: memStats.totalEvictedCount,\n },\n\n shutdown: {\n phase: getShutdownPhase(),\n },\n\n models: {\n totalCount: state.models?.data.length ?? 0,\n availableCount: state.modelIds.size,\n },\n })\n})\n","import { Hono } from \"hono\"\n\nimport { forwardError } from \"~/lib/error\"\nimport { state } from \"~/lib/state\"\n\nexport const tokenRoutes = new Hono()\n\ntokenRoutes.get(\"/\", (c) => {\n try {\n return c.json({\n github: state.tokenInfo\n ? {\n token: state.tokenInfo.token,\n source: state.tokenInfo.source,\n expiresAt: state.tokenInfo.expiresAt ?? null,\n refreshable: state.tokenInfo.refreshable,\n }\n : null,\n copilot: state.copilotTokenInfo\n ? {\n token: state.copilotTokenInfo.token,\n expiresAt: state.copilotTokenInfo.expiresAt,\n refreshIn: state.copilotTokenInfo.refreshIn,\n }\n : null,\n })\n } catch (error) {\n return forwardError(c, error)\n }\n})\n","/** MIME type helper for history UI static assets */\nexport function getMimeType(path: string): string {\n if (path.endsWith(\".html\")) return \"text/html\"\n if (path.endsWith(\".js\")) return \"application/javascript\"\n if (path.endsWith(\".css\")) return \"text/css\"\n if (path.endsWith(\".json\")) return \"application/json\"\n if (path.endsWith(\".svg\")) return \"image/svg+xml\"\n if (path.endsWith(\".png\")) return \"image/png\"\n if (path.endsWith(\".ico\")) return \"image/x-icon\"\n return \"application/octet-stream\"\n}\n","import { Hono } from \"hono\"\nimport type { Context } from \"hono\"\nimport { existsSync } from \"node:fs\"\nimport { access, constants, readFile } from \"node:fs/promises\"\nimport { join, resolve } from \"node:path\"\n\nimport { getMimeType } from \"../history/assets\"\n\nexport const uiRoutes = new Hono()\n\n/**\n * Resolve a UI directory that exists at runtime.\n * In dev mode this file lives at src/routes/ui/ — 3 levels below project root.\n * In bundled mode (dist/main.mjs) — 1 level below project root.\n */\nfunction resolveUiDir(subpath: string): string {\n const candidates = [\n join(import.meta.dirname, \"../../..\", \"ui\", subpath),\n join(import.meta.dirname, \"..\", \"ui\", subpath),\n ]\n return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0]\n}\n\nconst uiDir = resolveUiDir(\"history-v3/dist\")\n\nasync function serveIndexHtml(c: Context) {\n try {\n await access(join(uiDir, \"index.html\"), constants.R_OK)\n const content = await readFile(join(uiDir, \"index.html\"), \"utf8\")\n return c.html(content)\n } catch {\n return c.notFound()\n }\n}\n\nasync function serveAsset(c: Context) {\n const assetsIdx = c.req.path.indexOf(\"/assets/\")\n if (assetsIdx === -1) return c.notFound()\n\n const filePath = c.req.path.slice(assetsIdx)\n const fullPath = resolve(join(uiDir, filePath))\n if (!fullPath.startsWith(uiDir)) return c.notFound()\n\n try {\n await access(fullPath, constants.R_OK)\n const content = await readFile(fullPath)\n return new Response(content, {\n headers: {\n \"Content-Type\": getMimeType(filePath),\n \"Cache-Control\": \"public, max-age=31536000, immutable\",\n },\n })\n } catch {\n return c.notFound()\n }\n}\n\nuiRoutes.get(\"/\", serveIndexHtml)\nuiRoutes.get(\"/assets/*\", serveAsset)\n","/**\n * Centralized route registration.\n * All API routes are registered here instead of scattered in server.ts.\n */\n\nimport type { Hono } from \"hono\"\nimport type { UpgradeWebSocket } from \"hono/ws\"\n\nimport { initWebSocket } from \"~/lib/ws\"\n\nimport { initResponsesWebSocket } from \"./responses/ws\"\nimport { chatCompletionRoutes } from \"./chat-completions/route\"\nimport { configRoutes } from \"./config/route\"\nimport { embeddingsRoutes } from \"./embeddings/route\"\nimport { eventLoggingRoutes } from \"./event-logging/route\"\nimport { historyRoutes } from \"./history/route\"\nimport { logsRoutes } from \"./logs/route\"\nimport { messagesRoutes } from \"./messages/route\"\nimport { modelsRoutes } from \"./models/route\"\nimport { responsesRoutes } from \"./responses/route\"\nimport { statusRoutes } from \"./status/route\"\nimport { tokenRoutes } from \"./token/route\"\nimport { uiRoutes } from \"./ui/route\"\n\n/**\n * Register all HTTP routes on the given Hono app.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function registerHttpRoutes(app: Hono) {\n // OpenAI-compatible endpoints\n app.route(\"/chat/completions\", chatCompletionRoutes)\n app.route(\"/models\", modelsRoutes)\n app.route(\"/embeddings\", embeddingsRoutes)\n app.route(\"/responses\", responsesRoutes)\n\n // OpenAI-compatible with /v1 prefix\n app.route(\"/v1/chat/completions\", chatCompletionRoutes)\n app.route(\"/v1/models\", modelsRoutes)\n app.route(\"/v1/embeddings\", embeddingsRoutes)\n app.route(\"/v1/responses\", responsesRoutes)\n\n // Anthropic-compatible endpoints\n app.route(\"/v1/messages\", messagesRoutes)\n app.route(\"/api/event_logging\", eventLoggingRoutes)\n\n // Management API\n app.route(\"/api/status\", statusRoutes)\n app.route(\"/api/tokens\", tokenRoutes)\n app.route(\"/api/config\", configRoutes)\n app.route(\"/api/logs\", logsRoutes)\n\n // History API and standalone Web UI entry\n app.route(\"/history\", historyRoutes)\n app.route(\"/ui\", uiRoutes)\n}\n\n/**\n * Register all WebSocket routes on the given Hono app.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function registerWsRoutes(app: Hono, wsUpgrade: UpgradeWebSocket<any>) {\n initWebSocket(app, wsUpgrade)\n initResponsesWebSocket(app, wsUpgrade)\n}\n","import consola from \"consola\"\nimport { Hono } from \"hono\"\nimport { cors } from \"hono/cors\"\nimport { trimTrailingSlash } from \"hono/trailing-slash\"\n\nimport { applyConfigToState } from \"./lib/config/config\"\nimport { forwardError } from \"./lib/error\"\nimport { state } from \"./lib/state\"\nimport { ensureValidCopilotToken } from \"./lib/token\"\nimport { tuiMiddleware } from \"./lib/tui\"\nimport { registerHttpRoutes } from \"./routes\"\n\nexport const server = new Hono()\n\n// Global error handler - catches any unhandled errors from route handlers\nserver.onError((error, c) => {\n // WebSocket errors after upgrade - connection is already upgraded,\n // cannot send HTTP response; log at debug level since these are normal\n // (e.g. client disconnect)\n if (c.req.header(\"upgrade\")?.toLowerCase() === \"websocket\") {\n consola.debug(\"WebSocket error:\", error)\n return c.text(\"\", 500)\n }\n\n consola.error(`Unhandled route error in ${c.req.method} ${c.req.path}:`, error)\n return forwardError(c, error)\n})\n\n// Browser auto-requests (favicon, devtools config) — return 204 silently\n// to avoid [FAIL] 404 noise in TUI logs.\nconst browserProbePaths = new Set([\"/favicon.ico\", \"/.well-known/appspecific/com.chrome.devtools.json\"])\n\nserver.notFound((c) => {\n if (browserProbePaths.has(c.req.path)) {\n return c.body(null, 204)\n }\n return c.json({ error: \"Not Found\" }, 404)\n})\n\n// Config hot-reload: re-apply config.yaml settings before each request.\n// loadConfig() is mtime-cached — only costs one stat() syscall when config is unchanged.\n// Also proactively ensure the Copilot token is valid — if the last background\n// refresh failed or the token is about to expire, try refreshing now rather than\n// waiting for a 401 from the upstream API.\nserver.use(async (_c, next) => {\n await applyConfigToState()\n await ensureValidCopilotToken()\n await next()\n})\n\nserver.use(tuiMiddleware())\nserver.use(cors())\nserver.use(trimTrailingSlash())\n\nserver.get(\"/\", (c) => c.text(\"Server running\"))\n\n// Health check endpoint for container orchestration (Docker, Kubernetes)\nserver.get(\"/health\", (c) => {\n const healthy = Boolean(state.copilotToken && state.githubToken)\n return c.json(\n {\n status: healthy ? \"healthy\" : \"unhealthy\",\n checks: {\n copilotToken: Boolean(state.copilotToken),\n githubToken: Boolean(state.githubToken),\n models: Boolean(state.models),\n },\n },\n healthy ? 200 : 503,\n )\n})\n\n// Register HTTP routes. WebSocket routes are injected later in start.ts after\n// a shared adapter is created for the concrete runtime/server instance.\nregisterHttpRoutes(server)\n","#!/usr/bin/env node\n\nimport { defineCommand } from \"citty\"\nimport consola from \"consola\"\nimport pc from \"picocolors\"\nimport { startServer } from \"./lib/serve\"\n\nimport type { Model } from \"./lib/models/client\"\n\nimport packageJson from \"../package.json\"\nimport { initAdaptiveRateLimiter } from \"./lib/adaptive-rate-limiter\"\nimport { loadPersistedLimits } from \"./lib/auto-truncate\"\nimport { applyConfigToState } from \"./lib/config/config\"\nimport { PATHS, ensurePaths } from \"./lib/config/paths\"\nimport { registerContextConsumers } from \"./lib/context/consumers\"\nimport { initRequestContextManager } from \"./lib/context/manager\"\nimport { cacheVSCodeVersion } from \"./lib/copilot-api\"\nimport { initHistory, startMemoryPressureMonitor } from \"./lib/history\"\nimport { cacheModels } from \"./lib/models/client\"\nimport { getEffectiveEndpoints } from \"./lib/models/endpoint\"\nimport { initProxy } from \"./lib/proxy\"\nimport { setServerInstance, setupShutdownHandlers, waitForShutdown } from \"./lib/shutdown\"\nimport { setCliState, setServerStartTime, state } from \"./lib/state\"\nimport { initTokenManagers } from \"./lib/token\"\nimport { initTuiLogger } from \"./lib/tui\"\nimport { createWebSocketAdapter, setConnectedDataFactory } from \"./lib/ws\"\nimport { registerWsRoutes } from \"./routes\"\nimport { server } from \"./server\"\n\n/** Format limit values as \"Xk\" or \"?\" if not available */\nfunction formatLimit(value?: number): string {\n return value ? `${Math.round(value / 1000)}k` : \"?\"\n}\n\n/**\n * Format a model as 3 lines: main info, features, and supported endpoints.\n *\n * Example output:\n * - claude-opus-4.6-1m (Anthropic) ctx:1000k prp: 936k out: 64k\n * features: adaptive-thinking, thinking, streaming, vision, tool-calls\n * endpoints: /v1/messages, /chat/completions\n */\nfunction formatModelInfo(model: Model): string {\n const limits = model.capabilities?.limits\n const supports = model.capabilities?.supports\n\n const contextK = formatLimit(limits?.max_context_window_tokens)\n const promptK = formatLimit(limits?.max_prompt_tokens)\n const outputK = formatLimit(limits?.max_output_tokens)\n\n const label = `${model.id} (${model.vendor})`\n const padded = label.length > 45 ? `${label.slice(0, 42)}...` : label.padEnd(45)\n const mainLine =\n ` - ${padded} ` + `ctx:${contextK.padStart(5)} ` + `prp:${promptK.padStart(5)} ` + `out:${outputK.padStart(5)}`\n\n const features = [\n ...Object.entries(supports ?? {})\n .filter(([, value]) => value === true)\n .map(([key]) => key.replaceAll(\"_\", \"-\")),\n supports?.max_thinking_budget && \"thinking\",\n model.capabilities?.type === \"embeddings\" && \"embeddings\",\n model.preview && \"preview\",\n ]\n .filter(Boolean)\n .join(\", \")\n const featLine = features ? pc.dim(` features: ${features}`) : \"\"\n\n const endpoints = getEffectiveEndpoints(model)\n const endpLine = pc.dim(` endpoints: ${endpoints?.join(\", \") ?? \"(unknown)\"}`)\n\n return [mainLine, featLine, endpLine].filter(Boolean).join(\"\\n\")\n}\n\n/** Parse an integer from a string, returning a default if the result is NaN. */\nfunction parseIntOrDefault(value: string, defaultValue: number): number {\n const parsed = Number.parseInt(value, 10)\n return Number.isFinite(parsed) ? parsed : defaultValue\n}\n\nconst VALID_ACCOUNT_TYPES = [\"individual\", \"business\", \"enterprise\"] as const\n\ninterface RunServerOptions {\n port: number\n host?: string\n verbose: boolean\n accountType: \"individual\" | \"business\" | \"enterprise\"\n // Adaptive rate limiting (disabled if rateLimit is false)\n rateLimit: boolean\n githubToken?: string\n showGitHubToken: boolean\n /** Explicit proxy URL (CLI --proxy). Takes precedence over config.yaml and env vars. */\n proxy?: string\n httpProxyFromEnv: boolean\n autoTruncate: boolean\n}\n\nexport async function runServer(options: RunServerOptions): Promise<void> {\n // ===========================================================================\n // Phase 0: Validate critical options\n // ===========================================================================\n if (!VALID_ACCOUNT_TYPES.includes(options.accountType)) {\n consola.error(`Invalid account type: \"${options.accountType}\". Must be one of: ${VALID_ACCOUNT_TYPES.join(\", \")}`)\n process.exit(1)\n }\n\n // ===========================================================================\n // Phase 1: Logging and Verbose Mode\n // ===========================================================================\n if (options.verbose) {\n consola.level = 5\n setCliState({ verbose: true })\n }\n\n // ===========================================================================\n // Phase 2: Version and Configuration Display\n // ===========================================================================\n consola.info(`copilot-api v${packageJson.version}`)\n\n // Set global state from CLI options\n setCliState({\n accountType: options.accountType,\n showGitHubToken: options.showGitHubToken,\n autoTruncate: options.autoTruncate,\n })\n\n // ===========================================================================\n // Phase 2.5: Load config.yaml and apply runtime settings\n // ===========================================================================\n // ensurePaths must run first so the config directory exists\n await ensurePaths()\n consola.info(`Data directory: ${PATHS.APP_DIR}`)\n\n const config = await applyConfigToState()\n\n // ===========================================================================\n // Phase 2.6: Initialize proxy (must be before any network requests)\n // ===========================================================================\n // Priority: CLI --proxy > config.yaml proxy > env vars (--http-proxy-from-env)\n const proxyUrl = options.proxy ?? config.proxy\n initProxy({ url: proxyUrl, fromEnv: !proxyUrl && options.httpProxyFromEnv })\n\n // Rate limiter configuration (used in Phase 3)\n const rlConfig = config.rate_limiter\n const rlRetryInterval = rlConfig?.retry_interval ?? 10\n const rlRequestInterval = rlConfig?.request_interval ?? 10\n const rlRecoveryTimeout = rlConfig?.recovery_timeout ?? 10\n const rlConsecutiveSuccesses = rlConfig?.consecutive_successes ?? 5\n\n // ===========================================================================\n // Phase 3: Initialize Internal Services (rate limiter, history)\n // ===========================================================================\n if (options.rateLimit) {\n initAdaptiveRateLimiter({\n baseRetryIntervalSeconds: rlRetryInterval,\n requestIntervalSeconds: rlRequestInterval,\n recoveryTimeoutMinutes: rlRecoveryTimeout,\n consecutiveSuccessesForRecovery: rlConsecutiveSuccesses,\n })\n }\n\n initHistory(true, state.historyLimit)\n startMemoryPressureMonitor()\n\n // Initialize request context manager and register event consumers\n // Must be after initHistory so history store is ready to receive events\n const contextManager = initRequestContextManager()\n registerContextConsumers(contextManager)\n\n // Provide active requests snapshot for WS connected events\n setConnectedDataFactory(() =>\n contextManager.getAll().map((ctx) => ({\n id: ctx.id,\n endpoint: ctx.endpoint,\n state: ctx.state,\n startTime: ctx.startTime,\n durationMs: ctx.durationMs,\n model: ctx.originalRequest?.model,\n stream: ctx.originalRequest?.stream,\n })),\n )\n\n // Start stale request reaper (periodic cleanup of stuck active contexts)\n contextManager.startReaper()\n\n // Initialize TUI request tracking (renderer was created in main.ts via initConsolaReporter)\n initTuiLogger()\n\n // ===========================================================================\n // Phase 4: External Dependencies (network)\n // ===========================================================================\n // cacheVSCodeVersion is independent network call\n await cacheVSCodeVersion()\n\n // Initialize token management and authenticate\n await initTokenManagers({ cliToken: options.githubToken })\n\n // Fetch available models from Copilot API\n try {\n await cacheModels()\n } catch (error) {\n consola.error(\"Failed to fetch models from Copilot API:\", error instanceof Error ? error.message : error)\n consola.error(\n `Verify that --account-type \"${state.accountType}\" is correct. `\n + `Available types: ${VALID_ACCOUNT_TYPES.join(\", \")}`,\n )\n process.exit(1)\n }\n\n consola.info(`Available models:\\n${state.models?.data.map((m) => formatModelInfo(m)).join(\"\\n\")}`)\n\n // Load previously learned auto-truncate limits (calibration + token limits)\n await loadPersistedLimits()\n\n // ===========================================================================\n // Phase 5: Start Server\n // ===========================================================================\n const displayHost = options.host ?? \"localhost\"\n const serverUrl = `http://${displayHost}:${options.port}`\n\n // Initialize WebSocket support using a single shared adapter.\n // A single createNodeWebSocket instance avoids multiple `upgrade` listeners\n // on the Node HTTP server, which would cause ERR_STREAM_WRITE_AFTER_END\n // when one handler consumes the socket and the other tries to reject.\n const wsAdapter = await createWebSocketAdapter(server)\n registerWsRoutes(server, wsAdapter.upgradeWebSocket)\n\n consola.info(`Web UI: ${serverUrl}/ui`)\n\n // Import hono/bun websocket handler for Bun's WebSocket support.\n // Bun.serve() requires an explicit `websocket` handler object alongside `fetch`\n // for WebSocket upgrades to work. Without this, server.upgrade() in\n // hono/bun's upgradeWebSocket middleware silently fails.\n const bunWebSocket = typeof globalThis.Bun !== \"undefined\" ? (await import(\"hono/bun\")).websocket : undefined\n\n let serverInstance\n try {\n serverInstance = await startServer({\n fetch: server.fetch,\n port: options.port,\n hostname: options.host,\n bunWebSocket,\n })\n } catch (error) {\n consola.error(`Failed to start server on port ${options.port}. Is the port already in use?`, error)\n process.exit(1)\n }\n\n consola.info(`Listening on ${serverUrl}`)\n setServerStartTime(Date.now())\n\n // Store server instance and register signal handlers for graceful shutdown.\n // Order matters: setServerInstance must be called before setupShutdownHandlers\n // so the handler has access to the server instance when closing.\n setServerInstance(serverInstance)\n setupShutdownHandlers()\n\n // Inject the single shared WebSocket upgrade handler into Node.js HTTP server (no-op under Bun)\n if (wsAdapter.injectWebSocket && serverInstance.nodeServer) {\n wsAdapter.injectWebSocket(serverInstance.nodeServer)\n }\n\n // Block until a shutdown signal (SIGINT/SIGTERM) is received.\n // This prevents runMain() from returning, which would trigger\n // process.exit(0) in main.ts (needed for one-shot commands).\n await waitForShutdown()\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 host: {\n alias: \"H\",\n type: \"string\",\n description: \"Host/interface to bind to (e.g., 127.0.0.1 for localhost only, 0.0.0.0 for all interfaces)\",\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 \"rate-limit\": {\n type: \"boolean\",\n default: true,\n description: \"Adaptive rate limiting (disable with --no-rate-limit)\",\n },\n \"github-token\": {\n alias: \"g\",\n type: \"string\",\n description: \"Provide GitHub token directly (must be generated using the `auth` subcommand)\",\n },\n \"show-github-token\": {\n type: \"boolean\",\n default: false,\n description: \"Show GitHub token in logs (use --verbose for Copilot token refresh logs)\",\n },\n proxy: {\n type: \"string\",\n description:\n \"Proxy URL for all outgoing requests (http://, https://, socks5://, socks5h://). Overrides config.yaml and env vars.\",\n },\n \"http-proxy-from-env\": {\n type: \"boolean\",\n default: true,\n description: \"Use HTTP proxy from environment variables (disable with --no-http-proxy-from-env)\",\n },\n \"auto-truncate\": {\n type: \"boolean\",\n default: true,\n description:\n \"Reactive auto-truncate: retries with truncated payload on limit errors (disable with --no-auto-truncate)\",\n },\n },\n run({ args }) {\n // Check for unknown arguments\n // Known args include both kebab-case (as defined) and camelCase (citty auto-converts)\n const knownArgs = new Set([\n \"_\",\n // port\n \"port\",\n \"p\",\n // host\n \"host\",\n \"H\",\n // verbose\n \"verbose\",\n \"v\",\n // account-type\n \"account-type\",\n \"accountType\",\n \"a\",\n // rate-limit (citty handles --no-rate-limit via built-in negation)\n \"rate-limit\",\n \"rateLimit\",\n // github-token\n \"github-token\",\n \"githubToken\",\n \"g\",\n // show-github-token\n \"show-github-token\",\n \"showGithubToken\",\n // proxy\n \"proxy\",\n // http-proxy-from-env (citty handles --no-http-proxy-from-env via built-in negation)\n \"http-proxy-from-env\",\n \"httpProxyFromEnv\",\n // auto-truncate (citty handles --no-auto-truncate via built-in negation)\n \"auto-truncate\",\n \"autoTruncate\",\n ])\n const unknownArgs = Object.keys(args).filter((key) => !knownArgs.has(key))\n if (unknownArgs.length > 0) {\n consola.warn(`Unknown argument(s): ${unknownArgs.map((a) => `--${a}`).join(\", \")}`)\n }\n\n return runServer({\n port: parseIntOrDefault(args.port, 4141),\n host: args.host,\n verbose: args.verbose,\n accountType: args[\"account-type\"] as \"individual\" | \"business\" | \"enterprise\",\n rateLimit: args[\"rate-limit\"],\n githubToken: args[\"github-token\"],\n showGitHubToken: args[\"show-github-token\"],\n proxy: args.proxy,\n httpProxyFromEnv: args[\"http-proxy-from-env\"],\n autoTruncate: args[\"auto-truncate\"],\n })\n },\n})\n","#!/usr/bin/env node\n\nimport { defineCommand, runMain } from \"citty\"\nimport consola from \"consola\"\n\nimport { auth } from \"./auth\"\nimport { checkUsage } from \"./check-usage\"\nimport { debug } from \"./debug\"\nimport { initConsolaReporter } from \"./lib/tui\"\nimport { listClaudeCode } from \"./list-claude-code\"\nimport { logout } from \"./logout\"\nimport { setupClaudeCode } from \"./setup-claude-code\"\nimport { start } from \"./start\"\n\n// Initialize console reporter before any logging\ninitConsolaReporter()\n\n// Global error handlers - catch errors from timers, callbacks, etc.\n// that would otherwise cause a silent process exit\nprocess.on(\"uncaughtException\", (error) => {\n consola.error(\"Uncaught exception:\", error)\n process.exit(1)\n})\n\nprocess.on(\"unhandledRejection\", (reason) => {\n consola.error(\"Unhandled rejection:\", reason)\n process.exit(1)\n})\n\nconst main = defineCommand({\n meta: {\n name: \"copilot-api\",\n description: \"A wrapper around GitHub Copilot API to make it OpenAI compatible, making it usable for other tools.\",\n },\n subCommands: {\n auth,\n logout,\n start,\n \"check-usage\": checkUsage,\n debug,\n \"list-claude-code\": listClaudeCode,\n \"setup-claude-code\": setupClaudeCode,\n },\n})\n\nawait runMain(main)\n\n// When runMain() returns, the command has finished.\n// The `start` subcommand keeps the event loop alive (HTTP server),\n// so this line only executes for one-shot commands (debug, auth, etc.).\n// Explicit exit is needed because `bun run --watch` keeps the process alive otherwise.\nprocess.exit(0)\n"],"mappings":"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"}
|