@recombine-ai/bosun 0.16.2 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/client/_app/immutable/assets/0.B4Hv80zl.css +1 -0
- package/build/client/_app/immutable/assets/0.B4Hv80zl.css.br +0 -0
- package/build/client/_app/immutable/assets/0.B4Hv80zl.css.gz +0 -0
- package/build/client/_app/immutable/assets/{2.DtxQDOXx.css → 2.Bc0Sf3YD.css} +1 -1
- package/build/client/_app/immutable/assets/2.Bc0Sf3YD.css.br +0 -0
- package/build/client/_app/immutable/assets/2.Bc0Sf3YD.css.gz +0 -0
- package/build/client/_app/immutable/chunks/A4HgFSOM.js +1 -0
- package/build/client/_app/immutable/chunks/A4HgFSOM.js.br +2 -0
- package/build/client/_app/immutable/chunks/A4HgFSOM.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DoD3q5w5.js → B00B3Gwh.js} +1 -1
- package/build/client/_app/immutable/chunks/B00B3Gwh.js.br +0 -0
- package/build/client/_app/immutable/chunks/B00B3Gwh.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B43-lfXY.js +1 -0
- package/build/client/_app/immutable/chunks/B43-lfXY.js.br +0 -0
- package/build/client/_app/immutable/chunks/B43-lfXY.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B4tOnDzM.js +1 -0
- package/build/client/_app/immutable/chunks/B4tOnDzM.js.br +2 -0
- package/build/client/_app/immutable/chunks/B4tOnDzM.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Bv1mNXs0.js → BDY0xZsU.js} +1 -1
- package/build/client/_app/immutable/chunks/BDY0xZsU.js.br +0 -0
- package/build/client/_app/immutable/chunks/BDY0xZsU.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{UqjCSZmA.js → BIxSqfKT.js} +1 -1
- package/build/client/_app/immutable/chunks/BIxSqfKT.js.br +0 -0
- package/build/client/_app/immutable/chunks/BIxSqfKT.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{r0sHOWrM.js → BQJs3N6E.js} +1 -1
- package/build/client/_app/immutable/chunks/BQJs3N6E.js.br +0 -0
- package/build/client/_app/immutable/chunks/BQJs3N6E.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BHHfrjbV.js → BXh99ZNk.js} +1 -1
- package/build/client/_app/immutable/chunks/BXh99ZNk.js.br +0 -0
- package/build/client/_app/immutable/chunks/BXh99ZNk.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B_RIpUuZ.js +1 -0
- package/build/client/_app/immutable/chunks/B_RIpUuZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/B_RIpUuZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CEHsomMo.js → Bd-a6hgR.js} +1 -1
- package/build/client/_app/immutable/chunks/Bd-a6hgR.js.br +0 -0
- package/build/client/_app/immutable/chunks/{CEHsomMo.js.gz → Bd-a6hgR.js.gz} +0 -0
- package/build/client/_app/immutable/chunks/{Dm49pcMm.js → BftRRaUE.js} +1 -1
- package/build/client/_app/immutable/chunks/BftRRaUE.js.br +0 -0
- package/build/client/_app/immutable/chunks/BftRRaUE.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bm4FdRXc.js +1 -0
- package/build/client/_app/immutable/chunks/Bm4FdRXc.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bm4FdRXc.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Ddv5LgEc.js → BmdpEiKy.js} +2 -2
- package/build/client/_app/immutable/chunks/BmdpEiKy.js.br +0 -0
- package/build/client/_app/immutable/chunks/BmdpEiKy.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{B5zf3x62.js → BxZ1WgZm.js} +1 -1
- package/build/client/_app/immutable/chunks/BxZ1WgZm.js.br +0 -0
- package/build/client/_app/immutable/chunks/BxZ1WgZm.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Cp_st-64.js → C9LBPFJH.js} +1 -1
- package/build/client/_app/immutable/chunks/C9LBPFJH.js.br +0 -0
- package/build/client/_app/immutable/chunks/C9LBPFJH.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{JcG4w2gF.js → CH3XJDoO.js} +1 -1
- package/build/client/_app/immutable/chunks/CH3XJDoO.js.br +0 -0
- package/build/client/_app/immutable/chunks/CH3XJDoO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CWVkeRMt.js +3 -0
- package/build/client/_app/immutable/chunks/CWVkeRMt.js.br +0 -0
- package/build/client/_app/immutable/chunks/CWVkeRMt.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CXfw8fai.js +1 -0
- package/build/client/_app/immutable/chunks/CXfw8fai.js.br +2 -0
- package/build/client/_app/immutable/chunks/CXfw8fai.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DfcDjAv-.js → Clfp4Zf_.js} +1 -1
- package/build/client/_app/immutable/chunks/Clfp4Zf_.js.br +0 -0
- package/build/client/_app/immutable/chunks/Clfp4Zf_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DneQQ7D5.js → Cr5XqyuF.js} +1 -1
- package/build/client/_app/immutable/chunks/Cr5XqyuF.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cr5XqyuF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Bmd5zhWC.js → CxzD8Edk.js} +1 -1
- package/build/client/_app/immutable/chunks/CxzD8Edk.js.br +0 -0
- package/build/client/_app/immutable/chunks/CxzD8Edk.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D6iBTJD_.js +1 -0
- package/build/client/_app/immutable/chunks/D6iBTJD_.js.br +0 -0
- package/build/client/_app/immutable/chunks/D6iBTJD_.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CJcZ2GVC.js → DB-s6wHX.js} +1 -1
- package/build/client/_app/immutable/chunks/DB-s6wHX.js.br +0 -0
- package/build/client/_app/immutable/chunks/DB-s6wHX.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D_LCsjN0.js +1 -0
- package/build/client/_app/immutable/chunks/D_LCsjN0.js.br +0 -0
- package/build/client/_app/immutable/chunks/D_LCsjN0.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dd-8KgV2.js +2 -0
- package/build/client/_app/immutable/chunks/Dd-8KgV2.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dd-8KgV2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{pPpIn6tB.js → DdmShlNz.js} +1 -1
- package/build/client/_app/immutable/chunks/DdmShlNz.js.br +0 -0
- package/build/client/_app/immutable/chunks/DdmShlNz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Df5gZp_x.js +1 -0
- package/build/client/_app/immutable/chunks/Df5gZp_x.js.br +0 -0
- package/build/client/_app/immutable/chunks/Df5gZp_x.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{wcTQUnZO.js → Dfg-7N3Z.js} +1 -1
- package/build/client/_app/immutable/chunks/Dfg-7N3Z.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dfg-7N3Z.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{BnqH7p3U.js → DnsnkygW.js} +2 -2
- package/build/client/_app/immutable/chunks/DnsnkygW.js.br +0 -0
- package/build/client/_app/immutable/chunks/DnsnkygW.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{D8mA-uZ4.js → Do-VHn0U.js} +1 -1
- package/build/client/_app/immutable/chunks/Do-VHn0U.js.br +0 -0
- package/build/client/_app/immutable/chunks/Do-VHn0U.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DyBwTDJp.js → DtC6KNFZ.js} +1 -1
- package/build/client/_app/immutable/chunks/DtC6KNFZ.js.br +0 -0
- package/build/client/_app/immutable/chunks/DtC6KNFZ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DtXXy94i.js → LEDvCGy2.js} +1 -1
- package/build/client/_app/immutable/chunks/LEDvCGy2.js.br +0 -0
- package/build/client/_app/immutable/chunks/LEDvCGy2.js.gz +0 -0
- package/build/client/_app/immutable/chunks/PrCSpCPg.js +1 -0
- package/build/client/_app/immutable/chunks/PrCSpCPg.js.br +0 -0
- package/build/client/_app/immutable/chunks/PrCSpCPg.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Si6VyCMt.js +8 -0
- package/build/client/_app/immutable/chunks/Si6VyCMt.js.br +0 -0
- package/build/client/_app/immutable/chunks/Si6VyCMt.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Dy2CFyD6.js → W2LAs4ks.js} +1 -1
- package/build/client/_app/immutable/chunks/W2LAs4ks.js.br +0 -0
- package/build/client/_app/immutable/chunks/W2LAs4ks.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DCXjBDmY.js → cxBf7mxj.js} +1 -1
- package/build/client/_app/immutable/chunks/cxBf7mxj.js.br +0 -0
- package/build/client/_app/immutable/chunks/cxBf7mxj.js.gz +0 -0
- package/build/client/_app/immutable/chunks/g6pAcE-g.js +1 -0
- package/build/client/_app/immutable/chunks/g6pAcE-g.js.br +0 -0
- package/build/client/_app/immutable/chunks/g6pAcE-g.js.gz +0 -0
- package/build/client/_app/immutable/chunks/nt0OWMrn.js +2 -0
- package/build/client/_app/immutable/chunks/nt0OWMrn.js.br +0 -0
- package/build/client/_app/immutable/chunks/nt0OWMrn.js.gz +0 -0
- package/build/client/_app/immutable/chunks/r0jEdDRH.js +1 -0
- package/build/client/_app/immutable/chunks/r0jEdDRH.js.br +0 -0
- package/build/client/_app/immutable/chunks/r0jEdDRH.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.C6BwoHPB.js +2 -0
- package/build/client/_app/immutable/entry/app.C6BwoHPB.js.br +0 -0
- package/build/client/_app/immutable/entry/app.C6BwoHPB.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.DtdsXA4D.js +1 -0
- package/build/client/_app/immutable/entry/start.DtdsXA4D.js.br +2 -0
- package/build/client/_app/immutable/entry/start.DtdsXA4D.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.nG3DdvIX.js → 0.Be4zhwyU.js} +1 -1
- package/build/client/_app/immutable/nodes/0.Be4zhwyU.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.Be4zhwyU.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.DU-ZnWjM.js +1 -0
- package/build/client/_app/immutable/nodes/1.DU-ZnWjM.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.DU-ZnWjM.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.CnYak_rj.js +28 -0
- package/build/client/_app/immutable/nodes/10.CnYak_rj.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.CnYak_rj.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{15.D2I0y3ys.js → 15.DZxmOvq_.js} +1 -1
- package/build/client/_app/immutable/nodes/15.DZxmOvq_.js.br +0 -0
- package/build/client/_app/immutable/nodes/15.DZxmOvq_.js.gz +0 -0
- package/build/client/_app/immutable/nodes/17.6u3BMaGP.js +3 -0
- package/build/client/_app/immutable/nodes/17.6u3BMaGP.js.br +0 -0
- package/build/client/_app/immutable/nodes/17.6u3BMaGP.js.gz +0 -0
- package/build/client/_app/immutable/nodes/18.B74hqK3k.js +11 -0
- package/build/client/_app/immutable/nodes/18.B74hqK3k.js.br +0 -0
- package/build/client/_app/immutable/nodes/18.B74hqK3k.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.tsu7vFWw.js +35 -0
- package/build/client/_app/immutable/nodes/2.tsu7vFWw.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.tsu7vFWw.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.uagy_HG1.js +1 -0
- package/build/client/_app/immutable/nodes/3.uagy_HG1.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.uagy_HG1.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{4.C8VxVbVn.js → 4.DMbfNKYL.js} +1 -1
- package/build/client/_app/immutable/nodes/4.DMbfNKYL.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.DMbfNKYL.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{5.BjrnKYaA.js → 5.DpWrPX3L.js} +1 -1
- package/build/client/_app/immutable/nodes/5.DpWrPX3L.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.DpWrPX3L.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{6.C_hEzPrU.js → 6.D7wtfdve.js} +1 -1
- package/build/client/_app/immutable/nodes/6.D7wtfdve.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.D7wtfdve.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.Bo50UIdy.js +2 -0
- package/build/client/_app/immutable/nodes/8.Bo50UIdy.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.Bo50UIdy.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{9.DVspuDYY.js → 9.B2xXyeYp.js} +1 -1
- package/build/client/_app/immutable/nodes/9.B2xXyeYp.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.B2xXyeYp.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-BFGwTpfy.js +9 -0
- package/build/server/chunks/0-BFGwTpfy.js.map +1 -0
- package/build/server/chunks/1-B_bvJcKE.js +9 -0
- package/build/server/chunks/{1-C0HdTvXW.js.map → 1-B_bvJcKE.js.map} +1 -1
- package/build/server/chunks/10-CVT4oo13.js +9 -0
- package/build/server/chunks/10-CVT4oo13.js.map +1 -0
- package/build/server/chunks/15-B6cgfT20.js +9 -0
- package/build/server/chunks/{15-BQcb9C_y.js.map → 15-B6cgfT20.js.map} +1 -1
- package/build/server/chunks/17-fi2rlX_T.js +9 -0
- package/build/server/chunks/17-fi2rlX_T.js.map +1 -0
- package/build/server/chunks/18-CdypjWwX.js +9 -0
- package/build/server/chunks/18-CdypjWwX.js.map +1 -0
- package/build/server/chunks/2-CkwyQ1U4.js +9 -0
- package/build/server/chunks/2-CkwyQ1U4.js.map +1 -0
- package/build/server/chunks/3-BXLsvzRq.js +9 -0
- package/build/server/chunks/3-BXLsvzRq.js.map +1 -0
- package/build/server/chunks/4-C3Me7zq3.js +9 -0
- package/build/server/chunks/4-C3Me7zq3.js.map +1 -0
- package/build/server/chunks/5-DYS5LmK2.js +9 -0
- package/build/server/chunks/{5-CBdMutbv.js.map → 5-DYS5LmK2.js.map} +1 -1
- package/build/server/chunks/6-Cp6GA1FS.js +9 -0
- package/build/server/chunks/{6-FoEi7ggm.js.map → 6-Cp6GA1FS.js.map} +1 -1
- package/build/server/chunks/8-Csly_0ub.js +9 -0
- package/build/server/chunks/8-Csly_0ub.js.map +1 -0
- package/build/server/chunks/9-C1UZfJDT.js +9 -0
- package/build/server/chunks/9-C1UZfJDT.js.map +1 -0
- package/build/server/chunks/{AgentSidebar-BWBnqHnV.js → AgentSidebar-BqqgUCT2.js} +4 -4
- package/build/server/chunks/{AgentSidebar-BWBnqHnV.js.map → AgentSidebar-BqqgUCT2.js.map} +1 -1
- package/build/server/chunks/{Button-DH7gdU0s.js → Button-Huqrc2wY.js} +2 -2
- package/build/server/chunks/{Button-DH7gdU0s.js.map → Button-Huqrc2wY.js.map} +1 -1
- package/build/server/chunks/{ChatStepTrace-BivoNYOY.js → ChatStepTrace-CmyR6NOL.js} +21 -7
- package/build/server/chunks/ChatStepTrace-CmyR6NOL.js.map +1 -0
- package/build/server/chunks/{Icon-DP1YKCRV.js → Icon-BJfQMgGu.js} +2 -2
- package/build/server/chunks/{Icon-DP1YKCRV.js.map → Icon-BJfQMgGu.js.map} +1 -1
- package/build/server/chunks/{Input-CUJ9Cg7b.js → Input-D-uxKbuX.js} +2 -2
- package/build/server/chunks/{Input-CUJ9Cg7b.js.map → Input-D-uxKbuX.js.map} +1 -1
- package/build/server/chunks/{Modal-CGJuNpZ0.js → Modal-B_fVcSLK.js} +5 -5
- package/build/server/chunks/{Modal-CGJuNpZ0.js.map → Modal-B_fVcSLK.js.map} +1 -1
- package/build/server/chunks/{Panel-zvjxe036.js → Panel-mV52rK4D.js} +2 -2
- package/build/server/chunks/{Panel-zvjxe036.js.map → Panel-mV52rK4D.js.map} +1 -1
- package/build/server/chunks/{Tab-CUkyLqbN.js → Tab-tFWS4zzn.js} +8 -11
- package/build/server/chunks/Tab-tFWS4zzn.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-B-0DHjLB.js → _layout.svelte-BjBXlTt-.js} +8 -8
- package/build/server/chunks/{_layout.svelte-B-0DHjLB.js.map → _layout.svelte-BjBXlTt-.js.map} +1 -1
- package/build/server/chunks/{_layout.svelte-BnrWYuLv.js → _layout.svelte-Bv87ouh2.js} +9 -10
- package/build/server/chunks/{_layout.svelte-BnrWYuLv.js.map → _layout.svelte-Bv87ouh2.js.map} +1 -1
- package/build/server/chunks/{_layout.svelte-B88WMyk0.js → _layout.svelte-CHJ_Q00I.js} +130 -37
- package/build/server/chunks/_layout.svelte-CHJ_Q00I.js.map +1 -0
- package/build/server/chunks/{_layout.svelte-wi6o5Ecu.js → _layout.svelte-DPP4TE7p.js} +305 -46
- package/build/server/chunks/_layout.svelte-DPP4TE7p.js.map +1 -0
- package/build/server/chunks/{_page.svelte-DhFNFMnl.js → _page.svelte-CDGPJuVG.js} +11 -11
- package/build/server/chunks/_page.svelte-CDGPJuVG.js.map +1 -0
- package/build/server/chunks/{_page.svelte-CeS_21Ho.js → _page.svelte-CXjHZqjR.js} +22 -20
- package/build/server/chunks/_page.svelte-CXjHZqjR.js.map +1 -0
- package/build/server/chunks/{_page.svelte-BoHt2cpr.js → _page.svelte-CZXVTrWJ.js} +5 -5
- package/build/server/chunks/{_page.svelte-BoHt2cpr.js.map → _page.svelte-CZXVTrWJ.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-h_J4lNT9.js → _page.svelte-CvPyTgqp.js} +6 -6
- package/build/server/chunks/{_page.svelte-h_J4lNT9.js.map → _page.svelte-CvPyTgqp.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-DHSYVLoj.js → _page.svelte-DHE9UaxV.js} +11 -12
- package/build/server/chunks/{_page.svelte-DHSYVLoj.js.map → _page.svelte-DHE9UaxV.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-CHgQjBQj.js → _page.svelte-DSLYtPvs.js} +2 -2
- package/build/server/chunks/{_page.svelte-CHgQjBQj.js.map → _page.svelte-DSLYtPvs.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-57pyhszV.js → _page.svelte-Yjru58jZ.js} +20 -107
- package/build/server/chunks/_page.svelte-Yjru58jZ.js.map +1 -0
- package/build/server/chunks/{_page.svelte-BSVJ2d2G.js → _page.svelte-nQ_thh1S.js} +186 -104
- package/build/server/chunks/_page.svelte-nQ_thh1S.js.map +1 -0
- package/build/server/chunks/{agents-context-CbFsZmi5.js → agents-context-BdX2j9DI.js} +2 -2
- package/build/server/chunks/{agents-context-CbFsZmi5.js.map → agents-context-BdX2j9DI.js.map} +1 -1
- package/build/server/chunks/{Dropdown-DnARl86Q.js → chat-content.svelte-x4GXVNwU.js} +16 -4
- package/build/server/chunks/chat-content.svelte-x4GXVNwU.js.map +1 -0
- package/build/server/chunks/{client-DO-hqMbT.js → client-D1Y2ZQJN.js} +4 -4
- package/build/server/chunks/{client-DO-hqMbT.js.map → client-D1Y2ZQJN.js.map} +1 -1
- package/build/server/chunks/{conversation-view-memory-CtbUZd_L.js → conversation-view-memory-CMgSQUdu.js} +2 -2
- package/build/server/chunks/{conversation-view-memory-CtbUZd_L.js.map → conversation-view-memory-CMgSQUdu.js.map} +1 -1
- package/build/server/chunks/{edit-workspace-context-tyHBPujC.js → edit-workspace-context-ClnqepZ4.js} +2 -2
- package/build/server/chunks/{edit-workspace-context-tyHBPujC.js.map → edit-workspace-context-ClnqepZ4.js.map} +1 -1
- package/build/server/chunks/error.svelte-B5VtiMGd.js +15 -0
- package/build/server/chunks/{error.svelte-DmKKN1RU.js.map → error.svelte-B5VtiMGd.js.map} +1 -1
- package/build/server/chunks/{index-LMsd1ih2.js → index-Do08hKbr.js} +8 -5
- package/build/server/chunks/index-Do08hKbr.js.map +1 -0
- package/build/server/chunks/{index-server-BRr5mZEq.js → index-server-CHFC9Apw.js} +2 -2
- package/build/server/chunks/index-server-CHFC9Apw.js.map +1 -0
- package/build/server/chunks/{index2-ClgfzZ89.js → index2-B_Xf0ugP.js} +3 -3
- package/build/server/chunks/{index2-ClgfzZ89.js.map → index2-B_Xf0ugP.js.map} +1 -1
- package/build/server/chunks/{index3-BdRUAffj.js → index3-C9gC86lU.js} +2 -2
- package/build/server/chunks/{index3-BdRUAffj.js.map → index3-C9gC86lU.js.map} +1 -1
- package/build/server/chunks/log-content.svelte-D5gN0L7Q.js +7 -0
- package/build/server/chunks/log-content.svelte-D5gN0L7Q.js.map +1 -0
- package/build/server/chunks/{root-4q2Pr7ck.js → root-DoSdrdfw.js} +2 -2
- package/build/server/chunks/root-DoSdrdfw.js.map +1 -0
- package/build/server/chunks/{source-selection-CcN1cgXc.js → source-selection-1aBuBpkL.js} +45 -2
- package/build/server/chunks/source-selection-1aBuBpkL.js.map +1 -0
- package/build/server/chunks/{state.svelte-Bd1i-u49.js → state.svelte-B2OaeWMp.js} +3 -3
- package/build/server/chunks/{state.svelte-Bd1i-u49.js.map → state.svelte-B2OaeWMp.js.map} +1 -1
- package/build/server/index.js +4 -4
- 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 +1 -5
- package/src/backend/roleplay/agent-loader.ts +49 -0
- package/src/backend/roleplay/prompt-edit-proposals.ts +38 -4
- package/src/backend/roleplay/prompt-file.ts +59 -0
- package/src/backend/roleplay/router.spec.ts +75 -0
- package/src/backend/roleplay/router.ts +123 -69
- package/src/backend/roleplay/suggest-file-edit.ts +160 -0
- package/src/backend/roleplay/suggest-prompt-edit.ts +9 -138
- package/src/backend/roleplay/suggest-test-fix.spec.ts +50 -0
- package/src/backend/roleplay/suggest-test-fix.ts +65 -0
- package/src/backend/roleplay/test-fix-proposal-acceptance.spec.ts +253 -0
- package/src/backend/tests/academy.ts +16 -2
- package/src/backend/tests/data.spec.ts +81 -2
- package/src/backend/tests/data.ts +74 -4
- package/src/backend/tests/router.ts +101 -36
- package/src/backend/tests/suggest-test-history-edit.spec.ts +20 -0
- package/src/backend/tests/suggest-test-history-edit.ts +52 -0
- package/src/backend/tests/test-edit-source.spec.ts +61 -0
- package/src/backend/tests/test-edit-source.ts +34 -0
- package/src/backend/tests/test-history-edit-proposals.ts +69 -0
- package/src/backend/tests/test-history-overrides.spec.ts +24 -0
- package/src/backend/tests/test-selection.ts +27 -0
- package/src/lib/api-telescope/client.ts +25 -0
- package/src/lib/api-telescope/source-selection.spec.ts +44 -0
- package/src/lib/api-telescope/source-selection.ts +28 -0
- package/src/lib/api-telescope/types.ts +28 -0
- package/src/lib/components/Modal.svelte +1 -1
- package/src/lib/components/Tab.svelte +10 -12
- package/src/lib/edit/edit-workspace-context.ts +29 -0
- package/src/lib/edit/test-ai-edit.spec.ts +31 -0
- package/src/lib/edit/test-ai-edit.ts +52 -0
- package/src/lib/monaco/bosun-theme.ts +6 -83
- package/src/lib/monaco/editor-appearance.spec.ts +84 -0
- package/src/lib/monaco/editor-appearance.ts +154 -0
- package/src/lib/monaco/editor-decorations.spec.ts +91 -0
- package/src/lib/monaco/editor-decorations.ts +133 -0
- package/src/lib/tests/assertion-types.ts +74 -0
- package/build/client/_app/immutable/assets/0.Rrc0O--C.css +0 -1
- package/build/client/_app/immutable/assets/0.Rrc0O--C.css.br +0 -0
- package/build/client/_app/immutable/assets/0.Rrc0O--C.css.gz +0 -0
- package/build/client/_app/immutable/assets/2.DtxQDOXx.css.br +0 -0
- package/build/client/_app/immutable/assets/2.DtxQDOXx.css.gz +0 -0
- package/build/client/_app/immutable/chunks/B1HdnQRQ.js +0 -1
- package/build/client/_app/immutable/chunks/B1HdnQRQ.js.br +0 -0
- package/build/client/_app/immutable/chunks/B1HdnQRQ.js.gz +0 -0
- package/build/client/_app/immutable/chunks/B5zf3x62.js.br +0 -0
- package/build/client/_app/immutable/chunks/B5zf3x62.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BHHfrjbV.js.br +0 -2
- package/build/client/_app/immutable/chunks/BHHfrjbV.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BU7aeYn4.js +0 -1
- package/build/client/_app/immutable/chunks/BU7aeYn4.js.br +0 -0
- package/build/client/_app/immutable/chunks/BU7aeYn4.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BbGYcfPY.js +0 -3
- package/build/client/_app/immutable/chunks/BbGYcfPY.js.br +0 -0
- package/build/client/_app/immutable/chunks/BbGYcfPY.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bmd5zhWC.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bmd5zhWC.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BnqH7p3U.js.br +0 -0
- package/build/client/_app/immutable/chunks/BnqH7p3U.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bog028VR.js +0 -1
- package/build/client/_app/immutable/chunks/Bog028VR.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bog028VR.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Bv1mNXs0.js.br +0 -0
- package/build/client/_app/immutable/chunks/Bv1mNXs0.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BvjXTrOz.js +0 -1
- package/build/client/_app/immutable/chunks/BvjXTrOz.js.br +0 -0
- package/build/client/_app/immutable/chunks/BvjXTrOz.js.gz +0 -0
- package/build/client/_app/immutable/chunks/C1R-s4aD.js +0 -1
- package/build/client/_app/immutable/chunks/C1R-s4aD.js.br +0 -0
- package/build/client/_app/immutable/chunks/C1R-s4aD.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CC1OcqhR.js +0 -1
- package/build/client/_app/immutable/chunks/CC1OcqhR.js.br +0 -0
- package/build/client/_app/immutable/chunks/CC1OcqhR.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CEHsomMo.js.br +0 -0
- package/build/client/_app/immutable/chunks/CJcZ2GVC.js.br +0 -0
- package/build/client/_app/immutable/chunks/CJcZ2GVC.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CN555stc.js +0 -2
- package/build/client/_app/immutable/chunks/CN555stc.js.br +0 -0
- package/build/client/_app/immutable/chunks/CN555stc.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CQg2Mme0.js +0 -9
- package/build/client/_app/immutable/chunks/CQg2Mme0.js.br +0 -0
- package/build/client/_app/immutable/chunks/CQg2Mme0.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Cp_st-64.js.br +0 -0
- package/build/client/_app/immutable/chunks/Cp_st-64.js.gz +0 -0
- package/build/client/_app/immutable/chunks/D8mA-uZ4.js.br +0 -0
- package/build/client/_app/immutable/chunks/D8mA-uZ4.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DCXjBDmY.js.br +0 -0
- package/build/client/_app/immutable/chunks/DCXjBDmY.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DSLyVE1P.js +0 -1
- package/build/client/_app/immutable/chunks/DSLyVE1P.js.br +0 -0
- package/build/client/_app/immutable/chunks/DSLyVE1P.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Ddv5LgEc.js.br +0 -0
- package/build/client/_app/immutable/chunks/Ddv5LgEc.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DfcDjAv-.js.br +0 -0
- package/build/client/_app/immutable/chunks/DfcDjAv-.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dg0A8wPA.js +0 -1
- package/build/client/_app/immutable/chunks/Dg0A8wPA.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dg0A8wPA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dm49pcMm.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dm49pcMm.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DneQQ7D5.js.br +0 -0
- package/build/client/_app/immutable/chunks/DneQQ7D5.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DoD3q5w5.js.br +0 -0
- package/build/client/_app/immutable/chunks/DoD3q5w5.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DtXXy94i.js.br +0 -0
- package/build/client/_app/immutable/chunks/DtXXy94i.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dy2CFyD6.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dy2CFyD6.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DyBwTDJp.js.br +0 -0
- package/build/client/_app/immutable/chunks/DyBwTDJp.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ILTCveYH.js +0 -1
- package/build/client/_app/immutable/chunks/ILTCveYH.js.br +0 -0
- package/build/client/_app/immutable/chunks/ILTCveYH.js.gz +0 -0
- package/build/client/_app/immutable/chunks/JcG4w2gF.js.br +0 -0
- package/build/client/_app/immutable/chunks/JcG4w2gF.js.gz +0 -0
- package/build/client/_app/immutable/chunks/UqjCSZmA.js.br +0 -0
- package/build/client/_app/immutable/chunks/UqjCSZmA.js.gz +0 -0
- package/build/client/_app/immutable/chunks/_YcrHZ6P.js +0 -1
- package/build/client/_app/immutable/chunks/_YcrHZ6P.js.br +0 -2
- package/build/client/_app/immutable/chunks/_YcrHZ6P.js.gz +0 -0
- package/build/client/_app/immutable/chunks/i6ygh6h8.js +0 -1
- package/build/client/_app/immutable/chunks/i6ygh6h8.js.br +0 -2
- package/build/client/_app/immutable/chunks/i6ygh6h8.js.gz +0 -0
- package/build/client/_app/immutable/chunks/oRi68ch3.js +0 -1
- package/build/client/_app/immutable/chunks/oRi68ch3.js.br +0 -0
- package/build/client/_app/immutable/chunks/oRi68ch3.js.gz +0 -0
- package/build/client/_app/immutable/chunks/pPpIn6tB.js.br +0 -0
- package/build/client/_app/immutable/chunks/pPpIn6tB.js.gz +0 -0
- package/build/client/_app/immutable/chunks/r0sHOWrM.js.br +0 -0
- package/build/client/_app/immutable/chunks/r0sHOWrM.js.gz +0 -0
- package/build/client/_app/immutable/chunks/wcTQUnZO.js.br +0 -0
- package/build/client/_app/immutable/chunks/wcTQUnZO.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.DdO0gkPg.js +0 -2
- package/build/client/_app/immutable/entry/app.DdO0gkPg.js.br +0 -0
- package/build/client/_app/immutable/entry/app.DdO0gkPg.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.eAjDq0c9.js +0 -1
- package/build/client/_app/immutable/entry/start.eAjDq0c9.js.br +0 -2
- package/build/client/_app/immutable/entry/start.eAjDq0c9.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.nG3DdvIX.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.nG3DdvIX.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.Ceu0XS6v.js +0 -1
- package/build/client/_app/immutable/nodes/1.Ceu0XS6v.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.Ceu0XS6v.js.gz +0 -0
- package/build/client/_app/immutable/nodes/10.dm2dgy31.js +0 -13
- package/build/client/_app/immutable/nodes/10.dm2dgy31.js.br +0 -0
- package/build/client/_app/immutable/nodes/10.dm2dgy31.js.gz +0 -0
- package/build/client/_app/immutable/nodes/15.D2I0y3ys.js.br +0 -0
- package/build/client/_app/immutable/nodes/15.D2I0y3ys.js.gz +0 -0
- package/build/client/_app/immutable/nodes/17.CKxGp0E7.js +0 -3
- package/build/client/_app/immutable/nodes/17.CKxGp0E7.js.br +0 -0
- package/build/client/_app/immutable/nodes/17.CKxGp0E7.js.gz +0 -0
- package/build/client/_app/immutable/nodes/18.DHJxyQpw.js +0 -11
- package/build/client/_app/immutable/nodes/18.DHJxyQpw.js.br +0 -0
- package/build/client/_app/immutable/nodes/18.DHJxyQpw.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.DesR2keg.js +0 -35
- package/build/client/_app/immutable/nodes/2.DesR2keg.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.DesR2keg.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.BUKrXGR_.js +0 -1
- package/build/client/_app/immutable/nodes/3.BUKrXGR_.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.BUKrXGR_.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.C8VxVbVn.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.C8VxVbVn.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.BjrnKYaA.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.BjrnKYaA.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.C_hEzPrU.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.C_hEzPrU.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.BTYaOiDC.js +0 -2
- package/build/client/_app/immutable/nodes/8.BTYaOiDC.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.BTYaOiDC.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.DVspuDYY.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.DVspuDYY.js.gz +0 -0
- package/build/server/chunks/0-D_duQDWy.js +0 -9
- package/build/server/chunks/0-D_duQDWy.js.map +0 -1
- package/build/server/chunks/1-C0HdTvXW.js +0 -9
- package/build/server/chunks/10-DcfhCR4R.js +0 -9
- package/build/server/chunks/10-DcfhCR4R.js.map +0 -1
- package/build/server/chunks/15-BQcb9C_y.js +0 -9
- package/build/server/chunks/17-DuVCTioj.js +0 -9
- package/build/server/chunks/17-DuVCTioj.js.map +0 -1
- package/build/server/chunks/18-CT0VFW_p.js +0 -9
- package/build/server/chunks/18-CT0VFW_p.js.map +0 -1
- package/build/server/chunks/2-DgTzKQAP.js +0 -9
- package/build/server/chunks/2-DgTzKQAP.js.map +0 -1
- package/build/server/chunks/3-eQMSMzXP.js +0 -9
- package/build/server/chunks/3-eQMSMzXP.js.map +0 -1
- package/build/server/chunks/4-OaAJtDqP.js +0 -9
- package/build/server/chunks/4-OaAJtDqP.js.map +0 -1
- package/build/server/chunks/5-CBdMutbv.js +0 -9
- package/build/server/chunks/6-FoEi7ggm.js +0 -9
- package/build/server/chunks/8-B44yRkfX.js +0 -9
- package/build/server/chunks/8-B44yRkfX.js.map +0 -1
- package/build/server/chunks/9-eUjFpHzI.js +0 -9
- package/build/server/chunks/9-eUjFpHzI.js.map +0 -1
- package/build/server/chunks/ChatStepTrace-BivoNYOY.js.map +0 -1
- package/build/server/chunks/Dropdown-DnARl86Q.js.map +0 -1
- package/build/server/chunks/Tab-CUkyLqbN.js.map +0 -1
- package/build/server/chunks/_layout.svelte-B88WMyk0.js.map +0 -1
- package/build/server/chunks/_layout.svelte-wi6o5Ecu.js.map +0 -1
- package/build/server/chunks/_page.svelte-57pyhszV.js.map +0 -1
- package/build/server/chunks/_page.svelte-BSVJ2d2G.js.map +0 -1
- package/build/server/chunks/_page.svelte-CeS_21Ho.js.map +0 -1
- package/build/server/chunks/_page.svelte-DhFNFMnl.js.map +0 -1
- package/build/server/chunks/error.svelte-DmKKN1RU.js +0 -15
- package/build/server/chunks/index-LMsd1ih2.js.map +0 -1
- package/build/server/chunks/index-server-BRr5mZEq.js.map +0 -1
- package/build/server/chunks/log-content.svelte-CfQzj-v_.js +0 -20
- package/build/server/chunks/log-content.svelte-CfQzj-v_.js.map +0 -1
- package/build/server/chunks/root-4q2Pr7ck.js.map +0 -1
- package/build/server/chunks/source-selection-CcN1cgXc.js.map +0 -1
- /package/build/client/_app/immutable/assets/{ReadOnlyMonacoDiff.iiWNRlR-.css → Modal.iiWNRlR-.css} +0 -0
- /package/build/client/_app/immutable/assets/{ReadOnlyMonacoDiff.iiWNRlR-.css.br → Modal.iiWNRlR-.css.br} +0 -0
- /package/build/client/_app/immutable/assets/{ReadOnlyMonacoDiff.iiWNRlR-.css.gz → Modal.iiWNRlR-.css.gz} +0 -0
|
@@ -77,6 +77,17 @@ export interface WrittenRunReportConfig {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export type PromptTestsRunConfig = Pick<WrittenRunReportConfig, 'repeats' | 'concurrency'>
|
|
80
|
+
export type TestHistoryOverrides = Record<string, { conversation: string }>
|
|
81
|
+
|
|
82
|
+
export function applyTestHistoryOverrides<T extends { id?: unknown; conversation: string }>(
|
|
83
|
+
rows: T[],
|
|
84
|
+
overrides: TestHistoryOverrides
|
|
85
|
+
): T[] {
|
|
86
|
+
return rows.map((row) => {
|
|
87
|
+
const override = overrides[typeof row.id === 'string' ? row.id : '']
|
|
88
|
+
return override ? { ...row, conversation: override.conversation } : row
|
|
89
|
+
})
|
|
90
|
+
}
|
|
80
91
|
|
|
81
92
|
export interface WrittenRunReportRow extends TableRow {
|
|
82
93
|
test: SuiteTestRow
|
|
@@ -141,7 +152,8 @@ export async function runPromptTestsManaged(
|
|
|
141
152
|
suite: SuiteSelection,
|
|
142
153
|
selection: PromptTestsSelection,
|
|
143
154
|
requestedConfig: PromptTestsRunConfig,
|
|
144
|
-
promptOverrides: Record<string, string> = {}
|
|
155
|
+
promptOverrides: Record<string, string> = {},
|
|
156
|
+
testHistoryOverrides: TestHistoryOverrides = {}
|
|
145
157
|
): Promise<WrittenRunReport> {
|
|
146
158
|
const run = activeRuns.start(suite.suiteKey)
|
|
147
159
|
if (!run) throw new Error('Tests already running for this agent')
|
|
@@ -149,6 +161,7 @@ export async function runPromptTestsManaged(
|
|
|
149
161
|
return await runPromptTests(suite, selection, requestedConfig, {
|
|
150
162
|
signal: run.controller.signal,
|
|
151
163
|
promptOverrides,
|
|
164
|
+
testHistoryOverrides,
|
|
152
165
|
setProgress: (progress) => activeRuns.setProgress(suite.suiteKey, run, progress)
|
|
153
166
|
})
|
|
154
167
|
} finally {
|
|
@@ -619,6 +632,7 @@ export async function runPromptTests(
|
|
|
619
632
|
opts?: {
|
|
620
633
|
signal?: AbortSignal
|
|
621
634
|
promptOverrides?: Record<string, string>
|
|
635
|
+
testHistoryOverrides?: TestHistoryOverrides
|
|
622
636
|
setProgress?: (progress: MutablePromptTestsProgress) => boolean
|
|
623
637
|
}
|
|
624
638
|
): Promise<WrittenRunReport> {
|
|
@@ -627,7 +641,7 @@ export async function runPromptTests(
|
|
|
627
641
|
const onlyTestId = 'testId' in selection ? selection.testId : undefined
|
|
628
642
|
const suiteKey = suite.suiteKey
|
|
629
643
|
const loadedSuite = await readSuite(suiteKey)
|
|
630
|
-
const rows = loadedSuite.tests
|
|
644
|
+
const rows = applyTestHistoryOverrides(loadedSuite.tests, opts?.testHistoryOverrides ?? {})
|
|
631
645
|
|
|
632
646
|
const repeats = Math.min(500, Math.max(1, Math.floor(Number(requestedConfig.repeats) || 1)))
|
|
633
647
|
const concurrency = Math.min(
|
|
@@ -11,8 +11,16 @@ vi.mock('../lib/get-root-path', () => ({
|
|
|
11
11
|
}))
|
|
12
12
|
vi.mock('../lib/project-api', () => ({ getProjectBasePath: async () => project.basePath }))
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
addTest,
|
|
16
|
+
getRunResultEntry,
|
|
17
|
+
loadLatestResultsByTest,
|
|
18
|
+
loadTests,
|
|
19
|
+
patchTestConversationIfCurrent,
|
|
20
|
+
type TestRow
|
|
21
|
+
} from './data'
|
|
15
22
|
import { compressReportFile } from './report-files'
|
|
23
|
+
import { writeSuite } from './suite'
|
|
16
24
|
|
|
17
25
|
const temporaryDirectories: string[] = []
|
|
18
26
|
|
|
@@ -20,7 +28,7 @@ afterEach(async () => {
|
|
|
20
28
|
await Promise.all(temporaryDirectories.splice(0).map((dir) => rm(dir, { recursive: true })))
|
|
21
29
|
})
|
|
22
30
|
|
|
23
|
-
function testRow(id: string) {
|
|
31
|
+
function testRow(id: string): TestRow {
|
|
24
32
|
return {
|
|
25
33
|
id,
|
|
26
34
|
order: 0,
|
|
@@ -36,6 +44,47 @@ function testRow(id: string) {
|
|
|
36
44
|
}
|
|
37
45
|
}
|
|
38
46
|
|
|
47
|
+
describe('addTest', () => {
|
|
48
|
+
it('inserts before a test without changing existing test files', async () => {
|
|
49
|
+
const projectDir = await mkdtemp(path.join(os.tmpdir(), 'bosun-add-test-'))
|
|
50
|
+
temporaryDirectories.push(projectDir)
|
|
51
|
+
root.path = path.join(projectDir, 'src', 'lib', 'bosun', 'index.ts')
|
|
52
|
+
project.basePath = path.join(projectDir, 'src', 'agents')
|
|
53
|
+
const agent = `agent-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
54
|
+
const suiteDirectory = path.join(project.basePath, 'support', 'bosun-tests', agent)
|
|
55
|
+
await mkdir(suiteDirectory, { recursive: true })
|
|
56
|
+
|
|
57
|
+
const rows: TestRow[] = [
|
|
58
|
+
{ ...testRow('main-first'), step: 'main', order: 0 },
|
|
59
|
+
{ ...testRow('other-first'), step: 'other', order: 0 },
|
|
60
|
+
{ ...testRow('a-other-second'), step: 'other', order: 1024 },
|
|
61
|
+
{ ...testRow('z-main-second'), step: 'main', order: 1024 }
|
|
62
|
+
]
|
|
63
|
+
await writeSuite(agent, { tests: rows })
|
|
64
|
+
const originalFiles = new Map(
|
|
65
|
+
await Promise.all(
|
|
66
|
+
rows.map(
|
|
67
|
+
async (row) =>
|
|
68
|
+
[row.id, await readFile(path.join(suiteDirectory, `${row.id}.yaml`), 'utf8')] as const
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const created = await addTest(
|
|
74
|
+
agent,
|
|
75
|
+
{ scenario: 'duplicate', step: 'main' },
|
|
76
|
+
{ beforeTestId: 'z-main-second' }
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
expect(created.order).toBe(512)
|
|
80
|
+
for (const row of rows) {
|
|
81
|
+
expect(await readFile(path.join(suiteDirectory, `${row.id}.yaml`), 'utf8')).toBe(
|
|
82
|
+
originalFiles.get(row.id)
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
39
88
|
async function writeTests(projectDir: string, agent: string, rows: ReturnType<typeof testRow>[]) {
|
|
40
89
|
project.basePath = path.join(projectDir, 'src', 'agents')
|
|
41
90
|
const suiteDirectory = path.join(project.basePath, 'support', 'bosun-tests', agent)
|
|
@@ -144,3 +193,33 @@ describe('loadLatestResultsByTest', () => {
|
|
|
144
193
|
expect((await getRunResultEntry(agent, 'a')).success).toBe(true)
|
|
145
194
|
})
|
|
146
195
|
})
|
|
196
|
+
|
|
197
|
+
describe('patchTestConversationIfCurrent', () => {
|
|
198
|
+
it('does not overwrite a conversation that changed after the suggestion was created', async () => {
|
|
199
|
+
const projectDir = await mkdtemp(path.join(os.tmpdir(), 'bosun-history-suggestion-'))
|
|
200
|
+
temporaryDirectories.push(projectDir)
|
|
201
|
+
root.path = path.join(projectDir, 'src', 'lib', 'bosun', 'index.ts')
|
|
202
|
+
const agent = `agent-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
203
|
+
await writeTests(projectDir, agent, [testRow('test-1')])
|
|
204
|
+
|
|
205
|
+
const applied = await patchTestConversationIfCurrent(
|
|
206
|
+
agent,
|
|
207
|
+
'test-1',
|
|
208
|
+
'user: hello',
|
|
209
|
+
'user: corrected'
|
|
210
|
+
)
|
|
211
|
+
expect(applied).toMatchObject({ status: 'changed' })
|
|
212
|
+
|
|
213
|
+
const stale = await patchTestConversationIfCurrent(
|
|
214
|
+
agent,
|
|
215
|
+
'test-1',
|
|
216
|
+
'user: hello',
|
|
217
|
+
'user: stale suggestion'
|
|
218
|
+
)
|
|
219
|
+
expect(stale).toEqual({
|
|
220
|
+
status: 'stale',
|
|
221
|
+
currentConversation: 'user: corrected'
|
|
222
|
+
})
|
|
223
|
+
expect((await loadTests(agent)).rows[0].conversation).toBe('user: corrected')
|
|
224
|
+
})
|
|
225
|
+
})
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
} from './academy'
|
|
18
18
|
import { makeContext } from './context-utils'
|
|
19
19
|
import { listAgentReportFiles, readReportFile, reportTimestamp } from './report-files'
|
|
20
|
+
import { testEditSourceFingerprint } from './test-edit-source'
|
|
20
21
|
|
|
21
22
|
export type CsvRow = Record<string, string>
|
|
22
23
|
|
|
@@ -174,6 +175,38 @@ export async function patchTest(
|
|
|
174
175
|
})
|
|
175
176
|
}
|
|
176
177
|
|
|
178
|
+
export async function patchTestConversationIfCurrent(
|
|
179
|
+
agent: string,
|
|
180
|
+
id: string,
|
|
181
|
+
expectedConversation: string,
|
|
182
|
+
conversation: string,
|
|
183
|
+
allowedSourceFingerprints?: string[]
|
|
184
|
+
): Promise<
|
|
185
|
+
| { status: 'changed'; test: TestRow }
|
|
186
|
+
| { status: 'stale'; currentConversation: string }
|
|
187
|
+
| { status: 'not-found' }
|
|
188
|
+
> {
|
|
189
|
+
return withSuiteMutationLock(agent, async () => {
|
|
190
|
+
const suite = await readSuite(agent)
|
|
191
|
+
const current = suite.tests.find((row) => row.id === id)
|
|
192
|
+
if (!current) return { status: 'not-found' as const }
|
|
193
|
+
if (
|
|
194
|
+
current.conversation !== expectedConversation ||
|
|
195
|
+
(allowedSourceFingerprints &&
|
|
196
|
+
!allowedSourceFingerprints.includes(testEditSourceFingerprint(current)))
|
|
197
|
+
) {
|
|
198
|
+
return { status: 'stale' as const, currentConversation: current.conversation }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const updated = preparePatch(current, { conversation })
|
|
202
|
+
await writeSuite(agent, {
|
|
203
|
+
...suite,
|
|
204
|
+
tests: suite.tests.map((row) => (row.id === id ? updated : row))
|
|
205
|
+
})
|
|
206
|
+
return { status: 'changed' as const, test: updated }
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
|
|
177
210
|
function preparePatch(current: TestRow, patch: Partial<TestRow>) {
|
|
178
211
|
const updated: TestRow = {
|
|
179
212
|
...defaultTestRow,
|
|
@@ -210,8 +243,16 @@ export async function patchTestsBatch(
|
|
|
210
243
|
})
|
|
211
244
|
}
|
|
212
245
|
|
|
213
|
-
|
|
214
|
-
|
|
246
|
+
interface AddTestOptions {
|
|
247
|
+
beforeTestId?: string
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Add a new row at the top or directly before an existing row.
|
|
251
|
+
export async function addTest(
|
|
252
|
+
agent: string,
|
|
253
|
+
seed?: Partial<TestRow>,
|
|
254
|
+
options: AddTestOptions = {}
|
|
255
|
+
): Promise<TestRow> {
|
|
215
256
|
return withSuiteMutationLock(agent, async () => {
|
|
216
257
|
const suite = await loadSuiteOrEmpty(agent)
|
|
217
258
|
const id = randomUUID()
|
|
@@ -219,18 +260,47 @@ export async function addTest(agent: string, seed?: Partial<TestRow>): Promise<T
|
|
|
219
260
|
(minimum, row) => Math.min(minimum, row.order),
|
|
220
261
|
Number.POSITIVE_INFINITY
|
|
221
262
|
)
|
|
263
|
+
let existingTests = suite.tests
|
|
264
|
+
let order = Number.isFinite(firstOrder) ? firstOrder - 1024 : 0
|
|
265
|
+
|
|
266
|
+
if (options.beforeTestId) {
|
|
267
|
+
const beforeTest = suite.tests.find((row) => row.id === options.beforeTestId)
|
|
268
|
+
if (!beforeTest) throw new Error(`Test '${options.beforeTestId}' was not found`)
|
|
269
|
+
|
|
270
|
+
const stepTests = suite.tests.filter((row) => row.step === beforeTest.step)
|
|
271
|
+
const beforeIndex = stepTests.findIndex((row) => row.id === beforeTest.id)
|
|
272
|
+
const previousTest = stepTests[beforeIndex - 1]
|
|
273
|
+
const candidate = previousTest
|
|
274
|
+
? previousTest.order + (beforeTest.order - previousTest.order) / 2
|
|
275
|
+
: beforeTest.order - 1024
|
|
276
|
+
|
|
277
|
+
if (
|
|
278
|
+
Number.isFinite(candidate) &&
|
|
279
|
+
candidate < beforeTest.order &&
|
|
280
|
+
(!previousTest || candidate > previousTest.order)
|
|
281
|
+
) {
|
|
282
|
+
order = candidate
|
|
283
|
+
} else {
|
|
284
|
+
const rebalancedOrders = new Map(stepTests.map((row, index) => [row.id, index * 1024]))
|
|
285
|
+
existingTests = suite.tests.map((row) => {
|
|
286
|
+
const rebalancedOrder = rebalancedOrders.get(row.id)
|
|
287
|
+
return typeof rebalancedOrder === 'number' ? { ...row, order: rebalancedOrder } : row
|
|
288
|
+
})
|
|
289
|
+
order = beforeIndex === 0 ? -1024 : beforeIndex * 1024 - 512
|
|
290
|
+
}
|
|
291
|
+
}
|
|
222
292
|
|
|
223
293
|
const newRow: TestRow = {
|
|
224
294
|
...defaultTestRow,
|
|
225
295
|
id,
|
|
226
|
-
order: Number.isFinite(firstOrder) ? firstOrder - 1024 : 0,
|
|
227
296
|
...seed,
|
|
297
|
+
order,
|
|
228
298
|
context: makeContext(seed?.context)
|
|
229
299
|
}
|
|
230
300
|
if (typeof seed?.mocks !== 'undefined') {
|
|
231
301
|
newRow.mocks = makeContext(seed.mocks)
|
|
232
302
|
}
|
|
233
|
-
await writeSuite(agent, { tests: [newRow, ...
|
|
303
|
+
await writeSuite(agent, { tests: [newRow, ...existingTests] })
|
|
234
304
|
return newRow
|
|
235
305
|
})
|
|
236
306
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
loadLatestResultsByTest,
|
|
11
11
|
loadTests,
|
|
12
12
|
patchTest,
|
|
13
|
+
patchTestConversationIfCurrent,
|
|
13
14
|
patchTestsBatch,
|
|
14
15
|
reorderTests
|
|
15
16
|
} from './data'
|
|
@@ -18,7 +19,6 @@ import { cancelPromptTests, getPromptTestsProgress, runPromptTestsManaged } from
|
|
|
18
19
|
import { analyzeRunQuotes } from './analyze'
|
|
19
20
|
import { findPromptIssues } from './prompt-issues'
|
|
20
21
|
import type { TrpcContext } from '../lib/trpc-context'
|
|
21
|
-
import { getProjectBosunApi } from '../lib/project-api'
|
|
22
22
|
import {
|
|
23
23
|
TraceTestReviewContentSchema,
|
|
24
24
|
TraceTestSeedSchema
|
|
@@ -28,38 +28,16 @@ import {
|
|
|
28
28
|
createTestFromPIISanitizedTestDraft
|
|
29
29
|
} from './pii-sanitized-test-drafts'
|
|
30
30
|
import { PIISanitizerResponseError } from './pii-sanitizer'
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
): Promise<SuiteSelection> {
|
|
42
|
-
// Convention:
|
|
43
|
-
// - `agentId` is the canonical factory key (workflow export key).
|
|
44
|
-
// - `suiteKey` is the filesystem/report key:
|
|
45
|
-
// - non-regional projects: `${agentId}`
|
|
46
|
-
// - regional projects: `${agentId}.${region}`
|
|
47
|
-
const bosunApi = await getProjectBosunApi()
|
|
48
|
-
const meta = bosunApi ? await bosunApi.getAgentMeta(agentId) : null
|
|
49
|
-
const isRegionScoped = meta?.testScope === 'region'
|
|
50
|
-
if (!isRegionScoped) return { suiteKey: agentId, agentId }
|
|
51
|
-
|
|
52
|
-
if (!bosunApi) {
|
|
53
|
-
throw new Error(`Project does not export bosunApi, cannot resolve suite for '${agentId}'.`)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const resolved = await bosunApi.resolveTestSuite({ agentId, region })
|
|
57
|
-
return {
|
|
58
|
-
suiteKey: resolved.suiteKey,
|
|
59
|
-
agentId,
|
|
60
|
-
region: resolved.resolvedRegion ?? undefined
|
|
61
|
-
}
|
|
62
|
-
}
|
|
31
|
+
import { suggestTestHistoryEdit } from './suggest-test-history-edit'
|
|
32
|
+
import { updatePromptEditProgress } from '../roleplay/prompt-edit-progress'
|
|
33
|
+
import { resolveSuiteSelection } from './test-selection'
|
|
34
|
+
import { testEditSourceFingerprint } from './test-edit-source'
|
|
35
|
+
import {
|
|
36
|
+
createTestHistoryEditProposal,
|
|
37
|
+
deleteTestHistoryEditProposal,
|
|
38
|
+
getTestHistoryEditProposalForAcceptance
|
|
39
|
+
} from './test-history-edit-proposals'
|
|
40
|
+
import { withPromptFileLock } from '../roleplay/prompt-file'
|
|
63
41
|
|
|
64
42
|
export function createTestsRouter(t: TRPCRootObject<TrpcContext, any, any>) {
|
|
65
43
|
return t.router({
|
|
@@ -151,6 +129,51 @@ export function createTestsRouter(t: TRPCRootObject<TrpcContext, any, any>) {
|
|
|
151
129
|
.mutation(async ({ input }) => {
|
|
152
130
|
return findPromptIssues(input)
|
|
153
131
|
}),
|
|
132
|
+
suggestHistoryEdit: t.procedure
|
|
133
|
+
.input(
|
|
134
|
+
z.object({
|
|
135
|
+
agent: z.string(),
|
|
136
|
+
region: z.string().nullish(),
|
|
137
|
+
testId: z.string(),
|
|
138
|
+
step: z.string().trim().min(1).max(512),
|
|
139
|
+
request: z.string().trim().min(1).max(100_000),
|
|
140
|
+
prompt: z.string().max(1_000_000),
|
|
141
|
+
basePrompt: z.string().max(1_000_000),
|
|
142
|
+
progressId: z.string().uuid()
|
|
143
|
+
})
|
|
144
|
+
)
|
|
145
|
+
.mutation(async ({ input }) => {
|
|
146
|
+
const { suiteKey } = await resolveSuiteSelection(input.agent, input.region)
|
|
147
|
+
const test = (await readSuite(suiteKey)).tests.find((row) => row.id === input.testId)
|
|
148
|
+
if (!test) throw new Error('This test is no longer available.')
|
|
149
|
+
const suggestedConversation = await suggestTestHistoryEdit({
|
|
150
|
+
request: input.request,
|
|
151
|
+
prompt: input.prompt,
|
|
152
|
+
context: JSON.stringify(test.context ?? {}, null, 2),
|
|
153
|
+
conversation: test.conversation,
|
|
154
|
+
expectedOutput: test.expected_output,
|
|
155
|
+
onProgress: (progress) => updatePromptEditProgress(input.progressId, progress)
|
|
156
|
+
})
|
|
157
|
+
return suggestedConversation === test.conversation
|
|
158
|
+
? { status: 'unchanged' as const }
|
|
159
|
+
: {
|
|
160
|
+
status: 'changed' as const,
|
|
161
|
+
proposal: createTestHistoryEditProposal({
|
|
162
|
+
agentId: input.agent,
|
|
163
|
+
region: input.region ?? null,
|
|
164
|
+
testId: input.testId,
|
|
165
|
+
step: input.step,
|
|
166
|
+
originalConversation: test.conversation,
|
|
167
|
+
suggestedConversation,
|
|
168
|
+
expectedOutput: test.expected_output,
|
|
169
|
+
sourceGuard: {
|
|
170
|
+
suiteKey,
|
|
171
|
+
allowedTestFingerprints: [testEditSourceFingerprint(test)],
|
|
172
|
+
allowedPromptContents: [input.basePrompt, input.prompt]
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
}),
|
|
154
177
|
suite: t.procedure
|
|
155
178
|
.input(z.object({ agent: z.string(), region: z.string().nullish() }))
|
|
156
179
|
.query(async ({ input }) => {
|
|
@@ -162,12 +185,13 @@ export function createTestsRouter(t: TRPCRootObject<TrpcContext, any, any>) {
|
|
|
162
185
|
z.object({
|
|
163
186
|
agent: z.string(),
|
|
164
187
|
region: z.string().nullish(),
|
|
188
|
+
beforeTestId: z.string().optional(),
|
|
165
189
|
seed: TestRowSchema.omit({ id: true, order: true }).partial()
|
|
166
190
|
})
|
|
167
191
|
)
|
|
168
192
|
.mutation(async ({ input }) => {
|
|
169
193
|
const { suiteKey } = await resolveSuiteSelection(input.agent, input.region)
|
|
170
|
-
return addTest(suiteKey, input.seed)
|
|
194
|
+
return addTest(suiteKey, input.seed, { beforeTestId: input.beforeTestId })
|
|
171
195
|
}),
|
|
172
196
|
createPIISanitizedTestDraft: t.procedure
|
|
173
197
|
.input(
|
|
@@ -221,6 +245,39 @@ export function createTestsRouter(t: TRPCRootObject<TrpcContext, any, any>) {
|
|
|
221
245
|
const { suiteKey } = await resolveSuiteSelection(input.agent, input.region)
|
|
222
246
|
return patchTest(suiteKey, input.id, input.patch)
|
|
223
247
|
}),
|
|
248
|
+
acceptHistoryEdit: t.procedure
|
|
249
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
250
|
+
.mutation(async ({ input }) => {
|
|
251
|
+
const proposal = getTestHistoryEditProposalForAcceptance(input.id)
|
|
252
|
+
if (!proposal) throw new Error('This test history suggestion is no longer available.')
|
|
253
|
+
const result = await withPromptFileLock(proposal.agentId, proposal.step, async (file) => {
|
|
254
|
+
if (!proposal.sourceGuard.allowedPromptContents.includes(await file.read())) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
'The prompt changed while this suggestion was open. Reject this suggestion, then ask Bosun to create a new one.'
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
return patchTestConversationIfCurrent(
|
|
260
|
+
proposal.sourceGuard.suiteKey,
|
|
261
|
+
proposal.testId,
|
|
262
|
+
proposal.originalConversation,
|
|
263
|
+
proposal.suggestedConversation,
|
|
264
|
+
proposal.sourceGuard.allowedTestFingerprints
|
|
265
|
+
)
|
|
266
|
+
})
|
|
267
|
+
if (result.status === 'not-found') {
|
|
268
|
+
throw new Error('This test is no longer available.')
|
|
269
|
+
}
|
|
270
|
+
if (result.status === 'stale') {
|
|
271
|
+
throw new Error(
|
|
272
|
+
'This test changed while the suggestion was open. Reject this suggestion, then ask Bosun to create a new one.'
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
deleteTestHistoryEditProposal(proposal.id)
|
|
276
|
+
return { ok: true as const, test: result.test }
|
|
277
|
+
}),
|
|
278
|
+
rejectHistoryEdit: t.procedure
|
|
279
|
+
.input(z.object({ id: z.string().uuid() }))
|
|
280
|
+
.mutation(({ input }) => ({ ok: deleteTestHistoryEditProposal(input.id) })),
|
|
224
281
|
patchTestsBatch: t.procedure
|
|
225
282
|
.input(
|
|
226
283
|
z.object({
|
|
@@ -262,6 +319,9 @@ export function createTestsRouter(t: TRPCRootObject<TrpcContext, any, any>) {
|
|
|
262
319
|
step: z.string(),
|
|
263
320
|
scope: z.enum(['pinned', 'all']).optional(),
|
|
264
321
|
promptOverrides: z.record(z.string(), z.string().max(1_000_000)).optional(),
|
|
322
|
+
testHistoryOverrides: z
|
|
323
|
+
.record(z.string(), z.object({ conversation: z.string().max(1_000_000) }).strict())
|
|
324
|
+
.optional(),
|
|
265
325
|
repeats: z.number().int().min(1).max(500),
|
|
266
326
|
concurrency: z.number().int().min(1).max(75)
|
|
267
327
|
})
|
|
@@ -272,6 +332,9 @@ export function createTestsRouter(t: TRPCRootObject<TrpcContext, any, any>) {
|
|
|
272
332
|
region: z.string().nullish(),
|
|
273
333
|
testId: z.string(),
|
|
274
334
|
promptOverrides: z.record(z.string(), z.string().max(1_000_000)).optional(),
|
|
335
|
+
testHistoryOverrides: z
|
|
336
|
+
.record(z.string(), z.object({ conversation: z.string().max(1_000_000) }).strict())
|
|
337
|
+
.optional(),
|
|
275
338
|
repeats: z.number().int().min(1).max(500),
|
|
276
339
|
concurrency: z.number().int().min(1).max(75)
|
|
277
340
|
})
|
|
@@ -286,7 +349,8 @@ export function createTestsRouter(t: TRPCRootObject<TrpcContext, any, any>) {
|
|
|
286
349
|
selection,
|
|
287
350
|
{ testId: input.testId },
|
|
288
351
|
runConfig,
|
|
289
|
-
input.promptOverrides
|
|
352
|
+
input.promptOverrides,
|
|
353
|
+
input.testHistoryOverrides
|
|
290
354
|
)
|
|
291
355
|
}
|
|
292
356
|
return runPromptTestsManaged(
|
|
@@ -296,7 +360,8 @@ export function createTestsRouter(t: TRPCRootObject<TrpcContext, any, any>) {
|
|
|
296
360
|
step: input.step
|
|
297
361
|
},
|
|
298
362
|
runConfig,
|
|
299
|
-
input.promptOverrides
|
|
363
|
+
input.promptOverrides,
|
|
364
|
+
input.testHistoryOverrides
|
|
300
365
|
)
|
|
301
366
|
}),
|
|
302
367
|
cancelRun: t.procedure
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { buildTestHistoryPrompt } from './suggest-test-history-edit'
|
|
4
|
+
|
|
5
|
+
describe('buildTestHistoryPrompt', () => {
|
|
6
|
+
it('supplies the prompt, history, and immutable expected outcome', () => {
|
|
7
|
+
const prompt = buildTestHistoryPrompt({
|
|
8
|
+
request: 'Make the history valid.',
|
|
9
|
+
prompt: 'Always ask for a name.',
|
|
10
|
+
context: '{ "customer": { "name": "Sam" } }',
|
|
11
|
+
conversation: 'User: Hello\nAgent: Welcome back, Sam.',
|
|
12
|
+
expectedOutput: 'What is your name?'
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
expect(prompt).toContain('Always ask for a name.')
|
|
16
|
+
expect(prompt).toContain('{ "customer": { "name": "Sam" } }')
|
|
17
|
+
expect(prompt).toContain('User: Hello\nAgent: Welcome back, Sam.')
|
|
18
|
+
expect(prompt).toContain('immutable expected outcome:\nWhat is your name?')
|
|
19
|
+
})
|
|
20
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { PromptEditProgressPhase } from '../roleplay/prompt-edit-progress'
|
|
2
|
+
import { suggestFileEdit } from '../roleplay/suggest-file-edit'
|
|
3
|
+
|
|
4
|
+
const VIRTUAL_HISTORY_PATH = 'history.md'
|
|
5
|
+
|
|
6
|
+
export async function suggestTestHistoryEdit(input: {
|
|
7
|
+
request: string
|
|
8
|
+
prompt: string
|
|
9
|
+
context: string
|
|
10
|
+
conversation: string
|
|
11
|
+
expectedOutput: string
|
|
12
|
+
onProgress?: (progress: {
|
|
13
|
+
phase: PromptEditProgressPhase
|
|
14
|
+
activity: number
|
|
15
|
+
summary: string
|
|
16
|
+
}) => void
|
|
17
|
+
}): Promise<string> {
|
|
18
|
+
return suggestFileEdit({
|
|
19
|
+
request: buildTestHistoryPrompt(input),
|
|
20
|
+
originalContent: input.conversation,
|
|
21
|
+
virtualPath: VIRTUAL_HISTORY_PATH,
|
|
22
|
+
instructions:
|
|
23
|
+
'The conversation history is the existing virtual file history.md. Decide whether it conflicts with the current LLM prompt or contains agent behaviour that the current prompt would not produce. If it does, use the Apply Patch tool to update history.md minimally. Otherwise, do not call Apply Patch. Preserve the test intent and never add or change the expected outcome. Do not create, delete, or modify any other file.',
|
|
24
|
+
onProgress: input.onProgress
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function buildTestHistoryPrompt(input: {
|
|
29
|
+
request: string
|
|
30
|
+
prompt: string
|
|
31
|
+
context: string
|
|
32
|
+
conversation: string
|
|
33
|
+
expectedOutput: string
|
|
34
|
+
}): string {
|
|
35
|
+
return `The user wants you to update an LLM test's conversation history when needed. The history must reflect behaviour that is valid under the current prompt. Make the smallest useful change. The expected outcome is immutable and is provided only so you can preserve the test's intent.
|
|
36
|
+
|
|
37
|
+
This is the user's request:
|
|
38
|
+
${input.request}
|
|
39
|
+
|
|
40
|
+
This is the current LLM prompt:
|
|
41
|
+
${input.prompt}
|
|
42
|
+
|
|
43
|
+
This is the test context:
|
|
44
|
+
${input.context}
|
|
45
|
+
|
|
46
|
+
This is the conversation history in history.md:
|
|
47
|
+
${input.conversation}
|
|
48
|
+
|
|
49
|
+
This is the immutable expected outcome:
|
|
50
|
+
${input.expectedOutput}
|
|
51
|
+
`
|
|
52
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import type { TestRow } from './suite'
|
|
4
|
+
import { testEditSourceFingerprint } from './test-edit-source'
|
|
5
|
+
|
|
6
|
+
const sourceChanges: Array<[string, Partial<TestRow>]> = [
|
|
7
|
+
['step', { step: 'follow-up' }],
|
|
8
|
+
['evaluation mode', { evaluation_mode: 'json-deep-equal' }],
|
|
9
|
+
['assertion type', { assertion_type: 'contains' }],
|
|
10
|
+
['assertion value', { assertion_value: 'A different assertion' }],
|
|
11
|
+
['case sensitivity', { case_sensitive: true }],
|
|
12
|
+
['context mode', { context_mode: 'replace' }],
|
|
13
|
+
['conversation', { conversation: 'User: changed' }],
|
|
14
|
+
['expected output', { expected_output: 'Agent: changed' }],
|
|
15
|
+
['timeout', { timeout: '10' }],
|
|
16
|
+
['similarity threshold', { similarity_threshold: '80' }],
|
|
17
|
+
['context', { context: { customer: 'Alex' } }],
|
|
18
|
+
['mocks', { mocks: { booking: { id: '456' } } }]
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
describe('testEditSourceFingerprint', () => {
|
|
22
|
+
it.each(sourceChanges)('changes when the test %s changes', (_name, change) => {
|
|
23
|
+
const test = testRow()
|
|
24
|
+
|
|
25
|
+
expect(testEditSourceFingerprint({ ...test, ...change })).not.toBe(
|
|
26
|
+
testEditSourceFingerprint(test)
|
|
27
|
+
)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('uses the proposed conversation without changing the other source fields', () => {
|
|
31
|
+
const test = testRow()
|
|
32
|
+
const conversation = 'User: corrected'
|
|
33
|
+
|
|
34
|
+
expect(testEditSourceFingerprint(test, { conversation })).toBe(
|
|
35
|
+
testEditSourceFingerprint({ ...test, conversation })
|
|
36
|
+
)
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
function testRow(): TestRow {
|
|
41
|
+
return {
|
|
42
|
+
id: 'test-1',
|
|
43
|
+
order: 0,
|
|
44
|
+
pinned: 'TRUE',
|
|
45
|
+
scenario: 'Scenario',
|
|
46
|
+
step: 'main',
|
|
47
|
+
author: '',
|
|
48
|
+
category: '',
|
|
49
|
+
assertion_type: 'llm-rubric',
|
|
50
|
+
assertion_value: 'Be correct',
|
|
51
|
+
conversation: 'User: original',
|
|
52
|
+
expected_output: 'Agent: expected',
|
|
53
|
+
evaluation_mode: 'assertion',
|
|
54
|
+
case_sensitive: false,
|
|
55
|
+
context_mode: 'merge',
|
|
56
|
+
timeout: '0',
|
|
57
|
+
similarity_threshold: '50',
|
|
58
|
+
context: { customer: 'Sam' },
|
|
59
|
+
mocks: { booking: { id: '123' } }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
|
|
3
|
+
import type { TestRow } from './suite'
|
|
4
|
+
|
|
5
|
+
function canonicalize(value: unknown): unknown {
|
|
6
|
+
if (Array.isArray(value)) return value.map(canonicalize)
|
|
7
|
+
if (!value || typeof value !== 'object') return value
|
|
8
|
+
return Object.fromEntries(
|
|
9
|
+
Object.entries(value as Record<string, unknown>)
|
|
10
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
11
|
+
.map(([key, entry]) => [key, canonicalize(entry)])
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function testEditSourceFingerprint(
|
|
16
|
+
test: TestRow,
|
|
17
|
+
overrides?: { conversation?: string }
|
|
18
|
+
): string {
|
|
19
|
+
const source = canonicalize({
|
|
20
|
+
step: test.step,
|
|
21
|
+
evaluationMode: test.evaluation_mode,
|
|
22
|
+
assertionType: test.assertion_type,
|
|
23
|
+
assertionValue: test.assertion_value,
|
|
24
|
+
caseSensitive: test.case_sensitive,
|
|
25
|
+
contextMode: test.context_mode,
|
|
26
|
+
conversation: overrides?.conversation ?? test.conversation,
|
|
27
|
+
expectedOutput: test.expected_output,
|
|
28
|
+
timeout: test.timeout,
|
|
29
|
+
similarityThreshold: test.similarity_threshold,
|
|
30
|
+
context: test.context,
|
|
31
|
+
mocks: test.mocks ?? null
|
|
32
|
+
})
|
|
33
|
+
return createHash('sha256').update(JSON.stringify(source)).digest('hex')
|
|
34
|
+
}
|