termdock 1.4.313 → 1.4.314
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.
- package/dist/client/assets/{App-ChS2-lkz.js → App-BCTGnwU1.js} +53 -53
- package/dist/client/assets/App-BCTGnwU1.js.br +0 -0
- package/dist/client/assets/App-BCTGnwU1.js.gz +0 -0
- package/dist/client/assets/{ArchitecturePanel-JtjyyJoW.js → ArchitecturePanel-CVTIYbyj.js} +6 -6
- package/dist/client/assets/ArchitecturePanel-CVTIYbyj.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-CVTIYbyj.js.gz +0 -0
- package/dist/client/assets/{ChangeWalkthroughPanel-BlQ5-VPp.js → ChangeWalkthroughPanel-BKsuofw6.js} +2 -2
- package/dist/client/assets/ChangeWalkthroughPanel-BKsuofw6.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BKsuofw6.js.gz +0 -0
- package/dist/client/assets/{CollaborationMainWorkspace-aFLAMqDr.js → CollaborationMainWorkspace-DZ0qwLUz.js} +1 -1
- package/dist/client/assets/CollaborationMainWorkspace-DZ0qwLUz.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-DZ0qwLUz.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-BLQNknOq.js +2 -0
- package/dist/client/assets/{ComputerControlView-TAKDbBUl.js → ComputerControlView-DN6cXOnb.js} +2 -2
- package/dist/client/assets/ComputerControlView-DN6cXOnb.js.br +0 -0
- package/dist/client/assets/ComputerControlView-DN6cXOnb.js.gz +0 -0
- package/dist/client/assets/{DagPlayground-CR4uDFOx.js → DagPlayground-ZgszT1TH.js} +1 -1
- package/dist/client/assets/DagPlayground-ZgszT1TH.js.br +0 -0
- package/dist/client/assets/DagPlayground-ZgszT1TH.js.gz +0 -0
- package/dist/client/assets/{DiffLab-BqeHMDOy.js → DiffLab-D_9U-zfZ.js} +1 -1
- package/dist/client/assets/DiffLab-D_9U-zfZ.js.br +0 -0
- package/dist/client/assets/DiffLab-D_9U-zfZ.js.gz +0 -0
- package/dist/client/assets/{DiffReview-DqrOcRfP.js → DiffReview-8Pfe_BNO.js} +1 -1
- package/dist/client/assets/DiffReview-8Pfe_BNO.js.br +0 -0
- package/dist/client/assets/DiffReview-8Pfe_BNO.js.gz +0 -0
- package/dist/client/assets/{DiffReviewLab-CEYpbrL6.js → DiffReviewLab-p3ZSl0P2.js} +1 -1
- package/dist/client/assets/DiffReviewLab-p3ZSl0P2.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-p3ZSl0P2.js.gz +0 -0
- package/dist/client/assets/{DiffViewer-F-uQSmhG.js → DiffViewer-Cz64sLJT.js} +8 -8
- package/dist/client/assets/DiffViewer-Cz64sLJT.js.br +0 -0
- package/dist/client/assets/DiffViewer-Cz64sLJT.js.gz +0 -0
- package/dist/client/assets/{ModelPreview-KmXFkgij.js → ModelPreview-B28x060d.js} +1 -1
- package/dist/client/assets/ModelPreview-B28x060d.js.br +0 -0
- package/dist/client/assets/ModelPreview-B28x060d.js.gz +0 -0
- package/dist/client/assets/{RightSidebar-VMdXpo4h.js → RightSidebar-SIqDjggD.js} +3 -3
- package/dist/client/assets/RightSidebar-SIqDjggD.js.br +0 -0
- package/dist/client/assets/RightSidebar-SIqDjggD.js.gz +0 -0
- package/dist/client/assets/{abnfDiagram-VCTEODGH-CAMk1C0N.js → abnfDiagram-VCTEODGH--t0bQKCQ.js} +1 -1
- package/dist/client/assets/abnfDiagram-VCTEODGH--t0bQKCQ.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH--t0bQKCQ.js.gz +0 -0
- package/dist/client/assets/{app-CHxLSuGQ.js → app-DWidKLU2.js} +13 -13
- package/dist/client/assets/app-DWidKLU2.js.br +0 -0
- package/dist/client/assets/app-DWidKLU2.js.gz +0 -0
- package/dist/client/assets/{arc-CYG_-1vC.js → arc-DMWsmL7t.js} +1 -1
- package/dist/client/assets/arc-DMWsmL7t.js.br +0 -0
- package/dist/client/assets/arc-DMWsmL7t.js.gz +0 -0
- package/dist/client/assets/{architectureDiagram-5GKGNRK7-BqHzezLR.js → architectureDiagram-5GKGNRK7-BYkvSKnR.js} +1 -1
- package/dist/client/assets/architectureDiagram-5GKGNRK7-BYkvSKnR.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-BYkvSKnR.js.gz +0 -0
- package/dist/client/assets/{blockDiagram-I7D4REHJ-D6GWRO_Z.js → blockDiagram-I7D4REHJ-B574d9TS.js} +1 -1
- package/dist/client/assets/blockDiagram-I7D4REHJ-B574d9TS.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-B574d9TS.js.gz +0 -0
- package/dist/client/assets/{c4Diagram-7LVT6UL2-CutmGIGb.js → c4Diagram-7LVT6UL2-D__CwUct.js} +1 -1
- package/dist/client/assets/c4Diagram-7LVT6UL2-D__CwUct.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-D__CwUct.js.gz +0 -0
- package/dist/client/assets/channel-BonXK9tr.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-DwiUpGFu.js → chunk-2Q5K7J3B-BvwmL72I.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-C35gLPSH.js → chunk-5VM5RSS4-BktO-a5l.js} +1 -1
- package/dist/client/assets/{chunk-F27PBJKO-CNptP19P.js → chunk-F27PBJKO-DeClqFBe.js} +1 -1
- package/dist/client/assets/chunk-F27PBJKO-DeClqFBe.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-DeClqFBe.js.gz +0 -0
- package/dist/client/assets/{chunk-IMKFNOWR-DHdpB9A0.js → chunk-IMKFNOWR-DBRCslkn.js} +1 -1
- package/dist/client/assets/chunk-IMKFNOWR-DBRCslkn.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-DBRCslkn.js.gz +0 -0
- package/dist/client/assets/{chunk-JWPE2WC7-CrKSOzdE.js → chunk-JWPE2WC7-BfpbqgrR.js} +1 -1
- package/dist/client/assets/{chunk-POPQ4Y6H-CiZGLRSk.js → chunk-POPQ4Y6H-Bw-dqneu.js} +1 -1
- package/dist/client/assets/{chunk-SVP7TREG-B70pX8OB.js → chunk-SVP7TREG-BdxOWBjP.js} +1 -1
- package/dist/client/assets/chunk-SVP7TREG-BdxOWBjP.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-BdxOWBjP.js.gz +0 -0
- package/dist/client/assets/{chunk-TICWLB2K-Ba1mI99F.js → chunk-TICWLB2K-BtNXehgz.js} +1 -1
- package/dist/client/assets/chunk-TICWLB2K-BtNXehgz.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-BtNXehgz.js.gz +0 -0
- package/dist/client/assets/{chunk-XXDRQBXY-DmKIl18h.js → chunk-XXDRQBXY-CUxGdImw.js} +1 -1
- package/dist/client/assets/classDiagram-ZZMXUADV-CXO_pgVP.js +1 -0
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CXO_pgVP.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-C-NMDUHW.js → cose-bilkent-JH36ORCC-Buod2YZ0.js} +1 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-Buod2YZ0.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-Buod2YZ0.js.gz +0 -0
- package/dist/client/assets/{cynefin-OW5HDTMX-CZgEc1ab.js → cynefin-OW5HDTMX-i2eiybdj.js} +1 -1
- package/dist/client/assets/cynefin-OW5HDTMX-i2eiybdj.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-i2eiybdj.js.gz +0 -0
- package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-CHQeSNm2.js → cynefinDiagram-5FMLGOSQ-D_YF2Yi3.js} +1 -1
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-D_YF2Yi3.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-D_YF2Yi3.js.gz +0 -0
- package/dist/client/assets/{dagre-GXQ25YYZ-B_jkMdPS.js → dagre-GXQ25YYZ-CfLEvt7Z.js} +1 -1
- package/dist/client/assets/dagre-GXQ25YYZ-CfLEvt7Z.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-CfLEvt7Z.js.gz +0 -0
- package/dist/client/assets/{diagram-S7CK7UJ4-nv6Lnsrp.js → diagram-S7CK7UJ4-BV0Wackt.js} +1 -1
- package/dist/client/assets/diagram-S7CK7UJ4-BV0Wackt.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-BV0Wackt.js.gz +0 -0
- package/dist/client/assets/{diagram-UQ7AKVKN-BZf2fzaA.js → diagram-UQ7AKVKN-jwaoLY_P.js} +1 -1
- package/dist/client/assets/diagram-UQ7AKVKN-jwaoLY_P.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-jwaoLY_P.js.gz +0 -0
- package/dist/client/assets/{diagram-VSXAHHWV-CxL0_BH8.js → diagram-VSXAHHWV-CuAFyl0n.js} +1 -1
- package/dist/client/assets/diagram-VSXAHHWV-CuAFyl0n.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CuAFyl0n.js.gz +0 -0
- package/dist/client/assets/{diagram-VX7I27RA-Dm9MqpIE.js → diagram-VX7I27RA-bbmA7rRk.js} +1 -1
- package/dist/client/assets/diagram-VX7I27RA-bbmA7rRk.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-bbmA7rRk.js.gz +0 -0
- package/dist/client/assets/{diagram-Z3DM3KII-BIfTa7P5.js → diagram-Z3DM3KII-CX3-Mxwo.js} +1 -1
- package/dist/client/assets/diagram-Z3DM3KII-CX3-Mxwo.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-CX3-Mxwo.js.gz +0 -0
- package/dist/client/assets/{ebnfDiagram-PWID7BFC-D4XhCDFS.js → ebnfDiagram-PWID7BFC-Pkq6omzy.js} +1 -1
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Pkq6omzy.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-Pkq6omzy.js.gz +0 -0
- package/dist/client/assets/{erDiagram-RLTQ6QDP-Dxd48dXC.js → erDiagram-RLTQ6QDP-CjdD0JCI.js} +1 -1
- package/dist/client/assets/erDiagram-RLTQ6QDP-CjdD0JCI.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-CjdD0JCI.js.gz +0 -0
- package/dist/client/assets/{flowDiagram-HODETNUW-BXBMsEmE.js → flowDiagram-HODETNUW-DKECYHVY.js} +1 -1
- package/dist/client/assets/flowDiagram-HODETNUW-DKECYHVY.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-DKECYHVY.js.gz +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-CBfDd-KZ.js → ganttDiagram-EL5Y4UJY-BPCGT0Kd.js} +1 -1
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-BPCGT0Kd.js.br +0 -0
- package/dist/client/assets/{ganttDiagram-EL5Y4UJY-CBfDd-KZ.js.gz → ganttDiagram-EL5Y4UJY-BPCGT0Kd.js.gz} +0 -0
- package/dist/client/assets/{gestureArbiter-DXLmNGli.js → gestureArbiter-DVPPQnKq.js} +1 -1
- package/dist/client/assets/{gestureArbiter-DXLmNGli.js.br → gestureArbiter-DVPPQnKq.js.br} +0 -0
- package/dist/client/assets/gestureArbiter-DVPPQnKq.js.gz +0 -0
- package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-DtLM68fN.js → gitGraphDiagram-WWUBYQGX-DFCvVJ6L.js} +1 -1
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DFCvVJ6L.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DFCvVJ6L.js.gz +0 -0
- package/dist/client/assets/{index-KuPaZyuZ.css → index-CSp4mPGE.css} +1 -1
- package/dist/client/assets/index-CSp4mPGE.css.br +0 -0
- package/dist/client/assets/{index-KuPaZyuZ.css.gz → index-CSp4mPGE.css.gz} +0 -0
- package/dist/client/assets/{infoDiagram-27XIBGKW-FRhqdJvW.js → infoDiagram-27XIBGKW-BTL8-sSN.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-5VMMS53U-DAHBvAZ-.js → ishikawaDiagram-5VMMS53U-Xtc0AfGK.js} +1 -1
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-Xtc0AfGK.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-Xtc0AfGK.js.gz +0 -0
- package/dist/client/assets/{journeyDiagram-3NMN7TZE-CVuWuL3Z.js → journeyDiagram-3NMN7TZE-BLtTDKtV.js} +1 -1
- package/dist/client/assets/journeyDiagram-3NMN7TZE-BLtTDKtV.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-BLtTDKtV.js.gz +0 -0
- package/dist/client/assets/{kanban-definition-UXKFOSKX-ByQSUVJV.js → kanban-definition-UXKFOSKX-C65azoTU.js} +1 -1
- package/dist/client/assets/kanban-definition-UXKFOSKX-C65azoTU.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-C65azoTU.js.gz +0 -0
- package/dist/client/assets/{layout-Bb3Nb1c4.js → layout-TEicKqeV.js} +1 -1
- package/dist/client/assets/layout-TEicKqeV.js.br +0 -0
- package/dist/client/assets/layout-TEicKqeV.js.gz +0 -0
- package/dist/client/assets/{linear-DHMMQlOo.js → linear-DQSikGMo.js} +1 -1
- package/dist/client/assets/{linear-DHMMQlOo.js.br → linear-DQSikGMo.js.br} +0 -0
- package/dist/client/assets/linear-DQSikGMo.js.gz +0 -0
- package/dist/client/assets/{mermaid.core-BYfniK0H.js → mermaid.core-vZB1UnWX.js} +5 -5
- package/dist/client/assets/mermaid.core-vZB1UnWX.js.br +0 -0
- package/dist/client/assets/mermaid.core-vZB1UnWX.js.gz +0 -0
- package/dist/client/assets/{mindmap-definition-YA3MSWOX-EDLD5AyH.js → mindmap-definition-YA3MSWOX-B-coBapV.js} +1 -1
- package/dist/client/assets/mindmap-definition-YA3MSWOX-B-coBapV.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-B-coBapV.js.gz +0 -0
- package/dist/client/assets/{passwordBootstrap-DCrtirwT.js → passwordBootstrap-LSQpoVpD.js} +1 -1
- package/dist/client/assets/passwordBootstrap-LSQpoVpD.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-LSQpoVpD.js.gz +0 -0
- package/dist/client/assets/{pegDiagram-XKGWAZYB-BHYhkqJN.js → pegDiagram-XKGWAZYB--jWB5n1V.js} +1 -1
- package/dist/client/assets/pegDiagram-XKGWAZYB--jWB5n1V.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB--jWB5n1V.js.gz +0 -0
- package/dist/client/assets/{pieDiagram-E7YTZNPT-BUIJlswC.js → pieDiagram-E7YTZNPT-DmAqsAUK.js} +1 -1
- package/dist/client/assets/pieDiagram-E7YTZNPT-DmAqsAUK.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-DmAqsAUK.js.gz +0 -0
- package/dist/client/assets/{quadrantDiagram-AXDQQJYC-D8Im-7yC.js → quadrantDiagram-AXDQQJYC-CqsfUApA.js} +1 -1
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-CqsfUApA.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-CqsfUApA.js.gz +0 -0
- package/dist/client/assets/{railroadDiagram-O6MQD6OU-D7n6z2G1.js → railroadDiagram-O6MQD6OU-CzvYijD5.js} +1 -1
- package/dist/client/assets/railroadDiagram-O6MQD6OU-CzvYijD5.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-CzvYijD5.js.gz +0 -0
- package/dist/client/assets/{rdpSession-DJlNIbVk.js → rdpSession-DGvDRc95.js} +1 -1
- package/dist/client/assets/rdpSession-DGvDRc95.js.br +0 -0
- package/dist/client/assets/rdpSession-DGvDRc95.js.gz +0 -0
- package/dist/client/assets/{referenceLongPress-DFSrssSr.js → referenceLongPress-BwP_B3l2.js} +1 -1
- package/dist/client/assets/referenceLongPress-BwP_B3l2.js.br +0 -0
- package/dist/client/assets/referenceLongPress-BwP_B3l2.js.gz +0 -0
- package/dist/client/assets/{requirementDiagram-BXWQKSXE-Ki0rR-F7.js → requirementDiagram-BXWQKSXE-E2FSO_hc.js} +1 -1
- package/dist/client/assets/requirementDiagram-BXWQKSXE-E2FSO_hc.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-E2FSO_hc.js.gz +0 -0
- package/dist/client/assets/{sankeyDiagram-P5KCCOFB-36NmMqsW.js → sankeyDiagram-P5KCCOFB-uSCR6gDg.js} +1 -1
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-uSCR6gDg.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-uSCR6gDg.js.gz +0 -0
- package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-5vsPuFyO.js → sequenceDiagram-WJ2MYXX4-BqxdP03l.js} +1 -1
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BqxdP03l.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BqxdP03l.js.gz +0 -0
- package/dist/client/assets/{sizeCapture-INFHLROL-DbAulwBg.js → sizeCapture-INFHLROL-B7GSZxIt.js} +1 -1
- package/dist/client/assets/sizeCapture-INFHLROL-B7GSZxIt.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-B7GSZxIt.js.gz +0 -0
- package/dist/client/assets/{sparkles-CapLTKAs.js → sparkles-DBVpVoIl.js} +1 -1
- package/dist/client/assets/{stateDiagram-D77RDMKH-Bgwe0g5S.js → stateDiagram-D77RDMKH-DJ9UpQL9.js} +1 -1
- package/dist/client/assets/stateDiagram-D77RDMKH-DJ9UpQL9.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-DJ9UpQL9.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-P9vVPNJW.js +1 -0
- package/dist/client/assets/{swimlanes-42K2YHIH-COGAWe-g.js → swimlanes-42K2YHIH-B6f_z8xv.js} +1 -1
- package/dist/client/assets/swimlanes-42K2YHIH-B6f_z8xv.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-B6f_z8xv.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-Deucv-sT.js +8 -0
- package/dist/client/assets/{timeline-definition-24CTP7MA-C4gt5z68.js → timeline-definition-24CTP7MA-CMNs0nui.js} +1 -1
- package/dist/client/assets/timeline-definition-24CTP7MA-CMNs0nui.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-CMNs0nui.js.gz +0 -0
- package/dist/client/assets/{triangle-alert-xPHw25jD.js → triangle-alert-CN3m1g76.js} +1 -1
- package/dist/client/assets/{vennDiagram-4TSXK5OY-CIEXh9oD.js → vennDiagram-4TSXK5OY-B2glpBlo.js} +1 -1
- package/dist/client/assets/vennDiagram-4TSXK5OY-B2glpBlo.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-B2glpBlo.js.gz +0 -0
- package/dist/client/assets/{videoCompression-CUkCS2-q.js → videoCompression-DwronsgA.js} +1 -1
- package/dist/client/assets/videoCompression-DwronsgA.js.br +0 -0
- package/dist/client/assets/videoCompression-DwronsgA.js.gz +0 -0
- package/dist/client/assets/{wardleyDiagram-VM6X3IG4-gQhcW1vd.js → wardleyDiagram-VM6X3IG4-DyMNl-ZC.js} +1 -1
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-DyMNl-ZC.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-DyMNl-ZC.js.gz +0 -0
- package/dist/client/assets/{xychartDiagram-S5SC5T6Z-BmIcdejz.js → xychartDiagram-S5SC5T6Z-DhZFXjY5.js} +1 -1
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DhZFXjY5.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-DhZFXjY5.js.gz +0 -0
- package/dist/client/index.html +3 -2
- package/dist/client/index.html.br +0 -0
- package/dist/client/index.html.gz +0 -0
- package/dist/client/ipad-status-bar.js +12 -0
- package/dist/client/sw.js +1 -1
- package/dist/client/sw.js.br +0 -0
- package/dist/client/sw.js.gz +0 -0
- package/dist/server/agent/collaborationCli.js +17 -5
- package/dist/server/agent/collaborationStore.js +25 -2
- package/dist/server/agent/collaborationTmuxDelivery.js +11 -3
- package/dist/server/agent/integrationCli.js +8 -3
- package/dist/server/agent/integrationServer.js +4 -1
- package/dist/server/agent/integrationSessions.js +147 -13
- package/dist/server/agent/promptDelivery.js +7 -1
- package/dist/server/agent/sessionLaunchDiagnostics.js +26 -0
- package/dist/server/agent/terminalInput.js +9 -1
- package/dist/server/routes/terminal.js +48 -19
- package/docs/integrations.md +98 -0
- package/package.json +1 -1
- package/runtime-manifest.json +3 -3
- package/dist/client/assets/App-ChS2-lkz.js.br +0 -0
- package/dist/client/assets/App-ChS2-lkz.js.gz +0 -0
- package/dist/client/assets/ArchitecturePanel-JtjyyJoW.js.br +0 -0
- package/dist/client/assets/ArchitecturePanel-JtjyyJoW.js.gz +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BlQ5-VPp.js.br +0 -0
- package/dist/client/assets/ChangeWalkthroughPanel-BlQ5-VPp.js.gz +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-aFLAMqDr.js.br +0 -0
- package/dist/client/assets/CollaborationMainWorkspace-aFLAMqDr.js.gz +0 -0
- package/dist/client/assets/CollaborationTaskContent-BDQMRhU9.js +0 -2
- package/dist/client/assets/ComputerControlView-TAKDbBUl.js.br +0 -0
- package/dist/client/assets/ComputerControlView-TAKDbBUl.js.gz +0 -0
- package/dist/client/assets/DagPlayground-CR4uDFOx.js.br +0 -0
- package/dist/client/assets/DagPlayground-CR4uDFOx.js.gz +0 -0
- package/dist/client/assets/DiffLab-BqeHMDOy.js.br +0 -0
- package/dist/client/assets/DiffLab-BqeHMDOy.js.gz +0 -0
- package/dist/client/assets/DiffReview-DqrOcRfP.js.br +0 -0
- package/dist/client/assets/DiffReview-DqrOcRfP.js.gz +0 -0
- package/dist/client/assets/DiffReviewLab-CEYpbrL6.js.br +0 -0
- package/dist/client/assets/DiffReviewLab-CEYpbrL6.js.gz +0 -0
- package/dist/client/assets/DiffViewer-F-uQSmhG.js.br +0 -0
- package/dist/client/assets/DiffViewer-F-uQSmhG.js.gz +0 -0
- package/dist/client/assets/ModelPreview-KmXFkgij.js.br +0 -0
- package/dist/client/assets/ModelPreview-KmXFkgij.js.gz +0 -0
- package/dist/client/assets/RightSidebar-VMdXpo4h.js.br +0 -0
- package/dist/client/assets/RightSidebar-VMdXpo4h.js.gz +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-CAMk1C0N.js.br +0 -0
- package/dist/client/assets/abnfDiagram-VCTEODGH-CAMk1C0N.js.gz +0 -0
- package/dist/client/assets/app-CHxLSuGQ.js.br +0 -0
- package/dist/client/assets/app-CHxLSuGQ.js.gz +0 -0
- package/dist/client/assets/arc-CYG_-1vC.js.br +0 -0
- package/dist/client/assets/arc-CYG_-1vC.js.gz +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-BqHzezLR.js.br +0 -0
- package/dist/client/assets/architectureDiagram-5GKGNRK7-BqHzezLR.js.gz +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-D6GWRO_Z.js.br +0 -0
- package/dist/client/assets/blockDiagram-I7D4REHJ-D6GWRO_Z.js.gz +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-CutmGIGb.js.br +0 -0
- package/dist/client/assets/c4Diagram-7LVT6UL2-CutmGIGb.js.gz +0 -0
- package/dist/client/assets/channel-B3BKM2gg.js +0 -1
- package/dist/client/assets/chunk-F27PBJKO-CNptP19P.js.br +0 -0
- package/dist/client/assets/chunk-F27PBJKO-CNptP19P.js.gz +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-DHdpB9A0.js.br +0 -0
- package/dist/client/assets/chunk-IMKFNOWR-DHdpB9A0.js.gz +0 -0
- package/dist/client/assets/chunk-SVP7TREG-B70pX8OB.js.br +0 -0
- package/dist/client/assets/chunk-SVP7TREG-B70pX8OB.js.gz +0 -0
- package/dist/client/assets/chunk-TICWLB2K-Ba1mI99F.js.br +0 -0
- package/dist/client/assets/chunk-TICWLB2K-Ba1mI99F.js.gz +0 -0
- package/dist/client/assets/classDiagram-ZZMXUADV-CS4V_MQS.js +0 -1
- package/dist/client/assets/classDiagram-v2-VYDZK3BY-CS4V_MQS.js +0 -1
- package/dist/client/assets/cose-bilkent-JH36ORCC-C-NMDUHW.js.br +0 -0
- package/dist/client/assets/cose-bilkent-JH36ORCC-C-NMDUHW.js.gz +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CZgEc1ab.js.br +0 -0
- package/dist/client/assets/cynefin-OW5HDTMX-CZgEc1ab.js.gz +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CHQeSNm2.js.br +0 -0
- package/dist/client/assets/cynefinDiagram-5FMLGOSQ-CHQeSNm2.js.gz +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-B_jkMdPS.js.br +0 -0
- package/dist/client/assets/dagre-GXQ25YYZ-B_jkMdPS.js.gz +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-nv6Lnsrp.js.br +0 -0
- package/dist/client/assets/diagram-S7CK7UJ4-nv6Lnsrp.js.gz +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BZf2fzaA.js.br +0 -0
- package/dist/client/assets/diagram-UQ7AKVKN-BZf2fzaA.js.gz +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CxL0_BH8.js.br +0 -0
- package/dist/client/assets/diagram-VSXAHHWV-CxL0_BH8.js.gz +0 -0
- package/dist/client/assets/diagram-VX7I27RA-Dm9MqpIE.js.br +0 -0
- package/dist/client/assets/diagram-VX7I27RA-Dm9MqpIE.js.gz +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-BIfTa7P5.js.br +0 -0
- package/dist/client/assets/diagram-Z3DM3KII-BIfTa7P5.js.gz +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-D4XhCDFS.js.br +0 -0
- package/dist/client/assets/ebnfDiagram-PWID7BFC-D4XhCDFS.js.gz +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Dxd48dXC.js.br +0 -0
- package/dist/client/assets/erDiagram-RLTQ6QDP-Dxd48dXC.js.gz +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BXBMsEmE.js.br +0 -0
- package/dist/client/assets/flowDiagram-HODETNUW-BXBMsEmE.js.gz +0 -0
- package/dist/client/assets/ganttDiagram-EL5Y4UJY-CBfDd-KZ.js.br +0 -0
- package/dist/client/assets/gestureArbiter-DXLmNGli.js.gz +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DtLM68fN.js.br +0 -0
- package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DtLM68fN.js.gz +0 -0
- package/dist/client/assets/index-KuPaZyuZ.css.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-DAHBvAZ-.js.br +0 -0
- package/dist/client/assets/ishikawaDiagram-5VMMS53U-DAHBvAZ-.js.gz +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-CVuWuL3Z.js.br +0 -0
- package/dist/client/assets/journeyDiagram-3NMN7TZE-CVuWuL3Z.js.gz +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-ByQSUVJV.js.br +0 -0
- package/dist/client/assets/kanban-definition-UXKFOSKX-ByQSUVJV.js.gz +0 -0
- package/dist/client/assets/layout-Bb3Nb1c4.js.br +0 -0
- package/dist/client/assets/layout-Bb3Nb1c4.js.gz +0 -0
- package/dist/client/assets/linear-DHMMQlOo.js.gz +0 -0
- package/dist/client/assets/mermaid.core-BYfniK0H.js.br +0 -0
- package/dist/client/assets/mermaid.core-BYfniK0H.js.gz +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-EDLD5AyH.js.br +0 -0
- package/dist/client/assets/mindmap-definition-YA3MSWOX-EDLD5AyH.js.gz +0 -0
- package/dist/client/assets/passwordBootstrap-DCrtirwT.js.br +0 -0
- package/dist/client/assets/passwordBootstrap-DCrtirwT.js.gz +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-BHYhkqJN.js.br +0 -0
- package/dist/client/assets/pegDiagram-XKGWAZYB-BHYhkqJN.js.gz +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-BUIJlswC.js.br +0 -0
- package/dist/client/assets/pieDiagram-E7YTZNPT-BUIJlswC.js.gz +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-D8Im-7yC.js.br +0 -0
- package/dist/client/assets/quadrantDiagram-AXDQQJYC-D8Im-7yC.js.gz +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-D7n6z2G1.js.br +0 -0
- package/dist/client/assets/railroadDiagram-O6MQD6OU-D7n6z2G1.js.gz +0 -0
- package/dist/client/assets/rdpSession-DJlNIbVk.js.br +0 -0
- package/dist/client/assets/rdpSession-DJlNIbVk.js.gz +0 -0
- package/dist/client/assets/referenceLongPress-DFSrssSr.js.br +0 -0
- package/dist/client/assets/referenceLongPress-DFSrssSr.js.gz +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-Ki0rR-F7.js.br +0 -0
- package/dist/client/assets/requirementDiagram-BXWQKSXE-Ki0rR-F7.js.gz +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-36NmMqsW.js.br +0 -0
- package/dist/client/assets/sankeyDiagram-P5KCCOFB-36NmMqsW.js.gz +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-5vsPuFyO.js.br +0 -0
- package/dist/client/assets/sequenceDiagram-WJ2MYXX4-5vsPuFyO.js.gz +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-DbAulwBg.js.br +0 -0
- package/dist/client/assets/sizeCapture-INFHLROL-DbAulwBg.js.gz +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-Bgwe0g5S.js.br +0 -0
- package/dist/client/assets/stateDiagram-D77RDMKH-Bgwe0g5S.js.gz +0 -0
- package/dist/client/assets/stateDiagram-v2-MP3YSRHH-Bg6S3Zm0.js +0 -1
- package/dist/client/assets/swimlanes-42K2YHIH-COGAWe-g.js.br +0 -0
- package/dist/client/assets/swimlanes-42K2YHIH-COGAWe-g.js.gz +0 -0
- package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DwBBOocG.js +0 -8
- package/dist/client/assets/timeline-definition-24CTP7MA-C4gt5z68.js.br +0 -0
- package/dist/client/assets/timeline-definition-24CTP7MA-C4gt5z68.js.gz +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CIEXh9oD.js.br +0 -0
- package/dist/client/assets/vennDiagram-4TSXK5OY-CIEXh9oD.js.gz +0 -0
- package/dist/client/assets/videoCompression-CUkCS2-q.js.br +0 -0
- package/dist/client/assets/videoCompression-CUkCS2-q.js.gz +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-gQhcW1vd.js.br +0 -0
- package/dist/client/assets/wardleyDiagram-VM6X3IG4-gQhcW1vd.js.gz +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-BmIcdejz.js.br +0 -0
- package/dist/client/assets/xychartDiagram-S5SC5T6Z-BmIcdejz.js.gz +0 -0
|
@@ -36,12 +36,16 @@ function paneInputKey(pane) {
|
|
|
36
36
|
return `${pane.serverPid}:${pane.sessionId}:${pane.paneId}:${pane.panePid}`;
|
|
37
37
|
}
|
|
38
38
|
/** Serialize TD paste/submit pairs for this exact runtime pane. */
|
|
39
|
-
export function writeCollaborationTmuxPane(run, pane, prompt, stdin, assertCurrent) {
|
|
40
|
-
|
|
39
|
+
export function writeCollaborationTmuxPane(run, pane, prompt, stdin, assertCurrent, progress) {
|
|
40
|
+
progress?.('queue_wait');
|
|
41
|
+
return serializeTerminalInput(paneInputKey(pane), () => {
|
|
42
|
+
progress?.('queue_acquired');
|
|
43
|
+
return writeTmuxPrompt(run, pane, prompt, stdin, assertCurrent, progress);
|
|
44
|
+
});
|
|
41
45
|
}
|
|
42
46
|
async function writeTmuxPrompt(run, pane, prompt,
|
|
43
47
|
/** Body travels on stdin so tmux cannot parse its semicolons/backslashes. */
|
|
44
|
-
stdin, assertCurrent) {
|
|
48
|
+
stdin, assertCurrent, progress) {
|
|
45
49
|
await assertSamePane(run, pane);
|
|
46
50
|
assertCurrent?.();
|
|
47
51
|
// A fixed pane target is independent of the current window, keyboard focus
|
|
@@ -64,6 +68,7 @@ stdin, assertCurrent) {
|
|
|
64
68
|
try {
|
|
65
69
|
// Paste once, then send one unbracketed CR after 50ms. No screen parsing,
|
|
66
70
|
// Agent-specific recovery, consumption confirmation or additional Enter.
|
|
71
|
+
progress?.('paste_attempted');
|
|
67
72
|
await stdin([
|
|
68
73
|
// The body arrives on stdin, not argv: it is user content, and in a
|
|
69
74
|
// `\;` chain argv is a language where `;` and `\` mean something.
|
|
@@ -73,9 +78,11 @@ stdin, assertCurrent) {
|
|
|
73
78
|
// for it to rewrite — the flag is insurance, not the mechanism.
|
|
74
79
|
'paste-buffer', '-p', '-r', '-d', '-b', buffer, '-t', pane.paneId,
|
|
75
80
|
], normalizePromptForPaste(prompt));
|
|
81
|
+
progress?.('paste_written');
|
|
76
82
|
await waitForPromptSubmit();
|
|
77
83
|
await assertSamePane(run, pane);
|
|
78
84
|
assertCurrent?.();
|
|
85
|
+
progress?.('submit_attempted');
|
|
79
86
|
await run([
|
|
80
87
|
// The submit key rides outside the paste block: inside it, an editor
|
|
81
88
|
// inserts a pasted CR as text instead of acting on it. Pasting the bare
|
|
@@ -83,6 +90,7 @@ stdin, assertCurrent) {
|
|
|
83
90
|
'set-buffer', '-b', buffer, '--', '\r', ';',
|
|
84
91
|
'paste-buffer', '-d', '-b', buffer, '-t', pane.paneId,
|
|
85
92
|
]);
|
|
93
|
+
progress?.('submit_written');
|
|
86
94
|
}
|
|
87
95
|
finally {
|
|
88
96
|
// -d consumed the buffer on the happy path; this sweeps up after a failed
|
|
@@ -28,7 +28,10 @@ export const INTEGRATION_HELP = `td integration — local scoped integration adm
|
|
|
28
28
|
Minimum CLI/running service 1.4.309; capabilities.session_resume_configuration=true.
|
|
29
29
|
Errors: RESUME_CONFIGURATION_CHANGED, EXACT_RESUME_UNSUPPORTED,
|
|
30
30
|
SESSION_GENERATION_MISMATCH, SESSION_OPERATION_IN_PROGRESS, LAUNCHER_UNAVAILABLE.
|
|
31
|
-
diagnostics <TD-session-id> (read
|
|
31
|
+
diagnostics <TD-session-id> (read stored launch/restore diagnostics; administrator only)
|
|
32
|
+
1.4.314 adds phase/timestamps, paste/submit write facts and target-change
|
|
33
|
+
evidence; no argv/environment/credentials or application readiness claim.
|
|
34
|
+
Prior versions cannot reconstruct missing launch evidence retroactively.
|
|
32
35
|
create --file <policy.json> --credential-file <private-output.json>
|
|
33
36
|
update --file <policy.json> (same identity/group; preserves the credential)
|
|
34
37
|
revoke <principal-id>
|
|
@@ -217,11 +220,13 @@ export async function runIntegrationCollab(command, port, io) {
|
|
|
217
220
|
return subscribe(socketPath, `/events?${params}`, credential, command.options.timeout ? duration(String(command.options.timeout)) : undefined, io.write);
|
|
218
221
|
}
|
|
219
222
|
if (command.action === 'session') {
|
|
223
|
+
if (command.operation === 'retry-cold' && capability.session_cold_retry !== true)
|
|
224
|
+
throw new CollaborationError('SESSION_COLD_RETRY_UNSUPPORTED', 'CLI and running service must support explicit cold retry (minimum 1.4.314)', 409);
|
|
220
225
|
if (command.operation === 'release' && (capability.session_runtime_release !== true || capability.session_lifecycle_generation !== true || capability.stable_session_identity !== true))
|
|
221
226
|
throw new CollaborationError('SESSION_RELEASE_UNSUPPORTED', 'CLI and running service must support the lifecycle contract (minimum 1.4.309)', 409);
|
|
222
|
-
const route = command.operation === 'create' ? '/sessions' : `/sessions/${encodeURIComponent(command.target)}${['restore', 'release'].includes(command.operation) ? `/${command.operation}` : ''}`;
|
|
227
|
+
const route = command.operation === 'create' ? '/sessions' : `/sessions/${encodeURIComponent(command.target)}${['restore', 'release', 'retry-cold'].includes(command.operation) ? `/${command.operation}` : ''}`;
|
|
223
228
|
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'] }
|
|
224
|
-
: ['restore', 'release'].includes(command.operation) ? { ...(command.operation
|
|
229
|
+
: ['restore', 'release', 'retry-cold'].includes(command.operation) ? { ...(['release', 'retry-cold'].includes(command.operation) ? { generation: Number(command.options.generation) } : {}), idempotency_key: command.options['idempotency-key'] } : undefined;
|
|
225
230
|
io.write(JSON.stringify(parsed(await request(socketPath, command.operation === 'get' ? 'GET' : 'POST', route, body, headers(credential)))));
|
|
226
231
|
return 0;
|
|
227
232
|
}
|
|
@@ -70,7 +70,7 @@ export class IntegrationRuntime {
|
|
|
70
70
|
};
|
|
71
71
|
app.get('/admin/groups', run(req => { admin(req); return { groups: options.messages.list().filter(g => !g.federated).map(g => ({ id: g.id, name: g.name })) }; }));
|
|
72
72
|
app.get('/admin/capabilities', run(req => { admin(req); return this.capabilities(); }));
|
|
73
|
-
app.get('/admin/sessions/:id/restore-diagnostics', run(req => { admin(req); return this.sessions.restoreDiagnostics(req.params.id); }));
|
|
73
|
+
app.get('/admin/sessions/:id/restore-diagnostics', run(req => { admin(req); return { ...this.sessions.restoreDiagnostics(req.params.id), ...this.sessions.launchDiagnostics(req.params.id) }; }));
|
|
74
74
|
app.get('/admin/sessions/:id/resume-configuration', run(async (req) => { admin(req); return this.sessions.resumeConfiguration(req.params.id); }));
|
|
75
75
|
app.post('/admin/sessions/:id/resume-configuration', run(async (req) => { admin(req); return { session: publicIntegrationSession(await this.sessions.confirmResumeConfiguration(req.params.id, req.body?.generation, req.body?.fingerprint)) }; }));
|
|
76
76
|
app.post('/admin/sessions/:id/startup-input/clear', run(async (req) => {
|
|
@@ -197,6 +197,7 @@ export class IntegrationRuntime {
|
|
|
197
197
|
}));
|
|
198
198
|
app.get('/sessions/:id', run(async (req) => { const p = principal(req); this.authorize(p, 'session.read'); return { session: publicIntegrationSession(await this.sessions.get(p, req.params.id)) }; }));
|
|
199
199
|
app.post('/sessions/:id/restore', run(async (req) => { const p = principal(req); this.authorize(p, 'session.restore'); const session = await this.sessions.restore(p, req.params.id, req.body?.idempotency_key); this.sync(); return { session: publicIntegrationSession(session) }; }));
|
|
200
|
+
app.post('/sessions/:id/retry-cold', run(async (req) => { const p = principal(req); this.authorize(p, 'session.create'); const session = await this.sessions.retryCold(p, req.params.id, req.body?.generation, req.body?.idempotency_key); this.sync(); return { session: publicIntegrationSession(session) }; }));
|
|
200
201
|
app.post('/sessions/:id/release', run(async (req) => { const p = principal(req); this.authorize(p, 'session.release'); const session = await this.sessions.release(p, req.params.id, req.body?.generation, req.body?.idempotency_key); this.sync(); return { session: publicIntegrationSession(session) }; }));
|
|
201
202
|
app.get('/events', run((req, res) => {
|
|
202
203
|
const p = principal(req);
|
|
@@ -226,6 +227,8 @@ export class IntegrationRuntime {
|
|
|
226
227
|
session_startup_input_clear: true, session_startup_input_clear_min_version: '1.4.310',
|
|
227
228
|
agent_launch_process_check: true, agent_launch_process_check_min_version: '1.4.312',
|
|
228
229
|
terminal_input_runtime_fence: true, terminal_input_runtime_fence_min_version: '1.4.313',
|
|
230
|
+
session_launch_diagnostics: true, session_launch_diagnostics_min_version: '1.4.314',
|
|
231
|
+
session_cold_retry: true, session_cold_retry_min_version: '1.4.314',
|
|
229
232
|
integration_policy_update: true,
|
|
230
233
|
session_restore_diagnostics: true,
|
|
231
234
|
native_identity_linux_flock_owner: process.platform === 'linux',
|
|
@@ -5,6 +5,8 @@ import { atomicJson, integrationError } from './integrationStore.js';
|
|
|
5
5
|
import { SessionLifecycleCoordinator, assertMessageRuntime, assertReleaseFacts, assertReleaseGeneration } from './sessionLifecycle.js';
|
|
6
6
|
import { resumeConfigurationFingerprint } from './sessionResumeConfiguration.js';
|
|
7
7
|
import { NativeResumeOwnerError } from './nativeResumeOwner.js';
|
|
8
|
+
import { CollaborationError } from './collaborationProtocol.js';
|
|
9
|
+
import { beginLaunchDiagnostics, launchFailure, launchProgress } from './sessionLaunchDiagnostics.js';
|
|
8
10
|
/** Quote each configured argument as literal shell data, including paths. */
|
|
9
11
|
export function integrationLaunchCommand(record, restore) {
|
|
10
12
|
const launchArgs = record.profile.argv.map(arg => arg === '{cwd}' ? record.cwd : arg);
|
|
@@ -20,8 +22,9 @@ function startupInput(record, now) {
|
|
|
20
22
|
return condition ? { state: 'pending', deadline: now + (condition.timeoutMs ?? 120000), matched_since: null, observed_at: null } : undefined;
|
|
21
23
|
}
|
|
22
24
|
export function publicIntegrationSession(record) {
|
|
23
|
-
const { profile: _, deadline: _deadline, launch_submitted: _submitted, startup_condition: _condition, restore_diagnostics: diagnostics, ...publicRecord } = record;
|
|
24
|
-
return { ...publicRecord, ...(diagnostics ? { restore_diagnostics_available: true } : {})
|
|
25
|
+
const { profile: _, deadline: _deadline, launch_submitted: _submitted, startup_condition: _condition, restore_diagnostics: diagnostics, launch_diagnostics: launchDiagnostics, launch_diagnostics_history: _history, message_history_seen: _messages, ...publicRecord } = record;
|
|
26
|
+
return { ...publicRecord, ...(diagnostics ? { restore_diagnostics_available: true } : {}),
|
|
27
|
+
...(launchDiagnostics ? { launch_diagnostics_available: true } : {}) };
|
|
25
28
|
}
|
|
26
29
|
export function permittedCwd(profile, cwd) {
|
|
27
30
|
if (typeof cwd !== 'string' || !path.isAbsolute(cwd) || /[\x00-\x1f\x7f]/.test(cwd))
|
|
@@ -56,6 +59,7 @@ export class IntegrationSessions {
|
|
|
56
59
|
listeners = new Set();
|
|
57
60
|
lifecycle = new SessionLifecycleCoordinator();
|
|
58
61
|
mutations = new Map();
|
|
62
|
+
coldRetries = new Set();
|
|
59
63
|
timers = new Set();
|
|
60
64
|
stopObservation;
|
|
61
65
|
stopDeliveryGuard;
|
|
@@ -111,7 +115,9 @@ export class IntegrationSessions {
|
|
|
111
115
|
}
|
|
112
116
|
saveRecord(record) {
|
|
113
117
|
const before = this.doc.sessions;
|
|
114
|
-
|
|
118
|
+
const seen = before.find(s => s.session_id === record.session_id)?.message_history_seen;
|
|
119
|
+
this.doc.sessions = [...before.filter(s => s.session_id !== record.session_id), structuredClone({ ...record,
|
|
120
|
+
...(seen ? { message_history_seen: true } : {}) })];
|
|
115
121
|
try {
|
|
116
122
|
this.save();
|
|
117
123
|
}
|
|
@@ -154,7 +160,8 @@ export class IntegrationSessions {
|
|
|
154
160
|
const now = Date.now(), record = { operation_id: randomUUID(), session_id: randomBytes(6).toString('hex'),
|
|
155
161
|
group_id: principal.groupId, principal_id: principal.id, agent_slug: profile.agentSlug, agent_native_session_id: null,
|
|
156
162
|
state: 'starting', generation: 1, runtime_present: null, error_code: null, cwd, launch_profile: profile.id, profile: structuredClone(profile),
|
|
157
|
-
created_at: now, updated_at: now, deadline: now + this.bindingTimeout, launch_submitted: false };
|
|
163
|
+
created_at: now, updated_at: now, deadline: now + this.bindingTimeout, launch_submitted: false, message_history_seen: false };
|
|
164
|
+
record.launch_diagnostics = beginLaunchDiagnostics(record.operation_id, 1, 'cold_start');
|
|
158
165
|
record.startup_input = startupInput(record, now);
|
|
159
166
|
this.doc.requests[request.scoped] = { hash: request.hash, sessionId: record.session_id };
|
|
160
167
|
try {
|
|
@@ -166,7 +173,7 @@ export class IntegrationSessions {
|
|
|
166
173
|
}
|
|
167
174
|
return this.launch(record, false);
|
|
168
175
|
}
|
|
169
|
-
launch(record, restoring) {
|
|
176
|
+
launch(record, restoring, retryCold = false) {
|
|
170
177
|
const promise = (async () => {
|
|
171
178
|
try {
|
|
172
179
|
// Store intent before launching. A failed persistence cannot execute.
|
|
@@ -181,17 +188,29 @@ export class IntegrationSessions {
|
|
|
181
188
|
catch (error) {
|
|
182
189
|
// Existing creation supports plugins without an exact resume path.
|
|
183
190
|
// They remain launchable, but cannot gain a releasable descriptor.
|
|
184
|
-
if (restoring || error.code !== 'EXACT_RESUME_UNSUPPORTED')
|
|
191
|
+
if (restoring || retryCold || error.code !== 'EXACT_RESUME_UNSUPPORTED')
|
|
185
192
|
throw error;
|
|
186
193
|
}
|
|
187
194
|
}
|
|
188
195
|
record.launch_submitted = true;
|
|
189
196
|
this.saveRecord(record);
|
|
197
|
+
const progress = (phase) => {
|
|
198
|
+
const latest = this.doc.sessions.find(s => s.session_id === record.session_id);
|
|
199
|
+
if (latest.operation_id !== record.operation_id || latest.generation !== record.generation)
|
|
200
|
+
integrationError('SESSION_GENERATION_MISMATCH', 'Launch operation changed during progress recording', 409);
|
|
201
|
+
if (latest.launch_diagnostics) {
|
|
202
|
+
record.launch_diagnostics = launchProgress(latest.launch_diagnostics, phase);
|
|
203
|
+
this.saveRecord({ ...latest, launch_diagnostics: record.launch_diagnostics });
|
|
204
|
+
}
|
|
205
|
+
};
|
|
190
206
|
const prepared = (binding) => { record.terminal_binding = binding; this.saveRecord(record); };
|
|
191
|
-
if (
|
|
192
|
-
await this.adapter.
|
|
207
|
+
if (retryCold)
|
|
208
|
+
await this.adapter.retryCold(record, prepared, progress);
|
|
209
|
+
else if (restoring)
|
|
210
|
+
await this.adapter.restore(record, prepared, progress);
|
|
193
211
|
else
|
|
194
|
-
await this.adapter.create(record, prepared);
|
|
212
|
+
await this.adapter.create(record, prepared, progress);
|
|
213
|
+
progress('binding_observe');
|
|
195
214
|
await this.refresh(record.session_id);
|
|
196
215
|
this.scheduleRefresh(record);
|
|
197
216
|
}
|
|
@@ -199,6 +218,7 @@ export class IntegrationSessions {
|
|
|
199
218
|
const current = this.doc.sessions.find(s => s.session_id === record.session_id);
|
|
200
219
|
const code = error.code;
|
|
201
220
|
this.saveRecord({ ...current, state: 'failed', error_code: typeof code === 'string' ? code : 'SESSION_LAUNCH_FAILED',
|
|
221
|
+
launch_diagnostics: current.launch_diagnostics ? launchFailure(current.launch_diagnostics, error, typeof code === 'string' ? code : 'SESSION_LAUNCH_FAILED') : undefined,
|
|
202
222
|
restore_diagnostics: error instanceof NativeResumeOwnerError ? { ...structuredClone(error.diagnostics), operation_id: current.operation_id } : undefined,
|
|
203
223
|
updated_at: Date.now() });
|
|
204
224
|
}
|
|
@@ -216,10 +236,27 @@ export class IntegrationSessions {
|
|
|
216
236
|
return structuredClone({ session_id: record.session_id, operation_id: record.operation_id,
|
|
217
237
|
state: record.state, error_code: record.error_code, diagnostics: record.restore_diagnostics ?? null });
|
|
218
238
|
}
|
|
219
|
-
|
|
239
|
+
launchDiagnostics(id) {
|
|
240
|
+
const record = this.doc.sessions.find(s => s.session_id === id);
|
|
241
|
+
if (!record)
|
|
242
|
+
return integrationError('INTEGRATION_SESSION_NOT_FOUND', 'Integration session does not exist', 404);
|
|
243
|
+
return structuredClone({ launch_diagnostics: record.launch_diagnostics ?? null,
|
|
244
|
+
launch_diagnostics_history: record.launch_diagnostics_history ?? [] });
|
|
245
|
+
}
|
|
246
|
+
assertRuntimeMessage(id) {
|
|
247
|
+
const record = this.doc.sessions.find(s => s.session_id === id);
|
|
248
|
+
if (this.coldRetries.has(id) || record?.cold_retry_pending)
|
|
249
|
+
integrationError('SESSION_OPERATION_IN_PROGRESS', 'Cold retry must finish or be reconciled before sending', 409);
|
|
250
|
+
assertMessageRuntime(record);
|
|
251
|
+
// Durable, conservative latch before admission; even a later enqueue
|
|
252
|
+
// failure cannot make a previously attempted business request disappear.
|
|
253
|
+
if (record && !record.message_history_seen)
|
|
254
|
+
this.saveRecord({ ...record, message_history_seen: true });
|
|
255
|
+
}
|
|
220
256
|
assertMessageTarget(principal, id) {
|
|
221
257
|
const record = this.doc.sessions.find(s => s.session_id === id && s.group_id === principal.groupId);
|
|
222
|
-
|
|
258
|
+
if (record)
|
|
259
|
+
this.assertRuntimeMessage(id);
|
|
223
260
|
}
|
|
224
261
|
async mutate(principal, key, input, action) {
|
|
225
262
|
const request = this.request(principal, key, input);
|
|
@@ -259,6 +296,74 @@ export class IntegrationSessions {
|
|
|
259
296
|
async restore(principal, id, key) {
|
|
260
297
|
return this.mutate(principal, key, ['restore', id], () => this.lifecycle.run(id, () => this.restoreOnce(principal, id, key)));
|
|
261
298
|
}
|
|
299
|
+
async retryCold(principal, id, generation, key) {
|
|
300
|
+
return this.mutate(principal, key, ['retry-cold', id, generation], () => this.lifecycle.run(id, async () => {
|
|
301
|
+
const request = this.request(principal, key, ['retry-cold', id, generation]);
|
|
302
|
+
if (request.previous)
|
|
303
|
+
return this.replayRequest(principal, id, request.previous);
|
|
304
|
+
if (!this.adapter.retryCold || !this.adapter.messageHistory)
|
|
305
|
+
integrationError('SESSION_COLD_RETRY_UNSUPPORTED', 'Runtime adapter does not support verified cold retry', 409);
|
|
306
|
+
if (this.pending.has(id))
|
|
307
|
+
integrationError('SESSION_OPERATION_IN_PROGRESS', 'Launch is in progress', 409);
|
|
308
|
+
this.coldRetries.add(id);
|
|
309
|
+
try {
|
|
310
|
+
const record = await this.get(principal, id);
|
|
311
|
+
assertReleaseGeneration(record, generation);
|
|
312
|
+
if (record.cold_retry_pending || record.operation_uncertain)
|
|
313
|
+
integrationError('OPERATION_OUTCOME_UNCONFIRMED', 'Reconcile prior lifecycle outcome before cold retry', 409);
|
|
314
|
+
if (record.agent_native_session_id)
|
|
315
|
+
integrationError('SESSION_COLD_RETRY_NATIVE_BOUND', 'Use exact native restore for a bound session', 409);
|
|
316
|
+
if (record.state !== 'failed')
|
|
317
|
+
integrationError('SESSION_COLD_RETRY_NOT_FAILED', 'Only failed unbound cold starts can be retried', 409);
|
|
318
|
+
const history = this.adapter.messageHistory(id);
|
|
319
|
+
if (history === null)
|
|
320
|
+
integrationError('SESSION_MESSAGE_HISTORY_UNCONFIRMED', 'Message history cannot be verified', 409);
|
|
321
|
+
if (record.message_history_seen || history || this.adapter.pendingMessages?.(id))
|
|
322
|
+
integrationError('SESSION_MESSAGE_HISTORY_PRESENT', 'Cold retry refuses any TD business message history or attempted admission', 409);
|
|
323
|
+
const authorized = principal.launchProfiles.find(p => p.id === record.launch_profile);
|
|
324
|
+
if (!authorized)
|
|
325
|
+
integrationError('LAUNCH_PROFILE_DENIED', 'Original launch profile is no longer authorized', 403);
|
|
326
|
+
permittedCwd(authorized, record.cwd);
|
|
327
|
+
permittedCwd(record.profile, record.cwd);
|
|
328
|
+
if (!record.resume_configuration_fingerprint)
|
|
329
|
+
integrationError('RESUME_CONFIGURATION_UNCONFIRMED', 'Administrator must confirm the launcher/plugin fingerprint', 409);
|
|
330
|
+
if (record.resume_configuration_fingerprint !== await resumeConfigurationFingerprint(record, this.adapter))
|
|
331
|
+
integrationError('RESUME_CONFIGURATION_CHANGED', 'Launcher/plugin configuration changed', 409);
|
|
332
|
+
const observed = await this.adapter.inspect(record);
|
|
333
|
+
if (!record.terminal_binding || !observed.exists || observed.running || !observed.shell || observed.nativeId)
|
|
334
|
+
integrationError('SESSION_COLD_RETRY_RUNTIME_UNCONFIRMED', 'Retry requires the exact original, exited foreground shell', 409);
|
|
335
|
+
record.launch_diagnostics_history = [...(record.launch_diagnostics_history ?? []), ...(record.launch_diagnostics ? [record.launch_diagnostics] : [])].slice(-16);
|
|
336
|
+
record.generation = generation + 1;
|
|
337
|
+
record.operation_id = randomUUID();
|
|
338
|
+
record.state = 'starting';
|
|
339
|
+
record.error_code = null;
|
|
340
|
+
record.cold_retry_pending = true;
|
|
341
|
+
record.cold_retry_count = (record.cold_retry_count ?? 0) + 1;
|
|
342
|
+
record.runtime_present = true;
|
|
343
|
+
record.launch_submitted = false;
|
|
344
|
+
record.updated_at = Date.now();
|
|
345
|
+
record.deadline = Date.now() + this.bindingTimeout;
|
|
346
|
+
record.restore_diagnostics = undefined;
|
|
347
|
+
record.startup_input = startupInput(record, Date.now());
|
|
348
|
+
record.launch_diagnostics = beginLaunchDiagnostics(record.operation_id, record.generation, 'cold_retry');
|
|
349
|
+
this.doc.requests[request.scoped] = { hash: request.hash, sessionId: id, operationId: record.operation_id, generation: record.generation };
|
|
350
|
+
try {
|
|
351
|
+
this.saveRecord(record);
|
|
352
|
+
}
|
|
353
|
+
catch (error) {
|
|
354
|
+
delete this.doc.requests[request.scoped];
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
357
|
+
const result = await this.launch(record, false, true);
|
|
358
|
+
result.cold_retry_pending = false;
|
|
359
|
+
this.saveRecord(result);
|
|
360
|
+
return result;
|
|
361
|
+
}
|
|
362
|
+
finally {
|
|
363
|
+
this.coldRetries.delete(id);
|
|
364
|
+
}
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
262
367
|
async restoreOnce(principal, id, idempotencyKey) {
|
|
263
368
|
const request = this.request(principal, idempotencyKey, ['restore', id]);
|
|
264
369
|
if (request.previous)
|
|
@@ -268,6 +373,8 @@ export class IntegrationSessions {
|
|
|
268
373
|
let releaseNative;
|
|
269
374
|
try {
|
|
270
375
|
const record = await this.get(principal, id);
|
|
376
|
+
if (record.cold_retry_pending)
|
|
377
|
+
integrationError('OPERATION_OUTCOME_UNCONFIRMED', 'Reconcile the cold retry before native restore', 409);
|
|
271
378
|
if (!record.agent_native_session_id)
|
|
272
379
|
integrationError('NATIVE_SESSION_ID_MISSING', 'An exact native session binding is required', 409);
|
|
273
380
|
// Recheck the current grant and actual directories; restore uses the
|
|
@@ -310,6 +417,8 @@ export class IntegrationSessions {
|
|
|
310
417
|
record.deadline = Date.now() + this.bindingTimeout;
|
|
311
418
|
record.updated_at = Date.now();
|
|
312
419
|
record.restore_diagnostics = undefined;
|
|
420
|
+
record.launch_diagnostics_history = [...(record.launch_diagnostics_history ?? []), ...(record.launch_diagnostics ? [record.launch_diagnostics] : [])].slice(-16);
|
|
421
|
+
record.launch_diagnostics = beginLaunchDiagnostics(record.operation_id, record.generation, 'native_restore');
|
|
313
422
|
if (!record.startup_input_disabled && !record.profile.startupInput && !record.startup_condition)
|
|
314
423
|
record.startup_condition = structuredClone(principal.launchProfiles.find(profile => profile.id === record.launch_profile)?.startupInput);
|
|
315
424
|
record.startup_input = startupInput(record, Date.now());
|
|
@@ -443,6 +552,8 @@ export class IntegrationSessions {
|
|
|
443
552
|
const record = structuredClone(this.doc.sessions.find(s => s.session_id === id));
|
|
444
553
|
if (!record)
|
|
445
554
|
return;
|
|
555
|
+
if (record.cold_retry_pending && (this.lifecycle.active(id) || this.pending.has(id)))
|
|
556
|
+
return;
|
|
446
557
|
if (record.state === 'released' || record.state === 'releasing' || record.operation_uncertain) {
|
|
447
558
|
if (this.lifecycle.active(id))
|
|
448
559
|
return;
|
|
@@ -483,8 +594,10 @@ export class IntegrationSessions {
|
|
|
483
594
|
if (JSON.stringify(current) !== JSON.stringify(record))
|
|
484
595
|
return;
|
|
485
596
|
const code = error.code;
|
|
486
|
-
|
|
487
|
-
|
|
597
|
+
const errorCode = typeof code === 'string' ? code : 'SESSION_OBSERVATION_FAILED';
|
|
598
|
+
if (record.state !== 'failed' || record.error_code !== errorCode)
|
|
599
|
+
this.saveRecord({ ...record, state: 'failed', error_code: errorCode,
|
|
600
|
+
launch_diagnostics: record.launch_diagnostics ? launchFailure(record.launch_diagnostics, error, errorCode) : undefined, updated_at: Date.now() });
|
|
488
601
|
return;
|
|
489
602
|
}
|
|
490
603
|
const next = { ...record, runtime_present: this.adapter.runtimePresent ? await this.adapter.runtimePresent(record) : observed.exists };
|
|
@@ -521,6 +634,27 @@ export class IntegrationSessions {
|
|
|
521
634
|
next.state = 'failed';
|
|
522
635
|
next.error_code = 'SESSION_BINDING_TIMEOUT';
|
|
523
636
|
}
|
|
637
|
+
if (record.cold_retry_pending) {
|
|
638
|
+
if (next.state === 'ready')
|
|
639
|
+
next.cold_retry_pending = false;
|
|
640
|
+
else if (record.terminal_binding && observed.exists && !observed.running && observed.shell && !observed.nativeId) {
|
|
641
|
+
next.state = 'failed';
|
|
642
|
+
next.error_code = 'SESSION_COLD_RETRY_INTERRUPTED';
|
|
643
|
+
next.cold_retry_pending = false;
|
|
644
|
+
if (next.launch_diagnostics)
|
|
645
|
+
next.launch_diagnostics = launchProgress(next.launch_diagnostics, 'reconciled_shell');
|
|
646
|
+
}
|
|
647
|
+
else if (!observed.exists) {
|
|
648
|
+
next.state = 'failed';
|
|
649
|
+
next.error_code = 'SESSION_COLD_RETRY_RUNTIME_UNCONFIRMED';
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (next.launch_diagnostics) {
|
|
653
|
+
next.launch_diagnostics = { ...next.launch_diagnostics, runtime_exists: observed.exists };
|
|
654
|
+
if (next.state === 'failed' && next.error_code && (record.state !== 'failed' || record.error_code !== next.error_code)) {
|
|
655
|
+
next.launch_diagnostics = launchFailure(next.launch_diagnostics, new CollaborationError(next.error_code, '', 409), next.error_code);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
524
658
|
if (next.startup_input && next.startup_input.state !== 'observed') {
|
|
525
659
|
const condition = next.startup_condition ?? next.profile.startupInput;
|
|
526
660
|
let matched = false;
|
|
@@ -18,13 +18,19 @@ export function buildBracketedPasteBytes(prompt) {
|
|
|
18
18
|
return `\x1b[200~${normalizePromptForPaste(prompt)}\x1b[201~`;
|
|
19
19
|
}
|
|
20
20
|
/** Serialize TD prompt pairs per PTY. Success only records terminal writes. */
|
|
21
|
-
export function writeBracketedPrompt(target, prompt, assertCurrent) {
|
|
21
|
+
export function writeBracketedPrompt(target, prompt, assertCurrent, progress) {
|
|
22
|
+
progress?.('queue_wait');
|
|
22
23
|
return serializeTerminalInput(target, async () => {
|
|
24
|
+
progress?.('queue_acquired');
|
|
23
25
|
assertCurrent?.();
|
|
26
|
+
progress?.('paste_attempted');
|
|
24
27
|
target.write(buildBracketedPasteBytes(prompt));
|
|
28
|
+
progress?.('paste_written');
|
|
25
29
|
await waitForPromptSubmit();
|
|
26
30
|
assertCurrent?.();
|
|
31
|
+
progress?.('submit_attempted');
|
|
27
32
|
target.write('\r');
|
|
33
|
+
progress?.('submit_written');
|
|
28
34
|
});
|
|
29
35
|
}
|
|
30
36
|
/**
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CollaborationError } from './collaborationProtocol.js';
|
|
2
|
+
import { TerminalInputTargetChangedError } from './terminalInput.js';
|
|
3
|
+
export function beginLaunchDiagnostics(operation_id, generation, kind) {
|
|
4
|
+
const now = Date.now();
|
|
5
|
+
return { operation_id, generation, kind, phase: 'configuration', updated_at: now,
|
|
6
|
+
error_code: null, reason_code: null, paste: 'not_attempted', submit: 'not_attempted',
|
|
7
|
+
runtime_exists: null, steps: [{ phase: 'configuration', at: now }] };
|
|
8
|
+
}
|
|
9
|
+
export function launchProgress(current, phase) {
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
return { ...current, phase, updated_at: now, steps: [...current.steps, { phase, at: now }].slice(-24),
|
|
12
|
+
paste: phase === 'paste_attempted' ? 'attempted' : phase === 'paste_written' ? 'write_returned' : current.paste,
|
|
13
|
+
submit: phase === 'submit_attempted' ? 'attempted' : phase === 'submit_written' ? 'write_returned' : current.submit };
|
|
14
|
+
}
|
|
15
|
+
/** Error text/stdout/argv are never copied into durable diagnostics. */
|
|
16
|
+
export function launchFailure(current, error, error_code) {
|
|
17
|
+
const value = error;
|
|
18
|
+
const standard = new Set(['ENOENT', 'EACCES', 'EPERM', 'EIO', 'ETIMEDOUT', 'EPIPE', 'ECONNRESET',
|
|
19
|
+
'TMUX_PANE_CHANGED', 'TMUX_PANE_IN_MODE', 'SESSION_REMOVED', 'SHELL_BACKEND_CHANGED']);
|
|
20
|
+
const reason_code = error instanceof TerminalInputTargetChangedError ? error.code
|
|
21
|
+
: error instanceof CollaborationError && /^[A-Z][A-Z0-9_]{0,79}$/.test(error.code) ? error.code
|
|
22
|
+
: typeof value?.code === 'string' && standard.has(value.code) ? value.code
|
|
23
|
+
: typeof value?.message === 'string' && standard.has(value.message) ? value.message : 'UNCLASSIFIED_LAUNCH_ERROR';
|
|
24
|
+
return { ...current, updated_at: Date.now(), error_code, reason_code,
|
|
25
|
+
...(error instanceof TerminalInputTargetChangedError ? { target_change: error.evidence } : {}) };
|
|
26
|
+
}
|
|
@@ -14,11 +14,19 @@ function serializeInQueue(store, target, action) {
|
|
|
14
14
|
store.set(target, operation);
|
|
15
15
|
return operation;
|
|
16
16
|
}
|
|
17
|
+
export class TerminalInputTargetChangedError extends Error {
|
|
18
|
+
evidence;
|
|
19
|
+
code = 'TERMINAL_INPUT_TARGET_CHANGED';
|
|
20
|
+
constructor(evidence) {
|
|
21
|
+
super('TERMINAL_INPUT_TARGET_CHANGED');
|
|
22
|
+
this.evidence = evidence;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
17
25
|
/** Clearing a one-time launch check does not change the runtime generation. */
|
|
18
26
|
export function captureTerminalInputGuard(current, expected = current()) {
|
|
19
27
|
return () => {
|
|
20
28
|
if (current() !== expected)
|
|
21
|
-
throw new
|
|
29
|
+
throw new TerminalInputTargetChangedError();
|
|
22
30
|
};
|
|
23
31
|
}
|
|
24
32
|
/** Await the actual write instead of leaving detached timers after reporting success. */
|
|
@@ -50,7 +50,7 @@ import { archiveTaskWorkspace, restoreTaskWorkspace, snapshotTaskWorkspace } fro
|
|
|
50
50
|
import { isTmuxRecoveryCandidate } from '../utils/tmuxRecoveryCandidate.js';
|
|
51
51
|
import { AutomationStore, normalizeAutomationSchedule } from '../agent/automationStore.js';
|
|
52
52
|
import { writeBracketedPrompt, canDeliverPromptToAgent } from '../agent/promptDelivery.js';
|
|
53
|
-
import { captureTerminalInputGuard, waitAndWriteTerminalInput } from '../agent/terminalInput.js';
|
|
53
|
+
import { captureTerminalInputGuard, waitAndWriteTerminalInput, TerminalInputTargetChangedError } from '../agent/terminalInput.js';
|
|
54
54
|
import { agentLaunchBlockReason, normalizeAgentLaunchCheck } from '../agent/agentLaunchCheck.js';
|
|
55
55
|
import { prepareSupervisionEnable } from '../utils/enableSupervision.js';
|
|
56
56
|
import { buildServerHealthState, dismissServerHealth } from '../utils/serverHealth.js';
|
|
@@ -1479,21 +1479,21 @@ async function observeAgentLaunchCheck(record, observed) {
|
|
|
1479
1479
|
function terminalInputGuard(record) {
|
|
1480
1480
|
const frontendSessionId = record.sessionId;
|
|
1481
1481
|
const backendSessionId = record.backendSessionId;
|
|
1482
|
-
const
|
|
1483
|
-
.find(candidate => candidate.sessionId === frontendSessionId)?.agentLaunchGeneration ?? null, record.agentLaunchGeneration ?? null);
|
|
1482
|
+
const launchId = record.agentLaunchGeneration ?? null;
|
|
1484
1483
|
const backend = backendSessionId ? terminalSessions.get(backendSessionId) : undefined;
|
|
1485
1484
|
return () => {
|
|
1486
1485
|
const latest = globalSessionState.sessions.find(candidate => candidate.sessionId === frontendSessionId);
|
|
1487
1486
|
if (!latest)
|
|
1488
1487
|
throw new Error('SESSION_REMOVED');
|
|
1489
|
-
|
|
1490
|
-
if (latest.backendSessionId !== backendSessionId
|
|
1491
|
-
|
|
1492
|
-
|
|
1488
|
+
const instanceMatches = !backendSessionId || terminalSessions.get(backendSessionId) === backend;
|
|
1489
|
+
if ((latest.agentLaunchGeneration ?? null) !== launchId || latest.backendSessionId !== backendSessionId || !instanceMatches) {
|
|
1490
|
+
throw new TerminalInputTargetChangedError({ expected_backend_session_id: backendSessionId, current_backend_session_id: latest.backendSessionId,
|
|
1491
|
+
expected_launch_id: launchId, current_launch_id: latest.agentLaunchGeneration ?? null, backend_instance_matches: instanceMatches });
|
|
1492
|
+
}
|
|
1493
1493
|
};
|
|
1494
1494
|
}
|
|
1495
1495
|
/** tmux input always uses the pane queue, including automation and launch commands. */
|
|
1496
|
-
async function automaticTerminalInput(record, assertCurrent = terminalInputGuard(record)) {
|
|
1496
|
+
async function automaticTerminalInput(record, assertCurrent = terminalInputGuard(record), progress) {
|
|
1497
1497
|
assertCurrent();
|
|
1498
1498
|
if (record.mode === 'tmux') {
|
|
1499
1499
|
const binding = collaborationRouting.get(record.sessionId) ?? {
|
|
@@ -1505,18 +1505,18 @@ async function automaticTerminalInput(record, assertCurrent = terminalInputGuard
|
|
|
1505
1505
|
if (inspected.state !== 'ready' || !inspected.pane)
|
|
1506
1506
|
throw new Error(inspected.reason ?? 'TMUX_PANE_UNAVAILABLE');
|
|
1507
1507
|
const pane = inspected.pane;
|
|
1508
|
-
return (prompt) => writeCollaborationTmuxPane(runTmux, pane, prompt, runTmuxStdin, assertCurrent);
|
|
1508
|
+
return (prompt) => writeCollaborationTmuxPane(runTmux, pane, prompt, runTmuxStdin, assertCurrent, progress);
|
|
1509
1509
|
}
|
|
1510
1510
|
const backend = record.backendSessionId ? terminalSessions.get(record.backendSessionId) : null;
|
|
1511
1511
|
if (!backend)
|
|
1512
1512
|
throw new Error('SHELL_BACKEND_NOT_RUNNING');
|
|
1513
|
-
return (prompt) => writeBracketedPrompt(backend.ptyProcess, prompt, assertCurrent);
|
|
1513
|
+
return (prompt) => writeBracketedPrompt(backend.ptyProcess, prompt, assertCurrent, progress);
|
|
1514
1514
|
}
|
|
1515
|
-
async function writeAutomaticTerminalPrompt(frontendSessionId, prompt) {
|
|
1515
|
+
async function writeAutomaticTerminalPrompt(frontendSessionId, prompt, progress) {
|
|
1516
1516
|
const record = globalSessionState.sessions.find(candidate => candidate.sessionId === frontendSessionId);
|
|
1517
1517
|
if (!record)
|
|
1518
1518
|
throw new Error('SESSION_REMOVED');
|
|
1519
|
-
const write = await automaticTerminalInput(record);
|
|
1519
|
+
const write = await automaticTerminalInput(record, terminalInputGuard(record), progress);
|
|
1520
1520
|
await write(prompt);
|
|
1521
1521
|
}
|
|
1522
1522
|
async function deliverAutomationPromptWhenReady(backendSessionId, prompt) {
|
|
@@ -1985,6 +1985,7 @@ export const integrationSessionAdapter = {
|
|
|
1985
1985
|
registerDeliveryGuard(guard) { integrationDeliveryGuard = guard; return () => { if (integrationDeliveryGuard === guard)
|
|
1986
1986
|
integrationDeliveryGuard = undefined; }; },
|
|
1987
1987
|
pendingMessages(id) { return collaborationStore.pendingCount(id); },
|
|
1988
|
+
messageHistory(id) { return collaborationStore.hasTerminalMessageHistory(id); },
|
|
1988
1989
|
async prepareRelease(record) {
|
|
1989
1990
|
if (collaborationStore.groupsForSession(record.session_id).some(group => group.id !== record.group_id))
|
|
1990
1991
|
integrationError('SESSION_SCOPE_CONFLICT', 'Runtime belongs to another collaboration group; administrator must resolve its membership first', 409);
|
|
@@ -2076,21 +2077,28 @@ export const integrationSessionAdapter = {
|
|
|
2076
2077
|
collaborationRouting.bind({ ...route, nativeSessionId: nativeId, pane: { ...route.pane, nativeSessionId: nativeId } });
|
|
2077
2078
|
return { exists: true, running: Boolean(pane.agentSlug), shell: await integrationPaneIsIdleShell(pane), agentSlug: pane.agentSlug || null, nativeId };
|
|
2078
2079
|
},
|
|
2079
|
-
async create(record, prepared) {
|
|
2080
|
+
async create(record, prepared, progress) {
|
|
2080
2081
|
await listDetectedAgentLaunchers();
|
|
2081
2082
|
if (!agentBySlug(record.agent_slug))
|
|
2082
2083
|
integrationError('COLLAB_AGENT_UNAVAILABLE', 'Configured agent plugin is not installed');
|
|
2083
2084
|
if (integrationTerminalRecord(record))
|
|
2084
2085
|
integrationError('SESSION_CREATE_OUTCOME_UNKNOWN', 'Creation intent already has a terminal; inspect it instead of relaunching', 409);
|
|
2085
|
-
await launchIntegrationTerminal(record, false, prepared);
|
|
2086
|
+
await launchIntegrationTerminal(record, false, prepared, progress);
|
|
2086
2087
|
},
|
|
2087
|
-
async restore(record, prepared) {
|
|
2088
|
+
async restore(record, prepared, progress) {
|
|
2088
2089
|
await listDetectedAgentLaunchers();
|
|
2089
2090
|
const agent = agentBySlug(record.agent_slug);
|
|
2090
2091
|
if (!record.agent_native_session_id || !agent || !buildResumeCommand(agent, record.agent_native_session_id, null))
|
|
2091
2092
|
integrationError('EXACT_RESUME_UNSUPPORTED', 'Agent plugin must support exact native resume');
|
|
2093
|
+
progress?.('native_owner_check');
|
|
2092
2094
|
await assertIntegrationNativeResumeAvailable(record);
|
|
2093
|
-
await launchIntegrationTerminal(record, true, prepared);
|
|
2095
|
+
await launchIntegrationTerminal(record, true, prepared, progress);
|
|
2096
|
+
},
|
|
2097
|
+
async retryCold(record, prepared, progress) {
|
|
2098
|
+
await listDetectedAgentLaunchers();
|
|
2099
|
+
if (!agentBySlug(record.agent_slug))
|
|
2100
|
+
integrationError('COLLAB_AGENT_UNAVAILABLE', 'Configured agent plugin is not installed');
|
|
2101
|
+
await launchIntegrationTerminal(record, false, prepared, progress, true);
|
|
2094
2102
|
},
|
|
2095
2103
|
};
|
|
2096
2104
|
async function assertIntegrationNativeResumeAvailable(record) {
|
|
@@ -2141,12 +2149,28 @@ async function assertIntegrationNativeResumeAvailable(record) {
|
|
|
2141
2149
|
program: program?.command ?? null, processSource: program?.source, argumentsObserved: !!program?.rawArgs }];
|
|
2142
2150
|
});
|
|
2143
2151
|
}
|
|
2144
|
-
async function launchIntegrationTerminal(record, restoring, prepared) {
|
|
2152
|
+
async function launchIntegrationTerminal(record, restoring, prepared, progress, retryCold = false) {
|
|
2145
2153
|
await collaborationDeliveryWorker.reconfigure(record.session_id, async () => {
|
|
2154
|
+
progress?.('runtime_prepare');
|
|
2146
2155
|
const group = collaborationStore.getGroup(record.group_id);
|
|
2147
2156
|
if (!group || group.deleted)
|
|
2148
2157
|
integrationError('TASK_GROUP_NOT_FOUND', 'Integration group no longer exists', 404);
|
|
2149
2158
|
const original = await integrationSessionAdapter.inspect(record);
|
|
2159
|
+
if (retryCold) {
|
|
2160
|
+
await integrationSessionAdapter.prepareRelease(record);
|
|
2161
|
+
const owned = integrationTerminalRecord(record);
|
|
2162
|
+
if (!owned || globalSessionState.sessions.some(other => other.sessionId !== record.session_id
|
|
2163
|
+
&& (other.tmuxSessionName === owned.tmuxSessionName || owned.backendSessionId && other.backendSessionId === owned.backendSessionId))) {
|
|
2164
|
+
integrationError('SESSION_IDENTITY_MISMATCH', 'Cold retry runtime lacks unique logical ownership', 409);
|
|
2165
|
+
}
|
|
2166
|
+
const history = collaborationStore.hasTerminalMessageHistory(record.session_id);
|
|
2167
|
+
if (history === null)
|
|
2168
|
+
integrationError('SESSION_MESSAGE_HISTORY_UNCONFIRMED', 'Message history cannot be verified', 409);
|
|
2169
|
+
if (history || collaborationStore.pendingCount(record.session_id))
|
|
2170
|
+
integrationError('SESSION_MESSAGE_HISTORY_PRESENT', 'TD message history appeared before retry', 409);
|
|
2171
|
+
if (!record.terminal_binding || !original.exists || original.running || !original.shell || original.nativeId)
|
|
2172
|
+
integrationError('SESSION_COLD_RETRY_RUNTIME_UNCONFIRMED', 'Original shell could not be confirmed before retry', 409);
|
|
2173
|
+
}
|
|
2150
2174
|
if (!original.exists) {
|
|
2151
2175
|
let tmuxExists = false;
|
|
2152
2176
|
try {
|
|
@@ -2178,6 +2202,7 @@ async function launchIntegrationTerminal(record, restoring, prepared) {
|
|
|
2178
2202
|
const pane = inspected.pane;
|
|
2179
2203
|
const pinned = { serverPid: pane.serverPid, sessionId: pane.sessionId,
|
|
2180
2204
|
paneId: pane.paneId, panePid: pane.panePid, agentSlug: record.agent_slug, nativeSessionId: record.agent_native_session_id };
|
|
2205
|
+
progress?.('binding_persist');
|
|
2181
2206
|
prepared(pinned); // Must persist before the command enters the terminal.
|
|
2182
2207
|
collaborationRouting.bind({ sessionId: record.session_id, backendSessionId: opened.terminalSession.sessionId,
|
|
2183
2208
|
mode: 'tmux', tmuxSessionName: terminal.tmuxSessionName, agentSlug: record.agent_slug, nativeSessionId: record.agent_native_session_id, pane: pinned });
|
|
@@ -2195,8 +2220,11 @@ async function launchIntegrationTerminal(record, restoring, prepared) {
|
|
|
2195
2220
|
// use an active-pane target which might have changed during preparation.
|
|
2196
2221
|
if (record.resume_configuration_fingerprint && record.resume_configuration_fingerprint !== await resumeConfigurationFingerprint(record, integrationSessionAdapter))
|
|
2197
2222
|
integrationError('RESUME_CONFIGURATION_CHANGED', 'Recovery configuration changed before terminal input', 409);
|
|
2198
|
-
if (restoring)
|
|
2223
|
+
if (restoring) {
|
|
2224
|
+
progress?.('native_owner_check');
|
|
2199
2225
|
await assertIntegrationNativeResumeAvailable(record);
|
|
2226
|
+
}
|
|
2227
|
+
progress?.('runtime_verify');
|
|
2200
2228
|
const shell = await inspectCollaborationTmux({ sessionId: record.session_id, backendSessionId: opened.terminalSession.sessionId,
|
|
2201
2229
|
mode: 'tmux', tmuxSessionName: terminal.tmuxSessionName, agentSlug: null, nativeSessionId: null, pane: pinned });
|
|
2202
2230
|
if (shell.state !== 'ready' || !shell.pane || !await integrationPaneIsIdleShell(shell.pane))
|
|
@@ -2206,8 +2234,9 @@ async function launchIntegrationTerminal(record, restoring, prepared) {
|
|
|
2206
2234
|
backend.agentSession = null;
|
|
2207
2235
|
backend.agent = null;
|
|
2208
2236
|
backend.agentResumeRecovered = false;
|
|
2237
|
+
progress?.('launch_intent');
|
|
2209
2238
|
await beginAgentLaunchCheck(record.session_id, record.agent_slug, restoring ? record.agent_native_session_id : null);
|
|
2210
|
-
await writeAutomaticTerminalPrompt(record.session_id, integrationLaunchCommand(record, restoring));
|
|
2239
|
+
await writeAutomaticTerminalPrompt(record.session_id, integrationLaunchCommand(record, restoring), progress);
|
|
2211
2240
|
broadcastClientState();
|
|
2212
2241
|
});
|
|
2213
2242
|
}
|