Files
triggerdotdev--trigger.dev/internal-packages/database/prisma/schema.prisma
T
2026-07-13 13:32:57 +08:00

3197 lines
95 KiB
Plaintext

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])
}