@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,138 @@
|
|
|
1
|
+
// Hand-written autopilot types — union/record types not expressible in YAML.
|
|
2
|
+
|
|
3
|
+
/** A raw proposal from a single agent. */
|
|
4
|
+
export interface AgentProposal {
|
|
5
|
+
confidence: number;
|
|
6
|
+
text: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Autopilot module-level state (not a Svelte store — held in the autopilot module). */
|
|
10
|
+
export interface AutopilotState {
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
status: Record<string, AutopilotStatus>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Per-session autopilot lifecycle state. */
|
|
16
|
+
export type AutopilotStatus = 'error' | 'idle' | 'running';
|
|
17
|
+
|
|
18
|
+
/** Verdict from guardCommand() — whether a concrete command is safe to write to the PTY. */
|
|
19
|
+
export interface CommandVerdict {
|
|
20
|
+
/** The trimmed command (echoed back for convenience). */
|
|
21
|
+
command: string;
|
|
22
|
+
/** Human-readable reason for the verdict. */
|
|
23
|
+
reason: string;
|
|
24
|
+
/** True when the command passed all guards and may be injected. */
|
|
25
|
+
safe: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Result from mergeNextStepConsensus(). */
|
|
29
|
+
export interface ConsensusResult {
|
|
30
|
+
/** Number of agent lists passed in. */
|
|
31
|
+
agentCount: number;
|
|
32
|
+
/** Quorum threshold used. */
|
|
33
|
+
quorum: number;
|
|
34
|
+
/** Consensus (or tentative) next-step list, sorted by votes desc then confidence desc. */
|
|
35
|
+
steps: NextStep[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A record of one autonomous-loop decision, surfaced in the dashboard panel. */
|
|
39
|
+
export interface DriverAction {
|
|
40
|
+
/** ms timestamp of the action. */
|
|
41
|
+
at: number;
|
|
42
|
+
/** The command injected, or a short reason it was skipped / failed. */
|
|
43
|
+
detail: string;
|
|
44
|
+
/** What happened. */
|
|
45
|
+
kind: DriverActionKind;
|
|
46
|
+
/** The terminal acted on. */
|
|
47
|
+
terminalId: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Outcome of one driver evaluation. */
|
|
51
|
+
export type DriverActionKind = 'error' | 'injected' | 'skipped';
|
|
52
|
+
|
|
53
|
+
/** Decision from decideInjection() — the gate deciding whether to auto-act. */
|
|
54
|
+
export interface GateDecision {
|
|
55
|
+
/** True when all safety gates pass and the loop should produce + inject a command. */
|
|
56
|
+
act: boolean;
|
|
57
|
+
/** Human-readable reason (always set, for logging + the phone UI). */
|
|
58
|
+
reason: string;
|
|
59
|
+
/** The consensus step being acted on (present when act is true). */
|
|
60
|
+
step?: NextStep;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Tunable thresholds for the auto-inject safety gate. */
|
|
64
|
+
export interface InjectionPolicy {
|
|
65
|
+
/** Suppress injection within this many ms of observed human / output activity. */
|
|
66
|
+
humanGraceMs: number;
|
|
67
|
+
/** Minimum confidence of the top consensus step required to inject. */
|
|
68
|
+
injectConfidence: number;
|
|
69
|
+
/** Stop auto-injecting a terminal after this many consecutive actions without progress. */
|
|
70
|
+
maxAutoActions: number;
|
|
71
|
+
/** Minimum gap in ms between injections into the same terminal. */
|
|
72
|
+
minIntervalMs: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Per-terminal snapshot the driver passes to decideInjection(). */
|
|
76
|
+
export interface InjectionState {
|
|
77
|
+
/** Consecutive auto-injections without a human touch or successful tool completion. */
|
|
78
|
+
autoActionCount: number;
|
|
79
|
+
/** True only for terminals Shooter created (POST /api/terminals); external sessions are read-only. */
|
|
80
|
+
isManaged: boolean;
|
|
81
|
+
/** Normalized text of the last consensus step acted on (dedup guard). */
|
|
82
|
+
lastActedStep: null | string;
|
|
83
|
+
/** ms timestamp of the last observed human input / terminal output activity. */
|
|
84
|
+
lastActivityAt: number;
|
|
85
|
+
/** The most recent WireShooterEvent type seen for this terminal. */
|
|
86
|
+
lastEventType: string;
|
|
87
|
+
/** ms timestamp of the last command injection into this terminal. */
|
|
88
|
+
lastInjectedAt: number;
|
|
89
|
+
/** The terminal id. */
|
|
90
|
+
terminalId: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Options for mergeNextStepConsensus(). */
|
|
94
|
+
export interface MergeOptions {
|
|
95
|
+
/** Max steps taken from each agent list (default 3). */
|
|
96
|
+
k?: number;
|
|
97
|
+
/** Minimum vote count for a group to reach consensus (default 3). */
|
|
98
|
+
quorum?: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** A single proposed next step from one agent or the merged consensus. */
|
|
102
|
+
export interface NextStep {
|
|
103
|
+
/** Confidence score in [0, 1]. For consensus steps this is the mean across proposers. */
|
|
104
|
+
confidence: number;
|
|
105
|
+
/** Present and true when no group reached quorum; indicates low-certainty result. */
|
|
106
|
+
tentative?: boolean;
|
|
107
|
+
/** The original (highest-confidence) phrasing of the step. */
|
|
108
|
+
text: string;
|
|
109
|
+
/** Number of distinct agents that proposed this step (consensus only). */
|
|
110
|
+
votes?: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Additional per-session fields added by the autopilot engine.
|
|
115
|
+
* These are optional so that SessionState objects (which lack them by default)
|
|
116
|
+
* remain assignable; the engine writes them via Svelte 5's reactive proxy.
|
|
117
|
+
*/
|
|
118
|
+
export interface SessionAutopilotFields {
|
|
119
|
+
/** ISO 8601 timestamp of the last completed autopilot pipeline run. */
|
|
120
|
+
autopilotLastRun?: null | string;
|
|
121
|
+
/** Current autopilot pipeline status for this session. */
|
|
122
|
+
autopilotStatus?: AutopilotStatus;
|
|
123
|
+
/** Consensus next-step list from the last pipeline run. */
|
|
124
|
+
nextSteps?: NextStep[];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** A persisted summary record stored in session_summaries. */
|
|
128
|
+
export interface SessionSummaryRecord {
|
|
129
|
+
createdAt: string;
|
|
130
|
+
id: string;
|
|
131
|
+
/** JSON-serialised NextStep[] */
|
|
132
|
+
nextSteps: string;
|
|
133
|
+
projectName: null | string;
|
|
134
|
+
sessionId: null | string;
|
|
135
|
+
summary: string;
|
|
136
|
+
terminalId: null | string;
|
|
137
|
+
trigger: string;
|
|
138
|
+
}
|
package/src/lib/types/index.ts
CHANGED
|
@@ -5,5 +5,7 @@ import type { LayoutServerLoad } from './$types';
|
|
|
5
5
|
|
|
6
6
|
export const load: LayoutServerLoad = () => ({
|
|
7
7
|
aiProviders: getProviderAvailability(env),
|
|
8
|
+
litellmBaseUrl: env.LITELLM_BASE_URL ?? '',
|
|
9
|
+
litellmModel: env.LITELLM_MODEL ?? 'open-large',
|
|
8
10
|
neurolinkProvider: env.NEUROLINK_PROVIDER ?? '',
|
|
9
11
|
});
|
|
@@ -22,11 +22,26 @@
|
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
(window as unknown as Record<string, unknown>).__aiProviders = data.aiProviders;
|
|
25
|
+
|
|
26
|
+
// Ensure window.process.env exists minimally so env vars can be injected.
|
|
27
|
+
const win = window as unknown as Record<string, unknown>;
|
|
28
|
+
if (!win.process || typeof win.process !== 'object') {
|
|
29
|
+
win.process = { env: {} };
|
|
30
|
+
}
|
|
31
|
+
const proc = win.process as Record<string, unknown>;
|
|
32
|
+
if (!proc.env || typeof proc.env !== 'object') {
|
|
33
|
+
proc.env = {};
|
|
34
|
+
}
|
|
35
|
+
const procEnv = proc.env as Record<string, string>;
|
|
36
|
+
|
|
25
37
|
if (data.neurolinkProvider) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
procEnv.NEUROLINK_PROVIDER = data.neurolinkProvider;
|
|
39
|
+
}
|
|
40
|
+
if (data.litellmBaseUrl) {
|
|
41
|
+
procEnv.LITELLM_BASE_URL = data.litellmBaseUrl;
|
|
42
|
+
}
|
|
43
|
+
if (data.litellmModel) {
|
|
44
|
+
procEnv.LITELLM_MODEL = data.litellmModel;
|
|
30
45
|
}
|
|
31
46
|
});
|
|
32
47
|
|
package/src/routes/+page.svelte
CHANGED
|
@@ -12,7 +12,13 @@
|
|
|
12
12
|
isShooterConfig,
|
|
13
13
|
setCache,
|
|
14
14
|
} from '$lib/modules/client/common';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
AutopilotPanel,
|
|
17
|
+
connect,
|
|
18
|
+
DashboardView,
|
|
19
|
+
disconnect,
|
|
20
|
+
getCards,
|
|
21
|
+
} from '$lib/modules/client/dashboard';
|
|
16
22
|
import { Banner, Button, EmptyState, Icon, Pill, Shimmer } from '@juspay/svelte-ui-components';
|
|
17
23
|
import { onDestroy, onMount } from 'svelte';
|
|
18
24
|
|
|
@@ -203,6 +209,8 @@
|
|
|
203
209
|
<Button classes="btn-primary" onclick={navigateToConfig} text="Configure Settings" />
|
|
204
210
|
</EmptyState>
|
|
205
211
|
{:else}
|
|
212
|
+
<AutopilotPanel />
|
|
213
|
+
|
|
206
214
|
<!-- Dashboard section: active terminal sessions -->
|
|
207
215
|
{#if cards.length > 0}
|
|
208
216
|
<div class="dashboard-section">
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Control endpoint for the always-on server-side autopilot engine.
|
|
2
|
+
// GET returns the enabled flag; POST { enabled } toggles it.
|
|
3
|
+
//
|
|
4
|
+
// The engine runs in the server.ts module graph and exposes its control on
|
|
5
|
+
// globalThis.__shooter_autopilot. This route (bundled handler graph) reaches it
|
|
6
|
+
// there rather than importing the engine — importing it would start a second
|
|
7
|
+
// event subscriber. Falls back to the on-disk state file if the engine has not
|
|
8
|
+
// started yet.
|
|
9
|
+
|
|
10
|
+
import { validateAuth } from '$lib/modules/server/auth';
|
|
11
|
+
import { json } from '@sveltejs/kit';
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
13
|
+
import { homedir } from 'os';
|
|
14
|
+
import { join } from 'path';
|
|
15
|
+
|
|
16
|
+
import type { RequestHandler } from './$types';
|
|
17
|
+
|
|
18
|
+
const STATE_FILE = join(homedir(), '.shooter', 'autopilot.json');
|
|
19
|
+
|
|
20
|
+
function control(): undefined | { isEnabled: () => boolean; setEnabled: (v: boolean) => void } {
|
|
21
|
+
return (globalThis as Record<string, unknown>).__shooter_autopilot as
|
|
22
|
+
| undefined
|
|
23
|
+
| { isEnabled: () => boolean; setEnabled: (v: boolean) => void };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function readFileEnabled(): boolean {
|
|
27
|
+
try {
|
|
28
|
+
if (existsSync(STATE_FILE)) {
|
|
29
|
+
const parsed: unknown = JSON.parse(readFileSync(STATE_FILE, 'utf-8'));
|
|
30
|
+
return Boolean((parsed as { enabled?: unknown })?.enabled);
|
|
31
|
+
}
|
|
32
|
+
} catch {
|
|
33
|
+
// corrupt / unreadable
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const GET: RequestHandler = ({ request }) => {
|
|
39
|
+
const authError = validateAuth(request);
|
|
40
|
+
if (authError) {
|
|
41
|
+
return authError;
|
|
42
|
+
}
|
|
43
|
+
const ctrl = control();
|
|
44
|
+
return json({ enabled: ctrl ? ctrl.isEnabled() : readFileEnabled(), running: Boolean(ctrl) });
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const POST: RequestHandler = async ({ request }) => {
|
|
48
|
+
const authError = validateAuth(request);
|
|
49
|
+
if (authError) {
|
|
50
|
+
return authError;
|
|
51
|
+
}
|
|
52
|
+
let body: { enabled?: unknown };
|
|
53
|
+
try {
|
|
54
|
+
body = (await request.json()) as { enabled?: unknown };
|
|
55
|
+
} catch {
|
|
56
|
+
return json({ error: 'Invalid JSON body' }, { status: 400 });
|
|
57
|
+
}
|
|
58
|
+
if (typeof body.enabled !== 'boolean') {
|
|
59
|
+
return json({ error: 'enabled must be a boolean' }, { status: 400 });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const ctrl = control();
|
|
63
|
+
if (ctrl) {
|
|
64
|
+
ctrl.setEnabled(body.enabled);
|
|
65
|
+
} else {
|
|
66
|
+
try {
|
|
67
|
+
mkdirSync(join(homedir(), '.shooter'), { recursive: true });
|
|
68
|
+
writeFileSync(STATE_FILE, JSON.stringify({ enabled: body.enabled }), 'utf-8');
|
|
69
|
+
} catch {
|
|
70
|
+
// best-effort
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return json({ enabled: body.enabled, running: Boolean(ctrl) });
|
|
74
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Set/read the per-terminal autopilot GOAL that anchors the engine's context every cycle.
|
|
2
|
+
//
|
|
3
|
+
// POST { terminalId, goal } pins the goal; GET ?terminalId=… reads it back. Like
|
|
4
|
+
// /api/autopilot, this reaches the engine via globalThis.__shooter_autopilot rather than
|
|
5
|
+
// importing it (importing would start a second event subscriber). Goals live in-memory in the
|
|
6
|
+
// engine (no file fallback): if the engine is not running there is nothing to set, so we 503.
|
|
7
|
+
|
|
8
|
+
import { validateAuth } from '$lib/modules/server/auth';
|
|
9
|
+
import { json } from '@sveltejs/kit';
|
|
10
|
+
|
|
11
|
+
import type { RequestHandler } from './$types';
|
|
12
|
+
|
|
13
|
+
function control():
|
|
14
|
+
| undefined
|
|
15
|
+
| {
|
|
16
|
+
getGoal?: (terminalId: string) => string | undefined;
|
|
17
|
+
setGoal?: (terminalId: string, goal: string) => void;
|
|
18
|
+
} {
|
|
19
|
+
return (globalThis as Record<string, unknown>).__shooter_autopilot as
|
|
20
|
+
| undefined
|
|
21
|
+
| {
|
|
22
|
+
getGoal?: (terminalId: string) => string | undefined;
|
|
23
|
+
setGoal?: (terminalId: string, goal: string) => void;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const GET: RequestHandler = ({ request, url }) => {
|
|
28
|
+
const authError = validateAuth(request);
|
|
29
|
+
if (authError) {
|
|
30
|
+
return authError;
|
|
31
|
+
}
|
|
32
|
+
const terminalId = url.searchParams.get('terminalId') ?? '';
|
|
33
|
+
if (!terminalId) {
|
|
34
|
+
return json({ error: 'terminalId query param is required' }, { status: 400 });
|
|
35
|
+
}
|
|
36
|
+
const ctrl = control();
|
|
37
|
+
if (!ctrl?.getGoal) {
|
|
38
|
+
return json({ error: 'autopilot engine not running', running: false }, { status: 503 });
|
|
39
|
+
}
|
|
40
|
+
return json({ goal: ctrl.getGoal(terminalId) ?? null, running: true });
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const POST: RequestHandler = async ({ request }) => {
|
|
44
|
+
const authError = validateAuth(request);
|
|
45
|
+
if (authError) {
|
|
46
|
+
return authError;
|
|
47
|
+
}
|
|
48
|
+
let body: { goal?: unknown; terminalId?: unknown };
|
|
49
|
+
try {
|
|
50
|
+
body = (await request.json()) as { goal?: unknown; terminalId?: unknown };
|
|
51
|
+
} catch {
|
|
52
|
+
return json({ error: 'Invalid JSON body' }, { status: 400 });
|
|
53
|
+
}
|
|
54
|
+
if (typeof body.terminalId !== 'string' || body.terminalId.length === 0) {
|
|
55
|
+
return json({ error: 'terminalId must be a non-empty string' }, { status: 400 });
|
|
56
|
+
}
|
|
57
|
+
if (typeof body.goal !== 'string') {
|
|
58
|
+
return json({ error: 'goal must be a string' }, { status: 400 });
|
|
59
|
+
}
|
|
60
|
+
if (body.goal.length > 500) {
|
|
61
|
+
// The goal is prepended to EVERY engine LLM context, so an unbounded string would permanently
|
|
62
|
+
// bloat (and could dominate) the prompt. Cap it like the summaries route caps its fields.
|
|
63
|
+
return json({ error: 'goal must be 500 characters or fewer' }, { status: 400 });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const ctrl = control();
|
|
67
|
+
if (!ctrl?.setGoal) {
|
|
68
|
+
return json({ error: 'autopilot engine not running', running: false }, { status: 503 });
|
|
69
|
+
}
|
|
70
|
+
ctrl.setGoal(body.terminalId, body.goal);
|
|
71
|
+
return json({ goal: body.goal.trim() || null, running: true, terminalId: body.terminalId });
|
|
72
|
+
};
|
|
@@ -15,6 +15,7 @@ import type { RequestHandler } from './$types';
|
|
|
15
15
|
const ALLOWED_CLIENT_HEADERS: Record<string, Set<string>> = {
|
|
16
16
|
anthropic: new Set(['anthropic-beta', 'anthropic-version']),
|
|
17
17
|
'google-ai': new Set<string>([]),
|
|
18
|
+
litellm: new Set<string>([]),
|
|
18
19
|
mistral: new Set([]),
|
|
19
20
|
openai: new Set(['openai-organization', 'openai-project']),
|
|
20
21
|
};
|
|
@@ -63,15 +64,29 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
63
64
|
openai: 'https://api.openai.com/',
|
|
64
65
|
};
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
if (provider === 'litellm') {
|
|
68
|
+
// SSRF-safe: only forward to the operator-configured LiteLLM endpoint.
|
|
69
|
+
// Trim to align with getProviderAvailability semantics (whitespace-only = not configured).
|
|
70
|
+
const rawBase = env.LITELLM_BASE_URL?.trim();
|
|
71
|
+
if (!rawBase) {
|
|
72
|
+
return json({ error: 'LiteLLM is not configured on this server' }, { status: 403 });
|
|
73
|
+
}
|
|
74
|
+
const litellmPrefix = `${rawBase.replace(/\/+$/, '')}/`;
|
|
75
|
+
if (!url.startsWith(litellmPrefix)) {
|
|
76
|
+
return json({ error: `Provider "${provider}" or URL not allowed` }, { status: 403 });
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
const allowedPrefix = ALLOWED_PREFIXES[provider];
|
|
80
|
+
if (!allowedPrefix || !url.startsWith(allowedPrefix)) {
|
|
81
|
+
return json({ error: `Provider "${provider}" or URL not allowed` }, { status: 403 });
|
|
82
|
+
}
|
|
69
83
|
}
|
|
70
84
|
|
|
71
85
|
// Inject the server-side API key so the browser never sees it
|
|
72
86
|
const apiKeyEnv: Record<string, string> = {
|
|
73
87
|
anthropic: env.ANTHROPIC_API_KEY ?? '',
|
|
74
88
|
'google-ai': env.GOOGLE_AI_API_KEY ?? '',
|
|
89
|
+
litellm: env.LITELLM_API_KEY ?? '',
|
|
75
90
|
mistral: env.MISTRAL_API_KEY ?? '',
|
|
76
91
|
openai: env.OPENAI_API_KEY ?? '',
|
|
77
92
|
};
|
|
@@ -93,16 +108,32 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
93
108
|
...normalizedReqHeaders,
|
|
94
109
|
};
|
|
95
110
|
|
|
96
|
-
// Override / set auth header with server-side key
|
|
111
|
+
// Override / set auth header with server-side key.
|
|
112
|
+
// For Bearer-token providers, only inject the header when the key is non-empty
|
|
113
|
+
// to avoid sending a malformed `Authorization: Bearer ` to the upstream.
|
|
97
114
|
if (provider === 'anthropic') {
|
|
98
|
-
|
|
115
|
+
// Only inject the key header when non-empty — sending `x-api-key: ` (empty) is a malformed
|
|
116
|
+
// header that the upstream rejects with a confusing error instead of a clean 401.
|
|
117
|
+
if (apiKeyEnv.anthropic) {
|
|
118
|
+
forwardHeaders['x-api-key'] = apiKeyEnv.anthropic;
|
|
119
|
+
}
|
|
99
120
|
forwardHeaders['anthropic-version'] = forwardHeaders['anthropic-version'] ?? '2023-06-01';
|
|
100
121
|
} else if (provider === 'google-ai') {
|
|
101
|
-
|
|
122
|
+
if (apiKeyEnv['google-ai']) {
|
|
123
|
+
forwardHeaders['x-goog-api-key'] = apiKeyEnv['google-ai'];
|
|
124
|
+
}
|
|
102
125
|
} else if (provider === 'openai') {
|
|
103
|
-
|
|
126
|
+
if (apiKeyEnv.openai) {
|
|
127
|
+
forwardHeaders.Authorization = `Bearer ${apiKeyEnv.openai}`;
|
|
128
|
+
}
|
|
104
129
|
} else if (provider === 'mistral') {
|
|
105
|
-
|
|
130
|
+
if (apiKeyEnv.mistral) {
|
|
131
|
+
forwardHeaders.Authorization = `Bearer ${apiKeyEnv.mistral}`;
|
|
132
|
+
}
|
|
133
|
+
} else if (provider === 'litellm') {
|
|
134
|
+
if (apiKeyEnv.litellm) {
|
|
135
|
+
forwardHeaders.Authorization = `Bearer ${apiKeyEnv.litellm}`;
|
|
136
|
+
}
|
|
106
137
|
}
|
|
107
138
|
|
|
108
139
|
const controller = new AbortController();
|
|
@@ -173,13 +173,8 @@ function intelligentNotificationFilter(
|
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
reason: 'Stop hook completion notification - session finished',
|
|
180
|
-
send: true,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
176
|
+
// (Removed a dead `source === 'stop-hook'` branch: the notifier emits
|
|
177
|
+
// 'shooter-completion-detector', never 'stop-hook', and the default below already allows it.)
|
|
183
178
|
|
|
184
179
|
// Filter out only very specific spam patterns to be less restrictive
|
|
185
180
|
const spamPatterns = [
|
|
@@ -238,7 +233,15 @@ function isDuplicateNotification(
|
|
|
238
233
|
return false;
|
|
239
234
|
}
|
|
240
235
|
|
|
241
|
-
|
|
236
|
+
// Autopilot pushes include a stable dedupKey keyed on sessionId + top-step text
|
|
237
|
+
// (not the variable summary). Prefer it over the title|message|category key so
|
|
238
|
+
// two runs with the same top-step but different summary wording are correctly
|
|
239
|
+
// deduplicated.
|
|
240
|
+
const dataRecord = data as (NotificationData & { dedupKey?: string }) | undefined;
|
|
241
|
+
const key = dataRecord?.dedupKey
|
|
242
|
+
? dataRecord.dedupKey
|
|
243
|
+
: `${title}|${message}|${data?.category || 'unknown'}`;
|
|
244
|
+
|
|
242
245
|
const now = Date.now();
|
|
243
246
|
|
|
244
247
|
if (notificationCache.has(key)) {
|
|
@@ -255,15 +258,28 @@ function isDuplicateNotification(
|
|
|
255
258
|
}
|
|
256
259
|
}
|
|
257
260
|
|
|
258
|
-
//
|
|
259
|
-
//
|
|
261
|
+
// RESERVE the slot atomically (check-and-set): a second concurrent request with the same key now
|
|
262
|
+
// sees it as a duplicate before either has delivered, closing the TOCTOU window that let two
|
|
263
|
+
// identical pushes through. The delivery path RELEASES the slot (releaseNotification) if the send
|
|
264
|
+
// fails, so a legitimate retry is not blocked — this replaces the old record-only-on-success
|
|
265
|
+
// scheme while still avoiding cache poisoning on failure.
|
|
266
|
+
notificationCache.set(key, now);
|
|
260
267
|
return false;
|
|
261
268
|
}
|
|
262
269
|
|
|
263
|
-
|
|
270
|
+
function notificationKey(title: string, message: string, data?: NotificationData): string {
|
|
271
|
+
const dataRecord = data as (NotificationData & { dedupKey?: string }) | undefined;
|
|
272
|
+
return dataRecord?.dedupKey ?? `${title}|${message}|${data?.category || 'unknown'}`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Refresh a reserved dedup key after successful delivery (keeps the window measured from send). */
|
|
264
276
|
function recordNotification(title: string, message: string, data?: NotificationData): void {
|
|
265
|
-
|
|
266
|
-
|
|
277
|
+
notificationCache.set(notificationKey(title, message, data), Date.now());
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Release a reserved dedup key when delivery failed, so a legitimate retry is not blocked. */
|
|
281
|
+
function releaseNotification(title: string, message: string, data?: NotificationData): void {
|
|
282
|
+
notificationCache.delete(notificationKey(title, message, data));
|
|
267
283
|
}
|
|
268
284
|
|
|
269
285
|
// TODO(refactor): extract body parsing, filtering, and platform routing into
|
|
@@ -302,6 +318,11 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
302
318
|
const waitForResponse =
|
|
303
319
|
typeof body.waitForResponse === 'boolean' ? body.waitForResponse : false;
|
|
304
320
|
|
|
321
|
+
// forcePush: when true, send the push even if WS clients are connected.
|
|
322
|
+
// Used by the autopilot engine for high-signal notifications.
|
|
323
|
+
// Deduplication still applies — only the WS-client skip is bypassed.
|
|
324
|
+
const forcePush = typeof body.forcePush === 'boolean' ? body.forcePush : false;
|
|
325
|
+
|
|
305
326
|
// Dynamic-options fields (PR-2/PR-3). When the caller wants to drive
|
|
306
327
|
// a richer notification category — plan-mode approval, MCP
|
|
307
328
|
// elicitation, AskUserQuestion choices — these arrive in the top
|
|
@@ -375,7 +396,8 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
375
396
|
// (for bidirectional permission polling) without actually sending a push
|
|
376
397
|
// notification. This happens when WebSocket clients are connected and the
|
|
377
398
|
// events channel will broadcast the permission-requested event instead.
|
|
378
|
-
|
|
399
|
+
// forcePush overrides this so high-signal autopilot pushes still reach the device.
|
|
400
|
+
if (skipPush && !forcePush) {
|
|
379
401
|
if (waitForResponse) {
|
|
380
402
|
createPendingRequest(canonicalRequestId, {
|
|
381
403
|
options,
|
|
@@ -484,6 +506,7 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
484
506
|
});
|
|
485
507
|
} else {
|
|
486
508
|
console.error(`[notify] FCM delivery failed: ${fcmResult.error}`);
|
|
509
|
+
releaseNotification(title, message, data); // free the reserved dedup slot for a retry
|
|
487
510
|
|
|
488
511
|
addNotification(
|
|
489
512
|
buildNotificationRecord(
|
|
@@ -564,6 +587,7 @@ export const POST: RequestHandler = async ({ request }) => {
|
|
|
564
587
|
} catch (notificationError) {
|
|
565
588
|
const notifErrMsg = toErrorMessage(notificationError);
|
|
566
589
|
console.error(`[notify] APNs delivery failed: ${notifErrMsg}`);
|
|
590
|
+
releaseNotification(title, message, data); // free the reserved dedup slot for a retry
|
|
567
591
|
|
|
568
592
|
addNotification(
|
|
569
593
|
buildNotificationRecord(canonicalRequestId, title, message, 'failed', data, notifErrMsg)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Viewer-presence endpoint. The dashboard / phone posts a heartbeat so the autopilot
|
|
2
|
+
// engine can push only when the user is AWAY (not foregrounded) — see presence-store.ts.
|
|
3
|
+
// Distinct from raw WebSocket connection count, which the autonomous loop keeps open.
|
|
4
|
+
|
|
5
|
+
import { validateAuth } from '$lib/modules/server/auth';
|
|
6
|
+
import {
|
|
7
|
+
hasEverReported,
|
|
8
|
+
isViewerPresent,
|
|
9
|
+
reportPresence,
|
|
10
|
+
} from '$lib/modules/server/ws/presence-store';
|
|
11
|
+
import { json } from '@sveltejs/kit';
|
|
12
|
+
|
|
13
|
+
import type { RequestHandler } from './$types';
|
|
14
|
+
|
|
15
|
+
export const GET: RequestHandler = ({ request }) => {
|
|
16
|
+
const authError = validateAuth(request);
|
|
17
|
+
if (authError) {
|
|
18
|
+
return authError;
|
|
19
|
+
}
|
|
20
|
+
return json({ everReported: hasEverReported(), present: isViewerPresent() });
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const POST: RequestHandler = async ({ request }) => {
|
|
24
|
+
const authError = validateAuth(request);
|
|
25
|
+
if (authError) {
|
|
26
|
+
return authError;
|
|
27
|
+
}
|
|
28
|
+
let body: { state?: unknown };
|
|
29
|
+
try {
|
|
30
|
+
body = (await request.json()) as { state?: unknown };
|
|
31
|
+
} catch {
|
|
32
|
+
return json({ error: 'Invalid JSON body' }, { status: 400 });
|
|
33
|
+
}
|
|
34
|
+
if (body.state !== 'foreground' && body.state !== 'background') {
|
|
35
|
+
return json({ error: "state must be 'foreground' or 'background'" }, { status: 400 });
|
|
36
|
+
}
|
|
37
|
+
reportPresence(body.state);
|
|
38
|
+
return json({ everReported: hasEverReported(), present: isViewerPresent(), state: body.state });
|
|
39
|
+
};
|