-- AlterTable ALTER TABLE "bots" ADD COLUMN "allowedBuiltinTools" TEXT[] DEFAULT ARRAY[]::TEXT[], ADD COLUMN "kind" TEXT NOT NULL DEFAULT 'specialist', ADD COLUMN "modelTier" TEXT, ADD COLUMN "restrictBuiltinTools" BOOLEAN NOT NULL DEFAULT false, ADD COLUMN "tags" TEXT[] DEFAULT ARRAY[]::TEXT[]; -- AlterTable ALTER TABLE "chat_groups" ADD COLUMN "kind" TEXT NOT NULL DEFAULT 'group', ADD COLUMN "leadBotId" TEXT; -- CreateTable CREATE TABLE "space_model_tiers" ( "id" TEXT NOT NULL, "spaceId" TEXT NOT NULL, "userId" TEXT NOT NULL, "tier" TEXT NOT NULL, "credentialId" TEXT NOT NULL, "modelId" TEXT NOT NULL, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL, CONSTRAINT "space_model_tiers_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "user_profiles" ( "userId" TEXT NOT NULL, "timeZone" TEXT, "locale" TEXT, "language" TEXT, "communicationStyle" TEXT NOT NULL DEFAULT '', "summary" TEXT NOT NULL DEFAULT '', "consolidatedAt" TIMESTAMP(3), "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL, CONSTRAINT "user_profiles_pkey" PRIMARY KEY ("userId") ); -- CreateTable CREATE TABLE "user_facts" ( "id" TEXT NOT NULL, "userId" TEXT NOT NULL, "category" TEXT NOT NULL, "value" TEXT NOT NULL, "confidence" DOUBLE PRECISION NOT NULL DEFAULT 1, "source" TEXT NOT NULL, "status" TEXT NOT NULL DEFAULT 'active', "sensitive" BOOLEAN NOT NULL DEFAULT false, "allSpaces" BOOLEAN NOT NULL DEFAULT true, "sourceBotId" TEXT, "sourceThreadId" TEXT, "sourceMessageId" TEXT, "embedding" DOUBLE PRECISION[] DEFAULT ARRAY[]::DOUBLE PRECISION[], "embeddingModel" TEXT, "confirmedAt" TIMESTAMP(3), "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL, CONSTRAINT "user_facts_pkey" PRIMARY KEY ("id") ); -- CreateTable CREATE TABLE "user_fact_spaces" ( "factId" TEXT NOT NULL, "spaceId" TEXT NOT NULL, CONSTRAINT "user_fact_spaces_pkey" PRIMARY KEY ("factId","spaceId") ); -- CreateTable CREATE TABLE "delegated_tasks" ( "id" TEXT NOT NULL, "userId" TEXT NOT NULL, "spaceId" TEXT NOT NULL, "threadId" TEXT NOT NULL, "requesterBotId" TEXT NOT NULL, "assigneeBotId" TEXT NOT NULL, "assigneeSpaceId" TEXT NOT NULL, "title" TEXT NOT NULL, "brief" TEXT NOT NULL, "status" TEXT NOT NULL DEFAULT 'queued', "deadlineAt" TIMESTAMP(3), "result" TEXT, "artifacts" JSONB NOT NULL DEFAULT '[]', "runId" TEXT, "messageId" TEXT, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "updatedAt" TIMESTAMP(3) NOT NULL, "completedAt" TIMESTAMP(3), CONSTRAINT "delegated_tasks_pkey" PRIMARY KEY ("id") ); -- CreateIndex CREATE INDEX "space_model_tiers_credentialId_idx" ON "space_model_tiers"("credentialId"); -- CreateIndex CREATE UNIQUE INDEX "space_model_tiers_spaceId_userId_tier_key" ON "space_model_tiers"("spaceId", "userId", "tier"); -- CreateIndex CREATE INDEX "user_facts_userId_status_category_updatedAt_idx" ON "user_facts"("userId", "status", "category", "updatedAt"); -- CreateIndex CREATE INDEX "user_fact_spaces_spaceId_idx" ON "user_fact_spaces"("spaceId"); -- CreateIndex CREATE UNIQUE INDEX "delegated_tasks_runId_key" ON "delegated_tasks"("runId"); -- CreateIndex CREATE INDEX "delegated_tasks_threadId_status_createdAt_idx" ON "delegated_tasks"("threadId", "status", "createdAt"); -- CreateIndex CREATE INDEX "delegated_tasks_assigneeBotId_status_idx" ON "delegated_tasks"("assigneeBotId", "status"); -- CreateIndex CREATE INDEX "delegated_tasks_userId_status_updatedAt_idx" ON "delegated_tasks"("userId", "status", "updatedAt"); -- CreateIndex CREATE INDEX "chat_groups_leadBotId_idx" ON "chat_groups"("leadBotId"); -- AddForeignKey ALTER TABLE "space_model_tiers" ADD CONSTRAINT "space_model_tiers_spaceId_userId_fkey" FOREIGN KEY ("spaceId", "userId") REFERENCES "space_members"("spaceId", "userId") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "space_model_tiers" ADD CONSTRAINT "space_model_tiers_credentialId_userId_fkey" FOREIGN KEY ("credentialId", "userId") REFERENCES "user_model_credentials"("id", "userId") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "chat_groups" ADD CONSTRAINT "chat_groups_leadBotId_fkey" FOREIGN KEY ("leadBotId") REFERENCES "bots"("id") ON DELETE SET NULL ON UPDATE CASCADE NOT VALID; -- AddForeignKey ALTER TABLE "user_profiles" ADD CONSTRAINT "user_profiles_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "user_facts" ADD CONSTRAINT "user_facts_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "user_fact_spaces" ADD CONSTRAINT "user_fact_spaces_factId_fkey" FOREIGN KEY ("factId") REFERENCES "user_facts"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "user_fact_spaces" ADD CONSTRAINT "user_fact_spaces_spaceId_fkey" FOREIGN KEY ("spaceId") REFERENCES "spaces"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "delegated_tasks" ADD CONSTRAINT "delegated_tasks_spaceId_fkey" FOREIGN KEY ("spaceId") REFERENCES "spaces"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey ALTER TABLE "delegated_tasks" ADD CONSTRAINT "delegated_tasks_threadId_fkey" FOREIGN KEY ("threadId") REFERENCES "threads"("id") ON DELETE CASCADE ON UPDATE CASCADE; -- CHECK constraints on existing tables are added NOT VALID and validated in the next migration. ALTER TABLE "bots" ADD CONSTRAINT "bots_kind_chk" CHECK ("kind" IN ('main', 'specialist')) NOT VALID; ALTER TABLE "bots" ADD CONSTRAINT "bots_modelTier_chk" CHECK ("modelTier" IS NULL OR "modelTier" IN ('fast', 'standard', 'code')) NOT VALID; ALTER TABLE "chat_groups" ADD CONSTRAINT "chat_groups_kind_chk" CHECK ("kind" IN ('group', 'main')) NOT VALID; -- New tables are empty, so their constraints apply immediately. ALTER TABLE "space_model_tiers" ADD CONSTRAINT "space_model_tiers_tier_chk" CHECK ("tier" IN ('fast', 'standard', 'code')); ALTER TABLE "user_facts" ADD CONSTRAINT "user_facts_category_chk" CHECK ("category" IN ('identity', 'work', 'goals', 'people', 'health', 'values', 'habits', 'preferences', 'tools')); ALTER TABLE "user_facts" ADD CONSTRAINT "user_facts_source_chk" CHECK ("source" IN ('onboarding', 'user', 'conversation', 'consolidation', 'connector')); ALTER TABLE "user_facts" ADD CONSTRAINT "user_facts_status_chk" CHECK ("status" IN ('active', 'pending')); ALTER TABLE "user_facts" ADD CONSTRAINT "user_facts_confidence_chk" CHECK ("confidence" >= 0 AND "confidence" <= 1); ALTER TABLE "delegated_tasks" ADD CONSTRAINT "delegated_tasks_status_chk" CHECK ("status" IN ('queued', 'running', 'waiting', 'done', 'failed', 'cancelled')); -- Promote one existing first bot per user to the main bot, preferring the user's home space -- (the same order requireMembership uses to pick the default space). UPDATE "bots" AS b SET "kind" = 'main' FROM ( SELECT DISTINCT ON (fb."userId") fb."id" FROM "bots" AS fb JOIN "spaces" AS s ON s."id" = fb."spaceId" LEFT JOIN "space_members" AS sm ON sm."spaceId" = fb."spaceId" AND sm."userId" = fb."userId" WHERE fb."spawnKey" = 'onboarding:first' AND fb."archivedAt" IS NULL ORDER BY fb."userId", s."isDefault" DESC, sm."createdAt" ASC NULLS LAST, fb."createdAt" ASC, fb."id" ASC ) AS first_bot WHERE b."id" = first_bot."id"; -- Turn an existing client-created "Hauptthread" group that includes the main bot into the -- main conversation, so its history stays where the user expects it. UPDATE "chat_groups" AS g SET "kind" = 'main', "leadBotId" = picked."botId" FROM ( SELECT DISTINCT ON (cg."userId") cg."id" AS "groupId", mb."id" AS "botId" FROM "chat_groups" AS cg JOIN "chat_group_members" AS cm ON cm."groupId" = cg."id" JOIN "bots" AS mb ON mb."id" = cm."botId" AND mb."kind" = 'main' AND mb."spaceId" = cg."spaceId" WHERE cg."archivedAt" IS NULL AND cg."name" IN ('Hauptthread', 'Team Chat') ORDER BY cg."userId", cg."createdAt" ASC, cg."id" ASC ) AS picked WHERE g."id" = picked."groupId"; -- One main bot and one main conversation per user. CREATE UNIQUE INDEX "bots_userId_main_key" ON "bots"("userId") WHERE "kind" = 'main'; CREATE UNIQUE INDEX "chat_groups_userId_main_key" ON "chat_groups"("userId") WHERE "kind" = 'main';