/** * In-memory account registry for {@link AccountManager}. * * Owns the mutable account list, per-family cursors/active indices, and * auxiliary in-memory state (toast debounce, auth-failure counters). * Other account services (persistence, rotation, recovery) receive a reference * to an `AccountState` instance and mutate it through this narrow surface. */ import { type ModelFamily } from "../prompts/codex.js"; import type { AccountIdSource, OAuthAuthDetails } from "../types.js"; import { type RateLimitReason } from "./rate-limits.js"; import type { AccountStorageV3, CooldownReason, RateLimitStateV3 } from "../storage.js"; export interface ManagedAccount { index: number; accountId?: string; accountUserId?: string; organizationId?: string; accountIdSource?: AccountIdSource; accountLabel?: string; planType?: string; accountTags?: string[]; accountNote?: string; email?: string; refreshToken: string; enabled?: boolean; access?: string; expires?: number; oauthScope?: string; /** When the refresh token was last rotated (ms since epoch); see AccountMetadataV3. */ tokenRotatedAt?: number; addedAt: number; lastUsed: number; lastSwitchReason?: "rate-limit" | "initial" | "rotation"; lastRateLimitReason?: RateLimitReason; rateLimitResetTimes: RateLimitStateV3; quotaExhaustedUntil?: number; /** When the quota-exhaustion stamp was written; see AccountMetadataV3. */ quotaExhaustedStampAt?: number; /** Doctor-clear tombstone; see AccountMetadataV3. */ quotaExhaustedClearedAt?: number; coolingDownUntil?: number; cooldownReason?: CooldownReason; } export interface AccountSelectionExplainability { index: number; enabled: boolean; isCurrentForFamily: boolean; eligible: boolean; reasons: string[]; healthScore: number; tokensAvailable: number; rateLimitedUntil?: number; quotaExhaustedUntil?: number; coolingDownUntil?: number; cooldownReason?: CooldownReason; lastUsed: number; } export declare class AccountState { accounts: ManagedAccount[]; cursorByFamily: Record; currentAccountIndexByFamily: Record; lastToastAccountIndex: number; lastToastTime: number; authFailuresByRefreshToken: Map; /** * Per-refresh-token promise chain used to serialize concurrent * `incrementAuthFailures` calls. Prevents lost updates when two org-variant * accounts that share a refresh token observe an auth failure at once — see * the lost-update fix for shared-refresh-token accounts. */ incrementAuthFailuresChain: Map>; /** * Set when `initializeFromStorage` re-enabled an account that an earlier * build had wrongly disabled for missing scopes. The repair happens in * memory, so it must be flushed to disk or every surface that reads storage * directly keeps showing the stale disabled state and re-auth note. * `consumeScopeRepairs()` clears it, so the extra write happens once. */ private scopeRepairsPending; consumeScopeRepairs(): boolean; initializeFromStorage(authFallback: OAuthAuthDetails | undefined, stored: AccountStorageV3 | null | undefined): void; hasRefreshToken(refreshToken: string): boolean; getAccountCount(): number; getActiveIndexForFamily(family: ModelFamily): number; getAccountsSnapshot(): ManagedAccount[]; getSelectionExplainability(family: ModelFamily, model?: string | null, now?: number): AccountSelectionExplainability[]; setActiveIndex(index: number): ManagedAccount | null; getCurrentAccountForFamily(family: ModelFamily): ManagedAccount | null; shouldShowAccountToast(accountIndex: number, debounceMs?: number): boolean; markToastShown(accountIndex: number): void; updateFromAuth(account: ManagedAccount, auth: OAuthAuthDetails): void; /** * After a refresh rotates the refresh token on `account`, update every OTHER * account that still holds the pre-rotation token so the shared credential * stays consistent across org-variant siblings. */ private propagateRotatedRefreshTokenToSiblings; toAuthDetails(account: ManagedAccount): OAuthAuthDetails; markSwitched(account: ManagedAccount, reason: "rate-limit" | "initial" | "rotation", family: ModelFamily): void; removeAccount(account: ManagedAccount): boolean; removeAccountByIndex(index: number): boolean; setAccountEnabled(index: number, enabled: boolean): ManagedAccount | null; /** * Check whether a cooldown window is still active for the given account. * Clears expired cooldowns as a side-effect so that stale `coolingDownUntil` * timestamps do not leak into snapshots or persistence. */ isAccountCoolingDown(account: ManagedAccount): boolean; clearAccountCooldown(account: ManagedAccount): void; /** * Shared predicate used by rotation and diagnostic paths: is this account * usable for the given family/model right now? Combines enabled flag, * rate-limit expiry, and cooldown. */ isEligibleForFamily(account: ManagedAccount, family: ModelFamily, model?: string | null): boolean; } //# sourceMappingURL=state.d.ts.map