termdock 1.4.195 → 1.4.197

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 (314) hide show
  1. package/dist/client/assets/{App-BbIl0DAa.js → App-B2nTzqxn.js} +10 -10
  2. package/dist/client/assets/App-B2nTzqxn.js.br +0 -0
  3. package/dist/client/assets/App-B2nTzqxn.js.gz +0 -0
  4. package/dist/client/assets/{ChangeWalkthroughPanel-CT7mLy_e.js → ChangeWalkthroughPanel-8XBpObo8.js} +1 -1
  5. package/dist/client/assets/ChangeWalkthroughPanel-8XBpObo8.js.br +0 -0
  6. package/dist/client/assets/ChangeWalkthroughPanel-8XBpObo8.js.gz +0 -0
  7. package/dist/client/assets/{DagPlayground-Bsf4bv7e.js → DagPlayground-BrvWN-BF.js} +1 -1
  8. package/dist/client/assets/DagPlayground-BrvWN-BF.js.br +0 -0
  9. package/dist/client/assets/DagPlayground-BrvWN-BF.js.gz +0 -0
  10. package/dist/client/assets/{DiffLab-BjiZcUSn.js → DiffLab-B_6noZdn.js} +1 -1
  11. package/dist/client/assets/DiffLab-B_6noZdn.js.br +0 -0
  12. package/dist/client/assets/DiffLab-B_6noZdn.js.gz +0 -0
  13. package/dist/client/assets/{DiffReview-DmkAyOh4.js → DiffReview-BYnopIh6.js} +1 -1
  14. package/dist/client/assets/DiffReview-BYnopIh6.js.br +0 -0
  15. package/dist/client/assets/DiffReview-BYnopIh6.js.gz +0 -0
  16. package/dist/client/assets/{DiffReviewLab-wXYUmdna.js → DiffReviewLab-DgGq28V3.js} +1 -1
  17. package/dist/client/assets/DiffReviewLab-DgGq28V3.js.br +0 -0
  18. package/dist/client/assets/DiffReviewLab-DgGq28V3.js.gz +0 -0
  19. package/dist/client/assets/{DiffViewer-BlwwMnsB.js → DiffViewer-DBB2Nxe-.js} +1 -1
  20. package/dist/client/assets/DiffViewer-DBB2Nxe-.js.br +0 -0
  21. package/dist/client/assets/DiffViewer-DBB2Nxe-.js.gz +0 -0
  22. package/dist/client/assets/{ModelPreview-DGIaQAcq.js → ModelPreview-cxnQVfjP.js} +1 -1
  23. package/dist/client/assets/ModelPreview-cxnQVfjP.js.br +0 -0
  24. package/dist/client/assets/ModelPreview-cxnQVfjP.js.gz +0 -0
  25. package/dist/client/assets/{RightSidebar-PgJ0XzEh.js → RightSidebar-BrLZH29S.js} +4 -4
  26. package/dist/client/assets/RightSidebar-BrLZH29S.js.br +0 -0
  27. package/dist/client/assets/RightSidebar-BrLZH29S.js.gz +0 -0
  28. package/dist/client/assets/{abnfDiagram-VCTEODGH-B_sCIKms.js → abnfDiagram-VCTEODGH-53wXKK7P.js} +1 -1
  29. package/dist/client/assets/abnfDiagram-VCTEODGH-53wXKK7P.js.br +0 -0
  30. package/dist/client/assets/abnfDiagram-VCTEODGH-53wXKK7P.js.gz +0 -0
  31. package/dist/client/assets/{app-5zC_J9mq.js → app-C6U2Oauh.js} +3 -3
  32. package/dist/client/assets/app-C6U2Oauh.js.br +0 -0
  33. package/dist/client/assets/app-C6U2Oauh.js.gz +0 -0
  34. package/dist/client/assets/{arc-Ps2GTfJ3.js → arc-4tt2sKMs.js} +1 -1
  35. package/dist/client/assets/arc-4tt2sKMs.js.br +0 -0
  36. package/dist/client/assets/arc-4tt2sKMs.js.gz +0 -0
  37. package/dist/client/assets/{architectureDiagram-5GKGNRK7-DxKfqPAR.js → architectureDiagram-5GKGNRK7-Clcm83eD.js} +1 -1
  38. package/dist/client/assets/architectureDiagram-5GKGNRK7-Clcm83eD.js.br +0 -0
  39. package/dist/client/assets/{architectureDiagram-5GKGNRK7-DxKfqPAR.js.gz → architectureDiagram-5GKGNRK7-Clcm83eD.js.gz} +0 -0
  40. package/dist/client/assets/{blockDiagram-I7D4REHJ-BNL8tkC0.js → blockDiagram-I7D4REHJ-CfdVuYkG.js} +1 -1
  41. package/dist/client/assets/blockDiagram-I7D4REHJ-CfdVuYkG.js.br +0 -0
  42. package/dist/client/assets/blockDiagram-I7D4REHJ-CfdVuYkG.js.gz +0 -0
  43. package/dist/client/assets/{c4Diagram-7LVT6UL2-hyEavwo6.js → c4Diagram-7LVT6UL2-BHtwHuZs.js} +1 -1
  44. package/dist/client/assets/c4Diagram-7LVT6UL2-BHtwHuZs.js.br +0 -0
  45. package/dist/client/assets/c4Diagram-7LVT6UL2-BHtwHuZs.js.gz +0 -0
  46. package/dist/client/assets/channel-B2oxQFp_.js +1 -0
  47. package/dist/client/assets/{chunk-2Q5K7J3B-DN_HrxPz.js → chunk-2Q5K7J3B-BVNi4bCF.js} +1 -1
  48. package/dist/client/assets/{chunk-5VM5RSS4-BofAFEcC.js → chunk-5VM5RSS4-lA-EJ6oE.js} +1 -1
  49. package/dist/client/assets/{chunk-F27PBJKO-CLNw1JDM.js → chunk-F27PBJKO-CrWbPzSA.js} +1 -1
  50. package/dist/client/assets/chunk-F27PBJKO-CrWbPzSA.js.br +0 -0
  51. package/dist/client/assets/chunk-F27PBJKO-CrWbPzSA.js.gz +0 -0
  52. package/dist/client/assets/{chunk-IMKFNOWR-DsVDl8p7.js → chunk-IMKFNOWR-yohtKh6p.js} +1 -1
  53. package/dist/client/assets/chunk-IMKFNOWR-yohtKh6p.js.br +0 -0
  54. package/dist/client/assets/chunk-IMKFNOWR-yohtKh6p.js.gz +0 -0
  55. package/dist/client/assets/{chunk-JWPE2WC7-CdfzOrPC.js → chunk-JWPE2WC7-DvZqwUFX.js} +1 -1
  56. package/dist/client/assets/{chunk-POPQ4Y6H-MXmTC-sG.js → chunk-POPQ4Y6H-DiGp5sHG.js} +1 -1
  57. package/dist/client/assets/{chunk-SVP7TREG-DfwfQAY6.js → chunk-SVP7TREG-B-1tHXph.js} +1 -1
  58. package/dist/client/assets/chunk-SVP7TREG-B-1tHXph.js.br +0 -0
  59. package/dist/client/assets/chunk-SVP7TREG-B-1tHXph.js.gz +0 -0
  60. package/dist/client/assets/{chunk-TICWLB2K-N3OPIvmU.js → chunk-TICWLB2K-DGO5T9qo.js} +1 -1
  61. package/dist/client/assets/chunk-TICWLB2K-DGO5T9qo.js.br +0 -0
  62. package/dist/client/assets/chunk-TICWLB2K-DGO5T9qo.js.gz +0 -0
  63. package/dist/client/assets/{chunk-XXDRQBXY-IurIojt5.js → chunk-XXDRQBXY-BC6AmHii.js} +1 -1
  64. package/dist/client/assets/classDiagram-ZZMXUADV-CQOHLrnb.js +1 -0
  65. package/dist/client/assets/classDiagram-v2-VYDZK3BY-CQOHLrnb.js +1 -0
  66. package/dist/client/assets/{cose-bilkent-JH36ORCC-DHbDHTvp.js → cose-bilkent-JH36ORCC-Bph_dwwU.js} +1 -1
  67. package/dist/client/assets/cose-bilkent-JH36ORCC-Bph_dwwU.js.br +0 -0
  68. package/dist/client/assets/cose-bilkent-JH36ORCC-Bph_dwwU.js.gz +0 -0
  69. package/dist/client/assets/{cynefin-OW5HDTMX-DyTW2Iyi.js → cynefin-OW5HDTMX-CSaZ7osN.js} +1 -1
  70. package/dist/client/assets/{cynefin-OW5HDTMX-DyTW2Iyi.js.br → cynefin-OW5HDTMX-CSaZ7osN.js.br} +0 -0
  71. package/dist/client/assets/cynefin-OW5HDTMX-CSaZ7osN.js.gz +0 -0
  72. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-DEan3pcN.js → cynefinDiagram-5FMLGOSQ-Jj0ksAR5.js} +1 -1
  73. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Jj0ksAR5.js.br +0 -0
  74. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Jj0ksAR5.js.gz +0 -0
  75. package/dist/client/assets/{dagre-GXQ25YYZ-Rnw2v1uV.js → dagre-GXQ25YYZ-DqOb5Z8X.js} +1 -1
  76. package/dist/client/assets/dagre-GXQ25YYZ-DqOb5Z8X.js.br +0 -0
  77. package/dist/client/assets/dagre-GXQ25YYZ-DqOb5Z8X.js.gz +0 -0
  78. package/dist/client/assets/{diagram-S7CK7UJ4-CmBa29S9.js → diagram-S7CK7UJ4-BjVavRpU.js} +1 -1
  79. package/dist/client/assets/diagram-S7CK7UJ4-BjVavRpU.js.br +0 -0
  80. package/dist/client/assets/diagram-S7CK7UJ4-BjVavRpU.js.gz +0 -0
  81. package/dist/client/assets/{diagram-UQ7AKVKN-Cqzmpane.js → diagram-UQ7AKVKN-BUi_Iz8S.js} +1 -1
  82. package/dist/client/assets/diagram-UQ7AKVKN-BUi_Iz8S.js.br +0 -0
  83. package/dist/client/assets/diagram-UQ7AKVKN-BUi_Iz8S.js.gz +0 -0
  84. package/dist/client/assets/{diagram-VSXAHHWV-CrHTQozH.js → diagram-VSXAHHWV-yGmUbeDV.js} +1 -1
  85. package/dist/client/assets/diagram-VSXAHHWV-yGmUbeDV.js.br +0 -0
  86. package/dist/client/assets/diagram-VSXAHHWV-yGmUbeDV.js.gz +0 -0
  87. package/dist/client/assets/{diagram-VX7I27RA-C7nQgan9.js → diagram-VX7I27RA-1wI9D0Xy.js} +1 -1
  88. package/dist/client/assets/diagram-VX7I27RA-1wI9D0Xy.js.br +0 -0
  89. package/dist/client/assets/diagram-VX7I27RA-1wI9D0Xy.js.gz +0 -0
  90. package/dist/client/assets/{diagram-Z3DM3KII-93X66n3v.js → diagram-Z3DM3KII-Ah9R4yGC.js} +1 -1
  91. package/dist/client/assets/diagram-Z3DM3KII-Ah9R4yGC.js.br +0 -0
  92. package/dist/client/assets/diagram-Z3DM3KII-Ah9R4yGC.js.gz +0 -0
  93. package/dist/client/assets/{ebnfDiagram-PWID7BFC-LfScrrH8.js → ebnfDiagram-PWID7BFC-Bc9e_6tX.js} +1 -1
  94. package/dist/client/assets/ebnfDiagram-PWID7BFC-Bc9e_6tX.js.br +0 -0
  95. package/dist/client/assets/ebnfDiagram-PWID7BFC-Bc9e_6tX.js.gz +0 -0
  96. package/dist/client/assets/{erDiagram-RLTQ6QDP-DAWKsrIB.js → erDiagram-RLTQ6QDP-CcDEtpQh.js} +1 -1
  97. package/dist/client/assets/erDiagram-RLTQ6QDP-CcDEtpQh.js.br +0 -0
  98. package/dist/client/assets/erDiagram-RLTQ6QDP-CcDEtpQh.js.gz +0 -0
  99. package/dist/client/assets/{flowDiagram-HODETNUW-CcdXfpU7.js → flowDiagram-HODETNUW-yJnnTJvd.js} +1 -1
  100. package/dist/client/assets/flowDiagram-HODETNUW-yJnnTJvd.js.br +0 -0
  101. package/dist/client/assets/flowDiagram-HODETNUW-yJnnTJvd.js.gz +0 -0
  102. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-DPLNbHSY.js → ganttDiagram-EL5Y4UJY-DazfV-Pr.js} +1 -1
  103. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DazfV-Pr.js.br +0 -0
  104. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-DPLNbHSY.js.gz → ganttDiagram-EL5Y4UJY-DazfV-Pr.js.gz} +0 -0
  105. package/dist/client/assets/{gestureArbiter-B5PxTluh.js → gestureArbiter-CIH61YPG.js} +1 -1
  106. package/dist/client/assets/gestureArbiter-CIH61YPG.js.br +0 -0
  107. package/dist/client/assets/gestureArbiter-CIH61YPG.js.gz +0 -0
  108. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-P741cwUf.js → gitGraphDiagram-WWUBYQGX-I_lUerNQ.js} +1 -1
  109. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-I_lUerNQ.js.br +0 -0
  110. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-I_lUerNQ.js.gz +0 -0
  111. package/dist/client/assets/{infoDiagram-27XIBGKW-xrl-VW74.js → infoDiagram-27XIBGKW-DEo4Zn5O.js} +1 -1
  112. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-BJmiVWrB.js → ishikawaDiagram-5VMMS53U-CiefWGfu.js} +1 -1
  113. package/dist/client/assets/ishikawaDiagram-5VMMS53U-CiefWGfu.js.br +0 -0
  114. package/dist/client/assets/ishikawaDiagram-5VMMS53U-CiefWGfu.js.gz +0 -0
  115. package/dist/client/assets/{journeyDiagram-3NMN7TZE-DZX8uYK2.js → journeyDiagram-3NMN7TZE-BHDqhmj7.js} +1 -1
  116. package/dist/client/assets/journeyDiagram-3NMN7TZE-BHDqhmj7.js.br +0 -0
  117. package/dist/client/assets/journeyDiagram-3NMN7TZE-BHDqhmj7.js.gz +0 -0
  118. package/dist/client/assets/{kanban-definition-UXKFOSKX-1SmdXf0S.js → kanban-definition-UXKFOSKX-C5EVX9Si.js} +1 -1
  119. package/dist/client/assets/kanban-definition-UXKFOSKX-C5EVX9Si.js.br +0 -0
  120. package/dist/client/assets/kanban-definition-UXKFOSKX-C5EVX9Si.js.gz +0 -0
  121. package/dist/client/assets/{layout-De4QxmMN.js → layout-BsZ6KtNp.js} +1 -1
  122. package/dist/client/assets/layout-BsZ6KtNp.js.br +0 -0
  123. package/dist/client/assets/layout-BsZ6KtNp.js.gz +0 -0
  124. package/dist/client/assets/{linear-Dc93ZnhP.js → linear-gI0rPpZO.js} +1 -1
  125. package/dist/client/assets/linear-gI0rPpZO.js.br +0 -0
  126. package/dist/client/assets/linear-gI0rPpZO.js.gz +0 -0
  127. package/dist/client/assets/{mermaid.core-Ny4ifEul.js → mermaid.core-DN5JSxkg.js} +5 -5
  128. package/dist/client/assets/mermaid.core-DN5JSxkg.js.br +0 -0
  129. package/dist/client/assets/mermaid.core-DN5JSxkg.js.gz +0 -0
  130. package/dist/client/assets/{mindmap-definition-YA3MSWOX-CE75rUrK.js → mindmap-definition-YA3MSWOX-CctHLCOq.js} +1 -1
  131. package/dist/client/assets/mindmap-definition-YA3MSWOX-CctHLCOq.js.br +0 -0
  132. package/dist/client/assets/mindmap-definition-YA3MSWOX-CctHLCOq.js.gz +0 -0
  133. package/dist/client/assets/{passwordBootstrap-BuXV9qsA.js → passwordBootstrap-CUBwV9a2.js} +1 -1
  134. package/dist/client/assets/passwordBootstrap-CUBwV9a2.js.br +0 -0
  135. package/dist/client/assets/{passwordBootstrap-BuXV9qsA.js.gz → passwordBootstrap-CUBwV9a2.js.gz} +0 -0
  136. package/dist/client/assets/{pegDiagram-XKGWAZYB-CR8E9wWF.js → pegDiagram-XKGWAZYB-BOxyckzz.js} +1 -1
  137. package/dist/client/assets/pegDiagram-XKGWAZYB-BOxyckzz.js.br +0 -0
  138. package/dist/client/assets/pegDiagram-XKGWAZYB-BOxyckzz.js.gz +0 -0
  139. package/dist/client/assets/{pieDiagram-E7YTZNPT-CKZz2-Iz.js → pieDiagram-E7YTZNPT-hBT7FUaD.js} +1 -1
  140. package/dist/client/assets/pieDiagram-E7YTZNPT-hBT7FUaD.js.br +0 -0
  141. package/dist/client/assets/pieDiagram-E7YTZNPT-hBT7FUaD.js.gz +0 -0
  142. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-w0wqPGg1.js → quadrantDiagram-AXDQQJYC-C76EhRIn.js} +1 -1
  143. package/dist/client/assets/quadrantDiagram-AXDQQJYC-C76EhRIn.js.br +0 -0
  144. package/dist/client/assets/quadrantDiagram-AXDQQJYC-C76EhRIn.js.gz +0 -0
  145. package/dist/client/assets/{railroadDiagram-O6MQD6OU-CWYxj4Iu.js → railroadDiagram-O6MQD6OU-C0OkFlWF.js} +1 -1
  146. package/dist/client/assets/railroadDiagram-O6MQD6OU-C0OkFlWF.js.br +0 -0
  147. package/dist/client/assets/railroadDiagram-O6MQD6OU-C0OkFlWF.js.gz +0 -0
  148. package/dist/client/assets/{referenceLongPress-xQTJmyUv.js → referenceLongPress-C2o_5tJ3.js} +1 -1
  149. package/dist/client/assets/referenceLongPress-C2o_5tJ3.js.br +0 -0
  150. package/dist/client/assets/referenceLongPress-C2o_5tJ3.js.gz +0 -0
  151. package/dist/client/assets/{requirementDiagram-BXWQKSXE-DzcUlIZo.js → requirementDiagram-BXWQKSXE-CMlSQQ_a.js} +1 -1
  152. package/dist/client/assets/requirementDiagram-BXWQKSXE-CMlSQQ_a.js.br +0 -0
  153. package/dist/client/assets/requirementDiagram-BXWQKSXE-CMlSQQ_a.js.gz +0 -0
  154. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-B1KFzqqb.js → sankeyDiagram-P5KCCOFB-DbwnoS5J.js} +1 -1
  155. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DbwnoS5J.js.br +0 -0
  156. package/dist/client/assets/sankeyDiagram-P5KCCOFB-DbwnoS5J.js.gz +0 -0
  157. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-Bph5Zy95.js → sequenceDiagram-WJ2MYXX4-MThPpc05.js} +1 -1
  158. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-MThPpc05.js.br +0 -0
  159. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-MThPpc05.js.gz +0 -0
  160. package/dist/client/assets/{sizeCapture-INFHLROL-B22qdSe2.js → sizeCapture-INFHLROL-Dv47MfN9.js} +1 -1
  161. package/dist/client/assets/sizeCapture-INFHLROL-Dv47MfN9.js.br +0 -0
  162. package/dist/client/assets/sizeCapture-INFHLROL-Dv47MfN9.js.gz +0 -0
  163. package/dist/client/assets/{sparkles-iFGazcNw.js → sparkles-C5GIf57F.js} +1 -1
  164. package/dist/client/assets/{stateDiagram-D77RDMKH-qt4ZIeIU.js → stateDiagram-D77RDMKH-BQMqsv45.js} +1 -1
  165. package/dist/client/assets/stateDiagram-D77RDMKH-BQMqsv45.js.br +0 -0
  166. package/dist/client/assets/stateDiagram-D77RDMKH-BQMqsv45.js.gz +0 -0
  167. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-BRUElrwt.js +1 -0
  168. package/dist/client/assets/{swimlanes-42K2YHIH-BLCfdR2d.js → swimlanes-42K2YHIH-DKZcb7w-.js} +1 -1
  169. package/dist/client/assets/swimlanes-42K2YHIH-DKZcb7w-.js.br +0 -0
  170. package/dist/client/assets/swimlanes-42K2YHIH-DKZcb7w-.js.gz +0 -0
  171. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-EvdYcr5I.js +8 -0
  172. package/dist/client/assets/{timeline-definition-24CTP7MA-B1KrZ6Jd.js → timeline-definition-24CTP7MA-C1PZiVJx.js} +1 -1
  173. package/dist/client/assets/timeline-definition-24CTP7MA-C1PZiVJx.js.br +0 -0
  174. package/dist/client/assets/timeline-definition-24CTP7MA-C1PZiVJx.js.gz +0 -0
  175. package/dist/client/assets/{triangle-alert-BNMREFNU.js → triangle-alert-BB655vyE.js} +1 -1
  176. package/dist/client/assets/{vennDiagram-4TSXK5OY-DWNDoCmv.js → vennDiagram-4TSXK5OY-CxBRn0Fl.js} +1 -1
  177. package/dist/client/assets/vennDiagram-4TSXK5OY-CxBRn0Fl.js.br +0 -0
  178. package/dist/client/assets/vennDiagram-4TSXK5OY-CxBRn0Fl.js.gz +0 -0
  179. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-kdVZqb5Z.js → wardleyDiagram-VM6X3IG4-D57k3iEF.js} +1 -1
  180. package/dist/client/assets/wardleyDiagram-VM6X3IG4-D57k3iEF.js.br +0 -0
  181. package/dist/client/assets/wardleyDiagram-VM6X3IG4-D57k3iEF.js.gz +0 -0
  182. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-C1U9Cr8F.js → xychartDiagram-S5SC5T6Z-rv8u8ZHA.js} +1 -1
  183. package/dist/client/assets/xychartDiagram-S5SC5T6Z-rv8u8ZHA.js.br +0 -0
  184. package/dist/client/assets/xychartDiagram-S5SC5T6Z-rv8u8ZHA.js.gz +0 -0
  185. package/dist/client/index.html +1 -1
  186. package/dist/client/index.html.br +0 -0
  187. package/dist/client/index.html.gz +0 -0
  188. package/dist/client/sw.js +1 -1
  189. package/dist/client/sw.js.br +0 -0
  190. package/dist/client/sw.js.gz +0 -0
  191. package/dist/server/agent/collaborationCli.js +7 -4
  192. package/dist/server/agent/collaborationDeliveryWorker.js +29 -24
  193. package/dist/server/agent/collaborationPrompt.js +41 -13
  194. package/dist/server/agent/collaborationProtocol.js +91 -0
  195. package/dist/server/agent/collaborationRoutes.js +57 -7
  196. package/dist/server/agent/collaborationStore.js +51 -5
  197. package/dist/server/routes/terminal.js +26 -5
  198. package/package.json +1 -1
  199. package/runtime-manifest.json +3 -3
  200. package/dist/client/assets/App-BbIl0DAa.js.br +0 -0
  201. package/dist/client/assets/App-BbIl0DAa.js.gz +0 -0
  202. package/dist/client/assets/ChangeWalkthroughPanel-CT7mLy_e.js.br +0 -0
  203. package/dist/client/assets/ChangeWalkthroughPanel-CT7mLy_e.js.gz +0 -0
  204. package/dist/client/assets/DagPlayground-Bsf4bv7e.js.br +0 -0
  205. package/dist/client/assets/DagPlayground-Bsf4bv7e.js.gz +0 -0
  206. package/dist/client/assets/DiffLab-BjiZcUSn.js.br +0 -0
  207. package/dist/client/assets/DiffLab-BjiZcUSn.js.gz +0 -0
  208. package/dist/client/assets/DiffReview-DmkAyOh4.js.br +0 -0
  209. package/dist/client/assets/DiffReview-DmkAyOh4.js.gz +0 -0
  210. package/dist/client/assets/DiffReviewLab-wXYUmdna.js.br +0 -0
  211. package/dist/client/assets/DiffReviewLab-wXYUmdna.js.gz +0 -0
  212. package/dist/client/assets/DiffViewer-BlwwMnsB.js.br +0 -0
  213. package/dist/client/assets/DiffViewer-BlwwMnsB.js.gz +0 -0
  214. package/dist/client/assets/ModelPreview-DGIaQAcq.js.br +0 -0
  215. package/dist/client/assets/ModelPreview-DGIaQAcq.js.gz +0 -0
  216. package/dist/client/assets/RightSidebar-PgJ0XzEh.js.br +0 -0
  217. package/dist/client/assets/RightSidebar-PgJ0XzEh.js.gz +0 -0
  218. package/dist/client/assets/abnfDiagram-VCTEODGH-B_sCIKms.js.br +0 -0
  219. package/dist/client/assets/abnfDiagram-VCTEODGH-B_sCIKms.js.gz +0 -0
  220. package/dist/client/assets/app-5zC_J9mq.js.br +0 -0
  221. package/dist/client/assets/app-5zC_J9mq.js.gz +0 -0
  222. package/dist/client/assets/arc-Ps2GTfJ3.js.br +0 -0
  223. package/dist/client/assets/arc-Ps2GTfJ3.js.gz +0 -0
  224. package/dist/client/assets/architectureDiagram-5GKGNRK7-DxKfqPAR.js.br +0 -0
  225. package/dist/client/assets/blockDiagram-I7D4REHJ-BNL8tkC0.js.br +0 -0
  226. package/dist/client/assets/blockDiagram-I7D4REHJ-BNL8tkC0.js.gz +0 -0
  227. package/dist/client/assets/c4Diagram-7LVT6UL2-hyEavwo6.js.br +0 -0
  228. package/dist/client/assets/c4Diagram-7LVT6UL2-hyEavwo6.js.gz +0 -0
  229. package/dist/client/assets/channel-Bkfq4dnz.js +0 -1
  230. package/dist/client/assets/chunk-F27PBJKO-CLNw1JDM.js.br +0 -0
  231. package/dist/client/assets/chunk-F27PBJKO-CLNw1JDM.js.gz +0 -0
  232. package/dist/client/assets/chunk-IMKFNOWR-DsVDl8p7.js.br +0 -0
  233. package/dist/client/assets/chunk-IMKFNOWR-DsVDl8p7.js.gz +0 -0
  234. package/dist/client/assets/chunk-SVP7TREG-DfwfQAY6.js.br +0 -0
  235. package/dist/client/assets/chunk-SVP7TREG-DfwfQAY6.js.gz +0 -0
  236. package/dist/client/assets/chunk-TICWLB2K-N3OPIvmU.js.br +0 -0
  237. package/dist/client/assets/chunk-TICWLB2K-N3OPIvmU.js.gz +0 -0
  238. package/dist/client/assets/classDiagram-ZZMXUADV-Dahb2o7K.js +0 -1
  239. package/dist/client/assets/classDiagram-v2-VYDZK3BY-Dahb2o7K.js +0 -1
  240. package/dist/client/assets/cose-bilkent-JH36ORCC-DHbDHTvp.js.br +0 -0
  241. package/dist/client/assets/cose-bilkent-JH36ORCC-DHbDHTvp.js.gz +0 -0
  242. package/dist/client/assets/cynefin-OW5HDTMX-DyTW2Iyi.js.gz +0 -0
  243. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DEan3pcN.js.br +0 -0
  244. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-DEan3pcN.js.gz +0 -0
  245. package/dist/client/assets/dagre-GXQ25YYZ-Rnw2v1uV.js.br +0 -0
  246. package/dist/client/assets/dagre-GXQ25YYZ-Rnw2v1uV.js.gz +0 -0
  247. package/dist/client/assets/diagram-S7CK7UJ4-CmBa29S9.js.br +0 -0
  248. package/dist/client/assets/diagram-S7CK7UJ4-CmBa29S9.js.gz +0 -0
  249. package/dist/client/assets/diagram-UQ7AKVKN-Cqzmpane.js.br +0 -0
  250. package/dist/client/assets/diagram-UQ7AKVKN-Cqzmpane.js.gz +0 -0
  251. package/dist/client/assets/diagram-VSXAHHWV-CrHTQozH.js.br +0 -0
  252. package/dist/client/assets/diagram-VSXAHHWV-CrHTQozH.js.gz +0 -0
  253. package/dist/client/assets/diagram-VX7I27RA-C7nQgan9.js.br +0 -0
  254. package/dist/client/assets/diagram-VX7I27RA-C7nQgan9.js.gz +0 -0
  255. package/dist/client/assets/diagram-Z3DM3KII-93X66n3v.js.br +0 -0
  256. package/dist/client/assets/diagram-Z3DM3KII-93X66n3v.js.gz +0 -0
  257. package/dist/client/assets/ebnfDiagram-PWID7BFC-LfScrrH8.js.br +0 -0
  258. package/dist/client/assets/ebnfDiagram-PWID7BFC-LfScrrH8.js.gz +0 -0
  259. package/dist/client/assets/erDiagram-RLTQ6QDP-DAWKsrIB.js.br +0 -0
  260. package/dist/client/assets/erDiagram-RLTQ6QDP-DAWKsrIB.js.gz +0 -0
  261. package/dist/client/assets/flowDiagram-HODETNUW-CcdXfpU7.js.br +0 -0
  262. package/dist/client/assets/flowDiagram-HODETNUW-CcdXfpU7.js.gz +0 -0
  263. package/dist/client/assets/ganttDiagram-EL5Y4UJY-DPLNbHSY.js.br +0 -0
  264. package/dist/client/assets/gestureArbiter-B5PxTluh.js.br +0 -0
  265. package/dist/client/assets/gestureArbiter-B5PxTluh.js.gz +0 -0
  266. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-P741cwUf.js.br +0 -0
  267. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-P741cwUf.js.gz +0 -0
  268. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BJmiVWrB.js.br +0 -0
  269. package/dist/client/assets/ishikawaDiagram-5VMMS53U-BJmiVWrB.js.gz +0 -0
  270. package/dist/client/assets/journeyDiagram-3NMN7TZE-DZX8uYK2.js.br +0 -0
  271. package/dist/client/assets/journeyDiagram-3NMN7TZE-DZX8uYK2.js.gz +0 -0
  272. package/dist/client/assets/kanban-definition-UXKFOSKX-1SmdXf0S.js.br +0 -0
  273. package/dist/client/assets/kanban-definition-UXKFOSKX-1SmdXf0S.js.gz +0 -0
  274. package/dist/client/assets/layout-De4QxmMN.js.br +0 -0
  275. package/dist/client/assets/layout-De4QxmMN.js.gz +0 -0
  276. package/dist/client/assets/linear-Dc93ZnhP.js.br +0 -0
  277. package/dist/client/assets/linear-Dc93ZnhP.js.gz +0 -0
  278. package/dist/client/assets/mermaid.core-Ny4ifEul.js.br +0 -0
  279. package/dist/client/assets/mermaid.core-Ny4ifEul.js.gz +0 -0
  280. package/dist/client/assets/mindmap-definition-YA3MSWOX-CE75rUrK.js.br +0 -0
  281. package/dist/client/assets/mindmap-definition-YA3MSWOX-CE75rUrK.js.gz +0 -0
  282. package/dist/client/assets/passwordBootstrap-BuXV9qsA.js.br +0 -0
  283. package/dist/client/assets/pegDiagram-XKGWAZYB-CR8E9wWF.js.br +0 -0
  284. package/dist/client/assets/pegDiagram-XKGWAZYB-CR8E9wWF.js.gz +0 -0
  285. package/dist/client/assets/pieDiagram-E7YTZNPT-CKZz2-Iz.js.br +0 -0
  286. package/dist/client/assets/pieDiagram-E7YTZNPT-CKZz2-Iz.js.gz +0 -0
  287. package/dist/client/assets/quadrantDiagram-AXDQQJYC-w0wqPGg1.js.br +0 -0
  288. package/dist/client/assets/quadrantDiagram-AXDQQJYC-w0wqPGg1.js.gz +0 -0
  289. package/dist/client/assets/railroadDiagram-O6MQD6OU-CWYxj4Iu.js.br +0 -0
  290. package/dist/client/assets/railroadDiagram-O6MQD6OU-CWYxj4Iu.js.gz +0 -0
  291. package/dist/client/assets/referenceLongPress-xQTJmyUv.js.br +0 -0
  292. package/dist/client/assets/referenceLongPress-xQTJmyUv.js.gz +0 -0
  293. package/dist/client/assets/requirementDiagram-BXWQKSXE-DzcUlIZo.js.br +0 -0
  294. package/dist/client/assets/requirementDiagram-BXWQKSXE-DzcUlIZo.js.gz +0 -0
  295. package/dist/client/assets/sankeyDiagram-P5KCCOFB-B1KFzqqb.js.br +0 -0
  296. package/dist/client/assets/sankeyDiagram-P5KCCOFB-B1KFzqqb.js.gz +0 -0
  297. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Bph5Zy95.js.br +0 -0
  298. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-Bph5Zy95.js.gz +0 -0
  299. package/dist/client/assets/sizeCapture-INFHLROL-B22qdSe2.js.br +0 -0
  300. package/dist/client/assets/sizeCapture-INFHLROL-B22qdSe2.js.gz +0 -0
  301. package/dist/client/assets/stateDiagram-D77RDMKH-qt4ZIeIU.js.br +0 -0
  302. package/dist/client/assets/stateDiagram-D77RDMKH-qt4ZIeIU.js.gz +0 -0
  303. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-xKezo5Hj.js +0 -1
  304. package/dist/client/assets/swimlanes-42K2YHIH-BLCfdR2d.js.br +0 -0
  305. package/dist/client/assets/swimlanes-42K2YHIH-BLCfdR2d.js.gz +0 -0
  306. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-CNA032xr.js +0 -8
  307. package/dist/client/assets/timeline-definition-24CTP7MA-B1KrZ6Jd.js.br +0 -0
  308. package/dist/client/assets/timeline-definition-24CTP7MA-B1KrZ6Jd.js.gz +0 -0
  309. package/dist/client/assets/vennDiagram-4TSXK5OY-DWNDoCmv.js.br +0 -0
  310. package/dist/client/assets/vennDiagram-4TSXK5OY-DWNDoCmv.js.gz +0 -0
  311. package/dist/client/assets/wardleyDiagram-VM6X3IG4-kdVZqb5Z.js.br +0 -0
  312. package/dist/client/assets/wardleyDiagram-VM6X3IG4-kdVZqb5Z.js.gz +0 -0
  313. package/dist/client/assets/xychartDiagram-S5SC5T6Z-C1U9Cr8F.js.br +0 -0
  314. package/dist/client/assets/xychartDiagram-S5SC5T6Z-C1U9Cr8F.js.gz +0 -0
@@ -1,3 +1,4 @@
1
+ import { canonicalShortId } from './collaborationProtocol.js';
1
2
  import { sanitizeCollaborationRole } from './collaborationStore.js';
2
3
  const SHELL_RULE = '─'.repeat(30);
3
4
  /** Body larger than this (in UTF-8 bytes) is replaced by a retrieval pointer
@@ -25,51 +26,78 @@ export function sanitizeCollaborationName(name, max = 40) {
25
26
  .trim();
26
27
  return cleaned.length > max ? `${cleaned.slice(0, max - 1)}…` : cleaned;
27
28
  }
28
- /** The one line of a delivered block that carries `message.id`, chosen by what
29
- * the recipient can actually do with it: an agent-sourced message gets the
29
+ /** The id form a delivery shows and the confirm gate searches for, per message.
30
+ * Canonical UUIDs shorten to their SHORT_ID_LENGTH prefix (a 36-character id
31
+ * wraps in a narrow pane and has to be retyped by whoever answers it); ids
32
+ * that are not canonical UUIDs pass through untouched. A prefix shared by two
33
+ * messages in the same delivery would make a `includes` search match the wrong block —
34
+ * including against the other one's line still sitting in terminal history —
35
+ * so every colliding message falls back to its full id. The batch is the unit
36
+ * because one delivery is what a confirm search scans against.
37
+ *
38
+ * Delivered text and the confirm gate must both derive the token here: if the
39
+ * text carried a short id while the gate searched the full one, the gate could
40
+ * never match a delivery again and would re-write it until the attempt bound
41
+ * (the "one message arrived three times" regression). */
42
+ export function collaborationMessageAnchorTokens(messages) {
43
+ const counts = new Map();
44
+ for (const message of messages) {
45
+ const short = canonicalShortId(message.id);
46
+ counts.set(short, (counts.get(short) ?? 0) + 1);
47
+ }
48
+ return new Map(messages.map((message) => {
49
+ const short = canonicalShortId(message.id);
50
+ return [message.id, counts.get(short) === 1 ? short : message.id];
51
+ }));
52
+ }
53
+ /** The one line of a delivered block that carries the message's id, chosen by
54
+ * what the recipient can actually do with it: an agent-sourced message gets the
30
55
  * reply command, a user message gets the read-back command (`td collab reply`
31
56
  * refuses user messages — NO_REPLY_TARGET — so naming it there would be a dead
32
- * command). The delivery-confirm gate searches the recipient terminal for
33
- * `message.id`, so every message must carry it regardless of source: this
34
- * function is the single place that decides how, and the invariant is pinned
35
- * by the "carries its id" test in collaborationPrompt.test.ts. */
36
- export function collaborationMessageAnchorLine(message) {
57
+ * command). Both routes accept the short form (the CLI resolves id prefixes),
58
+ * so whichever token collaborationMessageAnchorTokens picks for this delivery is what
59
+ * the recipient can paste back. Every message must carry its token regardless
60
+ * of source: the invariant is pinned by the "carries its id" test in
61
+ * collaborationPrompt.test.ts. */
62
+ export function collaborationMessageAnchorLine(message, token = canonicalShortId(message.id)) {
37
63
  return message.fromSessionId
38
- ? `回复:td collab reply ${message.id} "回复内容" --text`
39
- : `详情:td collab message get ${message.id} --json`;
64
+ ? `回复:td collab reply ${token} "回复内容" --text`
65
+ : `详情:td collab message get ${token} --json`;
40
66
  }
41
67
  /** A single delivered message: `来自:X · kind` over a fenced body, with the
42
68
  * anchor line (reply route / read-back route) kept outside the fence. A fan-out
43
69
  * dispatch (message.fanOutIds present) is flagged `· 群发` and names the
44
70
  * sibling recipients on their own line, so a broadcast is never mistaken for
45
71
  * a one-to-one assignment — raw ids fall back to the sanitized id itself. */
46
- function formatCollaborationMessage(message, source, fannedNames, fence) {
72
+ function formatCollaborationMessage(message, source, fannedNames, fence, token) {
47
73
  const lines = [`来自:${source} · ${message.kind}${fannedNames.length ? ' · 群发' : ''}`];
48
74
  if (fannedNames.length)
49
75
  lines.push(`同时发给了:${fannedNames.join('、')}`);
50
76
  lines.push('', fence, message.content, fence);
51
77
  if (message.task)
52
78
  lines.push('', `任务上报:${JSON.stringify(message.task)}`);
53
- lines.push('', collaborationMessageAnchorLine(message));
79
+ lines.push('', collaborationMessageAnchorLine(message, token));
54
80
  return lines.join('\n');
55
81
  }
56
82
  export function formatCollaborationDelivery(input) {
57
83
  const groupsById = new Map(input.groups.map((group) => [group.id, group]));
58
84
  const sessionsById = new Map(input.sessions.map((session) => [session.sessionId, session]));
85
+ const tokens = collaborationMessageAnchorTokens(input.messages);
59
86
  const blocks = [];
60
87
  for (const message of input.messages) {
61
88
  const sourceSession = message.fromSessionId ? sessionsById.get(message.fromSessionId) : null;
62
89
  const source = message.fromSessionId
63
90
  ? sanitizeCollaborationName(sourceSession?.name ?? message.fromSessionId)
64
91
  : '用户';
92
+ const token = tokens.get(message.id) ?? message.id;
65
93
  const bytes = Buffer.byteLength(message.content);
66
94
  const body = bytes > MAX_INLINE_BODY_BYTES
67
- ? `大消息已完整保存(${bytes} 字节)。使用 td collab message get ${message.id} --json 获取正文;不要把这条提示当作消息正文。`
95
+ ? `大消息已完整保存(${bytes} 字节)。使用 td collab message get ${token} --json 获取正文;不要把这条提示当作消息正文。`
68
96
  : message.content;
69
97
  const fence = '`'.repeat(Math.max(3, ...Array.from(body.matchAll(/`+/g), (match) => match[0].length + 1)));
70
98
  const fannedNames = (message.fanOutIds ?? [])
71
99
  .map((sessionId) => sanitizeCollaborationName(sessionsById.get(sessionId)?.name ?? sessionId));
72
- blocks.push(formatCollaborationMessage({ ...message, content: body }, source, fannedNames, fence));
100
+ blocks.push(formatCollaborationMessage({ ...message, content: body }, source, fannedNames, fence, token));
73
101
  }
74
102
  // The shell header names the group only when every block belongs to one;
75
103
  // a mixed batch drops the header entirely (no single-group claim to make).
@@ -1,3 +1,4 @@
1
+ import crypto from 'node:crypto';
1
2
  /** Transport facts and application reports deliberately remain separate. */
2
3
  export const COLLAB_LIMITS = {
3
4
  message_bytes: 1_048_576,
@@ -18,6 +19,96 @@ export class CollaborationError extends Error {
18
19
  this.httpStatus = httpStatus;
19
20
  }
20
21
  }
22
+ /** A delivered terminal shows a short id, because a 36-character one wraps in
23
+ * a narrow pane and has to be retyped by whoever answers it. New ids are
24
+ * minted at that length already (newCollaborationId); ids stored before that
25
+ * change are UUIDs, and read-only shortening renders those as their first
26
+ * SHORT_ID_LENGTH characters. Both forms are the same width and both resolve,
27
+ * so the display is uniform across the old and new records.
28
+ *
29
+ * The length is 10 rather than 8 because a UUID's 9th character is always a
30
+ * hyphen: at 10 characters a legacy UUID displays as `765c8819-a`, and since
31
+ * base36 never draws a hyphen that exact id cannot be minted, so a new id
32
+ * landing on an older UUID's displayed form is impossible by construction
33
+ * instead of merely guarded against. The space is also 36^10 ≈ 3.7e15, 1296×
34
+ * the 8-character one.
35
+ *
36
+ * Anything that is neither a UUID nor an already-short id passes through
37
+ * untouched — a hand-written session id (`40bc89py`), a
38
+ * `remote:<origin>:<id>` address or a `cross-<uuid>` federated group are
39
+ * either already short or decoded by their own structural rules
40
+ * (`split(':')`), so truncating them would corrupt the parse. Storage, wire
41
+ * format, receipts and federation always carry the id as stored; nothing is
42
+ * rewritten on read. */
43
+ export const SHORT_ID_LENGTH = 10;
44
+ /** Shortest input accepted as a prefix; below this a lookup is exact-match
45
+ * only, so a one-character typo never turns into an ambiguity error. With
46
+ * 10-character ids a 4-character prefix is routinely shared, so callers who
47
+ * type a prefix rather than the whole id should expect to be told to add
48
+ * characters. */
49
+ export const MIN_ID_PREFIX_LENGTH = 4;
50
+ const CANONICAL_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
51
+ export function canonicalShortId(id) {
52
+ return CANONICAL_ID.test(id) ? id.slice(0, SHORT_ID_LENGTH) : id;
53
+ }
54
+ /** Lowercase base36, matching the session ids this system already mints. The
55
+ * 10-character space is 36^10 ≈ 3.7e15 values. */
56
+ const ID_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
57
+ /** Drawn from a CSPRNG: an id is the only thing standing between a caller and
58
+ * a message they are not party to, and a predictable sequence would let a
59
+ * member of one group walk into another's. */
60
+ export function drawCollaborationId() {
61
+ let id = '';
62
+ for (let index = 0; index < SHORT_ID_LENGTH; index += 1)
63
+ id += ID_ALPHABET[crypto.randomInt(ID_ALPHABET.length)];
64
+ return id;
65
+ }
66
+ /** Mint a message, thread or group id, refusing anything already in `taken`.
67
+ * `taken` must hold every id in use *and* the truncated form each one
68
+ * displays as: resolveIdPrefix prefers an exact match, so a new id equal to a
69
+ * stored UUID's display prefix would win that race and hide the older
70
+ * message. At SHORT_ID_LENGTH = 10 a UUID's displayed form cannot be drawn
71
+ * at all (position 9 is a hyphen, which base36 never produces), but the guard
72
+ * stays because `taken` also holds ids that are short already. Locally this
73
+ * makes collisions impossible.
74
+ *
75
+ * Across machines that merge by id — federation carries ids as given, it
76
+ * cannot rewrite them — two independent draws still collide with probability
77
+ * ≈ n_A·n_B/36^10, about 1 in 900 million with both sides at the
78
+ * 2,000-message retention cap. A collision is not detected and does not
79
+ * simply drop a message: the two records merge by id, and because a
80
+ * higher-ranked status overwrites the receipt, a message that was never
81
+ * delivered can be reported as read. That fake receipt is what this length
82
+ * buys down (mergeFederatedMessages in collaborationStore.ts). */
83
+ export function newCollaborationId(taken, draw = drawCollaborationId) {
84
+ for (;;) {
85
+ const id = draw();
86
+ if (!taken.has(id)) {
87
+ taken.add(id);
88
+ return id;
89
+ }
90
+ }
91
+ }
92
+ /** Resolve what a user typed against the ids that exist, git-style: an exact
93
+ * match always wins, otherwise a prefix of at least MIN_ID_PREFIX_LENGTH
94
+ * characters resolves when it is unique. Callers turn 'ambiguous' into an
95
+ * error built by ambiguousIdMessage. */
96
+ export function resolveIdPrefix(ids, input) {
97
+ if (ids.includes(input))
98
+ return { status: 'ok', id: input };
99
+ if (input.length < MIN_ID_PREFIX_LENGTH)
100
+ return { status: 'not-found' };
101
+ const matches = ids.filter((id) => id.startsWith(input));
102
+ if (matches.length === 1)
103
+ return { status: 'ok', id: matches[0] };
104
+ return matches.length ? { status: 'ambiguous', matches } : { status: 'not-found' };
105
+ }
106
+ /** Both message and group lookups answer an ambiguous prefix the same way: say
107
+ * how many matched, never which ones (a prefix search can reach another
108
+ * session's ids), and name the way out. */
109
+ export function ambiguousIdMessage(kind, count) {
110
+ return `${kind} id 前缀匹配到 ${count} 个对象,请提供更长的前缀或完整 id`;
111
+ }
21
112
  export function validateExtras(input) {
22
113
  if (input.idempotencyKey !== undefined && (typeof input.idempotencyKey !== 'string' || !input.idempotencyKey.trim() || input.idempotencyKey.length > 256)) {
23
114
  throw new CollaborationError('INVALID_IDEMPOTENCY_KEY', 'idempotency key must contain 1–256 characters');
@@ -1,5 +1,5 @@
1
1
  import { Router } from 'express';
2
- import { COLLAB_LIMITS, CollaborationError, extrasFromBody } from './collaborationProtocol.js';
2
+ import { COLLAB_LIMITS, CollaborationError, MIN_ID_PREFIX_LENGTH, ambiguousIdMessage, extrasFromBody } from './collaborationProtocol.js';
3
3
  /** Member names arrive agent-authored over a CLI; control characters would
4
4
  * corrupt terminal shells, tmux options and persisted records. Fold them to
5
5
  * spaces, collapse runs, and cap the length (display sanitization for the
@@ -30,10 +30,32 @@ export function collaborationRoutes({ store, resolveSession, deliver, rebind, re
30
30
  }
31
31
  res.json({ ok: true, route: await rebind(sessionId, pane ?? null) });
32
32
  }));
33
+ /** Accepts the short id a terminal shows as well as the full one; an
34
+ * ambiguous prefix is refused rather than guessed, so a reply can never
35
+ * land on the wrong thread. */
36
+ const resolveMessage = (id) => {
37
+ const resolved = store.resolveMessageId(id);
38
+ if (resolved.status === 'ambiguous')
39
+ throw new CollaborationError('MESSAGE_ID_AMBIGUOUS', ambiguousIdMessage('消息', resolved.matches.length));
40
+ return resolved.status === 'ok' ? store.getMessage(resolved.id) : null;
41
+ };
42
+ /** "Nothing matched" and "matched, but not yours" used to share both a code
43
+ * and a sentence, and the code is what a caller reads first: MESSAGE_NOT_FOUND
44
+ * next to "does not belong to this session" reads as a broken id lookup, not
45
+ * as a refusal. A caller holding a real id has hit this. So the code names
46
+ * the case and the sentence says which way it failed; the status stays 404
47
+ * either way, and the body still carries no content the caller may not see. */
33
48
  const ownMessage = (id, sessionId, recipientOnly = false) => {
34
- const message = store.getMessage(id);
35
- if (!message || (message.toSessionId !== sessionId && (recipientOnly || message.fromSessionId !== sessionId)))
36
- throw new CollaborationError('MESSAGE_NOT_FOUND', 'Message does not belong to this session', 404);
49
+ const message = resolveMessage(id);
50
+ if (!message)
51
+ throw new CollaborationError('MESSAGE_NOT_FOUND', id.length < MIN_ID_PREFIX_LENGTH
52
+ ? `No message has this exact id; prefixes are searched from ${MIN_ID_PREFIX_LENGTH} characters up`
53
+ : 'No message with this id or prefix; it may have expired or been pruned', 404);
54
+ if (message.toSessionId !== sessionId && (recipientOnly || message.fromSessionId !== sessionId)) {
55
+ throw new CollaborationError('MESSAGE_NOT_YOURS', recipientOnly
56
+ ? 'Message exists but was not addressed to this session'
57
+ : 'Message exists but this session is neither its sender nor its recipient', 404);
58
+ }
37
59
  return message;
38
60
  };
39
61
  const sent = (res, ids) => {
@@ -83,8 +105,18 @@ export function collaborationRoutes({ store, resolveSession, deliver, rebind, re
83
105
  const targets = requestedTargets.length > 0 ? requestedTargets : (target ? [target] : []);
84
106
  if (!targets.length)
85
107
  throw new CollaborationError('NO_TARGET', 'send requires a targetSessionId (or toSessionIds for a fan-out)', 400);
108
+ // `--group` may be the short id; an unresolvable one must stay a
109
+ // GROUP_NOT_FOUND below instead of silently falling back to the only
110
+ // shared group, which would send to a set the sender did not name.
111
+ let requestedGroupId = typeof req.body.group_id === 'string' ? req.body.group_id : '';
112
+ if (requestedGroupId) {
113
+ const resolved = store.resolveGroupId(requestedGroupId);
114
+ if (resolved.status === 'ambiguous')
115
+ throw new CollaborationError('GROUP_ID_AMBIGUOUS', ambiguousIdMessage('协作组', resolved.matches.length));
116
+ requestedGroupId = resolved.status === 'ok' ? resolved.id : requestedGroupId;
117
+ }
86
118
  const groups = store.groupsForSession(sessionId)
87
- .filter((group) => targets.every((candidate) => group.sessionIds.includes(candidate)) && (!req.body.group_id || group.id === req.body.group_id));
119
+ .filter((group) => targets.every((candidate) => group.sessionIds.includes(candidate)) && (!requestedGroupId || group.id === requestedGroupId));
88
120
  if (!groups.length)
89
121
  throw new CollaborationError('GROUP_NOT_FOUND', 'Sender and recipients must share the specified group');
90
122
  if (groups.length > 1 && !req.body.group_id)
@@ -129,9 +161,24 @@ export function collaborationRoutes({ store, resolveSession, deliver, rebind, re
129
161
  const since = sinceText === undefined ? undefined : /^\d+$/.test(sinceText) ? Number(sinceText) : Date.parse(sinceText);
130
162
  if (since !== undefined && !Number.isFinite(since))
131
163
  throw new CollaborationError('INVALID_SINCE', 'since must be an ISO timestamp or epoch milliseconds');
164
+ // `--group` accepts the short id; an unresolvable or ambiguous one
165
+ // keeps the caller's text, which matches no group and yields an empty page
166
+ // (the pre-existing behavior for an unknown group filter).
167
+ let groupFilter = string('group');
168
+ if (groupFilter) {
169
+ const resolved = store.resolveGroupId(groupFilter);
170
+ if (resolved.status === 'ok')
171
+ groupFilter = resolved.id;
172
+ }
173
+ let threadFilter = string('thread');
174
+ if (threadFilter) {
175
+ const resolved = store.resolveThreadId(threadFilter);
176
+ if (resolved.status === 'ok')
177
+ threadFilter = resolved.id;
178
+ }
132
179
  const page = store.page(sessionId, { unread: string('unread') === 'true', since, afterId: string('after_id'), cursor: string('cursor'),
133
180
  consumer: string('consumer'), limit: string('limit') ? Number(string('limit')) : undefined,
134
- from: string('from'), group: string('group'), thread: string('thread'), kind: string('kind'), responseKind: string('response_kind'), order: string('order') });
181
+ from: string('from'), group: groupFilter, thread: threadFilter, kind: string('kind'), responseKind: string('response_kind'), order: string('order') });
135
182
  // Fan-out edges carry sibling recipients; the names map lets the CLI text
136
183
  // renderer show 同时发给了:… as people instead of bare session ids. The
137
184
  // fallback to the raw id stays server-side agnostic (federated/offline).
@@ -143,7 +190,10 @@ export function collaborationRoutes({ store, resolveSession, deliver, rebind, re
143
190
  res.json({ ok: true, consumer: req.body.consumer, cursor: req.body.cursor });
144
191
  }));
145
192
  const groupOf = (id, sessionId) => {
146
- const group = store.getGroup(id);
193
+ const resolved = store.resolveGroupId(id);
194
+ if (resolved.status === 'ambiguous')
195
+ throw new CollaborationError('GROUP_ID_AMBIGUOUS', ambiguousIdMessage('协作组', resolved.matches.length));
196
+ const group = resolved.status === 'ok' ? store.getGroup(resolved.id) : null;
147
197
  if (!group || group.deleted)
148
198
  throw new CollaborationError('GROUP_NOT_FOUND', 'Collaboration group not found', 404);
149
199
  if (!group.sessionIds.includes(sessionId))
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import crypto from 'node:crypto';
4
- import { COLLAB_LIMITS, CollaborationError, STATUS_RANK, validateExtras } from './collaborationProtocol.js';
4
+ import { COLLAB_LIMITS, CollaborationError, STATUS_RANK, canonicalShortId, drawCollaborationId, newCollaborationId, resolveIdPrefix, validateExtras } from './collaborationProtocol.js';
5
5
  /** Characters that would corrupt a role line injected into the delivery shell
6
6
  * (`你的定位:...`) or smuggle terminal output. Unlike names, roles are plain
7
7
  * prose — 「」 and · are harmless there — so only control sequences are barred. */
@@ -25,10 +25,15 @@ const MAX_MESSAGES = 2_000;
25
25
  const MESSAGE_KINDS = new Set(['message', 'ask', 'reply', 'task', 'handoff', 'done']);
26
26
  export class CollaborationStore {
27
27
  filePath;
28
+ drawId;
28
29
  document = { version: 2, groups: [], messages: [] };
29
30
  persistedDocument = JSON.stringify(this.document);
30
- constructor(filePath) {
31
+ /** `drawId` is the random source behind newCollaborationId. It is a
32
+ * constructor seam so tests can force a collision and prove the guard
33
+ * below actually skips it; production always uses the CSPRNG default. */
34
+ constructor(filePath, drawId = drawCollaborationId) {
31
35
  this.filePath = filePath;
36
+ this.drawId = drawId;
32
37
  try {
33
38
  const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
34
39
  if (Array.isArray(parsed.groups)) {
@@ -57,6 +62,36 @@ export class CollaborationStore {
57
62
  getGroup(id) {
58
63
  return this.document.groups.find((group) => group.id === id) ?? null;
59
64
  }
65
+ /** Typed lookups accept the short id a terminal shows as well as the full
66
+ * one: exact match first, then a unique prefix. Ambiguity is reported,
67
+ * never guessed at, and resolution happens before any send so the
68
+ * idempotency payload hash still sees the full id. */
69
+ resolveGroupId(id) {
70
+ return resolveIdPrefix(this.document.groups.map((group) => group.id), id);
71
+ }
72
+ /** Everything a freshly minted id must not collide with. It holds two forms
73
+ * of each stored id: the id itself, and the truncated form it displays as.
74
+ * The second form is what keeps a new id from landing on the visible form
75
+ * of an older UUID — resolveIdPrefix prefers an exact match, so that
76
+ * collision would silently answer lookups with the wrong message. Ids
77
+ * arrive from federation as given and cannot be rewritten, so this only
78
+ * guarantees uniqueness within what this store has seen. */
79
+ takenIds() {
80
+ const taken = new Set();
81
+ const add = (id) => {
82
+ if (!id)
83
+ return;
84
+ taken.add(id);
85
+ taken.add(canonicalShortId(id));
86
+ };
87
+ for (const group of this.document.groups)
88
+ add(group.id);
89
+ for (const message of this.document.messages) {
90
+ add(message.id);
91
+ add(message.threadId);
92
+ }
93
+ return taken;
94
+ }
60
95
  save(input) {
61
96
  const now = Date.now();
62
97
  const existing = input.id ? this.document.groups.find((group) => group.id === input.id) : null;
@@ -64,7 +99,7 @@ export class CollaborationStore {
64
99
  const group = pruneRoles({
65
100
  ...existing,
66
101
  ...(existing?.remoteSessions ? { remoteSessions: existing.remoteSessions.filter((session) => sessionIds.includes(session.sessionId)) } : {}),
67
- id: existing?.id ?? crypto.randomUUID(),
102
+ id: existing?.id ?? newCollaborationId(this.takenIds(), this.drawId),
68
103
  name: input.name.trim(),
69
104
  sessionIds,
70
105
  createdAt: existing?.createdAt ?? now,
@@ -197,11 +232,12 @@ export class CollaborationStore {
197
232
  throw new CollaborationError('IDEMPOTENCY_RECORD_GONE', 'Original message removed; this key cannot be reused yet', 409);
198
233
  return originals;
199
234
  }
200
- const threadId = input.threadId?.trim() || crypto.randomUUID();
235
+ const taken = this.takenIds();
236
+ const threadId = input.threadId?.trim() || newCollaborationId(taken, this.drawId);
201
237
  const messages = recipients.map((toSessionId) => ({
202
238
  idempotencyKey: input.idempotencyKey, responseKind: extras.responseKind, metadata: extras.metadata, task: extras.task, expiresAt: extras.expiresAt,
203
239
  sequence: this.nextSequence(),
204
- id: crypto.randomUUID(), groupId: group.id, fromSessionId: input.fromSessionId,
240
+ id: newCollaborationId(taken, this.drawId), groupId: group.id, fromSessionId: input.fromSessionId,
205
241
  toSessionId, kind: input.kind, content, threadId,
206
242
  fanOutIds: recipients.length > 1 ? recipients.filter((id) => id !== toSessionId) : undefined,
207
243
  replyTo: input.replyTo?.trim() || null, status: 'pending', createdAt: now,
@@ -222,6 +258,16 @@ export class CollaborationStore {
222
258
  this.expire();
223
259
  return this.document.messages.find((message) => message.id === id) ?? null;
224
260
  }
261
+ resolveMessageId(id) {
262
+ this.expire();
263
+ return resolveIdPrefix(this.document.messages.map((message) => message.id), id);
264
+ }
265
+ /** Thread ids are shown shortened alongside message ids, so the `--thread`
266
+ * filter has to take one back the same way. */
267
+ resolveThreadId(id) {
268
+ this.expire();
269
+ return resolveIdPrefix([...new Set(this.document.messages.map((message) => message.threadId).filter((threadId) => Boolean(threadId)))], id);
270
+ }
225
271
  inbox(sessionId, options = {}) {
226
272
  this.expire();
227
273
  const matches = this.document.messages.filter((message) => message.toSessionId === sessionId && (!options.pendingOnly || message.status === 'pending'));
@@ -40,6 +40,7 @@ import { collaborationRoutes } from '../agent/collaborationRoutes.js';
40
40
  import { COLLAB_LIMITS, extrasFromBody } from '../agent/collaborationProtocol.js';
41
41
  import { CollaborationStore } from '../agent/collaborationStore.js';
42
42
  import { COLLAB_NAME_FORBIDDEN, formatCollaborationDelivery } from '../agent/collaborationPrompt.js';
43
+ import { ambiguousIdMessage } from '../agent/collaborationProtocol.js';
43
44
  import { buildCollaborationSpawnCommand, resolveCollaborationSpawnMode } from '../agent/collaborationSpawn.js';
44
45
  import { SessionSearchStore } from '../agent/sessionSearchStore.js';
45
46
  import { resolveCollaborationBackend, resolveCollaborationSessionId } from '../agent/sessionBindingRecovery.js';
@@ -5519,8 +5520,20 @@ router.post('/operations/collaboration-federation', (req, res) => {
5519
5520
  });
5520
5521
  router.use('/operations', collaborationGroupRoutes({ store: collaborationStore,
5521
5522
  sessions: () => globalSessionState.sessions.map(orchestrationSessionSnapshot) }));
5523
+ /** Group id from a URL param: the short id a terminal shows resolves to
5524
+ * its full id, an ambiguous prefix is refused with 409, and anything else is
5525
+ * simply "no such group" — the same 404 each route already answers. */
5526
+ function resolveGroupParam(id) {
5527
+ const resolved = collaborationStore.resolveGroupId(id);
5528
+ if (resolved.status === 'ambiguous')
5529
+ return { group: null, ambiguous: resolved.matches.length };
5530
+ return { group: resolved.status === 'ok' ? collaborationStore.getGroup(resolved.id) : null, ambiguous: 0 };
5531
+ }
5532
+ const ambiguousGroupResponse = (res, count) => res.status(409).json({ error: ambiguousIdMessage('协作组', count), code: 'GROUP_ID_AMBIGUOUS' });
5522
5533
  router.post('/operations/collaboration-groups/:groupId/spawn', async (req, res) => {
5523
- const group = collaborationStore.getGroup(req.params.groupId);
5534
+ const { group, ambiguous } = resolveGroupParam(req.params.groupId);
5535
+ if (ambiguous)
5536
+ return ambiguousGroupResponse(res, ambiguous);
5524
5537
  if (!group)
5525
5538
  return res.status(404).json({ error: '协作组不存在' });
5526
5539
  try {
@@ -5533,14 +5546,18 @@ router.post('/operations/collaboration-groups/:groupId/spawn', async (req, res)
5533
5546
  }
5534
5547
  });
5535
5548
  router.get('/operations/collaboration-groups/:groupId/messages', (req, res) => {
5536
- const group = collaborationStore.getGroup(req.params.groupId);
5549
+ const { group, ambiguous } = resolveGroupParam(req.params.groupId);
5550
+ if (ambiguous)
5551
+ return ambiguousGroupResponse(res, ambiguous);
5537
5552
  if (!group)
5538
5553
  return res.status(404).json({ error: '协作组不存在' });
5539
5554
  res.json({ messages: collaborationStore.listMessages(group.id, Number(req.query.limit) || 200) });
5540
5555
  });
5541
5556
  router.post('/operations/collaboration-groups/:groupId/messages', (req, res) => {
5542
5557
  try {
5543
- const group = collaborationStore.getGroup(req.params.groupId);
5558
+ const { group, ambiguous } = resolveGroupParam(req.params.groupId);
5559
+ if (ambiguous)
5560
+ return ambiguousGroupResponse(res, ambiguous);
5544
5561
  if (!group)
5545
5562
  return res.status(404).json({ error: '协作组不存在' });
5546
5563
  const fromSessionId = resolveFrontendSessionId(req.body ?? {});
@@ -5592,7 +5609,9 @@ router.post('/operations/orchestration/members', (req, res) => {
5592
5609
  const groupId = typeof req.body?.groupId === 'string' ? req.body.groupId.trim() : '';
5593
5610
  const targetSessionId = typeof req.body?.targetSessionId === 'string' ? req.body.targetSessionId.trim() : '';
5594
5611
  const action = req.body?.action === 'remove' ? 'remove' : req.body?.action === 'add' ? 'add' : null;
5595
- const group = collaborationStore.getGroup(groupId);
5612
+ const { group, ambiguous } = resolveGroupParam(groupId);
5613
+ if (ambiguous)
5614
+ return ambiguousGroupResponse(res, ambiguous);
5596
5615
  if (!sourceSessionId)
5597
5616
  return res.status(400).json({ error: '无法识别当前会话;请从 Termdock 会话内运行 td collab' });
5598
5617
  if (!group || !group.sessionIds.includes(sourceSessionId))
@@ -5613,7 +5632,9 @@ router.post('/operations/orchestration/members', (req, res) => {
5613
5632
  router.post('/operations/orchestration/spawn', async (req, res) => {
5614
5633
  const sourceSessionId = resolveFrontendSessionId(req.body ?? {});
5615
5634
  const groupId = typeof req.body?.groupId === 'string' ? req.body.groupId.trim() : '';
5616
- const group = collaborationStore.getGroup(groupId);
5635
+ const { group, ambiguous } = resolveGroupParam(groupId);
5636
+ if (ambiguous)
5637
+ return ambiguousGroupResponse(res, ambiguous);
5617
5638
  if (!sourceSessionId)
5618
5639
  return res.status(400).json({ error: '无法识别当前会话;请从 Termdock 会话内运行 td collab' });
5619
5640
  if (!group || !group.sessionIds.includes(sourceSessionId))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termdock",
3
- "version": "1.4.195",
3
+ "version": "1.4.197",
4
4
  "description": "A complete web-based terminal application with modern UI",
5
5
  "type": "module",
6
6
  "main": ".desktop-dist/main.js",
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "packageName": "termdock",
4
- "version": "1.4.195",
4
+ "version": "1.4.197",
5
5
  "runtimeProtocolVersion": 1,
6
6
  "minimumDesktopVersion": "1.4.46",
7
7
  "nodeMajor": 22,
8
8
  "dependencyHash": "sha256-K4KyYJuVoQDpZsBM8ZX1Rw3MddOxAzVdp/cPm0iGZz0=",
9
- "serverBundleHash": "sha256-T9Yr6kyM0VWWMdS6LFYinasOYunqdstwvkzVg2zQqZE=",
10
- "clientBundleHash": "sha256-wpGsT/oJup/G4MyJSKqpHBJlzc2Ha3wFIWmtp4vc/Wc=",
9
+ "serverBundleHash": "sha256-+T9vc3U3rPgbrg1oKb9GWwaTbuYXTow0I3cAqV6TIlc=",
10
+ "clientBundleHash": "sha256-GKvLn08PjXFK40TGm1OlgtPAA9Tcvy+GYnD2hU+pys0=",
11
11
  "entrypoint": "dist/server/cli.js",
12
12
  "clientEntrypoint": "dist/client/index.html"
13
13
  }
@@ -1 +0,0 @@
1
- import{c as a,C as n}from"./mermaid.core-Ny4ifEul.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,a as s,c as t,C as e}from"./chunk-TICWLB2K-N3OPIvmU.js";import{d as i}from"./mermaid.core-Ny4ifEul.js";import"./chunk-5VM5RSS4-BofAFEcC.js";import"./chunk-XXDRQBXY-IurIojt5.js";import"./chunk-POPQ4Y6H-MXmTC-sG.js";import"./chunk-F27PBJKO-CLNw1JDM.js";import"./purify.es-4eNOHKSq.js";import"./app-5zC_J9mq.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:t,get db(){return new e},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{g as diagram};
@@ -1 +0,0 @@
1
- import{s as a,a as s,c as t,C as e}from"./chunk-TICWLB2K-N3OPIvmU.js";import{d as i}from"./mermaid.core-Ny4ifEul.js";import"./chunk-5VM5RSS4-BofAFEcC.js";import"./chunk-XXDRQBXY-IurIojt5.js";import"./chunk-POPQ4Y6H-MXmTC-sG.js";import"./chunk-F27PBJKO-CLNw1JDM.js";import"./purify.es-4eNOHKSq.js";import"./app-5zC_J9mq.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:t,get db(){return new e},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{g as diagram};