import type { IncomingMessage, ServerResponse } from "node:http"; import type { Config, ResolvedTarget } from "../config.js"; import type { ToolUseValidator } from "../validator.js"; import type { Reshaper } from "../reshaper.js"; import type { MetadataLogger, RequestLog } from "../log.js"; import type { CircuitBreaker } from "../circuit-breaker.js"; import { CredentialLru } from "../credential-select.js"; import type { ModelLimits } from "../catalog.js"; import type { JsonSchema, AssistantMessage } from "../anthropic.js"; import type { ResolvedAttempt } from "../resolved-attempt.js"; import type { RequestAccountingState } from "../accounting-state.js"; import { Pool429Tracker, type CandidateRunnerHandlers, type HealthAttempt, type StickyRequestContext } from "../candidate-runner.js"; export interface MessagesHandlers extends CandidateRunnerHandlers { validator: ToolUseValidator; logger: MetadataLogger; breaker: CircuitBreaker; credentialLru: CredentialLru; hedgeMaxInFlight: number; isDestructive: (name: string) => boolean; resolveReshaper: (attempt: ResolvedAttempt) => Reshaper | undefined; withRepairAccounting: (reshaper: Reshaper, accounting: RequestAccountingState | null) => Reshaper; catalog: { cachedLimits: (provider: string, model: string) => ModelLimits | null | undefined; }; } export interface MessagesContext { req: IncomingMessage; reqBuf: Buffer; reqJson: unknown; path: string; pathname: string; started: number; hadTools: boolean; tools: Map; wantsStream: boolean; walkAttempts: ResolvedAttempt[]; addressedPool: string | null; degradedSpecs: Set | null; sticky: StickyRequestContext | null; quotaDemotedFirst: string | null; latencyDemotedFirst: string | null; pacedFirst: string | null; accounting: RequestAccountingState | null; cfg: Config; /** * The relay's own chars/4 estimate of this request's INPUT size (`estimateRequestTokens` in * `metadata.ts`), captured once in `handle()` alongside `routingNow` — the same value the context * guardrail already computed, threaded rather than re-estimated so a hedge decision and the * guardrail can never disagree about how big this request is. */ estimatedInputTokens: number; /** * The ONE routing instant, captured in `handle` after route-level pruning. * * ⚠ It is threaded in rather than re-read here, and that is load-bearing. The same instant drives * `orderDeploymentGroupsByUsability`, `rankCredentialAttempts` and this walk, so the ordering and * the walk cannot disagree about whether a cell is cooling. Calling `Date.now()` here instead * re-reads the clock per candidate, so a cooldown lapsing mid-walk changes the answer part-way * through and the walk stops being deterministic — and the OpenAI front, which still passes * `routingNow`, would then run a different policy from this one. */ routingNow: number; } export interface AnthropicCtx { tools: Map; streamed: boolean; started: number; path: string; hadTools: boolean; req?: IncomingMessage; retryAfterOverrideMs?: number | undefined; poolSummary?: string | null; poolUnknownRefusals?: number | null; tried?: readonly string[]; credentialHeaders?: Record; degraded?: string | null; quotaDemoted?: string | null; latencyDemoted?: string | null; paced?: string | null; probation?: string | null; hedged?: string | null; paid?: string | null; sticky?: StickyRequestContext | null; target: ResolvedTarget; attempt: HealthAttempt; signal: AbortSignal; callerSignal: AbortSignal; reportedModelSource: Response; } export type RepairCtx = AnthropicCtx & { wantsStream: boolean; reshaper: Reshaper; maxAttempts: number; pool429: Pool429Tracker; }; export interface BufferedRepairDeadTurn { validated: RequestLog["validated"]; toolUseCount: number; uncheckableCount: number; errorKinds: string[]; } export declare function transparentPath(res: ServerResponse, backendRes: Response, timer: NodeJS.Timeout, ctx: AnthropicCtx & { willValidate: boolean; }, h: MessagesHandlers): Promise; export declare function repairPath(res: ServerResponse, backendRes: Response, timer: NodeJS.Timeout, ctx: RepairCtx, h: MessagesHandlers): Promise; export declare function repairStreamingPath(res: ServerResponse, backendRes: Response, timer: NodeJS.Timeout, ctx: RepairCtx, h: MessagesHandlers): Promise; export declare function repairBufferedPath(res: ServerResponse, backendRes: Response, timer: NodeJS.Timeout, ctx: RepairCtx, h: MessagesHandlers): Promise; export declare function emitFixed(res: ServerResponse, status: number, filtered: Record, message: AssistantMessage, wantsStream: boolean, beforeBody?: () => void): void; export declare function toAnthropicMessage(msg: AssistantMessage): object; export declare function anthropicMessagesPath(res: ServerResponse, ctx: MessagesContext, h: MessagesHandlers): Promise;