@juspay/shooter 1.17.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/build/client/_app/immutable/assets/{0.B0O0vCnX.css → 0.BwNtE8TX.css} +1 -1
  2. package/build/client/_app/immutable/assets/0.BwNtE8TX.css.br +0 -0
  3. package/build/client/_app/immutable/assets/{0.B0O0vCnX.css.gz → 0.BwNtE8TX.css.gz} +0 -0
  4. package/build/client/_app/immutable/assets/8.BYgAX7hR.css +1 -0
  5. package/build/client/_app/immutable/assets/8.BYgAX7hR.css.br +0 -0
  6. package/build/client/_app/immutable/assets/8.BYgAX7hR.css.gz +0 -0
  7. package/build/client/_app/immutable/assets/9.DV6pZunn.css +1 -0
  8. package/build/client/_app/immutable/assets/9.DV6pZunn.css.br +0 -0
  9. package/build/client/_app/immutable/assets/9.DV6pZunn.css.gz +0 -0
  10. package/build/client/_app/immutable/chunks/{DZQMsHM5.js → 2rBV5OkJ.js} +1 -1
  11. package/build/client/_app/immutable/chunks/2rBV5OkJ.js.br +0 -0
  12. package/build/client/_app/immutable/chunks/2rBV5OkJ.js.gz +0 -0
  13. package/build/client/_app/immutable/chunks/BB2l8o4X.js +1 -0
  14. package/build/client/_app/immutable/chunks/BB2l8o4X.js.br +0 -0
  15. package/build/client/_app/immutable/chunks/BB2l8o4X.js.gz +0 -0
  16. package/build/client/_app/immutable/chunks/{Cg3dlX05.js → BPDiEZo0.js} +2 -2
  17. package/build/client/_app/immutable/chunks/BPDiEZo0.js.br +0 -0
  18. package/build/client/_app/immutable/chunks/BPDiEZo0.js.gz +0 -0
  19. package/build/client/_app/immutable/chunks/{C_9BZILB.js → BcpydfqI.js} +1 -1
  20. package/build/client/_app/immutable/chunks/BcpydfqI.js.br +0 -0
  21. package/build/client/_app/immutable/chunks/BcpydfqI.js.gz +0 -0
  22. package/build/client/_app/immutable/chunks/BcqA7eKM.js +3 -0
  23. package/build/client/_app/immutable/chunks/BcqA7eKM.js.br +0 -0
  24. package/build/client/_app/immutable/chunks/BcqA7eKM.js.gz +0 -0
  25. package/build/client/_app/immutable/chunks/BdtLzPpO.js +1 -0
  26. package/build/client/_app/immutable/chunks/BdtLzPpO.js.br +0 -0
  27. package/build/client/_app/immutable/chunks/BdtLzPpO.js.gz +0 -0
  28. package/build/client/_app/immutable/chunks/{BRqaaL5D.js → BvmdJful.js} +1 -1
  29. package/build/client/_app/immutable/chunks/BvmdJful.js.br +0 -0
  30. package/build/client/_app/immutable/chunks/BvmdJful.js.gz +0 -0
  31. package/build/client/_app/immutable/chunks/{C5VOyQCG.js → ClIPTXf3.js} +1 -1
  32. package/build/client/_app/immutable/chunks/ClIPTXf3.js.br +0 -0
  33. package/build/client/_app/immutable/chunks/ClIPTXf3.js.gz +0 -0
  34. package/build/client/_app/immutable/chunks/{BctvtE4d.js → DA4Zt9Me.js} +1 -1
  35. package/build/client/_app/immutable/chunks/DA4Zt9Me.js.br +0 -0
  36. package/build/client/_app/immutable/chunks/{BctvtE4d.js.gz → DA4Zt9Me.js.gz} +0 -0
  37. package/build/client/_app/immutable/chunks/{CjfxuHdN.js → DCDL_9ys.js} +1 -1
  38. package/build/client/_app/immutable/chunks/DCDL_9ys.js.br +0 -0
  39. package/build/client/_app/immutable/chunks/DCDL_9ys.js.gz +0 -0
  40. package/build/client/_app/immutable/chunks/{DYuMZGL5.js → DWmC0QM7.js} +1 -1
  41. package/build/client/_app/immutable/chunks/DWmC0QM7.js.br +0 -0
  42. package/build/client/_app/immutable/chunks/DWmC0QM7.js.gz +0 -0
  43. package/build/client/_app/immutable/chunks/{DZvnhU_8.js → ZS5XYDx_.js} +2 -2
  44. package/build/client/_app/immutable/chunks/ZS5XYDx_.js.br +0 -0
  45. package/build/client/_app/immutable/chunks/ZS5XYDx_.js.gz +0 -0
  46. package/build/client/_app/immutable/entry/app.D4TXlu7A.js +2 -0
  47. package/build/client/_app/immutable/entry/app.D4TXlu7A.js.br +0 -0
  48. package/build/client/_app/immutable/entry/app.D4TXlu7A.js.gz +0 -0
  49. package/build/client/_app/immutable/entry/start.BBQhtURO.js +1 -0
  50. package/build/client/_app/immutable/entry/start.BBQhtURO.js.br +0 -0
  51. package/build/client/_app/immutable/entry/start.BBQhtURO.js.gz +0 -0
  52. package/build/client/_app/immutable/nodes/0.1zylwAPT.js +10 -0
  53. package/build/client/_app/immutable/nodes/0.1zylwAPT.js.br +0 -0
  54. package/build/client/_app/immutable/nodes/0.1zylwAPT.js.gz +0 -0
  55. package/build/client/_app/immutable/nodes/{1.Fqso94b3.js → 1.BVnLUSs-.js} +1 -1
  56. package/build/client/_app/immutable/nodes/1.BVnLUSs-.js.br +0 -0
  57. package/build/client/_app/immutable/nodes/1.BVnLUSs-.js.gz +0 -0
  58. package/build/client/_app/immutable/nodes/{8.BjKgvSie.js → 10.D1wl2wPX.js} +2 -2
  59. package/build/client/_app/immutable/nodes/10.D1wl2wPX.js.br +0 -0
  60. package/build/client/_app/immutable/nodes/10.D1wl2wPX.js.gz +0 -0
  61. package/build/client/_app/immutable/nodes/{9.BRT6HOXB.js → 11.C18nMGmp.js} +1 -1
  62. package/build/client/_app/immutable/nodes/11.C18nMGmp.js.br +0 -0
  63. package/build/client/_app/immutable/nodes/11.C18nMGmp.js.gz +0 -0
  64. package/build/client/_app/immutable/nodes/{2.BusCVJWk.js → 2.D1Mm0DUX.js} +2 -2
  65. package/build/client/_app/immutable/nodes/2.D1Mm0DUX.js.br +0 -0
  66. package/build/client/_app/immutable/nodes/2.D1Mm0DUX.js.gz +0 -0
  67. package/build/client/_app/immutable/nodes/{3.DUlpocIc.js → 3.Wfz3TcJd.js} +3 -3
  68. package/build/client/_app/immutable/nodes/3.Wfz3TcJd.js.br +0 -0
  69. package/build/client/_app/immutable/nodes/3.Wfz3TcJd.js.gz +0 -0
  70. package/build/client/_app/immutable/nodes/{4.BSVqdrrD.js → 4.CBX9A3ka.js} +2 -2
  71. package/build/client/_app/immutable/nodes/4.CBX9A3ka.js.br +0 -0
  72. package/build/client/_app/immutable/nodes/4.CBX9A3ka.js.gz +0 -0
  73. package/build/client/_app/immutable/nodes/{5.Cfj35gpY.js → 5.DIVKuZc9.js} +1 -1
  74. package/build/client/_app/immutable/nodes/5.DIVKuZc9.js.br +0 -0
  75. package/build/client/_app/immutable/nodes/5.DIVKuZc9.js.gz +0 -0
  76. package/build/client/_app/immutable/nodes/{6.CG4eKRH0.js → 6.DtZAEPXb.js} +1 -1
  77. package/build/client/_app/immutable/nodes/6.DtZAEPXb.js.br +0 -0
  78. package/build/client/_app/immutable/nodes/6.DtZAEPXb.js.gz +0 -0
  79. package/build/client/_app/immutable/nodes/{7.DHilxD1o.js → 7.MfBRh32I.js} +1 -1
  80. package/build/client/_app/immutable/nodes/7.MfBRh32I.js.br +0 -0
  81. package/build/client/_app/immutable/nodes/7.MfBRh32I.js.gz +0 -0
  82. package/build/client/_app/immutable/nodes/8.DVE6LnOC.js +1 -0
  83. package/build/client/_app/immutable/nodes/8.DVE6LnOC.js.br +0 -0
  84. package/build/client/_app/immutable/nodes/8.DVE6LnOC.js.gz +0 -0
  85. package/build/client/_app/immutable/nodes/9.BCel5OqI.js +2 -0
  86. package/build/client/_app/immutable/nodes/9.BCel5OqI.js.br +0 -0
  87. package/build/client/_app/immutable/nodes/9.BCel5OqI.js.gz +0 -0
  88. package/build/client/_app/version.json +1 -1
  89. package/build/client/_app/version.json.br +0 -0
  90. package/build/client/_app/version.json.gz +0 -0
  91. package/build/server/chunks/{0-BWFSL107.js → 0-DJqyZZTr.js} +4 -4
  92. package/build/server/chunks/{0-BWFSL107.js.map → 0-DJqyZZTr.js.map} +1 -1
  93. package/build/server/chunks/1-2YUVen1F.js +9 -0
  94. package/build/server/chunks/{1-Bw5KlAjL.js.map → 1-2YUVen1F.js.map} +1 -1
  95. package/build/server/chunks/10-D1X7LB3v.js +9 -0
  96. package/build/server/chunks/10-D1X7LB3v.js.map +1 -0
  97. package/build/server/chunks/11-qXSPdF5j.js +9 -0
  98. package/build/server/chunks/11-qXSPdF5j.js.map +1 -0
  99. package/build/server/chunks/{2-CQ3yYSVK.js → 2-BD7kj1mt.js} +3 -3
  100. package/build/server/chunks/{2-CQ3yYSVK.js.map → 2-BD7kj1mt.js.map} +1 -1
  101. package/build/server/chunks/{3-DZ4H9hPs.js → 3-oNjv-BhZ.js} +3 -3
  102. package/build/server/chunks/{3-DZ4H9hPs.js.map → 3-oNjv-BhZ.js.map} +1 -1
  103. package/build/server/chunks/{4-BtYdKCVW.js → 4-Bb5VFhsO.js} +3 -3
  104. package/build/server/chunks/{4-BtYdKCVW.js.map → 4-Bb5VFhsO.js.map} +1 -1
  105. package/build/server/chunks/{5-CvJK3PiH.js → 5-oNoWuIsn.js} +3 -3
  106. package/build/server/chunks/{5-CvJK3PiH.js.map → 5-oNoWuIsn.js.map} +1 -1
  107. package/build/server/chunks/6-DRJGUqHG.js +9 -0
  108. package/build/server/chunks/{6-BZ0enR6b.js.map → 6-DRJGUqHG.js.map} +1 -1
  109. package/build/server/chunks/7-_giJiu0L.js +9 -0
  110. package/build/server/chunks/{7-Lg8imTZn.js.map → 7-_giJiu0L.js.map} +1 -1
  111. package/build/server/chunks/8-zvWAVNT5.js +9 -0
  112. package/build/server/chunks/8-zvWAVNT5.js.map +1 -0
  113. package/build/server/chunks/9-DVyDL445.js +9 -0
  114. package/build/server/chunks/9-DVyDL445.js.map +1 -0
  115. package/build/server/chunks/Banner-BgaAs1rs.js +90 -0
  116. package/build/server/chunks/Banner-BgaAs1rs.js.map +1 -0
  117. package/build/server/chunks/{Button-B5dU-ntz.js → Button-D0hZ7JYt.js} +2 -2
  118. package/build/server/chunks/Button-D0hZ7JYt.js.map +1 -0
  119. package/build/server/chunks/{Icon-C7Ml3GX6.js → Icon-D0GBnDcs.js} +3 -3
  120. package/build/server/chunks/Icon-D0GBnDcs.js.map +1 -0
  121. package/build/server/chunks/{Input-CPGO0sbS.js → Input-OmIiydSx.js} +2 -2
  122. package/build/server/chunks/Input-OmIiydSx.js.map +1 -0
  123. package/build/server/chunks/{Pill-CcrtCejm.js → Pill-4xJ-VhAA.js} +3 -3
  124. package/build/server/chunks/Pill-4xJ-VhAA.js.map +1 -0
  125. package/build/server/chunks/{Shimmer-C5jkvGr1.js → Shimmer-Dw2uvTC1.js} +2 -2
  126. package/build/server/chunks/Shimmer-Dw2uvTC1.js.map +1 -0
  127. package/build/server/chunks/{_error.svelte-CSIxs-ab.js → _error.svelte-CZnkxeLr.js} +8 -8
  128. package/build/server/chunks/{_error.svelte-CSIxs-ab.js.map → _error.svelte-CZnkxeLr.js.map} +1 -1
  129. package/build/server/chunks/{_layout.svelte-noB4j-v2.js → _layout.svelte-DfgNGGiM.js} +16 -11
  130. package/build/server/chunks/_layout.svelte-DfgNGGiM.js.map +1 -0
  131. package/build/server/chunks/{_page.svelte-DnTpPnPR.js → _page.svelte-BLo2v_8E.js} +7 -88
  132. package/build/server/chunks/_page.svelte-BLo2v_8E.js.map +1 -0
  133. package/build/server/chunks/_page.svelte-BTlfUsBp.js +43 -0
  134. package/build/server/chunks/_page.svelte-BTlfUsBp.js.map +1 -0
  135. package/build/server/chunks/{_page.svelte-C60lAagP.js → _page.svelte-BUBLUSGo.js} +8 -8
  136. package/build/server/chunks/_page.svelte-BUBLUSGo.js.map +1 -0
  137. package/build/server/chunks/{_page.svelte-BV0XyYJZ.js → _page.svelte-BX2FMgSg.js} +4 -4
  138. package/build/server/chunks/{_page.svelte-BV0XyYJZ.js.map → _page.svelte-BX2FMgSg.js.map} +1 -1
  139. package/build/server/chunks/{_page.svelte-BUkm2304.js → _page.svelte-C7B0qdrC.js} +5 -5
  140. package/build/server/chunks/{_page.svelte-BUkm2304.js.map → _page.svelte-C7B0qdrC.js.map} +1 -1
  141. package/build/server/chunks/{_page.svelte-Dmg-RFCg.js → _page.svelte-CE7COWnF.js} +7 -7
  142. package/build/server/chunks/{_page.svelte-Dmg-RFCg.js.map → _page.svelte-CE7COWnF.js.map} +1 -1
  143. package/build/server/chunks/{_page.svelte-BfB8maoc.js → _page.svelte-CWsjjd4l.js} +9 -9
  144. package/build/server/chunks/{_page.svelte-BfB8maoc.js.map → _page.svelte-CWsjjd4l.js.map} +1 -1
  145. package/build/server/chunks/_page.svelte-D5S2hkBk.js +104 -0
  146. package/build/server/chunks/_page.svelte-D5S2hkBk.js.map +1 -0
  147. package/build/server/chunks/{_page.svelte-B6qyh-K-.js → _page.svelte-D_Ey8QRG.js} +11 -11
  148. package/build/server/chunks/{_page.svelte-B6qyh-K-.js.map → _page.svelte-D_Ey8QRG.js.map} +1 -1
  149. package/build/server/chunks/{_page.svelte-DuzZr5dA.js → _page.svelte-tBuIq8Pg.js} +11 -11
  150. package/build/server/chunks/{_page.svelte-DuzZr5dA.js.map → _page.svelte-tBuIq8Pg.js.map} +1 -1
  151. package/build/server/chunks/_server.ts-BaaY7Z9D.js +77 -0
  152. package/build/server/chunks/_server.ts-BaaY7Z9D.js.map +1 -0
  153. package/build/server/chunks/{_server.ts-5wx4ZppI.js → _server.ts-Bi0Oe4PF.js} +7 -4
  154. package/build/server/chunks/_server.ts-Bi0Oe4PF.js.map +1 -0
  155. package/build/server/chunks/_server.ts-C0317RBD.js +57 -0
  156. package/build/server/chunks/_server.ts-C0317RBD.js.map +1 -0
  157. package/build/server/chunks/{_server.ts-CKXVBbwb.js → _server.ts-CRVNEOd2.js} +10 -8
  158. package/build/server/chunks/_server.ts-CRVNEOd2.js.map +1 -0
  159. package/build/server/chunks/_server.ts-CVPZOpiv.js +23 -0
  160. package/build/server/chunks/_server.ts-CVPZOpiv.js.map +1 -0
  161. package/build/server/chunks/{_server.ts-CyjDrcZN.js → _server.ts-C_OOUqsd.js} +9 -1
  162. package/build/server/chunks/_server.ts-C_OOUqsd.js.map +1 -0
  163. package/build/server/chunks/{_server.ts-BMMTS86y.js → _server.ts-D9ir7u24.js} +3 -4
  164. package/build/server/chunks/{_server.ts-BMMTS86y.js.map → _server.ts-D9ir7u24.js.map} +1 -1
  165. package/build/server/chunks/{_server.ts-DZ5naqSL.js → _server.ts-DMm0hBP4.js} +5 -1
  166. package/build/server/chunks/_server.ts-DMm0hBP4.js.map +1 -0
  167. package/build/server/chunks/{_server.ts-CgHc1Zpx.js → _server.ts-DhJx0DLr.js} +7 -4
  168. package/build/server/chunks/_server.ts-DhJx0DLr.js.map +1 -0
  169. package/build/server/chunks/_server.ts-DkZX_O9a.js +39 -0
  170. package/build/server/chunks/_server.ts-DkZX_O9a.js.map +1 -0
  171. package/build/server/chunks/{_server.ts-B1z0q6qZ.js → _server.ts-DxT9IlZF.js} +6 -5
  172. package/build/server/chunks/_server.ts-DxT9IlZF.js.map +1 -0
  173. package/build/server/chunks/{_server.ts-Bt7EAfjo.js → _server.ts-MbnroWEF.js} +25 -48
  174. package/build/server/chunks/_server.ts-MbnroWEF.js.map +1 -0
  175. package/build/server/chunks/_server.ts-Mttr0-Sl.js +48 -0
  176. package/build/server/chunks/_server.ts-Mttr0-Sl.js.map +1 -0
  177. package/build/server/chunks/_server.ts-jtqWDWcf.js +45 -0
  178. package/build/server/chunks/_server.ts-jtqWDWcf.js.map +1 -0
  179. package/build/server/chunks/{cache-Me3zUAaD.js → cache-BlMaDsHi.js} +2 -2
  180. package/build/server/chunks/{cache-Me3zUAaD.js.map → cache-BlMaDsHi.js.map} +1 -1
  181. package/build/server/chunks/{client-CfNnl32g.js → client-Ds1brw-8.js} +4 -4
  182. package/build/server/chunks/{client-CfNnl32g.js.map → client-Ds1brw-8.js.map} +1 -1
  183. package/build/server/chunks/client2-DngLdcUc.js +7 -0
  184. package/build/server/chunks/{client2-DDP30_vY.js.map → client2-DngLdcUc.js.map} +1 -1
  185. package/build/server/chunks/coordinator-DMU_ADXf.js +530 -0
  186. package/build/server/chunks/coordinator-DMU_ADXf.js.map +1 -0
  187. package/build/server/chunks/{index-CJrGuxuM.js → index-CoYB03g7.js} +2 -2
  188. package/build/server/chunks/{index-CJrGuxuM.js.map → index-CoYB03g7.js.map} +1 -1
  189. package/build/server/chunks/{index-server--49oHtA0.js → index-server-Bq3cnK69.js} +2 -2
  190. package/build/server/chunks/{index-server--49oHtA0.js.map → index-server-Bq3cnK69.js.map} +1 -1
  191. package/build/server/chunks/{index2-MY7PXeAc.js → index2-dSGQ9Eaa.js} +2 -2
  192. package/build/server/chunks/{index2-MY7PXeAc.js.map → index2-dSGQ9Eaa.js.map} +1 -1
  193. package/build/server/chunks/{pty-manager-RmhVe2Ez.js → pty-manager-41h3IK8K.js} +100 -2
  194. package/build/server/chunks/pty-manager-41h3IK8K.js.map +1 -0
  195. package/build/server/chunks/qwen-reader-DGfUbKaJ.js +2112 -0
  196. package/build/server/chunks/qwen-reader-DGfUbKaJ.js.map +1 -0
  197. package/build/server/chunks/{registry-DzJj2E6I.js → registry-D4J_CuzW.js} +56 -24
  198. package/build/server/chunks/registry-D4J_CuzW.js.map +1 -0
  199. package/build/server/chunks/{root-xvQIR1Bt.js → root-D4IoFC8F.js} +2 -2
  200. package/build/server/chunks/root-D4IoFC8F.js.map +1 -0
  201. package/build/server/chunks/{state.svelte-RCtlkrNH.js → state.svelte-CmHqngc_.js} +3 -3
  202. package/build/server/chunks/{state.svelte-RCtlkrNH.js.map → state.svelte-CmHqngc_.js.map} +1 -1
  203. package/build/server/chunks/{stores-C-LqoonT.js → stores-CRYxfF0o.js} +4 -4
  204. package/build/server/chunks/stores-CRYxfF0o.js.map +1 -0
  205. package/build/server/chunks/super-session-handler-DPyxFgmz.js +22 -0
  206. package/build/server/chunks/super-session-handler-DPyxFgmz.js.map +1 -0
  207. package/build/server/index.js +4 -4
  208. package/build/server/index.js.map +1 -1
  209. package/build/server/manifest.js +79 -21
  210. package/build/server/manifest.js.map +1 -1
  211. package/package.json +2 -2
  212. package/scripts/e2e-all-features.sh +204 -0
  213. package/scripts/e2e-cross-terminal.sh +168 -0
  214. package/server.ts +37 -0
  215. package/src/lib/modules/client/common/provider.ts +0 -2
  216. package/src/lib/modules/client/terminal/ChatView.svelte +9 -2
  217. package/src/lib/modules/client/terminal/LaunchSheet.svelte +3 -0
  218. package/src/lib/modules/server/sessions/amp-reader.ts +439 -0
  219. package/src/lib/modules/server/sessions/copilot-reader.ts +542 -0
  220. package/src/lib/modules/server/sessions/cursor-reader.ts +634 -0
  221. package/src/lib/modules/server/sessions/gemini-reader.ts +48 -25
  222. package/src/lib/modules/server/sessions/opencode-reader.ts +13 -12
  223. package/src/lib/modules/server/sessions/process-detector.ts +37 -60
  224. package/src/lib/modules/server/sessions/provider-paths.ts +173 -0
  225. package/src/lib/modules/server/sessions/qwen-reader.ts +41 -15
  226. package/src/lib/modules/server/sessions/registry.ts +55 -14
  227. package/src/lib/modules/server/sos/coordinator.ts +492 -0
  228. package/src/lib/modules/server/sos/policy-gate.ts +56 -0
  229. package/src/lib/modules/server/sos/relay-store.ts +159 -0
  230. package/src/lib/modules/server/terminal/generic-session-watcher.ts +163 -0
  231. package/src/lib/modules/server/terminal/pty-input.ts +37 -0
  232. package/src/lib/modules/server/terminal/pty-manager.ts +51 -0
  233. package/src/lib/modules/server/ws/server.ts +6 -1
  234. package/src/lib/modules/server/ws/session-handler.ts +17 -2
  235. package/src/lib/modules/server/ws/super-session-handler.ts +200 -0
  236. package/src/lib/theme.css +1 -2
  237. package/src/lib/types/generated/Sessions.ts +1 -4
  238. package/src/lib/types/index.ts +2 -1
  239. package/src/lib/types/server.ts +23 -6
  240. package/src/lib/types/sessions.ts +1 -10
  241. package/src/lib/types/sos.ts +134 -0
  242. package/src/routes/+layout.svelte +9 -2
  243. package/src/routes/api/sessions/connect/+server.ts +7 -3
  244. package/src/routes/api/sos/+server.ts +36 -0
  245. package/src/routes/api/sos/[id]/+server.ts +55 -0
  246. package/src/routes/api/sos/[id]/inject/+server.ts +44 -0
  247. package/src/routes/api/sos/[id]/members/+server.ts +47 -0
  248. package/src/routes/api/sos/[id]/members/[mid]/+server.ts +17 -0
  249. package/src/routes/api/sos/[id]/rules/+server.ts +85 -0
  250. package/src/routes/sos/+page.svelte +195 -0
  251. package/src/routes/sos/[id]/+page.svelte +677 -0
  252. package/build/client/_app/immutable/assets/0.B0O0vCnX.css.br +0 -0
  253. package/build/client/_app/immutable/chunks/BRqaaL5D.js.br +0 -0
  254. package/build/client/_app/immutable/chunks/BRqaaL5D.js.gz +0 -0
  255. package/build/client/_app/immutable/chunks/BctvtE4d.js.br +0 -0
  256. package/build/client/_app/immutable/chunks/BxFShcQO.js +0 -1
  257. package/build/client/_app/immutable/chunks/BxFShcQO.js.br +0 -0
  258. package/build/client/_app/immutable/chunks/BxFShcQO.js.gz +0 -0
  259. package/build/client/_app/immutable/chunks/ByzqAuXw.js +0 -3
  260. package/build/client/_app/immutable/chunks/ByzqAuXw.js.br +0 -0
  261. package/build/client/_app/immutable/chunks/ByzqAuXw.js.gz +0 -0
  262. package/build/client/_app/immutable/chunks/C5VOyQCG.js.br +0 -0
  263. package/build/client/_app/immutable/chunks/C5VOyQCG.js.gz +0 -0
  264. package/build/client/_app/immutable/chunks/C_9BZILB.js.br +0 -0
  265. package/build/client/_app/immutable/chunks/C_9BZILB.js.gz +0 -0
  266. package/build/client/_app/immutable/chunks/Cg3dlX05.js.br +0 -0
  267. package/build/client/_app/immutable/chunks/Cg3dlX05.js.gz +0 -0
  268. package/build/client/_app/immutable/chunks/CjfxuHdN.js.br +0 -0
  269. package/build/client/_app/immutable/chunks/CjfxuHdN.js.gz +0 -0
  270. package/build/client/_app/immutable/chunks/DYuMZGL5.js.br +0 -0
  271. package/build/client/_app/immutable/chunks/DYuMZGL5.js.gz +0 -0
  272. package/build/client/_app/immutable/chunks/DZQMsHM5.js.br +0 -0
  273. package/build/client/_app/immutable/chunks/DZQMsHM5.js.gz +0 -0
  274. package/build/client/_app/immutable/chunks/DZvnhU_8.js.br +0 -0
  275. package/build/client/_app/immutable/chunks/DZvnhU_8.js.gz +0 -0
  276. package/build/client/_app/immutable/chunks/Pw0jDB7M.js +0 -1
  277. package/build/client/_app/immutable/chunks/Pw0jDB7M.js.br +0 -0
  278. package/build/client/_app/immutable/chunks/Pw0jDB7M.js.gz +0 -0
  279. package/build/client/_app/immutable/entry/app.CNaTe-zm.js +0 -2
  280. package/build/client/_app/immutable/entry/app.CNaTe-zm.js.br +0 -0
  281. package/build/client/_app/immutable/entry/app.CNaTe-zm.js.gz +0 -0
  282. package/build/client/_app/immutable/entry/start.hxYnjcDu.js +0 -1
  283. package/build/client/_app/immutable/entry/start.hxYnjcDu.js.br +0 -0
  284. package/build/client/_app/immutable/entry/start.hxYnjcDu.js.gz +0 -0
  285. package/build/client/_app/immutable/nodes/0.C3ELOf4c.js +0 -7
  286. package/build/client/_app/immutable/nodes/0.C3ELOf4c.js.br +0 -0
  287. package/build/client/_app/immutable/nodes/0.C3ELOf4c.js.gz +0 -0
  288. package/build/client/_app/immutable/nodes/1.Fqso94b3.js.br +0 -0
  289. package/build/client/_app/immutable/nodes/1.Fqso94b3.js.gz +0 -0
  290. package/build/client/_app/immutable/nodes/2.BusCVJWk.js.br +0 -0
  291. package/build/client/_app/immutable/nodes/2.BusCVJWk.js.gz +0 -0
  292. package/build/client/_app/immutable/nodes/3.DUlpocIc.js.br +0 -0
  293. package/build/client/_app/immutable/nodes/3.DUlpocIc.js.gz +0 -0
  294. package/build/client/_app/immutable/nodes/4.BSVqdrrD.js.br +0 -0
  295. package/build/client/_app/immutable/nodes/4.BSVqdrrD.js.gz +0 -0
  296. package/build/client/_app/immutable/nodes/5.Cfj35gpY.js.br +0 -0
  297. package/build/client/_app/immutable/nodes/5.Cfj35gpY.js.gz +0 -0
  298. package/build/client/_app/immutable/nodes/6.CG4eKRH0.js.br +0 -0
  299. package/build/client/_app/immutable/nodes/6.CG4eKRH0.js.gz +0 -0
  300. package/build/client/_app/immutable/nodes/7.DHilxD1o.js.br +0 -0
  301. package/build/client/_app/immutable/nodes/7.DHilxD1o.js.gz +0 -0
  302. package/build/client/_app/immutable/nodes/8.BjKgvSie.js.br +0 -0
  303. package/build/client/_app/immutable/nodes/8.BjKgvSie.js.gz +0 -0
  304. package/build/client/_app/immutable/nodes/9.BRT6HOXB.js.br +0 -0
  305. package/build/client/_app/immutable/nodes/9.BRT6HOXB.js.gz +0 -0
  306. package/build/server/chunks/1-Bw5KlAjL.js +0 -9
  307. package/build/server/chunks/6-BZ0enR6b.js +0 -9
  308. package/build/server/chunks/7-Lg8imTZn.js +0 -9
  309. package/build/server/chunks/8-DKs4yOL7.js +0 -9
  310. package/build/server/chunks/8-DKs4yOL7.js.map +0 -1
  311. package/build/server/chunks/9-UNmpUWDY.js +0 -9
  312. package/build/server/chunks/9-UNmpUWDY.js.map +0 -1
  313. package/build/server/chunks/Button-B5dU-ntz.js.map +0 -1
  314. package/build/server/chunks/Icon-C7Ml3GX6.js.map +0 -1
  315. package/build/server/chunks/Input-CPGO0sbS.js.map +0 -1
  316. package/build/server/chunks/Pill-CcrtCejm.js.map +0 -1
  317. package/build/server/chunks/Shimmer-C5jkvGr1.js.map +0 -1
  318. package/build/server/chunks/_layout.svelte-noB4j-v2.js.map +0 -1
  319. package/build/server/chunks/_page.svelte-C60lAagP.js.map +0 -1
  320. package/build/server/chunks/_page.svelte-DnTpPnPR.js.map +0 -1
  321. package/build/server/chunks/_server.ts-5wx4ZppI.js.map +0 -1
  322. package/build/server/chunks/_server.ts-B1z0q6qZ.js.map +0 -1
  323. package/build/server/chunks/_server.ts-Bt7EAfjo.js.map +0 -1
  324. package/build/server/chunks/_server.ts-CKXVBbwb.js.map +0 -1
  325. package/build/server/chunks/_server.ts-CgHc1Zpx.js.map +0 -1
  326. package/build/server/chunks/_server.ts-CyjDrcZN.js.map +0 -1
  327. package/build/server/chunks/_server.ts-DZ5naqSL.js.map +0 -1
  328. package/build/server/chunks/client2-DDP30_vY.js +0 -7
  329. package/build/server/chunks/opencode-db-path-BwaPufWf.js +0 -411
  330. package/build/server/chunks/opencode-db-path-BwaPufWf.js.map +0 -1
  331. package/build/server/chunks/pty-manager-RmhVe2Ez.js.map +0 -1
  332. package/build/server/chunks/qwen-reader-2fTFuC_D.js +0 -622
  333. package/build/server/chunks/qwen-reader-2fTFuC_D.js.map +0 -1
  334. package/build/server/chunks/registry-DzJj2E6I.js.map +0 -1
  335. package/build/server/chunks/root-xvQIR1Bt.js.map +0 -1
  336. package/build/server/chunks/stores-C-LqoonT.js.map +0 -1
  337. /package/build/client/_app/immutable/assets/{8.BhoBXADL.css → 10.BhoBXADL.css} +0 -0
  338. /package/build/client/_app/immutable/assets/{8.BhoBXADL.css.br → 10.BhoBXADL.css.br} +0 -0
  339. /package/build/client/_app/immutable/assets/{8.BhoBXADL.css.gz → 10.BhoBXADL.css.gz} +0 -0
  340. /package/build/client/_app/immutable/assets/{9.v5KA95xm.css → 11.v5KA95xm.css} +0 -0
  341. /package/build/client/_app/immutable/assets/{9.v5KA95xm.css.br → 11.v5KA95xm.css.br} +0 -0
  342. /package/build/client/_app/immutable/assets/{9.v5KA95xm.css.gz → 11.v5KA95xm.css.gz} +0 -0
@@ -0,0 +1,163 @@
1
+ /**
2
+ * GenericSessionWatcher — live tailing for the five read-only providers
3
+ * (cursor, copilot, qwen, gemini, amp).
4
+ *
5
+ * The byte-offset watchers (claude/codex) and the SQLite poller (opencode)
6
+ * each understand one wire format. These five providers store sessions in
7
+ * heterogeneous shapes — per-turn JSONL for some, a single rewritten JSON
8
+ * document for others — so a byte-incremental reader would need five bespoke
9
+ * parsers. Instead this watcher re-parses the whole file on each change via
10
+ * the shared `parseReadOnlyProviderFile` dispatch and emits only messages
11
+ * whose ID it has not delivered before. That makes it correct for both
12
+ * append-only and whole-document-rewrite formats with one code path.
13
+ *
14
+ * The public surface (getHistory + ref-counted subscribe) matches
15
+ * SessionWatcherLike, so the server's session-watcher adapter can route to it
16
+ * by path without any handler changes.
17
+ */
18
+
19
+ import type { ConversationMessage, GenericWatchedFile, OnNewEntries } from '$lib/types';
20
+
21
+ import { watch as chokidarWatch } from 'chokidar';
22
+
23
+ import { parseReadOnlyProviderFile } from '../sessions/provider-paths';
24
+
25
+ class GenericSessionWatcher {
26
+ private watched = new Map<string, GenericWatchedFile>();
27
+
28
+ /** Re-read the whole file and return the full parsed conversation. */
29
+ getHistory(filePath: string): ConversationMessage[] {
30
+ return parseReadOnlyProviderFile(filePath);
31
+ }
32
+
33
+ /** Stop watching a single file and release its chokidar handle. */
34
+ stop(filePath: string): void {
35
+ const watched = this.watched.get(filePath);
36
+ if (!watched) {
37
+ return;
38
+ }
39
+ void watched.watcher.close();
40
+ this.watched.delete(filePath);
41
+ console.log(`[generic-watcher] Stopped watching: ${filePath}`);
42
+ }
43
+
44
+ /** Stop watching every file. */
45
+ stopAll(): void {
46
+ for (const [filePath] of this.watched) {
47
+ this.stop(filePath);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Subscribe to new messages for a file. Adds the callback to an existing
53
+ * watcher when one is present (ref-counted), otherwise starts a chokidar
54
+ * watch. Returns an unsubscribe function that tears the watcher down once
55
+ * the last subscriber leaves — identical lifecycle to SessionWatcher.
56
+ */
57
+ subscribe(filePath: string, onNewEntries: OnNewEntries): () => void {
58
+ const existing = this.watched.get(filePath);
59
+ if (existing) {
60
+ existing.callbacks.add(onNewEntries);
61
+ return () => {
62
+ this.removeCallback(filePath, onNewEntries);
63
+ };
64
+ }
65
+
66
+ // Seed the emitted set with everything already in the file so the first
67
+ // change only surfaces genuinely new messages — history is sent separately
68
+ // via getHistory, mirroring the byte watchers' initial-offset behaviour.
69
+ const emittedMessageIds = new Set<string>();
70
+ try {
71
+ for (const msg of parseReadOnlyProviderFile(filePath)) {
72
+ emittedMessageIds.add(msg.id);
73
+ }
74
+ } catch (error) {
75
+ // Degrade gracefully: an unreadable/racy file at subscribe time must not
76
+ // prevent watching — the first change will just re-surface what's there.
77
+ console.error(`[generic-watcher] Failed to seed ${filePath}:`, error);
78
+ }
79
+
80
+ const watcher = chokidarWatch(filePath, {
81
+ awaitWriteFinish: { pollInterval: 100, stabilityThreshold: 200 },
82
+ ignoreInitial: true,
83
+ usePolling: false,
84
+ });
85
+
86
+ const watched: GenericWatchedFile = {
87
+ callbacks: new Set([onNewEntries]),
88
+ emittedMessageIds,
89
+ filePath,
90
+ watcher,
91
+ };
92
+
93
+ const onChange = (): void => {
94
+ this.readNew(watched);
95
+ };
96
+ watcher.on('add', onChange);
97
+ watcher.on('change', onChange);
98
+ watcher.on('error', (error) => {
99
+ console.error(`[generic-watcher] Error watching ${filePath}:`, error);
100
+ });
101
+
102
+ this.watched.set(filePath, watched);
103
+ console.log(`[generic-watcher] Watching: ${filePath} (seeded ${emittedMessageIds.size} msgs)`);
104
+
105
+ return () => {
106
+ this.removeCallback(filePath, onNewEntries);
107
+ };
108
+ }
109
+
110
+ /** Legacy fire-and-forget API matching SessionWatcher.watch(). */
111
+ watch(filePath: string, onNewEntries: OnNewEntries): void {
112
+ this.subscribe(filePath, onNewEntries);
113
+ }
114
+
115
+ /**
116
+ * Re-parse the file and deliver any messages whose ID has not been emitted.
117
+ * If a rewrite drops the count below what we have seen (truncation/reset),
118
+ * the new IDs simply won't match, so nothing spurious is sent.
119
+ */
120
+ private readNew(watched: GenericWatchedFile): void {
121
+ let messages: ConversationMessage[];
122
+ try {
123
+ messages = parseReadOnlyProviderFile(watched.filePath);
124
+ } catch (error) {
125
+ console.error(`[generic-watcher] Failed to re-read ${watched.filePath}:`, error);
126
+ return;
127
+ }
128
+
129
+ const fresh = messages.filter((msg) => !watched.emittedMessageIds.has(msg.id));
130
+ if (fresh.length === 0) {
131
+ return;
132
+ }
133
+ for (const msg of fresh) {
134
+ watched.emittedMessageIds.add(msg.id);
135
+ }
136
+ for (const cb of watched.callbacks) {
137
+ try {
138
+ cb(fresh);
139
+ } catch (cbError) {
140
+ console.error('[generic-watcher] Callback error:', cbError);
141
+ }
142
+ }
143
+ }
144
+
145
+ /** Remove one callback; stop watching when none remain. */
146
+ private removeCallback(filePath: string, onNewEntries: OnNewEntries): void {
147
+ const watched = this.watched.get(filePath);
148
+ if (!watched) {
149
+ return;
150
+ }
151
+ watched.callbacks.delete(onNewEntries);
152
+ if (watched.callbacks.size === 0) {
153
+ this.stop(filePath);
154
+ }
155
+ }
156
+ }
157
+
158
+ // Single shared instance across module loaders (matches sessionWatcher).
159
+ const GW_GLOBAL_KEY = '__shooter_generic_session_watcher';
160
+ export const genericSessionWatcher: GenericSessionWatcher =
161
+ ((globalThis as Record<string, unknown>)[GW_GLOBAL_KEY] as GenericSessionWatcher) ||
162
+ new GenericSessionWatcher();
163
+ (globalThis as Record<string, unknown>)[GW_GLOBAL_KEY] = genericSessionWatcher;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * PTY input helpers.
3
+ *
4
+ * Submitting text to an interactive agent TUI (codex, claude, gemini, qwen, …)
5
+ * is not as simple as appending a newline. These TUIs read the PTY in raw mode:
6
+ *
7
+ * - A bare LF (`\n`) is NOT the Enter key — it types a literal newline into
8
+ * the prompt and never submits. (Verified: LF leaves codex sitting on its
9
+ * prompt; the message just accumulates.)
10
+ * - Even `"<text>\r"` written as a SINGLE chunk is treated as a bracketed
11
+ * paste by the TUI, so the trailing CR is absorbed into the pasted body
12
+ * instead of submitting. (Verified: codex types the text but does not run.)
13
+ *
14
+ * The reliable approach — the same bytes a real terminal emulator sends when a
15
+ * human pastes and presses Enter — is to wrap the body in an explicit bracketed
16
+ * paste (`ESC[200~` … `ESC[201~`) and then send a CR. The paste-end marker
17
+ * closes the paste unambiguously, so the following CR is a real Enter. This
18
+ * also preserves embedded newlines in multi-line messages (the whole point of
19
+ * bracketed paste) and submits correctly in modern interactive shells, where
20
+ * bracketed paste is enabled by default.
21
+ *
22
+ * Verified empirically against codex 0.136 and claude 2.1 — both receive the
23
+ * message and complete a turn.
24
+ */
25
+
26
+ const PASTE_START = '\x1b[200~';
27
+ const PASTE_END = '\x1b[201~';
28
+
29
+ /**
30
+ * Build the PTY byte sequence that delivers `text` to an interactive terminal
31
+ * and submits it (presses Enter). Any trailing newline the caller added is
32
+ * stripped — the CR after the paste-end marker is what submits.
33
+ */
34
+ export function ptySubmitSequence(text: string): string {
35
+ const body = text.replace(/[\r\n]+$/, '');
36
+ return `${PASTE_START}${body}${PASTE_END}\r`;
37
+ }
@@ -13,6 +13,10 @@ import path from 'path';
13
13
  import { fileURLToPath } from 'url';
14
14
 
15
15
  import { findCodexRolloutForCwd } from '../sessions/codex-reader';
16
+ import {
17
+ discoverReadOnlyProviderSessionFile,
18
+ readOnlySourceForCommand,
19
+ } from '../sessions/provider-paths';
16
20
  import { broadcastEvent } from '../ws/server.js';
17
21
  import { HolderClient } from './holder-client';
18
22
  import { openCodeWatcher } from './opencode-watcher';
@@ -1005,6 +1009,53 @@ class PtyManager {
1005
1009
  5 * 60 * 1000
1006
1010
  );
1007
1011
  }
1012
+
1013
+ // For the read-only providers (cursor/copilot/qwen/gemini/amp): poll their
1014
+ // store for a session started after launch and matching this cwd, then set
1015
+ // sessionFile to that path. The WS adapter routes it to the generic watcher
1016
+ // by its provider-root path, giving the same live-tail the JSONL watchers
1017
+ // provide for Claude/Codex.
1018
+ const readOnlySource = readOnlySourceForCommand(command);
1019
+ if (readOnlySource) {
1020
+ const launchTime = terminal.createdAt.getTime();
1021
+ terminal.pollTimer = setInterval(() => {
1022
+ if (terminal.status === 'exited' || terminal.sessionFile) {
1023
+ if (terminal.pollTimer) {
1024
+ clearInterval(terminal.pollTimer);
1025
+ terminal.pollTimer = null;
1026
+ }
1027
+ return;
1028
+ }
1029
+ try {
1030
+ const file = discoverReadOnlyProviderSessionFile(
1031
+ readOnlySource,
1032
+ cwd,
1033
+ launchTime,
1034
+ Date.now()
1035
+ );
1036
+ if (file) {
1037
+ terminal.sessionFile = file;
1038
+ if (terminal.pollTimer) {
1039
+ clearInterval(terminal.pollTimer);
1040
+ terminal.pollTimer = null;
1041
+ }
1042
+ terminalStore.update(id, { sessionFile: file });
1043
+ }
1044
+ } catch {
1045
+ // ignore filesystem errors
1046
+ }
1047
+ }, 2000);
1048
+
1049
+ setTimeout(
1050
+ () => {
1051
+ if (terminal.pollTimer) {
1052
+ clearInterval(terminal.pollTimer);
1053
+ terminal.pollTimer = null;
1054
+ }
1055
+ },
1056
+ 5 * 60 * 1000
1057
+ );
1058
+ }
1008
1059
  }
1009
1060
 
1010
1061
  /** Wire up all HolderClient callbacks (activity, CWD, output, exit, disconnect). */
@@ -15,6 +15,7 @@ import {
15
15
  handleEventsConnection,
16
16
  } from './events-handler.js';
17
17
  import { handleSessionConnection } from './session-handler.js';
18
+ import { handleSuperSessionConnection } from './super-session-handler.js';
18
19
  import { handleTerminalConnection } from './terminal-handler.js';
19
20
  export type { WireShooterEvent as ShooterEvent } from '$lib/types';
20
21
 
@@ -61,10 +62,11 @@ export function setupWebSocketHandlers(
61
62
 
62
63
  // Route matching
63
64
  const terminalMatch = /^\/ws\/terminal\/(.+)$/.exec(pathname);
65
+ const superSessionMatch = /^\/ws\/super-session\/(.+)$/.exec(pathname);
64
66
  const sessionMatch = /^\/ws\/session\/(.+)$/.exec(pathname);
65
67
  const isEvents = pathname === '/ws/events';
66
68
 
67
- if (!terminalMatch && !sessionMatch && !isEvents) {
69
+ if (!terminalMatch && !superSessionMatch && !sessionMatch && !isEvents) {
68
70
  socket.destroy();
69
71
  return;
70
72
  }
@@ -83,6 +85,9 @@ export function setupWebSocketHandlers(
83
85
  if (terminalMatch) {
84
86
  const terminalId = terminalMatch[1];
85
87
  handleTerminalConnection(ws, terminalId);
88
+ } else if (superSessionMatch) {
89
+ const superSessionId = superSessionMatch[1];
90
+ handleSuperSessionConnection(ws, superSessionId);
86
91
  } else if (sessionMatch) {
87
92
  const sessionId = sessionMatch[1];
88
93
  handleSessionConnection(ws, sessionId);
@@ -23,6 +23,7 @@ import * as fs from 'fs';
23
23
  import * as path from 'path';
24
24
 
25
25
  import { findCodexRolloutById } from '../sessions/codex-reader';
26
+ import { ptySubmitSequence } from '../terminal/pty-input';
26
27
 
27
28
  // ── Module-level references ──────────────────────────────────────────
28
29
 
@@ -222,7 +223,17 @@ function findTerminalBySessionUuid(uuid: string): ManagedTerminal | undefined {
222
223
  // module-level _ptyManagerFull reference if available.
223
224
  if (_ptyManagerFull) {
224
225
  for (const t of _ptyManagerFull.list()) {
225
- if (t.sessionFile?.includes(`${uuid}.jsonl`)) {
226
+ // Match across every provider's file naming so a running non-Claude
227
+ // agent can be reached (and replied to) by its session UUID:
228
+ // claude/cursor/qwen: <uuid>.jsonl ; codex: -<uuid>.jsonl ;
229
+ // copilot: <uuid>.jsonl OR <uuid>/events.jsonl ; amp: T-<uuid>.json ;
230
+ // opencode: bare session id.
231
+ if (
232
+ t.sessionFile?.includes(`${uuid}.jsonl`) ||
233
+ t.sessionFile?.endsWith(`/${uuid}/events.jsonl`) ||
234
+ t.sessionFile?.endsWith(`/T-${uuid}.json`) ||
235
+ t.openCodeSessionId === uuid
236
+ ) {
226
237
  return _ptyManager.getTerminal(t.id);
227
238
  }
228
239
  }
@@ -537,7 +548,11 @@ function wireClientMessages(ws: WebSocket, state: ConnectionState): void {
537
548
  safeSend(ws, { message: 'Terminal has exited', type: 'error' });
538
549
  return;
539
550
  }
540
- currentTerminal.pty.write(`${msg.text}\n`);
551
+ // Deliver + submit via a bracketed paste (see pty-input.ts): a bare
552
+ // LF never submits to an agent TUI and a trailing CR in the same
553
+ // write is absorbed as paste content. Bracketed paste closes the
554
+ // paste explicitly so the following CR is a real Enter.
555
+ currentTerminal.pty.write(ptySubmitSequence(msg.text));
541
556
  break;
542
557
  }
543
558
 
@@ -0,0 +1,200 @@
1
+ // WebSocket handler for /ws/super-session/:id — the merged Session-Over-Sessions
2
+ // stream. On connect it replays the merged transcript (sos-history) and then
3
+ // streams live tagged messages from every member. Accepts human-driven control
4
+ // messages: relay-forward (inject into a member's terminal), member-add,
5
+ // member-remove. Mirrors session-handler.ts in shape.
6
+
7
+ import type { SessionSource, SosClientMessage, SosServerMessage } from '$lib/types';
8
+ import type { WebSocket } from 'ws';
9
+
10
+ import * as path from 'path';
11
+
12
+ import { PROVIDERS } from '../sessions/registry';
13
+ import { sosCoordinator } from '../sos/coordinator';
14
+
15
+ const MAX_RELAY_TEXT = 10240; // 10 KB, same cap as /ws/session send-input
16
+ // Stop streaming to a client whose outbound buffer exceeds this — bounds memory
17
+ // growth when a slow consumer can't drain sos-history replay or a live burst.
18
+ const MAX_WS_BUFFERED_BYTES = 1_000_000;
19
+ const VALID_SOURCES = new Set<string>(PROVIDERS.map((p) => p.source));
20
+
21
+ export function handleSuperSessionConnection(ws: WebSocket, id: string): void {
22
+ const session = sosCoordinator.getSuperSession(id);
23
+ if (!session) {
24
+ safeSend(ws, { message: `Super-session not found: ${id}`, type: 'sos-error' });
25
+ ws.close(1008, 'Super-session not found');
26
+ return;
27
+ }
28
+
29
+ // subscribe() replays the current transcript as an sos-history message, then
30
+ // streams live sos-message / sos-member-* events. If a send fails (socket
31
+ // closed or buffer over cap), stop feeding this client to bound memory.
32
+ let unsubscribe: (() => void) | null = null;
33
+ unsubscribe = sosCoordinator.subscribe(id, (msg) => {
34
+ if (!safeSend(ws, msg)) {
35
+ unsubscribe?.();
36
+ unsubscribe = null;
37
+ }
38
+ });
39
+
40
+ ws.on('message', (raw: Buffer | string) => {
41
+ const data = typeof raw === 'string' ? raw : raw.toString('utf-8');
42
+ const msg = parseClientMessage(data);
43
+ if (!msg) {
44
+ // Surface schema drift instead of silently dropping the frame — a dead
45
+ // control in the UI is much harder to debug than an explicit error.
46
+ safeSend(ws, { message: 'Invalid super-session control message', type: 'sos-error' });
47
+ return;
48
+ }
49
+ handleClientMessage(ws, id, msg);
50
+ });
51
+
52
+ ws.on('close', () => {
53
+ unsubscribe?.();
54
+ });
55
+ ws.on('error', () => {
56
+ // cleanup happens in 'close'
57
+ });
58
+ }
59
+
60
+ /** True when the string is a known provider source. */
61
+ export function isValidProvider(value: string): value is SessionSource {
62
+ return VALID_SOURCES.has(value);
63
+ }
64
+
65
+ /** True for a bare session id (OpenCode) or an absolute path under HOME. */
66
+ export function isValidSessionKey(key: string): boolean {
67
+ if (/^[A-Za-z0-9_-]+$/.test(key)) {
68
+ return true;
69
+ }
70
+ const home = process.env.HOME || '';
71
+ if (home === '' || !path.isAbsolute(key)) {
72
+ return false;
73
+ }
74
+ // Resolve before comparing so `..` segments cannot escape the HOME sandbox
75
+ // (e.g. /home/user/../etc/passwd passes a raw prefix check but not this).
76
+ const relative = path.relative(path.resolve(home), path.resolve(key));
77
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
78
+ }
79
+
80
+ function handleClientMessage(ws: WebSocket, superSessionId: string, msg: SosClientMessage): void {
81
+ switch (msg.type) {
82
+ case 'member-add': {
83
+ const member = sosCoordinator.addMember(superSessionId, {
84
+ capability: msg.capability,
85
+ provider: msg.provider,
86
+ sessionKey: msg.sessionKey,
87
+ terminalId: msg.terminalId ?? null,
88
+ });
89
+ if (!member) {
90
+ safeSend(ws, { message: 'Failed to add member', type: 'sos-error' });
91
+ }
92
+ break;
93
+ }
94
+ case 'member-remove': {
95
+ const ok = sosCoordinator.removeMember(superSessionId, msg.memberId);
96
+ if (!ok) {
97
+ safeSend(ws, { message: 'Member not found', type: 'sos-error' });
98
+ }
99
+ break;
100
+ }
101
+ case 'relay-approve': {
102
+ if (!sosCoordinator.approveRelay(superSessionId, msg.relayId)) {
103
+ safeSend(ws, { message: 'Pending relay not found', type: 'sos-error' });
104
+ }
105
+ break;
106
+ }
107
+ case 'relay-deny': {
108
+ if (!sosCoordinator.denyRelay(superSessionId, msg.relayId)) {
109
+ safeSend(ws, { message: 'Pending relay not found', type: 'sos-error' });
110
+ }
111
+ break;
112
+ }
113
+ case 'relay-forward': {
114
+ const error = sosCoordinator.relayForward(superSessionId, msg.toMemberId, msg.text);
115
+ if (error) {
116
+ safeSend(ws, { message: error, type: 'sos-error' });
117
+ }
118
+ break;
119
+ }
120
+ }
121
+ }
122
+
123
+ function parseClientMessage(raw: string): null | SosClientMessage {
124
+ let parsed: unknown;
125
+ try {
126
+ parsed = JSON.parse(raw);
127
+ } catch {
128
+ return null;
129
+ }
130
+ if (typeof parsed !== 'object' || parsed === null) {
131
+ return null;
132
+ }
133
+ const msg = parsed as Record<string, unknown>;
134
+
135
+ switch (msg.type) {
136
+ case 'member-add': {
137
+ if (
138
+ typeof msg.sessionKey !== 'string' ||
139
+ !isValidSessionKey(msg.sessionKey) ||
140
+ typeof msg.provider !== 'string' ||
141
+ !isValidProvider(msg.provider)
142
+ ) {
143
+ return null;
144
+ }
145
+ return {
146
+ capability: typeof msg.capability === 'string' ? msg.capability : undefined,
147
+ provider: msg.provider,
148
+ sessionKey: msg.sessionKey,
149
+ terminalId: typeof msg.terminalId === 'string' ? msg.terminalId : undefined,
150
+ type: 'member-add',
151
+ };
152
+ }
153
+ case 'member-remove': {
154
+ if (typeof msg.memberId !== 'string' || msg.memberId.length === 0) {
155
+ return null;
156
+ }
157
+ return { memberId: msg.memberId, type: 'member-remove' };
158
+ }
159
+ case 'relay-approve': {
160
+ if (typeof msg.relayId !== 'string' || msg.relayId.length === 0) {
161
+ return null;
162
+ }
163
+ return { relayId: msg.relayId, type: 'relay-approve' };
164
+ }
165
+ case 'relay-deny': {
166
+ if (typeof msg.relayId !== 'string' || msg.relayId.length === 0) {
167
+ return null;
168
+ }
169
+ return { relayId: msg.relayId, type: 'relay-deny' };
170
+ }
171
+ case 'relay-forward': {
172
+ if (
173
+ typeof msg.toMemberId !== 'string' ||
174
+ typeof msg.text !== 'string' ||
175
+ msg.text.length === 0 ||
176
+ msg.text.length > MAX_RELAY_TEXT
177
+ ) {
178
+ return null;
179
+ }
180
+ return { text: msg.text, toMemberId: msg.toMemberId, type: 'relay-forward' };
181
+ }
182
+ default:
183
+ return null;
184
+ }
185
+ }
186
+
187
+ function safeSend(ws: WebSocket, msg: SosServerMessage): boolean {
188
+ try {
189
+ if (ws.readyState !== 1 /* OPEN */) {
190
+ return false;
191
+ }
192
+ if (ws.bufferedAmount > MAX_WS_BUFFERED_BYTES) {
193
+ return false;
194
+ }
195
+ ws.send(JSON.stringify(msg));
196
+ return true;
197
+ } catch {
198
+ return false;
199
+ }
200
+ }
package/src/lib/theme.css CHANGED
@@ -527,8 +527,7 @@
527
527
  }
528
528
 
529
529
  /* Additional providers reuse the existing colour families (cosmetic only). */
530
- .pill-source-qwen,
531
- .pill-source-iflow {
530
+ .pill-source-qwen {
532
531
  --pill-background: var(--ds-red-100);
533
532
  --pill-color: var(--ds-red-900);
534
533
  --pill-font-size: 10px;
@@ -22,8 +22,7 @@ export type SessionSource =
22
22
  | 'qwen'
23
23
  | 'cursor'
24
24
  | 'copilot'
25
- | 'amp'
26
- | 'iflow';
25
+ | 'amp';
27
26
 
28
27
  export function decodeSessionSource(rawInput: unknown): SessionSource | null {
29
28
  switch (rawInput) {
@@ -35,7 +34,6 @@ export function decodeSessionSource(rawInput: unknown): SessionSource | null {
35
34
  case 'cursor':
36
35
  case 'copilot':
37
36
  case 'amp':
38
- case 'iflow':
39
37
  return rawInput;
40
38
  }
41
39
  return null;
@@ -51,7 +49,6 @@ export function _decodeSessionSource(rawInput: unknown): SessionSource | undefin
51
49
  case 'cursor':
52
50
  case 'copilot':
53
51
  case 'amp':
54
- case 'iflow':
55
52
  return rawInput;
56
53
  }
57
54
  return;
@@ -12,8 +12,8 @@ export type * from './gemini';
12
12
  export * from './generated';
13
13
  export type * from './neurolink';
14
14
  export type * from './server';
15
-
16
15
  export type * from './sessions';
16
+
17
17
  // Explicit re-exports to resolve conflicts between generated types and the
18
18
  // hand-written sessions.ts versions. The generated Sessions module exports
19
19
  // wrapper-class variants (CMessagePartTextPart, etc.) with `type: string`
@@ -27,5 +27,6 @@ export type {
27
27
  ToolResultPart,
28
28
  ToolUsePart,
29
29
  } from './sessions';
30
+ export type * from './sos';
30
31
  export type * from './terminal-client';
31
32
  export type * from './ws';
@@ -24,6 +24,23 @@ export interface CodexWatchState {
24
24
  watcher: FSWatcher;
25
25
  }
26
26
 
27
+ /**
28
+ * Watch state for the generic re-read-on-change watcher used by the
29
+ * read-only providers (cursor, copilot, qwen, gemini, amp). Unlike the
30
+ * byte-offset watchers, it re-parses the whole file on each change and
31
+ * dedupes by message ID — robust for both append-only JSONL and
32
+ * whole-document JSON formats.
33
+ */
34
+ export interface GenericWatchedFile {
35
+ callbacks: Set<OnNewEntries>;
36
+ /** Message IDs already delivered, so re-reads only emit genuinely new messages. */
37
+ emittedMessageIds: Set<string>;
38
+ filePath: string;
39
+ watcher: FSWatcher;
40
+ }
41
+
42
+ // ── pty-manager types ───────────────────────────────────────────────
43
+
27
44
  /** Messages received from the holder process (local ndjson protocol). */
28
45
  export type HolderIncomingMessage =
29
46
  | { active: boolean; type: 'activity' }
@@ -33,21 +50,19 @@ export type HolderIncomingMessage =
33
50
  | { exitCode: null | number; exited: boolean; pid: number; type: 'info' }
34
51
  | { path: string; type: 'cwd' };
35
52
 
36
- // ── pty-manager types ───────────────────────────────────────────────
37
-
38
53
  /** Messages sent to the holder process (local ndjson protocol). */
39
54
  export type HolderOutgoingMessage =
40
55
  | { cols: number; rows: number; type: 'resize' }
41
56
  | { data: string; type: 'input' }
42
57
  | { signal?: string; type: 'kill' };
43
58
 
59
+ // ── session-watcher types ───────────────────────────────────────────
60
+
44
61
  /**
45
62
  * Callback invoked when new JSONL entries are parsed from a watched file.
46
63
  */
47
64
  export type OnNewEntries = (entries: ConversationMessage[]) => void;
48
65
 
49
- // ── session-watcher types ───────────────────────────────────────────
50
-
51
66
  export interface OpenCodeWatchState {
52
67
  callbacks: Set<(messages: ConversationMessage[]) => void>;
53
68
  /** Set of message IDs we have already emitted, to avoid duplicates. */
@@ -62,6 +77,8 @@ export interface OpenCodeWatchState {
62
77
  sessionId: string;
63
78
  }
64
79
 
80
+ // ── opencode-watcher types ──────────────────────────────────────────
81
+
65
82
  export interface PtyManagedTerminal {
66
83
  args: string[];
67
84
  clients: Set<WebSocket>;
@@ -89,14 +106,14 @@ export interface PtyManagedTerminal {
89
106
  watcherOffset: number;
90
107
  }
91
108
 
92
- // ── opencode-watcher types ──────────────────────────────────────────
109
+ // ── codex-watcher types ─────────────────────────────────────────────
93
110
 
94
111
  export interface PtyOutputBuffer {
95
112
  data: string[];
96
113
  size: number;
97
114
  }
98
115
 
99
- // ── codex-watcher types ─────────────────────────────────────────────
116
+ // ── generic-session-watcher types ───────────────────────────────────
100
117
 
101
118
  export interface SessionWatchedFile {
102
119
  callbacks: Set<OnNewEntries>;
@@ -23,16 +23,7 @@ export interface ConversationMessage {
23
23
  }
24
24
 
25
25
  export interface DetectedProcess {
26
- command:
27
- | 'amp'
28
- | 'claude'
29
- | 'codex'
30
- | 'copilot'
31
- | 'cursor-agent'
32
- | 'gemini'
33
- | 'iflow'
34
- | 'opencode'
35
- | 'qwen';
26
+ command: 'amp' | 'claude' | 'codex' | 'copilot' | 'cursor-agent' | 'gemini' | 'opencode' | 'qwen';
36
27
  cwd: string;
37
28
  kind: string;
38
29
  pid: number;