import { type DeviceProfile } from '../../devices/registry.js';
import { type RemoteAgentsJsonParseResult } from '../../remote-agents-json.js';
import type { SessionMeta } from '../types.js';
import { type ToolProgramCountEnvelope, type ToolSearchEnvelope } from '../tool-index.js';
export { REMOTE_STDOUT_MAX_BYTES, RemoteUtf8Accumulator } from '../../ssh-exec.js';
export declare const REMOTE_TOOL_AGGREGATE_MAX_BYTES: number;
export interface RemoteToolByteBudget {
    remainingBytes: number;
    exhausted: boolean;
}
/** Claim received bytes against one fleet-query budget before retaining them. */
export declare function consumeRemoteToolByteBudget(budget: RemoteToolByteBudget, bytes: number): boolean;
/** Charge sanitized, machine-stamped evidence because redaction may expand it. */
export declare function consumeParsedRemoteToolSearchBudget(budget: RemoteToolByteBudget, envelope: ToolSearchEnvelope): boolean;
/**
 * The command run on each peer: answer for itself, as JSON, without recursing.
 * `forwardedArgs` carry the caller's own query/filters (already including the
 * leading `sessions` and a `--json`) so each peer returns a comparable slice.
 * A Windows peer gets a PowerShell invocation (ssh lands in cmd.exe/PowerShell
 * there, where `bash -lc` is not a command); every other OS keeps `bash -lc`.
 */
export declare function remoteListCommand(forwardedArgs: string[], os?: string): string;
/**
 * Parse a peer's `sessions --json` stdout into `SessionMeta[]`, tagging each
 * with `machine`. Defensive against version skew / partial output: non-JSON or
 * a non-array yields `[]`, and non-object entries are dropped rather than
 * throwing. The `machine` we dialed always wins over any value the peer set on
 * its own rows, so grouping keys off the computer we asked. Exported for unit
 * testing without a live tailnet.
 */
export declare function parseRemoteList(stdout: string, machine: string): SessionMeta[];
export declare function parseRemoteListPayload(stdout: string, machine: string, safeResolver?: boolean): {
    items: SessionMeta[];
    valid: boolean;
};
/** Run one remote `agents sessions … --json` and capture stdout. Resolves
 * `{ code: null }` on spawn error or timeout (host treated as dead). */
export declare function sshCapture(target: string, remoteCmd: string, timeoutMs: number, aggregateBudget?: RemoteToolByteBudget, options?: {
    multiplex?: boolean;
    port?: number;
    hostKeyOpts?: string[];
}): Promise<{
    code: number | null;
    stdout: string;
    aggregateBudgetExceeded?: boolean;
}>;
export interface RemoteListResult {
    sessions: SessionMeta[];
    /** How many peer machines we attempted to reach (drives the empty-fleet tip). */
    deviceCount: number;
    /**
     * Peers that failed to answer, by display name. The stderr note above is
     * enough for a printed listing, but the interactive browser repaints over it —
     * so callers rendering a full-screen UI need the outcome as data to tell
     * "that box is asleep" apart from "that box has no matching sessions".
     */
    unreachable: string[];
}
/** Keep browse and tool-search fan-out on the same automatic peer set. */
export declare function isAutomaticSessionPeer(d: DeviceProfile, self: string): boolean;
/**
 * Gather listing sessions from other machines. With an explicit `hosts` list
 * (from `--device`), fan out to exactly those. Otherwise sweep the registered,
 * online devices from `ag devices`, excluding this machine and any without an
 * address. `forwardedArgs` are the caller's own sessions args (query + filters,
 * already `--json`) so every peer returns the same slice this machine asked for.
 */
export interface GatherRemoteListOptions {
    /**
     * Opt-in early-exit for a globally-unique id lookup (a full UUID): the first
     * peer to return the matching row resolves the fan-out and cancels the rest.
     * Omitted for browse/label/prefix sweeps, which must wait for every peer to
     * know whether the match is unique or conflicting.
     */
    isDefinitive?: (session: SessionMeta, machine: string) => boolean;
    /** Per-peer deadline for slower indexed browse queries. */
    timeoutMs?: number;
}
export declare function gatherRemoteList(forwardedArgs: string[], hosts?: string[], opts?: GatherRemoteListOptions): Promise<RemoteListResult>;
export interface RemoteToolSearchResult {
    envelopes: Array<{
        machine: string;
        envelope: ToolSearchEnvelope;
    }>;
    deviceCount: number;
    unreachable: string[];
    truncated: string[];
}
export interface RemoteToolProgramCountResult {
    envelopes: Array<{
        machine: string;
        envelope: ToolProgramCountEnvelope;
    }>;
    deviceCount: number;
    unreachable: string[];
}
export declare function parseRemoteToolProgramCount(stdout: string, machine: string, expectedProgram: string): RemoteAgentsJsonParseResult<{
    machine: string;
    envelope: ToolProgramCountEnvelope;
}>;
export declare function gatherRemoteToolProgramCounts(forwardedArgs: string[], hosts: string[] | undefined, expectedProgram: string): Promise<RemoteToolProgramCountResult>;
export declare function parseRemoteToolSearch(stdout: string, machine: string, expectedClauses?: string[]): ToolSearchEnvelope | undefined;
/**
 * Fleet sibling of {@link gatherRemoteList} for the versioned tool-search
 * envelope. Each peer executes the same local-only query against its own
 * SQLite index; only compact matches cross SSH.
 */
export declare function gatherRemoteToolSearch(forwardedArgs: string[], hosts?: string[], maxAggregateBytes?: number, expectedClauses?: string[]): Promise<RemoteToolSearchResult>;
/** Resolve a peer's SSH target (and OS) from the device registry by its
 * normalized machine id — the same id the fan-out tags rows with. Returns
 * undefined when no registered device with an address matches. */
export declare function resolvePeerTarget(machine: string): Promise<{
    target: string;
    os?: string;
} | undefined>;
/**
 * Fetch one remote session's preview digest from its owning peer — the data
 * behind the picker pane for a `_remote` row, whose transcript file this
 * machine cannot parse. Runs the peer's own `agents sessions preview <id>
 * --local --json` (the same envelope `agents sessions preview` already
 * delegates to for a remote id) and returns its `preview` object, verbatim and
 * UNSANITIZED — the caller owns scrubbing peer-supplied strings before any of
 * them reach a TTY. Undefined on every failure: unregistered machine, SSH
 * error, timeout, version-skewed peer with no `--json` preview envelope.
 */
export declare function fetchPeerPreviewDigest(sessionId: string, machine: string, timeoutMs?: number): Promise<unknown | undefined>;
/** Parse the JSON envelope returned by `sessions preview --json`. */
export declare function parsePeerPreviewDigest(parsed: unknown): unknown | undefined;
/**
 * Run `agents <args>` ON a peer over SSH, attached to this terminal (inherited
 * stdio). `args` is the full arg vector after the binary — callers pass e.g.
 * `['sessions', id, '--markdown']` or `['sessions', 'resume', id]`. Used when a
 * picked session lives on another machine: its transcript and agent binary are
 * there, so both reading (no TTY) and resuming (TTY) must execute on the peer —
 * not via a local `--device` hop, which would discover locally and dead-end for a
 * session that exists only on the peer. Resolves 'no-target' when the machine
 * isn't a dialable registered device; the caller surfaces a clear message.
 *
 * `opts.env` adds variables to the remote command. It deliberately does NOT
 * carry `AGENTS_FLEET_REMOTE` the way the `--device` passthrough does: that marker
 * gates consent-sensitive actions on the far side
 * (lib/browser/remote-control.ts), and a resumed agent is a long-lived session
 * that would inherit it for its whole life — `agents browser start` inside it
 * would then be refused as a cross-machine drive. A one-shot `--device` command
 * can carry the marker; a session cannot.
 *
 * `opts.sessionId` (with `tty`) prints the session id and resume command when
 * the SSH hop ends, so OpenSSH's `Shared connection … closed.` is not the last
 * thing on the local shell (RUSH-3227). Omit it for one-shot non-TTY renders.
 *
 * Resolves `'unreachable'` when the SSH connection itself failed — ssh could not
 * launch (spawn error) or exited with {@link SSH_CONN_FAILURE_CODE} (255, its
 * connect-failure convention) — as opposed to `'ok'` for a hop that actually
 * reached the peer (whatever the remote command's own exit code). This lets a
 * caller prefer the recorded device yet fall back locally when it is genuinely
 * offline (PHNX-3626); callers that ignore `'unreachable'` behave exactly as
 * before (it was `'ok'`).
 */
/**
 * Classify a finished SSH hop by its exit code: `'unreachable'` when the
 * connection itself failed (ssh's {@link SSH_CONN_FAILURE_CODE} = 255, or a null
 * code from a killed/never-launched child), else `'ok'` — the remote command ran,
 * whatever its own exit status. Pure so the offline-device fallback is unit-tested
 * without a live SSH hop (PHNX-3626).
 */
export declare function peerHopOutcome(code: number | null): 'ok' | 'unreachable';
export declare function peerHopCloseNotice(opts: {
    tty?: boolean;
    sessionId?: string;
}, machine: string, code: number | null): string | undefined;
export declare function runOnPeer(args: string[], machine: string, opts?: {
    tty?: boolean;
    env?: Record<string, string>;
    sessionId?: string;
}): Promise<'ok' | 'no-target' | 'unreachable'>;
