agent-tower 0.7.3 → 0.7.4-beta.1
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 +18 -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__/sessions-runtime.test.js +2 -0
- package/dist/routes/__tests__/sessions-runtime.test.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 +64 -0
- package/dist/routes/__tests__/tasks.test.js.map +1 -1
- package/dist/routes/__tests__/workspace-history.test.d.ts +2 -0
- package/dist/routes/__tests__/workspace-history.test.d.ts.map +1 -0
- package/dist/routes/__tests__/workspace-history.test.js +174 -0
- package/dist/routes/__tests__/workspace-history.test.js.map +1 -0
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +10 -0
- package/dist/routes/sessions.js.map +1 -1
- package/dist/routes/tasks.d.ts.map +1 -1
- package/dist/routes/tasks.js +67 -2
- package/dist/routes/tasks.js.map +1 -1
- package/dist/runtime/__tests__/cli-driver-lifecycle.test.js +84 -0
- package/dist/runtime/__tests__/cli-driver-lifecycle.test.js.map +1 -1
- package/dist/runtime/cli-driver.js +11 -8
- package/dist/runtime/cli-driver.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 +152 -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 +100 -5
- package/dist/services/__tests__/workspace.service.test.js.map +1 -1
- package/dist/services/message-delivery.service.d.ts +3 -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 +12 -1
- package/dist/services/session-manager.d.ts.map +1 -1
- package/dist/services/session-manager.js +21 -3
- package/dist/services/session-manager.js.map +1 -1
- package/dist/services/session-resource-cleanup.d.ts +5 -0
- package/dist/services/session-resource-cleanup.d.ts.map +1 -1
- package/dist/services/session-resource-cleanup.js +7 -0
- package/dist/services/session-resource-cleanup.js.map +1 -1
- package/dist/services/task-cleanup.service.d.ts +4 -0
- package/dist/services/task-cleanup.service.d.ts.map +1 -1
- package/dist/services/task-cleanup.service.js +6 -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 +46 -10
- package/dist/services/task.service.d.ts.map +1 -1
- package/dist/services/task.service.js +258 -170
- 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 +20 -7
- package/dist/services/workspace.service.d.ts.map +1 -1
- package/dist/services/workspace.service.js +197 -82
- package/dist/services/workspace.service.js.map +1 -1
- package/dist/web/assets/AgentDemoPage-DiHEh1iv.js +1 -0
- package/dist/web/assets/{AgentEnvironmentSettingsPage-CvvkMKT1.js → AgentEnvironmentSettingsPage-B_Xk7Bef.js} +1 -1
- package/dist/web/assets/AttachmentPreview-BngGSahV.js +2 -0
- package/dist/web/assets/{BranchSelect-Cv92vEjG.js → BranchSelect-BgVKUnxj.js} +1 -1
- package/dist/web/assets/BrandLogo-CBahjTd7.js +1 -0
- package/dist/web/assets/ConversationPage-kgJIlyS5.js +1 -0
- package/dist/web/assets/CreateTaskInput-Cllw4abW.js +1 -0
- package/dist/web/assets/{DemoPage-CpYNBQhA.js → DemoPage-CkS__9CE.js} +3 -3
- package/dist/web/assets/{DesignSystemPage-DLeH6DnV.js → DesignSystemPage-BklN99GA.js} +3 -3
- package/dist/web/assets/{ExecutionPermissionSelector-IoWR9YLb.js → ExecutionPermissionSelector-Bsi1aa7q.js} +1 -1
- package/dist/web/assets/{GeneralSettingsPage-BAYgxCqx.js → GeneralSettingsPage-BC9TUWiu.js} +1 -1
- package/dist/web/assets/{LoadingPreviewPage-CumrzyxW.js → LoadingPreviewPage-Dx8Si6-g.js} +1 -1
- package/dist/web/assets/{McpSettingsPage-CFNwxT9Y.js → McpSettingsPage-fqXRGtFu.js} +1 -1
- package/dist/web/assets/{MemberAvatar-BVXD0jTy.js → MemberAvatar-CZ3F53Ep.js} +1 -1
- package/dist/web/assets/MobileTaskDetail-B68iYPOI.js +3 -0
- package/dist/web/assets/{NotificationSettingsPage-B0s5YbG3.js → NotificationSettingsPage-D9KGIFVA.js} +1 -1
- package/dist/web/assets/{ProfileSettingsPage-BuhoF-mL.js → ProfileSettingsPage-D_Hw5lpd.js} +2 -2
- package/dist/web/assets/ProjectKanbanPage-CUX0Kh-t.js +12 -0
- package/dist/web/assets/{ProjectSettingsPage-DDMg6axy.js → ProjectSettingsPage-CJANCPJX.js} +2 -2
- package/dist/web/assets/{ProviderSettingsPage-DRt9m4mF.js → ProviderSettingsPage-B6Ro5PQa.js} +2 -2
- package/dist/web/assets/{SettingsMasterDetail-D7vStQjR.js → SettingsMasterDetail-Q-049Dvh.js} +1 -1
- package/dist/web/assets/TaskDetail-DBtWf0YV.js +1 -0
- package/dist/web/assets/TaskHandoffPanel-BpGiZ5eT.js +70 -0
- package/dist/web/assets/TeamSettingsPage-DbKgqEO_.js +1 -0
- package/dist/web/assets/{TranslationSettingsPage-D2oks6OM.js → TranslationSettingsPage-Ba-iypOV.js} +1 -1
- package/dist/web/assets/{arc-vYWCZsE_.js → arc-SOqs3Svk.js} +1 -1
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-Cu1VIpcJ.js → architectureDiagram-3BPJPVTR-BH25ukq_.js} +1 -1
- package/dist/web/assets/{at-sign-BhSPafOS.js → at-sign-CEsum1vJ.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-C4x_nj5S.js → blockDiagram-GPEHLZMM-CKA17iNP.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-jHOo5lal.js → c4Diagram-AAUBKEIU-DnZ_8oOf.js} +1 -1
- package/dist/web/assets/channel-xU45IQlP.js +1 -0
- package/dist/web/assets/check-DUA2DjGH.js +1 -0
- package/dist/web/assets/{chevron-down-BoS08pMa.js → chevron-down-Bmwy-nvS.js} +1 -1
- package/dist/web/assets/{chevron-up-BWtKfdKT.js → chevron-up-CClL6-SF.js} +1 -1
- package/dist/web/assets/{chip-Ck4dqxR3.js → chip-BSMT2uT1.js} +1 -1
- package/dist/web/assets/{chunk-2J33WTMH-BZcpjmfm.js → chunk-2J33WTMH-BrtUyBDF.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-DZGB3q-I.js → chunk-4BX2VUAB-DZDNetDC.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-CcNrOmyy.js → chunk-55IACEB6-xVa3bJqv.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-dWrxio8O.js → chunk-727SXJPM-B7n9vxY4.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-K4gkVzqE.js → chunk-AQP2D5EJ-DQd4G76y.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-CnHyY4qD.js → chunk-FMBD7UC4-CXyP08kl.js} +1 -1
- package/dist/web/assets/{chunk-ND2GUHAM-CHUmcyDv.js → chunk-ND2GUHAM-BtnIS-HK.js} +1 -1
- package/dist/web/assets/{chunk-QZHKN3VN-Bzzjd-Qt.js → chunk-QZHKN3VN-D4D-V_ex.js} +1 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-pvRulc6s.js +1 -0
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-pvRulc6s.js +1 -0
- package/dist/web/assets/{code-block-OCS4YCEC-B2hHBO_G.js → code-block-OCS4YCEC-DfERo9L1.js} +1 -1
- package/dist/web/assets/confirm-dialog-8ItnHBi0.js +1 -0
- package/dist/web/assets/{content-D0qmHfHK.js → content-DrW8KnXu.js} +2 -2
- package/dist/web/assets/{copy-C4WnOY6Y.js → copy-prrQrInw.js} +1 -1
- package/dist/web/assets/{cose-bilkent-S5V4N54A-Dcn8tOhb.js → cose-bilkent-S5V4N54A-CXdH8Idq.js} +1 -1
- package/dist/web/assets/cssMode-CJ6_f10q.js +1 -0
- package/dist/web/assets/{dagre-BM42HDAG-DWBV4UfP.js → dagre-BM42HDAG-XZydDwXe.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-CnNTJQ3q.js → diagram-2AECGRRQ-CofEe0Fr.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-DlXJFzDF.js → diagram-5GNKFQAL-BbTtW5WH.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-DZ22YV4n.js → diagram-KO2AKTUF-BJHZXpUM.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-B4pQJjG9.js → diagram-LMA3HP47-DN7A27YU.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-DJBLjtgL.js → diagram-OG6HWLK6-4IdYadiw.js} +1 -1
- package/dist/web/assets/{dialog-1o8KQWoQ.js → dialog-BUsI4h2U.js} +2 -2
- package/dist/web/assets/{erDiagram-TEJ5UH35-CDsfoJUT.js → erDiagram-TEJ5UH35-BFijhCWv.js} +1 -1
- package/dist/web/assets/{file-6qwbIga1.js → file-C6QMUr5_.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-CuGOAYmY.js → flowDiagram-I6XJVG4X-D9MxC-El.js} +1 -1
- package/dist/web/assets/{folder-picker-Cj1VZOZU.js → folder-picker-CBdw-4v3.js} +1 -1
- package/dist/web/assets/{freemarker2-BG8VyI2V.js → freemarker2-CJVobC22.js} +2 -2
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-BjrSOqAM.js → ganttDiagram-6RSMTGT7-BijCAyPQ.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-4VVr4F8_.js → gitGraphDiagram-PVQCEYII-AvI5QqxQ.js} +1 -1
- package/dist/web/assets/{globe-CxeM27yy.js → globe-BOfcTsvL.js} +1 -1
- package/dist/web/assets/handlebars-DTqWmI25.js +1 -0
- package/dist/web/assets/html-Bx7L8DX_.js +1 -0
- package/dist/web/assets/htmlMode-D_S0NjZc.js +1 -0
- package/dist/web/assets/{index-hHrknzh7.js → index-BClyhWqz.js} +1 -1
- package/dist/web/assets/index-BDON669P.css +1 -0
- package/dist/web/assets/{index-DVdiOmqt.js → index-BFcDmhr0.js} +1 -1
- package/dist/web/assets/index-BRu1Yhrx.js +122 -0
- package/dist/web/assets/{index-Db28hRe8.js → index-Bg8TlDo_.js} +1 -1
- package/dist/web/assets/{index-Da2mWAtm.js → index-COigMgrJ.js} +1 -1
- package/dist/web/assets/{index-cu8sedJH.js → index-Cdmj8aPv.js} +1 -1
- package/dist/web/assets/index-CtOu4p9B.js +1 -0
- package/dist/web/assets/{index-CrKx8Yaz.js → index-DUmVscrR.js} +4 -4
- package/dist/web/assets/{index-C1lMEjmZ.js → index-JXcQeorP.js} +1 -1
- package/dist/web/assets/{infoDiagram-5YYISTIA-Cy9ntb6t.js → infoDiagram-5YYISTIA-76JLqjJ0.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-D6UiVzRz.js → ishikawaDiagram-YF4QCWOH-CXHFTVly.js} +1 -1
- package/dist/web/assets/javascript-80FDzX3y.js +1 -0
- package/dist/web/assets/{journeyDiagram-JHISSGLW-owdKzZoh.js → journeyDiagram-JHISSGLW-6SDJs9C5.js} +1 -1
- package/dist/web/assets/jsonMode-BGg9MhFR.js +7 -0
- package/dist/web/assets/{kanban-definition-UN3LZRKU-v5TBnyp0.js → kanban-definition-UN3LZRKU-C0Y3zy1P.js} +1 -1
- package/dist/web/assets/{kbd-Cl4AzzJp.js → kbd-D4KIW7lm.js} +1 -1
- package/dist/web/assets/{key-round-CnYjSrCz.js → key-round-C4vhwqUu.js} +1 -1
- package/dist/web/assets/{layers-XImbMX-P.js → layers-BBHgMnhN.js} +1 -1
- package/dist/web/assets/{linear-DNlKYt8R.js → linear-CjeIgBwO.js} +1 -1
- package/dist/web/assets/{link-DQk-Vapq.js → link-C-Jq7dnc.js} +1 -1
- package/dist/web/assets/liquid-guocpCJB.js +1 -0
- package/dist/web/assets/{lspLanguageFeatures-DD_y1bZa.js → lspLanguageFeatures-CPXERxVW.js} +1 -1
- package/dist/web/assets/mdx-DA4ymd3l.js +1 -0
- package/dist/web/assets/{menu-DXF6iUsq.js → menu-BgtmXAzT.js} +1 -1
- package/dist/web/assets/{mermaid-NOHMQCX5-CG_LpwAI.js → mermaid-NOHMQCX5-XQbfQDGS.js} +49 -49
- package/dist/web/assets/{message-square-BviHdb67.js → message-square-DIFv9yrO.js} +1 -1
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-Bn_rnnGT.js → mindmap-definition-RKZ34NQL-6ucD61UV.js} +1 -1
- package/dist/web/assets/modal-Dx4N46s5.js +1 -0
- package/dist/web/assets/{monaco-yuGji-2E.js → monaco-Mn1xgio_.js} +3 -3
- package/dist/web/assets/nav-row-DwEkbw3N.js +1 -0
- package/dist/web/assets/{paperclip-D9ka2IAB.js → paperclip-CdQvl5ee.js} +1 -1
- package/dist/web/assets/{pencil-isP1rXC-.js → pencil-ChX7G9j9.js} +1 -1
- package/dist/web/assets/{people-CbmH8m4i.js → people-D_VwidfV.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-DIE_f9W_.js → pieDiagram-4H26LBE5-KVvAyrIn.js} +1 -1
- package/dist/web/assets/{popover-D8VtO5qF.js → popover-CGEFZKuu.js} +1 -1
- package/dist/web/assets/python-BzNNRn8W.js +1 -0
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-CavFxJCk.js → quadrantDiagram-W4KKPZXB-ClVzvpE7.js} +1 -1
- package/dist/web/assets/razor-DF2ZxqCE.js +1 -0
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-Dm1FwUUh.js → requirementDiagram-4Y6WPE33-uXN6Uoln.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-qeqcO27M.js → sankeyDiagram-5OEKKPKP-Bi3Tc0wR.js} +1 -1
- package/dist/web/assets/{search-DOMr5jPS.js → search--FRFbCre.js} +1 -1
- package/dist/web/assets/{select-Cw5BaOLQ.js → select-D0sHgfcU.js} +1 -1
- package/dist/web/assets/{select-CzZNbe1Z.js → select-c5IIpQj0.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-Dqtb3BYG.js → sequenceDiagram-3UESZ5HK-CLdrO2PY.js} +1 -1
- package/dist/web/assets/{server-u-PC-CcX.js → server-Dr_6KJFM.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-C_MqF9Fy.js → stateDiagram-AJRCARHV-BGInldGc.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-D44x8SXb.js +1 -0
- package/dist/web/assets/{textarea-xt2hLb-d.js → textarea-W-xQPto3.js} +1 -1
- package/dist/web/assets/{timeline-definition-PNZ67QCA-Bzov6t31.js → timeline-definition-PNZ67QCA-BUp6Qykc.js} +1 -1
- package/dist/web/assets/{toggles-B6E1UBYN.js → toggles-Bgp7vdwg.js} +1 -1
- package/dist/web/assets/tooltip-DZcaSN1F.js +1 -0
- package/dist/web/assets/{track-BeqaPopG.js → track-Cj1Ogapu.js} +1 -1
- package/dist/web/assets/{trash-2-5jI3_KHh.js → trash-2-DmFXzIhh.js} +1 -1
- package/dist/web/assets/tsMode-CSHMQttX.js +11 -0
- package/dist/web/assets/typescript-CyQKmnN-.js +1 -0
- package/dist/web/assets/{upload-DUteazR7.js → upload-C-4QDrwN.js} +1 -1
- package/dist/web/assets/use-profiles-D5mIgeQU.js +1 -0
- package/dist/web/assets/{use-providers-CQwBj9hp.js → use-providers-BViTes-6.js} +1 -1
- package/dist/web/assets/{use-translation-settings-DBzFqP-d.js → use-translation-settings-Dgw0oKP1.js} +1 -1
- package/dist/web/assets/{useNormalizedLogs-DsdwmfS7.js → useNormalizedLogs-BX7pMg0-.js} +1 -1
- package/dist/web/assets/useTokenUsage-T03faPfn.js +1 -0
- package/dist/web/assets/{vennDiagram-CIIHVFJN-BqxICvT_.js → vennDiagram-CIIHVFJN-iQYnP9Ux.js} +1 -1
- package/dist/web/assets/{wardley-L42UT6IY-BTdLPFcA.js → wardley-L42UT6IY-J7vXwA5J.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-icnFsFn4.js → wardleyDiagram-YWT4CUSO-CdtM5GJv.js} +1 -1
- package/dist/web/assets/xml-C79KdvcD.js +1 -0
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-BN2_ducK.js → xychartDiagram-2RQKCTM6-B3gcBChQ.js} +1 -1
- package/dist/web/assets/yaml-C1WLpuVe.js +1 -0
- package/dist/web/index.html +2 -2
- package/node_modules/@agent-tower/shared/dist/index.d.ts +2 -0
- package/node_modules/@agent-tower/shared/dist/index.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/index.js +2 -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/task-hierarchy.d.ts +17 -0
- package/node_modules/@agent-tower/shared/dist/task-hierarchy.d.ts.map +1 -0
- package/node_modules/@agent-tower/shared/dist/task-hierarchy.js +12 -0
- package/node_modules/@agent-tower/shared/dist/task-hierarchy.js.map +1 -0
- package/node_modules/@agent-tower/shared/dist/types.d.ts +22 -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/migrations/20261027000000_preserve_workspace_history/migration.sql +2 -0
- package/prisma/schema.prisma +7 -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,14 +3,21 @@ 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';
|
|
10
10
|
import { defaultTeamLockService } from './team-lock.service.js';
|
|
11
|
-
import { TaskPriority } from '@agent-tower/shared';
|
|
11
|
+
import { TaskPriority, MAX_TASK_DEPTH } 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 parent."parentTaskId" FROM "Task" parent WHERE parent."id" = task."parentTaskId") AS "grandparentTaskId",
|
|
18
|
+
(SELECT COUNT(*) FROM "Task" child WHERE child."parentTaskId" = task."id" AND child."deletedAt" IS NULL) AS "subtaskCount",
|
|
19
|
+
(SELECT COUNT(*) FROM "Task" child WHERE child."parentTaskId" = task."id" AND child."deletedAt" IS NULL AND child."status" = 'IN_PROGRESS') AS "runningSubtaskCount"
|
|
20
|
+
`;
|
|
14
21
|
/**
|
|
15
22
|
* 合法的状态流转规则
|
|
16
23
|
* 看板拖拽场景下允许任意状态互转,状态变更无危险副作用
|
|
@@ -43,6 +50,19 @@ const visibleSessionSummary = {
|
|
|
43
50
|
updatedAt: true,
|
|
44
51
|
},
|
|
45
52
|
};
|
|
53
|
+
// The schema stores adjacency; these bounded reads cover the supported three levels.
|
|
54
|
+
function subtreeWhere(id, projectId) {
|
|
55
|
+
return { deletedAt: null, ...(projectId ? { projectId } : {}), OR: [
|
|
56
|
+
{ id }, { parentTaskId: id }, { parentTask: { parentTaskId: id, deletedAt: null } },
|
|
57
|
+
] };
|
|
58
|
+
}
|
|
59
|
+
function ancestorIds(parentTaskId, grandparentTaskId) {
|
|
60
|
+
return [grandparentTaskId, parentTaskId].filter((id) => Boolean(id));
|
|
61
|
+
}
|
|
62
|
+
function withHierarchy(task) {
|
|
63
|
+
const { parentTask, ...metadata } = task;
|
|
64
|
+
return { ...metadata, ancestorIds: ancestorIds(task.parentTaskId, parentTask?.parentTaskId) };
|
|
65
|
+
}
|
|
46
66
|
function sliceAtCompleteCodePointBoundary(value, maxLength) {
|
|
47
67
|
let end = 0;
|
|
48
68
|
for (const character of value) {
|
|
@@ -211,13 +231,17 @@ function normalizeUpdateTaskInput(current, input) {
|
|
|
211
231
|
]),
|
|
212
232
|
};
|
|
213
233
|
}
|
|
234
|
+
function withoutTaskHandoff(task) {
|
|
235
|
+
const { handoff: _handoff, ...metadata } = task;
|
|
236
|
+
return metadata;
|
|
237
|
+
}
|
|
214
238
|
function withTaskPreviews(task, options = {}) {
|
|
215
239
|
const titlePreview = buildTextPreview(task.title, TASK_TITLE_MAX_LENGTH);
|
|
216
240
|
const contentPreview = buildTextPreview(task.description);
|
|
217
241
|
const isTruncated = task.title !== titlePreview
|
|
218
242
|
|| Boolean(task.description && task.description !== contentPreview);
|
|
219
243
|
const base = {
|
|
220
|
-
...task,
|
|
244
|
+
...withoutTaskHandoff(task),
|
|
221
245
|
title: titlePreview,
|
|
222
246
|
titlePreview,
|
|
223
247
|
contentPreview: contentPreview || undefined,
|
|
@@ -235,13 +259,15 @@ function withProjectGitMetadata(task) {
|
|
|
235
259
|
return withProjectGitCapability(task, capability);
|
|
236
260
|
}
|
|
237
261
|
function withProjectGitCapability(task, capability) {
|
|
238
|
-
|
|
262
|
+
const result = {
|
|
239
263
|
...task,
|
|
240
264
|
project: {
|
|
241
265
|
...task.project,
|
|
242
266
|
...capability,
|
|
243
267
|
},
|
|
244
268
|
};
|
|
269
|
+
delete result.handoff;
|
|
270
|
+
return result;
|
|
245
271
|
}
|
|
246
272
|
function buildProjectGitMetadata(project) {
|
|
247
273
|
return getStoredProjectGitCapability(project);
|
|
@@ -275,30 +301,28 @@ export class TaskService {
|
|
|
275
301
|
deletedAt: null,
|
|
276
302
|
project: { archivedAt: null },
|
|
277
303
|
...(params.projectId ? { projectId: params.projectId } : {}),
|
|
304
|
+
...(params.parentTaskId ? { parentTaskId: params.parentTaskId } : {}),
|
|
278
305
|
...(params.status ? { status: params.status } : {}),
|
|
279
306
|
};
|
|
280
307
|
const projectFilter = params.projectId
|
|
281
308
|
? Prisma.sql `AND task."projectId" = ${params.projectId}`
|
|
282
309
|
: Prisma.empty;
|
|
310
|
+
const parentFilter = params.parentTaskId
|
|
311
|
+
? Prisma.sql `AND task."parentTaskId" = ${params.parentTaskId}`
|
|
312
|
+
: Prisma.empty;
|
|
283
313
|
const statusFilter = params.status
|
|
284
314
|
? Prisma.sql `AND task."status" = ${params.status}`
|
|
285
315
|
: Prisma.empty;
|
|
286
316
|
const [tasks, total] = await Promise.all([
|
|
287
317
|
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"
|
|
318
|
+
SELECT ${taskBoardColumns}
|
|
296
319
|
FROM "Task" task
|
|
297
320
|
INNER JOIN "Project" project ON project."id" = task."projectId"
|
|
298
321
|
WHERE task."deletedAt" IS NULL
|
|
299
322
|
AND project."archivedAt" IS NULL
|
|
300
323
|
${projectFilter}
|
|
301
324
|
${statusFilter}
|
|
325
|
+
${parentFilter}
|
|
302
326
|
ORDER BY
|
|
303
327
|
CASE task."status"
|
|
304
328
|
WHEN ${TaskStatus.IN_PROGRESS} THEN 0
|
|
@@ -315,7 +339,19 @@ export class TaskService {
|
|
|
315
339
|
`),
|
|
316
340
|
prisma.task.count({ where }),
|
|
317
341
|
]);
|
|
318
|
-
|
|
342
|
+
// Descendants can rank ahead of their ancestors or fall on another page.
|
|
343
|
+
// Return the complete compact path without changing the page count/limit.
|
|
344
|
+
const pageIds = new Set(tasks.map((task) => task.id));
|
|
345
|
+
const missingParentIds = [...new Set(tasks.flatMap((task) => ancestorIds(task.parentTaskId, task.grandparentTaskId).filter(id => !pageIds.has(id))))];
|
|
346
|
+
const parents = params.parentTaskId || missingParentIds.length === 0 ? []
|
|
347
|
+
: await prisma.$queryRaw(Prisma.sql `
|
|
348
|
+
SELECT ${taskBoardColumns} FROM "Task" task
|
|
349
|
+
INNER JOIN "Project" project ON project."id" = task."projectId"
|
|
350
|
+
WHERE task."id" IN (SELECT value FROM json_each(${JSON.stringify(missingParentIds)}))
|
|
351
|
+
AND task."deletedAt" IS NULL AND project."archivedAt" IS NULL
|
|
352
|
+
`);
|
|
353
|
+
const allTasks = [...tasks, ...parents];
|
|
354
|
+
const taskIds = allTasks.map((task) => task.id);
|
|
319
355
|
const workspaceRows = taskIds.length === 0
|
|
320
356
|
? []
|
|
321
357
|
: await prisma.$queryRaw(Prisma.sql `
|
|
@@ -363,7 +399,7 @@ export class TaskService {
|
|
|
363
399
|
workspacesByTaskId.set(workspace.taskId, group);
|
|
364
400
|
}
|
|
365
401
|
const preferredWorkspaceByTaskId = new Map();
|
|
366
|
-
for (const task of
|
|
402
|
+
for (const task of allTasks) {
|
|
367
403
|
const candidates = workspacesByTaskId.get(task.id) ?? [];
|
|
368
404
|
const preferred = candidates.find((workspace) => workspace.status === WorkspaceStatus.ACTIVE)
|
|
369
405
|
?? candidates[0];
|
|
@@ -371,7 +407,7 @@ export class TaskService {
|
|
|
371
407
|
preferredWorkspaceByTaskId.set(task.id, preferred);
|
|
372
408
|
}
|
|
373
409
|
}
|
|
374
|
-
const data =
|
|
410
|
+
const data = allTasks.map((task) => {
|
|
375
411
|
const titlePreview = buildTextPreview(task.title, TASK_TITLE_MAX_LENGTH);
|
|
376
412
|
const preferredWorkspace = preferredWorkspaceByTaskId.get(task.id);
|
|
377
413
|
const hasRunningSession = (workspacesByTaskId.get(task.id) ?? [])
|
|
@@ -382,6 +418,10 @@ export class TaskService {
|
|
|
382
418
|
return {
|
|
383
419
|
id: task.id,
|
|
384
420
|
projectId: task.projectId,
|
|
421
|
+
parentTaskId: task.parentTaskId,
|
|
422
|
+
ancestorIds: ancestorIds(task.parentTaskId, task.grandparentTaskId),
|
|
423
|
+
subtaskCount: Number(task.subtaskCount),
|
|
424
|
+
runningSubtaskCount: Number(task.runningSubtaskCount),
|
|
385
425
|
title: titlePreview,
|
|
386
426
|
status: task.status,
|
|
387
427
|
priority: task.priority ?? TaskPriority.NORMAL,
|
|
@@ -401,7 +441,8 @@ export class TaskService {
|
|
|
401
441
|
};
|
|
402
442
|
});
|
|
403
443
|
return {
|
|
404
|
-
data,
|
|
444
|
+
data: data.slice(0, tasks.length),
|
|
445
|
+
...(parents.length ? { parents: data.slice(tasks.length) } : {}),
|
|
405
446
|
total,
|
|
406
447
|
page,
|
|
407
448
|
limit,
|
|
@@ -443,6 +484,8 @@ export class TaskService {
|
|
|
443
484
|
projectId: true,
|
|
444
485
|
title: true,
|
|
445
486
|
baseBranch: true,
|
|
487
|
+
parentTaskId: true,
|
|
488
|
+
parentTask: { select: { parentTaskId: true } },
|
|
446
489
|
status: true,
|
|
447
490
|
priority: true,
|
|
448
491
|
position: true,
|
|
@@ -471,7 +514,7 @@ export class TaskService {
|
|
|
471
514
|
});
|
|
472
515
|
const projectGitCapability = getStoredProjectGitCapability(project);
|
|
473
516
|
return {
|
|
474
|
-
data: data.map((task) => (withTaskPreviews(withProjectGitCapability(task, projectGitCapability), { omitDescription: true }))),
|
|
517
|
+
data: data.map((task) => (withTaskPreviews(withProjectGitCapability(withHierarchy(task), projectGitCapability), { omitDescription: true }))),
|
|
475
518
|
total,
|
|
476
519
|
page,
|
|
477
520
|
limit,
|
|
@@ -490,6 +533,9 @@ export class TaskService {
|
|
|
490
533
|
project: true,
|
|
491
534
|
title: true,
|
|
492
535
|
baseBranch: true,
|
|
536
|
+
parentTaskId: true,
|
|
537
|
+
parentTask: { select: { parentTaskId: true } },
|
|
538
|
+
_count: { select: { subtasks: { where: { deletedAt: null } } } },
|
|
493
539
|
status: true,
|
|
494
540
|
priority: true,
|
|
495
541
|
position: true,
|
|
@@ -501,7 +547,14 @@ export class TaskService {
|
|
|
501
547
|
if (!task) {
|
|
502
548
|
throw new NotFoundError('Task', id);
|
|
503
549
|
}
|
|
504
|
-
|
|
550
|
+
const { _count, ...metadata } = task;
|
|
551
|
+
return withTaskPreviews(withProjectGitMetadata({ ...withHierarchy(metadata), subtaskCount: _count.subtasks }), { omitDescription: true });
|
|
552
|
+
}
|
|
553
|
+
async findSubtasks(id, params = {}) {
|
|
554
|
+
const parent = await prisma.task.findFirst({ where: { id, deletedAt: null } });
|
|
555
|
+
if (!parent)
|
|
556
|
+
throw new NotFoundError('Task', id);
|
|
557
|
+
return this.findBoard({ ...params, projectId: parent.projectId, parentTaskId: id });
|
|
505
558
|
}
|
|
506
559
|
async findBodyById(id) {
|
|
507
560
|
const task = await prisma.task.findFirst({
|
|
@@ -554,37 +607,82 @@ export class TaskService {
|
|
|
554
607
|
throw new NotFoundError('Project', projectId);
|
|
555
608
|
}
|
|
556
609
|
ensureProjectIsMutable(project, 'create tasks');
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
projectId,
|
|
579
|
-
},
|
|
610
|
+
const parent = input.parentTaskId ? await prisma.task.findFirst({
|
|
611
|
+
where: { id: input.parentTaskId, projectId, deletedAt: null },
|
|
612
|
+
include: { parentTask: { select: { parentTaskId: true } }, workspaces: {
|
|
613
|
+
where: { parentWorkspaceId: null, ownerMemberId: null, status: { not: WorkspaceStatus.ABANDONED }, cleanedAt: null },
|
|
614
|
+
orderBy: { createdAt: 'desc' },
|
|
615
|
+
} },
|
|
616
|
+
}) : null;
|
|
617
|
+
if (input.parentTaskId && !parent)
|
|
618
|
+
throw new NotFoundError('Parent task', input.parentTaskId);
|
|
619
|
+
if (parent?.parentTask?.parentTaskId)
|
|
620
|
+
throw new ValidationError('Tasks support at most three levels');
|
|
621
|
+
const parentWorkspace = parent?.workspaces.find((workspace) => workspace.status === WorkspaceStatus.ACTIVE)
|
|
622
|
+
?? parent?.workspaces.find((workspace) => workspace.status === WorkspaceStatus.HIBERNATED)
|
|
623
|
+
?? parent?.workspaces[0];
|
|
624
|
+
const parentBranch = parentWorkspace?.branchName || parent?.baseBranch;
|
|
625
|
+
// A grandchild may inherit an ancestor branch before its parent starts.
|
|
626
|
+
// Serialize validation/persistence with that branch owner's cleanup as well.
|
|
627
|
+
const requestedParentBranch = input.baseBranch ?? parentBranch;
|
|
628
|
+
const branchOwners = input.baseBranch === null || !requestedParentBranch ? [] : await prisma.workspace.findMany({
|
|
629
|
+
where: { task: { projectId, deletedAt: null }, branchName: requestedParentBranch },
|
|
630
|
+
select: { id: true },
|
|
580
631
|
});
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
project
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
632
|
+
const createWithParentBranch = async () => {
|
|
633
|
+
// Resolve at task creation so delayed starts and TeamRun main workspaces
|
|
634
|
+
// cannot silently follow a later project default change. Local tasks opt out.
|
|
635
|
+
let baseBranch = null;
|
|
636
|
+
if (input.baseBranch !== null) {
|
|
637
|
+
const requestedBranch = input.baseBranch ?? parentBranch ?? ((await detectProjectGitCapability(project.repoPath)).worktreeReady ? project.mainBranch : null);
|
|
638
|
+
if (requestedBranch !== null) {
|
|
639
|
+
baseBranch = await requireLocalBaseBranch(project.repoPath, requestedBranch);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
// 自动计算 position
|
|
643
|
+
const maxPosition = await prisma.task.aggregate({
|
|
644
|
+
where: { projectId, status: TaskStatus.TODO, deletedAt: null },
|
|
645
|
+
_max: { position: true },
|
|
646
|
+
});
|
|
647
|
+
const created = await prisma.$transaction(async (tx) => {
|
|
648
|
+
// Recheck after Git validation, serialized with the deletion marker.
|
|
649
|
+
if (parent) {
|
|
650
|
+
const liveParent = await tx.task.findFirst({
|
|
651
|
+
where: { id: parent.id, projectId, deletedAt: null, project: { archivedAt: null } },
|
|
652
|
+
include: { parentTask: { select: { parentTaskId: true } } },
|
|
653
|
+
});
|
|
654
|
+
if (!liveParent)
|
|
655
|
+
throw new NotFoundError('Parent task', parent.id);
|
|
656
|
+
if (liveParent.parentTask?.parentTaskId)
|
|
657
|
+
throw new ValidationError('Tasks support at most three levels');
|
|
658
|
+
}
|
|
659
|
+
return tx.task.create({
|
|
660
|
+
data: {
|
|
661
|
+
title: normalizedInput.title,
|
|
662
|
+
description: normalizedInput.description,
|
|
663
|
+
priority: normalizedInput.priority ?? TaskPriority.NORMAL,
|
|
664
|
+
baseBranch,
|
|
665
|
+
parentTaskId: parent?.id,
|
|
666
|
+
position: (maxPosition._max.position ?? 0) + 1,
|
|
667
|
+
projectId,
|
|
668
|
+
},
|
|
669
|
+
});
|
|
670
|
+
});
|
|
671
|
+
this.eventBus.emit('task:updated', { taskId: created.id, projectId, status: created.status });
|
|
672
|
+
if (parent)
|
|
673
|
+
this.eventBus.emit('task:updated', { taskId: parent.id, projectId, status: parent.status });
|
|
674
|
+
return {
|
|
675
|
+
...created,
|
|
676
|
+
ancestorIds: parent ? ancestorIds(parent.id, parent.parentTaskId) : [],
|
|
677
|
+
project: {
|
|
678
|
+
...project,
|
|
679
|
+
...buildProjectGitMetadata(project),
|
|
680
|
+
},
|
|
681
|
+
};
|
|
587
682
|
};
|
|
683
|
+
// Serialize branch validation and child persistence with parent workspace
|
|
684
|
+
// cleanup, so its reference becomes visible before that branch is reclaimed.
|
|
685
|
+
return defaultWorkspaceLifecycleBarrier.withWorkspaces([...branchOwners.map(workspace => workspace.id), ...(parentWorkspace ? [parentWorkspace.id] : [])], createWithParentBranch);
|
|
588
686
|
}
|
|
589
687
|
/**
|
|
590
688
|
* 更新任务基本信息
|
|
@@ -607,7 +705,7 @@ export class TaskService {
|
|
|
607
705
|
this.emitTaskUpdated(id, task.projectId, updated.status, updated.priority);
|
|
608
706
|
}
|
|
609
707
|
return {
|
|
610
|
-
...updated,
|
|
708
|
+
...withoutTaskHandoff(updated),
|
|
611
709
|
project: {
|
|
612
710
|
...task.project,
|
|
613
711
|
...buildProjectGitMetadata(task.project),
|
|
@@ -652,7 +750,7 @@ export class TaskService {
|
|
|
652
750
|
// 通知前端
|
|
653
751
|
this.emitTaskUpdated(id, task.projectId, status);
|
|
654
752
|
return {
|
|
655
|
-
...updated,
|
|
753
|
+
...withoutTaskHandoff(updated),
|
|
656
754
|
project: {
|
|
657
755
|
...task.project,
|
|
658
756
|
...buildProjectGitMetadata(task.project),
|
|
@@ -689,7 +787,7 @@ export class TaskService {
|
|
|
689
787
|
this.emitTaskUpdated(id, task.projectId, status);
|
|
690
788
|
}
|
|
691
789
|
return {
|
|
692
|
-
...updated,
|
|
790
|
+
...withoutTaskHandoff(updated),
|
|
693
791
|
project: {
|
|
694
792
|
...task.project,
|
|
695
793
|
...buildProjectGitMetadata(task.project),
|
|
@@ -699,149 +797,139 @@ export class TaskService {
|
|
|
699
797
|
/**
|
|
700
798
|
* 快速删除任务
|
|
701
799
|
*
|
|
702
|
-
* 1.
|
|
703
|
-
* 2.
|
|
800
|
+
* 1. 在同一事务中标记父子 Task 为 deletedAt,并保存清理快照
|
|
801
|
+
* 2. 普通读取立即隐藏已删除的任务
|
|
704
802
|
* 3. 通过 EventBus 通知前端实时删除
|
|
705
803
|
* 4. 后台 worker 再停止 Session、删除 worktree/branch,并最终硬删除 Task
|
|
706
804
|
*/
|
|
707
|
-
async delete(id) {
|
|
708
|
-
const
|
|
709
|
-
|
|
710
|
-
where: { id },
|
|
711
|
-
include: { project: true },
|
|
712
|
-
});
|
|
713
|
-
if (!taskForGuard || taskForGuard.deletedAt) {
|
|
805
|
+
async delete(id, expectedSubtaskCount) {
|
|
806
|
+
const taskForGuard = await prisma.task.findUnique({ where: { id }, include: { project: true } });
|
|
807
|
+
if (!taskForGuard || taskForGuard.deletedAt)
|
|
714
808
|
throw new NotFoundError('Task', id);
|
|
715
|
-
}
|
|
716
809
|
ensureProjectIsMutable(taskForGuard.project, 'delete tasks');
|
|
810
|
+
const familyWhere = subtreeWhere(id, taskForGuard.projectId);
|
|
811
|
+
if (expectedSubtaskCount !== undefined && expectedSubtaskCount !== (await prisma.task.count({
|
|
812
|
+
where: familyWhere,
|
|
813
|
+
})) - 1) {
|
|
814
|
+
throw new ServiceError('Subtasks changed. Review the deletion confirmation again.', 'TASK_DELETE_SCOPE_CHANGED', 409);
|
|
815
|
+
}
|
|
717
816
|
const workspaceIds = await prisma.workspace.findMany({
|
|
718
|
-
where: {
|
|
817
|
+
where: { task: familyWhere },
|
|
719
818
|
select: { id: true },
|
|
720
819
|
});
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
for (const workspace of workspaceIds) {
|
|
820
|
+
const result = await defaultWorkspaceLifecycleBarrier.withWorkspaces(workspaceIds.map((workspace) => workspace.id), async () => {
|
|
821
|
+
for (const workspace of workspaceIds)
|
|
724
822
|
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
|
-
|
|
749
|
-
|
|
750
|
-
where: { id },
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
include: {
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
},
|
|
759
|
-
},
|
|
760
|
-
},
|
|
761
|
-
workspaces: {
|
|
762
|
-
include: { sessions: true },
|
|
823
|
+
// Mark the complete family and persist its recovery snapshot together.
|
|
824
|
+
// Child creation rechecks the parent inside its own transaction.
|
|
825
|
+
return prisma.$transaction(async (tx) => {
|
|
826
|
+
const parent = await tx.task.findFirst({ where: { id, deletedAt: null }, include: { project: true } });
|
|
827
|
+
if (!parent)
|
|
828
|
+
throw new NotFoundError('Task', id);
|
|
829
|
+
ensureProjectIsMutable(parent.project, 'delete tasks');
|
|
830
|
+
const family = await tx.task.findMany({
|
|
831
|
+
where: familyWhere,
|
|
832
|
+
select: { id: true, parentTaskId: true },
|
|
833
|
+
});
|
|
834
|
+
if (expectedSubtaskCount !== undefined && expectedSubtaskCount !== family.length - 1) {
|
|
835
|
+
throw new ServiceError('Subtasks changed. Review the deletion confirmation again.', 'TASK_DELETE_SCOPE_CHANGED', 409);
|
|
836
|
+
}
|
|
837
|
+
const byId = new Map(family.map(task => [task.id, task]));
|
|
838
|
+
const depth = (task) => {
|
|
839
|
+
let current = task;
|
|
840
|
+
let value = 0;
|
|
841
|
+
while (current.parentTaskId && byId.has(current.parentTaskId) && value < MAX_TASK_DEPTH) {
|
|
842
|
+
current = byId.get(current.parentTaskId);
|
|
843
|
+
value++;
|
|
844
|
+
}
|
|
845
|
+
return value;
|
|
846
|
+
};
|
|
847
|
+
const taskIds = family.sort((a, b) => depth(b) - depth(a) || a.id.localeCompare(b.id)).map(task => task.id);
|
|
848
|
+
await tx.task.updateMany({ where: { id: { in: taskIds } }, data: { deletedAt: new Date() } });
|
|
849
|
+
const tasks = await tx.task.findMany({
|
|
850
|
+
where: { id: { in: taskIds } },
|
|
851
|
+
include: {
|
|
852
|
+
workspaces: { include: { sessions: true } },
|
|
853
|
+
teamRun: { include: { invocations: {
|
|
854
|
+
where: { status: { in: CANCELLABLE_DELETE_INVOCATION_STATUSES } }, select: { id: true },
|
|
855
|
+
} } },
|
|
763
856
|
},
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
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) => {
|
|
857
|
+
});
|
|
858
|
+
const tasksById = new Map(tasks.map((task) => [task.id, task]));
|
|
859
|
+
const orderedTasks = taskIds.map((taskId) => tasksById.get(taskId));
|
|
860
|
+
const snapshot = {
|
|
861
|
+
taskId: id, taskIds, projectId: parent.projectId,
|
|
862
|
+
project: { repoPath: parent.project.repoPath, mainBranch: parent.project.mainBranch },
|
|
863
|
+
workspaces: orderedTasks.flatMap((task) => task.workspaces.map((workspace) => ({
|
|
864
|
+
id: workspace.id,
|
|
865
|
+
worktreePath: workspace.worktreePath,
|
|
866
|
+
resourcesCleaned: workspace.cleanedAt != null,
|
|
867
|
+
workingDir: getWorkspaceWorkingDir(workspace),
|
|
868
|
+
workspaceKind: workspace.workspaceKind,
|
|
869
|
+
branchName: workspace.branchName,
|
|
870
|
+
baseBranch: workspace.baseBranch,
|
|
871
|
+
sessions: workspace.sessions.map((session) => ({ id: session.id })),
|
|
872
|
+
}))),
|
|
873
|
+
};
|
|
791
874
|
await tx.taskCleanupJob.create({
|
|
792
|
-
data: {
|
|
793
|
-
taskId: taskForCleanup.id,
|
|
794
|
-
projectId: taskForCleanup.projectId,
|
|
795
|
-
payload: JSON.stringify(snapshot),
|
|
796
|
-
},
|
|
875
|
+
data: { taskId: id, projectId: parent.projectId, payload: JSON.stringify(snapshot) },
|
|
797
876
|
});
|
|
798
|
-
|
|
877
|
+
const teamRunIds = tasks.flatMap((task) => task.teamRun ? [task.teamRun.id] : []);
|
|
878
|
+
if (teamRunIds.length) {
|
|
799
879
|
await tx.workRequest.updateMany({
|
|
800
|
-
where: {
|
|
801
|
-
teamRunId,
|
|
802
|
-
status: { in: CANCELLABLE_DELETE_WORK_REQUEST_STATUSES },
|
|
803
|
-
},
|
|
880
|
+
where: { teamRunId: { in: teamRunIds }, status: { in: CANCELLABLE_DELETE_WORK_REQUEST_STATUSES } },
|
|
804
881
|
data: { status: 'CANCELLED' },
|
|
805
882
|
});
|
|
806
883
|
await tx.agentInvocation.updateMany({
|
|
807
|
-
where: {
|
|
808
|
-
|
|
809
|
-
status: { in: CANCELLABLE_DELETE_INVOCATION_STATUSES },
|
|
810
|
-
},
|
|
811
|
-
data: {
|
|
812
|
-
status: 'CANCELLED',
|
|
813
|
-
nextRoomReplyReminderAt: null,
|
|
814
|
-
},
|
|
884
|
+
where: { teamRunId: { in: teamRunIds }, status: { in: CANCELLABLE_DELETE_INVOCATION_STATUSES } },
|
|
885
|
+
data: { status: 'CANCELLED', nextRoomReplyReminderAt: null },
|
|
815
886
|
});
|
|
816
887
|
}
|
|
888
|
+
return {
|
|
889
|
+
taskIds, projectId: parent.projectId,
|
|
890
|
+
invocationIds: tasks.flatMap((task) => task.teamRun?.invocations.map((invocation) => invocation.id) ?? []),
|
|
891
|
+
};
|
|
817
892
|
});
|
|
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
893
|
});
|
|
894
|
+
for (const invocationId of result.invocationIds)
|
|
895
|
+
defaultTeamLockService.releaseByOwner(invocationId);
|
|
896
|
+
for (const taskId of result.taskIds)
|
|
897
|
+
this.eventBus.emit('task:deleted', { taskId, projectId: result.projectId });
|
|
842
898
|
this.cleanupService?.trigger();
|
|
843
899
|
return true;
|
|
844
900
|
}
|
|
901
|
+
async getDeletionScope(id) {
|
|
902
|
+
return prisma.$transaction(async (tx) => {
|
|
903
|
+
const task = await tx.task.findFirst({ where: { id, deletedAt: null }, select: { projectId: true } });
|
|
904
|
+
if (!task)
|
|
905
|
+
throw new NotFoundError('Task', id);
|
|
906
|
+
return { descendantCount: (await tx.task.count({ where: subtreeWhere(id, task.projectId) })) - 1 };
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
async getCleanupStatus(taskId) {
|
|
910
|
+
const job = await prisma.taskCleanupJob.findFirst({
|
|
911
|
+
where: { taskId }, orderBy: { createdAt: 'desc' },
|
|
912
|
+
select: { taskId: true, status: true, lastError: true, nextRetryAt: true },
|
|
913
|
+
});
|
|
914
|
+
if (!job)
|
|
915
|
+
throw new NotFoundError('Task cleanup', taskId);
|
|
916
|
+
return job;
|
|
917
|
+
}
|
|
918
|
+
async retryCleanup(taskId) {
|
|
919
|
+
const job = await prisma.taskCleanupJob.findFirst({ where: { taskId }, orderBy: { createdAt: 'desc' } });
|
|
920
|
+
if (!job)
|
|
921
|
+
throw new NotFoundError('Task cleanup', taskId);
|
|
922
|
+
const project = await prisma.project.findUnique({ where: { id: job.projectId } });
|
|
923
|
+
if (!project)
|
|
924
|
+
throw new NotFoundError('Project', job.projectId);
|
|
925
|
+
ensureProjectIsMutable(project, 'retry task cleanup');
|
|
926
|
+
await prisma.taskCleanupJob.updateMany({
|
|
927
|
+
where: { id: job.id, status: 'FAILED' },
|
|
928
|
+
data: { status: 'PENDING', attempts: 0, lastError: null, nextRetryAt: null },
|
|
929
|
+
});
|
|
930
|
+
this.cleanupService?.trigger();
|
|
931
|
+
return this.getCleanupStatus(taskId);
|
|
932
|
+
}
|
|
845
933
|
/**
|
|
846
934
|
* 获取项目的任务统计
|
|
847
935
|
*/
|