/** * DatabaseManager: Single long-lived database connection * * Responsibility: * - Manage single database connection for worker lifetime * - Provide centralized access to SessionStore and SessionSearch * - High-level database operations * - ChromaSync integration */ import { SessionStore } from '../sqlite/SessionStore.js'; import { SessionSearch } from '../sqlite/SessionSearch.js'; import { ChromaSync } from '../sync/ChromaSync.js'; import { logger } from '../../utils/logger.js'; import type { DBSession } from '../worker-types.js'; export class DatabaseManager { private sessionStore: SessionStore | null = null; private sessionSearch: SessionSearch | null = null; private chromaSync: ChromaSync | null = null; /** * Initialize database connection (once, stays open) */ async initialize(): Promise { // Open database connection (ONCE) this.sessionStore = new SessionStore(); this.sessionSearch = new SessionSearch(); // Initialize ChromaSync this.chromaSync = new ChromaSync('claude-mem'); // Start background backfill (fire-and-forget) this.chromaSync.ensureBackfilled().catch(error => { logger.error('DB', 'Chroma backfill failed (non-fatal)', {}, error); }); logger.info('DB', 'Database initialized'); } /** * Close database connection and cleanup all resources */ async close(): Promise { // Close ChromaSync first (terminates uvx/python processes) if (this.chromaSync) { await this.chromaSync.close(); this.chromaSync = null; } if (this.sessionStore) { this.sessionStore.close(); this.sessionStore = null; } if (this.sessionSearch) { this.sessionSearch.close(); this.sessionSearch = null; } logger.info('DB', 'Database closed'); } /** * Get SessionStore instance (throws if not initialized) */ getSessionStore(): SessionStore { if (!this.sessionStore) { throw new Error('Database not initialized'); } return this.sessionStore; } /** * Get SessionSearch instance (throws if not initialized) */ getSessionSearch(): SessionSearch { if (!this.sessionSearch) { throw new Error('Database not initialized'); } return this.sessionSearch; } /** * Get ChromaSync instance (throws if not initialized) */ getChromaSync(): ChromaSync { if (!this.chromaSync) { throw new Error('ChromaSync not initialized'); } return this.chromaSync; } // REMOVED: cleanupOrphanedSessions - violates "EVERYTHING SHOULD SAVE ALWAYS" // Worker restarts don't make sessions orphaned. Sessions are managed by hooks // and exist independently of worker state. /** * Get session by ID (throws if not found) */ getSessionById(sessionDbId: number): { id: number; claude_session_id: string; sdk_session_id: string | null; project: string; user_prompt: string; } { const session = this.getSessionStore().getSessionById(sessionDbId); if (!session) { throw new Error(`Session ${sessionDbId} not found`); } return session; } }