@juspay/shooter 1.21.0 → 1.23.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 (221) hide show
  1. package/.claude/hooks/notifier.cjs +94 -1
  2. package/build/client/_app/immutable/assets/2.JWRrnR-w.css +1 -0
  3. package/build/client/_app/immutable/assets/2.JWRrnR-w.css.br +0 -0
  4. package/build/client/_app/immutable/assets/2.JWRrnR-w.css.gz +0 -0
  5. package/build/client/_app/immutable/chunks/{DOEXXmsh.js → Bj5wFimK.js} +2 -2
  6. package/build/client/_app/immutable/chunks/Bj5wFimK.js.br +0 -0
  7. package/build/client/_app/immutable/chunks/Bj5wFimK.js.gz +0 -0
  8. package/build/client/_app/immutable/chunks/{EqMAkEha.js → BjYr_-Ss.js} +1 -1
  9. package/build/client/_app/immutable/chunks/BjYr_-Ss.js.br +0 -0
  10. package/build/client/_app/immutable/chunks/BjYr_-Ss.js.gz +0 -0
  11. package/build/client/_app/immutable/chunks/C4Hns_Wl.js +1 -0
  12. package/build/client/_app/immutable/chunks/C4Hns_Wl.js.br +0 -0
  13. package/build/client/_app/immutable/chunks/C4Hns_Wl.js.gz +0 -0
  14. package/build/client/_app/immutable/chunks/DULfdsh6.js +6 -0
  15. package/build/client/_app/immutable/chunks/DULfdsh6.js.br +0 -0
  16. package/build/client/_app/immutable/chunks/DULfdsh6.js.gz +0 -0
  17. package/build/client/_app/immutable/chunks/{BmfLecb1.js → fcNfTA-E.js} +1 -1
  18. package/build/client/_app/immutable/chunks/fcNfTA-E.js.br +0 -0
  19. package/build/client/_app/immutable/chunks/fcNfTA-E.js.gz +0 -0
  20. package/build/client/_app/immutable/entry/{app.CeSxgGat.js → app.Bvoqymnp.js} +2 -2
  21. package/build/client/_app/immutable/entry/app.Bvoqymnp.js.br +0 -0
  22. package/build/client/_app/immutable/entry/app.Bvoqymnp.js.gz +0 -0
  23. package/build/client/_app/immutable/entry/start.BqXCPPZJ.js +1 -0
  24. package/build/client/_app/immutable/entry/start.BqXCPPZJ.js.br +2 -0
  25. package/build/client/_app/immutable/entry/start.BqXCPPZJ.js.gz +0 -0
  26. package/build/client/_app/immutable/nodes/{0.oaPwxh1O.js → 0.Bv_TwEnq.js} +1 -1
  27. package/build/client/_app/immutable/nodes/0.Bv_TwEnq.js.br +0 -0
  28. package/build/client/_app/immutable/nodes/0.Bv_TwEnq.js.gz +0 -0
  29. package/build/client/_app/immutable/nodes/{1.DMPyoM-M.js → 1.7lffTIeb.js} +1 -1
  30. package/build/client/_app/immutable/nodes/1.7lffTIeb.js.br +0 -0
  31. package/build/client/_app/immutable/nodes/1.7lffTIeb.js.gz +0 -0
  32. package/build/client/_app/immutable/nodes/{10.Cbm7nQKK.js → 10.ChiIrIDl.js} +1 -1
  33. package/build/client/_app/immutable/nodes/10.ChiIrIDl.js.br +0 -0
  34. package/build/client/_app/immutable/nodes/10.ChiIrIDl.js.gz +0 -0
  35. package/build/client/_app/immutable/nodes/{11.CKmZjP_a.js → 11.DO3vyXEv.js} +2 -2
  36. package/build/client/_app/immutable/nodes/11.DO3vyXEv.js.br +0 -0
  37. package/build/client/_app/immutable/nodes/{11.CKmZjP_a.js.gz → 11.DO3vyXEv.js.gz} +0 -0
  38. package/build/client/_app/immutable/nodes/2.iMIqsE7n.js +23 -0
  39. package/build/client/_app/immutable/nodes/2.iMIqsE7n.js.br +0 -0
  40. package/build/client/_app/immutable/nodes/2.iMIqsE7n.js.gz +0 -0
  41. package/build/client/_app/immutable/nodes/{3.BgLpGnzb.js → 3.CArnSHOO.js} +1 -1
  42. package/build/client/_app/immutable/nodes/3.CArnSHOO.js.br +0 -0
  43. package/build/client/_app/immutable/nodes/3.CArnSHOO.js.gz +0 -0
  44. package/build/client/_app/immutable/nodes/{5.Avc1-gVb.js → 5.DziEu9rx.js} +1 -1
  45. package/build/client/_app/immutable/nodes/5.DziEu9rx.js.br +0 -0
  46. package/build/client/_app/immutable/nodes/5.DziEu9rx.js.gz +0 -0
  47. package/build/client/_app/immutable/nodes/{6.Dw2wEssJ.js → 6.B8l1RwkB.js} +1 -1
  48. package/build/client/_app/immutable/nodes/6.B8l1RwkB.js.br +0 -0
  49. package/build/client/_app/immutable/nodes/6.B8l1RwkB.js.gz +0 -0
  50. package/build/client/_app/immutable/nodes/{7.DwKZjoBg.js → 7.BPyfhDis.js} +1 -1
  51. package/build/client/_app/immutable/nodes/7.BPyfhDis.js.br +0 -0
  52. package/build/client/_app/immutable/nodes/7.BPyfhDis.js.gz +0 -0
  53. package/build/client/_app/immutable/nodes/{8.ZUAI6g5E.js → 8.D_vszZ9E.js} +1 -1
  54. package/build/client/_app/immutable/nodes/8.D_vszZ9E.js.br +0 -0
  55. package/build/client/_app/immutable/nodes/8.D_vszZ9E.js.gz +0 -0
  56. package/build/client/_app/immutable/nodes/{9.I_KGXPwB.js → 9.Drah-do-.js} +1 -1
  57. package/build/client/_app/immutable/nodes/9.Drah-do-.js.br +0 -0
  58. package/build/client/_app/immutable/nodes/9.Drah-do-.js.gz +0 -0
  59. package/build/client/_app/version.json +1 -1
  60. package/build/client/_app/version.json.br +0 -0
  61. package/build/client/_app/version.json.gz +0 -0
  62. package/build/pty-holder.cjs +6 -0
  63. package/build/server/chunks/{0-vrTNAfZB.js → 0-DAB_6Vm1.js} +2 -2
  64. package/build/server/chunks/{0-vrTNAfZB.js.map → 0-DAB_6Vm1.js.map} +1 -1
  65. package/build/server/chunks/{1-nbr-bOoF.js → 1-D-qMYaCx.js} +2 -2
  66. package/build/server/chunks/{1-nbr-bOoF.js.map → 1-D-qMYaCx.js.map} +1 -1
  67. package/build/server/chunks/{10-ChyvvJ6w.js → 10-CeFFGo-X.js} +2 -2
  68. package/build/server/chunks/{10-ChyvvJ6w.js.map → 10-CeFFGo-X.js.map} +1 -1
  69. package/build/server/chunks/{11-6ZAjL3uU.js → 11-DRMu_ATU.js} +2 -2
  70. package/build/server/chunks/{11-6ZAjL3uU.js.map → 11-DRMu_ATU.js.map} +1 -1
  71. package/build/server/chunks/{2-DWFRVDWJ.js → 2-B7OLBMNH.js} +4 -4
  72. package/build/server/chunks/{2-DWFRVDWJ.js.map → 2-B7OLBMNH.js.map} +1 -1
  73. package/build/server/chunks/{3-CKANM_WM.js → 3-B38ZarLw.js} +2 -2
  74. package/build/server/chunks/{3-CKANM_WM.js.map → 3-B38ZarLw.js.map} +1 -1
  75. package/build/server/chunks/{5-BxVjs2qi.js → 5-D-Uv1voC.js} +2 -2
  76. package/build/server/chunks/{5-BxVjs2qi.js.map → 5-D-Uv1voC.js.map} +1 -1
  77. package/build/server/chunks/{6-Cbf1AAMQ.js → 6-DP46cUej.js} +2 -2
  78. package/build/server/chunks/{6-Cbf1AAMQ.js.map → 6-DP46cUej.js.map} +1 -1
  79. package/build/server/chunks/{7-CMK2quEf.js → 7-B29_3ar6.js} +2 -2
  80. package/build/server/chunks/{7-CMK2quEf.js.map → 7-B29_3ar6.js.map} +1 -1
  81. package/build/server/chunks/{8-DhdfkfDM.js → 8-DCnSDVrX.js} +2 -2
  82. package/build/server/chunks/{8-DhdfkfDM.js.map → 8-DCnSDVrX.js.map} +1 -1
  83. package/build/server/chunks/{9-CPpxtRM5.js → 9-BwqDc8wC.js} +2 -2
  84. package/build/server/chunks/{9-CPpxtRM5.js.map → 9-BwqDc8wC.js.map} +1 -1
  85. package/build/server/chunks/_page.svelte-8OFzwdNA.js +758 -0
  86. package/build/server/chunks/_page.svelte-8OFzwdNA.js.map +1 -0
  87. package/build/server/chunks/{_server.ts-BWVlO8iV.js → _server.ts-05JJOdcX.js} +15 -12
  88. package/build/server/chunks/_server.ts-05JJOdcX.js.map +1 -0
  89. package/build/server/chunks/{_server.ts-BevnuePu.js → _server.ts-BCljU9Sg.js} +7 -3
  90. package/build/server/chunks/_server.ts-BCljU9Sg.js.map +1 -0
  91. package/build/server/chunks/{_server.ts-D-vgx5UZ.js → _server.ts-BTmknWpO.js} +2 -2
  92. package/build/server/chunks/{_server.ts-D-vgx5UZ.js.map → _server.ts-BTmknWpO.js.map} +1 -1
  93. package/build/server/chunks/{_server.ts-tChyh9FX.js → _server.ts-BXhmLZwN.js} +4 -2
  94. package/build/server/chunks/{_server.ts-tChyh9FX.js.map → _server.ts-BXhmLZwN.js.map} +1 -1
  95. package/build/server/chunks/{_server.ts-CvJKTS3Z.js → _server.ts-BbRSpB74.js} +4 -2
  96. package/build/server/chunks/{_server.ts-CvJKTS3Z.js.map → _server.ts-BbRSpB74.js.map} +1 -1
  97. package/build/server/chunks/{_server.ts-CC2K8-L2.js → _server.ts-Blx6TuRU.js} +4 -2
  98. package/build/server/chunks/_server.ts-Blx6TuRU.js.map +1 -0
  99. package/build/server/chunks/_server.ts-C6NRpe7e.js +33 -0
  100. package/build/server/chunks/_server.ts-C6NRpe7e.js.map +1 -0
  101. package/build/server/chunks/_server.ts-CGqCOCdK.js +53 -0
  102. package/build/server/chunks/_server.ts-CGqCOCdK.js.map +1 -0
  103. package/build/server/chunks/{_server.ts-X1R7L_QI.js → _server.ts-CYWXjihn.js} +4 -2
  104. package/build/server/chunks/{_server.ts-X1R7L_QI.js.map → _server.ts-CYWXjihn.js.map} +1 -1
  105. package/build/server/chunks/{_server.ts-CD7JP3fz.js → _server.ts-D0___krA.js} +4 -2
  106. package/build/server/chunks/_server.ts-D0___krA.js.map +1 -0
  107. package/build/server/chunks/{_server.ts-VzDcFFgy.js → _server.ts-DPHRUFYS.js} +4 -2
  108. package/build/server/chunks/_server.ts-DPHRUFYS.js.map +1 -0
  109. package/build/server/chunks/{_server.ts-D0zRDSx0.js → _server.ts-D_WRex0k.js} +4 -2
  110. package/build/server/chunks/_server.ts-D_WRex0k.js.map +1 -0
  111. package/build/server/chunks/{_server.ts-CA5KUENM.js → _server.ts-Da1kSClZ.js} +4 -2
  112. package/build/server/chunks/_server.ts-Da1kSClZ.js.map +1 -0
  113. package/build/server/chunks/{_server.ts-Dp-hXW_I.js → _server.ts-l3cd4Cto.js} +4 -2
  114. package/build/server/chunks/_server.ts-l3cd4Cto.js.map +1 -0
  115. package/build/server/chunks/{library-apns-Dl3iRE2h.js → library-apns-D8RPINlv.js} +62 -7
  116. package/build/server/chunks/library-apns-D8RPINlv.js.map +1 -0
  117. package/build/server/chunks/{pending-requests-C9p57WoU.js → pending-requests-8rWjrF6d.js} +3 -2
  118. package/build/server/chunks/pending-requests-8rWjrF6d.js.map +1 -0
  119. package/build/server/chunks/presence-store-Bx_g0-Gd.js +23 -0
  120. package/build/server/chunks/presence-store-Bx_g0-Gd.js.map +1 -0
  121. package/build/server/chunks/{pty-manager-ZqXqa-6A.js → pty-manager-DDjG7DlH.js} +297 -31
  122. package/build/server/chunks/pty-manager-DDjG7DlH.js.map +1 -0
  123. package/build/server/chunks/shooter-home-4f_HkdGI.js +10 -0
  124. package/build/server/chunks/shooter-home-4f_HkdGI.js.map +1 -0
  125. package/build/server/index.js +1 -1
  126. package/build/server/index.js.map +1 -1
  127. package/build/server/manifest.js +38 -24
  128. package/build/server/manifest.js.map +1 -1
  129. package/package.json +4 -2
  130. package/server.ts +2 -2
  131. package/src/lib/modules/client/common/index.ts +1 -0
  132. package/src/lib/modules/client/common/presence.ts +47 -0
  133. package/src/lib/modules/client/dashboard/AutopilotPanel.svelte +188 -4
  134. package/src/lib/modules/client/dashboard/autopilot-driver.svelte.ts +681 -0
  135. package/src/lib/modules/client/dashboard/decide-injection.ts +127 -0
  136. package/src/lib/modules/client/dashboard/store.svelte.ts +65 -24
  137. package/src/lib/modules/client/neurolink/fetch-proxy.ts +38 -1
  138. package/src/lib/modules/client/terminal/xterm-wrapper.ts +52 -12
  139. package/src/lib/modules/server/apn/apns-payload.ts +50 -0
  140. package/src/lib/modules/server/apn/library-apns.ts +50 -8
  141. package/src/lib/modules/server/apn/pending-requests.ts +3 -1
  142. package/src/lib/modules/server/sessions/autopilot-context.ts +57 -0
  143. package/src/lib/modules/server/sessions/autopilot-engine.ts +148 -43
  144. package/src/lib/modules/server/sessions/litellm-client.ts +90 -34
  145. package/src/lib/modules/server/sessions/next-step-consensus.ts +27 -2
  146. package/src/lib/modules/server/sessions/summary-store.ts +3 -1
  147. package/src/lib/modules/server/terminal/agent-launch.ts +26 -0
  148. package/src/lib/modules/server/terminal/pty-holder.cjs +6 -0
  149. package/src/lib/modules/server/terminal/pty-manager.ts +292 -38
  150. package/src/lib/modules/server/terminal/session-watcher.ts +12 -2
  151. package/src/lib/modules/server/terminal/terminal-emulator.ts +102 -0
  152. package/src/lib/modules/server/terminal/terminal-store.ts +3 -1
  153. package/src/lib/modules/server/utils/shooter-home.ts +16 -0
  154. package/src/lib/modules/server/ws/presence-store.ts +50 -0
  155. package/src/lib/modules/server/ws/server.ts +18 -2
  156. package/src/lib/modules/server/ws/terminal-handler.ts +11 -6
  157. package/src/lib/types/autopilot.ts +65 -0
  158. package/src/lib/types/generated/WsProtocol.ts +10 -1
  159. package/src/lib/types/server.ts +27 -1
  160. package/src/lib/types/terminal-client.ts +3 -0
  161. package/src/lib/types/ws.ts +3 -2
  162. package/src/routes/api/autopilot/goal/+server.ts +72 -0
  163. package/src/routes/api/neurolink-proxy/+server.ts +8 -2
  164. package/src/routes/api/notify/+server.ts +22 -15
  165. package/src/routes/api/presence/+server.ts +39 -0
  166. package/src/routes/api/ws-status/+server.ts +8 -5
  167. package/build/client/_app/immutable/assets/2.BHi6pjT2.css +0 -1
  168. package/build/client/_app/immutable/assets/2.BHi6pjT2.css.br +0 -0
  169. package/build/client/_app/immutable/assets/2.BHi6pjT2.css.gz +0 -0
  170. package/build/client/_app/immutable/chunks/BmfLecb1.js.br +0 -0
  171. package/build/client/_app/immutable/chunks/BmfLecb1.js.gz +0 -0
  172. package/build/client/_app/immutable/chunks/CRkG7oE4.js +0 -1
  173. package/build/client/_app/immutable/chunks/CRkG7oE4.js.br +0 -0
  174. package/build/client/_app/immutable/chunks/CRkG7oE4.js.gz +0 -0
  175. package/build/client/_app/immutable/chunks/DOEXXmsh.js.br +0 -0
  176. package/build/client/_app/immutable/chunks/DOEXXmsh.js.gz +0 -0
  177. package/build/client/_app/immutable/chunks/EqMAkEha.js.br +0 -0
  178. package/build/client/_app/immutable/chunks/EqMAkEha.js.gz +0 -0
  179. package/build/client/_app/immutable/chunks/J5-Cr5oR.js +0 -6
  180. package/build/client/_app/immutable/chunks/J5-Cr5oR.js.br +0 -0
  181. package/build/client/_app/immutable/chunks/J5-Cr5oR.js.gz +0 -0
  182. package/build/client/_app/immutable/entry/app.CeSxgGat.js.br +0 -0
  183. package/build/client/_app/immutable/entry/app.CeSxgGat.js.gz +0 -0
  184. package/build/client/_app/immutable/entry/start.DrnJFwxA.js +0 -1
  185. package/build/client/_app/immutable/entry/start.DrnJFwxA.js.br +0 -2
  186. package/build/client/_app/immutable/entry/start.DrnJFwxA.js.gz +0 -0
  187. package/build/client/_app/immutable/nodes/0.oaPwxh1O.js.br +0 -0
  188. package/build/client/_app/immutable/nodes/0.oaPwxh1O.js.gz +0 -0
  189. package/build/client/_app/immutable/nodes/1.DMPyoM-M.js.br +0 -0
  190. package/build/client/_app/immutable/nodes/1.DMPyoM-M.js.gz +0 -0
  191. package/build/client/_app/immutable/nodes/10.Cbm7nQKK.js.br +0 -0
  192. package/build/client/_app/immutable/nodes/10.Cbm7nQKK.js.gz +0 -0
  193. package/build/client/_app/immutable/nodes/11.CKmZjP_a.js.br +0 -0
  194. package/build/client/_app/immutable/nodes/2.zlrdNFtH.js +0 -13
  195. package/build/client/_app/immutable/nodes/2.zlrdNFtH.js.br +0 -0
  196. package/build/client/_app/immutable/nodes/2.zlrdNFtH.js.gz +0 -0
  197. package/build/client/_app/immutable/nodes/3.BgLpGnzb.js.br +0 -0
  198. package/build/client/_app/immutable/nodes/3.BgLpGnzb.js.gz +0 -0
  199. package/build/client/_app/immutable/nodes/5.Avc1-gVb.js.br +0 -0
  200. package/build/client/_app/immutable/nodes/5.Avc1-gVb.js.gz +0 -0
  201. package/build/client/_app/immutable/nodes/6.Dw2wEssJ.js.br +0 -0
  202. package/build/client/_app/immutable/nodes/6.Dw2wEssJ.js.gz +0 -0
  203. package/build/client/_app/immutable/nodes/7.DwKZjoBg.js.br +0 -0
  204. package/build/client/_app/immutable/nodes/7.DwKZjoBg.js.gz +0 -0
  205. package/build/client/_app/immutable/nodes/8.ZUAI6g5E.js.br +0 -0
  206. package/build/client/_app/immutable/nodes/8.ZUAI6g5E.js.gz +0 -0
  207. package/build/client/_app/immutable/nodes/9.I_KGXPwB.js.br +0 -0
  208. package/build/client/_app/immutable/nodes/9.I_KGXPwB.js.gz +0 -0
  209. package/build/server/chunks/_page.svelte-tBuIq8Pg.js +0 -159
  210. package/build/server/chunks/_page.svelte-tBuIq8Pg.js.map +0 -1
  211. package/build/server/chunks/_server.ts-BWVlO8iV.js.map +0 -1
  212. package/build/server/chunks/_server.ts-BevnuePu.js.map +0 -1
  213. package/build/server/chunks/_server.ts-CA5KUENM.js.map +0 -1
  214. package/build/server/chunks/_server.ts-CC2K8-L2.js.map +0 -1
  215. package/build/server/chunks/_server.ts-CD7JP3fz.js.map +0 -1
  216. package/build/server/chunks/_server.ts-D0zRDSx0.js.map +0 -1
  217. package/build/server/chunks/_server.ts-Dp-hXW_I.js.map +0 -1
  218. package/build/server/chunks/_server.ts-VzDcFFgy.js.map +0 -1
  219. package/build/server/chunks/library-apns-Dl3iRE2h.js.map +0 -1
  220. package/build/server/chunks/pending-requests-C9p57WoU.js.map +0 -1
  221. package/build/server/chunks/pty-manager-ZqXqa-6A.js.map +0 -1
@@ -10,6 +10,8 @@ import { b as broadcastEvent } from './guest-registry-Dxvd7p-g.js';
10
10
  import './super-session-handler-DPyxFgmz.js';
11
11
  import * as net from 'net';
12
12
  import Database from 'better-sqlite3';
13
+ import { createRequire } from 'node:module';
14
+ import { s as shooterDataDir } from './shooter-home-4f_HkdGI.js';
13
15
 
14
16
  function readOnlySourceForCommand(command) {
15
17
  switch (command) {
@@ -69,6 +71,21 @@ function discoverReadOnlyProviderSessionFile(source, cwd, launchTimeMs, nowMs) {
69
71
  }
70
72
  return resolveReadOnlyProviderFile(source, match.id);
71
73
  }
74
+ const CLAUDE_COMMANDS = /* @__PURE__ */ new Set(["claude"]);
75
+ function withAgentPermissionMode(command, args, mode) {
76
+ const trimmed = (mode ?? "").trim();
77
+ if (trimmed.length === 0) {
78
+ return args;
79
+ }
80
+ const base = command.split("/").pop() ?? command;
81
+ if (!CLAUDE_COMMANDS.has(base)) {
82
+ return args;
83
+ }
84
+ if (args.includes("--permission-mode")) {
85
+ return args;
86
+ }
87
+ return [...args, "--permission-mode", trimmed];
88
+ }
72
89
  class HolderClient {
73
90
  connected = false;
74
91
  pid = 0;
@@ -705,7 +722,63 @@ function toMillis(timestamp) {
705
722
  const OW_GLOBAL_KEY = "__shooter_opencode_watcher";
706
723
  const openCodeWatcher = globalThis[OW_GLOBAL_KEY] || new OpenCodeWatcher();
707
724
  globalThis[OW_GLOBAL_KEY] = openCodeWatcher;
708
- const DB_DIR = path.join(process.env.HOME || "", ".shooter");
725
+ const require$1 = createRequire(import.meta.url);
726
+ const { Terminal } = require$1("@xterm/headless");
727
+ const { SerializeAddon } = require$1("@xterm/addon-serialize");
728
+ const SNAPSHOT_SCROLLBACK_LINES = 1e3;
729
+ const HIDE_CURSOR = "\x1B[?25l";
730
+ const SHOW_CURSOR = "\x1B[?25h";
731
+ class TerminalEmulator {
732
+ cursorHidden = false;
733
+ serializer;
734
+ term;
735
+ constructor(cols, rows) {
736
+ this.term = new Terminal({
737
+ allowProposedApi: true,
738
+ cols: cols > 0 ? cols : 80,
739
+ rows: rows > 0 ? rows : 24,
740
+ scrollback: SNAPSHOT_SCROLLBACK_LINES
741
+ });
742
+ this.serializer = new SerializeAddon();
743
+ this.term.loadAddon(this.serializer);
744
+ }
745
+ dispose() {
746
+ try {
747
+ this.serializer.dispose();
748
+ this.term.dispose();
749
+ } catch {
750
+ }
751
+ }
752
+ resize(cols, rows) {
753
+ if (cols > 0 && rows > 0) {
754
+ this.term.resize(cols, rows);
755
+ }
756
+ }
757
+ /**
758
+ * Capture the current screen as a VT-escape string. Serialization runs inside
759
+ * a write() callback so all previously-written bytes are parsed first.
760
+ */
761
+ snapshot() {
762
+ return new Promise((resolve) => {
763
+ this.term.write("", () => {
764
+ let data = this.serializer.serialize({ scrollback: SNAPSHOT_SCROLLBACK_LINES });
765
+ if (this.cursorHidden) {
766
+ data += HIDE_CURSOR;
767
+ }
768
+ resolve({ cols: this.term.cols, data, rows: this.term.rows });
769
+ });
770
+ });
771
+ }
772
+ write(data) {
773
+ const hideIdx = data.lastIndexOf(HIDE_CURSOR);
774
+ const showIdx = data.lastIndexOf(SHOW_CURSOR);
775
+ if (hideIdx !== -1 || showIdx !== -1) {
776
+ this.cursorHidden = hideIdx > showIdx;
777
+ }
778
+ this.term.write(data);
779
+ }
780
+ }
781
+ const DB_DIR = shooterDataDir();
709
782
  const DB_PATH = path.join(DB_DIR, "shooter.db");
710
783
  const COLUMNS = [
711
784
  "id",
@@ -849,6 +922,11 @@ globalThis[TS_GLOBAL_KEY] = terminalStore;
849
922
  const MAX_SCROLLBACK_BYTES = 512 * 1024;
850
923
  const MAX_OUTPUT_BUFFER_BYTES = 1024 * 1024;
851
924
  const SCROLLBACK_CHUNK_SIZE = 50 * 1024;
925
+ const SEQ_RING_MAX_ENTRIES = 2e3;
926
+ const SNAPSHOT_ENABLED = process.env.SHOOTER_SNAPSHOT_FALLBACK !== "raw";
927
+ const RESNAPSHOT_LOW_WATER_BYTES = MAX_OUTPUT_BUFFER_BYTES / 4;
928
+ const RESNAPSHOT_POLL_MS = 100;
929
+ const RESNAPSHOT_MAX_WAIT_MS = 1e4;
852
930
  const CLEANUP_INTERVAL_MS = 5 * 60 * 1e3;
853
931
  const EXITED_TTL_MS = 60 * 60 * 1e3;
854
932
  const MAX_EXITED_TERMINALS = 10;
@@ -859,6 +937,10 @@ const __filename$1 = fileURLToPath(import.meta.url);
859
937
  const __dirname$1 = path__default.dirname(__filename$1);
860
938
  class PtyManager {
861
939
  cleanupTimer = null;
940
+ // Clients currently converging via a resnapshot (Phase 2). While pending, a
941
+ // client receives no normal output frames — the forthcoming snapshot brings
942
+ // it to the current screen. WeakSet so disconnected sockets drop out on GC.
943
+ resnapshotPending = /* @__PURE__ */ new WeakSet();
862
944
  terminals = /* @__PURE__ */ new Map();
863
945
  constructor() {
864
946
  this.cleanupTimer = setInterval(() => {
@@ -869,19 +951,42 @@ class PtyManager {
869
951
  // create — now async: forks a holder process, connects via HolderClient,
870
952
  // persists to SQLite
871
953
  // -----------------------------------------------------------------------
872
- attach(id, ws) {
954
+ attach(id, ws, opts) {
873
955
  const terminal = this.terminals.get(id);
874
956
  if (!terminal) {
875
957
  return false;
876
958
  }
877
- terminal.clients.add(ws);
878
959
  terminal.outputBuffers.set(ws, { data: [], size: 0 });
960
+ const wantsSnapshot = opts?.snapshot === true;
961
+ const lastSeq = opts?.lastSeq ?? 0;
962
+ if (wantsSnapshot && lastSeq > 0) {
963
+ const gap = this.getSeqRingFrom(id, lastSeq);
964
+ if (gap !== null) {
965
+ for (const entry of gap) {
966
+ this.safeSend(ws, JSON.stringify({ data: entry.data, seq: entry.seq, type: "output" }));
967
+ }
968
+ terminal.clients.add(ws);
969
+ return true;
970
+ }
971
+ }
972
+ if (wantsSnapshot && terminal.emulator) {
973
+ void this.snapshotAndSend(terminal, ws).then((ok) => {
974
+ if (ws.readyState !== 1) {
975
+ return;
976
+ }
977
+ if (!ok) {
978
+ terminal.clients.add(ws);
979
+ void this.sendScrollback(terminal, ws);
980
+ return;
981
+ }
982
+ terminal.clients.add(ws);
983
+ });
984
+ return true;
985
+ }
986
+ terminal.clients.add(ws);
879
987
  void this.sendScrollback(terminal, ws);
880
988
  return true;
881
989
  }
882
- // -----------------------------------------------------------------------
883
- // reconnectAll — recover persisted terminals on server startup
884
- // -----------------------------------------------------------------------
885
990
  cleanup() {
886
991
  const now = Date.now();
887
992
  const exited = [];
@@ -912,13 +1017,18 @@ class PtyManager {
912
1017
  }
913
1018
  }
914
1019
  // -----------------------------------------------------------------------
915
- // disconnectAllgraceful shutdown: disconnect clients, keep holders alive
1020
+ // reconnectAllrecover persisted terminals on server startup
916
1021
  // -----------------------------------------------------------------------
917
1022
  async create(command, args, cwd, cols, rows) {
918
1023
  const id = randomBytes(4).toString("hex");
919
1024
  const socketPath = `/tmp/shooter-term-${id}.sock`;
920
1025
  const holderScript = resolveHolderPath();
921
- const holderArgs = [id, socketPath, cwd, String(cols), String(rows), command, ...args];
1026
+ const launchArgs = withAgentPermissionMode(
1027
+ command,
1028
+ args,
1029
+ process.env.SHOOTER_AGENT_PERMISSION_MODE
1030
+ );
1031
+ const holderArgs = [id, socketPath, cwd, String(cols), String(rows), command, ...launchArgs];
922
1032
  const holder = fork(holderScript, holderArgs, {
923
1033
  detached: true,
924
1034
  stdio: ["ignore", "ignore", "ignore", "ipc"]
@@ -953,13 +1063,14 @@ class PtyManager {
953
1063
  const connectResult = await client.connect(socketPath);
954
1064
  const now = /* @__PURE__ */ new Date();
955
1065
  const terminal = {
956
- args,
1066
+ args: launchArgs,
957
1067
  clients: /* @__PURE__ */ new Set(),
958
1068
  cols,
959
1069
  command,
960
1070
  createdAt: now,
961
1071
  currentCwd: null,
962
1072
  cwd,
1073
+ emulator: SNAPSHOT_ENABLED ? new TerminalEmulator(cols, rows) : null,
963
1074
  exitCode: connectResult.exitCode,
964
1075
  exitedAt: null,
965
1076
  holderPid,
@@ -973,6 +1084,8 @@ class PtyManager {
973
1084
  pty: client,
974
1085
  rows,
975
1086
  scrollback: connectResult.scrollback,
1087
+ seqCounter: 0,
1088
+ seqRing: [],
976
1089
  sessionFile: null,
977
1090
  socketPath,
978
1091
  status: connectResult.exited ? "exited" : "running",
@@ -980,7 +1093,7 @@ class PtyManager {
980
1093
  };
981
1094
  this.wireHolderCallbacks(client, terminal);
982
1095
  terminalStore.insert({
983
- args: JSON.stringify(args),
1096
+ args: JSON.stringify(launchArgs),
984
1097
  cols,
985
1098
  command,
986
1099
  createdAt: now.toISOString(),
@@ -1006,7 +1119,7 @@ class PtyManager {
1006
1119
  return terminal;
1007
1120
  }
1008
1121
  // -----------------------------------------------------------------------
1009
- // get
1122
+ // disconnectAll — graceful shutdown: disconnect clients, keep holders alive
1010
1123
  // -----------------------------------------------------------------------
1011
1124
  destroy() {
1012
1125
  if (this.cleanupTimer) {
@@ -1041,8 +1154,7 @@ class PtyManager {
1041
1154
  }
1042
1155
  }
1043
1156
  // -----------------------------------------------------------------------
1044
- // list — running first, then recently exited, each group sorted by
1045
- // createdAt descending
1157
+ // get
1046
1158
  // -----------------------------------------------------------------------
1047
1159
  detach(id, ws) {
1048
1160
  const terminal = this.terminals.get(id);
@@ -1054,7 +1166,8 @@ class PtyManager {
1054
1166
  return true;
1055
1167
  }
1056
1168
  // -----------------------------------------------------------------------
1057
- // killroute through holder: SIGTERM, then SIGKILL after 5 s
1169
+ // listrunning first, then recently exited, each group sorted by
1170
+ // createdAt descending
1058
1171
  // -----------------------------------------------------------------------
1059
1172
  disconnectAll() {
1060
1173
  if (this.cleanupTimer) {
@@ -1079,13 +1192,13 @@ class PtyManager {
1079
1192
  this.terminals.clear();
1080
1193
  }
1081
1194
  // -----------------------------------------------------------------------
1082
- // removeremove an exited terminal from the map
1195
+ // killroute through holder: SIGTERM, then SIGKILL after 5 s
1083
1196
  // -----------------------------------------------------------------------
1084
1197
  get(id) {
1085
1198
  return this.terminals.get(id) ?? null;
1086
1199
  }
1087
1200
  // -----------------------------------------------------------------------
1088
- // resize
1201
+ // remove — remove an exited terminal from the map
1089
1202
  // -----------------------------------------------------------------------
1090
1203
  getScrollback(id) {
1091
1204
  const terminal = this.terminals.get(id);
@@ -1095,8 +1208,40 @@ class PtyManager {
1095
1208
  return terminal.scrollback;
1096
1209
  }
1097
1210
  // -----------------------------------------------------------------------
1098
- // attach — register a WebSocket client and replay scrollback
1211
+ // resize
1099
1212
  // -----------------------------------------------------------------------
1213
+ /** Current highest assigned seq for a terminal, or null if unknown. */
1214
+ getSeqCounter(id) {
1215
+ return this.terminals.get(id)?.seqCounter ?? null;
1216
+ }
1217
+ /**
1218
+ * Return the ring entries with seq > afterSeq, in order. Returns an empty
1219
+ * array when the caller is already current, or null when the gap is
1220
+ * unresolvable from the ring (caller must take a full snapshot). Unresolvable
1221
+ * means any of:
1222
+ * - afterSeq > seqCounter: the caller claims a seq we never produced — this
1223
+ * happens when the seq counter reset across a server restart (the client
1224
+ * is from a previous terminal lifetime), so its content is unrelated.
1225
+ * - ring empty but afterSeq > 0: nothing buffered to bridge the gap.
1226
+ * - afterSeq predates the oldest retained entry: the gap aged out.
1227
+ */
1228
+ getSeqRingFrom(id, afterSeq) {
1229
+ const terminal = this.terminals.get(id);
1230
+ if (!terminal) {
1231
+ return null;
1232
+ }
1233
+ if (afterSeq > terminal.seqCounter) {
1234
+ return null;
1235
+ }
1236
+ const ring = terminal.seqRing;
1237
+ if (ring.length === 0) {
1238
+ return afterSeq <= 0 ? [] : null;
1239
+ }
1240
+ if (afterSeq < ring[0].seq - 1) {
1241
+ return null;
1242
+ }
1243
+ return ring.filter((e) => e.seq > afterSeq);
1244
+ }
1100
1245
  kill(id) {
1101
1246
  const terminal = this.terminals.get(id);
1102
1247
  if (!terminal) {
@@ -1129,7 +1274,7 @@ class PtyManager {
1129
1274
  return true;
1130
1275
  }
1131
1276
  // -----------------------------------------------------------------------
1132
- // detachremove a WebSocket client
1277
+ // attachregister a WebSocket client and replay scrollback
1133
1278
  // -----------------------------------------------------------------------
1134
1279
  list() {
1135
1280
  const all = Array.from(this.terminals.values());
@@ -1142,7 +1287,7 @@ class PtyManager {
1142
1287
  return [...running, ...exited];
1143
1288
  }
1144
1289
  // -----------------------------------------------------------------------
1145
- // getScrollbackreturn raw scrollback data for replay
1290
+ // detachremove a WebSocket client
1146
1291
  // -----------------------------------------------------------------------
1147
1292
  async reconnectAll() {
1148
1293
  const running = terminalStore.listRunning();
@@ -1162,8 +1307,7 @@ class PtyManager {
1162
1307
  }
1163
1308
  }
1164
1309
  // -----------------------------------------------------------------------
1165
- // cleanupevict exited terminals older than 1 hour, cap at 10 exited;
1166
- // also clean up old SQLite records
1310
+ // getScrollbackreturn raw scrollback data for replay
1167
1311
  // -----------------------------------------------------------------------
1168
1312
  remove(id) {
1169
1313
  const terminal = this.terminals.get(id);
@@ -1177,7 +1321,8 @@ class PtyManager {
1177
1321
  return true;
1178
1322
  }
1179
1323
  // -----------------------------------------------------------------------
1180
- // destroyemergency forced kill (kills holder processes too)
1324
+ // cleanupevict exited terminals older than 1 hour, cap at 10 exited;
1325
+ // also clean up old SQLite records
1181
1326
  // -----------------------------------------------------------------------
1182
1327
  resize(id, cols, rows) {
1183
1328
  const terminal = this.terminals.get(id);
@@ -1188,6 +1333,7 @@ class PtyManager {
1188
1333
  terminal.pty.resize(cols, rows);
1189
1334
  terminal.cols = cols;
1190
1335
  terminal.rows = rows;
1336
+ terminal.emulator?.resize(cols, rows);
1191
1337
  const msg = JSON.stringify({ cols, rows, type: "resize" });
1192
1338
  for (const ws of terminal.clients) {
1193
1339
  this.safeSend(ws, msg);
@@ -1198,6 +1344,39 @@ class PtyManager {
1198
1344
  }
1199
1345
  }
1200
1346
  // -----------------------------------------------------------------------
1347
+ // destroy — emergency forced kill (kills holder processes too)
1348
+ // -----------------------------------------------------------------------
1349
+ /**
1350
+ * Compute the current-screen snapshot from the emulator and send it as a
1351
+ * single {type:'snapshot'} frame stamped with the current seq. Returns false
1352
+ * if there is no emulator, the socket closed, or serialization failed.
1353
+ * Reused by Phase 2 to resnapshot a client after a backpressure gap.
1354
+ */
1355
+ async snapshotAndSend(terminal, ws) {
1356
+ if (!terminal.emulator) {
1357
+ return false;
1358
+ }
1359
+ try {
1360
+ const snap = await terminal.emulator.snapshot();
1361
+ if (ws.readyState !== 1) {
1362
+ return false;
1363
+ }
1364
+ this.safeSend(
1365
+ ws,
1366
+ JSON.stringify({
1367
+ cols: snap.cols,
1368
+ data: snap.data,
1369
+ rows: snap.rows,
1370
+ seq: terminal.seqCounter,
1371
+ type: "snapshot"
1372
+ })
1373
+ );
1374
+ return true;
1375
+ } catch {
1376
+ return false;
1377
+ }
1378
+ }
1379
+ // -----------------------------------------------------------------------
1201
1380
  // Private: reconnectOne — reconnect to a single persisted terminal
1202
1381
  // -----------------------------------------------------------------------
1203
1382
  appendScrollback(terminal, data) {
@@ -1212,21 +1391,103 @@ class PtyManager {
1212
1391
  }
1213
1392
  }
1214
1393
  }
1394
+ /**
1395
+ * Assign the next sequence number to an output chunk and append it to the
1396
+ * bounded replay ring. Returns the new seq. Phase 2 uses the ring to replay
1397
+ * the gap to a reconnecting client without a full snapshot.
1398
+ */
1399
+ appendSeqRing(terminal, data) {
1400
+ const seq = terminal.seqCounter + 1;
1401
+ terminal.seqRing.push({ data, seq });
1402
+ if (terminal.seqRing.length > SEQ_RING_MAX_ENTRIES) {
1403
+ terminal.seqRing.shift();
1404
+ }
1405
+ terminal.seqCounter = seq;
1406
+ return seq;
1407
+ }
1408
+ /**
1409
+ * Mark a client for convergence (Phase 2). Its queued output is discarded and
1410
+ * further live frames are withheld until its socket drains below the low-water
1411
+ * mark (or a hard timeout elapses), at which point a fresh snapshot resets it
1412
+ * to the current screen. This replaces silent byte-dropping so a slow or
1413
+ * throttled client can never diverge permanently (G1).
1414
+ */
1415
+ beginResnapshot(terminal, ws) {
1416
+ if (this.resnapshotPending.has(ws)) {
1417
+ return;
1418
+ }
1419
+ this.resnapshotPending.add(ws);
1420
+ const buffer = terminal.outputBuffers.get(ws);
1421
+ if (buffer) {
1422
+ buffer.data.length = 0;
1423
+ buffer.size = 0;
1424
+ }
1425
+ this.safeSend(ws, JSON.stringify({ bytes: 0, type: "output-dropped" }));
1426
+ const startedAt = Date.now();
1427
+ const poll = () => {
1428
+ if (!this.resnapshotPending.has(ws)) {
1429
+ return;
1430
+ }
1431
+ if (ws.readyState !== 1 || !terminal.emulator || !terminal.clients.has(ws)) {
1432
+ this.resnapshotPending.delete(ws);
1433
+ return;
1434
+ }
1435
+ const drained = ws.bufferedAmount <= RESNAPSHOT_LOW_WATER_BYTES;
1436
+ const timedOut = Date.now() - startedAt > RESNAPSHOT_MAX_WAIT_MS;
1437
+ if (drained || timedOut) {
1438
+ void this.snapshotAndSend(terminal, ws).finally(() => {
1439
+ this.resnapshotPending.delete(ws);
1440
+ });
1441
+ return;
1442
+ }
1443
+ setTimeout(poll, RESNAPSHOT_POLL_MS);
1444
+ };
1445
+ setTimeout(poll, RESNAPSHOT_POLL_MS);
1446
+ }
1215
1447
  // -----------------------------------------------------------------------
1216
1448
  // Private: handleReconnectFailure — handle failed reconnection
1217
1449
  // -----------------------------------------------------------------------
1218
1450
  broadcastOutput(terminal, data) {
1219
- const msg = JSON.stringify({ data, type: "output" });
1451
+ const seq = this.appendSeqRing(terminal, data);
1452
+ const msg = JSON.stringify({ data, seq, type: "output" });
1453
+ if (!terminal.emulator) {
1454
+ this.broadcastOutputLegacy(terminal, msg);
1455
+ return;
1456
+ }
1457
+ const msgSize = Buffer.byteLength(msg, "utf8");
1458
+ for (const ws of terminal.clients) {
1459
+ if (this.resnapshotPending.has(ws)) {
1460
+ continue;
1461
+ }
1462
+ const buffer = terminal.outputBuffers.get(ws);
1463
+ if (!buffer) {
1464
+ continue;
1465
+ }
1466
+ if (ws.bufferedAmount > MAX_OUTPUT_BUFFER_BYTES || buffer.size + msgSize > MAX_OUTPUT_BUFFER_BYTES) {
1467
+ this.beginResnapshot(terminal, ws);
1468
+ continue;
1469
+ }
1470
+ buffer.data.push(msg);
1471
+ buffer.size += msgSize;
1472
+ this.flushOutputBuffer(ws, buffer);
1473
+ }
1474
+ }
1475
+ /**
1476
+ * Legacy broadcast path used only when the emulator is disabled
1477
+ * (SHOOTER_SNAPSHOT_FALLBACK=raw). Drops the oldest buffered output to make
1478
+ * room and notifies the client; there is no snapshot to converge it to.
1479
+ */
1480
+ broadcastOutputLegacy(terminal, msg) {
1481
+ const msgSize = Buffer.byteLength(msg, "utf8");
1220
1482
  for (const ws of terminal.clients) {
1221
1483
  if (ws.bufferedAmount > MAX_OUTPUT_BUFFER_BYTES) {
1222
- this.safeSend(ws, JSON.stringify({ bytes: data.length, type: "output-dropped" }));
1484
+ this.safeSend(ws, JSON.stringify({ bytes: msgSize, type: "output-dropped" }));
1223
1485
  continue;
1224
1486
  }
1225
1487
  const buffer = terminal.outputBuffers.get(ws);
1226
1488
  if (!buffer) {
1227
1489
  continue;
1228
1490
  }
1229
- const msgSize = Buffer.byteLength(msg, "utf8");
1230
1491
  if (buffer.size + msgSize > MAX_OUTPUT_BUFFER_BYTES) {
1231
1492
  let droppedBytes = 0;
1232
1493
  while (buffer.size + msgSize > MAX_OUTPUT_BUFFER_BYTES && buffer.data.length > 0) {
@@ -1238,11 +1499,7 @@ class PtyManager {
1238
1499
  }
1239
1500
  }
1240
1501
  if (droppedBytes > 0) {
1241
- const dropMsg = JSON.stringify({
1242
- bytes: droppedBytes,
1243
- type: "output-dropped"
1244
- });
1245
- this.safeSend(ws, dropMsg);
1502
+ this.safeSend(ws, JSON.stringify({ bytes: droppedBytes, type: "output-dropped" }));
1246
1503
  }
1247
1504
  }
1248
1505
  buffer.data.push(msg);
@@ -1275,6 +1532,8 @@ class PtyManager {
1275
1532
  openCodeWatcher.stop(terminal.openCodeSessionId, terminal.openCodeNoopCb);
1276
1533
  terminal.openCodeNoopCb = null;
1277
1534
  }
1535
+ terminal.emulator?.dispose();
1536
+ terminal.emulator = null;
1278
1537
  terminal.pty.disconnect();
1279
1538
  for (const ws of terminal.clients) {
1280
1539
  try {
@@ -1366,6 +1625,7 @@ class PtyManager {
1366
1625
  createdAt: new Date(record.createdAt),
1367
1626
  currentCwd: null,
1368
1627
  cwd: record.cwd,
1628
+ emulator: SNAPSHOT_ENABLED ? new TerminalEmulator(record.cols, record.rows) : null,
1369
1629
  exitCode: connectResult.exitCode,
1370
1630
  exitedAt: record.exitedAt ? new Date(record.exitedAt) : null,
1371
1631
  holderPid: record.holderPid ?? 0,
@@ -1379,11 +1639,16 @@ class PtyManager {
1379
1639
  pty: client,
1380
1640
  rows: record.rows,
1381
1641
  scrollback: connectResult.scrollback,
1642
+ seqCounter: 0,
1643
+ seqRing: [],
1382
1644
  sessionFile: record.sessionFile ?? null,
1383
1645
  socketPath: record.socketPath,
1384
1646
  status: connectResult.exited ? "exited" : "running",
1385
1647
  watcherOffset: 0
1386
1648
  };
1649
+ if (terminal.emulator && connectResult.scrollback.length > 0) {
1650
+ terminal.emulator.write(connectResult.scrollback);
1651
+ }
1387
1652
  if (connectResult.exited) {
1388
1653
  terminal.exitedAt = terminal.exitedAt ?? /* @__PURE__ */ new Date();
1389
1654
  terminalStore.markExited(record.id, connectResult.exitCode);
@@ -1646,6 +1911,7 @@ class PtyManager {
1646
1911
  }
1647
1912
  });
1648
1913
  client.onOutput((data) => {
1914
+ terminal.emulator?.write(data);
1649
1915
  this.appendScrollback(terminal, data);
1650
1916
  this.broadcastOutput(terminal, data);
1651
1917
  });
@@ -1690,4 +1956,4 @@ const ptyManager = globalThis[PTY_GLOBAL_KEY] || new PtyManager();
1690
1956
  globalThis[PTY_GLOBAL_KEY] = ptyManager;
1691
1957
 
1692
1958
  export { ptyManager as p };
1693
- //# sourceMappingURL=pty-manager-ZqXqa-6A.js.map
1959
+ //# sourceMappingURL=pty-manager-DDjG7DlH.js.map