@recombine-ai/bosun 0.10.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/README.md +12 -0
- package/bin/bosun.js +3 -44
- 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/{Dp760K13.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/BoPSJr4b.js +1 -0
- 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/{CNLU4B3p.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/CVlsAO8k.js +11 -0
- 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/{DTXUDKH5.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/DXUXXCDA.js +1 -0
- 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/{CF66fWJN.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.BCmKHiYo.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.BdQ4KFOK.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.DWNYX1kH.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-C1mGPV-S.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-DblV-OAd.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/{Button-Dsy3QY7B.js → Button-BLigKkGG.js} +2 -2
- package/build/server/chunks/Button-BLigKkGG.js.map +1 -0
- package/build/server/chunks/{ChatStepTrace-1q74tVYO.js → ChatStepTrace-CqqIjRLU.js} +47 -11
- package/build/server/chunks/ChatStepTrace-CqqIjRLU.js.map +1 -0
- package/build/server/chunks/{Input-CHFlfduw.js → Input-OdQKNdS6.js} +1 -2
- package/build/server/chunks/Input-OdQKNdS6.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/Modal-DtVsRmmO.js.map +1 -1
- package/build/server/chunks/{_layout.svelte-DJ4kDgDL.js → _layout.svelte-B-CkD4a0.js} +14 -8
- package/build/server/chunks/_layout.svelte-B-CkD4a0.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-B2lwUacy.js → _layout.svelte-BBFCF-xw.js} +8 -4
- 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-FufhMKh4.js → _page.svelte-0J2xaeyC.js} +71 -16
- 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-GaxktsuT.js → _page.svelte-C_S_qUgZ.js} +480 -72
- 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-CA3-vgl2.js → _page.svelte-Cxh9_vUl.js} +151 -100
- package/build/server/chunks/_page.svelte-Cxh9_vUl.js.map +1 -0
- package/build/server/chunks/_page.svelte-zOI_s_3J.js +2151 -0
- package/build/server/chunks/_page.svelte-zOI_s_3J.js.map +1 -0
- package/build/server/chunks/agents-context-rV2narXG.js.map +1 -1
- 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/edit-workspace-context-B1nYQ5hD.js.map +1 -1
- package/build/server/chunks/index-CSpGrMM2.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 +4 -4
- 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 +31 -0
- package/src/backend/lib/voice-agent-lifecycle.ts +16 -0
- package/src/backend/roleplay/agents.ts +189 -69
- package/src/backend/roleplay/message-dispatcher.ts +0 -7
- package/src/backend/roleplay/router.ts +14 -17
- package/src/backend/roleplay/trace-serialization.spec.ts +35 -0
- package/src/backend/roleplay/trace-serialization.ts +57 -0
- package/src/backend/roleplay/variants.ts +33 -49
- package/src/backend/tests/academy.ts +202 -100
- package/src/backend/tests/analyze.ts +0 -3
- package/src/backend/tests/data.spec.ts +139 -0
- package/src/backend/tests/data.ts +452 -68
- package/src/backend/tests/emulate-agents.ts +60 -44
- 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 +28 -2
- package/src/backend/tests/suite.ts +13 -4
- package/build/client/_app/immutable/assets/0.B4t3dozz.css +0 -1
- package/build/client/_app/immutable/assets/0.B4t3dozz.css.br +0 -0
- package/build/client/_app/immutable/assets/0.B4t3dozz.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/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/Ajb1KYIn.js +0 -1
- package/build/client/_app/immutable/chunks/Ajb1KYIn.js.br +0 -0
- package/build/client/_app/immutable/chunks/Ajb1KYIn.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/BM0jKynj.js.br +0 -0
- package/build/client/_app/immutable/chunks/BM0jKynj.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/BwtfALfL.js +0 -11
- package/build/client/_app/immutable/chunks/BwtfALfL.js.br +0 -0
- package/build/client/_app/immutable/chunks/BwtfALfL.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BxsDwlgp.js +0 -4
- package/build/client/_app/immutable/chunks/BxsDwlgp.js.br +0 -0
- package/build/client/_app/immutable/chunks/BxsDwlgp.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/C4D-JXnS.js +0 -1
- package/build/client/_app/immutable/chunks/C4D-JXnS.js.br +0 -0
- package/build/client/_app/immutable/chunks/C4D-JXnS.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/CF66fWJN.js.br +0 -0
- package/build/client/_app/immutable/chunks/CF66fWJN.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CNLU4B3p.js.br +0 -0
- package/build/client/_app/immutable/chunks/CNLU4B3p.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/CqO5tR2_.js +0 -1
- package/build/client/_app/immutable/chunks/CqO5tR2_.js.br +0 -0
- package/build/client/_app/immutable/chunks/CqO5tR2_.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/D-wf2QNc.js +0 -1
- package/build/client/_app/immutable/chunks/D-wf2QNc.js.br +0 -0
- package/build/client/_app/immutable/chunks/D-wf2QNc.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/DTXUDKH5.js.br +0 -0
- package/build/client/_app/immutable/chunks/DTXUDKH5.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/DhBm2Vx9.js +0 -1
- package/build/client/_app/immutable/chunks/DhBm2Vx9.js.br +0 -0
- package/build/client/_app/immutable/chunks/DhBm2Vx9.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dp760K13.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dp760K13.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/_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/xebNt8-Z.js +0 -1
- package/build/client/_app/immutable/chunks/xebNt8-Z.js.br +0 -0
- package/build/client/_app/immutable/chunks/xebNt8-Z.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.C4wJtvtH.js +0 -2
- package/build/client/_app/immutable/entry/app.C4wJtvtH.js.br +0 -0
- package/build/client/_app/immutable/entry/app.C4wJtvtH.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BId2vqhS.js +0 -1
- package/build/client/_app/immutable/entry/start.BId2vqhS.js.br +0 -2
- package/build/client/_app/immutable/entry/start.BId2vqhS.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.BCmKHiYo.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.BCmKHiYo.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.8skEZBEw.js +0 -1
- package/build/client/_app/immutable/nodes/1.8skEZBEw.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.8skEZBEw.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.YgmBfSWN.js +0 -5
- package/build/client/_app/immutable/nodes/10.YgmBfSWN.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.YgmBfSWN.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.D8ydco6g.js +0 -3
- package/build/client/_app/immutable/nodes/17.D8ydco6g.js.br +0 -0
- package/build/client/_app/immutable/nodes/17.D8ydco6g.js.gz +0 -0
- package/build/client/_app/immutable/nodes/18.C67CqGm5.js +0 -11
- package/build/client/_app/immutable/nodes/18.C67CqGm5.js.br +0 -0
- package/build/client/_app/immutable/nodes/18.C67CqGm5.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.BdQ4KFOK.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.BdQ4KFOK.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.nCmkz6kS.js +0 -1
- package/build/client/_app/immutable/nodes/3.nCmkz6kS.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.nCmkz6kS.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.DAwO0_Am.js +0 -1
- package/build/client/_app/immutable/nodes/4.DAwO0_Am.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.DAwO0_Am.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.C_SvvZjG.js +0 -1
- package/build/client/_app/immutable/nodes/5.C_SvvZjG.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.C_SvvZjG.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.DfOe9lph.js +0 -1
- package/build/client/_app/immutable/nodes/6.DfOe9lph.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.DfOe9lph.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.DWNYX1kH.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.DWNYX1kH.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.D7SG4T7Z.js +0 -2
- package/build/client/_app/immutable/nodes/9.D7SG4T7Z.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.D7SG4T7Z.js.gz +0 -0
- package/build/server/chunks/0-D5BNSI7t.js +0 -9
- package/build/server/chunks/0-D5BNSI7t.js.map +0 -1
- package/build/server/chunks/1-C1mGPV-S.js +0 -9
- package/build/server/chunks/10-BUo1e_HG.js +0 -9
- package/build/server/chunks/10-BUo1e_HG.js.map +0 -1
- package/build/server/chunks/17-iNFqG1My.js +0 -9
- package/build/server/chunks/17-iNFqG1My.js.map +0 -1
- package/build/server/chunks/18-u8T5X6fM.js +0 -9
- package/build/server/chunks/18-u8T5X6fM.js.map +0 -1
- package/build/server/chunks/2-tLlSByHA.js +0 -9
- package/build/server/chunks/2-tLlSByHA.js.map +0 -1
- package/build/server/chunks/3-D1KY9yVZ.js +0 -9
- package/build/server/chunks/3-D1KY9yVZ.js.map +0 -1
- package/build/server/chunks/4-7zu0cJha.js +0 -9
- package/build/server/chunks/4-7zu0cJha.js.map +0 -1
- package/build/server/chunks/5-DblV-OAd.js +0 -9
- package/build/server/chunks/6-BDC5budM.js +0 -9
- package/build/server/chunks/6-BDC5budM.js.map +0 -1
- package/build/server/chunks/8-DhmD-opq.js +0 -9
- package/build/server/chunks/8-DhmD-opq.js.map +0 -1
- package/build/server/chunks/9-BGj28GI6.js +0 -9
- package/build/server/chunks/9-BGj28GI6.js.map +0 -1
- package/build/server/chunks/AgentSidebar-XdGj1sqT.js.map +0 -1
- package/build/server/chunks/Button-Dsy3QY7B.js.map +0 -1
- package/build/server/chunks/ChatStepTrace-1q74tVYO.js.map +0 -1
- package/build/server/chunks/Input-CHFlfduw.js.map +0 -1
- package/build/server/chunks/_layout.svelte-B2lwUacy.js.map +0 -1
- package/build/server/chunks/_layout.svelte-C7JgNCIu.js.map +0 -1
- package/build/server/chunks/_layout.svelte-DJ4kDgDL.js.map +0 -1
- package/build/server/chunks/_page.svelte-Bl1WcAXy.js +0 -671
- package/build/server/chunks/_page.svelte-Bl1WcAXy.js.map +0 -1
- package/build/server/chunks/_page.svelte-CA3-vgl2.js.map +0 -1
- package/build/server/chunks/_page.svelte-DDpBtd3X.js.map +0 -1
- package/build/server/chunks/_page.svelte-FufhMKh4.js.map +0 -1
- package/build/server/chunks/_page.svelte-GaxktsuT.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,
|
|
@@ -12,8 +12,14 @@ import {
|
|
|
12
12
|
} from './suite'
|
|
13
13
|
import { getRootPath } from '../lib/get-root-path'
|
|
14
14
|
import z from 'zod'
|
|
15
|
-
import type {
|
|
15
|
+
import type {
|
|
16
|
+
ResultEntry,
|
|
17
|
+
WrittenRunReport,
|
|
18
|
+
WrittenRunReportConfig,
|
|
19
|
+
WrittenRunResult
|
|
20
|
+
} from './academy'
|
|
16
21
|
import { makeContext, makeContextDiff } from './context-utils'
|
|
22
|
+
import { listAgentReportFiles, readReportFile, reportTimestamp } from './report-files'
|
|
17
23
|
|
|
18
24
|
export type CsvRow = Record<string, string>
|
|
19
25
|
|
|
@@ -51,16 +57,52 @@ export interface TestResult {
|
|
|
51
57
|
test_id: string
|
|
52
58
|
success_rate_percent?: number
|
|
53
59
|
success_rate?: string
|
|
54
|
-
results:
|
|
60
|
+
results: LatestRunResultSummary[]
|
|
55
61
|
report_timestamp: string // ISO
|
|
56
62
|
is_old: boolean
|
|
57
63
|
previous?: PreviousTestResultSummary
|
|
58
64
|
}
|
|
65
|
+
|
|
66
|
+
export type LatestRunResultSummary = Pick<RunResult, 'success' | 'result' | 'evaluation' | 'errors'>
|
|
59
67
|
export interface TestTable {
|
|
60
68
|
rows: TestRow[]
|
|
61
69
|
headers: string[]
|
|
62
70
|
}
|
|
63
71
|
|
|
72
|
+
export interface TestRunHistorySummary {
|
|
73
|
+
timestamp: string
|
|
74
|
+
runScope: 'all' | 'pinned'
|
|
75
|
+
testCount: number
|
|
76
|
+
successCount: number
|
|
77
|
+
totalRuns: number
|
|
78
|
+
successRatePercent: number
|
|
79
|
+
successRate: string
|
|
80
|
+
passed: boolean
|
|
81
|
+
repeats: number
|
|
82
|
+
concurrency: number
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface TestRunHistoryResultEntry {
|
|
86
|
+
success: boolean
|
|
87
|
+
result: string
|
|
88
|
+
evaluation: string
|
|
89
|
+
errors: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface TestRunHistoryRow {
|
|
93
|
+
test: TestRow
|
|
94
|
+
success_rate?: string
|
|
95
|
+
success_rate_percent?: number
|
|
96
|
+
results: TestRunHistoryResultEntry[]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface TestRunHistoryDetail {
|
|
100
|
+
timestamp: string
|
|
101
|
+
runScope: 'all' | 'pinned'
|
|
102
|
+
runConfig: WrittenRunReportConfig
|
|
103
|
+
rows: TestRunHistoryRow[]
|
|
104
|
+
}
|
|
105
|
+
|
|
64
106
|
const defaultTestRow: Omit<TestRow, 'id'> = {
|
|
65
107
|
pinned: 'FALSE',
|
|
66
108
|
scenario: 'New scenario',
|
|
@@ -71,7 +113,7 @@ const defaultTestRow: Omit<TestRow, 'id'> = {
|
|
|
71
113
|
conversation: 'user: ...',
|
|
72
114
|
expected_output: '',
|
|
73
115
|
timeout: '0',
|
|
74
|
-
similarity_threshold: '
|
|
116
|
+
similarity_threshold: '50',
|
|
75
117
|
context: {}
|
|
76
118
|
}
|
|
77
119
|
|
|
@@ -90,7 +132,8 @@ export async function loadTests(agent: string): Promise<TestTable> {
|
|
|
90
132
|
seenIds.add(id)
|
|
91
133
|
// previously tests were storing context as a string...
|
|
92
134
|
const context = typeof r.context === 'string' ? JSON.parse(r.context) : r.context
|
|
93
|
-
|
|
135
|
+
const mocks = typeof r.mocks === 'string' ? JSON.parse(r.mocks) : r.mocks
|
|
136
|
+
return typeof mocks === 'undefined' ? { ...r, context, id } : { ...r, context, mocks, id }
|
|
94
137
|
})
|
|
95
138
|
if (changed) {
|
|
96
139
|
await writeSuite(agent, { ...suite, tests: rows })
|
|
@@ -106,9 +149,11 @@ export async function loadTests(agent: string): Promise<TestTable> {
|
|
|
106
149
|
'assertion_value',
|
|
107
150
|
'conversation',
|
|
108
151
|
'expected_output',
|
|
152
|
+
'capture_mode',
|
|
109
153
|
'timeout',
|
|
110
154
|
'similarity_threshold',
|
|
111
|
-
'context'
|
|
155
|
+
'context',
|
|
156
|
+
'mocks'
|
|
112
157
|
]
|
|
113
158
|
return { rows, headers }
|
|
114
159
|
})
|
|
@@ -133,12 +178,16 @@ export async function patchTest(
|
|
|
133
178
|
}
|
|
134
179
|
|
|
135
180
|
function preparePatch(current: TestRow, patch: Partial<TestRow>) {
|
|
136
|
-
|
|
181
|
+
const updated: TestRow = {
|
|
137
182
|
...defaultTestRow,
|
|
138
183
|
...current,
|
|
139
184
|
...patch,
|
|
140
185
|
context: makeContext(patch.context || current.context)
|
|
141
186
|
}
|
|
187
|
+
if (typeof patch.mocks !== 'undefined' || typeof current.mocks !== 'undefined') {
|
|
188
|
+
updated.mocks = makeContext(patch.mocks ?? current.mocks)
|
|
189
|
+
}
|
|
190
|
+
return updated
|
|
142
191
|
}
|
|
143
192
|
|
|
144
193
|
// Batch update multiple rows in a single read/write to avoid race conditions
|
|
@@ -183,6 +232,9 @@ export async function addTest(
|
|
|
183
232
|
? makeContextDiff(seed?.context, suite.meta?.defaultContext)
|
|
184
233
|
: makeContext(seed?.context)
|
|
185
234
|
}
|
|
235
|
+
if (typeof seed?.mocks !== 'undefined') {
|
|
236
|
+
newRow.mocks = makeContext(seed.mocks)
|
|
237
|
+
}
|
|
186
238
|
await writeSuite(agent, { ...(suite as any), tests: [newRow, ...suite.tests] })
|
|
187
239
|
await ensureTestExpandedByDefault(agent, newRow.id)
|
|
188
240
|
return newRow
|
|
@@ -207,7 +259,7 @@ async function ensureTestExpandedByDefault(agent: string, id: string): Promise<v
|
|
|
207
259
|
async function ensureConfigExists(agent: string): Promise<void> {
|
|
208
260
|
const existing = await tryReadConfig(agent)
|
|
209
261
|
if (existing) return
|
|
210
|
-
await writeConfig(agent, { repeats: 1, concurrency:
|
|
262
|
+
await writeConfig(agent, { repeats: 1, concurrency: 75, expandedTestIds: [] })
|
|
211
263
|
}
|
|
212
264
|
|
|
213
265
|
async function loadSuiteOrEmpty(agent: string): Promise<TestSuite> {
|
|
@@ -273,7 +325,7 @@ export async function loadConfig(agent: string): Promise<RunConfig> {
|
|
|
273
325
|
: []
|
|
274
326
|
return {
|
|
275
327
|
repeats: typeof (cfg as any).repeats === 'number' ? (cfg as any).repeats : 1,
|
|
276
|
-
concurrency: typeof (cfg as any).concurrency === 'number' ? (cfg as any).concurrency :
|
|
328
|
+
concurrency: typeof (cfg as any).concurrency === 'number' ? (cfg as any).concurrency : 75,
|
|
277
329
|
defaultContext,
|
|
278
330
|
expandedTestIds
|
|
279
331
|
}
|
|
@@ -306,72 +358,394 @@ export async function saveConfig(agent: string, cfg: RunConfig): Promise<void> {
|
|
|
306
358
|
|
|
307
359
|
// We no longer migrate legacy reports automatically. Please update old reports manually.
|
|
308
360
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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> {
|
|
313
436
|
try {
|
|
314
|
-
|
|
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 }
|
|
315
441
|
} catch {
|
|
316
|
-
return
|
|
442
|
+
return null
|
|
317
443
|
}
|
|
318
|
-
|
|
319
|
-
const suffix = `.report.json`
|
|
320
|
-
const files = entries.filter((n) => n.startsWith(prefix) && n.endsWith(suffix)).sort()
|
|
321
|
-
if (!files.length) return {}
|
|
444
|
+
}
|
|
322
445
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
+
}
|
|
327
461
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
+
}
|
|
470
|
+
|
|
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) {
|
|
339
533
|
throw new Error(
|
|
340
|
-
`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.`
|
|
341
535
|
)
|
|
342
536
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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)
|
|
353
549
|
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
354
552
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
+
)
|
|
366
585
|
}
|
|
367
|
-
if (
|
|
368
|
-
|
|
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)
|
|
369
600
|
}
|
|
370
601
|
}
|
|
602
|
+
|
|
603
|
+
if (!unresolvedLatest.size && !unresolvedPrevious.size) break
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return results
|
|
607
|
+
}
|
|
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
|
|
371
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)
|
|
372
638
|
return results
|
|
373
639
|
}
|
|
374
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
|
+
|
|
652
|
+
function summarizeReport(report: WrittenRunReport, timestamp: string): TestRunHistorySummary {
|
|
653
|
+
requireTestSnapshots(report)
|
|
654
|
+
const rows = report.rows
|
|
655
|
+
const testCount = rows.length
|
|
656
|
+
const successCount = rows.reduce((acc, row) => {
|
|
657
|
+
return acc + row.results.reduce((inner, result) => inner + (result.success ? 1 : 0), 0)
|
|
658
|
+
}, 0)
|
|
659
|
+
const totalRuns = rows.reduce((acc, row) => {
|
|
660
|
+
return acc + row.results.length
|
|
661
|
+
}, 0)
|
|
662
|
+
const successRatePercent = totalRuns ? Math.round((successCount / totalRuns) * 100) : 0
|
|
663
|
+
|
|
664
|
+
return {
|
|
665
|
+
timestamp,
|
|
666
|
+
runScope: report.run_scope,
|
|
667
|
+
testCount,
|
|
668
|
+
successCount,
|
|
669
|
+
totalRuns,
|
|
670
|
+
successRatePercent,
|
|
671
|
+
successRate: `${successCount}/${totalRuns}`,
|
|
672
|
+
passed: totalRuns > 0 && successCount === totalRuns,
|
|
673
|
+
repeats: report.run_config.repeats,
|
|
674
|
+
concurrency: report.run_config.concurrency
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
async function getRunReportDir() {
|
|
679
|
+
const projectRoot = path.dirname(await getRootPath())
|
|
680
|
+
return path.join(projectRoot, 'data', 'test_runs')
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
async function listRunReportFiles(agent: string): Promise<string[]> {
|
|
684
|
+
const outDir = await getRunReportDir()
|
|
685
|
+
let entries: string[] = []
|
|
686
|
+
try {
|
|
687
|
+
entries = await readdir(outDir)
|
|
688
|
+
} catch {
|
|
689
|
+
return []
|
|
690
|
+
}
|
|
691
|
+
return listAgentReportFiles(entries, agent)
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function requireTestSnapshots(report: WrittenRunReport): void {
|
|
695
|
+
for (const row of report.rows) {
|
|
696
|
+
if (!row.test) {
|
|
697
|
+
throw new Error(`Report ${report.run_timestamp} is missing preserved test snapshots`)
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
export async function listRunHistory(agent: string): Promise<TestRunHistorySummary[]> {
|
|
703
|
+
const outDir = await getRunReportDir()
|
|
704
|
+
const files = await listRunReportFiles(agent)
|
|
705
|
+
const summaries: TestRunHistorySummary[] = []
|
|
706
|
+
|
|
707
|
+
for (const name of files.slice().reverse()) {
|
|
708
|
+
try {
|
|
709
|
+
const parsed = await readReportFile<WrittenRunReport>(path.join(outDir, name))
|
|
710
|
+
summaries.push(summarizeReport(parsed, parsed.run_timestamp))
|
|
711
|
+
} catch (e) {
|
|
712
|
+
console.error(`Failed to read test report ${name}`, e)
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
return summaries
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
function normalizeHistoryRow(row: WrittenRunReport['rows'][number]): TestRunHistoryRow {
|
|
720
|
+
const rawResults = row.results
|
|
721
|
+
return {
|
|
722
|
+
test: row.test,
|
|
723
|
+
success_rate: row.success_rate,
|
|
724
|
+
success_rate_percent: row.success_rate_percent,
|
|
725
|
+
results: rawResults.map((result) => ({
|
|
726
|
+
success: result.success,
|
|
727
|
+
result: result.result,
|
|
728
|
+
evaluation: result.evaluation,
|
|
729
|
+
errors: result.errors
|
|
730
|
+
}))
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
export async function getRunHistory(
|
|
735
|
+
agent: string,
|
|
736
|
+
timestamp: string
|
|
737
|
+
): Promise<TestRunHistoryDetail> {
|
|
738
|
+
const report = await getRun(agent, timestamp)
|
|
739
|
+
requireTestSnapshots(report)
|
|
740
|
+
|
|
741
|
+
return {
|
|
742
|
+
timestamp: report.run_timestamp,
|
|
743
|
+
runScope: report.run_scope,
|
|
744
|
+
runConfig: report.run_config,
|
|
745
|
+
rows: report.rows.map(normalizeHistoryRow)
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
375
749
|
// Fallback UUID for Node environments without global crypto
|
|
376
750
|
function randomUUID() {
|
|
377
751
|
const rnd = () =>
|
|
@@ -384,22 +758,26 @@ function randomUUID() {
|
|
|
384
758
|
async function getRun(agent: string, ts?: string) {
|
|
385
759
|
const projectRoot = path.dirname(await getRootPath())
|
|
386
760
|
const outDir = path.join(projectRoot, 'data', 'test_runs')
|
|
387
|
-
const
|
|
388
|
-
const reportPath = ts ? path.join(outDir, file) : null
|
|
761
|
+
const logicalFile = `${agent}.${(ts || '').replace(/:/g, '-')}.report.json`
|
|
389
762
|
|
|
390
763
|
async function readLatestPath(): Promise<string> {
|
|
391
764
|
const entries = await readdir(outDir)
|
|
392
|
-
const
|
|
393
|
-
const suffix = `.report.json`
|
|
394
|
-
const candidates = entries.filter((n) => n.startsWith(prefix) && n.endsWith(suffix)).sort()
|
|
765
|
+
const candidates = listAgentReportFiles(entries, agent)
|
|
395
766
|
if (!candidates.length) throw new Error('No reports found')
|
|
396
767
|
return path.join(outDir, candidates[candidates.length - 1])
|
|
397
768
|
}
|
|
398
769
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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)
|
|
403
781
|
}
|
|
404
782
|
|
|
405
783
|
type ResultKey = keyof Omit<ResultEntry, 'success'>
|
|
@@ -442,5 +820,11 @@ export async function getResultEntry(
|
|
|
442
820
|
}
|
|
443
821
|
|
|
444
822
|
// TODO get separate RPCs for logs and traces (list)
|
|
445
|
-
|
|
823
|
+
if (Array.isArray(entryContent)) {
|
|
824
|
+
return entryContent
|
|
825
|
+
.map((entry) => (typeof entry === 'string' ? entry : JSON.stringify(entry)))
|
|
826
|
+
.join('\n')
|
|
827
|
+
}
|
|
828
|
+
if (typeof entryContent === 'string') return entryContent
|
|
829
|
+
return entryContent == null ? '' : JSON.stringify(entryContent)
|
|
446
830
|
}
|