Changeset 0.28.0 (#172)
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
-- ==========================================
|
||||
-- Chat Switchboard — 005 Channels & Conversations
|
||||
-- ==========================================
|
||||
-- Channels, messages, participants, models, cursors, folders, user prefs.
|
||||
-- ICD §3 (Channels & Conversations)
|
||||
-- Note: project_id and workspace_id FKs added by 010_projects.sql
|
||||
-- Channels (all types, all columns), messages, participants,
|
||||
-- models, cursors, folders, user prefs, session participants.
|
||||
-- Consolidated v0.28.0: merges 005 + 016 (multiuser) + 017 (unread)
|
||||
-- + 021 (sessions) + 024 (workflow instance cols) + 026 (service type)
|
||||
-- + v0.28.0 (kb_auto_inject).
|
||||
--
|
||||
-- Note: project_id/workspace_id FKs added by 010_projects.sql.
|
||||
-- Note: workflow_id FK added by 018_workflows.sql.
|
||||
-- ==========================================
|
||||
|
||||
-- =========================================
|
||||
@@ -38,17 +43,43 @@ CREATE TABLE IF NOT EXISTS channels (
|
||||
title VARCHAR(500) NOT NULL,
|
||||
description TEXT,
|
||||
type VARCHAR(20) DEFAULT 'direct'
|
||||
CHECK (type IN ('direct', 'group', 'workflow')),
|
||||
CHECK (type IN ('direct', 'dm', 'group', 'channel', 'workflow', 'service')),
|
||||
model VARCHAR(100),
|
||||
system_prompt TEXT,
|
||||
provider_config_id UUID REFERENCES provider_configs(id) ON DELETE SET NULL,
|
||||
is_archived BOOLEAN DEFAULT false,
|
||||
is_pinned BOOLEAN DEFAULT false,
|
||||
folder_id UUID,
|
||||
folder TEXT,
|
||||
folder_id UUID REFERENCES folders(id) ON DELETE SET NULL,
|
||||
folder TEXT, -- legacy text folder (compat)
|
||||
team_id UUID REFERENCES teams(id) ON DELETE SET NULL,
|
||||
settings JSONB DEFAULT '{}'::jsonb,
|
||||
tags TEXT[],
|
||||
|
||||
-- Multi-user (v0.23.1)
|
||||
ai_mode VARCHAR(20) NOT NULL DEFAULT 'auto'
|
||||
CHECK (ai_mode IN ('auto', 'mention_only', 'off')),
|
||||
topic TEXT,
|
||||
|
||||
-- Anonymous sessions (v0.24.3)
|
||||
allow_anonymous BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
-- Workflow instance columns (v0.26.0)
|
||||
-- workflow_id FK added by 018_workflows.sql (deferred — table doesn't exist yet)
|
||||
workflow_id UUID,
|
||||
workflow_version INTEGER,
|
||||
current_stage INTEGER DEFAULT 0,
|
||||
stage_data JSONB DEFAULT '{}'::jsonb,
|
||||
workflow_status TEXT DEFAULT 'active'
|
||||
CHECK (workflow_status IN ('active', 'completed', 'stale', 'cancelled')),
|
||||
last_activity_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
|
||||
-- KB auto-injection (v0.28.0)
|
||||
kb_auto_inject BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
-- Cross-domain FKs (added by 010_projects.sql)
|
||||
project_id UUID,
|
||||
workspace_id UUID,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
@@ -58,22 +89,27 @@ CREATE INDEX IF NOT EXISTS idx_channels_updated ON channels(updated_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_channels_type ON channels(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_channels_team ON channels(team_id) WHERE team_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_channels_tags ON channels USING GIN(tags);
|
||||
CREATE INDEX IF NOT EXISTS idx_channels_folder ON channels(folder_id) WHERE folder_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_channels_project ON channels(project_id) WHERE project_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_channels_workspace ON channels(workspace_id) WHERE workspace_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_channels_workflow ON channels(workflow_id) WHERE workflow_id IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_channels_workflow_status ON channels(workflow_status) WHERE workflow_status = 'active';
|
||||
|
||||
DROP TRIGGER IF EXISTS channels_updated_at ON channels;
|
||||
CREATE TRIGGER channels_updated_at BEFORE UPDATE ON channels
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
-- Deferred FK: channels.folder_id → folders.id
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1 FROM pg_constraint WHERE conname = 'fk_channels_folder'
|
||||
) THEN
|
||||
ALTER TABLE channels ADD CONSTRAINT fk_channels_folder
|
||||
FOREIGN KEY (folder_id) REFERENCES folders(id) ON DELETE SET NULL;
|
||||
END IF;
|
||||
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=single-user chat, group=multi-user/multi-model, workflow=staged channel';
|
||||
COMMENT ON COLUMN channels.type IS 'direct=single-user, dm=human-to-human, group=multi-participant, channel=named persistent, workflow=staged, service=autonomous task';
|
||||
COMMENT ON COLUMN channels.ai_mode IS 'auto=respond always, mention_only=@mention required, off=AI disabled';
|
||||
COMMENT ON COLUMN channels.topic IS 'Short channel description shown in header';
|
||||
COMMENT ON COLUMN channels.allow_anonymous IS 'When true, unauthenticated visitors can join via session token';
|
||||
COMMENT ON COLUMN channels.workflow_id IS 'FK to workflows — set when type=workflow (constraint added by 018)';
|
||||
COMMENT ON COLUMN channels.workflow_version IS 'Pinned version number from workflow_versions';
|
||||
COMMENT ON COLUMN channels.current_stage IS 'Ordinal of the active workflow stage';
|
||||
COMMENT ON COLUMN channels.stage_data IS 'Accumulated form data collected across stages';
|
||||
COMMENT ON COLUMN channels.workflow_status IS 'active/completed/stale/cancelled';
|
||||
COMMENT ON COLUMN channels.last_activity_at IS 'Updated on each message — used by staleness sweep';
|
||||
COMMENT ON COLUMN channels.kb_auto_inject IS 'When true, top-K KB chunks auto-prepend to context';
|
||||
|
||||
|
||||
-- =========================================
|
||||
@@ -113,12 +149,6 @@ COMMENT ON COLUMN messages.sibling_index IS 'Position among siblings (0-indexed)
|
||||
-- =========================================
|
||||
-- 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_participants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
@@ -132,6 +162,7 @@ CREATE TABLE IF NOT EXISTS channel_participants (
|
||||
avatar_url TEXT,
|
||||
joined_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
last_read_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
last_read_message_id UUID, -- v0.23.2: unread cursor
|
||||
UNIQUE(channel_id, participant_type, participant_id)
|
||||
);
|
||||
|
||||
@@ -200,3 +231,25 @@ CREATE INDEX IF NOT EXISTS idx_user_model_settings_user ON user_model_settings(u
|
||||
DROP TRIGGER IF EXISTS user_model_settings_updated_at ON user_model_settings;
|
||||
CREATE TRIGGER user_model_settings_updated_at BEFORE UPDATE ON user_model_settings
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
||||
|
||||
|
||||
-- =========================================
|
||||
-- SESSION PARTICIPANTS (v0.24.3)
|
||||
-- =========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS session_participants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
session_token TEXT NOT NULL UNIQUE,
|
||||
channel_id UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE,
|
||||
display_name TEXT NOT NULL DEFAULT 'Visitor',
|
||||
fingerprint TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_session_participants_channel
|
||||
ON session_participants(channel_id);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_session_participants_token
|
||||
ON session_participants(session_token);
|
||||
|
||||
COMMENT ON TABLE session_participants IS 'Ephemeral identities for anonymous workflow channel visitors.';
|
||||
|
||||
Reference in New Issue
Block a user