4runr-os 2.10.9 → 2.10.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,471 +1,471 @@
1
- // This is your Prisma schema file,
2
- // learn more about it in the docs: https://pris.ly/d/prisma-schema
3
-
4
- generator client {
5
- provider = "prisma-client-js"
6
- binaryTargets = ["native", "linux-musl-openssl-3.0.x", "linux-musl"]
7
- // Path is relative to this schema file; ensures client lands in apps/gateway/node_modules (4Runr TUI + npm global)
8
- output = "../apps/gateway/node_modules/.prisma/client"
9
- }
10
-
11
- datasource db {
12
- provider = "postgresql"
13
- url = env("DATABASE_URL")
14
- }
15
-
16
- model Agent {
17
- id String @id @default(uuid())
18
- name String
19
- role String
20
- createdBy String
21
- publicKey String
22
- status String @default("active")
23
- createdAt DateTime @default(now())
24
- tokens Token[]
25
- requestLogs RequestLog[]
26
- policies Policy[]
27
- policyLogs PolicyLog[]
28
- tokenRegistries TokenRegistry[]
29
- }
30
-
31
- model Token {
32
- id String @id @default(uuid())
33
- agentId String
34
- agent Agent @relation(fields: [agentId], references: [id])
35
- encrypted String
36
- expiresAt DateTime
37
- revoked Boolean @default(false)
38
- createdAt DateTime @default(now())
39
- }
40
-
41
- model RequestLog {
42
- id String @id @default(uuid())
43
- agentId String
44
- agent Agent @relation(fields: [agentId], references: [id])
45
- tool String
46
- action String
47
- responseTime Int // milliseconds
48
- statusCode Int
49
- success Boolean
50
- createdAt DateTime @default(now())
51
- }
52
-
53
- model Policy {
54
- id String @id @default(uuid())
55
- name String
56
- description String?
57
- agentId String? // null for role-based policies
58
- role String? // null for agent-specific policies
59
- spec String // JSON policy specification
60
- specHash String // SHA256 hash of spec for change detection
61
- active Boolean @default(true)
62
- createdAt DateTime @default(now())
63
- updatedAt DateTime @updatedAt
64
- agent Agent? @relation(fields: [agentId], references: [id])
65
- policyLogs PolicyLog[]
66
- quotaCounters QuotaCounter[]
67
- }
68
-
69
- model PolicyLog {
70
- id String @id @default(uuid())
71
- policyId String
72
- agentId String
73
- tool String
74
- action String
75
- decision String // "allow", "deny"
76
- reason String? // denial reason
77
- requestData String? // JSON request data (truncated)
78
- responseData String? // JSON response data (truncated)
79
- createdAt DateTime @default(now())
80
- policy Policy @relation(fields: [policyId], references: [id])
81
- agent Agent @relation(fields: [agentId], references: [id])
82
- }
83
-
84
- model QuotaCounter {
85
- id String @id @default(uuid())
86
- policyId String
87
- quotaKey String // e.g., "serpapi:search:2024-01-15"
88
- current Int @default(0)
89
- resetAt DateTime
90
- createdAt DateTime @default(now())
91
- updatedAt DateTime @updatedAt
92
- policy Policy @relation(fields: [policyId], references: [id])
93
- }
94
-
95
- model ToolCredential {
96
- id String @id @default(uuid())
97
- tool String // e.g., "serpapi", "openai", "gmail_send"
98
- version String // e.g., "v1", "v2"
99
- isActive Boolean @default(false)
100
- encryptedCredential String // Encrypted credential data
101
- metadata String? // JSON metadata (encrypted)
102
- createdAt DateTime @default(now())
103
- updatedAt DateTime @updatedAt
104
- activatedAt DateTime?
105
- deactivatedAt DateTime?
106
-
107
- @@unique([tool, version])
108
- }
109
-
110
- model TokenRegistry {
111
- id String @id @default(uuid())
112
- tokenId String @unique // The token ID issued to the agent
113
- agentId String
114
- payloadHash String // SHA256 hash of the token payload
115
- issuedAt DateTime @default(now())
116
- expiresAt DateTime
117
- isRevoked Boolean @default(false)
118
- revokedAt DateTime?
119
- agent Agent @relation(fields: [agentId], references: [id])
120
- }
121
-
122
- // ====== AI AGENT RUNTIME MODELS (TASK-001) ======
123
-
124
- model RuntimeAgent {
125
- id String @id @default(uuid())
126
- name String
127
- language AgentLanguage
128
- sourceType SourceType @default(ZIP) // ZIP | GIT (ZIP for MVP)
129
- sourceUri String? // path to extracted zip in local FS (MVP)
130
- entrypoint String // index.js | main.py
131
- env Json // non-secret env vars (secrets via Gateway only)
132
- limitsCpu Float? // e.g. 0.5
133
- limitsMemMb Int? // e.g. 256
134
- networkMode NetworkMode @default(NONE) // NONE | EGRESS (via Gateway proxy only)
135
- status AgentStatus @default(READY)
136
- // NEW: restart policy & limits
137
- maxRestarts Int @default(2) // per run
138
- restartBackoffMs Int @default(5000)
139
- // NEW: tags for demo functionality (JSON array for SQLite compatibility)
140
- tags Json @default("[]")
141
- createdAt DateTime @default(now())
142
- updatedAt DateTime @updatedAt
143
-
144
- runs RuntimeRun[]
145
- schedules RuntimeSchedule[]
146
-
147
- @@map("runtime_agents")
148
- }
149
-
150
- model RuntimeRun {
151
- id String @id @default(uuid())
152
- agentId String
153
- agent RuntimeAgent @relation(fields: [agentId], references: [id], onDelete: Cascade)
154
- status RunStatus @default(QUEUED) // QUEUED | RUNNING | SUCCEEDED | FAILED | KILLED
155
- startedAt DateTime?
156
- endedAt DateTime?
157
- exitCode Int?
158
- reason String? // containerId or failure reason
159
- // NEW: restart policy & limits
160
- cpuSeconds Float? // cumulative seconds
161
- maxMemMb Int? // peak RSS MB
162
- restarts Int @default(0)
163
- lastSampleAt DateTime? // last stats collection time
164
- // NEW: trigger tracking
165
- triggeredBy TriggerType @default(MANUAL)
166
- // (optional) logsPtr for future
167
- createdAt DateTime @default(now())
168
-
169
- @@map("runtime_runs")
170
- }
171
-
172
- model RuntimeSchedule {
173
- id String @id @default(uuid())
174
- agentId String
175
- agent RuntimeAgent @relation(fields: [agentId], references: [id], onDelete: Cascade)
176
- cronExpr String
177
- enabled Boolean @default(true)
178
- lastRunAt DateTime?
179
- nextRunAt DateTime?
180
- createdAt DateTime @default(now())
181
-
182
- @@map("runtime_schedules")
183
- }
184
-
185
- enum AgentLanguage {
186
- NODE
187
- PYTHON
188
- }
189
-
190
- enum SourceType {
191
- ZIP
192
- GIT
193
- }
194
-
195
- enum NetworkMode {
196
- NONE
197
- EGRESS
198
- }
199
-
200
- enum AgentStatus {
201
- READY
202
- ERROR
203
- DISABLED
204
- }
205
-
206
- enum RunStatus {
207
- CREATED
208
- QUEUED
209
- RUNNING
210
- COMPLETED
211
- FAILED
212
- KILLED
213
- SUCCEEDED // Alias for COMPLETED (keeping for RuntimeRun compatibility)
214
- }
215
-
216
- enum TriggerType {
217
- MANUAL
218
- SCHEDULE
219
- }
220
-
221
- // ====== GATEWAY RUNS MODELS ======
222
-
223
- model Run {
224
- id String @id @default(uuid())
225
- name String
226
- status RunStatus @default(CREATED)
227
- input Json? // JSON input payload
228
- output Json? // JSON output payload
229
- logs Json @default("[]") // Array of log entries
230
- clientToken String? @unique // Idempotency token from client
231
- tags String[] @default([])
232
- startedAt DateTime?
233
- completedAt DateTime?
234
- createdAt DateTime @default(now())
235
- updatedAt DateTime @updatedAt
236
-
237
- @@map("runs")
238
- @@index([status])
239
- @@index([clientToken])
240
- @@index([createdAt])
241
- }
242
-
243
- model ApiKey {
244
- id String @id @default(cuid())
245
- key String @unique // Hashed or opaque token
246
- label String?
247
- revoked Boolean @default(false)
248
- userId String? // Link to user if applicable
249
- user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
250
- createdAt DateTime @default(now())
251
- revokedAt DateTime?
252
-
253
- @@map("api_keys")
254
- @@index([key])
255
- @@index([userId])
256
- }
257
-
258
- // ====== RBAC MODELS ======
259
-
260
- model User {
261
- id String @id @default(uuid())
262
- email String @unique
263
- username String @unique
264
- passwordHash String? // Optional - can use external auth
265
- status UserStatus @default(ACTIVE)
266
- createdAt DateTime @default(now())
267
- updatedAt DateTime @updatedAt
268
- lastLoginAt DateTime?
269
-
270
- // MFA fields
271
- mfaEnabled Boolean @default(false)
272
- mfaSecret String? // TOTP secret (encrypted)
273
- mfaBackupCodes String[] @default([]) // Encrypted backup codes
274
- mfaVerifiedAt DateTime? // When MFA was last verified
275
-
276
- // Relations
277
- roles UserRole[]
278
- apiKeys ApiKey[]
279
- sessions UserSession[]
280
-
281
- @@map("users")
282
- @@index([email])
283
- @@index([username])
284
- @@index([status])
285
- }
286
-
287
- model Role {
288
- id String @id @default(uuid())
289
- name String @unique
290
- description String?
291
- isSystem Boolean @default(false) // System roles cannot be deleted
292
- createdAt DateTime @default(now())
293
- updatedAt DateTime @updatedAt
294
-
295
- // Relations
296
- users UserRole[]
297
- permissions RolePermission[]
298
-
299
- @@map("roles")
300
- @@index([name])
301
- }
302
-
303
- model Permission {
304
- id String @id @default(uuid())
305
- resource String // e.g., "runs", "agents", "users"
306
- action String // e.g., "create", "read", "update", "delete", "execute"
307
- description String?
308
- createdAt DateTime @default(now())
309
-
310
- // Relations
311
- roles RolePermission[]
312
-
313
- @@unique([resource, action])
314
- @@map("permissions")
315
- @@index([resource])
316
- @@index([action])
317
- }
318
-
319
- model UserRole {
320
- id String @id @default(uuid())
321
- userId String
322
- roleId String
323
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
324
- role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
325
- assignedAt DateTime @default(now())
326
- assignedBy String? // User ID who assigned this role
327
-
328
- @@unique([userId, roleId])
329
- @@map("user_roles")
330
- @@index([userId])
331
- @@index([roleId])
332
- }
333
-
334
- model RolePermission {
335
- id String @id @default(uuid())
336
- roleId String
337
- permissionId String
338
- role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
339
- permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
340
- grantedAt DateTime @default(now())
341
- grantedBy String? // User ID who granted this permission
342
-
343
- @@unique([roleId, permissionId])
344
- @@map("role_permissions")
345
- @@index([roleId])
346
- @@index([permissionId])
347
- }
348
-
349
- model UserSession {
350
- id String @id @default(uuid())
351
- userId String
352
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
353
- token String @unique // JWT or session token
354
- ipAddress String?
355
- userAgent String?
356
- expiresAt DateTime
357
- createdAt DateTime @default(now())
358
- lastActivityAt DateTime @default(now())
359
-
360
- @@map("user_sessions")
361
- @@index([userId])
362
- @@index([token])
363
- @@index([expiresAt])
364
- }
365
-
366
- enum UserStatus {
367
- ACTIVE
368
- INACTIVE
369
- SUSPENDED
370
- DELETED
371
- }
372
-
373
- // ====== IDEMPOTENCY & CACHE MODELS ======
374
-
375
- model IdempotencyToken {
376
- id String @id @default(uuid())
377
- token String @unique
378
- runId String? // Reference to Run if applicable
379
- response Json? // Cached response
380
- expiresAt DateTime
381
- createdAt DateTime @default(now())
382
-
383
- @@map("idempotency_tokens")
384
- @@index([token])
385
- @@index([expiresAt])
386
- }
387
-
388
- model CacheEntry {
389
- id String @id @default(uuid())
390
- key String @unique
391
- value Json
392
- expiresAt DateTime?
393
- createdAt DateTime @default(now())
394
- updatedAt DateTime @updatedAt
395
-
396
- @@map("cache_entries")
397
- @@index([key])
398
- @@index([expiresAt])
399
- }
400
-
401
- // ====== CHAT HISTORY MODELS ======
402
-
403
- model ChatHistory {
404
- id String @id @default(uuid())
405
- clientId String // Client identifier (e.g., machine ID, user ID)
406
- agentName String
407
- agentDescription String?
408
- messages Json // Array of { role: 'user' | 'assistant', content: string }
409
- createdAt DateTime @default(now())
410
- lastMessageAt DateTime @default(now())
411
- updatedAt DateTime @updatedAt
412
-
413
- @@map("chat_history")
414
- @@index([clientId])
415
- @@index([clientId, lastMessageAt])
416
- }
417
-
418
- // ====== DEVKIT AGENT & TOOL MANAGEMENT ======
419
-
420
- model DevKitAgent {
421
- id String @id @default(uuid())
422
- name String
423
- slug String @unique
424
- description String?
425
- type String // lead_agent, education_agent, custom, etc.
426
- model String // gpt-4, claude-3, etc.
427
- config Json @default("{}") // { prompt, temperature, maxTokens, etc. }
428
- createdAt DateTime @default(now())
429
- updatedAt DateTime @updatedAt
430
-
431
- // Relations
432
- tools DevKitAgentTool[]
433
-
434
- @@map("devkit_agents")
435
- @@index([slug])
436
- @@index([type])
437
- }
438
-
439
- model DevKitTool {
440
- id String @id @default(uuid())
441
- name String
442
- kind String // http, email, vector, database, etc.
443
- config Json @default("{}") // Flexible config per kind
444
- enabled Boolean @default(true)
445
- createdAt DateTime @default(now())
446
- updatedAt DateTime @updatedAt
447
-
448
- // Relations
449
- agents DevKitAgentTool[]
450
-
451
- @@map("devkit_tools")
452
- @@index([kind])
453
- @@index([enabled])
454
- }
455
-
456
- model DevKitAgentTool {
457
- id String @id @default(uuid())
458
- agentId String
459
- toolId String
460
- configOverride Json? // Optional per-agent tool configuration
461
- createdAt DateTime @default(now())
462
-
463
- // Relations
464
- agent DevKitAgent @relation(fields: [agentId], references: [id], onDelete: Cascade)
465
- tool DevKitTool @relation(fields: [toolId], references: [id], onDelete: Cascade)
466
-
467
- @@unique([agentId, toolId])
468
- @@map("devkit_agent_tools")
469
- @@index([agentId])
470
- @@index([toolId])
1
+ // This is your Prisma schema file,
2
+ // learn more about it in the docs: https://pris.ly/d/prisma-schema
3
+
4
+ generator client {
5
+ provider = "prisma-client-js"
6
+ binaryTargets = ["native", "linux-musl-openssl-3.0.x", "linux-musl"]
7
+ // Path is relative to this schema file; ensures client lands in apps/gateway/node_modules (4Runr TUI + npm global)
8
+ output = "../apps/gateway/node_modules/.prisma/client"
9
+ }
10
+
11
+ datasource db {
12
+ provider = "postgresql"
13
+ url = env("DATABASE_URL")
14
+ }
15
+
16
+ model Agent {
17
+ id String @id @default(uuid())
18
+ name String
19
+ role String
20
+ createdBy String
21
+ publicKey String
22
+ status String @default("active")
23
+ createdAt DateTime @default(now())
24
+ tokens Token[]
25
+ requestLogs RequestLog[]
26
+ policies Policy[]
27
+ policyLogs PolicyLog[]
28
+ tokenRegistries TokenRegistry[]
29
+ }
30
+
31
+ model Token {
32
+ id String @id @default(uuid())
33
+ agentId String
34
+ agent Agent @relation(fields: [agentId], references: [id])
35
+ encrypted String
36
+ expiresAt DateTime
37
+ revoked Boolean @default(false)
38
+ createdAt DateTime @default(now())
39
+ }
40
+
41
+ model RequestLog {
42
+ id String @id @default(uuid())
43
+ agentId String
44
+ agent Agent @relation(fields: [agentId], references: [id])
45
+ tool String
46
+ action String
47
+ responseTime Int // milliseconds
48
+ statusCode Int
49
+ success Boolean
50
+ createdAt DateTime @default(now())
51
+ }
52
+
53
+ model Policy {
54
+ id String @id @default(uuid())
55
+ name String
56
+ description String?
57
+ agentId String? // null for role-based policies
58
+ role String? // null for agent-specific policies
59
+ spec String // JSON policy specification
60
+ specHash String // SHA256 hash of spec for change detection
61
+ active Boolean @default(true)
62
+ createdAt DateTime @default(now())
63
+ updatedAt DateTime @updatedAt
64
+ agent Agent? @relation(fields: [agentId], references: [id])
65
+ policyLogs PolicyLog[]
66
+ quotaCounters QuotaCounter[]
67
+ }
68
+
69
+ model PolicyLog {
70
+ id String @id @default(uuid())
71
+ policyId String
72
+ agentId String
73
+ tool String
74
+ action String
75
+ decision String // "allow", "deny"
76
+ reason String? // denial reason
77
+ requestData String? // JSON request data (truncated)
78
+ responseData String? // JSON response data (truncated)
79
+ createdAt DateTime @default(now())
80
+ policy Policy @relation(fields: [policyId], references: [id])
81
+ agent Agent @relation(fields: [agentId], references: [id])
82
+ }
83
+
84
+ model QuotaCounter {
85
+ id String @id @default(uuid())
86
+ policyId String
87
+ quotaKey String // e.g., "serpapi:search:2024-01-15"
88
+ current Int @default(0)
89
+ resetAt DateTime
90
+ createdAt DateTime @default(now())
91
+ updatedAt DateTime @updatedAt
92
+ policy Policy @relation(fields: [policyId], references: [id])
93
+ }
94
+
95
+ model ToolCredential {
96
+ id String @id @default(uuid())
97
+ tool String // e.g., "serpapi", "openai", "gmail_send"
98
+ version String // e.g., "v1", "v2"
99
+ isActive Boolean @default(false)
100
+ encryptedCredential String // Encrypted credential data
101
+ metadata String? // JSON metadata (encrypted)
102
+ createdAt DateTime @default(now())
103
+ updatedAt DateTime @updatedAt
104
+ activatedAt DateTime?
105
+ deactivatedAt DateTime?
106
+
107
+ @@unique([tool, version])
108
+ }
109
+
110
+ model TokenRegistry {
111
+ id String @id @default(uuid())
112
+ tokenId String @unique // The token ID issued to the agent
113
+ agentId String
114
+ payloadHash String // SHA256 hash of the token payload
115
+ issuedAt DateTime @default(now())
116
+ expiresAt DateTime
117
+ isRevoked Boolean @default(false)
118
+ revokedAt DateTime?
119
+ agent Agent @relation(fields: [agentId], references: [id])
120
+ }
121
+
122
+ // ====== AI AGENT RUNTIME MODELS (TASK-001) ======
123
+
124
+ model RuntimeAgent {
125
+ id String @id @default(uuid())
126
+ name String
127
+ language AgentLanguage
128
+ sourceType SourceType @default(ZIP) // ZIP | GIT (ZIP for MVP)
129
+ sourceUri String? // path to extracted zip in local FS (MVP)
130
+ entrypoint String // index.js | main.py
131
+ env Json // non-secret env vars (secrets via Gateway only)
132
+ limitsCpu Float? // e.g. 0.5
133
+ limitsMemMb Int? // e.g. 256
134
+ networkMode NetworkMode @default(NONE) // NONE | EGRESS (via Gateway proxy only)
135
+ status AgentStatus @default(READY)
136
+ // NEW: restart policy & limits
137
+ maxRestarts Int @default(2) // per run
138
+ restartBackoffMs Int @default(5000)
139
+ // NEW: tags for demo functionality (JSON array for SQLite compatibility)
140
+ tags Json @default("[]")
141
+ createdAt DateTime @default(now())
142
+ updatedAt DateTime @updatedAt
143
+
144
+ runs RuntimeRun[]
145
+ schedules RuntimeSchedule[]
146
+
147
+ @@map("runtime_agents")
148
+ }
149
+
150
+ model RuntimeRun {
151
+ id String @id @default(uuid())
152
+ agentId String
153
+ agent RuntimeAgent @relation(fields: [agentId], references: [id], onDelete: Cascade)
154
+ status RunStatus @default(QUEUED) // QUEUED | RUNNING | SUCCEEDED | FAILED | KILLED
155
+ startedAt DateTime?
156
+ endedAt DateTime?
157
+ exitCode Int?
158
+ reason String? // containerId or failure reason
159
+ // NEW: restart policy & limits
160
+ cpuSeconds Float? // cumulative seconds
161
+ maxMemMb Int? // peak RSS MB
162
+ restarts Int @default(0)
163
+ lastSampleAt DateTime? // last stats collection time
164
+ // NEW: trigger tracking
165
+ triggeredBy TriggerType @default(MANUAL)
166
+ // (optional) logsPtr for future
167
+ createdAt DateTime @default(now())
168
+
169
+ @@map("runtime_runs")
170
+ }
171
+
172
+ model RuntimeSchedule {
173
+ id String @id @default(uuid())
174
+ agentId String
175
+ agent RuntimeAgent @relation(fields: [agentId], references: [id], onDelete: Cascade)
176
+ cronExpr String
177
+ enabled Boolean @default(true)
178
+ lastRunAt DateTime?
179
+ nextRunAt DateTime?
180
+ createdAt DateTime @default(now())
181
+
182
+ @@map("runtime_schedules")
183
+ }
184
+
185
+ enum AgentLanguage {
186
+ NODE
187
+ PYTHON
188
+ }
189
+
190
+ enum SourceType {
191
+ ZIP
192
+ GIT
193
+ }
194
+
195
+ enum NetworkMode {
196
+ NONE
197
+ EGRESS
198
+ }
199
+
200
+ enum AgentStatus {
201
+ READY
202
+ ERROR
203
+ DISABLED
204
+ }
205
+
206
+ enum RunStatus {
207
+ CREATED
208
+ QUEUED
209
+ RUNNING
210
+ COMPLETED
211
+ FAILED
212
+ KILLED
213
+ SUCCEEDED // Alias for COMPLETED (keeping for RuntimeRun compatibility)
214
+ }
215
+
216
+ enum TriggerType {
217
+ MANUAL
218
+ SCHEDULE
219
+ }
220
+
221
+ // ====== GATEWAY RUNS MODELS ======
222
+
223
+ model Run {
224
+ id String @id @default(uuid())
225
+ name String
226
+ status RunStatus @default(CREATED)
227
+ input Json? // JSON input payload
228
+ output Json? // JSON output payload
229
+ logs Json @default("[]") // Array of log entries
230
+ clientToken String? @unique // Idempotency token from client
231
+ tags String[] @default([])
232
+ startedAt DateTime?
233
+ completedAt DateTime?
234
+ createdAt DateTime @default(now())
235
+ updatedAt DateTime @updatedAt
236
+
237
+ @@map("runs")
238
+ @@index([status])
239
+ @@index([clientToken])
240
+ @@index([createdAt])
241
+ }
242
+
243
+ model ApiKey {
244
+ id String @id @default(cuid())
245
+ key String @unique // Hashed or opaque token
246
+ label String?
247
+ revoked Boolean @default(false)
248
+ userId String? // Link to user if applicable
249
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
250
+ createdAt DateTime @default(now())
251
+ revokedAt DateTime?
252
+
253
+ @@map("api_keys")
254
+ @@index([key])
255
+ @@index([userId])
256
+ }
257
+
258
+ // ====== RBAC MODELS ======
259
+
260
+ model User {
261
+ id String @id @default(uuid())
262
+ email String @unique
263
+ username String @unique
264
+ passwordHash String? // Optional - can use external auth
265
+ status UserStatus @default(ACTIVE)
266
+ createdAt DateTime @default(now())
267
+ updatedAt DateTime @updatedAt
268
+ lastLoginAt DateTime?
269
+
270
+ // MFA fields
271
+ mfaEnabled Boolean @default(false)
272
+ mfaSecret String? // TOTP secret (encrypted)
273
+ mfaBackupCodes String[] @default([]) // Encrypted backup codes
274
+ mfaVerifiedAt DateTime? // When MFA was last verified
275
+
276
+ // Relations
277
+ roles UserRole[]
278
+ apiKeys ApiKey[]
279
+ sessions UserSession[]
280
+
281
+ @@map("users")
282
+ @@index([email])
283
+ @@index([username])
284
+ @@index([status])
285
+ }
286
+
287
+ model Role {
288
+ id String @id @default(uuid())
289
+ name String @unique
290
+ description String?
291
+ isSystem Boolean @default(false) // System roles cannot be deleted
292
+ createdAt DateTime @default(now())
293
+ updatedAt DateTime @updatedAt
294
+
295
+ // Relations
296
+ users UserRole[]
297
+ permissions RolePermission[]
298
+
299
+ @@map("roles")
300
+ @@index([name])
301
+ }
302
+
303
+ model Permission {
304
+ id String @id @default(uuid())
305
+ resource String // e.g., "runs", "agents", "users"
306
+ action String // e.g., "create", "read", "update", "delete", "execute"
307
+ description String?
308
+ createdAt DateTime @default(now())
309
+
310
+ // Relations
311
+ roles RolePermission[]
312
+
313
+ @@unique([resource, action])
314
+ @@map("permissions")
315
+ @@index([resource])
316
+ @@index([action])
317
+ }
318
+
319
+ model UserRole {
320
+ id String @id @default(uuid())
321
+ userId String
322
+ roleId String
323
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
324
+ role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
325
+ assignedAt DateTime @default(now())
326
+ assignedBy String? // User ID who assigned this role
327
+
328
+ @@unique([userId, roleId])
329
+ @@map("user_roles")
330
+ @@index([userId])
331
+ @@index([roleId])
332
+ }
333
+
334
+ model RolePermission {
335
+ id String @id @default(uuid())
336
+ roleId String
337
+ permissionId String
338
+ role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
339
+ permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
340
+ grantedAt DateTime @default(now())
341
+ grantedBy String? // User ID who granted this permission
342
+
343
+ @@unique([roleId, permissionId])
344
+ @@map("role_permissions")
345
+ @@index([roleId])
346
+ @@index([permissionId])
347
+ }
348
+
349
+ model UserSession {
350
+ id String @id @default(uuid())
351
+ userId String
352
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
353
+ token String @unique // JWT or session token
354
+ ipAddress String?
355
+ userAgent String?
356
+ expiresAt DateTime
357
+ createdAt DateTime @default(now())
358
+ lastActivityAt DateTime @default(now())
359
+
360
+ @@map("user_sessions")
361
+ @@index([userId])
362
+ @@index([token])
363
+ @@index([expiresAt])
364
+ }
365
+
366
+ enum UserStatus {
367
+ ACTIVE
368
+ INACTIVE
369
+ SUSPENDED
370
+ DELETED
371
+ }
372
+
373
+ // ====== IDEMPOTENCY & CACHE MODELS ======
374
+
375
+ model IdempotencyToken {
376
+ id String @id @default(uuid())
377
+ token String @unique
378
+ runId String? // Reference to Run if applicable
379
+ response Json? // Cached response
380
+ expiresAt DateTime
381
+ createdAt DateTime @default(now())
382
+
383
+ @@map("idempotency_tokens")
384
+ @@index([token])
385
+ @@index([expiresAt])
386
+ }
387
+
388
+ model CacheEntry {
389
+ id String @id @default(uuid())
390
+ key String @unique
391
+ value Json
392
+ expiresAt DateTime?
393
+ createdAt DateTime @default(now())
394
+ updatedAt DateTime @updatedAt
395
+
396
+ @@map("cache_entries")
397
+ @@index([key])
398
+ @@index([expiresAt])
399
+ }
400
+
401
+ // ====== CHAT HISTORY MODELS ======
402
+
403
+ model ChatHistory {
404
+ id String @id @default(uuid())
405
+ clientId String // Client identifier (e.g., machine ID, user ID)
406
+ agentName String
407
+ agentDescription String?
408
+ messages Json // Array of { role: 'user' | 'assistant', content: string }
409
+ createdAt DateTime @default(now())
410
+ lastMessageAt DateTime @default(now())
411
+ updatedAt DateTime @updatedAt
412
+
413
+ @@map("chat_history")
414
+ @@index([clientId])
415
+ @@index([clientId, lastMessageAt])
416
+ }
417
+
418
+ // ====== DEVKIT AGENT & TOOL MANAGEMENT ======
419
+
420
+ model DevKitAgent {
421
+ id String @id @default(uuid())
422
+ name String
423
+ slug String @unique
424
+ description String?
425
+ type String // lead_agent, education_agent, custom, etc.
426
+ model String // gpt-4, claude-3, etc.
427
+ config Json @default("{}") // { prompt, temperature, maxTokens, etc. }
428
+ createdAt DateTime @default(now())
429
+ updatedAt DateTime @updatedAt
430
+
431
+ // Relations
432
+ tools DevKitAgentTool[]
433
+
434
+ @@map("devkit_agents")
435
+ @@index([slug])
436
+ @@index([type])
437
+ }
438
+
439
+ model DevKitTool {
440
+ id String @id @default(uuid())
441
+ name String
442
+ kind String // http, email, vector, database, etc.
443
+ config Json @default("{}") // Flexible config per kind
444
+ enabled Boolean @default(true)
445
+ createdAt DateTime @default(now())
446
+ updatedAt DateTime @updatedAt
447
+
448
+ // Relations
449
+ agents DevKitAgentTool[]
450
+
451
+ @@map("devkit_tools")
452
+ @@index([kind])
453
+ @@index([enabled])
454
+ }
455
+
456
+ model DevKitAgentTool {
457
+ id String @id @default(uuid())
458
+ agentId String
459
+ toolId String
460
+ configOverride Json? // Optional per-agent tool configuration
461
+ createdAt DateTime @default(now())
462
+
463
+ // Relations
464
+ agent DevKitAgent @relation(fields: [agentId], references: [id], onDelete: Cascade)
465
+ tool DevKitTool @relation(fields: [toolId], references: [id], onDelete: Cascade)
466
+
467
+ @@unique([agentId, toolId])
468
+ @@map("devkit_agent_tools")
469
+ @@index([agentId])
470
+ @@index([toolId])
471
471
  }