import { type H2ALaunchContext, type H2ASession, type H2ASessionState, type H2AWorkStatus } from "../../session.js"; export interface PresenceWriteResult { /** Absolute path of the presence file on disk. */ readonly path: string; } /** * Atomically write a session's presence file under `/.h2a/presence/`. * Uses a temp file + rename so concurrent readers always see a complete * JSON document (the session owns its own file, so writer contention is * not expected, but we keep the rename anyway for partial-write safety). */ export declare function writePresence(root: string, session: H2ASession): PresenceWriteResult; /** Read a presence file by session id. Returns undefined if absent or malformed. */ export declare function readPresence(root: string, sessionId: string): H2ASession | undefined; /** * Delete a presence file. Idempotent: missing file is not an error. */ export declare function deletePresence(root: string, sessionId: string): void; export interface ListPresenceOptions { /** Reference instant; defaults to Date.now(). */ readonly now?: number; /** Expiry window in ms; defaults to H2A_SESSION_DEFAULT_EXPIRY_MS. */ readonly expiryMs?: number; /** Include expired sessions (default false). */ readonly includeExpired?: boolean; /** Sweep malformed/expired files (default true). Read-only projections disable it. */ readonly sweep?: boolean; } export interface ListPresenceResult { readonly sessions: H2ASession[]; readonly warnings: string[]; } /** * Read all presence files under `/.h2a/presence/`, filter out malformed * files, and (by default) drop sessions whose heartbeat is older than * `expiryMs`. Sweep expired files from disk as a side-effect. */ export declare function listPresenceWithDiagnostics(root: string, options?: ListPresenceOptions): ListPresenceResult; export declare function listPresence(root: string, options?: ListPresenceOptions): H2ASession[]; /** * Mutate a session's `heartbeatAt` (or `state`) on disk by reading, * patching, and re-writing. The session owns its file so this is safe * without a lock for V1 (DEC-051 acknowledges this constraint). */ export declare function updatePresence(root: string, sessionId: string, patch: { heartbeatAt?: string; state?: H2ASessionState; workStatus?: H2AWorkStatus; launchContext?: H2ALaunchContext; lastMcpActivityAt?: string; /** * Display name (spec 2026-07-25-h2a-lane-addressing §D1b). Mutable at * runtime so a host-native rename converges into presence within one * heartbeat instead of staying stale until the host reconnects. UX only — * the routing key is the frozen `instance` handle, which never moves. * Pass only a non-empty value; omit to keep the current name. */ name?: string; }): H2ASession | undefined; /** * Reap presence files for the SAME `instance` whose owning process is provably * dead — the "false-live" left behind when a host (Claude Code / Codex) drops * the MCP stdio connection WITHOUT signalling the child: the process lingered * and its blind heartbeat kept the presence "live", so peers kept routing to an * agent that could no longer answer. A fresh `mcp-serve` boot calls this at * auto-open to clear the stale presence of a previous connection of the SAME * agent. * * Reaps an entry iff ALL hold: * - same `instance` (an instance never migrates machines — it is keyed on the * provider conversation — so the local pid probe is valid); * - a DIFFERENT `sessionId` than `keepSessionId` (never reap the caller); * - a numeric `pid` that `isAlive` reports dead. * * Best-effort: never throws. `isAlive` is injectable for deterministic tests. * * @returns the sessionIds that were reaped. */ export declare function reapDeadInstancePresence(root: string, instance: string, keepSessionId: string, isAlive?: (pid: number) => boolean): string[]; /** * Host-wide janitor: reap EVERY presence file whose owning process is provably * dead, regardless of instance. The safest possible reap — a dead process * cannot own a live session — but it assumes presence pids are local to this * machine (true for a single-host bus). On a bus shared across machines, scope * with `reapDeadInstancePresence` instead, since a remote pid would read as * dead here. Best-effort: never throws. `isAlive` is injectable for tests. * * @returns the reaped sessionIds (and, for reporting, their instances). */ export declare function reapAllDeadPresence(root: string, isAlive?: (pid: number) => boolean): Array<{ sessionId: string; instance: string; pid: number; }>; //# sourceMappingURL=presence.d.ts.map