@recombine-ai/bosun 0.11.1 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (403) hide show
  1. package/build/client/_app/immutable/assets/0.CH-WU4JN.css +1 -0
  2. package/build/client/_app/immutable/assets/0.CH-WU4JN.css.br +0 -0
  3. package/build/client/_app/immutable/assets/0.CH-WU4JN.css.gz +0 -0
  4. package/build/client/_app/immutable/assets/{10.BX46PfEJ.css → 10.DIVePEYf.css} +1 -1
  5. package/build/client/_app/immutable/assets/10.DIVePEYf.css.br +0 -0
  6. package/build/client/_app/immutable/assets/10.DIVePEYf.css.gz +0 -0
  7. package/build/client/_app/immutable/chunks/{AocOovHL.js → 07xNIa59.js} +2 -2
  8. package/build/client/_app/immutable/chunks/07xNIa59.js.br +0 -0
  9. package/build/client/_app/immutable/chunks/07xNIa59.js.gz +0 -0
  10. package/build/client/_app/immutable/chunks/BDl2QuyN.js +1 -0
  11. package/build/client/_app/immutable/chunks/BDl2QuyN.js.br +0 -0
  12. package/build/client/_app/immutable/chunks/BDl2QuyN.js.gz +0 -0
  13. package/build/client/_app/immutable/chunks/{CqxCTxvJ.js → BI42X8h_.js} +1 -1
  14. package/build/client/_app/immutable/chunks/BI42X8h_.js.br +0 -0
  15. package/build/client/_app/immutable/chunks/BI42X8h_.js.gz +0 -0
  16. package/build/client/_app/immutable/chunks/BJTwdUYr.js +1 -0
  17. package/build/client/_app/immutable/chunks/BJTwdUYr.js.br +0 -0
  18. package/build/client/_app/immutable/chunks/BJTwdUYr.js.gz +0 -0
  19. package/build/client/_app/immutable/chunks/BLu86SbA.js +1 -0
  20. package/build/client/_app/immutable/chunks/BLu86SbA.js.br +0 -0
  21. package/build/client/_app/immutable/chunks/BLu86SbA.js.gz +0 -0
  22. package/build/client/_app/immutable/chunks/BNifnXkO.js +1 -0
  23. package/build/client/_app/immutable/chunks/BNifnXkO.js.br +2 -0
  24. package/build/client/_app/immutable/chunks/BNifnXkO.js.gz +0 -0
  25. package/build/client/_app/immutable/chunks/{BM0jKynj.js → BPNEONy5.js} +1 -1
  26. package/build/client/_app/immutable/chunks/BPNEONy5.js.br +0 -0
  27. package/build/client/_app/immutable/chunks/BPNEONy5.js.gz +0 -0
  28. package/build/client/_app/immutable/chunks/B_K7D6rX.js +2 -0
  29. package/build/client/_app/immutable/chunks/B_K7D6rX.js.br +0 -0
  30. package/build/client/_app/immutable/chunks/B_K7D6rX.js.gz +0 -0
  31. package/build/client/_app/immutable/chunks/Bj8jfBTi.js +1 -0
  32. package/build/client/_app/immutable/chunks/Bj8jfBTi.js.br +0 -0
  33. package/build/client/_app/immutable/chunks/Bj8jfBTi.js.gz +0 -0
  34. package/build/client/_app/immutable/chunks/{BR5f5i_E.js → BoPSJr4b.js} +1 -1
  35. package/build/client/_app/immutable/chunks/BoPSJr4b.js.br +0 -0
  36. package/build/client/_app/immutable/chunks/BoPSJr4b.js.gz +0 -0
  37. package/build/client/_app/immutable/chunks/BsDZe4N6.js +1 -0
  38. package/build/client/_app/immutable/chunks/BsDZe4N6.js.br +0 -0
  39. package/build/client/_app/immutable/chunks/BsDZe4N6.js.gz +0 -0
  40. package/build/client/_app/immutable/chunks/{CXo2BVs0.js → Buo-57fF.js} +1 -1
  41. package/build/client/_app/immutable/chunks/Buo-57fF.js.br +0 -0
  42. package/build/client/_app/immutable/chunks/Buo-57fF.js.gz +0 -0
  43. package/build/client/_app/immutable/chunks/{C-whEtKd.js → C39ai1md.js} +1 -1
  44. package/build/client/_app/immutable/chunks/C39ai1md.js.br +0 -0
  45. package/build/client/_app/immutable/chunks/C39ai1md.js.gz +0 -0
  46. package/build/client/_app/immutable/chunks/C7he4XPB.js +1 -0
  47. package/build/client/_app/immutable/chunks/C7he4XPB.js.br +0 -0
  48. package/build/client/_app/immutable/chunks/C7he4XPB.js.gz +0 -0
  49. package/build/client/_app/immutable/chunks/{DYhTBLDc.js → CAjPRvNs.js} +2 -2
  50. package/build/client/_app/immutable/chunks/CAjPRvNs.js.br +0 -0
  51. package/build/client/_app/immutable/chunks/CAjPRvNs.js.gz +0 -0
  52. package/build/client/_app/immutable/chunks/CJYOmGlC.js +1 -0
  53. package/build/client/_app/immutable/chunks/CJYOmGlC.js.br +0 -0
  54. package/build/client/_app/immutable/chunks/CJYOmGlC.js.gz +0 -0
  55. package/build/client/_app/immutable/chunks/{UiXQyKcj.js → CMQRtcOV.js} +1 -1
  56. package/build/client/_app/immutable/chunks/CMQRtcOV.js.br +0 -0
  57. package/build/client/_app/immutable/chunks/CMQRtcOV.js.gz +0 -0
  58. package/build/client/_app/immutable/chunks/{HJHYLHDn.js → CPXdAKdv.js} +1 -1
  59. package/build/client/_app/immutable/chunks/CPXdAKdv.js.br +0 -0
  60. package/build/client/_app/immutable/chunks/CPXdAKdv.js.gz +0 -0
  61. package/build/client/_app/immutable/chunks/{DTlGvFHe.js → CVlsAO8k.js} +1 -1
  62. package/build/client/_app/immutable/chunks/CVlsAO8k.js.br +0 -0
  63. package/build/client/_app/immutable/chunks/CVlsAO8k.js.gz +0 -0
  64. package/build/client/_app/immutable/chunks/{DNLcpWO_.js → CX0Ekq8t.js} +1 -1
  65. package/build/client/_app/immutable/chunks/CX0Ekq8t.js.br +0 -0
  66. package/build/client/_app/immutable/chunks/CX0Ekq8t.js.gz +0 -0
  67. package/build/client/_app/immutable/chunks/Ca197UDq.js +1 -0
  68. package/build/client/_app/immutable/chunks/Ca197UDq.js.br +0 -0
  69. package/build/client/_app/immutable/chunks/Ca197UDq.js.gz +0 -0
  70. package/build/client/_app/immutable/chunks/CaHl3dME.js +1 -0
  71. package/build/client/_app/immutable/chunks/CaHl3dME.js.br +0 -0
  72. package/build/client/_app/immutable/chunks/CaHl3dME.js.gz +0 -0
  73. package/build/client/_app/immutable/chunks/Cql_e8Of.js +1 -0
  74. package/build/client/_app/immutable/chunks/Cql_e8Of.js.br +2 -0
  75. package/build/client/_app/immutable/chunks/Cql_e8Of.js.gz +0 -0
  76. package/build/client/_app/immutable/chunks/{Dv8m1cTI.js → CxLomzuE.js} +1 -1
  77. package/build/client/_app/immutable/chunks/CxLomzuE.js.br +0 -0
  78. package/build/client/_app/immutable/chunks/{Dv8m1cTI.js.gz → CxLomzuE.js.gz} +0 -0
  79. package/build/client/_app/immutable/chunks/Cyai3SOj.js +1 -0
  80. package/build/client/_app/immutable/chunks/Cyai3SOj.js.br +0 -0
  81. package/build/client/_app/immutable/chunks/Cyai3SOj.js.gz +0 -0
  82. package/build/client/_app/immutable/chunks/{-g1l5aoS.js → CyvQWZoe.js} +4 -4
  83. package/build/client/_app/immutable/chunks/CyvQWZoe.js.br +0 -0
  84. package/build/client/_app/immutable/chunks/CyvQWZoe.js.gz +0 -0
  85. package/build/client/_app/immutable/chunks/CzXNWL4m.js +1 -0
  86. package/build/client/_app/immutable/chunks/CzXNWL4m.js.br +1 -0
  87. package/build/client/_app/immutable/chunks/CzXNWL4m.js.gz +0 -0
  88. package/build/client/_app/immutable/chunks/D-B5X35l.js +1 -0
  89. package/build/client/_app/immutable/chunks/D-B5X35l.js.br +0 -0
  90. package/build/client/_app/immutable/chunks/D-B5X35l.js.gz +0 -0
  91. package/build/client/_app/immutable/chunks/DJriQKD9.js +2 -0
  92. package/build/client/_app/immutable/chunks/DJriQKD9.js.br +0 -0
  93. package/build/client/_app/immutable/chunks/DJriQKD9.js.gz +0 -0
  94. package/build/client/_app/immutable/chunks/{BL8rPcmP.js → DXUXXCDA.js} +1 -1
  95. package/build/client/_app/immutable/chunks/DXUXXCDA.js.br +0 -0
  96. package/build/client/_app/immutable/chunks/DXUXXCDA.js.gz +0 -0
  97. package/build/client/_app/immutable/chunks/{CZLNSMhF.js → Dslf9OwW.js} +10 -10
  98. package/build/client/_app/immutable/chunks/Dslf9OwW.js.br +0 -0
  99. package/build/client/_app/immutable/chunks/Dslf9OwW.js.gz +0 -0
  100. package/build/client/_app/immutable/chunks/QpaC6J6s.js +1 -0
  101. package/build/client/_app/immutable/chunks/QpaC6J6s.js.br +0 -0
  102. package/build/client/_app/immutable/chunks/QpaC6J6s.js.gz +0 -0
  103. package/build/client/_app/immutable/chunks/{HXxIsSOO.js → iAhzu4tv.js} +1 -1
  104. package/build/client/_app/immutable/chunks/iAhzu4tv.js.br +0 -0
  105. package/build/client/_app/immutable/chunks/iAhzu4tv.js.gz +0 -0
  106. package/build/client/_app/immutable/chunks/i_5fJYKS.js +3 -0
  107. package/build/client/_app/immutable/chunks/i_5fJYKS.js.br +0 -0
  108. package/build/client/_app/immutable/chunks/i_5fJYKS.js.gz +0 -0
  109. package/build/client/_app/immutable/chunks/{Cosugfe8.js → nYD9wsyN.js} +1 -1
  110. package/build/client/_app/immutable/chunks/nYD9wsyN.js.br +0 -0
  111. package/build/client/_app/immutable/chunks/nYD9wsyN.js.gz +0 -0
  112. package/build/client/_app/immutable/chunks/{BIpSIzUr.js → uLGP0py1.js} +1 -1
  113. package/build/client/_app/immutable/chunks/uLGP0py1.js.br +0 -0
  114. package/build/client/_app/immutable/chunks/uLGP0py1.js.gz +0 -0
  115. package/build/client/_app/immutable/entry/app.Cr7CMG-3.js +2 -0
  116. package/build/client/_app/immutable/entry/app.Cr7CMG-3.js.br +0 -0
  117. package/build/client/_app/immutable/entry/app.Cr7CMG-3.js.gz +0 -0
  118. package/build/client/_app/immutable/entry/start.CDNd0Bml.js +1 -0
  119. package/build/client/_app/immutable/entry/start.CDNd0Bml.js.br +2 -0
  120. package/build/client/_app/immutable/entry/start.CDNd0Bml.js.gz +0 -0
  121. package/build/client/_app/immutable/nodes/{0.BiYgQci8.js → 0.AyqKXI2h.js} +2 -2
  122. package/build/client/_app/immutable/nodes/0.AyqKXI2h.js.br +0 -0
  123. package/build/client/_app/immutable/nodes/0.AyqKXI2h.js.gz +0 -0
  124. package/build/client/_app/immutable/nodes/1.BRmfm-nN.js +1 -0
  125. package/build/client/_app/immutable/nodes/1.BRmfm-nN.js.br +0 -0
  126. package/build/client/_app/immutable/nodes/1.BRmfm-nN.js.gz +0 -0
  127. package/build/client/_app/immutable/nodes/10.D03jjkr4.js +7 -0
  128. package/build/client/_app/immutable/nodes/10.D03jjkr4.js.br +0 -0
  129. package/build/client/_app/immutable/nodes/10.D03jjkr4.js.gz +0 -0
  130. package/build/client/_app/immutable/nodes/{15.Wmhj7gOy.js → 15.BFy4oAT6.js} +1 -1
  131. package/build/client/_app/immutable/nodes/15.BFy4oAT6.js.br +0 -0
  132. package/build/client/_app/immutable/nodes/15.BFy4oAT6.js.gz +0 -0
  133. package/build/client/_app/immutable/nodes/17.BOYU_WTT.js +3 -0
  134. package/build/client/_app/immutable/nodes/17.BOYU_WTT.js.br +0 -0
  135. package/build/client/_app/immutable/nodes/17.BOYU_WTT.js.gz +0 -0
  136. package/build/client/_app/immutable/nodes/18.Ddx80a4f.js +11 -0
  137. package/build/client/_app/immutable/nodes/18.Ddx80a4f.js.br +0 -0
  138. package/build/client/_app/immutable/nodes/18.Ddx80a4f.js.gz +0 -0
  139. package/build/client/_app/immutable/nodes/{2.DniQTiGZ.js → 2.CZEboQjo.js} +20 -20
  140. package/build/client/_app/immutable/nodes/2.CZEboQjo.js.br +0 -0
  141. package/build/client/_app/immutable/nodes/2.CZEboQjo.js.gz +0 -0
  142. package/build/client/_app/immutable/nodes/3.dAV7FviN.js +1 -0
  143. package/build/client/_app/immutable/nodes/3.dAV7FviN.js.br +0 -0
  144. package/build/client/_app/immutable/nodes/3.dAV7FviN.js.gz +0 -0
  145. package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js +1 -0
  146. package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js.br +0 -0
  147. package/build/client/_app/immutable/nodes/4.Cm_vMAEA.js.gz +0 -0
  148. package/build/client/_app/immutable/nodes/5.DPWuTlaI.js +1 -0
  149. package/build/client/_app/immutable/nodes/5.DPWuTlaI.js.br +0 -0
  150. package/build/client/_app/immutable/nodes/5.DPWuTlaI.js.gz +0 -0
  151. package/build/client/_app/immutable/nodes/6.DY-NLaRW.js +1 -0
  152. package/build/client/_app/immutable/nodes/6.DY-NLaRW.js.br +0 -0
  153. package/build/client/_app/immutable/nodes/6.DY-NLaRW.js.gz +0 -0
  154. package/build/client/_app/immutable/nodes/{8.2oZsfzo8.js → 8.Chuqhf8W.js} +1 -1
  155. package/build/client/_app/immutable/nodes/8.Chuqhf8W.js.br +0 -0
  156. package/build/client/_app/immutable/nodes/8.Chuqhf8W.js.gz +0 -0
  157. package/build/client/_app/immutable/nodes/9.BOEM8f6B.js +2 -0
  158. package/build/client/_app/immutable/nodes/9.BOEM8f6B.js.br +0 -0
  159. package/build/client/_app/immutable/nodes/9.BOEM8f6B.js.gz +0 -0
  160. package/build/client/_app/version.json +1 -1
  161. package/build/client/_app/version.json.br +0 -0
  162. package/build/client/_app/version.json.gz +0 -0
  163. package/build/server/chunks/0-TQkJBtAu.js +9 -0
  164. package/build/server/chunks/0-TQkJBtAu.js.map +1 -0
  165. package/build/server/chunks/1-CKxKlw1l.js +9 -0
  166. package/build/server/chunks/{1-BQkMpHdq.js.map → 1-CKxKlw1l.js.map} +1 -1
  167. package/build/server/chunks/10-Bly5Nk7M.js +9 -0
  168. package/build/server/chunks/10-Bly5Nk7M.js.map +1 -0
  169. package/build/server/chunks/{15-CZBxPMAc.js → 15-BMEAVslz.js} +2 -2
  170. package/build/server/chunks/{15-CZBxPMAc.js.map → 15-BMEAVslz.js.map} +1 -1
  171. package/build/server/chunks/17-C8CYQOYP.js +9 -0
  172. package/build/server/chunks/17-C8CYQOYP.js.map +1 -0
  173. package/build/server/chunks/18-CtXVGBbS.js +9 -0
  174. package/build/server/chunks/18-CtXVGBbS.js.map +1 -0
  175. package/build/server/chunks/2-DvD0dAh0.js +9 -0
  176. package/build/server/chunks/2-DvD0dAh0.js.map +1 -0
  177. package/build/server/chunks/3-DBzpRDK8.js +9 -0
  178. package/build/server/chunks/3-DBzpRDK8.js.map +1 -0
  179. package/build/server/chunks/4-BUNytyC2.js +9 -0
  180. package/build/server/chunks/4-BUNytyC2.js.map +1 -0
  181. package/build/server/chunks/5-DNo8_tFS.js +9 -0
  182. package/build/server/chunks/{5-C7KuaSU9.js.map → 5-DNo8_tFS.js.map} +1 -1
  183. package/build/server/chunks/6-DUMdIfbf.js +9 -0
  184. package/build/server/chunks/6-DUMdIfbf.js.map +1 -0
  185. package/build/server/chunks/8-DxTJ555u.js +9 -0
  186. package/build/server/chunks/8-DxTJ555u.js.map +1 -0
  187. package/build/server/chunks/9-B4-5k50d.js +9 -0
  188. package/build/server/chunks/9-B4-5k50d.js.map +1 -0
  189. package/build/server/chunks/{AgentSidebar-XdGj1sqT.js → AgentSidebar-DvIlda-E.js} +5 -6
  190. package/build/server/chunks/AgentSidebar-DvIlda-E.js.map +1 -0
  191. package/build/server/chunks/{ChatStepTrace-DnvGtFpb.js → ChatStepTrace-CqqIjRLU.js} +41 -7
  192. package/build/server/chunks/ChatStepTrace-CqqIjRLU.js.map +1 -0
  193. package/build/server/chunks/MockGenerator-GEpXTgX0.js +117 -0
  194. package/build/server/chunks/MockGenerator-GEpXTgX0.js.map +1 -0
  195. package/build/server/chunks/{_layout.svelte-C5Y9nSXi.js → _layout.svelte-B-CkD4a0.js} +12 -6
  196. package/build/server/chunks/_layout.svelte-B-CkD4a0.js.map +1 -0
  197. package/build/server/chunks/{_layout.svelte-_oqm8kmr.js → _layout.svelte-BBFCF-xw.js} +7 -3
  198. package/build/server/chunks/_layout.svelte-BBFCF-xw.js.map +1 -0
  199. package/build/server/chunks/{_layout.svelte-C7JgNCIu.js → _layout.svelte-cPhmW5bZ.js} +3 -1
  200. package/build/server/chunks/_layout.svelte-cPhmW5bZ.js.map +1 -0
  201. package/build/server/chunks/{_page.svelte-QlYvVgTj.js → _page.svelte-0J2xaeyC.js} +70 -15
  202. package/build/server/chunks/_page.svelte-0J2xaeyC.js.map +1 -0
  203. package/build/server/chunks/{_page.svelte-BpRksNEG.js → _page.svelte-BgoX8Y6N.js} +2 -2
  204. package/build/server/chunks/{_page.svelte-BpRksNEG.js.map → _page.svelte-BgoX8Y6N.js.map} +1 -1
  205. package/build/server/chunks/{_page.svelte-bkzbqII3.js → _page.svelte-C_S_qUgZ.js} +479 -71
  206. package/build/server/chunks/_page.svelte-C_S_qUgZ.js.map +1 -0
  207. package/build/server/chunks/{_page.svelte-DDpBtd3X.js → _page.svelte-CqGOvPRO.js} +3 -4
  208. package/build/server/chunks/_page.svelte-CqGOvPRO.js.map +1 -0
  209. package/build/server/chunks/{_page.svelte-DOhPbizt.js → _page.svelte-Cxh9_vUl.js} +34 -22
  210. package/build/server/chunks/_page.svelte-Cxh9_vUl.js.map +1 -0
  211. package/build/server/chunks/{_page.svelte-CEhhEdMi.js → _page.svelte-zOI_s_3J.js} +122 -71
  212. package/build/server/chunks/_page.svelte-zOI_s_3J.js.map +1 -0
  213. package/build/server/chunks/conversation-view-memory-BZguFlTt.js +20 -0
  214. package/build/server/chunks/conversation-view-memory-BZguFlTt.js.map +1 -0
  215. package/build/server/chunks/log-content.svelte-CfQzj-v_.js.map +1 -1
  216. package/build/server/chunks/{client-DzvEkq51.js → source-selection-CcN1cgXc.js} +122 -117
  217. package/build/server/chunks/source-selection-CcN1cgXc.js.map +1 -0
  218. package/build/server/index.js +1 -1
  219. package/build/server/index.js.map +1 -1
  220. package/build/server/manifest.js +14 -14
  221. package/build/server/manifest.js.map +1 -1
  222. package/package.json +3 -3
  223. package/src/backend/lib/get-agent-factories.spec.ts +59 -0
  224. package/src/backend/lib/get-agent-factories.ts +31 -1
  225. package/src/backend/lib/voice-agent-lifecycle.test.ts +12 -8
  226. package/src/backend/lib/voice-agent-lifecycle.ts +2 -1
  227. package/src/backend/roleplay/agents.ts +18 -0
  228. package/src/backend/roleplay/router.ts +8 -3
  229. package/src/backend/tests/academy.ts +151 -93
  230. package/src/backend/tests/analyze.ts +0 -3
  231. package/src/backend/tests/data.spec.ts +139 -0
  232. package/src/backend/tests/data.ts +296 -65
  233. package/src/backend/tests/emulate-agents.ts +45 -45
  234. package/src/backend/tests/openai-prompt-cache.ts +68 -0
  235. package/src/backend/tests/promptfoo-runtime.spec.ts +41 -0
  236. package/src/backend/tests/promptfoo-runtime.ts +27 -0
  237. package/src/backend/tests/report-files.spec.ts +42 -0
  238. package/src/backend/tests/report-files.ts +101 -0
  239. package/src/backend/tests/router.ts +7 -1
  240. package/build/client/_app/immutable/assets/0.Dz9hcoGY.css +0 -1
  241. package/build/client/_app/immutable/assets/0.Dz9hcoGY.css.br +0 -0
  242. package/build/client/_app/immutable/assets/0.Dz9hcoGY.css.gz +0 -0
  243. package/build/client/_app/immutable/assets/10.BX46PfEJ.css.br +0 -0
  244. package/build/client/_app/immutable/assets/10.BX46PfEJ.css.gz +0 -0
  245. package/build/client/_app/immutable/chunks/-g1l5aoS.js.br +0 -0
  246. package/build/client/_app/immutable/chunks/-g1l5aoS.js.gz +0 -0
  247. package/build/client/_app/immutable/chunks/7E8m6dwk.js +0 -1
  248. package/build/client/_app/immutable/chunks/7E8m6dwk.js.br +0 -0
  249. package/build/client/_app/immutable/chunks/7E8m6dwk.js.gz +0 -0
  250. package/build/client/_app/immutable/chunks/7qRqthsa.js +0 -1
  251. package/build/client/_app/immutable/chunks/7qRqthsa.js.br +0 -2
  252. package/build/client/_app/immutable/chunks/7qRqthsa.js.gz +0 -0
  253. package/build/client/_app/immutable/chunks/AocOovHL.js.br +0 -0
  254. package/build/client/_app/immutable/chunks/AocOovHL.js.gz +0 -0
  255. package/build/client/_app/immutable/chunks/B0iEglSe.js +0 -1
  256. package/build/client/_app/immutable/chunks/B0iEglSe.js.br +0 -0
  257. package/build/client/_app/immutable/chunks/B0iEglSe.js.gz +0 -0
  258. package/build/client/_app/immutable/chunks/BIpSIzUr.js.br +0 -0
  259. package/build/client/_app/immutable/chunks/BIpSIzUr.js.gz +0 -0
  260. package/build/client/_app/immutable/chunks/BL8rPcmP.js.br +0 -0
  261. package/build/client/_app/immutable/chunks/BL8rPcmP.js.gz +0 -0
  262. package/build/client/_app/immutable/chunks/BM0jKynj.js.br +0 -0
  263. package/build/client/_app/immutable/chunks/BM0jKynj.js.gz +0 -0
  264. package/build/client/_app/immutable/chunks/BR5f5i_E.js.br +0 -0
  265. package/build/client/_app/immutable/chunks/BR5f5i_E.js.gz +0 -0
  266. package/build/client/_app/immutable/chunks/BTvePR6r.js +0 -1
  267. package/build/client/_app/immutable/chunks/BTvePR6r.js.br +0 -2
  268. package/build/client/_app/immutable/chunks/BTvePR6r.js.gz +0 -0
  269. package/build/client/_app/immutable/chunks/Bcu62lFq.js +0 -1
  270. package/build/client/_app/immutable/chunks/Bcu62lFq.js.br +0 -0
  271. package/build/client/_app/immutable/chunks/Bcu62lFq.js.gz +0 -0
  272. package/build/client/_app/immutable/chunks/C-whEtKd.js.br +0 -2
  273. package/build/client/_app/immutable/chunks/C-whEtKd.js.gz +0 -0
  274. package/build/client/_app/immutable/chunks/C1WqE8CP.js +0 -1
  275. package/build/client/_app/immutable/chunks/C1WqE8CP.js.br +0 -2
  276. package/build/client/_app/immutable/chunks/C1WqE8CP.js.gz +0 -0
  277. package/build/client/_app/immutable/chunks/C4SnSfql.js +0 -1
  278. package/build/client/_app/immutable/chunks/C4SnSfql.js.br +0 -0
  279. package/build/client/_app/immutable/chunks/C4SnSfql.js.gz +0 -0
  280. package/build/client/_app/immutable/chunks/CSv1VDXg.js +0 -1
  281. package/build/client/_app/immutable/chunks/CSv1VDXg.js.br +0 -0
  282. package/build/client/_app/immutable/chunks/CSv1VDXg.js.gz +0 -0
  283. package/build/client/_app/immutable/chunks/CXo2BVs0.js.br +0 -0
  284. package/build/client/_app/immutable/chunks/CXo2BVs0.js.gz +0 -0
  285. package/build/client/_app/immutable/chunks/CZLNSMhF.js.br +0 -0
  286. package/build/client/_app/immutable/chunks/CZLNSMhF.js.gz +0 -0
  287. package/build/client/_app/immutable/chunks/Cosugfe8.js.br +0 -0
  288. package/build/client/_app/immutable/chunks/Cosugfe8.js.gz +0 -0
  289. package/build/client/_app/immutable/chunks/CqxCTxvJ.js.br +0 -0
  290. package/build/client/_app/immutable/chunks/CqxCTxvJ.js.gz +0 -0
  291. package/build/client/_app/immutable/chunks/CxLG58rb.js +0 -1
  292. package/build/client/_app/immutable/chunks/CxLG58rb.js.br +0 -0
  293. package/build/client/_app/immutable/chunks/CxLG58rb.js.gz +0 -0
  294. package/build/client/_app/immutable/chunks/D-puyTnN.js +0 -1
  295. package/build/client/_app/immutable/chunks/D-puyTnN.js.br +0 -0
  296. package/build/client/_app/immutable/chunks/D-puyTnN.js.gz +0 -0
  297. package/build/client/_app/immutable/chunks/D4obUD_s.js +0 -1
  298. package/build/client/_app/immutable/chunks/D4obUD_s.js.br +0 -0
  299. package/build/client/_app/immutable/chunks/D4obUD_s.js.gz +0 -0
  300. package/build/client/_app/immutable/chunks/D8e7SQh9.js +0 -1
  301. package/build/client/_app/immutable/chunks/D8e7SQh9.js.br +0 -0
  302. package/build/client/_app/immutable/chunks/D8e7SQh9.js.gz +0 -0
  303. package/build/client/_app/immutable/chunks/DNLcpWO_.js.br +0 -0
  304. package/build/client/_app/immutable/chunks/DNLcpWO_.js.gz +0 -0
  305. package/build/client/_app/immutable/chunks/DTlGvFHe.js.br +0 -0
  306. package/build/client/_app/immutable/chunks/DTlGvFHe.js.gz +0 -0
  307. package/build/client/_app/immutable/chunks/DYhTBLDc.js.br +0 -0
  308. package/build/client/_app/immutable/chunks/DYhTBLDc.js.gz +0 -0
  309. package/build/client/_app/immutable/chunks/DlZKMmqG.js +0 -4
  310. package/build/client/_app/immutable/chunks/DlZKMmqG.js.br +0 -0
  311. package/build/client/_app/immutable/chunks/DlZKMmqG.js.gz +0 -0
  312. package/build/client/_app/immutable/chunks/Dv0gjm25.js +0 -1
  313. package/build/client/_app/immutable/chunks/Dv0gjm25.js.br +0 -0
  314. package/build/client/_app/immutable/chunks/Dv0gjm25.js.gz +0 -0
  315. package/build/client/_app/immutable/chunks/Dv8m1cTI.js.br +0 -0
  316. package/build/client/_app/immutable/chunks/HJHYLHDn.js.br +0 -0
  317. package/build/client/_app/immutable/chunks/HJHYLHDn.js.gz +0 -0
  318. package/build/client/_app/immutable/chunks/HXxIsSOO.js.br +0 -0
  319. package/build/client/_app/immutable/chunks/HXxIsSOO.js.gz +0 -0
  320. package/build/client/_app/immutable/chunks/UiXQyKcj.js.br +0 -0
  321. package/build/client/_app/immutable/chunks/UiXQyKcj.js.gz +0 -0
  322. package/build/client/_app/immutable/chunks/_yZg6Sf-.js +0 -2
  323. package/build/client/_app/immutable/chunks/_yZg6Sf-.js.br +0 -0
  324. package/build/client/_app/immutable/chunks/_yZg6Sf-.js.gz +0 -0
  325. package/build/client/_app/immutable/chunks/ks45gSgq.js +0 -1
  326. package/build/client/_app/immutable/chunks/ks45gSgq.js.br +0 -0
  327. package/build/client/_app/immutable/chunks/ks45gSgq.js.gz +0 -0
  328. package/build/client/_app/immutable/entry/app.qxHHRD2v.js +0 -2
  329. package/build/client/_app/immutable/entry/app.qxHHRD2v.js.br +0 -0
  330. package/build/client/_app/immutable/entry/app.qxHHRD2v.js.gz +0 -0
  331. package/build/client/_app/immutable/entry/start.CSxD7zE8.js +0 -1
  332. package/build/client/_app/immutable/entry/start.CSxD7zE8.js.br +0 -2
  333. package/build/client/_app/immutable/entry/start.CSxD7zE8.js.gz +0 -0
  334. package/build/client/_app/immutable/nodes/0.BiYgQci8.js.br +0 -0
  335. package/build/client/_app/immutable/nodes/0.BiYgQci8.js.gz +0 -0
  336. package/build/client/_app/immutable/nodes/1.CShtqUFK.js +0 -1
  337. package/build/client/_app/immutable/nodes/1.CShtqUFK.js.br +0 -0
  338. package/build/client/_app/immutable/nodes/1.CShtqUFK.js.gz +0 -0
  339. package/build/client/_app/immutable/nodes/10.dE-XOJgk.js +0 -7
  340. package/build/client/_app/immutable/nodes/10.dE-XOJgk.js.br +0 -0
  341. package/build/client/_app/immutable/nodes/10.dE-XOJgk.js.gz +0 -0
  342. package/build/client/_app/immutable/nodes/15.Wmhj7gOy.js.br +0 -0
  343. package/build/client/_app/immutable/nodes/15.Wmhj7gOy.js.gz +0 -0
  344. package/build/client/_app/immutable/nodes/17.BwzZwCD0.js +0 -3
  345. package/build/client/_app/immutable/nodes/17.BwzZwCD0.js.br +0 -0
  346. package/build/client/_app/immutable/nodes/17.BwzZwCD0.js.gz +0 -0
  347. package/build/client/_app/immutable/nodes/18.BUMDuaQF.js +0 -11
  348. package/build/client/_app/immutable/nodes/18.BUMDuaQF.js.br +0 -0
  349. package/build/client/_app/immutable/nodes/18.BUMDuaQF.js.gz +0 -0
  350. package/build/client/_app/immutable/nodes/2.DniQTiGZ.js.br +0 -0
  351. package/build/client/_app/immutable/nodes/2.DniQTiGZ.js.gz +0 -0
  352. package/build/client/_app/immutable/nodes/3.BIO1e-qq.js +0 -1
  353. package/build/client/_app/immutable/nodes/3.BIO1e-qq.js.br +0 -0
  354. package/build/client/_app/immutable/nodes/3.BIO1e-qq.js.gz +0 -0
  355. package/build/client/_app/immutable/nodes/4.BwfVTawo.js +0 -1
  356. package/build/client/_app/immutable/nodes/4.BwfVTawo.js.br +0 -0
  357. package/build/client/_app/immutable/nodes/4.BwfVTawo.js.gz +0 -0
  358. package/build/client/_app/immutable/nodes/5.DdodCQcX.js +0 -1
  359. package/build/client/_app/immutable/nodes/5.DdodCQcX.js.br +0 -0
  360. package/build/client/_app/immutable/nodes/5.DdodCQcX.js.gz +0 -0
  361. package/build/client/_app/immutable/nodes/6.EIjYVI_W.js +0 -1
  362. package/build/client/_app/immutable/nodes/6.EIjYVI_W.js.br +0 -0
  363. package/build/client/_app/immutable/nodes/6.EIjYVI_W.js.gz +0 -0
  364. package/build/client/_app/immutable/nodes/8.2oZsfzo8.js.br +0 -0
  365. package/build/client/_app/immutable/nodes/8.2oZsfzo8.js.gz +0 -0
  366. package/build/client/_app/immutable/nodes/9.CjWG6Ha9.js +0 -2
  367. package/build/client/_app/immutable/nodes/9.CjWG6Ha9.js.br +0 -0
  368. package/build/client/_app/immutable/nodes/9.CjWG6Ha9.js.gz +0 -0
  369. package/build/server/chunks/0-S8VzkGgQ.js +0 -9
  370. package/build/server/chunks/0-S8VzkGgQ.js.map +0 -1
  371. package/build/server/chunks/1-BQkMpHdq.js +0 -9
  372. package/build/server/chunks/10-D1k3_nkw.js +0 -9
  373. package/build/server/chunks/10-D1k3_nkw.js.map +0 -1
  374. package/build/server/chunks/17-B72NO3Iu.js +0 -9
  375. package/build/server/chunks/17-B72NO3Iu.js.map +0 -1
  376. package/build/server/chunks/18-4kgQqgz-.js +0 -9
  377. package/build/server/chunks/18-4kgQqgz-.js.map +0 -1
  378. package/build/server/chunks/2-DO8jW3Pl.js +0 -9
  379. package/build/server/chunks/2-DO8jW3Pl.js.map +0 -1
  380. package/build/server/chunks/3-0_f3eZZi.js +0 -9
  381. package/build/server/chunks/3-0_f3eZZi.js.map +0 -1
  382. package/build/server/chunks/4-UxjRsyDW.js +0 -9
  383. package/build/server/chunks/4-UxjRsyDW.js.map +0 -1
  384. package/build/server/chunks/5-C7KuaSU9.js +0 -9
  385. package/build/server/chunks/6-C6EG0SXo.js +0 -9
  386. package/build/server/chunks/6-C6EG0SXo.js.map +0 -1
  387. package/build/server/chunks/8-BTSvDZhI.js +0 -9
  388. package/build/server/chunks/8-BTSvDZhI.js.map +0 -1
  389. package/build/server/chunks/9-CepnUatA.js +0 -9
  390. package/build/server/chunks/9-CepnUatA.js.map +0 -1
  391. package/build/server/chunks/AgentSidebar-XdGj1sqT.js.map +0 -1
  392. package/build/server/chunks/ChatStepTrace-DnvGtFpb.js.map +0 -1
  393. package/build/server/chunks/_layout.svelte-C5Y9nSXi.js.map +0 -1
  394. package/build/server/chunks/_layout.svelte-C7JgNCIu.js.map +0 -1
  395. package/build/server/chunks/_layout.svelte-_oqm8kmr.js.map +0 -1
  396. package/build/server/chunks/_page.svelte-CEhhEdMi.js.map +0 -1
  397. package/build/server/chunks/_page.svelte-DDpBtd3X.js.map +0 -1
  398. package/build/server/chunks/_page.svelte-DOhPbizt.js.map +0 -1
  399. package/build/server/chunks/_page.svelte-QlYvVgTj.js.map +0 -1
  400. package/build/server/chunks/_page.svelte-bkzbqII3.js.map +0 -1
  401. package/build/server/chunks/client-DzvEkq51.js.map +0 -1
  402. package/build/server/chunks/source-selection-eA--VEKo.js +0 -124
  403. package/build/server/chunks/source-selection-eA--VEKo.js.map +0 -1
@@ -2,8 +2,9 @@ import { j as escape_html, l as derived, n as ensure_array_like, q as attr_style
2
2
  import { o as onDestroy } from './index-server-DsYEXu5n.js';
3
3
  import { p as page } from './index2-DO4B3D-P.js';
4
4
  import { B as Button } from './Button-BLigKkGG.js';
5
- import { g as getPersistedTelescopeSourceId } from './source-selection-eA--VEKo.js';
5
+ import { g as getPersistedTelescopeSourceId, T as TelescopeClient } from './source-selection-CcN1cgXc.js';
6
6
  import { b as buildResponseLatencySpans, f as findResponseLatencyUserTurn, R as RESPONSE_LATENCY_THRESHOLD_MS } from './latency--f3JYIRg.js';
7
+ import { g as getConversationViewMemory, c as conversationViewMemoryKey } from './conversation-view-memory-BZguFlTt.js';
7
8
  import './state.svelte-DT7cgvTK.js';
8
9
  import './root-CDJwhpIB.js';
9
10
  import './events-BQ65tUJo.js';
@@ -23,6 +24,8 @@ function _page($$renderer, $$props) {
23
24
  const SPEECH_BUBBLE_PLAYBACK_SPAN_MATCH_AFTER_MS = 2500;
24
25
  const SPEECH_BUBBLE_AGENT_PLAYBACK_MATCH_WINDOW_MS = 3e4;
25
26
  const RESPONSE_LATENCY_TRACE_MATCH_TOLERANCE_MS = 250;
27
+ const GUIDE_ROW_LABELS = /* @__PURE__ */ new Set(["VAD", "Playback"]);
28
+ const client = new TelescopeClient(fetch);
26
29
  const BOSUN_COLORS = {
27
30
  blue: "#569cd6",
28
31
  green: "#93FF6D",
@@ -223,26 +226,65 @@ function _page($$renderer, $$props) {
223
226
  }
224
227
  ];
225
228
  const SPAN_RULE_BY_START = new Map(SPAN_RULES.map((rule) => [rule.startEventType, rule]));
226
- getPersistedTelescopeSourceId() ?? "";
227
- let traces = [];
228
- let conversationTraces = [];
229
- let recording = {
230
- exists: false,
231
- alignmentOffsetMs: 0
232
- };
229
+ function emptyRecordingResponse() {
230
+ return {
231
+ exists: false,
232
+ audioUrl: null,
233
+ fileName: null,
234
+ mimeType: null,
235
+ size: null,
236
+ uploadedAt: null,
237
+ alignmentOffsetMs: 0
238
+ };
239
+ }
240
+ const viewMemory = getConversationViewMemory();
241
+ const initialSourceId = getPersistedTelescopeSourceId() ?? "";
242
+ const initialConversationId = page.params.conversationId ?? "";
243
+ const initialMemoryKey = conversationViewMemoryKey(initialConversationId, initialSourceId);
244
+ const initialMemory = viewMemory.latency.get(initialMemoryKey);
245
+ client.setSourceId(initialSourceId || void 0);
246
+ let sourceId = initialSourceId;
247
+ let conversation = initialMemory?.conversation ?? null;
248
+ let traces = initialMemory?.traces ?? [];
249
+ let conversationTraces = initialMemory?.conversationTraces ?? [];
250
+ let dataLoaded = initialMemory?.dataLoaded ?? false;
251
+ let error = initialMemory?.error ?? null;
252
+ let recording = initialMemory?.recording ?? emptyRecordingResponse();
253
+ let recordingLoaded = initialMemory?.recordingLoaded ?? false;
254
+ let recordingError = initialMemory?.recordingError ?? null;
233
255
  let uploadInFlight = false;
234
- let playbackPositionMs = 0;
256
+ let waveform = initialMemory?.waveform ?? null;
257
+ let waveformError = initialMemory?.waveformError ?? null;
258
+ let playbackPositionMs = initialMemory?.playbackPositionMs ?? 0;
235
259
  let playbackPlaying = false;
260
+ let playbackError = null;
261
+ let alignmentSaving = false;
262
+ let alignmentError = null;
263
+ let viewDirty = initialMemory?.viewDirty ?? false;
236
264
  let viewportStartMs = null;
237
265
  let viewportEndMs = null;
266
+ let scrollViewportEl = null;
238
267
  let fileInputEl = null;
239
- let humanSpeechBubbleReservedLevels = 1;
268
+ let humanSpeechBubbleReservedLevels = initialMemory?.humanSpeechBubbleReservedLevels ?? 1;
240
269
  let copyLatencyState = "idle";
270
+ let loadKey = "";
271
+ let playbackFrame = 0;
272
+ let playbackAudioBuffer = initialMemory?.playbackAudioBuffer ?? null;
241
273
  let playbackAudioContext = null;
242
274
  let playbackSourceNode = null;
243
275
  let playbackStartedAtMs = null;
244
276
  let playbackStartedAtContextTime = null;
245
277
  const conversationId = derived(() => page.params.conversationId);
278
+ function buildRecordingApiUrlFor(cid, nextSourceId) {
279
+ const params = new URLSearchParams();
280
+ if (nextSourceId) params.set("source", nextSourceId);
281
+ const search = params.toString();
282
+ return `/traces/api/latency/${encodeURIComponent(cid)}${search ? `?${search}` : ""}`;
283
+ }
284
+ const recordingApiUrl = derived(() => {
285
+ if (!conversationId()) return "";
286
+ return buildRecordingApiUrlFor(conversationId(), sourceId);
287
+ });
246
288
  const timelineOriginMs = derived(() => {
247
289
  let min = Number.POSITIVE_INFINITY;
248
290
  for (const trace of traces) {
@@ -250,20 +292,31 @@ function _page($$renderer, $$props) {
250
292
  min = Math.min(min, trace.atMs);
251
293
  }
252
294
  if (Number.isFinite(min)) return min;
295
+ if (typeof conversation?.createdAt === "number" && Number.isFinite(conversation.createdAt)) {
296
+ return conversation.createdAt;
297
+ }
253
298
  return Number.isFinite(min) ? min : 0;
254
299
  });
255
300
  const callSegments = derived(() => buildCallLatencySegments(traces));
256
301
  const latencyTraceCount = derived(() => traces.length);
257
- const playbackSpeechBySegmentId = derived(() => buildPlaybackSpeechBySegmentId(callSegments()));
258
- const timelineSegments = derived(() => callSegments().map((segment) => {
259
- const speech = playbackSpeechBySegmentId().get(segment.id);
260
- if (!speech || segment.row !== "Playback" || segment.durationMs <= 0) return segment;
261
- return {
262
- ...segment,
263
- speechText: speech.text,
264
- speechTraceLabel: speech.traceLabel
265
- };
266
- }));
302
+ const aiResponses = derived(() => buildAiResponses());
303
+ const playbackSpeechMatches = derived(() => buildPlaybackSpeechMatches(callSegments(), aiResponses()));
304
+ const timelineSegments = derived(() => {
305
+ const playbackSegments = callSegments().map((segment) => {
306
+ const speech = playbackSpeechMatches().bySegmentId.get(segment.id);
307
+ if (!speech || segment.row !== "Playback" || segment.durationMs <= 0) return segment;
308
+ return {
309
+ ...segment,
310
+ speechText: speech.text,
311
+ speechFullText: speech.fullText,
312
+ speechTraceLabel: speech.traceLabel
313
+ };
314
+ });
315
+ return [
316
+ ...playbackSegments,
317
+ ...buildStandaloneWaitSegments(aiResponses(), playbackSpeechMatches().responseIds)
318
+ ];
319
+ });
267
320
  const timelineRows = derived(() => {
268
321
  const grouped = /* @__PURE__ */ new Map();
269
322
  for (const segment of timelineSegments()) {
@@ -285,7 +338,8 @@ function _page($$renderer, $$props) {
285
338
  const waveformRowHeightPx = derived(() => waveformRowVisible() ? TIMELINE_ROW_GAP_PX + WAVEFORM_ROW_HEIGHT_PX : 0);
286
339
  const guideLines = derived(() => buildGuideLines(timelineRows()));
287
340
  const recordingStartMs = derived(() => {
288
- return null;
341
+ if (!waveform) return null;
342
+ return timelineOriginMs() + recording.alignmentOffsetMs;
289
343
  });
290
344
  const timelinePlotHeightPx = derived(() => TIMELINE_RULER_HEIGHT_PX + timelineRowsHeightPx() + traceRowsToBubbleGapPx() + speechBubbleAreaHeightPx() + waveformRowHeightPx());
291
345
  const playbackAbsoluteMs = derived(() => {
@@ -322,6 +376,10 @@ function _page($$renderer, $$props) {
322
376
  min = Math.min(min, segment.start);
323
377
  max = Math.max(max, segment.end);
324
378
  }
379
+ if (waveform && recordingStartMs() != null) {
380
+ min = Math.min(min, recordingStartMs());
381
+ max = Math.max(max, recordingStartMs() + waveform.durationMs);
382
+ }
325
383
  if (!Number.isFinite(min) || !Number.isFinite(max)) return null;
326
384
  if (max <= min) max = min + 1;
327
385
  return { min, max };
@@ -347,7 +405,39 @@ function _page($$renderer, $$props) {
347
405
  if (viewportStartMs == null || viewportEndMs == null) return [];
348
406
  return buildTicks(viewportStartMs, viewportEndMs, timelineOriginMs());
349
407
  });
408
+ function saveViewMemory(key = loadKey) {
409
+ if (!key) return;
410
+ const previous = viewMemory.latency.get(key);
411
+ const memory = {
412
+ conversation,
413
+ traces,
414
+ conversationTraces,
415
+ dataLoaded,
416
+ error,
417
+ recording,
418
+ recordingLoaded,
419
+ recordingError,
420
+ waveform,
421
+ waveformError,
422
+ playbackAudioBuffer,
423
+ playbackPositionMs,
424
+ viewportStartMs,
425
+ viewportEndMs,
426
+ viewportOriginMs: timelineOriginMs(),
427
+ viewDirty,
428
+ humanSpeechBubbleReservedLevels,
429
+ scrollTop: previous?.scrollTop ?? scrollViewportEl?.scrollTop ?? 0,
430
+ scrollLeft: previous?.scrollLeft ?? scrollViewportEl?.scrollLeft ?? 0
431
+ };
432
+ viewMemory.latency.set(key, memory);
433
+ const cid = conversationId();
434
+ const currentSourceId = getPersistedTelescopeSourceId() ?? "";
435
+ if (cid && currentSourceId && key === conversationViewMemoryKey(cid, "")) {
436
+ viewMemory.latency.set(conversationViewMemoryKey(cid, currentSourceId), memory);
437
+ }
438
+ }
350
439
  onDestroy(() => {
440
+ saveViewMemory();
351
441
  stopPlayback(false);
352
442
  void closePlaybackAudioContext();
353
443
  });
@@ -377,7 +467,7 @@ function _page($$renderer, $$props) {
377
467
  return `#${clampByte(r + (target - r) * t).toString(16).padStart(2, "0")}${clampByte(g + (target - g) * t).toString(16).padStart(2, "0")}${clampByte(b + (target - b) * t).toString(16).padStart(2, "0")}`;
378
468
  }
379
469
  function fmtSignedMs(value) {
380
- if (!Number.isFinite(value)) return "0 ms";
470
+ if (value == null || !Number.isFinite(value)) return "0 ms";
381
471
  const rounded = Math.round(value);
382
472
  if (rounded === 0) return "0 ms";
383
473
  return `${rounded > 0 ? "+" : ""}${rounded} ms`;
@@ -677,21 +767,20 @@ function _page($$renderer, $$props) {
677
767
  function isFinalSttLatencyTrace(trace) {
678
768
  return trace.eventType === "stt.final" || trace.eventType === "stt.final_transcript";
679
769
  }
680
- function cleanBubbleText(value) {
770
+ function bubbleText(value) {
681
771
  const text = normalizeNonEmptyString(value);
682
772
  if (!text) return null;
683
- const cleaned = text.replace(/<break\b[^>]*\/?>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
684
- return cleaned || null;
773
+ return text.replace(/\s+/g, " ").trim() || null;
685
774
  }
686
775
  function latencyTranscriptText(trace) {
687
776
  const payload = latencyPayload(trace);
688
- const text = cleanBubbleText(payload.text) ?? cleanBubbleText(payload.transcript) ?? cleanBubbleText(payload.finalTranscript) ?? cleanBubbleText(payload.final_transcript) ?? cleanBubbleText(payload.message);
777
+ const text = bubbleText(payload.text) ?? bubbleText(payload.transcript) ?? bubbleText(payload.finalTranscript) ?? bubbleText(payload.final_transcript) ?? bubbleText(payload.message);
689
778
  if (text) return text;
690
779
  const alternatives = payload.alternatives;
691
780
  if (Array.isArray(alternatives)) {
692
781
  for (const alternative of alternatives) {
693
782
  if (!alternative || typeof alternative !== "object") continue;
694
- const alternativeText = cleanBubbleText(alternative.text);
783
+ const alternativeText = bubbleText(alternative.text);
695
784
  if (alternativeText) return alternativeText;
696
785
  }
697
786
  }
@@ -747,13 +836,11 @@ function _page($$renderer, $$props) {
747
836
  if (candidates[0]) return candidates[0];
748
837
  return playbackSegments.find((segment) => !usedSegmentIds.has(segment.id) && segment.start <= span.end + SPEECH_BUBBLE_PLAYBACK_SPAN_MATCH_AFTER_MS && segment.end >= span.end - SPEECH_BUBBLE_PLAYBACK_SPAN_MATCH_BEFORE_MS) ?? null;
749
838
  }
750
- function findPlaybackSegmentForAgentTrace(trace, playbackSegments, usedSegmentIds) {
751
- const traceTime = ms(trace.createdAt);
752
- if (traceTime == null) return null;
839
+ function findPlaybackSegmentForResponse(responseTime, playbackSegments, usedSegmentIds) {
753
840
  const candidates = playbackSegments.filter((segment) => {
754
841
  if (usedSegmentIds.has(segment.id)) return false;
755
- return segment.end >= traceTime - SPEECH_BUBBLE_AGENT_PLAYBACK_MATCH_WINDOW_MS && segment.start <= traceTime + SPEECH_BUBBLE_AGENT_PLAYBACK_MATCH_WINDOW_MS;
756
- }).sort((a, b) => segmentDistanceFromTime(a, traceTime) - segmentDistanceFromTime(b, traceTime));
842
+ return segment.end >= responseTime - SPEECH_BUBBLE_AGENT_PLAYBACK_MATCH_WINDOW_MS && segment.start <= responseTime + SPEECH_BUBBLE_AGENT_PLAYBACK_MATCH_WINDOW_MS;
843
+ }).sort((a, b) => segmentDistanceFromTime(a, responseTime) - segmentDistanceFromTime(b, responseTime));
757
844
  return candidates[0] ?? null;
758
845
  }
759
846
  function callStartTime() {
@@ -768,6 +855,85 @@ function _page($$renderer, $$props) {
768
855
  if (lastPlayback.end <= anchorTime) return Math.min(lastPlayback.end, anchorTime);
769
856
  return Math.min(lastPlayback.start, anchorTime);
770
857
  }
858
+ function isTagOnlyResponse(value) {
859
+ return value.replace(/<[^>]+>/g, "").trim() === "";
860
+ }
861
+ function isWaitResponse(value) {
862
+ return /<WAIT\b[^>]*\/?\s*>/i.test(value);
863
+ }
864
+ function isMainStepResponse(trace) {
865
+ return trace.traceType === "STEP" && trace.name === "main" && normalizeNonEmptyString(trace.response) != null;
866
+ }
867
+ function responseTextWithoutTags(value) {
868
+ return value.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
869
+ }
870
+ function isEquivalentAiResponseText(a, b) {
871
+ if (a === b) return true;
872
+ const spokenA = responseTextWithoutTags(a);
873
+ const spokenB = responseTextWithoutTags(b);
874
+ return spokenA !== "" && spokenA === spokenB;
875
+ }
876
+ function responseTurnBounds(createdAt) {
877
+ let start = createdAt - SPEECH_BUBBLE_AGENT_PLAYBACK_MATCH_WINDOW_MS;
878
+ let end = createdAt + SPEECH_BUBBLE_AGENT_PLAYBACK_MATCH_WINDOW_MS;
879
+ for (const trace of conversationTraces) {
880
+ if (!isUserConversationalTrace(trace)) continue;
881
+ if (trace.createdAt <= createdAt) start = trace.createdAt;
882
+ else {
883
+ end = trace.createdAt;
884
+ break;
885
+ }
886
+ }
887
+ return { start, end };
888
+ }
889
+ function findStepResponseForAgentTrace(agentTrace, stepResponses, usedStepTraceIds) {
890
+ const bounds = responseTurnBounds(agentTrace.createdAt);
891
+ return stepResponses.filter((trace) => !usedStepTraceIds.has(traceKey(trace)) && trace.createdAt >= bounds.start && trace.createdAt < bounds.end).sort((a, b) => Math.abs(a.createdAt - agentTrace.createdAt) - Math.abs(b.createdAt - agentTrace.createdAt))[0] ?? null;
892
+ }
893
+ function buildAiResponses() {
894
+ const built = [];
895
+ const usedStepTraceIds = /* @__PURE__ */ new Set();
896
+ const stepResponses = conversationTraces.filter(isMainStepResponse).sort(compareConversationTraces);
897
+ const agentTraces = conversationTraces.filter(isAgentConversationalTrace).sort(compareConversationTraces);
898
+ for (const agentTrace of agentTraces) {
899
+ const stepTrace = findStepResponseForAgentTrace(agentTrace, stepResponses, usedStepTraceIds);
900
+ const stepFullText = stepTrace ? normalizeNonEmptyString(stepTrace.response) : null;
901
+ const fullText = stepFullText ?? normalizeNonEmptyString(agentTrace.content);
902
+ const text = bubbleText(fullText);
903
+ if (!text || !fullText) continue;
904
+ const duplicate = built.find((response) => response.agentTraceIds.size > 0 && Math.abs(response.createdAt - agentTrace.createdAt) <= 1e3 && isEquivalentAiResponseText(response.fullText, fullText));
905
+ if (duplicate) {
906
+ duplicate.agentTraceIds.add(conversationalTraceKey(agentTrace));
907
+ if (stepTrace) usedStepTraceIds.add(traceKey(stepTrace));
908
+ continue;
909
+ }
910
+ if (stepTrace) usedStepTraceIds.add(traceKey(stepTrace));
911
+ built.push({
912
+ id: stepTrace ? `response:step:${traceKey(stepTrace)}` : `response:agent:${conversationalTraceKey(agentTrace)}`,
913
+ text,
914
+ fullText,
915
+ createdAt: agentTrace.createdAt,
916
+ agentTraceIds: /* @__PURE__ */ new Set([conversationalTraceKey(agentTrace)]),
917
+ traceLabel: describeTrace(stepTrace ?? agentTrace)
918
+ });
919
+ }
920
+ for (const stepTrace of stepResponses) {
921
+ const stepTraceId = traceKey(stepTrace);
922
+ if (usedStepTraceIds.has(stepTraceId)) continue;
923
+ const fullText = normalizeNonEmptyString(stepTrace.response);
924
+ const text = bubbleText(fullText);
925
+ if (!text || !fullText) continue;
926
+ built.push({
927
+ id: `response:step:${stepTraceId}`,
928
+ text,
929
+ fullText,
930
+ createdAt: stepTrace.createdAt,
931
+ agentTraceIds: /* @__PURE__ */ new Set(),
932
+ traceLabel: describeTrace(stepTrace)
933
+ });
934
+ }
935
+ return built.sort((a, b) => a.createdAt - b.createdAt || a.id.localeCompare(b.id));
936
+ }
771
937
  function traceKey(trace) {
772
938
  return normalizeNonEmptyString(trace.traceId) ?? `${trace.seq ?? "?"}:${trace.createdAt ?? "?"}`;
773
939
  }
@@ -776,7 +942,7 @@ function _page($$renderer, $$props) {
776
942
  if (trace.eventType !== "tts.text.first_sent" && trace.eventType !== "tts.text.completed") {
777
943
  return [];
778
944
  }
779
- const text = cleanBubbleText(latencyText(trace));
945
+ const text = bubbleText(latencyText(trace));
780
946
  return text ? [{ trace, text }] : [];
781
947
  });
782
948
  }
@@ -789,11 +955,11 @@ function _page($$renderer, $$props) {
789
955
  }).sort((a, b) => Math.abs(a.trace.atMs - playbackSegment.start) - Math.abs(b.trace.atMs - playbackSegment.start));
790
956
  return candidates[0] ?? null;
791
957
  }
792
- function buildPlaybackSpeechBySegmentId(inputSegments) {
793
- const built = /* @__PURE__ */ new Map();
958
+ function buildPlaybackSpeechMatches(inputSegments, responses) {
959
+ const bySegmentId = /* @__PURE__ */ new Map();
794
960
  const playbackSegments = inputSegments.filter((segment) => segment.row === "Playback" && segment.durationMs > 0).sort((a, b) => a.start - b.start || a.end - b.end);
795
- const usedPlaybackSegmentIds = /* @__PURE__ */ new Set();
796
- const usedAgentTraceIds = /* @__PURE__ */ new Set();
961
+ const usedAgentPlaybackSegmentIds = /* @__PURE__ */ new Set();
962
+ const usedResponseIds = /* @__PURE__ */ new Set();
797
963
  const usedTtsTextTraceIds = /* @__PURE__ */ new Set();
798
964
  const ttsTextEvents = buildTtsTextEvents();
799
965
  let previousPlaybackEnd = callStartTime();
@@ -801,35 +967,71 @@ function _page($$renderer, $$props) {
801
967
  const ttsText = findTtsTextForPlayback(playbackSegment, ttsTextEvents, usedTtsTextTraceIds, previousPlaybackEnd);
802
968
  previousPlaybackEnd = Math.max(previousPlaybackEnd, playbackSegment.end);
803
969
  if (!ttsText) continue;
804
- usedPlaybackSegmentIds.add(playbackSegment.id);
805
970
  usedTtsTextTraceIds.add(traceKey(ttsText.trace));
806
- built.set(playbackSegment.id, { text: ttsText.text, traceLabel: describeTrace(ttsText.trace) });
971
+ bySegmentId.set(playbackSegment.id, {
972
+ text: ttsText.text,
973
+ fullText: ttsText.text,
974
+ traceLabel: describeTrace(ttsText.trace)
975
+ });
807
976
  }
808
977
  for (const span of buildResponseLatencySpans(traces)) {
809
978
  if (findResponseLatencyUserTurn(conversationTraces, span) == null) continue;
810
979
  const agentTrace = findResponseLatencyAgentTrace(conversationTraces, span);
811
980
  if (!agentTrace) continue;
812
- const text = cleanBubbleText(agentTrace.content);
813
- if (!text) continue;
814
- const playbackSegment = findPlaybackSegmentForSpan(span, playbackSegments, usedPlaybackSegmentIds);
981
+ const response = responses.find((candidate) => candidate.agentTraceIds.has(conversationalTraceKey(agentTrace)));
982
+ if (!response) continue;
983
+ if (usedResponseIds.has(response.id)) continue;
984
+ if (isTagOnlyResponse(response.fullText)) continue;
985
+ const playbackSegment = findPlaybackSegmentForSpan(span, playbackSegments, usedAgentPlaybackSegmentIds);
815
986
  if (!playbackSegment) continue;
816
- usedPlaybackSegmentIds.add(playbackSegment.id);
817
- usedAgentTraceIds.add(conversationalTraceKey(agentTrace));
818
- built.set(playbackSegment.id, { text, traceLabel: describeTrace(agentTrace) });
987
+ usedAgentPlaybackSegmentIds.add(playbackSegment.id);
988
+ usedResponseIds.add(response.id);
989
+ bySegmentId.set(playbackSegment.id, {
990
+ text: response.text,
991
+ fullText: response.fullText,
992
+ traceLabel: response.traceLabel
993
+ });
819
994
  }
820
- const agentTraces = conversationTraces.filter(isAgentConversationalTrace).sort(compareConversationTraces);
821
- for (const agentTrace of agentTraces) {
822
- const traceKey2 = conversationalTraceKey(agentTrace);
823
- if (usedAgentTraceIds.has(traceKey2)) continue;
824
- const text = cleanBubbleText(agentTrace.content);
825
- if (!text) continue;
826
- const playbackSegment = findPlaybackSegmentForAgentTrace(agentTrace, playbackSegments, usedPlaybackSegmentIds);
995
+ for (const response of responses) {
996
+ if (usedResponseIds.has(response.id)) continue;
997
+ if (isTagOnlyResponse(response.fullText)) continue;
998
+ const playbackSegment = findPlaybackSegmentForResponse(response.createdAt, playbackSegments, usedAgentPlaybackSegmentIds);
827
999
  if (!playbackSegment) continue;
828
- usedPlaybackSegmentIds.add(playbackSegment.id);
829
- usedAgentTraceIds.add(traceKey2);
830
- built.set(playbackSegment.id, { text, traceLabel: describeTrace(agentTrace) });
1000
+ usedAgentPlaybackSegmentIds.add(playbackSegment.id);
1001
+ usedResponseIds.add(response.id);
1002
+ bySegmentId.set(playbackSegment.id, {
1003
+ text: response.text,
1004
+ fullText: response.fullText,
1005
+ traceLabel: response.traceLabel
1006
+ });
831
1007
  }
832
- return built;
1008
+ return { bySegmentId, responseIds: usedResponseIds };
1009
+ }
1010
+ function buildStandaloneWaitSegments(responses, usedResponseIds) {
1011
+ return responses.flatMap((response) => {
1012
+ if (usedResponseIds.has(response.id) || !isWaitResponse(response.fullText)) return [];
1013
+ const end = findWaitResponseEnd(response.createdAt);
1014
+ return [
1015
+ {
1016
+ ...makeSegment({
1017
+ id: `playback:wait:${response.id}`,
1018
+ row: "Playback",
1019
+ start: response.createdAt,
1020
+ end,
1021
+ colorKey: "playback",
1022
+ label: "WAIT response",
1023
+ traceLabel: response.traceLabel
1024
+ }),
1025
+ speechText: response.text,
1026
+ speechFullText: response.fullText,
1027
+ speechTraceLabel: response.traceLabel
1028
+ }
1029
+ ];
1030
+ });
1031
+ }
1032
+ function findWaitResponseEnd(start) {
1033
+ const nextActivity = traces.filter((trace) => trace.atMs > start && (trace.eventType === "audio.playback.started" || trace.eventType === "stt.started" || trace.eventType === "vad.activated" || trace.eventType === "call.ended")).sort((a, b) => a.atMs - b.atMs || a.seq - b.seq)[0];
1034
+ return nextActivity ? nextActivity.atMs : start + 1;
833
1035
  }
834
1036
  function buildSpeechBubbleCandidates() {
835
1037
  const built = [];
@@ -862,10 +1064,10 @@ function _page($$renderer, $$props) {
862
1064
  if (bubble.role !== role) continue;
863
1065
  maxLevel = Math.max(maxLevel, bubble.level);
864
1066
  }
865
- return Math.max(1, maxLevel + 1);
1067
+ return maxLevel + 1;
866
1068
  }
867
1069
  function speechBubbleLaneHeightPx(levelCount) {
868
- return Math.max(1, levelCount) * SPEECH_BUBBLE_LEVEL_HEIGHT_PX;
1070
+ return Math.max(0, levelCount) * SPEECH_BUBBLE_LEVEL_HEIGHT_PX;
869
1071
  }
870
1072
  function compactSpeechBubbleLayouts(bubbles) {
871
1073
  const bubbleAreaTopPx = speechBubbleAreaTopPx();
@@ -914,6 +1116,7 @@ function _page($$renderer, $$props) {
914
1116
  explain: input.explain ?? null,
915
1117
  text: input.text ?? null,
916
1118
  speechText: null,
1119
+ speechFullText: null,
917
1120
  speechTraceLabel: null,
918
1121
  traceLabel: input.traceLabel,
919
1122
  eventMarker: input.eventMarker ?? null
@@ -981,6 +1184,41 @@ function _page($$renderer, $$props) {
981
1184
  const derivedSegments = buildDerivedSegments(events);
982
1185
  return [...derivedSegments, ...pointSegments].sort((a, b) => a.start - b.start || (a.durationMs === 0 ? 1 : 0) - (b.durationMs === 0 ? 1 : 0) || a.end - b.end || a.row.localeCompare(b.row));
983
1186
  }
1187
+ function buildRecordingResponse(raw) {
1188
+ const next = raw && typeof raw === "object" ? raw : {};
1189
+ const audioUrl = typeof next.audioUrl === "string" && next.audioUrl.trim() ? next.audioUrl : null;
1190
+ return {
1191
+ exists: next.exists === true && audioUrl != null,
1192
+ audioUrl,
1193
+ fileName: typeof next.fileName === "string" ? next.fileName : null,
1194
+ mimeType: typeof next.mimeType === "string" ? next.mimeType : null,
1195
+ size: typeof next.size === "number" && Number.isFinite(next.size) ? next.size : null,
1196
+ uploadedAt: typeof next.uploadedAt === "string" ? next.uploadedAt : null,
1197
+ alignmentOffsetMs: typeof next.alignmentOffsetMs === "number" && Number.isFinite(next.alignmentOffsetMs) ? next.alignmentOffsetMs : 0
1198
+ };
1199
+ }
1200
+ function getResponseError(payload, fallback) {
1201
+ const next = payload && typeof payload === "object" ? payload : {};
1202
+ if (typeof next.error === "string" && next.error.trim()) return next.error;
1203
+ if (typeof next.message === "string" && next.message.trim()) return next.message;
1204
+ return fallback;
1205
+ }
1206
+ async function readResponsePayload(response) {
1207
+ const contentType = response.headers.get("content-type") ?? "";
1208
+ if (contentType.includes("application/json")) {
1209
+ return response.json().catch(() => ({}));
1210
+ }
1211
+ const text = (await response.text().catch(() => "")).trim();
1212
+ if (!text) return {};
1213
+ if (contentType.includes("text/html")) {
1214
+ const titleMatch = text.match(/<title>([^<]+)<\/title>/i);
1215
+ const headingMatch = text.match(/<h1[^>]*>([^<]+)<\/h1>/i);
1216
+ return {
1217
+ message: titleMatch?.[1]?.trim() || headingMatch?.[1]?.trim() || `${response.status} ${response.statusText}`.trim()
1218
+ };
1219
+ }
1220
+ return { message: text };
1221
+ }
984
1222
  function isPlaybackTraceBubble(segment) {
985
1223
  return segment.row === "Playback" && !segment.eventMarker && segment.durationMs > 0;
986
1224
  }
@@ -1037,7 +1275,44 @@ function _page($$renderer, $$props) {
1037
1275
  return `${base} -translate-x-1/2`;
1038
1276
  }
1039
1277
  function buildGuideLines(rows) {
1040
- return [];
1278
+ if (!recording.exists || viewportStartMs == null || viewportEndMs == null) return [];
1279
+ const rangeStartMs = viewportStartMs;
1280
+ const rangeEndMs = viewportEndMs;
1281
+ const durationMs = rangeEndMs - rangeStartMs;
1282
+ if (!Number.isFinite(durationMs) || durationMs <= 0) return [];
1283
+ const waveformBottomPx = timelinePlotHeightPx();
1284
+ const seen = /* @__PURE__ */ new Set();
1285
+ const built = [];
1286
+ rows.forEach((row, rowIndex) => {
1287
+ if (!GUIDE_ROW_LABELS.has(row.label)) return;
1288
+ const rowTopPx = TIMELINE_RULER_HEIGHT_PX + rowIndex * (TIMELINE_ROW_HEIGHT_PX + TIMELINE_ROW_GAP_PX);
1289
+ const rowAnchorPx = rowTopPx + Math.round(TIMELINE_ROW_HEIGHT_PX / 2);
1290
+ row.segments.forEach((segment) => {
1291
+ if (segment.id.startsWith("playback:wait:")) return;
1292
+ const color = tintHex(COLOR_BY_KEY[segment.colorKey] ?? BOSUN_COLORS.gray, 0.15);
1293
+ const times = segment.end === segment.start ? segment.start >= rangeStartMs && segment.start <= rangeEndMs ? [segment.start] : [] : (() => {
1294
+ const visibleStart = Math.max(segment.start, rangeStartMs);
1295
+ const visibleEnd = Math.min(segment.end, rangeEndMs);
1296
+ return visibleEnd < visibleStart ? [] : [visibleStart, visibleEnd];
1297
+ })();
1298
+ for (const timeMs of times) {
1299
+ const roundedTime = Math.round(timeMs);
1300
+ const key = `${row.label}:${roundedTime}`;
1301
+ if (seen.has(key)) continue;
1302
+ seen.add(key);
1303
+ const leftPercent = clamp((timeMs - rangeStartMs) / durationMs * 100, 0, 100);
1304
+ built.push({
1305
+ id: `${segment.id}:${roundedTime}`,
1306
+ leftPercent,
1307
+ topPx: rowAnchorPx,
1308
+ heightPx: Math.max(1, waveformBottomPx - rowAnchorPx),
1309
+ color,
1310
+ opacity: row.label === "VAD" ? 0.62 : 0.48
1311
+ });
1312
+ }
1313
+ });
1314
+ });
1315
+ return built;
1041
1316
  }
1042
1317
  function chooseTickStep(durationMs) {
1043
1318
  const approx = durationMs / 7;
@@ -1064,6 +1339,18 @@ function _page($$renderer, $$props) {
1064
1339
  }
1065
1340
  return ticks;
1066
1341
  }
1342
+ function createAudioContext() {
1343
+ const next = window.AudioContext ?? window.webkitAudioContext;
1344
+ if (!next) {
1345
+ throw new Error("Web Audio is not available in this browser");
1346
+ }
1347
+ return new next();
1348
+ }
1349
+ function stopPlaybackTicker() {
1350
+ if (!playbackFrame) return;
1351
+ window.cancelAnimationFrame(playbackFrame);
1352
+ playbackFrame = 0;
1353
+ }
1067
1354
  async function closePlaybackAudioContext() {
1068
1355
  const context = playbackAudioContext;
1069
1356
  playbackAudioContext = null;
@@ -1071,6 +1358,15 @@ function _page($$renderer, $$props) {
1071
1358
  await context.close().catch(() => {
1072
1359
  });
1073
1360
  }
1361
+ async function ensurePlaybackAudioContext() {
1362
+ if (!playbackAudioContext || playbackAudioContext.state === "closed") {
1363
+ playbackAudioContext = createAudioContext();
1364
+ }
1365
+ if (playbackAudioContext.state === "suspended") {
1366
+ await playbackAudioContext.resume();
1367
+ }
1368
+ return playbackAudioContext;
1369
+ }
1074
1370
  function getPlaybackPositionMs() {
1075
1371
  const durationMs = getRecordingDurationMs();
1076
1372
  if (playbackPlaying && playbackAudioContext && playbackStartedAtMs != null && playbackStartedAtContextTime != null) {
@@ -1089,6 +1385,7 @@ function _page($$renderer, $$props) {
1089
1385
  playbackStartedAtMs = null;
1090
1386
  playbackStartedAtContextTime = null;
1091
1387
  playbackPlaying = false;
1388
+ stopPlaybackTicker();
1092
1389
  if (!source) return;
1093
1390
  source.onended = null;
1094
1391
  try {
@@ -1097,11 +1394,72 @@ function _page($$renderer, $$props) {
1097
1394
  }
1098
1395
  source.disconnect();
1099
1396
  }
1397
+ function startPlaybackTicker() {
1398
+ stopPlaybackTicker();
1399
+ const tick = () => {
1400
+ if (!playbackPlaying) {
1401
+ playbackFrame = 0;
1402
+ return;
1403
+ }
1404
+ playbackPositionMs = getPlaybackPositionMs();
1405
+ playbackFrame = window.requestAnimationFrame(tick);
1406
+ };
1407
+ playbackFrame = window.requestAnimationFrame(tick);
1408
+ }
1100
1409
  function getRecordingDurationMs() {
1410
+ if (waveform && Number.isFinite(waveform.durationMs)) {
1411
+ return Math.max(0, waveform.durationMs);
1412
+ }
1413
+ if (playbackAudioBuffer && Number.isFinite(playbackAudioBuffer.duration)) {
1414
+ return Math.max(0, playbackAudioBuffer.duration * 1e3);
1415
+ }
1101
1416
  return 0;
1102
1417
  }
1103
1418
  async function togglePlayback() {
1104
- return;
1419
+ if (!recording.exists || !recording.audioUrl) return;
1420
+ playbackError = null;
1421
+ if (playbackPlaying) {
1422
+ stopPlayback(true);
1423
+ return;
1424
+ }
1425
+ await startPlaybackAtMs(playbackPositionMs);
1426
+ }
1427
+ async function startPlaybackAtMs(nextMs) {
1428
+ const buffer = playbackAudioBuffer;
1429
+ if (!buffer) {
1430
+ playbackError = "Failed to decode recording for playback";
1431
+ return;
1432
+ }
1433
+ const durationMs = Math.max(0, buffer.duration * 1e3);
1434
+ const startMs = durationMs > 0 ? nextMs >= durationMs ? 0 : clamp(nextMs, 0, durationMs) : Math.max(0, nextMs);
1435
+ stopPlayback(false);
1436
+ playbackPositionMs = startMs;
1437
+ try {
1438
+ const context = await ensurePlaybackAudioContext();
1439
+ const source = context.createBufferSource();
1440
+ source.buffer = buffer;
1441
+ source.connect(context.destination);
1442
+ source.onended = () => {
1443
+ if (playbackSourceNode !== source) return;
1444
+ playbackSourceNode = null;
1445
+ playbackStartedAtMs = null;
1446
+ playbackStartedAtContextTime = null;
1447
+ playbackPlaying = false;
1448
+ playbackPositionMs = durationMs;
1449
+ stopPlaybackTicker();
1450
+ };
1451
+ playbackSourceNode = source;
1452
+ playbackStartedAtMs = startMs;
1453
+ playbackStartedAtContextTime = context.currentTime;
1454
+ playbackPlaying = true;
1455
+ playbackError = null;
1456
+ startPlaybackTicker();
1457
+ source.start(0, startMs / 1e3);
1458
+ } catch (reason) {
1459
+ stopPlayback(false);
1460
+ playbackPlaying = false;
1461
+ playbackError = reason instanceof Error ? reason.message : "Failed to play recording";
1462
+ }
1105
1463
  }
1106
1464
  function compareConversationTraces(a, b) {
1107
1465
  return a.createdAt - b.createdAt || String(a.traceId ?? "").localeCompare(String(b.traceId ?? ""));
@@ -1112,6 +1470,7 @@ function _page($$renderer, $$props) {
1112
1470
  const originMs = timelineOriginMs();
1113
1471
  viewportStartMs = originMs;
1114
1472
  viewportEndMs = Math.max(originMs + 1e3, extent.max);
1473
+ viewDirty = markDirty;
1115
1474
  }
1116
1475
  function clampViewport(startMs, endMs) {
1117
1476
  const extent = contentExtent();
@@ -1152,10 +1511,41 @@ function _page($$renderer, $$props) {
1152
1511
  const nextFocusRatio = clamp(focusRatio, 0, 1);
1153
1512
  const focusMs = viewportStartMs + currentDuration * nextFocusRatio;
1154
1513
  const nextStartMs = focusMs - nextDuration * nextFocusRatio;
1514
+ viewDirty = true;
1155
1515
  setViewport(nextStartMs, nextStartMs + nextDuration);
1156
1516
  }
1517
+ function setLocalAlignmentOffset(nextOffsetMs) {
1518
+ recording = { ...recording, alignmentOffsetMs: Math.round(nextOffsetMs) };
1519
+ }
1520
+ async function persistAlignmentOffset() {
1521
+ if (!recording.exists || !recordingApiUrl()) return;
1522
+ alignmentSaving = true;
1523
+ alignmentError = null;
1524
+ try {
1525
+ const response = await fetch(recordingApiUrl(), {
1526
+ method: "PATCH",
1527
+ headers: {
1528
+ accept: "application/json",
1529
+ "content-type": "application/json"
1530
+ },
1531
+ body: JSON.stringify({ alignmentOffsetMs: recording.alignmentOffsetMs })
1532
+ });
1533
+ const payload = await readResponsePayload(response);
1534
+ if (!response.ok) {
1535
+ throw new Error(getResponseError(payload, "Failed to save alignment offset"));
1536
+ }
1537
+ recording = buildRecordingResponse(payload);
1538
+ } catch (reason) {
1539
+ alignmentError = reason instanceof Error ? reason.message : String(reason);
1540
+ } finally {
1541
+ alignmentSaving = false;
1542
+ }
1543
+ }
1157
1544
  async function resetAlignment() {
1158
- return;
1545
+ if (!recording.exists) return;
1546
+ viewDirty = true;
1547
+ setLocalAlignmentOffset(0);
1548
+ await persistAlignmentOffset();
1159
1549
  }
1160
1550
  $$renderer2.push(`<div class="flex h-full min-h-0 flex-col bg-rc-gray-700"><div class="flex-none border-b border-rc-gray-300/40 p-4"><div class="flex flex-wrap items-start justify-between gap-3"><div><h2 class="text-lg font-semibold text-rc-gray-100">Latency `);
1161
1551
  if (conversationId()) {
@@ -1165,7 +1555,10 @@ function _page($$renderer, $$props) {
1165
1555
  $$renderer2.push("<!--[-1-->");
1166
1556
  }
1167
1557
  $$renderer2.push(`<!--]--></h2> <div class="mt-2 flex flex-wrap gap-x-5 gap-y-1 text-xs text-rc-gray-300"><div><span class="text-rc-gray-400">Latency events:</span> <span class="font-mono text-rc-gray-100">${escape_html(latencyTraceCount())}</span></div> <div><span class="text-rc-gray-400">Timeline items:</span> <span class="font-mono text-rc-gray-100">${escape_html(callSegments().length)}</span></div> <div><span class="text-rc-gray-400">Recording offset:</span> <span class="font-mono text-rc-gray-100">${escape_html(fmtSignedMs(recording.alignmentOffsetMs))}</span></div> `);
1168
- {
1558
+ if (recording.exists && playbackAbsoluteMs() != null) {
1559
+ $$renderer2.push("<!--[0-->");
1560
+ $$renderer2.push(`<div><span class="text-rc-gray-400">Playhead:</span> <span class="font-mono text-rc-gray-100">${escape_html(formatRelativeTime(playbackAbsoluteMs() - timelineOriginMs()))}</span></div>`);
1561
+ } else {
1169
1562
  $$renderer2.push("<!--[-1-->");
1170
1563
  }
1171
1564
  $$renderer2.push(`<!--]--> `);
@@ -1182,7 +1575,7 @@ function _page($$renderer, $$props) {
1182
1575
  disabled: uploadInFlight,
1183
1576
  onclick: () => fileInputEl?.click(),
1184
1577
  children: ($$renderer3) => {
1185
- $$renderer3.push(`<!---->${escape_html("Upload WAV")}`);
1578
+ $$renderer3.push(`<!---->${escape_html(recording.exists ? "Replace WAV" : "Upload WAV")}`);
1186
1579
  },
1187
1580
  $$slots: { default: true }
1188
1581
  });
@@ -1190,7 +1583,7 @@ function _page($$renderer, $$props) {
1190
1583
  Button($$renderer2, {
1191
1584
  variant: "secondary",
1192
1585
  size: "small",
1193
- disabled: true,
1586
+ disabled: !recording.exists,
1194
1587
  onclick: () => void togglePlayback(),
1195
1588
  children: ($$renderer3) => {
1196
1589
  $$renderer3.push(`<!---->${escape_html(playbackPlaying ? "Pause" : "Play")}`);
@@ -1234,7 +1627,7 @@ function _page($$renderer, $$props) {
1234
1627
  Button($$renderer2, {
1235
1628
  variant: "secondary",
1236
1629
  size: "small",
1237
- disabled: true,
1630
+ disabled: !recording.exists || alignmentSaving,
1238
1631
  onclick: resetAlignment,
1239
1632
  children: ($$renderer3) => {
1240
1633
  $$renderer3.push(`<!---->Reset alignment`);
@@ -1257,19 +1650,31 @@ function _page($$renderer, $$props) {
1257
1650
  $$renderer2.push("<!--[-1-->");
1258
1651
  }
1259
1652
  $$renderer2.push(`<!--]--> `);
1260
- {
1653
+ if (error) {
1654
+ $$renderer2.push("<!--[0-->");
1655
+ $$renderer2.push(`<div class="mt-2 text-sm text-red-300">Failed to load latency traces: ${escape_html(error)}</div>`);
1656
+ } else {
1261
1657
  $$renderer2.push("<!--[-1-->");
1262
1658
  }
1263
1659
  $$renderer2.push(`<!--]--> `);
1264
- {
1660
+ if (recordingError) {
1661
+ $$renderer2.push("<!--[0-->");
1662
+ $$renderer2.push(`<div class="mt-2 text-sm text-red-300">Recording error: ${escape_html(recordingError)}</div>`);
1663
+ } else {
1265
1664
  $$renderer2.push("<!--[-1-->");
1266
1665
  }
1267
1666
  $$renderer2.push(`<!--]--> `);
1268
- {
1667
+ if (alignmentError) {
1668
+ $$renderer2.push("<!--[0-->");
1669
+ $$renderer2.push(`<div class="mt-2 text-sm text-red-300">Alignment error: ${escape_html(alignmentError)}</div>`);
1670
+ } else {
1269
1671
  $$renderer2.push("<!--[-1-->");
1270
1672
  }
1271
1673
  $$renderer2.push(`<!--]--> `);
1272
- {
1674
+ if (playbackError) {
1675
+ $$renderer2.push("<!--[0-->");
1676
+ $$renderer2.push(`<div class="mt-2 text-sm text-red-300">Playback error: ${escape_html(playbackError)}</div>`);
1677
+ } else {
1273
1678
  $$renderer2.push("<!--[-1-->");
1274
1679
  }
1275
1680
  $$renderer2.push(`<!--]--></div> <div class="rc-scroll min-h-0 flex-1 overflow-auto p-4"><div class="flex flex-col gap-4"><div class="rounded-md border border-rc-gray-650 p-3">`);
@@ -1378,7 +1783,10 @@ function _page($$renderer, $$props) {
1378
1783
  if (waveformRowVisible()) {
1379
1784
  $$renderer2.push("<!--[0-->");
1380
1785
  $$renderer2.push(`<div class="relative z-0 overflow-hidden rounded-b border-x border-b border-t border-rc-gray-700/60"${attr_style(`height:${WAVEFORM_ROW_HEIGHT_PX}px;margin-top:${TIMELINE_ROW_GAP_PX}px;`)}>`);
1381
- {
1786
+ if (waveformError) {
1787
+ $$renderer2.push("<!--[1-->");
1788
+ $$renderer2.push(`<div class="absolute inset-0 flex items-center justify-center px-4 text-sm text-red-300">Failed to decode waveform: ${escape_html(waveformError)}</div>`);
1789
+ } else {
1382
1790
  $$renderer2.push("<!--[-1-->");
1383
1791
  $$renderer2.push(`<canvas class="absolute inset-0 h-full w-full"></canvas>`);
1384
1792
  }
@@ -1407,4 +1815,4 @@ function _page($$renderer, $$props) {
1407
1815
  }
1408
1816
 
1409
1817
  export { _page as default };
1410
- //# sourceMappingURL=_page.svelte-bkzbqII3.js.map
1818
+ //# sourceMappingURL=_page.svelte-C_S_qUgZ.js.map