import { type AccountInfo } from '../agents.js';
import type { AgentId } from '../types.js';
/**
 * Why a usage read produced no snapshot, when the cause is the credential or the
 * server rather than the payload. Every provider used to return `error: null`
 * for all three, which made an account nobody can read indistinguishable from a
 * healthy one: the caller fell back to whatever was in the SWR cache and
 * rendered its bars as fact. On `yosemite-s1` that hid five Claude accounts
 * whose stored access token had expired — one of them eleven days earlier —
 * behind a cache frozen for 26h, and balanced routing launched into an account
 * that was actually at its weekly cap.
 *
 * No usage read ever refreshes a token (RUSH-1822 for Claude; the same rule for
 * Kimi/Droid/Cursor, whose own CLIs rotate on their next launch), so an expired
 * credential cannot heal on its own — the account stays unreadable until that
 * agent actually runs, or a long-lived token is provisioned for it.
 *
 * Shared across all four networked providers on purpose: the failure shape is
 * identical, and wiring only Claude would leave `agents view --refresh`
 * reporting Claude accounts while silently presenting stale Kimi, Droid, and
 * Cursor readings as confirmed.
 */
export declare function usageNoCredentialError(agent: string): string;
export declare function usageExpiredCredentialError(agent: string): string;
/**
 * Kimi-specific expired-credential wording. A normal Kimi launch refreshes its
 * own OAuth access token, so the recovery action for an expired Kimi credential
 * is to run Kimi once — not to re-auth through agents-cli (RUSH-3198).
 */
export declare function usageExpiredKimiCredentialError(): string;
export declare function usageRejectedError(agent: string, status: number): string;
/**
 * Canonical phrase for the Anthropic setup-token scope gap (RUSH-2392).
 * `claude setup-token` mints `user:inference` only; the usage endpoint requires
 * `user:profile`. The account can still run; usage bars cannot populate via
 * that token. Callers detect this string with {@link isUsageHeadlessScopeError}
 * so the UI can render it distinctly from a generic "unverified" failure.
 */
export declare const USAGE_HEADLESS_SCOPE_MARKER = "usage unavailable (headless)";
/**
 * Distinct error when Claude's usage API returns 403 because the setup-token
 * lacks `user:profile` (RUSH-2392). Not a revocation, not a missing mint —
 * a permanent tradeoff of the headless credential.
 */
export declare function usageHeadlessScopeError(agent?: string): string;
/** True when an error string is the setup-token scope gap (RUSH-2392). */
export declare function isUsageHeadlessScopeError(error: string | null | undefined): boolean;
/**
 * Canonical phrase for a Claude account the usage reader holds no usable
 * credential for. Distinct from {@link USAGE_HEADLESS_SCOPE_MARKER}, which
 * means a setup-token WAS read and the endpoint refused its scope.
 */
export declare const USAGE_NO_USAGE_CREDENTIAL_MARKER = "usage unavailable (no usage credential)";
/**
 * Claude's own no-credential message. The shared
 * {@link usageNoCredentialError} offers "sign in" as the remedy, which holds
 * for Kimi/Droid/Cursor — their CLIs rotate a readable token on the next launch
 * — and is false for Claude: the usage read deliberately never touches the
 * interactive login (RUSH-1822), so an account that IS signed in reads as
 * unreadable here and signing in again changes nothing. Naming only the second
 * remedy would send the operator to `claude setup-token`, whose token then hits
 * the `user:profile` scope gap (RUSH-2392) — the loop reported in #2987 — so
 * this message states both constraints and that the account still runs.
 */
export declare function usageNoClaudeUsageCredentialError(): string;
/** True when an error string is the Claude no-usage-credential state (#2987). */
export declare function isUsageNoUsageCredentialError(error: string | null | undefined): boolean;
/**
 * Detect Anthropic's usage-endpoint scope denial: HTTP 403 whose body names
 * `user:profile` (or "scope requirement"). A bare 403 without that body stays
 * classified as a real rejection — only the known setup-token shape is special
 * (RUSH-2392).
 */
export declare function isClaudeUsageScopeDenied(status: number, bodyText: string | null | undefined): boolean;
/**
 * The read threw rather than answering — a timeout, DNS/TLS failure, a payload
 * that would not parse, a credential that would not decrypt. Every provider
 * swallowed these into `error: null`, which is the same silence as an expired
 * token: the caller renders a stale snapshot as confirmed. The cause is carried
 * verbatim because these are the failures a user cannot otherwise see.
 */
/**
 * The provider told us to back off and we are still inside that window, so this
 * read made no request at all. Distinct from `usageRejectedError(agent, 429)`,
 * which is the 429 itself: this one says we are *honouring* it.
 */
export declare function usageThrottledError(agent: string, untilMs: number): string;
export declare function usageUnreachableError(agent: string, cause?: unknown): string;
/**
 * Marker for a log-based (`network: false`) provider — Codex, Grok — that has
 * simply never recorded a rate-limit event on this machine yet: no session
 * log exists, or no session in it carries usage data. Distinct on purpose from
 * `usageUnreachableError`: that one means the local log COULDN'T be read (a
 * real failure worth surfacing distinctly); this one means there is nothing to
 * read because the account has not run here, which is expected for a fresh
 * install and should render as a benign state, not an error (RUSH-3040).
 */
export declare const USAGE_NO_RECENT_USAGE_MARKER = "no usage recorded yet";
export declare const USAGE_BENIGN_STATE: unique symbol;
export type UsageBenignState = 'no-recent-usage';
/**
 * Sentinel `UsageInfo.error` for a read-only lookup whose cache held nothing
 * (`getUsageInfoForIdentity`). No request was made and nothing failed — the
 * daemon simply has not collected this account yet. It was an unclassified
 * literal, so `classifyUsageErrorKind` fell through to `'rejected'` and
 * `agents view` printed the generic "usage unavailable" for a cold cache,
 * which reads as a failure the operator should chase (#2987). The string value
 * is unchanged; callers that already compare against `'stale'` keep working.
 */
export declare const USAGE_NOT_COLLECTED_MARKER = "stale";
/**
 * Human-facing form of a `UsageInfo.error` for a machine/JSON consumer
 * (`agents view --json`'s `usageError`). Every error string this module
 * constructs is already a full human sentence EXCEPT the internal
 * {@link USAGE_NOT_COLLECTED_MARKER} (`'stale'`) sentinel, which a read-only
 * lookup returns for a never-cached account when `--refresh` was not passed. That
 * value is an internal cache signal, not an error message, and leaking it verbatim
 * contradicts the field's "human-readable" contract (PHNX-3348). Map it to a
 * plain-language, actionable string and pass every genuine error through
 * unchanged. Returns `null` when there is no error.
 */
export declare function usageErrorForDisplay(error: string | null | undefined): string | null;
/**
 * Shared error-classification + 429 backoff for a networked usage fetch whose
 * only signal is an HTTP status (or none at all, on a network failure) —
 * Antigravity's :retrieveUserQuota and Muse's Meta Model API probe are both
 * this shape. They were added to `USAGE_SOURCES` after the four original
 * `usageXError` constructors and did not get their own scheme (usage.ts's
 * error handling was written for "four networked providers"; RUSH-3040).
 * Route every no-snapshot outcome for either through this one function so a
 * future entry cannot be added second-class again — it owns noting the 429
 * backoff, so callers must NOT also call {@link noteUsageRateLimited} for the
 * same response.
 */
export declare function classifyUsageFetchFailure(agent: string, agentId: 'antigravity' | 'muse', status: number | null, retryAfterHeader: string | null | undefined, usageScope?: string | null): string;
/**
 * The specific cause behind a `UsageInfo.error`, so a renderer can name the
 * exact state instead of a generic "usage unavailable" for one of several
 * distinct causes (RUSH-3040). Matched against the canonical strings this file
 * constructs — never re-derive these prefixes at a call site.
 */
export type UsageErrorKind = 'no-credential' | 'no-usage-credential' | 'expired-credential' | 'rate-limited' | 'rejected' | 'headless-scope' | 'unreachable' | 'not-collected';
/** Classify a `UsageInfo.error` string into its {@link UsageErrorKind}, or null when there is no error. */
export declare function classifyUsageErrorKind(error: string | null | undefined): UsageErrorKind | null;
/**
 * True when a Claude OAuth access token is within the refresh leeway of expiry
 * (or already expired) — i.e. it "would need a refresh" before the next use.
 *
 * Single source of truth for the expiry gate, shared by the two callers that
 * must agree on it but act differently: the run/usage hot path
 * (`getClaudeAccessToken`) refreshes when this is true; the health probe
 * (`probeClaudeStatus`) must NOT refresh and instead reports the non-fatal
 * `expired` state (RUSH-1822). A missing `expiresAt` is treated as "still
 * fresh" (never force a refresh on a token with no known expiry).
 */
export declare function claudeAccessTokenNeedsRefresh(expiresAt: number | null | undefined, nowMs?: number): boolean;
export declare function setClaudeUsageCachePathForTest(cachePath: string | null): string | null;
/** Discriminator for usage window types. */
export type UsageWindowKey = 'session' | 'week' | 'sonnet_week' | 'month';
/** A single rate-limit window with utilization percentage and reset time. */
export interface UsageWindow {
    key: UsageWindowKey;
    label: string;
    shortLabel: string;
    usedPercent: number;
    resetsAt: Date | null;
    windowMinutes: number | null;
}
/** A point-in-time collection of usage windows from a single source. */
export interface UsageSnapshot {
    source: 'live' | 'last_seen';
    sourceLabel: string;
    capturedAt: Date | null;
    windows: UsageWindow[];
    /**
     * Last-known windows the freshness gate DROPPED from `windows` — expired by
     * `resetsAt`/`windowMinutes`, or from a rolled-over billing period. VIEW-ONLY:
     * `agents view` renders these with a staleness age ("30% · 6h old") so the
     * user always sees the last number instead of a bare "unavailable". Routing
     * MUST NEVER read this field — `isUsageVerified`/`hasStaleUsage`/
     * `hasUsageAvailable`/`deriveUsageStatusFromSnapshot` consult only `windows`,
     * so a stale number rendered here can never make a stale account read as
     * verified or eligible (the RUSH-2858 property). Not a serialized key of its
     * own, and dropped from `--json` (which projects `windows` explicitly) — but
     * the READINGS it holds do round-trip through the on-disk cache:
     * `serializeClaudeUsageSnapshot` persists the union of `windows` and
     * `staleWindows`, and `deserializeClaudeUsageSnapshot` re-runs the freshness
     * gate on read to re-partition them (so a collector like Grok that pre-splits
     * an ended-period reading onto `staleWindows` still survives the round-trip).
     */
    staleWindows?: UsageWindow[];
    plan?: string | null;
    /** Action that makes an event-fed source emit a current reading. */
    refreshHint?: string | null;
    /**
     * A refusal observed from a real harness run, independent of API windows.
     * `session_limit` recovers on a clock (`resetsAt`). `out_of_credits` is a
     * tokens/balance exhaustion that does NOT reset on a clock — it has no
     * `resetsAt` and is cleared only by a later successful run on the account
     * (clearClaudeAccountRefusal). Both exclude the account from rotation while set.
     */
    unavailable?: {
        reason: 'session_limit' | 'out_of_credits';
        resetsAt?: Date;
    };
}
/** Usage data plus any error encountered while fetching. */
export interface UsageInfo {
    snapshot: UsageSnapshot | null;
    error: string | null;
    /** Benign local state, symbol-backed so `--json` keeps its existing shape. */
    [USAGE_BENIGN_STATE]?: UsageBenignState;
}
/** Construct the benign no-local-log result without overloading `error`. */
export declare function usageNoRecentUsageInfo(): UsageInfo;
/** Read a benign state for the human renderer; symbols are omitted by JSON serialization. */
export declare function getUsageBenignState(info: UsageInfo): UsageBenignState | null;
/** Input needed to identify an account for usage lookup. */
export interface UsageIdentityInput {
    agentId: AgentId;
    info: AccountInfo;
    home?: string;
    cliVersion?: string | null;
}
/** Options for fetching usage data. */
interface UsageOptions {
    home?: string;
    cliVersion?: string | null;
    organizationId?: string | null;
    /**
     * The account's usage key (`claude:org=…`, `kimi:user=…`, …) when the caller
     * knows which account this fetch is for. Scopes the 429 backoff to that
     * account (RUSH-3036) so one throttled account cannot park its siblings;
     * absent, the backoff stays provider-wide.
     */
    usageScope?: string | null;
    /**
     * Caller-supplied abort signal (the daemon tick's deadline). Combined with each
     * provider fetch's own timeout so a hung refresh is bounded by BOTH the
     * per-fetch timeout and the supervisor deadline (PHNX-3608).
     */
    signal?: AbortSignal;
    /**
     * When true, never open the ACL-bound OS keychain item (macOS Touch ID).
     * Daemon usage refresh sets this so a background tick cannot pop biometrics.
     * Credentials come from the no-ACL access-token cache, a file-based
     * setup-token, or `<home>/.claude/.credentials.json` only.
     */
    fileOnly?: boolean;
    /**
     * When true, a read that finds no file-based setup-token MAY fall through to
     * Claude Code's interactive OAuth login (the only credential carrying
     * `user:profile`, which `/api/oauth/usage` requires). OFF by default and set
     * ONLY by a foreground human `agents view` on a headed device (personal or
     * desktop; see USAGE-READ-2). Every background caller — daemon usage warm, auth-health
     * probe, watchdog — leaves it unset, preserving the RUSH-1822 guarantee that
     * an unattended loop never transmits the interactive login to Anthropic.
     */
    allowInteractiveLogin?: boolean;
}
/** Canonical input for a single usage fetch operation. */
export interface UsageFetchInput {
    agentId: AgentId;
    home?: string;
    cliVersion: string | null;
    organizationId: string | null;
}
/** Claude OAuth credentials stored in the macOS Keychain. */
interface ClaudeOauthCredentials {
    accessToken?: string | null;
    refreshToken?: string | null;
    expiresAt?: number | null;
    scopes?: string[] | null;
    subscriptionType?: string | null;
    rateLimitTier?: string | null;
    organizationUuid?: string | null;
}
/** Serialized usage window for the on-disk cache. */
interface CachedUsageWindow {
    key: UsageWindowKey;
    label: string;
    shortLabel: string;
    usedPercent: number;
    resetsAt: string | null;
    windowMinutes: number | null;
}
/** Serialized usage snapshot for the on-disk cache. */
export interface CachedUsageSnapshot {
    capturedAt: string | null;
    windows: CachedUsageWindow[];
    plan?: string | null;
    refreshHint?: string | null;
    unavailable?: {
        reason: 'session_limit' | 'out_of_credits';
        resetsAt?: string;
    };
}
/** The single registry of agent usage sources and their transport. */
declare const USAGE_SOURCES: {
    readonly claude: {
        readonly fetch: typeof getClaudeUsageInfo;
        readonly network: true;
    };
    readonly codex: {
        readonly fetch: typeof getCodexUsageInfo;
        readonly network: false;
    };
    readonly kimi: {
        readonly fetch: typeof getKimiUsageInfo;
        readonly network: true;
    };
    readonly droid: {
        readonly fetch: typeof getDroidUsageInfo;
        readonly network: true;
    };
    readonly grok: {
        readonly fetch: typeof getGrokUsageInfo;
        readonly network: false;
    };
    readonly cursor: {
        readonly fetch: typeof getCursorUsageInfo;
        readonly network: true;
    };
    readonly antigravity: {
        readonly fetch: typeof getAntigravityUsageInfo;
        readonly network: true;
    };
    readonly muse: {
        readonly fetch: typeof getMuseUsageInfo;
        readonly network: true;
    };
};
export declare const USAGE_SOURCE_AGENT_IDS: (keyof typeof USAGE_SOURCES)[];
/** Fetch usage info for a given agent through the canonical source registry. */
export declare function getUsageInfo(agentId: AgentId, options?: UsageOptions): Promise<UsageInfo>;
/** Derive a stable lookup key from account info for usage deduplication. */
export declare function getUsageLookupKey(info?: Pick<AccountInfo, 'usageKey' | 'accountKey'> | null): string | null;
/**
 * Deduplicate identity inputs into canonical (most-recently-active) accounts
 * and build the corresponding fetch inputs for each unique usage key.
 */
export declare function buildCanonicalUsageContext(inputs: UsageIdentityInput[]): {
    canonicalByUsageKey: Map<string, AccountInfo>;
    usageFetchInputs: Map<string, UsageFetchInput>;
};
/**
 * Whether an agent exposes usage/limit data we can render — Claude/Kimi/Droid/
 * Cursor/Antigravity via a live API, Codex/Grok via local session logs.
 * Everything else has no usage concept, so callers use this to decide whether
 * a missing snapshot is worth flagging as "usage unavailable" (a signed-in
 * Claude account with no data) versus simply not applicable (OpenCode).
 */
export declare function agentReportsUsage(agentId: AgentId): boolean;
/**
 * Whether an agent's usage source makes a live NETWORK call (Claude/Kimi/Droid/
 * Cursor/Antigravity) versus reading local session logs (Codex/Grok). Both
 * kinds publish through the shared cache; callers use this only to distinguish
 * provider I/O from local collection.
 */
export declare function agentUsesNetworkUsage(agentId: AgentId): boolean;
/**
 * Concurrent live usage fetches for a single `agents view` / rotation pass.
 * High enough to finish a multi-account refresh in one round-trip window; low
 * enough that a cold cache of 10+ accounts cannot open 10+ HTTP calls at once
 * (and cannot stack behind delayed responses until the process is pegged).
 */
export declare const USAGE_FETCH_CONCURRENCY = 3;
/**
 * Unified entry for every multi-account usage lookup (`agents view`, rotation,
 * JSON export). Deduplicates by usage identity and reads the shared snapshot.
 * Only an explicit `forceRefresh` call may collect provider or local-log state.
 */
export interface UsageLookupOptions {
    forceRefresh?: boolean;
    fileOnly?: boolean;
    /** Daemon tick deadline signal, combined with each provider fetch's own timeout (PHNX-3608). */
    signal?: AbortSignal;
    /**
     * Permit a foreground personal-device usage read to fall through to the
     * interactive login when no setup-token exists (USAGE-READ-2). Set ONLY by
     * `agents view` when `selfConfiguredDeviceRole() === 'personal'` and the
     * output is a human TTY (not `--json`). Threads into `getClaudeUsageInfo` →
     * `loadClaudeOauth`. Unset for every other lookup.
     */
    allowInteractiveLogin?: boolean;
}
export declare function getUsageInfoByIdentity(inputs: UsageIdentityInput[], opts?: UsageLookupOptions): Promise<{
    canonicalByUsageKey: Map<string, AccountInfo>;
    usageByKey: Map<string, UsageInfo>;
}>;
/**
 * Fetch usage for one identity. Ordinary callers always read the shared cache;
 * the daemon and explicit `--refresh` calls collect through one device lease.
 */
export declare function getUsageInfoForIdentity(input: UsageIdentityInput, opts?: UsageLookupOptions): Promise<UsageInfo>;
/**
 * Pick which usage windows to render in a compact one-line summary.
 *
 * Overview rows (`agents view` all agents) must stay narrow enough that one
 * multi-window agent (Antigravity's four model quotas, Droid's three buckets)
 * does not force every other row to pad to ~200 columns and wrap. Prefer the
 * canonical session + week windows when present; otherwise take the highest
 * utilization remaining. Returns the full set when `maxWindows` is unset.
 */
export declare function pickCompactUsageWindows(windows: UsageWindow[], maxWindows?: number): UsageWindow[];
/** Options for {@link formatUsageSummary}. */
export interface FormatUsageSummaryOpts {
    unavailable?: boolean;
    unverified?: boolean;
    /**
     * Setup-token lacks `user:profile` so usage cannot be read headlessly
     * (RUSH-2392). Distinct from generic `unverified` (cache unconfirmed) —
     * minting again will not help; the account still runs.
     */
    headless?: boolean;
    /**
     * Cap how many usage windows render on one line. Overview (`agents view`
     * with no agent filter) passes 2 so multi-window agents cannot blow out
     * column width; single-agent and detail views leave this unset.
     */
    maxWindows?: number;
    /** Windows that must keep a visible slot even when the provider omits one. */
    expectedWindows?: Array<{
        key: string;
        shortLabel: string;
    }>;
    /**
     * The classified cause of `usageInfo.error` (RUSH-3040), from
     * {@link classifyUsageErrorKind}. Lets the no-bars branch below name the
     * SPECIFIC reason ('re-auth for usage', 'sign in / provision a long-lived
     * token', 'rate-limited (retry ~12m)') instead of
     * the generic 'usage unavailable' that used to cover ~6 distinct causes.
     * Only consulted when `unavailable` is set — a snapshot WITH bars still
     * renders 'unverified'/`headless` as before. `--json` output is unaffected:
     * `UsageInfo.error` keeps carrying the full message; this only changes the
     * short human string rendered here.
     */
    errorKind?: UsageErrorKind | null;
    /**
     * The raw `UsageInfo.error` string, read only to pull the retry-time hint
     * out of a `rate-limited` classification (the exact duration lives in the
     * message text, not the kind).
     */
    errorDetail?: string | null;
    /** Benign state from {@link getUsageBenignState}; never sourced from `UsageInfo.error`. */
    benignState?: UsageBenignState | null;
    /** Provider-specific replacement for the generic no-local-event marker. */
    noRecentUsageLabel?: string | null;
}
/** Format a one-line usage summary with compact bars for inline display. */
export declare function formatUsageSummary(plan: string | null, snapshot: UsageSnapshot | null, planWidth?: number, opts?: FormatUsageSummaryOpts): string;
/**
 * Derive an account's real throttle state from its live usage windows — the
 * single signal both the `agents view` badge and run-rotation eligibility share
 * (`hasUsageAvailable` in rotate.ts treats a `rate_limited` verdict here as
 * ineligible). A window at 100% utilization means the account is throttled until
 * that window resets. Rotation *weighting* still ranks eligible accounts by
 * weekly headroom (`getRoutingUsedPercent`); this function is the yes/no gate.
 *
 * Returns `null` when there is no snapshot, so callers render no badge rather
 * than a misleading one. This deliberately never consults
 * `cachedExtraUsageDisabledReason`: that field describes why pay-as-you-go
 * overage is disabled (`out_of_credits` = no overage credits purchased,
 * `org_level_disabled` = an admin turned overage off), NOT whether the account
 * can do work right now. A Pro account at 5% weekly usage with overage disabled
 * is fully usable, yet that flag would mislabel it "out of credits".
 *
 * The model-specific `sonnet_week` sub-limit is excluded: hitting it throttles
 * one model, not the account, so it shouldn't flip the whole row to throttled.
 */
export declare function deriveUsageStatusFromSnapshot(snapshot: UsageSnapshot | null | undefined): 'available' | 'rate_limited' | null;
/** A prior sample of one window's utilization, for burn-rate projection. */
export interface UsagePriorSample {
    /** Epoch ms the prior snapshot was captured. */
    capturedAt: number;
    /** The session window's `usedPercent` in that prior snapshot. */
    usedPercent: number;
}
/**
 * An account's throttle state PLUS how long until it caps, projected from the
 * burn rate on its 5-hour `session` window — the window that throttles the next
 * request soonest. `deriveUsageStatusFromSnapshot` answers only "maxed right
 * now (100%)?"; this answers "and how close is it getting?", so routing can
 * deprioritize an account burning toward its cap before it actually hits it,
 * instead of treating 85%-and-climbing the same as 85%-and-idle.
 *
 * `minutesToLimit`:
 *   - `0`      — already rate-limited (a blocking window at 100%).
 *   - `n > 0`  — projected minutes until the session window reaches 100%, from
 *                `(100 - used) / burnRatePerMinute`, where the burn rate is
 *                measured between `prev` and this snapshot.
 *   - `null`   — unknown: no snapshot, no session window, no prior sample, or
 *                usage flat/falling since `prev` (a reset or an idle account is
 *                not "projected to cap", so it is NOT deprioritized).
 *
 * Pure: the daemon's refresher supplies `prev` from the last snapshot it stored
 * (`usage-refresh.ts`); the routing hot path reads the daemon-computed result
 * from the headroom cache rather than recomputing (it has no `prev`).
 */
export interface UsageHeadroom {
    status: 'available' | 'rate_limited' | null;
    minutesToLimit: number | null;
}
export declare function deriveUsageHeadroom(snapshot: UsageSnapshot | null | undefined, prev?: UsagePriorSample | null): UsageHeadroom;
/**
 * Compact colored badge for the account's overall usage status. Renders only
 * when the account is throttled — `available` and `null` return ''.
 *
 * - `out_of_credits` → red "out of credits" (terminal account, all buckets dry)
 * - `rate_limited`   → yellow "rate-limited" (transient throttling)
 *
 * The badge sits between the usage bars and `lastActive` in `agents view`, so
 * a glance at the row tells the user whether the version can do useful work.
 * The same signal is exposed as `usageStatus` in `agents view --json` for
 * programmatic consumers (e.g. the swarmify panel's "resume in healthy agent").
 *
 * The switch is exhaustive on purpose — adding a new `AccountInfo.usageStatus`
 * value without updating the cases here is a build error at `_exhaustive`,
 * which is exactly the bug class this PR is fixing.
 */
export declare function formatUsageStatusBadge(usageStatus: 'available' | 'rate_limited' | 'out_of_credits' | null | undefined): string;
/** Format a multi-line usage section for detailed agent views. */
export declare function formatUsageSection(usage: UsageInfo): string[];
/** Fetch Codex usage by scanning the most recent session files for rate-limit events. */
declare function getCodexUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
/**
 * The access token to use for a READ-ONLY Claude usage fetch, or null when the
 * stored token is within the refresh leeway.
 *
 * Returns null instead of refreshing on purpose. Claude's refresh token is
 * single-use and rotates server-side on every refresh; with one account signed
 * into several machines, refreshing here would stampede that one token and
 * silently invalidate every other holder — the RUSH-1822 failure, except in the
 * usage path (fired in the background by the SWR cache and by `agents run`'s
 * default "balanced" rotation on every unpinned run) rather than the health
 * probe. So a usage read must never rotate: a near-expiry token yields "no usage
 * right now" instead of a fleet-wide logout. Mirrors {@link probeClaudeStatus};
 * the single legitimate refresh belongs to the actual claude run, never a read.
 * Pure — unit-tested.
 */
export declare function claudeUsageAccessTokenNoRefresh(oauth: Pick<ClaudeOauthCredentials, 'accessToken' | 'expiresAt'>): string | null;
/** Fetch Claude usage via the Anthropic OAuth usage API. */
declare function getClaudeUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
/** Raw quota bucket from the Kimi /usages response (numbers arrive as strings). */
interface KimiUsageQuota {
    limit?: string | number | null;
    used?: string | number | null;
    remaining?: string | number | null;
    resetTime?: string | null;
}
/** Response shape from the Kimi Code /usages endpoint (subset we render). */
export interface KimiUsagesResponse {
    user?: {
        userId?: string | null;
        membership?: {
            level?: string | null;
        } | null;
    } | null;
    usage?: KimiUsageQuota | null;
    limits?: Array<{
        window?: {
            duration?: number | null;
            timeUnit?: string | null;
        } | null;
        detail?: KimiUsageQuota | null;
    } | null> | null;
    subType?: string | null;
}
/**
 * Fetch Kimi usage via the Kimi Code /usages API. Kimi's JWT has no email
 * claim, so the account row can't show an address — but /usages returns quota
 * windows and the membership tier, which is what we render.
 *
 * Deliberately NO token refresh: `agents view` is a read/inspect command and
 * must not rotate the user's Kimi OAuth credential (rewriting the file,
 * invalidating the old refresh token, racing a concurrently-running kimi CLI).
 * The kimi CLI refreshes on its own launch; if the stored token is expired we
 * skip the live fetch and let the SWR cache serve the last-seen snapshot.
 */
declare function getKimiUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
/** Normalize the Kimi /usages payload into the common UsageWindow shape. */
export declare function normalizeKimiWindows(data: KimiUsagesResponse): UsageWindow[];
/** Derive a display plan label from Kimi's membership tier or subscription type. */
export declare function formatKimiPlan(data: KimiUsagesResponse): string | null;
/** A single Droid token-rate-limit window from /api/billing/limits. */
interface DroidLimitWindow {
    usedPercent?: number | null;
    windowEnd?: string | null;
}
/** Response shape from Factory.ai's billing limits endpoint (subset we render). */
export interface DroidBillingLimitsResponse {
    usesTokenRateLimitsBilling?: boolean | null;
    limits?: {
        standard?: {
            fiveHour?: DroidLimitWindow | null;
            weekly?: DroidLimitWindow | null;
            monthly?: DroidLimitWindow | null;
        } | null;
    } | null;
}
/**
 * Fetch Droid usage via Factory.ai's billing limits API — the same endpoint the
 * droid CLI polls for its token-limit banner. The WorkOS access token comes
 * from the locally decrypted ~/.factory/auth.v2.file (the same credential
 * account identity in agents.ts reads).
 *
 * Deliberately NO token refresh, for a sharper reason than Kimi's: WorkOS
 * refresh tokens are single-use and rotate on every exchange, so refreshing
 * here would race a concurrently running droid session and can permanently
 * invalidate the user's login chain. Droid refreshes its own credential when
 * it runs; if the stored token is expired we skip the live fetch and let the
 * SWR cache serve the last-seen snapshot. This same single-use-rotation property
 * is why `agents apply` refuses to propagate droid credentials across machines
 * (see `isCredentialSafeToPropagate` in `../fleet/auth-sync.ts`).
 */
declare function getDroidUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
/**
 * Live auth probes — the same authenticated GET the usage fetchers above do,
 * but surfacing the raw HTTP status instead of swallowing 401/expired to null.
 * These back `agents fleet ping` and the fleet auth-health cache: completing a
 * real request is the only proof a token is accepted. The local "signed in"
 * flag cannot tell a revoked-but-unexpired token from a good one. Classification
 * of the returned status into a verdict lives in lib/auth-health.ts (kept there
 * so it stays pure/testable and to avoid an import cycle).
 */
export interface ProviderProbe {
    /** HTTP status of the probe request, or null when no request was made (missing/expired token) or the request threw. */
    status: number | null;
    /** Local credential state observed before the request. */
    token: 'present' | 'missing' | 'expired';
    /** Network/parse error message when status is null but a token was present. */
    error?: string;
    /**
     * Known non-revocation cause for a non-2xx status.
     * `usage_scope` — Anthropic returned 403 because the setup-token lacks
     * `user:profile` (RUSH-2392). Token is valid for inference; usage is unreadable.
     * Auth-health MUST NOT map this to `revoked`.
     */
    reason?: 'usage_scope';
}
/** Probe Claude's OAuth token against the usage endpoint. Never refreshes — reports `expired` for a near-expiry token; see the comment below (RUSH-1822). */
export declare function probeClaudeStatus(home?: string, cliVersion?: string | null, usageScope?: string | null, signal?: AbortSignal): Promise<ProviderProbe>;
/** Probe Kimi's OAuth token against the /usages endpoint. Never refreshes (single-use rotation — see getKimiUsageInfo). */
export declare function probeKimiStatus(home?: string, usageScope?: string | null, signal?: AbortSignal): Promise<ProviderProbe>;
/** Probe Droid's WorkOS token against the billing-limits endpoint. Never refreshes (single-use rotation — see getDroidUsageInfo). */
export declare function probeDroidStatus(home?: string, usageScope?: string | null, signal?: AbortSignal): Promise<ProviderProbe>;
/**
 * Normalize the Factory billing-limits payload into the common UsageWindow
 * shape. Orgs on the legacy (non token-rate-limit) billing model have no
 * meaningful windows, so they render nothing — mirrors droid's own gate on
 * `usesTokenRateLimitsBilling` before it reads `limits.standard`.
 */
export declare function normalizeDroidWindows(data: DroidBillingLimitsResponse): UsageWindow[];
/**
 * Load a version home's Claude OAuth credential from the two stores Claude Code
 * uses, tried in order:
 *
 *  1. The OS keychain (`getKeychainToken`). Canonical on macOS — Claude Code
 *     writes the token to the login keychain and we read it via `/usr/bin/security`.
 *  2. `<home>/.claude/.credentials.json`. On a headless Linux box (the
 *     `agents view --device <linux>` case) there is no reachable Secret Service, so
 *     the Claude CLI stores its OAuth token in this plaintext file instead. The
 *     keychain read above finds nothing on that platform, so we fall back to the
 *     file. Same wrapped `{ claudeAiOauth }` shape, so one parser handles both.
 *
 * Without step 2 the live usage fetch got no token on Linux, so `agents view`
 * (run remotely over SSH by `--device`) rendered no usage bars even though the
 * account + plan — read from the plaintext `.claude.json` — showed fine.
 *
 * `opts.accessTokenCache` marks a read-only, access-token-only consumer (the
 * usage fetch and the auth-health probe). Such a caller authenticates ONLY with
 * a file-based setup-token and, when none is provisioned, gets `null` — it never
 * reads Claude Code's interactive login (transmitting that ACL-bound OAuth token
 * to Anthropic's API is what gets it revoked; see the branch body and
 * docs/secrets.md). It is OFF by default so full-credential
 * callers that refresh (`isClaudeAuthValid` -> `getClaudeAccessToken`) still
 * read the interactive login. Rush Cloud dispatch does not call this helper
 * at all (SING-1b: the account manifest is email-only; RUSH-2359 removed the
 * leftover blob reader that used to send the interactive login).
 *
 * `opts.fileOnly` skips the ACL keychain read entirely — setup-token and
 * `.credentials.json` only. Used by the daemon usage refresher so a background
 * tick can never pop Touch ID.
 */
export declare function loadClaudeOauth(home?: string, opts?: {
    accessTokenCache?: boolean;
    fileOnly?: boolean;
    allowInteractiveLogin?: boolean;
}): Promise<ClaudeOauthCredentials | null>;
/**
 * Save Claude OAuth credentials to the system keychain/keyring.
 * Reads the existing payload, merges the new OAuth fields, and writes back.
 * Exported for regression tests; not part of the public command surface.
 */
export declare function saveClaudeOauth(home: string | undefined, credentials: ClaudeOauthCredentials): Promise<boolean>;
/**
 * Derive the Keychain service name for a Claude home directory.
 * Managed (non-default) homes get a hash suffix for isolation.
 */
export declare function getClaudeKeychainService(home?: string): string;
/**
 * Check whether a requested org ID matches the live OAuth org ID.
 * Returns true when either is absent (no filtering) or when they match.
 */
export declare function isClaudeUsageOrgMatch(requestedOrgId: string | null | undefined, liveOrgId: string | null | undefined): boolean;
/** Read a cached usage snapshot for a given usage key. Returns null if absent or stale. */
export declare function readClaudeUsageCache(usageKey: string, cachePath?: string, now?: Date): UsageSnapshot | null;
/** Delete an expired cache row only if it is still expired under the write lock. */
export declare function pruneExpiredClaudeUsageCacheEntry(usageKey: string, cachePath?: string, now?: Date): void;
/** Write a usage snapshot to the on-disk cache. */
export declare function writeClaudeUsageCache(usageKey: string, snapshot: UsageSnapshot, cachePath?: string): void;
/** Atomically merge partial native Claude windows into the current fresh row. */
export declare function mergeClaudeUsageCacheWindows(usageKey: string, snapshot: UsageSnapshot, cachePath?: string): void;
/**
 * Export the local usage cache rows worth publishing to fleet peers (PHNX-3392
 * usage-sync). Returns the raw serialized rows keyed by usage identity, filtered
 * to those carrying at least one window — an empty row has nothing to teach a
 * worker. The transport is the on-disk cache form, so there is no Date round-trip.
 */
export declare function exportClaudeUsageCacheRows(cachePath?: string): Record<string, CachedUsageSnapshot>;
/**
 * Merge usage rows received from a fleet peer into the local cache, NEWEST-WINS
 * per identity by `capturedAt` (PHNX-3392 usage-sync). A worker has no local
 * usage writer, so an incoming row is almost always the freshest it will get; the
 * timestamp guard exists so a stale push from one headed peer can never overwrite
 * a fresher row another peer (or, on a headed receiver, the local status-line)
 * already wrote. An incoming row with no `capturedAt` cannot prove it is newer, so
 * it never displaces an existing timestamped row. Returns the count updated.
 * Locked + atomic like every other cache writer.
 *
 * Deliberately NOT role-gated on the receiver. "Consume only on worker/unmarked"
 * is a SENDER-side optimization (don't waste a push on a headed peer that reads
 * its own usage), not a safety invariant — the actual safety property is this
 * newest-wins guard. Receiving on a headed box is harmless (its fresher local
 * status-line row survives) or helpful (an account it is signed into but never
 * runs now shows a usage bar), so gating here on the receiver's own — laggier —
 * view of its role would only reject legitimate data.
 */
export declare function ingestPeerClaudeUsageRows(rows: Record<string, CachedUsageSnapshot>, cachePath?: string): number;
/**
 * Persist a Claude tokens/credits exhaustion (`out of usage credits` / `monthly
 * spend limit`) from a real run. Unlike a rate/session limit this does NOT reset
 * on a clock, so no reset time is stored — rotation excludes the account until a
 * later successful run clears it via {@link clearClaudeAccountRefusal}.
 */
export declare function noteClaudeOutOfCredits(usageKey: string, cachePath?: string): void;
/**
 * Clear any persisted refusal marker for an account after a run SUCCEEDS on it.
 * This is the recovery path for `out_of_credits` (which has no clock) and also
 * proactively clears a stale `session_limit` the moment the account serves again.
 */
export declare function clearClaudeAccountRefusal(usageKey: string, cachePath?: string): void;
/**
 * Persist a Claude session-limit refusal from a real run until its stated reset.
 * This quota is not part of Anthropic's five-hour/weekly usage response.
 */
export declare function noteClaudeSessionLimit(usageKey: string, resetsAt: Date, cachePath?: string): void;
/** Parse Claude's `hit your session limit · resets …` refusal. */
export declare function parseClaudeSessionLimitReset(text: string, nowMs?: number): Date | null;
/**
 * Check whether the Claude OAuth credentials for a given home are usable.
 * Attempts a token refresh if the access token is expired.
 * Returns true only when a valid access token can be obtained.
 */
export declare function isClaudeAuthValid(home?: string): Promise<boolean>;
/** Render a proportional colored progress bar at one-eighth-cell resolution. */
export declare function renderBar(usedPercent: number, length: number): string;
/** Return a chalk color function based on the usage percentage threshold. */
export declare function getUsageColor(usedPercent: number): (text: string) => string;
/** Parse the latest billing info from Grok's unified log. */
declare function getGrokUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
/**
 * Muse Code usage.
 *
 * Prefer live Meta Model API rate-limit headers when a key is available
 * (META_API_KEY / MODEL_API_KEY / ~/.config/muse/auth.json). Fall back to
 * aggregating `model_completed.usage` from local session.jsonl logs under
 * ~/.local/share/muse/sessions for a last-7-days token window.
 */
declare function getMuseUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
/** Per-model bucket in Cursor's /api/usage response. */
interface CursorUsageModel {
    numRequests?: number | null;
    maxRequestUsage?: number | null;
}
/** Response shape from Cursor's dashboard usage endpoint. */
export interface CursorUsageResponse {
    /** The premium ("fast request") bucket the plan meters. */
    'gpt-4'?: CursorUsageModel | null;
    /** ISO timestamp the monthly request window resets from. */
    startOfMonth?: string | null;
    [model: string]: CursorUsageModel | string | null | undefined;
}
/**
 * Normalize Cursor's /api/usage payload into the common UsageWindow shape.
 *
 * Only free / legacy request-capped plans carry a `maxRequestUsage` on the
 * premium ("gpt-4") bucket — that's the fast-request cap the plan meters, and it
 * maps cleanly to a monthly window. Usage-based plans report `maxRequestUsage:
 * null` (no request cap — spend is metered in dollars instead), so they have no
 * bar to draw here and return no windows rather than a misleading empty gauge.
 */
export declare function normalizeCursorUsage(data: CursorUsageResponse): UsageWindow[];
/** Per-window plan usage percentages Cursor's dashboard breaks usage into (Auto+Composer / API / Total). */
interface CursorPlanUsage {
    autoPercentUsed?: number | null;
    apiPercentUsed?: number | null;
    totalPercentUsed?: number | null;
}
/** Response shape from Cursor's dashboard current-period-usage endpoint (subset we render). */
export interface CursorPeriodUsageResponse {
    planUsage?: CursorPlanUsage | null;
    /** ISO timestamp, or a unix-ms string, marking the end of the current billing cycle. */
    billingCycleEnd?: string | number | null;
}
/** Response shape from Cursor's usage-summary endpoint (subset we render). */
export interface CursorUsageSummaryResponse {
    /** True on a plan with no consumption cap; only tiered self-serve plans populate the percent fields. */
    isUnlimited?: boolean | null;
    individualUsage?: {
        plan?: CursorPlanUsage | null;
    } | null;
    billingCycleEnd?: string | number | null;
}
/**
 * Normalize Cursor's dashboard `get-current-period-usage` payload — the
 * primary usage source, giving the same Auto+Composer / API / Total breakdown
 * the web dashboard shows.
 */
export declare function normalizeCursorPeriodUsage(data: CursorPeriodUsageResponse): UsageWindow[];
/**
 * Normalize Cursor's `usage-summary` fallback payload — the same Auto/API/Total
 * breakdown nested under `individualUsage.plan`, used when the primary
 * dashboard endpoint returns no usable `planUsage` (seen on some
 * enterprise/team accounts). An unlimited plan (`isUnlimited: true`) with no
 * usable percent has nothing to draw and returns no windows, rather than a
 * misleading empty gauge.
 */
export declare function normalizeCursorUsageSummary(data: CursorUsageSummaryResponse): UsageWindow[];
/**
 * Fetch Cursor usage. Cursor authenticates every one of these requests with a
 * `WorkosCursorSessionToken` cookie of the form `<oauth-subject>::<access-token>`
 * (the same pair the web dashboard sends), not a bearer header, so all three
 * sources below share one resolved cookie.
 *
 * Three sources, tried in order, because no single endpoint carries usable data
 * for every plan shape:
 *
 *  1. `get-current-period-usage` — the primary source, and the richest: the
 *     Auto+Composer / API / Total percent breakdown the dashboard itself shows.
 *  2. `usage-summary` — some enterprise/team accounts return no usable
 *     `planUsage` from (1); this nests the same three percentages under
 *     `individualUsage.plan` instead.
 *  3. The legacy `/api/usage` request-cap endpoint — the original source,
 *     kept as the final fallback for free/legacy plans that predate the
 *     percent-based breakdown above and only ever exposed a monthly request cap.
 *
 * The first source to yield a non-empty window list wins; a source that errors
 * or returns no usable numbers falls through to the next rather than surfacing
 * an error — only the last resort's own response/error is surfaced when every
 * source comes up empty, so a plan enrolled in exactly one billing model still
 * renders instead of reporting three swallowed failures.
 */
declare function getCursorUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
/** The OAuth token `agy` stores (inside `{ token: … }`) in the OS keyring or file. */
interface AntigravityOauthToken {
    access_token?: string | null;
    refresh_token?: string | null;
    /** RFC3339 expiry timestamp for the access token. */
    expiry?: string | null;
}
/** One per-model quota bucket from the :retrieveUserQuota response. */
export interface AntigravityQuotaBucket {
    modelId?: string | null;
    tokenType?: string | null;
    remainingFraction?: number | null;
    resetTime?: string | null;
}
/** Response shape from the Code Assist :retrieveUserQuota endpoint. */
export interface AntigravityQuotaResponse {
    buckets?: AntigravityQuotaBucket[] | null;
}
/**
 * Parse a stored `agy` OAuth payload into its token. Handles both on-disk
 * shapes: the raw `{ token: {…} }` JSON (Linux file fallback) and the
 * `go-keyring-base64:<base64>` wrapper zalando/go-keyring writes into the
 * macOS Keychain item (service `gemini`, account `antigravity`). Never throws
 * (malformed input => null).
 */
export declare function parseAntigravityOauthPayload(raw: string): AntigravityOauthToken | null;
/**
 * True when the stored access token is expired (or inside the refresh leeway).
 * A missing/unparseable expiry is treated as still-fresh — the quota call
 * below is the source of truth if the token is actually dead (401 => render
 * nothing), and we never want to force a refresh without evidence.
 */
export declare function antigravityTokenNeedsRefresh(expiry: string | null | undefined, nowMs?: number): boolean;
/** Compact model tag for the inline bar — 'gemini-2.5-flash-lite' => '2.5FL'. */
export declare function antigravityModelShortLabel(modelId: string): string;
/**
 * Normalize the per-model quota buckets into the common UsageWindow shape —
 * one window per model (`gemini-3.1-pro`, `gemini-2.5-flash`, …), keyed
 * `session` since each bucket is a short-cycle quota with its own reset time.
 * Duplicate buckets for one model keep the LOWEST remaining fraction (the
 * most conservative read). Sorted most-used first so the bar closest to
 * throttling leads the row. `windowMinutes` stays null: the API reports only
 * the reset timestamp, not the window length, and an inferred 5h session
 * length would wrongly zero the SWR cache between resets.
 */
export declare function normalizeAntigravityWindows(buckets: AntigravityQuotaBucket[]): UsageWindow[];
/**
 * Fetch Antigravity usage via Google Code Assist's :retrieveUserQuota — the
 * quota API `agy` itself talks to (its log shows the sibling :loadCodeAssist
 * and :fetchAvailableModels calls on the same host). Auth is the stored `agy`
 * OAuth token (OS keyring on macOS, file fallback on Linux), refreshed
 * in-memory when expired — safe because Google's refresh tokens are
 * non-rotating (see refreshAntigravityAccessToken).
 */
declare function getAntigravityUsageInfo(options?: UsageOptions): Promise<UsageInfo>;
export {};
