/**
 * Reconcile a local host-task record against the remote run's ground truth.
 *
 * A detached `--device` run outlives the local follower: it keeps running and
 * writes its exit code to `<id>.exit` on the host even if the laptop sleeps or
 * the SSH connection drops mid-follow. When that happens the local record is
 * left at `status:'running'` forever, because the only path that finalizes it
 * (dispatch's post-follow `updateTask`) never runs. This module re-reads the
 * remote `.exit` on demand — from `agents devices ps` / `agents logs` — and heals the
 * record. We only ever CONFIRM completion; an unreachable host or an absent
 * `.exit` leaves the record `running` (we never guess failure).
 */
import { type SshExecResult } from '../ssh-exec.js';
import { type HostTask } from './tasks.js';
export type RemoteExitState = {
    state: 'running';
} | {
    state: 'done';
    code: number;
} | {
    state: 'unreachable';
};
/**
 * Classify a `cat <remoteExit>` result into a remote run state. Pure: all the
 * bug-prone branching (ssh-failure vs absent vs empty vs coded) lives here so it
 * can be unit-tested without a live host. ssh's own connection failure surfaces
 * as code 255, a spawn error/timeout as `code === null`; neither is the remote
 * command's exit and both mean "unreachable". An empty read is "still running"
 * (the `.exit` is written only after the run ends, so absent `cat` → exit 1 →
 * empty stdout, and a truncate-then-write mid-race is a sub-ms empty window).
 */
export declare function classifyExit(res: Pick<SshExecResult, 'code' | 'stdout' | 'timedOut'>): RemoteExitState;
export declare function readRemoteExit(target: string, remoteExit: string, timeoutMs?: number, identityFile?: string, remoteShell?: 'posix' | 'powershell'): RemoteExitState;
/**
 * Async twin of {@link readRemoteExit} for the daemon's heartbeat tick — the ssh
 * read runs on the shared event loop, so it MUST NOT block it with a synchronous
 * `spawnSync('ssh', …)` (PHNX-3695). `sshExecAsync` applies the same
 * timeout/kill-grace bound.
 */
export declare function readRemoteExitAsync(target: string, remoteExit: string, timeoutMs?: number, identityFile?: string, remoteShell?: 'posix' | 'powershell'): Promise<RemoteExitState>;
/**
 * Heal one record. Terminal records are immutable (and never re-probed); a
 * `running` record is resolved to completed/failed only when the remote `.exit`
 * holds a code. Returns the (possibly updated) task.
 */
export declare function reconcileTask(task: HostTask): HostTask;
/**
 * Async twin of {@link reconcileTask} for the daemon heartbeat tick (PHNX-3695):
 * identical healing logic, non-blocking ssh read via {@link readRemoteExitAsync}.
 */
export declare function reconcileTaskAsync(task: HostTask): Promise<HostTask>;
/**
 * Heal a list of records for a listing (`agents devices ps`). Only `running` tasks
 * are probed; each host is reachability-checked ONCE (deduped by target) so a
 * down host costs a single short timeout instead of one per task, and its tasks
 * are left `running` rather than falsely failed. Sequential by design — with the
 * shared ssh control socket the live-host reads are sub-100ms, and a parallel
 * (async) ssh path is deliberately out of scope.
 */
export declare function reconcileRunningTasks(tasks: HostTask[]): HostTask[];
export declare function reachabilityProbeCommand(remoteShell?: 'posix' | 'powershell'): string;
