/**
 * Agent Registry — unified agent tracking and port allocation.
 *
 * Maintains a machine-wide registry at ~/.instar/registry.json tracking
 * ALL agents on the machine (both standalone and project-bound).
 *
 * Replaces the older PortRegistry with:
 *   - Agent type awareness (standalone vs project-bound)
 *   - Status tracking with heartbeat
 *   - File locking for safe concurrent access
 *   - Migration from legacy port-registry.json
 *
 * The canonical unique key is `path` (absolute path to the project directory).
 * Agent names are display labels only — NOT unique.
 */
import type { AgentRegistry, AgentRegistryEntry, AgentType, AgentStatus } from './types.js';
/**
 * WHATWG Fetch "bad ports" — node's global `fetch` (undici) throws
 * `TypeError: fetch failed { cause: 'bad port' }` for any URL on one of these
 * ports. Instar's multi-machine mesh does ALL of its cross-machine I/O over node
 * fetch (pairing `POST /api/pair`, lease broadcast/pull, heartbeat), so an agent
 * whose server lands on a blocked port is SILENTLY unreachable over the mesh —
 * curl works (it ignores the blocklist), so the failure is invisible. Observed
 * live 2026-06-02: a paired test agent on port 4045 (NFS `lockd`, on this list)
 * could never be joined ("fetch failed: bad port") despite curl reaching it. The
 * default allocation range (4040–4099) contains 4045, so the allocator MUST skip
 * these. Source: the WHATWG Fetch spec "bad ports" table.
 */
export declare const FETCH_BLOCKED_PORTS: ReadonlySet<number>;
/** True if `port` is on the WHATWG fetch bad-ports list (node `fetch` refuses it). */
export declare function isFetchBlockedPort(port: number): boolean;
/**
 * Validate an agent name for use in paths and registration.
 * Rejects names with path separators, null bytes, or `..`.
 */
export declare function validateAgentName(name: string): boolean;
/**
 * Load the agent registry from disk.
 * On first load, migrates from legacy port-registry.json if present.
 * Returns an empty registry if no file exists.
 */
export declare function loadRegistry(): AgentRegistry;
/**
 * Save the registry to disk (atomic write via temp file + rename).
 */
export declare function saveRegistry(registry: AgentRegistry): void;
/**
 * Remove stale entries where the process is no longer running.
 * Prunes entries that are stale AND either from ephemeral paths or expired (>1 hour).
 * Returns the cleaned registry (mutates in-place for efficiency).
 */
export declare function cleanStaleEntries(registry: AgentRegistry): AgentRegistry;
/**
 * Register an agent (add or update by canonical path).
 */
export declare function registerAgent(agentPath: string, name: string, port: number, type?: AgentType, pid?: number): void;
/**
 * Unregister an agent by its canonical path.
 *
 * `opts.onlyIfPid` guards the removal: the entry is only removed when its
 * recorded pid matches. Server shutdown MUST pass its own pid — otherwise an
 * old server generation's late shutdown (auto-update restart) deletes the
 * successor's fresh registration by path, and the agent silently vanishes
 * from the registry (the registry lost-update race). An entry whose pid is
 * missing or different is presumed to belong to the successor and is left
 * intact. Operator/CLI removal (no opts) still removes unconditionally.
 */
export declare function unregisterAgent(agentPath: string, opts?: {
    onlyIfPid?: number;
}): void;
/**
 * Update an agent's status and optionally its PID.
 */
export declare function updateStatus(agentPath: string, status: AgentStatus, pid?: number): void;
/**
 * Update the heartbeat for an agent by canonical path.
 *
 * Returns whether the entry was found. A `false` return means the entry has
 * vanished (e.g. an old generation's shutdown raced the registration away) —
 * callers that own a live server should re-register rather than silently
 * no-op forever.
 */
export declare function heartbeat(agentPath: string): boolean;
/**
 * Force-remove a stale registry lock file.
 * Used as a recovery mechanism when proper-lockfile's stale detection fails
 * (e.g., after a mutex crash leaves the lock in an inconsistent state).
 */
export declare function forceRemoveRegistryLock(): boolean;
/**
 * Start a periodic heartbeat. Returns a cleanup function.
 * Tracks consecutive failures and force-removes the registry lock after
 * repeated failures — recovers from crash-induced stale locks.
 *
 * `reRegister` (optional) is invoked when a heartbeat finds the entry MISSING
 * — i.e. something deleted this agent's registration while its server is
 * still alive (the classic case: an old generation's pid-unguarded shutdown
 * unregister racing a back-to-back update restart). The callback should
 * re-create the registration (e.g. `() => registerAgent(...)`); the next
 * heartbeat then confirms it. Without the callback the missing entry is only
 * logged — preserving the old behavior for callers that don't own a server.
 */
export declare function startHeartbeat(agentPath: string, intervalMs?: number, reRegister?: () => void): () => void;
/**
 * List all agents, optionally filtered by type and/or status.
 * Cleans stale entries before returning.
 */
export declare function listAgents(filter?: {
    type?: AgentType;
    status?: AgentStatus;
}): AgentRegistryEntry[];
/**
 * Get a specific agent by canonical path.
 */
export declare function getAgent(agentPath: string): AgentRegistryEntry | null;
/**
 * Allocate a free port from the range, avoiding conflicts.
 * If the agent already has a port, return that port.
 */
export declare function allocatePort(agentPath: string, rangeStart?: number, rangeEnd?: number): number;
/**
 * Register a port for a project (PortRegistry compatibility).
 * Uses projectDir as the canonical path key.
 */
export declare function registerPort(projectName: string, port: number, projectDir: string, pid?: number): void;
/**
 * Unregister a port by project name (PortRegistry compatibility).
 * Looks up the agent by name and removes it.
 */
export declare function unregisterPort(projectName: string): void;
/**
 * Start a heartbeat by project name (PortRegistry compatibility).
 * Looks up the agent's path by name.
 */
export declare function startHeartbeatByName(projectName: string, intervalMs?: number): () => void;
/**
 * List all instances (PortRegistry compatibility).
 * Returns entries in the legacy PortEntry-compatible shape.
 */
export declare function listInstances(): AgentRegistryEntry[];
/**
 * Allocate a port by project name (PortRegistry compatibility).
 */
export declare function allocatePortByName(projectName: string, rangeStart?: number, rangeEnd?: number): number;
//# sourceMappingURL=AgentRegistry.d.ts.map