abtars 0.1.0-alpha.2 → 0.1.0-alpha.21
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/CHANGELOG.md +64 -0
- package/bundle/_registry.generated-KQODGKTQ.js +36 -0
- package/bundle/{_registry.generated-M4WY2MMI.js.map → _registry.generated-KQODGKTQ.js.map} +1 -1
- package/bundle/abtars-browser.js +8 -7
- package/bundle/abtars-browser.js.map +1 -1
- package/bundle/abtars-cli.js +646 -73
- package/bundle/abtars-cli.js.map +4 -4
- package/bundle/abtars-restart.js +7 -6
- package/bundle/abtars-rss.js +2 -1
- package/bundle/abtars-rss.js.map +1 -1
- package/bundle/abtars-task.js +9 -8
- package/bundle/abtars-task.js.map +1 -1
- package/bundle/abtars.js +103 -96
- package/bundle/abtars.js.map +2 -2
- package/bundle/{agent-api-rate-limit-OQNFMXTZ.js → agent-api-rate-limit-7R5TX2F2.js} +7 -6
- package/bundle/{agent-api-rate-limit-OQNFMXTZ.js.map → agent-api-rate-limit-7R5TX2F2.js.map} +1 -1
- package/bundle/agent-registry-5M77ZOMV.js +19 -0
- package/bundle/agent-registry-VJMNIQ5W.js +19 -0
- package/bundle/{anthropic-adapter-2APTH3LA.js → anthropic-adapter-IBY3NPXW.js} +4 -3
- package/bundle/{anthropic-adapter-2APTH3LA.js.map → anthropic-adapter-IBY3NPXW.js.map} +1 -1
- package/bundle/{bridge-lock-transport-4AC2G5G6.js → bridge-lock-transport-HO545SBK.js} +9 -8
- package/bundle/browse-delivery-64GQIUHG.js +18 -0
- package/bundle/browser-EXR5OQGK.js +19 -0
- package/bundle/capability-HIE7UGFU.js +18 -0
- package/bundle/{chunk-BUUVFUPO.js → chunk-2BY6I4P5.js} +5 -4
- package/bundle/{chunk-BUUVFUPO.js.map → chunk-2BY6I4P5.js.map} +1 -1
- package/bundle/{chunk-Y6XAEX2Q.js → chunk-2F6XKG7Y.js} +15 -9
- package/bundle/chunk-2F6XKG7Y.js.map +7 -0
- package/bundle/{chunk-V76TVMCM.js → chunk-3MO2MDXJ.js} +5 -4
- package/bundle/{chunk-V76TVMCM.js.map → chunk-3MO2MDXJ.js.map} +1 -1
- package/bundle/{chunk-6UCRKRWR.js → chunk-6XX4OAAM.js} +22 -21
- package/bundle/chunk-6XX4OAAM.js.map +7 -0
- package/bundle/{chunk-XREWVCUO.js → chunk-7CHLS36W.js} +16 -148
- package/bundle/chunk-7CHLS36W.js.map +7 -0
- package/bundle/{chunk-NWDBD4PA.js → chunk-7K2YZTLD.js} +3 -2
- package/bundle/{chunk-JCJS4ZIB.js → chunk-AQVOAQQI.js} +5 -4
- package/bundle/{chunk-JCJS4ZIB.js.map → chunk-AQVOAQQI.js.map} +1 -1
- package/bundle/chunk-AUQD2PKM.js +136 -0
- package/bundle/chunk-AUQD2PKM.js.map +7 -0
- package/bundle/{chunk-YOCTDKKL.js → chunk-BYDUMHXT.js} +4 -3
- package/bundle/{chunk-YOCTDKKL.js.map → chunk-BYDUMHXT.js.map} +1 -1
- package/bundle/{chunk-RVE2N7FA.js → chunk-CELR236Q.js} +5 -4
- package/bundle/{chunk-RVE2N7FA.js.map → chunk-CELR236Q.js.map} +1 -1
- package/bundle/{chunk-2XU2X4OI.js → chunk-CUQA2AJT.js} +3 -2
- package/bundle/{chunk-2XU2X4OI.js.map → chunk-CUQA2AJT.js.map} +1 -1
- package/bundle/chunk-DMPR5MYT.js +183 -0
- package/bundle/chunk-DMPR5MYT.js.map +7 -0
- package/bundle/{chunk-BHMZ4RCC.js → chunk-DY3R7LDW.js} +55 -54
- package/bundle/{chunk-BHMZ4RCC.js.map → chunk-DY3R7LDW.js.map} +1 -1
- package/bundle/{chunk-AR6GO6YC.js → chunk-ELRAH7VL.js} +5 -4
- package/bundle/{chunk-AR6GO6YC.js.map → chunk-ELRAH7VL.js.map} +1 -1
- package/bundle/{chunk-FMWKEPM7.js → chunk-EX2SRTUE.js} +5 -4
- package/bundle/{chunk-FMWKEPM7.js.map → chunk-EX2SRTUE.js.map} +1 -1
- package/bundle/{chunk-JW6RU47G.js → chunk-FVQGP5YO.js} +8 -7
- package/bundle/{chunk-JW6RU47G.js.map → chunk-FVQGP5YO.js.map} +1 -1
- package/bundle/{chunk-GRNENTPA.js → chunk-G6IXMYIO.js} +4 -3
- package/bundle/{chunk-GRNENTPA.js.map → chunk-G6IXMYIO.js.map} +1 -1
- package/bundle/{chunk-6NR3OHEW.js → chunk-H2RZ4NEJ.js} +6 -5
- package/bundle/{chunk-6NR3OHEW.js.map → chunk-H2RZ4NEJ.js.map} +1 -1
- package/bundle/chunk-HVKJN3AG.js +189 -0
- package/bundle/chunk-HVKJN3AG.js.map +7 -0
- package/bundle/{chunk-265TPOPC.js → chunk-HXJRZWKA.js} +3 -2
- package/bundle/{chunk-265TPOPC.js.map → chunk-HXJRZWKA.js.map} +1 -1
- package/bundle/chunk-IU3RI5E4.js +645 -0
- package/bundle/chunk-IU3RI5E4.js.map +7 -0
- package/bundle/{chunk-GST5T3WZ.js → chunk-J5YIMCLT.js} +6 -5
- package/bundle/{chunk-GST5T3WZ.js.map → chunk-J5YIMCLT.js.map} +1 -1
- package/bundle/chunk-JAJ3DUQ2.js +30 -0
- package/bundle/{chunk-OP7BTAWY.js.map → chunk-JAJ3DUQ2.js.map} +1 -1
- package/bundle/chunk-JHF25OOG.js +645 -0
- package/bundle/chunk-JHF25OOG.js.map +7 -0
- package/bundle/{chunk-MPX525QO.js → chunk-JRG4EFMP.js} +5 -4
- package/bundle/{chunk-MPX525QO.js.map → chunk-JRG4EFMP.js.map} +1 -1
- package/bundle/{chunk-VVEDVGCR.js → chunk-JU3UBWLN.js} +17 -16
- package/bundle/{chunk-VVEDVGCR.js.map → chunk-JU3UBWLN.js.map} +1 -1
- package/bundle/{chunk-QBGBT5QS.js → chunk-JX3ZZU3O.js} +5 -4
- package/bundle/{chunk-QBGBT5QS.js.map → chunk-JX3ZZU3O.js.map} +1 -1
- package/bundle/{chunk-6SETMHNN.js → chunk-K7P74UNQ.js} +8 -7
- package/bundle/{chunk-6SETMHNN.js.map → chunk-K7P74UNQ.js.map} +1 -1
- package/bundle/{chunk-AZJIODTQ.js → chunk-KED3G7HS.js} +6 -5
- package/bundle/{chunk-AZJIODTQ.js.map → chunk-KED3G7HS.js.map} +1 -1
- package/bundle/chunk-KI2ROWAH.js +3707 -0
- package/bundle/chunk-KI2ROWAH.js.map +7 -0
- package/bundle/{chunk-UHRP745J.js → chunk-L7YHV5DL.js} +6 -5
- package/bundle/{chunk-UHRP745J.js.map → chunk-L7YHV5DL.js.map} +1 -1
- package/bundle/{chunk-BSSBCSCL.js → chunk-LD5BMLHG.js} +11 -10
- package/bundle/{chunk-BSSBCSCL.js.map → chunk-LD5BMLHG.js.map} +1 -1
- package/bundle/{chunk-2UPU3OW6.js → chunk-LYEAHE5V.js} +5 -4
- package/bundle/{chunk-2UPU3OW6.js.map → chunk-LYEAHE5V.js.map} +1 -1
- package/bundle/{chunk-3B7BBE4F.js → chunk-MCGEXAG5.js} +8 -7
- package/bundle/{chunk-3B7BBE4F.js.map → chunk-MCGEXAG5.js.map} +1 -1
- package/bundle/{chunk-X76UX47U.js → chunk-MJ6PHMOK.js} +4 -3
- package/bundle/{chunk-X76UX47U.js.map → chunk-MJ6PHMOK.js.map} +1 -1
- package/bundle/{chunk-LSPKJQCI.js → chunk-MV6CJFWR.js} +3 -2
- package/bundle/{chunk-LSPKJQCI.js.map → chunk-MV6CJFWR.js.map} +1 -1
- package/bundle/chunk-MZWMYN4O.js +17 -0
- package/bundle/{chunk-M6VBAPNT.js.map → chunk-MZWMYN4O.js.map} +1 -1
- package/bundle/{chunk-HX7Y7EYP.js → chunk-NIRYBWUW.js} +4 -3
- package/bundle/{chunk-HX7Y7EYP.js.map → chunk-NIRYBWUW.js.map} +1 -1
- package/bundle/{chunk-3E545J66.js → chunk-OW64RUE5.js} +3 -2
- package/bundle/{chunk-3E545J66.js.map → chunk-OW64RUE5.js.map} +1 -1
- package/bundle/chunk-P56PLAIC.js +126 -0
- package/bundle/chunk-P56PLAIC.js.map +7 -0
- package/bundle/{chunk-TZHIDLDS.js → chunk-P6PN34XD.js} +5 -4
- package/bundle/{chunk-TZHIDLDS.js.map → chunk-P6PN34XD.js.map} +1 -1
- package/bundle/{chunk-2UENBO6M.js → chunk-PF5UQ64X.js} +9 -8
- package/bundle/{chunk-2UENBO6M.js.map → chunk-PF5UQ64X.js.map} +1 -1
- package/bundle/{chunk-6CPN4IGS.js → chunk-PQ62LZNA.js} +9 -8
- package/bundle/{chunk-6CPN4IGS.js.map → chunk-PQ62LZNA.js.map} +1 -1
- package/bundle/{chunk-UCQ2WC3B.js → chunk-PQW5QBPY.js} +15 -8
- package/bundle/chunk-PQW5QBPY.js.map +7 -0
- package/bundle/{chunk-D2DCBO6M.js → chunk-R36WIOYX.js} +3 -2
- package/bundle/{chunk-D2DCBO6M.js.map → chunk-R36WIOYX.js.map} +1 -1
- package/bundle/chunk-RB3X66KM.js +386 -0
- package/bundle/chunk-RB3X66KM.js.map +7 -0
- package/bundle/{chunk-GUQVJC3U.js → chunk-RE3F3CFW.js} +7 -6
- package/bundle/{chunk-GUQVJC3U.js.map → chunk-RE3F3CFW.js.map} +1 -1
- package/bundle/chunk-RWUINZUQ.js +19 -0
- package/bundle/chunk-RWUINZUQ.js.map +7 -0
- package/bundle/{chunk-NT3OBORC.js → chunk-S54DBUZ4.js} +10 -9
- package/bundle/{chunk-NT3OBORC.js.map → chunk-S54DBUZ4.js.map} +1 -1
- package/bundle/{chunk-CWOHNFUV.js → chunk-SY67HM2Y.js} +3 -2
- package/bundle/{chunk-CWOHNFUV.js.map → chunk-SY67HM2Y.js.map} +1 -1
- package/bundle/{chunk-BQ2L4GMG.js → chunk-TBLYGCPQ.js} +4 -3
- package/bundle/{chunk-BQ2L4GMG.js.map → chunk-TBLYGCPQ.js.map} +1 -1
- package/bundle/chunk-TCBMBX3Z.js +183 -0
- package/bundle/chunk-TCBMBX3Z.js.map +7 -0
- package/bundle/chunk-TXRWQIQQ.js +3707 -0
- package/bundle/chunk-TXRWQIQQ.js.map +7 -0
- package/bundle/chunk-U34CSHFS.js +645 -0
- package/bundle/chunk-U34CSHFS.js.map +7 -0
- package/bundle/{chunk-CEVRHKJY.js → chunk-UDZIZB5F.js} +6 -5
- package/bundle/{chunk-CEVRHKJY.js.map → chunk-UDZIZB5F.js.map} +1 -1
- package/bundle/{chunk-W6FAL35D.js → chunk-VA5WKN3Z.js} +7 -6
- package/bundle/{chunk-W6FAL35D.js.map → chunk-VA5WKN3Z.js.map} +1 -1
- package/bundle/{chunk-X6TERNVJ.js → chunk-WX7GHGFX.js} +10 -9
- package/bundle/{chunk-X6TERNVJ.js.map → chunk-WX7GHGFX.js.map} +1 -1
- package/bundle/{chunk-PNEDC45Y.js → chunk-XETTJVEU.js} +4 -3
- package/bundle/{chunk-PNEDC45Y.js.map → chunk-XETTJVEU.js.map} +1 -1
- package/bundle/{chunk-PLCY3GFH.js → chunk-XLLSPBBT.js} +5 -4
- package/bundle/{chunk-PLCY3GFH.js.map → chunk-XLLSPBBT.js.map} +1 -1
- package/bundle/{chunk-ZXPXCDA6.js → chunk-XOCP5BMO.js} +6 -5
- package/bundle/{chunk-ZXPXCDA6.js.map → chunk-XOCP5BMO.js.map} +1 -1
- package/bundle/chunk-ZEY6YZAB.js +138 -0
- package/bundle/chunk-ZEY6YZAB.js.map +7 -0
- package/bundle/{chunk-MW6WDLU7.js → chunk-ZZR3JZHR.js} +10 -9
- package/bundle/{chunk-MW6WDLU7.js.map → chunk-ZZR3JZHR.js.map} +1 -1
- package/bundle/commands-AIL4XOIZ.js +33 -0
- package/bundle/commands-K77NVSXZ.js +32 -0
- package/bundle/commands-V6RSVC4Y.js +32 -0
- package/bundle/completion-buffer-S3LXDZG2.js +14 -0
- package/bundle/config-C6VHRJQ7.js +20 -0
- package/bundle/{config-show-ERTATR6E.js → config-show-ZTXX27FW.js} +4 -3
- package/bundle/{config-show-ERTATR6E.js.map → config-show-ZTXX27FW.js.map} +1 -1
- package/bundle/{context-HCEGZNDC.js → context-OCS7HLJP.js} +5 -4
- package/bundle/{context-HCEGZNDC.js.map → context-OCS7HLJP.js.map} +1 -1
- package/bundle/daemon-NPKYZ3CJ.js +292 -0
- package/bundle/daemon-NPKYZ3CJ.js.map +7 -0
- package/bundle/delegation-tools-PF7RD2RW.js +28 -0
- package/bundle/{deploy-lib-import-32ZFKHWP.js → deploy-lib-import-ODLDL2DB.js} +5 -4
- package/bundle/digital-signature-PNY4TR2W.js +14 -0
- package/bundle/{direct-api-transport-YR7SXXNN.js → direct-api-transport-EADHM67Z.js} +21 -20
- package/bundle/{direct-api-transport-YR7SXXNN.js.map → direct-api-transport-EADHM67Z.js.map} +1 -1
- package/bundle/direct-api-transport-SLJ2Z6NX.js +861 -0
- package/bundle/direct-api-transport-SLJ2Z6NX.js.map +7 -0
- package/bundle/{discord-adapter-YYWVMPPU.js → discord-adapter-FBJOJSTW.js} +25 -24
- package/bundle/{discord-adapter-YYWVMPPU.js.map → discord-adapter-FBJOJSTW.js.map} +1 -1
- package/bundle/discord-adapter-IJISVHUE.js +585 -0
- package/bundle/discord-adapter-IJISVHUE.js.map +7 -0
- package/bundle/discord-adapter-UYOCKRDF.js +586 -0
- package/bundle/discord-adapter-UYOCKRDF.js.map +7 -0
- package/bundle/{dist-MTMKARCP.js → dist-J3T4XVKX.js} +4 -3
- package/bundle/{dist-MTMKARCP.js.map → dist-J3T4XVKX.js.map} +1 -1
- package/bundle/{dns-wakeup-27M7D2MR.js → dns-wakeup-RYOCQ6GR.js} +6 -5
- package/bundle/{dns-wakeup-27M7D2MR.js.map → dns-wakeup-RYOCQ6GR.js.map} +1 -1
- package/bundle/{doctor-QNUSDY73.js → doctor-R54GZPKL.js} +10 -9
- package/bundle/{doctor-QNUSDY73.js.map → doctor-R54GZPKL.js.map} +1 -1
- package/bundle/{ensure-invariants-NMXNS476.js → ensure-invariants-K2ZUZ6NR.js} +7 -6
- package/bundle/{ensure-invariants-NMXNS476.js.map → ensure-invariants-K2ZUZ6NR.js.map} +1 -1
- package/bundle/ensure-invariants-KUXIW73S.js +50 -0
- package/bundle/ensure-invariants-KUXIW73S.js.map +7 -0
- package/bundle/env-schema-DGD6QWPA.js +20 -0
- package/bundle/{esm-DDP6NCZG.js → esm-PFOJARXA.js} +5 -4
- package/bundle/{esm-DDP6NCZG.js.map → esm-PFOJARXA.js.map} +1 -1
- package/bundle/{fallback-policy-L4QV2PEJ.js → fallback-policy-SR6ED5I3.js} +4 -3
- package/bundle/{fallback-policy-L4QV2PEJ.js.map → fallback-policy-SR6ED5I3.js.map} +1 -1
- package/bundle/{health-check-SPA7NT6N.js → health-check-RJ2SUJYL.js} +4 -3
- package/bundle/{health-check-SPA7NT6N.js.map → health-check-RJ2SUJYL.js.map} +1 -1
- package/bundle/hook-system-POI5VRIX.js +18 -0
- package/bundle/hotskills-6ECHLXTJ.js +13 -0
- package/bundle/install-24XR5FO5.js +13 -0
- package/bundle/install-AJ7VW76P.js +13 -0
- package/bundle/{install-log-IAPHYKD4.js → install-log-Q6RUHKWC.js} +4 -3
- package/bundle/{install-log-IAPHYKD4.js.map → install-log-Q6RUHKWC.js.map} +1 -1
- package/bundle/{install-manifest-SPQRUNXL.js → install-manifest-MCJCAYSR.js} +9 -7
- package/bundle/install-manifest-MCJCAYSR.js.map +7 -0
- package/bundle/install-manifest-ZETY4AFS.js +104 -0
- package/bundle/install-manifest-ZETY4AFS.js.map +7 -0
- package/bundle/{install-validate-PVLZXYLQ.js → install-validate-H74LUCE2.js} +4 -3
- package/bundle/{install-validate-PVLZXYLQ.js.map → install-validate-H74LUCE2.js.map} +1 -1
- package/bundle/{irc-adapter-OI5UZSQF.js → irc-adapter-RKRUSZXB.js} +7 -6
- package/bundle/{irc-adapter-OI5UZSQF.js.map → irc-adapter-RKRUSZXB.js.map} +1 -1
- package/bundle/{irc-config-55YO6EGB.js → irc-config-6VY67UPQ.js} +8 -7
- package/bundle/{irc-config-55YO6EGB.js.map → irc-config-6VY67UPQ.js.map} +1 -1
- package/bundle/{logs-ZNYXX5PA.js → logs-EK4HYRKR.js} +4 -3
- package/bundle/{logs-ZNYXX5PA.js.map → logs-EK4HYRKR.js.map} +1 -1
- package/bundle/{media-utils-XNNDTYFI.js → media-utils-QBY5WBF3.js} +8 -7
- package/bundle/{media-utils-XNNDTYFI.js.map → media-utils-QBY5WBF3.js.map} +1 -1
- package/bundle/message-pipeline-ANSMPK5O.js +34 -0
- package/bundle/message-pipeline-HXZMRGXZ.js +34 -0
- package/bundle/message-pipeline-XUUTGPFH.js +35 -0
- package/bundle/meta.json +1828 -1650
- package/bundle/model-health-registry-7ECZFCW4.js +12 -0
- package/bundle/model-health-registry-LDC76RPP.js +12 -0
- package/bundle/notification-OJ4YE4VG.js +14 -0
- package/bundle/{openrouter-credits-EDY7ETAU.js → openrouter-credits-L45SYKT3.js} +7 -6
- package/bundle/{openrouter-credits-EDY7ETAU.js.map → openrouter-credits-L45SYKT3.js.map} +1 -1
- package/bundle/{passwd-RRFV4CC5.js → passwd-QSHZJ2CG.js} +4 -3
- package/bundle/{passwd-RRFV4CC5.js.map → passwd-QSHZJ2CG.js.map} +1 -1
- package/bundle/paths-ZJYIDND2.js +18 -0
- package/bundle/{peer-client-52XYMNI7.js → peer-client-T44VI7NB.js} +13 -12
- package/bundle/{peer-client-52XYMNI7.js.map → peer-client-T44VI7NB.js.map} +1 -1
- package/bundle/peer-config-D5A4454H.js +17 -0
- package/bundle/{peer-sessions-EAXTNQ36.js → peer-sessions-MY2YVXHC.js} +4 -3
- package/bundle/{peer-sessions-EAXTNQ36.js.map → peer-sessions-MY2YVXHC.js.map} +1 -1
- package/bundle/{pending-callback-RIMQZ7FJ.js → pending-callback-6KLBSHLX.js} +4 -3
- package/bundle/{pending-callback-RIMQZ7FJ.js.map → pending-callback-6KLBSHLX.js.map} +1 -1
- package/bundle/phase-transport-43NP5XBK.js +23 -0
- package/bundle/phase-transport-GNUZI6EW.js +23 -0
- package/bundle/phase-transport-Q7K6V3VZ.js +23 -0
- package/bundle/phase-transport-SLJXIAY5.js +23 -0
- package/bundle/{responses-adapter-AAQTY3K4.js → responses-adapter-DAV2JUL7.js} +4 -3
- package/bundle/{responses-adapter-AAQTY3K4.js.map → responses-adapter-DAV2JUL7.js.map} +1 -1
- package/bundle/{restore-ZE3SEPSS.js → restore-ROJF22R2.js} +5 -4
- package/bundle/{restore-ZE3SEPSS.js.map → restore-ROJF22R2.js.map} +1 -1
- package/bundle/{self-healer-utils-DMUUXC47.js → self-healer-utils-7NFH22VJ.js} +7 -6
- package/bundle/{self-healer-utils-DMUUXC47.js.map → self-healer-utils-7NFH22VJ.js.map} +1 -1
- package/bundle/skill-stats-IPVKMWN3.js +23 -0
- package/bundle/sleep-4NVWZHVN.js +20 -0
- package/bundle/soul-bundle-7EYTEKFE.js +15 -0
- package/bundle/soul-loader-7FN7WDHM.js +18 -0
- package/bundle/soul-loader-K237NP4T.js +17 -0
- package/bundle/soul-loader-K237NP4T.js.map +7 -0
- package/bundle/soul-loader-UVJ6HZM3.js +17 -0
- package/bundle/soul-loader-UVJ6HZM3.js.map +7 -0
- package/bundle/src-Z3WR7SRT.js +9 -0
- package/bundle/src-Z3WR7SRT.js.map +7 -0
- package/bundle/{sse-parser-anthropic-P7CE2MH2.js → sse-parser-anthropic-H42TTLBD.js} +7 -6
- package/bundle/{sse-parser-anthropic-P7CE2MH2.js.map → sse-parser-anthropic-H42TTLBD.js.map} +1 -1
- package/bundle/{sse-parser-responses-EQQA5FWN.js → sse-parser-responses-WG2LY2ML.js} +7 -6
- package/bundle/{sse-parser-responses-EQQA5FWN.js.map → sse-parser-responses-WG2LY2ML.js.map} +1 -1
- package/bundle/{ssrf-guard-FZCBYIVW.js → ssrf-guard-E2KBBC5E.js} +7 -6
- package/bundle/{ssrf-guard-FZCBYIVW.js.map → ssrf-guard-E2KBBC5E.js.map} +1 -1
- package/bundle/{start-FH3GRMJ4.js → start-4IWBKLWO.js} +4 -3
- package/bundle/{start-FH3GRMJ4.js.map → start-4IWBKLWO.js.map} +1 -1
- package/bundle/{stream-single-WSG4D53C.js → stream-single-RFJNUTL6.js} +4 -3
- package/bundle/{stream-single-WSG4D53C.js.map → stream-single-RFJNUTL6.js.map} +1 -1
- package/bundle/stt-CF3CPFDC.js +15 -0
- package/bundle/stt-CF3CPFDC.js.map +7 -0
- package/bundle/subagent-runtime-P7GCFBM3.js +13 -0
- package/bundle/subagent-runtime-P7GCFBM3.js.map +7 -0
- package/bundle/subagent-runtime-USNPO4WF.js +13 -0
- package/bundle/subagent-runtime-USNPO4WF.js.map +7 -0
- package/bundle/subagent-runtime-XS2ZXYOZ.js +13 -0
- package/bundle/subagent-runtime-XS2ZXYOZ.js.map +7 -0
- package/bundle/{system-message-T5R3EYYN.js → system-message-TALP6GP2.js} +6 -5
- package/bundle/{system-message-T5R3EYYN.js.map → system-message-TALP6GP2.js.map} +1 -1
- package/bundle/{system-status-KQ6KHFJ6.js → system-status-2CR5OUDY.js} +10 -9
- package/bundle/{system-status-KQ6KHFJ6.js.map → system-status-2CR5OUDY.js.map} +1 -1
- package/bundle/task-store-KIBFZL5A.js +23 -0
- package/bundle/task-store-KIBFZL5A.js.map +7 -0
- package/bundle/{telegram-adapter-2V3XUMT5.js → telegram-adapter-ISQRW7PN.js} +34 -33
- package/bundle/{telegram-adapter-2V3XUMT5.js.map → telegram-adapter-ISQRW7PN.js.map} +1 -1
- package/bundle/telegram-adapter-QCD7AG5D.js +1062 -0
- package/bundle/telegram-adapter-QCD7AG5D.js.map +7 -0
- package/bundle/telegram-adapter-Y3PVA25S.js +1061 -0
- package/bundle/telegram-adapter-Y3PVA25S.js.map +7 -0
- package/bundle/telegram-adapter-YAXSK2RT.js +1062 -0
- package/bundle/telegram-adapter-YAXSK2RT.js.map +7 -0
- package/bundle/tool-registry-DFCCGZCB.js +39 -0
- package/bundle/tool-registry-DFCCGZCB.js.map +7 -0
- package/bundle/tool-sandbox-OZMXJZLQ.js +21 -0
- package/bundle/tool-sandbox-OZMXJZLQ.js.map +7 -0
- package/bundle/{transport-config-YLXU33RO.js → transport-config-ANPS2RYT.js} +11 -10
- package/bundle/transport-config-ANPS2RYT.js.map +7 -0
- package/bundle/update-check-O5MS6B3L.js +13 -0
- package/bundle/update-check-O5MS6B3L.js.map +7 -0
- package/bundle/usage-tracker-S4Z2G2K5.js +18 -0
- package/bundle/usage-tracker-S4Z2G2K5.js.map +7 -0
- package/bundle/user-registry-GTAJIW6E.js +17 -0
- package/bundle/user-registry-GTAJIW6E.js.map +7 -0
- package/config/.env +2 -0
- package/config/auto-fix.json +14 -0
- package/config/irc.json.example +30 -0
- package/config/models.json.example +229 -0
- package/config/peers.json.example +12 -0
- package/config/schemas/irc.schema.json +42 -0
- package/config/schemas/models.schema.json +17 -0
- package/config/schemas/peers.schema.json +35 -0
- package/config/schemas/transport.schema.json +35 -0
- package/config/schemas/users.schema.json +22 -0
- package/config/transport.default.json +30 -0
- package/config/transport.json.example +102 -0
- package/config/users.json.example +11 -0
- package/install-manifest.json +148 -0
- package/package.json +4 -2
- package/scripts/abtars-daemon.service +1 -0
- package/scripts/watchdog.sh +1 -1
- package/bundle/_registry.generated-M4WY2MMI.js +0 -35
- package/bundle/agent-registry-LT4JNQH6.js +0 -18
- package/bundle/browse-delivery-JXBY36GK.js +0 -17
- package/bundle/browser-ELNDVPLC.js +0 -18
- package/bundle/capability-CIL3G4FI.js +0 -17
- package/bundle/chunk-5R2ANXQ7.js +0 -510
- package/bundle/chunk-5R2ANXQ7.js.map +0 -7
- package/bundle/chunk-6UCRKRWR.js.map +0 -7
- package/bundle/chunk-M6VBAPNT.js +0 -16
- package/bundle/chunk-OP7BTAWY.js +0 -29
- package/bundle/chunk-UCQ2WC3B.js.map +0 -7
- package/bundle/chunk-XREWVCUO.js.map +0 -7
- package/bundle/chunk-Y6XAEX2Q.js.map +0 -7
- package/bundle/commands-BHVUOU3V.js +0 -31
- package/bundle/completion-buffer-P253ONKF.js +0 -13
- package/bundle/config-RGSDAPZN.js +0 -19
- package/bundle/delegation-tools-GYTS2D6A.js +0 -27
- package/bundle/digital-signature-OFCGSHWO.js +0 -13
- package/bundle/env-schema-2KBHBDGN.js +0 -19
- package/bundle/hook-system-6Q5YTR53.js +0 -17
- package/bundle/hotskills-K7BM4YLB.js +0 -12
- package/bundle/install-6HRZVKUM.js +0 -15
- package/bundle/install-manifest-SPQRUNXL.js.map +0 -7
- package/bundle/message-pipeline-LLH5SYMO.js +0 -33
- package/bundle/model-health-registry-35LQNVQR.js +0 -11
- package/bundle/notification-Y5S5MMLV.js +0 -13
- package/bundle/paths-G33RZWZ7.js +0 -17
- package/bundle/peer-config-VK6EDLN5.js +0 -16
- package/bundle/phase-transport-KYERDL2O.js +0 -22
- package/bundle/skill-stats-LLEXEXLR.js +0 -22
- package/bundle/sleep-OYIUOVQD.js +0 -19
- package/bundle/soul-loader-54WCVNLJ.js +0 -16
- package/bundle/src-JL4PVO23.js +0 -8
- package/bundle/stt-2UH3RITX.js +0 -14
- package/bundle/subagent-runtime-LE2ZXH3G.js +0 -12
- package/bundle/task-store-K7CQDEPI.js +0 -22
- package/bundle/tool-registry-MU3OX4UI.js +0 -38
- package/bundle/tool-sandbox-VYOK4ZOA.js +0 -20
- package/bundle/update-QCW5LXRN.js +0 -13
- package/bundle/update-check-27KZSAP6.js +0 -12
- package/bundle/usage-tracker-OVVEVMOY.js +0 -17
- package/bundle/user-registry-D4SD73UV.js +0 -16
- /package/bundle/{agent-registry-LT4JNQH6.js.map → agent-registry-5M77ZOMV.js.map} +0 -0
- /package/bundle/{bridge-lock-transport-4AC2G5G6.js.map → agent-registry-VJMNIQ5W.js.map} +0 -0
- /package/bundle/{browse-delivery-JXBY36GK.js.map → bridge-lock-transport-HO545SBK.js.map} +0 -0
- /package/bundle/{browser-ELNDVPLC.js.map → browse-delivery-64GQIUHG.js.map} +0 -0
- /package/bundle/{capability-CIL3G4FI.js.map → browser-EXR5OQGK.js.map} +0 -0
- /package/bundle/{chunk-NWDBD4PA.js.map → capability-HIE7UGFU.js.map} +0 -0
- /package/bundle/{commands-BHVUOU3V.js.map → chunk-7K2YZTLD.js.map} +0 -0
- /package/bundle/{completion-buffer-P253ONKF.js.map → commands-AIL4XOIZ.js.map} +0 -0
- /package/bundle/{config-RGSDAPZN.js.map → commands-K77NVSXZ.js.map} +0 -0
- /package/bundle/{delegation-tools-GYTS2D6A.js.map → commands-V6RSVC4Y.js.map} +0 -0
- /package/bundle/{deploy-lib-import-32ZFKHWP.js.map → completion-buffer-S3LXDZG2.js.map} +0 -0
- /package/bundle/{digital-signature-OFCGSHWO.js.map → config-C6VHRJQ7.js.map} +0 -0
- /package/bundle/{env-schema-2KBHBDGN.js.map → delegation-tools-PF7RD2RW.js.map} +0 -0
- /package/bundle/{hook-system-6Q5YTR53.js.map → deploy-lib-import-ODLDL2DB.js.map} +0 -0
- /package/bundle/{hotskills-K7BM4YLB.js.map → digital-signature-PNY4TR2W.js.map} +0 -0
- /package/bundle/{install-6HRZVKUM.js.map → env-schema-DGD6QWPA.js.map} +0 -0
- /package/bundle/{message-pipeline-LLH5SYMO.js.map → hook-system-POI5VRIX.js.map} +0 -0
- /package/bundle/{model-health-registry-35LQNVQR.js.map → hotskills-6ECHLXTJ.js.map} +0 -0
- /package/bundle/{notification-Y5S5MMLV.js.map → install-24XR5FO5.js.map} +0 -0
- /package/bundle/{paths-G33RZWZ7.js.map → install-AJ7VW76P.js.map} +0 -0
- /package/bundle/{peer-config-VK6EDLN5.js.map → message-pipeline-ANSMPK5O.js.map} +0 -0
- /package/bundle/{phase-transport-KYERDL2O.js.map → message-pipeline-HXZMRGXZ.js.map} +0 -0
- /package/bundle/{skill-stats-LLEXEXLR.js.map → message-pipeline-XUUTGPFH.js.map} +0 -0
- /package/bundle/{sleep-OYIUOVQD.js.map → model-health-registry-7ECZFCW4.js.map} +0 -0
- /package/bundle/{soul-loader-54WCVNLJ.js.map → model-health-registry-LDC76RPP.js.map} +0 -0
- /package/bundle/{src-JL4PVO23.js.map → notification-OJ4YE4VG.js.map} +0 -0
- /package/bundle/{stt-2UH3RITX.js.map → paths-ZJYIDND2.js.map} +0 -0
- /package/bundle/{subagent-runtime-LE2ZXH3G.js.map → peer-config-D5A4454H.js.map} +0 -0
- /package/bundle/{task-store-K7CQDEPI.js.map → phase-transport-43NP5XBK.js.map} +0 -0
- /package/bundle/{tool-registry-MU3OX4UI.js.map → phase-transport-GNUZI6EW.js.map} +0 -0
- /package/bundle/{tool-sandbox-VYOK4ZOA.js.map → phase-transport-Q7K6V3VZ.js.map} +0 -0
- /package/bundle/{transport-config-YLXU33RO.js.map → phase-transport-SLJXIAY5.js.map} +0 -0
- /package/bundle/{update-QCW5LXRN.js.map → skill-stats-IPVKMWN3.js.map} +0 -0
- /package/bundle/{update-check-27KZSAP6.js.map → sleep-4NVWZHVN.js.map} +0 -0
- /package/bundle/{usage-tracker-OVVEVMOY.js.map → soul-bundle-7EYTEKFE.js.map} +0 -0
- /package/bundle/{user-registry-D4SD73UV.js.map → soul-loader-7FN7WDHM.js.map} +0 -0
|
@@ -0,0 +1,3707 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __bundleCreateRequire } from 'node:module'; import { fileURLToPath as __bundleFileURLToPath } from 'node:url'; import { dirname as __bundleDirname } from 'node:path'; const require = __bundleCreateRequire(import.meta.url); const __chunk_filename = __bundleFileURLToPath(import.meta.url); const __chunk_dirname = __bundleDirname(__chunk_filename);
|
|
3
|
+
import {
|
|
4
|
+
require_ws
|
|
5
|
+
} from "./chunk-NIRYBWUW.js";
|
|
6
|
+
import {
|
|
7
|
+
transcribeAudio
|
|
8
|
+
} from "./chunk-3MO2MDXJ.js";
|
|
9
|
+
import {
|
|
10
|
+
ModelNotFoundError
|
|
11
|
+
} from "./chunk-WX7GHGFX.js";
|
|
12
|
+
import {
|
|
13
|
+
loadMinimalSoul,
|
|
14
|
+
loadSoulBundle
|
|
15
|
+
} from "./chunk-AUQD2PKM.js";
|
|
16
|
+
import {
|
|
17
|
+
loadUsers
|
|
18
|
+
} from "./chunk-J5YIMCLT.js";
|
|
19
|
+
import {
|
|
20
|
+
abmind
|
|
21
|
+
} from "./chunk-JAJ3DUQ2.js";
|
|
22
|
+
import {
|
|
23
|
+
fire,
|
|
24
|
+
hasHooks
|
|
25
|
+
} from "./chunk-G6IXMYIO.js";
|
|
26
|
+
import {
|
|
27
|
+
readAndClearRestartReason,
|
|
28
|
+
readBridgeLockField,
|
|
29
|
+
updateBridgeLockField,
|
|
30
|
+
writeForceSleep,
|
|
31
|
+
writeSleepStatus
|
|
32
|
+
} from "./chunk-UDZIZB5F.js";
|
|
33
|
+
import {
|
|
34
|
+
readEntries
|
|
35
|
+
} from "./chunk-XLLSPBBT.js";
|
|
36
|
+
import {
|
|
37
|
+
localTime
|
|
38
|
+
} from "./chunk-MZWMYN4O.js";
|
|
39
|
+
import {
|
|
40
|
+
init_log_and_swallow,
|
|
41
|
+
logAndSwallow
|
|
42
|
+
} from "./chunk-EX2SRTUE.js";
|
|
43
|
+
import {
|
|
44
|
+
getEnv,
|
|
45
|
+
init_env_schema
|
|
46
|
+
} from "./chunk-AQVOAQQI.js";
|
|
47
|
+
import {
|
|
48
|
+
init_logger,
|
|
49
|
+
logDebug,
|
|
50
|
+
logError,
|
|
51
|
+
logInfo,
|
|
52
|
+
logTrace,
|
|
53
|
+
logWarn
|
|
54
|
+
} from "./chunk-2BY6I4P5.js";
|
|
55
|
+
import {
|
|
56
|
+
abtarsHome,
|
|
57
|
+
init_paths
|
|
58
|
+
} from "./chunk-MJ6PHMOK.js";
|
|
59
|
+
import {
|
|
60
|
+
__commonJS,
|
|
61
|
+
__require,
|
|
62
|
+
__toESM
|
|
63
|
+
} from "./chunk-7K2YZTLD.js";
|
|
64
|
+
|
|
65
|
+
// node_modules/@andresaya/edge-tts/dist/config/constants.js
|
|
66
|
+
var require_constants = __commonJS({
|
|
67
|
+
"node_modules/@andresaya/edge-tts/dist/config/constants.js"(exports) {
|
|
68
|
+
"use strict";
|
|
69
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
70
|
+
exports.Constants = void 0;
|
|
71
|
+
exports.Constants = {
|
|
72
|
+
TRUSTED_CLIENT_TOKEN: "6A5AA1D4EAFF4E9FB37E23D68491D6F4",
|
|
73
|
+
BASE_URL: "https://speech.platform.bing.com/consumer/speech/synthesize/readaloud",
|
|
74
|
+
WSS_URL: "wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1",
|
|
75
|
+
VOICES_URL: "https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list",
|
|
76
|
+
CHROMIUM_FULL_VERSION: "143.0.3650.75",
|
|
77
|
+
CHROMIUM_MAJOR_VERSION: "143",
|
|
78
|
+
VERSION_MS_GEC: "1-143.0.3650",
|
|
79
|
+
token32() {
|
|
80
|
+
const bytes = new Uint8Array(16);
|
|
81
|
+
crypto.getRandomValues(bytes);
|
|
82
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
83
|
+
},
|
|
84
|
+
getBaseHeaders() {
|
|
85
|
+
return {
|
|
86
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0",
|
|
87
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
88
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
89
|
+
"Cookie": "MUID=" + this.token32()
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
WSS_HEADERS: {
|
|
93
|
+
"Pragma": "no-cache",
|
|
94
|
+
"Cache-Control": "no-cache",
|
|
95
|
+
"Origin": "chrome-extension://jdiccldimpdaibmpdkjnbmckianbfold",
|
|
96
|
+
"Sec-WebSocket-Protocol": "synthesize",
|
|
97
|
+
"Sec-WebSocket-Version": "13",
|
|
98
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.3650.75 Safari/537.36 Edg/143.0.3650.75"
|
|
99
|
+
},
|
|
100
|
+
VOICE_HEADERS: {
|
|
101
|
+
"Sec-CH-UA": '" Not;A Brand";v="99", "Microsoft Edge";v="143", "Chromium";v="143"',
|
|
102
|
+
"Sec-CH-UA-Mobile": "?0",
|
|
103
|
+
"Accept": "*/*",
|
|
104
|
+
"Sec-Fetch-Site": "none",
|
|
105
|
+
"Sec-Fetch-Mode": "cors",
|
|
106
|
+
"Sec-Fetch-Dest": "empty"
|
|
107
|
+
},
|
|
108
|
+
// https://learn.microsoft.com/en-us/azure/ai-services/speech-service/rest-text-to-speech?tabs=nonstreaming
|
|
109
|
+
OUTPUT_FORMAT: {
|
|
110
|
+
"AUDIO_24KHZ_48KBITRATE_MONO_MP3": "audio-24khz-48kbitrate-mono-mp3",
|
|
111
|
+
"AUDIO_24KHZ_96KBITRATE_MONO_MP3": "audio-24khz-96kbitrate-mono-mp3",
|
|
112
|
+
"WEBM_24KHZ_16BIT_MONO_OPUS": "webm-24khz-16bit-mono-opus"
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// node_modules/@andresaya/edge-tts/dist/services/EdgeTTS.js
|
|
119
|
+
var require_EdgeTTS = __commonJS({
|
|
120
|
+
"node_modules/@andresaya/edge-tts/dist/services/EdgeTTS.js"(exports) {
|
|
121
|
+
"use strict";
|
|
122
|
+
var __importDefault = exports && exports.__importDefault || function(mod) {
|
|
123
|
+
return mod && mod.__esModule ? mod : { "default": mod };
|
|
124
|
+
};
|
|
125
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
126
|
+
exports.EdgeTTS = void 0;
|
|
127
|
+
var ws_1 = __importDefault(require_ws());
|
|
128
|
+
var constants_1 = require_constants();
|
|
129
|
+
var promises_1 = __require("fs/promises");
|
|
130
|
+
var buffer_1 = __require("buffer");
|
|
131
|
+
var https_1 = __importDefault(__require("https"));
|
|
132
|
+
function ensureBuffer(data) {
|
|
133
|
+
if (buffer_1.Buffer.isBuffer(data)) {
|
|
134
|
+
return data;
|
|
135
|
+
}
|
|
136
|
+
if (data instanceof ArrayBuffer) {
|
|
137
|
+
return buffer_1.Buffer.from(data);
|
|
138
|
+
}
|
|
139
|
+
if (Array.isArray(data)) {
|
|
140
|
+
return buffer_1.Buffer.concat(data);
|
|
141
|
+
}
|
|
142
|
+
if (typeof data === "string") {
|
|
143
|
+
return buffer_1.Buffer.from(data, "utf-8");
|
|
144
|
+
}
|
|
145
|
+
throw new Error(`Unsupported RawData type: ${typeof data}`);
|
|
146
|
+
}
|
|
147
|
+
var EdgeTTS2 = class {
|
|
148
|
+
audio_stream = [];
|
|
149
|
+
audio_format = "mp3";
|
|
150
|
+
output_format = "audio-24khz-48kbitrate-mono-mp3";
|
|
151
|
+
word_boundaries = [];
|
|
152
|
+
ws;
|
|
153
|
+
async normalizeVoices(data) {
|
|
154
|
+
const out = [];
|
|
155
|
+
for (const v of data || []) {
|
|
156
|
+
const short = v?.ShortName || "";
|
|
157
|
+
const locale = v?.Locale || "";
|
|
158
|
+
let base = short.replace(/^[a-z]{2}-[A-Z]{2}-/, "");
|
|
159
|
+
base = base.replace(/NeuralHD$/, "").replace(/Neural$/, "").trim();
|
|
160
|
+
const mix = `${v?.Name || ""} ${short}`;
|
|
161
|
+
const voiceType = v?.VoiceType || (/NeuralHD/i.test(mix) ? "NeuralHD" : "Neural");
|
|
162
|
+
const localeName = v?.LocaleName || (locale || null);
|
|
163
|
+
let display = v?.DisplayName || v?.FriendlyName || base || short;
|
|
164
|
+
display = display.replace(/^Microsoft\s+/i, "");
|
|
165
|
+
display = display.split(" - ")[0].trim();
|
|
166
|
+
display = display.replace(/\s*Online\s*\(Natural\)\s*/i, " ");
|
|
167
|
+
display = display.replace(/\s*Online\s*/i, " ");
|
|
168
|
+
display = display.replace(/\s+/g, " ").trim();
|
|
169
|
+
const tag = v?.VoiceTag && typeof v.VoiceTag === "object" ? v.VoiceTag : {};
|
|
170
|
+
const tailored = Array.isArray(tag.TailoredScenarios) ? tag.TailoredScenarios : Array.isArray(tag.ContentCategories) ? tag.ContentCategories : [];
|
|
171
|
+
const personalities = Array.isArray(tag.VoicePersonalities) ? tag.VoicePersonalities : [];
|
|
172
|
+
out.push({
|
|
173
|
+
Name: short || (v?.Name || ""),
|
|
174
|
+
DisplayName: display,
|
|
175
|
+
LocalName: display,
|
|
176
|
+
ShortName: short || (v?.Name || ""),
|
|
177
|
+
Gender: v?.Gender ?? null,
|
|
178
|
+
Locale: locale || null,
|
|
179
|
+
LocaleName: localeName,
|
|
180
|
+
SecondaryLocaleList: Array.isArray(v?.SecondaryLocaleList) ? v.SecondaryLocaleList : [],
|
|
181
|
+
VoiceType: voiceType,
|
|
182
|
+
VoiceTag: {
|
|
183
|
+
TailoredScenarios: tailored,
|
|
184
|
+
VoicePersonalities: personalities
|
|
185
|
+
},
|
|
186
|
+
FriendlyName: `${display} (${voiceType}) - ${localeName}`
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
async getVoices() {
|
|
192
|
+
const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
|
|
193
|
+
const httpsAgent = new https_1.default.Agent({ rejectUnauthorized: false });
|
|
194
|
+
const url = `${constants_1.Constants.VOICES_URL}?TrustedClientToken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${constants_1.Constants.VERSION_MS_GEC}`;
|
|
195
|
+
const headers = {
|
|
196
|
+
...constants_1.Constants.getBaseHeaders(),
|
|
197
|
+
"Accept-Encoding": "identity"
|
|
198
|
+
// evita gzip/br/zstd
|
|
199
|
+
};
|
|
200
|
+
const voicesRaw = await new Promise((resolve, reject) => {
|
|
201
|
+
const req = https_1.default.request(url, { method: "GET", headers, agent: httpsAgent }, (res) => {
|
|
202
|
+
const chunks = [];
|
|
203
|
+
res.on("data", (chunk) => chunks.push(buffer_1.Buffer.isBuffer(chunk) ? chunk : buffer_1.Buffer.from(chunk)));
|
|
204
|
+
res.on("error", reject);
|
|
205
|
+
res.on("end", () => {
|
|
206
|
+
const body = buffer_1.Buffer.concat(chunks).toString("utf8");
|
|
207
|
+
try {
|
|
208
|
+
const parsed = JSON.parse(body);
|
|
209
|
+
const voices = Array.isArray(parsed) ? parsed : parsed.voices || parsed.Voices || [];
|
|
210
|
+
resolve(Array.isArray(voices) ? voices : []);
|
|
211
|
+
} catch (e) {
|
|
212
|
+
reject(new Error("JSON inv\xE1lido: " + (e?.message || String(e))));
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
req.on("error", reject);
|
|
217
|
+
req.end();
|
|
218
|
+
});
|
|
219
|
+
return this.normalizeVoices(voicesRaw);
|
|
220
|
+
}
|
|
221
|
+
async getVoicesByLanguage(locale) {
|
|
222
|
+
const voices = await this.getVoices();
|
|
223
|
+
return voices.filter((voice) => voice.Locale.startsWith(locale));
|
|
224
|
+
}
|
|
225
|
+
async getVoicesByGender(gender) {
|
|
226
|
+
const voices = await this.getVoices();
|
|
227
|
+
return voices.filter((voice) => voice.Gender === gender);
|
|
228
|
+
}
|
|
229
|
+
generateUUID() {
|
|
230
|
+
return "xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
|
|
231
|
+
const r = Math.random() * 16 | 0;
|
|
232
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
233
|
+
return v.toString(16);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
validatePitch(pitch) {
|
|
237
|
+
if (typeof pitch === "number") {
|
|
238
|
+
return pitch >= 0 ? `+${pitch}Hz` : `${pitch}Hz`;
|
|
239
|
+
}
|
|
240
|
+
if (!/^[+-]?\d{1,3}(?:\.\d+)?Hz$/.test(pitch)) {
|
|
241
|
+
throw new Error("Invalid pitch format. Expected format: '-100Hz to +100Hz' or a number.");
|
|
242
|
+
}
|
|
243
|
+
return pitch;
|
|
244
|
+
}
|
|
245
|
+
validateRate(rate) {
|
|
246
|
+
let rateValue;
|
|
247
|
+
if (typeof rate === "string") {
|
|
248
|
+
rateValue = parseFloat(rate.replace("%", ""));
|
|
249
|
+
if (isNaN(rateValue))
|
|
250
|
+
throw new Error("Invalid rate format.");
|
|
251
|
+
} else {
|
|
252
|
+
rateValue = rate;
|
|
253
|
+
}
|
|
254
|
+
if (rateValue >= 0) {
|
|
255
|
+
return `+${rateValue}%`;
|
|
256
|
+
}
|
|
257
|
+
return `${rateValue}%`;
|
|
258
|
+
}
|
|
259
|
+
validateVolume(volume) {
|
|
260
|
+
let volumeValue;
|
|
261
|
+
if (typeof volume === "string") {
|
|
262
|
+
volumeValue = parseInt(volume.replace("%", ""), 10);
|
|
263
|
+
if (isNaN(volumeValue))
|
|
264
|
+
throw new Error("Invalid volume format.");
|
|
265
|
+
} else {
|
|
266
|
+
volumeValue = volume;
|
|
267
|
+
}
|
|
268
|
+
if (volumeValue < -100 || volumeValue > 100) {
|
|
269
|
+
throw new Error("Volume cannot be negative. Expected a value from -100% to 100% (or more).");
|
|
270
|
+
}
|
|
271
|
+
return `${volumeValue}%`;
|
|
272
|
+
}
|
|
273
|
+
async synthesize(text, voice = "en-US-AnaNeural", options = {}) {
|
|
274
|
+
const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
|
|
275
|
+
return new Promise((resolve, reject) => {
|
|
276
|
+
this.audio_stream = [];
|
|
277
|
+
const reqId = this.generateUUID();
|
|
278
|
+
const url = `${constants_1.Constants.WSS_URL}?TrustedClientToken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${constants_1.Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;
|
|
279
|
+
this.ws = new ws_1.default(url, {
|
|
280
|
+
headers: constants_1.Constants.getBaseHeaders(),
|
|
281
|
+
rejectUnauthorized: false
|
|
282
|
+
});
|
|
283
|
+
const SSML_text = this.getSSML(text, voice, options);
|
|
284
|
+
const outputFormat = options.outputFormat || "audio-24khz-48kbitrate-mono-mp3";
|
|
285
|
+
this.output_format = outputFormat;
|
|
286
|
+
let timedOut = false;
|
|
287
|
+
let inactivityTimeout;
|
|
288
|
+
const resetInactivityTimeout = () => {
|
|
289
|
+
clearTimeout(inactivityTimeout);
|
|
290
|
+
inactivityTimeout = setTimeout(() => {
|
|
291
|
+
timedOut = true;
|
|
292
|
+
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
293
|
+
this.ws.close();
|
|
294
|
+
}
|
|
295
|
+
reject(new Error("WebSocket inactivity timeout - no response from server"));
|
|
296
|
+
}, 3e4);
|
|
297
|
+
};
|
|
298
|
+
this.ws.on("open", () => {
|
|
299
|
+
resetInactivityTimeout();
|
|
300
|
+
const message = this.buildTTSConfigMessage(outputFormat);
|
|
301
|
+
this.ws.send(message);
|
|
302
|
+
const timestamp = this.nowRFC1123();
|
|
303
|
+
const speechMessage = `X-RequestId:${reqId}\r
|
|
304
|
+
Content-Type:application/ssml+xml\r
|
|
305
|
+
X-Timestamp:${timestamp}\r
|
|
306
|
+
Path:ssml\r
|
|
307
|
+
\r
|
|
308
|
+
${SSML_text}`;
|
|
309
|
+
this.ws.send(speechMessage);
|
|
310
|
+
});
|
|
311
|
+
this.ws.on("message", (data) => {
|
|
312
|
+
resetInactivityTimeout();
|
|
313
|
+
this.processAudioData(data);
|
|
314
|
+
});
|
|
315
|
+
this.ws.on("error", (err) => {
|
|
316
|
+
clearTimeout(inactivityTimeout);
|
|
317
|
+
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
318
|
+
this.ws.close();
|
|
319
|
+
}
|
|
320
|
+
reject(err);
|
|
321
|
+
});
|
|
322
|
+
this.ws.on("close", () => {
|
|
323
|
+
clearTimeout(inactivityTimeout);
|
|
324
|
+
if (!timedOut) {
|
|
325
|
+
resolve();
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
escapeXML(text) {
|
|
331
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
332
|
+
}
|
|
333
|
+
getSSML(content, voice, options = {}) {
|
|
334
|
+
const pitch = this.validatePitch(options.pitch ?? 0);
|
|
335
|
+
const rate = this.validateRate(options.rate ?? 0);
|
|
336
|
+
const volume = this.validateVolume(options.volume ?? 0);
|
|
337
|
+
const escapedText = this.escapeXML(content);
|
|
338
|
+
return `<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US">
|
|
339
|
+
<voice name="${voice}">
|
|
340
|
+
<prosody pitch="${pitch}" rate="${rate}" volume="${volume}">
|
|
341
|
+
${escapedText}
|
|
342
|
+
</prosody>
|
|
343
|
+
</voice>
|
|
344
|
+
</speak>
|
|
345
|
+
`;
|
|
346
|
+
}
|
|
347
|
+
nowRFC1123(timeZone = "UTC") {
|
|
348
|
+
const now = /* @__PURE__ */ new Date();
|
|
349
|
+
const options = {
|
|
350
|
+
weekday: "short",
|
|
351
|
+
year: "numeric",
|
|
352
|
+
month: "short",
|
|
353
|
+
day: "2-digit",
|
|
354
|
+
hour: "2-digit",
|
|
355
|
+
minute: "2-digit",
|
|
356
|
+
second: "2-digit",
|
|
357
|
+
timeZone,
|
|
358
|
+
timeZoneName: "short"
|
|
359
|
+
};
|
|
360
|
+
return now.toLocaleString("en-US", options);
|
|
361
|
+
}
|
|
362
|
+
parseRFC1123(rfcStr) {
|
|
363
|
+
return new Date(rfcStr);
|
|
364
|
+
}
|
|
365
|
+
buildTTSConfigMessage(outputFormat = "audio-24khz-48kbitrate-mono-mp3") {
|
|
366
|
+
const timestamp = this.nowRFC1123();
|
|
367
|
+
return `X-Timestamp:${timestamp}\r
|
|
368
|
+
Content-Type:application/json; charset=utf-8\r
|
|
369
|
+
Path:speech.config\r
|
|
370
|
+
\r
|
|
371
|
+
{"context":{"synthesis":{"audio":{"metadataoptions":{"sentenceBoundaryEnabled":false,"wordBoundaryEnabled":true},"outputFormat":"${outputFormat}"}}}}`;
|
|
372
|
+
}
|
|
373
|
+
async *synthesizeStream(text, voice = "en-US-AnaNeural", options = {}) {
|
|
374
|
+
this.audio_stream = [];
|
|
375
|
+
const reqId = this.generateUUID();
|
|
376
|
+
const secMsGEC = await this.generateSecMsGec(constants_1.Constants.TRUSTED_CLIENT_TOKEN);
|
|
377
|
+
const url = `${constants_1.Constants.WSS_URL}?TrustedClientToken=${constants_1.Constants.TRUSTED_CLIENT_TOKEN}&Sec-MS-GEC=${secMsGEC}&Sec-MS-GEC-Version=${constants_1.Constants.VERSION_MS_GEC}&ConnectionId=${reqId}`;
|
|
378
|
+
this.ws = new ws_1.default(url, {
|
|
379
|
+
headers: constants_1.Constants.getBaseHeaders(),
|
|
380
|
+
rejectUnauthorized: false
|
|
381
|
+
});
|
|
382
|
+
const SSML_text = this.getSSML(text, voice, options);
|
|
383
|
+
const outputFormat = options.outputFormat || "audio-24khz-48kbitrate-mono-mp3";
|
|
384
|
+
this.output_format = outputFormat;
|
|
385
|
+
const queue = [];
|
|
386
|
+
let done = false;
|
|
387
|
+
let error = null;
|
|
388
|
+
let notify = null;
|
|
389
|
+
const push = (chunk) => {
|
|
390
|
+
queue.push(chunk);
|
|
391
|
+
if (notify) {
|
|
392
|
+
notify();
|
|
393
|
+
notify = null;
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
let timedOut = false;
|
|
397
|
+
let inactivityTimeout;
|
|
398
|
+
const resetInactivityTimeout = () => {
|
|
399
|
+
clearTimeout(inactivityTimeout);
|
|
400
|
+
inactivityTimeout = setTimeout(() => {
|
|
401
|
+
timedOut = true;
|
|
402
|
+
error = new Error("WebSocket inactivity timeout - no response from server");
|
|
403
|
+
done = true;
|
|
404
|
+
if (this.ws && this.ws.readyState === ws_1.default.OPEN) {
|
|
405
|
+
this.ws.close();
|
|
406
|
+
}
|
|
407
|
+
if (notify) {
|
|
408
|
+
notify();
|
|
409
|
+
notify = null;
|
|
410
|
+
}
|
|
411
|
+
}, 3e4);
|
|
412
|
+
};
|
|
413
|
+
this.ws.on("open", () => {
|
|
414
|
+
resetInactivityTimeout();
|
|
415
|
+
const message = this.buildTTSConfigMessage(outputFormat);
|
|
416
|
+
this.ws.send(message);
|
|
417
|
+
const timestamp = this.nowRFC1123();
|
|
418
|
+
const speechMessage = `X-RequestId:${reqId}\r
|
|
419
|
+
Content-Type:application/ssml+xml\r
|
|
420
|
+
X-Timestamp:${timestamp}\r
|
|
421
|
+
Path:ssml\r
|
|
422
|
+
\r
|
|
423
|
+
${SSML_text}`;
|
|
424
|
+
this.ws.send(speechMessage);
|
|
425
|
+
});
|
|
426
|
+
this.ws.on("message", (data) => {
|
|
427
|
+
resetInactivityTimeout();
|
|
428
|
+
const buffer = ensureBuffer(data);
|
|
429
|
+
const needle = buffer_1.Buffer.from("Path:audio\r\n");
|
|
430
|
+
const audioStartIndex = buffer.indexOf(new Uint8Array(needle));
|
|
431
|
+
if (audioStartIndex !== -1) {
|
|
432
|
+
const audioChunk = buffer.subarray(audioStartIndex + needle.length);
|
|
433
|
+
const chunk = new Uint8Array(audioChunk);
|
|
434
|
+
this.audio_stream.push(chunk);
|
|
435
|
+
push(chunk);
|
|
436
|
+
}
|
|
437
|
+
if (buffer.toString().includes("Path:audio.metadata")) {
|
|
438
|
+
const metadataStart = buffer.indexOf("\r\n\r\n") + 4;
|
|
439
|
+
const metadataJson = buffer.toString().substring(metadataStart);
|
|
440
|
+
const meta = this.parseMetadata(metadataJson);
|
|
441
|
+
if (meta !== null) {
|
|
442
|
+
this.word_boundaries.push(meta);
|
|
443
|
+
}
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (buffer.toString().includes("Path:turn.end")) {
|
|
447
|
+
this.ws?.close();
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
this.ws.on("error", (err) => {
|
|
451
|
+
clearTimeout(inactivityTimeout);
|
|
452
|
+
error = err;
|
|
453
|
+
done = true;
|
|
454
|
+
if (notify) {
|
|
455
|
+
notify();
|
|
456
|
+
notify = null;
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
this.ws.on("close", () => {
|
|
460
|
+
clearTimeout(inactivityTimeout);
|
|
461
|
+
done = true;
|
|
462
|
+
if (notify) {
|
|
463
|
+
notify();
|
|
464
|
+
notify = null;
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
while (!done || queue.length > 0) {
|
|
468
|
+
if (queue.length === 0) {
|
|
469
|
+
await new Promise((resolve) => notify = resolve);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const chunk = queue.shift();
|
|
473
|
+
if (chunk) {
|
|
474
|
+
yield chunk;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (error) {
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
processAudioData(data) {
|
|
482
|
+
const buffer = ensureBuffer(data);
|
|
483
|
+
const needle = buffer_1.Buffer.from("Path:audio\r\n");
|
|
484
|
+
const audioStartIndex = buffer.indexOf(new Uint8Array(needle));
|
|
485
|
+
if (audioStartIndex !== -1) {
|
|
486
|
+
const audioChunk = buffer.subarray(audioStartIndex + needle.length);
|
|
487
|
+
this.audio_stream.push(new Uint8Array(audioChunk));
|
|
488
|
+
}
|
|
489
|
+
if (buffer.toString().includes("Path:audio.metadata")) {
|
|
490
|
+
const metadataStart = buffer.indexOf("\r\n\r\n") + 4;
|
|
491
|
+
const metadataJson = buffer.toString().substring(metadataStart);
|
|
492
|
+
const meta = this.parseMetadata(metadataJson);
|
|
493
|
+
if (meta !== null) {
|
|
494
|
+
this.word_boundaries.push(meta);
|
|
495
|
+
}
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (buffer.toString().includes("Path:turn.end")) {
|
|
499
|
+
this.ws?.close();
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
parseMetadata(data, offsetCompensation = 0) {
|
|
503
|
+
let metadata;
|
|
504
|
+
try {
|
|
505
|
+
metadata = JSON.parse(data);
|
|
506
|
+
} catch {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
if (!metadata.Metadata) {
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
for (const metaObj of metadata.Metadata) {
|
|
513
|
+
if (metaObj.Type === "WordBoundary") {
|
|
514
|
+
const currentOffset = metaObj.Data.Offset + offsetCompensation;
|
|
515
|
+
const currentDuration = metaObj.Data.Duration;
|
|
516
|
+
return {
|
|
517
|
+
type: "WordBoundary",
|
|
518
|
+
offset: currentOffset,
|
|
519
|
+
duration: currentDuration,
|
|
520
|
+
text: metaObj.Data.text?.Text
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
generateSecMsGec = async (trustedClientToken) => {
|
|
527
|
+
const now = this.nowRFC1123();
|
|
528
|
+
const fixedDate = this.parseRFC1123(now);
|
|
529
|
+
const ticks = Math.floor(fixedDate.getTime() / 1e3) + 11644473600;
|
|
530
|
+
const rounded = ticks - ticks % 300;
|
|
531
|
+
const windowsTicks = rounded * 1e7;
|
|
532
|
+
const encoder = new TextEncoder();
|
|
533
|
+
const data = encoder.encode(`${windowsTicks}${trustedClientToken}`);
|
|
534
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
535
|
+
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
536
|
+
};
|
|
537
|
+
getDuration() {
|
|
538
|
+
if (this.audio_stream.length === 0) {
|
|
539
|
+
throw new Error("No audio data available");
|
|
540
|
+
}
|
|
541
|
+
const bufferSize = this.toBuffer().length;
|
|
542
|
+
const estimatedDuration = bufferSize / (24e3 * 3);
|
|
543
|
+
return estimatedDuration;
|
|
544
|
+
}
|
|
545
|
+
getFileExtension(format) {
|
|
546
|
+
if (format.includes("mp3"))
|
|
547
|
+
return "mp3";
|
|
548
|
+
if (format.includes("opus") && format.includes("webm"))
|
|
549
|
+
return "webm";
|
|
550
|
+
if (format.includes("opus") && format.includes("ogg"))
|
|
551
|
+
return "ogg";
|
|
552
|
+
if (format.includes("wav") || format.includes("riff"))
|
|
553
|
+
return "wav";
|
|
554
|
+
if (format.includes("pcm") && format.includes("raw"))
|
|
555
|
+
return "pcm";
|
|
556
|
+
if (format.includes("alaw"))
|
|
557
|
+
return "alaw";
|
|
558
|
+
if (format.includes("mulaw"))
|
|
559
|
+
return "mulaw";
|
|
560
|
+
if (format.includes("truesilk"))
|
|
561
|
+
return "silk";
|
|
562
|
+
if (format.includes("g722"))
|
|
563
|
+
return "g722";
|
|
564
|
+
if (format.includes("amr"))
|
|
565
|
+
return "amr";
|
|
566
|
+
return "audio";
|
|
567
|
+
}
|
|
568
|
+
getAudioInfo() {
|
|
569
|
+
const buffer = this.toBuffer();
|
|
570
|
+
return {
|
|
571
|
+
size: buffer.length,
|
|
572
|
+
format: this.getFileExtension(this.output_format),
|
|
573
|
+
estimatedDuration: this.getDuration()
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
async toFile(outputPath, format) {
|
|
577
|
+
if (!format) {
|
|
578
|
+
format = this.getFileExtension(this.output_format);
|
|
579
|
+
}
|
|
580
|
+
const audioBuffer = this.toBuffer();
|
|
581
|
+
const finalPath = `${outputPath}.${format}`;
|
|
582
|
+
await (0, promises_1.writeFile)(finalPath, new Uint8Array(audioBuffer));
|
|
583
|
+
return finalPath;
|
|
584
|
+
}
|
|
585
|
+
toRaw() {
|
|
586
|
+
return this.toBase64();
|
|
587
|
+
}
|
|
588
|
+
toBase64() {
|
|
589
|
+
return this.toBuffer().toString("base64");
|
|
590
|
+
}
|
|
591
|
+
toBuffer() {
|
|
592
|
+
if (this.audio_stream.length === 0) {
|
|
593
|
+
throw new Error("No audio data available. Did you run synthesize() first?");
|
|
594
|
+
}
|
|
595
|
+
return buffer_1.Buffer.concat(this.audio_stream);
|
|
596
|
+
}
|
|
597
|
+
async saveMetadata(outputPath) {
|
|
598
|
+
if (this.word_boundaries.length === 0) {
|
|
599
|
+
throw new Error("No metadata available to save.");
|
|
600
|
+
}
|
|
601
|
+
const json = JSON.stringify(this.word_boundaries, null, 4);
|
|
602
|
+
await (0, promises_1.writeFile)(outputPath, json);
|
|
603
|
+
}
|
|
604
|
+
getWordBoundaries() {
|
|
605
|
+
return this.word_boundaries;
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
exports.EdgeTTS = EdgeTTS2;
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
// node_modules/@andresaya/edge-tts/dist/index.js
|
|
613
|
+
var require_dist = __commonJS({
|
|
614
|
+
"node_modules/@andresaya/edge-tts/dist/index.js"(exports) {
|
|
615
|
+
"use strict";
|
|
616
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
617
|
+
exports.Constants = exports.EdgeTTS = void 0;
|
|
618
|
+
var EdgeTTS_1 = require_EdgeTTS();
|
|
619
|
+
Object.defineProperty(exports, "EdgeTTS", { enumerable: true, get: function() {
|
|
620
|
+
return EdgeTTS_1.EdgeTTS;
|
|
621
|
+
} });
|
|
622
|
+
var constants_1 = require_constants();
|
|
623
|
+
Object.defineProperty(exports, "Constants", { enumerable: true, get: function() {
|
|
624
|
+
return constants_1.Constants;
|
|
625
|
+
} });
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
// src/components/commands/registry.ts
|
|
630
|
+
init_logger();
|
|
631
|
+
|
|
632
|
+
// src/components/message-pipeline.ts
|
|
633
|
+
init_logger();
|
|
634
|
+
init_log_and_swallow();
|
|
635
|
+
|
|
636
|
+
// src/components/clean-response.ts
|
|
637
|
+
var REACT_RE = /\[REACT:(.+?)\]/;
|
|
638
|
+
var NO_REPLY_RE = /\s*\[NO[-_]REPLY\]\s*/gi;
|
|
639
|
+
var LANG_TAG_RE = /^\[lang:\w{2}\]\s*/i;
|
|
640
|
+
var TOPICS_RE = /\[TOPICS:\s*(.+?)\]/i;
|
|
641
|
+
var CONTEXT_BLOCK_RE = /\[CONTEXT[^\]]*\][\s\S]*?\[\/CONTEXT\]/gi;
|
|
642
|
+
var MEMORY_BLOCK_RE = /\[MEMORY CONTEXT[^\]]*\][\s\S]*?\[\/MEMORY CONTEXT\]/gi;
|
|
643
|
+
var COMPACT_BLOCK_RE = /\[COMPACTED CONVERSATION\][\s\S]*?\[\/COMPACTED CONVERSATION\]/gi;
|
|
644
|
+
var SESSION_REASON_RE = /\[SESSION START REASON\][^\n]*/gi;
|
|
645
|
+
var CURRENT_USER_RE = /\[CURRENT USER\][^\[]*/gi;
|
|
646
|
+
var FLASHBACK_RE = /\[Flashback\][^\n]*/gi;
|
|
647
|
+
var CURRENT_TIME_RE = /\[Current time:[^\]]*\]/gi;
|
|
648
|
+
function cleanResponse(raw) {
|
|
649
|
+
const noReply = NO_REPLY_RE.test(raw);
|
|
650
|
+
NO_REPLY_RE.lastIndex = 0;
|
|
651
|
+
let text = raw.replace(NO_REPLY_RE, "").replace(LANG_TAG_RE, "").trim();
|
|
652
|
+
let reactionEmoji;
|
|
653
|
+
let topics;
|
|
654
|
+
const reactMatch = text.match(REACT_RE);
|
|
655
|
+
if (reactMatch) {
|
|
656
|
+
reactionEmoji = reactMatch[1];
|
|
657
|
+
text = text.replace(reactMatch[0], "").trim();
|
|
658
|
+
}
|
|
659
|
+
const topicsMatch = text.match(TOPICS_RE);
|
|
660
|
+
if (topicsMatch) {
|
|
661
|
+
topics = topicsMatch[1].split(",").map((t) => t.trim().toLowerCase()).filter((t) => t.length >= 2);
|
|
662
|
+
text = text.replace(topicsMatch[0], "").trim();
|
|
663
|
+
}
|
|
664
|
+
text = text.replace(CONTEXT_BLOCK_RE, "").replace(MEMORY_BLOCK_RE, "").replace(COMPACT_BLOCK_RE, "").replace(SESSION_REASON_RE, "").replace(CURRENT_USER_RE, "").replace(FLASHBACK_RE, "").replace(CURRENT_TIME_RE, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
665
|
+
return { text, reactionEmoji, noReply, topics };
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// src/components/reactions.ts
|
|
669
|
+
init_log_and_swallow();
|
|
670
|
+
init_logger();
|
|
671
|
+
var TAG = "reaction";
|
|
672
|
+
var TELEGRAM_ALLOWED_REACTIONS = /* @__PURE__ */ new Set([
|
|
673
|
+
"\u{1F44D}",
|
|
674
|
+
"\u{1F44E}",
|
|
675
|
+
"\u2764",
|
|
676
|
+
"\u{1F525}",
|
|
677
|
+
"\u{1F970}",
|
|
678
|
+
"\u{1F44F}",
|
|
679
|
+
"\u{1F601}",
|
|
680
|
+
"\u{1F914}",
|
|
681
|
+
"\u{1F92F}",
|
|
682
|
+
"\u{1F631}",
|
|
683
|
+
"\u{1F92C}",
|
|
684
|
+
"\u{1F622}",
|
|
685
|
+
"\u{1F389}",
|
|
686
|
+
"\u{1F929}",
|
|
687
|
+
"\u{1F92E}",
|
|
688
|
+
"\u{1F4A9}",
|
|
689
|
+
"\u{1F64F}",
|
|
690
|
+
"\u{1F44C}",
|
|
691
|
+
"\u{1F54A}",
|
|
692
|
+
"\u{1F921}",
|
|
693
|
+
"\u{1F971}",
|
|
694
|
+
"\u{1F974}",
|
|
695
|
+
"\u{1F60D}",
|
|
696
|
+
"\u{1F433}",
|
|
697
|
+
"\u2764\u{1F525}",
|
|
698
|
+
"\u{1F31A}",
|
|
699
|
+
"\u{1F32D}",
|
|
700
|
+
"\u{1F4AF}",
|
|
701
|
+
"\u{1F923}",
|
|
702
|
+
"\u26A1",
|
|
703
|
+
"\u{1F34C}",
|
|
704
|
+
"\u{1F3C6}",
|
|
705
|
+
"\u{1F494}",
|
|
706
|
+
"\u{1F928}",
|
|
707
|
+
"\u{1F610}",
|
|
708
|
+
"\u{1F353}",
|
|
709
|
+
"\u{1F37E}",
|
|
710
|
+
"\u{1F48B}",
|
|
711
|
+
"\u{1F595}",
|
|
712
|
+
"\u{1F608}",
|
|
713
|
+
"\u{1F634}",
|
|
714
|
+
"\u{1F62D}",
|
|
715
|
+
"\u{1F913}",
|
|
716
|
+
"\u{1F47B}",
|
|
717
|
+
"\u{1F468}\u{1F4BB}",
|
|
718
|
+
"\u{1F440}",
|
|
719
|
+
"\u{1F383}",
|
|
720
|
+
"\u{1F648}",
|
|
721
|
+
"\u{1F607}",
|
|
722
|
+
"\u{1F628}",
|
|
723
|
+
"\u{1F91D}",
|
|
724
|
+
"\u270D",
|
|
725
|
+
"\u{1F917}",
|
|
726
|
+
"\u{1FAE1}",
|
|
727
|
+
"\u{1F385}",
|
|
728
|
+
"\u{1F384}",
|
|
729
|
+
"\u2603",
|
|
730
|
+
"\u{1F485}",
|
|
731
|
+
"\u{1F92A}",
|
|
732
|
+
"\u{1F5FF}",
|
|
733
|
+
"\u{1F192}",
|
|
734
|
+
"\u{1F498}",
|
|
735
|
+
"\u{1F649}",
|
|
736
|
+
"\u{1F984}",
|
|
737
|
+
"\u{1F618}",
|
|
738
|
+
"\u{1F48A}",
|
|
739
|
+
"\u{1F64A}",
|
|
740
|
+
"\u{1F60E}",
|
|
741
|
+
"\u{1F47E}",
|
|
742
|
+
"\u{1F937}\u2642",
|
|
743
|
+
"\u{1F937}",
|
|
744
|
+
"\u{1F937}\u2640",
|
|
745
|
+
"\u{1F621}"
|
|
746
|
+
]);
|
|
747
|
+
var REACTION_FALLBACK_MAP = {
|
|
748
|
+
"\u{1F605}": "\u{1F923}",
|
|
749
|
+
"\u{1F602}": "\u{1F923}",
|
|
750
|
+
"\u{1F606}": "\u{1F601}",
|
|
751
|
+
"\u{1F604}": "\u{1F601}",
|
|
752
|
+
"\u{1F603}": "\u{1F601}",
|
|
753
|
+
"\u{1F642}": "\u{1F601}",
|
|
754
|
+
"\u{1F60A}": "\u{1F601}",
|
|
755
|
+
"\u263A": "\u{1F601}",
|
|
756
|
+
"\u{1F609}": "\u{1F601}",
|
|
757
|
+
"\u{1FAE0}": "\u{1F92A}",
|
|
758
|
+
"\u{1F61E}": "\u{1F622}",
|
|
759
|
+
"\u{1F614}": "\u{1F622}",
|
|
760
|
+
"\u{1F61F}": "\u{1F622}",
|
|
761
|
+
"\u{1F615}": "\u{1F914}",
|
|
762
|
+
"\u{1FAE4}": "\u{1F928}",
|
|
763
|
+
"\u{1F624}": "\u{1F621}",
|
|
764
|
+
"\u{1F620}": "\u{1F621}",
|
|
765
|
+
"\u{1F4AA}": "\u{1F44F}",
|
|
766
|
+
"\u{1F91E}": "\u{1F64F}",
|
|
767
|
+
"\u2705": "\u{1F44D}",
|
|
768
|
+
"\u274C": "\u{1F44E}",
|
|
769
|
+
"\u{1F62C}": "\u{1F648}",
|
|
770
|
+
"\u{1FAE3}": "\u{1F648}",
|
|
771
|
+
"\u{1F92D}": "\u{1F64A}",
|
|
772
|
+
"\u{1F480}": "\u{1F47B}"
|
|
773
|
+
};
|
|
774
|
+
function routeReaction(isAuthorized, chatType) {
|
|
775
|
+
if (!isAuthorized) return "discard";
|
|
776
|
+
if (chatType === "group" || chatType === "supergroup") return "buffer";
|
|
777
|
+
return "transport";
|
|
778
|
+
}
|
|
779
|
+
function formatReactionSignal(senderName, emojis) {
|
|
780
|
+
return `[${senderName} reaction: ${emojis.join(" ")}]`;
|
|
781
|
+
}
|
|
782
|
+
async function tryReaction(adapter, channelId, messageId, emoji, threadId, platform2) {
|
|
783
|
+
if (!adapter.setReaction || !messageId) {
|
|
784
|
+
await adapter.sendMessage(channelId, emoji, { threadId });
|
|
785
|
+
logDebug(TAG, `No reaction API \u2014 sent ${emoji} as text`);
|
|
786
|
+
return true;
|
|
787
|
+
}
|
|
788
|
+
if (platform2 && platform2 !== "telegram") {
|
|
789
|
+
try {
|
|
790
|
+
await adapter.setReaction(channelId, messageId, emoji);
|
|
791
|
+
logDebug(TAG, `Reaction: ${emoji}`);
|
|
792
|
+
return true;
|
|
793
|
+
} catch {
|
|
794
|
+
await adapter.sendMessage(channelId, emoji, { threadId });
|
|
795
|
+
return true;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
const fallback = TELEGRAM_ALLOWED_REACTIONS.has(emoji) ? emoji : REACTION_FALLBACK_MAP[emoji] ?? null;
|
|
799
|
+
if (fallback) {
|
|
800
|
+
try {
|
|
801
|
+
await adapter.setReaction(channelId, messageId, fallback);
|
|
802
|
+
logDebug(TAG, `Reaction: ${emoji}${emoji !== fallback ? ` \u2192 ${fallback}` : ""}`);
|
|
803
|
+
return true;
|
|
804
|
+
} catch (err) {
|
|
805
|
+
logAndSwallow("reactions", "op", err);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
await adapter.sendMessage(channelId, emoji, { threadId });
|
|
809
|
+
logDebug(TAG, `Reaction ${emoji} not supported \u2014 sent as text`);
|
|
810
|
+
return true;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// src/components/tts.ts
|
|
814
|
+
var import_edge_tts = __toESM(require_dist(), 1);
|
|
815
|
+
init_logger();
|
|
816
|
+
var MAX_TTS_CHARS = 4e3;
|
|
817
|
+
var DEFAULT_VOICE_MAP = {
|
|
818
|
+
hu: "hu-HU-TamasNeural",
|
|
819
|
+
en: "en-US-AndrewMultilingualNeural"
|
|
820
|
+
};
|
|
821
|
+
function extractLangTag(text) {
|
|
822
|
+
const m = text.match(/^\[lang:(\w{2})\]\s*/i);
|
|
823
|
+
if (m) return { lang: m[1].toLowerCase(), text: text.slice(m[0].length) };
|
|
824
|
+
return { lang: null, text };
|
|
825
|
+
}
|
|
826
|
+
async function synthesizeSpeech(text, config) {
|
|
827
|
+
const { lang, text: stripped } = extractLangTag(text);
|
|
828
|
+
const cleaned = cleanForTts(stripped).trim();
|
|
829
|
+
if (!cleaned || cleaned.length < 5) {
|
|
830
|
+
logDebug("tts", `Text too short for TTS (${cleaned.length} chars)`);
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
const voiceMap = { ...DEFAULT_VOICE_MAP, ...config.voiceMap };
|
|
834
|
+
const voice = lang && voiceMap[lang] || config.voice;
|
|
835
|
+
const input = cleaned.length > MAX_TTS_CHARS ? cleaned.slice(0, MAX_TTS_CHARS) + "... (truncated)" : cleaned;
|
|
836
|
+
logInfo("tts", `Synthesizing ${input.length} chars with voice ${voice}${lang ? ` (lang:${lang})` : ""}`);
|
|
837
|
+
try {
|
|
838
|
+
const tts = new import_edge_tts.EdgeTTS();
|
|
839
|
+
await tts.synthesize(input, voice, {
|
|
840
|
+
rate: "+0%",
|
|
841
|
+
pitch: "+0Hz",
|
|
842
|
+
outputFormat: import_edge_tts.Constants.OUTPUT_FORMAT.WEBM_24KHZ_16BIT_MONO_OPUS
|
|
843
|
+
});
|
|
844
|
+
const audioBuffer = tts.toBuffer();
|
|
845
|
+
if (!audioBuffer || audioBuffer.length === 0) {
|
|
846
|
+
logWarn("tts", "Edge TTS returned empty audio");
|
|
847
|
+
return null;
|
|
848
|
+
}
|
|
849
|
+
logInfo("tts", `Audio generated: ${audioBuffer.length} bytes`);
|
|
850
|
+
return audioBuffer;
|
|
851
|
+
} catch (err) {
|
|
852
|
+
logWarn("tts", `TTS synthesis failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function cleanForTts(text) {
|
|
857
|
+
let result = text.replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]+>/g, "").replace(/^.*\(using tool:\s*\w+\).*$/gm, "").replace(/^.*\[mode:.*$/gm, "").replace(/^(Fetching content from|Searching the web|Searching for|Reading file|Writing to|Running command|Executing|Looking at|Checking).*$/gm, "").replace(/^(Let me|I'll|I will|I need to|I'm going to|I should)\b.*$/gm, "").replace(/^- Completed in \d+(\.\d+)?s.*$/gm, "").replace(/^WARNING:.*$/gm, "").replace(/^https?:\/\/\S+$/gm, "").replace(/^\s*\[.*?\]\s*$/gm, "").replace(/```[\s\S]*?```/g, " (code block omitted) ").replace(/`[^`]+`/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[*_~]{1,3}/g, "").replace(/^#{1,6}\s+/gm, "").replace(/^[-*+]\s+/gm, "").replace(/^\d+\.\s+/gm, "");
|
|
858
|
+
try {
|
|
859
|
+
result = result.replace(/\p{Extended_Pictographic}/gu, "");
|
|
860
|
+
} catch (err) {
|
|
861
|
+
logWarn("tts", `Emoji filter failed, using unfiltered text: ${err instanceof Error ? err.message : String(err)}`);
|
|
862
|
+
}
|
|
863
|
+
return result.replace(/\n{3,}/g, "\n\n").trim();
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/components/sanitize-outbound.ts
|
|
867
|
+
var STRIP = [
|
|
868
|
+
/\s*\[TOPICS:\s*.+?\]/gi,
|
|
869
|
+
/\s*\[NO_REPLY\]\s*/gi,
|
|
870
|
+
/\s*\[REACT:.+?\]\s*/gi
|
|
871
|
+
];
|
|
872
|
+
function sanitizeOutbound(text) {
|
|
873
|
+
let out = text;
|
|
874
|
+
for (const re of STRIP) {
|
|
875
|
+
re.lastIndex = 0;
|
|
876
|
+
out = out.replace(re, "");
|
|
877
|
+
}
|
|
878
|
+
return out.trim();
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// src/components/pipeline/middleware.ts
|
|
882
|
+
async function runPipeline(ctx, middlewares) {
|
|
883
|
+
let index = 0;
|
|
884
|
+
const next = async () => {
|
|
885
|
+
if (ctx.handled || index >= middlewares.length) return;
|
|
886
|
+
const mw = middlewares[index++];
|
|
887
|
+
await mw(ctx, next);
|
|
888
|
+
};
|
|
889
|
+
await next();
|
|
890
|
+
}
|
|
891
|
+
function createMessageContext(msg, adapter, deps) {
|
|
892
|
+
const reply = (text, opts) => {
|
|
893
|
+
const clean = sanitizeOutbound(text);
|
|
894
|
+
if (!clean) return Promise.resolve(void 0);
|
|
895
|
+
return adapter.sendMessage(msg.channelId, clean, { threadId: msg.threadId, ...opts });
|
|
896
|
+
};
|
|
897
|
+
const chatId = parseInt(msg.channelId, 10) || 0;
|
|
898
|
+
const userId = msg.userId;
|
|
899
|
+
return {
|
|
900
|
+
msg,
|
|
901
|
+
adapter,
|
|
902
|
+
deps,
|
|
903
|
+
text: msg.text,
|
|
904
|
+
chatId,
|
|
905
|
+
userId,
|
|
906
|
+
reply,
|
|
907
|
+
handled: false
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// src/components/pipeline/voice.ts
|
|
912
|
+
init_logger();
|
|
913
|
+
var voiceMiddleware = async (ctx, next) => {
|
|
914
|
+
const { msg, adapter, deps } = ctx;
|
|
915
|
+
if (msg.isVoice && msg.voiceFileId && adapter.downloadVoice && deps.sttConfig) {
|
|
916
|
+
try {
|
|
917
|
+
if (adapter.setReaction && msg.messageId) {
|
|
918
|
+
await adapter.setReaction(msg.channelId, msg.messageId, "\u{1F440}");
|
|
919
|
+
}
|
|
920
|
+
const audioBuffer = await adapter.downloadVoice(msg.voiceFileId);
|
|
921
|
+
const { text: transcript, language } = await transcribeAudio(audioBuffer, "voice.ogg", deps.sttConfig);
|
|
922
|
+
if (!transcript) {
|
|
923
|
+
if (adapter.setReaction && msg.messageId) await adapter.setReaction(msg.channelId, msg.messageId, "");
|
|
924
|
+
await ctx.reply("\u{1F937} Couldn't transcribe the voice note.");
|
|
925
|
+
ctx.handled = true;
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
const langTag = language ? `, ${language}` : "";
|
|
929
|
+
ctx.text = `[\u{1F3A4} voice${langTag}] ${transcript}`;
|
|
930
|
+
} catch (err) {
|
|
931
|
+
logError("voice-mw", "Voice transcription failed", err);
|
|
932
|
+
if (adapter.setReaction && msg.messageId) await adapter.setReaction(msg.channelId, msg.messageId, "");
|
|
933
|
+
await ctx.reply("\u274C Voice transcription failed.");
|
|
934
|
+
ctx.handled = true;
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
} else if (msg.isVoice && !deps.sttConfig) {
|
|
938
|
+
await ctx.reply("\u{1F3A4} Voice notes require STT (set GROQ_API_KEY).");
|
|
939
|
+
ctx.handled = true;
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
await next();
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
// src/components/pipeline/commands.ts
|
|
946
|
+
init_logger();
|
|
947
|
+
var BANG_PREFIX = /^[!!ǃ❗❕‼⁉]+/u;
|
|
948
|
+
var INTERRUPT_COMMANDS = /* @__PURE__ */ new Set(["/stop", "/ctrlc", "/new", "/reset", "/restart"]);
|
|
949
|
+
var DESTRUCTIVE_COMMANDS = /* @__PURE__ */ new Set(["/stop", "/ctrlc", "/new", "/reset", "/restart", "/compact", "/coding", "/default"]);
|
|
950
|
+
var commandMiddleware = async (ctx, next) => {
|
|
951
|
+
const { msg, deps } = ctx;
|
|
952
|
+
const {
|
|
953
|
+
transport,
|
|
954
|
+
config,
|
|
955
|
+
startedAt,
|
|
956
|
+
memory,
|
|
957
|
+
memoryConfig,
|
|
958
|
+
nlmConfig,
|
|
959
|
+
idleSave,
|
|
960
|
+
sessions,
|
|
961
|
+
sessionManager,
|
|
962
|
+
updateCtxStart,
|
|
963
|
+
conversationBuffer
|
|
964
|
+
} = deps;
|
|
965
|
+
const registry = loadUsers();
|
|
966
|
+
const resolvedUserId = msg.userId;
|
|
967
|
+
const user = registry.byUserId.get(resolvedUserId);
|
|
968
|
+
const isMaster = user?.role === "master";
|
|
969
|
+
if (!isMaster && BANG_PREFIX.test(ctx.text)) {
|
|
970
|
+
const original = ctx.text;
|
|
971
|
+
ctx.text = ctx.text.replace(BANG_PREFIX, "");
|
|
972
|
+
logInfo("commands", `Defanged bang prefix from ${user?.userId ?? "unknown"}: "${original.slice(0, 25)}"`);
|
|
973
|
+
if (!ctx.text.trim()) {
|
|
974
|
+
ctx.handled = true;
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
const trimmed = ctx.text.trim();
|
|
979
|
+
const cmd = trimmed.split(/\s/)[0].toLowerCase();
|
|
980
|
+
const activeId = sessionManager.getActiveSessionId(msg.userId, msg.platform);
|
|
981
|
+
if (INTERRUPT_COMMANDS.has(cmd) && sessions.get(activeId)?.busy) {
|
|
982
|
+
logInfo("commands", `Interrupt command ${cmd} while busy \u2014 stopping current response`);
|
|
983
|
+
await transport.sendInterrupt();
|
|
984
|
+
sessions.getOrCreate(activeId).busy = false;
|
|
985
|
+
}
|
|
986
|
+
if (!INTERRUPT_COMMANDS.has(cmd) && DESTRUCTIVE_COMMANDS.has(cmd) && sessions.get(activeId)?.busy) {
|
|
987
|
+
const deferredWording = {
|
|
988
|
+
"/compact": "\u23F3 Will /compact after current response finishes.",
|
|
989
|
+
"/coding": "\u23F3 Will switch to coding mode after current response finishes.",
|
|
990
|
+
"/default": "\u23F3 Will switch to default mode after current response finishes."
|
|
991
|
+
};
|
|
992
|
+
const specific = deferredWording[cmd];
|
|
993
|
+
if (specific) {
|
|
994
|
+
await ctx.reply(specific);
|
|
995
|
+
ctx.deferReply = true;
|
|
996
|
+
}
|
|
997
|
+
await next();
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const { adapter } = ctx;
|
|
1001
|
+
const editReply = adapter.editMessage ? async (messageId, text) => {
|
|
1002
|
+
await adapter.editMessage(msg.channelId, messageId, text);
|
|
1003
|
+
} : void 0;
|
|
1004
|
+
const cmdCtx = {
|
|
1005
|
+
sessionKey: activeId,
|
|
1006
|
+
chatId: ctx.chatId,
|
|
1007
|
+
userId: ctx.userId ?? "master",
|
|
1008
|
+
platform: msg.platform,
|
|
1009
|
+
reply: ctx.reply,
|
|
1010
|
+
editReply,
|
|
1011
|
+
transport,
|
|
1012
|
+
config,
|
|
1013
|
+
startedAt,
|
|
1014
|
+
memory,
|
|
1015
|
+
memoryConfig,
|
|
1016
|
+
nlmConfig,
|
|
1017
|
+
idleSave,
|
|
1018
|
+
sessions,
|
|
1019
|
+
sessionManager: deps.sessionManager,
|
|
1020
|
+
updateCtxStart,
|
|
1021
|
+
cronCurrentJob: deps.cronCurrentJob?.() ?? null,
|
|
1022
|
+
enqueueCron: deps.enqueueCron,
|
|
1023
|
+
requestShutdown: deps.requestShutdown,
|
|
1024
|
+
sleepProgress: deps.sleepProgress,
|
|
1025
|
+
loadedCapabilities: deps.loadedCapabilities,
|
|
1026
|
+
selfHealerTask: deps.selfHealerTask,
|
|
1027
|
+
hailMary: deps.hailMary,
|
|
1028
|
+
rebuildTransport: deps.rebuildTransport,
|
|
1029
|
+
phaseHealth: deps.phaseHealth,
|
|
1030
|
+
registry: deps.registry,
|
|
1031
|
+
bridgeLockPath: deps.bridgeLockPath,
|
|
1032
|
+
conversationBuffer: msg.isGroup ? conversationBuffer : void 0,
|
|
1033
|
+
bufKey: msg.isGroup ? `${msg.platform}:${msg.channelId}` : void 0
|
|
1034
|
+
};
|
|
1035
|
+
if (await handleCommand(ctx.text, cmdCtx)) {
|
|
1036
|
+
ctx.handled = true;
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
if (ctx.text.startsWith("//")) {
|
|
1040
|
+
ctx.text = ctx.text.slice(1);
|
|
1041
|
+
}
|
|
1042
|
+
if (transport.transportCommands.includes(cmd) && transport.executeCommand) {
|
|
1043
|
+
const result = await transport.executeCommand(ctx.text);
|
|
1044
|
+
await ctx.reply(result);
|
|
1045
|
+
ctx.handled = true;
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
await next();
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
// src/components/pipeline/busy-guard.ts
|
|
1052
|
+
init_logger();
|
|
1053
|
+
var MAX_QUEUE_DEPTH = 20;
|
|
1054
|
+
var busyGuardMiddleware = async (ctx, next) => {
|
|
1055
|
+
const { msg, adapter, deps } = ctx;
|
|
1056
|
+
const userId = msg.userId;
|
|
1057
|
+
const activeId = deps.sessionManager.getActiveSessionId(userId, msg.platform);
|
|
1058
|
+
const entry = deps.sessions.getOrCreate(activeId);
|
|
1059
|
+
if (entry.busy) {
|
|
1060
|
+
const text = ctx.text.trim();
|
|
1061
|
+
const lower = text.toLowerCase();
|
|
1062
|
+
if (lower === "/stop" || lower === "/ctrlc") {
|
|
1063
|
+
logInfo("busy-guard", `STOP interrupt for ${activeId}`);
|
|
1064
|
+
await deps.transport.sendInterrupt();
|
|
1065
|
+
entry.busy = false;
|
|
1066
|
+
try {
|
|
1067
|
+
await adapter.sendMessage(msg.channelId, "\u{1F6D1} Stopped.", { threadId: msg.threadId });
|
|
1068
|
+
} catch {
|
|
1069
|
+
}
|
|
1070
|
+
ctx.handled = true;
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
if (lower.startsWith("/wait") || lower.startsWith("/steer")) {
|
|
1074
|
+
const body = text.replace(/^\/(wait|steer)\s*/i, "").trim();
|
|
1075
|
+
const steer = body ? `[USER] Wait! ${body}` : "[USER] Wait!";
|
|
1076
|
+
entry.pendingWait = entry.pendingWait ? entry.pendingWait + "\n" + steer : steer;
|
|
1077
|
+
logInfo("busy-guard", `Steer queued for ${activeId}: "${body || "(no message)"}"`);
|
|
1078
|
+
try {
|
|
1079
|
+
await adapter.sendMessage(msg.channelId, "\u{1F4CC} Noted.", { threadId: msg.threadId });
|
|
1080
|
+
} catch {
|
|
1081
|
+
}
|
|
1082
|
+
ctx.handled = true;
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
if (lower === "wait") {
|
|
1086
|
+
logInfo("busy-guard", `Legacy WAIT interrupt for ${activeId}`);
|
|
1087
|
+
await deps.transport.sendInterrupt();
|
|
1088
|
+
entry.busy = false;
|
|
1089
|
+
try {
|
|
1090
|
+
await adapter.sendMessage(msg.channelId, "\u{1F6D1} Stopped.", { threadId: msg.threadId });
|
|
1091
|
+
} catch {
|
|
1092
|
+
}
|
|
1093
|
+
ctx.handled = true;
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
if (entry.queue.length >= MAX_QUEUE_DEPTH) {
|
|
1097
|
+
const dropped = entry.queue.length - MAX_QUEUE_DEPTH + 1;
|
|
1098
|
+
entry.queue.splice(0, dropped);
|
|
1099
|
+
logWarn("busy-guard", `Queue overflow for ${activeId} \u2014 dropped ${dropped} oldest message(s)`);
|
|
1100
|
+
}
|
|
1101
|
+
entry.queue.push({ msg, adapter });
|
|
1102
|
+
logDebug("busy-guard", `Queued "${ctx.text.slice(0, 40)}" for ${activeId} (${entry.queue.length} pending)`);
|
|
1103
|
+
if (!ctx.deferReply) {
|
|
1104
|
+
if (entry.compacting) {
|
|
1105
|
+
try {
|
|
1106
|
+
await adapter.sendMessage(msg.channelId, "\u2615 Hold on, just tidying up my thoughts over coffee... I'll get to you in a moment!", { threadId: msg.threadId });
|
|
1107
|
+
} catch {
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
logDebug("busy-guard", `Queue: ${entry.compacting ? "compacting" : `queued (${entry.queue.length})`} for ${activeId}`);
|
|
1111
|
+
}
|
|
1112
|
+
ctx.handled = true;
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
await next();
|
|
1116
|
+
};
|
|
1117
|
+
|
|
1118
|
+
// src/components/pipeline/prompt-builder.ts
|
|
1119
|
+
init_log_and_swallow();
|
|
1120
|
+
init_logger();
|
|
1121
|
+
|
|
1122
|
+
// src/components/message-interceptor.ts
|
|
1123
|
+
init_paths();
|
|
1124
|
+
init_logger();
|
|
1125
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
1126
|
+
import { join } from "node:path";
|
|
1127
|
+
var TAG2 = "MessageInterceptor";
|
|
1128
|
+
var OVERFLOW_DIR = join(abtarsHome(), "overflow");
|
|
1129
|
+
var DEFAULT_THRESHOLD = 8e3;
|
|
1130
|
+
var PREVIEW_LENGTH = 500;
|
|
1131
|
+
function interceptLargeMessage(text, threshold = DEFAULT_THRESHOLD) {
|
|
1132
|
+
if (text.length <= threshold) {
|
|
1133
|
+
return { text, intercepted: false };
|
|
1134
|
+
}
|
|
1135
|
+
mkdirSync(OVERFLOW_DIR, { recursive: true });
|
|
1136
|
+
const filename = `overflow_${Date.now()}.md`;
|
|
1137
|
+
const filePath = join(OVERFLOW_DIR, filename);
|
|
1138
|
+
writeFileSync(filePath, text, "utf-8");
|
|
1139
|
+
const preview = text.slice(0, PREVIEW_LENGTH);
|
|
1140
|
+
const replaced = `${preview}
|
|
1141
|
+
|
|
1142
|
+
---
|
|
1143
|
+
\u26A0\uFE0F Message truncated (${text.length} chars). Full content saved to: ${filePath}`;
|
|
1144
|
+
logInfo(TAG2, `Intercepted oversized message (${text.length} chars) \u2192 ${filePath}`);
|
|
1145
|
+
return { text: replaced, intercepted: true, filePath };
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// src/components/pipeline/prompt-builder.ts
|
|
1149
|
+
init_env_schema();
|
|
1150
|
+
var TAG3 = "pipeline";
|
|
1151
|
+
var ACTIVE_MEMORY_LIMIT = 5;
|
|
1152
|
+
async function buildPrompt(msg, text, deps, registry) {
|
|
1153
|
+
const { memory, sessions, conversationBuffer, contextPercent } = deps;
|
|
1154
|
+
const { channelId, isGroup } = msg;
|
|
1155
|
+
const userId = msg.userId;
|
|
1156
|
+
const sessionKey = deps.sessionManager.getActiveSessionId(userId, msg.platform);
|
|
1157
|
+
const bufKey = `${msg.platform}:${channelId}`;
|
|
1158
|
+
let prompt = `[${localTime()}] ${text}`;
|
|
1159
|
+
let imageContent;
|
|
1160
|
+
if (msg.mediaPath) {
|
|
1161
|
+
if (deps.isAcp) {
|
|
1162
|
+
prompt += `
|
|
1163
|
+
Image saved at: ${msg.mediaPath}`;
|
|
1164
|
+
} else {
|
|
1165
|
+
const { readFileSync: readFileSync3 } = await import("node:fs");
|
|
1166
|
+
const ext = msg.mediaPath.split(".").pop()?.toLowerCase();
|
|
1167
|
+
const visionMimes = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp" };
|
|
1168
|
+
const mime = ext ? visionMimes[ext] : void 0;
|
|
1169
|
+
if (mime) {
|
|
1170
|
+
try {
|
|
1171
|
+
const buf = readFileSync3(msg.mediaPath);
|
|
1172
|
+
const b64 = buf.toString("base64");
|
|
1173
|
+
const maxCtxPct = parseInt(process.env["IMAGE_MAX_CONTEXT_PCT"] ?? "30", 10);
|
|
1174
|
+
const maxContext = deps.maxContext ?? 128e3;
|
|
1175
|
+
const imgTokens = Math.ceil(b64.length / 4);
|
|
1176
|
+
if (imgTokens <= maxContext * (maxCtxPct / 100)) {
|
|
1177
|
+
imageContent = { mime, base64: b64, path: msg.mediaPath };
|
|
1178
|
+
} else {
|
|
1179
|
+
prompt += `
|
|
1180
|
+
\u26A0\uFE0F Image too large. Saved at: ${msg.mediaPath}`;
|
|
1181
|
+
}
|
|
1182
|
+
} catch {
|
|
1183
|
+
prompt += `
|
|
1184
|
+
File saved at: ${msg.mediaPath}`;
|
|
1185
|
+
}
|
|
1186
|
+
} else {
|
|
1187
|
+
prompt += `
|
|
1188
|
+
File saved at: ${msg.mediaPath}`;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
if (isGroup) {
|
|
1193
|
+
const context = conversationBuffer.drain(bufKey);
|
|
1194
|
+
if (context) {
|
|
1195
|
+
prompt = context + text;
|
|
1196
|
+
logDebug(TAG3, "Prepended group context to prompt");
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
const entry = sessions.getOrCreate(sessionKey);
|
|
1200
|
+
const isSessionStart = entry.pendingStart || !entry.seen;
|
|
1201
|
+
logTrace(TAG3, `session-state: key=${sessionKey} seen=${entry.seen} pendingStart=${entry.pendingStart} isSessionStart=${isSessionStart}`);
|
|
1202
|
+
if (isSessionStart && memory) {
|
|
1203
|
+
prompt = buildSessionStartPrompt(prompt, memory, userId, sessionKey, deps.maxContext);
|
|
1204
|
+
}
|
|
1205
|
+
entry.seen = true;
|
|
1206
|
+
entry.pendingStart = false;
|
|
1207
|
+
const userRole = registry.byUserId.get(userId)?.role;
|
|
1208
|
+
if (memory && userRole !== "guest") {
|
|
1209
|
+
const numericMsgId = typeof msg.messageId === "number" ? msg.messageId : void 0;
|
|
1210
|
+
memory.recordMessage({ role: "user", content: text, timestamp: Date.now(), userId, sessionId: sessionKey, platformMessageId: numericMsgId });
|
|
1211
|
+
}
|
|
1212
|
+
if (getEnv().activeMemory && memory) {
|
|
1213
|
+
const userEntry = registry.byUserId.get(userId);
|
|
1214
|
+
if (userEntry?.role !== "guest" && (contextPercent < 0 || contextPercent < getEnv().ctxCompactPct)) {
|
|
1215
|
+
try {
|
|
1216
|
+
const t0 = performance.now();
|
|
1217
|
+
const priming = sessions.get(sessionKey)?.primingTerms ?? [];
|
|
1218
|
+
const now = /* @__PURE__ */ new Date();
|
|
1219
|
+
const recall = await memory.recallSearch({
|
|
1220
|
+
translated: [.../* @__PURE__ */ new Set([text, ...priming])],
|
|
1221
|
+
original: text,
|
|
1222
|
+
userId,
|
|
1223
|
+
limit: ACTIVE_MEMORY_LIMIT,
|
|
1224
|
+
maxClassification: userEntry?.maxClass ?? 0,
|
|
1225
|
+
stages: ["Sf", "Ss"],
|
|
1226
|
+
currentContext: { hour: now.getHours(), dayOfWeek: now.getDay() }
|
|
1227
|
+
});
|
|
1228
|
+
const TRIVIAL_TTL_MS = 36 * 60 * 6e4;
|
|
1229
|
+
const nowMs = Date.now();
|
|
1230
|
+
const hits = recall.results.filter((h) => {
|
|
1231
|
+
if (h.score <= 0.7) return false;
|
|
1232
|
+
if (h.memoryType === "fact" && h.score < 1 && h.createdAt && nowMs - h.createdAt > TRIVIAL_TTL_MS) {
|
|
1233
|
+
if (!h.emotionTags && !h.importanceFlags) return false;
|
|
1234
|
+
}
|
|
1235
|
+
return true;
|
|
1236
|
+
});
|
|
1237
|
+
if (hits.length > 0) {
|
|
1238
|
+
const lines = hits.map((h) => abmind().renderMemory({
|
|
1239
|
+
content_en: h.content,
|
|
1240
|
+
topic: h.topic ?? void 0,
|
|
1241
|
+
emotion_tags: h.emotionTags ?? void 0,
|
|
1242
|
+
importance_flags: h.importanceFlags ?? void 0,
|
|
1243
|
+
memory_type: h.memoryType ?? void 0,
|
|
1244
|
+
confidence: h.confidence ?? void 0,
|
|
1245
|
+
createdAt: h.createdAt
|
|
1246
|
+
}));
|
|
1247
|
+
const block = `[MEMORY CONTEXT \u2014 auto-recalled, do not repeat verbatim]
|
|
1248
|
+
${lines.join("\n")}
|
|
1249
|
+
[/MEMORY CONTEXT]
|
|
1250
|
+
|
|
1251
|
+
`;
|
|
1252
|
+
prompt = block + prompt;
|
|
1253
|
+
logDebug(TAG3, `Active recall: ${hits.length} hits, ${block.length} chars, ${Math.round(performance.now() - t0)}ms`);
|
|
1254
|
+
logTrace(TAG3, `recall content: ${block}`);
|
|
1255
|
+
}
|
|
1256
|
+
} catch (err) {
|
|
1257
|
+
logDebug(TAG3, `Active recall failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
if (!isSessionStart) {
|
|
1262
|
+
prompt = interceptLargeMessage(prompt).text;
|
|
1263
|
+
}
|
|
1264
|
+
if (userRole !== "master" && text.length > 10) {
|
|
1265
|
+
const scanForInjection = abmind().scanForInjection;
|
|
1266
|
+
const scan = scanForInjection(text);
|
|
1267
|
+
if (!scan.safe) {
|
|
1268
|
+
logInfo(TAG3, `Injection blocked from ${userId}: ${scan.flags.map((f) => f.category).join(", ")}`);
|
|
1269
|
+
return { prompt: "__INJECTION_BLOCKED__", isSessionStart, imageContent: void 0 };
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
return { prompt, isSessionStart, imageContent };
|
|
1273
|
+
}
|
|
1274
|
+
function buildSessionStartPrompt(prompt, memory, userId, sessionKey, maxContext) {
|
|
1275
|
+
const contextParts = [];
|
|
1276
|
+
const reason = readAndClearRestartReason();
|
|
1277
|
+
if (reason) {
|
|
1278
|
+
contextParts.push(`[SESSION START REASON] ${reason}`);
|
|
1279
|
+
logInfo(TAG3, `Injected restart reason: ${reason}`);
|
|
1280
|
+
}
|
|
1281
|
+
let sessionType = "A";
|
|
1282
|
+
if (sessionKey) {
|
|
1283
|
+
const parts = sessionKey.split("_");
|
|
1284
|
+
if (parts.length === 3) {
|
|
1285
|
+
sessionType = parts[1];
|
|
1286
|
+
const typeMap = { A: "Main", B: "Browse", C: "Code", T: "Task" };
|
|
1287
|
+
const type = typeMap[sessionType] ?? sessionType;
|
|
1288
|
+
const index = parseInt(parts[2], 10);
|
|
1289
|
+
contextParts.push(`[SESSION] #${index} (${type})`);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
const isCodeSession = sessionType === "C";
|
|
1293
|
+
if (isCodeSession) {
|
|
1294
|
+
const minimal = loadMinimalSoul(memory);
|
|
1295
|
+
if (minimal) {
|
|
1296
|
+
contextParts.push(minimal);
|
|
1297
|
+
logInfo(TAG3, `Injected minimal soul for Code session (${minimal.length} chars)`);
|
|
1298
|
+
}
|
|
1299
|
+
} else {
|
|
1300
|
+
const soul = loadSoulBundle(memory);
|
|
1301
|
+
if (soul) {
|
|
1302
|
+
contextParts.push(soul);
|
|
1303
|
+
logInfo(TAG3, `Injected soul bundle (${soul.length} chars)`);
|
|
1304
|
+
} else {
|
|
1305
|
+
contextParts.push("[\u26A0\uFE0F SOUL BUNDLE MISSING] Your persona files failed to load. Alert the user immediately and request a /reset.");
|
|
1306
|
+
logWarn(TAG3, "Soul bundle empty \u2014 injected missing-soul warning");
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
if (sessionKey) {
|
|
1310
|
+
try {
|
|
1311
|
+
const registry = loadUsers();
|
|
1312
|
+
const user = registry.byUserId.get(userId);
|
|
1313
|
+
if (user) {
|
|
1314
|
+
const CLASS_NAMES = ["UNCLASSIFIED", "RESTRICTED", "CONFIDENTIAL", "SECRET"];
|
|
1315
|
+
const lang = user.languages?.length ? `
|
|
1316
|
+
Their languages: ${user.languages.join(", ")}. Respond ONLY in these languages.` : "";
|
|
1317
|
+
const userBlock = `[CURRENT USER]
|
|
1318
|
+
You are now talking to ${user.userId} (${user.role}, ${CLASS_NAMES[user.maxClass] ?? `class ${user.maxClass}`} clearance).${lang}`;
|
|
1319
|
+
contextParts.push(userBlock);
|
|
1320
|
+
logInfo(TAG3, `Injected [CURRENT USER] for ${user.userId} (${user.role})`);
|
|
1321
|
+
} else {
|
|
1322
|
+
logInfo(TAG3, `[CURRENT USER] skipped \u2014 userId "${userId}" not found in registry (${registry.byUserId.size} users loaded)`);
|
|
1323
|
+
}
|
|
1324
|
+
} catch (err) {
|
|
1325
|
+
logAndSwallow("prompt_builder", "op", err);
|
|
1326
|
+
}
|
|
1327
|
+
} else {
|
|
1328
|
+
logInfo(TAG3, `[CURRENT USER] skipped \u2014 no sessionKey`);
|
|
1329
|
+
}
|
|
1330
|
+
const compSummary = null;
|
|
1331
|
+
if (compSummary && sessionKey) {
|
|
1332
|
+
} else {
|
|
1333
|
+
const ctxOpts = isCodeSession ? { skipDailies: true, maxAgeMs: 48 * 60 * 60 * 1e3 } : void 0;
|
|
1334
|
+
const ctx = abmind()?.buildSessionStartContext(memory, userId, maxContext, ctxOpts);
|
|
1335
|
+
if (ctx) {
|
|
1336
|
+
contextParts.push(ctx);
|
|
1337
|
+
logInfo(TAG3, `Injected session-start context (${ctx.length} chars${isCodeSession ? ", Code mode" : ""})`);
|
|
1338
|
+
logTrace(TAG3, `session-start content: ${ctx}`);
|
|
1339
|
+
}
|
|
1340
|
+
try {
|
|
1341
|
+
const userRole = loadUsers().byUserId.get(userId)?.role ?? "master";
|
|
1342
|
+
if (userRole === "guest") {
|
|
1343
|
+
contextParts.push("Hi! How can I help?");
|
|
1344
|
+
} else if (userRole === "user") {
|
|
1345
|
+
contextParts.push("[SESSION START] Returning user. Be friendly and helpful.");
|
|
1346
|
+
} else if (!isCodeSession) {
|
|
1347
|
+
const wakeUp = memory.buildWakeUp();
|
|
1348
|
+
if (wakeUp) {
|
|
1349
|
+
contextParts.push(wakeUp);
|
|
1350
|
+
logInfo(TAG3, `Injected ABM wake-up (${wakeUp.length} chars)`);
|
|
1351
|
+
logTrace(TAG3, `wake-up content: ${wakeUp}`);
|
|
1352
|
+
}
|
|
1353
|
+
if (sessionKey && !sessionKey.includes("_C_")) {
|
|
1354
|
+
const status = abmind().buildStatusBlock(memory);
|
|
1355
|
+
if (status) contextParts.push(status);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
} catch (err) {
|
|
1359
|
+
logAndSwallow("prompt_builder", "op", err);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
const contextBlock = contextParts.length > 0 ? `[CONTEXT \u2014 do not respond to this section]
|
|
1363
|
+
${contextParts.join("\n\n")}
|
|
1364
|
+
[/CONTEXT]
|
|
1365
|
+
|
|
1366
|
+
` : "";
|
|
1367
|
+
const result = contextBlock + prompt;
|
|
1368
|
+
logTrace(TAG3, `session-start assembled: ${contextParts.length} parts, context=${contextBlock.length} chars, prompt=${prompt.length} chars, total=${result.length} chars`);
|
|
1369
|
+
if (result.length < 5e3) {
|
|
1370
|
+
logInfo(TAG3, `Session-start prompt suspiciously small (${result.length} chars) \u2014 SOUL may be missing`);
|
|
1371
|
+
}
|
|
1372
|
+
if (maxContext && contextBlock.length > maxContext * 0.15) {
|
|
1373
|
+
logWarn(TAG3, `\u26A0\uFE0F Session injection is ${Math.round(contextBlock.length / maxContext * 100)}% of context window \u2014 consider reducing SESSION_HISTORY_PCT`);
|
|
1374
|
+
}
|
|
1375
|
+
return result;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// src/components/message-pipeline.ts
|
|
1379
|
+
init_env_schema();
|
|
1380
|
+
|
|
1381
|
+
// src/components/session-registry.ts
|
|
1382
|
+
function createEntry() {
|
|
1383
|
+
return {
|
|
1384
|
+
busy: false,
|
|
1385
|
+
queue: [],
|
|
1386
|
+
fullMode: false,
|
|
1387
|
+
pendingStart: false,
|
|
1388
|
+
seen: false,
|
|
1389
|
+
compacting: false,
|
|
1390
|
+
ctxWarned: false,
|
|
1391
|
+
compactFailures: 0,
|
|
1392
|
+
primingTerms: [],
|
|
1393
|
+
lastActiveAt: Date.now()
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
var SessionRegistry = class {
|
|
1397
|
+
entries = /* @__PURE__ */ new Map();
|
|
1398
|
+
get(key) {
|
|
1399
|
+
return this.entries.get(key);
|
|
1400
|
+
}
|
|
1401
|
+
getOrCreate(key) {
|
|
1402
|
+
let entry = this.entries.get(key);
|
|
1403
|
+
if (!entry) {
|
|
1404
|
+
entry = createEntry();
|
|
1405
|
+
this.entries.set(key, entry);
|
|
1406
|
+
}
|
|
1407
|
+
entry.lastActiveAt = Date.now();
|
|
1408
|
+
return entry;
|
|
1409
|
+
}
|
|
1410
|
+
delete(key) {
|
|
1411
|
+
this.entries.delete(key);
|
|
1412
|
+
}
|
|
1413
|
+
/** Mark every tracked session as needing session-start injection on its next message. */
|
|
1414
|
+
has(key) {
|
|
1415
|
+
return this.entries.has(key);
|
|
1416
|
+
}
|
|
1417
|
+
keys() {
|
|
1418
|
+
return this.entries.keys();
|
|
1419
|
+
}
|
|
1420
|
+
get size() {
|
|
1421
|
+
return this.entries.size;
|
|
1422
|
+
}
|
|
1423
|
+
/** JSON-serializable snapshot for dashboard. */
|
|
1424
|
+
snapshot() {
|
|
1425
|
+
return Object.fromEntries(this.entries);
|
|
1426
|
+
}
|
|
1427
|
+
/** Remove idle entries older than maxAgeMs. Returns count pruned. */
|
|
1428
|
+
prune(maxAgeMs) {
|
|
1429
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
1430
|
+
let pruned = 0;
|
|
1431
|
+
for (const [key, entry] of this.entries) {
|
|
1432
|
+
if (entry.lastActiveAt < cutoff && !entry.busy && !entry.compacting && entry.queue.length === 0) {
|
|
1433
|
+
this.entries.delete(key);
|
|
1434
|
+
pruned++;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
return pruned;
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
// src/components/message-pipeline.ts
|
|
1442
|
+
async function retrySend(fn, attempts = 3) {
|
|
1443
|
+
for (let i = 0; i < attempts; i++) {
|
|
1444
|
+
try {
|
|
1445
|
+
return await fn();
|
|
1446
|
+
} catch (err) {
|
|
1447
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1448
|
+
const transient = msg.includes("fetch failed") || msg.includes("ETIMEDOUT") || msg.includes("ECONNRESET") || /^5\d\d/.test(msg);
|
|
1449
|
+
if (!transient || i === attempts - 1) throw err;
|
|
1450
|
+
const delay = 1e3 * Math.pow(3, i);
|
|
1451
|
+
logWarn("pipeline", `Delivery failed (attempt ${i + 1}/${attempts}), retrying in ${delay}ms: ${msg}`);
|
|
1452
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
throw new Error("unreachable");
|
|
1456
|
+
}
|
|
1457
|
+
var TAG4 = "pipeline";
|
|
1458
|
+
var PRIMING_MAX = 8;
|
|
1459
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
1460
|
+
"the",
|
|
1461
|
+
"a",
|
|
1462
|
+
"an",
|
|
1463
|
+
"is",
|
|
1464
|
+
"are",
|
|
1465
|
+
"was",
|
|
1466
|
+
"were",
|
|
1467
|
+
"be",
|
|
1468
|
+
"been",
|
|
1469
|
+
"have",
|
|
1470
|
+
"has",
|
|
1471
|
+
"had",
|
|
1472
|
+
"do",
|
|
1473
|
+
"does",
|
|
1474
|
+
"did",
|
|
1475
|
+
"will",
|
|
1476
|
+
"would",
|
|
1477
|
+
"could",
|
|
1478
|
+
"should",
|
|
1479
|
+
"can",
|
|
1480
|
+
"may",
|
|
1481
|
+
"might",
|
|
1482
|
+
"shall",
|
|
1483
|
+
"it",
|
|
1484
|
+
"its",
|
|
1485
|
+
"this",
|
|
1486
|
+
"that",
|
|
1487
|
+
"what",
|
|
1488
|
+
"how",
|
|
1489
|
+
"when",
|
|
1490
|
+
"where",
|
|
1491
|
+
"who",
|
|
1492
|
+
"which",
|
|
1493
|
+
"why",
|
|
1494
|
+
"about",
|
|
1495
|
+
"for",
|
|
1496
|
+
"with",
|
|
1497
|
+
"from",
|
|
1498
|
+
"into",
|
|
1499
|
+
"just",
|
|
1500
|
+
"also",
|
|
1501
|
+
"very",
|
|
1502
|
+
"not",
|
|
1503
|
+
"but",
|
|
1504
|
+
"and",
|
|
1505
|
+
"or",
|
|
1506
|
+
"if",
|
|
1507
|
+
"so",
|
|
1508
|
+
"too",
|
|
1509
|
+
"let",
|
|
1510
|
+
"lets",
|
|
1511
|
+
"dont",
|
|
1512
|
+
"you",
|
|
1513
|
+
"we",
|
|
1514
|
+
"my",
|
|
1515
|
+
"your",
|
|
1516
|
+
"our",
|
|
1517
|
+
"me",
|
|
1518
|
+
"us",
|
|
1519
|
+
"them",
|
|
1520
|
+
"they",
|
|
1521
|
+
"he",
|
|
1522
|
+
"she"
|
|
1523
|
+
]);
|
|
1524
|
+
function extractKeywords(text) {
|
|
1525
|
+
return text.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter((w) => w.length >= 3 && !STOPWORDS.has(w)).slice(0, 3);
|
|
1526
|
+
}
|
|
1527
|
+
var resetIdleCompactFlag = null;
|
|
1528
|
+
function setIdleCompactReset(fn) {
|
|
1529
|
+
resetIdleCompactFlag = fn;
|
|
1530
|
+
}
|
|
1531
|
+
async function resetAndPrepare(opts) {
|
|
1532
|
+
await opts.transport.resetSession(opts.sessionKey);
|
|
1533
|
+
if (opts.conversationBuffer && opts.bufKey) opts.conversationBuffer.clear(opts.bufKey);
|
|
1534
|
+
opts.sessions.delete(opts.sessionKey);
|
|
1535
|
+
opts.sessions.getOrCreate(opts.sessionKey).pendingStart = true;
|
|
1536
|
+
const t = opts.transport;
|
|
1537
|
+
t.setEmergencyMode?.(null);
|
|
1538
|
+
}
|
|
1539
|
+
async function handleInboundMessage(msg, adapter, deps) {
|
|
1540
|
+
const ctx = createMessageContext(msg, adapter, deps);
|
|
1541
|
+
await runPipeline(ctx, [voiceMiddleware, commandMiddleware, busyGuardMiddleware]);
|
|
1542
|
+
if (ctx.handled) return;
|
|
1543
|
+
if (hasHooks("BeforeMessage")) {
|
|
1544
|
+
const result = await fire("BeforeMessage", {
|
|
1545
|
+
event: "BeforeMessage",
|
|
1546
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1547
|
+
sessionKey: "",
|
|
1548
|
+
platform: msg.platform,
|
|
1549
|
+
userId: msg.userId,
|
|
1550
|
+
chatId: String(ctx.chatId),
|
|
1551
|
+
text: ctx.text
|
|
1552
|
+
});
|
|
1553
|
+
if (result?.decision === "block") {
|
|
1554
|
+
logInfo(TAG4, `BeforeMessage hook blocked: ${result.reason ?? "no reason"}`);
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
const {
|
|
1559
|
+
transport,
|
|
1560
|
+
memory,
|
|
1561
|
+
memoryConfig,
|
|
1562
|
+
idleSave,
|
|
1563
|
+
conversationBuffer,
|
|
1564
|
+
ttsConfig,
|
|
1565
|
+
sessions
|
|
1566
|
+
} = deps;
|
|
1567
|
+
const { channelId, isVoice } = msg;
|
|
1568
|
+
const chatId = ctx.chatId;
|
|
1569
|
+
const text = ctx.text;
|
|
1570
|
+
const registry = loadUsers();
|
|
1571
|
+
const userId = msg.userId;
|
|
1572
|
+
const activeSessionId = deps.sessionManager.getActiveSessionId(userId, msg.platform);
|
|
1573
|
+
const busyEntry = sessions.getOrCreate(activeSessionId);
|
|
1574
|
+
let typingInterval;
|
|
1575
|
+
let typingTtlTimer;
|
|
1576
|
+
let toolElapsedTimer;
|
|
1577
|
+
let streamMsgId;
|
|
1578
|
+
try {
|
|
1579
|
+
busyEntry.busy = true;
|
|
1580
|
+
resetIdleCompactFlag?.();
|
|
1581
|
+
const ctxPct = transport.contextPercent;
|
|
1582
|
+
logInfo(TAG4, `\u2190 [${msg.platform}] ${isVoice ? "\u{1F3A4} " : ""}"${text.slice(0, 60)}"${ctxPct >= 0 ? ` (ctx: ${ctxPct}%)` : ""}`);
|
|
1583
|
+
const { prompt: builtPrompt, imageContent } = await buildPrompt(msg, text, {
|
|
1584
|
+
memory,
|
|
1585
|
+
memoryConfig,
|
|
1586
|
+
sessions,
|
|
1587
|
+
sessionManager: deps.sessionManager,
|
|
1588
|
+
conversationBuffer,
|
|
1589
|
+
contextPercent: ctxPct,
|
|
1590
|
+
maxContext: deps.maxContext,
|
|
1591
|
+
isAcp: !("agentLoop" in transport)
|
|
1592
|
+
}, registry);
|
|
1593
|
+
if (builtPrompt === "__INJECTION_BLOCKED__") {
|
|
1594
|
+
await adapter.sendMessage(channelId, "\u26D4 Message blocked \u2014 suspicious content detected.", { threadId: msg.threadId });
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
let prompt = builtPrompt;
|
|
1598
|
+
const { drainCompletions } = await import("./completion-buffer-S3LXDZG2.js");
|
|
1599
|
+
const completions = drainCompletions(activeSessionId);
|
|
1600
|
+
if (completions.length > 0) {
|
|
1601
|
+
const notes = completions.map((c) => {
|
|
1602
|
+
const cost = c.inputTokens + c.outputTokens > 0 ? ` [${((c.inputTokens + c.outputTokens) / 1e3).toFixed(1)}k tokens]` : "";
|
|
1603
|
+
return `[SYSTEM] Background session ${c.sessionId} ${c.status}
|
|
1604
|
+
Goal: ${c.goal}
|
|
1605
|
+
Result: ${c.result}${cost}`;
|
|
1606
|
+
}).join("\n\n");
|
|
1607
|
+
prompt = `${notes}
|
|
1608
|
+
|
|
1609
|
+
---
|
|
1610
|
+
|
|
1611
|
+
${prompt}`;
|
|
1612
|
+
}
|
|
1613
|
+
const activeSession = deps.sessionManager.getActiveSession(userId, msg.platform);
|
|
1614
|
+
const agentSession = activeSession.agentSession;
|
|
1615
|
+
logDebug(TAG4, `Route: session=${activeSessionId} type=${activeSession.type} agentSession=${agentSession ? "yes" : "no"}`);
|
|
1616
|
+
if ("sandboxPolicy" in transport) {
|
|
1617
|
+
const { buildPolicy } = await import("./tool-sandbox-OZMXJZLQ.js");
|
|
1618
|
+
transport.sandboxPolicy = buildPolicy("owner");
|
|
1619
|
+
}
|
|
1620
|
+
if ("isPaused" in transport) {
|
|
1621
|
+
transport.isPaused = () => activeSession.paused;
|
|
1622
|
+
}
|
|
1623
|
+
if ("getPendingInstruction" in transport) {
|
|
1624
|
+
const sessionEntry = deps.sessions.getOrCreate(activeSessionId);
|
|
1625
|
+
transport.getPendingInstruction = () => {
|
|
1626
|
+
const pending = sessionEntry.pendingWait;
|
|
1627
|
+
if (!pending) return void 0;
|
|
1628
|
+
sessionEntry.pendingWait = void 0;
|
|
1629
|
+
return pending;
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
const responsePromise = agentSession ? agentSession.sendPrompt(activeSessionId, prompt, imageContent) : transport.sendPrompt(activeSessionId, prompt, imageContent, userId);
|
|
1633
|
+
if (!isVoice && adapter.setReaction && msg.messageId) {
|
|
1634
|
+
await adapter.setReaction(channelId, msg.messageId, "\u{1F440}");
|
|
1635
|
+
}
|
|
1636
|
+
if (adapter.sendTyping) {
|
|
1637
|
+
await adapter.sendTyping(channelId, msg.threadId);
|
|
1638
|
+
typingInterval = setInterval(() => {
|
|
1639
|
+
adapter.sendTyping(channelId, msg.threadId).catch((err) => logAndSwallow(TAG4, "adapter call", err));
|
|
1640
|
+
}, 8e3);
|
|
1641
|
+
}
|
|
1642
|
+
const TYPING_TTL_MS = getEnv().typingTtlMs;
|
|
1643
|
+
typingTtlTimer = setTimeout(() => {
|
|
1644
|
+
if (typingInterval) {
|
|
1645
|
+
clearInterval(typingInterval);
|
|
1646
|
+
typingInterval = void 0;
|
|
1647
|
+
}
|
|
1648
|
+
}, TYPING_TTL_MS);
|
|
1649
|
+
let lastToolNotifyAt = 0;
|
|
1650
|
+
let toolBatch = [];
|
|
1651
|
+
let toolBatchTimer;
|
|
1652
|
+
let currentToolName = "";
|
|
1653
|
+
let toolStartAt = 0;
|
|
1654
|
+
let toolCallCount = 0;
|
|
1655
|
+
let totalToolStartAt = 0;
|
|
1656
|
+
transport.onToolCallStart = (toolName) => {
|
|
1657
|
+
toolCallCount++;
|
|
1658
|
+
if (!totalToolStartAt) totalToolStartAt = Date.now();
|
|
1659
|
+
currentToolName = toolName;
|
|
1660
|
+
toolStartAt = Date.now();
|
|
1661
|
+
adapter.sendTyping?.(channelId, msg.threadId).catch((err) => logAndSwallow(TAG4, "adapter call", err));
|
|
1662
|
+
if (toolElapsedTimer) {
|
|
1663
|
+
clearInterval(toolElapsedTimer);
|
|
1664
|
+
toolElapsedTimer = void 0;
|
|
1665
|
+
}
|
|
1666
|
+
toolBatch.push(toolName);
|
|
1667
|
+
if (!toolBatchTimer) {
|
|
1668
|
+
toolBatchTimer = setTimeout(async () => {
|
|
1669
|
+
const now = Date.now();
|
|
1670
|
+
if (now - lastToolNotifyAt >= 1e4) {
|
|
1671
|
+
const names = toolBatch.join(", ");
|
|
1672
|
+
const status = `\u{1F527} ${names}...`;
|
|
1673
|
+
if (streamMsgId && adapter.editMessage) {
|
|
1674
|
+
adapter.editMessage(channelId, streamMsgId, status + "...").catch((err) => logAndSwallow(TAG4, "adapter call", err));
|
|
1675
|
+
} else {
|
|
1676
|
+
const id = await adapter.sendMessage(channelId, status, { threadId: msg.threadId }).catch((err) => {
|
|
1677
|
+
logAndSwallow(TAG4, "sendMessage tool status", err);
|
|
1678
|
+
return void 0;
|
|
1679
|
+
});
|
|
1680
|
+
if (id && adapter.editMessage) streamMsgId = id;
|
|
1681
|
+
}
|
|
1682
|
+
lastToolNotifyAt = now;
|
|
1683
|
+
}
|
|
1684
|
+
toolBatch = [];
|
|
1685
|
+
toolBatchTimer = void 0;
|
|
1686
|
+
}, 500);
|
|
1687
|
+
}
|
|
1688
|
+
toolElapsedTimer = setInterval(() => {
|
|
1689
|
+
const elapsed = Math.round((Date.now() - toolStartAt) / 1e3);
|
|
1690
|
+
const elapsedStr = elapsed >= 60 ? `${Math.floor(elapsed / 60)}m${elapsed % 60}s` : `${elapsed}s`;
|
|
1691
|
+
const status = `\u{1F527} ${currentToolName} (${elapsedStr})...`;
|
|
1692
|
+
if (streamMsgId && adapter.editMessage) {
|
|
1693
|
+
adapter.editMessage(channelId, streamMsgId, status + "...").catch((err) => logAndSwallow(TAG4, "adapter call", err));
|
|
1694
|
+
}
|
|
1695
|
+
}, 1e4);
|
|
1696
|
+
};
|
|
1697
|
+
if ("onFallback" in transport) {
|
|
1698
|
+
const prev = transport.onFallback;
|
|
1699
|
+
transport.onFallback = (model, _ctxPct, reason) => {
|
|
1700
|
+
prev?.(model, _ctxPct, reason);
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
let fullResponseSegments = [];
|
|
1704
|
+
transport.onSegmentBreak = (text2) => {
|
|
1705
|
+
fullResponseSegments.push(text2);
|
|
1706
|
+
if (streamMsgId && adapter.editMessage) {
|
|
1707
|
+
adapter.editMessage(channelId, streamMsgId, text2).catch((err) => logAndSwallow(TAG4, "adapter call", err));
|
|
1708
|
+
} else if (text2) {
|
|
1709
|
+
adapter.sendMessage(channelId, text2, { threadId: msg.threadId }).catch((err) => logAndSwallow(TAG4, "adapter call", err));
|
|
1710
|
+
}
|
|
1711
|
+
streamMsgId = void 0;
|
|
1712
|
+
};
|
|
1713
|
+
const response = await responsePromise;
|
|
1714
|
+
clearTimeout(toolBatchTimer);
|
|
1715
|
+
transport.onIntermediateResponse = void 0;
|
|
1716
|
+
logDebug(TAG4, `Response (${response.length} chars): "${response.trim().slice(0, 120)}"`);
|
|
1717
|
+
const cleanAnswer = transport.answerOnly;
|
|
1718
|
+
const rawResponse = sessions.get(activeSessionId)?.fullMode ? response : cleanAnswer || response;
|
|
1719
|
+
const { text: cleanedText, reactionEmoji, noReply, topics } = cleanResponse(rawResponse);
|
|
1720
|
+
let userResponse = cleanedText;
|
|
1721
|
+
for (const [key, val] of Object.entries(process.env)) {
|
|
1722
|
+
if (key.startsWith("SECRET_") && val && userResponse.includes(val)) {
|
|
1723
|
+
userResponse = userResponse.replaceAll(val, `[REDACTED:$${key}]`);
|
|
1724
|
+
logWarn(TAG4, `Redacted leaked secret $${key} from response`);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
if (!userResponse) {
|
|
1728
|
+
if (noReply) {
|
|
1729
|
+
logDebug(TAG4, "LLM returned [NO_REPLY], dropping silently");
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
if (reactionEmoji) {
|
|
1733
|
+
if (adapter.setReaction && msg.messageId) {
|
|
1734
|
+
try {
|
|
1735
|
+
await adapter.setReaction(channelId, msg.messageId, reactionEmoji);
|
|
1736
|
+
} catch {
|
|
1737
|
+
await adapter.sendMessage(channelId, reactionEmoji, { threadId: msg.threadId });
|
|
1738
|
+
}
|
|
1739
|
+
} else {
|
|
1740
|
+
await adapter.sendMessage(channelId, reactionEmoji, { threadId: msg.threadId });
|
|
1741
|
+
}
|
|
1742
|
+
return;
|
|
1743
|
+
}
|
|
1744
|
+
if (transport.toolCallsSucceeded > 0) {
|
|
1745
|
+
logDebug(TAG4, `Empty text but ${transport.toolCallsSucceeded} tool call(s) succeeded \u2014 suppressing fallback`);
|
|
1746
|
+
if (adapter.setReaction && msg.messageId) await adapter.setReaction(channelId, msg.messageId, "").catch((err) => logAndSwallow(TAG4, "adapter call", err));
|
|
1747
|
+
} else {
|
|
1748
|
+
logWarn(TAG4, "Empty response from transport");
|
|
1749
|
+
if (adapter.setReaction && msg.messageId) await adapter.setReaction(channelId, msg.messageId, "\u{1F937}");
|
|
1750
|
+
await adapter.sendMessage(channelId, "\u{1F937} Model returned an empty response. Try again or /reset.", { threadId: msg.threadId });
|
|
1751
|
+
}
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
if (adapter.setReaction && msg.messageId) {
|
|
1755
|
+
await adapter.setReaction(channelId, msg.messageId, "").catch((err) => logAndSwallow(TAG4, "adapter call", err));
|
|
1756
|
+
}
|
|
1757
|
+
const trimmed = userResponse.trim();
|
|
1758
|
+
const isEmojiOnly = /^[\p{Emoji_Presentation}\p{Extended_Pictographic}]{1,2}$/u.test(trimmed);
|
|
1759
|
+
if (isEmojiOnly) {
|
|
1760
|
+
await tryReaction(adapter, channelId, msg.messageId, trimmed, msg.threadId);
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
let lastSentMsgId;
|
|
1764
|
+
const chunks = adapter.chunkResponse(userResponse);
|
|
1765
|
+
logDebug(TAG4, `Sending ${chunks.length} chunk(s)`);
|
|
1766
|
+
for (const chunk of chunks) {
|
|
1767
|
+
const clean = chunk.replace(/\[TOPICS:\s*.+?\]/gi, "").replace(/\[REACT:.+?\]/gi, "").trim();
|
|
1768
|
+
if (clean) {
|
|
1769
|
+
await adapter.sendTyping?.(channelId, msg.threadId);
|
|
1770
|
+
lastSentMsgId = await retrySend(() => adapter.sendMessage(channelId, clean, { threadId: msg.threadId }));
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
if (reactionEmoji) {
|
|
1774
|
+
if (adapter.setReaction && msg.messageId) {
|
|
1775
|
+
try {
|
|
1776
|
+
await adapter.setReaction(channelId, msg.messageId, reactionEmoji);
|
|
1777
|
+
} catch {
|
|
1778
|
+
await adapter.sendMessage(channelId, reactionEmoji, { threadId: msg.threadId });
|
|
1779
|
+
}
|
|
1780
|
+
} else {
|
|
1781
|
+
await adapter.sendMessage(channelId, reactionEmoji, { threadId: msg.threadId });
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
if (getEnv().activeMemory) {
|
|
1785
|
+
const modelTopics = getEnv().primingModelTopics && topics ? topics : [];
|
|
1786
|
+
const regexKw = extractKeywords(text);
|
|
1787
|
+
const existing = sessions.get(activeSessionId)?.primingTerms ?? [];
|
|
1788
|
+
sessions.getOrCreate(activeSessionId).primingTerms = [.../* @__PURE__ */ new Set([...modelTopics, ...regexKw, ...existing])].slice(0, PRIMING_MAX);
|
|
1789
|
+
}
|
|
1790
|
+
const isGuest = registry.byUserId.get(userId)?.role === "guest";
|
|
1791
|
+
if (memory && !isGuest) {
|
|
1792
|
+
memory.recordMessage({
|
|
1793
|
+
role: "assistant",
|
|
1794
|
+
content: cleanAnswer || response,
|
|
1795
|
+
timestamp: Date.now(),
|
|
1796
|
+
userId,
|
|
1797
|
+
sessionId: activeSessionId,
|
|
1798
|
+
platformMessageId: typeof lastSentMsgId === "number" ? lastSentMsgId : void 0
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
if (isVoice && ttsConfig && !sessions.get(activeSessionId)?.fullMode && adapter.sendVoice) {
|
|
1802
|
+
try {
|
|
1803
|
+
await adapter.sendTyping?.(channelId, msg.threadId);
|
|
1804
|
+
const audio = await synthesizeSpeech(cleanAnswer || response, ttsConfig);
|
|
1805
|
+
if (audio) {
|
|
1806
|
+
await adapter.sendVoice(channelId, audio, { threadId: msg.threadId });
|
|
1807
|
+
logInfo(TAG4, `\u{1F50A} Voice reply sent (${audio.length} bytes)`);
|
|
1808
|
+
}
|
|
1809
|
+
} catch (err) {
|
|
1810
|
+
logWarn(TAG4, `TTS failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
if (adapter.setReaction && msg.messageId) {
|
|
1814
|
+
await adapter.setReaction(channelId, msg.messageId, "");
|
|
1815
|
+
}
|
|
1816
|
+
const ctxAfter = transport.contextPercent;
|
|
1817
|
+
logInfo(TAG4, `\u2192 [${msg.platform}] Response delivered${ctxAfter >= 0 ? ` (ctx: ${ctxAfter}%)` : ""}`);
|
|
1818
|
+
updateBridgeLockField("lastPromptAt", Date.now());
|
|
1819
|
+
if (hasHooks("AfterMessage")) {
|
|
1820
|
+
fire("AfterMessage", {
|
|
1821
|
+
event: "AfterMessage",
|
|
1822
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1823
|
+
sessionKey: activeSessionId,
|
|
1824
|
+
platform: msg.platform,
|
|
1825
|
+
userId,
|
|
1826
|
+
chatId: String(chatId),
|
|
1827
|
+
text,
|
|
1828
|
+
response: userResponse,
|
|
1829
|
+
model: "currentModel" in transport ? String(transport.currentModel) : "unknown",
|
|
1830
|
+
success: true
|
|
1831
|
+
}).catch((err) => logAndSwallow(TAG4, "adapter call", err));
|
|
1832
|
+
}
|
|
1833
|
+
} catch (err) {
|
|
1834
|
+
if (err instanceof ModelNotFoundError) {
|
|
1835
|
+
logWarn(TAG4, `Model not found for ${activeSessionId}: ${err.message}`);
|
|
1836
|
+
await adapter.sendMessage(channelId, `\u274C ${err.message}
|
|
1837
|
+
Use /model to switch.`, { threadId: msg.threadId });
|
|
1838
|
+
} else {
|
|
1839
|
+
logError(TAG4, `Error for ${activeSessionId} \u2014 ${err instanceof Error ? err.message : JSON.stringify(err)}`);
|
|
1840
|
+
}
|
|
1841
|
+
if (adapter.setReaction && msg.messageId) {
|
|
1842
|
+
await adapter.setReaction(channelId, msg.messageId, "").catch((err2) => logAndSwallow(TAG4, "adapter call", err2));
|
|
1843
|
+
}
|
|
1844
|
+
if (hasHooks("AfterMessage")) {
|
|
1845
|
+
fire("AfterMessage", {
|
|
1846
|
+
event: "AfterMessage",
|
|
1847
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1848
|
+
sessionKey: activeSessionId,
|
|
1849
|
+
platform: msg.platform,
|
|
1850
|
+
userId,
|
|
1851
|
+
chatId: String(chatId),
|
|
1852
|
+
text,
|
|
1853
|
+
success: false,
|
|
1854
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1855
|
+
}).catch((err2) => logAndSwallow(TAG4, "adapter call", err2));
|
|
1856
|
+
}
|
|
1857
|
+
const errStr = String(err instanceof Error ? err.message : JSON.stringify(err));
|
|
1858
|
+
const isContextOverflow = errStr.includes("ValidationException") || (errStr.includes("context window") || errStr.includes("token limit") || errStr.includes("maximum context"));
|
|
1859
|
+
const isTimeout = errStr.includes("timed out") || errStr.includes("Prompt already in progress");
|
|
1860
|
+
if (isContextOverflow) {
|
|
1861
|
+
logWarn(TAG4, `Context overflow detected \u2014 auto-resetting session`);
|
|
1862
|
+
await resetAndPrepare({ transport, sessionKey: activeSessionId, reason: `ctx-overflow: ${errStr.slice(0, 100)}`, sessions });
|
|
1863
|
+
await adapter.sendMessage(channelId, "\u{1F504} Context window full \u2014 session reset. Send your message again.", { threadId: msg.threadId }).catch((err2) => logAndSwallow(TAG4, "adapter call", err2));
|
|
1864
|
+
} else if (isTimeout) {
|
|
1865
|
+
logWarn(TAG4, `Request timeout \u2014 not resetting session`);
|
|
1866
|
+
await adapter.sendMessage(channelId, "\u274C Model timed out.", { threadId: msg.threadId }).catch((err2) => logAndSwallow(TAG4, "adapter call", err2));
|
|
1867
|
+
} else {
|
|
1868
|
+
const reason = errStr.includes("rate") || errStr.includes("429") ? "Rate limited." : errStr.includes("auth") || errStr.includes("401") || errStr.includes("403") ? "Authentication failed." : errStr.includes("connect") || errStr.includes("ECONNREFUSED") ? "Connection lost." : errStr.includes("exhausted") || errStr.includes("no candidates") ? "All models exhausted." : "Something went wrong.";
|
|
1869
|
+
await adapter.sendMessage(channelId, `\u274C ${reason}`, { threadId: msg.threadId }).catch((err2) => logAndSwallow(TAG4, "adapter call", err2));
|
|
1870
|
+
}
|
|
1871
|
+
} finally {
|
|
1872
|
+
clearInterval(typingInterval);
|
|
1873
|
+
clearTimeout(typingTtlTimer);
|
|
1874
|
+
if (toolElapsedTimer) clearInterval(toolElapsedTimer);
|
|
1875
|
+
transport.onToolCallStart = void 0;
|
|
1876
|
+
transport.onSegmentBreak = void 0;
|
|
1877
|
+
busyEntry.busy = false;
|
|
1878
|
+
idleSave.reset(activeSessionId, chatId);
|
|
1879
|
+
const entry = sessions.get(activeSessionId);
|
|
1880
|
+
if (entry?.queue.length) {
|
|
1881
|
+
const next = entry.queue.shift();
|
|
1882
|
+
logInfo(TAG4, `Draining queued message for ${activeSessionId} (${entry.queue.length} remaining)`);
|
|
1883
|
+
handleInboundMessage(next.msg, next.adapter, deps).catch((e) => logError(TAG4, "Queue drain error", e));
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
async function startSession(transport, memory, userId, sessionKey, greeting, sendResponse) {
|
|
1888
|
+
const prompt = buildSessionStartPrompt(greeting, memory, userId, sessionKey);
|
|
1889
|
+
logInfo(TAG4, `Session start for ${sessionKey} \u2014 prompt ${prompt.length} chars`);
|
|
1890
|
+
const response = await transport.sendPrompt(sessionKey, prompt, void 0, userId);
|
|
1891
|
+
if (response?.trim() && response.trim() !== "[NO_REPLY]" && response.trim() !== "(no response)") {
|
|
1892
|
+
await sendResponse(response);
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
// src/components/commands/registry.ts
|
|
1897
|
+
init_log_and_swallow();
|
|
1898
|
+
var TAG5 = "cmd_registry";
|
|
1899
|
+
var exactCommands = {};
|
|
1900
|
+
var prefixCommands = [];
|
|
1901
|
+
var KNOWN_COMMANDS = /* @__PURE__ */ new Set();
|
|
1902
|
+
var NON_MASTER_COMMANDS = /* @__PURE__ */ new Set(["/new", "/reset", "/stop", "/ctrlc", "/status", "/help", "/whoami"]);
|
|
1903
|
+
function registerExact(name, handler) {
|
|
1904
|
+
exactCommands[name] = handler;
|
|
1905
|
+
KNOWN_COMMANDS.add(name);
|
|
1906
|
+
}
|
|
1907
|
+
function registerPrefix(prefix, handler) {
|
|
1908
|
+
prefixCommands.push({ prefix, handler });
|
|
1909
|
+
KNOWN_COMMANDS.add(prefix.split(" ")[0]);
|
|
1910
|
+
}
|
|
1911
|
+
function registerCommand(name, handler) {
|
|
1912
|
+
registerExact(name, handler);
|
|
1913
|
+
}
|
|
1914
|
+
async function handleCommand(text, ctx) {
|
|
1915
|
+
const isMaster = !ctx.userId || ctx.userId === "master" || (await import("./user-registry-GTAJIW6E.js")).loadUsers().byUserId.get(ctx.userId)?.role === "master";
|
|
1916
|
+
if (!isMaster) {
|
|
1917
|
+
const cmd = text.split(/\s/)[0];
|
|
1918
|
+
if (cmd.startsWith("/") && !NON_MASTER_COMMANDS.has(cmd)) {
|
|
1919
|
+
await ctx.reply("\u26D4 Owner-only command.");
|
|
1920
|
+
return true;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
const exact = exactCommands[text];
|
|
1924
|
+
if (exact) return exact(text, ctx);
|
|
1925
|
+
for (const { prefix, handler } of prefixCommands) {
|
|
1926
|
+
if (text.startsWith(prefix)) return handler(text, ctx);
|
|
1927
|
+
}
|
|
1928
|
+
const firstWord = text.split(/\s/)[0];
|
|
1929
|
+
if (text !== firstWord) {
|
|
1930
|
+
const byFirstWord = exactCommands[firstWord];
|
|
1931
|
+
if (byFirstWord) return byFirstWord(text, ctx);
|
|
1932
|
+
}
|
|
1933
|
+
if (text.startsWith("/") && /^\/\w+/.test(text) && !text.startsWith("//")) {
|
|
1934
|
+
const cmd = text.split(/\s/)[0];
|
|
1935
|
+
if (!KNOWN_COMMANDS.has(cmd)) {
|
|
1936
|
+
await ctx.reply(`\u2753 Unknown command: ${cmd}
|
|
1937
|
+
Type /help for available commands.`);
|
|
1938
|
+
return true;
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return false;
|
|
1942
|
+
}
|
|
1943
|
+
async function triggerNewSession(ctx, reason = "new-session") {
|
|
1944
|
+
const { hasHooks: hasHooks2, fire: fireHook } = await import("./hook-system-POI5VRIX.js");
|
|
1945
|
+
if (hasHooks2("SessionEnd")) {
|
|
1946
|
+
await fireHook("SessionEnd", { event: "SessionEnd", timestamp: (/* @__PURE__ */ new Date()).toISOString(), sessionKey: ctx.sessionKey, platform: ctx.platform, userId: ctx.userId, reason }).catch((err) => logAndSwallow(TAG5, "fireHook session", err));
|
|
1947
|
+
}
|
|
1948
|
+
await ctx.idleSave.save(ctx.sessionKey, ctx.chatId);
|
|
1949
|
+
await resetAndPrepare({
|
|
1950
|
+
transport: ctx.transport,
|
|
1951
|
+
sessionKey: ctx.sessionKey,
|
|
1952
|
+
reason,
|
|
1953
|
+
sessions: ctx.sessions,
|
|
1954
|
+
conversationBuffer: ctx.conversationBuffer,
|
|
1955
|
+
bufKey: ctx.bufKey
|
|
1956
|
+
});
|
|
1957
|
+
if (ctx.memoryConfig.memoryEnabled) ctx.updateCtxStart(ctx.memoryConfig.memoryDir, ctx.userId);
|
|
1958
|
+
if (hasHooks2("SessionStart")) {
|
|
1959
|
+
await fireHook("SessionStart", { event: "SessionStart", timestamp: (/* @__PURE__ */ new Date()).toISOString(), sessionKey: ctx.sessionKey, platform: ctx.platform, userId: ctx.userId, reason }).catch((err) => logAndSwallow(TAG5, "fireHook session", err));
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
async function triggerResetSession(ctx) {
|
|
1963
|
+
const { hasHooks: hasHooks2, fire: fireHook } = await import("./hook-system-POI5VRIX.js");
|
|
1964
|
+
if (hasHooks2("SessionEnd")) {
|
|
1965
|
+
await fireHook("SessionEnd", { event: "SessionEnd", timestamp: (/* @__PURE__ */ new Date()).toISOString(), sessionKey: ctx.sessionKey, platform: ctx.platform, userId: ctx.userId, reason: "reset-transport" }).catch((err) => logAndSwallow(TAG5, "fireHook session", err));
|
|
1966
|
+
}
|
|
1967
|
+
await ctx.idleSave.save(ctx.sessionKey, ctx.chatId);
|
|
1968
|
+
const { clearTransportCache } = await import("./transport-config-ANPS2RYT.js");
|
|
1969
|
+
clearTransportCache();
|
|
1970
|
+
if (ctx.rebuildTransport) await ctx.rebuildTransport();
|
|
1971
|
+
await resetAndPrepare({
|
|
1972
|
+
transport: ctx.transport,
|
|
1973
|
+
sessionKey: ctx.sessionKey,
|
|
1974
|
+
reason: "reset-transport",
|
|
1975
|
+
sessions: ctx.sessions,
|
|
1976
|
+
conversationBuffer: ctx.conversationBuffer,
|
|
1977
|
+
bufKey: ctx.bufKey
|
|
1978
|
+
});
|
|
1979
|
+
if (ctx.memoryConfig.memoryEnabled) ctx.updateCtxStart(ctx.memoryConfig.memoryDir, ctx.userId);
|
|
1980
|
+
if (hasHooks2("SessionStart")) {
|
|
1981
|
+
await fireHook("SessionStart", { event: "SessionStart", timestamp: (/* @__PURE__ */ new Date()).toISOString(), sessionKey: ctx.sessionKey, platform: ctx.platform, userId: ctx.userId, reason: "reset-transport" }).catch((err) => logAndSwallow(TAG5, "fireHook session", err));
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
var _wakeInhibitPid = null;
|
|
1985
|
+
function killWakeInhibit() {
|
|
1986
|
+
if (_wakeInhibitPid) {
|
|
1987
|
+
try {
|
|
1988
|
+
process.kill(_wakeInhibitPid);
|
|
1989
|
+
} catch (err) {
|
|
1990
|
+
logAndSwallow("command_handlers", "op", err);
|
|
1991
|
+
}
|
|
1992
|
+
logInfo("wakeup", `Killed wake inhibitor pid=${_wakeInhibitPid}`);
|
|
1993
|
+
_wakeInhibitPid = null;
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
function setWakeInhibitPid(pid) {
|
|
1997
|
+
_wakeInhibitPid = pid;
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
// src/components/commands/handlers-transport.ts
|
|
2001
|
+
init_logger();
|
|
2002
|
+
init_log_and_swallow();
|
|
2003
|
+
var TAG6 = "cmd";
|
|
2004
|
+
async function handleNewReset(text, ctx) {
|
|
2005
|
+
const isResetDefault = text.trim().toLowerCase() === "/reset default";
|
|
2006
|
+
if (isResetDefault) {
|
|
2007
|
+
const { resetToDefaults } = await import("./transport-config-ANPS2RYT.js");
|
|
2008
|
+
resetToDefaults();
|
|
2009
|
+
} else {
|
|
2010
|
+
try {
|
|
2011
|
+
await triggerResetSession(ctx);
|
|
2012
|
+
} catch (err) {
|
|
2013
|
+
await ctx.reply(`\u26A0\uFE0F Transport rebuild failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2014
|
+
return true;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
ctx.sessionManager.endSession(ctx.userId, ctx.platform);
|
|
2018
|
+
const activeId = ctx.sessionManager.getActiveSessionId(ctx.userId, ctx.platform);
|
|
2019
|
+
await ctx.transport.resetSession(activeId);
|
|
2020
|
+
const label = isResetDefault ? "\u{1F504} Reset to defaults." : "\u{1F504} Transport reloaded.";
|
|
2021
|
+
await ctx.reply(label);
|
|
2022
|
+
logInfo(TAG6, `Reset session \u2192 ${activeId} (${ctx.platform})`);
|
|
2023
|
+
return true;
|
|
2024
|
+
}
|
|
2025
|
+
async function handleCompact(_text, ctx) {
|
|
2026
|
+
try {
|
|
2027
|
+
const transport = ctx.transport;
|
|
2028
|
+
if (transport.contextOrchestrator) {
|
|
2029
|
+
const budget = transport.config?.maxContext ?? 2e5;
|
|
2030
|
+
const success = await transport.contextOrchestrator.forceCompact(ctx.sessionKey, budget);
|
|
2031
|
+
await ctx.reply(success ? "\u{1F4E6} Compaction complete." : "\u{1F4E6} Nothing to compact.");
|
|
2032
|
+
} else {
|
|
2033
|
+
await ctx.reply("\u{1F4E6} Context engine not active for this transport.");
|
|
2034
|
+
}
|
|
2035
|
+
} catch (err) {
|
|
2036
|
+
logError(TAG6, "Manual compaction failed", err);
|
|
2037
|
+
await ctx.reply("\u274C Compaction failed.");
|
|
2038
|
+
}
|
|
2039
|
+
return true;
|
|
2040
|
+
}
|
|
2041
|
+
async function handleEmergencyAlias(_text, ctx) {
|
|
2042
|
+
return handleModels("/model emergency", ctx);
|
|
2043
|
+
}
|
|
2044
|
+
async function handleModels(text, ctx) {
|
|
2045
|
+
const { loadTransport, resolveAgent, getModelsForProvider, writeTransportConfig } = await import("./transport-config-ANPS2RYT.js");
|
|
2046
|
+
const tc = loadTransport();
|
|
2047
|
+
const prof = tc ? resolveAgent("professor", tc) : null;
|
|
2048
|
+
const currentModel = ("currentModel" in ctx.transport ? ctx.transport.currentModel : void 0) ?? prof?.model ?? "unknown";
|
|
2049
|
+
const arg = text.replace(/^\/(models?)\s*/i, "").trim().toLowerCase();
|
|
2050
|
+
if (arg === "emergency" || arg === "hailmary") {
|
|
2051
|
+
if (!ctx.hailMary) {
|
|
2052
|
+
await ctx.reply("\u274C hailMary not configured in transport.json");
|
|
2053
|
+
return true;
|
|
2054
|
+
}
|
|
2055
|
+
const t = ctx.transport;
|
|
2056
|
+
if (!t.setEmergencyMode) {
|
|
2057
|
+
await ctx.reply("\u274C Transport does not support emergency mode");
|
|
2058
|
+
return true;
|
|
2059
|
+
}
|
|
2060
|
+
if (tc) {
|
|
2061
|
+
const hmProvider = tc.hailMary ? tc.providers[tc.hailMary.provider] : void 0;
|
|
2062
|
+
if (hmProvider) {
|
|
2063
|
+
const { validateProviderReady, formatValidationError } = await import("./transport-config-ANPS2RYT.js");
|
|
2064
|
+
const { getEnv: getEnv2 } = await import("./env-schema-DGD6QWPA.js");
|
|
2065
|
+
const result = validateProviderReady(tc.hailMary.provider, hmProvider, getEnv2());
|
|
2066
|
+
if (!result.ok) {
|
|
2067
|
+
await ctx.reply(formatValidationError(tc.hailMary.provider, result));
|
|
2068
|
+
return true;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
t.setEmergencyMode({ ...ctx.hailMary, maxContext: 1e6 });
|
|
2073
|
+
await ctx.reply(`\u{1F6A8} EMERGENCY MODE: using ${ctx.hailMary.model} (paid). Clears on /model restore, /reset, or wake-up.`);
|
|
2074
|
+
return true;
|
|
2075
|
+
}
|
|
2076
|
+
if (arg === "restore") {
|
|
2077
|
+
const { restorePrevious } = await import("./transport-config-ANPS2RYT.js");
|
|
2078
|
+
const result = restorePrevious();
|
|
2079
|
+
if (!result.ok) {
|
|
2080
|
+
await ctx.reply(`\u274C ${result.error}`);
|
|
2081
|
+
return true;
|
|
2082
|
+
}
|
|
2083
|
+
const t = ctx.transport;
|
|
2084
|
+
t.setEmergencyMode?.(null);
|
|
2085
|
+
await ctx.reply("\u{1F504} Restored previous config.");
|
|
2086
|
+
return true;
|
|
2087
|
+
}
|
|
2088
|
+
if (arg === "default") {
|
|
2089
|
+
const { resetToDefaults } = await import("./transport-config-ANPS2RYT.js");
|
|
2090
|
+
if (!resetToDefaults()) {
|
|
2091
|
+
await ctx.reply("\u274C Factory config not found \u2014 run abtars install to restore.");
|
|
2092
|
+
return true;
|
|
2093
|
+
}
|
|
2094
|
+
await ctx.reply("\u{1F504} Factory config restored.");
|
|
2095
|
+
return true;
|
|
2096
|
+
}
|
|
2097
|
+
if (arg === "health reset" || arg === "primary") {
|
|
2098
|
+
const t = ctx.transport;
|
|
2099
|
+
const wasEmergency = t.isEmergencyMode;
|
|
2100
|
+
t.setEmergencyMode?.(null);
|
|
2101
|
+
if (t.policy?.registry) {
|
|
2102
|
+
t.policy.registry.resetAll();
|
|
2103
|
+
await ctx.reply(wasEmergency ? "\u{1F50C} Emergency mode cleared + model health reset \u2014 free models active." : "\u{1F50C} Model health reset \u2014 all models available.");
|
|
2104
|
+
} else {
|
|
2105
|
+
await ctx.reply("\u{1F50C} No fallback policy configured.");
|
|
2106
|
+
}
|
|
2107
|
+
return true;
|
|
2108
|
+
}
|
|
2109
|
+
if (arg === "doctor") {
|
|
2110
|
+
if (!prof) {
|
|
2111
|
+
await ctx.reply("\u274C No transport configured.");
|
|
2112
|
+
return true;
|
|
2113
|
+
}
|
|
2114
|
+
const endpoint = prof.provider.endpoint ?? "http://localhost:11434/v1";
|
|
2115
|
+
const apiKey = prof.provider.apiKeyEnv ? (await import("./env-schema-DGD6QWPA.js")).getEnv().getApiKey(prof.provider.apiKeyEnv) : void 0;
|
|
2116
|
+
const models = /* @__PURE__ */ new Set();
|
|
2117
|
+
for (const [, agent] of Object.entries(tc.agents)) {
|
|
2118
|
+
if (agent.provider === prof.providerName) models.add(agent.model);
|
|
2119
|
+
for (const fb of agent.fallbacks ?? []) {
|
|
2120
|
+
if (fb.provider === prof.providerName) models.add(fb.model);
|
|
2121
|
+
}
|
|
2122
|
+
}
|
|
2123
|
+
if (tc.hailMary?.provider === prof.providerName) models.add(tc.hailMary.model);
|
|
2124
|
+
await ctx.reply(`\u{1FA7A} Checking ${models.size} models on ${prof.providerName}...`);
|
|
2125
|
+
const results = [];
|
|
2126
|
+
const { loadModels } = await import("./transport-config-ANPS2RYT.js");
|
|
2127
|
+
const catalog = loadModels();
|
|
2128
|
+
for (const model of models) {
|
|
2129
|
+
try {
|
|
2130
|
+
const res = await fetch(`${endpoint}/chat/completions`, {
|
|
2131
|
+
method: "POST",
|
|
2132
|
+
headers: { "Content-Type": "application/json", ...apiKey ? { Authorization: `Bearer ${apiKey}` } : {} },
|
|
2133
|
+
body: JSON.stringify({ model, messages: [{ role: "user", content: "hi" }], max_tokens: 1 }),
|
|
2134
|
+
signal: AbortSignal.timeout(1e4)
|
|
2135
|
+
});
|
|
2136
|
+
if (res.ok) {
|
|
2137
|
+
results.push(`\u2705 ${model} \u2014 alive`);
|
|
2138
|
+
if (catalog[model]) catalog[model].status = "alive";
|
|
2139
|
+
} else {
|
|
2140
|
+
const body = await res.text().catch((err) => {
|
|
2141
|
+
logAndSwallow(TAG6, "read model probe error body", err);
|
|
2142
|
+
return "";
|
|
2143
|
+
});
|
|
2144
|
+
const short = body.slice(0, 80).replace(/\n/g, " ");
|
|
2145
|
+
const status = res.status === 404 ? "dead" : res.status === 403 ? "subscription" : res.status === 429 ? "rate_limited" : "error";
|
|
2146
|
+
results.push(`\u274C ${model} \u2014 ${status} (${res.status}: ${short})`);
|
|
2147
|
+
if (catalog[model]) {
|
|
2148
|
+
catalog[model].status = status;
|
|
2149
|
+
catalog[model].error = `${res.status}: ${short}`;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
} catch (err) {
|
|
2153
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2154
|
+
results.push(`\u26A0\uFE0F ${model} \u2014 timeout/error (${msg.slice(0, 60)})`);
|
|
2155
|
+
if (catalog[model]) {
|
|
2156
|
+
catalog[model].status = "dead";
|
|
2157
|
+
catalog[model].error = msg.slice(0, 80);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
if (catalog[model]) catalog[model].lastChecked = (/* @__PURE__ */ new Date()).toISOString();
|
|
2161
|
+
}
|
|
2162
|
+
const { writeFileSync: writeFileSync2 } = await import("node:fs");
|
|
2163
|
+
const { join: join5 } = await import("node:path");
|
|
2164
|
+
const { configDir } = await import("./transport-config-ANPS2RYT.js");
|
|
2165
|
+
writeFileSync2(join5(configDir(), "models.json"), JSON.stringify(catalog, null, 2) + "\n");
|
|
2166
|
+
await ctx.reply(`\u{1FA7A} Model Health:
|
|
2167
|
+
${results.join("\n")}`);
|
|
2168
|
+
return true;
|
|
2169
|
+
}
|
|
2170
|
+
if (arg.startsWith("quick ") || arg.startsWith("switch ")) {
|
|
2171
|
+
const newModel = arg.split(" ").slice(1).join(" ").trim();
|
|
2172
|
+
if (!newModel) {
|
|
2173
|
+
await ctx.reply("Usage: /models quick <model>");
|
|
2174
|
+
return true;
|
|
2175
|
+
}
|
|
2176
|
+
if (!tc || !prof) {
|
|
2177
|
+
await ctx.reply("\u274C transport.json not loaded");
|
|
2178
|
+
return true;
|
|
2179
|
+
}
|
|
2180
|
+
const models = getModelsForProvider(prof.providerName);
|
|
2181
|
+
const match = models.find((m) => m.id === newModel);
|
|
2182
|
+
if (!match) {
|
|
2183
|
+
await ctx.reply(`\u274C ${newModel} not available on ${prof.providerName}. Use /models change to switch provider.`);
|
|
2184
|
+
return true;
|
|
2185
|
+
}
|
|
2186
|
+
{
|
|
2187
|
+
const { validateProviderReady, formatValidationError } = await import("./transport-config-ANPS2RYT.js");
|
|
2188
|
+
const { getEnv: getEnv2 } = await import("./env-schema-DGD6QWPA.js");
|
|
2189
|
+
const result = validateProviderReady(prof.providerName, prof.provider, getEnv2());
|
|
2190
|
+
if (!result.ok) {
|
|
2191
|
+
await ctx.reply(formatValidationError(prof.providerName, result));
|
|
2192
|
+
return true;
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
tc.agents["professor"].model = newModel;
|
|
2196
|
+
const { cleanDemotedModels } = await import("./transport-config-ANPS2RYT.js");
|
|
2197
|
+
cleanDemotedModels(tc, newModel);
|
|
2198
|
+
writeTransportConfig(tc, `professor model \u2192 ${newModel}`);
|
|
2199
|
+
if ("setModel" in ctx.transport) {
|
|
2200
|
+
await ctx.transport.setModel(newModel);
|
|
2201
|
+
}
|
|
2202
|
+
await ctx.reply(`\u2705 Switched to ${newModel}`);
|
|
2203
|
+
return true;
|
|
2204
|
+
}
|
|
2205
|
+
if (arg === "list" || arg.startsWith("list ")) {
|
|
2206
|
+
const providerArg = arg.slice(5).trim();
|
|
2207
|
+
const { getAvailableProviders, getModelsForProvider: getModels } = await import("./transport-config-ANPS2RYT.js");
|
|
2208
|
+
if (!tc) {
|
|
2209
|
+
await ctx.reply("\u274C transport.json not loaded");
|
|
2210
|
+
return true;
|
|
2211
|
+
}
|
|
2212
|
+
if (!providerArg) {
|
|
2213
|
+
const providers = getAvailableProviders(tc);
|
|
2214
|
+
const lines2 = ["\u{1F50C} Providers:"];
|
|
2215
|
+
for (const p of providers) {
|
|
2216
|
+
const count = getModels(p.name).length;
|
|
2217
|
+
lines2.push(` \u2022 ${p.name} (${p.config.transport})${count > 0 ? ` \u2014 ${count} models` : ""}`);
|
|
2218
|
+
}
|
|
2219
|
+
lines2.push("\nUse /model list <provider> to see models.");
|
|
2220
|
+
await ctx.reply(lines2.join("\n"));
|
|
2221
|
+
} else {
|
|
2222
|
+
const models = getModels(providerArg);
|
|
2223
|
+
if (models.length === 0) {
|
|
2224
|
+
await ctx.reply(`\u274C No models found for provider "${providerArg}"`);
|
|
2225
|
+
return true;
|
|
2226
|
+
}
|
|
2227
|
+
const lines2 = [`\u{1F4CB} Models on ${providerArg}:`];
|
|
2228
|
+
for (const m of models) {
|
|
2229
|
+
const current = m.id === currentModel ? " \u2705" : "";
|
|
2230
|
+
lines2.push(` \u2022 ${m.id}${current}`);
|
|
2231
|
+
}
|
|
2232
|
+
lines2.push(`
|
|
2233
|
+
Use /model quick <name> to switch.`);
|
|
2234
|
+
await ctx.reply(lines2.join("\n"));
|
|
2235
|
+
}
|
|
2236
|
+
return true;
|
|
2237
|
+
}
|
|
2238
|
+
if (arg === "change") {
|
|
2239
|
+
if (ctx.platform !== "telegram") {
|
|
2240
|
+
await ctx.reply("\u{1F916} Use /model list to discover, /model quick <model> to switch.");
|
|
2241
|
+
return true;
|
|
2242
|
+
}
|
|
2243
|
+
const AGENT_LABELS = [
|
|
2244
|
+
{ key: "professor", label: "Professor" },
|
|
2245
|
+
{ key: "dreamy", label: "Dreamy (sleep)" },
|
|
2246
|
+
{ key: "browsie", label: "Browsie (browse)" },
|
|
2247
|
+
{ key: "coding", label: "Cody (coding)" }
|
|
2248
|
+
];
|
|
2249
|
+
const buttons = AGENT_LABELS.map((a) => [{ text: a.label, callback_data: `mslot:${a.key}` }]);
|
|
2250
|
+
buttons.push([{ text: "\u2190 Cancel", callback_data: "mb:" }]);
|
|
2251
|
+
await ctx.reply("\u{1F916} Which agent to change?", { reply_markup: { inline_keyboard: buttons } });
|
|
2252
|
+
return true;
|
|
2253
|
+
}
|
|
2254
|
+
if (arg.startsWith("provider ")) {
|
|
2255
|
+
const providerName = arg.slice(9).trim();
|
|
2256
|
+
if (!tc || !prof) {
|
|
2257
|
+
await ctx.reply("\u274C transport.json not loaded");
|
|
2258
|
+
return true;
|
|
2259
|
+
}
|
|
2260
|
+
const provider2 = tc.providers[providerName];
|
|
2261
|
+
if (!provider2) {
|
|
2262
|
+
await ctx.reply(`\u274C Provider "${providerName}" not found. Available: ${Object.keys(tc.providers).join(", ")}`);
|
|
2263
|
+
return true;
|
|
2264
|
+
}
|
|
2265
|
+
const { validateProviderReady, formatValidationError, loadProviderDefaults } = await import("./transport-config-ANPS2RYT.js");
|
|
2266
|
+
const { getEnv: getEnv2 } = await import("./env-schema-DGD6QWPA.js");
|
|
2267
|
+
const validation = validateProviderReady(providerName, provider2, getEnv2());
|
|
2268
|
+
if (!validation.ok) {
|
|
2269
|
+
await ctx.reply(formatValidationError(providerName, validation));
|
|
2270
|
+
return true;
|
|
2271
|
+
}
|
|
2272
|
+
const defaults = loadProviderDefaults(providerName);
|
|
2273
|
+
if (defaults?.professor) {
|
|
2274
|
+
tc.agents["professor"] = { model: defaults.professor.model, provider: providerName, fallbacks: defaults.professor.fallbacks?.map((m) => ({ model: m, provider: providerName })) };
|
|
2275
|
+
for (const role of ["dreamy", "browsie", "coding"]) {
|
|
2276
|
+
tc.agents[role] = { model: defaults[role]?.model ?? defaults.professor.model, provider: providerName };
|
|
2277
|
+
}
|
|
2278
|
+
} else {
|
|
2279
|
+
for (const role of Object.keys(tc.agents)) {
|
|
2280
|
+
tc.agents[role] = { ...tc.agents[role], provider: providerName };
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
writeTransportConfig(tc, `global provider \u2192 ${providerName}`);
|
|
2284
|
+
await ctx.reply(`\u2705 All agents \u2192 ${providerName}. Use /reset to apply.`);
|
|
2285
|
+
return true;
|
|
2286
|
+
}
|
|
2287
|
+
const transportStatus = ctx.transport.isReady ? "\u2713 Connected" : "\u274C Disconnected";
|
|
2288
|
+
const ctxPct = ctx.transport.contextPercent >= 0 ? `${ctx.transport.contextPercent}%` : "n/a";
|
|
2289
|
+
const mode = prof?.provider.transport?.toUpperCase() ?? "ACP";
|
|
2290
|
+
const provider = prof?.providerName ?? "unknown";
|
|
2291
|
+
const isEmergency = ctx.transport.isEmergencyMode === true;
|
|
2292
|
+
const lines = [
|
|
2293
|
+
`\u{1F50C} Transport: ${mode} (${provider}) \u2014 ${transportStatus}`,
|
|
2294
|
+
isEmergency ? `\u{1F6A8} EMERGENCY MODE: ${currentModel} (paid)` : `\u{1F916} Model: ${currentModel}`,
|
|
2295
|
+
`\u{1F4CA} Context: ${ctxPct}`,
|
|
2296
|
+
"",
|
|
2297
|
+
"\u{1F4CB} Agents:"
|
|
2298
|
+
];
|
|
2299
|
+
const agents = ["professor", "dreamy", "browsie", "coding"];
|
|
2300
|
+
const names = { professor: "Professor", dreamy: "Dreamy", browsie: "Browsie", coding: "Cody" };
|
|
2301
|
+
for (const a of agents) {
|
|
2302
|
+
const r = tc ? resolveAgent(a, tc) : null;
|
|
2303
|
+
let line = ` ${names[a]}: ${r?.model ?? "unknown"} (${r?.providerName ?? "?"}, ${r?.provider.transport ?? "?"})`;
|
|
2304
|
+
if (a === "professor" && r?.fallbacks.length) {
|
|
2305
|
+
line += "\n" + r.fallbacks.map((f, i) => ` \u21B3 fb${i + 1}: ${f.model} (${f.provider})`).join("\n");
|
|
2306
|
+
}
|
|
2307
|
+
lines.push(line);
|
|
2308
|
+
}
|
|
2309
|
+
lines.push(" Cron: inherits Professor");
|
|
2310
|
+
if (prof?.provider.fallbackChain?.length) {
|
|
2311
|
+
lines.push(`
|
|
2312
|
+
\u{1F6DF} Fallback chain: ${prof.provider.fallbackChain.join(" \u2192 ")}`);
|
|
2313
|
+
}
|
|
2314
|
+
if (ctx.hailMary) {
|
|
2315
|
+
lines.push(`\u{1F6A8} hailMary: ${ctx.hailMary.model} `);
|
|
2316
|
+
}
|
|
2317
|
+
lines.push("\nUse /models change to switch.");
|
|
2318
|
+
await ctx.reply(lines.join("\n"));
|
|
2319
|
+
return true;
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
// src/components/commands/exec-async.ts
|
|
2323
|
+
import { execFile } from "node:child_process";
|
|
2324
|
+
function execAsync(cmd, args, timeoutMs) {
|
|
2325
|
+
return new Promise((resolve) => {
|
|
2326
|
+
const child = execFile(cmd, args, { timeout: timeoutMs, encoding: "utf-8" }, (err, stdout) => {
|
|
2327
|
+
resolve(err ? null : stdout.trim());
|
|
2328
|
+
});
|
|
2329
|
+
child.stderr?.resume();
|
|
2330
|
+
});
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
// src/components/commands/handlers-system.ts
|
|
2334
|
+
init_logger();
|
|
2335
|
+
init_log_and_swallow();
|
|
2336
|
+
import { readFileSync, readdirSync } from "node:fs";
|
|
2337
|
+
import { join as join2 } from "node:path";
|
|
2338
|
+
import { homedir } from "node:os";
|
|
2339
|
+
init_paths();
|
|
2340
|
+
var TAG7 = "cmd";
|
|
2341
|
+
async function handleDoctor(_text, ctx) {
|
|
2342
|
+
const arg = _text.replace(/^\/doctor\s*/i, "").trim().toLowerCase();
|
|
2343
|
+
if (arg === "fix" || arg === "fix-full") {
|
|
2344
|
+
const flag = arg === "fix-full" ? "--fix-full" : "--fix";
|
|
2345
|
+
try {
|
|
2346
|
+
const raw = await execAsync("bash", [join2(abtarsHome(), "scripts", "doctor.sh"), flag], 3e4);
|
|
2347
|
+
await ctx.reply(`\u{1FA7A} doctor.sh ${flag}:
|
|
2348
|
+
${raw || "(no output)"}`);
|
|
2349
|
+
} catch (err) {
|
|
2350
|
+
await ctx.reply(`\u274C doctor.sh failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2351
|
+
}
|
|
2352
|
+
return true;
|
|
2353
|
+
}
|
|
2354
|
+
const { getDoctorReport, renderDoctorText } = await import("./doctor-R54GZPKL.js");
|
|
2355
|
+
const force = arg === "force";
|
|
2356
|
+
const svcStates = ctx.registry?.getStates() ?? {};
|
|
2357
|
+
const report = await getDoctorReport({
|
|
2358
|
+
memory: ctx.memory,
|
|
2359
|
+
transport: ctx.transport,
|
|
2360
|
+
telegramRunning: svcStates.telegram?.running ?? false,
|
|
2361
|
+
discordRunning: svcStates.discord?.running ?? false,
|
|
2362
|
+
phaseHealth: ctx.phaseHealth
|
|
2363
|
+
}, { force });
|
|
2364
|
+
await ctx.reply(renderDoctorText(report));
|
|
2365
|
+
return true;
|
|
2366
|
+
}
|
|
2367
|
+
async function handleStatus(_text, ctx) {
|
|
2368
|
+
if (ctx.phaseHealth && ctx.registry) {
|
|
2369
|
+
const { getSystemStatus, renderStatusText } = await import("./system-status-2CR5OUDY.js");
|
|
2370
|
+
const status = await getSystemStatus({
|
|
2371
|
+
phaseHealth: ctx.phaseHealth,
|
|
2372
|
+
registry: ctx.registry,
|
|
2373
|
+
transport: ctx.transport,
|
|
2374
|
+
startedAt: ctx.startedAt,
|
|
2375
|
+
bridgeLockPath: ctx.bridgeLockPath ?? "",
|
|
2376
|
+
heartbeat: { intervalMs: Math.max(60, parseInt(process.env["HEARTBEAT_INTERVAL_SEC"] ?? "60", 10)) * 1e3 }
|
|
2377
|
+
});
|
|
2378
|
+
let text = renderStatusText(status);
|
|
2379
|
+
if (_text.trim().toLowerCase() === "full") {
|
|
2380
|
+
const { envDump } = await import("./env-schema-DGD6QWPA.js");
|
|
2381
|
+
const dump = envDump();
|
|
2382
|
+
const envLines = Object.entries(dump).slice(0, 30).map(([k, v]) => ` ${k}: ${v}`);
|
|
2383
|
+
text += "\n\n\u{1F4CB} Config (top 30):\n" + envLines.join("\n");
|
|
2384
|
+
}
|
|
2385
|
+
await ctx.reply(text);
|
|
2386
|
+
} else {
|
|
2387
|
+
const lines = await buildStatusLines(ctx);
|
|
2388
|
+
await ctx.reply(lines.join("\n"));
|
|
2389
|
+
}
|
|
2390
|
+
return true;
|
|
2391
|
+
}
|
|
2392
|
+
async function handleWait(text, ctx) {
|
|
2393
|
+
const body = text.replace(/^\/(wait|steer)\s*/i, "").trim();
|
|
2394
|
+
if (!body) {
|
|
2395
|
+
await ctx.reply("Nothing running. Send a message to start.");
|
|
2396
|
+
return true;
|
|
2397
|
+
}
|
|
2398
|
+
return false;
|
|
2399
|
+
}
|
|
2400
|
+
async function handleStop(_text, ctx) {
|
|
2401
|
+
await ctx.transport.sendInterrupt();
|
|
2402
|
+
ctx.sessions.getOrCreate(ctx.sessionKey).busy = false;
|
|
2403
|
+
await ctx.reply("\u{1F6D1} Ctrl+C sent.");
|
|
2404
|
+
logInfo(TAG7, "Ctrl+C interrupt sent");
|
|
2405
|
+
return true;
|
|
2406
|
+
}
|
|
2407
|
+
async function handleRestart(_text, ctx) {
|
|
2408
|
+
await ctx.reply("\u267B\uFE0F Restarting bridge...");
|
|
2409
|
+
setTimeout(() => ctx.requestShutdown?.(0), 500);
|
|
2410
|
+
return true;
|
|
2411
|
+
}
|
|
2412
|
+
async function handleHeartbeat(_text, ctx) {
|
|
2413
|
+
const cronInfo = ctx.memory?.getCronInfo();
|
|
2414
|
+
if (!cronInfo) {
|
|
2415
|
+
await ctx.reply("\u{1F493} Heartbeat not available.");
|
|
2416
|
+
return true;
|
|
2417
|
+
}
|
|
2418
|
+
const mins = Math.round(cronInfo.intervalMs / 6e4);
|
|
2419
|
+
const lines = [
|
|
2420
|
+
`\u{1F493} Heartbeat: ${cronInfo.heartbeatRunning ? "running" : "stopped"} (${mins}min interval)`,
|
|
2421
|
+
""
|
|
2422
|
+
];
|
|
2423
|
+
if (cronInfo.taskStatuses.size > 0) {
|
|
2424
|
+
lines.push("Tasks (last tick):");
|
|
2425
|
+
for (const [name, status] of cronInfo.taskStatuses) {
|
|
2426
|
+
lines.push(` ${status} ${name}`);
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
try {
|
|
2430
|
+
const lock = JSON.parse(readFileSync(join2(abtarsHome(), "bridge.lock"), "utf-8"));
|
|
2431
|
+
if (lock.lastHeartbeat > 0) {
|
|
2432
|
+
const agoMin = Math.round((Date.now() - lock.lastHeartbeat) / 6e4);
|
|
2433
|
+
lines.push("", `\u{1FAC0} Last tick: ${agoMin}min ago`);
|
|
2434
|
+
}
|
|
2435
|
+
} catch (err) {
|
|
2436
|
+
logAndSwallow("command_handlers", "op", err);
|
|
2437
|
+
}
|
|
2438
|
+
await ctx.reply(lines.join("\n"));
|
|
2439
|
+
return true;
|
|
2440
|
+
}
|
|
2441
|
+
async function handleHealing(text, ctx) {
|
|
2442
|
+
if (!ctx.selfHealerTask) {
|
|
2443
|
+
await ctx.reply("\u{1FA7A} Self-healer not available.");
|
|
2444
|
+
return true;
|
|
2445
|
+
}
|
|
2446
|
+
const arg = text.replace(/^\/healing\s*/, "").trim().toLowerCase();
|
|
2447
|
+
if (arg === "on") {
|
|
2448
|
+
ctx.selfHealerTask.enabled = true;
|
|
2449
|
+
} else if (arg === "off") {
|
|
2450
|
+
ctx.selfHealerTask.enabled = false;
|
|
2451
|
+
} else if (arg === "reset") {
|
|
2452
|
+
ctx.selfHealerTask.resetCircuitBreaker?.();
|
|
2453
|
+
await ctx.reply("\u{1FA7A} Circuit breaker reset \u2014 all paused rules re-enabled.");
|
|
2454
|
+
return true;
|
|
2455
|
+
}
|
|
2456
|
+
const status = ctx.selfHealerTask.enabled ? "ON" : "OFF";
|
|
2457
|
+
const paused = ctx.selfHealerTask.pausedRules?.() ?? 0;
|
|
2458
|
+
const pausedText = paused > 0 ? ` (${paused} rule${paused > 1 ? "s" : ""} paused)` : "";
|
|
2459
|
+
await ctx.reply(`\u{1FA7A} Self-healing: ${status}${pausedText}`);
|
|
2460
|
+
if (arg === "on" || arg === "off") logInfo(TAG7, `Self-healer ${status} by user`);
|
|
2461
|
+
return true;
|
|
2462
|
+
}
|
|
2463
|
+
async function handleFull(_text, ctx) {
|
|
2464
|
+
if (ctx.platform !== "telegram") {
|
|
2465
|
+
await ctx.reply("\u{1F4FA} Full mode is only available on Telegram.");
|
|
2466
|
+
return true;
|
|
2467
|
+
}
|
|
2468
|
+
ctx.sessions.getOrCreate(ctx.sessionKey).fullMode = true;
|
|
2469
|
+
await ctx.reply("\u{1F4FA} Full mode \u2014 sending raw output, TTS disabled.");
|
|
2470
|
+
return true;
|
|
2471
|
+
}
|
|
2472
|
+
async function handleShort(_text, ctx) {
|
|
2473
|
+
if (ctx.platform !== "telegram") {
|
|
2474
|
+
await ctx.reply("\u2702\uFE0F Short mode is only available on Telegram.");
|
|
2475
|
+
return true;
|
|
2476
|
+
}
|
|
2477
|
+
ctx.sessions.getOrCreate(ctx.sessionKey).fullMode = false;
|
|
2478
|
+
await ctx.reply("\u2702\uFE0F Short mode \u2014 clean responses, TTS enabled.");
|
|
2479
|
+
return true;
|
|
2480
|
+
}
|
|
2481
|
+
async function buildStatusLines(ctx) {
|
|
2482
|
+
let version = "?";
|
|
2483
|
+
let buildInfo = "";
|
|
2484
|
+
try {
|
|
2485
|
+
const pkgPath = join2(import.meta.dirname, "..", "..", "..", "package.json");
|
|
2486
|
+
version = JSON.parse(readFileSync(pkgPath, "utf-8")).version;
|
|
2487
|
+
} catch (err) {
|
|
2488
|
+
logAndSwallow("command_handlers", "op", err);
|
|
2489
|
+
}
|
|
2490
|
+
try {
|
|
2491
|
+
const biPath = join2(import.meta.dirname, "..", "..", "build-info.json");
|
|
2492
|
+
const bi = JSON.parse(readFileSync(biPath, "utf-8"));
|
|
2493
|
+
buildInfo = ` (${bi.hash} ${bi.date.slice(0, 10)})`;
|
|
2494
|
+
} catch (err) {
|
|
2495
|
+
logAndSwallow("command_handlers", "op", err);
|
|
2496
|
+
}
|
|
2497
|
+
let model = "unknown";
|
|
2498
|
+
if ("currentModel" in ctx.transport) {
|
|
2499
|
+
model = ctx.transport.currentModel;
|
|
2500
|
+
} else {
|
|
2501
|
+
const { loadTransport, resolveAgent } = await import("./transport-config-ANPS2RYT.js");
|
|
2502
|
+
const tc2 = loadTransport();
|
|
2503
|
+
const prof2 = tc2 ? resolveAgent("professor", tc2) : null;
|
|
2504
|
+
model = prof2?.model ?? "unknown";
|
|
2505
|
+
}
|
|
2506
|
+
const transportStatus = ctx.transport.isReady ? "\u2713 Connected" : "\u274C Disconnected";
|
|
2507
|
+
const uptime = formatUptime(Date.now() - ctx.startedAt);
|
|
2508
|
+
const ctxPct = ctx.transport.contextPercent >= 0 ? `${ctx.transport.contextPercent}%` : "n/a";
|
|
2509
|
+
const cronInfo = ctx.memory?.getCronInfo();
|
|
2510
|
+
const { loadTransport: lt, resolveAgent: ra } = await import("./transport-config-ANPS2RYT.js");
|
|
2511
|
+
const tc = lt();
|
|
2512
|
+
const prof = tc ? ra("professor", tc) : null;
|
|
2513
|
+
const provider = prof?.providerName ?? "unknown";
|
|
2514
|
+
const mode = prof?.provider.transport?.toUpperCase() ?? "ACP";
|
|
2515
|
+
const transportLine = `\u{1F50C} Transport: ${mode} (${provider}) \u2014 ${transportStatus}`;
|
|
2516
|
+
const fallbackModels = prof?.fallbacks.map((f) => `${f.model} (${f.provider})`) ?? [];
|
|
2517
|
+
const lines = [
|
|
2518
|
+
`Abtars v${version}${buildInfo}`,
|
|
2519
|
+
transportLine,
|
|
2520
|
+
`\u{1F916} Model: ${model}`,
|
|
2521
|
+
...fallbackModels.length > 0 ? [` Fallbacks: ${fallbackModels.join(", ")}`] : [],
|
|
2522
|
+
`\u{1F4CA} Context window: ${ctxPct}`,
|
|
2523
|
+
`\u23F1\uFE0F Uptime: ${uptime}`
|
|
2524
|
+
];
|
|
2525
|
+
if (cronInfo) {
|
|
2526
|
+
const mins = Math.round(cronInfo.intervalMs / 6e4);
|
|
2527
|
+
lines.push(
|
|
2528
|
+
`\u{1F493} Heartbeat: ${cronInfo.heartbeatRunning ? "running" : "stopped"} (${mins}min)`
|
|
2529
|
+
);
|
|
2530
|
+
if (ctx.loadedCapabilities?.length) {
|
|
2531
|
+
lines.push(`\u{1F50C} Capabilities: ${ctx.loadedCapabilities.join(", ")}`);
|
|
2532
|
+
}
|
|
2533
|
+
lines.push(`\u{1F634} Last sleep: ${cronInfo.lastSleepAudit ?? "(never)"}`);
|
|
2534
|
+
const sp = ctx.sleepProgress?.();
|
|
2535
|
+
if (sp) {
|
|
2536
|
+
lines.push(`\u{1F634} Sleep: ${sp.percent}% (${sp.step})`);
|
|
2537
|
+
}
|
|
2538
|
+
try {
|
|
2539
|
+
const lock = JSON.parse(readFileSync(join2(abtarsHome(), "bridge.lock"), "utf-8"));
|
|
2540
|
+
if (lock.lastHeartbeat > 0) lines.push(`\u{1FAC0} Last tick: ${Math.round((Date.now() - lock.lastHeartbeat) / 6e4)}min ago`);
|
|
2541
|
+
} catch (err) {
|
|
2542
|
+
logAndSwallow("command_handlers", "op", err);
|
|
2543
|
+
}
|
|
2544
|
+
try {
|
|
2545
|
+
const ce = readEntries();
|
|
2546
|
+
const r = ce.filter((e) => e.schedule && !e.paused).length;
|
|
2547
|
+
const p = ce.filter((e) => !e.fired && !e.schedule).length;
|
|
2548
|
+
const pa = ce.filter((e) => e.paused).length;
|
|
2549
|
+
lines.push(`\u23F0 Tasks: ${r} recurring, ${p} pending${pa ? `, ${pa} paused` : ""}`);
|
|
2550
|
+
} catch (err) {
|
|
2551
|
+
logAndSwallow("command_handlers", "op", err);
|
|
2552
|
+
}
|
|
2553
|
+
try {
|
|
2554
|
+
const bd = join2(homedir(), ".backup-abtars");
|
|
2555
|
+
const bk = readdirSync(bd).filter((f) => f.startsWith("abtars-")).sort();
|
|
2556
|
+
if (bk.length > 0) lines.push(`\u{1F4BE} Last backup: ${bk[bk.length - 1]}`);
|
|
2557
|
+
} catch (err) {
|
|
2558
|
+
logAndSwallow("command_handlers", "op", err);
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
lines.push("");
|
|
2562
|
+
lines.push("Use /mcp for MCP server status.");
|
|
2563
|
+
return lines;
|
|
2564
|
+
}
|
|
2565
|
+
function formatUptime(ms) {
|
|
2566
|
+
const h = Math.floor(ms / 36e5);
|
|
2567
|
+
const m = Math.floor(ms % 36e5 / 6e4);
|
|
2568
|
+
return h > 0 ? `${h}h ${m}m` : `${m}m`;
|
|
2569
|
+
}
|
|
2570
|
+
async function handleUsage(_text, ctx) {
|
|
2571
|
+
const { readUsage, resetUsage } = await import("./usage-tracker-S4Z2G2K5.js");
|
|
2572
|
+
const { loadModels } = await import("./transport-config-ANPS2RYT.js");
|
|
2573
|
+
const arg = _text.replace("/usage", "").trim();
|
|
2574
|
+
if (arg === "reset") {
|
|
2575
|
+
resetUsage();
|
|
2576
|
+
await ctx.reply("\u2713 Usage stats reset.");
|
|
2577
|
+
return true;
|
|
2578
|
+
}
|
|
2579
|
+
const models = loadModels();
|
|
2580
|
+
const costTable = /* @__PURE__ */ new Map();
|
|
2581
|
+
for (const [id, entry] of Object.entries(models)) {
|
|
2582
|
+
costTable.set(id, entry.cost);
|
|
2583
|
+
}
|
|
2584
|
+
const now = Date.now();
|
|
2585
|
+
const startOfToday = (/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0);
|
|
2586
|
+
const startOfYesterday = startOfToday - 864e5;
|
|
2587
|
+
const startOfDayBefore = startOfYesterday - 864e5;
|
|
2588
|
+
const today = readUsage(startOfToday, costTable);
|
|
2589
|
+
const yesterday = readUsage(startOfYesterday, costTable);
|
|
2590
|
+
const dayBefore = readUsage(startOfDayBefore, costTable);
|
|
2591
|
+
const week = readUsage(now - 7 * 864e5, costTable);
|
|
2592
|
+
const month = readUsage(now - 30 * 864e5, costTable);
|
|
2593
|
+
const yIn = yesterday.inputTokens - today.inputTokens;
|
|
2594
|
+
const yOut = yesterday.outputTokens - today.outputTokens;
|
|
2595
|
+
const yCost = yesterday.cost - today.cost;
|
|
2596
|
+
const dbIn = dayBefore.inputTokens - yesterday.inputTokens;
|
|
2597
|
+
const dbOut = dayBefore.outputTokens - yesterday.outputTokens;
|
|
2598
|
+
const dbCost = dayBefore.cost - yesterday.cost;
|
|
2599
|
+
const fmt = (n) => n >= 1e6 ? `${(n / 1e6).toFixed(1)}M` : n >= 1e3 ? `${(n / 1e3).toFixed(1)}k` : String(n);
|
|
2600
|
+
const fmtCost = (c) => c < 0.01 ? `$${c.toFixed(4)}` : `$${c.toFixed(2)}`;
|
|
2601
|
+
let msg = `\u{1F4CA} Token usage
|
|
2602
|
+
|
|
2603
|
+
`;
|
|
2604
|
+
msg += `Today: ${fmt(today.inputTokens)} / ${fmt(today.outputTokens)} \u2014 ${fmtCost(today.cost)}
|
|
2605
|
+
`;
|
|
2606
|
+
msg += `Yesterday: ${fmt(yIn)} / ${fmt(yOut)} \u2014 ${fmtCost(yCost)}
|
|
2607
|
+
`;
|
|
2608
|
+
msg += `Day before: ${fmt(dbIn)} / ${fmt(dbOut)} \u2014 ${fmtCost(dbCost)}
|
|
2609
|
+
`;
|
|
2610
|
+
msg += `Last 7d: ${fmt(week.inputTokens)} / ${fmt(week.outputTokens)} \u2014 ${fmtCost(week.cost)}
|
|
2611
|
+
`;
|
|
2612
|
+
msg += `Last 30d: ${fmt(month.inputTokens)} / ${fmt(month.outputTokens)} \u2014 ${fmtCost(month.cost)}
|
|
2613
|
+
`;
|
|
2614
|
+
if (arg === "detail") {
|
|
2615
|
+
msg += `
|
|
2616
|
+
\u{1F4CB} Today by model:
|
|
2617
|
+
`;
|
|
2618
|
+
for (const [model, stats] of today.byModel) {
|
|
2619
|
+
msg += ` ${model}: ${fmt(stats.in)}/${fmt(stats.out)} \u2014 ${fmtCost(stats.cost)}
|
|
2620
|
+
`;
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
const { fetchOpenRouterCredits } = await import("./openrouter-credits-L45SYKT3.js");
|
|
2624
|
+
const credits = await fetchOpenRouterCredits();
|
|
2625
|
+
if (credits) {
|
|
2626
|
+
msg += `
|
|
2627
|
+
\u{1F4B3} OpenRouter: $${credits.remaining.toFixed(2)} remaining ($${credits.purchased.toFixed(2)} purchased, $${credits.used.toFixed(2)} used)`;
|
|
2628
|
+
}
|
|
2629
|
+
await ctx.reply(msg.trim());
|
|
2630
|
+
return true;
|
|
2631
|
+
}
|
|
2632
|
+
async function handleOpenRouter(_text, ctx) {
|
|
2633
|
+
const { fetchOpenRouterCredits } = await import("./openrouter-credits-L45SYKT3.js");
|
|
2634
|
+
const credits = await fetchOpenRouterCredits();
|
|
2635
|
+
if (!credits) {
|
|
2636
|
+
await ctx.reply("\u274C OpenRouter API key not set or request failed.");
|
|
2637
|
+
return true;
|
|
2638
|
+
}
|
|
2639
|
+
await ctx.reply(
|
|
2640
|
+
`\u{1F4B3} OpenRouter credits
|
|
2641
|
+
|
|
2642
|
+
Purchased: $${credits.purchased.toFixed(2)}
|
|
2643
|
+
Used: $${credits.used.toFixed(2)}
|
|
2644
|
+
Remaining: $${credits.remaining.toFixed(2)}`
|
|
2645
|
+
);
|
|
2646
|
+
return true;
|
|
2647
|
+
}
|
|
2648
|
+
async function handleWhoami(_text, ctx) {
|
|
2649
|
+
const { loadUsers: loadUsers2 } = await import("./user-registry-GTAJIW6E.js");
|
|
2650
|
+
const reg = loadUsers2();
|
|
2651
|
+
const CLASS_NAMES = ["UNCLASSIFIED", "RESTRICTED", "CONFIDENTIAL", "SECRET"];
|
|
2652
|
+
const user = ctx.userId ? reg.byUserId.get(ctx.userId) : void 0;
|
|
2653
|
+
if (user) {
|
|
2654
|
+
const clearance = CLASS_NAMES[user.maxClass] ?? `class ${user.maxClass}`;
|
|
2655
|
+
await ctx.reply(`${user.displayName ?? user.userId} (${user.role}, ${clearance} clearance)`);
|
|
2656
|
+
} else {
|
|
2657
|
+
await ctx.reply(`${ctx.userId ?? "unknown"} (unregistered)`);
|
|
2658
|
+
}
|
|
2659
|
+
return true;
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
// src/components/nlm-command-handler.ts
|
|
2663
|
+
init_env_schema();
|
|
2664
|
+
init_logger();
|
|
2665
|
+
init_log_and_swallow();
|
|
2666
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
2667
|
+
var TAG8 = "NLMCommand";
|
|
2668
|
+
var TIMEOUT_MS = 12e4;
|
|
2669
|
+
function loadNLMConfig() {
|
|
2670
|
+
const raw = String(getEnv().notebooklmEnabled);
|
|
2671
|
+
return {
|
|
2672
|
+
enabled: raw === "true" || raw === "1",
|
|
2673
|
+
defaultNotebook: getEnv().notebooklmDefaultNotebook
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
function nlmExec(args) {
|
|
2677
|
+
const start = Date.now();
|
|
2678
|
+
logDebug(TAG8, `nlm ${args.join(" ")}`);
|
|
2679
|
+
return new Promise((resolve) => {
|
|
2680
|
+
const ac = new AbortController();
|
|
2681
|
+
const timer = setTimeout(() => ac.abort(), TIMEOUT_MS);
|
|
2682
|
+
try {
|
|
2683
|
+
execFile2("nlm", args, { signal: ac.signal, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
2684
|
+
clearTimeout(timer);
|
|
2685
|
+
logDebug(TAG8, `nlm completed in ${Date.now() - start}ms`);
|
|
2686
|
+
if (err) {
|
|
2687
|
+
if (err.name === "AbortError" || err.code === "ABORT_ERR") {
|
|
2688
|
+
resolve({ ok: false, error: `Timeout after ${TIMEOUT_MS / 1e3}s` });
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
resolve({ ok: false, error: stderr?.trim() || err.message });
|
|
2692
|
+
return;
|
|
2693
|
+
}
|
|
2694
|
+
resolve({ ok: true, data: stdout.trim() });
|
|
2695
|
+
});
|
|
2696
|
+
} catch (err) {
|
|
2697
|
+
clearTimeout(timer);
|
|
2698
|
+
resolve({ ok: false, error: err instanceof Error ? err.message : String(err) });
|
|
2699
|
+
}
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
async function handleNLMCommand(args, config) {
|
|
2703
|
+
if (!config.enabled) {
|
|
2704
|
+
return { text: "\u{1F4DA} Knowledge base is disabled." };
|
|
2705
|
+
}
|
|
2706
|
+
const parts = args.split(/\s+/).filter(Boolean);
|
|
2707
|
+
const sub = parts[0] ?? "";
|
|
2708
|
+
try {
|
|
2709
|
+
switch (sub) {
|
|
2710
|
+
case "list":
|
|
2711
|
+
return await handleList();
|
|
2712
|
+
case "create":
|
|
2713
|
+
return await handleCreate(parts.slice(1).join(" "));
|
|
2714
|
+
case "sources":
|
|
2715
|
+
return await handleSources(parts[1] ?? "");
|
|
2716
|
+
case "query":
|
|
2717
|
+
return await handleQuery(parts.slice(1).join(" "), config);
|
|
2718
|
+
default:
|
|
2719
|
+
return { text: "Usage: /nlm list | /nlm create <name> | /nlm sources <notebook-id> | /nlm query <question>" };
|
|
2720
|
+
}
|
|
2721
|
+
} catch (err) {
|
|
2722
|
+
logError(TAG8, "NLM command failed", err);
|
|
2723
|
+
return { text: `\u274C NLM error: ${err instanceof Error ? err.message : String(err)}` };
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
async function handleList() {
|
|
2727
|
+
const result = await nlmExec(["notebook", "list", "--json"]);
|
|
2728
|
+
if (!result.ok) return { text: `\u274C ${result.error}` };
|
|
2729
|
+
try {
|
|
2730
|
+
const notebooks = JSON.parse(result.data);
|
|
2731
|
+
if (!Array.isArray(notebooks) || notebooks.length === 0) return { text: "\u{1F4DA} No notebooks found." };
|
|
2732
|
+
const lines = notebooks.map(
|
|
2733
|
+
(n) => `\u2022 ${n.title ?? n.name ?? "?"} (${n.id ?? n.notebook_id ?? "?"})`
|
|
2734
|
+
);
|
|
2735
|
+
return { text: `\u{1F4DA} Notebooks:
|
|
2736
|
+
|
|
2737
|
+
${lines.join("\n")}` };
|
|
2738
|
+
} catch (err) {
|
|
2739
|
+
logAndSwallow(TAG8, "JSON.parse notebook list", err);
|
|
2740
|
+
return { text: `\u{1F4DA} ${result.data}` };
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
async function handleCreate(name) {
|
|
2744
|
+
if (!name) return { text: "Usage: /nlm create <name>" };
|
|
2745
|
+
const result = await nlmExec(["notebook", "create", name, "--json"]);
|
|
2746
|
+
if (!result.ok) return { text: `\u274C ${result.error}` };
|
|
2747
|
+
try {
|
|
2748
|
+
const parsed = JSON.parse(result.data);
|
|
2749
|
+
const id = parsed.notebook_id ?? parsed.id ?? "?";
|
|
2750
|
+
logInfo(TAG8, `Created notebook "${name}" \u2192 ${id}`);
|
|
2751
|
+
return { text: `\u2705 Notebook "${name}" created (${id})` };
|
|
2752
|
+
} catch (err) {
|
|
2753
|
+
logAndSwallow(TAG8, "JSON.parse notebook create", err);
|
|
2754
|
+
return { text: `\u2705 ${result.data}` };
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
async function handleSources(notebookId) {
|
|
2758
|
+
if (!notebookId) return { text: "Usage: /nlm sources <notebook-id>" };
|
|
2759
|
+
const result = await nlmExec(["source", "list", notebookId, "--json"]);
|
|
2760
|
+
if (!result.ok) return { text: `\u274C ${result.error}` };
|
|
2761
|
+
try {
|
|
2762
|
+
const sources = JSON.parse(result.data);
|
|
2763
|
+
if (!Array.isArray(sources) || sources.length === 0) return { text: `\u{1F4C4} No sources in notebook.` };
|
|
2764
|
+
const lines = sources.map(
|
|
2765
|
+
(s) => `\u2022 [${s.type ?? s.source_type ?? "?"}] ${s.title ?? s.name ?? "?"}`
|
|
2766
|
+
);
|
|
2767
|
+
return { text: `\u{1F4C4} Sources:
|
|
2768
|
+
|
|
2769
|
+
${lines.join("\n")}` };
|
|
2770
|
+
} catch (err) {
|
|
2771
|
+
logAndSwallow(TAG8, "JSON.parse source list", err);
|
|
2772
|
+
return { text: `\u{1F4C4} ${result.data}` };
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
async function handleQuery(question, config) {
|
|
2776
|
+
if (!question) return { text: "Usage: /nlm query <question>" };
|
|
2777
|
+
const nbId = config.defaultNotebook;
|
|
2778
|
+
if (!nbId) return { text: "\u274C No default notebook configured (NOTEBOOKLM_DEFAULT_NOTEBOOK)." };
|
|
2779
|
+
const result = await nlmExec(["notebook", "query", nbId, question, "--json"]);
|
|
2780
|
+
if (!result.ok) return { text: `\u274C ${result.error}` };
|
|
2781
|
+
try {
|
|
2782
|
+
const parsed = JSON.parse(result.data);
|
|
2783
|
+
const answer = parsed.answer ?? parsed.response ?? result.data;
|
|
2784
|
+
const sources = parsed.sources_used ?? parsed.citations ?? [];
|
|
2785
|
+
const citations = Array.isArray(sources) && sources.length > 0 ? `
|
|
2786
|
+
|
|
2787
|
+
\u{1F4CE} Sources: ${sources.map((s) => s.title ?? s.name ?? s.source_name ?? "?").join(", ")}` : "";
|
|
2788
|
+
logInfo(TAG8, `Query OK \u2014 answerLen=${String(answer).length}`);
|
|
2789
|
+
return { text: `\u{1F4DA} ${answer}${citations}` };
|
|
2790
|
+
} catch (err) {
|
|
2791
|
+
logAndSwallow(TAG8, "JSON.parse query result", err);
|
|
2792
|
+
return { text: `\u{1F4DA} ${result.data}` };
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
|
|
2796
|
+
// src/components/commands/handlers-memory.ts
|
|
2797
|
+
async function handleMemory(_text, ctx) {
|
|
2798
|
+
if (!ctx.memory) {
|
|
2799
|
+
await ctx.reply("\u{1F9E0} Memory is disabled.");
|
|
2800
|
+
return true;
|
|
2801
|
+
}
|
|
2802
|
+
const stats = ctx.memory.getStats(ctx.userId);
|
|
2803
|
+
if (!stats) {
|
|
2804
|
+
await ctx.reply("\u26A0\uFE0F Could not retrieve memory stats.");
|
|
2805
|
+
return true;
|
|
2806
|
+
}
|
|
2807
|
+
const dbMb = (stats.dbSizeBytes / (1024 * 1024)).toFixed(1);
|
|
2808
|
+
const types = Object.entries(stats.extractedByType).map(([t, n]) => ` ${t}: ${n}`).join("\n") || " (none)";
|
|
2809
|
+
const msg = [
|
|
2810
|
+
"\u{1F9E0} Memory Status",
|
|
2811
|
+
"",
|
|
2812
|
+
`\u{1F4AC} Raw messages: ${stats.totalMessages}`,
|
|
2813
|
+
`\u{1F9E9} Extracted memories: ${stats.extractedMemories}`,
|
|
2814
|
+
types,
|
|
2815
|
+
`\u{1F511} Preserved keywords: ${stats.preservedKeywords}`,
|
|
2816
|
+
"",
|
|
2817
|
+
`\u{1F4C4} Consolidations:`,
|
|
2818
|
+
` daily: ${stats.consolidationFiles.daily}`,
|
|
2819
|
+
` weekly: ${stats.consolidationFiles.weekly}`,
|
|
2820
|
+
` quarterly: ${stats.consolidationFiles.quarterly}`,
|
|
2821
|
+
"",
|
|
2822
|
+
`\u{1F4C4} Ingested documents: ${stats.ingestedDocuments}`,
|
|
2823
|
+
`\u{1F493} Heartbeat: ${stats.heartbeatRunning ? "running" : "stopped"}`,
|
|
2824
|
+
`\u{1F4BE} DB size: ${dbMb} MB`,
|
|
2825
|
+
"",
|
|
2826
|
+
`\u{1F4DA} Layer 6 (NotebookLM): ${ctx.nlmConfig.enabled ? "enabled" : "disabled"}`
|
|
2827
|
+
].join("\n");
|
|
2828
|
+
await ctx.reply(msg);
|
|
2829
|
+
return true;
|
|
2830
|
+
}
|
|
2831
|
+
async function handleFacts(_text, ctx) {
|
|
2832
|
+
if (ctx.memory) {
|
|
2833
|
+
const facts = ctx.memory.readCoreKnowledge();
|
|
2834
|
+
await ctx.reply(facts ? `\u{1F4CB} Core knowledge:
|
|
2835
|
+
|
|
2836
|
+
${facts}` : "\u{1F4CB} No core knowledge yet.");
|
|
2837
|
+
} else {
|
|
2838
|
+
await ctx.reply("\u{1F9E0} Memory is disabled.");
|
|
2839
|
+
}
|
|
2840
|
+
return true;
|
|
2841
|
+
}
|
|
2842
|
+
async function handleNlm(text, ctx) {
|
|
2843
|
+
const args = text.slice("/nlm".length).trim();
|
|
2844
|
+
const result = await handleNLMCommand(args, ctx.nlmConfig);
|
|
2845
|
+
await ctx.reply(result.text);
|
|
2846
|
+
return true;
|
|
2847
|
+
}
|
|
2848
|
+
|
|
2849
|
+
// src/components/commands/handlers-sleep.ts
|
|
2850
|
+
init_logger();
|
|
2851
|
+
init_log_and_swallow();
|
|
2852
|
+
init_env_schema();
|
|
2853
|
+
import { spawn } from "node:child_process";
|
|
2854
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync2, unlinkSync } from "node:fs";
|
|
2855
|
+
import { join as join3 } from "node:path";
|
|
2856
|
+
import { platform } from "node:os";
|
|
2857
|
+
var TAG9 = "cmd";
|
|
2858
|
+
async function handleSleep(_text, ctx) {
|
|
2859
|
+
const sleepStatus = readBridgeLockField("sleepStatus") ?? "awake";
|
|
2860
|
+
const progress = ctx.sleepProgress?.();
|
|
2861
|
+
const force = readBridgeLockField("forceSleep");
|
|
2862
|
+
const bedTime = getEnv().bedTime.raw;
|
|
2863
|
+
const auditDir = ctx.memoryConfig?.memoryDir ? join3(ctx.memoryConfig.memoryDir, "sleep") : "";
|
|
2864
|
+
const lock = auditDir ? readLatestSleepLock(auditDir) : null;
|
|
2865
|
+
const lines = ["\u{1F634} Sleep status"];
|
|
2866
|
+
const stateLabel = progress ? `\u{1F9E0} Sleep cycle running (${progress.step}, ${progress.percent}%)` : sleepStatus === "sleeping" ? "\u{1F634} Asleep (idle)" : sleepStatus === "hw_sleep" ? "\u{1F634} Hardware sleep" : "\u{1F44B} Awake";
|
|
2867
|
+
lines.push(` State: ${stateLabel}`);
|
|
2868
|
+
if (lock) {
|
|
2869
|
+
const counts = Object.values(lock.steps);
|
|
2870
|
+
const ok = counts.filter((s) => s.status === "ok").length;
|
|
2871
|
+
const failed = counts.filter((s) => s.status === "failed").length;
|
|
2872
|
+
const skipped = counts.filter((s) => s.status === "skipped").length;
|
|
2873
|
+
lines.push(` Last cycle: ${lock.date} \u2014 ${ok} ok, ${failed} failed, ${skipped} skipped (${lock.status}, ${lock.llmCalls} LLM calls)`);
|
|
2874
|
+
} else {
|
|
2875
|
+
lines.push(" Last cycle: (none found)");
|
|
2876
|
+
}
|
|
2877
|
+
lines.push(` Schedule: BED_TIME=${bedTime}`);
|
|
2878
|
+
if (force) lines.push(` Force-trigger: ${force}`);
|
|
2879
|
+
lines.push("");
|
|
2880
|
+
lines.push("/sleep resume \u2014 retry failed steps");
|
|
2881
|
+
lines.push("/sleep now \u2014 full fresh cycle");
|
|
2882
|
+
await ctx.reply(lines.join("\n"));
|
|
2883
|
+
return true;
|
|
2884
|
+
}
|
|
2885
|
+
async function handleSleepSub(text, ctx) {
|
|
2886
|
+
const sub = text.replace(/^\/sleep\s+/i, "").trim().toLowerCase();
|
|
2887
|
+
const sleepStatus = readBridgeLockField("sleepStatus") ?? "awake";
|
|
2888
|
+
if (sleepStatus === "sleeping") {
|
|
2889
|
+
await ctx.reply("\u{1F634} Sleep already running.");
|
|
2890
|
+
return true;
|
|
2891
|
+
}
|
|
2892
|
+
const auditDir = ctx.memoryConfig?.memoryDir ? join3(ctx.memoryConfig.memoryDir, "sleep") : "";
|
|
2893
|
+
if (sub === "resume") {
|
|
2894
|
+
const lock = auditDir ? readLatestSleepLock(auditDir) : null;
|
|
2895
|
+
const hasFailed = lock && Object.values(lock.steps).some((s) => s.status === "failed");
|
|
2896
|
+
if (!lock || lock.status === "completed" || !hasFailed) {
|
|
2897
|
+
await ctx.reply("No failed sleep cycle to resume \u2014 use /sleep now for a fresh run.");
|
|
2898
|
+
return true;
|
|
2899
|
+
}
|
|
2900
|
+
writeForceSleep("resume via /sleep resume");
|
|
2901
|
+
await ctx.reply("Sleep resume queued");
|
|
2902
|
+
logInfo(TAG9, "Sleep resume triggered via /sleep resume");
|
|
2903
|
+
return true;
|
|
2904
|
+
}
|
|
2905
|
+
if (sub === "now") {
|
|
2906
|
+
if (auditDir) {
|
|
2907
|
+
try {
|
|
2908
|
+
unlinkSync(todayLockPath(auditDir));
|
|
2909
|
+
} catch (err) {
|
|
2910
|
+
logAndSwallow("command_handlers", "op", err);
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
writeForceSleep("fresh via /sleep now");
|
|
2914
|
+
await ctx.reply("\u26A1 Fresh sleep cycle queued \u2014 starts on next heartbeat tick (\u22645min)");
|
|
2915
|
+
logInfo(TAG9, "Fresh sleep triggered via /sleep now");
|
|
2916
|
+
return true;
|
|
2917
|
+
}
|
|
2918
|
+
await ctx.reply("Unknown subcommand. Use /sleep, /sleep resume, or /sleep now.");
|
|
2919
|
+
return true;
|
|
2920
|
+
}
|
|
2921
|
+
async function handleWakeup(_text, ctx) {
|
|
2922
|
+
if (readBridgeLockField("sleepStatus") !== "hw_sleep") {
|
|
2923
|
+
await ctx.reply("Already awake.");
|
|
2924
|
+
return true;
|
|
2925
|
+
}
|
|
2926
|
+
const os = platform();
|
|
2927
|
+
let child = null;
|
|
2928
|
+
if (os === "darwin") {
|
|
2929
|
+
child = spawn("caffeinate", ["-su"], { stdio: "ignore", detached: true });
|
|
2930
|
+
} else if (os === "linux") {
|
|
2931
|
+
child = spawn("systemd-inhibit", ["--what=idle:sleep", "sleep", "infinity"], { stdio: "ignore", detached: true });
|
|
2932
|
+
}
|
|
2933
|
+
if (child?.pid) {
|
|
2934
|
+
child.unref();
|
|
2935
|
+
setWakeInhibitPid(child.pid);
|
|
2936
|
+
writeSleepStatus("awake");
|
|
2937
|
+
const bedTime = getEnv().bedTime.raw;
|
|
2938
|
+
await ctx.reply(`\u2600\uFE0F Awake! Will sleep again at ${bedTime} or when requested.`);
|
|
2939
|
+
logInfo("wakeup", `Emergency wake \u2014 inhibit pid=${child.pid}`);
|
|
2940
|
+
} else {
|
|
2941
|
+
writeSleepStatus("awake");
|
|
2942
|
+
await ctx.reply("\u2600\uFE0F Awake! (sleep inhibitor not available on this platform)");
|
|
2943
|
+
}
|
|
2944
|
+
return true;
|
|
2945
|
+
}
|
|
2946
|
+
function readLatestSleepLock(auditDir) {
|
|
2947
|
+
try {
|
|
2948
|
+
const files = readdirSync2(auditDir).filter((f) => f.startsWith("sleep_") && f.endsWith(".lock")).sort();
|
|
2949
|
+
if (files.length === 0) return null;
|
|
2950
|
+
const latest = files[files.length - 1];
|
|
2951
|
+
const raw = JSON.parse(readFileSync2(join3(auditDir, latest), "utf-8"));
|
|
2952
|
+
const dateMatch = latest.match(/sleep_(\d{4})(\d{2})(\d{2})/);
|
|
2953
|
+
const date = dateMatch ? `${dateMatch[1]}-${dateMatch[2]}-${dateMatch[3]}` : "unknown";
|
|
2954
|
+
return { date, status: raw.status ?? "unknown", llmCalls: raw.llmCalls ?? 0, steps: raw.steps ?? {} };
|
|
2955
|
+
} catch (err) {
|
|
2956
|
+
logAndSwallow(TAG9, "readLastSleepAudit", err);
|
|
2957
|
+
return null;
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
function todayLockPath(auditDir) {
|
|
2961
|
+
const d = /* @__PURE__ */ new Date();
|
|
2962
|
+
const ds = `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`;
|
|
2963
|
+
return join3(auditDir, `sleep_${ds}.lock`);
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2966
|
+
// src/components/commands/handlers-tasks.ts
|
|
2967
|
+
init_logger();
|
|
2968
|
+
init_log_and_swallow();
|
|
2969
|
+
var TAG10 = "cmd_tasks";
|
|
2970
|
+
async function handleTasksList(_text, ctx) {
|
|
2971
|
+
const tz = process.env["TZ"] || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
2972
|
+
const now = (/* @__PURE__ */ new Date()).toLocaleString("en-GB", { timeZone: tz, dateStyle: "medium", timeStyle: "medium" });
|
|
2973
|
+
let listing;
|
|
2974
|
+
try {
|
|
2975
|
+
const { readEntries: readEntries2 } = await import("./task-store-KIBFZL5A.js");
|
|
2976
|
+
const entries = readEntries2();
|
|
2977
|
+
const active = entries.filter((e) => !e.fired);
|
|
2978
|
+
active.sort((a, b) => {
|
|
2979
|
+
const timeOf = (e) => {
|
|
2980
|
+
const s = e.schedule;
|
|
2981
|
+
if (!s) return e.fireAt ?? 0;
|
|
2982
|
+
const parts = s.split(" ");
|
|
2983
|
+
return parseInt(parts[1] ?? "0", 10) * 60 + parseInt(parts[0] ?? "0", 10);
|
|
2984
|
+
};
|
|
2985
|
+
return timeOf(a) - timeOf(b);
|
|
2986
|
+
});
|
|
2987
|
+
const today = /* @__PURE__ */ new Date();
|
|
2988
|
+
const dow = today.getDay();
|
|
2989
|
+
const lines = active.map((e) => {
|
|
2990
|
+
const sched = e.schedule ?? "one-shot";
|
|
2991
|
+
let runsToday = true;
|
|
2992
|
+
if (sched !== "one-shot") {
|
|
2993
|
+
const parts = sched.split(" ");
|
|
2994
|
+
const dowField = parts[4] ?? "*";
|
|
2995
|
+
if (dowField !== "*") {
|
|
2996
|
+
const allowed = /* @__PURE__ */ new Set();
|
|
2997
|
+
for (const seg of dowField.split(",")) {
|
|
2998
|
+
if (seg.includes("-")) {
|
|
2999
|
+
const [a, b] = seg.split("-").map(Number);
|
|
3000
|
+
for (let i = a; i <= b; i++) allowed.add(i);
|
|
3001
|
+
} else allowed.add(Number(seg));
|
|
3002
|
+
}
|
|
3003
|
+
runsToday = allowed.has(dow);
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
const succeeded = e.history?.some((h) => h.exitCode === 0 && new Date(h.ts).toDateString() === today.toDateString());
|
|
3007
|
+
const failed = e.history?.some((h) => h.exitCode !== void 0 && h.exitCode !== 0 && new Date(h.ts).toDateString() === today.toDateString());
|
|
3008
|
+
const started = e.lastRanAt && new Date(e.lastRanAt).toDateString() === today.toDateString();
|
|
3009
|
+
const running2 = ctx.cronCurrentJob?.entryId === e.id;
|
|
3010
|
+
const tick = e.paused ? "\u23F8" : !runsToday ? "\u2014" : succeeded ? "\u2713" : running2 ? "~" : failed ? "\u2717" : started ? "\u2717" : "+";
|
|
3011
|
+
const label = e.title || e.message.split("\n")[0].replace(/[~\/][\w.\/-]+\//g, "").slice(0, 30);
|
|
3012
|
+
return `${tick} ${e.id.padEnd(22)}${sched.padEnd(16)}${label}`;
|
|
3013
|
+
});
|
|
3014
|
+
listing = lines.length > 0 ? "<pre>" + lines.join("\n") + "</pre>" : "(no active entries)";
|
|
3015
|
+
} catch (err) {
|
|
3016
|
+
logError("tasks", `Failed to read cron: ${err instanceof Error ? err.message : String(err)}`);
|
|
3017
|
+
listing = "(no active entries)";
|
|
3018
|
+
}
|
|
3019
|
+
let running = "";
|
|
3020
|
+
if (ctx.cronCurrentJob) {
|
|
3021
|
+
const j = ctx.cronCurrentJob;
|
|
3022
|
+
const ago = Math.round((Date.now() - j.startedAt) / 1e3);
|
|
3023
|
+
running = `
|
|
3024
|
+
\u25B6 Running: ${j.type} (pid ${j.pid}, ${ago}s ago)
|
|
3025
|
+
${j.entryId}`;
|
|
3026
|
+
}
|
|
3027
|
+
await ctx.reply(`\u23F0 ${now}
|
|
3028
|
+
|
|
3029
|
+
${listing}${running}`, { parseMode: "HTML" });
|
|
3030
|
+
return true;
|
|
3031
|
+
}
|
|
3032
|
+
async function handleTasksTrigger(text, ctx) {
|
|
3033
|
+
const id = text.replace(/^\/(tasks?|cron) run /, "").trim();
|
|
3034
|
+
if (!id) {
|
|
3035
|
+
await ctx.reply("Usage: /task run <cron-id>");
|
|
3036
|
+
return true;
|
|
3037
|
+
}
|
|
3038
|
+
const err = ctx.enqueueCron?.(id, true);
|
|
3039
|
+
await ctx.reply(err ?? `\u23F3 Running: ${id}`);
|
|
3040
|
+
return true;
|
|
3041
|
+
}
|
|
3042
|
+
async function handleTasksLog(text, ctx) {
|
|
3043
|
+
const id = text.replace(/^\/(tasks?|cron) log /, "").trim();
|
|
3044
|
+
const placeholderId = await ctx.reply("\u{1F4CB} Loading task log...");
|
|
3045
|
+
try {
|
|
3046
|
+
const raw = await execAsync("abtars-task", ["history", id], 5e3);
|
|
3047
|
+
if (!raw) throw new Error("empty");
|
|
3048
|
+
const data = JSON.parse(raw);
|
|
3049
|
+
if (!data.ok) {
|
|
3050
|
+
const msg = `\u274C ${data.error}`;
|
|
3051
|
+
if (placeholderId !== void 0 && ctx.editReply) await ctx.editReply(placeholderId, msg);
|
|
3052
|
+
else await ctx.reply(msg);
|
|
3053
|
+
return true;
|
|
3054
|
+
}
|
|
3055
|
+
const runs = data.runs.slice(-5);
|
|
3056
|
+
const lines = runs.map((r) => `${r.ranAt} exit=${r.exitCode ?? "?"}`);
|
|
3057
|
+
const body = `\u{1F4CB} ${data.message}
|
|
3058
|
+
|
|
3059
|
+
\`\`\`
|
|
3060
|
+
${lines.join("\n") || "(no runs)"}
|
|
3061
|
+
\`\`\``;
|
|
3062
|
+
if (placeholderId !== void 0 && ctx.editReply) await ctx.editReply(placeholderId, body);
|
|
3063
|
+
else await ctx.reply(body, { parseMode: "Markdown" });
|
|
3064
|
+
} catch (err) {
|
|
3065
|
+
logAndSwallow(TAG10, "read task history", err);
|
|
3066
|
+
const msg = "\u274C Failed to read history";
|
|
3067
|
+
if (placeholderId !== void 0 && ctx.editReply) await ctx.editReply(placeholderId, msg);
|
|
3068
|
+
else await ctx.reply(msg);
|
|
3069
|
+
}
|
|
3070
|
+
return true;
|
|
3071
|
+
}
|
|
3072
|
+
async function handleTaskPause(text, ctx) {
|
|
3073
|
+
const match = text.match(/^\/(tasks?|cron)\s+(pause|resume)\s+(.+)/i);
|
|
3074
|
+
if (!match) {
|
|
3075
|
+
await ctx.reply("Usage: /task pause|resume <id>");
|
|
3076
|
+
return true;
|
|
3077
|
+
}
|
|
3078
|
+
const action = match[2].toLowerCase();
|
|
3079
|
+
const id = match[3].trim();
|
|
3080
|
+
try {
|
|
3081
|
+
const raw = await execAsync("abtars-task", [action, id], 5e3);
|
|
3082
|
+
const data = JSON.parse(raw || "{}");
|
|
3083
|
+
await ctx.reply(data.ok ? `\u2713 ${id} ${action}d` : `\u274C ${data.error ?? "unknown error"}`);
|
|
3084
|
+
} catch (err) {
|
|
3085
|
+
await ctx.reply(`\u274C Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3086
|
+
}
|
|
3087
|
+
return true;
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
// src/components/commands/handlers-admin.ts
|
|
3091
|
+
init_log_and_swallow();
|
|
3092
|
+
init_paths();
|
|
3093
|
+
import { readdirSync as readdirSync3 } from "node:fs";
|
|
3094
|
+
import { join as join4 } from "node:path";
|
|
3095
|
+
var TAG11 = "cmd_admin";
|
|
3096
|
+
async function handleUsers(text, ctx) {
|
|
3097
|
+
const { loadUsers: loadUsers2 } = await import("./user-registry-GTAJIW6E.js");
|
|
3098
|
+
const parts = text.trim().split(/\s+/);
|
|
3099
|
+
const sub = parts[1];
|
|
3100
|
+
if (sub === "approve" && parts[2]) {
|
|
3101
|
+
const platformId = parts[2];
|
|
3102
|
+
const { writeFileSync: writeFileSync2, readFileSync: readFileSync3 } = await import("node:fs");
|
|
3103
|
+
const { join: join5 } = await import("node:path");
|
|
3104
|
+
const { abtarsHome: abtarsHome2 } = await import("./paths-ZJYIDND2.js");
|
|
3105
|
+
const configPath = join5(abtarsHome2(), "config", "users.json");
|
|
3106
|
+
try {
|
|
3107
|
+
const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
3108
|
+
const users = Array.isArray(raw.users) ? raw.users : [];
|
|
3109
|
+
if (users.some((u) => String(u.platforms?.telegram) === platformId)) {
|
|
3110
|
+
await ctx.reply(`User with platform ID ${platformId} already exists.`);
|
|
3111
|
+
return true;
|
|
3112
|
+
}
|
|
3113
|
+
const guestId = `guest-${platformId}`;
|
|
3114
|
+
users.push({ userId: guestId, role: "guest", maxClass: 0, tools: [], platforms: { telegram: parseInt(platformId, 10) || 0 } });
|
|
3115
|
+
writeFileSync2(configPath, JSON.stringify({ users }, null, 2), "utf-8");
|
|
3116
|
+
await ctx.reply(`\u2705 Approved guest: ${guestId} (platform ID: ${platformId})`);
|
|
3117
|
+
} catch (err) {
|
|
3118
|
+
await ctx.reply(`\u274C Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3119
|
+
}
|
|
3120
|
+
return true;
|
|
3121
|
+
}
|
|
3122
|
+
if (sub === "revoke" && parts[2]) {
|
|
3123
|
+
const targetUserId = parts[2];
|
|
3124
|
+
const { writeFileSync: writeFileSync2, readFileSync: readFileSync3 } = await import("node:fs");
|
|
3125
|
+
const { join: join5 } = await import("node:path");
|
|
3126
|
+
const { abtarsHome: abtarsHome2 } = await import("./paths-ZJYIDND2.js");
|
|
3127
|
+
const configPath = join5(abtarsHome2(), "config", "users.json");
|
|
3128
|
+
try {
|
|
3129
|
+
const raw = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
3130
|
+
const users = (Array.isArray(raw.users) ? raw.users : []).filter((u) => u.userId !== targetUserId);
|
|
3131
|
+
writeFileSync2(configPath, JSON.stringify({ users }, null, 2), "utf-8");
|
|
3132
|
+
await ctx.reply(`\u2705 Revoked: ${targetUserId}`);
|
|
3133
|
+
} catch (err) {
|
|
3134
|
+
await ctx.reply(`\u274C Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
3135
|
+
}
|
|
3136
|
+
return true;
|
|
3137
|
+
}
|
|
3138
|
+
const registry = loadUsers2();
|
|
3139
|
+
const CLASS_NAMES = ["UNCLASSIFIED", "RESTRICTED", "CONFIDENTIAL", "SECRET"];
|
|
3140
|
+
const lines = registry.users.map(
|
|
3141
|
+
(u) => `\u2022 ${u.userId} (${u.role}, ${CLASS_NAMES[u.maxClass] ?? `class ${u.maxClass}`}) \u2014 tools: ${u.tools.join(", ") || "none"}`
|
|
3142
|
+
);
|
|
3143
|
+
await ctx.reply(`\u{1F465} Users (${registry.users.length}):
|
|
3144
|
+
${lines.join("\n")}
|
|
3145
|
+
|
|
3146
|
+
/users approve <platformId>
|
|
3147
|
+
/users revoke <userId>`);
|
|
3148
|
+
return true;
|
|
3149
|
+
}
|
|
3150
|
+
async function handleSkills(_text, ctx) {
|
|
3151
|
+
const base = join4(abtarsHome(), "skills");
|
|
3152
|
+
const groups = ["core", "personal", "auto", "downloaded"];
|
|
3153
|
+
const sections = [];
|
|
3154
|
+
let total = 0;
|
|
3155
|
+
for (const group of groups) {
|
|
3156
|
+
const dir = join4(base, group);
|
|
3157
|
+
try {
|
|
3158
|
+
const files = readdirSync3(dir, { recursive: true }).map((f) => String(f)).filter((f) => f.endsWith(".md") && !f.endsWith("TOOLS.md")).sort();
|
|
3159
|
+
if (files.length > 0) {
|
|
3160
|
+
total += files.length;
|
|
3161
|
+
sections.push(`${group} (${files.length}):
|
|
3162
|
+
${files.map((f) => ` \u2022 ${f.replace(/\.md$/, "")}`).join("\n")}`);
|
|
3163
|
+
}
|
|
3164
|
+
} catch (err) {
|
|
3165
|
+
logAndSwallow("command_handlers", "op", err);
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
await ctx.reply(total > 0 ? `\u{1F4DA} Skills (${total}):
|
|
3169
|
+
|
|
3170
|
+
${sections.join("\n\n")}` : "\u{1F4DA} No skills found.");
|
|
3171
|
+
return true;
|
|
3172
|
+
}
|
|
3173
|
+
async function handleHooks(_text, ctx) {
|
|
3174
|
+
const { getHookSummary } = await import("./hook-system-POI5VRIX.js");
|
|
3175
|
+
const summary = getHookSummary();
|
|
3176
|
+
const lines = ["\u{1FA9D} Hooks:"];
|
|
3177
|
+
for (const { event, hooks } of summary) {
|
|
3178
|
+
if (hooks.length === 0) {
|
|
3179
|
+
lines.push(` ${event}: (none)`);
|
|
3180
|
+
} else {
|
|
3181
|
+
lines.push(` ${event}: ${hooks.map((h) => `${h.name} (${h.timeout ?? 5e3}ms)`).join(", ")}`);
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
await ctx.reply(lines.join("\n"));
|
|
3185
|
+
return true;
|
|
3186
|
+
}
|
|
3187
|
+
async function handleMcp(_text, ctx) {
|
|
3188
|
+
const arg = _text.replace(/^\/mcp\s*/i, "").trim().toLowerCase();
|
|
3189
|
+
if (arg === "start") {
|
|
3190
|
+
const { loadUsers: loadUsers2 } = await import("./user-registry-GTAJIW6E.js");
|
|
3191
|
+
const registry = loadUsers2();
|
|
3192
|
+
const user = registry.byUserId.get(ctx.userId);
|
|
3193
|
+
if (user?.role !== "master") {
|
|
3194
|
+
await ctx.reply("\u274C /mcp start is master-only.");
|
|
3195
|
+
return true;
|
|
3196
|
+
}
|
|
3197
|
+
const result = await execAsync("mcporter", ["daemon", "start"], 1e4);
|
|
3198
|
+
await ctx.reply(result ? `\u{1F4E6} mcporter daemon started.
|
|
3199
|
+
${result}` : "\u{1F4E6} mcporter daemon start failed.");
|
|
3200
|
+
return true;
|
|
3201
|
+
}
|
|
3202
|
+
let version = await execAsync("mcporter", ["--version"], 2e3);
|
|
3203
|
+
if (version === null) {
|
|
3204
|
+
await ctx.reply("\u{1F4E6} mcporter not installed");
|
|
3205
|
+
return true;
|
|
3206
|
+
}
|
|
3207
|
+
if (!version) version = "unknown";
|
|
3208
|
+
const placeholderId = await ctx.reply("\u{1F4E6} Checking MCP servers...");
|
|
3209
|
+
const raw = await execAsync("mcporter", ["list", "--json"], 15e3);
|
|
3210
|
+
let body;
|
|
3211
|
+
if (!raw) {
|
|
3212
|
+
body = `\u{1F4E6} MCP: mcporter installed (${version.split("\n")[0]}) but list failed`;
|
|
3213
|
+
} else {
|
|
3214
|
+
try {
|
|
3215
|
+
const data = JSON.parse(raw);
|
|
3216
|
+
const servers = data.servers ?? [];
|
|
3217
|
+
const ok = servers.filter((s) => s.status === "ok").length;
|
|
3218
|
+
const lines = [
|
|
3219
|
+
"\u{1F4E6} MCP status",
|
|
3220
|
+
` mcporter: installed (${version.split("\n")[0]})`,
|
|
3221
|
+
` Servers: ${ok}/${servers.length} online`
|
|
3222
|
+
];
|
|
3223
|
+
for (const s of servers) {
|
|
3224
|
+
const mark = s.status === "ok" ? "\u2713" : "\u2717";
|
|
3225
|
+
const toolCount = Array.isArray(s.tools) ? s.tools.length : s.tools ?? 0;
|
|
3226
|
+
const detail = s.status === "ok" ? `tools: ${toolCount}${s.prompts ? `, prompts: ${s.prompts}` : ""}` : s.error ?? s.status ?? "error";
|
|
3227
|
+
lines.push(` ${mark} ${s.name ?? "?"} (${detail})`);
|
|
3228
|
+
}
|
|
3229
|
+
body = lines.join("\n");
|
|
3230
|
+
} catch (err) {
|
|
3231
|
+
logAndSwallow(TAG11, "JSON.parse mcp list", err);
|
|
3232
|
+
body = "\u{1F4E6} MCP: installed, list output unparseable";
|
|
3233
|
+
}
|
|
3234
|
+
}
|
|
3235
|
+
if (placeholderId !== void 0 && ctx.editReply) {
|
|
3236
|
+
await ctx.editReply(placeholderId, body);
|
|
3237
|
+
} else {
|
|
3238
|
+
await ctx.reply(body);
|
|
3239
|
+
}
|
|
3240
|
+
return true;
|
|
3241
|
+
}
|
|
3242
|
+
async function handleHelp(_text, ctx) {
|
|
3243
|
+
const cmds = [
|
|
3244
|
+
"/reset \u2014 Reload transport + fresh session",
|
|
3245
|
+
"/reset default \u2014 Restore transport.default.json + fresh session",
|
|
3246
|
+
"/compact \u2014 Compact context window (summarize + fresh session)",
|
|
3247
|
+
"/status \u2014 Bridge status, transport, heartbeat",
|
|
3248
|
+
"/doctor \u2014 Deep probe all subsystems",
|
|
3249
|
+
"/doctor fix \u2014 Run safe auto-repairs",
|
|
3250
|
+
"/doctor fix-full \u2014 Full repair (+ FTS rebuild, WAL checkpoint)",
|
|
3251
|
+
"/mcp \u2014 MCP server status",
|
|
3252
|
+
"/hooks \u2014 List configured hooks",
|
|
3253
|
+
"/stop, /ctrlc \u2014 Stop current response",
|
|
3254
|
+
"/memory \u2014 Memory storage statistics",
|
|
3255
|
+
"/heartbeat \u2014 Heartbeat diagnostics (tasks, last tick)",
|
|
3256
|
+
"/models \u2014 Model, transport & agent status",
|
|
3257
|
+
"/models change \u2014 Switch model/provider (any agent)",
|
|
3258
|
+
"/models quick <model> \u2014 Instant switch on same provider",
|
|
3259
|
+
"/models list [provider] \u2014 List providers or models on a provider",
|
|
3260
|
+
"/models restore \u2014 Undo last model/provider switch",
|
|
3261
|
+
"/models default \u2014 Factory reset (transport.default.json)",
|
|
3262
|
+
"/models emergency \u2014 \u{1F6A8} Activate paid hailMary model (manual)",
|
|
3263
|
+
"/emergency \u2014 Shortcut for /models emergency",
|
|
3264
|
+
"/models health reset \u2014 Reset model health buckets",
|
|
3265
|
+
"/tasks \u2014 Scheduled tasks",
|
|
3266
|
+
"/tasks log <id> \u2014 Last 5 runs for a task",
|
|
3267
|
+
"/task run <id> \u2014 Manually fire a task",
|
|
3268
|
+
"/task pause <id> \u2014 Pause a task",
|
|
3269
|
+
"/task resume <id> \u2014 Resume a paused task",
|
|
3270
|
+
"/facts \u2014 Core knowledge (user profile + agent notes)",
|
|
3271
|
+
"/skills \u2014 List loaded skills",
|
|
3272
|
+
"/session \u2014 List sessions",
|
|
3273
|
+
"/session new [browse|code|task] \u2014 New session",
|
|
3274
|
+
"/session <#> \u2014 Switch session",
|
|
3275
|
+
"/session end [#] \u2014 End session (keep messages)",
|
|
3276
|
+
"/session kill <#> \u2014 Kill session (wipe messages)",
|
|
3277
|
+
"/default \u2014 Switch back to default agent",
|
|
3278
|
+
"/nlm \u2014 Knowledge base (list/create/sources/query)",
|
|
3279
|
+
"/restart \u2014 Restart CLI session",
|
|
3280
|
+
"/wakeup \u2014 Wake Mac from sleep (cancel hw_sleep)",
|
|
3281
|
+
"/sleep \u2014 Sleep status",
|
|
3282
|
+
"/sleep resume \u2014 Retry failed sleep steps",
|
|
3283
|
+
"/sleep now \u2014 Full fresh sleep cycle",
|
|
3284
|
+
"/skill reload \u2014 Regenerate skills catalog"
|
|
3285
|
+
];
|
|
3286
|
+
if (ctx.platform === "telegram") {
|
|
3287
|
+
cmds.push("/full \u2014 Raw output, TTS disabled", "/short \u2014 Clean responses (default)", "/healing \u2014 Toggle self-healer on/off");
|
|
3288
|
+
}
|
|
3289
|
+
cmds.push("/help \u2014 Show this help");
|
|
3290
|
+
cmds.push("/skills \u2014 List available skills");
|
|
3291
|
+
await ctx.reply(`\u{1F4CB} Available commands:
|
|
3292
|
+
|
|
3293
|
+
${cmds.join("\n")}`);
|
|
3294
|
+
return true;
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
// src/components/commands/session-handler.ts
|
|
3298
|
+
init_logger();
|
|
3299
|
+
init_log_and_swallow();
|
|
3300
|
+
|
|
3301
|
+
// src/components/session-manager.ts
|
|
3302
|
+
var TYPE_LABELS = { A: "Main", B: "Browse", C: "Code", T: "Task", P: "Peer", S: "System" };
|
|
3303
|
+
var TYPE_AGENT = { C: "coding", B: "browsie", P: "coding", S: "coding" };
|
|
3304
|
+
function typeLabel(t) {
|
|
3305
|
+
return TYPE_LABELS[t];
|
|
3306
|
+
}
|
|
3307
|
+
function parseSessionType(input) {
|
|
3308
|
+
switch (input.toLowerCase()) {
|
|
3309
|
+
case "browse":
|
|
3310
|
+
return "B";
|
|
3311
|
+
case "code":
|
|
3312
|
+
return "C";
|
|
3313
|
+
case "task":
|
|
3314
|
+
return "T";
|
|
3315
|
+
default:
|
|
3316
|
+
return null;
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
var SessionManager = class {
|
|
3320
|
+
states = /* @__PURE__ */ new Map();
|
|
3321
|
+
// key = "userId:platform"
|
|
3322
|
+
maxSessions;
|
|
3323
|
+
runtime = null;
|
|
3324
|
+
constructor(maxSessions = 10) {
|
|
3325
|
+
this.maxSessions = maxSessions;
|
|
3326
|
+
}
|
|
3327
|
+
/** Set runtime (called after boot phase creates it). */
|
|
3328
|
+
setRuntime(runtime) {
|
|
3329
|
+
this.runtime = runtime;
|
|
3330
|
+
}
|
|
3331
|
+
stateKey(userId, platform2) {
|
|
3332
|
+
return `${userId}:${platform2}`;
|
|
3333
|
+
}
|
|
3334
|
+
getOrCreateState(userId, platform2) {
|
|
3335
|
+
const key = this.stateKey(userId, platform2);
|
|
3336
|
+
let state = this.states.get(key);
|
|
3337
|
+
if (!state) {
|
|
3338
|
+
state = { sessions: [], activeIndex: 1, nextIndex: 1 };
|
|
3339
|
+
this.states.set(key, state);
|
|
3340
|
+
const main = this.allocateSession(state, "A", true);
|
|
3341
|
+
state.activeIndex = main.shortIndex;
|
|
3342
|
+
}
|
|
3343
|
+
return state;
|
|
3344
|
+
}
|
|
3345
|
+
allocateSession(state, type, isTransport, motherId) {
|
|
3346
|
+
const idx = state.nextIndex++;
|
|
3347
|
+
const ts = Math.floor(Date.now() / 1e3);
|
|
3348
|
+
const session = {
|
|
3349
|
+
id: `${ts}_${type}_${String(idx).padStart(2, "0")}`,
|
|
3350
|
+
shortIndex: idx,
|
|
3351
|
+
type,
|
|
3352
|
+
createdAt: Date.now(),
|
|
3353
|
+
isTransport,
|
|
3354
|
+
ended: false,
|
|
3355
|
+
paused: false,
|
|
3356
|
+
motherId
|
|
3357
|
+
};
|
|
3358
|
+
state.sessions.push(session);
|
|
3359
|
+
return session;
|
|
3360
|
+
}
|
|
3361
|
+
/** Get or create the initial main session. Returns its session ID. */
|
|
3362
|
+
getActiveSessionId(userId, platform2) {
|
|
3363
|
+
const state = this.getOrCreateState(userId, platform2);
|
|
3364
|
+
const active = state.sessions.find((s) => s.shortIndex === state.activeIndex && !s.ended);
|
|
3365
|
+
return active?.id ?? state.sessions[0].id;
|
|
3366
|
+
}
|
|
3367
|
+
/** Get the active session entry. */
|
|
3368
|
+
getActiveSession(userId, platform2) {
|
|
3369
|
+
const state = this.getOrCreateState(userId, platform2);
|
|
3370
|
+
return state.sessions.find((s) => s.shortIndex === state.activeIndex && !s.ended) ?? state.sessions[0];
|
|
3371
|
+
}
|
|
3372
|
+
/** Initialize agent session for non-Main types. Returns the AgentSession or null. */
|
|
3373
|
+
async initAgentSession(session) {
|
|
3374
|
+
const agentName = TYPE_AGENT[session.type];
|
|
3375
|
+
if (!agentName || !this.runtime) return null;
|
|
3376
|
+
if (session.agentSession) return session.agentSession;
|
|
3377
|
+
session.agentSession = await this.runtime.session(agentName);
|
|
3378
|
+
return session.agentSession;
|
|
3379
|
+
}
|
|
3380
|
+
/** Create a new transport session. Returns the session or error string. */
|
|
3381
|
+
createSession(userId, platform2, type) {
|
|
3382
|
+
const state = this.getOrCreateState(userId, platform2);
|
|
3383
|
+
const alive = state.sessions.filter((s) => !s.ended);
|
|
3384
|
+
if (alive.length >= this.maxSessions) {
|
|
3385
|
+
return `Max sessions reached (${this.maxSessions}). End or kill a session first.`;
|
|
3386
|
+
}
|
|
3387
|
+
const session = this.allocateSession(state, type, true);
|
|
3388
|
+
state.activeIndex = session.shortIndex;
|
|
3389
|
+
return session;
|
|
3390
|
+
}
|
|
3391
|
+
/** Create a sub-session ID for auto-spawn (storage tag only, no transport switch). */
|
|
3392
|
+
createSubSession(userId, platform2, type) {
|
|
3393
|
+
const state = this.getOrCreateState(userId, platform2);
|
|
3394
|
+
const alive = state.sessions.filter((s) => !s.ended);
|
|
3395
|
+
if (alive.length >= this.maxSessions) {
|
|
3396
|
+
return `Max sessions reached \u2014 auto-spawn skipped.`;
|
|
3397
|
+
}
|
|
3398
|
+
const active = state.sessions.find((s) => s.shortIndex === state.activeIndex && !s.ended);
|
|
3399
|
+
return this.allocateSession(state, type, false, active?.id);
|
|
3400
|
+
}
|
|
3401
|
+
/** Switch active transport session. Returns session or error. */
|
|
3402
|
+
switchSession(userId, platform2, index) {
|
|
3403
|
+
const state = this.getOrCreateState(userId, platform2);
|
|
3404
|
+
const target = state.sessions.find((s) => s.shortIndex === index && !s.ended && s.isTransport);
|
|
3405
|
+
if (!target) return `Session #${index} not found or not switchable.`;
|
|
3406
|
+
state.activeIndex = target.shortIndex;
|
|
3407
|
+
return target;
|
|
3408
|
+
}
|
|
3409
|
+
/** Gracefully end a session (keeps messages). Returns ended session or error. */
|
|
3410
|
+
endSession(userId, platform2, index) {
|
|
3411
|
+
const state = this.getOrCreateState(userId, platform2);
|
|
3412
|
+
const targetIdx = index ?? state.activeIndex;
|
|
3413
|
+
const target = state.sessions.find((s) => s.shortIndex === targetIdx && !s.ended);
|
|
3414
|
+
if (!target) return `Session #${targetIdx} not found.`;
|
|
3415
|
+
const aliveMains = state.sessions.filter((s) => s.type === "A" && !s.ended);
|
|
3416
|
+
if (target.type === "A" && aliveMains.length <= 1) {
|
|
3417
|
+
target.ended = true;
|
|
3418
|
+
const newMain = this.allocateSession(state, "A", true);
|
|
3419
|
+
state.activeIndex = newMain.shortIndex;
|
|
3420
|
+
return target;
|
|
3421
|
+
}
|
|
3422
|
+
target.ended = true;
|
|
3423
|
+
if (state.activeIndex === targetIdx) {
|
|
3424
|
+
const main = state.sessions.find((s) => s.type === "A" && !s.ended);
|
|
3425
|
+
state.activeIndex = main?.shortIndex ?? 1;
|
|
3426
|
+
}
|
|
3427
|
+
return target;
|
|
3428
|
+
}
|
|
3429
|
+
/** Kill a session (wipe messages). Returns killed session or error. */
|
|
3430
|
+
killSession(userId, platform2, index) {
|
|
3431
|
+
const state = this.getOrCreateState(userId, platform2);
|
|
3432
|
+
const target = state.sessions.find((s) => s.shortIndex === index && !s.ended);
|
|
3433
|
+
if (!target) return `Session #${index} not found.`;
|
|
3434
|
+
if (state.activeIndex === index) {
|
|
3435
|
+
const otherMain = state.sessions.find((s) => s.type === "A" && !s.ended && s.shortIndex !== index);
|
|
3436
|
+
if (otherMain) {
|
|
3437
|
+
state.activeIndex = otherMain.shortIndex;
|
|
3438
|
+
} else {
|
|
3439
|
+
target.ended = true;
|
|
3440
|
+
const newMain = this.allocateSession(state, "A", true);
|
|
3441
|
+
state.activeIndex = newMain.shortIndex;
|
|
3442
|
+
return target;
|
|
3443
|
+
}
|
|
3444
|
+
} else {
|
|
3445
|
+
const aliveMains = state.sessions.filter((s) => s.type === "A" && !s.ended);
|
|
3446
|
+
if (target.type === "A" && aliveMains.length <= 1) {
|
|
3447
|
+
target.ended = true;
|
|
3448
|
+
this.allocateSession(state, "A", true);
|
|
3449
|
+
return target;
|
|
3450
|
+
}
|
|
3451
|
+
}
|
|
3452
|
+
target.ended = true;
|
|
3453
|
+
return target;
|
|
3454
|
+
}
|
|
3455
|
+
/** List all sessions for a user+platform. */
|
|
3456
|
+
listSessions(userId, platform2) {
|
|
3457
|
+
const state = this.getOrCreateState(userId, platform2);
|
|
3458
|
+
return { sessions: state.sessions.filter((s) => !s.ended), activeIndex: state.activeIndex };
|
|
3459
|
+
}
|
|
3460
|
+
/** Find a session by ID across all platforms. */
|
|
3461
|
+
getSessionById(sessionId) {
|
|
3462
|
+
for (const state of this.states.values()) {
|
|
3463
|
+
const found = state.sessions.find((s) => s.id === sessionId);
|
|
3464
|
+
if (found) return found;
|
|
3465
|
+
}
|
|
3466
|
+
return void 0;
|
|
3467
|
+
}
|
|
3468
|
+
/** Pause a session (cooperative interrupt — agent loop stops between tool calls). */
|
|
3469
|
+
pauseSession(userId, platform2, index) {
|
|
3470
|
+
const state = this.getOrCreateState(userId, platform2);
|
|
3471
|
+
const targetIdx = index ?? state.activeIndex;
|
|
3472
|
+
const target = state.sessions.find((s) => s.shortIndex === targetIdx && !s.ended);
|
|
3473
|
+
if (!target) return `Session #${targetIdx} not found.`;
|
|
3474
|
+
if (target.paused) return `Session #${targetIdx} is already paused.`;
|
|
3475
|
+
target.paused = true;
|
|
3476
|
+
return target;
|
|
3477
|
+
}
|
|
3478
|
+
/** Resume a paused session. */
|
|
3479
|
+
resumeSession(userId, platform2, index) {
|
|
3480
|
+
const state = this.getOrCreateState(userId, platform2);
|
|
3481
|
+
const targetIdx = index ?? state.activeIndex;
|
|
3482
|
+
const target = state.sessions.find((s) => s.shortIndex === targetIdx && !s.ended);
|
|
3483
|
+
if (!target) return `Session #${targetIdx} not found.`;
|
|
3484
|
+
if (!target.paused) return `Session #${targetIdx} is not paused.`;
|
|
3485
|
+
target.paused = false;
|
|
3486
|
+
return target;
|
|
3487
|
+
}
|
|
3488
|
+
/** End auto-spawn sessions that have been inactive. */
|
|
3489
|
+
expireAutoSessions(timeoutMs) {
|
|
3490
|
+
const expired = [];
|
|
3491
|
+
const cutoff = Date.now() - timeoutMs;
|
|
3492
|
+
for (const state of this.states.values()) {
|
|
3493
|
+
for (const s of state.sessions) {
|
|
3494
|
+
if (!s.ended && !s.isTransport && s.createdAt < cutoff) {
|
|
3495
|
+
s.ended = true;
|
|
3496
|
+
expired.push(s);
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
return expired;
|
|
3501
|
+
}
|
|
3502
|
+
/** Clear all sessions for a platform (transport destruction). */
|
|
3503
|
+
clearPlatform(userId, platform2) {
|
|
3504
|
+
this.states.delete(this.stateKey(userId, platform2));
|
|
3505
|
+
}
|
|
3506
|
+
/** Clear everything (bridge restart). */
|
|
3507
|
+
clearAll() {
|
|
3508
|
+
this.states.clear();
|
|
3509
|
+
}
|
|
3510
|
+
/** Format session list for display. */
|
|
3511
|
+
formatList(userId, platform2) {
|
|
3512
|
+
const { sessions, activeIndex } = this.listSessions(userId, platform2);
|
|
3513
|
+
if (sessions.length === 0) return "No active sessions.";
|
|
3514
|
+
const lines = sessions.map((s) => {
|
|
3515
|
+
const marker = s.shortIndex === activeIndex ? " *" : "";
|
|
3516
|
+
const transport = s.isTransport ? "" : " (sub)";
|
|
3517
|
+
const paused = s.paused ? " \u23F8" : "";
|
|
3518
|
+
const mother = s.motherId ? ` \u2190 #${this.getSessionById(s.motherId)?.shortIndex ?? "?"}` : "";
|
|
3519
|
+
const time = new Date(s.createdAt).toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" });
|
|
3520
|
+
return `#${s.shortIndex} ${typeLabel(s.type)}${transport}${mother} \u2014 ${time}${paused}${marker}`;
|
|
3521
|
+
});
|
|
3522
|
+
return lines.join("\n");
|
|
3523
|
+
}
|
|
3524
|
+
};
|
|
3525
|
+
|
|
3526
|
+
// src/components/commands/session-handler.ts
|
|
3527
|
+
var TAG12 = "cmd:session";
|
|
3528
|
+
async function handleSession(text, ctx) {
|
|
3529
|
+
const args = text.replace(/^\/session\s*/i, "").trim();
|
|
3530
|
+
if (!args) {
|
|
3531
|
+
await ctx.reply(ctx.sessionManager.formatList(ctx.userId, ctx.platform));
|
|
3532
|
+
return true;
|
|
3533
|
+
}
|
|
3534
|
+
if (args.startsWith("new")) {
|
|
3535
|
+
const typeArg = args.slice(3).trim();
|
|
3536
|
+
const type = typeArg ? parseSessionType(typeArg) : "A";
|
|
3537
|
+
if (!type) {
|
|
3538
|
+
await ctx.reply(`Unknown session type: ${typeArg}. Available: browse, code, task`);
|
|
3539
|
+
return true;
|
|
3540
|
+
}
|
|
3541
|
+
const result = ctx.sessionManager.createSession(ctx.userId, ctx.platform, type);
|
|
3542
|
+
if (typeof result === "string") {
|
|
3543
|
+
await ctx.reply(`\u274C ${result}`);
|
|
3544
|
+
return true;
|
|
3545
|
+
}
|
|
3546
|
+
await ctx.sessionManager.initAgentSession(result);
|
|
3547
|
+
await ctx.reply(`\u2705 Session #${result.shortIndex} (${typeLabel(result.type)}) created.`);
|
|
3548
|
+
logInfo(TAG12, `New session ${result.id} for ${ctx.userId}`);
|
|
3549
|
+
return true;
|
|
3550
|
+
}
|
|
3551
|
+
if (args.startsWith("end")) {
|
|
3552
|
+
const indexStr = args.slice(3).trim();
|
|
3553
|
+
const index2 = indexStr ? parseInt(indexStr, 10) : void 0;
|
|
3554
|
+
if (indexStr && isNaN(index2)) {
|
|
3555
|
+
await ctx.reply("Usage: /session end [#]");
|
|
3556
|
+
return true;
|
|
3557
|
+
}
|
|
3558
|
+
const result = ctx.sessionManager.endSession(ctx.userId, ctx.platform, index2);
|
|
3559
|
+
if (typeof result === "string") {
|
|
3560
|
+
await ctx.reply(`\u274C ${result}`);
|
|
3561
|
+
return true;
|
|
3562
|
+
}
|
|
3563
|
+
await ctx.reply(`\u2705 Session #${result.shortIndex} (${typeLabel(result.type)}) ended.`);
|
|
3564
|
+
logInfo(TAG12, `Ended session ${result.id}`);
|
|
3565
|
+
return true;
|
|
3566
|
+
}
|
|
3567
|
+
if (args.startsWith("kill")) {
|
|
3568
|
+
const indexStr = args.slice(4).trim();
|
|
3569
|
+
const index2 = parseInt(indexStr, 10);
|
|
3570
|
+
if (isNaN(index2)) {
|
|
3571
|
+
await ctx.reply("Usage: /session kill <#>");
|
|
3572
|
+
return true;
|
|
3573
|
+
}
|
|
3574
|
+
const result = ctx.sessionManager.killSession(ctx.userId, ctx.platform, index2);
|
|
3575
|
+
if (typeof result === "string") {
|
|
3576
|
+
await ctx.reply(`\u274C ${result}`);
|
|
3577
|
+
return true;
|
|
3578
|
+
}
|
|
3579
|
+
if (ctx.memory) {
|
|
3580
|
+
try {
|
|
3581
|
+
const db = ctx.memory.db;
|
|
3582
|
+
if (db) db.prepare("DELETE FROM messages WHERE session_id = ?").run(result.id);
|
|
3583
|
+
} catch (err) {
|
|
3584
|
+
logAndSwallow(TAG12, "delete session messages", err);
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
await ctx.reply(`\u{1F5D1}\uFE0F Session #${result.shortIndex} (${typeLabel(result.type)}) killed.`);
|
|
3588
|
+
logInfo(TAG12, `Killed session ${result.id}`);
|
|
3589
|
+
return true;
|
|
3590
|
+
}
|
|
3591
|
+
if (args.startsWith("pause")) {
|
|
3592
|
+
const indexStr = args.slice(5).trim();
|
|
3593
|
+
const index2 = indexStr ? parseInt(indexStr, 10) : void 0;
|
|
3594
|
+
if (indexStr && isNaN(index2)) {
|
|
3595
|
+
await ctx.reply("Usage: /session pause [#]");
|
|
3596
|
+
return true;
|
|
3597
|
+
}
|
|
3598
|
+
const result = ctx.sessionManager.pauseSession(ctx.userId, ctx.platform, index2);
|
|
3599
|
+
if (typeof result === "string") {
|
|
3600
|
+
await ctx.reply(`\u274C ${result}`);
|
|
3601
|
+
return true;
|
|
3602
|
+
}
|
|
3603
|
+
await ctx.reply(`\u23F8 Session #${result.shortIndex} (${typeLabel(result.type)}) paused.`);
|
|
3604
|
+
logInfo(TAG12, `Paused session ${result.id}`);
|
|
3605
|
+
return true;
|
|
3606
|
+
}
|
|
3607
|
+
if (args.startsWith("resume")) {
|
|
3608
|
+
const indexStr = args.slice(6).trim();
|
|
3609
|
+
const index2 = indexStr ? parseInt(indexStr, 10) : void 0;
|
|
3610
|
+
if (indexStr && isNaN(index2)) {
|
|
3611
|
+
await ctx.reply("Usage: /session resume [#]");
|
|
3612
|
+
return true;
|
|
3613
|
+
}
|
|
3614
|
+
const result = ctx.sessionManager.resumeSession(ctx.userId, ctx.platform, index2);
|
|
3615
|
+
if (typeof result === "string") {
|
|
3616
|
+
await ctx.reply(`\u274C ${result}`);
|
|
3617
|
+
return true;
|
|
3618
|
+
}
|
|
3619
|
+
await ctx.reply(`\u25B6\uFE0F Session #${result.shortIndex} (${typeLabel(result.type)}) resumed.`);
|
|
3620
|
+
logInfo(TAG12, `Resumed session ${result.id}`);
|
|
3621
|
+
return true;
|
|
3622
|
+
}
|
|
3623
|
+
const index = parseInt(args, 10);
|
|
3624
|
+
if (!isNaN(index)) {
|
|
3625
|
+
const result = ctx.sessionManager.switchSession(ctx.userId, ctx.platform, index);
|
|
3626
|
+
if (typeof result === "string") {
|
|
3627
|
+
await ctx.reply(`\u274C ${result}`);
|
|
3628
|
+
return true;
|
|
3629
|
+
}
|
|
3630
|
+
await ctx.reply(`\u{1F500} Switched to session #${result.shortIndex} (${typeLabel(result.type)}).`);
|
|
3631
|
+
logInfo(TAG12, `Switched to session ${result.id}`);
|
|
3632
|
+
return true;
|
|
3633
|
+
}
|
|
3634
|
+
await ctx.reply("Usage: /session [new [browse|code|task] | end [#] | kill <#> | pause [#] | resume [#] | <#>]");
|
|
3635
|
+
return true;
|
|
3636
|
+
}
|
|
3637
|
+
|
|
3638
|
+
// src/components/commands/index.ts
|
|
3639
|
+
registerExact("/reset", handleNewReset);
|
|
3640
|
+
registerExact("/compact", handleCompact);
|
|
3641
|
+
registerExact("/status", handleStatus);
|
|
3642
|
+
registerExact("/doctor", handleDoctor);
|
|
3643
|
+
registerExact("/stop", handleStop);
|
|
3644
|
+
registerExact("/ctrlc", handleStop);
|
|
3645
|
+
registerExact("/wait", handleWait);
|
|
3646
|
+
registerExact("/steer", handleWait);
|
|
3647
|
+
registerExact("/restart", handleRestart);
|
|
3648
|
+
registerExact("/full", handleFull);
|
|
3649
|
+
registerExact("/short", handleShort);
|
|
3650
|
+
registerExact("/healing", handleHealing);
|
|
3651
|
+
registerExact("/facts", handleFacts);
|
|
3652
|
+
registerExact("/tasks", handleTasksList);
|
|
3653
|
+
registerExact("/task", handleTasksList);
|
|
3654
|
+
registerExact("/cron", handleTasksList);
|
|
3655
|
+
registerExact("/memory", handleMemory);
|
|
3656
|
+
registerExact("/heartbeat", handleHeartbeat);
|
|
3657
|
+
registerExact("/models", handleModels);
|
|
3658
|
+
registerExact("/model", handleModels);
|
|
3659
|
+
registerExact("/change", (_, ctx) => handleModels("/model change", ctx));
|
|
3660
|
+
registerExact("/emergency", handleEmergencyAlias);
|
|
3661
|
+
registerExact("/help", handleHelp);
|
|
3662
|
+
registerExact("/users", handleUsers);
|
|
3663
|
+
registerExact("/skills", handleSkills);
|
|
3664
|
+
registerExact("/skill", handleSkills);
|
|
3665
|
+
registerExact("/wakeup", handleWakeup);
|
|
3666
|
+
registerExact("/sleep", handleSleep);
|
|
3667
|
+
registerExact("/mcp", handleMcp);
|
|
3668
|
+
registerExact("/hooks", handleHooks);
|
|
3669
|
+
registerExact("/usage", handleUsage);
|
|
3670
|
+
registerExact("/openrouter", handleOpenRouter);
|
|
3671
|
+
registerExact("/whoami", handleWhoami);
|
|
3672
|
+
registerExact("/session", handleSession);
|
|
3673
|
+
registerPrefix("/session ", handleSession);
|
|
3674
|
+
registerPrefix("/tasks run ", handleTasksTrigger);
|
|
3675
|
+
registerPrefix("/task run ", handleTasksTrigger);
|
|
3676
|
+
registerPrefix("/cron run ", handleTasksTrigger);
|
|
3677
|
+
registerPrefix("/tasks log ", handleTasksLog);
|
|
3678
|
+
registerPrefix("/task log ", handleTasksLog);
|
|
3679
|
+
registerPrefix("/cron log ", handleTasksLog);
|
|
3680
|
+
registerPrefix("/nlm", handleNlm);
|
|
3681
|
+
registerPrefix("/sleep ", handleSleepSub);
|
|
3682
|
+
registerPrefix("/usage ", handleUsage);
|
|
3683
|
+
registerPrefix("/task pause ", handleTaskPause);
|
|
3684
|
+
registerPrefix("/task resume ", handleTaskPause);
|
|
3685
|
+
registerPrefix("/tasks pause ", handleTaskPause);
|
|
3686
|
+
registerPrefix("/tasks resume ", handleTaskPause);
|
|
3687
|
+
|
|
3688
|
+
export {
|
|
3689
|
+
SessionRegistry,
|
|
3690
|
+
SessionManager,
|
|
3691
|
+
loadNLMConfig,
|
|
3692
|
+
sanitizeOutbound,
|
|
3693
|
+
routeReaction,
|
|
3694
|
+
formatReactionSignal,
|
|
3695
|
+
cleanResponse,
|
|
3696
|
+
registerCommand,
|
|
3697
|
+
handleCommand,
|
|
3698
|
+
triggerNewSession,
|
|
3699
|
+
triggerResetSession,
|
|
3700
|
+
killWakeInhibit,
|
|
3701
|
+
resetIdleCompactFlag,
|
|
3702
|
+
setIdleCompactReset,
|
|
3703
|
+
resetAndPrepare,
|
|
3704
|
+
handleInboundMessage,
|
|
3705
|
+
startSession
|
|
3706
|
+
};
|
|
3707
|
+
//# sourceMappingURL=chunk-TXRWQIQQ.js.map
|