/** * persistence.ts — Disk I/O for session registry and voice configuration. * * Parameterized by appDir (e.g. "~/.whazaa" or "~/.telex") so each * consumer uses its own data directory. Transport-specific store caches * (Baileys stores, Telegram chats) remain in per-project code. * * Both stores here load into module state at startup and save that state back, * which is the read -> substitute-empty -> make-it-permanent shape described at * the top of core/json-store.ts. They go through it: an unparseable file blocks * writes instead of being replaced, and every write is atomic with a `.bak`. */ import type { VoiceConfig } from "../types/index.js"; /** * Set the application data directory. * Must be called before any load/save operations. */ export declare function setAppDir(dir: string): void; export declare function getAppDir(): string; export declare const DEFAULT_VOICE_CONFIG: VoiceConfig; export declare function loadVoiceConfig(): VoiceConfig; export declare function saveVoiceConfig(config?: VoiceConfig): void; export declare function loadSessionRegistry(): void; export declare function saveSessionRegistry(): void; type SessionNamesStore = Record; /** Drop the cached name store (used when the app dir changes, and by tests). */ export declare function resetSessionNamesCache(): void; /** * What a claim on an already-held name found, so the caller can report it. * * `heldBy` is the live session id that owns the name — reported, not repaired * here: only the caller knows whether its failure is worth a toast, an IPC * error, or just a log line. */ export interface NameClaimResult { ok: boolean; heldBy?: string; } /** * Is a store key a live session right now? Wired by the daemon at startup; * "unknown" when unwired (CLI, tests) or when the enumeration has nothing to * say — an empty or failed enumeration must not read as "every holder is gone". * * "live" — the id (or its aibrokerId) is in the current snapshot * "gone" — the snapshot answered and does not contain it * "unknown" — no probe, or the enumeration came back empty/failed */ export type HolderLiveness = (key: string) => "live" | "gone" | "unknown"; /** Wire/unwire the liveness oracle the theft guard asks. Tests use this too. */ export declare function setHolderLivenessProbe(fn: HolderLiveness | null): void; /** * Persist a user-chosen name for an iTerm2 session. * @param itermSessionId - the raw iTerm2 session UUID (e.g. "942A4044-...") * @param name - the user-set name * * ONE NAME, ONE LIVE HOLDER. On 2026-09-26 21:07 a second iTerm tab registered * over MCP under a name a live session had held since 2026-09-24; the store * then mapped the name to both ids, a daemon restart bound it to the newcomer, * and when that tab dropped to a shell the dispatcher probed the corpse, * declared the name unreachable, and parked both daily sweeps — the real * session was never tried. So a claim is refused while another LIVE session * holds the name; succession requires the old holder to be verifiably gone, * and a gone holder's binding is dropped rather than left to shadow the name. * Unknown liveness keeps the old binding (it may still be live) and allows the * write — resolution prefers the live holder either way. */ export declare function setPersistentSessionName(itermSessionId: string, name: string): NameClaimResult; /** * Drop names whose session no longer exists. * * This map is keyed by session id and has never been garbage-collected: on * 2026-08-04 it held 96 entries, most of them ids belonging to terminals closed * days earlier, including four separate dead ids all answering to "PAI". That * is not merely untidy. `lookupPersistentName` resolves by durable id as well * as primary id, so a stale entry is a live session's opportunity to answer to * the wrong name — and answering to the wrong name is how `pai ` * reached the wrong terminal. * * `liveIds` must be the CURRENT set. The guard below is the whole safety of * this function: called with an empty set — iTerm not running, an enumeration * that failed, a daemon starting before the terminal — "no session is live" * reads identically to "every session ended", and pruning would erase every * name the user has ever assigned. So an empty set prunes nothing. * * `opts.prunable` scopes WHICH keys may ever be dropped. The daemon's * enumeration tick passes an iTerm-UUID test: durable-id keys (tmux * @aibroker_id) survive a tmux server being down, so absence from one * enumeration is not evidence about them. Keys the filter rejects are skipped * entirely — no miss recorded, nothing removed. * * Returns the number of entries removed, for the caller to log. */ export declare function pruneSessionNames(liveIds: Iterable, opts?: { prunable?: (key: string) => boolean; }): number; /** * A store key in iTerm's session-UUID shape. The daemon's enumeration tick * prunes only keys that pass this: a tmux durable id (@aibroker_id) keys a * name that is MEANT to outlive pane churn and server restarts, so absence * from one enumeration says nothing about it. */ export declare const ITERM_UUID_KEY: RegExp; /** * Get the user-chosen persistent name for an iTerm2 session, or undefined. */ export declare function getPersistentSessionName(itermSessionId: string): string | undefined; /** * Get all persistent session names (for re-assert on rescan). */ export declare function getAllPersistentSessionNames(): SessionNamesStore; /** * Resolve the persistent name for a session, preferring the durable id over the * primary id. tmux pane ids (%N) reset on server restart, so tmux names are keyed * on the pane's @aibroker_id (passed as `durableId`); iTerm GUIDs are already * stable and pass `durableId` undefined. */ export declare function lookupPersistentName(names: SessionNamesStore, id: string, durableId?: string | null): string | null; /** * Remove a persistent name when a session ends. */ export declare function removePersistentSessionName(itermSessionId: string): void; export {}; //# sourceMappingURL=persistence.d.ts.map