termdock 1.4.312 → 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 +21 -5
- package/dist/server/agent/collaborationStore.js +25 -2
- package/dist/server/agent/collaborationTmuxDelivery.js +32 -19
- package/dist/server/agent/integrationCli.js +8 -3
- package/dist/server/agent/integrationServer.js +5 -1
- package/dist/server/agent/integrationSessions.js +147 -13
- package/dist/server/agent/promptDelivery.js +12 -8
- package/dist/server/agent/sessionLaunchDiagnostics.js +26 -0
- package/dist/server/agent/terminalInput.js +44 -0
- package/dist/server/routes/terminal.js +140 -53
- package/docs/integrations.md +110 -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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { plainCollaborationSnapshot } from './collaborationText.js';
|
|
2
2
|
import { normalizePromptForPaste, waitForPromptSubmit } from './promptDelivery.js';
|
|
3
|
+
import { serializeTerminalInput } from './terminalInput.js';
|
|
3
4
|
const TMUX_KEY_NAMES = {
|
|
4
5
|
enter: 'Enter', escape: 'Escape', space: 'Space', left: 'Left', right: 'Right', up: 'Up', down: 'Down',
|
|
5
6
|
};
|
|
@@ -31,21 +32,22 @@ async function assertSamePane(run, pane) {
|
|
|
31
32
|
/** Unique-per-process buffer name so concurrent deliveries to different panes
|
|
32
33
|
* never share (or clobber) a buffer. */
|
|
33
34
|
let bufferSequence = 0;
|
|
34
|
-
|
|
35
|
+
function paneInputKey(pane) {
|
|
36
|
+
return `${pane.serverPid}:${pane.sessionId}:${pane.paneId}:${pane.panePid}`;
|
|
37
|
+
}
|
|
35
38
|
/** Serialize TD paste/submit pairs for this exact runtime pane. */
|
|
36
|
-
export function writeCollaborationTmuxPane(run, pane, prompt, stdin) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
paneWrites.set(key, operation);
|
|
43
|
-
return operation;
|
|
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
|
+
});
|
|
44
45
|
}
|
|
45
46
|
async function writeTmuxPrompt(run, pane, prompt,
|
|
46
47
|
/** Body travels on stdin so tmux cannot parse its semicolons/backslashes. */
|
|
47
|
-
stdin) {
|
|
48
|
+
stdin, assertCurrent, progress) {
|
|
48
49
|
await assertSamePane(run, pane);
|
|
50
|
+
assertCurrent?.();
|
|
49
51
|
// A fixed pane target is independent of the current window, keyboard focus
|
|
50
52
|
// and browser presence. Delivery goes through a named tmux buffer rather
|
|
51
53
|
// than `send-keys -l`: while a pane is in copy-mode, literal bytes are
|
|
@@ -66,6 +68,7 @@ stdin) {
|
|
|
66
68
|
try {
|
|
67
69
|
// Paste once, then send one unbracketed CR after 50ms. No screen parsing,
|
|
68
70
|
// Agent-specific recovery, consumption confirmation or additional Enter.
|
|
71
|
+
progress?.('paste_attempted');
|
|
69
72
|
await stdin([
|
|
70
73
|
// The body arrives on stdin, not argv: it is user content, and in a
|
|
71
74
|
// `\;` chain argv is a language where `;` and `\` mean something.
|
|
@@ -75,8 +78,11 @@ stdin) {
|
|
|
75
78
|
// for it to rewrite — the flag is insurance, not the mechanism.
|
|
76
79
|
'paste-buffer', '-p', '-r', '-d', '-b', buffer, '-t', pane.paneId,
|
|
77
80
|
], normalizePromptForPaste(prompt));
|
|
81
|
+
progress?.('paste_written');
|
|
78
82
|
await waitForPromptSubmit();
|
|
79
83
|
await assertSamePane(run, pane);
|
|
84
|
+
assertCurrent?.();
|
|
85
|
+
progress?.('submit_attempted');
|
|
80
86
|
await run([
|
|
81
87
|
// The submit key rides outside the paste block: inside it, an editor
|
|
82
88
|
// inserts a pasted CR as text instead of acting on it. Pasting the bare
|
|
@@ -84,6 +90,7 @@ stdin) {
|
|
|
84
90
|
'set-buffer', '-b', buffer, '--', '\r', ';',
|
|
85
91
|
'paste-buffer', '-d', '-b', buffer, '-t', pane.paneId,
|
|
86
92
|
]);
|
|
93
|
+
progress?.('submit_written');
|
|
87
94
|
}
|
|
88
95
|
finally {
|
|
89
96
|
// -d consumed the buffer on the happy path; this sweeps up after a failed
|
|
@@ -100,10 +107,14 @@ export async function paneInMode(run, pane) {
|
|
|
100
107
|
* unchanged. Named keys only — no freeform keystroke passthrough. Refuses
|
|
101
108
|
* while the pane is in a mode: there the key would be taken by the mode
|
|
102
109
|
* handler (Enter in copy-mode exits the user's scroll) rather than the app. */
|
|
103
|
-
export
|
|
110
|
+
export function sendTmuxPaneKey(run, pane, key, assertCurrent) {
|
|
111
|
+
return serializeTerminalInput(paneInputKey(pane), () => sendTmuxPaneKeyUnlocked(run, pane, key, assertCurrent));
|
|
112
|
+
}
|
|
113
|
+
async function sendTmuxPaneKeyUnlocked(run, pane, key, assertCurrent) {
|
|
104
114
|
await assertSamePane(run, pane);
|
|
105
115
|
if (await paneInMode(run, pane))
|
|
106
116
|
throw new Error('TMUX_PANE_IN_MODE');
|
|
117
|
+
assertCurrent?.();
|
|
107
118
|
await run(['send-keys', '-t', pane.paneId, TMUX_KEY_NAMES[key]]);
|
|
108
119
|
}
|
|
109
120
|
/** Current pane screen text (viewport), identity-checked. */
|
|
@@ -116,14 +127,16 @@ export async function captureTmuxPaneText(run, pane, rawOutput = false) {
|
|
|
116
127
|
* Enter (the highlighted option). Refuses unless the screen actually shows
|
|
117
128
|
* an approval prompt: driving a dialog that isn't there would only inject
|
|
118
129
|
* a stray keystroke into the agent's work. Returns false when no dialog. */
|
|
119
|
-
export
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
export function approveCollaborationDialog(run, pane, assertCurrent) {
|
|
131
|
+
return serializeTerminalInput(paneInputKey(pane), async () => {
|
|
132
|
+
assertCurrent?.();
|
|
133
|
+
const content = await captureTmuxPaneText(run, pane);
|
|
134
|
+
if (!detectApprovalDialog(content))
|
|
135
|
+
return false;
|
|
136
|
+
// The same input queue covers both the observation and requested key.
|
|
137
|
+
await sendTmuxPaneKeyUnlocked(run, pane, 'enter', assertCurrent);
|
|
138
|
+
return true;
|
|
139
|
+
});
|
|
127
140
|
}
|
|
128
141
|
/** History-inclusive terminal observation, without inferring consumption. */
|
|
129
142
|
export async function captureTmuxPaneHistory(run, pane, lines = 800, rawOutput = false) {
|
|
@@ -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);
|
|
@@ -225,6 +226,9 @@ export class IntegrationRuntime {
|
|
|
225
226
|
session_resume_configuration: true, session_lifecycle_min_version: '1.4.309',
|
|
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',
|
|
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',
|
|
228
232
|
integration_policy_update: true,
|
|
229
233
|
session_restore_diagnostics: true,
|
|
230
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;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { serializeTerminalInput } from './terminalInput.js';
|
|
1
2
|
/**
|
|
2
3
|
* Fold a prompt into the single-channel form both tmux and agent TUIs expect:
|
|
3
4
|
* every line break becomes one CR (commits a line inside the editor, and is
|
|
@@ -16,18 +17,21 @@ export function waitForPromptSubmit() {
|
|
|
16
17
|
export function buildBracketedPasteBytes(prompt) {
|
|
17
18
|
return `\x1b[200~${normalizePromptForPaste(prompt)}\x1b[201~`;
|
|
18
19
|
}
|
|
19
|
-
const promptWrites = new WeakMap();
|
|
20
20
|
/** Serialize TD prompt pairs per PTY. Success only records terminal writes. */
|
|
21
|
-
export function writeBracketedPrompt(target, prompt) {
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
export function writeBracketedPrompt(target, prompt, assertCurrent, progress) {
|
|
22
|
+
progress?.('queue_wait');
|
|
23
|
+
return serializeTerminalInput(target, async () => {
|
|
24
|
+
progress?.('queue_acquired');
|
|
25
|
+
assertCurrent?.();
|
|
26
|
+
progress?.('paste_attempted');
|
|
24
27
|
target.write(buildBracketedPasteBytes(prompt));
|
|
28
|
+
progress?.('paste_written');
|
|
25
29
|
await waitForPromptSubmit();
|
|
30
|
+
assertCurrent?.();
|
|
31
|
+
progress?.('submit_attempted');
|
|
26
32
|
target.write('\r');
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
promptWrites.set(target, operation);
|
|
30
|
-
return operation;
|
|
33
|
+
progress?.('submit_written');
|
|
34
|
+
});
|
|
31
35
|
}
|
|
32
36
|
/**
|
|
33
37
|
* Process detection and hook events are independent Agent signals. A target is
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** One TD input operation per actual transport, independent of application state. */
|
|
2
|
+
const objectInputs = new WeakMap();
|
|
3
|
+
const namedInputs = new Map();
|
|
4
|
+
export function serializeTerminalInput(target, action) {
|
|
5
|
+
return typeof target === 'string' ? serializeInQueue(namedInputs, target, action)
|
|
6
|
+
: serializeInQueue(objectInputs, target, action);
|
|
7
|
+
}
|
|
8
|
+
function serializeInQueue(store, target, action) {
|
|
9
|
+
const previous = store.get(target) ?? Promise.resolve();
|
|
10
|
+
const operation = previous.catch(() => undefined).then(action).finally(() => {
|
|
11
|
+
if (store.get(target) === operation)
|
|
12
|
+
store.delete(target);
|
|
13
|
+
});
|
|
14
|
+
store.set(target, operation);
|
|
15
|
+
return operation;
|
|
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
|
+
}
|
|
25
|
+
/** Clearing a one-time launch check does not change the runtime generation. */
|
|
26
|
+
export function captureTerminalInputGuard(current, expected = current()) {
|
|
27
|
+
return () => {
|
|
28
|
+
if (current() !== expected)
|
|
29
|
+
throw new TerminalInputTargetChangedError();
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Await the actual write instead of leaving detached timers after reporting success. */
|
|
33
|
+
export async function waitAndWriteTerminalInput(input) {
|
|
34
|
+
for (let attempt = 0;; attempt++) {
|
|
35
|
+
input.assertCurrent();
|
|
36
|
+
const ready = await input.ready(attempt);
|
|
37
|
+
input.assertCurrent();
|
|
38
|
+
if (ready) {
|
|
39
|
+
await input.write();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
await new Promise(resolve => setTimeout(resolve, 250));
|
|
43
|
+
}
|
|
44
|
+
}
|