termdock 1.4.300 → 1.4.302

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 (356) 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/agent/registry.js +31 -1
  224. package/dist/server/routes/terminal.js +43 -12
  225. package/docs/integrations.md +84 -4
  226. package/package.json +1 -1
  227. package/runtime-manifest.json +3 -3
  228. package/dist/client/assets/App-7k2I_09f.js +0 -179
  229. package/dist/client/assets/App-7k2I_09f.js.br +0 -0
  230. package/dist/client/assets/App-7k2I_09f.js.gz +0 -0
  231. package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js.br +0 -0
  232. package/dist/client/assets/ArchitecturePanel-BrfM7rGb.js.gz +0 -0
  233. package/dist/client/assets/ChangeWalkthroughPanel-RoMt6giT.js.br +0 -0
  234. package/dist/client/assets/ChangeWalkthroughPanel-RoMt6giT.js.gz +0 -0
  235. package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js.br +0 -0
  236. package/dist/client/assets/CollaborationMainWorkspace-CK2BKKnn.js.gz +0 -0
  237. package/dist/client/assets/CollaborationTaskContent-FlTbV9e1.js +0 -2
  238. package/dist/client/assets/ComputerControlView-AdjmvNUa.js.br +0 -0
  239. package/dist/client/assets/ComputerControlView-AdjmvNUa.js.gz +0 -0
  240. package/dist/client/assets/DagPlayground-x8-mp6wC.js.br +0 -0
  241. package/dist/client/assets/DagPlayground-x8-mp6wC.js.gz +0 -0
  242. package/dist/client/assets/DiffLab-B9KLZ--H.js.br +0 -0
  243. package/dist/client/assets/DiffLab-B9KLZ--H.js.gz +0 -0
  244. package/dist/client/assets/DiffReview-IEcDVREB.js.br +0 -0
  245. package/dist/client/assets/DiffReview-IEcDVREB.js.gz +0 -0
  246. package/dist/client/assets/DiffReviewLab-lWra5pTJ.js.br +0 -0
  247. package/dist/client/assets/DiffReviewLab-lWra5pTJ.js.gz +0 -0
  248. package/dist/client/assets/DiffViewer-B2Rue9PA.js.br +0 -0
  249. package/dist/client/assets/DiffViewer-B2Rue9PA.js.gz +0 -0
  250. package/dist/client/assets/ModelPreview-NMQsfqAe.js.br +0 -0
  251. package/dist/client/assets/ModelPreview-NMQsfqAe.js.gz +0 -0
  252. package/dist/client/assets/RightSidebar-BOBbegdP.js.br +0 -0
  253. package/dist/client/assets/RightSidebar-BOBbegdP.js.gz +0 -0
  254. package/dist/client/assets/abnfDiagram-VCTEODGH-BIVuPQbA.js.br +0 -0
  255. package/dist/client/assets/abnfDiagram-VCTEODGH-BIVuPQbA.js.gz +0 -0
  256. package/dist/client/assets/app-CHoOHnjf.js.br +0 -0
  257. package/dist/client/assets/app-CHoOHnjf.js.gz +0 -0
  258. package/dist/client/assets/arc-CNTT1p1A.js.br +0 -0
  259. package/dist/client/assets/arc-CNTT1p1A.js.gz +0 -0
  260. package/dist/client/assets/architectureDiagram-5GKGNRK7-Cl3gg8ZE.js.br +0 -0
  261. package/dist/client/assets/architectureDiagram-5GKGNRK7-Cl3gg8ZE.js.gz +0 -0
  262. package/dist/client/assets/blockDiagram-I7D4REHJ-Sxmfy_3_.js.br +0 -0
  263. package/dist/client/assets/blockDiagram-I7D4REHJ-Sxmfy_3_.js.gz +0 -0
  264. package/dist/client/assets/c4Diagram-7LVT6UL2-DaTnrOn7.js.br +0 -0
  265. package/dist/client/assets/c4Diagram-7LVT6UL2-DaTnrOn7.js.gz +0 -0
  266. package/dist/client/assets/channel-CEvBVvt3.js +0 -1
  267. package/dist/client/assets/chunk-F27PBJKO-BNHv_EnL.js.gz +0 -0
  268. package/dist/client/assets/chunk-IMKFNOWR-CxgtyKdq.js.br +0 -0
  269. package/dist/client/assets/chunk-IMKFNOWR-CxgtyKdq.js.gz +0 -0
  270. package/dist/client/assets/chunk-SVP7TREG-B2W8-Lxh.js.br +0 -0
  271. package/dist/client/assets/chunk-TICWLB2K-BYDr9nkD.js.br +0 -0
  272. package/dist/client/assets/chunk-TICWLB2K-BYDr9nkD.js.gz +0 -0
  273. package/dist/client/assets/classDiagram-ZZMXUADV-nSmGM9Fl.js +0 -1
  274. package/dist/client/assets/classDiagram-v2-VYDZK3BY-nSmGM9Fl.js +0 -1
  275. package/dist/client/assets/cose-bilkent-JH36ORCC-CvfJj195.js.br +0 -0
  276. package/dist/client/assets/cose-bilkent-JH36ORCC-CvfJj195.js.gz +0 -0
  277. package/dist/client/assets/cynefin-OW5HDTMX-ByWE_fUn.js.gz +0 -0
  278. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BRT0d0MN.js.br +0 -0
  279. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-BRT0d0MN.js.gz +0 -0
  280. package/dist/client/assets/dagre-GXQ25YYZ-Twtyv7ud.js.br +0 -0
  281. package/dist/client/assets/dagre-GXQ25YYZ-Twtyv7ud.js.gz +0 -0
  282. package/dist/client/assets/diagram-S7CK7UJ4-Vl06hZtd.js.br +0 -0
  283. package/dist/client/assets/diagram-S7CK7UJ4-Vl06hZtd.js.gz +0 -0
  284. package/dist/client/assets/diagram-UQ7AKVKN-Cp39Mpse.js.br +0 -0
  285. package/dist/client/assets/diagram-UQ7AKVKN-Cp39Mpse.js.gz +0 -0
  286. package/dist/client/assets/diagram-VSXAHHWV-Drchji0N.js.br +0 -0
  287. package/dist/client/assets/diagram-VSXAHHWV-Drchji0N.js.gz +0 -0
  288. package/dist/client/assets/diagram-VX7I27RA-rp8n_lct.js.br +0 -0
  289. package/dist/client/assets/diagram-VX7I27RA-rp8n_lct.js.gz +0 -0
  290. package/dist/client/assets/diagram-Z3DM3KII-BEGDaMBm.js.br +0 -0
  291. package/dist/client/assets/diagram-Z3DM3KII-BEGDaMBm.js.gz +0 -0
  292. package/dist/client/assets/ebnfDiagram-PWID7BFC-kvgdD4A7.js.br +0 -0
  293. package/dist/client/assets/ebnfDiagram-PWID7BFC-kvgdD4A7.js.gz +0 -0
  294. package/dist/client/assets/erDiagram-RLTQ6QDP-DcYHv9hK.js.br +0 -0
  295. package/dist/client/assets/erDiagram-RLTQ6QDP-DcYHv9hK.js.gz +0 -0
  296. package/dist/client/assets/flowDiagram-HODETNUW-T9FQKn1F.js.br +0 -0
  297. package/dist/client/assets/flowDiagram-HODETNUW-T9FQKn1F.js.gz +0 -0
  298. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BEB_X-y5.js.br +0 -0
  299. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BEB_X-y5.js.gz +0 -0
  300. package/dist/client/assets/gestureArbiter-Do0TIdkh.js.br +0 -0
  301. package/dist/client/assets/gestureArbiter-Do0TIdkh.js.gz +0 -0
  302. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js.br +0 -0
  303. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-_0Y7kKA2.js.gz +0 -0
  304. package/dist/client/assets/index-D2_-ypCL.css.br +0 -0
  305. package/dist/client/assets/index-D2_-ypCL.css.gz +0 -0
  306. package/dist/client/assets/ishikawaDiagram-5VMMS53U-ebGuykGn.js.br +0 -0
  307. package/dist/client/assets/ishikawaDiagram-5VMMS53U-ebGuykGn.js.gz +0 -0
  308. package/dist/client/assets/journeyDiagram-3NMN7TZE-DJDkcIq5.js.br +0 -0
  309. package/dist/client/assets/journeyDiagram-3NMN7TZE-DJDkcIq5.js.gz +0 -0
  310. package/dist/client/assets/kanban-definition-UXKFOSKX-pTBbviRZ.js.br +0 -0
  311. package/dist/client/assets/kanban-definition-UXKFOSKX-pTBbviRZ.js.gz +0 -0
  312. package/dist/client/assets/layout-Dw9FB4Uc.js.br +0 -0
  313. package/dist/client/assets/layout-Dw9FB4Uc.js.gz +0 -0
  314. package/dist/client/assets/linear-CyLfVGgB.js.br +0 -0
  315. package/dist/client/assets/linear-CyLfVGgB.js.gz +0 -0
  316. package/dist/client/assets/mermaid.core-xRkirK0l.js.br +0 -0
  317. package/dist/client/assets/mermaid.core-xRkirK0l.js.gz +0 -0
  318. package/dist/client/assets/mindmap-definition-YA3MSWOX-BxvaDEGQ.js.br +0 -0
  319. package/dist/client/assets/mindmap-definition-YA3MSWOX-BxvaDEGQ.js.gz +0 -0
  320. package/dist/client/assets/passwordBootstrap-CaYbIQDC.js.br +0 -0
  321. package/dist/client/assets/passwordBootstrap-CaYbIQDC.js.gz +0 -0
  322. package/dist/client/assets/pegDiagram-XKGWAZYB-Cxh-Bbay.js.br +0 -0
  323. package/dist/client/assets/pegDiagram-XKGWAZYB-Cxh-Bbay.js.gz +0 -0
  324. package/dist/client/assets/pieDiagram-E7YTZNPT-CgOsk0Ai.js.br +0 -0
  325. package/dist/client/assets/pieDiagram-E7YTZNPT-CgOsk0Ai.js.gz +0 -0
  326. package/dist/client/assets/quadrantDiagram-AXDQQJYC-D1S_gKcT.js.br +0 -0
  327. package/dist/client/assets/quadrantDiagram-AXDQQJYC-D1S_gKcT.js.gz +0 -0
  328. package/dist/client/assets/railroadDiagram-O6MQD6OU-BG5e87Np.js.br +0 -0
  329. package/dist/client/assets/railroadDiagram-O6MQD6OU-BG5e87Np.js.gz +0 -0
  330. package/dist/client/assets/rdpSession-DL1rZ49m.js.br +0 -0
  331. package/dist/client/assets/rdpSession-DL1rZ49m.js.gz +0 -0
  332. package/dist/client/assets/referenceLongPress-DB039WbW.js.br +0 -0
  333. package/dist/client/assets/referenceLongPress-DB039WbW.js.gz +0 -0
  334. package/dist/client/assets/requirementDiagram-BXWQKSXE-C37DsQfu.js.gz +0 -0
  335. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DLzhCYoj.js.br +0 -0
  336. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DLzhCYoj.js.gz +0 -0
  337. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DeVpSvB0.js.br +0 -0
  338. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DeVpSvB0.js.gz +0 -0
  339. package/dist/client/assets/sizeCapture-INFHLROL-CE03E66U.js.br +0 -0
  340. package/dist/client/assets/sizeCapture-INFHLROL-CE03E66U.js.gz +0 -0
  341. package/dist/client/assets/stateDiagram-D77RDMKH-DuqhHT3k.js.br +0 -0
  342. package/dist/client/assets/stateDiagram-D77RDMKH-DuqhHT3k.js.gz +0 -0
  343. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CxfzEwAH.js +0 -1
  344. package/dist/client/assets/swimlanes-42K2YHIH-DxkSdJ8g.js.br +0 -0
  345. package/dist/client/assets/swimlanes-42K2YHIH-DxkSdJ8g.js.gz +0 -0
  346. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-B_muKJLi.js +0 -8
  347. package/dist/client/assets/timeline-definition-24CTP7MA-DVpcJ6y5.js.br +0 -0
  348. package/dist/client/assets/timeline-definition-24CTP7MA-DVpcJ6y5.js.gz +0 -0
  349. package/dist/client/assets/vennDiagram-4TSXK5OY-BEKXLWGa.js.br +0 -0
  350. package/dist/client/assets/vennDiagram-4TSXK5OY-BEKXLWGa.js.gz +0 -0
  351. package/dist/client/assets/videoCompression-CoVXZ8qw.js.br +0 -0
  352. package/dist/client/assets/videoCompression-CoVXZ8qw.js.gz +0 -0
  353. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cj30GTa9.js.br +0 -0
  354. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cj30GTa9.js.gz +0 -0
  355. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Cm7NEdCl.js.br +0 -0
  356. 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)
@@ -220,7 +220,7 @@ function replayFlags(agent, argv) {
220
220
  * the only durable source available for a future recovery.
221
221
  */
222
222
  export function inferResumeSessionId(agent, argv) {
223
- const safeId = (value) => value && /^[A-Za-z0-9._-]+$/.test(value) ? value : null;
223
+ const safeId = (value) => value && !value.startsWith('-') && /^[A-Za-z0-9._-]+$/.test(value) ? value : null;
224
224
  let named = -1;
225
225
  for (let index = 0; index < argv.length; index += 1) {
226
226
  if (tokenNamesAgent(argv[index], agent)) {
@@ -231,6 +231,36 @@ export function inferResumeSessionId(agent, argv) {
231
231
  if (named < 0)
232
232
  return null;
233
233
  const tail = argv.slice(named + 1);
234
+ if (agent.isPlugin) {
235
+ const template = getPluginResumeConfig(agent.slug)?.command.trim().split(/\s+/);
236
+ if (!template || !tokenNamesAgent(template[0], agent))
237
+ return null;
238
+ const pattern = template.slice(1);
239
+ if (pattern.join(' ').split('{sessionId}').length !== 2)
240
+ return null;
241
+ const matches = new Set();
242
+ // A positional resume command must start the argument tail. Flag-based
243
+ // templates may follow other launch flags, as for built-in --resume.
244
+ const starts = pattern[0]?.startsWith('-') ? tail.map((_, index) => index) : [0];
245
+ for (const start of starts) {
246
+ let id = null;
247
+ const matched = pattern.every((token, index) => {
248
+ const value = tail[start + index];
249
+ if (value === undefined)
250
+ return false;
251
+ if (!token.includes('{sessionId}'))
252
+ return token === value;
253
+ const [prefix, suffix] = token.split('{sessionId}');
254
+ if (!value.startsWith(prefix) || !value.endsWith(suffix))
255
+ return false;
256
+ id = safeId(value.slice(prefix.length, suffix ? -suffix.length : undefined));
257
+ return !!id;
258
+ });
259
+ if (matched && id)
260
+ matches.add(id);
261
+ }
262
+ return matches.size === 1 ? [...matches][0] : null;
263
+ }
234
264
  if (agent.slug === 'codex') {
235
265
  const resume = tail.indexOf('resume');
236
266
  return resume >= 0 ? safeId(tail[resume + 1]) : null;
@@ -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();
@@ -1594,7 +1596,9 @@ async function inspectCollaborationTmux(binding, requestedPane, allowPlainPane =
1594
1596
  if (!Number.isInteger(serverPid) || serverPid <= 0)
1595
1597
  throw new Error('TMUX_SERVER_IDENTITY_UNAVAILABLE');
1596
1598
  const panes = await Promise.all(layout.windows.flatMap((window) => window.panes).map(async (pane) => {
1597
- const program = await resolveTmuxPaneProgram(pane);
1599
+ // Native identity needs the foreground argv even for a named binary;
1600
+ // pane_current_command alone cannot prove an exact resumed conversation.
1601
+ const program = await resolveTmuxPaneProgram(pane, true);
1598
1602
  const agent = detectAgentFromCommand(program?.rawArgs ?? program?.command ?? '', agentCustomCommands());
1599
1603
  const nativeSessionId = agent && program?.rawArgs ? inferResumeSessionId(agent, splitCommandToArgv(program.rawArgs)) : null;
1600
1604
  return { serverPid, sessionId: layout.sessionId, paneId: pane.id, panePid: pane.pid,
@@ -1658,6 +1662,11 @@ async function resolveCollaborationRoute(frontendSessionId) {
1658
1662
  let record = globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId);
1659
1663
  if (!record)
1660
1664
  return { state: 'offline', reason: 'SESSION_REMOVED' };
1665
+ if (record.tmuxSessionName?.startsWith('wt-integration-') && !integrationDeliveryGuard)
1666
+ return { state: 'unavailable', reason: 'INTEGRATION_RUNTIME_UNAVAILABLE' };
1667
+ const startup = await integrationDeliveryGuard?.(frontendSessionId);
1668
+ if (startup && !startup.allowed)
1669
+ return { state: 'recovering', reason: startup.reason ?? 'SESSION_STARTUP_INPUT_PENDING' };
1661
1670
  let backend = resolveOrchestrationBackend(record);
1662
1671
  let binding = collaborationRouting.get(frontendSessionId) ?? {
1663
1672
  sessionId: record.sessionId, backendSessionId: null, mode: record.mode, tmuxSessionName: record.tmuxSessionName,
@@ -1745,7 +1754,7 @@ function deliverCollaborationInboxWhenAgentReady(frontendSessionId) {
1745
1754
  collaborationDeliveryWorker.wake(frontendSessionId);
1746
1755
  }
1747
1756
  async function spawnCollaborationAgentSession(req, group, sourceSessionId, input, bootstrapRole) {
1748
- const agentSlug = typeof input.agentSlug === 'string' ? input.agentSlug.trim().toLowerCase() : '';
1757
+ const { agentSlug, profile } = resolveCollaborationLaunch(group, input);
1749
1758
  const launchers = await listDetectedAgentLaunchers();
1750
1759
  const launcher = launchers.find((candidate) => candidate.slug === agentSlug);
1751
1760
  if (!launcher)
@@ -1767,7 +1776,7 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
1767
1776
  }
1768
1777
  const requestedCwd = typeof input.cwd === 'string' ? input.cwd.trim() : '';
1769
1778
  const opened = await openInventorySession(req, {
1770
- name: requestedName || `${launcher.displayName} · ${group.name}`,
1779
+ name: requestedName || `${profile?.name ?? launcher.displayName} · ${group.name}`,
1771
1780
  customName: true,
1772
1781
  mode,
1773
1782
  cwd: requestedCwd || fallbackRecord?.cwd || undefined,
@@ -1796,7 +1805,9 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
1796
1805
  });
1797
1806
  if (bootstrapRole)
1798
1807
  collaborationStore.setRole({ groupId: group.id, sessionId: frontendSessionId, role: bootstrapRole });
1799
- writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: launcher.command })}\r`);
1808
+ if (profile)
1809
+ collaborationStore.setMemberLaunchProfile(group.id, frontendSessionId, profile);
1810
+ writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: profile?.command ?? launcher.command })}\r`);
1800
1811
  const requestedTask = typeof input.task === 'string' ? input.task.trim().slice(0, 20_000) : '';
1801
1812
  collaborationStore.send({
1802
1813
  groupId: updatedGroup.id,
@@ -1808,7 +1819,7 @@ async function spawnCollaborationAgentSession(req, group, sourceSessionId, input
1808
1819
  setTimeout(() => {
1809
1820
  deliverCollaborationInboxWhenAgentReady(frontendSessionId);
1810
1821
  }, 300).unref?.();
1811
- return { group: updatedGroup, session: orchestrationSessionSnapshot(globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId)) };
1822
+ return { group: collaborationStore.getGroup(group.id), session: orchestrationSessionSnapshot(globalSessionState.sessions.find((candidate) => candidate.sessionId === frontendSessionId)) };
1812
1823
  }
1813
1824
  /** Restricted session adapter: only preconfigured argv and the exact owned
1814
1825
  * terminal can be launched; no remote drive or arbitrary shell commands. */
@@ -1836,6 +1847,16 @@ function integrationTerminalRecord(record) {
1836
1847
  }
1837
1848
  export const integrationSessionAdapter = {
1838
1849
  subscribe(listener) { integrationSessionObservers.add(listener); return () => { integrationSessionObservers.delete(listener); }; },
1850
+ registerDeliveryGuard(guard) { integrationDeliveryGuard = guard; return () => { if (integrationDeliveryGuard === guard)
1851
+ integrationDeliveryGuard = undefined; }; },
1852
+ async capture(record) {
1853
+ const observed = await integrationSessionAdapter.inspect(record);
1854
+ if (!observed.exists || !observed.running || observed.agentSlug !== record.agent_slug || !record.terminal_binding
1855
+ || record.agent_native_session_id && observed.nativeId && record.agent_native_session_id !== observed.nativeId) {
1856
+ integrationError('SESSION_IDENTITY_MISMATCH', 'Startup input observation requires the original Agent pane', 409);
1857
+ }
1858
+ return captureTmuxPaneText(runTmux, record.terminal_binding);
1859
+ },
1839
1860
  async inspect(record) {
1840
1861
  const terminal = integrationTerminalRecord(record);
1841
1862
  if (!terminal)
@@ -1959,10 +1980,16 @@ export function prepareCollaborationTaskWorker(task, template, dependencies, ser
1959
1980
  if (!source || template.serviceId !== service.descriptor().serviceId)
1960
1981
  throw new Error('执行成员当前不可用,请协调者重新分派');
1961
1982
  const snapshot = orchestrationSessionSnapshot(source);
1983
+ const inheritedProfile = collaborationStore.getGroup(task.groupId)?.memberLaunchProfiles?.[template.sessionId];
1962
1984
  const slug = snapshot.agent?.slug ?? source.agentResume?.slug;
1963
1985
  const launcher = (await listDetectedAgentLaunchers()).find(a => a.slug === slug);
1964
1986
  if (!launcher || !source.cwd)
1965
1987
  throw new Error('独立代码任务需要有项目目录的 Agent 成员,请选择可用成员');
1988
+ if (inheritedProfile) {
1989
+ if (inheritedProfile.agentSlug !== launcher.slug)
1990
+ throw new Error('执行成员的启动方案与当前 Agent 不一致,请重新分派');
1991
+ resolveCollaborationLaunch({ launchProfiles: [inheritedProfile] }, { launchProfileId: inheritedProfile.id, agentSlug: launcher.slug });
1992
+ }
1966
1993
  const workspace = await prepareTaskWorkspace(task, source.cwd, dependencies, (dependency, repository) => importTaskDependency(task, dependency, repository, service, readDependency));
1967
1994
  await pathValidator.allowSessionCwd(workspace.cwd);
1968
1995
  const existing = globalSessionState.sessions.find(s => s.cwd === workspace.cwd);
@@ -1988,7 +2015,7 @@ export function prepareCollaborationTaskWorker(task, template, dependencies, ser
1988
2015
  const temporary = `${launchFile}.${process.pid}.tmp`;
1989
2016
  await fs.promises.writeFile(temporary, JSON.stringify({ sessionId: record.sessionId, backendId: opened.terminalSession.sessionId }), { mode: 0o600 });
1990
2017
  await fs.promises.rename(temporary, launchFile);
1991
- writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: launcher.command })}\r`);
2018
+ writeTerminalInput(backend, `${buildCollaborationSpawnCommand({ slug: launcher.slug, command: inheritedProfile?.command ?? launcher.command })}\r`);
1992
2019
  }
1993
2020
  const group = collaborationStore.getGroup(task.groupId);
1994
2021
  if (!group || group.deleted)
@@ -1996,6 +2023,8 @@ export function prepareCollaborationTaskWorker(task, template, dependencies, ser
1996
2023
  if (!group.sessionIds.includes(record.sessionId))
1997
2024
  await service.save({ id: group.id, name: group.name,
1998
2025
  sessionIds: [...group.sessionIds, record.sessionId], expectedUpdatedAt: group.updatedAt });
2026
+ if (inheritedProfile)
2027
+ collaborationStore.setMemberLaunchProfile(group.id, record.sessionId, inheritedProfile);
1999
2028
  return { sessionId: record.sessionId, workspace };
2000
2029
  })().finally(() => preparingTaskWorkers.delete(key));
2001
2030
  preparingTaskWorkers.set(key, operation);
@@ -3817,14 +3846,14 @@ async function getProcessSnapshot() {
3817
3846
  });
3818
3847
  return processSnapshotPromise;
3819
3848
  }
3820
- async function resolveTmuxPaneProgram(pane) {
3849
+ async function resolveTmuxPaneProgram(pane, requireArgs = false) {
3821
3850
  // If pane command is a known shell, try to find a child foreground process
3822
3851
  const command = normalizeProgramName(pane.command);
3823
3852
  const commandKey = command?.toLowerCase() ?? null;
3824
3853
  const isShell = commandKey ? shellNamesBackend.has(commandKey) : false;
3825
3854
  // If pane command is NOT a shell but also too generic (e.g. "node"), also try
3826
3855
  const isGeneric = commandKey ? genericProgramNames.has(commandKey) : false;
3827
- if (!isShell && !isGeneric) {
3856
+ if (!isShell && !isGeneric && !requireArgs) {
3828
3857
  // Non-shell, non-generic command — pane_current_command is good enough
3829
3858
  return { command, source: 'tmux-pane', rawArgs: null };
3830
3859
  }
@@ -5956,26 +5985,28 @@ router.post('/operations/collaboration-groups/:groupId/team', async (req, res) =
5956
5985
  return res.status(404).json({ error: '协作组不存在' });
5957
5986
  if (group.federated)
5958
5987
  throw new Error('跨服务组请先从成员设置添加 Agent');
5959
- const agentSlug = typeof req.body?.agentSlug === 'string' ? req.body.agentSlug : '';
5988
+ const { agentSlug, profile } = resolveCollaborationLaunch(group, req.body ?? {});
5960
5989
  if (!(await listDetectedAgentLaunchers()).some(agent => agent.slug === agentSlug))
5961
5990
  throw new Error('所选 Agent 当前不可用');
5962
5991
  if (typeof req.body?.cwd !== 'string' || !req.body.cwd.trim())
5963
5992
  throw new Error('请选择工作目录');
5964
5993
  const cwd = await resolveWorkingDirectory(req, req.body.cwd);
5965
5994
  const previous = preparingTeams.get(group.id) ?? Promise.resolve();
5966
- const operation = previous.catch(() => { }).then(() => ensureTeam({ agentSlug, cwd,
5995
+ const operation = previous.catch(() => { }).then(() => ensureTeam({ agentSlug, cwd, launchProfileKey: launchProfileKey(profile),
5967
5996
  members: () => {
5968
5997
  const current = collaborationStore.getGroup(group.id);
5969
5998
  if (!current || current.deleted)
5970
5999
  throw new Error('协作组已删除');
6000
+ if (profile && launchProfileKey(current.launchProfiles?.find(p => p.id === profile.id)) !== launchProfileKey(profile))
6001
+ throw new Error('启动方案已变更,请重新开始协作');
5971
6002
  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 }));
6003
+ .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
6004
  },
5974
6005
  spawn: async (role) => {
5975
6006
  const current = collaborationStore.getGroup(group.id);
5976
6007
  if (!current || current.deleted)
5977
6008
  throw new Error('协作组已删除');
5978
- const result = await spawnCollaborationAgentSession(req, current, null, { agentSlug, cwd, mode: 'tmux', name: role.startsWith('自动协调者') ? `协调 ${group.name}` : `执行与评审 ${group.name}` }, role);
6009
+ const result = await spawnCollaborationAgentSession(req, current, null, { agentSlug, launchProfileId: profile?.id ?? '', cwd, mode: 'tmux', name: role.startsWith('自动协调者') ? `协调 ${group.name}` : `执行与评审 ${group.name}` }, role);
5979
6010
  return result.session.sessionId;
5980
6011
  },
5981
6012
  }));