@recombine-ai/bosun 0.16.2 → 0.18.0

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