/** * Thread Deduplication Service (Phase 3) * * Pure functions for detecting duplicate threads by embedding similarity, * token overlap, or normalized text equality. Zero I/O — all Supabase * and embedding calls live in the caller (create-thread.ts). * * Strategy: * 1. If embedding available: cosine similarity > 0.85 → duplicate * 2. Token overlap coefficient > 0.6 → duplicate (no API key needed) * - Lowered to 0.4 when both threads share an issue prefix (e.g., OD-692:) * 3. Normalized text equality → duplicate * 4. If no existing threads: skip check */ import type { ThreadObject } from "../types/index.js"; export interface ThreadWithEmbedding { thread_id: string; text: string; embedding: number[] | null; } export interface DedupResult { is_duplicate: boolean; matched_thread_id: string | null; matched_text: string | null; similarity: number | null; method: "embedding" | "token_overlap" | "text_normalization" | "skipped"; } export declare const DEDUP_SIMILARITY_THRESHOLD = 0.85; export declare const TOKEN_OVERLAP_THRESHOLD = 0.6; export declare const TOKEN_OVERLAP_ISSUE_PREFIX_THRESHOLD = 0.4; /** * Check if new thread text is a semantic duplicate of any existing open thread. * * @param newText - Trimmed thread text * @param newEmbedding - Normalized embedding vector, or null if unavailable * @param existingThreads - Open threads with optional embeddings */ export declare function checkDuplicate(newText: string, newEmbedding: number[] | null, existingThreads: ThreadWithEmbedding[]): DedupResult; /** * Cosine similarity between two normalized vectors. * Assumes vectors are already L2-normalized (dot product = cosine similarity). */ export declare function cosineSimilarity(a: number[], b: number[]): number; /** * Normalize text for conservative text-only comparison. * Lowercase, collapse whitespace, trim, strip trailing punctuation. */ export declare function normalizeText(text: string): string; /** * Tokenize text into content words for overlap comparison. * Lowercase, split on non-alphanumeric boundaries, remove stop words. */ export declare function tokenize(text: string): Set; /** * Overlap coefficient: |intersection| / min(|A|, |B|). * Handles the common case where one thread is a shorter variant of another. * Returns 0 if either set is empty. */ export declare function tokenOverlap(a: Set, b: Set): number; /** * Extract issue prefix like "OD-692" or "PROJ-123" from thread text. * Returns null if no prefix found. */ export declare function extractIssuePrefix(text: string): string | null; /** * Deduplicate a thread list by ID, normalized text, and token overlap. * First-seen wins. Skips empty-text threads. Does not mutate input. * * Applied at every thread loading/merging exit point to guarantee * no duplicates escape regardless of upstream logic. */ export declare function deduplicateThreadList(threads: ThreadObject[]): ThreadObject[]; //# sourceMappingURL=thread-dedup.d.ts.map