/**
 * POSIX single-quote a string for safe interpolation into a remote shell command.
 * Always wraps (unlike the bare-passthrough variant in `ssh-exec.ts`) — the
 * forwarded `agents` argv is embedded verbatim inside `bash -lc '<cmd>'`, so
 * every token is quoted to keep the command boundary unambiguous.
 */
export declare function shellQuote(s: string): string;
/**
 * Strip the `--device`/`-D` flag (and its value) from a raw `agents sessions` argv,
 * leaving the args to forward to the remote unchanged. The remote runs the same
 * binary, so every other flag (`--since`, `--last`, `--json`, query, …) carries
 * over for free. Handles every form commander accepts: `--device h`, `--device=h`,
 * `-D h`, `-D=h`, and the glued short form `-Dh`.
 *
 * @param argv full process argv; the sessions args begin at index 2
 *             (`[runtime, script, 'sessions', ...]`).
 */
export declare function buildForwardedArgs(argv: string[], hosts?: Set<string>): string[];
/**
 * Force a forwarded `agents sessions` listing to span the peer's WHOLE index.
 *
 * A remote listing runs in the peer's SSH-login cwd — its home dir — and the
 * default listing is silently cwd-scoped, so `sessions --device box` reads as
 * empty even when the box's index is full (`No sessions found for /home/<user>`).
 * Across SSH a peer's cwd is meaningless, so `--device` defaults to `--all`
 * (whole-index) scope. This only drops the *cwd* narrowing — an explicit path
 * query, `--project`, `--since`, or `--agent` filter still narrows on top, and
 * a query that looks like a path takes precedence over `--all` on the remote.
 * Idempotent: never adds a second `--all`.
 */
export declare function ensureWholeIndex(forwardedArgs: string[]): string[];
/**
 * Build the single remote command string for `ssh <host> <cmd>`. Forwarded args
 * are quoted for the inner login shell, then the whole `agents …` invocation is
 * quoted again so it survives `bash -lc <...>`.
 *
 * `os` selects the remote shell: a Windows host gets a PowerShell invocation
 * (ssh lands in cmd.exe/PowerShell there, where `bash -lc` does not exist);
 * anything else — including unknown/absent — keeps the POSIX form unchanged.
 * The forwarded terminal width rides across as an env var either way so the
 * remote renders its table to the local screen.
 */
export declare function buildRemoteCommand(forwardedArgs: string[], columns?: number, os?: string): string;
/** The four outcomes of one `ssh <host> agents sessions …` invocation. */
export type SshOutcome = 'ok' | 'unreachable' | 'query-failed' | 'spawn-error';
/**
 * Classify an ssh `spawnSync` result. ssh(1) reserves exit 255 for its own
 * connection-layer failures (host down, timeout, refused, auth, changed host
 * key) — distinct from any other non-zero, which is the remote `agents sessions`
 * exit code forwarded back (the query ran but failed). The two must be handled
 * differently: 255 may fall back to cache, a forwarded failure must surface.
 */
export declare function classifySshFailure(res: {
    error?: Error | null;
    status: number | null;
}): SshOutcome;
/**
 * How long a successful remote fetch may be served without re-SSHing.
 * Short on purpose: session listings must stay near-live (RUSH-2062). Match the
 * active-session snapshot window so surfaces share one freshness model.
 */
export declare const REMOTE_CACHE_MAX_AGE_MS = 15000;
/**
 * Deterministic cache path for a (host, forwarded-args) pair. The forwarded args
 * are hashed so distinct queries cache independently; the host stays readable in
 * the filename (sanitised so `user@host` and aliases are filesystem-safe).
 */
export declare function remoteCachePath(host: string, forwardedArgs: string[]): string;
/**
 * Pure freshness check for a remote-sessions cache entry. A reachable host
 * skips SSH only while this returns true; unreachable fallback ignores age.
 */
export declare function isRemoteCacheFresh(mtimeMs: number, nowMs: number, maxAgeMs?: number): boolean;
export interface RemoteCacheHit {
    output: string;
    mtimeMs: number;
}
/**
 * Read a cached remote fetch. When `maxAgeMs` is set, returns null if the
 * entry is older than the window (cache-first path for reachable hosts).
 * Omit `maxAgeMs` to accept any age (unreachable fallback).
 */
export declare function readRemoteCache(host: string, forwardedArgs: string[], opts?: {
    maxAgeMs?: number;
    nowMs?: number;
}): RemoteCacheHit | null;
/** Banner shown above replayed cache rows when the peer is offline. */
export declare function formatStaleBanner(host: string, mtimeMs: number): string;
/** Message shown when a host is unreachable and there is no cache to fall back to. */
export declare function formatUnreachable(host: string): string;
/** Persist a successful fetch for later cache-first / offline replay.
 * Best-effort: a cache write must never break the live query. Exported for tests. */
export declare function writeRemoteCache(host: string, forwardedArgs: string[], output: string): void;
/**
 * Serve a *fresh* cache entry for a reachable-host skip (no banner — the data
 * is still within the freshness window). Returns false when missing/stale so
 * the caller SSHes. RUSH-2062: without this, a reachable host never skipped SSH
 * even when the cache was just written.
 */
export declare function serveWarmRemoteCache(host: string, forwardedArgs: string[], opts?: {
    maxAgeMs?: number;
    nowMs?: number;
}): boolean;
/** Replay a cached fetch for an unreachable host (any age). Banner goes to
 * stderr (so a piped stdout stays exactly the cached rows); returns false when
 * nothing is cached for this exact (host, query). */
export declare function replayRemoteCache(host: string, forwardedArgs: string[]): boolean;
export interface RunRemoteSessionsOptions {
    /** Skip warm cache and SSH every host (force-refresh). */
    forceRefresh?: boolean;
    /** Override freshness window for the warm path. */
    maxAgeMs?: number;
    /** Clock (tests). */
    nowMs?: number;
}
/**
 * Run the current `agents sessions` invocation on one or more remote machines over
 * SSH, writing each remote's output to the terminal.
 *
 * Cache policy (RUSH-2062):
 * - **Default:** serve a fresh cache hit without SSH; SSH only on miss/stale.
 * - **`forceRefresh`:** always SSH, then rewrite the cache.
 * - **Unreachable:** fall back to any cached output (with a stale banner).
 *
 * Sets `process.exitCode = 1` if any host could not be answered (live or cached).
 * Reads the invocation from `process.argv` (override via `argv` for testing).
 *
 * Output is captured rather than `stdio: 'inherit'`-streamed so it can be cached.
 * Session output is small and the remote returns quickly, so buffering is
 * imperceptible; `maxBuffer` is generous for the rare large `--markdown <id>` dump.
 */
export declare function runRemoteSessions(hosts: string[], argv?: string[], opts?: RunRemoteSessionsOptions): void;
