/**
 * SubscriptionPool — multi-account subscription registry (P1.1).
 *
 * Part of the Subscription & Auth Standard
 * (docs/specs/_drafts/subscription-auth-standard-master-spec.md).
 *
 * The source of truth for "which subscriptions does this operator have, per
 * provider." Each account is a first-class registry entry keyed to its login
 * LOCATION — its per-account config home (e.g. CLAUDE_CONFIG_DIR) — NOT its
 * tokens. This is the load-bearing invariant behind decision 1A
 * ("re-enroll per machine"): because the registry only ever stores the
 * config-home path, a leaked registry file never leaks a credential.
 *
 * Cross-machine account follow-me (WS5.2, docs/specs/ws52-account-follow-me-security.md):
 * the DEFAULT is RE-MINT PER MACHINE (Mechanism B) — each machine drives its OWN
 * operator-approved login and holds its own grant; an OAuth config-home NEVER crosses
 * machines. Only a NON-credential, redacted metadata projection replicates (the
 * `subscription-account-meta` JournalKind — id/nickname/email/provider/framework/status/
 * quota; configHome STRIPPED). Shipping each account's credential blob over E2E secret-sync
 * is Mechanism A — a SEPARATE, per-provider-allowlist, default-OFF path that is REFUSED for
 * Anthropic (its ToS prohibits relocating Claude OAuth tokens). NOT the default; do not
 * conflate the two.
 *
 * Why never tokens: Anthropic prohibits Claude OAuth tokens in non-Claude-Code
 * tools and enforces it. The pool drives each account through its real
 * framework client pointed at that account's config home; instar never extracts
 * a token. Storing only the location keeps that invariant structural.
 *
 * File-backed JSON at `<stateDir>/subscription-pool.json`, atomic tmp+rename
 * writes, optimistic CAS via a per-record `version` field. Mirrors the
 * CommitmentTracker durable-registry pattern (a simpler single-writer form).
 *
 * Ships DARK: nothing instantiates a pool with accounts unless the operator
 * enrolls one. A pool of zero accounts is a no-op — single-account agents are
 * entirely unaffected.
 */
import type { ComponentHealth } from './types.js';
import type { SubscriptionAccountMetaReplicationEmitter } from './SubscriptionAccountMetaReplicatedStore.js';
/** Provider behind a subscription (the account's billing identity). */
export type SubscriptionProvider = 'anthropic' | 'openai' | 'github-copilot' | 'google';
/** Framework client that drives the account. */
export type SubscriptionFramework = 'claude-code' | 'codex-cli' | 'gemini-cli' | 'pi-cli';
/**
 * Account lifecycle status.
 *   active        — usable, login fresh
 *   warming       — being kept warm / refreshing (headless-refresh guard)
 *   rate-limited  — currently at/over a quota window
 *   needs-reauth  — login genuinely failed (refresh token revoked / pw change)
 *   disabled      — operator-disabled; scheduler never selects it
 */
export type SubscriptionAccountStatus = 'active' | 'warming' | 'rate-limited' | 'needs-reauth' | 'disabled';
/**
 * Live per-account quota reading (decision C: hybrid read). Populated by the
 * QuotaPoller in P1.2 — in P1.1 it is just carried metadata (optional).
 */
export interface AccountQuotaSnapshot {
    fiveHour?: {
        utilizationPct: number;
        resetsAt: string;
    };
    sevenDay?: {
        utilizationPct: number;
        resetsAt: string;
    };
    perModel?: Record<string, number | null>;
    extraUsage?: {
        isEnabled: boolean;
        usedCredits: number;
        monthlyLimit: number;
    };
    /** Which read path produced this snapshot (decision C provenance). */
    source?: 'claude-code-usage-screen' | 'oauth-usage-endpoint-fallback';
    measuredAt?: string;
}
export interface SubscriptionAccount {
    /** Stable id, charset-clamped to ^[a-z0-9-]+$. */
    id: string;
    /** Operator-facing handle (editable), like a machine nickname. */
    nickname: string;
    /** Account email — the disambiguator across same-org accounts (e.g.
     *  "SageMind - Justin" vs "SageMind - Adriana"). Auto-populated from the
     *  account's own login (oauthAccount.emailAddress) on poll, so the stored email
     *  always reflects which account actually authenticated. NEVER a secret. */
    email?: string;
    /** Billing provider. */
    provider: SubscriptionProvider;
    /** Framework client that drives this account. */
    framework: SubscriptionFramework;
    /**
     * The login LOCATION — the per-account config home (CLAUDE_CONFIG_DIR for
     * claude-code). NEVER tokens. This is the swap mechanism: select an account =
     * spawn the framework pointed at this configHome.
     */
    configHome: string;
    /** Lifecycle status. */
    status: SubscriptionAccountStatus;
    /** Last known quota reading (P1.2 populates; optional in P1.1). */
    lastQuota?: AccountQuotaSnapshot | null;
    /** ISO timestamp the account was enrolled. */
    enrolledAt: string;
    /** ISO timestamp the account was last selected for a session. */
    lastUsedAt?: string;
    /**
     * ISO timestamp the poller last silently refreshed this account's access token
     * from its refresh token (P1.2 hardening). Visibility only — lets the dashboard
     * show "token auto-refreshed" so a routine access-token expiry reads as healthy
     * rather than a re-auth event.
     */
    lastRefreshAt?: string;
    /** Monotonic version for optimistic CAS in update(). */
    version: number;
}
/**
 * WS5.2 §6.2 — "locally executable" predicate. An account is executable on THIS
 * machine iff this machine holds it with a real local `configHome` AND a valid
 * login (status active/warming, never needs-reauth/disabled/rate-limited). A
 * meta-only account replicated in from a peer (no local credential, empty
 * `configHome`) is NOT locally executable and must be invisible to every
 * account-selection / swap-target / placement path — closing the force-mode
 * "use an account I have metadata for but no credential" hole at SELECTION time.
 *
 * This is a pure tightening: every real pool account today carries a non-empty
 * `configHome` (required by `add()`), so this only ever excludes a credential-less
 * meta projection — it never changes selection among genuinely-held accounts.
 */
export declare function isLocallyExecutable(a: SubscriptionAccount): boolean;
export interface SubscriptionPoolConfig {
    /** Agent stateDir (e.g. `.instar`). The store lives at <stateDir>/subscription-pool.json. */
    stateDir: string;
}
export interface AddAccountInput {
    id: string;
    nickname: string;
    provider: SubscriptionProvider;
    framework: SubscriptionFramework;
    configHome: string;
    status?: SubscriptionAccountStatus;
    /** Optional at add time; auto-populated from the account's login on poll. */
    email?: string;
}
/** Fields an operator may patch. id/provider/enrolledAt/version are immutable here. */
export interface UpdateAccountInput {
    nickname?: string;
    framework?: SubscriptionFramework;
    configHome?: string;
    status?: SubscriptionAccountStatus;
    lastQuota?: AccountQuotaSnapshot | null;
    lastUsedAt?: string;
    lastRefreshAt?: string;
    email?: string;
}
export declare class ValidationError extends Error {
    constructor(message: string);
}
export declare class SubscriptionPool {
    private storePath;
    private store;
    /**
     * WS5.2 §6.1a — optional emit seam for cross-machine registry follow-me (metadata only).
     * Wired in server.ts ONLY when `multiMachine.accountFollowMe` resolves enabled; null = no
     * replication (single-machine / dark default). The pool emits a REDACTED projection
     * (projectAccountToMeta strips configHome + every credential field by allowlist) — never the
     * login location, never a token.
     */
    private metaReplication;
    constructor(config: SubscriptionPoolConfig);
    /** Inject the follow-me meta emitter (server.ts, gated behind accountFollowMe). */
    setMetaReplicationEmitter(emitter: SubscriptionAccountMetaReplicationEmitter | null): void;
    /** All accounts (a shallow copy — callers can't mutate the store). */
    list(): SubscriptionAccount[];
    /** One account by id, or null. */
    get(id: string): SubscriptionAccount | null;
    /**
     * WS5.2 §6.2 — accounts THIS machine can actually execute against (real local
     * `configHome` + a valid login). The canonical selectable set for the router and
     * every swap/placement path; a credential-less meta projection is excluded.
     */
    locallyExecutable(): SubscriptionAccount[];
    /** Count of accounts. A pool of 0 is the dark/no-op default. */
    size(): number;
    /**
     * Add a new account. Throws ValidationError on bad input or duplicate id.
     * `rawExtra` (if provided) is scanned for credential-bearing field names and
     * rejected — the registry never stores tokens.
     */
    add(input: AddAccountInput, rawExtra?: Record<string, unknown>): SubscriptionAccount;
    /**
     * Patch a mutable account. Returns the updated account, or null if not found.
     * id/provider/enrolledAt are immutable here; version auto-increments (CAS).
     * Throws ValidationError on bad field values or credential-bearing input.
     */
    update(id: string, patch: UpdateAccountInput, rawExtra?: Record<string, unknown>): SubscriptionAccount | null;
    /** Remove an account. Returns true if one was removed. */
    remove(id: string): boolean;
    getHealth(): ComponentHealth;
    private assertNoCredentialFields;
    private load;
    private save;
}
//# sourceMappingURL=SubscriptionPool.d.ts.map