"""Database migrations — schema versioning.""" SCHEMA_VERSION = 1 MIGRATIONS = { 1: """ CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, username TEXT UNIQUE NOT NULL, display_name TEXT, email TEXT, avatar_url TEXT, preferences TEXT DEFAULT '{}', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS conversations ( id TEXT PRIMARY KEY, user_id TEXT, title TEXT DEFAULT 'New Chat', folder_id TEXT, personality_id TEXT DEFAULT 'synapse', mode TEXT DEFAULT 'agent', is_pinned INTEGER DEFAULT 0, is_bookmarked INTEGER DEFAULT 0, summary TEXT, metadata TEXT DEFAULT '{}', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS messages ( id TEXT PRIMARY KEY, conversation_id TEXT NOT NULL, role TEXT NOT NULL CHECK(role IN ('user','assistant','system','tool')), content TEXT NOT NULL, reasoning TEXT, tool_calls TEXT, tool_results TEXT, model TEXT, tokens_used INTEGER DEFAULT 0, metadata TEXT DEFAULT '{}', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS folders ( id TEXT PRIMARY KEY, user_id TEXT, name TEXT NOT NULL, parent_id TEXT, color TEXT DEFAULT '#6366f1', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS memories ( id TEXT PRIMARY KEY, user_id TEXT, conversation_id TEXT, content TEXT NOT NULL, memory_type TEXT DEFAULT 'long_term', importance REAL DEFAULT 0.5, embedding BLOB, is_pinned INTEGER DEFAULT 0, tags TEXT DEFAULT '[]', metadata TEXT DEFAULT '{}', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, accessed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS user_facts ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL, fact TEXT NOT NULL, category TEXT DEFAULT 'general', confidence REAL DEFAULT 1.0, source TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS personalities ( id TEXT PRIMARY KEY, name TEXT NOT NULL, system_prompt TEXT NOT NULL, description TEXT, mood TEXT DEFAULT 'neutral', humor_level REAL DEFAULT 0.5, creativity_level REAL DEFAULT 0.7, is_default INTEGER DEFAULT 0, avatar_url TEXT, metadata TEXT DEFAULT '{}', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS chat_history ( id TEXT PRIMARY KEY, user_id TEXT, conversation_id TEXT NOT NULL, action TEXT NOT NULL, old_value TEXT, new_value TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS plugins ( id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT, version TEXT DEFAULT '1.0.0', author TEXT, enabled INTEGER DEFAULT 1, config TEXT DEFAULT '{}', tools TEXT DEFAULT '[]', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS generated_files ( id TEXT PRIMARY KEY, user_id TEXT, file_type TEXT NOT NULL, file_path TEXT NOT NULL, original_prompt TEXT, metadata TEXT DEFAULT '{}', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS sessions ( id TEXT PRIMARY KEY, user_id TEXT, token TEXT UNIQUE NOT NULL, ip_address TEXT, user_agent TEXT, expires_at TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS settings ( user_id TEXT, key TEXT NOT NULL, value TEXT, PRIMARY KEY (user_id, key), FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS migrations ( version INTEGER PRIMARY KEY, applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(conversation_id); CREATE INDEX IF NOT EXISTS idx_messages_role ON messages(role); CREATE INDEX IF NOT EXISTS idx_conversations_user ON conversations(user_id); CREATE INDEX IF NOT EXISTS idx_memories_user ON memories(user_id); CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(memory_type); CREATE INDEX IF NOT EXISTS idx_generated_files_user ON generated_files(user_id); CREATE INDEX IF NOT EXISTS idx_chat_history_conversation ON chat_history(conversation_id); """, } async def run_migrations(db): await db.execute(""" CREATE TABLE IF NOT EXISTS migrations ( version INTEGER PRIMARY KEY, applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) """) await db.commit() cursor = await db.execute( "SELECT version FROM migrations ORDER BY version DESC LIMIT 1" ) row = await cursor.fetchone() current_version = row[0] if row else 0 for version, sql in sorted(MIGRATIONS.items()): if version > current_version: await db.executescript(sql) await db.execute( "INSERT INTO migrations (version) VALUES (?)", (version,) ) await db.commit()