{"repo": "ak123yay/ai-coding-router", "files": {"src/server.ts": {"7adb89e389dfc23b21331ba5618eb7d2f22f72d1": {"t": 1785747188.8139794, "n": 22451, "h": "deb4bf4f52df552a", "c": "import http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type { CompletionRequest, Provider, RouterConfig, TaskFeatures } from \"./types.js\";\nimport { HybridClassifier } from \"./routing/hybrid-classifier.js\";\nimport { ModelRouter } from \"./routing/router.js\";\nimport { GovernorError } from \"./core/governor.js\";\nimport { FileUsageLedger } from \"./core/usage-ledger.js\";\nimport { Logger } from \"./core/logger.js\";\nimport { RateLimiter, assertAuthConfigured, bearerToken, tokenMatches } from \"./core/security.js\";\nimport { assertAllowedModel } from \"./config.js\";\nimport { PromptCompiler } from \"./core/prompt-compiler.js\";\nimport { boundMessages } from \"./core/message-budget.js\";\nimport { classifyTask } from \"./routing/classifier.js\";\n\nclass HttpError extends Error { constructor(readonly status: number, message: string) { super(message); } }\n\nexport function createServer(router: ModelRouter, config: RouterConfig, logger: Pick = new Logger(\"http\"), promptProvider?: Provider) {\n const serverConfig = config.server;\n const expectedToken = assertAuthConfigured(config);\n const limiter = new RateLimiter(serverConfig.rateLimitWindowMs, serverConfig.rateLimitMaxRequests);\n const ledger = new FileUsageLedger(serverConfig.usageStateDir);\n const classifier = new HybridClassifier(config);\n const promptCompiler = new PromptCompiler(config, { provider: promptProvider });\n const server = http.createServer(async (request, response) => {\n const requestId = crypto.randomUUID(); response.setHeader(\"x-request-id\", requestId);\n const route = new URL(request.url ?? \"/\", \"http://router.local\").pathname;\n try {\n if (request.method === \"GET\" && route === \"/health\") return send(response, 200, { ok: true, buildId: process.env.AK_ROUTER_BUILD_ID ?? \"standalone\", requestId });\n if (!isV1Route(request.method, route)) return send(response, 404, { error: { message: \"Not found\", type: \"not_found\" }, requestId });\n authorize(request, expectedToken);\n const client = clientKey(request, serverConfig.trustProxy);\n if (!limiter.allow(client)) throw new HttpError(429, \"Rate limit exceeded.\");\n if (route === \"/v1/models\" && request.method === \"GET\") return send(response, 200, { object: \"list\", data: router.availableModels().map((model) => ({ id: model.model, object: \"model\", owned_by: modelOwner(model.provider) })) });\n if (route === \"/v1/prompt/expand\" && request.method === \"POST\") {\n const body = await readJson(request, serverConfig.maxBodyBytes);\n if (typeof body.task !== \"string\" || !body.task.trim()) throw new HttpError(400, \"task is required.\");\n const result = await promptCompiler.expand({ task: body.task, cwd: typeof body.cwd === \"string\" ? body.cwd : undefined, handoff: typeof body.handoff === \"string\" ? body.handoff : undefined, force: body.force === true });\n return send(response, 200, { ...result, requestId });\n }\n if (route === \"/v1/route\" && request.method === \"POST\") {\n const body = await readJson(request, serverConfig.maxBodyBytes);\n if (typeof body.task !== \"string\" || !body.task.trim()) throw new HttpError(400, \"task is required.\");\n const toolRound = typeof body.toolRound === \"number\" && Number.isFinite(body.toolRound)\n ? Math.max(0, Math.floor(body.toolRound))\n : 0;\n const requestedModel = body.model === undefined ? undefined : resolveRequestedModel(body.model, config);\n const features = classifyTask(body.task.slice(0, 12_000), { toolRound });\n const decision = router.decide(features, body.manualMax === true, requestedModel, resolveRequestedThinking(body.reasoningEffort));\n return send(response, 200, {\n requestId,\n noModelCall: true,\n features: {\n kind: features.kind,\n filesAffected: features.filesAffected,\n complexity: features.complexity,\n risk: features.risk,\n ambiguity: features.ambiguity,\n toolRound: features.toolRound,\n },\n route: decision,\n });\n }\n if (route !== \"/v1/chat/completions\" || request.method !== \"POST\") return send(response, 404, { error: { message: \"Not found\", type: \"not_found\" }, requestId });\n\n const body = await readJson(request, serverConfig.maxBodyBytes);\n const rawMessages = Array.isArray(body.messages) ? body.messages as CompletionRequest[\"messages\"] : [];\n const budget = boundMessages(rawMessages, config.routing.maxContextChars);\n const messages = budget.messages;\n if (!messages.length) throw new HttpError(400, \"messages is required.\");\n if (isLocalTitleModel(body.model)) return sendLocalTitle(response, body.stream === true, messages);\n // Route the active task, not a bag of words from the entire chat. Old\n // turns frequently contain \"review\", \"find\", or \"debug\" and otherwise\n // override a later implementation request.\n const activeUser = [...messages].reverse().find((message) => message.role === \"user\");\n const userText = routingText(activeUser?.content);\n const metadata = body.metadata as Partial & { taskId?: string; manualMax?: boolean; fastMode?: boolean } | undefined;\n const fastMode = body.variant === \"fast\" || body.service_tier === \"priority\" || body.serviceTier === \"priority\" || metadata?.fastMode === true;\n const toolContinuation = messages.some((message) => message.role === \"tool\" || (message.role === \"assistant\" && Boolean(message.tool_calls?.length)));\n const toolRound = typeof metadata?.toolRound === \"number\" && Number.isFinite(metadata.toolRound)\n ? Math.max(0, Math.floor(metadata.toolRound))\n : messages.filter((message) => message.role === \"assistant\" && Boolean(message.tool_calls?.length)).length;\n const requestedModel = resolveRequestedModel(body.model, config);\n // An explicit model pin is already the user's routing decision. Keep\n // the deterministic task shape for fallback/telemetry, but do not spend\n // a second helper-model call trying to reinterpret a pinned request.\n const failureState = verificationFailureState(messages);\n const features = await classifyRequest(classifier, userText, {\n ...(metadata ?? {}),\n toolRound,\n // A failed verification result is an execution signal, not a new\n // user requirement. Feed it into routing so the next round can move\n // to a stronger model instead of repeating the same cheap attempt.\n failedAttempts: Math.max(metadata?.failedAttempts ?? 0, failureState.count),\n ...(failureState.latestRound === undefined ? {} : { failureRound: failureState.latestRound }),\n }, toolContinuation, requestedModel);\n // A variant such as OpenCode's `fast` applies to the automatic router\n // too. Previously this was discarded unless a model was pinned, which\n // made a session-level fast setting appear to do nothing.\n const requestedThinking = resolveRequestedThinking(body.reasoning_effort ?? body.reasoningEffort);\n const headerSessionID = headerValue(request, \"x-router-session-id\")\n const headerTaskID = headerValue(request, \"x-router-task-id\")\n const headerGoalMode = headerValue(request, \"x-router-goal-mode\")\n const requestMetadata = {\n ...(scalarMetadata(metadata) ?? {}),\n ...(headerSessionID ? { routerSessionID: headerSessionID } : {}),\n ...(headerTaskID ? { routerTaskID: headerTaskID } : {}),\n ...(headerGoalMode === \"true\" ? { goalMode: true } : {}),\n ...(fastMode ? { fastMode: true } : {}),\n routerContextTrimmed: budget.trimmed,\n routerContextOriginalChars: budget.originalChars,\n routerContextFinalChars: budget.finalChars,\n routerContextDroppedMessages: budget.droppedMessages,\n }\n const requestOptions: Omit = {\n messages,\n stream: body.stream === true,\n maxTokens: typeof body.max_tokens === \"number\" ? body.max_tokens : undefined,\n tools: Array.isArray(body.tools) ? body.tools : undefined,\n toolChoice: body.tool_choice,\n responseFormat: body.response_format,\n temperature: typeof body.temperature === \"number\" ? body.temperature : undefined,\n topP: typeof body.top_p === \"number\" ? body.top_p : undefined,\n metadata: requestMetadata\n };\n const taskId = taskKey(request, metadata, body.messages, body.project);\n\n if (body.stream === true) {\n await ledger.withTask(taskId, config.routing, async (governor) => {\n const routed = await router.stream(features, requestOptions, governor, metadata?.manualMax === true, requestedModel, requestedThinking);\n response.statusCode = 200; response.setHeader(\"content-type\", \"text/event-stream\"); response.setHeader(\"cache-control\", \"no-cache\"); response.setHeader(\"connection\", \"keep-alive\"); setRouterHeaders(response, routed.decision, budget);\n for await (const chunk of routed.stream) {\n const payload = { id: chunk.id, object: \"chat.completion.chunk\", created: Math.floor(Date.now() / 1000), model: chunk.model, choices: [{ index: 0, delta: chunk.delta ?? {}, finish_reason: chunk.finishReason ?? null }], usage: publicUsage(chunk.usage), router: { selectedModel: routed.decision.model, provider: routed.decision.provider, thinking: routed.decision.thinking, reasoningEffort: routed.decision.reasoningEffort, alternatives: routed.decision.fallbackChain } };\n response.write(`data: ${JSON.stringify(payload)}\\n\\n`);\n }\n response.write(\"data: [DONE]\\n\\n\");\n });\n response.end();\n return;\n }\n\n const result = await ledger.withTask(taskId, config.routing, (governor) => router.complete(features, requestOptions, governor, metadata?.manualMax === true, requestedModel, requestedThinking));\n setRouterHeaders(response, result.decision, budget);\n return send(response, 200, { id: result.response.id, object: \"chat.completion\", created: Math.floor(Date.now() / 1000), model: result.response.model, choices: [{ index: 0, message: { role: \"assistant\", content: result.response.content, ...(result.response.reasoningContent ? { reasoning_content: result.response.reasoningContent } : {}), ...(result.response.toolCalls?.length ? { tool_calls: result.response.toolCalls } : {}) }, finish_reason: result.response.finishReason }], usage: publicUsage(result.response.usage), router: { selectedModel: result.decision.model, provider: result.decision.provider, thinking: result.decision.thinking, reasoningEffort: result.decision.reasoningEffort, role: result.decision.role, alternatives: result.decision.fallbackChain, context: { trimmed: budget.trimmed, originalChars: budget.originalChars, finalChars: budget.finalChars, droppedMessages: budget.droppedMessages } } });\n } catch (error) {\n const status = error instanceof HttpError ? error.status : error instanceof GovernorError ? 429 : error instanceof SyntaxError ? 400 : 502;\n logger.error(\"request_failed\", { requestId, route, status, ...(headerValue(request, \"x-router-session-id\") ? { sessionID: headerValue(request, \"x-router-session-id\") } : {}), ...(headerValue(request, \"x-router-task-id\") ? { taskID: headerValue(request, \"x-router-task-id\") } : {}), error: error instanceof Error ? error.message : String(error) });\n if (!response.headersSent) send(response, status, { error: { message: error instanceof Error ? error.message : String(error), type: status === 502 ? \"upstream_error\" : \"request_error\" }, requestId });\n else response.end(`data: ${JSON.stringify({ error: { message: error instanceof Error ? error.message : String(error) } })}\\n\\n`);\n }\n });\n const cleanup = setInterval(() => limiter.prune(), serverConfig.rateLimitWindowMs).unref();\n server.on(\"close\", () => clearInterval(cleanup));\n return server;\n}\n\n/** Skip the optional hybrid helper when the caller explicitly pinned a model. */\nexport async function classifyRequest(\n classifier: Pick,\n text: string,\n hints: Partial,\n toolContinuation: boolean,\n requestedModel?: string,\n): Promise {\n return requestedModel ? classifyTask(text, hints) : classifier.classify(text, hints, toolContinuation);\n}\n\nexport function routingText(content: CompletionRequest[\"messages\"][number][\"content\"] | undefined): string {\n const text = typeof content === \"string\"\n ? content\n : Array.isArray(content)\n ? content.map((part) => part.text ?? (part.image_url ? \"[image]\" : \"\")).filter(Boolean).join(\"\\n\")\n : \"\";\n const compiled = text.match(/(?:^|\\r?\\n)ORIGINAL REQUEST:\\r?\\n([\\s\\S]*?)\\r?\\n(?:\\r?\\n)?COMPILED BRIEF:/);\n if (compiled?.[1]) return compiled[1].trim();\n const additive = text.match(/(?:^|\\r?\\n)ORIGINAL USER REQUEST:\\r?\\n([\\s\\S]*?)\\r?\\n\\r?\\nAK WORKSPACE BRIEF \\(non-authoritative\\):/i);\n return additive?.[1].trim() || text;\n}\n\n/** Detect only concrete verification failures from the active user turn.\n * Generic words such as \"error\" in source or search output are intentionally\n * ignored; escalation should follow a failed command/test, not exploration. */\nexport function verificationFailureEvidence(messages: CompletionRequest[\"messages\"]): boolean {\n return verificationFailureState(messages).count > 0;\n}\n\nexport function verificationFailureState(messages: CompletionRequest[\"messages\"]): { count: number; latestRound?: number } {\n let lastUser = -1;\n for (let index = messages.length - 1; index >= 0; index--) {\n if (messages[index]?.role === \"user\") { lastUser = index; break; }\n }\n let round = 0;\n let count = 0;\n let latestRound: number | undefined;\n const seenFailureRounds = new Set();\n for (const message of messages.slice(lastUser + 1)) {\n if (message.role === \"assistant\" && message.tool_calls?.length) round++;\n if (message.role !== \"tool\") continue;\n const text = messageText(message.content);\n if (!/(?:exit(?:ed)?\\s*(?:with|code)\\s*[1-9]\\b|(?:tests?|test suite|typecheck|lint|build)[^\\n]{0,120}\\b(?:fail|failed|failure|error)\\b|\\b(?:AssertionError|TypeError|ReferenceError|SyntaxError)\\b|\\bFAILED\\b)/i.test(text)) continue;\n if (!seenFailureRounds.has(round)) {\n seenFailureRounds.add(round);\n count++;\n }\n latestRound = round;\n }\n return { count, ...(latestRound === undefined ? {} : { latestRound }) };\n}\n\nfunction messageText(content: CompletionRequest[\"messages\"][number][\"content\"] | undefined): string {\n return typeof content === \"string\"\n ? content\n : Array.isArray(content)\n ? content.map((part) => part.text ?? \"\").join(\"\\n\")\n : \"\";\n}\n\nfunction isV1Route(method: string | undefined, route: string) { return (method === \"GET\" && route === \"/v1/models\") || ((method === \"POST\") && (route === \"/v1/chat/completions\" || route === \"/v1/prompt/expand\" || route === \"/v1/route\")); }\nfunction modelOwner(provider: string) {\n if (provider === \"openai-subscription\") return \"openai-subscription\";\n if (provider === \"opencode\" || provider === \"opencode-zen\") return \"opencode\";\n if (provider === \"nim\") return \"nvidia-nim\";\n return provider;\n}\nfunction isLocalTitleModel(value: unknown) { return value === \"local-title\" || value === \"ai-coding-router/local-title\"; }\nfunction resolveRequestedThinking(value: unknown): CompletionRequest[\"thinking\"] | undefined {\n if (typeof value !== \"string\") return undefined;\n return ({ none: \"fast\", minimal: \"fast\", low: \"light\", medium: \"standard\", high: \"deep\", max: \"max\" } as const)[value.toLowerCase() as \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"max\"];\n}\nfunction sendLocalTitle(response: http.ServerResponse, stream: boolean, messages: CompletionRequest[\"messages\"]) {\n const title = localTitle(messages);\n const id = `local-title-${crypto.randomUUID()}`;\n response.setHeader(\"x-router-provider\", \"local\");\n response.setHeader(\"x-router-model\", \"local-title\");\n response.setHeader(\"x-router-thinking\", \"none\");\n if (stream) {\n response.statusCode = 200;\n response.setHeader(\"content-type\", \"text/event-stream\");\n response.write(`data: ${JSON.stringify({ id, object: \"chat.completion.chunk\", created: Math.floor(Date.now() / 1000), model: \"local-title\", choices: [{ index: 0, delta: { role: \"assistant\", content: title }, finish_reason: null }] })}\\n\\n`);\n response.end(\"data: [DONE]\\n\\n\");\n return;\n }\n return send(response, 200, { id, object: \"chat.completion\", created: Math.floor(Date.now() / 1000), model: \"local-title\", choices: [{ index: 0, message: { role: \"assistant\", content: title }, finish_reason: \"stop\" }], usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 } });\n}\nfunction localTitle(messages: CompletionRequest[\"messages\"]) {\n const source = [...messages].reverse().find((message) => message.role === \"user\")?.content;\n const text = typeof source === \"string\" ? source : JSON.stringify(source ?? \"Coding task\");\n const words = text.replace(/https?:\\/\\/\\S+/g, \"\").replace(/[^\\p{L}\\p{N}._-]+/gu, \" \").trim().split(/\\s+/).filter(Boolean);\n const title = words.slice(0, 7).join(\" \");\n return (title || \"Coding task\").slice(0, 80);\n}\nfunction authorize(request: http.IncomingMessage, expectedToken: string | undefined) { if (expectedToken && !tokenMatches(expectedToken, bearerToken({ headers: request.headers }))) throw new HttpError(401, \"Bearer authentication required.\"); }\nfunction clientKey(request: http.IncomingMessage, trustProxy: boolean) { if (trustProxy && typeof request.headers[\"x-forwarded-for\"] === \"string\") return request.headers[\"x-forwarded-for\"].split(\",\")[0].trim(); return request.socket.remoteAddress ?? \"unknown\"; }\nfunction setRouterHeaders(response: http.ServerResponse, decision: { provider: string; model: string; thinking: string; reasoningEffort?: string; requiresPlan: boolean; readOnly: boolean; fallbackChain: Array<{ provider: string; model: string; thinking: string; reasoningEffort?: string }> }, budget?: { trimmed: boolean; originalChars: number; finalChars: number; droppedMessages: number }) { response.setHeader(\"x-router-provider\", decision.provider); response.setHeader(\"x-router-model\", decision.model); response.setHeader(\"x-router-thinking\", decision.thinking); response.setHeader(\"x-router-reasoning-effort\", decision.reasoningEffort ?? decision.thinking); response.setHeader(\"x-router-alternatives\", decision.fallbackChain.map((candidate) => `${candidate.provider}/${candidate.model}:${candidate.reasoningEffort ?? candidate.thinking}`).join(\",\")); response.setHeader(\"x-router-requires-plan\", String(decision.requiresPlan)); response.setHeader(\"x-router-read-only\", String(decision.readOnly)); if (budget) { response.setHeader(\"x-router-context-trimmed\", String(budget.trimmed)); response.setHeader(\"x-router-context-chars\", `${budget.finalChars}/${budget.originalChars}`); response.setHeader(\"x-router-context-dropped\", String(budget.droppedMessages)); } }\nfunction publicUsage(usage: { inputTokens?: number; outputTokens?: number; totalTokens?: number; cachedInputTokens?: number; reasoningTokens?: number } | undefined) { return usage ? { prompt_tokens: usage.inputTokens ?? 0, completion_tokens: usage.outputTokens ?? 0, total_tokens: usage.totalTokens ?? 0, prompt_tokens_details: { cached_tokens: usage.cachedInputTokens ?? 0 }, completion_tokens_details: { reasoning_tokens: usage.reasoningTokens ?? 0 } } : undefined; }\nfunction send(response: http.ServerResponse, status: number, payload: unknown) { response.statusCode = status; response.setHeader(\"content-type\", \"application/json\"); response.end(JSON.stringify(payload)); }\nasync function readJson(request: http.IncomingMessage, limit: number): Promise> {\n const declared = Number(request.headers[\"content-length\"] ?? 0); if (declared > limit) throw new HttpError(413, \"Request body exceeds the configured limit.\");\n let body = \"\";\n for await (const chunk of request) { body += chunk.toString(); if (Buffer.byteLength(body) > limit) throw new HttpError(413, \"Request body exceeds the configured limit.\"); }\n return JSON.parse(body || \"{}\");\n}\nfunction scalarMetadata(value: Record | undefined): Record | undefined { if (!value) return undefined; return Object.fromEntries(Object.entries(value).filter(([, item]) => [\"string\", \"number\", \"boolean\"].includes(typeof item))) as Record; }\nfunction headerValue(request: http.IncomingMessage, name: string) { const value = request.headers[name]; return typeof value === \"string\" ? value.slice(0, 200) : undefined; }\nfunction taskKey(request: http.IncomingMessage, metadata: { taskId?: string } | undefined, messages: unknown, project: unknown) {\n if (metadata?.taskId) return metadata.taskId;\n const header = request.headers[\"x-router-task-id\"];\n if (typeof header === \"string\" && header) return header;\n // Tool-loop requests append assistant and tool messages while retaining the\n // latest user instruction. Key on that instruction so usage caps apply to a\n // complete OpenCode task instead of resetting after every tool call.\n const lastUser = Array.isArray(messages) ? [...messages].reverse().find((message: any) => message?.role === \"user\") : undefined;\n return crypto.createHash(\"sha256\").update(JSON.stringify({ project, user: lastUser?.content ?? messages })).digest(\"hex\");\n}\nfunction resolveRequestedModel(value: unknown, config: RouterConfig): string | undefined {\n if (value === undefined || value === null || value === \"router\" || value === \"ai-coding-router/router\") return undefined;\n if (typeof value !== \"string\") throw new HttpError(400, \"model must be a string.\");\n const model = value.startsWith(\"ai-coding-router/\")\n ? value.slice(\"ai-coding-router/\".length)\n : value.startsWith(\"opencode/\")\n ? value.slice(\"opencode/\".length)\n : value.startsWith(\"opencode-zen/\")\n ? value.slice(\"opencode-zen/\".length)\n : value;\n // OpenCode's user-facing selector has a stable, readable name while the\n // router keeps the free route's legacy internal key for compatibility.\n const canonical = model === \"deepseek-v4-flash-new\" ? \"deepseek-v4-flash-free\" : model;\n try { assertAllowedModel(config, canonical); return canonical; }\n catch { throw new HttpError(400, `Model '${value}' is not available in this router allowlist.`); }\n}\n"}, "6fe51777517d556aeb1af0f3d6c07c474f0fbe3c": {"t": 1785771136.024274, "n": 15084, "h": "7d9a13b28510685b", "c": "import http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type { CompletionRequest, Provider, RouterConfig, TaskFeatures } from \"./types.js\";\nimport { HybridClassifier } from \"./routing/hybrid-classifier.js\";\nimport { ModelRouter } from \"./routing/router.js\";\nimport { GovernorError } from \"./core/governor.js\";\nimport { FileUsageLedger } from \"./core/usage-ledger.js\";\nimport { Logger } from \"./core/logger.js\";\nimport { RateLimiter, assertAuthConfigured, bearerToken, tokenMatches } from \"./core/security.js\";\nimport { assertAllowedModel } from \"./config.js\";\nimport { PromptCompiler } from \"./core/prompt-compiler.js\";\nimport { boundMessages } from \"./core/message-budget.js\";\n\nclass HttpError extends Error { constructor(readonly status: number, message: string) { super(message); } }\n\nexport function createServer(router: ModelRouter, config: RouterConfig, logger: Pick = new Logger(\"http\"), promptProvider?: Provider) {\n const serverConfig = config.server;\n const expectedToken = assertAuthConfigured(config);\n const limiter = new RateLimiter(serverConfig.rateLimitWindowMs, serverConfig.rateLimitMaxRequests);\n const ledger = new FileUsageLedger(serverConfig.usageStateDir);\n const classifier = new HybridClassifier(config);\n const promptCompiler = new PromptCompiler(config, { provider: promptProvider });\n const server = http.createServer(async (request, response) => {\n const requestId = crypto.randomUUID(); response.setHeader(\"x-request-id\", requestId);\n const route = new URL(request.url ?? \"/\", \"http://router.local\").pathname;\n try {\n if (request.method === \"GET\" && route === \"/health\") return send(response, 200, { ok: true, buildId: process.env.AK_ROUTER_BUILD_ID ?? \"standalone\", requestId });\n if (!isV1Route(request.method, route)) return send(response, 404, { error: { message: \"Not found\", type: \"not_found\" }, requestId });\n authorize(request, expectedToken);\n const client = clientKey(request, serverConfig.trustProxy);\n if (!limiter.allow(client)) throw new HttpError(429, \"Rate limit exceeded.\");\n if (route === \"/v1/models\" && request.method === \"GET\") return send(response, 200, { object: \"list\", data: router.availableModels().map((model) => ({ id: model.model, object: \"model\", owned_by: model.provider === \"openai-subscription\" ? \"openai-subscription\" : \"nvidia-nim\" })) });\n if (route === \"/v1/prompt/expand\" && request.method === \"POST\") {\n const body = await readJson(request, serverConfig.maxBodyBytes);\n if (typeof body.task !== \"string\" || !body.task.trim()) throw new HttpError(400, \"task is required.\");\n const result = await promptCompiler.expand({ task: body.task, cwd: typeof body.cwd === \"string\" ? body.cwd : undefined, handoff: typeof body.handoff === \"string\" ? body.handoff : undefined, force: body.force === true });\n return send(response, 200, { ...result, requestId });\n }\n if (route !== \"/v1/chat/completions\" || request.method !== \"POST\") return send(response, 404, { error: { message: \"Not found\", type: \"not_found\" }, requestId });\n\n const body = await readJson(request, serverConfig.maxBodyBytes);\n const rawMessages = Array.isArray(body.messages) ? body.messages as CompletionRequest[\"messages\"] : [];\n const budget = boundMessages(rawMessages, config.routing.maxContextChars);\n const messages = budget.messages;\n if (!messages.length) throw new HttpError(400, \"messages is required.\");\n if (isLocalTitleModel(body.model)) return sendLocalTitle(response, body.stream === true, messages);\n // Route the active task, not a bag of words from the entire chat. Old\n // turns frequently contain \"review\", \"find\", or \"debug\" and otherwise\n // override a later implementation request.\n const activeUser = [...messages].reverse().find((message) => message.role === \"user\");\n const userText = routingText(activeUser?.content);\n const metadata = body.metadata as Partial & { taskId?: string; manualMax?: boolean } | undefined;\n const toolContinuation = messages.some((message) => message.role === \"tool\" || (message.role === \"assistant\" && Boolean(message.tool_calls?.length)));\n const features = await classifier.classify(userText, metadata, toolContinuation);\n const requestedModel = resolveRequestedModel(body.model, config);\n const requestOptions: Omit = {\n messages,\n stream: body.stream === true,\n maxTokens: typeof body.max_tokens === \"number\" ? body.max_tokens : undefined,\n tools: Array.isArray(body.tools) ? body.tools : undefined,\n toolChoice: body.tool_choice,\n responseFormat: body.response_format,\n temperature: typeof body.temperature === \"number\" ? body.temperature : undefined,\n topP: typeof body.top_p === \"number\" ? body.top_p : undefined,\n metadata: scalarMetadata(metadata)\n };\n const taskId = taskKey(request, metadata, body.messages, body.project);\n\n if (body.stream === true) {\n await ledger.withTask(taskId, config.routing, async (governor) => {\n const routed = await router.stream(features, requestOptions, governor, metadata?.manualMax === true, requestedModel);\n response.statusCode = 200; response.setHeader(\"content-type\", \"text/event-stream\"); response.setHeader(\"cache-control\", \"no-cache\"); response.setHeader(\"connection\", \"keep-alive\"); setRouterHeaders(response, routed.decision, budget);\n for await (const chunk of routed.stream) {\n const payload = { id: chunk.id, object: \"chat.completion.chunk\", created: Math.floor(Date.now() / 1000), model: chunk.model, choices: [{ index: 0, delta: chunk.delta ?? {}, finish_reason: chunk.finishReason ?? null }], usage: publicUsage(chunk.usage), router: { selectedModel: routed.decision.model, provider: routed.decision.provider, thinking: routed.decision.thinking, reasoningEffort: routed.decision.reasoningEffort, alternatives: routed.decision.fallbackChain } };\n response.write(`data: ${JSON.stringify(payload)}\\n\\n`);\n }\n response.write(\"data: [DONE]\\n\\n\");\n });\n response.end();\n return;\n }\n\n const result = await ledger.withTask(taskId, config.routing, (governor) => router.complete(features, requestOptions, governor, metadata?.manualMax === true, requestedModel));\n setRouterHeaders(response, result.decision, budget);\n return send(response, 200, { id: result.response.id, object: \"chat.completion\", created: Math.floor(Date.now() / 1000), model: result.response.model, choices: [{ index: 0, message: { role: \"assistant\", content: result.response.content, ...(result.response.toolCalls?.length ? { tool_calls: result.response.toolCalls } : {}) }, finish_reason: result.response.finishReason }], usage: publicUsage(result.response.usage), router: { selectedModel: result.decision.model, provider: result.decision.provider, thinking: result.decision.thinking, reasoningEffort: result.decision.reasoningEffort, role: result.decision.role, alternatives: result.decision.fallbackChain, context: { trimmed: budget.trimmed, originalChars: budget.originalChars, finalChars: budget.finalChars, droppedMessages: budget.droppedMessages } } });\n } catch (error) {\n const status = error instanceof HttpError ? error.status : error instanceof GovernorError ? 429 : error instanceof SyntaxError ? 400 : 502;\n logger.error(\"request_failed\", { requestId, route, status, error: error instanceof Error ? error.message : String(error) });\n if (!response.headersSent) send(response, status, { error: { message: error instanceof Error ? error.message : String(error), type: status === 502 ? \"upstream_error\" : \"request_error\" }, requestId });\n else response.end(`data: ${JSON.stringify({ error: { message: error instanceof Error ? error.message : String(error) } })}\\n\\n`);\n }\n });\n const cleanup = setInterval(() => limiter.prune(), serverConfig.rateLimitWindowMs).unref();\n server.on(\"close\", () => clearInterval(cleanup));\n return server;\n}\n\nexport function routingText(content: CompletionRequest[\"messages\"][number][\"content\"] | undefined): string {\n const text = typeof content === \"string\"\n ? content\n : Array.isArray(content)\n ? content.map((part) => part.text ?? (part.image_url ? \"[image]\" : \"\")).filter(Boolean).join(\"\\n\")\n : \"\";\n const compiled = text.match(/(?:^|\\r?\\n)ORIGINAL REQUEST:\\r?\\n([\\s\\S]*?)\\r?\\n(?:\\r?\\n)?COMPILED BRIEF:/);\n return compiled?.[1].trim() || text;\n}\n\nfunction isV1Route(method: string | undefined, route: string) { return (method === \"GET\" && route === \"/v1/models\") || ((method === \"POST\") && (route === \"/v1/chat/completions\" || route === \"/v1/prompt/expand\")); }\nfunction isLocalTitleModel(value: unknown) { return value === \"local-title\" || value === \"ai-coding-router/local-title\"; }\nfunction sendLocalTitle(response: http.ServerResponse, stream: boolean, messages: CompletionRequest[\"messages\"]) {\n const title = localTitle(messages);\n const id = `local-title-${crypto.randomUUID()}`;\n response.setHeader(\"x-router-provider\", \"local\");\n response.setHeader(\"x-router-model\", \"local-title\");\n response.setHeader(\"x-router-thinking\", \"none\");\n if (stream) {\n response.statusCode = 200;\n response.setHeader(\"content-type\", \"text/event-stream\");\n response.write(`data: ${JSON.stringify({ id, object: \"chat.completion.chunk\", created: Math.floor(Date.now() / 1000), model: \"local-title\", choices: [{ index: 0, delta: { role: \"assistant\", content: title }, finish_reason: null }] })}\\n\\n`);\n response.end(\"data: [DONE]\\n\\n\");\n return;\n }\n return send(response, 200, { id, object: \"chat.completion\", created: Math.floor(Date.now() / 1000), model: \"local-title\", choices: [{ index: 0, message: { role: \"assistant\", content: title }, finish_reason: \"stop\" }], usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 } });\n}\nfunction localTitle(messages: CompletionRequest[\"messages\"]) {\n const source = [...messages].reverse().find((message) => message.role === \"user\")?.content;\n const text = typeof source === \"string\" ? source : JSON.stringify(source ?? \"Coding task\");\n const words = text.replace(/https?:\\/\\/\\S+/g, \"\").replace(/[^\\p{L}\\p{N}._-]+/gu, \" \").trim().split(/\\s+/).filter(Boolean);\n const title = words.slice(0, 7).join(\" \");\n return (title || \"Coding task\").slice(0, 80);\n}\nfunction authorize(request: http.IncomingMessage, expectedToken: string | undefined) { if (expectedToken && !tokenMatches(expectedToken, bearerToken({ headers: request.headers }))) throw new HttpError(401, \"Bearer authentication required.\"); }\nfunction clientKey(request: http.IncomingMessage, trustProxy: boolean) { if (trustProxy && typeof request.headers[\"x-forwarded-for\"] === \"string\") return request.headers[\"x-forwarded-for\"].split(\",\")[0].trim(); return request.socket.remoteAddress ?? \"unknown\"; }\nfunction setRouterHeaders(response: http.ServerResponse, decision: { provider: string; model: string; thinking: string; reasoningEffort?: string; requiresPlan: boolean; readOnly: boolean; fallbackChain: Array<{ provider: string; model: string; thinking: string; reasoningEffort?: string }> }, budget?: { trimmed: boolean; originalChars: number; finalChars: number; droppedMessages: number }) { response.setHeader(\"x-router-provider\", decision.provider); response.setHeader(\"x-router-model\", decision.model); response.setHeader(\"x-router-thinking\", decision.thinking); response.setHeader(\"x-router-reasoning-effort\", decision.reasoningEffort ?? decision.thinking); response.setHeader(\"x-router-alternatives\", decision.fallbackChain.map((candidate) => `${candidate.provider}/${candidate.model}:${candidate.reasoningEffort ?? candidate.thinking}`).join(\",\")); response.setHeader(\"x-router-requires-plan\", String(decision.requiresPlan)); response.setHeader(\"x-router-read-only\", String(decision.readOnly)); if (budget) { response.setHeader(\"x-router-context-trimmed\", String(budget.trimmed)); response.setHeader(\"x-router-context-chars\", `${budget.finalChars}/${budget.originalChars}`); response.setHeader(\"x-router-context-dropped\", String(budget.droppedMessages)); } }\nfunction publicUsage(usage: { inputTokens?: number; outputTokens?: number; totalTokens?: number; cachedInputTokens?: number; reasoningTokens?: number } | undefined) { return usage ? { prompt_tokens: usage.inputTokens ?? 0, completion_tokens: usage.outputTokens ?? 0, total_tokens: usage.totalTokens ?? 0, prompt_tokens_details: { cached_tokens: usage.cachedInputTokens ?? 0 }, completion_tokens_details: { reasoning_tokens: usage.reasoningTokens ?? 0 } } : undefined; }\nfunction send(response: http.ServerResponse, status: number, payload: unknown) { response.statusCode = status; response.setHeader(\"content-type\", \"application/json\"); response.end(JSON.stringify(payload)); }\nasync function readJson(request: http.IncomingMessage, limit: number): Promise> {\n const declared = Number(request.headers[\"content-length\"] ?? 0); if (declared > limit) throw new HttpError(413, \"Request body exceeds the configured limit.\");\n let body = \"\";\n for await (const chunk of request) { body += chunk.toString(); if (Buffer.byteLength(body) > limit) throw new HttpError(413, \"Request body exceeds the configured limit.\"); }\n return JSON.parse(body || \"{}\");\n}\nfunction scalarMetadata(value: Record | undefined): Record | undefined { if (!value) return undefined; return Object.fromEntries(Object.entries(value).filter(([, item]) => [\"string\", \"number\", \"boolean\"].includes(typeof item))) as Record; }\nfunction taskKey(request: http.IncomingMessage, metadata: { taskId?: string } | undefined, messages: unknown, project: unknown) {\n if (metadata?.taskId) return metadata.taskId;\n const header = request.headers[\"x-router-task-id\"];\n if (typeof header === \"string\" && header) return header;\n // Tool-loop requests append assistant and tool messages while retaining the\n // latest user instruction. Key on that instruction so usage caps apply to a\n // complete OpenCode task instead of resetting after every tool call.\n const lastUser = Array.isArray(messages) ? [...messages].reverse().find((message: any) => message?.role === \"user\") : undefined;\n return crypto.createHash(\"sha256\").update(JSON.stringify({ project, user: lastUser?.content ?? messages })).digest(\"hex\");\n}\nfunction resolveRequestedModel(value: unknown, config: RouterConfig): string | undefined {\n if (value === undefined || value === null || value === \"router\" || value === \"ai-coding-router/router\") return undefined;\n if (typeof value !== \"string\") throw new HttpError(400, \"model must be a string.\");\n const model = value.startsWith(\"ai-coding-router/\") ? value.slice(\"ai-coding-router/\".length) : value;\n try { assertAllowedModel(config, model); return model; }\n catch { throw new HttpError(400, `Model '${value}' is not available in this router allowlist.`); }\n}\n"}, "74b9833cb73f29dd16f99341b262254864f58aa5": {"t": 1785771137.9059262, "n": 14400, "h": "8f869c967cd07a34", "c": "import http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type { CompletionRequest, Provider, RouterConfig, TaskFeatures } from \"./types.js\";\nimport { HybridClassifier } from \"./routing/hybrid-classifier.js\";\nimport { ModelRouter } from \"./routing/router.js\";\nimport { GovernorError } from \"./core/governor.js\";\nimport { FileUsageLedger } from \"./core/usage-ledger.js\";\nimport { Logger } from \"./core/logger.js\";\nimport { RateLimiter, assertAuthConfigured, bearerToken, tokenMatches } from \"./core/security.js\";\nimport { assertAllowedModel } from \"./config.js\";\nimport { PromptCompiler } from \"./core/prompt-compiler.js\";\nimport { boundMessages } from \"./core/message-budget.js\";\n\nclass HttpError extends Error { constructor(readonly status: number, message: string) { super(message); } }\n\nexport function createServer(router: ModelRouter, config: RouterConfig, logger: Pick = new Logger(\"http\"), promptProvider?: Provider) {\n const serverConfig = config.server;\n const expectedToken = assertAuthConfigured(config);\n const limiter = new RateLimiter(serverConfig.rateLimitWindowMs, serverConfig.rateLimitMaxRequests);\n const ledger = new FileUsageLedger(serverConfig.usageStateDir);\n const classifier = new HybridClassifier(config);\n const promptCompiler = new PromptCompiler(config, { provider: promptProvider });\n const server = http.createServer(async (request, response) => {\n const requestId = crypto.randomUUID(); response.setHeader(\"x-request-id\", requestId);\n const route = new URL(request.url ?? \"/\", \"http://router.local\").pathname;\n try {\n if (request.method === \"GET\" && route === \"/health\") return send(response, 200, { ok: true, requestId });\n if (!isV1Route(request.method, route)) return send(response, 404, { error: { message: \"Not found\", type: \"not_found\" }, requestId });\n authorize(request, expectedToken);\n const client = clientKey(request, serverConfig.trustProxy);\n if (!limiter.allow(client)) throw new HttpError(429, \"Rate limit exceeded.\");\n if (route === \"/v1/models\" && request.method === \"GET\") return send(response, 200, { object: \"list\", data: router.availableModels().map((model) => ({ id: model.model, object: \"model\", owned_by: model.provider === \"openai-subscription\" ? \"openai-subscription\" : \"nvidia-nim\" })) });\n if (route === \"/v1/prompt/expand\" && request.method === \"POST\") {\n const body = await readJson(request, serverConfig.maxBodyBytes);\n if (typeof body.task !== \"string\" || !body.task.trim()) throw new HttpError(400, \"task is required.\");\n const result = await promptCompiler.expand({ task: body.task, cwd: typeof body.cwd === \"string\" ? body.cwd : undefined, handoff: typeof body.handoff === \"string\" ? body.handoff : undefined, force: body.force === true });\n return send(response, 200, { ...result, requestId });\n }\n if (route !== \"/v1/chat/completions\" || request.method !== \"POST\") return send(response, 404, { error: { message: \"Not found\", type: \"not_found\" }, requestId });\n\n const body = await readJson(request, serverConfig.maxBodyBytes);\n const rawMessages = Array.isArray(body.messages) ? body.messages as CompletionRequest[\"messages\"] : [];\n const budget = boundMessages(rawMessages, config.routing.maxContextChars);\n const messages = budget.messages;\n if (!messages.length) throw new HttpError(400, \"messages is required.\");\n if (isLocalTitleModel(body.model)) return sendLocalTitle(response, body.stream === true, messages);\n const userText = messages.filter((message) => message.role === \"user\").map((message) => typeof message.content === \"string\" ? message.content : JSON.stringify(message.content)).join(\"\\n\");\n const metadata = body.metadata as Partial & { taskId?: string; manualMax?: boolean } | undefined;\n const toolContinuation = messages.some((message) => message.role === \"tool\" || (message.role === \"assistant\" && Boolean(message.tool_calls?.length)));\n const features = await classifier.classify(userText, metadata, toolContinuation);\n const requestedModel = resolveRequestedModel(body.model, config);\n const requestOptions: Omit = {\n messages,\n stream: body.stream === true,\n maxTokens: typeof body.max_tokens === \"number\" ? body.max_tokens : undefined,\n tools: Array.isArray(body.tools) ? body.tools : undefined,\n toolChoice: body.tool_choice,\n responseFormat: body.response_format,\n temperature: typeof body.temperature === \"number\" ? body.temperature : undefined,\n topP: typeof body.top_p === \"number\" ? body.top_p : undefined,\n metadata: scalarMetadata(metadata)\n };\n const taskId = taskKey(request, metadata, body.messages, body.project);\n\n if (body.stream === true) {\n await ledger.withTask(taskId, config.routing, async (governor) => {\n const routed = router.stream(features, requestOptions, governor, metadata?.manualMax === true, requestedModel);\n response.statusCode = 200; response.setHeader(\"content-type\", \"text/event-stream\"); response.setHeader(\"cache-control\", \"no-cache\"); response.setHeader(\"connection\", \"keep-alive\"); setRouterHeaders(response, routed.decision, budget);\n for await (const chunk of routed.stream) {\n const payload = { id: chunk.id, object: \"chat.completion.chunk\", created: Math.floor(Date.now() / 1000), model: chunk.model, choices: [{ index: 0, delta: chunk.delta ?? {}, finish_reason: chunk.finishReason ?? null }], usage: publicUsage(chunk.usage), router: { selectedModel: routed.decision.model, provider: routed.decision.provider, thinking: routed.decision.thinking, reasoningEffort: routed.decision.reasoningEffort, alternatives: routed.decision.fallbackChain } };\n response.write(`data: ${JSON.stringify(payload)}\\n\\n`);\n }\n response.write(\"data: [DONE]\\n\\n\");\n });\n response.end();\n return;\n }\n\n const result = await ledger.withTask(taskId, config.routing, (governor) => router.complete(features, requestOptions, governor, metadata?.manualMax === true, requestedModel));\n setRouterHeaders(response, result.decision, budget);\n return send(response, 200, { id: result.response.id, object: \"chat.completion\", created: Math.floor(Date.now() / 1000), model: result.response.model, choices: [{ index: 0, message: { role: \"assistant\", content: result.response.content, ...(result.response.toolCalls?.length ? { tool_calls: result.response.toolCalls } : {}) }, finish_reason: result.response.finishReason }], usage: publicUsage(result.response.usage), router: { selectedModel: result.decision.model, provider: result.decision.provider, thinking: result.decision.thinking, reasoningEffort: result.decision.reasoningEffort, role: result.decision.role, alternatives: result.decision.fallbackChain, context: { trimmed: budget.trimmed, originalChars: budget.originalChars, finalChars: budget.finalChars, droppedMessages: budget.droppedMessages } } });\n } catch (error) {\n const status = error instanceof HttpError ? error.status : error instanceof GovernorError ? 429 : error instanceof SyntaxError ? 400 : 502;\n logger.error(\"request_failed\", { requestId, route, status, error: error instanceof Error ? error.message : String(error) });\n if (!response.headersSent) send(response, status, { error: { message: error instanceof Error ? error.message : String(error), type: status === 502 ? \"upstream_error\" : \"request_error\" }, requestId });\n else response.end(`data: ${JSON.stringify({ error: { message: error instanceof Error ? error.message : String(error) } })}\\n\\n`);\n }\n });\n const cleanup = setInterval(() => limiter.prune(), serverConfig.rateLimitWindowMs).unref();\n server.on(\"close\", () => clearInterval(cleanup));\n return server;\n}\n\nfunction isV1Route(method: string | undefined, route: string) { return (method === \"GET\" && route === \"/v1/models\") || ((method === \"POST\") && (route === \"/v1/chat/completions\" || route === \"/v1/prompt/expand\")); }\nfunction isLocalTitleModel(value: unknown) { return value === \"local-title\" || value === \"ai-coding-router/local-title\"; }\nfunction sendLocalTitle(response: http.ServerResponse, stream: boolean, messages: CompletionRequest[\"messages\"]) {\n const title = localTitle(messages);\n const id = `local-title-${crypto.randomUUID()}`;\n response.setHeader(\"x-router-provider\", \"local\");\n response.setHeader(\"x-router-model\", \"local-title\");\n response.setHeader(\"x-router-thinking\", \"none\");\n if (stream) {\n response.statusCode = 200;\n response.setHeader(\"content-type\", \"text/event-stream\");\n response.write(`data: ${JSON.stringify({ id, object: \"chat.completion.chunk\", created: Math.floor(Date.now() / 1000), model: \"local-title\", choices: [{ index: 0, delta: { role: \"assistant\", content: title }, finish_reason: null }] })}\\n\\n`);\n response.end(\"data: [DONE]\\n\\n\");\n return;\n }\n return send(response, 200, { id, object: \"chat.completion\", created: Math.floor(Date.now() / 1000), model: \"local-title\", choices: [{ index: 0, message: { role: \"assistant\", content: title }, finish_reason: \"stop\" }], usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 } });\n}\nfunction localTitle(messages: CompletionRequest[\"messages\"]) {\n const source = [...messages].reverse().find((message) => message.role === \"user\")?.content;\n const text = typeof source === \"string\" ? source : JSON.stringify(source ?? \"Coding task\");\n const words = text.replace(/https?:\\/\\/\\S+/g, \"\").replace(/[^\\p{L}\\p{N}._-]+/gu, \" \").trim().split(/\\s+/).filter(Boolean);\n const title = words.slice(0, 7).join(\" \");\n return (title || \"Coding task\").slice(0, 80);\n}\nfunction authorize(request: http.IncomingMessage, expectedToken: string | undefined) { if (expectedToken && !tokenMatches(expectedToken, bearerToken({ headers: request.headers }))) throw new HttpError(401, \"Bearer authentication required.\"); }\nfunction clientKey(request: http.IncomingMessage, trustProxy: boolean) { if (trustProxy && typeof request.headers[\"x-forwarded-for\"] === \"string\") return request.headers[\"x-forwarded-for\"].split(\",\")[0].trim(); return request.socket.remoteAddress ?? \"unknown\"; }\nfunction setRouterHeaders(response: http.ServerResponse, decision: { provider: string; model: string; thinking: string; reasoningEffort?: string; requiresPlan: boolean; readOnly: boolean; fallbackChain: Array<{ provider: string; model: string; thinking: string; reasoningEffort?: string }> }, budget?: { trimmed: boolean; originalChars: number; finalChars: number; droppedMessages: number }) { response.setHeader(\"x-router-provider\", decision.provider); response.setHeader(\"x-router-model\", decision.model); response.setHeader(\"x-router-thinking\", decision.thinking); response.setHeader(\"x-router-reasoning-effort\", decision.reasoningEffort ?? decision.thinking); response.setHeader(\"x-router-alternatives\", decision.fallbackChain.map((candidate) => `${candidate.provider}/${candidate.model}:${candidate.reasoningEffort ?? candidate.thinking}`).join(\",\")); response.setHeader(\"x-router-requires-plan\", String(decision.requiresPlan)); response.setHeader(\"x-router-read-only\", String(decision.readOnly)); if (budget) { response.setHeader(\"x-router-context-trimmed\", String(budget.trimmed)); response.setHeader(\"x-router-context-chars\", `${budget.finalChars}/${budget.originalChars}`); response.setHeader(\"x-router-context-dropped\", String(budget.droppedMessages)); } }\nfunction publicUsage(usage: { inputTokens?: number; outputTokens?: number; totalTokens?: number; cachedInputTokens?: number; reasoningTokens?: number } | undefined) { return usage ? { prompt_tokens: usage.inputTokens ?? 0, completion_tokens: usage.outputTokens ?? 0, total_tokens: usage.totalTokens ?? 0, prompt_tokens_details: { cached_tokens: usage.cachedInputTokens ?? 0 }, completion_tokens_details: { reasoning_tokens: usage.reasoningTokens ?? 0 } } : undefined; }\nfunction send(response: http.ServerResponse, status: number, payload: unknown) { response.statusCode = status; response.setHeader(\"content-type\", \"application/json\"); response.end(JSON.stringify(payload)); }\nasync function readJson(request: http.IncomingMessage, limit: number): Promise> {\n const declared = Number(request.headers[\"content-length\"] ?? 0); if (declared > limit) throw new HttpError(413, \"Request body exceeds the configured limit.\");\n let body = \"\";\n for await (const chunk of request) { body += chunk.toString(); if (Buffer.byteLength(body) > limit) throw new HttpError(413, \"Request body exceeds the configured limit.\"); }\n return JSON.parse(body || \"{}\");\n}\nfunction scalarMetadata(value: Record | undefined): Record | undefined { if (!value) return undefined; return Object.fromEntries(Object.entries(value).filter(([, item]) => [\"string\", \"number\", \"boolean\"].includes(typeof item))) as Record; }\nfunction taskKey(request: http.IncomingMessage, metadata: { taskId?: string } | undefined, messages: unknown, project: unknown) {\n if (metadata?.taskId) return metadata.taskId;\n const header = request.headers[\"x-router-task-id\"];\n if (typeof header === \"string\" && header) return header;\n // Tool-loop requests append assistant and tool messages while retaining the\n // latest user instruction. Key on that instruction so usage caps apply to a\n // complete OpenCode task instead of resetting after every tool call.\n const lastUser = Array.isArray(messages) ? [...messages].reverse().find((message: any) => message?.role === \"user\") : undefined;\n return crypto.createHash(\"sha256\").update(JSON.stringify({ project, user: lastUser?.content ?? messages })).digest(\"hex\");\n}\nfunction resolveRequestedModel(value: unknown, config: RouterConfig): string | undefined {\n if (value === undefined || value === null || value === \"router\" || value === \"ai-coding-router/router\") return undefined;\n if (typeof value !== \"string\") throw new HttpError(400, \"model must be a string.\");\n const model = value.startsWith(\"ai-coding-router/\") ? value.slice(\"ai-coding-router/\".length) : value;\n try { assertAllowedModel(config, model); return model; }\n catch { throw new HttpError(400, `Model '${value}' is not available in this router allowlist.`); }\n}\n"}, "426a4dcc452baca10dc4c42ce8c04c0b94f8e681": {"t": 1785771139.7245731, "n": 12745, "h": "8e10c6cf63b8034f", "c": "import http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type { CompletionRequest, RouterConfig, TaskFeatures } from \"./types.js\";\nimport { HybridClassifier } from \"./routing/hybrid-classifier.js\";\nimport { ModelRouter } from \"./routing/router.js\";\nimport { GovernorError } from \"./core/governor.js\";\nimport { FileUsageLedger } from \"./core/usage-ledger.js\";\nimport { Logger } from \"./core/logger.js\";\nimport { RateLimiter, assertAuthConfigured, bearerToken, tokenMatches } from \"./core/security.js\";\nimport { assertAllowedModel } from \"./config.js\";\n\nclass HttpError extends Error { constructor(readonly status: number, message: string) { super(message); } }\n\nexport function createServer(router: ModelRouter, config: RouterConfig, logger: Pick = new Logger(\"http\")) {\n const serverConfig = config.server;\n const expectedToken = assertAuthConfigured(config);\n const limiter = new RateLimiter(serverConfig.rateLimitWindowMs, serverConfig.rateLimitMaxRequests);\n const ledger = new FileUsageLedger(serverConfig.usageStateDir);\n const classifier = new HybridClassifier(config);\n const server = http.createServer(async (request, response) => {\n const requestId = crypto.randomUUID(); response.setHeader(\"x-request-id\", requestId);\n const route = new URL(request.url ?? \"/\", \"http://router.local\").pathname;\n try {\n if (request.method === \"GET\" && route === \"/health\") return send(response, 200, { ok: true, requestId });\n if (!isV1Route(request.method, route)) return send(response, 404, { error: { message: \"Not found\", type: \"not_found\" }, requestId });\n authorize(request, expectedToken);\n const client = clientKey(request, serverConfig.trustProxy);\n if (!limiter.allow(client)) throw new HttpError(429, \"Rate limit exceeded.\");\n if (route === \"/v1/models\" && request.method === \"GET\") return send(response, 200, { object: \"list\", data: router.availableModels().map((model) => ({ id: model.model, object: \"model\", owned_by: model.provider === \"openai-subscription\" ? \"openai-subscription\" : \"nvidia-nim\" })) });\n if (route !== \"/v1/chat/completions\" || request.method !== \"POST\") return send(response, 404, { error: { message: \"Not found\", type: \"not_found\" }, requestId });\n\n const body = await readJson(request, serverConfig.maxBodyBytes);\n const messages = Array.isArray(body.messages) ? body.messages as CompletionRequest[\"messages\"] : [];\n if (!messages.length) throw new HttpError(400, \"messages is required.\");\n if (isLocalTitleModel(body.model)) return sendLocalTitle(response, body.stream === true, messages);\n const userText = messages.filter((message) => message.role === \"user\").map((message) => typeof message.content === \"string\" ? message.content : JSON.stringify(message.content)).join(\"\\n\");\n const metadata = body.metadata as Partial & { taskId?: string; manualMax?: boolean } | undefined;\n const toolContinuation = messages.some((message) => message.role === \"tool\" || (message.role === \"assistant\" && Boolean(message.tool_calls?.length)));\n const features = await classifier.classify(userText, metadata, toolContinuation);\n const requestedModel = resolveRequestedModel(body.model, config);\n const requestOptions: Omit = {\n messages,\n stream: body.stream === true,\n maxTokens: typeof body.max_tokens === \"number\" ? body.max_tokens : undefined,\n tools: Array.isArray(body.tools) ? body.tools : undefined,\n toolChoice: body.tool_choice,\n responseFormat: body.response_format,\n temperature: typeof body.temperature === \"number\" ? body.temperature : undefined,\n topP: typeof body.top_p === \"number\" ? body.top_p : undefined,\n metadata: scalarMetadata(metadata)\n };\n const taskId = taskKey(request, metadata, body.messages, body.project);\n\n if (body.stream === true) {\n await ledger.withTask(taskId, config.routing, async (governor) => {\n const routed = router.stream(features, requestOptions, governor, metadata?.manualMax === true, requestedModel);\n response.statusCode = 200; response.setHeader(\"content-type\", \"text/event-stream\"); response.setHeader(\"cache-control\", \"no-cache\"); response.setHeader(\"connection\", \"keep-alive\"); setRouterHeaders(response, routed.decision);\n for await (const chunk of routed.stream) {\n const payload = { id: chunk.id, object: \"chat.completion.chunk\", created: Math.floor(Date.now() / 1000), model: chunk.model, choices: [{ index: 0, delta: chunk.delta ?? {}, finish_reason: chunk.finishReason ?? null }], usage: chunk.usage ? { prompt_tokens: chunk.usage.inputTokens ?? 0, completion_tokens: chunk.usage.outputTokens ?? 0, total_tokens: chunk.usage.totalTokens ?? 0 } : undefined, router: { selectedModel: routed.decision.model, provider: routed.decision.provider, thinking: routed.decision.thinking, reasoningEffort: routed.decision.reasoningEffort, alternatives: routed.decision.fallbackChain } };\n response.write(`data: ${JSON.stringify(payload)}\\n\\n`);\n }\n response.write(\"data: [DONE]\\n\\n\");\n });\n response.end();\n return;\n }\n\n const result = await ledger.withTask(taskId, config.routing, (governor) => router.complete(features, requestOptions, governor, metadata?.manualMax === true, requestedModel));\n setRouterHeaders(response, result.decision);\n return send(response, 200, { id: result.response.id, object: \"chat.completion\", created: Math.floor(Date.now() / 1000), model: result.response.model, choices: [{ index: 0, message: { role: \"assistant\", content: result.response.content, ...(result.response.toolCalls?.length ? { tool_calls: result.response.toolCalls } : {}) }, finish_reason: result.response.finishReason }], usage: result.response.usage ? { prompt_tokens: result.response.usage.inputTokens ?? 0, completion_tokens: result.response.usage.outputTokens ?? 0, total_tokens: result.response.usage.totalTokens ?? 0 } : undefined, router: { selectedModel: result.decision.model, provider: result.decision.provider, thinking: result.decision.thinking, reasoningEffort: result.decision.reasoningEffort, role: result.decision.role, alternatives: result.decision.fallbackChain } });\n } catch (error) {\n const status = error instanceof HttpError ? error.status : error instanceof GovernorError ? 429 : error instanceof SyntaxError ? 400 : 502;\n logger.error(\"request_failed\", { requestId, route, status, error: error instanceof Error ? error.message : String(error) });\n if (!response.headersSent) send(response, status, { error: { message: error instanceof Error ? error.message : String(error), type: status === 502 ? \"upstream_error\" : \"request_error\" }, requestId });\n else response.end(`data: ${JSON.stringify({ error: { message: error instanceof Error ? error.message : String(error) } })}\\n\\n`);\n }\n });\n const cleanup = setInterval(() => limiter.prune(), serverConfig.rateLimitWindowMs).unref();\n server.on(\"close\", () => clearInterval(cleanup));\n return server;\n}\n\nfunction isV1Route(method: string | undefined, route: string) { return (method === \"GET\" && route === \"/v1/models\") || route === \"/v1/chat/completions\"; }\nfunction isLocalTitleModel(value: unknown) { return value === \"local-title\" || value === \"ai-coding-router/local-title\"; }\nfunction sendLocalTitle(response: http.ServerResponse, stream: boolean, messages: CompletionRequest[\"messages\"]) {\n const title = localTitle(messages);\n const id = `local-title-${crypto.randomUUID()}`;\n response.setHeader(\"x-router-provider\", \"local\");\n response.setHeader(\"x-router-model\", \"local-title\");\n response.setHeader(\"x-router-thinking\", \"none\");\n if (stream) {\n response.statusCode = 200;\n response.setHeader(\"content-type\", \"text/event-stream\");\n response.write(`data: ${JSON.stringify({ id, object: \"chat.completion.chunk\", created: Math.floor(Date.now() / 1000), model: \"local-title\", choices: [{ index: 0, delta: { role: \"assistant\", content: title }, finish_reason: null }] })}\\n\\n`);\n response.end(\"data: [DONE]\\n\\n\");\n return;\n }\n return send(response, 200, { id, object: \"chat.completion\", created: Math.floor(Date.now() / 1000), model: \"local-title\", choices: [{ index: 0, message: { role: \"assistant\", content: title }, finish_reason: \"stop\" }], usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 } });\n}\nfunction localTitle(messages: CompletionRequest[\"messages\"]) {\n const source = [...messages].reverse().find((message) => message.role === \"user\")?.content;\n const text = typeof source === \"string\" ? source : JSON.stringify(source ?? \"Coding task\");\n const words = text.replace(/https?:\\/\\/\\S+/g, \"\").replace(/[^\\p{L}\\p{N}._-]+/gu, \" \").trim().split(/\\s+/).filter(Boolean);\n const title = words.slice(0, 7).join(\" \");\n return (title || \"Coding task\").slice(0, 80);\n}\nfunction authorize(request: http.IncomingMessage, expectedToken: string | undefined) { if (expectedToken && !tokenMatches(expectedToken, bearerToken({ headers: request.headers }))) throw new HttpError(401, \"Bearer authentication required.\"); }\nfunction clientKey(request: http.IncomingMessage, trustProxy: boolean) { if (trustProxy && typeof request.headers[\"x-forwarded-for\"] === \"string\") return request.headers[\"x-forwarded-for\"].split(\",\")[0].trim(); return request.socket.remoteAddress ?? \"unknown\"; }\nfunction setRouterHeaders(response: http.ServerResponse, decision: { provider: string; model: string; thinking: string; reasoningEffort?: string; requiresPlan: boolean; readOnly: boolean; fallbackChain: Array<{ provider: string; model: string; thinking: string; reasoningEffort?: string }> }) { response.setHeader(\"x-router-provider\", decision.provider); response.setHeader(\"x-router-model\", decision.model); response.setHeader(\"x-router-thinking\", decision.thinking); response.setHeader(\"x-router-reasoning-effort\", decision.reasoningEffort ?? decision.thinking); response.setHeader(\"x-router-alternatives\", decision.fallbackChain.map((candidate) => `${candidate.provider}/${candidate.model}:${candidate.reasoningEffort ?? candidate.thinking}`).join(\",\")); response.setHeader(\"x-router-requires-plan\", String(decision.requiresPlan)); response.setHeader(\"x-router-read-only\", String(decision.readOnly)); }\nfunction send(response: http.ServerResponse, status: number, payload: unknown) { response.statusCode = status; response.setHeader(\"content-type\", \"application/json\"); response.end(JSON.stringify(payload)); }\nasync function readJson(request: http.IncomingMessage, limit: number): Promise> {\n const declared = Number(request.headers[\"content-length\"] ?? 0); if (declared > limit) throw new HttpError(413, \"Request body exceeds the configured limit.\");\n let body = \"\";\n for await (const chunk of request) { body += chunk.toString(); if (Buffer.byteLength(body) > limit) throw new HttpError(413, \"Request body exceeds the configured limit.\"); }\n return JSON.parse(body || \"{}\");\n}\nfunction scalarMetadata(value: Record | undefined): Record | undefined { if (!value) return undefined; return Object.fromEntries(Object.entries(value).filter(([, item]) => [\"string\", \"number\", \"boolean\"].includes(typeof item))) as Record; }\nfunction taskKey(request: http.IncomingMessage, metadata: { taskId?: string } | undefined, messages: unknown, project: unknown) {\n if (metadata?.taskId) return metadata.taskId;\n const header = request.headers[\"x-router-task-id\"];\n if (typeof header === \"string\" && header) return header;\n // Tool-loop requests append assistant and tool messages while retaining the\n // latest user instruction. Key on that instruction so usage caps apply to a\n // complete OpenCode task instead of resetting after every tool call.\n const lastUser = Array.isArray(messages) ? [...messages].reverse().find((message: any) => message?.role === \"user\") : undefined;\n return crypto.createHash(\"sha256\").update(JSON.stringify({ project, user: lastUser?.content ?? messages })).digest(\"hex\");\n}\nfunction resolveRequestedModel(value: unknown, config: RouterConfig): string | undefined {\n if (value === undefined || value === null || value === \"router\" || value === \"ai-coding-router/router\") return undefined;\n if (typeof value !== \"string\") throw new HttpError(400, \"model must be a string.\");\n const model = value.startsWith(\"ai-coding-router/\") ? value.slice(\"ai-coding-router/\".length) : value;\n try { assertAllowedModel(config, model); return model; }\n catch { throw new HttpError(400, `Model '${value}' is not available in this router allowlist.`); }\n}\n"}, "29b978c19b6af6a655f9b0c8a507606385a4ad65": {"t": 1785771141.6150155, "n": 12468, "h": "65a8772db66f7904", "c": "import http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type { CompletionRequest, RouterConfig, TaskFeatures } from \"./types.js\";\nimport { HybridClassifier } from \"./routing/hybrid-classifier.js\";\nimport { ModelRouter } from \"./routing/router.js\";\nimport { GovernorError } from \"./core/governor.js\";\nimport { FileUsageLedger } from \"./core/usage-ledger.js\";\nimport { Logger } from \"./core/logger.js\";\nimport { RateLimiter, assertAuthConfigured, bearerToken, tokenMatches } from \"./core/security.js\";\nimport { assertAllowedModel } from \"./config.js\";\n\nclass HttpError extends Error { constructor(readonly status: number, message: string) { super(message); } }\n\nexport function createServer(router: ModelRouter, config: RouterConfig, logger: Pick = new Logger(\"http\")) {\n const serverConfig = config.server;\n const expectedToken = assertAuthConfigured(config);\n const limiter = new RateLimiter(serverConfig.rateLimitWindowMs, serverConfig.rateLimitMaxRequests);\n const ledger = new FileUsageLedger(serverConfig.usageStateDir);\n const classifier = new HybridClassifier(config);\n const server = http.createServer(async (request, response) => {\n const requestId = crypto.randomUUID(); response.setHeader(\"x-request-id\", requestId);\n const route = new URL(request.url ?? \"/\", \"http://router.local\").pathname;\n try {\n if (request.method === \"GET\" && route === \"/health\") return send(response, 200, { ok: true, requestId });\n if (!isV1Route(request.method, route)) return send(response, 404, { error: { message: \"Not found\", type: \"not_found\" }, requestId });\n authorize(request, expectedToken);\n const client = clientKey(request, serverConfig.trustProxy);\n if (!limiter.allow(client)) throw new HttpError(429, \"Rate limit exceeded.\");\n if (route === \"/v1/models\" && request.method === \"GET\") return send(response, 200, { object: \"list\", data: router.availableModels().map((model) => ({ id: model.model, object: \"model\", owned_by: model.provider === \"openai-subscription\" ? \"openai-subscription\" : \"nvidia-nim\" })) });\n if (route !== \"/v1/chat/completions\" || request.method !== \"POST\") return send(response, 404, { error: { message: \"Not found\", type: \"not_found\" }, requestId });\n\n const body = await readJson(request, serverConfig.maxBodyBytes);\n const messages = Array.isArray(body.messages) ? body.messages as CompletionRequest[\"messages\"] : [];\n if (!messages.length) throw new HttpError(400, \"messages is required.\");\n if (isLocalTitleModel(body.model)) return sendLocalTitle(response, body.stream === true, messages);\n const userText = messages.filter((message) => message.role === \"user\").map((message) => typeof message.content === \"string\" ? message.content : JSON.stringify(message.content)).join(\"\\n\");\n const metadata = body.metadata as Partial & { taskId?: string; manualMax?: boolean } | undefined;\n const toolContinuation = messages.some((message) => message.role === \"tool\" || (message.role === \"assistant\" && Boolean(message.tool_calls?.length)));\n const features = await classifier.classify(userText, metadata, toolContinuation);\n const requestedModel = resolveRequestedModel(body.model, config);\n const requestOptions: Omit = {\n messages,\n stream: body.stream === true,\n maxTokens: typeof body.max_tokens === \"number\" ? body.max_tokens : undefined,\n tools: Array.isArray(body.tools) ? body.tools : undefined,\n toolChoice: body.tool_choice,\n responseFormat: body.response_format,\n temperature: typeof body.temperature === \"number\" ? body.temperature : undefined,\n topP: typeof body.top_p === \"number\" ? body.top_p : undefined,\n metadata: scalarMetadata(metadata)\n };\n const taskId = taskKey(request, metadata, body.messages, body.project);\n\n if (body.stream === true) {\n await ledger.withTask(taskId, config.routing, async (governor) => {\n const routed = router.stream(features, requestOptions, governor, metadata?.manualMax === true, requestedModel);\n response.statusCode = 200; response.setHeader(\"content-type\", \"text/event-stream\"); response.setHeader(\"cache-control\", \"no-cache\"); response.setHeader(\"connection\", \"keep-alive\"); setRouterHeaders(response, routed.decision);\n for await (const chunk of routed.stream) {\n const payload = { id: chunk.id, object: \"chat.completion.chunk\", created: Math.floor(Date.now() / 1000), model: chunk.model, choices: [{ index: 0, delta: chunk.delta ?? {}, finish_reason: chunk.finishReason ?? null }], usage: chunk.usage ? { prompt_tokens: chunk.usage.inputTokens ?? 0, completion_tokens: chunk.usage.outputTokens ?? 0, total_tokens: chunk.usage.totalTokens ?? 0 } : undefined, router: { selectedModel: routed.decision.model, provider: routed.decision.provider, thinking: routed.decision.thinking, alternatives: routed.decision.fallbackChain } };\n response.write(`data: ${JSON.stringify(payload)}\\n\\n`);\n }\n response.write(\"data: [DONE]\\n\\n\");\n });\n response.end();\n return;\n }\n\n const result = await ledger.withTask(taskId, config.routing, (governor) => router.complete(features, requestOptions, governor, metadata?.manualMax === true, requestedModel));\n setRouterHeaders(response, result.decision);\n return send(response, 200, { id: result.response.id, object: \"chat.completion\", created: Math.floor(Date.now() / 1000), model: result.response.model, choices: [{ index: 0, message: { role: \"assistant\", content: result.response.content, ...(result.response.toolCalls?.length ? { tool_calls: result.response.toolCalls } : {}) }, finish_reason: result.response.finishReason }], usage: result.response.usage ? { prompt_tokens: result.response.usage.inputTokens ?? 0, completion_tokens: result.response.usage.outputTokens ?? 0, total_tokens: result.response.usage.totalTokens ?? 0 } : undefined, router: { selectedModel: result.decision.model, provider: result.decision.provider, thinking: result.decision.thinking, role: result.decision.role, alternatives: result.decision.fallbackChain } });\n } catch (error) {\n const status = error instanceof HttpError ? error.status : error instanceof GovernorError ? 429 : error instanceof SyntaxError ? 400 : 502;\n logger.error(\"request_failed\", { requestId, route, status, error: error instanceof Error ? error.message : String(error) });\n if (!response.headersSent) send(response, status, { error: { message: error instanceof Error ? error.message : String(error), type: status === 502 ? \"upstream_error\" : \"request_error\" }, requestId });\n else response.end(`data: ${JSON.stringify({ error: { message: error instanceof Error ? error.message : String(error) } })}\\n\\n`);\n }\n });\n const cleanup = setInterval(() => limiter.prune(), serverConfig.rateLimitWindowMs).unref();\n server.on(\"close\", () => clearInterval(cleanup));\n return server;\n}\n\nfunction isV1Route(method: string | undefined, route: string) { return (method === \"GET\" && route === \"/v1/models\") || route === \"/v1/chat/completions\"; }\nfunction isLocalTitleModel(value: unknown) { return value === \"local-title\" || value === \"ai-coding-router/local-title\"; }\nfunction sendLocalTitle(response: http.ServerResponse, stream: boolean, messages: CompletionRequest[\"messages\"]) {\n const title = localTitle(messages);\n const id = `local-title-${crypto.randomUUID()}`;\n response.setHeader(\"x-router-provider\", \"local\");\n response.setHeader(\"x-router-model\", \"local-title\");\n response.setHeader(\"x-router-thinking\", \"none\");\n if (stream) {\n response.statusCode = 200;\n response.setHeader(\"content-type\", \"text/event-stream\");\n response.write(`data: ${JSON.stringify({ id, object: \"chat.completion.chunk\", created: Math.floor(Date.now() / 1000), model: \"local-title\", choices: [{ index: 0, delta: { role: \"assistant\", content: title }, finish_reason: null }] })}\\n\\n`);\n response.end(\"data: [DONE]\\n\\n\");\n return;\n }\n return send(response, 200, { id, object: \"chat.completion\", created: Math.floor(Date.now() / 1000), model: \"local-title\", choices: [{ index: 0, message: { role: \"assistant\", content: title }, finish_reason: \"stop\" }], usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 } });\n}\nfunction localTitle(messages: CompletionRequest[\"messages\"]) {\n const source = [...messages].reverse().find((message) => message.role === \"user\")?.content;\n const text = typeof source === \"string\" ? source : JSON.stringify(source ?? \"Coding task\");\n const words = text.replace(/https?:\\/\\/\\S+/g, \"\").replace(/[^\\p{L}\\p{N}._-]+/gu, \" \").trim().split(/\\s+/).filter(Boolean);\n const title = words.slice(0, 7).join(\" \");\n return (title || \"Coding task\").slice(0, 80);\n}\nfunction authorize(request: http.IncomingMessage, expectedToken: string | undefined) { if (expectedToken && !tokenMatches(expectedToken, bearerToken({ headers: request.headers }))) throw new HttpError(401, \"Bearer authentication required.\"); }\nfunction clientKey(request: http.IncomingMessage, trustProxy: boolean) { if (trustProxy && typeof request.headers[\"x-forwarded-for\"] === \"string\") return request.headers[\"x-forwarded-for\"].split(\",\")[0].trim(); return request.socket.remoteAddress ?? \"unknown\"; }\nfunction setRouterHeaders(response: http.ServerResponse, decision: { provider: string; model: string; thinking: string; requiresPlan: boolean; readOnly: boolean; fallbackChain: Array<{ provider: string; model: string; thinking: string }> }) { response.setHeader(\"x-router-provider\", decision.provider); response.setHeader(\"x-router-model\", decision.model); response.setHeader(\"x-router-thinking\", decision.thinking); response.setHeader(\"x-router-alternatives\", decision.fallbackChain.map((candidate) => `${candidate.provider}/${candidate.model}:${candidate.thinking}`).join(\",\")); response.setHeader(\"x-router-requires-plan\", String(decision.requiresPlan)); response.setHeader(\"x-router-read-only\", String(decision.readOnly)); }\nfunction send(response: http.ServerResponse, status: number, payload: unknown) { response.statusCode = status; response.setHeader(\"content-type\", \"application/json\"); response.end(JSON.stringify(payload)); }\nasync function readJson(request: http.IncomingMessage, limit: number): Promise> {\n const declared = Number(request.headers[\"content-length\"] ?? 0); if (declared > limit) throw new HttpError(413, \"Request body exceeds the configured limit.\");\n let body = \"\";\n for await (const chunk of request) { body += chunk.toString(); if (Buffer.byteLength(body) > limit) throw new HttpError(413, \"Request body exceeds the configured limit.\"); }\n return JSON.parse(body || \"{}\");\n}\nfunction scalarMetadata(value: Record | undefined): Record | undefined { if (!value) return undefined; return Object.fromEntries(Object.entries(value).filter(([, item]) => [\"string\", \"number\", \"boolean\"].includes(typeof item))) as Record; }\nfunction taskKey(request: http.IncomingMessage, metadata: { taskId?: string } | undefined, messages: unknown, project: unknown) {\n if (metadata?.taskId) return metadata.taskId;\n const header = request.headers[\"x-router-task-id\"];\n if (typeof header === \"string\" && header) return header;\n // Tool-loop requests append assistant and tool messages while retaining the\n // latest user instruction. Key on that instruction so usage caps apply to a\n // complete OpenCode task instead of resetting after every tool call.\n const lastUser = Array.isArray(messages) ? [...messages].reverse().find((message: any) => message?.role === \"user\") : undefined;\n return crypto.createHash(\"sha256\").update(JSON.stringify({ project, user: lastUser?.content ?? messages })).digest(\"hex\");\n}\nfunction resolveRequestedModel(value: unknown, config: RouterConfig): string | undefined {\n if (value === undefined || value === null || value === \"router\" || value === \"ai-coding-router/router\") return undefined;\n if (typeof value !== \"string\") throw new HttpError(400, \"model must be a string.\");\n const model = value.startsWith(\"ai-coding-router/\") ? value.slice(\"ai-coding-router/\".length) : value;\n try { assertAllowedModel(config, model); return model; }\n catch { throw new HttpError(400, `Model '${value}' is not available in this router allowlist.`); }\n}\n"}}}}