agent-tower 0.4.16-beta.0 → 0.4.16-beta.3
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.
- package/dist/app.test.js +2 -0
- package/dist/app.test.js.map +1 -1
- package/dist/git/git-cli.d.ts +18 -1
- package/dist/git/git-cli.d.ts.map +1 -1
- package/dist/git/git-cli.js +17 -1
- package/dist/git/git-cli.js.map +1 -1
- package/dist/git/worktree.manager.d.ts +29 -2
- package/dist/git/worktree.manager.d.ts.map +1 -1
- package/dist/git/worktree.manager.js +137 -16
- package/dist/git/worktree.manager.js.map +1 -1
- package/dist/git/worktree.manager.test.d.ts +2 -0
- package/dist/git/worktree.manager.test.d.ts.map +1 -0
- package/dist/git/worktree.manager.test.js +104 -0
- package/dist/git/worktree.manager.test.js.map +1 -0
- package/dist/mcp/http-client.d.ts.map +1 -1
- package/dist/mcp/http-client.js +8 -1
- package/dist/mcp/http-client.js.map +1 -1
- package/dist/middleware/tunnel-auth.d.ts.map +1 -1
- package/dist/middleware/tunnel-auth.js +2 -0
- package/dist/middleware/tunnel-auth.js.map +1 -1
- package/dist/output/__tests__/codex-parser.test.d.ts +2 -0
- package/dist/output/__tests__/codex-parser.test.d.ts.map +1 -0
- package/dist/output/__tests__/codex-parser.test.js +148 -0
- package/dist/output/__tests__/codex-parser.test.js.map +1 -0
- package/dist/output/codex-parser.d.ts +12 -0
- package/dist/output/codex-parser.d.ts.map +1 -1
- package/dist/output/codex-parser.js +129 -12
- package/dist/output/codex-parser.js.map +1 -1
- package/dist/routes/__tests__/attachments.test.d.ts +2 -0
- package/dist/routes/__tests__/attachments.test.d.ts.map +1 -0
- package/dist/routes/__tests__/attachments.test.js +86 -0
- package/dist/routes/__tests__/attachments.test.js.map +1 -0
- package/dist/routes/__tests__/filesystem.test.d.ts +2 -0
- package/dist/routes/__tests__/filesystem.test.d.ts.map +1 -0
- package/dist/routes/__tests__/filesystem.test.js +80 -0
- package/dist/routes/__tests__/filesystem.test.js.map +1 -0
- package/dist/routes/__tests__/previews.test.d.ts +2 -0
- package/dist/routes/__tests__/previews.test.d.ts.map +1 -0
- package/dist/routes/__tests__/previews.test.js +89 -0
- package/dist/routes/__tests__/previews.test.js.map +1 -0
- package/dist/routes/__tests__/tasks.test.d.ts +2 -0
- package/dist/routes/__tests__/tasks.test.d.ts.map +1 -0
- package/dist/routes/__tests__/tasks.test.js +72 -0
- package/dist/routes/__tests__/tasks.test.js.map +1 -0
- package/dist/routes/attachments.d.ts.map +1 -1
- package/dist/routes/attachments.js +36 -16
- package/dist/routes/attachments.js.map +1 -1
- package/dist/routes/filesystem.d.ts.map +1 -1
- package/dist/routes/filesystem.js +24 -3
- package/dist/routes/filesystem.js.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +3 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/previews.d.ts +6 -0
- package/dist/routes/previews.d.ts.map +1 -0
- package/dist/routes/previews.js +413 -0
- package/dist/routes/previews.js.map +1 -0
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +1 -0
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/tasks.js +2 -2
- package/dist/routes/tasks.js.map +1 -1
- package/dist/routes/team-runs.d.ts.map +1 -1
- package/dist/routes/team-runs.js +18 -8
- package/dist/routes/team-runs.js.map +1 -1
- package/dist/routes/tunnel.d.ts.map +1 -1
- package/dist/routes/tunnel.js +20 -0
- package/dist/routes/tunnel.js.map +1 -1
- package/dist/routes/workspaces.d.ts.map +1 -1
- package/dist/routes/workspaces.js +15 -1
- package/dist/routes/workspaces.js.map +1 -1
- package/dist/services/__tests__/preview.service.test.d.ts +2 -0
- package/dist/services/__tests__/preview.service.test.d.ts.map +1 -0
- package/dist/services/__tests__/preview.service.test.js +29 -0
- package/dist/services/__tests__/preview.service.test.js.map +1 -0
- package/dist/services/__tests__/task.service.test.d.ts +2 -0
- package/dist/services/__tests__/task.service.test.d.ts.map +1 -0
- package/dist/services/__tests__/task.service.test.js +65 -0
- package/dist/services/__tests__/task.service.test.js.map +1 -0
- package/dist/services/__tests__/team-reconciler.service.test.js +525 -27
- package/dist/services/__tests__/team-reconciler.service.test.js.map +1 -1
- package/dist/services/__tests__/team-run.service.test.js +252 -0
- package/dist/services/__tests__/team-run.service.test.js.map +1 -1
- package/dist/services/__tests__/team-scheduler.service.test.js +551 -21
- package/dist/services/__tests__/team-scheduler.service.test.js.map +1 -1
- package/dist/services/__tests__/tunnel.service.test.d.ts +2 -0
- package/dist/services/__tests__/tunnel.service.test.d.ts.map +1 -0
- package/dist/services/__tests__/tunnel.service.test.js +138 -0
- package/dist/services/__tests__/tunnel.service.test.js.map +1 -0
- package/dist/services/__tests__/workspace.service.test.d.ts +2 -0
- package/dist/services/__tests__/workspace.service.test.d.ts.map +1 -0
- package/dist/services/__tests__/workspace.service.test.js +695 -0
- package/dist/services/__tests__/workspace.service.test.js.map +1 -0
- package/dist/services/attachment-context.d.ts +3 -0
- package/dist/services/attachment-context.d.ts.map +1 -0
- package/dist/services/attachment-context.js +34 -0
- package/dist/services/attachment-context.js.map +1 -0
- package/dist/services/preview.service.d.ts +19 -0
- package/dist/services/preview.service.d.ts.map +1 -0
- package/dist/services/preview.service.js +147 -0
- package/dist/services/preview.service.js.map +1 -0
- package/dist/services/project.service.d.ts +2 -0
- package/dist/services/project.service.d.ts.map +1 -1
- package/dist/services/project.service.js +87 -18
- package/dist/services/project.service.js.map +1 -1
- package/dist/services/session-manager.d.ts +12 -0
- package/dist/services/session-manager.d.ts.map +1 -1
- package/dist/services/task.service.d.ts +6 -0
- package/dist/services/task.service.d.ts.map +1 -1
- package/dist/services/task.service.js +15 -3
- package/dist/services/task.service.js.map +1 -1
- package/dist/services/team-lock.service.d.ts +3 -0
- package/dist/services/team-lock.service.d.ts.map +1 -1
- package/dist/services/team-lock.service.js +11 -0
- package/dist/services/team-lock.service.js.map +1 -1
- package/dist/services/team-run.service.d.ts +3 -0
- package/dist/services/team-run.service.d.ts.map +1 -1
- package/dist/services/team-run.service.js +288 -30
- package/dist/services/team-run.service.js.map +1 -1
- package/dist/services/team-scheduler.service.d.ts +15 -0
- package/dist/services/team-scheduler.service.d.ts.map +1 -1
- package/dist/services/team-scheduler.service.js +125 -32
- package/dist/services/team-scheduler.service.js.map +1 -1
- package/dist/services/tunnel.service.d.ts +31 -5
- package/dist/services/tunnel.service.d.ts.map +1 -1
- package/dist/services/tunnel.service.js +293 -32
- package/dist/services/tunnel.service.js.map +1 -1
- package/dist/services/workspace.service.d.ts +161 -7
- package/dist/services/workspace.service.d.ts.map +1 -1
- package/dist/services/workspace.service.js +396 -51
- package/dist/services/workspace.service.js.map +1 -1
- package/dist/web/assets/{AgentDemoPage-p9YI4_l4.js → AgentDemoPage-Bf6labVB.js} +1 -1
- package/dist/web/assets/{DemoPage-B5DTSEbS.js → DemoPage-DlfG47rV.js} +1 -1
- package/dist/web/assets/{GeneralSettingsPage-Cspr7Vol.js → GeneralSettingsPage-DefqwzVn.js} +1 -1
- package/dist/web/assets/MemberAvatar-DVw_TedB.js +1 -0
- package/dist/web/assets/NotificationSettingsPage-C9h1U1Za.js +1 -0
- package/dist/web/assets/{ProfileSettingsPage-CNugU40a.js → ProfileSettingsPage-BkZE2yVP.js} +1 -1
- package/dist/web/assets/ProjectKanbanPage-B1Ckl1uY.js +89 -0
- package/dist/web/assets/ProjectSettingsPage-ByZ13awb.js +2 -0
- package/dist/web/assets/{ProviderSettingsPage-D_KWkgRM.js → ProviderSettingsPage-DSQYe8B6.js} +12 -12
- package/dist/web/assets/TeamSettingsPage-DUukJ_Ih.js +1 -0
- package/dist/web/assets/agent-tower-logo-COx9gy77.png +0 -0
- package/dist/web/assets/{button-B6JaSbDB.js → button-Bpm98eOV.js} +1 -1
- package/dist/web/assets/{chevron-down-CACy4UFq.js → chevron-down-DSKKXCi8.js} +1 -1
- package/dist/web/assets/{chevron-right-DFWfnDJY.js → chevron-right-CZdDV9GU.js} +1 -1
- package/dist/web/assets/{chevron-up-CGlf6jzw.js → chevron-up-gnnlwvYe.js} +1 -1
- package/dist/web/assets/{circle-check-DMK8auwb.js → circle-check-DeD_VuLK.js} +1 -1
- package/dist/web/assets/{code-block-OCS4YCEC-Hn75KHRK.js → code-block-OCS4YCEC-BrGjkdjS.js} +1 -1
- package/dist/web/assets/{confirm-dialog-DHI2f7Ni.js → confirm-dialog-CEVVvAcE.js} +1 -1
- package/dist/web/assets/folder-picker-ZBQlFEWL.js +1 -0
- package/dist/web/assets/{index-BFAA3PTl.js → index-B5g4V0NU.js} +6 -6
- package/dist/web/assets/index-ltjI8o6A.css +1 -0
- package/dist/web/assets/loader-circle-GMfBClX0.js +1 -0
- package/dist/web/assets/{log-adapter-CeKrvZcz.js → log-adapter-DKKM3sxS.js} +1 -1
- package/dist/web/assets/{mermaid-NOHMQCX5-DJFgrXPd.js → mermaid-NOHMQCX5-D5USvUiZ.js} +4 -4
- package/dist/web/assets/{modal-B5IRN7QI.js → modal-JMpuh-LG.js} +1 -1
- package/dist/web/assets/{pencil-CJY6Ahn7.js → pencil-QrCW47nn.js} +1 -1
- package/dist/web/assets/{select-BPZZlla1.js → select-CINRzLiE.js} +1 -1
- package/dist/web/assets/upload-vFxZxKHo.js +1 -0
- package/dist/web/assets/{use-profiles-C2k04ICZ.js → use-profiles-SrVWPYv0.js} +1 -1
- package/dist/web/assets/{use-providers-C7fIDWzP.js → use-providers-BihMydl0.js} +1 -1
- package/dist/web/avatars/presets/avatar-preset-01-developer.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-02-architect.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-03-tester.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-04-devops.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-05-data-scientist.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-06-frontend.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-07-backend.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-08-security.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-09-project-manager.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-10-product-manager.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-11-scrum-master.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-12-tech-lead.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-13-coordinator.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-14-mentor.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-15-reviewer.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-16-ui-designer.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-17-ux-researcher.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-18-documenter.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-19-translator.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-20-analyst.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-21-consultant.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-22-creative-director.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-23-support.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-24-assistant.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-25-robot.png +0 -0
- package/dist/web/avatars/presets/avatar-preset-grid.png +0 -0
- package/dist/web/index.html +2 -2
- package/node_modules/@agent-tower/shared/dist/types.d.ts +9 -1
- package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
- package/node_modules/@prisma/client/.prisma/client/edge.js +9 -5
- package/node_modules/@prisma/client/.prisma/client/index-browser.js +4 -0
- package/node_modules/@prisma/client/.prisma/client/index.d.ts +1242 -14
- package/node_modules/@prisma/client/.prisma/client/index.js +9 -5
- package/node_modules/@prisma/client/.prisma/client/package.json +1 -1
- package/node_modules/@prisma/client/.prisma/client/schema.prisma +47 -31
- package/node_modules/@prisma/client/.prisma/client/wasm.js +4 -0
- package/package.json +1 -1
- package/prisma/migrations/20260515000000_add_workspace_preview_target/migration.sql +2 -0
- package/prisma/migrations/20260526000000_add_team_run_main_and_dedicated_workspaces/migration.sql +21 -0
- package/prisma/schema.prisma +27 -11
- package/dist/web/assets/NotificationSettingsPage-C9VfrRr-.js +0 -1
- package/dist/web/assets/ProjectKanbanPage-CkGNuqxq.js +0 -87
- package/dist/web/assets/ProjectSettingsPage-f1dg0XMf.js +0 -2
- package/dist/web/assets/TeamSettingsPage-B6WciZyi.js +0 -1
- package/dist/web/assets/circle-alert-BSAUEd9O.js +0 -1
- package/dist/web/assets/folder-picker-CtQkbWfa.js +0 -1
- package/dist/web/assets/index-mBCb67dB.css +0 -1
- package/dist/web/assets/loader-circle-CkDnf8ST.js +0 -1
- package/dist/web/assets/use-projects-BxuE-ulT.js +0 -1
|
@@ -194,6 +194,22 @@ async function createFixture(options = {}) {
|
|
|
194
194
|
}
|
|
195
195
|
return { project, task, workspace, teamRun, members };
|
|
196
196
|
}
|
|
197
|
+
async function createMemberPreset(options = {}) {
|
|
198
|
+
const name = options.name ?? 'Leader';
|
|
199
|
+
return prisma.memberPreset.create({
|
|
200
|
+
data: {
|
|
201
|
+
name,
|
|
202
|
+
aliases: stringifyJson([name.toLowerCase()]),
|
|
203
|
+
providerId: `provider-${name.toLowerCase()}`,
|
|
204
|
+
rolePrompt: `${name} role`,
|
|
205
|
+
capabilities: stringifyJson(capabilities),
|
|
206
|
+
workspacePolicy: 'shared',
|
|
207
|
+
triggerPolicy: options.triggerPolicy ?? 'USER_MESSAGES',
|
|
208
|
+
sessionPolicy: 'new_per_request',
|
|
209
|
+
avatar: null,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
197
213
|
async function createWorkRequest(options) {
|
|
198
214
|
return prisma.workRequest.create({
|
|
199
215
|
data: {
|
|
@@ -279,6 +295,9 @@ describe('TeamReconcilerService', () => {
|
|
|
279
295
|
await prisma.roomMessage.deleteMany();
|
|
280
296
|
await prisma.teamMember.deleteMany();
|
|
281
297
|
await prisma.teamRun.deleteMany();
|
|
298
|
+
await prisma.teamTemplateMember.deleteMany();
|
|
299
|
+
await prisma.teamTemplate.deleteMany();
|
|
300
|
+
await prisma.memberPreset.deleteMany();
|
|
282
301
|
await prisma.session.deleteMany();
|
|
283
302
|
await prisma.workspace.deleteMany();
|
|
284
303
|
await prisma.task.deleteMany();
|
|
@@ -588,16 +607,30 @@ describe('TeamReconcilerService', () => {
|
|
|
588
607
|
senderId: members[0].id,
|
|
589
608
|
senderInvocationId: invocation.id,
|
|
590
609
|
});
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
610
|
+
let createdRequestId = '';
|
|
611
|
+
await vi.waitFor(async () => {
|
|
612
|
+
const createdRequest = await prisma.workRequest.findFirst({
|
|
613
|
+
where: {
|
|
614
|
+
teamRunId: teamRun.id,
|
|
615
|
+
triggerMessageId: message.id,
|
|
616
|
+
targetMemberId: members[1].id,
|
|
617
|
+
},
|
|
618
|
+
});
|
|
619
|
+
expect(createdRequest).toMatchObject({
|
|
620
|
+
requesterType: 'agent',
|
|
621
|
+
requesterMemberId: members[0].id,
|
|
622
|
+
status: 'STARTED',
|
|
623
|
+
});
|
|
624
|
+
createdRequestId = createdRequest.id;
|
|
625
|
+
});
|
|
626
|
+
await vi.waitFor(async () => {
|
|
627
|
+
await expect(prisma.agentInvocation.findFirst({
|
|
628
|
+
where: { workRequestId: createdRequestId },
|
|
629
|
+
})).resolves.toMatchObject({
|
|
630
|
+
memberId: members[1].id,
|
|
631
|
+
sessionId: null,
|
|
632
|
+
status: 'FAILED',
|
|
633
|
+
});
|
|
601
634
|
});
|
|
602
635
|
}
|
|
603
636
|
finally {
|
|
@@ -693,16 +726,30 @@ describe('TeamReconcilerService', () => {
|
|
|
693
726
|
senderId: members[0].id,
|
|
694
727
|
senderInvocationId: invocation.id,
|
|
695
728
|
});
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
729
|
+
let createdRequestId = '';
|
|
730
|
+
await vi.waitFor(async () => {
|
|
731
|
+
const createdRequest = await prisma.workRequest.findFirst({
|
|
732
|
+
where: {
|
|
733
|
+
teamRunId: teamRun.id,
|
|
734
|
+
triggerMessageId: message.id,
|
|
735
|
+
targetMemberId: members[1].id,
|
|
736
|
+
},
|
|
737
|
+
});
|
|
738
|
+
expect(createdRequest).toMatchObject({
|
|
739
|
+
requesterType: 'agent',
|
|
740
|
+
requesterMemberId: members[0].id,
|
|
741
|
+
status: 'STARTED',
|
|
742
|
+
});
|
|
743
|
+
createdRequestId = createdRequest.id;
|
|
744
|
+
});
|
|
745
|
+
await vi.waitFor(async () => {
|
|
746
|
+
await expect(prisma.agentInvocation.findFirst({
|
|
747
|
+
where: { workRequestId: createdRequestId },
|
|
748
|
+
})).resolves.toMatchObject({
|
|
749
|
+
memberId: members[1].id,
|
|
750
|
+
sessionId: null,
|
|
751
|
+
status: 'FAILED',
|
|
752
|
+
});
|
|
706
753
|
});
|
|
707
754
|
}
|
|
708
755
|
finally {
|
|
@@ -736,7 +783,19 @@ describe('TeamReconcilerService', () => {
|
|
|
736
783
|
it('lists TeamRun members through MCP without exposing role prompts', async () => {
|
|
737
784
|
const previousTeamRunId = process.env.AGENT_TOWER_TEAM_RUN_ID;
|
|
738
785
|
const previousMemberId = process.env.AGENT_TOWER_MEMBER_ID;
|
|
739
|
-
const { teamRun, members } = await createFixture({ memberCount: 2 });
|
|
786
|
+
const { workspace, teamRun, members } = await createFixture({ memberCount: 2 });
|
|
787
|
+
const runningRequest = await createWorkRequest({
|
|
788
|
+
teamRunId: teamRun.id,
|
|
789
|
+
targetMemberId: members[0].id,
|
|
790
|
+
status: 'STARTED',
|
|
791
|
+
});
|
|
792
|
+
await createRunningInvocation({
|
|
793
|
+
teamRunId: teamRun.id,
|
|
794
|
+
workRequestId: runningRequest.id,
|
|
795
|
+
memberId: members[0].id,
|
|
796
|
+
workspaceId: workspace.id,
|
|
797
|
+
status: 'RUNNING',
|
|
798
|
+
});
|
|
740
799
|
const app = Fastify({ logger: false });
|
|
741
800
|
try {
|
|
742
801
|
process.env.AGENT_TOWER_TEAM_RUN_ID = teamRun.id;
|
|
@@ -771,7 +830,7 @@ describe('TeamReconcilerService', () => {
|
|
|
771
830
|
id: members[0].id,
|
|
772
831
|
name: 'Member 1',
|
|
773
832
|
aliases: ['member-1'],
|
|
774
|
-
status: '
|
|
833
|
+
status: 'RUNNING',
|
|
775
834
|
workspacePolicy: 'shared',
|
|
776
835
|
triggerPolicy: 'MENTION_ONLY',
|
|
777
836
|
sessionPolicy: 'new_per_request',
|
|
@@ -948,6 +1007,11 @@ describe('TeamReconcilerService', () => {
|
|
|
948
1007
|
triggerPolicies: ['USER_MESSAGES', 'MENTION_ONLY'],
|
|
949
1008
|
});
|
|
950
1009
|
const routeScheduler = createRouteSchedulerMock();
|
|
1010
|
+
const startedTeamRunIds = [];
|
|
1011
|
+
routeScheduler.startNextSessions = vi.fn(async (teamRunId) => {
|
|
1012
|
+
startedTeamRunIds.push(teamRunId);
|
|
1013
|
+
return await new Promise(() => { });
|
|
1014
|
+
});
|
|
951
1015
|
const app = Fastify({ logger: false });
|
|
952
1016
|
try {
|
|
953
1017
|
await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
|
|
@@ -963,22 +1027,456 @@ describe('TeamReconcilerService', () => {
|
|
|
963
1027
|
const message = response.json();
|
|
964
1028
|
expect(message.mentions).toEqual([]);
|
|
965
1029
|
expect(message.workRequestIds).toHaveLength(1);
|
|
966
|
-
expect(routeScheduler.startNextSessions).
|
|
967
|
-
expect(routeScheduler.startedTeamRunIds).toEqual([teamRun.id]);
|
|
1030
|
+
expect(routeScheduler.startNextSessions).not.toHaveBeenCalled();
|
|
968
1031
|
await expect(prisma.workRequest.findUnique({ where: { id: message.workRequestIds[0] } })).resolves.toMatchObject({
|
|
969
1032
|
targetMemberId: members[0].id,
|
|
970
1033
|
instruction: '普通用户消息',
|
|
971
|
-
status: '
|
|
1034
|
+
status: 'QUEUED',
|
|
972
1035
|
});
|
|
973
1036
|
await expect(prisma.agentInvocation.count({
|
|
974
1037
|
where: {
|
|
975
1038
|
teamRunId: teamRun.id,
|
|
976
1039
|
memberId: members[0].id,
|
|
977
|
-
status: 'RUNNING',
|
|
978
1040
|
},
|
|
979
|
-
})).resolves.toBe(
|
|
1041
|
+
})).resolves.toBe(0);
|
|
1042
|
+
await vi.waitFor(() => {
|
|
1043
|
+
expect(routeScheduler.startNextSessions).toHaveBeenCalledWith(teamRun.id);
|
|
1044
|
+
expect(startedTeamRunIds).toEqual([teamRun.id]);
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
finally {
|
|
1048
|
+
await app.close();
|
|
1049
|
+
}
|
|
1050
|
+
});
|
|
1051
|
+
it('creates an initial Team Room message without WorkRequests for unmentioned MENTION_ONLY members', async () => {
|
|
1052
|
+
const project = await prisma.project.create({
|
|
1053
|
+
data: {
|
|
1054
|
+
name: 'Initial message project',
|
|
1055
|
+
repoPath: testDir,
|
|
1056
|
+
},
|
|
1057
|
+
});
|
|
1058
|
+
const task = await prisma.task.create({
|
|
1059
|
+
data: {
|
|
1060
|
+
title: 'Build CSV import',
|
|
1061
|
+
description: 'Parse uploaded files and show validation errors.',
|
|
1062
|
+
projectId: project.id,
|
|
1063
|
+
},
|
|
1064
|
+
});
|
|
1065
|
+
const implementerPreset = await createMemberPreset({ name: 'Implementer', triggerPolicy: 'MENTION_ONLY' });
|
|
1066
|
+
const reviewerPreset = await createMemberPreset({ name: 'Reviewer', triggerPolicy: 'MENTION_ONLY' });
|
|
1067
|
+
const routeScheduler = createRouteSchedulerMock();
|
|
1068
|
+
const app = Fastify({ logger: false });
|
|
1069
|
+
try {
|
|
1070
|
+
await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
|
|
1071
|
+
const response = await app.inject({
|
|
1072
|
+
method: 'POST',
|
|
1073
|
+
url: `/api/tasks/${task.id}/team-runs`,
|
|
1074
|
+
payload: {
|
|
1075
|
+
mode: 'AUTO',
|
|
1076
|
+
memberPresetIds: [implementerPreset.id, reviewerPreset.id],
|
|
1077
|
+
},
|
|
1078
|
+
});
|
|
1079
|
+
expect(response.statusCode).toBe(201);
|
|
1080
|
+
const teamRun = response.json();
|
|
1081
|
+
const initialContent = 'Build CSV import\n\nParse uploaded files and show validation errors.';
|
|
1082
|
+
expect(teamRun.messages).toHaveLength(1);
|
|
1083
|
+
expect(teamRun.messages[0]).toMatchObject({
|
|
1084
|
+
senderType: 'user',
|
|
1085
|
+
kind: 'chat',
|
|
1086
|
+
content: initialContent,
|
|
1087
|
+
mentions: [],
|
|
1088
|
+
workRequestIds: [],
|
|
1089
|
+
});
|
|
1090
|
+
expect(teamRun.workRequests).toEqual([]);
|
|
1091
|
+
const messagesResponse = await app.inject({
|
|
1092
|
+
method: 'GET',
|
|
1093
|
+
url: `/api/team-runs/${teamRun.id}/messages`,
|
|
1094
|
+
});
|
|
1095
|
+
expect(messagesResponse.statusCode).toBe(200);
|
|
1096
|
+
const messages = messagesResponse.json();
|
|
1097
|
+
expect(messages).toEqual([
|
|
1098
|
+
expect.objectContaining({
|
|
1099
|
+
id: teamRun.messages[0].id,
|
|
1100
|
+
content: initialContent,
|
|
1101
|
+
mentions: [],
|
|
1102
|
+
workRequestIds: [],
|
|
1103
|
+
}),
|
|
1104
|
+
]);
|
|
1105
|
+
const workRequestsResponse = await app.inject({
|
|
1106
|
+
method: 'GET',
|
|
1107
|
+
url: `/api/team-runs/${teamRun.id}/work-requests`,
|
|
1108
|
+
});
|
|
1109
|
+
expect(workRequestsResponse.statusCode).toBe(200);
|
|
1110
|
+
const workRequests = workRequestsResponse.json();
|
|
1111
|
+
expect(workRequests).toEqual([]);
|
|
1112
|
+
await expect(prisma.roomMessage.count({ where: { teamRunId: teamRun.id } })).resolves.toBe(1);
|
|
1113
|
+
await expect(prisma.workRequest.count({ where: { teamRunId: teamRun.id } })).resolves.toBe(0);
|
|
1114
|
+
expect(routeScheduler.startNextSessions).not.toHaveBeenCalled();
|
|
1115
|
+
await expect(prisma.agentInvocation.count({ where: { teamRunId: teamRun.id } })).resolves.toBe(0);
|
|
1116
|
+
}
|
|
1117
|
+
finally {
|
|
1118
|
+
await app.close();
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
it('creates USER_MESSAGES WorkRequests from a title-only initial message and auto-starts it', async () => {
|
|
1122
|
+
const project = await prisma.project.create({
|
|
1123
|
+
data: {
|
|
1124
|
+
name: 'Title-only project',
|
|
1125
|
+
repoPath: testDir,
|
|
1126
|
+
},
|
|
1127
|
+
});
|
|
1128
|
+
const task = await prisma.task.create({
|
|
1129
|
+
data: {
|
|
1130
|
+
title: 'Investigate slow dashboard',
|
|
1131
|
+
projectId: project.id,
|
|
1132
|
+
},
|
|
1133
|
+
});
|
|
1134
|
+
const preset = await createMemberPreset({ name: 'Leader', triggerPolicy: 'USER_MESSAGES' });
|
|
1135
|
+
const routeScheduler = createRouteSchedulerMock();
|
|
1136
|
+
const app = Fastify({ logger: false });
|
|
1137
|
+
try {
|
|
1138
|
+
await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
|
|
1139
|
+
const response = await app.inject({
|
|
1140
|
+
method: 'POST',
|
|
1141
|
+
url: `/api/tasks/${task.id}/team-runs`,
|
|
1142
|
+
payload: {
|
|
1143
|
+
mode: 'AUTO',
|
|
1144
|
+
memberPresetIds: [preset.id],
|
|
1145
|
+
},
|
|
1146
|
+
});
|
|
1147
|
+
expect(response.statusCode).toBe(201);
|
|
1148
|
+
const teamRun = response.json();
|
|
1149
|
+
expect(teamRun.messages).toHaveLength(1);
|
|
1150
|
+
expect(teamRun.messages[0]).toMatchObject({
|
|
1151
|
+
content: 'Investigate slow dashboard',
|
|
1152
|
+
workRequestIds: [teamRun.workRequests[0].id],
|
|
1153
|
+
});
|
|
1154
|
+
expect(teamRun.workRequests[0]).toMatchObject({
|
|
1155
|
+
instruction: 'Investigate slow dashboard',
|
|
1156
|
+
status: 'QUEUED',
|
|
1157
|
+
});
|
|
1158
|
+
await vi.waitFor(() => {
|
|
1159
|
+
expect(routeScheduler.startNextSessions).toHaveBeenCalledWith(teamRun.id);
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
finally {
|
|
1163
|
+
await app.close();
|
|
1164
|
+
}
|
|
1165
|
+
});
|
|
1166
|
+
it('creates an initial WorkRequest only for a mentioned MENTION_ONLY member', async () => {
|
|
1167
|
+
const project = await prisma.project.create({
|
|
1168
|
+
data: {
|
|
1169
|
+
name: 'Mentioned initial message project',
|
|
1170
|
+
repoPath: testDir,
|
|
1171
|
+
},
|
|
1172
|
+
});
|
|
1173
|
+
const task = await prisma.task.create({
|
|
1174
|
+
data: {
|
|
1175
|
+
title: 'Investigate import failures',
|
|
1176
|
+
description: 'Please @Reviewer check the error handling.',
|
|
1177
|
+
projectId: project.id,
|
|
1178
|
+
},
|
|
1179
|
+
});
|
|
1180
|
+
const implementerPreset = await createMemberPreset({ name: 'Implementer', triggerPolicy: 'MENTION_ONLY' });
|
|
1181
|
+
const reviewerPreset = await createMemberPreset({ name: 'Reviewer', triggerPolicy: 'MENTION_ONLY' });
|
|
1182
|
+
const routeScheduler = createRouteSchedulerMock();
|
|
1183
|
+
const app = Fastify({ logger: false });
|
|
1184
|
+
try {
|
|
1185
|
+
await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
|
|
1186
|
+
const response = await app.inject({
|
|
1187
|
+
method: 'POST',
|
|
1188
|
+
url: `/api/tasks/${task.id}/team-runs`,
|
|
1189
|
+
payload: {
|
|
1190
|
+
mode: 'AUTO',
|
|
1191
|
+
memberPresetIds: [implementerPreset.id, reviewerPreset.id],
|
|
1192
|
+
},
|
|
1193
|
+
});
|
|
1194
|
+
expect(response.statusCode).toBe(201);
|
|
1195
|
+
const teamRun = response.json();
|
|
1196
|
+
const reviewer = teamRun.members.find((member) => member.name === 'Reviewer');
|
|
1197
|
+
const implementer = teamRun.members.find((member) => member.name === 'Implementer');
|
|
1198
|
+
const initialContent = 'Investigate import failures\n\nPlease @Reviewer check the error handling.';
|
|
1199
|
+
expect(reviewer).toBeDefined();
|
|
1200
|
+
expect(implementer).toBeDefined();
|
|
1201
|
+
expect(teamRun.messages).toHaveLength(1);
|
|
1202
|
+
expect(teamRun.messages[0]).toMatchObject({
|
|
1203
|
+
content: initialContent,
|
|
1204
|
+
mentions: [{ memberId: reviewer.id, label: 'Reviewer' }],
|
|
1205
|
+
});
|
|
1206
|
+
expect(teamRun.workRequests).toEqual([
|
|
1207
|
+
expect.objectContaining({
|
|
1208
|
+
id: teamRun.messages[0].workRequestIds[0],
|
|
1209
|
+
targetMemberId: reviewer.id,
|
|
1210
|
+
instruction: initialContent,
|
|
1211
|
+
status: 'QUEUED',
|
|
1212
|
+
}),
|
|
1213
|
+
]);
|
|
1214
|
+
expect(teamRun.workRequests[0].targetMemberId).not.toBe(implementer.id);
|
|
1215
|
+
await vi.waitFor(() => {
|
|
1216
|
+
expect(routeScheduler.startNextSessions).toHaveBeenCalledWith(teamRun.id);
|
|
1217
|
+
});
|
|
1218
|
+
await expect(prisma.agentInvocation.count({ where: { teamRunId: teamRun.id } })).resolves.toBe(1);
|
|
1219
|
+
}
|
|
1220
|
+
finally {
|
|
1221
|
+
await app.close();
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
it('matches initial @mention tokens exactly instead of matching name prefixes', async () => {
|
|
1225
|
+
const project = await prisma.project.create({
|
|
1226
|
+
data: {
|
|
1227
|
+
name: 'Mention prefix project',
|
|
1228
|
+
repoPath: testDir,
|
|
1229
|
+
},
|
|
1230
|
+
});
|
|
1231
|
+
const task = await prisma.task.create({
|
|
1232
|
+
data: {
|
|
1233
|
+
title: 'Route QA review',
|
|
1234
|
+
description: 'Please @QA-Lead verify the release notes.',
|
|
1235
|
+
projectId: project.id,
|
|
1236
|
+
},
|
|
1237
|
+
});
|
|
1238
|
+
const qaPreset = await createMemberPreset({ name: 'QA', triggerPolicy: 'MENTION_ONLY' });
|
|
1239
|
+
const qaLeadPreset = await createMemberPreset({ name: 'QA-Lead', triggerPolicy: 'MENTION_ONLY' });
|
|
1240
|
+
const routeScheduler = createRouteSchedulerMock();
|
|
1241
|
+
const app = Fastify({ logger: false });
|
|
1242
|
+
try {
|
|
1243
|
+
await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
|
|
1244
|
+
const response = await app.inject({
|
|
1245
|
+
method: 'POST',
|
|
1246
|
+
url: `/api/tasks/${task.id}/team-runs`,
|
|
1247
|
+
payload: {
|
|
1248
|
+
mode: 'AUTO',
|
|
1249
|
+
memberPresetIds: [qaPreset.id, qaLeadPreset.id],
|
|
1250
|
+
},
|
|
1251
|
+
});
|
|
1252
|
+
expect(response.statusCode).toBe(201);
|
|
1253
|
+
const teamRun = response.json();
|
|
1254
|
+
const qa = teamRun.members.find((member) => member.name === 'QA');
|
|
1255
|
+
const qaLead = teamRun.members.find((member) => member.name === 'QA-Lead');
|
|
1256
|
+
expect(qa).toBeDefined();
|
|
1257
|
+
expect(qaLead).toBeDefined();
|
|
1258
|
+
expect(teamRun.messages[0].mentions).toEqual([{ memberId: qaLead.id, label: 'QA-Lead' }]);
|
|
1259
|
+
expect(teamRun.workRequests).toEqual([
|
|
1260
|
+
expect.objectContaining({
|
|
1261
|
+
id: teamRun.messages[0].workRequestIds[0],
|
|
1262
|
+
targetMemberId: qaLead.id,
|
|
1263
|
+
}),
|
|
1264
|
+
]);
|
|
1265
|
+
expect(teamRun.workRequests[0].targetMemberId).not.toBe(qa.id);
|
|
1266
|
+
}
|
|
1267
|
+
finally {
|
|
1268
|
+
await app.close();
|
|
1269
|
+
}
|
|
1270
|
+
});
|
|
1271
|
+
it('does not create initial WorkRequests for ambiguous @mention aliases', async () => {
|
|
1272
|
+
const project = await prisma.project.create({
|
|
1273
|
+
data: {
|
|
1274
|
+
name: 'Ambiguous mention project',
|
|
1275
|
+
repoPath: testDir,
|
|
1276
|
+
},
|
|
1277
|
+
});
|
|
1278
|
+
const task = await prisma.task.create({
|
|
1279
|
+
data: {
|
|
1280
|
+
title: 'Review ambiguous owner',
|
|
1281
|
+
description: 'Please @reviewer check this.',
|
|
1282
|
+
projectId: project.id,
|
|
1283
|
+
},
|
|
1284
|
+
});
|
|
1285
|
+
const firstPreset = await createMemberPreset({ name: 'Frontend Reviewer', triggerPolicy: 'MENTION_ONLY' });
|
|
1286
|
+
const secondPreset = await createMemberPreset({ name: 'Backend Reviewer', triggerPolicy: 'MENTION_ONLY' });
|
|
1287
|
+
await prisma.memberPreset.updateMany({
|
|
1288
|
+
where: { id: { in: [firstPreset.id, secondPreset.id] } },
|
|
1289
|
+
data: { aliases: stringifyJson(['reviewer']) },
|
|
1290
|
+
});
|
|
1291
|
+
const routeScheduler = createRouteSchedulerMock();
|
|
1292
|
+
const app = Fastify({ logger: false });
|
|
1293
|
+
try {
|
|
1294
|
+
await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
|
|
1295
|
+
const response = await app.inject({
|
|
1296
|
+
method: 'POST',
|
|
1297
|
+
url: `/api/tasks/${task.id}/team-runs`,
|
|
1298
|
+
payload: {
|
|
1299
|
+
mode: 'AUTO',
|
|
1300
|
+
memberPresetIds: [firstPreset.id, secondPreset.id],
|
|
1301
|
+
},
|
|
1302
|
+
});
|
|
1303
|
+
expect(response.statusCode).toBe(201);
|
|
1304
|
+
const teamRun = response.json();
|
|
1305
|
+
expect(teamRun.messages[0]).toMatchObject({
|
|
1306
|
+
mentions: [],
|
|
1307
|
+
workRequestIds: [],
|
|
1308
|
+
});
|
|
1309
|
+
expect(teamRun.workRequests).toEqual([]);
|
|
1310
|
+
await expect(prisma.workRequest.count({ where: { teamRunId: teamRun.id } })).resolves.toBe(0);
|
|
1311
|
+
expect(routeScheduler.startNextSessions).not.toHaveBeenCalled();
|
|
1312
|
+
}
|
|
1313
|
+
finally {
|
|
1314
|
+
await app.close();
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
it('does not fall back to USER_MESSAGES when initial @mention aliases are ambiguous', async () => {
|
|
1318
|
+
const project = await prisma.project.create({
|
|
1319
|
+
data: {
|
|
1320
|
+
name: 'Mixed ambiguous mention project',
|
|
1321
|
+
repoPath: testDir,
|
|
1322
|
+
},
|
|
1323
|
+
});
|
|
1324
|
+
const task = await prisma.task.create({
|
|
1325
|
+
data: {
|
|
1326
|
+
title: 'Route ambiguous review',
|
|
1327
|
+
description: 'Please @reviewer decide who owns this.',
|
|
1328
|
+
projectId: project.id,
|
|
1329
|
+
},
|
|
1330
|
+
});
|
|
1331
|
+
const firstPreset = await createMemberPreset({ name: 'Frontend Reviewer', triggerPolicy: 'MENTION_ONLY' });
|
|
1332
|
+
const secondPreset = await createMemberPreset({ name: 'Backend Reviewer', triggerPolicy: 'MENTION_ONLY' });
|
|
1333
|
+
const observerPreset = await createMemberPreset({ name: 'Observer', triggerPolicy: 'USER_MESSAGES' });
|
|
1334
|
+
await prisma.memberPreset.updateMany({
|
|
1335
|
+
where: { id: { in: [firstPreset.id, secondPreset.id] } },
|
|
1336
|
+
data: { aliases: stringifyJson(['reviewer']) },
|
|
1337
|
+
});
|
|
1338
|
+
const routeScheduler = createRouteSchedulerMock();
|
|
1339
|
+
const app = Fastify({ logger: false });
|
|
1340
|
+
try {
|
|
1341
|
+
await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
|
|
1342
|
+
const response = await app.inject({
|
|
1343
|
+
method: 'POST',
|
|
1344
|
+
url: `/api/tasks/${task.id}/team-runs`,
|
|
1345
|
+
payload: {
|
|
1346
|
+
mode: 'AUTO',
|
|
1347
|
+
memberPresetIds: [firstPreset.id, secondPreset.id, observerPreset.id],
|
|
1348
|
+
},
|
|
1349
|
+
});
|
|
1350
|
+
expect(response.statusCode).toBe(201);
|
|
1351
|
+
const teamRun = response.json();
|
|
1352
|
+
expect(teamRun.messages[0]).toMatchObject({
|
|
1353
|
+
mentions: [],
|
|
1354
|
+
workRequestIds: [],
|
|
1355
|
+
});
|
|
1356
|
+
expect(teamRun.workRequests).toEqual([]);
|
|
1357
|
+
await expect(prisma.workRequest.count({ where: { teamRunId: teamRun.id } })).resolves.toBe(0);
|
|
1358
|
+
expect(routeScheduler.startNextSessions).not.toHaveBeenCalled();
|
|
1359
|
+
}
|
|
1360
|
+
finally {
|
|
1361
|
+
await app.close();
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
it('rejects creating a TeamRun for a blank-title task before writing TeamRun data', async () => {
|
|
1365
|
+
const project = await prisma.project.create({
|
|
1366
|
+
data: {
|
|
1367
|
+
name: 'Blank title project',
|
|
1368
|
+
repoPath: testDir,
|
|
1369
|
+
},
|
|
1370
|
+
});
|
|
1371
|
+
const task = await prisma.task.create({
|
|
1372
|
+
data: {
|
|
1373
|
+
title: ' ',
|
|
1374
|
+
projectId: project.id,
|
|
1375
|
+
},
|
|
1376
|
+
});
|
|
1377
|
+
const preset = await createMemberPreset({ name: 'Leader', triggerPolicy: 'USER_MESSAGES' });
|
|
1378
|
+
const routeScheduler = createRouteSchedulerMock();
|
|
1379
|
+
const app = Fastify({ logger: false });
|
|
1380
|
+
try {
|
|
1381
|
+
await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
|
|
1382
|
+
const response = await app.inject({
|
|
1383
|
+
method: 'POST',
|
|
1384
|
+
url: `/api/tasks/${task.id}/team-runs`,
|
|
1385
|
+
payload: {
|
|
1386
|
+
mode: 'AUTO',
|
|
1387
|
+
memberPresetIds: [preset.id],
|
|
1388
|
+
},
|
|
1389
|
+
});
|
|
1390
|
+
expect(response.statusCode).toBe(400);
|
|
1391
|
+
expect(response.json()).toMatchObject({ code: 'VALIDATION_ERROR' });
|
|
1392
|
+
expect(routeScheduler.startNextSessions).not.toHaveBeenCalled();
|
|
1393
|
+
await expect(prisma.teamRun.count({ where: { taskId: task.id } })).resolves.toBe(0);
|
|
1394
|
+
await expect(prisma.roomMessage.count()).resolves.toBe(0);
|
|
1395
|
+
await expect(prisma.workRequest.count()).resolves.toBe(0);
|
|
1396
|
+
}
|
|
1397
|
+
finally {
|
|
1398
|
+
await app.close();
|
|
1399
|
+
}
|
|
1400
|
+
});
|
|
1401
|
+
it('rolls back TeamRun creation when initial WorkRequest creation fails and allows retry', async () => {
|
|
1402
|
+
const project = await prisma.project.create({
|
|
1403
|
+
data: {
|
|
1404
|
+
name: 'Atomic TeamRun project',
|
|
1405
|
+
repoPath: testDir,
|
|
1406
|
+
},
|
|
1407
|
+
});
|
|
1408
|
+
const task = await prisma.task.create({
|
|
1409
|
+
data: {
|
|
1410
|
+
title: 'Start atomically',
|
|
1411
|
+
projectId: project.id,
|
|
1412
|
+
},
|
|
1413
|
+
});
|
|
1414
|
+
const preset = await createMemberPreset({ name: 'Implementer', triggerPolicy: 'USER_MESSAGES' });
|
|
1415
|
+
const routeScheduler = createRouteSchedulerMock();
|
|
1416
|
+
const originalTransaction = prisma.$transaction.bind(prisma);
|
|
1417
|
+
const transactionSpy = vi.spyOn(prisma, '$transaction');
|
|
1418
|
+
transactionSpy.mockImplementationOnce(async (arg, ...rest) => {
|
|
1419
|
+
if (typeof arg !== 'function') {
|
|
1420
|
+
return originalTransaction(arg, ...rest);
|
|
1421
|
+
}
|
|
1422
|
+
return originalTransaction(async (tx) => {
|
|
1423
|
+
const failingTx = new Proxy(tx, {
|
|
1424
|
+
get(target, property, receiver) {
|
|
1425
|
+
if (property !== 'workRequest') {
|
|
1426
|
+
return Reflect.get(target, property, receiver);
|
|
1427
|
+
}
|
|
1428
|
+
const delegate = Reflect.get(target, property, receiver);
|
|
1429
|
+
return new Proxy(delegate, {
|
|
1430
|
+
get(delegateTarget, delegateProperty, delegateReceiver) {
|
|
1431
|
+
if (delegateProperty === 'create') {
|
|
1432
|
+
return async () => {
|
|
1433
|
+
throw new Error('injected initial WorkRequest failure');
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
return Reflect.get(delegateTarget, delegateProperty, delegateReceiver);
|
|
1437
|
+
},
|
|
1438
|
+
});
|
|
1439
|
+
},
|
|
1440
|
+
});
|
|
1441
|
+
return arg(failingTx);
|
|
1442
|
+
}, ...rest);
|
|
1443
|
+
});
|
|
1444
|
+
const app = Fastify({ logger: false });
|
|
1445
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined);
|
|
1446
|
+
try {
|
|
1447
|
+
await app.register(teamRunRoutes, { prefix: '/api', scheduler: routeScheduler });
|
|
1448
|
+
const failedResponse = await app.inject({
|
|
1449
|
+
method: 'POST',
|
|
1450
|
+
url: `/api/tasks/${task.id}/team-runs`,
|
|
1451
|
+
payload: {
|
|
1452
|
+
mode: 'AUTO',
|
|
1453
|
+
memberPresetIds: [preset.id],
|
|
1454
|
+
},
|
|
1455
|
+
});
|
|
1456
|
+
expect(failedResponse.statusCode).toBe(500);
|
|
1457
|
+
expect(routeScheduler.startNextSessions).not.toHaveBeenCalled();
|
|
1458
|
+
await expect(prisma.teamRun.count({ where: { taskId: task.id } })).resolves.toBe(0);
|
|
1459
|
+
await expect(prisma.teamMember.count()).resolves.toBe(0);
|
|
1460
|
+
await expect(prisma.roomMessage.count()).resolves.toBe(0);
|
|
1461
|
+
await expect(prisma.workRequest.count()).resolves.toBe(0);
|
|
1462
|
+
const retryResponse = await app.inject({
|
|
1463
|
+
method: 'POST',
|
|
1464
|
+
url: `/api/tasks/${task.id}/team-runs`,
|
|
1465
|
+
payload: {
|
|
1466
|
+
mode: 'AUTO',
|
|
1467
|
+
memberPresetIds: [preset.id],
|
|
1468
|
+
},
|
|
1469
|
+
});
|
|
1470
|
+
expect(retryResponse.statusCode).toBe(201);
|
|
1471
|
+
const teamRun = retryResponse.json();
|
|
1472
|
+
expect(teamRun.messages[0].workRequestIds).toEqual([teamRun.workRequests[0].id]);
|
|
1473
|
+
expect(teamRun.workRequests[0]).toMatchObject({ status: 'QUEUED' });
|
|
1474
|
+
await vi.waitFor(() => {
|
|
1475
|
+
expect(routeScheduler.startNextSessions).toHaveBeenCalledWith(teamRun.id);
|
|
1476
|
+
});
|
|
980
1477
|
}
|
|
981
1478
|
finally {
|
|
1479
|
+
consoleErrorSpy.mockRestore();
|
|
982
1480
|
await app.close();
|
|
983
1481
|
}
|
|
984
1482
|
});
|