@recombine-ai/bosun 0.16.2 → 0.17.1

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 (524) hide show
  1. package/README.md +24 -0
  2. package/bin/bosun.js +153 -20
  3. package/bin/git-mode.js +1139 -0
  4. package/build/client/_app/immutable/assets/0.D9HpHezk.css +1 -0
  5. package/build/client/_app/immutable/assets/0.D9HpHezk.css.br +0 -0
  6. package/build/client/_app/immutable/assets/0.D9HpHezk.css.gz +0 -0
  7. package/build/client/_app/immutable/assets/{2.DtxQDOXx.css → 2.Bc0Sf3YD.css} +1 -1
  8. package/build/client/_app/immutable/assets/2.Bc0Sf3YD.css.br +0 -0
  9. package/build/client/_app/immutable/assets/2.Bc0Sf3YD.css.gz +0 -0
  10. package/build/client/_app/immutable/chunks/{DneQQ7D5.js → -3ZOIHDA.js} +1 -1
  11. package/build/client/_app/immutable/chunks/-3ZOIHDA.js.br +0 -0
  12. package/build/client/_app/immutable/chunks/-3ZOIHDA.js.gz +0 -0
  13. package/build/client/_app/immutable/chunks/7hV1SZxK.js +1 -0
  14. package/build/client/_app/immutable/chunks/7hV1SZxK.js.br +2 -0
  15. package/build/client/_app/immutable/chunks/7hV1SZxK.js.gz +0 -0
  16. package/build/client/_app/immutable/chunks/B-dTD_wm.js +1 -0
  17. package/build/client/_app/immutable/chunks/B-dTD_wm.js.br +0 -0
  18. package/build/client/_app/immutable/chunks/B-dTD_wm.js.gz +0 -0
  19. package/build/client/_app/immutable/chunks/{UqjCSZmA.js → B-iasAJv.js} +1 -1
  20. package/build/client/_app/immutable/chunks/B-iasAJv.js.br +0 -0
  21. package/build/client/_app/immutable/chunks/B-iasAJv.js.gz +0 -0
  22. package/build/client/_app/immutable/chunks/BDijIo6D.js +33 -0
  23. package/build/client/_app/immutable/chunks/BDijIo6D.js.br +0 -0
  24. package/build/client/_app/immutable/chunks/BDijIo6D.js.gz +0 -0
  25. package/build/client/_app/immutable/chunks/BHB0Pj1h.js +2 -0
  26. package/build/client/_app/immutable/chunks/BHB0Pj1h.js.br +0 -0
  27. package/build/client/_app/immutable/chunks/BHB0Pj1h.js.gz +0 -0
  28. package/build/client/_app/immutable/chunks/{Bog028VR.js → BKfmN1Rj.js} +1 -1
  29. package/build/client/_app/immutable/chunks/BKfmN1Rj.js.br +0 -0
  30. package/build/client/_app/immutable/chunks/BKfmN1Rj.js.gz +0 -0
  31. package/build/client/_app/immutable/chunks/{pPpIn6tB.js → BlFOg-Sd.js} +1 -1
  32. package/build/client/_app/immutable/chunks/BlFOg-Sd.js.br +0 -0
  33. package/build/client/_app/immutable/chunks/BlFOg-Sd.js.gz +0 -0
  34. package/build/client/_app/immutable/chunks/{CJcZ2GVC.js → Byg3jr1u.js} +1 -1
  35. package/build/client/_app/immutable/chunks/Byg3jr1u.js.br +0 -0
  36. package/build/client/_app/immutable/chunks/Byg3jr1u.js.gz +0 -0
  37. package/build/client/_app/immutable/chunks/{Bmd5zhWC.js → BzxxLgHi.js} +1 -1
  38. package/build/client/_app/immutable/chunks/BzxxLgHi.js.br +0 -0
  39. package/build/client/_app/immutable/chunks/BzxxLgHi.js.gz +0 -0
  40. package/build/client/_app/immutable/chunks/{CEHsomMo.js → C4UC6AgE.js} +1 -1
  41. package/build/client/_app/immutable/chunks/C4UC6AgE.js.br +0 -0
  42. package/build/client/_app/immutable/chunks/{CEHsomMo.js.gz → C4UC6AgE.js.gz} +0 -0
  43. package/build/client/_app/immutable/chunks/C60AG6QQ.js +1 -0
  44. package/build/client/_app/immutable/chunks/C60AG6QQ.js.br +0 -0
  45. package/build/client/_app/immutable/chunks/C60AG6QQ.js.gz +0 -0
  46. package/build/client/_app/immutable/chunks/CAIfVN23.js +1 -0
  47. package/build/client/_app/immutable/chunks/CAIfVN23.js.br +0 -0
  48. package/build/client/_app/immutable/chunks/CAIfVN23.js.gz +0 -0
  49. package/build/client/_app/immutable/chunks/{JcG4w2gF.js → CB9iDxwI.js} +1 -1
  50. package/build/client/_app/immutable/chunks/CB9iDxwI.js.br +0 -0
  51. package/build/client/_app/immutable/chunks/CB9iDxwI.js.gz +0 -0
  52. package/build/client/_app/immutable/chunks/{DoD3q5w5.js → CD_KdWs9.js} +1 -1
  53. package/build/client/_app/immutable/chunks/CD_KdWs9.js.br +0 -0
  54. package/build/client/_app/immutable/chunks/CD_KdWs9.js.gz +0 -0
  55. package/build/client/_app/immutable/chunks/CDy1LBR5.js +1 -0
  56. package/build/client/_app/immutable/chunks/CDy1LBR5.js.br +0 -0
  57. package/build/client/_app/immutable/chunks/CDy1LBR5.js.gz +0 -0
  58. package/build/client/_app/immutable/chunks/{DCXjBDmY.js → CMFNmSvo.js} +1 -1
  59. package/build/client/_app/immutable/chunks/CMFNmSvo.js.br +0 -0
  60. package/build/client/_app/immutable/chunks/CMFNmSvo.js.gz +0 -0
  61. package/build/client/_app/immutable/chunks/{Bv1mNXs0.js → CNIn4L55.js} +1 -1
  62. package/build/client/_app/immutable/chunks/CNIn4L55.js.br +0 -0
  63. package/build/client/_app/immutable/chunks/CNIn4L55.js.gz +0 -0
  64. package/build/client/_app/immutable/chunks/CUKoVL36.js +1 -0
  65. package/build/client/_app/immutable/chunks/CUKoVL36.js.br +0 -0
  66. package/build/client/_app/immutable/chunks/CUKoVL36.js.gz +0 -0
  67. package/build/client/_app/immutable/chunks/CYGbCr1i.js +3 -0
  68. package/build/client/_app/immutable/chunks/CYGbCr1i.js.br +0 -0
  69. package/build/client/_app/immutable/chunks/CYGbCr1i.js.gz +0 -0
  70. package/build/client/_app/immutable/chunks/CYr0FGgn.js +1 -0
  71. package/build/client/_app/immutable/chunks/CYr0FGgn.js.br +0 -0
  72. package/build/client/_app/immutable/chunks/CYr0FGgn.js.gz +0 -0
  73. package/build/client/_app/immutable/chunks/{DtXXy94i.js → Ce6a22qU.js} +1 -1
  74. package/build/client/_app/immutable/chunks/Ce6a22qU.js.br +0 -0
  75. package/build/client/_app/immutable/chunks/Ce6a22qU.js.gz +0 -0
  76. package/build/client/_app/immutable/chunks/{DfcDjAv-.js → CeoQ-OhK.js} +1 -1
  77. package/build/client/_app/immutable/chunks/CeoQ-OhK.js.br +0 -0
  78. package/build/client/_app/immutable/chunks/CeoQ-OhK.js.gz +0 -0
  79. package/build/client/_app/immutable/chunks/{D8mA-uZ4.js → CupXtwVS.js} +1 -1
  80. package/build/client/_app/immutable/chunks/CupXtwVS.js.br +0 -0
  81. package/build/client/_app/immutable/chunks/CupXtwVS.js.gz +0 -0
  82. package/build/client/_app/immutable/chunks/{B5zf3x62.js → Cy2Uze-c.js} +1 -1
  83. package/build/client/_app/immutable/chunks/Cy2Uze-c.js.br +0 -0
  84. package/build/client/_app/immutable/chunks/Cy2Uze-c.js.gz +0 -0
  85. package/build/client/_app/immutable/chunks/{Dg0A8wPA.js → D8Lmn3S5.js} +1 -1
  86. package/build/client/_app/immutable/chunks/D8Lmn3S5.js.br +0 -0
  87. package/build/client/_app/immutable/chunks/D8Lmn3S5.js.gz +0 -0
  88. package/build/client/_app/immutable/chunks/D8doM1Y6.js +1 -0
  89. package/build/client/_app/immutable/chunks/D8doM1Y6.js.br +2 -0
  90. package/build/client/_app/immutable/chunks/D8doM1Y6.js.gz +0 -0
  91. package/build/client/_app/immutable/chunks/{BHHfrjbV.js → D9BWOD7V.js} +1 -1
  92. package/build/client/_app/immutable/chunks/D9BWOD7V.js.br +3 -0
  93. package/build/client/_app/immutable/chunks/D9BWOD7V.js.gz +0 -0
  94. package/build/client/_app/immutable/chunks/{Cp_st-64.js → DBB88ZB7.js} +1 -1
  95. package/build/client/_app/immutable/chunks/DBB88ZB7.js.br +2 -0
  96. package/build/client/_app/immutable/chunks/DBB88ZB7.js.gz +0 -0
  97. package/build/client/_app/immutable/chunks/{Dm49pcMm.js → DC7lPrn4.js} +1 -1
  98. package/build/client/_app/immutable/chunks/DC7lPrn4.js.br +0 -0
  99. package/build/client/_app/immutable/chunks/DC7lPrn4.js.gz +0 -0
  100. package/build/client/_app/immutable/chunks/DCwWNTpf.js +9 -0
  101. package/build/client/_app/immutable/chunks/DCwWNTpf.js.br +0 -0
  102. package/build/client/_app/immutable/chunks/DCwWNTpf.js.gz +0 -0
  103. package/build/client/_app/immutable/chunks/{wcTQUnZO.js → DEnUeQNt.js} +1 -1
  104. package/build/client/_app/immutable/chunks/DEnUeQNt.js.br +0 -0
  105. package/build/client/_app/immutable/chunks/DEnUeQNt.js.gz +0 -0
  106. package/build/client/_app/immutable/chunks/D_laxq3h.js +1 -0
  107. package/build/client/_app/immutable/chunks/D_laxq3h.js.br +0 -0
  108. package/build/client/_app/immutable/chunks/D_laxq3h.js.gz +0 -0
  109. package/build/client/_app/immutable/chunks/{Ddv5LgEc.js → DfkYlKBk.js} +1 -1
  110. package/build/client/_app/immutable/chunks/DfkYlKBk.js.br +0 -0
  111. package/build/client/_app/immutable/chunks/DfkYlKBk.js.gz +0 -0
  112. package/build/client/_app/immutable/chunks/DnMS2X6M.js +2 -0
  113. package/build/client/_app/immutable/chunks/DnMS2X6M.js.br +0 -0
  114. package/build/client/_app/immutable/chunks/DnMS2X6M.js.gz +0 -0
  115. package/build/client/_app/immutable/chunks/{B1HdnQRQ.js → DrE1ro9S.js} +1 -1
  116. package/build/client/_app/immutable/chunks/DrE1ro9S.js.br +0 -0
  117. package/build/client/_app/immutable/chunks/DrE1ro9S.js.gz +0 -0
  118. package/build/client/_app/immutable/chunks/DzY_RPb9.js +1 -0
  119. package/build/client/_app/immutable/chunks/DzY_RPb9.js.br +0 -0
  120. package/build/client/_app/immutable/chunks/DzY_RPb9.js.gz +0 -0
  121. package/build/client/_app/immutable/chunks/PrCSpCPg.js +1 -0
  122. package/build/client/_app/immutable/chunks/PrCSpCPg.js.br +0 -0
  123. package/build/client/_app/immutable/chunks/PrCSpCPg.js.gz +0 -0
  124. package/build/client/_app/immutable/chunks/{CN555stc.js → eMofpOrR.js} +1 -1
  125. package/build/client/_app/immutable/chunks/eMofpOrR.js.br +0 -0
  126. package/build/client/_app/immutable/chunks/eMofpOrR.js.gz +0 -0
  127. package/build/client/_app/immutable/entry/app.BQUuHkSR.js +2 -0
  128. package/build/client/_app/immutable/entry/app.BQUuHkSR.js.br +0 -0
  129. package/build/client/_app/immutable/entry/app.BQUuHkSR.js.gz +0 -0
  130. package/build/client/_app/immutable/entry/start.DoeTycdu.js +1 -0
  131. package/build/client/_app/immutable/entry/start.DoeTycdu.js.br +2 -0
  132. package/build/client/_app/immutable/entry/start.DoeTycdu.js.gz +0 -0
  133. package/build/client/_app/immutable/nodes/0.DBnFpwRM.js +4 -0
  134. package/build/client/_app/immutable/nodes/0.DBnFpwRM.js.br +0 -0
  135. package/build/client/_app/immutable/nodes/0.DBnFpwRM.js.gz +0 -0
  136. package/build/client/_app/immutable/nodes/1.D-kI-fLd.js +1 -0
  137. package/build/client/_app/immutable/nodes/1.D-kI-fLd.js.br +0 -0
  138. package/build/client/_app/immutable/nodes/1.D-kI-fLd.js.gz +0 -0
  139. package/build/client/_app/immutable/nodes/10.DWb5H7vX.js +28 -0
  140. package/build/client/_app/immutable/nodes/10.DWb5H7vX.js.br +0 -0
  141. package/build/client/_app/immutable/nodes/10.DWb5H7vX.js.gz +0 -0
  142. package/build/client/_app/immutable/nodes/{15.D2I0y3ys.js → 15.CGc3_07c.js} +1 -1
  143. package/build/client/_app/immutable/nodes/15.CGc3_07c.js.br +3 -0
  144. package/build/client/_app/immutable/nodes/15.CGc3_07c.js.gz +0 -0
  145. package/build/client/_app/immutable/nodes/17.BIba62hW.js +3 -0
  146. package/build/client/_app/immutable/nodes/17.BIba62hW.js.br +0 -0
  147. package/build/client/_app/immutable/nodes/17.BIba62hW.js.gz +0 -0
  148. package/build/client/_app/immutable/nodes/18.Clxr6FwI.js +11 -0
  149. package/build/client/_app/immutable/nodes/18.Clxr6FwI.js.br +0 -0
  150. package/build/client/_app/immutable/nodes/18.Clxr6FwI.js.gz +0 -0
  151. package/build/client/_app/immutable/nodes/2.cbpnR8be.js +35 -0
  152. package/build/client/_app/immutable/nodes/2.cbpnR8be.js.br +0 -0
  153. package/build/client/_app/immutable/nodes/2.cbpnR8be.js.gz +0 -0
  154. package/build/client/_app/immutable/nodes/3.DsP759xk.js +1 -0
  155. package/build/client/_app/immutable/nodes/3.DsP759xk.js.br +0 -0
  156. package/build/client/_app/immutable/nodes/3.DsP759xk.js.gz +0 -0
  157. package/build/client/_app/immutable/nodes/{4.C8VxVbVn.js → 4.BIGLAIDe.js} +1 -1
  158. package/build/client/_app/immutable/nodes/4.BIGLAIDe.js.br +0 -0
  159. package/build/client/_app/immutable/nodes/4.BIGLAIDe.js.gz +0 -0
  160. package/build/client/_app/immutable/nodes/{5.BjrnKYaA.js → 5.zMHOyhDv.js} +1 -1
  161. package/build/client/_app/immutable/nodes/5.zMHOyhDv.js.br +0 -0
  162. package/build/client/_app/immutable/nodes/5.zMHOyhDv.js.gz +0 -0
  163. package/build/client/_app/immutable/nodes/6.DFQ1IZsQ.js +1 -0
  164. package/build/client/_app/immutable/nodes/6.DFQ1IZsQ.js.br +0 -0
  165. package/build/client/_app/immutable/nodes/6.DFQ1IZsQ.js.gz +0 -0
  166. package/build/client/_app/immutable/nodes/8.CTgEdr7L.js +2 -0
  167. package/build/client/_app/immutable/nodes/8.CTgEdr7L.js.br +0 -0
  168. package/build/client/_app/immutable/nodes/8.CTgEdr7L.js.gz +0 -0
  169. package/build/client/_app/immutable/nodes/9.BidF5Uvq.js +2 -0
  170. package/build/client/_app/immutable/nodes/9.BidF5Uvq.js.br +0 -0
  171. package/build/client/_app/immutable/nodes/9.BidF5Uvq.js.gz +0 -0
  172. package/build/client/_app/version.json +1 -1
  173. package/build/client/_app/version.json.br +0 -0
  174. package/build/client/_app/version.json.gz +0 -0
  175. package/build/server/chunks/0-BZOFUIfF.js +9 -0
  176. package/build/server/chunks/0-BZOFUIfF.js.map +1 -0
  177. package/build/server/chunks/1-XwGU5VVN.js +9 -0
  178. package/build/server/chunks/{1-C0HdTvXW.js.map → 1-XwGU5VVN.js.map} +1 -1
  179. package/build/server/chunks/10-BgFSx4ib.js +9 -0
  180. package/build/server/chunks/10-BgFSx4ib.js.map +1 -0
  181. package/build/server/chunks/15-BjTTC1jf.js +9 -0
  182. package/build/server/chunks/{15-BQcb9C_y.js.map → 15-BjTTC1jf.js.map} +1 -1
  183. package/build/server/chunks/17-BEkWCfzc.js +9 -0
  184. package/build/server/chunks/17-BEkWCfzc.js.map +1 -0
  185. package/build/server/chunks/18-C-S0wKdp.js +9 -0
  186. package/build/server/chunks/18-C-S0wKdp.js.map +1 -0
  187. package/build/server/chunks/2-o-cIkIjs.js +9 -0
  188. package/build/server/chunks/2-o-cIkIjs.js.map +1 -0
  189. package/build/server/chunks/3-B-ABlyFO.js +9 -0
  190. package/build/server/chunks/3-B-ABlyFO.js.map +1 -0
  191. package/build/server/chunks/4-joAFA_Y8.js +9 -0
  192. package/build/server/chunks/4-joAFA_Y8.js.map +1 -0
  193. package/build/server/chunks/5-DIhC9k4l.js +9 -0
  194. package/build/server/chunks/5-DIhC9k4l.js.map +1 -0
  195. package/build/server/chunks/6-CMuAQIcW.js +9 -0
  196. package/build/server/chunks/6-CMuAQIcW.js.map +1 -0
  197. package/build/server/chunks/8-CbETTVRG.js +9 -0
  198. package/build/server/chunks/8-CbETTVRG.js.map +1 -0
  199. package/build/server/chunks/9-nMV90uVU.js +9 -0
  200. package/build/server/chunks/9-nMV90uVU.js.map +1 -0
  201. package/build/server/chunks/{AgentSidebar-BWBnqHnV.js → AgentSidebar-BEbSdZdN.js} +4 -4
  202. package/build/server/chunks/{AgentSidebar-BWBnqHnV.js.map → AgentSidebar-BEbSdZdN.js.map} +1 -1
  203. package/build/server/chunks/{Button-DH7gdU0s.js → Button-Dc9CR4Wh.js} +2 -2
  204. package/build/server/chunks/{Button-DH7gdU0s.js.map → Button-Dc9CR4Wh.js.map} +1 -1
  205. package/build/server/chunks/{ChatStepTrace-BivoNYOY.js → ChatStepTrace-OM96X4g8.js} +18 -7
  206. package/build/server/chunks/ChatStepTrace-OM96X4g8.js.map +1 -0
  207. package/build/server/chunks/{Dropdown-DnARl86Q.js → Dropdown-Cgd7pG-k.js} +2 -2
  208. package/build/server/chunks/{Dropdown-DnARl86Q.js.map → Dropdown-Cgd7pG-k.js.map} +1 -1
  209. package/build/server/chunks/GitControlsButton-BLcNzrDt.js +81 -0
  210. package/build/server/chunks/GitControlsButton-BLcNzrDt.js.map +1 -0
  211. package/build/server/chunks/{Icon-DP1YKCRV.js → Icon-Bogvl2n9.js} +2 -2
  212. package/build/server/chunks/{Icon-DP1YKCRV.js.map → Icon-Bogvl2n9.js.map} +1 -1
  213. package/build/server/chunks/{Input-CUJ9Cg7b.js → Input-7hpw1WLW.js} +2 -2
  214. package/build/server/chunks/{Input-CUJ9Cg7b.js.map → Input-7hpw1WLW.js.map} +1 -1
  215. package/build/server/chunks/{Modal-CGJuNpZ0.js → Modal-7IIIfFEG.js} +5 -5
  216. package/build/server/chunks/{Modal-CGJuNpZ0.js.map → Modal-7IIIfFEG.js.map} +1 -1
  217. package/build/server/chunks/{Panel-zvjxe036.js → Panel-Cj1mXx9-.js} +2 -2
  218. package/build/server/chunks/{Panel-zvjxe036.js.map → Panel-Cj1mXx9-.js.map} +1 -1
  219. package/build/server/chunks/{Tab-CUkyLqbN.js → Tab-bf4ZDs5y.js} +4 -4
  220. package/build/server/chunks/{Tab-CUkyLqbN.js.map → Tab-bf4ZDs5y.js.map} +1 -1
  221. package/build/server/chunks/{_layout.svelte-BnrWYuLv.js → _layout.svelte-C71luIwv.js} +10 -10
  222. package/build/server/chunks/{_layout.svelte-BnrWYuLv.js.map → _layout.svelte-C71luIwv.js.map} +1 -1
  223. package/build/server/chunks/{_layout.svelte-wi6o5Ecu.js → _layout.svelte-CFhHgXI_.js} +24 -18
  224. package/build/server/chunks/_layout.svelte-CFhHgXI_.js.map +1 -0
  225. package/build/server/chunks/_layout.svelte-CJd_pmkx.js +431 -0
  226. package/build/server/chunks/_layout.svelte-CJd_pmkx.js.map +1 -0
  227. package/build/server/chunks/{_layout.svelte-B88WMyk0.js → _layout.svelte-D6rhwm98.js} +445 -70
  228. package/build/server/chunks/_layout.svelte-D6rhwm98.js.map +1 -0
  229. package/build/server/chunks/{_page.svelte-CeS_21Ho.js → _page.svelte-BBvDMiTV.js} +25 -21
  230. package/build/server/chunks/_page.svelte-BBvDMiTV.js.map +1 -0
  231. package/build/server/chunks/{_page.svelte-h_J4lNT9.js → _page.svelte-BQs9dEuq.js} +8 -7
  232. package/build/server/chunks/{_page.svelte-h_J4lNT9.js.map → _page.svelte-BQs9dEuq.js.map} +1 -1
  233. package/build/server/chunks/{_page.svelte-DhFNFMnl.js → _page.svelte-BoO2LWVX.js} +34 -14
  234. package/build/server/chunks/_page.svelte-BoO2LWVX.js.map +1 -0
  235. package/build/server/chunks/{_page.svelte-CHgQjBQj.js → _page.svelte-DUCTSdv-.js} +2 -2
  236. package/build/server/chunks/{_page.svelte-CHgQjBQj.js.map → _page.svelte-DUCTSdv-.js.map} +1 -1
  237. package/build/server/chunks/_page.svelte-FXYLKyMw.js +110 -0
  238. package/build/server/chunks/_page.svelte-FXYLKyMw.js.map +1 -0
  239. package/build/server/chunks/{_page.svelte-57pyhszV.js → _page.svelte-KA1hqKj2.js} +20 -107
  240. package/build/server/chunks/_page.svelte-KA1hqKj2.js.map +1 -0
  241. package/build/server/chunks/{_page.svelte-BSVJ2d2G.js → _page.svelte-y8CZFL8a.js} +210 -106
  242. package/build/server/chunks/_page.svelte-y8CZFL8a.js.map +1 -0
  243. package/build/server/chunks/{_page.svelte-DHSYVLoj.js → _page.svelte-yIHSIEW6.js} +21 -15
  244. package/build/server/chunks/_page.svelte-yIHSIEW6.js.map +1 -0
  245. package/build/server/chunks/_server.ts-Cp9DgLcE.js +336 -0
  246. package/build/server/chunks/_server.ts-Cp9DgLcE.js.map +1 -0
  247. package/build/server/chunks/{agents-context-CbFsZmi5.js → agents-context-owgusUPR.js} +2 -2
  248. package/build/server/chunks/{agents-context-CbFsZmi5.js.map → agents-context-owgusUPR.js.map} +1 -1
  249. package/build/server/chunks/{client-DO-hqMbT.js → client-P-7bDULt.js} +4 -4
  250. package/build/server/chunks/{client-DO-hqMbT.js.map → client-P-7bDULt.js.map} +1 -1
  251. package/build/server/chunks/{conversation-view-memory-CtbUZd_L.js → conversation-view-memory-SdpdqTR7.js} +2 -2
  252. package/build/server/chunks/{conversation-view-memory-CtbUZd_L.js.map → conversation-view-memory-SdpdqTR7.js.map} +1 -1
  253. package/build/server/chunks/{edit-workspace-context-tyHBPujC.js → edit-workspace-context-hanFnXR3.js} +2 -2
  254. package/build/server/chunks/{edit-workspace-context-tyHBPujC.js.map → edit-workspace-context-hanFnXR3.js.map} +1 -1
  255. package/build/server/chunks/error.svelte-yI9RS6H4.js +15 -0
  256. package/build/server/chunks/{error.svelte-DmKKN1RU.js.map → error.svelte-yI9RS6H4.js.map} +1 -1
  257. package/build/server/chunks/git-mode-context-BpNsAUGF.js +18 -0
  258. package/build/server/chunks/git-mode-context-BpNsAUGF.js.map +1 -0
  259. package/build/server/chunks/{index-LMsd1ih2.js → index-DGtbUu8Y.js} +2 -2
  260. package/build/server/chunks/{index-LMsd1ih2.js.map → index-DGtbUu8Y.js.map} +1 -1
  261. package/build/server/chunks/{index-server-BRr5mZEq.js → index-server-l8-WsVn4.js} +2 -2
  262. package/build/server/chunks/{index-server-BRr5mZEq.js.map → index-server-l8-WsVn4.js.map} +1 -1
  263. package/build/server/chunks/{index2-ClgfzZ89.js → index2-CtgLiJVO.js} +3 -3
  264. package/build/server/chunks/{index2-ClgfzZ89.js.map → index2-CtgLiJVO.js.map} +1 -1
  265. package/build/server/chunks/{index3-BdRUAffj.js → index3-4hbbCqW0.js} +2 -2
  266. package/build/server/chunks/{index3-BdRUAffj.js.map → index3-4hbbCqW0.js.map} +1 -1
  267. package/build/server/chunks/{root-4q2Pr7ck.js → root-sWPnlpi3.js} +2 -2
  268. package/build/server/chunks/{root-4q2Pr7ck.js.map → root-sWPnlpi3.js.map} +1 -1
  269. package/build/server/chunks/{source-selection-CcN1cgXc.js → source-selection-DSkaPGfZ.js} +7 -2
  270. package/build/server/chunks/source-selection-DSkaPGfZ.js.map +1 -0
  271. package/build/server/chunks/{state.svelte-Bd1i-u49.js → state.svelte-BkvaElc6.js} +3 -3
  272. package/build/server/chunks/{state.svelte-Bd1i-u49.js.map → state.svelte-BkvaElc6.js.map} +1 -1
  273. package/build/server/chunks/trpc-client-Cr8RJ4UO.js +260 -0
  274. package/build/server/chunks/trpc-client-Cr8RJ4UO.js.map +1 -0
  275. package/build/server/index.js +4 -4
  276. package/build/server/index.js.map +1 -1
  277. package/build/server/manifest.js +15 -15
  278. package/build/server/manifest.js.map +1 -1
  279. package/package.json +1 -1
  280. package/src/backend/git-mode/command.spec.ts +172 -0
  281. package/src/backend/git-mode/command.ts +405 -0
  282. package/src/backend/git-mode/config.spec.ts +45 -0
  283. package/src/backend/git-mode/config.ts +90 -0
  284. package/src/backend/git-mode/index.ts +20 -0
  285. package/src/backend/git-mode/repository-write-gate.spec.ts +116 -0
  286. package/src/backend/git-mode/repository-write-gate.ts +260 -0
  287. package/src/backend/git-mode/router.ts +59 -0
  288. package/src/backend/git-mode/service.spec.ts +1653 -0
  289. package/src/backend/git-mode/service.ts +2183 -0
  290. package/src/backend/git-mode/types.ts +82 -0
  291. package/src/backend/git-mode/write-tracker.spec.ts +46 -0
  292. package/src/backend/git-mode/write-tracker.ts +39 -0
  293. package/src/backend/lib/backend-restart.spec.ts +27 -0
  294. package/src/backend/lib/backend-restart.ts +16 -2
  295. package/src/backend/lib/middleware/cors.spec.ts +67 -0
  296. package/src/backend/lib/middleware/cors.ts +40 -12
  297. package/src/backend/roleplay/agent-loader.ts +49 -0
  298. package/src/backend/roleplay/old-server.ts +106 -56
  299. package/src/backend/roleplay/prompt-edit-proposals.ts +38 -4
  300. package/src/backend/roleplay/prompt-file.ts +59 -0
  301. package/src/backend/roleplay/router.spec.ts +75 -0
  302. package/src/backend/roleplay/router.ts +123 -69
  303. package/src/backend/roleplay/suggest-file-edit.ts +160 -0
  304. package/src/backend/roleplay/suggest-prompt-edit.ts +9 -138
  305. package/src/backend/roleplay/suggest-test-fix.spec.ts +50 -0
  306. package/src/backend/roleplay/suggest-test-fix.ts +65 -0
  307. package/src/backend/roleplay/test-fix-proposal-acceptance.spec.ts +253 -0
  308. package/src/backend/server.ts +116 -13
  309. package/src/backend/tests/academy.ts +17 -3
  310. package/src/backend/tests/data.spec.ts +81 -2
  311. package/src/backend/tests/data.ts +74 -4
  312. package/src/backend/tests/router.ts +101 -36
  313. package/src/backend/tests/suggest-test-history-edit.spec.ts +20 -0
  314. package/src/backend/tests/suggest-test-history-edit.ts +52 -0
  315. package/src/backend/tests/test-edit-source.spec.ts +61 -0
  316. package/src/backend/tests/test-edit-source.ts +34 -0
  317. package/src/backend/tests/test-history-edit-proposals.ts +69 -0
  318. package/src/backend/tests/test-history-overrides.spec.ts +24 -0
  319. package/src/backend/tests/test-selection.ts +27 -0
  320. package/src/lib/api-telescope/source-selection.spec.ts +44 -0
  321. package/src/lib/api-telescope/source-selection.ts +28 -0
  322. package/src/lib/components/Modal.svelte +1 -1
  323. package/src/lib/edit/edit-workspace-context.ts +33 -3
  324. package/src/lib/edit/test-ai-edit.spec.ts +31 -0
  325. package/src/lib/edit/test-ai-edit.ts +52 -0
  326. package/src/lib/git-mode/BlockingGitDialog.svelte +118 -0
  327. package/src/lib/git-mode/GitControlsButton.svelte +96 -0
  328. package/src/lib/git-mode/client-admission.spec.ts +304 -0
  329. package/src/lib/git-mode/client-admission.ts +343 -0
  330. package/src/lib/git-mode/errors.ts +2 -0
  331. package/src/lib/git-mode/git-mode-context.ts +70 -0
  332. package/src/lib/git-mode/refresh-policy.spec.ts +95 -0
  333. package/src/lib/git-mode/refresh-policy.ts +39 -0
  334. package/src/lib/git-mode/status-recovery.spec.ts +86 -0
  335. package/src/lib/git-mode/status-recovery.ts +33 -0
  336. package/src/lib/monaco/bosun-theme.ts +6 -83
  337. package/src/lib/monaco/editor-appearance.spec.ts +84 -0
  338. package/src/lib/monaco/editor-appearance.ts +154 -0
  339. package/src/lib/monaco/editor-decorations.spec.ts +91 -0
  340. package/src/lib/monaco/editor-decorations.ts +133 -0
  341. package/src/lib/tests/assertion-types.ts +74 -0
  342. package/src/lib/trpc-client.ts +15 -1
  343. package/src/lib/ws-client.spec.ts +208 -0
  344. package/src/lib/ws-client.ts +196 -14
  345. package/src/lib/ws-message.ts +24 -0
  346. package/build/client/_app/immutable/assets/0.Rrc0O--C.css +0 -1
  347. package/build/client/_app/immutable/assets/0.Rrc0O--C.css.br +0 -0
  348. package/build/client/_app/immutable/assets/0.Rrc0O--C.css.gz +0 -0
  349. package/build/client/_app/immutable/assets/2.DtxQDOXx.css.br +0 -0
  350. package/build/client/_app/immutable/assets/2.DtxQDOXx.css.gz +0 -0
  351. package/build/client/_app/immutable/chunks/B1HdnQRQ.js.br +0 -0
  352. package/build/client/_app/immutable/chunks/B1HdnQRQ.js.gz +0 -0
  353. package/build/client/_app/immutable/chunks/B5zf3x62.js.br +0 -0
  354. package/build/client/_app/immutable/chunks/B5zf3x62.js.gz +0 -0
  355. package/build/client/_app/immutable/chunks/BHHfrjbV.js.br +0 -2
  356. package/build/client/_app/immutable/chunks/BHHfrjbV.js.gz +0 -0
  357. package/build/client/_app/immutable/chunks/BU7aeYn4.js +0 -1
  358. package/build/client/_app/immutable/chunks/BU7aeYn4.js.br +0 -0
  359. package/build/client/_app/immutable/chunks/BU7aeYn4.js.gz +0 -0
  360. package/build/client/_app/immutable/chunks/BbGYcfPY.js +0 -3
  361. package/build/client/_app/immutable/chunks/BbGYcfPY.js.br +0 -0
  362. package/build/client/_app/immutable/chunks/BbGYcfPY.js.gz +0 -0
  363. package/build/client/_app/immutable/chunks/Bmd5zhWC.js.br +0 -0
  364. package/build/client/_app/immutable/chunks/Bmd5zhWC.js.gz +0 -0
  365. package/build/client/_app/immutable/chunks/BnqH7p3U.js +0 -33
  366. package/build/client/_app/immutable/chunks/BnqH7p3U.js.br +0 -0
  367. package/build/client/_app/immutable/chunks/BnqH7p3U.js.gz +0 -0
  368. package/build/client/_app/immutable/chunks/Bog028VR.js.br +0 -0
  369. package/build/client/_app/immutable/chunks/Bog028VR.js.gz +0 -0
  370. package/build/client/_app/immutable/chunks/Bv1mNXs0.js.br +0 -0
  371. package/build/client/_app/immutable/chunks/Bv1mNXs0.js.gz +0 -0
  372. package/build/client/_app/immutable/chunks/BvjXTrOz.js +0 -1
  373. package/build/client/_app/immutable/chunks/BvjXTrOz.js.br +0 -0
  374. package/build/client/_app/immutable/chunks/BvjXTrOz.js.gz +0 -0
  375. package/build/client/_app/immutable/chunks/C1R-s4aD.js +0 -1
  376. package/build/client/_app/immutable/chunks/C1R-s4aD.js.br +0 -0
  377. package/build/client/_app/immutable/chunks/C1R-s4aD.js.gz +0 -0
  378. package/build/client/_app/immutable/chunks/CC1OcqhR.js +0 -1
  379. package/build/client/_app/immutable/chunks/CC1OcqhR.js.br +0 -0
  380. package/build/client/_app/immutable/chunks/CC1OcqhR.js.gz +0 -0
  381. package/build/client/_app/immutable/chunks/CEHsomMo.js.br +0 -0
  382. package/build/client/_app/immutable/chunks/CJcZ2GVC.js.br +0 -0
  383. package/build/client/_app/immutable/chunks/CJcZ2GVC.js.gz +0 -0
  384. package/build/client/_app/immutable/chunks/CN555stc.js.br +0 -0
  385. package/build/client/_app/immutable/chunks/CN555stc.js.gz +0 -0
  386. package/build/client/_app/immutable/chunks/CQg2Mme0.js +0 -9
  387. package/build/client/_app/immutable/chunks/CQg2Mme0.js.br +0 -0
  388. package/build/client/_app/immutable/chunks/CQg2Mme0.js.gz +0 -0
  389. package/build/client/_app/immutable/chunks/Cp_st-64.js.br +0 -0
  390. package/build/client/_app/immutable/chunks/Cp_st-64.js.gz +0 -0
  391. package/build/client/_app/immutable/chunks/D8mA-uZ4.js.br +0 -0
  392. package/build/client/_app/immutable/chunks/D8mA-uZ4.js.gz +0 -0
  393. package/build/client/_app/immutable/chunks/DCXjBDmY.js.br +0 -0
  394. package/build/client/_app/immutable/chunks/DCXjBDmY.js.gz +0 -0
  395. package/build/client/_app/immutable/chunks/DSLyVE1P.js +0 -1
  396. package/build/client/_app/immutable/chunks/DSLyVE1P.js.br +0 -0
  397. package/build/client/_app/immutable/chunks/DSLyVE1P.js.gz +0 -0
  398. package/build/client/_app/immutable/chunks/Ddv5LgEc.js.br +0 -0
  399. package/build/client/_app/immutable/chunks/Ddv5LgEc.js.gz +0 -0
  400. package/build/client/_app/immutable/chunks/DfcDjAv-.js.br +0 -0
  401. package/build/client/_app/immutable/chunks/DfcDjAv-.js.gz +0 -0
  402. package/build/client/_app/immutable/chunks/Dg0A8wPA.js.br +0 -0
  403. package/build/client/_app/immutable/chunks/Dg0A8wPA.js.gz +0 -0
  404. package/build/client/_app/immutable/chunks/Dm49pcMm.js.br +0 -0
  405. package/build/client/_app/immutable/chunks/Dm49pcMm.js.gz +0 -0
  406. package/build/client/_app/immutable/chunks/DneQQ7D5.js.br +0 -0
  407. package/build/client/_app/immutable/chunks/DneQQ7D5.js.gz +0 -0
  408. package/build/client/_app/immutable/chunks/DoD3q5w5.js.br +0 -0
  409. package/build/client/_app/immutable/chunks/DoD3q5w5.js.gz +0 -0
  410. package/build/client/_app/immutable/chunks/DtXXy94i.js.br +0 -0
  411. package/build/client/_app/immutable/chunks/DtXXy94i.js.gz +0 -0
  412. package/build/client/_app/immutable/chunks/Dy2CFyD6.js +0 -2
  413. package/build/client/_app/immutable/chunks/Dy2CFyD6.js.br +0 -0
  414. package/build/client/_app/immutable/chunks/Dy2CFyD6.js.gz +0 -0
  415. package/build/client/_app/immutable/chunks/DyBwTDJp.js +0 -2
  416. package/build/client/_app/immutable/chunks/DyBwTDJp.js.br +0 -0
  417. package/build/client/_app/immutable/chunks/DyBwTDJp.js.gz +0 -0
  418. package/build/client/_app/immutable/chunks/ILTCveYH.js +0 -1
  419. package/build/client/_app/immutable/chunks/ILTCveYH.js.br +0 -0
  420. package/build/client/_app/immutable/chunks/ILTCveYH.js.gz +0 -0
  421. package/build/client/_app/immutable/chunks/JcG4w2gF.js.br +0 -0
  422. package/build/client/_app/immutable/chunks/JcG4w2gF.js.gz +0 -0
  423. package/build/client/_app/immutable/chunks/UqjCSZmA.js.br +0 -0
  424. package/build/client/_app/immutable/chunks/UqjCSZmA.js.gz +0 -0
  425. package/build/client/_app/immutable/chunks/_YcrHZ6P.js +0 -1
  426. package/build/client/_app/immutable/chunks/_YcrHZ6P.js.br +0 -2
  427. package/build/client/_app/immutable/chunks/_YcrHZ6P.js.gz +0 -0
  428. package/build/client/_app/immutable/chunks/i6ygh6h8.js +0 -1
  429. package/build/client/_app/immutable/chunks/i6ygh6h8.js.br +0 -2
  430. package/build/client/_app/immutable/chunks/i6ygh6h8.js.gz +0 -0
  431. package/build/client/_app/immutable/chunks/oRi68ch3.js +0 -1
  432. package/build/client/_app/immutable/chunks/oRi68ch3.js.br +0 -0
  433. package/build/client/_app/immutable/chunks/oRi68ch3.js.gz +0 -0
  434. package/build/client/_app/immutable/chunks/pPpIn6tB.js.br +0 -0
  435. package/build/client/_app/immutable/chunks/pPpIn6tB.js.gz +0 -0
  436. package/build/client/_app/immutable/chunks/r0sHOWrM.js +0 -1
  437. package/build/client/_app/immutable/chunks/r0sHOWrM.js.br +0 -0
  438. package/build/client/_app/immutable/chunks/r0sHOWrM.js.gz +0 -0
  439. package/build/client/_app/immutable/chunks/wcTQUnZO.js.br +0 -0
  440. package/build/client/_app/immutable/chunks/wcTQUnZO.js.gz +0 -0
  441. package/build/client/_app/immutable/entry/app.DdO0gkPg.js +0 -2
  442. package/build/client/_app/immutable/entry/app.DdO0gkPg.js.br +0 -0
  443. package/build/client/_app/immutable/entry/app.DdO0gkPg.js.gz +0 -0
  444. package/build/client/_app/immutable/entry/start.eAjDq0c9.js +0 -1
  445. package/build/client/_app/immutable/entry/start.eAjDq0c9.js.br +0 -2
  446. package/build/client/_app/immutable/entry/start.eAjDq0c9.js.gz +0 -0
  447. package/build/client/_app/immutable/nodes/0.nG3DdvIX.js +0 -3
  448. package/build/client/_app/immutable/nodes/0.nG3DdvIX.js.br +0 -0
  449. package/build/client/_app/immutable/nodes/0.nG3DdvIX.js.gz +0 -0
  450. package/build/client/_app/immutable/nodes/1.Ceu0XS6v.js +0 -1
  451. package/build/client/_app/immutable/nodes/1.Ceu0XS6v.js.br +0 -0
  452. package/build/client/_app/immutable/nodes/1.Ceu0XS6v.js.gz +0 -0
  453. package/build/client/_app/immutable/nodes/10.dm2dgy31.js +0 -13
  454. package/build/client/_app/immutable/nodes/10.dm2dgy31.js.br +0 -0
  455. package/build/client/_app/immutable/nodes/10.dm2dgy31.js.gz +0 -0
  456. package/build/client/_app/immutable/nodes/15.D2I0y3ys.js.br +0 -0
  457. package/build/client/_app/immutable/nodes/15.D2I0y3ys.js.gz +0 -0
  458. package/build/client/_app/immutable/nodes/17.CKxGp0E7.js +0 -3
  459. package/build/client/_app/immutable/nodes/17.CKxGp0E7.js.br +0 -0
  460. package/build/client/_app/immutable/nodes/17.CKxGp0E7.js.gz +0 -0
  461. package/build/client/_app/immutable/nodes/18.DHJxyQpw.js +0 -11
  462. package/build/client/_app/immutable/nodes/18.DHJxyQpw.js.br +0 -0
  463. package/build/client/_app/immutable/nodes/18.DHJxyQpw.js.gz +0 -0
  464. package/build/client/_app/immutable/nodes/2.DesR2keg.js +0 -35
  465. package/build/client/_app/immutable/nodes/2.DesR2keg.js.br +0 -0
  466. package/build/client/_app/immutable/nodes/2.DesR2keg.js.gz +0 -0
  467. package/build/client/_app/immutable/nodes/3.BUKrXGR_.js +0 -1
  468. package/build/client/_app/immutable/nodes/3.BUKrXGR_.js.br +0 -0
  469. package/build/client/_app/immutable/nodes/3.BUKrXGR_.js.gz +0 -0
  470. package/build/client/_app/immutable/nodes/4.C8VxVbVn.js.br +0 -0
  471. package/build/client/_app/immutable/nodes/4.C8VxVbVn.js.gz +0 -0
  472. package/build/client/_app/immutable/nodes/5.BjrnKYaA.js.br +0 -0
  473. package/build/client/_app/immutable/nodes/5.BjrnKYaA.js.gz +0 -0
  474. package/build/client/_app/immutable/nodes/6.C_hEzPrU.js +0 -1
  475. package/build/client/_app/immutable/nodes/6.C_hEzPrU.js.br +0 -0
  476. package/build/client/_app/immutable/nodes/6.C_hEzPrU.js.gz +0 -0
  477. package/build/client/_app/immutable/nodes/8.BTYaOiDC.js +0 -2
  478. package/build/client/_app/immutable/nodes/8.BTYaOiDC.js.br +0 -0
  479. package/build/client/_app/immutable/nodes/8.BTYaOiDC.js.gz +0 -0
  480. package/build/client/_app/immutable/nodes/9.DVspuDYY.js +0 -2
  481. package/build/client/_app/immutable/nodes/9.DVspuDYY.js.br +0 -0
  482. package/build/client/_app/immutable/nodes/9.DVspuDYY.js.gz +0 -0
  483. package/build/server/chunks/0-D_duQDWy.js +0 -9
  484. package/build/server/chunks/0-D_duQDWy.js.map +0 -1
  485. package/build/server/chunks/1-C0HdTvXW.js +0 -9
  486. package/build/server/chunks/10-DcfhCR4R.js +0 -9
  487. package/build/server/chunks/10-DcfhCR4R.js.map +0 -1
  488. package/build/server/chunks/15-BQcb9C_y.js +0 -9
  489. package/build/server/chunks/17-DuVCTioj.js +0 -9
  490. package/build/server/chunks/17-DuVCTioj.js.map +0 -1
  491. package/build/server/chunks/18-CT0VFW_p.js +0 -9
  492. package/build/server/chunks/18-CT0VFW_p.js.map +0 -1
  493. package/build/server/chunks/2-DgTzKQAP.js +0 -9
  494. package/build/server/chunks/2-DgTzKQAP.js.map +0 -1
  495. package/build/server/chunks/3-eQMSMzXP.js +0 -9
  496. package/build/server/chunks/3-eQMSMzXP.js.map +0 -1
  497. package/build/server/chunks/4-OaAJtDqP.js +0 -9
  498. package/build/server/chunks/4-OaAJtDqP.js.map +0 -1
  499. package/build/server/chunks/5-CBdMutbv.js +0 -9
  500. package/build/server/chunks/5-CBdMutbv.js.map +0 -1
  501. package/build/server/chunks/6-FoEi7ggm.js +0 -9
  502. package/build/server/chunks/6-FoEi7ggm.js.map +0 -1
  503. package/build/server/chunks/8-B44yRkfX.js +0 -9
  504. package/build/server/chunks/8-B44yRkfX.js.map +0 -1
  505. package/build/server/chunks/9-eUjFpHzI.js +0 -9
  506. package/build/server/chunks/9-eUjFpHzI.js.map +0 -1
  507. package/build/server/chunks/ChatStepTrace-BivoNYOY.js.map +0 -1
  508. package/build/server/chunks/_layout.svelte-B-0DHjLB.js +0 -45
  509. package/build/server/chunks/_layout.svelte-B-0DHjLB.js.map +0 -1
  510. package/build/server/chunks/_layout.svelte-B88WMyk0.js.map +0 -1
  511. package/build/server/chunks/_layout.svelte-wi6o5Ecu.js.map +0 -1
  512. package/build/server/chunks/_page.svelte-57pyhszV.js.map +0 -1
  513. package/build/server/chunks/_page.svelte-BSVJ2d2G.js.map +0 -1
  514. package/build/server/chunks/_page.svelte-BoHt2cpr.js +0 -61
  515. package/build/server/chunks/_page.svelte-BoHt2cpr.js.map +0 -1
  516. package/build/server/chunks/_page.svelte-CeS_21Ho.js.map +0 -1
  517. package/build/server/chunks/_page.svelte-DHSYVLoj.js.map +0 -1
  518. package/build/server/chunks/_page.svelte-DhFNFMnl.js.map +0 -1
  519. package/build/server/chunks/_server.ts-D_eo_OAK.js +0 -139
  520. package/build/server/chunks/_server.ts-D_eo_OAK.js.map +0 -1
  521. package/build/server/chunks/error.svelte-DmKKN1RU.js +0 -15
  522. package/build/server/chunks/source-selection-CcN1cgXc.js.map +0 -1
  523. package/build/server/chunks/trpc-client-D8L6vWf4.js +0 -14
  524. package/build/server/chunks/trpc-client-D8L6vWf4.js.map +0 -1
@@ -1,21 +1,59 @@
1
1
  import 'dotenv/config'
2
2
  import { WebSocketServer } from 'ws'
3
- import { initTRPC } from '@trpc/server'
3
+ import { initTRPC, TRPCError } from '@trpc/server'
4
4
  import { createHTTPServer } from '@trpc/server/adapters/standalone'
5
5
  import { oldServer } from './roleplay/old-server'
6
6
  import { createRolePlayRouter } from './roleplay/router'
7
7
  import { createTestsRouter } from './tests/router'
8
8
  import { createRegionConfigRouter } from './region/router'
9
9
  import { createContext, type TrpcContext } from './lib/trpc-context'
10
- import { cors } from './lib/middleware/cors'
10
+ import { createCors, isAllowedBrowserOrigin } from './lib/middleware/cors'
11
+ import {
12
+ registerManagedBackendRestartHandler,
13
+ scheduleManagedBackendRestart
14
+ } from './lib/backend-restart'
15
+ import { createGitModeRouter, createGitModeService, readGitModeConfig } from './git-mode'
16
+ import { createCommandSupervisor } from './git-mode/command'
17
+ import { RepositoryWriteTracker } from './git-mode/write-tracker'
18
+ import { GIT_MODE_USAGE_BLOCKED_MESSAGE } from '../lib/git-mode/errors'
19
+ import { createRepositoryWriteGateFromEnvironment } from './git-mode/repository-write-gate'
11
20
 
12
21
  const WS_PORT = readPort(['BOSUN_WS_PORT', 'PUBLIC_WS_PORT'], 8080)
13
22
  const HTTP_PORT = readPort(['BOSUN_TRPC_PORT', 'PUBLIC_TRPC_PORT'], 2022)
23
+ const ALLOWED_ORIGIN = process.env.BOSUN_ALLOWED_ORIGIN?.trim() || 'http://localhost:5177'
24
+ const repositoryWrites = new RepositoryWriteTracker()
25
+ const repositoryWriteGate = createRepositoryWriteGateFromEnvironment()
26
+ await repositoryWriteGate.initializeBackend()
27
+ const commandSupervisor = createCommandSupervisor({
28
+ commandStatePath: process.env.BOSUN_GIT_COMMAND_STATE_PATH,
29
+ leaseId: process.env.BOSUN_GIT_LEASE_ID
30
+ })
31
+ const gitModeService = createGitModeService(readGitModeConfig(), {
32
+ scheduleRestart: scheduleManagedBackendRestart,
33
+ settleWrites: () =>
34
+ Promise.all([repositoryWriteGate.lockAndDrain(), repositoryWrites.drain()]).then(
35
+ () => undefined
36
+ ),
37
+ resumeWrites: () => repositoryWriteGate.unlock(),
38
+ runCommand: commandSupervisor.run
39
+ })
14
40
 
15
- const wss = new WebSocketServer({ port: WS_PORT })
41
+ const wss = new WebSocketServer({
42
+ port: WS_PORT,
43
+ verifyClient: ({ origin }, accept) => {
44
+ if (isAllowedBrowserOrigin(origin, ALLOWED_ORIGIN)) {
45
+ accept(true)
46
+ return
47
+ }
48
+ accept(false, 403, 'Bosun rejected an untrusted WebSocket origin.')
49
+ }
50
+ })
16
51
 
17
52
  wss.on('connection', (ws) => {
18
- void oldServer(ws).catch((error: unknown) => {
53
+ void oldServer(ws, {
54
+ isUsageBlocked: () => shuttingDown || gitModeService.isUsageBlocked(),
55
+ trackRepositoryOperation: (operation) => repositoryWrites.track(operation)
56
+ }).catch((error: unknown) => {
19
57
  console.error(error)
20
58
  })
21
59
  })
@@ -24,17 +62,31 @@ console.log(`WebSocket server started on ws://localhost:${WS_PORT}`)
24
62
 
25
63
  // ref: https://github.com/trpc/trpc/blob/main/examples/standalone-server/src/server.ts
26
64
  const t = initTRPC.context<TrpcContext>().create()
65
+ const usageGuard = t.middleware(({ next }) => {
66
+ if (shuttingDown || gitModeService.isUsageBlocked()) {
67
+ throw new TRPCError({
68
+ code: 'FORBIDDEN',
69
+ message: GIT_MODE_USAGE_BLOCKED_MESSAGE
70
+ })
71
+ }
72
+ return repositoryWrites.track(() => Promise.resolve(next()))
73
+ })
74
+ const regularT: typeof t = {
75
+ ...t,
76
+ procedure: t.procedure.use(usageGuard)
77
+ }
27
78
 
28
79
  const appRouter = t.router({
29
- agents: createRolePlayRouter(t),
30
- tests: createTestsRouter(t),
31
- regionConfig: createRegionConfigRouter(t)
80
+ agents: createRolePlayRouter(regularT),
81
+ tests: createTestsRouter(regularT),
82
+ regionConfig: createRegionConfigRouter(regularT),
83
+ gitMode: createGitModeRouter(t, gitModeService)
32
84
  })
33
85
 
34
86
  export type AppRouter = typeof appRouter
35
87
 
36
88
  const server = createHTTPServer({
37
- middleware: cors,
89
+ middleware: createCors(ALLOWED_ORIGIN),
38
90
  router: appRouter,
39
91
  createContext,
40
92
  onError: ({ error }) => {
@@ -46,12 +98,59 @@ server.listen(HTTP_PORT)
46
98
 
47
99
  console.log(`tRPC server started at http://localhost:${HTTP_PORT}`)
48
100
 
49
- process.on('SIGINT', shutdown)
50
- process.on('SIGTERM', shutdown)
101
+ process.on('SIGINT', () => void shutdown())
102
+ process.on('SIGTERM', () => void shutdown())
103
+ registerManagedBackendRestartHandler((exitCode) => shutdown(exitCode))
104
+
105
+ let shuttingDown = false
106
+ async function shutdown(exitCode = 0) {
107
+ if (shuttingDown) return
108
+ shuttingDown = true
109
+ repositoryWrites.close()
110
+
111
+ // Close command admission before awaiting network teardown: an already accepted request must
112
+ // not start a new repository process while shutdown is draining the current ones.
113
+ const commandShutdown = commandSupervisor.shutdown()
114
+ const results = await Promise.allSettled([
115
+ closeHttpServer(),
116
+ closeWebSocketServer(),
117
+ gitModeService.dispose(),
118
+ repositoryWrites.drain(),
119
+ repositoryWriteGate.lockAndDrain(),
120
+ commandShutdown
121
+ ])
122
+ // Disposal waits for any activation completion, including a no-op path that can briefly release
123
+ // the gate. Take the shutdown lock last so no in-flight completion can reopen frontend writes.
124
+ results.push(...(await Promise.allSettled([repositoryWriteGate.lockAndDrain()])))
125
+ const failures = results.filter((result): result is PromiseRejectedResult => {
126
+ return result.status === 'rejected'
127
+ })
128
+ if (failures.length > 0) {
129
+ for (const failure of failures) console.error('[Bosun shutdown]', failure.reason)
130
+ process.exit(1)
131
+ }
132
+
133
+ process.exit(exitCode)
134
+ }
135
+
136
+ function closeHttpServer(): Promise<void> {
137
+ return new Promise((resolve, reject) => {
138
+ server.close((error) => {
139
+ if (error && !isNodeError(error, 'ERR_SERVER_NOT_RUNNING')) reject(error)
140
+ else resolve()
141
+ })
142
+ server.closeAllConnections?.()
143
+ })
144
+ }
51
145
 
52
- function shutdown() {
53
- server.close()
54
- wss.close()
146
+ function closeWebSocketServer(): Promise<void> {
147
+ for (const client of wss.clients) client.terminate()
148
+ return new Promise((resolve, reject) => {
149
+ wss.close((error) => {
150
+ if (error && !isNodeError(error, 'ERR_SERVER_NOT_RUNNING')) reject(error)
151
+ else resolve()
152
+ })
153
+ })
55
154
  }
56
155
 
57
156
  function readPort(names: string[], fallback: number): number {
@@ -68,3 +167,7 @@ function readPort(names: string[], fallback: number): number {
68
167
 
69
168
  return fallback
70
169
  }
170
+
171
+ function isNodeError(error: unknown, code: string): boolean {
172
+ return error instanceof Error && 'code' in error && error.code === code
173
+ }
@@ -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(
@@ -936,7 +950,7 @@ export async function runPromptTests(
936
950
  }
937
951
  return body
938
952
  }
939
- void queueReportCompression(outPath).catch((error) => {
953
+ await queueReportCompression(outPath).catch((error) => {
940
954
  console.error(`Failed to compress completed test report ${path.basename(outPath)}`, error)
941
955
  })
942
956
 
@@ -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
+ })