@juspay/shooter 1.20.0 → 1.22.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.
- package/.claude/hooks/notifier.cjs +94 -1
- package/build/client/_app/immutable/assets/2.JWRrnR-w.css +1 -0
- package/build/client/_app/immutable/assets/2.JWRrnR-w.css.br +0 -0
- package/build/client/_app/immutable/assets/2.JWRrnR-w.css.gz +0 -0
- package/build/client/_app/immutable/chunks/{ZS5XYDx_.js → B1bOvemT.js} +1 -1
- package/build/client/_app/immutable/chunks/B1bOvemT.js.br +0 -0
- package/build/client/_app/immutable/chunks/{ZS5XYDx_.js.gz → B1bOvemT.js.gz} +0 -0
- package/build/client/_app/immutable/chunks/BfbPKMXz.js +3 -0
- package/build/client/_app/immutable/chunks/BfbPKMXz.js.br +0 -0
- package/build/client/_app/immutable/chunks/BfbPKMXz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C4Hns_Wl.js +1 -0
- package/build/client/_app/immutable/chunks/C4Hns_Wl.js.br +0 -0
- package/build/client/_app/immutable/chunks/C4Hns_Wl.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C87ZRWX0.js +1 -0
- package/build/client/_app/immutable/chunks/C87ZRWX0.js.br +0 -0
- package/build/client/_app/immutable/chunks/C87ZRWX0.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BvmdJful.js → CJulw9ux.js} +1 -1
- package/build/client/_app/immutable/chunks/CJulw9ux.js.br +0 -0
- package/build/client/_app/immutable/chunks/CJulw9ux.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DT4H19pV.js → CZg4kn4E.js} +1 -1
- package/build/client/_app/immutable/chunks/CZg4kn4E.js.br +0 -0
- package/build/client/_app/immutable/chunks/CZg4kn4E.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DIZ3Qst5.js → DhK7PwI_.js} +1 -1
- package/build/client/_app/immutable/chunks/DhK7PwI_.js.br +0 -0
- package/build/client/_app/immutable/chunks/{DIZ3Qst5.js.gz → DhK7PwI_.js.gz} +0 -0
- package/build/client/_app/immutable/chunks/{ClIPTXf3.js → DomZZqvG.js} +1 -1
- package/build/client/_app/immutable/chunks/DomZZqvG.js.br +0 -0
- package/build/client/_app/immutable/chunks/DomZZqvG.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BB2l8o4X.js → i5iZvmIH.js} +1 -1
- package/build/client/_app/immutable/chunks/i5iZvmIH.js.br +0 -0
- package/build/client/_app/immutable/chunks/i5iZvmIH.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.Bd-DfeJi.js → app.CTqz33nP.js} +2 -2
- package/build/client/_app/immutable/entry/app.CTqz33nP.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CTqz33nP.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.Dj-Kvgwo.js +1 -0
- package/build/client/_app/immutable/entry/start.Dj-Kvgwo.js.br +2 -0
- package/build/client/_app/immutable/entry/start.Dj-Kvgwo.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.Qn7Ktiht.js +10 -0
- package/build/client/_app/immutable/nodes/0.Qn7Ktiht.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Qn7Ktiht.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.DT4dq6Ay.js → 1.BxWOfNlo.js} +1 -1
- package/build/client/_app/immutable/nodes/1.BxWOfNlo.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.BxWOfNlo.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{10.CF7RGXpe.js → 10.BGPYD1s1.js} +1 -1
- package/build/client/_app/immutable/nodes/10.BGPYD1s1.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.BGPYD1s1.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{11.BV_G7yLI.js → 11.BxY1PUjC.js} +1 -1
- package/build/client/_app/immutable/nodes/11.BxY1PUjC.js.br +0 -0
- package/build/client/_app/immutable/nodes/11.BxY1PUjC.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.Bc2qALkX.js +23 -0
- package/build/client/_app/immutable/nodes/2.Bc2qALkX.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.Bc2qALkX.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{3.0MMe3oxR.js → 3.N2-A8noI.js} +1 -1
- package/build/client/_app/immutable/nodes/3.N2-A8noI.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.N2-A8noI.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{4.CBX9A3ka.js → 4.BFYS2g9C.js} +3 -3
- package/build/client/_app/immutable/nodes/4.BFYS2g9C.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.BFYS2g9C.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{5.DIVKuZc9.js → 5.DziEu9rx.js} +1 -1
- package/build/client/_app/immutable/nodes/5.DziEu9rx.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.DziEu9rx.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{6.ComiWlV6.js → 6.BWF9Qx6F.js} +1 -1
- package/build/client/_app/immutable/nodes/6.BWF9Qx6F.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.BWF9Qx6F.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{7.vkPx1kVP.js → 7.DHuDIdpz.js} +1 -1
- package/build/client/_app/immutable/nodes/7.DHuDIdpz.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.DHuDIdpz.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{8.Bmr3sWbS.js → 8.D0Ijt9Vv.js} +1 -1
- package/build/client/_app/immutable/nodes/8.D0Ijt9Vv.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.D0Ijt9Vv.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{9.CAJucyeI.js → 9.2Piwo35J.js} +1 -1
- package/build/client/_app/immutable/nodes/9.2Piwo35J.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.2Piwo35J.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/pty-holder.cjs +6 -0
- package/build/server/chunks/{0-DDGB6CRT.js → 0-CVGsyVKN.js} +4 -2
- package/build/server/chunks/0-CVGsyVKN.js.map +1 -0
- package/build/server/chunks/{1-DEjonQXD.js → 1-BAlAsKdp.js} +2 -2
- package/build/server/chunks/{1-DEjonQXD.js.map → 1-BAlAsKdp.js.map} +1 -1
- package/build/server/chunks/{10-BK1kiiiw.js → 10-BUCX7Aqz.js} +2 -2
- package/build/server/chunks/{10-BK1kiiiw.js.map → 10-BUCX7Aqz.js.map} +1 -1
- package/build/server/chunks/{11-CJPjkEF3.js → 11-DHPvc2yA.js} +2 -2
- package/build/server/chunks/{11-CJPjkEF3.js.map → 11-DHPvc2yA.js.map} +1 -1
- package/build/server/chunks/{2-RLnhlWh5.js → 2-DLOMdCHW.js} +4 -4
- package/build/server/chunks/{2-RLnhlWh5.js.map → 2-DLOMdCHW.js.map} +1 -1
- package/build/server/chunks/{3-Dd4pJBqZ.js → 3-DCf69LYo.js} +2 -2
- package/build/server/chunks/{3-Dd4pJBqZ.js.map → 3-DCf69LYo.js.map} +1 -1
- package/build/server/chunks/{4-Bb5VFhsO.js → 4-D92KnTmb.js} +3 -3
- package/build/server/chunks/{4-Bb5VFhsO.js.map → 4-D92KnTmb.js.map} +1 -1
- package/build/server/chunks/{5-oNoWuIsn.js → 5-D-Uv1voC.js} +2 -2
- package/build/server/chunks/{5-oNoWuIsn.js.map → 5-D-Uv1voC.js.map} +1 -1
- package/build/server/chunks/{6-DdRMnKNa.js → 6-DUrC2Naz.js} +2 -2
- package/build/server/chunks/{6-DdRMnKNa.js.map → 6-DUrC2Naz.js.map} +1 -1
- package/build/server/chunks/{7-vLOMMetm.js → 7-TXwjMHt2.js} +2 -2
- package/build/server/chunks/{7-vLOMMetm.js.map → 7-TXwjMHt2.js.map} +1 -1
- package/build/server/chunks/{8-rJyiQLFs.js → 8-D2X_jBsT.js} +2 -2
- package/build/server/chunks/{8-rJyiQLFs.js.map → 8-D2X_jBsT.js.map} +1 -1
- package/build/server/chunks/{9-CVSNNYED.js → 9-DK0hH5Xa.js} +2 -2
- package/build/server/chunks/{9-CVSNNYED.js.map → 9-DK0hH5Xa.js.map} +1 -1
- package/build/server/chunks/Banner-BgaAs1rs.js.map +1 -1
- package/build/server/chunks/Button-D0hZ7JYt.js.map +1 -1
- package/build/server/chunks/Icon-D0GBnDcs.js.map +1 -1
- package/build/server/chunks/Input-OmIiydSx.js.map +1 -1
- package/build/server/chunks/Pill-4xJ-VhAA.js.map +1 -1
- package/build/server/chunks/Shimmer-Dw2uvTC1.js.map +1 -1
- package/build/server/chunks/_error.svelte-CZnkxeLr.js.map +1 -1
- package/build/server/chunks/_layout.svelte-DfgNGGiM.js.map +1 -1
- package/build/server/chunks/_page.svelte-8OFzwdNA.js +758 -0
- package/build/server/chunks/_page.svelte-8OFzwdNA.js.map +1 -0
- package/build/server/chunks/_page.svelte-BTlfUsBp.js.map +1 -1
- package/build/server/chunks/_page.svelte-C7B0qdrC.js.map +1 -1
- package/build/server/chunks/{_page.svelte-BLo2v_8E.js → _page.svelte-Gv9p8nlS.js} +3 -4
- package/build/server/chunks/_page.svelte-Gv9p8nlS.js.map +1 -0
- package/build/server/chunks/_page.svelte-dabsQl9c.js.map +1 -1
- package/build/server/chunks/{_server.ts-bk_EeAdY.js → _server.ts-05JJOdcX.js} +20 -14
- package/build/server/chunks/_server.ts-05JJOdcX.js.map +1 -0
- package/build/server/chunks/{_server.ts-DEx9-epI.js → _server.ts-B54Pvhgc.js} +4 -3
- package/build/server/chunks/_server.ts-B54Pvhgc.js.map +1 -0
- package/build/server/chunks/_server.ts-BB46Fbqn.js +59 -0
- package/build/server/chunks/_server.ts-BB46Fbqn.js.map +1 -0
- package/build/server/chunks/{_server.ts-BRAzC6W1.js → _server.ts-BCljU9Sg.js} +33 -8
- package/build/server/chunks/_server.ts-BCljU9Sg.js.map +1 -0
- package/build/server/chunks/{_server.ts-D-vgx5UZ.js → _server.ts-BTmknWpO.js} +2 -2
- package/build/server/chunks/{_server.ts-D-vgx5UZ.js.map → _server.ts-BTmknWpO.js.map} +1 -1
- package/build/server/chunks/{_server.ts-tChyh9FX.js → _server.ts-BXhmLZwN.js} +4 -2
- package/build/server/chunks/{_server.ts-tChyh9FX.js.map → _server.ts-BXhmLZwN.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CvJKTS3Z.js → _server.ts-BbRSpB74.js} +4 -2
- package/build/server/chunks/{_server.ts-CvJKTS3Z.js.map → _server.ts-BbRSpB74.js.map} +1 -1
- package/build/server/chunks/{_server.ts-DKNIsQeH.js → _server.ts-Bol54_Qo.js} +4 -3
- package/build/server/chunks/_server.ts-Bol54_Qo.js.map +1 -0
- package/build/server/chunks/{_server.ts-Dz9Jd9Jh.js → _server.ts-C0PO_cAu.js} +4 -3
- package/build/server/chunks/{_server.ts-Dz9Jd9Jh.js.map → _server.ts-C0PO_cAu.js.map} +1 -1
- package/build/server/chunks/_server.ts-C6NRpe7e.js +33 -0
- package/build/server/chunks/_server.ts-C6NRpe7e.js.map +1 -0
- package/build/server/chunks/_server.ts-CGqCOCdK.js +53 -0
- package/build/server/chunks/_server.ts-CGqCOCdK.js.map +1 -0
- package/build/server/chunks/{_server.ts-B2wIgsW4.js → _server.ts-CZb-BI5H.js} +4 -3
- package/build/server/chunks/_server.ts-CZb-BI5H.js.map +1 -0
- package/build/server/chunks/_server.ts-DPHRUFYS.js +159 -0
- package/build/server/chunks/_server.ts-DPHRUFYS.js.map +1 -0
- package/build/server/chunks/{_server.ts-AnBXfZXh.js → _server.ts-D_WRex0k.js} +5 -3
- package/build/server/chunks/_server.ts-D_WRex0k.js.map +1 -0
- package/build/server/chunks/{_server.ts-CJGyN8mw.js → _server.ts-DiBMY7Ho.js} +4 -3
- package/build/server/chunks/_server.ts-DiBMY7Ho.js.map +1 -0
- package/build/server/chunks/cache-BlMaDsHi.js.map +1 -1
- package/build/server/chunks/{guest-registry-t0-7Zv5q.js → guest-registry-Dxvd7p-g.js} +10 -1
- package/build/server/chunks/guest-registry-Dxvd7p-g.js.map +1 -0
- package/build/server/chunks/index-CoYB03g7.js.map +1 -1
- package/build/server/chunks/index2-dSGQ9Eaa.js.map +1 -1
- package/build/server/chunks/{library-apns-Dl3iRE2h.js → library-apns-D8RPINlv.js} +62 -7
- package/build/server/chunks/library-apns-D8RPINlv.js.map +1 -0
- package/build/server/chunks/{pending-requests-C9p57WoU.js → pending-requests-8rWjrF6d.js} +3 -2
- package/build/server/chunks/pending-requests-8rWjrF6d.js.map +1 -0
- package/build/server/chunks/presence-store-Bx_g0-Gd.js +23 -0
- package/build/server/chunks/presence-store-Bx_g0-Gd.js.map +1 -0
- package/build/server/chunks/{pty-manager-CkZNoW1t.js → pty-manager-CoWVT56F.js} +27 -6
- package/build/server/chunks/pty-manager-CoWVT56F.js.map +1 -0
- package/build/server/chunks/root-D4IoFC8F.js.map +1 -1
- package/build/server/chunks/shooter-home-4f_HkdGI.js +10 -0
- package/build/server/chunks/shooter-home-4f_HkdGI.js.map +1 -0
- package/build/server/chunks/state.svelte-CmHqngc_.js.map +1 -1
- package/build/server/index.js +1 -1
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +52 -24
- package/build/server/manifest.js.map +1 -1
- package/package.json +2 -2
- package/server.ts +2 -0
- package/src/lib/modules/client/common/index.ts +1 -0
- package/src/lib/modules/client/common/presence.ts +47 -0
- package/src/lib/modules/client/dashboard/AutopilotPanel.svelte +584 -0
- package/src/lib/modules/client/dashboard/autopilot-driver.svelte.ts +681 -0
- package/src/lib/modules/client/dashboard/decide-injection.ts +127 -0
- package/src/lib/modules/client/dashboard/index.ts +1 -0
- package/src/lib/modules/client/dashboard/store.svelte.ts +65 -24
- package/src/lib/modules/client/neurolink/fetch-proxy.ts +38 -1
- package/src/lib/modules/client/neurolink/provider-config.ts +13 -37
- package/src/lib/modules/server/apn/apns-payload.ts +50 -0
- package/src/lib/modules/server/apn/library-apns.ts +50 -8
- package/src/lib/modules/server/apn/pending-requests.ts +3 -1
- package/src/lib/modules/server/sessions/autopilot-context.ts +57 -0
- package/src/lib/modules/server/sessions/autopilot-engine.ts +451 -0
- package/src/lib/modules/server/sessions/litellm-client.ts +171 -0
- package/src/lib/modules/server/sessions/next-step-consensus.ts +210 -0
- package/src/lib/modules/server/sessions/summary-store.ts +113 -0
- package/src/lib/modules/server/terminal/agent-launch.ts +26 -0
- package/src/lib/modules/server/terminal/pty-holder.cjs +6 -0
- package/src/lib/modules/server/terminal/pty-manager.ts +13 -3
- package/src/lib/modules/server/terminal/session-watcher.ts +12 -2
- package/src/lib/modules/server/terminal/terminal-store.ts +3 -1
- package/src/lib/modules/server/utils/shooter-home.ts +16 -0
- package/src/lib/modules/server/ws/events-handler.ts +32 -0
- package/src/lib/modules/server/ws/presence-store.ts +50 -0
- package/src/lib/types/autopilot.ts +138 -0
- package/src/lib/types/index.ts +1 -0
- package/src/lib/types/terminal-client.ts +2 -0
- package/src/routes/+layout.server.ts +2 -0
- package/src/routes/+layout.svelte +19 -4
- package/src/routes/+page.svelte +9 -1
- package/src/routes/api/autopilot/+server.ts +74 -0
- package/src/routes/api/autopilot/goal/+server.ts +72 -0
- package/src/routes/api/neurolink-proxy/+server.ts +39 -8
- package/src/routes/api/notify/+server.ts +38 -14
- package/src/routes/api/presence/+server.ts +39 -0
- package/src/routes/api/summaries/+server.ts +99 -0
- package/src/routes/api/ws-status/+server.ts +8 -5
- package/build/client/_app/immutable/assets/2.DjiwkLqE.css +0 -1
- package/build/client/_app/immutable/assets/2.DjiwkLqE.css.br +0 -0
- package/build/client/_app/immutable/assets/2.DjiwkLqE.css.gz +0 -0
- package/build/client/_app/immutable/chunks/BB2l8o4X.js.br +0 -0
- package/build/client/_app/immutable/chunks/BB2l8o4X.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BvmdJful.js.br +0 -0
- package/build/client/_app/immutable/chunks/BvmdJful.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CRkG7oE4.js +0 -1
- package/build/client/_app/immutable/chunks/CRkG7oE4.js.br +0 -0
- package/build/client/_app/immutable/chunks/CRkG7oE4.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C_YNQL8b.js +0 -3
- package/build/client/_app/immutable/chunks/C_YNQL8b.js.br +0 -0
- package/build/client/_app/immutable/chunks/C_YNQL8b.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ClIPTXf3.js.br +0 -0
- package/build/client/_app/immutable/chunks/ClIPTXf3.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DIZ3Qst5.js.br +0 -0
- package/build/client/_app/immutable/chunks/DT4H19pV.js.br +0 -0
- package/build/client/_app/immutable/chunks/DT4H19pV.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ZS5XYDx_.js.br +0 -0
- package/build/client/_app/immutable/chunks/pRcLbE0d.js +0 -1
- package/build/client/_app/immutable/chunks/pRcLbE0d.js.br +0 -0
- package/build/client/_app/immutable/chunks/pRcLbE0d.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.Bd-DfeJi.js.br +0 -0
- package/build/client/_app/immutable/entry/app.Bd-DfeJi.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.evvp4tX7.js +0 -1
- package/build/client/_app/immutable/entry/start.evvp4tX7.js.br +0 -2
- package/build/client/_app/immutable/entry/start.evvp4tX7.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.Bl-1LQWM.js +0 -10
- package/build/client/_app/immutable/nodes/0.Bl-1LQWM.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Bl-1LQWM.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.DT4dq6Ay.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.DT4dq6Ay.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.CF7RGXpe.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.CF7RGXpe.js.gz +0 -0
- package/build/client/_app/immutable/nodes/11.BV_G7yLI.js.br +0 -0
- package/build/client/_app/immutable/nodes/11.BV_G7yLI.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DcRhsjYp.js +0 -13
- package/build/client/_app/immutable/nodes/2.DcRhsjYp.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DcRhsjYp.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.0MMe3oxR.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.0MMe3oxR.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.CBX9A3ka.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.CBX9A3ka.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.DIVKuZc9.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.DIVKuZc9.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.ComiWlV6.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.ComiWlV6.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.vkPx1kVP.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.vkPx1kVP.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.Bmr3sWbS.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.Bmr3sWbS.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.CAJucyeI.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.CAJucyeI.js.gz +0 -0
- package/build/server/chunks/0-DDGB6CRT.js.map +0 -1
- package/build/server/chunks/_page.svelte-BLo2v_8E.js.map +0 -1
- package/build/server/chunks/_page.svelte-tBuIq8Pg.js +0 -159
- package/build/server/chunks/_page.svelte-tBuIq8Pg.js.map +0 -1
- package/build/server/chunks/_server.ts-AnBXfZXh.js.map +0 -1
- package/build/server/chunks/_server.ts-B2wIgsW4.js.map +0 -1
- package/build/server/chunks/_server.ts-BRAzC6W1.js.map +0 -1
- package/build/server/chunks/_server.ts-CJGyN8mw.js.map +0 -1
- package/build/server/chunks/_server.ts-DEx9-epI.js.map +0 -1
- package/build/server/chunks/_server.ts-DKNIsQeH.js.map +0 -1
- package/build/server/chunks/_server.ts-bk_EeAdY.js.map +0 -1
- package/build/server/chunks/guest-registry-t0-7Zv5q.js.map +0 -1
- package/build/server/chunks/library-apns-Dl3iRE2h.js.map +0 -1
- package/build/server/chunks/pending-requests-C9p57WoU.js.map +0 -1
- package/build/server/chunks/pty-manager-CkZNoW1t.js.map +0 -1
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// Pure, deterministic next-step consensus algorithm.
|
|
2
|
+
// No I/O, no LLM. Fully unit-testable.
|
|
3
|
+
// See spec docs/superpowers/specs/2026-05-29-autonomous-summarization-engine-design.md §4
|
|
4
|
+
|
|
5
|
+
import type { AgentProposal, ConsensusResult, MergeOptions, NextStep } from '$lib/types';
|
|
6
|
+
|
|
7
|
+
const DEFAULT_K = 3;
|
|
8
|
+
const DEFAULT_QUORUM = 3;
|
|
9
|
+
const JACCARD_THRESHOLD = 0.6;
|
|
10
|
+
|
|
11
|
+
/** A grouped cluster of proposals that all map to the same semantic step. @internal */
|
|
12
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
13
|
+
interface Cluster {
|
|
14
|
+
/** Distinct agent indices that proposed a member of this cluster. */
|
|
15
|
+
agentIndices: Set<number>;
|
|
16
|
+
/** All individual proposals that map into this cluster (for label + confidence). */
|
|
17
|
+
members: { confidence: number; text: string }[];
|
|
18
|
+
/** Normalized representative text (first normalized form in cluster). */
|
|
19
|
+
normalized: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Merge up to 5 agent next-step lists into a consensus result.
|
|
24
|
+
*
|
|
25
|
+
* Algorithm (per spec §4):
|
|
26
|
+
* 1. Cap each agent list at K items.
|
|
27
|
+
* 2. Normalize each text (lowercase, trim, collapse whitespace, strip trailing punct).
|
|
28
|
+
* 3. Group near-duplicates: same normalized string OR Jaccard token-overlap ≥ 0.6.
|
|
29
|
+
* Track distinct agent indices per group for vote counting.
|
|
30
|
+
* 4. Score each group by vote count = |distinct agent indices|.
|
|
31
|
+
* 5. Consensus = groups with votes ≥ quorum, sorted by votes desc then mean confidence desc.
|
|
32
|
+
* 6. If no group meets quorum, return the single highest-vote group flagged tentative:true.
|
|
33
|
+
* 7. Label each output step with the highest-confidence original phrasing in the group.
|
|
34
|
+
*/
|
|
35
|
+
export function mergeNextStepConsensus(
|
|
36
|
+
lists: readonly (readonly AgentProposal[])[],
|
|
37
|
+
opts?: MergeOptions
|
|
38
|
+
): ConsensusResult {
|
|
39
|
+
const k = opts?.k ?? DEFAULT_K;
|
|
40
|
+
const quorum = opts?.quorum ?? DEFAULT_QUORUM;
|
|
41
|
+
const agentCount = lists.length;
|
|
42
|
+
|
|
43
|
+
if (agentCount === 0) {
|
|
44
|
+
return { agentCount: 0, quorum, steps: [] };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Step 1 + 2: cap each agent list and normalize proposals.
|
|
48
|
+
const clusters: Cluster[] = [];
|
|
49
|
+
|
|
50
|
+
for (let agentIndex = 0; agentIndex < lists.length; agentIndex++) {
|
|
51
|
+
const list = lists[agentIndex];
|
|
52
|
+
const capped = list.slice(0, k);
|
|
53
|
+
|
|
54
|
+
for (const proposal of capped) {
|
|
55
|
+
const norm = normalize(proposal.text);
|
|
56
|
+
if (norm.length === 0) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Step 3: find an existing cluster to join.
|
|
61
|
+
const targetCluster = findOrCreateCluster(clusters, norm);
|
|
62
|
+
targetCluster.agentIndices.add(agentIndex);
|
|
63
|
+
// Sanitise confidence at ingestion: the type says `number` but the value comes from raw LLM
|
|
64
|
+
// JSON, so a missing/NaN/Infinity/out-of-range confidence must NOT leak into meanConf (it
|
|
65
|
+
// would poison the mean and could spuriously clear the downstream 0.7 inject floor). Invalid
|
|
66
|
+
// → 0 (correctly fails the floor); valid → clamped to [0,1].
|
|
67
|
+
targetCluster.members.push({
|
|
68
|
+
confidence: safeConfidence(proposal.confidence),
|
|
69
|
+
text: proposal.text,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (clusters.length === 0) {
|
|
75
|
+
return { agentCount, quorum, steps: [] };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Step 4: compute vote count and mean confidence per cluster.
|
|
79
|
+
const scored = clusters.map((cluster) => {
|
|
80
|
+
const votes = cluster.agentIndices.size;
|
|
81
|
+
const meanConf =
|
|
82
|
+
cluster.members.reduce((sum, m) => sum + m.confidence, 0) / cluster.members.length;
|
|
83
|
+
|
|
84
|
+
// Label = original phrasing from the highest-confidence member.
|
|
85
|
+
const bestMember = cluster.members.reduce((best, m) =>
|
|
86
|
+
m.confidence > best.confidence ? m : best
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return { cluster, meanConf, text: bestMember.text, votes };
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Step 5: separate consensus groups from sub-quorum groups.
|
|
93
|
+
const consensusGroups = scored
|
|
94
|
+
.filter((s) => s.votes >= quorum)
|
|
95
|
+
.sort((a, b) => b.votes - a.votes || b.meanConf - a.meanConf);
|
|
96
|
+
|
|
97
|
+
if (consensusGroups.length > 0) {
|
|
98
|
+
const steps: NextStep[] = consensusGroups.map((s) => ({
|
|
99
|
+
confidence: s.meanConf,
|
|
100
|
+
text: s.text,
|
|
101
|
+
votes: s.votes,
|
|
102
|
+
}));
|
|
103
|
+
return { agentCount, quorum, steps };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Step 6: no group reached quorum — tentative fallback (single highest-vote group).
|
|
107
|
+
const best = scored.reduce((prev, curr) => {
|
|
108
|
+
if (curr.votes > prev.votes) {
|
|
109
|
+
return curr;
|
|
110
|
+
}
|
|
111
|
+
if (curr.votes === prev.votes && curr.meanConf > prev.meanConf) {
|
|
112
|
+
return curr;
|
|
113
|
+
}
|
|
114
|
+
return prev;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const steps: NextStep[] = [
|
|
118
|
+
{
|
|
119
|
+
confidence: best.meanConf,
|
|
120
|
+
tentative: true,
|
|
121
|
+
text: best.text,
|
|
122
|
+
votes: best.votes,
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
return { agentCount, quorum, steps };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Find a cluster whose normalized key matches `norm` exactly or has Jaccard ≥ threshold,
|
|
130
|
+
* or create and register a new one.
|
|
131
|
+
*/
|
|
132
|
+
function findOrCreateCluster(clusters: Cluster[], norm: string): Cluster {
|
|
133
|
+
for (const cluster of clusters) {
|
|
134
|
+
if (cluster.normalized === norm) {
|
|
135
|
+
return cluster;
|
|
136
|
+
}
|
|
137
|
+
if (jaccard(cluster.normalized, norm) >= JACCARD_THRESHOLD) {
|
|
138
|
+
return cluster;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const newCluster: Cluster = {
|
|
142
|
+
agentIndices: new Set(),
|
|
143
|
+
members: [],
|
|
144
|
+
normalized: norm,
|
|
145
|
+
};
|
|
146
|
+
clusters.push(newCluster);
|
|
147
|
+
return newCluster;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Compute the Jaccard token-overlap coefficient between two normalized strings.
|
|
152
|
+
* J(A,B) = |A ∩ B| / |A ∪ B|
|
|
153
|
+
*/
|
|
154
|
+
function jaccard(a: string, b: string): number {
|
|
155
|
+
const setA = tokenSet(a);
|
|
156
|
+
const setB = tokenSet(b);
|
|
157
|
+
if (setA.size === 0 && setB.size === 0) {
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
if (setA.size === 0 || setB.size === 0) {
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
let intersection = 0;
|
|
164
|
+
for (const token of setA) {
|
|
165
|
+
if (setB.has(token)) {
|
|
166
|
+
intersection++;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const union = setA.size + setB.size - intersection;
|
|
170
|
+
return intersection / union;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Normalize a step text for grouping:
|
|
175
|
+
* lowercase, trim, collapse internal whitespace, strip trailing punctuation.
|
|
176
|
+
*/
|
|
177
|
+
function normalize(text: string): string {
|
|
178
|
+
return text
|
|
179
|
+
.toLowerCase()
|
|
180
|
+
.trim()
|
|
181
|
+
.replace(/\s+/g, ' ')
|
|
182
|
+
.replace(/[.,;:!?]+$/, '');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Coerce a raw (untrusted) confidence into a finite number in [0,1]; invalid → 0. */
|
|
186
|
+
function safeConfidence(value: unknown): number {
|
|
187
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
return Math.max(0, Math.min(1, value));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── Private helper ──────────────────────────────────────────────────────────
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Tokenize a normalized string into a set of tokens.
|
|
197
|
+
*/
|
|
198
|
+
function tokenSet(normalized: string): Set<string> {
|
|
199
|
+
// Strip wrapping punctuation from each token so code-bearing proposals cluster: the lenses phrase
|
|
200
|
+
// the same action differently and wrap code in backticks/quotes (`return a - b`, "calc.js"), which
|
|
201
|
+
// otherwise fragments the tokens ("`return" ≠ "return") and makes the Jaccard overlap undercount
|
|
202
|
+
// real agreement. Pure-punctuation tokens (operators like - / +) drop out, which is fine — we are
|
|
203
|
+
// grouping intent, not preserving exact syntax.
|
|
204
|
+
return new Set(
|
|
205
|
+
normalized
|
|
206
|
+
.split(/\s+/)
|
|
207
|
+
.map((t) => t.replace(/^[^\p{L}\p{N}]+|[^\p{L}\p{N}]+$/gu, ''))
|
|
208
|
+
.filter((t) => t.length > 0)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summary Store — SQLite persistence for session summary records.
|
|
3
|
+
*
|
|
4
|
+
* Persists SessionSummaryRecord rows written by the autopilot engine after
|
|
5
|
+
* each summarise+consensus pipeline run. Uses better-sqlite3 with WAL journal
|
|
6
|
+
* mode, mirroring the pattern from terminal-store.ts.
|
|
7
|
+
*
|
|
8
|
+
* Database location: ~/.shooter/shooter.db (shared with terminal-store)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { SessionSummaryRecord } from '$lib/types';
|
|
12
|
+
|
|
13
|
+
import Database from 'better-sqlite3';
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
|
|
17
|
+
import { shooterDataDir } from '../utils/shooter-home.js';
|
|
18
|
+
|
|
19
|
+
// ── Constants ────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const DB_DIR = shooterDataDir();
|
|
22
|
+
const DB_PATH = path.join(DB_DIR, 'shooter.db');
|
|
23
|
+
|
|
24
|
+
// ── Column list ──────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
const COLUMNS = [
|
|
27
|
+
'id',
|
|
28
|
+
'terminal_id',
|
|
29
|
+
'session_id',
|
|
30
|
+
'project_name',
|
|
31
|
+
'summary',
|
|
32
|
+
'next_steps',
|
|
33
|
+
'trigger',
|
|
34
|
+
'created_at',
|
|
35
|
+
] as const;
|
|
36
|
+
|
|
37
|
+
// ── Row ↔ Record conversion ──────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
export class SummaryStore {
|
|
40
|
+
private db: Database.Database;
|
|
41
|
+
|
|
42
|
+
constructor() {
|
|
43
|
+
fs.mkdirSync(DB_DIR, { recursive: true });
|
|
44
|
+
this.db = new Database(DB_PATH);
|
|
45
|
+
this.db.pragma('journal_mode = WAL');
|
|
46
|
+
this.db.exec(`
|
|
47
|
+
CREATE TABLE IF NOT EXISTS session_summaries (
|
|
48
|
+
id TEXT PRIMARY KEY,
|
|
49
|
+
terminal_id TEXT,
|
|
50
|
+
session_id TEXT,
|
|
51
|
+
project_name TEXT,
|
|
52
|
+
summary TEXT NOT NULL,
|
|
53
|
+
next_steps TEXT NOT NULL DEFAULT '[]',
|
|
54
|
+
trigger TEXT NOT NULL,
|
|
55
|
+
created_at TEXT NOT NULL
|
|
56
|
+
)
|
|
57
|
+
`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
insert(record: SessionSummaryRecord): void {
|
|
61
|
+
const placeholders = COLUMNS.map(() => '?').join(', ');
|
|
62
|
+
this.db
|
|
63
|
+
.prepare(`INSERT INTO session_summaries (${COLUMNS.join(', ')}) VALUES (${placeholders})`)
|
|
64
|
+
.run(
|
|
65
|
+
record.id,
|
|
66
|
+
record.terminalId,
|
|
67
|
+
record.sessionId,
|
|
68
|
+
record.projectName,
|
|
69
|
+
record.summary,
|
|
70
|
+
record.nextSteps,
|
|
71
|
+
record.trigger,
|
|
72
|
+
record.createdAt
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
listRecent(limit: number, sessionId?: string): SessionSummaryRecord[] {
|
|
77
|
+
if (sessionId) {
|
|
78
|
+
const rows = this.db
|
|
79
|
+
.prepare(
|
|
80
|
+
'SELECT * FROM session_summaries WHERE session_id = ? ORDER BY created_at DESC LIMIT ?'
|
|
81
|
+
)
|
|
82
|
+
.all(sessionId, limit) as Record<string, unknown>[];
|
|
83
|
+
return rows.map(rowToRecord);
|
|
84
|
+
}
|
|
85
|
+
const rows = this.db
|
|
86
|
+
.prepare('SELECT * FROM session_summaries ORDER BY created_at DESC LIMIT ?')
|
|
87
|
+
.all(limit) as Record<string, unknown>[];
|
|
88
|
+
return rows.map(rowToRecord);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── SummaryStore ─────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
function rowToRecord(row: Record<string, unknown>): SessionSummaryRecord {
|
|
95
|
+
return {
|
|
96
|
+
createdAt: row.created_at as string,
|
|
97
|
+
id: row.id as string,
|
|
98
|
+
nextSteps: row.next_steps as string,
|
|
99
|
+
projectName: (row.project_name as string) ?? null,
|
|
100
|
+
sessionId: (row.session_id as string) ?? null,
|
|
101
|
+
summary: row.summary as string,
|
|
102
|
+
terminalId: (row.terminal_id as string) ?? null,
|
|
103
|
+
trigger: row.trigger as string,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── Singleton ────────────────────────────────────────────────────────
|
|
108
|
+
// Shared instance across module loaders (same pattern as terminal-store).
|
|
109
|
+
|
|
110
|
+
const SS_GLOBAL_KEY = '__shooter_summary_store';
|
|
111
|
+
export const summaryStore: SummaryStore =
|
|
112
|
+
((globalThis as Record<string, unknown>)[SS_GLOBAL_KEY] as SummaryStore) || new SummaryStore();
|
|
113
|
+
(globalThis as Record<string, unknown>)[SS_GLOBAL_KEY] = summaryStore;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Inject a default `--permission-mode` into a claude launch, configurable via the
|
|
2
|
+
// SHOOTER_AGENT_PERMISSION_MODE env var, so managed agent terminals can actually act instead of
|
|
3
|
+
// being auto-denied by a restrictive global Claude config. Claude-only (the flag is
|
|
4
|
+
// claude-specific) and never overrides an explicit --permission-mode the caller passed.
|
|
5
|
+
// Pure — unit-tested in tests/agent-launch.test.cjs.
|
|
6
|
+
|
|
7
|
+
const CLAUDE_COMMANDS = new Set(['claude']);
|
|
8
|
+
|
|
9
|
+
export function withAgentPermissionMode(
|
|
10
|
+
command: string,
|
|
11
|
+
args: string[],
|
|
12
|
+
mode: string | undefined
|
|
13
|
+
): string[] {
|
|
14
|
+
const trimmed = (mode ?? '').trim();
|
|
15
|
+
if (trimmed.length === 0) {
|
|
16
|
+
return args; // feature off when unset
|
|
17
|
+
}
|
|
18
|
+
const base = command.split('/').pop() ?? command;
|
|
19
|
+
if (!CLAUDE_COMMANDS.has(base)) {
|
|
20
|
+
return args; // --permission-mode is claude-specific
|
|
21
|
+
}
|
|
22
|
+
if (args.includes('--permission-mode')) {
|
|
23
|
+
return args; // an explicit flag always wins
|
|
24
|
+
}
|
|
25
|
+
return [...args, '--permission-mode', trimmed];
|
|
26
|
+
}
|
|
@@ -114,6 +114,12 @@ try {
|
|
|
114
114
|
/* best effort */
|
|
115
115
|
}
|
|
116
116
|
ptyEnv.SHOOTER_CLIPBOARD_DIR = clipboardDir;
|
|
117
|
+
// Tag this PTY with its Shooter terminal id so in-agent lifecycle hooks
|
|
118
|
+
// (notifier.cjs) can attribute their events to THIS managed terminal. The
|
|
119
|
+
// autopilot engine + phone driver key on terminalId; a plain shell that a
|
|
120
|
+
// user starts outside Shooter never sees this var, so its behaviour is
|
|
121
|
+
// unchanged.
|
|
122
|
+
ptyEnv.SHOOTER_TERMINAL_ID = id;
|
|
117
123
|
|
|
118
124
|
// Prepend clipboard shim scripts to PATH so tools find our xclip/wl-paste
|
|
119
125
|
const shimsDir = path.resolve(__dirname, '..', 'scripts', 'clipboard-shims');
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
readOnlySourceForCommand,
|
|
19
19
|
} from '../sessions/provider-paths';
|
|
20
20
|
import { broadcastEvent } from '../ws/server.js';
|
|
21
|
+
import { withAgentPermissionMode } from './agent-launch.js';
|
|
21
22
|
import { HolderClient } from './holder-client';
|
|
22
23
|
import { openCodeWatcher } from './opencode-watcher';
|
|
23
24
|
import { terminalStore } from './terminal-store';
|
|
@@ -134,8 +135,17 @@ class PtyManager {
|
|
|
134
135
|
const socketPath = `/tmp/shooter-term-${id}.sock`;
|
|
135
136
|
const holderScript = resolveHolderPath();
|
|
136
137
|
|
|
138
|
+
// Inject the configured default --permission-mode for claude (SHOOTER_AGENT_PERMISSION_MODE),
|
|
139
|
+
// so a managed agent can act instead of being auto-denied by a restrictive global config.
|
|
140
|
+
// No-op unless set; never overrides an explicit flag. Persisted so reconnect keeps it.
|
|
141
|
+
const launchArgs = withAgentPermissionMode(
|
|
142
|
+
command,
|
|
143
|
+
args,
|
|
144
|
+
process.env.SHOOTER_AGENT_PERMISSION_MODE
|
|
145
|
+
);
|
|
146
|
+
|
|
137
147
|
// Fork the holder process as detached so it survives server restarts
|
|
138
|
-
const holderArgs = [id, socketPath, cwd, String(cols), String(rows), command, ...
|
|
148
|
+
const holderArgs = [id, socketPath, cwd, String(cols), String(rows), command, ...launchArgs];
|
|
139
149
|
const holder: ChildProcess = fork(holderScript, holderArgs, {
|
|
140
150
|
detached: true,
|
|
141
151
|
stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
|
|
@@ -179,7 +189,7 @@ class PtyManager {
|
|
|
179
189
|
|
|
180
190
|
const now = new Date();
|
|
181
191
|
const terminal: ManagedTerminal = {
|
|
182
|
-
args,
|
|
192
|
+
args: launchArgs,
|
|
183
193
|
clients: new Set(),
|
|
184
194
|
cols,
|
|
185
195
|
command,
|
|
@@ -210,7 +220,7 @@ class PtyManager {
|
|
|
210
220
|
|
|
211
221
|
// Persist to SQLite
|
|
212
222
|
terminalStore.insert({
|
|
213
|
-
args: JSON.stringify(
|
|
223
|
+
args: JSON.stringify(launchArgs),
|
|
214
224
|
cols,
|
|
215
225
|
command,
|
|
216
226
|
createdAt: now.toISOString(),
|
|
@@ -26,6 +26,10 @@ class SessionWatcher {
|
|
|
26
26
|
string,
|
|
27
27
|
Map<string, { parts: MessagePart[]; timestamp: string }>
|
|
28
28
|
>();
|
|
29
|
+
// Last read offset per file, retained ACROSS stop() so a re-subscribe resumes where it left off
|
|
30
|
+
// instead of jumping to the current end of file (which would skip any lines — e.g. an agent-idle
|
|
31
|
+
// marker — written while no subscriber happened to be attached).
|
|
32
|
+
private lastOffsetPerFile = new Map<string, number>();
|
|
29
33
|
// Buffer for incomplete trailing lines (no terminating newline yet)
|
|
30
34
|
private lineBufferPerFile = new Map<string, string>();
|
|
31
35
|
// Track message index per file for generating fallback IDs
|
|
@@ -107,6 +111,8 @@ class SessionWatcher {
|
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
void watched.watcher.close();
|
|
114
|
+
// Remember where we stopped reading so a future re-subscribe resumes from here, not from EOF.
|
|
115
|
+
this.lastOffsetPerFile.set(filePath, watched.offset);
|
|
110
116
|
this.watchedFiles.delete(filePath);
|
|
111
117
|
this.assistantTurnsPerFile.delete(filePath);
|
|
112
118
|
this.messageIndexPerFile.delete(filePath);
|
|
@@ -149,8 +155,12 @@ class SessionWatcher {
|
|
|
149
155
|
};
|
|
150
156
|
}
|
|
151
157
|
|
|
152
|
-
// Initialize tracking state for this file
|
|
153
|
-
|
|
158
|
+
// Initialize tracking state for this file. Resume from the last read offset if we watched this
|
|
159
|
+
// file before (so content written during the gap is not skipped); otherwise start at EOF.
|
|
160
|
+
const fileSize = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0;
|
|
161
|
+
const remembered = this.lastOffsetPerFile.get(filePath);
|
|
162
|
+
this.lastOffsetPerFile.delete(filePath);
|
|
163
|
+
const initialOffset = remembered !== undefined ? Math.min(remembered, fileSize) : fileSize;
|
|
154
164
|
this.assistantTurnsPerFile.set(filePath, new Map());
|
|
155
165
|
this.messageIndexPerFile.set(filePath, 0);
|
|
156
166
|
this.lineBufferPerFile.set(filePath, '');
|
|
@@ -13,9 +13,11 @@ import Database from 'better-sqlite3';
|
|
|
13
13
|
import * as fs from 'fs';
|
|
14
14
|
import * as path from 'path';
|
|
15
15
|
|
|
16
|
+
import { shooterDataDir } from '../utils/shooter-home.js';
|
|
17
|
+
|
|
16
18
|
// ── Constants ────────────────────────────────────────────────────────
|
|
17
19
|
|
|
18
|
-
const DB_DIR =
|
|
20
|
+
const DB_DIR = shooterDataDir();
|
|
19
21
|
const DB_PATH = path.join(DB_DIR, 'shooter.db');
|
|
20
22
|
|
|
21
23
|
// ── Snake/Camel Conversion ───────────────────────────────────────────
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The Shooter data directory — the SQLite DB and engine/permission state live here.
|
|
6
|
+
*
|
|
7
|
+
* Honors `SHOOTER_HOME` so an isolated instance (a test server, or a second server run alongside
|
|
8
|
+
* the user's daemon) keeps its DB/state separate from the default `~/.shooter` instead of sharing
|
|
9
|
+
* it (which would let one server's `reconnectAll()` adopt the other's live terminals). This matches
|
|
10
|
+
* the CLI (`bin/shooter.cjs`) and the env loader (`env.ts`), which already treat `SHOOTER_HOME` as
|
|
11
|
+
* the data dir. When unset, falls back to `~/.shooter`.
|
|
12
|
+
*/
|
|
13
|
+
export function shooterDataDir(): string {
|
|
14
|
+
const override = process.env.SHOOTER_HOME?.trim();
|
|
15
|
+
return override && override.length > 0 ? override : join(homedir(), '.shooter');
|
|
16
|
+
}
|
|
@@ -13,6 +13,18 @@ const eventsClients: Set<WebSocket> =
|
|
|
13
13
|
((globalThis as Record<string, unknown>)[EVENTS_KEY] as Set<WebSocket>) || new Set<WebSocket>();
|
|
14
14
|
(globalThis as Record<string, unknown>)[EVENTS_KEY] = eventsClients;
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Server-side listeners (e.g. the always-on autopilot engine) that observe
|
|
18
|
+
* every broadcast event. Kept on globalThis so the listener registered from the
|
|
19
|
+
* server.ts module graph and the broadcaster called from the bundled SvelteKit
|
|
20
|
+
* handler graph share one set.
|
|
21
|
+
*/
|
|
22
|
+
const LISTENERS_KEY = '__shooter_ws_event_listeners';
|
|
23
|
+
const eventListeners: Set<(event: ShooterEvent) => void> =
|
|
24
|
+
((globalThis as Record<string, unknown>)[LISTENERS_KEY] as Set<(event: ShooterEvent) => void>) ||
|
|
25
|
+
new Set<(event: ShooterEvent) => void>();
|
|
26
|
+
(globalThis as Record<string, unknown>)[LISTENERS_KEY] = eventListeners;
|
|
27
|
+
|
|
16
28
|
// ── Handlers ────────────────────────────────────────────────────────
|
|
17
29
|
|
|
18
30
|
/**
|
|
@@ -31,6 +43,15 @@ export function broadcastEvent(event: ShooterEvent): void {
|
|
|
31
43
|
ws.send(data);
|
|
32
44
|
}
|
|
33
45
|
}
|
|
46
|
+
|
|
47
|
+
// Fan out to server-side listeners (autopilot engine, etc.).
|
|
48
|
+
for (const listener of eventListeners) {
|
|
49
|
+
try {
|
|
50
|
+
listener(event);
|
|
51
|
+
} catch {
|
|
52
|
+
// a faulty listener must never break the broadcast
|
|
53
|
+
}
|
|
54
|
+
}
|
|
34
55
|
}
|
|
35
56
|
|
|
36
57
|
/**
|
|
@@ -62,3 +83,14 @@ export function handleEventsConnection(ws: WebSocket): void {
|
|
|
62
83
|
eventsClients.delete(ws);
|
|
63
84
|
});
|
|
64
85
|
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Register a server-side listener that receives every broadcast ShooterEvent.
|
|
89
|
+
* Returns an unsubscribe function. Used by the always-on autopilot engine.
|
|
90
|
+
*/
|
|
91
|
+
export function onShooterEvent(listener: (event: ShooterEvent) => void): () => void {
|
|
92
|
+
eventListeners.add(listener);
|
|
93
|
+
return (): void => {
|
|
94
|
+
eventListeners.delete(listener);
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Viewer-presence store: "is someone actually watching" (foreground) vs "away".
|
|
2
|
+
//
|
|
3
|
+
// Why this exists: the phone-resident autonomous loop holds a PERSISTENT /ws/events
|
|
4
|
+
// connection to run. So raw WebSocket-connection count no longer means "the user is
|
|
5
|
+
// watching" — it's always > 0. The autopilot push decision must instead key on whether a
|
|
6
|
+
// viewer is FOREGROUNDED (heartbeat within TTL), reported via POST /api/presence.
|
|
7
|
+
//
|
|
8
|
+
// globalThis singleton so the SvelteKit route (which writes it) and the server.ts engine
|
|
9
|
+
// (which reads it) share one instance across the dual module graph — same pattern as the
|
|
10
|
+
// ws ticket store and the event-listener registry.
|
|
11
|
+
|
|
12
|
+
const PRESENCE_TTL_MS = 45_000;
|
|
13
|
+
const KEY = '__shooter_presence';
|
|
14
|
+
|
|
15
|
+
// eslint-disable-next-line no-restricted-syntax -- internal singleton shape, never exported
|
|
16
|
+
interface PresenceRecord {
|
|
17
|
+
everReported: boolean;
|
|
18
|
+
lastForegroundAt: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** True once any client has ever reported presence (used for backward-compatible fallback). */
|
|
22
|
+
export function hasEverReported(): boolean {
|
|
23
|
+
return store().everReported;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** True when a viewer reported `foreground` within the TTL window. */
|
|
27
|
+
export function isViewerPresent(
|
|
28
|
+
now: number = Date.now(),
|
|
29
|
+
ttlMs: number = PRESENCE_TTL_MS
|
|
30
|
+
): boolean {
|
|
31
|
+
return now - store().lastForegroundAt < ttlMs;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Record a presence heartbeat. `foreground` marks the viewer as actively watching (now);
|
|
36
|
+
* `background` marks them away immediately (so push resumes without waiting out the TTL).
|
|
37
|
+
*/
|
|
38
|
+
export function reportPresence(state: 'background' | 'foreground', now: number = Date.now()): void {
|
|
39
|
+
const s = store();
|
|
40
|
+
s.everReported = true;
|
|
41
|
+
s.lastForegroundAt = state === 'foreground' ? now : 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function store(): PresenceRecord {
|
|
45
|
+
const g = globalThis as Record<string, unknown>;
|
|
46
|
+
if (!g[KEY]) {
|
|
47
|
+
g[KEY] = { everReported: false, lastForegroundAt: 0 };
|
|
48
|
+
}
|
|
49
|
+
return g[KEY] as PresenceRecord;
|
|
50
|
+
}
|