export declare const REGISTRY_FILE: string; /** Most recent embeddings kept per speaker (same cap as koi-g2). */ export declare const MAX_EMBEDDINGS_PER_SPEAKER = 8; export type VoiceSpeaker = { name: string; embeddings: number[][]; wearer?: boolean; /** Runtime extras — optional, ignored (but preserved) by koi-g2. */ anon?: boolean; samples?: number; lastHeardAt?: number; }; export type VoiceRegistry = { speakers: VoiceSpeaker[]; }; /** Read the registry (mtime-cached). Missing/corrupt file → empty registry. */ export declare function loadRegistry(): VoiceRegistry; /** Persist the registry (compact JSON, same shape koi-g2 writes). */ export declare function saveRegistry(registry: VoiceRegistry): void; export declare function findSpeaker(registry: VoiceRegistry, name: string): VoiceSpeaker | undefined; export declare function getWearer(registry: VoiceRegistry): VoiceSpeaker | undefined; /** * Best match across ALL stored embeddings of all speakers (max cosine — the * exact koi-g2 `identify` semantics, threshold applied by the caller). */ export declare function bestMatch(registry: VoiceRegistry, emb: readonly number[]): { speaker: VoiceSpeaker | null; score: number; }; /** * Best match restricted to anonymous tracks (max cosine over their stored * embeddings). Used to fold near-miss / over-cap voices into an existing * anonymous track instead of forking a new one (churn control). */ export declare function bestAnonMatch(registry: VoiceRegistry, emb: readonly number[]): { speaker: VoiceSpeaker | null; score: number; }; /** Count of anonymous tracks currently in the registry. */ export declare function countAnonSpeakers(registry: VoiceRegistry): number; /** Append a fingerprint sample (keep the newest 8) + bump stats. */ export declare function addEmbedding(speaker: VoiceSpeaker, emb: number[], ts?: number): void; /** Bump samples/lastHeardAt without storing a new fingerprint. */ export declare function touchSpeaker(speaker: VoiceSpeaker, ts?: number): void; /** * Enroll or extend a named speaker (koi-g2 `enroll` semantics: name matched * case-insensitively, wearer flag is exclusive, newest 8 embeddings kept). * Does NOT save — callers batch registry mutations, then saveRegistry(). */ export declare function enrollSpeaker(registry: VoiceRegistry, name: string, embs: number[][], opts?: { wearer?: boolean; anon?: boolean; }): VoiceSpeaker; /** Is this an auto-assigned anonymous track ("voice 2", "voice 3", …)? */ export declare function isAnonSpeaker(speaker: VoiceSpeaker): boolean; /** * Next free anonymous track name. Starts at "voice 2" — "voice 1" is * implicitly the wearer ("you"), so numbering other voices from 2 reads * naturally in transcripts. */ export declare function nextAnonName(registry: VoiceRegistry): string; /** * Rename a track (e.g. "voice 2" → "Thomas"). If the target name already * exists, the tracks merge: embeddings pool (newest 8 win), stats sum, and * wearer status survives from either side. Saves on success. */ export declare function renameSpeaker(currentName: string, newName: string): VoiceSpeaker | null; /** Remove a track entirely. Returns false when no such speaker exists. */ export declare function forgetSpeaker(name: string): boolean;