@theokit/sdk 4.38.0 → 4.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +237 -0
- package/dist/a2a/index.cjs +3 -3
- package/dist/a2a/index.js +1 -1
- package/dist/a2a/subagent.d.cts +1 -0
- package/dist/a2a/subagent.d.ts +1 -0
- package/dist/{agent-U3lBX-5t.d.cts → agent-D7bRK9Kg.d.cts} +70 -5
- package/dist/agent-I3EZZR6S.cjs +55 -0
- package/dist/{agent-2GZC4W2A.cjs.map → agent-I3EZZR6S.cjs.map} +1 -1
- package/dist/agent-KKZYLY5D.js +46 -0
- package/dist/{agent-3H3UKD6U.js.map → agent-KKZYLY5D.js.map} +1 -1
- package/dist/{agent-C3AEtM-W.d.ts → agent-ZCZa7YQv.d.ts} +70 -5
- package/dist/agent-session-store-N4TVKHBU.cjs +23 -0
- package/dist/agent-session-store-N4TVKHBU.cjs.map +1 -0
- package/dist/agent-session-store-RHMHP5DG.js +6 -0
- package/dist/agent-session-store-RHMHP5DG.js.map +1 -0
- package/dist/agent.d.ts +37 -1
- package/dist/auth/index.cjs +15 -15
- package/dist/auth/index.js +4 -4
- package/dist/{batch-LXSALQC3.js → batch-6KQM3EBA.js} +12 -12
- package/dist/batch-6KQM3EBA.js.map +1 -0
- package/dist/{batch-NZGN6SFV.cjs → batch-X6JKGDPK.cjs} +26 -26
- package/dist/batch-X6JKGDPK.cjs.map +1 -0
- package/dist/chunk-2RW7K6FN.cjs +34 -0
- package/dist/chunk-2RW7K6FN.cjs.map +1 -0
- package/dist/chunk-3I4B3OIX.js +105 -0
- package/dist/chunk-3I4B3OIX.js.map +1 -0
- package/dist/{chunk-KWXC6ERT.js → chunk-42IFCKQZ.js} +5 -5
- package/dist/{chunk-KWXC6ERT.js.map → chunk-42IFCKQZ.js.map} +1 -1
- package/dist/{chunk-4APWS2YF.js → chunk-4F7JQZOX.js} +9 -36
- package/dist/chunk-4F7JQZOX.js.map +1 -0
- package/dist/{chunk-VYD6BGKK.js → chunk-4JSZYSGW.js} +3 -3
- package/dist/{chunk-VYD6BGKK.js.map → chunk-4JSZYSGW.js.map} +1 -1
- package/dist/{chunk-A6GXYF2Q.js → chunk-65ONFZWM.js} +7 -6
- package/dist/chunk-65ONFZWM.js.map +1 -0
- package/dist/{chunk-SGTMDQ4O.js → chunk-6EEYRFJC.js} +5 -5
- package/dist/{chunk-SGTMDQ4O.js.map → chunk-6EEYRFJC.js.map} +1 -1
- package/dist/{chunk-M4OGSS66.cjs → chunk-6LGQECT7.cjs} +11 -11
- package/dist/{chunk-M4OGSS66.cjs.map → chunk-6LGQECT7.cjs.map} +1 -1
- package/dist/{chunk-KTXOHCAE.cjs → chunk-6NEHIHFU.cjs} +4 -4
- package/dist/{chunk-KTXOHCAE.cjs.map → chunk-6NEHIHFU.cjs.map} +1 -1
- package/dist/chunk-6YNYA7GD.cjs +103 -0
- package/dist/chunk-6YNYA7GD.cjs.map +1 -0
- package/dist/{chunk-2R6NBT5F.cjs → chunk-74D7KSBP.cjs} +11 -11
- package/dist/{chunk-2R6NBT5F.cjs.map → chunk-74D7KSBP.cjs.map} +1 -1
- package/dist/{chunk-6OIO4FYX.cjs → chunk-75DSEWTJ.cjs} +7 -7
- package/dist/{chunk-6OIO4FYX.cjs.map → chunk-75DSEWTJ.cjs.map} +1 -1
- package/dist/{chunk-3XRXORBS.cjs → chunk-7OLG7VI4.cjs} +4 -4
- package/dist/{chunk-3XRXORBS.cjs.map → chunk-7OLG7VI4.cjs.map} +1 -1
- package/dist/chunk-7U2HAAWB.cjs +199 -0
- package/dist/chunk-7U2HAAWB.cjs.map +1 -0
- package/dist/{chunk-QJQ2MAZF.cjs → chunk-7YBJZS5P.cjs} +8 -8
- package/dist/{chunk-QJQ2MAZF.cjs.map → chunk-7YBJZS5P.cjs.map} +1 -1
- package/dist/{chunk-ZFZYPLQI.js → chunk-AECV3G5Z.js} +4 -4
- package/dist/{chunk-ZFZYPLQI.js.map → chunk-AECV3G5Z.js.map} +1 -1
- package/dist/{chunk-QFWQLXCL.js → chunk-AR5MQZ3J.js} +3 -3
- package/dist/{chunk-QFWQLXCL.js.map → chunk-AR5MQZ3J.js.map} +1 -1
- package/dist/{chunk-G2F67XUR.cjs → chunk-B4YA6BRS.cjs} +13 -14
- package/dist/chunk-B4YA6BRS.cjs.map +1 -0
- package/dist/{chunk-BE3W4HYV.cjs → chunk-BCQ3E2ZG.cjs} +12 -3
- package/dist/chunk-BCQ3E2ZG.cjs.map +1 -0
- package/dist/{chunk-G7X2Y7Y2.cjs → chunk-BPN5ONSW.cjs} +182 -236
- package/dist/chunk-BPN5ONSW.cjs.map +1 -0
- package/dist/{chunk-XJ5TMOGA.js → chunk-BQDUEG4C.js} +3 -3
- package/dist/{chunk-XJ5TMOGA.js.map → chunk-BQDUEG4C.js.map} +1 -1
- package/dist/{chunk-OFWU6UKF.cjs → chunk-C7OP3FAK.cjs} +47 -11
- package/dist/chunk-C7OP3FAK.cjs.map +1 -0
- package/dist/{chunk-BUGVDCI5.cjs → chunk-CEIHCKTO.cjs} +4 -4
- package/dist/chunk-CEIHCKTO.cjs.map +1 -0
- package/dist/{chunk-3BSP7LSG.cjs → chunk-CN6UX5EV.cjs} +28 -56
- package/dist/chunk-CN6UX5EV.cjs.map +1 -0
- package/dist/{chunk-5BVBNE2F.js → chunk-D7IULBF2.js} +3 -3
- package/dist/{chunk-5BVBNE2F.js.map → chunk-D7IULBF2.js.map} +1 -1
- package/dist/{chunk-6ZGD7R27.cjs → chunk-DCXRHCZJ.cjs} +8 -75
- package/dist/chunk-DCXRHCZJ.cjs.map +1 -0
- package/dist/{chunk-YVI4YPUP.cjs → chunk-DGLLVCNB.cjs} +4 -4
- package/dist/{chunk-YVI4YPUP.cjs.map → chunk-DGLLVCNB.cjs.map} +1 -1
- package/dist/{chunk-JI34KBNC.js → chunk-DKFGUOWL.js} +3 -3
- package/dist/{chunk-JI34KBNC.js.map → chunk-DKFGUOWL.js.map} +1 -1
- package/dist/{chunk-ZET34IZE.cjs → chunk-DMTZ3FII.cjs} +5 -5
- package/dist/{chunk-ZET34IZE.cjs.map → chunk-DMTZ3FII.cjs.map} +1 -1
- package/dist/{chunk-6XZBKFQ7.js → chunk-DU7DE543.js} +3 -3
- package/dist/{chunk-6XZBKFQ7.js.map → chunk-DU7DE543.js.map} +1 -1
- package/dist/{chunk-VVMLMKYZ.cjs → chunk-DWY25VLP.cjs} +9 -9
- package/dist/{chunk-VVMLMKYZ.cjs.map → chunk-DWY25VLP.cjs.map} +1 -1
- package/dist/{chunk-A4XXV2C6.js → chunk-E2FFG33C.js} +5 -72
- package/dist/chunk-E2FFG33C.js.map +1 -0
- package/dist/{chunk-R4AMI7KD.cjs → chunk-E4IUN3JB.cjs} +15 -15
- package/dist/{chunk-R4AMI7KD.cjs.map → chunk-E4IUN3JB.cjs.map} +1 -1
- package/dist/{chunk-TKDWD3MU.cjs → chunk-ECHGWE47.cjs} +4 -4
- package/dist/{chunk-TKDWD3MU.cjs.map → chunk-ECHGWE47.cjs.map} +1 -1
- package/dist/{chunk-KL7E244K.cjs → chunk-ECW2MLEW.cjs} +4 -4
- package/dist/{chunk-KL7E244K.cjs.map → chunk-ECW2MLEW.cjs.map} +1 -1
- package/dist/{chunk-EDI7DBWO.js → chunk-ET6PHZRX.js} +3 -3
- package/dist/{chunk-EDI7DBWO.js.map → chunk-ET6PHZRX.js.map} +1 -1
- package/dist/{chunk-HLK35TD7.cjs → chunk-FRUBE44B.cjs} +24 -24
- package/dist/{chunk-HLK35TD7.cjs.map → chunk-FRUBE44B.cjs.map} +1 -1
- package/dist/{chunk-SRIF7HMN.cjs → chunk-FWBSDQZ7.cjs} +5 -5
- package/dist/{chunk-SRIF7HMN.cjs.map → chunk-FWBSDQZ7.cjs.map} +1 -1
- package/dist/{chunk-XKBJT7WI.cjs → chunk-G2Q3ZC6C.cjs} +4 -4
- package/dist/{chunk-XKBJT7WI.cjs.map → chunk-G2Q3ZC6C.cjs.map} +1 -1
- package/dist/{chunk-4MWBKBS4.cjs → chunk-G4XUXSRN.cjs} +12 -12
- package/dist/{chunk-4MWBKBS4.cjs.map → chunk-G4XUXSRN.cjs.map} +1 -1
- package/dist/{chunk-VUDH4D2M.js → chunk-GIRD3RWH.js} +282 -276
- package/dist/chunk-GIRD3RWH.js.map +1 -0
- package/dist/{chunk-BT6DFWZB.cjs → chunk-HIQFVHC6.cjs} +9 -9
- package/dist/{chunk-BT6DFWZB.cjs.map → chunk-HIQFVHC6.cjs.map} +1 -1
- package/dist/{chunk-UWEALKAN.js → chunk-HWNVAUK4.js} +3 -3
- package/dist/{chunk-UWEALKAN.js.map → chunk-HWNVAUK4.js.map} +1 -1
- package/dist/{chunk-3VKTDQMX.cjs → chunk-I53ZQHU6.cjs} +4 -4
- package/dist/{chunk-3VKTDQMX.cjs.map → chunk-I53ZQHU6.cjs.map} +1 -1
- package/dist/{chunk-A3MHE2MQ.cjs → chunk-IHFNRNTJ.cjs} +8 -8
- package/dist/{chunk-A3MHE2MQ.cjs.map → chunk-IHFNRNTJ.cjs.map} +1 -1
- package/dist/{chunk-UZBA6YYP.cjs → chunk-INSFS5GT.cjs} +540 -534
- package/dist/chunk-INSFS5GT.cjs.map +1 -0
- package/dist/{chunk-KCZW75PI.js → chunk-IVSD7L22.js} +12 -4
- package/dist/chunk-IVSD7L22.js.map +1 -0
- package/dist/{chunk-JCXIDFRI.js → chunk-J3UK2GMN.js} +3 -3
- package/dist/{chunk-JCXIDFRI.js.map → chunk-J3UK2GMN.js.map} +1 -1
- package/dist/{chunk-IBDE4R5L.cjs → chunk-J6T5LWJ4.cjs} +10 -9
- package/dist/chunk-J6T5LWJ4.cjs.map +1 -0
- package/dist/{chunk-AOZYORH7.js → chunk-JRLXEYR4.js} +21 -5
- package/dist/chunk-JRLXEYR4.js.map +1 -0
- package/dist/{chunk-EUB7YLU2.js → chunk-JZUPMTJY.js} +3 -3
- package/dist/{chunk-EUB7YLU2.js.map → chunk-JZUPMTJY.js.map} +1 -1
- package/dist/{chunk-ZK3BJ4XQ.js → chunk-KE6XKRLQ.js} +3 -3
- package/dist/{chunk-ZK3BJ4XQ.js.map → chunk-KE6XKRLQ.js.map} +1 -1
- package/dist/{chunk-4NVV3GRU.js → chunk-KGRIHEDT.js} +5 -5
- package/dist/{chunk-4NVV3GRU.js.map → chunk-KGRIHEDT.js.map} +1 -1
- package/dist/{chunk-EE24WJJR.js → chunk-KWUNN5J2.js} +5 -5
- package/dist/{chunk-EE24WJJR.js.map → chunk-KWUNN5J2.js.map} +1 -1
- package/dist/{chunk-LGCEYEU2.js → chunk-KX7JV5KM.js} +3 -3
- package/dist/{chunk-LGCEYEU2.js.map → chunk-KX7JV5KM.js.map} +1 -1
- package/dist/{chunk-2K7PRHDB.cjs → chunk-LALG2K33.cjs} +8 -8
- package/dist/{chunk-2K7PRHDB.cjs.map → chunk-LALG2K33.cjs.map} +1 -1
- package/dist/{chunk-4WIJSKFA.js → chunk-LGILIGV3.js} +3 -3
- package/dist/{chunk-4WIJSKFA.js.map → chunk-LGILIGV3.js.map} +1 -1
- package/dist/{chunk-7QDTZJRV.js → chunk-LJX2SQV2.js} +6 -6
- package/dist/{chunk-7QDTZJRV.js.map → chunk-LJX2SQV2.js.map} +1 -1
- package/dist/{chunk-ACHGT3D2.js → chunk-MC5EINBV.js} +44 -8
- package/dist/chunk-MC5EINBV.js.map +1 -0
- package/dist/{chunk-M6UUTNDT.js → chunk-ML5A5DOJ.js} +4 -4
- package/dist/{chunk-M6UUTNDT.js.map → chunk-ML5A5DOJ.js.map} +1 -1
- package/dist/{chunk-3MUIYMUG.cjs → chunk-N475ETD6.cjs} +17 -17
- package/dist/{chunk-3MUIYMUG.cjs.map → chunk-N475ETD6.cjs.map} +1 -1
- package/dist/chunk-OXNYIMZZ.js +31 -0
- package/dist/chunk-OXNYIMZZ.js.map +1 -0
- package/dist/{chunk-YCHC45BY.js → chunk-PI4RO4GR.js} +3 -3
- package/dist/{chunk-YCHC45BY.js.map → chunk-PI4RO4GR.js.map} +1 -1
- package/dist/chunk-QKE556YD.js +197 -0
- package/dist/chunk-QKE556YD.js.map +1 -0
- package/dist/{chunk-FV3YEMQ7.js → chunk-RP5HQVLD.js} +13 -14
- package/dist/chunk-RP5HQVLD.js.map +1 -0
- package/dist/chunk-SCFW5MJZ.js +100 -0
- package/dist/chunk-SCFW5MJZ.js.map +1 -0
- package/dist/{chunk-ZYOHF37V.js → chunk-SG5ZWT6C.js} +3 -3
- package/dist/{chunk-ZYOHF37V.js.map → chunk-SG5ZWT6C.js.map} +1 -1
- package/dist/{chunk-ED7ANP6W.cjs → chunk-TIMPJL5V.cjs} +4 -4
- package/dist/{chunk-ED7ANP6W.cjs.map → chunk-TIMPJL5V.cjs.map} +1 -1
- package/dist/{chunk-3XKVRLR2.js → chunk-TOTP24P6.js} +4 -4
- package/dist/{chunk-3XKVRLR2.js.map → chunk-TOTP24P6.js.map} +1 -1
- package/dist/{chunk-PG3QQLWS.cjs → chunk-USZTVNUQ.cjs} +5 -5
- package/dist/{chunk-PG3QQLWS.cjs.map → chunk-USZTVNUQ.cjs.map} +1 -1
- package/dist/{chunk-XS7SKY2F.js → chunk-V2RBCCN5.js} +3 -3
- package/dist/chunk-V2RBCCN5.js.map +1 -0
- package/dist/{chunk-D3SJLXYZ.cjs → chunk-VJYSQXN6.cjs} +10 -10
- package/dist/{chunk-D3SJLXYZ.cjs.map → chunk-VJYSQXN6.cjs.map} +1 -1
- package/dist/{chunk-MXRBNJ6D.cjs → chunk-W4WKVOOQ.cjs} +11 -11
- package/dist/{chunk-MXRBNJ6D.cjs.map → chunk-W4WKVOOQ.cjs.map} +1 -1
- package/dist/chunk-XYOHKCYG.cjs +109 -0
- package/dist/chunk-XYOHKCYG.cjs.map +1 -0
- package/dist/{chunk-UD2T7ISG.js → chunk-YP67XZES.js} +4 -4
- package/dist/{chunk-UD2T7ISG.js.map → chunk-YP67XZES.js.map} +1 -1
- package/dist/{chunk-HHUDNTZC.js → chunk-YRXY2A5L.js} +4 -4
- package/dist/{chunk-HHUDNTZC.js.map → chunk-YRXY2A5L.js.map} +1 -1
- package/dist/{chunk-6OKPJZMX.cjs → chunk-Z2CUEBXV.cjs} +25 -9
- package/dist/chunk-Z2CUEBXV.cjs.map +1 -0
- package/dist/{chunk-HI6G3SNY.js → chunk-ZYB6B7QJ.js} +72 -125
- package/dist/chunk-ZYB6B7QJ.js.map +1 -0
- package/dist/compact-session-PYEC3NZV.js +18 -0
- package/dist/{compact-session-RR3ILTES.js.map → compact-session-PYEC3NZV.js.map} +1 -1
- package/dist/compact-session-VVT6IBJG.cjs +55 -0
- package/dist/{compact-session-YOWJJPPB.cjs.map → compact-session-VVT6IBJG.cjs.map} +1 -1
- package/dist/compaction.cjs +16 -16
- package/dist/compaction.js +3 -3
- package/dist/concurrency.cjs +6 -6
- package/dist/concurrency.js +4 -4
- package/dist/context-2FZSC6VQ.cjs +22 -0
- package/dist/{context-Q6ISOCN2.cjs.map → context-2FZSC6VQ.cjs.map} +1 -1
- package/dist/context-ATJJAFOS.js +5 -0
- package/dist/{context-ZDBBPIHY.js.map → context-ATJJAFOS.js.map} +1 -1
- package/dist/{cron-ChQ6zcW2.d.ts → cron-Bx7tzm5R.d.ts} +54 -2
- package/dist/{cron-BdXOZco9.d.cts → cron-D-DVjGQe.d.cts} +54 -2
- package/dist/cron.cjs +33 -29
- package/dist/cron.d.cts +3 -3
- package/dist/cron.d.ts +3 -3
- package/dist/cron.js +32 -28
- package/dist/{errors-BUVjJGJA.d.cts → errors-DMY-gLSK.d.cts} +1 -1
- package/dist/{errors-CZIUKLHt.d.ts → errors-DjBQ4Tn8.d.ts} +1 -1
- package/dist/errors.cjs +21 -21
- package/dist/errors.d.cts +2 -2
- package/dist/errors.d.ts +2 -2
- package/dist/errors.js +2 -2
- package/dist/eval.cjs +41 -36
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.js +37 -32
- package/dist/eval.js.map +1 -1
- package/dist/{executor-DPCBSI35.cjs → executor-BMRICN46.cjs} +26 -25
- package/dist/executor-BMRICN46.cjs.map +1 -0
- package/dist/{executor-ASDDIM2X.js → executor-T65PPOPT.js} +10 -9
- package/dist/executor-T65PPOPT.js.map +1 -0
- package/dist/filesystem/index.cjs +7 -7
- package/dist/filesystem/index.js +3 -3
- package/dist/fs-session-store-J5MCFFUM.cjs +19 -0
- package/dist/{fs-session-store-T5ZLQX42.cjs.map → fs-session-store-J5MCFFUM.cjs.map} +1 -1
- package/dist/fs-session-store-O6FNHECD.js +10 -0
- package/dist/{fs-session-store-K5QUPNJZ.js.map → fs-session-store-O6FNHECD.js.map} +1 -1
- package/dist/generate-object-UF6WIZLF.cjs +20 -0
- package/dist/{generate-object-F56VIZ6U.cjs.map → generate-object-UF6WIZLF.cjs.map} +1 -1
- package/dist/generate-object-XDCBYVK3.js +7 -0
- package/dist/{generate-object-LPSO6GVW.js.map → generate-object-XDCBYVK3.js.map} +1 -1
- package/dist/index-manager-N7RQYUKT.cjs +20 -0
- package/dist/{index-manager-DBJ55ZSO.cjs.map → index-manager-N7RQYUKT.cjs.map} +1 -1
- package/dist/index-manager-ZVZUHPP7.js +11 -0
- package/dist/{index-manager-F2H2RCZU.js.map → index-manager-ZVZUHPP7.js.map} +1 -1
- package/dist/index.cjs +138 -132
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +167 -9
- package/dist/index.d.ts +167 -9
- package/dist/index.js +47 -44
- package/dist/index.js.map +1 -1
- package/dist/{inject-session-RKNLUE72.js → inject-session-GQMTDZUN.js} +7 -6
- package/dist/inject-session-GQMTDZUN.js.map +1 -0
- package/dist/{inject-session-25EC74WV.cjs → inject-session-LFCYVSLJ.cjs} +9 -8
- package/dist/inject-session-LFCYVSLJ.cjs.map +1 -0
- package/dist/internal/agent-loop/loop-thinking-steps.d.ts +28 -0
- package/dist/internal/agent-loop/message-builders.d.ts +12 -3
- package/dist/internal/diagnostics.d.ts +24 -11
- package/dist/internal/llm/anthropic-shared.d.ts +4 -4
- package/dist/internal/llm/types.d.ts +49 -9
- package/dist/internal/local-agent/mcp-pool.d.ts +7 -3
- package/dist/internal/memory/adapters/azure-openai-embedding.d.cts +2 -0
- package/dist/internal/memory/adapters/catalog.d.cts +1 -0
- package/dist/internal/memory/adapters/cohere-embedding.d.cts +2 -0
- package/dist/internal/memory/adapters/deepinfra-embedding.d.cts +2 -0
- package/dist/internal/memory/adapters/gemini-embedding.d.cts +2 -0
- package/dist/internal/memory/adapters/index.cjs +28 -0
- package/dist/internal/memory/adapters/index.cjs.map +1 -0
- package/dist/internal/memory/adapters/index.d.cts +16 -0
- package/dist/internal/memory/adapters/index.d.ts +16 -0
- package/dist/internal/memory/adapters/index.js +11 -0
- package/dist/internal/memory/adapters/index.js.map +1 -0
- package/dist/internal/memory/adapters/jina-embedding.d.cts +2 -0
- package/dist/internal/memory/adapters/mistral-embedding.d.cts +2 -0
- package/dist/internal/memory/adapters/ollama-embedding.d.cts +34 -0
- package/dist/internal/memory/adapters/openai-compatible.d.cts +51 -0
- package/dist/internal/memory/adapters/openai-compatible.d.ts +28 -0
- package/dist/internal/memory/adapters/openai-embedding.d.cts +2 -0
- package/dist/internal/memory/adapters/openrouter-embedding.d.cts +2 -0
- package/dist/internal/memory/adapters/voyage-embedding.d.cts +2 -0
- package/dist/internal/memory/dreaming/diary.d.ts +4 -0
- package/dist/internal/memory/dreaming/phases.d.ts +15 -0
- package/dist/internal/memory/dreaming/run.d.ts +10 -0
- package/dist/internal/memory/migrate-sqlite-to-lance.d.ts +15 -0
- package/dist/internal/memory/sdk-memory-peer-loader.d.ts +21 -0
- package/dist/internal/persistence/index.cjs +25 -25
- package/dist/internal/persistence/index.js +6 -6
- package/dist/internal/persistence/session-transcript.d.cts +6 -0
- package/dist/internal/persistence/session-transcript.d.ts +6 -0
- package/dist/internal/runtime/concurrency/subagent-credentials.d.ts +35 -0
- package/dist/internal/security/index.cjs +13 -13
- package/dist/internal/security/index.js +3 -3
- package/dist/internal/session/agent-session-store.d.ts +1 -0
- package/dist/internal/session/session-types.d.ts +6 -1
- package/dist/judge-call-NA2G6ZMJ.js +5 -0
- package/dist/{judge-call-3S3T6O2Z.js.map → judge-call-NA2G6ZMJ.js.map} +1 -1
- package/dist/judge-call-U2D33JOW.cjs +22 -0
- package/dist/{judge-call-XIQJ5GIE.cjs.map → judge-call-U2D33JOW.cjs.map} +1 -1
- package/dist/models.cjs +20 -20
- package/dist/models.js +5 -5
- package/dist/path-safety.cjs +10 -10
- package/dist/path-safety.js +3 -3
- package/dist/persistence.cjs +35 -35
- package/dist/persistence.js +7 -7
- package/dist/project.cjs +5 -5
- package/dist/project.js +3 -3
- package/dist/registry-CAS6Y5ZY.js +9 -0
- package/dist/{registry-KMI5RA22.js.map → registry-CAS6Y5ZY.js.map} +1 -1
- package/dist/registry-JSDI4WKA.cjs +46 -0
- package/dist/{registry-BA3SLAN7.cjs.map → registry-JSDI4WKA.cjs.map} +1 -1
- package/dist/retry.cjs +4 -4
- package/dist/retry.js +3 -3
- package/dist/{run-vJHg2eGw.d.cts → run-i2-fUohR.d.cts} +16 -0
- package/dist/{run-vJHg2eGw.d.ts → run-i2-fUohR.d.ts} +16 -0
- package/dist/sandbox/index.cjs +6 -6
- package/dist/sandbox/index.js +2 -2
- package/dist/server/errors-envelope.cjs +14 -14
- package/dist/server/errors-envelope.d.cts +3 -3
- package/dist/server/errors-envelope.d.ts +3 -3
- package/dist/server/errors-envelope.js +3 -3
- package/dist/session-transcript-3B52DKTG.js +5 -0
- package/dist/{session-transcript-MMPWCESR.js.map → session-transcript-3B52DKTG.js.map} +1 -1
- package/dist/session-transcript-H7NUFXXM.cjs +50 -0
- package/dist/{session-transcript-DFGX6WNL.cjs.map → session-transcript-H7NUFXXM.cjs.map} +1 -1
- package/dist/skills.cjs +7 -7
- package/dist/skills.js +5 -5
- package/dist/stream-object-4FDU76YW.cjs +20 -0
- package/dist/{stream-object-YPXZ4F5P.cjs.map → stream-object-4FDU76YW.cjs.map} +1 -1
- package/dist/stream-object-HWDEEQN4.js +7 -0
- package/dist/{stream-object-AFERZU63.js.map → stream-object-HWDEEQN4.js.map} +1 -1
- package/dist/subagents-loader-A4W6C3QP.cjs +15 -0
- package/dist/subagents-loader-A4W6C3QP.cjs.map +1 -0
- package/dist/subagents-loader-X33KH2SZ.js +6 -0
- package/dist/subagents-loader-X33KH2SZ.js.map +1 -0
- package/dist/subagents-loader.cjs +6 -6
- package/dist/subagents-loader.d.cts +2 -2
- package/dist/subagents-loader.d.ts +2 -2
- package/dist/subagents-loader.js +4 -4
- package/dist/subscription/index.cjs +3 -3
- package/dist/subscription/index.js +2 -2
- package/dist/task-store.cjs +7 -7
- package/dist/task-store.js +4 -4
- package/dist/types/agent.d.ts +68 -3
- package/dist/types/conversation.d.ts +7 -0
- package/dist/types/messages.d.ts +9 -0
- package/dist/types/session-message.d.ts +59 -0
- package/dist/types/workflow.d.ts +36 -0
- package/dist/workflow.cjs +27 -27
- package/dist/workflow.d.cts +17 -1
- package/dist/workflow.d.ts +17 -1
- package/dist/workflow.js +6 -6
- package/docs/harness-capability-map.md +22 -0
- package/package.json +11 -1
- package/dist/agent-2GZC4W2A.cjs +0 -51
- package/dist/agent-3H3UKD6U.js +0 -42
- package/dist/batch-LXSALQC3.js.map +0 -1
- package/dist/batch-NZGN6SFV.cjs.map +0 -1
- package/dist/chunk-3BSP7LSG.cjs.map +0 -1
- package/dist/chunk-4APWS2YF.js.map +0 -1
- package/dist/chunk-6OKPJZMX.cjs.map +0 -1
- package/dist/chunk-6ZGD7R27.cjs.map +0 -1
- package/dist/chunk-A4XXV2C6.js.map +0 -1
- package/dist/chunk-A6GXYF2Q.js.map +0 -1
- package/dist/chunk-ACHGT3D2.js.map +0 -1
- package/dist/chunk-AOZYORH7.js.map +0 -1
- package/dist/chunk-BE3W4HYV.cjs.map +0 -1
- package/dist/chunk-BUGVDCI5.cjs.map +0 -1
- package/dist/chunk-FV3YEMQ7.js.map +0 -1
- package/dist/chunk-G2F67XUR.cjs.map +0 -1
- package/dist/chunk-G7X2Y7Y2.cjs.map +0 -1
- package/dist/chunk-HI6G3SNY.js.map +0 -1
- package/dist/chunk-IBDE4R5L.cjs.map +0 -1
- package/dist/chunk-KCZW75PI.js.map +0 -1
- package/dist/chunk-OFWU6UKF.cjs.map +0 -1
- package/dist/chunk-UZBA6YYP.cjs.map +0 -1
- package/dist/chunk-VUDH4D2M.js.map +0 -1
- package/dist/chunk-XS7SKY2F.js.map +0 -1
- package/dist/compact-session-RR3ILTES.js +0 -17
- package/dist/compact-session-YOWJJPPB.cjs +0 -54
- package/dist/context-Q6ISOCN2.cjs +0 -22
- package/dist/context-ZDBBPIHY.js +0 -5
- package/dist/executor-ASDDIM2X.js.map +0 -1
- package/dist/executor-DPCBSI35.cjs.map +0 -1
- package/dist/fs-session-store-K5QUPNJZ.js +0 -10
- package/dist/fs-session-store-T5ZLQX42.cjs +0 -19
- package/dist/generate-object-F56VIZ6U.cjs +0 -20
- package/dist/generate-object-LPSO6GVW.js +0 -7
- package/dist/index-manager-DBJ55ZSO.cjs +0 -19
- package/dist/index-manager-F2H2RCZU.js +0 -10
- package/dist/inject-session-25EC74WV.cjs.map +0 -1
- package/dist/inject-session-RKNLUE72.js.map +0 -1
- package/dist/judge-call-3S3T6O2Z.js +0 -5
- package/dist/judge-call-XIQJ5GIE.cjs +0 -22
- package/dist/registry-BA3SLAN7.cjs +0 -46
- package/dist/registry-KMI5RA22.js +0 -9
- package/dist/session-transcript-DFGX6WNL.cjs +0 -50
- package/dist/session-transcript-MMPWCESR.js +0 -5
- package/dist/stream-object-AFERZU63.js +0 -7
- package/dist/stream-object-YPXZ4F5P.cjs +0 -20
|
@@ -1,73 +1,6 @@
|
|
|
1
1
|
import { sessions, transcriptKey, hydratedKeys } from './chunk-5KOW3GLE.js';
|
|
2
|
-
import {
|
|
3
|
-
import { diag } from './chunk-
|
|
4
|
-
|
|
5
|
-
// src/internal/session/agent-session-store.ts
|
|
6
|
-
function seedTranscript(prior, opts) {
|
|
7
|
-
return SessionTranscript.fromRecords(prior, opts);
|
|
8
|
-
}
|
|
9
|
-
function mapAgentTurn(steps) {
|
|
10
|
-
const assistant = {};
|
|
11
|
-
const toolResults = [];
|
|
12
|
-
const toolCalls = [];
|
|
13
|
-
for (const step of steps) {
|
|
14
|
-
if (step.type === "thinkingMessage") assistant.thinking = step.message.text;
|
|
15
|
-
else if (step.type === "assistantMessage") assistant.text = step.message.text;
|
|
16
|
-
else if (step.type === "toolCall")
|
|
17
|
-
toolCalls.push({
|
|
18
|
-
id: step.message.callId,
|
|
19
|
-
name: step.message.name,
|
|
20
|
-
input: step.message.args ?? {}
|
|
21
|
-
});
|
|
22
|
-
else
|
|
23
|
-
toolResults.push({
|
|
24
|
-
toolUseId: step.message.callId,
|
|
25
|
-
content: step.message.result,
|
|
26
|
-
isError: step.message.isError
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
if (toolCalls.length > 0) assistant.toolCalls = toolCalls;
|
|
30
|
-
return { assistant, toolResults };
|
|
31
|
-
}
|
|
32
|
-
function hasAssistantContent(a) {
|
|
33
|
-
return a.text !== void 0 || a.thinking !== void 0 || (a.toolCalls?.length ?? 0) > 0;
|
|
34
|
-
}
|
|
35
|
-
function appendConversation(transcript, conversation) {
|
|
36
|
-
for (const ct of conversation) {
|
|
37
|
-
if (ct.type !== "agentConversationTurn") continue;
|
|
38
|
-
const { assistant, toolResults } = mapAgentTurn(ct.turn.steps);
|
|
39
|
-
if (hasAssistantContent(assistant)) transcript.appendAssistantTurn(assistant);
|
|
40
|
-
if (toolResults.length > 0) transcript.appendToolResults(toolResults);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
async function readSessionMessages(store, agentId) {
|
|
44
|
-
const records = await store.readRecords(agentId);
|
|
45
|
-
return reconstructMessages(records).map(narrowToSessionMessage);
|
|
46
|
-
}
|
|
47
|
-
function partToText(p) {
|
|
48
|
-
if (p.type === "text") return p.text ?? "";
|
|
49
|
-
if (p.type === "tool_use") return `[tool call] ${p.name ?? ""}`;
|
|
50
|
-
if (p.type === "tool_result") {
|
|
51
|
-
const body = typeof p.content === "string" ? p.content : JSON.stringify(p.content);
|
|
52
|
-
return `[tool result] ${body}`;
|
|
53
|
-
}
|
|
54
|
-
return "";
|
|
55
|
-
}
|
|
56
|
-
function narrowToSessionMessage(m) {
|
|
57
|
-
const role = m.role === "user" ? "user" : "assistant";
|
|
58
|
-
const text = m.content.map(partToText).filter((s) => s.length > 0).join("\n");
|
|
59
|
-
return { role, text };
|
|
60
|
-
}
|
|
61
|
-
function deltaRecords(transcript, priorLength) {
|
|
62
|
-
return transcript.records().slice(priorLength);
|
|
63
|
-
}
|
|
64
|
-
async function persistTurn(store, loc, sessionId, turn) {
|
|
65
|
-
const prior = await store.readRecords(loc.agentId);
|
|
66
|
-
const transcript = seedTranscript(prior, { cwd: loc.cwd, sessionId, model: loc.model });
|
|
67
|
-
transcript.appendUserTurn(turn.userText);
|
|
68
|
-
appendConversation(transcript, turn.conversation);
|
|
69
|
-
await store.appendRecords(loc.agentId, deltaRecords(transcript, prior.length));
|
|
70
|
-
}
|
|
2
|
+
import { persistTurn, readSessionMessages } from './chunk-3I4B3OIX.js';
|
|
3
|
+
import { diag } from './chunk-IVSD7L22.js';
|
|
71
4
|
|
|
72
5
|
// src/internal/session/agent-session.ts
|
|
73
6
|
var pendingWrites = /* @__PURE__ */ new Map();
|
|
@@ -113,7 +46,7 @@ function persistTurnToTranscript(store, loc, sessionId, turn, onCompact) {
|
|
|
113
46
|
const count = (recordCounts.get(key) ?? 0) + 1;
|
|
114
47
|
recordCounts.set(key, count);
|
|
115
48
|
if (turn.autoCompact !== void 0) {
|
|
116
|
-
const { autoCompactIfNeeded } = await import('./compact-session-
|
|
49
|
+
const { autoCompactIfNeeded } = await import('./compact-session-PYEC3NZV.js');
|
|
117
50
|
const fired = await autoCompactIfNeeded({
|
|
118
51
|
store,
|
|
119
52
|
loc,
|
|
@@ -172,5 +105,5 @@ function enqueueSessionWrite(cwd, agentId, fn) {
|
|
|
172
105
|
}
|
|
173
106
|
|
|
174
107
|
export { appendSessionMessage, clearSession, discardSession, enqueueSessionWrite, flushSessionWrites, getSessionMessages, hydrateSession, persistTurnToTranscript };
|
|
175
|
-
//# sourceMappingURL=chunk-
|
|
176
|
-
//# sourceMappingURL=chunk-
|
|
108
|
+
//# sourceMappingURL=chunk-E2FFG33C.js.map
|
|
109
|
+
//# sourceMappingURL=chunk-E2FFG33C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/session/agent-session.ts"],"names":[],"mappings":"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","file":"chunk-E2FFG33C.js","sourcesContent":["import type { SessionStore } from \"../../types/session-store.js\";\nimport {\n type PersistTurnInput,\n persistTurn,\n readSessionMessages,\n type TranscriptLocation,\n} from \"./agent-session-store.js\";\n\n/**\n * Per-agent conversation history kept across runs (and across `Agent.resume()`\n * within the same process). Lets the fixture responder recall prior facts when\n * the user asks a follow-up question.\n *\n * SE40 — disk persistence IS the native Claude-shaped `.jsonl` transcript. The\n * in-memory cache holds the narrowed `SessionMessage[]` (user/assistant text) so\n * a send can read `priorMessages` synchronously; the whole rich turn (user +\n * assistant + tool blocks) lands on disk once per send via {@link persistTurnToTranscript}\n * (fed by `run.conversation()`). Hydration on resume reconstructs the session by\n * walking the transcript DAG (`readSessionMessages`).\n *\n * @internal\n */\n\n// `SessionMessage` lives in `./session-types.ts` (leaf types file). Re-exported\n// for back-compat with downstream importers that pulled it from here.\nexport type { SessionMessage } from \"./session-types.js\";\n\nimport type { SessionMessage } from \"./session-types.js\";\n\n// M75 — the cache lives in a leaf; see session-cache.ts for the reason (cycle broken by extraction).\nexport {\n hydratedKeys,\n invalidateSessionCache,\n sessions,\n transcriptKey,\n} from \"./session-cache.js\";\n\nimport { diag } from \"../diagnostics.js\";\nimport { hydratedKeys, sessions, transcriptKey } from \"./session-cache.js\";\n\nconst pendingWrites = new Map<string, Promise<void>>();\nconst recordCounts = new Map<string, number>();\n\n/**\n * Append a session message to the in-memory cache only. Disk persistence for the\n * whole turn happens once per send via {@link persistTurnToTranscript}; the cache\n * feeds `priorMessages` / `onBeforeSend.previousMessageCount` synchronously.\n *\n * @internal\n */\nexport function appendSessionMessage(agentId: string, message: SessionMessage): void {\n const existing = sessions.get(agentId) ?? [];\n existing.push(message);\n // `delete` + `set` reinserts at the END: a JS `Map` preserves insertion order, so the first key\n // is always the least recently touched. That is the entire LRU, with no new structure (parsimony\n // rungs 2/5 — the ordering we need is already a language guarantee).\n sessions.delete(agentId);\n sessions.set(agentId, existing);\n enforceCeiling();\n}\n\n/**\n * Ceiling on sessions kept in memory.\n *\n * The runtime only reads the **active** session; the rest is pure cache, rebuildable from the\n * on-disk transcript. 32 is deliberately generous — the primary removal path is the explicit\n * `discardSession()` at the end of the agent's life, and this ceiling is a safety net against a\n * long-lived process running hundreds of sessions (plan risk #2: a tight ceiling could evict a\n * session still referenced by an in-flight async path).\n */\nexport const MAX_CACHED_SESSIONS = 32;\n\nfunction enforceCeiling(): void {\n while (sessions.size > MAX_CACHED_SESSIONS) {\n const maisAntiga = sessions.keys().next().value;\n if (maisAntiga === undefined) return;\n sessions.delete(maisAntiga);\n esquecerEscrituracao(maisAntiga);\n }\n}\n\n/**\n * Erases the bookkeeping for an `agentId` across EVERY `cwd` it appears in.\n *\n * The three maps are keyed by `transcriptKey(cwd, agentId)`; `sessions` is the only one keyed by the\n * raw `agentId`. The ceiling's first version deleted from all three using `sessions`' key — that is,\n * deleted nothing — and left `hydratedKeys` **orphaned**. Since `hydrateSession` returns early when\n * the marker is present, an evicted session came back **empty** instead of rehydrating from disk:\n * silent amnesia, and a regression new to M95, because before it nothing evicted.\n *\n * The ceiling only knows the `agentId`, not the `cwd`, so it scans by suffix — which is the format\n * `transcriptKey` produces. A scan rather than an index because these maps hold tens of entries, not\n * thousands: a reverse index here would be new structure for a problem that does not exist.\n */\nfunction esquecerEscrituracao(agentId: string): void {\n const sufixo = transcriptKey(\"\", agentId).slice(0 - agentId.length - 2);\n for (const k of [...hydratedKeys]) if (k.endsWith(sufixo)) hydratedKeys.delete(k);\n for (const k of [...pendingWrites.keys()]) if (k.endsWith(sufixo)) pendingWrites.delete(k);\n for (const k of [...recordCounts.keys()]) if (k.endsWith(sufixo)) recordCounts.delete(k);\n}\n\n/**\n * Erases the agent's module bookkeeping and returns how many entries were removed.\n *\n * M95 — `invalidateSessionCache` limpava **dois** dos quatro mapas (`sessions`, `hydratedKeys`);\n * `pendingWrites` and `recordCounts` were never touched by id, so they grew for the life of the\n * process. Neither is large per entry — the leak is in count, not volume — but a cache with no owner\n * for removal is a cache that only grows.\n *\n * Returns the count so the caller can prove the removal; a second discard returns 0, which is what\n * makes the idempotency test possible without exposing the maps.\n */\nexport function discardSession(cwd: string, agentId: string): number {\n const key = transcriptKey(cwd, agentId);\n let removed = 0;\n // `sessions` is NOT erased here — and the distinction is measured, not aesthetic. It holds the\n // readable conversation, and there is a legitimate reader AFTER dispose: the golden\n // `two-concurrent-sends-serialize` calls `getSessionMessages(agentId)` after `agent.dispose()`.\n // Erasing it here returned an empty list and broke two goldens. What bounds it is the LRU ceiling\n // above; the three below are pure bookkeeping, with no post-dispose reader.\n //\n // The key is `transcriptKey(cwd, agentId)` in ALL THREE — not the raw `agentId`. The first version\n // erased two of them by `agentId` and therefore **never erased anything**; the test did not catch\n // it because it only asserted that the SECOND call returns 0, which is true either way.\n if (hydratedKeys.delete(key)) removed++;\n if (pendingWrites.delete(key)) removed++;\n if (recordCounts.delete(key)) removed++;\n return removed;\n}\n\nexport function getSessionMessages(agentId: string): SessionMessage[] {\n return sessions.get(agentId) ?? [];\n}\n\n/**\n * Persist a full conversation turn (user + assistant + tool blocks) to the native\n * transcript. Chained per-(agent, transcript) so on-disk order matches send order,\n * and fire-and-forget so `send()` is not blocked by disk I/O. Every\n * M50 — when the caller supplies `turn.autoCompact`, size-driven auto-compaction (usage real vs\n * the model's context window) runs in this same chain, surfaced via the optional `onCompact` observer.\n *\n * @internal\n */\nexport function persistTurnToTranscript(\n store: SessionStore,\n loc: TranscriptLocation,\n sessionId: string,\n turn: PersistTurnInput,\n onCompact?: () => void,\n): void {\n const key = transcriptKey(loc.cwd, loc.agentId);\n // PRE-EXISTING debt, exposed when M75 fixed the Biome config that used to abort before\n // sweeping these files (a nested root under refactor/). It is not new code and was not touched\n // by M75; refactoring SDK internals without review would trade a visible problem for a diff\n // arriscado. Rastreado em usetheodev/theokit-sdk#151.\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: see the reason just above\n const chained = (pendingWrites.get(key) ?? Promise.resolve()).then(async () => {\n try {\n await persistTurn(store, loc, sessionId, turn);\n const count = (recordCounts.get(key) ?? 0) + 1;\n recordCounts.set(key, count);\n // M50 — the 50-turn no-summary boundary stub is GONE (it silently amnesia'd the session).\n // Auto-compaction is now size-driven with a real summary: see `maybeAutoCompact` below,\n // invoked in this same write chain by the post-run lifecycle when usage is known.\n if (turn.autoCompact !== undefined) {\n const { autoCompactIfNeeded } = await import(\"./compact-session.js\");\n const fired = await autoCompactIfNeeded({\n store,\n loc,\n sessionId,\n usageTotal: turn.autoCompact.usageTotal,\n contextWindow: turn.autoCompact.contextWindow,\n turnCount: count,\n summarize: turn.autoCompact.summarize,\n });\n if (fired) onCompact?.();\n }\n } catch (cause) {\n const msg = cause instanceof Error ? cause.message : String(cause);\n diag(`[theokit-sdk] session transcript write failed (${loc.agentId}): ${msg}\\n`);\n }\n });\n pendingWrites.set(\n key,\n chained.then(\n () => undefined,\n () => undefined,\n ),\n );\n}\n\n/**\n * Load the persisted transcript into the in-memory cache. Idempotent per\n * (baseDir, cwd, agentId). Call once per agent lifecycle before the first read.\n *\n * @internal\n */\nexport async function hydrateSession(\n agentId: string,\n loc: { store: SessionStore; cwd: string },\n): Promise<void> {\n const key = transcriptKey(loc.cwd, agentId);\n if (hydratedKeys.has(key)) return;\n hydratedKeys.add(key);\n\n const persisted = await readSessionMessages(loc.store, agentId);\n if (persisted.length === 0) return;\n // M51 review F4 — the DISK is the source of truth at hydration time: after an invalidation\n // (compact/inject), an in-flight turn may have repopulated the cache with a SINGLE message before\n // this hydrate ran; the old \"skip when non-empty\" guard then pinned the parent to a 1-message\n // context (history + injected pair lost until restart). The persist chain serializes writes, so\n // the disk already contains that in-flight turn — replacing is always correct.\n sessions.set(agentId, persisted);\n}\n\n/**\n * Wait for all pending transcript writes to settle. Used by tests and by the\n * agent dispose path so on-disk state matches in-memory before the caller proceeds.\n *\n * @internal\n */\nexport async function flushSessionWrites(): Promise<void> {\n while (pendingWrites.size > 0) {\n const all = Array.from(pendingWrites.values());\n pendingWrites.clear();\n await Promise.all(all);\n }\n}\n\nexport function clearSession(agentId: string): void {\n sessions.delete(agentId);\n}\n\n/**\n * M50 review F1 — drop BOTH the message cache and the hydration marker so the NEXT send re-hydrates\n * from disk. Without this, a compaction only helps after a process restart: the live process keeps\n * sending the full pre-compact history (the cache is read synchronously by every send).\n */\n/**\n * M50 review F5 — run `fn` serialized on the SAME per-(cwd,agentId) write chain the per-turn\n * persistence uses, so a manual `Agent.compact` can never interleave with an in-flight turn's\n * writes (boundary landing mid-turn would orphan the turn from the replay).\n */\nexport function enqueueSessionWrite<T>(\n cwd: string,\n agentId: string,\n fn: () => Promise<T>,\n): Promise<T> {\n const key = transcriptKey(cwd, agentId);\n const prior = pendingWrites.get(key) ?? Promise.resolve();\n const result = prior.then(fn);\n pendingWrites.set(\n key,\n result.then(\n () => undefined,\n () => undefined,\n ),\n );\n return result;\n}\n\n/** Test-only: drop every cached session and hydration marker. @internal */\nexport function clearAllSessions(): void {\n sessions.clear();\n hydratedKeys.clear();\n recordCounts.clear();\n // M50 review F11 — the auto-compact attempt marks live on globalThis; tests reset them here.\n const g = globalThis as unknown as Record<symbol, Map<string, number>>;\n const sym = Symbol.for(\"theokit-sdk.session.auto-compact-attempts\");\n g[sym]?.clear();\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
3
|
+
var chunk7YBJZS5P_cjs = require('./chunk-7YBJZS5P.cjs');
|
|
4
|
+
var chunkG2Q3ZC6C_cjs = require('./chunk-G2Q3ZC6C.cjs');
|
|
5
|
+
var chunkIHFNRNTJ_cjs = require('./chunk-IHFNRNTJ.cjs');
|
|
6
6
|
var promises = require('fs/promises');
|
|
7
7
|
var path = require('path');
|
|
8
8
|
|
|
@@ -28,16 +28,16 @@ function stripSkillFrontmatter(raw) {
|
|
|
28
28
|
function extractAndParseFrontmatter(raw, fallbackName) {
|
|
29
29
|
const match = /^---\s*\n([\s\S]*?)\n---\s*\n/.exec(raw);
|
|
30
30
|
if (match === null) {
|
|
31
|
-
throw new
|
|
31
|
+
throw new chunkIHFNRNTJ_cjs.ConfigurationError(`Skill ${fallbackName} is missing frontmatter`, {
|
|
32
32
|
code: "missing_frontmatter"
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
const frontmatter = match[1] ?? "";
|
|
36
36
|
try {
|
|
37
|
-
return toStringFields(
|
|
37
|
+
return toStringFields(chunkG2Q3ZC6C_cjs.parseSimpleYaml(frontmatter));
|
|
38
38
|
} catch (cause) {
|
|
39
39
|
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
40
|
-
throw new
|
|
40
|
+
throw new chunkIHFNRNTJ_cjs.ConfigurationError(
|
|
41
41
|
`Skill ${fallbackName} has malformed YAML frontmatter: ${detail}`,
|
|
42
42
|
{ code: "schema_invalid", cause }
|
|
43
43
|
);
|
|
@@ -46,13 +46,13 @@ function extractAndParseFrontmatter(raw, fallbackName) {
|
|
|
46
46
|
function resolveName(fields, fallbackName) {
|
|
47
47
|
if (hasContent(fields.name)) return fields.name;
|
|
48
48
|
if (hasContent(fallbackName)) return fallbackName;
|
|
49
|
-
throw new
|
|
49
|
+
throw new chunkIHFNRNTJ_cjs.ConfigurationError("Skill at unknown path is missing required field: name", {
|
|
50
50
|
code: "schema_invalid"
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
function ensureRequiredFields(fields, name) {
|
|
54
54
|
if (!hasContent(fields.description)) {
|
|
55
|
-
throw new
|
|
55
|
+
throw new chunkIHFNRNTJ_cjs.ConfigurationError(`Skill ${name} is missing required field: description`, {
|
|
56
56
|
code: "schema_invalid"
|
|
57
57
|
});
|
|
58
58
|
}
|
|
@@ -60,7 +60,7 @@ function ensureRequiredFields(fields, name) {
|
|
|
60
60
|
function buildFrontmatter(fields, name) {
|
|
61
61
|
const description = fields.description;
|
|
62
62
|
if (description === void 0) {
|
|
63
|
-
throw new
|
|
63
|
+
throw new chunkIHFNRNTJ_cjs.ConfigurationError(`Skill ${name} missing description`, { code: "schema_invalid" });
|
|
64
64
|
}
|
|
65
65
|
const result = { name, description };
|
|
66
66
|
if (hasContent(fields.category)) result.category = fields.category;
|
|
@@ -81,7 +81,7 @@ function hasContent(value) {
|
|
|
81
81
|
async function discoverSkills(dir, options) {
|
|
82
82
|
let entries;
|
|
83
83
|
try {
|
|
84
|
-
entries = await
|
|
84
|
+
entries = await chunkG2Q3ZC6C_cjs.readWorkspaceDir(dir, "skills_read_error", "skills directory");
|
|
85
85
|
} catch {
|
|
86
86
|
return [];
|
|
87
87
|
}
|
|
@@ -90,8 +90,8 @@ async function discoverSkills(dir, options) {
|
|
|
90
90
|
if (!entry.isDirectory()) continue;
|
|
91
91
|
let skillDir;
|
|
92
92
|
try {
|
|
93
|
-
skillDir =
|
|
94
|
-
|
|
93
|
+
skillDir = chunk7YBJZS5P_cjs.safePathJoin(dir, entry.name);
|
|
94
|
+
chunk7YBJZS5P_cjs.assertNoSymlinkEscape(skillDir, dir);
|
|
95
95
|
} catch {
|
|
96
96
|
continue;
|
|
97
97
|
}
|
|
@@ -119,7 +119,7 @@ function tryParseSkill(raw, fallbackName, source, options) {
|
|
|
119
119
|
if (frontmatter.dependencies !== void 0) skill.dependencies = frontmatter.dependencies;
|
|
120
120
|
return skill;
|
|
121
121
|
} catch (cause) {
|
|
122
|
-
if (cause instanceof
|
|
122
|
+
if (cause instanceof chunkIHFNRNTJ_cjs.ConfigurationError) {
|
|
123
123
|
options?.onInvalidSkill?.({
|
|
124
124
|
name: fallbackName,
|
|
125
125
|
source,
|
|
@@ -150,5 +150,5 @@ exports.buildSkillsBlock = buildSkillsBlock;
|
|
|
150
150
|
exports.discoverSkills = discoverSkills;
|
|
151
151
|
exports.escapeBlockBody = escapeBlockBody;
|
|
152
152
|
exports.stripSkillFrontmatter = stripSkillFrontmatter;
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
154
|
-
//# sourceMappingURL=chunk-
|
|
153
|
+
//# sourceMappingURL=chunk-E4IUN3JB.cjs.map
|
|
154
|
+
//# sourceMappingURL=chunk-E4IUN3JB.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/runtime/skills/skill-frontmatter.ts","../src/internal/runtime/skills/discover-skills.ts","../src/internal/runtime/system-prompt/escape.ts","../src/internal/runtime/skills/skills-block.ts"],"names":["ConfigurationError","parseSimpleYaml","readWorkspaceDir","safePathJoin","assertNoSymlinkEscape","join","readFile"],"mappings":"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","file":"chunk-R4AMI7KD.cjs","sourcesContent":["import { ConfigurationError } from \"../../../errors.js\";\nimport { type FrontmatterValue, parseSimpleYaml } from \"../context/yaml-frontmatter.js\";\n\ntype StringFields = Record<string, string | undefined>;\n\n/** Narrow a FrontmatterValue to string; non-strings + undefined → undefined. */\nfunction asString(v: FrontmatterValue | undefined): string | undefined {\n return typeof v === \"string\" ? v : undefined;\n}\n\n/** Coerce parser output to legacy string-only shape (skill schema is all-string). */\nfunction toStringFields(raw: Record<string, FrontmatterValue | undefined>): StringFields {\n const out: StringFields = {};\n for (const [k, v] of Object.entries(raw)) out[k] = asString(v);\n return out;\n}\n\n/**\n * Strict skill frontmatter schema (ADR D10).\n *\n * Required: `name`, `description`.\n * Optional: `category`, `dependencies` (comma-separated string in the\n * simple-YAML dialect — parsed to `string[]`).\n *\n * Unknown fields are ignored (forward-compat). Malformed YAML or missing\n * required fields surface as `ConfigurationError` with one of the typed\n * codes below.\n *\n * @internal\n */\nexport interface SkillFrontmatter {\n name: string;\n description: string;\n category?: string;\n dependencies?: string[];\n}\n\n/**\n * Parse a SKILL.md file body into validated frontmatter.\n *\n * @throws ConfigurationError(code: \"missing_frontmatter\") — no `---` block at file head.\n * @throws ConfigurationError(code: \"schema_invalid\") — YAML malformed OR required field missing.\n *\n * @internal\n */\nexport function parseSkillFrontmatter(raw: string, fallbackName: string): SkillFrontmatter {\n const fields = extractAndParseFrontmatter(raw, fallbackName);\n const name = resolveName(fields, fallbackName);\n ensureRequiredFields(fields, name);\n return buildFrontmatter(fields, name);\n}\n\n/**\n * SE20 — return a SKILL.md's BODY (everything after the frontmatter block), trimmed.\n * When there is no frontmatter block, the whole file is the body. Reuses the same\n * frontmatter regex as {@link parseSkillFrontmatter} (DRY).\n */\nexport function stripSkillFrontmatter(raw: string): string {\n const match = /^---\\s*\\n[\\s\\S]*?\\n---\\s*\\n/.exec(raw);\n return (match === null ? raw : raw.slice(match[0].length)).trim();\n}\n\nfunction extractAndParseFrontmatter(raw: string, fallbackName: string): StringFields {\n const match = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n/.exec(raw);\n if (match === null) {\n throw new ConfigurationError(`Skill ${fallbackName} is missing frontmatter`, {\n code: \"missing_frontmatter\",\n });\n }\n const frontmatter = match[1] ?? \"\";\n // EC-5: guard against syntactically invalid frontmatter so the loader\n // surfaces schema_invalid rather than crashing.\n try {\n return toStringFields(parseSimpleYaml(frontmatter));\n } catch (cause) {\n const detail = cause instanceof Error ? cause.message : String(cause);\n throw new ConfigurationError(\n `Skill ${fallbackName} has malformed YAML frontmatter: ${detail}`,\n { code: \"schema_invalid\", cause },\n );\n }\n}\n\nfunction resolveName(fields: StringFields, fallbackName: string): string {\n if (hasContent(fields.name)) return fields.name;\n if (hasContent(fallbackName)) return fallbackName;\n throw new ConfigurationError(\"Skill at unknown path is missing required field: name\", {\n code: \"schema_invalid\",\n });\n}\n\nfunction ensureRequiredFields(fields: StringFields, name: string): void {\n if (!hasContent(fields.description)) {\n throw new ConfigurationError(`Skill ${name} is missing required field: description`, {\n code: \"schema_invalid\",\n });\n }\n}\n\nfunction buildFrontmatter(fields: StringFields, name: string): SkillFrontmatter {\n const description = fields.description;\n if (description === undefined) {\n // ensureRequiredFields already threw; this is unreachable but satisfies TS\n throw new ConfigurationError(`Skill ${name} missing description`, { code: \"schema_invalid\" });\n }\n const result: SkillFrontmatter = { name, description };\n if (hasContent(fields.category)) result.category = fields.category;\n const deps = parseDependencies(fields.dependencies);\n if (deps !== undefined) result.dependencies = deps;\n return result;\n}\n\nfunction parseDependencies(raw: string | undefined): string[] | undefined {\n if (!hasContent(raw)) return undefined;\n const deps = (raw as string)\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return deps.length > 0 ? deps : undefined;\n}\n\nfunction hasContent(value: string | undefined): value is string {\n return value !== undefined && value.trim().length > 0;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../../errors.js\";\nimport { assertNoSymlinkEscape, safePathJoin } from \"../../security/path-guard.js\";\nimport { readWorkspaceDir } from \"../config/workspace-dir.js\";\nimport { parseSkillFrontmatter } from \"./skill-frontmatter.js\";\n\n/**\n * A discovered skill's metadata. The skill BODY is never included — only the\n * strict frontmatter fields plus the resolved `source` path.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport interface Skill {\n name: string;\n description: string;\n /** Absolute path to the discovered `SKILL.md`. */\n source: string;\n category?: string;\n dependencies?: string[];\n}\n\n/**\n * Information passed to `onInvalidSkill` when a `SKILL.md` is present but its\n * frontmatter is malformed (missing required field or invalid YAML).\n *\n * @public\n */\nexport interface InvalidSkillInfo {\n /** The skill directory name (used as the fallback skill name). */\n name: string;\n /** Absolute path to the offending `SKILL.md`. */\n source: string;\n /** Typed reason: `missing_frontmatter` or `schema_invalid`. */\n code: string;\n message: string;\n}\n\n/**\n * Options for {@link discoverSkills}.\n *\n * @public\n */\nexport interface DiscoverSkillsOptions {\n /**\n * Called once per directory that contains a `SKILL.md` with malformed\n * frontmatter. The skill is excluded from the result; discovery continues\n * (strict-frontmatter ADR / EC-5). A directory WITHOUT a `SKILL.md` is NOT a\n * malformed skill and does not trigger this callback.\n *\n * Default: no-op (a library primitive must not write to the consumer's\n * stderr by default).\n */\n onInvalidSkill?: (info: InvalidSkillInfo) => void;\n}\n\n/**\n * Discover `SKILL.md` skills under an arbitrary directory.\n *\n * For each immediate subdirectory `<dir>/<name>/` containing a `SKILL.md`, the\n * file's strict YAML frontmatter is parsed (`name`/`description` required;\n * `category`/`dependencies` optional). Malformed skills are skipped (optionally\n * reported via {@link DiscoverSkillsOptions.onInvalidSkill}); a subdirectory\n * whose realpath escapes `dir` (via symlink) is skipped (symlink-escape guard,\n * reusing `@theokit/sdk/path-safety`).\n *\n * NEVER throws: a missing, unreadable, or non-directory `dir` yields `[]`.\n *\n * Discovery order follows the filesystem `readdir` order (OS-dependent). Sort\n * the result before {@link buildSkillsBlock} if a stable block order matters.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport async function discoverSkills(\n dir: string,\n options?: DiscoverSkillsOptions,\n): Promise<Skill[]> {\n let entries: Awaited<ReturnType<typeof readWorkspaceDir>>;\n try {\n entries = await readWorkspaceDir(dir, \"skills_read_error\", \"skills directory\");\n } catch {\n // never-throw contract: unreadable / not-a-directory → no skills (EC-1)\n return [];\n }\n\n const skills: Skill[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n let skillDir: string;\n try {\n skillDir = safePathJoin(dir, entry.name);\n assertNoSymlinkEscape(skillDir, dir);\n } catch {\n continue;\n }\n const skillPath = join(skillDir, \"SKILL.md\");\n let raw: string;\n try {\n raw = await readFile(skillPath, \"utf8\");\n } catch {\n // no SKILL.md in this subdir → not a skill, not an error (EC-2)\n continue;\n }\n const skill = tryParseSkill(raw, entry.name, skillPath, options);\n if (skill !== undefined) skills.push(skill);\n }\n return skills;\n}\n\nfunction tryParseSkill(\n raw: string,\n fallbackName: string,\n source: string,\n options: DiscoverSkillsOptions | undefined,\n): Skill | undefined {\n try {\n const frontmatter = parseSkillFrontmatter(raw, fallbackName);\n const skill: Skill = {\n name: frontmatter.name,\n description: frontmatter.description,\n source,\n };\n if (frontmatter.category !== undefined) skill.category = frontmatter.category;\n if (frontmatter.dependencies !== undefined) skill.dependencies = frontmatter.dependencies;\n return skill;\n } catch (cause) {\n if (cause instanceof ConfigurationError) {\n options?.onInvalidSkill?.({\n name: fallbackName,\n source,\n code: cause.code ?? \"unknown\",\n message: cause.message,\n });\n return undefined;\n }\n throw cause;\n }\n}\n","/**\n * Block-body XML escape (ADR D9 — prompt-injection defence).\n *\n * Order matters: `&` MUST be escaped first so subsequent `<`/`>` replacements\n * do not double-encode the `&` characters they introduce.\n *\n * @internal\n */\nexport const escapeBlockBody = (s: string): string =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n","import { escapeBlockBody } from \"../system-prompt/escape.js\";\n\n/**\n * Render the `<skills>` system-prompt block from a skill list.\n *\n * Input is the structural subset `{ name, description }` — the skill BODY is\n * NOT in the type, so it cannot leak into the prompt. Both fields are passed\n * through `escapeBlockBody` to neutralise prompt-injection vectors hidden in\n * user-controlled SKILL.md frontmatter (injection-escape ADR).\n *\n * Returns `undefined` for an empty list so the caller can omit the block.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport function buildSkillsBlock(\n skills: ReadonlyArray<{ name: string; description: string }>,\n): string | undefined {\n if (skills.length === 0) return undefined;\n const lines = skills.map(\n (skill) => ` - ${escapeBlockBody(skill.name)}: ${escapeBlockBody(skill.description)}`,\n );\n return `<skills>\\n${lines.join(\"\\n\")}\\n</skills>`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/runtime/skills/skill-frontmatter.ts","../src/internal/runtime/skills/discover-skills.ts","../src/internal/runtime/system-prompt/escape.ts","../src/internal/runtime/skills/skills-block.ts"],"names":["ConfigurationError","parseSimpleYaml","readWorkspaceDir","safePathJoin","assertNoSymlinkEscape","join","readFile"],"mappings":"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","file":"chunk-E4IUN3JB.cjs","sourcesContent":["import { ConfigurationError } from \"../../../errors.js\";\nimport { type FrontmatterValue, parseSimpleYaml } from \"../context/yaml-frontmatter.js\";\n\ntype StringFields = Record<string, string | undefined>;\n\n/** Narrow a FrontmatterValue to string; non-strings + undefined → undefined. */\nfunction asString(v: FrontmatterValue | undefined): string | undefined {\n return typeof v === \"string\" ? v : undefined;\n}\n\n/** Coerce parser output to legacy string-only shape (skill schema is all-string). */\nfunction toStringFields(raw: Record<string, FrontmatterValue | undefined>): StringFields {\n const out: StringFields = {};\n for (const [k, v] of Object.entries(raw)) out[k] = asString(v);\n return out;\n}\n\n/**\n * Strict skill frontmatter schema (ADR D10).\n *\n * Required: `name`, `description`.\n * Optional: `category`, `dependencies` (comma-separated string in the\n * simple-YAML dialect — parsed to `string[]`).\n *\n * Unknown fields are ignored (forward-compat). Malformed YAML or missing\n * required fields surface as `ConfigurationError` with one of the typed\n * codes below.\n *\n * @internal\n */\nexport interface SkillFrontmatter {\n name: string;\n description: string;\n category?: string;\n dependencies?: string[];\n}\n\n/**\n * Parse a SKILL.md file body into validated frontmatter.\n *\n * @throws ConfigurationError(code: \"missing_frontmatter\") — no `---` block at file head.\n * @throws ConfigurationError(code: \"schema_invalid\") — YAML malformed OR required field missing.\n *\n * @internal\n */\nexport function parseSkillFrontmatter(raw: string, fallbackName: string): SkillFrontmatter {\n const fields = extractAndParseFrontmatter(raw, fallbackName);\n const name = resolveName(fields, fallbackName);\n ensureRequiredFields(fields, name);\n return buildFrontmatter(fields, name);\n}\n\n/**\n * SE20 — return a SKILL.md's BODY (everything after the frontmatter block), trimmed.\n * When there is no frontmatter block, the whole file is the body. Reuses the same\n * frontmatter regex as {@link parseSkillFrontmatter} (DRY).\n */\nexport function stripSkillFrontmatter(raw: string): string {\n const match = /^---\\s*\\n[\\s\\S]*?\\n---\\s*\\n/.exec(raw);\n return (match === null ? raw : raw.slice(match[0].length)).trim();\n}\n\nfunction extractAndParseFrontmatter(raw: string, fallbackName: string): StringFields {\n const match = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n/.exec(raw);\n if (match === null) {\n throw new ConfigurationError(`Skill ${fallbackName} is missing frontmatter`, {\n code: \"missing_frontmatter\",\n });\n }\n const frontmatter = match[1] ?? \"\";\n // EC-5: guard against syntactically invalid frontmatter so the loader\n // surfaces schema_invalid rather than crashing.\n try {\n return toStringFields(parseSimpleYaml(frontmatter));\n } catch (cause) {\n const detail = cause instanceof Error ? cause.message : String(cause);\n throw new ConfigurationError(\n `Skill ${fallbackName} has malformed YAML frontmatter: ${detail}`,\n { code: \"schema_invalid\", cause },\n );\n }\n}\n\nfunction resolveName(fields: StringFields, fallbackName: string): string {\n if (hasContent(fields.name)) return fields.name;\n if (hasContent(fallbackName)) return fallbackName;\n throw new ConfigurationError(\"Skill at unknown path is missing required field: name\", {\n code: \"schema_invalid\",\n });\n}\n\nfunction ensureRequiredFields(fields: StringFields, name: string): void {\n if (!hasContent(fields.description)) {\n throw new ConfigurationError(`Skill ${name} is missing required field: description`, {\n code: \"schema_invalid\",\n });\n }\n}\n\nfunction buildFrontmatter(fields: StringFields, name: string): SkillFrontmatter {\n const description = fields.description;\n if (description === undefined) {\n // ensureRequiredFields already threw; this is unreachable but satisfies TS\n throw new ConfigurationError(`Skill ${name} missing description`, { code: \"schema_invalid\" });\n }\n const result: SkillFrontmatter = { name, description };\n if (hasContent(fields.category)) result.category = fields.category;\n const deps = parseDependencies(fields.dependencies);\n if (deps !== undefined) result.dependencies = deps;\n return result;\n}\n\nfunction parseDependencies(raw: string | undefined): string[] | undefined {\n if (!hasContent(raw)) return undefined;\n const deps = (raw as string)\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return deps.length > 0 ? deps : undefined;\n}\n\nfunction hasContent(value: string | undefined): value is string {\n return value !== undefined && value.trim().length > 0;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { ConfigurationError } from \"../../../errors.js\";\nimport { assertNoSymlinkEscape, safePathJoin } from \"../../security/path-guard.js\";\nimport { readWorkspaceDir } from \"../config/workspace-dir.js\";\nimport { parseSkillFrontmatter } from \"./skill-frontmatter.js\";\n\n/**\n * A discovered skill's metadata. The skill BODY is never included — only the\n * strict frontmatter fields plus the resolved `source` path.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport interface Skill {\n name: string;\n description: string;\n /** Absolute path to the discovered `SKILL.md`. */\n source: string;\n category?: string;\n dependencies?: string[];\n}\n\n/**\n * Information passed to `onInvalidSkill` when a `SKILL.md` is present but its\n * frontmatter is malformed (missing required field or invalid YAML).\n *\n * @public\n */\nexport interface InvalidSkillInfo {\n /** The skill directory name (used as the fallback skill name). */\n name: string;\n /** Absolute path to the offending `SKILL.md`. */\n source: string;\n /** Typed reason: `missing_frontmatter` or `schema_invalid`. */\n code: string;\n message: string;\n}\n\n/**\n * Options for {@link discoverSkills}.\n *\n * @public\n */\nexport interface DiscoverSkillsOptions {\n /**\n * Called once per directory that contains a `SKILL.md` with malformed\n * frontmatter. The skill is excluded from the result; discovery continues\n * (strict-frontmatter ADR / EC-5). A directory WITHOUT a `SKILL.md` is NOT a\n * malformed skill and does not trigger this callback.\n *\n * Default: no-op (a library primitive must not write to the consumer's\n * stderr by default).\n */\n onInvalidSkill?: (info: InvalidSkillInfo) => void;\n}\n\n/**\n * Discover `SKILL.md` skills under an arbitrary directory.\n *\n * For each immediate subdirectory `<dir>/<name>/` containing a `SKILL.md`, the\n * file's strict YAML frontmatter is parsed (`name`/`description` required;\n * `category`/`dependencies` optional). Malformed skills are skipped (optionally\n * reported via {@link DiscoverSkillsOptions.onInvalidSkill}); a subdirectory\n * whose realpath escapes `dir` (via symlink) is skipped (symlink-escape guard,\n * reusing `@theokit/sdk/path-safety`).\n *\n * NEVER throws: a missing, unreadable, or non-directory `dir` yields `[]`.\n *\n * Discovery order follows the filesystem `readdir` order (OS-dependent). Sort\n * the result before {@link buildSkillsBlock} if a stable block order matters.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport async function discoverSkills(\n dir: string,\n options?: DiscoverSkillsOptions,\n): Promise<Skill[]> {\n let entries: Awaited<ReturnType<typeof readWorkspaceDir>>;\n try {\n entries = await readWorkspaceDir(dir, \"skills_read_error\", \"skills directory\");\n } catch {\n // never-throw contract: unreadable / not-a-directory → no skills (EC-1)\n return [];\n }\n\n const skills: Skill[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n let skillDir: string;\n try {\n skillDir = safePathJoin(dir, entry.name);\n assertNoSymlinkEscape(skillDir, dir);\n } catch {\n continue;\n }\n const skillPath = join(skillDir, \"SKILL.md\");\n let raw: string;\n try {\n raw = await readFile(skillPath, \"utf8\");\n } catch {\n // no SKILL.md in this subdir → not a skill, not an error (EC-2)\n continue;\n }\n const skill = tryParseSkill(raw, entry.name, skillPath, options);\n if (skill !== undefined) skills.push(skill);\n }\n return skills;\n}\n\nfunction tryParseSkill(\n raw: string,\n fallbackName: string,\n source: string,\n options: DiscoverSkillsOptions | undefined,\n): Skill | undefined {\n try {\n const frontmatter = parseSkillFrontmatter(raw, fallbackName);\n const skill: Skill = {\n name: frontmatter.name,\n description: frontmatter.description,\n source,\n };\n if (frontmatter.category !== undefined) skill.category = frontmatter.category;\n if (frontmatter.dependencies !== undefined) skill.dependencies = frontmatter.dependencies;\n return skill;\n } catch (cause) {\n if (cause instanceof ConfigurationError) {\n options?.onInvalidSkill?.({\n name: fallbackName,\n source,\n code: cause.code ?? \"unknown\",\n message: cause.message,\n });\n return undefined;\n }\n throw cause;\n }\n}\n","/**\n * Block-body XML escape (ADR D9 — prompt-injection defence).\n *\n * Order matters: `&` MUST be escaped first so subsequent `<`/`>` replacements\n * do not double-encode the `&` characters they introduce.\n *\n * @internal\n */\nexport const escapeBlockBody = (s: string): string =>\n s.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n","import { escapeBlockBody } from \"../system-prompt/escape.js\";\n\n/**\n * Render the `<skills>` system-prompt block from a skill list.\n *\n * Input is the structural subset `{ name, description }` — the skill BODY is\n * NOT in the type, so it cannot leak into the prompt. Both fields are passed\n * through `escapeBlockBody` to neutralise prompt-injection vectors hidden in\n * user-controlled SKILL.md frontmatter (injection-escape ADR).\n *\n * Returns `undefined` for an empty list so the caller can omit the block.\n *\n * Public via `@theokit/sdk/skills`.\n *\n * @public\n */\nexport function buildSkillsBlock(\n skills: ReadonlyArray<{ name: string; description: string }>,\n): string | undefined {\n if (skills.length === 0) return undefined;\n const lines = skills.map(\n (skill) => ` - ${escapeBlockBody(skill.name)}: ${escapeBlockBody(skill.description)}`,\n );\n return `<skills>\\n${lines.join(\"\\n\")}\\n</skills>`;\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkIHFNRNTJ_cjs = require('./chunk-IHFNRNTJ.cjs');
|
|
4
4
|
|
|
5
5
|
// src/internal/judge/parse-verdict.ts
|
|
6
6
|
var DONE_PREFIX = "DONE:";
|
|
@@ -45,7 +45,7 @@ function parseVerdict(text) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// src/internal/judge/judge-call.ts
|
|
48
|
-
var JudgeCredentialError = class extends
|
|
48
|
+
var JudgeCredentialError = class extends chunkIHFNRNTJ_cjs.TheokitAgentError {
|
|
49
49
|
constructor(httpStatus, judgeModel, cause) {
|
|
50
50
|
super(
|
|
51
51
|
`judge unavailable: model "${judgeModel}" returned ${String(httpStatus)}. Pass \`judgeModel\`/\`apiKey\` that resolve for this provider, or omit \`judgeModel\` to derive it from the agent being driven.`,
|
|
@@ -125,5 +125,5 @@ Be strict. If unclear, prefer CONTINUE.`;
|
|
|
125
125
|
exports.JudgeCredentialError = JudgeCredentialError;
|
|
126
126
|
exports.composeJudgePrompt = composeJudgePrompt;
|
|
127
127
|
exports.judgeCallImpl = judgeCallImpl;
|
|
128
|
-
//# sourceMappingURL=chunk-
|
|
129
|
-
//# sourceMappingURL=chunk-
|
|
128
|
+
//# sourceMappingURL=chunk-ECHGWE47.cjs.map
|
|
129
|
+
//# sourceMappingURL=chunk-ECHGWE47.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/judge/parse-verdict.ts","../src/internal/judge/judge-call.ts"],"names":["TheokitAgentError"],"mappings":"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","file":"chunk-TKDWD3MU.cjs","sourcesContent":["/**\n * Pure verdict parser (T2.1, ADRs D120-D121).\n *\n * Strict prefix matching against `DONE:`, `CONTINUE:`, `SKIPPED:`. Reason\n * is the suffix, trimmed. Anything else is fail-safe: verdict = `continue`,\n * `parseFailed: true` — caller's max-consecutive-failure cap stops the\n * loop after N flakes.\n *\n * @internal\n */\n\nimport type { JudgeResult } from \"../../types/goal-events.js\";\n\nconst DONE_PREFIX = \"DONE:\";\nconst CONTINUE_PREFIX = \"CONTINUE:\";\nconst SKIPPED_PREFIX = \"SKIPPED:\";\n/** M80 — the judge can now declare impossibility, not just \"continue\". */\nconst BLOCKED_PREFIX = \"BLOCKED:\";\n\nexport function parseVerdict(text: string): JudgeResult {\n const trimmed = text.trim();\n\n if (trimmed.startsWith(DONE_PREFIX)) {\n return {\n verdict: \"done\",\n reason: trimmed.slice(DONE_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(CONTINUE_PREFIX)) {\n return {\n verdict: \"continue\",\n reason: trimmed.slice(CONTINUE_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(BLOCKED_PREFIX)) {\n return {\n verdict: \"blocked\",\n reason: trimmed.slice(BLOCKED_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(SKIPPED_PREFIX)) {\n return {\n verdict: \"skipped\",\n reason: trimmed.slice(SKIPPED_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n\n // Fail-safe: ADR D121 — prefer \"continue\" so we don't stop prematurely.\n // The runUntil loop counts consecutive parseFailed responses and bails\n // after `maxConsecutiveJudgeFailures` (default 3).\n return {\n verdict: \"continue\",\n reason: `judge response malformed: \"${trimmed.slice(0, 100)}\"`,\n parseFailed: true,\n };\n}\n","import { TheokitAgentError } from \"../../errors.js\";\n/**\n * Judge call primitive (T2.2, ADRs D119-D121).\n *\n * Instantiates a short-lived auxiliary agent that evaluates whether a\n * goal is satisfied. The auxiliary judge runs with `tools: []` and a\n * cheap model (default `openai/gpt-4o-mini`) — see ADR D119. The judge\n * model needs only API access; we read `OPENROUTER_API_KEY` directly\n * from the environment (EC-A — single source of truth) and let the\n * caller override via {@link JudgeOptions.apiKey} for Anthropic or\n * direct-OpenAI environments.\n *\n * NOTE: judge aux agents created from inside a `forkAgent` context will\n * inherit the parent fork's whitelist via AsyncLocalStorage (EC-J).\n * `tools: []` keeps this benign today; future callers that add tools to\n * a judge should be aware.\n *\n * @internal\n */\n\nimport type { AgentOptions, SDKAgent } from \"../../types/agent.js\";\nimport type { JudgeResult } from \"../../types/goal-events.js\";\nimport { parseVerdict } from \"./parse-verdict.js\";\n\n/** Inputs to the judge — pure data. */\nexport interface JudgeContext {\n goal: string;\n lastResponse: string;\n subgoals?: string[];\n}\n\n/** Caller-supplied tuning knobs for the judge call. */\nexport interface JudgeOptions {\n /**\n * Judge model identifier. When absent, it DERIVES from {@link agentModel} — and only falls back to the literal\n * `\"openai/gpt-4o-mini\"` when neither is provided.\n *\n * M80 — the fixed default was provider-blind: it only resolves on OpenRouter. With an Anthropic key it gives\n * 404, with an OAuth bearer it gives 401, and in both the goal burned 3 whole turns before failing with\n * a misleading reason. The agent-builder already worked around this by deriving on its own; the knowledge\n * belongs here.\n */\n judgeModel?: string;\n /**\n * M80 — the DRIVEN agent's model. It is the basis of the derivation: a judge running on the same model as the\n * chat works wherever chat works.\n */\n agentModel?: string;\n /** Override env. Default `process.env.OPENROUTER_API_KEY` (EC-A). */\n apiKey?: string;\n}\n\n/**\n * M80 — the judge's credential or model does not work: 401/404.\n *\n * Fails FAST by design. `rules/error-handling.md` § 2 separates recoverable from unrecoverable, and a\n * nonexistent model does not start existing on retry — folding that into `{parseFailed: true}` made the loop\n * try three times and report \"failed\" on a consecutive-failure limit, hiding that the cause was the\n * credential. A slow, opaque failure traded for a fast, clear one.\n *\n * PARSE failures and network errors stay folded: they are recoverable, and the loop already decides on\n * consecutivas.\n */\nexport class JudgeCredentialError extends TheokitAgentError {\n override readonly name = \"JudgeCredentialError\";\n\n constructor(\n readonly httpStatus: number,\n readonly judgeModel: string,\n cause: unknown,\n ) {\n super(\n `judge unavailable: model \"${judgeModel}\" returned ${String(httpStatus)}. ` +\n \"Pass `judgeModel`/`apiKey` that resolve for this provider, or omit `judgeModel` to derive \" +\n \"it from the agent being driven.\",\n { code: \"judge_credential\", cause, isRetryable: false },\n );\n }\n}\n\n/** Extracts the HTTP status from a provider error, when it carries one. */\nfunction statusHttpDe(err: unknown): number | undefined {\n const s =\n (err as { status?: unknown; statusCode?: unknown }).status ??\n (err as { statusCode?: unknown }).statusCode;\n if (typeof s === \"number\") return s;\n const m = /\\b(401|403|404)\\b/.exec(err instanceof Error ? err.message : String(err));\n return m?.[1] !== undefined ? Number(m[1]) : undefined;\n}\n\n/** Dependencies injected so `judge-call.ts` stays free of `Agent` import. */\nexport interface JudgeDeps {\n create: (options: AgentOptions) => Promise<SDKAgent>;\n}\n\n/**\n * Run the judge auxiliary agent and parse the verdict. Always returns a\n * `JudgeResult` — failures are folded into `{ parseFailed: true,\n * verdict: \"continue\" }` so the loop can decide based on consecutive\n * failures (ADR D121).\n *\n * @internal\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: judge call must (1) check env or override, (2) catch errors as fail-safe, (3) ensure aux dispose runs once regardless of outcome — three concerns linearly arranged, harm clarity to extract.\nexport async function judgeCallImpl(\n ctx: JudgeContext,\n options: JudgeOptions | undefined,\n deps: JudgeDeps,\n): Promise<JudgeResult> {\n const prompt = composeJudgePrompt(ctx);\n // EC-A: single env source — OpenRouter only. No multi-provider fallback.\n const apiKey = options?.apiKey ?? process.env.OPENROUTER_API_KEY;\n if (apiKey === undefined) {\n return {\n verdict: \"continue\",\n reason:\n \"judge unavailable: OPENROUTER_API_KEY missing and no override passed via options.apiKey\",\n parseFailed: true,\n };\n }\n\n // M80 — precedence: explicit > driven agent's model > the historical literal. The literal only\n // survives as a last resort, so as not to break callers who never passed either.\n const judgeModel = options?.judgeModel ?? options?.agentModel ?? \"openai/gpt-4o-mini\";\n let auxAgent: SDKAgent | undefined;\n try {\n auxAgent = await deps.create({\n apiKey,\n model: { id: judgeModel },\n tools: [],\n local: {},\n metadata: { forkOrigin: \"judge\" },\n } as AgentOptions);\n const run = await auxAgent.send(prompt);\n const result = await run.wait();\n return parseVerdict(result.result ?? \"\");\n } catch (err) {\n // M80 — 401/403/404 are credential/model errors: unrecoverable, failing fast and typed. Everything\n // else (network, timeout, 5xx) stays folded, because it IS recoverable and the loop already decides on\n // consecutivas.\n const status = statusHttpDe(err);\n if (status === 401 || status === 403 || status === 404) {\n throw new JudgeCredentialError(status, judgeModel, err);\n }\n return {\n verdict: \"continue\",\n reason: `judge call failed: ${err instanceof Error ? err.message : String(err)}`,\n parseFailed: true,\n };\n } finally {\n if (auxAgent !== undefined) {\n try {\n await auxAgent.dispose();\n } catch {\n // dispose errors are non-fatal; judge result is already prepared\n }\n }\n }\n}\n\n/**\n * Build the strict-format prompt the judge expects.\n *\n * @internal\n */\nexport function composeJudgePrompt(ctx: JudgeContext): string {\n const subgoals =\n ctx.subgoals !== undefined && ctx.subgoals.length > 0 ? ctx.subgoals.join(\", \") : \"(none)\";\n return `You are a goal judge. Determine if this goal is satisfied.\n\nGoal: ${ctx.goal}\nSubgoals: ${subgoals}\nLast agent response: ${ctx.lastResponse}\n\nRespond with EXACTLY one of:\n- DONE: <reason>\n- CONTINUE: <what's left>\n- SKIPPED: <why not applicable>\n\nBe strict. If unclear, prefer CONTINUE.`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/judge/parse-verdict.ts","../src/internal/judge/judge-call.ts"],"names":["TheokitAgentError"],"mappings":"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","file":"chunk-ECHGWE47.cjs","sourcesContent":["/**\n * Pure verdict parser (T2.1, ADRs D120-D121).\n *\n * Strict prefix matching against `DONE:`, `CONTINUE:`, `SKIPPED:`. Reason\n * is the suffix, trimmed. Anything else is fail-safe: verdict = `continue`,\n * `parseFailed: true` — caller's max-consecutive-failure cap stops the\n * loop after N flakes.\n *\n * @internal\n */\n\nimport type { JudgeResult } from \"../../types/goal-events.js\";\n\nconst DONE_PREFIX = \"DONE:\";\nconst CONTINUE_PREFIX = \"CONTINUE:\";\nconst SKIPPED_PREFIX = \"SKIPPED:\";\n/** M80 — the judge can now declare impossibility, not just \"continue\". */\nconst BLOCKED_PREFIX = \"BLOCKED:\";\n\nexport function parseVerdict(text: string): JudgeResult {\n const trimmed = text.trim();\n\n if (trimmed.startsWith(DONE_PREFIX)) {\n return {\n verdict: \"done\",\n reason: trimmed.slice(DONE_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(CONTINUE_PREFIX)) {\n return {\n verdict: \"continue\",\n reason: trimmed.slice(CONTINUE_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(BLOCKED_PREFIX)) {\n return {\n verdict: \"blocked\",\n reason: trimmed.slice(BLOCKED_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n if (trimmed.startsWith(SKIPPED_PREFIX)) {\n return {\n verdict: \"skipped\",\n reason: trimmed.slice(SKIPPED_PREFIX.length).trim(),\n parseFailed: false,\n };\n }\n\n // Fail-safe: ADR D121 — prefer \"continue\" so we don't stop prematurely.\n // The runUntil loop counts consecutive parseFailed responses and bails\n // after `maxConsecutiveJudgeFailures` (default 3).\n return {\n verdict: \"continue\",\n reason: `judge response malformed: \"${trimmed.slice(0, 100)}\"`,\n parseFailed: true,\n };\n}\n","import { TheokitAgentError } from \"../../errors.js\";\n/**\n * Judge call primitive (T2.2, ADRs D119-D121).\n *\n * Instantiates a short-lived auxiliary agent that evaluates whether a\n * goal is satisfied. The auxiliary judge runs with `tools: []` and a\n * cheap model (default `openai/gpt-4o-mini`) — see ADR D119. The judge\n * model needs only API access; we read `OPENROUTER_API_KEY` directly\n * from the environment (EC-A — single source of truth) and let the\n * caller override via {@link JudgeOptions.apiKey} for Anthropic or\n * direct-OpenAI environments.\n *\n * NOTE: judge aux agents created from inside a `forkAgent` context will\n * inherit the parent fork's whitelist via AsyncLocalStorage (EC-J).\n * `tools: []` keeps this benign today; future callers that add tools to\n * a judge should be aware.\n *\n * @internal\n */\n\nimport type { AgentOptions, SDKAgent } from \"../../types/agent.js\";\nimport type { JudgeResult } from \"../../types/goal-events.js\";\nimport { parseVerdict } from \"./parse-verdict.js\";\n\n/** Inputs to the judge — pure data. */\nexport interface JudgeContext {\n goal: string;\n lastResponse: string;\n subgoals?: string[];\n}\n\n/** Caller-supplied tuning knobs for the judge call. */\nexport interface JudgeOptions {\n /**\n * Judge model identifier. When absent, it DERIVES from {@link agentModel} — and only falls back to the literal\n * `\"openai/gpt-4o-mini\"` when neither is provided.\n *\n * M80 — the fixed default was provider-blind: it only resolves on OpenRouter. With an Anthropic key it gives\n * 404, with an OAuth bearer it gives 401, and in both the goal burned 3 whole turns before failing with\n * a misleading reason. The agent-builder already worked around this by deriving on its own; the knowledge\n * belongs here.\n */\n judgeModel?: string;\n /**\n * M80 — the DRIVEN agent's model. It is the basis of the derivation: a judge running on the same model as the\n * chat works wherever chat works.\n */\n agentModel?: string;\n /** Override env. Default `process.env.OPENROUTER_API_KEY` (EC-A). */\n apiKey?: string;\n}\n\n/**\n * M80 — the judge's credential or model does not work: 401/404.\n *\n * Fails FAST by design. `rules/error-handling.md` § 2 separates recoverable from unrecoverable, and a\n * nonexistent model does not start existing on retry — folding that into `{parseFailed: true}` made the loop\n * try three times and report \"failed\" on a consecutive-failure limit, hiding that the cause was the\n * credential. A slow, opaque failure traded for a fast, clear one.\n *\n * PARSE failures and network errors stay folded: they are recoverable, and the loop already decides on\n * consecutivas.\n */\nexport class JudgeCredentialError extends TheokitAgentError {\n override readonly name = \"JudgeCredentialError\";\n\n constructor(\n readonly httpStatus: number,\n readonly judgeModel: string,\n cause: unknown,\n ) {\n super(\n `judge unavailable: model \"${judgeModel}\" returned ${String(httpStatus)}. ` +\n \"Pass `judgeModel`/`apiKey` that resolve for this provider, or omit `judgeModel` to derive \" +\n \"it from the agent being driven.\",\n { code: \"judge_credential\", cause, isRetryable: false },\n );\n }\n}\n\n/** Extracts the HTTP status from a provider error, when it carries one. */\nfunction statusHttpDe(err: unknown): number | undefined {\n const s =\n (err as { status?: unknown; statusCode?: unknown }).status ??\n (err as { statusCode?: unknown }).statusCode;\n if (typeof s === \"number\") return s;\n const m = /\\b(401|403|404)\\b/.exec(err instanceof Error ? err.message : String(err));\n return m?.[1] !== undefined ? Number(m[1]) : undefined;\n}\n\n/** Dependencies injected so `judge-call.ts` stays free of `Agent` import. */\nexport interface JudgeDeps {\n create: (options: AgentOptions) => Promise<SDKAgent>;\n}\n\n/**\n * Run the judge auxiliary agent and parse the verdict. Always returns a\n * `JudgeResult` — failures are folded into `{ parseFailed: true,\n * verdict: \"continue\" }` so the loop can decide based on consecutive\n * failures (ADR D121).\n *\n * @internal\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: judge call must (1) check env or override, (2) catch errors as fail-safe, (3) ensure aux dispose runs once regardless of outcome — three concerns linearly arranged, harm clarity to extract.\nexport async function judgeCallImpl(\n ctx: JudgeContext,\n options: JudgeOptions | undefined,\n deps: JudgeDeps,\n): Promise<JudgeResult> {\n const prompt = composeJudgePrompt(ctx);\n // EC-A: single env source — OpenRouter only. No multi-provider fallback.\n const apiKey = options?.apiKey ?? process.env.OPENROUTER_API_KEY;\n if (apiKey === undefined) {\n return {\n verdict: \"continue\",\n reason:\n \"judge unavailable: OPENROUTER_API_KEY missing and no override passed via options.apiKey\",\n parseFailed: true,\n };\n }\n\n // M80 — precedence: explicit > driven agent's model > the historical literal. The literal only\n // survives as a last resort, so as not to break callers who never passed either.\n const judgeModel = options?.judgeModel ?? options?.agentModel ?? \"openai/gpt-4o-mini\";\n let auxAgent: SDKAgent | undefined;\n try {\n auxAgent = await deps.create({\n apiKey,\n model: { id: judgeModel },\n tools: [],\n local: {},\n metadata: { forkOrigin: \"judge\" },\n } as AgentOptions);\n const run = await auxAgent.send(prompt);\n const result = await run.wait();\n return parseVerdict(result.result ?? \"\");\n } catch (err) {\n // M80 — 401/403/404 are credential/model errors: unrecoverable, failing fast and typed. Everything\n // else (network, timeout, 5xx) stays folded, because it IS recoverable and the loop already decides on\n // consecutivas.\n const status = statusHttpDe(err);\n if (status === 401 || status === 403 || status === 404) {\n throw new JudgeCredentialError(status, judgeModel, err);\n }\n return {\n verdict: \"continue\",\n reason: `judge call failed: ${err instanceof Error ? err.message : String(err)}`,\n parseFailed: true,\n };\n } finally {\n if (auxAgent !== undefined) {\n try {\n await auxAgent.dispose();\n } catch {\n // dispose errors are non-fatal; judge result is already prepared\n }\n }\n }\n}\n\n/**\n * Build the strict-format prompt the judge expects.\n *\n * @internal\n */\nexport function composeJudgePrompt(ctx: JudgeContext): string {\n const subgoals =\n ctx.subgoals !== undefined && ctx.subgoals.length > 0 ? ctx.subgoals.join(\", \") : \"(none)\";\n return `You are a goal judge. Determine if this goal is satisfied.\n\nGoal: ${ctx.goal}\nSubgoals: ${subgoals}\nLast agent response: ${ctx.lastResponse}\n\nRespond with EXACTLY one of:\n- DONE: <reason>\n- CONTINUE: <what's left>\n- SKIPPED: <why not applicable>\n\nBe strict. If unclear, prefer CONTINUE.`;\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunkYLQQX5W2_cjs = require('./chunk-YLQQX5W2.cjs');
|
|
4
|
-
var
|
|
4
|
+
var chunkIHFNRNTJ_cjs = require('./chunk-IHFNRNTJ.cjs');
|
|
5
5
|
var async_hooks = require('async_hooks');
|
|
6
6
|
|
|
7
7
|
// src/internal/llm/credential-pool-types.ts
|
|
@@ -70,7 +70,7 @@ var CredentialPool = class _CredentialPool {
|
|
|
70
70
|
entries;
|
|
71
71
|
constructor(provider, entries, strategy = "fill_first") {
|
|
72
72
|
if (entries.length === 0) {
|
|
73
|
-
throw new
|
|
73
|
+
throw new chunkIHFNRNTJ_cjs.ConfigurationError(
|
|
74
74
|
`credential-pool: cannot construct empty pool for provider "${provider}"`,
|
|
75
75
|
{ code: "credential_pool_empty" }
|
|
76
76
|
);
|
|
@@ -292,5 +292,5 @@ exports.currentCredentialPool = currentCredentialPool;
|
|
|
292
292
|
exports.newPooledCredential = newPooledCredential;
|
|
293
293
|
exports.sleepWithAbort = sleepWithAbort;
|
|
294
294
|
exports.withCredentialPool = withCredentialPool;
|
|
295
|
-
//# sourceMappingURL=chunk-
|
|
296
|
-
//# sourceMappingURL=chunk-
|
|
295
|
+
//# sourceMappingURL=chunk-ECW2MLEW.cjs.map
|
|
296
|
+
//# sourceMappingURL=chunk-ECW2MLEW.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/llm/credential-pool-types.ts","../src/internal/llm/retry.ts","../src/internal/llm/credential-pool.ts","../src/internal/llm/credential-pool-context.ts"],"names":["ConfigurationError","withCwdMutex","picked","AsyncLocalStorage"],"mappings":"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","file":"chunk-KL7E244K.cjs","sourcesContent":["/**\n * Types for the credential pool subsystem (ADRs D123-D133).\n *\n * Pool a developer's multiple API keys for the SAME provider so the SDK\n * can rotate transparently on HTTP 429/402/401 instead of jumping\n * straight to a different provider via `FallbackLlmClient`.\n *\n * @internal\n */\n\n/**\n * Rotation strategy when picking the next credential from a pool.\n *\n * - `fill_first` (default): use entries[0] until exhausted, then [1]…\n * - `round_robin`: rotate after each `select()`\n * - `least_used`: pick the entry with the lowest `requestCount`\n * - `random`: random pick among healthy entries\n *\n * Closed enum per ADR D124 — adding a strategy is an explicit semver\n * minor that updates `selectByStrategy` exhaustively.\n *\n * @internal\n */\nexport type CredentialPoolStrategy = \"fill_first\" | \"round_robin\" | \"least_used\" | \"random\";\n\n/** Health flag tracked per credential. */\nexport type CredentialStatus = \"ok\" | \"exhausted\";\n\n/**\n * Provenance of a credential — where it came from when seeded.\n * Free-form string; conventional prefixes:\n *\n * - `env:<VAR_NAME>` — auto-discovered from environment variable\n * - `manual` — added via explicit `apiKeys` config\n * - `explicit-apikey` — wrapped from `AgentOptions.apiKey` single-key path (D132)\n */\nexport type CredentialSource = string;\n\n/**\n * Single credential entry inside a {@link CredentialPool}.\n *\n * `accessToken` is the sensitive field — must never be logged unredacted.\n * The lint gate `tests/lint/no-unredacted-pool-token.test.ts` enforces this.\n *\n * @internal\n */\nexport interface PooledCredential {\n /** Stable identifier (uuid v4) for log lines + telemetry. */\n id: string;\n /** Human-readable label for `Theokit.credentialPool.list()`. */\n label: string;\n /** Provider name (matches `ProviderProfile.name`). */\n provider: string;\n /** Sort key; lower = earlier in fill_first. Unique within a pool. */\n priority: number;\n /** Provenance string. */\n source: CredentialSource;\n /** Sensitive API key. NEVER LOG UNREDACTED. */\n accessToken: string;\n /** Current health. */\n lastStatus: CredentialStatus;\n /** Epoch ms of the last status change. */\n lastStatusAt: number | undefined;\n /** HTTP code that caused exhaustion (401/402/429). */\n lastErrorCode: number | undefined;\n /** Provider-supplied \"retry after this epoch ms\" hint. Overrides cooldown defaults. */\n lastErrorResetAt: number | undefined;\n /** Bumped per successful `select()` for `least_used` strategy. Lazy-persisted. */\n requestCount: number;\n}\n\n/**\n * Serializable snapshot of a single provider's pool. Used by the\n * persistence layer (T2.1) to read/write `~/.theokit/credential-pool.json`.\n *\n * @internal\n */\nexport interface CredentialPoolSnapshot {\n provider: string;\n strategy: CredentialPoolStrategy;\n entries: PooledCredential[];\n}\n\n/**\n * Cooldown ladder by HTTP error code (ADR D125). Provider-supplied\n * `lastErrorResetAt` overrides these defaults when present.\n *\n * @internal\n */\nexport const COOLDOWN_MS: Readonly<Record<number, number>> = {\n 401: 5 * 60 * 1000, // 5 minutes — OAuth refresh can recover quickly\n 402: 60 * 60 * 1000, // 1 hour — billing quota typically hourly+\n 429: 60 * 60 * 1000, // 1 hour — daily-rate-limit windows\n};\n\n/** Default cooldown when no specific code matches. */\nexport const DEFAULT_COOLDOWN_MS = 60 * 60 * 1000;\n","/**\n * T3.4 — exponential backoff with full jitter for credential-pool retry.\n *\n * Why we need this: pre-T3.4 `pool-aware-client.ts:79-83` retried the\n * SAME credential immediately on the first 429 without any wait. When\n * every credential in the pool returned 429 (thundering herd against a\n * shared quota), the client burned through all 5 retry attempts in <1ms,\n * tripped `CredentialPoolExhaustedError`, and surfaced to the agent loop\n * as a hard failure. The user-visible symptom was \"all my keys are\n * exhausted!\" when in reality a 1-2s wait would have cleared the rate\n * limit.\n *\n * AWS Architecture Blog \"Exponential Backoff And Jitter\" (Marc Brooker,\n * 2015) demonstrates full jitter as the canonical pattern: `sleep =\n * random_between(0, base * 2^attempt)`. We extend it with a hard cap\n * (so a 32s sleep never blocks an interactive agent) and honor the\n * provider's `Retry-After` hint when present — the hint always wins over\n * the computed value when it falls within `[base, cap]`.\n *\n * @internal\n */\n\nexport interface BackoffOptions {\n /** 0-indexed retry attempt number. */\n attempt: number;\n /** Base delay in ms (multiplier for 2^attempt). Default 500. */\n baseMs?: number;\n /** Hard cap in ms — backoff never sleeps longer than this. Default 32_000. */\n capMs?: number;\n /** Provider's Retry-After hint in ms (from header). Takes precedence when in-range. */\n retryAfterMs?: number;\n /** Random source — pass a deterministic mock from tests. Default `Math.random`. */\n rng?: () => number;\n}\n\nconst DEFAULT_BASE_MS = 500;\nconst DEFAULT_CAP_MS = 32_000;\n\n/**\n * Compute the next sleep duration (ms) before retrying a failed request.\n *\n * Algorithm:\n * 1. If `retryAfterMs` is set, prefer it — clamped to `[baseMs, capMs]`.\n * The provider's hint is the most accurate available signal.\n * 2. Otherwise: `sleep = random(0, min(cap, base * 2^attempt))` — full\n * jitter per Brooker 2015 ensures coordinated clients (each with\n * different RNG seeds) spread the retry storm.\n *\n * @internal\n */\nexport function computeBackoffMs(opts: BackoffOptions): number {\n const base = opts.baseMs ?? DEFAULT_BASE_MS;\n const cap = opts.capMs ?? DEFAULT_CAP_MS;\n const rng = opts.rng ?? Math.random;\n if (opts.retryAfterMs !== undefined && opts.retryAfterMs >= 0) {\n return Math.max(base, Math.min(cap, opts.retryAfterMs));\n }\n const ceiling = Math.min(cap, base * 2 ** opts.attempt);\n return Math.floor(rng() * ceiling);\n}\n\n/**\n * `setTimeout`-based sleep that resolves early when the abort signal fires.\n * Resolves cleanly (does not throw on abort) so callers decide how to\n * react to the abort downstream.\n *\n * @internal\n */\nexport function sleepWithAbort(ms: number, signal: AbortSignal): Promise<void> {\n if (ms <= 0 || signal.aborted) return Promise.resolve();\n return new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = (): void => {\n clearTimeout(timer);\n resolve();\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n","/**\n * In-memory credential pool with strategy-based rotation (ADRs D123-D133).\n *\n * Pure logic. No I/O — persistence lives in `internal/persistence/\n * credential-pool-store.ts`. The pool is the single source of truth for\n * rotation state within a process; if it diverges from disk (e.g.,\n * process killed mid-rotation per EC-I), the next save heals the\n * inconsistency — at the cost of one extra HTTP 429 per crash.\n *\n * Thread safety: the pool uses an in-process async-aware mutex\n * (`cwd-mutex` D9 keyed by `credential-pool:${provider}`). Cross-process\n * safety happens at the persistence layer via `withFileLock` (D61).\n * Two Node processes sharing the same file may double-pick momentarily\n * (worst case = 1 extra 429 per race) — acceptable per ADR D128.\n *\n * @internal\n */\n\nimport { ConfigurationError } from \"../../errors.js\";\nimport { withCwdMutex } from \"../persistence/cwd-mutex.js\";\nimport {\n COOLDOWN_MS,\n type CredentialPoolSnapshot,\n type CredentialPoolStrategy,\n DEFAULT_COOLDOWN_MS,\n type PooledCredential,\n} from \"./credential-pool-types.js\";\nimport { sleepWithAbort } from \"./retry.js\";\n\n/**\n * Construct a `PooledCredential` with sane defaults.\n *\n * @internal\n */\nexport function newPooledCredential(args: {\n provider: string;\n accessToken: string;\n priority: number;\n source: string;\n label?: string;\n}): PooledCredential {\n return {\n id: globalThis.crypto.randomUUID(),\n label: args.label ?? labelFromSource(args.source, args.accessToken),\n provider: args.provider,\n priority: args.priority,\n source: args.source,\n accessToken: args.accessToken,\n lastStatus: \"ok\",\n lastStatusAt: undefined,\n lastErrorCode: undefined,\n lastErrorResetAt: undefined,\n requestCount: 0,\n };\n}\n\n/**\n * Build a human-readable label from the credential source. Used for\n * `Theokit.credentialPool.list()` UX. NEVER includes the raw token.\n *\n * @internal\n */\nfunction labelFromSource(source: string, accessToken: string): string {\n if (source.startsWith(\"env:\")) return source.slice(4);\n const masked =\n accessToken.length > 8 ? `${accessToken.slice(0, 4)}…${accessToken.slice(-4)}` : \"***\";\n return `${source}:${masked}`;\n}\n\n/**\n * Per-provider credential pool. Construct with one or more entries\n * sorted by priority. Throws `ConfigurationError` on empty input.\n *\n * EC-C: identical `accessToken`s are deduplicated during construction\n * (3 copies of \"k1\" → 1 entry). Otherwise an exhaustion event would\n * mark \"k1\" exhausted 3× wastefully.\n *\n * @internal\n */\nexport class CredentialPool {\n readonly provider: string;\n private readonly strategy: CredentialPoolStrategy;\n private entries: PooledCredential[];\n\n constructor(\n provider: string,\n entries: PooledCredential[],\n strategy: CredentialPoolStrategy = \"fill_first\",\n ) {\n if (entries.length === 0) {\n throw new ConfigurationError(\n `credential-pool: cannot construct empty pool for provider \"${provider}\"`,\n { code: \"credential_pool_empty\" },\n );\n }\n this.provider = provider;\n this.strategy = strategy;\n // EC-C: dedupe by accessToken before sort.\n this.entries = dedupeByToken(entries).sort((a, b) => a.priority - b.priority);\n }\n\n /** True if the pool was seeded with at least one entry. */\n hasCredentials(): boolean {\n return this.entries.length > 0;\n }\n\n /** True iff at least one entry is healthy (auto-healed if cooldown expired). */\n hasAvailable(): boolean {\n return this.availableEntries().length > 0;\n }\n\n /**\n * Earliest `lastErrorResetAt` across all entries, or `undefined` if no\n * entry is in cooldown. Used by `waitForAvailable` to compute the\n * upper bound of each jittered sleep window.\n *\n * @internal\n */\n earliestResetAt(): number | undefined {\n let earliest: number | undefined;\n for (const entry of this.entries) {\n if (entry.lastStatus === \"ok\") continue;\n if (entry.lastErrorResetAt === undefined) continue;\n if (earliest === undefined || entry.lastErrorResetAt < earliest) {\n earliest = entry.lastErrorResetAt;\n }\n }\n return earliest;\n }\n\n /**\n * Wait until at least one entry becomes healthy OR `maxWaitMs` elapses\n * OR `signal` aborts. Returns `true` if a healthy entry is available\n * when the call returns, `false` otherwise.\n *\n * T3.9 — Reconnect storm prevention. Concurrent callers each sleep a\n * random fraction of the window to the earliest cooldown reset (full\n * jitter — AWS Brooker 2015) so they do not all wake at the exact\n * instant cooldown expires and hammer the upstream simultaneously.\n *\n * The `sleeper` parameter is a dependency-injection seam for tests:\n * callers in production omit it and the default `setTimeout`-based\n * sleeper is used. The seam avoids the `vi.useFakeTimers()` mismatch\n * that blocked T3.4's wiring.\n *\n * @internal\n */\n async waitForAvailable(\n signal: AbortSignal,\n opts: {\n maxWaitMs: number;\n sleeper?: (ms: number, signal: AbortSignal) => Promise<void>;\n },\n ): Promise<boolean> {\n if (signal.aborted) return false;\n if (this.hasAvailable()) return true;\n const sleeper = opts.sleeper ?? sleepWithAbort;\n const deadline = Date.now() + opts.maxWaitMs;\n while (Date.now() < deadline) {\n if (signal.aborted) return false;\n const jittered = this.computeJitteredSleepMs(deadline);\n await sleeper(jittered, signal);\n if (signal.aborted) return false;\n if (this.hasAvailable()) return true;\n }\n return this.hasAvailable();\n }\n\n /**\n * Compute the next jittered sleep window (ms) for `waitForAvailable`.\n * Full-jitter cap: at most (a) time until the earliest reset (plus a\n * 50 ms grace so the entry actually heals before re-probe), or (b) the\n * remaining wait window, whichever is smaller. When no entry has a\n * known reset (rare — only if `markExhaustedAndRotate` was called\n * without `resetAtMs`), fall back to a 250 ms ceiling so we stay\n * responsive without hot-looping.\n */\n private computeJitteredSleepMs(deadline: number): number {\n const resetAt = this.earliestResetAt();\n const remaining = deadline - Date.now();\n const ceiling =\n resetAt === undefined\n ? Math.min(remaining, 250)\n : Math.min(remaining, Math.max(0, resetAt - Date.now()) + 50);\n return Math.max(0, Math.floor(Math.random() * ceiling));\n }\n\n /** Live (frozen) view of all entries. */\n list(): readonly PooledCredential[] {\n return this.entries;\n }\n\n /**\n * Pick a healthy entry according to the configured strategy.\n * Mutates `requestCount` and (for round_robin) entry order.\n * Returns null if every entry is in cooldown.\n *\n * @internal\n */\n async select(): Promise<PooledCredential | null> {\n return withCwdMutex(`credential-pool:${this.provider}`, async () => this.selectLocked());\n }\n\n /**\n * Mark the entry whose id matches `entryId` as exhausted with the\n * appropriate cooldown, then return the next healthy entry (or null).\n * If `entryId` is undefined, marks all currently-active entries (rare —\n * only used by admin reset scenarios).\n *\n * @internal\n */\n async markExhaustedAndRotate(args: {\n entryId: string;\n statusCode: number;\n resetAtMs?: number;\n }): Promise<PooledCredential | null> {\n return withCwdMutex(`credential-pool:${this.provider}`, async () => {\n const entry = this.entries.find((e) => e.id === args.entryId);\n if (entry !== undefined) {\n const now = Date.now();\n const cooldown = COOLDOWN_MS[args.statusCode] ?? DEFAULT_COOLDOWN_MS;\n entry.lastStatus = \"exhausted\";\n entry.lastStatusAt = now;\n entry.lastErrorCode = args.statusCode;\n // EC-5: negative or stale resetAt → ignore, fall through to default.\n entry.lastErrorResetAt =\n args.resetAtMs !== undefined && args.resetAtMs > now ? args.resetAtMs : now + cooldown;\n }\n return this.selectLocked();\n });\n }\n\n /** Admin escape hatch: clear all cooldowns. */\n async resetAll(): Promise<void> {\n await withCwdMutex(`credential-pool:${this.provider}`, async () => {\n for (const e of this.entries) {\n e.lastStatus = \"ok\";\n e.lastStatusAt = undefined;\n e.lastErrorCode = undefined;\n e.lastErrorResetAt = undefined;\n }\n });\n }\n\n /** Materialize for persistence. */\n toSnapshot(): CredentialPoolSnapshot {\n return {\n provider: this.provider,\n strategy: this.strategy,\n entries: this.entries.map((e) => ({ ...e })),\n };\n }\n\n /** Hydrate from a previously-saved snapshot. */\n static fromSnapshot(snapshot: CredentialPoolSnapshot): CredentialPool {\n return new CredentialPool(snapshot.provider, snapshot.entries, snapshot.strategy);\n }\n\n // ─── Internal helpers (always called inside the mutex) ───\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: strategy dispatch must branch on 4 enum values (random/least_used/round_robin/fill_first) with 1-entry short-circuits per strategy. Each branch is 5-10 lines of focused logic; extracting per-strategy methods fragments the linear narrative.\n private selectLocked(): PooledCredential | null {\n const available = this.availableEntries();\n if (available.length === 0) return null;\n\n if (this.strategy === \"random\") {\n const idx = Math.floor(Math.random() * available.length);\n const picked = available[idx];\n if (picked === undefined) return null; // unreachable; satisfies strict\n picked.requestCount += 1;\n return picked;\n }\n\n if (this.strategy === \"least_used\" && available.length > 1) {\n // EC-3: tie-break by priority ascending (first wins).\n const picked = [...available].sort(\n (a, b) => a.requestCount - b.requestCount || a.priority - b.priority,\n )[0];\n if (picked === undefined) return null;\n picked.requestCount += 1;\n return picked;\n }\n\n if (this.strategy === \"round_robin\" && available.length > 1) {\n // Pick head; move it to the end of `entries` so next call picks the next.\n const picked = available[0];\n if (picked === undefined) return null;\n picked.requestCount += 1;\n const rest = this.entries.filter((e) => e.id !== picked.id);\n // Re-assign priorities: rotated entry gets the highest priority value\n // so it sorts last after `fromSnapshot` rehydration. This makes EC-F\n // (round-robin state survives save/load) work without a separate field.\n const maxPriority = this.entries.length - 1;\n rest.forEach((e, i) => {\n e.priority = i;\n });\n picked.priority = maxPriority;\n this.entries = [...rest, picked];\n return picked;\n }\n\n // fill_first (default) and 1-entry edge case for round_robin/least_used\n const picked = available[0];\n if (picked === undefined) return null;\n picked.requestCount += 1;\n return picked;\n }\n\n private availableEntries(): PooledCredential[] {\n const now = Date.now();\n const out: PooledCredential[] = [];\n for (const entry of this.entries) {\n if (entry.lastStatus === \"ok\") {\n out.push(entry);\n continue;\n }\n // EC-1: auto-heal expired cooldowns inline.\n const resetAt = entry.lastErrorResetAt;\n if (resetAt !== undefined && now >= resetAt) {\n entry.lastStatus = \"ok\";\n entry.lastStatusAt = undefined;\n entry.lastErrorCode = undefined;\n entry.lastErrorResetAt = undefined;\n out.push(entry);\n }\n }\n return out;\n }\n}\n\n/**\n * Drop duplicates by `accessToken`. Keeps the first occurrence (lowest\n * priority wins after sort). EC-C from the edge-case review.\n */\nfunction dedupeByToken(entries: PooledCredential[]): PooledCredential[] {\n const seen = new Set<string>();\n const out: PooledCredential[] = [];\n for (const e of entries) {\n if (seen.has(e.accessToken)) continue;\n seen.add(e.accessToken);\n out.push(e);\n }\n return out;\n}\n","/**\n * Per-fork credential-pool inheritance via `AsyncLocalStorage` (ADR D131).\n *\n * Mirrors the whitelist context pattern from D111\n * (`runtime/async-local-storage.ts`). Forked sub-agents inherit the\n * parent's pool *by reference* so concurrent rotations all observe\n * the same cooldown state — the desired behavior per Hermes parity.\n *\n * @internal\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport type { CredentialPool } from \"./credential-pool.js\";\n\n/** ALS slot — keyed by provider name → pool. */\nconst credentialPoolStore = new AsyncLocalStorage<Map<string, CredentialPool>>();\n\n/**\n * Run `fn` with `pools` as the active credential-pool registry for the\n * current async context. Nested calls shadow the outer map (parent\n * pools are not visible to children when the child sets its own).\n *\n * @internal\n */\nexport async function withCredentialPool<T>(\n pools: Map<string, CredentialPool>,\n fn: () => Promise<T>,\n): Promise<T> {\n return credentialPoolStore.run(pools, fn);\n}\n\n/**\n * Pool registered for `provider` in the current async context, or\n * `undefined` when no `withCredentialPool(...)` scope is active.\n *\n * @internal\n */\nexport function currentCredentialPool(provider: string): CredentialPool | undefined {\n return credentialPoolStore.getStore()?.get(provider);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/internal/llm/credential-pool-types.ts","../src/internal/llm/retry.ts","../src/internal/llm/credential-pool.ts","../src/internal/llm/credential-pool-context.ts"],"names":["ConfigurationError","withCwdMutex","picked","AsyncLocalStorage"],"mappings":"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","file":"chunk-ECW2MLEW.cjs","sourcesContent":["/**\n * Types for the credential pool subsystem (ADRs D123-D133).\n *\n * Pool a developer's multiple API keys for the SAME provider so the SDK\n * can rotate transparently on HTTP 429/402/401 instead of jumping\n * straight to a different provider via `FallbackLlmClient`.\n *\n * @internal\n */\n\n/**\n * Rotation strategy when picking the next credential from a pool.\n *\n * - `fill_first` (default): use entries[0] until exhausted, then [1]…\n * - `round_robin`: rotate after each `select()`\n * - `least_used`: pick the entry with the lowest `requestCount`\n * - `random`: random pick among healthy entries\n *\n * Closed enum per ADR D124 — adding a strategy is an explicit semver\n * minor that updates `selectByStrategy` exhaustively.\n *\n * @internal\n */\nexport type CredentialPoolStrategy = \"fill_first\" | \"round_robin\" | \"least_used\" | \"random\";\n\n/** Health flag tracked per credential. */\nexport type CredentialStatus = \"ok\" | \"exhausted\";\n\n/**\n * Provenance of a credential — where it came from when seeded.\n * Free-form string; conventional prefixes:\n *\n * - `env:<VAR_NAME>` — auto-discovered from environment variable\n * - `manual` — added via explicit `apiKeys` config\n * - `explicit-apikey` — wrapped from `AgentOptions.apiKey` single-key path (D132)\n */\nexport type CredentialSource = string;\n\n/**\n * Single credential entry inside a {@link CredentialPool}.\n *\n * `accessToken` is the sensitive field — must never be logged unredacted.\n * The lint gate `tests/lint/no-unredacted-pool-token.test.ts` enforces this.\n *\n * @internal\n */\nexport interface PooledCredential {\n /** Stable identifier (uuid v4) for log lines + telemetry. */\n id: string;\n /** Human-readable label for `Theokit.credentialPool.list()`. */\n label: string;\n /** Provider name (matches `ProviderProfile.name`). */\n provider: string;\n /** Sort key; lower = earlier in fill_first. Unique within a pool. */\n priority: number;\n /** Provenance string. */\n source: CredentialSource;\n /** Sensitive API key. NEVER LOG UNREDACTED. */\n accessToken: string;\n /** Current health. */\n lastStatus: CredentialStatus;\n /** Epoch ms of the last status change. */\n lastStatusAt: number | undefined;\n /** HTTP code that caused exhaustion (401/402/429). */\n lastErrorCode: number | undefined;\n /** Provider-supplied \"retry after this epoch ms\" hint. Overrides cooldown defaults. */\n lastErrorResetAt: number | undefined;\n /** Bumped per successful `select()` for `least_used` strategy. Lazy-persisted. */\n requestCount: number;\n}\n\n/**\n * Serializable snapshot of a single provider's pool. Used by the\n * persistence layer (T2.1) to read/write `~/.theokit/credential-pool.json`.\n *\n * @internal\n */\nexport interface CredentialPoolSnapshot {\n provider: string;\n strategy: CredentialPoolStrategy;\n entries: PooledCredential[];\n}\n\n/**\n * Cooldown ladder by HTTP error code (ADR D125). Provider-supplied\n * `lastErrorResetAt` overrides these defaults when present.\n *\n * @internal\n */\nexport const COOLDOWN_MS: Readonly<Record<number, number>> = {\n 401: 5 * 60 * 1000, // 5 minutes — OAuth refresh can recover quickly\n 402: 60 * 60 * 1000, // 1 hour — billing quota typically hourly+\n 429: 60 * 60 * 1000, // 1 hour — daily-rate-limit windows\n};\n\n/** Default cooldown when no specific code matches. */\nexport const DEFAULT_COOLDOWN_MS = 60 * 60 * 1000;\n","/**\n * T3.4 — exponential backoff with full jitter for credential-pool retry.\n *\n * Why we need this: pre-T3.4 `pool-aware-client.ts:79-83` retried the\n * SAME credential immediately on the first 429 without any wait. When\n * every credential in the pool returned 429 (thundering herd against a\n * shared quota), the client burned through all 5 retry attempts in <1ms,\n * tripped `CredentialPoolExhaustedError`, and surfaced to the agent loop\n * as a hard failure. The user-visible symptom was \"all my keys are\n * exhausted!\" when in reality a 1-2s wait would have cleared the rate\n * limit.\n *\n * AWS Architecture Blog \"Exponential Backoff And Jitter\" (Marc Brooker,\n * 2015) demonstrates full jitter as the canonical pattern: `sleep =\n * random_between(0, base * 2^attempt)`. We extend it with a hard cap\n * (so a 32s sleep never blocks an interactive agent) and honor the\n * provider's `Retry-After` hint when present — the hint always wins over\n * the computed value when it falls within `[base, cap]`.\n *\n * @internal\n */\n\nexport interface BackoffOptions {\n /** 0-indexed retry attempt number. */\n attempt: number;\n /** Base delay in ms (multiplier for 2^attempt). Default 500. */\n baseMs?: number;\n /** Hard cap in ms — backoff never sleeps longer than this. Default 32_000. */\n capMs?: number;\n /** Provider's Retry-After hint in ms (from header). Takes precedence when in-range. */\n retryAfterMs?: number;\n /** Random source — pass a deterministic mock from tests. Default `Math.random`. */\n rng?: () => number;\n}\n\nconst DEFAULT_BASE_MS = 500;\nconst DEFAULT_CAP_MS = 32_000;\n\n/**\n * Compute the next sleep duration (ms) before retrying a failed request.\n *\n * Algorithm:\n * 1. If `retryAfterMs` is set, prefer it — clamped to `[baseMs, capMs]`.\n * The provider's hint is the most accurate available signal.\n * 2. Otherwise: `sleep = random(0, min(cap, base * 2^attempt))` — full\n * jitter per Brooker 2015 ensures coordinated clients (each with\n * different RNG seeds) spread the retry storm.\n *\n * @internal\n */\nexport function computeBackoffMs(opts: BackoffOptions): number {\n const base = opts.baseMs ?? DEFAULT_BASE_MS;\n const cap = opts.capMs ?? DEFAULT_CAP_MS;\n const rng = opts.rng ?? Math.random;\n if (opts.retryAfterMs !== undefined && opts.retryAfterMs >= 0) {\n return Math.max(base, Math.min(cap, opts.retryAfterMs));\n }\n const ceiling = Math.min(cap, base * 2 ** opts.attempt);\n return Math.floor(rng() * ceiling);\n}\n\n/**\n * `setTimeout`-based sleep that resolves early when the abort signal fires.\n * Resolves cleanly (does not throw on abort) so callers decide how to\n * react to the abort downstream.\n *\n * @internal\n */\nexport function sleepWithAbort(ms: number, signal: AbortSignal): Promise<void> {\n if (ms <= 0 || signal.aborted) return Promise.resolve();\n return new Promise<void>((resolve) => {\n const timer = setTimeout(() => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve();\n }, ms);\n const onAbort = (): void => {\n clearTimeout(timer);\n resolve();\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n}\n","/**\n * In-memory credential pool with strategy-based rotation (ADRs D123-D133).\n *\n * Pure logic. No I/O — persistence lives in `internal/persistence/\n * credential-pool-store.ts`. The pool is the single source of truth for\n * rotation state within a process; if it diverges from disk (e.g.,\n * process killed mid-rotation per EC-I), the next save heals the\n * inconsistency — at the cost of one extra HTTP 429 per crash.\n *\n * Thread safety: the pool uses an in-process async-aware mutex\n * (`cwd-mutex` D9 keyed by `credential-pool:${provider}`). Cross-process\n * safety happens at the persistence layer via `withFileLock` (D61).\n * Two Node processes sharing the same file may double-pick momentarily\n * (worst case = 1 extra 429 per race) — acceptable per ADR D128.\n *\n * @internal\n */\n\nimport { ConfigurationError } from \"../../errors.js\";\nimport { withCwdMutex } from \"../persistence/cwd-mutex.js\";\nimport {\n COOLDOWN_MS,\n type CredentialPoolSnapshot,\n type CredentialPoolStrategy,\n DEFAULT_COOLDOWN_MS,\n type PooledCredential,\n} from \"./credential-pool-types.js\";\nimport { sleepWithAbort } from \"./retry.js\";\n\n/**\n * Construct a `PooledCredential` with sane defaults.\n *\n * @internal\n */\nexport function newPooledCredential(args: {\n provider: string;\n accessToken: string;\n priority: number;\n source: string;\n label?: string;\n}): PooledCredential {\n return {\n id: globalThis.crypto.randomUUID(),\n label: args.label ?? labelFromSource(args.source, args.accessToken),\n provider: args.provider,\n priority: args.priority,\n source: args.source,\n accessToken: args.accessToken,\n lastStatus: \"ok\",\n lastStatusAt: undefined,\n lastErrorCode: undefined,\n lastErrorResetAt: undefined,\n requestCount: 0,\n };\n}\n\n/**\n * Build a human-readable label from the credential source. Used for\n * `Theokit.credentialPool.list()` UX. NEVER includes the raw token.\n *\n * @internal\n */\nfunction labelFromSource(source: string, accessToken: string): string {\n if (source.startsWith(\"env:\")) return source.slice(4);\n const masked =\n accessToken.length > 8 ? `${accessToken.slice(0, 4)}…${accessToken.slice(-4)}` : \"***\";\n return `${source}:${masked}`;\n}\n\n/**\n * Per-provider credential pool. Construct with one or more entries\n * sorted by priority. Throws `ConfigurationError` on empty input.\n *\n * EC-C: identical `accessToken`s are deduplicated during construction\n * (3 copies of \"k1\" → 1 entry). Otherwise an exhaustion event would\n * mark \"k1\" exhausted 3× wastefully.\n *\n * @internal\n */\nexport class CredentialPool {\n readonly provider: string;\n private readonly strategy: CredentialPoolStrategy;\n private entries: PooledCredential[];\n\n constructor(\n provider: string,\n entries: PooledCredential[],\n strategy: CredentialPoolStrategy = \"fill_first\",\n ) {\n if (entries.length === 0) {\n throw new ConfigurationError(\n `credential-pool: cannot construct empty pool for provider \"${provider}\"`,\n { code: \"credential_pool_empty\" },\n );\n }\n this.provider = provider;\n this.strategy = strategy;\n // EC-C: dedupe by accessToken before sort.\n this.entries = dedupeByToken(entries).sort((a, b) => a.priority - b.priority);\n }\n\n /** True if the pool was seeded with at least one entry. */\n hasCredentials(): boolean {\n return this.entries.length > 0;\n }\n\n /** True iff at least one entry is healthy (auto-healed if cooldown expired). */\n hasAvailable(): boolean {\n return this.availableEntries().length > 0;\n }\n\n /**\n * Earliest `lastErrorResetAt` across all entries, or `undefined` if no\n * entry is in cooldown. Used by `waitForAvailable` to compute the\n * upper bound of each jittered sleep window.\n *\n * @internal\n */\n earliestResetAt(): number | undefined {\n let earliest: number | undefined;\n for (const entry of this.entries) {\n if (entry.lastStatus === \"ok\") continue;\n if (entry.lastErrorResetAt === undefined) continue;\n if (earliest === undefined || entry.lastErrorResetAt < earliest) {\n earliest = entry.lastErrorResetAt;\n }\n }\n return earliest;\n }\n\n /**\n * Wait until at least one entry becomes healthy OR `maxWaitMs` elapses\n * OR `signal` aborts. Returns `true` if a healthy entry is available\n * when the call returns, `false` otherwise.\n *\n * T3.9 — Reconnect storm prevention. Concurrent callers each sleep a\n * random fraction of the window to the earliest cooldown reset (full\n * jitter — AWS Brooker 2015) so they do not all wake at the exact\n * instant cooldown expires and hammer the upstream simultaneously.\n *\n * The `sleeper` parameter is a dependency-injection seam for tests:\n * callers in production omit it and the default `setTimeout`-based\n * sleeper is used. The seam avoids the `vi.useFakeTimers()` mismatch\n * that blocked T3.4's wiring.\n *\n * @internal\n */\n async waitForAvailable(\n signal: AbortSignal,\n opts: {\n maxWaitMs: number;\n sleeper?: (ms: number, signal: AbortSignal) => Promise<void>;\n },\n ): Promise<boolean> {\n if (signal.aborted) return false;\n if (this.hasAvailable()) return true;\n const sleeper = opts.sleeper ?? sleepWithAbort;\n const deadline = Date.now() + opts.maxWaitMs;\n while (Date.now() < deadline) {\n if (signal.aborted) return false;\n const jittered = this.computeJitteredSleepMs(deadline);\n await sleeper(jittered, signal);\n if (signal.aborted) return false;\n if (this.hasAvailable()) return true;\n }\n return this.hasAvailable();\n }\n\n /**\n * Compute the next jittered sleep window (ms) for `waitForAvailable`.\n * Full-jitter cap: at most (a) time until the earliest reset (plus a\n * 50 ms grace so the entry actually heals before re-probe), or (b) the\n * remaining wait window, whichever is smaller. When no entry has a\n * known reset (rare — only if `markExhaustedAndRotate` was called\n * without `resetAtMs`), fall back to a 250 ms ceiling so we stay\n * responsive without hot-looping.\n */\n private computeJitteredSleepMs(deadline: number): number {\n const resetAt = this.earliestResetAt();\n const remaining = deadline - Date.now();\n const ceiling =\n resetAt === undefined\n ? Math.min(remaining, 250)\n : Math.min(remaining, Math.max(0, resetAt - Date.now()) + 50);\n return Math.max(0, Math.floor(Math.random() * ceiling));\n }\n\n /** Live (frozen) view of all entries. */\n list(): readonly PooledCredential[] {\n return this.entries;\n }\n\n /**\n * Pick a healthy entry according to the configured strategy.\n * Mutates `requestCount` and (for round_robin) entry order.\n * Returns null if every entry is in cooldown.\n *\n * @internal\n */\n async select(): Promise<PooledCredential | null> {\n return withCwdMutex(`credential-pool:${this.provider}`, async () => this.selectLocked());\n }\n\n /**\n * Mark the entry whose id matches `entryId` as exhausted with the\n * appropriate cooldown, then return the next healthy entry (or null).\n * If `entryId` is undefined, marks all currently-active entries (rare —\n * only used by admin reset scenarios).\n *\n * @internal\n */\n async markExhaustedAndRotate(args: {\n entryId: string;\n statusCode: number;\n resetAtMs?: number;\n }): Promise<PooledCredential | null> {\n return withCwdMutex(`credential-pool:${this.provider}`, async () => {\n const entry = this.entries.find((e) => e.id === args.entryId);\n if (entry !== undefined) {\n const now = Date.now();\n const cooldown = COOLDOWN_MS[args.statusCode] ?? DEFAULT_COOLDOWN_MS;\n entry.lastStatus = \"exhausted\";\n entry.lastStatusAt = now;\n entry.lastErrorCode = args.statusCode;\n // EC-5: negative or stale resetAt → ignore, fall through to default.\n entry.lastErrorResetAt =\n args.resetAtMs !== undefined && args.resetAtMs > now ? args.resetAtMs : now + cooldown;\n }\n return this.selectLocked();\n });\n }\n\n /** Admin escape hatch: clear all cooldowns. */\n async resetAll(): Promise<void> {\n await withCwdMutex(`credential-pool:${this.provider}`, async () => {\n for (const e of this.entries) {\n e.lastStatus = \"ok\";\n e.lastStatusAt = undefined;\n e.lastErrorCode = undefined;\n e.lastErrorResetAt = undefined;\n }\n });\n }\n\n /** Materialize for persistence. */\n toSnapshot(): CredentialPoolSnapshot {\n return {\n provider: this.provider,\n strategy: this.strategy,\n entries: this.entries.map((e) => ({ ...e })),\n };\n }\n\n /** Hydrate from a previously-saved snapshot. */\n static fromSnapshot(snapshot: CredentialPoolSnapshot): CredentialPool {\n return new CredentialPool(snapshot.provider, snapshot.entries, snapshot.strategy);\n }\n\n // ─── Internal helpers (always called inside the mutex) ───\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: strategy dispatch must branch on 4 enum values (random/least_used/round_robin/fill_first) with 1-entry short-circuits per strategy. Each branch is 5-10 lines of focused logic; extracting per-strategy methods fragments the linear narrative.\n private selectLocked(): PooledCredential | null {\n const available = this.availableEntries();\n if (available.length === 0) return null;\n\n if (this.strategy === \"random\") {\n const idx = Math.floor(Math.random() * available.length);\n const picked = available[idx];\n if (picked === undefined) return null; // unreachable; satisfies strict\n picked.requestCount += 1;\n return picked;\n }\n\n if (this.strategy === \"least_used\" && available.length > 1) {\n // EC-3: tie-break by priority ascending (first wins).\n const picked = [...available].sort(\n (a, b) => a.requestCount - b.requestCount || a.priority - b.priority,\n )[0];\n if (picked === undefined) return null;\n picked.requestCount += 1;\n return picked;\n }\n\n if (this.strategy === \"round_robin\" && available.length > 1) {\n // Pick head; move it to the end of `entries` so next call picks the next.\n const picked = available[0];\n if (picked === undefined) return null;\n picked.requestCount += 1;\n const rest = this.entries.filter((e) => e.id !== picked.id);\n // Re-assign priorities: rotated entry gets the highest priority value\n // so it sorts last after `fromSnapshot` rehydration. This makes EC-F\n // (round-robin state survives save/load) work without a separate field.\n const maxPriority = this.entries.length - 1;\n rest.forEach((e, i) => {\n e.priority = i;\n });\n picked.priority = maxPriority;\n this.entries = [...rest, picked];\n return picked;\n }\n\n // fill_first (default) and 1-entry edge case for round_robin/least_used\n const picked = available[0];\n if (picked === undefined) return null;\n picked.requestCount += 1;\n return picked;\n }\n\n private availableEntries(): PooledCredential[] {\n const now = Date.now();\n const out: PooledCredential[] = [];\n for (const entry of this.entries) {\n if (entry.lastStatus === \"ok\") {\n out.push(entry);\n continue;\n }\n // EC-1: auto-heal expired cooldowns inline.\n const resetAt = entry.lastErrorResetAt;\n if (resetAt !== undefined && now >= resetAt) {\n entry.lastStatus = \"ok\";\n entry.lastStatusAt = undefined;\n entry.lastErrorCode = undefined;\n entry.lastErrorResetAt = undefined;\n out.push(entry);\n }\n }\n return out;\n }\n}\n\n/**\n * Drop duplicates by `accessToken`. Keeps the first occurrence (lowest\n * priority wins after sort). EC-C from the edge-case review.\n */\nfunction dedupeByToken(entries: PooledCredential[]): PooledCredential[] {\n const seen = new Set<string>();\n const out: PooledCredential[] = [];\n for (const e of entries) {\n if (seen.has(e.accessToken)) continue;\n seen.add(e.accessToken);\n out.push(e);\n }\n return out;\n}\n","/**\n * Per-fork credential-pool inheritance via `AsyncLocalStorage` (ADR D131).\n *\n * Mirrors the whitelist context pattern from D111\n * (`runtime/async-local-storage.ts`). Forked sub-agents inherit the\n * parent's pool *by reference* so concurrent rotations all observe\n * the same cooldown state — the desired behavior per Hermes parity.\n *\n * @internal\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nimport type { CredentialPool } from \"./credential-pool.js\";\n\n/** ALS slot — keyed by provider name → pool. */\nconst credentialPoolStore = new AsyncLocalStorage<Map<string, CredentialPool>>();\n\n/**\n * Run `fn` with `pools` as the active credential-pool registry for the\n * current async context. Nested calls shadow the outer map (parent\n * pools are not visible to children when the child sets its own).\n *\n * @internal\n */\nexport async function withCredentialPool<T>(\n pools: Map<string, CredentialPool>,\n fn: () => Promise<T>,\n): Promise<T> {\n return credentialPoolStore.run(pools, fn);\n}\n\n/**\n * Pool registered for `provider` in the current async context, or\n * `undefined` when no `withCredentialPool(...)` scope is active.\n *\n * @internal\n */\nexport function currentCredentialPool(provider: string): CredentialPool | undefined {\n return credentialPoolStore.getStore()?.get(provider);\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TheokitAgentError } from './chunk-
|
|
1
|
+
import { TheokitAgentError } from './chunk-SG5ZWT6C.js';
|
|
2
2
|
import { existsSync, openSync, closeSync, rmSync, readFileSync, writeSync, fchmodSync } from 'fs';
|
|
3
3
|
import { hostname } from 'os';
|
|
4
4
|
|
|
@@ -103,5 +103,5 @@ function createLease(sessionPath, lockPath) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
export { SessionBusyError, acquireSessionWriter, sessionHasWriter };
|
|
106
|
-
//# sourceMappingURL=chunk-
|
|
107
|
-
//# sourceMappingURL=chunk-
|
|
106
|
+
//# sourceMappingURL=chunk-ET6PHZRX.js.map
|
|
107
|
+
//# sourceMappingURL=chunk-ET6PHZRX.js.map
|