/**
 * Disk TTL cache for the Linear answers behind the `agents projects` card.
 *
 * Linear meters two budgets independently, and only one of them binds. Observed
 * on this account's response headers:
 *
 *   x-ratelimit-requests-limit:   2500      remaining: 2
 *   x-ratelimit-complexity-limit: 3000000   remaining: 2999987
 *
 * Requests are scarce; complexity is 99.999% untouched. So the thing to
 * optimize is the NUMBER of calls, not their cost — and the way to spend 2500
 * of them is an agent (or a watch loop) running `projects status` repeatedly.
 * A human typing it is not the exhauster.
 *
 * The CLI is a short-lived process, so an in-memory memo would only help within
 * one invocation, which is the case that never needed help. This caches to disk.
 *
 * **One file per key, written by atomic rename.** A single JSON document holding
 * every entry has to be read, modified, and written back, and that sequence is
 * not atomic across processes — measured on this machine, two concurrent writers
 * of 40 distinct keys each left **8 of 80** surviving. This box routinely runs a
 * dozen agent sessions, so that is the normal case, not a corner. Per-key files
 * remove the shared mutable document entirely: two processes caching different
 * projects never touch the same path, and two caching the SAME project race only
 * to write identical data. `writeFileSync` to a temp path followed by `rename`
 * makes each file appear whole or not at all, so a reader never sees a partial
 * write.
 *
 * The load-bearing behavior is what happens on FAILURE: a stale entry keeps
 * being served, marked stale, instead of the line vanishing. That rule is
 * borrowed from `mergeAuthHealthEntries` — one 8s timeout must not flip a
 * populated chip to empty — and it is the fix for the card silently losing its
 * Linear line mid-session when the request budget ran out.
 */
/** Matches `SKILL_INDEX_TTL_MS` (`lib/registry.ts`) — the repo's TTL convention. */
export declare const LINEAR_CACHE_TTL_MS: number;
/**
 * Resolve the Linear API key from the `LINEAR_API_KEY` env var, falling back to
 * the macOS keychain generic-password `linear-api-key` (how the rest of the
 * stack stores it). Null if none is resolvable. The shared resolver for every
 * Linear-touching surface (project counts, session discovery).
 */
export declare function resolveLinearApiKey(): string | null;
/** What a lookup found, and how much to trust it. */
export interface CacheHit<T> {
    value: T;
    /** Age in ms. Past the TTL the value is still returned, flagged stale. */
    ageMs: number;
    stale: boolean;
}
/** Look up a project's cached answer. Returns stale entries too — the caller decides. */
export declare function readCached<T>(projectId: string, nowMs: number): CacheHit<T> | undefined;
/** Store a freshly fetched answer. */
export declare function writeCached<T>(projectId: string, value: T, nowMs: number): void;
/** Drop one project's entry — used when `projects link` re-points a definition. */
export declare function invalidateCached(projectId: string): void;
/** True when a prior 429 said the budget is exhausted and has not yet reset. */
export declare function isRateLimited(nowMs: number): boolean;
/**
 * Read a 429's `x-ratelimit-requests-reset` header into an epoch-ms instant.
 * Linear sends epoch milliseconds; anything absent, non-numeric, or already in
 * the past is not usable and the caller backs off a TTL instead. Pure, so the
 * parsing is testable without a live 429.
 */
export declare function parseRateLimitReset(header: string | null, nowMs: number): number | undefined;
/**
 * Record a 429 so the next runs don't spend a request learning the same thing.
 * `resetAtMs` comes from {@link parseRateLimitReset}; without it, back off one TTL.
 */
export declare function noteRateLimited(resetAtMs: number | undefined, nowMs: number): void;
