/**
 * In-memory peer registry for tracking orchestrator peers in a cluster.
 *
 * Maintains peer state including agent inventories from heartbeats,
 * enabling routing decisions without real-time queries. Follows the
 * same label-matching semantics as agent/registry.ts for consistency.
 */
import type { PeerHeartbeat, PeerCapabilities, ScalerCapacitySummary } from '@kici-dev/engine';
export interface PeerAgentInfo {
    agentId: string;
    labels: string[];
    activeJobs: number;
    maxConcurrency: number;
    platform: string;
    arch: string;
    /**
     * Kubernetes-taint-style mandatory labels inherited from the spawning
     * scaler. Empty for static agents and warm-pool replenishment spawns.
     * Cross-peer routing applies the same gate the local label matcher does:
     * a connected-agent entry only matches when every mandatory label appears
     * in the required label set.
     */
    mandatoryLabels: string[];
    /**
     * Name of the scaler backend that spawned this agent, or null for static
     * (stateful) agents not bound to any scaler. Surfaced in diagnostics so the
     * dashboard can group a peer's agents under the correct scaler row.
     */
    scalerName?: string | null;
}
export interface PeerInfo {
    instanceId: string;
    connectionId: string;
    address: string | null;
    routingKeys: string[];
    connected: boolean;
    lastHeartbeatAt: number;
    agents: PeerAgentInfo[];
    draining: boolean;
    capabilities: PeerCapabilities;
    term: number;
    leaderId: string | null;
    /** Scaler-advertised capacity for on-demand backends (from heartbeat). */
    scalerCapacity?: ScalerCapacitySummary[];
    /** Shared config version reported by this peer (0 = unknown/legacy). */
    configVersion: number;
    /** Registry version reported by this peer (0 = unknown/legacy). */
    registryVersion: number;
    /** Cluster role of the peer. */
    role: 'coordinator' | 'worker';
    hostname?: string;
    osRelease?: string;
    totalMemoryMb?: number;
    memoryUsedMb?: number;
    memoryAvailableMb?: number;
    cpuCount?: number;
    uptimeSeconds?: number;
    nodeVersion?: string;
    runningAsUser?: string | null;
    runningAsUid?: number | null;
    version?: string | null;
}
export interface PeerRegistryOptions {
    /** Called when a peer's configVersion is higher than the local version. */
    onConfigVersionBehind?: (peerVersion: number) => void;
    /** Called when a peer's registryVersion is higher than the local version. */
    onRegistryVersionBehind?: (peerVersion: number) => void;
    /** Called when a peer transitions to disconnected state. */
    onPeerDisconnected?: (instanceId: string) => void;
}
export declare class PeerRegistry {
    private readonly peers;
    private localConfigVersion;
    private localRegistryVersion;
    private readonly onConfigVersionBehind?;
    private readonly onRegistryVersionBehind?;
    private readonly onPeerDisconnected?;
    constructor(options?: PeerRegistryOptions);
    /**
     * Update the local config version for comparison with peer heartbeats.
     */
    setLocalConfigVersion(version: number): void;
    /**
     * Update the local registry version for comparison with peer heartbeats.
     */
    setLocalRegistryVersion(version: number): void;
    /**
     * Add or update a peer entry. Sets initial connected state to true.
     * If the peer already exists and is connected, preserves capabilities
     * (avoids the incoming handler resetting data set by the outgoing client).
     * On fresh add or disconnected peer, starts with empty capabilities —
     * auth response or first heartbeat will populate fresh data.
     */
    addPeer(info: {
        instanceId: string;
        connectionId: string;
        address: string | null;
        routingKeys: string[];
        role?: 'coordinator' | 'worker';
    }): void;
    /**
     * Remove a peer entirely from the registry.
     */
    removePeer(instanceId: string): void;
    /**
     * Update peer state from a heartbeat message.
     * Updates agent inventory, draining status, capabilities, term, leaderId,
     * and lastHeartbeatAt timestamp.
     */
    updateHeartbeat(instanceId: string, heartbeat: PeerHeartbeat): void;
    /**
     * Get a single peer by instanceId.
     */
    getPeer(instanceId: string): PeerInfo | undefined;
    /**
     * Get all tracked peers.
     */
    getAllPeers(): PeerInfo[];
    /**
     * Get only connected peers.
     */
    getConnectedPeers(): PeerInfo[];
    /**
     * Find peers whose agents or scaler backends match any of the given label
     * sets AND have available capacity.
     *
     * A peer matches if EITHER:
     * 1. Connected agents: at least one agent's labels are a superset of a
     *    required label set with activeJobs < maxConcurrency.
     * 2. Scaler capacity: at least one scaler backend has activeCount < maxAgents
     *    AND its label sets overlap with the required label sets (same
     *    intersection semantics as agent matching).
     *
     * Excludes draining peers and disconnected peers.
     *
     * @param labelSets - Array of label sets. A peer matches if ANY of its
     *   agents or scaler backends match ANY of the label sets with capacity.
     */
    findPeersWithCapacity(labelSets: string[][]): PeerInfo[];
    /**
     * Find peers whose agents or scaler backends match any of the given label sets,
     * regardless of current capacity. Used to differentiate "no peer handles this label"
     * from "peers exist but are at capacity".
     *
     * Excludes disconnected and draining peers.
     */
    findPeersWithLabels(labelSets: string[][]): PeerInfo[];
    /**
     * Mark a peer as disconnected and immediately evict its capabilities.
     * No grace period — disconnected peers have no usable agents or scaler capacity.
     */
    markDisconnected(instanceId: string): void;
    /**
     * Mark a peer as connected.
     */
    markConnected(instanceId: string): void;
    /**
     * Check if a peer's last heartbeat is older than the given threshold.
     */
    isStale(instanceId: string, thresholdMs: number): boolean;
    /**
     * Total number of tracked peers (connected + disconnected).
     */
    getPeerCount(): number;
    /**
     * Number of currently connected peers.
     */
    getConnectedPeerCount(): number;
    /**
     * Get count of connected coordinator (non-worker) peers.
     * Used for Raft quorum calculations — workers don't participate in elections.
     */
    getConnectedCoordinatorPeerCount(): number;
    /**
     * Evict stale peers whose last heartbeat exceeds the timeout.
     * Marks them as disconnected and clears their capabilities.
     * Returns the list of evicted peer instanceIds.
     *
     * called on a timer by the coordinator
     * (2 missed heartbeats at 30s interval = 60s default timeout).
     */
    evictStalePeers(staleTimeoutMs: number): string[];
    /**
     * Get all peers with role=worker.
     */
    getWorkerPeers(): PeerInfo[];
    /**
     * Get all peers with role=coordinator.
     */
    getCoordinatorPeers(): PeerInfo[];
}
//# sourceMappingURL=peer-registry.d.ts.map