theokit 0.43.7 → 0.44.3
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/dist/{actions-virtual-module-LD3YKWIX.js → actions-virtual-module-67K4POQ2.js} +20 -20
- package/dist/actions-virtual-module-67K4POQ2.js.map +1 -0
- package/dist/{actions-virtual-module-N7GKUS3D.js → actions-virtual-module-IK2SDIZ3.js} +9 -8
- package/dist/actions-virtual-module-IK2SDIZ3.js.map +1 -0
- package/dist/adapters/web-shim.js +1 -1
- package/dist/adapters/web-shim.js.map +1 -1
- package/dist/adapters/ws-shim.js +1 -1
- package/dist/adapters/ws-shim.js.map +1 -1
- package/dist/{add-W6FNTAFS.js → add-3VLFUPF4.js} +1 -1
- package/dist/add-3VLFUPF4.js.map +1 -0
- package/dist/{agent-U35G3WNE.js → agent-OCCL5RUY.js} +5 -5
- package/dist/{agents-typed-client-CHMVW7MK.js → agents-typed-client-TNHCUU7Q.js} +2 -2
- package/dist/agents-typed-client-TNHCUU7Q.js.map +1 -0
- package/dist/{agents-typed-client-NJ73Z6XY.js → agents-typed-client-X56CQ6FX.js} +1 -1
- package/dist/agents-typed-client-X56CQ6FX.js.map +1 -0
- package/dist/{app-typed-client-6YZRDAPY.js → app-typed-client-4OH3JREB.js} +51 -12
- package/dist/app-typed-client-4OH3JREB.js.map +1 -0
- package/dist/{app-typed-client-V77SVU2O.js → app-typed-client-N5WUE2CP.js} +62 -24
- package/dist/app-typed-client-N5WUE2CP.js.map +1 -0
- package/dist/{aws-lambda-7GSCNLPY.js → aws-lambda-473QCOL6.js} +3 -3
- package/dist/{body-parser-web-MUWBKZ3F.js → body-parser-web-4Z2QTPDU.js} +2 -2
- package/dist/body-parser-web-4Z2QTPDU.js.map +1 -0
- package/dist/{body-parser-web-FV5HWCY3.js → body-parser-web-TIOTXPDW.js} +1 -1
- package/dist/body-parser-web-TIOTXPDW.js.map +1 -0
- package/dist/boot/index.js +4 -4
- package/dist/boot/index.js.map +1 -1
- package/dist/{broadcast-4SX7R5XE.js → broadcast-H64DJRBE.js} +3 -3
- package/dist/broadcast-H64DJRBE.js.map +1 -0
- package/dist/{build-BYLQZJT2.js → build-25RSFP22.js} +20 -11
- package/dist/build-25RSFP22.js.map +1 -0
- package/dist/{build-request-body-preview-EOP2Y4F3.js → build-request-body-preview-CKWWBF2M.js} +2 -2
- package/dist/build-request-body-preview-CKWWBF2M.js.map +1 -0
- package/dist/{build-request-body-preview-QMWD2IXK.js → build-request-body-preview-XPZ2GYMG.js} +1 -1
- package/dist/build-request-body-preview-XPZ2GYMG.js.map +1 -0
- package/dist/{bun-KP2KES6S.js → bun-OQUCL3XW.js} +3 -3
- package/dist/{check-PD2FTKMM.js → check-45NKUVSQ.js} +1 -1
- package/dist/check-45NKUVSQ.js.map +1 -0
- package/dist/{chunk-FRUEDJFX.js → chunk-226KMIGZ.js} +3 -3
- package/dist/{chunk-FRUEDJFX.js.map → chunk-226KMIGZ.js.map} +1 -1
- package/dist/{chunk-U3NTEK46.js → chunk-2ADKT37V.js} +5 -5
- package/dist/chunk-2ADKT37V.js.map +1 -0
- package/dist/{chunk-QWBXUZAX.js → chunk-37ZO643S.js} +9 -9
- package/dist/chunk-37ZO643S.js.map +1 -0
- package/dist/{chunk-ESC54TAK.js → chunk-3DGIANAN.js} +1 -1
- package/dist/chunk-3DGIANAN.js.map +1 -0
- package/dist/{chunk-GY5Q27BJ.js → chunk-47FFT43U.js} +1 -1
- package/dist/chunk-47FFT43U.js.map +1 -0
- package/dist/{chunk-JAIKGP3Q.js → chunk-47VNWTL4.js} +1 -1
- package/dist/chunk-47VNWTL4.js.map +1 -0
- package/dist/{chunk-ZJQ4O76G.js → chunk-4FWBKX6G.js} +1 -1
- package/dist/chunk-4FWBKX6G.js.map +1 -0
- package/dist/{chunk-NM4WF3XD.js → chunk-4PBJ266F.js} +1 -1
- package/dist/chunk-4PBJ266F.js.map +1 -0
- package/dist/{chunk-7MQOHNHE.js → chunk-52PDWL5S.js} +1 -1
- package/dist/chunk-52PDWL5S.js.map +1 -0
- package/dist/{chunk-JQJN6LFK.js → chunk-62EU2BVZ.js} +2 -2
- package/dist/chunk-62EU2BVZ.js.map +1 -0
- package/dist/{chunk-SJIRP73B.js → chunk-646CA6RV.js} +2 -2
- package/dist/chunk-646CA6RV.js.map +1 -0
- package/dist/{chunk-ASDXVRJD.js → chunk-6KDXP472.js} +4 -4
- package/dist/chunk-6KDXP472.js.map +1 -0
- package/dist/chunk-6LNIGF5P.js +46 -0
- package/dist/chunk-6LNIGF5P.js.map +1 -0
- package/dist/chunk-7IHWXDJJ.js +1 -0
- package/dist/chunk-7IHWXDJJ.js.map +1 -0
- package/dist/{chunk-JQSKBMXP.js → chunk-7LVM22VG.js} +1 -1
- package/dist/chunk-7LVM22VG.js.map +1 -0
- package/dist/{chunk-UVXB2ER7.js → chunk-7SABX4DJ.js} +1 -1
- package/dist/chunk-7SABX4DJ.js.map +1 -0
- package/dist/{chunk-P37RZRFV.js → chunk-A7JBF5FO.js} +1 -1
- package/dist/chunk-A7JBF5FO.js.map +1 -0
- package/dist/{chunk-X32XEJXR.js → chunk-ACKBVBUI.js} +1 -1
- package/dist/chunk-ACKBVBUI.js.map +1 -0
- package/dist/{chunk-UUFYP2UM.js → chunk-AM3QABW5.js} +3 -3
- package/dist/chunk-AM3QABW5.js.map +1 -0
- package/dist/{chunk-FH3DYBAN.js → chunk-ANS4ELRH.js} +1 -1
- package/dist/chunk-ANS4ELRH.js.map +1 -0
- package/dist/{chunk-UD3LFGDL.js → chunk-AVQU5CQF.js} +1 -1
- package/dist/chunk-AVQU5CQF.js.map +1 -0
- package/dist/{chunk-AFVTEAES.js → chunk-B7ONQ4QJ.js} +2 -2
- package/dist/{chunk-WSJKACWB.js → chunk-C34PMLSN.js} +1 -1
- package/dist/chunk-C34PMLSN.js.map +1 -0
- package/dist/{chunk-FEDSPOY6.js → chunk-DSOBIXXR.js} +6 -6
- package/dist/chunk-DSOBIXXR.js.map +1 -0
- package/dist/{chunk-AXVFVMLM.js → chunk-EIRQM55C.js} +12 -7
- package/dist/chunk-EIRQM55C.js.map +1 -0
- package/dist/{chunk-ZEGYW52B.js → chunk-FB2HBNIC.js} +1 -1
- package/dist/chunk-FB2HBNIC.js.map +1 -0
- package/dist/chunk-FXZZS7YW.js +605 -0
- package/dist/chunk-FXZZS7YW.js.map +1 -0
- package/dist/{chunk-5AVPWHUE.js → chunk-FZG3JYB3.js} +14 -50
- package/dist/chunk-FZG3JYB3.js.map +1 -0
- package/dist/{chunk-V3LJN2GS.js → chunk-GAPBPRPP.js} +1 -1
- package/dist/chunk-GAPBPRPP.js.map +1 -0
- package/dist/chunk-GUBN5UTT.js +1 -0
- package/dist/chunk-GUBN5UTT.js.map +1 -0
- package/dist/{chunk-GSO77FSM.js → chunk-HEK4SET7.js} +45 -47
- package/dist/chunk-HEK4SET7.js.map +1 -0
- package/dist/{chunk-SPJNVWOK.js → chunk-HRCNHWLP.js} +7 -110
- package/dist/chunk-HRCNHWLP.js.map +1 -0
- package/dist/{chunk-C3ZZ56YZ.js → chunk-JA2ZAT5Q.js} +1 -1
- package/dist/chunk-JA2ZAT5Q.js.map +1 -0
- package/dist/{chunk-KI7OWQUX.js → chunk-KBGGQK4I.js} +87 -27
- package/dist/chunk-KBGGQK4I.js.map +1 -0
- package/dist/{chunk-WFNLNIJX.js → chunk-KD5G3YXI.js} +4 -4
- package/dist/chunk-KD5G3YXI.js.map +1 -0
- package/dist/{chunk-VMEWD57H.js → chunk-KPKLIUUL.js} +1 -1
- package/dist/chunk-KPKLIUUL.js.map +1 -0
- package/dist/{chunk-IHJZPQEF.js → chunk-L4Y6RJ4E.js} +4 -4
- package/dist/chunk-L4Y6RJ4E.js.map +1 -0
- package/dist/{chunk-GUUWDMR2.js → chunk-L5Z6IY5U.js} +5 -5
- package/dist/chunk-L5Z6IY5U.js.map +1 -0
- package/dist/{chunk-L4GCEMNW.js → chunk-LNVMHQ2Z.js} +2 -2
- package/dist/{chunk-JBCHWRKF.js → chunk-MTEWLGVY.js} +2 -2
- package/dist/chunk-MTEWLGVY.js.map +1 -0
- package/dist/{chunk-5PU65T5W.js → chunk-MWP4YXWJ.js} +1 -1
- package/dist/chunk-MWP4YXWJ.js.map +1 -0
- package/dist/{chunk-IPC5UREF.js → chunk-NGEBYCOZ.js} +70 -46
- package/dist/chunk-NGEBYCOZ.js.map +1 -0
- package/dist/{chunk-5QW7IQQU.js → chunk-NTDN54XO.js} +1 -1
- package/dist/chunk-NTDN54XO.js.map +1 -0
- package/dist/{chunk-7BIM27LV.js → chunk-OIGJOYX6.js} +1 -1
- package/dist/chunk-OIGJOYX6.js.map +1 -0
- package/dist/{chunk-TSLSIRBR.js → chunk-OLGPYOLO.js} +2 -2
- package/dist/chunk-OLGPYOLO.js.map +1 -0
- package/dist/{chunk-NQRNDPZJ.js → chunk-OXCALO3W.js} +3 -3
- package/dist/{chunk-UNLG36Z5.js → chunk-Q5OT7LZG.js} +19 -499
- package/dist/chunk-Q5OT7LZG.js.map +1 -0
- package/dist/{chunk-45C3WUQ7.js → chunk-QPVXGC7O.js} +1 -1
- package/dist/chunk-QPVXGC7O.js.map +1 -0
- package/dist/{chunk-6NEXVBPY.js → chunk-RWNSW4FM.js} +1 -1
- package/dist/chunk-RWNSW4FM.js.map +1 -0
- package/dist/{chunk-E25FBYJP.js → chunk-SAFGDIPQ.js} +1 -1
- package/dist/chunk-SAFGDIPQ.js.map +1 -0
- package/dist/{chunk-RMU7EBRO.js → chunk-SEELKQUM.js} +2 -2
- package/dist/chunk-SEELKQUM.js.map +1 -0
- package/dist/{chunk-FOZIR3TG.js → chunk-SMP2T335.js} +2 -2
- package/dist/{chunk-D7ZH7DTG.js → chunk-SSYDEKWF.js} +1 -1
- package/dist/chunk-SSYDEKWF.js.map +1 -0
- package/dist/{chunk-2ZX4J4SX.js → chunk-SZU6BOMV.js} +114 -90
- package/dist/chunk-SZU6BOMV.js.map +1 -0
- package/dist/{chunk-M6NMS5LR.js → chunk-TDNNBLGW.js} +1 -1
- package/dist/chunk-TDNNBLGW.js.map +1 -0
- package/dist/{chunk-X2VVCJ4V.js → chunk-U267AOWS.js} +1 -1
- package/dist/chunk-U267AOWS.js.map +1 -0
- package/dist/{chunk-GW5H4KB4.js → chunk-U3H53IIN.js} +6 -6
- package/dist/chunk-U3H53IIN.js.map +1 -0
- package/dist/{chunk-OPLICDUB.js → chunk-UW2TEAGR.js} +1 -1
- package/dist/chunk-UW2TEAGR.js.map +1 -0
- package/dist/{chunk-IAJ2JVEH.js → chunk-VSLTKX5L.js} +1 -1
- package/dist/chunk-VSLTKX5L.js.map +1 -0
- package/dist/{chunk-YB5DJJ4U.js → chunk-VZTYCAO6.js} +1 -1
- package/dist/chunk-VZTYCAO6.js.map +1 -0
- package/dist/chunk-WDGYCBNI.js +65 -0
- package/dist/chunk-WDGYCBNI.js.map +1 -0
- package/dist/chunk-WPJOTQA5.js +1 -0
- package/dist/chunk-WPJOTQA5.js.map +1 -0
- package/dist/{chunk-DGUM43GV.js → chunk-XTHN6JBX.js} +1 -1
- package/dist/chunk-XTHN6JBX.js.map +1 -0
- package/dist/{chunk-HZ6USUHC.js → chunk-YOIVCCZT.js} +1 -1
- package/dist/chunk-YOIVCCZT.js.map +1 -0
- package/dist/{chunk-UO7WS32S.js → chunk-ZC2UESE4.js} +7 -5
- package/dist/chunk-ZC2UESE4.js.map +1 -0
- package/dist/cli/index.js +15 -15
- package/dist/client/core.d.ts +4 -258
- package/dist/client/core.js +13 -9
- package/dist/client/core.js.map +1 -1
- package/dist/client/index.d.ts +8 -97
- package/dist/client/index.js +39 -92
- package/dist/client/index.js.map +1 -1
- package/dist/{cloudflare-C6E5SPAE.js → cloudflare-4YT5CK3P.js} +3 -3
- package/dist/{controller-swc-transform-XV3ETSE3.js → controller-swc-transform-LUJ4VAZJ.js} +1 -1
- package/dist/controller-swc-transform-LUJ4VAZJ.js.map +1 -0
- package/dist/{controller-swc-transform-URELVCBM.js → controller-swc-transform-T7ZTD55V.js} +2 -2
- package/dist/controller-swc-transform-T7ZTD55V.js.map +1 -0
- package/dist/{cookies-MJKMGJ7N.js → cookies-SOXUQJXG.js} +3 -3
- package/dist/cookies-SOXUQJXG.js.map +1 -0
- package/dist/{db-3UNAMSFK.js → db-IVVCQE5Z.js} +1 -2
- package/dist/db-IVVCQE5Z.js.map +1 -0
- package/dist/{deno-deploy-RFZN56X4.js → deno-deploy-YYN6BNLF.js} +3 -3
- package/dist/{dev-PS2NGT6I.js → dev-V6UBM2OL.js} +14 -13
- package/dist/{dev-PS2NGT6I.js.map → dev-V6UBM2OL.js.map} +1 -1
- package/dist/{dev-emit-2DL3MCLM.js → dev-emit-F54Y5TIU.js} +5 -5
- package/dist/dev-emit-F54Y5TIU.js.map +1 -0
- package/dist/{dev-emit-XVTJXZGA.js → dev-emit-J7V6GWSK.js} +6 -6
- package/dist/devtools/entry.js +61 -25
- package/dist/devtools/entry.js.map +1 -1
- package/dist/{dispatcher-EJHL6JMJ.js → dispatcher-KMZBGMM5.js} +1 -1
- package/dist/dispatcher-KMZBGMM5.js.map +1 -0
- package/dist/dispatcher-MXKF3CRH.js +8 -0
- package/dist/dispatcher-MXKF3CRH.js.map +1 -0
- package/dist/{generate-5X4LG77M.js → generate-GXWPUVS7.js} +4 -5
- package/dist/generate-GXWPUVS7.js.map +1 -0
- package/dist/index.js +25 -25
- package/dist/index.js.map +1 -1
- package/dist/{info-2OOXFTJX.js → info-ICDTPJDC.js} +4 -4
- package/dist/info-ICDTPJDC.js.map +1 -0
- package/dist/{internal-api-FBFV265J.js → internal-api-ALXVBL56.js} +14 -14
- package/dist/{internal-api-LFW7R65K.js → internal-api-S7WK2WCY.js} +20 -22
- package/dist/internal-api-S7WK2WCY.js.map +1 -0
- package/dist/{load-config-3EAQTXPH.js → load-config-ICCNMO74.js} +4 -4
- package/dist/{mcp-ML4QK5ON.js → mcp-4JFODA5U.js} +6 -6
- package/dist/mcp-4JFODA5U.js.map +1 -0
- package/dist/{netlify-PMLHVPN4.js → netlify-2WZUQYI3.js} +3 -3
- package/dist/node-CMAYKDCM.js +10 -0
- package/dist/{openapi-2HVGJMSN.js → openapi-NIYA2VBG.js} +9 -9
- package/dist/openapi-NIYA2VBG.js.map +1 -0
- package/dist/react-query/index.js +2 -2
- package/dist/react-query/index.js.map +1 -1
- package/dist/registry-72YH7ZJM.js +23 -0
- package/dist/registry-72YH7ZJM.js.map +1 -0
- package/dist/{router-TLEAOFID.js → router-6MAACS25.js} +1 -2
- package/dist/router-6MAACS25.js.map +1 -0
- package/dist/{routes-XNS3UM3B.js → routes-2RD4S5PZ.js} +6 -6
- package/dist/schema-S553QCAU.js +22 -0
- package/dist/server/agent/index.d.ts +3 -72
- package/dist/server/agent/index.js +4 -4
- package/dist/server/agent/index.js.map +1 -1
- package/dist/server/auth/index.js +4 -4
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/cost/index.js +2 -2
- package/dist/server/cost/index.js.map +1 -1
- package/dist/server/cron/index.js +6 -6
- package/dist/server/cron/index.js.map +1 -1
- package/dist/server/define/index.js +4 -4
- package/dist/server/define/index.js.map +1 -1
- package/dist/server/http/index.js +11 -11
- package/dist/server/http/index.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +41 -41
- package/dist/server/index.js.map +1 -1
- package/dist/server/jobs/index.js +7 -7
- package/dist/server/jobs/index.js.map +1 -1
- package/dist/server/observability/index.d.ts +9 -39
- package/dist/server/observability/index.js +5 -5
- package/dist/server/observability/index.js.map +1 -1
- package/dist/server/plugins/index.js +3 -3
- package/dist/server/plugins/index.js.map +1 -1
- package/dist/server/rate-limit/index.js +4 -4
- package/dist/server/rate-limit/index.js.map +1 -1
- package/dist/server/realtime/index.js +2 -2
- package/dist/server/realtime/index.js.map +1 -1
- package/dist/server/scan/index.js +7 -7
- package/dist/server/scan/index.js.map +1 -1
- package/dist/server/security/index.js +5 -5
- package/dist/server/security/index.js.map +1 -1
- package/dist/server/storage/index.js +2 -2
- package/dist/server/storage/index.js.map +1 -1
- package/dist/server/webhook/index.js +3 -3
- package/dist/server/webhook/index.js.map +1 -1
- package/dist/server-error-to-envelope-UM7DEVM4.js +8 -0
- package/dist/server-error-to-envelope-UM7DEVM4.js.map +1 -0
- package/dist/{server-routes-hmr-VZXOP5YT.js → server-routes-hmr-4W5L2MKN.js} +1 -1
- package/dist/server-routes-hmr-4W5L2MKN.js.map +1 -0
- package/dist/{server-routes-hmr-PBHSKCQJ.js → server-routes-hmr-6CRNSEWF.js} +2 -2
- package/dist/server-routes-hmr-6CRNSEWF.js.map +1 -0
- package/dist/{services-json-Y3XODNB5.js → services-json-7ZXJ4CIS.js} +1 -1
- package/dist/services-json-7ZXJ4CIS.js.map +1 -0
- package/dist/{services-typed-client-32KMTTXR.js → services-typed-client-EO4RFNTZ.js} +2 -2
- package/dist/services-typed-client-EO4RFNTZ.js.map +1 -0
- package/dist/{services-typed-client-IETY2SAK.js → services-typed-client-IXNOPVZA.js} +3 -3
- package/dist/services-typed-client-IXNOPVZA.js.map +1 -0
- package/dist/{start-YLR2SFHU.js → start-2LNPPG74.js} +40 -16
- package/dist/start-2LNPPG74.js.map +1 -0
- package/dist/{static-MB2CD5Y3.js → static-25H5K42R.js} +3 -3
- package/dist/{static-MB2CD5Y3.js.map → static-25H5K42R.js.map} +1 -1
- package/dist/{theo-cloud-DQRP6S6M.js → theo-cloud-JL2KXHIS.js} +2 -2
- package/dist/{upgrade-readiness-ACGW44JC.js → upgrade-readiness-77P6VNB4.js} +1 -1
- package/dist/upgrade-readiness-77P6VNB4.js.map +1 -0
- package/dist/{vercel-J6G7ZHYQ.js → vercel-FP3C6LN6.js} +3 -3
- package/dist/vite-plugin/index.js +25 -25
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/{vite-plugin-6VVU6PPC.js → vite-plugin-X6YO3OUH.js} +14 -13
- package/package.json +9 -8
- package/dist/actions-virtual-module-LD3YKWIX.js.map +0 -1
- package/dist/actions-virtual-module-N7GKUS3D.js.map +0 -1
- package/dist/add-W6FNTAFS.js.map +0 -1
- package/dist/agents-typed-client-CHMVW7MK.js.map +0 -1
- package/dist/agents-typed-client-NJ73Z6XY.js.map +0 -1
- package/dist/app-typed-client-6YZRDAPY.js.map +0 -1
- package/dist/app-typed-client-V77SVU2O.js.map +0 -1
- package/dist/body-parser-web-FV5HWCY3.js.map +0 -1
- package/dist/body-parser-web-MUWBKZ3F.js.map +0 -1
- package/dist/build-BYLQZJT2.js.map +0 -1
- package/dist/build-request-body-preview-EOP2Y4F3.js.map +0 -1
- package/dist/build-request-body-preview-QMWD2IXK.js.map +0 -1
- package/dist/check-PD2FTKMM.js.map +0 -1
- package/dist/chunk-2ZX4J4SX.js.map +0 -1
- package/dist/chunk-45C3WUQ7.js.map +0 -1
- package/dist/chunk-5AVPWHUE.js.map +0 -1
- package/dist/chunk-5PU65T5W.js.map +0 -1
- package/dist/chunk-5QW7IQQU.js.map +0 -1
- package/dist/chunk-6NEXVBPY.js.map +0 -1
- package/dist/chunk-7BIM27LV.js.map +0 -1
- package/dist/chunk-7MQOHNHE.js.map +0 -1
- package/dist/chunk-ASDXVRJD.js.map +0 -1
- package/dist/chunk-AXVFVMLM.js.map +0 -1
- package/dist/chunk-C3ZZ56YZ.js.map +0 -1
- package/dist/chunk-D7ZH7DTG.js.map +0 -1
- package/dist/chunk-E25FBYJP.js.map +0 -1
- package/dist/chunk-E3JH6YUM.js +0 -1
- package/dist/chunk-ESC54TAK.js.map +0 -1
- package/dist/chunk-FEDSPOY6.js.map +0 -1
- package/dist/chunk-FH3DYBAN.js.map +0 -1
- package/dist/chunk-GSO77FSM.js.map +0 -1
- package/dist/chunk-GUUWDMR2.js.map +0 -1
- package/dist/chunk-GW5H4KB4.js.map +0 -1
- package/dist/chunk-GY5Q27BJ.js.map +0 -1
- package/dist/chunk-HZ6USUHC.js.map +0 -1
- package/dist/chunk-IAJ2JVEH.js.map +0 -1
- package/dist/chunk-IHJZPQEF.js.map +0 -1
- package/dist/chunk-IPC5UREF.js.map +0 -1
- package/dist/chunk-JAIKGP3Q.js.map +0 -1
- package/dist/chunk-JBCHWRKF.js.map +0 -1
- package/dist/chunk-JHEAWR3L.js +0 -1
- package/dist/chunk-JQJN6LFK.js.map +0 -1
- package/dist/chunk-JQSKBMXP.js.map +0 -1
- package/dist/chunk-KI7OWQUX.js.map +0 -1
- package/dist/chunk-KPRJS3II.js +0 -505
- package/dist/chunk-KPRJS3II.js.map +0 -1
- package/dist/chunk-M6NMS5LR.js.map +0 -1
- package/dist/chunk-NM4WF3XD.js.map +0 -1
- package/dist/chunk-NPMCKOX4.js +0 -1
- package/dist/chunk-OPLICDUB.js.map +0 -1
- package/dist/chunk-P37RZRFV.js.map +0 -1
- package/dist/chunk-QWBXUZAX.js.map +0 -1
- package/dist/chunk-RMU7EBRO.js.map +0 -1
- package/dist/chunk-SJIRP73B.js.map +0 -1
- package/dist/chunk-SPJNVWOK.js.map +0 -1
- package/dist/chunk-TSLSIRBR.js.map +0 -1
- package/dist/chunk-U3NTEK46.js.map +0 -1
- package/dist/chunk-UD3LFGDL.js.map +0 -1
- package/dist/chunk-UNLG36Z5.js.map +0 -1
- package/dist/chunk-UO7WS32S.js.map +0 -1
- package/dist/chunk-UUFYP2UM.js.map +0 -1
- package/dist/chunk-UVXB2ER7.js.map +0 -1
- package/dist/chunk-V3LJN2GS.js.map +0 -1
- package/dist/chunk-VMEWD57H.js.map +0 -1
- package/dist/chunk-WFNLNIJX.js.map +0 -1
- package/dist/chunk-WSJKACWB.js.map +0 -1
- package/dist/chunk-X2VVCJ4V.js.map +0 -1
- package/dist/chunk-X32XEJXR.js.map +0 -1
- package/dist/chunk-YB5DJJ4U.js.map +0 -1
- package/dist/chunk-ZEGYW52B.js.map +0 -1
- package/dist/chunk-ZJQ4O76G.js.map +0 -1
- package/dist/controller-swc-transform-URELVCBM.js.map +0 -1
- package/dist/controller-swc-transform-XV3ETSE3.js.map +0 -1
- package/dist/cookies-MJKMGJ7N.js.map +0 -1
- package/dist/db-3UNAMSFK.js.map +0 -1
- package/dist/dev-emit-2DL3MCLM.js.map +0 -1
- package/dist/dispatcher-63LBXYLE.js +0 -8
- package/dist/dispatcher-63LBXYLE.js.map +0 -1
- package/dist/dispatcher-EJHL6JMJ.js.map +0 -1
- package/dist/generate-5X4LG77M.js.map +0 -1
- package/dist/info-2OOXFTJX.js.map +0 -1
- package/dist/internal-api-FBFV265J.js.map +0 -1
- package/dist/internal-api-LFW7R65K.js.map +0 -1
- package/dist/load-config-3EAQTXPH.js.map +0 -1
- package/dist/mcp-ML4QK5ON.js.map +0 -1
- package/dist/node-BPJ3Z4DT.js +0 -10
- package/dist/node-BPJ3Z4DT.js.map +0 -1
- package/dist/openapi-2HVGJMSN.js.map +0 -1
- package/dist/registry-FKPH7XEL.js +0 -24
- package/dist/registry-FKPH7XEL.js.map +0 -1
- package/dist/router-TLEAOFID.js.map +0 -1
- package/dist/schema-LYOLNHZ7.js +0 -30
- package/dist/schema-LYOLNHZ7.js.map +0 -1
- package/dist/server-error-to-envelope-EOFUIUNB.js +0 -8
- package/dist/server-error-to-envelope-EOFUIUNB.js.map +0 -1
- package/dist/server-routes-hmr-PBHSKCQJ.js.map +0 -1
- package/dist/server-routes-hmr-VZXOP5YT.js.map +0 -1
- package/dist/services-json-Y3XODNB5.js.map +0 -1
- package/dist/services-typed-client-32KMTTXR.js.map +0 -1
- package/dist/services-typed-client-IETY2SAK.js.map +0 -1
- package/dist/start-YLR2SFHU.js.map +0 -1
- package/dist/upgrade-readiness-ACGW44JC.js.map +0 -1
- package/dist/vite-plugin-6VVU6PPC.js.map +0 -1
- /package/dist/{agent-U35G3WNE.js.map → agent-OCCL5RUY.js.map} +0 -0
- /package/dist/{aws-lambda-7GSCNLPY.js.map → aws-lambda-473QCOL6.js.map} +0 -0
- /package/dist/{bun-KP2KES6S.js.map → bun-OQUCL3XW.js.map} +0 -0
- /package/dist/{chunk-AFVTEAES.js.map → chunk-B7ONQ4QJ.js.map} +0 -0
- /package/dist/{chunk-L4GCEMNW.js.map → chunk-LNVMHQ2Z.js.map} +0 -0
- /package/dist/{chunk-NQRNDPZJ.js.map → chunk-OXCALO3W.js.map} +0 -0
- /package/dist/{chunk-FOZIR3TG.js.map → chunk-SMP2T335.js.map} +0 -0
- /package/dist/{cloudflare-C6E5SPAE.js.map → cloudflare-4YT5CK3P.js.map} +0 -0
- /package/dist/{deno-deploy-RFZN56X4.js.map → deno-deploy-YYN6BNLF.js.map} +0 -0
- /package/dist/{dev-emit-XVTJXZGA.js.map → dev-emit-J7V6GWSK.js.map} +0 -0
- /package/dist/{broadcast-4SX7R5XE.js.map → internal-api-ALXVBL56.js.map} +0 -0
- /package/dist/{chunk-DGUM43GV.js.map → load-config-ICCNMO74.js.map} +0 -0
- /package/dist/{netlify-PMLHVPN4.js.map → netlify-2WZUQYI3.js.map} +0 -0
- /package/dist/{chunk-E3JH6YUM.js.map → node-CMAYKDCM.js.map} +0 -0
- /package/dist/{routes-XNS3UM3B.js.map → routes-2RD4S5PZ.js.map} +0 -0
- /package/dist/{chunk-JHEAWR3L.js.map → schema-S553QCAU.js.map} +0 -0
- /package/dist/{theo-cloud-DQRP6S6M.js.map → theo-cloud-JL2KXHIS.js.map} +0 -0
- /package/dist/{vercel-J6G7ZHYQ.js.map → vercel-FP3C6LN6.js.map} +0 -0
- /package/dist/{chunk-NPMCKOX4.js.map → vite-plugin-X6YO3OUH.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/adapters-bridge/adapter-support.ts","../src/services/generators/caddy-generator.ts","../src/services/generators/compose-generator.ts","../src/services/generators/openapi-client-gen.ts","../src/services/adapters-bridge/manifest.ts","../src/services/schema.ts","../src/services/runtime/orchestrator.ts","../src/services/runtime/healthcheck-poller.ts","../src/services/runtime/log-merge.ts","../src/services/runtime/process-spawn-helpers.ts","../src/services/adapters-bridge/theo-cloud-adapter-stub.ts","../src/services/adapters-bridge/vite-proxy-builder.ts"],"sourcesContent":["/**\n * Adapter support gates (Wave 2 — TheoCloud-first focus).\n *\n * Wave 2 SUPPORTS polyglot `services: {}` on TWO targets ONLY:\n * - `node` (local docker-compose harness — TheoCloud-shaped)\n * - `theo-cloud` (the principal deploy target — Wave 3 adapter)\n *\n * All other in-tree deploy adapters (vercel, cloudflare, bun, deno-deploy,\n * aws-lambda, netlify, static) ARE STILL FIRST-CLASS DEPLOY TARGETS for\n * the TS app, but `services: {}` Wave 2 is NOT yet wired through them.\n * They reject loudly with an actionable message when the manifest has\n * non-empty services — pointing the user at `node` (local) or TheoCloud.\n *\n * Per owner decision 2026-05-27: TheoKit's polyglot services energy is\n * 100% TheoCloud; Vercel/Cloudflare/etc. polyglot-services wire-ups are\n * out of Wave 2 scope and deferred to fresh ADRs if demand emerges.\n */\nimport type { ServicesManifest } from './manifest.js'\n\nconst SUPPORTED_IN_WAVE_2 = ['node (local)', 'theo-cloud (Wave 3)'] as const\n\n/**\n * Reject if the adapter does not support polyglot services. Adapters\n * outside `node` + `theo-cloud` call this; the message points to the\n * supported alternatives.\n *\n * Throws when manifest has non-empty services. No-op when manifest is null\n * or empty (Wave 1 BC — the app still deploys, just without sidecars).\n */\nexport function assertServicesUnsupported(\n adapterName: string,\n manifest: ServicesManifest | null,\n): void {\n if (!manifest || manifest.services.length === 0) return\n const names = manifest.services.map((s) => s.name).join(', ')\n throw new Error(\n `Adapter '${adapterName}' does not support polyglot services in Wave 2.\\n` +\n `Detected services in theo.config.ts: ${names}.\\n\\n` +\n `Wave 2 supports: ${SUPPORTED_IN_WAVE_2.join(', ')}.\\n` +\n `TheoCloud is the strategic deploy target — the adapter ships in Wave 3.\\n` +\n `For local dev/prod-like validation today, use \\`theokit build --target node\\`\\n` +\n `(emits docker-compose + Caddyfile — TheoCloud-shaped harness).\\n\\n` +\n `See docs/concepts/services.md.`,\n )\n}\n","/**\n * Caddyfile generator (T3.3).\n *\n * Emits a Caddyfile that fronts the TheoKit web app + declared services\n * with W3C trace context propagation enabled (Caddy 2.11+ `tracing` directive).\n *\n * Generated output sits at `<dist>/.theokit/node/Caddyfile`, consumed by the\n * docker-compose stack (T3.3 compose-generator).\n *\n * EC-23: `reverse_proxy` directives are ordered by prefix length DESC.\n * Caddy matches longest-prefix-first when written in order.\n */\nimport type { ServicesManifest } from '../adapters-bridge/manifest.js'\n\nexport interface CaddyfileOptions {\n /** Port Caddy listens on (TheoKit web port; default 3000). */\n port: number\n /** TheoKit web container hostname (compose service name). Default 'web'. */\n webHost: string\n}\n\nfunction reverseProxyLines(servicePath: string, target: string, cors: boolean): string[] {\n const out = [`\\treverse_proxy ${servicePath}* ${target}`]\n if (cors) {\n out.push(\n `\\theader ${servicePath}* Access-Control-Allow-Origin \"*\"`,\n `\\theader ${servicePath}* Access-Control-Allow-Methods \"GET, POST, PUT, DELETE, OPTIONS\"`,\n `\\theader ${servicePath}* Access-Control-Allow-Headers \"Content-Type, Authorization, traceparent, tracestate, baggage\"`,\n )\n }\n return out\n}\n\nexport function generateCaddyfile(\n manifest: ServicesManifest | null,\n options: CaddyfileOptions,\n): string {\n // EC-23: sort services by proxy prefix length DESC so longest matches first\n const services = (manifest?.services ?? [])\n .slice()\n .sort((a, b) => b.proxy.length - a.proxy.length)\n\n const serviceLines = services.flatMap((svc) =>\n reverseProxyLines(svc.proxy, `${svc.name}:${String(svc.port)}`, svc.cors),\n )\n\n const lines: string[] = [\n `:${String(options.port)} {`,\n `\\t# W3C Trace Context propagation (Caddy 2.11+)`,\n `\\ttracing`,\n `\\theader -Server`,\n ...serviceLines,\n `\\treverse_proxy ${options.webHost}:${String(options.port)}`,\n `}`,\n '',\n ]\n return lines.join('\\n')\n}\n","/**\n * docker-compose.yml generator (T3.3).\n *\n * Emits the \"TheoCloud-shaped\" local harness — Caddy ingress in front of\n * web + declared services. Each service has a healthcheck and Caddy\n * `depends_on: service_healthy`.\n *\n * Used by the Node adapter: `theokit build --target node` writes the\n * compose stack to `.theokit/node/docker-compose.yml`.\n */\nimport type { ManifestServiceEntry, ServicesManifest } from '../adapters-bridge/manifest.js'\n\nexport interface ComposeOptions {\n /** Port for the web container (TheoKit). Default 3000. */\n webPort: number\n /** Caddy listening port (default same as webPort — Caddy fronts everything). */\n caddyPort?: number\n /** Project name (compose file `name:` field). */\n projectName?: string\n}\n\nfunction indent(level: number): string {\n return ' '.repeat(level)\n}\n\nfunction healthcheckLines(svc: ManifestServiceEntry): string[] {\n const checker =\n svc.runtime === 'python'\n ? `curl -f http://localhost:${String(svc.port)}${svc.healthcheck} || exit 1`\n : `wget --spider -q http://localhost:${String(svc.port)}${svc.healthcheck} || exit 1`\n return [\n `${indent(2)}healthcheck:`,\n `${indent(3)}test: [\"CMD-SHELL\", \"${checker}\"]`,\n `${indent(3)}interval: 10s`,\n `${indent(3)}timeout: 5s`,\n `${indent(3)}retries: 3`,\n ]\n}\n\nfunction envLines(env: Record<string, string>): string[] {\n const lines: string[] = [`${indent(2)}environment:`]\n for (const [k, v] of Object.entries(env)) {\n lines.push(`${indent(3)}${k}: ${JSON.stringify(v)}`)\n }\n return lines\n}\n\nfunction serviceBlockLines(svc: ManifestServiceEntry): string[] {\n // EC-8: auto-inject convention env vars in the container too\n const env: Record<string, string> = {\n THEOKIT_SERVICE_NAME: svc.name,\n THEOKIT_SERVICE_PORT: String(svc.port),\n ...(svc.env ?? {}),\n }\n return [\n `${indent(1)}${svc.name}:`,\n `${indent(2)}build:`,\n `${indent(3)}context: ./services/${svc.name}`,\n `${indent(2)}expose:`,\n `${indent(3)}- \"${String(svc.port)}\"`,\n ...envLines(env),\n ...healthcheckLines(svc),\n ]\n}\n\nfunction caddyBlockLines(caddyPort: number, services: ManifestServiceEntry[]): string[] {\n const depends: string[] = [\n `${indent(2)}depends_on:`,\n `${indent(3)}web:`,\n `${indent(4)}condition: service_healthy`,\n ]\n for (const svc of services) {\n depends.push(`${indent(3)}${svc.name}:`, `${indent(4)}condition: service_healthy`)\n }\n return [\n `${indent(1)}caddy:`,\n `${indent(2)}image: caddy:2.11`,\n `${indent(2)}ports:`,\n `${indent(3)}- \"${String(caddyPort)}:${String(caddyPort)}\"`,\n `${indent(2)}volumes:`,\n `${indent(3)}- ./Caddyfile:/etc/caddy/Caddyfile:ro`,\n ...depends,\n ]\n}\n\nfunction webBlockLines(webPort: number): string[] {\n return [\n `${indent(1)}web:`,\n `${indent(2)}build:`,\n `${indent(3)}context: .`,\n `${indent(3)}dockerfile: Dockerfile`,\n `${indent(2)}expose:`,\n `${indent(3)}- \"${String(webPort)}\"`,\n `${indent(2)}healthcheck:`,\n `${indent(3)}test: [\"CMD-SHELL\", \"wget --spider -q http://localhost:${String(webPort)}/api/health || exit 1\"]`,\n `${indent(3)}interval: 10s`,\n `${indent(3)}timeout: 5s`,\n `${indent(3)}retries: 3`,\n ]\n}\n\nexport function generateComposeYaml(\n manifest: ServicesManifest | null,\n options: ComposeOptions,\n): string {\n const services = manifest?.services ?? []\n const webPort = options.webPort\n const caddyPort = options.caddyPort ?? webPort\n\n const projectLines = options.projectName ? [`name: ${options.projectName}`] : []\n\n const lines: string[] = [\n ...projectLines,\n `services:`,\n ...caddyBlockLines(caddyPort, services),\n ...webBlockLines(webPort),\n ...services.flatMap(serviceBlockLines),\n '',\n ]\n return lines.join('\\n')\n}\n","/**\n * OpenAPI → TypeScript client generation (T5.1).\n *\n * Thin wrapper around `@hey-api/openapi-ts` (the de facto 2026 generator\n * used by Vercel/OpenCode/PayPal). Called by the Vite plugin\n * `services-typed-client.ts` at dev startup and when an OpenAPI URL\n * changes.\n *\n * Production behavior:\n * - For each service with a `openapi` URL, fetch the spec\n * - Run Hey API generator to write `<cwd>/clients/<service-name>.ts`\n * - On any failure: log warning, do NOT crash dev (best-effort)\n *\n * The actual `@hey-api/openapi-ts` invocation is dynamic-imported so\n * that consumers without the dep (TS-only Wave 1 apps) don't pay\n * the bundle cost.\n */\nimport type { ManifestServiceEntry } from '../adapters-bridge/manifest.js'\n\nexport interface GenerateClientOptions {\n service: ManifestServiceEntry\n /** Directory to write `<service-name>.ts` into. */\n outputDir: string\n /** Logger for warnings/info. */\n log?: (level: 'info' | 'warn' | 'error', msg: string) => void\n /** Test injection — replace fetch (so tests don't hit the network). */\n customFetch?: typeof fetch\n}\n\nexport interface GenerateClientResult {\n generated: boolean\n outputFile?: string\n skippedReason?: string\n}\n\n/**\n * Generate a typed client for one service. No-op (returns skipped=true)\n * if the service has no `openapi` URL.\n *\n * The generator runs the Hey API tool. If the tool import fails (not\n * installed), the function logs a warning and returns `{ generated: false }`.\n */\nexport async function generateTypedClient(\n options: GenerateClientOptions,\n): Promise<GenerateClientResult> {\n const log =\n options.log ??\n ((_level: 'info' | 'warn' | 'error', _msg: string) => {\n // default: silent in production; tests inject their own\n })\n const { service, outputDir } = options\n\n if (!service.openapi) {\n return { generated: false, skippedReason: 'no openapi URL declared' }\n }\n\n const f = options.customFetch ?? fetch\n let spec: unknown\n try {\n const res = await f(service.openapi)\n if (!res.ok) {\n log('warn', `[${service.name}] openapi fetch returned ${String(res.status)}; skipping`)\n return { generated: false, skippedReason: `fetch returned ${String(res.status)}` }\n }\n spec = await res.json()\n } catch (err) {\n log(\n 'warn',\n `[${service.name}] openapi fetch failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n return { generated: false, skippedReason: 'fetch failed' }\n }\n\n // Dynamic import — soft dep. We pass through a variable so the typechecker\n // doesn't resolve the literal at build time (the dep is optional;\n // consumers without it get a graceful skip).\n let createClient: unknown\n try {\n // Soft-dep resolution. We pass the module name through a String wrapper\n // call site that the typechecker cannot statically resolve — so missing\n // dep at typecheck time does not break the build. Runtime behavior is\n // identical to `import('@hey-api/openapi-ts')`.\n const segment1 = '@hey-api/'\n const segment2 = 'openapi-ts'\n const moduleName = `${segment1}${segment2}`\n const dyn: Promise<unknown> = import(moduleName).catch(() => null)\n const mod = (await dyn) as { createClient?: unknown } | null\n createClient = mod?.createClient\n } catch {\n createClient = undefined\n }\n\n if (typeof createClient !== 'function') {\n log(\n 'warn',\n `[${service.name}] @hey-api/openapi-ts not installed; typed client not generated. ` +\n `Run \\`pnpm add -D @hey-api/openapi-ts @hey-api/client-fetch\\` to enable.`,\n )\n return { generated: false, skippedReason: 'hey-api not installed' }\n }\n\n const outputFile = `${outputDir}/${service.name}.ts`\n try {\n await (createClient as (cfg: unknown) => Promise<unknown>)({\n input: spec,\n output: { path: outputDir, format: 'prettier' },\n plugins: [{ name: '@hey-api/client-fetch' }],\n })\n log('info', `[${service.name}] typed client generated at ${outputFile}`)\n return { generated: true, outputFile }\n } catch (err) {\n log(\n 'warn',\n `[${service.name}] Hey API generation failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n return { generated: false, skippedReason: 'generator threw' }\n }\n}\n","/**\n * Neutral cross-product manifest (T1.4).\n *\n * Emitted by `theokit build` at `<cwd>/.theokit/services.json` (schemaVersion 1).\n * Consumed by EVERY deploy adapter (Vercel, Node, Cloudflare, future TheoCloud).\n *\n * Shape is INTENTIONALLY platform-neutral (ADR-0015 invariant + D5). No\n * `vercel`/`cloudflare`/`theoCloud` keys — platform-specific tuning lives in\n * `theo.config.ts` and is read by each adapter's own code, NOT serialized\n * into this manifest.\n *\n * Topological ordering enforced (dependsOn-resolved deps first) so adapters\n * that need boot order (docker-compose, K8s) can consume the array verbatim.\n */\n/* eslint-disable security/detect-non-literal-fs-filename --\n * Manifest read/write paths are derived from the trusted `cwd` plus a fixed\n * subpath (`.theokit/services.json`). No user input flows into the path.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nimport type { ServiceDefinition, ServicesConfig } from '../schema.js'\n\n/**\n * Plan v1.2 T2.2 — service shape enum, mirrored from\n * `theo-cloud/api/internal/source/services.schema.json`. Optional in\n * services.json v2; TheoCloud Go-side defaults to `server` when absent.\n */\n// `frontend` removed in simplify-deploy-theokit-only v2.0 (D5) — TheoCloud\n// serves no static frontends; service shape is server | worker only.\nexport type ManifestServiceType = 'server' | 'worker'\n\nexport interface ManifestServiceEntry {\n name: string\n runtime: 'python' | 'node'\n /** Plan v1.2 T2.2 — service shape. Omit to keep default `server`. */\n type?: ManifestServiceType\n port: number\n proxy: string\n dev: string\n build?: string\n start: string\n openapi?: string\n healthcheck: string\n cors: boolean\n env?: Record<string, string>\n dependsOn?: string[]\n passSetCookie: boolean\n}\n\n/**\n * Plan v1.2 T2.1 — services.json now ships in 2 shapes:\n *\n * v1 (deprecated, sunset theokit 0.6.0): { version: 1, services: [...] }\n * v2 (current): { version: 2, project: <name>, services: [...] }\n *\n * `buildManifest()` selects which version to emit based on whether a\n * project name is supplied. Consumers (TheoCloud Go-side validator)\n * accept both via the JSON Schema 7 `oneOf` block.\n */\nexport interface ServicesManifestV1 {\n version: 1\n services: ManifestServiceEntry[]\n}\n\nexport interface ServicesManifestV2 {\n version: 2\n project: string\n services: ManifestServiceEntry[]\n}\n\nexport type ServicesManifest = ServicesManifestV1 | ServicesManifestV2\n\nconst MANIFEST_RELATIVE_PATH = ['.theokit', 'services.json'] as const\n\n/**\n * Topological sort using Kahn's algorithm.\n * Returns service names in dependency order (deps before dependants).\n * Schema validation guarantees no cycles, no self-deps, no missing references —\n * but we still defensively handle missing deps as no-op.\n */\nfunction topoSort(services: ServicesConfig): string[] {\n const names = Object.keys(services)\n const indeg: Record<string, number> = Object.fromEntries(names.map((n) => [n, 0]))\n const adj: Record<string, string[]> = Object.fromEntries(names.map((n) => [n, []]))\n\n const cmp = (a: string, b: string) => a.localeCompare(b)\n\n for (const name of names) {\n const deps = services[name].dependsOn ?? []\n for (const dep of deps) {\n if (!(dep in indeg)) continue\n adj[dep].push(name)\n indeg[name] = (indeg[name] ?? 0) + 1\n }\n }\n\n // Sort initial roots for determinism\n const queue: string[] = names.filter((n) => indeg[n] === 0).sort(cmp)\n const result: string[] = []\n while (queue.length > 0) {\n const cur = queue.shift()\n if (cur === undefined) break\n result.push(cur)\n const adjacent = (adj[cur] ?? []).slice().sort(cmp)\n for (const next of adjacent) {\n indeg[next] = (indeg[next] ?? 0) - 1\n if (indeg[next] === 0) {\n queue.push(next)\n }\n }\n }\n return result.length === names.length ? result : names // fallback (should not hit; schema rejects cycles)\n}\n\nfunction definitionToEntry(name: string, def: ServiceDefinition): ManifestServiceEntry {\n const entry: ManifestServiceEntry = {\n name,\n runtime: def.runtime,\n port: def.port,\n proxy: def.proxy,\n dev: def.dev,\n start: def.start,\n healthcheck: def.healthcheck,\n cors: def.cors,\n passSetCookie: def.passSetCookie,\n }\n // Plan v1.2 T2.2 — only emit `type` when explicitly set so v1 fixtures\n // (no type field at all) stay byte-identical.\n if (def.type !== undefined) entry.type = def.type\n if (def.build !== undefined) entry.build = def.build\n if (def.openapi !== undefined) entry.openapi = def.openapi\n if (def.env !== undefined) entry.env = def.env\n if (def.dependsOn !== undefined) entry.dependsOn = [...def.dependsOn]\n return entry\n}\n\n/**\n * Build a services manifest. Plan v1.2 T2.1: pass a `project` name to emit\n * v2; omit it to emit v1 (deprecated, accepted by TheoCloud until 0.6.0\n * sunset).\n */\nexport function buildManifest(services: ServicesConfig, project?: string): ServicesManifest {\n const ordered = topoSort(services)\n const entries = ordered.map((name) => definitionToEntry(name, services[name]))\n if (project !== undefined && project.length > 0) {\n return { version: 2, project, services: entries }\n }\n return { version: 1, services: entries }\n}\n\nfunction manifestPath(cwd: string): string {\n return join(cwd, ...MANIFEST_RELATIVE_PATH)\n}\n\n/**\n * Write the manifest to `<cwd>/.theokit/services.json`.\n *\n * EC-6 fix: creates `.theokit/` recursively if absent (fresh project on first\n * `pnpm build` would otherwise hit ENOENT).\n */\nexport function writeManifest(cwd: string, manifest: ServicesManifest): void {\n const file = manifestPath(cwd)\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, JSON.stringify(manifest, null, 2), 'utf-8')\n}\n\n/**\n * Read the manifest from `<cwd>/.theokit/services.json`.\n *\n * Returns null if the file does not exist (adapter's job to handle).\n * Throws with an actionable message if the file is malformed JSON.\n */\nexport function readManifest(cwd: string): ServicesManifest | null {\n const file = manifestPath(cwd)\n if (!existsSync(file)) return null\n const raw = readFileSync(file, 'utf-8')\n try {\n return JSON.parse(raw) as ServicesManifest\n } catch (err) {\n throw new Error(\n `failed to parse services.json at ${file}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n )\n }\n}\n","/**\n * Wave 2 — Polyglot Services Orchestration (T1.1).\n *\n * Declarative `services: {}` primitive for `theo.config.ts`. Each entry\n * declares an external sidecar process (Python FastAPI / Node Hono) that\n * boots alongside the TheoKit TS app. Empty `services: {}` is the default\n * and preserves Wave 1 behavior (no impact on TS-only apps).\n *\n * See ADRs 0012-0015 + plan: docs/plans/wave-2-polyglot-services-plan.md\n */\nimport { z } from 'zod'\n\n/** Wave 2 runtime kinds. Go/Rust/Java/Ruby/PHP archived; deferred per ADR-0012 invariant #2/#3. */\nconst ServiceRuntimeSchema = z.enum(['python', 'node'])\n\n/**\n * Service shape enum, mirrored from `services.schema.json`: server | worker.\n * Optional; emitters default to \"server\". `frontend` removed in\n * simplify-deploy-theokit-only v2.0 (D5) — TheoCloud serves no static frontends.\n */\nconst ServiceTypeSchema = z.enum(['server', 'worker'])\n\n/** EC-3 fix: names that conflict with generated docker-compose entries. */\nconst RESERVED_SERVICE_NAMES = ['web', 'caddy', 'postgres', 'redis'] as const\n\n/**\n * EC-12 fix: service names must be docker-compose-safe — lowercase,\n * start with a letter, contain only a-z 0-9 -. Reserved names rejected.\n */\nconst ServiceNameSchema = z\n .string()\n .min(1)\n .regex(\n /^[a-z][a-z0-9-]*$/,\n 'service name must be lowercase, start with letter, contain only a-z 0-9 -',\n )\n .refine((n) => !(RESERVED_SERVICE_NAMES as readonly string[]).includes(n), {\n message: `service name conflicts with reserved name (${RESERVED_SERVICE_NAMES.join('/')})`,\n })\n\n/**\n * Single service definition. All commands run from `services/<name>/` cwd.\n *\n * EC-4 fix: `proxy` regex requires NON-EMPTY path after `/` (the `+` quantifier)\n * to reject `/` which would catch-all and conflict with TheoKit's own routing.\n */\nconst ServiceDefinitionSchema = z.object({\n runtime: ServiceRuntimeSchema,\n type: ServiceTypeSchema.optional(),\n port: z.number().int().min(1).max(65535),\n proxy: z.string().regex(/^\\/[a-zA-Z0-9\\-_/]+$/, 'proxy must be a non-root path starting with /'),\n dev: z.string().min(1),\n build: z.string().optional(),\n start: z.string().min(1),\n openapi: z.url().optional(),\n healthcheck: z.string().regex(/^\\//, 'healthcheck must start with /').default('/health'),\n cors: z.boolean().default(false),\n env: z.record(z.string(), z.string()).optional(),\n dependsOn: z.array(z.string()).optional(),\n /**\n * EC-25 + ref doc §8: by default the proxy strips upstream Set-Cookie\n * to prevent the polyglot service from issuing cookies that conflict\n * with TheoKit's encrypted session. Set to true to opt-in.\n */\n passSetCookie: z.boolean().default(false),\n})\n\nexport type ServiceDefinition = z.infer<typeof ServiceDefinitionSchema>\nexport type ServicesConfig = Record<string, ServiceDefinition>\n\n/**\n * Topological-order helper used by `dependsOn` cycle detection refine.\n * Returns true iff `graph` is a DAG. Each node's deps must all reference\n * existing nodes; cycles return false.\n */\nfunction isDag(graph: Record<string, readonly string[]>): boolean {\n const WHITE = 0\n const GRAY = 1\n const BLACK = 2\n const colors: Record<string, 0 | 1 | 2> = {}\n for (const node of Object.keys(graph)) colors[node] = WHITE\n\n function visit(node: string): boolean {\n if (colors[node] === GRAY) return false // cycle\n if (colors[node] === BLACK) return true\n colors[node] = GRAY\n for (const dep of graph[node] ?? []) {\n if (!(dep in graph)) return false // missing reference\n if (!visit(dep)) return false\n }\n colors[node] = BLACK\n return true\n }\n\n for (const node of Object.keys(graph)) {\n if (colors[node] === WHITE && !visit(node)) return false\n }\n return true\n}\n\n/**\n * Full services config schema with cross-service refines.\n *\n * EC-1 fix: detect duplicate ports across services.\n * EC-13: empty `dependsOn: []` accepted as no-deps.\n * Self-dep, missing-ref, and cycles rejected via topological check.\n */\nexport const servicesConfigSchema = z\n .record(ServiceNameSchema, ServiceDefinitionSchema)\n .default({})\n // EC-1: duplicate port detection across services\n .refine(\n (s) => {\n const ports = Object.values(s).map((v) => v.port)\n return new Set(ports).size === ports.length\n },\n {\n message: 'duplicate port across services — each service must bind a unique port',\n },\n )\n // duplicate proxy prefix detection\n .refine(\n (s) => {\n const prefixes = Object.values(s).map((v) => v.proxy)\n return new Set(prefixes).size === prefixes.length\n },\n { message: 'duplicate proxy prefix across services' },\n )\n // dependsOn correctness (self-dep, missing reference, cycle)\n .refine(\n (s) => {\n // Build graph\n const graph: Record<string, readonly string[]> = {}\n for (const [name, def] of Object.entries(s)) {\n graph[name] = def.dependsOn ?? []\n }\n // Self-dep: a service mentioning itself is rejected as part of cycle detection\n for (const [name, deps] of Object.entries(graph)) {\n if (deps.includes(name)) return false\n }\n return isDag(graph)\n },\n {\n message: 'invalid dependsOn — must reference existing services, no self-deps, no cycles',\n },\n )\n\nexport type ServicesConfigInput = z.input<typeof servicesConfigSchema>\nexport type ServicesConfigOutput = z.output<typeof servicesConfigSchema>\n","/**\n * Dev orchestration entry point (T2.4).\n *\n * Spawns declared polyglot services, waits for all healthchecks to pass,\n * then signals readiness. The `theokit dev` CLI calls `orchestrateDev`\n * BEFORE starting Vite — Vite only boots after all services healthy.\n *\n * Empty `services: {}` → no-op (Wave 1 BC).\n *\n * Single composable entry that the CLI consumes. Spawn/healthcheck/log\n * primitives live in their own modules; this file ties them together.\n */\nimport type { ChildProcess, SpawnOptions } from 'node:child_process'\nimport { spawn } from 'node:child_process'\nimport { resolve } from 'node:path'\n\nimport type { ServiceDefinition, ServicesConfig } from '../schema.js'\n\nimport { pollHealthcheck } from './healthcheck-poller.js'\nimport { createLogMerger } from './log-merge.js'\nimport { buildSpawnEnv, installLifecycleHandlers } from './process-spawn-helpers.js'\n\nexport interface SpawnedService {\n name: string\n port: number\n process: ChildProcess\n}\n\nexport interface OrchestrateDevOptions {\n cwd: string\n services: ServicesConfig\n /** Defaults to `process.stdout.write.bind(process.stdout)`. */\n write?: (s: string) => void\n /** Test injection — replace child_process.spawn. */\n spawnFn?: typeof spawn\n /** Test injection — replace global fetch (for healthchecks). */\n customFetch?: typeof fetch\n /** Healthcheck timeout per service. Default 30_000. */\n healthcheckTimeoutMs?: number\n /** Whether to install parent lifecycle handlers (EC-7). Default true. */\n installSignalHandlers?: boolean\n}\n\nexport interface OrchestrateDevResult {\n spawned: SpawnedService[]\n /** Whether ALL services passed healthcheck. */\n allHealthy: boolean\n /** Services that timed out (only populated when !allHealthy). */\n unhealthy: string[]\n /** Call to gracefully stop all spawned services. */\n stop: () => Promise<void>\n}\n\nfunction spawnService(\n name: string,\n service: ServiceDefinition,\n cwd: string,\n options: {\n spawnFn: typeof spawn\n onLog: (s: string, stream: 'stdout' | 'stderr', line: string) => void\n },\n): SpawnedService {\n const env = buildSpawnEnv(name, service, process.env)\n const spawnOpts: SpawnOptions = {\n cwd,\n shell: true,\n env,\n stdio: ['ignore', 'pipe', 'pipe'],\n }\n const child = options.spawnFn(service.dev, spawnOpts)\n child.stdout?.setEncoding('utf-8')\n child.stderr?.setEncoding('utf-8')\n child.stdout?.on('data', (chunk: string) => {\n options.onLog(name, 'stdout', chunk)\n })\n child.stderr?.on('data', (chunk: string) => {\n options.onLog(name, 'stderr', chunk)\n })\n return { name, port: service.port, process: child }\n}\n\nasync function stopSpawned(s: SpawnedService): Promise<void> {\n return new Promise<void>((resolve) => {\n if (s.process.killed || s.process.exitCode !== null) {\n resolve()\n return\n }\n const timer = setTimeout(() => {\n s.process.kill('SIGKILL')\n resolve()\n }, 5_000)\n s.process.once('exit', () => {\n clearTimeout(timer)\n resolve()\n })\n s.process.kill('SIGTERM')\n })\n}\n\nexport async function orchestrateDev(\n options: OrchestrateDevOptions,\n): Promise<OrchestrateDevResult> {\n const entries = Object.entries(options.services)\n if (entries.length === 0) {\n return {\n spawned: [],\n allHealthy: true,\n unhealthy: [],\n stop: () => Promise.resolve(),\n }\n }\n\n const write =\n options.write ??\n ((s: string) => {\n process.stdout.write(s)\n })\n const spawnFn = options.spawnFn ?? spawn\n const merger = createLogMerger({ write })\n\n const spawned: SpawnedService[] = entries.map(([name, def]) =>\n spawnService(name, def, resolve(options.cwd, 'services', name), {\n spawnFn,\n onLog: merger.onLog,\n }),\n )\n\n const stop = async (): Promise<void> => {\n await Promise.all(spawned.map(stopSpawned))\n }\n\n if (options.installSignalHandlers !== false) {\n installLifecycleHandlers(process, stop)\n }\n\n // Parallel healthcheck\n const results = await Promise.all(\n entries.map(([name, def]) =>\n pollHealthcheck({\n url: `http://localhost:${String(def.port)}${def.healthcheck}`,\n timeoutMs: options.healthcheckTimeoutMs ?? 30_000,\n intervalMs: 500,\n customFetch: options.customFetch,\n }).then((r) => ({ name, result: r })),\n ),\n )\n\n const unhealthy = results.filter((r) => !r.result.healthy).map((r) => r.name)\n return {\n spawned,\n allHealthy: unhealthy.length === 0,\n unhealthy,\n stop,\n }\n}\n","/**\n * Healthcheck poller (T1.5).\n *\n * Polls `GET <url>` with backoff until 200 OR timeout. Used by `pnpm dev`\n * (T2.4) to gate readiness on declared services being healthy before\n * starting Vite.\n *\n * Invariants:\n * - Returns within `timeoutMs + intervalMs` (no hang)\n * - Never throws (returns `{ healthy: false }` on any error)\n * - `attempts >= 1` always\n * - Respects external AbortSignal (pre-aborted → immediate return, EC-18)\n */\n\nexport interface HealthcheckOptions {\n /** Full URL to poll, e.g. 'http://localhost:8001/health' */\n url: string\n /** Total time budget in ms. Default 30_000. */\n timeoutMs?: number\n /** Sleep between attempts in ms. Default 500. */\n intervalMs?: number\n /** Optional external cancel. Aborted signal causes early return. */\n signal?: AbortSignal\n /** Test injection — replace global fetch. */\n customFetch?: typeof fetch\n}\n\nexport interface HealthcheckResult {\n healthy: boolean\n attempts: number\n durationMs: number\n lastError?: string\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve) => {\n if (signal?.aborted) {\n resolve()\n return\n }\n const t = setTimeout(() => {\n resolve()\n }, ms)\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(t)\n resolve()\n },\n { once: true },\n )\n })\n}\n\nexport async function pollHealthcheck(options: HealthcheckOptions): Promise<HealthcheckResult> {\n const timeoutMs = options.timeoutMs ?? 30_000\n const intervalMs = options.intervalMs ?? 500\n const f = options.customFetch ?? fetch\n const start = Date.now()\n let attempts = 0\n let lastError: string | undefined\n\n // EC-18: signal already aborted → return immediately with at least 1 attempt counted\n if (options.signal?.aborted) {\n return {\n healthy: false,\n attempts: 1,\n durationMs: Date.now() - start,\n lastError: 'aborted',\n }\n }\n\n // Infinite poll loop terminated by status === 200 OR timeout OR abort.\n for (;;) {\n if (options.signal?.aborted) {\n return {\n healthy: false,\n attempts: Math.max(attempts, 1),\n durationMs: Date.now() - start,\n lastError: 'aborted',\n }\n }\n attempts++\n try {\n const res = await f(options.url, { method: 'GET', signal: options.signal })\n if (res.status === 200) {\n return { healthy: true, attempts, durationMs: Date.now() - start }\n }\n lastError = `status ${res.status}`\n } catch (err) {\n lastError = err instanceof Error ? err.message : String(err)\n }\n\n if (Date.now() - start >= timeoutMs) {\n return {\n healthy: false,\n attempts,\n durationMs: Date.now() - start,\n lastError,\n }\n }\n await sleep(intervalMs, options.signal)\n }\n}\n","/**\n * Log merger (T2.3).\n *\n * Combines stdout/stderr from multiple polyglot services into TheoKit's\n * dev terminal, prefixed with `[service-name]`. Parses JSON-line logs\n * (ADR-0015 invariant #5) for pretty-printing; falls back to raw line on\n * non-JSON.\n *\n * Pure / synchronous. The Vite plugin (T2.1) wires this as the `onLog`\n * callback passed to spawnServices.\n */\n\ninterface LogEntry {\n level?: string\n message?: string\n timestamp?: string\n traceparent?: string\n service?: string\n}\n\nexport interface LogMergerOptions {\n write: (s: string) => void\n}\n\nexport interface LogMerger {\n onLog: (service: string, stream: 'stdout' | 'stderr', chunk: string) => void\n}\n\n/**\n * Try to parse a log line as JSON. Returns parsed object on success,\n * undefined on failure (will fall back to raw rendering).\n */\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return v !== null && typeof v === 'object' && !Array.isArray(v)\n}\n\nfunction tryParseJson(line: string): LogEntry | undefined {\n const trimmed = line.trim()\n if (!trimmed.startsWith('{')) return undefined\n try {\n const parsed: unknown = JSON.parse(trimmed)\n if (!isPlainObject(parsed)) return undefined\n const entry: LogEntry = {}\n if (typeof parsed.level === 'string') entry.level = parsed.level\n if (typeof parsed.message === 'string') entry.message = parsed.message\n if (typeof parsed.timestamp === 'string') entry.timestamp = parsed.timestamp\n if (typeof parsed.traceparent === 'string') entry.traceparent = parsed.traceparent\n if (typeof parsed.service === 'string') entry.service = parsed.service\n return entry\n } catch {\n return undefined\n }\n}\n\nfunction renderLine(\n service: string,\n stream: 'stdout' | 'stderr',\n line: string,\n write: (s: string) => void,\n): void {\n if (line.length === 0) return\n\n const prefix = `[${service}]`\n const streamMarker = stream === 'stderr' ? '!' : ' '\n\n const parsed = tryParseJson(line)\n if (parsed) {\n const level = parsed.level ?? 'info'\n const msg = parsed.message ?? line\n write(`${prefix}${streamMarker}${level.toUpperCase()} ${msg}\\n`)\n return\n }\n\n write(`${prefix}${streamMarker}${line}\\n`)\n}\n\nexport function createLogMerger(options: LogMergerOptions): LogMerger {\n return {\n onLog(service, stream, chunk) {\n // Multi-line chunks: split by \\n and render each\n const lines = chunk.split('\\n')\n for (const line of lines) {\n renderLine(service, stream, line, options.write)\n }\n },\n }\n}\n","/**\n * Pure helpers extracted from process-spawn.ts (T2.2) for unit testability.\n *\n * The full spawn orchestration (child_process + healthcheck poll + log\n * merge) lives in process-spawn.ts; this file holds the parts that are\n * safe to test without mocking node:child_process.\n *\n * EC-7: lifecycle handlers prevent orphan children on parent exit/signal.\n * EC-8: auto-injected env vars (THEOKIT_SERVICE_NAME, THEOKIT_SERVICE_PORT).\n */\nimport type { ServiceDefinition } from '../schema.js'\n\n/**\n * Build the env passed to `child_process.spawn`.\n *\n * Precedence (low → high):\n * 1. process.env (the parent's environment)\n * 2. auto-injected THEOKIT_SERVICE_NAME + THEOKIT_SERVICE_PORT\n * 3. service.env (user-provided overrides win)\n */\nexport function buildSpawnEnv(\n name: string,\n service: ServiceDefinition,\n parentEnv: Record<string, string | undefined>,\n): Record<string, string> {\n const env: Record<string, string> = {}\n for (const [k, v] of Object.entries(parentEnv)) {\n if (typeof v === 'string') env[k] = v\n }\n env.THEOKIT_SERVICE_NAME = name\n env.THEOKIT_SERVICE_PORT = String(service.port)\n if (service.env) {\n for (const [k, v] of Object.entries(service.env)) {\n env[k] = v\n }\n }\n return env\n}\n\n/**\n * Format a service name as a log prefix. Deterministic; ANSI codes added\n * by the log-merge layer (T2.3), not here.\n */\nexport function formatLogPrefix(name: string): string {\n return `[${name}]`\n}\n\n/**\n * Install parent-process lifecycle handlers (EC-7).\n *\n * - `exit`: best-effort SIGKILL of all children (synchronous; we cannot await here)\n * - `SIGINT`: graceful stopAllServices then exit 130\n * - `SIGTERM`: graceful stopAllServices then exit 143\n *\n * SIGKILL on the parent is uncatchable (kernel-level); orphan children\n * are documented as a known limit on force-close (EC-27 doc).\n */\nexport function installLifecycleHandlers(proc: NodeJS.Process, stopAll: () => Promise<void>): void {\n proc.on('exit', () => {\n // Best-effort sync notify; cannot await\n })\n proc.on('SIGINT', () => {\n void stopAll().finally(() => {\n proc.exit(130)\n })\n })\n proc.on('SIGTERM', () => {\n void stopAll().finally(() => {\n proc.exit(143)\n })\n })\n}\n","/**\n * TheoCloud adapter scaffolding (T3.5 — Wave 2 stub; full Wave 3).\n *\n * Wave 2 establishes the contract: the adapter CONSUMES `.theokit/services.json`\n * (manifest emitted by `theokit build`) and produces TheoCloud deployment\n * artifacts. The artifacts themselves (K8s manifests, Helm charts, etc.) are\n * Wave 3 deliverables — this module provides the read/validate gate so the\n * manifest format is locked NOW.\n *\n * Per ADR-0012 invariant #4, the same `.theokit/services.json` consumed by\n * the `node` adapter (docker-compose generator) is the same shape consumed\n * here. No platform-specific fields. The contract is global.\n */\nimport type { ServicesManifest } from './manifest.js'\n\nexport interface TheoCloudAdapterArtifacts {\n /** Manifest schemaVersion the adapter consumed (v1 deprecated, v2 current). */\n manifestVersion: 1 | 2\n /** Service names that will be deployed. */\n services: string[]\n /** Wave 3 will populate K8s manifests here. */\n k8sManifests?: never\n /** Wave 3 will populate Helm values here. */\n helmValues?: never\n}\n\n/**\n * Wave 2 stub — validates the manifest is consumable. Throws if shape is\n * unexpected (forward-compat guard for Wave 3 development).\n *\n * Wave 3 will replace the body with real K8s manifest emission.\n */\nexport function prepareTheoCloudArtifacts(\n manifest: ServicesManifest | null,\n): TheoCloudAdapterArtifacts {\n if (manifest === null) {\n return { manifestVersion: 1, services: [] }\n }\n // v1 (deprecated, sunset 0.6.0) and v2 (current, adds `project`) share the\n // same `services[]` shape the adapter consumes — both are accepted. Any\n // other version is a forward-compat guard: bump handling here before the\n // builder emits a newer schema. (Regression: usetheodev/theokit#9 — the\n // builder emits v2 when a project name is supplied; rejecting v2 broke\n // every `theokit build --target theo-cloud`.)\n //\n // The type says `1 | 2`, but the manifest is read from disk (`readManifest`)\n // so a malformed/newer file can carry any number at runtime — read through a\n // numeric local so the guard stays reachable (narrowing `manifest.version`\n // directly would collapse the guard body to `never`).\n const version: number = manifest.version\n if (version !== 1 && version !== 2) {\n throw new Error(\n `TheoCloud adapter: unsupported manifest version ${String(version)}. ` +\n `Supported: schemaVersion 1 (deprecated) or 2. Update the adapter before bumping.`,\n )\n }\n return {\n manifestVersion: manifest.version,\n services: manifest.services.map((s) => s.name),\n }\n}\n","/**\n * Builds a Vite `server.proxy` configuration from TheoKit's\n * declarative `services: {}` config (T2.1).\n *\n * Pure function — no Vite imports, just data shaping. The Vite plugin\n * (services-dev.ts) calls this and merges the result into `vite.config.server.proxy`.\n *\n * Pattern follows Vite proxy reference (referencias/vite/packages/vite/src/node/server/middlewares/proxy.ts):\n * - Each path prefix becomes its own entry\n * - `changeOrigin: true` set by default (matches Vite's string-shortcut behavior)\n * - User-set entries take precedence on prefix collision (we never clobber)\n */\nimport type { ServicesConfig } from '../schema.js'\n\n/**\n * Shape compatible with Vite's `server.proxy` type. We intentionally don't\n * import Vite's `ProxyOptions` so this module stays platform-neutral and\n * unit-testable without Vite.\n */\nexport interface ViteProxyEntry {\n target: string\n changeOrigin: boolean\n rewrite?: (path: string) => string\n}\n\nexport type ViteProxyConfig = Record<string, string | ViteProxyEntry>\n\nexport function buildServicesProxyConfig(\n services: ServicesConfig,\n userProxy: ViteProxyConfig = {},\n): ViteProxyConfig {\n const fromServices: ViteProxyConfig = {}\n for (const [, def] of Object.entries(services)) {\n const prefix = def.proxy\n fromServices[prefix] = {\n target: `http://localhost:${String(def.port)}`,\n changeOrigin: true,\n // Strip the service's `proxy` prefix from the upstream URL so the\n // sidecar receives its own native paths (e.g. `/api/agent/echo` →\n // `/echo`). The sidecar declares routes against its own root path —\n // it doesn't know about the TheoKit proxy prefix.\n rewrite: (path: string) =>\n path.startsWith(prefix) ? path.slice(prefix.length) || '/' : path,\n }\n }\n // User proxy wins on collision (we add services first, then spread user on top)\n return { ...fromServices, ...userProxy }\n}\n"],"mappings":"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","names":["resolve","resolve"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/agent/workflow-tool.ts","../src/server/agent/acp-tool.ts","../src/server/agent/vendor-agent-tool.ts","../src/server/agent/code-mode.ts","../src/server/agent/channel-webhook.ts","../src/server/agent/stream-agent-turn-in-process.ts","../src/server/agent/mcp-stdio.ts"],"sourcesContent":["/**\n * M26 (ADR-0041) — `createWorkflowTool`: wrap an SDK `Workflow` as a `CustomTool`.\n *\n * THIN adapter. `packages/workflows/` stays G13-forbidden — the workflow ENGINE is the SDK's\n * (`Workflow.create(...).run(input)`). This exposes an already-built `Workflow` to an agent as one\n * callable tool: it validates the tool input, delegates to `workflow.run(input)`, and shapes the\n * result for the model. It calls no LLM, dispatches no tool, and runs no orchestration of its own —\n * the SDK owns all of that (sdk-runtime.md / G2).\n */\nimport { z } from 'zod'\n\nimport { defineAgentTool, type CustomTool } from '../define/define-agent-tool.js'\n\n/**\n * Structural stand-in for the SDK `Workflow` (the adapter never imports the SDK type — keeps the\n * SDK an optional peer). Any object with a `run(input)` resolving to `{ status, output }` matches.\n */\nexport interface WorkflowLike {\n run(input: unknown): Promise<{ status: string; output: unknown; runId?: string }>\n}\n\n/** Config for {@link createWorkflowTool}. `inputSchema` defaults to an open object. */\nexport interface WorkflowToolConfig {\n /** Tool name surfaced to the LLM. */\n name: string\n /** Tool description surfaced to the LLM. */\n description: string\n /** Zod schema for the workflow input (defaults to `z.object({}).passthrough()`). */\n inputSchema?: z.ZodType\n}\n\n/** Statuses `Workflow.run` reports as a non-success terminal state. */\nconst FAILURE_STATUSES = new Set(['failed', 'error', 'cancelled', 'canceled'])\n\n/**\n * Wrap an SDK `Workflow` as a {@link CustomTool}. Fails fast if `workflow` does not expose a\n * `run()` method (the SDK Workflow contract), so a mis-wired call is caught at definition time, not\n * at the first invocation (error-handling.md).\n */\nexport function createWorkflowTool(workflow: WorkflowLike, config: WorkflowToolConfig): CustomTool {\n const runFn = (workflow as { run?: unknown } | null | undefined)?.run\n if (typeof runFn !== 'function') {\n throw new Error(\n 'createWorkflowTool: the SDK does not expose a Workflow (expected an object with a run() method). ' +\n 'Pass a `Workflow.create(...).…build()` instance from @theokit/sdk.',\n )\n }\n // Open object by default so arbitrary workflow inputs pass through un-stripped.\n const inputSchema = config.inputSchema ?? z.looseObject({})\n\n return defineAgentTool({\n name: config.name,\n description: config.description,\n inputSchema: inputSchema as z.ZodObject<z.ZodRawShape>,\n handler: async (input: unknown): Promise<string> => {\n const run = await workflow.run(input)\n if (FAILURE_STATUSES.has(run.status)) {\n throw new Error(\n `createWorkflowTool(${JSON.stringify(config.name)}): workflow run ${\n run.runId ? `'${run.runId}' ` : ''\n }failed with status '${run.status}'.`,\n )\n }\n return typeof run.output === 'string' ? run.output : JSON.stringify(run.output)\n },\n })\n}\n","/**\n * M17 (theokit-ai-first) — createACPTool: wrap a coding agent (Claude Code, Amp, Codex) as a tool.\n *\n * Spawns the agent as a subprocess (Node `child_process` — an adapter concern per G8), drives it\n * with the transport-agnostic {@link AcpClient} over newline-delimited JSON-RPC, and returns a\n * `CustomTool`. `onPermissionRequest` is REQUIRED — security by default (no default-allow for file/\n * shell operations). The transport is injectable for tests.\n */\nimport { spawn, type ChildProcessByStdio } from 'node:child_process'\nimport type { Readable, Writable } from 'node:stream'\n\nimport { AcpClient, type AcpTransport } from '@theokit/agents'\nimport { encodeAcpMessage } from '@theokit/agents'\nimport type { CustomTool } from '@theokit/sdk'\n\n/** Stdio transport backed by a spawned subprocess (the default for {@link createACPTool}). */\nexport class NodeAcpTransport implements AcpTransport {\n // stdin=pipe, stdout=pipe, stderr=inherit → the third stream is null.\n private readonly proc: ChildProcessByStdio<Writable, Readable, null>\n\n constructor(command: string, args: string[] = [], cwd?: string) {\n this.proc = spawn(command, args, { cwd, stdio: ['pipe', 'pipe', 'inherit'] })\n }\n\n send(line: string): void {\n this.proc.stdin.write(line)\n }\n\n subscribe(onData: (chunk: string) => void): void {\n this.proc.stdout.on('data', (buf: Buffer) => {\n onData(buf.toString('utf8'))\n })\n }\n\n close(): void {\n this.proc.kill()\n }\n}\n\nexport interface AcpToolConfig {\n /** Executable for the coding agent (e.g. `claude`, `amp`, `codex`). */\n command: string\n /** Command-line arguments. */\n args?: string[]\n /** Working directory for the spawned agent. */\n cwd?: string\n /** Tool name the model calls. */\n name: string\n /** Tool description surfaced to the model. */\n description: string\n /**\n * REQUIRED — decide file/shell permission requests from the coding agent. Security by default:\n * there is NO default-allow. Return `{ granted: boolean }` (may be async).\n */\n onPermissionRequest: (params: unknown) => { granted: boolean } | Promise<{ granted: boolean }>\n /** Injected transport factory (defaults to spawning via {@link NodeAcpTransport}) — for tests. */\n transportFactory?: (config: AcpToolConfig) => AcpTransport\n}\n\nfunction defaultTransport(config: AcpToolConfig): AcpTransport {\n return new NodeAcpTransport(config.command, config.args, config.cwd)\n}\n\n/** Wrap a coding agent as a `CustomTool`. Fails fast if `onPermissionRequest` is missing. */\nexport function createACPTool(config: AcpToolConfig): CustomTool {\n if (typeof config.onPermissionRequest !== 'function') {\n throw new Error('[theokit] createACPTool requires onPermissionRequest (security by default — no default-allow)')\n }\n const makeTransport = config.transportFactory ?? defaultTransport\n return {\n name: config.name,\n description: config.description,\n inputSchema: {\n type: 'object',\n properties: { message: { type: 'string', description: 'The task/prompt for the coding agent.' } },\n required: ['message'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const message = typeof input.message === 'string' ? input.message : ''\n const client = new AcpClient(makeTransport(config))\n client.onRequest('session/request_permission', (params) => config.onPermissionRequest(params))\n const result = (await client.request('session/prompt', { message })) as { text?: string }\n return result.text ?? ''\n },\n }\n}\n\n// `encodeAcpMessage` is re-exported for callers building custom transports/handshakes.\nexport { encodeAcpMessage }\n","/**\n * M28 (ADR-0041) — `createVendorAgentTool`: expose a third-party agent SDK (Claude Agent SDK,\n * OpenAI, Cursor) behind a uniform `CustomTool`, mirroring the M17 ACP pattern.\n *\n * The vendor RUNTIME stays theirs — TheoKit only wires. The vendor client is INJECTED (the real\n * vendor SDK client in prod, a fake in tests), so no vendor dependency enters core; vendor-specific\n * client packages belong under `@theokit/agent-*`, never here. This calls no LLM of its own and runs\n * no loop — it delegates each prompt to `client.query(...)` (sdk-runtime.md / G2). Resume is threaded\n * via the vendor's own session id.\n */\nimport type { CustomTool } from '../define/define-agent-tool.js'\n\n/**\n * Structural contract a vendor agent client must satisfy (the adapter never imports a vendor type).\n * `query` runs one turn; `resumeSessionId` continues a prior vendor session; the returned\n * `sessionId` identifies the session to resume next.\n */\nexport interface VendorAgentClient {\n query(\n prompt: string,\n opts?: { resumeSessionId?: string },\n ): Promise<{ text: string; sessionId?: string }>\n}\n\n/** Config for {@link createVendorAgentTool}. */\nexport interface VendorAgentToolConfig {\n /** Vendor label (e.g. `claude`, `openai`, `cursor`). Drives the default tool name. */\n vendor: string\n /** The injected vendor client (real SDK client in prod, a fake in tests). */\n client: VendorAgentClient\n /** Tool name the model calls (defaults to `<vendor>_agent`). */\n name?: string\n /** Tool description surfaced to the model (defaults to a one-line delegate hint). */\n description?: string\n /**\n * Side-channel callback invoked with the vendor session id after each turn — lets the app capture\n * it for a later resume WITHOUT leaking session bookkeeping into the model's view of the result.\n */\n onSession?: (sessionId: string) => void\n}\n\n/**\n * Wrap a vendor agent SDK as a {@link CustomTool}. Fails fast if `vendor` is empty or the client\n * does not expose `query()` (error-handling.md) — a mis-wired call is caught at definition time.\n */\nexport function createVendorAgentTool(config: VendorAgentToolConfig): CustomTool {\n if (!config.vendor || config.vendor.length === 0) {\n throw new Error('createVendorAgentTool: `vendor` is required (e.g. \"claude\", \"openai\").')\n }\n const queryFn = (config.client as { query?: unknown } | null | undefined)?.query\n if (typeof queryFn !== 'function') {\n throw new Error(\n `createVendorAgentTool(${JSON.stringify(config.vendor)}): the vendor client does not expose a query() method. ` +\n 'Pass the vendor SDK client (or a @theokit/agent-* wrapper).',\n )\n }\n\n const name = config.name ?? `${config.vendor}_agent`\n const description =\n config.description ?? `Delegate a task to the ${config.vendor} agent and return its answer.`\n\n return {\n name,\n description,\n inputSchema: {\n type: 'object',\n properties: {\n prompt: { type: 'string', description: 'The task/prompt for the vendor agent.' },\n resumeSessionId: {\n type: 'string',\n description: 'Optional vendor session id to resume a prior conversation.',\n },\n },\n required: ['prompt'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const prompt = typeof input.prompt === 'string' ? input.prompt : ''\n const resumeSessionId =\n typeof input.resumeSessionId === 'string' ? input.resumeSessionId : undefined\n const result = await config.client.query(\n prompt,\n resumeSessionId !== undefined ? { resumeSessionId } : undefined,\n )\n if (result.sessionId !== undefined && config.onSession) config.onSession(result.sessionId)\n return result.text\n },\n }\n}\n","/**\n * M29 (ADR-0041) — `createCodeMode`: expose a set of tools to agent-authored code run inside an\n * ISOLATION boundary, so the agent composes tools programmatically instead of one call at a time.\n *\n * Security posture (the whole point of this feature):\n * - The isolation boundary (`sandbox`) is **injected**, never hand-rolled here (Top-risk 1). The app\n * supplies a vetted sandbox — isolated-vm, QuickJS-WASM, or a locked-down worker. TheoKit core\n * ships no VM and adds no sandbox dependency (same posture as the injected deploy adapter / the\n * M17 transport). `node:vm` is NOT a security boundary and MUST NOT be used as the sandbox.\n * - TheoKit owns the **restricted API** (only the declared tools are reachable from the code — no\n * `fs`, `process`, `require`, or network unless a declared, permission-gated tool provides it) and\n * the **mandatory permission gate**: every tool call from the code passes `onPermissionRequest`\n * first, and there is NO default-allow (mirrors M17 `onPermissionRequest`).\n *\n * Threat model (summary): a malicious model could author code that (a) calls a dangerous tool, or\n * (b) tries to reach a host capability. (a) is stopped by the permission gate (deny → the API call\n * throws). (b) is stopped by the injected sandbox (the restricted API is the ONLY surface the code\n * sees). If the app injects a weak sandbox, (b) is on the app — hence the vetted-sandbox requirement.\n */\nimport type { CustomTool } from '../define/define-agent-tool.js'\n\n/** The restricted API handed to sandboxed code: declared tool names → permission-gated callables. */\nexport type CodeModeApi = Record<string, (args: unknown) => Promise<unknown>>\n\n/** The injected isolation boundary. The app supplies a vetted implementation. */\nexport interface Sandbox {\n /** Run `code` with access to ONLY `api` (the restricted tool surface). Resolve the code's result. */\n run(code: string, api: CodeModeApi): Promise<unknown>\n}\n\n/** A permission decision for one tool call from sandboxed code. */\nexport interface CodeModePermission {\n granted: boolean\n /** Optional reason surfaced to the model on denial. */\n reason?: string\n}\n\nexport interface CodeModeConfig {\n /** The tools reachable from the code (the restricted API). */\n tools: CustomTool[]\n /** The injected isolation boundary (vetted sandbox — NEVER node:vm). */\n sandbox: Sandbox\n /**\n * REQUIRED — decide each tool call the code attempts. Security by default: NO default-allow.\n * Return `{ granted }` (may be async). Mirrors M17 `onPermissionRequest`.\n */\n onPermissionRequest: (req: {\n tool: string\n args: unknown\n }) => CodeModePermission | Promise<CodeModePermission>\n /** Tool name the model calls (default `run_code`). */\n name?: string\n /** Tool description surfaced to the model. */\n description?: string\n}\n\n/** Thrown when the permission gate denies a tool call from sandboxed code. */\nexport class CodeModePermissionDeniedError extends Error {\n constructor(tool: string, reason?: string) {\n const suffix = reason ? `: ${reason}` : ''\n super(`code-mode: tool '${tool}' denied by permission gate${suffix}`)\n this.name = 'CodeModePermissionDeniedError'\n }\n}\n\n/** M40 (ADR-0049) — render a tool's JSON-Schema input as a readable arg shape, e.g. `{ limit: number, region?: string }`. */\nfunction describeToolInput(inputSchema: Record<string, unknown>): string {\n const props = (inputSchema.properties as Record<string, { type?: unknown }> | undefined) ?? {}\n const required = new Set((inputSchema.required as string[] | undefined) ?? [])\n const entries = Object.entries(props).map(([key, spec]) => {\n // Complex Zod types (union/intersection/enum) may emit no top-level `type` → 'unknown' (safe).\n const type = typeof spec.type === 'string' ? spec.type : 'unknown'\n return `${key}${required.has(key) ? '' : '?'}: ${type}`\n })\n return entries.length > 0 ? `{ ${entries.join(', ')} }` : '{}'\n}\n\n/**\n * M40 (ADR-0049) — generate the model-facing instructions from the SAME `tools` allow-list\n * `createCodeMode` captures (DRY — cannot drift from the api surface it describes). Teaches the model\n * that its code runs in a sandbox, the available `api.<name>(input)` calls (ONLY this allow-list —\n * least-privilege scoping), and the return contract. Add it to the agent's system prompt.\n */\nfunction generateCodeModeInstructions(tools: CustomTool[], toolName: string): string {\n const calls = tools\n .map((t) => `- \\`await api.${t.name}(${describeToolInput(t.inputSchema)})\\` — ${t.description}`)\n .join('\\n')\n return [\n `The \\`${toolName}\\` tool runs your code in a sandbox. Your code may call ONLY these functions (each bridges to a real, validated tool on the host):`,\n calls,\n 'Write an async function body that composes these calls and return exactly ONE structured result. Prefer `Promise.all` for independent calls; do arithmetic and aggregation in code, not in prose.',\n ].join('\\n\\n')\n}\n\n/**\n * Build a code-mode tool + its generated model instructions (M40 / ADR-0049). Fails fast if\n * `onPermissionRequest` or `sandbox` is missing (security by default). `tool` takes `{ code }`,\n * assembles the permission-gated restricted API from `tools`, runs the code in the injected sandbox,\n * and returns the code's result. `instructions` (add it to the agent's system prompt) teaches the\n * model the sandboxed-code contract + the available `api.<name>(input)` calls.\n */\nexport function createCodeMode(config: CodeModeConfig): { tool: CustomTool; instructions: string } {\n if (typeof config.onPermissionRequest !== 'function') {\n throw new Error(\n 'createCodeMode requires onPermissionRequest (security by default — no default-allow for any tool).',\n )\n }\n const sandboxRun = (config.sandbox as { run?: unknown } | null | undefined)?.run\n if (typeof sandboxRun !== 'function') {\n throw new Error(\n 'createCodeMode requires an injected `sandbox` with a run() method (a vetted isolation boundary — never node:vm).',\n )\n }\n // M40 — an empty allow-list is a config mistake: the restricted API (and the generated\n // instructions) would be empty, and the code could call nothing. Fail fast.\n if (config.tools.length === 0) {\n throw new Error(\n 'createCodeMode requires a non-empty tools[] — the restricted API would be empty.',\n )\n }\n\n // Assemble the restricted API: each declared tool becomes a permission-gated callable.\n const api: CodeModeApi = {}\n for (const tool of config.tools) {\n api[tool.name] = async (args: unknown): Promise<unknown> => {\n const decision = await config.onPermissionRequest({ tool: tool.name, args })\n if (!decision.granted) throw new CodeModePermissionDeniedError(tool.name, decision.reason)\n return tool.handler(args as Record<string, unknown>)\n }\n }\n\n const name = config.name ?? 'run_code'\n const tool: CustomTool = {\n name,\n description:\n config.description ??\n 'Run code that composes the available tools. Only the declared tools are callable.',\n inputSchema: {\n type: 'object',\n properties: { code: { type: 'string', description: 'The code to run in the sandbox.' } },\n required: ['code'],\n },\n handler: async (input: Record<string, unknown>): Promise<string> => {\n const code = typeof input.code === 'string' ? input.code : ''\n const result = await config.sandbox.run(code, api)\n return typeof result === 'string' ? result : JSON.stringify(result)\n },\n }\n return { tool, instructions: generateCodeModeInstructions(config.tools, name) }\n}\n","/**\n * M27 (ADR-0041) — channel webhook routes: `POST /api/agents/<name>/channels/<platform>/webhook`.\n *\n * Auto-generates a per-platform inbound webhook endpoint that VALIDATES the platform signature\n * (reusing the existing webhook `VerifyFn` providers — Slack/Telegram/Discord — never a hand-rolled\n * scheme) and hands the parsed payload to an injected `onMessage` seam. The seam is where an app\n * wires the SDK gateway package (`@theokit/gateway-*`) that translates the payload into an agent\n * turn — TheoKit provides the route + signature gate, NOT the gateway's parsing (G2 / it does not\n * reimplement the gateway).\n */\nimport type { VerifyFn } from '../webhook/webhook-types.js'\n\nconst CHANNEL_PATH = /^\\/api\\/agents\\/([^/]+)\\/channels\\/([^/]+)\\/webhook$/\n\n/** Parsed `{ agent, platform }` from a channel webhook path, or `null` when it doesn't match. */\nexport function parseChannelPath(urlPath: string): { agent: string; platform: string } | null {\n const match = CHANNEL_PATH.exec(urlPath)\n if (!match) return null\n return { agent: decodeURIComponent(match[1]), platform: decodeURIComponent(match[2]) }\n}\n\n/** True when `urlPath` targets a channel webhook (dev/prod routing branches on this). */\nexport function isChannelPath(urlPath: string): boolean {\n return CHANNEL_PATH.test(urlPath)\n}\n\n/** The inbound message handed to the app after signature validation passes. */\nexport interface ChannelMessage {\n agent: string\n platform: string\n /** The parsed JSON payload from the platform (the gateway translates this to an agent turn). */\n payload: unknown\n}\n\nexport interface ChannelWebhookConfig {\n /** Per-platform signature validators (e.g. `{ slack: slack({...}), telegram: telegram({...}) }`). */\n validators: Record<string, VerifyFn>\n /** Handoff seam — wire the SDK gateway / agent here. Invoked only after signature validation. */\n onMessage: (message: ChannelMessage) => void | Promise<void>\n}\n\nfunction jsonError(status: number, code: string, message: string): Response {\n return new Response(JSON.stringify({ error: { code, message } }), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n/**\n * Handle one channel webhook request. Returns:\n * 404 UNKNOWN_PLATFORM — no validator configured for `<platform>`\n * 400 BAD_REQUEST — path is not a channel webhook, or the body is not JSON\n * 401 INVALID_SIGNATURE — the platform signature check failed (negative case)\n * 200 { ok: true } — validated + handed to `onMessage`\n */\nexport async function handleChannelWebhook(\n request: Request,\n urlPath: string,\n config: ChannelWebhookConfig,\n): Promise<Response> {\n const parsed = parseChannelPath(urlPath)\n if (parsed === null) {\n return jsonError(\n 400,\n 'BAD_REQUEST',\n 'Path must be /api/agents/<name>/channels/<platform>/webhook.',\n )\n }\n if (!Object.hasOwn(config.validators, parsed.platform)) {\n return jsonError(\n 404,\n 'UNKNOWN_PLATFORM',\n `No validator configured for platform '${parsed.platform}'.`,\n )\n }\n const verify = config.validators[parsed.platform]\n\n // Validate the signature against a CLONE so the body stays readable for the payload parse.\n const verifyResult = await verify(request.clone())\n if (!verifyResult.ok) {\n return jsonError(\n 401,\n 'INVALID_SIGNATURE',\n `Signature validation failed: ${verifyResult.reason}`,\n )\n }\n\n let payload: unknown\n try {\n payload = await request.json()\n } catch {\n return jsonError(400, 'BAD_REQUEST', 'Request body must be JSON.')\n }\n\n await config.onMessage({ agent: parsed.agent, platform: parsed.platform, payload })\n return new Response(JSON.stringify({ ok: true }), {\n status: 200,\n headers: { 'content-type': 'application/json' },\n })\n}\n","/**\n * M35 (multi-surface) — the in-process agent-turn seam (Model A).\n *\n * The FRAMEWORK-owned sibling of the HTTP `mountAgent` and the stdout `runAgentInTerminal`: it runs a\n * compiled agent with the SAME `compileAgentModule` + SAME `streamAgentUIMessages` (G2 — reuses the\n * SDK runtime, reimplements nothing), but returns the raw `UIMessageChunk` generator so ANY consumer\n * drives it directly — the Ink TUI (M35), a Tauri window (M36), or a test — in a SINGLE process with\n * NO HTTP loopback, NO port, and NO CSRF (there is no network boundary to defend).\n *\n * The ONLY difference from the HTTP mount is HITL resolution: the mount pauses the run and resolves\n * the approval via a SECOND HTTP request to `/approve/:id` (the approval registry). In-process there\n * is no second request — the caller resolves the approval INLINE via `awaitApproval` (e.g. the Ink\n * TUI's y/n prompt). The gated-tool map is `compiled.hitl` verbatim, so the pause semantics are\n * byte-identical to the HTTP path; only the resolver differs. Parity with the mount is by\n * construction: both compile the module, resolve function-form skills, and call `streamAgentUIMessages`\n * with the same `{ message, sessionId, hitl }`.\n *\n * Consumers WILL still receive `tool-approval-request` chunks from the returned generator — they are\n * INFORMATIONAL (render them or ignore them). The authoritative human gate is `awaitApproval`, which\n * the SDK awaits BEFORE the gated tool runs; the chunk is not the gate.\n */\nimport {\n compileAgentModule,\n resolveEnabledSkills,\n streamAgentUIMessages,\n type HitlDecision,\n type HumanInTheLoopOptions,\n} from '@theokit/agents'\nimport type { UIMessageChunk } from 'ai'\n\n/** An inline approval request handed to the caller's `awaitApproval` (the Ink/Tauri prompt). */\nexport interface InProcessApprovalRequest {\n approvalId: string\n toolName: string\n opts: HumanInTheLoopOptions\n}\n\n/** Resolve one gated-tool approval inline (approve/deny, or a structured {@link HitlDecision}). */\nexport type InProcessAwaitApproval = (\n req: InProcessApprovalRequest,\n) => Promise<boolean | HitlDecision>\n\nexport interface StreamAgentTurnInProcessInput {\n message: string\n /**\n * M35 (multimodal) — images to send alongside the text. Threaded to the SDK's structured\n * `SDKUserMessage { text, images }` send form. Absent ⇒ the string send path is byte-unchanged.\n */\n images?: Parameters<typeof streamAgentUIMessages>[2]['images']\n /** Resume key; a fresh id per run when omitted. */\n sessionId?: string\n /**\n * Inline HITL resolver — required IFF the agent has `@HumanInTheLoop`-gated tools. Omitting it for a\n * gated agent is a fail-fast error, never a silent bypass (Rule 8, the #99 lesson).\n */\n awaitApproval?: InProcessAwaitApproval\n /** Labels a fail-fast `AgentDefinitionError` (the file path). */\n source?: string\n /** Abort signal forwarded to the SDK stream (client disconnect / window close). */\n signal?: AbortSignal\n}\n\n/** Injectable stream fn (defaults to the real SDK bridge) — lets tests drive a deterministic stream. */\nexport interface StreamAgentTurnDeps {\n stream: typeof streamAgentUIMessages\n}\n\n/**\n * Thrown when a gated agent is run in-process without an `awaitApproval` resolver. Refusing loudly is\n * the correct posture: silently running a `@HumanInTheLoop`-gated tool with no human gate is exactly\n * the #99 class of bug. Typed so callers can catch it distinctly.\n */\nexport class InProcessApprovalRequiredError extends Error {\n constructor(toolNames: readonly string[]) {\n super(\n `Agent has HITL-gated tool(s) [${toolNames.join(', ')}] but no \\`awaitApproval\\` resolver was ` +\n `supplied to streamAgentTurnInProcess. In-process runs must resolve approvals inline — pass ` +\n `awaitApproval, or remove the gate. Refused (fail-closed).`,\n )\n this.name = 'InProcessApprovalRequiredError'\n }\n}\n\n/**\n * Run a compiled agent in-process and return its `UIMessageChunk` stream. `apiKey` is resolved by the\n * caller (same contract as the HTTP mount). Validation + compile happen SYNCHRONOUSLY (so a gated\n * agent without a resolver throws at call time, not lazily on first iteration); the returned value is\n * the SDK's `streamAgentUIMessages` generator.\n */\nexport function streamAgentTurnInProcess(\n mod: unknown,\n apiKey: string,\n input: StreamAgentTurnInProcessInput,\n deps: StreamAgentTurnDeps = { stream: streamAgentUIMessages },\n): AsyncGenerator<UIMessageChunk> {\n const compiled = compileAgentModule(mod, input.source)\n const gated = compiled.hitl\n\n // Fail-fast BEFORE building the stream: a gated agent with no inline resolver would silently bypass\n // the human gate (the #99 class of bug). Refuse loudly (Rule 8, fail-closed).\n if (gated && gated.size > 0 && !input.awaitApproval) {\n throw new InProcessApprovalRequiredError([...gated.keys()])\n }\n const resolve = input.awaitApproval\n\n // Mirror mount-agent's HITL wiring cast-free (structural inference); absent gate ⇒ the non-HITL\n // stream path (M2), unchanged. The SDK calls (approvalId, opts, toolName); route to the caller.\n const hitl =\n gated && gated.size > 0 && resolve\n ? {\n gated,\n awaitApproval: (approvalId: string, opts: HumanInTheLoopOptions, toolName: string) =>\n resolve({ approvalId, toolName, opts }),\n }\n : undefined\n\n const sessionId = input.sessionId ?? crypto.randomUUID()\n\n // Resolve function-form skills (`defineAgent({ skills: (ctx) => [...] })`) BEFORE streaming — exact\n // parity with mount-agent. Done INSIDE the returned generator so the synchronous fail-fast above is\n // preserved (the caller still gets a plain `AsyncGenerator`, no `await` at the call site). A static\n // skill list leaves `skillsResolver` undefined and this is a no-op.\n return (async function* () {\n if (compiled.skillsResolver) {\n const enabled = await resolveEnabledSkills(compiled.skillsResolver, compiled.runContext ?? {})\n if (enabled !== undefined) compiled.skills = { enabled, autoInject: true }\n }\n yield* deps.stream(compiled, apiKey, {\n message: input.message,\n // M35 — pass images through so the SDK send uses the structured `{ text, images }` form.\n ...(input.images !== undefined ? { images: input.images } : {}),\n sessionId,\n hitl,\n signal: input.signal,\n })\n })()\n}\n","/**\n * MCP stdio transport (M16 follow-up) — expose a TheoKit agent as an MCP server over stdin/stdout,\n * the sibling of the M16 HTTP route (`POST /api/agents/<name>/mcp`). A desktop MCP client (e.g.\n * Claude Desktop) spawns `theokit mcp <agent>` and speaks newline-delimited JSON-RPC over the pipe.\n *\n * This is a TRANSPORT over the framework's OWN {@link handleMcpJsonRpc} — it reuses the exact handler\n * the HTTP route uses; it calls no LLM, spawns no MCP client, and reimplements no runtime (G2 /\n * sdk-runtime.md). Distinct from the SDK's MCP CLIENT stdio (which spawns external MCP servers via\n * `mcpServers` command/args) — that stays SDK-side. Here TheoKit is the SERVER. The framework-side\n * placement of this server-exposure transport is recorded in ADR-0042 (refining ADR-0040's M16 note).\n */\nimport type { AppResource } from './mcp-app-resources.js'\nimport { handleMcpJsonRpc } from './mcp-handler.js'\n\n/**\n * Handle one newline-delimited JSON-RPC line. Returns the response line to write to stdout, or\n * `null` for a blank line (nothing to emit). A malformed JSON line yields a `-32700` (Parse error)\n * envelope — never throws, so the stdio loop never dies on bad input.\n */\nexport async function handleMcpStdioLine(\n line: string,\n mod: unknown,\n name: string,\n appResources: readonly AppResource[] = [],\n): Promise<string | null> {\n const trimmed = line.trim()\n if (trimmed.length === 0) return null\n let body: unknown\n try {\n body = JSON.parse(trimmed)\n } catch {\n return JSON.stringify({\n jsonrpc: '2.0',\n id: null,\n error: { code: -32700, message: 'Parse error' },\n })\n }\n const response = await handleMcpJsonRpc(mod, name, body, appResources)\n const payload: unknown = await response.json()\n return JSON.stringify(payload)\n}\n\n/** A minimal readable line source (an async iterable of lines) + a writable sink. */\nexport interface StdioStreams {\n /** Async iterable of newline-delimited input lines (e.g. `readline.createInterface({ input })`). */\n lines: AsyncIterable<string>\n /** Write a response line (the caller appends no newline). */\n write: (line: string) => void\n}\n\n/**\n * Drive the MCP stdio server loop: for each input line, dispatch via {@link handleMcpStdioLine} and\n * write the response line (with a trailing `\\n`). Returns when the input stream ends (EOF).\n */\nexport async function serveMcpStdio(\n mod: unknown,\n name: string,\n appResources: readonly AppResource[],\n streams: StdioStreams,\n): Promise<void> {\n for await (const line of streams.lines) {\n const out = await handleMcpStdioLine(line, mod, name, appResources)\n if (out !== null) streams.write(`${out}\\n`)\n }\n}\n"],"mappings":"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","names":["tool"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/devtools/shared.ts","../src/devtools/bridge/dispatcher.ts"],"sourcesContent":["/**\n * Devtools — shared types.\n *\n * NEVER use dangerouslySetInnerHTML in any devtools component — see plan EC-20.\n */\n\nexport type DevtoolsPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\nexport type DevtoolsTab =\n | 'requests'\n | 'routes'\n | 'agents'\n | 'actions'\n | 'errors'\n | 'csrf-readiness'\n | 'settings'\nexport type DevtoolsTheme = 'light' | 'dark' | 'system'\n\nexport interface RequestRecord {\n id: string\n traceId: string\n method: string\n path: string\n status: number\n durationMs: number\n startedAt: number\n csrfWarn?: { code: string; docsUrl: string }\n headers?: Record<string, string>\n bodyPreview?: string\n bodyLength?: number\n bodyTruncated?: boolean\n}\n\nexport interface ErrorRecord {\n id: string\n type: 'console' | 'unhandled' | 'csrf.warn'\n message: string\n stack?: string\n code?: string\n docsUrl?: string\n timestamp: number\n}\n\nexport interface CsrfWarnPayload {\n event: 'csrf.warn'\n code: string\n docsUrl: string\n method: string\n path: string\n reason: string\n}\n\nexport interface RouteInfo {\n path: string\n absoluteFilePath: string\n layoutChain: string[]\n hasLoading: boolean\n hasError: boolean\n hasNotFound: boolean\n}\n\nexport interface RouteManifest {\n routes: RouteInfo[]\n}\n\n/**\n * Per-agent-run telemetry surfaced to the Agents devtools tab.\n *\n * Emitted by `trackAgentRun` (server-side) via the dispatcher in dev mode;\n * prod tree-shakes the entire wire (v1.1 EC-4 `__IS_DEV` IIFE guard).\n * Type lives here (devtools/shared) since both producer (server/cost) and\n * consumer (AgentsTab) need the shape; following architecture v3 ADR-0001\n * rule \"shared types in core/contracts\" is the canonical home — for this\n * one we keep it in devtools/shared to minimize churn in core/contracts/.\n */\nexport interface AgentRunRecord {\n id: string\n timestamp: number\n userId: string\n model: string\n tokensInput: number\n tokensOutput: number\n costUsd: number\n status: 'finished' | 'error' | 'aborted'\n}\n\n/**\n * T5.1 — Per-action-call telemetry surfaced to the Actions devtools tab.\n *\n * Emitted by `action-execute.ts` (server-side) via the dispatcher in dev\n * mode; prod tree-shakes the wire (mirrors AgentRunRecord pattern).\n * Field-level error pins consume `error.fields` directly when status='error'\n * (per plan ADR D6).\n */\nexport interface ActionCallRecord {\n id: string\n timestamp: number\n name: string\n input: unknown\n output?: unknown\n error?: {\n code: string\n message: string\n fields?: Record<string, string[]>\n issues?: unknown[]\n }\n durationMs: number\n status: 'success' | 'error'\n}\n\nexport interface DevtoolsState {\n open: boolean\n visible: boolean\n position: DevtoolsPosition\n theme: DevtoolsTheme\n activeTab: DevtoolsTab\n requests: RequestRecord[]\n errors: ErrorRecord[]\n agentRuns: AgentRunRecord[]\n actionCalls: ActionCallRecord[]\n routeManifest: RouteManifest | null\n activeRoutePath: string | null\n activeChain: string[]\n agentStreamEvents: AgentStreamRecord[]\n}\n\nexport type DevtoolsAction =\n | { type: 'TOGGLE_PANEL' }\n | { type: 'TOGGLE_VISIBLE' }\n | { type: 'SET_TAB'; tab: DevtoolsTab }\n | { type: 'SET_POSITION'; position: DevtoolsPosition }\n | { type: 'SET_THEME'; theme: DevtoolsTheme }\n | { type: 'REQUEST_ADD'; request: RequestRecord }\n | { type: 'ERROR_ADD'; error: ErrorRecord }\n | { type: 'CSRF_WARN'; payload: CsrfWarnPayload }\n | { type: 'MANIFEST_UPDATED'; manifest: RouteManifest }\n | { type: 'ROUTE_MATCHED'; path: string; chain: string[] }\n | { type: 'AGENT_RUN_ADD'; run: AgentRunRecord }\n | { type: 'ACTION_CALL_ADD'; record: ActionCallRecord }\n | { type: 'RESET_REQUESTS' }\n | { type: 'RESET_ERRORS' }\n | { type: 'RESET_AGENT_RUNS' }\n | { type: 'RESET_ACTION_CALLS' }\n | { type: 'AGENT_STREAM_EVENT'; event: AgentStreamRecord }\n | { type: 'RESET_AGENT_STREAM' }\n\nexport const RING_BUFFER_CAP = 50\nexport const MAX_QUEUE_SIZE = 100\nexport const STORAGE_VERSION = 2\n\nexport const initialState: DevtoolsState = {\n open: false,\n visible: true,\n position: 'bottom-right',\n theme: 'dark',\n activeTab: 'requests',\n requests: [],\n errors: [],\n agentRuns: [],\n actionCalls: [],\n routeManifest: null,\n activeRoutePath: null,\n activeChain: [],\n agentStreamEvents: [],\n}\n\nexport const CHANNEL_REQUEST = 'theo:devtools:request' as const\nexport const CHANNEL_ERROR = 'theo:devtools:error' as const\nexport const CHANNEL_CSRF_WARN = 'theo:devtools:csrf.warn' as const\nexport const CHANNEL_MANIFEST = 'theo:devtools:manifest' as const\nexport const CHANNEL_ROUTE_MATCHED = 'theo:devtools:route-matched' as const\nexport const CHANNEL_AGENT_RUN = 'theo:devtools:agent.run' as const\nexport const CHANNEL_ACTION_CALL = 'theo:devtools:action.call' as const\nexport const CHANNEL_AGENT_STREAM = 'theo:devtools:agent.stream' as const\n\nexport interface AgentStreamRecord {\n runId: string\n agentName: string\n type: string\n content?: string\n toolName?: string\n timestamp: number\n}\n","/**\n * T2.1 — Devtools dispatcher with pre-mount event queue.\n *\n * Pattern: events emitted from anywhere (server WS, browser console.error,\n * dispatcher.onCsrfWarn) can fire BEFORE the React tree mounts. Each call\n * either dispatches immediately (when `setDispatch` registered a dispatch\n * fn) or queues until React is ready, then flushes on first mount.\n *\n * EC-23: queue capped at MAX_QUEUE_SIZE (FIFO eviction).\n * EC-24: setDispatch is idempotent — flush only on NULL → non-null\n * transition (StrictMode re-mount safe).\n *\n * NEVER use dangerouslySetInnerHTML in any devtools component — see plan EC-20.\n */\nimport {\n type ActionCallRecord,\n type AgentRunRecord,\n type AgentStreamRecord,\n type CsrfWarnPayload,\n type DevtoolsAction,\n type ErrorRecord,\n MAX_QUEUE_SIZE,\n type RequestRecord,\n type RouteManifest,\n} from '../shared.js'\n\ntype Dispatch = (action: DevtoolsAction) => void\ntype QueuedItem = (d: Dispatch) => void\n\nlet _dispatch: Dispatch | null = null\nconst _queue: QueuedItem[] = []\n\nfunction queuable<Args extends unknown[]>(\n fn: (d: Dispatch, ...args: Args) => void,\n): (...args: Args) => void {\n return (...args: Args) => {\n if (_dispatch) {\n try {\n fn(_dispatch, ...args)\n } catch (err) {\n // EC-25: an error inside reducer-bound dispatch path MUST NOT bubble\n // to the original event source (e.g. logger / HMR callback / global\n // error handler). Log and continue.\n\n console.error('[theo devtools] dispatch failed', err)\n }\n } else {\n // EC-23: cap queue, FIFO-evict if full\n if (_queue.length >= MAX_QUEUE_SIZE) _queue.shift()\n _queue.push((d) => {\n fn(d, ...args)\n })\n }\n }\n}\n\nfunction flushQueue(): void {\n while (_queue.length) {\n const fn = _queue.shift()\n if (!fn || !_dispatch) break\n try {\n fn(_dispatch)\n } catch (err) {\n console.error('[theo devtools] queued event failed', err)\n }\n }\n}\n\nexport const dispatcher = {\n onRequest: queuable((d: Dispatch, req: RequestRecord) => {\n d({ type: 'REQUEST_ADD', request: req })\n }),\n onError: queuable((d: Dispatch, err: ErrorRecord) => {\n d({ type: 'ERROR_ADD', error: err })\n }),\n onCsrfWarn: queuable((d: Dispatch, payload: CsrfWarnPayload) => {\n d({ type: 'CSRF_WARN', payload })\n }),\n onManifestUpdated: queuable((d: Dispatch, manifest: RouteManifest) => {\n d({ type: 'MANIFEST_UPDATED', manifest })\n }),\n onRouteMatched: queuable((d: Dispatch, path: string, chain: string[]) => {\n d({ type: 'ROUTE_MATCHED', path, chain })\n }),\n onAgentRun: queuable((d: Dispatch, run: AgentRunRecord) => {\n d({ type: 'AGENT_RUN_ADD', run })\n }),\n onActionCall: queuable((d: Dispatch, record: ActionCallRecord) => {\n d({ type: 'ACTION_CALL_ADD', record })\n }),\n onAgentStreamEvent: queuable((d: Dispatch, event: AgentStreamRecord) => {\n d({ type: 'AGENT_STREAM_EVENT', event })\n }),\n\n /**\n * Wire React's dispatch function in. Idempotent (EC-24): only the\n * NULL → non-null transition flushes the queue; subsequent non-null\n * sets just replace the reference (used for StrictMode re-mount).\n */\n setDispatch(d: Dispatch | null): void {\n const prev = _dispatch\n _dispatch = d\n if (d && !prev) flushQueue()\n },\n\n /** Testing helper — clear queue + dispatch reference between tests. */\n _reset(): void {\n _dispatch = null\n _queue.length = 0\n },\n\n /** Testing helper — observe queue length. */\n _queueLength(): number {\n return _queue.length\n },\n}\n\nexport type Dispatcher = typeof dispatcher\n"],"mappings":";AAiJO,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AAExB,IAAM,eAA8B;AAAA,EACzC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AAAA,EACX,UAAU,CAAC;AAAA,EACX,QAAQ,CAAC;AAAA,EACT,WAAW,CAAC;AAAA,EACZ,aAAa,CAAC;AAAA,EACd,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,aAAa,CAAC;AAAA,EACd,mBAAmB,CAAC;AACtB;AAEO,IAAM,kBAAkB;AACxB,IAAM,gBAAgB;AACtB,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB;AACzB,IAAM,wBAAwB;AAG9B,IAAM,uBAAuB;;;AC/IpC,IAAI,YAA6B;AACjC,IAAM,SAAuB,CAAC;AAE9B,SAAS,SACP,IACyB;AACzB,SAAO,IAAI,SAAe;AACxB,QAAI,WAAW;AACb,UAAI;AACF,WAAG,WAAW,GAAG,IAAI;AAAA,MACvB,SAAS,KAAK;AAKZ,gBAAQ,MAAM,mCAAmC,GAAG;AAAA,MACtD;AAAA,IACF,OAAO;AAEL,UAAI,OAAO,UAAU,eAAgB,QAAO,MAAM;AAClD,aAAO,KAAK,CAAC,MAAM;AACjB,WAAG,GAAG,GAAG,IAAI;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEA,SAAS,aAAmB;AAC1B,SAAO,OAAO,QAAQ;AACpB,UAAM,KAAK,OAAO,MAAM;AACxB,QAAI,CAAC,MAAM,CAAC,UAAW;AACvB,QAAI;AACF,SAAG,SAAS;AAAA,IACd,SAAS,KAAK;AACZ,cAAQ,MAAM,uCAAuC,GAAG;AAAA,IAC1D;AAAA,EACF;AACF;AAEO,IAAM,aAAa;AAAA,EACxB,WAAW,SAAS,CAAC,GAAa,QAAuB;AACvD,MAAE,EAAE,MAAM,eAAe,SAAS,IAAI,CAAC;AAAA,EACzC,CAAC;AAAA,EACD,SAAS,SAAS,CAAC,GAAa,QAAqB;AACnD,MAAE,EAAE,MAAM,aAAa,OAAO,IAAI,CAAC;AAAA,EACrC,CAAC;AAAA,EACD,YAAY,SAAS,CAAC,GAAa,YAA6B;AAC9D,MAAE,EAAE,MAAM,aAAa,QAAQ,CAAC;AAAA,EAClC,CAAC;AAAA,EACD,mBAAmB,SAAS,CAAC,GAAa,aAA4B;AACpE,MAAE,EAAE,MAAM,oBAAoB,SAAS,CAAC;AAAA,EAC1C,CAAC;AAAA,EACD,gBAAgB,SAAS,CAAC,GAAa,MAAc,UAAoB;AACvE,MAAE,EAAE,MAAM,iBAAiB,MAAM,MAAM,CAAC;AAAA,EAC1C,CAAC;AAAA,EACD,YAAY,SAAS,CAAC,GAAa,QAAwB;AACzD,MAAE,EAAE,MAAM,iBAAiB,IAAI,CAAC;AAAA,EAClC,CAAC;AAAA,EACD,cAAc,SAAS,CAAC,GAAa,WAA6B;AAChE,MAAE,EAAE,MAAM,mBAAmB,OAAO,CAAC;AAAA,EACvC,CAAC;AAAA,EACD,oBAAoB,SAAS,CAAC,GAAa,UAA6B;AACtE,MAAE,EAAE,MAAM,sBAAsB,MAAM,CAAC;AAAA,EACzC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOD,YAAY,GAA0B;AACpC,UAAM,OAAO;AACb,gBAAY;AACZ,QAAI,KAAK,CAAC,KAAM,YAAW;AAAA,EAC7B;AAAA;AAAA,EAGA,SAAe;AACb,gBAAY;AACZ,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA,EAGA,eAAuB;AACrB,WAAO,OAAO;AAAA,EAChB;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/observability/suggest.ts"],"sourcesContent":["export function levenshtein(a: string, b: string): number {\n const m = a.length\n const n = b.length\n const dp: number[][] = Array.from({ length: m + 1 }, () =>\n Array.from<number>({ length: n + 1 }).fill(0),\n )\n\n for (let i = 0; i <= m; i++) dp[i][0] = i\n for (let j = 0; j <= n; j++) dp[0][j] = j\n\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost)\n }\n }\n\n return dp[m][n]\n}\n\nexport function findSuggestion(\n input: string,\n candidates: string[],\n maxDistance = 3,\n): string | null {\n let best: string | null = null\n let bestDist = maxDistance + 1\n\n for (const c of candidates) {\n const d = levenshtein(input, c)\n if (d < bestDist) {\n bestDist = d\n best = c\n }\n }\n\n return best\n}\n"],"mappings":";AAAO,SAAS,YAAY,GAAW,GAAmB;AACxD,QAAM,IAAI,EAAE;AACZ,QAAM,IAAI,EAAE;AACZ,QAAM,KAAiB,MAAM;AAAA,IAAK,EAAE,QAAQ,IAAI,EAAE;AAAA,IAAG,MACnD,MAAM,KAAa,EAAE,QAAQ,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC;AAAA,EAC9C;AAEA,WAAS,IAAI,GAAG,KAAK,GAAG,IAAK,IAAG,CAAC,EAAE,CAAC,IAAI;AACxC,WAAS,IAAI,GAAG,KAAK,GAAG,IAAK,IAAG,CAAC,EAAE,CAAC,IAAI;AAExC,WAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,aAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,YAAM,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,IAAI;AACzC,SAAG,CAAC,EAAE,CAAC,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,IAAI;AAAA,IACjF;AAAA,EACF;AAEA,SAAO,GAAG,CAAC,EAAE,CAAC;AAChB;AAEO,SAAS,eACd,OACA,YACA,cAAc,GACC;AACf,MAAI,OAAsB;AAC1B,MAAI,WAAW,cAAc;AAE7B,aAAW,KAAK,YAAY;AAC1B,UAAM,IAAI,YAAY,OAAO,CAAC;AAC9B,QAAI,IAAI,UAAU;AAChB,iBAAW;AACX,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/observability/trace-context-propagation.ts"],"sourcesContent":["// T5a.1a — Web Standards migration (leaf-first slice). Web Crypto's\n// `crypto.getRandomValues()` is available on globalThis in every supported\n// runtime per ADR-0028 (Node 22+, CF Workers, Bun, Deno, browsers). No\n// node:crypto import needed — Web Crypto returns a typed array filled with\n// CSPRNG bytes, which we hex-encode locally instead of relying on Buffer.\n\n/**\n * W3C Trace Context propagation helpers for non-HTTP carriers (job\n * leases, webhook reply headers, agent SSE frames). Works against any\n * `Headers`-shaped carrier — Web Standards only.\n *\n * The HTTP-side extractor `extractTraceId` in `../http/trace-context.ts`\n * is request-scoped and returns only the trace_id string. This module\n * exposes the full `TraceContext` shape (trace_id + span_id + flags)\n * because downstream jobs/webhooks need to propagate a NEW span_id\n * (child span) while keeping the same trace_id.\n *\n * Format spec (W3C Trace Context Level 2):\n * traceparent: version-trace_id-parent_id-trace_flags\n * - version = '00' (current)\n * - trace_id = 32 hex chars (128-bit)\n * - parent_id = 16 hex chars (64-bit) — \"span_id\" of the producer\n * - trace_flags = 2 hex chars (sampled bit)\n *\n * @see https://www.w3.org/TR/trace-context/\n */\n\nexport interface TraceContext {\n /** 32 hex chars. NEVER the reserved all-zeros value. */\n readonly trace_id: string\n /** 16 hex chars. NEVER the reserved all-zeros value. */\n readonly span_id: string\n /** 2 hex chars. Sampled bit + reserved. */\n readonly flags: string\n}\n\nconst TRACEPARENT_RE = /^00-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/\nconst ALL_ZEROS_TRACE = '00000000000000000000000000000000'\nconst ALL_ZEROS_SPAN = '0000000000000000'\n\n/**\n * Extract a TraceContext from a Web Headers object. Returns `null` for:\n * - missing header\n * - malformed (not matching the version-trace-span-flags regex)\n * - reserved all-zeros trace_id (W3C-invalid)\n * - reserved all-zeros span_id (W3C-invalid)\n *\n * NEVER throws — defensive read.\n */\nexport function extractTraceContext(headers: Headers): TraceContext | null {\n const value = headers.get('traceparent')\n if (!value) return null\n const m = TRACEPARENT_RE.exec(value)\n if (!m) return null\n const trace_id = m[1]\n const span_id = m[2]\n const flags = m[3]\n if (trace_id === ALL_ZEROS_TRACE) return null\n if (span_id === ALL_ZEROS_SPAN) return null\n return { trace_id, span_id, flags }\n}\n\n/**\n * Write a canonical traceparent into a Web Headers object. Always\n * succeeds (defensive write — the caller is responsible for passing\n * a valid TraceContext; passing malformed input is a programming bug).\n */\nexport function injectTraceContext(headers: Headers, ctx: TraceContext): void {\n headers.set('traceparent', `00-${ctx.trace_id}-${ctx.span_id}-${ctx.flags}`)\n}\n\n/**\n * Generate a fresh TraceContext (new trace_id + span_id, flags='01' for\n * sampled). Used when no upstream traceparent exists — e.g., when a\n * cron fires or a webhook arrives without an upstream tracer.\n */\nexport function generateNewTraceContext(): TraceContext {\n return {\n trace_id: randomHex(16), // 16 bytes = 32 hex chars\n span_id: randomHex(8), // 8 bytes = 16 hex chars\n flags: '01',\n }\n}\n\nfunction randomHex(bytes: number): string {\n // crypto.getRandomValues is overwhelmingly unlikely to produce all-zeros,\n // but we guard anyway — the W3C spec rejects all-zeros and tests assert\n // this. Web Crypto API returns a Uint8Array; we hex-encode without Buffer\n // to stay runtime-agnostic (CF Workers / Bun / Deno have no Buffer global).\n for (;;) {\n const buf = new Uint8Array(bytes)\n globalThis.crypto.getRandomValues(buf)\n let hex = ''\n for (const b of buf) hex += b.toString(16).padStart(2, '0')\n if (!/^0+$/.test(hex)) return hex\n }\n}\n"],"mappings":";AAoCA,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AAWhB,SAAS,oBAAoB,SAAuC;AACzE,QAAM,QAAQ,QAAQ,IAAI,aAAa;AACvC,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,IAAI,eAAe,KAAK,KAAK;AACnC,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,WAAW,EAAE,CAAC;AACpB,QAAM,UAAU,EAAE,CAAC;AACnB,QAAM,QAAQ,EAAE,CAAC;AACjB,MAAI,aAAa,gBAAiB,QAAO;AACzC,MAAI,YAAY,eAAgB,QAAO;AACvC,SAAO,EAAE,UAAU,SAAS,MAAM;AACpC;AAgBO,SAAS,0BAAwC;AACtD,SAAO;AAAA,IACL,UAAU,UAAU,EAAE;AAAA;AAAA,IACtB,SAAS,UAAU,CAAC;AAAA;AAAA,IACpB,OAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAU,OAAuB;AAKxC,aAAS;AACP,UAAM,MAAM,IAAI,WAAW,KAAK;AAChC,eAAW,OAAO,gBAAgB,GAAG;AACrC,QAAI,MAAM;AACV,eAAW,KAAK,IAAK,QAAO,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1D,QAAI,CAAC,OAAO,KAAK,GAAG,EAAG,QAAO;AAAA,EAChC;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/devtools/server-side/redact.ts","../src/devtools/server-side/broadcast.ts"],"sourcesContent":["/**\n * T2.3 — Privacy redaction for devtools broadcasts.\n *\n * Pure helpers. NO side effects.\n *\n * - EC-18: redactQueryString — `?token=`, `?api_key=`, etc → '[REDACTED]'\n * - EC-19: truncateBody — accepts unknown; binary → '[binary body]'\n * - EC-26: serializeSafely — BigInt walker (recursive)\n * - redactHeaders — Authorization, Cookie, Set-Cookie, etc → '[REDACTED]'\n *\n * NEVER use dangerouslySetInnerHTML in any devtools component — see plan EC-20.\n */\n\nconst REDACTED_HEADERS = new Set([\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'x-auth-token',\n 'proxy-authorization',\n])\n\nconst REDACTED_QUERY_KEYS = new Set([\n 'token',\n 'api_key',\n 'password',\n 'secret',\n 'auth',\n 'access_token',\n])\n\nconst REDACTED_VALUE = '[REDACTED]'\nconst REDACTED_QUERY_VALUE = '%5BREDACTED%5D' // URL-encoded form\n\nexport function redactHeaders(\n h: Record<string, string | string[] | undefined>,\n): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries(h)) {\n if (v === undefined) continue\n if (REDACTED_HEADERS.has(k.toLowerCase())) {\n out[k] = REDACTED_VALUE\n } else {\n out[k] = Array.isArray(v) ? v.join(', ') : v\n }\n }\n return out\n}\n\n/**\n * EC-19: type-safe across string / Buffer / FormData / Blob / undefined.\n * Non-string bodies surface as a placeholder; never crash on .slice().\n */\nexport function truncateBody(\n body: unknown,\n max = 4096,\n): { preview: string; length: number; truncated: boolean } {\n if (body === null || body === undefined) {\n return { preview: '', length: 0, truncated: false }\n }\n if (typeof body !== 'string') {\n return { preview: '[binary body]', length: 0, truncated: true }\n }\n const length = body.length\n if (length <= max) return { preview: body, length, truncated: false }\n return { preview: body.slice(0, max), length, truncated: true }\n}\n\n/**\n * EC-18: scrub sensitive keys from the URL's query string.\n * Case-insensitive match on key names (after URL-decode).\n */\nexport function redactQueryString(path: string): string {\n const qIndex = path.indexOf('?')\n if (qIndex === -1) return path\n const base = path.slice(0, qIndex)\n const query = path.slice(qIndex + 1)\n if (query.length === 0) return path\n const parts = query.split('&').map((pair) => {\n const eqIndex = pair.indexOf('=')\n if (eqIndex === -1) return pair\n const key = pair.slice(0, eqIndex)\n let decodedKey = key\n try {\n decodedKey = decodeURIComponent(key)\n } catch {\n // Malformed encoding — fall back to raw key\n }\n if (REDACTED_QUERY_KEYS.has(decodedKey.toLowerCase())) {\n return `${key}=${REDACTED_QUERY_VALUE}`\n }\n return pair\n })\n return `${base}?${parts.join('&')}`\n}\n\n/**\n * EC-26: BigInt → '<n>n' string. Recursive walk for arrays and plain objects.\n * Returns a NEW value (does not mutate the input).\n *\n * Limitations:\n * - Functions, Symbols, Promises, class instances → returned as-is (broadcaster\n * will likely silently drop them via JSON.stringify default behavior).\n * - Cycles → would loop forever; defensive seen-set caps recursion.\n */\nexport function serializeSafely(value: unknown, seen = new WeakSet()): unknown {\n if (typeof value === 'bigint') return `${value.toString()}n`\n if (value === null || value === undefined) return value\n if (typeof value !== 'object') return value\n if (seen.has(value)) return '[Circular]'\n seen.add(value)\n if (Array.isArray(value)) {\n return value.map((v) => serializeSafely(v, seen))\n }\n // Treat plain objects only — class instances pass through unchanged.\n // `getPrototypeOf` returns `unknown` under strict typing; we only need\n // the identity check against `Object.prototype`.\n const proto: unknown = Object.getPrototypeOf(value)\n if (proto !== null && proto !== Object.prototype) return value\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = serializeSafely(v, seen)\n }\n return out\n}\n","/**\n * T2.1 + T2.3 + T2.4 — Server-side broadcast helper.\n *\n * `globalThis.__theoViteHotServer` is populated by the Vite plugin's\n * `configureServer` hook (T2.4) when dev mode is active. The helper:\n * - no-ops if the global is undefined (production, build, no dev server)\n * - applies redaction (T2.3) before sending — secrets never enter the WS payload\n * - wraps the whole send in try/catch to prevent ws.send failures from\n * leaking out and crashing the request handler\n *\n * NEVER use dangerouslySetInnerHTML in any devtools component — see plan EC-20.\n */\nimport type { RequestRecord, CsrfWarnPayload, ErrorRecord } from '../shared.js'\n\nimport { redactHeaders, redactQueryString, serializeSafely, truncateBody } from './redact.js'\n\ninterface ViteWsLike {\n ws?: {\n send(payload: { type: 'custom'; event: string; data: unknown }): void\n }\n httpServer?: unknown\n}\n\nfunction getViteServer(): ViteWsLike | undefined {\n const g = globalThis as { __theoViteHotServer?: ViteWsLike }\n return g.__theoViteHotServer\n}\n\n/**\n * Send a typed event to the devtools UI over Vite's HMR WebSocket.\n *\n * Default-deny — ALL errors (server gone, redact throws, ws.send fails)\n * result in a silent no-op + console.warn. Never throws to caller.\n */\nexport function broadcastToDevtools(event: string, data: unknown): void {\n try {\n const server = getViteServer()\n if (!server?.ws) return\n // Safe-serialize: convert BigInt; pure pass-through otherwise.\n const safe = serializeSafely(data)\n server.ws.send({ type: 'custom', event, data: safe })\n } catch (err) {\n console.warn('[theo devtools] broadcast failed', err)\n }\n}\n\n/**\n * Convenience: redact a RequestRecord before broadcasting.\n *\n * Applies:\n * - EC-18: redactQueryString on path\n * - redactHeaders on headers\n * - EC-19: truncateBody (binary-safe)\n * - EC-26: serializeSafely (BigInt walk) inside broadcastToDevtools\n */\nexport function broadcastRequest(req: RequestRecord): void {\n const safePath = redactQueryString(req.path)\n const safeHeaders = req.headers ? redactHeaders(req.headers) : undefined\n const bodyInfo = truncateBody(req.bodyPreview)\n const sanitized: RequestRecord = {\n ...req,\n path: safePath,\n headers: safeHeaders,\n bodyPreview: bodyInfo.preview,\n bodyLength: bodyInfo.length,\n bodyTruncated: bodyInfo.truncated,\n }\n broadcastToDevtools('theo:devtools:request', sanitized)\n}\n\nexport function broadcastError(err: ErrorRecord): void {\n broadcastToDevtools('theo:devtools:error', err)\n}\n\nexport function broadcastCsrfWarn(payload: CsrfWarnPayload): void {\n // CSRF payload has no secret-ish fields, but the path may carry tokens.\n const safe: CsrfWarnPayload = { ...payload, path: redactQueryString(payload.path) }\n broadcastToDevtools('theo:devtools:csrf.warn', safe)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/import-user-module.ts"],"sourcesContent":["/**\n * Helper for loading user-authored TypeScript modules at runtime.\n *\n * Why this exists: theokit framework code (`load-config.ts`, `job-scan.ts`,\n * `cron-scan.ts`, `integrate-ui.ts`, `static.ts`) dynamically imports\n * `.ts` files from user space (`theo.config.ts`, `server/jobs/*.ts`,\n * `static-paths.ts`, etc.). Two execution environments must work:\n *\n * 1. **Production CLI** — `dist/cli/index.js` starts with\n * `import \"tsx/esm\"` which registers a global Node ESM loader hook.\n * Subsequent `await import('foo.ts')` calls are intercepted by the\n * hook, transformed in-place, and returned as ESM namespaces.\n *\n * 2. **Vitest tests** — Vitest runs framework code through Vite's SSR\n * pipeline. Dynamic imports inside framework modules are hijacked\n * by Vite's `loadAndTransform`, which cannot resolve absolute paths\n * outside the Vite project root (e.g., `/tmp/test-fixture/foo.ts`).\n * Even with `server.deps.external` configured to hand the path\n * back to Node, the registered tsx/esm loader hits a `require()`\n * ESM cycle error on Node 22 because the calling stack contains\n * both the loader's own evaluation context AND the importer.\n *\n * `tsImport()` from `tsx/esm/api` is the documented escape hatch for\n * exactly this scenario: it transforms the source file via tsx's\n * standalone pipeline and evaluates the result via a fresh namespace,\n * bypassing both Vite SSR and any registered global hook.\n *\n * Strategy:\n * - Try native `await import()` first (zero overhead in production\n * where tsx hook is registered).\n * - On failure with the well-known cycle/resolution errors, fall back\n * to `tsImport()`. Other errors (genuine syntax errors, missing\n * module, etc.) propagate unchanged.\n *\n * Idempotent and side-effect-free.\n */\nimport { pathToFileURL } from 'node:url'\n\nconst CYCLE_PATTERNS = [\n // Node 22 strict ESM cycle detection when a registered loader hook\n // re-enters its own evaluation context.\n 'require() ES Module',\n 'in a cycle',\n // Vite SSR throws this when it cannot resolve an out-of-root URL.\n 'Failed to load url',\n 'Does the file exist?',\n // Node native ESM resolver throws this for `.ts` files when no\n // tsx loader is registered globally (the common test-worker case).\n 'Cannot find module',\n // ERR_UNKNOWN_FILE_EXTENSION for `.ts` files.\n 'Unknown file extension',\n]\n\nfunction isLoaderInteropError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n const msg = err.message\n return CYCLE_PATTERNS.some((p) => msg.includes(p))\n}\n\n/**\n * Load a user-authored TS/JS module at the given absolute file path.\n * `parentURL` defaults to the URL of this module file.\n *\n * @returns module namespace object (matches `await import(...)` return)\n */\nexport async function importUserModule(\n absoluteFilePath: string,\n parentURL: string = import.meta.url,\n): Promise<Record<string, unknown>> {\n const fileURL = pathToFileURL(absoluteFilePath).href\n try {\n return (await import(fileURL)) as Record<string, unknown>\n } catch (err) {\n if (!isLoaderInteropError(err)) throw err\n // Fallback: tsx programmatic API. Imports are inline, no global hook\n // mutation, no Vite SSR interference. Slower per call (~10-50ms vs\n // microseconds for registered loader) but only used as a last resort.\n const { tsImport } = (await import('tsx/esm/api')) as {\n tsImport: (\n specifier: string,\n options: string | { parentURL: string; tsconfig?: string; namespace?: string },\n ) => Promise<Record<string, unknown>>\n }\n // Bypass tsconfig — tsx's default bundler-style resolver handles\n // `.js → .ts` fallback for relative imports under the user's module.\n // Passing an explicit tsconfig sometimes shadows the default and\n // breaks the `.js → .ts` rewrite chain inside the namespaced loader\n // (verified empirically in cron-scan/job-scan vitest workers).\n return await tsImport(fileURL, parentURL)\n }\n}\n"],"mappings":";AAoCA,SAAS,qBAAqB;AAE9B,IAAM,iBAAiB;AAAA;AAAA;AAAA,EAGrB;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA,EAEA;AACF;AAEA,SAAS,qBAAqB,KAAuB;AACnD,MAAI,EAAE,eAAe,OAAQ,QAAO;AACpC,QAAM,MAAM,IAAI;AAChB,SAAO,eAAe,KAAK,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;AACnD;AAQA,eAAsB,iBACpB,kBACA,YAAoB,YAAY,KACE;AAClC,QAAM,UAAU,cAAc,gBAAgB,EAAE;AAChD,MAAI;AACF,WAAQ,MAAM,OAAO;AAAA,EACvB,SAAS,KAAK;AACZ,QAAI,CAAC,qBAAqB,GAAG,EAAG,OAAM;AAItC,UAAM,EAAE,SAAS,IAAK,MAAM,OAAO,aAAa;AAWhD,WAAO,MAAM,SAAS,SAAS,SAAS;AAAA,EAC1C;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/rate-limit/rate-limit-per-route.ts"],"sourcesContent":["// T5a.1d — Web Crypto migration. The last node:crypto consumer in server/.\n// `createHash('sha256')` is sync but Web Crypto's `subtle.digest('SHA-256', ...)`\n// is async. This propagates through hashFragment → deriveKey → the factory's\n// returned checker. createRouteRateLimiter has NO production consumers\n// (verified via grep; api-middleware uses the sibling createRateLimiter from\n// rate-limit.ts), so the cascade only affects test sites. IncomingMessage stays\n// as a type-only import (TS-erased; runtime-clean).\nimport type { IncomingMessage } from 'node:http'\n\nimport { parseCookieHeader } from '../http/cookies.js'\n\nimport { InMemoryStore, type RateLimitStore } from './rate-limit-store.js'\nimport type { RateLimitConfig, RateLimitResult } from './rate-limit.js'\n\n/**\n * T2.2 — Per-route + per-user rate limiting.\n *\n * Layered on top of `rate-limit-store.ts`. The route map allows\n * declarative policies (\"strict /api/login, loose everything else\")\n * driven by config, not handler-decorated. `keyBy` selects what\n * identifier the limiter buckets on.\n *\n * ADR D2: per-route via path matching, NOT per-handler decorator.\n * Operators can tune policies without touching route definitions.\n */\n\nexport type KeyByMode = 'ip' | 'session' | 'user' | ((req: IncomingMessage) => string)\n\nexport interface RouteRateLimitConfig {\n /** Fallback config used when no per-route entry matches. */\n default?: RateLimitConfig\n /** Map of path pattern → config. Exact-string keys (RegExp via API). */\n routes?: Record<string, RateLimitConfig>\n /** Same as `routes` but each entry is a [pattern, config] tuple, RegExp allowed. */\n routePatterns?: readonly [string | RegExp, RateLimitConfig][]\n /** Bucket identifier strategy. Default 'ip'. */\n keyBy?: KeyByMode\n /** Cookie name used by keyBy='session'. Defaults to 'theo_session'. */\n cookieName?: string\n /** Optional shared store (for multi-route correlation). Default per-limiter InMemoryStore. */\n store?: RateLimitStore\n}\n\n/**\n * Normalize a path for matching: strip query string, drop trailing slash\n * unless root. EC-5: `/api/login` and `/api/login/` collapse to the same\n * canonical form so attackers can't bypass strict limits.\n */\nfunction normalizePath(input: string): string {\n const noQuery = input.split('?')[0]\n if (noQuery.length > 1 && noQuery.endsWith('/')) return noQuery.slice(0, -1)\n return noQuery\n}\n\n/**\n * Test whether `path` matches `pattern`. String patterns are compared\n * after trailing-slash normalization (EC-5). RegExp uses `.test` after\n * resetting `lastIndex` (defensive against `/g` flag).\n */\nexport function matchRoutePattern(path: string, pattern: string | RegExp): boolean {\n const canonical = normalizePath(path)\n if (typeof pattern === 'string') {\n return canonical === normalizePath(pattern)\n }\n pattern.lastIndex = 0\n return pattern.test(canonical)\n}\n\n/**\n * Hash a string with SHA-256 and return the first 16 base64url chars.\n * Used by `keyBy='session'` so the raw cookie value never lands in a\n * rate-limit key (which may flow into audit logs).\n *\n * T5a.1d — async via Web Crypto subtle.digest (no node:crypto). The\n * base64url encoding is done manually because btoa+url-safe transform is\n * available everywhere but `digest('base64url')` is Node-only.\n */\nasync function hashFragment(input: string): Promise<string> {\n const buf = await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(input))\n const bytes = new Uint8Array(buf)\n let bin = ''\n for (const b of bytes) bin += String.fromCharCode(b)\n // eslint-disable-next-line sonarjs/slow-regex -- input is fixed-length 44 chars (SHA-256 base64), trailing '=' padding ≤ 2 chars, no ReDoS surface\n const b64url = btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n return b64url.slice(0, 16)\n}\n\n// T3.2 DRY consolidation: cookie parsing moved to ../http/cookies.ts.\n// The canonical `parseCookieHeader` returns a Map for O(1) lookup; this\n// wrapper preserves the original `readCookie(req, name)` signature so the\n// rest of the file stays untouched.\nfunction readCookie(req: IncomingMessage, name: string): string | undefined {\n return parseCookieHeader(req.headers.cookie ?? undefined).get(name)\n}\n\n/**\n * Build the rate-limit bucket key for the request based on `keyBy`.\n *\n * EC-6: session mode reads the configured `cookieName`. With the wrong\n * cookie name (e.g., default 'theo_session' but app uses 'app_session'),\n * we fall back to IP so anonymous users still get rate-limited rather\n * than sharing an empty bucket.\n */\nexport async function deriveKey(\n req: IncomingMessage,\n keyBy: KeyByMode,\n cookieName: string,\n): Promise<string> {\n if (typeof keyBy === 'function') return keyBy(req)\n // `req.socket` is typed as always-present in Node typings, but in test\n // doubles (object literals without `socket`) it can be missing — the\n // optional chain keeps the fallback path reachable.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive for test doubles\n const ip = req.socket?.remoteAddress ?? 'unknown'\n switch (keyBy) {\n case 'session': {\n const cookie = readCookie(req, cookieName)\n return cookie ? `session:${await hashFragment(cookie)}` : `ip:${ip}`\n }\n case 'user': {\n const userId = (req as unknown as { user?: { id?: string } }).user?.id\n return userId ? `user:${userId}` : `ip:${ip}`\n }\n case 'ip':\n default:\n return `ip:${ip}`\n }\n}\n\n/**\n * Per-route rate limiter factory. Returns a sync checker compatible with\n * the existing api-middleware shape.\n *\n * Backwards-compatibility (ADR D2): a flat `{ windowMs, max }` config is\n * accepted and treated as `default` (no per-route variants).\n */\nexport function createRouteRateLimiter(config: RouteRateLimitConfig | RateLimitConfig) {\n // Detect legacy flat shape\n const isFlat =\n 'windowMs' in config && 'max' in config && !('default' in config) && !('routes' in config)\n const cfg: RouteRateLimitConfig = isFlat ? { default: config } : config\n\n const store = cfg.store ?? new InMemoryStore()\n // CR-005: validate store shape ONCE at construction. The previous\n // implementation ran `instanceof InMemoryStore` on every request and\n // threw at request-time if a non-InMemoryStore was passed — which\n // turned a clear config error into a runtime 500 on the first request.\n if (!(store instanceof InMemoryStore)) {\n throw new Error(\n 'createRouteRateLimiter: async RateLimitStore implementations require a dedicated async middleware path. ' +\n 'Use the InMemoryStore default for the sync façade.',\n )\n }\n const inMemoryStore = store\n const keyBy = cfg.keyBy ?? 'ip'\n const cookieName = cfg.cookieName ?? 'theo_session'\n\n // Build a pre-compiled list of (pattern, config) tuples for matching.\n const patternList: [string | RegExp, RateLimitConfig][] = []\n if (cfg.routes) {\n for (const [pattern, c] of Object.entries(cfg.routes)) patternList.push([pattern, c])\n }\n if (cfg.routePatterns) {\n for (const tuple of cfg.routePatterns) patternList.push(tuple)\n }\n\n return async function checkRouteRateLimit(req: IncomingMessage): Promise<RateLimitResult> {\n const url = req.url ?? ''\n let matched: RateLimitConfig | undefined\n for (const [pattern, c] of patternList) {\n if (matchRoutePattern(url, pattern)) {\n matched = c\n break\n }\n }\n const effective = matched ?? cfg.default\n if (!effective) {\n // No route match + no default → not limited.\n return { limited: false, headers: {} }\n }\n\n // Bucket key includes normalized path so /api/login and /api/login/\n // collapse to the same bucket (EC-5).\n const bucketSuffix = typeof matched === 'undefined' ? '*default*' : normalizePath(url)\n const key = `${await deriveKey(req, keyBy, cookieName)}|${bucketSuffix}`\n const state = inMemoryStore.incrSync(key, effective.windowMs)\n\n if (state.count > effective.max) {\n const retryAfter = Math.ceil((state.resetAt - Date.now()) / 1000)\n return {\n limited: true,\n headers: {\n 'X-RateLimit-Limit': String(effective.max),\n 'X-RateLimit-Remaining': '0',\n 'Retry-After': String(retryAfter),\n },\n }\n }\n return {\n limited: false,\n headers: {\n 'X-RateLimit-Limit': String(effective.max),\n 'X-RateLimit-Remaining': String(Math.max(0, effective.max - state.count)),\n },\n }\n }\n}\n\n/**\n * T5a.2 Phase D slice 1/3 — Web-Standards rate-limiter inputs context.\n *\n * Web `Request` has no equivalent of `req.socket.remoteAddress` (Node\n * runtime concept) or `req.user` (set by upstream middleware). The\n * Web-shaped rate-limiter requires the caller to pass these explicitly:\n *\n * - `clientIp` — resolved per-runtime (Node: `socket.remoteAddress`;\n * CF Workers: `request.headers.get('cf-connecting-ip')`; Vercel:\n * `x-forwarded-for` first hop; Bun/Deno: adapter-specific).\n * - `userId` — resolved by auth middleware (Phase D slice 3/3 ships\n * the Web-shaped session helper).\n *\n * Defaults: `clientIp = 'unknown'`, `userId = undefined` (matches the\n * IncomingMessage path's fallback semantics).\n */\nexport interface DeriveKeyRequestContext {\n clientIp?: string\n userId?: string\n}\n\n/**\n * T5a.2 Phase D slice 1/3 — Web-Standards-shaped key derivation.\n *\n * Mirror of `deriveKey(req: IncomingMessage, keyBy, cookieName)` for the\n * Web `Request` shape. Same `'ip' | 'session' | 'user'` enum cases (the\n * `function` callback case is IncomingMessage-only because the existing\n * `KeyByMode` callback type is Node-shaped; Web callers use the enum\n * cases or call a future `KeyByModeWeb` shape — out of T5a.2 Phase D scope).\n *\n * Uses `getCookieFromRequest` (Phase B slice 6/6) for session-mode cookie\n * lookup. Cookie parsing has the same CR-009 percent-encoding safety.\n */\nexport async function deriveKeyFromRequest(\n request: Request,\n keyBy: Exclude<KeyByMode, (req: IncomingMessage) => string>,\n cookieName: string,\n ctx: DeriveKeyRequestContext = {},\n): Promise<string> {\n const ip = ctx.clientIp ?? 'unknown'\n switch (keyBy) {\n case 'session': {\n // Reuse the Web cookie helper extracted in Phase B slice 6/6 so\n // CR-009 percent-encoding sanity stays consistent across paths.\n const { getCookieFromRequest } = await import('../http/cookies.js')\n const cookie = getCookieFromRequest(request, cookieName)\n return cookie ? `session:${await hashFragment(cookie)}` : `ip:${ip}`\n }\n case 'user': {\n return ctx.userId ? `user:${ctx.userId}` : `ip:${ip}`\n }\n case 'ip':\n default:\n return `ip:${ip}`\n }\n}\n\n/**\n * T5a.2 Phase D slice 1/3 — Web-Standards rate-limiter factory.\n *\n * Mirror of `createRouteRateLimiter(config)` returning a checker that\n * accepts `(request: Request, ctx?: DeriveKeyRequestContext)` instead of\n * `(req: IncomingMessage)`. Same `RouteRateLimitConfig` accepted; same\n * `keyBy` enum cases; same `InMemoryStore` constraint (CR-005 guard).\n *\n * Same returned `RateLimitResult` shape (headers + limited boolean).\n *\n * Web `Request` has no `req.url` path-only property — uses\n * `new URL(request.url).pathname + search` to derive the URL the way the\n * IncomingMessage path's `req.url ?? ''` would.\n */\nexport function createRouteRateLimiterWeb(config: RouteRateLimitConfig | RateLimitConfig) {\n const isFlat =\n 'windowMs' in config && 'max' in config && !('default' in config) && !('routes' in config)\n const cfg: RouteRateLimitConfig = isFlat ? { default: config } : config\n\n const store = cfg.store ?? new InMemoryStore()\n if (!(store instanceof InMemoryStore)) {\n throw new Error(\n 'createRouteRateLimiterWeb: async RateLimitStore implementations require a dedicated async middleware path. ' +\n 'Use the InMemoryStore default for the Web façade.',\n )\n }\n const inMemoryStore = store\n const keyBy = (cfg.keyBy ?? 'ip') as Exclude<KeyByMode, (req: IncomingMessage) => string>\n const cookieName = cfg.cookieName ?? 'theo_session'\n\n const patternList: [string | RegExp, RateLimitConfig][] = []\n if (cfg.routes) {\n for (const [pattern, c] of Object.entries(cfg.routes)) patternList.push([pattern, c])\n }\n if (cfg.routePatterns) {\n for (const tuple of cfg.routePatterns) patternList.push(tuple)\n }\n\n return async function checkRouteRateLimitWeb(\n request: Request,\n ctx: DeriveKeyRequestContext = {},\n ): Promise<RateLimitResult> {\n // Web Request guarantees absolute URL — extract path+query for pattern\n // matching (mirror of IncomingMessage's `req.url ?? ''`).\n const parsed = new URL(request.url)\n const url = `${parsed.pathname}${parsed.search}`\n let matched: RateLimitConfig | undefined\n for (const [pattern, c] of patternList) {\n if (matchRoutePattern(url, pattern)) {\n matched = c\n break\n }\n }\n const effective = matched ?? cfg.default\n if (!effective) {\n return { limited: false, headers: {} }\n }\n\n const bucketSuffix = typeof matched === 'undefined' ? '*default*' : normalizePath(url)\n const key = `${await deriveKeyFromRequest(request, keyBy, cookieName, ctx)}|${bucketSuffix}`\n const state = inMemoryStore.incrSync(key, effective.windowMs)\n\n if (state.count > effective.max) {\n const retryAfter = Math.ceil((state.resetAt - Date.now()) / 1000)\n return {\n limited: true,\n headers: {\n 'X-RateLimit-Limit': String(effective.max),\n 'X-RateLimit-Remaining': '0',\n 'Retry-After': String(retryAfter),\n },\n }\n }\n return {\n limited: false,\n headers: {\n 'X-RateLimit-Limit': String(effective.max),\n 'X-RateLimit-Remaining': String(Math.max(0, effective.max - state.count)),\n },\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/http/controller-dispatch.ts","../src/server/agent/mount-agent.ts","../src/server/agent/build-agent-streamer.ts","../src/server/agent/approval-registry.ts","../src/server/agent/durable-ui-message-stream-response.ts","../src/server/agent/run-event-cache.ts","../src/server/agent/provider-resolver.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename --\n * Controller files are walked from the developer's `serverDir/controllers`\n * (a build-time config path), never from HTTP input. No injection vector.\n */\nimport { readdirSync, type Dirent } from 'node:fs'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { join } from 'node:path'\n\nimport { createDecoratorHandler, isControllerClass, type ServeAgent } from '@theokit/http'\n\nimport { dispatchCsrfWarn } from '../security/csrf-warn-dispatch.js'\nimport { enforceCsrf, type DisallowedConfig } from '../security/csrf.js'\n\nimport { incomingMessageToWebRequest } from './node-request.js'\nimport { sendError } from './send-response.js'\n\n/** A decorator controller constructor (`@Controller` class). */\ntype ControllerClass = new (...args: never[]) => object\n\n/** Loads a controller module by absolute path. In dev this is Vite's `ssrLoadModule`\n * (the Task 1.1 swc transform has already compiled the parameter decorators); tests\n * inject `@theokit/http`'s `loadControllerWithSwc`. */\nexport type ControllerModuleLoader = (absPath: string) => Promise<Record<string, unknown>>\n\n/** A built controller route table exposed as a pure Web-Standard handler. */\nexport interface ControllerDispatcher {\n /** `null` = no controller route matched — the host owns the miss (404 / fall-through). */\n dispatch(request: Request): Promise<Response | null>\n /** Non-executing route probe — true when a controller route owns `method` + `pathname`. */\n matches(method: string, pathname: string): boolean\n}\n\n// State-mutating methods get CSRF, mirroring the file-route pipeline (execute.ts).\nconst CSRF_PROTECTED_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE'])\n\n/** Recursively collect `*.controller.ts` files under `dir` (absolute paths). */\nfunction findControllerFiles(dir: string): string[] {\n const found: string[] = []\n const walk = (current: string): void => {\n let entries: Dirent[]\n try {\n entries = readdirSync(current, { withFileTypes: true })\n } catch {\n return // dir doesn't exist — no controllers\n }\n for (const entry of entries) {\n const full = join(current, entry.name)\n if (entry.isDirectory()) walk(full)\n else if (entry.name.endsWith('.controller.ts')) found.push(full)\n }\n }\n walk(dir)\n return found\n}\n\n/** A discovered controller: its source file + the loaded `@Controller` class. */\nexport interface ControllerModule {\n filePath: string\n cls: ControllerClass\n}\n\n/**\n * Load every `@Controller` class under `controllersDir` via the injected loader,\n * keeping each class paired with its source file (needed by the typed-client\n * codegen to emit `import type { X } from '<file>'`). Non-controller exports are\n * ignored (`isControllerClass` — reused from @theokit/http).\n */\nexport async function scanControllerModules(\n controllersDir: string,\n loadModule: ControllerModuleLoader,\n): Promise<ControllerModule[]> {\n const files = findControllerFiles(controllersDir)\n const modules: ControllerModule[] = []\n for (const filePath of files) {\n const mod = await loadModule(filePath)\n for (const exported of Object.values(mod)) {\n if (typeof exported === 'function' && isControllerClass(exported)) {\n modules.push({ filePath, cls: exported as ControllerClass })\n }\n }\n }\n return modules\n}\n\n/** Load every `@Controller` class under `controllersDir` (classes only). */\nexport async function scanControllers(\n controllersDir: string,\n loadModule: ControllerModuleLoader,\n): Promise<ControllerClass[]> {\n const modules = await scanControllerModules(controllersDir, loadModule)\n return modules.map((m) => m.cls)\n}\n\n/**\n * Scan `controllersDir` and build a Web-Standard dispatcher over the decorator\n * controllers found. Returns `null` when the directory has no controllers, so\n * the host can skip the controller path entirely (zero cost for routes-only apps).\n *\n * Dispatch REUSES @theokit/http's `createDecoratorHandler` (match + `@Param`\n * binding + `@Body` validation + Response building) — never re-implemented (ADR-1).\n */\nexport async function createControllerDispatcher(opts: {\n controllersDir: string\n loadModule: ControllerModuleLoader\n /** M47 — serves `@Expose`-bound agent routes (theo supplies a `mountAgent`-backed impl). */\n serveAgent?: ServeAgent\n}): Promise<ControllerDispatcher | null> {\n const classes = await scanControllers(opts.controllersDir, opts.loadModule)\n if (classes.length === 0) return null\n const handle = createDecoratorHandler({ controllers: classes, serveAgent: opts.serveAgent })\n return {\n dispatch: (request) => handle(request),\n matches: (method, pathname) => handle.matches(method, pathname),\n }\n}\n\n/** Write a buffered Web `Response` (controllers never stream) to a Node response. */\nasync function writeControllerResponse(res: ServerResponse, response: Response): Promise<void> {\n const headersBag: Record<string, string> = {}\n for (const [k, v] of response.headers) {\n if (k.toLowerCase() !== 'set-cookie') headersBag[k] = v\n }\n const setCookies = response.headers.getSetCookie()\n if (setCookies.length > 0) res.setHeader('Set-Cookie', setCookies)\n res.writeHead(response.status, headersBag)\n const body = response.body ? await response.text() : ''\n res.end(body || undefined)\n}\n\n/**\n * The `api-middleware` fall-through in one call: scan `controllersDir`, build the\n * dispatcher, and serve the request. Builds a body-ful Web `Request` (`@Body`\n * needs the body; the raw stream is undrained at a route miss) and enforces CSRF\n * with the SAME gate file routes use (parity). Returns `true` when a controller\n * handled it (or CSRF blocked it), `false` when there are no controllers OR none\n * matched (the host continues to its own 404). Built per-miss so controller edits\n * reflect via HMR.\n */\nexport async function dispatchControllerRequest(args: {\n controllersDir: string\n loadModule: ControllerModuleLoader\n req: IncomingMessage\n res: ServerResponse\n csrfMode: 'off' | 'warn' | 'strict'\n disallowed?: DisallowedConfig\n requestId: string\n /** M47 — serves `@Expose`-bound agent routes (mountAgent-backed); omit for routes-only apps. */\n serveAgent?: ServeAgent\n}): Promise<boolean> {\n const { req, res, csrfMode, disallowed, requestId } = args\n const dispatcher = await createControllerDispatcher({\n controllersDir: args.controllersDir,\n loadModule: args.loadModule,\n serveAgent: args.serveAgent,\n })\n if (!dispatcher) return false\n\n const method = (req.method ?? 'GET').toUpperCase()\n const webRequest = incomingMessageToWebRequest(req)\n const pathname = new URL(webRequest.url).pathname\n\n // CSRF parity: enforce ONLY when a protected-method controller route actually\n // owns this path (probe with the non-executing matcher — never double-dispatch,\n // which would run the handler + its side effects). An unrouted path falls\n // through to the host's own 404, not a 403.\n if (CSRF_PROTECTED_METHODS.has(method) && dispatcher.matches(method, pathname)) {\n const decision = enforceCsrf(\n req,\n csrfMode,\n { warn: dispatchCsrfWarn, path: req.url },\n disallowed,\n )\n if (!decision.allow) {\n sendError(\n res,\n 'CSRF_INVALID',\n decision.reason ?? 'CSRF check failed',\n 403,\n undefined,\n requestId,\n )\n return true\n }\n }\n\n const response = await dispatcher.dispatch(webRequest)\n if (response === null) return false\n await writeControllerResponse(res, response)\n return true\n}\n","/**\n * M2 (theokit-ai-first) — mount a scanned `agents/<name>.ts` module as an SSE endpoint.\n *\n * The SINGLE wiring point shared by dev (vite middleware) and prod (built server), so the\n * two never drift (EC-4). Web-Standard `Request` → `Response`: parse the chat body, compile\n * the module (`compileAgentModule`, converges both surfaces), stream via the M0/M1 canonical\n * protocol (`streamAgentUIMessages` → `uiMessageStreamResponse`).\n *\n * Request body — accepts the `@ai-sdk/react` `useChat` shape (`{ id, messages: UIMessage[] }`,\n * the typed-client path) AND a simple `{ message, sessionId? }` shape (M0/M1-style clients).\n */\nimport { compileAgentModule, resolveEnabledSkills, streamAgentUIMessages } from '@theokit/agents'\n\nimport { validateCsrfRequest, type CsrfMode } from '../security/csrf.js'\n\nimport { buildAgentHitl } from './build-agent-streamer.js'\nimport { durableUiMessageStreamResponse } from './durable-ui-message-stream-response.js'\nimport { getRunEventCache, mintRunId } from './run-event-cache.js'\n\n/** The message + session extracted from a chat request, or `null` when the body is invalid. */\nexport interface AgentRequestInput {\n message: string\n sessionId: string\n}\n\n/** Extract the text of a `{ type: 'text', text: string }` part from an untrusted value. */\nfunction partText(part: unknown): string {\n if (typeof part !== 'object' || part === null) return ''\n const p = part as Record<string, unknown>\n return p.type === 'text' && typeof p.text === 'string' ? p.text : ''\n}\n\nfunction jsonError(status: number, code: string, message: string): Response {\n return new Response(JSON.stringify({ error: { code, message } }), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n/**\n * Extract `{ message, sessionId }` from a chat request body. Returns `null` when neither the\n * ai-sdk `messages` shape nor the simple `message` shape yields non-empty user text.\n */\nexport function parseAgentRequestBody(body: unknown): AgentRequestInput | null {\n if (typeof body !== 'object' || body === null) return null\n const b = body as Record<string, unknown>\n\n // ai-sdk useChat shape: { id, messages: UIMessage[] } — take the last message's text parts.\n if (Array.isArray(b.messages) && b.messages.length > 0) {\n const last = b.messages[b.messages.length - 1] as Record<string, unknown>\n const parts = Array.isArray(last.parts) ? last.parts : []\n const text = parts.map(partText).join('')\n if (text.length === 0) return null\n const sessionId = typeof b.id === 'string' && b.id.length > 0 ? b.id : crypto.randomUUID()\n return { message: text, sessionId }\n }\n\n // Simple shape: { message, sessionId? }.\n if (typeof b.message === 'string' && b.message.length > 0) {\n const sessionId =\n typeof b.sessionId === 'string' && b.sessionId.length > 0 ? b.sessionId : crypto.randomUUID()\n return { message: b.message, sessionId }\n }\n\n return null\n}\n\n/** Non-runtime options for {@link mountAgent} (grouped so the call stays ≤ 5 params). */\nexport interface MountAgentOptions {\n /** Labels a fail-fast `AgentDefinitionError` (the agent file path). Default `'agent module'`. */\n source?: string\n /** CSRF posture at this boundary. Default `'strict'`; `'off'` when a controller already gated (G5). */\n csrfMode?: CsrfMode\n /**\n * theokit-file-based-config (EC-1) — the framework-resolved app root. When the agent opted into\n * `.theokit/` file-based config (`.settingSources([...])`), discovery points here, NOT `process.cwd()`.\n */\n projectRoot?: string\n}\n\n/**\n * Mount a loaded agent module as a `Response`. `apiKey` is resolved by the caller\n * (`resolveProvider`). See {@link MountAgentOptions} for the labeling / CSRF / app-root knobs.\n */\nexport async function mountAgent(\n mod: unknown,\n request: Request,\n apiKey: string,\n { source = 'agent module', csrfMode = 'strict', projectRoot }: MountAgentOptions = {},\n): Promise<Response> {\n // Enforce CSRF BEFORE any work — an agent run spends real LLM tokens, so a cross-origin\n // POST must be rejected before it reaches the SDK (parity with actions/routes). The custom\n // `X-Theo-Action` header + Origin match is the same defense `executeRoute`/`executeAction`\n // apply; the `useAgent` client sends the header. `off` skips; `warn` never blocks.\n if (csrfMode !== 'off') {\n const csrf = validateCsrfRequest(request)\n if (!csrf.valid && csrfMode === 'strict') {\n return jsonError(403, 'CSRF_FAILED', `CSRF check failed: ${csrf.reason}`)\n }\n }\n\n const compiled = compileAgentModule(mod, source)\n\n // M13 — resolve a per-request skills selector (from `defineAgent({ skills: (ctx) => [...] })`)\n // against the M7 run-context, setting `skills.enabled` before the SDK runs. `undefined` ⇒ the\n // SDK enables every discovered skill. `compiled` is fresh per request, so mutation is safe.\n if (compiled.skillsResolver) {\n const enabled = await resolveEnabledSkills(compiled.skillsResolver, compiled.runContext ?? {})\n if (enabled !== undefined) compiled.skills = { enabled, autoInject: true }\n }\n\n let body: unknown = null\n try {\n body = await request.json()\n } catch {\n /* invalid/empty JSON → handled below as a 400 */\n }\n\n const input = parseAgentRequestBody(body)\n if (input === null) {\n return jsonError(400, 'BAD_REQUEST', 'Request must contain a non-empty message or messages[].')\n }\n\n // When the agent has @HumanInTheLoop-gated tools (M4), wire the pause: the plugin's `awaitApproval`\n // registers a pending approval in the shared registry (the Promise that PAUSES the run); the\n // approve route (`handleAgentApproval`) resolves it. No gated tools ⇒ the M2 stream path unchanged.\n // Extracted to `build-agent-streamer.ts` (M39 / DRY — the thread routes reuse the same wiring).\n const hitl = buildAgentHitl(compiled)\n\n // M37 (ADR-0046) — mint a transport runId + stream through the durable layer:\n // each SSE frame is cached + `id:`-tagged so a dropped client can reconnect\n // (or a second client observe) via `GET /api/agents/<name>/runs/<runId>/stream`.\n // The runId is surfaced in the `x-theokit-run-id` response header.\n const runId = mintRunId()\n return durableUiMessageStreamResponse(\n streamAgentUIMessages(compiled, apiKey, {\n ...input,\n hitl,\n signal: request.signal,\n cwd: resolveDiscoveryCwd(compiled, projectRoot),\n baseDir: resolveSessionBaseDir(projectRoot),\n }),\n { runId, cache: getRunEventCache() },\n )\n}\n\n/**\n * theokit-file-based-config (EC-1) — resolve the `.theokit/` discovery cwd. When the agent opted into\n * file-based config (`.settingSources([...])`), point discovery at the framework-resolved app root,\n * NOT `process.cwd()` (which is not guaranteed to be the app root). No opt-in ⇒ `undefined` (byte-unchanged).\n */\nfunction resolveDiscoveryCwd(\n compiled: { settingSources?: readonly unknown[] },\n projectRoot: string | undefined,\n): string | undefined {\n const optedIn = (compiled.settingSources?.length ?? 0) > 0\n return projectRoot !== undefined && optedIn ? projectRoot : undefined\n}\n\n/**\n * SDK 4.0 (SE40) — resolve the root of the native `.jsonl` session transcript. Unlike `.theokit/`\n * discovery, persistence is NOT gated on a file-based-config opt-in — every agent session persists.\n * Root it under the app's `.data/` (git-ignored, EC-2), kept OUT of the `.theokit/` config dir so a\n * `projects/` transcript subtree never collides with `settingSources` discovery. Absent `projectRoot`\n * ⇒ `undefined` (the SDK default `~/.theokit` applies).\n */\nexport function resolveSessionBaseDir(projectRoot: string | undefined): string | undefined {\n if (projectRoot === undefined) return undefined\n // Web-Standards discipline (R3a/G8): no `node:path` in `server/` — append with `/` (the SDK\n // normalizes the separator). Strip a trailing slash on `projectRoot` so we never emit `//`.\n return `${projectRoot.replace(/\\/+$/, '')}/.data/agent-sessions`\n}\n","/**\n * M39 (ADR-0048) — shared agent-run streamer builders, extracted from\n * `mount-agent.ts` (DRY / G12) so the thread routes drive the SAME SDK run path\n * as the plain POST. Reuses SDK primitives (`compileAgentModule` +\n * `streamAgentUIMessages`) — it does NOT reimplement the loop (G2).\n */\n\nimport {\n compileAgentModule,\n type HumanInTheLoopOptions,\n resolveEnabledSkills,\n streamAgentUIMessages,\n} from '@theokit/agents'\nimport type { UIMessageChunk } from 'ai'\n\nimport { getApprovalRegistry } from './approval-registry.js'\n\ntype Compiled = ReturnType<typeof compileAgentModule>\n\n/**\n * Build the HITL wiring for a compiled agent: gated tools register a pending\n * approval in the shared registry (the Promise that PAUSES the run). `undefined`\n * when the agent has no `@HumanInTheLoop`-gated tools. Extracted verbatim from\n * `mountAgent` so both the plain POST and the thread routes wire HITL identically.\n */\nexport function buildAgentHitl(compiled: Compiled) {\n const gated = compiled.hitl\n if (gated === undefined || gated.size === 0) return undefined\n const registry = getApprovalRegistry()\n return {\n gated,\n awaitApproval: (approvalId: string, opts: HumanInTheLoopOptions, toolName: string) =>\n registry.register(approvalId, {\n timeoutMs: opts.timeout ?? 300_000,\n onTimeout: opts.onTimeout ?? 'abort',\n toolName,\n question: opts.question,\n ...(opts.payloadSchema !== undefined ? { payloadSchema: opts.payloadSchema } : {}),\n }),\n }\n}\n\n/**\n * A `startRun(sessionId, message)` closure for the thread dispatcher. Per run it\n * compiles the module fresh (skills resolution mutates `compiled`), resolves\n * per-request skills, builds HITL, and streams. Headless — no request signal (a\n * thread continuation runs to completion into the durable cache, not to a client).\n */\nexport function makeThreadStartRun(\n mod: unknown,\n apiKey: string,\n source: string,\n): (sessionId: string, message: string) => AsyncIterable<UIMessageChunk> {\n return (sessionId, message) =>\n (async function* () {\n const compiled = compileAgentModule(mod, source)\n if (compiled.skillsResolver) {\n const enabled = await resolveEnabledSkills(\n compiled.skillsResolver,\n compiled.runContext ?? {},\n )\n if (enabled !== undefined) compiled.skills = { enabled, autoInject: true }\n }\n const hitl = buildAgentHitl(compiled)\n yield* streamAgentUIMessages(compiled, apiKey, { message, sessionId, hitl })\n })()\n}\n","/**\n * M4 (theokit-ai-first) — the in-process HITL approval registry.\n *\n * The HITL plugin's `awaitApproval` calls `register(approvalId)` and awaits the returned Promise\n * (this is what genuinely PAUSES the SDK run — the SDK `pre_tool_call` hook is awaited). The\n * approve route calls `resolve(approvalId, approved)` to settle it. A per-approval timeout settles\n * the Promise deterministically per the `@HumanInTheLoop` `onTimeout` policy so a hung approval\n * never leaks the paused stream.\n *\n * Single-process contract (ADR 0038 / plan Drawback 2): a multi-instance deploy needs a shared\n * registry — the interface is injectable so a durable impl (Redis, etc.) slots in without touching\n * the harness. We do NOT build a durable store now (YAGNI).\n */\n// The timeout policy vocabulary is owned by the `@HumanInTheLoop` decorator (DRY / G12) — reuse it\n// rather than re-declaring the union, so the two can never drift.\nimport type { TimeoutAction } from '@theokit/agents'\n\nexport type { TimeoutAction }\n\nexport interface RegisterOptions {\n /** Milliseconds before the approval auto-settles per `onTimeout`. */\n timeoutMs: number\n /**\n * What a timeout means. Only `'proceed'` auto-approves; `'abort'` and `'retry'` both deny — the\n * registry does NOT implement retry semantics (a timed-out `'retry'` is a deny, not a re-prompt).\n */\n onTimeout: TimeoutAction\n /** M14 — the gated tool name, surfaced by `list()` (optional; absent for legacy callers). */\n toolName?: string\n /** M14 — the approval question, surfaced by `list()` (optional). */\n question?: string\n /**\n * M20 — an optional JSON-schema descriptor of the custom payload the approver may attach. Carried\n * verbatim into `list()` + the `approval_required` event so the UI knows what to collect. Kept as\n * a plain JSON object (not a live Zod schema) so the registry stays serializable and SDK-free.\n */\n payloadSchema?: Record<string, unknown>\n}\n\n/**\n * M20 — a settled HITL decision. `approved` is the allow/deny bit (backward-compatible with the\n * legacy boolean); `reason` + `payload` are the optional approver-attached extras that surface to\n * the model (on denial, via the veto message) and to the app/UI.\n */\nexport interface ApprovalDecision {\n approved: boolean\n reason?: string\n payload?: unknown\n}\n\n/** M14 — a pending approval as surfaced by {@link ApprovalRegistry.list}. */\nexport interface PendingApproval {\n approvalId: string\n toolName?: string\n question?: string\n /** Epoch millis when the pending approval auto-settles (registeredAt + timeoutMs). */\n expiresAt: number\n /** M20 — the declared custom-payload schema, if the gated tool declares one. */\n payloadSchema?: Record<string, unknown>\n}\n\nexport interface ApprovalRegistry {\n /**\n * Register a pending approval; the returned Promise settles with the full {@link ApprovalDecision}\n * on `resolve` or timeout (M20 — was a bare boolean pre-M20).\n */\n register(approvalId: string, opts: RegisterOptions): Promise<ApprovalDecision>\n /**\n * Settle a pending approval. Accepts a full {@link ApprovalDecision} OR a bare boolean (coerced to\n * `{ approved }` — backward-compatible). Returns false if the id is unknown or already settled.\n */\n resolve(approvalId: string, decision: boolean | ApprovalDecision): boolean\n /** M14 — list the currently-pending approvals (process-wide; single-process contract). */\n list(): PendingApproval[]\n}\n\ninterface Pending {\n settle: (decision: ApprovalDecision) => void\n timer: ReturnType<typeof setTimeout>\n info: PendingApproval\n}\n\n/**\n * The one process-wide registry the stream mount (`mountAgent`) and the approve route share.\n *\n * The in-process impl holds LIVE Promise resolvers in memory — the approval a request awaits and\n * the approval the route resolves MUST be the same object, so a single instance per process is not\n * a convenience but a correctness requirement. Lazily created; a durable/multi-instance deploy\n * swaps this accessor for a shared-store impl (ADR 0038 / plan Drawback 2) without touching callers.\n * Tests use {@link createInProcessApprovalRegistry} directly — never this singleton.\n */\nlet serverRegistry: ApprovalRegistry | undefined\nexport function getApprovalRegistry(): ApprovalRegistry {\n serverRegistry ??= createInProcessApprovalRegistry()\n return serverRegistry\n}\n\nexport function createInProcessApprovalRegistry(): ApprovalRegistry {\n const pending = new Map<string, Pending>()\n\n return {\n register(approvalId, opts) {\n return new Promise<ApprovalDecision>((resolve) => {\n const settle = (decision: ApprovalDecision): void => {\n const entry = pending.get(approvalId)\n if (!entry) return\n clearTimeout(entry.timer)\n pending.delete(approvalId)\n resolve(decision)\n }\n // 'proceed' → allow on timeout; 'abort'/'retry' → deny on timeout.\n const timer = setTimeout(() => {\n settle({ approved: opts.onTimeout === 'proceed' })\n }, opts.timeoutMs)\n const info: PendingApproval = {\n approvalId,\n toolName: opts.toolName,\n question: opts.question,\n expiresAt: Date.now() + opts.timeoutMs,\n ...(opts.payloadSchema !== undefined ? { payloadSchema: opts.payloadSchema } : {}),\n }\n pending.set(approvalId, { settle, timer, info })\n })\n },\n resolve(approvalId, decision) {\n const entry = pending.get(approvalId)\n if (!entry) return false\n // M20 — coerce the legacy bare-boolean form to a decision object.\n entry.settle(typeof decision === 'boolean' ? { approved: decision } : decision)\n return true\n },\n list() {\n return [...pending.values()].map((p) => p.info)\n },\n }\n}\n","import type { UIMessageChunk } from 'ai'\n\nimport type { RunEventCache } from './run-event-cache.js'\n\n/**\n * M37 (ADR-0046) — the DURABLE variant of `uiMessageStreamResponse`. Same\n * `UIMessageStream` wire (`useChat` still consumes it), plus three additions so\n * a dropped client can reconnect without missing chunks:\n *\n * 1. each SSE frame gains a monotonic `id: <seq>\\n` line (SSE-native — the\n * browser echoes it as `Last-Event-ID` on reconnect, ADR-0046 D3);\n * 2. every frame is `append`ed to the {@link RunEventCache} keyed by `runId`\n * so the reconnect endpoint can replay it;\n * 3. the response carries `x-theokit-run-id: <runId>` so the client learns the\n * reconnect key (ADR-0046 D2).\n *\n * Fail-clear (error-handling.md): if the source aborts mid-stream, the run is\n * still `end`ed in the cache and `[DONE]` is flushed — never left hanging.\n */\n\n/** The UIMessageStream SSE base headers — shared by the encoder + reconnect handler (DRY). */\nexport const SSE_BASE_HEADERS = {\n 'content-type': 'text/event-stream',\n 'x-vercel-ai-ui-message-stream': 'v1',\n} as const\n\n/** Header the client reads to obtain the reconnect key. */\nexport const RUN_ID_HEADER = 'x-theokit-run-id'\n\nexport const SSE_DONE_FRAME = 'data: [DONE]\\n\\n'\n\n/** Format one SSE frame with its sequence id (shared by the encoder + reconnect replay). */\nexport function formatSseFrame(seq: number, data: string): string {\n return `id: ${seq}\\ndata: ${data}\\n\\n`\n}\n\n/** UTF-8 encode an SSE frame for a `ReadableStream<Uint8Array>` (shared). */\nexport function encodeSse(text: string): Uint8Array {\n return new TextEncoder().encode(text)\n}\n\nexport interface DurableStreamDeps {\n readonly runId: string\n readonly cache: RunEventCache\n}\n\nexport function durableUiMessageStreamResponse(\n chunks: AsyncIterable<UIMessageChunk>,\n deps: DurableStreamDeps,\n): Response {\n const { runId, cache } = deps\n const stream = new ReadableStream<Uint8Array>({\n async start(controller) {\n try {\n for await (const chunk of chunks) {\n const data = JSON.stringify(chunk)\n const seq = cache.append(runId, data)\n controller.enqueue(encodeSse(formatSseFrame(seq, data)))\n }\n } catch {\n // Source aborted mid-stream. The upstream translator owns error\n // semantics (surfaces failures as chunks + closes gracefully); this\n // transport guarantees only a terminated, cache-ended stream.\n } finally {\n cache.end(runId)\n controller.enqueue(encodeSse(SSE_DONE_FRAME))\n controller.close()\n }\n },\n })\n return new Response(stream, {\n headers: { ...SSE_BASE_HEADERS, [RUN_ID_HEADER]: runId },\n })\n}\n","/**\n * M37 (ADR-0046) — the durable event cache behind resumable agent streams.\n *\n * As an agent turn streams over SSE, each `UIMessageChunk` frame is `append`ed\n * here keyed by a transport `runId`. A client that drops and reconnects (or a\n * second client that observes) `attach`es to the run: it replays the frames it\n * missed (`seq > Last-Event-ID`) and then follows the live tail. This is the\n * TRANSPORT half of durable agents (ADR-0040/0044) — it caches serialized\n * frames, it does NOT run the agent loop.\n *\n * Single-process by default (an in-memory `Map`, same contract as the HITL\n * `approval-registry`). A persistent backend (Redis / `ConversationStorageAdapter`)\n * plugs in behind {@link RunEventCache} for multi-instance deploys — it is NOT\n * shipped in core (ADR-0046 D4: no broker in core).\n */\n\n/** One recorded SSE frame: its monotonic sequence id + the serialized `data:` payload. */\nexport interface CachedFrame {\n readonly seq: number\n readonly data: string\n}\n\n/** The result of {@link RunEventCache.attach}. */\nexport interface AttachResult {\n /** False when `runId` is unknown (never started, or evicted). */\n readonly known: boolean\n /** Frames with `seq > afterSeq`, in order — replay these before the live tail. */\n readonly replay: readonly CachedFrame[]\n /** True when the run already terminated: `replay` is complete, no live frames follow. */\n readonly ended: boolean\n /** Detach the live listener (idempotent). */\n readonly unsubscribe: () => void\n}\n\n/**\n * A per-`runId` ordered frame buffer with live fan-out. Implemented in-memory by\n * default; swap a persistent backend for multi-instance deploys.\n */\nexport interface RunEventCache {\n /**\n * M39 — synchronously register `runId` as a live (frame-less) run so `has()` is\n * true and a subscriber can `attach()` BEFORE the first frame. Idempotent.\n * Closes the race where a thread's active run is resolvable in the registry but\n * not yet in the cache (a headless pump appends its first frame asynchronously).\n */\n begin(runId: string): void\n /** Record a frame for `runId`, returning its assigned monotonic `seq`. Notifies live listeners. */\n append(runId: string, data: string): number\n /** Mark `runId` terminated: notify listeners, then schedule bounded eviction. Idempotent. */\n end(runId: string): void\n /** Whether `runId` is currently cached (started + not yet evicted). */\n has(runId: string): boolean\n /**\n * Atomically snapshot the frames after `afterSeq` AND (when the run is still\n * live) register `onFrame`/`onEnd` in the SAME synchronous tick — so no frame\n * slips between replay and subscribe, and none is duplicated.\n */\n attach(\n runId: string,\n afterSeq: number,\n onFrame: (frame: CachedFrame) => void,\n onEnd: () => void,\n ): AttachResult\n}\n\ninterface Listener {\n readonly onFrame: (frame: CachedFrame) => void\n readonly onEnd: () => void\n}\n\ninterface RunBuffer {\n readonly frames: CachedFrame[]\n ended: boolean\n readonly listeners: Set<Listener>\n evictTimer?: ReturnType<typeof setTimeout>\n}\n\nconst DEFAULT_EVICT_AFTER_MS = 5 * 60_000 // 5 min after end (parity with approval-registry)\n\nconst NOOP_UNSUBSCRIBE = (): void => {\n // No live listener was registered (unknown or already-ended run).\n}\n\nexport interface RunEventCacheOptions {\n /** Milliseconds after `end()` before a run's buffer is evicted. Default 5 min. */\n readonly evictAfterMs?: number\n}\n\nexport function createInMemoryRunEventCache(opts: RunEventCacheOptions = {}): RunEventCache {\n const evictAfterMs = opts.evictAfterMs ?? DEFAULT_EVICT_AFTER_MS\n const runs = new Map<string, RunBuffer>()\n\n function getOrCreate(runId: string): RunBuffer {\n let buf = runs.get(runId)\n if (buf === undefined) {\n buf = { frames: [], ended: false, listeners: new Set() }\n runs.set(runId, buf)\n }\n return buf\n }\n\n return {\n begin(runId) {\n getOrCreate(runId)\n },\n\n append(runId, data) {\n const buf = getOrCreate(runId)\n const seq = buf.frames.length\n const frame: CachedFrame = { seq, data }\n buf.frames.push(frame)\n // Notify live listeners (a throwing listener must not break the others).\n for (const l of buf.listeners) {\n try {\n l.onFrame(frame)\n } catch {\n /* a broken subscriber never breaks the producer or its peers */\n }\n }\n return seq\n },\n\n has(runId) {\n return runs.has(runId)\n },\n\n end(runId) {\n const buf = runs.get(runId)\n if (buf === undefined || buf.ended) return\n buf.ended = true\n for (const l of buf.listeners) {\n try {\n l.onEnd()\n } catch {\n /* isolate a broken subscriber */\n }\n }\n buf.listeners.clear()\n buf.evictTimer = setTimeout(() => runs.delete(runId), evictAfterMs)\n // Never keep the process alive just for an eviction timer.\n buf.evictTimer.unref()\n },\n\n attach(runId, afterSeq, onFrame, onEnd) {\n const buf = runs.get(runId)\n if (buf === undefined) {\n return { known: false, replay: [], ended: false, unsubscribe: NOOP_UNSUBSCRIBE }\n }\n // Synchronous snapshot — no `await` before the subscribe below, so no\n // append can interleave (single-threaded): no gap, no dup.\n const replay = buf.frames.filter((f) => f.seq > afterSeq)\n if (buf.ended) {\n return { known: true, replay, ended: true, unsubscribe: NOOP_UNSUBSCRIBE }\n }\n const listener: Listener = { onFrame, onEnd }\n buf.listeners.add(listener)\n return {\n known: true,\n replay,\n ended: false,\n unsubscribe: () => {\n buf.listeners.delete(listener)\n },\n }\n },\n }\n}\n\n/**\n * Process-wide singleton — the SSE encoder (`durableUiMessageStreamResponse`) and\n * the reconnect endpoint MUST share ONE cache. Lazily created; a multi-instance\n * deploy swaps this accessor for a shared-store impl (ADR-0046 D4) without\n * touching callers. Tests use {@link createInMemoryRunEventCache} directly.\n */\nlet serverCache: RunEventCache | undefined\nexport function getRunEventCache(): RunEventCache {\n serverCache ??= createInMemoryRunEventCache()\n return serverCache\n}\n\n/** Mint a stable transport `runId` (the reconnect key, ADR-0046 D2). */\nexport function mintRunId(): string {\n return `run-${crypto.randomUUID()}`\n}\n","/**\n * Provider Resolver — Strategy + Registry pattern (FAANG-grade).\n *\n * Inspiração: Dapr Conversation Registry (`dapr/pkg/components/conversation/registry.go`)\n * + Encore Manager provider array (`encore/runtimes/go/pubsub/manager_internal.go`).\n *\n * Princípio: provider routing é responsabilidade do FRAMEWORK, não do consumer.\n * Consumer template usa `model: { id: 'gpt-4o-mini' }` puro — sem conditionals.\n *\n * Wire protocol: OpenAI Chat Completions (universal — implementado por todos\n * os providers: OpenRouter, Groq, Mistral, Together, Anthropic via proxy, etc).\n *\n * Resolução por prioridade (FIRST match wins):\n * 1. OPENROUTER_API_KEY → baseUrl=openrouter.ai (gateway multi-modelo)\n * 2. OPENAI_API_KEY → baseUrl=api.openai.com\n * 3. ANTHROPIC_API_KEY → direct Anthropic (Messages API, não OpenAI-compat)\n *\n * Escape hatch: `options.apiKey` explícito SOBREPÕE auto-resolution\n * (consumer pode forçar provider específico se quiser).\n */\n\n/**\n * Provider configuration descriptor — Registry entry shape.\n *\n * @public\n */\nexport interface ProviderDescriptor {\n /** Stable name used internally — não exposto no wire. */\n name: string\n /** Environment variable that holds the API key for this provider. */\n envKey: string\n /** Base URL for the provider's OpenAI-compatible (or native) API. */\n baseUrl: string\n /** Resolution priority (lower = higher priority). FIRST match wins. */\n priority: number\n}\n\n/**\n * Resolved provider configuration — output of `resolveProvider()`.\n *\n * @public\n */\nexport interface ResolvedProvider {\n name: string\n apiKey: string\n baseUrl: string\n}\n\n/**\n * Default provider registry. Order = priority (first = highest).\n *\n * Adding a new provider:\n * 1. Append entry below (or register via `registerProvider()`).\n * 2. Set `envKey` matching the user's env var convention.\n * 3. Set `baseUrl` to the OpenAI-compat endpoint (or native if not compat).\n * 4. Provider name used in telemetry/logs only — never wire-exposed.\n */\nconst DEFAULT_REGISTRY: ProviderDescriptor[] = [\n {\n name: 'openrouter',\n envKey: 'OPENROUTER_API_KEY',\n baseUrl: 'https://openrouter.ai/api/v1',\n priority: 1,\n },\n {\n name: 'openai',\n envKey: 'OPENAI_API_KEY',\n baseUrl: 'https://api.openai.com/v1',\n priority: 2,\n },\n {\n name: 'anthropic',\n envKey: 'ANTHROPIC_API_KEY',\n baseUrl: 'https://api.anthropic.com',\n priority: 3,\n },\n]\n\n/**\n * Runtime registry — copy of DEFAULT_REGISTRY mutable via registerProvider().\n * Sorted by priority on every resolve (stable, O(n log n) — n <= ~10 providers).\n */\nconst registry: ProviderDescriptor[] = [...DEFAULT_REGISTRY]\n\n/**\n * Register a new provider (Registry pattern — runtime extension point).\n * Useful for self-hosted endpoints or custom providers without touching theokit src.\n *\n * @example\n * registerProvider({\n * name: 'self-hosted',\n * envKey: 'SELF_HOSTED_API_KEY',\n * baseUrl: 'https://llm.internal.acme.com/v1',\n * priority: 0, // highest priority\n * })\n *\n * @public\n */\nexport function registerProvider(descriptor: ProviderDescriptor): void {\n // Idempotent — replace existing by name.\n const idx = registry.findIndex((p) => p.name === descriptor.name)\n if (idx >= 0) registry[idx] = descriptor\n else registry.push(descriptor)\n}\n\n/**\n * Reset registry to DEFAULT_REGISTRY (test-only / dev escape hatch).\n *\n * @public\n */\nexport function resetProviderRegistry(): void {\n registry.length = 0\n registry.push(...DEFAULT_REGISTRY)\n}\n\n/**\n * Get current registry snapshot (read-only — inspection).\n *\n * @public\n */\nexport function listProviders(): readonly ProviderDescriptor[] {\n return [...registry].sort((a, b) => a.priority - b.priority)\n}\n\n/**\n * Resolve provider from env vars by priority. FIRST env var found wins.\n *\n * @returns ResolvedProvider with apiKey + baseUrl + name\n * @throws Error if NO provider env var is set (actionable message)\n *\n * @public\n */\nexport function resolveProvider(): ResolvedProvider {\n const sorted = [...registry].sort((a, b) => a.priority - b.priority)\n for (const desc of sorted) {\n const apiKey = process.env[desc.envKey]\n if (apiKey && apiKey.length > 0) {\n return {\n name: desc.name,\n apiKey,\n baseUrl: desc.baseUrl,\n }\n }\n }\n // No env var found — emit actionable error.\n const envKeys = sorted.map((p) => p.envKey).join(' OR ')\n throw new Error(\n `No LLM provider API key found in environment. Set one of: ${envKeys}. ` +\n `Get a free OpenRouter key at https://openrouter.ai/keys (recommended — one key, many models).`,\n )\n}\n\n/**\n * Try to resolve — does NOT throw. Returns null if no provider available.\n * Useful for graceful degradation (e.g., mock mode).\n *\n * @public\n */\nexport function tryResolveProvider(): ResolvedProvider | null {\n try {\n return resolveProvider()\n } catch {\n return null\n }\n}\n"],"mappings":"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","names":["compileAgentModule","resolveEnabledSkills","streamAgentUIMessages","registry","compileAgentModule","resolveEnabledSkills","streamAgentUIMessages"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/auth/nonce.ts"],"sourcesContent":["/**\n * T4.1 — Per-request CSP nonce generation.\n *\n * Returns a 22–24 character base64-encoded string carrying 16 bytes of\n * cryptographic entropy. Used by the SSR pipeline to nonce the inline\n * hydration data script so the default CSP can drop `'unsafe-inline'`\n * from `script-src` in 0.3.0.\n *\n * Runtime portability: prefers Web Crypto (`globalThis.crypto`) because\n * it is available on every target runtime (Node 19+, Bun, Deno, Vercel\n * Edge, Cloudflare Workers). Falls back to `node:crypto` for older Node\n * builds where `globalThis.crypto` is absent.\n *\n * NOT a security primitive in the cryptographic sense — the nonce is a\n * one-shot, single-request value that defeats trivial XSS injection by\n * making the attacker guess a 128-bit string per request. It is NOT\n * suitable for signing or session tokens.\n */\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n // Prefer Buffer when available (Node, Bun) — its base64 encoder is fast\n // and well-tested. In Edge runtimes that lack Buffer, fall back to the\n // standard btoa(String.fromCharCode(...)) trick.\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(bytes).toString('base64')\n }\n let binary = ''\n for (const b of bytes) {\n binary += String.fromCharCode(b)\n }\n // `btoa` exists in browsers + edge runtimes; if it doesn't, the caller\n // is on a JS runtime we don't support — let the ReferenceError bubble.\n return btoa(binary)\n}\n\n// CR-027 fix: the previous fallback path used `require('node:crypto')`,\n// which throws ReferenceError in strict ESM contexts (Bun, Deno, Vercel\n// Edge) when reached. Web Crypto has been a hard requirement since Node\n// 19; for older Node we now throw a clear error at module load time\n// instead of failing on first SSR request.\nlet cachedWebCrypto: Crypto | null | undefined\n\nfunction getWebCrypto(): Crypto {\n if (cachedWebCrypto === undefined) {\n const candidate = (globalThis as { crypto?: Crypto }).crypto\n cachedWebCrypto =\n candidate && typeof candidate.getRandomValues === 'function' ? candidate : null\n }\n if (!cachedWebCrypto) {\n throw new Error(\n 'generateNonce: Web Crypto unavailable. TheoKit requires Node.js 19+, Bun, Deno, ' +\n 'or any modern Edge runtime where `globalThis.crypto.getRandomValues` is present.',\n )\n }\n return cachedWebCrypto\n}\n\nexport function generateNonce(): string {\n const buf = new Uint8Array(16)\n getWebCrypto().getRandomValues(buf)\n return bytesToBase64(buf)\n}\n"],"mappings":";AAmBA,SAAS,cAAc,OAA2B;AAIhD,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ;AAAA,EAC7C;AACA,MAAI,SAAS;AACb,aAAW,KAAK,OAAO;AACrB,cAAU,OAAO,aAAa,CAAC;AAAA,EACjC;AAGA,SAAO,KAAK,MAAM;AACpB;AAOA,IAAI;AAEJ,SAAS,eAAuB;AAC9B,MAAI,oBAAoB,QAAW;AACjC,UAAM,YAAa,WAAmC;AACtD,sBACE,aAAa,OAAO,UAAU,oBAAoB,aAAa,YAAY;AAAA,EAC/E;AACA,MAAI,CAAC,iBAAiB;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,gBAAwB;AACtC,QAAM,MAAM,IAAI,WAAW,EAAE;AAC7B,eAAa,EAAE,gBAAgB,GAAG;AAClC,SAAO,cAAc,GAAG;AAC1B;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/services/generators/openapi-client-gen.ts","../src/services/schema.ts","../src/services/adapters-bridge/vite-proxy-builder.ts"],"sourcesContent":["/**\n * OpenAPI → TypeScript client generation (T5.1).\n *\n * Thin wrapper around `@hey-api/openapi-ts` (the de facto 2026 generator\n * used by Vercel/OpenCode/PayPal). Called by the Vite plugin\n * `services-typed-client.ts` at dev startup and when an OpenAPI URL\n * changes.\n *\n * Production behavior:\n * - For each service with a `openapi` URL, fetch the spec\n * - Run Hey API generator to write `<cwd>/clients/<service-name>.ts`\n * - On any failure: log warning, do NOT crash dev (best-effort)\n *\n * The actual `@hey-api/openapi-ts` invocation is dynamic-imported so\n * that consumers without the dep (TS-only Wave 1 apps) don't pay\n * the bundle cost.\n */\nimport type { ManifestServiceEntry } from '../adapters-bridge/manifest.js'\n\nexport interface GenerateClientOptions {\n service: ManifestServiceEntry\n /** Directory to write `<service-name>.ts` into. */\n outputDir: string\n /** Logger for warnings/info. */\n log?: (level: 'info' | 'warn' | 'error', msg: string) => void\n /** Test injection — replace fetch (so tests don't hit the network). */\n customFetch?: typeof fetch\n}\n\nexport interface GenerateClientResult {\n generated: boolean\n outputFile?: string\n skippedReason?: string\n}\n\n/**\n * Generate a typed client for one service. No-op (returns skipped=true)\n * if the service has no `openapi` URL.\n *\n * The generator runs the Hey API tool. If the tool import fails (not\n * installed), the function logs a warning and returns `{ generated: false }`.\n */\nexport async function generateTypedClient(\n options: GenerateClientOptions,\n): Promise<GenerateClientResult> {\n const log =\n options.log ??\n ((_level: 'info' | 'warn' | 'error', _msg: string) => {\n // default: silent in production; tests inject their own\n })\n const { service, outputDir } = options\n\n if (!service.openapi) {\n return { generated: false, skippedReason: 'no openapi URL declared' }\n }\n\n const f = options.customFetch ?? fetch\n let spec: unknown\n try {\n const res = await f(service.openapi)\n if (!res.ok) {\n log('warn', `[${service.name}] openapi fetch returned ${String(res.status)}; skipping`)\n return { generated: false, skippedReason: `fetch returned ${String(res.status)}` }\n }\n spec = await res.json()\n } catch (err) {\n log(\n 'warn',\n `[${service.name}] openapi fetch failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n return { generated: false, skippedReason: 'fetch failed' }\n }\n\n // Dynamic import — soft dep. We pass through a variable so the typechecker\n // doesn't resolve the literal at build time (the dep is optional;\n // consumers without it get a graceful skip).\n let createClient: unknown\n try {\n // Soft-dep resolution. We pass the module name through a String wrapper\n // call site that the typechecker cannot statically resolve — so missing\n // dep at typecheck time does not break the build. Runtime behavior is\n // identical to `import('@hey-api/openapi-ts')`.\n const segment1 = '@hey-api/'\n const segment2 = 'openapi-ts'\n const moduleName = `${segment1}${segment2}`\n const dyn: Promise<unknown> = import(moduleName).catch(() => null)\n const mod = (await dyn) as { createClient?: unknown } | null\n createClient = mod?.createClient\n } catch {\n createClient = undefined\n }\n\n if (typeof createClient !== 'function') {\n log(\n 'warn',\n `[${service.name}] @hey-api/openapi-ts not installed; typed client not generated. ` +\n `Run \\`pnpm add -D @hey-api/openapi-ts @hey-api/client-fetch\\` to enable.`,\n )\n return { generated: false, skippedReason: 'hey-api not installed' }\n }\n\n const outputFile = `${outputDir}/${service.name}.ts`\n try {\n await (createClient as (cfg: unknown) => Promise<unknown>)({\n input: spec,\n output: { path: outputDir, format: 'prettier' },\n plugins: [{ name: '@hey-api/client-fetch' }],\n })\n log('info', `[${service.name}] typed client generated at ${outputFile}`)\n return { generated: true, outputFile }\n } catch (err) {\n log(\n 'warn',\n `[${service.name}] Hey API generation failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n return { generated: false, skippedReason: 'generator threw' }\n }\n}\n","/**\n * Wave 2 — Polyglot Services Orchestration (T1.1).\n *\n * Declarative `services: {}` primitive for `theo.config.ts`. Each entry\n * declares an external sidecar process (Python FastAPI / Node Hono) that\n * boots alongside the TheoKit TS app. Empty `services: {}` is the default\n * and preserves Wave 1 behavior (no impact on TS-only apps).\n *\n * See ADRs 0012-0015 + plan: docs/plans/wave-2-polyglot-services-plan.md\n */\nimport { z } from 'zod'\n\n/** Wave 2 runtime kinds. Go/Rust/Java/Ruby/PHP archived; deferred per ADR-0012 invariant #2/#3. */\nconst ServiceRuntimeSchema = z.enum(['python', 'node'])\n\n/**\n * Service shape enum, mirrored from `services.schema.json`: server | worker.\n * Optional; emitters default to \"server\". `frontend` removed in\n * simplify-deploy-theokit-only v2.0 (D5) — TheoCloud serves no static frontends.\n */\nconst ServiceTypeSchema = z.enum(['server', 'worker'])\n\n/** EC-3 fix: names that conflict with generated docker-compose entries. */\nconst RESERVED_SERVICE_NAMES = ['web', 'caddy', 'postgres', 'redis'] as const\n\n/**\n * EC-12 fix: service names must be docker-compose-safe — lowercase,\n * start with a letter, contain only a-z 0-9 -. Reserved names rejected.\n */\nconst ServiceNameSchema = z\n .string()\n .min(1)\n .regex(\n /^[a-z][a-z0-9-]*$/,\n 'service name must be lowercase, start with letter, contain only a-z 0-9 -',\n )\n .refine((n) => !(RESERVED_SERVICE_NAMES as readonly string[]).includes(n), {\n message: `service name conflicts with reserved name (${RESERVED_SERVICE_NAMES.join('/')})`,\n })\n\n/**\n * Single service definition. All commands run from `services/<name>/` cwd.\n *\n * EC-4 fix: `proxy` regex requires NON-EMPTY path after `/` (the `+` quantifier)\n * to reject `/` which would catch-all and conflict with TheoKit's own routing.\n */\nconst ServiceDefinitionSchema = z.object({\n runtime: ServiceRuntimeSchema,\n type: ServiceTypeSchema.optional(),\n port: z.number().int().min(1).max(65535),\n proxy: z.string().regex(/^\\/[a-zA-Z0-9\\-_/]+$/, 'proxy must be a non-root path starting with /'),\n dev: z.string().min(1),\n build: z.string().optional(),\n start: z.string().min(1),\n openapi: z.url().optional(),\n healthcheck: z.string().regex(/^\\//, 'healthcheck must start with /').default('/health'),\n cors: z.boolean().default(false),\n env: z.record(z.string(), z.string()).optional(),\n dependsOn: z.array(z.string()).optional(),\n /**\n * EC-25 + ref doc §8: by default the proxy strips upstream Set-Cookie\n * to prevent the polyglot service from issuing cookies that conflict\n * with TheoKit's encrypted session. Set to true to opt-in.\n */\n passSetCookie: z.boolean().default(false),\n})\n\nexport type ServiceDefinition = z.infer<typeof ServiceDefinitionSchema>\nexport type ServicesConfig = Record<string, ServiceDefinition>\n\n/**\n * Topological-order helper used by `dependsOn` cycle detection refine.\n * Returns true iff `graph` is a DAG. Each node's deps must all reference\n * existing nodes; cycles return false.\n */\nfunction isDag(graph: Record<string, readonly string[]>): boolean {\n const WHITE = 0\n const GRAY = 1\n const BLACK = 2\n const colors: Record<string, 0 | 1 | 2> = {}\n for (const node of Object.keys(graph)) colors[node] = WHITE\n\n function visit(node: string): boolean {\n if (colors[node] === GRAY) return false // cycle\n if (colors[node] === BLACK) return true\n colors[node] = GRAY\n for (const dep of graph[node] ?? []) {\n if (!(dep in graph)) return false // missing reference\n if (!visit(dep)) return false\n }\n colors[node] = BLACK\n return true\n }\n\n for (const node of Object.keys(graph)) {\n if (colors[node] === WHITE && !visit(node)) return false\n }\n return true\n}\n\n/**\n * Full services config schema with cross-service refines.\n *\n * EC-1 fix: detect duplicate ports across services.\n * EC-13: empty `dependsOn: []` accepted as no-deps.\n * Self-dep, missing-ref, and cycles rejected via topological check.\n */\nexport const servicesConfigSchema = z\n .record(ServiceNameSchema, ServiceDefinitionSchema)\n .default({})\n // EC-1: duplicate port detection across services\n .refine(\n (s) => {\n const ports = Object.values(s).map((v) => v.port)\n return new Set(ports).size === ports.length\n },\n {\n message: 'duplicate port across services — each service must bind a unique port',\n },\n )\n // duplicate proxy prefix detection\n .refine(\n (s) => {\n const prefixes = Object.values(s).map((v) => v.proxy)\n return new Set(prefixes).size === prefixes.length\n },\n { message: 'duplicate proxy prefix across services' },\n )\n // dependsOn correctness (self-dep, missing reference, cycle)\n .refine(\n (s) => {\n // Build graph\n const graph: Record<string, readonly string[]> = {}\n for (const [name, def] of Object.entries(s)) {\n graph[name] = def.dependsOn ?? []\n }\n // Self-dep: a service mentioning itself is rejected as part of cycle detection\n for (const [name, deps] of Object.entries(graph)) {\n if (deps.includes(name)) return false\n }\n return isDag(graph)\n },\n {\n message: 'invalid dependsOn — must reference existing services, no self-deps, no cycles',\n },\n )\n\nexport type ServicesConfigInput = z.input<typeof servicesConfigSchema>\nexport type ServicesConfigOutput = z.output<typeof servicesConfigSchema>\n","/**\n * Builds a Vite `server.proxy` configuration from TheoKit's\n * declarative `services: {}` config (T2.1).\n *\n * Pure function — no Vite imports, just data shaping. The Vite plugin\n * (services-dev.ts) calls this and merges the result into `vite.config.server.proxy`.\n *\n * Pattern follows Vite proxy reference (referencias/vite/packages/vite/src/node/server/middlewares/proxy.ts):\n * - Each path prefix becomes its own entry\n * - `changeOrigin: true` set by default (matches Vite's string-shortcut behavior)\n * - User-set entries take precedence on prefix collision (we never clobber)\n */\nimport type { ServicesConfig } from '../schema.js'\n\n/**\n * Shape compatible with Vite's `server.proxy` type. We intentionally don't\n * import Vite's `ProxyOptions` so this module stays platform-neutral and\n * unit-testable without Vite.\n */\nexport interface ViteProxyEntry {\n target: string\n changeOrigin: boolean\n rewrite?: (path: string) => string\n}\n\nexport type ViteProxyConfig = Record<string, string | ViteProxyEntry>\n\nexport function buildServicesProxyConfig(\n services: ServicesConfig,\n userProxy: ViteProxyConfig = {},\n): ViteProxyConfig {\n const fromServices: ViteProxyConfig = {}\n for (const [, def] of Object.entries(services)) {\n const prefix = def.proxy\n fromServices[prefix] = {\n target: `http://localhost:${String(def.port)}`,\n changeOrigin: true,\n // Strip the service's `proxy` prefix from the upstream URL so the\n // sidecar receives its own native paths (e.g. `/api/agent/echo` →\n // `/echo`). The sidecar declares routes against its own root path —\n // it doesn't know about the TheoKit proxy prefix.\n rewrite: (path: string) =>\n path.startsWith(prefix) ? path.slice(prefix.length) || '/' : path,\n }\n }\n // User proxy wins on collision (we add services first, then spread user on top)\n return { ...fromServices, ...userProxy }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/define/define-agent-tool.ts"],"sourcesContent":["import { z } from 'zod'\n\n/**\n * Item #4 — `defineAgentTool`\n *\n * Sugar over the `@theokit/sdk` `CustomTool` contract. Takes a Zod schema +\n * handler and produces a structurally-compatible `CustomTool` that\n * `Agent.create({ tools: [...] })` accepts.\n *\n * Uses Zod v4's native `z.toJSONSchema()` to convert the input schema to\n * JSON Schema for LLM providers.\n *\n * Handler error propagation:\n * `defineAgentTool` parses the input via the Zod schema BEFORE calling the\n * user handler. Invalid input throws a `ZodError`, which the SDK's tool-\n * dispatcher treats as a tool failure and surfaces to the model as a tool\n * error (the SDK owns the wire; ADR D3).\n */\n\n/**\n * Local mirror of the SDK's `CustomTool` interface. We don't `import type`\n * from `@theokit/sdk` because the SDK is an optional peer (consumers who\n * never call `defineAgentTool` shouldn't need it installed). The shape is\n * the wire contract; any structurally-matching object is accepted by\n * `Agent.create({ tools })`.\n *\n * @public\n */\n/**\n * M48 — the tool-handler run context, a standalone mirror of `@theokit/sdk`'s tool `ctx` (kept in\n * sync by the `tests/type/custom-tool-mirror.test-d.ts` type gate). `context` (M7) is the object\n * supplied once at agent/run level; `messages` (SE12) is a read-only transcript projection of the\n * current turn; `threadId` (#119) is the run's session identity — so a stateful tool shared across\n * sessions can scope its state per session instead of leaking it. Standalone (no `@theokit/sdk`\n * import) because the SDK is an optional peer.\n */\nexport interface ToolHandlerContext {\n signal?: AbortSignal\n context?: unknown\n messages?: readonly ToolContextMessage[]\n threadId?: string\n}\n\n/** SE12 — a read-only, text-only projection of a turn message (mirror of the SDK type). */\nexport interface ToolContextMessage {\n role: 'system' | 'user' | 'assistant'\n content: string\n}\n\nexport interface CustomTool {\n name: string\n description: string\n inputSchema: Record<string, unknown>\n /**\n * Returns `string` (theokit's `defineAgentTool` maps every result to the model-visible string).\n * The SDK's `CustomTool` handler return is WIDER (`string | ToolResultContentBlock[]`, SE7); a\n * `string`-returning handler is a valid subtype the SDK accepts — the type gate asserts\n * `mirror ⊆ SDK` for the return (covariant) and `mirror === SDK` for the `ctx` param (contravariant,\n * to catch #119 `ctx.threadId` drift). See `tests/type/custom-tool-mirror.test-d.ts`.\n */\n handler: (input: Record<string, unknown>, ctx?: ToolHandlerContext) => string | Promise<string>\n /** M18 — optional per-target formatters for the app's UI/transcript (ignored by the SDK wire). */\n transform?: ToolTransform\n}\n\n/**\n * M18 — per-target formatters. `display` shapes the rich handler result for the UI; `transcript`\n * shapes it for a saved transcript. Applied by {@link applyTransform}, never by the model wire.\n */\nexport interface ToolTransform<R = unknown> {\n display?: (result: R) => unknown\n transcript?: (result: R) => unknown\n}\n\n/**\n * Spec accepted by {@link defineAgentTool}. `inputSchema` is a Zod 3 schema\n * rooted in `z.object(...)`. The `handler` argument type is inferred via\n * `z.infer<T>`.\n *\n * @public\n */\nexport interface DefineAgentToolSpec<T extends z.ZodType, R = string> {\n /** Tool name surfaced to the LLM. Must match `^[a-zA-Z][a-zA-Z0-9_-]{0,63}$`. */\n name: string\n /** Description surfaced to the LLM. Required — drives tool-selection accuracy. */\n description: string\n /** Zod schema describing the input. Must be `z.object(...)` at the root. */\n inputSchema: T\n /**\n * Handler invoked with the parsed input and, optionally, the run `ctx` (M7). `ctx.context`\n * is the object supplied once at the agent level (`defineAgent({ context })`) or per-run —\n * read it for shared config like `projectRoot` instead of baking it into the factory.\n * `ctx.signal` is the abort signal. Optional so existing one-arg handlers keep working.\n *\n * M18 — the handler may return RICH data `R` (not just a string) when `toModelOutput` is\n * provided to map it to the model-visible string.\n */\n handler: (input: z.infer<T>, ctx?: ToolHandlerContext) => R | Promise<R>\n /**\n * M18 — map the rich handler result `R` to the string the model sees. Required (in practice)\n * when `handler` returns a non-string; absent ⇒ the handler must return a string.\n */\n toModelOutput?: (result: R) => string\n /** M18 — per-target formatters (`display` / `transcript`) for the app, applied by {@link applyTransform}. */\n transform?: ToolTransform<R>\n}\n\nconst TOOL_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/\n\nfunction isZodObject(schema: z.ZodType): boolean {\n // Refinements (`.refine`), transforms (`.transform`), and defaults wrap the\n // underlying schema — walk the chain until we hit a ZodObject (or give up).\n // Supports both zod 4 (`instanceof z.ZodObject`, `def.type === 'object'`,\n // wrappers via `def.innerType` / pipe via `def.in`) and zod 3\n // (`_def.typeName === 'ZodObject'`, wrappers via `_def.schema`/`_def.innerType`).\n let current: unknown = schema\n for (let depth = 0; depth < 10; depth++) {\n if (current instanceof z.ZodObject) return true\n const z4 = (current as { def?: { type?: string; innerType?: unknown; in?: unknown } }).def\n if (z4?.type === 'object') return true\n const z3 = (current as { _def?: { typeName?: string; schema?: unknown; innerType?: unknown } })\n ._def\n if (z3?.typeName === 'ZodObject') return true\n const next = z4?.innerType ?? z4?.in ?? z3?.schema ?? z3?.innerType\n if (next !== undefined) {\n current = next\n continue\n }\n return false\n }\n return false\n}\n\n/**\n * Build a {@link CustomTool} from a Zod 3 schema + handler.\n *\n * Behavior:\n * - Validates `name` matches the LLM tool-name regex.\n * - Requires `inputSchema` to be a `ZodObject` (Anthropic + SDK contract).\n * - Warns (not throws) if `description` is empty — empty descriptions\n * degrade LLM tool selection.\n * - Converts the Zod schema to JSON Schema 7 inline (no `$ref`s — LLMs handle\n * inline schemas more reliably).\n * - Strips the top-level `$schema` field (Anthropic rejects schemas with\n * `$schema` at root in some provider modes).\n * - Wraps the handler to parse the input via the Zod schema BEFORE invoking\n * the user code — bad LLM-supplied input throws `ZodError`, which the SDK\n * converts to `tool_result(isError)`.\n *\n * @public\n */\nexport function defineAgentTool<T extends z.ZodType, R = string>(\n spec: DefineAgentToolSpec<T, R>,\n): CustomTool {\n if (!TOOL_NAME_REGEX.test(spec.name)) {\n throw new Error(\n `defineAgentTool: name must match ${TOOL_NAME_REGEX.source}. Got: ${JSON.stringify(spec.name)}`,\n )\n }\n if (!isZodObject(spec.inputSchema)) {\n throw new Error('defineAgentTool: inputSchema must be a ZodObject (z.object({...}))')\n }\n if (spec.description.length === 0) {\n console.warn(\n `defineAgentTool(${JSON.stringify(spec.name)}): empty description degrades LLM tool selection — provide a one-sentence summary.`,\n )\n }\n\n // Zod v4 native JSON Schema conversion — replaces zod-to-json-schema dep.\n // Strip $schema (Anthropic + some providers reject it).\n const { $schema: _$schema, ...inputSchema } = z.toJSONSchema(spec.inputSchema) as Record<\n string,\n unknown\n > & {\n $schema?: unknown\n }\n\n return {\n name: spec.name,\n description: spec.description,\n inputSchema,\n handler: async (input: Record<string, unknown>, ctx?: ToolHandlerContext): Promise<string> => {\n const parsed = spec.inputSchema.parse(input)\n // M7 — forward the run ctx so the handler can read `ctx.context` (e.g. projectRoot).\n const result = await spec.handler(parsed, ctx)\n // M18 — shape the (possibly rich) result into the model-visible string.\n if (spec.toModelOutput) return spec.toModelOutput(result)\n if (typeof result !== 'string') {\n throw new Error(\n `defineAgentTool(${JSON.stringify(spec.name)}): handler returned a non-string; provide toModelOutput to map it to a string for the model.`,\n )\n }\n return result\n },\n // M18 — carry the per-target formatters for the app (ignored by the SDK wire).\n ...(spec.transform !== undefined ? { transform: spec.transform as ToolTransform } : {}),\n }\n}\n\n/**\n * M18 — apply a tool's `transform` for a target (`display` / `transcript`). Returns the formatted\n * value, or the raw `result` when the tool declares no transform for that target.\n */\nexport function applyTransform(\n tool: CustomTool,\n result: unknown,\n target: 'display' | 'transcript',\n): unknown {\n const fn = tool.transform?.[target]\n return fn ? fn(result) : result\n}\n"],"mappings":";AAAA,SAAS,SAAS;AA2GlB,IAAM,kBAAkB;AAExB,SAAS,YAAY,QAA4B;AAM/C,MAAI,UAAmB;AACvB,WAAS,QAAQ,GAAG,QAAQ,IAAI,SAAS;AACvC,QAAI,mBAAmB,EAAE,UAAW,QAAO;AAC3C,UAAM,KAAM,QAA2E;AACvF,QAAI,IAAI,SAAS,SAAU,QAAO;AAClC,UAAM,KAAM,QACT;AACH,QAAI,IAAI,aAAa,YAAa,QAAO;AACzC,UAAM,OAAO,IAAI,aAAa,IAAI,MAAM,IAAI,UAAU,IAAI;AAC1D,QAAI,SAAS,QAAW;AACtB,gBAAU;AACV;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAoBO,SAAS,gBACd,MACY;AACZ,MAAI,CAAC,gBAAgB,KAAK,KAAK,IAAI,GAAG;AACpC,UAAM,IAAI;AAAA,MACR,oCAAoC,gBAAgB,MAAM,UAAU,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,IAC/F;AAAA,EACF;AACA,MAAI,CAAC,YAAY,KAAK,WAAW,GAAG;AAClC,UAAM,IAAI,MAAM,oEAAoE;AAAA,EACtF;AACA,MAAI,KAAK,YAAY,WAAW,GAAG;AACjC,YAAQ;AAAA,MACN,mBAAmB,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,IAC9C;AAAA,EACF;AAIA,QAAM,EAAE,SAAS,UAAU,GAAG,YAAY,IAAI,EAAE,aAAa,KAAK,WAAW;AAO7E,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX,aAAa,KAAK;AAAA,IAClB;AAAA,IACA,SAAS,OAAO,OAAgC,QAA8C;AAC5F,YAAM,SAAS,KAAK,YAAY,MAAM,KAAK;AAE3C,YAAM,SAAS,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAE7C,UAAI,KAAK,cAAe,QAAO,KAAK,cAAc,MAAM;AACxD,UAAI,OAAO,WAAW,UAAU;AAC9B,cAAM,IAAI;AAAA,UACR,mBAAmB,KAAK,UAAU,KAAK,IAAI,CAAC;AAAA,QAC9C;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA;AAAA,IAEA,GAAI,KAAK,cAAc,SAAY,EAAE,WAAW,KAAK,UAA2B,IAAI,CAAC;AAAA,EACvF;AACF;AAMO,SAAS,eACd,MACA,QACA,QACS;AACT,QAAM,KAAK,KAAK,YAAY,MAAM;AAClC,SAAO,KAAK,GAAG,MAAM,IAAI;AAC3B;","names":[]}
|
package/dist/chunk-E3JH6YUM.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=chunk-E3JH6YUM.js.map
|