datasource db { provider = "postgresql" url = env("DATABASE_URL") directUrl = env("DIRECT_URL") } generator client { provider = "prisma-client-js" output = "../generated/prisma" binaryTargets = ["native", "debian-openssl-1.1.x"] previewFeatures = ["metrics"] } model User { id String @id @default(cuid()) email String @unique authenticationMethod AuthenticationMethod authenticationProfile Json? authenticationExtraParams Json? authIdentifier String? @unique displayName String? name String? avatarUrl String? admin Boolean @default(false) /// Preferences for the dashboard dashboardPreferences Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt /// @deprecated isOnCloudWaitlist Boolean @default(false) /// @deprecated featureCloud Boolean @default(false) /// @deprecated isOnHostedRepoWaitlist Boolean @default(false) marketingEmails Boolean @default(true) confirmedBasicDetails Boolean @default(false) referralSource String? onboardingData Json? orgMemberships OrgMember[] sentInvites OrgMemberInvite[] mfaEnabledAt DateTime? mfaSecretReference SecretReference? @relation(fields: [mfaSecretReferenceId], references: [id]) mfaSecretReferenceId String? /// Hash of the last used code to prevent replay attacks mfaLastUsedCode String? /// Maximum session lifetime in seconds for this user. Default = 1 year (Gregorian). /// May be further restricted by Organization.maxSessionDuration on any of the user's orgs. sessionDuration Int @default(31556952) /// Absolute timestamp at which the user's current session must end. Written /// at login (and any time the effective duration is recomputed) and shortened /// when an admin lowers an org cap. Read on requireUser / getUser; null is /// treated as "no enforced deadline" (legacy sessions from before this /// shipped). Read-on-demand has zero per-request DB cost — the value /// piggybacks on the User fetch that requireUser already does. nextSessionEnd DateTime? invitationCode InvitationCode? @relation(fields: [invitationCodeId], references: [id]) invitationCodeId String? personalAccessTokens PersonalAccessToken[] deployments WorkerDeployment[] backupCodes MfaBackupCode[] bulkActions BulkActionGroup[] impersonationsPerformed ImpersonationAuditLog[] @relation("ImpersonationAdmin") impersonationsReceived ImpersonationAuditLog[] @relation("ImpersonationTarget") customerQueries CustomerQuery[] metricsDashboards MetricsDashboard[] platformNotifications PlatformNotification[] platformNotificationInteractions PlatformNotificationInteraction[] } model MfaBackupCode { id String @id @default(cuid()) /// Hash of the actual code code String user User @relation(fields: [userId], references: [id]) userId String usedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([userId, code]) } // @deprecated This model is no longer used as the Cloud is out of private beta // Leaving it here for now for historical reasons model InvitationCode { id String @id @default(cuid()) code String @unique users User[] createdAt DateTime @default(now()) } enum AuthenticationMethod { GITHUB MAGIC_LINK GOOGLE SSO } /// Used to generate PersonalAccessTokens, they're one-time use model AuthorizationCode { id String @id @default(cuid()) code String @unique personalAccessToken PersonalAccessToken? @relation(fields: [personalAccessTokenId], references: [id], onDelete: Cascade, onUpdate: Cascade) personalAccessTokenId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } // Used by User's to perform API actions model PersonalAccessToken { id String @id @default(cuid()) /// If generated by the CLI this will be "cli", otherwise user-provided name String /// This is the token encrypted using the ENCRYPTION_KEY encryptedToken Json /// This is shown in the UI, with ******** obfuscatedToken String /// This is used to find the token in the database hashedToken String @unique user User @relation(fields: [userId], references: [id]) userId String revokedAt DateTime? lastAccessedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt authorizationCodes AuthorizationCode[] } enum OrganizationAccessTokenType { USER SYSTEM } model OrganizationAccessToken { id String @id @default(cuid()) /// User-provided name for the token name String /// Used to differentiate between user-generated and system-generated tokens type OrganizationAccessTokenType @default(USER) /// This is used to find the token in the database hashedToken String @unique organization Organization @relation(fields: [organizationId], references: [id]) organizationId String /// Optional expiration date for the token expiresAt DateTime? revokedAt DateTime? lastAccessedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, type, createdAt(sort: Desc)]) } model Organization { id String @id @default(cuid()) slug String @unique title String maximumExecutionTimePerRunInMs Int @default(900000) // 15 minutes maximumConcurrencyLimit Int @default(10) /// This is deprecated and will be removed in the future maximumSchedulesLimit Int @default(5) maximumDevQueueSize Int? maximumDeployedQueueSize Int? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? companySize String? onboardingData Json? avatar Json? runsEnabled Boolean @default(true) isActivated Boolean @default(false) @map("v3Enabled") /// @deprecated v2Enabled Boolean @default(false) /// @deprecated v2MarqsEnabled Boolean @default(false) /// @deprecated hasRequestedV3 Boolean @default(false) environments RuntimeEnvironment[] apiRateLimiterConfig Json? realtimeRateLimiterConfig Json? batchRateLimitConfig Json? batchQueueConcurrencyConfig Json? featureFlags Json? maximumProjectCount Int @default(25) /// Maximum session lifetime in seconds for members of this organization. /// When set, caps each member's User.sessionDuration. The most restrictive cap across /// all of a user's orgs (excluding nulls) wins. maxSessionDuration Int? projects Project[] members OrgMember[] invites OrgMemberInvite[] organizationIntegrations OrganizationIntegration[] organizationAccessTokens OrganizationAccessToken[] workerGroups WorkerInstanceGroup[] workerInstances WorkerInstance[] githubAppInstallations GithubAppInstallation[] customerQueries CustomerQuery[] metricsDashboards MetricsDashboard[] prompts Prompt[] platformNotifications PlatformNotification[] errorGroupStates ErrorGroupState[] /// S2 basin that holds this org's realtime streams. Null until the /// per-org basin has been provisioned (OSS / s2-lite installs leave /// it null forever; reads fall back to the global basin env var). /// Set once at provisioning time; retention is reconfigured in-place /// when the org's plan changes. streamBasinName String? } model OrgMember { id String @id @default(cuid()) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) userId String role OrgMemberRole @default(MEMBER) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt environments RuntimeEnvironment[] @@unique([organizationId, userId]) } enum OrgMemberRole { ADMIN MEMBER } model OrgMemberInvite { id String @id @default(cuid()) token String @unique @default(cuid()) email String role OrgMemberRole @default(MEMBER) /// Optional RBAC role to assign on invite acceptance. When set, the /// accept-invite flow calls the loaded RBAC plugin's setUserRole with /// this id after creating the OrgMember. Null = legacy behaviour, the /// runtime fallback derives the role from `role` above. /// /// Plain text (not an FK) — the RBAC plugin's RbacRole table lives on /// a separate schema (Drizzle, not Prisma) so we can't model the FK /// here. Validation happens at write time (action) and read time /// (acceptInvite). rbacRoleId String? organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String inviter User @relation(fields: [inviterId], references: [id], onDelete: Cascade, onUpdate: Cascade) inviterId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([organizationId, email]) } model RuntimeEnvironment { id String @id @default(cuid()) slug String apiKey String @unique /// @deprecated was for v2 pkApiKey String @unique type RuntimeEnvironmentType @default(DEVELOPMENT) // Preview branches /// If true, this environment has branches and is treated differently in the dashboard/API /// NB: this flag is NOT used for Development branches, instead (type, parentEnvironmentId) = (DEVELOPMENT, NULL) is used isBranchableEnvironment Boolean @default(false) branchName String? parentEnvironment RuntimeEnvironment? @relation("parentEnvironment", fields: [parentEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) parentEnvironmentId String? childEnvironments RuntimeEnvironment[] @relation("parentEnvironment") // This is GitMeta type git Json? /// When set API calls will fail archivedAt DateTime? ///A memorable code for the environment shortcode String maximumConcurrencyLimit Int @default(5) concurrencyLimitBurstFactor Decimal @default("2.00") @db.Decimal(4, 2) paused Boolean @default(false) pauseSource EnvironmentPauseSource? autoEnableInternalSources Boolean @default(true) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String //when the org member is deleted, it will keep the environment but set it to null orgMember OrgMember? @relation(fields: [orgMemberId], references: [id], onDelete: SetNull, onUpdate: Cascade) orgMemberId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt /// Allows us to customize the built-in environment variables for a specific environment, like TRIGGER_OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT builtInEnvironmentVariableOverrides Json? tunnelId String? backgroundWorkers BackgroundWorker[] backgroundWorkerTasks BackgroundWorkerTask[] taskRuns TaskRun[] taskQueues TaskQueue[] environmentVariableValues EnvironmentVariableValue[] checkpoints Checkpoint[] workerDeployments WorkerDeployment[] workerDeploymentPromotions WorkerDeploymentPromotion[] taskRunAttempts TaskRunAttempt[] CheckpointRestoreEvent CheckpointRestoreEvent[] taskScheduleInstances TaskScheduleInstance[] alerts ProjectAlert[] sessions RuntimeEnvironmentSession[] currentSession RuntimeEnvironmentSession? @relation("currentSession", fields: [currentSessionId], references: [id], onDelete: SetNull, onUpdate: Cascade) currentSessionId String? taskRunNumberCounter TaskRunNumberCounter[] taskRunCheckpoints TaskRunCheckpoint[] waitpoints Waitpoint[] workerInstances WorkerInstance[] waitpointTags WaitpointTag[] BulkActionGroup BulkActionGroup[] customerQueries CustomerQuery[] prompts Prompt[] playgroundConversations PlaygroundConversation[] errorGroupStates ErrorGroupState[] taskIdentifiers TaskIdentifier[] revokedApiKeys RevokedApiKey[] @@unique([projectId, slug, orgMemberId]) @@unique([projectId, shortcode]) @@index([parentEnvironmentId]) @@index([projectId]) @@index([organizationId]) } /// Records of previously-valid API keys that are still accepted for authentication /// during a grace window after rotation. Extend or end the grace period by updating `expiresAt`. model RevokedApiKey { id String @id @default(cuid()) apiKey String runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String expiresAt DateTime createdAt DateTime @default(now()) @@index([apiKey]) @@index([runtimeEnvironmentId]) } enum RuntimeEnvironmentType { PRODUCTION STAGING DEVELOPMENT PREVIEW } enum EnvironmentPauseSource { BILLING_LIMIT } model Project { id String @id @default(cuid()) slug String @unique name String externalRef String @unique organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? version ProjectVersion @default(V2) engine RunEngineVersion @default(V1) builderProjectId String? workerGroups WorkerInstanceGroup[] workers WorkerInstance[] defaultWorkerGroup WorkerInstanceGroup? @relation("ProjectDefaultWorkerGroup", fields: [defaultWorkerGroupId], references: [id]) defaultWorkerGroupId String? /// The master queues they are allowed to use (impacts what they can set as default and trigger runs with) allowedWorkerQueues String[] @default([]) @map("allowedMasterQueues") environments RuntimeEnvironment[] backgroundWorkers BackgroundWorker[] backgroundWorkerTasks BackgroundWorkerTask[] taskRuns TaskRun[] runTags TaskRunTag[] taskQueues TaskQueue[] environmentVariables EnvironmentVariable[] checkpoints Checkpoint[] WorkerDeployment WorkerDeployment[] CheckpointRestoreEvent CheckpointRestoreEvent[] taskSchedules TaskSchedule[] alertChannels ProjectAlertChannel[] alerts ProjectAlert[] alertStorages ProjectAlertStorage[] bulkActionGroups BulkActionGroup[] BackgroundWorkerFile BackgroundWorkerFile[] waitpoints Waitpoint[] taskRunWaitpoints TaskRunWaitpoint[] taskRunCheckpoints TaskRunCheckpoint[] waitpointTags WaitpointTag[] connectedGithubRepository ConnectedGithubRepository? organizationProjectIntegration OrganizationProjectIntegration[] customerQueries CustomerQuery[] playgroundConversations PlaygroundConversation[] buildSettings Json? onboardingData Json? taskScheduleInstances TaskScheduleInstance[] metricsDashboards MetricsDashboard[] llmModels LlmModel[] prompts Prompt[] platformNotifications PlatformNotification[] errorGroupStates ErrorGroupState[] taskIdentifiers TaskIdentifier[] } enum ProjectVersion { V2 V3 } model SecretReference { id String @id @default(cuid()) key String @unique provider SecretStoreProvider @default(DATABASE) environmentVariableValues EnvironmentVariableValue[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt OrganizationIntegration OrganizationIntegration[] User User[] } enum SecretStoreProvider { DATABASE AWS_PARAM_STORE } model SecretStore { key String @unique value Json version String @default("1") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([key(ops: raw("text_pattern_ops"))], type: BTree) } model DataMigration { id String @id @default(cuid()) name String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt completedAt DateTime? } // ==================================================== // v3 Models // ==================================================== model BackgroundWorker { id String @id @default(cuid()) friendlyId String @unique engine RunEngineVersion @default(V1) contentHash String sdkVersion String @default("unknown") cliVersion String @default("unknown") project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String version String metadata Json runtime String? runtimeVersion String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tasks BackgroundWorkerTask[] attempts TaskRunAttempt[] lockedRuns TaskRun[] files BackgroundWorkerFile[] queues TaskQueue[] promptVersions PromptVersion[] deployment WorkerDeployment? workerGroup WorkerInstanceGroup? @relation(fields: [workerGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade) workerGroupId String? supportsLazyAttempts Boolean @default(false) taskIdentifiers TaskIdentifier[] @@unique([projectId, runtimeEnvironmentId, version]) @@index([runtimeEnvironmentId]) // Get the latest worker for a given environment @@index([runtimeEnvironmentId, createdAt(sort: Desc)]) } model BackgroundWorkerFile { id String @id @default(cuid()) friendlyId String @unique filePath String contentHash String contents Bytes project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String backgroundWorkers BackgroundWorker[] tasks BackgroundWorkerTask[] createdAt DateTime @default(now()) @@unique([projectId, contentHash]) } model Prompt { id String @id @default(cuid()) friendlyId String @unique @map("friendly_id") slug String description String? type String @default("text") // "text" | "chat" organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String filePath String? exportName String? variableSchema Json? defaultModel String? defaultConfig Json? tags String[] @default([]) archivedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt versions PromptVersion[] @@unique([projectId, runtimeEnvironmentId, slug]) @@index([projectId]) @@index([runtimeEnvironmentId]) @@map("prompts") } model PromptVersion { id String @id @default(cuid()) version Int prompt Prompt @relation(fields: [promptId], references: [id], onDelete: Cascade, onUpdate: Cascade) promptId String textContent String? chatContent Json? model String? config Json? source String // "deploy" | "dashboard" | "api" commitMessage String? contentHash String labels String[] @default([]) worker BackgroundWorker? @relation(fields: [workerId], references: [id]) workerId String? createdBy String? createdAt DateTime @default(now()) @@unique([promptId, version]) @@index([promptId]) @@index([contentHash]) @@map("prompt_versions") } model BackgroundWorkerTask { id String @id @default(cuid()) slug String description String? friendlyId String @unique filePath String exportName String? worker BackgroundWorker @relation(fields: [workerId], references: [id], onDelete: Cascade, onUpdate: Cascade) workerId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String file BackgroundWorkerFile? @relation(fields: [fileId], references: [id], onDelete: Cascade, onUpdate: Cascade) fileId String? runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt attempts TaskRunAttempt[] runs TaskRun[] queueConfig Json? retryConfig Json? machineConfig Json? queueId String? queue TaskQueue? @relation(fields: [queueId], references: [id], onDelete: SetNull, onUpdate: Cascade) maxDurationInSeconds Int? ttl String? triggerSource TaskTriggerSource @default(STANDARD) /// Extra task configuration JSON. Shape depends on triggerSource. /// AGENT: { type: "ai-sdk-chat" } config Json? payloadSchema Json? @@unique([workerId, slug]) // Quick lookup of task identifiers @@index([projectId, slug]) @@index([runtimeEnvironmentId, projectId]) @@index([runtimeEnvironmentId, slug, triggerSource]) } enum TaskTriggerSource { STANDARD SCHEDULED AGENT } model PlaygroundConversation { id String @id @default(cuid()) /// The chat session ID used by the transport chatId String /// User-editable conversation title (auto-generated from first message) title String @default("New conversation") /// Which agent this conversation is with agentSlug String /// The current active run backing this conversation (null if no run yet) runId String? run TaskRun? @relation(fields: [runId], references: [id], onDelete: SetNull, onUpdate: Cascade) /// The client data JSON used for this conversation clientData Json? /// Accumulated UIMessages from completed turns (for resume without stream replay) messages Json? /// Last SSE event ID — resume from this position to avoid replaying old turns lastEventId String? project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String /// The user who started this conversation userId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([chatId, runtimeEnvironmentId]) @@index([runtimeEnvironmentId, agentSlug, updatedAt(sort: Desc)]) @@index([userId, runtimeEnvironmentId]) } /// Durable, typed, bidirectional I/O primitive. Owns two S2 streams (.out / .in). /// The row is essentially static — no status, no counters, no pointers. No /// foreign keys: project/runtimeEnvironment/organization ids are plain /// scalar columns (matches TaskRun pattern). List-style queries are served /// from ClickHouse, not Postgres, so only point-lookup indexes live here. model Session { id String @id @default(cuid()) friendlyId String @unique /// User-supplied identifier scoped to the environment. Used for /// idempotent upsert and for resolving sessions via the public API. externalId String? /// Plain string — intentionally not an enum. type String /// Denormalized scoping columns — no FK relations. projectId String runtimeEnvironmentId String environmentType RuntimeEnvironmentType organizationId String /// Task this session triggers runs against. Required — Sessions are /// task-bound: creating a session also triggers its first run, and /// every subsequent re-trigger uses this same identifier. taskIdentifier String /// Trigger config used for every run this session schedules. Shape /// (validated at the route layer, opaque to the DB): /// { basePayload: object, machine?: string, queue?: string, /// tags?: string[], maxAttempts?: number, /// idleTimeoutInSeconds?: number } /// `basePayload` carries the customer's client-data; runtime fields /// (chatId, messages, trigger) are merged at trigger time. triggerConfig Json /// Whether this session was created from the Test/playground UI rather /// than by a real chat.agent() trigger. Mirrors TaskRun.isTest. isTest Boolean @default(false) tags String[] @default([]) metadata Json? /// Live run pointer — non-FK so run deletion never cascades. Can lag /// reality; the `.in/append` handler re-checks the snapshot status /// before reusing it. currentRunId String? /// Monotonic counter used for optimistic locking on `currentRunId` /// swaps. Bumped atomically alongside any update that changes /// `currentRunId`. currentRunVersion Int @default(0) /// Terminal markers — written once, never flipped back. closedAt DateTime? closedReason String? expiresAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt /// S2 basin where this session's stream pair lives. Stamped at create /// time from `Organization.streamBasinName` so reads can resolve the /// basin without joining org. Null when the org has no per-org basin /// (OSS, or pre-backfill); reads fall back to the global basin. streamBasinName String? /// Storage URI (with protocol prefix) for this session's chat.agent /// snapshot blob. Set on first snapshot write. Null = pre-column session, /// fall back to computed default path. chatSnapshotStoragePath String? runs SessionRun[] /// Idempotency: `(env, externalId)` uniquely identifies a session. /// PostgreSQL treats NULLs as distinct, so `externalId=NULL` rows never collide. @@unique([runtimeEnvironmentId, externalId]) @@index([expiresAt]) @@index([currentRunId]) } /// Historical record of every run a Session has owned. Append-only — /// rows are inserted on each `ensureRunForSession` claim, never updated. /// Lets us reconstruct the run timeline of a chat for debugging / /// dashboard surfaces. The relation cascades on Session delete (tied to /// the session lifecycle) but `runId` is a plain string column with no /// FK to TaskRun so run pruning is independent. model SessionRun { id String @id @default(cuid()) sessionId String /// TaskRun.id (no FK — runs may be archived independently of session history) runId String @unique /// One of: "initial" | "continuation" | "upgrade" | "manual". /// Plain string for forward-compat with future trigger reasons. reason String triggeredAt DateTime @default(now()) session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade) @@index([sessionId]) } model TaskRun { id String @id @default(cuid()) number Int @default(0) friendlyId String @unique engine RunEngineVersion @default(V1) status TaskRunStatus @default(PENDING) statusReason String? idempotencyKey String? idempotencyKeyExpiresAt DateTime? /// Stores the user-provided key and scope: { key: string, scope: "run" | "attempt" | "global" } idempotencyKeyOptions Json? /// Debounce options: { key: string, delay: string, createdAt: Date } debounce Json? taskIdentifier String isTest Boolean @default(false) payload String payloadType String @default("application/json") context Json? traceContext Json? traceId String spanId String runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String environmentType RuntimeEnvironmentType? project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String organizationId String? // The specific queue this run is in queue String // The queueId is set when the run is locked to a specific queue lockedQueueId String? /// The main queue that this run is part of workerQueue String @default("main") @map("masterQueue") /// User-facing geo region, stamped at trigger; workerQueue is where it actually ran. region String? /// @deprecated secondaryMasterQueue String? /// From engine v2+ this will be defined after a run has been dequeued (starting at 1) attemptNumber Int? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt attempts TaskRunAttempt[] @relation("attempts") /// Denormized column that holds the raw tags runTags String[] /// Denormalized version of the background worker task taskVersion String? sdkVersion String? cliVersion String? checkpoints Checkpoint[] /// startedAt marks the point at which a run is dequeued from MarQS startedAt DateTime? /// executedAt is set when the first attempt is about to execute executedAt DateTime? completedAt DateTime? machinePreset String? usageDurationMs Int @default(0) costInCents Float @default(0) baseCostInCents Float @default(0) lockedAt DateTime? lockedBy BackgroundWorkerTask? @relation(fields: [lockedById], references: [id]) lockedById String? lockedToVersion BackgroundWorker? @relation(fields: [lockedToVersionId], references: [id]) lockedToVersionId String? /// The "priority" of the run. This is just a negative offset in ms for the queue timestamp /// E.g. a value of 60_000 would put the run into the queue 60s ago. priorityMs Int @default(0) concurrencyKey String? delayUntil DateTime? queuedAt DateTime? ttl String? expiredAt DateTime? maxAttempts Int? lockedRetryConfig Json? /// optional token that can be used to authenticate the task run oneTimeUseToken String? ///When this run is finished, the waitpoint will be marked as completed associatedWaitpoint Waitpoint? @relation("CompletingRun") ///If there are any blocked waitpoints, the run won't be executed blockedByWaitpoints TaskRunWaitpoint[] /// All waitpoints that blocked this run at some point, used for display purposes connectedWaitpoints Waitpoint[] @relation("WaitpointRunConnections") /// Where the logs are stored taskEventStore String @default("taskEvent") queueTimestamp DateTime? batchItems BatchTaskRunItem[] dependency TaskRunDependency? CheckpointRestoreEvent CheckpointRestoreEvent[] executionSnapshots TaskRunExecutionSnapshot[] scheduleInstanceId String? scheduleId String? sourceBulkActionItems BulkActionItem[] @relation("SourceActionItemRun") destinationBulkActionItems BulkActionItem[] @relation("DestinationActionItemRun") bulkActionGroupIds String[] @default([]) logsDeletedAt DateTime? replayedFromTaskRunFriendlyId String? /// This represents the original task that that was triggered outside of a Trigger.dev task rootTaskRun TaskRun? @relation("TaskRootRun", fields: [rootTaskRunId], references: [id], onDelete: SetNull, onUpdate: NoAction) rootTaskRunId String? /// The root run will have a list of all the descendant runs, children, grand children, etc. descendantRuns TaskRun[] @relation("TaskRootRun") /// The immediate parent run of this task run parentTaskRun TaskRun? @relation("TaskParentRun", fields: [parentTaskRunId], references: [id], onDelete: SetNull, onUpdate: NoAction) parentTaskRunId String? /// The immediate child runs of this task run childRuns TaskRun[] @relation("TaskParentRun") /// The immediate parent attempt of this task run parentTaskRunAttempt TaskRunAttempt? @relation("TaskParentRunAttempt", fields: [parentTaskRunAttemptId], references: [id], onDelete: SetNull, onUpdate: NoAction) parentTaskRunAttemptId String? /// The batch run that this task run is a part of batch BatchTaskRun? @relation(fields: [batchId], references: [id], onDelete: SetNull, onUpdate: NoAction) batchId String? /// whether or not the task run was created because of a triggerAndWait for batchTriggerAndWait resumeParentOnCompletion Boolean @default(false) /// The depth of this task run in the task run hierarchy depth Int @default(0) /// The span ID of the "trigger" span in the parent task run parentSpanId String? /// Holds the state of the run chain for deadlock detection runChainState Json? /// seed run metadata seedMetadata String? seedMetadataType String @default("application/json") /// Run metadata metadata String? metadataType String @default("application/json") metadataVersion Int @default(1) /// Structured annotations: triggerSource, triggerAction, rootTriggerSource, rootScheduleId annotations Json? /// Whether the latest attempt was a warm start. Null until first attempt starts. isWarmStart Boolean? /// Run output output String? outputType String @default("application/json") /// Run error error Json? /// Organization's billing plan type (cached for fallback when billing API fails) planType String? maxDurationInSeconds Int? /// The version of the realtime streams implementation used by the run realtimeStreamsVersion String @default("v1") /// Store the stream keys that are being used by the run realtimeStreams String[] @default([]) /// S2 basin where this run's realtime streams live. Stamped at create /// time from `Organization.streamBasinName` so reads can resolve the /// basin without joining org. Null when the org has no per-org basin /// (OSS, or pre-backfill); reads fall back to the global basin. streamBasinName String? playgroundConversations PlaygroundConversation[] @@unique([oneTimeUseToken]) @@unique([runtimeEnvironmentId, taskIdentifier, idempotencyKey]) // Finding child runs @@index([parentTaskRunId]) // Run page inspector @@index([spanId]) @@index([parentSpanId]) // Finding runs in a batch @@index([runTags(ops: ArrayOps)], type: Gin) @@index([runtimeEnvironmentId, batchId]) @@index([runtimeEnvironmentId, createdAt(sort: Desc)]) @@index([createdAt], type: Brin) } model TaskRunTemplate { id String @id @default(cuid()) taskSlug String triggerSource TaskTriggerSource label String payload String? payloadType String @default("application/json") metadata String? metadataType String @default("application/json") queue String? concurrencyKey String? ttlSeconds Int? maxAttempts Int? maxDurationSeconds Int? tags String[] machinePreset String? projectId String organizationId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([projectId, taskSlug, triggerSource, createdAt(sort: Desc)]) } enum TaskRunStatus { /// /// NON-FINAL STATUSES /// /// Task has been scheduled to run in the future DELAYED /// Task is waiting to be executed by a worker PENDING /// The run is pending a version update because it cannot execute without additional information (task, queue, etc.). Replaces WAITING_FOR_DEPLOY PENDING_VERSION /// Task hasn't been deployed yet but is waiting to be executed. Deprecated in favor of PENDING_VERSION WAITING_FOR_DEPLOY /// Task has been dequeued from the queue but is not yet executing DEQUEUED /// Task is currently being executed by a worker EXECUTING /// Task has been paused by the system, and will be resumed by the system WAITING_TO_RESUME /// Task has failed and is waiting to be retried RETRYING_AFTER_FAILURE /// Task has been paused by the user, and can be resumed by the user PAUSED /// /// FINAL STATUSES /// /// Task has been canceled by the user CANCELED /// Task was interrupted during execution, mostly this happens in development environments INTERRUPTED /// Task has been completed successfully COMPLETED_SUCCESSFULLY /// Task has been completed with errors COMPLETED_WITH_ERRORS /// Task has failed to complete, due to an error in the system SYSTEM_FAILURE /// Task has crashed and won't be retried, most likely the worker ran out of resources, e.g. memory or storage CRASHED /// Task reached the ttl without being executed EXPIRED /// Task has been timed out when using maxDuration TIMED_OUT } enum RunEngineVersion { /// The original version that uses marqs v1 and Graphile V1 V2 } /// Used by the RunEngine during TaskRun execution /// It has the required information to transactionally progress a run through states, /// and prevent side effects like heartbeats failing a run that has progressed. /// It is optimised for performance and is designed to be cleared at some point, /// so there are no cascading relationships to other models. model TaskRunExecutionSnapshot { id String @id @default(cuid()) /// This should always be 2+ (V1 didn't use the run engine or snapshots) engine RunEngineVersion @default(V2) /// The execution status executionStatus TaskRunExecutionStatus /// For debugging description String /// We store invalid snapshots as a record of the run state when we tried to move isValid Boolean @default(true) error String? /// The previous snapshot ID previousSnapshotId String? /// Run runId String run TaskRun @relation(fields: [runId], references: [id]) runStatus TaskRunStatus // Batch batchId String? /// This is the current run attempt number. Users can define how many attempts they want for a run. attemptNumber Int? /// Environment environmentId String environmentType RuntimeEnvironmentType projectId String organizationId String /// Waitpoints that have been completed for this execution completedWaitpoints Waitpoint[] @relation("completedWaitpoints") /// An array of waitpoint IDs in the correct order, used for batches completedWaitpointOrder String[] /// Checkpoint checkpointId String? checkpoint TaskRunCheckpoint? @relation(fields: [checkpointId], references: [id]) /// Worker workerId String? runnerId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt lastHeartbeatAt DateTime? /// Metadata used by various systems in the run engine metadata Json? /// Used to get the latest valid snapshot quickly @@index([runId, isValid, createdAt(sort: Desc)]) } enum TaskRunExecutionStatus { /// Run has been created RUN_CREATED /// Run is delayed, waiting to be enqueued DELAYED /// Run is in the RunQueue QUEUED /// Run is in the RunQueue, and is also executing. This happens when a run is continued cannot reacquire concurrency QUEUED_EXECUTING /// Run has been pulled from the queue, but isn't executing yet PENDING_EXECUTING /// Run is executing on a worker EXECUTING /// Run is executing on a worker but is waiting for waitpoints to complete EXECUTING_WITH_WAITPOINTS /// Run has been suspended and may be waiting for waitpoints to complete before resuming SUSPENDED /// Run has been scheduled for cancellation PENDING_CANCEL /// Run is finished (success of failure) FINISHED } model TaskRunCheckpoint { id String @id @default(cuid()) friendlyId String @unique type TaskRunCheckpointType location String imageRef String? reason String? metadata String? project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String executionSnapshot TaskRunExecutionSnapshot[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } enum TaskRunCheckpointType { DOCKER KUBERNETES COMPUTE } /// A Waitpoint blocks a run from continuing until it's completed /// If there's a waitpoint blocking a run, it shouldn't be in the queue model Waitpoint { id String @id @default(cuid()) friendlyId String @unique type WaitpointType status WaitpointStatus @default(PENDING) completedAt DateTime? /// If it's an Event type waitpoint, this is the event. It can also be provided for the DATETIME type idempotencyKey String /// If this is true then we can show it in the dashboard/return it from the SDK userProvidedIdempotencyKey Boolean /// If there's a user provided idempotency key, this is the time it expires at idempotencyKeyExpiresAt DateTime? /// If an idempotencyKey is no longer active, we store it here and generate a new one for the idempotencyKey field. /// Clearing an idempotencyKey is useful for debounce or cancelling child runs. /// This is a workaround because Prisma doesn't support partial indexes. inactiveIdempotencyKey String? /// If it's a RUN type waitpoint, this is the associated run completedByTaskRunId String? @unique completedByTaskRun TaskRun? @relation("CompletingRun", fields: [completedByTaskRunId], references: [id], onDelete: SetNull) /// If it's a DATETIME type waitpoint, this is the date. /// If it's a MANUAL waitpoint, this can be set as the `timeout`. completedAfter DateTime? /// If it's a BATCH type waitpoint, this is the associated batch completedByBatchId String? completedByBatch BatchTaskRun? @relation(fields: [completedByBatchId], references: [id], onDelete: SetNull) /// The runs this waitpoint is blocking blockingTaskRuns TaskRunWaitpoint[] /// All runs that have ever been blocked by this waitpoint, used for display purposes connectedRuns TaskRun[] @relation("WaitpointRunConnections") /// When a waitpoint is complete completedExecutionSnapshots TaskRunExecutionSnapshot[] @relation("completedWaitpoints") /// When completed, an output can be stored here output String? outputType String @default("application/json") outputIsError Boolean @default(false) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt /// Denormized column that holds the raw tags /// Denormalized column that holds the raw tags tags String[] /// Quickly find an idempotent waitpoint @@unique([environmentId, idempotencyKey]) /// Quickly find a batch waitpoint @@index([completedByBatchId]) /// Used on the Waitpoint dashboard pages /// Time period filtering @@index([environmentId, type, createdAt(sort: Desc)]) /// Status filtering @@index([environmentId, type, status]) /// For the waitpoint token dashboard page @@index([environmentId, type, id(sort: Desc)]) } enum WaitpointType { RUN DATETIME MANUAL BATCH } enum WaitpointStatus { PENDING COMPLETED } model TaskRunWaitpoint { id String @id @default(cuid()) taskRun TaskRun @relation(fields: [taskRunId], references: [id]) taskRunId String waitpoint Waitpoint @relation(fields: [waitpointId], references: [id]) waitpointId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String /// This span id is completed when the waitpoint is completed. This is used with cached runs (idempotent) spanIdToComplete String? //associated batch batchId String? batch BatchTaskRun? @relation(fields: [batchId], references: [id]) //if there's an associated batch and this isn't set it's for the entire batch //if it is set, it's a specific run in the batch batchIndex Int? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt /// There are two constraints, the one below and also one that Prisma doesn't support /// The second one implemented in SQL only prevents a TaskRun + Waitpoint with a null batchIndex @@unique([taskRunId, waitpointId, batchIndex]) @@index([taskRunId]) @@index([waitpointId]) } model WaitpointTag { id String @id @default(cuid()) name String environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String createdAt DateTime @default(now()) @@unique([environmentId, name]) } model FeatureFlag { id String @id @default(cuid()) key String @unique value Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model WorkerInstance { id String @id @default(cuid()) /// For example "worker-1" name String /// If managed, it will default to the name, e.g. "worker-1" /// If unmanged, it will be prefixed with the deployment ID e.g. "deploy-123-worker-1" resourceIdentifier String metadata Json? workerGroup WorkerInstanceGroup @relation(fields: [workerGroupId], references: [id]) workerGroupId String organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String? project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String? environment RuntimeEnvironment? @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String? deployment WorkerDeployment? @relation(fields: [deploymentId], references: [id], onDelete: SetNull, onUpdate: Cascade) deploymentId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt lastDequeueAt DateTime? lastHeartbeatAt DateTime? @@unique([workerGroupId, resourceIdentifier]) } enum WorkerInstanceGroupType { MANAGED UNMANAGED } enum WorkloadType { CONTAINER MICROVM } model WorkerInstanceGroup { id String @id @default(cuid()) type WorkerInstanceGroupType /// For example "us-east-1" name String /// If managed, it will default to the name, e.g. "us-east-1" /// If unmanged, it will be prefixed with the project ID e.g. "project_1-us-east-1" masterQueue String @unique /// "N. Virginia, USA", "Frankfurt, Germany", etc. Used for display purposes description String? hidden Boolean @default(false) token WorkerGroupToken @relation(fields: [tokenId], references: [id], onDelete: Cascade, onUpdate: Cascade) tokenId String @unique workers WorkerInstance[] backgroundWorkers BackgroundWorker[] defaultForProjects Project[] @relation("ProjectDefaultWorkerGroup") organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String? project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String? cloudProvider String? /// "usa", "europe", etc. Used like a pseudo enum for things like flags location String? staticIPs String? workloadType WorkloadType @default(CONTAINER) /// Geo region; container + MICROVM groups for one geo share it. Set-once once it has runs. region String? /// When true, runs enqueued to this worker queue may skip the intermediate queue /// and be pushed directly to the worker queue when concurrency is available. enableFastPath Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model WorkerGroupToken { id String @id @default(cuid()) tokenHash String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt workerGroup WorkerInstanceGroup? } model TaskRunTag { id String @id @default(cuid()) name String friendlyId String @unique project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String createdAt DateTime @default(now()) @@unique([projectId, name]) //Makes run filtering by tag faster @@index([name, id]) } /// This is used for triggerAndWait and batchTriggerAndWait. The taskRun is the child task, it points at a parent attempt or a batch model TaskRunDependency { id String @id @default(cuid()) /// The child run taskRun TaskRun @relation(fields: [taskRunId], references: [id], onDelete: Cascade, onUpdate: Cascade) taskRunId String @unique checkpointEvent CheckpointRestoreEvent? @relation(fields: [checkpointEventId], references: [id], onDelete: Cascade, onUpdate: Cascade) checkpointEventId String? @unique /// An attempt that is dependent on this task run. dependentAttempt TaskRunAttempt? @relation(fields: [dependentAttemptId], references: [id]) dependentAttemptId String? /// A batch run that is dependent on this task run dependentBatchRun BatchTaskRun? @relation("dependentBatchRun", fields: [dependentBatchRunId], references: [id]) dependentBatchRunId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt resumedAt DateTime? @@index([dependentAttemptId]) @@index([dependentBatchRunId]) } /// deprecated, we hadn't included the project id in the unique constraint model TaskRunCounter { taskIdentifier String @id lastNumber Int @default(0) } /// Used for the TaskRun number (e.g. #1,421) model TaskRunNumberCounter { id String @id @default(cuid()) taskIdentifier String environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String lastNumber Int @default(0) @@unique([taskIdentifier, environmentId]) } /// This is not used from engine v2+, attempts use the TaskRunExecutionSnapshot and TaskRun model TaskRunAttempt { id String @id @default(cuid()) number Int @default(0) friendlyId String @unique taskRun TaskRun @relation("attempts", fields: [taskRunId], references: [id], onDelete: Cascade, onUpdate: Cascade) taskRunId String backgroundWorker BackgroundWorker @relation(fields: [backgroundWorkerId], references: [id], onDelete: Cascade, onUpdate: Cascade) backgroundWorkerId String backgroundWorkerTask BackgroundWorkerTask @relation(fields: [backgroundWorkerTaskId], references: [id], onDelete: Cascade, onUpdate: Cascade) backgroundWorkerTaskId String runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String queue TaskQueue @relation(fields: [queueId], references: [id], onDelete: Cascade, onUpdate: Cascade) queueId String status TaskRunAttemptStatus @default(PENDING) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt startedAt DateTime? completedAt DateTime? usageDurationMs Int @default(0) error Json? output String? outputType String @default("application/json") dependencies TaskRunDependency[] batchDependencies BatchTaskRun[] checkpoints Checkpoint[] batchTaskRunItems BatchTaskRunItem[] CheckpointRestoreEvent CheckpointRestoreEvent[] childRuns TaskRun[] @relation("TaskParentRunAttempt") @@unique([taskRunId, number]) @@index([taskRunId]) } enum TaskRunAttemptStatus { /// NON-FINAL PENDING EXECUTING PAUSED /// FINAL FAILED CANCELED COMPLETED } /// This is the unified otel span/log model that will eventually be replaced by clickhouse model TaskEvent { id String @id @default(cuid()) /// This matches the span name for a trace event, or the log body for a log event message String traceId String spanId String parentId String? tracestate String? isError Boolean @default(false) isPartial Boolean @default(false) isCancelled Boolean @default(false) /// deprecated: don't use this, moving this to properties, this now uses TaskEventKind.LOG isDebug Boolean @default(false) serviceName String serviceNamespace String level TaskEventLevel @default(TRACE) kind TaskEventKind @default(INTERNAL) status TaskEventStatus @default(UNSET) links Json? events Json? /// This is the time the event started in nanoseconds since the epoch startTime BigInt /// This is the duration of the event in nanoseconds duration BigInt @default(0) attemptId String? attemptNumber Int? environmentId String environmentType RuntimeEnvironmentType organizationId String projectId String projectRef String runId String runIsTest Boolean @default(false) idempotencyKey String? taskSlug String taskPath String? taskExportName String? workerId String? workerVersion String? queueId String? queueName String? batchId String? /// This represents all the span attributes available, like http.status_code, and special attributes like $style.icon, $output, $metadata.payload.userId, as it's used for searching and filtering properties Json /// This represents all span attributes in the $metadata namespace, like $metadata.payload metadata Json? /// This represents all span attributes in the $style namespace, like $style style Json? /// This represents all span attributes in the $output namespace, like $output output Json? /// This represents the mimetype of the output, such as application/json or application/super+json outputType String? payload Json? payloadType String? createdAt DateTime @default(now()) // This represents the amount of "usage time" the event took, e.g. the CPU time usageDurationMs Int @default(0) usageCostInCents Float @default(0) machinePreset String? machinePresetCpu Float? machinePresetMemory Float? machinePresetCentsPerMs Float? /// Used on the run page @@index([traceId]) /// Used when looking up span events to complete when a run completes @@index([spanId]) // Used for getting all logs for a run @@index([runId]) } enum TaskEventLevel { TRACE DEBUG LOG INFO WARN ERROR } enum TaskEventKind { UNSPECIFIED INTERNAL SERVER CLIENT PRODUCER CONSUMER UNRECOGNIZED LOG } enum TaskEventStatus { UNSET OK ERROR UNRECOGNIZED } model TaskQueue { id String @id @default(cuid()) friendlyId String @unique name String type TaskQueueType @default(VIRTUAL) version TaskQueueVersion @default(V1) orderableName String? project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String /// Represents the current concurrency limit for the queue concurrencyLimit Int? /// When the concurrency limit was overridden concurrencyLimitOverriddenAt DateTime? /// Who overrode the concurrency limit (will be null if overridden via the API) concurrencyLimitOverriddenBy String? /// If concurrencyLimit is overridden, this is the overridden value concurrencyLimitBase Int? rateLimit Json? paused Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt attempts TaskRunAttempt[] tasks BackgroundWorkerTask[] workers BackgroundWorker[] @@unique([runtimeEnvironmentId, name]) } enum TaskQueueType { VIRTUAL NAMED } enum TaskQueueVersion { V1 V2 } model BatchTaskRun { id String @id @default(cuid()) friendlyId String @unique idempotencyKey String? idempotencyKeyExpiresAt DateTime? status BatchTaskRunStatus @default(PENDING) runtimeEnvironmentId String /// This only includes new runs, not idempotent runs. runs TaskRun[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // new columns /// Friendly IDs runIds String[] @default([]) runCount Int @default(0) payload String? payloadType String @default("application/json") options Json? batchVersion String @default("v1") //engine v2 /// Specific run blockers, runsBlocked TaskRunWaitpoint[] /// Waitpoints that are blocked by this batch. /// When a Batch is created it blocks execution of the associated parent run (for andWait) waitpoints Waitpoint[] // This is for v3 batches /// sealed is set to true once no more items can be added to the batch sealed Boolean @default(false) sealedAt DateTime? /// this is the expected number of items in the batch expectedCount Int @default(0) /// this is the completed number of items in the batch. once this reaches expectedCount, and the batch is sealed, the batch is considered completed completedCount Int @default(0) completedAt DateTime? resumedAt DateTime? /// this is used to be able to "seal" this BatchTaskRun when all of the runs have been triggered asynchronously, and using the "parallel" processing strategy processingJobsCount Int @default(0) processingJobsExpectedCount Int @default(0) /// optional token that can be used to authenticate the task run oneTimeUseToken String? // Run Engine v2 batch queue fields /// When processing started (status changed to PROCESSING) processingStartedAt DateTime? /// When processing completed (all items processed) processingCompletedAt DateTime? /// Count of successfully created runs successfulRunCount Int? /// Count of failed run creations failedRunCount Int? /// Detailed failure records errors BatchTaskRunError[] ///all the below properties are engine v1 only items BatchTaskRunItem[] taskIdentifier String? checkpointEvent CheckpointRestoreEvent? @relation(fields: [checkpointEventId], references: [id], onDelete: Cascade, onUpdate: Cascade) checkpointEventId String? @unique dependentTaskAttempt TaskRunAttempt? @relation(fields: [dependentTaskAttemptId], references: [id], onDelete: Cascade, onUpdate: Cascade) dependentTaskAttemptId String? runDependencies TaskRunDependency[] @relation("dependentBatchRun") @@unique([oneTimeUseToken]) ///this is used for all engine versions @@unique([runtimeEnvironmentId, idempotencyKey]) @@index([dependentTaskAttemptId]) // This is for the batch list dashboard page @@index([runtimeEnvironmentId, id(sort: Desc)]) } enum BatchTaskRunStatus { PENDING PROCESSING COMPLETED PARTIAL_FAILED ABORTED } ///Used in engine V1 only model BatchTaskRunItem { id String @id @default(cuid()) status BatchTaskRunItemStatus @default(PENDING) batchTaskRun BatchTaskRun @relation(fields: [batchTaskRunId], references: [id], onDelete: Cascade, onUpdate: Cascade) batchTaskRunId String taskRun TaskRun @relation(fields: [taskRunId], references: [id], onDelete: Cascade, onUpdate: Cascade) taskRunId String taskRunAttempt TaskRunAttempt? @relation(fields: [taskRunAttemptId], references: [id], onDelete: SetNull, onUpdate: Cascade) taskRunAttemptId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt completedAt DateTime? @@unique([batchTaskRunId, taskRunId]) @@index([taskRunAttemptId], map: "idx_batchtaskrunitem_taskrunattempt") @@index([taskRunId], map: "idx_batchtaskrunitem_taskrun") } enum BatchTaskRunItemStatus { PENDING FAILED CANCELED COMPLETED } /// Track individual run creation failures in batch processing (Run Engine v2) model BatchTaskRunError { id String @id @default(cuid()) batchTaskRun BatchTaskRun @relation(fields: [batchTaskRunId], references: [id], onDelete: Cascade, onUpdate: Cascade) batchTaskRunId String /// Which item in the batch (0-based index) index Int /// The task identifier that was being triggered taskIdentifier String /// The payload that failed (JSON, may be truncated) payload String? /// The options that were used options Json? /// Error message error String /// Error code if available errorCode String? createdAt DateTime @default(now()) @@unique([batchTaskRunId, index]) @@index([batchTaskRunId]) } model EnvironmentVariable { id String @id @default(cuid()) friendlyId String @unique key String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt values EnvironmentVariableValue[] @@unique([projectId, key]) } model EnvironmentVariableValue { id String @id @default(cuid()) valueReference SecretReference? @relation(fields: [valueReferenceId], references: [id], onDelete: SetNull, onUpdate: Cascade) valueReferenceId String? variable EnvironmentVariable @relation(fields: [variableId], references: [id], onDelete: Cascade, onUpdate: Cascade) variableId String environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String /// If true, the value is secret and cannot be revealed isSecret Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt version Int @default(1) lastUpdatedBy Json? @@unique([variableId, environmentId]) @@index([environmentId]) } model Checkpoint { id String @id @default(cuid()) friendlyId String @unique type CheckpointType location String imageRef String reason String? metadata String? events CheckpointRestoreEvent[] run TaskRun @relation(fields: [runId], references: [id], onDelete: Cascade, onUpdate: Cascade) runId String attempt TaskRunAttempt @relation(fields: [attemptId], references: [id], onDelete: Cascade, onUpdate: Cascade) attemptId String attemptNumber Int? project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([attemptId]) @@index([runId]) } enum CheckpointType { DOCKER KUBERNETES } model CheckpointRestoreEvent { id String @id @default(cuid()) type CheckpointRestoreEventType reason String? metadata String? checkpoint Checkpoint @relation(fields: [checkpointId], references: [id], onDelete: Cascade, onUpdate: Cascade) checkpointId String run TaskRun @relation(fields: [runId], references: [id], onDelete: Cascade, onUpdate: Cascade) runId String attempt TaskRunAttempt @relation(fields: [attemptId], references: [id], onDelete: Cascade, onUpdate: Cascade) attemptId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String taskRunDependency TaskRunDependency? batchTaskRunDependency BatchTaskRun? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([checkpointId]) @@index([runId]) } enum CheckpointRestoreEventType { CHECKPOINT RESTORE } enum WorkerDeploymentType { MANAGED UNMANAGED V1 } model WorkerDeployment { id String @id @default(cuid()) contentHash String friendlyId String @unique shortCode String version String runtime String? runtimeVersion String? imageReference String? imagePlatform String @default("linux/amd64") externalBuildData Json? buildServerMetadata Json? status WorkerDeploymentStatus @default(PENDING) type WorkerDeploymentType @default(V1) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String worker BackgroundWorker? @relation(fields: [workerId], references: [id], onDelete: Cascade, onUpdate: Cascade) workerId String? @unique triggeredBy User? @relation(fields: [triggeredById], references: [id], onDelete: SetNull, onUpdate: Cascade) triggeredById String? triggeredVia String? commitSHA String? startedAt DateTime? installedAt DateTime? builtAt DateTime? deployedAt DateTime? failedAt DateTime? errorData Json? canceledAt DateTime? canceledReason String? // This is GitMeta type git Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt promotions WorkerDeploymentPromotion[] alerts ProjectAlert[] workerInstance WorkerInstance[] integrationDeployments IntegrationDeployment[] @@unique([projectId, shortCode]) @@unique([environmentId, version]) @@index([commitSHA]) } enum WorkerDeploymentStatus { PENDING INSTALLING /// This is the status when the image is being built BUILDING /// This is the status when the image is built and we are waiting for the indexing to finish DEPLOYING /// This is the status when the image is built and indexed, meaning we have everything we need to deploy DEPLOYED FAILED CANCELED /// This is the status when the image is built and indexing does not finish in time TIMED_OUT } model WorkerDeploymentPromotion { id String @id @default(cuid()) /// This is the promotion label, e.g. "current" label String deployment WorkerDeployment @relation(fields: [deploymentId], references: [id], onDelete: Cascade, onUpdate: Cascade) deploymentId String environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String // Only one promotion per environment can be active at a time @@unique([environmentId, label]) } ///Schedules can be attached to tasks to trigger them on a schedule model TaskSchedule { id String @id @default(cuid()) type ScheduleType @default(IMPERATIVE) ///users see this as `id`. They start with schedule_ friendlyId String @unique ///a reference to a task (not a foreign key because it's across versions) taskIdentifier String ///can be provided and we won't create another with the same key deduplicationKey String @default(cuid()) userProvidedDeduplicationKey Boolean @default(false) ///the CRON pattern generatorExpression String generatorDescription String @default("") generatorType ScheduleGeneratorType @default(CRON) /// These are IANA format string, or the default "UTC". E.g. "America/New_York" timezone String @default("UTC") ///Can be provided by the user then accessed inside a run externalId String? ///Instances of the schedule that are active instances TaskScheduleInstance[] /// @deprecated stop writing 2026-04-30; reads moved out of code (UI now derives from cron's previous slot). Drop in follow-up. lastRunTriggeredAt DateTime? project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt active Boolean @default(true) @@unique([projectId, deduplicationKey]) /// Dashboard list view @@index([projectId]) @@index([projectId, createdAt(sort: Desc)]) } enum ScheduleType { /// defined on your task using the `cron` property DECLARATIVE /// explicit calls to the SDK are used to create, or using the dashboard IMPERATIVE } enum ScheduleGeneratorType { CRON } ///An instance links a schedule with an environment model TaskScheduleInstance { id String @id @default(cuid()) taskSchedule TaskSchedule @relation(fields: [taskScheduleId], references: [id], onDelete: Cascade, onUpdate: Cascade) taskScheduleId String environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt active Boolean @default(true) /// @deprecated stop writing 2026-04-30; engine derives from cron + exactScheduleTime. Drop in follow-up. lastScheduledTimestamp DateTime? /// @deprecated stop writing 2026-04-30; engine derives from cron + now(). Drop in follow-up. nextScheduledTimestamp DateTime? //you can only have a schedule attached to each environment once @@unique([taskScheduleId, environmentId]) @@index([environmentId]) @@index([projectId, active]) } model RuntimeEnvironmentSession { id String @id @default(cuid()) ipAddress String environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt disconnectedAt DateTime? currentEnvironments RuntimeEnvironment[] @relation("currentSession") } model ProjectAlertChannel { id String @id @default(cuid()) friendlyId String @unique ///can be provided and we won't create another with the same key deduplicationKey String @default(cuid()) userProvidedDeduplicationKey Boolean @default(false) integration OrganizationIntegration? @relation(fields: [integrationId], references: [id], onDelete: SetNull, onUpdate: Cascade) integrationId String? enabled Boolean @default(true) type ProjectAlertChannelType name String properties Json alertTypes ProjectAlertType[] environmentTypes RuntimeEnvironmentType[] @default([STAGING, PRODUCTION]) errorAlertConfig Json? project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt alerts ProjectAlert[] alertStorages ProjectAlertStorage[] @@unique([projectId, deduplicationKey]) } enum ProjectAlertChannelType { EMAIL SLACK WEBHOOK } model ProjectAlert { id String @id @default(cuid()) friendlyId String @unique project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String environment RuntimeEnvironment @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String channel ProjectAlertChannel @relation(fields: [channelId], references: [id], onDelete: Cascade, onUpdate: Cascade) channelId String status ProjectAlertStatus @default(PENDING) type ProjectAlertType // Run-ops split: these reference run-subgraph rows that live on the dedicated run-ops DB for run-ops id // runs, so the cross-DB FK can't hold. Scalar-only; the run is resolved via runStore.findRun. taskRunAttemptId String? taskRunId String? workerDeployment WorkerDeployment? @relation(fields: [workerDeploymentId], references: [id], onDelete: Cascade, onUpdate: Cascade) workerDeploymentId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } enum ProjectAlertType { TASK_RUN /// deprecated, we don't send new alerts for this type TASK_RUN_ATTEMPT DEPLOYMENT_FAILURE DEPLOYMENT_SUCCESS ERROR_GROUP } enum ProjectAlertStatus { PENDING SENT FAILED } model ProjectAlertStorage { id String @id @default(cuid()) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String alertChannel ProjectAlertChannel @relation(fields: [alertChannelId], references: [id], onDelete: Cascade, onUpdate: Cascade) alertChannelId String alertType ProjectAlertType storageId String storageData Json createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([alertChannelId, alertType, storageId]) } model OrganizationIntegration { id String @id @default(cuid()) friendlyId String @unique service IntegrationService externalOrganizationId String? /// Identifier for external, integration's organization (e.g. Vercel's team) integrationData Json tokenReference SecretReference @relation(fields: [tokenReferenceId], references: [id], onDelete: Cascade, onUpdate: Cascade) tokenReferenceId String organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? alertChannels ProjectAlertChannel[] organizationProjectIntegration OrganizationProjectIntegration[] @@index([externalOrganizationId]) } model OrganizationProjectIntegration { id String @id @default(cuid()) organizationIntegration OrganizationIntegration @relation(fields: [organizationIntegrationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationIntegrationId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String externalEntityId String /// Identifier for webhooks, for example Vercel's projectId integrationData Json /// Save useful data like config or external entity name installedBy String? /// UserId who installed the integration createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? @@index([projectId]) @@index([projectId, organizationIntegrationId]) @@index([externalEntityId]) } enum IntegrationService { SLACK VERCEL } /// Bulk actions, like canceling and replaying runs model BulkActionGroup { id String @id @default(cuid()) friendlyId String @unique project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String /// If this is set then it's a V2 Bulk Action that supports queries environment RuntimeEnvironment? @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String? type BulkActionType /// When the group is created it's pending. After we've processed all the items it's completed. This does not mean the associated runs are completed. status BulkActionStatus @default(PENDING) /// The query that will be executed to get the runs to process (if null, we are passing run ids directly) queryName String? /// The params that will be passed to the query params Json? /// Billing-limit dedupe key (grace hitAt or resolve job key). Null for dashboard bulk actions. dedupeKey String? /// The cursor that will be passed to the query (null for the first page) cursor Json? /// The number of runs that have been processed successfully successCount Int @default(0) /// The number of runs that have been failed failureCount Int @default(0) /// The total number of runs that will be processed totalCount Int @default(0) completionNotification BulkActionNotificationType @default(NONE) /// The userId who did the bulk action user User? @relation(fields: [userId], references: [id], onDelete: SetNull, onUpdate: Cascade) userId String? /// The name of the bulk action name String? /// The time the bulk action was completed completedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([environmentId, createdAt(sort: Desc)]) // NOTE: the real index is PARTIAL (`WHERE "dedupeKey" IS NOT NULL`) and created // CONCURRENTLY — see migration 20260626120001_bulk_action_group_dedupe_key_index. // Prisma can't express partial indexes, so this @@index is an approximation (as with // the INCLUDE/fillfactor indexes elsewhere in this schema). `migrate dev` will report // drift here; do NOT accept its drop/recreate — keep the hand-written migration. @@index([environmentId, type, dedupeKey]) // Backs the per-environment concurrent-replay limit (count of PENDING REPLAY groups). // Plain composite (not partial) so Prisma's parameterized count reliably uses it; the // migration creates it CONCURRENTLY to avoid locking writes. See migration // 20260702120000_bulk_action_group_pending_replay_index. @@index([environmentId, status, type]) } enum BulkActionType { /// Cancels existing runs. This populates the destination runs. CANCEL /// Replays existing runs. The original runs go as source runs, and the new runs go as destination runs. REPLAY } enum BulkActionStatus { PENDING COMPLETED ABORTED } enum BulkActionNotificationType { NONE EMAIL } model BulkActionItem { id String @id @default(cuid()) /// @deprecated not used in new BulkActions friendlyId String? groupId String type BulkActionType /// When the item is created it's pending. After we've processed the item it's completed. This does not mean the associated runs are completed. status BulkActionItemStatus @default(PENDING) /// The run that is the source of the action, e.g. when replaying this is the original run sourceRun TaskRun @relation("SourceActionItemRun", fields: [sourceRunId], references: [id], onDelete: Cascade, onUpdate: Cascade) sourceRunId String /// The run that's a result of the action, this will be set when the run has been created destinationRun TaskRun? @relation("DestinationActionItemRun", fields: [destinationRunId], references: [id], onDelete: Cascade, onUpdate: Cascade) destinationRunId String? error String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } enum BulkActionItemStatus { PENDING COMPLETED FAILED } model RealtimeStreamChunk { id String @id @default(cuid()) key String value String sequence Int runId String createdAt DateTime @default(now()) @@index([runId]) @@index([createdAt]) } /// This is the unified otel span/log model that will eventually be replaced by clickhouse model TaskEventPartitioned { id String @default(cuid()) /// This matches the span name for a trace event, or the log body for a log event message String traceId String spanId String parentId String? tracestate String? isError Boolean @default(false) isPartial Boolean @default(false) isCancelled Boolean @default(false) serviceName String serviceNamespace String level TaskEventLevel @default(TRACE) kind TaskEventKind @default(INTERNAL) status TaskEventStatus @default(UNSET) links Json? events Json? /// This is the time the event started in nanoseconds since the epoch startTime BigInt /// This is the duration of the event in nanoseconds duration BigInt @default(0) attemptId String? attemptNumber Int? environmentId String environmentType RuntimeEnvironmentType organizationId String projectId String projectRef String runId String runIsTest Boolean @default(false) idempotencyKey String? taskSlug String taskPath String? taskExportName String? workerId String? workerVersion String? queueId String? queueName String? batchId String? /// This represents all the span attributes available, like http.status_code, and special attributes like $style.icon, $output, $metadata.payload.userId, as it's used for searching and filtering properties Json /// This represents all span attributes in the $metadata namespace, like $metadata.payload metadata Json? /// This represents all span attributes in the $style namespace, like $style style Json? /// This represents all span attributes in the $output namespace, like $output output Json? /// This represents the mimetype of the output, such as application/json or application/super+json outputType String? payload Json? payloadType String? createdAt DateTime @default(now()) // This represents the amount of "usage time" the event took, e.g. the CPU time usageDurationMs Int @default(0) usageCostInCents Float @default(0) machinePreset String? machinePresetCpu Float? machinePresetMemory Float? machinePresetCentsPerMs Float? @@id([id, createdAt]) /// Used on the run page @@index([traceId]) /// Used when looking up span events to complete when a run completes @@index([spanId]) // Used for getting all logs for a run @@index([runId]) } enum GithubRepositorySelection { ALL SELECTED } model GithubAppInstallation { id String @id @default(cuid()) appInstallationId BigInt @unique targetId BigInt targetType String accountHandle String permissions Json? repositorySelection GithubRepositorySelection installedBy String? organization Organization @relation(fields: [organizationId], references: [id]) organizationId String repositories GithubRepository[] deletedAt DateTime? suspendedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId]) } model GithubRepository { id String @id @default(cuid()) githubId BigInt name String fullName String htmlUrl String private Boolean defaultBranch String installation GithubAppInstallation @relation(fields: [installationId], references: [id], onDelete: Cascade, onUpdate: Cascade) installationId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt ConnectedGithubRepository ConnectedGithubRepository[] @@unique([installationId, githubId]) @@index([installationId]) } model ConnectedGithubRepository { id String @id @default(cuid()) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String repository GithubRepository @relation(fields: [repositoryId], references: [id], onDelete: Cascade, onUpdate: Cascade) repositoryId String // Branch configuration by environment slug, stored as JSON // Example: { // "prod": { "branch": "main" }, // "staging": { "branch": "staging" }, // } // We could alternatively store tracking branch configurations in a separate table and reference the runtime environments properly. // We don't need that functionality currently so it's simpler to store it here. // Should we need to introduce such functionality in the future, e.g., supporting custom environments, the migration should be straightforward. branchTracking Json previewDeploymentsEnabled Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt // A project can only be connected to one repository @@unique([projectId]) @@index([repositoryId]) } enum ImpersonationAuditLogAction { START STOP } model ImpersonationAuditLog { id String @id @default(cuid()) action ImpersonationAuditLogAction /// The admin user who initiated/ended the impersonation admin User @relation("ImpersonationAdmin", fields: [adminId], references: [id], onDelete: Cascade, onUpdate: Cascade) adminId String /// The user being impersonated target User @relation("ImpersonationTarget", fields: [targetId], references: [id], onDelete: Cascade, onUpdate: Cascade) targetId String ipAddress String? createdAt DateTime @default(now()) @@index([adminId]) @@index([targetId]) @@index([createdAt]) } enum CustomerQuerySource { DASHBOARD API } enum CustomerQueryScope { ORGANIZATION PROJECT ENVIRONMENT } model CustomerQuery { id String @id @default(cuid()) /// The TSQL query text that was executed query String /// The scope of the query (determines which tenant IDs were used) scope CustomerQueryScope /// Query execution statistics from ClickHouse stats Json /// AI-generated title summarizing the query title String? /// Where the query originated from source CustomerQuerySource @default(DASHBOARD) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String? environment RuntimeEnvironment? @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String? /// Optional user who executed the query (null for API calls) user User? @relation(fields: [userId], references: [id], onDelete: SetNull, onUpdate: Cascade) userId String? /// Time filter settings used when the query was executed /// Period like "7d", "24h", etc. filterPeriod String? /// Custom start date (ISO 8601) filterFrom DateTime? /// Custom end date (ISO 8601) filterTo DateTime? createdAt DateTime @default(now()) /// Fast lookup for history menu (most recent 20 per org) @@index([organizationId, createdAt(sort: Desc)]) /// For Stripe metering job - find unprocessed queries @@index([createdAt]) } model IntegrationDeployment { id String @id @default(cuid()) integrationName String /// For example Vercel integrationDeploymentId String /// External ID commitSHA String deploymentId String? status String? /// External deployment status workerDeployment WorkerDeployment? @relation(fields: [deploymentId], references: [id], onDelete: SetNull, onUpdate: Cascade) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([commitSHA]) @@index([deploymentId]) } /// A user-defined metrics dashboard model MetricsDashboard { id String @id @default(cuid()) friendlyId String @unique title String description String @default("") organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String? /// Who created the dashboard owner User? @relation(fields: [ownerId], references: [id], onDelete: SetNull, onUpdate: Cascade) ownerId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt /// JSON that defines the config, queries, layout and config of all widgets. /// There will be a version field for the format. layout String /// Fast lookup for the list @@index([projectId, createdAt(sort: Desc)]) } // ==================================================== // LLM Pricing Models // ==================================================== /// A known LLM model or model pattern for cost tracking model LlmModel { id String @id @default(cuid()) friendlyId String @unique @map("friendly_id") projectId String? @map("project_id") project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade) modelName String @map("model_name") matchPattern String @map("match_pattern") startDate DateTime? @map("start_date") source String @default("default") // "default", "admin", "project" createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") // Catalog metadata for model registry provider String? @map("provider") description String? @map("description") contextWindow Int? @map("context_window") maxOutputTokens Int? @map("max_output_tokens") capabilities String[] @default([]) @map("capabilities") isHidden Boolean @default(false) @map("is_hidden") baseModelName String? @map("base_model_name") // "tokens", "images", "characters", "minutes", "requests", "free", "not_findable"; null until researched pricingUnit String? @map("pricing_unit") pricingTiers LlmPricingTier[] prices LlmPrice[] @@unique([projectId, modelName, startDate]) @@index([projectId]) @@map("llm_models") } /// A pricing tier for a model (supports volume-based or conditional pricing) model LlmPricingTier { id String @id @default(cuid()) modelId String @map("model_id") model LlmModel @relation(fields: [modelId], references: [id], onDelete: Cascade) name String isDefault Boolean @default(true) @map("is_default") priority Int @default(0) conditions Json @default("[]") @db.JsonB prices LlmPrice[] @@unique([modelId, priority]) @@unique([modelId, name]) @@map("llm_pricing_tiers") } /// A price point for a usage type within a pricing tier model LlmPrice { id String @id @default(cuid()) modelId String @map("model_id") model LlmModel @relation(fields: [modelId], references: [id], onDelete: Cascade) pricingTierId String @map("pricing_tier_id") pricingTier LlmPricingTier @relation(fields: [pricingTierId], references: [id], onDelete: Cascade) usageType String @map("usage_type") price Decimal @db.Decimal(20, 12) @@unique([modelId, usageType, pricingTierId]) @@map("llm_prices") } enum PlatformNotificationSurface { WEBAPP CLI } enum PlatformNotificationScope { USER PROJECT ORGANIZATION GLOBAL } /// Admin-created notification definitions model PlatformNotification { id String @id @default(cuid()) friendlyId String @unique @default(cuid()) /// Admin-facing title for identification in admin tools title String /// Versioned JSON rendering content (see payload schema in spec) payload Json surface PlatformNotificationSurface scope PlatformNotificationScope /// Set when scope = USER user User? @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) userId String? /// Set when scope = ORGANIZATION organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String? /// Set when scope = PROJECT project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String? /// When notification becomes active startsAt DateTime /// When notification expires endsAt DateTime /// CLI: auto-expire N days after user first saw it cliMaxDaysAfterFirstSeen Int? /// CLI: auto-expire after shown N times to user cliMaxShowCount Int? /// CLI: only show every N-th request (e.g. cliShowEvery=3 means show on 3rd, 6th, 9th…) cliShowEvery Int? /// Ordering within same scope level (higher = more important) priority Int @default(0) /// Soft delete archivedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt interactions PlatformNotificationInteraction[] @@index([surface, scope, startsAt]) } /// Per-user tracking of notification views and dismissals model PlatformNotificationInteraction { id String @id @default(cuid()) notification PlatformNotification @relation(fields: [notificationId], references: [id], onDelete: Cascade, onUpdate: Cascade) notificationId String user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) userId String /// Set by beacon/CLI GET on first impression firstSeenAt DateTime @default(now()) /// Times shown (incremented per beacon/CLI view) showCount Int @default(1) /// User dismissed in webapp webappDismissedAt DateTime? /// User clicked a link in webapp webappClickedAt DateTime? /// Auto-dismissed or expired in CLI cliDismissedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([notificationId, userId]) } enum ErrorGroupStatus { UNRESOLVED RESOLVED IGNORED } /** * Error group state is used to track when a user has interacted with an error (ignored/resolved) * The actual error data is in ClickHouse. */ model ErrorGroupState { id String @id @default(cuid()) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade, onUpdate: Cascade) organizationId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String /** * You can ignore/resolve an error across all environments, or specific ones */ environment RuntimeEnvironment? @relation(fields: [environmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) environmentId String? taskIdentifier String errorFingerprint String status ErrorGroupStatus @default(UNRESOLVED) /** * Error is ignored until this date */ ignoredUntil DateTime? /** * Error is ignored until this occurrence rate */ ignoredUntilOccurrenceRate Int? /** * Error is ignored until this total occurrences */ ignoredUntilTotalOccurrences Int? /// Total occurrence count at the time the error was ignored (from ClickHouse). /// Used with ignoredUntilTotalOccurrences to compute occurrences since ignoring. ignoredAtOccurrenceCount BigInt? /** * Error was ignored at this date */ ignoredAt DateTime? /** * Reason for ignoring the error */ ignoredReason String? /** * User who ignored the error */ ignoredByUserId String? /** * Error was resolved at this date */ resolvedAt DateTime? /** * Error was resolved in this version */ resolvedInVersion String? /** * User who resolved the error */ resolvedBy String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([environmentId, taskIdentifier, errorFingerprint]) @@index([environmentId, status]) } model TaskIdentifier { id String @id @default(cuid()) runtimeEnvironment RuntimeEnvironment @relation(fields: [runtimeEnvironmentId], references: [id], onDelete: Cascade, onUpdate: Cascade) runtimeEnvironmentId String project Project @relation(fields: [projectId], references: [id], onDelete: Cascade, onUpdate: Cascade) projectId String slug String currentTriggerSource TaskTriggerSource @default(STANDARD) currentWorker BackgroundWorker? @relation(fields: [currentWorkerId], references: [id], onDelete: SetNull, onUpdate: Cascade) currentWorkerId String? firstSeenAt DateTime @default(now()) lastSeenAt DateTime @default(now()) isInLatestDeployment Boolean @default(true) @@unique([runtimeEnvironmentId, slug]) @@index([runtimeEnvironmentId, isInLatestDeployment]) } enum DataStoreKind { CLICKHOUSE } /// Defines org-scoped data store overrides (e.g. dedicated ClickHouse for HIPAA orgs). /// Multiple organizations can share a single data store row via organizationIds. model OrganizationDataStore { id String @id @default(cuid()) /// Human-readable unique key (e.g. "hipaa-clickhouse-us-east") key String @unique /// Organization IDs that use this data store organizationIds String[] kind DataStoreKind /// Versioned config JSON. Structure is discriminated by the top-level `version` field. config Json createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([kind]) }