agent-tower 0.5.4-beta.0 → 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/README.md +6 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +15 -1
- package/dist/app.js.map +1 -1
- package/dist/app.test.js +12 -0
- package/dist/app.test.js.map +1 -1
- package/dist/cli.js +18 -1
- package/dist/cli.js.map +1 -1
- package/dist/core/event-bus.d.ts +3 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/executors/__tests__/base.executor.test.js +29 -1
- package/dist/executors/__tests__/base.executor.test.js.map +1 -1
- package/dist/executors/__tests__/codex.executor.test.js +13 -0
- package/dist/executors/__tests__/codex.executor.test.js.map +1 -1
- 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/base.executor.d.ts.map +1 -1
- package/dist/executors/base.executor.js +4 -0
- package/dist/executors/base.executor.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/execution-env.d.ts +1 -1
- package/dist/executors/execution-env.d.ts.map +1 -1
- package/dist/executors/execution-env.js +7 -0
- package/dist/executors/execution-env.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/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/http-client.d.ts +2 -0
- package/dist/mcp/http-client.d.ts.map +1 -1
- package/dist/mcp/http-client.js +7 -0
- package/dist/mcp/http-client.js.map +1 -1
- package/dist/mcp/index.js +4 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/server.d.ts +3 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +2 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/middleware/__tests__/access-auth.test.d.ts +2 -0
- package/dist/middleware/__tests__/access-auth.test.d.ts.map +1 -0
- package/dist/middleware/__tests__/access-auth.test.js +249 -0
- package/dist/middleware/__tests__/access-auth.test.js.map +1 -0
- package/dist/middleware/access-auth.d.ts +3 -0
- package/dist/middleware/access-auth.d.ts.map +1 -0
- package/dist/middleware/access-auth.js +110 -0
- package/dist/middleware/access-auth.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/__tests__/files.test.d.ts +2 -0
- package/dist/routes/__tests__/files.test.d.ts.map +1 -0
- package/dist/routes/__tests__/files.test.js +101 -0
- package/dist/routes/__tests__/files.test.js.map +1 -0
- package/dist/routes/__tests__/previews.integration.test.d.ts +2 -0
- package/dist/routes/__tests__/previews.integration.test.d.ts.map +1 -0
- package/dist/routes/__tests__/previews.integration.test.js +269 -0
- package/dist/routes/__tests__/previews.integration.test.js.map +1 -0
- package/dist/routes/__tests__/previews.test.js +103 -1
- package/dist/routes/__tests__/previews.test.js.map +1 -1
- package/dist/routes/access-auth.d.ts +3 -0
- package/dist/routes/access-auth.d.ts.map +1 -0
- package/dist/routes/access-auth.js +89 -0
- package/dist/routes/access-auth.js.map +1 -0
- package/dist/routes/files.d.ts.map +1 -1
- package/dist/routes/files.js +46 -0
- package/dist/routes/files.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 +7 -2
- package/dist/routes/previews.d.ts.map +1 -1
- package/dist/routes/previews.js +106 -32
- package/dist/routes/previews.js.map +1 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +9 -0
- package/dist/routes/projects.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__/access-auth.service.test.d.ts +2 -0
- package/dist/services/__tests__/access-auth.service.test.d.ts.map +1 -0
- package/dist/services/__tests__/access-auth.service.test.js +316 -0
- package/dist/services/__tests__/access-auth.service.test.js.map +1 -0
- 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.d.ts +2 -0
- package/dist/services/__tests__/member-heartbeat.test.d.ts.map +1 -0
- package/dist/services/__tests__/member-heartbeat.test.js +531 -0
- package/dist/services/__tests__/member-heartbeat.test.js.map +1 -0
- package/dist/services/__tests__/project.service.test.js +29 -0
- package/dist/services/__tests__/project.service.test.js.map +1 -1
- package/dist/services/__tests__/session-manager.team-run.test.js +237 -0
- package/dist/services/__tests__/session-manager.team-run.test.js.map +1 -1
- package/dist/services/__tests__/task.service.test.js +34 -0
- package/dist/services/__tests__/task.service.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-reconciler.service.test.js +180 -3
- package/dist/services/__tests__/team-reconciler.service.test.js.map +1 -1
- package/dist/services/__tests__/team-run.service.test.js +196 -2
- package/dist/services/__tests__/team-run.service.test.js.map +1 -1
- package/dist/services/__tests__/team-scheduler.service.test.js +362 -10
- package/dist/services/__tests__/team-scheduler.service.test.js.map +1 -1
- package/dist/services/__tests__/workspace.service.test.js +120 -1
- package/dist/services/__tests__/workspace.service.test.js.map +1 -1
- package/dist/services/access-auth.service.d.ts +82 -0
- package/dist/services/access-auth.service.d.ts.map +1 -0
- package/dist/services/access-auth.service.js +385 -0
- package/dist/services/access-auth.service.js.map +1 -0
- 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/mcp-config.service.d.ts.map +1 -1
- package/dist/services/mcp-config.service.js +8 -2
- package/dist/services/mcp-config.service.js.map +1 -1
- package/dist/services/mcp-config.service.test.js +18 -3
- package/dist/services/mcp-config.service.test.js.map +1 -1
- package/dist/services/member-heartbeat-scheduler.d.ts +39 -0
- package/dist/services/member-heartbeat-scheduler.d.ts.map +1 -0
- package/dist/services/member-heartbeat-scheduler.js +97 -0
- package/dist/services/member-heartbeat-scheduler.js.map +1 -0
- package/dist/services/project-guards.d.ts +5 -0
- package/dist/services/project-guards.d.ts.map +1 -1
- package/dist/services/project-guards.js +47 -0
- package/dist/services/project-guards.js.map +1 -1
- package/dist/services/project.service.d.ts +6 -4
- package/dist/services/project.service.d.ts.map +1 -1
- package/dist/services/project.service.js +16 -7
- package/dist/services/project.service.js.map +1 -1
- package/dist/services/session-manager.d.ts +13 -0
- package/dist/services/session-manager.d.ts.map +1 -1
- package/dist/services/session-manager.js +106 -40
- package/dist/services/session-manager.js.map +1 -1
- package/dist/services/task.service.d.ts +11 -9
- package/dist/services/task.service.d.ts.map +1 -1
- package/dist/services/task.service.js +17 -12
- package/dist/services/task.service.js.map +1 -1
- package/dist/services/team-reconciler.service.d.ts +49 -0
- package/dist/services/team-reconciler.service.d.ts.map +1 -1
- package/dist/services/team-reconciler.service.js +313 -4
- package/dist/services/team-reconciler.service.js.map +1 -1
- package/dist/services/team-run.service.d.ts +2 -0
- package/dist/services/team-run.service.d.ts.map +1 -1
- package/dist/services/team-run.service.js +36 -7
- 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 +237 -21
- package/dist/services/team-scheduler.service.js.map +1 -1
- package/dist/services/terminal-manager.d.ts.map +1 -1
- package/dist/services/terminal-manager.js +1 -0
- package/dist/services/terminal-manager.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 +62 -9
- package/dist/services/workspace.service.js.map +1 -1
- package/dist/socket/__tests__/socket-gateway.test.d.ts +2 -0
- package/dist/socket/__tests__/socket-gateway.test.d.ts.map +1 -0
- package/dist/socket/__tests__/socket-gateway.test.js +119 -0
- package/dist/socket/__tests__/socket-gateway.test.js.map +1 -0
- package/dist/socket/middleware/__tests__/auth.test.js +102 -2
- package/dist/socket/middleware/__tests__/auth.test.js.map +1 -1
- package/dist/socket/middleware/auth.d.ts +1 -0
- package/dist/socket/middleware/auth.d.ts.map +1 -1
- package/dist/socket/middleware/auth.js +22 -2
- package/dist/socket/middleware/auth.js.map +1 -1
- package/dist/socket/socket-gateway.d.ts +6 -2
- package/dist/socket/socket-gateway.d.ts.map +1 -1
- package/dist/socket/socket-gateway.js +78 -15
- package/dist/socket/socket-gateway.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/internal-api-token.d.ts +9 -0
- package/dist/utils/internal-api-token.d.ts.map +1 -0
- package/dist/utils/internal-api-token.js +74 -0
- package/dist/utils/internal-api-token.js.map +1 -0
- package/dist/utils/preview-path.d.ts +11 -0
- package/dist/utils/preview-path.d.ts.map +1 -0
- package/dist/utils/preview-path.js +46 -0
- package/dist/utils/preview-path.js.map +1 -0
- 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 +37 -3
- package/dist/utils/process-launch.test.js.map +1 -1
- package/dist/web/assets/{AgentDemoPage-DMLw4xpg.js → AgentDemoPage-CAfZ4j97.js} +1 -1
- package/dist/web/assets/{AgentEnvironmentSettingsPage-NgRdouLu.js → AgentEnvironmentSettingsPage-DsNEtF9E.js} +1 -1
- package/dist/web/assets/{AgentLogo-ThhvSi7G.js → AgentLogo-jT7FgIek.js} +1 -1
- package/dist/web/assets/{ConversationPage-B1ZcyF_h.js → ConversationPage-BoHNGkW4.js} +2 -2
- package/dist/web/assets/CreateTaskInput-Cc5iLMFm.js +1 -0
- package/dist/web/assets/{DemoPage-CN2OBKzu.js → DemoPage-D7TaL1CJ.js} +3 -3
- package/dist/web/assets/GeneralSettingsPage-D5K3E8MX.js +1 -0
- package/dist/web/assets/{Icons-DIZBOtuC.js → Icons-CqiMv6t9.js} +1 -1
- package/dist/web/assets/{LoadingPreviewPage-CRBcypeY.js → LoadingPreviewPage-y_zIMHD7.js} +1 -1
- package/dist/web/assets/McpSettingsPage-Cx-qKB2x.js +1 -0
- package/dist/web/assets/{MemberAvatar-Y_pJlw-N.js → MemberAvatar-MDUj0w6B.js} +1 -1
- package/dist/web/assets/NotificationSettingsPage-9aGt7-Lh.js +1 -0
- package/dist/web/assets/ProfileSettingsPage-Ct5ha4tH.js +3 -0
- package/dist/web/assets/{ProjectKanbanPage-CBfocvKp.js → ProjectKanbanPage-C-opG86F.js} +31 -32
- package/dist/web/assets/ProjectSettingsPage-B5ONsxYH.js +2 -0
- package/dist/web/assets/{ProviderSettingsPage-D-1Fyh5B.js → ProviderSettingsPage-B_bvOzI_.js} +6 -6
- package/dist/web/assets/{SettingsMasterDetail-CZfVaFo2.js → SettingsMasterDetail-rDCxJGeO.js} +1 -1
- package/dist/web/assets/{TeamSettingsPage-DysTmOGE.js → TeamSettingsPage-CQqEaxN8.js} +1 -1
- package/dist/web/assets/{arc-CgLBAHTH.js → arc-4bMzF8s5.js} +1 -1
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-CP9eQfdk.js → architectureDiagram-3BPJPVTR-DHLpZrsg.js} +1 -1
- package/dist/web/assets/{arrow-left-y1LwO7sB.js → arrow-left-CHWUcgUT.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-BURZnUwn.js → blockDiagram-GPEHLZMM-BWnkREjA.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-Dzgp3z3S.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--B7iZqBL.js → chevron-down-KH9YdB6d.js} +1 -1
- package/dist/web/assets/{chevron-right-7PShDIwg.js → chevron-right-CAUiKY10.js} +1 -1
- package/dist/web/assets/{chevron-up-3n_vHdrD.js → chevron-up-5_Vvb-Ci.js} +1 -1
- package/dist/web/assets/{chunk-2J33WTMH-CIpasa01.js → chunk-2J33WTMH-BZ13KXVx.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-DeSe2m1y.js → chunk-4BX2VUAB-1I8GhF4l.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-BkJ6ogdn.js → chunk-55IACEB6-XvpeWieM.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-Bkbv-knP.js → chunk-727SXJPM-D7Al9-bq.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-BG9wGFIo.js → chunk-AQP2D5EJ-Btn846zU.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-Cvl_2za9.js → chunk-FMBD7UC4-A2nqCnZf.js} +1 -1
- package/dist/web/assets/{chunk-ND2GUHAM-B_zdzkWX.js → chunk-ND2GUHAM-BVkIo6Pj.js} +1 -1
- package/dist/web/assets/{chunk-QZHKN3VN-Ch6dvKNw.js → chunk-QZHKN3VN-BoNYHO2n.js} +1 -1
- package/dist/web/assets/{circle-alert-W13JkYj3.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-CYhJ9OPu.js → code-block-OCS4YCEC-CclNpE_3.js} +1 -1
- package/dist/web/assets/confirm-dialog-DvRd9Tcf.js +1 -0
- package/dist/web/assets/{copy-bJ2sut_L.js → copy-Dyh8NYTA.js} +1 -1
- package/dist/web/assets/{cose-bilkent-S5V4N54A-DWlnxyfH.js → cose-bilkent-S5V4N54A-BgA5aQr4.js} +1 -1
- package/dist/web/assets/{dagre-BM42HDAG-Bt3QI02a.js → dagre-BM42HDAG-C1zMROWX.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-EaJBYSnS.js → diagram-2AECGRRQ-DkMjxfgh.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-BGyRb-_h.js → diagram-5GNKFQAL-CxpZDmT0.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-CZhDTMfm.js → diagram-KO2AKTUF-BtMUfvmN.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-Be8BAxmL.js → diagram-LMA3HP47-BZi1ud-6.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-DEdQ7Cml.js → diagram-OG6HWLK6-_MZa2Vmf.js} +1 -1
- package/dist/web/assets/{erDiagram-TEJ5UH35-CG9SNlbz.js → erDiagram-TEJ5UH35-DpDg2-r9.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-BUpx5PVX.js → flowDiagram-I6XJVG4X-CLR_X1xm.js} +1 -1
- package/dist/web/assets/folder-picker-FcqjOvor.js +1 -0
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-D7vRxMpE.js → ganttDiagram-6RSMTGT7-ClNXygJc.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-3lhH-RcD.js → gitGraphDiagram-PVQCEYII-CVdJEdZV.js} +1 -1
- package/dist/web/assets/index-DRJLGAPR.css +1 -0
- package/dist/web/assets/index-RC-dgDMb.js +122 -0
- package/dist/web/assets/{index-CGMjRoF7.js → index-gMgAFdpQ.js} +4 -4
- package/dist/web/assets/{infoDiagram-5YYISTIA-C5SNbJpO.js → infoDiagram-5YYISTIA-XrlQ9Cnl.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-Ca7tX9_0.js → ishikawaDiagram-YF4QCWOH-I2xf8q1-.js} +1 -1
- package/dist/web/assets/{journeyDiagram-JHISSGLW-B2oo6nxY.js → journeyDiagram-JHISSGLW-DMDaSMF5.js} +1 -1
- package/dist/web/assets/{kanban-definition-UN3LZRKU-D1CzIzX9.js → kanban-definition-UN3LZRKU-kVff9awG.js} +1 -1
- package/dist/web/assets/{layers-BbLzQqyy.js → layers-UNThcvce.js} +1 -1
- package/dist/web/assets/{linear-BWowtcGw.js → linear-HJ2-smLG.js} +1 -1
- package/dist/web/assets/{mermaid-NOHMQCX5-B42ziicV.js → mermaid-NOHMQCX5-BFBL5hms.js} +40 -40
- package/dist/web/assets/{message-square-Bd_m34OX.js → message-square-C3yz80ya.js} +1 -1
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-CYbO6QCk.js → mindmap-definition-RKZ34NQL-CSkOHlF2.js} +1 -1
- package/dist/web/assets/{modal-CxOKWOjZ.js → modal-DDKEV5YV.js} +1 -1
- package/dist/web/assets/{pencil-h5kxwbcu.js → pencil-AIclOdlA.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-DXQghKRy.js → pieDiagram-4H26LBE5-DIk67jqC.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-C6lAMMQB.js → quadrantDiagram-W4KKPZXB-BqRrzRBU.js} +1 -1
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-Di9Le7Wy.js → requirementDiagram-4Y6WPE33-DNo804ZW.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-BGpnptvR.js → sankeyDiagram-5OEKKPKP-I5HhBnqr.js} +1 -1
- package/dist/web/assets/{select-DFuN9DX8.js → select-BEwAtIzV.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-DjfkfaQL.js → sequenceDiagram-3UESZ5HK-BCmc9m_1.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-DsELotm9.js → stateDiagram-AJRCARHV-Dgue8k7_.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-CygUwX8V.js +1 -0
- package/dist/web/assets/{switch-gL7lBFj6.js → switch-CdgTf7ni.js} +1 -1
- package/dist/web/assets/{textarea-Lr4GZaYW.js → textarea-D6QSwteN.js} +1 -1
- package/dist/web/assets/{timeline-definition-PNZ67QCA-Cq1BWC0T.js → timeline-definition-PNZ67QCA-Bl4mDUDT.js} +1 -1
- package/dist/web/assets/{trash-2-B7GFt5Wl.js → trash-2-B47vLMwl.js} +1 -1
- package/dist/web/assets/{upload-L8hpDFoO.js → upload-E5fNVeCI.js} +1 -1
- package/dist/web/assets/{use-profiles-zIsoigD8.js → use-profiles-CDvw0uSu.js} +1 -1
- package/dist/web/assets/use-projects-DMxMsQFw.js +1 -0
- package/dist/web/assets/{use-providers-erJ2iHLz.js → use-providers-DBKhBg_2.js} +1 -1
- package/dist/web/assets/{useNormalizedLogs-CXAn7xj_.js → useNormalizedLogs-BM-drJin.js} +1 -1
- package/dist/web/assets/{vennDiagram-CIIHVFJN-DdOfNtRo.js → vennDiagram-CIIHVFJN-rjeTPeto.js} +1 -1
- package/dist/web/assets/{wardley-L42UT6IY-BeKcEIgK.js → wardley-L42UT6IY-2cunyDSM.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-DPKOxABd.js → wardleyDiagram-YWT4CUSO-CNxobWk8.js} +1 -1
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-olIy_aaY.js → xychartDiagram-2RQKCTM6-DBVimyeP.js} +1 -1
- package/dist/web/index.html +2 -2
- package/node_modules/@agent-tower/shared/dist/types.d.ts +45 -5
- 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 +21 -5
- package/node_modules/@prisma/client/.prisma/client/index-browser.js +16 -0
- package/node_modules/@prisma/client/.prisma/client/index.d.ts +1599 -215
- package/node_modules/@prisma/client/.prisma/client/index.js +21 -5
- package/node_modules/@prisma/client/.prisma/client/package.json +1 -1
- package/node_modules/@prisma/client/.prisma/client/schema.prisma +24 -7
- package/node_modules/@prisma/client/.prisma/client/wasm.js +16 -0
- package/package.json +1 -1
- package/prisma/migrations/20260701000000_add_access_auth_settings/migration.sql +9 -0
- package/prisma/migrations/20260713000000_add_work_request_start_retry/migration.sql +7 -0
- package/prisma/schema.prisma +17 -0
- package/dist/web/assets/CreateTaskInput-Cb3aRHr3.js +0 -1
- package/dist/web/assets/GeneralSettingsPage-CE7P3t_v.js +0 -1
- package/dist/web/assets/McpSettingsPage-gp1XDMUd.js +0 -1
- package/dist/web/assets/NotificationSettingsPage-BhZeHEef.js +0 -1
- package/dist/web/assets/ProfileSettingsPage-D_y3ePFs.js +0 -3
- package/dist/web/assets/ProjectSettingsPage-V38j8T61.js +0 -2
- package/dist/web/assets/channel-CWEYGfCJ.js +0 -1
- package/dist/web/assets/check-D2WWIhwV.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-CcU8zfK8.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-CcU8zfK8.js +0 -1
- package/dist/web/assets/confirm-dialog-BAqfLDT8.js +0 -1
- package/dist/web/assets/folder-picker-DFB9_pv4.js +0 -1
- package/dist/web/assets/folder-u7fKAtz_.js +0 -1
- package/dist/web/assets/index-7nw4fV42.js +0 -122
- package/dist/web/assets/index-YCjjSSLC.css +0 -1
- package/dist/web/assets/input-CKwRYuUJ.js +0 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-CudtEyHd.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 () => {
|
|
@@ -2312,7 +2323,7 @@ describe('TeamSchedulerService', () => {
|
|
|
2312
2323
|
status: 'FAILED',
|
|
2313
2324
|
});
|
|
2314
2325
|
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2315
|
-
status: '
|
|
2326
|
+
status: 'FAILED',
|
|
2316
2327
|
});
|
|
2317
2328
|
await expect(service.startNextSessions(teamRun.id)).resolves.toEqual([]);
|
|
2318
2329
|
});
|
|
@@ -2333,7 +2344,7 @@ describe('TeamSchedulerService', () => {
|
|
|
2333
2344
|
});
|
|
2334
2345
|
await expect(service.startNextSessions(teamRun.id)).resolves.toEqual([]);
|
|
2335
2346
|
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2336
|
-
status: '
|
|
2347
|
+
status: 'FAILED',
|
|
2337
2348
|
});
|
|
2338
2349
|
await expect(prisma.agentInvocation.findFirst({ where: { workRequestId: request.id } })).resolves.toMatchObject({
|
|
2339
2350
|
status: 'FAILED',
|
|
@@ -2385,7 +2396,7 @@ describe('TeamSchedulerService', () => {
|
|
|
2385
2396
|
sessionId: expect.any(String),
|
|
2386
2397
|
});
|
|
2387
2398
|
await expect(prisma.workRequest.findUnique({ where: { id: missingProviderRequest.id } })).resolves.toMatchObject({
|
|
2388
|
-
status: '
|
|
2399
|
+
status: 'FAILED',
|
|
2389
2400
|
});
|
|
2390
2401
|
await expect(prisma.agentInvocation.findFirst({
|
|
2391
2402
|
where: { workRequestId: missingProviderRequest.id },
|
|
@@ -2400,21 +2411,26 @@ describe('TeamSchedulerService', () => {
|
|
|
2400
2411
|
expect(sessionManager.create).toHaveBeenCalledTimes(1);
|
|
2401
2412
|
expect(lockService.listLocks()).toEqual([]);
|
|
2402
2413
|
});
|
|
2403
|
-
it('
|
|
2404
|
-
|
|
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));
|
|
2416
|
+
const { task, teamRun, members } = await createTeamRunFixture({
|
|
2405
2417
|
memberCapabilities: [writeCapabilities],
|
|
2406
2418
|
withWorkspace: false,
|
|
2419
|
+
taskStatus: TaskStatus.IN_PROGRESS,
|
|
2407
2420
|
});
|
|
2408
2421
|
const request = await createWorkRequest({
|
|
2409
2422
|
teamRunId: teamRun.id,
|
|
2410
2423
|
targetMemberId: members[0].id,
|
|
2411
2424
|
});
|
|
2425
|
+
const sessionManager = createSessionManagerMock();
|
|
2426
|
+
sessionManager.start.mockRejectedValueOnce(new Error('session start failed'));
|
|
2412
2427
|
service = new TeamSchedulerService(lockService, {
|
|
2413
2428
|
workspaceService: createWorkspaceServiceMock(),
|
|
2414
|
-
sessionManager
|
|
2429
|
+
sessionManager,
|
|
2415
2430
|
getProviderById: createProviderLookup(),
|
|
2431
|
+
now: () => now,
|
|
2416
2432
|
});
|
|
2417
|
-
await expect(service.startNextSessions(teamRun.id)).
|
|
2433
|
+
await expect(service.startNextSessions(teamRun.id)).resolves.toEqual([]);
|
|
2418
2434
|
expect(lockService.listLocks()).toEqual([]);
|
|
2419
2435
|
const invocation = await prisma.agentInvocation.findFirstOrThrow({
|
|
2420
2436
|
where: { workRequestId: request.id },
|
|
@@ -2427,11 +2443,347 @@ describe('TeamSchedulerService', () => {
|
|
|
2427
2443
|
await expect(prisma.session.findUnique({ where: { id: invocation.sessionId } })).resolves.toMatchObject({
|
|
2428
2444
|
status: 'FAILED',
|
|
2429
2445
|
});
|
|
2446
|
+
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2447
|
+
status: 'QUEUED',
|
|
2448
|
+
startAttemptCount: 1,
|
|
2449
|
+
lastStartError: expect.stringContaining('session start failed'),
|
|
2450
|
+
nextStartRetryAt: new Date(now.getTime() + 1_000),
|
|
2451
|
+
});
|
|
2452
|
+
await expect(prisma.task.findUnique({ where: { id: task.id } })).resolves.toMatchObject({
|
|
2453
|
+
status: TaskStatus.IN_PROGRESS,
|
|
2454
|
+
});
|
|
2455
|
+
await expect(prisma.teamRun.findUnique({ where: { id: teamRun.id } })).resolves.toMatchObject({
|
|
2456
|
+
reviewReason: null,
|
|
2457
|
+
});
|
|
2458
|
+
await expect(service.startNextSessions(teamRun.id)).resolves.toEqual([]);
|
|
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);
|
|
2430
2636
|
await expect(prisma.workRequest.findUnique({ where: { id: request.id } })).resolves.toMatchObject({
|
|
2431
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,
|
|
2432
2669
|
});
|
|
2433
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);
|
|
2434
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' });
|
|
2435
2787
|
});
|
|
2436
2788
|
});
|
|
2437
2789
|
//# sourceMappingURL=team-scheduler.service.test.js.map
|