export type PresenceStatus = 'connected' | 'disconnected';
export type PresenceLocation = 'local' | 'ssh';
/** A session as OBSERVED by one tick's active scan — the pure reconcile input. */
export interface ObservedSession {
    sessionId: string;
    agent: string;
    /** `local` when the session runs on this box, `ssh` when it's on a peer. */
    location: PresenceLocation;
    /** The device/host the session runs on (this box's hostname, or the peer). */
    device: string;
    /** Transport the session's provenance reports (`local` | `ssh` | ...). */
    transport: string;
    /** True for an interactive (terminal/tmux) session — a drop is reconnect-worthy;
     *  false for a headless remote continuation, where a drop means keep-alive. */
    interactive: boolean;
}
export interface PresenceRecord {
    sessionId: string;
    agent: string;
    location: PresenceLocation;
    device: string;
    transport: string;
    interactive: boolean;
    /** ms of the last tick this session was observed active. */
    lastSeenMs: number;
    status: PresenceStatus;
}
/** What the tick should do about a session that just flipped `disconnected`. */
export type PresenceAction = 'reconnect-nudge' | 'keep-alive' | 'none';
export interface PresenceTransition {
    record: PresenceRecord;
    from: PresenceStatus;
    to: PresenceStatus;
    action: PresenceAction;
}
/** A `disconnected` record older than this is pruned — the session is gone for
 *  good, not merely between clients. Kept generous so a long reconnect window
 *  (the reconnect loop's own bounded backoff) never drops a record mid-recovery. */
export declare const PRESENCE_TTL_MS: number;
/** `presence.json` under the watchdog state dir. `dir` is the watchdog state dir
 *  (the tick passes its own, overridable in tests); default is the real one. */
export declare function presenceFilePath(dir?: string): string;
export declare function loadPresence(dir?: string): Record<string, PresenceRecord>;
export declare function savePresence(map: Record<string, PresenceRecord>, dir?: string): void;
/** The subset of an `ActiveSession` the presence adapter reads (structural, so
 *  this module doesn't import the heavy active-session graph). */
export interface ActiveSessionLike {
    sessionId?: string;
    kind: string;
    /** 'terminal' (interactive) | 'headless' | 'teams' | 'cloud' | ... */
    context: string;
    /** Set for a peer session by the fleet fan-out (`gatherRemoteActive`). */
    machine?: string;
    provenance?: {
        transport?: string;
    };
}
/**
 * Map this tick's active sessions to presence observations. A session on a peer
 * carries `machine` (from the fleet fan-out) → `location: 'ssh'`; a local one →
 * `'local'`. `interactive` is true only for a `terminal` context (a tmux/terminal
 * session whose drop is reconnect-worthy) — headless/teams/cloud are not.
 * Sessions with no id are skipped (they can't be tracked or addressed).
 */
export declare function observedFromActive(sessions: ActiveSessionLike[], selfHost?: string): ObservedSession[];
/** The action for a session that just flipped to `disconnected`: an interactive
 *  session wants a reconnect nudge; a headless remote wants keep-alive; a local
 *  headless that vanished is simply gone (no action). */
export declare function actionFor(record: PresenceRecord): PresenceAction;
/**
 * Pure state machine. Given the prior store, the sessions observed THIS tick, and
 * `nowMs`, return the next store plus the set of status transitions.
 *
 * - observed now                 -> `connected`, lastSeen = now (record refreshed)
 * - tracked but absent now       -> `disconnected` (lastSeen kept from prior tick)
 * - `disconnected` older than TTL -> pruned (gone for good)
 *
 * `transitions` carries only sessions whose status actually changed, each with
 * the action the tick should take — so the caller never re-nudges a session that
 * was already disconnected last tick.
 */
export declare function reconcilePresence(prev: Record<string, PresenceRecord>, observed: ObservedSession[], nowMs: number): {
    next: Record<string, PresenceRecord>;
    transitions: PresenceTransition[];
};
