@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,542 @@
1
+ /**
2
+ * GitHub Copilot CLI session reader — listing + conversation retrieval.
3
+ *
4
+ * Mirrors qwen-reader.ts for structure/conventions: produces the same
5
+ * SessionInfo / ProjectGroup / ConversationMessage shapes so the rest of
6
+ * the app is provider-agnostic.
7
+ *
8
+ * Copilot sessions are stored at:
9
+ * - ~/.copilot/session-state/<uuid>.jsonl (flat file form)
10
+ * - ~/.copilot/session-state/<uuid>/events.jsonl (directory form)
11
+ *
12
+ * Each file is a sequence of typed event lines. Every event wraps its
13
+ * payload under a top-level `data` field:
14
+ * session.start { data: { sessionId, context: { cwd, branch } } }
15
+ * user.message { data: { content } }
16
+ * assistant.message { data: { content, toolRequests?, reasoningText? } }
17
+ * assistant.reasoning { data: { text? } } → thinking part
18
+ * tool.execution_complete { data: { toolCallId, result } } → tool_result part
19
+ * session.model_change (skipped)
20
+ */
21
+
22
+ import type { ConversationMessage, MessagePart, ProjectGroup, SessionInfo } from '$lib/types';
23
+
24
+ import * as crypto from 'crypto';
25
+ import * as fs from 'fs';
26
+ import { homedir } from 'os';
27
+ import * as path from 'path';
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Constants
31
+ // ---------------------------------------------------------------------------
32
+
33
+ /** Bytes read from the head of a session file when listing. */
34
+ const PREFIX_BYTES = 64 * 1024;
35
+
36
+ /** Max bytes read for a full conversation parse. */
37
+ const MAX_FULL_READ_BYTES = 16 * 1024 * 1024;
38
+
39
+ /** Root directory where Copilot stores session state. */
40
+ const COPILOT_SESSION_STATE = path.join(homedir(), '.copilot', 'session-state');
41
+
42
+ /** Regex to validate session IDs used in path construction (UUIDs). */
43
+ const SESSION_ID_RE = /^[A-Za-z0-9_.-]+$/;
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Public API
47
+ // ---------------------------------------------------------------------------
48
+
49
+ /**
50
+ * Return sessions whose JSONL file was written within `thresholdMs` of now —
51
+ * i.e. sessions that are currently (or very recently) active.
52
+ */
53
+ export function detectActiveCopilotSessions(
54
+ thresholdMs: number
55
+ ): { cwd: string; id: string; startedAt: number }[] {
56
+ const cutoff = Date.now() - thresholdMs;
57
+ const out: { cwd: string; id: string; startedAt: number }[] = [];
58
+
59
+ for (const filePath of collectCopilotFiles()) {
60
+ try {
61
+ const stat = fs.statSync(filePath);
62
+ if (stat.mtimeMs < cutoff) {
63
+ continue;
64
+ }
65
+ const meta = readMeta(readPrefix(filePath), filePath);
66
+ if (meta) {
67
+ out.push({ cwd: meta.cwd, id: meta.id, startedAt: stat.birthtimeMs });
68
+ }
69
+ } catch {
70
+ // skip unreadable files
71
+ }
72
+ }
73
+
74
+ return out;
75
+ }
76
+
77
+ /**
78
+ * Return the conversation messages for a given Copilot session ID.
79
+ * Mirrors the pagination behaviour of getQwenConversation().
80
+ */
81
+ export function getCopilotConversation(
82
+ sessionId: string,
83
+ offset = 0,
84
+ limit = 200
85
+ ): ConversationMessage[] {
86
+ if (!SESSION_ID_RE.test(sessionId)) {
87
+ return [];
88
+ }
89
+
90
+ const filePath = resolveCopilotSessionFile(sessionId);
91
+ if (!filePath) {
92
+ return [];
93
+ }
94
+
95
+ try {
96
+ const text = readBounded(filePath);
97
+ const messages = parseCopilotText(text);
98
+
99
+ if (offset === 0 && messages.length > limit) {
100
+ let startIdx = messages.length - limit;
101
+ while (startIdx > 0 && messages[startIdx]?.role !== 'user') {
102
+ startIdx--;
103
+ }
104
+ return messages.slice(startIdx);
105
+ }
106
+ return messages.slice(offset, offset + limit);
107
+ } catch (error) {
108
+ console.error('[copilot] Failed to read conversation:', error);
109
+ return [];
110
+ }
111
+ }
112
+
113
+ /**
114
+ * List all Copilot sessions grouped by project (cwd), sorted by most-recently-
115
+ * modified first. Mirrors listQwenProjects().
116
+ *
117
+ * The session id exposed to callers is the filename stem (not data.sessionId)
118
+ * so that list and get agree — resolveCopilotSessionFile uses the filename stem.
119
+ */
120
+ export function listCopilotProjects(): ProjectGroup[] {
121
+ const byCwd = new Map<string, SessionInfo[]>();
122
+
123
+ for (const filePath of collectCopilotFiles()) {
124
+ let stat: fs.Stats;
125
+ try {
126
+ stat = fs.statSync(filePath);
127
+ } catch {
128
+ continue;
129
+ }
130
+
131
+ const prefix = safeReadPrefix(filePath);
132
+ if (!prefix) {
133
+ continue;
134
+ }
135
+
136
+ const meta = readMeta(prefix, filePath);
137
+ if (!meta) {
138
+ continue;
139
+ }
140
+
141
+ const session: SessionInfo = {
142
+ created: stat.birthtime.toISOString(),
143
+ gitBranch: meta.gitBranch,
144
+ id: meta.id,
145
+ messageCount: 0,
146
+ modified: stat.mtime.toISOString(),
147
+ projectPath: meta.cwd,
148
+ source: 'copilot' as const,
149
+ summary: '',
150
+ title: meta.title || 'Untitled Session',
151
+ };
152
+
153
+ const bucket = byCwd.get(meta.cwd);
154
+ if (bucket) {
155
+ bucket.push(session);
156
+ } else {
157
+ byCwd.set(meta.cwd, [session]);
158
+ }
159
+ }
160
+
161
+ const projects: ProjectGroup[] = [];
162
+ for (const [cwd, sessions] of byCwd) {
163
+ sessions.sort((a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime());
164
+ const segments = cwd.split('/').filter(Boolean);
165
+ projects.push({
166
+ fullPath: cwd,
167
+ id: shortHash(cwd),
168
+ lastModified: sessions[0]?.modified ?? '',
169
+ name: segments.slice(-2).join('/'),
170
+ sessionCount: sessions.length,
171
+ sessions,
172
+ });
173
+ }
174
+
175
+ return projects.sort(
176
+ (a, b) => new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime()
177
+ );
178
+ }
179
+
180
+ /**
181
+ * Parse the single Copilot JSONL file at `filePath` into the full list of
182
+ * ConversationMessage with no pagination or tail-limit applied.
183
+ * Returns `[]` on any read or parse error.
184
+ */
185
+ export function parseCopilotSessionFile(filePath: string): ConversationMessage[] {
186
+ try {
187
+ return parseCopilotText(readBounded(filePath));
188
+ } catch (error) {
189
+ console.error('[copilot] Failed to parse session file:', error);
190
+ return [];
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Return the absolute path of the JSONL file backing `sessionId`, or `null`.
196
+ * Validates `sessionId` the same way `getCopilotConversation` does and
197
+ * enumerates via `collectCopilotFiles` so both flat and directory forms are
198
+ * handled identically to how the rest of the reader discovers them.
199
+ */
200
+ export function resolveCopilotSessionFile(sessionId: string): null | string {
201
+ if (!SESSION_ID_RE.test(sessionId)) {
202
+ return null;
203
+ }
204
+ for (const filePath of collectCopilotFiles()) {
205
+ if (sessionIdFromFilePath(filePath) === sessionId) {
206
+ return filePath;
207
+ }
208
+ }
209
+ return null;
210
+ }
211
+
212
+ // ---------------------------------------------------------------------------
213
+ // File collection helpers
214
+ // ---------------------------------------------------------------------------
215
+
216
+ /**
217
+ * Collect all Copilot JSONL file paths from ~/.copilot/session-state/.
218
+ * Supports both the flat <uuid>.jsonl and the directory <uuid>/events.jsonl forms.
219
+ */
220
+ function collectCopilotFiles(): string[] {
221
+ const out: string[] = [];
222
+ let entries: fs.Dirent[];
223
+ try {
224
+ entries = fs.readdirSync(COPILOT_SESSION_STATE, { withFileTypes: true });
225
+ } catch {
226
+ return out;
227
+ }
228
+
229
+ for (const entry of entries) {
230
+ const full = path.join(COPILOT_SESSION_STATE, entry.name);
231
+ if (entry.isFile() && entry.name.endsWith('.jsonl')) {
232
+ // Flat form: <uuid>.jsonl
233
+ out.push(full);
234
+ } else if (entry.isDirectory()) {
235
+ // Directory form: <uuid>/events.jsonl
236
+ const eventsPath = path.join(full, 'events.jsonl');
237
+ try {
238
+ fs.statSync(eventsPath);
239
+ out.push(eventsPath);
240
+ } catch {
241
+ // no events.jsonl in this directory
242
+ }
243
+ }
244
+ }
245
+
246
+ return out;
247
+ }
248
+
249
+ // ---------------------------------------------------------------------------
250
+ // Parsing helpers
251
+ // ---------------------------------------------------------------------------
252
+
253
+ /**
254
+ * Map a single parsed Copilot event line to a ConversationMessage.
255
+ * Returns null for events that don't map to conversation turns.
256
+ * `pendingToolUseId` is a mutable box used to thread the most recent
257
+ * tool_use id through to the following tool.execution_complete event.
258
+ *
259
+ * Real format: every event wraps its payload under entry.data:
260
+ * {"type":"user.message","data":{"content":"..."},...}
261
+ * We fall back to reading from entry directly for forward-compat.
262
+ */
263
+ function copilotEventToMessage(
264
+ entry: Record<string, unknown>,
265
+ pendingToolUseId: { value: string }
266
+ ): ConversationMessage | null {
267
+ const type = typeof entry.type === 'string' ? entry.type : '';
268
+ const ts = typeof entry.timestamp === 'string' ? entry.timestamp : '';
269
+
270
+ // All payloads are nested under entry.data; fall back to entry for compat.
271
+ const data = isRecord(entry.data) ? entry.data : entry;
272
+
273
+ if (type === 'user.message') {
274
+ const content = typeof data.content === 'string' ? data.content : '';
275
+ if (!content) {
276
+ return null;
277
+ }
278
+ const id = typeof data.id === 'string' ? data.id : `copilot-user-${ts}`;
279
+ return {
280
+ id,
281
+ parts: [{ content, type: 'text' }],
282
+ role: 'user',
283
+ timestamp: ts,
284
+ };
285
+ }
286
+
287
+ if (type === 'assistant.reasoning') {
288
+ const text = typeof data.text === 'string' ? data.text : '';
289
+ if (!text) {
290
+ return null;
291
+ }
292
+ const id = typeof data.id === 'string' ? data.id : `copilot-reasoning-${ts}`;
293
+ return {
294
+ id,
295
+ parts: [{ content: text, type: 'thinking' }],
296
+ role: 'assistant',
297
+ timestamp: ts,
298
+ };
299
+ }
300
+
301
+ if (type === 'assistant.message') {
302
+ const content = typeof data.content === 'string' ? data.content : '';
303
+ const parts: MessagePart[] = [];
304
+
305
+ // Copilot can attach reasoning inline on the message (reasoningText) rather
306
+ // than as a separate assistant.reasoning event. Emit it as a thinking part
307
+ // that precedes the visible content.
308
+ const reasoningText = typeof data.reasoningText === 'string' ? data.reasoningText : '';
309
+ if (reasoningText) {
310
+ parts.push({ content: reasoningText, type: 'thinking' });
311
+ }
312
+
313
+ if (content) {
314
+ parts.push({ content, type: 'text' });
315
+ }
316
+
317
+ // toolRequests is an array of tool-call objects in the real format.
318
+ if (Array.isArray(data.toolRequests)) {
319
+ for (const req of data.toolRequests) {
320
+ if (!isRecord(req)) {
321
+ continue;
322
+ }
323
+ const toolId = typeof req.toolCallId === 'string' ? req.toolCallId : `copilot-tool-${ts}`;
324
+ pendingToolUseId.value = toolId;
325
+
326
+ // arguments may be a JSON string or a plain object.
327
+ let input: Record<string, unknown> = {};
328
+ if (isRecord(req.arguments)) {
329
+ input = req.arguments;
330
+ } else if (typeof req.arguments === 'string') {
331
+ try {
332
+ const parsed = JSON.parse(req.arguments) as unknown;
333
+ if (isRecord(parsed)) {
334
+ input = parsed;
335
+ }
336
+ } catch {
337
+ // leave input as {}
338
+ }
339
+ }
340
+
341
+ parts.push({
342
+ id: toolId,
343
+ input,
344
+ toolName: typeof req.name === 'string' ? req.name : 'tool',
345
+ type: 'tool_use',
346
+ });
347
+ }
348
+ }
349
+
350
+ if (parts.length === 0) {
351
+ return null;
352
+ }
353
+
354
+ const id = typeof data.id === 'string' ? data.id : `copilot-assistant-${ts}`;
355
+ return {
356
+ id,
357
+ parts,
358
+ role: 'assistant',
359
+ timestamp: ts,
360
+ };
361
+ }
362
+
363
+ if (type === 'tool.execution_complete') {
364
+ const rawResult = data.result;
365
+ const result = typeof rawResult === 'string' ? rawResult : JSON.stringify(rawResult ?? '');
366
+ // Copilot signals failure via `success: false`; older shapes used `isError`.
367
+ const isError = data.isError === true || data.success === false;
368
+ const toolCallId =
369
+ typeof data.toolCallId === 'string' ? data.toolCallId : pendingToolUseId.value;
370
+ const toolUseId = toolCallId || pendingToolUseId.value || 'unknown';
371
+ const id = typeof data.id === 'string' ? data.id : `copilot-tool-result-${ts}`;
372
+ return {
373
+ id,
374
+ parts: [
375
+ {
376
+ isError,
377
+ output: result.slice(0, 2000),
378
+ toolUseId,
379
+ type: 'tool_result',
380
+ },
381
+ ],
382
+ role: 'system',
383
+ timestamp: ts,
384
+ };
385
+ }
386
+
387
+ return null;
388
+ }
389
+
390
+ /** Narrow an unknown value to a plain object with string keys. */
391
+ function isRecord(value: unknown): value is Record<string, unknown> {
392
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
393
+ }
394
+
395
+ /**
396
+ * Parse raw Copilot JSONL text into ConversationMessage entries.
397
+ */
398
+ function parseCopilotText(text: string): ConversationMessage[] {
399
+ const messages: ConversationMessage[] = [];
400
+ // Mutable box to track the most recent tool_use id for matching tool_result events.
401
+ const pendingToolUseId = { value: '' };
402
+
403
+ for (const line of text.split('\n')) {
404
+ const trimmed = line.trim();
405
+ if (!trimmed) {
406
+ continue;
407
+ }
408
+ try {
409
+ const entry = JSON.parse(trimmed) as Record<string, unknown>;
410
+ const msg = copilotEventToMessage(entry, pendingToolUseId);
411
+ if (msg) {
412
+ messages.push(msg);
413
+ // Reset pending tool use id once we've emitted a tool_result
414
+ if (msg.parts[0]?.type === 'tool_result') {
415
+ pendingToolUseId.value = '';
416
+ }
417
+ }
418
+ } catch {
419
+ // skip malformed lines
420
+ }
421
+ }
422
+
423
+ return messages;
424
+ }
425
+
426
+ /**
427
+ * Read a Copilot JSONL file bounded to MAX_FULL_READ_BYTES.
428
+ * For oversized files, keeps the first line (session.start) + the tail.
429
+ */
430
+ function readBounded(filePath: string): string {
431
+ const stat = fs.statSync(filePath);
432
+ if (stat.size <= MAX_FULL_READ_BYTES) {
433
+ return fs.readFileSync(filePath, 'utf-8');
434
+ }
435
+ // Oversized: keep first line (session.start) + tail.
436
+ const head = readPrefix(filePath).split('\n')[0] ?? '';
437
+ const fd = fs.openSync(filePath, 'r');
438
+ try {
439
+ const start = stat.size - MAX_FULL_READ_BYTES;
440
+ const buf = Buffer.alloc(MAX_FULL_READ_BYTES);
441
+ const bytesRead = fs.readSync(fd, buf, 0, MAX_FULL_READ_BYTES, start);
442
+ const tail = buf.toString('utf-8', 0, bytesRead);
443
+ return `${head}\n${tail.slice(tail.indexOf('\n') + 1)}`;
444
+ } finally {
445
+ fs.closeSync(fd);
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Extract metadata (id, cwd, gitBranch, title) from a session file prefix.
451
+ * Uses the filename stem as the session id (not data.sessionId) so that the
452
+ * id exposed in listCopilotProjects matches what resolveCopilotSessionFile expects.
453
+ */
454
+ function readMeta(
455
+ prefix: string,
456
+ filePath: string
457
+ ): null | { cwd: string; gitBranch: string; id: string; title: string } {
458
+ let cwd = '';
459
+ let gitBranch = '';
460
+ let title = '';
461
+
462
+ // Session id is always the filename stem — never data.sessionId — so that
463
+ // getCopilotConversation can resolve the file by id.
464
+ const id = sessionIdFromFilePath(filePath);
465
+
466
+ for (const line of prefix.split('\n')) {
467
+ const trimmed = line.trim();
468
+ if (!trimmed) {
469
+ continue;
470
+ }
471
+ let entry: Record<string, unknown>;
472
+ try {
473
+ entry = JSON.parse(trimmed) as Record<string, unknown>;
474
+ } catch {
475
+ continue;
476
+ }
477
+
478
+ const type = typeof entry.type === 'string' ? entry.type : '';
479
+ const data = isRecord(entry.data) ? entry.data : entry;
480
+
481
+ if (type === 'session.start') {
482
+ // cwd and branch are under data.context in the real format.
483
+ const context = isRecord(data.context) ? data.context : data;
484
+ if (!cwd && typeof context.cwd === 'string') {
485
+ cwd = context.cwd;
486
+ }
487
+ if (!gitBranch && typeof context.branch === 'string') {
488
+ gitBranch = context.branch;
489
+ }
490
+ }
491
+
492
+ if (!title && type === 'user.message' && typeof data.content === 'string') {
493
+ title = data.content.split('\n')[0]?.slice(0, 80) ?? '';
494
+ }
495
+
496
+ if (cwd && title) {
497
+ break;
498
+ }
499
+ }
500
+
501
+ return cwd ? { cwd, gitBranch, id, title } : null;
502
+ }
503
+
504
+ /** Read the head of a file (complete lines only). */
505
+ function readPrefix(filePath: string): string {
506
+ const fd = fs.openSync(filePath, 'r');
507
+ try {
508
+ const buf = Buffer.alloc(PREFIX_BYTES);
509
+ const n = fs.readSync(fd, buf, 0, PREFIX_BYTES, 0);
510
+ const text = buf.toString('utf-8', 0, n);
511
+ const lastNl = text.lastIndexOf('\n');
512
+ return lastNl === -1 ? text : text.slice(0, lastNl);
513
+ } finally {
514
+ fs.closeSync(fd);
515
+ }
516
+ }
517
+
518
+ /** Read prefix, returning empty string on any I/O error. */
519
+ function safeReadPrefix(filePath: string): string {
520
+ try {
521
+ return readPrefix(filePath);
522
+ } catch {
523
+ return '';
524
+ }
525
+ }
526
+
527
+ /**
528
+ * Derive the session id from a file path — filename stem for flat files,
529
+ * parent directory name for directory-form events.jsonl.
530
+ * This must match resolveCopilotSessionFile so that list and get agree.
531
+ */
532
+ function sessionIdFromFilePath(filePath: string): string {
533
+ const base = path.basename(filePath);
534
+ if (base === 'events.jsonl') {
535
+ return path.basename(path.dirname(filePath));
536
+ }
537
+ return base.replace(/\.jsonl$/, '');
538
+ }
539
+
540
+ function shortHash(input: string): string {
541
+ return crypto.createHash('sha256').update(input).digest('hex').slice(0, 8);
542
+ }