export type FactCategory = 'fact' | 'preference' | 'goal' | 'history' | 'profile'; export interface Fact { id: number; user_key: string; what: string; who: string; when_text: string; where_label: string; why: string; category: FactCategory; confidence: number; last_referenced_at: number; source: string; created_at: number; } export interface SaveFactInput { user_key: string; what: string; who?: string; when_text?: string; where_label?: string; why?: string; category?: FactCategory; confidence?: number; source?: string; } export declare function userKey(platform: string, userId: string): string; export declare function saveFact(input: SaveFactInput): number | null; /** * Exact-match lookup of an existing fact's id by its `what` slot, scoped to * one user. Match is case- and surrounding-whitespace-insensitive. Used by * distillation to avoid re-inserting a fact it already learned in an earlier * turn (the append-only "user lives in 杭州 ×3" problem). Returns the oldest * matching id (so reinforcement consistently touches the same row), or null. */ export declare function findFactIdByWhat(user_key: string, what: string): number | null; /** * Reinforcement touch: bump `last_referenced_at` so a re-confirmed fact * survives LRU retention without inserting a duplicate row. Best-effort * (never throws). Mirrors how queryFacts keeps recalled facts fresh. */ export declare function touchFact(id: number): void; export declare function deleteFact(id: number, user_key: string): boolean; export interface QueryOpts { user_key: string; query?: string; category?: FactCategory; k?: number; } export interface QueryResult { facts: Fact[]; matched: number; } /** * Hybrid retrieval. v1.5 first cut uses FTS5 + recency boost; v1.6 will * layer in sqlite-vss dense vectors and rerank. * * - Empty query → most-recent N facts. * - With query → FTS5 MATCH, sorted by FTS5's bm25() with a recency tie- * breaker (newer first). * * Side effect: bumps `last_referenced_at` for returned rows so weak / * unused facts can be retired by future consolidation. */ export declare function queryFacts(opts: QueryOpts): QueryResult; /** * v1.6 — async hybrid retrieval. When a vector backend is ready, run FTS5 * + vector top-K in parallel, fuse with reciprocal-rank-fusion, and return * the merged top-K. Falls back to plain queryFacts when backend is off or * the embedding model isn't loaded. * * Use this from memory-rpc.handleMemoryOp('query') so MCP callers get * better recall automatically without changing the call site. */ export declare function queryFactsHybrid(opts: QueryOpts): Promise; /** v1.6 — backfill embeddings for facts that don't have one (e.g. existed * before vector backend was enabled). Returns counts; safe to call again. * Stops early on backend errors (returns partial). */ export declare function backfillEmbeddings(opts: { user_key?: string; batchSize?: number; maxRows?: number; }): Promise<{ processed: number; succeeded: number; failed: number; errored?: string; }>; export interface VectorCoverage { total: number; withEmbedding: number; withDifferentModel: number; } export declare function getVectorCoverage(user_key?: string): VectorCoverage; /** Clear all embedding fields for a user (or all). Useful when switching * backends — embeddings from different models can't be compared. */ export declare function clearEmbeddings(user_key?: string): number; export declare function listRecentFacts(user_key: string, limit?: number): Fact[]; export declare function countFacts(user_key: string): number; export interface PersonaProfile { user_key: string; summary: string; updated_at: number; } export declare function getPersona(user_key: string): PersonaProfile | null; export declare function upsertPersona(user_key: string, summary: string): boolean; export interface UserSummary { user_key: string; fact_count: number; oldest_fact_at: number | null; newest_fact_at: number | null; has_persona: boolean; persona_updated_at: number | null; } /** Enumerate every user_key the memory system has touched, with quick stats. * Used by /api/memory/users to populate the admin selector. */ export declare function listUsers(): UserSummary[]; export interface FactPage { total: number; limit: number; offset: number; facts: Fact[]; } /** Paged fact listing with optional filters. Mirrors queryFacts but adds * offset + total count for table pagination. */ export declare function listFacts(opts: { user_key: string; query?: string; category?: FactCategory; limit?: number; offset?: number; }): FactPage; /** Bulk delete by user + optional filters. Returns row count actually deleted. * Defaults are intentionally inert ({user_key} alone deletes ALL facts for * that user) — caller must explicitly opt in to clear-all by passing * confirm_clear: true. Otherwise needs at least one filter. */ export declare function bulkDeleteFacts(opts: { user_key: string; ids?: number[]; category?: FactCategory; max_confidence?: number; confirm_clear?: boolean; }): number; export declare function deletePersona(user_key: string): boolean; /** Full export: persona + all facts for one user, JSON-safe shape. */ export declare function exportUserMemory(user_key: string): { user_key: string; persona: PersonaProfile | null; facts: Fact[]; exported_at: string; }; /** Public retention API — useful for /api/memory/prune endpoint + tests. */ export declare function pruneExpiredFacts(): number; export declare function pruneExpiredPersona(): number; export interface PendingTurnInput { user_key: string; platform: string; user_message: string; agent_reply: string; agent_name?: string; trace_id?: string; } export interface PendingTurn { id: number; user_key: string; platform: string; user_message: string; agent_reply: string; agent_name: string; trace_id: string; created_at: number; } /** Buffer one (user msg, agent reply) turn for later batch distillation. * Best-effort (a write failure / disabled sqlite is swallowed). Enforces a * per-user cap by dropping the oldest rows beyond `maxPerUser` (backpressure). */ export declare function enqueuePendingTurn(input: PendingTurnInput, maxPerUser?: number): void; /** Distinct user_keys with buffered turns. When `olderThanSec` is set, only * users whose OLDEST pending turn is at least that old (the staleness floor). */ export declare function listPendingUserKeys(opts?: { olderThanSec?: number; }): string[]; /** Oldest-first buffered turns for a user, capped at `limit`. */ export declare function listPendingForUser(user_key: string, limit: number): PendingTurn[]; export declare function deletePendingByIds(ids: number[]): void; export declare function countPendingTotal(): number; /** Stop the periodic retention sweep (tests + graceful shutdown). */ export declare function stopMemoryRetentionSweep(): void; export declare function closeMemoryDb(): void; export declare const MEMORY_DB_PATH: string; //# sourceMappingURL=memory.d.ts.map