@recombine-ai/bosun 0.16.2-cloud-1 → 0.16.2-cloud-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 (359) hide show
  1. package/build/_app/immutable/assets/0.CDmx-FLf.css +1 -0
  2. package/build/_app/immutable/assets/0.CDmx-FLf.css.br +0 -0
  3. package/build/_app/immutable/assets/0.CDmx-FLf.css.gz +0 -0
  4. package/build/_app/immutable/assets/{2.Dc5_ru9X.css → 2.C-ckNQUz.css} +1 -1
  5. package/build/_app/immutable/assets/2.C-ckNQUz.css.br +0 -0
  6. package/build/_app/immutable/assets/2.C-ckNQUz.css.gz +0 -0
  7. package/build/_app/immutable/chunks/12bfCwgn.js +2 -0
  8. package/build/_app/immutable/chunks/12bfCwgn.js.br +0 -0
  9. package/build/_app/immutable/chunks/12bfCwgn.js.gz +0 -0
  10. package/build/_app/immutable/chunks/{C-Qcqb3K.js → 6OCsQ6r7.js} +2 -2
  11. package/build/_app/immutable/chunks/6OCsQ6r7.js.br +0 -0
  12. package/build/_app/immutable/chunks/6OCsQ6r7.js.gz +0 -0
  13. package/build/_app/immutable/chunks/AvXp07rr.js +1 -0
  14. package/build/_app/immutable/chunks/AvXp07rr.js.br +2 -0
  15. package/build/_app/immutable/chunks/AvXp07rr.js.gz +0 -0
  16. package/build/_app/immutable/chunks/{OYhYuonT.js → B434V0h8.js} +1 -1
  17. package/build/_app/immutable/chunks/B434V0h8.js.br +0 -0
  18. package/build/_app/immutable/chunks/B434V0h8.js.gz +0 -0
  19. package/build/_app/immutable/chunks/BJqZY8o2.js +1 -0
  20. package/build/_app/immutable/chunks/BJqZY8o2.js.br +0 -0
  21. package/build/_app/immutable/chunks/BJqZY8o2.js.gz +0 -0
  22. package/build/_app/immutable/chunks/{DHvIizf-.js → BKQbRSey.js} +1 -1
  23. package/build/_app/immutable/chunks/BKQbRSey.js.br +0 -0
  24. package/build/_app/immutable/chunks/BKQbRSey.js.gz +0 -0
  25. package/build/_app/immutable/chunks/BOHUKGg_.js +1 -0
  26. package/build/_app/immutable/chunks/BOHUKGg_.js.br +0 -0
  27. package/build/_app/immutable/chunks/BOHUKGg_.js.gz +0 -0
  28. package/build/_app/immutable/chunks/BP0Xadq7.js +5 -0
  29. package/build/_app/immutable/chunks/BP0Xadq7.js.br +0 -0
  30. package/build/_app/immutable/chunks/BP0Xadq7.js.gz +0 -0
  31. package/build/_app/immutable/chunks/BT98u4gR.js +35 -0
  32. package/build/_app/immutable/chunks/BT98u4gR.js.br +0 -0
  33. package/build/_app/immutable/chunks/BT98u4gR.js.gz +0 -0
  34. package/build/_app/immutable/chunks/{DjqcYvLr.js → Bc1oIj2c.js} +1 -1
  35. package/build/_app/immutable/chunks/Bc1oIj2c.js.br +0 -0
  36. package/build/_app/immutable/chunks/Bc1oIj2c.js.gz +0 -0
  37. package/build/_app/immutable/chunks/{CWB6mNDX.js → BiDy9-HP.js} +1 -1
  38. package/build/_app/immutable/chunks/BiDy9-HP.js.br +0 -0
  39. package/build/_app/immutable/chunks/BiDy9-HP.js.gz +0 -0
  40. package/build/_app/immutable/chunks/{BEa3nvFX.js → By9tvIIw.js} +1 -1
  41. package/build/_app/immutable/chunks/By9tvIIw.js.br +0 -0
  42. package/build/_app/immutable/chunks/By9tvIIw.js.gz +0 -0
  43. package/build/_app/immutable/chunks/Byx1piFp.js +3 -0
  44. package/build/_app/immutable/chunks/Byx1piFp.js.br +0 -0
  45. package/build/_app/immutable/chunks/Byx1piFp.js.gz +0 -0
  46. package/build/_app/immutable/chunks/{DSaIAZ57.js → C1TsmEOe.js} +1 -1
  47. package/build/_app/immutable/chunks/C1TsmEOe.js.br +0 -0
  48. package/build/_app/immutable/chunks/C1TsmEOe.js.gz +0 -0
  49. package/build/_app/immutable/chunks/CGV7fMmX.js +1 -0
  50. package/build/_app/immutable/chunks/CGV7fMmX.js.br +0 -0
  51. package/build/_app/immutable/chunks/CGV7fMmX.js.gz +0 -0
  52. package/build/_app/immutable/chunks/CL717zGu.js +2 -0
  53. package/build/_app/immutable/chunks/CL717zGu.js.br +0 -0
  54. package/build/_app/immutable/chunks/CL717zGu.js.gz +0 -0
  55. package/build/_app/immutable/chunks/{DrOC9WNt.js → CPCsWwEy.js} +1 -1
  56. package/build/_app/immutable/chunks/CPCsWwEy.js.br +0 -0
  57. package/build/_app/immutable/chunks/{DrOC9WNt.js.gz → CPCsWwEy.js.gz} +0 -0
  58. package/build/_app/immutable/chunks/{Ds1yKsjr.js → CUZn05nQ.js} +2 -2
  59. package/build/_app/immutable/chunks/CUZn05nQ.js.br +0 -0
  60. package/build/_app/immutable/chunks/{Ds1yKsjr.js.gz → CUZn05nQ.js.gz} +0 -0
  61. package/build/_app/immutable/chunks/{ClP8OL3G.js → CZMMlaMl.js} +1 -1
  62. package/build/_app/immutable/chunks/CZMMlaMl.js.br +0 -0
  63. package/build/_app/immutable/chunks/CZMMlaMl.js.gz +0 -0
  64. package/build/_app/immutable/chunks/{DLL_0B-9.js → C_yvzB4Z.js} +1 -1
  65. package/build/_app/immutable/chunks/C_yvzB4Z.js.br +0 -0
  66. package/build/_app/immutable/chunks/{DLL_0B-9.js.gz → C_yvzB4Z.js.gz} +0 -0
  67. package/build/_app/immutable/chunks/{DC_-4bAG.js → CdnbUlBq.js} +1 -1
  68. package/build/_app/immutable/chunks/CdnbUlBq.js.br +0 -0
  69. package/build/_app/immutable/chunks/CdnbUlBq.js.gz +0 -0
  70. package/build/_app/immutable/chunks/Cf3l6QEG.js +2 -0
  71. package/build/_app/immutable/chunks/Cf3l6QEG.js.br +0 -0
  72. package/build/_app/immutable/chunks/Cf3l6QEG.js.gz +0 -0
  73. package/build/_app/immutable/chunks/CfzHeBbL.js +1 -0
  74. package/build/_app/immutable/chunks/CfzHeBbL.js.br +1 -0
  75. package/build/_app/immutable/chunks/CfzHeBbL.js.gz +0 -0
  76. package/build/_app/immutable/chunks/{BzyUIe1_.js → D1-opoqB.js} +1 -1
  77. package/build/_app/immutable/chunks/D1-opoqB.js.br +0 -0
  78. package/build/_app/immutable/chunks/D1-opoqB.js.gz +0 -0
  79. package/build/_app/immutable/chunks/D3fLfD-Y.js +1 -0
  80. package/build/_app/immutable/chunks/D3fLfD-Y.js.br +0 -0
  81. package/build/_app/immutable/chunks/D3fLfD-Y.js.gz +0 -0
  82. package/build/_app/immutable/chunks/DDZ8GqHh.js +1 -0
  83. package/build/_app/immutable/chunks/DDZ8GqHh.js.br +0 -0
  84. package/build/_app/immutable/chunks/DDZ8GqHh.js.gz +0 -0
  85. package/build/_app/immutable/chunks/{CH4VrfW2.js → DD_fL9ig.js} +1 -1
  86. package/build/_app/immutable/chunks/DD_fL9ig.js.br +0 -0
  87. package/build/_app/immutable/chunks/DD_fL9ig.js.gz +0 -0
  88. package/build/_app/immutable/chunks/{ByU1nS5x.js → DVZC5aEW.js} +1 -1
  89. package/build/_app/immutable/chunks/DVZC5aEW.js.br +0 -0
  90. package/build/_app/immutable/chunks/DVZC5aEW.js.gz +0 -0
  91. package/build/_app/immutable/chunks/D_MTg6YY.js +1 -0
  92. package/build/_app/immutable/chunks/D_MTg6YY.js.br +0 -0
  93. package/build/_app/immutable/chunks/D_MTg6YY.js.gz +0 -0
  94. package/build/_app/immutable/chunks/{C78K0g2i.js → DbZCwjKq.js} +1 -1
  95. package/build/_app/immutable/chunks/DbZCwjKq.js.br +0 -0
  96. package/build/_app/immutable/chunks/DbZCwjKq.js.gz +0 -0
  97. package/build/_app/immutable/chunks/Dc2hpeC4.js +1 -0
  98. package/build/_app/immutable/chunks/Dc2hpeC4.js.br +2 -0
  99. package/build/_app/immutable/chunks/Dc2hpeC4.js.gz +0 -0
  100. package/build/_app/immutable/chunks/{NZcW2kfJ.js → DjxKCxwY.js} +1 -1
  101. package/build/_app/immutable/chunks/DjxKCxwY.js.br +0 -0
  102. package/build/_app/immutable/chunks/DjxKCxwY.js.gz +0 -0
  103. package/build/_app/immutable/chunks/{DeiSJRh0.js → DkJi5BM4.js} +1 -1
  104. package/build/_app/immutable/chunks/DkJi5BM4.js.br +0 -0
  105. package/build/_app/immutable/chunks/DkJi5BM4.js.gz +0 -0
  106. package/build/_app/immutable/chunks/Dne1wF_T.js +1 -0
  107. package/build/_app/immutable/chunks/Dne1wF_T.js.br +0 -0
  108. package/build/_app/immutable/chunks/Dne1wF_T.js.gz +0 -0
  109. package/build/_app/immutable/chunks/{BlwlkbCs.js → Dt1k81ic.js} +1 -1
  110. package/build/_app/immutable/chunks/Dt1k81ic.js.br +0 -0
  111. package/build/_app/immutable/chunks/Dt1k81ic.js.gz +0 -0
  112. package/build/_app/immutable/chunks/{CKf4QksN.js → DugeGFnl.js} +1 -1
  113. package/build/_app/immutable/chunks/DugeGFnl.js.br +0 -0
  114. package/build/_app/immutable/chunks/DugeGFnl.js.gz +0 -0
  115. package/build/_app/immutable/chunks/{D5UNd3Zp.js → MIEISUXS.js} +1 -1
  116. package/build/_app/immutable/chunks/MIEISUXS.js.br +0 -0
  117. package/build/_app/immutable/chunks/MIEISUXS.js.gz +0 -0
  118. package/build/_app/immutable/chunks/{lyi4TUVm.js → Vn-6xHGu.js} +1 -1
  119. package/build/_app/immutable/chunks/Vn-6xHGu.js.br +0 -0
  120. package/build/_app/immutable/chunks/Vn-6xHGu.js.gz +0 -0
  121. package/build/_app/immutable/chunks/{jk4Hz_9M.js → krcqaNpB.js} +1 -1
  122. package/build/_app/immutable/chunks/krcqaNpB.js.br +0 -0
  123. package/build/_app/immutable/chunks/krcqaNpB.js.gz +0 -0
  124. package/build/_app/immutable/chunks/oarl--hZ.js +1 -0
  125. package/build/_app/immutable/chunks/oarl--hZ.js.br +0 -0
  126. package/build/_app/immutable/chunks/oarl--hZ.js.gz +0 -0
  127. package/build/_app/immutable/chunks/{CfbKmr1m.js → qrpS1hNb.js} +1 -1
  128. package/build/_app/immutable/chunks/qrpS1hNb.js.br +0 -0
  129. package/build/_app/immutable/chunks/qrpS1hNb.js.gz +0 -0
  130. package/build/_app/immutable/entry/app.D_IBy7uS.js +2 -0
  131. package/build/_app/immutable/entry/app.D_IBy7uS.js.br +0 -0
  132. package/build/_app/immutable/entry/app.D_IBy7uS.js.gz +0 -0
  133. package/build/_app/immutable/entry/start.DbNkBLf5.js +1 -0
  134. package/build/_app/immutable/entry/start.DbNkBLf5.js.br +0 -0
  135. package/build/_app/immutable/entry/start.DbNkBLf5.js.gz +0 -0
  136. package/build/_app/immutable/nodes/0.CS5BBLx9.js +3 -0
  137. package/build/_app/immutable/nodes/0.CS5BBLx9.js.br +0 -0
  138. package/build/_app/immutable/nodes/0.CS5BBLx9.js.gz +0 -0
  139. package/build/_app/immutable/nodes/1.Bx_DOFs1.js +1 -0
  140. package/build/_app/immutable/nodes/1.Bx_DOFs1.js.br +1 -0
  141. package/build/_app/immutable/nodes/1.Bx_DOFs1.js.gz +0 -0
  142. package/build/_app/immutable/nodes/14.DSu8i_qz.js +1 -0
  143. package/build/_app/immutable/nodes/14.DSu8i_qz.js.br +0 -0
  144. package/build/_app/immutable/nodes/14.DSu8i_qz.js.gz +0 -0
  145. package/build/_app/immutable/nodes/16.Cev9Grgc.js +3 -0
  146. package/build/_app/immutable/nodes/16.Cev9Grgc.js.br +0 -0
  147. package/build/_app/immutable/nodes/16.Cev9Grgc.js.gz +0 -0
  148. package/build/_app/immutable/nodes/17.C869oOf4.js +10 -0
  149. package/build/_app/immutable/nodes/17.C869oOf4.js.br +0 -0
  150. package/build/_app/immutable/nodes/17.C869oOf4.js.gz +0 -0
  151. package/build/_app/immutable/nodes/2.DdU1kSNu.js +35 -0
  152. package/build/_app/immutable/nodes/2.DdU1kSNu.js.br +0 -0
  153. package/build/_app/immutable/nodes/2.DdU1kSNu.js.gz +0 -0
  154. package/build/_app/immutable/nodes/3.C5QPqWTE.js +1 -0
  155. package/build/_app/immutable/nodes/3.C5QPqWTE.js.br +0 -0
  156. package/build/_app/immutable/nodes/3.C5QPqWTE.js.gz +0 -0
  157. package/build/_app/immutable/nodes/4.CNS3NZ4Z.js +1 -0
  158. package/build/_app/immutable/nodes/4.CNS3NZ4Z.js.br +0 -0
  159. package/build/_app/immutable/nodes/4.CNS3NZ4Z.js.gz +0 -0
  160. package/build/_app/immutable/nodes/5.B1H9pyMk.js +1 -0
  161. package/build/_app/immutable/nodes/5.B1H9pyMk.js.br +0 -0
  162. package/build/_app/immutable/nodes/5.B1H9pyMk.js.gz +0 -0
  163. package/build/_app/immutable/nodes/6.h6Y1Z_zh.js +1 -0
  164. package/build/_app/immutable/nodes/6.h6Y1Z_zh.js.br +0 -0
  165. package/build/_app/immutable/nodes/6.h6Y1Z_zh.js.gz +0 -0
  166. package/build/_app/immutable/nodes/8.BCatMgwB.js +2 -0
  167. package/build/_app/immutable/nodes/8.BCatMgwB.js.br +0 -0
  168. package/build/_app/immutable/nodes/8.BCatMgwB.js.gz +0 -0
  169. package/build/_app/immutable/nodes/9.BUnel48-.js +24 -0
  170. package/build/_app/immutable/nodes/9.BUnel48-.js.br +0 -0
  171. package/build/_app/immutable/nodes/9.BUnel48-.js.gz +0 -0
  172. package/build/_app/immutable/workers/{css.worker-DflDYzxC.js → css.worker-D61Wlo1y.js} +1 -1
  173. package/build/_app/immutable/workers/css.worker-D61Wlo1y.js.br +0 -0
  174. package/build/_app/immutable/workers/{css.worker-DflDYzxC.js.gz → css.worker-D61Wlo1y.js.gz} +0 -0
  175. package/build/_app/immutable/workers/{editor.worker-DbG82rLA.js → editor.worker-BaAK1IVA.js} +1 -1
  176. package/build/_app/immutable/workers/editor.worker-BaAK1IVA.js.br +0 -0
  177. package/build/_app/immutable/workers/{editor.worker-DbG82rLA.js.gz → editor.worker-BaAK1IVA.js.gz} +0 -0
  178. package/build/_app/immutable/workers/{html.worker-Bn6S4v1s.js → html.worker-i4ABlmR0.js} +1 -1
  179. package/build/_app/immutable/workers/html.worker-i4ABlmR0.js.br +0 -0
  180. package/build/_app/immutable/workers/{html.worker-Bn6S4v1s.js.gz → html.worker-i4ABlmR0.js.gz} +0 -0
  181. package/build/_app/immutable/workers/{json.worker-CfS7sFxM.js → json.worker-C-kBQgxp.js} +1 -1
  182. package/build/_app/immutable/workers/json.worker-C-kBQgxp.js.br +0 -0
  183. package/build/_app/immutable/workers/{json.worker-CfS7sFxM.js.gz → json.worker-C-kBQgxp.js.gz} +0 -0
  184. package/build/_app/immutable/workers/{ts.worker-Bqwj6xoy.js → ts.worker-KJ4wGYue.js} +2 -2
  185. package/build/_app/immutable/workers/ts.worker-KJ4wGYue.js.br +0 -0
  186. package/build/_app/immutable/workers/{ts.worker-Bqwj6xoy.js.gz → ts.worker-KJ4wGYue.js.gz} +0 -0
  187. package/build/_app/version.json +1 -1
  188. package/build/_app/version.json.br +0 -0
  189. package/build/_app/version.json.gz +0 -0
  190. package/build/index.html +13 -13
  191. package/build/index.html.br +0 -0
  192. package/build/index.html.gz +0 -0
  193. package/dist/index.js +616 -106
  194. package/dist/index.js.map +4 -4
  195. package/dist/types/backend/roleplay/prompt-edit-proposals.d.ts +13 -1
  196. package/dist/types/backend/roleplay/prompt-file.d.ts +8 -0
  197. package/dist/types/backend/roleplay/router.d.ts +29 -0
  198. package/dist/types/backend/roleplay/suggest-file-edit.d.ts +13 -0
  199. package/dist/types/backend/roleplay/suggest-test-fix.d.ts +29 -0
  200. package/dist/types/backend/server.d.ts +116 -0
  201. package/dist/types/backend/telescope/router.d.ts +23 -0
  202. package/dist/types/backend/tests/academy.d.ts +9 -1
  203. package/dist/types/backend/tests/data.d.ts +13 -1
  204. package/dist/types/backend/tests/router.d.ts +64 -0
  205. package/dist/types/backend/tests/suggest-test-history-edit.d.ts +21 -0
  206. package/dist/types/backend/tests/test-edit-source.d.ts +4 -0
  207. package/dist/types/backend/tests/test-history-edit-proposals.d.ts +24 -0
  208. package/dist/types/lib/api-telescope/types.d.ts +38 -0
  209. package/dist/types/lib/edit/test-ai-edit.d.ts +12 -0
  210. package/dist/types/lib/tests/assertion-types.d.ts +6 -0
  211. package/package.json +1 -1
  212. package/build/_app/immutable/assets/0.CyF-TaEi.css +0 -1
  213. package/build/_app/immutable/assets/0.CyF-TaEi.css.br +0 -0
  214. package/build/_app/immutable/assets/0.CyF-TaEi.css.gz +0 -0
  215. package/build/_app/immutable/assets/2.Dc5_ru9X.css.br +0 -0
  216. package/build/_app/immutable/assets/2.Dc5_ru9X.css.gz +0 -0
  217. package/build/_app/immutable/chunks/0Yl3erOE.js +0 -2
  218. package/build/_app/immutable/chunks/0Yl3erOE.js.br +0 -0
  219. package/build/_app/immutable/chunks/0Yl3erOE.js.gz +0 -0
  220. package/build/_app/immutable/chunks/797NXciN.js +0 -1
  221. package/build/_app/immutable/chunks/797NXciN.js.br +0 -0
  222. package/build/_app/immutable/chunks/797NXciN.js.gz +0 -0
  223. package/build/_app/immutable/chunks/9JriNUvn.js +0 -1
  224. package/build/_app/immutable/chunks/9JriNUvn.js.br +0 -0
  225. package/build/_app/immutable/chunks/9JriNUvn.js.gz +0 -0
  226. package/build/_app/immutable/chunks/BEa3nvFX.js.br +0 -0
  227. package/build/_app/immutable/chunks/BEa3nvFX.js.gz +0 -0
  228. package/build/_app/immutable/chunks/BOL6qRLe.js +0 -3
  229. package/build/_app/immutable/chunks/BOL6qRLe.js.br +0 -0
  230. package/build/_app/immutable/chunks/BOL6qRLe.js.gz +0 -0
  231. package/build/_app/immutable/chunks/BYkIDsVz.js +0 -1
  232. package/build/_app/immutable/chunks/BYkIDsVz.js.br +0 -0
  233. package/build/_app/immutable/chunks/BYkIDsVz.js.gz +0 -0
  234. package/build/_app/immutable/chunks/BlwlkbCs.js.br +0 -0
  235. package/build/_app/immutable/chunks/BlwlkbCs.js.gz +0 -0
  236. package/build/_app/immutable/chunks/ByU1nS5x.js.br +0 -0
  237. package/build/_app/immutable/chunks/ByU1nS5x.js.gz +0 -0
  238. package/build/_app/immutable/chunks/BzyUIe1_.js.br +0 -0
  239. package/build/_app/immutable/chunks/BzyUIe1_.js.gz +0 -0
  240. package/build/_app/immutable/chunks/C-Qcqb3K.js.br +0 -0
  241. package/build/_app/immutable/chunks/C-Qcqb3K.js.gz +0 -0
  242. package/build/_app/immutable/chunks/C-qso1Pb.js +0 -2
  243. package/build/_app/immutable/chunks/C-qso1Pb.js.br +0 -0
  244. package/build/_app/immutable/chunks/C-qso1Pb.js.gz +0 -0
  245. package/build/_app/immutable/chunks/C78K0g2i.js.br +0 -0
  246. package/build/_app/immutable/chunks/C78K0g2i.js.gz +0 -0
  247. package/build/_app/immutable/chunks/C970FsJW.js +0 -1
  248. package/build/_app/immutable/chunks/C970FsJW.js.br +0 -0
  249. package/build/_app/immutable/chunks/C970FsJW.js.gz +0 -0
  250. package/build/_app/immutable/chunks/CBzPmXSf.js +0 -1
  251. package/build/_app/immutable/chunks/CBzPmXSf.js.br +0 -0
  252. package/build/_app/immutable/chunks/CBzPmXSf.js.gz +0 -0
  253. package/build/_app/immutable/chunks/CH4VrfW2.js.br +0 -0
  254. package/build/_app/immutable/chunks/CH4VrfW2.js.gz +0 -0
  255. package/build/_app/immutable/chunks/CKf4QksN.js.br +0 -0
  256. package/build/_app/immutable/chunks/CKf4QksN.js.gz +0 -0
  257. package/build/_app/immutable/chunks/CPnjcwfB.js +0 -1
  258. package/build/_app/immutable/chunks/CPnjcwfB.js.br +0 -0
  259. package/build/_app/immutable/chunks/CPnjcwfB.js.gz +0 -0
  260. package/build/_app/immutable/chunks/CWB6mNDX.js.br +0 -0
  261. package/build/_app/immutable/chunks/CWB6mNDX.js.gz +0 -0
  262. package/build/_app/immutable/chunks/CcZs-N7g.js +0 -6
  263. package/build/_app/immutable/chunks/CcZs-N7g.js.br +0 -0
  264. package/build/_app/immutable/chunks/CcZs-N7g.js.gz +0 -0
  265. package/build/_app/immutable/chunks/CfbKmr1m.js.br +0 -0
  266. package/build/_app/immutable/chunks/CfbKmr1m.js.gz +0 -0
  267. package/build/_app/immutable/chunks/ClP8OL3G.js.br +0 -0
  268. package/build/_app/immutable/chunks/ClP8OL3G.js.gz +0 -0
  269. package/build/_app/immutable/chunks/D5UNd3Zp.js.br +0 -0
  270. package/build/_app/immutable/chunks/D5UNd3Zp.js.gz +0 -0
  271. package/build/_app/immutable/chunks/D6ibSY3A.js +0 -1
  272. package/build/_app/immutable/chunks/D6ibSY3A.js.br +0 -1
  273. package/build/_app/immutable/chunks/D6ibSY3A.js.gz +0 -0
  274. package/build/_app/immutable/chunks/DC_-4bAG.js.br +0 -1
  275. package/build/_app/immutable/chunks/DC_-4bAG.js.gz +0 -0
  276. package/build/_app/immutable/chunks/DHvIizf-.js.br +0 -0
  277. package/build/_app/immutable/chunks/DHvIizf-.js.gz +0 -0
  278. package/build/_app/immutable/chunks/DLL_0B-9.js.br +0 -0
  279. package/build/_app/immutable/chunks/DSaIAZ57.js.br +0 -0
  280. package/build/_app/immutable/chunks/DSaIAZ57.js.gz +0 -0
  281. package/build/_app/immutable/chunks/DeiSJRh0.js.br +0 -0
  282. package/build/_app/immutable/chunks/DeiSJRh0.js.gz +0 -0
  283. package/build/_app/immutable/chunks/DjqcYvLr.js.br +0 -0
  284. package/build/_app/immutable/chunks/DjqcYvLr.js.gz +0 -0
  285. package/build/_app/immutable/chunks/DrOC9WNt.js.br +0 -0
  286. package/build/_app/immutable/chunks/Ds1yKsjr.js.br +0 -0
  287. package/build/_app/immutable/chunks/NZcW2kfJ.js.br +0 -0
  288. package/build/_app/immutable/chunks/NZcW2kfJ.js.gz +0 -0
  289. package/build/_app/immutable/chunks/OPRu0_Dd.js +0 -1
  290. package/build/_app/immutable/chunks/OPRu0_Dd.js.br +0 -2
  291. package/build/_app/immutable/chunks/OPRu0_Dd.js.gz +0 -0
  292. package/build/_app/immutable/chunks/OYhYuonT.js.br +0 -0
  293. package/build/_app/immutable/chunks/OYhYuonT.js.gz +0 -0
  294. package/build/_app/immutable/chunks/VR9lxEL5.js +0 -1
  295. package/build/_app/immutable/chunks/VR9lxEL5.js.br +0 -0
  296. package/build/_app/immutable/chunks/VR9lxEL5.js.gz +0 -0
  297. package/build/_app/immutable/chunks/_lbjRyAo.js +0 -35
  298. package/build/_app/immutable/chunks/_lbjRyAo.js.br +0 -0
  299. package/build/_app/immutable/chunks/_lbjRyAo.js.gz +0 -0
  300. package/build/_app/immutable/chunks/arxGXJ5w.js +0 -1
  301. package/build/_app/immutable/chunks/arxGXJ5w.js.br +0 -2
  302. package/build/_app/immutable/chunks/arxGXJ5w.js.gz +0 -0
  303. package/build/_app/immutable/chunks/cVn7fglh.js +0 -1
  304. package/build/_app/immutable/chunks/cVn7fglh.js.br +0 -1
  305. package/build/_app/immutable/chunks/cVn7fglh.js.gz +0 -0
  306. package/build/_app/immutable/chunks/jk4Hz_9M.js.br +0 -0
  307. package/build/_app/immutable/chunks/jk4Hz_9M.js.gz +0 -0
  308. package/build/_app/immutable/chunks/lyi4TUVm.js.br +0 -0
  309. package/build/_app/immutable/chunks/lyi4TUVm.js.gz +0 -0
  310. package/build/_app/immutable/entry/app.D01tWdhc.js +0 -2
  311. package/build/_app/immutable/entry/app.D01tWdhc.js.br +0 -0
  312. package/build/_app/immutable/entry/app.D01tWdhc.js.gz +0 -0
  313. package/build/_app/immutable/entry/start.Dkgn2MUA.js +0 -1
  314. package/build/_app/immutable/entry/start.Dkgn2MUA.js.br +0 -0
  315. package/build/_app/immutable/entry/start.Dkgn2MUA.js.gz +0 -0
  316. package/build/_app/immutable/nodes/0.t2rvsaQV.js +0 -3
  317. package/build/_app/immutable/nodes/0.t2rvsaQV.js.br +0 -0
  318. package/build/_app/immutable/nodes/0.t2rvsaQV.js.gz +0 -0
  319. package/build/_app/immutable/nodes/1.B8XtGxov.js +0 -1
  320. package/build/_app/immutable/nodes/1.B8XtGxov.js.br +0 -2
  321. package/build/_app/immutable/nodes/1.B8XtGxov.js.gz +0 -0
  322. package/build/_app/immutable/nodes/14.uj6UKEKM.js +0 -1
  323. package/build/_app/immutable/nodes/14.uj6UKEKM.js.br +0 -0
  324. package/build/_app/immutable/nodes/14.uj6UKEKM.js.gz +0 -0
  325. package/build/_app/immutable/nodes/16.C4g9w36Y.js +0 -3
  326. package/build/_app/immutable/nodes/16.C4g9w36Y.js.br +0 -0
  327. package/build/_app/immutable/nodes/16.C4g9w36Y.js.gz +0 -0
  328. package/build/_app/immutable/nodes/17.DQrHVfP-.js +0 -10
  329. package/build/_app/immutable/nodes/17.DQrHVfP-.js.br +0 -0
  330. package/build/_app/immutable/nodes/17.DQrHVfP-.js.gz +0 -0
  331. package/build/_app/immutable/nodes/2.DFs_rmXb.js +0 -35
  332. package/build/_app/immutable/nodes/2.DFs_rmXb.js.br +0 -0
  333. package/build/_app/immutable/nodes/2.DFs_rmXb.js.gz +0 -0
  334. package/build/_app/immutable/nodes/3.CmzGRE5R.js +0 -1
  335. package/build/_app/immutable/nodes/3.CmzGRE5R.js.br +0 -0
  336. package/build/_app/immutable/nodes/3.CmzGRE5R.js.gz +0 -0
  337. package/build/_app/immutable/nodes/4.D6TiMacd.js +0 -1
  338. package/build/_app/immutable/nodes/4.D6TiMacd.js.br +0 -0
  339. package/build/_app/immutable/nodes/4.D6TiMacd.js.gz +0 -0
  340. package/build/_app/immutable/nodes/5.C3QbUkKf.js +0 -1
  341. package/build/_app/immutable/nodes/5.C3QbUkKf.js.br +0 -0
  342. package/build/_app/immutable/nodes/5.C3QbUkKf.js.gz +0 -0
  343. package/build/_app/immutable/nodes/6.UWx6wdAE.js +0 -1
  344. package/build/_app/immutable/nodes/6.UWx6wdAE.js.br +0 -0
  345. package/build/_app/immutable/nodes/6.UWx6wdAE.js.gz +0 -0
  346. package/build/_app/immutable/nodes/8.bZ206KXo.js +0 -2
  347. package/build/_app/immutable/nodes/8.bZ206KXo.js.br +0 -0
  348. package/build/_app/immutable/nodes/8.bZ206KXo.js.gz +0 -0
  349. package/build/_app/immutable/nodes/9.CCSxSizl.js +0 -24
  350. package/build/_app/immutable/nodes/9.CCSxSizl.js.br +0 -0
  351. package/build/_app/immutable/nodes/9.CCSxSizl.js.gz +0 -0
  352. package/build/_app/immutable/workers/css.worker-DflDYzxC.js.br +0 -0
  353. package/build/_app/immutable/workers/editor.worker-DbG82rLA.js.br +0 -0
  354. package/build/_app/immutable/workers/html.worker-Bn6S4v1s.js.br +0 -0
  355. package/build/_app/immutable/workers/json.worker-CfS7sFxM.js.br +0 -0
  356. package/build/_app/immutable/workers/ts.worker-Bqwj6xoy.js.br +0 -0
  357. /package/build/_app/immutable/assets/{ReadOnlyMonacoDiff.Ba-kUPX7.css → Modal.Ba-kUPX7.css} +0 -0
  358. /package/build/_app/immutable/assets/{ReadOnlyMonacoDiff.Ba-kUPX7.css.br → Modal.Ba-kUPX7.css.br} +0 -0
  359. /package/build/_app/immutable/assets/{ReadOnlyMonacoDiff.Ba-kUPX7.css.gz → Modal.Ba-kUPX7.css.gz} +0 -0
package/dist/index.js CHANGED
@@ -627,8 +627,6 @@ function toText(event) {
627
627
  }
628
628
 
629
629
  // src/backend/roleplay/router.ts
630
- import fs3 from "fs";
631
- import path8 from "path";
632
630
  import { z as z4 } from "zod";
633
631
 
634
632
  // src/backend/lib/trpc-context.ts
@@ -646,7 +644,7 @@ function writeProcedure(t2) {
646
644
  import "openai";
647
645
 
648
646
  // src/backend/tests/data.ts
649
- import { createHash, randomUUID as randomUUID2 } from "node:crypto";
647
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
650
648
  import { readdir as readdir2, readFile as readFile3, stat, writeFile as writeFile2 } from "node:fs/promises";
651
649
  import path7 from "node:path";
652
650
 
@@ -927,6 +925,33 @@ function queueReportCompression(filePath) {
927
925
  return compression;
928
926
  }
929
927
 
928
+ // src/backend/tests/test-edit-source.ts
929
+ import { createHash } from "node:crypto";
930
+ function canonicalize(value) {
931
+ if (Array.isArray(value)) return value.map(canonicalize);
932
+ if (!value || typeof value !== "object") return value;
933
+ return Object.fromEntries(
934
+ Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, canonicalize(entry)])
935
+ );
936
+ }
937
+ function testEditSourceFingerprint(test, overrides) {
938
+ const source = canonicalize({
939
+ step: test.step,
940
+ evaluationMode: test.evaluation_mode,
941
+ assertionType: test.assertion_type,
942
+ assertionValue: test.assertion_value,
943
+ caseSensitive: test.case_sensitive,
944
+ contextMode: test.context_mode,
945
+ conversation: overrides?.conversation ?? test.conversation,
946
+ expectedOutput: test.expected_output,
947
+ timeout: test.timeout,
948
+ similarityThreshold: test.similarity_threshold,
949
+ context: test.context,
950
+ mocks: test.mocks ?? null
951
+ });
952
+ return createHash("sha256").update(JSON.stringify(source)).digest("hex");
953
+ }
954
+
930
955
  // src/backend/tests/data.ts
931
956
  var defaultTestRow = {
932
957
  order: 0,
@@ -983,7 +1008,7 @@ async function loadTests(ctx, agent) {
983
1008
  "context",
984
1009
  "mocks"
985
1010
  ];
986
- const workspaceId = createHash("sha256").update(process.cwd()).digest("hex").slice(0, 16);
1011
+ const workspaceId = createHash2("sha256").update(process.cwd()).digest("hex").slice(0, 16);
987
1012
  return { rows, headers, settingsKey: `bosun:test-settings:${workspaceId}:${agent}` };
988
1013
  });
989
1014
  }
@@ -999,6 +1024,22 @@ async function patchTest(ctx, agent, id, patch) {
999
1024
  return updated;
1000
1025
  });
1001
1026
  }
1027
+ async function patchTestConversationIfCurrent(ctx, agent, id, expectedConversation, conversation, allowedSourceFingerprints) {
1028
+ return withSuiteMutationLock(agent, async () => {
1029
+ const suite = await readSuite(ctx, agent);
1030
+ const current = suite.tests.find((row) => row.id === id);
1031
+ if (!current) return { status: "not-found" };
1032
+ if (current.conversation !== expectedConversation || allowedSourceFingerprints && !allowedSourceFingerprints.includes(testEditSourceFingerprint(current))) {
1033
+ return { status: "stale", currentConversation: current.conversation };
1034
+ }
1035
+ const updated = preparePatch(current, { conversation });
1036
+ await writeSuite(ctx, agent, {
1037
+ ...suite,
1038
+ tests: suite.tests.map((row) => row.id === id ? updated : row)
1039
+ });
1040
+ return { status: "changed", test: updated };
1041
+ });
1042
+ }
1002
1043
  function preparePatch(current, patch) {
1003
1044
  const updated = {
1004
1045
  ...defaultTestRow,
@@ -1029,7 +1070,7 @@ async function patchTestsBatch(ctx, agent, updates) {
1029
1070
  return updatedRows;
1030
1071
  });
1031
1072
  }
1032
- async function addTest(ctx, agent, seed) {
1073
+ async function addTest(ctx, agent, seed, options = {}) {
1033
1074
  return withSuiteMutationLock(agent, async () => {
1034
1075
  const suite = await loadSuiteOrEmpty(ctx, agent);
1035
1076
  const id = randomUUID2();
@@ -1037,17 +1078,37 @@ async function addTest(ctx, agent, seed) {
1037
1078
  (minimum, row) => Math.min(minimum, row.order),
1038
1079
  Number.POSITIVE_INFINITY
1039
1080
  );
1081
+ let existingTests = suite.tests;
1082
+ let order = Number.isFinite(firstOrder) ? firstOrder - 1024 : 0;
1083
+ if (options.beforeTestId) {
1084
+ const beforeTest = suite.tests.find((row) => row.id === options.beforeTestId);
1085
+ if (!beforeTest) throw new Error(`Test '${options.beforeTestId}' was not found`);
1086
+ const stepTests = suite.tests.filter((row) => row.step === beforeTest.step);
1087
+ const beforeIndex = stepTests.findIndex((row) => row.id === beforeTest.id);
1088
+ const previousTest = stepTests[beforeIndex - 1];
1089
+ const candidate = previousTest ? previousTest.order + (beforeTest.order - previousTest.order) / 2 : beforeTest.order - 1024;
1090
+ if (Number.isFinite(candidate) && candidate < beforeTest.order && (!previousTest || candidate > previousTest.order)) {
1091
+ order = candidate;
1092
+ } else {
1093
+ const rebalancedOrders = new Map(stepTests.map((row, index) => [row.id, index * 1024]));
1094
+ existingTests = suite.tests.map((row) => {
1095
+ const rebalancedOrder = rebalancedOrders.get(row.id);
1096
+ return typeof rebalancedOrder === "number" ? { ...row, order: rebalancedOrder } : row;
1097
+ });
1098
+ order = beforeIndex === 0 ? -1024 : beforeIndex * 1024 - 512;
1099
+ }
1100
+ }
1040
1101
  const newRow = {
1041
1102
  ...defaultTestRow,
1042
1103
  id,
1043
- order: Number.isFinite(firstOrder) ? firstOrder - 1024 : 0,
1044
1104
  ...seed,
1105
+ order,
1045
1106
  context: makeContext(seed?.context)
1046
1107
  };
1047
1108
  if (typeof seed?.mocks !== "undefined") {
1048
1109
  newRow.mocks = makeContext(seed.mocks);
1049
1110
  }
1050
- await writeSuite(ctx, agent, { tests: [newRow, ...suite.tests] });
1111
+ await writeSuite(ctx, agent, { tests: [newRow, ...existingTests] });
1051
1112
  return newRow;
1052
1113
  });
1053
1114
  }
@@ -1828,6 +1889,52 @@ function envIntOrDefault(key, defaultValue, opts) {
1828
1889
  return Math.max(opts.min, Math.min(opts.max, rounded));
1829
1890
  }
1830
1891
 
1892
+ // src/backend/roleplay/prompt-file.ts
1893
+ import fs3 from "node:fs";
1894
+ import path8 from "node:path";
1895
+ var promptMutationQueue = /* @__PURE__ */ new Map();
1896
+ async function resolveEditablePrompt(ctx, agentId, stepName) {
1897
+ const { basePath, steps } = await getAgentSteps(ctx, agentId);
1898
+ const targetStep = steps.find((step) => step?.name === stepName);
1899
+ if (!targetStep || targetStep?.prompt?.type !== "file") {
1900
+ throw new Error(`Step '${stepName}' does not have an editable file prompt`);
1901
+ }
1902
+ return {
1903
+ promptPath: path8.resolve(basePath, targetStep.prompt.path),
1904
+ readContent: () => targetStep.prompt.content()
1905
+ };
1906
+ }
1907
+ async function readEditablePrompt(ctx, agentId, stepName) {
1908
+ const { readContent } = await resolveEditablePrompt(ctx, agentId, stepName);
1909
+ return readContent();
1910
+ }
1911
+ async function withPromptFileLock(ctx, agentId, stepName, operation) {
1912
+ const { promptPath } = await resolveEditablePrompt(ctx, agentId, stepName);
1913
+ const canonicalPath = await fs3.promises.realpath(promptPath);
1914
+ const previous = promptMutationQueue.get(canonicalPath) ?? Promise.resolve();
1915
+ let release;
1916
+ const gate = new Promise((resolve) => {
1917
+ release = resolve;
1918
+ });
1919
+ const queued = previous.catch(() => {
1920
+ }).then(() => gate);
1921
+ promptMutationQueue.set(canonicalPath, queued);
1922
+ await previous.catch(() => {
1923
+ });
1924
+ try {
1925
+ return await operation({
1926
+ path: canonicalPath,
1927
+ read: () => fs3.promises.readFile(canonicalPath, "utf-8"),
1928
+ write: (prompt) => fs3.promises.writeFile(canonicalPath, prompt, "utf-8")
1929
+ });
1930
+ } finally {
1931
+ release();
1932
+ if (promptMutationQueue.get(canonicalPath) === queued) {
1933
+ promptMutationQueue.delete(canonicalPath);
1934
+ }
1935
+ }
1936
+ }
1937
+
1831
1938
  // src/backend/roleplay/variants.ts
1832
1939
  async function generateReplyVariants(ctx, props) {
1833
1940
  const { conversation, count } = props;
@@ -1874,12 +1981,24 @@ function createPromptEditProposal(proposal) {
1874
1981
  removeExpiredPromptEditProposals();
1875
1982
  const stored = { ...proposal, id: randomUUID3(), createdAt: Date.now() };
1876
1983
  proposals.set(stored.id, stored);
1877
- return { ...stored };
1984
+ return publicProposal(stored);
1878
1985
  }
1879
1986
  function getPromptEditProposal(id) {
1880
1987
  removeExpiredPromptEditProposals();
1881
1988
  const proposal = proposals.get(id);
1882
- return proposal ? { ...proposal } : null;
1989
+ return proposal ? publicProposal(proposal) : null;
1990
+ }
1991
+ function getPromptEditProposalForAcceptance(id) {
1992
+ removeExpiredPromptEditProposals();
1993
+ const proposal = proposals.get(id);
1994
+ return proposal ? {
1995
+ ...proposal,
1996
+ testSourceGuard: proposal.testSourceGuard ? {
1997
+ suiteKey: proposal.testSourceGuard.suiteKey,
1998
+ testId: proposal.testSourceGuard.testId,
1999
+ allowedFingerprints: [...proposal.testSourceGuard.allowedFingerprints]
2000
+ } : void 0
2001
+ } : null;
1883
2002
  }
1884
2003
  function deletePromptEditProposal(id) {
1885
2004
  return proposals.delete(id);
@@ -1889,8 +2008,12 @@ function removeExpiredPromptEditProposals(now = Date.now()) {
1889
2008
  if (proposal.createdAt + PROPOSAL_TTL_MS <= now) proposals.delete(id);
1890
2009
  }
1891
2010
  }
2011
+ function publicProposal(proposal) {
2012
+ const { testSourceGuard: _testSourceGuard, ...result } = proposal;
2013
+ return { ...result };
2014
+ }
1892
2015
 
1893
- // src/backend/roleplay/suggest-prompt-edit.ts
2016
+ // src/backend/roleplay/suggest-file-edit.ts
1894
2017
  import "openai";
1895
2018
 
1896
2019
  // src/backend/lib/apply-v4a-diff.ts
@@ -2026,13 +2149,12 @@ function applyChunks(input, chunks) {
2026
2149
  return output.join("\n");
2027
2150
  }
2028
2151
 
2029
- // src/backend/roleplay/suggest-prompt-edit.ts
2152
+ // src/backend/roleplay/suggest-file-edit.ts
2030
2153
  var MODEL = "gpt-5.4";
2031
- var VIRTUAL_PROMPT_PATH = "prompt.md";
2032
2154
  var MAX_PATCH_ROUNDS = 8;
2033
- async function suggestPromptEdit(ctx, input) {
2155
+ async function suggestFileEdit(ctx, input) {
2034
2156
  const client = ctx.openai;
2035
- let virtualPrompt = input.originalPrompt;
2157
+ let virtualContent = input.originalContent;
2036
2158
  let activity = 0;
2037
2159
  let modelRound = 0;
2038
2160
  let reasoningSummary = "";
@@ -2048,22 +2170,23 @@ async function suggestPromptEdit(ctx, input) {
2048
2170
  });
2049
2171
  };
2050
2172
  let response = await createResponse(client, {
2051
- input: buildPrompt(input.request, input.originalPrompt),
2173
+ input: input.request,
2174
+ instructions: input.instructions,
2052
2175
  onEvent: handleEvent
2053
2176
  });
2054
2177
  for (let patchRound = 0; patchRound < MAX_PATCH_ROUNDS; patchRound += 1) {
2055
2178
  const patchCalls = response.output.filter(
2056
2179
  (item) => item?.type === "apply_patch_call"
2057
2180
  );
2058
- if (!patchCalls.length) return virtualPrompt;
2181
+ if (!patchCalls.length) return virtualContent;
2059
2182
  const outputs = patchCalls.map((call) => {
2060
2183
  try {
2061
- virtualPrompt = applyPromptOperation(virtualPrompt, call.operation);
2184
+ virtualContent = applyFileOperation(virtualContent, input.virtualPath, call.operation);
2062
2185
  return {
2063
2186
  type: "apply_patch_call_output",
2064
2187
  call_id: call.call_id,
2065
2188
  status: "completed",
2066
- output: `Updated ${VIRTUAL_PROMPT_PATH} in memory`
2189
+ output: `Updated ${input.virtualPath} in memory`
2067
2190
  };
2068
2191
  } catch (error) {
2069
2192
  return {
@@ -2080,29 +2203,20 @@ async function suggestPromptEdit(ctx, input) {
2080
2203
  response = await createResponse(client, {
2081
2204
  previousResponseId: response.id,
2082
2205
  input: outputs,
2206
+ instructions: input.instructions,
2083
2207
  onEvent: handleEvent
2084
2208
  });
2085
2209
  }
2086
- throw new Error("The model did not finish editing the prompt");
2087
- }
2088
- function buildPrompt(request, originalPrompt) {
2089
- return `The user is asking you to modify an LLM prompt given below. Try and make minimal changes. You are not allowed to change the shape of variables, their names or their set - this is set outside. Note that this is a Nunjucks template and it will be rendered according to context. You can change/append the template logic if necessary.
2090
-
2091
- This is their request:
2092
- ${request}
2093
-
2094
- This is the original prompt:
2095
- ${originalPrompt}
2096
- `;
2210
+ throw new Error(`The model did not finish editing ${input.virtualPath}`);
2097
2211
  }
2098
2212
  async function createResponse(client, input) {
2099
2213
  const stream = await client.responses.create({
2100
2214
  model: MODEL,
2101
- instructions: "The original prompt is the existing virtual file prompt.md. Use the Apply Patch tool to make the requested edit to that file. Do not create, delete, or modify any other file.",
2215
+ instructions: input.instructions,
2102
2216
  input: input.input,
2103
2217
  ...input.previousResponseId ? { previous_response_id: input.previousResponseId } : {},
2104
2218
  tools: [{ type: "apply_patch" }],
2105
- reasoning: { effort: "xhigh", summary: "auto" },
2219
+ reasoning: { effort: "high", summary: "auto" },
2106
2220
  text: { verbosity: "low" },
2107
2221
  stream: true
2108
2222
  });
@@ -2111,14 +2225,14 @@ async function createResponse(client, input) {
2111
2225
  input.onEvent(event);
2112
2226
  if (event?.type === "response.completed") completedResponse = event.response;
2113
2227
  if (event?.type === "response.failed") {
2114
- throw new Error(event.response?.error?.message || "OpenAI failed to suggest prompt edits");
2228
+ throw new Error(event.response?.error?.message || "OpenAI failed to suggest edits");
2115
2229
  }
2116
2230
  if (event?.type === "response.incomplete") {
2117
- throw new Error("OpenAI returned an incomplete prompt edit");
2231
+ throw new Error("OpenAI returned an incomplete edit");
2118
2232
  }
2119
2233
  if (event?.type === "error") throw new Error(event.message || "OpenAI streaming failed");
2120
2234
  }
2121
- if (!completedResponse) throw new Error("OpenAI stream ended before the prompt edit completed");
2235
+ if (!completedResponse) throw new Error("OpenAI stream ended before the edit completed");
2122
2236
  return completedResponse;
2123
2237
  }
2124
2238
  function progressPhase(event, round) {
@@ -2127,18 +2241,140 @@ function progressPhase(event, round) {
2127
2241
  if (eventType.includes("apply_patch") || itemType === "apply_patch_call") return "drafting";
2128
2242
  return round > 0 ? "reviewing" : "thinking";
2129
2243
  }
2130
- function applyPromptOperation(currentPrompt, operation) {
2244
+ function applyFileOperation(currentContent, virtualPath, operation) {
2131
2245
  const normalizedPath = String(operation.path || "").replace(/\\/g, "/").replace(/^\.\//, "").replace(/^\//, "");
2132
- if (normalizedPath !== VIRTUAL_PROMPT_PATH) {
2133
- throw new Error(`Only ${VIRTUAL_PROMPT_PATH} may be edited`);
2246
+ if (normalizedPath !== virtualPath) {
2247
+ throw new Error(`Only ${virtualPath} may be edited`);
2134
2248
  }
2135
2249
  if (operation.type !== "update_file") {
2136
- throw new Error(`${VIRTUAL_PROMPT_PATH} already exists and may only be updated`);
2250
+ throw new Error(`${virtualPath} already exists and may only be updated`);
2137
2251
  }
2138
2252
  if (typeof operation.diff !== "string" || !operation.diff.trim()) {
2139
2253
  throw new Error("Apply Patch returned an empty diff");
2140
2254
  }
2141
- return applyV4ADiff(currentPrompt, operation.diff);
2255
+ return applyV4ADiff(currentContent, operation.diff);
2256
+ }
2257
+
2258
+ // src/backend/roleplay/suggest-prompt-edit.ts
2259
+ var VIRTUAL_PROMPT_PATH = "prompt.md";
2260
+ async function suggestPromptEdit(ctx, input) {
2261
+ return suggestFileEdit(ctx, {
2262
+ request: buildPrompt(input.request, input.originalPrompt),
2263
+ originalContent: input.originalPrompt,
2264
+ virtualPath: VIRTUAL_PROMPT_PATH,
2265
+ instructions: "The original prompt is the existing virtual file prompt.md. Use the Apply Patch tool to make the requested edit to that file. Do not create, delete, or modify any other file.",
2266
+ onProgress: input.onProgress
2267
+ });
2268
+ }
2269
+ function buildPrompt(request, originalPrompt) {
2270
+ return `The user is asking you to modify an LLM prompt given below. Try and make minimal changes. You are not allowed to change the shape of variables, their names or their set - this is set outside. Note that this is a Nunjucks template and it will be rendered according to context. You can change/append the template logic if necessary.
2271
+
2272
+ This is their request:
2273
+ ${request}
2274
+
2275
+ This is the original prompt:
2276
+ ${originalPrompt}
2277
+ `;
2278
+ }
2279
+
2280
+ // src/lib/edit/test-ai-edit.ts
2281
+ function formatTestFixAssertion(label, value) {
2282
+ const normalizedLabel = label.trim() === "LLM" ? "" : label.trim();
2283
+ return [normalizedLabel, value.trim()].filter(Boolean).join(" ");
2284
+ }
2285
+ function buildTestFixRequest(input) {
2286
+ const assertion = formatTestFixAssertion(input.assertionLabel, input.assertionValue);
2287
+ return `You will be given the context, the conversation and the desired output and assertion about it. First, find why the prompt is not producing the correct result. It's usually one concrete instruction within the relevant section, or a contradiction. Then, change the prompt minimally so this will work, but keeping the prompt clean and comprehensive is more important. Try to provide as general solution as possible. Try to change just one part of the prompt, not multiple parts. Avoid adding exceptions and overrides. Avoid duplicating information that already exists in the prompt.
2288
+
2289
+ Context:
2290
+ ${input.context.trim()}
2291
+
2292
+ Conversation:
2293
+ ${input.conversation.trim()}
2294
+
2295
+ Desired outcome:
2296
+ ${input.expectedOutput.trim()}
2297
+
2298
+ Assertion about outcome:
2299
+ ${assertion}
2300
+
2301
+ Current incorrect outcome:
2302
+ ${input.currentIncorrectOutcome.trim()}`;
2303
+ }
2304
+ function buildPromptFixRequest(enteredRequest, input, originalConversation) {
2305
+ const canonicalOriginal = buildTestFixRequest({ ...input, conversation: originalConversation });
2306
+ const canonicalCorrected = buildTestFixRequest(input);
2307
+ if (enteredRequest.trim() === canonicalOriginal.trim()) return canonicalCorrected;
2308
+ return `${enteredRequest.trim()}
2309
+
2310
+ The conversation history has now been checked and may have been corrected. Use the following test case as authoritative instead of any earlier version included above:
2311
+
2312
+ ${canonicalCorrected}`;
2313
+ }
2314
+
2315
+ // src/backend/tests/suggest-test-history-edit.ts
2316
+ var VIRTUAL_HISTORY_PATH = "history.md";
2317
+ async function suggestTestHistoryEdit(ctx, input) {
2318
+ return suggestFileEdit(ctx, {
2319
+ request: buildTestHistoryPrompt(input),
2320
+ originalContent: input.conversation,
2321
+ virtualPath: VIRTUAL_HISTORY_PATH,
2322
+ instructions: "The conversation history is the existing virtual file history.md. Decide whether it conflicts with the current LLM prompt or contains agent behaviour that the current prompt would not produce. If it does, use the Apply Patch tool to update history.md minimally. Otherwise, do not call Apply Patch. Preserve the test intent and never add or change the expected outcome. Do not create, delete, or modify any other file.",
2323
+ onProgress: input.onProgress
2324
+ });
2325
+ }
2326
+ function buildTestHistoryPrompt(input) {
2327
+ return `The user wants you to update an LLM test's conversation history when needed. The history must reflect behaviour that is valid under the current prompt. Make the smallest useful change. The expected outcome is immutable and is provided only so you can preserve the test's intent.
2328
+
2329
+ This is the user's request:
2330
+ ${input.request}
2331
+
2332
+ This is the current LLM prompt:
2333
+ ${input.prompt}
2334
+
2335
+ This is the test context:
2336
+ ${input.context}
2337
+
2338
+ This is the conversation history in history.md:
2339
+ ${input.conversation}
2340
+
2341
+ This is the immutable expected outcome:
2342
+ ${input.expectedOutput}
2343
+ `;
2344
+ }
2345
+
2346
+ // src/backend/roleplay/suggest-test-fix.ts
2347
+ var defaultDependencies = {
2348
+ suggestHistory: suggestTestHistoryEdit,
2349
+ suggestPrompt: suggestPromptEdit
2350
+ };
2351
+ async function suggestTestFix(ctx, input, dependencies = defaultDependencies) {
2352
+ const suggestedConversation = await dependencies.suggestHistory(ctx, {
2353
+ request: input.request,
2354
+ prompt: input.originalPrompt,
2355
+ context: input.context,
2356
+ conversation: input.conversation,
2357
+ expectedOutput: input.expectedOutput,
2358
+ onProgress: input.onProgress
2359
+ });
2360
+ const promptRequest = buildPromptFixRequest(
2361
+ input.request,
2362
+ {
2363
+ context: input.context,
2364
+ conversation: suggestedConversation,
2365
+ expectedOutput: input.expectedOutput,
2366
+ assertionLabel: input.assertionLabel,
2367
+ assertionValue: input.assertionValue,
2368
+ currentIncorrectOutcome: input.currentIncorrectOutcome
2369
+ },
2370
+ input.conversation
2371
+ );
2372
+ const suggestedPrompt = await dependencies.suggestPrompt(ctx, {
2373
+ request: promptRequest,
2374
+ originalPrompt: input.originalPrompt,
2375
+ onProgress: input.onProgress
2376
+ });
2377
+ return { suggestedConversation, suggestedPrompt };
2142
2378
  }
2143
2379
 
2144
2380
  // src/backend/roleplay/prompt-edit-progress.ts
@@ -2159,6 +2395,107 @@ function removeExpiredProgress(now = Date.now()) {
2159
2395
  }
2160
2396
  }
2161
2397
 
2398
+ // src/backend/tests/test-history-edit-proposals.ts
2399
+ import { randomUUID as randomUUID4 } from "node:crypto";
2400
+ var proposals2 = /* @__PURE__ */ new Map();
2401
+ var PROPOSAL_TTL_MS2 = 12 * 60 * 60 * 1e3;
2402
+ function createTestHistoryEditProposal(proposal) {
2403
+ removeExpiredTestHistoryEditProposals();
2404
+ const stored = { ...proposal, id: randomUUID4(), createdAt: Date.now() };
2405
+ proposals2.set(stored.id, stored);
2406
+ return publicProposal2(stored);
2407
+ }
2408
+ function getTestHistoryEditProposalForAcceptance(id) {
2409
+ removeExpiredTestHistoryEditProposals();
2410
+ const proposal = proposals2.get(id);
2411
+ return proposal ? {
2412
+ ...proposal,
2413
+ sourceGuard: {
2414
+ suiteKey: proposal.sourceGuard.suiteKey,
2415
+ allowedTestFingerprints: [...proposal.sourceGuard.allowedTestFingerprints],
2416
+ allowedPromptContents: [...proposal.sourceGuard.allowedPromptContents]
2417
+ }
2418
+ } : null;
2419
+ }
2420
+ function deleteTestHistoryEditProposal(id) {
2421
+ return proposals2.delete(id);
2422
+ }
2423
+ function removeExpiredTestHistoryEditProposals(now = Date.now()) {
2424
+ for (const [id, proposal] of proposals2) {
2425
+ if (proposal.createdAt + PROPOSAL_TTL_MS2 <= now) proposals2.delete(id);
2426
+ }
2427
+ }
2428
+ function publicProposal2(proposal) {
2429
+ const { sourceGuard: _sourceGuard, ...result } = proposal;
2430
+ return { ...result };
2431
+ }
2432
+
2433
+ // src/lib/tests/assertion-types.ts
2434
+ var baseAssertionTypes = [
2435
+ { value: "llm-rubric", label: "LLM" },
2436
+ { value: "answer-relevance", label: "Answer Relevance (LLM)" },
2437
+ { value: "context-faithfulness", label: "Context Faithfulness (LLM)" },
2438
+ { value: "context-recall", label: "Context Recall (LLM)" },
2439
+ { value: "context-relevance", label: "Context Relevance (LLM)" },
2440
+ { value: "model-graded-closedqa", label: "Model-Graded Closed QA" },
2441
+ { value: "model-graded-factuality", label: "Model-Graded Factuality" },
2442
+ { value: "moderation", label: "Moderation" },
2443
+ { value: "classifier", label: "Classifier" },
2444
+ { value: "g-eval", label: "G-Eval" },
2445
+ { value: "factuality", label: "Factuality" },
2446
+ { value: "conversation-relevance", label: "Conversation Relevance" },
2447
+ { value: "equals", label: "Equals" },
2448
+ { value: "contains", label: "Contains" },
2449
+ { value: "icontains", label: "Contains (case-insensitive)" },
2450
+ { value: "contains-all", label: "Contains All" },
2451
+ { value: "contains-any", label: "Contains Any" },
2452
+ { value: "icontains-all", label: "Contains All (iCase)" },
2453
+ { value: "icontains-any", label: "Contains Any (iCase)" },
2454
+ { value: "starts-with", label: "Starts With" },
2455
+ { value: "regex", label: "Regex" },
2456
+ { value: "levenshtein", label: "Levenshtein Distance" },
2457
+ { value: "is-refusal", label: "Is Refusal" },
2458
+ { value: "is-html", label: "Is HTML" },
2459
+ { value: "contains-html", label: "Contains HTML" },
2460
+ { value: "is-json", label: "Is JSON" },
2461
+ { value: "contains-json", label: "Contains JSON" },
2462
+ { value: "is-xml", label: "Is XML" },
2463
+ { value: "contains-xml", label: "Contains XML" },
2464
+ { value: "is-sql", label: "Is SQL" },
2465
+ { value: "contains-sql", label: "Contains SQL" },
2466
+ { value: "similar", label: "Semantic Similarity" },
2467
+ { value: "rouge-n", label: "ROUGE-N" },
2468
+ { value: "bleu", label: "BLEU" },
2469
+ { value: "gleu", label: "GLEU" },
2470
+ { value: "meteor", label: "METEOR" },
2471
+ { value: "perplexity", label: "Perplexity" },
2472
+ { value: "perplexity-score", label: "Perplexity Score" },
2473
+ { value: "latency", label: "Latency" },
2474
+ { value: "cost", label: "Cost" },
2475
+ { value: "finish-reason", label: "Finish Reason" },
2476
+ { value: "trace-error-spans", label: "Trace Error Spans" },
2477
+ { value: "trace-span-count", label: "Trace Span Count" },
2478
+ { value: "trace-span-duration", label: "Trace Span Duration" },
2479
+ { value: "is-valid-function-call", label: "Is Valid Function Call" },
2480
+ { value: "is-valid-openai-function-call", label: "Is Valid OpenAI Function Call" },
2481
+ { value: "is-valid-openai-tools-call", label: "Is Valid OpenAI Tools Call" },
2482
+ { value: "guardrails", label: "Guardrails" },
2483
+ { value: "javascript", label: "JavaScript (custom)" },
2484
+ { value: "python", label: "Python (custom)" },
2485
+ { value: "webhook", label: "Webhook (custom)" },
2486
+ { value: "pi", label: "Pi Scorer" }
2487
+ ];
2488
+ var assertionTypes = [
2489
+ ...baseAssertionTypes,
2490
+ ...baseAssertionTypes.map((option) => ({
2491
+ value: `not-${option.value}`,
2492
+ label: `Not ${option.label}`
2493
+ }))
2494
+ ];
2495
+ function assertionLabel(value) {
2496
+ return assertionTypes.find((option) => option.value === value)?.label ?? value;
2497
+ }
2498
+
2162
2499
  // src/backend/roleplay/router.ts
2163
2500
  var AgentIdSchema = z4.string().trim().min(1).max(512);
2164
2501
  var ConversationIdSchema = z4.string().trim().min(1).max(64);
@@ -2232,7 +2569,7 @@ function createRolePlayRouter(t2) {
2232
2569
  prompt: z4.string().max(1e6)
2233
2570
  })
2234
2571
  ).mutation(async ({ input, ctx }) => {
2235
- await persistPromptUpdate(ctx, input.agentId, input.step, input.prompt);
2572
+ await withPromptFileLock(ctx, input.agentId, input.step, (file) => file.write(input.prompt));
2236
2573
  return { ok: true };
2237
2574
  }),
2238
2575
  suggestPromptEdit: t2.procedure.input(
@@ -2258,12 +2595,109 @@ function createRolePlayRouter(t2) {
2258
2595
  });
2259
2596
  return { status: "changed", proposal };
2260
2597
  }),
2598
+ suggestTestFix: t2.procedure.input(
2599
+ z4.object({
2600
+ agentId: AgentIdSchema,
2601
+ testId: z4.string(),
2602
+ step: z4.string().trim().min(1).max(512),
2603
+ request: z4.string().trim().min(1).max(1e5),
2604
+ prompt: z4.string().max(1e6),
2605
+ basePrompt: z4.string().max(1e6),
2606
+ currentIncorrectOutcome: z4.string().max(1e6),
2607
+ progressId: z4.string().uuid()
2608
+ })
2609
+ ).mutation(async ({ input, ctx }) => {
2610
+ const suiteKey = input.agentId;
2611
+ const test = (await readSuite(ctx, suiteKey)).tests.find((row) => row.id === input.testId);
2612
+ if (!test) throw new Error("This test is no longer available.");
2613
+ await withPromptFileLock(ctx, input.agentId, input.step, async (file) => {
2614
+ const currentPrompt = await file.read();
2615
+ if (currentPrompt !== input.basePrompt && currentPrompt !== input.prompt) {
2616
+ throw new Error(
2617
+ "The prompt changed before Bosun could start this Fix. Check the prompt, then try again."
2618
+ );
2619
+ }
2620
+ if (currentPrompt !== input.prompt) await file.write(input.prompt);
2621
+ });
2622
+ const originalPrompt = input.prompt;
2623
+ const context = JSON.stringify(test.context ?? {});
2624
+ const assertionLabel2 = test.evaluation_mode === "json-deep-equal" ? "" : assertionLabel(test.assertion_type);
2625
+ const assertionValue = test.evaluation_mode === "json-deep-equal" ? "" : test.assertion_value;
2626
+ const suggestion = await suggestTestFix(ctx, {
2627
+ request: input.request,
2628
+ originalPrompt,
2629
+ context,
2630
+ conversation: test.conversation,
2631
+ expectedOutput: test.expected_output,
2632
+ assertionLabel: assertionLabel2,
2633
+ assertionValue,
2634
+ currentIncorrectOutcome: input.currentIncorrectOutcome,
2635
+ onProgress: (progress) => updatePromptEditProgress(input.progressId, progress)
2636
+ });
2637
+ const originalTestFingerprint = testEditSourceFingerprint(test);
2638
+ const historyTestFingerprint = testEditSourceFingerprint(test, {
2639
+ conversation: suggestion.suggestedConversation
2640
+ });
2641
+ const history = suggestion.suggestedConversation === test.conversation ? { status: "unchanged" } : {
2642
+ status: "changed",
2643
+ proposal: createTestHistoryEditProposal({
2644
+ agentId: input.agentId,
2645
+ testId: input.testId,
2646
+ step: input.step,
2647
+ originalConversation: test.conversation,
2648
+ suggestedConversation: suggestion.suggestedConversation,
2649
+ expectedOutput: test.expected_output,
2650
+ sourceGuard: {
2651
+ suiteKey,
2652
+ allowedTestFingerprints: [originalTestFingerprint],
2653
+ allowedPromptContents: [originalPrompt, suggestion.suggestedPrompt]
2654
+ }
2655
+ })
2656
+ };
2657
+ const prompt = suggestion.suggestedPrompt === originalPrompt ? { status: "unchanged" } : {
2658
+ status: "changed",
2659
+ proposal: createPromptEditProposal({
2660
+ agentId: input.agentId,
2661
+ step: input.step,
2662
+ originalPrompt,
2663
+ suggestedPrompt: suggestion.suggestedPrompt,
2664
+ testSourceGuard: {
2665
+ suiteKey,
2666
+ testId: input.testId,
2667
+ allowedFingerprints: [originalTestFingerprint, historyTestFingerprint]
2668
+ }
2669
+ })
2670
+ };
2671
+ return { history, prompt };
2672
+ }),
2261
2673
  promptEditProgress: t2.procedure.input(z4.object({ id: z4.string().uuid() })).query(({ input }) => getPromptEditProgress(input.id)),
2262
2674
  promptEditProposal: t2.procedure.input(z4.object({ id: z4.string().uuid() })).query(({ input }) => getPromptEditProposal(input.id)),
2263
2675
  acceptPromptEdit: write.input(z4.object({ id: z4.string().uuid() })).mutation(async ({ input, ctx }) => {
2264
- const proposal = getPromptEditProposal(input.id);
2676
+ const proposal = getPromptEditProposalForAcceptance(input.id);
2265
2677
  if (!proposal) throw new Error("This prompt suggestion is no longer available");
2266
- await persistPromptUpdate(ctx, proposal.agentId, proposal.step, proposal.suggestedPrompt);
2678
+ await withPromptFileLock(ctx, proposal.agentId, proposal.step, async (file) => {
2679
+ if (await file.read() !== proposal.originalPrompt) {
2680
+ throw new Error(
2681
+ "The prompt changed while this suggestion was open. Reject this suggestion, then ask Bosun to create a new one."
2682
+ );
2683
+ }
2684
+ const guard = proposal.testSourceGuard;
2685
+ if (!guard) {
2686
+ await file.write(proposal.suggestedPrompt);
2687
+ return;
2688
+ }
2689
+ await withSuiteMutationLock(guard.suiteKey, async () => {
2690
+ const test = (await readSuite(ctx, guard.suiteKey)).tests.find(
2691
+ (row) => row.id === guard.testId
2692
+ );
2693
+ if (!test || !guard.allowedFingerprints.includes(testEditSourceFingerprint(test))) {
2694
+ throw new Error(
2695
+ "This test changed while the suggestion was open. Reject this suggestion, then ask Bosun to create a new one."
2696
+ );
2697
+ }
2698
+ await file.write(proposal.suggestedPrompt);
2699
+ });
2700
+ });
2267
2701
  deletePromptEditProposal(proposal.id);
2268
2702
  return { ok: true, prompt: proposal.suggestedPrompt };
2269
2703
  }),
@@ -2309,12 +2743,7 @@ function createRolePlayRouter(t2) {
2309
2743
  promptOverrides: input.promptOverrides ?? {}
2310
2744
  });
2311
2745
  }),
2312
- commitVariant: write.input(
2313
- z4.object({
2314
- conversationId: ConversationIdSchema,
2315
- entries: z4.array(z4.any())
2316
- })
2317
- ).mutation(async ({ input, ctx }) => {
2746
+ commitVariant: write.input(z4.object({ conversationId: ConversationIdSchema, entries: z4.array(z4.any()) })).mutation(async ({ input, ctx }) => {
2318
2747
  const conversation = await loadConversation(ctx, input.conversationId);
2319
2748
  const turn = lastTurn(conversation);
2320
2749
  if (!turn) throw new Error("This conversation has no reply to replace");
@@ -2333,21 +2762,6 @@ function createRolePlayRouter(t2) {
2333
2762
  })
2334
2763
  });
2335
2764
  }
2336
- async function persistPromptUpdate(ctx, agentId, stepName, prompt) {
2337
- const { basePath, steps } = await getAgentSteps(ctx, agentId);
2338
- const targetStep = steps.find((step) => step?.name === stepName);
2339
- if (!targetStep || targetStep?.prompt?.type !== "file") return;
2340
- const promptPath = path8.resolve(basePath, targetStep.prompt.path);
2341
- await fs3.promises.writeFile(promptPath, prompt, "utf-8");
2342
- }
2343
- async function readEditablePrompt(ctx, agentId, stepName) {
2344
- const { steps } = await getAgentSteps(ctx, agentId);
2345
- const targetStep = steps.find((step) => step?.name === stepName);
2346
- if (!targetStep || targetStep?.prompt?.type !== "file") {
2347
- throw new Error(`Step '${stepName}' does not have an editable file prompt`);
2348
- }
2349
- return targetStep.prompt.content();
2350
- }
2351
2765
 
2352
2766
  // src/backend/roleplay/conversations-router.ts
2353
2767
  import { z as z5 } from "zod";
@@ -2405,7 +2819,7 @@ import "@recombine-ai/telescope";
2405
2819
 
2406
2820
  // src/backend/prompt-cache.ts
2407
2821
  import { AsyncLocalStorage } from "node:async_hooks";
2408
- import { randomUUID as randomUUID4 } from "node:crypto";
2822
+ import { randomUUID as randomUUID5 } from "node:crypto";
2409
2823
  var cacheBustContext = new AsyncLocalStorage();
2410
2824
  function withTestPromptCacheBust(run) {
2411
2825
  return cacheBustContext.run(true, run);
@@ -2423,7 +2837,7 @@ function wrapCreate(resource) {
2423
2837
  return Reflect.apply(originalCreate, this, [body, ...args]);
2424
2838
  }
2425
2839
  return Reflect.apply(originalCreate, this, [
2426
- { ...body, prompt_cache_key: `bosun-test-${randomUUID4()}` },
2840
+ { ...body, prompt_cache_key: `bosun-test-${randomUUID5()}` },
2427
2841
  ...args
2428
2842
  ]);
2429
2843
  };
@@ -2774,6 +3188,12 @@ function gradeJsonExpectedFields(output, expectedOutput, caseSensitive = false)
2774
3188
  }
2775
3189
 
2776
3190
  // src/backend/tests/academy.ts
3191
+ function applyTestHistoryOverrides(rows, overrides) {
3192
+ return rows.map((row) => {
3193
+ const override = overrides[typeof row.id === "string" ? row.id : ""];
3194
+ return override ? { ...row, conversation: override.conversation } : row;
3195
+ });
3196
+ }
2777
3197
  var activeRuns = new ActiveTestRuns();
2778
3198
  function getPromptTestsProgress(suiteKey) {
2779
3199
  const progress = activeRuns.getProgress(suiteKey);
@@ -2787,13 +3207,14 @@ function getPromptTestsProgress(suiteKey) {
2787
3207
  function cancelPromptTests(suiteKey) {
2788
3208
  return activeRuns.cancel(suiteKey);
2789
3209
  }
2790
- async function runPromptTestsManaged(ctx, suite, selection, requestedConfig, promptOverrides = {}) {
3210
+ async function runPromptTestsManaged(ctx, suite, selection, requestedConfig, promptOverrides = {}, testHistoryOverrides = {}) {
2791
3211
  const run = activeRuns.start(suite.suiteKey);
2792
3212
  if (!run) throw new Error("Tests already running for this agent");
2793
3213
  try {
2794
3214
  return await runPromptTests(ctx, suite, selection, requestedConfig, {
2795
3215
  signal: run.controller.signal,
2796
3216
  promptOverrides,
3217
+ testHistoryOverrides,
2797
3218
  setProgress: (progress) => activeRuns.setProgress(suite.suiteKey, run, progress)
2798
3219
  });
2799
3220
  } finally {
@@ -2863,12 +3284,7 @@ function toTestInputs(rows, selection = { scope: "pinned" }) {
2863
3284
  type: "javascript",
2864
3285
  value: (output) => gradeJsonExpectedFields(output, row.expected_output, row.case_sensitive === true)
2865
3286
  }
2866
- ] : [
2867
- {
2868
- type: row.assertion_type,
2869
- value: row.assertion_value
2870
- }
2871
- ];
3287
+ ] : [{ type: row.assertion_type, value: row.assertion_value }];
2872
3288
  return {
2873
3289
  id: row.id,
2874
3290
  scenario: row.scenario,
@@ -3118,7 +3534,7 @@ async function runPromptTests(ctx, suite, selection, requestedConfig, opts) {
3118
3534
  const onlyTestId = "testId" in selection ? selection.testId : void 0;
3119
3535
  const suiteKey = suite.suiteKey;
3120
3536
  const loadedSuite = await readSuite(ctx, suiteKey);
3121
- const rows = loadedSuite.tests;
3537
+ const rows = applyTestHistoryOverrides(loadedSuite.tests, opts?.testHistoryOverrides ?? {});
3122
3538
  const repeats = Math.min(500, Math.max(1, Math.floor(Number(requestedConfig.repeats) || 1)));
3123
3539
  const concurrency = Math.min(
3124
3540
  75,
@@ -3140,9 +3556,7 @@ async function runPromptTests(ctx, suite, selection, requestedConfig, opts) {
3140
3556
  completedRunsByTestId: /* @__PURE__ */ new Map()
3141
3557
  };
3142
3558
  if (opts?.setProgress && !opts.setProgress(progress)) throw new Error("Operation cancelled");
3143
- const providerInstance = new RecombineAgentProvider(ctx, {
3144
- id: "recombine-agent"
3145
- });
3559
+ const providerInstance = new RecombineAgentProvider(ctx, { id: "recombine-agent" });
3146
3560
  providerInstance.setAbortSignal(signal);
3147
3561
  providerInstance.setOnTestStarted((testId) => {
3148
3562
  if (progress.completedTestIds.includes(testId)) return;
@@ -3207,10 +3621,7 @@ async function runPromptTests(ctx, suite, selection, requestedConfig, opts) {
3207
3621
  await rename3(tmpPath, outPath);
3208
3622
  }
3209
3623
  }
3210
- const runConfig = {
3211
- repeats,
3212
- concurrency
3213
- };
3624
+ const runConfig = { repeats, concurrency };
3214
3625
  const body = {
3215
3626
  agent: suiteKey,
3216
3627
  run_timestamp: iso,
@@ -3571,7 +3982,7 @@ var PIIFindingSchema = z7.object({
3571
3982
  }).strict();
3572
3983
 
3573
3984
  // src/backend/tests/pii-sanitized-test-drafts.ts
3574
- import { randomUUID as randomUUID5 } from "node:crypto";
3985
+ import { randomUUID as randomUUID6 } from "node:crypto";
3575
3986
 
3576
3987
  // src/backend/tests/pii-sanitizer.ts
3577
3988
  import { z as z8 } from "zod";
@@ -3795,7 +4206,7 @@ async function createPIISanitizedTestDraft(ctx, input, dependencies = {}) {
3795
4206
  pruneExpiredDrafts(dependencies.now?.() ?? Date.now());
3796
4207
  const seed = TraceTestSeedSchema.parse(input.seed);
3797
4208
  const sanitized = await (dependencies.sanitize ?? ((s) => sanitizeTraceTestSeed(ctx, s)))(seed);
3798
- const draftId = randomUUID5();
4209
+ const draftId = randomUUID6();
3799
4210
  const expiresAt = (dependencies.now?.() ?? Date.now()) + DRAFT_TTL_MS;
3800
4211
  drafts.set(draftId, {
3801
4212
  suiteKey: input.suiteKey,
@@ -3863,12 +4274,7 @@ function createTestsRouter(t2) {
3863
4274
  latestResult: t2.procedure.input(z9.object({ agent: z9.string() })).query(async ({ input, ctx }) => loadLatestResultsByTest(ctx, input.agent)),
3864
4275
  runProgress: t2.procedure.input(z9.object({ agent: z9.string() })).query(async ({ input }) => getPromptTestsProgress(input.agent)),
3865
4276
  historyList: t2.procedure.input(z9.object({ agent: z9.string() })).query(async ({ input, ctx }) => listRunHistory(ctx, input.agent)),
3866
- historyRun: t2.procedure.input(
3867
- z9.object({
3868
- agent: z9.string(),
3869
- timestamp: z9.string()
3870
- })
3871
- ).query(async ({ input, ctx }) => getRunHistory(ctx, input.agent, input.timestamp)),
4277
+ historyRun: t2.procedure.input(z9.object({ agent: z9.string(), timestamp: z9.string() })).query(async ({ input, ctx }) => getRunHistory(ctx, input.agent, input.timestamp)),
3872
4278
  resultEntry: t2.procedure.input(
3873
4279
  z9.object({
3874
4280
  agent: z9.string(),
@@ -3903,28 +4309,59 @@ function createTestsRouter(t2) {
3903
4309
  timestamp: input.timestamp
3904
4310
  });
3905
4311
  }),
3906
- findPromptIssues: t2.procedure.input(
4312
+ findPromptIssues: t2.procedure.input(z9.object({ agent: z9.string(), step: z9.string(), prompt: z9.string() })).mutation(async ({ input, ctx }) => {
4313
+ return findPromptIssues(ctx, input);
4314
+ }),
4315
+ suggestHistoryEdit: t2.procedure.input(
3907
4316
  z9.object({
3908
4317
  agent: z9.string(),
3909
- step: z9.string(),
3910
- prompt: z9.string()
4318
+ testId: z9.string(),
4319
+ step: z9.string().trim().min(1).max(512),
4320
+ request: z9.string().trim().min(1).max(1e5),
4321
+ prompt: z9.string().max(1e6),
4322
+ basePrompt: z9.string().max(1e6),
4323
+ progressId: z9.string().uuid()
3911
4324
  })
3912
4325
  ).mutation(async ({ input, ctx }) => {
3913
- return findPromptIssues(ctx, input);
4326
+ const suiteKey = input.agent;
4327
+ const test = (await readSuite(ctx, suiteKey)).tests.find((row) => row.id === input.testId);
4328
+ if (!test) throw new Error("This test is no longer available.");
4329
+ const suggestedConversation = await suggestTestHistoryEdit(ctx, {
4330
+ request: input.request,
4331
+ prompt: input.prompt,
4332
+ context: JSON.stringify(test.context ?? {}, null, 2),
4333
+ conversation: test.conversation,
4334
+ expectedOutput: test.expected_output,
4335
+ onProgress: (progress) => updatePromptEditProgress(input.progressId, progress)
4336
+ });
4337
+ return suggestedConversation === test.conversation ? { status: "unchanged" } : {
4338
+ status: "changed",
4339
+ proposal: createTestHistoryEditProposal({
4340
+ agentId: input.agent,
4341
+ testId: input.testId,
4342
+ step: input.step,
4343
+ originalConversation: test.conversation,
4344
+ suggestedConversation,
4345
+ expectedOutput: test.expected_output,
4346
+ sourceGuard: {
4347
+ suiteKey,
4348
+ allowedTestFingerprints: [testEditSourceFingerprint(test)],
4349
+ allowedPromptContents: [input.basePrompt, input.prompt]
4350
+ }
4351
+ })
4352
+ };
3914
4353
  }),
3915
4354
  suite: t2.procedure.input(z9.object({ agent: z9.string() })).query(async ({ input, ctx }) => readSuite(ctx, input.agent)),
3916
4355
  addTest: write.input(
3917
4356
  z9.object({
3918
4357
  agent: z9.string(),
4358
+ beforeTestId: z9.string().optional(),
3919
4359
  seed: TestRowSchema.omit({ id: true, order: true }).partial()
3920
4360
  })
3921
- ).mutation(async ({ input, ctx }) => addTest(ctx, input.agent, input.seed)),
3922
- createPIISanitizedTestDraft: t2.procedure.input(
3923
- z9.object({
3924
- agent: z9.string(),
3925
- seed: TraceTestSeedSchema
3926
- })
3927
- ).mutation(async ({ input, ctx }) => {
4361
+ ).mutation(
4362
+ async ({ input, ctx }) => addTest(ctx, input.agent, input.seed, { beforeTestId: input.beforeTestId })
4363
+ ),
4364
+ createPIISanitizedTestDraft: t2.procedure.input(z9.object({ agent: z9.string(), seed: TraceTestSeedSchema })).mutation(async ({ input, ctx }) => {
3928
4365
  try {
3929
4366
  const draft = await createPIISanitizedTestDraft(ctx, {
3930
4367
  suiteKey: input.agent,
@@ -3960,6 +4397,41 @@ function createTestsRouter(t2) {
3960
4397
  patch: TestRowSchema.omit({ id: true, order: true }).partial()
3961
4398
  })
3962
4399
  ).mutation(async ({ input, ctx }) => patchTest(ctx, input.agent, input.id, input.patch)),
4400
+ acceptHistoryEdit: write.input(z9.object({ id: z9.string().uuid() })).mutation(async ({ input, ctx }) => {
4401
+ const proposal = getTestHistoryEditProposalForAcceptance(input.id);
4402
+ if (!proposal) throw new Error("This test history suggestion is no longer available.");
4403
+ const result = await withPromptFileLock(
4404
+ ctx,
4405
+ proposal.agentId,
4406
+ proposal.step,
4407
+ async (file) => {
4408
+ if (!proposal.sourceGuard.allowedPromptContents.includes(await file.read())) {
4409
+ throw new Error(
4410
+ "The prompt changed while this suggestion was open. Reject this suggestion, then ask Bosun to create a new one."
4411
+ );
4412
+ }
4413
+ return patchTestConversationIfCurrent(
4414
+ ctx,
4415
+ proposal.sourceGuard.suiteKey,
4416
+ proposal.testId,
4417
+ proposal.originalConversation,
4418
+ proposal.suggestedConversation,
4419
+ proposal.sourceGuard.allowedTestFingerprints
4420
+ );
4421
+ }
4422
+ );
4423
+ if (result.status === "not-found") {
4424
+ throw new Error("This test is no longer available.");
4425
+ }
4426
+ if (result.status === "stale") {
4427
+ throw new Error(
4428
+ "This test changed while the suggestion was open. Reject this suggestion, then ask Bosun to create a new one."
4429
+ );
4430
+ }
4431
+ deleteTestHistoryEditProposal(proposal.id);
4432
+ return { ok: true, test: result.test };
4433
+ }),
4434
+ rejectHistoryEdit: t2.procedure.input(z9.object({ id: z9.string().uuid() })).mutation(({ input }) => ({ ok: deleteTestHistoryEditProposal(input.id) })),
3963
4435
  patchTestsBatch: write.input(
3964
4436
  z9.object({
3965
4437
  agent: z9.string(),
@@ -3980,6 +4452,7 @@ function createTestsRouter(t2) {
3980
4452
  step: z9.string(),
3981
4453
  scope: z9.enum(["pinned", "all"]).optional(),
3982
4454
  promptOverrides: z9.record(z9.string(), z9.string().max(1e6)).optional(),
4455
+ testHistoryOverrides: z9.record(z9.string(), z9.object({ conversation: z9.string().max(1e6) }).strict()).optional(),
3983
4456
  repeats: z9.number().int().min(1).max(500),
3984
4457
  concurrency: z9.number().int().min(1).max(75)
3985
4458
  }).strict(),
@@ -3987,6 +4460,7 @@ function createTestsRouter(t2) {
3987
4460
  agent: z9.string(),
3988
4461
  testId: z9.string(),
3989
4462
  promptOverrides: z9.record(z9.string(), z9.string().max(1e6)).optional(),
4463
+ testHistoryOverrides: z9.record(z9.string(), z9.object({ conversation: z9.string().max(1e6) }).strict()).optional(),
3990
4464
  repeats: z9.number().int().min(1).max(500),
3991
4465
  concurrency: z9.number().int().min(1).max(75)
3992
4466
  }).strict()
@@ -4000,18 +4474,17 @@ function createTestsRouter(t2) {
4000
4474
  selection,
4001
4475
  { testId: input.testId },
4002
4476
  runConfig,
4003
- input.promptOverrides
4477
+ input.promptOverrides,
4478
+ input.testHistoryOverrides
4004
4479
  );
4005
4480
  }
4006
4481
  return runPromptTestsManaged(
4007
4482
  ctx,
4008
4483
  selection,
4009
- {
4010
- scope: input.scope ?? "pinned",
4011
- step: input.step
4012
- },
4484
+ { scope: input.scope ?? "pinned", step: input.step },
4013
4485
  runConfig,
4014
- input.promptOverrides
4486
+ input.promptOverrides,
4487
+ input.testHistoryOverrides
4015
4488
  );
4016
4489
  }),
4017
4490
  cancelRun: t2.procedure.input(z9.object({ agent: z9.string() })).mutation(async ({ input }) => cancelPromptTests(input.agent))
@@ -4047,6 +4520,21 @@ var PaginatedConversationsResponseSchema = z10.object({
4047
4520
  items: z10.array(ConversationSchema),
4048
4521
  lastConversationId: z10.string().optional()
4049
4522
  });
4523
+ var AnalyticsScalarSchema = z10.union([z10.boolean(), z10.number(), z10.string()]);
4524
+ var AnalyticsFieldSchema = z10.object({
4525
+ key: z10.string(),
4526
+ type: z10.enum(["boolean", "number", "string"]),
4527
+ variantCount: z10.number(),
4528
+ values: z10.array(
4529
+ z10.object({
4530
+ value: AnalyticsScalarSchema,
4531
+ count: z10.number()
4532
+ })
4533
+ )
4534
+ });
4535
+ var AnalyticsFieldsResponseSchema = z10.object({
4536
+ items: z10.array(AnalyticsFieldSchema)
4537
+ });
4050
4538
  var LlmUsageSchema = z10.object({
4051
4539
  promptTokens: z10.number().optional(),
4052
4540
  completionTokens: z10.number().optional(),
@@ -4130,6 +4618,11 @@ var PaginatedLatencyTracesResponseSchema = z10.object({
4130
4618
  var SourceIdSchema = z11.string().trim().max(128).optional();
4131
4619
  var ConversationIdSchema3 = z11.string().trim().min(1).max(256);
4132
4620
  var LimitSchema = z11.number().int().positive().max(1e3).optional();
4621
+ var AnalyticsFilterSchema = z11.object({
4622
+ key: z11.string().trim().min(1).max(256),
4623
+ type: z11.enum(["boolean", "number", "string"]),
4624
+ values: z11.array(AnalyticsScalarSchema).min(1)
4625
+ });
4133
4626
  var ConversationsPageSchema = PaginatedConversationsResponseSchema.extend({
4134
4627
  items: z11.array(ConversationSchema).optional()
4135
4628
  });
@@ -4160,13 +4653,20 @@ function createTelescopeRouter(t2) {
4160
4653
  limit: LimitSchema,
4161
4654
  lastConversationId: z11.string().trim().min(1).optional(),
4162
4655
  phoneNumber: z11.string().trim().min(1).optional(),
4163
- agentId: z11.string().trim().min(1).optional()
4656
+ agentId: z11.string().trim().min(1).optional(),
4657
+ analyticsFilters: z11.array(AnalyticsFilterSchema).optional()
4164
4658
  })
4165
4659
  ).query(async ({ input, ctx }) => {
4166
4660
  const search = new URLSearchParams({ limit: String(input.limit ?? 10) });
4167
4661
  if (input.lastConversationId) search.set("lastConversationId", input.lastConversationId);
4168
4662
  if (input.phoneNumber) search.set("phoneNumber", input.phoneNumber);
4169
4663
  if (input.agentId) search.set("agentId", input.agentId);
4664
+ if (input.analyticsFilters?.length) {
4665
+ search.set(
4666
+ "filters",
4667
+ JSON.stringify(input.analyticsFilters.map((filter) => ({ ...filter, operator: "in" })))
4668
+ );
4669
+ }
4170
4670
  const page = await get(ctx, {
4171
4671
  sourceId: input.sourceId,
4172
4672
  path: "conversations",
@@ -4182,6 +4682,16 @@ function createTelescopeRouter(t2) {
4182
4682
  }))
4183
4683
  };
4184
4684
  }),
4685
+ analyticsFields: t2.procedure.input(z11.object({ sourceId: SourceIdSchema, agentId: z11.string().trim().min(1).optional() })).query(({ input, ctx }) => {
4686
+ const search = new URLSearchParams();
4687
+ if (input.agentId) search.set("agentId", input.agentId);
4688
+ return get(ctx, {
4689
+ sourceId: input.sourceId,
4690
+ path: "analytics/fields",
4691
+ search,
4692
+ schema: AnalyticsFieldsResponseSchema
4693
+ });
4694
+ }),
4185
4695
  conversation: t2.procedure.input(z11.object({ sourceId: SourceIdSchema, conversationId: ConversationIdSchema3 })).query(
4186
4696
  ({ input, ctx }) => get(ctx, {
4187
4697
  sourceId: input.sourceId,