agent-tower 0.5.3-beta.9 → 0.5.4-beta.0

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.
Files changed (73) hide show
  1. package/dist/mcp/http-client.d.ts +31 -1
  2. package/dist/mcp/http-client.d.ts.map +1 -1
  3. package/dist/mcp/http-client.js +14 -2
  4. package/dist/mcp/http-client.js.map +1 -1
  5. package/dist/mcp/server.d.ts.map +1 -1
  6. package/dist/mcp/server.js +62 -8
  7. package/dist/mcp/server.js.map +1 -1
  8. package/dist/mcp/tools/__tests__/workspaces.test.d.ts +2 -0
  9. package/dist/mcp/tools/__tests__/workspaces.test.d.ts.map +1 -0
  10. package/dist/mcp/tools/__tests__/workspaces.test.js +72 -0
  11. package/dist/mcp/tools/__tests__/workspaces.test.js.map +1 -0
  12. package/dist/mcp/tools/workspaces.d.ts +9 -1
  13. package/dist/mcp/tools/workspaces.d.ts.map +1 -1
  14. package/dist/mcp/tools/workspaces.js +96 -2
  15. package/dist/mcp/tools/workspaces.js.map +1 -1
  16. package/dist/mcp/types.d.ts +46 -0
  17. package/dist/mcp/types.d.ts.map +1 -1
  18. package/dist/mcp/types.js +23 -0
  19. package/dist/mcp/types.js.map +1 -1
  20. package/dist/routes/__tests__/team-runs-merge.test.d.ts +2 -0
  21. package/dist/routes/__tests__/team-runs-merge.test.d.ts.map +1 -0
  22. package/dist/routes/__tests__/team-runs-merge.test.js +156 -0
  23. package/dist/routes/__tests__/team-runs-merge.test.js.map +1 -0
  24. package/dist/routes/__tests__/team-runs-private-messages.test.d.ts +2 -0
  25. package/dist/routes/__tests__/team-runs-private-messages.test.d.ts.map +1 -0
  26. package/dist/routes/__tests__/team-runs-private-messages.test.js +82 -0
  27. package/dist/routes/__tests__/team-runs-private-messages.test.js.map +1 -0
  28. package/dist/routes/team-runs.d.ts +2 -0
  29. package/dist/routes/team-runs.d.ts.map +1 -1
  30. package/dist/routes/team-runs.js +48 -1
  31. package/dist/routes/team-runs.js.map +1 -1
  32. package/dist/routes/workspaces.d.ts.map +1 -1
  33. package/dist/routes/workspaces.js +43 -4
  34. package/dist/routes/workspaces.js.map +1 -1
  35. package/dist/services/__tests__/session-manager.team-run.test.js +75 -0
  36. package/dist/services/__tests__/session-manager.team-run.test.js.map +1 -1
  37. package/dist/services/__tests__/team-reconciler.service.test.js +127 -8
  38. package/dist/services/__tests__/team-reconciler.service.test.js.map +1 -1
  39. package/dist/services/__tests__/team-run.service.test.js +172 -0
  40. package/dist/services/__tests__/team-run.service.test.js.map +1 -1
  41. package/dist/services/__tests__/team-scheduler.service.test.js +397 -0
  42. package/dist/services/__tests__/team-scheduler.service.test.js.map +1 -1
  43. package/dist/services/__tests__/workspace.service.test.js +959 -3
  44. package/dist/services/__tests__/workspace.service.test.js.map +1 -1
  45. package/dist/services/session-manager.d.ts.map +1 -1
  46. package/dist/services/session-manager.js +16 -0
  47. package/dist/services/session-manager.js.map +1 -1
  48. package/dist/services/team-run.service.d.ts +5 -1
  49. package/dist/services/team-run.service.d.ts.map +1 -1
  50. package/dist/services/team-run.service.js +63 -2
  51. package/dist/services/team-run.service.js.map +1 -1
  52. package/dist/services/team-scheduler.service.d.ts +21 -1
  53. package/dist/services/team-scheduler.service.d.ts.map +1 -1
  54. package/dist/services/team-scheduler.service.js +230 -8
  55. package/dist/services/team-scheduler.service.js.map +1 -1
  56. package/dist/services/workspace.service.d.ts +56 -1
  57. package/dist/services/workspace.service.d.ts.map +1 -1
  58. package/dist/services/workspace.service.js +837 -4
  59. package/dist/services/workspace.service.js.map +1 -1
  60. package/node_modules/@agent-tower/shared/dist/types.d.ts +168 -0
  61. package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
  62. package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
  63. package/node_modules/@prisma/client/.prisma/client/edge.js +39 -5
  64. package/node_modules/@prisma/client/.prisma/client/index-browser.js +34 -0
  65. package/node_modules/@prisma/client/.prisma/client/index.d.ts +2871 -158
  66. package/node_modules/@prisma/client/.prisma/client/index.js +39 -5
  67. package/node_modules/@prisma/client/.prisma/client/package.json +1 -1
  68. package/node_modules/@prisma/client/.prisma/client/schema.prisma +81 -30
  69. package/node_modules/@prisma/client/.prisma/client/wasm.js +34 -0
  70. package/package.json +1 -1
  71. package/prisma/migrations/20260618010000_add_workspace_verdicts/migration.sql +27 -0
  72. package/prisma/migrations/20260702000000_add_teamrun_target_commit_fields/migration.sql +38 -0
  73. package/prisma/schema.prisma +51 -0
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "prisma-client-9b2814343bb75bf50926f62478962d7b4091d20d3be657242d6e6463a564b5c7",
2
+ "name": "prisma-client-bf1ec758f6396e55f79a58dfb50036200f73ffdc72811e12769dfcb63bd331d7",
3
3
  "main": "index.js",
4
4
  "types": "index.d.ts",
5
5
  "browser": "index-browser.js",
@@ -108,19 +108,20 @@ model TeamTemplateMember {
108
108
  }
109
109
 
110
110
  model TeamRun {
111
- id String @id @default(uuid())
112
- taskId String @unique
113
- task Task @relation(fields: [taskId], references: [id], onDelete: Cascade)
111
+ id String @id @default(uuid())
112
+ taskId String @unique
113
+ task Task @relation(fields: [taskId], references: [id], onDelete: Cascade)
114
114
  mainWorkspaceId String?
115
- mainWorkspace Workspace? @relation("TeamRunMainWorkspace", fields: [mainWorkspaceId], references: [id], onDelete: SetNull)
115
+ mainWorkspace Workspace? @relation("TeamRunMainWorkspace", fields: [mainWorkspaceId], references: [id], onDelete: SetNull)
116
116
  mode String
117
117
  reviewReason String?
118
- createdAt DateTime @default(now())
119
- updatedAt DateTime @updatedAt
118
+ createdAt DateTime @default(now())
119
+ updatedAt DateTime @updatedAt
120
120
  members TeamMember[]
121
121
  messages RoomMessage[]
122
122
  workRequests WorkRequest[]
123
123
  invocations AgentInvocation[]
124
+ verdicts WorkspaceVerdict[]
124
125
 
125
126
  @@index([mainWorkspaceId])
126
127
  }
@@ -190,22 +191,31 @@ model RoomMessageParticipant {
190
191
  }
191
192
 
192
193
  model WorkRequest {
193
- id String @id @default(uuid())
194
- teamRunId String
195
- teamRun TeamRun @relation(fields: [teamRunId], references: [id], onDelete: Cascade)
196
- requesterMemberId String?
197
- requesterType String
198
- targetMemberId String
199
- triggerMessageId String
200
- instruction String
201
- ifBusy String @default("queue")
202
- cancelQueued Boolean @default(false)
203
- status String @default("QUEUED")
204
- createdAt DateTime @default(now())
205
- updatedAt DateTime @updatedAt
194
+ id String @id @default(uuid())
195
+ teamRunId String
196
+ teamRun TeamRun @relation(fields: [teamRunId], references: [id], onDelete: Cascade)
197
+ requesterMemberId String?
198
+ requesterType String
199
+ targetMemberId String
200
+ targetKind String?
201
+ targetPurpose String?
202
+ targetSourceWorkspaceId String?
203
+ targetSourceMemberId String?
204
+ targetHeadSha String?
205
+ targetBranchName String?
206
+ targetPlanItemId String?
207
+ triggerMessageId String
208
+ instruction String
209
+ ifBusy String @default("queue")
210
+ cancelQueued Boolean @default(false)
211
+ status String @default("QUEUED")
212
+ createdAt DateTime @default(now())
213
+ updatedAt DateTime @updatedAt
206
214
 
207
215
  @@index([teamRunId])
208
216
  @@index([targetMemberId])
217
+ @@index([targetSourceWorkspaceId])
218
+ @@index([targetHeadSha])
209
219
  @@index([status])
210
220
  }
211
221
 
@@ -217,6 +227,19 @@ model AgentInvocation {
217
227
  memberId String
218
228
  workspaceId String?
219
229
  sessionId String?
230
+ targetKind String?
231
+ targetPurpose String?
232
+ targetSourceWorkspaceId String?
233
+ targetSourceMemberId String?
234
+ targetHeadSha String?
235
+ targetBranchName String?
236
+ targetPlanItemId String?
237
+ targetSyncStatus String?
238
+ targetSyncError String?
239
+ targetExecutionBranch String?
240
+ targetPort Int?
241
+ targetVitePort Int?
242
+ targetE2EPort Int?
220
243
  status String @default("QUEUED")
221
244
  roomReplyReminderCount Int @default(0)
222
245
  nextRoomReplyReminderAt DateTime?
@@ -226,6 +249,9 @@ model AgentInvocation {
226
249
  @@index([teamRunId])
227
250
  @@index([memberId])
228
251
  @@index([sessionId])
252
+ @@index([targetSourceWorkspaceId])
253
+ @@index([targetHeadSha])
254
+ @@index([targetSyncStatus])
229
255
  @@index([status])
230
256
  }
231
257
 
@@ -233,27 +259,28 @@ model AgentInvocation {
233
259
  // WorkspaceStatus: ACTIVE, MERGED, ABANDONED, HIBERNATED
234
260
  // WorkspaceKind: WORKTREE, MAIN_DIRECTORY
235
261
  model Workspace {
236
- id String @id @default(uuid())
262
+ id String @id @default(uuid())
237
263
  taskId String
238
- task Task @relation(fields: [taskId], references: [id], onDelete: Cascade)
264
+ task Task @relation(fields: [taskId], references: [id], onDelete: Cascade)
239
265
  parentWorkspaceId String?
240
- parentWorkspace Workspace? @relation("WorkspaceChildren", fields: [parentWorkspaceId], references: [id], onDelete: SetNull)
241
- childWorkspaces Workspace[] @relation("WorkspaceChildren")
266
+ parentWorkspace Workspace? @relation("WorkspaceChildren", fields: [parentWorkspaceId], references: [id], onDelete: SetNull)
267
+ childWorkspaces Workspace[] @relation("WorkspaceChildren")
242
268
  ownerMemberId String?
243
- ownerMember TeamMember? @relation("WorkspaceOwner", fields: [ownerMemberId], references: [id], onDelete: SetNull)
244
- mainForTeamRuns TeamRun[] @relation("TeamRunMainWorkspace")
269
+ ownerMember TeamMember? @relation("WorkspaceOwner", fields: [ownerMemberId], references: [id], onDelete: SetNull)
270
+ mainForTeamRuns TeamRun[] @relation("TeamRunMainWorkspace")
245
271
  branchName String
246
272
  baseBranch String?
247
273
  worktreePath String
248
- workspaceKind String @default("WORKTREE")
249
- workingDir String @default("")
250
- status String @default("ACTIVE")
274
+ workspaceKind String @default("WORKTREE")
275
+ workingDir String @default("")
276
+ status String @default("ACTIVE")
251
277
  commitMessage String? // AI 生成的 commit message(合并时使用)
252
278
  previewTarget String? // Preview 代理目标(仅允许 loopback 地址)
253
279
  hibernatedAt DateTime? // 自动休眠时间
254
280
  sessions Session[]
255
- createdAt DateTime @default(now())
256
- updatedAt DateTime @updatedAt
281
+ verdicts WorkspaceVerdict[]
282
+ createdAt DateTime @default(now())
283
+ updatedAt DateTime @updatedAt
257
284
 
258
285
  @@unique([parentWorkspaceId, ownerMemberId])
259
286
  @@index([taskId])
@@ -264,6 +291,30 @@ model Workspace {
264
291
  @@index([workingDir])
265
292
  }
266
293
 
294
+ // 工作区审查/测试记录
295
+ // WorkspaceVerdictKind: REVIEW, TEST
296
+ // REVIEW verdict: APPROVED, CHANGES_REQUESTED
297
+ // TEST verdict: PASSED, FAILED
298
+ model WorkspaceVerdict {
299
+ id String @id @default(uuid())
300
+ workspaceId String
301
+ workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
302
+ teamRunId String
303
+ teamRun TeamRun @relation(fields: [teamRunId], references: [id], onDelete: Cascade)
304
+ kind String
305
+ verdict String
306
+ reviewedSha String
307
+ reviewerMemberId String?
308
+ reason String?
309
+ sequence Int @default(0)
310
+ createdAt DateTime @default(now())
311
+
312
+ @@index([workspaceId, kind, createdAt])
313
+ @@index([workspaceId, kind, sequence])
314
+ @@index([teamRunId])
315
+ @@index([reviewerMemberId])
316
+ }
317
+
267
318
  // 独立对话:不绑定 Project/Task/Workspace,仅复用 Session 执行链路
268
319
  model Conversation {
269
320
  id String @id @default(uuid())
@@ -254,6 +254,13 @@ exports.Prisma.WorkRequestScalarFieldEnum = {
254
254
  requesterMemberId: 'requesterMemberId',
255
255
  requesterType: 'requesterType',
256
256
  targetMemberId: 'targetMemberId',
257
+ targetKind: 'targetKind',
258
+ targetPurpose: 'targetPurpose',
259
+ targetSourceWorkspaceId: 'targetSourceWorkspaceId',
260
+ targetSourceMemberId: 'targetSourceMemberId',
261
+ targetHeadSha: 'targetHeadSha',
262
+ targetBranchName: 'targetBranchName',
263
+ targetPlanItemId: 'targetPlanItemId',
257
264
  triggerMessageId: 'triggerMessageId',
258
265
  instruction: 'instruction',
259
266
  ifBusy: 'ifBusy',
@@ -270,6 +277,19 @@ exports.Prisma.AgentInvocationScalarFieldEnum = {
270
277
  memberId: 'memberId',
271
278
  workspaceId: 'workspaceId',
272
279
  sessionId: 'sessionId',
280
+ targetKind: 'targetKind',
281
+ targetPurpose: 'targetPurpose',
282
+ targetSourceWorkspaceId: 'targetSourceWorkspaceId',
283
+ targetSourceMemberId: 'targetSourceMemberId',
284
+ targetHeadSha: 'targetHeadSha',
285
+ targetBranchName: 'targetBranchName',
286
+ targetPlanItemId: 'targetPlanItemId',
287
+ targetSyncStatus: 'targetSyncStatus',
288
+ targetSyncError: 'targetSyncError',
289
+ targetExecutionBranch: 'targetExecutionBranch',
290
+ targetPort: 'targetPort',
291
+ targetVitePort: 'targetVitePort',
292
+ targetE2EPort: 'targetE2EPort',
273
293
  status: 'status',
274
294
  roomReplyReminderCount: 'roomReplyReminderCount',
275
295
  nextRoomReplyReminderAt: 'nextRoomReplyReminderAt',
@@ -295,6 +315,19 @@ exports.Prisma.WorkspaceScalarFieldEnum = {
295
315
  updatedAt: 'updatedAt'
296
316
  };
297
317
 
318
+ exports.Prisma.WorkspaceVerdictScalarFieldEnum = {
319
+ id: 'id',
320
+ workspaceId: 'workspaceId',
321
+ teamRunId: 'teamRunId',
322
+ kind: 'kind',
323
+ verdict: 'verdict',
324
+ reviewedSha: 'reviewedSha',
325
+ reviewerMemberId: 'reviewerMemberId',
326
+ reason: 'reason',
327
+ sequence: 'sequence',
328
+ createdAt: 'createdAt'
329
+ };
330
+
298
331
  exports.Prisma.ConversationScalarFieldEnum = {
299
332
  id: 'id',
300
333
  title: 'title',
@@ -391,6 +424,7 @@ exports.Prisma.ModelName = {
391
424
  WorkRequest: 'WorkRequest',
392
425
  AgentInvocation: 'AgentInvocation',
393
426
  Workspace: 'Workspace',
427
+ WorkspaceVerdict: 'WorkspaceVerdict',
394
428
  Conversation: 'Conversation',
395
429
  Session: 'Session',
396
430
  ExecutionProcess: 'ExecutionProcess',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-tower",
3
- "version": "0.5.3-beta.9",
3
+ "version": "0.5.4-beta.0",
4
4
  "description": "AI Agent Task Management Dashboard",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,27 @@
1
+ -- CreateTable
2
+ CREATE TABLE "WorkspaceVerdict" (
3
+ "id" TEXT NOT NULL PRIMARY KEY,
4
+ "workspaceId" TEXT NOT NULL,
5
+ "teamRunId" TEXT NOT NULL,
6
+ "kind" TEXT NOT NULL,
7
+ "verdict" TEXT NOT NULL,
8
+ "reviewedSha" TEXT NOT NULL,
9
+ "reviewerMemberId" TEXT,
10
+ "reason" TEXT,
11
+ "sequence" INTEGER NOT NULL DEFAULT 0,
12
+ "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
13
+ CONSTRAINT "WorkspaceVerdict_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
14
+ CONSTRAINT "WorkspaceVerdict_teamRunId_fkey" FOREIGN KEY ("teamRunId") REFERENCES "TeamRun" ("id") ON DELETE CASCADE ON UPDATE CASCADE
15
+ );
16
+
17
+ -- CreateIndex
18
+ CREATE INDEX "WorkspaceVerdict_workspaceId_kind_createdAt_idx" ON "WorkspaceVerdict"("workspaceId", "kind", "createdAt");
19
+
20
+ -- CreateIndex
21
+ CREATE INDEX "WorkspaceVerdict_workspaceId_kind_sequence_idx" ON "WorkspaceVerdict"("workspaceId", "kind", "sequence");
22
+
23
+ -- CreateIndex
24
+ CREATE INDEX "WorkspaceVerdict_teamRunId_idx" ON "WorkspaceVerdict"("teamRunId");
25
+
26
+ -- CreateIndex
27
+ CREATE INDEX "WorkspaceVerdict_reviewerMemberId_idx" ON "WorkspaceVerdict"("reviewerMemberId");
@@ -0,0 +1,38 @@
1
+ -- AlterTable
2
+ ALTER TABLE "WorkRequest" ADD COLUMN "targetKind" TEXT;
3
+ ALTER TABLE "WorkRequest" ADD COLUMN "targetPurpose" TEXT;
4
+ ALTER TABLE "WorkRequest" ADD COLUMN "targetSourceWorkspaceId" TEXT;
5
+ ALTER TABLE "WorkRequest" ADD COLUMN "targetSourceMemberId" TEXT;
6
+ ALTER TABLE "WorkRequest" ADD COLUMN "targetHeadSha" TEXT;
7
+ ALTER TABLE "WorkRequest" ADD COLUMN "targetBranchName" TEXT;
8
+ ALTER TABLE "WorkRequest" ADD COLUMN "targetPlanItemId" TEXT;
9
+
10
+ -- AlterTable
11
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetKind" TEXT;
12
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetPurpose" TEXT;
13
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetSourceWorkspaceId" TEXT;
14
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetSourceMemberId" TEXT;
15
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetHeadSha" TEXT;
16
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetBranchName" TEXT;
17
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetPlanItemId" TEXT;
18
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetSyncStatus" TEXT;
19
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetSyncError" TEXT;
20
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetExecutionBranch" TEXT;
21
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetPort" INTEGER;
22
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetVitePort" INTEGER;
23
+ ALTER TABLE "AgentInvocation" ADD COLUMN "targetE2EPort" INTEGER;
24
+
25
+ -- CreateIndex
26
+ CREATE INDEX "WorkRequest_targetSourceWorkspaceId_idx" ON "WorkRequest"("targetSourceWorkspaceId");
27
+
28
+ -- CreateIndex
29
+ CREATE INDEX "WorkRequest_targetHeadSha_idx" ON "WorkRequest"("targetHeadSha");
30
+
31
+ -- CreateIndex
32
+ CREATE INDEX "AgentInvocation_targetSourceWorkspaceId_idx" ON "AgentInvocation"("targetSourceWorkspaceId");
33
+
34
+ -- CreateIndex
35
+ CREATE INDEX "AgentInvocation_targetHeadSha_idx" ON "AgentInvocation"("targetHeadSha");
36
+
37
+ -- CreateIndex
38
+ CREATE INDEX "AgentInvocation_targetSyncStatus_idx" ON "AgentInvocation"("targetSyncStatus");
@@ -121,6 +121,7 @@ model TeamRun {
121
121
  messages RoomMessage[]
122
122
  workRequests WorkRequest[]
123
123
  invocations AgentInvocation[]
124
+ verdicts WorkspaceVerdict[]
124
125
 
125
126
  @@index([mainWorkspaceId])
126
127
  }
@@ -196,6 +197,13 @@ model WorkRequest {
196
197
  requesterMemberId String?
197
198
  requesterType String
198
199
  targetMemberId String
200
+ targetKind String?
201
+ targetPurpose String?
202
+ targetSourceWorkspaceId String?
203
+ targetSourceMemberId String?
204
+ targetHeadSha String?
205
+ targetBranchName String?
206
+ targetPlanItemId String?
199
207
  triggerMessageId String
200
208
  instruction String
201
209
  ifBusy String @default("queue")
@@ -206,6 +214,8 @@ model WorkRequest {
206
214
 
207
215
  @@index([teamRunId])
208
216
  @@index([targetMemberId])
217
+ @@index([targetSourceWorkspaceId])
218
+ @@index([targetHeadSha])
209
219
  @@index([status])
210
220
  }
211
221
 
@@ -217,6 +227,19 @@ model AgentInvocation {
217
227
  memberId String
218
228
  workspaceId String?
219
229
  sessionId String?
230
+ targetKind String?
231
+ targetPurpose String?
232
+ targetSourceWorkspaceId String?
233
+ targetSourceMemberId String?
234
+ targetHeadSha String?
235
+ targetBranchName String?
236
+ targetPlanItemId String?
237
+ targetSyncStatus String?
238
+ targetSyncError String?
239
+ targetExecutionBranch String?
240
+ targetPort Int?
241
+ targetVitePort Int?
242
+ targetE2EPort Int?
220
243
  status String @default("QUEUED")
221
244
  roomReplyReminderCount Int @default(0)
222
245
  nextRoomReplyReminderAt DateTime?
@@ -226,6 +249,9 @@ model AgentInvocation {
226
249
  @@index([teamRunId])
227
250
  @@index([memberId])
228
251
  @@index([sessionId])
252
+ @@index([targetSourceWorkspaceId])
253
+ @@index([targetHeadSha])
254
+ @@index([targetSyncStatus])
229
255
  @@index([status])
230
256
  }
231
257
 
@@ -252,6 +278,7 @@ model Workspace {
252
278
  previewTarget String? // Preview 代理目标(仅允许 loopback 地址)
253
279
  hibernatedAt DateTime? // 自动休眠时间
254
280
  sessions Session[]
281
+ verdicts WorkspaceVerdict[]
255
282
  createdAt DateTime @default(now())
256
283
  updatedAt DateTime @updatedAt
257
284
 
@@ -264,6 +291,30 @@ model Workspace {
264
291
  @@unique([parentWorkspaceId, ownerMemberId])
265
292
  }
266
293
 
294
+ // 工作区审查/测试记录
295
+ // WorkspaceVerdictKind: REVIEW, TEST
296
+ // REVIEW verdict: APPROVED, CHANGES_REQUESTED
297
+ // TEST verdict: PASSED, FAILED
298
+ model WorkspaceVerdict {
299
+ id String @id @default(uuid())
300
+ workspaceId String
301
+ workspace Workspace @relation(fields: [workspaceId], references: [id], onDelete: Cascade)
302
+ teamRunId String
303
+ teamRun TeamRun @relation(fields: [teamRunId], references: [id], onDelete: Cascade)
304
+ kind String
305
+ verdict String
306
+ reviewedSha String
307
+ reviewerMemberId String?
308
+ reason String?
309
+ sequence Int @default(0)
310
+ createdAt DateTime @default(now())
311
+
312
+ @@index([workspaceId, kind, createdAt])
313
+ @@index([workspaceId, kind, sequence])
314
+ @@index([teamRunId])
315
+ @@index([reviewerMemberId])
316
+ }
317
+
267
318
  // 独立对话:不绑定 Project/Task/Workspace,仅复用 Session 执行链路
268
319
  model Conversation {
269
320
  id String @id @default(uuid())