agent-tower 0.5.4-beta.1 → 0.5.4-beta.2
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/executors/__tests__/providers-availability.test.js +19 -0
- package/dist/executors/__tests__/providers-availability.test.js.map +1 -1
- package/dist/executors/__tests__/stdin-prompt.executors.test.js +10 -0
- package/dist/executors/__tests__/stdin-prompt.executors.test.js.map +1 -1
- package/dist/executors/claude-code.executor.d.ts.map +1 -1
- package/dist/executors/claude-code.executor.js +18 -5
- package/dist/executors/claude-code.executor.js.map +1 -1
- package/dist/executors/codex.executor.d.ts.map +1 -1
- package/dist/executors/codex.executor.js +9 -3
- package/dist/executors/codex.executor.js.map +1 -1
- package/dist/executors/cursor-agent.executor.d.ts +2 -1
- package/dist/executors/cursor-agent.executor.d.ts.map +1 -1
- package/dist/executors/cursor-agent.executor.js +29 -9
- package/dist/executors/cursor-agent.executor.js.map +1 -1
- package/dist/executors/index.d.ts +1 -0
- package/dist/executors/index.d.ts.map +1 -1
- package/dist/executors/index.js +1 -0
- package/dist/executors/index.js.map +1 -1
- package/dist/executors/start-error.d.ts +13 -0
- package/dist/executors/start-error.d.ts.map +1 -0
- package/dist/executors/start-error.js +25 -0
- package/dist/executors/start-error.js.map +1 -0
- package/dist/routes/__tests__/agent-cli-environment.test.js +28 -10
- package/dist/routes/__tests__/agent-cli-environment.test.js.map +1 -1
- package/dist/routes/system.d.ts.map +1 -1
- package/dist/routes/system.js +17 -11
- package/dist/routes/system.js.map +1 -1
- package/dist/services/__tests__/member-heartbeat-scheduler.test.d.ts +2 -0
- package/dist/services/__tests__/member-heartbeat-scheduler.test.d.ts.map +1 -0
- package/dist/services/__tests__/member-heartbeat-scheduler.test.js +75 -0
- package/dist/services/__tests__/member-heartbeat-scheduler.test.js.map +1 -0
- package/dist/services/__tests__/member-heartbeat.test.js +187 -0
- package/dist/services/__tests__/member-heartbeat.test.js.map +1 -1
- package/dist/services/__tests__/session-manager.team-run.test.js +168 -0
- package/dist/services/__tests__/session-manager.team-run.test.js.map +1 -1
- package/dist/services/__tests__/team-queue-recovery.integration.test.d.ts +2 -0
- package/dist/services/__tests__/team-queue-recovery.integration.test.d.ts.map +1 -0
- package/dist/services/__tests__/team-queue-recovery.integration.test.js +279 -0
- package/dist/services/__tests__/team-queue-recovery.integration.test.js.map +1 -0
- package/dist/services/__tests__/team-scheduler.service.test.js +354 -9
- package/dist/services/__tests__/team-scheduler.service.test.js.map +1 -1
- package/dist/services/__tests__/workspace.service.test.js +88 -1
- package/dist/services/__tests__/workspace.service.test.js.map +1 -1
- package/dist/services/agent-cli/__tests__/command-runner.test.d.ts +2 -0
- package/dist/services/agent-cli/__tests__/command-runner.test.d.ts.map +1 -0
- package/dist/services/agent-cli/__tests__/command-runner.test.js +64 -0
- package/dist/services/agent-cli/__tests__/command-runner.test.js.map +1 -0
- package/dist/services/agent-cli/__tests__/detection.test.js +48 -0
- package/dist/services/agent-cli/__tests__/detection.test.js.map +1 -1
- package/dist/services/agent-cli/__tests__/manifest-downloader.test.js +272 -14
- package/dist/services/agent-cli/__tests__/manifest-downloader.test.js.map +1 -1
- package/dist/services/agent-cli/command-runner.d.ts +21 -0
- package/dist/services/agent-cli/command-runner.d.ts.map +1 -0
- package/dist/services/agent-cli/command-runner.js +83 -0
- package/dist/services/agent-cli/command-runner.js.map +1 -0
- package/dist/services/agent-cli/detection.d.ts +1 -12
- package/dist/services/agent-cli/detection.d.ts.map +1 -1
- package/dist/services/agent-cli/detection.js +5 -27
- package/dist/services/agent-cli/detection.js.map +1 -1
- package/dist/services/agent-cli/downloader.d.ts +2 -2
- package/dist/services/agent-cli/downloader.d.ts.map +1 -1
- package/dist/services/agent-cli/downloader.js +4 -8
- package/dist/services/agent-cli/downloader.js.map +1 -1
- package/dist/services/agent-cli/environment.service.d.ts +2 -1
- package/dist/services/agent-cli/environment.service.d.ts.map +1 -1
- package/dist/services/agent-cli/environment.service.js +25 -12
- package/dist/services/agent-cli/environment.service.js.map +1 -1
- package/dist/services/agent-cli/manifest-validator.d.ts.map +1 -1
- package/dist/services/agent-cli/manifest-validator.js +31 -22
- package/dist/services/agent-cli/manifest-validator.js.map +1 -1
- package/dist/services/agent-cli/manifest.d.ts.map +1 -1
- package/dist/services/agent-cli/manifest.js +97 -56
- package/dist/services/agent-cli/manifest.js.map +1 -1
- package/dist/services/agent-cli/security.d.ts +1 -1
- package/dist/services/agent-cli/security.d.ts.map +1 -1
- package/dist/services/agent-cli/security.js +11 -1
- package/dist/services/agent-cli/security.js.map +1 -1
- package/dist/services/agent-cli/task-manager.d.ts.map +1 -1
- package/dist/services/agent-cli/task-manager.js +8 -19
- package/dist/services/agent-cli/task-manager.js.map +1 -1
- package/dist/services/member-heartbeat-scheduler.d.ts +9 -3
- package/dist/services/member-heartbeat-scheduler.d.ts.map +1 -1
- package/dist/services/member-heartbeat-scheduler.js +24 -10
- package/dist/services/member-heartbeat-scheduler.js.map +1 -1
- package/dist/services/session-manager.d.ts +2 -0
- package/dist/services/session-manager.d.ts.map +1 -1
- package/dist/services/session-manager.js +46 -40
- package/dist/services/session-manager.js.map +1 -1
- package/dist/services/team-reconciler.service.d.ts +11 -1
- package/dist/services/team-reconciler.service.d.ts.map +1 -1
- package/dist/services/team-reconciler.service.js +146 -68
- package/dist/services/team-reconciler.service.js.map +1 -1
- package/dist/services/team-run.service.d.ts.map +1 -1
- package/dist/services/team-run.service.js +1 -0
- package/dist/services/team-run.service.js.map +1 -1
- package/dist/services/team-scheduler.service.d.ts +5 -0
- package/dist/services/team-scheduler.service.d.ts.map +1 -1
- package/dist/services/team-scheduler.service.js +223 -22
- package/dist/services/team-scheduler.service.js.map +1 -1
- package/dist/services/workspace.service.d.ts +1 -0
- package/dist/services/workspace.service.d.ts.map +1 -1
- package/dist/services/workspace.service.js +57 -4
- package/dist/services/workspace.service.js.map +1 -1
- package/dist/utils/index.d.ts +6 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +18 -7
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/process-launch.d.ts +3 -1
- package/dist/utils/process-launch.d.ts.map +1 -1
- package/dist/utils/process-launch.js +41 -5
- package/dist/utils/process-launch.js.map +1 -1
- package/dist/utils/process-launch.test.js +23 -3
- package/dist/utils/process-launch.test.js.map +1 -1
- package/dist/web/assets/{AgentDemoPage-UmBBXiZq.js → AgentDemoPage-CAfZ4j97.js} +1 -1
- package/dist/web/assets/{AgentEnvironmentSettingsPage-DeMVC_O2.js → AgentEnvironmentSettingsPage-DsNEtF9E.js} +1 -1
- package/dist/web/assets/{AgentLogo-sxp-yGLB.js → AgentLogo-jT7FgIek.js} +1 -1
- package/dist/web/assets/{ConversationPage-ykUuKcMG.js → ConversationPage-BoHNGkW4.js} +1 -1
- package/dist/web/assets/{CreateTaskInput-WmNhRnVp.js → CreateTaskInput-Cc5iLMFm.js} +1 -1
- package/dist/web/assets/{DemoPage-Cx-F46PV.js → DemoPage-D7TaL1CJ.js} +1 -1
- package/dist/web/assets/{GeneralSettingsPage-DM4iy6dp.js → GeneralSettingsPage-D5K3E8MX.js} +1 -1
- package/dist/web/assets/{Icons-CO02_BMI.js → Icons-CqiMv6t9.js} +1 -1
- package/dist/web/assets/{LoadingPreviewPage-C58uR8H7.js → LoadingPreviewPage-y_zIMHD7.js} +1 -1
- package/dist/web/assets/{McpSettingsPage-CTe-SNfY.js → McpSettingsPage-Cx-qKB2x.js} +1 -1
- package/dist/web/assets/{MemberAvatar-CZegYpYl.js → MemberAvatar-MDUj0w6B.js} +1 -1
- package/dist/web/assets/{NotificationSettingsPage-D17SAmMK.js → NotificationSettingsPage-9aGt7-Lh.js} +1 -1
- package/dist/web/assets/{ProfileSettingsPage-B2rHV2eU.js → ProfileSettingsPage-Ct5ha4tH.js} +1 -1
- package/dist/web/assets/{ProjectKanbanPage-SpCqpcZ0.js → ProjectKanbanPage-C-opG86F.js} +1 -1
- package/dist/web/assets/{ProjectSettingsPage-2dD_ahm-.js → ProjectSettingsPage-B5ONsxYH.js} +1 -1
- package/dist/web/assets/{ProviderSettingsPage-D9vu3rO0.js → ProviderSettingsPage-B_bvOzI_.js} +1 -1
- package/dist/web/assets/{SettingsMasterDetail-CwRB1WBt.js → SettingsMasterDetail-rDCxJGeO.js} +1 -1
- package/dist/web/assets/{TeamSettingsPage-QXwc1AJE.js → TeamSettingsPage-CQqEaxN8.js} +1 -1
- package/dist/web/assets/{arc-BRCC5BeU.js → arc-4bMzF8s5.js} +1 -1
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-7lTGsSu5.js → architectureDiagram-3BPJPVTR-DHLpZrsg.js} +1 -1
- package/dist/web/assets/{arrow-left-CNgpy5r6.js → arrow-left-CHWUcgUT.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-CnscyQkX.js → blockDiagram-GPEHLZMM-BWnkREjA.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-DDhuPTLZ.js → c4Diagram-AAUBKEIU-Cgz8B5xI.js} +1 -1
- package/dist/web/assets/channel-Cm1ouAHS.js +1 -0
- package/dist/web/assets/check-B0AgpiMM.js +1 -0
- package/dist/web/assets/{chevron-down-CqZBsPeR.js → chevron-down-KH9YdB6d.js} +1 -1
- package/dist/web/assets/{chevron-right-YWciNkox.js → chevron-right-CAUiKY10.js} +1 -1
- package/dist/web/assets/{chevron-up-BJ4i5iAn.js → chevron-up-5_Vvb-Ci.js} +1 -1
- package/dist/web/assets/{chunk-2J33WTMH-B96XJhZG.js → chunk-2J33WTMH-BZ13KXVx.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-IUZUHtbw.js → chunk-4BX2VUAB-1I8GhF4l.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-DcO4xSOO.js → chunk-55IACEB6-XvpeWieM.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-DuQwKkk-.js → chunk-727SXJPM-D7Al9-bq.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-QotCqjAo.js → chunk-AQP2D5EJ-Btn846zU.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-C7hUbfZh.js → chunk-FMBD7UC4-A2nqCnZf.js} +1 -1
- package/dist/web/assets/{chunk-ND2GUHAM-B_C5eChQ.js → chunk-ND2GUHAM-BVkIo6Pj.js} +1 -1
- package/dist/web/assets/{chunk-QZHKN3VN-DRgZY4LJ.js → chunk-QZHKN3VN-BoNYHO2n.js} +1 -1
- package/dist/web/assets/{circle-alert-Ch4pQ3zT.js → circle-alert-fO3Tkh3f.js} +1 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-BfqKofse.js +1 -0
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-BfqKofse.js +1 -0
- package/dist/web/assets/{code-block-OCS4YCEC-h5zD4EvJ.js → code-block-OCS4YCEC-CclNpE_3.js} +1 -1
- package/dist/web/assets/{confirm-dialog-DG0L1v-9.js → confirm-dialog-DvRd9Tcf.js} +1 -1
- package/dist/web/assets/{copy-JHS-5tjr.js → copy-Dyh8NYTA.js} +1 -1
- package/dist/web/assets/{cose-bilkent-S5V4N54A-6Gv8U8Cj.js → cose-bilkent-S5V4N54A-BgA5aQr4.js} +1 -1
- package/dist/web/assets/{dagre-BM42HDAG-Cf4rsmyS.js → dagre-BM42HDAG-C1zMROWX.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-DW3DQ8NA.js → diagram-2AECGRRQ-DkMjxfgh.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-C5cQ_MTX.js → diagram-5GNKFQAL-CxpZDmT0.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-Dcs4NTfn.js → diagram-KO2AKTUF-BtMUfvmN.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-lLI-OolH.js → diagram-LMA3HP47-BZi1ud-6.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-BTdMG1u6.js → diagram-OG6HWLK6-_MZa2Vmf.js} +1 -1
- package/dist/web/assets/{erDiagram-TEJ5UH35-BrKfSMWb.js → erDiagram-TEJ5UH35-DpDg2-r9.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-BFT9EHqo.js → flowDiagram-I6XJVG4X-CLR_X1xm.js} +1 -1
- package/dist/web/assets/{folder-picker--V79noua.js → folder-picker-FcqjOvor.js} +1 -1
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-D_44EJ_y.js → ganttDiagram-6RSMTGT7-ClNXygJc.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-COrCr27e.js → gitGraphDiagram-PVQCEYII-CVdJEdZV.js} +1 -1
- package/dist/web/assets/{index-BC5stA77.js → index-RC-dgDMb.js} +11 -11
- package/dist/web/assets/{index-Cjaho2ST.js → index-gMgAFdpQ.js} +4 -4
- package/dist/web/assets/{infoDiagram-5YYISTIA-Derwhgw-.js → infoDiagram-5YYISTIA-XrlQ9Cnl.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-Dv_SKfKy.js → ishikawaDiagram-YF4QCWOH-I2xf8q1-.js} +1 -1
- package/dist/web/assets/{journeyDiagram-JHISSGLW-BvL0Myto.js → journeyDiagram-JHISSGLW-DMDaSMF5.js} +1 -1
- package/dist/web/assets/{kanban-definition-UN3LZRKU-D9iCCF7g.js → kanban-definition-UN3LZRKU-kVff9awG.js} +1 -1
- package/dist/web/assets/{layers-D-OLv40U.js → layers-UNThcvce.js} +1 -1
- package/dist/web/assets/{linear-DLebIyPZ.js → linear-HJ2-smLG.js} +1 -1
- package/dist/web/assets/{mermaid-NOHMQCX5-CyA4Kmm7.js → mermaid-NOHMQCX5-BFBL5hms.js} +3 -3
- package/dist/web/assets/{message-square-ByVmnBN0.js → message-square-C3yz80ya.js} +1 -1
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-CJXkujXD.js → mindmap-definition-RKZ34NQL-CSkOHlF2.js} +1 -1
- package/dist/web/assets/{modal-KKPLseLk.js → modal-DDKEV5YV.js} +1 -1
- package/dist/web/assets/{pencil-B7ylaB7k.js → pencil-AIclOdlA.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-C8DfXjYu.js → pieDiagram-4H26LBE5-DIk67jqC.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-BxaUhA-J.js → quadrantDiagram-W4KKPZXB-BqRrzRBU.js} +1 -1
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-CkP381y3.js → requirementDiagram-4Y6WPE33-DNo804ZW.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-Ciot-1f1.js → sankeyDiagram-5OEKKPKP-I5HhBnqr.js} +1 -1
- package/dist/web/assets/{select-CLvHC1t4.js → select-BEwAtIzV.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-ZrO6aulq.js → sequenceDiagram-3UESZ5HK-BCmc9m_1.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-BDC3J_xT.js → stateDiagram-AJRCARHV-Dgue8k7_.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-CygUwX8V.js +1 -0
- package/dist/web/assets/{switch-DqXAu8pW.js → switch-CdgTf7ni.js} +1 -1
- package/dist/web/assets/{textarea-ByV2n54w.js → textarea-D6QSwteN.js} +1 -1
- package/dist/web/assets/{timeline-definition-PNZ67QCA-CG898aCW.js → timeline-definition-PNZ67QCA-Bl4mDUDT.js} +1 -1
- package/dist/web/assets/{trash-2-D-MBxpK4.js → trash-2-B47vLMwl.js} +1 -1
- package/dist/web/assets/{upload-CkkorsYk.js → upload-E5fNVeCI.js} +1 -1
- package/dist/web/assets/{use-profiles-B62R9Kbn.js → use-profiles-CDvw0uSu.js} +1 -1
- package/dist/web/assets/{use-projects-JMhtZFd6.js → use-projects-DMxMsQFw.js} +1 -1
- package/dist/web/assets/{use-providers-Cg1RDnUJ.js → use-providers-DBKhBg_2.js} +1 -1
- package/dist/web/assets/{useNormalizedLogs-BMa5-cCl.js → useNormalizedLogs-BM-drJin.js} +1 -1
- package/dist/web/assets/{vennDiagram-CIIHVFJN-CNRNbGoo.js → vennDiagram-CIIHVFJN-rjeTPeto.js} +1 -1
- package/dist/web/assets/{wardley-L42UT6IY-Fm0b_w6O.js → wardley-L42UT6IY-2cunyDSM.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-C_xf1EWg.js → wardleyDiagram-YWT4CUSO-CNxobWk8.js} +1 -1
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-D9yke6dF.js → xychartDiagram-2RQKCTM6-DBVimyeP.js} +1 -1
- package/dist/web/index.html +1 -1
- package/node_modules/@agent-tower/shared/dist/types.d.ts +15 -4
- 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 +8 -5
- package/node_modules/@prisma/client/.prisma/client/index-browser.js +3 -0
- package/node_modules/@prisma/client/.prisma/client/index.d.ts +149 -0
- package/node_modules/@prisma/client/.prisma/client/index.js +8 -5
- package/node_modules/@prisma/client/.prisma/client/package.json +1 -1
- package/node_modules/@prisma/client/.prisma/client/schema.prisma +11 -7
- package/node_modules/@prisma/client/.prisma/client/wasm.js +3 -0
- package/package.json +1 -1
- package/prisma/migrations/20260713000000_add_work_request_start_retry/migration.sql +7 -0
- package/prisma/schema.prisma +4 -0
- package/dist/web/assets/channel-S-3oxkZZ.js +0 -1
- package/dist/web/assets/check-DRdZLPss.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-C00aPgpD.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-C00aPgpD.js +0 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-C_4zupNH.js +0 -1
|
@@ -5,6 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
7
7
|
import { AgentType, TaskStatus } from '../../types/index.js';
|
|
8
|
+
import { ServiceError } from '../../errors.js';
|
|
8
9
|
import { TEAM_ROOM_SYSTEM_SHARED_PROTOCOL } from '../../prompts/team-room-system-shared-protocol.js';
|
|
9
10
|
import { TeamLockService } from '../team-lock.service.js';
|
|
10
11
|
const testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-tower-team-scheduler-'));
|
|
@@ -16,6 +17,9 @@ const serverRoot = path.resolve(__dirname, '../../..');
|
|
|
16
17
|
const schemaPath = path.join(serverRoot, 'prisma/schema.prisma');
|
|
17
18
|
let TeamSchedulerService;
|
|
18
19
|
let prisma;
|
|
20
|
+
let CommandBuildError;
|
|
21
|
+
let ExecutorNotFoundError;
|
|
22
|
+
let normalizeExecutorStartError;
|
|
19
23
|
let workRequestSequence = 0;
|
|
20
24
|
let createdWorkspaceSequence = 0;
|
|
21
25
|
const readOnlyCapabilities = {
|
|
@@ -220,8 +224,13 @@ describe('TeamSchedulerService', () => {
|
|
|
220
224
|
});
|
|
221
225
|
const serviceModule = await import('../team-scheduler.service.js');
|
|
222
226
|
const utilsModule = await import('../../utils/index.js');
|
|
227
|
+
const commandBuilderModule = await import('../../executors/command-builder.js');
|
|
228
|
+
const startErrorModule = await import('../../executors/start-error.js');
|
|
223
229
|
TeamSchedulerService = serviceModule.TeamSchedulerService;
|
|
224
230
|
prisma = utilsModule.prisma;
|
|
231
|
+
CommandBuildError = commandBuilderModule.CommandBuildError;
|
|
232
|
+
ExecutorNotFoundError = startErrorModule.ExecutorNotFoundError;
|
|
233
|
+
normalizeExecutorStartError = startErrorModule.normalizeExecutorStartError;
|
|
225
234
|
});
|
|
226
235
|
beforeEach(async () => {
|
|
227
236
|
vi.restoreAllMocks();
|
|
@@ -443,9 +452,10 @@ describe('TeamSchedulerService', () => {
|
|
|
443
452
|
expect(workspaceService.create).not.toHaveBeenCalled();
|
|
444
453
|
expect(sessionManager.create).not.toHaveBeenCalled();
|
|
445
454
|
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
446
|
-
status: '
|
|
455
|
+
status: 'FAILED',
|
|
456
|
+
lastStartError: expect.stringContaining('MEMBER_NOT_FOUND'),
|
|
447
457
|
});
|
|
448
|
-
await expect(prisma.agentInvocation.count({ where: { teamRunId: teamRun.id } })).resolves.toBe(
|
|
458
|
+
await expect(prisma.agentInvocation.count({ where: { teamRunId: teamRun.id } })).resolves.toBe(1);
|
|
449
459
|
});
|
|
450
460
|
it('rejects approving a WorkRequest for a deleted task', async () => {
|
|
451
461
|
const { task, teamRun, members } = await createTeamRunFixture();
|
|
@@ -1063,7 +1073,8 @@ describe('TeamSchedulerService', () => {
|
|
|
1063
1073
|
workRequestIds: JSON.stringify([request.id]),
|
|
1064
1074
|
});
|
|
1065
1075
|
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
1066
|
-
status: '
|
|
1076
|
+
status: 'FAILED',
|
|
1077
|
+
lastStartError: expect.stringContaining('workspacePolicy=dedicated'),
|
|
1067
1078
|
});
|
|
1068
1079
|
});
|
|
1069
1080
|
it('syncs a targeted dedicated execution workspace to the target commit before starting', async () => {
|
|
@@ -2400,7 +2411,8 @@ describe('TeamSchedulerService', () => {
|
|
|
2400
2411
|
expect(sessionManager.create).toHaveBeenCalledTimes(1);
|
|
2401
2412
|
expect(lockService.listLocks()).toEqual([]);
|
|
2402
2413
|
});
|
|
2403
|
-
it('
|
|
2414
|
+
it('keeps a transient session start failure queued while retaining failed diagnostics', async () => {
|
|
2415
|
+
let now = new Date(Date.UTC(2026, 0, 2, 0, 0, 0));
|
|
2404
2416
|
const { task, teamRun, members } = await createTeamRunFixture({
|
|
2405
2417
|
memberCapabilities: [writeCapabilities],
|
|
2406
2418
|
withWorkspace: false,
|
|
@@ -2410,12 +2422,15 @@ describe('TeamSchedulerService', () => {
|
|
|
2410
2422
|
teamRunId: teamRun.id,
|
|
2411
2423
|
targetMemberId: members[0].id,
|
|
2412
2424
|
});
|
|
2425
|
+
const sessionManager = createSessionManagerMock();
|
|
2426
|
+
sessionManager.start.mockRejectedValueOnce(new Error('session start failed'));
|
|
2413
2427
|
service = new TeamSchedulerService(lockService, {
|
|
2414
2428
|
workspaceService: createWorkspaceServiceMock(),
|
|
2415
|
-
sessionManager
|
|
2429
|
+
sessionManager,
|
|
2416
2430
|
getProviderById: createProviderLookup(),
|
|
2431
|
+
now: () => now,
|
|
2417
2432
|
});
|
|
2418
|
-
await expect(service.startNextSessions(teamRun.id)).
|
|
2433
|
+
await expect(service.startNextSessions(teamRun.id)).resolves.toEqual([]);
|
|
2419
2434
|
expect(lockService.listLocks()).toEqual([]);
|
|
2420
2435
|
const invocation = await prisma.agentInvocation.findFirstOrThrow({
|
|
2421
2436
|
where: { workRequestId: request.id },
|
|
@@ -2429,16 +2444,346 @@ describe('TeamSchedulerService', () => {
|
|
|
2429
2444
|
status: 'FAILED',
|
|
2430
2445
|
});
|
|
2431
2446
|
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2432
|
-
status: '
|
|
2447
|
+
status: 'QUEUED',
|
|
2448
|
+
startAttemptCount: 1,
|
|
2449
|
+
lastStartError: expect.stringContaining('session start failed'),
|
|
2450
|
+
nextStartRetryAt: new Date(now.getTime() + 1_000),
|
|
2433
2451
|
});
|
|
2434
2452
|
await expect(prisma.task.findUnique({ where: { id: task.id } })).resolves.toMatchObject({
|
|
2435
|
-
status: TaskStatus.
|
|
2453
|
+
status: TaskStatus.IN_PROGRESS,
|
|
2436
2454
|
});
|
|
2437
2455
|
await expect(prisma.teamRun.findUnique({ where: { id: teamRun.id } })).resolves.toMatchObject({
|
|
2438
|
-
reviewReason:
|
|
2456
|
+
reviewReason: null,
|
|
2439
2457
|
});
|
|
2440
2458
|
await expect(service.startNextSessions(teamRun.id)).resolves.toEqual([]);
|
|
2441
2459
|
await expect(prisma.agentInvocation.count({ where: { workRequestId: request.id } })).resolves.toBe(1);
|
|
2460
|
+
now = new Date(now.getTime() + 1_000);
|
|
2461
|
+
await expect(service.reconcileQueuedWork()).resolves.toBe(1);
|
|
2462
|
+
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2463
|
+
status: 'STARTED',
|
|
2464
|
+
lastStartError: null,
|
|
2465
|
+
nextStartRetryAt: null,
|
|
2466
|
+
});
|
|
2467
|
+
await expect(prisma.agentInvocation.count({ where: { workRequestId: request.id } })).resolves.toBe(2);
|
|
2468
|
+
});
|
|
2469
|
+
it('does not requeue a real CommandBuildError normalized by the session boundary', async () => {
|
|
2470
|
+
const { teamRun, members } = await createTeamRunFixture({ withWorkspace: false });
|
|
2471
|
+
const request = await createWorkRequest({
|
|
2472
|
+
teamRunId: teamRun.id,
|
|
2473
|
+
targetMemberId: members[0].id,
|
|
2474
|
+
});
|
|
2475
|
+
const sessionManager = createSessionManagerMock();
|
|
2476
|
+
sessionManager.start.mockRejectedValueOnce(normalizeExecutorStartError(new CommandBuildError("Executable 'codex' not found in PATH")));
|
|
2477
|
+
service = new TeamSchedulerService(lockService, {
|
|
2478
|
+
workspaceService: createWorkspaceServiceMock(),
|
|
2479
|
+
sessionManager,
|
|
2480
|
+
getProviderById: createProviderLookup(),
|
|
2481
|
+
});
|
|
2482
|
+
await expect(service.startNextSessions(teamRun.id)).resolves.toEqual([]);
|
|
2483
|
+
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2484
|
+
status: 'FAILED',
|
|
2485
|
+
startAttemptCount: 1,
|
|
2486
|
+
lastStartError: expect.stringContaining('AGENT_COMMAND_UNAVAILABLE'),
|
|
2487
|
+
nextStartRetryAt: null,
|
|
2488
|
+
});
|
|
2489
|
+
await expect(prisma.agentInvocation.findFirst({ where: { workRequestId: request.id } })).resolves.toMatchObject({
|
|
2490
|
+
status: 'FAILED',
|
|
2491
|
+
sessionId: expect.any(String),
|
|
2492
|
+
});
|
|
2493
|
+
});
|
|
2494
|
+
it('does not requeue an executor-not-found start error', async () => {
|
|
2495
|
+
const { teamRun, members } = await createTeamRunFixture({ withWorkspace: false });
|
|
2496
|
+
const request = await createWorkRequest({
|
|
2497
|
+
teamRunId: teamRun.id,
|
|
2498
|
+
targetMemberId: members[0].id,
|
|
2499
|
+
});
|
|
2500
|
+
const sessionManager = createSessionManagerMock();
|
|
2501
|
+
sessionManager.start.mockRejectedValueOnce(new ExecutorNotFoundError(AgentType.CODEX, members[0].providerId));
|
|
2502
|
+
service = new TeamSchedulerService(lockService, {
|
|
2503
|
+
workspaceService: createWorkspaceServiceMock(),
|
|
2504
|
+
sessionManager,
|
|
2505
|
+
getProviderById: createProviderLookup(),
|
|
2506
|
+
});
|
|
2507
|
+
await expect(service.startNextSessions(teamRun.id)).resolves.toEqual([]);
|
|
2508
|
+
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2509
|
+
status: 'FAILED',
|
|
2510
|
+
startAttemptCount: 1,
|
|
2511
|
+
lastStartError: expect.stringContaining('EXECUTOR_NOT_FOUND'),
|
|
2512
|
+
nextStartRetryAt: null,
|
|
2513
|
+
});
|
|
2514
|
+
await expect(prisma.agentInvocation.findFirst({ where: { workRequestId: request.id } })).resolves.toMatchObject({
|
|
2515
|
+
status: 'FAILED',
|
|
2516
|
+
sessionId: expect.any(String),
|
|
2517
|
+
});
|
|
2518
|
+
});
|
|
2519
|
+
it('fails a deterministic poisoned queue head without blocking another member', async () => {
|
|
2520
|
+
const { teamRun, workspace, members } = await createTeamRunFixture({
|
|
2521
|
+
memberCapabilities: [writeCapabilities, writeCapabilities],
|
|
2522
|
+
workspacePolicies: ['dedicated', 'dedicated'],
|
|
2523
|
+
});
|
|
2524
|
+
const poisoned = await createWorkRequest({
|
|
2525
|
+
teamRunId: teamRun.id,
|
|
2526
|
+
targetMemberId: members[0].id,
|
|
2527
|
+
});
|
|
2528
|
+
const healthy = await createWorkRequest({
|
|
2529
|
+
teamRunId: teamRun.id,
|
|
2530
|
+
targetMemberId: members[1].id,
|
|
2531
|
+
});
|
|
2532
|
+
const workspaceService = {
|
|
2533
|
+
create: vi.fn(),
|
|
2534
|
+
getOrCreateDedicatedWorkspace: vi.fn(async (_teamRunId, memberId) => {
|
|
2535
|
+
if (memberId === members[0].id) {
|
|
2536
|
+
throw new ServiceError('merged workspace is dirty', 'MERGED_WORKSPACE_DIRTY', 409);
|
|
2537
|
+
}
|
|
2538
|
+
return workspace;
|
|
2539
|
+
}),
|
|
2540
|
+
};
|
|
2541
|
+
service = new TeamSchedulerService(lockService, {
|
|
2542
|
+
workspaceService,
|
|
2543
|
+
sessionManager: createSessionManagerMock(),
|
|
2544
|
+
getProviderById: createProviderLookup(),
|
|
2545
|
+
});
|
|
2546
|
+
const started = await service.startNextSessions(teamRun.id);
|
|
2547
|
+
expect(started).toHaveLength(1);
|
|
2548
|
+
expect(started[0]).toMatchObject({ workRequestId: healthy.id, memberId: members[1].id });
|
|
2549
|
+
await expect(prisma.workRequest.findUnique({ where: { id: poisoned.id } })).resolves.toMatchObject({
|
|
2550
|
+
status: 'FAILED',
|
|
2551
|
+
startAttemptCount: 1,
|
|
2552
|
+
lastStartError: expect.stringContaining('MERGED_WORKSPACE_DIRTY'),
|
|
2553
|
+
nextStartRetryAt: null,
|
|
2554
|
+
});
|
|
2555
|
+
await expect(prisma.agentInvocation.findFirst({ where: { workRequestId: poisoned.id } })).resolves.toMatchObject({
|
|
2556
|
+
status: 'FAILED',
|
|
2557
|
+
sessionId: null,
|
|
2558
|
+
});
|
|
2559
|
+
});
|
|
2560
|
+
it('backs off a transient queue head without blocking another member', async () => {
|
|
2561
|
+
const now = new Date(Date.UTC(2026, 0, 2, 0, 0, 0));
|
|
2562
|
+
const { teamRun, workspace, members } = await createTeamRunFixture({
|
|
2563
|
+
memberCapabilities: [writeCapabilities, writeCapabilities],
|
|
2564
|
+
workspacePolicies: ['dedicated', 'dedicated'],
|
|
2565
|
+
});
|
|
2566
|
+
const retrying = await createWorkRequest({ teamRunId: teamRun.id, targetMemberId: members[0].id });
|
|
2567
|
+
const healthy = await createWorkRequest({ teamRunId: teamRun.id, targetMemberId: members[1].id });
|
|
2568
|
+
const workspaceService = {
|
|
2569
|
+
create: vi.fn(),
|
|
2570
|
+
getOrCreateDedicatedWorkspace: vi.fn(async (_teamRunId, memberId) => {
|
|
2571
|
+
if (memberId === members[0].id) {
|
|
2572
|
+
throw new Error('temporary filesystem failure');
|
|
2573
|
+
}
|
|
2574
|
+
return workspace;
|
|
2575
|
+
}),
|
|
2576
|
+
};
|
|
2577
|
+
service = new TeamSchedulerService(lockService, {
|
|
2578
|
+
workspaceService,
|
|
2579
|
+
sessionManager: createSessionManagerMock(),
|
|
2580
|
+
getProviderById: createProviderLookup(),
|
|
2581
|
+
now: () => now,
|
|
2582
|
+
});
|
|
2583
|
+
const started = await service.startNextSessions(teamRun.id);
|
|
2584
|
+
expect(started).toHaveLength(1);
|
|
2585
|
+
expect(started[0].workRequestId).toBe(healthy.id);
|
|
2586
|
+
await expect(prisma.workRequest.findUnique({ where: { id: retrying.id } })).resolves.toMatchObject({
|
|
2587
|
+
status: 'QUEUED',
|
|
2588
|
+
startAttemptCount: 1,
|
|
2589
|
+
lastStartError: expect.stringContaining('temporary filesystem failure'),
|
|
2590
|
+
nextStartRetryAt: new Date(now.getTime() + 1_000),
|
|
2591
|
+
});
|
|
2592
|
+
await expect(prisma.agentInvocation.count({ where: { workRequestId: retrying.id } })).resolves.toBe(0);
|
|
2593
|
+
});
|
|
2594
|
+
it('backs off a transient targeted workspace sync failure', async () => {
|
|
2595
|
+
let now = new Date(Date.UTC(2026, 0, 2, 0, 0, 0));
|
|
2596
|
+
const { teamRun, workspace, members } = await createTeamRunFixture({
|
|
2597
|
+
memberCapabilities: [commandCapabilities],
|
|
2598
|
+
workspacePolicies: ['dedicated'],
|
|
2599
|
+
});
|
|
2600
|
+
const request = await createWorkRequest({
|
|
2601
|
+
teamRunId: teamRun.id,
|
|
2602
|
+
targetMemberId: members[0].id,
|
|
2603
|
+
target: {
|
|
2604
|
+
targetKind: 'WORKSPACE_COMMIT',
|
|
2605
|
+
targetPurpose: 'REVIEW',
|
|
2606
|
+
targetSourceWorkspaceId: workspace.id,
|
|
2607
|
+
targetHeadSha: 'a'.repeat(40),
|
|
2608
|
+
targetBranchName: workspace.branchName,
|
|
2609
|
+
},
|
|
2610
|
+
});
|
|
2611
|
+
const sessionManager = createSessionManagerMock();
|
|
2612
|
+
const prepareTargetedExecutionWorkspace = vi.fn(async () => ({ executionBranch: 'target-retry-branch' }));
|
|
2613
|
+
prepareTargetedExecutionWorkspace.mockRejectedValueOnce(new Error('temporary target sync failure'));
|
|
2614
|
+
service = new TeamSchedulerService(lockService, {
|
|
2615
|
+
workspaceService: {
|
|
2616
|
+
create: vi.fn(),
|
|
2617
|
+
getOrCreateDedicatedWorkspace: vi.fn(async () => workspace),
|
|
2618
|
+
prepareTargetedExecutionWorkspace,
|
|
2619
|
+
},
|
|
2620
|
+
sessionManager,
|
|
2621
|
+
getProviderById: createProviderLookup(),
|
|
2622
|
+
now: () => now,
|
|
2623
|
+
});
|
|
2624
|
+
await expect(service.startNextSessions(teamRun.id)).resolves.toEqual([]);
|
|
2625
|
+
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2626
|
+
status: 'QUEUED',
|
|
2627
|
+
startAttemptCount: 1,
|
|
2628
|
+
lastStartError: expect.stringContaining('temporary target sync failure'),
|
|
2629
|
+
nextStartRetryAt: new Date(now.getTime() + 1_000),
|
|
2630
|
+
});
|
|
2631
|
+
await expect(prisma.agentInvocation.count({ where: { workRequestId: request.id } })).resolves.toBe(0);
|
|
2632
|
+
expect(sessionManager.create).not.toHaveBeenCalled();
|
|
2633
|
+
expect(lockService.listLocks()).toEqual([]);
|
|
2634
|
+
now = new Date(now.getTime() + 1_000);
|
|
2635
|
+
await expect(service.reconcileQueuedWork()).resolves.toBe(1);
|
|
2636
|
+
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2637
|
+
status: 'STARTED',
|
|
2638
|
+
lastStartError: null,
|
|
2639
|
+
nextStartRetryAt: null,
|
|
2640
|
+
});
|
|
2641
|
+
expect(prepareTargetedExecutionWorkspace).toHaveBeenCalledTimes(2);
|
|
2642
|
+
});
|
|
2643
|
+
it('retries due naked AUTO work', async () => {
|
|
2644
|
+
let now = new Date(Date.UTC(2026, 0, 2, 0, 0, 0));
|
|
2645
|
+
const { teamRun, members } = await createTeamRunFixture({ withWorkspace: false });
|
|
2646
|
+
const request = await createWorkRequest({ teamRunId: teamRun.id, targetMemberId: members[0].id });
|
|
2647
|
+
let createAttempts = 0;
|
|
2648
|
+
const workspaceService = createWorkspaceServiceMock();
|
|
2649
|
+
workspaceService.create.mockImplementation(async (taskId) => {
|
|
2650
|
+
createAttempts++;
|
|
2651
|
+
if (createAttempts === 1) {
|
|
2652
|
+
throw new Error('temporary workspace failure');
|
|
2653
|
+
}
|
|
2654
|
+
const sequence = createdWorkspaceSequence++;
|
|
2655
|
+
return prisma.workspace.create({
|
|
2656
|
+
data: {
|
|
2657
|
+
taskId,
|
|
2658
|
+
branchName: `retry-workspace-${sequence}`,
|
|
2659
|
+
worktreePath: path.join(testDir, `retry-workspace-${sequence}`),
|
|
2660
|
+
status: 'ACTIVE',
|
|
2661
|
+
},
|
|
2662
|
+
});
|
|
2663
|
+
});
|
|
2664
|
+
service = new TeamSchedulerService(lockService, {
|
|
2665
|
+
workspaceService,
|
|
2666
|
+
sessionManager: createSessionManagerMock(),
|
|
2667
|
+
getProviderById: createProviderLookup(),
|
|
2668
|
+
now: () => now,
|
|
2669
|
+
});
|
|
2670
|
+
await expect(service.startNextSessions(teamRun.id)).resolves.toEqual([]);
|
|
2671
|
+
await expect(service.reconcileQueuedWork()).resolves.toBe(0);
|
|
2672
|
+
now = new Date(now.getTime() + 1_000);
|
|
2673
|
+
await expect(service.reconcileQueuedWork()).resolves.toBe(1);
|
|
2674
|
+
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2675
|
+
status: 'STARTED',
|
|
2676
|
+
lastStartError: null,
|
|
2677
|
+
nextStartRetryAt: null,
|
|
2678
|
+
});
|
|
2679
|
+
});
|
|
2680
|
+
it('retries approved CONFIRM work but never starts PENDING_APPROVAL requests', async () => {
|
|
2681
|
+
let now = new Date(Date.UTC(2026, 0, 2, 0, 0, 0));
|
|
2682
|
+
const { teamRun, members } = await createTeamRunFixture({ withWorkspace: false });
|
|
2683
|
+
await prisma.teamRun.update({ where: { id: teamRun.id }, data: { mode: 'CONFIRM' } });
|
|
2684
|
+
const request = await createWorkRequest({
|
|
2685
|
+
teamRunId: teamRun.id,
|
|
2686
|
+
targetMemberId: members[0].id,
|
|
2687
|
+
status: 'PENDING_APPROVAL',
|
|
2688
|
+
});
|
|
2689
|
+
let createAttempts = 0;
|
|
2690
|
+
const workspaceService = createWorkspaceServiceMock();
|
|
2691
|
+
workspaceService.create.mockImplementation(async (taskId) => {
|
|
2692
|
+
createAttempts++;
|
|
2693
|
+
if (createAttempts === 1) {
|
|
2694
|
+
throw new Error('temporary approved workspace failure');
|
|
2695
|
+
}
|
|
2696
|
+
const sequence = createdWorkspaceSequence++;
|
|
2697
|
+
return prisma.workspace.create({
|
|
2698
|
+
data: {
|
|
2699
|
+
taskId,
|
|
2700
|
+
branchName: `confirm-retry-${sequence}`,
|
|
2701
|
+
worktreePath: path.join(testDir, `confirm-retry-${sequence}`),
|
|
2702
|
+
status: 'ACTIVE',
|
|
2703
|
+
},
|
|
2704
|
+
});
|
|
2705
|
+
});
|
|
2706
|
+
service = new TeamSchedulerService(lockService, {
|
|
2707
|
+
workspaceService,
|
|
2708
|
+
sessionManager: createSessionManagerMock(),
|
|
2709
|
+
getProviderById: createProviderLookup(),
|
|
2710
|
+
now: () => now,
|
|
2711
|
+
});
|
|
2712
|
+
await expect(service.reconcileQueuedWork()).resolves.toBe(0);
|
|
2713
|
+
expect(workspaceService.create).not.toHaveBeenCalled();
|
|
2714
|
+
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2715
|
+
status: 'PENDING_APPROVAL',
|
|
2716
|
+
});
|
|
2717
|
+
const approved = await service.approveWorkRequestAndStartNext(request.id);
|
|
2718
|
+
expect(approved.startedInvocations).toEqual([]);
|
|
2719
|
+
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2720
|
+
status: 'QUEUED',
|
|
2721
|
+
startAttemptCount: 1,
|
|
2722
|
+
nextStartRetryAt: new Date(now.getTime() + 1_000),
|
|
2723
|
+
});
|
|
2724
|
+
await expect(service.reconcileQueuedWork()).resolves.toBe(0);
|
|
2725
|
+
now = new Date(now.getTime() + 1_000);
|
|
2726
|
+
await expect(service.reconcileQueuedWork()).resolves.toBe(1);
|
|
2727
|
+
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2728
|
+
status: 'STARTED',
|
|
2729
|
+
lastStartError: null,
|
|
2730
|
+
nextStartRetryAt: null,
|
|
2731
|
+
});
|
|
2732
|
+
});
|
|
2733
|
+
it('keeps concurrent AUTO queue pumps idempotent', async () => {
|
|
2734
|
+
const { teamRun, members } = await createTeamRunFixture({ withWorkspace: false });
|
|
2735
|
+
const request = await createWorkRequest({ teamRunId: teamRun.id, targetMemberId: members[0].id });
|
|
2736
|
+
const workspaceService = createWorkspaceServiceMock();
|
|
2737
|
+
const sessionManager = createSessionManagerMock();
|
|
2738
|
+
const firstService = new TeamSchedulerService(lockService, {
|
|
2739
|
+
workspaceService,
|
|
2740
|
+
sessionManager,
|
|
2741
|
+
getProviderById: createProviderLookup(),
|
|
2742
|
+
});
|
|
2743
|
+
const secondService = new TeamSchedulerService(lockService, {
|
|
2744
|
+
workspaceService,
|
|
2745
|
+
sessionManager,
|
|
2746
|
+
getProviderById: createProviderLookup(),
|
|
2747
|
+
});
|
|
2748
|
+
const counts = await Promise.all([
|
|
2749
|
+
firstService.reconcileQueuedWork(),
|
|
2750
|
+
secondService.reconcileQueuedWork(),
|
|
2751
|
+
]);
|
|
2752
|
+
expect(counts.reduce((sum, count) => sum + count, 0)).toBe(1);
|
|
2753
|
+
await expect(prisma.agentInvocation.count({ where: { workRequestId: request.id } })).resolves.toBe(1);
|
|
2754
|
+
await expect(prisma.session.count()).resolves.toBe(1);
|
|
2755
|
+
});
|
|
2756
|
+
it('scans beyond a busy first page without starving later TeamRuns', async () => {
|
|
2757
|
+
const fixtures = await Promise.all(Array.from({ length: 51 }, () => (createTeamRunFixture({ withWorkspace: false }))));
|
|
2758
|
+
const ordered = [...fixtures].sort((left, right) => left.teamRun.id.localeCompare(right.teamRun.id));
|
|
2759
|
+
const requests = new Map();
|
|
2760
|
+
for (const fixture of ordered) {
|
|
2761
|
+
const request = await createWorkRequest({
|
|
2762
|
+
teamRunId: fixture.teamRun.id,
|
|
2763
|
+
targetMemberId: fixture.members[0].id,
|
|
2764
|
+
});
|
|
2765
|
+
requests.set(fixture.teamRun.id, request);
|
|
2766
|
+
}
|
|
2767
|
+
for (const fixture of ordered.slice(0, 50)) {
|
|
2768
|
+
await prisma.agentInvocation.create({
|
|
2769
|
+
data: {
|
|
2770
|
+
teamRunId: fixture.teamRun.id,
|
|
2771
|
+
workRequestId: `busy-${fixture.teamRun.id}`,
|
|
2772
|
+
memberId: fixture.members[0].id,
|
|
2773
|
+
status: 'RUNNING',
|
|
2774
|
+
},
|
|
2775
|
+
});
|
|
2776
|
+
}
|
|
2777
|
+
service = new TeamSchedulerService(lockService, {
|
|
2778
|
+
workspaceService: createWorkspaceServiceMock(),
|
|
2779
|
+
sessionManager: createSessionManagerMock(),
|
|
2780
|
+
getProviderById: createProviderLookup(),
|
|
2781
|
+
});
|
|
2782
|
+
await expect(service.reconcileQueuedWork(50)).resolves.toBe(1);
|
|
2783
|
+
const last = ordered[50];
|
|
2784
|
+
await expect(prisma.workRequest.findUnique({
|
|
2785
|
+
where: { id: requests.get(last.teamRun.id).id },
|
|
2786
|
+
})).resolves.toMatchObject({ status: 'STARTED' });
|
|
2442
2787
|
});
|
|
2443
2788
|
});
|
|
2444
2789
|
//# sourceMappingURL=team-scheduler.service.test.js.map
|