/** Minimal account shape the selector reasons over (registry Account satisfies this). */ export interface SelectableAccount { name: string; priority: number; enabled: boolean; } /** * How eligible accounts are ordered. * - `priority`: lowest `priority` number first (ties by name) - the classic order. * - `most-room`: the account with the most remaining headroom first (the * least-used one), ties broken by priority then name. Needs `roomOf`. */ export type AccountOrder = 'priority' | 'most-room'; export interface SelectInput { accounts: T[]; /** Names currently logged in. */ loggedIn: Set; /** Names currently rate-limited (empty in Phase 1; filled by the ledger in Phase 2). */ capped: Set; /** A manually pinned account; used if it is still eligible. */ pinned?: string; /** Ordering policy for eligible accounts. Defaults to `priority`. */ order?: AccountOrder; /** * Remaining headroom 0..1 for an account (higher = less used), used by the * `most-room` order. Omitted (or returning the same for all) falls back to * priority ordering, so a caller with no usage data keeps the classic order. */ roomOf?: (name: string) => number; } export type SelectResult = { ok: true; account: T; } | { ok: false; reason: string; }; /** * Pure active-account policy: pick an enabled, logged-in, non-capped account. * A pinned account wins when still eligible; otherwise the lowest `priority` * (ties broken by name). Generic so it returns the caller's full account type * (e.g. a registry Account with its `dir`), not just the minimal shape. */ export declare function select(input: SelectInput): SelectResult; /** * Every account that could run, in the order this policy would try them. * * `select` is the first of these. Callers that must choose among several (the * rotation planner needs the whole list, because the best account depends on * which model still has room) take the list instead of re-deriving eligibility * themselves. One definition, so a second copy cannot drift from it. */ export declare function eligibleInOrder(input: SelectInput): T[]; /** * Sort order for eligible accounts. * * `most-room` puts the account with the most remaining headroom first (the * least-used one); when two are equally roomy, or `roomOf` is not provided, it * falls through to the classic priority-then-name tiebreak, so the order is * always fully determined and a caller without usage data behaves as before. */ export declare function orderComparator(order: AccountOrder, roomOf: ((name: string) => number) | undefined): (a: T, b: T) => number;