Files
hermes-agent-mission-control/prisma/schema.prisma
T

546 lines
16 KiB
Plaintext

generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "linux-arm64-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
}
model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}
model Draft {
id String @id @default(uuid())
text String
title String?
model String?
status String @default("pending")
type String?
category String?
hookType String?
feedbackRating String?
feedbackReason String?
viralScore Json?
impressionScore Json?
scheduledDate String?
scheduledTime String?
visualUrl String?
variantGroup String?
postedAt DateTime?
postedUrl String?
tweetUrl String?
tweetId String?
metrics Json?
editHistory Json? @default("[]")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
quoteUrl String?
quoteText String?
// Content intelligence fields
source String? // "sage" | "organic" | "csv_import"
engagementScore Float? // time-weighted composite score (updated by scoring engine)
engagementTier String? // "top20" | "bottom20" | null (cleared/set on rescore)
@@index([status])
@@index([createdAt])
@@index([tweetId])
@@index([source])
@@index([engagementTier])
}
model TweetMetric {
id String @id @default(cuid())
draftId String
tweetId String?
url String?
postedAt DateTime?
checkpoints Json?
createdAt DateTime @default(now())
@@index([draftId])
}
model Idea {
id String @id @default(uuid())
title String
description String?
category String?
type String?
model String?
status String?
timestamp DateTime @default(now())
@@index([category])
@@index([type])
}
model ContentCalendar {
id String @id @default(cuid())
week String @unique
data Json
}
model YoutubeIdea {
id String @id @default(cuid())
title String
hook String?
angle String?
hookType String?
funnelStage String?
status String @default("pending")
rejectedReason String?
category String? @default("brands")
sourceUrl String?
sourceSummary String?
competitionScore String? // "gap" | "moderate" | "crowded"
competitionData Json? // { resultCount, avgViews, topVideos[], scoredAt }
createdAt DateTime @default(now())
@@index([status])
}
model YoutubeScript {
id String @id @default(cuid())
title String
hook String?
storySetup String?
conflict String?
insight String?
cta String?
caption String?
onScreenText String?
hookType String?
funnelStage String?
factCheck Json?
fullScript String?
status String @default("draft")
rejectedReason String?
// SEO Package
seoTitle String?
seoDescription String?
seoTags Json?
seoChapters Json?
titleVariants Json?
thumbnailConcept String?
// Render output (shorts)
videoUrl String?
thumbnailUrl String?
render YoutubeRender?
// Source content (tweet/thread/news) rendered as visual cards
sourceUrl String?
sourceData Json?
// Content category (brands | ai-agent-tips | ...)
category String? @default("brands")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
}
model YoutubeFeedback {
id String @id @default(cuid())
title String
reason String?
type String?
date DateTime @default(now())
}
model YoutubeRender {
id String @id @default(cuid())
scriptId String @unique
script YoutubeScript @relation(fields: [scriptId], references: [id], onDelete: Cascade)
status String @default("queued") // queued | rendering | rendered | failed
videoPath String?
thumbnailPath String?
durationMs Int?
error String?
metadata Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
}
model LongformScript {
id String @id @default(cuid())
title String
type String?
status String @default("draft")
platform String?
platforms Json?
description String?
hook String?
outline String?
fullScript String?
targetLength String?
factCheck Json?
notes String?
rejectedReason String?
youtubeUrl String?
thumbnailUrl String?
tweetUrl String?
youtubeViews Int?
youtubeLikes Int?
youtubeComments Int?
tweetViews Int?
tweetLikes Int?
tweetBookmarks Int?
tweetRetweets Int?
tweetReplies Int?
postedAt DateTime?
competitionScore String? // "gap" | "moderate" | "crowded"
competitionData Json? // { resultCount, avgViews, topVideos[], scoredAt }
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
}
model AgentState {
id String @id
name String
emoji String?
role String?
status String @default("offline")
lastActive DateTime?
tasksCompleted Int @default(0)
totalCost Float @default(0)
currentTask String?
recentActivity Json? @default("[]")
updatedAt DateTime @updatedAt
}
model AgentBusMessage {
id String @id @default(uuid())
fromAgent String
toAgent String
type String
content String
metadata Json?
read Boolean @default(false)
timestamp DateTime @default(now())
@@index([toAgent, read])
@@index([fromAgent])
}
model BattleRoyaleBot {
id String @id
state Json
updatedAt DateTime @updatedAt
}
model ContentRequest {
id String @id @default(uuid())
prompt String
status String @default("pending")
resultDraftIds Json?
createdAt DateTime @default(now())
completedAt DateTime?
}
// ─── Generic Key-Value Store ────────────────────────────────────
model DataStore {
key String @id
data Json
updatedAt DateTime @updatedAt
}
// ─── Daily Briefs ───────────────────────────────────────────────
model Brief {
id String @id @default(cuid())
date String @unique
content String @db.Text
source String @default("brief")
createdAt DateTime @default(now())
}
// ─── Missions ───────────────────────────────────────────────────
model Mission {
id String @id @default(uuid())
agentId String
title String
description String @db.Text
status String @default("pending") // pending, active, completed, failed
priority String @default("medium") // high, medium, low
result String? @db.Text
createdAt DateTime @default(now())
completedAt DateTime?
@@index([agentId])
@@index([status])
}
// ─── Articles ───────────────────────────────────────────────────
model Article {
id String @id @default(uuid())
title String
body String @db.Text
track String // "mega-viral" | "local-viral" | "icp-viral"
status String @default("idea") // "idea" | "draft" | "ready" | "posted"
qtTweet String? @db.Text
qtUrl String?
heroImageUrl String?
heroImageHtml String? @db.Text
inspirationUrls String? @db.Text // JSON array of URLs
themes String? // JSON array of themes
scheduledDate String?
postedAt DateTime?
postedUrl String?
impressions Int?
likes Int?
bookmarks Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
@@index([track])
@@index([scheduledDate])
}
model SavedTitle {
id String @id @default(cuid())
title String
track String
themes String? // JSON array
createdAt DateTime @default(now())
@@index([track])
}
// ─── Client Pulse Telegram Bot ─────────────────────────────────
model ClientPulseClient {
id String @id @default(cuid())
notionPageId String? @unique
clientName String
status String?
services Json @default("[]")
productManagerName String?
renewalDate DateTime?
endDate DateTime?
expectedCheckinHours Int @default(72)
sourceUpdatedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
chats ClientPulseChat[]
analyses ClientPulseAnalysis[]
alerts ClientPulseAlert[]
@@index([status])
@@index([clientName])
@@index([productManagerName])
}
model ClientPulseChat {
id String @id @default(cuid())
telegramChatId String @unique
title String
linkedClientId String?
status String @default("active")
internalUserIds Json @default("[]")
lastSeenAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
linkedClient ClientPulseClient? @relation(fields: [linkedClientId], references: [id], onDelete: SetNull)
messages ClientPulseMessage[]
alerts ClientPulseAlert[]
@@index([linkedClientId])
@@index([status])
}
model ClientPulseMessage {
id String @id @default(cuid())
telegramMessageId String
telegramChatId String
chatId String?
senderTelegramId String
senderName String
isInternal Boolean @default(false)
isBot Boolean @default(false)
text String @db.Text
sentAt DateTime
intent String @default("other")
sentiment String @default("neutral")
needsReply Boolean @default(false)
rawJson Json?
createdAt DateTime @default(now())
chat ClientPulseChat? @relation(fields: [chatId], references: [id], onDelete: SetNull)
@@unique([telegramChatId, telegramMessageId])
@@index([telegramChatId, sentAt])
@@index([chatId, sentAt])
@@index([needsReply])
}
model ClientPulseAnalysis {
id String @id @default(cuid())
clientId String
windowStart DateTime
windowEnd DateTime
sentimentScore Int
responseScore Int
cadenceScore Int
renewalRiskScore Int
positiveSignalScore Int @default(70)
overallScore Int
category String
reasons Json @default("[]")
evidence Json @default("[]")
recommendedAction String? @db.Text
llmSummary String? @db.Text
analysisSource String @default("fallback")
analyzedAt DateTime @default(now())
client ClientPulseClient @relation(fields: [clientId], references: [id], onDelete: Cascade)
@@index([clientId, analyzedAt])
@@index([category])
@@index([overallScore])
}
model ClientPulseAlert {
id String @id @default(cuid())
clientId String?
chatId String?
type String
severity String
evidence Json @default("[]")
reason String @db.Text
recommendedAction String? @db.Text
sentTo String?
sentAt DateTime?
acknowledgedAt DateTime?
createdAt DateTime @default(now())
client ClientPulseClient? @relation(fields: [clientId], references: [id], onDelete: SetNull)
chat ClientPulseChat? @relation(fields: [chatId], references: [id], onDelete: SetNull)
@@index([clientId, createdAt])
@@index([severity])
@@index([sentAt])
@@index([acknowledgedAt])
}
// ─── Hermes bus (Hermy HQ website ↔ Hermes on the Mac mini) ─────
// Shared Postgres is the message bus. A bridge on the mini polls
// AgentRequest, runs approved/safe ones via the `hermes` CLI, and
// mirrors kanban tasks + activity back into HermesTask / AgentEvent.
model AgentRequest {
id String @id @default(cuid())
origin String @default("web") // "web" | "hermes"
kind String @default("oneshot") // "oneshot" | "kanban" | "chat"
title String
prompt String?
sideEffecting Boolean @default(false)
status String @default("queued") // queued | awaiting_approval | approved | running | done | failed | rejected
result String?
error String?
hermesTaskId String?
decidedAt DateTime?
startedAt DateTime?
finishedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([status])
@@index([createdAt])
}
model AgentEvent {
id String @id @default(cuid())
kind String @default("activity") // activity | run | status | memory
title String
detail String?
agent String?
level String @default("info") // info | up | warn | down
meta Json?
createdAt DateTime @default(now())
@@index([createdAt])
}
model HermesTask {
id String @id // Hermes task id
board String @default("default")
title String
assignee String?
status String @default("todo")
priority Int?
result String?
updatedAt DateTime @default(now())
syncedAt DateTime @updatedAt
@@index([board])
@@index([status])
}
// ─── Hermes Memory Wiki (warm tier — git-tracked markdown, mirrored) ─────
model HermesMemory {
id String @id // wiki entry slug (relative path w/o .md)
path String // file path relative to wiki root
type String @default("fact")
title String
status String @default("active")
confidence String?
provenance String?
tags String[]
links String[]
body String @default("")
validFrom DateTime?
validTo DateTime?
updatedAt DateTime @default(now())
syncedAt DateTime @updatedAt
@@index([type])
@@index([status])
}