// Mosaic Stack Database Schema // PostgreSQL 17 with pgvector extension generator client { provider = "prisma-client-js" previewFeatures = ["postgresqlExtensions"] } datasource db { provider = "postgresql" url = env("DATABASE_URL") extensions = [pgvector(map: "vector"), uuid_ossp(map: "uuid-ossp")] } // ============================================ // ENUMS // ============================================ enum TaskStatus { NOT_STARTED IN_PROGRESS PAUSED COMPLETED ARCHIVED } enum TaskPriority { LOW MEDIUM HIGH } enum ProjectStatus { PLANNING ACTIVE PAUSED COMPLETED ARCHIVED } enum WorkspaceMemberRole { OWNER ADMIN MEMBER GUEST } enum TeamMemberRole { OWNER ADMIN MEMBER } enum ActivityAction { CREATED UPDATED DELETED COMPLETED ASSIGNED COMMENTED LOGIN LOGOUT PASSWORD_RESET EMAIL_VERIFIED } enum EntityType { TASK EVENT PROJECT WORKSPACE USER IDEA DOMAIN } enum IdeaStatus { CAPTURED PROCESSING ACTIONABLE ARCHIVED DISCARDED } enum RelationshipType { BLOCKS BLOCKED_BY DEPENDS_ON PARENT_OF CHILD_OF RELATED_TO DUPLICATE_OF SUPERSEDES PART_OF } enum AgentStatus { IDLE WORKING WAITING ERROR TERMINATED } enum AgentTaskStatus { PENDING RUNNING COMPLETED FAILED } enum AgentTaskPriority { LOW MEDIUM HIGH } enum EntryStatus { DRAFT PUBLISHED ARCHIVED } enum Visibility { PRIVATE WORKSPACE PUBLIC } enum FormalityLevel { VERY_CASUAL CASUAL NEUTRAL FORMAL VERY_FORMAL } enum RunnerJobStatus { PENDING QUEUED RUNNING COMPLETED FAILED CANCELLED } enum JobStepPhase { SETUP EXECUTION VALIDATION CLEANUP } enum JobStepType { COMMAND AI_ACTION GATE ARTIFACT } enum JobStepStatus { PENDING RUNNING COMPLETED FAILED SKIPPED } // ============================================ // MODELS // ============================================ model User { id String @id @default(uuid()) @db.Uuid email String @unique name String emailVerified Boolean @default(false) @map("email_verified") image String? authProviderId String? @unique @map("auth_provider_id") preferences Json @default("{}") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations ownedWorkspaces Workspace[] @relation("WorkspaceOwner") workspaceMemberships WorkspaceMember[] teamMemberships TeamMember[] assignedTasks Task[] @relation("TaskAssignee") createdTasks Task[] @relation("TaskCreator") createdEvents Event[] @relation("EventCreator") createdProjects Project[] @relation("ProjectCreator") activityLogs ActivityLog[] sessions Session[] accounts Account[] ideas Idea[] @relation("IdeaCreator") relationships Relationship[] @relation("RelationshipCreator") agentSessions AgentSession[] agentTasks AgentTask[] @relation("AgentTaskCreator") userLayouts UserLayout[] userPreference UserPreference? knowledgeEntryVersions KnowledgeEntryVersion[] @relation("EntryVersionAuthor") llmProviders LlmProviderInstance[] @relation("UserLlmProviders") @@map("users") } model UserPreference { id String @id @default(uuid()) @db.Uuid userId String @unique @map("user_id") @db.Uuid user User @relation(fields: [userId], references: [id], onDelete: Cascade) theme String @default("system") locale String @default("en") timezone String? settings Json @default("{}") updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz @@map("user_preferences") } model Workspace { id String @id @default(uuid()) @db.Uuid name String ownerId String @map("owner_id") @db.Uuid settings Json @default("{}") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations owner User @relation("WorkspaceOwner", fields: [ownerId], references: [id], onDelete: Cascade) members WorkspaceMember[] teams Team[] tasks Task[] events Event[] projects Project[] activityLogs ActivityLog[] memoryEmbeddings MemoryEmbedding[] domains Domain[] ideas Idea[] relationships Relationship[] agents Agent[] agentSessions AgentSession[] agentTasks AgentTask[] userLayouts UserLayout[] knowledgeEntries KnowledgeEntry[] knowledgeTags KnowledgeTag[] cronSchedules CronSchedule[] personalities Personality[] llmSettings WorkspaceLlmSettings? qualityGates QualityGate[] runnerJobs RunnerJob[] @@index([ownerId]) @@map("workspaces") } model WorkspaceMember { workspaceId String @map("workspace_id") @db.Uuid userId String @map("user_id") @db.Uuid role WorkspaceMemberRole @default(MEMBER) joinedAt DateTime @default(now()) @map("joined_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@id([workspaceId, userId]) @@index([userId]) @@map("workspace_members") } model Team { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid name String description String? @db.Text metadata Json @default("{}") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) members TeamMember[] @@index([workspaceId]) @@map("teams") } model TeamMember { teamId String @map("team_id") @db.Uuid userId String @map("user_id") @db.Uuid role TeamMemberRole @default(MEMBER) joinedAt DateTime @default(now()) @map("joined_at") @db.Timestamptz // Relations team Team @relation(fields: [teamId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@id([teamId, userId]) @@index([userId]) @@map("team_members") } model Task { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid title String description String? @db.Text status TaskStatus @default(NOT_STARTED) priority TaskPriority @default(MEDIUM) dueDate DateTime? @map("due_date") @db.Timestamptz assigneeId String? @map("assignee_id") @db.Uuid creatorId String @map("creator_id") @db.Uuid projectId String? @map("project_id") @db.Uuid parentId String? @map("parent_id") @db.Uuid domainId String? @map("domain_id") @db.Uuid sortOrder Int @default(0) @map("sort_order") metadata Json @default("{}") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz completedAt DateTime? @map("completed_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) assignee User? @relation("TaskAssignee", fields: [assigneeId], references: [id], onDelete: SetNull) creator User @relation("TaskCreator", fields: [creatorId], references: [id], onDelete: Cascade) project Project? @relation(fields: [projectId], references: [id], onDelete: SetNull) parent Task? @relation("TaskSubtasks", fields: [parentId], references: [id], onDelete: Cascade) subtasks Task[] @relation("TaskSubtasks") domain Domain? @relation(fields: [domainId], references: [id], onDelete: SetNull) @@unique([id, workspaceId]) @@index([workspaceId]) @@index([workspaceId, status]) @@index([workspaceId, dueDate]) @@index([assigneeId]) @@index([projectId]) @@index([parentId]) @@index([domainId]) @@map("tasks") } model Event { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid title String description String? @db.Text startTime DateTime @map("start_time") @db.Timestamptz endTime DateTime? @map("end_time") @db.Timestamptz allDay Boolean @default(false) @map("all_day") location String? recurrence Json? creatorId String @map("creator_id") @db.Uuid projectId String? @map("project_id") @db.Uuid domainId String? @map("domain_id") @db.Uuid metadata Json @default("{}") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) creator User @relation("EventCreator", fields: [creatorId], references: [id], onDelete: Cascade) project Project? @relation(fields: [projectId], references: [id], onDelete: SetNull) domain Domain? @relation(fields: [domainId], references: [id], onDelete: SetNull) @@unique([id, workspaceId]) @@index([workspaceId]) @@index([workspaceId, startTime]) @@index([creatorId]) @@index([projectId]) @@index([domainId]) @@map("events") } model Project { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid name String description String? @db.Text status ProjectStatus @default(PLANNING) startDate DateTime? @map("start_date") @db.Date endDate DateTime? @map("end_date") @db.Date creatorId String @map("creator_id") @db.Uuid domainId String? @map("domain_id") @db.Uuid color String? metadata Json @default("{}") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) creator User @relation("ProjectCreator", fields: [creatorId], references: [id], onDelete: Cascade) tasks Task[] events Event[] domain Domain? @relation(fields: [domainId], references: [id], onDelete: SetNull) ideas Idea[] @@unique([id, workspaceId]) @@index([workspaceId]) @@index([workspaceId, status]) @@index([creatorId]) @@index([domainId]) @@map("projects") } model ActivityLog { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid userId String @map("user_id") @db.Uuid action ActivityAction entityType EntityType @map("entity_type") entityId String @map("entity_id") @db.Uuid details Json @default("{}") ipAddress String? @map("ip_address") userAgent String? @map("user_agent") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([id, workspaceId]) @@index([workspaceId]) @@index([workspaceId, createdAt]) @@index([entityType, entityId]) @@index([userId]) @@index([action]) @@map("activity_logs") } model MemoryEmbedding { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid content String @db.Text // Note: vector dimension (1536) must match EMBEDDING_DIMENSION constant in @mosaic/shared embedding Unsupported("vector(1536)")? entityType EntityType? @map("entity_type") entityId String? @map("entity_id") @db.Uuid metadata Json @default("{}") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) @@index([workspaceId]) @@map("memory_embeddings") } // ============================================ // NEW MODELS // ============================================ model Domain { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid name String slug String description String? @db.Text color String? icon String? sortOrder Int @default(0) @map("sort_order") metadata Json @default("{}") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) tasks Task[] events Event[] projects Project[] ideas Idea[] @@unique([id, workspaceId]) @@unique([workspaceId, slug]) @@index([workspaceId]) @@map("domains") } model Idea { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid domainId String? @map("domain_id") @db.Uuid projectId String? @map("project_id") @db.Uuid // Core fields title String? content String @db.Text // Status status IdeaStatus @default(CAPTURED) priority TaskPriority @default(MEDIUM) // Categorization category String? tags String[] metadata Json @default("{}") // Embedding for semantic search (pgvector) embedding Unsupported("vector(1536)")? // Audit creatorId String @map("creator_id") @db.Uuid createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) domain Domain? @relation(fields: [domainId], references: [id], onDelete: SetNull) project Project? @relation(fields: [projectId], references: [id], onDelete: SetNull) creator User @relation("IdeaCreator", fields: [creatorId], references: [id], onDelete: Cascade) @@unique([id, workspaceId]) @@index([workspaceId]) @@index([workspaceId, status]) @@index([domainId]) @@index([projectId]) @@index([creatorId]) @@map("ideas") } model Relationship { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid // Source entity sourceType EntityType @map("source_type") sourceId String @map("source_id") @db.Uuid // Target entity targetType EntityType @map("target_type") targetId String @map("target_id") @db.Uuid // Relationship type relationship RelationshipType metadata Json @default("{}") notes String? @db.Text // Audit creatorId String @map("creator_id") @db.Uuid createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) creator User @relation("RelationshipCreator", fields: [creatorId], references: [id], onDelete: Cascade) // Prevent duplicate relationships @@unique([workspaceId, sourceType, sourceId, targetType, targetId, relationship]) @@index([sourceType, sourceId]) @@index([targetType, targetId]) @@index([relationship]) @@map("relationships") } model Agent { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid // Identity agentId String @map("agent_id") name String? model String? role String? // Status status AgentStatus @default(IDLE) currentTask String? @map("current_task") @db.Text // Performance metrics metrics Json @default("{\"totalTasks\": 0, \"successfulTasks\": 0, \"failedTasks\": 0, \"avgResponseTimeMs\": 0}") // Health lastHeartbeat DateTime? @map("last_heartbeat") @db.Timestamptz errorCount Int @default(0) @map("error_count") lastError String? @map("last_error") @db.Text // Firing history firedCount Int @default(0) @map("fired_count") fireHistory Json @default("[]") @map("fire_history") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz terminatedAt DateTime? @map("terminated_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) sessions AgentSession[] @@unique([workspaceId, agentId]) @@index([workspaceId]) @@index([status]) @@map("agents") } model AgentTask { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid // Task details title String description String? @db.Text status AgentTaskStatus @default(PENDING) priority AgentTaskPriority @default(MEDIUM) // Agent configuration agentType String @map("agent_type") agentConfig Json @default("{}") @map("agent_config") // Results result Json? error String? @db.Text // Timing createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz startedAt DateTime? @map("started_at") @db.Timestamptz completedAt DateTime? @map("completed_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) createdBy User @relation("AgentTaskCreator", fields: [createdById], references: [id], onDelete: Cascade) createdById String @map("created_by_id") @db.Uuid runnerJobs RunnerJob[] @@unique([id, workspaceId]) @@index([workspaceId]) @@index([workspaceId, status]) @@index([createdById]) @@index([agentType]) @@map("agent_tasks") } model AgentSession { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid userId String @map("user_id") @db.Uuid agentId String? @map("agent_id") @db.Uuid // Identity sessionKey String @map("session_key") label String? channel String? // Context contextSummary String? @map("context_summary") @db.Text messageCount Int @default(0) @map("message_count") // Status isActive Boolean @default(true) @map("is_active") startedAt DateTime @default(now()) @map("started_at") @db.Timestamptz lastMessageAt DateTime? @map("last_message_at") @db.Timestamptz endedAt DateTime? @map("ended_at") @db.Timestamptz metadata Json @default("{}") // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) agent Agent? @relation(fields: [agentId], references: [id], onDelete: SetNull) @@unique([workspaceId, sessionKey]) @@index([workspaceId]) @@index([userId]) @@index([agentId]) @@index([isActive]) @@map("agent_sessions") } model WidgetDefinition { id String @id @default(uuid()) @db.Uuid name String @unique displayName String @map("display_name") description String? @db.Text component String // Default size (grid units) defaultWidth Int @default(1) @map("default_width") defaultHeight Int @default(1) @map("default_height") minWidth Int @default(1) @map("min_width") minHeight Int @default(1) @map("min_height") maxWidth Int? @map("max_width") maxHeight Int? @map("max_height") // Configuration schema (JSON Schema for widget config) configSchema Json @default("{}") @map("config_schema") isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz @@map("widget_definitions") } model UserLayout { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid userId String @map("user_id") @db.Uuid name String isDefault Boolean @default(false) @map("is_default") // Layout configuration (array of widget placements) layout Json @default("[]") // Additional metadata for the layout metadata Json @default("{}") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([id, workspaceId]) @@unique([workspaceId, userId, name]) @@index([userId]) @@map("user_layouts") } // ============================================ // AUTHENTICATION MODELS (BetterAuth) // ============================================ model Session { id String @id @default(uuid()) @db.Uuid userId String @map("user_id") @db.Uuid token String @unique expiresAt DateTime @map("expires_at") @db.Timestamptz ipAddress String? @map("ip_address") userAgent String? @map("user_agent") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@index([token]) @@map("sessions") } model Account { id String @id @default(uuid()) @db.Uuid userId String @map("user_id") @db.Uuid accountId String @map("account_id") providerId String @map("provider_id") accessToken String? @map("access_token") refreshToken String? @map("refresh_token") idToken String? @map("id_token") accessTokenExpiresAt DateTime? @map("access_token_expires_at") @db.Timestamptz refreshTokenExpiresAt DateTime? @map("refresh_token_expires_at") @db.Timestamptz scope String? password String? createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([providerId, accountId]) @@index([userId]) @@map("accounts") } model Verification { id String @id @default(uuid()) @db.Uuid identifier String value String expiresAt DateTime @map("expires_at") @db.Timestamptz createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz @@index([identifier]) @@map("verifications") } // ============================================ // KNOWLEDGE MODULE // ============================================ model KnowledgeEntry { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) // Identity slug String title String // Content content String @db.Text contentHtml String? @map("content_html") @db.Text summary String? // Status status EntryStatus @default(DRAFT) visibility Visibility @default(PRIVATE) // Audit createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz createdBy String @map("created_by") @db.Uuid updatedBy String @map("updated_by") @db.Uuid // Relations tags KnowledgeEntryTag[] outgoingLinks KnowledgeLink[] @relation("SourceEntry") incomingLinks KnowledgeLink[] @relation("TargetEntry") versions KnowledgeEntryVersion[] embedding KnowledgeEmbedding? @@unique([workspaceId, slug]) @@index([workspaceId, status]) @@index([workspaceId, updatedAt]) @@index([createdBy]) @@index([updatedBy]) @@map("knowledge_entries") } model KnowledgeEntryVersion { id String @id @default(uuid()) @db.Uuid entryId String @map("entry_id") @db.Uuid entry KnowledgeEntry @relation(fields: [entryId], references: [id], onDelete: Cascade) version Int title String content String @db.Text summary String? createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz createdBy String @map("created_by") @db.Uuid author User @relation("EntryVersionAuthor", fields: [createdBy], references: [id]) changeNote String? @map("change_note") @@unique([entryId, version]) @@index([entryId, version]) @@map("knowledge_entry_versions") } model KnowledgeLink { id String @id @default(uuid()) @db.Uuid sourceId String @map("source_id") @db.Uuid source KnowledgeEntry @relation("SourceEntry", fields: [sourceId], references: [id], onDelete: Cascade) targetId String @map("target_id") @db.Uuid target KnowledgeEntry @relation("TargetEntry", fields: [targetId], references: [id], onDelete: Cascade) // Link metadata linkText String @map("link_text") displayText String @map("display_text") context String? // Position in source content positionStart Int @map("position_start") positionEnd Int @map("position_end") // Resolution status resolved Boolean @default(true) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz @@unique([sourceId, targetId]) @@index([sourceId]) @@index([targetId]) @@index([resolved]) @@map("knowledge_links") } model KnowledgeTag { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) name String slug String color String? description String? entries KnowledgeEntryTag[] @@unique([workspaceId, slug]) @@index([workspaceId]) @@map("knowledge_tags") } model KnowledgeEntryTag { entryId String @map("entry_id") @db.Uuid entry KnowledgeEntry @relation(fields: [entryId], references: [id], onDelete: Cascade) tagId String @map("tag_id") @db.Uuid tag KnowledgeTag @relation(fields: [tagId], references: [id], onDelete: Cascade) @@id([entryId, tagId]) @@index([entryId]) @@index([tagId]) @@map("knowledge_entry_tags") } model KnowledgeEmbedding { id String @id @default(uuid()) @db.Uuid entryId String @unique @map("entry_id") @db.Uuid entry KnowledgeEntry @relation(fields: [entryId], references: [id], onDelete: Cascade) embedding Unsupported("vector(1536)") model String createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz @@index([entryId]) @@map("knowledge_embeddings") } // ============================================ // CRON JOBS // ============================================ model CronSchedule { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) // Cron configuration expression String // Standard cron: "0 9 * * *" = 9am daily command String // MoltBot command to trigger // State enabled Boolean @default(true) lastRun DateTime? @map("last_run") @db.Timestamptz nextRun DateTime? @map("next_run") @db.Timestamptz // Audit createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz @@index([workspaceId]) @@index([workspaceId, enabled]) @@index([nextRun]) @@map("cron_schedules") } // ============================================ // PERSONALITY MODULE // ============================================ model Personality { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) // Identity name String // unique identifier slug displayName String @map("display_name") description String? @db.Text // System prompt systemPrompt String @map("system_prompt") @db.Text // LLM configuration temperature Float? // null = use provider default maxTokens Int? @map("max_tokens") // null = use provider default llmProviderInstanceId String? @map("llm_provider_instance_id") @db.Uuid // Status isDefault Boolean @default(false) @map("is_default") isEnabled Boolean @default(true) @map("is_enabled") // Audit createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations llmProviderInstance LlmProviderInstance? @relation("PersonalityLlmProvider", fields: [llmProviderInstanceId], references: [id], onDelete: SetNull) workspaceLlmSettings WorkspaceLlmSettings[] @relation("WorkspacePersonality") @@unique([id, workspaceId]) @@unique([workspaceId, name]) @@index([workspaceId]) @@index([workspaceId, isDefault]) @@index([workspaceId, isEnabled]) @@index([llmProviderInstanceId]) @@map("personalities") } // ============================================ // LLM PROVIDER MODULE // ============================================ model LlmProviderInstance { id String @id @default(uuid()) @db.Uuid providerType String @map("provider_type") // "ollama" | "claude" | "openai" displayName String @map("display_name") userId String? @map("user_id") @db.Uuid // NULL = system-level, UUID = user-level config Json // Provider-specific configuration isDefault Boolean @default(false) @map("is_default") isEnabled Boolean @default(true) @map("is_enabled") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz // Relations user User? @relation("UserLlmProviders", fields: [userId], references: [id], onDelete: Cascade) personalities Personality[] @relation("PersonalityLlmProvider") workspaceLlmSettings WorkspaceLlmSettings[] @relation("WorkspaceLlmProvider") @@index([userId]) @@index([providerType]) @@index([isDefault]) @@index([isEnabled]) @@map("llm_provider_instances") } // ============================================ // WORKSPACE LLM SETTINGS // ============================================ model WorkspaceLlmSettings { id String @id @default(uuid()) @db.Uuid workspaceId String @unique @map("workspace_id") @db.Uuid workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) defaultLlmProviderId String? @map("default_llm_provider_id") @db.Uuid defaultLlmProvider LlmProviderInstance? @relation("WorkspaceLlmProvider", fields: [defaultLlmProviderId], references: [id], onDelete: SetNull) defaultPersonalityId String? @map("default_personality_id") @db.Uuid defaultPersonality Personality? @relation("WorkspacePersonality", fields: [defaultPersonalityId], references: [id], onDelete: SetNull) settings Json? @default("{}") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz @@index([workspaceId]) @@index([defaultLlmProviderId]) @@index([defaultPersonalityId]) @@map("workspace_llm_settings") } // ============================================ // QUALITY GATE MODULE // ============================================ model QualityGate { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) name String description String? type String // 'build' | 'lint' | 'test' | 'coverage' | 'custom' command String? expectedOutput String? @map("expected_output") isRegex Boolean @default(false) @map("is_regex") required Boolean @default(true) order Int @default(0) isEnabled Boolean @default(true) @map("is_enabled") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz @@unique([workspaceId, name]) @@index([workspaceId]) @@index([workspaceId, isEnabled]) @@map("quality_gates") } model TaskRejection { id String @id @default(uuid()) @db.Uuid taskId String @map("task_id") workspaceId String @map("workspace_id") agentId String @map("agent_id") attemptCount Int @map("attempt_count") failures Json // FailureSummary[] originalTask String @map("original_task") startedAt DateTime @map("started_at") @db.Timestamptz rejectedAt DateTime @map("rejected_at") @db.Timestamptz escalated Boolean @default(false) manualReview Boolean @default(false) @map("manual_review") resolvedAt DateTime? @map("resolved_at") @db.Timestamptz resolution String? @@index([taskId]) @@index([workspaceId]) @@index([agentId]) @@index([escalated]) @@index([manualReview]) @@map("task_rejections") } model TokenBudget { id String @id @default(uuid()) @db.Uuid taskId String @unique @map("task_id") @db.Uuid workspaceId String @map("workspace_id") @db.Uuid agentId String @map("agent_id") // Budget allocation allocatedTokens Int @map("allocated_tokens") estimatedComplexity String @map("estimated_complexity") // "low", "medium", "high", "critical" // Usage tracking inputTokensUsed Int @default(0) @map("input_tokens_used") outputTokensUsed Int @default(0) @map("output_tokens_used") totalTokensUsed Int @default(0) @map("total_tokens_used") // Cost tracking estimatedCost Decimal? @map("estimated_cost") @db.Decimal(10, 6) // State startedAt DateTime @default(now()) @map("started_at") @db.Timestamptz lastUpdatedAt DateTime @updatedAt @map("last_updated_at") @db.Timestamptz completedAt DateTime? @map("completed_at") @db.Timestamptz // Analysis budgetUtilization Float? @map("budget_utilization") // 0.0 - 1.0 suspiciousPattern Boolean @default(false) @map("suspicious_pattern") suspiciousReason String? @map("suspicious_reason") @@index([taskId]) @@index([workspaceId]) @@index([suspiciousPattern]) @@map("token_budgets") } // ============================================ // RUNNER JOB TRACKING MODULE // ============================================ model RunnerJob { id String @id @default(uuid()) @db.Uuid workspaceId String @map("workspace_id") @db.Uuid agentTaskId String? @map("agent_task_id") @db.Uuid // Job details type String // 'git-status', 'code-task', 'priority-calc' status RunnerJobStatus @default(PENDING) priority Int progressPercent Int @default(0) @map("progress_percent") // Results result Json? error String? @db.Text // Timing createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz startedAt DateTime? @map("started_at") @db.Timestamptz completedAt DateTime? @map("completed_at") @db.Timestamptz // Relations workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade) agentTask AgentTask? @relation(fields: [agentTaskId], references: [id], onDelete: SetNull) steps JobStep[] events JobEvent[] @@unique([id, workspaceId]) @@index([workspaceId]) @@index([workspaceId, status]) @@index([agentTaskId]) @@index([priority]) @@map("runner_jobs") } model JobStep { id String @id @default(uuid()) @db.Uuid jobId String @map("job_id") @db.Uuid // Step details ordinal Int phase JobStepPhase name String type JobStepType status JobStepStatus @default(PENDING) // Output and metrics output String? @db.Text tokensInput Int? @map("tokens_input") tokensOutput Int? @map("tokens_output") // Timing startedAt DateTime? @map("started_at") @db.Timestamptz completedAt DateTime? @map("completed_at") @db.Timestamptz durationMs Int? @map("duration_ms") // Relations job RunnerJob @relation(fields: [jobId], references: [id], onDelete: Cascade) events JobEvent[] @@index([jobId]) @@index([jobId, ordinal]) @@index([status]) @@map("job_steps") } model JobEvent { id String @id @default(uuid()) @db.Uuid jobId String @map("job_id") @db.Uuid stepId String? @map("step_id") @db.Uuid // Event details type String timestamp DateTime @db.Timestamptz actor String payload Json // Relations job RunnerJob @relation(fields: [jobId], references: [id], onDelete: Cascade) step JobStep? @relation(fields: [stepId], references: [id], onDelete: Cascade) @@index([jobId]) @@index([stepId]) @@index([timestamp]) @@index([type]) @@map("job_events") }