@recombine-ai/bosun 0.10.1 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. package/README.md +12 -0
  2. package/bin/bosun.js +3 -44
  3. package/build/client/_app/immutable/assets/0.CH-WU4JN.css +1 -0
  4. package/build/client/_app/immutable/assets/0.CH-WU4JN.css.br +0 -0
  5. package/build/client/_app/immutable/assets/0.CH-WU4JN.css.gz +0 -0
  6. package/build/client/_app/immutable/assets/{10.BX46PfEJ.css → 10.DIVePEYf.css} +1 -1
  7. package/build/client/_app/immutable/assets/10.DIVePEYf.css.br +0 -0
  8. package/build/client/_app/immutable/assets/10.DIVePEYf.css.gz +0 -0
  9. package/build/client/_app/immutable/chunks/{Dp760K13.js → 07xNIa59.js} +2 -2
  10. package/build/client/_app/immutable/chunks/07xNIa59.js.br +0 -0
  11. package/build/client/_app/immutable/chunks/07xNIa59.js.gz +0 -0
  12. package/build/client/_app/immutable/chunks/BDl2QuyN.js +1 -0
  13. package/build/client/_app/immutable/chunks/BDl2QuyN.js.br +0 -0
  14. package/build/client/_app/immutable/chunks/BDl2QuyN.js.gz +0 -0
  15. package/build/client/_app/immutable/chunks/{CqxCTxvJ.js → BI42X8h_.js} +1 -1
  16. package/build/client/_app/immutable/chunks/BI42X8h_.js.br +0 -0
  17. package/build/client/_app/immutable/chunks/BI42X8h_.js.gz +0 -0
  18. package/build/client/_app/immutable/chunks/BJTwdUYr.js +1 -0
  19. package/build/client/_app/immutable/chunks/BJTwdUYr.js.br +0 -0
  20. package/build/client/_app/immutable/chunks/BJTwdUYr.js.gz +0 -0
  21. package/build/client/_app/immutable/chunks/BLu86SbA.js +1 -0
  22. package/build/client/_app/immutable/chunks/BLu86SbA.js.br +0 -0
  23. package/build/client/_app/immutable/chunks/BLu86SbA.js.gz +0 -0
  24. package/build/client/_app/immutable/chunks/BNifnXkO.js +1 -0
  25. package/build/client/_app/immutable/chunks/BNifnXkO.js.br +2 -0
  26. package/build/client/_app/immutable/chunks/BNifnXkO.js.gz +0 -0
  27. package/build/client/_app/immutable/chunks/{BM0jKynj.js → BPNEONy5.js} +1 -1
  28. package/build/client/_app/immutable/chunks/BPNEONy5.js.br +0 -0
  29. package/build/client/_app/immutable/chunks/BPNEONy5.js.gz +0 -0
  30. package/build/client/_app/immutable/chunks/B_K7D6rX.js +2 -0
  31. package/build/client/_app/immutable/chunks/B_K7D6rX.js.br +0 -0
  32. package/build/client/_app/immutable/chunks/B_K7D6rX.js.gz +0 -0
  33. package/build/client/_app/immutable/chunks/Bj8jfBTi.js +1 -0
  34. package/build/client/_app/immutable/chunks/Bj8jfBTi.js.br +0 -0
  35. package/build/client/_app/immutable/chunks/Bj8jfBTi.js.gz +0 -0
  36. package/build/client/_app/immutable/chunks/BoPSJr4b.js +1 -0
  37. package/build/client/_app/immutable/chunks/BoPSJr4b.js.br +0 -0
  38. package/build/client/_app/immutable/chunks/BoPSJr4b.js.gz +0 -0
  39. package/build/client/_app/immutable/chunks/BsDZe4N6.js +1 -0
  40. package/build/client/_app/immutable/chunks/BsDZe4N6.js.br +0 -0
  41. package/build/client/_app/immutable/chunks/BsDZe4N6.js.gz +0 -0
  42. package/build/client/_app/immutable/chunks/{CXo2BVs0.js → Buo-57fF.js} +1 -1
  43. package/build/client/_app/immutable/chunks/Buo-57fF.js.br +0 -0
  44. package/build/client/_app/immutable/chunks/Buo-57fF.js.gz +0 -0
  45. package/build/client/_app/immutable/chunks/{C-whEtKd.js → C39ai1md.js} +1 -1
  46. package/build/client/_app/immutable/chunks/C39ai1md.js.br +0 -0
  47. package/build/client/_app/immutable/chunks/C39ai1md.js.gz +0 -0
  48. package/build/client/_app/immutable/chunks/C7he4XPB.js +1 -0
  49. package/build/client/_app/immutable/chunks/C7he4XPB.js.br +0 -0
  50. package/build/client/_app/immutable/chunks/C7he4XPB.js.gz +0 -0
  51. package/build/client/_app/immutable/chunks/{DYhTBLDc.js → CAjPRvNs.js} +2 -2
  52. package/build/client/_app/immutable/chunks/CAjPRvNs.js.br +0 -0
  53. package/build/client/_app/immutable/chunks/CAjPRvNs.js.gz +0 -0
  54. package/build/client/_app/immutable/chunks/CJYOmGlC.js +1 -0
  55. package/build/client/_app/immutable/chunks/CJYOmGlC.js.br +0 -0
  56. package/build/client/_app/immutable/chunks/CJYOmGlC.js.gz +0 -0
  57. package/build/client/_app/immutable/chunks/{CNLU4B3p.js → CMQRtcOV.js} +1 -1
  58. package/build/client/_app/immutable/chunks/CMQRtcOV.js.br +0 -0
  59. package/build/client/_app/immutable/chunks/CMQRtcOV.js.gz +0 -0
  60. package/build/client/_app/immutable/chunks/{HJHYLHDn.js → CPXdAKdv.js} +1 -1
  61. package/build/client/_app/immutable/chunks/CPXdAKdv.js.br +0 -0
  62. package/build/client/_app/immutable/chunks/CPXdAKdv.js.gz +0 -0
  63. package/build/client/_app/immutable/chunks/CVlsAO8k.js +11 -0
  64. package/build/client/_app/immutable/chunks/CVlsAO8k.js.br +0 -0
  65. package/build/client/_app/immutable/chunks/CVlsAO8k.js.gz +0 -0
  66. package/build/client/_app/immutable/chunks/{DNLcpWO_.js → CX0Ekq8t.js} +1 -1
  67. package/build/client/_app/immutable/chunks/CX0Ekq8t.js.br +0 -0
  68. package/build/client/_app/immutable/chunks/CX0Ekq8t.js.gz +0 -0
  69. package/build/client/_app/immutable/chunks/Ca197UDq.js +1 -0
  70. package/build/client/_app/immutable/chunks/Ca197UDq.js.br +0 -0
  71. package/build/client/_app/immutable/chunks/Ca197UDq.js.gz +0 -0
  72. package/build/client/_app/immutable/chunks/CaHl3dME.js +1 -0
  73. package/build/client/_app/immutable/chunks/CaHl3dME.js.br +0 -0
  74. package/build/client/_app/immutable/chunks/CaHl3dME.js.gz +0 -0
  75. package/build/client/_app/immutable/chunks/Cql_e8Of.js +1 -0
  76. package/build/client/_app/immutable/chunks/Cql_e8Of.js.br +2 -0
  77. package/build/client/_app/immutable/chunks/Cql_e8Of.js.gz +0 -0
  78. package/build/client/_app/immutable/chunks/{Dv8m1cTI.js → CxLomzuE.js} +1 -1
  79. package/build/client/_app/immutable/chunks/CxLomzuE.js.br +0 -0
  80. package/build/client/_app/immutable/chunks/{Dv8m1cTI.js.gz → CxLomzuE.js.gz} +0 -0
  81. package/build/client/_app/immutable/chunks/Cyai3SOj.js +1 -0
  82. package/build/client/_app/immutable/chunks/Cyai3SOj.js.br +0 -0
  83. package/build/client/_app/immutable/chunks/Cyai3SOj.js.gz +0 -0
  84. package/build/client/_app/immutable/chunks/{DTXUDKH5.js → CyvQWZoe.js} +4 -4
  85. package/build/client/_app/immutable/chunks/CyvQWZoe.js.br +0 -0
  86. package/build/client/_app/immutable/chunks/CyvQWZoe.js.gz +0 -0
  87. package/build/client/_app/immutable/chunks/CzXNWL4m.js +1 -0
  88. package/build/client/_app/immutable/chunks/CzXNWL4m.js.br +1 -0
  89. package/build/client/_app/immutable/chunks/CzXNWL4m.js.gz +0 -0
  90. package/build/client/_app/immutable/chunks/D-B5X35l.js +1 -0
  91. package/build/client/_app/immutable/chunks/D-B5X35l.js.br +0 -0
  92. package/build/client/_app/immutable/chunks/D-B5X35l.js.gz +0 -0
  93. package/build/client/_app/immutable/chunks/DJriQKD9.js +2 -0
  94. package/build/client/_app/immutable/chunks/DJriQKD9.js.br +0 -0
  95. package/build/client/_app/immutable/chunks/DJriQKD9.js.gz +0 -0
  96. package/build/client/_app/immutable/chunks/DXUXXCDA.js +1 -0
  97. package/build/client/_app/immutable/chunks/DXUXXCDA.js.br +0 -0
  98. package/build/client/_app/immutable/chunks/DXUXXCDA.js.gz +0 -0
  99. package/build/client/_app/immutable/chunks/{CZLNSMhF.js → Dslf9OwW.js} +10 -10
  100. package/build/client/_app/immutable/chunks/Dslf9OwW.js.br +0 -0
  101. package/build/client/_app/immutable/chunks/Dslf9OwW.js.gz +0 -0
  102. package/build/client/_app/immutable/chunks/QpaC6J6s.js +1 -0
  103. package/build/client/_app/immutable/chunks/QpaC6J6s.js.br +0 -0
  104. package/build/client/_app/immutable/chunks/QpaC6J6s.js.gz +0 -0
  105. package/build/client/_app/immutable/chunks/{HXxIsSOO.js → iAhzu4tv.js} +1 -1
  106. package/build/client/_app/immutable/chunks/iAhzu4tv.js.br +0 -0
  107. package/build/client/_app/immutable/chunks/iAhzu4tv.js.gz +0 -0
  108. package/build/client/_app/immutable/chunks/i_5fJYKS.js +3 -0
  109. package/build/client/_app/immutable/chunks/i_5fJYKS.js.br +0 -0
  110. package/build/client/_app/immutable/chunks/i_5fJYKS.js.gz +0 -0
  111. package/build/client/_app/immutable/chunks/{CF66fWJN.js → nYD9wsyN.js} +1 -1
  112. package/build/client/_app/immutable/chunks/nYD9wsyN.js.br +0 -0
  113. package/build/client/_app/immutable/chunks/nYD9wsyN.js.gz +0 -0
  114. package/build/client/_app/immutable/chunks/{BIpSIzUr.js → uLGP0py1.js} +1 -1
  115. package/build/client/_app/immutable/chunks/uLGP0py1.js.br +0 -0
  116. package/build/client/_app/immutable/chunks/uLGP0py1.js.gz +0 -0
  117. package/build/client/_app/immutable/entry/app.Cr7CMG-3.js +2 -0
  118. package/build/client/_app/immutable/entry/app.Cr7CMG-3.js.br +0 -0
  119. package/build/client/_app/immutable/entry/app.Cr7CMG-3.js.gz +0 -0
  120. package/build/client/_app/immutable/entry/start.CDNd0Bml.js +1 -0
  121. package/build/client/_app/immutable/entry/start.CDNd0Bml.js.br +2 -0
  122. package/build/client/_app/immutable/entry/start.CDNd0Bml.js.gz +0 -0
  123. package/build/client/_app/immutable/nodes/{0.BCmKHiYo.js → 0.AyqKXI2h.js} +2 -2
  124. package/build/client/_app/immutable/nodes/0.AyqKXI2h.js.br +0 -0
  125. package/build/client/_app/immutable/nodes/0.AyqKXI2h.js.gz +0 -0
  126. package/build/client/_app/immutable/nodes/1.BRmfm-nN.js +1 -0
  127. package/build/client/_app/immutable/nodes/1.BRmfm-nN.js.br +0 -0
  128. package/build/client/_app/immutable/nodes/1.BRmfm-nN.js.gz +0 -0
  129. package/build/client/_app/immutable/nodes/10.D03jjkr4.js +7 -0
  130. package/build/client/_app/immutable/nodes/10.D03jjkr4.js.br +0 -0
  131. package/build/client/_app/immutable/nodes/10.D03jjkr4.js.gz +0 -0
  132. package/build/client/_app/immutable/nodes/{15.Wmhj7gOy.js → 15.BFy4oAT6.js} +1 -1
  133. package/build/client/_app/immutable/nodes/15.BFy4oAT6.js.br +0 -0
  134. package/build/client/_app/immutable/nodes/15.BFy4oAT6.js.gz +0 -0
  135. package/build/client/_app/immutable/nodes/17.BOYU_WTT.js +3 -0
  136. package/build/client/_app/immutable/nodes/17.BOYU_WTT.js.br +0 -0
  137. package/build/client/_app/immutable/nodes/17.BOYU_WTT.js.gz +0 -0
  138. package/build/client/_app/immutable/nodes/18.Ddx80a4f.js +11 -0
  139. package/build/client/_app/immutable/nodes/18.Ddx80a4f.js.br +0 -0
  140. package/build/client/_app/immutable/nodes/18.Ddx80a4f.js.gz +0 -0
  141. package/build/client/_app/immutable/nodes/{2.BdQ4KFOK.js → 2.CZEboQjo.js} +20 -20
  142. package/build/client/_app/immutable/nodes/2.CZEboQjo.js.br +0 -0
  143. package/build/client/_app/immutable/nodes/2.CZEboQjo.js.gz +0 -0
  144. package/build/client/_app/immutable/nodes/3.dAV7FviN.js +1 -0
  145. package/build/client/_app/immutable/nodes/3.dAV7FviN.js.br +0 -0
  146. package/build/client/_app/immutable/nodes/3.dAV7FviN.js.gz +0 -0
  147. package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js +1 -0
  148. package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js.br +0 -0
  149. package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js.gz +0 -0
  150. package/build/client/_app/immutable/nodes/5.DPWuTlaI.js +1 -0
  151. package/build/client/_app/immutable/nodes/5.DPWuTlaI.js.br +0 -0
  152. package/build/client/_app/immutable/nodes/5.DPWuTlaI.js.gz +0 -0
  153. package/build/client/_app/immutable/nodes/6.DY-NLaRW.js +1 -0
  154. package/build/client/_app/immutable/nodes/6.DY-NLaRW.js.br +0 -0
  155. package/build/client/_app/immutable/nodes/6.DY-NLaRW.js.gz +0 -0
  156. package/build/client/_app/immutable/nodes/{8.DWNYX1kH.js → 8.Chuqhf8W.js} +1 -1
  157. package/build/client/_app/immutable/nodes/8.Chuqhf8W.js.br +0 -0
  158. package/build/client/_app/immutable/nodes/8.Chuqhf8W.js.gz +0 -0
  159. package/build/client/_app/immutable/nodes/9.BOEM8f6B.js +2 -0
  160. package/build/client/_app/immutable/nodes/9.BOEM8f6B.js.br +0 -0
  161. package/build/client/_app/immutable/nodes/9.BOEM8f6B.js.gz +0 -0
  162. package/build/client/_app/version.json +1 -1
  163. package/build/client/_app/version.json.br +0 -0
  164. package/build/client/_app/version.json.gz +0 -0
  165. package/build/server/chunks/0-TQkJBtAu.js +9 -0
  166. package/build/server/chunks/0-TQkJBtAu.js.map +1 -0
  167. package/build/server/chunks/1-CKxKlw1l.js +9 -0
  168. package/build/server/chunks/{1-C1mGPV-S.js.map → 1-CKxKlw1l.js.map} +1 -1
  169. package/build/server/chunks/10-Bly5Nk7M.js +9 -0
  170. package/build/server/chunks/10-Bly5Nk7M.js.map +1 -0
  171. package/build/server/chunks/{15-CZBxPMAc.js → 15-BMEAVslz.js} +2 -2
  172. package/build/server/chunks/{15-CZBxPMAc.js.map → 15-BMEAVslz.js.map} +1 -1
  173. package/build/server/chunks/17-C8CYQOYP.js +9 -0
  174. package/build/server/chunks/17-C8CYQOYP.js.map +1 -0
  175. package/build/server/chunks/18-CtXVGBbS.js +9 -0
  176. package/build/server/chunks/18-CtXVGBbS.js.map +1 -0
  177. package/build/server/chunks/2-DvD0dAh0.js +9 -0
  178. package/build/server/chunks/2-DvD0dAh0.js.map +1 -0
  179. package/build/server/chunks/3-DBzpRDK8.js +9 -0
  180. package/build/server/chunks/3-DBzpRDK8.js.map +1 -0
  181. package/build/server/chunks/4-BUNytyC2.js +9 -0
  182. package/build/server/chunks/4-BUNytyC2.js.map +1 -0
  183. package/build/server/chunks/5-DNo8_tFS.js +9 -0
  184. package/build/server/chunks/{5-DblV-OAd.js.map → 5-DNo8_tFS.js.map} +1 -1
  185. package/build/server/chunks/6-DUMdIfbf.js +9 -0
  186. package/build/server/chunks/6-DUMdIfbf.js.map +1 -0
  187. package/build/server/chunks/8-DxTJ555u.js +9 -0
  188. package/build/server/chunks/8-DxTJ555u.js.map +1 -0
  189. package/build/server/chunks/9-B4-5k50d.js +9 -0
  190. package/build/server/chunks/9-B4-5k50d.js.map +1 -0
  191. package/build/server/chunks/{AgentSidebar-XdGj1sqT.js → AgentSidebar-DvIlda-E.js} +5 -6
  192. package/build/server/chunks/AgentSidebar-DvIlda-E.js.map +1 -0
  193. package/build/server/chunks/{Button-Dsy3QY7B.js → Button-BLigKkGG.js} +2 -2
  194. package/build/server/chunks/Button-BLigKkGG.js.map +1 -0
  195. package/build/server/chunks/{ChatStepTrace-1q74tVYO.js → ChatStepTrace-CqqIjRLU.js} +47 -11
  196. package/build/server/chunks/ChatStepTrace-CqqIjRLU.js.map +1 -0
  197. package/build/server/chunks/{Input-CHFlfduw.js → Input-OdQKNdS6.js} +1 -2
  198. package/build/server/chunks/Input-OdQKNdS6.js.map +1 -0
  199. package/build/server/chunks/MockGenerator-GEpXTgX0.js +117 -0
  200. package/build/server/chunks/MockGenerator-GEpXTgX0.js.map +1 -0
  201. package/build/server/chunks/Modal-DtVsRmmO.js.map +1 -1
  202. package/build/server/chunks/{_layout.svelte-DJ4kDgDL.js → _layout.svelte-B-CkD4a0.js} +14 -8
  203. package/build/server/chunks/_layout.svelte-B-CkD4a0.js.map +1 -0
  204. package/build/server/chunks/{_layout.svelte-B2lwUacy.js → _layout.svelte-BBFCF-xw.js} +8 -4
  205. package/build/server/chunks/_layout.svelte-BBFCF-xw.js.map +1 -0
  206. package/build/server/chunks/{_layout.svelte-C7JgNCIu.js → _layout.svelte-cPhmW5bZ.js} +3 -1
  207. package/build/server/chunks/_layout.svelte-cPhmW5bZ.js.map +1 -0
  208. package/build/server/chunks/{_page.svelte-FufhMKh4.js → _page.svelte-0J2xaeyC.js} +71 -16
  209. package/build/server/chunks/_page.svelte-0J2xaeyC.js.map +1 -0
  210. package/build/server/chunks/{_page.svelte-BpRksNEG.js → _page.svelte-BgoX8Y6N.js} +2 -2
  211. package/build/server/chunks/{_page.svelte-BpRksNEG.js.map → _page.svelte-BgoX8Y6N.js.map} +1 -1
  212. package/build/server/chunks/{_page.svelte-GaxktsuT.js → _page.svelte-C_S_qUgZ.js} +480 -72
  213. package/build/server/chunks/_page.svelte-C_S_qUgZ.js.map +1 -0
  214. package/build/server/chunks/{_page.svelte-DDpBtd3X.js → _page.svelte-CqGOvPRO.js} +3 -4
  215. package/build/server/chunks/_page.svelte-CqGOvPRO.js.map +1 -0
  216. package/build/server/chunks/{_page.svelte-CA3-vgl2.js → _page.svelte-Cxh9_vUl.js} +151 -100
  217. package/build/server/chunks/_page.svelte-Cxh9_vUl.js.map +1 -0
  218. package/build/server/chunks/_page.svelte-zOI_s_3J.js +2151 -0
  219. package/build/server/chunks/_page.svelte-zOI_s_3J.js.map +1 -0
  220. package/build/server/chunks/agents-context-rV2narXG.js.map +1 -1
  221. package/build/server/chunks/conversation-view-memory-BZguFlTt.js +20 -0
  222. package/build/server/chunks/conversation-view-memory-BZguFlTt.js.map +1 -0
  223. package/build/server/chunks/edit-workspace-context-B1nYQ5hD.js.map +1 -1
  224. package/build/server/chunks/index-CSpGrMM2.js.map +1 -1
  225. package/build/server/chunks/{client-DzvEkq51.js → source-selection-CcN1cgXc.js} +122 -117
  226. package/build/server/chunks/source-selection-CcN1cgXc.js.map +1 -0
  227. package/build/server/index.js +1 -1
  228. package/build/server/index.js.map +1 -1
  229. package/build/server/manifest.js +14 -14
  230. package/build/server/manifest.js.map +1 -1
  231. package/package.json +4 -4
  232. package/src/backend/lib/get-agent-factories.spec.ts +59 -0
  233. package/src/backend/lib/get-agent-factories.ts +31 -1
  234. package/src/backend/lib/voice-agent-lifecycle.test.ts +31 -0
  235. package/src/backend/lib/voice-agent-lifecycle.ts +16 -0
  236. package/src/backend/roleplay/agents.ts +189 -69
  237. package/src/backend/roleplay/message-dispatcher.ts +0 -7
  238. package/src/backend/roleplay/router.ts +14 -17
  239. package/src/backend/roleplay/trace-serialization.spec.ts +35 -0
  240. package/src/backend/roleplay/trace-serialization.ts +57 -0
  241. package/src/backend/roleplay/variants.ts +33 -49
  242. package/src/backend/tests/academy.ts +202 -100
  243. package/src/backend/tests/analyze.ts +0 -3
  244. package/src/backend/tests/data.spec.ts +139 -0
  245. package/src/backend/tests/data.ts +452 -68
  246. package/src/backend/tests/emulate-agents.ts +60 -44
  247. package/src/backend/tests/openai-prompt-cache.ts +68 -0
  248. package/src/backend/tests/promptfoo-runtime.spec.ts +41 -0
  249. package/src/backend/tests/promptfoo-runtime.ts +27 -0
  250. package/src/backend/tests/report-files.spec.ts +42 -0
  251. package/src/backend/tests/report-files.ts +101 -0
  252. package/src/backend/tests/router.ts +28 -2
  253. package/src/backend/tests/suite.ts +13 -4
  254. package/build/client/_app/immutable/assets/0.B4t3dozz.css +0 -1
  255. package/build/client/_app/immutable/assets/0.B4t3dozz.css.br +0 -0
  256. package/build/client/_app/immutable/assets/0.B4t3dozz.css.gz +0 -0
  257. package/build/client/_app/immutable/assets/10.BX46PfEJ.css.br +0 -0
  258. package/build/client/_app/immutable/assets/10.BX46PfEJ.css.gz +0 -0
  259. package/build/client/_app/immutable/chunks/7E8m6dwk.js +0 -1
  260. package/build/client/_app/immutable/chunks/7E8m6dwk.js.br +0 -0
  261. package/build/client/_app/immutable/chunks/7E8m6dwk.js.gz +0 -0
  262. package/build/client/_app/immutable/chunks/7qRqthsa.js +0 -1
  263. package/build/client/_app/immutable/chunks/7qRqthsa.js.br +0 -2
  264. package/build/client/_app/immutable/chunks/7qRqthsa.js.gz +0 -0
  265. package/build/client/_app/immutable/chunks/Ajb1KYIn.js +0 -1
  266. package/build/client/_app/immutable/chunks/Ajb1KYIn.js.br +0 -0
  267. package/build/client/_app/immutable/chunks/Ajb1KYIn.js.gz +0 -0
  268. package/build/client/_app/immutable/chunks/B0iEglSe.js +0 -1
  269. package/build/client/_app/immutable/chunks/B0iEglSe.js.br +0 -0
  270. package/build/client/_app/immutable/chunks/B0iEglSe.js.gz +0 -0
  271. package/build/client/_app/immutable/chunks/BIpSIzUr.js.br +0 -0
  272. package/build/client/_app/immutable/chunks/BIpSIzUr.js.gz +0 -0
  273. package/build/client/_app/immutable/chunks/BM0jKynj.js.br +0 -0
  274. package/build/client/_app/immutable/chunks/BM0jKynj.js.gz +0 -0
  275. package/build/client/_app/immutable/chunks/BTvePR6r.js +0 -1
  276. package/build/client/_app/immutable/chunks/BTvePR6r.js.br +0 -2
  277. package/build/client/_app/immutable/chunks/BTvePR6r.js.gz +0 -0
  278. package/build/client/_app/immutable/chunks/Bcu62lFq.js +0 -1
  279. package/build/client/_app/immutable/chunks/Bcu62lFq.js.br +0 -0
  280. package/build/client/_app/immutable/chunks/Bcu62lFq.js.gz +0 -0
  281. package/build/client/_app/immutable/chunks/BwtfALfL.js +0 -11
  282. package/build/client/_app/immutable/chunks/BwtfALfL.js.br +0 -0
  283. package/build/client/_app/immutable/chunks/BwtfALfL.js.gz +0 -0
  284. package/build/client/_app/immutable/chunks/BxsDwlgp.js +0 -4
  285. package/build/client/_app/immutable/chunks/BxsDwlgp.js.br +0 -0
  286. package/build/client/_app/immutable/chunks/BxsDwlgp.js.gz +0 -0
  287. package/build/client/_app/immutable/chunks/C-whEtKd.js.br +0 -2
  288. package/build/client/_app/immutable/chunks/C-whEtKd.js.gz +0 -0
  289. package/build/client/_app/immutable/chunks/C1WqE8CP.js +0 -1
  290. package/build/client/_app/immutable/chunks/C1WqE8CP.js.br +0 -2
  291. package/build/client/_app/immutable/chunks/C1WqE8CP.js.gz +0 -0
  292. package/build/client/_app/immutable/chunks/C4D-JXnS.js +0 -1
  293. package/build/client/_app/immutable/chunks/C4D-JXnS.js.br +0 -0
  294. package/build/client/_app/immutable/chunks/C4D-JXnS.js.gz +0 -0
  295. package/build/client/_app/immutable/chunks/C4SnSfql.js +0 -1
  296. package/build/client/_app/immutable/chunks/C4SnSfql.js.br +0 -0
  297. package/build/client/_app/immutable/chunks/C4SnSfql.js.gz +0 -0
  298. package/build/client/_app/immutable/chunks/CF66fWJN.js.br +0 -0
  299. package/build/client/_app/immutable/chunks/CF66fWJN.js.gz +0 -0
  300. package/build/client/_app/immutable/chunks/CNLU4B3p.js.br +0 -0
  301. package/build/client/_app/immutable/chunks/CNLU4B3p.js.gz +0 -0
  302. package/build/client/_app/immutable/chunks/CXo2BVs0.js.br +0 -0
  303. package/build/client/_app/immutable/chunks/CXo2BVs0.js.gz +0 -0
  304. package/build/client/_app/immutable/chunks/CZLNSMhF.js.br +0 -0
  305. package/build/client/_app/immutable/chunks/CZLNSMhF.js.gz +0 -0
  306. package/build/client/_app/immutable/chunks/CqO5tR2_.js +0 -1
  307. package/build/client/_app/immutable/chunks/CqO5tR2_.js.br +0 -0
  308. package/build/client/_app/immutable/chunks/CqO5tR2_.js.gz +0 -0
  309. package/build/client/_app/immutable/chunks/CqxCTxvJ.js.br +0 -0
  310. package/build/client/_app/immutable/chunks/CqxCTxvJ.js.gz +0 -0
  311. package/build/client/_app/immutable/chunks/D-wf2QNc.js +0 -1
  312. package/build/client/_app/immutable/chunks/D-wf2QNc.js.br +0 -0
  313. package/build/client/_app/immutable/chunks/D-wf2QNc.js.gz +0 -0
  314. package/build/client/_app/immutable/chunks/D4obUD_s.js +0 -1
  315. package/build/client/_app/immutable/chunks/D4obUD_s.js.br +0 -0
  316. package/build/client/_app/immutable/chunks/D4obUD_s.js.gz +0 -0
  317. package/build/client/_app/immutable/chunks/D8e7SQh9.js +0 -1
  318. package/build/client/_app/immutable/chunks/D8e7SQh9.js.br +0 -0
  319. package/build/client/_app/immutable/chunks/D8e7SQh9.js.gz +0 -0
  320. package/build/client/_app/immutable/chunks/DNLcpWO_.js.br +0 -0
  321. package/build/client/_app/immutable/chunks/DNLcpWO_.js.gz +0 -0
  322. package/build/client/_app/immutable/chunks/DTXUDKH5.js.br +0 -0
  323. package/build/client/_app/immutable/chunks/DTXUDKH5.js.gz +0 -0
  324. package/build/client/_app/immutable/chunks/DYhTBLDc.js.br +0 -0
  325. package/build/client/_app/immutable/chunks/DYhTBLDc.js.gz +0 -0
  326. package/build/client/_app/immutable/chunks/DhBm2Vx9.js +0 -1
  327. package/build/client/_app/immutable/chunks/DhBm2Vx9.js.br +0 -0
  328. package/build/client/_app/immutable/chunks/DhBm2Vx9.js.gz +0 -0
  329. package/build/client/_app/immutable/chunks/Dp760K13.js.br +0 -0
  330. package/build/client/_app/immutable/chunks/Dp760K13.js.gz +0 -0
  331. package/build/client/_app/immutable/chunks/Dv0gjm25.js +0 -1
  332. package/build/client/_app/immutable/chunks/Dv0gjm25.js.br +0 -0
  333. package/build/client/_app/immutable/chunks/Dv0gjm25.js.gz +0 -0
  334. package/build/client/_app/immutable/chunks/Dv8m1cTI.js.br +0 -0
  335. package/build/client/_app/immutable/chunks/HJHYLHDn.js.br +0 -0
  336. package/build/client/_app/immutable/chunks/HJHYLHDn.js.gz +0 -0
  337. package/build/client/_app/immutable/chunks/HXxIsSOO.js.br +0 -0
  338. package/build/client/_app/immutable/chunks/HXxIsSOO.js.gz +0 -0
  339. package/build/client/_app/immutable/chunks/_yZg6Sf-.js +0 -2
  340. package/build/client/_app/immutable/chunks/_yZg6Sf-.js.br +0 -0
  341. package/build/client/_app/immutable/chunks/_yZg6Sf-.js.gz +0 -0
  342. package/build/client/_app/immutable/chunks/xebNt8-Z.js +0 -1
  343. package/build/client/_app/immutable/chunks/xebNt8-Z.js.br +0 -0
  344. package/build/client/_app/immutable/chunks/xebNt8-Z.js.gz +0 -0
  345. package/build/client/_app/immutable/entry/app.C4wJtvtH.js +0 -2
  346. package/build/client/_app/immutable/entry/app.C4wJtvtH.js.br +0 -0
  347. package/build/client/_app/immutable/entry/app.C4wJtvtH.js.gz +0 -0
  348. package/build/client/_app/immutable/entry/start.BId2vqhS.js +0 -1
  349. package/build/client/_app/immutable/entry/start.BId2vqhS.js.br +0 -2
  350. package/build/client/_app/immutable/entry/start.BId2vqhS.js.gz +0 -0
  351. package/build/client/_app/immutable/nodes/0.BCmKHiYo.js.br +0 -0
  352. package/build/client/_app/immutable/nodes/0.BCmKHiYo.js.gz +0 -0
  353. package/build/client/_app/immutable/nodes/1.8skEZBEw.js +0 -1
  354. package/build/client/_app/immutable/nodes/1.8skEZBEw.js.br +0 -0
  355. package/build/client/_app/immutable/nodes/1.8skEZBEw.js.gz +0 -0
  356. package/build/client/_app/immutable/nodes/10.YgmBfSWN.js +0 -5
  357. package/build/client/_app/immutable/nodes/10.YgmBfSWN.js.br +0 -0
  358. package/build/client/_app/immutable/nodes/10.YgmBfSWN.js.gz +0 -0
  359. package/build/client/_app/immutable/nodes/15.Wmhj7gOy.js.br +0 -0
  360. package/build/client/_app/immutable/nodes/15.Wmhj7gOy.js.gz +0 -0
  361. package/build/client/_app/immutable/nodes/17.D8ydco6g.js +0 -3
  362. package/build/client/_app/immutable/nodes/17.D8ydco6g.js.br +0 -0
  363. package/build/client/_app/immutable/nodes/17.D8ydco6g.js.gz +0 -0
  364. package/build/client/_app/immutable/nodes/18.C67CqGm5.js +0 -11
  365. package/build/client/_app/immutable/nodes/18.C67CqGm5.js.br +0 -0
  366. package/build/client/_app/immutable/nodes/18.C67CqGm5.js.gz +0 -0
  367. package/build/client/_app/immutable/nodes/2.BdQ4KFOK.js.br +0 -0
  368. package/build/client/_app/immutable/nodes/2.BdQ4KFOK.js.gz +0 -0
  369. package/build/client/_app/immutable/nodes/3.nCmkz6kS.js +0 -1
  370. package/build/client/_app/immutable/nodes/3.nCmkz6kS.js.br +0 -0
  371. package/build/client/_app/immutable/nodes/3.nCmkz6kS.js.gz +0 -0
  372. package/build/client/_app/immutable/nodes/4.DAwO0_Am.js +0 -1
  373. package/build/client/_app/immutable/nodes/4.DAwO0_Am.js.br +0 -0
  374. package/build/client/_app/immutable/nodes/4.DAwO0_Am.js.gz +0 -0
  375. package/build/client/_app/immutable/nodes/5.C_SvvZjG.js +0 -1
  376. package/build/client/_app/immutable/nodes/5.C_SvvZjG.js.br +0 -0
  377. package/build/client/_app/immutable/nodes/5.C_SvvZjG.js.gz +0 -0
  378. package/build/client/_app/immutable/nodes/6.DfOe9lph.js +0 -1
  379. package/build/client/_app/immutable/nodes/6.DfOe9lph.js.br +0 -0
  380. package/build/client/_app/immutable/nodes/6.DfOe9lph.js.gz +0 -0
  381. package/build/client/_app/immutable/nodes/8.DWNYX1kH.js.br +0 -0
  382. package/build/client/_app/immutable/nodes/8.DWNYX1kH.js.gz +0 -0
  383. package/build/client/_app/immutable/nodes/9.D7SG4T7Z.js +0 -2
  384. package/build/client/_app/immutable/nodes/9.D7SG4T7Z.js.br +0 -0
  385. package/build/client/_app/immutable/nodes/9.D7SG4T7Z.js.gz +0 -0
  386. package/build/server/chunks/0-D5BNSI7t.js +0 -9
  387. package/build/server/chunks/0-D5BNSI7t.js.map +0 -1
  388. package/build/server/chunks/1-C1mGPV-S.js +0 -9
  389. package/build/server/chunks/10-BUo1e_HG.js +0 -9
  390. package/build/server/chunks/10-BUo1e_HG.js.map +0 -1
  391. package/build/server/chunks/17-iNFqG1My.js +0 -9
  392. package/build/server/chunks/17-iNFqG1My.js.map +0 -1
  393. package/build/server/chunks/18-u8T5X6fM.js +0 -9
  394. package/build/server/chunks/18-u8T5X6fM.js.map +0 -1
  395. package/build/server/chunks/2-tLlSByHA.js +0 -9
  396. package/build/server/chunks/2-tLlSByHA.js.map +0 -1
  397. package/build/server/chunks/3-D1KY9yVZ.js +0 -9
  398. package/build/server/chunks/3-D1KY9yVZ.js.map +0 -1
  399. package/build/server/chunks/4-7zu0cJha.js +0 -9
  400. package/build/server/chunks/4-7zu0cJha.js.map +0 -1
  401. package/build/server/chunks/5-DblV-OAd.js +0 -9
  402. package/build/server/chunks/6-BDC5budM.js +0 -9
  403. package/build/server/chunks/6-BDC5budM.js.map +0 -1
  404. package/build/server/chunks/8-DhmD-opq.js +0 -9
  405. package/build/server/chunks/8-DhmD-opq.js.map +0 -1
  406. package/build/server/chunks/9-BGj28GI6.js +0 -9
  407. package/build/server/chunks/9-BGj28GI6.js.map +0 -1
  408. package/build/server/chunks/AgentSidebar-XdGj1sqT.js.map +0 -1
  409. package/build/server/chunks/Button-Dsy3QY7B.js.map +0 -1
  410. package/build/server/chunks/ChatStepTrace-1q74tVYO.js.map +0 -1
  411. package/build/server/chunks/Input-CHFlfduw.js.map +0 -1
  412. package/build/server/chunks/_layout.svelte-B2lwUacy.js.map +0 -1
  413. package/build/server/chunks/_layout.svelte-C7JgNCIu.js.map +0 -1
  414. package/build/server/chunks/_layout.svelte-DJ4kDgDL.js.map +0 -1
  415. package/build/server/chunks/_page.svelte-Bl1WcAXy.js +0 -671
  416. package/build/server/chunks/_page.svelte-Bl1WcAXy.js.map +0 -1
  417. package/build/server/chunks/_page.svelte-CA3-vgl2.js.map +0 -1
  418. package/build/server/chunks/_page.svelte-DDpBtd3X.js.map +0 -1
  419. package/build/server/chunks/_page.svelte-FufhMKh4.js.map +0 -1
  420. package/build/server/chunks/_page.svelte-GaxktsuT.js.map +0 -1
  421. package/build/server/chunks/client-DzvEkq51.js.map +0 -1
  422. package/build/server/chunks/source-selection-eA--VEKo.js +0 -124
  423. package/build/server/chunks/source-selection-eA--VEKo.js.map +0 -1
@@ -28,28 +28,30 @@ export async function generateReplyVariants(props: {
28
28
  agentId: string
29
29
  region?: string | null
30
30
  states: AgentState[]
31
- prevStateId: string | null
32
- rawUserMessage: string
33
31
  count: number
34
32
  context: Record<string, any>
35
33
  }): Promise<GeneratedVariant[]> {
36
- const { agentId, region, states, prevStateId, rawUserMessage, count, context } = props
34
+ const { agentId, region, states, count, context } = props
37
35
  if (!Number.isFinite(count) || count <= 0) return []
36
+ const targetState = states.at(-1)
37
+ if (!targetState) throw new Error('No saved roleplay state found for variants')
38
38
 
39
39
  const rootPath = await getRootPath()
40
40
  const { factories } = await getAgentFactories(rootPath, getImportNonce())
41
41
  const factory = factories[agentId]
42
42
  if (!factory) throw new Error(`Cannot find agent ${agentId}`)
43
43
 
44
- const statesById = new Map<string, AgentState>()
45
- for (const state of states) statesById.set(state.id, clone(state))
44
+ const statesById = new Map(states.map((state) => [state.id, clone(state)]))
46
45
 
46
+ const prevStateId = targetState.prev
47
47
  const prevState = prevStateId ? (statesById.get(prevStateId) ?? null) : null
48
48
  if (prevStateId && !prevState) {
49
49
  throw new Error('Could not find the previous roleplay state for this reply')
50
50
  }
51
51
 
52
- const message = parseRawUserMessage(rawUserMessage)
52
+ const message = latestInputMessage(targetState)
53
+ if (!message) throw new Error('Could not reconstruct the input message for variants')
54
+
53
55
  return Promise.all(
54
56
  Array.from({ length: count }, (_, index) =>
55
57
  generateSingleReplyVariant({
@@ -59,7 +61,6 @@ export async function generateReplyVariants(props: {
59
61
  context,
60
62
  prevState,
61
63
  statesById,
62
- rawUserMessage,
63
64
  message
64
65
  })
65
66
  )
@@ -84,20 +85,6 @@ export function replaceSavedState(
84
85
  return next
85
86
  }
86
87
 
87
- function parseRawUserMessage(raw: string): Message {
88
- const trimmed = String(raw ?? '').trim()
89
- if (!trimmed) throw new Error('Could not reconstruct the user message for variants')
90
-
91
- const imageMatch = /^\[image\]\s+(.+)$/u.exec(trimmed)
92
- if (imageMatch) {
93
- const imageUrl = (imageMatch[1] ?? '').trim()
94
- if (!imageUrl) throw new Error('Image variant message is missing its URL')
95
- return { sender: 'user', text: '', imageUrl }
96
- }
97
-
98
- return { sender: 'user', text: trimmed }
99
- }
100
-
101
88
  function collectMessagesUpToState(
102
89
  statesById: Map<string, AgentState>,
103
90
  stateId: string | null
@@ -124,30 +111,32 @@ function collectMessagesUpToState(
124
111
  }
125
112
 
126
113
  function collectMessagesFromHistory(history: AgentState['history']): Message[] {
127
- const messages: Message[] = []
128
- for (const entry of history) {
129
- if (entry.type !== 'message') continue
130
- messages.push(entry.payload as Message)
131
- }
132
- return messages
114
+ return history.filter((entry) => entry.type === 'message').map((entry) => entry.payload)
115
+ }
116
+
117
+ function latestInputMessage(state: AgentState): Message | null {
118
+ const entry = [...state.history]
119
+ .reverse()
120
+ .find(
121
+ (entry) =>
122
+ entry.type === 'message' &&
123
+ (entry.payload.sender === 'user' || entry.payload.sender === 'system')
124
+ )
125
+ return entry ? clone(entry.payload as Message) : null
133
126
  }
134
127
 
135
128
  function extractVariantText(state: AgentState): string {
136
- for (let index = state.history.length - 1; index >= 0; index--) {
137
- const entry = state.history[index]
138
- if (entry.type !== 'conversational-trace') continue
139
- const trace = entry.payload as ConversationalTrace
140
- if (trace.eventName === 'finished-main-streaming-step' || trace.eventName === 'sent-message') {
141
- return String(trace.content ?? '')
142
- }
143
- }
129
+ const traceEntry = [...state.history].reverse().find((entry) => {
130
+ if (entry.type !== 'conversational-trace') return false
131
+ const trace = entry.payload
132
+ return trace.eventName === 'finished-main-streaming-step' || trace.eventName === 'sent-message'
133
+ })
134
+ if (traceEntry) return String((traceEntry.payload as ConversationalTrace).content ?? '')
144
135
 
145
- for (let index = state.history.length - 1; index >= 0; index--) {
146
- const entry = state.history[index]
147
- if (entry.type !== 'message') continue
148
- const payload = entry.payload as Message
149
- if (payload.sender === 'agent') return String(payload.text ?? '')
150
- }
136
+ const messageEntry = [...state.history]
137
+ .reverse()
138
+ .find((entry) => entry.type === 'message' && entry.payload.sender === 'agent')
139
+ if (messageEntry) return String((messageEntry.payload as Message).text ?? '')
151
140
 
152
141
  return ''
153
142
  }
@@ -171,10 +160,9 @@ async function generateSingleReplyVariant(props: {
171
160
  context: Record<string, any>
172
161
  prevState: AgentState | null
173
162
  statesById: Map<string, AgentState>
174
- rawUserMessage: string
175
163
  message: Message
176
164
  }): Promise<GeneratedVariant> {
177
- const { index, factory, region, context, prevState, statesById, rawUserMessage, message } = props
165
+ const { index, factory, region, context, prevState, statesById, message } = props
178
166
 
179
167
  const state = createVariantState(prevState?.id ?? null, index)
180
168
  const scheduleSeed = clone(prevState?.schedule ?? {})
@@ -212,9 +200,6 @@ async function generateSingleReplyVariant(props: {
212
200
  }
213
201
  const conversationalTracer: ConversationalTracer = {
214
202
  addConversationalTrace: (trace: ConversationalTrace) => {
215
- ;(trace as any).bosunStateId = state.id
216
- ;(trace as any).bosunPrevStateId = state.prev
217
- ;(trace as any).bosunUserMessage = rawUserMessage
218
203
  state.history.push({
219
204
  type: 'conversational-trace',
220
205
  payload: trace
@@ -228,9 +213,6 @@ async function generateSingleReplyVariant(props: {
228
213
  const current = (ctx.get() as Record<string, unknown> | null | undefined) || {}
229
214
  ctx.set({ ...current, recombineRegion: region })
230
215
  }
231
- if (context && typeof context === 'object') {
232
- ctx.swap(region ? { ...context, recombineRegion: region } : context)
233
- }
234
216
 
235
217
  const getMessages = () => [
236
218
  ...collectMessagesUpToState(statesById, state.prev),
@@ -263,6 +245,8 @@ async function generateSingleReplyVariant(props: {
263
245
  }
264
246
  })
265
247
 
248
+ ctx.swap(region ? { ...context, recombineRegion: region } : context)
249
+
266
250
  scheduler.setState(scheduleSeed)
267
251
 
268
252
  if (!state.prev && hasStart(agent)) {
@@ -1,21 +1,27 @@
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'
10
9
  import path from 'node:path'
11
- import { readSuite } from './suite'
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>
@@ -30,9 +36,13 @@ interface CsvRow {
30
36
  expected_output: string
31
37
  similarity_threshold: number | string
32
38
  pinned: 'TRUE' | 'FALSE'
33
- context: string
39
+ context: string | Record<string, unknown>
40
+ mocks?: string | Record<string, unknown>
34
41
  timeout: string
42
+ capture_mode?: string
35
43
  scenario?: string
44
+ step?: string
45
+ author?: string
36
46
  }
37
47
 
38
48
  export interface ResultEntry {
@@ -65,6 +75,7 @@ export interface WrittenRunReportConfig {
65
75
  }
66
76
 
67
77
  export interface WrittenRunReportRow extends TableRow {
78
+ test: SuiteTestRow
68
79
  agent: string
69
80
  test_id: string
70
81
  run_timestamp: string
@@ -84,9 +95,34 @@ export interface WrittenRunReport {
84
95
  type PromptTestsScope = 'all' | 'pinned'
85
96
 
86
97
  type PromptTestsSelection = { scope: PromptTestsScope } | { testId: string }
98
+ type CaptureMode = 'immediate' | 'final'
87
99
 
88
100
  const activeRunsBySuiteKey = new Map<string, AbortController>()
89
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
+
90
126
  export type SuiteSelection = {
91
127
  // `suiteKey` is the filesystem/report key (e.g. `voiceSupport.en-gb`).
92
128
  // `agentId` is the canonical factory key (workflow export key, e.g. `voiceSupport`).
@@ -116,6 +152,7 @@ export async function runPromptTestsManaged(
116
152
  return await runPromptTests(suite, selection, { signal: controller.signal })
117
153
  } finally {
118
154
  activeRunsBySuiteKey.delete(suite.suiteKey)
155
+ activeProgressBySuiteKey.delete(suite.suiteKey)
119
156
  }
120
157
  }
121
158
 
@@ -209,7 +246,7 @@ function toTestInputs(
209
246
  value: row.assertion_value
210
247
  },
211
248
  {
212
- type: 'similar' as 'similar',
249
+ type: 'similar' as const,
213
250
  value: String(row.expected_output),
214
251
  threshold: Number(row.similarity_threshold) / 100
215
252
  }
@@ -217,7 +254,9 @@ function toTestInputs(
217
254
  context: (() => {
218
255
  const testCtx = makeContext(row.context)
219
256
  return defaultContext ? mergeContexts(defaultContext, testCtx) : testCtx
220
- })()
257
+ })(),
258
+ captureMode: row.capture_mode === 'final' ? 'final' : 'immediate',
259
+ mocks: typeof row.mocks === 'undefined' ? undefined : makeContext(row.mocks)
221
260
  }
222
261
  })
223
262
  }
@@ -226,6 +265,7 @@ export class RecombineAgentProvider implements ApiProvider {
226
265
  protected providerId: string
227
266
  public config: Record<string, unknown>
228
267
  private abortSignal?: AbortSignal
268
+ private onTestStarted?: (testId: string) => void
229
269
 
230
270
  constructor(options: ProviderOptions) {
231
271
  this.providerId = options.id || 'recombine-agent'
@@ -236,6 +276,10 @@ export class RecombineAgentProvider implements ApiProvider {
236
276
  this.abortSignal = signal
237
277
  }
238
278
 
279
+ setOnTestStarted(callback: (testId: string) => void) {
280
+ this.onTestStarted = callback
281
+ }
282
+
239
283
  id() {
240
284
  return this.providerId
241
285
  }
@@ -244,17 +288,25 @@ export class RecombineAgentProvider implements ApiProvider {
244
288
  const projectRoot = path.dirname(await getRootPath())
245
289
  const vars = (context?.vars || {}) as unknown as {
246
290
  agentId: string
291
+ testId: string
247
292
  region?: string
248
293
  conversation: Conversation
249
294
  conversationParseError?: string
250
295
  context: Context
296
+ mocks?: Context
297
+ captureMode?: CaptureMode | string
251
298
  }
299
+ const testId = String(vars.testId ?? '')
300
+ if (testId) this.onTestStarted?.(testId)
252
301
 
253
302
  if (typeof vars.conversationParseError === 'string' && vars.conversationParseError.trim()) {
254
303
  return { output: '', error: vars.conversationParseError }
255
304
  }
256
305
 
306
+ const captureMode: CaptureMode = vars.captureMode === 'final' ? 'final' : 'immediate'
257
307
  let captured: string = ''
308
+ let captureEnabled = false
309
+ const finalCaptureErrors: string[] = []
258
310
  const capturedLogs: string[] = []
259
311
  const capturedStepTraces: StepTrace[] = []
260
312
 
@@ -265,7 +317,7 @@ export class RecombineAgentProvider implements ApiProvider {
265
317
  // Clone the conversation so parallel runs don't share the same array reference.
266
318
  // Shallow-clone each message; messages are simple { sender, text } objects in this flow.
267
319
  const conv: Conversation = Array.isArray(vars.conversation)
268
- ? (vars.conversation as Conversation).map((m) => ({ ...m }))
320
+ ? vars.conversation.map((m) => ({ ...m }))
269
321
  : []
270
322
 
271
323
  // Prepare a streaming log writer so logs are written as they occur
@@ -280,7 +332,6 @@ export class RecombineAgentProvider implements ApiProvider {
280
332
  const stepTracer: StepTracer = {
281
333
  addStepTrace: (stepTrace: StepTrace) => {
282
334
  try {
283
- console.log(`trace: ${stepTrace.name}`)
284
335
  capturedStepTraces.push(stepTrace)
285
336
  } catch {
286
337
  // best-effort capture; ignore serialization issues
@@ -307,6 +358,9 @@ export class RecombineAgentProvider implements ApiProvider {
307
358
  line = line.slice(0, 5000) + '... [TRUNCATED]'
308
359
  }
309
360
  capturedLogs.push(line)
361
+ if (captureMode === 'final' && captureEnabled && level === 'error') {
362
+ finalCaptureErrors.push(line)
363
+ }
310
364
  try {
311
365
  logStream.write(line + '\n')
312
366
  } catch {
@@ -331,14 +385,24 @@ export class RecombineAgentProvider implements ApiProvider {
331
385
  getMessages: () => conv,
332
386
  sendMessage: async (message: Message | string) => {
333
387
  const m = typeof message === 'string' ? { sender: 'agent', text: message } : message
334
- if (m.sender === 'agent') {
335
- captured = typeof m.text === 'string' ? m.text : JSON.stringify(m)
336
- logger.log('Agent message captured:', captured)
388
+ if (captureEnabled && m.sender === 'agent') {
389
+ const text = typeof m.text === 'string' ? m.text : JSON.stringify(m)
390
+ if (captureMode === 'final' || !captured) {
391
+ captured = text
392
+ logger.log('Agent message captured:', captured)
393
+ } else {
394
+ logger.log('Agent message ignored after immediate capture:', text)
395
+ }
337
396
  }
338
397
  },
398
+ onBeforeTestResponse: () => {
399
+ captured = ''
400
+ captureEnabled = true
401
+ },
339
402
  stepTracer,
340
403
  logger,
341
404
  context: vars.context || {},
405
+ mocks: vars.mocks,
342
406
  abortSignal: this.abortSignal
343
407
  })
344
408
 
@@ -378,6 +442,19 @@ export class RecombineAgentProvider implements ApiProvider {
378
442
  ? capturedStepTraces.map(sanitizeTrace)
379
443
  : []
380
444
 
445
+ if (captureMode === 'final' && finalCaptureErrors.length > 0) {
446
+ const detail = finalCaptureErrors.at(-1) ?? 'Unknown final capture error'
447
+ return {
448
+ output: captured,
449
+ error: `Final capture failed because a subsequent step emitted an error: ${detail}`,
450
+ metadata: {
451
+ logs: capturedLogs,
452
+ stepTraces: sanitizedStepTraces,
453
+ rawStepTraces: capturedStepTraces
454
+ }
455
+ }
456
+ }
457
+
381
458
  if (captured && captured.trim()) {
382
459
  return {
383
460
  output: captured,
@@ -399,6 +476,7 @@ export class RecombineAgentProvider implements ApiProvider {
399
476
  }
400
477
  }
401
478
  } catch (err) {
479
+ if (this.abortSignal?.aborted) throw err
402
480
  const msg = err instanceof Error ? `${err.name}: ${err.message}` : String(err)
403
481
  capturedLogs.push(`[ERROR] ${msg}`)
404
482
  try {
@@ -531,15 +609,15 @@ export async function runPromptTests(
531
609
  const onlyTestId = 'testId' in selection ? selection.testId : undefined
532
610
  const suiteKey = suite.suiteKey
533
611
  const loadedSuite = await readSuite(suiteKey)
534
- const rows = loadedSuite.tests as unknown as CsvRow[]
612
+ const rows = loadedSuite.tests
535
613
  const cfg = (await tryReadConfig(suiteKey)) || {}
536
614
 
537
615
  const repeats = Number(cfg?.repeats ?? 1)
538
- const concurrency = Number(cfg?.concurrency ?? 1)
616
+ const concurrency = Math.min(75, Math.max(1, Math.floor(Number(cfg?.concurrency ?? 75))))
539
617
  const baseContext = loadedSuite?.meta?.defaultContext || {}
540
618
 
541
619
  // Add an index field to each row for mapping
542
- const typedRows = rows as unknown as CsvRow[]
620
+ const typedRows = rows
543
621
  const indexedRows = typedRows.map((row, index) => ({ ...row, index }))
544
622
 
545
623
  if (onlyTestId && !indexedRows.some((row) => String(row.id ?? '') === String(onlyTestId))) {
@@ -547,12 +625,27 @@ export async function runPromptTests(
547
625
  }
548
626
 
549
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)
550
638
 
551
639
  // Provider function with stable label
552
640
  const providerInstance = new RecombineAgentProvider({
553
641
  id: 'recombine-agent'
554
642
  })
555
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
+ })
556
649
  const providerFn = Object.assign(
557
650
  (_promptStr: string, ctx?: CallApiContextParams) => {
558
651
  if (signal?.aborted) {
@@ -566,37 +659,40 @@ export async function runPromptTests(
566
659
  const hookKey = `__bosun_promptfoo_hook_${process.pid}_${Date.now()}_${Math.random().toString(16).slice(2)}`
567
660
  const extensionCode = `globalThis[${JSON.stringify(hookKey)}]?.(output, context)`
568
661
 
569
- const testSuites = tests.map((test) => ({
662
+ const testSuite = {
570
663
  prompts: ['agent-run'], // placeholder; provider ignores
571
- scenario: test.scenario,
572
- tests: [
573
- {
574
- metadata: { testId: test.id },
575
- assert: test.assertions,
576
- model: 'not used!',
577
- vars: {
578
- agentId: suite.agentId,
579
- ...(suite.region ? { region: suite.region } : {}),
580
- context: test.context,
581
- conversation: test.conversation,
582
- ...(test.conversationParseError
583
- ? { conversationParseError: test.conversationParseError }
584
- : {})
585
- }
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
+ : {})
586
680
  }
587
- ],
681
+ })),
588
682
  providers: [providerFn],
589
683
  extensions: [extensionCode]
590
- }))
684
+ }
591
685
 
592
- const projectRoot = path.dirname(await getRootPath())
686
+ const rootPath = await getRootPath()
687
+ installTestPromptCacheBust(rootPath)
688
+ const projectRoot = path.dirname(rootPath)
593
689
  const outDir = path.join(projectRoot, 'data', 'test_runs')
594
690
  await mkdir(outDir, { recursive: true })
595
691
 
596
692
  // Use full ISO timestamp and sanitize ':' for filesystem safety
597
693
  const iso = new Date().toISOString()
598
694
  const isoFile = iso.replace(/:/g, '-')
599
- const outPath = path.join(outDir, `${suiteKey}.${isoFile}.report.json`)
695
+ const outPath = path.join(outDir, `${suiteKey}.${isoFile}${REPORT_JSON_SUFFIX}`)
600
696
 
601
697
  async function writeReport(report: WrittenRunReport) {
602
698
  const dir = path.dirname(outPath)
@@ -639,7 +735,7 @@ export async function runPromptTests(
639
735
 
640
736
  const byTestId = new Map<string, WrittenRunReportRow>()
641
737
 
642
- const originalRowById = new Map<string, CsvRow>()
738
+ const originalRowById = new Map<string, SuiteTestRow>()
643
739
  for (const r of typedRows) {
644
740
  const id = String((r as any)?.id ?? '').trim()
645
741
  if (id) originalRowById.set(id, r)
@@ -685,6 +781,7 @@ export async function runPromptTests(
685
781
  String((originalRow as any).pinned || '').toUpperCase() === 'TRUE' ? 'TRUE' : 'FALSE'
686
782
  const nextRow: WrittenRunReportRow = {
687
783
  ...(originalRow as any),
784
+ test: originalRow,
688
785
  prompts: `suite://${suiteKey}?testId=${testId}`,
689
786
  success_rate_percent: 0,
690
787
  success_rate: '0/0',
@@ -704,6 +801,15 @@ export async function runPromptTests(
704
801
 
705
802
  const entry = toResultEntry(r)
706
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
+ }
707
813
 
708
814
  const counts = countsByTestId.get(testId) ?? { pass: 0, total: 0 }
709
815
  counts.total += 1
@@ -722,75 +828,68 @@ export async function runPromptTests(
722
828
  }
723
829
  }
724
830
 
725
- try {
726
- for (const suite of testSuites) {
727
- if (signal?.aborted) break
728
- const testId = String((suite.tests?.[0] as any)?.metadata?.testId ?? '')
729
- const originalRow = testId ? typedRows.find((r) => String(r.id) === testId) : undefined
730
- try {
731
- console.log(`Running: ${suite.scenario}`)
732
- const ev = await promptfoo.evaluate(suite, {
733
- repeat: repeats,
734
- cache: false,
735
- maxConcurrency: concurrency,
736
- abortSignal: signal
737
- })
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
+ }
738
854
 
739
- if (originalRow && testId) {
740
- const rowToWrite = buildTableRows([ev], [originalRow], suiteKey)[0]
741
- const pinned: 'TRUE' | 'FALSE' =
742
- (rowToWrite.pinned || '').toUpperCase() === 'TRUE' ? 'TRUE' : 'FALSE'
743
- byTestId.set(testId, {
744
- ...rowToWrite,
745
- agent: suiteKey,
746
- test_id: testId,
747
- run_timestamp: iso,
748
- run_config: JSON.stringify(runConfig),
749
- pinned_at_run: scope === 'all' || !!onlyTestId ? 'TRUE' : pinned,
750
- run_scope: scope
751
- })
752
- body.rows = Array.from(byTestId.values())
753
- await enqueueWrite()
754
- }
755
- } catch (err) {
756
- const cancelled =
757
- !!signal?.aborted ||
758
- (err instanceof Error && /operation cancelled/i.test(err.message || ''))
759
- if (cancelled) {
760
- break
761
- }
762
- const msg = err instanceof Error ? `${err.name}: ${err.message}` : String(err)
763
- const ev = {
764
- results: [
765
- {
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
+ ({
766
880
  success: false,
767
881
  response: { output: '' },
768
882
  error: msg,
769
- metadata: { testId: (suite.tests?.[0] as any)?.metadata?.testId }
770
- } as any
771
- ]
772
- } as any
773
-
774
- // Persist failure row immediately for real-time UI updates.
775
- if (originalRow && testId) {
776
- const rowToWrite = buildTableRows([ev], [originalRow], suiteKey)[0]
777
- const pinned: 'TRUE' | 'FALSE' =
778
- (rowToWrite.pinned || '').toUpperCase() === 'TRUE' ? 'TRUE' : 'FALSE'
779
- byTestId.set(testId, {
780
- ...rowToWrite,
781
- agent: suiteKey,
782
- test_id: testId,
783
- run_timestamp: iso,
784
- run_config: JSON.stringify(runConfig),
785
- pinned_at_run: scope === 'all' || !!onlyTestId ? 'TRUE' : pinned,
786
- run_scope: scope
787
- })
788
- body.rows = Array.from(byTestId.values())
789
- await enqueueWrite()
790
- }
791
- }
883
+ metadata: { testId }
884
+ }) as any
885
+ )
886
+ } as PFEval
887
+ persistEvaluation(evaluation)
888
+ await enqueueWrite()
792
889
  }
793
890
  } finally {
891
+ if (previousIsTest == null) delete process.env.IS_TEST
892
+ else process.env.IS_TEST = previousIsTest
794
893
  if (writeTimer) clearTimeout(writeTimer)
795
894
  writeTimer = null
796
895
  delete (globalThis as any)[hookKey]
@@ -800,6 +899,9 @@ export async function runPromptTests(
800
899
  // Ensure the final report is written even if no tests ran (or an update was skipped).
801
900
  body.rows = Array.from(byTestId.values())
802
901
  await enqueueWrite()
902
+ void queueReportCompression(outPath).catch((error) => {
903
+ console.error(`Failed to compress completed test report ${path.basename(outPath)}`, error)
904
+ })
803
905
 
804
906
  return body
805
907
  }