termdock 1.4.307 → 1.4.309

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 (352) hide show
  1. package/dist/client/assets/{App-cagV-flM.js → App-ggIA_xqR.js} +6 -6
  2. package/dist/client/assets/App-ggIA_xqR.js.br +0 -0
  3. package/dist/client/assets/App-ggIA_xqR.js.gz +0 -0
  4. package/dist/client/assets/{ArchitecturePanel-TdKPja7L.js → ArchitecturePanel-CoBspW_C.js} +1 -1
  5. package/dist/client/assets/ArchitecturePanel-CoBspW_C.js.br +0 -0
  6. package/dist/client/assets/ArchitecturePanel-CoBspW_C.js.gz +0 -0
  7. package/dist/client/assets/{ChangeWalkthroughPanel-DOj40Srm.js → ChangeWalkthroughPanel-D1Puc7B3.js} +1 -1
  8. package/dist/client/assets/ChangeWalkthroughPanel-D1Puc7B3.js.br +0 -0
  9. package/dist/client/assets/ChangeWalkthroughPanel-D1Puc7B3.js.gz +0 -0
  10. package/dist/client/assets/{CollaborationMainWorkspace-C5Tcos5l.js → CollaborationMainWorkspace-BPyK5Xs8.js} +1 -1
  11. package/dist/client/assets/CollaborationMainWorkspace-BPyK5Xs8.js.br +0 -0
  12. package/dist/client/assets/CollaborationMainWorkspace-BPyK5Xs8.js.gz +0 -0
  13. package/dist/client/assets/CollaborationTaskContent-CTSJJqze.js +2 -0
  14. package/dist/client/assets/{ComputerControlView-C2sOFyuA.js → ComputerControlView-D5Lw9sNA.js} +2 -2
  15. package/dist/client/assets/ComputerControlView-D5Lw9sNA.js.br +0 -0
  16. package/dist/client/assets/ComputerControlView-D5Lw9sNA.js.gz +0 -0
  17. package/dist/client/assets/{DagPlayground-DFfVJW00.js → DagPlayground-DYJrGfiG.js} +1 -1
  18. package/dist/client/assets/DagPlayground-DYJrGfiG.js.br +0 -0
  19. package/dist/client/assets/DagPlayground-DYJrGfiG.js.gz +0 -0
  20. package/dist/client/assets/{DiffLab-hoj8gwUi.js → DiffLab-5zdcFW3z.js} +1 -1
  21. package/dist/client/assets/DiffLab-5zdcFW3z.js.br +0 -0
  22. package/dist/client/assets/DiffLab-5zdcFW3z.js.gz +0 -0
  23. package/dist/client/assets/{DiffReview-Cn_PmN-9.js → DiffReview-BBBC_NjV.js} +1 -1
  24. package/dist/client/assets/DiffReview-BBBC_NjV.js.br +0 -0
  25. package/dist/client/assets/DiffReview-BBBC_NjV.js.gz +0 -0
  26. package/dist/client/assets/{DiffReviewLab-DycgD40E.js → DiffReviewLab-CbhosFSB.js} +1 -1
  27. package/dist/client/assets/DiffReviewLab-CbhosFSB.js.br +0 -0
  28. package/dist/client/assets/DiffReviewLab-CbhosFSB.js.gz +0 -0
  29. package/dist/client/assets/{DiffViewer-DRnuph7U.js → DiffViewer-BuQwFQ4V.js} +1 -1
  30. package/dist/client/assets/DiffViewer-BuQwFQ4V.js.br +0 -0
  31. package/dist/client/assets/DiffViewer-BuQwFQ4V.js.gz +0 -0
  32. package/dist/client/assets/{ModelPreview-Dvw-UtQQ.js → ModelPreview-nO2VmqdP.js} +1 -1
  33. package/dist/client/assets/ModelPreview-nO2VmqdP.js.br +0 -0
  34. package/dist/client/assets/ModelPreview-nO2VmqdP.js.gz +0 -0
  35. package/dist/client/assets/{RightSidebar-CnnU7Ffp.js → RightSidebar-BoNzpS2K.js} +3 -3
  36. package/dist/client/assets/RightSidebar-BoNzpS2K.js.br +0 -0
  37. package/dist/client/assets/{RightSidebar-CnnU7Ffp.js.gz → RightSidebar-BoNzpS2K.js.gz} +0 -0
  38. package/dist/client/assets/{abnfDiagram-VCTEODGH-BQVIT5gD.js → abnfDiagram-VCTEODGH-yi0K8GDG.js} +1 -1
  39. package/dist/client/assets/abnfDiagram-VCTEODGH-yi0K8GDG.js.br +0 -0
  40. package/dist/client/assets/abnfDiagram-VCTEODGH-yi0K8GDG.js.gz +0 -0
  41. package/dist/client/assets/{app-CXvK8WZg.js → app-B6FdOF6K.js} +3 -3
  42. package/dist/client/assets/app-B6FdOF6K.js.br +0 -0
  43. package/dist/client/assets/app-B6FdOF6K.js.gz +0 -0
  44. package/dist/client/assets/{arc-DZG1NzUc.js → arc-_nLI_-XZ.js} +1 -1
  45. package/dist/client/assets/arc-_nLI_-XZ.js.br +0 -0
  46. package/dist/client/assets/arc-_nLI_-XZ.js.gz +0 -0
  47. package/dist/client/assets/{architectureDiagram-5GKGNRK7-Bv15lJGE.js → architectureDiagram-5GKGNRK7-CikrA78O.js} +1 -1
  48. package/dist/client/assets/architectureDiagram-5GKGNRK7-CikrA78O.js.br +0 -0
  49. package/dist/client/assets/architectureDiagram-5GKGNRK7-CikrA78O.js.gz +0 -0
  50. package/dist/client/assets/{blockDiagram-I7D4REHJ-DShw-lH7.js → blockDiagram-I7D4REHJ-CWJq6qJ7.js} +1 -1
  51. package/dist/client/assets/blockDiagram-I7D4REHJ-CWJq6qJ7.js.br +0 -0
  52. package/dist/client/assets/blockDiagram-I7D4REHJ-CWJq6qJ7.js.gz +0 -0
  53. package/dist/client/assets/{c4Diagram-7LVT6UL2-BN6SsyOM.js → c4Diagram-7LVT6UL2-gzPzLGvs.js} +1 -1
  54. package/dist/client/assets/c4Diagram-7LVT6UL2-gzPzLGvs.js.br +0 -0
  55. package/dist/client/assets/c4Diagram-7LVT6UL2-gzPzLGvs.js.gz +0 -0
  56. package/dist/client/assets/channel-BYsFmOmF.js +1 -0
  57. package/dist/client/assets/{chunk-2Q5K7J3B-BkBkNjv0.js → chunk-2Q5K7J3B-CfX9l18E.js} +1 -1
  58. package/dist/client/assets/{chunk-5VM5RSS4-LrpNvv_o.js → chunk-5VM5RSS4-JSV1yrXM.js} +1 -1
  59. package/dist/client/assets/{chunk-F27PBJKO-BcpMMU4P.js → chunk-F27PBJKO-CQ0LXeap.js} +1 -1
  60. package/dist/client/assets/chunk-F27PBJKO-CQ0LXeap.js.br +0 -0
  61. package/dist/client/assets/chunk-F27PBJKO-CQ0LXeap.js.gz +0 -0
  62. package/dist/client/assets/{chunk-IMKFNOWR-cWvd5gJk.js → chunk-IMKFNOWR-CJOEPkK9.js} +1 -1
  63. package/dist/client/assets/chunk-IMKFNOWR-CJOEPkK9.js.br +0 -0
  64. package/dist/client/assets/chunk-IMKFNOWR-CJOEPkK9.js.gz +0 -0
  65. package/dist/client/assets/{chunk-JWPE2WC7-Bf4ORht5.js → chunk-JWPE2WC7-Dpp7Xu_E.js} +1 -1
  66. package/dist/client/assets/{chunk-POPQ4Y6H-M5MSHOUM.js → chunk-POPQ4Y6H-9hDa1b8z.js} +1 -1
  67. package/dist/client/assets/{chunk-SVP7TREG-Bj8gUFc2.js → chunk-SVP7TREG-BskkG9gg.js} +1 -1
  68. package/dist/client/assets/chunk-SVP7TREG-BskkG9gg.js.br +0 -0
  69. package/dist/client/assets/chunk-SVP7TREG-BskkG9gg.js.gz +0 -0
  70. package/dist/client/assets/{chunk-TICWLB2K-W1cGkC4s.js → chunk-TICWLB2K-DyzuP-9I.js} +1 -1
  71. package/dist/client/assets/chunk-TICWLB2K-DyzuP-9I.js.br +0 -0
  72. package/dist/client/assets/chunk-TICWLB2K-DyzuP-9I.js.gz +0 -0
  73. package/dist/client/assets/{chunk-XXDRQBXY-mXLNbLH8.js → chunk-XXDRQBXY-C31_1i6z.js} +1 -1
  74. package/dist/client/assets/classDiagram-ZZMXUADV-BnmZv6Ns.js +1 -0
  75. package/dist/client/assets/classDiagram-v2-VYDZK3BY-BnmZv6Ns.js +1 -0
  76. package/dist/client/assets/{cose-bilkent-JH36ORCC-CK0EaMQY.js → cose-bilkent-JH36ORCC-Bm-ESDOc.js} +1 -1
  77. package/dist/client/assets/cose-bilkent-JH36ORCC-Bm-ESDOc.js.br +0 -0
  78. package/dist/client/assets/cose-bilkent-JH36ORCC-Bm-ESDOc.js.gz +0 -0
  79. package/dist/client/assets/{cynefin-OW5HDTMX-C-7tsMpt.js → cynefin-OW5HDTMX-DbFWAPvX.js} +1 -1
  80. package/dist/client/assets/cynefin-OW5HDTMX-DbFWAPvX.js.br +0 -0
  81. package/dist/client/assets/cynefin-OW5HDTMX-DbFWAPvX.js.gz +0 -0
  82. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CijqM5Xa.js → cynefinDiagram-5FMLGOSQ-Ca0IKRdH.js} +1 -1
  83. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Ca0IKRdH.js.br +0 -0
  84. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-Ca0IKRdH.js.gz +0 -0
  85. package/dist/client/assets/{dagre-GXQ25YYZ-DCLINHGD.js → dagre-GXQ25YYZ-D_5uEMjJ.js} +1 -1
  86. package/dist/client/assets/dagre-GXQ25YYZ-D_5uEMjJ.js.br +0 -0
  87. package/dist/client/assets/dagre-GXQ25YYZ-D_5uEMjJ.js.gz +0 -0
  88. package/dist/client/assets/{diagram-S7CK7UJ4-D-JvTWtL.js → diagram-S7CK7UJ4-DWVpndRP.js} +1 -1
  89. package/dist/client/assets/diagram-S7CK7UJ4-DWVpndRP.js.br +0 -0
  90. package/dist/client/assets/diagram-S7CK7UJ4-DWVpndRP.js.gz +0 -0
  91. package/dist/client/assets/{diagram-UQ7AKVKN-BT01Oee8.js → diagram-UQ7AKVKN-DmYTya3w.js} +1 -1
  92. package/dist/client/assets/diagram-UQ7AKVKN-DmYTya3w.js.br +0 -0
  93. package/dist/client/assets/diagram-UQ7AKVKN-DmYTya3w.js.gz +0 -0
  94. package/dist/client/assets/{diagram-VSXAHHWV-C_j4_Ldm.js → diagram-VSXAHHWV-BEA6kqNj.js} +1 -1
  95. package/dist/client/assets/diagram-VSXAHHWV-BEA6kqNj.js.br +0 -0
  96. package/dist/client/assets/diagram-VSXAHHWV-BEA6kqNj.js.gz +0 -0
  97. package/dist/client/assets/{diagram-VX7I27RA-DY62ObO9.js → diagram-VX7I27RA-CU9vP0P0.js} +1 -1
  98. package/dist/client/assets/diagram-VX7I27RA-CU9vP0P0.js.br +0 -0
  99. package/dist/client/assets/diagram-VX7I27RA-CU9vP0P0.js.gz +0 -0
  100. package/dist/client/assets/{diagram-Z3DM3KII-TnINooIy.js → diagram-Z3DM3KII-B_GvE2g-.js} +1 -1
  101. package/dist/client/assets/diagram-Z3DM3KII-B_GvE2g-.js.br +0 -0
  102. package/dist/client/assets/diagram-Z3DM3KII-B_GvE2g-.js.gz +0 -0
  103. package/dist/client/assets/{ebnfDiagram-PWID7BFC-BahaUYTh.js → ebnfDiagram-PWID7BFC-BLrPQuLE.js} +1 -1
  104. package/dist/client/assets/ebnfDiagram-PWID7BFC-BLrPQuLE.js.br +0 -0
  105. package/dist/client/assets/ebnfDiagram-PWID7BFC-BLrPQuLE.js.gz +0 -0
  106. package/dist/client/assets/{erDiagram-RLTQ6QDP-DNiKhK5j.js → erDiagram-RLTQ6QDP-DlXgoyZ-.js} +1 -1
  107. package/dist/client/assets/erDiagram-RLTQ6QDP-DlXgoyZ-.js.br +0 -0
  108. package/dist/client/assets/erDiagram-RLTQ6QDP-DlXgoyZ-.js.gz +0 -0
  109. package/dist/client/assets/{flowDiagram-HODETNUW-x9c9CGXD.js → flowDiagram-HODETNUW-fPHWxEDZ.js} +1 -1
  110. package/dist/client/assets/flowDiagram-HODETNUW-fPHWxEDZ.js.br +0 -0
  111. package/dist/client/assets/flowDiagram-HODETNUW-fPHWxEDZ.js.gz +0 -0
  112. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-BofL5Lxs.js → ganttDiagram-EL5Y4UJY-B8MOv6ug.js} +1 -1
  113. package/dist/client/assets/ganttDiagram-EL5Y4UJY-B8MOv6ug.js.br +0 -0
  114. package/dist/client/assets/ganttDiagram-EL5Y4UJY-B8MOv6ug.js.gz +0 -0
  115. package/dist/client/assets/{gestureArbiter-Cy5v_ZUL.js → gestureArbiter-JW8vcVRm.js} +1 -1
  116. package/dist/client/assets/gestureArbiter-JW8vcVRm.js.br +0 -0
  117. package/dist/client/assets/gestureArbiter-JW8vcVRm.js.gz +0 -0
  118. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-CHYuiGg1.js → gitGraphDiagram-WWUBYQGX-lH9gSoFx.js} +1 -1
  119. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-lH9gSoFx.js.br +0 -0
  120. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-lH9gSoFx.js.gz +0 -0
  121. package/dist/client/assets/{infoDiagram-27XIBGKW-mfFgE6OE.js → infoDiagram-27XIBGKW-CSG29Ty_.js} +1 -1
  122. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-CiCmFa95.js → ishikawaDiagram-5VMMS53U-CcwSE5qV.js} +1 -1
  123. package/dist/client/assets/ishikawaDiagram-5VMMS53U-CcwSE5qV.js.br +0 -0
  124. package/dist/client/assets/ishikawaDiagram-5VMMS53U-CcwSE5qV.js.gz +0 -0
  125. package/dist/client/assets/{journeyDiagram-3NMN7TZE-CVnBzo7n.js → journeyDiagram-3NMN7TZE-CXFUeP2c.js} +1 -1
  126. package/dist/client/assets/journeyDiagram-3NMN7TZE-CXFUeP2c.js.br +0 -0
  127. package/dist/client/assets/journeyDiagram-3NMN7TZE-CXFUeP2c.js.gz +0 -0
  128. package/dist/client/assets/{kanban-definition-UXKFOSKX-3-zdGxI0.js → kanban-definition-UXKFOSKX-KEHwljd2.js} +1 -1
  129. package/dist/client/assets/kanban-definition-UXKFOSKX-KEHwljd2.js.br +0 -0
  130. package/dist/client/assets/kanban-definition-UXKFOSKX-KEHwljd2.js.gz +0 -0
  131. package/dist/client/assets/{layout-DnB4RHWA.js → layout-DqYx0MnO.js} +1 -1
  132. package/dist/client/assets/layout-DqYx0MnO.js.br +0 -0
  133. package/dist/client/assets/layout-DqYx0MnO.js.gz +0 -0
  134. package/dist/client/assets/{linear-B_lli4BL.js → linear-BnG3XaJW.js} +1 -1
  135. package/dist/client/assets/linear-BnG3XaJW.js.br +0 -0
  136. package/dist/client/assets/linear-BnG3XaJW.js.gz +0 -0
  137. package/dist/client/assets/{mermaid.core-ClemZ-kc.js → mermaid.core-Ce00rdM6.js} +5 -5
  138. package/dist/client/assets/mermaid.core-Ce00rdM6.js.br +0 -0
  139. package/dist/client/assets/mermaid.core-Ce00rdM6.js.gz +0 -0
  140. package/dist/client/assets/{mindmap-definition-YA3MSWOX-hXDufapN.js → mindmap-definition-YA3MSWOX-D0hOW4SY.js} +1 -1
  141. package/dist/client/assets/mindmap-definition-YA3MSWOX-D0hOW4SY.js.br +0 -0
  142. package/dist/client/assets/mindmap-definition-YA3MSWOX-D0hOW4SY.js.gz +0 -0
  143. package/dist/client/assets/{passwordBootstrap-c-hmAGCQ.js → passwordBootstrap-Bncb8qF_.js} +1 -1
  144. package/dist/client/assets/passwordBootstrap-Bncb8qF_.js.br +0 -0
  145. package/dist/client/assets/passwordBootstrap-Bncb8qF_.js.gz +0 -0
  146. package/dist/client/assets/{pegDiagram-XKGWAZYB-yb1lAfHR.js → pegDiagram-XKGWAZYB-UKp9t91x.js} +1 -1
  147. package/dist/client/assets/pegDiagram-XKGWAZYB-UKp9t91x.js.br +0 -0
  148. package/dist/client/assets/pegDiagram-XKGWAZYB-UKp9t91x.js.gz +0 -0
  149. package/dist/client/assets/{pieDiagram-E7YTZNPT-lVC2DWY_.js → pieDiagram-E7YTZNPT-Dif97wbH.js} +1 -1
  150. package/dist/client/assets/pieDiagram-E7YTZNPT-Dif97wbH.js.br +0 -0
  151. package/dist/client/assets/pieDiagram-E7YTZNPT-Dif97wbH.js.gz +0 -0
  152. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-CeqeNP5C.js → quadrantDiagram-AXDQQJYC-BPyHtH91.js} +1 -1
  153. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BPyHtH91.js.br +0 -0
  154. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BPyHtH91.js.gz +0 -0
  155. package/dist/client/assets/{railroadDiagram-O6MQD6OU-BMEBmjF2.js → railroadDiagram-O6MQD6OU-Wleq9LmS.js} +1 -1
  156. package/dist/client/assets/railroadDiagram-O6MQD6OU-Wleq9LmS.js.br +0 -0
  157. package/dist/client/assets/railroadDiagram-O6MQD6OU-Wleq9LmS.js.gz +0 -0
  158. package/dist/client/assets/{rdpSession-D77Fs4-L.js → rdpSession-CobFMSUs.js} +1 -1
  159. package/dist/client/assets/rdpSession-CobFMSUs.js.br +0 -0
  160. package/dist/client/assets/rdpSession-CobFMSUs.js.gz +0 -0
  161. package/dist/client/assets/{referenceLongPress-xNXQRqYn.js → referenceLongPress-bZ0xp-v2.js} +1 -1
  162. package/dist/client/assets/referenceLongPress-bZ0xp-v2.js.br +0 -0
  163. package/dist/client/assets/referenceLongPress-bZ0xp-v2.js.gz +0 -0
  164. package/dist/client/assets/{requirementDiagram-BXWQKSXE-CRhmIdDq.js → requirementDiagram-BXWQKSXE-BL4pp0Ta.js} +1 -1
  165. package/dist/client/assets/requirementDiagram-BXWQKSXE-BL4pp0Ta.js.br +0 -0
  166. package/dist/client/assets/requirementDiagram-BXWQKSXE-BL4pp0Ta.js.gz +0 -0
  167. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-CZZPxGRu.js → sankeyDiagram-P5KCCOFB-CV1cKx37.js} +1 -1
  168. package/dist/client/assets/sankeyDiagram-P5KCCOFB-CV1cKx37.js.br +0 -0
  169. package/dist/client/assets/sankeyDiagram-P5KCCOFB-CV1cKx37.js.gz +0 -0
  170. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-CvKYTCaL.js → sequenceDiagram-WJ2MYXX4-BF2InLwV.js} +1 -1
  171. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BF2InLwV.js.br +0 -0
  172. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BF2InLwV.js.gz +0 -0
  173. package/dist/client/assets/{sizeCapture-INFHLROL-DpMFgVd8.js → sizeCapture-INFHLROL-CldUyStR.js} +1 -1
  174. package/dist/client/assets/sizeCapture-INFHLROL-CldUyStR.js.br +0 -0
  175. package/dist/client/assets/sizeCapture-INFHLROL-CldUyStR.js.gz +0 -0
  176. package/dist/client/assets/{sparkles-BBYDWqCu.js → sparkles-Cg-yPdE2.js} +1 -1
  177. package/dist/client/assets/{stateDiagram-D77RDMKH-BUCKWh69.js → stateDiagram-D77RDMKH-CByehbEd.js} +1 -1
  178. package/dist/client/assets/stateDiagram-D77RDMKH-CByehbEd.js.br +0 -0
  179. package/dist/client/assets/stateDiagram-D77RDMKH-CByehbEd.js.gz +0 -0
  180. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CTcrJ-w0.js +1 -0
  181. package/dist/client/assets/{swimlanes-42K2YHIH-Du84aO1l.js → swimlanes-42K2YHIH--1_7YTL4.js} +1 -1
  182. package/dist/client/assets/swimlanes-42K2YHIH--1_7YTL4.js.br +0 -0
  183. package/dist/client/assets/swimlanes-42K2YHIH--1_7YTL4.js.gz +0 -0
  184. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-RSMV9AhO.js +8 -0
  185. package/dist/client/assets/{timeline-definition-24CTP7MA-94wGyYVM.js → timeline-definition-24CTP7MA-CXfXJFK6.js} +1 -1
  186. package/dist/client/assets/timeline-definition-24CTP7MA-CXfXJFK6.js.br +0 -0
  187. package/dist/client/assets/timeline-definition-24CTP7MA-CXfXJFK6.js.gz +0 -0
  188. package/dist/client/assets/{triangle-alert-BFCgLlxS.js → triangle-alert-CbMRJ6sx.js} +1 -1
  189. package/dist/client/assets/{vennDiagram-4TSXK5OY-C74llwIj.js → vennDiagram-4TSXK5OY-byr6UQpL.js} +1 -1
  190. package/dist/client/assets/vennDiagram-4TSXK5OY-byr6UQpL.js.br +0 -0
  191. package/dist/client/assets/vennDiagram-4TSXK5OY-byr6UQpL.js.gz +0 -0
  192. package/dist/client/assets/{videoCompression-b96RKMFE.js → videoCompression-ivJAy00d.js} +1 -1
  193. package/dist/client/assets/videoCompression-ivJAy00d.js.br +0 -0
  194. package/dist/client/assets/videoCompression-ivJAy00d.js.gz +0 -0
  195. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-DVdgFaBs.js → wardleyDiagram-VM6X3IG4-FJduk-7s.js} +1 -1
  196. package/dist/client/assets/wardleyDiagram-VM6X3IG4-FJduk-7s.js.br +0 -0
  197. package/dist/client/assets/wardleyDiagram-VM6X3IG4-FJduk-7s.js.gz +0 -0
  198. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-BmUxPbqk.js → xychartDiagram-S5SC5T6Z-Ct1fBPFS.js} +1 -1
  199. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Ct1fBPFS.js.br +0 -0
  200. package/dist/client/assets/xychartDiagram-S5SC5T6Z-Ct1fBPFS.js.gz +0 -0
  201. package/dist/client/index.html +1 -1
  202. package/dist/client/index.html.br +0 -0
  203. package/dist/client/index.html.gz +0 -0
  204. package/dist/client/sw.js +1 -1
  205. package/dist/client/sw.js.br +0 -0
  206. package/dist/client/sw.js.gz +0 -0
  207. package/dist/server/agent/collaborationCli.js +77 -17
  208. package/dist/server/agent/collaborationPrompt.js +3 -1
  209. package/dist/server/agent/collaborationProtocol.js +1 -1
  210. package/dist/server/agent/collaborationRoutes.js +7 -1
  211. package/dist/server/agent/collaborationStore.js +86 -11
  212. package/dist/server/agent/collaborationTaskService.js +2 -0
  213. package/dist/server/agent/integrationCli.js +52 -6
  214. package/dist/server/agent/integrationServer.js +99 -9
  215. package/dist/server/agent/integrationSessions.js +226 -18
  216. package/dist/server/agent/integrationStore.js +1 -1
  217. package/dist/server/agent/sessionLifecycle.js +81 -0
  218. package/dist/server/agent/sessionResumeConfiguration.js +32 -0
  219. package/dist/server/cli.js +1 -1
  220. package/dist/server/routes/terminal.js +75 -1
  221. package/docs/integrations.md +226 -6
  222. package/package.json +1 -1
  223. package/runtime-manifest.json +3 -3
  224. package/dist/client/assets/App-cagV-flM.js.br +0 -0
  225. package/dist/client/assets/App-cagV-flM.js.gz +0 -0
  226. package/dist/client/assets/ArchitecturePanel-TdKPja7L.js.br +0 -0
  227. package/dist/client/assets/ArchitecturePanel-TdKPja7L.js.gz +0 -0
  228. package/dist/client/assets/ChangeWalkthroughPanel-DOj40Srm.js.br +0 -0
  229. package/dist/client/assets/ChangeWalkthroughPanel-DOj40Srm.js.gz +0 -0
  230. package/dist/client/assets/CollaborationMainWorkspace-C5Tcos5l.js.br +0 -0
  231. package/dist/client/assets/CollaborationMainWorkspace-C5Tcos5l.js.gz +0 -0
  232. package/dist/client/assets/CollaborationTaskContent-DQpH2HIj.js +0 -2
  233. package/dist/client/assets/ComputerControlView-C2sOFyuA.js.br +0 -0
  234. package/dist/client/assets/ComputerControlView-C2sOFyuA.js.gz +0 -0
  235. package/dist/client/assets/DagPlayground-DFfVJW00.js.br +0 -0
  236. package/dist/client/assets/DagPlayground-DFfVJW00.js.gz +0 -0
  237. package/dist/client/assets/DiffLab-hoj8gwUi.js.br +0 -0
  238. package/dist/client/assets/DiffLab-hoj8gwUi.js.gz +0 -0
  239. package/dist/client/assets/DiffReview-Cn_PmN-9.js.br +0 -0
  240. package/dist/client/assets/DiffReview-Cn_PmN-9.js.gz +0 -0
  241. package/dist/client/assets/DiffReviewLab-DycgD40E.js.br +0 -0
  242. package/dist/client/assets/DiffReviewLab-DycgD40E.js.gz +0 -0
  243. package/dist/client/assets/DiffViewer-DRnuph7U.js.br +0 -0
  244. package/dist/client/assets/DiffViewer-DRnuph7U.js.gz +0 -0
  245. package/dist/client/assets/ModelPreview-Dvw-UtQQ.js.br +0 -0
  246. package/dist/client/assets/ModelPreview-Dvw-UtQQ.js.gz +0 -0
  247. package/dist/client/assets/RightSidebar-CnnU7Ffp.js.br +0 -0
  248. package/dist/client/assets/abnfDiagram-VCTEODGH-BQVIT5gD.js.br +0 -0
  249. package/dist/client/assets/abnfDiagram-VCTEODGH-BQVIT5gD.js.gz +0 -0
  250. package/dist/client/assets/app-CXvK8WZg.js.br +0 -0
  251. package/dist/client/assets/app-CXvK8WZg.js.gz +0 -0
  252. package/dist/client/assets/arc-DZG1NzUc.js.br +0 -0
  253. package/dist/client/assets/arc-DZG1NzUc.js.gz +0 -0
  254. package/dist/client/assets/architectureDiagram-5GKGNRK7-Bv15lJGE.js.br +0 -0
  255. package/dist/client/assets/architectureDiagram-5GKGNRK7-Bv15lJGE.js.gz +0 -0
  256. package/dist/client/assets/blockDiagram-I7D4REHJ-DShw-lH7.js.br +0 -0
  257. package/dist/client/assets/blockDiagram-I7D4REHJ-DShw-lH7.js.gz +0 -0
  258. package/dist/client/assets/c4Diagram-7LVT6UL2-BN6SsyOM.js.br +0 -0
  259. package/dist/client/assets/c4Diagram-7LVT6UL2-BN6SsyOM.js.gz +0 -0
  260. package/dist/client/assets/channel-YDx-Ycsw.js +0 -1
  261. package/dist/client/assets/chunk-F27PBJKO-BcpMMU4P.js.br +0 -0
  262. package/dist/client/assets/chunk-F27PBJKO-BcpMMU4P.js.gz +0 -0
  263. package/dist/client/assets/chunk-IMKFNOWR-cWvd5gJk.js.br +0 -0
  264. package/dist/client/assets/chunk-IMKFNOWR-cWvd5gJk.js.gz +0 -0
  265. package/dist/client/assets/chunk-SVP7TREG-Bj8gUFc2.js.br +0 -0
  266. package/dist/client/assets/chunk-SVP7TREG-Bj8gUFc2.js.gz +0 -0
  267. package/dist/client/assets/chunk-TICWLB2K-W1cGkC4s.js.br +0 -0
  268. package/dist/client/assets/chunk-TICWLB2K-W1cGkC4s.js.gz +0 -0
  269. package/dist/client/assets/classDiagram-ZZMXUADV-qeb16N1g.js +0 -1
  270. package/dist/client/assets/classDiagram-v2-VYDZK3BY-qeb16N1g.js +0 -1
  271. package/dist/client/assets/cose-bilkent-JH36ORCC-CK0EaMQY.js.br +0 -0
  272. package/dist/client/assets/cose-bilkent-JH36ORCC-CK0EaMQY.js.gz +0 -0
  273. package/dist/client/assets/cynefin-OW5HDTMX-C-7tsMpt.js.br +0 -0
  274. package/dist/client/assets/cynefin-OW5HDTMX-C-7tsMpt.js.gz +0 -0
  275. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CijqM5Xa.js.br +0 -0
  276. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CijqM5Xa.js.gz +0 -0
  277. package/dist/client/assets/dagre-GXQ25YYZ-DCLINHGD.js.br +0 -0
  278. package/dist/client/assets/dagre-GXQ25YYZ-DCLINHGD.js.gz +0 -0
  279. package/dist/client/assets/diagram-S7CK7UJ4-D-JvTWtL.js.br +0 -0
  280. package/dist/client/assets/diagram-S7CK7UJ4-D-JvTWtL.js.gz +0 -0
  281. package/dist/client/assets/diagram-UQ7AKVKN-BT01Oee8.js.br +0 -0
  282. package/dist/client/assets/diagram-UQ7AKVKN-BT01Oee8.js.gz +0 -0
  283. package/dist/client/assets/diagram-VSXAHHWV-C_j4_Ldm.js.br +0 -0
  284. package/dist/client/assets/diagram-VSXAHHWV-C_j4_Ldm.js.gz +0 -0
  285. package/dist/client/assets/diagram-VX7I27RA-DY62ObO9.js.br +0 -0
  286. package/dist/client/assets/diagram-VX7I27RA-DY62ObO9.js.gz +0 -0
  287. package/dist/client/assets/diagram-Z3DM3KII-TnINooIy.js.br +0 -0
  288. package/dist/client/assets/diagram-Z3DM3KII-TnINooIy.js.gz +0 -0
  289. package/dist/client/assets/ebnfDiagram-PWID7BFC-BahaUYTh.js.br +0 -0
  290. package/dist/client/assets/ebnfDiagram-PWID7BFC-BahaUYTh.js.gz +0 -0
  291. package/dist/client/assets/erDiagram-RLTQ6QDP-DNiKhK5j.js.br +0 -0
  292. package/dist/client/assets/erDiagram-RLTQ6QDP-DNiKhK5j.js.gz +0 -0
  293. package/dist/client/assets/flowDiagram-HODETNUW-x9c9CGXD.js.br +0 -0
  294. package/dist/client/assets/flowDiagram-HODETNUW-x9c9CGXD.js.gz +0 -0
  295. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BofL5Lxs.js.br +0 -0
  296. package/dist/client/assets/ganttDiagram-EL5Y4UJY-BofL5Lxs.js.gz +0 -0
  297. package/dist/client/assets/gestureArbiter-Cy5v_ZUL.js.br +0 -0
  298. package/dist/client/assets/gestureArbiter-Cy5v_ZUL.js.gz +0 -0
  299. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CHYuiGg1.js.br +0 -0
  300. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CHYuiGg1.js.gz +0 -0
  301. package/dist/client/assets/ishikawaDiagram-5VMMS53U-CiCmFa95.js.br +0 -0
  302. package/dist/client/assets/ishikawaDiagram-5VMMS53U-CiCmFa95.js.gz +0 -0
  303. package/dist/client/assets/journeyDiagram-3NMN7TZE-CVnBzo7n.js.br +0 -0
  304. package/dist/client/assets/journeyDiagram-3NMN7TZE-CVnBzo7n.js.gz +0 -0
  305. package/dist/client/assets/kanban-definition-UXKFOSKX-3-zdGxI0.js.br +0 -0
  306. package/dist/client/assets/kanban-definition-UXKFOSKX-3-zdGxI0.js.gz +0 -0
  307. package/dist/client/assets/layout-DnB4RHWA.js.br +0 -0
  308. package/dist/client/assets/layout-DnB4RHWA.js.gz +0 -0
  309. package/dist/client/assets/linear-B_lli4BL.js.br +0 -0
  310. package/dist/client/assets/linear-B_lli4BL.js.gz +0 -0
  311. package/dist/client/assets/mermaid.core-ClemZ-kc.js.br +0 -0
  312. package/dist/client/assets/mermaid.core-ClemZ-kc.js.gz +0 -0
  313. package/dist/client/assets/mindmap-definition-YA3MSWOX-hXDufapN.js.br +0 -0
  314. package/dist/client/assets/mindmap-definition-YA3MSWOX-hXDufapN.js.gz +0 -0
  315. package/dist/client/assets/passwordBootstrap-c-hmAGCQ.js.br +0 -0
  316. package/dist/client/assets/passwordBootstrap-c-hmAGCQ.js.gz +0 -0
  317. package/dist/client/assets/pegDiagram-XKGWAZYB-yb1lAfHR.js.br +0 -0
  318. package/dist/client/assets/pegDiagram-XKGWAZYB-yb1lAfHR.js.gz +0 -0
  319. package/dist/client/assets/pieDiagram-E7YTZNPT-lVC2DWY_.js.br +0 -0
  320. package/dist/client/assets/pieDiagram-E7YTZNPT-lVC2DWY_.js.gz +0 -0
  321. package/dist/client/assets/quadrantDiagram-AXDQQJYC-CeqeNP5C.js.br +0 -0
  322. package/dist/client/assets/quadrantDiagram-AXDQQJYC-CeqeNP5C.js.gz +0 -0
  323. package/dist/client/assets/railroadDiagram-O6MQD6OU-BMEBmjF2.js.br +0 -0
  324. package/dist/client/assets/railroadDiagram-O6MQD6OU-BMEBmjF2.js.gz +0 -0
  325. package/dist/client/assets/rdpSession-D77Fs4-L.js.br +0 -0
  326. package/dist/client/assets/rdpSession-D77Fs4-L.js.gz +0 -0
  327. package/dist/client/assets/referenceLongPress-xNXQRqYn.js.br +0 -0
  328. package/dist/client/assets/referenceLongPress-xNXQRqYn.js.gz +0 -0
  329. package/dist/client/assets/requirementDiagram-BXWQKSXE-CRhmIdDq.js.br +0 -0
  330. package/dist/client/assets/requirementDiagram-BXWQKSXE-CRhmIdDq.js.gz +0 -0
  331. package/dist/client/assets/sankeyDiagram-P5KCCOFB-CZZPxGRu.js.br +0 -0
  332. package/dist/client/assets/sankeyDiagram-P5KCCOFB-CZZPxGRu.js.gz +0 -0
  333. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CvKYTCaL.js.br +0 -0
  334. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-CvKYTCaL.js.gz +0 -0
  335. package/dist/client/assets/sizeCapture-INFHLROL-DpMFgVd8.js.br +0 -0
  336. package/dist/client/assets/sizeCapture-INFHLROL-DpMFgVd8.js.gz +0 -0
  337. package/dist/client/assets/stateDiagram-D77RDMKH-BUCKWh69.js.br +0 -0
  338. package/dist/client/assets/stateDiagram-D77RDMKH-BUCKWh69.js.gz +0 -0
  339. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CrhJVriY.js +0 -1
  340. package/dist/client/assets/swimlanes-42K2YHIH-Du84aO1l.js.br +0 -0
  341. package/dist/client/assets/swimlanes-42K2YHIH-Du84aO1l.js.gz +0 -0
  342. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-bsc39ya3.js +0 -8
  343. package/dist/client/assets/timeline-definition-24CTP7MA-94wGyYVM.js.br +0 -0
  344. package/dist/client/assets/timeline-definition-24CTP7MA-94wGyYVM.js.gz +0 -0
  345. package/dist/client/assets/vennDiagram-4TSXK5OY-C74llwIj.js.br +0 -0
  346. package/dist/client/assets/vennDiagram-4TSXK5OY-C74llwIj.js.gz +0 -0
  347. package/dist/client/assets/videoCompression-b96RKMFE.js.br +0 -0
  348. package/dist/client/assets/videoCompression-b96RKMFE.js.gz +0 -0
  349. package/dist/client/assets/wardleyDiagram-VM6X3IG4-DVdgFaBs.js.br +0 -0
  350. package/dist/client/assets/wardleyDiagram-VM6X3IG4-DVdgFaBs.js.gz +0 -0
  351. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BmUxPbqk.js.br +0 -0
  352. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BmUxPbqk.js.gz +0 -0
@@ -52,12 +52,41 @@ export const COLLAB_HELP = `td collab — durable messages; no agent-specific ho
52
52
  ── 后台集成(独立凭据;本机 Unix socket) ──
53
53
  td integration help(管理授权与启动配置)
54
54
  td collab --principal <id> capabilities
55
+ send <成员id> "执行正文" --idempotency-key <key>(权限 message.send;普通消息,不创建看板任务)
56
+ reply <原消息id> "追问正文" --idempotency-key <key>(回到原成员、原话题)
57
+ message get <id> | message list [--thread <id>] [--after-id <id>] [--limit 1..200]
58
+ (权限 message.read;list 返回保留记录,next_after_id/has_more 用于分页)
59
+ 以上命令均需前缀 td collab --principal <id>;send/reply 支持 --file/--stdin。
60
+ 来源可附 --source <name> --external-actor '<JSON object>' --external-message-id <id>。
61
+ Agent 在终端收到消息后:td collab --session <自己的TD成员id> reply <原消息id> "答复" --response-kind result --idempotency-key <key>
62
+ ack=明确回复收到;progress=原文进展;result=原文结果。后台回复 status=stored,仅表示持久保存。
63
+ 回复通过 message.reply 事件主动推送;payload.reply_to 关联原消息,response_kind 标记回复类型。
55
64
  events subscribe --consumer <name> --jsonl(主动推送,默认持续运行)
56
65
  events ack <cursor> --consumer <name>(仅声明已持久接收)
66
+ events 需权限 events.read;按 event_id 去重,落盘后 ACK 连续前缀;订阅读到不等于 ACK。
67
+ 断线自动重连,同 consumer 从已 ACK 处重放;保留缺口 EVENT_RETENTION_GAP 停止,需对账后换 consumer。
68
+ 消息历史只覆盖仍保留记录;不存在的 after-id 返回 MESSAGE_NOT_FOUND,不能据此声称历史完整。
57
69
  session create --group <id> --launch-profile <id> --cwd <absolute-path> --idempotency-key <key>
58
70
  session get <id> | session restore <id> --idempotency-key <key>
71
+ session release <id> --generation <session-get-generation> --idempotency-key <key>
72
+ 权限 session.release;仅原 Agent 已退出、精确空 shell、无 pending 消息时释放实际运行承载。
73
+ released: runtime_present=false、terminal_binding=null;保留逻辑 ID/原生 UUID/原消息,restore 后可继续 reply。
74
+ released 上 send/reply 返回 SESSION_RELEASED,不入队、不隐式恢复;业务先持久接收,再明确 restore。
75
+ 释放/恢复最低 CLI/实际服务 1.4.309;检查 session_runtime_release/stable_session_identity/session_lifecycle_generation。
76
+ generation 每次实际 restore/release/管理员兼容确认递增;旧 generation 拒绝,运行中精确 restore 不递增。
77
+ 重用同幂等键回读原操作结果,不能用于新一轮恢复;用 session get 查询当前状态。
78
+ 旧配置须管理员 td integration resume-configuration <id> 查看,再 confirm-resume 显式确认当前指纹。
79
+ 状态 starting/restoring/binding_pending/ready/failed/releasing/released;runtime_present=null 表示无法确认。
80
+ 失败码:INVALID_SESSION_GENERATION、SESSION_GENERATION_MISMATCH、SESSION_RELEASE_UNSUPPORTED;
81
+ SESSION_PENDING_MESSAGES/SESSION_STILL_RUNNING/SESSION_OPERATION_IN_PROGRESS 表示应先处理原现场;
82
+ SESSION_NATIVE_OPERATION_IN_PROGRESS、SESSION_SCOPE_CONFLICT、SESSION_IDENTITY_MISMATCH 拒绝并发或身份/归属冲突;
83
+ SESSION_RELEASE_FAILED、SESSION_STORAGE_FULL 保留原记录,不表示回收成功;
84
+ RESUME_CONFIGURATION_CHANGED/RESUME_CONFIGURATION_UNCONFIRMED 拒绝静默替换配置;OPERATION_OUTCOME_UNCONFIRMED 保留记录待核对。
59
85
  task respond <task-id> --attempt <attempt-id> --to-event <comment-event-id> --content <答复>
60
86
  凭据通过 TERMDOCK_INTEGRATION_CREDENTIAL_FILE 注入,不通过命令行传 token。
87
+ 消息接入最低 CLI/实际服务均为 1.4.308;capabilities 回读双方版本与 integration_message_send/reply/history。
88
+ principal 只操作获授权本机组;不能冒充用户/成员或用户验收。普通消息不需要 task/accepted。
89
+ 任务命令适用于需要任务闭环的流程;message.send/read 与 task 权限独立。
61
90
  已验收任务可追加纯咨询;respond 保留当前结果与验收,revise 是修改要求。
62
91
  ── 常用 ──
63
92
  status (who is in my groups, their names, roles, observed activity.
@@ -145,11 +174,11 @@ Message limit: ${COLLAB_LIMITS.message_bytes} UTF-8 bytes; metadata: ${COLLAB_LI
145
174
  Idempotency retention: 7 days. Terminal delivery, ACK and result never imply each other.
146
175
  Delivery semantics: delivered = written to the terminal — never proof of
147
176
  reading or task completion; a timeout stops waiting, it does not cancel delivery.
148
- The service cannot see whether the recipient's agent consumed a message. To
149
- judge for yourself, use --wait-until delivered and read the recipient-screen
150
- snapshot in the receipt: if your message is not visible on that screen, send it
151
- again (a fresh send is a new message and will be written again); if it is
152
- visible but unanswered, the recipient may simply not have started yet.`;
177
+ The service cannot see whether the recipient's agent consumed a message.
178
+ Use explicit replies as evidence. A screen snapshot is only an observation;
179
+ do not automatically resend a delivered message because it is absent from a snapshot.
180
+ stored = durable reply to a nonterminal principal; terminal delivery waits do not apply.
181
+ Idempotent retries reuse the same key and payload; a fresh key creates a new message.`;
153
182
  /** One roster row: prefer the member's human name, keep the full session id
154
183
  * reachable for role set/unset targeting, mark unset members explicitly. */
155
184
  function roleLine(member, role) {
@@ -157,7 +186,7 @@ function roleLine(member, role) {
157
186
  return role ? `- ${label}:${role}` : `- ${label}(未设置)`;
158
187
  }
159
188
  const BOOLEAN_OPTIONS = new Set(['json', 'jsonl', 'text', 'follow', 'stdin', 'receipt-only', 'confirm', 'raw', 'help', 'no-rules', 'managed', 'shared-directory', 'integration']);
160
- const VALUE_OPTIONS = new Set(['purpose', 'principal', 'source', 'external-actor', 'external-message-id', 'launch-profile', 'to-event', 'decision', 'work-type', 'summary', 'verdict', 'reviewers', 'title', 'content', 'constraints', 'acceptance', 'assignee', 'coordinator', 'parent', 'depends-on', 'attempt', 'status', 'artifact', 'options', 'revision', 'evidence', 'session', 'group', 'thread', 'idempotency-key', 'file', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'task-envelope', 'expires-at', 'since', 'after-id', 'cursor', 'consumer', 'limit', 'from', 'kind', 'name', 'cwd', 'task', 'pane', 'lines', 'if-version', 'origin']);
189
+ const VALUE_OPTIONS = new Set(['generation', 'purpose', 'principal', 'source', 'external-actor', 'external-message-id', 'launch-profile', 'to-event', 'decision', 'work-type', 'summary', 'verdict', 'reviewers', 'title', 'content', 'constraints', 'acceptance', 'assignee', 'coordinator', 'parent', 'depends-on', 'attempt', 'status', 'artifact', 'options', 'revision', 'evidence', 'session', 'group', 'thread', 'idempotency-key', 'file', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'task-envelope', 'expires-at', 'since', 'after-id', 'cursor', 'consumer', 'limit', 'from', 'kind', 'name', 'cwd', 'task', 'pane', 'lines', 'if-version', 'origin']);
161
190
  export function parseCollaborationCommand(argv) {
162
191
  const options = {};
163
192
  const positional = [];
@@ -223,12 +252,14 @@ export function parseCollaborationCommand(argv) {
223
252
  throw new Error('Unexpected integration arguments');
224
253
  if (action === 'events' && (!['subscribe', 'ack'].includes(command.operation ?? '') || (command.operation === 'ack' ? !command.target : Boolean(command.target))))
225
254
  throw new Error('Usage: events subscribe | events ack <cursor>');
226
- if (action === 'session' && (!['create', 'get', 'restore'].includes(command.operation ?? '') || (command.operation === 'create' ? Boolean(command.target) : !command.target)))
227
- throw new Error('Usage: session create|get|restore');
255
+ if (action === 'session' && (!['create', 'get', 'restore', 'release'].includes(command.operation ?? '') || (command.operation === 'create' ? Boolean(command.target) : !command.target)))
256
+ throw new Error('Usage: session create|get|restore|release');
228
257
  if (action === 'session' && command.operation !== 'get' && !options['idempotency-key'])
229
258
  throw new Error('Session mutation requires an explicit --idempotency-key');
259
+ if (action === 'session' && command.operation === 'release' && (!/^[1-9][0-9]*$/.test(String(options.generation ?? '')) || !Number.isSafeInteger(Number(options.generation))))
260
+ throw new Error('session release requires --generation from session get');
230
261
  const integrationFlags = action === 'events' ? command.operation === 'ack' ? ['consumer'] : ['consumer', 'group', 'timeout']
231
- : command.operation === 'create' ? ['group', 'launch-profile', 'cwd', 'idempotency-key'] : command.operation === 'restore' ? ['idempotency-key'] : [];
262
+ : command.operation === 'create' ? ['group', 'launch-profile', 'cwd', 'idempotency-key'] : command.operation === 'restore' ? ['idempotency-key'] : command.operation === 'release' ? ['idempotency-key', 'generation'] : [];
232
263
  for (const flag of Object.keys(options))
233
264
  if (!['principal', 'json', 'jsonl', 'text', 'help', ...integrationFlags].includes(flag))
234
265
  throw new Error(`--${flag} is not supported by ${action} ${command.operation}`);
@@ -250,6 +281,8 @@ export function parseCollaborationCommand(argv) {
250
281
  throw new Error(`${action} requires a target and message`);
251
282
  if ([Boolean(command.message), Boolean(options.file), Boolean(options.stdin)].filter(Boolean).length !== 1)
252
283
  throw new Error('Choose inline body, --file, or --stdin');
284
+ if (options.principal && !options['idempotency-key'])
285
+ throw new Error('Integration message mutation requires an explicit --idempotency-key');
253
286
  }
254
287
  else if (action === 'rules') {
255
288
  command.operation = positional.shift();
@@ -286,8 +319,15 @@ export function parseCollaborationCommand(argv) {
286
319
  else if (action === 'message') {
287
320
  command.operation = positional.shift();
288
321
  command.target = positional.shift();
289
- if (!['get', 'watch', 'confirm-shell'].includes(command.operation ?? '') || !command.target || positional.length)
290
- throw new Error('Usage: td collab message get|watch|confirm-shell <id>');
322
+ if (!['get', 'watch', 'confirm-shell', 'list'].includes(command.operation ?? '') || (command.operation === 'list' ? !options.principal || Boolean(command.target) : !command.target) || positional.length)
323
+ throw new Error('Usage: td collab message get|watch|confirm-shell <id>; --principal <id> message list');
324
+ if (command.operation === 'list') {
325
+ for (const flag of Object.keys(options))
326
+ if (!['principal', 'json', 'jsonl', 'text', 'group', 'thread', 'after-id', 'limit'].includes(flag))
327
+ throw new Error(`--${flag} is not supported by message list`);
328
+ }
329
+ else if (['group', 'thread', 'after-id', 'limit'].some(flag => options[flag] !== undefined))
330
+ throw new Error('History filters apply to --principal message list only');
291
331
  }
292
332
  else if (action === 'cursor') {
293
333
  command.operation = positional.shift();
@@ -357,18 +397,21 @@ export function parseCollaborationCommand(argv) {
357
397
  throw new Error(`Unexpected arguments for ${action}`);
358
398
  const allowed = new Set(['json', 'jsonl', 'text', 'help', 'session', 'principal']);
359
399
  const byAction = {
360
- events: ['consumer', 'group', 'timeout'], session: ['group', 'launch-profile', 'cwd', 'idempotency-key'],
400
+ events: ['consumer', 'group', 'timeout'], session: ['group', 'launch-profile', 'cwd', 'idempotency-key', 'generation'],
361
401
  task: ['purpose', 'to-event', 'decision', 'source', 'external-actor', 'external-message-id', 'metadata', 'work-type', 'summary', 'integration', 'managed', 'shared-directory', 'reviewers', 'verdict', 'group', 'title', 'content', 'constraints', 'acceptance', 'assignee', 'coordinator', 'parent', 'depends-on', 'attempt', 'status', 'artifact', 'options', 'revision', 'evidence', 'idempotency-key', 'file', 'stdin'],
362
402
  rules: ['file', 'stdin', 'if-version'], transport: ['file', 'origin'], group: ['file'], status: [], capabilities: [], rebind: ['pane'], help: [...BOOLEAN_OPTIONS, ...VALUE_OPTIONS],
363
403
  send: ['group', 'thread', 'idempotency-key', 'file', 'stdin', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'expires-at', 'kind'],
364
404
  handoff: ['group', 'thread', 'idempotency-key', 'file', 'stdin', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'expires-at'],
365
405
  reply: ['idempotency-key', 'file', 'stdin', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'task-envelope', 'expires-at'],
366
406
  inbox: ['raw', 'since', 'after-id', 'cursor', 'consumer', 'limit', 'from', 'group', 'thread', 'kind', 'response-kind', 'follow', 'timeout'],
367
- message: ['raw', 'receipt-only', 'no-rules', 'follow', 'wait-until', 'timeout', 'expect-reply'], cursor: ['consumer'],
407
+ message: ['raw', 'receipt-only', 'no-rules', 'follow', 'wait-until', 'timeout', 'expect-reply', 'group', 'thread', 'after-id', 'limit'], cursor: ['consumer'],
368
408
  add: [], remove: [], spawn: ['launch-profile', 'name', 'cwd', 'task'], role: [], rename: [], cleanup: ['confirm'], drive: ['lines', 'raw'],
369
409
  };
370
410
  for (const option of byAction[action])
371
411
  allowed.add(option);
412
+ if (options.principal && ['send', 'reply'].includes(action))
413
+ for (const option of ['source', 'external-actor', 'external-message-id'])
414
+ allowed.add(option);
372
415
  for (const option of Object.keys(options))
373
416
  if (!allowed.has(option))
374
417
  throw new Error(`--${option} is not supported by ${action}`);
@@ -394,7 +437,7 @@ export function waitSatisfied(receipt, stage, reply) {
394
437
  * machine reasons onto what the sender should do next, so a settled-but-
395
438
  * unconsumed message is not mistaken for a lost one (or a good one). */
396
439
  const DELIVERY_DIAGNOSTICS = {
397
- AGENT_CONSUME_UNCONFIRMED: '消息已写入对方终端,但未确认被对方消费;它可能尚未开始处理,可用 capture 查看当前屏幕或重发',
440
+ AGENT_CONSUME_UNCONFIRMED: '消息已写入对方终端,但未确认被对方消费;等待明确回复,不从终端快照推断消费或自动重发',
398
441
  SHELL_CONFIRMATION_REQUIRED: '目标当前是 shell,消息可能被当命令执行;确认请运行 message confirm-shell <id>',
399
442
  DELIVERY_IN_PROGRESS: '投递进行中(写入前的中间标记,正常会在数秒内推进为 delivered 或带原因的重试)',
400
443
  TERMINAL_WRITE_FAILED: '写入对方终端失败,会按重试间隔继续尝试',
@@ -430,17 +473,22 @@ export async function executeCollaborationCommand(command, context, io) {
430
473
  const output = (value) => {
431
474
  if (o.text) {
432
475
  let diagnosed = false;
433
- if (Array.isArray(value.messages))
476
+ if (Array.isArray(value.messages)) {
434
477
  for (const message of value.messages) {
435
478
  const fanIds = Array.isArray(message.fanOutIds) ? message.fanOutIds : [];
436
479
  const names = value.names ?? {};
437
- io.write(`[${message.responseKind ?? message.kind}] ${canonicalShortId(message.id)} from ${message.fromSessionId ?? 'user'}${fanIds.length ? ' · 群发' : ''}`);
480
+ io.write(`[${message.responseKind ?? message.kind}] ${canonicalShortId(message.id)} from ${message.fromSessionId ?? (message.integrationOrigin ? `principal:${message.integrationOrigin.integrationId}` : 'user')}${fanIds.length ? ' · 群发' : ''}`);
438
481
  // A fan-out dispatch names its sibling recipients so a broadcast is
439
482
  // never read as a one-to-one assignment; unknown ids stay raw.
440
483
  if (fanIds.length)
441
484
  io.write(`同时发给了:${fanIds.map((id) => names[id] ?? id).join('、')}`);
442
485
  io.write(message.content);
486
+ if (message.status === 'stored')
487
+ io.write('答复已持久保存;接收方是后台程序,不是终端。');
443
488
  }
489
+ if (value.next_after_id !== undefined)
490
+ io.write(`next_after_id=${value.next_after_id ?? ''} has_more=${value.has_more === true}(仅保留记录)`);
491
+ }
444
492
  else if (value.message) {
445
493
  // One line instead of the full rules text — the CLI is stateless so it
446
494
  // cannot diff versions across calls; --no-rules silences it entirely.
@@ -645,12 +693,24 @@ export async function executeCollaborationCommand(command, context, io) {
645
693
  return 0;
646
694
  }
647
695
  if (command.action === 'message') {
696
+ if (command.operation === 'list') {
697
+ const params = new URLSearchParams();
698
+ for (const key of ['group', 'thread', 'after-id', 'limit'])
699
+ if (o[key])
700
+ params.set(key, String(o[key]));
701
+ output(await request('GET', `/message?${params}`));
702
+ return 0;
703
+ }
648
704
  const route = `/message/${encodeURIComponent(command.target)}`;
649
705
  if (command.operation === 'confirm-shell') {
650
706
  output(await request('POST', `${route}/${command.operation}`));
651
707
  return 0;
652
708
  }
653
709
  receipt = await request('GET', `${route}?receipt_only=${Boolean(o['receipt-only'] || command.operation === 'watch')}&raw=${Boolean(o.raw)}`);
710
+ if (receipt.status === 'stored') {
711
+ output(receipt);
712
+ return 0;
713
+ }
654
714
  if (command.operation === 'get' && !o.follow && !o['wait-until'] && !o['expect-reply']) {
655
715
  output(receipt);
656
716
  return 0;
@@ -692,7 +752,7 @@ export async function executeCollaborationCommand(command, context, io) {
692
752
  // CLI receipts stay small even when the message is a large evidence package.
693
753
  delete receipt.messages;
694
754
  receipt.idempotency_key = idempotencyKey;
695
- if (receipt.task_recorded) {
755
+ if (receipt.task_recorded || receipt.integration_recorded) {
696
756
  output(receipt);
697
757
  return 0;
698
758
  }
@@ -89,7 +89,7 @@ export function formatCollaborationDelivery(input) {
89
89
  const sourceSession = message.fromSessionId ? sessionsById.get(message.fromSessionId) : null;
90
90
  const source = message.fromSessionId
91
91
  ? sanitizeCollaborationName(sourceSession?.name ?? message.fromSessionId)
92
- : '用户';
92
+ : message.integrationOrigin ? `后台接入 ${sanitizeCollaborationName(message.integrationOrigin.integrationId, 80)}` : '用户';
93
93
  const token = tokens.get(message.id) ?? message.id;
94
94
  const bytes = Buffer.byteLength(message.content);
95
95
  // A remote recipient's CLI cannot detect its identity (the session is not
@@ -103,6 +103,8 @@ export function formatCollaborationDelivery(input) {
103
103
  const fannedNames = (message.fanOutIds ?? [])
104
104
  .map((sessionId) => sanitizeCollaborationName(sessionsById.get(sessionId)?.name ?? sessionId));
105
105
  blocks.push(formatCollaborationMessage({ ...message, content: body }, source, fannedNames, fence, token));
106
+ if (message.integrationOrigin)
107
+ blocks.push(`此消息来自后台程序,使用原消息 id 回复:td collab --session ${input.targetSessionId} reply ${token} "答复正文" --response-kind result --idempotency-key <稳定唯一key> --text\n需要时用 --response-kind ack|progress 明确回复收到或进展;result 提交结果。回复持久保存为 stored 并主动推送给后台,不创建看板任务,不代表用户验收。`);
106
108
  }
107
109
  // The shell header names the group only when every block belongs to one;
108
110
  // a mixed batch drops the header entirely (no single-group claim to make).
@@ -143,4 +143,4 @@ export function extrasFromBody(body) {
143
143
  responseKind: body.response_kind, metadata: body.metadata,
144
144
  task: body.task, expiresAt: body.expires_at });
145
145
  }
146
- export const STATUS_RANK = { pending: 0, failed: 1, expired: 1, delivered: 2, read: 3 };
146
+ export const STATUS_RANK = { pending: 0, failed: 1, expired: 1, delivered: 2, read: 3, stored: 4 };
@@ -159,8 +159,9 @@ export function collaborationRoutes({ store, resolveSession, deliver, rebind, re
159
159
  background_task_records: true, task_purpose_configuration: true, explicit_execution_state: true,
160
160
  task_reply: 'Replies to task-linked user messages are stored in the task; status=stored is not a terminal delivery receipt' },
161
161
  routing: { background_recovery: true, explicit_rebind: Boolean(rebind), fixed_tmux_pane: true },
162
- statuses: ['pending', 'delivered', 'failed', 'expired'], queued_status: 'pending',
162
+ statuses: ['pending', 'delivered', 'failed', 'expired', 'stored'], queued_status: 'pending',
163
163
  semantics: { delivered: 'written to terminal; no application acknowledgement implied',
164
+ stored: 'durable reply to a nonterminal integration principal; never a terminal write',
164
165
  ack: 'explicit response_kind=ack', result: 'explicit response_kind=result; inspect task.status and evidence',
165
166
  snapshot: 'terminal observation, never proof of reading or task completion',
166
167
  timeout: 'stops waiting, does not cancel delivery',
@@ -208,6 +209,11 @@ export function collaborationRoutes({ store, resolveSession, deliver, rebind, re
208
209
  }));
209
210
  router.post('/reply', run(async (req, res, sessionId) => {
210
211
  const original = ownMessage(String(req.body.messageId ?? ''), sessionId, true);
212
+ if (original.integrationOrigin) {
213
+ const reply = store.storeIntegrationReply(original.id, sessionId, req.body.content, extrasFromBody(req.body));
214
+ res.json({ ok: true, ...store.receipt(reply.id), integration_recorded: true });
215
+ return;
216
+ }
211
217
  if (!original.fromSessionId) {
212
218
  const link = original.metadata?.termdockTask;
213
219
  if (link?.taskId && req.app.locals.collaborationTasks) {
@@ -45,6 +45,8 @@ export class CollaborationStore {
45
45
  drawId;
46
46
  onMessageQueued;
47
47
  changeListeners = new Set();
48
+ runtimeGuards = new Set();
49
+ registerRuntimeGuard(guard) { this.runtimeGuards.add(guard); return () => { this.runtimeGuards.delete(guard); }; }
48
50
  subscribe(listener) { this.changeListeners.add(listener); return () => { this.changeListeners.delete(listener); }; }
49
51
  snapshotMessages() { return structuredClone(this.document.messages); }
50
52
  peerActivity = new Map();
@@ -318,16 +320,27 @@ export class CollaborationStore {
318
320
  throw new Error('消息类型无效');
319
321
  const extras = validateExtras(input);
320
322
  const content = input.content;
323
+ if (typeof content !== 'string')
324
+ throw new CollaborationError('INVALID_MESSAGE', 'Message content must be text');
321
325
  if (Buffer.byteLength(content) > COLLAB_LIMITS.message_bytes)
322
326
  throw new CollaborationError('MESSAGE_TOO_LARGE', `Message exceeds ${COLLAB_LIMITS.message_bytes} UTF-8 bytes`, 413);
323
327
  if (!content.trim())
324
328
  throw new Error('消息不能为空');
329
+ for (const id of input.toSessionIds)
330
+ for (const guard of this.runtimeGuards)
331
+ guard(id);
325
332
  const recipients = Array.from(new Set(input.toSessionIds)).filter((id) => group.sessionIds.includes(id) && id !== input.fromSessionId);
326
333
  if (recipients.length === 0)
327
334
  throw new Error('没有有效的接收会话');
328
335
  const now = Date.now();
329
- const key = input.idempotencyKey ? JSON.stringify([input.groupId, input.fromSessionId, input.idempotencyKey]) : null;
330
- const hash = crypto.createHash('sha256').update(stableJson([recipients.slice().sort(), input.kind, content, input.threadId ?? null, input.replyTo ?? null, extras.responseKind, extras.metadata, extras.task, extras.expiresAt])).digest('hex');
336
+ const key = input.idempotencyKey ? JSON.stringify([input.groupId, input.integrationOrigin ? `principal:${input.integrationOrigin.integrationId}` : input.fromSessionId, input.idempotencyKey]) : null;
337
+ const payload = [recipients.slice().sort(), input.kind, content, input.threadId ?? null, input.replyTo ?? null, extras.responseKind, extras.metadata, extras.task, extras.expiresAt];
338
+ if (input.integrationOrigin) {
339
+ if (input.fromSessionId !== null || group.federated || !/^[a-zA-Z0-9._-]{1,80}$/.test(input.integrationOrigin.integrationId))
340
+ throw new CollaborationError('INVALID_INTEGRATION_MESSAGE', 'Integration messages require an authenticated local nonterminal source');
341
+ payload.push(input.integrationOrigin);
342
+ }
343
+ const hash = crypto.createHash('sha256').update(stableJson(payload)).digest('hex');
331
344
  const previous = key ? this.document.idempotency?.[key] : null;
332
345
  if (previous && previous.expiresAt > now) {
333
346
  if (previous.hash !== hash)
@@ -340,6 +353,7 @@ export class CollaborationStore {
340
353
  const taken = this.takenIds();
341
354
  const threadId = input.threadId?.trim() || newCollaborationId(taken, this.drawId);
342
355
  const messages = recipients.map((toSessionId) => ({
356
+ ...(input.integrationOrigin ? { integrationOrigin: structuredClone(input.integrationOrigin) } : {}),
343
357
  idempotencyKey: input.idempotencyKey, responseKind: extras.responseKind, metadata: extras.metadata, task: extras.task, expiresAt: extras.expiresAt,
344
358
  sequence: this.nextSequence(),
345
359
  id: newCollaborationId(taken, this.drawId), groupId: group.id, fromSessionId: input.fromSessionId,
@@ -353,10 +367,51 @@ export class CollaborationStore {
353
367
  this.document.messages.push(...messages);
354
368
  if (key)
355
369
  (this.document.idempotency ??= {})[key] = { hash, ids: messages.map((message) => message.id), expiresAt: now + COLLAB_LIMITS.idempotency_retention_ms };
356
- this.persist();
370
+ this.persist(!!input.integrationOrigin);
357
371
  queueMicrotask(() => this.onMessageQueued?.());
358
372
  return messages;
359
373
  }
374
+ /** An original recipient can persist a reply to a nonterminal integration.
375
+ * This is a stored fact, not a fake terminal write or a new task. */
376
+ storeIntegrationReply(originalId, sessionId, content, input) {
377
+ const original = this.getMessage(originalId), group = original && this.getGroup(original.groupId);
378
+ if (!original?.integrationOrigin || original.fromSessionId !== null || original.toSessionId !== sessionId
379
+ || !group || group.deleted || group.federated || !group.sessionIds.includes(sessionId))
380
+ throw new CollaborationError('NOT_MESSAGE_RECIPIENT', 'Only the original local recipient can reply to this integration message', 403);
381
+ const extras = validateExtras(input);
382
+ if (extras.task || extras.metadata?.termdockTask)
383
+ throw new CollaborationError('INVALID_INTEGRATION_REPLY', 'Ordinary integration replies cannot carry task envelopes');
384
+ if (!extras.idempotencyKey)
385
+ throw new CollaborationError('INVALID_IDEMPOTENCY_KEY', 'Integration replies require an idempotency key');
386
+ if (typeof content !== 'string' || !content.trim())
387
+ throw new CollaborationError('INVALID_MESSAGE', 'Reply content must not be empty');
388
+ if (Buffer.byteLength(content) > COLLAB_LIMITS.message_bytes)
389
+ throw new CollaborationError('MESSAGE_TOO_LARGE', 'Reply exceeds 1 MiB', 413);
390
+ if (extras.expiresAt !== undefined && extras.expiresAt !== null)
391
+ throw new CollaborationError('STORED_REPLY_CANNOT_EXPIRE', 'A persisted reply is not a pending delivery and cannot expire');
392
+ const principalId = original.integrationOrigin.integrationId;
393
+ const key = JSON.stringify([original.groupId, `integration-reply:${principalId}:${sessionId}`, extras.idempotencyKey]);
394
+ const hash = crypto.createHash('sha256').update(stableJson([original.id, content, extras.responseKind, extras.metadata, extras.task])).digest('hex');
395
+ const previous = this.document.idempotency?.[key];
396
+ if (previous && previous.expiresAt > Date.now()) {
397
+ if (previous.hash !== hash)
398
+ throw new CollaborationError('IDEMPOTENCY_CONFLICT', 'Reply key was already used with different content', 409);
399
+ const reply = this.getMessage(previous.ids[0]);
400
+ if (!reply)
401
+ throw new CollaborationError('IDEMPOTENCY_RECORD_GONE', 'Original reply is no longer retained', 409);
402
+ return reply;
403
+ }
404
+ const now = Date.now();
405
+ const reply = { ...extras, id: newCollaborationId(this.takenIds(), this.drawId), sequence: this.nextSequence(),
406
+ groupId: original.groupId, fromSessionId: sessionId, toSessionId: `principal:${principalId}`, toPrincipalId: principalId,
407
+ kind: 'reply', content, threadId: original.threadId, replyTo: original.id, status: 'stored', createdAt: now, storedAt: now, deliveredAt: null, readAt: null };
408
+ if (Buffer.byteLength(JSON.stringify(reply)) > COLLAB_LIMITS.wire_bytes)
409
+ throw new CollaborationError('MESSAGE_WIRE_TOO_LARGE', 'Encoded reply exceeds the wire limit', 413);
410
+ this.document.messages.push(reply);
411
+ (this.document.idempotency ??= {})[key] = { hash, ids: [reply.id], expiresAt: now + COLLAB_LIMITS.idempotency_retention_ms };
412
+ this.persist(true);
413
+ return reply;
414
+ }
360
415
  listMessages(groupId, limit = 200) {
361
416
  return this.document.messages.filter((message) => message.groupId === groupId).slice(-Math.max(1, Math.min(limit, 500)));
362
417
  }
@@ -414,7 +469,7 @@ export class CollaborationStore {
414
469
  const now = Date.now();
415
470
  const changed = [];
416
471
  this.document.messages = this.document.messages.map((message) => {
417
- if (!ids.has(message.id) || message.status === 'expired' || message.status === 'failed' || message.status === 'read')
472
+ if (!ids.has(message.id) || message.status === 'stored' || message.status === 'expired' || message.status === 'failed' || message.status === 'read')
418
473
  return message;
419
474
  const updated = status === 'read'
420
475
  ? { ...message, status, deliveredAt: message.deliveredAt ?? now, readAt: now, readSource, deliverySource: message.deliverySource ?? (message.deliveredAt === null ? 'consumer_read' : 'legacy_or_unspecified') }
@@ -499,7 +554,7 @@ export class CollaborationStore {
499
554
  if (!message || typeof message !== 'object')
500
555
  continue;
501
556
  const group = this.getGroup(message.groupId);
502
- if (!group?.federated || group.deleted || !group.sessionIds.includes(message.toSessionId)
557
+ if (!group?.federated || group.deleted || message.status === 'stored' || message.integrationOrigin || message.toPrincipalId || !group.sessionIds.includes(message.toSessionId)
503
558
  || (message.fromSessionId !== null && !group.sessionIds.includes(message.fromSessionId))
504
559
  || typeof message.id !== 'string' || typeof message.threadId !== 'string' || !Number.isFinite(message.createdAt)
505
560
  || typeof message.content !== 'string'
@@ -581,15 +636,18 @@ export class CollaborationStore {
581
636
  const message = this.getMessage(id);
582
637
  if (!message)
583
638
  throw new CollaborationError('MESSAGE_NOT_FOUND', 'Message not found or no longer retained', 404);
584
- const replies = this.document.messages.filter((reply) => reply.replyTo === id && reply.fromSessionId === message.toSessionId && reply.toSessionId === message.fromSessionId);
639
+ const replies = this.document.messages.filter(reply => reply.replyTo === id
640
+ && (message.toPrincipalId ? reply.integrationOrigin?.integrationId === message.toPrincipalId : reply.fromSessionId === message.toSessionId)
641
+ && (message.integrationOrigin ? reply.toPrincipalId === message.integrationOrigin.integrationId : reply.toSessionId === message.fromSessionId));
585
642
  const diagnostic = this.diagnostic(id);
586
643
  const facts = terminalMessage(message, raw);
587
- return { message_id: id, thread_id: message.threadId, status: facts.status, queued_at: message.createdAt,
644
+ return { message_id: id, thread_id: message.threadId, status: facts.status, queued_at: message.status === 'stored' ? null : message.createdAt, stored_at: message.storedAt ?? null,
645
+ from_principal_id: message.integrationOrigin?.integrationId ?? null, to_principal_id: message.toPrincipalId ?? null,
588
646
  delivered_at: facts.deliveredAt, expires_at: message.expiresAt ?? null,
589
- failure_reason: message.failureReason ?? null, delivery_semantics: facts.deliverySource ?? (facts.deliveredAt === null ? 'not_delivered' : 'legacy_or_unspecified'),
647
+ failure_reason: message.failureReason ?? null, delivery_semantics: message.status === 'stored' ? 'durable_storage' : facts.deliverySource ?? (facts.deliveredAt === null ? 'not_delivered' : 'legacy_or_unspecified'),
590
648
  snapshot: message.snapshot ? (raw ? message.snapshot : plainCollaborationSnapshot(message.snapshot)) : null,
591
649
  delivery: { status: facts.status,
592
- stage: facts.deliveredAt !== null ? 'terminal_written' : diagnostic?.remote_received_at ? 'remote_received' : 'queued',
650
+ stage: message.status === 'stored' ? 'stored' : facts.deliveredAt !== null ? 'terminal_written' : diagnostic?.remote_received_at ? 'remote_received' : 'queued',
593
651
  remote_received_at: diagnostic?.remote_received_at ?? null, delivered_at: facts.deliveredAt, error: message.failureReason ?? diagnostic?.last_error ?? null },
594
652
  reply: { status: replies.length ? 'received' : 'pending', ack_at: replies.find(reply => reply.responseKind === 'ack')?.createdAt ?? null, reply_ids: replies.map(reply => reply.id) },
595
653
  idempotency_key: message.idempotencyKey ?? null,
@@ -725,7 +783,7 @@ export class CollaborationStore {
725
783
  this.persist();
726
784
  return { message_id: fragment.message_id, received, total: state.total, complete: received === state.total };
727
785
  }
728
- persist() {
786
+ persist(durable = false) {
729
787
  const now = Date.now();
730
788
  for (const [key, record] of Object.entries(this.document.idempotency ?? {}))
731
789
  if (record.expiresAt <= now)
@@ -744,9 +802,26 @@ export class CollaborationStore {
744
802
  try {
745
803
  fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
746
804
  const serialized = JSON.stringify(this.document);
747
- fs.writeFileSync(temporaryPath, serialized, { mode: 0o600 });
805
+ const descriptor = fs.openSync(temporaryPath, 'w', 0o600);
806
+ try {
807
+ fs.writeFileSync(descriptor, serialized);
808
+ if (durable)
809
+ fs.fsyncSync(descriptor);
810
+ }
811
+ finally {
812
+ fs.closeSync(descriptor);
813
+ }
748
814
  fs.renameSync(temporaryPath, this.filePath);
749
815
  this.persistedDocument = serialized;
816
+ if (durable) {
817
+ const directory = fs.openSync(path.dirname(this.filePath), 'r');
818
+ try {
819
+ fs.fsyncSync(directory);
820
+ }
821
+ finally {
822
+ fs.closeSync(directory);
823
+ }
824
+ }
750
825
  for (const listener of this.changeListeners) {
751
826
  try {
752
827
  listener();
@@ -268,6 +268,8 @@ export class CollaborationTaskService {
268
268
  }
269
269
  receipt(messageId) {
270
270
  const receipt = this.messages.receipt(messageId);
271
+ if (receipt.status === 'stored')
272
+ throw new CollaborationError('INVALID_TASK_DELIVERY', 'A nonterminal reply cannot be a task delivery');
271
273
  return { messageId, status: receipt.status, deliveredAt: receipt.delivered_at, error: receipt.delivery.error };
272
274
  }
273
275
  start() { this.timer = setInterval(() => void this.flush(), 1500); this.timer.unref(); void this.flush(); }
@@ -4,16 +4,28 @@ import http from 'node:http';
4
4
  import { INTEGRATION_PROTOCOL } from './integrationStore.js';
5
5
  import { integrationSocketPath } from './integrationServer.js';
6
6
  import { CollaborationError } from './collaborationProtocol.js';
7
- import { executeCollaborationCommand, duration } from './collaborationCli.js';
7
+ import { COLLAB_HELP, executeCollaborationCommand, duration } from './collaborationCli.js';
8
8
  import { getTermdockVersion } from '../utils/version.js';
9
9
  export const INTEGRATION_HELP = `td integration — local scoped integration administration
10
10
  groups | list
11
+ resume-configuration <TD-session-id> (read current/recorded fingerprints; no mutation)
12
+ confirm-resume <TD-session-id> --generation <N> --fingerprint <sha256>
13
+ Administrator explicitly authorizes the current launcher/plugin recovery semantics.
14
+ This never proves unknown historical file contents or modifies Agent configuration.
15
+ Old records need this confirmation before release; new records capture a fingerprint.
16
+ Later changes fail closed. Grant session.release separately for runtime reclamation.
17
+ Minimum CLI/running service 1.4.309; capabilities.session_resume_configuration=true.
18
+ Errors: RESUME_CONFIGURATION_CHANGED, EXACT_RESUME_UNSUPPORTED,
19
+ SESSION_GENERATION_MISMATCH, SESSION_OPERATION_IN_PROGRESS, LAUNCHER_UNAVAILABLE.
11
20
  diagnostics <TD-session-id> (read the last restore check; administrator only)
12
21
  create --file <policy.json> --credential-file <private-output.json>
13
22
  update --file <policy.json> (same identity/group; preserves the credential)
14
23
  revoke <principal-id>
15
24
  Credentials are saved as a private file and never printed. Workers set
16
25
  TERMDOCK_INTEGRATION_CREDENTIAL_FILE, then use td collab --principal <id> …
26
+ Ordinary messages: permissions message.send, message.read, events.read;
27
+ td collab --principal <id> --help lists send/reply/history and event examples.
28
+ No task or user acceptance is required for a message-driven integration.
17
29
  Only the local administrator can provision/revoke grants. See docs/integrations.md.`;
18
30
  export function readIntegrationCredential(file, principalId) {
19
31
  const info = fs.lstatSync(file);
@@ -55,11 +67,35 @@ export async function runIntegrationAdmin(argv, port, adminToken, write) {
55
67
  let reserved;
56
68
  try {
57
69
  const action = argv[0] ?? 'help', options = {};
58
- if (action === 'help' || action === '--help') {
70
+ if (action === 'help' || argv.includes('--help')) {
59
71
  write(INTEGRATION_HELP);
60
72
  return 0;
61
73
  }
62
74
  const socketPath = integrationSocketPath(port);
75
+ if (action === 'resume-configuration' || action === 'confirm-resume') {
76
+ const id = argv[1];
77
+ if (!id || !/^[a-zA-Z0-9._-]{1,80}$/.test(id))
78
+ throw new Error('A full TD session id is required');
79
+ const capabilities = parsed(await request(socketPath, 'GET', '/admin/capabilities', undefined, headers(undefined, adminToken)));
80
+ if (capabilities.session_resume_configuration !== true)
81
+ throw new CollaborationError('SESSION_RELEASE_UNSUPPORTED', 'CLI and running service must be at least 1.4.309', 409);
82
+ let body;
83
+ if (action === 'confirm-resume') {
84
+ const args = {};
85
+ for (let i = 2; i < argv.length; i += 2) {
86
+ if (!['--generation', '--fingerprint'].includes(argv[i]) || !argv[i + 1] || args[argv[i]])
87
+ throw new Error('confirm-resume requires --generation and --fingerprint');
88
+ args[argv[i]] = argv[i + 1];
89
+ }
90
+ if (!/^[1-9][0-9]*$/.test(args['--generation'] ?? '') || !Number.isSafeInteger(Number(args['--generation'])) || !/^[a-f0-9]{64}$/.test(args['--fingerprint'] ?? ''))
91
+ throw new Error('Invalid generation or fingerprint');
92
+ body = { generation: Number(args['--generation']), fingerprint: args['--fingerprint'] };
93
+ }
94
+ else if (argv.length !== 2)
95
+ throw new Error('resume-configuration requires one TD session id');
96
+ write(JSON.stringify(parsed(await request(socketPath, body ? 'POST' : 'GET', `/admin/sessions/${encodeURIComponent(id)}/resume-configuration`, body, headers(undefined, adminToken)))));
97
+ return 0;
98
+ }
63
99
  if (action === 'diagnostics') {
64
100
  if (argv.length !== 2 || !/^[a-zA-Z0-9._-]{1,80}$/.test(argv[1]))
65
101
  throw new Error('diagnostics requires one TD session id');
@@ -131,6 +167,10 @@ export async function revokeIntegration(id, port, adminToken, write) {
131
167
  }
132
168
  export async function runIntegrationCollab(command, port, io) {
133
169
  try {
170
+ if (command.action === 'help') {
171
+ io.write(COLLAB_HELP);
172
+ return 0;
173
+ }
134
174
  const principalId = String(command.options.principal), file = process.env.TERMDOCK_INTEGRATION_CREDENTIAL_FILE;
135
175
  if (!file)
136
176
  throw new CollaborationError('INTEGRATION_CREDENTIAL_REQUIRED', 'Set TERMDOCK_INTEGRATION_CREDENTIAL_FILE to a private credential file', 401);
@@ -154,14 +194,20 @@ export async function runIntegrationCollab(command, port, io) {
154
194
  return subscribe(socketPath, `/events?${params}`, credential, command.options.timeout ? duration(String(command.options.timeout)) : undefined, io.write);
155
195
  }
156
196
  if (command.action === 'session') {
157
- const route = command.operation === 'create' ? '/sessions' : `/sessions/${encodeURIComponent(command.target)}${command.operation === 'restore' ? '/restore' : ''}`;
197
+ if (command.operation === 'release' && (capability.session_runtime_release !== true || capability.session_lifecycle_generation !== true || capability.stable_session_identity !== true))
198
+ throw new CollaborationError('SESSION_RELEASE_UNSUPPORTED', 'CLI and running service must support the lifecycle contract (minimum 1.4.309)', 409);
199
+ const route = command.operation === 'create' ? '/sessions' : `/sessions/${encodeURIComponent(command.target)}${['restore', 'release'].includes(command.operation) ? `/${command.operation}` : ''}`;
158
200
  const body = command.operation === 'create' ? { group_id: command.options.group, launch_profile: command.options['launch-profile'], cwd: command.options.cwd, idempotency_key: command.options['idempotency-key'] }
159
- : command.operation === 'restore' ? { idempotency_key: command.options['idempotency-key'] } : undefined;
201
+ : ['restore', 'release'].includes(command.operation) ? { ...(command.operation === 'release' ? { generation: Number(command.options.generation) } : {}), idempotency_key: command.options['idempotency-key'] } : undefined;
160
202
  io.write(JSON.stringify(parsed(await request(socketPath, command.operation === 'get' ? 'GET' : 'POST', route, body, headers(credential)))));
161
203
  return 0;
162
204
  }
163
- if (!['task', 'message', 'help'].includes(command.action) || command.action === 'message' && command.operation !== 'get')
164
- throw new CollaborationError('INTEGRATION_OPERATION_DENIED', 'Command is not exposed to integration identities', 403);
205
+ const messageCapability = command.action === 'send' ? 'integration_message_send' : command.action === 'reply' ? 'integration_message_reply'
206
+ : command.action === 'message' && command.operation === 'list' ? 'integration_message_history' : null;
207
+ if (messageCapability && capability[messageCapability] !== true)
208
+ throw new CollaborationError('INTEGRATION_MESSAGING_UNSUPPORTED', 'CLI and running service must support integration messaging (minimum 1.4.308); upgrade both and read capabilities', 409);
209
+ if (!['task', 'message', 'send', 'reply'].includes(command.action) || command.action === 'message' && !['get', 'list'].includes(command.operation))
210
+ throw new CollaborationError('INTEGRATION_OPERATION_DENIED', 'Command is not exposed to integration identities; see td collab --principal <id> --help', 403);
165
211
  return executeCollaborationCommand(command, {}, { ...io, request: async (method, endpoint, body, timeout) => {
166
212
  const origin = { source: command.options.source, externalActor: command.options['external-actor'] ? JSON.parse(String(command.options['external-actor'])) : undefined,
167
213
  externalMessageId: command.options['external-message-id'], metadata: command.options.metadata ? JSON.parse(String(command.options.metadata)) : undefined };