@recombine-ai/bosun 0.11.1 → 0.11.2
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/build/client/_app/immutable/assets/0.CH-WU4JN.css +1 -0
- package/build/client/_app/immutable/assets/0.CH-WU4JN.css.br +0 -0
- package/build/client/_app/immutable/assets/0.CH-WU4JN.css.gz +0 -0
- package/build/client/_app/immutable/assets/{10.BX46PfEJ.css → 10.DIVePEYf.css} +1 -1
- package/build/client/_app/immutable/assets/10.DIVePEYf.css.br +0 -0
- package/build/client/_app/immutable/assets/10.DIVePEYf.css.gz +0 -0
- package/build/client/_app/immutable/chunks/{AocOovHL.js → 07xNIa59.js} +2 -2
- package/build/client/_app/immutable/chunks/07xNIa59.js.br +0 -0
- package/build/client/_app/immutable/chunks/07xNIa59.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BDl2QuyN.js +1 -0
- package/build/client/_app/immutable/chunks/BDl2QuyN.js.br +0 -0
- package/build/client/_app/immutable/chunks/BDl2QuyN.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CqxCTxvJ.js → BI42X8h_.js} +1 -1
- package/build/client/_app/immutable/chunks/BI42X8h_.js.br +0 -0
- package/build/client/_app/immutable/chunks/BI42X8h_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BJTwdUYr.js +1 -0
- package/build/client/_app/immutable/chunks/BJTwdUYr.js.br +0 -0
- package/build/client/_app/immutable/chunks/BJTwdUYr.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BLu86SbA.js +1 -0
- package/build/client/_app/immutable/chunks/BLu86SbA.js.br +0 -0
- package/build/client/_app/immutable/chunks/BLu86SbA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BNifnXkO.js +1 -0
- package/build/client/_app/immutable/chunks/BNifnXkO.js.br +2 -0
- package/build/client/_app/immutable/chunks/BNifnXkO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BM0jKynj.js → BPNEONy5.js} +1 -1
- package/build/client/_app/immutable/chunks/BPNEONy5.js.br +0 -0
- package/build/client/_app/immutable/chunks/BPNEONy5.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B_K7D6rX.js +2 -0
- package/build/client/_app/immutable/chunks/B_K7D6rX.js.br +0 -0
- package/build/client/_app/immutable/chunks/B_K7D6rX.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bj8jfBTi.js +1 -0
- package/build/client/_app/immutable/chunks/Bj8jfBTi.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bj8jfBTi.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BR5f5i_E.js → BoPSJr4b.js} +1 -1
- package/build/client/_app/immutable/chunks/BoPSJr4b.js.br +0 -0
- package/build/client/_app/immutable/chunks/BoPSJr4b.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BsDZe4N6.js +1 -0
- package/build/client/_app/immutable/chunks/BsDZe4N6.js.br +0 -0
- package/build/client/_app/immutable/chunks/BsDZe4N6.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CXo2BVs0.js → Buo-57fF.js} +1 -1
- package/build/client/_app/immutable/chunks/Buo-57fF.js.br +0 -0
- package/build/client/_app/immutable/chunks/Buo-57fF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{C-whEtKd.js → C39ai1md.js} +1 -1
- package/build/client/_app/immutable/chunks/C39ai1md.js.br +0 -0
- package/build/client/_app/immutable/chunks/C39ai1md.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C7he4XPB.js +1 -0
- package/build/client/_app/immutable/chunks/C7he4XPB.js.br +0 -0
- package/build/client/_app/immutable/chunks/C7he4XPB.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DYhTBLDc.js → CAjPRvNs.js} +2 -2
- package/build/client/_app/immutable/chunks/CAjPRvNs.js.br +0 -0
- package/build/client/_app/immutable/chunks/CAjPRvNs.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CJYOmGlC.js +1 -0
- package/build/client/_app/immutable/chunks/CJYOmGlC.js.br +0 -0
- package/build/client/_app/immutable/chunks/CJYOmGlC.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{UiXQyKcj.js → CMQRtcOV.js} +1 -1
- package/build/client/_app/immutable/chunks/CMQRtcOV.js.br +0 -0
- package/build/client/_app/immutable/chunks/CMQRtcOV.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{HJHYLHDn.js → CPXdAKdv.js} +1 -1
- package/build/client/_app/immutable/chunks/CPXdAKdv.js.br +0 -0
- package/build/client/_app/immutable/chunks/CPXdAKdv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DTlGvFHe.js → CVlsAO8k.js} +1 -1
- package/build/client/_app/immutable/chunks/CVlsAO8k.js.br +0 -0
- package/build/client/_app/immutable/chunks/CVlsAO8k.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DNLcpWO_.js → CX0Ekq8t.js} +1 -1
- package/build/client/_app/immutable/chunks/CX0Ekq8t.js.br +0 -0
- package/build/client/_app/immutable/chunks/CX0Ekq8t.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Ca197UDq.js +1 -0
- package/build/client/_app/immutable/chunks/Ca197UDq.js.br +0 -0
- package/build/client/_app/immutable/chunks/Ca197UDq.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CaHl3dME.js +1 -0
- package/build/client/_app/immutable/chunks/CaHl3dME.js.br +0 -0
- package/build/client/_app/immutable/chunks/CaHl3dME.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cql_e8Of.js +1 -0
- package/build/client/_app/immutable/chunks/Cql_e8Of.js.br +2 -0
- package/build/client/_app/immutable/chunks/Cql_e8Of.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Dv8m1cTI.js → CxLomzuE.js} +1 -1
- package/build/client/_app/immutable/chunks/CxLomzuE.js.br +0 -0
- package/build/client/_app/immutable/chunks/{Dv8m1cTI.js.gz → CxLomzuE.js.gz} +0 -0
- package/build/client/_app/immutable/chunks/Cyai3SOj.js +1 -0
- package/build/client/_app/immutable/chunks/Cyai3SOj.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cyai3SOj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{-g1l5aoS.js → CyvQWZoe.js} +4 -4
- package/build/client/_app/immutable/chunks/CyvQWZoe.js.br +0 -0
- package/build/client/_app/immutable/chunks/CyvQWZoe.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CzXNWL4m.js +1 -0
- package/build/client/_app/immutable/chunks/CzXNWL4m.js.br +1 -0
- package/build/client/_app/immutable/chunks/CzXNWL4m.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D-B5X35l.js +1 -0
- package/build/client/_app/immutable/chunks/D-B5X35l.js.br +0 -0
- package/build/client/_app/immutable/chunks/D-B5X35l.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DJriQKD9.js +2 -0
- package/build/client/_app/immutable/chunks/DJriQKD9.js.br +0 -0
- package/build/client/_app/immutable/chunks/DJriQKD9.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BL8rPcmP.js → DXUXXCDA.js} +1 -1
- package/build/client/_app/immutable/chunks/DXUXXCDA.js.br +0 -0
- package/build/client/_app/immutable/chunks/DXUXXCDA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CZLNSMhF.js → Dslf9OwW.js} +10 -10
- package/build/client/_app/immutable/chunks/Dslf9OwW.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dslf9OwW.js.gz +0 -0
- package/build/client/_app/immutable/chunks/QpaC6J6s.js +1 -0
- package/build/client/_app/immutable/chunks/QpaC6J6s.js.br +0 -0
- package/build/client/_app/immutable/chunks/QpaC6J6s.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{HXxIsSOO.js → iAhzu4tv.js} +1 -1
- package/build/client/_app/immutable/chunks/iAhzu4tv.js.br +0 -0
- package/build/client/_app/immutable/chunks/iAhzu4tv.js.gz +0 -0
- package/build/client/_app/immutable/chunks/i_5fJYKS.js +3 -0
- package/build/client/_app/immutable/chunks/i_5fJYKS.js.br +0 -0
- package/build/client/_app/immutable/chunks/i_5fJYKS.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Cosugfe8.js → nYD9wsyN.js} +1 -1
- package/build/client/_app/immutable/chunks/nYD9wsyN.js.br +0 -0
- package/build/client/_app/immutable/chunks/nYD9wsyN.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BIpSIzUr.js → uLGP0py1.js} +1 -1
- package/build/client/_app/immutable/chunks/uLGP0py1.js.br +0 -0
- package/build/client/_app/immutable/chunks/uLGP0py1.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.Cr7CMG-3.js +2 -0
- package/build/client/_app/immutable/entry/app.Cr7CMG-3.js.br +0 -0
- package/build/client/_app/immutable/entry/app.Cr7CMG-3.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.CDNd0Bml.js +1 -0
- package/build/client/_app/immutable/entry/start.CDNd0Bml.js.br +2 -0
- package/build/client/_app/immutable/entry/start.CDNd0Bml.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.BiYgQci8.js → 0.AyqKXI2h.js} +2 -2
- package/build/client/_app/immutable/nodes/0.AyqKXI2h.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.AyqKXI2h.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.BRmfm-nN.js +1 -0
- package/build/client/_app/immutable/nodes/1.BRmfm-nN.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.BRmfm-nN.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.D03jjkr4.js +7 -0
- package/build/client/_app/immutable/nodes/10.D03jjkr4.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.D03jjkr4.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{15.Wmhj7gOy.js → 15.BFy4oAT6.js} +1 -1
- package/build/client/_app/immutable/nodes/15.BFy4oAT6.js.br +0 -0
- package/build/client/_app/immutable/nodes/15.BFy4oAT6.js.gz +0 -0
- package/build/client/_app/immutable/nodes/17.BOYU_WTT.js +3 -0
- package/build/client/_app/immutable/nodes/17.BOYU_WTT.js.br +0 -0
- package/build/client/_app/immutable/nodes/17.BOYU_WTT.js.gz +0 -0
- package/build/client/_app/immutable/nodes/18.Ddx80a4f.js +11 -0
- package/build/client/_app/immutable/nodes/18.Ddx80a4f.js.br +0 -0
- package/build/client/_app/immutable/nodes/18.Ddx80a4f.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{2.DniQTiGZ.js → 2.CZEboQjo.js} +20 -20
- package/build/client/_app/immutable/nodes/2.CZEboQjo.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.CZEboQjo.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.dAV7FviN.js +1 -0
- package/build/client/_app/immutable/nodes/3.dAV7FviN.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.dAV7FviN.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js +1 -0
- package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.DPWuTlaI.js +1 -0
- package/build/client/_app/immutable/nodes/5.DPWuTlaI.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.DPWuTlaI.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.DY-NLaRW.js +1 -0
- package/build/client/_app/immutable/nodes/6.DY-NLaRW.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.DY-NLaRW.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{8.2oZsfzo8.js → 8.Chuqhf8W.js} +1 -1
- package/build/client/_app/immutable/nodes/8.Chuqhf8W.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.Chuqhf8W.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.BOEM8f6B.js +2 -0
- package/build/client/_app/immutable/nodes/9.BOEM8f6B.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.BOEM8f6B.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/server/chunks/0-TQkJBtAu.js +9 -0
- package/build/server/chunks/0-TQkJBtAu.js.map +1 -0
- package/build/server/chunks/1-CKxKlw1l.js +9 -0
- package/build/server/chunks/{1-BQkMpHdq.js.map → 1-CKxKlw1l.js.map} +1 -1
- package/build/server/chunks/10-Bly5Nk7M.js +9 -0
- package/build/server/chunks/10-Bly5Nk7M.js.map +1 -0
- package/build/server/chunks/{15-CZBxPMAc.js → 15-BMEAVslz.js} +2 -2
- package/build/server/chunks/{15-CZBxPMAc.js.map → 15-BMEAVslz.js.map} +1 -1
- package/build/server/chunks/17-C8CYQOYP.js +9 -0
- package/build/server/chunks/17-C8CYQOYP.js.map +1 -0
- package/build/server/chunks/18-CtXVGBbS.js +9 -0
- package/build/server/chunks/18-CtXVGBbS.js.map +1 -0
- package/build/server/chunks/2-DvD0dAh0.js +9 -0
- package/build/server/chunks/2-DvD0dAh0.js.map +1 -0
- package/build/server/chunks/3-DBzpRDK8.js +9 -0
- package/build/server/chunks/3-DBzpRDK8.js.map +1 -0
- package/build/server/chunks/4-BUNytyC2.js +9 -0
- package/build/server/chunks/4-BUNytyC2.js.map +1 -0
- package/build/server/chunks/5-DNo8_tFS.js +9 -0
- package/build/server/chunks/{5-C7KuaSU9.js.map → 5-DNo8_tFS.js.map} +1 -1
- package/build/server/chunks/6-DUMdIfbf.js +9 -0
- package/build/server/chunks/6-DUMdIfbf.js.map +1 -0
- package/build/server/chunks/8-DxTJ555u.js +9 -0
- package/build/server/chunks/8-DxTJ555u.js.map +1 -0
- package/build/server/chunks/9-B4-5k50d.js +9 -0
- package/build/server/chunks/9-B4-5k50d.js.map +1 -0
- package/build/server/chunks/{AgentSidebar-XdGj1sqT.js → AgentSidebar-DvIlda-E.js} +5 -6
- package/build/server/chunks/AgentSidebar-DvIlda-E.js.map +1 -0
- package/build/server/chunks/{ChatStepTrace-DnvGtFpb.js → ChatStepTrace-CqqIjRLU.js} +41 -7
- package/build/server/chunks/ChatStepTrace-CqqIjRLU.js.map +1 -0
- package/build/server/chunks/MockGenerator-GEpXTgX0.js +117 -0
- package/build/server/chunks/MockGenerator-GEpXTgX0.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-C5Y9nSXi.js → _layout.svelte-B-CkD4a0.js} +12 -6
- package/build/server/chunks/_layout.svelte-B-CkD4a0.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-_oqm8kmr.js → _layout.svelte-BBFCF-xw.js} +7 -3
- package/build/server/chunks/_layout.svelte-BBFCF-xw.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-C7JgNCIu.js → _layout.svelte-cPhmW5bZ.js} +3 -1
- package/build/server/chunks/_layout.svelte-cPhmW5bZ.js.map +1 -0
- package/build/server/chunks/{_page.svelte-QlYvVgTj.js → _page.svelte-0J2xaeyC.js} +70 -15
- package/build/server/chunks/_page.svelte-0J2xaeyC.js.map +1 -0
- package/build/server/chunks/{_page.svelte-BpRksNEG.js → _page.svelte-BgoX8Y6N.js} +2 -2
- package/build/server/chunks/{_page.svelte-BpRksNEG.js.map → _page.svelte-BgoX8Y6N.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-bkzbqII3.js → _page.svelte-C_S_qUgZ.js} +479 -71
- package/build/server/chunks/_page.svelte-C_S_qUgZ.js.map +1 -0
- package/build/server/chunks/{_page.svelte-DDpBtd3X.js → _page.svelte-CqGOvPRO.js} +3 -4
- package/build/server/chunks/_page.svelte-CqGOvPRO.js.map +1 -0
- package/build/server/chunks/{_page.svelte-DOhPbizt.js → _page.svelte-Cxh9_vUl.js} +34 -22
- package/build/server/chunks/_page.svelte-Cxh9_vUl.js.map +1 -0
- package/build/server/chunks/{_page.svelte-CEhhEdMi.js → _page.svelte-zOI_s_3J.js} +122 -71
- package/build/server/chunks/_page.svelte-zOI_s_3J.js.map +1 -0
- package/build/server/chunks/conversation-view-memory-BZguFlTt.js +20 -0
- package/build/server/chunks/conversation-view-memory-BZguFlTt.js.map +1 -0
- package/build/server/chunks/log-content.svelte-CfQzj-v_.js.map +1 -1
- package/build/server/chunks/{client-DzvEkq51.js → source-selection-CcN1cgXc.js} +122 -117
- package/build/server/chunks/source-selection-CcN1cgXc.js.map +1 -0
- package/build/server/index.js +1 -1
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +14 -14
- package/build/server/manifest.js.map +1 -1
- package/package.json +3 -3
- package/src/backend/lib/get-agent-factories.spec.ts +59 -0
- package/src/backend/lib/get-agent-factories.ts +31 -1
- package/src/backend/lib/voice-agent-lifecycle.test.ts +12 -8
- package/src/backend/lib/voice-agent-lifecycle.ts +2 -1
- package/src/backend/roleplay/agents.ts +18 -0
- package/src/backend/roleplay/router.ts +8 -3
- package/src/backend/tests/academy.ts +151 -93
- package/src/backend/tests/analyze.ts +0 -3
- package/src/backend/tests/data.spec.ts +139 -0
- package/src/backend/tests/data.ts +296 -65
- package/src/backend/tests/emulate-agents.ts +45 -45
- package/src/backend/tests/openai-prompt-cache.ts +68 -0
- package/src/backend/tests/promptfoo-runtime.spec.ts +41 -0
- package/src/backend/tests/promptfoo-runtime.ts +27 -0
- package/src/backend/tests/report-files.spec.ts +42 -0
- package/src/backend/tests/report-files.ts +101 -0
- package/src/backend/tests/router.ts +7 -1
- package/build/client/_app/immutable/assets/0.Dz9hcoGY.css +0 -1
- package/build/client/_app/immutable/assets/0.Dz9hcoGY.css.br +0 -0
- package/build/client/_app/immutable/assets/0.Dz9hcoGY.css.gz +0 -0
- package/build/client/_app/immutable/assets/10.BX46PfEJ.css.br +0 -0
- package/build/client/_app/immutable/assets/10.BX46PfEJ.css.gz +0 -0
- package/build/client/_app/immutable/chunks/-g1l5aoS.js.br +0 -0
- package/build/client/_app/immutable/chunks/-g1l5aoS.js.gz +0 -0
- package/build/client/_app/immutable/chunks/7E8m6dwk.js +0 -1
- package/build/client/_app/immutable/chunks/7E8m6dwk.js.br +0 -0
- package/build/client/_app/immutable/chunks/7E8m6dwk.js.gz +0 -0
- package/build/client/_app/immutable/chunks/7qRqthsa.js +0 -1
- package/build/client/_app/immutable/chunks/7qRqthsa.js.br +0 -2
- package/build/client/_app/immutable/chunks/7qRqthsa.js.gz +0 -0
- package/build/client/_app/immutable/chunks/AocOovHL.js.br +0 -0
- package/build/client/_app/immutable/chunks/AocOovHL.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B0iEglSe.js +0 -1
- package/build/client/_app/immutable/chunks/B0iEglSe.js.br +0 -0
- package/build/client/_app/immutable/chunks/B0iEglSe.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BIpSIzUr.js.br +0 -0
- package/build/client/_app/immutable/chunks/BIpSIzUr.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BL8rPcmP.js.br +0 -0
- package/build/client/_app/immutable/chunks/BL8rPcmP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BM0jKynj.js.br +0 -0
- package/build/client/_app/immutable/chunks/BM0jKynj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BR5f5i_E.js.br +0 -0
- package/build/client/_app/immutable/chunks/BR5f5i_E.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BTvePR6r.js +0 -1
- package/build/client/_app/immutable/chunks/BTvePR6r.js.br +0 -2
- package/build/client/_app/immutable/chunks/BTvePR6r.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bcu62lFq.js +0 -1
- package/build/client/_app/immutable/chunks/Bcu62lFq.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bcu62lFq.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C-whEtKd.js.br +0 -2
- package/build/client/_app/immutable/chunks/C-whEtKd.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C1WqE8CP.js +0 -1
- package/build/client/_app/immutable/chunks/C1WqE8CP.js.br +0 -2
- package/build/client/_app/immutable/chunks/C1WqE8CP.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C4SnSfql.js +0 -1
- package/build/client/_app/immutable/chunks/C4SnSfql.js.br +0 -0
- package/build/client/_app/immutable/chunks/C4SnSfql.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CSv1VDXg.js +0 -1
- package/build/client/_app/immutable/chunks/CSv1VDXg.js.br +0 -0
- package/build/client/_app/immutable/chunks/CSv1VDXg.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CXo2BVs0.js.br +0 -0
- package/build/client/_app/immutable/chunks/CXo2BVs0.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CZLNSMhF.js.br +0 -0
- package/build/client/_app/immutable/chunks/CZLNSMhF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cosugfe8.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cosugfe8.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CqxCTxvJ.js.br +0 -0
- package/build/client/_app/immutable/chunks/CqxCTxvJ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CxLG58rb.js +0 -1
- package/build/client/_app/immutable/chunks/CxLG58rb.js.br +0 -0
- package/build/client/_app/immutable/chunks/CxLG58rb.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D-puyTnN.js +0 -1
- package/build/client/_app/immutable/chunks/D-puyTnN.js.br +0 -0
- package/build/client/_app/immutable/chunks/D-puyTnN.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D4obUD_s.js +0 -1
- package/build/client/_app/immutable/chunks/D4obUD_s.js.br +0 -0
- package/build/client/_app/immutable/chunks/D4obUD_s.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D8e7SQh9.js +0 -1
- package/build/client/_app/immutable/chunks/D8e7SQh9.js.br +0 -0
- package/build/client/_app/immutable/chunks/D8e7SQh9.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DNLcpWO_.js.br +0 -0
- package/build/client/_app/immutable/chunks/DNLcpWO_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DTlGvFHe.js.br +0 -0
- package/build/client/_app/immutable/chunks/DTlGvFHe.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DYhTBLDc.js.br +0 -0
- package/build/client/_app/immutable/chunks/DYhTBLDc.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DlZKMmqG.js +0 -4
- package/build/client/_app/immutable/chunks/DlZKMmqG.js.br +0 -0
- package/build/client/_app/immutable/chunks/DlZKMmqG.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dv0gjm25.js +0 -1
- package/build/client/_app/immutable/chunks/Dv0gjm25.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dv0gjm25.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dv8m1cTI.js.br +0 -0
- package/build/client/_app/immutable/chunks/HJHYLHDn.js.br +0 -0
- package/build/client/_app/immutable/chunks/HJHYLHDn.js.gz +0 -0
- package/build/client/_app/immutable/chunks/HXxIsSOO.js.br +0 -0
- package/build/client/_app/immutable/chunks/HXxIsSOO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/UiXQyKcj.js.br +0 -0
- package/build/client/_app/immutable/chunks/UiXQyKcj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/_yZg6Sf-.js +0 -2
- package/build/client/_app/immutable/chunks/_yZg6Sf-.js.br +0 -0
- package/build/client/_app/immutable/chunks/_yZg6Sf-.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ks45gSgq.js +0 -1
- package/build/client/_app/immutable/chunks/ks45gSgq.js.br +0 -0
- package/build/client/_app/immutable/chunks/ks45gSgq.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.qxHHRD2v.js +0 -2
- package/build/client/_app/immutable/entry/app.qxHHRD2v.js.br +0 -0
- package/build/client/_app/immutable/entry/app.qxHHRD2v.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.CSxD7zE8.js +0 -1
- package/build/client/_app/immutable/entry/start.CSxD7zE8.js.br +0 -2
- package/build/client/_app/immutable/entry/start.CSxD7zE8.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.BiYgQci8.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.BiYgQci8.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.CShtqUFK.js +0 -1
- package/build/client/_app/immutable/nodes/1.CShtqUFK.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.CShtqUFK.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.dE-XOJgk.js +0 -7
- package/build/client/_app/immutable/nodes/10.dE-XOJgk.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.dE-XOJgk.js.gz +0 -0
- package/build/client/_app/immutable/nodes/15.Wmhj7gOy.js.br +0 -0
- package/build/client/_app/immutable/nodes/15.Wmhj7gOy.js.gz +0 -0
- package/build/client/_app/immutable/nodes/17.BwzZwCD0.js +0 -3
- package/build/client/_app/immutable/nodes/17.BwzZwCD0.js.br +0 -0
- package/build/client/_app/immutable/nodes/17.BwzZwCD0.js.gz +0 -0
- package/build/client/_app/immutable/nodes/18.BUMDuaQF.js +0 -11
- package/build/client/_app/immutable/nodes/18.BUMDuaQF.js.br +0 -0
- package/build/client/_app/immutable/nodes/18.BUMDuaQF.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DniQTiGZ.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DniQTiGZ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.BIO1e-qq.js +0 -1
- package/build/client/_app/immutable/nodes/3.BIO1e-qq.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.BIO1e-qq.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.BwfVTawo.js +0 -1
- package/build/client/_app/immutable/nodes/4.BwfVTawo.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.BwfVTawo.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.DdodCQcX.js +0 -1
- package/build/client/_app/immutable/nodes/5.DdodCQcX.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.DdodCQcX.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.EIjYVI_W.js +0 -1
- package/build/client/_app/immutable/nodes/6.EIjYVI_W.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.EIjYVI_W.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.2oZsfzo8.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.2oZsfzo8.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.CjWG6Ha9.js +0 -2
- package/build/client/_app/immutable/nodes/9.CjWG6Ha9.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.CjWG6Ha9.js.gz +0 -0
- package/build/server/chunks/0-S8VzkGgQ.js +0 -9
- package/build/server/chunks/0-S8VzkGgQ.js.map +0 -1
- package/build/server/chunks/1-BQkMpHdq.js +0 -9
- package/build/server/chunks/10-D1k3_nkw.js +0 -9
- package/build/server/chunks/10-D1k3_nkw.js.map +0 -1
- package/build/server/chunks/17-B72NO3Iu.js +0 -9
- package/build/server/chunks/17-B72NO3Iu.js.map +0 -1
- package/build/server/chunks/18-4kgQqgz-.js +0 -9
- package/build/server/chunks/18-4kgQqgz-.js.map +0 -1
- package/build/server/chunks/2-DO8jW3Pl.js +0 -9
- package/build/server/chunks/2-DO8jW3Pl.js.map +0 -1
- package/build/server/chunks/3-0_f3eZZi.js +0 -9
- package/build/server/chunks/3-0_f3eZZi.js.map +0 -1
- package/build/server/chunks/4-UxjRsyDW.js +0 -9
- package/build/server/chunks/4-UxjRsyDW.js.map +0 -1
- package/build/server/chunks/5-C7KuaSU9.js +0 -9
- package/build/server/chunks/6-C6EG0SXo.js +0 -9
- package/build/server/chunks/6-C6EG0SXo.js.map +0 -1
- package/build/server/chunks/8-BTSvDZhI.js +0 -9
- package/build/server/chunks/8-BTSvDZhI.js.map +0 -1
- package/build/server/chunks/9-CepnUatA.js +0 -9
- package/build/server/chunks/9-CepnUatA.js.map +0 -1
- package/build/server/chunks/AgentSidebar-XdGj1sqT.js.map +0 -1
- package/build/server/chunks/ChatStepTrace-DnvGtFpb.js.map +0 -1
- package/build/server/chunks/_layout.svelte-C5Y9nSXi.js.map +0 -1
- package/build/server/chunks/_layout.svelte-C7JgNCIu.js.map +0 -1
- package/build/server/chunks/_layout.svelte-_oqm8kmr.js.map +0 -1
- package/build/server/chunks/_page.svelte-CEhhEdMi.js.map +0 -1
- package/build/server/chunks/_page.svelte-DDpBtd3X.js.map +0 -1
- package/build/server/chunks/_page.svelte-DOhPbizt.js.map +0 -1
- package/build/server/chunks/_page.svelte-QlYvVgTj.js.map +0 -1
- package/build/server/chunks/_page.svelte-bkzbqII3.js.map +0 -1
- package/build/server/chunks/client-DzvEkq51.js.map +0 -1
- package/build/server/chunks/source-selection-eA--VEKo.js +0 -124
- package/build/server/chunks/source-selection-eA--VEKo.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { tryReadConfig, writeConfig } from './config'
|
|
2
|
-
import { access, readdir, readFile } from 'node:fs/promises'
|
|
2
|
+
import { access, readdir, readFile, stat, writeFile } from 'node:fs/promises'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import {
|
|
5
5
|
getSuiteFilePath,
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
WrittenRunResult
|
|
20
20
|
} from './academy'
|
|
21
21
|
import { makeContext, makeContextDiff } from './context-utils'
|
|
22
|
+
import { listAgentReportFiles, readReportFile, reportTimestamp } from './report-files'
|
|
22
23
|
|
|
23
24
|
export type CsvRow = Record<string, string>
|
|
24
25
|
|
|
@@ -56,11 +57,13 @@ export interface TestResult {
|
|
|
56
57
|
test_id: string
|
|
57
58
|
success_rate_percent?: number
|
|
58
59
|
success_rate?: string
|
|
59
|
-
results:
|
|
60
|
+
results: LatestRunResultSummary[]
|
|
60
61
|
report_timestamp: string // ISO
|
|
61
62
|
is_old: boolean
|
|
62
63
|
previous?: PreviousTestResultSummary
|
|
63
64
|
}
|
|
65
|
+
|
|
66
|
+
export type LatestRunResultSummary = Pick<RunResult, 'success' | 'result' | 'evaluation' | 'errors'>
|
|
64
67
|
export interface TestTable {
|
|
65
68
|
rows: TestRow[]
|
|
66
69
|
headers: string[]
|
|
@@ -256,7 +259,7 @@ async function ensureTestExpandedByDefault(agent: string, id: string): Promise<v
|
|
|
256
259
|
async function ensureConfigExists(agent: string): Promise<void> {
|
|
257
260
|
const existing = await tryReadConfig(agent)
|
|
258
261
|
if (existing) return
|
|
259
|
-
await writeConfig(agent, { repeats: 1, concurrency:
|
|
262
|
+
await writeConfig(agent, { repeats: 1, concurrency: 75, expandedTestIds: [] })
|
|
260
263
|
}
|
|
261
264
|
|
|
262
265
|
async function loadSuiteOrEmpty(agent: string): Promise<TestSuite> {
|
|
@@ -322,7 +325,7 @@ export async function loadConfig(agent: string): Promise<RunConfig> {
|
|
|
322
325
|
: []
|
|
323
326
|
return {
|
|
324
327
|
repeats: typeof (cfg as any).repeats === 'number' ? (cfg as any).repeats : 1,
|
|
325
|
-
concurrency: typeof (cfg as any).concurrency === 'number' ? (cfg as any).concurrency :
|
|
328
|
+
concurrency: typeof (cfg as any).concurrency === 'number' ? (cfg as any).concurrency : 75,
|
|
326
329
|
defaultContext,
|
|
327
330
|
expandedTestIds
|
|
328
331
|
}
|
|
@@ -355,70 +358,297 @@ export async function saveConfig(agent: string, cfg: RunConfig): Promise<void> {
|
|
|
355
358
|
|
|
356
359
|
// We no longer migrate legacy reports automatically. Please update old reports manually.
|
|
357
360
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
361
|
+
type ReportFileState = {
|
|
362
|
+
name: string
|
|
363
|
+
size: number
|
|
364
|
+
mtimeMs: number
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
type LatestResultsCacheEntry = {
|
|
368
|
+
files: ReportFileState[]
|
|
369
|
+
results: Record<string, TestResult>
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
type PersistedLatestResultsCache = LatestResultsCacheEntry & {
|
|
373
|
+
version: 1
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const latestResultsCache = new Map<string, LatestResultsCacheEntry>()
|
|
377
|
+
const latestResultsLoads = new Map<string, Promise<Record<string, TestResult>>>()
|
|
378
|
+
|
|
379
|
+
function latestResultsCachePath(outDir: string, agent: string): string {
|
|
380
|
+
return path.join(outDir, `.bosun-latest-results-${encodeURIComponent(agent)}.json`)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function reportFileStates(outDir: string, files: string[]): Promise<ReportFileState[]> {
|
|
384
|
+
return Promise.all(
|
|
385
|
+
files.map(async (name) => {
|
|
386
|
+
const info = await stat(path.join(outDir, name))
|
|
387
|
+
return { name, size: info.size, mtimeMs: info.mtimeMs }
|
|
388
|
+
})
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function sameReportFile(a: ReportFileState, b: ReportFileState): boolean {
|
|
393
|
+
return a.name === b.name && a.size === b.size && a.mtimeMs === b.mtimeMs
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function incrementalReportStart(
|
|
397
|
+
previous: ReportFileState[],
|
|
398
|
+
current: ReportFileState[]
|
|
399
|
+
): number | null {
|
|
400
|
+
if (current.length < previous.length) return -1
|
|
401
|
+
|
|
402
|
+
const sharedLength = Math.min(previous.length, current.length)
|
|
403
|
+
for (let index = 0; index < sharedLength; index += 1) {
|
|
404
|
+
if (sameReportFile(previous[index], current[index])) continue
|
|
405
|
+
return index >= Math.max(0, previous.length - 1) ? index : -1
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return current.length === previous.length ? null : previous.length
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function isPersistedLatestResultsCache(value: unknown): value is PersistedLatestResultsCache {
|
|
412
|
+
if (!value || typeof value !== 'object') return false
|
|
413
|
+
const candidate = value as Partial<PersistedLatestResultsCache>
|
|
414
|
+
return (
|
|
415
|
+
candidate.version === 1 &&
|
|
416
|
+
Array.isArray(candidate.files) &&
|
|
417
|
+
candidate.files.every(
|
|
418
|
+
(file) =>
|
|
419
|
+
file &&
|
|
420
|
+
typeof file.name === 'string' &&
|
|
421
|
+
typeof file.size === 'number' &&
|
|
422
|
+
Number.isFinite(file.size) &&
|
|
423
|
+
typeof file.mtimeMs === 'number' &&
|
|
424
|
+
Number.isFinite(file.mtimeMs)
|
|
425
|
+
) &&
|
|
426
|
+
!!candidate.results &&
|
|
427
|
+
typeof candidate.results === 'object' &&
|
|
428
|
+
!Array.isArray(candidate.results)
|
|
429
|
+
)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async function readPersistedLatestResultsCache(
|
|
433
|
+
outDir: string,
|
|
434
|
+
agent: string
|
|
435
|
+
): Promise<LatestResultsCacheEntry | null> {
|
|
362
436
|
try {
|
|
363
|
-
|
|
437
|
+
const raw = await readFile(latestResultsCachePath(outDir, agent), 'utf8')
|
|
438
|
+
const parsed: unknown = JSON.parse(raw)
|
|
439
|
+
if (!isPersistedLatestResultsCache(parsed)) return null
|
|
440
|
+
return { files: parsed.files, results: parsed.results }
|
|
364
441
|
} catch {
|
|
365
|
-
return
|
|
442
|
+
return null
|
|
366
443
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
async function persistLatestResultsCache(
|
|
447
|
+
outDir: string,
|
|
448
|
+
agent: string,
|
|
449
|
+
entry: LatestResultsCacheEntry
|
|
450
|
+
): Promise<void> {
|
|
451
|
+
try {
|
|
452
|
+
await writeFile(
|
|
453
|
+
latestResultsCachePath(outDir, agent),
|
|
454
|
+
JSON.stringify({ version: 1, ...entry } satisfies PersistedLatestResultsCache),
|
|
455
|
+
'utf8'
|
|
456
|
+
)
|
|
457
|
+
} catch {
|
|
458
|
+
// The in-memory cache still avoids repeat work for the current Bosun process.
|
|
459
|
+
}
|
|
460
|
+
}
|
|
371
461
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
462
|
+
function summarizeLatestRun(run: RunResult): LatestRunResultSummary {
|
|
463
|
+
return {
|
|
464
|
+
success: !!run?.success,
|
|
465
|
+
result: String(run?.result ?? ''),
|
|
466
|
+
evaluation: String(run?.evaluation ?? ''),
|
|
467
|
+
errors: String(run?.errors ?? '')
|
|
468
|
+
}
|
|
469
|
+
}
|
|
376
470
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
471
|
+
function reportRows(parsed: any): any[] {
|
|
472
|
+
return Array.isArray(parsed) ? parsed : Array.isArray(parsed?.rows) ? parsed.rows : []
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function resultSummary(result: TestResult): PreviousTestResultSummary {
|
|
476
|
+
return {
|
|
477
|
+
success_count: result.results.reduce((count, run) => count + (run.success ? 1 : 0), 0),
|
|
478
|
+
total_runs: result.results.length,
|
|
479
|
+
report_timestamp: result.report_timestamp
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function resultFromReportRow(
|
|
484
|
+
row: any,
|
|
485
|
+
timestamp: string,
|
|
486
|
+
latestTimestamp: string,
|
|
487
|
+
previous?: PreviousTestResultSummary
|
|
488
|
+
): TestResult {
|
|
489
|
+
return {
|
|
490
|
+
test_id: String(row.test_id),
|
|
491
|
+
success_rate_percent: row.success_rate_percent,
|
|
492
|
+
success_rate: row.success_rate,
|
|
493
|
+
results: (Array.isArray(row.results) ? (row.results as RunResult[]) : []).map(
|
|
494
|
+
summarizeLatestRun
|
|
495
|
+
),
|
|
496
|
+
report_timestamp: timestamp,
|
|
497
|
+
is_old: timestamp < latestTimestamp,
|
|
498
|
+
...(previous ? { previous } : {})
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function cloneLatestResults(results: Record<string, TestResult>): Record<string, TestResult> {
|
|
503
|
+
return Object.fromEntries(
|
|
504
|
+
Object.entries(results).map(([testId, result]) => [
|
|
505
|
+
testId,
|
|
506
|
+
{
|
|
507
|
+
...result,
|
|
508
|
+
results: result.results.map((run) => ({ ...run })),
|
|
509
|
+
...(result.previous ? { previous: { ...result.previous } } : {})
|
|
510
|
+
}
|
|
511
|
+
])
|
|
512
|
+
)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
async function mergeIncrementalReports(
|
|
516
|
+
outDir: string,
|
|
517
|
+
files: ReportFileState[],
|
|
518
|
+
startIndex: number,
|
|
519
|
+
baseResults: Record<string, TestResult>,
|
|
520
|
+
agent: string
|
|
521
|
+
): Promise<Record<string, TestResult>> {
|
|
522
|
+
const results = cloneLatestResults(baseResults)
|
|
523
|
+
const latestTimestamp = reportTimestamp(files[files.length - 1].name, agent)
|
|
524
|
+
|
|
525
|
+
for (let index = startIndex; index < files.length; index += 1) {
|
|
526
|
+
const file = files[index]
|
|
527
|
+
const timestamp = reportTimestamp(file.name, agent)
|
|
528
|
+
const parsed = await readReportFile<any>(path.join(outDir, file.name))
|
|
529
|
+
|
|
530
|
+
for (const row of reportRows(parsed)) {
|
|
531
|
+
const testId = String(row.test_id ?? '').trim()
|
|
532
|
+
if (!testId) {
|
|
388
533
|
throw new Error(
|
|
389
|
-
`Missing test_id in report ${name}. Please add 'test_id' and provenance columns manually.`
|
|
534
|
+
`Missing test_id in report ${file.name}. Please add 'test_id' and provenance columns manually.`
|
|
390
535
|
)
|
|
391
536
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
537
|
+
if (String(row.pinned_at_run ?? 'TRUE').toUpperCase() === 'FALSE') continue
|
|
538
|
+
|
|
539
|
+
const current = results[testId]
|
|
540
|
+
if (!current || timestamp > current.report_timestamp) {
|
|
541
|
+
results[testId] = resultFromReportRow(
|
|
542
|
+
row,
|
|
543
|
+
timestamp,
|
|
544
|
+
latestTimestamp,
|
|
545
|
+
current ? resultSummary(current) : undefined
|
|
546
|
+
)
|
|
547
|
+
} else if (timestamp === current.report_timestamp) {
|
|
548
|
+
results[testId] = resultFromReportRow(row, timestamp, latestTimestamp, current.previous)
|
|
400
549
|
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
401
552
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
553
|
+
for (const result of Object.values(results)) {
|
|
554
|
+
result.is_old = result.report_timestamp < latestTimestamp
|
|
555
|
+
}
|
|
556
|
+
return results
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
async function rebuildLatestResults(
|
|
560
|
+
agent: string,
|
|
561
|
+
outDir: string,
|
|
562
|
+
files: ReportFileState[]
|
|
563
|
+
): Promise<Record<string, TestResult>> {
|
|
564
|
+
const latestTimestamp = reportTimestamp(files[files.length - 1].name, agent)
|
|
565
|
+
const wantedTestIds = new Set(
|
|
566
|
+
(await readSuite(agent)).tests.map((test) => String(test.id ?? '').trim()).filter(Boolean)
|
|
567
|
+
)
|
|
568
|
+
if (!wantedTestIds.size) return {}
|
|
569
|
+
|
|
570
|
+
const unresolvedLatest = new Set(wantedTestIds)
|
|
571
|
+
const unresolvedPrevious = new Set<string>()
|
|
572
|
+
const results: Record<string, TestResult> = {}
|
|
573
|
+
|
|
574
|
+
for (let index = files.length - 1; index >= 0; index -= 1) {
|
|
575
|
+
const file = files[index]
|
|
576
|
+
const timestamp = reportTimestamp(file.name, agent)
|
|
577
|
+
const parsed = await readReportFile<any>(path.join(outDir, file.name))
|
|
578
|
+
|
|
579
|
+
for (const row of reportRows(parsed)) {
|
|
580
|
+
const testId = String(row.test_id ?? '').trim()
|
|
581
|
+
if (!testId) {
|
|
582
|
+
throw new Error(
|
|
583
|
+
`Missing test_id in report ${file.name}. Please add 'test_id' and provenance columns manually.`
|
|
584
|
+
)
|
|
413
585
|
}
|
|
414
|
-
if (
|
|
415
|
-
|
|
586
|
+
if (
|
|
587
|
+
!wantedTestIds.has(testId) ||
|
|
588
|
+
String(row.pinned_at_run ?? 'TRUE').toUpperCase() === 'FALSE'
|
|
589
|
+
)
|
|
590
|
+
continue
|
|
591
|
+
|
|
592
|
+
const current = results[testId]
|
|
593
|
+
if (!current) {
|
|
594
|
+
results[testId] = resultFromReportRow(row, timestamp, latestTimestamp)
|
|
595
|
+
unresolvedLatest.delete(testId)
|
|
596
|
+
if (timestamp === latestTimestamp) unresolvedPrevious.add(testId)
|
|
597
|
+
} else if (unresolvedPrevious.has(testId) && timestamp < current.report_timestamp) {
|
|
598
|
+
current.previous = resultSummary(resultFromReportRow(row, timestamp, latestTimestamp))
|
|
599
|
+
unresolvedPrevious.delete(testId)
|
|
416
600
|
}
|
|
417
601
|
}
|
|
602
|
+
|
|
603
|
+
if (!unresolvedLatest.size && !unresolvedPrevious.size) break
|
|
418
604
|
}
|
|
605
|
+
|
|
419
606
|
return results
|
|
420
607
|
}
|
|
421
608
|
|
|
609
|
+
async function loadLatestResultsByTestUncached(agent: string): Promise<Record<string, TestResult>> {
|
|
610
|
+
const projectRoot = path.dirname(await getRootPath())
|
|
611
|
+
const outDir = path.join(projectRoot, 'data', 'test_runs')
|
|
612
|
+
let entries: string[] = []
|
|
613
|
+
try {
|
|
614
|
+
entries = await readdir(outDir)
|
|
615
|
+
} catch {
|
|
616
|
+
return {}
|
|
617
|
+
}
|
|
618
|
+
const files = listAgentReportFiles(entries, agent)
|
|
619
|
+
if (!files.length) return {}
|
|
620
|
+
const states = await reportFileStates(outDir, files)
|
|
621
|
+
const cached =
|
|
622
|
+
latestResultsCache.get(agent) ?? (await readPersistedLatestResultsCache(outDir, agent))
|
|
623
|
+
const incrementalStart = cached ? incrementalReportStart(cached.files, states) : -1
|
|
624
|
+
if (cached && incrementalStart === null) {
|
|
625
|
+
latestResultsCache.set(agent, cached)
|
|
626
|
+
return cached.results
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
let results: Record<string, TestResult>
|
|
630
|
+
if (cached && incrementalStart !== null && incrementalStart >= 0) {
|
|
631
|
+
results = await mergeIncrementalReports(outDir, states, incrementalStart, cached.results, agent)
|
|
632
|
+
} else {
|
|
633
|
+
results = await rebuildLatestResults(agent, outDir, states)
|
|
634
|
+
}
|
|
635
|
+
const entry = { files: states, results }
|
|
636
|
+
latestResultsCache.set(agent, entry)
|
|
637
|
+
await persistLatestResultsCache(outDir, agent, entry)
|
|
638
|
+
return results
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
export function loadLatestResultsByTest(agent: string): Promise<Record<string, TestResult>> {
|
|
642
|
+
const pending = latestResultsLoads.get(agent)
|
|
643
|
+
if (pending) return pending
|
|
644
|
+
|
|
645
|
+
const load = loadLatestResultsByTestUncached(agent).finally(() => {
|
|
646
|
+
if (latestResultsLoads.get(agent) === load) latestResultsLoads.delete(agent)
|
|
647
|
+
})
|
|
648
|
+
latestResultsLoads.set(agent, load)
|
|
649
|
+
return load
|
|
650
|
+
}
|
|
651
|
+
|
|
422
652
|
function summarizeReport(report: WrittenRunReport, timestamp: string): TestRunHistorySummary {
|
|
423
653
|
requireTestSnapshots(report)
|
|
424
654
|
const rows = report.rows
|
|
@@ -458,9 +688,7 @@ async function listRunReportFiles(agent: string): Promise<string[]> {
|
|
|
458
688
|
} catch {
|
|
459
689
|
return []
|
|
460
690
|
}
|
|
461
|
-
|
|
462
|
-
const suffix = `.report.json`
|
|
463
|
-
return entries.filter((n) => n.startsWith(prefix) && n.endsWith(suffix)).sort()
|
|
691
|
+
return listAgentReportFiles(entries, agent)
|
|
464
692
|
}
|
|
465
693
|
|
|
466
694
|
function requireTestSnapshots(report: WrittenRunReport): void {
|
|
@@ -478,8 +706,7 @@ export async function listRunHistory(agent: string): Promise<TestRunHistorySumma
|
|
|
478
706
|
|
|
479
707
|
for (const name of files.slice().reverse()) {
|
|
480
708
|
try {
|
|
481
|
-
const
|
|
482
|
-
const parsed = JSON.parse(raw) as WrittenRunReport
|
|
709
|
+
const parsed = await readReportFile<WrittenRunReport>(path.join(outDir, name))
|
|
483
710
|
summaries.push(summarizeReport(parsed, parsed.run_timestamp))
|
|
484
711
|
} catch (e) {
|
|
485
712
|
console.error(`Failed to read test report ${name}`, e)
|
|
@@ -531,22 +758,26 @@ function randomUUID() {
|
|
|
531
758
|
async function getRun(agent: string, ts?: string) {
|
|
532
759
|
const projectRoot = path.dirname(await getRootPath())
|
|
533
760
|
const outDir = path.join(projectRoot, 'data', 'test_runs')
|
|
534
|
-
const
|
|
535
|
-
const reportPath = ts ? path.join(outDir, file) : null
|
|
761
|
+
const logicalFile = `${agent}.${(ts || '').replace(/:/g, '-')}.report.json`
|
|
536
762
|
|
|
537
763
|
async function readLatestPath(): Promise<string> {
|
|
538
764
|
const entries = await readdir(outDir)
|
|
539
|
-
const
|
|
540
|
-
const suffix = `.report.json`
|
|
541
|
-
const candidates = entries.filter((n) => n.startsWith(prefix) && n.endsWith(suffix)).sort()
|
|
765
|
+
const candidates = listAgentReportFiles(entries, agent)
|
|
542
766
|
if (!candidates.length) throw new Error('No reports found')
|
|
543
767
|
return path.join(outDir, candidates[candidates.length - 1])
|
|
544
768
|
}
|
|
545
769
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
770
|
+
async function readTimestampPath(): Promise<string> {
|
|
771
|
+
const entries = await readdir(outDir)
|
|
772
|
+
const candidate = listAgentReportFiles(entries, agent).find((name) =>
|
|
773
|
+
name.startsWith(logicalFile)
|
|
774
|
+
)
|
|
775
|
+
if (!candidate) throw new Error(`Report not found: ${ts}`)
|
|
776
|
+
return path.join(outDir, candidate)
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const chosen = ts ? await readTimestampPath() : await readLatestPath()
|
|
780
|
+
return readReportFile<WrittenRunResult>(chosen)
|
|
550
781
|
}
|
|
551
782
|
|
|
552
783
|
type ResultKey = keyof Omit<ResultEntry, 'success'>
|
|
@@ -5,6 +5,7 @@ import { getAgentFactories } from '../lib/get-agent-factories'
|
|
|
5
5
|
import { getImportNonce } from '../lib/import-nonce'
|
|
6
6
|
import { getRootPath } from '../lib/get-root-path'
|
|
7
7
|
import { sendCallStartMessage } from '../lib/voice-agent-lifecycle'
|
|
8
|
+
import { withTestPromptCacheBust } from './openai-prompt-cache'
|
|
8
9
|
|
|
9
10
|
export async function loadAgents(props: {
|
|
10
11
|
agentId: string
|
|
@@ -63,29 +64,31 @@ export async function loadAgents(props: {
|
|
|
63
64
|
ctx.set({ ...current, recombineRegion: region })
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
const agent = await
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
const agent = await withTestPromptCacheBust(() =>
|
|
68
|
+
factory({
|
|
69
|
+
stepTracer: stepTracer,
|
|
70
|
+
stepRegistry,
|
|
71
|
+
logger: logger,
|
|
72
|
+
scheduler: scheduler,
|
|
73
|
+
getMessages: () => history,
|
|
74
|
+
async sendMessage(message) {
|
|
75
|
+
const m =
|
|
76
|
+
typeof message === 'string' ? ({ sender: 'agent', text: message } as Message) : message
|
|
77
|
+
try {
|
|
78
|
+
history.push(m)
|
|
79
|
+
} catch {
|
|
80
|
+
// Best-effort: if history is immutable, ignore push errors
|
|
81
|
+
logger.log('Failed to push message to history:', m)
|
|
82
|
+
}
|
|
83
|
+
await sendMessage(m)
|
|
84
|
+
},
|
|
85
|
+
ctx,
|
|
86
|
+
config: {
|
|
87
|
+
isTest: true,
|
|
88
|
+
enableTimers: false
|
|
80
89
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
ctx,
|
|
84
|
-
config: {
|
|
85
|
-
isTest: true,
|
|
86
|
-
enableTimers: false
|
|
87
|
-
}
|
|
88
|
-
})
|
|
90
|
+
})
|
|
91
|
+
)
|
|
89
92
|
|
|
90
93
|
const hasSplitContext =
|
|
91
94
|
typeof mocks !== 'undefined' &&
|
|
@@ -100,32 +103,29 @@ export async function loadAgents(props: {
|
|
|
100
103
|
ctx.swap(context)
|
|
101
104
|
}
|
|
102
105
|
|
|
103
|
-
process.on('unhandledRejection', (r) => {
|
|
104
|
-
console.error('UNHANDLED', r)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
106
|
const withAbort = wrapAbort(abortSignal)
|
|
108
107
|
|
|
109
|
-
process.env.IS_TEST = 'true'
|
|
110
108
|
try {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
await withTestPromptCacheBust(async () => {
|
|
110
|
+
if (isVoiceAgent(agent)) {
|
|
111
|
+
// preparation
|
|
112
|
+
history = []
|
|
113
|
+
const callStartResult = await withAbort(agent.onCallStart())
|
|
114
|
+
await withAbort(sendCallStartMessage(callStartResult, sendMessage))
|
|
115
|
+
await withAbort(agent.waitForIdle())
|
|
117
116
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
117
|
+
// actual test
|
|
118
|
+
history = getMessages()
|
|
119
|
+
onBeforeTestResponse?.()
|
|
120
|
+
await withAbort(agent.streamResponse())
|
|
121
|
+
await withAbort(agent.waitForIdle())
|
|
122
|
+
} else {
|
|
123
|
+
history = getMessages()
|
|
124
|
+
onBeforeTestResponse?.()
|
|
125
|
+
await withAbort(agent.start())
|
|
126
|
+
await withAbort(agent.respondToMessage())
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
129
|
} catch (err) {
|
|
130
130
|
const error = err instanceof Error ? err : new Error(String(err))
|
|
131
131
|
try {
|
|
@@ -134,8 +134,8 @@ export async function loadAgents(props: {
|
|
|
134
134
|
} catch {
|
|
135
135
|
/* noop */
|
|
136
136
|
}
|
|
137
|
+
if (abortSignal?.aborted) throw error
|
|
137
138
|
}
|
|
138
|
-
process.env.IS_TEST = undefined
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
function isVoiceAgent(agent: TestAgent): agent is TestVoiceAgent {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
+
import { randomUUID } from 'node:crypto'
|
|
3
|
+
import { createRequire } from 'node:module'
|
|
4
|
+
|
|
5
|
+
const cacheBustContext = new AsyncLocalStorage<boolean>()
|
|
6
|
+
const patchedPrototypes = new WeakSet<object>()
|
|
7
|
+
|
|
8
|
+
type OpenAIResourceConstructor = {
|
|
9
|
+
prototype: {
|
|
10
|
+
create: (body: Record<string, unknown>, ...args: unknown[]) => unknown
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* OpenAI clients are constructed by the project under test, outside Bosun's control. Patch the
|
|
16
|
+
* request-resource prototypes once, then use async-local state to alter only requests made by a
|
|
17
|
+
* Tests run. Roleplay/Playground requests execute outside this context and remain unchanged.
|
|
18
|
+
*/
|
|
19
|
+
export function installTestPromptCacheBust(projectEntryPath: string): void {
|
|
20
|
+
const projectRequire = createRequire(projectEntryPath)
|
|
21
|
+
patchOpenAIResources(projectRequire)
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const enginePackagePath = projectRequire.resolve('@recombine-ai/engine/package.json')
|
|
25
|
+
patchOpenAIResources(createRequire(enginePackagePath))
|
|
26
|
+
} catch {
|
|
27
|
+
// Some projects may use OpenAI directly without @recombine-ai/engine.
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function withTestPromptCacheBust<T>(run: () => Promise<T>): Promise<T> {
|
|
32
|
+
return cacheBustContext.run(true, run)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function patchOpenAIResources(requireFromProject: NodeJS.Require): void {
|
|
36
|
+
patchResource(requireFromProject, 'openai/resources/chat/completions', 'Completions')
|
|
37
|
+
patchResource(requireFromProject, 'openai/resources/responses/responses', 'Responses')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function patchResource(
|
|
41
|
+
requireFromProject: NodeJS.Require,
|
|
42
|
+
moduleId: string,
|
|
43
|
+
exportName: string
|
|
44
|
+
): void {
|
|
45
|
+
let Resource: OpenAIResourceConstructor | undefined
|
|
46
|
+
try {
|
|
47
|
+
Resource = requireFromProject(moduleId)?.[exportName] as OpenAIResourceConstructor | undefined
|
|
48
|
+
} catch {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const prototype = Resource?.prototype
|
|
53
|
+
if (!prototype || patchedPrototypes.has(prototype) || typeof prototype.create !== 'function')
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
const originalCreate = prototype.create
|
|
57
|
+
prototype.create = function (body: Record<string, unknown>, ...args: unknown[]) {
|
|
58
|
+
if (!cacheBustContext.getStore() || !body || typeof body !== 'object') {
|
|
59
|
+
return Reflect.apply(originalCreate, this, [body, ...args])
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return Reflect.apply(originalCreate, this, [
|
|
63
|
+
{ ...body, prompt_cache_key: `bosun-test-${randomUUID()}` },
|
|
64
|
+
...args
|
|
65
|
+
])
|
|
66
|
+
}
|
|
67
|
+
patchedPrototypes.add(prototype)
|
|
68
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
4
|
+
|
|
5
|
+
import { loadPromptfooForTests } from './promptfoo-runtime'
|
|
6
|
+
|
|
7
|
+
const requireFromTest = createRequire(import.meta.url)
|
|
8
|
+
const originalLogLevel = process.env.LOG_LEVEL
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
vi.restoreAllMocks()
|
|
12
|
+
if (originalLogLevel === undefined) delete process.env.LOG_LEVEL
|
|
13
|
+
else process.env.LOG_LEVEL = originalLogLevel
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
describe('loadPromptfooForTests', () => {
|
|
17
|
+
it('keeps Promptfoo quiet without changing the project log level', async () => {
|
|
18
|
+
process.env.LOG_LEVEL = 'debug'
|
|
19
|
+
|
|
20
|
+
const runtime = loadPromptfooForTests()
|
|
21
|
+
|
|
22
|
+
expect(typeof runtime.evaluate).toBe('function')
|
|
23
|
+
expect(process.env.LOG_LEVEL).toBe('debug')
|
|
24
|
+
|
|
25
|
+
const promptfooEntry = requireFromTest.resolve('promptfoo')
|
|
26
|
+
const promptfooLogger = requireFromTest(
|
|
27
|
+
path.join(path.dirname(promptfooEntry), 'logger.js')
|
|
28
|
+
) as {
|
|
29
|
+
getLogLevel(): string
|
|
30
|
+
default: { debug(message: string): void }
|
|
31
|
+
}
|
|
32
|
+
expect(promptfooLogger.getLogLevel()).toBe('warn')
|
|
33
|
+
|
|
34
|
+
const stdoutWrite = vi.spyOn(process.stdout, 'write')
|
|
35
|
+
const marker = 'OpenAI embeddings API response: [should-not-reach-stdout]'
|
|
36
|
+
promptfooLogger.default.debug(marker)
|
|
37
|
+
await new Promise<void>((resolve) => setImmediate(resolve))
|
|
38
|
+
|
|
39
|
+
expect(stdoutWrite.mock.calls.some(([chunk]) => String(chunk).includes(marker))).toBe(false)
|
|
40
|
+
})
|
|
41
|
+
})
|