@recombine-ai/bosun 0.11.1 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (403) hide show
  1. package/build/client/_app/immutable/assets/0.CH-WU4JN.css +1 -0
  2. package/build/client/_app/immutable/assets/0.CH-WU4JN.css.br +0 -0
  3. package/build/client/_app/immutable/assets/0.CH-WU4JN.css.gz +0 -0
  4. package/build/client/_app/immutable/assets/{10.BX46PfEJ.css → 10.DIVePEYf.css} +1 -1
  5. package/build/client/_app/immutable/assets/10.DIVePEYf.css.br +0 -0
  6. package/build/client/_app/immutable/assets/10.DIVePEYf.css.gz +0 -0
  7. package/build/client/_app/immutable/chunks/{AocOovHL.js → 07xNIa59.js} +2 -2
  8. package/build/client/_app/immutable/chunks/07xNIa59.js.br +0 -0
  9. package/build/client/_app/immutable/chunks/07xNIa59.js.gz +0 -0
  10. package/build/client/_app/immutable/chunks/BDl2QuyN.js +1 -0
  11. package/build/client/_app/immutable/chunks/BDl2QuyN.js.br +0 -0
  12. package/build/client/_app/immutable/chunks/BDl2QuyN.js.gz +0 -0
  13. package/build/client/_app/immutable/chunks/{CqxCTxvJ.js → BI42X8h_.js} +1 -1
  14. package/build/client/_app/immutable/chunks/BI42X8h_.js.br +0 -0
  15. package/build/client/_app/immutable/chunks/BI42X8h_.js.gz +0 -0
  16. package/build/client/_app/immutable/chunks/BJTwdUYr.js +1 -0
  17. package/build/client/_app/immutable/chunks/BJTwdUYr.js.br +0 -0
  18. package/build/client/_app/immutable/chunks/BJTwdUYr.js.gz +0 -0
  19. package/build/client/_app/immutable/chunks/BLu86SbA.js +1 -0
  20. package/build/client/_app/immutable/chunks/BLu86SbA.js.br +0 -0
  21. package/build/client/_app/immutable/chunks/BLu86SbA.js.gz +0 -0
  22. package/build/client/_app/immutable/chunks/BNifnXkO.js +1 -0
  23. package/build/client/_app/immutable/chunks/BNifnXkO.js.br +2 -0
  24. package/build/client/_app/immutable/chunks/BNifnXkO.js.gz +0 -0
  25. package/build/client/_app/immutable/chunks/{BM0jKynj.js → BPNEONy5.js} +1 -1
  26. package/build/client/_app/immutable/chunks/BPNEONy5.js.br +0 -0
  27. package/build/client/_app/immutable/chunks/BPNEONy5.js.gz +0 -0
  28. package/build/client/_app/immutable/chunks/B_K7D6rX.js +2 -0
  29. package/build/client/_app/immutable/chunks/B_K7D6rX.js.br +0 -0
  30. package/build/client/_app/immutable/chunks/B_K7D6rX.js.gz +0 -0
  31. package/build/client/_app/immutable/chunks/Bj8jfBTi.js +1 -0
  32. package/build/client/_app/immutable/chunks/Bj8jfBTi.js.br +0 -0
  33. package/build/client/_app/immutable/chunks/Bj8jfBTi.js.gz +0 -0
  34. package/build/client/_app/immutable/chunks/{BR5f5i_E.js → BoPSJr4b.js} +1 -1
  35. package/build/client/_app/immutable/chunks/BoPSJr4b.js.br +0 -0
  36. package/build/client/_app/immutable/chunks/BoPSJr4b.js.gz +0 -0
  37. package/build/client/_app/immutable/chunks/BsDZe4N6.js +1 -0
  38. package/build/client/_app/immutable/chunks/BsDZe4N6.js.br +0 -0
  39. package/build/client/_app/immutable/chunks/BsDZe4N6.js.gz +0 -0
  40. package/build/client/_app/immutable/chunks/{CXo2BVs0.js → Buo-57fF.js} +1 -1
  41. package/build/client/_app/immutable/chunks/Buo-57fF.js.br +0 -0
  42. package/build/client/_app/immutable/chunks/Buo-57fF.js.gz +0 -0
  43. package/build/client/_app/immutable/chunks/{C-whEtKd.js → C39ai1md.js} +1 -1
  44. package/build/client/_app/immutable/chunks/C39ai1md.js.br +0 -0
  45. package/build/client/_app/immutable/chunks/C39ai1md.js.gz +0 -0
  46. package/build/client/_app/immutable/chunks/C7he4XPB.js +1 -0
  47. package/build/client/_app/immutable/chunks/C7he4XPB.js.br +0 -0
  48. package/build/client/_app/immutable/chunks/C7he4XPB.js.gz +0 -0
  49. package/build/client/_app/immutable/chunks/{DYhTBLDc.js → CAjPRvNs.js} +2 -2
  50. package/build/client/_app/immutable/chunks/CAjPRvNs.js.br +0 -0
  51. package/build/client/_app/immutable/chunks/CAjPRvNs.js.gz +0 -0
  52. package/build/client/_app/immutable/chunks/CJYOmGlC.js +1 -0
  53. package/build/client/_app/immutable/chunks/CJYOmGlC.js.br +0 -0
  54. package/build/client/_app/immutable/chunks/CJYOmGlC.js.gz +0 -0
  55. package/build/client/_app/immutable/chunks/{UiXQyKcj.js → CMQRtcOV.js} +1 -1
  56. package/build/client/_app/immutable/chunks/CMQRtcOV.js.br +0 -0
  57. package/build/client/_app/immutable/chunks/CMQRtcOV.js.gz +0 -0
  58. package/build/client/_app/immutable/chunks/{HJHYLHDn.js → CPXdAKdv.js} +1 -1
  59. package/build/client/_app/immutable/chunks/CPXdAKdv.js.br +0 -0
  60. package/build/client/_app/immutable/chunks/CPXdAKdv.js.gz +0 -0
  61. package/build/client/_app/immutable/chunks/{DTlGvFHe.js → CVlsAO8k.js} +1 -1
  62. package/build/client/_app/immutable/chunks/CVlsAO8k.js.br +0 -0
  63. package/build/client/_app/immutable/chunks/CVlsAO8k.js.gz +0 -0
  64. package/build/client/_app/immutable/chunks/{DNLcpWO_.js → CX0Ekq8t.js} +1 -1
  65. package/build/client/_app/immutable/chunks/CX0Ekq8t.js.br +0 -0
  66. package/build/client/_app/immutable/chunks/CX0Ekq8t.js.gz +0 -0
  67. package/build/client/_app/immutable/chunks/Ca197UDq.js +1 -0
  68. package/build/client/_app/immutable/chunks/Ca197UDq.js.br +0 -0
  69. package/build/client/_app/immutable/chunks/Ca197UDq.js.gz +0 -0
  70. package/build/client/_app/immutable/chunks/CaHl3dME.js +1 -0
  71. package/build/client/_app/immutable/chunks/CaHl3dME.js.br +0 -0
  72. package/build/client/_app/immutable/chunks/CaHl3dME.js.gz +0 -0
  73. package/build/client/_app/immutable/chunks/Cql_e8Of.js +1 -0
  74. package/build/client/_app/immutable/chunks/Cql_e8Of.js.br +2 -0
  75. package/build/client/_app/immutable/chunks/Cql_e8Of.js.gz +0 -0
  76. package/build/client/_app/immutable/chunks/{Dv8m1cTI.js → CxLomzuE.js} +1 -1
  77. package/build/client/_app/immutable/chunks/CxLomzuE.js.br +0 -0
  78. package/build/client/_app/immutable/chunks/{Dv8m1cTI.js.gz → CxLomzuE.js.gz} +0 -0
  79. package/build/client/_app/immutable/chunks/Cyai3SOj.js +1 -0
  80. package/build/client/_app/immutable/chunks/Cyai3SOj.js.br +0 -0
  81. package/build/client/_app/immutable/chunks/Cyai3SOj.js.gz +0 -0
  82. package/build/client/_app/immutable/chunks/{-g1l5aoS.js → CyvQWZoe.js} +4 -4
  83. package/build/client/_app/immutable/chunks/CyvQWZoe.js.br +0 -0
  84. package/build/client/_app/immutable/chunks/CyvQWZoe.js.gz +0 -0
  85. package/build/client/_app/immutable/chunks/CzXNWL4m.js +1 -0
  86. package/build/client/_app/immutable/chunks/CzXNWL4m.js.br +1 -0
  87. package/build/client/_app/immutable/chunks/CzXNWL4m.js.gz +0 -0
  88. package/build/client/_app/immutable/chunks/D-B5X35l.js +1 -0
  89. package/build/client/_app/immutable/chunks/D-B5X35l.js.br +0 -0
  90. package/build/client/_app/immutable/chunks/D-B5X35l.js.gz +0 -0
  91. package/build/client/_app/immutable/chunks/DJriQKD9.js +2 -0
  92. package/build/client/_app/immutable/chunks/DJriQKD9.js.br +0 -0
  93. package/build/client/_app/immutable/chunks/DJriQKD9.js.gz +0 -0
  94. package/build/client/_app/immutable/chunks/{BL8rPcmP.js → DXUXXCDA.js} +1 -1
  95. package/build/client/_app/immutable/chunks/DXUXXCDA.js.br +0 -0
  96. package/build/client/_app/immutable/chunks/DXUXXCDA.js.gz +0 -0
  97. package/build/client/_app/immutable/chunks/{CZLNSMhF.js → Dslf9OwW.js} +10 -10
  98. package/build/client/_app/immutable/chunks/Dslf9OwW.js.br +0 -0
  99. package/build/client/_app/immutable/chunks/Dslf9OwW.js.gz +0 -0
  100. package/build/client/_app/immutable/chunks/QpaC6J6s.js +1 -0
  101. package/build/client/_app/immutable/chunks/QpaC6J6s.js.br +0 -0
  102. package/build/client/_app/immutable/chunks/QpaC6J6s.js.gz +0 -0
  103. package/build/client/_app/immutable/chunks/{HXxIsSOO.js → iAhzu4tv.js} +1 -1
  104. package/build/client/_app/immutable/chunks/iAhzu4tv.js.br +0 -0
  105. package/build/client/_app/immutable/chunks/iAhzu4tv.js.gz +0 -0
  106. package/build/client/_app/immutable/chunks/i_5fJYKS.js +3 -0
  107. package/build/client/_app/immutable/chunks/i_5fJYKS.js.br +0 -0
  108. package/build/client/_app/immutable/chunks/i_5fJYKS.js.gz +0 -0
  109. package/build/client/_app/immutable/chunks/{Cosugfe8.js → nYD9wsyN.js} +1 -1
  110. package/build/client/_app/immutable/chunks/nYD9wsyN.js.br +0 -0
  111. package/build/client/_app/immutable/chunks/nYD9wsyN.js.gz +0 -0
  112. package/build/client/_app/immutable/chunks/{BIpSIzUr.js → uLGP0py1.js} +1 -1
  113. package/build/client/_app/immutable/chunks/uLGP0py1.js.br +0 -0
  114. package/build/client/_app/immutable/chunks/uLGP0py1.js.gz +0 -0
  115. package/build/client/_app/immutable/entry/app.Cr7CMG-3.js +2 -0
  116. package/build/client/_app/immutable/entry/app.Cr7CMG-3.js.br +0 -0
  117. package/build/client/_app/immutable/entry/app.Cr7CMG-3.js.gz +0 -0
  118. package/build/client/_app/immutable/entry/start.CDNd0Bml.js +1 -0
  119. package/build/client/_app/immutable/entry/start.CDNd0Bml.js.br +2 -0
  120. package/build/client/_app/immutable/entry/start.CDNd0Bml.js.gz +0 -0
  121. package/build/client/_app/immutable/nodes/{0.BiYgQci8.js → 0.AyqKXI2h.js} +2 -2
  122. package/build/client/_app/immutable/nodes/0.AyqKXI2h.js.br +0 -0
  123. package/build/client/_app/immutable/nodes/0.AyqKXI2h.js.gz +0 -0
  124. package/build/client/_app/immutable/nodes/1.BRmfm-nN.js +1 -0
  125. package/build/client/_app/immutable/nodes/1.BRmfm-nN.js.br +0 -0
  126. package/build/client/_app/immutable/nodes/1.BRmfm-nN.js.gz +0 -0
  127. package/build/client/_app/immutable/nodes/10.D03jjkr4.js +7 -0
  128. package/build/client/_app/immutable/nodes/10.D03jjkr4.js.br +0 -0
  129. package/build/client/_app/immutable/nodes/10.D03jjkr4.js.gz +0 -0
  130. package/build/client/_app/immutable/nodes/{15.Wmhj7gOy.js → 15.BFy4oAT6.js} +1 -1
  131. package/build/client/_app/immutable/nodes/15.BFy4oAT6.js.br +0 -0
  132. package/build/client/_app/immutable/nodes/15.BFy4oAT6.js.gz +0 -0
  133. package/build/client/_app/immutable/nodes/17.BOYU_WTT.js +3 -0
  134. package/build/client/_app/immutable/nodes/17.BOYU_WTT.js.br +0 -0
  135. package/build/client/_app/immutable/nodes/17.BOYU_WTT.js.gz +0 -0
  136. package/build/client/_app/immutable/nodes/18.Ddx80a4f.js +11 -0
  137. package/build/client/_app/immutable/nodes/18.Ddx80a4f.js.br +0 -0
  138. package/build/client/_app/immutable/nodes/18.Ddx80a4f.js.gz +0 -0
  139. package/build/client/_app/immutable/nodes/{2.DniQTiGZ.js → 2.CZEboQjo.js} +20 -20
  140. package/build/client/_app/immutable/nodes/2.CZEboQjo.js.br +0 -0
  141. package/build/client/_app/immutable/nodes/2.CZEboQjo.js.gz +0 -0
  142. package/build/client/_app/immutable/nodes/3.dAV7FviN.js +1 -0
  143. package/build/client/_app/immutable/nodes/3.dAV7FviN.js.br +0 -0
  144. package/build/client/_app/immutable/nodes/3.dAV7FviN.js.gz +0 -0
  145. package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js +1 -0
  146. package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js.br +0 -0
  147. package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js.gz +0 -0
  148. package/build/client/_app/immutable/nodes/5.DPWuTlaI.js +1 -0
  149. package/build/client/_app/immutable/nodes/5.DPWuTlaI.js.br +0 -0
  150. package/build/client/_app/immutable/nodes/5.DPWuTlaI.js.gz +0 -0
  151. package/build/client/_app/immutable/nodes/6.DY-NLaRW.js +1 -0
  152. package/build/client/_app/immutable/nodes/6.DY-NLaRW.js.br +0 -0
  153. package/build/client/_app/immutable/nodes/6.DY-NLaRW.js.gz +0 -0
  154. package/build/client/_app/immutable/nodes/{8.2oZsfzo8.js → 8.Chuqhf8W.js} +1 -1
  155. package/build/client/_app/immutable/nodes/8.Chuqhf8W.js.br +0 -0
  156. package/build/client/_app/immutable/nodes/8.Chuqhf8W.js.gz +0 -0
  157. package/build/client/_app/immutable/nodes/9.BOEM8f6B.js +2 -0
  158. package/build/client/_app/immutable/nodes/9.BOEM8f6B.js.br +0 -0
  159. package/build/client/_app/immutable/nodes/9.BOEM8f6B.js.gz +0 -0
  160. package/build/client/_app/version.json +1 -1
  161. package/build/client/_app/version.json.br +0 -0
  162. package/build/client/_app/version.json.gz +0 -0
  163. package/build/server/chunks/0-TQkJBtAu.js +9 -0
  164. package/build/server/chunks/0-TQkJBtAu.js.map +1 -0
  165. package/build/server/chunks/1-CKxKlw1l.js +9 -0
  166. package/build/server/chunks/{1-BQkMpHdq.js.map → 1-CKxKlw1l.js.map} +1 -1
  167. package/build/server/chunks/10-Bly5Nk7M.js +9 -0
  168. package/build/server/chunks/10-Bly5Nk7M.js.map +1 -0
  169. package/build/server/chunks/{15-CZBxPMAc.js → 15-BMEAVslz.js} +2 -2
  170. package/build/server/chunks/{15-CZBxPMAc.js.map → 15-BMEAVslz.js.map} +1 -1
  171. package/build/server/chunks/17-C8CYQOYP.js +9 -0
  172. package/build/server/chunks/17-C8CYQOYP.js.map +1 -0
  173. package/build/server/chunks/18-CtXVGBbS.js +9 -0
  174. package/build/server/chunks/18-CtXVGBbS.js.map +1 -0
  175. package/build/server/chunks/2-DvD0dAh0.js +9 -0
  176. package/build/server/chunks/2-DvD0dAh0.js.map +1 -0
  177. package/build/server/chunks/3-DBzpRDK8.js +9 -0
  178. package/build/server/chunks/3-DBzpRDK8.js.map +1 -0
  179. package/build/server/chunks/4-BUNytyC2.js +9 -0
  180. package/build/server/chunks/4-BUNytyC2.js.map +1 -0
  181. package/build/server/chunks/5-DNo8_tFS.js +9 -0
  182. package/build/server/chunks/{5-C7KuaSU9.js.map → 5-DNo8_tFS.js.map} +1 -1
  183. package/build/server/chunks/6-DUMdIfbf.js +9 -0
  184. package/build/server/chunks/6-DUMdIfbf.js.map +1 -0
  185. package/build/server/chunks/8-DxTJ555u.js +9 -0
  186. package/build/server/chunks/8-DxTJ555u.js.map +1 -0
  187. package/build/server/chunks/9-B4-5k50d.js +9 -0
  188. package/build/server/chunks/9-B4-5k50d.js.map +1 -0
  189. package/build/server/chunks/{AgentSidebar-XdGj1sqT.js → AgentSidebar-DvIlda-E.js} +5 -6
  190. package/build/server/chunks/AgentSidebar-DvIlda-E.js.map +1 -0
  191. package/build/server/chunks/{ChatStepTrace-DnvGtFpb.js → ChatStepTrace-CqqIjRLU.js} +41 -7
  192. package/build/server/chunks/ChatStepTrace-CqqIjRLU.js.map +1 -0
  193. package/build/server/chunks/MockGenerator-GEpXTgX0.js +117 -0
  194. package/build/server/chunks/MockGenerator-GEpXTgX0.js.map +1 -0
  195. package/build/server/chunks/{_layout.svelte-C5Y9nSXi.js → _layout.svelte-B-CkD4a0.js} +12 -6
  196. package/build/server/chunks/_layout.svelte-B-CkD4a0.js.map +1 -0
  197. package/build/server/chunks/{_layout.svelte-_oqm8kmr.js → _layout.svelte-BBFCF-xw.js} +7 -3
  198. package/build/server/chunks/_layout.svelte-BBFCF-xw.js.map +1 -0
  199. package/build/server/chunks/{_layout.svelte-C7JgNCIu.js → _layout.svelte-cPhmW5bZ.js} +3 -1
  200. package/build/server/chunks/_layout.svelte-cPhmW5bZ.js.map +1 -0
  201. package/build/server/chunks/{_page.svelte-QlYvVgTj.js → _page.svelte-0J2xaeyC.js} +70 -15
  202. package/build/server/chunks/_page.svelte-0J2xaeyC.js.map +1 -0
  203. package/build/server/chunks/{_page.svelte-BpRksNEG.js → _page.svelte-BgoX8Y6N.js} +2 -2
  204. package/build/server/chunks/{_page.svelte-BpRksNEG.js.map → _page.svelte-BgoX8Y6N.js.map} +1 -1
  205. package/build/server/chunks/{_page.svelte-bkzbqII3.js → _page.svelte-C_S_qUgZ.js} +479 -71
  206. package/build/server/chunks/_page.svelte-C_S_qUgZ.js.map +1 -0
  207. package/build/server/chunks/{_page.svelte-DDpBtd3X.js → _page.svelte-CqGOvPRO.js} +3 -4
  208. package/build/server/chunks/_page.svelte-CqGOvPRO.js.map +1 -0
  209. package/build/server/chunks/{_page.svelte-DOhPbizt.js → _page.svelte-Cxh9_vUl.js} +34 -22
  210. package/build/server/chunks/_page.svelte-Cxh9_vUl.js.map +1 -0
  211. package/build/server/chunks/{_page.svelte-CEhhEdMi.js → _page.svelte-zOI_s_3J.js} +122 -71
  212. package/build/server/chunks/_page.svelte-zOI_s_3J.js.map +1 -0
  213. package/build/server/chunks/conversation-view-memory-BZguFlTt.js +20 -0
  214. package/build/server/chunks/conversation-view-memory-BZguFlTt.js.map +1 -0
  215. package/build/server/chunks/log-content.svelte-CfQzj-v_.js.map +1 -1
  216. package/build/server/chunks/{client-DzvEkq51.js → source-selection-CcN1cgXc.js} +122 -117
  217. package/build/server/chunks/source-selection-CcN1cgXc.js.map +1 -0
  218. package/build/server/index.js +1 -1
  219. package/build/server/index.js.map +1 -1
  220. package/build/server/manifest.js +14 -14
  221. package/build/server/manifest.js.map +1 -1
  222. package/package.json +3 -3
  223. package/src/backend/lib/get-agent-factories.spec.ts +59 -0
  224. package/src/backend/lib/get-agent-factories.ts +31 -1
  225. package/src/backend/lib/voice-agent-lifecycle.test.ts +12 -8
  226. package/src/backend/lib/voice-agent-lifecycle.ts +2 -1
  227. package/src/backend/roleplay/agents.ts +18 -0
  228. package/src/backend/roleplay/router.ts +8 -3
  229. package/src/backend/tests/academy.ts +151 -93
  230. package/src/backend/tests/analyze.ts +0 -3
  231. package/src/backend/tests/data.spec.ts +139 -0
  232. package/src/backend/tests/data.ts +296 -65
  233. package/src/backend/tests/emulate-agents.ts +45 -45
  234. package/src/backend/tests/openai-prompt-cache.ts +68 -0
  235. package/src/backend/tests/promptfoo-runtime.spec.ts +41 -0
  236. package/src/backend/tests/promptfoo-runtime.ts +27 -0
  237. package/src/backend/tests/report-files.spec.ts +42 -0
  238. package/src/backend/tests/report-files.ts +101 -0
  239. package/src/backend/tests/router.ts +7 -1
  240. package/build/client/_app/immutable/assets/0.Dz9hcoGY.css +0 -1
  241. package/build/client/_app/immutable/assets/0.Dz9hcoGY.css.br +0 -0
  242. package/build/client/_app/immutable/assets/0.Dz9hcoGY.css.gz +0 -0
  243. package/build/client/_app/immutable/assets/10.BX46PfEJ.css.br +0 -0
  244. package/build/client/_app/immutable/assets/10.BX46PfEJ.css.gz +0 -0
  245. package/build/client/_app/immutable/chunks/-g1l5aoS.js.br +0 -0
  246. package/build/client/_app/immutable/chunks/-g1l5aoS.js.gz +0 -0
  247. package/build/client/_app/immutable/chunks/7E8m6dwk.js +0 -1
  248. package/build/client/_app/immutable/chunks/7E8m6dwk.js.br +0 -0
  249. package/build/client/_app/immutable/chunks/7E8m6dwk.js.gz +0 -0
  250. package/build/client/_app/immutable/chunks/7qRqthsa.js +0 -1
  251. package/build/client/_app/immutable/chunks/7qRqthsa.js.br +0 -2
  252. package/build/client/_app/immutable/chunks/7qRqthsa.js.gz +0 -0
  253. package/build/client/_app/immutable/chunks/AocOovHL.js.br +0 -0
  254. package/build/client/_app/immutable/chunks/AocOovHL.js.gz +0 -0
  255. package/build/client/_app/immutable/chunks/B0iEglSe.js +0 -1
  256. package/build/client/_app/immutable/chunks/B0iEglSe.js.br +0 -0
  257. package/build/client/_app/immutable/chunks/B0iEglSe.js.gz +0 -0
  258. package/build/client/_app/immutable/chunks/BIpSIzUr.js.br +0 -0
  259. package/build/client/_app/immutable/chunks/BIpSIzUr.js.gz +0 -0
  260. package/build/client/_app/immutable/chunks/BL8rPcmP.js.br +0 -0
  261. package/build/client/_app/immutable/chunks/BL8rPcmP.js.gz +0 -0
  262. package/build/client/_app/immutable/chunks/BM0jKynj.js.br +0 -0
  263. package/build/client/_app/immutable/chunks/BM0jKynj.js.gz +0 -0
  264. package/build/client/_app/immutable/chunks/BR5f5i_E.js.br +0 -0
  265. package/build/client/_app/immutable/chunks/BR5f5i_E.js.gz +0 -0
  266. package/build/client/_app/immutable/chunks/BTvePR6r.js +0 -1
  267. package/build/client/_app/immutable/chunks/BTvePR6r.js.br +0 -2
  268. package/build/client/_app/immutable/chunks/BTvePR6r.js.gz +0 -0
  269. package/build/client/_app/immutable/chunks/Bcu62lFq.js +0 -1
  270. package/build/client/_app/immutable/chunks/Bcu62lFq.js.br +0 -0
  271. package/build/client/_app/immutable/chunks/Bcu62lFq.js.gz +0 -0
  272. package/build/client/_app/immutable/chunks/C-whEtKd.js.br +0 -2
  273. package/build/client/_app/immutable/chunks/C-whEtKd.js.gz +0 -0
  274. package/build/client/_app/immutable/chunks/C1WqE8CP.js +0 -1
  275. package/build/client/_app/immutable/chunks/C1WqE8CP.js.br +0 -2
  276. package/build/client/_app/immutable/chunks/C1WqE8CP.js.gz +0 -0
  277. package/build/client/_app/immutable/chunks/C4SnSfql.js +0 -1
  278. package/build/client/_app/immutable/chunks/C4SnSfql.js.br +0 -0
  279. package/build/client/_app/immutable/chunks/C4SnSfql.js.gz +0 -0
  280. package/build/client/_app/immutable/chunks/CSv1VDXg.js +0 -1
  281. package/build/client/_app/immutable/chunks/CSv1VDXg.js.br +0 -0
  282. package/build/client/_app/immutable/chunks/CSv1VDXg.js.gz +0 -0
  283. package/build/client/_app/immutable/chunks/CXo2BVs0.js.br +0 -0
  284. package/build/client/_app/immutable/chunks/CXo2BVs0.js.gz +0 -0
  285. package/build/client/_app/immutable/chunks/CZLNSMhF.js.br +0 -0
  286. package/build/client/_app/immutable/chunks/CZLNSMhF.js.gz +0 -0
  287. package/build/client/_app/immutable/chunks/Cosugfe8.js.br +0 -0
  288. package/build/client/_app/immutable/chunks/Cosugfe8.js.gz +0 -0
  289. package/build/client/_app/immutable/chunks/CqxCTxvJ.js.br +0 -0
  290. package/build/client/_app/immutable/chunks/CqxCTxvJ.js.gz +0 -0
  291. package/build/client/_app/immutable/chunks/CxLG58rb.js +0 -1
  292. package/build/client/_app/immutable/chunks/CxLG58rb.js.br +0 -0
  293. package/build/client/_app/immutable/chunks/CxLG58rb.js.gz +0 -0
  294. package/build/client/_app/immutable/chunks/D-puyTnN.js +0 -1
  295. package/build/client/_app/immutable/chunks/D-puyTnN.js.br +0 -0
  296. package/build/client/_app/immutable/chunks/D-puyTnN.js.gz +0 -0
  297. package/build/client/_app/immutable/chunks/D4obUD_s.js +0 -1
  298. package/build/client/_app/immutable/chunks/D4obUD_s.js.br +0 -0
  299. package/build/client/_app/immutable/chunks/D4obUD_s.js.gz +0 -0
  300. package/build/client/_app/immutable/chunks/D8e7SQh9.js +0 -1
  301. package/build/client/_app/immutable/chunks/D8e7SQh9.js.br +0 -0
  302. package/build/client/_app/immutable/chunks/D8e7SQh9.js.gz +0 -0
  303. package/build/client/_app/immutable/chunks/DNLcpWO_.js.br +0 -0
  304. package/build/client/_app/immutable/chunks/DNLcpWO_.js.gz +0 -0
  305. package/build/client/_app/immutable/chunks/DTlGvFHe.js.br +0 -0
  306. package/build/client/_app/immutable/chunks/DTlGvFHe.js.gz +0 -0
  307. package/build/client/_app/immutable/chunks/DYhTBLDc.js.br +0 -0
  308. package/build/client/_app/immutable/chunks/DYhTBLDc.js.gz +0 -0
  309. package/build/client/_app/immutable/chunks/DlZKMmqG.js +0 -4
  310. package/build/client/_app/immutable/chunks/DlZKMmqG.js.br +0 -0
  311. package/build/client/_app/immutable/chunks/DlZKMmqG.js.gz +0 -0
  312. package/build/client/_app/immutable/chunks/Dv0gjm25.js +0 -1
  313. package/build/client/_app/immutable/chunks/Dv0gjm25.js.br +0 -0
  314. package/build/client/_app/immutable/chunks/Dv0gjm25.js.gz +0 -0
  315. package/build/client/_app/immutable/chunks/Dv8m1cTI.js.br +0 -0
  316. package/build/client/_app/immutable/chunks/HJHYLHDn.js.br +0 -0
  317. package/build/client/_app/immutable/chunks/HJHYLHDn.js.gz +0 -0
  318. package/build/client/_app/immutable/chunks/HXxIsSOO.js.br +0 -0
  319. package/build/client/_app/immutable/chunks/HXxIsSOO.js.gz +0 -0
  320. package/build/client/_app/immutable/chunks/UiXQyKcj.js.br +0 -0
  321. package/build/client/_app/immutable/chunks/UiXQyKcj.js.gz +0 -0
  322. package/build/client/_app/immutable/chunks/_yZg6Sf-.js +0 -2
  323. package/build/client/_app/immutable/chunks/_yZg6Sf-.js.br +0 -0
  324. package/build/client/_app/immutable/chunks/_yZg6Sf-.js.gz +0 -0
  325. package/build/client/_app/immutable/chunks/ks45gSgq.js +0 -1
  326. package/build/client/_app/immutable/chunks/ks45gSgq.js.br +0 -0
  327. package/build/client/_app/immutable/chunks/ks45gSgq.js.gz +0 -0
  328. package/build/client/_app/immutable/entry/app.qxHHRD2v.js +0 -2
  329. package/build/client/_app/immutable/entry/app.qxHHRD2v.js.br +0 -0
  330. package/build/client/_app/immutable/entry/app.qxHHRD2v.js.gz +0 -0
  331. package/build/client/_app/immutable/entry/start.CSxD7zE8.js +0 -1
  332. package/build/client/_app/immutable/entry/start.CSxD7zE8.js.br +0 -2
  333. package/build/client/_app/immutable/entry/start.CSxD7zE8.js.gz +0 -0
  334. package/build/client/_app/immutable/nodes/0.BiYgQci8.js.br +0 -0
  335. package/build/client/_app/immutable/nodes/0.BiYgQci8.js.gz +0 -0
  336. package/build/client/_app/immutable/nodes/1.CShtqUFK.js +0 -1
  337. package/build/client/_app/immutable/nodes/1.CShtqUFK.js.br +0 -0
  338. package/build/client/_app/immutable/nodes/1.CShtqUFK.js.gz +0 -0
  339. package/build/client/_app/immutable/nodes/10.dE-XOJgk.js +0 -7
  340. package/build/client/_app/immutable/nodes/10.dE-XOJgk.js.br +0 -0
  341. package/build/client/_app/immutable/nodes/10.dE-XOJgk.js.gz +0 -0
  342. package/build/client/_app/immutable/nodes/15.Wmhj7gOy.js.br +0 -0
  343. package/build/client/_app/immutable/nodes/15.Wmhj7gOy.js.gz +0 -0
  344. package/build/client/_app/immutable/nodes/17.BwzZwCD0.js +0 -3
  345. package/build/client/_app/immutable/nodes/17.BwzZwCD0.js.br +0 -0
  346. package/build/client/_app/immutable/nodes/17.BwzZwCD0.js.gz +0 -0
  347. package/build/client/_app/immutable/nodes/18.BUMDuaQF.js +0 -11
  348. package/build/client/_app/immutable/nodes/18.BUMDuaQF.js.br +0 -0
  349. package/build/client/_app/immutable/nodes/18.BUMDuaQF.js.gz +0 -0
  350. package/build/client/_app/immutable/nodes/2.DniQTiGZ.js.br +0 -0
  351. package/build/client/_app/immutable/nodes/2.DniQTiGZ.js.gz +0 -0
  352. package/build/client/_app/immutable/nodes/3.BIO1e-qq.js +0 -1
  353. package/build/client/_app/immutable/nodes/3.BIO1e-qq.js.br +0 -0
  354. package/build/client/_app/immutable/nodes/3.BIO1e-qq.js.gz +0 -0
  355. package/build/client/_app/immutable/nodes/4.BwfVTawo.js +0 -1
  356. package/build/client/_app/immutable/nodes/4.BwfVTawo.js.br +0 -0
  357. package/build/client/_app/immutable/nodes/4.BwfVTawo.js.gz +0 -0
  358. package/build/client/_app/immutable/nodes/5.DdodCQcX.js +0 -1
  359. package/build/client/_app/immutable/nodes/5.DdodCQcX.js.br +0 -0
  360. package/build/client/_app/immutable/nodes/5.DdodCQcX.js.gz +0 -0
  361. package/build/client/_app/immutable/nodes/6.EIjYVI_W.js +0 -1
  362. package/build/client/_app/immutable/nodes/6.EIjYVI_W.js.br +0 -0
  363. package/build/client/_app/immutable/nodes/6.EIjYVI_W.js.gz +0 -0
  364. package/build/client/_app/immutable/nodes/8.2oZsfzo8.js.br +0 -0
  365. package/build/client/_app/immutable/nodes/8.2oZsfzo8.js.gz +0 -0
  366. package/build/client/_app/immutable/nodes/9.CjWG6Ha9.js +0 -2
  367. package/build/client/_app/immutable/nodes/9.CjWG6Ha9.js.br +0 -0
  368. package/build/client/_app/immutable/nodes/9.CjWG6Ha9.js.gz +0 -0
  369. package/build/server/chunks/0-S8VzkGgQ.js +0 -9
  370. package/build/server/chunks/0-S8VzkGgQ.js.map +0 -1
  371. package/build/server/chunks/1-BQkMpHdq.js +0 -9
  372. package/build/server/chunks/10-D1k3_nkw.js +0 -9
  373. package/build/server/chunks/10-D1k3_nkw.js.map +0 -1
  374. package/build/server/chunks/17-B72NO3Iu.js +0 -9
  375. package/build/server/chunks/17-B72NO3Iu.js.map +0 -1
  376. package/build/server/chunks/18-4kgQqgz-.js +0 -9
  377. package/build/server/chunks/18-4kgQqgz-.js.map +0 -1
  378. package/build/server/chunks/2-DO8jW3Pl.js +0 -9
  379. package/build/server/chunks/2-DO8jW3Pl.js.map +0 -1
  380. package/build/server/chunks/3-0_f3eZZi.js +0 -9
  381. package/build/server/chunks/3-0_f3eZZi.js.map +0 -1
  382. package/build/server/chunks/4-UxjRsyDW.js +0 -9
  383. package/build/server/chunks/4-UxjRsyDW.js.map +0 -1
  384. package/build/server/chunks/5-C7KuaSU9.js +0 -9
  385. package/build/server/chunks/6-C6EG0SXo.js +0 -9
  386. package/build/server/chunks/6-C6EG0SXo.js.map +0 -1
  387. package/build/server/chunks/8-BTSvDZhI.js +0 -9
  388. package/build/server/chunks/8-BTSvDZhI.js.map +0 -1
  389. package/build/server/chunks/9-CepnUatA.js +0 -9
  390. package/build/server/chunks/9-CepnUatA.js.map +0 -1
  391. package/build/server/chunks/AgentSidebar-XdGj1sqT.js.map +0 -1
  392. package/build/server/chunks/ChatStepTrace-DnvGtFpb.js.map +0 -1
  393. package/build/server/chunks/_layout.svelte-C5Y9nSXi.js.map +0 -1
  394. package/build/server/chunks/_layout.svelte-C7JgNCIu.js.map +0 -1
  395. package/build/server/chunks/_layout.svelte-_oqm8kmr.js.map +0 -1
  396. package/build/server/chunks/_page.svelte-CEhhEdMi.js.map +0 -1
  397. package/build/server/chunks/_page.svelte-DDpBtd3X.js.map +0 -1
  398. package/build/server/chunks/_page.svelte-DOhPbizt.js.map +0 -1
  399. package/build/server/chunks/_page.svelte-QlYvVgTj.js.map +0 -1
  400. package/build/server/chunks/_page.svelte-bkzbqII3.js.map +0 -1
  401. package/build/server/chunks/client-DzvEkq51.js.map +0 -1
  402. package/build/server/chunks/source-selection-eA--VEKo.js +0 -124
  403. package/build/server/chunks/source-selection-eA--VEKo.js.map +0 -1
@@ -1,9 +1,8 @@
1
1
  // cspell:words conv
2
- import { type ProviderOptions } from 'promptfoo'
3
- import * as promptfoo from 'promptfoo'
4
2
  import { type Message, type StepTrace, type StepTracer } from '@recombine-ai/engine'
5
3
  import { loadAgents } from './emulate-agents'
6
- import { type Assertion as PFAssertion } from 'promptfoo'
4
+ import { installTestPromptCacheBust } from './openai-prompt-cache'
5
+ import { promptfoo } from './promptfoo-runtime'
7
6
  // Backend: per-project JSON test suites (no CSV)
8
7
  import { tryReadConfig } from './config'
9
8
  import { mkdir, rename, unlink, writeFile } from 'node:fs/promises'
@@ -11,11 +10,18 @@ import path from 'node:path'
11
10
  import { readSuite, type TestRow as SuiteTestRow } from './suite'
12
11
  // Use types derived from promptfoo exports instead of incorrect default type imports
13
12
 
14
- import type { ApiProvider, CallApiContextParams, ProviderResponse } from 'promptfoo'
13
+ import type {
14
+ ApiProvider,
15
+ Assertion as PFAssertion,
16
+ CallApiContextParams,
17
+ ProviderOptions,
18
+ ProviderResponse
19
+ } from 'promptfoo'
15
20
  import deepmerge from 'deepmerge'
16
21
  import fs from 'node:fs'
17
22
  import { getRootPath } from '../lib/get-root-path'
18
23
  import { makeContext } from './context-utils'
24
+ import { queueReportCompression, REPORT_JSON_SUFFIX } from './report-files'
19
25
 
20
26
  // ----------------- Type aliases & helpers -----------------
21
27
  type Context = Record<string, unknown>
@@ -93,6 +99,30 @@ type CaptureMode = 'immediate' | 'final'
93
99
 
94
100
  const activeRunsBySuiteKey = new Map<string, AbortController>()
95
101
 
102
+ export interface PromptTestsProgress {
103
+ plannedTestIds: string[]
104
+ runningTestIds: string[]
105
+ completedTestIds: string[]
106
+ }
107
+
108
+ type MutablePromptTestsProgress = PromptTestsProgress & {
109
+ expectedRuns: number
110
+ startedTestIds: Set<string>
111
+ completedRunsByTestId: Map<string, number>
112
+ }
113
+
114
+ const activeProgressBySuiteKey = new Map<string, MutablePromptTestsProgress>()
115
+
116
+ export function getPromptTestsProgress(suiteKey: string): PromptTestsProgress | null {
117
+ const progress = activeProgressBySuiteKey.get(suiteKey)
118
+ if (!progress) return null
119
+ return {
120
+ plannedTestIds: [...progress.plannedTestIds],
121
+ runningTestIds: [...progress.runningTestIds],
122
+ completedTestIds: [...progress.completedTestIds]
123
+ }
124
+ }
125
+
96
126
  export type SuiteSelection = {
97
127
  // `suiteKey` is the filesystem/report key (e.g. `voiceSupport.en-gb`).
98
128
  // `agentId` is the canonical factory key (workflow export key, e.g. `voiceSupport`).
@@ -122,6 +152,7 @@ export async function runPromptTestsManaged(
122
152
  return await runPromptTests(suite, selection, { signal: controller.signal })
123
153
  } finally {
124
154
  activeRunsBySuiteKey.delete(suite.suiteKey)
155
+ activeProgressBySuiteKey.delete(suite.suiteKey)
125
156
  }
126
157
  }
127
158
 
@@ -234,6 +265,7 @@ export class RecombineAgentProvider implements ApiProvider {
234
265
  protected providerId: string
235
266
  public config: Record<string, unknown>
236
267
  private abortSignal?: AbortSignal
268
+ private onTestStarted?: (testId: string) => void
237
269
 
238
270
  constructor(options: ProviderOptions) {
239
271
  this.providerId = options.id || 'recombine-agent'
@@ -244,6 +276,10 @@ export class RecombineAgentProvider implements ApiProvider {
244
276
  this.abortSignal = signal
245
277
  }
246
278
 
279
+ setOnTestStarted(callback: (testId: string) => void) {
280
+ this.onTestStarted = callback
281
+ }
282
+
247
283
  id() {
248
284
  return this.providerId
249
285
  }
@@ -252,6 +288,7 @@ export class RecombineAgentProvider implements ApiProvider {
252
288
  const projectRoot = path.dirname(await getRootPath())
253
289
  const vars = (context?.vars || {}) as unknown as {
254
290
  agentId: string
291
+ testId: string
255
292
  region?: string
256
293
  conversation: Conversation
257
294
  conversationParseError?: string
@@ -259,6 +296,8 @@ export class RecombineAgentProvider implements ApiProvider {
259
296
  mocks?: Context
260
297
  captureMode?: CaptureMode | string
261
298
  }
299
+ const testId = String(vars.testId ?? '')
300
+ if (testId) this.onTestStarted?.(testId)
262
301
 
263
302
  if (typeof vars.conversationParseError === 'string' && vars.conversationParseError.trim()) {
264
303
  return { output: '', error: vars.conversationParseError }
@@ -293,7 +332,6 @@ export class RecombineAgentProvider implements ApiProvider {
293
332
  const stepTracer: StepTracer = {
294
333
  addStepTrace: (stepTrace: StepTrace) => {
295
334
  try {
296
- console.log(`trace: ${stepTrace.name}`)
297
335
  capturedStepTraces.push(stepTrace)
298
336
  } catch {
299
337
  // best-effort capture; ignore serialization issues
@@ -438,6 +476,7 @@ export class RecombineAgentProvider implements ApiProvider {
438
476
  }
439
477
  }
440
478
  } catch (err) {
479
+ if (this.abortSignal?.aborted) throw err
441
480
  const msg = err instanceof Error ? `${err.name}: ${err.message}` : String(err)
442
481
  capturedLogs.push(`[ERROR] ${msg}`)
443
482
  try {
@@ -574,7 +613,7 @@ export async function runPromptTests(
574
613
  const cfg = (await tryReadConfig(suiteKey)) || {}
575
614
 
576
615
  const repeats = Number(cfg?.repeats ?? 1)
577
- const concurrency = Number(cfg?.concurrency ?? 1)
616
+ const concurrency = Math.min(75, Math.max(1, Math.floor(Number(cfg?.concurrency ?? 75))))
578
617
  const baseContext = loadedSuite?.meta?.defaultContext || {}
579
618
 
580
619
  // Add an index field to each row for mapping
@@ -586,12 +625,27 @@ export async function runPromptTests(
586
625
  }
587
626
 
588
627
  const tests = toTestInputs(indexedRows, baseContext, selection)
628
+ const plannedTestIds = tests.map((test) => String(test.id ?? '')).filter(Boolean)
629
+ const progress: MutablePromptTestsProgress = {
630
+ plannedTestIds,
631
+ runningTestIds: [],
632
+ completedTestIds: [],
633
+ expectedRuns: repeats,
634
+ startedTestIds: new Set(),
635
+ completedRunsByTestId: new Map()
636
+ }
637
+ activeProgressBySuiteKey.set(suiteKey, progress)
589
638
 
590
639
  // Provider function with stable label
591
640
  const providerInstance = new RecombineAgentProvider({
592
641
  id: 'recombine-agent'
593
642
  })
594
643
  providerInstance.setAbortSignal(signal)
644
+ providerInstance.setOnTestStarted((testId) => {
645
+ if (progress.completedTestIds.includes(testId)) return
646
+ progress.startedTestIds.add(testId)
647
+ progress.runningTestIds = [...progress.startedTestIds]
648
+ })
595
649
  const providerFn = Object.assign(
596
650
  (_promptStr: string, ctx?: CallApiContextParams) => {
597
651
  if (signal?.aborted) {
@@ -605,39 +659,40 @@ export async function runPromptTests(
605
659
  const hookKey = `__bosun_promptfoo_hook_${process.pid}_${Date.now()}_${Math.random().toString(16).slice(2)}`
606
660
  const extensionCode = `globalThis[${JSON.stringify(hookKey)}]?.(output, context)`
607
661
 
608
- const testSuites = tests.map((test) => ({
662
+ const testSuite = {
609
663
  prompts: ['agent-run'], // placeholder; provider ignores
610
- scenario: test.scenario,
611
- tests: [
612
- {
613
- metadata: { testId: test.id },
614
- assert: test.assertions,
615
- model: 'not used!',
616
- vars: {
617
- agentId: suite.agentId,
618
- ...(suite.region ? { region: suite.region } : {}),
619
- context: test.context,
620
- ...(typeof test.mocks !== 'undefined' ? { mocks: test.mocks } : {}),
621
- captureMode: test.captureMode,
622
- conversation: test.conversation,
623
- ...(test.conversationParseError
624
- ? { conversationParseError: test.conversationParseError }
625
- : {})
626
- }
664
+ tests: tests.map((test) => ({
665
+ description: test.scenario,
666
+ metadata: { testId: test.id },
667
+ assert: test.assertions,
668
+ model: 'not used!',
669
+ vars: {
670
+ agentId: suite.agentId,
671
+ testId: String(test.id ?? ''),
672
+ ...(suite.region ? { region: suite.region } : {}),
673
+ context: test.context,
674
+ ...(typeof test.mocks !== 'undefined' ? { mocks: test.mocks } : {}),
675
+ captureMode: test.captureMode,
676
+ conversation: test.conversation,
677
+ ...(test.conversationParseError
678
+ ? { conversationParseError: test.conversationParseError }
679
+ : {})
627
680
  }
628
- ],
681
+ })),
629
682
  providers: [providerFn],
630
683
  extensions: [extensionCode]
631
- }))
684
+ }
632
685
 
633
- const projectRoot = path.dirname(await getRootPath())
686
+ const rootPath = await getRootPath()
687
+ installTestPromptCacheBust(rootPath)
688
+ const projectRoot = path.dirname(rootPath)
634
689
  const outDir = path.join(projectRoot, 'data', 'test_runs')
635
690
  await mkdir(outDir, { recursive: true })
636
691
 
637
692
  // Use full ISO timestamp and sanitize ':' for filesystem safety
638
693
  const iso = new Date().toISOString()
639
694
  const isoFile = iso.replace(/:/g, '-')
640
- const outPath = path.join(outDir, `${suiteKey}.${isoFile}.report.json`)
695
+ const outPath = path.join(outDir, `${suiteKey}.${isoFile}${REPORT_JSON_SUFFIX}`)
641
696
 
642
697
  async function writeReport(report: WrittenRunReport) {
643
698
  const dir = path.dirname(outPath)
@@ -746,6 +801,15 @@ export async function runPromptTests(
746
801
 
747
802
  const entry = toResultEntry(r)
748
803
  reportRow.results.push(entry)
804
+ const completedRuns = (progress.completedRunsByTestId.get(testId) ?? 0) + 1
805
+ progress.completedRunsByTestId.set(testId, completedRuns)
806
+ if (completedRuns >= progress.expectedRuns) {
807
+ progress.startedTestIds.delete(testId)
808
+ progress.runningTestIds = [...progress.startedTestIds]
809
+ if (!progress.completedTestIds.includes(testId)) {
810
+ progress.completedTestIds = [...progress.completedTestIds, testId]
811
+ }
812
+ }
749
813
 
750
814
  const counts = countsByTestId.get(testId) ?? { pass: 0, total: 0 }
751
815
  counts.total += 1
@@ -764,77 +828,68 @@ export async function runPromptTests(
764
828
  }
765
829
  }
766
830
 
767
- try {
768
- for (const suite of testSuites) {
769
- if (signal?.aborted) break
770
- const testId = String((suite.tests?.[0] as any)?.metadata?.testId ?? '')
771
- const originalRow = testId ? typedRows.find((r) => String(r.id) === testId) : undefined
772
- try {
773
- console.log(`Running: ${suite.scenario}`)
774
- const ev = await promptfoo.evaluate(suite, {
775
- repeat: repeats,
776
- cache: false,
777
- maxConcurrency: concurrency,
778
- abortSignal: signal
779
- })
831
+ const selectedOriginalRows = plannedTestIds
832
+ .map((testId) => originalRowById.get(testId))
833
+ .filter((row): row is SuiteTestRow => !!row)
834
+ const persistEvaluation = (evaluation: PFEval) => {
835
+ for (const rowToWrite of buildTableRows([evaluation], selectedOriginalRows, suiteKey)) {
836
+ const testId = String(rowToWrite.id ?? '')
837
+ const originalRow = originalRowById.get(testId)
838
+ if (!testId || !originalRow) continue
839
+ const pinned: 'TRUE' | 'FALSE' =
840
+ (rowToWrite.pinned || '').toUpperCase() === 'TRUE' ? 'TRUE' : 'FALSE'
841
+ byTestId.set(testId, {
842
+ ...rowToWrite,
843
+ test: originalRow,
844
+ agent: suiteKey,
845
+ test_id: testId,
846
+ run_timestamp: iso,
847
+ run_config: JSON.stringify(runConfig),
848
+ pinned_at_run: scope === 'all' || !!onlyTestId ? 'TRUE' : pinned,
849
+ run_scope: scope
850
+ })
851
+ }
852
+ body.rows = Array.from(byTestId.values())
853
+ }
780
854
 
781
- if (originalRow && testId) {
782
- const rowToWrite = buildTableRows([ev], [originalRow], suiteKey)[0]
783
- const pinned: 'TRUE' | 'FALSE' =
784
- (rowToWrite.pinned || '').toUpperCase() === 'TRUE' ? 'TRUE' : 'FALSE'
785
- byTestId.set(testId, {
786
- ...rowToWrite,
787
- test: originalRow,
788
- agent: suiteKey,
789
- test_id: testId,
790
- run_timestamp: iso,
791
- run_config: JSON.stringify(runConfig),
792
- pinned_at_run: scope === 'all' || !!onlyTestId ? 'TRUE' : pinned,
793
- run_scope: scope
794
- })
795
- body.rows = Array.from(byTestId.values())
796
- await enqueueWrite()
797
- }
798
- } catch (err) {
799
- const cancelled =
800
- !!signal?.aborted ||
801
- (err instanceof Error && /operation cancelled/i.test(err.message || ''))
802
- if (cancelled) {
803
- break
804
- }
805
- const msg = err instanceof Error ? `${err.name}: ${err.message}` : String(err)
806
- const ev = {
807
- results: [
808
- {
855
+ const previousIsTest = process.env.IS_TEST
856
+ process.env.IS_TEST = 'true'
857
+ try {
858
+ if (tests.length > 0 && !signal?.aborted) {
859
+ const evaluation = await promptfoo.evaluate(testSuite, {
860
+ repeat: repeats,
861
+ cache: false,
862
+ maxConcurrency: concurrency,
863
+ // Bosun reports run progress in the tests UI. Promptfoo's terminal progress bars
864
+ // continuously rewrite stdout (up to 20 bars at higher concurrency).
865
+ showProgressBar: false,
866
+ abortSignal: signal
867
+ })
868
+ persistEvaluation(evaluation)
869
+ await enqueueWrite()
870
+ }
871
+ } catch (err) {
872
+ const cancelled =
873
+ !!signal?.aborted || (err instanceof Error && /operation cancelled/i.test(err.message || ''))
874
+ if (!cancelled) {
875
+ const msg = err instanceof Error ? `${err.name}: ${err.message}` : String(err)
876
+ const evaluation = {
877
+ results: plannedTestIds.map(
878
+ (testId) =>
879
+ ({
809
880
  success: false,
810
881
  response: { output: '' },
811
882
  error: msg,
812
- metadata: { testId: (suite.tests?.[0] as any)?.metadata?.testId }
813
- } as any
814
- ]
815
- } as any
816
-
817
- // Persist failure row immediately for real-time UI updates.
818
- if (originalRow && testId) {
819
- const rowToWrite = buildTableRows([ev], [originalRow], suiteKey)[0]
820
- const pinned: 'TRUE' | 'FALSE' =
821
- (rowToWrite.pinned || '').toUpperCase() === 'TRUE' ? 'TRUE' : 'FALSE'
822
- byTestId.set(testId, {
823
- ...rowToWrite,
824
- test: originalRow,
825
- agent: suiteKey,
826
- test_id: testId,
827
- run_timestamp: iso,
828
- run_config: JSON.stringify(runConfig),
829
- pinned_at_run: scope === 'all' || !!onlyTestId ? 'TRUE' : pinned,
830
- run_scope: scope
831
- })
832
- body.rows = Array.from(byTestId.values())
833
- await enqueueWrite()
834
- }
835
- }
883
+ metadata: { testId }
884
+ }) as any
885
+ )
886
+ } as PFEval
887
+ persistEvaluation(evaluation)
888
+ await enqueueWrite()
836
889
  }
837
890
  } finally {
891
+ if (previousIsTest == null) delete process.env.IS_TEST
892
+ else process.env.IS_TEST = previousIsTest
838
893
  if (writeTimer) clearTimeout(writeTimer)
839
894
  writeTimer = null
840
895
  delete (globalThis as any)[hookKey]
@@ -844,6 +899,9 @@ export async function runPromptTests(
844
899
  // Ensure the final report is written even if no tests ran (or an update was skipped).
845
900
  body.rows = Array.from(byTestId.values())
846
901
  await enqueueWrite()
902
+ void queueReportCompression(outPath).catch((error) => {
903
+ console.error(`Failed to compress completed test report ${path.basename(outPath)}`, error)
904
+ })
847
905
 
848
906
  return body
849
907
  }
@@ -107,9 +107,6 @@ export async function analyzeTraceQuotes(input: {
107
107
  }
108
108
 
109
109
  const conversation = decodeHtmlEntities(String(input.stringifiedConversation ?? ''))
110
- if (!conversation) {
111
- throw new Error('Missing conversation')
112
- }
113
110
 
114
111
  const resolvedModel = resolveModel(input.model)
115
112
  if (!resolvedModel) {
@@ -0,0 +1,139 @@
1
+ import { mkdtemp, mkdir, readdir, readFile, rm, stat, utimes, writeFile } from 'node:fs/promises'
2
+ import os from 'node:os'
3
+ import path from 'node:path'
4
+ import { afterEach, describe, expect, it, vi } from 'vitest'
5
+
6
+ const root = vi.hoisted(() => ({ path: '' }))
7
+
8
+ vi.mock('../lib/get-root-path', () => ({
9
+ getRootPath: async () => root.path
10
+ }))
11
+
12
+ import { getRunResultEntry, loadLatestResultsByTest } from './data'
13
+ import { compressReportFile } from './report-files'
14
+
15
+ const temporaryDirectories: string[] = []
16
+
17
+ afterEach(async () => {
18
+ await Promise.all(temporaryDirectories.splice(0).map((dir) => rm(dir, { recursive: true })))
19
+ })
20
+
21
+ function testRow(id: string) {
22
+ return {
23
+ id,
24
+ pinned: 'TRUE',
25
+ scenario: id,
26
+ assertion_type: 'equals',
27
+ assertion_value: '',
28
+ conversation: 'user: hello',
29
+ expected_output: 'hello',
30
+ timeout: '0',
31
+ similarity_threshold: '50',
32
+ context: {}
33
+ }
34
+ }
35
+
36
+ function reportRow(testId: string, success: boolean) {
37
+ return {
38
+ test_id: testId,
39
+ pinned_at_run: 'TRUE',
40
+ success_rate_percent: success ? 100 : 0,
41
+ success_rate: success ? '1/1' : '0/1',
42
+ results: [
43
+ {
44
+ success,
45
+ result: `${testId}-${success ? 'pass' : 'fail'}`,
46
+ evaluation: '',
47
+ errors: ''
48
+ }
49
+ ]
50
+ }
51
+ }
52
+
53
+ async function writeReport(dir: string, agent: string, timestamp: string, rows: unknown[]) {
54
+ const file = path.join(dir, `${agent}.${timestamp}.report.json`)
55
+ await writeFile(file, JSON.stringify({ rows }), 'utf8')
56
+ return file
57
+ }
58
+
59
+ describe('loadLatestResultsByTest', () => {
60
+ it('persists summaries and incrementally reads appended reports', async () => {
61
+ const projectDir = await mkdtemp(path.join(os.tmpdir(), 'bosun-latest-results-'))
62
+ temporaryDirectories.push(projectDir)
63
+ const bosunDir = path.join(projectDir, 'src', 'lib', 'bosun')
64
+ const reportDir = path.join(bosunDir, 'data', 'test_runs')
65
+ await mkdir(reportDir, { recursive: true })
66
+ root.path = path.join(bosunDir, 'index.ts')
67
+
68
+ const agent = `agent-${Date.now()}-${Math.random().toString(16).slice(2)}`
69
+ await writeFile(
70
+ path.join(bosunDir, `${agent}.tests.jsonl`),
71
+ [
72
+ JSON.stringify({ version: 1, meta: {} }),
73
+ JSON.stringify(testRow('a')),
74
+ JSON.stringify(testRow('b'))
75
+ ].join('\n') + '\n',
76
+ 'utf8'
77
+ )
78
+
79
+ const firstReport = await writeReport(reportDir, agent, '2026-07-16T10-00-00.000Z', [
80
+ reportRow('a', false),
81
+ reportRow('b', false)
82
+ ])
83
+ await utimes(firstReport, 1_700_000_000, 1_700_000_000)
84
+ await writeReport(reportDir, agent, '2026-07-16T11-00-00.000Z', [reportRow('a', true)])
85
+
86
+ const initial = await loadLatestResultsByTest(agent)
87
+ expect(initial.a.results[0].success).toBe(true)
88
+ expect(initial.a.previous).toMatchObject({ success_count: 0, total_runs: 1 })
89
+ expect(initial.b.is_old).toBe(true)
90
+
91
+ expect((await readdir(reportDir)).some((name) => name.includes('latest-results'))).toBe(true)
92
+
93
+ // Keep the cached file signature but make the old report unreadable as JSON. An incremental
94
+ // update must only read the newly appended report and therefore still succeed.
95
+ const firstState = await stat(firstReport)
96
+ const original = await readFile(firstReport)
97
+ await writeFile(firstReport, Buffer.alloc(original.length, 0x20))
98
+ await utimes(firstReport, firstState.atimeMs / 1000, firstState.mtimeMs / 1000)
99
+ expect((await stat(firstReport)).mtimeMs).toBe(firstState.mtimeMs)
100
+ await writeReport(reportDir, agent, '2026-07-16T12-00-00.000Z', [reportRow('b', true)])
101
+
102
+ const updated = await loadLatestResultsByTest(agent)
103
+ expect(updated.a.is_old).toBe(true)
104
+ expect(updated.b.results[0]).toMatchObject({ success: true, result: 'b-pass' })
105
+ expect(updated.b.previous).toMatchObject({ success_count: 0, total_runs: 1 })
106
+ })
107
+
108
+ it('loads compressed reports and keeps the cache valid when the latest report is compressed', async () => {
109
+ const projectDir = await mkdtemp(path.join(os.tmpdir(), 'bosun-gzip-results-'))
110
+ temporaryDirectories.push(projectDir)
111
+ const bosunDir = path.join(projectDir, 'src', 'lib', 'bosun')
112
+ const reportDir = path.join(bosunDir, 'data', 'test_runs')
113
+ await mkdir(reportDir, { recursive: true })
114
+ root.path = path.join(bosunDir, 'index.ts')
115
+
116
+ const agent = `agent-${Date.now()}-${Math.random().toString(16).slice(2)}`
117
+ await writeFile(
118
+ path.join(bosunDir, `${agent}.tests.jsonl`),
119
+ [JSON.stringify({ version: 1, meta: {} }), JSON.stringify(testRow('a'))].join('\n') + '\n',
120
+ 'utf8'
121
+ )
122
+
123
+ await compressReportFile(
124
+ await writeReport(reportDir, agent, '2026-07-16T10-00-00.000Z', [reportRow('a', false)])
125
+ )
126
+ const latestReport = await writeReport(reportDir, agent, '2026-07-16T11-00-00.000Z', [
127
+ reportRow('a', true)
128
+ ])
129
+
130
+ const initial = await loadLatestResultsByTest(agent)
131
+ expect(initial.a.results[0].success).toBe(true)
132
+ expect(initial.a.previous).toMatchObject({ success_count: 0, total_runs: 1 })
133
+
134
+ await compressReportFile(latestReport)
135
+ const compressed = await loadLatestResultsByTest(agent)
136
+ expect(compressed.a.results[0]).toMatchObject({ success: true, result: 'a-pass' })
137
+ expect((await getRunResultEntry(agent, 'a')).success).toBe(true)
138
+ })
139
+ })