All checks were successful
ci/woodpecker/push/api Pipeline was successful
Prisma client was generated for debian-openssl-1.1.x but the node:24-slim Docker image runs Debian Bookworm with OpenSSL 3.0.x. Adding explicit binaryTargets ensures the correct query engine is always generated regardless of build host. Also removes inline comments from .env.example rate limit values — Portainer treats trailing comments as part of the value. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1537 lines
48 KiB
Plaintext
1537 lines
48 KiB
Plaintext
// Mosaic Stack Database Schema
|
|
// PostgreSQL 17 with pgvector extension
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
binaryTargets = ["native", "debian-openssl-3.0.x"]
|
|
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
|
|
CREDENTIAL_CREATED
|
|
CREDENTIAL_ACCESSED
|
|
CREDENTIAL_ROTATED
|
|
CREDENTIAL_REVOKED
|
|
}
|
|
|
|
enum EntityType {
|
|
TASK
|
|
EVENT
|
|
PROJECT
|
|
WORKSPACE
|
|
USER
|
|
IDEA
|
|
DOMAIN
|
|
CREDENTIAL
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
enum FederationConnectionStatus {
|
|
PENDING
|
|
ACTIVE
|
|
SUSPENDED
|
|
DISCONNECTED
|
|
}
|
|
|
|
enum FederationMessageType {
|
|
QUERY
|
|
COMMAND
|
|
EVENT
|
|
}
|
|
|
|
enum FederationMessageStatus {
|
|
PENDING
|
|
DELIVERED
|
|
FAILED
|
|
TIMEOUT
|
|
}
|
|
|
|
enum CredentialType {
|
|
API_KEY
|
|
OAUTH_TOKEN
|
|
ACCESS_TOKEN
|
|
SECRET
|
|
PASSWORD
|
|
CUSTOM
|
|
}
|
|
|
|
enum CredentialScope {
|
|
USER
|
|
WORKSPACE
|
|
SYSTEM
|
|
}
|
|
|
|
enum TerminalSessionStatus {
|
|
ACTIVE
|
|
CLOSED
|
|
}
|
|
|
|
// ============================================
|
|
// 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")
|
|
federatedIdentities FederatedIdentity[]
|
|
llmUsageLogs LlmUsageLog[] @relation("UserLlmUsageLogs")
|
|
userCredentials UserCredential[] @relation("UserCredentials")
|
|
|
|
@@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("{}")
|
|
matrixRoomId String? @map("matrix_room_id")
|
|
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[]
|
|
federationConnections FederationConnection[]
|
|
federationMessages FederationMessage[]
|
|
federationEventSubscriptions FederationEventSubscription[]
|
|
llmUsageLogs LlmUsageLog[]
|
|
userCredentials UserCredential[]
|
|
terminalSessions TerminalSession[]
|
|
|
|
@@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?
|
|
encryptionVersion String? @map("encryption_version") @db.VarChar(20)
|
|
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])
|
|
@@index([encryptionVersion])
|
|
@@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")
|
|
}
|
|
|
|
// ============================================
|
|
// USER CREDENTIALS MODULE
|
|
// ============================================
|
|
|
|
model UserCredential {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String @map("user_id") @db.Uuid
|
|
workspaceId String? @map("workspace_id") @db.Uuid
|
|
|
|
// Identity
|
|
name String
|
|
provider String // "github", "openai", "custom"
|
|
type CredentialType
|
|
scope CredentialScope @default(USER)
|
|
|
|
// Encrypted storage
|
|
encryptedValue String @map("encrypted_value") @db.Text
|
|
maskedValue String? @map("masked_value") @db.VarChar(20)
|
|
|
|
// Metadata
|
|
description String? @db.Text
|
|
expiresAt DateTime? @map("expires_at") @db.Timestamptz
|
|
lastUsedAt DateTime? @map("last_used_at") @db.Timestamptz
|
|
metadata Json @default("{}")
|
|
|
|
// Status
|
|
isActive Boolean @default(true) @map("is_active")
|
|
rotatedAt DateTime? @map("rotated_at") @db.Timestamptz
|
|
|
|
// Audit
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
|
|
// Relations
|
|
user User @relation("UserCredentials", fields: [userId], references: [id], onDelete: Cascade)
|
|
workspace Workspace? @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, workspaceId, provider, name])
|
|
@@index([userId])
|
|
@@index([workspaceId])
|
|
@@index([userId, scope])
|
|
@@index([workspaceId, scope])
|
|
@@index([scope, isActive])
|
|
@@map("user_credentials")
|
|
}
|
|
|
|
// ============================================
|
|
// 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?
|
|
|
|
// Full-text search vector (automatically maintained by trigger)
|
|
searchVector Unsupported("tsvector")? @map("search_vector")
|
|
|
|
// 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])
|
|
// Note: GIN index on searchVector created via migration (not supported in Prisma schema)
|
|
@@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")
|
|
llmUsageLogs LlmUsageLog[] @relation("LlmUsageLogs")
|
|
|
|
@@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")
|
|
version Int @default(1) // Optimistic locking version
|
|
|
|
// 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])
|
|
@@index([jobId, timestamp])
|
|
@@map("job_events")
|
|
}
|
|
|
|
// ============================================
|
|
// FEDERATION MODULE
|
|
// ============================================
|
|
|
|
model Instance {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
instanceId String @unique @map("instance_id") // Unique identifier for federation
|
|
name String
|
|
url String
|
|
publicKey String @map("public_key") @db.Text
|
|
privateKey String @map("private_key") @db.Text // AES-256-GCM encrypted with ENCRYPTION_KEY
|
|
|
|
// Capabilities and metadata
|
|
capabilities Json @default("{}")
|
|
metadata Json @default("{}")
|
|
|
|
// Timestamps
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
|
|
@@map("instances")
|
|
}
|
|
|
|
model FederationConnection {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
workspaceId String @map("workspace_id") @db.Uuid
|
|
|
|
// Remote instance details
|
|
remoteInstanceId String @map("remote_instance_id")
|
|
remoteUrl String @map("remote_url")
|
|
remotePublicKey String @map("remote_public_key") @db.Text
|
|
remoteCapabilities Json @default("{}") @map("remote_capabilities")
|
|
|
|
// Connection status
|
|
status FederationConnectionStatus @default(PENDING)
|
|
|
|
// Metadata
|
|
metadata Json @default("{}")
|
|
|
|
// Timestamps
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
connectedAt DateTime? @map("connected_at") @db.Timestamptz
|
|
disconnectedAt DateTime? @map("disconnected_at") @db.Timestamptz
|
|
|
|
// Relations
|
|
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
|
messages FederationMessage[]
|
|
eventSubscriptions FederationEventSubscription[]
|
|
|
|
@@unique([workspaceId, remoteInstanceId])
|
|
@@index([workspaceId])
|
|
@@index([workspaceId, status])
|
|
@@index([remoteInstanceId])
|
|
@@map("federation_connections")
|
|
}
|
|
|
|
model FederatedIdentity {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
localUserId String @map("local_user_id") @db.Uuid
|
|
remoteUserId String @map("remote_user_id")
|
|
remoteInstanceId String @map("remote_instance_id")
|
|
oidcSubject String @map("oidc_subject")
|
|
email String
|
|
metadata Json @default("{}")
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
|
|
user User @relation(fields: [localUserId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([localUserId, remoteInstanceId])
|
|
@@index([localUserId])
|
|
@@index([remoteInstanceId])
|
|
@@index([oidcSubject])
|
|
@@map("federated_identities")
|
|
}
|
|
|
|
model FederationMessage {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
workspaceId String @map("workspace_id") @db.Uuid
|
|
connectionId String @map("connection_id") @db.Uuid
|
|
|
|
// Message metadata
|
|
messageType FederationMessageType @map("message_type")
|
|
messageId String @unique @map("message_id") // UUID for deduplication
|
|
correlationId String? @map("correlation_id") // For request/response tracking
|
|
|
|
// Message content
|
|
query String? @db.Text
|
|
commandType String? @map("command_type") @db.Text
|
|
eventType String? @map("event_type") @db.Text // For EVENT messages
|
|
payload Json? @default("{}")
|
|
response Json? @default("{}")
|
|
|
|
// Status tracking
|
|
status FederationMessageStatus @default(PENDING)
|
|
error String? @db.Text
|
|
|
|
// Security
|
|
signature String @db.Text
|
|
|
|
// Timestamps
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
deliveredAt DateTime? @map("delivered_at") @db.Timestamptz
|
|
|
|
// Relations
|
|
connection FederationConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([workspaceId])
|
|
@@index([connectionId])
|
|
@@index([messageId])
|
|
@@index([correlationId])
|
|
@@index([eventType])
|
|
@@map("federation_messages")
|
|
}
|
|
|
|
model FederationEventSubscription {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
workspaceId String @map("workspace_id") @db.Uuid
|
|
connectionId String @map("connection_id") @db.Uuid
|
|
|
|
// Event subscription details
|
|
eventType String @map("event_type")
|
|
metadata Json @default("{}")
|
|
isActive Boolean @default(true) @map("is_active")
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz
|
|
|
|
// Relations
|
|
connection FederationConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([workspaceId, connectionId, eventType])
|
|
@@index([workspaceId])
|
|
@@index([connectionId])
|
|
@@index([eventType])
|
|
@@index([workspaceId, isActive])
|
|
@@map("federation_event_subscriptions")
|
|
}
|
|
|
|
// ============================================
|
|
// LLM USAGE TRACKING MODULE
|
|
// ============================================
|
|
|
|
model LlmUsageLog {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
workspaceId String @map("workspace_id") @db.Uuid
|
|
userId String @map("user_id") @db.Uuid
|
|
|
|
// LLM provider and model info
|
|
provider String @db.VarChar(50)
|
|
model String @db.VarChar(100)
|
|
providerInstanceId String? @map("provider_instance_id") @db.Uuid
|
|
|
|
// Token usage
|
|
promptTokens Int @default(0) @map("prompt_tokens")
|
|
completionTokens Int @default(0) @map("completion_tokens")
|
|
totalTokens Int @default(0) @map("total_tokens")
|
|
|
|
// Optional cost (in cents for precision)
|
|
costCents Float? @map("cost_cents")
|
|
|
|
// Task type for routing analytics
|
|
taskType String? @map("task_type") @db.VarChar(50)
|
|
|
|
// Optional reference to conversation/session
|
|
conversationId String? @map("conversation_id") @db.Uuid
|
|
|
|
// Duration in milliseconds
|
|
durationMs Int? @map("duration_ms")
|
|
|
|
// Timestamp
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
|
|
// Relations
|
|
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
|
user User @relation("UserLlmUsageLogs", fields: [userId], references: [id], onDelete: Cascade)
|
|
llmProviderInstance LlmProviderInstance? @relation("LlmUsageLogs", fields: [providerInstanceId], references: [id], onDelete: SetNull)
|
|
|
|
@@index([workspaceId])
|
|
@@index([workspaceId, createdAt])
|
|
@@index([userId])
|
|
@@index([provider])
|
|
@@index([model])
|
|
@@index([providerInstanceId])
|
|
@@index([taskType])
|
|
@@index([conversationId])
|
|
@@map("llm_usage_logs")
|
|
}
|
|
|
|
// ============================================
|
|
// TERMINAL MODULE
|
|
// ============================================
|
|
|
|
model TerminalSession {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
workspaceId String @map("workspace_id") @db.Uuid
|
|
name String @default("Terminal")
|
|
status TerminalSessionStatus @default(ACTIVE)
|
|
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz
|
|
closedAt DateTime? @map("closed_at") @db.Timestamptz
|
|
|
|
// Relations
|
|
workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([workspaceId])
|
|
@@index([workspaceId, status])
|
|
@@map("terminal_sessions")
|
|
}
|