Changeset 0.23.0 (#153)
This commit is contained in:
@@ -30,8 +30,12 @@ CREATE TABLE IF NOT EXISTS provider_configs (
|
||||
settings JSONB DEFAULT '{}'::jsonb,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
is_private BOOLEAN DEFAULT false,
|
||||
proxy_mode TEXT NOT NULL DEFAULT 'system'
|
||||
CHECK (proxy_mode IN ('system', 'direct', 'custom')),
|
||||
proxy_url TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
CONSTRAINT chk_proxy_url CHECK (proxy_mode != 'custom' OR proxy_url IS NOT NULL)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_provider_configs_scope ON provider_configs(scope);
|
||||
@@ -49,6 +53,8 @@ COMMENT ON COLUMN provider_configs.key_scope IS 'Encryption tier: global/team us
|
||||
COMMENT ON COLUMN provider_configs.headers IS 'Custom HTTP headers (e.g. OpenRouter HTTP-Referer)';
|
||||
COMMENT ON COLUMN provider_configs.settings IS 'Provider-specific params (e.g. Venice web_search, OpenRouter route)';
|
||||
COMMENT ON COLUMN provider_configs.is_private IS 'Data stays on-prem (local/self-hosted provider)';
|
||||
COMMENT ON COLUMN provider_configs.proxy_mode IS 'system=env proxy, direct=no proxy, custom=explicit URL';
|
||||
COMMENT ON COLUMN provider_configs.proxy_url IS 'Explicit proxy URL when proxy_mode=custom (http/https/socks5)';
|
||||
|
||||
|
||||
-- =========================================
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
CREATE TABLE IF NOT EXISTS personas (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(100) NOT NULL,
|
||||
handle VARCHAR(50),
|
||||
description TEXT DEFAULT '',
|
||||
icon VARCHAR(10) DEFAULT '',
|
||||
avatar TEXT DEFAULT '',
|
||||
@@ -48,17 +49,54 @@ CREATE TABLE IF NOT EXISTS personas (
|
||||
CREATE INDEX IF NOT EXISTS idx_personas_scope ON personas(scope);
|
||||
CREATE INDEX IF NOT EXISTS idx_personas_owner ON personas(owner_id) WHERE owner_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_personas_active ON personas(is_active) WHERE is_active = true;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_personas_handle ON personas(handle) WHERE handle IS NOT NULL;
|
||||
DROP TRIGGER IF EXISTS personas_updated_at ON personas;
|
||||
CREATE TRIGGER personas_updated_at BEFORE UPDATE ON personas
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
COMMENT ON COLUMN personas.scope IS 'global=all users, team=team members, personal=creator only';
|
||||
COMMENT ON COLUMN personas.owner_id IS 'NULL for global; teams.id for team scope; users.id for personal';
|
||||
COMMENT ON COLUMN personas.handle IS 'Unique @mention handle, e.g. veronica-sharpe. Auto-generated from name.';
|
||||
COMMENT ON COLUMN personas.is_shared IS 'Personal personas shared with others (read-only)';
|
||||
COMMENT ON COLUMN personas.memory_enabled IS 'Whether memory tools and extraction are active for this persona';
|
||||
COMMENT ON COLUMN personas.memory_extraction_prompt IS 'Custom extraction prompt; NULL = use system default';
|
||||
|
||||
|
||||
-- =========================================
|
||||
-- PERSONA GROUPS (v0.23.0)
|
||||
-- =========================================
|
||||
-- Saved roster templates. A group is a named set of personas
|
||||
-- that can be stamped onto any new conversation.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS persona_groups (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
owner_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
scope VARCHAR(10) NOT NULL DEFAULT 'personal'
|
||||
CHECK (scope IN ('global', 'team', 'personal')),
|
||||
team_id UUID REFERENCES teams(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_persona_groups_owner ON persona_groups(owner_id);
|
||||
DROP TRIGGER IF EXISTS persona_groups_updated_at ON persona_groups;
|
||||
CREATE TRIGGER persona_groups_updated_at BEFORE UPDATE ON persona_groups
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
CREATE TABLE IF NOT EXISTS persona_group_members (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
group_id UUID NOT NULL REFERENCES persona_groups(id) ON DELETE CASCADE,
|
||||
persona_id UUID NOT NULL REFERENCES personas(id) ON DELETE CASCADE,
|
||||
is_leader BOOLEAN NOT NULL DEFAULT false,
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
UNIQUE(group_id, persona_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_persona_group_members_group ON persona_group_members(group_id);
|
||||
|
||||
|
||||
-- =========================================
|
||||
-- PERSONA GRANTS (what a persona can do)
|
||||
-- =========================================
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
-- ==========================================
|
||||
-- Chat Switchboard — 005 Channels & Conversations
|
||||
-- ==========================================
|
||||
-- Channels, messages, members, models, cursors, folders, user prefs.
|
||||
-- Channels, messages, participants, models, cursors, folders, user prefs.
|
||||
-- ICD §3 (Channels & Conversations)
|
||||
-- Note: project_id and workspace_id FKs added by 010_projects.sql
|
||||
-- ==========================================
|
||||
@@ -38,7 +38,7 @@ CREATE TABLE IF NOT EXISTS channels (
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT,
|
||||
type VARCHAR(20) DEFAULT 'direct'
|
||||
CHECK (type IN ('direct', 'group', 'channel')),
|
||||
CHECK (type IN ('direct', 'group', 'workflow')),
|
||||
model VARCHAR(100),
|
||||
system_prompt TEXT,
|
||||
provider_config_id UUID REFERENCES provider_configs(id) ON DELETE SET NULL,
|
||||
@@ -73,7 +73,7 @@ DO $$ BEGIN
|
||||
END $$;
|
||||
CREATE INDEX IF NOT EXISTS idx_channels_folder ON channels(folder_id) WHERE folder_id IS NOT NULL;
|
||||
|
||||
COMMENT ON COLUMN channels.type IS 'direct=1:1 AI chat, group=multi-model, channel=named persistent';
|
||||
COMMENT ON COLUMN channels.type IS 'direct=single-user chat, group=multi-user/multi-model, workflow=staged channel';
|
||||
|
||||
|
||||
-- =========================================
|
||||
@@ -94,6 +94,7 @@ CREATE TABLE IF NOT EXISTS messages (
|
||||
sibling_index INTEGER DEFAULT 0,
|
||||
participant_type VARCHAR(10) DEFAULT 'user',
|
||||
participant_id VARCHAR(255),
|
||||
provider_config_id UUID REFERENCES provider_configs(id) ON DELETE SET NULL,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
@@ -110,35 +111,53 @@ COMMENT ON COLUMN messages.sibling_index IS 'Position among siblings (0-indexed)
|
||||
|
||||
|
||||
-- =========================================
|
||||
-- CHANNEL MEMBERS & MODELS
|
||||
-- CHANNEL PARTICIPANTS & MODELS
|
||||
-- =========================================
|
||||
-- ICD §3.7: Polymorphic participant system.
|
||||
-- participant_type determines what participant_id references:
|
||||
-- user → users.id
|
||||
-- persona → personas.id
|
||||
-- session → opaque session token
|
||||
-- =========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS channel_members (
|
||||
CREATE TABLE IF NOT EXISTS channel_participants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
channel_id UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
role VARCHAR(20) DEFAULT 'member',
|
||||
participant_type VARCHAR(10) NOT NULL DEFAULT 'user'
|
||||
CHECK (participant_type IN ('user', 'persona', 'session')),
|
||||
participant_id UUID NOT NULL,
|
||||
role VARCHAR(10) NOT NULL DEFAULT 'member'
|
||||
CHECK (role IN ('owner', 'member', 'observer')),
|
||||
display_name VARCHAR(200),
|
||||
avatar_url TEXT,
|
||||
joined_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
last_read_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(channel_id, user_id)
|
||||
UNIQUE(channel_id, participant_type, participant_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_members_channel ON channel_members(channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_members_user ON channel_members(user_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 UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
channel_id UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
||||
model_id VARCHAR(255) NOT NULL,
|
||||
provider_config_id UUID REFERENCES provider_configs(id) ON DELETE SET NULL,
|
||||
persona_id UUID REFERENCES personas(id) ON DELETE SET NULL,
|
||||
display_name VARCHAR(100),
|
||||
system_prompt TEXT,
|
||||
settings JSONB DEFAULT '{}'::jsonb,
|
||||
is_default BOOLEAN DEFAULT false,
|
||||
added_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(channel_id, model_id)
|
||||
added_at TIMESTAMPTZ DEFAULT 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 (same model allowed for different personas)
|
||||
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);
|
||||
|
||||
|
||||
@@ -167,13 +186,14 @@ CREATE TABLE IF NOT EXISTS user_model_settings (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
model_id TEXT NOT NULL,
|
||||
provider_config_id UUID REFERENCES provider_configs(id) ON DELETE CASCADE,
|
||||
hidden BOOLEAN DEFAULT false,
|
||||
preferred_temperature FLOAT,
|
||||
preferred_max_tokens INT,
|
||||
sort_order INT DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(user_id, model_id)
|
||||
UNIQUE(user_id, model_id, provider_config_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_user_model_settings_user ON user_model_settings(user_id);
|
||||
|
||||
@@ -16,8 +16,11 @@ CREATE TABLE IF NOT EXISTS provider_configs (
|
||||
settings TEXT DEFAULT '{}',
|
||||
is_active INTEGER DEFAULT 1,
|
||||
is_private INTEGER DEFAULT 0,
|
||||
proxy_mode TEXT NOT NULL DEFAULT 'system' CHECK (proxy_mode IN ('system', 'direct', 'custom')),
|
||||
proxy_url TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
CHECK (proxy_mode != 'custom' OR proxy_url IS NOT NULL)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_provider_configs_scope ON provider_configs(scope);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
CREATE TABLE IF NOT EXISTS personas (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
handle TEXT,
|
||||
description TEXT DEFAULT '',
|
||||
icon TEXT DEFAULT '',
|
||||
avatar TEXT DEFAULT '',
|
||||
@@ -27,6 +28,31 @@ CREATE TABLE IF NOT EXISTS personas (
|
||||
CREATE INDEX IF NOT EXISTS idx_personas_scope ON personas(scope);
|
||||
CREATE INDEX IF NOT EXISTS idx_personas_owner ON personas(owner_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_personas_active ON personas(is_active);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_personas_handle ON personas(handle) WHERE handle IS NOT NULL;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS persona_groups (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
owner_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
scope TEXT NOT NULL DEFAULT 'personal' CHECK (scope IN ('global', 'team', 'personal')),
|
||||
team_id TEXT REFERENCES teams(id) ON DELETE CASCADE,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_persona_groups_owner ON persona_groups(owner_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS persona_group_members (
|
||||
id TEXT PRIMARY KEY,
|
||||
group_id TEXT NOT NULL REFERENCES persona_groups(id) ON DELETE CASCADE,
|
||||
persona_id TEXT NOT NULL REFERENCES personas(id) ON DELETE CASCADE,
|
||||
is_leader INTEGER NOT NULL DEFAULT 0,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
UNIQUE(group_id, persona_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_persona_group_members_group ON persona_group_members(group_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS persona_grants (
|
||||
id TEXT PRIMARY KEY,
|
||||
|
||||
@@ -19,7 +19,7 @@ CREATE TABLE IF NOT EXISTS channels (
|
||||
user_id TEXT REFERENCES users(id) ON DELETE CASCADE,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
type TEXT DEFAULT 'direct' CHECK (type IN ('direct', 'group', 'channel')),
|
||||
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,
|
||||
@@ -57,6 +57,7 @@ CREATE TABLE IF NOT EXISTS messages (
|
||||
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'))
|
||||
);
|
||||
@@ -64,32 +65,44 @@ CREATE TABLE IF NOT EXISTS messages (
|
||||
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_members (
|
||||
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,
|
||||
role TEXT DEFAULT 'member',
|
||||
joined_at TEXT DEFAULT (datetime('now')),
|
||||
last_read_at TEXT DEFAULT (datetime('now')),
|
||||
UNIQUE(channel_id, user_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_members_channel ON channel_members(channel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_channel_members_user ON channel_members(user_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')),
|
||||
UNIQUE(channel_id, model_id)
|
||||
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 (
|
||||
@@ -108,13 +121,14 @@ 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)
|
||||
UNIQUE(user_id, model_id, provider_config_id)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_user_model_settings_user ON user_model_settings(user_id);
|
||||
|
||||
Reference in New Issue
Block a user