/** * Relevance decay for palace memories. * * Uses exponential half-life decay: relevance halves every N days. * Memories that are accessed/referenced get their relevance boosted. */ const MS_PER_DAY = 86_400_000; /** * Compute decayed relevance using exponential half-life. * * @param originalRelevance - Initial relevance score (0-1) * @param createdAt - ISO date string or Date when the memory was created/last accessed * @param halfLifeDays - Number of days for relevance to halve (default: 30) * @param asOf - Reference time for decay calculation (default: now) * @returns Decayed relevance score (0-1) */ export function computeDecayedRelevance( originalRelevance: number, createdAt: string | Date, halfLifeDays = 30, asOf: Date = new Date() ): number { if (halfLifeDays <= 0) return originalRelevance; if (originalRelevance <= 0) return 0; const created = typeof createdAt === 'string' ? new Date(createdAt) : createdAt; const elapsedMs = asOf.getTime() - created.getTime(); if (elapsedMs <= 0) return Math.min(originalRelevance, 1); const elapsedDays = elapsedMs / MS_PER_DAY; const decayFactor = Math.pow(0.5, elapsedDays / halfLifeDays); return Math.max(0, Math.min(1, originalRelevance * decayFactor)); } /** * Compute a boosted relevance for a memory that was accessed/referenced. * * Boost formula: new = old + (1 - old) * boostFactor * This asymptotically approaches 1.0 without exceeding it. * * @param currentRelevance - Current relevance score (0-1) * @param boostFactor - How much of the remaining gap to close (default: 0.3) * @returns Boosted relevance score (0-1) */ export function boostRelevance(currentRelevance: number, boostFactor = 0.3): number { const boosted = currentRelevance + (1 - currentRelevance) * boostFactor; return Math.min(1, Math.max(0, boosted)); }