-- ========================================== -- Chat Switchboard — 008 Memory -- ========================================== -- Long-term memory across conversations, extraction tracking. -- ICD §9 (Memory) -- ========================================== -- ========================================= -- MEMORIES -- ========================================= CREATE TABLE IF NOT EXISTS memories ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), scope TEXT NOT NULL CHECK (scope IN ('user', 'persona', 'persona_user')), owner_id UUID NOT NULL, user_id UUID, key TEXT NOT NULL, value TEXT NOT NULL, source_channel_id UUID, confidence REAL NOT NULL DEFAULT 1.0, status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'pending_review', 'archived')), embedding vector(3072), created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX IF NOT EXISTS idx_memories_scope_owner ON memories(scope, owner_id, status) WHERE status = 'active'; CREATE INDEX IF NOT EXISTS idx_memories_persona_user ON memories(owner_id, user_id) WHERE scope = 'persona_user' AND status = 'active'; CREATE UNIQUE INDEX IF NOT EXISTS idx_memories_unique_key ON memories(scope, owner_id, COALESCE(user_id, '00000000-0000-0000-0000-000000000000'), key); CREATE INDEX IF NOT EXISTS idx_memories_fts ON memories USING gin(to_tsvector('english', key || ' ' || value)); CREATE INDEX IF NOT EXISTS idx_memories_source_channel ON memories(source_channel_id) WHERE source_channel_id IS NOT NULL; COMMENT ON TABLE memories IS 'Long-term memory facts persisted across conversations'; COMMENT ON COLUMN memories.scope IS 'user = personal facts; persona = shared; persona_user = per-user within a persona'; COMMENT ON COLUMN memories.owner_id IS 'user_id for user scope, persona_id for persona/persona_user scopes'; COMMENT ON COLUMN memories.confidence IS '0.0-1.0 confidence score — higher = more certain'; CREATE OR REPLACE FUNCTION update_memories_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trg_memories_updated_at ON memories; CREATE TRIGGER trg_memories_updated_at BEFORE UPDATE ON memories FOR EACH ROW EXECUTE FUNCTION update_memories_updated_at(); -- ========================================= -- MEMORY EXTRACTION LOG (v0.18.0 Phase 2) -- ========================================= CREATE TABLE IF NOT EXISTS memory_extraction_log ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), channel_id UUID NOT NULL, user_id UUID NOT NULL, last_message_id UUID NOT NULL, extracted_at TIMESTAMPTZ DEFAULT now(), memory_count INT NOT NULL DEFAULT 0, UNIQUE(channel_id, user_id) ); CREATE INDEX IF NOT EXISTS idx_mem_extract_log_channel ON memory_extraction_log(channel_id); COMMENT ON TABLE memory_extraction_log IS 'Tracks extraction progress per channel+user to prevent re-processing';