agent-tower 0.7.3 → 0.7.4-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/git/worktree.manager.d.ts +2 -0
- package/dist/git/worktree.manager.d.ts.map +1 -1
- package/dist/git/worktree.manager.js +29 -8
- package/dist/git/worktree.manager.js.map +1 -1
- package/dist/git/worktree.manager.test.js +38 -0
- package/dist/git/worktree.manager.test.js.map +1 -1
- package/dist/mcp/http-client.d.ts +3 -1
- package/dist/mcp/http-client.d.ts.map +1 -1
- package/dist/mcp/http-client.js +6 -0
- package/dist/mcp/http-client.js.map +1 -1
- package/dist/mcp/tools/__tests__/tasks.test.js +33 -1
- package/dist/mcp/tools/__tests__/tasks.test.js.map +1 -1
- package/dist/mcp/tools/tasks.d.ts.map +1 -1
- package/dist/mcp/tools/tasks.js +30 -2
- package/dist/mcp/tools/tasks.js.map +1 -1
- package/dist/mcp/types.d.ts +24 -0
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js +12 -1
- package/dist/mcp/types.js.map +1 -1
- package/dist/prompts/task-handoff.d.ts +6 -0
- package/dist/prompts/task-handoff.d.ts.map +1 -0
- package/dist/prompts/task-handoff.js +17 -0
- package/dist/prompts/task-handoff.js.map +1 -0
- package/dist/prompts/task-handoff.test.d.ts +2 -0
- package/dist/prompts/task-handoff.test.d.ts.map +1 -0
- package/dist/prompts/task-handoff.test.js +27 -0
- package/dist/prompts/task-handoff.test.js.map +1 -0
- package/dist/prompts/workspace-background-service-policy.d.ts +6 -0
- package/dist/prompts/workspace-background-service-policy.d.ts.map +1 -1
- package/dist/prompts/workspace-background-service-policy.js +2 -1
- package/dist/prompts/workspace-background-service-policy.js.map +1 -1
- package/dist/routes/__tests__/task-handoff.test.d.ts +2 -0
- package/dist/routes/__tests__/task-handoff.test.d.ts.map +1 -0
- package/dist/routes/__tests__/task-handoff.test.js +185 -0
- package/dist/routes/__tests__/task-handoff.test.js.map +1 -0
- package/dist/routes/__tests__/tasks.test.js +23 -0
- package/dist/routes/__tests__/tasks.test.js.map +1 -1
- package/dist/routes/tasks.d.ts.map +1 -1
- package/dist/routes/tasks.js +59 -2
- package/dist/routes/tasks.js.map +1 -1
- package/dist/services/__tests__/session-manager.lifecycle.test.js +171 -1
- package/dist/services/__tests__/session-manager.lifecycle.test.js.map +1 -1
- package/dist/services/__tests__/task.service.test.js +99 -76
- package/dist/services/__tests__/task.service.test.js.map +1 -1
- package/dist/services/__tests__/workspace-background-service.service.test.js +74 -26
- package/dist/services/__tests__/workspace-background-service.service.test.js.map +1 -1
- package/dist/services/__tests__/workspace.service.test.js +36 -0
- package/dist/services/__tests__/workspace.service.test.js.map +1 -1
- package/dist/services/message-delivery.service.d.ts +2 -0
- package/dist/services/message-delivery.service.d.ts.map +1 -1
- package/dist/services/project.service.d.ts +2 -0
- package/dist/services/project.service.d.ts.map +1 -1
- package/dist/services/session-manager.d.ts +8 -1
- package/dist/services/session-manager.d.ts.map +1 -1
- package/dist/services/session-manager.js +18 -2
- package/dist/services/session-manager.js.map +1 -1
- package/dist/services/task-cleanup.service.d.ts +2 -0
- package/dist/services/task-cleanup.service.d.ts.map +1 -1
- package/dist/services/task-cleanup.service.js +2 -2
- package/dist/services/task-cleanup.service.js.map +1 -1
- package/dist/services/task-handoff.service.d.ts +18 -0
- package/dist/services/task-handoff.service.d.ts.map +1 -0
- package/dist/services/task-handoff.service.js +87 -0
- package/dist/services/task-handoff.service.js.map +1 -0
- package/dist/services/task.service.d.ts +35 -10
- package/dist/services/task.service.d.ts.map +1 -1
- package/dist/services/task.service.js +210 -168
- package/dist/services/task.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-task-input.service.d.ts +2 -0
- package/dist/services/team-task-input.service.d.ts.map +1 -1
- package/dist/services/workspace-background-service.service.d.ts +1 -3
- package/dist/services/workspace-background-service.service.d.ts.map +1 -1
- package/dist/services/workspace-background-service.service.js +17 -65
- package/dist/services/workspace-background-service.service.js.map +1 -1
- package/dist/services/workspace.service.d.ts +10 -0
- package/dist/services/workspace.service.d.ts.map +1 -1
- package/dist/services/workspace.service.js +50 -7
- package/dist/services/workspace.service.js.map +1 -1
- package/dist/web/assets/AgentDemoPage-JXetlNBf.js +1 -0
- package/dist/web/assets/{AgentEnvironmentSettingsPage-CvvkMKT1.js → AgentEnvironmentSettingsPage-CdYyUCwJ.js} +1 -1
- package/dist/web/assets/AttachmentPreview-DMS-PM-1.js +2 -0
- package/dist/web/assets/{BranchSelect-Cv92vEjG.js → BranchSelect-BAq4L2ZS.js} +1 -1
- package/dist/web/assets/BrandLogo--kWXXJtZ.js +1 -0
- package/dist/web/assets/ConversationPage-CvC4RyB5.js +1 -0
- package/dist/web/assets/CreateTaskInput-DjVxCAfc.js +1 -0
- package/dist/web/assets/{DemoPage-CpYNBQhA.js → DemoPage-cDVTGOp8.js} +3 -3
- package/dist/web/assets/{DesignSystemPage-DLeH6DnV.js → DesignSystemPage-BI54IkKi.js} +3 -3
- package/dist/web/assets/{ExecutionPermissionSelector-IoWR9YLb.js → ExecutionPermissionSelector-f1UYIm1z.js} +1 -1
- package/dist/web/assets/{GeneralSettingsPage-BAYgxCqx.js → GeneralSettingsPage-C0yto6C_.js} +1 -1
- package/dist/web/assets/{LoadingPreviewPage-CumrzyxW.js → LoadingPreviewPage-BiJSaHUi.js} +1 -1
- package/dist/web/assets/{McpSettingsPage-CFNwxT9Y.js → McpSettingsPage-BbHwZtyj.js} +1 -1
- package/dist/web/assets/{MemberAvatar-BVXD0jTy.js → MemberAvatar-BLezO3vS.js} +1 -1
- package/dist/web/assets/MobileTaskDetail-M_Kke9rL.js +3 -0
- package/dist/web/assets/{NotificationSettingsPage-B0s5YbG3.js → NotificationSettingsPage-CiKoL2Kv.js} +1 -1
- package/dist/web/assets/{ProfileSettingsPage-BuhoF-mL.js → ProfileSettingsPage-DCB48cPS.js} +2 -2
- package/dist/web/assets/ProjectKanbanPage-soDqevjQ.js +12 -0
- package/dist/web/assets/{ProjectSettingsPage-DDMg6axy.js → ProjectSettingsPage-COvIVq1h.js} +2 -2
- package/dist/web/assets/{ProviderSettingsPage-DRt9m4mF.js → ProviderSettingsPage-CPuJcZDg.js} +2 -2
- package/dist/web/assets/{SettingsMasterDetail-D7vStQjR.js → SettingsMasterDetail-Cv7YaRJB.js} +1 -1
- package/dist/web/assets/TaskDetail-CqhPxvg3.js +1 -0
- package/dist/web/assets/TaskHandoffPanel-CxH3Mgyd.js +70 -0
- package/dist/web/assets/TeamSettingsPage-D-XDefNd.js +1 -0
- package/dist/web/assets/{TranslationSettingsPage-D2oks6OM.js → TranslationSettingsPage-BPHI-SZW.js} +1 -1
- package/dist/web/assets/{arc-vYWCZsE_.js → arc-CC81aLuf.js} +1 -1
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-Cu1VIpcJ.js → architectureDiagram-3BPJPVTR-DL08XGaN.js} +1 -1
- package/dist/web/assets/{at-sign-BhSPafOS.js → at-sign-DYPIlD6D.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-C4x_nj5S.js → blockDiagram-GPEHLZMM-B4IW5WxI.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-jHOo5lal.js → c4Diagram-AAUBKEIU-Cj4d_FqK.js} +1 -1
- package/dist/web/assets/channel-CUa8oyzC.js +1 -0
- package/dist/web/assets/check-DaZqOL8l.js +1 -0
- package/dist/web/assets/{chevron-down-BoS08pMa.js → chevron-down-DNhYsNAt.js} +1 -1
- package/dist/web/assets/{chevron-up-BWtKfdKT.js → chevron-up-CjrLieBR.js} +1 -1
- package/dist/web/assets/{chip-Ck4dqxR3.js → chip-Cf92J-pQ.js} +1 -1
- package/dist/web/assets/{chunk-2J33WTMH-BZcpjmfm.js → chunk-2J33WTMH-DeCj8QSD.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-DZGB3q-I.js → chunk-4BX2VUAB-k-6rzQmQ.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-CcNrOmyy.js → chunk-55IACEB6-qMTCQ5bX.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-dWrxio8O.js → chunk-727SXJPM-CMnu18d1.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-K4gkVzqE.js → chunk-AQP2D5EJ-C0mFbUMn.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-CnHyY4qD.js → chunk-FMBD7UC4-u6-_M137.js} +1 -1
- package/dist/web/assets/{chunk-ND2GUHAM-CHUmcyDv.js → chunk-ND2GUHAM-DHrIE_8H.js} +1 -1
- package/dist/web/assets/{chunk-QZHKN3VN-Bzzjd-Qt.js → chunk-QZHKN3VN-aTyC0wZq.js} +1 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-BAx111NO.js +1 -0
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-BAx111NO.js +1 -0
- package/dist/web/assets/{code-block-OCS4YCEC-B2hHBO_G.js → code-block-OCS4YCEC-CBIOQOMH.js} +1 -1
- package/dist/web/assets/confirm-dialog-DWeBZLJW.js +1 -0
- package/dist/web/assets/{content-D0qmHfHK.js → content-JM9E1MeD.js} +1 -1
- package/dist/web/assets/{copy-C4WnOY6Y.js → copy-D0gl9kF8.js} +1 -1
- package/dist/web/assets/{cose-bilkent-S5V4N54A-Dcn8tOhb.js → cose-bilkent-S5V4N54A-xLyliSgU.js} +1 -1
- package/dist/web/assets/cssMode-DO5UnQ64.js +1 -0
- package/dist/web/assets/{dagre-BM42HDAG-DWBV4UfP.js → dagre-BM42HDAG-CbNpoO2d.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-CnNTJQ3q.js → diagram-2AECGRRQ-CG3hKxAX.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-DlXJFzDF.js → diagram-5GNKFQAL-mQu9M28s.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-DZ22YV4n.js → diagram-KO2AKTUF-BSeEp_Uw.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-B4pQJjG9.js → diagram-LMA3HP47-D6yRdao-.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-DJBLjtgL.js → diagram-OG6HWLK6-BUBUW9wD.js} +1 -1
- package/dist/web/assets/{dialog-1o8KQWoQ.js → dialog-D1lkhqVL.js} +2 -2
- package/dist/web/assets/{erDiagram-TEJ5UH35-CDsfoJUT.js → erDiagram-TEJ5UH35-BQSZgRqq.js} +1 -1
- package/dist/web/assets/{file-6qwbIga1.js → file-BijjwxRR.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-CuGOAYmY.js → flowDiagram-I6XJVG4X-D-eqyr5Y.js} +1 -1
- package/dist/web/assets/{folder-picker-Cj1VZOZU.js → folder-picker-Czxm0_S9.js} +1 -1
- package/dist/web/assets/{freemarker2-BG8VyI2V.js → freemarker2-B4RvEa_U.js} +2 -2
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-BjrSOqAM.js → ganttDiagram-6RSMTGT7-BWGkCt_x.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-4VVr4F8_.js → gitGraphDiagram-PVQCEYII-DGVc49pR.js} +1 -1
- package/dist/web/assets/{globe-CxeM27yy.js → globe-Ce01-45x.js} +1 -1
- package/dist/web/assets/handlebars-oyT7drqS.js +1 -0
- package/dist/web/assets/html-CxxWf2Qk.js +1 -0
- package/dist/web/assets/htmlMode-RfAHh5Jj.js +1 -0
- package/dist/web/assets/index-BMZFF1UJ.js +1 -0
- package/dist/web/assets/index-BN53vGxp.css +1 -0
- package/dist/web/assets/index-Cc3qXMhU.js +122 -0
- package/dist/web/assets/{index-DVdiOmqt.js → index-DWrSDMX2.js} +1 -1
- package/dist/web/assets/{index-CrKx8Yaz.js → index-Dh-tgeaL.js} +4 -4
- package/dist/web/assets/{index-hHrknzh7.js → index-Disv1kQS.js} +1 -1
- package/dist/web/assets/{index-Da2mWAtm.js → index-DyJfPc4O.js} +1 -1
- package/dist/web/assets/{index-cu8sedJH.js → index-Pt4TsHjm.js} +1 -1
- package/dist/web/assets/{index-C1lMEjmZ.js → index-T38iVLEb.js} +1 -1
- package/dist/web/assets/{index-Db28hRe8.js → index-hpHE1jXY.js} +1 -1
- package/dist/web/assets/{infoDiagram-5YYISTIA-Cy9ntb6t.js → infoDiagram-5YYISTIA-DLPs3dV_.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-D6UiVzRz.js → ishikawaDiagram-YF4QCWOH-d_OrnwDY.js} +1 -1
- package/dist/web/assets/javascript-BNXtViSL.js +1 -0
- package/dist/web/assets/{journeyDiagram-JHISSGLW-owdKzZoh.js → journeyDiagram-JHISSGLW-EtU1st_0.js} +1 -1
- package/dist/web/assets/jsonMode-Bqh5vIxx.js +7 -0
- package/dist/web/assets/{kanban-definition-UN3LZRKU-v5TBnyp0.js → kanban-definition-UN3LZRKU-ZWzUnsFK.js} +1 -1
- package/dist/web/assets/{kbd-Cl4AzzJp.js → kbd-IjDGsZ2B.js} +1 -1
- package/dist/web/assets/{key-round-CnYjSrCz.js → key-round-CbnAuUxp.js} +1 -1
- package/dist/web/assets/{layers-XImbMX-P.js → layers-Pdy9vZ6K.js} +1 -1
- package/dist/web/assets/{linear-DNlKYt8R.js → linear-D7n23wcA.js} +1 -1
- package/dist/web/assets/{link-DQk-Vapq.js → link-Bl1ZbjFh.js} +1 -1
- package/dist/web/assets/liquid-Ds47iVLN.js +1 -0
- package/dist/web/assets/{lspLanguageFeatures-DD_y1bZa.js → lspLanguageFeatures-Dcch3bsp.js} +1 -1
- package/dist/web/assets/mdx-CM7ky-2B.js +1 -0
- package/dist/web/assets/{menu-DXF6iUsq.js → menu-DubntFHq.js} +1 -1
- package/dist/web/assets/{mermaid-NOHMQCX5-CG_LpwAI.js → mermaid-NOHMQCX5-nBsnjgeU.js} +49 -49
- package/dist/web/assets/{message-square-BviHdb67.js → message-square-BE-pW0m_.js} +1 -1
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-Bn_rnnGT.js → mindmap-definition-RKZ34NQL-ClHORNTt.js} +1 -1
- package/dist/web/assets/modal-CHLtZWvx.js +1 -0
- package/dist/web/assets/{monaco-yuGji-2E.js → monaco-BbAmalE2.js} +3 -3
- package/dist/web/assets/nav-row-DH7rNq1V.js +1 -0
- package/dist/web/assets/{paperclip-D9ka2IAB.js → paperclip-YP3Bk1lI.js} +1 -1
- package/dist/web/assets/{pencil-isP1rXC-.js → pencil-BFDuhbtW.js} +1 -1
- package/dist/web/assets/{people-CbmH8m4i.js → people-DEeNaaLt.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-DIE_f9W_.js → pieDiagram-4H26LBE5-BUOaKIyP.js} +1 -1
- package/dist/web/assets/{popover-D8VtO5qF.js → popover-CUmBEXxL.js} +1 -1
- package/dist/web/assets/python-BODTtU6m.js +1 -0
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-CavFxJCk.js → quadrantDiagram-W4KKPZXB-Caezg-U2.js} +1 -1
- package/dist/web/assets/razor-CHm8DY1p.js +1 -0
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-Dm1FwUUh.js → requirementDiagram-4Y6WPE33-BtdP3Ka6.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-qeqcO27M.js → sankeyDiagram-5OEKKPKP-Bv74oA-n.js} +1 -1
- package/dist/web/assets/{search-DOMr5jPS.js → search-ClmOQd4H.js} +1 -1
- package/dist/web/assets/{select-CzZNbe1Z.js → select-BB7QG08o.js} +1 -1
- package/dist/web/assets/{select-Cw5BaOLQ.js → select-CYtYPr-c.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-Dqtb3BYG.js → sequenceDiagram-3UESZ5HK-DuNPl-dg.js} +1 -1
- package/dist/web/assets/{server-u-PC-CcX.js → server-bEB_DY55.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-C_MqF9Fy.js → stateDiagram-AJRCARHV-B72HAgBJ.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-Bu3l_CgB.js +1 -0
- package/dist/web/assets/{textarea-xt2hLb-d.js → textarea-BqYPvoO-.js} +1 -1
- package/dist/web/assets/{timeline-definition-PNZ67QCA-Bzov6t31.js → timeline-definition-PNZ67QCA-DTmHUgmQ.js} +1 -1
- package/dist/web/assets/{toggles-B6E1UBYN.js → toggles-a5fkwT_D.js} +1 -1
- package/dist/web/assets/tooltip-DuyJMci9.js +1 -0
- package/dist/web/assets/{track-BeqaPopG.js → track-DCjyrv3n.js} +1 -1
- package/dist/web/assets/{trash-2-5jI3_KHh.js → trash-2-CIyBqaVy.js} +1 -1
- package/dist/web/assets/tsMode-C1UXAkMh.js +11 -0
- package/dist/web/assets/typescript-Biht2wHb.js +1 -0
- package/dist/web/assets/{upload-DUteazR7.js → upload-D05XS5-G.js} +1 -1
- package/dist/web/assets/use-profiles-CrWTyz1G.js +1 -0
- package/dist/web/assets/{use-providers-CQwBj9hp.js → use-providers-Dv1Nk6s7.js} +1 -1
- package/dist/web/assets/{use-translation-settings-DBzFqP-d.js → use-translation-settings-CcUnipVO.js} +1 -1
- package/dist/web/assets/{useNormalizedLogs-DsdwmfS7.js → useNormalizedLogs-D9W1cT4R.js} +1 -1
- package/dist/web/assets/useTokenUsage-CZ5mDshD.js +1 -0
- package/dist/web/assets/{vennDiagram-CIIHVFJN-BqxICvT_.js → vennDiagram-CIIHVFJN-ByoNydXe.js} +1 -1
- package/dist/web/assets/{wardley-L42UT6IY-BTdLPFcA.js → wardley-L42UT6IY-BqdMkOLl.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-icnFsFn4.js → wardleyDiagram-YWT4CUSO-AO22HrEF.js} +1 -1
- package/dist/web/assets/xml-NLGqNFDv.js +1 -0
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-BN2_ducK.js → xychartDiagram-2RQKCTM6-vzPgjgqW.js} +1 -1
- package/dist/web/assets/yaml-AWlGS_Ig.js +1 -0
- package/dist/web/index.html +2 -2
- package/node_modules/@agent-tower/shared/dist/index.d.ts +1 -0
- package/node_modules/@agent-tower/shared/dist/index.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/index.js +1 -0
- package/node_modules/@agent-tower/shared/dist/index.js.map +1 -1
- package/node_modules/@agent-tower/shared/dist/task-handoff.d.ts +12 -0
- package/node_modules/@agent-tower/shared/dist/task-handoff.d.ts.map +1 -0
- package/node_modules/@agent-tower/shared/dist/task-handoff.js +2 -0
- package/node_modules/@agent-tower/shared/dist/task-handoff.js.map +1 -0
- package/node_modules/@agent-tower/shared/dist/types.d.ts +16 -1
- package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/20261023010000_add_subtasks/migration.sql +2 -0
- package/prisma/migrations/20261026000000_add_task_handoff/migration.sql +1 -0
- package/prisma/schema.prisma +5 -0
- package/dist/web/assets/AgentDemoPage-Dwux6TyV.js +0 -1
- package/dist/web/assets/AttachmentPreview-CFJ6MZ1f.js +0 -2
- package/dist/web/assets/BrandLogo-H4nCbVzs.js +0 -1
- package/dist/web/assets/ConversationPage-CZzGME21.js +0 -1
- package/dist/web/assets/CreateTaskInput-BM2tx4hJ.js +0 -1
- package/dist/web/assets/EditableTaskTitle-BEsiULnD.js +0 -70
- package/dist/web/assets/MobileTaskDetail-IOQ1WnS8.js +0 -3
- package/dist/web/assets/ProjectKanbanPage-DMtv15rq.js +0 -12
- package/dist/web/assets/TaskDetail-DzKbs5Jt.js +0 -1
- package/dist/web/assets/TeamSettingsPage-fjlaqaLo.js +0 -1
- package/dist/web/assets/channel-D1cEj22-.js +0 -1
- package/dist/web/assets/check-D_VFGpIy.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-DIrDMefa.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-DIrDMefa.js +0 -1
- package/dist/web/assets/confirm-dialog-CL_1pF4K.js +0 -1
- package/dist/web/assets/cssMode-CXBMyfYY.js +0 -1
- package/dist/web/assets/handlebars-Ci38FPsA.js +0 -1
- package/dist/web/assets/html-5hdYEgVF.js +0 -1
- package/dist/web/assets/htmlMode-BzU9qsw0.js +0 -1
- package/dist/web/assets/icon-button-BmrJtYFC.js +0 -1
- package/dist/web/assets/index-BR3Wg2fS.js +0 -122
- package/dist/web/assets/index-DSy5JLC1.js +0 -1
- package/dist/web/assets/index-DZLg4-xM.css +0 -1
- package/dist/web/assets/javascript-Ib060GQ8.js +0 -1
- package/dist/web/assets/jsonMode-CLmu6gzc.js +0 -7
- package/dist/web/assets/liquid-Bc7OSAPR.js +0 -1
- package/dist/web/assets/mdx-CxJNHHTn.js +0 -1
- package/dist/web/assets/modal-D496f1fE.js +0 -1
- package/dist/web/assets/python-C0E43gUo.js +0 -1
- package/dist/web/assets/razor-xarhYR9V.js +0 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-BxmbGAhh.js +0 -1
- package/dist/web/assets/tooltip-C8--8CNX.js +0 -1
- package/dist/web/assets/tsMode-uT3sMVHt.js +0 -11
- package/dist/web/assets/typescript-Dkdj2ks0.js +0 -1
- package/dist/web/assets/use-profiles-T3saKiBg.js +0 -1
- package/dist/web/assets/useTokenUsage-Drh3v3kx.js +0 -1
- package/dist/web/assets/xml-BO8gYu0L.js +0 -1
- package/dist/web/assets/yaml-DlovbQzw.js +0 -1
- /package/dist/web/assets/{EditableTaskTitle-DcpNTP1B.css → TaskHandoffPanel-DcpNTP1B.css} +0 -0
|
@@ -3,7 +3,7 @@ export { buildTextPreview } from '../utils/text-preview.js';
|
|
|
3
3
|
import { prisma } from '../utils/index.js';
|
|
4
4
|
import { TaskStatus, SessionStatus, SessionPurpose, WorkspaceKind, WorkspaceStatus } from '../types/index.js';
|
|
5
5
|
import { getWorkspaceWorkingDir } from './workspace-kind.js';
|
|
6
|
-
import { NotFoundError, ValidationError, InvalidStateTransitionError, } from '../errors.js';
|
|
6
|
+
import { NotFoundError, ServiceError, ValidationError, InvalidStateTransitionError, } from '../errors.js';
|
|
7
7
|
import { defaultWorkspaceLifecycleBarrier } from './workspace-lifecycle-barrier.js';
|
|
8
8
|
import { ensureProjectIsMutable, getStoredProjectGitCapability, detectProjectGitCapability } from './project-guards.js';
|
|
9
9
|
import { requireLocalBaseBranch } from '../git/base-branch.js';
|
|
@@ -11,6 +11,12 @@ import { defaultTeamLockService } from './team-lock.service.js';
|
|
|
11
11
|
import { TaskPriority } from '@agent-tower/shared';
|
|
12
12
|
import prismaPkg from '@prisma/client';
|
|
13
13
|
const { Prisma } = prismaPkg;
|
|
14
|
+
const taskBoardColumns = Prisma.sql `
|
|
15
|
+
task."id", task."projectId", task."title", task."status", task."priority",
|
|
16
|
+
task."position", task."updatedAt", task."parentTaskId",
|
|
17
|
+
(SELECT COUNT(*) FROM "Task" child WHERE child."parentTaskId" = task."id" AND child."deletedAt" IS NULL) AS "subtaskCount",
|
|
18
|
+
(SELECT COUNT(*) FROM "Task" child WHERE child."parentTaskId" = task."id" AND child."deletedAt" IS NULL AND child."status" = 'IN_PROGRESS') AS "runningSubtaskCount"
|
|
19
|
+
`;
|
|
14
20
|
/**
|
|
15
21
|
* 合法的状态流转规则
|
|
16
22
|
* 看板拖拽场景下允许任意状态互转,状态变更无危险副作用
|
|
@@ -211,13 +217,17 @@ function normalizeUpdateTaskInput(current, input) {
|
|
|
211
217
|
]),
|
|
212
218
|
};
|
|
213
219
|
}
|
|
220
|
+
function withoutTaskHandoff(task) {
|
|
221
|
+
const { handoff: _handoff, ...metadata } = task;
|
|
222
|
+
return metadata;
|
|
223
|
+
}
|
|
214
224
|
function withTaskPreviews(task, options = {}) {
|
|
215
225
|
const titlePreview = buildTextPreview(task.title, TASK_TITLE_MAX_LENGTH);
|
|
216
226
|
const contentPreview = buildTextPreview(task.description);
|
|
217
227
|
const isTruncated = task.title !== titlePreview
|
|
218
228
|
|| Boolean(task.description && task.description !== contentPreview);
|
|
219
229
|
const base = {
|
|
220
|
-
...task,
|
|
230
|
+
...withoutTaskHandoff(task),
|
|
221
231
|
title: titlePreview,
|
|
222
232
|
titlePreview,
|
|
223
233
|
contentPreview: contentPreview || undefined,
|
|
@@ -235,13 +245,15 @@ function withProjectGitMetadata(task) {
|
|
|
235
245
|
return withProjectGitCapability(task, capability);
|
|
236
246
|
}
|
|
237
247
|
function withProjectGitCapability(task, capability) {
|
|
238
|
-
|
|
248
|
+
const result = {
|
|
239
249
|
...task,
|
|
240
250
|
project: {
|
|
241
251
|
...task.project,
|
|
242
252
|
...capability,
|
|
243
253
|
},
|
|
244
254
|
};
|
|
255
|
+
delete result.handoff;
|
|
256
|
+
return result;
|
|
245
257
|
}
|
|
246
258
|
function buildProjectGitMetadata(project) {
|
|
247
259
|
return getStoredProjectGitCapability(project);
|
|
@@ -275,30 +287,28 @@ export class TaskService {
|
|
|
275
287
|
deletedAt: null,
|
|
276
288
|
project: { archivedAt: null },
|
|
277
289
|
...(params.projectId ? { projectId: params.projectId } : {}),
|
|
290
|
+
...(params.parentTaskId ? { parentTaskId: params.parentTaskId } : {}),
|
|
278
291
|
...(params.status ? { status: params.status } : {}),
|
|
279
292
|
};
|
|
280
293
|
const projectFilter = params.projectId
|
|
281
294
|
? Prisma.sql `AND task."projectId" = ${params.projectId}`
|
|
282
295
|
: Prisma.empty;
|
|
296
|
+
const parentFilter = params.parentTaskId
|
|
297
|
+
? Prisma.sql `AND task."parentTaskId" = ${params.parentTaskId}`
|
|
298
|
+
: Prisma.empty;
|
|
283
299
|
const statusFilter = params.status
|
|
284
300
|
? Prisma.sql `AND task."status" = ${params.status}`
|
|
285
301
|
: Prisma.empty;
|
|
286
302
|
const [tasks, total] = await Promise.all([
|
|
287
303
|
prisma.$queryRaw(Prisma.sql `
|
|
288
|
-
SELECT
|
|
289
|
-
task."id" AS "id",
|
|
290
|
-
task."projectId" AS "projectId",
|
|
291
|
-
task."title" AS "title",
|
|
292
|
-
task."status" AS "status",
|
|
293
|
-
task."priority" AS "priority",
|
|
294
|
-
task."position" AS "position",
|
|
295
|
-
task."updatedAt" AS "updatedAt"
|
|
304
|
+
SELECT ${taskBoardColumns}
|
|
296
305
|
FROM "Task" task
|
|
297
306
|
INNER JOIN "Project" project ON project."id" = task."projectId"
|
|
298
307
|
WHERE task."deletedAt" IS NULL
|
|
299
308
|
AND project."archivedAt" IS NULL
|
|
300
309
|
${projectFilter}
|
|
301
310
|
${statusFilter}
|
|
311
|
+
${parentFilter}
|
|
302
312
|
ORDER BY
|
|
303
313
|
CASE task."status"
|
|
304
314
|
WHEN ${TaskStatus.IN_PROGRESS} THEN 0
|
|
@@ -315,7 +325,19 @@ export class TaskService {
|
|
|
315
325
|
`),
|
|
316
326
|
prisma.task.count({ where }),
|
|
317
327
|
]);
|
|
318
|
-
|
|
328
|
+
// A child can rank ahead of its completed parent or fall on another page.
|
|
329
|
+
// Return compact parent context without changing the page's count/limit.
|
|
330
|
+
const pageIds = new Set(tasks.map((task) => task.id));
|
|
331
|
+
const missingParentIds = [...new Set(tasks.flatMap((task) => task.parentTaskId && !pageIds.has(task.parentTaskId) ? [task.parentTaskId] : []))];
|
|
332
|
+
const parents = params.parentTaskId || missingParentIds.length === 0 ? []
|
|
333
|
+
: await prisma.$queryRaw(Prisma.sql `
|
|
334
|
+
SELECT ${taskBoardColumns} FROM "Task" task
|
|
335
|
+
INNER JOIN "Project" project ON project."id" = task."projectId"
|
|
336
|
+
WHERE task."id" IN (${Prisma.join(missingParentIds)})
|
|
337
|
+
AND task."deletedAt" IS NULL AND project."archivedAt" IS NULL
|
|
338
|
+
`);
|
|
339
|
+
const allTasks = [...tasks, ...parents];
|
|
340
|
+
const taskIds = allTasks.map((task) => task.id);
|
|
319
341
|
const workspaceRows = taskIds.length === 0
|
|
320
342
|
? []
|
|
321
343
|
: await prisma.$queryRaw(Prisma.sql `
|
|
@@ -363,7 +385,7 @@ export class TaskService {
|
|
|
363
385
|
workspacesByTaskId.set(workspace.taskId, group);
|
|
364
386
|
}
|
|
365
387
|
const preferredWorkspaceByTaskId = new Map();
|
|
366
|
-
for (const task of
|
|
388
|
+
for (const task of allTasks) {
|
|
367
389
|
const candidates = workspacesByTaskId.get(task.id) ?? [];
|
|
368
390
|
const preferred = candidates.find((workspace) => workspace.status === WorkspaceStatus.ACTIVE)
|
|
369
391
|
?? candidates[0];
|
|
@@ -371,7 +393,7 @@ export class TaskService {
|
|
|
371
393
|
preferredWorkspaceByTaskId.set(task.id, preferred);
|
|
372
394
|
}
|
|
373
395
|
}
|
|
374
|
-
const data =
|
|
396
|
+
const data = allTasks.map((task) => {
|
|
375
397
|
const titlePreview = buildTextPreview(task.title, TASK_TITLE_MAX_LENGTH);
|
|
376
398
|
const preferredWorkspace = preferredWorkspaceByTaskId.get(task.id);
|
|
377
399
|
const hasRunningSession = (workspacesByTaskId.get(task.id) ?? [])
|
|
@@ -382,6 +404,9 @@ export class TaskService {
|
|
|
382
404
|
return {
|
|
383
405
|
id: task.id,
|
|
384
406
|
projectId: task.projectId,
|
|
407
|
+
parentTaskId: task.parentTaskId,
|
|
408
|
+
subtaskCount: Number(task.subtaskCount),
|
|
409
|
+
runningSubtaskCount: Number(task.runningSubtaskCount),
|
|
385
410
|
title: titlePreview,
|
|
386
411
|
status: task.status,
|
|
387
412
|
priority: task.priority ?? TaskPriority.NORMAL,
|
|
@@ -401,7 +426,8 @@ export class TaskService {
|
|
|
401
426
|
};
|
|
402
427
|
});
|
|
403
428
|
return {
|
|
404
|
-
data,
|
|
429
|
+
data: data.slice(0, tasks.length),
|
|
430
|
+
...(parents.length ? { parents: data.slice(tasks.length) } : {}),
|
|
405
431
|
total,
|
|
406
432
|
page,
|
|
407
433
|
limit,
|
|
@@ -443,6 +469,7 @@ export class TaskService {
|
|
|
443
469
|
projectId: true,
|
|
444
470
|
title: true,
|
|
445
471
|
baseBranch: true,
|
|
472
|
+
parentTaskId: true,
|
|
446
473
|
status: true,
|
|
447
474
|
priority: true,
|
|
448
475
|
position: true,
|
|
@@ -490,6 +517,8 @@ export class TaskService {
|
|
|
490
517
|
project: true,
|
|
491
518
|
title: true,
|
|
492
519
|
baseBranch: true,
|
|
520
|
+
parentTaskId: true,
|
|
521
|
+
_count: { select: { subtasks: { where: { deletedAt: null } } } },
|
|
493
522
|
status: true,
|
|
494
523
|
priority: true,
|
|
495
524
|
position: true,
|
|
@@ -501,7 +530,14 @@ export class TaskService {
|
|
|
501
530
|
if (!task) {
|
|
502
531
|
throw new NotFoundError('Task', id);
|
|
503
532
|
}
|
|
504
|
-
|
|
533
|
+
const { _count, ...metadata } = task;
|
|
534
|
+
return withTaskPreviews(withProjectGitMetadata({ ...metadata, subtaskCount: _count.subtasks }), { omitDescription: true });
|
|
535
|
+
}
|
|
536
|
+
async findSubtasks(id, params = {}) {
|
|
537
|
+
const parent = await prisma.task.findFirst({ where: { id, deletedAt: null } });
|
|
538
|
+
if (!parent)
|
|
539
|
+
throw new NotFoundError('Task', id);
|
|
540
|
+
return this.findBoard({ ...params, projectId: parent.projectId, parentTaskId: id });
|
|
505
541
|
}
|
|
506
542
|
async findBodyById(id) {
|
|
507
543
|
const task = await prisma.task.findFirst({
|
|
@@ -554,37 +590,73 @@ export class TaskService {
|
|
|
554
590
|
throw new NotFoundError('Project', projectId);
|
|
555
591
|
}
|
|
556
592
|
ensureProjectIsMutable(project, 'create tasks');
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
593
|
+
const parent = input.parentTaskId ? await prisma.task.findFirst({
|
|
594
|
+
where: { id: input.parentTaskId, projectId, deletedAt: null },
|
|
595
|
+
include: { workspaces: {
|
|
596
|
+
where: { parentWorkspaceId: null, ownerMemberId: null, status: { not: WorkspaceStatus.ABANDONED } },
|
|
597
|
+
orderBy: { createdAt: 'desc' },
|
|
598
|
+
} },
|
|
599
|
+
}) : null;
|
|
600
|
+
if (input.parentTaskId && !parent)
|
|
601
|
+
throw new NotFoundError('Parent task', input.parentTaskId);
|
|
602
|
+
if (parent?.parentTaskId)
|
|
603
|
+
throw new ValidationError('Subtasks cannot have their own subtasks');
|
|
604
|
+
const parentWorkspace = parent?.workspaces.find((workspace) => workspace.status === WorkspaceStatus.ACTIVE)
|
|
605
|
+
?? parent?.workspaces.find((workspace) => workspace.status === WorkspaceStatus.HIBERNATED)
|
|
606
|
+
?? parent?.workspaces[0];
|
|
607
|
+
const parentBranch = parentWorkspace?.branchName || parent?.baseBranch;
|
|
608
|
+
const createWithParentBranch = async () => {
|
|
609
|
+
// Resolve at task creation so delayed starts and TeamRun main workspaces
|
|
610
|
+
// cannot silently follow a later project default change. Local tasks opt out.
|
|
611
|
+
let baseBranch = null;
|
|
612
|
+
if (input.baseBranch !== null) {
|
|
613
|
+
const requestedBranch = input.baseBranch ?? parentBranch ?? ((await detectProjectGitCapability(project.repoPath)).worktreeReady ? project.mainBranch : null);
|
|
614
|
+
if (requestedBranch !== null) {
|
|
615
|
+
baseBranch = await requireLocalBaseBranch(project.repoPath, requestedBranch);
|
|
616
|
+
}
|
|
564
617
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
618
|
+
// 自动计算 position
|
|
619
|
+
const maxPosition = await prisma.task.aggregate({
|
|
620
|
+
where: { projectId, status: TaskStatus.TODO, deletedAt: null },
|
|
621
|
+
_max: { position: true },
|
|
622
|
+
});
|
|
623
|
+
const created = await prisma.$transaction(async (tx) => {
|
|
624
|
+
// Recheck after Git validation, serialized with the deletion marker.
|
|
625
|
+
if (parent) {
|
|
626
|
+
const liveParent = await tx.task.findFirst({
|
|
627
|
+
where: { id: parent.id, projectId, deletedAt: null, project: { archivedAt: null } },
|
|
628
|
+
});
|
|
629
|
+
if (!liveParent)
|
|
630
|
+
throw new NotFoundError('Parent task', parent.id);
|
|
631
|
+
}
|
|
632
|
+
return tx.task.create({
|
|
633
|
+
data: {
|
|
634
|
+
title: normalizedInput.title,
|
|
635
|
+
description: normalizedInput.description,
|
|
636
|
+
priority: normalizedInput.priority ?? TaskPriority.NORMAL,
|
|
637
|
+
baseBranch,
|
|
638
|
+
parentTaskId: parent?.id,
|
|
639
|
+
position: (maxPosition._max.position ?? 0) + 1,
|
|
640
|
+
projectId,
|
|
641
|
+
},
|
|
642
|
+
});
|
|
643
|
+
});
|
|
644
|
+
this.eventBus.emit('task:updated', { taskId: created.id, projectId, status: created.status });
|
|
645
|
+
if (parent)
|
|
646
|
+
this.eventBus.emit('task:updated', { taskId: parent.id, projectId, status: parent.status });
|
|
647
|
+
return {
|
|
648
|
+
...created,
|
|
649
|
+
project: {
|
|
650
|
+
...project,
|
|
651
|
+
...buildProjectGitMetadata(project),
|
|
652
|
+
},
|
|
653
|
+
};
|
|
587
654
|
};
|
|
655
|
+
// Serialize branch validation and child persistence with parent workspace
|
|
656
|
+
// cleanup, so its reference becomes visible before that branch is reclaimed.
|
|
657
|
+
return parentWorkspace
|
|
658
|
+
? defaultWorkspaceLifecycleBarrier.withWorkspace(parentWorkspace.id, createWithParentBranch)
|
|
659
|
+
: createWithParentBranch();
|
|
588
660
|
}
|
|
589
661
|
/**
|
|
590
662
|
* 更新任务基本信息
|
|
@@ -607,7 +679,7 @@ export class TaskService {
|
|
|
607
679
|
this.emitTaskUpdated(id, task.projectId, updated.status, updated.priority);
|
|
608
680
|
}
|
|
609
681
|
return {
|
|
610
|
-
...updated,
|
|
682
|
+
...withoutTaskHandoff(updated),
|
|
611
683
|
project: {
|
|
612
684
|
...task.project,
|
|
613
685
|
...buildProjectGitMetadata(task.project),
|
|
@@ -652,7 +724,7 @@ export class TaskService {
|
|
|
652
724
|
// 通知前端
|
|
653
725
|
this.emitTaskUpdated(id, task.projectId, status);
|
|
654
726
|
return {
|
|
655
|
-
...updated,
|
|
727
|
+
...withoutTaskHandoff(updated),
|
|
656
728
|
project: {
|
|
657
729
|
...task.project,
|
|
658
730
|
...buildProjectGitMetadata(task.project),
|
|
@@ -689,7 +761,7 @@ export class TaskService {
|
|
|
689
761
|
this.emitTaskUpdated(id, task.projectId, status);
|
|
690
762
|
}
|
|
691
763
|
return {
|
|
692
|
-
...updated,
|
|
764
|
+
...withoutTaskHandoff(updated),
|
|
693
765
|
project: {
|
|
694
766
|
...task.project,
|
|
695
767
|
...buildProjectGitMetadata(task.project),
|
|
@@ -699,149 +771,119 @@ export class TaskService {
|
|
|
699
771
|
/**
|
|
700
772
|
* 快速删除任务
|
|
701
773
|
*
|
|
702
|
-
* 1.
|
|
703
|
-
* 2.
|
|
774
|
+
* 1. 在同一事务中标记父子 Task 为 deletedAt,并保存清理快照
|
|
775
|
+
* 2. 普通读取立即隐藏已删除的任务
|
|
704
776
|
* 3. 通过 EventBus 通知前端实时删除
|
|
705
777
|
* 4. 后台 worker 再停止 Session、删除 worktree/branch,并最终硬删除 Task
|
|
706
778
|
*/
|
|
707
|
-
async delete(id) {
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
where: { id },
|
|
711
|
-
include: { project: true },
|
|
712
|
-
});
|
|
713
|
-
if (!taskForGuard || taskForGuard.deletedAt) {
|
|
779
|
+
async delete(id, expectedSubtaskCount) {
|
|
780
|
+
const taskForGuard = await prisma.task.findUnique({ where: { id }, include: { project: true } });
|
|
781
|
+
if (!taskForGuard || taskForGuard.deletedAt)
|
|
714
782
|
throw new NotFoundError('Task', id);
|
|
715
|
-
}
|
|
716
783
|
ensureProjectIsMutable(taskForGuard.project, 'delete tasks');
|
|
784
|
+
if (expectedSubtaskCount !== undefined && expectedSubtaskCount !== await prisma.task.count({
|
|
785
|
+
where: { parentTaskId: id, deletedAt: null },
|
|
786
|
+
})) {
|
|
787
|
+
throw new ServiceError('Subtasks changed. Review the deletion confirmation again.', 'TASK_DELETE_SCOPE_CHANGED', 409);
|
|
788
|
+
}
|
|
717
789
|
const workspaceIds = await prisma.workspace.findMany({
|
|
718
|
-
where: {
|
|
790
|
+
where: { task: { OR: [{ id }, { parentTaskId: id }], deletedAt: null } },
|
|
719
791
|
select: { id: true },
|
|
720
792
|
});
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
for (const workspace of workspaceIds) {
|
|
793
|
+
const result = await defaultWorkspaceLifecycleBarrier.withWorkspaces(workspaceIds.map((workspace) => workspace.id), async () => {
|
|
794
|
+
for (const workspace of workspaceIds)
|
|
724
795
|
await this.backgroundService?.stopAllForWorkspace(workspace.id);
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
try {
|
|
749
|
-
const taskForCleanup = await prisma.task.findUnique({
|
|
750
|
-
where: { id },
|
|
751
|
-
include: {
|
|
752
|
-
project: true,
|
|
753
|
-
teamRun: {
|
|
754
|
-
include: {
|
|
755
|
-
invocations: {
|
|
756
|
-
where: { status: { in: CANCELLABLE_DELETE_INVOCATION_STATUSES } },
|
|
757
|
-
select: { id: true },
|
|
758
|
-
},
|
|
759
|
-
},
|
|
760
|
-
},
|
|
761
|
-
workspaces: {
|
|
762
|
-
include: { sessions: true },
|
|
796
|
+
// Mark the complete family and persist its recovery snapshot together.
|
|
797
|
+
// Child creation rechecks the parent inside its own transaction.
|
|
798
|
+
return prisma.$transaction(async (tx) => {
|
|
799
|
+
const parent = await tx.task.findFirst({ where: { id, deletedAt: null }, include: { project: true } });
|
|
800
|
+
if (!parent)
|
|
801
|
+
throw new NotFoundError('Task', id);
|
|
802
|
+
ensureProjectIsMutable(parent.project, 'delete tasks');
|
|
803
|
+
const family = await tx.task.findMany({
|
|
804
|
+
where: { OR: [{ id }, { parentTaskId: id }], deletedAt: null },
|
|
805
|
+
select: { id: true },
|
|
806
|
+
});
|
|
807
|
+
if (expectedSubtaskCount !== undefined && expectedSubtaskCount !== family.length - 1) {
|
|
808
|
+
throw new ServiceError('Subtasks changed. Review the deletion confirmation again.', 'TASK_DELETE_SCOPE_CHANGED', 409);
|
|
809
|
+
}
|
|
810
|
+
const taskIds = [...family.filter((task) => task.id !== id).map((task) => task.id), id];
|
|
811
|
+
await tx.task.updateMany({ where: { id: { in: taskIds } }, data: { deletedAt: new Date() } });
|
|
812
|
+
const tasks = await tx.task.findMany({
|
|
813
|
+
where: { id: { in: taskIds } },
|
|
814
|
+
include: {
|
|
815
|
+
workspaces: { include: { sessions: true } },
|
|
816
|
+
teamRun: { include: { invocations: {
|
|
817
|
+
where: { status: { in: CANCELLABLE_DELETE_INVOCATION_STATUSES } }, select: { id: true },
|
|
818
|
+
} } },
|
|
763
819
|
},
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
workingDir: getWorkspaceWorkingDir(workspace),
|
|
781
|
-
workspaceKind: workspace.workspaceKind,
|
|
782
|
-
branchName: workspace.branchName,
|
|
783
|
-
baseBranch: workspace.baseBranch,
|
|
784
|
-
// Logical completion does not close a reusable ACP adapter.
|
|
785
|
-
sessions: workspace.sessions.map((session) => ({ id: session.id })),
|
|
786
|
-
})),
|
|
787
|
-
};
|
|
788
|
-
const teamRunId = taskForCleanup.teamRun?.id;
|
|
789
|
-
const cancelledInvocationIds = taskForCleanup.teamRun?.invocations.map((invocation) => invocation.id) ?? [];
|
|
790
|
-
await prisma.$transaction(async (tx) => {
|
|
820
|
+
});
|
|
821
|
+
const tasksById = new Map(tasks.map((task) => [task.id, task]));
|
|
822
|
+
const orderedTasks = taskIds.map((taskId) => tasksById.get(taskId));
|
|
823
|
+
const snapshot = {
|
|
824
|
+
taskId: id, taskIds, projectId: parent.projectId,
|
|
825
|
+
project: { repoPath: parent.project.repoPath, mainBranch: parent.project.mainBranch },
|
|
826
|
+
workspaces: orderedTasks.flatMap((task) => task.workspaces.map((workspace) => ({
|
|
827
|
+
id: workspace.id,
|
|
828
|
+
worktreePath: workspace.worktreePath,
|
|
829
|
+
workingDir: getWorkspaceWorkingDir(workspace),
|
|
830
|
+
workspaceKind: workspace.workspaceKind,
|
|
831
|
+
branchName: workspace.branchName,
|
|
832
|
+
baseBranch: workspace.baseBranch,
|
|
833
|
+
sessions: workspace.sessions.map((session) => ({ id: session.id })),
|
|
834
|
+
}))),
|
|
835
|
+
};
|
|
791
836
|
await tx.taskCleanupJob.create({
|
|
792
|
-
data: {
|
|
793
|
-
taskId: taskForCleanup.id,
|
|
794
|
-
projectId: taskForCleanup.projectId,
|
|
795
|
-
payload: JSON.stringify(snapshot),
|
|
796
|
-
},
|
|
837
|
+
data: { taskId: id, projectId: parent.projectId, payload: JSON.stringify(snapshot) },
|
|
797
838
|
});
|
|
798
|
-
|
|
839
|
+
const teamRunIds = tasks.flatMap((task) => task.teamRun ? [task.teamRun.id] : []);
|
|
840
|
+
if (teamRunIds.length) {
|
|
799
841
|
await tx.workRequest.updateMany({
|
|
800
|
-
where: {
|
|
801
|
-
teamRunId,
|
|
802
|
-
status: { in: CANCELLABLE_DELETE_WORK_REQUEST_STATUSES },
|
|
803
|
-
},
|
|
842
|
+
where: { teamRunId: { in: teamRunIds }, status: { in: CANCELLABLE_DELETE_WORK_REQUEST_STATUSES } },
|
|
804
843
|
data: { status: 'CANCELLED' },
|
|
805
844
|
});
|
|
806
845
|
await tx.agentInvocation.updateMany({
|
|
807
|
-
where: {
|
|
808
|
-
|
|
809
|
-
status: { in: CANCELLABLE_DELETE_INVOCATION_STATUSES },
|
|
810
|
-
},
|
|
811
|
-
data: {
|
|
812
|
-
status: 'CANCELLED',
|
|
813
|
-
nextRoomReplyReminderAt: null,
|
|
814
|
-
},
|
|
846
|
+
where: { teamRunId: { in: teamRunIds }, status: { in: CANCELLABLE_DELETE_INVOCATION_STATUSES } },
|
|
847
|
+
data: { status: 'CANCELLED', nextRoomReplyReminderAt: null },
|
|
815
848
|
});
|
|
816
849
|
}
|
|
850
|
+
return {
|
|
851
|
+
taskIds, projectId: parent.projectId,
|
|
852
|
+
invocationIds: tasks.flatMap((task) => task.teamRun?.invocations.map((invocation) => invocation.id) ?? []),
|
|
853
|
+
};
|
|
817
854
|
});
|
|
818
|
-
cleanupJobCreated = true;
|
|
819
|
-
deleteResult = {
|
|
820
|
-
projectId: taskForCleanup.projectId,
|
|
821
|
-
cancelledInvocationIds,
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
catch (error) {
|
|
825
|
-
if (!cleanupJobCreated) {
|
|
826
|
-
await prisma.task.updateMany({
|
|
827
|
-
where: { id, deletedAt },
|
|
828
|
-
data: { deletedAt: null },
|
|
829
|
-
}).catch(() => {
|
|
830
|
-
// If rollback fails, leave the task hidden rather than masking the original error.
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
throw error;
|
|
834
|
-
}
|
|
835
|
-
for (const invocationId of deleteResult.cancelledInvocationIds) {
|
|
836
|
-
defaultTeamLockService.releaseByOwner(invocationId);
|
|
837
|
-
}
|
|
838
|
-
this.eventBus.emit('task:deleted', {
|
|
839
|
-
taskId: id,
|
|
840
|
-
projectId: deleteResult.projectId,
|
|
841
855
|
});
|
|
856
|
+
for (const invocationId of result.invocationIds)
|
|
857
|
+
defaultTeamLockService.releaseByOwner(invocationId);
|
|
858
|
+
for (const taskId of result.taskIds)
|
|
859
|
+
this.eventBus.emit('task:deleted', { taskId, projectId: result.projectId });
|
|
842
860
|
this.cleanupService?.trigger();
|
|
843
861
|
return true;
|
|
844
862
|
}
|
|
863
|
+
async getCleanupStatus(taskId) {
|
|
864
|
+
const job = await prisma.taskCleanupJob.findFirst({
|
|
865
|
+
where: { taskId }, orderBy: { createdAt: 'desc' },
|
|
866
|
+
select: { taskId: true, status: true, lastError: true, nextRetryAt: true },
|
|
867
|
+
});
|
|
868
|
+
if (!job)
|
|
869
|
+
throw new NotFoundError('Task cleanup', taskId);
|
|
870
|
+
return job;
|
|
871
|
+
}
|
|
872
|
+
async retryCleanup(taskId) {
|
|
873
|
+
const job = await prisma.taskCleanupJob.findFirst({ where: { taskId }, orderBy: { createdAt: 'desc' } });
|
|
874
|
+
if (!job)
|
|
875
|
+
throw new NotFoundError('Task cleanup', taskId);
|
|
876
|
+
const project = await prisma.project.findUnique({ where: { id: job.projectId } });
|
|
877
|
+
if (!project)
|
|
878
|
+
throw new NotFoundError('Project', job.projectId);
|
|
879
|
+
ensureProjectIsMutable(project, 'retry task cleanup');
|
|
880
|
+
await prisma.taskCleanupJob.updateMany({
|
|
881
|
+
where: { id: job.id, status: 'FAILED' },
|
|
882
|
+
data: { status: 'PENDING', attempts: 0, lastError: null, nextRetryAt: null },
|
|
883
|
+
});
|
|
884
|
+
this.cleanupService?.trigger();
|
|
885
|
+
return this.getCleanupStatus(taskId);
|
|
886
|
+
}
|
|
845
887
|
/**
|
|
846
888
|
* 获取项目的任务统计
|
|
847
889
|
*/
|