import { type AccountInfo } from './agents.js';
import type { AgentId } from './types.js';
import { type ProviderProbe } from './accounting/usage.js';
/**
 * - `live`        — completed an authenticated request (200).
 * - `revoked`     — the server rejected the token (401/403), except Claude
 *                   setup-token `user:profile` scope denials (those are
 *                   `unverified` via `reason: 'usage_scope'` — RUSH-2392).
 * - `expired`     — locally-detected expiry; not network-verified (no refresh on the read path).
 * - `rate_limited`— token works but is throttled right now (429).
 * - `unverified`  — credential present, not locally expired, but this agent has
 *                   no in-repo probe endpoint (codex/grok), OR the probe
 *                   endpoint cannot prove live for a known non-revocation
 *                   reason (Claude setup-token usage-scope gap — RUSH-2392).
 * - `unconfigured`— no usable credential on disk.
 * - `error`       — network/other failure; verdict indeterminate (keep the last known one).
 */
export type AuthVerdict = 'live' | 'revoked' | 'expired' | 'rate_limited' | 'unverified' | 'unconfigured' | 'error';
export interface AuthHealth {
    verdict: AuthVerdict;
    /** epoch ms of the probe. */
    checkedAt: number;
    /** optional short human detail (e.g. "HTTP 401", a network error). */
    detail?: string;
    /** account label for display (email / id), when known. Never part of the key. */
    account?: string;
}
/** Maximum age of an auth verdict used for automatic routing decisions. */
export declare const AUTH_PROBE_MAX_AGE_MS: number;
/** Agents with a live network probe wired up today. The rest are best-effort. */
export declare const LIVE_PROBE_AGENTS: ReadonlySet<AgentId>;
/** Map an HTTP status from a live probe to a verdict. */
export declare function classifyHttpStatus(status: number): AuthVerdict;
/** Turn a raw provider probe (from usage.ts) into a verdict. */
export declare function verdictFromProbe(probe: ProviderProbe): AuthVerdict;
/** A short human detail line for a probe result (rendered under --verbose). */
export declare function probeDetail(probe: ProviderProbe): string | undefined;
/** Uncolored glyph for a verdict (color is applied by the caller). */
export declare function verdictGlyph(verdict: AuthVerdict): string;
/** One-word label for matrices/verbose output. */
export declare function verdictLabel(verdict: AuthVerdict): string;
/** Roll a set of verdicts (one host×agent's installs) into counts for a matrix cell. */
export interface VerdictSummary {
    live: number;
    /**
     * unverified — signed in, but this agent has no in-repo live-probe endpoint
     * (codex/grok). A benign, neutral state: the account is present and usable, we
     * just can't complete a 2xx to prove it. It must NOT be lumped with the soft
     * `warn` bucket, or a fully-logged-in codex/grok fleet reads as half-degraded
     * (the exact "cry wolf" the ping matrix used to produce).
     */
    present: number;
    /** revoked — the server rejected the token (401/403). Genuinely needs re-login. */
    bad: number;
    /**
     * expired / rate_limited / error — degraded or unknown, but NOT "re-login now".
     * `expired` is soft for kimi/droid (their CLIs refresh the token on next launch;
     * we don't refresh on the read path), so it must not be lumped with revoked or
     * we'd cry wolf on a self-healing token.
     */
    warn: number;
    total: number;
}
export declare function summarizeVerdicts(verdicts: AuthVerdict[]): VerdictSummary;
/** A resolved display color; the caller maps it to chalk. Pure, so it's unit-tested. */
export type AuthCellColor = 'green' | 'yellow' | 'red' | 'gray' | 'dim';
/**
 * Color for a single verdict in the per-account (`--verbose`) breakdown. This is
 * the one source of truth shared with {@link authCellColor} so the matrix and the
 * account list can never drift — they did: the matrix painted `expired` yellow
 * while the verbose list painted it *red* (lumped with revoked), directly against
 * the {@link VerdictSummary} contract. Red is reserved for `revoked` (the only
 * "re-login now"); `unverified` is a neutral signed-in state (gray);
 * `expired`/`rate_limited`/`error` are soft (yellow).
 */
export declare function verdictColor(verdict: AuthVerdict): AuthCellColor;
/**
 * Color for a matrix cell that rolls up several accounts. Red only when a token
 * was genuinely rejected (`revoked`); yellow for soft/expired; green when at least
 * one account is live-verified and none are soft/revoked; gray when accounts are
 * present but unverifiable (codex/grok) — never the alarming yellow the old
 * renderer used, which made a fully-logged-in fleet read as half-broken.
 */
export declare function authCellColor(summary: VerdictSummary): AuthCellColor;
/** Verdicts that mean "this token was rejected by the server — re-login required". */
export declare function isDeadVerdict(verdict: AuthVerdict): boolean;
/**
 * A host's rolled-up auth state for the `fleet status` Auth column.
 *
 * The four display buckets are deliberately finer-grained than
 * {@link VerdictSummary}'s live/bad/warn: they separate "present but this agent
 * has no live probe" (`unverified`) and "soft, self-healing expiry"
 * (`expired`/`rate_limited`) from a genuine server rejection (`revoked`). The
 * old three-bucket rollup lumped all of those into `warn` and the column painted
 * them one alarming yellow — so a fleet of perfectly logged-in accounts on
 * codex/grok/etc (which can NEVER be probed live) read as half-degraded. These
 * buckets let the renderer show `unverified` as neutral and reserve red for the
 * only verdict that actually means "re-login now" ({@link isDeadVerdict}).
 */
export interface HostAuthSummary {
    /** Live-verified accounts (a real 2xx). */
    live: number;
    /** Signed in but this agent has no live-probe endpoint — benign, neutral. */
    present: number;
    /** Soft/degraded: expired (self-healing) / rate_limited / error. Mild warning. */
    degraded: number;
    /** Server rejected the token — genuinely needs re-login. */
    revoked: number;
    /** Total cached rows for this host (0 → the renderer shows "—"). */
    total: number;
    /** Oldest `checkedAt` (epoch ms) among this host's cached rows, or null when none. */
    oldestCheckedAt: number | null;
}
/**
 * Roll every cached (agent, version) row for one host into a {@link HostAuthSummary}
 * plus the age of its stalest entry. Pure — reads the map the caller already
 * loaded via {@link readAuthHealthCache}, so `fleet status` renders the Auth
 * column without any network probe. A host with no cached rows yields an empty
 * summary (total 0), which the renderer shows as "—".
 *
 * Keys are `host:agent:version` ({@link authCacheKey}); we match on the `host:`
 * prefix so agent/version segments can never be mistaken for a host.
 */
export declare function summarizeHostAuth(cache: Record<string, AuthHealth>, host: string): HostAuthSummary;
/** Human "3m ago" style age for a checkedAt timestamp. */
export declare function formatCheckedAge(checkedAt: number, now?: number): string;
/**
 * Human account label for display (email, else id). NOT used in the cache key —
 * two installs on one host can hold the same account with independently valid
 * tokens, so the key is keyed by version (below), not account.
 */
export declare function authAccountLabel(info: Pick<AccountInfo, 'email' | 'accountId' | 'userId'> | null | undefined): string | undefined;
/** Cache key: one entry per install — (host, agent, version). Unique per token. */
export declare function authCacheKey(host: string, agent: AgentId | string, version: string): string;
/** Read the whole cache (best-effort; a corrupt/missing file yields an empty map). */
export declare function readAuthHealthCache(): Record<string, AuthHealth>;
/** Read one entry, or null. */
export declare function readAuthHealth(host: string, agent: AgentId | string, version: string): AuthHealth | null;
/** Reconstruct one host's published probe rows for a lease waiter/CLI reader. */
export declare function readFleetAuthRows(host: string): AuthProbeRow[];
/**
 * Merge entries into the cache. An incoming `error` verdict (a network blip,
 * not a server rejection) is indeterminate, so it must NOT clobber a prior
 * known verdict — otherwise one 8s timeout flips a `live` chip to `error`,
 * exactly the "cry wolf" the verdict model avoids for `expired`. This is the
 * behaviour promised by the `error` doc on AuthVerdict ("keep the last known
 * one"). Pure, so it's unit-tested directly. */
export declare function mergeAuthHealthEntries(current: Record<string, AuthHealth>, incoming: Record<string, AuthHealth>): Record<string, AuthHealth>;
/** Merge one or more entries into the cache (best-effort write). */
export declare function writeAuthHealthEntries(entries: Record<string, AuthHealth>): void;
/**
 * Complete a live auth probe for one (agent, home). For claude/kimi/droid this
 * hits the provider — unless the account's usage cache already holds a fresh
 * successful fetch, which is the same authenticated request and proves the
 * token live without spending a second one (RUSH-3036). For everyone else it
 * reports a best-effort local verdict (`unverified` when a credential is
 * present, `unconfigured` otherwise) — never masquerading as `live`.
 */
export declare function probeAuthHealth(agent: AgentId, home: string | undefined, opts?: {
    cliVersion?: string | null;
    info?: AccountInfo | null;
    /**
     * Skip the derived-from-usage shortcut and fire a real network probe
     * (RUSH-3036). Set by `agents devices ping [--strict]`, whose contract is
     * a genuinely live request that surfaces `revoked` immediately.
     */
    forceLive?: boolean;
    /** Daemon tick deadline signal, combined with each probe fetch's own timeout (PHNX-3608). */
    signal?: AbortSignal;
}): Promise<AuthHealth>;
/** One probed install on a host. */
export interface AuthProbeRow {
    agent: AgentId;
    version: string;
    account?: string;
    health: AuthHealth;
}
/** One installed (agent, version) home, tagged with its resolved account label. */
export interface FleetAuthInstall {
    agent: AgentId;
    version: string;
    /** Human account label from {@link authAccountLabel}, or undefined when none resolves. */
    account: string | undefined;
}
/**
 * A set of installs that share one provider account and MUST be probed once.
 * The live probe runs against `probe` (the representative home); every entry in
 * `members` — the representative included — then receives that one verdict.
 */
export interface FleetAuthProbeGroup<T extends FleetAuthInstall> {
    probe: T;
    members: T[];
}
/**
 * Collapse installs so a live auth probe fires ONCE per (agent, account) rather
 * than once per version home.
 *
 * Several version homes signed into the same provider account share one OAuth
 * rate limit, so probing each of them concurrently — which the daemon did every
 * three minutes across every installed home (RUSH-2111) — raced that limit into
 * a 429 storm that then parked the whole box behind a `Retry-After` penalty (the
 * incident {@link file:./usage-backoff.ts} was written to survive; this removes
 * its cause). Grouping by account means N homes on one account issue ONE request.
 *
 * `isMergeable` scopes the dedup to the installs it actually helps: only agents
 * that make a network probe ({@link LIVE_PROBE_AGENTS}) can 429, so only they are
 * merged. A best-effort agent (its verdict is a cheap local file read, no rate
 * limit) is left per-install — collapsing it would gain nothing and could
 * silently override one home's local `signedIn` verdict with another's. Installs
 * with no resolvable account label, or that `isMergeable` rejects, are never
 * merged: each becomes its own group keyed by version, preserving the old
 * per-install probe (also the `unconfigured` case dropped downstream). Pure: no
 * fs, no network, so the dedup decision is unit-tested directly.
 */
export declare function groupFleetAuthInstalls<T extends FleetAuthInstall>(installs: readonly T[], isMergeable?: (install: T) => boolean): FleetAuthProbeGroup<T>[];
/**
 * Enumerate every installed (agent, version) on THIS host and return one row per
 * install (installs with no credential at all are dropped). Shared by
 * `agents fleet ping --local` and the daemon refresh.
 *
 * The live network probe is deduped by account: homes sharing one provider
 * account are probed ONCE and the verdict is fanned out to each home's row, so
 * the daemon's every-3-minute refresh can no longer fire concurrent same-account
 * requests and self-inflict a 429 (RUSH-2111). Each home still gets its own cache
 * row (the cache key is per-version by design — see {@link authCacheKey}).
 */
export declare function probeLocalFleetAuth(opts?: {
    cliVersion?: string | null;
    agents?: readonly AgentId[];
    /** Fire real network probes even when fresh usage evidence exists (RUSH-3036) — the `devices ping [--strict]` contract. */
    forceLive?: boolean;
    /**
     * Deadline signal from the daemon's supervised auth tick (PHNX-3608). Aborts
     * the probe's in-flight network work when the tick deadline elapses so it
     * unwinds instead of leaking a runaway await; on-demand CLI callers omit it.
     */
    signal?: AbortSignal;
}): Promise<AuthProbeRow[]>;
/** Persist a host's probed rows into the cache (keyed by host+agent+version). */
export declare function writeFleetAuthRows(host: string, rows: AuthProbeRow[]): void;
