import type { ProxyAccountPrecedenceReason, ProxyAccountRankingPolicy, ProxyAccountRoutingReason, ProxyAccountSortMetrics, ProxyAccountSpillMove, ProxyAffinityPrecedenceSkipReason, ProxyPassthroughAccount } from "../types/index.js";
/**
 * The pre-existing "spend the soonest-expiring weekly allowance first" order.
 * Moved verbatim from claudeProxyRoutes.ts — availability → quota-evidence
 * quality → session saturation → model-scoped saturation → soonest weekly
 * reset → soonest session-reset bucket → model-scoped utilization → highest
 * weekly utilization → configured primary → insertion order.
 */
export declare function compareExpiryFirst(a: ProxyPassthroughAccount, b: ProxyPassthroughAccount, metricsByKey: ReadonlyMap<string, ProxyAccountSortMetrics>, primaryKey: string | undefined): [number, ProxyAccountRoutingReason];
/**
 * Availability → quota-evidence quality → session saturation → model-scoped
 * saturation → highest headroom (min(1 - sessionUsed, 1 - weeklyUsed), reset-
 * freshened via the same accountSortMetrics values compareExpiryFirst uses)
 * → soonest weekly reset → configured primary → insertion order. An account
 * whose headroom cannot be computed sorts after every account whose can.
 */
export declare function compareHeadroomFirst(a: ProxyPassthroughAccount, b: ProxyPassthroughAccount, metricsByKey: ReadonlyMap<string, ProxyAccountSortMetrics>, primaryKey: string | undefined): [number, ProxyAccountRoutingReason];
/**
 * Whether an account may hold a session binding or take the primary's place:
 * usable and not session-saturated.
 */
export declare function isPrecedenceEligible(metrics: ProxyAccountSortMetrics | undefined): boolean;
/**
 * Applies the precedence order a fill-first request tries: (1) the bound
 * (session-affinity) account, (2) the configured primary, (3) the rest in
 * ranking order. Shared by rankAccounts (for the quota-ordered path) and the
 * route directly (for quota-off fill-first, where the base order is never
 * comparator-sorted but affinity/prefer-primary must still apply per spec).
 * `strategy: round-robin` ignores all five policy keys and never calls this.
 *
 * When affinity applies and prefer-primary is also on, and the configured
 * primary is itself eligible (usable, not session-saturated) and is not the
 * bound account, the primary is placed second — [bound, primary, ...rest] —
 * so a request that cannot use its bound account's session still prefers
 * the primary over the rest of the ranking. The reported reason stays
 * "session_affinity" since the bound account still won rank 0.
 */
export declare function applyAffinityAndPrimary(args: {
    orderedAccounts: ProxyPassthroughAccount[];
    metricsByKey: ReadonlyMap<string, ProxyAccountSortMetrics>;
    affinityKey?: string;
    primaryKey?: string;
    preferPrimary?: boolean;
}): {
    orderedAccounts: ProxyPassthroughAccount[];
    reason: ProxyAccountPrecedenceReason | null;
    affinitySkippedReason: ProxyAffinityPrecedenceSkipReason | null;
};
/**
 * Orders accounts by the configured ranking (`expiry-first` or
 * `headroom-first`), applies the affinity/prefer-primary precedence, and
 * reports the reason that placed rank 0.
 */
export declare function rankAccounts(args: {
    accounts: ProxyPassthroughAccount[];
    metricsByKey: ReadonlyMap<string, ProxyAccountSortMetrics>;
    primaryKey: string | undefined;
    ranking?: ProxyAccountRankingPolicy;
    affinityKey?: string;
    preferPrimary?: boolean;
}): {
    orderedAccounts: ProxyPassthroughAccount[];
    reason: ProxyAccountRoutingReason;
    affinitySkippedReason: ProxyAffinityPrecedenceSkipReason | null;
};
/**
 * For a request without an active binding: if the current first choice is
 * already at spillInflight in-flight requests, try the next account below
 * that threshold that is usable and not session-saturated — the same
 * eligibility as every precedence step. An account with no metrics, an
 * unusable one or a saturated one is never a spill target even if its
 * in-flight count is low. If there is no eligible account under the
 * threshold, spill does nothing. `spill.inflight` records the SOURCE
 * account's count — the number that triggered the move, not the
 * destination's.
 */
export declare function applySpill(args: {
    orderedAccounts: ProxyPassthroughAccount[];
    inflightByKey: ReadonlyMap<string, number>;
    metricsByKey: ReadonlyMap<string, ProxyAccountSortMetrics>;
    spillInflight: number;
}): {
    orderedAccounts: ProxyPassthroughAccount[];
    spill: ProxyAccountSpillMove | null;
};
