import { type BridgeEntry, type OwnershipVerdict } from "./wake-ownership.js";
/** The states an agent can be in with respect to its channel. */
export type PresenceState = "local" | "deferred" | "offline" | "online" | "standby";
/**
 * What the bridge holding the name is to this one, from the two agent-session ids (Claude Code
 * passes one, codex and gemini pass none, and nothing before 3.25.7 recorded it):
 *   same-session   both known, equal    → this agent's own other bridge
 *   no-session     neither side has one → this agent's previous bridge, or another agent (codex, gemini)
 *   other-session  both known, differ   → another agent, proven
 *   unknown        only one side has one → this agent's own older bridge, or another agent
 * 🔴 An ABSENT id is never "different" (review r5 SEC-2): the plugin 3.25.6 + `npx @latest` pair of
 * ONE Claude session was told another agent session held its name.
 */
export type HolderRelation = "same-session" | "no-session" | "other-session" | "unknown";
export declare function holderRelation(holderSession: string | undefined, ownSession: string | undefined): HolderRelation;
/** The live bridge that already serves this peer (from its ownership announcement). */
export interface ServingBridge {
    pid: number;
    version?: string;
    startedAt?: number;
    /** The agent session it announced (3.25.7+, Claude Code); absent = cannot be compared. */
    sessionId?: string;
}
/** One process as `ps` reports it (structural subset of stale-bridge-reaper's ProcInfo). */
export interface PsProc {
    pid: number;
    ppid: number;
    argv: string;
}
/**
 * Is the MCP client of an announced bridge GONE? Then the bridge serves no agent — it is an orphan
 * left draining or (pre-3.25.7) running forever, like marvin's script-spawned bridges — and must not
 * keep a restarted agent out of its own channel.
 *
 *  - 3.25.7+ announcements record `parentPid`: gone when the bridge's CURRENT parent differs from it
 *    (an orphan is reparented; the kernel never hands a live child to another parent otherwise).
 *  - older announcements carry none: gone when the parent is pid 1 or an init-like process.
 *    🔴 Not "ppid === 1" alone: a Linux desktop reparents orphans to its `systemd --user`
 *    subreaper (measured on this dev box, 2026-10-13: an orphaned `sleep` got ppid 1615 = systemd).
 *    🔴 And not the immediate parent alone (review r3 R3-5): an npx-launched bridge keeps its `sh`
 *    and `npm exec` wrappers when the client dies — the one reparented is npm. Walk up through them.
 *
 * Unknown (the holder is not in the process list, ps failed) ⇒ NOT gone: no proof ⇒ it still counts.
 * A holder that announced it started WITHOUT a client (headless) cannot lose one ⇒ never gone.
 */
export declare function holderClientGone(holder: {
    pid: number;
    parentPid?: number;
    noClient?: boolean;
}, procs: PsProc[]): boolean;
/**
 * Was this bridge started inside a `codex exec resume` wake turn? codex (unlike claude's
 * --strict-mcp-config, measured: no bridge starts in a claude wake turn) starts the configured MCP
 * servers for the resumed turn, so each codex wake spawns a bridge in the same checkout. It serves
 * no agent: the bridge that is waking it does. It must never register as the peer (review r3 R3-1)
 * and must not be reported as an agent locked out of its channel (review r2 NEW-4).
 */
export declare function insideCodexWakeTurn(selfPid: number, procs: PsProc[]): boolean;
/**
 * The OTHER live bridges registered in this folder — what an explicit cogent_register_peer tells
 * its caller about (otherBridgeNotice). Not counted: this process, a bridge that announced this same
 * agent session (the agent's own second bridge), a draining bridge and one whose MCP client is gone
 * (they serve no agent), and the process marks.
 */
export declare function otherBridgesInFolder(entries: BridgeEntry[], self: {
    pid: number;
    sessionId?: string;
}, 
/** Called at most once, and only when a candidate is left to check (one `ps`). */
listProcs: () => PsProc[]): BridgeEntry[];
/**
 * COG-85: how long a closed agent's old connection can still hold its name. MEASURED 2026-10-15 (codex
 * 0.162.1, Linux and macOS, probe MCP server): codex's app-server keeps a quit thread's MCP server
 * alive for 60.0 s, then SIGTERMs it. Plain `codex` uses that daemon too. Gemini and `codex --no-daemon`
 * end it at once. A name still held after this is held by another live connection.
 */
export declare const HOLDER_LINGER_SECONDS = 60;
/**
 * The live bridge in this folder that holds `peerId`, from otherBridgesInFolder (so never this
 * process, this agent session's own bridge, a draining one, or one whose client is gone).
 */
export declare function heldInFolder(others: BridgeEntry[], peerId: string, ownSession?: string): BridgeEntry | undefined;
/**
 * A bridge older than 3.25.7 records no agent session at all. Beside a bridge that knows its Claude
 * session it may be that session's OWN older bridge — the plugin's bridge next to an `npx @latest` one
 * (r5 SEC-2) — which lives as long as the session, so "wait 60 s" would never free the name (side-agent
 * review 2026-10-15). Not refused, as before COG-85. Only a plain x.y.z version below 3.25.7 qualifies:
 * an unparseable one is treated as current.
 */
export declare function mayBeOwnPreSessionBridge(holder: BridgeEntry, ownSession: string | undefined): boolean;
/**
 * What an agent locked out of a held name is told to do (COG-85, operator 2026-10-15: retry in 60+ s).
 * 🔴 COG-80: "wait 60 s" is only true when the holder may be this agent's OWN previous connection. When
 * it is provably ANOTHER agent's live bridge (another session, or another platform), waiting frees
 * nothing — KLAIRE 2026-10-16: Codex was told to wait while Claude's live bridge held its name.
 */
export declare function heldNameAdvice(peerId: string, holder?: {
    sessionId?: string;
    platform?: string;
}, own?: {
    sessionId?: string;
    platform?: string;
}): string;
/** True when one of them is PROVEN to be another agent session (both ids recorded, different). */
export declare function anotherAgentSessionAmong(others: BridgeEntry[], ownSession: string | undefined): boolean;
/**
 * What the agent that just registered `peerId` is told about them, in plain words (operator,
 * 2026-10-13: "notify"). The registration is NOT refused: a lingering old window must not lock an
 * agent out of its own name. `verdict` is checkWakeOwnership AFTER this bridge announced: the
 * newest PROCESS wakes for a name, which is not always the one that registered last.
 *
 * 🔴 "Another agent session" is said only when both session ids are recorded and differ. A bridge
 * that recorded none is usually this agent's OWN — a restarted codex agent's previous thread, the
 * plugin 3.25.6 bridge beside `npx @latest` — and was told two agents shared its folder (r5 SEC-2).
 */
export declare function otherBridgeNotice(others: BridgeEntry[], peerId: string, verdict: OwnershipVerdict, ownSession?: string): string;
/** What a presence evaluation concluded, and what it did about it. */
export type PresenceOutcome = {
    state: "local" | "deferred" | "online";
    restored: false;
    detail: string;
} | {
    state: "online";
    restored: true;
    peerId: string;
    detail: string;
} | {
    state: "standby";
    restored: false;
    peerId: string;
    servedBy: ServingBridge;
    /** What servedBy is to this bridge, as far as the two agent-session ids can say. */
    holder: HolderRelation;
    /** True when the standby is re-checked and restores by itself (rule b); false = final (rule c). */
    recheck: boolean;
    detail: string;
} | {
    state: "offline";
    restored: false;
    detail: string;
};
export interface PresenceDeps {
    /** True only when the ACTIVE backend really is the cloud one — not a configuration flag. */
    isCloudConfigured: () => boolean;
    /** True when something already registered in THIS process; its state is authoritative. */
    alreadyRegisteredInProcess: () => boolean;
    /**
     * Is a LIVE bridge already announcing this peer for this working directory?
     *
     * 🔴 TWO BRIDGES, ONE NAME. Credentials are one file per working directory, and every bridge
     * in the checkout reads it — one Claude Code session can run two (plugin + npx), and two agents
     * can share a checkout. Without this check the second bridge
     * re-registers the first one's peerId and supersession stands one of them down. Proven by
     * controlled A/B: with the restore unguarded, wake-dedup-matrix-test --topology two-peer could no
     * longer detect its own break (STAYED_GREEN, 0 overlapping resumes); with presence off it went
     * RED with 1. The gate was telling the truth.
     *
     * Returns that bridge, or null when nobody else serves the peer.
     */
    peerAlreadyServed: (peerId: string) => ServingBridge | null;
    /** The agent session this bridge serves (wake-ownership agentSessionId); undefined = not known. */
    agentSessionId?: () => string | undefined;
    /** True when this bridge runs inside a `codex exec resume` wake turn (insideCodexWakeTurn). */
    startedByWakeTurn?: () => boolean;
    loadCredentials: () => Promise<{
        endpoint?: string;
        sessionId?: string;
        token?: string;
        peerId?: string;
        /** AUD-003 ownership proof, issued once when this peer was created. */
        peerSecret?: string;
    } | null>;
    /**
     * COG-80: why loadCredentials returned nothing although the folder has saved names (they belong to
     * other agents, or several candidates and none is provably this agent's). undefined = no such case.
     */
    restoreSkip?: () => string | undefined;
    /** Go cloud-live and register, restoring the channel entry. Throws on failure. */
    goOnline: (peer: {
        peerId: string;
        endpoint: string;
        sessionId: string;
        token: string;
        cwd: string;
        label: string;
        /** Replayed on register so a peer owned by an EARLIER token can be reclaimed. */
        peerSecret?: string;
    }) => Promise<void>;
    cwd: () => string;
    /** How often a re-checked standby looks again, for the message only (startup.ts owns the timer). */
    recheckSeconds?: number;
    env?: NodeJS.ProcessEnv;
}
/**
 * Automatic presence restore. ON by default — a default-off self-heal heals nobody.
 * Opt out with COGENT_AUTO_PRESENCE=false to observe the state without acting on it.
 */
export declare function autoPresenceEnabled(env?: NodeJS.ProcessEnv): boolean;
/**
 * Determine this agent's presence and, when it is `offline` with a peer it owns,
 * bring it back `online`.
 *
 * NEVER THROWS. Runs fire-and-forget after the MCP transport connects — the
 * `initialize` handshake must never wait on I/O (scripts/startup-nonblocking-test.mjs).
 */
export declare function restorePresence(deps: PresenceDeps): Promise<PresenceOutcome>;
/** Record the evaluated presence. Called by startup after each evaluation. */
export declare function setPresence(outcome: PresenceOutcome): void;
/** The agent's own view of its channel presence. Null until first evaluated. */
export declare function getPresence(): PresenceOutcome | null;
/** Report the outcome once, at a level that matches how much it matters. */
export declare function logPresence(outcome: PresenceOutcome): void;
//# sourceMappingURL=peer-presence.d.ts.map