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.
Files changed (364) hide show
  1. package/README.md +6 -1
  2. package/dist/app.d.ts.map +1 -1
  3. package/dist/app.js +15 -1
  4. package/dist/app.js.map +1 -1
  5. package/dist/app.test.js +12 -0
  6. package/dist/app.test.js.map +1 -1
  7. package/dist/cli.js +18 -1
  8. package/dist/cli.js.map +1 -1
  9. package/dist/core/event-bus.d.ts +3 -0
  10. package/dist/core/event-bus.d.ts.map +1 -1
  11. package/dist/core/event-bus.js.map +1 -1
  12. package/dist/executors/__tests__/base.executor.test.js +29 -1
  13. package/dist/executors/__tests__/base.executor.test.js.map +1 -1
  14. package/dist/executors/__tests__/codex.executor.test.js +13 -0
  15. package/dist/executors/__tests__/codex.executor.test.js.map +1 -1
  16. package/dist/executors/__tests__/providers-availability.test.js +19 -0
  17. package/dist/executors/__tests__/providers-availability.test.js.map +1 -1
  18. package/dist/executors/__tests__/stdin-prompt.executors.test.js +10 -0
  19. package/dist/executors/__tests__/stdin-prompt.executors.test.js.map +1 -1
  20. package/dist/executors/base.executor.d.ts.map +1 -1
  21. package/dist/executors/base.executor.js +4 -0
  22. package/dist/executors/base.executor.js.map +1 -1
  23. package/dist/executors/claude-code.executor.d.ts.map +1 -1
  24. package/dist/executors/claude-code.executor.js +18 -5
  25. package/dist/executors/claude-code.executor.js.map +1 -1
  26. package/dist/executors/codex.executor.d.ts.map +1 -1
  27. package/dist/executors/codex.executor.js +9 -3
  28. package/dist/executors/codex.executor.js.map +1 -1
  29. package/dist/executors/cursor-agent.executor.d.ts +2 -1
  30. package/dist/executors/cursor-agent.executor.d.ts.map +1 -1
  31. package/dist/executors/cursor-agent.executor.js +29 -9
  32. package/dist/executors/cursor-agent.executor.js.map +1 -1
  33. package/dist/executors/execution-env.d.ts +1 -1
  34. package/dist/executors/execution-env.d.ts.map +1 -1
  35. package/dist/executors/execution-env.js +7 -0
  36. package/dist/executors/execution-env.js.map +1 -1
  37. package/dist/executors/index.d.ts +1 -0
  38. package/dist/executors/index.d.ts.map +1 -1
  39. package/dist/executors/index.js +1 -0
  40. package/dist/executors/index.js.map +1 -1
  41. package/dist/executors/start-error.d.ts +13 -0
  42. package/dist/executors/start-error.d.ts.map +1 -0
  43. package/dist/executors/start-error.js +25 -0
  44. package/dist/executors/start-error.js.map +1 -0
  45. package/dist/index.js +2 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/mcp/http-client.d.ts +2 -0
  48. package/dist/mcp/http-client.d.ts.map +1 -1
  49. package/dist/mcp/http-client.js +7 -0
  50. package/dist/mcp/http-client.js.map +1 -1
  51. package/dist/mcp/index.js +4 -1
  52. package/dist/mcp/index.js.map +1 -1
  53. package/dist/mcp/server.d.ts +3 -1
  54. package/dist/mcp/server.d.ts.map +1 -1
  55. package/dist/mcp/server.js +2 -1
  56. package/dist/mcp/server.js.map +1 -1
  57. package/dist/middleware/__tests__/access-auth.test.d.ts +2 -0
  58. package/dist/middleware/__tests__/access-auth.test.d.ts.map +1 -0
  59. package/dist/middleware/__tests__/access-auth.test.js +249 -0
  60. package/dist/middleware/__tests__/access-auth.test.js.map +1 -0
  61. package/dist/middleware/access-auth.d.ts +3 -0
  62. package/dist/middleware/access-auth.d.ts.map +1 -0
  63. package/dist/middleware/access-auth.js +110 -0
  64. package/dist/middleware/access-auth.js.map +1 -0
  65. package/dist/routes/__tests__/agent-cli-environment.test.js +28 -10
  66. package/dist/routes/__tests__/agent-cli-environment.test.js.map +1 -1
  67. package/dist/routes/__tests__/files.test.d.ts +2 -0
  68. package/dist/routes/__tests__/files.test.d.ts.map +1 -0
  69. package/dist/routes/__tests__/files.test.js +101 -0
  70. package/dist/routes/__tests__/files.test.js.map +1 -0
  71. package/dist/routes/__tests__/previews.integration.test.d.ts +2 -0
  72. package/dist/routes/__tests__/previews.integration.test.d.ts.map +1 -0
  73. package/dist/routes/__tests__/previews.integration.test.js +269 -0
  74. package/dist/routes/__tests__/previews.integration.test.js.map +1 -0
  75. package/dist/routes/__tests__/previews.test.js +103 -1
  76. package/dist/routes/__tests__/previews.test.js.map +1 -1
  77. package/dist/routes/access-auth.d.ts +3 -0
  78. package/dist/routes/access-auth.d.ts.map +1 -0
  79. package/dist/routes/access-auth.js +89 -0
  80. package/dist/routes/access-auth.js.map +1 -0
  81. package/dist/routes/files.d.ts.map +1 -1
  82. package/dist/routes/files.js +46 -0
  83. package/dist/routes/files.js.map +1 -1
  84. package/dist/routes/index.d.ts.map +1 -1
  85. package/dist/routes/index.js +3 -0
  86. package/dist/routes/index.js.map +1 -1
  87. package/dist/routes/previews.d.ts +7 -2
  88. package/dist/routes/previews.d.ts.map +1 -1
  89. package/dist/routes/previews.js +106 -32
  90. package/dist/routes/previews.js.map +1 -1
  91. package/dist/routes/projects.d.ts.map +1 -1
  92. package/dist/routes/projects.js +9 -0
  93. package/dist/routes/projects.js.map +1 -1
  94. package/dist/routes/system.d.ts.map +1 -1
  95. package/dist/routes/system.js +17 -11
  96. package/dist/routes/system.js.map +1 -1
  97. package/dist/services/__tests__/access-auth.service.test.d.ts +2 -0
  98. package/dist/services/__tests__/access-auth.service.test.d.ts.map +1 -0
  99. package/dist/services/__tests__/access-auth.service.test.js +316 -0
  100. package/dist/services/__tests__/access-auth.service.test.js.map +1 -0
  101. package/dist/services/__tests__/member-heartbeat-scheduler.test.d.ts +2 -0
  102. package/dist/services/__tests__/member-heartbeat-scheduler.test.d.ts.map +1 -0
  103. package/dist/services/__tests__/member-heartbeat-scheduler.test.js +75 -0
  104. package/dist/services/__tests__/member-heartbeat-scheduler.test.js.map +1 -0
  105. package/dist/services/__tests__/member-heartbeat.test.d.ts +2 -0
  106. package/dist/services/__tests__/member-heartbeat.test.d.ts.map +1 -0
  107. package/dist/services/__tests__/member-heartbeat.test.js +531 -0
  108. package/dist/services/__tests__/member-heartbeat.test.js.map +1 -0
  109. package/dist/services/__tests__/project.service.test.js +29 -0
  110. package/dist/services/__tests__/project.service.test.js.map +1 -1
  111. package/dist/services/__tests__/session-manager.team-run.test.js +237 -0
  112. package/dist/services/__tests__/session-manager.team-run.test.js.map +1 -1
  113. package/dist/services/__tests__/task.service.test.js +34 -0
  114. package/dist/services/__tests__/task.service.test.js.map +1 -1
  115. package/dist/services/__tests__/team-queue-recovery.integration.test.d.ts +2 -0
  116. package/dist/services/__tests__/team-queue-recovery.integration.test.d.ts.map +1 -0
  117. package/dist/services/__tests__/team-queue-recovery.integration.test.js +279 -0
  118. package/dist/services/__tests__/team-queue-recovery.integration.test.js.map +1 -0
  119. package/dist/services/__tests__/team-reconciler.service.test.js +180 -3
  120. package/dist/services/__tests__/team-reconciler.service.test.js.map +1 -1
  121. package/dist/services/__tests__/team-run.service.test.js +196 -2
  122. package/dist/services/__tests__/team-run.service.test.js.map +1 -1
  123. package/dist/services/__tests__/team-scheduler.service.test.js +362 -10
  124. package/dist/services/__tests__/team-scheduler.service.test.js.map +1 -1
  125. package/dist/services/__tests__/workspace.service.test.js +120 -1
  126. package/dist/services/__tests__/workspace.service.test.js.map +1 -1
  127. package/dist/services/access-auth.service.d.ts +82 -0
  128. package/dist/services/access-auth.service.d.ts.map +1 -0
  129. package/dist/services/access-auth.service.js +385 -0
  130. package/dist/services/access-auth.service.js.map +1 -0
  131. package/dist/services/agent-cli/__tests__/command-runner.test.d.ts +2 -0
  132. package/dist/services/agent-cli/__tests__/command-runner.test.d.ts.map +1 -0
  133. package/dist/services/agent-cli/__tests__/command-runner.test.js +64 -0
  134. package/dist/services/agent-cli/__tests__/command-runner.test.js.map +1 -0
  135. package/dist/services/agent-cli/__tests__/detection.test.js +48 -0
  136. package/dist/services/agent-cli/__tests__/detection.test.js.map +1 -1
  137. package/dist/services/agent-cli/__tests__/manifest-downloader.test.js +272 -14
  138. package/dist/services/agent-cli/__tests__/manifest-downloader.test.js.map +1 -1
  139. package/dist/services/agent-cli/command-runner.d.ts +21 -0
  140. package/dist/services/agent-cli/command-runner.d.ts.map +1 -0
  141. package/dist/services/agent-cli/command-runner.js +83 -0
  142. package/dist/services/agent-cli/command-runner.js.map +1 -0
  143. package/dist/services/agent-cli/detection.d.ts +1 -12
  144. package/dist/services/agent-cli/detection.d.ts.map +1 -1
  145. package/dist/services/agent-cli/detection.js +5 -27
  146. package/dist/services/agent-cli/detection.js.map +1 -1
  147. package/dist/services/agent-cli/downloader.d.ts +2 -2
  148. package/dist/services/agent-cli/downloader.d.ts.map +1 -1
  149. package/dist/services/agent-cli/downloader.js +4 -8
  150. package/dist/services/agent-cli/downloader.js.map +1 -1
  151. package/dist/services/agent-cli/environment.service.d.ts +2 -1
  152. package/dist/services/agent-cli/environment.service.d.ts.map +1 -1
  153. package/dist/services/agent-cli/environment.service.js +25 -12
  154. package/dist/services/agent-cli/environment.service.js.map +1 -1
  155. package/dist/services/agent-cli/manifest-validator.d.ts.map +1 -1
  156. package/dist/services/agent-cli/manifest-validator.js +31 -22
  157. package/dist/services/agent-cli/manifest-validator.js.map +1 -1
  158. package/dist/services/agent-cli/manifest.d.ts.map +1 -1
  159. package/dist/services/agent-cli/manifest.js +97 -56
  160. package/dist/services/agent-cli/manifest.js.map +1 -1
  161. package/dist/services/agent-cli/security.d.ts +1 -1
  162. package/dist/services/agent-cli/security.d.ts.map +1 -1
  163. package/dist/services/agent-cli/security.js +11 -1
  164. package/dist/services/agent-cli/security.js.map +1 -1
  165. package/dist/services/agent-cli/task-manager.d.ts.map +1 -1
  166. package/dist/services/agent-cli/task-manager.js +8 -19
  167. package/dist/services/agent-cli/task-manager.js.map +1 -1
  168. package/dist/services/mcp-config.service.d.ts.map +1 -1
  169. package/dist/services/mcp-config.service.js +8 -2
  170. package/dist/services/mcp-config.service.js.map +1 -1
  171. package/dist/services/mcp-config.service.test.js +18 -3
  172. package/dist/services/mcp-config.service.test.js.map +1 -1
  173. package/dist/services/member-heartbeat-scheduler.d.ts +39 -0
  174. package/dist/services/member-heartbeat-scheduler.d.ts.map +1 -0
  175. package/dist/services/member-heartbeat-scheduler.js +97 -0
  176. package/dist/services/member-heartbeat-scheduler.js.map +1 -0
  177. package/dist/services/project-guards.d.ts +5 -0
  178. package/dist/services/project-guards.d.ts.map +1 -1
  179. package/dist/services/project-guards.js +47 -0
  180. package/dist/services/project-guards.js.map +1 -1
  181. package/dist/services/project.service.d.ts +6 -4
  182. package/dist/services/project.service.d.ts.map +1 -1
  183. package/dist/services/project.service.js +16 -7
  184. package/dist/services/project.service.js.map +1 -1
  185. package/dist/services/session-manager.d.ts +13 -0
  186. package/dist/services/session-manager.d.ts.map +1 -1
  187. package/dist/services/session-manager.js +106 -40
  188. package/dist/services/session-manager.js.map +1 -1
  189. package/dist/services/task.service.d.ts +11 -9
  190. package/dist/services/task.service.d.ts.map +1 -1
  191. package/dist/services/task.service.js +17 -12
  192. package/dist/services/task.service.js.map +1 -1
  193. package/dist/services/team-reconciler.service.d.ts +49 -0
  194. package/dist/services/team-reconciler.service.d.ts.map +1 -1
  195. package/dist/services/team-reconciler.service.js +313 -4
  196. package/dist/services/team-reconciler.service.js.map +1 -1
  197. package/dist/services/team-run.service.d.ts +2 -0
  198. package/dist/services/team-run.service.d.ts.map +1 -1
  199. package/dist/services/team-run.service.js +36 -7
  200. package/dist/services/team-run.service.js.map +1 -1
  201. package/dist/services/team-scheduler.service.d.ts +5 -0
  202. package/dist/services/team-scheduler.service.d.ts.map +1 -1
  203. package/dist/services/team-scheduler.service.js +237 -21
  204. package/dist/services/team-scheduler.service.js.map +1 -1
  205. package/dist/services/terminal-manager.d.ts.map +1 -1
  206. package/dist/services/terminal-manager.js +1 -0
  207. package/dist/services/terminal-manager.js.map +1 -1
  208. package/dist/services/workspace.service.d.ts +1 -0
  209. package/dist/services/workspace.service.d.ts.map +1 -1
  210. package/dist/services/workspace.service.js +62 -9
  211. package/dist/services/workspace.service.js.map +1 -1
  212. package/dist/socket/__tests__/socket-gateway.test.d.ts +2 -0
  213. package/dist/socket/__tests__/socket-gateway.test.d.ts.map +1 -0
  214. package/dist/socket/__tests__/socket-gateway.test.js +119 -0
  215. package/dist/socket/__tests__/socket-gateway.test.js.map +1 -0
  216. package/dist/socket/middleware/__tests__/auth.test.js +102 -2
  217. package/dist/socket/middleware/__tests__/auth.test.js.map +1 -1
  218. package/dist/socket/middleware/auth.d.ts +1 -0
  219. package/dist/socket/middleware/auth.d.ts.map +1 -1
  220. package/dist/socket/middleware/auth.js +22 -2
  221. package/dist/socket/middleware/auth.js.map +1 -1
  222. package/dist/socket/socket-gateway.d.ts +6 -2
  223. package/dist/socket/socket-gateway.d.ts.map +1 -1
  224. package/dist/socket/socket-gateway.js +78 -15
  225. package/dist/socket/socket-gateway.js.map +1 -1
  226. package/dist/utils/index.d.ts +6 -2
  227. package/dist/utils/index.d.ts.map +1 -1
  228. package/dist/utils/index.js +18 -7
  229. package/dist/utils/index.js.map +1 -1
  230. package/dist/utils/internal-api-token.d.ts +9 -0
  231. package/dist/utils/internal-api-token.d.ts.map +1 -0
  232. package/dist/utils/internal-api-token.js +74 -0
  233. package/dist/utils/internal-api-token.js.map +1 -0
  234. package/dist/utils/preview-path.d.ts +11 -0
  235. package/dist/utils/preview-path.d.ts.map +1 -0
  236. package/dist/utils/preview-path.js +46 -0
  237. package/dist/utils/preview-path.js.map +1 -0
  238. package/dist/utils/process-launch.d.ts +3 -1
  239. package/dist/utils/process-launch.d.ts.map +1 -1
  240. package/dist/utils/process-launch.js +41 -5
  241. package/dist/utils/process-launch.js.map +1 -1
  242. package/dist/utils/process-launch.test.js +37 -3
  243. package/dist/utils/process-launch.test.js.map +1 -1
  244. package/dist/web/assets/{AgentDemoPage-DMLw4xpg.js → AgentDemoPage-CAfZ4j97.js} +1 -1
  245. package/dist/web/assets/{AgentEnvironmentSettingsPage-NgRdouLu.js → AgentEnvironmentSettingsPage-DsNEtF9E.js} +1 -1
  246. package/dist/web/assets/{AgentLogo-ThhvSi7G.js → AgentLogo-jT7FgIek.js} +1 -1
  247. package/dist/web/assets/{ConversationPage-B1ZcyF_h.js → ConversationPage-BoHNGkW4.js} +2 -2
  248. package/dist/web/assets/CreateTaskInput-Cc5iLMFm.js +1 -0
  249. package/dist/web/assets/{DemoPage-CN2OBKzu.js → DemoPage-D7TaL1CJ.js} +3 -3
  250. package/dist/web/assets/GeneralSettingsPage-D5K3E8MX.js +1 -0
  251. package/dist/web/assets/{Icons-DIZBOtuC.js → Icons-CqiMv6t9.js} +1 -1
  252. package/dist/web/assets/{LoadingPreviewPage-CRBcypeY.js → LoadingPreviewPage-y_zIMHD7.js} +1 -1
  253. package/dist/web/assets/McpSettingsPage-Cx-qKB2x.js +1 -0
  254. package/dist/web/assets/{MemberAvatar-Y_pJlw-N.js → MemberAvatar-MDUj0w6B.js} +1 -1
  255. package/dist/web/assets/NotificationSettingsPage-9aGt7-Lh.js +1 -0
  256. package/dist/web/assets/ProfileSettingsPage-Ct5ha4tH.js +3 -0
  257. package/dist/web/assets/{ProjectKanbanPage-CBfocvKp.js → ProjectKanbanPage-C-opG86F.js} +31 -32
  258. package/dist/web/assets/ProjectSettingsPage-B5ONsxYH.js +2 -0
  259. package/dist/web/assets/{ProviderSettingsPage-D-1Fyh5B.js → ProviderSettingsPage-B_bvOzI_.js} +6 -6
  260. package/dist/web/assets/{SettingsMasterDetail-CZfVaFo2.js → SettingsMasterDetail-rDCxJGeO.js} +1 -1
  261. package/dist/web/assets/{TeamSettingsPage-DysTmOGE.js → TeamSettingsPage-CQqEaxN8.js} +1 -1
  262. package/dist/web/assets/{arc-CgLBAHTH.js → arc-4bMzF8s5.js} +1 -1
  263. package/dist/web/assets/{architectureDiagram-3BPJPVTR-CP9eQfdk.js → architectureDiagram-3BPJPVTR-DHLpZrsg.js} +1 -1
  264. package/dist/web/assets/{arrow-left-y1LwO7sB.js → arrow-left-CHWUcgUT.js} +1 -1
  265. package/dist/web/assets/{blockDiagram-GPEHLZMM-BURZnUwn.js → blockDiagram-GPEHLZMM-BWnkREjA.js} +1 -1
  266. package/dist/web/assets/{c4Diagram-AAUBKEIU-Dzgp3z3S.js → c4Diagram-AAUBKEIU-Cgz8B5xI.js} +1 -1
  267. package/dist/web/assets/channel-Cm1ouAHS.js +1 -0
  268. package/dist/web/assets/check-B0AgpiMM.js +1 -0
  269. package/dist/web/assets/{chevron-down--B7iZqBL.js → chevron-down-KH9YdB6d.js} +1 -1
  270. package/dist/web/assets/{chevron-right-7PShDIwg.js → chevron-right-CAUiKY10.js} +1 -1
  271. package/dist/web/assets/{chevron-up-3n_vHdrD.js → chevron-up-5_Vvb-Ci.js} +1 -1
  272. package/dist/web/assets/{chunk-2J33WTMH-CIpasa01.js → chunk-2J33WTMH-BZ13KXVx.js} +1 -1
  273. package/dist/web/assets/{chunk-4BX2VUAB-DeSe2m1y.js → chunk-4BX2VUAB-1I8GhF4l.js} +1 -1
  274. package/dist/web/assets/{chunk-55IACEB6-BkJ6ogdn.js → chunk-55IACEB6-XvpeWieM.js} +1 -1
  275. package/dist/web/assets/{chunk-727SXJPM-Bkbv-knP.js → chunk-727SXJPM-D7Al9-bq.js} +1 -1
  276. package/dist/web/assets/{chunk-AQP2D5EJ-BG9wGFIo.js → chunk-AQP2D5EJ-Btn846zU.js} +1 -1
  277. package/dist/web/assets/{chunk-FMBD7UC4-Cvl_2za9.js → chunk-FMBD7UC4-A2nqCnZf.js} +1 -1
  278. package/dist/web/assets/{chunk-ND2GUHAM-B_zdzkWX.js → chunk-ND2GUHAM-BVkIo6Pj.js} +1 -1
  279. package/dist/web/assets/{chunk-QZHKN3VN-Ch6dvKNw.js → chunk-QZHKN3VN-BoNYHO2n.js} +1 -1
  280. package/dist/web/assets/{circle-alert-W13JkYj3.js → circle-alert-fO3Tkh3f.js} +1 -1
  281. package/dist/web/assets/classDiagram-4FO5ZUOK-BfqKofse.js +1 -0
  282. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-BfqKofse.js +1 -0
  283. package/dist/web/assets/{code-block-OCS4YCEC-CYhJ9OPu.js → code-block-OCS4YCEC-CclNpE_3.js} +1 -1
  284. package/dist/web/assets/confirm-dialog-DvRd9Tcf.js +1 -0
  285. package/dist/web/assets/{copy-bJ2sut_L.js → copy-Dyh8NYTA.js} +1 -1
  286. package/dist/web/assets/{cose-bilkent-S5V4N54A-DWlnxyfH.js → cose-bilkent-S5V4N54A-BgA5aQr4.js} +1 -1
  287. package/dist/web/assets/{dagre-BM42HDAG-Bt3QI02a.js → dagre-BM42HDAG-C1zMROWX.js} +1 -1
  288. package/dist/web/assets/{diagram-2AECGRRQ-EaJBYSnS.js → diagram-2AECGRRQ-DkMjxfgh.js} +1 -1
  289. package/dist/web/assets/{diagram-5GNKFQAL-BGyRb-_h.js → diagram-5GNKFQAL-CxpZDmT0.js} +1 -1
  290. package/dist/web/assets/{diagram-KO2AKTUF-CZhDTMfm.js → diagram-KO2AKTUF-BtMUfvmN.js} +1 -1
  291. package/dist/web/assets/{diagram-LMA3HP47-Be8BAxmL.js → diagram-LMA3HP47-BZi1ud-6.js} +1 -1
  292. package/dist/web/assets/{diagram-OG6HWLK6-DEdQ7Cml.js → diagram-OG6HWLK6-_MZa2Vmf.js} +1 -1
  293. package/dist/web/assets/{erDiagram-TEJ5UH35-CG9SNlbz.js → erDiagram-TEJ5UH35-DpDg2-r9.js} +1 -1
  294. package/dist/web/assets/{flowDiagram-I6XJVG4X-BUpx5PVX.js → flowDiagram-I6XJVG4X-CLR_X1xm.js} +1 -1
  295. package/dist/web/assets/folder-picker-FcqjOvor.js +1 -0
  296. package/dist/web/assets/{ganttDiagram-6RSMTGT7-D7vRxMpE.js → ganttDiagram-6RSMTGT7-ClNXygJc.js} +1 -1
  297. package/dist/web/assets/{gitGraphDiagram-PVQCEYII-3lhH-RcD.js → gitGraphDiagram-PVQCEYII-CVdJEdZV.js} +1 -1
  298. package/dist/web/assets/index-DRJLGAPR.css +1 -0
  299. package/dist/web/assets/index-RC-dgDMb.js +122 -0
  300. package/dist/web/assets/{index-CGMjRoF7.js → index-gMgAFdpQ.js} +4 -4
  301. package/dist/web/assets/{infoDiagram-5YYISTIA-C5SNbJpO.js → infoDiagram-5YYISTIA-XrlQ9Cnl.js} +1 -1
  302. package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-Ca7tX9_0.js → ishikawaDiagram-YF4QCWOH-I2xf8q1-.js} +1 -1
  303. package/dist/web/assets/{journeyDiagram-JHISSGLW-B2oo6nxY.js → journeyDiagram-JHISSGLW-DMDaSMF5.js} +1 -1
  304. package/dist/web/assets/{kanban-definition-UN3LZRKU-D1CzIzX9.js → kanban-definition-UN3LZRKU-kVff9awG.js} +1 -1
  305. package/dist/web/assets/{layers-BbLzQqyy.js → layers-UNThcvce.js} +1 -1
  306. package/dist/web/assets/{linear-BWowtcGw.js → linear-HJ2-smLG.js} +1 -1
  307. package/dist/web/assets/{mermaid-NOHMQCX5-B42ziicV.js → mermaid-NOHMQCX5-BFBL5hms.js} +40 -40
  308. package/dist/web/assets/{message-square-Bd_m34OX.js → message-square-C3yz80ya.js} +1 -1
  309. package/dist/web/assets/{mindmap-definition-RKZ34NQL-CYbO6QCk.js → mindmap-definition-RKZ34NQL-CSkOHlF2.js} +1 -1
  310. package/dist/web/assets/{modal-CxOKWOjZ.js → modal-DDKEV5YV.js} +1 -1
  311. package/dist/web/assets/{pencil-h5kxwbcu.js → pencil-AIclOdlA.js} +1 -1
  312. package/dist/web/assets/{pieDiagram-4H26LBE5-DXQghKRy.js → pieDiagram-4H26LBE5-DIk67jqC.js} +1 -1
  313. package/dist/web/assets/{quadrantDiagram-W4KKPZXB-C6lAMMQB.js → quadrantDiagram-W4KKPZXB-BqRrzRBU.js} +1 -1
  314. package/dist/web/assets/{requirementDiagram-4Y6WPE33-Di9Le7Wy.js → requirementDiagram-4Y6WPE33-DNo804ZW.js} +1 -1
  315. package/dist/web/assets/{sankeyDiagram-5OEKKPKP-BGpnptvR.js → sankeyDiagram-5OEKKPKP-I5HhBnqr.js} +1 -1
  316. package/dist/web/assets/{select-DFuN9DX8.js → select-BEwAtIzV.js} +1 -1
  317. package/dist/web/assets/{sequenceDiagram-3UESZ5HK-DjfkfaQL.js → sequenceDiagram-3UESZ5HK-BCmc9m_1.js} +1 -1
  318. package/dist/web/assets/{stateDiagram-AJRCARHV-DsELotm9.js → stateDiagram-AJRCARHV-Dgue8k7_.js} +1 -1
  319. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-CygUwX8V.js +1 -0
  320. package/dist/web/assets/{switch-gL7lBFj6.js → switch-CdgTf7ni.js} +1 -1
  321. package/dist/web/assets/{textarea-Lr4GZaYW.js → textarea-D6QSwteN.js} +1 -1
  322. package/dist/web/assets/{timeline-definition-PNZ67QCA-Cq1BWC0T.js → timeline-definition-PNZ67QCA-Bl4mDUDT.js} +1 -1
  323. package/dist/web/assets/{trash-2-B7GFt5Wl.js → trash-2-B47vLMwl.js} +1 -1
  324. package/dist/web/assets/{upload-L8hpDFoO.js → upload-E5fNVeCI.js} +1 -1
  325. package/dist/web/assets/{use-profiles-zIsoigD8.js → use-profiles-CDvw0uSu.js} +1 -1
  326. package/dist/web/assets/use-projects-DMxMsQFw.js +1 -0
  327. package/dist/web/assets/{use-providers-erJ2iHLz.js → use-providers-DBKhBg_2.js} +1 -1
  328. package/dist/web/assets/{useNormalizedLogs-CXAn7xj_.js → useNormalizedLogs-BM-drJin.js} +1 -1
  329. package/dist/web/assets/{vennDiagram-CIIHVFJN-DdOfNtRo.js → vennDiagram-CIIHVFJN-rjeTPeto.js} +1 -1
  330. package/dist/web/assets/{wardley-L42UT6IY-BeKcEIgK.js → wardley-L42UT6IY-2cunyDSM.js} +1 -1
  331. package/dist/web/assets/{wardleyDiagram-YWT4CUSO-DPKOxABd.js → wardleyDiagram-YWT4CUSO-CNxobWk8.js} +1 -1
  332. package/dist/web/assets/{xychartDiagram-2RQKCTM6-olIy_aaY.js → xychartDiagram-2RQKCTM6-DBVimyeP.js} +1 -1
  333. package/dist/web/index.html +2 -2
  334. package/node_modules/@agent-tower/shared/dist/types.d.ts +45 -5
  335. package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
  336. package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
  337. package/node_modules/@prisma/client/.prisma/client/edge.js +21 -5
  338. package/node_modules/@prisma/client/.prisma/client/index-browser.js +16 -0
  339. package/node_modules/@prisma/client/.prisma/client/index.d.ts +1599 -215
  340. package/node_modules/@prisma/client/.prisma/client/index.js +21 -5
  341. package/node_modules/@prisma/client/.prisma/client/package.json +1 -1
  342. package/node_modules/@prisma/client/.prisma/client/schema.prisma +24 -7
  343. package/node_modules/@prisma/client/.prisma/client/wasm.js +16 -0
  344. package/package.json +1 -1
  345. package/prisma/migrations/20260701000000_add_access_auth_settings/migration.sql +9 -0
  346. package/prisma/migrations/20260713000000_add_work_request_start_retry/migration.sql +7 -0
  347. package/prisma/schema.prisma +17 -0
  348. package/dist/web/assets/CreateTaskInput-Cb3aRHr3.js +0 -1
  349. package/dist/web/assets/GeneralSettingsPage-CE7P3t_v.js +0 -1
  350. package/dist/web/assets/McpSettingsPage-gp1XDMUd.js +0 -1
  351. package/dist/web/assets/NotificationSettingsPage-BhZeHEef.js +0 -1
  352. package/dist/web/assets/ProfileSettingsPage-D_y3ePFs.js +0 -3
  353. package/dist/web/assets/ProjectSettingsPage-V38j8T61.js +0 -2
  354. package/dist/web/assets/channel-CWEYGfCJ.js +0 -1
  355. package/dist/web/assets/check-D2WWIhwV.js +0 -1
  356. package/dist/web/assets/classDiagram-4FO5ZUOK-CcU8zfK8.js +0 -1
  357. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-CcU8zfK8.js +0 -1
  358. package/dist/web/assets/confirm-dialog-BAqfLDT8.js +0 -1
  359. package/dist/web/assets/folder-picker-DFB9_pv4.js +0 -1
  360. package/dist/web/assets/folder-u7fKAtz_.js +0 -1
  361. package/dist/web/assets/index-7nw4fV42.js +0 -122
  362. package/dist/web/assets/index-YCjjSSLC.css +0 -1
  363. package/dist/web/assets/input-CKwRYuUJ.js +0 -1
  364. 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: 'QUEUED',
455
+ status: 'FAILED',
456
+ lastStartError: expect.stringContaining('MEMBER_NOT_FOUND'),
447
457
  });
448
- await expect(prisma.agentInvocation.count({ where: { teamRunId: teamRun.id } })).resolves.toBe(0);
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: 'STARTED',
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: 'STARTED',
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: 'STARTED',
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: 'STARTED',
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('marks invocation and session failed and releases locks when session start fails', async () => {
2404
- const { teamRun, members } = await createTeamRunFixture({
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: createSessionManagerMock({ failStart: true }),
2429
+ sessionManager,
2415
2430
  getProviderById: createProviderLookup(),
2431
+ now: () => now,
2416
2432
  });
2417
- await expect(service.startNextSessions(teamRun.id)).rejects.toThrow('session start failed');
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