-- Chat Switchboard — 005 Channels & Conversations (SQLite) CREATE TABLE IF NOT EXISTS folders ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, name TEXT NOT NULL, parent_id TEXT REFERENCES folders(id) ON DELETE CASCADE, sort_order INTEGER DEFAULT 0, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), UNIQUE(user_id, name, parent_id) ); CREATE INDEX IF NOT EXISTS idx_folders_user ON folders(user_id); CREATE INDEX IF NOT EXISTS idx_folders_parent ON folders(parent_id); CREATE TABLE IF NOT EXISTS channels ( id TEXT PRIMARY KEY, user_id TEXT REFERENCES users(id) ON DELETE CASCADE, title TEXT NOT NULL, description TEXT, type TEXT DEFAULT 'direct' CHECK (type IN ('direct', 'group', 'workflow')), model TEXT, system_prompt TEXT, provider_config_id TEXT REFERENCES provider_configs(id) ON DELETE SET NULL, is_archived INTEGER DEFAULT 0, is_pinned INTEGER DEFAULT 0, folder_id TEXT REFERENCES folders(id) ON DELETE SET NULL, folder TEXT, team_id TEXT REFERENCES teams(id) ON DELETE SET NULL, settings TEXT DEFAULT '{}', tags TEXT DEFAULT '[]', project_id TEXT, workspace_id TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_channels_user ON channels(user_id); CREATE INDEX IF NOT EXISTS idx_channels_updated ON channels(updated_at); CREATE INDEX IF NOT EXISTS idx_channels_type ON channels(type); CREATE INDEX IF NOT EXISTS idx_channels_team ON channels(team_id); CREATE INDEX IF NOT EXISTS idx_channels_folder ON channels(folder_id); CREATE INDEX IF NOT EXISTS idx_channels_project ON channels(project_id); CREATE INDEX IF NOT EXISTS idx_channels_workspace ON channels(workspace_id); CREATE TABLE IF NOT EXISTS messages ( id TEXT PRIMARY KEY, channel_id TEXT NOT NULL REFERENCES channels(id) ON DELETE CASCADE, role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system', 'tool')), content TEXT NOT NULL, model TEXT, tokens_used INTEGER, tool_calls TEXT, metadata TEXT DEFAULT '{}', parent_id TEXT REFERENCES messages(id) ON DELETE SET NULL, sibling_index INTEGER DEFAULT 0, participant_type TEXT DEFAULT 'user', participant_id TEXT, provider_config_id TEXT REFERENCES provider_configs(id) ON DELETE SET NULL, deleted_at TEXT, created_at TEXT DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_messages_channel ON messages(channel_id, created_at); CREATE INDEX IF NOT EXISTS idx_messages_parent ON messages(parent_id); CREATE TABLE IF NOT EXISTS channel_participants ( id TEXT PRIMARY KEY, channel_id TEXT NOT NULL REFERENCES channels(id) ON DELETE CASCADE, participant_type TEXT NOT NULL DEFAULT 'user' CHECK (participant_type IN ('user', 'persona', 'session')), participant_id TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'member' CHECK (role IN ('owner', 'member', 'observer')), display_name TEXT, avatar_url TEXT, joined_at TEXT DEFAULT (datetime('now')), last_read_at TEXT DEFAULT (datetime('now')), UNIQUE(channel_id, participant_type, participant_id) ); CREATE INDEX IF NOT EXISTS idx_channel_participants_channel ON channel_participants(channel_id); CREATE INDEX IF NOT EXISTS idx_channel_participants_ref ON channel_participants(participant_type, participant_id); CREATE TABLE IF NOT EXISTS channel_models ( id TEXT PRIMARY KEY, channel_id TEXT NOT NULL REFERENCES channels(id) ON DELETE CASCADE, model_id TEXT NOT NULL, provider_config_id TEXT REFERENCES provider_configs(id) ON DELETE SET NULL, persona_id TEXT REFERENCES personas(id) ON DELETE SET NULL, display_name TEXT, system_prompt TEXT, settings TEXT DEFAULT '{}', is_default INTEGER DEFAULT 0, added_at TEXT DEFAULT (datetime('now')) ); -- Raw model entries (no persona): unique per channel+model+provider CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_models_raw ON channel_models(channel_id, model_id, provider_config_id) WHERE persona_id IS NULL; -- Persona entries: one per persona per channel CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_models_persona ON channel_models(channel_id, persona_id) WHERE persona_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_channel_models_channel ON channel_models(channel_id); CREATE TABLE IF NOT EXISTS channel_cursors ( id TEXT PRIMARY KEY, channel_id TEXT NOT NULL REFERENCES channels(id) ON DELETE CASCADE, user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, active_leaf_id TEXT REFERENCES messages(id) ON DELETE SET NULL, updated_at TEXT DEFAULT (datetime('now')), UNIQUE(channel_id, user_id) ); CREATE INDEX IF NOT EXISTS idx_channel_cursors_channel ON channel_cursors(channel_id); CREATE INDEX IF NOT EXISTS idx_channel_cursors_user ON channel_cursors(user_id); CREATE TABLE IF NOT EXISTS user_model_settings ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, model_id TEXT NOT NULL, provider_config_id TEXT REFERENCES provider_configs(id) ON DELETE CASCADE, hidden INTEGER DEFAULT 0, preferred_temperature REAL, preferred_max_tokens INTEGER, sort_order INTEGER DEFAULT 0, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), UNIQUE(user_id, model_id, provider_config_id) ); CREATE INDEX IF NOT EXISTS idx_user_model_settings_user ON user_model_settings(user_id);