termdock 1.4.306 → 1.4.308

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 (350) hide show
  1. package/dist/client/assets/{App-BziVLy1b.js → App-ggIA_xqR.js} +7 -7
  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-CARNBEqQ.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-00oa0z6w.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-DuEeKQKZ.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-C9v1Ooc9.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-DfUpMALj.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-Cf_URhQt.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-DjG9vCpH.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-BKOvHdJY.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-6n3tgsdc.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-BNs2sRct.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-CcMSU1v8.js → RightSidebar-BoNzpS2K.js} +3 -3
  36. package/dist/client/assets/RightSidebar-BoNzpS2K.js.br +0 -0
  37. package/dist/client/assets/RightSidebar-BoNzpS2K.js.gz +0 -0
  38. package/dist/client/assets/{abnfDiagram-VCTEODGH-DogjvRKp.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-D6vN8PW-.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-Cqel4Ted.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-DgvPtgNb.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-BxbWO4x4.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-gsA37gjL.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-ChC9yYOd.js → chunk-2Q5K7J3B-CfX9l18E.js} +1 -1
  58. package/dist/client/assets/{chunk-5VM5RSS4-Bi4Hc8yL.js → chunk-5VM5RSS4-JSV1yrXM.js} +1 -1
  59. package/dist/client/assets/{chunk-F27PBJKO-DvVWvQEk.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-BnwQLR5S.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-BkNnaDiR.js → chunk-JWPE2WC7-Dpp7Xu_E.js} +1 -1
  66. package/dist/client/assets/{chunk-POPQ4Y6H-BRzm6Uzi.js → chunk-POPQ4Y6H-9hDa1b8z.js} +1 -1
  67. package/dist/client/assets/{chunk-SVP7TREG-CDYm-dzk.js → chunk-SVP7TREG-BskkG9gg.js} +1 -1
  68. package/dist/client/assets/{chunk-SVP7TREG-CDYm-dzk.js.br → 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-BKaW9Z9i.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-DcHZ_FHb.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-DCbbTNc6.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-i23hD0Z2.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-CbyLYcqc.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-iJ4VKzZm.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-BNVZcdsO.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-1ghTvPiu.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-Ce_0nMd3.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-CLHEkhUt.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-iyb5DkP6.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-Dxh2TzXm.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-Cr8IkdCC.js → erDiagram-RLTQ6QDP-DlXgoyZ-.js} +1 -1
  107. package/dist/client/assets/{erDiagram-RLTQ6QDP-Cr8IkdCC.js.br → 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-B8ZF1bwr.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-zhdL10eM.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-CXYHfU__.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-BQf2fHBo.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-BT8xey7S.js → infoDiagram-27XIBGKW-CSG29Ty_.js} +1 -1
  122. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-DunPdwW9.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-B0y75iIg.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-BfgA5WLo.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-BvjABLff.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-DgeFRliF.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-DM6Rlal9.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-DhEiABv6.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-Dxv0I4wM.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-LTlVaJfm.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-9bnryna3.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-ZBmgIpsJ.js → quadrantDiagram-AXDQQJYC-BPyHtH91.js} +1 -1
  153. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js.br → 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-DTtXvKOu.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-Ggo3N5s5.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-Df8W2cyP.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-BNia9zI0.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-uvg5npfK.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-DpJNf2MD.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-CkWB2AnF.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-DOuEtn62.js → sparkles-Cg-yPdE2.js} +1 -1
  177. package/dist/client/assets/{stateDiagram-D77RDMKH-CeMzMQq-.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-Bo9GejWo.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-cvWXYEZ4.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-DzV0gQXz.js → triangle-alert-CbMRJ6sx.js} +1 -1
  189. package/dist/client/assets/{vennDiagram-4TSXK5OY-CzleWcmk.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-DCnzjEfU.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-Cir1iutR.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-pNPcVJ1C.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 +62 -16
  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 +8 -1
  211. package/dist/server/agent/collaborationStore.js +81 -11
  212. package/dist/server/agent/collaborationTaskCli.js +25 -7
  213. package/dist/server/agent/collaborationTaskPurpose.js +20 -0
  214. package/dist/server/agent/collaborationTaskRoutes.js +1 -1
  215. package/dist/server/agent/collaborationTaskService.js +15 -6
  216. package/dist/server/agent/collaborationTaskStore.js +35 -6
  217. package/dist/server/agent/integrationCli.js +15 -4
  218. package/dist/server/agent/integrationServer.js +92 -14
  219. package/dist/server/agent/integrationStore.js +1 -1
  220. package/dist/server/cli.js +1 -1
  221. package/docs/integrations.md +177 -5
  222. package/package.json +1 -1
  223. package/runtime-manifest.json +3 -3
  224. package/dist/client/assets/App-BziVLy1b.js.br +0 -0
  225. package/dist/client/assets/App-BziVLy1b.js.gz +0 -0
  226. package/dist/client/assets/ArchitecturePanel-CARNBEqQ.js.br +0 -0
  227. package/dist/client/assets/ArchitecturePanel-CARNBEqQ.js.gz +0 -0
  228. package/dist/client/assets/ChangeWalkthroughPanel-00oa0z6w.js.br +0 -0
  229. package/dist/client/assets/ChangeWalkthroughPanel-00oa0z6w.js.gz +0 -0
  230. package/dist/client/assets/CollaborationMainWorkspace-DuEeKQKZ.js.br +0 -0
  231. package/dist/client/assets/CollaborationMainWorkspace-DuEeKQKZ.js.gz +0 -0
  232. package/dist/client/assets/CollaborationTaskContent-B_mzpNuw.js +0 -2
  233. package/dist/client/assets/ComputerControlView-C9v1Ooc9.js.br +0 -0
  234. package/dist/client/assets/ComputerControlView-C9v1Ooc9.js.gz +0 -0
  235. package/dist/client/assets/DagPlayground-DfUpMALj.js.br +0 -0
  236. package/dist/client/assets/DagPlayground-DfUpMALj.js.gz +0 -0
  237. package/dist/client/assets/DiffLab-Cf_URhQt.js.br +0 -0
  238. package/dist/client/assets/DiffLab-Cf_URhQt.js.gz +0 -0
  239. package/dist/client/assets/DiffReview-DjG9vCpH.js.br +0 -0
  240. package/dist/client/assets/DiffReview-DjG9vCpH.js.gz +0 -0
  241. package/dist/client/assets/DiffReviewLab-BKOvHdJY.js.br +0 -0
  242. package/dist/client/assets/DiffReviewLab-BKOvHdJY.js.gz +0 -0
  243. package/dist/client/assets/DiffViewer-6n3tgsdc.js.br +0 -0
  244. package/dist/client/assets/DiffViewer-6n3tgsdc.js.gz +0 -0
  245. package/dist/client/assets/ModelPreview-BNs2sRct.js.br +0 -0
  246. package/dist/client/assets/ModelPreview-BNs2sRct.js.gz +0 -0
  247. package/dist/client/assets/RightSidebar-CcMSU1v8.js.br +0 -0
  248. package/dist/client/assets/RightSidebar-CcMSU1v8.js.gz +0 -0
  249. package/dist/client/assets/abnfDiagram-VCTEODGH-DogjvRKp.js.br +0 -0
  250. package/dist/client/assets/abnfDiagram-VCTEODGH-DogjvRKp.js.gz +0 -0
  251. package/dist/client/assets/app-D6vN8PW-.js.br +0 -0
  252. package/dist/client/assets/app-D6vN8PW-.js.gz +0 -0
  253. package/dist/client/assets/arc-Cqel4Ted.js.br +0 -0
  254. package/dist/client/assets/arc-Cqel4Ted.js.gz +0 -0
  255. package/dist/client/assets/architectureDiagram-5GKGNRK7-DgvPtgNb.js.br +0 -0
  256. package/dist/client/assets/architectureDiagram-5GKGNRK7-DgvPtgNb.js.gz +0 -0
  257. package/dist/client/assets/blockDiagram-I7D4REHJ-BxbWO4x4.js.br +0 -0
  258. package/dist/client/assets/blockDiagram-I7D4REHJ-BxbWO4x4.js.gz +0 -0
  259. package/dist/client/assets/c4Diagram-7LVT6UL2-gsA37gjL.js.br +0 -0
  260. package/dist/client/assets/c4Diagram-7LVT6UL2-gsA37gjL.js.gz +0 -0
  261. package/dist/client/assets/channel-CBc8-n7y.js +0 -1
  262. package/dist/client/assets/chunk-F27PBJKO-DvVWvQEk.js.br +0 -0
  263. package/dist/client/assets/chunk-F27PBJKO-DvVWvQEk.js.gz +0 -0
  264. package/dist/client/assets/chunk-IMKFNOWR-BnwQLR5S.js.br +0 -0
  265. package/dist/client/assets/chunk-IMKFNOWR-BnwQLR5S.js.gz +0 -0
  266. package/dist/client/assets/chunk-SVP7TREG-CDYm-dzk.js.gz +0 -0
  267. package/dist/client/assets/chunk-TICWLB2K-BKaW9Z9i.js.br +0 -0
  268. package/dist/client/assets/chunk-TICWLB2K-BKaW9Z9i.js.gz +0 -0
  269. package/dist/client/assets/classDiagram-ZZMXUADV-CNFc2dOc.js +0 -1
  270. package/dist/client/assets/classDiagram-v2-VYDZK3BY-CNFc2dOc.js +0 -1
  271. package/dist/client/assets/cose-bilkent-JH36ORCC-DCbbTNc6.js.br +0 -0
  272. package/dist/client/assets/cose-bilkent-JH36ORCC-DCbbTNc6.js.gz +0 -0
  273. package/dist/client/assets/cynefin-OW5HDTMX-i23hD0Z2.js.br +0 -0
  274. package/dist/client/assets/cynefin-OW5HDTMX-i23hD0Z2.js.gz +0 -0
  275. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CbyLYcqc.js.br +0 -0
  276. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CbyLYcqc.js.gz +0 -0
  277. package/dist/client/assets/dagre-GXQ25YYZ-iJ4VKzZm.js.br +0 -0
  278. package/dist/client/assets/dagre-GXQ25YYZ-iJ4VKzZm.js.gz +0 -0
  279. package/dist/client/assets/diagram-S7CK7UJ4-BNVZcdsO.js.br +0 -0
  280. package/dist/client/assets/diagram-S7CK7UJ4-BNVZcdsO.js.gz +0 -0
  281. package/dist/client/assets/diagram-UQ7AKVKN-1ghTvPiu.js.br +0 -0
  282. package/dist/client/assets/diagram-UQ7AKVKN-1ghTvPiu.js.gz +0 -0
  283. package/dist/client/assets/diagram-VSXAHHWV-Ce_0nMd3.js.br +0 -0
  284. package/dist/client/assets/diagram-VSXAHHWV-Ce_0nMd3.js.gz +0 -0
  285. package/dist/client/assets/diagram-VX7I27RA-CLHEkhUt.js.br +0 -0
  286. package/dist/client/assets/diagram-VX7I27RA-CLHEkhUt.js.gz +0 -0
  287. package/dist/client/assets/diagram-Z3DM3KII-iyb5DkP6.js.br +0 -0
  288. package/dist/client/assets/diagram-Z3DM3KII-iyb5DkP6.js.gz +0 -0
  289. package/dist/client/assets/ebnfDiagram-PWID7BFC-Dxh2TzXm.js.br +0 -0
  290. package/dist/client/assets/ebnfDiagram-PWID7BFC-Dxh2TzXm.js.gz +0 -0
  291. package/dist/client/assets/erDiagram-RLTQ6QDP-Cr8IkdCC.js.gz +0 -0
  292. package/dist/client/assets/flowDiagram-HODETNUW-B8ZF1bwr.js.br +0 -0
  293. package/dist/client/assets/flowDiagram-HODETNUW-B8ZF1bwr.js.gz +0 -0
  294. package/dist/client/assets/ganttDiagram-EL5Y4UJY-zhdL10eM.js.br +0 -0
  295. package/dist/client/assets/ganttDiagram-EL5Y4UJY-zhdL10eM.js.gz +0 -0
  296. package/dist/client/assets/gestureArbiter-CXYHfU__.js.br +0 -0
  297. package/dist/client/assets/gestureArbiter-CXYHfU__.js.gz +0 -0
  298. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BQf2fHBo.js.br +0 -0
  299. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-BQf2fHBo.js.gz +0 -0
  300. package/dist/client/assets/ishikawaDiagram-5VMMS53U-DunPdwW9.js.br +0 -0
  301. package/dist/client/assets/ishikawaDiagram-5VMMS53U-DunPdwW9.js.gz +0 -0
  302. package/dist/client/assets/journeyDiagram-3NMN7TZE-B0y75iIg.js.br +0 -0
  303. package/dist/client/assets/journeyDiagram-3NMN7TZE-B0y75iIg.js.gz +0 -0
  304. package/dist/client/assets/kanban-definition-UXKFOSKX-BfgA5WLo.js.br +0 -0
  305. package/dist/client/assets/kanban-definition-UXKFOSKX-BfgA5WLo.js.gz +0 -0
  306. package/dist/client/assets/layout-BvjABLff.js.br +0 -0
  307. package/dist/client/assets/layout-BvjABLff.js.gz +0 -0
  308. package/dist/client/assets/linear-DgeFRliF.js.br +0 -0
  309. package/dist/client/assets/linear-DgeFRliF.js.gz +0 -0
  310. package/dist/client/assets/mermaid.core-DM6Rlal9.js.br +0 -0
  311. package/dist/client/assets/mermaid.core-DM6Rlal9.js.gz +0 -0
  312. package/dist/client/assets/mindmap-definition-YA3MSWOX-DhEiABv6.js.br +0 -0
  313. package/dist/client/assets/mindmap-definition-YA3MSWOX-DhEiABv6.js.gz +0 -0
  314. package/dist/client/assets/passwordBootstrap-Dxv0I4wM.js.br +0 -0
  315. package/dist/client/assets/passwordBootstrap-Dxv0I4wM.js.gz +0 -0
  316. package/dist/client/assets/pegDiagram-XKGWAZYB-LTlVaJfm.js.br +0 -0
  317. package/dist/client/assets/pegDiagram-XKGWAZYB-LTlVaJfm.js.gz +0 -0
  318. package/dist/client/assets/pieDiagram-E7YTZNPT-9bnryna3.js.br +0 -0
  319. package/dist/client/assets/pieDiagram-E7YTZNPT-9bnryna3.js.gz +0 -0
  320. package/dist/client/assets/quadrantDiagram-AXDQQJYC-ZBmgIpsJ.js.gz +0 -0
  321. package/dist/client/assets/railroadDiagram-O6MQD6OU-DTtXvKOu.js.br +0 -0
  322. package/dist/client/assets/railroadDiagram-O6MQD6OU-DTtXvKOu.js.gz +0 -0
  323. package/dist/client/assets/rdpSession-Ggo3N5s5.js.br +0 -0
  324. package/dist/client/assets/rdpSession-Ggo3N5s5.js.gz +0 -0
  325. package/dist/client/assets/referenceLongPress-Df8W2cyP.js.br +0 -0
  326. package/dist/client/assets/referenceLongPress-Df8W2cyP.js.gz +0 -0
  327. package/dist/client/assets/requirementDiagram-BXWQKSXE-BNia9zI0.js.br +0 -0
  328. package/dist/client/assets/requirementDiagram-BXWQKSXE-BNia9zI0.js.gz +0 -0
  329. package/dist/client/assets/sankeyDiagram-P5KCCOFB-uvg5npfK.js.br +0 -0
  330. package/dist/client/assets/sankeyDiagram-P5KCCOFB-uvg5npfK.js.gz +0 -0
  331. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DpJNf2MD.js.br +0 -0
  332. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-DpJNf2MD.js.gz +0 -0
  333. package/dist/client/assets/sizeCapture-INFHLROL-CkWB2AnF.js.br +0 -0
  334. package/dist/client/assets/sizeCapture-INFHLROL-CkWB2AnF.js.gz +0 -0
  335. package/dist/client/assets/stateDiagram-D77RDMKH-CeMzMQq-.js.br +0 -0
  336. package/dist/client/assets/stateDiagram-D77RDMKH-CeMzMQq-.js.gz +0 -0
  337. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-CNYUpLxt.js +0 -1
  338. package/dist/client/assets/swimlanes-42K2YHIH-Bo9GejWo.js.br +0 -0
  339. package/dist/client/assets/swimlanes-42K2YHIH-Bo9GejWo.js.gz +0 -0
  340. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-EB7zBRFR.js +0 -8
  341. package/dist/client/assets/timeline-definition-24CTP7MA-cvWXYEZ4.js.br +0 -0
  342. package/dist/client/assets/timeline-definition-24CTP7MA-cvWXYEZ4.js.gz +0 -0
  343. package/dist/client/assets/vennDiagram-4TSXK5OY-CzleWcmk.js.br +0 -0
  344. package/dist/client/assets/vennDiagram-4TSXK5OY-CzleWcmk.js.gz +0 -0
  345. package/dist/client/assets/videoCompression-DCnzjEfU.js.br +0 -0
  346. package/dist/client/assets/videoCompression-DCnzjEfU.js.gz +0 -0
  347. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cir1iutR.js.br +0 -0
  348. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Cir1iutR.js.gz +0 -0
  349. package/dist/client/assets/xychartDiagram-S5SC5T6Z-pNPcVJ1C.js.br +0 -0
  350. package/dist/client/assets/xychartDiagram-S5SC5T6Z-pNPcVJ1C.js.gz +0 -0
@@ -5,10 +5,12 @@ import { randomUUID } from 'node:crypto';
5
5
  import { COLLAB_LIMITS, CollaborationError, canonicalShortId } from './collaborationProtocol.js';
6
6
  export const COLLAB_HELP = `td collab — durable messages; no agent-specific hooks required
7
7
  ── 任务闭环 ──
8
- task list [--group <组id>] | task get <任务id>
8
+ task list [--group <组id>] [--purpose interactive|automation|all] | task get <任务id>
9
+ task configure <任务id> --purpose interactive|automation --revision <当前版本>
9
10
  task create --group <组id> --title <标题> --content <正文> [--assignee <成员id>]
10
11
  [--constraints <约束>] [--acceptance <验收标准>] [--coordinator <成员id>]
11
12
  [--parent <任务id>] [--depends-on <任务id,任务id>] [--integration]
13
+ [--purpose interactive|automation](默认 interactive;automation 是独立后台记录,无需用户验收)
12
14
  task assign <任务id> --assignee <成员id> --revision <当前版本>
13
15
  task report <任务id> --attempt <尝试id> --status ack|working|blocked|complete|failed --content <正文>
14
16
  task ask <任务id> --attempt <尝试id> --content <问题> [--options '["选项一","选项二"]']
@@ -50,12 +52,27 @@ export const COLLAB_HELP = `td collab — durable messages; no agent-specific ho
50
52
  ── 后台集成(独立凭据;本机 Unix socket) ──
51
53
  td integration help(管理授权与启动配置)
52
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 标记回复类型。
53
64
  events subscribe --consumer <name> --jsonl(主动推送,默认持续运行)
54
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,不能据此声称历史完整。
55
69
  session create --group <id> --launch-profile <id> --cwd <absolute-path> --idempotency-key <key>
56
70
  session get <id> | session restore <id> --idempotency-key <key>
57
71
  task respond <task-id> --attempt <attempt-id> --to-event <comment-event-id> --content <答复>
58
72
  凭据通过 TERMDOCK_INTEGRATION_CREDENTIAL_FILE 注入,不通过命令行传 token。
73
+ 消息接入最低 CLI/实际服务均为 1.4.308;capabilities 回读双方版本与 integration_message_send/reply/history。
74
+ principal 只操作获授权本机组;不能冒充用户/成员或用户验收。普通消息不需要 task/accepted。
75
+ 任务命令适用于需要任务闭环的流程;message.send/read 与 task 权限独立。
59
76
  已验收任务可追加纯咨询;respond 保留当前结果与验收,revise 是修改要求。
60
77
  ── 常用 ──
61
78
  status (who is in my groups, their names, roles, observed activity.
@@ -143,11 +160,11 @@ Message limit: ${COLLAB_LIMITS.message_bytes} UTF-8 bytes; metadata: ${COLLAB_LI
143
160
  Idempotency retention: 7 days. Terminal delivery, ACK and result never imply each other.
144
161
  Delivery semantics: delivered = written to the terminal — never proof of
145
162
  reading or task completion; a timeout stops waiting, it does not cancel delivery.
146
- The service cannot see whether the recipient's agent consumed a message. To
147
- judge for yourself, use --wait-until delivered and read the recipient-screen
148
- snapshot in the receipt: if your message is not visible on that screen, send it
149
- again (a fresh send is a new message and will be written again); if it is
150
- visible but unanswered, the recipient may simply not have started yet.`;
163
+ The service cannot see whether the recipient's agent consumed a message.
164
+ Use explicit replies as evidence. A screen snapshot is only an observation;
165
+ do not automatically resend a delivered message because it is absent from a snapshot.
166
+ stored = durable reply to a nonterminal principal; terminal delivery waits do not apply.
167
+ Idempotent retries reuse the same key and payload; a fresh key creates a new message.`;
151
168
  /** One roster row: prefer the member's human name, keep the full session id
152
169
  * reachable for role set/unset targeting, mark unset members explicitly. */
153
170
  function roleLine(member, role) {
@@ -155,7 +172,7 @@ function roleLine(member, role) {
155
172
  return role ? `- ${label}:${role}` : `- ${label}(未设置)`;
156
173
  }
157
174
  const BOOLEAN_OPTIONS = new Set(['json', 'jsonl', 'text', 'follow', 'stdin', 'receipt-only', 'confirm', 'raw', 'help', 'no-rules', 'managed', 'shared-directory', 'integration']);
158
- const VALUE_OPTIONS = new Set(['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']);
175
+ 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']);
159
176
  export function parseCollaborationCommand(argv) {
160
177
  const options = {};
161
178
  const positional = [];
@@ -234,7 +251,7 @@ export function parseCollaborationCommand(argv) {
234
251
  else if (action === 'task') {
235
252
  command.operation = positional.shift();
236
253
  command.target = positional.shift();
237
- if (positional.length || !['list', 'get', 'create', 'assign', 'report', 'respond', 'answer', 'ask', 'plan', 'review', 'request-review', 'comment', 'revise', 'close', 'reopen', 'coordinate', 'pause', 'resume', 'retry'].includes(command.operation ?? ''))
254
+ if (positional.length || !['list', 'get', 'create', 'configure', 'assign', 'report', 'respond', 'answer', 'ask', 'plan', 'review', 'request-review', 'comment', 'revise', 'close', 'reopen', 'coordinate', 'pause', 'resume', 'retry'].includes(command.operation ?? ''))
238
255
  throw new Error('Usage: td collab task list|get|create|assign|report|ask|plan|review|request-review|comment|revise|close|reopen');
239
256
  if (['list', 'create'].includes(command.operation) ? Boolean(command.target) : !command.target)
240
257
  throw new Error('task command requires a full task id except list/create');
@@ -248,6 +265,8 @@ export function parseCollaborationCommand(argv) {
248
265
  throw new Error(`${action} requires a target and message`);
249
266
  if ([Boolean(command.message), Boolean(options.file), Boolean(options.stdin)].filter(Boolean).length !== 1)
250
267
  throw new Error('Choose inline body, --file, or --stdin');
268
+ if (options.principal && !options['idempotency-key'])
269
+ throw new Error('Integration message mutation requires an explicit --idempotency-key');
251
270
  }
252
271
  else if (action === 'rules') {
253
272
  command.operation = positional.shift();
@@ -284,8 +303,15 @@ export function parseCollaborationCommand(argv) {
284
303
  else if (action === 'message') {
285
304
  command.operation = positional.shift();
286
305
  command.target = positional.shift();
287
- if (!['get', 'watch', 'confirm-shell'].includes(command.operation ?? '') || !command.target || positional.length)
288
- throw new Error('Usage: td collab message get|watch|confirm-shell <id>');
306
+ if (!['get', 'watch', 'confirm-shell', 'list'].includes(command.operation ?? '') || (command.operation === 'list' ? !options.principal || Boolean(command.target) : !command.target) || positional.length)
307
+ throw new Error('Usage: td collab message get|watch|confirm-shell <id>; --principal <id> message list');
308
+ if (command.operation === 'list') {
309
+ for (const flag of Object.keys(options))
310
+ if (!['principal', 'json', 'jsonl', 'text', 'group', 'thread', 'after-id', 'limit'].includes(flag))
311
+ throw new Error(`--${flag} is not supported by message list`);
312
+ }
313
+ else if (['group', 'thread', 'after-id', 'limit'].some(flag => options[flag] !== undefined))
314
+ throw new Error('History filters apply to --principal message list only');
289
315
  }
290
316
  else if (action === 'cursor') {
291
317
  command.operation = positional.shift();
@@ -356,17 +382,20 @@ export function parseCollaborationCommand(argv) {
356
382
  const allowed = new Set(['json', 'jsonl', 'text', 'help', 'session', 'principal']);
357
383
  const byAction = {
358
384
  events: ['consumer', 'group', 'timeout'], session: ['group', 'launch-profile', 'cwd', 'idempotency-key'],
359
- task: ['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'],
385
+ 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'],
360
386
  rules: ['file', 'stdin', 'if-version'], transport: ['file', 'origin'], group: ['file'], status: [], capabilities: [], rebind: ['pane'], help: [...BOOLEAN_OPTIONS, ...VALUE_OPTIONS],
361
387
  send: ['group', 'thread', 'idempotency-key', 'file', 'stdin', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'expires-at', 'kind'],
362
388
  handoff: ['group', 'thread', 'idempotency-key', 'file', 'stdin', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'expires-at'],
363
389
  reply: ['idempotency-key', 'file', 'stdin', 'wait-until', 'timeout', 'expect-reply', 'response-kind', 'metadata', 'task-envelope', 'expires-at'],
364
390
  inbox: ['raw', 'since', 'after-id', 'cursor', 'consumer', 'limit', 'from', 'group', 'thread', 'kind', 'response-kind', 'follow', 'timeout'],
365
- message: ['raw', 'receipt-only', 'no-rules', 'follow', 'wait-until', 'timeout', 'expect-reply'], cursor: ['consumer'],
391
+ message: ['raw', 'receipt-only', 'no-rules', 'follow', 'wait-until', 'timeout', 'expect-reply', 'group', 'thread', 'after-id', 'limit'], cursor: ['consumer'],
366
392
  add: [], remove: [], spawn: ['launch-profile', 'name', 'cwd', 'task'], role: [], rename: [], cleanup: ['confirm'], drive: ['lines', 'raw'],
367
393
  };
368
394
  for (const option of byAction[action])
369
395
  allowed.add(option);
396
+ if (options.principal && ['send', 'reply'].includes(action))
397
+ for (const option of ['source', 'external-actor', 'external-message-id'])
398
+ allowed.add(option);
370
399
  for (const option of Object.keys(options))
371
400
  if (!allowed.has(option))
372
401
  throw new Error(`--${option} is not supported by ${action}`);
@@ -392,7 +421,7 @@ export function waitSatisfied(receipt, stage, reply) {
392
421
  * machine reasons onto what the sender should do next, so a settled-but-
393
422
  * unconsumed message is not mistaken for a lost one (or a good one). */
394
423
  const DELIVERY_DIAGNOSTICS = {
395
- AGENT_CONSUME_UNCONFIRMED: '消息已写入对方终端,但未确认被对方消费;它可能尚未开始处理,可用 capture 查看当前屏幕或重发',
424
+ AGENT_CONSUME_UNCONFIRMED: '消息已写入对方终端,但未确认被对方消费;等待明确回复,不从终端快照推断消费或自动重发',
396
425
  SHELL_CONFIRMATION_REQUIRED: '目标当前是 shell,消息可能被当命令执行;确认请运行 message confirm-shell <id>',
397
426
  DELIVERY_IN_PROGRESS: '投递进行中(写入前的中间标记,正常会在数秒内推进为 delivered 或带原因的重试)',
398
427
  TERMINAL_WRITE_FAILED: '写入对方终端失败,会按重试间隔继续尝试',
@@ -428,17 +457,22 @@ export async function executeCollaborationCommand(command, context, io) {
428
457
  const output = (value) => {
429
458
  if (o.text) {
430
459
  let diagnosed = false;
431
- if (Array.isArray(value.messages))
460
+ if (Array.isArray(value.messages)) {
432
461
  for (const message of value.messages) {
433
462
  const fanIds = Array.isArray(message.fanOutIds) ? message.fanOutIds : [];
434
463
  const names = value.names ?? {};
435
- io.write(`[${message.responseKind ?? message.kind}] ${canonicalShortId(message.id)} from ${message.fromSessionId ?? 'user'}${fanIds.length ? ' · 群发' : ''}`);
464
+ io.write(`[${message.responseKind ?? message.kind}] ${canonicalShortId(message.id)} from ${message.fromSessionId ?? (message.integrationOrigin ? `principal:${message.integrationOrigin.integrationId}` : 'user')}${fanIds.length ? ' · 群发' : ''}`);
436
465
  // A fan-out dispatch names its sibling recipients so a broadcast is
437
466
  // never read as a one-to-one assignment; unknown ids stay raw.
438
467
  if (fanIds.length)
439
468
  io.write(`同时发给了:${fanIds.map((id) => names[id] ?? id).join('、')}`);
440
469
  io.write(message.content);
470
+ if (message.status === 'stored')
471
+ io.write('答复已持久保存;接收方是后台程序,不是终端。');
441
472
  }
473
+ if (value.next_after_id !== undefined)
474
+ io.write(`next_after_id=${value.next_after_id ?? ''} has_more=${value.has_more === true}(仅保留记录)`);
475
+ }
442
476
  else if (value.message) {
443
477
  // One line instead of the full rules text — the CLI is stateless so it
444
478
  // cannot diff versions across calls; --no-rules silences it entirely.
@@ -643,12 +677,24 @@ export async function executeCollaborationCommand(command, context, io) {
643
677
  return 0;
644
678
  }
645
679
  if (command.action === 'message') {
680
+ if (command.operation === 'list') {
681
+ const params = new URLSearchParams();
682
+ for (const key of ['group', 'thread', 'after-id', 'limit'])
683
+ if (o[key])
684
+ params.set(key, String(o[key]));
685
+ output(await request('GET', `/message?${params}`));
686
+ return 0;
687
+ }
646
688
  const route = `/message/${encodeURIComponent(command.target)}`;
647
689
  if (command.operation === 'confirm-shell') {
648
690
  output(await request('POST', `${route}/${command.operation}`));
649
691
  return 0;
650
692
  }
651
693
  receipt = await request('GET', `${route}?receipt_only=${Boolean(o['receipt-only'] || command.operation === 'watch')}&raw=${Boolean(o.raw)}`);
694
+ if (receipt.status === 'stored') {
695
+ output(receipt);
696
+ return 0;
697
+ }
652
698
  if (command.operation === 'get' && !o.follow && !o['wait-until'] && !o['expect-reply']) {
653
699
  output(receipt);
654
700
  return 0;
@@ -690,7 +736,7 @@ export async function executeCollaborationCommand(command, context, io) {
690
736
  // CLI receipts stay small even when the message is a large evidence package.
691
737
  delete receipt.messages;
692
738
  receipt.idempotency_key = idempotencyKey;
693
- if (receipt.task_recorded) {
739
+ if (receipt.task_recorded || receipt.integration_recorded) {
694
740
  output(receipt);
695
741
  return 0;
696
742
  }
@@ -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 };
@@ -156,10 +156,12 @@ export function collaborationRoutes({ store, resolveSession, deliver, rebind, re
156
156
  managed_goals: true, automatic_review: true, dependency_dispatch: true, isolated_worktrees: true, encrypted_commit_transfer: true,
157
157
  durable_questions: true, user_acceptance: true, versioned_artifacts: true, independent_reviews: true,
158
158
  correlated_responses: true, accepted_comments: true,
159
+ background_task_records: true, task_purpose_configuration: true, explicit_execution_state: true,
159
160
  task_reply: 'Replies to task-linked user messages are stored in the task; status=stored is not a terminal delivery receipt' },
160
161
  routing: { background_recovery: true, explicit_rebind: Boolean(rebind), fixed_tmux_pane: true },
161
- statuses: ['pending', 'delivered', 'failed', 'expired'], queued_status: 'pending',
162
+ statuses: ['pending', 'delivered', 'failed', 'expired', 'stored'], queued_status: 'pending',
162
163
  semantics: { delivered: 'written to terminal; no application acknowledgement implied',
164
+ stored: 'durable reply to a nonterminal integration principal; never a terminal write',
163
165
  ack: 'explicit response_kind=ack', result: 'explicit response_kind=result; inspect task.status and evidence',
164
166
  snapshot: 'terminal observation, never proof of reading or task completion',
165
167
  timeout: 'stops waiting, does not cancel delivery',
@@ -207,6 +209,11 @@ export function collaborationRoutes({ store, resolveSession, deliver, rebind, re
207
209
  }));
208
210
  router.post('/reply', run(async (req, res, sessionId) => {
209
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
+ }
210
217
  if (!original.fromSessionId) {
211
218
  const link = original.metadata?.termdockTask;
212
219
  if (link?.taskId && req.app.locals.collaborationTasks) {
@@ -318,6 +318,8 @@ export class CollaborationStore {
318
318
  throw new Error('消息类型无效');
319
319
  const extras = validateExtras(input);
320
320
  const content = input.content;
321
+ if (typeof content !== 'string')
322
+ throw new CollaborationError('INVALID_MESSAGE', 'Message content must be text');
321
323
  if (Buffer.byteLength(content) > COLLAB_LIMITS.message_bytes)
322
324
  throw new CollaborationError('MESSAGE_TOO_LARGE', `Message exceeds ${COLLAB_LIMITS.message_bytes} UTF-8 bytes`, 413);
323
325
  if (!content.trim())
@@ -326,8 +328,14 @@ export class CollaborationStore {
326
328
  if (recipients.length === 0)
327
329
  throw new Error('没有有效的接收会话');
328
330
  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');
331
+ const key = input.idempotencyKey ? JSON.stringify([input.groupId, input.integrationOrigin ? `principal:${input.integrationOrigin.integrationId}` : input.fromSessionId, input.idempotencyKey]) : null;
332
+ const payload = [recipients.slice().sort(), input.kind, content, input.threadId ?? null, input.replyTo ?? null, extras.responseKind, extras.metadata, extras.task, extras.expiresAt];
333
+ if (input.integrationOrigin) {
334
+ if (input.fromSessionId !== null || group.federated || !/^[a-zA-Z0-9._-]{1,80}$/.test(input.integrationOrigin.integrationId))
335
+ throw new CollaborationError('INVALID_INTEGRATION_MESSAGE', 'Integration messages require an authenticated local nonterminal source');
336
+ payload.push(input.integrationOrigin);
337
+ }
338
+ const hash = crypto.createHash('sha256').update(stableJson(payload)).digest('hex');
331
339
  const previous = key ? this.document.idempotency?.[key] : null;
332
340
  if (previous && previous.expiresAt > now) {
333
341
  if (previous.hash !== hash)
@@ -340,6 +348,7 @@ export class CollaborationStore {
340
348
  const taken = this.takenIds();
341
349
  const threadId = input.threadId?.trim() || newCollaborationId(taken, this.drawId);
342
350
  const messages = recipients.map((toSessionId) => ({
351
+ ...(input.integrationOrigin ? { integrationOrigin: structuredClone(input.integrationOrigin) } : {}),
343
352
  idempotencyKey: input.idempotencyKey, responseKind: extras.responseKind, metadata: extras.metadata, task: extras.task, expiresAt: extras.expiresAt,
344
353
  sequence: this.nextSequence(),
345
354
  id: newCollaborationId(taken, this.drawId), groupId: group.id, fromSessionId: input.fromSessionId,
@@ -353,10 +362,51 @@ export class CollaborationStore {
353
362
  this.document.messages.push(...messages);
354
363
  if (key)
355
364
  (this.document.idempotency ??= {})[key] = { hash, ids: messages.map((message) => message.id), expiresAt: now + COLLAB_LIMITS.idempotency_retention_ms };
356
- this.persist();
365
+ this.persist(!!input.integrationOrigin);
357
366
  queueMicrotask(() => this.onMessageQueued?.());
358
367
  return messages;
359
368
  }
369
+ /** An original recipient can persist a reply to a nonterminal integration.
370
+ * This is a stored fact, not a fake terminal write or a new task. */
371
+ storeIntegrationReply(originalId, sessionId, content, input) {
372
+ const original = this.getMessage(originalId), group = original && this.getGroup(original.groupId);
373
+ if (!original?.integrationOrigin || original.fromSessionId !== null || original.toSessionId !== sessionId
374
+ || !group || group.deleted || group.federated || !group.sessionIds.includes(sessionId))
375
+ throw new CollaborationError('NOT_MESSAGE_RECIPIENT', 'Only the original local recipient can reply to this integration message', 403);
376
+ const extras = validateExtras(input);
377
+ if (extras.task || extras.metadata?.termdockTask)
378
+ throw new CollaborationError('INVALID_INTEGRATION_REPLY', 'Ordinary integration replies cannot carry task envelopes');
379
+ if (!extras.idempotencyKey)
380
+ throw new CollaborationError('INVALID_IDEMPOTENCY_KEY', 'Integration replies require an idempotency key');
381
+ if (typeof content !== 'string' || !content.trim())
382
+ throw new CollaborationError('INVALID_MESSAGE', 'Reply content must not be empty');
383
+ if (Buffer.byteLength(content) > COLLAB_LIMITS.message_bytes)
384
+ throw new CollaborationError('MESSAGE_TOO_LARGE', 'Reply exceeds 1 MiB', 413);
385
+ if (extras.expiresAt !== undefined && extras.expiresAt !== null)
386
+ throw new CollaborationError('STORED_REPLY_CANNOT_EXPIRE', 'A persisted reply is not a pending delivery and cannot expire');
387
+ const principalId = original.integrationOrigin.integrationId;
388
+ const key = JSON.stringify([original.groupId, `integration-reply:${principalId}:${sessionId}`, extras.idempotencyKey]);
389
+ const hash = crypto.createHash('sha256').update(stableJson([original.id, content, extras.responseKind, extras.metadata, extras.task])).digest('hex');
390
+ const previous = this.document.idempotency?.[key];
391
+ if (previous && previous.expiresAt > Date.now()) {
392
+ if (previous.hash !== hash)
393
+ throw new CollaborationError('IDEMPOTENCY_CONFLICT', 'Reply key was already used with different content', 409);
394
+ const reply = this.getMessage(previous.ids[0]);
395
+ if (!reply)
396
+ throw new CollaborationError('IDEMPOTENCY_RECORD_GONE', 'Original reply is no longer retained', 409);
397
+ return reply;
398
+ }
399
+ const now = Date.now();
400
+ const reply = { ...extras, id: newCollaborationId(this.takenIds(), this.drawId), sequence: this.nextSequence(),
401
+ groupId: original.groupId, fromSessionId: sessionId, toSessionId: `principal:${principalId}`, toPrincipalId: principalId,
402
+ kind: 'reply', content, threadId: original.threadId, replyTo: original.id, status: 'stored', createdAt: now, storedAt: now, deliveredAt: null, readAt: null };
403
+ if (Buffer.byteLength(JSON.stringify(reply)) > COLLAB_LIMITS.wire_bytes)
404
+ throw new CollaborationError('MESSAGE_WIRE_TOO_LARGE', 'Encoded reply exceeds the wire limit', 413);
405
+ this.document.messages.push(reply);
406
+ (this.document.idempotency ??= {})[key] = { hash, ids: [reply.id], expiresAt: now + COLLAB_LIMITS.idempotency_retention_ms };
407
+ this.persist(true);
408
+ return reply;
409
+ }
360
410
  listMessages(groupId, limit = 200) {
361
411
  return this.document.messages.filter((message) => message.groupId === groupId).slice(-Math.max(1, Math.min(limit, 500)));
362
412
  }
@@ -414,7 +464,7 @@ export class CollaborationStore {
414
464
  const now = Date.now();
415
465
  const changed = [];
416
466
  this.document.messages = this.document.messages.map((message) => {
417
- if (!ids.has(message.id) || message.status === 'expired' || message.status === 'failed' || message.status === 'read')
467
+ if (!ids.has(message.id) || message.status === 'stored' || message.status === 'expired' || message.status === 'failed' || message.status === 'read')
418
468
  return message;
419
469
  const updated = status === 'read'
420
470
  ? { ...message, status, deliveredAt: message.deliveredAt ?? now, readAt: now, readSource, deliverySource: message.deliverySource ?? (message.deliveredAt === null ? 'consumer_read' : 'legacy_or_unspecified') }
@@ -499,7 +549,7 @@ export class CollaborationStore {
499
549
  if (!message || typeof message !== 'object')
500
550
  continue;
501
551
  const group = this.getGroup(message.groupId);
502
- if (!group?.federated || group.deleted || !group.sessionIds.includes(message.toSessionId)
552
+ if (!group?.federated || group.deleted || message.status === 'stored' || message.integrationOrigin || message.toPrincipalId || !group.sessionIds.includes(message.toSessionId)
503
553
  || (message.fromSessionId !== null && !group.sessionIds.includes(message.fromSessionId))
504
554
  || typeof message.id !== 'string' || typeof message.threadId !== 'string' || !Number.isFinite(message.createdAt)
505
555
  || typeof message.content !== 'string'
@@ -581,15 +631,18 @@ export class CollaborationStore {
581
631
  const message = this.getMessage(id);
582
632
  if (!message)
583
633
  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);
634
+ const replies = this.document.messages.filter(reply => reply.replyTo === id
635
+ && (message.toPrincipalId ? reply.integrationOrigin?.integrationId === message.toPrincipalId : reply.fromSessionId === message.toSessionId)
636
+ && (message.integrationOrigin ? reply.toPrincipalId === message.integrationOrigin.integrationId : reply.toSessionId === message.fromSessionId));
585
637
  const diagnostic = this.diagnostic(id);
586
638
  const facts = terminalMessage(message, raw);
587
- return { message_id: id, thread_id: message.threadId, status: facts.status, queued_at: message.createdAt,
639
+ return { message_id: id, thread_id: message.threadId, status: facts.status, queued_at: message.status === 'stored' ? null : message.createdAt, stored_at: message.storedAt ?? null,
640
+ from_principal_id: message.integrationOrigin?.integrationId ?? null, to_principal_id: message.toPrincipalId ?? null,
588
641
  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'),
642
+ failure_reason: message.failureReason ?? null, delivery_semantics: message.status === 'stored' ? 'durable_storage' : facts.deliverySource ?? (facts.deliveredAt === null ? 'not_delivered' : 'legacy_or_unspecified'),
590
643
  snapshot: message.snapshot ? (raw ? message.snapshot : plainCollaborationSnapshot(message.snapshot)) : null,
591
644
  delivery: { status: facts.status,
592
- stage: facts.deliveredAt !== null ? 'terminal_written' : diagnostic?.remote_received_at ? 'remote_received' : 'queued',
645
+ stage: message.status === 'stored' ? 'stored' : facts.deliveredAt !== null ? 'terminal_written' : diagnostic?.remote_received_at ? 'remote_received' : 'queued',
593
646
  remote_received_at: diagnostic?.remote_received_at ?? null, delivered_at: facts.deliveredAt, error: message.failureReason ?? diagnostic?.last_error ?? null },
594
647
  reply: { status: replies.length ? 'received' : 'pending', ack_at: replies.find(reply => reply.responseKind === 'ack')?.createdAt ?? null, reply_ids: replies.map(reply => reply.id) },
595
648
  idempotency_key: message.idempotencyKey ?? null,
@@ -725,7 +778,7 @@ export class CollaborationStore {
725
778
  this.persist();
726
779
  return { message_id: fragment.message_id, received, total: state.total, complete: received === state.total };
727
780
  }
728
- persist() {
781
+ persist(durable = false) {
729
782
  const now = Date.now();
730
783
  for (const [key, record] of Object.entries(this.document.idempotency ?? {}))
731
784
  if (record.expiresAt <= now)
@@ -744,9 +797,26 @@ export class CollaborationStore {
744
797
  try {
745
798
  fs.mkdirSync(path.dirname(this.filePath), { recursive: true });
746
799
  const serialized = JSON.stringify(this.document);
747
- fs.writeFileSync(temporaryPath, serialized, { mode: 0o600 });
800
+ const descriptor = fs.openSync(temporaryPath, 'w', 0o600);
801
+ try {
802
+ fs.writeFileSync(descriptor, serialized);
803
+ if (durable)
804
+ fs.fsyncSync(descriptor);
805
+ }
806
+ finally {
807
+ fs.closeSync(descriptor);
808
+ }
748
809
  fs.renameSync(temporaryPath, this.filePath);
749
810
  this.persistedDocument = serialized;
811
+ if (durable) {
812
+ const directory = fs.openSync(path.dirname(this.filePath), 'r');
813
+ try {
814
+ fs.fsyncSync(directory);
815
+ }
816
+ finally {
817
+ fs.closeSync(directory);
818
+ }
819
+ }
750
820
  for (const listener of this.changeListeners) {
751
821
  try {
752
822
  listener();
@@ -1,5 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import { randomUUID } from 'node:crypto';
3
+ import { CollaborationError } from './collaborationProtocol.js';
4
+ import { taskPurpose, taskPurposeFilter } from './collaborationTaskPurpose.js';
3
5
  export async function executeTaskCommand(command, request, io) {
4
6
  const o = command.options, operation = command.operation;
5
7
  const bodyOptions = ['content', 'file', 'stdin', 'idempotency-key'];
@@ -7,7 +9,7 @@ export async function executeTaskCommand(command, request, io) {
7
9
  if (originOptions.some(key => o[key]) && !o.principal)
8
10
  throw new Error('External source metadata requires --principal');
9
11
  const allowed = {
10
- list: ['group'], get: [], create: [...bodyOptions, 'group', 'title', 'constraints', 'acceptance', 'assignee', 'coordinator', 'parent', 'depends-on', 'managed', 'reviewers', 'shared-directory', 'integration', 'work-type'],
12
+ list: ['group', 'purpose'], get: [], configure: [...bodyOptions, 'purpose', 'revision'], create: [...bodyOptions, 'purpose', 'group', 'title', 'constraints', 'acceptance', 'assignee', 'coordinator', 'parent', 'depends-on', 'managed', 'reviewers', 'shared-directory', 'integration', 'work-type'],
11
13
  answer: [...bodyOptions, 'decision'], respond: [...bodyOptions, 'attempt', 'to-event', 'evidence'],
12
14
  assign: [...bodyOptions, 'assignee', 'revision'], report: [...bodyOptions, 'attempt', 'status', 'evidence', 'summary'], ask: [...bodyOptions, 'attempt', 'options'],
13
15
  plan: [...bodyOptions, 'attempt', 'evidence'], review: [...bodyOptions, 'artifact', 'evidence', 'verdict'], comment: bodyOptions,
@@ -18,21 +20,37 @@ export async function executeTaskCommand(command, request, io) {
18
20
  for (const flag of Object.keys(o))
19
21
  if (![...allowed[operation], ...(o.principal ? originOptions : []), 'principal', 'session', 'json', 'jsonl', 'text', 'help'].includes(flag))
20
22
  throw new Error(`--${flag} is not supported by task ${operation}`);
21
- if (operation === 'list')
22
- return request('GET', `/tasks${o.group ? `?group=${encodeURIComponent(String(o.group))}` : ''}`);
23
+ if (o.purpose !== undefined)
24
+ (operation === 'list' ? taskPurposeFilter : taskPurpose)(o.purpose);
25
+ if (operation === 'configure' && o.purpose === undefined)
26
+ throw new Error('configure requires --purpose interactive|automation');
27
+ if (o.purpose !== undefined || operation === 'configure') {
28
+ const capabilities = await request('GET', '/capabilities');
29
+ const features = o.principal ? capabilities : capabilities.task_workflow;
30
+ if (features?.background_task_records !== true || features.task_purpose_configuration !== true || features.explicit_execution_state !== true)
31
+ throw new CollaborationError('BACKGROUND_TASK_RECORDS_UNSUPPORTED', 'Running service does not support background task records', 409);
32
+ }
33
+ if (operation === 'list') {
34
+ const query = new URLSearchParams();
35
+ if (o.group)
36
+ query.set('group', String(o.group));
37
+ if (o.purpose)
38
+ query.set('purpose', String(o.purpose));
39
+ return request('GET', `/tasks${query.size ? `?${query}` : ''}`);
40
+ }
23
41
  if (operation === 'get')
24
42
  return request('GET', `/tasks/${encodeURIComponent(command.target)}`);
25
43
  const contentSources = [o.content, o.file, o.stdin].filter(Boolean).length;
26
44
  if (contentSources > 1)
27
45
  throw new Error('Choose --content, --file or --stdin');
28
46
  const content = o.file ? fs.readFileSync(String(o.file), 'utf8') : o.stdin ? await io.stdin?.() : o.content;
29
- if (!['assign', 'close', 'reopen', 'request-review', 'pause', 'resume', 'retry'].includes(operation) && (typeof content !== 'string' || !content.trim()))
47
+ if (!['configure', 'assign', 'close', 'reopen', 'request-review', 'pause', 'resume', 'retry'].includes(operation) && (typeof content !== 'string' || !content.trim()))
30
48
  throw new Error('This task operation requires --content, --file or --stdin');
31
49
  const idempotencyKey = String(o['idempotency-key'] ?? randomUUID());
32
50
  const expectedRevision = o.revision === undefined ? undefined : Number(o.revision);
33
51
  if (expectedRevision !== undefined && (!Number.isSafeInteger(expectedRevision) || expectedRevision < 1))
34
52
  throw new Error('--revision must be a positive integer');
35
- if (['assign', 'revise', 'close', 'reopen', 'request-review', 'coordinate', 'pause', 'resume', 'retry'].includes(operation) && expectedRevision === undefined)
53
+ if (['configure', 'assign', 'revise', 'close', 'reopen', 'request-review', 'coordinate', 'pause', 'resume', 'retry'].includes(operation) && expectedRevision === undefined)
36
54
  throw new Error('Read task get first, then supply --revision to avoid overwriting another decision');
37
55
  if (['assign', 'request-review'].includes(operation) && !o.assignee)
38
56
  throw new Error('This task operation requires --assignee');
@@ -46,7 +64,7 @@ export async function executeTaskCommand(command, request, io) {
46
64
  if (!o.title || !o.group)
47
65
  throw new Error('task create requires --title and --group');
48
66
  return request('POST', '/tasks', { input: { idempotencyKey, groupId: o.group, title: o.title, spec: content,
49
- constraints: o.constraints, acceptance: o.acceptance, assigneeSessionId: o.assignee, coordinatorSessionId: o.coordinator,
67
+ purpose: o.purpose, constraints: o.constraints, acceptance: o.acceptance, assigneeSessionId: o.assignee, coordinatorSessionId: o.coordinator,
50
68
  parentTaskId: o.parent, dependsOn: o['depends-on'] ? String(o['depends-on']).split(',').filter(Boolean) : undefined,
51
69
  integration: o.integration === true, managed: o.managed === true, isolated: o['shared-directory'] !== true, workType: o['work-type'],
52
70
  reviewerSessionIds: o.reviewers ? String(o.reviewers).split(',').filter(Boolean) : undefined } });
@@ -55,7 +73,7 @@ export async function executeTaskCommand(command, request, io) {
55
73
  if (options !== undefined && (!Array.isArray(options) || options.some(v => typeof v !== 'string')))
56
74
  throw new Error('--options must be a JSON array of strings');
57
75
  return request('POST', `/tasks/${encodeURIComponent(command.target)}`, { input: {
58
- replyToEventId: o['to-event'], decisionId: o.decision, kind: operation === 'plan' ? 'submit-plan' : operation, idempotencyKey, expectedRevision, content,
76
+ purpose: o.purpose, replyToEventId: o['to-event'], decisionId: o.decision, kind: operation === 'plan' ? 'submit-plan' : operation, idempotencyKey, expectedRevision, content,
59
77
  assigneeSessionId: o.assignee, attemptId: o.attempt, status: o.status, artifactId: o.artifact, options, verdict: o.verdict,
60
78
  evidence: o.evidence ? JSON.parse(String(o.evidence)) : undefined, summary: o.summary,
61
79
  } });
@@ -0,0 +1,20 @@
1
+ import { CollaborationError } from './collaborationProtocol.js';
2
+ export function taskPurpose(value = 'interactive') {
3
+ if (value !== 'interactive' && value !== 'automation')
4
+ throw new CollaborationError('INVALID_TASK_PURPOSE', 'purpose must be interactive or automation');
5
+ return value;
6
+ }
7
+ export function taskPurposeFilter(value = 'all') {
8
+ return value === 'all' ? value : taskPurpose(value);
9
+ }
10
+ /** Only ordered, explicit reports count. Revisions invalidate the previous round;
11
+ * comments and responses leave it intact. Delivery never implies completion. */
12
+ export function taskExecution(task) {
13
+ const report = task.events.filter(e => e.kind === 'report' && e.attemptId === task.activeAttemptId).at(-1);
14
+ const revision = task.events.filter(e => e.kind === 'revise' && e.attemptId === task.activeAttemptId).at(-1);
15
+ if (!task.activeAttemptId || !report?.reportStatus || revision && revision.sequence > report.sequence) {
16
+ return { status: task.activeAttemptId ? 'awaiting_report' : 'unassigned', attemptId: task.activeAttemptId, reportEventId: null, artifactId: null, reportedAt: null };
17
+ }
18
+ return { status: report.reportStatus, attemptId: task.activeAttemptId, reportEventId: report.id,
19
+ artifactId: report.reportStatus === 'complete' ? report.artifactId ?? null : null, reportedAt: report.createdAt };
20
+ }
@@ -40,7 +40,7 @@ export function collaborationTaskRoutes(options) {
40
40
  router.get('/', run((req, { tasks, actor }) => {
41
41
  const requested = typeof req.query.group === 'string' ? groupId(req.query.group) : null;
42
42
  const groups = requested ? [requested] : actor ? options.store.groupsForSession(actor.sessionId).map(g => g.id) : options.store.list().map(g => g.id);
43
- return { tasks: groups.flatMap(id => tasks.list(id, actor)) };
43
+ return { tasks: groups.flatMap(id => tasks.list(id, actor, req.query.purpose ?? (options.agent ? 'all' : 'interactive'))) };
44
44
  }));
45
45
  router.get('/:taskId', run(async (req, { tasks, actor }) => ({ task: await tasks.get(req.params.taskId, actor) })));
46
46
  const input = (req, peers) => {
@@ -1,6 +1,7 @@
1
1
  import { CollaborationError } from './collaborationProtocol.js';
2
2
  import { captureTaskCommit } from './collaborationTaskWorkspace.js';
3
3
  import { taskBundleChunk } from './collaborationTaskBundles.js';
4
+ import { taskExecution, taskPurpose, taskPurposeFilter } from './collaborationTaskPurpose.js';
4
5
  import { taskMemberKey } from './collaborationTaskTypes.js';
5
6
  /** One authority per task; replicas are records, never competing decision writers. */
6
7
  export class CollaborationTaskService {
@@ -56,12 +57,13 @@ export class CollaborationTaskService {
56
57
  const template = task.workflow?.reviewers.some(m => taskMemberKey(m) === taskMemberKey(member));
57
58
  return { member, sessionId, launchProfile: group.memberLaunchProfiles?.[sessionId], role: `${template ? '此目标的执行成员模板;' : ''}${group.roles?.[sessionId] ?? ''}` };
58
59
  });
59
- return { ...task, children, roster, launchProfiles: summary ? undefined : group?.launchProfiles, defaultLaunchProfileId: summary ? undefined : group?.defaultLaunchProfileId, memberSessions, replica: task.ownerServiceId !== this.self, summaryOnly: summary, outbox: (summary ? [] : this.tasks.pending(task.id))
60
+ return { ...task, purpose: taskPurpose(task.purpose), ...(task.purpose === 'automation' ? { execution: task.execution ?? taskExecution(task) } : {}), children, roster, launchProfiles: summary ? undefined : group?.launchProfiles, defaultLaunchProfileId: summary ? undefined : group?.defaultLaunchProfileId, memberSessions, replica: task.ownerServiceId !== this.self, summaryOnly: summary, outbox: (summary ? [] : this.tasks.pending(task.id))
60
61
  .map(({ id, attemptId, messageId, lastError }) => ({ id, attemptId, messageId, lastError })) };
61
62
  }
62
- list(groupId, actor) {
63
+ list(groupId, actor, purpose = 'all') {
63
64
  this.group(groupId, actor);
64
- return this.tasks.list([groupId], true).map(task => this.view(task, true));
65
+ const filter = taskPurposeFilter(purpose);
66
+ return this.tasks.list([groupId], true).filter(task => filter === 'all' || taskPurpose(task.purpose) === filter).map(task => this.view(task, true));
65
67
  }
66
68
  async get(taskId, actor) {
67
69
  const task = this.tasks.get(taskId);
@@ -71,8 +73,10 @@ export class CollaborationTaskService {
71
73
  return this.view(task);
72
74
  }
73
75
  async create(input, actor, origin) {
74
- this.group(input.groupId, actor);
76
+ const group = this.group(input.groupId, actor);
75
77
  this.validateMembers(input.groupId, input);
78
+ if (input.purpose === 'automation' && group.federated)
79
+ throw new CollaborationError('BACKGROUND_TASK_LOCAL_ONLY', '首版后台自动化记录使用本机协作组', 409);
76
80
  const dependencies = [...(input.dependsOn ?? []), ...(input.parentTaskId ? [input.parentTaskId] : [])];
77
81
  const authority = dependencies.length ? this.tasks.get(dependencies[0])?.ownerServiceId : this.self;
78
82
  if (!authority)
@@ -97,8 +101,10 @@ export class CollaborationTaskService {
97
101
  const task = this.tasks.get(taskId);
98
102
  if (!task)
99
103
  throw new CollaborationError('TASK_NOT_FOUND', '任务不存在', 404);
100
- this.group(task.groupId, actor);
104
+ const group = this.group(task.groupId, actor);
101
105
  this.validateMembers(task.groupId, input);
106
+ if (input.kind === 'configure' && input.purpose === 'automation' && group.federated)
107
+ throw new CollaborationError('BACKGROUND_TASK_LOCAL_ONLY', '首版后台自动化记录使用本机协作组', 409);
102
108
  if (origin && task.ownerServiceId !== this.self)
103
109
  throw new CollaborationError('INTEGRATION_LOCAL_ONLY', '首版集成任务须由本机服务保存', 409);
104
110
  if (task.ownerServiceId !== this.self) {
@@ -131,7 +137,8 @@ export class CollaborationTaskService {
131
137
  commit: child.artifacts.find(a => a.id === child.acceptedArtifactId)?.evidence?.commit })) } };
132
138
  }
133
139
  const updated = this.tasks.apply(taskId, input, actor, origin);
134
- void this.flush();
140
+ if (input.kind !== 'configure')
141
+ void this.flush();
135
142
  return this.view(updated);
136
143
  }
137
144
  heads(groupIds) { return this.tasks.heads(groupIds, this.self); }
@@ -261,6 +268,8 @@ export class CollaborationTaskService {
261
268
  }
262
269
  receipt(messageId) {
263
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');
264
273
  return { messageId, status: receipt.status, deliveredAt: receipt.delivered_at, error: receipt.delivery.error };
265
274
  }
266
275
  start() { this.timer = setInterval(() => void this.flush(), 1500); this.timer.unref(); void this.flush(); }