termdock 1.4.296 → 1.4.297

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/.desktop-dist/main.js +35 -2
  2. package/README.en.md +257 -0
  3. package/README.md +284 -133
  4. package/dist/client/assets/App-BbqtNY2x.js +177 -0
  5. package/dist/client/assets/App-BbqtNY2x.js.br +0 -0
  6. package/dist/client/assets/App-BbqtNY2x.js.gz +0 -0
  7. package/dist/client/assets/{ChangeWalkthroughPanel-DXprT-7m.js → ChangeWalkthroughPanel-CvQJIp_C.js} +1 -1
  8. package/dist/client/assets/ChangeWalkthroughPanel-CvQJIp_C.js.br +0 -0
  9. package/dist/client/assets/ChangeWalkthroughPanel-CvQJIp_C.js.gz +0 -0
  10. package/dist/client/assets/CollaborationMainWorkspace-B0OgRtyR.js +1 -0
  11. package/dist/client/assets/CollaborationMainWorkspace-B0OgRtyR.js.br +0 -0
  12. package/dist/client/assets/CollaborationMainWorkspace-B0OgRtyR.js.gz +0 -0
  13. package/dist/client/assets/CollaborationTaskContent-C01GXOEq.js +2 -0
  14. package/dist/client/assets/ComputerControlView-CW1uA133.js +4 -0
  15. package/dist/client/assets/ComputerControlView-CW1uA133.js.br +0 -0
  16. package/dist/client/assets/ComputerControlView-CW1uA133.js.gz +0 -0
  17. package/dist/client/assets/{DagPlayground-DZcUunT5.js → DagPlayground-CdE3tQsl.js} +1 -1
  18. package/dist/client/assets/DagPlayground-CdE3tQsl.js.br +0 -0
  19. package/dist/client/assets/DagPlayground-CdE3tQsl.js.gz +0 -0
  20. package/dist/client/assets/{DiffLab-Brv4DCTb.js → DiffLab-BLGNnazd.js} +1 -1
  21. package/dist/client/assets/DiffLab-BLGNnazd.js.br +0 -0
  22. package/dist/client/assets/DiffLab-BLGNnazd.js.gz +0 -0
  23. package/dist/client/assets/{DiffReview-BuLu1qCi.js → DiffReview-CYw3ASI6.js} +1 -1
  24. package/dist/client/assets/DiffReview-CYw3ASI6.js.br +0 -0
  25. package/dist/client/assets/DiffReview-CYw3ASI6.js.gz +0 -0
  26. package/dist/client/assets/{DiffReviewLab-DSGamuAD.js → DiffReviewLab-CySIuWq_.js} +1 -1
  27. package/dist/client/assets/DiffReviewLab-CySIuWq_.js.br +0 -0
  28. package/dist/client/assets/DiffReviewLab-CySIuWq_.js.gz +0 -0
  29. package/dist/client/assets/{DiffViewer-BUCYxg27.js → DiffViewer-WsYvN75z.js} +13 -13
  30. package/dist/client/assets/DiffViewer-WsYvN75z.js.br +0 -0
  31. package/dist/client/assets/DiffViewer-WsYvN75z.js.gz +0 -0
  32. package/dist/client/assets/{ModelPreview-Bnlo-oVE.js → ModelPreview-Dnlx9ByX.js} +1 -1
  33. package/dist/client/assets/ModelPreview-Dnlx9ByX.js.br +0 -0
  34. package/dist/client/assets/ModelPreview-Dnlx9ByX.js.gz +0 -0
  35. package/dist/client/assets/RightSidebar-Cp5b_y0u.js +1678 -0
  36. package/dist/client/assets/RightSidebar-Cp5b_y0u.js.br +0 -0
  37. package/dist/client/assets/RightSidebar-Cp5b_y0u.js.gz +0 -0
  38. package/dist/client/assets/{abnfDiagram-VCTEODGH-K6Ppkrbt.js → abnfDiagram-VCTEODGH-CK1mGFRQ.js} +1 -1
  39. package/dist/client/assets/abnfDiagram-VCTEODGH-CK1mGFRQ.js.br +0 -0
  40. package/dist/client/assets/abnfDiagram-VCTEODGH-CK1mGFRQ.js.gz +0 -0
  41. package/dist/client/assets/app-DfACMixk.js +35 -0
  42. package/dist/client/assets/app-DfACMixk.js.br +0 -0
  43. package/dist/client/assets/app-DfACMixk.js.gz +0 -0
  44. package/dist/client/assets/{arc-Co2OQqtH.js → arc-yZE1gqDl.js} +1 -1
  45. package/dist/client/assets/arc-yZE1gqDl.js.br +0 -0
  46. package/dist/client/assets/arc-yZE1gqDl.js.gz +0 -0
  47. package/dist/client/assets/{architectureDiagram-5GKGNRK7-BQnl7tVF.js → architectureDiagram-5GKGNRK7-U5XSC7cY.js} +1 -1
  48. package/dist/client/assets/architectureDiagram-5GKGNRK7-U5XSC7cY.js.br +0 -0
  49. package/dist/client/assets/architectureDiagram-5GKGNRK7-U5XSC7cY.js.gz +0 -0
  50. package/dist/client/assets/{blockDiagram-I7D4REHJ-Bm5D5-qq.js → blockDiagram-I7D4REHJ-pVJnntSG.js} +1 -1
  51. package/dist/client/assets/blockDiagram-I7D4REHJ-pVJnntSG.js.br +0 -0
  52. package/dist/client/assets/blockDiagram-I7D4REHJ-pVJnntSG.js.gz +0 -0
  53. package/dist/client/assets/{c4Diagram-7LVT6UL2-B9oPpey8.js → c4Diagram-7LVT6UL2-Bwz0AJJV.js} +1 -1
  54. package/dist/client/assets/c4Diagram-7LVT6UL2-Bwz0AJJV.js.br +0 -0
  55. package/dist/client/assets/c4Diagram-7LVT6UL2-Bwz0AJJV.js.gz +0 -0
  56. package/dist/client/assets/channel-qqjDJtcZ.js +1 -0
  57. package/dist/client/assets/{chunk-2Q5K7J3B-TMaWHH0U.js → chunk-2Q5K7J3B-CXJ1kP4X.js} +1 -1
  58. package/dist/client/assets/{chunk-5VM5RSS4-CH7XpknV.js → chunk-5VM5RSS4-Dpdum_2z.js} +1 -1
  59. package/dist/client/assets/{chunk-F27PBJKO-CJ1rotBe.js → chunk-F27PBJKO-BCG5Zi_-.js} +1 -1
  60. package/dist/client/assets/chunk-F27PBJKO-BCG5Zi_-.js.br +0 -0
  61. package/dist/client/assets/chunk-F27PBJKO-BCG5Zi_-.js.gz +0 -0
  62. package/dist/client/assets/{chunk-IMKFNOWR-UYKZ5uOG.js → chunk-IMKFNOWR-BM-GcNcv.js} +1 -1
  63. package/dist/client/assets/chunk-IMKFNOWR-BM-GcNcv.js.br +0 -0
  64. package/dist/client/assets/chunk-IMKFNOWR-BM-GcNcv.js.gz +0 -0
  65. package/dist/client/assets/{chunk-JWPE2WC7-BYL8aXjQ.js → chunk-JWPE2WC7-BWiXpQT9.js} +1 -1
  66. package/dist/client/assets/{chunk-POPQ4Y6H-C3E8yfU2.js → chunk-POPQ4Y6H-vQSslMNv.js} +1 -1
  67. package/dist/client/assets/{chunk-SVP7TREG-DCTDYa1E.js → chunk-SVP7TREG-Aqfz6nPA.js} +1 -1
  68. package/dist/client/assets/chunk-SVP7TREG-Aqfz6nPA.js.br +0 -0
  69. package/dist/client/assets/chunk-SVP7TREG-Aqfz6nPA.js.gz +0 -0
  70. package/dist/client/assets/{chunk-TICWLB2K-wHiaOsBh.js → chunk-TICWLB2K-DDjekMd0.js} +1 -1
  71. package/dist/client/assets/chunk-TICWLB2K-DDjekMd0.js.br +0 -0
  72. package/dist/client/assets/chunk-TICWLB2K-DDjekMd0.js.gz +0 -0
  73. package/dist/client/assets/{chunk-XXDRQBXY-D_d479hK.js → chunk-XXDRQBXY-BegIBcFg.js} +1 -1
  74. package/dist/client/assets/classDiagram-ZZMXUADV-BDuBrfNz.js +1 -0
  75. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BDuBrfNz.js +1 -0
  76. package/dist/client/assets/{cose-bilkent-JH36ORCC-DnhlPBLr.js → cose-bilkent-JH36ORCC-B2izFsBY.js} +1 -1
  77. package/dist/client/assets/cose-bilkent-JH36ORCC-B2izFsBY.js.br +0 -0
  78. package/dist/client/assets/cose-bilkent-JH36ORCC-B2izFsBY.js.gz +0 -0
  79. package/dist/client/assets/{cynefin-OW5HDTMX-BsjzNqJZ.js → cynefin-OW5HDTMX-C7HA99Zc.js} +1 -1
  80. package/dist/client/assets/cynefin-OW5HDTMX-C7HA99Zc.js.br +0 -0
  81. package/dist/client/assets/cynefin-OW5HDTMX-C7HA99Zc.js.gz +0 -0
  82. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-sVUb0zY5.js → cynefinDiagram-5FMLGOSQ-vNIzcCYl.js} +1 -1
  83. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-vNIzcCYl.js.br +0 -0
  84. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-vNIzcCYl.js.gz +0 -0
  85. package/dist/client/assets/{dagre-GXQ25YYZ-YErk8S-c.js → dagre-GXQ25YYZ-Bv9WHNT9.js} +1 -1
  86. package/dist/client/assets/dagre-GXQ25YYZ-Bv9WHNT9.js.br +0 -0
  87. package/dist/client/assets/dagre-GXQ25YYZ-Bv9WHNT9.js.gz +0 -0
  88. package/dist/client/assets/{diagram-S7CK7UJ4-BaHbV5ho.js → diagram-S7CK7UJ4-DFg5UNic.js} +1 -1
  89. package/dist/client/assets/diagram-S7CK7UJ4-DFg5UNic.js.br +0 -0
  90. package/dist/client/assets/diagram-S7CK7UJ4-DFg5UNic.js.gz +0 -0
  91. package/dist/client/assets/{diagram-UQ7AKVKN-BocCYx3V.js → diagram-UQ7AKVKN-BFQsCAs7.js} +1 -1
  92. package/dist/client/assets/diagram-UQ7AKVKN-BFQsCAs7.js.br +0 -0
  93. package/dist/client/assets/diagram-UQ7AKVKN-BFQsCAs7.js.gz +0 -0
  94. package/dist/client/assets/{diagram-VSXAHHWV-ZMTYwWOQ.js → diagram-VSXAHHWV-C4R8TKEv.js} +1 -1
  95. package/dist/client/assets/diagram-VSXAHHWV-C4R8TKEv.js.br +0 -0
  96. package/dist/client/assets/diagram-VSXAHHWV-C4R8TKEv.js.gz +0 -0
  97. package/dist/client/assets/{diagram-VX7I27RA-xmk_EY1h.js → diagram-VX7I27RA-CL75HIf0.js} +1 -1
  98. package/dist/client/assets/diagram-VX7I27RA-CL75HIf0.js.br +0 -0
  99. package/dist/client/assets/diagram-VX7I27RA-CL75HIf0.js.gz +0 -0
  100. package/dist/client/assets/{diagram-Z3DM3KII-DKDY_cG6.js → diagram-Z3DM3KII-CfJampuO.js} +1 -1
  101. package/dist/client/assets/diagram-Z3DM3KII-CfJampuO.js.br +0 -0
  102. package/dist/client/assets/diagram-Z3DM3KII-CfJampuO.js.gz +0 -0
  103. package/dist/client/assets/{ebnfDiagram-PWID7BFC-C_0z-Jx9.js → ebnfDiagram-PWID7BFC-Ct53jhNd.js} +1 -1
  104. package/dist/client/assets/ebnfDiagram-PWID7BFC-Ct53jhNd.js.br +0 -0
  105. package/dist/client/assets/ebnfDiagram-PWID7BFC-Ct53jhNd.js.gz +0 -0
  106. package/dist/client/assets/{erDiagram-RLTQ6QDP-DHUaG7mt.js → erDiagram-RLTQ6QDP-BgL2xOOa.js} +1 -1
  107. package/dist/client/assets/erDiagram-RLTQ6QDP-BgL2xOOa.js.br +0 -0
  108. package/dist/client/assets/erDiagram-RLTQ6QDP-BgL2xOOa.js.gz +0 -0
  109. package/dist/client/assets/{flowDiagram-HODETNUW-BMNsdMBD.js → flowDiagram-HODETNUW-Cp4Nu5jY.js} +1 -1
  110. package/dist/client/assets/flowDiagram-HODETNUW-Cp4Nu5jY.js.br +0 -0
  111. package/dist/client/assets/flowDiagram-HODETNUW-Cp4Nu5jY.js.gz +0 -0
  112. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-BH1AiKuF.js → ganttDiagram-EL5Y4UJY-DI0rdyPX.js} +1 -1
  113. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DI0rdyPX.js.br +0 -0
  114. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DI0rdyPX.js.gz +0 -0
  115. package/dist/client/assets/{gestureArbiter-DvupkPra.js → gestureArbiter-Belsq4xq.js} +1 -1
  116. package/dist/client/assets/gestureArbiter-Belsq4xq.js.br +0 -0
  117. package/dist/client/assets/gestureArbiter-Belsq4xq.js.gz +0 -0
  118. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-DAgV0MV-.js → gitGraphDiagram-WWUBYQGX-C0H4loQP.js} +1 -1
  119. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C0H4loQP.js.br +0 -0
  120. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-C0H4loQP.js.gz +0 -0
  121. package/dist/client/assets/index-CYmI1EsU.css +1 -0
  122. package/dist/client/assets/index-CYmI1EsU.css.br +0 -0
  123. package/dist/client/assets/index-CYmI1EsU.css.gz +0 -0
  124. package/dist/client/assets/{infoDiagram-27XIBGKW-C7iSg0cB.js → infoDiagram-27XIBGKW-BN4RpQTL.js} +1 -1
  125. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BpZwsQvw.js → ishikawaDiagram-5VMMS53U-BZQ5qZB_.js} +1 -1
  126. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BZQ5qZB_.js.br +0 -0
  127. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BZQ5qZB_.js.gz +0 -0
  128. package/dist/client/assets/{journeyDiagram-3NMN7TZE-DXEeFLvw.js → journeyDiagram-3NMN7TZE-Q8bx0_N4.js} +1 -1
  129. package/dist/client/assets/journeyDiagram-3NMN7TZE-Q8bx0_N4.js.br +0 -0
  130. package/dist/client/assets/journeyDiagram-3NMN7TZE-Q8bx0_N4.js.gz +0 -0
  131. package/dist/client/assets/{kanban-definition-UXKFOSKX-CV9ivL5u.js → kanban-definition-UXKFOSKX-D7JhGtpO.js} +1 -1
  132. package/dist/client/assets/kanban-definition-UXKFOSKX-D7JhGtpO.js.br +0 -0
  133. package/dist/client/assets/kanban-definition-UXKFOSKX-D7JhGtpO.js.gz +0 -0
  134. package/dist/client/assets/{layout-DsKKsJKX.js → layout-DERUg0xU.js} +1 -1
  135. package/dist/client/assets/layout-DERUg0xU.js.br +0 -0
  136. package/dist/client/assets/layout-DERUg0xU.js.gz +0 -0
  137. package/dist/client/assets/{linear-Dl9MDNV3.js → linear-lNTe4x00.js} +1 -1
  138. package/dist/client/assets/linear-lNTe4x00.js.br +0 -0
  139. package/dist/client/assets/linear-lNTe4x00.js.gz +0 -0
  140. package/dist/client/assets/{mermaid.core-CxZFe3as.js → mermaid.core-WHCwLQDQ.js} +5 -5
  141. package/dist/client/assets/mermaid.core-WHCwLQDQ.js.br +0 -0
  142. package/dist/client/assets/mermaid.core-WHCwLQDQ.js.gz +0 -0
  143. package/dist/client/assets/{mindmap-definition-YA3MSWOX-C3ormYdL.js → mindmap-definition-YA3MSWOX-BouapA4v.js} +1 -1
  144. package/dist/client/assets/mindmap-definition-YA3MSWOX-BouapA4v.js.br +0 -0
  145. package/dist/client/assets/mindmap-definition-YA3MSWOX-BouapA4v.js.gz +0 -0
  146. package/dist/client/assets/{passwordBootstrap-a8z6jJNY.js → passwordBootstrap-E0piP814.js} +2 -2
  147. package/dist/client/assets/passwordBootstrap-E0piP814.js.br +0 -0
  148. package/dist/client/assets/passwordBootstrap-E0piP814.js.gz +0 -0
  149. package/dist/client/assets/{pegDiagram-XKGWAZYB-CG0wNylH.js → pegDiagram-XKGWAZYB-Curpo5Rq.js} +1 -1
  150. package/dist/client/assets/pegDiagram-XKGWAZYB-Curpo5Rq.js.br +0 -0
  151. package/dist/client/assets/pegDiagram-XKGWAZYB-Curpo5Rq.js.gz +0 -0
  152. package/dist/client/assets/{pieDiagram-E7YTZNPT-D2-ydqyX.js → pieDiagram-E7YTZNPT-BLQCHaOG.js} +1 -1
  153. package/dist/client/assets/pieDiagram-E7YTZNPT-BLQCHaOG.js.br +0 -0
  154. package/dist/client/assets/pieDiagram-E7YTZNPT-BLQCHaOG.js.gz +0 -0
  155. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-CK3fRC18.js → quadrantDiagram-AXDQQJYC-PViFqGao.js} +1 -1
  156. package/dist/client/assets/quadrantDiagram-AXDQQJYC-PViFqGao.js.br +0 -0
  157. package/dist/client/assets/quadrantDiagram-AXDQQJYC-PViFqGao.js.gz +0 -0
  158. package/dist/client/assets/{railroadDiagram-O6MQD6OU-V4EFlydu.js → railroadDiagram-O6MQD6OU-BtHo1LpT.js} +1 -1
  159. package/dist/client/assets/railroadDiagram-O6MQD6OU-BtHo1LpT.js.br +0 -0
  160. package/dist/client/assets/railroadDiagram-O6MQD6OU-BtHo1LpT.js.gz +0 -0
  161. package/dist/client/assets/rdpSession-O_f9ejOI.js +1 -0
  162. package/dist/client/assets/rdpSession-O_f9ejOI.js.br +0 -0
  163. package/dist/client/assets/rdpSession-O_f9ejOI.js.gz +0 -0
  164. package/dist/client/assets/{referenceLongPress-BjmYd1vB.js → referenceLongPress-BZr7Lqzh.js} +1 -1
  165. package/dist/client/assets/referenceLongPress-BZr7Lqzh.js.br +0 -0
  166. package/dist/client/assets/referenceLongPress-BZr7Lqzh.js.gz +0 -0
  167. package/dist/client/assets/{requirementDiagram-BXWQKSXE-Di1_tyAX.js → requirementDiagram-BXWQKSXE-BUm81HUU.js} +1 -1
  168. package/dist/client/assets/requirementDiagram-BXWQKSXE-BUm81HUU.js.br +0 -0
  169. package/dist/client/assets/requirementDiagram-BXWQKSXE-BUm81HUU.js.gz +0 -0
  170. package/dist/client/assets/rfb-CxsXsMUX.js +5 -0
  171. package/dist/client/assets/rfb-CxsXsMUX.js.br +0 -0
  172. package/dist/client/assets/rfb-CxsXsMUX.js.gz +0 -0
  173. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-D-qpWNza.js → sankeyDiagram-P5KCCOFB-B-_-aZ_y.js} +1 -1
  174. package/dist/client/assets/sankeyDiagram-P5KCCOFB-B-_-aZ_y.js.br +0 -0
  175. package/dist/client/assets/sankeyDiagram-P5KCCOFB-B-_-aZ_y.js.gz +0 -0
  176. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-DmIxafrB.js → sequenceDiagram-WJ2MYXX4-DT6LDIfx.js} +1 -1
  177. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DT6LDIfx.js.br +0 -0
  178. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DT6LDIfx.js.gz +0 -0
  179. package/dist/client/assets/{sizeCapture-INFHLROL-4MTh3tTt.js → sizeCapture-INFHLROL-CTYerd9j.js} +1 -1
  180. package/dist/client/assets/sizeCapture-INFHLROL-CTYerd9j.js.br +0 -0
  181. package/dist/client/assets/sizeCapture-INFHLROL-CTYerd9j.js.gz +0 -0
  182. package/dist/client/assets/{sparkles-Dk93lHg0.js → sparkles-CgaUBIwH.js} +1 -1
  183. package/dist/client/assets/{stateDiagram-D77RDMKH-CgYgneIv.js → stateDiagram-D77RDMKH-CQbb5x2_.js} +1 -1
  184. package/dist/client/assets/stateDiagram-D77RDMKH-CQbb5x2_.js.br +0 -0
  185. package/dist/client/assets/stateDiagram-D77RDMKH-CQbb5x2_.js.gz +0 -0
  186. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CJAVXcx1.js +1 -0
  187. package/dist/client/assets/{swimlanes-42K2YHIH-OWA9HFVf.js → swimlanes-42K2YHIH-BHZ4X2Fd.js} +1 -1
  188. package/dist/client/assets/swimlanes-42K2YHIH-BHZ4X2Fd.js.br +0 -0
  189. package/dist/client/assets/swimlanes-42K2YHIH-BHZ4X2Fd.js.gz +0 -0
  190. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-VL2nyI27.js +8 -0
  191. package/dist/client/assets/{timeline-definition-24CTP7MA-BAzx5DyO.js → timeline-definition-24CTP7MA-BrjH6R6w.js} +1 -1
  192. package/dist/client/assets/timeline-definition-24CTP7MA-BrjH6R6w.js.br +0 -0
  193. package/dist/client/assets/timeline-definition-24CTP7MA-BrjH6R6w.js.gz +0 -0
  194. package/dist/client/assets/{triangle-alert-CUIBOwqW.js → triangle-alert-C9IHD_eF.js} +1 -1
  195. package/dist/client/assets/{vennDiagram-4TSXK5OY-CBZ-PhTQ.js → vennDiagram-4TSXK5OY-C9f5jYgk.js} +1 -1
  196. package/dist/client/assets/vennDiagram-4TSXK5OY-C9f5jYgk.js.br +0 -0
  197. package/dist/client/assets/vennDiagram-4TSXK5OY-C9f5jYgk.js.gz +0 -0
  198. package/dist/client/assets/{videoCompression-BCnqCkRK.js → videoCompression-DJaU8exd.js} +1 -1
  199. package/dist/client/assets/videoCompression-DJaU8exd.js.br +0 -0
  200. package/dist/client/assets/videoCompression-DJaU8exd.js.gz +0 -0
  201. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-DbWzC6l5.js → wardleyDiagram-VM6X3IG4-6ljFvtiC.js} +1 -1
  202. package/dist/client/assets/wardleyDiagram-VM6X3IG4-6ljFvtiC.js.br +0 -0
  203. package/dist/client/assets/wardleyDiagram-VM6X3IG4-6ljFvtiC.js.gz +0 -0
  204. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-BmApnTWE.js → xychartDiagram-S5SC5T6Z-BaR2hm3e.js} +1 -1
  205. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BaR2hm3e.js.br +0 -0
  206. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BaR2hm3e.js.gz +0 -0
  207. package/dist/client/index.html +2 -2
  208. package/dist/client/index.html.br +0 -0
  209. package/dist/client/index.html.gz +0 -0
  210. package/dist/client/sw.js +1 -1
  211. package/dist/client/sw.js.br +0 -0
  212. package/dist/client/sw.js.gz +0 -0
  213. package/dist/server/agent/collaborationCli.js +3 -3
  214. package/dist/server/agent/collaborationGroupRoutes.js +0 -2
  215. package/dist/server/agent/collaborationResultPresentation.js +14 -0
  216. package/dist/server/agent/collaborationService.js +3 -1
  217. package/dist/server/agent/collaborationStore.js +12 -21
  218. package/dist/server/agent/collaborationTaskBundles.js +2 -2
  219. package/dist/server/agent/collaborationTaskCli.js +4 -4
  220. package/dist/server/agent/collaborationTaskStore.js +87 -25
  221. package/dist/server/agent/collaborationTaskWorkspace.js +66 -1
  222. package/dist/server/agent/collaborationTeam.js +16 -0
  223. package/dist/server/agent/executionArchives.js +52 -0
  224. package/dist/server/entry.js +6 -1
  225. package/dist/server/federation/runtime.js +24 -6
  226. package/dist/server/routes/computer.js +198 -0
  227. package/dist/server/routes/computerTarget.js +23 -0
  228. package/dist/server/routes/guacamoleProtocol.js +55 -0
  229. package/dist/server/routes/rdp.js +162 -0
  230. package/dist/server/routes/terminal.js +375 -360
  231. package/dist/server/shared/computerPreferences.js +29 -0
  232. package/dist/server/utils/computerCredentials.js +88 -0
  233. package/dist/server/utils/sharedSampling.js +93 -0
  234. package/dist/server/utils/terminalOutputDelivery.js +8 -1
  235. package/dist/server/utils/tmuxMetadataWriter.js +61 -0
  236. package/package.json +4 -1
  237. package/runtime-manifest.json +4 -4
  238. package/dist/client/assets/App-BiLjlrk-.js +0 -171
  239. package/dist/client/assets/App-BiLjlrk-.js.br +0 -0
  240. package/dist/client/assets/App-BiLjlrk-.js.gz +0 -0
  241. package/dist/client/assets/ChangeWalkthroughPanel-DXprT-7m.js.br +0 -0
  242. package/dist/client/assets/ChangeWalkthroughPanel-DXprT-7m.js.gz +0 -0
  243. package/dist/client/assets/DagPlayground-DZcUunT5.js.br +0 -0
  244. package/dist/client/assets/DagPlayground-DZcUunT5.js.gz +0 -0
  245. package/dist/client/assets/DiffLab-Brv4DCTb.js.br +0 -0
  246. package/dist/client/assets/DiffLab-Brv4DCTb.js.gz +0 -0
  247. package/dist/client/assets/DiffReview-BuLu1qCi.js.br +0 -0
  248. package/dist/client/assets/DiffReview-BuLu1qCi.js.gz +0 -0
  249. package/dist/client/assets/DiffReviewLab-DSGamuAD.js.br +0 -0
  250. package/dist/client/assets/DiffReviewLab-DSGamuAD.js.gz +0 -0
  251. package/dist/client/assets/DiffViewer-BUCYxg27.js.br +0 -0
  252. package/dist/client/assets/DiffViewer-BUCYxg27.js.gz +0 -0
  253. package/dist/client/assets/ModelPreview-Bnlo-oVE.js.br +0 -0
  254. package/dist/client/assets/ModelPreview-Bnlo-oVE.js.gz +0 -0
  255. package/dist/client/assets/RightSidebar-r_3Lk5Rp.js +0 -1678
  256. package/dist/client/assets/RightSidebar-r_3Lk5Rp.js.br +0 -0
  257. package/dist/client/assets/RightSidebar-r_3Lk5Rp.js.gz +0 -0
  258. package/dist/client/assets/abnfDiagram-VCTEODGH-K6Ppkrbt.js.br +0 -0
  259. package/dist/client/assets/abnfDiagram-VCTEODGH-K6Ppkrbt.js.gz +0 -0
  260. package/dist/client/assets/app-CATnTUZF.js +0 -35
  261. package/dist/client/assets/app-CATnTUZF.js.br +0 -0
  262. package/dist/client/assets/app-CATnTUZF.js.gz +0 -0
  263. package/dist/client/assets/arc-Co2OQqtH.js.br +0 -0
  264. package/dist/client/assets/arc-Co2OQqtH.js.gz +0 -0
  265. package/dist/client/assets/architectureDiagram-5GKGNRK7-BQnl7tVF.js.br +0 -0
  266. package/dist/client/assets/architectureDiagram-5GKGNRK7-BQnl7tVF.js.gz +0 -0
  267. package/dist/client/assets/blockDiagram-I7D4REHJ-Bm5D5-qq.js.br +0 -0
  268. package/dist/client/assets/blockDiagram-I7D4REHJ-Bm5D5-qq.js.gz +0 -0
  269. package/dist/client/assets/c4Diagram-7LVT6UL2-B9oPpey8.js.br +0 -0
  270. package/dist/client/assets/c4Diagram-7LVT6UL2-B9oPpey8.js.gz +0 -0
  271. package/dist/client/assets/channel-Czv2W4qA.js +0 -1
  272. package/dist/client/assets/chunk-F27PBJKO-CJ1rotBe.js.br +0 -0
  273. package/dist/client/assets/chunk-F27PBJKO-CJ1rotBe.js.gz +0 -0
  274. package/dist/client/assets/chunk-IMKFNOWR-UYKZ5uOG.js.br +0 -0
  275. package/dist/client/assets/chunk-IMKFNOWR-UYKZ5uOG.js.gz +0 -0
  276. package/dist/client/assets/chunk-SVP7TREG-DCTDYa1E.js.br +0 -0
  277. package/dist/client/assets/chunk-SVP7TREG-DCTDYa1E.js.gz +0 -0
  278. package/dist/client/assets/chunk-TICWLB2K-wHiaOsBh.js.br +0 -0
  279. package/dist/client/assets/chunk-TICWLB2K-wHiaOsBh.js.gz +0 -0
  280. package/dist/client/assets/classDiagram-ZZMXUADV-BdvOOL9k.js +0 -1
  281. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BdvOOL9k.js +0 -1
  282. package/dist/client/assets/cose-bilkent-JH36ORCC-DnhlPBLr.js.br +0 -0
  283. package/dist/client/assets/cose-bilkent-JH36ORCC-DnhlPBLr.js.gz +0 -0
  284. package/dist/client/assets/cynefin-OW5HDTMX-BsjzNqJZ.js.br +0 -0
  285. package/dist/client/assets/cynefin-OW5HDTMX-BsjzNqJZ.js.gz +0 -0
  286. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-sVUb0zY5.js.br +0 -0
  287. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-sVUb0zY5.js.gz +0 -0
  288. package/dist/client/assets/dagre-GXQ25YYZ-YErk8S-c.js.br +0 -0
  289. package/dist/client/assets/dagre-GXQ25YYZ-YErk8S-c.js.gz +0 -0
  290. package/dist/client/assets/diagram-S7CK7UJ4-BaHbV5ho.js.br +0 -0
  291. package/dist/client/assets/diagram-S7CK7UJ4-BaHbV5ho.js.gz +0 -0
  292. package/dist/client/assets/diagram-UQ7AKVKN-BocCYx3V.js.br +0 -0
  293. package/dist/client/assets/diagram-UQ7AKVKN-BocCYx3V.js.gz +0 -0
  294. package/dist/client/assets/diagram-VSXAHHWV-ZMTYwWOQ.js.br +0 -0
  295. package/dist/client/assets/diagram-VSXAHHWV-ZMTYwWOQ.js.gz +0 -0
  296. package/dist/client/assets/diagram-VX7I27RA-xmk_EY1h.js.br +0 -0
  297. package/dist/client/assets/diagram-VX7I27RA-xmk_EY1h.js.gz +0 -0
  298. package/dist/client/assets/diagram-Z3DM3KII-DKDY_cG6.js.br +0 -0
  299. package/dist/client/assets/diagram-Z3DM3KII-DKDY_cG6.js.gz +0 -0
  300. package/dist/client/assets/ebnfDiagram-PWID7BFC-C_0z-Jx9.js.br +0 -0
  301. package/dist/client/assets/ebnfDiagram-PWID7BFC-C_0z-Jx9.js.gz +0 -0
  302. package/dist/client/assets/erDiagram-RLTQ6QDP-DHUaG7mt.js.br +0 -0
  303. package/dist/client/assets/erDiagram-RLTQ6QDP-DHUaG7mt.js.gz +0 -0
  304. package/dist/client/assets/flowDiagram-HODETNUW-BMNsdMBD.js.br +0 -0
  305. package/dist/client/assets/flowDiagram-HODETNUW-BMNsdMBD.js.gz +0 -0
  306. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BH1AiKuF.js.br +0 -0
  307. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BH1AiKuF.js.gz +0 -0
  308. package/dist/client/assets/gestureArbiter-DvupkPra.js.br +0 -0
  309. package/dist/client/assets/gestureArbiter-DvupkPra.js.gz +0 -0
  310. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DAgV0MV-.js.br +0 -0
  311. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DAgV0MV-.js.gz +0 -0
  312. package/dist/client/assets/index-Bi9A6QUP.css +0 -1
  313. package/dist/client/assets/index-Bi9A6QUP.css.br +0 -0
  314. package/dist/client/assets/index-Bi9A6QUP.css.gz +0 -0
  315. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BpZwsQvw.js.br +0 -0
  316. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BpZwsQvw.js.gz +0 -0
  317. package/dist/client/assets/journeyDiagram-3NMN7TZE-DXEeFLvw.js.br +0 -0
  318. package/dist/client/assets/journeyDiagram-3NMN7TZE-DXEeFLvw.js.gz +0 -0
  319. package/dist/client/assets/kanban-definition-UXKFOSKX-CV9ivL5u.js.br +0 -0
  320. package/dist/client/assets/kanban-definition-UXKFOSKX-CV9ivL5u.js.gz +0 -0
  321. package/dist/client/assets/layout-DsKKsJKX.js.br +0 -0
  322. package/dist/client/assets/layout-DsKKsJKX.js.gz +0 -0
  323. package/dist/client/assets/linear-Dl9MDNV3.js.br +0 -0
  324. package/dist/client/assets/linear-Dl9MDNV3.js.gz +0 -0
  325. package/dist/client/assets/mermaid.core-CxZFe3as.js.br +0 -0
  326. package/dist/client/assets/mermaid.core-CxZFe3as.js.gz +0 -0
  327. package/dist/client/assets/mindmap-definition-YA3MSWOX-C3ormYdL.js.br +0 -0
  328. package/dist/client/assets/mindmap-definition-YA3MSWOX-C3ormYdL.js.gz +0 -0
  329. package/dist/client/assets/passwordBootstrap-a8z6jJNY.js.br +0 -0
  330. package/dist/client/assets/passwordBootstrap-a8z6jJNY.js.gz +0 -0
  331. package/dist/client/assets/pegDiagram-XKGWAZYB-CG0wNylH.js.br +0 -0
  332. package/dist/client/assets/pegDiagram-XKGWAZYB-CG0wNylH.js.gz +0 -0
  333. package/dist/client/assets/pieDiagram-E7YTZNPT-D2-ydqyX.js.br +0 -0
  334. package/dist/client/assets/pieDiagram-E7YTZNPT-D2-ydqyX.js.gz +0 -0
  335. package/dist/client/assets/quadrantDiagram-AXDQQJYC-CK3fRC18.js.br +0 -0
  336. package/dist/client/assets/quadrantDiagram-AXDQQJYC-CK3fRC18.js.gz +0 -0
  337. package/dist/client/assets/railroadDiagram-O6MQD6OU-V4EFlydu.js.br +0 -0
  338. package/dist/client/assets/railroadDiagram-O6MQD6OU-V4EFlydu.js.gz +0 -0
  339. package/dist/client/assets/referenceLongPress-BjmYd1vB.js.br +0 -0
  340. package/dist/client/assets/referenceLongPress-BjmYd1vB.js.gz +0 -0
  341. package/dist/client/assets/requirementDiagram-BXWQKSXE-Di1_tyAX.js.br +0 -0
  342. package/dist/client/assets/requirementDiagram-BXWQKSXE-Di1_tyAX.js.gz +0 -0
  343. package/dist/client/assets/sankeyDiagram-P5KCCOFB-D-qpWNza.js.br +0 -0
  344. package/dist/client/assets/sankeyDiagram-P5KCCOFB-D-qpWNza.js.gz +0 -0
  345. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DmIxafrB.js.br +0 -0
  346. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DmIxafrB.js.gz +0 -0
  347. package/dist/client/assets/sizeCapture-INFHLROL-4MTh3tTt.js.br +0 -0
  348. package/dist/client/assets/sizeCapture-INFHLROL-4MTh3tTt.js.gz +0 -0
  349. package/dist/client/assets/stateDiagram-D77RDMKH-CgYgneIv.js.br +0 -0
  350. package/dist/client/assets/stateDiagram-D77RDMKH-CgYgneIv.js.gz +0 -0
  351. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CvPf0fxf.js +0 -1
  352. package/dist/client/assets/swimlanes-42K2YHIH-OWA9HFVf.js.br +0 -0
  353. package/dist/client/assets/swimlanes-42K2YHIH-OWA9HFVf.js.gz +0 -0
  354. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CZ_OauyW.js +0 -8
  355. package/dist/client/assets/timeline-definition-24CTP7MA-BAzx5DyO.js.br +0 -0
  356. package/dist/client/assets/timeline-definition-24CTP7MA-BAzx5DyO.js.gz +0 -0
  357. package/dist/client/assets/vennDiagram-4TSXK5OY-CBZ-PhTQ.js.br +0 -0
  358. package/dist/client/assets/vennDiagram-4TSXK5OY-CBZ-PhTQ.js.gz +0 -0
  359. package/dist/client/assets/videoCompression-BCnqCkRK.js.br +0 -0
  360. package/dist/client/assets/videoCompression-BCnqCkRK.js.gz +0 -0
  361. package/dist/client/assets/wardleyDiagram-VM6X3IG4-DbWzC6l5.js.br +0 -0
  362. package/dist/client/assets/wardleyDiagram-VM6X3IG4-DbWzC6l5.js.gz +0 -0
  363. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BmApnTWE.js.br +0 -0
  364. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BmApnTWE.js.gz +0 -0
@@ -4,8 +4,8 @@ export async function executeTaskCommand(command, request, io) {
4
4
  const o = command.options, operation = command.operation;
5
5
  const bodyOptions = ['content', 'file', 'stdin', 'idempotency-key'];
6
6
  const allowed = {
7
- list: ['group'], get: [], create: [...bodyOptions, 'group', 'title', 'constraints', 'acceptance', 'assignee', 'coordinator', 'parent', 'depends-on', 'managed', 'reviewers', 'shared-directory', 'integration'],
8
- assign: [...bodyOptions, 'assignee', 'revision'], report: [...bodyOptions, 'attempt', 'status', 'evidence'], ask: [...bodyOptions, 'attempt', 'options'],
7
+ list: ['group'], get: [], create: [...bodyOptions, 'group', 'title', 'constraints', 'acceptance', 'assignee', 'coordinator', 'parent', 'depends-on', 'managed', 'reviewers', 'shared-directory', 'integration', 'work-type'],
8
+ assign: [...bodyOptions, 'assignee', 'revision'], report: [...bodyOptions, 'attempt', 'status', 'evidence', 'summary'], ask: [...bodyOptions, 'attempt', 'options'],
9
9
  plan: [...bodyOptions, 'attempt', 'evidence'], review: [...bodyOptions, 'artifact', 'evidence', 'verdict'], comment: bodyOptions,
10
10
  'request-review': [...bodyOptions, 'artifact', 'assignee', 'revision'],
11
11
  revise: [...bodyOptions, 'revision'], close: [...bodyOptions, 'revision'], reopen: [...bodyOptions, 'revision'],
@@ -40,7 +40,7 @@ export async function executeTaskCommand(command, request, io) {
40
40
  return request('POST', '/tasks', { input: { idempotencyKey, groupId: o.group, title: o.title, spec: content,
41
41
  constraints: o.constraints, acceptance: o.acceptance, assigneeSessionId: o.assignee, coordinatorSessionId: o.coordinator,
42
42
  parentTaskId: o.parent, dependsOn: o['depends-on'] ? String(o['depends-on']).split(',').filter(Boolean) : undefined,
43
- integration: o.integration === true, managed: o.managed === true, isolated: o['shared-directory'] !== true,
43
+ integration: o.integration === true, managed: o.managed === true, isolated: o['shared-directory'] !== true, workType: o['work-type'],
44
44
  reviewerSessionIds: o.reviewers ? String(o.reviewers).split(',').filter(Boolean) : undefined } });
45
45
  }
46
46
  const options = o.options ? JSON.parse(String(o.options)) : undefined;
@@ -49,6 +49,6 @@ export async function executeTaskCommand(command, request, io) {
49
49
  return request('POST', `/tasks/${encodeURIComponent(command.target)}`, { input: {
50
50
  kind: operation === 'plan' ? 'submit-plan' : operation, idempotencyKey, expectedRevision, content,
51
51
  assigneeSessionId: o.assignee, attemptId: o.attempt, status: o.status, artifactId: o.artifact, options, verdict: o.verdict,
52
- evidence: o.evidence ? JSON.parse(String(o.evidence)) : undefined,
52
+ evidence: o.evidence ? JSON.parse(String(o.evidence)) : undefined, summary: o.summary,
53
53
  } });
54
54
  }
@@ -1,9 +1,19 @@
1
1
  import { randomBytes, createHash } from 'node:crypto';
2
2
  import { readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs';
3
3
  import { dirname } from 'node:path';
4
+ import { collaborationResultPresentation } from './collaborationResultPresentation.js';
4
5
  import { CollaborationError } from './collaborationProtocol.js';
5
6
  import { taskMemberKey } from './collaborationTaskTypes.js';
6
7
  const id = () => randomBytes(16).toString('hex');
8
+ const codeSteps = (tasks, rootId) => tasks.filter(t => {
9
+ if (t.workflow?.rootTaskId !== rootId || !t.workflow.isolated || t.workflow.workType === 'read-only' || t.workflow.integration || t.status === 'closed')
10
+ return false;
11
+ const accepted = t.artifacts.find(artifact => artifact.id === t.acceptedArtifactId);
12
+ const commit = accepted?.evidence?.commit;
13
+ // The service captures the worktree commit. A reviewed HEAD equal to its base
14
+ // proves there is no code delta to integrate, including legacy query tasks.
15
+ return !(t.status === 'accepted' && t.workspace && commit === t.workspace.base);
16
+ });
7
17
  const sameMember = (a, b) => a !== null && b !== null && taskMemberKey(a) === taskMemberKey(b);
8
18
  function text(value, name, limit = 32_000, required = false) {
9
19
  if (value !== undefined && typeof value !== 'string')
@@ -44,6 +54,8 @@ function validateSnapshot(task) {
44
54
  if (task.coordinator !== null)
45
55
  member(task.coordinator);
46
56
  if (task.workflow) {
57
+ if (task.workflow.workType !== undefined && (!['auto', 'code', 'read-only'].includes(task.workflow.workType) || task.workflow.workType === 'auto' && task.workflow.kind !== 'goal'))
58
+ invalid();
47
59
  if (!['goal', 'step'].includes(task.workflow.kind) || typeof task.workflow.paused !== 'boolean' || typeof task.workflow.isolated !== 'boolean'
48
60
  || !Array.isArray(task.workflow.reviewers) || task.workflow.reviewers.length > 32 || !Number.isInteger(task.workflow.maxRevisions)
49
61
  || task.workflow.maxRevisions < 1 || task.workflow.maxRevisions > 10 || task.workflow.rootTaskId && !validId(task.workflow.rootTaskId))
@@ -69,6 +81,10 @@ function validateSnapshot(task) {
69
81
  if (!event || !validId(event.id) || !Number.isSafeInteger(event.sequence) || event.sequence < 1 || typeof event.kind !== 'string'
70
82
  || typeof event.content !== 'string' || !time(event.createdAt) || event.attemptId !== null && !task.attempts.some(a => a.id === event.attemptId))
71
83
  invalid();
84
+ if (event.deliveryId !== undefined && !validId(event.deliveryId))
85
+ invalid();
86
+ if (event.source !== undefined && !['system', 'user'].includes(event.source))
87
+ invalid();
72
88
  if (event.actor !== null)
73
89
  member(event.actor);
74
90
  if (event.target)
@@ -85,6 +101,7 @@ function validateSnapshot(task) {
85
101
  if (!artifact || !validId(artifact.id) || !task.attempts.some(a => a.id === artifact.attemptId) || !['plan', 'result', 'review'].includes(artifact.kind)
86
102
  || typeof artifact.content !== 'string' || !time(artifact.createdAt))
87
103
  invalid();
104
+ text(artifact.summary, '结果摘要', 2400);
88
105
  member(artifact.actor);
89
106
  if (artifact.verdict && !['pass', 'changes', 'blocked'].includes(artifact.verdict))
90
107
  invalid();
@@ -144,9 +161,9 @@ export class CollaborationTaskStore {
144
161
  return { ...task, spec: task.spec.slice(0, 512), constraints: '', acceptance: '',
145
162
  deliveries: task.deliveries.filter(d => d.attemptId === task.activeAttemptId && d.error).slice(-1),
146
163
  attempts: current ? [{ ...current, report: current.report ? { ...current.report, content: '', evidence: undefined } : undefined }] : [],
147
- artifacts: [plan, result, ...task.artifacts.filter(a => a.kind === 'review' && a.reviewsArtifactId === result?.id)].filter((a) => !!a).map(a => ({ ...a, content: '', evidence: undefined })),
164
+ artifacts: [plan, result, ...task.artifacts.filter(a => a.kind === 'review' && a.reviewsArtifactId === result?.id)].filter((a) => !!a).map(a => ({ ...a, content: '', summary: (a.summary || (a.kind === 'result' && collaborationResultPresentation(a.content).condensed ? collaborationResultPresentation(a.content).summary : undefined))?.slice(0, 800), evidence: undefined })),
148
165
  decisions: task.decisions.filter(d => d.status === 'pending').map(d => ({ ...d, question: '', options: [] })),
149
- events: task.events.filter(e => ['revise', 'request-review'].includes(e.kind) && e.attemptId === task.activeAttemptId).slice(-4).map(e => ({ ...e, content: '' })) };
166
+ events: task.events.filter(e => ['revise', 'request-review'].includes(e.kind) && e.attemptId === task.activeAttemptId).slice(-4).map(e => ({ ...e, content: e.source === 'user' && e.kind === 'revise' ? e.content.slice(0, 512) : '' })) };
150
167
  }));
151
168
  }
152
169
  heads(groupIds, owner) { return this.document.tasks.filter(t => groupIds.includes(t.groupId) && t.ownerServiceId === owner).map(t => ({ id: t.id, revision: t.revision })); }
@@ -179,10 +196,10 @@ export class CollaborationTaskStore {
179
196
  task.attempts.push({ id: attemptId, assignee: member(assignee), createdAt: Date.now(), threadId });
180
197
  this.event(task, 'assigned', actor, content || '任务已分派');
181
198
  doc.outbox.push({ id: id(), taskId: task.id, attemptId, target: assignee, kind: 'task', threadId,
182
- content: `任务:${task.title}\n${task.spec}${task.constraints ? `\n约束:${task.constraints}` : ''}${task.acceptance ? `\n完成标准:${task.acceptance}` : ''}\n${content}\n任务 ID:${task.id}\n尝试 ID:${attemptId}\n用 td collab task get ${task.id} --text 查询完整上下文;用 td collab task report ${task.id} --attempt ${attemptId} --status ack --content '接手说明' 显式回复;遇必要问题用 task ask,提交结果用 report --status complete。如果同一尝试已经交付或任务已归档,不要重复实施。${task.workspace ? `\n代码只能在独立目录 ${task.workspace.cwd} 修改。分支 ${task.workspace.branch}。交付前提交代码,结果说明提交、变更、验证与限制。不能修改原仓库、合并到用户分支或发布。` : ''}${task.workflow?.kind === 'step' ? '\n完成报告会自动请求独立评审;评审要求修改时继续完成,重新提交 complete。只按用户授权运行测试。' : ''}` });
199
+ content: `本次执行身份:${assignee.sessionId}。所有 td collab 命令显式带 --session ${assignee.sessionId};这是服务核对的实际身份,正文中的模板身份和 tmux 标识不能替代它。\n任务:${task.title}\n${task.spec}${task.constraints ? `\n约束:${task.constraints}` : ''}${task.acceptance ? `\n完成标准:${task.acceptance}` : ''}\n${content}\n任务 ID:${task.id}\n尝试 ID:${attemptId}\n用 td collab task get ${task.id} --text 查询完整上下文;用 td collab task report ${task.id} --attempt ${attemptId} --status ack --content '接手说明' 显式回复;遇必要问题用 task ask,提交结果用 report --status complete --summary '简短结论、关键交付和限制'。摘要先回答用户问题,不放任务 ID、版本、分支或流程日志;完整证据保留在 --content/--file 正文中。如果同一尝试已经交付或任务已归档,不要重复实施。${task.workspace ? `\n代码只能在独立目录 ${task.workspace.cwd} 修改。分支 ${task.workspace.branch}。交付前提交代码,结果说明提交、变更、验证与限制。不能修改原仓库、合并到用户分支或发布。` : ''}${task.workflow?.workType === 'read-only' ? '\n这是只读查询或分析任务:在本机会话中采集并交付报告,不修改代码或系统,不创建提交、构建、部署或终止进程。结果需说明采样时间、证据和限制。' : ''}${task.workflow?.kind === 'step' ? '\n完成报告会自动请求独立评审;评审要求修改时继续完成,重新提交 complete。只按用户授权运行测试。' : ''}` });
183
200
  }
184
201
  goalInstructions(task) {
185
- return `你是此目标的协调者,负责把目标推进到可验收交付,不亲自实施子任务。\n目标:${task.spec}\n约束:${task.constraints || '未补充'}\n完成标准:${task.acceptance || '满足用户目标,说明交付与限制'}\n任务 ID:${task.id}\n用 td collab task get ${task.id} --json 读取 roster、子任务、完整记录和 revision。\n先报告 ack;拆分为可交付子任务,用 td collab task create --group ${task.groupId} --parent ${task.id} --title '标题' --content '完整要求' --assignee <roster里的成员sessionId> --acceptance '明确标准'。子任务继承自动评审、隔离目录与协调者。依赖任务用 --depends-on id,id,服务在依赖评审通过后自动分派。保持分工合理,不根据终端输出推断谁有空。\n每次收到明确更新后读取最新记录,处理失败与交接。服务自动安排独立评审、返工和依赖接续;不要重复请求评审或催促正在等待回复的成员。必要的人类决策用 task ask --attempt <本轮id>;方案需要用户确认时提交 task plan,不把普通回复当授权。\n代码任务需创建 --integration 的最终集成子任务,--depends-on 依赖全部实施任务;独立目录会包含依赖的提交。不得在原仓库自动合并或发布。\n结束本次协调用 td collab task coordinate ${task.id} --revision <最新版本> --content '已安排的工作和下一步';有新事件服务会再次唤起你。所有子任务评审通过后报告 complete,汇总交付目录、分支、提交、评审结论和限制,等待用户最终验收。任何异常都要留下明确说明,不伪造完成或进度。`;
202
+ return `你是此目标的协调者,负责把目标推进到可验收交付,不亲自实施子任务。\n目标:${task.spec}\n约束:${task.constraints || '未补充'}\n完成标准:${task.acceptance || '满足用户目标,说明交付与限制'}\n任务 ID:${task.id}\n当前协调身份:${task.coordinator?.sessionId};所有命令显式带 --session ${task.coordinator?.sessionId},不使用 tmux 标识。\n用 td collab task get ${task.id} --json 读取 roster、子任务、完整记录和 revision。\n先报告 ack;拆分为可交付子任务,用 td collab task create --group ${task.groupId} --parent ${task.id} --title '标题' --content '完整要求' --assignee <roster里的成员sessionId> --acceptance '明确标准' --work-type read-only|code。先按目标明确分类:查询、检查、研究、分析用 read-only,复用现有执行成员会话,不创建 Git 目录;修改代码用 code,才创建独立目录。简单问题只建一个必要任务,不为了形式拆成采集、复述、汇总多份任务。子任务继承自动评审与协调者。依赖任务用 --depends-on id,id,服务在依赖评审通过后自动分派。保持分工合理,不根据终端输出推断谁有空。\n每次收到明确更新后读取最新记录,处理失败与交接。服务自动安排独立评审、返工和依赖接续;不要重复请求评审或催促正在等待回复的成员。必要的人类决策用 task ask --attempt <本轮id>;方案需要用户确认时提交 task plan,不把普通回复当授权。\n只有已交付代码任务实际包含相对 base 的提交变化时,才需创建 --work-type code --integration 的最终集成子任务,--depends-on 依赖全部代码实施任务;独立目录会包含依赖的提交。不得在原仓库自动合并或发布。\n结束本次协调用 td collab task coordinate ${task.id} --revision <最新版本> --content '已安排的工作和下一步';有新事件服务会再次唤起你。所有子任务评审通过后报告 complete --summary '直接回答用户的简短结果和限制';只读目标直接汇总已核对结果,不创建集成或重复汇总任务。正文保留证据、评审结论和限制,仅代码目标补充目录、分支和提交,等待用户最终验收。任何异常都要留下明确说明,不伪造完成或进度。`;
186
203
  }
187
204
  notifyCoordinator(doc, task, update) {
188
205
  if (!task.coordinator)
@@ -214,6 +231,10 @@ export class CollaborationTaskStore {
214
231
  if (input.managed !== undefined && typeof input.managed !== 'boolean' || input.isolated !== undefined && typeof input.isolated !== 'boolean'
215
232
  || input.integration !== undefined && typeof input.integration !== 'boolean')
216
233
  throw new CollaborationError('INVALID_WORKFLOW', '协作模式无效');
234
+ if (input.workType !== undefined && !['code', 'read-only'].includes(input.workType))
235
+ throw new CollaborationError('INVALID_WORK_TYPE', '任务类型必须是 code 或 read-only');
236
+ if (input.integration && input.workType === 'read-only')
237
+ throw new CollaborationError('INVALID_WORK_TYPE', '只读任务不需要代码集成');
217
238
  if (input.reviewers && (!Array.isArray(input.reviewers) || input.reviewers.length > 32))
218
239
  throw new CollaborationError('INVALID_REVIEWERS', '评审成员列表无效');
219
240
  input.reviewers?.forEach(member);
@@ -230,14 +251,17 @@ export class CollaborationTaskStore {
230
251
  }
231
252
  const now = Date.now();
232
253
  const parent = doc.tasks.find(t => t.id === input.parentTaskId);
254
+ const root = parent?.workflow?.rootTaskId ? doc.tasks.find(t => t.id === parent.workflow.rootTaskId) : parent;
255
+ if (parent?.workflow && !input.workType)
256
+ throw new CollaborationError('WORK_TYPE_REQUIRED', '请先明确子任务类型:查询、分析用 --work-type read-only;修改代码用 --work-type code');
233
257
  if (parent?.workflow && actor && !sameMember(actor, parent.coordinator))
234
258
  throw new CollaborationError('TASK_PERMISSION_DENIED', '只有协调者可以创建此目标的子任务', 403);
235
259
  if (parent?.status !== undefined && parent.status !== 'open')
236
260
  throw new CollaborationError('TASK_ARCHIVED', '目标已归档', 409);
237
261
  if (parent?.workflow && doc.tasks.filter(t => t.workflow?.rootTaskId === (parent.workflow?.rootTaskId ?? parent.id)).length >= 32)
238
262
  throw new CollaborationError('GOAL_TASK_LIMIT', '单个目标最多 32 个子任务,请收敛分工或建立后续目标', 409);
239
- const workflow = parent?.workflow ? { ...parent.workflow, kind: 'step', rootTaskId: parent.workflow.rootTaskId ?? parent.id, paused: false, integration: input.integration === true }
240
- : input.managed ? { kind: 'goal', reviewers: input.reviewers ?? [], isolated: input.isolated !== false, paused: false, maxRevisions: 3, maxParallel: Math.min(4, input.reviewers?.length ?? 1) } : undefined;
263
+ const workflow = parent?.workflow ? { ...parent.workflow, kind: 'step', rootTaskId: parent.workflow.rootTaskId ?? parent.id, paused: false, integration: input.integration === true, workType: input.workType ?? 'code', isolated: input.workType === 'read-only' ? false : root?.workflow?.isolated ?? parent.workflow.isolated }
264
+ : input.managed ? { kind: 'goal', workType: 'auto', reviewers: input.reviewers ?? [], isolated: input.isolated !== false, paused: false, maxRevisions: 3, maxParallel: Math.min(4, input.reviewers?.length ?? 1) } : undefined;
241
265
  if (workflow?.kind === 'goal' && (!input.coordinator || workflow.reviewers.length < 1))
242
266
  throw new CollaborationError('WORKFLOW_MEMBERS_REQUIRED', '请选择协调者和至少一位执行成员', 400);
243
267
  if (workflow?.kind === 'goal' && !workflow.reviewers.some(m => !sameMember(m, input.coordinator ?? null)))
@@ -251,7 +275,7 @@ export class CollaborationTaskStore {
251
275
  if (workflow?.rootTaskId && doc.tasks.find(t => t.id === workflow.rootTaskId)?.workflow?.paused)
252
276
  throw new CollaborationError('WORKFLOW_PAUSED', '目标已暂停,请先继续协调');
253
277
  if (workflow?.integration) {
254
- const required = doc.tasks.filter(t => t.workflow?.rootTaskId === workflow.rootTaskId && !t.workflow?.integration && t.status !== 'closed');
278
+ const required = codeSteps(doc.tasks, workflow.rootTaskId);
255
279
  if (!required.length || required.some(t => !dependsOn.includes(t.id)))
256
280
  throw new CollaborationError('INTEGRATION_DEPENDENCIES_REQUIRED', '集成任务必须依赖目标下全部实施子任务');
257
281
  }
@@ -284,6 +308,9 @@ export class CollaborationTaskStore {
284
308
  }
285
309
  apply(taskId, input, actor) {
286
310
  const content = text(input.content, '内容');
311
+ const summary = text(input.summary, '结果摘要', 2400);
312
+ if (summary && (input.kind !== 'report' || input.status !== 'complete'))
313
+ throw new CollaborationError('INVALID_SUMMARY', '摘要只能随完成结果提交');
287
314
  if (input.verdict !== undefined && !['pass', 'changes', 'blocked'].includes(input.verdict))
288
315
  throw new CollaborationError('INVALID_VERDICT', '评审结论无效');
289
316
  if (input.evidence !== undefined && Buffer.byteLength(JSON.stringify(input.evidence)) > 16_384)
@@ -302,12 +329,30 @@ export class CollaborationTaskStore {
302
329
  throw new CollaborationError('USER_DECISION_REQUIRED', '回答、方案确认与验收需要用户操作', 403);
303
330
  if (!['report', 'ask', 'submit-plan', 'review', 'comment', 'answer'].includes(input.kind) && input.expectedRevision !== task.revision)
304
331
  throw new CollaborationError('TASK_CHANGED', '任务已更新,请查看最新记录后重试', 409);
305
- if (task.status !== 'open' && !['reopen', 'report'].includes(input.kind))
332
+ const userFollowup = actor === null && input.kind === 'revise' && !!content;
333
+ if (task.status !== 'open' && !['reopen', 'report'].includes(input.kind) && !(task.status === 'accepted' && userFollowup))
306
334
  throw new CollaborationError('TASK_ARCHIVED', '任务已归档;需要继续时请重新打开', 409);
307
- if (task.workflow && task.status !== 'open' && input.kind !== 'reopen')
335
+ if (task.workflow && task.status !== 'open' && input.kind !== 'reopen' && !(task.status === 'accepted' && userFollowup))
308
336
  throw new CollaborationError('TASK_ARCHIVED', '自动协作的已完成版本不能追加执行报告,请先重新打开', 409);
309
- if (task.workflow?.rootTaskId && doc.tasks.find(t => t.id === task.workflow.rootTaskId)?.status !== 'open')
337
+ const root = task.workflow?.rootTaskId ? doc.tasks.find(t => t.id === task.workflow.rootTaskId) : undefined;
338
+ if (root && root.status !== 'open' && !(root.status === 'accepted' && userFollowup))
310
339
  throw new CollaborationError('GOAL_ARCHIVED', '请先重新打开所属目标,再继续子任务', 409);
340
+ if (userFollowup && task.status === 'accepted') {
341
+ task.status = 'open';
342
+ delete task.acceptedArtifactId;
343
+ delete task.completionMode;
344
+ this.event(task, 'reopen', null, '用户要求继续跟进,保留上一版交付');
345
+ }
346
+ if (userFollowup && root?.status === 'accepted') {
347
+ root.status = 'open';
348
+ delete root.acceptedArtifactId;
349
+ delete root.completionMode;
350
+ if (root.workflow)
351
+ root.workflow.paused = false;
352
+ this.event(root, 'reopen', null, '用户要求继续子任务,所属目标同步重新开启');
353
+ root.revision++;
354
+ root.updatedAt = Date.now();
355
+ }
311
356
  const attempt = task.attempts.find(a => a.id === input.attemptId);
312
357
  if (['report', 'ask', 'submit-plan'].includes(input.kind) && (!attempt || !sameMember(actor, attempt.assignee)))
313
358
  throw new CollaborationError('ATTEMPT_PERMISSION_DENIED', '只能对自己负责的执行尝试提交报告', 403);
@@ -357,8 +402,8 @@ export class CollaborationTaskStore {
357
402
  || doc.tasks.some(t => t.workflow?.rootTaskId === task.id && !['accepted', 'closed'].includes(t.status))))
358
403
  throw new CollaborationError('CHILD_TASKS_PENDING', '请先完成全部子任务的独立评审,再汇总交付', 409);
359
404
  if (input.status === 'complete' && task.workflow?.kind === 'goal' && task.workflow.isolated) {
360
- const steps = doc.tasks.filter(t => t.workflow?.rootTaskId === task.id && !t.workflow.integration && t.status !== 'closed');
361
- if (!doc.tasks.some(t => t.workflow?.rootTaskId === task.id && t.workflow.integration && t.status === 'accepted' && steps.every(s => t.dependsOn.includes(s.id))))
405
+ const steps = codeSteps(doc.tasks, task.id);
406
+ if (steps.length && !doc.tasks.some(t => t.workflow?.rootTaskId === task.id && t.workflow.integration && t.status === 'accepted' && steps.every(s => t.dependsOn.includes(s.id))))
362
407
  throw new CollaborationError('INTEGRATION_PENDING', '代码目标需要依赖全部实施任务的集成交付通过评审', 409);
363
408
  }
364
409
  if (input.status === 'complete') {
@@ -371,7 +416,7 @@ export class CollaborationTaskStore {
371
416
  event.reportStatus = input.status;
372
417
  event.evidence = input.evidence;
373
418
  if (input.status === 'complete')
374
- task.artifacts.push({ id: id(), attemptId: attempt.id, kind: 'result', content, evidence: input.evidence, actor: actor, createdAt: event.createdAt });
419
+ task.artifacts.push({ id: id(), attemptId: attempt.id, kind: 'result', content, ...(summary ? { summary } : {}), evidence: input.evidence, actor: actor, createdAt: event.createdAt });
375
420
  }
376
421
  else if (input.kind === 'ask') {
377
422
  if (!content || input.options !== undefined && (!Array.isArray(input.options) || input.options.length > 8 || input.options.some(v => typeof v !== 'string' || !v.trim() || v.length > 500)))
@@ -437,8 +482,8 @@ export class CollaborationTaskStore {
437
482
  if (task.workflow?.kind === 'goal' && doc.tasks.some(t => t.workflow?.rootTaskId === task.id && !['accepted', 'closed'].includes(t.status)))
438
483
  throw new CollaborationError('CHILD_TASKS_PENDING', '子任务尚未全部完成', 409);
439
484
  if (task.workflow?.kind === 'goal' && task.workflow.isolated) {
440
- const steps = doc.tasks.filter(t => t.workflow?.rootTaskId === task.id && !t.workflow.integration && t.status !== 'closed');
441
- if (!doc.tasks.some(t => t.workflow?.rootTaskId === task.id && t.workflow.integration && t.status === 'accepted' && steps.every(s => t.dependsOn.includes(s.id))))
485
+ const steps = codeSteps(doc.tasks, task.id);
486
+ if (steps.length && !doc.tasks.some(t => t.workflow?.rootTaskId === task.id && t.workflow.integration && t.status === 'accepted' && steps.every(s => t.dependsOn.includes(s.id))))
442
487
  throw new CollaborationError('INTEGRATION_PENDING', '需要全部实施任务的集成交付通过评审');
443
488
  }
444
489
  if (task.dependsOn.some(dep => doc.tasks.find(t => t.id === dep)?.status !== 'accepted'))
@@ -463,11 +508,25 @@ export class CollaborationTaskStore {
463
508
  else if (input.kind === 'revise' || input.kind === 'comment') {
464
509
  if (!content)
465
510
  throw new CollaborationError('INVALID_COMMENT', '反馈不能为空');
466
- this.event(task, input.kind, actor, content);
511
+ const event = this.event(task, input.kind, actor, content);
512
+ if (actor === null)
513
+ event.source = 'user';
467
514
  const recipient = task.attempts.find(a => a.id === task.activeAttemptId);
468
- if (recipient && !sameMember(actor, recipient.assignee))
469
- doc.outbox.push({ id: id(), taskId: task.id, attemptId: recipient.id, target: recipient.assignee,
470
- kind: 'message', threadId: recipient.threadId, content: `任务「${task.title}」${input.kind === 'revise' ? '要求修改' : '补充反馈'}:\n${content}\n任务 ID:${task.id}\n尝试 ID:${recipient.id}` });
515
+ if (recipient && !sameMember(actor, recipient.assignee)) {
516
+ event.target = recipient.assignee;
517
+ event.deliveryId = id();
518
+ doc.outbox.push({ id: event.deliveryId, taskId: task.id, attemptId: recipient.id, target: recipient.assignee,
519
+ kind: 'message', threadId: recipient.threadId, content: `任务「${task.title}」${input.kind === 'revise' ? actor === null ? '用户要求继续跟进' : '修改要求' : '补充反馈'}:\n${content}\n任务 ID:${task.id}\n尝试 ID:${recipient.id}${task.workflow?.kind === 'goal' ? `\n本轮继续沿用此目标和已交付证据,只推进新增要求。\n${this.goalInstructions(task)}` : '\n按这次要求继续当前分派,重新提交 complete 和摘要;新版本会重新独立评审。'}` });
520
+ if (task.workflow?.kind === 'goal' && task.coordination) {
521
+ task.coordination.notifiedSequence = event.sequence;
522
+ task.coordination.notifiedAt = Date.now();
523
+ }
524
+ }
525
+ if (input.kind === 'revise') {
526
+ delete task.automationIssue;
527
+ if (userFollowup && task.workflow)
528
+ task.workflow.paused = false;
529
+ }
471
530
  }
472
531
  else if (input.kind === 'close' || input.kind === 'reopen') {
473
532
  task.status = input.kind === 'close' ? 'closed' : 'open';
@@ -535,7 +594,7 @@ export class CollaborationTaskStore {
535
594
  this.event(root, 'child-update', null, `子任务「${task.title}」(${task.id}):${content}`);
536
595
  const result = root.artifacts.filter(a => a.kind === 'result' && a.attemptId === root.activeAttemptId).at(-1);
537
596
  if (result && !root.events.some(e => e.kind === 'revise' && e.attemptId === root.activeAttemptId && e.createdAt >= result.createdAt)) {
538
- this.event(root, 'revise', null, '子任务在汇总之后发生变化,请重新核对并提交最终结果');
597
+ this.event(root, 'revise', null, '子任务在汇总之后发生变化,请重新核对并提交最终结果').source = 'system';
539
598
  }
540
599
  root.revision++;
541
600
  root.updatedAt = Date.now();
@@ -604,7 +663,7 @@ export class CollaborationTaskStore {
604
663
  && !['coordinate', 'request-review', 'assigned'].includes(e.kind) && !sameMember(e.actor, task.coordinator)))
605
664
  return 'wake';
606
665
  if (!task.automationIssue && task.coordination.acknowledgedSequence < task.coordination.notifiedSequence
607
- && !result && Date.now() - task.coordination.notifiedAt > 15 * 60_000)
666
+ && (!result || task.events.some(e => e.kind === 'revise' && e.attemptId === task.activeAttemptId && e.createdAt >= result.createdAt)) && Date.now() - task.coordination.notifiedAt > 15 * 60_000)
608
667
  return 'coordination-timeout';
609
668
  }
610
669
  return null;
@@ -632,7 +691,7 @@ export class CollaborationTaskStore {
632
691
  event.target = reviewer;
633
692
  event.source = 'system';
634
693
  doc.outbox.push({ id: id(), taskId: task.id, attemptId: task.activeAttemptId, target: reviewer, kind: 'message', threadId: attempt.threadId,
635
- content: `请独立评审「${task.title}」版本 ${result.id}。用 td collab task get ${task.id} --json 查看目标、结果和代码目录。${task.workspace ? `代码目录 ${task.workspace.cwd},分支 ${task.workspace.branch};只读检查此目录,不修改原仓库。` : ''}\n提交 td collab task review ${task.id} --artifact ${result.id} --verdict pass|changes|blocked --content '结论、具体问题与证据'。pass 表示符合完成标准;changes 给出可执行修改要求;blocked 留下必须处理的原因。只按用户授权运行测试,未执行不能称通过。` });
694
+ content: `本次评审身份:${reviewer.sessionId};所有命令带 --session ${reviewer.sessionId},不使用作者或模板身份。\n请独立评审「${task.title}」版本 ${result.id}。用 td collab task get ${task.id} --json 查看目标、结果、摘要和证据;摘要必须与正文一致,遗漏关键限制或夸大结果需要求修改。${task.workspace ? `代码目录 ${task.workspace.cwd},分支 ${task.workspace.branch};只读检查此目录,不修改原仓库。` : ''}\n提交 td collab task review ${task.id} --artifact ${result.id} --verdict pass|changes|blocked --content '结论、具体问题与证据'。pass 表示符合完成标准;changes 给出可执行修改要求;blocked 留下必须处理的原因。只按用户授权运行测试,未执行不能称通过。` });
636
695
  }
637
696
  this.touchParent(doc, task, '已自动请求独立评审');
638
697
  }
@@ -644,7 +703,8 @@ export class CollaborationTaskStore {
644
703
  this.touchParent(doc, task, '独立评审通过,后续依赖可以接续');
645
704
  }
646
705
  else if (action === 'review-feedback') {
647
- const rounds = task.events.filter(e => e.kind === 'revise' && e.actor === null && e.artifactId && e.attemptId === task.activeAttemptId).length;
706
+ const boundary = task.events.filter(e => e.kind === 'revise' && e.source === 'user' && e.attemptId === task.activeAttemptId).at(-1)?.sequence ?? 0;
707
+ const rounds = task.events.filter(e => e.kind === 'revise' && e.actor === null && e.artifactId && e.attemptId === task.activeAttemptId && e.sequence > boundary).length;
648
708
  const blocked = review.verdict === 'blocked' || rounds >= task.workflow.maxRevisions;
649
709
  const event = this.event(task, blocked ? 'review-blocked' : 'revise', null, review.content);
650
710
  event.artifactId = result.id;
@@ -659,7 +719,7 @@ export class CollaborationTaskStore {
659
719
  else if (action === 'wake') {
660
720
  const updates = task.events.filter(e => e.sequence > task.coordination.notifiedSequence && e.kind !== 'coordinate');
661
721
  doc.outbox.push({ id: id(), taskId: task.id, attemptId: task.activeAttemptId, target: task.coordinator, kind: 'message', threadId: task.id,
662
- content: `目标「${task.title}」有新的明确事件,请继续协调:\n${updates.slice(-8).map(e => `${new Date(e.createdAt).toISOString()} ${e.content.slice(0, 1200)}`).join('\n')}\n用 td collab task get ${task.id} --json 读取完整上下文与子任务;安排必要下一步后 task coordinate ${task.id} --revision <最新版本> --content '协调说明'。自动评审和依赖已由服务接续,不要重复分派。最终汇总已经通过评审时等待用户验收,不要重复提交相同结果。` });
722
+ content: `当前协调身份:${task.coordinator.sessionId};命令带 --session ${task.coordinator.sessionId}。\n目标「${task.title}」有新的明确事件,请继续协调:\n${updates.slice(-8).map(e => `${new Date(e.createdAt).toISOString()} ${e.content.slice(0, 1200)}`).join('\n')}\n用 td collab task get ${task.id} --json 读取完整上下文与子任务;安排必要下一步后 task coordinate ${task.id} --revision <最新版本> --content '协调说明'。自动评审和依赖已由服务接续,不要重复分派。\n当前流程指南:查询、检查、分析必须 --work-type read-only,复用 roster 现有执行成员;只有修改代码才用 code 和独立目录。只读报告由你直接汇总,不新增仅转述的汇总子任务。已评审且 HEAD 等于 base 的交付没有代码变化,不需要集成;有代码变化才安排必要集成。用户有新要求时继续本目标,交付新版本,不重做已有采样。最终汇总通过评审且无新要求时等待用户验收,不重复提交相同结果。` });
663
723
  task.coordination.notifiedSequence = task.events.at(-1).sequence;
664
724
  task.coordination.notifiedAt = Date.now();
665
725
  }
@@ -736,7 +796,9 @@ export class CollaborationTaskStore {
736
796
  task.automationIssue = `消息投递${receipt.status === 'expired' ? '过期' : '失败'}:${receipt.error ?? '请检查成员终端后重试'}`;
737
797
  this.touchParent(doc, task, task.automationIssue);
738
798
  }
739
- else if (receipt.status === 'delivered' && task.automationIssue?.startsWith('消息投递正在重试'))
799
+ else if (receipt.status === 'delivered' && task.automationIssue?.startsWith('消息投递')
800
+ && !doc.outbox.some(o => o.taskId === task.id && o.lastError)
801
+ && !task.deliveries.some(d => d.attemptId === task.activeAttemptId && ['failed', 'expired'].includes(d.status)))
740
802
  delete task.automationIssue;
741
803
  });
742
804
  }
@@ -1,6 +1,6 @@
1
1
  import { execFile } from 'node:child_process';
2
2
  import { promisify } from 'node:util';
3
- import { mkdir, readFile, writeFile, rename, realpath } from 'node:fs/promises';
3
+ import { mkdir, readFile, writeFile, rename, realpath, stat } from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
  import os from 'node:os';
6
6
  const exec = promisify(execFile);
@@ -98,3 +98,68 @@ export async function captureTaskCommit(workspace) {
98
98
  const commit = await git(workspace.cwd, ['rev-parse', 'HEAD']);
99
99
  return { commit, branch: workspace.branch, cwd: workspace.cwd, base: workspace.base };
100
100
  }
101
+ async function assertManagedWorkspace(workspace) {
102
+ const relative = path.relative(home, workspace.cwd);
103
+ if (!relative || relative.startsWith('..') || path.isAbsolute(relative)
104
+ || !/^termdock\/task\/[a-f0-9]{32}-[a-f0-9]{8}$/.test(workspace.branch))
105
+ throw new Error('目录不是 Termdock 独立执行工作区');
106
+ const actual = await realpath(workspace.cwd);
107
+ if (actual !== path.resolve(workspace.cwd) || !actual.startsWith(`${await realpath(home)}${path.sep}`)
108
+ || await git(actual, ['rev-parse', '--show-toplevel']) !== actual
109
+ || await git(actual, ['branch', '--show-current']) !== workspace.branch)
110
+ throw new Error('执行目录或分支发生变化,现场已保留');
111
+ const common = async (cwd) => realpath(path.resolve(cwd, await git(cwd, ['rev-parse', '--git-common-dir'])));
112
+ if (await common(actual) !== await common(workspace.repository))
113
+ throw new Error('执行目录不属于原仓库,现场已保留');
114
+ }
115
+ /** Remove only a verified, clean worktree. Keep its branch and all Git commits. */
116
+ export async function archiveTaskWorkspace(workspace) {
117
+ try {
118
+ await assertManagedWorkspace(workspace);
119
+ const commit = await git(workspace.cwd, ['rev-parse', 'HEAD']);
120
+ if (await git(workspace.cwd, ['status', '--porcelain']) || await git(workspace.cwd, ['ls-files', '--others', '--ignored', '--exclude-standard'])) {
121
+ return { state: 'retained', reason: '目录含未提交或忽略的文件,已保留现场', commit };
122
+ }
123
+ await git(workspace.repository, ['worktree', 'remove', workspace.cwd]);
124
+ return { state: 'removed', commit };
125
+ }
126
+ catch (error) {
127
+ return { state: 'retained', reason: error instanceof Error ? error.message : String(error) };
128
+ }
129
+ }
130
+ export async function snapshotTaskWorkspace(workspace) {
131
+ await assertManagedWorkspace(workspace);
132
+ return git(workspace.cwd, ['rev-parse', 'HEAD']);
133
+ }
134
+ export async function restoreTaskWorkspace(workspace, cleanup) {
135
+ if (cleanup.state !== 'removed') {
136
+ try {
137
+ await assertManagedWorkspace(workspace);
138
+ return;
139
+ }
140
+ catch (error) {
141
+ if (cleanup.state !== 'pending' || error.code !== 'ENOENT')
142
+ throw error;
143
+ }
144
+ }
145
+ const relative = path.relative(home, workspace.cwd);
146
+ if (!relative || relative.startsWith('..') || path.isAbsolute(relative)
147
+ || !/^termdock\/task\/[a-f0-9]{32}-[a-f0-9]{8}$/.test(workspace.branch) || !cleanup.commit)
148
+ throw new Error('工作区恢复记录无效');
149
+ try {
150
+ await stat(workspace.cwd);
151
+ await assertManagedWorkspace(workspace);
152
+ return;
153
+ }
154
+ catch (error) {
155
+ if (error.code !== 'ENOENT')
156
+ throw error;
157
+ }
158
+ if (await git(workspace.repository, ['rev-parse', workspace.branch]) !== cleanup.commit)
159
+ throw new Error('归档分支已有变化,请核对后再恢复;不会重置分支');
160
+ await mkdir(path.dirname(workspace.cwd), { recursive: true, mode: 0o700 });
161
+ if (!(await realpath(path.dirname(workspace.cwd))).startsWith(`${await realpath(home)}${path.sep}`))
162
+ throw new Error('工作区父目录发生变化');
163
+ await git(workspace.repository, ['worktree', 'add', workspace.cwd, workspace.branch]);
164
+ await assertManagedWorkspace(workspace);
165
+ }
@@ -0,0 +1,16 @@
1
+ /** Provision only missing roles. Persist each member before proceeding so a
2
+ * failed launch, retry or server restart cannot create another full team. */
3
+ export async function ensureTeam(options) {
4
+ const ids = [];
5
+ const labels = [`自动协调者 (${options.agentSlug})`, `自动执行与评审 (${options.agentSlug})`];
6
+ for (const role of labels) {
7
+ const members = options.members();
8
+ const reserved = members.find(member => member.role?.startsWith(role.split(' (')[0]));
9
+ if (reserved && (reserved.role !== role || reserved.cwd !== options.cwd)) {
10
+ throw new Error('本组已有自动配置的成员,请使用原 Agent 和目录,或在成员设置中移出后重新配置');
11
+ }
12
+ const existing = reserved ?? members.find(member => !ids.includes(member.id) && member.agentSlug === options.agentSlug && member.cwd === options.cwd);
13
+ ids.push(existing?.id ?? await options.spawn(role));
14
+ }
15
+ return { coordinatorSessionId: ids[0], reviewerSessionIds: [ids[1]] };
16
+ }
@@ -0,0 +1,52 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { normalizePersistedAgentResumeInfo } from './resumePersistence.js';
4
+ /** Explicit archives are durable records, independent of the recent-history cap. */
5
+ export class ExecutionArchiveStore {
6
+ file;
7
+ entries;
8
+ constructor(file) {
9
+ this.file = file;
10
+ try {
11
+ const document = JSON.parse(fs.readFileSync(file, 'utf8'));
12
+ if (document.version !== 1 || !Array.isArray(document.entries))
13
+ throw new Error('执行归档文件格式无效');
14
+ this.entries = document.entries.map((entry) => {
15
+ const agent = normalizePersistedAgentResumeInfo(entry.agent);
16
+ if (!agent || !entry.sessionId || !entry.workspace?.cwd || !Array.isArray(entry.groups) || !Array.isArray(entry.taskIds))
17
+ throw new Error('执行归档记录无效');
18
+ return { ...entry, agent };
19
+ });
20
+ }
21
+ catch (error) {
22
+ if (error.code !== 'ENOENT')
23
+ throw error;
24
+ this.entries = [];
25
+ }
26
+ }
27
+ list() { return structuredClone(this.entries); }
28
+ get(id) { return this.list().find(entry => entry.sessionId === id); }
29
+ save(entry) {
30
+ if (!normalizePersistedAgentResumeInfo(entry.agent))
31
+ throw new Error('没有可恢复的 Agent 原生会话 ID,终端已保留');
32
+ const entries = [structuredClone(entry), ...this.entries.filter(item => item.sessionId !== entry.sessionId)];
33
+ fs.mkdirSync(path.dirname(this.file), { recursive: true, mode: 0o700 });
34
+ const temporary = `${this.file}.${process.pid}.tmp`;
35
+ fs.writeFileSync(temporary, JSON.stringify({ version: 1, entries }), { mode: 0o600 });
36
+ fs.renameSync(temporary, this.file);
37
+ this.entries = entries;
38
+ return structuredClone(entry);
39
+ }
40
+ }
41
+ export function completedExecution(tasks, serviceId, sessionId) {
42
+ const matches = (member) => member?.serviceId === serviceId && member.sessionId === sessionId;
43
+ if (tasks.some(task => matches(task.coordinator) || task.workflow?.reviewers.some(matches)
44
+ || task.status === 'open' && (matches(task.attempts.find(a => a.id === task.activeAttemptId)?.assignee) || matches(task.scheduledAssignee)))) {
45
+ throw new Error('此会话仍承担协作工作,不能归档;请完成当前任务后再操作');
46
+ }
47
+ const completed = tasks.filter(task => task.status !== 'open' && task.workspace && task.workflow?.kind === 'step' && task.workflow.isolated
48
+ && matches(task.attempts.find(a => a.id === task.activeAttemptId)?.assignee));
49
+ if (!completed.length)
50
+ throw new Error('此会话没有已完成的独立执行任务,终端已保留');
51
+ return completed;
52
+ }
@@ -32,6 +32,7 @@ import { fileURLToPath } from 'url';
32
32
  import { WebSocketServer } from 'ws';
33
33
  import terminalRoutes, { handleTerminalWebSocket, handleControlWebSocket, resolveStableFederationSessionId } from './routes/terminal.js';
34
34
  import androidRoutes, { handleAndroidWebSocket } from './routes/android.js';
35
+ import computerRoutes, { handleComputerWebSocket, stopComputerBridges } from './routes/computer.js';
35
36
  import { exitWithStartupFailure } from './utils/startupFailure.js';
36
37
  import { androidRecordings } from './android/recording.js';
37
38
  import { stopAllScrcpySessions } from './android/scrcpy.js';
@@ -349,6 +350,9 @@ export function createApp(options = {}) {
349
350
  app.use('/api/android', requireAuth({ bypass: isTrustedLocalCliRequest }));
350
351
  app.use('/api/android', csrfProtection.verifyMiddleware({ bypass: isTrustedLocalCliRequest }));
351
352
  app.use('/api/android', androidRoutes);
353
+ app.use('/api/computer', requireAuth({ bypass: isTrustedLocalCliRequest }));
354
+ app.use('/api/computer', csrfProtection.verifyMiddleware({ bypass: isTrustedLocalCliRequest }));
355
+ app.use('/api/computer', computerRoutes);
352
356
  if (fs.existsSync(bundledClientIndexPath)) {
353
357
  const clientPath = pinBundledRuntimeClientDist(bundledClientDistPath);
354
358
  const compression = createStaticCompressionMiddleware(clientPath);
@@ -500,8 +504,9 @@ export function startServer(options = {}) {
500
504
  target.close(); });
501
505
  server.once('close', () => relayRouter.close());
502
506
  server.once('close', () => { void stopAllScrcpySessions(); void androidRecordings.stopAll(); });
507
+ server.once('close', stopComputerBridges);
503
508
  const federation = createFederationRuntime(app, path.join(homedir(), '.termdock', 'federation'), {
504
- terminal: handleTerminalWebSocket, control: handleControlWebSocket, android: handleAndroidWebSocket,
509
+ terminal: handleTerminalWebSocket, control: handleControlWebSocket, android: handleAndroidWebSocket, computer: handleComputerWebSocket,
505
510
  }, {
506
511
  resolveSessionId: resolveStableFederationSessionId,
507
512
  collaborationConnected: (subjectId, rpc) => {
@@ -252,8 +252,8 @@ export async function createFederationRuntime(app, directory, handlers, options
252
252
  }
253
253
  }
254
254
  revokeTimer = setInterval(() => {
255
- for (const { socket: logical, sessionId, android } of sockets.values()) {
256
- if (android) {
255
+ for (const { socket: logical, sessionId, android, computer } of sockets.values()) {
256
+ if (android || computer) {
257
257
  if (!full(subjectId))
258
258
  logical.close(4003, 'Authorization revoked');
259
259
  continue;
@@ -569,18 +569,20 @@ export async function createFederationRuntime(app, directory, handlers, options
569
569
  const url = new URL(packet.path, 'http://inner');
570
570
  const match = /^\/api\/terminal\/([^/%]+)\/ws$/.exec(url.pathname);
571
571
  const androidMatch = /^\/api\/android\/([^/%]+)\/ws$/.exec(url.pathname);
572
+ const computer = url.pathname === '/api/computer/ws';
572
573
  if (match)
573
574
  check('session.view', match[1]);
574
575
  else if (url.pathname === '/api/control/ws')
575
576
  check('service.view');
576
- else if (androidMatch) {
577
+ else if (androidMatch || computer) {
577
578
  if (!full(subjectId))
578
579
  throw new Error('API_NOT_ALLOWED');
579
580
  }
580
581
  else
581
582
  throw new Error('API_NOT_ALLOWED');
582
- const logical = new LogicalSocket(channel, packet.id, () => androidMatch ? full(subjectId) : allowed(subjectId, match ? 'session.view' : 'service.view', match?.[1]));
583
- sockets.set(packet.id, { socket: logical, sessionId: match?.[1], android: Boolean(androidMatch) });
583
+ const logical = new LogicalSocket(channel, packet.id, () => (androidMatch || computer) ? full(subjectId) : allowed(subjectId, match ? 'session.view' : 'service.view', match?.[1]));
584
+ sockets.set(packet.id, { socket: logical, sessionId: match?.[1], android: Boolean(androidMatch), computer,
585
+ computerProtocol: computer ? url.searchParams.get('protocol') ?? 'vnc' : undefined });
584
586
  logical.once('close', () => sockets.delete(packet.id));
585
587
  send({ type: 'ws-ready', id: packet.id });
586
588
  if (match)
@@ -598,6 +600,15 @@ export async function createFederationRuntime(app, directory, handlers, options
598
600
  maxFps: Number(url.searchParams.get('max_fps')) || undefined,
599
601
  });
600
602
  }
603
+ else if (computer) {
604
+ if (!handlers.computer) {
605
+ logical.close(4403, 'API_NOT_ALLOWED');
606
+ continue;
607
+ }
608
+ handlers.computer(logical, url.searchParams.get('host') ?? '', {
609
+ protocol: url.searchParams.get('protocol') ?? undefined, port: url.searchParams.get('port') ?? undefined,
610
+ });
611
+ }
601
612
  else
602
613
  handlers.control(logical, randomUUID());
603
614
  }
@@ -606,7 +617,14 @@ export async function createFederationRuntime(app, directory, handlers, options
606
617
  if (!item || typeof packet.data !== 'string')
607
618
  throw new Error('INVALID_STREAM');
608
619
  const data = JSON.parse(packet.data);
609
- if (item.android) {
620
+ if (item.computer) {
621
+ if (!full(subjectId))
622
+ throw new Error('AUTHORIZATION_DENIED');
623
+ const types = item.computerProtocol === 'rdp' ? ['start', 'instruction', 'ack'] : ['data', 'ack'];
624
+ if (!types.includes(data?.type))
625
+ throw new Error('ACTION_NOT_ALLOWED');
626
+ }
627
+ else if (item.android) {
610
628
  if (!full(subjectId))
611
629
  throw new Error('AUTHORIZATION_DENIED');
612
630
  if (!['control', 'ack', 'ping', 'bitrate'].includes(data?.type))