The main agent should coach the user, but nothing stored what the user wants to achieve or keep doing, and no routine ever checked in. This adds the data model and the interfaces the onboarding intake and the coach tools build on. - Schema: UserGoal, Habit, CheckIn (one per habit, goal or day and date) and DailyBrief, all keyed to the user's local calendar date, plus a coachKey on Routine that marks the main bot's coach check-ins. - Contracts (coach.ts): CoachRhythm, Goal, Habit, CheckIn, DailyBrief and CoachToday DTOs and the RPC inputs. - Repository (@rakazo/db coach.ts): goal, habit and check-in CRUD with per-user limits, upsertDailyBrief and loadCoachToday with streaks in the user's time zone. - coach-rhythm.ts: setCoachRhythm/getCoachRhythm keep up to three main bot routines (morning brief, evening check-in, weekly review) in the main conversation in step with the user's chosen times. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
1603 lines
56 KiB
Plaintext
1603 lines
56 KiB
Plaintext
generator client {
|
|
provider = "prisma-client"
|
|
output = "../src/generated/prisma"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
}
|
|
|
|
model AiDataConsent {
|
|
userId String
|
|
spaceId String
|
|
recipientKey String
|
|
version String
|
|
grantedAt DateTime @default(now())
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
spaceMember SpaceMember @relation(fields: [spaceId, userId], references: [spaceId, userId], onDelete: Cascade)
|
|
|
|
@@id([userId, spaceId, recipientKey])
|
|
@@map("ai_data_consents")
|
|
}
|
|
|
|
model User {
|
|
aiDataConsents AiDataConsent[]
|
|
id String @id
|
|
name String
|
|
email String
|
|
emailVerified Boolean @default(false)
|
|
image String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
avatarStyle String @default("robot")
|
|
sessions Session[]
|
|
accounts Account[]
|
|
members Member[]
|
|
invitations Invitation[]
|
|
modelCredentials UserModelCredential[]
|
|
voiceCredentials UserVoiceCredential[]
|
|
secrets Secret[]
|
|
mcpServers McpServer[]
|
|
botSections BotSection[]
|
|
actionApprovalRules ActionApprovalRule[]
|
|
actionAutoReviewPreferences ActionAutoReviewPreference[]
|
|
createdSpaces Space[] @relation("SpaceCreator")
|
|
botSecrets BotSecret[]
|
|
agentSecrets AgentSecret[]
|
|
profile UserProfile?
|
|
facts UserFact[]
|
|
goals UserGoal[]
|
|
habits Habit[]
|
|
checkIns CheckIn[]
|
|
dailyBriefs DailyBrief[]
|
|
|
|
@@unique([email])
|
|
@@map("user")
|
|
}
|
|
|
|
model Session {
|
|
id String @id
|
|
expiresAt DateTime
|
|
token String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
ipAddress String?
|
|
userAgent String?
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
activeOrganizationId String?
|
|
|
|
@@unique([token])
|
|
@@index([userId])
|
|
@@map("session")
|
|
}
|
|
|
|
model Account {
|
|
id String @id
|
|
accountId String
|
|
providerId String
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
accessToken String?
|
|
refreshToken String?
|
|
idToken String?
|
|
accessTokenExpiresAt DateTime?
|
|
refreshTokenExpiresAt DateTime?
|
|
scope String?
|
|
password String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([userId])
|
|
@@map("account")
|
|
}
|
|
|
|
model Verification {
|
|
id String @id
|
|
identifier String
|
|
value String
|
|
expiresAt DateTime
|
|
createdAt DateTime? @default(now())
|
|
updatedAt DateTime? @updatedAt
|
|
|
|
@@index([identifier])
|
|
@@map("verification")
|
|
}
|
|
|
|
model Organization {
|
|
id String @id
|
|
name String
|
|
slug String
|
|
logo String?
|
|
createdAt DateTime
|
|
metadata String?
|
|
members Member[]
|
|
invitations Invitation[]
|
|
spaces Space[]
|
|
|
|
@@unique([slug])
|
|
@@map("organization")
|
|
}
|
|
|
|
model ActionApprovalRule {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
createdByUserId String
|
|
createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade)
|
|
effect String
|
|
matchKind String
|
|
matchValue String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([spaceId, createdByUserId, effect, matchKind, matchValue])
|
|
@@index([spaceId, createdByUserId])
|
|
@@map("action_approval_rules")
|
|
}
|
|
|
|
model ActionAutoReviewPreference {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
enabled Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([spaceId, userId])
|
|
@@map("action_auto_review_preferences")
|
|
}
|
|
|
|
model Member {
|
|
id String @id
|
|
organizationId String
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
role String
|
|
createdAt DateTime
|
|
spaceMemberships SpaceMember[]
|
|
|
|
@@unique([organizationId, userId])
|
|
@@index([organizationId])
|
|
@@index([userId])
|
|
@@map("member")
|
|
}
|
|
|
|
model Space {
|
|
aiDataConsents AiDataConsent[]
|
|
id String @id
|
|
organizationId String
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
name String
|
|
workspacePath String?
|
|
gitRemoteUrl String?
|
|
gitDefaultBranch String?
|
|
gitHeadCommit String?
|
|
isDefault Boolean @default(false)
|
|
createdByUserId String?
|
|
createdByUser User? @relation("SpaceCreator", fields: [createdByUserId], references: [id], onDelete: SetNull)
|
|
createdAt DateTime @default(now())
|
|
deletingAt DateTime?
|
|
deletionClaimId String?
|
|
memberships SpaceMember[]
|
|
bots Bot[]
|
|
botSections BotSection[]
|
|
botDeletions BotDeletion[]
|
|
threads Thread[]
|
|
events Event[]
|
|
tasks Task[]
|
|
runs Run[]
|
|
routines Routine[]
|
|
scratchpadItems ScratchpadItem[]
|
|
projectGoals ProjectGoal[]
|
|
taughtSkills TaughtSkill[]
|
|
agentSkills AgentSkill[]
|
|
connections Connection[]
|
|
capabilities CapabilityInstall[]
|
|
memoryDocuments MemoryDocument[]
|
|
agentHomes AgentHome[]
|
|
browserProfiles BrowserProfile[]
|
|
artifacts Artifact[]
|
|
usageRecords UsageRecord[]
|
|
notificationPreferences NotificationPreference[]
|
|
computers Computer[]
|
|
externalEffects ExternalEffect[]
|
|
mcpServers McpServer[]
|
|
mcpOAuthSessions McpOAuthSession[]
|
|
botMcpServers BotMcpServer[]
|
|
messagingIdentities MessagingIdentity[]
|
|
actionApprovalRules ActionApprovalRule[]
|
|
actionAutoReviewPreferences ActionAutoReviewPreference[]
|
|
memoryConfig SpaceMemoryConfig?
|
|
secrets Secret[]
|
|
chatGroups ChatGroup[]
|
|
botSecrets BotSecret[]
|
|
externalConversations ExternalConversation[]
|
|
agentSecrets AgentSecret[]
|
|
userFactVisibility UserFactSpace[]
|
|
userGoals UserGoal[]
|
|
delegatedTasks DelegatedTask[]
|
|
semanticMemoryItems SemanticMemoryItem[]
|
|
|
|
@@unique([id, organizationId])
|
|
@@index([organizationId, createdAt])
|
|
@@index([createdByUserId])
|
|
@@map("spaces")
|
|
}
|
|
|
|
model SpaceMember {
|
|
id String @id
|
|
spaceId String
|
|
organizationId String
|
|
space Space @relation(fields: [spaceId, organizationId], references: [id, organizationId], onDelete: Cascade)
|
|
userId String
|
|
member Member @relation(fields: [organizationId, userId], references: [organizationId, userId], onDelete: Cascade)
|
|
role String @default("member")
|
|
createdAt DateTime
|
|
modelPreferences SpaceModelPreference[]
|
|
modelTiers SpaceModelTier[]
|
|
voicePreferences SpaceVoicePreference[]
|
|
aiDataConsents AiDataConsent[]
|
|
|
|
@@unique([spaceId, userId])
|
|
@@index([organizationId, userId])
|
|
@@index([userId, createdAt])
|
|
@@map("space_members")
|
|
}
|
|
|
|
model Invitation {
|
|
id String @id
|
|
organizationId String
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
email String
|
|
role String?
|
|
status String
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
inviterId String
|
|
user User @relation(fields: [inviterId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@index([email])
|
|
@@map("invitation")
|
|
}
|
|
|
|
model DeploymentSettings {
|
|
id String @id @default("default")
|
|
ownerUserId String?
|
|
signupsEnabled Boolean @default(true)
|
|
signupAllowlist String @default("")
|
|
signupPolicyInitialized Boolean @default(false)
|
|
defaultModelProvider String?
|
|
defaultModelId String?
|
|
deploymentModelCredentialCipher String?
|
|
computerHost String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@map("deployment_settings")
|
|
}
|
|
|
|
model UserModelCredential {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
provider String
|
|
label String
|
|
secretId String
|
|
supportsImages Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
preferences SpaceModelPreference[]
|
|
tiers SpaceModelTier[]
|
|
|
|
@@unique([id, userId])
|
|
@@index([userId, provider, updatedAt])
|
|
@@map("user_model_credentials")
|
|
}
|
|
|
|
model UserVoiceCredential {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
provider String
|
|
secretId String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
preferences SpaceVoicePreference[]
|
|
|
|
@@unique([id, userId])
|
|
@@index([userId, provider, updatedAt])
|
|
@@map("user_voice_credentials")
|
|
}
|
|
|
|
model SpaceModelPreference {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
userId String
|
|
spaceMember SpaceMember @relation(fields: [spaceId, userId], references: [spaceId, userId], onDelete: Cascade)
|
|
credentialId String
|
|
credential UserModelCredential @relation(fields: [credentialId, userId], references: [id, userId], onDelete: Cascade)
|
|
modelId String?
|
|
isDefault Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([spaceId, userId, credentialId])
|
|
@@index([spaceId, userId, isDefault, updatedAt])
|
|
@@index([credentialId])
|
|
// Partial unique index space_model_preferences_default_key is created in SQL.
|
|
@@map("space_model_preferences")
|
|
}
|
|
|
|
/// Maps a model tier (fast, standard, code) to a connected model for one user in one space.
|
|
model SpaceModelTier {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
userId String
|
|
spaceMember SpaceMember @relation(fields: [spaceId, userId], references: [spaceId, userId], onDelete: Cascade)
|
|
tier String
|
|
credentialId String
|
|
credential UserModelCredential @relation(fields: [credentialId, userId], references: [id, userId], onDelete: Cascade)
|
|
modelId String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([spaceId, userId, tier])
|
|
@@index([credentialId])
|
|
// CHECK constraint space_model_tiers_tier_chk is created in SQL.
|
|
@@map("space_model_tiers")
|
|
}
|
|
|
|
model SpaceVoicePreference {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
userId String
|
|
spaceMember SpaceMember @relation(fields: [spaceId, userId], references: [spaceId, userId], onDelete: Cascade)
|
|
credentialId String
|
|
credential UserVoiceCredential @relation(fields: [credentialId, userId], references: [id, userId], onDelete: Cascade)
|
|
voiceId String @default("")
|
|
isDefault Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([spaceId, userId, credentialId])
|
|
@@index([spaceId, userId, isDefault, updatedAt])
|
|
@@index([credentialId])
|
|
// Partial unique index space_voice_preferences_default_key is created in SQL.
|
|
@@map("space_voice_preferences")
|
|
}
|
|
|
|
model Bot {
|
|
screenGeneration Int @default(0)
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
name String
|
|
title String @default("")
|
|
description String @default("")
|
|
instructions String @default("")
|
|
color String
|
|
notifyOnFinish Boolean @default(true)
|
|
pinned Boolean @default(false)
|
|
position Int @default(0)
|
|
sectionId String?
|
|
section BotSection? @relation(fields: [sectionId], references: [id], onDelete: SetNull)
|
|
archivedAt DateTime?
|
|
parentBotId String?
|
|
spawnKey String?
|
|
memoryScope String?
|
|
parent Bot? @relation("BotChildren", fields: [parentBotId], references: [id], onDelete: SetNull)
|
|
children Bot[] @relation("BotChildren")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
thread Thread?
|
|
computerId String?
|
|
computer Computer? @relation(fields: [computerId], references: [id], onDelete: SetNull)
|
|
computerSwitching Boolean @default(false)
|
|
voiceId String?
|
|
autoSpeak Boolean @default(false)
|
|
modelProvider String?
|
|
modelId String?
|
|
thinkingLevel String?
|
|
webhookSecretId String?
|
|
teamChatAmbientEnabled Boolean @default(false)
|
|
teamChatRules String @default("")
|
|
/// "main" for the user's single lead agent, "specialist" for every other bot.
|
|
kind String @default("specialist")
|
|
tags String[] @default([])
|
|
restrictBuiltinTools Boolean @default(false)
|
|
allowedBuiltinTools String[] @default([])
|
|
modelTier String?
|
|
routines Routine[]
|
|
scratchpadItems ScratchpadItem[]
|
|
taughtSkills TaughtSkill[]
|
|
memoryDocuments MemoryDocument[]
|
|
agentHome AgentHome?
|
|
browserProfile BrowserProfile?
|
|
artifacts Artifact[]
|
|
tasks Task[]
|
|
runs Run[]
|
|
steeringMessages SteeringMessage[]
|
|
mcpServers BotMcpServer[]
|
|
groupMembers ChatGroupMember[]
|
|
ledGroups ChatGroup[] @relation("ChatGroupLead")
|
|
semanticMemoryItems SemanticMemoryItem[]
|
|
botSecrets BotSecret[]
|
|
|
|
externalConversations ExternalConversation[]
|
|
|
|
@@unique([spaceId, spawnKey])
|
|
// CHECK bots_kind_chk and partial unique index bots_userId_main_key are created in SQL.
|
|
@@index([spaceId, userId, archivedAt, pinned, updatedAt])
|
|
@@index([sectionId])
|
|
@@index([computerId])
|
|
@@index([parentBotId])
|
|
@@map("bots")
|
|
}
|
|
|
|
model BotSection {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
name String
|
|
position Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
bots Bot[]
|
|
groups ChatGroup[]
|
|
projectGoals ProjectGoal[]
|
|
|
|
@@unique([spaceId, userId, name])
|
|
@@index([spaceId, userId, position, createdAt])
|
|
@@map("bot_sections")
|
|
}
|
|
|
|
model BotDeletion {
|
|
id String @id
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
name String
|
|
deletedByUserId String
|
|
memoriesPreserved Boolean
|
|
deletedAt DateTime @default(now())
|
|
|
|
@@index([spaceId, deletedAt])
|
|
@@map("bot_deletions")
|
|
}
|
|
|
|
model ChatGroup {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
name String
|
|
/// "main" for the user's main conversation led by the main bot, "group" otherwise.
|
|
kind String @default("group")
|
|
leadBotId String?
|
|
leadBot Bot? @relation("ChatGroupLead", fields: [leadBotId], references: [id], onDelete: SetNull)
|
|
pinned Boolean @default(false)
|
|
sectionId String?
|
|
section BotSection? @relation(fields: [sectionId], references: [id], onDelete: SetNull)
|
|
archivedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
members ChatGroupMember[]
|
|
thread Thread?
|
|
artifacts Artifact[]
|
|
projectGoals ProjectGoal[]
|
|
|
|
@@index([spaceId, userId, archivedAt, pinned, updatedAt])
|
|
@@index([sectionId])
|
|
@@index([leadBotId])
|
|
// CHECK chat_groups_kind_chk and partial unique index chat_groups_userId_main_key are created in SQL.
|
|
@@map("chat_groups")
|
|
}
|
|
|
|
model ChatGroupMember {
|
|
id String @id @default(cuid())
|
|
groupId String
|
|
group ChatGroup @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: NoAction)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([groupId, botId])
|
|
@@index([botId])
|
|
@@map("chat_group_members")
|
|
}
|
|
|
|
model Thread {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String? @unique
|
|
bot Bot? @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
groupId String? @unique
|
|
group ChatGroup? @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
|
externalConversationId String? @unique
|
|
externalConversation ExternalConversation? @relation(fields: [externalConversationId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
createdAt DateTime @default(now())
|
|
nextEventSeq Int @default(0)
|
|
nextMessageSeq Int @default(0)
|
|
historyCompactedUpToSeq Int?
|
|
historyCompactionSummary String? @db.Text
|
|
historyCompactionGeneration Int @default(0)
|
|
unread Boolean @default(false)
|
|
messages Message[]
|
|
events Event[]
|
|
tasks Task[]
|
|
runs Run[]
|
|
routines Routine[]
|
|
delegatedTasks DelegatedTask[]
|
|
|
|
@@index([spaceId])
|
|
@@map("threads")
|
|
}
|
|
|
|
model Message {
|
|
id String @id @default(cuid())
|
|
threadId String
|
|
thread Thread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
|
seq Int
|
|
role String
|
|
blocks Json
|
|
botId String?
|
|
replyToMessageId String?
|
|
replyQuote String?
|
|
replyTo Message? @relation("MessageReplies", fields: [replyToMessageId], references: [id], onDelete: SetNull)
|
|
replies Message[] @relation("MessageReplies")
|
|
runId String?
|
|
clientNonce String?
|
|
sourceRuns Run[] @relation("RunSourceMessage")
|
|
externalTranscriptFor ExternalMessage?
|
|
steeringMessages SteeringMessage[]
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([threadId, seq])
|
|
@@unique([threadId, clientNonce])
|
|
@@index([threadId, seq])
|
|
@@index([replyToMessageId])
|
|
@@map("messages")
|
|
}
|
|
|
|
model Event {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
threadId String
|
|
thread Thread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
seq Int
|
|
type String
|
|
payload Json
|
|
runId String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([threadId, seq])
|
|
@@index([spaceId, createdAt])
|
|
@@index([threadId, seq])
|
|
@@index([runId, type, seq])
|
|
@@map("events")
|
|
}
|
|
|
|
model Task {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
threadId String
|
|
thread Thread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
prompt String
|
|
status String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
runs Run[]
|
|
|
|
@@index([spaceId, botId])
|
|
@@map("tasks")
|
|
}
|
|
|
|
model Run {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
threadId String
|
|
thread Thread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
|
taskId String
|
|
task Task @relation(fields: [taskId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
status String
|
|
trigger String
|
|
modelProvider String?
|
|
modelId String?
|
|
error String?
|
|
leaseOwner String?
|
|
leaseFence Int @default(0)
|
|
leaseExpiresAt DateTime?
|
|
checkpoint String?
|
|
clientNonce String?
|
|
sourceMessageId String?
|
|
sourceMessage Message? @relation("RunSourceMessage", fields: [sourceMessageId], references: [id], onDelete: SetNull)
|
|
routineId String?
|
|
routine Routine? @relation(fields: [routineId], references: [id], onDelete: SetNull)
|
|
startedAt DateTime?
|
|
completedAt DateTime?
|
|
toolCallBudgetCount Int?
|
|
toolCallBudgetExceeded Boolean?
|
|
botOutcomeReturnedAt DateTime?
|
|
messagingMirroredAt DateTime?
|
|
teamChatMirroredAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
attempts Attempt[]
|
|
effects ExternalEffect[]
|
|
usageRecords UsageRecord[]
|
|
steeringMessages SteeringMessage[]
|
|
externalMessage ExternalMessage?
|
|
|
|
@@unique([spaceId, clientNonce])
|
|
@@index([status, leaseExpiresAt])
|
|
@@index([status, updatedAt, id])
|
|
@@index([botOutcomeReturnedAt, status])
|
|
@@index([trigger, status, messagingMirroredAt, updatedAt])
|
|
@@index([trigger, status, teamChatMirroredAt, updatedAt])
|
|
@@index([spaceId, botId])
|
|
@@index([sourceMessageId])
|
|
@@index([threadId, status, createdAt])
|
|
@@index([routineId])
|
|
@@map("runs")
|
|
}
|
|
|
|
model SteeringMessage {
|
|
id String @id @default(cuid())
|
|
messageId String
|
|
message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
runId String?
|
|
run Run? @relation(fields: [runId], references: [id], onDelete: SetNull)
|
|
claimedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([messageId, botId])
|
|
@@index([botId, runId, createdAt])
|
|
@@index([runId])
|
|
@@map("steering_messages")
|
|
}
|
|
|
|
model Attempt {
|
|
id String @id @default(cuid())
|
|
runId String
|
|
run Run @relation(fields: [runId], references: [id], onDelete: Cascade)
|
|
fence Int
|
|
status String
|
|
error String?
|
|
startedAt DateTime @default(now())
|
|
finishedAt DateTime?
|
|
|
|
@@index([runId])
|
|
@@map("attempts")
|
|
}
|
|
|
|
// Deliberately independent of chat/user cascades: the worker must still be able
|
|
// to cancel remote work after its originating chat or account is deleted.
|
|
model CloudAgent {
|
|
id String @id @default(uuid())
|
|
operationKey String @unique
|
|
providerKey String
|
|
spaceId String
|
|
userId String
|
|
botId String
|
|
threadId String
|
|
messageId String?
|
|
remoteId String?
|
|
latestRunId String?
|
|
title String
|
|
status String @default("running")
|
|
url String @default("")
|
|
branch String?
|
|
prUrl String?
|
|
launchRequest Json
|
|
launchDispatched Boolean @default(false)
|
|
followup Json?
|
|
followupDispatching Boolean @default(false)
|
|
cancelRequested Boolean @default(false)
|
|
generation Int @default(0)
|
|
wakeGeneration Int @default(-1)
|
|
version Int @default(0)
|
|
leaseToken String?
|
|
leaseExpiresAt DateTime?
|
|
nextPollAt DateTime? @default(now())
|
|
errorCount Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([providerKey, nextPollAt])
|
|
@@index([spaceId, userId, id])
|
|
@@map("cloud_agents")
|
|
}
|
|
|
|
model ExternalEffect {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
runId String
|
|
run Run @relation(fields: [runId], references: [id], onDelete: Cascade)
|
|
kind String
|
|
idempotencyKey String
|
|
status String
|
|
request Json
|
|
result Json?
|
|
reviewDecision String?
|
|
reviewReason String?
|
|
reviewModel String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([idempotencyKey])
|
|
@@index([runId, status])
|
|
@@map("external_effects")
|
|
}
|
|
|
|
model Routine {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
threadId String?
|
|
thread Thread? @relation(fields: [threadId], references: [id], onDelete: SetNull)
|
|
/// The main bot when it scheduled this routine for another bot; null when the bot or the user created it.
|
|
createdByBotId String?
|
|
name String
|
|
prompt String
|
|
crons String[]
|
|
timezone String @default("UTC")
|
|
active Boolean @default(false)
|
|
notify Boolean @default(true)
|
|
webhookEnabled Boolean @default(false)
|
|
githubEnabled Boolean @default(false)
|
|
messageProvider String?
|
|
/// One of the user's coach check-ins (morning, evening, weekly) on the main bot; null otherwise.
|
|
coachKey String?
|
|
lastRunAt DateTime?
|
|
nextRunAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
runs Run[]
|
|
|
|
@@unique([botId, coachKey])
|
|
@@index([active, nextRunAt])
|
|
@@index([spaceId, botId])
|
|
@@index([createdByBotId])
|
|
// CHECK constraint routines_coachKey_chk is created in SQL.
|
|
@@map("routines")
|
|
}
|
|
|
|
model ScratchpadItem {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
title String
|
|
status String @default("open")
|
|
notes String @default("")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([spaceId, botId, status])
|
|
@@index([spaceId, botId, updatedAt])
|
|
@@map("scratchpad_items")
|
|
}
|
|
|
|
model ProjectGoal {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
sectionId String
|
|
section BotSection @relation(fields: [sectionId], references: [id], onDelete: Cascade)
|
|
groupId String?
|
|
group ChatGroup? @relation(fields: [groupId], references: [id], onDelete: SetNull)
|
|
title String
|
|
description String?
|
|
status String @default("open")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([spaceId, sectionId, status])
|
|
@@index([spaceId, userId, sectionId, updatedAt])
|
|
@@map("project_goals")
|
|
}
|
|
|
|
model TaughtSkill {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
name String @default("")
|
|
goal String
|
|
status String
|
|
playbook Json @default("{}")
|
|
recording Json @default("{\"events\":[],\"snapshots\":[]}")
|
|
startedAt DateTime?
|
|
expiresAt DateTime?
|
|
stoppedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([spaceId, botId])
|
|
@@index([spaceId, botId, status])
|
|
// Partial unique index taught_skills_botId_recording_key is created in SQL:
|
|
// one recording session per bot.
|
|
@@map("taught_skills")
|
|
}
|
|
|
|
/// Claude Agent Skills: SKILL.md recipes shared across assistants (not per-bot).
|
|
/// The Pi runtime already understands this format; we persist and inject them.
|
|
/// Distinct from TaughtSkill (demo/record playbooks).
|
|
model AgentSkill {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
name String
|
|
description String
|
|
content String
|
|
/// user | builtin | plugin — only user skills are writable via API/tools.
|
|
source String @default("user")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([spaceId, userId])
|
|
// Case-insensitive unique index agent_skills_spaceId_userId_name_lower_key is created in SQL.
|
|
@@map("agent_skills")
|
|
}
|
|
|
|
model Connection {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
connectorId String @default("composio")
|
|
provider String
|
|
displayName String
|
|
status String
|
|
secretId String?
|
|
providerRef String?
|
|
metadata Json @default("{}")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([spaceId, userId, connectorId])
|
|
@@map("connections")
|
|
}
|
|
|
|
model CapabilityInstall {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
kind String
|
|
name String
|
|
source String
|
|
version String?
|
|
digest String?
|
|
secretId String?
|
|
config Json @default("{}")
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([spaceId, userId])
|
|
@@map("capability_installs")
|
|
}
|
|
|
|
model MemoryDocument {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
botId String?
|
|
bot Bot? @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
scope String
|
|
path String
|
|
content String
|
|
revision Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
revisions MemoryRevision[]
|
|
|
|
@@unique([spaceId, scope, botId, path])
|
|
@@index([spaceId, userId])
|
|
@@map("memory_documents")
|
|
}
|
|
|
|
model MemoryRevision {
|
|
id String @id @default(cuid())
|
|
documentId String
|
|
document MemoryDocument @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
|
revision Int
|
|
content String
|
|
sourceRunId String?
|
|
sourceThreadId String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([documentId, revision])
|
|
@@map("memory_revisions")
|
|
}
|
|
|
|
/// One item of the built-in semantic memory, recalled by cosine similarity computed in process.
|
|
model SemanticMemoryItem {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
/// Shared items are also recalled by the Space's other bots when they use shared scope.
|
|
shared Boolean @default(false)
|
|
/// Compacted-history generation of the bot's thread; null for durable memories.
|
|
generation Int?
|
|
content String
|
|
embedding Float[]
|
|
embeddingModel String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([spaceId, botId, generation, createdAt])
|
|
@@index([spaceId, shared, createdAt])
|
|
@@map("semantic_memory_items")
|
|
}
|
|
|
|
model AgentHome {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String @unique
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
revision String @default("empty")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@map("agent_homes")
|
|
}
|
|
|
|
model BrowserProfile {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String @unique
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
secretId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@map("browser_profiles")
|
|
}
|
|
|
|
model ComputerUpdate {
|
|
action String @default("update")
|
|
id String @id @default(cuid())
|
|
computerId String
|
|
computer Computer @relation(fields: [computerId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
status String @default("queued")
|
|
stage String @default("preparing")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([computerId, createdAt])
|
|
@@index([status, updatedAt])
|
|
@@map("computer_updates")
|
|
}
|
|
|
|
model Computer {
|
|
screenGeneration Int @default(0)
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
scope String @default("team")
|
|
scopeKey String @unique
|
|
homeKey String @unique
|
|
homeRevision String @default("empty")
|
|
kind String
|
|
providerRef String?
|
|
state String @default("stopped")
|
|
controlHolder String @default("none")
|
|
controlLeaseId String?
|
|
controlLeaseExpiresAt DateTime?
|
|
controlBotId String?
|
|
controlRunId String?
|
|
controlFence Int @default(0)
|
|
executionRunId String?
|
|
executionBotId String?
|
|
executionLeaseExpiresAt DateTime?
|
|
executionFence Int @default(0)
|
|
screenUrl String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
bots Bot[]
|
|
executionLeases ComputerExecutionLease[]
|
|
maintenanceId String?
|
|
updates ComputerUpdate[]
|
|
|
|
@@index([spaceId, scope])
|
|
@@map("computers")
|
|
}
|
|
|
|
model ComputerExecutionLease {
|
|
id String @id @default(cuid())
|
|
computerId String
|
|
computer Computer @relation(fields: [computerId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
runId String
|
|
fence Int @default(0)
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([computerId, botId])
|
|
@@index([runId])
|
|
@@index([computerId, expiresAt])
|
|
@@map("computer_execution_leases")
|
|
}
|
|
|
|
model Artifact {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String?
|
|
bot Bot? @relation(fields: [botId], references: [id], onDelete: SetNull)
|
|
groupId String?
|
|
group ChatGroup? @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
runId String?
|
|
name String
|
|
mimeType String
|
|
size Int
|
|
hash String
|
|
storageKey String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([spaceId, botId])
|
|
@@index([groupId])
|
|
@@map("artifacts")
|
|
}
|
|
|
|
model UsageRecord {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
/// Which bot spent this. Deliberately NOT a relation: a usage row is spend that really
|
|
/// happened, and no lifecycle event on the bot may delete it or blank its attribution.
|
|
/// `Cascade` erased the row; `SetNull` would erase the attribution instead, and because
|
|
/// deleting a bot also deletes its runs, `runId` goes null in the same breath — the row
|
|
/// would keep only space, user, model and token counts.
|
|
///
|
|
/// So the id outlives the bot and may dangle. Read it with a LEFT JOIN; `BotDeletion.id`
|
|
/// IS the bot id, so a deleted bot's name is still reachable for display.
|
|
botId String?
|
|
userId String
|
|
runId String?
|
|
run Run? @relation(fields: [runId], references: [id], onDelete: SetNull)
|
|
provider String
|
|
model String
|
|
inputTokens Int
|
|
outputTokens Int
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([spaceId, userId, createdAt])
|
|
@@map("usage_records")
|
|
}
|
|
|
|
model NotificationPreference {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
finish Boolean @default(true)
|
|
help Boolean @default(true)
|
|
takeover Boolean @default(true)
|
|
|
|
@@unique([spaceId, userId])
|
|
@@map("notification_preferences")
|
|
}
|
|
|
|
model SpaceMemoryConfig {
|
|
id String @id @default(cuid())
|
|
spaceId String @unique
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
provider String
|
|
settings Json
|
|
secretId String @unique
|
|
secret Secret @relation(fields: [secretId], references: [id], onDelete: Cascade)
|
|
defaultMemoryScope String @default("isolated")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@map("space_memory_configs")
|
|
}
|
|
|
|
model Secret {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
spaceId String?
|
|
space Space? @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
kind String
|
|
ciphertext String
|
|
createdAt DateTime @default(now())
|
|
mcpServers McpServer[]
|
|
spaceMemoryConfig SpaceMemoryConfig?
|
|
agentSecret AgentSecret?
|
|
|
|
@@index([userId, spaceId])
|
|
@@map("secrets")
|
|
}
|
|
|
|
model McpServer {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
slug String
|
|
name String
|
|
description String @default("")
|
|
transport String
|
|
endpoint String?
|
|
command String?
|
|
args Json @default("[]")
|
|
env Json @default("{}")
|
|
headers Json @default("{}")
|
|
secretId String?
|
|
secret Secret? @relation(fields: [secretId], references: [id], onDelete: SetNull)
|
|
enabled Boolean @default(true)
|
|
revision Int @default(1)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
assignments BotMcpServer[]
|
|
oauthSessions McpOAuthSession[]
|
|
|
|
@@unique([spaceId, userId, slug])
|
|
@@index([spaceId, userId])
|
|
@@map("mcp_servers")
|
|
}
|
|
|
|
model McpOAuthSession {
|
|
id String @id
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
serverId String
|
|
server McpServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
|
endpoint String
|
|
redirectUri String
|
|
oauthCiphertext String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([spaceId, userId, createdAt])
|
|
@@map("mcp_oauth_sessions")
|
|
}
|
|
|
|
model BotMcpServer {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
serverId String
|
|
server McpServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
allowAllTools Boolean @default(true)
|
|
allowedTools Json @default("[]")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([botId, serverId])
|
|
@@index([spaceId, userId, botId])
|
|
@@map("bot_mcp_servers")
|
|
}
|
|
|
|
model MessagingIdentity {
|
|
id String @id @default(cuid())
|
|
provider String
|
|
/// Sender address within the provider: E.164 number, Slack user id, ….
|
|
address String
|
|
/// Opaque provider thread id for the 1:1 conversation; learned on inbound.
|
|
dmThreadId String?
|
|
userId String
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String @unique
|
|
verifiedAt DateTime?
|
|
lastInboundAt DateTime?
|
|
outboundSinceInbound Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([provider, address])
|
|
@@index([userId])
|
|
@@map("messaging_identities")
|
|
}
|
|
|
|
model MessagingChannel {
|
|
id String @id @default(cuid())
|
|
provider String
|
|
/// Opaque provider thread id of the group conversation.
|
|
threadId String @unique
|
|
name String?
|
|
introPostedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
members MessagingChannelMember[]
|
|
|
|
@@map("messaging_channels")
|
|
}
|
|
|
|
model MessagingChannelMember {
|
|
id String @id @default(cuid())
|
|
channelId String
|
|
channel MessagingChannel @relation(fields: [channelId], references: [id], onDelete: Cascade)
|
|
address String
|
|
identityId String?
|
|
status String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([channelId, address])
|
|
@@index([identityId])
|
|
@@map("messaging_channel_members")
|
|
}
|
|
|
|
/// Short-lived web-issued code binding a chat address to an existing user+bot.
|
|
model MessagingLinkCode {
|
|
id String @id @default(cuid())
|
|
code String @unique
|
|
userId String
|
|
spaceId String
|
|
botId String
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@map("messaging_link_codes")
|
|
}
|
|
|
|
model MessagingOutbound {
|
|
id String @id @default(cuid())
|
|
idempotencyKey String @unique
|
|
kind String
|
|
/// DM rows resolve their thread through the identity at drain time.
|
|
identityId String?
|
|
/// Group/intro rows post straight to this provider thread id.
|
|
threadId String?
|
|
body String
|
|
status String @default("pending")
|
|
attempts Int @default(0)
|
|
nextAttemptAt DateTime?
|
|
providerHandle String?
|
|
sourceMessageId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([status, nextAttemptAt])
|
|
@@map("messaging_outbound")
|
|
}
|
|
|
|
model AgentConnection {
|
|
id String @id @default(cuid())
|
|
requesterBotId String
|
|
targetBotId String
|
|
status String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([requesterBotId, targetBotId])
|
|
@@index([targetBotId, status])
|
|
@@map("agent_connections")
|
|
}
|
|
|
|
model BotSecret {
|
|
id String @id
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
name String
|
|
origin String
|
|
auth Json
|
|
ciphertext String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, spaceId, botId, name])
|
|
@@index([botId])
|
|
@@index([spaceId])
|
|
@@map("bot_secrets")
|
|
}
|
|
|
|
|
|
model ExternalConversation {
|
|
id String @id @default(cuid())
|
|
provider String
|
|
workspaceId String
|
|
externalKey String
|
|
conversationId String
|
|
displayName String?
|
|
participantNames String[] @default([])
|
|
teamChatAmbientEnabled Boolean?
|
|
teamChatRules String?
|
|
automatedSenderPolicies Json @default("{}")
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
botId String
|
|
bot Bot @relation(fields: [botId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
thread Thread?
|
|
messages ExternalMessage[]
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([provider, workspaceId, externalKey])
|
|
@@index([spaceId, botId])
|
|
@@map("external_conversations")
|
|
}
|
|
|
|
model ExternalMessage {
|
|
id String @id @default(cuid())
|
|
externalConversationId String
|
|
externalConversation ExternalConversation @relation(fields: [externalConversationId], references: [id], onDelete: Cascade)
|
|
providerEventId String
|
|
kind String @default("direct")
|
|
senderId String
|
|
senderName String
|
|
senderIsBot Boolean @default(false)
|
|
content String
|
|
replyThreadId String?
|
|
batchContext String?
|
|
engagementReason String?
|
|
judgedAt DateTime?
|
|
status String @default("received")
|
|
runId String? @unique
|
|
run Run? @relation(fields: [runId], references: [id], onDelete: SetNull)
|
|
threadMessageId String? @unique
|
|
threadMessage Message? @relation(fields: [threadMessageId], references: [id], onDelete: SetNull)
|
|
attempts Int @default(0)
|
|
nextAttemptAt DateTime?
|
|
lastError String?
|
|
providerReplyHandle String?
|
|
deliveredAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([externalConversationId, providerEventId])
|
|
@@index([status, nextAttemptAt])
|
|
@@map("external_messages")
|
|
}
|
|
|
|
model AgentSecret {
|
|
id String @id @default(cuid())
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
createdByUserId String
|
|
createdByUser User @relation(fields: [createdByUserId], references: [id], onDelete: Cascade)
|
|
name String
|
|
secretId String @unique
|
|
secret Secret @relation(fields: [secretId], references: [id], onDelete: Cascade)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([spaceId, name])
|
|
@@index([spaceId, updatedAt])
|
|
@@map("agent_secrets")
|
|
}
|
|
|
|
// Deployment-owned integration credentials; never exposed through read DTOs.
|
|
model IntegrationProviderConfig {
|
|
id String @id
|
|
ciphertext String
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@map("integration_provider_configs")
|
|
}
|
|
|
|
/// What the user's agents know about the user across spaces. One row per user.
|
|
model UserProfile {
|
|
userId String @id
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
timeZone String?
|
|
locale String?
|
|
language String?
|
|
communicationStyle String @default("")
|
|
summary String @default("")
|
|
consolidatedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@map("user_profiles")
|
|
}
|
|
|
|
/// One durable statement about the user, with its origin and consent state.
|
|
model UserFact {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
category String
|
|
value String
|
|
confidence Float @default(1)
|
|
source String
|
|
status String @default("active")
|
|
sensitive Boolean @default(false)
|
|
allSpaces Boolean @default(true)
|
|
sourceBotId String?
|
|
sourceThreadId String?
|
|
sourceMessageId String?
|
|
embedding Float[] @default([])
|
|
embeddingModel String?
|
|
confirmedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
visibleSpaces UserFactSpace[]
|
|
|
|
@@index([userId, status, category, updatedAt])
|
|
// CHECK constraints user_facts_category_chk, user_facts_source_chk and user_facts_status_chk are created in SQL.
|
|
@@map("user_facts")
|
|
}
|
|
|
|
/// Spaces a fact is limited to when UserFact.allSpaces is false.
|
|
model UserFactSpace {
|
|
factId String
|
|
fact UserFact @relation(fields: [factId], references: [id], onDelete: Cascade)
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
|
|
@@id([factId, spaceId])
|
|
@@index([spaceId])
|
|
@@map("user_fact_spaces")
|
|
}
|
|
|
|
/// Something the user wants to get done; the main bot coaches them toward it.
|
|
model UserGoal {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
title String
|
|
detail String @default("")
|
|
status String @default("active")
|
|
/// The user's local date the goal is due, if any.
|
|
targetDate DateTime? @db.Date
|
|
spaceId String?
|
|
space Space? @relation(fields: [spaceId], references: [id], onDelete: SetNull)
|
|
source String
|
|
/// Like the other provenance ids it has no foreign key.
|
|
createdByBotId String?
|
|
completedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
habits Habit[]
|
|
checkIns CheckIn[]
|
|
|
|
@@index([userId, status, createdAt])
|
|
@@index([spaceId])
|
|
// CHECK constraints user_goals_status_chk and user_goals_source_chk are created in SQL.
|
|
@@map("user_goals")
|
|
}
|
|
|
|
/// A recurring practice the user wants to keep, checked off per day or week.
|
|
model Habit {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
title String
|
|
cadence String @default("daily")
|
|
goalId String?
|
|
goal UserGoal? @relation(fields: [goalId], references: [id], onDelete: SetNull)
|
|
active Boolean @default(true)
|
|
source String
|
|
createdByBotId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
checkIns CheckIn[]
|
|
|
|
@@index([userId, active, createdAt])
|
|
@@index([goalId])
|
|
// CHECK constraints habits_cadence_chk and habits_source_chk are created in SQL.
|
|
@@map("habits")
|
|
}
|
|
|
|
/// What the user reported for one day: a habit kept or missed, goal progress, or how the day went.
|
|
model CheckIn {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
kind String
|
|
habitId String?
|
|
habit Habit? @relation(fields: [habitId], references: [id], onDelete: Cascade)
|
|
goalId String?
|
|
goal UserGoal? @relation(fields: [goalId], references: [id], onDelete: Cascade)
|
|
/// The user's local date.
|
|
date DateTime @db.Date
|
|
done Boolean?
|
|
mood Int?
|
|
energy Int?
|
|
note String @default("")
|
|
createdByBotId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([habitId, date])
|
|
@@unique([goalId, date])
|
|
@@index([userId, date])
|
|
// CHECK constraints check_ins_kind_chk, check_ins_target_chk, check_ins_mood_chk and
|
|
// check_ins_energy_chk and the partial unique index check_ins_userId_date_daily_key are
|
|
// created in SQL.
|
|
@@map("check_ins")
|
|
}
|
|
|
|
/// The main bot's short brief for one of the user's days.
|
|
model DailyBrief {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
/// The user's local date.
|
|
date DateTime @db.Date
|
|
text String
|
|
botId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, date])
|
|
@@map("daily_briefs")
|
|
}
|
|
|
|
/// Work the main bot hands to a specialist, reported back in the main conversation.
|
|
model DelegatedTask {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
spaceId String
|
|
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
|
|
threadId String
|
|
thread Thread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
|
requesterBotId String
|
|
assigneeBotId String
|
|
assigneeSpaceId String
|
|
title String
|
|
brief String
|
|
status String @default("queued")
|
|
deadlineAt DateTime?
|
|
result String?
|
|
artifacts Json @default("[]")
|
|
runId String? @unique
|
|
messageId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
completedAt DateTime?
|
|
|
|
@@index([threadId, status, createdAt])
|
|
@@index([assigneeBotId, status])
|
|
@@index([userId, status, updatedAt])
|
|
// CHECK constraint delegated_tasks_status_chk is created in SQL.
|
|
@@map("delegated_tasks")
|
|
}
|