21772e7611
Task 8.1: 12 Calendar/Meeting tables (meeting_request, meeting, participant, transcript, segment, summary, generic action_item + meeting link, provider account, calendar_event, availability_window, sync_cursor) + 13 enums; migration calendar-init. Adds calendar.meeting.requested/scheduled/reminder.queued/ summarized events. resetDb truncates the new tables. Kernel already had the MEETING_REQUEST / MEETING_FOLLOWUP / callback.meetingRef stubs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1158 lines
35 KiB
Plaintext
1158 lines
35 KiB
Plaintext
// IIOS Interaction Kernel — P1 schema (native portal only).
|
|
// Lifted from the Bottom-Up Evolution Atlas §10 kernel DDL. ONLY kernel tables:
|
|
// no support / inbox / route / AI tables (those are earned in later phases).
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// ─── Enums (one value per line) ───────────────────────────────────
|
|
enum IiosActorKind {
|
|
HUMAN
|
|
AI
|
|
SERVICE
|
|
BOT
|
|
UNKNOWN
|
|
}
|
|
|
|
enum IiosHandleKind {
|
|
PORTAL_USER
|
|
EMAIL
|
|
PHONE
|
|
WHATSAPP
|
|
SLACK
|
|
MATTERMOST
|
|
TELEGRAM
|
|
EXTERNAL_VISITOR
|
|
}
|
|
|
|
enum IiosInteractionKind {
|
|
MESSAGE
|
|
EMAIL
|
|
SYSTEM_NOTICE
|
|
INBOX_WORK
|
|
SUPPORT_CASE
|
|
MEETING_REQUEST
|
|
DIGEST
|
|
SUMMARY
|
|
NOTIFICATION
|
|
}
|
|
|
|
enum IiosMessagePartKind {
|
|
TEXT
|
|
HTML
|
|
MARKDOWN
|
|
MEDIA_REF
|
|
FILE_REF
|
|
VOICE_REF
|
|
LOCATION
|
|
STRUCTURED_JSON
|
|
}
|
|
|
|
enum IiosDeliveryState {
|
|
RECEIVED
|
|
NORMALIZED
|
|
POLICY_HELD
|
|
STORED
|
|
DELIVERED
|
|
READ
|
|
FAILED
|
|
DELETED_LOCAL
|
|
REDACTED
|
|
}
|
|
|
|
enum IiosReceiptKind {
|
|
DELIVERED
|
|
READ
|
|
ACKED
|
|
FAILED
|
|
SUPPRESSED
|
|
RATE_LIMITED
|
|
}
|
|
|
|
enum IiosInboxItemKind {
|
|
NEEDS_REPLY
|
|
NEEDS_REVIEW
|
|
NEEDS_APPROVAL
|
|
SUPPORT_UPDATE
|
|
MEETING_FOLLOWUP
|
|
DIGEST
|
|
SYSTEM_ALERT
|
|
CRM_OWNER_INTEREST
|
|
}
|
|
|
|
enum IiosInboxState {
|
|
OPEN
|
|
SNOOZED
|
|
DONE
|
|
ARCHIVED
|
|
CANCELLED
|
|
STALE
|
|
}
|
|
|
|
enum IiosTicketState {
|
|
NEW
|
|
OPEN
|
|
PENDING_CUSTOMER
|
|
PENDING_INTERNAL
|
|
RESOLVED
|
|
CLOSED
|
|
CANCELLED
|
|
}
|
|
|
|
enum IiosTicketPriority {
|
|
P0
|
|
P1
|
|
P2
|
|
P3
|
|
P4
|
|
}
|
|
|
|
enum IiosCallbackStatus {
|
|
PENDING
|
|
SCHEDULED
|
|
COMPLETED
|
|
MISSED
|
|
CANCELLED
|
|
}
|
|
|
|
enum IiosRouteMode {
|
|
MANUAL
|
|
AUTOMATIC
|
|
HYBRID
|
|
SIMULATION_ONLY
|
|
}
|
|
|
|
enum IiosOutputFormat {
|
|
FORWARD
|
|
THREADED
|
|
DIGEST
|
|
SUMMARY
|
|
TRANSCRIPT
|
|
}
|
|
|
|
enum IiosRouteDecisionState {
|
|
ALLOW
|
|
DENY
|
|
REVIEW
|
|
SUPPRESS
|
|
SIMULATED
|
|
}
|
|
|
|
// ─── AI enums (P7) ────────────────────────────────────────────────
|
|
|
|
enum IiosAiJobType {
|
|
CLASSIFY
|
|
SUMMARIZE
|
|
EXTRACT
|
|
}
|
|
|
|
enum IiosAiJobStatus {
|
|
PENDING
|
|
RUNNING
|
|
DONE
|
|
FAILED
|
|
ABSTAINED
|
|
}
|
|
|
|
enum IiosAiArtifactType {
|
|
CLASSIFICATION
|
|
SUMMARY
|
|
TRANSCRIPT
|
|
DIGEST
|
|
EXTRACTION
|
|
}
|
|
|
|
enum IiosAiArtifactStatus {
|
|
PROPOSED
|
|
ACCEPTED
|
|
REJECTED
|
|
SUPERSEDED
|
|
}
|
|
|
|
enum IiosAiClaimType {
|
|
MODERATION_FLAG
|
|
EVENT
|
|
FAQ
|
|
INTENT
|
|
PRIORITY
|
|
}
|
|
|
|
enum IiosAiClaimValidation {
|
|
UNVALIDATED
|
|
ACCEPTED
|
|
REJECTED
|
|
}
|
|
|
|
// ─── Calendar / Meeting enums (P8) ────────────────────────────────
|
|
|
|
enum IiosMeetingType {
|
|
ZOOM
|
|
PHONE
|
|
IN_PERSON
|
|
CALLBACK
|
|
INTERNAL
|
|
}
|
|
|
|
enum IiosMeetingStatus {
|
|
REQUESTED
|
|
SCHEDULED
|
|
CONFIRMED
|
|
CANCELLED
|
|
COMPLETED
|
|
NO_SHOW
|
|
}
|
|
|
|
enum IiosMeetingRequestStatus {
|
|
PENDING
|
|
PROPOSED
|
|
ACCEPTED
|
|
DECLINED
|
|
SCHEDULED
|
|
CANCELLED
|
|
}
|
|
|
|
enum IiosParticipantRole {
|
|
ORGANIZER
|
|
REQUIRED
|
|
OPTIONAL
|
|
}
|
|
|
|
enum IiosAttendanceStatus {
|
|
INVITED
|
|
ACCEPTED
|
|
DECLINED
|
|
TENTATIVE
|
|
ATTENDED
|
|
NO_SHOW
|
|
}
|
|
|
|
enum IiosParticipantVisibility {
|
|
FULL
|
|
LIMITED
|
|
NONE
|
|
}
|
|
|
|
enum IiosConsentStatus {
|
|
UNKNOWN
|
|
GRANTED
|
|
DENIED
|
|
REVOKED
|
|
}
|
|
|
|
enum IiosTranscriptStatus {
|
|
PENDING
|
|
READY
|
|
BLOCKED
|
|
}
|
|
|
|
enum IiosSummaryStatus {
|
|
PROPOSED
|
|
APPROVED
|
|
PUBLISHED
|
|
REJECTED
|
|
}
|
|
|
|
enum IiosActionItemStatus {
|
|
OPEN
|
|
IN_PROGRESS
|
|
DONE
|
|
CANCELLED
|
|
}
|
|
|
|
enum IiosCalendarProviderType {
|
|
SIMULATED
|
|
GOOGLE
|
|
OUTLOOK
|
|
ICS
|
|
}
|
|
|
|
enum IiosCalendarSyncStatus {
|
|
IDLE
|
|
SYNCING
|
|
ERROR
|
|
}
|
|
|
|
enum IiosCalendarEventStatus {
|
|
CONFIRMED
|
|
TENTATIVE
|
|
CANCELLED
|
|
}
|
|
|
|
// ─── Kernel tables ────────────────────────────────────────────────
|
|
|
|
/// Frozen six-vector scope. orgId + appId are REQUIRED (no global-by-null).
|
|
model IiosScope {
|
|
id String @id @default(cuid())
|
|
orgId String
|
|
appId String
|
|
buId String?
|
|
tenantId String?
|
|
workspaceId String?
|
|
projectId String?
|
|
userScopeId String?
|
|
createdAt DateTime @default(now())
|
|
|
|
sourceHandles IiosSourceHandle[]
|
|
channels IiosChannel[]
|
|
threads IiosThread[]
|
|
interactions IiosInteraction[]
|
|
inboxItems IiosInboxItem[]
|
|
supportQueues IiosSupportQueue[]
|
|
tickets IiosTicket[]
|
|
callbacks IiosCallbackRequest[]
|
|
|
|
@@index([orgId, appId, tenantId])
|
|
}
|
|
|
|
/// A channel-specific handle (phone/email/portal user). NOT identity — stays
|
|
/// UNVERIFIED until MDM resolves it; no silent merge.
|
|
model IiosSourceHandle {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
|
kind IiosHandleKind
|
|
externalId String
|
|
displayName String?
|
|
canonicalEntityId String? // set only when MDM resolves; null = unresolved
|
|
confidence Float @default(0)
|
|
verificationState String @default("UNVERIFIED")
|
|
firstSeenAt DateTime @default(now())
|
|
lastSeenAt DateTime @default(now())
|
|
metadata Json?
|
|
|
|
actors IiosActorRef[]
|
|
|
|
@@unique([scopeId, kind, externalId])
|
|
@@index([canonicalEntityId])
|
|
}
|
|
|
|
/// The local actor (human / AI / service / unknown) behind an interaction.
|
|
model IiosActorRef {
|
|
id String @id @default(cuid())
|
|
kind IiosActorKind
|
|
sourceHandleId String?
|
|
sourceHandle IiosSourceHandle? @relation(fields: [sourceHandleId], references: [id])
|
|
platformPrincipalRef String?
|
|
canonicalEntityId String?
|
|
displayName String?
|
|
createdAt DateTime @default(now())
|
|
|
|
threadsCreated IiosThread[] @relation("ThreadCreatedBy")
|
|
participations IiosThreadParticipant[]
|
|
interactions IiosInteraction[]
|
|
receipts IiosMessageReceipt[]
|
|
unreadCounters IiosUnreadCounter[]
|
|
inboxItemsOwned IiosInboxItem[]
|
|
teamMemberships IiosSupportTeamMember[]
|
|
ticketsRequested IiosTicket[] @relation("TicketRequester")
|
|
ticketsAssigned IiosTicket[] @relation("TicketAssignee")
|
|
callbacksRequested IiosCallbackRequest[]
|
|
}
|
|
|
|
/// A configured channel surface (PORTAL in P1).
|
|
model IiosChannel {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
|
channelType String
|
|
channelName String
|
|
direction String @default("BOTH")
|
|
capabilityContract Json?
|
|
externalRef String?
|
|
enabled Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
|
|
threads IiosThread[]
|
|
interactions IiosInteraction[]
|
|
|
|
@@index([scopeId, channelType])
|
|
}
|
|
|
|
/// A conversation/thread on a channel.
|
|
model IiosThread {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
|
channelId String?
|
|
channel IiosChannel? @relation(fields: [channelId], references: [id])
|
|
threadKind String @default("CONVERSATION")
|
|
subject String?
|
|
externalThreadRef String?
|
|
status String @default("OPEN")
|
|
visibilityClass String @default("PRIVATE")
|
|
createdByActorId String?
|
|
createdByActor IiosActorRef? @relation("ThreadCreatedBy", fields: [createdByActorId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
metadata Json?
|
|
|
|
participants IiosThreadParticipant[]
|
|
interactions IiosInteraction[]
|
|
unreadCounters IiosUnreadCounter[]
|
|
inboxItems IiosInboxItem[]
|
|
ticketLinks IiosTicketThreadLink[]
|
|
|
|
@@index([scopeId, status])
|
|
}
|
|
|
|
model IiosThreadParticipant {
|
|
threadId String
|
|
thread IiosThread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
|
actorId String
|
|
actor IiosActorRef @relation(fields: [actorId], references: [id])
|
|
participantRole String @default("MEMBER")
|
|
joinedAt DateTime @default(now())
|
|
leftAt DateTime?
|
|
|
|
@@id([threadId, actorId])
|
|
}
|
|
|
|
/// The semantic envelope. Idempotent per (scope, idempotencyKey).
|
|
model IiosInteraction {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
|
kind IiosInteractionKind
|
|
threadId String?
|
|
thread IiosThread? @relation(fields: [threadId], references: [id])
|
|
channelId String?
|
|
channel IiosChannel? @relation(fields: [channelId], references: [id])
|
|
actorId String?
|
|
actor IiosActorRef? @relation(fields: [actorId], references: [id])
|
|
parentInteractionId String?
|
|
parent IiosInteraction? @relation("InteractionParent", fields: [parentInteractionId], references: [id])
|
|
children IiosInteraction[] @relation("InteractionParent")
|
|
externalId String?
|
|
idempotencyKey String
|
|
occurredAt DateTime @default(now())
|
|
receivedAt DateTime @default(now())
|
|
status IiosDeliveryState @default(RECEIVED)
|
|
policyDecisionRef String?
|
|
consentReceiptRef String?
|
|
crreBundleRef String?
|
|
traceId String?
|
|
metadata Json?
|
|
|
|
parts IiosMessagePart[]
|
|
receipts IiosMessageReceipt[]
|
|
inboxItems IiosInboxItem[]
|
|
ticketsCreatedFrom IiosTicket[]
|
|
|
|
@@unique([scopeId, idempotencyKey])
|
|
@@index([threadId, occurredAt])
|
|
}
|
|
|
|
model IiosMessagePart {
|
|
id String @id @default(cuid())
|
|
interactionId String
|
|
interaction IiosInteraction @relation(fields: [interactionId], references: [id], onDelete: Cascade)
|
|
partIndex Int
|
|
kind IiosMessagePartKind
|
|
bodyText String?
|
|
contentRef String?
|
|
mimeType String?
|
|
sizeBytes BigInt?
|
|
checksumSha256 String?
|
|
metadata Json?
|
|
|
|
@@unique([interactionId, partIndex])
|
|
}
|
|
|
|
/// Transactional outbox — written in the same tx as the business row.
|
|
model IiosOutboxEvent {
|
|
eventId String @id @default(cuid())
|
|
aggregateType String
|
|
aggregateId String
|
|
eventType String
|
|
cloudEvent Json
|
|
partitionKey String
|
|
status String @default("PENDING")
|
|
attempts Int @default(0)
|
|
nextAttemptAt DateTime @default(now())
|
|
createdAt DateTime @default(now())
|
|
publishedAt DateTime?
|
|
|
|
@@index([status, nextAttemptAt, createdAt])
|
|
}
|
|
|
|
/// Idempotent-consumer ledger for the outbox relay / downstream consumers.
|
|
model IiosProcessedEvent {
|
|
consumerName String
|
|
eventId String
|
|
processedAt DateTime @default(now())
|
|
resultHash String?
|
|
|
|
@@id([consumerName, eventId])
|
|
}
|
|
|
|
// ─── P2: messaging — receipts & unread ────────────────────────────
|
|
|
|
/// Delivery/read receipt for a message interaction, per actor.
|
|
model IiosMessageReceipt {
|
|
id String @id @default(cuid())
|
|
interactionId String
|
|
interaction IiosInteraction @relation(fields: [interactionId], references: [id], onDelete: Cascade)
|
|
actorId String?
|
|
actor IiosActorRef? @relation(fields: [actorId], references: [id])
|
|
receiptKind IiosReceiptKind
|
|
occurredAt DateTime @default(now())
|
|
providerReceiptRef String?
|
|
metadata Json?
|
|
|
|
@@unique([interactionId, actorId, receiptKind])
|
|
@@index([interactionId])
|
|
}
|
|
|
|
/// Per-(thread, actor) unread count + last-read marker.
|
|
model IiosUnreadCounter {
|
|
threadId String
|
|
thread IiosThread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
|
actorId String
|
|
actor IiosActorRef @relation(fields: [actorId], references: [id])
|
|
unreadCount Int @default(0)
|
|
lastReadInteractionId String?
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@id([threadId, actorId])
|
|
}
|
|
|
|
// ─── P3: Inbox (work/awareness surface) ───────────────────────────
|
|
|
|
/// A durable action/awareness item owned by one actor. Fed by the event
|
|
/// projector (deterministic, collapse-per-thread). NOT a thin unread projection.
|
|
model IiosInboxItem {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
|
ownerActorId String
|
|
ownerActor IiosActorRef @relation(fields: [ownerActorId], references: [id])
|
|
kind IiosInboxItemKind
|
|
state IiosInboxState @default(OPEN)
|
|
title String
|
|
summary String?
|
|
priority String @default("NORMAL")
|
|
dueAt DateTime?
|
|
sourceInteractionId String?
|
|
sourceInteraction IiosInteraction? @relation(fields: [sourceInteractionId], references: [id])
|
|
threadId String?
|
|
thread IiosThread? @relation(fields: [threadId], references: [id])
|
|
traceId String?
|
|
actionRef Json?
|
|
policyDecisionRef String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
metadata Json?
|
|
|
|
stateHistory IiosInboxItemStateHistory[]
|
|
|
|
@@index([ownerActorId, state, priority])
|
|
}
|
|
|
|
/// Audit trail of inbox-item state transitions.
|
|
model IiosInboxItemStateHistory {
|
|
id String @id @default(cuid())
|
|
inboxItemId String
|
|
inboxItem IiosInboxItem @relation(fields: [inboxItemId], references: [id], onDelete: Cascade)
|
|
fromState IiosInboxState?
|
|
toState IiosInboxState
|
|
actorId String?
|
|
reasonCode String?
|
|
at DateTime @default(now())
|
|
|
|
@@index([inboxItemId])
|
|
}
|
|
|
|
// ─── P4: Support (specialization over message + inbox) ────────────
|
|
|
|
/// A support queue; agents (team members) are assigned tickets from it.
|
|
model IiosSupportQueue {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
|
name String
|
|
supportTier String @default("REGULAR")
|
|
slaPolicyRef String? // SLA placeholder (no timer logic until P7)
|
|
assignmentPolicy Json?
|
|
enabled Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
|
|
members IiosSupportTeamMember[]
|
|
tickets IiosTicket[]
|
|
|
|
@@index([scopeId])
|
|
}
|
|
|
|
/// An agent's membership + capacity in a queue. Agent = an ActorRef that is a member.
|
|
model IiosSupportTeamMember {
|
|
queueId String
|
|
queue IiosSupportQueue @relation(fields: [queueId], references: [id], onDelete: Cascade)
|
|
actorId String
|
|
actor IiosActorRef @relation(fields: [actorId], references: [id])
|
|
role String @default("AGENT")
|
|
maxActive Int @default(3)
|
|
activeCount Int @default(0)
|
|
availabilityState String @default("OFFLINE")
|
|
|
|
@@id([queueId, actorId])
|
|
@@index([actorId])
|
|
}
|
|
|
|
/// Support work object. Linked many-to-many to threads; the thread stays generic.
|
|
model IiosTicket {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
|
queueId String?
|
|
queue IiosSupportQueue? @relation(fields: [queueId], references: [id])
|
|
requesterActorId String?
|
|
requesterActor IiosActorRef? @relation("TicketRequester", fields: [requesterActorId], references: [id])
|
|
assignedActorId String?
|
|
assignedActor IiosActorRef? @relation("TicketAssignee", fields: [assignedActorId], references: [id])
|
|
subject String
|
|
state IiosTicketState @default(NEW)
|
|
priority IiosTicketPriority @default(P3)
|
|
issueClass String?
|
|
issueSubclass String?
|
|
slaDueAt DateTime? // SLA placeholder (no timer logic until P7)
|
|
createdFromInteractionId String?
|
|
createdFromInteraction IiosInteraction? @relation(fields: [createdFromInteractionId], references: [id])
|
|
traceId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
metadata Json?
|
|
|
|
threadLinks IiosTicketThreadLink[]
|
|
stateHistory IiosTicketStateHistory[]
|
|
callbacks IiosCallbackRequest[]
|
|
|
|
@@index([scopeId, state])
|
|
@@index([assignedActorId, state])
|
|
}
|
|
|
|
/// M:N link between tickets and kernel threads (one thread ↔ many tickets, and vice-versa).
|
|
model IiosTicketThreadLink {
|
|
ticketId String
|
|
ticket IiosTicket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
|
threadId String
|
|
thread IiosThread @relation(fields: [threadId], references: [id], onDelete: Cascade)
|
|
relationKind String @default("PRIMARY")
|
|
linkedAt DateTime @default(now())
|
|
|
|
@@id([ticketId, threadId])
|
|
@@index([threadId])
|
|
}
|
|
|
|
/// Ticket state-transition audit.
|
|
model IiosTicketStateHistory {
|
|
id String @id @default(cuid())
|
|
ticketId String
|
|
ticket IiosTicket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
|
|
fromState IiosTicketState?
|
|
toState IiosTicketState
|
|
actorId String?
|
|
reasonCode String?
|
|
at DateTime @default(now())
|
|
|
|
@@index([ticketId])
|
|
}
|
|
|
|
/// Callback request placeholder (the Meeting module is P8; meetingRef is a stub).
|
|
model IiosCallbackRequest {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
scope IiosScope @relation(fields: [scopeId], references: [id], onDelete: Cascade)
|
|
ticketId String?
|
|
ticket IiosTicket? @relation(fields: [ticketId], references: [id])
|
|
requesterActorId String
|
|
requesterActor IiosActorRef @relation(fields: [requesterActorId], references: [id])
|
|
preferChannel String
|
|
preferredTime DateTime?
|
|
notes String?
|
|
status IiosCallbackStatus @default(PENDING)
|
|
meetingRef String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([scopeId, status])
|
|
}
|
|
|
|
// ─── P5: Adapters (anti-corruption layer, simulation mode) ────────
|
|
|
|
/// Raw inbound provider payload (claim-check / replay). Verified before storing.
|
|
model IiosInboundRawEvent {
|
|
id String @id @default(cuid())
|
|
channelType String
|
|
externalEventId String?
|
|
signatureStatus String // VERIFIED | INVALID | UNSIGNED
|
|
status String @default("RECEIVED") // RECEIVED | NORMALIZED | REJECTED | FAILED
|
|
headers Json?
|
|
payload Json
|
|
traceId String?
|
|
interactionId String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([channelType, externalEventId])
|
|
@@index([status])
|
|
}
|
|
|
|
/// A signed command to send on an external channel — executed by the sandbox sink.
|
|
model IiosOutboundCommand {
|
|
id String @id @default(cuid())
|
|
channelType String
|
|
target String
|
|
payload Json
|
|
status String @default("PENDING") // PENDING | SENT | FAILED | RATE_LIMITED
|
|
idempotencyKey String @unique
|
|
providerRef String?
|
|
createdAt DateTime @default(now())
|
|
|
|
attempts IiosDeliveryAttempt[]
|
|
|
|
@@index([channelType, status])
|
|
}
|
|
|
|
model IiosDeliveryAttempt {
|
|
id String @id @default(cuid())
|
|
commandId String
|
|
command IiosOutboundCommand @relation(fields: [commandId], references: [id], onDelete: Cascade)
|
|
attemptNo Int
|
|
status String
|
|
providerRef String?
|
|
latencyMs Int?
|
|
errorCode String?
|
|
at DateTime @default(now())
|
|
|
|
@@index([commandId])
|
|
}
|
|
|
|
/// Per-(channelType, key) token/window bucket for outbound throttling.
|
|
model IiosRateLimitBucket {
|
|
channelType String
|
|
bucketKey String
|
|
windowStart DateTime @default(now())
|
|
count Int @default(0)
|
|
resetAt DateTime
|
|
|
|
@@id([channelType, bucketKey])
|
|
}
|
|
|
|
// ─── P6: Routing (preview-first community forwarding) ──────────────
|
|
|
|
/// One origin→destination route rule. Preview-first: SIMULATION_ONLY + disabled
|
|
/// + requiresReview by default. Restricted destinations are deny-by-default.
|
|
model IiosRouteBinding {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
originChannelType String
|
|
originRef String?
|
|
destinationChannelType String
|
|
destinationRef String?
|
|
restrictionProfile String? // e.g. CHILD / SENIORS / ADULT / PUBLIC
|
|
mode IiosRouteMode @default(SIMULATION_ONLY)
|
|
outputFormat IiosOutputFormat @default(FORWARD)
|
|
frequency String @default("IMMEDIATE")
|
|
includeAttachments Boolean @default(false)
|
|
requiresReview Boolean @default(true)
|
|
rulepackVersion String @default("v1")
|
|
enabled Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
decisions IiosRouteDecision[]
|
|
|
|
@@index([scopeId, originChannelType])
|
|
}
|
|
|
|
/// Per-(interaction, binding) routing decision + rendered preview. No send during simulation.
|
|
model IiosRouteDecision {
|
|
id String @id @default(cuid())
|
|
interactionId String
|
|
routeBindingId String
|
|
routeBinding IiosRouteBinding @relation(fields: [routeBindingId], references: [id], onDelete: Cascade)
|
|
decisionState IiosRouteDecisionState
|
|
reasonCodes String[]
|
|
previewPayload Json
|
|
outputFormat IiosOutputFormat
|
|
rulepackVersion String
|
|
simulationId String?
|
|
decidedByActorId String?
|
|
executed Boolean @default(false)
|
|
executedCommandId String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([interactionId, routeBindingId])
|
|
@@index([decisionState])
|
|
}
|
|
|
|
/// A deterministic (RULE) or later AI-proposed flag on an interaction. AI can
|
|
/// propose but never allow a route (P7).
|
|
model IiosModerationFlag {
|
|
id String @id @default(cuid())
|
|
interactionId String
|
|
flagType String
|
|
severity String @default("LOW")
|
|
proposedBy String @default("RULE")
|
|
confidence Float?
|
|
explanation String?
|
|
createdAt DateTime @default(now())
|
|
resolvedAt DateTime?
|
|
|
|
@@index([interactionId])
|
|
}
|
|
|
|
// ─── AI Interaction SDK tables (P7) — proposal layer, never decides ─────────
|
|
|
|
/// A scoped, purposed AI work order (classify / summarize / extract). Carries the
|
|
/// provenance the critics mandate (input_scope_hash, policy_decision_id, purpose,
|
|
/// replay_version, traceId, idempotencyKey). Idempotent per key.
|
|
model IiosAiJob {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
jobType IiosAiJobType
|
|
interactionId String?
|
|
inputWindowRef String
|
|
purpose String
|
|
status IiosAiJobStatus @default(PENDING)
|
|
modelPolicyRef String @default("golden-v1")
|
|
policyDecisionRef String?
|
|
inputScopeHash String
|
|
replayVersion String @default("v1")
|
|
abstentionReason String?
|
|
traceId String?
|
|
idempotencyKey String @unique
|
|
createdAt DateTime @default(now())
|
|
|
|
artifacts IiosAiArtifact[]
|
|
|
|
@@index([scopeId, jobType])
|
|
@@index([interactionId])
|
|
}
|
|
|
|
/// Model/prompt/tool execution metadata + cost. The budget governor (KG-12) sums
|
|
/// costUnits per scope. In P7 provider='fake' (deterministic golden model).
|
|
model IiosAiModelRun {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
provider String @default("fake")
|
|
model String
|
|
modelVersion String
|
|
promptVersion String
|
|
toolPolicyRef String?
|
|
tokensIn Int @default(0)
|
|
tokensOut Int @default(0)
|
|
costUnits Int @default(0)
|
|
latencyMs Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
|
|
artifacts IiosAiArtifact[]
|
|
toolCalls IiosAiToolCall[]
|
|
|
|
@@index([scopeId])
|
|
}
|
|
|
|
/// A durable AI proposal/output. NEVER committed truth by itself — status starts
|
|
/// PROPOSED and only a human accept (or a deterministic gate) advances it.
|
|
model IiosAiArtifact {
|
|
id String @id @default(cuid())
|
|
jobId String
|
|
job IiosAiJob @relation(fields: [jobId], references: [id], onDelete: Cascade)
|
|
modelRunId String
|
|
modelRun IiosAiModelRun @relation(fields: [modelRunId], references: [id], onDelete: Cascade)
|
|
artifactType IiosAiArtifactType
|
|
status IiosAiArtifactStatus @default(PROPOSED)
|
|
confidence Float?
|
|
contentRef Json
|
|
explanationRef String?
|
|
abstentionReason String?
|
|
acceptedByActorId String?
|
|
createdAt DateTime @default(now())
|
|
|
|
claims IiosAiClaim[]
|
|
evidence IiosAiEvidenceLink[]
|
|
|
|
@@index([jobId])
|
|
@@index([status])
|
|
}
|
|
|
|
/// A structured extracted claim (event date, intent, priority, FAQ, moderation
|
|
/// flag). validationStatus stays UNVALIDATED until a human/deterministic accept.
|
|
model IiosAiClaim {
|
|
id String @id @default(cuid())
|
|
artifactId String
|
|
artifact IiosAiArtifact @relation(fields: [artifactId], references: [id], onDelete: Cascade)
|
|
claimType IiosAiClaimType
|
|
claimJson Json
|
|
confidence Float?
|
|
validationStatus IiosAiClaimValidation @default(UNVALIDATED)
|
|
acceptedByRef String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([artifactId])
|
|
}
|
|
|
|
/// Source citation for an AI claim/artifact (message / segment / KB chunk).
|
|
model IiosAiEvidenceLink {
|
|
id String @id @default(cuid())
|
|
artifactId String
|
|
artifact IiosAiArtifact @relation(fields: [artifactId], references: [id], onDelete: Cascade)
|
|
sourceType String
|
|
sourceId String
|
|
spanRef String?
|
|
relevanceScore Float?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([artifactId])
|
|
}
|
|
|
|
/// Tool calls made by an agent under MCP/tool policy. Schema now; exercised when a
|
|
/// real provider + tools land in P9.
|
|
model IiosAiToolCall {
|
|
id String @id @default(cuid())
|
|
modelRunId String
|
|
modelRun IiosAiModelRun @relation(fields: [modelRunId], references: [id], onDelete: Cascade)
|
|
toolName String
|
|
toolInputHash String
|
|
toolOutputRef String?
|
|
policyRefId String?
|
|
status String @default("PENDING")
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([modelRunId])
|
|
}
|
|
|
|
/// Reference to a vector embedding record/index; does not expose text. Schema now;
|
|
/// exercised when RAG lands in P9.
|
|
model IiosEmbeddingRef {
|
|
id String @id @default(cuid())
|
|
sourceType String
|
|
sourceId String
|
|
embeddingModel String
|
|
vectorStoreRef String
|
|
embeddingHash String
|
|
version String
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([sourceType, sourceId, version])
|
|
}
|
|
|
|
// ─── Calendar / Meeting SDK tables (P8) — interaction specialization ────────
|
|
|
|
/// A request to schedule/reschedule/cancel a meeting. Genesis: a P4 callback
|
|
/// (callbackRequestId), an accepted P7 AI EVENT claim (sourceClaimId), or direct.
|
|
model IiosMeetingRequest {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
interactionId String?
|
|
requestedByActorId String
|
|
targetActorId String?
|
|
requestedWindowJson Json
|
|
meetingType IiosMeetingType @default(CALLBACK)
|
|
status IiosMeetingRequestStatus @default(PENDING)
|
|
resultingMeetingId String?
|
|
callbackRequestId String?
|
|
sourceClaimId String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([scopeId, status])
|
|
}
|
|
|
|
/// A meeting: Zoom / phone / in-person / callback / internal. Owns calendar
|
|
/// semantics; a message is only a related artifact.
|
|
model IiosMeeting {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
interactionId String?
|
|
calendarEventId String?
|
|
meetingType IiosMeetingType
|
|
providerRef String?
|
|
joinUrlRef String?
|
|
status IiosMeetingStatus @default(REQUESTED)
|
|
organizerActorId String
|
|
threadId String?
|
|
title String
|
|
startAt DateTime?
|
|
endAt DateTime?
|
|
timezone String @default("UTC")
|
|
createdAt DateTime @default(now())
|
|
|
|
participants IiosMeetingParticipant[]
|
|
transcripts IiosMeetingTranscript[]
|
|
summaries IiosMeetingSummary[]
|
|
actionItems IiosMeetingActionItem[]
|
|
|
|
@@index([scopeId, status])
|
|
}
|
|
|
|
/// Attendee with role, attendance, per-attendee recording consent + visibility.
|
|
/// The consent + visibility model is the P8 safety spine (KG-14 / Scenario 6).
|
|
model IiosMeetingParticipant {
|
|
id String @id @default(cuid())
|
|
meetingId String
|
|
meeting IiosMeeting @relation(fields: [meetingId], references: [id], onDelete: Cascade)
|
|
actorRefId String?
|
|
sourceHandleId String?
|
|
role IiosParticipantRole @default(REQUIRED)
|
|
attendanceStatus IiosAttendanceStatus @default(INVITED)
|
|
recordingConsent IiosConsentStatus @default(UNKNOWN)
|
|
visibility IiosParticipantVisibility @default(FULL)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([meetingId, actorRefId])
|
|
@@index([meetingId])
|
|
}
|
|
|
|
/// Transcript artifact metadata. Created only after the consent gate passes;
|
|
/// otherwise status=BLOCKED with no segments.
|
|
model IiosMeetingTranscript {
|
|
id String @id @default(cuid())
|
|
meetingId String
|
|
meeting IiosMeeting @relation(fields: [meetingId], references: [id], onDelete: Cascade)
|
|
sourceType String @default("SIMULATED")
|
|
transcriptRef String
|
|
consentRefId String?
|
|
status IiosTranscriptStatus @default(PENDING)
|
|
language String @default("en")
|
|
createdAt DateTime @default(now())
|
|
|
|
segments IiosTranscriptSegment[]
|
|
|
|
@@index([meetingId])
|
|
}
|
|
|
|
/// Speaker-attributed transcript segment.
|
|
model IiosTranscriptSegment {
|
|
id String @id @default(cuid())
|
|
transcriptId String
|
|
transcript IiosMeetingTranscript @relation(fields: [transcriptId], references: [id], onDelete: Cascade)
|
|
segmentNo Int
|
|
speakerActorId String?
|
|
startMs Int
|
|
endMs Int
|
|
contentRef String
|
|
confidence Float?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([transcriptId])
|
|
}
|
|
|
|
/// AI or human meeting summary; links to a P7 ai_artifact.
|
|
model IiosMeetingSummary {
|
|
id String @id @default(cuid())
|
|
meetingId String
|
|
meeting IiosMeeting @relation(fields: [meetingId], references: [id], onDelete: Cascade)
|
|
aiArtifactId String?
|
|
summaryType String @default("AI")
|
|
status IiosSummaryStatus @default(PROPOSED)
|
|
approvedByActorId String?
|
|
publishedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([meetingId])
|
|
}
|
|
|
|
/// Generic task/follow-up (Inbox-owned source of truth), derived from meetings,
|
|
/// support, messages or CRM workflows.
|
|
model IiosActionItem {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
sourceInteractionId String?
|
|
sourceMessageId String?
|
|
ownerActorId String?
|
|
ownerTeamId String?
|
|
title String
|
|
status IiosActionItemStatus @default(OPEN)
|
|
dueAt DateTime?
|
|
completedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
|
|
meetingLinks IiosMeetingActionItem[]
|
|
|
|
@@index([scopeId, status])
|
|
}
|
|
|
|
/// Meeting-derived action item linked to a generic action_item.
|
|
model IiosMeetingActionItem {
|
|
id String @id @default(cuid())
|
|
meetingId String
|
|
meeting IiosMeeting @relation(fields: [meetingId], references: [id], onDelete: Cascade)
|
|
actionItemId String
|
|
actionItem IiosActionItem @relation(fields: [actionItemId], references: [id], onDelete: Cascade)
|
|
ownerActorId String?
|
|
sourceSegmentId String?
|
|
status IiosActionItemStatus @default(OPEN)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([meetingId, actionItemId])
|
|
@@index([meetingId])
|
|
}
|
|
|
|
/// Connected calendar/provider account or service mailbox; stores refs, not secrets.
|
|
model IiosCalendarProviderAccount {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
actorRefId String?
|
|
providerType IiosCalendarProviderType @default(SIMULATED)
|
|
accountHandleToken String
|
|
secretRef String?
|
|
syncStatus IiosCalendarSyncStatus @default(IDLE)
|
|
createdAt DateTime @default(now())
|
|
|
|
events IiosCalendarEvent[]
|
|
cursors IiosCalendarSyncCursor[]
|
|
|
|
@@index([scopeId])
|
|
}
|
|
|
|
/// Imported or created calendar event with policy-scoped metadata.
|
|
model IiosCalendarEvent {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
providerAccountId String?
|
|
providerAccount IiosCalendarProviderAccount? @relation(fields: [providerAccountId], references: [id], onDelete: Cascade)
|
|
externalEventId String?
|
|
title String
|
|
startAt DateTime
|
|
endAt DateTime
|
|
status IiosCalendarEventStatus @default(CONFIRMED)
|
|
visibilityClass String @default("internal")
|
|
interactionId String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([providerAccountId, externalEventId])
|
|
@@index([scopeId])
|
|
}
|
|
|
|
/// Availability / scheduling constraints for an actor or team.
|
|
model IiosAvailabilityWindow {
|
|
id String @id @default(cuid())
|
|
scopeId String
|
|
actorRefId String?
|
|
teamProjectionId String?
|
|
startAt DateTime
|
|
endAt DateTime
|
|
source String @default("USER")
|
|
recurrenceJson Json?
|
|
status String @default("ACTIVE")
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([scopeId, actorRefId])
|
|
}
|
|
|
|
/// Incremental sync cursor / replay position for a provider calendar.
|
|
model IiosCalendarSyncCursor {
|
|
id String @id @default(cuid())
|
|
providerAccountId String
|
|
providerAccount IiosCalendarProviderAccount @relation(fields: [providerAccountId], references: [id], onDelete: Cascade)
|
|
externalCursor String?
|
|
lastSyncAt DateTime?
|
|
status IiosCalendarSyncStatus @default(IDLE)
|
|
errorCode String?
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([providerAccountId])
|
|
}
|