"use client"; import { Streamdown } from "streamdown"; import { useI18n } from "@/core/i18n/hooks"; import { useMemory } from "@/core/memory/hooks"; import type { UserMemory } from "@/core/memory/types"; import { streamdownPlugins } from "@/core/streamdown/plugins"; import { pathOfThread } from "@/core/threads/utils"; import { formatTimeAgo } from "@/core/utils/datetime"; import { SettingsSection } from "./settings-section"; function confidenceToLevelKey(confidence: unknown): { key: "veryHigh" | "high" | "normal" | "unknown"; value?: number; } { if (typeof confidence !== "number" || !Number.isFinite(confidence)) { return { key: "unknown" }; } // Clamp to [0, 1] since confidence is expected to be a probability-like score. const value = Math.min(1, Math.max(0, confidence)); // 3 levels: // - veryHigh: [0.85, 1] // - high: [0.65, 0.85) // - normal: [0, 0.65) if (value >= 0.85) return { key: "veryHigh", value }; if (value >= 0.65) return { key: "high", value }; return { key: "normal", value }; } function formatMemorySection( title: string, summary: string, updatedAt: string | undefined, t: ReturnType["t"], ): string { const content = summary.trim() || `${t.settings.memory.markdown.empty}`; return [ `### ${title}`, content, "", updatedAt && `> ${t.settings.memory.markdown.updatedAt}: \`${formatTimeAgo(updatedAt)}\``, ] .filter(Boolean) .join("\n"); } function memoryToMarkdown( memory: UserMemory, t: ReturnType["t"], ) { const parts: string[] = []; parts.push(`## ${t.settings.memory.markdown.overview}`); parts.push( `- **${t.common.lastUpdated}**: \`${formatTimeAgo(memory.lastUpdated)}\``, ); parts.push(`\n## ${t.settings.memory.markdown.userContext}`); parts.push( formatMemorySection( t.settings.memory.markdown.work, memory.user.workContext.summary, memory.user.workContext.updatedAt, t, ), ); parts.push( formatMemorySection( t.settings.memory.markdown.personal, memory.user.personalContext.summary, memory.user.personalContext.updatedAt, t, ), ); parts.push( formatMemorySection( t.settings.memory.markdown.topOfMind, memory.user.topOfMind.summary, memory.user.topOfMind.updatedAt, t, ), ); parts.push(`\n## ${t.settings.memory.markdown.historyBackground}`); parts.push( formatMemorySection( t.settings.memory.markdown.recentMonths, memory.history.recentMonths.summary, memory.history.recentMonths.updatedAt, t, ), ); parts.push( formatMemorySection( t.settings.memory.markdown.earlierContext, memory.history.earlierContext.summary, memory.history.earlierContext.updatedAt, t, ), ); parts.push( formatMemorySection( t.settings.memory.markdown.longTermBackground, memory.history.longTermBackground.summary, memory.history.longTermBackground.updatedAt, t, ), ); parts.push(`\n## ${t.settings.memory.markdown.facts}`); if (memory.facts.length === 0) { parts.push( `${t.settings.memory.markdown.empty}`, ); } else { parts.push( [ `| ${t.settings.memory.markdown.table.category} | ${t.settings.memory.markdown.table.confidence} | ${t.settings.memory.markdown.table.content} | ${t.settings.memory.markdown.table.source} | ${t.settings.memory.markdown.table.createdAt} |`, "|---|---|---|---|---|", ...memory.facts.map((f) => { const { key, value } = confidenceToLevelKey(f.confidence); const levelLabel = t.settings.memory.markdown.table.confidenceLevel[key]; const confidenceText = typeof value === "number" ? `${levelLabel}` : levelLabel; return `| ${upperFirst(f.category)} | ${confidenceText} | ${f.content} | [${t.settings.memory.markdown.table.view}](${pathOfThread(f.source)}) | ${formatTimeAgo(f.createdAt)} |`; }), ].join("\n"), ); } const markdown = parts.join("\n\n"); // Ensure every level-2 heading (##) is preceded by a horizontal rule. const lines = markdown.split("\n"); const out: string[] = []; let i = 0; for (const line of lines) { i++; if (i !== 1 && line.startsWith("## ")) { if (out.length === 0 || out[out.length - 1] !== "---") { out.push("---"); } } out.push(line); } return out.join("\n"); } export function MemorySettingsPage() { const { t } = useI18n(); const { memory, isLoading, error } = useMemory(); return ( {isLoading ? (
{t.common.loading}
) : error ? (
Error: {error.message}
) : !memory ? (
{t.settings.memory.empty}
) : (
{memoryToMarkdown(memory, t)}
)}
); } function upperFirst(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); }