import { MemoryRecord, MemoryState } from '@celiums/memory-types'; /** * packages/core/src/store.ts * * The HIPPOCAMPUS — Central memory store. * Handles encoding, storage, retrieval, and lifecycle management * across PostgreSQL (long-term), Qdrant (semantic), and Valkey (working memory). */ interface StoreConfig { postgres: { host: string; port: number; database: string; user: string; password: string; max?: number; ssl?: boolean; }; qdrant: { url: string; apiKey?: string; collectionName?: string; }; valkey: { host: string; port: number; password?: string; db?: number; keyPrefix?: string; }; embedding: { endpoint: string; apiKey?: string; model?: string; dimensions?: number; }; } declare class MemoryStore { private pg; private qdrant; private redis; private config; private collectionName; private valkeyPrefix; private vectorDimensions; constructor(config: StoreConfig); initialize(): Promise; saveMemory(memory: MemoryRecord): Promise; getMemory(id: string): Promise; searchByImportance(userId: string, minImportance: number, limit?: number): Promise; deleteMemories(ids: string[]): Promise; updateLifecycle(id: string, importance: number, state: MemoryState): Promise; reactivate(id: string): Promise; getForLifecycle(userId: string, batchSize?: number): Promise; health(): Promise<{ postgres: boolean; qdrant: boolean; valkey: boolean; overall: boolean; }>; embed(text: string): Promise; /** * Deterministic word-level embedding fallback. * Same algorithm as InMemoryMemoryStore — similar texts produce * similar vectors via word hashing with TF-like weighting. * Not as good as a real embedding model, but keeps the system * operational when the embedding service is down. */ private deterministicEmbed; getUserProfile(userId: string): Promise<{ id: string; externalId: string; timezone: string; communicationStyle: string | null; preferences: Record; knownPatterns: string[]; sleepSchedule: { start: string; end: string; }; lastActiveAt: Date; } | null>; getProjectContext(projectId: string): Promise<{ id: string; userId: string; name: string; description: string | null; techStack: string[]; conventions: string[]; currentGoals: string[]; recentDecisions: string[]; isActive: boolean; } | null>; semanticSearch(vector: number[], userId: string, projectId: string | null, limit?: number, scoreThreshold?: number): Promise>; fullTextSearch(query: string, userId: string, projectId: string | null, limit?: number): Promise>; getMemoriesByIds(ids: string[]): Promise; getRecentSessionMemories(userId: string, sessionId: string, limit?: number): Promise; findSimilarMemories(vector: number[], userId: string, threshold?: number, limit?: number): Promise>; updateMemory(id: string, updates: Partial>): Promise; updateSession(sessionId: string, updates: { endedAt?: Date; isConsolidated?: boolean; consolidationStartedAt?: Date; consolidationCompletedAt?: Date; summary?: string; messageCount?: number; }): Promise; bulkUpdateLifecycle(updates: Array<{ id: string; importance: number; strength: number; state: MemoryState; }>): Promise; /** * Load a user's circadian profile + persisted PAD/factors. * Returns null if the user has no row yet. */ loadCircadianProfile(userId: string): Promise<{ userId: string; circadian: { timezoneIana: string; timezoneOffset: number; peakHour: number; amplitude: number; baseArousal: number; lethargyRate: number; hemisphere: 1 | -1; seasonalAmplitude: number; }; pad: { pleasure: number; arousal: number; dominance: number; }; factors: { sessionActivity: number; stressLevel: number; caffeineLevel: number; sleepDebt: number; cognitiveLoad: number; emotionalAccumulator: number; exerciseLevel: number; motivationTrend: number; }; lastInteraction: Date; interactionCount: number; communicationStyle: string; preferences: Record; knownPatterns: string[]; createdAt: Date; updatedAt: Date; } | null>; /** * Get-or-create. If the profile doesn't exist, insert with sane defaults * (UTC timezone, peakHour 11) and return it. */ ensureCircadianProfile(userId: string): Promise<{ userId: string; circadian: { timezoneIana: string; timezoneOffset: number; peakHour: number; amplitude: number; baseArousal: number; lethargyRate: number; hemisphere: 1 | -1; seasonalAmplitude: number; }; pad: { pleasure: number; arousal: number; dominance: number; }; factors: { sessionActivity: number; stressLevel: number; caffeineLevel: number; sleepDebt: number; cognitiveLoad: number; emotionalAccumulator: number; exerciseLevel: number; motivationTrend: number; }; lastInteraction: Date; interactionCount: number; communicationStyle: string; preferences: Record; knownPatterns: string[]; createdAt: Date; updatedAt: Date; }>; /** * Update circadian config (timezone, peakHour, amplitude, etc.). * Only the fields present in `patch` are touched. Returns the updated row. */ updateCircadianConfig(userId: string, patch: Partial<{ timezoneIana: string; timezoneOffset: number; peakHour: number; amplitude: number; baseArousal: number; lethargyRate: number; hemisphere: 1 | -1; seasonalAmplitude: number; }>): Promise<{ userId: string; circadian: { timezoneIana: string; timezoneOffset: number; peakHour: number; amplitude: number; baseArousal: number; lethargyRate: number; hemisphere: 1 | -1; seasonalAmplitude: number; }; pad: { pleasure: number; arousal: number; dominance: number; }; factors: { sessionActivity: number; stressLevel: number; caffeineLevel: number; sleepDebt: number; cognitiveLoad: number; emotionalAccumulator: number; exerciseLevel: number; motivationTrend: number; }; lastInteraction: Date; interactionCount: number; communicationStyle: string; preferences: Record; knownPatterns: string[]; createdAt: Date; updatedAt: Date; }>; /** * Persist a user's PAD vector after the limbic engine processes an event. * Also bumps last_interaction and interaction_count. */ persistUserPad(userId: string, pad: { pleasure: number; arousal: number; dominance: number; }): Promise; /** * Persist updated factor accumulators (caffeine, stress, etc.). */ persistUserFactors(userId: string, factors: { sessionActivity: number; stressLevel: number; caffeineLevel: number; sleepDebt: number; cognitiveLoad: number; emotionalAccumulator: number; exerciseLevel: number; motivationTrend: number; }): Promise; /** * Lightweight last_interaction bump (no PAD change). Used by /recall when * we want to count the user as active without altering emotional state. */ touchUserInteraction(userId: string): Promise; shutdown(): Promise; } export { MemoryStore, type StoreConfig };