termdock 1.4.300 → 1.4.301

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 (355) hide show
  1. package/dist/client/assets/App-BziVLy1b.js +179 -0
  2. package/dist/client/assets/App-BziVLy1b.js.br +0 -0
  3. package/dist/client/assets/App-BziVLy1b.js.gz +0 -0
  4. package/dist/client/assets/{ArchitecturePanel-BrfM7rGb.js → ArchitecturePanel-CARNBEqQ.js} +1 -1
  5. package/dist/client/assets/ArchitecturePanel-CARNBEqQ.js.br +0 -0
  6. package/dist/client/assets/ArchitecturePanel-CARNBEqQ.js.gz +0 -0
  7. package/dist/client/assets/{ChangeWalkthroughPanel-RoMt6giT.js → ChangeWalkthroughPanel-00oa0z6w.js} +1 -1
  8. package/dist/client/assets/ChangeWalkthroughPanel-00oa0z6w.js.br +0 -0
  9. package/dist/client/assets/ChangeWalkthroughPanel-00oa0z6w.js.gz +0 -0
  10. package/dist/client/assets/{CollaborationMainWorkspace-CK2BKKnn.js → CollaborationMainWorkspace-DuEeKQKZ.js} +1 -1
  11. package/dist/client/assets/CollaborationMainWorkspace-DuEeKQKZ.js.br +0 -0
  12. package/dist/client/assets/CollaborationMainWorkspace-DuEeKQKZ.js.gz +0 -0
  13. package/dist/client/assets/CollaborationTaskContent-B_mzpNuw.js +2 -0
  14. package/dist/client/assets/{ComputerControlView-AdjmvNUa.js → ComputerControlView-C9v1Ooc9.js} +2 -2
  15. package/dist/client/assets/ComputerControlView-C9v1Ooc9.js.br +0 -0
  16. package/dist/client/assets/ComputerControlView-C9v1Ooc9.js.gz +0 -0
  17. package/dist/client/assets/{DagPlayground-x8-mp6wC.js → DagPlayground-DfUpMALj.js} +1 -1
  18. package/dist/client/assets/DagPlayground-DfUpMALj.js.br +0 -0
  19. package/dist/client/assets/DagPlayground-DfUpMALj.js.gz +0 -0
  20. package/dist/client/assets/{DiffLab-B9KLZ--H.js → DiffLab-Cf_URhQt.js} +1 -1
  21. package/dist/client/assets/DiffLab-Cf_URhQt.js.br +0 -0
  22. package/dist/client/assets/DiffLab-Cf_URhQt.js.gz +0 -0
  23. package/dist/client/assets/{DiffReview-IEcDVREB.js → DiffReview-DjG9vCpH.js} +1 -1
  24. package/dist/client/assets/DiffReview-DjG9vCpH.js.br +0 -0
  25. package/dist/client/assets/DiffReview-DjG9vCpH.js.gz +0 -0
  26. package/dist/client/assets/{DiffReviewLab-lWra5pTJ.js → DiffReviewLab-BKOvHdJY.js} +1 -1
  27. package/dist/client/assets/DiffReviewLab-BKOvHdJY.js.br +0 -0
  28. package/dist/client/assets/DiffReviewLab-BKOvHdJY.js.gz +0 -0
  29. package/dist/client/assets/{DiffViewer-B2Rue9PA.js → DiffViewer-6n3tgsdc.js} +1 -1
  30. package/dist/client/assets/DiffViewer-6n3tgsdc.js.br +0 -0
  31. package/dist/client/assets/DiffViewer-6n3tgsdc.js.gz +0 -0
  32. package/dist/client/assets/{ModelPreview-NMQsfqAe.js → ModelPreview-BNs2sRct.js} +1 -1
  33. package/dist/client/assets/ModelPreview-BNs2sRct.js.br +0 -0
  34. package/dist/client/assets/ModelPreview-BNs2sRct.js.gz +0 -0
  35. package/dist/client/assets/{RightSidebar-BOBbegdP.js → RightSidebar-CcMSU1v8.js} +3 -3
  36. package/dist/client/assets/RightSidebar-CcMSU1v8.js.br +0 -0
  37. package/dist/client/assets/RightSidebar-CcMSU1v8.js.gz +0 -0
  38. package/dist/client/assets/{abnfDiagram-VCTEODGH-BIVuPQbA.js → abnfDiagram-VCTEODGH-DogjvRKp.js} +1 -1
  39. package/dist/client/assets/abnfDiagram-VCTEODGH-DogjvRKp.js.br +0 -0
  40. package/dist/client/assets/abnfDiagram-VCTEODGH-DogjvRKp.js.gz +0 -0
  41. package/dist/client/assets/{app-CHoOHnjf.js → app-D6vN8PW-.js} +3 -3
  42. package/dist/client/assets/app-D6vN8PW-.js.br +0 -0
  43. package/dist/client/assets/app-D6vN8PW-.js.gz +0 -0
  44. package/dist/client/assets/{arc-CNTT1p1A.js → arc-Cqel4Ted.js} +1 -1
  45. package/dist/client/assets/arc-Cqel4Ted.js.br +0 -0
  46. package/dist/client/assets/arc-Cqel4Ted.js.gz +0 -0
  47. package/dist/client/assets/{architectureDiagram-5GKGNRK7-Cl3gg8ZE.js → architectureDiagram-5GKGNRK7-DgvPtgNb.js} +1 -1
  48. package/dist/client/assets/architectureDiagram-5GKGNRK7-DgvPtgNb.js.br +0 -0
  49. package/dist/client/assets/architectureDiagram-5GKGNRK7-DgvPtgNb.js.gz +0 -0
  50. package/dist/client/assets/{blockDiagram-I7D4REHJ-Sxmfy_3_.js → blockDiagram-I7D4REHJ-BxbWO4x4.js} +1 -1
  51. package/dist/client/assets/blockDiagram-I7D4REHJ-BxbWO4x4.js.br +0 -0
  52. package/dist/client/assets/blockDiagram-I7D4REHJ-BxbWO4x4.js.gz +0 -0
  53. package/dist/client/assets/{c4Diagram-7LVT6UL2-DaTnrOn7.js → c4Diagram-7LVT6UL2-gsA37gjL.js} +1 -1
  54. package/dist/client/assets/c4Diagram-7LVT6UL2-gsA37gjL.js.br +0 -0
  55. package/dist/client/assets/c4Diagram-7LVT6UL2-gsA37gjL.js.gz +0 -0
  56. package/dist/client/assets/channel-CBc8-n7y.js +1 -0
  57. package/dist/client/assets/{chunk-2Q5K7J3B-BoiPA32P.js → chunk-2Q5K7J3B-ChC9yYOd.js} +1 -1
  58. package/dist/client/assets/{chunk-5VM5RSS4-G5G1YBLk.js → chunk-5VM5RSS4-Bi4Hc8yL.js} +1 -1
  59. package/dist/client/assets/{chunk-F27PBJKO-BNHv_EnL.js → chunk-F27PBJKO-DvVWvQEk.js} +1 -1
  60. package/dist/client/assets/{chunk-F27PBJKO-BNHv_EnL.js.br → chunk-F27PBJKO-DvVWvQEk.js.br} +0 -0
  61. package/dist/client/assets/chunk-F27PBJKO-DvVWvQEk.js.gz +0 -0
  62. package/dist/client/assets/{chunk-IMKFNOWR-CxgtyKdq.js → chunk-IMKFNOWR-BnwQLR5S.js} +1 -1
  63. package/dist/client/assets/chunk-IMKFNOWR-BnwQLR5S.js.br +0 -0
  64. package/dist/client/assets/chunk-IMKFNOWR-BnwQLR5S.js.gz +0 -0
  65. package/dist/client/assets/{chunk-JWPE2WC7-UO-0iK-G.js → chunk-JWPE2WC7-BkNnaDiR.js} +1 -1
  66. package/dist/client/assets/{chunk-POPQ4Y6H-Cox9z-e2.js → chunk-POPQ4Y6H-BRzm6Uzi.js} +1 -1
  67. package/dist/client/assets/{chunk-SVP7TREG-B2W8-Lxh.js → chunk-SVP7TREG-CDYm-dzk.js} +1 -1
  68. package/dist/client/assets/chunk-SVP7TREG-CDYm-dzk.js.br +0 -0
  69. package/dist/client/assets/{chunk-SVP7TREG-B2W8-Lxh.js.gz → chunk-SVP7TREG-CDYm-dzk.js.gz} +0 -0
  70. package/dist/client/assets/{chunk-TICWLB2K-BYDr9nkD.js → chunk-TICWLB2K-BKaW9Z9i.js} +1 -1
  71. package/dist/client/assets/chunk-TICWLB2K-BKaW9Z9i.js.br +0 -0
  72. package/dist/client/assets/chunk-TICWLB2K-BKaW9Z9i.js.gz +0 -0
  73. package/dist/client/assets/{chunk-XXDRQBXY-atArreox.js → chunk-XXDRQBXY-DcHZ_FHb.js} +1 -1
  74. package/dist/client/assets/classDiagram-ZZMXUADV-CNFc2dOc.js +1 -0
  75. package/dist/client/assets/classDiagram-v2-VYDZK3BY-CNFc2dOc.js +1 -0
  76. package/dist/client/assets/{cose-bilkent-JH36ORCC-CvfJj195.js → cose-bilkent-JH36ORCC-DCbbTNc6.js} +1 -1
  77. package/dist/client/assets/cose-bilkent-JH36ORCC-DCbbTNc6.js.br +0 -0
  78. package/dist/client/assets/cose-bilkent-JH36ORCC-DCbbTNc6.js.gz +0 -0
  79. package/dist/client/assets/{cynefin-OW5HDTMX-ByWE_fUn.js → cynefin-OW5HDTMX-i23hD0Z2.js} +1 -1
  80. package/dist/client/assets/{cynefin-OW5HDTMX-ByWE_fUn.js.br → cynefin-OW5HDTMX-i23hD0Z2.js.br} +0 -0
  81. package/dist/client/assets/cynefin-OW5HDTMX-i23hD0Z2.js.gz +0 -0
  82. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-BRT0d0MN.js → cynefinDiagram-5FMLGOSQ-CbyLYcqc.js} +1 -1
  83. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CbyLYcqc.js.br +0 -0
  84. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CbyLYcqc.js.gz +0 -0
  85. package/dist/client/assets/{dagre-GXQ25YYZ-Twtyv7ud.js → dagre-GXQ25YYZ-iJ4VKzZm.js} +1 -1
  86. package/dist/client/assets/dagre-GXQ25YYZ-iJ4VKzZm.js.br +0 -0
  87. package/dist/client/assets/dagre-GXQ25YYZ-iJ4VKzZm.js.gz +0 -0
  88. package/dist/client/assets/{diagram-S7CK7UJ4-Vl06hZtd.js → diagram-S7CK7UJ4-BNVZcdsO.js} +1 -1
  89. package/dist/client/assets/diagram-S7CK7UJ4-BNVZcdsO.js.br +0 -0
  90. package/dist/client/assets/diagram-S7CK7UJ4-BNVZcdsO.js.gz +0 -0
  91. package/dist/client/assets/{diagram-UQ7AKVKN-Cp39Mpse.js → diagram-UQ7AKVKN-1ghTvPiu.js} +1 -1
  92. package/dist/client/assets/diagram-UQ7AKVKN-1ghTvPiu.js.br +0 -0
  93. package/dist/client/assets/diagram-UQ7AKVKN-1ghTvPiu.js.gz +0 -0
  94. package/dist/client/assets/{diagram-VSXAHHWV-Drchji0N.js → diagram-VSXAHHWV-Ce_0nMd3.js} +1 -1
  95. package/dist/client/assets/diagram-VSXAHHWV-Ce_0nMd3.js.br +0 -0
  96. package/dist/client/assets/diagram-VSXAHHWV-Ce_0nMd3.js.gz +0 -0
  97. package/dist/client/assets/{diagram-VX7I27RA-rp8n_lct.js → diagram-VX7I27RA-CLHEkhUt.js} +1 -1
  98. package/dist/client/assets/diagram-VX7I27RA-CLHEkhUt.js.br +0 -0
  99. package/dist/client/assets/diagram-VX7I27RA-CLHEkhUt.js.gz +0 -0
  100. package/dist/client/assets/{diagram-Z3DM3KII-BEGDaMBm.js → diagram-Z3DM3KII-iyb5DkP6.js} +1 -1
  101. package/dist/client/assets/diagram-Z3DM3KII-iyb5DkP6.js.br +0 -0
  102. package/dist/client/assets/diagram-Z3DM3KII-iyb5DkP6.js.gz +0 -0
  103. package/dist/client/assets/{ebnfDiagram-PWID7BFC-kvgdD4A7.js → ebnfDiagram-PWID7BFC-Dxh2TzXm.js} +1 -1
  104. package/dist/client/assets/ebnfDiagram-PWID7BFC-Dxh2TzXm.js.br +0 -0
  105. package/dist/client/assets/ebnfDiagram-PWID7BFC-Dxh2TzXm.js.gz +0 -0
  106. package/dist/client/assets/{erDiagram-RLTQ6QDP-DcYHv9hK.js → erDiagram-RLTQ6QDP-Cr8IkdCC.js} +1 -1
  107. package/dist/client/assets/erDiagram-RLTQ6QDP-Cr8IkdCC.js.br +0 -0
  108. package/dist/client/assets/erDiagram-RLTQ6QDP-Cr8IkdCC.js.gz +0 -0
  109. package/dist/client/assets/{flowDiagram-HODETNUW-T9FQKn1F.js → flowDiagram-HODETNUW-B8ZF1bwr.js} +1 -1
  110. package/dist/client/assets/flowDiagram-HODETNUW-B8ZF1bwr.js.br +0 -0
  111. package/dist/client/assets/flowDiagram-HODETNUW-B8ZF1bwr.js.gz +0 -0
  112. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-BEB_X-y5.js → ganttDiagram-EL5Y4UJY-zhdL10eM.js} +1 -1
  113. package/dist/client/assets/ganttDiagram-EL5Y4UJY-zhdL10eM.js.br +0 -0
  114. package/dist/client/assets/ganttDiagram-EL5Y4UJY-zhdL10eM.js.gz +0 -0
  115. package/dist/client/assets/{gestureArbiter-Do0TIdkh.js → gestureArbiter-CXYHfU__.js} +1 -1
  116. package/dist/client/assets/gestureArbiter-CXYHfU__.js.br +0 -0
  117. package/dist/client/assets/gestureArbiter-CXYHfU__.js.gz +0 -0
  118. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js → gitGraphDiagram-WWUBYQGX-BQf2fHBo.js} +1 -1
  119. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BQf2fHBo.js.br +0 -0
  120. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BQf2fHBo.js.gz +0 -0
  121. package/dist/client/assets/{index-D2_-ypCL.css → index-CNY7CTVa.css} +1 -1
  122. package/dist/client/assets/index-CNY7CTVa.css.br +0 -0
  123. package/dist/client/assets/index-CNY7CTVa.css.gz +0 -0
  124. package/dist/client/assets/{infoDiagram-27XIBGKW-B6y0nIx8.js → infoDiagram-27XIBGKW-BT8xey7S.js} +1 -1
  125. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-ebGuykGn.js → ishikawaDiagram-5VMMS53U-DunPdwW9.js} +1 -1
  126. package/dist/client/assets/ishikawaDiagram-5VMMS53U-DunPdwW9.js.br +0 -0
  127. package/dist/client/assets/ishikawaDiagram-5VMMS53U-DunPdwW9.js.gz +0 -0
  128. package/dist/client/assets/{journeyDiagram-3NMN7TZE-DJDkcIq5.js → journeyDiagram-3NMN7TZE-B0y75iIg.js} +1 -1
  129. package/dist/client/assets/journeyDiagram-3NMN7TZE-B0y75iIg.js.br +0 -0
  130. package/dist/client/assets/journeyDiagram-3NMN7TZE-B0y75iIg.js.gz +0 -0
  131. package/dist/client/assets/{kanban-definition-UXKFOSKX-pTBbviRZ.js → kanban-definition-UXKFOSKX-BfgA5WLo.js} +1 -1
  132. package/dist/client/assets/kanban-definition-UXKFOSKX-BfgA5WLo.js.br +0 -0
  133. package/dist/client/assets/kanban-definition-UXKFOSKX-BfgA5WLo.js.gz +0 -0
  134. package/dist/client/assets/{layout-Dw9FB4Uc.js → layout-BvjABLff.js} +1 -1
  135. package/dist/client/assets/layout-BvjABLff.js.br +0 -0
  136. package/dist/client/assets/layout-BvjABLff.js.gz +0 -0
  137. package/dist/client/assets/{linear-CyLfVGgB.js → linear-DgeFRliF.js} +1 -1
  138. package/dist/client/assets/linear-DgeFRliF.js.br +0 -0
  139. package/dist/client/assets/linear-DgeFRliF.js.gz +0 -0
  140. package/dist/client/assets/{mermaid.core-xRkirK0l.js → mermaid.core-DM6Rlal9.js} +5 -5
  141. package/dist/client/assets/mermaid.core-DM6Rlal9.js.br +0 -0
  142. package/dist/client/assets/mermaid.core-DM6Rlal9.js.gz +0 -0
  143. package/dist/client/assets/{mindmap-definition-YA3MSWOX-BxvaDEGQ.js → mindmap-definition-YA3MSWOX-DhEiABv6.js} +1 -1
  144. package/dist/client/assets/mindmap-definition-YA3MSWOX-DhEiABv6.js.br +0 -0
  145. package/dist/client/assets/mindmap-definition-YA3MSWOX-DhEiABv6.js.gz +0 -0
  146. package/dist/client/assets/{passwordBootstrap-CaYbIQDC.js → passwordBootstrap-Dxv0I4wM.js} +1 -1
  147. package/dist/client/assets/passwordBootstrap-Dxv0I4wM.js.br +0 -0
  148. package/dist/client/assets/passwordBootstrap-Dxv0I4wM.js.gz +0 -0
  149. package/dist/client/assets/{pegDiagram-XKGWAZYB-Cxh-Bbay.js → pegDiagram-XKGWAZYB-LTlVaJfm.js} +1 -1
  150. package/dist/client/assets/pegDiagram-XKGWAZYB-LTlVaJfm.js.br +0 -0
  151. package/dist/client/assets/pegDiagram-XKGWAZYB-LTlVaJfm.js.gz +0 -0
  152. package/dist/client/assets/{pieDiagram-E7YTZNPT-CgOsk0Ai.js → pieDiagram-E7YTZNPT-9bnryna3.js} +1 -1
  153. package/dist/client/assets/pieDiagram-E7YTZNPT-9bnryna3.js.br +0 -0
  154. package/dist/client/assets/pieDiagram-E7YTZNPT-9bnryna3.js.gz +0 -0
  155. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-D1S_gKcT.js → quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js} +1 -1
  156. package/dist/client/assets/quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js.br +0 -0
  157. package/dist/client/assets/quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js.gz +0 -0
  158. package/dist/client/assets/{railroadDiagram-O6MQD6OU-BG5e87Np.js → railroadDiagram-O6MQD6OU-DTtXvKOu.js} +1 -1
  159. package/dist/client/assets/railroadDiagram-O6MQD6OU-DTtXvKOu.js.br +0 -0
  160. package/dist/client/assets/railroadDiagram-O6MQD6OU-DTtXvKOu.js.gz +0 -0
  161. package/dist/client/assets/{rdpSession-DL1rZ49m.js → rdpSession-Ggo3N5s5.js} +1 -1
  162. package/dist/client/assets/rdpSession-Ggo3N5s5.js.br +0 -0
  163. package/dist/client/assets/rdpSession-Ggo3N5s5.js.gz +0 -0
  164. package/dist/client/assets/{referenceLongPress-DB039WbW.js → referenceLongPress-Df8W2cyP.js} +1 -1
  165. package/dist/client/assets/referenceLongPress-Df8W2cyP.js.br +0 -0
  166. package/dist/client/assets/referenceLongPress-Df8W2cyP.js.gz +0 -0
  167. package/dist/client/assets/{requirementDiagram-BXWQKSXE-C37DsQfu.js → requirementDiagram-BXWQKSXE-BNia9zI0.js} +1 -1
  168. package/dist/client/assets/{requirementDiagram-BXWQKSXE-C37DsQfu.js.br → requirementDiagram-BXWQKSXE-BNia9zI0.js.br} +0 -0
  169. package/dist/client/assets/requirementDiagram-BXWQKSXE-BNia9zI0.js.gz +0 -0
  170. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-DLzhCYoj.js → sankeyDiagram-P5KCCOFB-uvg5npfK.js} +1 -1
  171. package/dist/client/assets/sankeyDiagram-P5KCCOFB-uvg5npfK.js.br +0 -0
  172. package/dist/client/assets/sankeyDiagram-P5KCCOFB-uvg5npfK.js.gz +0 -0
  173. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-DeVpSvB0.js → sequenceDiagram-WJ2MYXX4-DpJNf2MD.js} +1 -1
  174. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DpJNf2MD.js.br +0 -0
  175. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DpJNf2MD.js.gz +0 -0
  176. package/dist/client/assets/{sizeCapture-INFHLROL-CE03E66U.js → sizeCapture-INFHLROL-CkWB2AnF.js} +1 -1
  177. package/dist/client/assets/sizeCapture-INFHLROL-CkWB2AnF.js.br +0 -0
  178. package/dist/client/assets/sizeCapture-INFHLROL-CkWB2AnF.js.gz +0 -0
  179. package/dist/client/assets/{sparkles-ButxQzs4.js → sparkles-DOuEtn62.js} +1 -1
  180. package/dist/client/assets/{stateDiagram-D77RDMKH-DuqhHT3k.js → stateDiagram-D77RDMKH-CeMzMQq-.js} +1 -1
  181. package/dist/client/assets/stateDiagram-D77RDMKH-CeMzMQq-.js.br +0 -0
  182. package/dist/client/assets/stateDiagram-D77RDMKH-CeMzMQq-.js.gz +0 -0
  183. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CNYUpLxt.js +1 -0
  184. package/dist/client/assets/{swimlanes-42K2YHIH-DxkSdJ8g.js → swimlanes-42K2YHIH-Bo9GejWo.js} +1 -1
  185. package/dist/client/assets/swimlanes-42K2YHIH-Bo9GejWo.js.br +0 -0
  186. package/dist/client/assets/swimlanes-42K2YHIH-Bo9GejWo.js.gz +0 -0
  187. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-EB7zBRFR.js +8 -0
  188. package/dist/client/assets/{timeline-definition-24CTP7MA-DVpcJ6y5.js → timeline-definition-24CTP7MA-cvWXYEZ4.js} +1 -1
  189. package/dist/client/assets/timeline-definition-24CTP7MA-cvWXYEZ4.js.br +0 -0
  190. package/dist/client/assets/timeline-definition-24CTP7MA-cvWXYEZ4.js.gz +0 -0
  191. package/dist/client/assets/{triangle-alert-BRy562K_.js → triangle-alert-DzV0gQXz.js} +1 -1
  192. package/dist/client/assets/{vennDiagram-4TSXK5OY-BEKXLWGa.js → vennDiagram-4TSXK5OY-CzleWcmk.js} +1 -1
  193. package/dist/client/assets/vennDiagram-4TSXK5OY-CzleWcmk.js.br +0 -0
  194. package/dist/client/assets/vennDiagram-4TSXK5OY-CzleWcmk.js.gz +0 -0
  195. package/dist/client/assets/{videoCompression-CoVXZ8qw.js → videoCompression-DCnzjEfU.js} +1 -1
  196. package/dist/client/assets/videoCompression-DCnzjEfU.js.br +0 -0
  197. package/dist/client/assets/videoCompression-DCnzjEfU.js.gz +0 -0
  198. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-Cj30GTa9.js → wardleyDiagram-VM6X3IG4-Cir1iutR.js} +1 -1
  199. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cir1iutR.js.br +0 -0
  200. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cir1iutR.js.gz +0 -0
  201. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-Cm7NEdCl.js → xychartDiagram-S5SC5T6Z-pNPcVJ1C.js} +1 -1
  202. package/dist/client/assets/xychartDiagram-S5SC5T6Z-pNPcVJ1C.js.br +0 -0
  203. package/dist/client/assets/xychartDiagram-S5SC5T6Z-pNPcVJ1C.js.gz +0 -0
  204. package/dist/client/index.html +2 -2
  205. package/dist/client/index.html.br +0 -0
  206. package/dist/client/index.html.gz +0 -0
  207. package/dist/client/sw.js +1 -1
  208. package/dist/client/sw.js.br +0 -0
  209. package/dist/client/sw.js.gz +0 -0
  210. package/dist/server/agent/collaborationCli.js +11 -4
  211. package/dist/server/agent/collaborationGroupRoutes.js +10 -5
  212. package/dist/server/agent/collaborationLaunchProfiles.js +43 -0
  213. package/dist/server/agent/collaborationRoutes.js +2 -0
  214. package/dist/server/agent/collaborationService.js +8 -2
  215. package/dist/server/agent/collaborationStore.js +23 -1
  216. package/dist/server/agent/collaborationTaskService.js +2 -2
  217. package/dist/server/agent/collaborationTaskStore.js +4 -1
  218. package/dist/server/agent/collaborationTeam.js +3 -3
  219. package/dist/server/agent/integrationCli.js +16 -0
  220. package/dist/server/agent/integrationServer.js +6 -1
  221. package/dist/server/agent/integrationSessions.js +55 -7
  222. package/dist/server/agent/integrationStore.js +23 -0
  223. package/dist/server/routes/terminal.js +38 -9
  224. package/docs/integrations.md +69 -4
  225. package/package.json +1 -1
  226. package/runtime-manifest.json +3 -3
  227. package/dist/client/assets/App-7k2I_09f.js +0 -179
  228. package/dist/client/assets/App-7k2I_09f.js.br +0 -0
  229. package/dist/client/assets/App-7k2I_09f.js.gz +0 -0
  230. package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js.br +0 -0
  231. package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js.gz +0 -0
  232. package/dist/client/assets/ChangeWalkthroughPanel-RoMt6giT.js.br +0 -0
  233. package/dist/client/assets/ChangeWalkthroughPanel-RoMt6giT.js.gz +0 -0
  234. package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js.br +0 -0
  235. package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js.gz +0 -0
  236. package/dist/client/assets/CollaborationTaskContent-FlTbV9e1.js +0 -2
  237. package/dist/client/assets/ComputerControlView-AdjmvNUa.js.br +0 -0
  238. package/dist/client/assets/ComputerControlView-AdjmvNUa.js.gz +0 -0
  239. package/dist/client/assets/DagPlayground-x8-mp6wC.js.br +0 -0
  240. package/dist/client/assets/DagPlayground-x8-mp6wC.js.gz +0 -0
  241. package/dist/client/assets/DiffLab-B9KLZ--H.js.br +0 -0
  242. package/dist/client/assets/DiffLab-B9KLZ--H.js.gz +0 -0
  243. package/dist/client/assets/DiffReview-IEcDVREB.js.br +0 -0
  244. package/dist/client/assets/DiffReview-IEcDVREB.js.gz +0 -0
  245. package/dist/client/assets/DiffReviewLab-lWra5pTJ.js.br +0 -0
  246. package/dist/client/assets/DiffReviewLab-lWra5pTJ.js.gz +0 -0
  247. package/dist/client/assets/DiffViewer-B2Rue9PA.js.br +0 -0
  248. package/dist/client/assets/DiffViewer-B2Rue9PA.js.gz +0 -0
  249. package/dist/client/assets/ModelPreview-NMQsfqAe.js.br +0 -0
  250. package/dist/client/assets/ModelPreview-NMQsfqAe.js.gz +0 -0
  251. package/dist/client/assets/RightSidebar-BOBbegdP.js.br +0 -0
  252. package/dist/client/assets/RightSidebar-BOBbegdP.js.gz +0 -0
  253. package/dist/client/assets/abnfDiagram-VCTEODGH-BIVuPQbA.js.br +0 -0
  254. package/dist/client/assets/abnfDiagram-VCTEODGH-BIVuPQbA.js.gz +0 -0
  255. package/dist/client/assets/app-CHoOHnjf.js.br +0 -0
  256. package/dist/client/assets/app-CHoOHnjf.js.gz +0 -0
  257. package/dist/client/assets/arc-CNTT1p1A.js.br +0 -0
  258. package/dist/client/assets/arc-CNTT1p1A.js.gz +0 -0
  259. package/dist/client/assets/architectureDiagram-5GKGNRK7-Cl3gg8ZE.js.br +0 -0
  260. package/dist/client/assets/architectureDiagram-5GKGNRK7-Cl3gg8ZE.js.gz +0 -0
  261. package/dist/client/assets/blockDiagram-I7D4REHJ-Sxmfy_3_.js.br +0 -0
  262. package/dist/client/assets/blockDiagram-I7D4REHJ-Sxmfy_3_.js.gz +0 -0
  263. package/dist/client/assets/c4Diagram-7LVT6UL2-DaTnrOn7.js.br +0 -0
  264. package/dist/client/assets/c4Diagram-7LVT6UL2-DaTnrOn7.js.gz +0 -0
  265. package/dist/client/assets/channel-CEvBVvt3.js +0 -1
  266. package/dist/client/assets/chunk-F27PBJKO-BNHv_EnL.js.gz +0 -0
  267. package/dist/client/assets/chunk-IMKFNOWR-CxgtyKdq.js.br +0 -0
  268. package/dist/client/assets/chunk-IMKFNOWR-CxgtyKdq.js.gz +0 -0
  269. package/dist/client/assets/chunk-SVP7TREG-B2W8-Lxh.js.br +0 -0
  270. package/dist/client/assets/chunk-TICWLB2K-BYDr9nkD.js.br +0 -0
  271. package/dist/client/assets/chunk-TICWLB2K-BYDr9nkD.js.gz +0 -0
  272. package/dist/client/assets/classDiagram-ZZMXUADV-nSmGM9Fl.js +0 -1
  273. package/dist/client/assets/classDiagram-v2-VYDZK3BY-nSmGM9Fl.js +0 -1
  274. package/dist/client/assets/cose-bilkent-JH36ORCC-CvfJj195.js.br +0 -0
  275. package/dist/client/assets/cose-bilkent-JH36ORCC-CvfJj195.js.gz +0 -0
  276. package/dist/client/assets/cynefin-OW5HDTMX-ByWE_fUn.js.gz +0 -0
  277. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BRT0d0MN.js.br +0 -0
  278. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BRT0d0MN.js.gz +0 -0
  279. package/dist/client/assets/dagre-GXQ25YYZ-Twtyv7ud.js.br +0 -0
  280. package/dist/client/assets/dagre-GXQ25YYZ-Twtyv7ud.js.gz +0 -0
  281. package/dist/client/assets/diagram-S7CK7UJ4-Vl06hZtd.js.br +0 -0
  282. package/dist/client/assets/diagram-S7CK7UJ4-Vl06hZtd.js.gz +0 -0
  283. package/dist/client/assets/diagram-UQ7AKVKN-Cp39Mpse.js.br +0 -0
  284. package/dist/client/assets/diagram-UQ7AKVKN-Cp39Mpse.js.gz +0 -0
  285. package/dist/client/assets/diagram-VSXAHHWV-Drchji0N.js.br +0 -0
  286. package/dist/client/assets/diagram-VSXAHHWV-Drchji0N.js.gz +0 -0
  287. package/dist/client/assets/diagram-VX7I27RA-rp8n_lct.js.br +0 -0
  288. package/dist/client/assets/diagram-VX7I27RA-rp8n_lct.js.gz +0 -0
  289. package/dist/client/assets/diagram-Z3DM3KII-BEGDaMBm.js.br +0 -0
  290. package/dist/client/assets/diagram-Z3DM3KII-BEGDaMBm.js.gz +0 -0
  291. package/dist/client/assets/ebnfDiagram-PWID7BFC-kvgdD4A7.js.br +0 -0
  292. package/dist/client/assets/ebnfDiagram-PWID7BFC-kvgdD4A7.js.gz +0 -0
  293. package/dist/client/assets/erDiagram-RLTQ6QDP-DcYHv9hK.js.br +0 -0
  294. package/dist/client/assets/erDiagram-RLTQ6QDP-DcYHv9hK.js.gz +0 -0
  295. package/dist/client/assets/flowDiagram-HODETNUW-T9FQKn1F.js.br +0 -0
  296. package/dist/client/assets/flowDiagram-HODETNUW-T9FQKn1F.js.gz +0 -0
  297. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BEB_X-y5.js.br +0 -0
  298. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BEB_X-y5.js.gz +0 -0
  299. package/dist/client/assets/gestureArbiter-Do0TIdkh.js.br +0 -0
  300. package/dist/client/assets/gestureArbiter-Do0TIdkh.js.gz +0 -0
  301. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js.br +0 -0
  302. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js.gz +0 -0
  303. package/dist/client/assets/index-D2_-ypCL.css.br +0 -0
  304. package/dist/client/assets/index-D2_-ypCL.css.gz +0 -0
  305. package/dist/client/assets/ishikawaDiagram-5VMMS53U-ebGuykGn.js.br +0 -0
  306. package/dist/client/assets/ishikawaDiagram-5VMMS53U-ebGuykGn.js.gz +0 -0
  307. package/dist/client/assets/journeyDiagram-3NMN7TZE-DJDkcIq5.js.br +0 -0
  308. package/dist/client/assets/journeyDiagram-3NMN7TZE-DJDkcIq5.js.gz +0 -0
  309. package/dist/client/assets/kanban-definition-UXKFOSKX-pTBbviRZ.js.br +0 -0
  310. package/dist/client/assets/kanban-definition-UXKFOSKX-pTBbviRZ.js.gz +0 -0
  311. package/dist/client/assets/layout-Dw9FB4Uc.js.br +0 -0
  312. package/dist/client/assets/layout-Dw9FB4Uc.js.gz +0 -0
  313. package/dist/client/assets/linear-CyLfVGgB.js.br +0 -0
  314. package/dist/client/assets/linear-CyLfVGgB.js.gz +0 -0
  315. package/dist/client/assets/mermaid.core-xRkirK0l.js.br +0 -0
  316. package/dist/client/assets/mermaid.core-xRkirK0l.js.gz +0 -0
  317. package/dist/client/assets/mindmap-definition-YA3MSWOX-BxvaDEGQ.js.br +0 -0
  318. package/dist/client/assets/mindmap-definition-YA3MSWOX-BxvaDEGQ.js.gz +0 -0
  319. package/dist/client/assets/passwordBootstrap-CaYbIQDC.js.br +0 -0
  320. package/dist/client/assets/passwordBootstrap-CaYbIQDC.js.gz +0 -0
  321. package/dist/client/assets/pegDiagram-XKGWAZYB-Cxh-Bbay.js.br +0 -0
  322. package/dist/client/assets/pegDiagram-XKGWAZYB-Cxh-Bbay.js.gz +0 -0
  323. package/dist/client/assets/pieDiagram-E7YTZNPT-CgOsk0Ai.js.br +0 -0
  324. package/dist/client/assets/pieDiagram-E7YTZNPT-CgOsk0Ai.js.gz +0 -0
  325. package/dist/client/assets/quadrantDiagram-AXDQQJYC-D1S_gKcT.js.br +0 -0
  326. package/dist/client/assets/quadrantDiagram-AXDQQJYC-D1S_gKcT.js.gz +0 -0
  327. package/dist/client/assets/railroadDiagram-O6MQD6OU-BG5e87Np.js.br +0 -0
  328. package/dist/client/assets/railroadDiagram-O6MQD6OU-BG5e87Np.js.gz +0 -0
  329. package/dist/client/assets/rdpSession-DL1rZ49m.js.br +0 -0
  330. package/dist/client/assets/rdpSession-DL1rZ49m.js.gz +0 -0
  331. package/dist/client/assets/referenceLongPress-DB039WbW.js.br +0 -0
  332. package/dist/client/assets/referenceLongPress-DB039WbW.js.gz +0 -0
  333. package/dist/client/assets/requirementDiagram-BXWQKSXE-C37DsQfu.js.gz +0 -0
  334. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DLzhCYoj.js.br +0 -0
  335. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DLzhCYoj.js.gz +0 -0
  336. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DeVpSvB0.js.br +0 -0
  337. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DeVpSvB0.js.gz +0 -0
  338. package/dist/client/assets/sizeCapture-INFHLROL-CE03E66U.js.br +0 -0
  339. package/dist/client/assets/sizeCapture-INFHLROL-CE03E66U.js.gz +0 -0
  340. package/dist/client/assets/stateDiagram-D77RDMKH-DuqhHT3k.js.br +0 -0
  341. package/dist/client/assets/stateDiagram-D77RDMKH-DuqhHT3k.js.gz +0 -0
  342. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CxfzEwAH.js +0 -1
  343. package/dist/client/assets/swimlanes-42K2YHIH-DxkSdJ8g.js.br +0 -0
  344. package/dist/client/assets/swimlanes-42K2YHIH-DxkSdJ8g.js.gz +0 -0
  345. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-B_muKJLi.js +0 -8
  346. package/dist/client/assets/timeline-definition-24CTP7MA-DVpcJ6y5.js.br +0 -0
  347. package/dist/client/assets/timeline-definition-24CTP7MA-DVpcJ6y5.js.gz +0 -0
  348. package/dist/client/assets/vennDiagram-4TSXK5OY-BEKXLWGa.js.br +0 -0
  349. package/dist/client/assets/vennDiagram-4TSXK5OY-BEKXLWGa.js.gz +0 -0
  350. package/dist/client/assets/videoCompression-CoVXZ8qw.js.br +0 -0
  351. package/dist/client/assets/videoCompression-CoVXZ8qw.js.gz +0 -0
  352. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cj30GTa9.js.br +0 -0
  353. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cj30GTa9.js.gz +0 -0
  354. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cm7NEdCl.js.br +0 -0
  355. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cm7NEdCl.js.gz +0 -0
@@ -3,6 +3,7 @@ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
3
3
  import { dirname } from 'node:path';
4
4
  import { CollaborationError } from './collaborationProtocol.js';
5
5
  import { COLLAB_NAME_FORBIDDEN } from './collaborationPrompt.js';
6
+ import { validateLaunchProfiles } from './collaborationLaunchProfiles.js';
6
7
  import { remoteSession, validateCollaborationNode } from './collaborationPeerTransport.js';
7
8
  const hash = (value) => createHash('sha256').update(value).digest('hex');
8
9
  const address = (id) => { if (!id.startsWith('remote:'))
@@ -12,6 +13,7 @@ const local = (origin, id) => { const a = address(id); return a?.origin === orig
12
13
  const mapRoles = (value, map) => value && Object.fromEntries(Object.entries(value).map(([id, item]) => [map(id), item]));
13
14
  function mapGroup(group, map) {
14
15
  return { ...group, sessionIds: group.sessionIds.map(map), roles: mapRoles(group.roles, map), roleVersions: mapRoles(group.roleVersions, map),
16
+ memberLaunchProfiles: mapRoles(group.memberLaunchProfiles, map),
15
17
  instructions: group.instructions && { ...group.instructions, updatedBy: map(group.instructions.updatedBy) } };
16
18
  }
17
19
  /** Sole owner of peer discovery and group replication. UI and CLI call the same
@@ -460,10 +462,14 @@ export class CollaborationService {
460
462
  throw new CollaborationError('GROUP_CHANGED', `协作组已变化(当前 updatedAt=${existing.updatedAt}),请刷新后重试`, 409, { currentUpdatedAt: existing.updatedAt });
461
463
  if (typeof input.name !== 'string' || !input.name.trim() || COLLAB_NAME_FORBIDDEN.test(input.name) || !Array.isArray(input.sessionIds) || input.sessionIds.some(id => typeof id !== 'string'))
462
464
  throw new Error('INVALID_GROUP');
465
+ const launch = input.launchProfiles !== undefined || input.defaultLaunchProfileId !== undefined ? {
466
+ launchProfiles: validateLaunchProfiles(input.launchProfiles ?? existing?.launchProfiles ?? [], input.defaultLaunchProfileId ?? null),
467
+ defaultLaunchProfileId: input.defaultLaunchProfileId ?? null,
468
+ } : {};
463
469
  if (!input.sessionIds.some(id => address(id)) && !existing?.federated) {
464
470
  if (input.sessionIds.some(id => !this.options.sessions().some(s => s.sessionId === id) && !existing?.sessionIds.includes(id)))
465
471
  throw new Error('GROUP_MEMBER_UNAVAILABLE');
466
- return { group: this.options.store.save(input) };
472
+ return { group: this.options.store.save({ ...input, ...launch }) };
467
473
  }
468
474
  if (input.sessionIds.length < 2)
469
475
  throw new CollaborationError('INVALID_GROUP', '跨服务组仍需两个成员;修改未保存', 400);
@@ -477,7 +483,7 @@ export class CollaborationService {
477
483
  const available = new Set([...this.options.sessions(), ...this.directory().sessions.filter(s => s.serviceConnected)].map(s => s.sessionId));
478
484
  if (!Array.isArray(input.sessionIds) || input.sessionIds.some(id => !available.has(id) && !existing?.sessionIds.includes(id)))
479
485
  throw new Error('GROUP_MEMBER_UNAVAILABLE');
480
- const group = { ...existing, id: existing?.federated ? existing.id : `cross-${randomBytes(16).toString('hex')}`, name: input.name,
486
+ const group = { ...existing, ...launch, id: existing?.federated ? existing.id : `cross-${randomBytes(16).toString('hex')}`, name: input.name,
481
487
  sessionIds: [...new Set(input.sessionIds)], createdAt: existing?.createdAt ?? Date.now(), updatedAt: Math.max(Date.now(), (existing?.updatedAt ?? 0) + 1), federated: true };
482
488
  const canonical = mapGroup(group, id => address(id) ? id : remoteSession(this.node().origin, id));
483
489
  if (canonical.sessionIds.some(id => !this.nodes().some(node => node.origin === address(id)?.origin)))
@@ -1,4 +1,5 @@
1
1
  import { plainCollaborationSnapshot } from './collaborationText.js';
2
+ import { validateLaunchProfiles } from './collaborationLaunchProfiles.js';
2
3
  import fs from 'node:fs';
3
4
  import path from 'node:path';
4
5
  import crypto from 'node:crypto';
@@ -125,9 +126,14 @@ export class CollaborationStore {
125
126
  const existing = input.id ? this.document.groups.find((group) => group.id === input.id) : null;
126
127
  if (input.id && (!existing || existing.deleted))
127
128
  throw new CollaborationError('GROUP_NOT_FOUND', '协作组已删除,请刷新列表', 404);
129
+ const launchProfiles = input.launchProfiles === undefined ? existing?.launchProfiles : validateLaunchProfiles(input.launchProfiles, input.defaultLaunchProfileId ?? null);
130
+ const defaultLaunchProfileId = input.defaultLaunchProfileId !== undefined ? input.defaultLaunchProfileId : input.launchProfiles !== undefined ? null : existing?.defaultLaunchProfileId;
131
+ if (launchProfiles || defaultLaunchProfileId)
132
+ validateLaunchProfiles(launchProfiles ?? [], defaultLaunchProfileId);
128
133
  const sessionIds = Array.from(new Set(input.sessionIds.map((id) => id.trim()).filter(Boolean)));
129
134
  const group = pruneRoles({
130
135
  ...existing,
136
+ launchProfiles, defaultLaunchProfileId,
131
137
  ...(existing?.remoteSessions ? { remoteSessions: existing.remoteSessions.filter((session) => sessionIds.includes(session.sessionId)) } : {}),
132
138
  id: existing?.id ?? newCollaborationId(this.takenIds(), this.drawId),
133
139
  name: input.name.trim(),
@@ -141,6 +147,15 @@ export class CollaborationStore {
141
147
  this.persist();
142
148
  return group;
143
149
  }
150
+ setMemberLaunchProfile(groupId, sessionId, profile) {
151
+ const group = this.getGroup(groupId);
152
+ if (!group || group.deleted || !group.sessionIds.includes(sessionId))
153
+ throw new CollaborationError('NOT_A_MEMBER', '会话不在协作组内', 409);
154
+ const [snapshot] = validateLaunchProfiles([profile], profile.id);
155
+ this.document.groups = this.document.groups.map(g => g.id === groupId ? { ...g,
156
+ memberLaunchProfiles: { ...g.memberLaunchProfiles, [sessionId]: snapshot }, updatedAt: Math.max(Date.now(), g.updatedAt + 1) } : g);
157
+ this.persist();
158
+ }
144
159
  /** Set or clear a member's role (定位). The target may be any group member —
145
160
  * oneself, a peer, or a federated remote session; peers can set each other's
146
161
  * roles. Roles ride the group document into federation and are dropped
@@ -202,7 +217,9 @@ export class CollaborationStore {
202
217
  ...(group.remoteSessions ? { remoteSessions: group.remoteSessions.filter((session) => sourceIds.includes(session.sessionId)) } : {}),
203
218
  updatedAt: Math.max(now, group.updatedAt + 1) }, sourceIds)];
204
219
  }
205
- return group.id === target.id ? [{ ...group, sessionIds: [...new Set([...group.sessionIds, input.sessionId])], updatedAt: Math.max(now, group.updatedAt + 1) }] : [group];
220
+ return group.id === target.id ? [{ ...group, sessionIds: [...new Set([...group.sessionIds, input.sessionId])],
221
+ ...(source.memberLaunchProfiles?.[input.sessionId] ? { memberLaunchProfiles: { ...group.memberLaunchProfiles, [input.sessionId]: source.memberLaunchProfiles[input.sessionId] } } : {}),
222
+ updatedAt: Math.max(now, group.updatedAt + 1) }] : [group];
206
223
  }),
207
224
  messages: this.document.messages,
208
225
  };
@@ -748,6 +765,11 @@ function stableJson(value) {
748
765
  ? Object.fromEntries(Object.entries(entry).sort(([a], [b]) => a.localeCompare(b))) : entry);
749
766
  }
750
767
  function validateFederatedGroup(group) {
768
+ if (group.launchProfiles !== undefined || group.defaultLaunchProfileId !== undefined)
769
+ validateLaunchProfiles(group.launchProfiles ?? [], group.defaultLaunchProfileId);
770
+ if (group.memberLaunchProfiles)
771
+ for (const profile of Object.values(group.memberLaunchProfiles))
772
+ validateLaunchProfiles([profile], profile.id);
751
773
  if (!group.federated || typeof group.id !== 'string' || !group.id.startsWith('cross-') || typeof group.name !== 'string' || !group.name.trim()
752
774
  || !Number.isFinite(group.updatedAt) || !Number.isFinite(group.createdAt)
753
775
  || !Array.isArray(group.sessionIds) || group.sessionIds.some((id) => typeof id !== 'string')
@@ -54,9 +54,9 @@ export class CollaborationTaskService {
54
54
  const roster = summary ? undefined : group?.sessionIds.map(sessionId => {
55
55
  const member = this.peers.taskMember(sessionId);
56
56
  const template = task.workflow?.reviewers.some(m => taskMemberKey(m) === taskMemberKey(member));
57
- return { member, sessionId, role: `${template ? '此目标的执行成员模板;' : ''}${group.roles?.[sessionId] ?? ''}` };
57
+ return { member, sessionId, launchProfile: group.memberLaunchProfiles?.[sessionId], role: `${template ? '此目标的执行成员模板;' : ''}${group.roles?.[sessionId] ?? ''}` };
58
58
  });
59
- return { ...task, children, roster, memberSessions, replica: task.ownerServiceId !== this.self, summaryOnly: summary, outbox: (summary ? [] : this.tasks.pending(task.id))
59
+ return { ...task, children, roster, launchProfiles: summary ? undefined : group?.launchProfiles, defaultLaunchProfileId: summary ? undefined : group?.defaultLaunchProfileId, memberSessions, replica: task.ownerServiceId !== this.self, summaryOnly: summary, outbox: (summary ? [] : this.tasks.pending(task.id))
60
60
  .map(({ id, attemptId, messageId, lastError }) => ({ id, attemptId, messageId, lastError })) };
61
61
  }
62
62
  list(groupId, actor) {
@@ -224,6 +224,9 @@ export class CollaborationTaskStore {
224
224
  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。只按用户授权运行测试。' : ''}` });
225
225
  }
226
226
  goalInstructions(task) {
227
+ return `${this.baseGoalInstructions(task)}\n启动 Agent 前,读取 task get 返回的 launchProfiles、defaultLaunchProfileId 和 roster.launchProfile。根据用户填写的 notes 判断适用任务;这些是选用建议,不是能力或性能保证。需要另一种模型或参数时,用 td collab spawn ${task.groupId} <agentSlug> --launch-profile <方案id> --cwd <项目目录> --session ${task.coordinator?.sessionId} 创建对应执行成员,再把子任务分派给它。已有成员及其代码执行会话保留创建时的启动方案,不因默认方案变更而更换模型。`;
228
+ }
229
+ baseGoalInstructions(task) {
227
230
  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 '直接回答用户的简短结果和限制';只读目标直接汇总已核对结果,不创建集成或重复汇总任务。正文保留证据、评审结论和限制,仅代码目标补充目录、分支和提交,等待用户最终验收。任何异常都要留下明确说明,不伪造完成或进度。`;
228
231
  }
229
232
  notifyCoordinator(doc, task, update) {
@@ -442,7 +445,7 @@ export class CollaborationTaskStore {
442
445
  const event = this.event(task, 'report', actor, content, attempt.id);
443
446
  event.reportStatus = input.status;
444
447
  event.evidence = input.evidence;
445
- if (input.status === 'complete' && task.status === 'accepted') {
448
+ if (input.status === 'complete' && attempt.id === task.activeAttemptId && task.status === 'accepted') {
446
449
  task.status = 'open';
447
450
  delete task.acceptedArtifactId;
448
451
  delete task.completionMode;
@@ -6,10 +6,10 @@ export async function ensureTeam(options) {
6
6
  for (const role of labels) {
7
7
  const members = options.members();
8
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 和目录,或在成员设置中移出后重新配置');
9
+ if (reserved && (reserved.role !== role || reserved.cwd !== options.cwd || (reserved.launchProfileKey ?? '') !== (options.launchProfileKey ?? ''))) {
10
+ throw new Error('本组已有自动配置的成员,请使用原 Agent、启动方案和目录,或在成员设置中移出后重新配置');
11
11
  }
12
- const existing = reserved ?? members.find(member => !ids.includes(member.id) && member.agentSlug === options.agentSlug && member.cwd === options.cwd);
12
+ const existing = reserved ?? members.find(member => !ids.includes(member.id) && member.agentSlug === options.agentSlug && member.cwd === options.cwd && (member.launchProfileKey ?? '') === (options.launchProfileKey ?? ''));
13
13
  ids.push(existing?.id ?? await options.spawn(role));
14
14
  }
15
15
  return { coordinatorSessionId: ids[0], reviewerSessionIds: [ids[1]] };
@@ -9,6 +9,7 @@ import { getTermdockVersion } from '../utils/version.js';
9
9
  export const INTEGRATION_HELP = `td integration — local scoped integration administration
10
10
  groups | list
11
11
  create --file <policy.json> --credential-file <private-output.json>
12
+ update --file <policy.json> (same identity/group; preserves the credential)
12
13
  revoke <principal-id>
13
14
  Credentials are saved as a private file and never printed. Workers set
14
15
  TERMDOCK_INTEGRATION_CREDENTIAL_FILE, then use td collab --principal <id> …
@@ -70,10 +71,25 @@ export async function runIntegrationAdmin(argv, port, adminToken, write) {
70
71
  write(JSON.stringify(parsed(await request(socketPath, 'GET', action === 'groups' ? '/admin/groups' : '/admin/principals', undefined, headers(undefined, adminToken)))));
71
72
  return 0;
72
73
  }
74
+ if (action === 'update') {
75
+ if (!options['--file'] || Object.keys(options).length !== 1)
76
+ throw new Error('update requires only --file');
77
+ const policy = JSON.parse(fs.readFileSync(options['--file'], 'utf8'));
78
+ const capabilities = parsed(await request(socketPath, 'GET', '/admin/capabilities', undefined, headers(undefined, adminToken)));
79
+ if (capabilities.integration_policy_update !== true || policy.launchProfiles?.some((profile) => profile.startupInput !== undefined) && capabilities.startup_input_conditions !== true)
80
+ throw new CollaborationError('STARTUP_INPUT_CONDITION_UNSUPPORTED', 'Running service does not support this policy update', 409);
81
+ write(JSON.stringify(parsed(await request(socketPath, 'POST', `/admin/principals/${encodeURIComponent(policy.id)}/policy`, policy, headers(undefined, adminToken)))));
82
+ return 0;
83
+ }
73
84
  if (action === 'create') {
74
85
  if (!options['--file'] || !options['--credential-file'] || Object.keys(options).length !== 2)
75
86
  throw new Error('create requires --file and --credential-file');
76
87
  const policy = JSON.parse(fs.readFileSync(options['--file'], 'utf8'));
88
+ if (policy.launchProfiles?.some((profile) => profile.startupInput !== undefined)) {
89
+ const capabilities = parsed(await request(socketPath, 'GET', '/admin/capabilities', undefined, headers(undefined, adminToken)));
90
+ if (capabilities.startup_input_conditions !== true)
91
+ throw new CollaborationError('STARTUP_INPUT_CONDITION_UNSUPPORTED', 'Running service does not support startup input conditions', 409);
92
+ }
77
93
  const destination = path.resolve(options['--credential-file']);
78
94
  fs.mkdirSync(path.dirname(destination), { recursive: true, mode: 0o700 });
79
95
  fs.closeSync(fs.openSync(destination, 'wx', 0o600));
@@ -60,6 +60,7 @@ export class IntegrationRuntime {
60
60
  integrationError('INTEGRATION_ADMIN_REQUIRED', 'Local administration credential is required', 401);
61
61
  };
62
62
  app.get('/admin/groups', run(req => { admin(req); return { groups: options.messages.list().filter(g => !g.federated).map(g => ({ id: g.id, name: g.name })) }; }));
63
+ app.get('/admin/capabilities', run(req => { admin(req); return this.capabilities(); }));
63
64
  app.get('/admin/principals', run(req => { admin(req); return { integrations: this.store.policies() }; }));
64
65
  app.post('/admin/principals', run(req => { admin(req); this.group(req.body?.groupId); const provisioned = this.store.provision(req.body); try {
65
66
  this.sync();
@@ -67,8 +68,10 @@ export class IntegrationRuntime {
67
68
  catch {
68
69
  this.scheduleRetry();
69
70
  } return { id: provisioned.principal.id, token: provisioned.token, protocol: INTEGRATION_PROTOCOL }; }));
71
+ app.post('/admin/principals/:id/policy', run(req => { admin(req); if (req.body?.id !== req.params.id)
72
+ integrationError('INVALID_INTEGRATION_POLICY', 'Policy identity must match the requested identity'); this.group(req.body?.groupId); this.store.update(req.body); return { ok: true, id: req.params.id }; }));
70
73
  app.post('/admin/principals/:id/revoke', run(req => { admin(req); this.store.revoke(req.params.id); return { ok: true }; }));
71
- app.get('/capabilities', run(req => { const p = principal(req); return { ...this.capabilities(), principal: { id: p.id, group_id: p.groupId, permissions: p.permissions, launch_profiles: p.launchProfiles.map(profile => ({ id: profile.id, agent_slug: profile.agentSlug, cwd_roots: profile.cwdRoots })) } }; }));
74
+ app.get('/capabilities', run(req => { const p = principal(req); return { ...this.capabilities(), principal: { id: p.id, group_id: p.groupId, permissions: p.permissions, launch_profiles: p.launchProfiles.map(profile => ({ id: profile.id, agent_slug: profile.agentSlug, cwd_roots: profile.cwdRoots, startup_input_condition: !!profile.startupInput })) } }; }));
72
75
  app.get('/tasks', run(req => { const p = principal(req); this.authorize(p, 'task.read', req.query.group); return { tasks: options.tasks.list(p.groupId, null) }; }));
73
76
  app.get('/tasks/:id', run(async (req) => { const p = principal(req); this.taskPermission(p, req.params.id, 'task.read'); return { task: await options.tasks.get(req.params.id, null) }; }));
74
77
  app.post('/tasks', run(async (req) => {
@@ -140,6 +143,8 @@ export class IntegrationRuntime {
140
143
  integration_principal: true, group_scoped_permissions: true, durable_event_push: true,
141
144
  event_ack_semantics: 'durable_received', correlated_task_responses: true, accepted_task_comments: true,
142
145
  idempotent_session_create: true, exact_session_restore: true, external_validation: false,
146
+ startup_input_conditions: true,
147
+ integration_policy_update: true,
143
148
  permissions: ['task.read', 'task.create', 'task.assign', 'task.comment', 'task.revise', 'task.answer', 'events.read', 'session.create', 'session.read', 'session.restore'] };
144
149
  }
145
150
  error(error) {
@@ -10,8 +10,12 @@ export function integrationLaunchCommand(record, restore) {
10
10
  const quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
11
11
  return `cd -- ${quote(record.cwd)} && ${[record.profile.executable, ...args].map(quote).join(' ')}`;
12
12
  }
13
+ function startupInput(record, now) {
14
+ const condition = record.startup_condition ?? record.profile.startupInput;
15
+ return condition ? { state: 'pending', deadline: now + (condition.timeoutMs ?? 120000), matched_since: null, observed_at: null } : undefined;
16
+ }
13
17
  export function publicIntegrationSession(record) {
14
- const { profile: _, deadline: _deadline, launch_submitted: _submitted, ...publicRecord } = record;
18
+ const { profile: _, deadline: _deadline, launch_submitted: _submitted, startup_condition: _condition, ...publicRecord } = record;
15
19
  return publicRecord;
16
20
  }
17
21
  export function permittedCwd(profile, cwd) {
@@ -48,6 +52,7 @@ export class IntegrationSessions {
48
52
  restoring = new Set();
49
53
  timers = new Set();
50
54
  stopObservation;
55
+ stopDeliveryGuard;
51
56
  observing = false;
52
57
  observeAgain = false;
53
58
  constructor(file, adapter, bindingTimeout = 120000) {
@@ -65,14 +70,21 @@ export class IntegrationSessions {
65
70
  throw error;
66
71
  }
67
72
  for (const record of this.doc.sessions)
68
- if (['starting', 'restoring', 'binding_pending'].includes(record.state))
73
+ if (['starting', 'restoring', 'binding_pending'].includes(record.state) || record.startup_input?.state === 'pending')
69
74
  this.scheduleRefresh(record);
75
+ // A pre-restart partial match cannot prove continuity of the live screen.
76
+ for (const record of this.doc.sessions)
77
+ if (record.startup_input && record.startup_input.state !== 'observed')
78
+ record.startup_input.matched_since = null;
79
+ this.stopDeliveryGuard = adapter.registerDeliveryGuard?.(id => this.deliveryReadiness(id));
70
80
  this.stopObservation = adapter.subscribe(() => { void this.refreshAll().catch(() => { }); });
71
81
  }
72
82
  scheduleRefresh(record) {
73
- const timer = setTimeout(() => { this.timers.delete(timer); void this.refresh(record.session_id).catch(() => { }); }, Math.max(1, record.deadline - Date.now() + 5));
74
- timer.unref();
75
- this.timers.add(timer);
83
+ for (const deadline of new Set([record.deadline, ...(record.startup_input ? [record.startup_input.deadline] : [])])) {
84
+ const timer = setTimeout(() => { this.timers.delete(timer); void this.refresh(record.session_id).catch(() => { }); }, Math.max(1, deadline - Date.now() + 5));
85
+ timer.unref();
86
+ this.timers.add(timer);
87
+ }
76
88
  }
77
89
  subscribe(listener) { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; }
78
90
  snapshot() { return structuredClone(this.doc.sessions); }
@@ -128,6 +140,7 @@ export class IntegrationSessions {
128
140
  group_id: principal.groupId, principal_id: principal.id, agent_slug: profile.agentSlug, agent_native_session_id: null,
129
141
  state: 'starting', error_code: null, cwd, launch_profile: profile.id, profile: structuredClone(profile),
130
142
  created_at: now, updated_at: now, deadline: now + this.bindingTimeout, launch_submitted: false };
143
+ record.startup_input = startupInput(record, now);
131
144
  this.doc.requests[request.scoped] = { hash: request.hash, sessionId: record.session_id };
132
145
  try {
133
146
  this.saveRecord(record);
@@ -202,6 +215,9 @@ export class IntegrationSessions {
202
215
  record.operation_id = randomUUID();
203
216
  record.deadline = Date.now() + this.bindingTimeout;
204
217
  record.updated_at = Date.now();
218
+ if (!record.profile.startupInput && !record.startup_condition)
219
+ record.startup_condition = structuredClone(principal.launchProfiles.find(profile => profile.id === record.launch_profile)?.startupInput);
220
+ record.startup_input = startupInput(record, Date.now());
205
221
  this.doc.requests[request.scoped] = { hash: request.hash, sessionId: id };
206
222
  try {
207
223
  this.saveRecord(record);
@@ -267,13 +283,45 @@ export class IntegrationSessions {
267
283
  next.state = 'failed';
268
284
  next.error_code = 'SESSION_BINDING_TIMEOUT';
269
285
  }
286
+ if (next.startup_input && next.startup_input.state !== 'observed') {
287
+ const condition = next.startup_condition ?? next.profile.startupInput;
288
+ let matched = false;
289
+ if (observed.running && observed.agentSlug === record.agent_slug && (!next.error_code || next.error_code === 'SESSION_BINDING_TIMEOUT') && this.adapter.capture) {
290
+ try {
291
+ const fold = (text) => text.replace(/[^\S\r\n]+/g, ' ').trim();
292
+ const viewport = fold(await this.adapter.capture(next));
293
+ matched = condition.allOf.every(term => viewport.includes(fold(term))) && !(condition.noneOf ?? []).some(term => viewport.includes(fold(term)));
294
+ }
295
+ catch { /* Missing capture never opens the delivery guard. */ }
296
+ }
297
+ const input = { ...next.startup_input };
298
+ input.matched_since = matched ? input.matched_since ?? Date.now() : null;
299
+ if (matched && Date.now() - input.matched_since >= (condition.stableMs ?? 1000)) {
300
+ input.state = 'observed';
301
+ input.observed_at = Date.now();
302
+ }
303
+ else
304
+ input.state = Date.now() >= input.deadline ? 'timed_out' : 'pending';
305
+ next.startup_input = input;
306
+ }
270
307
  if (JSON.stringify(this.doc.sessions.find(s => s.session_id === id)) !== JSON.stringify(record))
271
308
  return;
272
- if (next.state !== record.state || next.error_code !== record.error_code || next.agent_native_session_id !== record.agent_native_session_id) {
309
+ if (JSON.stringify(next) !== JSON.stringify(record)) {
273
310
  next.updated_at = Date.now();
274
311
  this.saveRecord(next);
275
312
  }
276
313
  }
314
+ /** Only gates configured launches. Native UUID and Agent turn state are not input prerequisites. */
315
+ async deliveryReadiness(id) {
316
+ if (!this.doc.sessions.find(record => record.session_id === id)?.startup_input)
317
+ return null;
318
+ await this.refresh(id);
319
+ const record = this.doc.sessions.find(record => record.session_id === id);
320
+ if (record.error_code && record.error_code !== 'SESSION_BINDING_TIMEOUT')
321
+ return { allowed: false, reason: record.error_code };
322
+ return record.startup_input.state === 'observed' ? { allowed: true, reason: null }
323
+ : { allowed: false, reason: record.startup_input.state === 'timed_out' ? 'SESSION_STARTUP_INPUT_TIMEOUT' : 'SESSION_STARTUP_INPUT_PENDING' };
324
+ }
277
325
  async refreshAll() {
278
326
  if (this.observing) {
279
327
  this.observeAgain = true;
@@ -291,6 +339,6 @@ export class IntegrationSessions {
291
339
  this.observing = false;
292
340
  }
293
341
  }
294
- close() { this.stopObservation(); for (const timer of this.timers)
342
+ close() { this.stopObservation(); this.stopDeliveryGuard?.(); for (const timer of this.timers)
295
343
  clearTimeout(timer); }
296
344
  }
@@ -54,6 +54,16 @@ export function validatePolicy(value) {
54
54
  integrationError('INVALID_INTEGRATION_POLICY', 'resumeArgv requires one {sessionId} and one {launchArgs}; --last is forbidden');
55
55
  if (!Array.isArray(profile.cwdRoots) || !profile.cwdRoots.length || profile.cwdRoots.some(root => typeof root !== 'string' || !path.isAbsolute(root) || /[\x00-\x1f\x7f]/.test(root)))
56
56
  integrationError('INVALID_INTEGRATION_POLICY', 'cwdRoots must be absolute paths');
57
+ if (profile.startupInput !== undefined) {
58
+ const condition = profile.startupInput;
59
+ const validTerms = (terms) => Array.isArray(terms) && terms.length <= 16
60
+ && terms.every(term => typeof term === 'string' && !!term.trim() && term.length <= 256 && !/[\x00-\x1f\x7f]/.test(term));
61
+ if (!condition || !validTerms(condition.allOf) || !condition.allOf.length || condition.noneOf !== undefined && !validTerms(condition.noneOf)
62
+ || condition.stableMs !== undefined && (!Number.isInteger(condition.stableMs) || condition.stableMs < 500 || condition.stableMs > 5000)
63
+ || condition.timeoutMs !== undefined && (!Number.isInteger(condition.timeoutMs) || condition.timeoutMs < 1000 || condition.timeoutMs > 120000)) {
64
+ integrationError('INVALID_INTEGRATION_POLICY', 'Invalid startupInput literal conditions or timing bounds');
65
+ }
66
+ }
57
67
  }
58
68
  // Policies contain launcher configuration, never authentication material.
59
69
  return structuredClone({ id: value.id, groupId: value.groupId, permissions: value.permissions, launchProfiles: value.launchProfiles });
@@ -101,6 +111,19 @@ export class IntegrationStore {
101
111
  this.write(next);
102
112
  return { principal: checked, token };
103
113
  }
114
+ update(policy) {
115
+ const checked = validatePolicy(policy), next = structuredClone(this.doc);
116
+ const index = next.principals.findIndex(principal => principal.id === checked.id);
117
+ if (index < 0)
118
+ integrationError('INTEGRATION_NOT_FOUND', 'Integration does not exist', 404);
119
+ const existing = next.principals[index];
120
+ if (existing.revoked)
121
+ integrationError('INTEGRATION_REVOKED', 'Revoked identities cannot be reactivated', 403);
122
+ if (existing.groupId !== checked.groupId)
123
+ integrationError('INTEGRATION_GROUP_IMMUTABLE', 'An existing identity cannot change its group', 409);
124
+ next.principals[index] = { ...checked, tokenHash: existing.tokenHash, revoked: false };
125
+ this.write(next);
126
+ }
104
127
  revoke(id) {
105
128
  const next = structuredClone(this.doc), principal = next.principals.find(p => p.id === id);
106
129
  if (!principal)
@@ -54,6 +54,7 @@ import { CollaborationStore } from '../agent/collaborationStore.js';
54
54
  import { COLLAB_NAME_FORBIDDEN, formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
55
55
  import { ambiguousIdMessage } from '../agent/collaborationProtocol.js';
56
56
  import { buildCollaborationSpawnCommand, resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
57
+ import { resolveCollaborationLaunch, launchProfileKey } from '../agent/collaborationLaunchProfiles.js';
57
58
  import { SessionSearchStore } from '../agent/sessionSearchStore.js';
58
59
  import { NativeSessionSearch } from '../agent/nativeSessionSearch.js';
59
60
  import { CollaborationError } from '../agent/collaborationProtocol.js';
@@ -439,6 +440,7 @@ async function flushClientStateBroadcast() {
439
440
  }
440
441
  }
441
442
  const integrationSessionObservers = new Set();
443
+ let integrationDeliveryGuard;
442
444
  function notifyIntegrationSessions() { for (const listener of integrationSessionObservers) {
443
445
  try {
444
446
  listener();
@@ -1658,6 +1660,11 @@ async function resolveCollaborationRoute(frontendSessionId) {
1658
1660
  let record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
1659
1661
  if (!record)
1660
1662
  return { state: 'offline', reason: 'SESSION_REMOVED' };
1663
+ if (record.tmuxSessionName?.startsWith('wt-integration-') && !integrationDeliveryGuard)
1664
+ return { state: 'unavailable', reason: 'INTEGRATION_RUNTIME_UNAVAILABLE' };
1665
+ const startup = await integrationDeliveryGuard?.(frontendSessionId);
1666
+ if (startup && !startup.allowed)
1667
+ return { state: 'recovering', reason: startup.reason ?? 'SESSION_STARTUP_INPUT_PENDING' };
1661
1668
  let backend = resolveOrchestrationBackend(record);
1662
1669
  let binding = collaborationRouting.get(frontendSessionId) ?? {
1663
1670
  sessionId: record.sessionId, backendSessionId: null, mode: record.mode, tmuxSessionName: record.tmuxSessionName,
@@ -1745,7 +1752,7 @@ function deliverCollaborationInboxWhenAgentReady(frontendSessionId) {
1745
1752
  collaborationDeliveryWorker.wake(frontendSessionId);
1746
1753
  }
1747
1754
  async function spawnCollaborationAgentSession(req, group, sourceSessionId, input, bootstrapRole) {
1748
- const agentSlug = typeof input.agentSlug === 'string' ? input.agentSlug.trim().toLowerCase() : '';
1755
+ const { agentSlug, profile } = resolveCollaborationLaunch(group, input);
1749
1756
  const launchers = await listDetectedAgentLaunchers();
1750
1757
  const launcher = launchers.find((candidate) => candidate.slug === agentSlug);
1751
1758
  if (!launcher)
@@ -1767,7 +1774,7 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
1767
1774
  }
1768
1775
  const requestedCwd = typeof input.cwd === 'string' ? input.cwd.trim() : '';
1769
1776
  const opened = await openInventorySession(req, {
1770
- name: requestedName || `${launcher.displayName} · ${group.name}`,
1777
+ name: requestedName || `${profile?.name ?? launcher.displayName} · ${group.name}`,
1771
1778
  customName: true,
1772
1779
  mode,
1773
1780
  cwd: requestedCwd || fallbackRecord?.cwd || undefined,
@@ -1796,7 +1803,9 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
1796
1803
  });
1797
1804
  if (bootstrapRole)
1798
1805
  collaborationStore.setRole({ groupId: group.id, sessionId: frontendSessionId, role: bootstrapRole });
1799
- writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: launcher.command })}\r`);
1806
+ if (profile)
1807
+ collaborationStore.setMemberLaunchProfile(group.id, frontendSessionId, profile);
1808
+ writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: profile?.command ?? launcher.command })}\r`);
1800
1809
  const requestedTask = typeof input.task === 'string' ? input.task.trim().slice(0, 20_000) : '';
1801
1810
  collaborationStore.send({
1802
1811
  groupId: updatedGroup.id,
@@ -1808,7 +1817,7 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
1808
1817
  setTimeout(() => {
1809
1818
  deliverCollaborationInboxWhenAgentReady(frontendSessionId);
1810
1819
  }, 300).unref?.();
1811
- return { group: updatedGroup, session: orchestrationSessionSnapshot(globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId)) };
1820
+ return { group: collaborationStore.getGroup(group.id), session: orchestrationSessionSnapshot(globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId)) };
1812
1821
  }
1813
1822
  /** Restricted session adapter: only preconfigured argv and the exact owned
1814
1823
  * terminal can be launched; no remote drive or arbitrary shell commands. */
@@ -1836,6 +1845,16 @@ function integrationTerminalRecord(record) {
1836
1845
  }
1837
1846
  export const integrationSessionAdapter = {
1838
1847
  subscribe(listener) { integrationSessionObservers.add(listener); return () => { integrationSessionObservers.delete(listener); }; },
1848
+ registerDeliveryGuard(guard) { integrationDeliveryGuard = guard; return () => { if (integrationDeliveryGuard === guard)
1849
+ integrationDeliveryGuard = undefined; }; },
1850
+ async capture(record) {
1851
+ const observed = await integrationSessionAdapter.inspect(record);
1852
+ if (!observed.exists || !observed.running || observed.agentSlug !== record.agent_slug || !record.terminal_binding
1853
+ || record.agent_native_session_id && observed.nativeId && record.agent_native_session_id !== observed.nativeId) {
1854
+ integrationError('SESSION_IDENTITY_MISMATCH', 'Startup input observation requires the original Agent pane', 409);
1855
+ }
1856
+ return captureTmuxPaneText(runTmux, record.terminal_binding);
1857
+ },
1839
1858
  async inspect(record) {
1840
1859
  const terminal = integrationTerminalRecord(record);
1841
1860
  if (!terminal)
@@ -1959,10 +1978,16 @@ export function prepareCollaborationTaskWorker(task, template, dependencies, ser
1959
1978
  if (!source || template.serviceId !== service.descriptor().serviceId)
1960
1979
  throw new Error('执行成员当前不可用,请协调者重新分派');
1961
1980
  const snapshot = orchestrationSessionSnapshot(source);
1981
+ const inheritedProfile = collaborationStore.getGroup(task.groupId)?.memberLaunchProfiles?.[template.sessionId];
1962
1982
  const slug = snapshot.agent?.slug ?? source.agentResume?.slug;
1963
1983
  const launcher = (await listDetectedAgentLaunchers()).find(a => a.slug === slug);
1964
1984
  if (!launcher || !source.cwd)
1965
1985
  throw new Error('独立代码任务需要有项目目录的 Agent 成员,请选择可用成员');
1986
+ if (inheritedProfile) {
1987
+ if (inheritedProfile.agentSlug !== launcher.slug)
1988
+ throw new Error('执行成员的启动方案与当前 Agent 不一致,请重新分派');
1989
+ resolveCollaborationLaunch({ launchProfiles: [inheritedProfile] }, { launchProfileId: inheritedProfile.id, agentSlug: launcher.slug });
1990
+ }
1966
1991
  const workspace = await prepareTaskWorkspace(task, source.cwd, dependencies, (dependency, repository) => importTaskDependency(task, dependency, repository, service, readDependency));
1967
1992
  await pathValidator.allowSessionCwd(workspace.cwd);
1968
1993
  const existing = globalSessionState.sessions.find(s => s.cwd === workspace.cwd);
@@ -1988,7 +2013,7 @@ export function prepareCollaborationTaskWorker(task, template, dependencies, ser
1988
2013
  const temporary = `${launchFile}.${process.pid}.tmp`;
1989
2014
  await fs.promises.writeFile(temporary, JSON.stringify({ sessionId: record.sessionId, backendId: opened.terminalSession.sessionId }), { mode: 0o600 });
1990
2015
  await fs.promises.rename(temporary, launchFile);
1991
- writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: launcher.command })}\r`);
2016
+ writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: inheritedProfile?.command ?? launcher.command })}\r`);
1992
2017
  }
1993
2018
  const group = collaborationStore.getGroup(task.groupId);
1994
2019
  if (!group || group.deleted)
@@ -1996,6 +2021,8 @@ export function prepareCollaborationTaskWorker(task, template, dependencies, ser
1996
2021
  if (!group.sessionIds.includes(record.sessionId))
1997
2022
  await service.save({ id: group.id, name: group.name,
1998
2023
  sessionIds: [...group.sessionIds, record.sessionId], expectedUpdatedAt: group.updatedAt });
2024
+ if (inheritedProfile)
2025
+ collaborationStore.setMemberLaunchProfile(group.id, record.sessionId, inheritedProfile);
1999
2026
  return { sessionId: record.sessionId, workspace };
2000
2027
  })().finally(() => preparingTaskWorkers.delete(key));
2001
2028
  preparingTaskWorkers.set(key, operation);
@@ -5956,26 +5983,28 @@ router.post('/operations/collaboration-groups/:groupId/team', async (req, res) =
5956
5983
  return res.status(404).json({ error: '协作组不存在' });
5957
5984
  if (group.federated)
5958
5985
  throw new Error('跨服务组请先从成员设置添加 Agent');
5959
- const agentSlug = typeof req.body?.agentSlug === 'string' ? req.body.agentSlug : '';
5986
+ const { agentSlug, profile } = resolveCollaborationLaunch(group, req.body ?? {});
5960
5987
  if (!(await listDetectedAgentLaunchers()).some(agent => agent.slug === agentSlug))
5961
5988
  throw new Error('所选 Agent 当前不可用');
5962
5989
  if (typeof req.body?.cwd !== 'string' || !req.body.cwd.trim())
5963
5990
  throw new Error('请选择工作目录');
5964
5991
  const cwd = await resolveWorkingDirectory(req, req.body.cwd);
5965
5992
  const previous = preparingTeams.get(group.id) ?? Promise.resolve();
5966
- const operation = previous.catch(() => { }).then(() => ensureTeam({ agentSlug, cwd,
5993
+ const operation = previous.catch(() => { }).then(() => ensureTeam({ agentSlug, cwd, launchProfileKey: launchProfileKey(profile),
5967
5994
  members: () => {
5968
5995
  const current = collaborationStore.getGroup(group.id);
5969
5996
  if (!current || current.deleted)
5970
5997
  throw new Error('协作组已删除');
5998
+ if (profile && launchProfileKey(current.launchProfiles?.find(p => p.id === profile.id)) !== launchProfileKey(profile))
5999
+ throw new Error('启动方案已变更,请重新开始协作');
5971
6000
  return globalSessionState.sessions.filter(record => current.sessionIds.includes(record.sessionId))
5972
- .map(record => ({ id: record.sessionId, cwd: record.cwd, role: current.roles?.[record.sessionId], agentSlug: orchestrationSessionSnapshot(record).agent?.slug }));
6001
+ .map(record => ({ id: record.sessionId, cwd: record.cwd, role: current.roles?.[record.sessionId], agentSlug: orchestrationSessionSnapshot(record).agent?.slug, launchProfileKey: launchProfileKey(current.memberLaunchProfiles?.[record.sessionId]) }));
5973
6002
  },
5974
6003
  spawn: async (role) => {
5975
6004
  const current = collaborationStore.getGroup(group.id);
5976
6005
  if (!current || current.deleted)
5977
6006
  throw new Error('协作组已删除');
5978
- const result = await spawnCollaborationAgentSession(req, current, null, { agentSlug, cwd, mode: 'tmux', name: role.startsWith('自动协调者') ? `协调 ${group.name}` : `执行与评审 ${group.name}` }, role);
6007
+ const result = await spawnCollaborationAgentSession(req, current, null, { agentSlug, launchProfileId: profile?.id ?? '', cwd, mode: 'tmux', name: role.startsWith('自动协调者') ? `协调 ${group.name}` : `执行与评审 ${group.name}` }, role);
5979
6008
  return result.session.sessionId;
5980
6009
  },
5981
6010
  }));