@a5c-ai/babysitter-observer-dashboard 5.0.1-staging.7a8768ec

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 (513) hide show
  1. package/.next/BUILD_ID +1 -0
  2. package/.next/app-path-routes-manifest.json +16 -0
  3. package/.next/build-manifest.json +23 -0
  4. package/.next/export-marker.json +6 -0
  5. package/.next/fallback-build-manifest.json +13 -0
  6. package/.next/images-manifest.json +68 -0
  7. package/.next/next-minimal-server.js.nft.json +1 -0
  8. package/.next/next-server.js.nft.json +1 -0
  9. package/.next/package.json +1 -0
  10. package/.next/prerender-manifest.json +114 -0
  11. package/.next/required-server-files.json +334 -0
  12. package/.next/routes-manifest.json +139 -0
  13. package/.next/server/app/_global-error/page/app-paths-manifest.json +3 -0
  14. package/.next/server/app/_global-error/page/build-manifest.json +19 -0
  15. package/.next/server/app/_global-error/page/next-font-manifest.json +6 -0
  16. package/.next/server/app/_global-error/page/react-loadable-manifest.json +1 -0
  17. package/.next/server/app/_global-error/page/server-reference-manifest.json +4 -0
  18. package/.next/server/app/_global-error/page.js +9 -0
  19. package/.next/server/app/_global-error/page.js.map +5 -0
  20. package/.next/server/app/_global-error/page.js.nft.json +1 -0
  21. package/.next/server/app/_global-error/page_client-reference-manifest.js +3 -0
  22. package/.next/server/app/_global-error.html +1 -0
  23. package/.next/server/app/_global-error.meta +15 -0
  24. package/.next/server/app/_global-error.rsc +14 -0
  25. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +5 -0
  26. package/.next/server/app/_global-error.segments/_full.segment.rsc +14 -0
  27. package/.next/server/app/_global-error.segments/_head.segment.rsc +5 -0
  28. package/.next/server/app/_global-error.segments/_index.segment.rsc +5 -0
  29. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
  30. package/.next/server/app/_not-found/page/app-paths-manifest.json +3 -0
  31. package/.next/server/app/_not-found/page/build-manifest.json +19 -0
  32. package/.next/server/app/_not-found/page/next-font-manifest.json +6 -0
  33. package/.next/server/app/_not-found/page/react-loadable-manifest.json +1 -0
  34. package/.next/server/app/_not-found/page/server-reference-manifest.json +4 -0
  35. package/.next/server/app/_not-found/page.js +13 -0
  36. package/.next/server/app/_not-found/page.js.map +5 -0
  37. package/.next/server/app/_not-found/page.js.nft.json +1 -0
  38. package/.next/server/app/_not-found/page_client-reference-manifest.js +3 -0
  39. package/.next/server/app/_not-found.html +1 -0
  40. package/.next/server/app/_not-found.meta +16 -0
  41. package/.next/server/app/_not-found.rsc +19 -0
  42. package/.next/server/app/_not-found.segments/_full.segment.rsc +19 -0
  43. package/.next/server/app/_not-found.segments/_head.segment.rsc +6 -0
  44. package/.next/server/app/_not-found.segments/_index.segment.rsc +8 -0
  45. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +6 -0
  46. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +5 -0
  47. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -0
  48. package/.next/server/app/api/config/route/app-paths-manifest.json +3 -0
  49. package/.next/server/app/api/config/route/build-manifest.json +9 -0
  50. package/.next/server/app/api/config/route/server-reference-manifest.json +4 -0
  51. package/.next/server/app/api/config/route.js +8 -0
  52. package/.next/server/app/api/config/route.js.map +5 -0
  53. package/.next/server/app/api/config/route.js.nft.json +1 -0
  54. package/.next/server/app/api/config/route_client-reference-manifest.js +3 -0
  55. package/.next/server/app/api/digest/route/app-paths-manifest.json +3 -0
  56. package/.next/server/app/api/digest/route/build-manifest.json +9 -0
  57. package/.next/server/app/api/digest/route/server-reference-manifest.json +4 -0
  58. package/.next/server/app/api/digest/route.js +9 -0
  59. package/.next/server/app/api/digest/route.js.map +5 -0
  60. package/.next/server/app/api/digest/route.js.nft.json +1 -0
  61. package/.next/server/app/api/digest/route_client-reference-manifest.js +3 -0
  62. package/.next/server/app/api/runs/[runId]/events/route/app-paths-manifest.json +3 -0
  63. package/.next/server/app/api/runs/[runId]/events/route/build-manifest.json +9 -0
  64. package/.next/server/app/api/runs/[runId]/events/route/server-reference-manifest.json +4 -0
  65. package/.next/server/app/api/runs/[runId]/events/route.js +8 -0
  66. package/.next/server/app/api/runs/[runId]/events/route.js.map +5 -0
  67. package/.next/server/app/api/runs/[runId]/events/route.js.nft.json +1 -0
  68. package/.next/server/app/api/runs/[runId]/events/route_client-reference-manifest.js +3 -0
  69. package/.next/server/app/api/runs/[runId]/route/app-paths-manifest.json +3 -0
  70. package/.next/server/app/api/runs/[runId]/route/build-manifest.json +9 -0
  71. package/.next/server/app/api/runs/[runId]/route/server-reference-manifest.json +4 -0
  72. package/.next/server/app/api/runs/[runId]/route.js +9 -0
  73. package/.next/server/app/api/runs/[runId]/route.js.map +5 -0
  74. package/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -0
  75. package/.next/server/app/api/runs/[runId]/route_client-reference-manifest.js +3 -0
  76. package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route/app-paths-manifest.json +3 -0
  77. package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route/build-manifest.json +9 -0
  78. package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route/server-reference-manifest.json +4 -0
  79. package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route.js +8 -0
  80. package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route.js.map +5 -0
  81. package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route.js.nft.json +1 -0
  82. package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route_client-reference-manifest.js +3 -0
  83. package/.next/server/app/api/runs/route/app-paths-manifest.json +3 -0
  84. package/.next/server/app/api/runs/route/build-manifest.json +9 -0
  85. package/.next/server/app/api/runs/route/server-reference-manifest.json +4 -0
  86. package/.next/server/app/api/runs/route.js +9 -0
  87. package/.next/server/app/api/runs/route.js.map +5 -0
  88. package/.next/server/app/api/runs/route.js.nft.json +1 -0
  89. package/.next/server/app/api/runs/route_client-reference-manifest.js +3 -0
  90. package/.next/server/app/api/stream/route/app-paths-manifest.json +3 -0
  91. package/.next/server/app/api/stream/route/build-manifest.json +9 -0
  92. package/.next/server/app/api/stream/route/server-reference-manifest.json +4 -0
  93. package/.next/server/app/api/stream/route.js +8 -0
  94. package/.next/server/app/api/stream/route.js.map +5 -0
  95. package/.next/server/app/api/stream/route.js.nft.json +1 -0
  96. package/.next/server/app/api/stream/route_client-reference-manifest.js +3 -0
  97. package/.next/server/app/api/test/route/app-paths-manifest.json +3 -0
  98. package/.next/server/app/api/test/route/build-manifest.json +9 -0
  99. package/.next/server/app/api/test/route/server-reference-manifest.json +4 -0
  100. package/.next/server/app/api/test/route.js +6 -0
  101. package/.next/server/app/api/test/route.js.map +5 -0
  102. package/.next/server/app/api/test/route.js.nft.json +1 -0
  103. package/.next/server/app/api/test/route_client-reference-manifest.js +3 -0
  104. package/.next/server/app/api/version/route/app-paths-manifest.json +3 -0
  105. package/.next/server/app/api/version/route/build-manifest.json +9 -0
  106. package/.next/server/app/api/version/route/server-reference-manifest.json +4 -0
  107. package/.next/server/app/api/version/route.js +7 -0
  108. package/.next/server/app/api/version/route.js.map +5 -0
  109. package/.next/server/app/api/version/route.js.nft.json +1 -0
  110. package/.next/server/app/api/version/route_client-reference-manifest.js +3 -0
  111. package/.next/server/app/icon.svg/route/app-paths-manifest.json +3 -0
  112. package/.next/server/app/icon.svg/route/build-manifest.json +9 -0
  113. package/.next/server/app/icon.svg/route.js +7 -0
  114. package/.next/server/app/icon.svg/route.js.map +5 -0
  115. package/.next/server/app/icon.svg/route.js.nft.json +1 -0
  116. package/.next/server/app/icon.svg.body +20 -0
  117. package/.next/server/app/icon.svg.meta +1 -0
  118. package/.next/server/app/index.html +1 -0
  119. package/.next/server/app/index.meta +14 -0
  120. package/.next/server/app/index.rsc +21 -0
  121. package/.next/server/app/index.segments/__PAGE__.segment.rsc +9 -0
  122. package/.next/server/app/index.segments/_full.segment.rsc +21 -0
  123. package/.next/server/app/index.segments/_head.segment.rsc +6 -0
  124. package/.next/server/app/index.segments/_index.segment.rsc +8 -0
  125. package/.next/server/app/index.segments/_tree.segment.rsc +2 -0
  126. package/.next/server/app/page/app-paths-manifest.json +3 -0
  127. package/.next/server/app/page/build-manifest.json +19 -0
  128. package/.next/server/app/page/next-font-manifest.json +6 -0
  129. package/.next/server/app/page/react-loadable-manifest.json +1 -0
  130. package/.next/server/app/page/server-reference-manifest.json +17 -0
  131. package/.next/server/app/page.js +15 -0
  132. package/.next/server/app/page.js.map +5 -0
  133. package/.next/server/app/page.js.nft.json +1 -0
  134. package/.next/server/app/page_client-reference-manifest.js +3 -0
  135. package/.next/server/app/runs/[runId]/page/app-paths-manifest.json +3 -0
  136. package/.next/server/app/runs/[runId]/page/build-manifest.json +19 -0
  137. package/.next/server/app/runs/[runId]/page/next-font-manifest.json +6 -0
  138. package/.next/server/app/runs/[runId]/page/react-loadable-manifest.json +22 -0
  139. package/.next/server/app/runs/[runId]/page/server-reference-manifest.json +17 -0
  140. package/.next/server/app/runs/[runId]/page.js +15 -0
  141. package/.next/server/app/runs/[runId]/page.js.map +5 -0
  142. package/.next/server/app/runs/[runId]/page.js.nft.json +1 -0
  143. package/.next/server/app/runs/[runId]/page_client-reference-manifest.js +3 -0
  144. package/.next/server/app-paths-manifest.json +16 -0
  145. package/.next/server/chunks/01oi_server_app_api_runs_[runId]_tasks_[effectId]_route_actions_0r72yai.js +3 -0
  146. package/.next/server/chunks/01oi_server_app_api_runs_[runId]_tasks_[effectId]_route_actions_0r72yai.js.map +1 -0
  147. package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_events_route_actions_0~msldk.js +3 -0
  148. package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_events_route_actions_0~msldk.js.map +1 -0
  149. package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_route_actions_09iz0n6.js +3 -0
  150. package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_route_actions_09iz0n6.js.map +1 -0
  151. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_config_route_actions_0~eypoa.js +3 -0
  152. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_config_route_actions_0~eypoa.js.map +1 -0
  153. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_digest_route_actions_04jj5zs.js +3 -0
  154. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_digest_route_actions_04jj5zs.js.map +1 -0
  155. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_runs_route_actions_0~-t-o4.js +3 -0
  156. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_runs_route_actions_0~-t-o4.js.map +1 -0
  157. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_stream_route_actions_0fkmv2_.js +3 -0
  158. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_stream_route_actions_0fkmv2_.js.map +1 -0
  159. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_test_route_actions_00ugava.js +3 -0
  160. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_test_route_actions_00ugava.js.map +1 -0
  161. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_version_route_actions_0~v3ojm.js +3 -0
  162. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_version_route_actions_0~v3ojm.js.map +1 -0
  163. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_icon_svg_route_actions_0yypxkm.js +3 -0
  164. package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_icon_svg_route_actions_0yypxkm.js.map +1 -0
  165. package/.next/server/chunks/[root-of-the-server]__0.6bt.6._.js +3 -0
  166. package/.next/server/chunks/[root-of-the-server]__0.6bt.6._.js.map +1 -0
  167. package/.next/server/chunks/[root-of-the-server]__08kwev1._.js +3 -0
  168. package/.next/server/chunks/[root-of-the-server]__08kwev1._.js.map +1 -0
  169. package/.next/server/chunks/[root-of-the-server]__096el.d._.js +3 -0
  170. package/.next/server/chunks/[root-of-the-server]__096el.d._.js.map +1 -0
  171. package/.next/server/chunks/[root-of-the-server]__0_bmt4z._.js +3 -0
  172. package/.next/server/chunks/[root-of-the-server]__0_bmt4z._.js.map +1 -0
  173. package/.next/server/chunks/[root-of-the-server]__0_ln2d2._.js +3 -0
  174. package/.next/server/chunks/[root-of-the-server]__0_ln2d2._.js.map +1 -0
  175. package/.next/server/chunks/[root-of-the-server]__0al3v65._.js +3 -0
  176. package/.next/server/chunks/[root-of-the-server]__0al3v65._.js.map +1 -0
  177. package/.next/server/chunks/[root-of-the-server]__0gf516b._.js +3 -0
  178. package/.next/server/chunks/[root-of-the-server]__0gf516b._.js.map +1 -0
  179. package/.next/server/chunks/[root-of-the-server]__0kdfw4x._.js +3 -0
  180. package/.next/server/chunks/[root-of-the-server]__0kdfw4x._.js.map +1 -0
  181. package/.next/server/chunks/[root-of-the-server]__0pxt00h._.js +3 -0
  182. package/.next/server/chunks/[root-of-the-server]__0pxt00h._.js.map +1 -0
  183. package/.next/server/chunks/[root-of-the-server]__0rubnza._.js +3 -0
  184. package/.next/server/chunks/[root-of-the-server]__0rubnza._.js.map +1 -0
  185. package/.next/server/chunks/[root-of-the-server]__0tn9iud._.js +3 -0
  186. package/.next/server/chunks/[root-of-the-server]__0tn9iud._.js.map +1 -0
  187. package/.next/server/chunks/[root-of-the-server]__0ws5o6i._.js +3 -0
  188. package/.next/server/chunks/[root-of-the-server]__0ws5o6i._.js.map +1 -0
  189. package/.next/server/chunks/[turbopack]_runtime.js +903 -0
  190. package/.next/server/chunks/[turbopack]_runtime.js.map +11 -0
  191. package/.next/server/chunks/node_modules_next_04~_e52._.js +13 -0
  192. package/.next/server/chunks/node_modules_next_04~_e52._.js.map +1 -0
  193. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0nyyph-.js +9 -0
  194. package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0nyyph-.js.map +1 -0
  195. package/.next/server/chunks/packages_observer-dashboard_src_lib_02.vvb.._.js +3 -0
  196. package/.next/server/chunks/packages_observer-dashboard_src_lib_02.vvb.._.js.map +1 -0
  197. package/.next/server/chunks/packages_observer-dashboard_src_lib_0rqgpk0._.js +3 -0
  198. package/.next/server/chunks/packages_observer-dashboard_src_lib_0rqgpk0._.js.map +1 -0
  199. package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__global-error_page_actions_0ekoxmy.js +3 -0
  200. package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__global-error_page_actions_0ekoxmy.js.map +1 -0
  201. package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__not-found_page_actions_09b3ti3.js +3 -0
  202. package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__not-found_page_actions_09b3ti3.js.map +1 -0
  203. package/.next/server/chunks/ssr/[root-of-the-server]__006f7~t._.js +4 -0
  204. package/.next/server/chunks/ssr/[root-of-the-server]__006f7~t._.js.map +1 -0
  205. package/.next/server/chunks/ssr/[root-of-the-server]__02jgvbi._.js +3 -0
  206. package/.next/server/chunks/ssr/[root-of-the-server]__02jgvbi._.js.map +1 -0
  207. package/.next/server/chunks/ssr/[root-of-the-server]__04j8t~1._.js +3 -0
  208. package/.next/server/chunks/ssr/[root-of-the-server]__04j8t~1._.js.map +1 -0
  209. package/.next/server/chunks/ssr/[root-of-the-server]__09c~s.0._.js +33 -0
  210. package/.next/server/chunks/ssr/[root-of-the-server]__09c~s.0._.js.map +1 -0
  211. package/.next/server/chunks/ssr/[root-of-the-server]__0cpy61n._.js +3 -0
  212. package/.next/server/chunks/ssr/[root-of-the-server]__0cpy61n._.js.map +1 -0
  213. package/.next/server/chunks/ssr/[root-of-the-server]__0cwa32j._.js +3 -0
  214. package/.next/server/chunks/ssr/[root-of-the-server]__0cwa32j._.js.map +1 -0
  215. package/.next/server/chunks/ssr/[root-of-the-server]__0fk_g0j._.js +19 -0
  216. package/.next/server/chunks/ssr/[root-of-the-server]__0fk_g0j._.js.map +1 -0
  217. package/.next/server/chunks/ssr/[root-of-the-server]__0pddpic._.js +3 -0
  218. package/.next/server/chunks/ssr/[root-of-the-server]__0pddpic._.js.map +1 -0
  219. package/.next/server/chunks/ssr/[root-of-the-server]__0pvtneq._.js +3 -0
  220. package/.next/server/chunks/ssr/[root-of-the-server]__0pvtneq._.js.map +1 -0
  221. package/.next/server/chunks/ssr/[root-of-the-server]__0xc-2vm._.js +3 -0
  222. package/.next/server/chunks/ssr/[root-of-the-server]__0xc-2vm._.js.map +1 -0
  223. package/.next/server/chunks/ssr/[root-of-the-server]__10xgshr._.js +33 -0
  224. package/.next/server/chunks/ssr/[root-of-the-server]__10xgshr._.js.map +1 -0
  225. package/.next/server/chunks/ssr/[turbopack]_runtime.js +903 -0
  226. package/.next/server/chunks/ssr/[turbopack]_runtime.js.map +11 -0
  227. package/.next/server/chunks/ssr/_00yo1im._.js +3 -0
  228. package/.next/server/chunks/ssr/_00yo1im._.js.map +1 -0
  229. package/.next/server/chunks/ssr/_03sbc.o._.js +3 -0
  230. package/.next/server/chunks/ssr/_03sbc.o._.js.map +1 -0
  231. package/.next/server/chunks/ssr/_04z5ea0._.js +3 -0
  232. package/.next/server/chunks/ssr/_04z5ea0._.js.map +1 -0
  233. package/.next/server/chunks/ssr/_0gmb3g_._.js +3 -0
  234. package/.next/server/chunks/ssr/_0gmb3g_._.js.map +1 -0
  235. package/.next/server/chunks/ssr/_0okz9j8._.js +6 -0
  236. package/.next/server/chunks/ssr/_0okz9j8._.js.map +1 -0
  237. package/.next/server/chunks/ssr/_0wrbwro._.js +6 -0
  238. package/.next/server/chunks/ssr/_0wrbwro._.js.map +1 -0
  239. package/.next/server/chunks/ssr/node_modules_09w7yel._.js +33 -0
  240. package/.next/server/chunks/ssr/node_modules_09w7yel._.js.map +1 -0
  241. package/.next/server/chunks/ssr/node_modules_next_dist_0avqw4q._.js +3 -0
  242. package/.next/server/chunks/ssr/node_modules_next_dist_0avqw4q._.js.map +1 -0
  243. package/.next/server/chunks/ssr/node_modules_next_dist_0h9llsw._.js +6 -0
  244. package/.next/server/chunks/ssr/node_modules_next_dist_0h9llsw._.js.map +1 -0
  245. package/.next/server/chunks/ssr/node_modules_next_dist_0i_._k3._.js +3 -0
  246. package/.next/server/chunks/ssr/node_modules_next_dist_0i_._k3._.js.map +1 -0
  247. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0ee1czk._.js +3 -0
  248. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0ee1czk._.js.map +1 -0
  249. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0lgvd_..js +3 -0
  250. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0lgvd_..js.map +1 -0
  251. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0cjv-23.js +3 -0
  252. package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0cjv-23.js.map +1 -0
  253. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0amzg6z.js +4 -0
  254. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0amzg6z.js.map +1 -0
  255. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dw1x0d.js +4 -0
  256. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dw1x0d.js.map +1 -0
  257. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js +4 -0
  258. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js.map +1 -0
  259. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_10iomok.js +4 -0
  260. package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_10iomok.js.map +1 -0
  261. package/.next/server/chunks/ssr/packages_observer-dashboard_0~a2tmu._.js +3 -0
  262. package/.next/server/chunks/ssr/packages_observer-dashboard_0~a2tmu._.js.map +1 -0
  263. package/.next/server/chunks/ssr/packages_observer-dashboard_src_0f.ozc-._.js +3 -0
  264. package/.next/server/chunks/ssr/packages_observer-dashboard_src_0f.ozc-._.js.map +1 -0
  265. package/.next/server/chunks/ssr/packages_observer-dashboard_src_components_providers_tsx_03_vrrn._.js +7 -0
  266. package/.next/server/chunks/ssr/packages_observer-dashboard_src_components_providers_tsx_03_vrrn._.js.map +1 -0
  267. package/.next/server/functions-config-manifest.json +4 -0
  268. package/.next/server/interception-route-rewrite-manifest.js +1 -0
  269. package/.next/server/middleware-build-manifest.js +23 -0
  270. package/.next/server/middleware-manifest.json +6 -0
  271. package/.next/server/next-font-manifest.js +1 -0
  272. package/.next/server/next-font-manifest.json +6 -0
  273. package/.next/server/pages/404.html +1 -0
  274. package/.next/server/pages/500.html +1 -0
  275. package/.next/server/pages-manifest.json +4 -0
  276. package/.next/server/prefetch-hints.json +1 -0
  277. package/.next/server/server-reference-manifest.js +1 -0
  278. package/.next/server/server-reference-manifest.json +24 -0
  279. package/.next/static/MvfPly4ZXHqaveDVOG1qq/_buildManifest.js +11 -0
  280. package/.next/static/MvfPly4ZXHqaveDVOG1qq/_clientMiddlewareManifest.js +1 -0
  281. package/.next/static/MvfPly4ZXHqaveDVOG1qq/_ssgManifest.js +1 -0
  282. package/.next/static/chunks/00c87uqn~kk2z.js +1 -0
  283. package/.next/static/chunks/01xlw8hd842-c.js +1 -0
  284. package/.next/static/chunks/02p9dpa.0oxiz.js +1 -0
  285. package/.next/static/chunks/034i63v_muq~d.js +1 -0
  286. package/.next/static/chunks/03mo-6~4pxdio.js +1 -0
  287. package/.next/static/chunks/03~yq9q893hmn.js +1 -0
  288. package/.next/static/chunks/04dm5qn77.fc2.js +1 -0
  289. package/.next/static/chunks/07uz2g0_38qia.js +4 -0
  290. package/.next/static/chunks/08nqnwvixoxnk.js +2 -0
  291. package/.next/static/chunks/0d3shmwh5_nmn.js +1 -0
  292. package/.next/static/chunks/0e_wyjw3nx.a..js +1 -0
  293. package/.next/static/chunks/0mstyq17cbf8-.js +1 -0
  294. package/.next/static/chunks/0ntuxw9.how-q.js +1 -0
  295. package/.next/static/chunks/0t3uzajv1qzmo.js +1 -0
  296. package/.next/static/chunks/0u~_nwr5-v.xp.js +1 -0
  297. package/.next/static/chunks/0wxcxh6eyzams.js +1 -0
  298. package/.next/static/chunks/0x6y7yt4kiddp.css +1 -0
  299. package/.next/static/chunks/0z6-vonyxr0dn.js +4 -0
  300. package/.next/static/chunks/0ze4gu236oq96.js +31 -0
  301. package/.next/static/chunks/0zwozael9msy1.js +1 -0
  302. package/.next/static/chunks/10qd__0r7a~.l.js +1 -0
  303. package/.next/static/chunks/11jh_u1ynmatg.js +1 -0
  304. package/.next/static/chunks/13xer3cb9shu-.js +5 -0
  305. package/.next/static/chunks/142zlnch_xmgd.js +1 -0
  306. package/.next/static/chunks/144kcri75qczu.js +1 -0
  307. package/.next/static/chunks/turbopack-0-ww6fe7b37qt.js +1 -0
  308. package/.next/static/media/icon.08ljfy7xai2x_.svg +20 -0
  309. package/LICENSE +21 -0
  310. package/README.md +490 -0
  311. package/next.config.mjs +25 -0
  312. package/package.json +104 -0
  313. package/postcss.config.mjs +8 -0
  314. package/src/app/actions/__tests__/approve-breakpoint.test.ts +246 -0
  315. package/src/app/actions/approve-breakpoint.ts +145 -0
  316. package/src/app/api/config/route.ts +137 -0
  317. package/src/app/api/digest/route.ts +45 -0
  318. package/src/app/api/runs/[runId]/events/route.ts +56 -0
  319. package/src/app/api/runs/[runId]/route.ts +84 -0
  320. package/src/app/api/runs/[runId]/tasks/[effectId]/route.ts +44 -0
  321. package/src/app/api/runs/route.ts +48 -0
  322. package/src/app/api/stream/route.ts +136 -0
  323. package/src/app/api/test/route.ts +1 -0
  324. package/src/app/api/version/route.ts +57 -0
  325. package/src/app/globals.css +555 -0
  326. package/src/app/icon.svg +20 -0
  327. package/src/app/layout.tsx +39 -0
  328. package/src/app/not-found.tsx +16 -0
  329. package/src/app/page.tsx +120 -0
  330. package/src/app/runs/[runId]/page.tsx +279 -0
  331. package/src/cli.ts +271 -0
  332. package/src/components/breakpoint/__tests__/breakpoint-approval.test.tsx +212 -0
  333. package/src/components/breakpoint/__tests__/breakpoint-panel.test.tsx +130 -0
  334. package/src/components/breakpoint/__tests__/file-preview.test.tsx +313 -0
  335. package/src/components/breakpoint/breakpoint-approval.tsx +138 -0
  336. package/src/components/breakpoint/breakpoint-panel.tsx +95 -0
  337. package/src/components/breakpoint/file-preview.tsx +215 -0
  338. package/src/components/dashboard/.gitkeep +0 -0
  339. package/src/components/dashboard/__tests__/breakpoint-banner.test.tsx +177 -0
  340. package/src/components/dashboard/__tests__/catch-up-banner.test.tsx +141 -0
  341. package/src/components/dashboard/__tests__/executive-summary-banner.test.tsx +164 -0
  342. package/src/components/dashboard/__tests__/kpi-grid.test.tsx +101 -0
  343. package/src/components/dashboard/__tests__/pagination-controls.test.tsx +125 -0
  344. package/src/components/dashboard/__tests__/project-accordion.test.tsx +97 -0
  345. package/src/components/dashboard/__tests__/project-list-view.test.tsx +174 -0
  346. package/src/components/dashboard/__tests__/project-search-input.test.tsx +110 -0
  347. package/src/components/dashboard/__tests__/project-section-header.test.tsx +91 -0
  348. package/src/components/dashboard/__tests__/project-section.test.tsx +151 -0
  349. package/src/components/dashboard/__tests__/run-card.test.tsx +164 -0
  350. package/src/components/dashboard/__tests__/run-filter-bar.test.tsx +109 -0
  351. package/src/components/dashboard/__tests__/run-list.test.tsx +123 -0
  352. package/src/components/dashboard/__tests__/search-filter.test.tsx +150 -0
  353. package/src/components/dashboard/__tests__/virtualized-run-list.test.tsx +179 -0
  354. package/src/components/dashboard/breakpoint-banner.tsx +301 -0
  355. package/src/components/dashboard/catch-up-banner.tsx +88 -0
  356. package/src/components/dashboard/executive-summary-banner.tsx +174 -0
  357. package/src/components/dashboard/global-search.tsx +323 -0
  358. package/src/components/dashboard/kpi-grid.tsx +140 -0
  359. package/src/components/dashboard/pagination-controls.tsx +100 -0
  360. package/src/components/dashboard/project-accordion.tsx +72 -0
  361. package/src/components/dashboard/project-health-card.tsx +536 -0
  362. package/src/components/dashboard/project-list-view.tsx +246 -0
  363. package/src/components/dashboard/project-search-input.tsx +41 -0
  364. package/src/components/dashboard/project-section-header.tsx +73 -0
  365. package/src/components/dashboard/project-section.tsx +89 -0
  366. package/src/components/dashboard/run-card.tsx +218 -0
  367. package/src/components/dashboard/run-filter-bar.tsx +100 -0
  368. package/src/components/dashboard/run-list.tsx +77 -0
  369. package/src/components/dashboard/search-filter.tsx +69 -0
  370. package/src/components/dashboard/virtualized-run-list.tsx +130 -0
  371. package/src/components/details/.gitkeep +0 -0
  372. package/src/components/details/__tests__/agent-panel.test.tsx +236 -0
  373. package/src/components/details/__tests__/json-tree.test.tsx +347 -0
  374. package/src/components/details/__tests__/log-viewer.test.tsx +168 -0
  375. package/src/components/details/__tests__/task-detail.test.tsx +212 -0
  376. package/src/components/details/__tests__/timing-panel.test.tsx +271 -0
  377. package/src/components/details/agent-panel.tsx +234 -0
  378. package/src/components/details/json-tree/categorize.ts +131 -0
  379. package/src/components/details/json-tree/index.tsx +120 -0
  380. package/src/components/details/json-tree/json-node.tsx +223 -0
  381. package/src/components/details/json-tree/smart-summary.tsx +596 -0
  382. package/src/components/details/json-tree/tree-controls.tsx +47 -0
  383. package/src/components/details/json-tree.tsx +9 -0
  384. package/src/components/details/log-viewer.tsx +140 -0
  385. package/src/components/details/task-detail.tsx +114 -0
  386. package/src/components/details/timing-panel.tsx +247 -0
  387. package/src/components/events/.gitkeep +0 -0
  388. package/src/components/events/__tests__/event-item.test.tsx +211 -0
  389. package/src/components/events/__tests__/event-stream.test.tsx +225 -0
  390. package/src/components/events/event-item.tsx +121 -0
  391. package/src/components/events/event-stream.tsx +260 -0
  392. package/src/components/notifications/.gitkeep +0 -0
  393. package/src/components/notifications/__tests__/notification-panel.test.tsx +287 -0
  394. package/src/components/notifications/__tests__/notification-provider.test.tsx +585 -0
  395. package/src/components/notifications/__tests__/toast-stack.test.tsx +217 -0
  396. package/src/components/notifications/notification-panel.tsx +124 -0
  397. package/src/components/notifications/notification-provider.tsx +175 -0
  398. package/src/components/notifications/toast-stack.tsx +75 -0
  399. package/src/components/pipeline/.gitkeep +0 -0
  400. package/src/components/pipeline/__tests__/parallel-group.test.tsx +88 -0
  401. package/src/components/pipeline/__tests__/pipeline-view.test.tsx +345 -0
  402. package/src/components/pipeline/__tests__/step-card.test.tsx +330 -0
  403. package/src/components/pipeline/parallel-group.tsx +39 -0
  404. package/src/components/pipeline/pipeline-view.tsx +197 -0
  405. package/src/components/pipeline/step-card.tsx +166 -0
  406. package/src/components/providers/event-stream-provider.tsx +29 -0
  407. package/src/components/providers.tsx +24 -0
  408. package/src/components/shared/.gitkeep +0 -0
  409. package/src/components/shared/__tests__/empty-state.test.tsx +49 -0
  410. package/src/components/shared/__tests__/friendly-id.test.tsx +47 -0
  411. package/src/components/shared/__tests__/kbd.test.tsx +45 -0
  412. package/src/components/shared/__tests__/kind-badge.test.tsx +71 -0
  413. package/src/components/shared/__tests__/metrics-row.test.tsx +74 -0
  414. package/src/components/shared/__tests__/outcome-banner.test.tsx +71 -0
  415. package/src/components/shared/__tests__/progress-bar.test.tsx +89 -0
  416. package/src/components/shared/__tests__/session-pill.test.tsx +62 -0
  417. package/src/components/shared/__tests__/settings-modal.test.tsx +201 -0
  418. package/src/components/shared/__tests__/shortcuts-help.test.tsx +103 -0
  419. package/src/components/shared/__tests__/status-badge.test.tsx +98 -0
  420. package/src/components/shared/__tests__/theme-provider.test.tsx +100 -0
  421. package/src/components/shared/__tests__/truncated-id.test.tsx +53 -0
  422. package/src/components/shared/app-footer.tsx +80 -0
  423. package/src/components/shared/app-header.tsx +160 -0
  424. package/src/components/shared/empty-state.tsx +18 -0
  425. package/src/components/shared/error-boundary.tsx +81 -0
  426. package/src/components/shared/friendly-id.tsx +48 -0
  427. package/src/components/shared/kbd.tsx +15 -0
  428. package/src/components/shared/kind-badge.tsx +51 -0
  429. package/src/components/shared/metrics-row.tsx +106 -0
  430. package/src/components/shared/outcome-banner.tsx +56 -0
  431. package/src/components/shared/progress-bar.tsx +42 -0
  432. package/src/components/shared/session-pill.tsx +69 -0
  433. package/src/components/shared/settings-modal.tsx +509 -0
  434. package/src/components/shared/shortcuts-help.tsx +113 -0
  435. package/src/components/shared/status-badge.tsx +110 -0
  436. package/src/components/shared/theme-provider.tsx +46 -0
  437. package/src/components/shared/truncated-id.tsx +51 -0
  438. package/src/components/ui/.gitkeep +0 -0
  439. package/src/components/ui/__tests__/accordion.test.tsx +96 -0
  440. package/src/components/ui/__tests__/badge.test.tsx +69 -0
  441. package/src/components/ui/__tests__/button.test.tsx +113 -0
  442. package/src/components/ui/__tests__/tabs.test.tsx +75 -0
  443. package/src/components/ui/__tests__/tooltip.test.tsx +90 -0
  444. package/src/components/ui/accordion.tsx +61 -0
  445. package/src/components/ui/badge.tsx +25 -0
  446. package/src/components/ui/button.tsx +40 -0
  447. package/src/components/ui/card.tsx +21 -0
  448. package/src/components/ui/scroll-area.tsx +35 -0
  449. package/src/components/ui/separator.tsx +24 -0
  450. package/src/components/ui/tabs.tsx +64 -0
  451. package/src/components/ui/tooltip.tsx +37 -0
  452. package/src/hooks/.gitkeep +0 -0
  453. package/src/hooks/__tests__/use-animated-number.test.ts +184 -0
  454. package/src/hooks/__tests__/use-batched-updates.test.ts +315 -0
  455. package/src/hooks/__tests__/use-event-stream.test.ts +243 -0
  456. package/src/hooks/__tests__/use-keyboard.test.ts +217 -0
  457. package/src/hooks/__tests__/use-notifications.test.ts +230 -0
  458. package/src/hooks/__tests__/use-polling.test.ts +274 -0
  459. package/src/hooks/__tests__/use-project-runs.test.ts +163 -0
  460. package/src/hooks/__tests__/use-projects.test.ts +248 -0
  461. package/src/hooks/__tests__/use-run-dashboard.test.ts +168 -0
  462. package/src/hooks/__tests__/use-run-detail.test.ts +273 -0
  463. package/src/hooks/__tests__/use-smart-polling.test.ts +305 -0
  464. package/src/hooks/use-animated-number.ts +87 -0
  465. package/src/hooks/use-batched-updates.ts +150 -0
  466. package/src/hooks/use-event-stream.ts +150 -0
  467. package/src/hooks/use-keyboard.ts +45 -0
  468. package/src/hooks/use-notifications.ts +82 -0
  469. package/src/hooks/use-persisted-state.ts +60 -0
  470. package/src/hooks/use-polling.ts +60 -0
  471. package/src/hooks/use-project-runs.ts +51 -0
  472. package/src/hooks/use-projects.ts +26 -0
  473. package/src/hooks/use-run-dashboard.ts +207 -0
  474. package/src/hooks/use-run-detail.ts +77 -0
  475. package/src/hooks/use-smart-polling.ts +144 -0
  476. package/src/lib/.gitkeep +0 -0
  477. package/src/lib/__tests__/cn.test.ts +69 -0
  478. package/src/lib/__tests__/config-loader.test.ts +210 -0
  479. package/src/lib/__tests__/config.test.ts +561 -0
  480. package/src/lib/__tests__/error-handler.test.ts +143 -0
  481. package/src/lib/__tests__/fetcher.test.ts +517 -0
  482. package/src/lib/__tests__/global-registry.test.ts +214 -0
  483. package/src/lib/__tests__/parser.test.ts +1532 -0
  484. package/src/lib/__tests__/path-resolver.test.ts +112 -0
  485. package/src/lib/__tests__/run-cache.test.ts +591 -0
  486. package/src/lib/__tests__/server-init.test.ts +512 -0
  487. package/src/lib/__tests__/source-discovery.test.ts +246 -0
  488. package/src/lib/__tests__/utils.test.ts +160 -0
  489. package/src/lib/__tests__/watcher.test.ts +227 -0
  490. package/src/lib/cn.ts +6 -0
  491. package/src/lib/config-loader.ts +195 -0
  492. package/src/lib/config.ts +20 -0
  493. package/src/lib/error-handler.ts +76 -0
  494. package/src/lib/fetcher.ts +394 -0
  495. package/src/lib/global-registry.ts +117 -0
  496. package/src/lib/parser.ts +794 -0
  497. package/src/lib/path-resolver.ts +16 -0
  498. package/src/lib/run-cache.ts +404 -0
  499. package/src/lib/server-init.ts +226 -0
  500. package/src/lib/services/__tests__/run-query-service.test.ts +819 -0
  501. package/src/lib/services/run-query-service.ts +286 -0
  502. package/src/lib/source-discovery.ts +216 -0
  503. package/src/lib/utils.ts +103 -0
  504. package/src/lib/watcher.ts +265 -0
  505. package/src/test/fixtures.ts +269 -0
  506. package/src/test/mocks/handlers.ts +110 -0
  507. package/src/test/mocks/server.ts +17 -0
  508. package/src/test/setup.ts +200 -0
  509. package/src/test/test-utils.tsx +36 -0
  510. package/src/types/.gitkeep +0 -0
  511. package/src/types/breakpoint.ts +17 -0
  512. package/src/types/index.ts +214 -0
  513. package/tsconfig.json +50 -0
@@ -0,0 +1,217 @@
1
+ import { render, screen, setupUser } from '@/test/test-utils';
2
+ import { ToastStack } from '../toast-stack';
3
+ import type { AppNotification } from '@/hooks/use-notifications';
4
+
5
+ // Mock next/navigation
6
+ const mockPush = vi.fn();
7
+ vi.mock('next/navigation', () => ({
8
+ useRouter: () => ({
9
+ push: mockPush,
10
+ replace: vi.fn(),
11
+ back: vi.fn(),
12
+ forward: vi.fn(),
13
+ refresh: vi.fn(),
14
+ prefetch: vi.fn(),
15
+ }),
16
+ }));
17
+
18
+ function makeNotification(overrides: Partial<AppNotification> = {}): AppNotification {
19
+ return {
20
+ id: overrides.id ?? 'toast-1',
21
+ title: overrides.title ?? 'Toast Title',
22
+ body: overrides.body ?? 'Toast body text',
23
+ type: overrides.type ?? 'info',
24
+ timestamp: overrides.timestamp ?? Date.now(),
25
+ href: overrides.href,
26
+ persistent: overrides.persistent,
27
+ };
28
+ }
29
+
30
+ describe('ToastStack', () => {
31
+ beforeEach(() => {
32
+ vi.clearAllMocks();
33
+ });
34
+
35
+ // -----------------------------------------------------------------------
36
+ // Empty state
37
+ // -----------------------------------------------------------------------
38
+ it('renders nothing when notifications array is empty', () => {
39
+ const { container } = render(
40
+ <ToastStack notifications={[]} onDismiss={vi.fn()} />,
41
+ );
42
+
43
+ // The container div is rendered but no toast items inside
44
+ const toastItems = container.querySelectorAll('[class*="animate-slide-in-right"]');
45
+ expect(toastItems).toHaveLength(0);
46
+ });
47
+
48
+ // -----------------------------------------------------------------------
49
+ // Renders toasts
50
+ // -----------------------------------------------------------------------
51
+ it('renders toast for each notification', () => {
52
+ const notifications = [
53
+ makeNotification({ id: 't1', title: 'First Toast', body: 'Body 1' }),
54
+ makeNotification({ id: 't2', title: 'Second Toast', body: 'Body 2' }),
55
+ ];
56
+
57
+ render(<ToastStack notifications={notifications} onDismiss={vi.fn()} />);
58
+
59
+ expect(screen.getByText('First Toast')).toBeInTheDocument();
60
+ expect(screen.getByText('Body 1')).toBeInTheDocument();
61
+ expect(screen.getByText('Second Toast')).toBeInTheDocument();
62
+ expect(screen.getByText('Body 2')).toBeInTheDocument();
63
+ });
64
+
65
+ // -----------------------------------------------------------------------
66
+ // Stacking -- multiple toasts rendered in order
67
+ // -----------------------------------------------------------------------
68
+ it('stacks multiple toasts', () => {
69
+ const notifications = [
70
+ makeNotification({ id: 't1', title: 'Alpha' }),
71
+ makeNotification({ id: 't2', title: 'Beta' }),
72
+ makeNotification({ id: 't3', title: 'Gamma' }),
73
+ ];
74
+
75
+ render(<ToastStack notifications={notifications} onDismiss={vi.fn()} />);
76
+
77
+ expect(screen.getByText('Alpha')).toBeInTheDocument();
78
+ expect(screen.getByText('Beta')).toBeInTheDocument();
79
+ expect(screen.getByText('Gamma')).toBeInTheDocument();
80
+ });
81
+
82
+ // -----------------------------------------------------------------------
83
+ // Dismiss button
84
+ // -----------------------------------------------------------------------
85
+ it('calls onDismiss with the toast id when dismiss button is clicked', async () => {
86
+ const user = setupUser();
87
+ const onDismiss = vi.fn();
88
+ const notifications = [
89
+ makeNotification({ id: 'toast-42', title: 'Dismissable' }),
90
+ ];
91
+
92
+ render(<ToastStack notifications={notifications} onDismiss={onDismiss} />);
93
+
94
+ // The X button inside the toast
95
+ const xIcon = screen.getByTestId('icon-X');
96
+ const dismissButton = xIcon.closest('button')!;
97
+ await user.click(dismissButton);
98
+
99
+ expect(onDismiss).toHaveBeenCalledWith('toast-42');
100
+ });
101
+
102
+ it('dismiss does not trigger navigation', async () => {
103
+ const user = setupUser();
104
+ const onDismiss = vi.fn();
105
+ const notifications = [
106
+ makeNotification({ id: 't1', title: 'Has Link', href: '/runs/abc' }),
107
+ ];
108
+
109
+ render(<ToastStack notifications={notifications} onDismiss={onDismiss} />);
110
+
111
+ // Click the dismiss button (not the toast body)
112
+ const xIcon = screen.getByTestId('icon-X');
113
+ const dismissButton = xIcon.closest('button')!;
114
+ await user.click(dismissButton);
115
+
116
+ // Dismiss was called but navigation should NOT happen (stopPropagation)
117
+ expect(onDismiss).toHaveBeenCalledWith('t1');
118
+ expect(mockPush).not.toHaveBeenCalled();
119
+ });
120
+
121
+ // -----------------------------------------------------------------------
122
+ // Clicking toast with href navigates
123
+ // -----------------------------------------------------------------------
124
+ it('navigates and dismisses when a toast with href is clicked', async () => {
125
+ const user = setupUser();
126
+ const onDismiss = vi.fn();
127
+ const notifications = [
128
+ makeNotification({ id: 't1', title: 'Clickable', href: '/runs/xyz' }),
129
+ ];
130
+
131
+ render(<ToastStack notifications={notifications} onDismiss={onDismiss} />);
132
+
133
+ await user.click(screen.getByText('Clickable'));
134
+
135
+ expect(mockPush).toHaveBeenCalledWith('/runs/xyz');
136
+ expect(onDismiss).toHaveBeenCalledWith('t1');
137
+ });
138
+
139
+ it('does not navigate when a toast without href is clicked', async () => {
140
+ const user = setupUser();
141
+ const onDismiss = vi.fn();
142
+ const notifications = [
143
+ makeNotification({ id: 't1', title: 'No Link' }),
144
+ ];
145
+
146
+ render(<ToastStack notifications={notifications} onDismiss={onDismiss} />);
147
+
148
+ await user.click(screen.getByText('No Link'));
149
+
150
+ expect(mockPush).not.toHaveBeenCalled();
151
+ expect(onDismiss).not.toHaveBeenCalled();
152
+ });
153
+
154
+ // -----------------------------------------------------------------------
155
+ // Notification type icons
156
+ // -----------------------------------------------------------------------
157
+ it('renders the correct icon for success type', () => {
158
+ const notifications = [makeNotification({ id: 't1', type: 'success' })];
159
+
160
+ render(<ToastStack notifications={notifications} onDismiss={vi.fn()} />);
161
+
162
+ expect(screen.getByTestId('icon-CheckCircle2')).toBeInTheDocument();
163
+ });
164
+
165
+ it('renders the correct icon for error type', () => {
166
+ const notifications = [makeNotification({ id: 't1', type: 'error' })];
167
+
168
+ render(<ToastStack notifications={notifications} onDismiss={vi.fn()} />);
169
+
170
+ expect(screen.getByTestId('icon-XCircle')).toBeInTheDocument();
171
+ });
172
+
173
+ it('renders the correct icon for warning type', () => {
174
+ const notifications = [makeNotification({ id: 't1', type: 'warning' })];
175
+
176
+ render(<ToastStack notifications={notifications} onDismiss={vi.fn()} />);
177
+
178
+ expect(screen.getByTestId('icon-AlertTriangle')).toBeInTheDocument();
179
+ });
180
+
181
+ it('renders the correct icon for info type', () => {
182
+ const notifications = [makeNotification({ id: 't1', type: 'info' })];
183
+
184
+ render(<ToastStack notifications={notifications} onDismiss={vi.fn()} />);
185
+
186
+ expect(screen.getByTestId('icon-Info')).toBeInTheDocument();
187
+ });
188
+
189
+ // -----------------------------------------------------------------------
190
+ // Persistent (pinned) notification
191
+ // -----------------------------------------------------------------------
192
+ it('shows pin icon for persistent notifications', () => {
193
+ const persistentNotif = makeNotification({ id: 't-pin', persistent: true });
194
+ render(<ToastStack notifications={[persistentNotif]} onDismiss={vi.fn()} />);
195
+ expect(screen.getByTitle('Pinned — won\'t auto-dismiss')).toBeInTheDocument();
196
+ });
197
+
198
+ it('does not show pin icon for non-persistent notifications', () => {
199
+ const notif = makeNotification({ id: 't-nopin' });
200
+ render(<ToastStack notifications={[notif]} onDismiss={vi.fn()} />);
201
+ expect(screen.queryByTitle('Pinned — won\'t auto-dismiss')).not.toBeInTheDocument();
202
+ });
203
+
204
+ // -----------------------------------------------------------------------
205
+ // Slide-in animation class
206
+ // -----------------------------------------------------------------------
207
+ it('applies the slide-in animation class to toasts', () => {
208
+ const notifications = [makeNotification({ id: 't1' })];
209
+
210
+ const { container } = render(
211
+ <ToastStack notifications={notifications} onDismiss={vi.fn()} />,
212
+ );
213
+
214
+ const toastEl = container.querySelector('[class*="animate-slide-in-right"]');
215
+ expect(toastEl).toBeInTheDocument();
216
+ });
217
+ });
@@ -0,0 +1,124 @@
1
+ "use client";
2
+ import { useRouter } from "next/navigation";
3
+ import * as Dialog from "@radix-ui/react-dialog";
4
+ import { cn } from "@/lib/cn";
5
+ import { X, CheckCircle2, XCircle, AlertTriangle, Info, Bell, Pin } from "lucide-react";
6
+ import type { AppNotification } from "@/hooks/use-notifications";
7
+
8
+ const iconMap: Record<AppNotification["type"], React.ReactNode> = {
9
+ success: <CheckCircle2 className="h-4 w-4 text-success drop-shadow-[var(--drop-glow-success)]" />,
10
+ error: <XCircle className="h-4 w-4 text-error drop-shadow-[var(--drop-glow-error)]" />,
11
+ warning: <AlertTriangle className="h-4 w-4 text-warning drop-shadow-[var(--drop-glow-warning)]" />,
12
+ info: <Info className="h-4 w-4 text-info drop-shadow-[var(--drop-glow-cyan)]" />,
13
+ };
14
+
15
+ const borderMap: Record<AppNotification["type"], string> = {
16
+ success: "border-l-success",
17
+ error: "border-l-error",
18
+ warning: "border-l-warning",
19
+ info: "border-l-info",
20
+ };
21
+
22
+ interface NotificationPanelProps {
23
+ open: boolean;
24
+ notifications: AppNotification[];
25
+ onDismiss: (id: string) => void;
26
+ onClose: () => void;
27
+ }
28
+
29
+ export function NotificationPanel({ open, notifications, onDismiss, onClose }: NotificationPanelProps) {
30
+ const router = useRouter();
31
+
32
+ const handleClick = (notif: AppNotification) => {
33
+ if (notif.href) {
34
+ router.push(notif.href);
35
+ onDismiss(notif.id);
36
+ onClose();
37
+ }
38
+ };
39
+
40
+ const formatTime = (timestamp: number) => {
41
+ const now = Date.now();
42
+ const diff = now - timestamp;
43
+ const seconds = Math.floor(diff / 1000);
44
+ const minutes = Math.floor(seconds / 60);
45
+ const hours = Math.floor(minutes / 60);
46
+
47
+ if (seconds < 60) return "just now";
48
+ if (minutes < 60) return `${minutes}m ago`;
49
+ if (hours < 24) return `${hours}h ago`;
50
+ return new Date(timestamp).toLocaleString();
51
+ };
52
+
53
+ return (
54
+ <Dialog.Root open={open} onOpenChange={(isOpen) => { if (!isOpen) onClose(); }}>
55
+ <Dialog.Portal>
56
+ <Dialog.Overlay className="fixed inset-0 z-50 bg-black/70 backdrop-blur-sm" />
57
+ <Dialog.Content
58
+ data-testid="notification-panel"
59
+ className="fixed right-4 top-4 z-50 rounded-lg border border-[var(--glass-border)] bg-[var(--glass-bg)] backdrop-blur-xl shadow-glass w-full max-w-md max-h-[80vh] flex flex-col"
60
+ >
61
+ <div className="flex items-center justify-between p-4 border-b border-[var(--glass-border-subtle)]">
62
+ <div className="flex items-center gap-2">
63
+ <Bell className="h-4 w-4 text-primary/60" />
64
+ <Dialog.Title className="text-sm font-medium text-foreground">Notifications</Dialog.Title>
65
+ {notifications.length > 0 && (
66
+ <span className="text-xs text-primary/70 font-mono">({notifications.length})</span>
67
+ )}
68
+ </div>
69
+ <Dialog.Close asChild>
70
+ <button
71
+ className="rounded-md p-2 min-h-[44px] min-w-[44px] flex items-center justify-center text-foreground-muted hover:text-primary transition-colors"
72
+ >
73
+ <X className="h-4 w-4" />
74
+ </button>
75
+ </Dialog.Close>
76
+ </div>
77
+ <div className="flex-1 overflow-y-auto p-2">
78
+ {notifications.length === 0 ? (
79
+ <div className="flex flex-col items-center justify-center py-12 text-foreground-muted">
80
+ <Bell className="h-8 w-8 mb-2 opacity-50" />
81
+ <p className="text-sm">No notifications</p>
82
+ </div>
83
+ ) : (
84
+ <div className="space-y-2">
85
+ {notifications.map((notif) => (
86
+ <div
87
+ key={notif.id}
88
+ data-testid={`notification-item-${notif.id}`}
89
+ className={cn(
90
+ "rounded-lg border border-[var(--glass-border-faint)] bg-[var(--glass-card-bg)] p-3 border-l-2",
91
+ "transition-colors duration-150",
92
+ notif.href && "cursor-pointer hover:bg-[var(--glass-border-faint)]",
93
+ borderMap[notif.type]
94
+ )}
95
+ onClick={() => handleClick(notif)}
96
+ >
97
+ <div className="flex items-start gap-2">
98
+ <div className="shrink-0 mt-0.5">{iconMap[notif.type]}</div>
99
+ {notif.persistent && <span title="Pinned — won't auto-dismiss" className="shrink-0"><Pin className="h-3 w-3 text-primary/50" /></span>}
100
+ <div className="flex-1 min-w-0">
101
+ <p className="text-sm font-medium text-foreground">{notif.title}</p>
102
+ <p className="text-xs text-foreground-muted mt-0.5">{notif.body}</p>
103
+ <p className="text-xs text-foreground-muted mt-1 opacity-70">
104
+ {formatTime(notif.timestamp)}
105
+ {notif.persistent && <span className="text-primary/50 ml-1">· Pinned</span>}
106
+ </p>
107
+ </div>
108
+ <button
109
+ onClick={(e) => { e.stopPropagation(); onDismiss(notif.id); }}
110
+ className="shrink-0 rounded-md p-2 min-h-[44px] min-w-[44px] flex items-center justify-center text-foreground-muted hover:text-primary transition-colors"
111
+ >
112
+ <X className="h-3.5 w-3.5" />
113
+ </button>
114
+ </div>
115
+ </div>
116
+ ))}
117
+ </div>
118
+ )}
119
+ </div>
120
+ </Dialog.Content>
121
+ </Dialog.Portal>
122
+ </Dialog.Root>
123
+ );
124
+ }
@@ -0,0 +1,175 @@
1
+ "use client";
2
+ import { createContext, useContext, useEffect, useRef, type ReactNode } from "react";
3
+ import {
4
+ useNotifications,
5
+ type AppNotification,
6
+ } from "@/hooks/use-notifications";
7
+ import { ToastStack } from "./toast-stack";
8
+ import { usePolling } from "@/hooks/use-polling";
9
+ import { formatShortId } from "@/lib/utils";
10
+ import type { DigestResponse } from "@/types";
11
+
12
+ interface NotificationContextValue {
13
+ notify: (
14
+ title: string,
15
+ body: string,
16
+ type?: AppNotification["type"],
17
+ options?: { href?: string; persistent?: boolean },
18
+ ) => void;
19
+ requestPermission: () => void;
20
+ permission: NotificationPermission;
21
+ notifications: AppNotification[];
22
+ dismiss: (id: string) => void;
23
+ }
24
+
25
+ const NotificationContext = createContext<NotificationContextValue>({
26
+ notify: () => {},
27
+ requestPermission: () => {},
28
+ permission: "default",
29
+ notifications: [],
30
+ dismiss: () => {},
31
+ });
32
+
33
+ export const useNotificationContext = () => useContext(NotificationContext);
34
+
35
+ // Watermark per run: tracks the highest-seen state so notifications only fire once
36
+ interface RunWatermark {
37
+ status: string;
38
+ completedTasks: number;
39
+ pendingBreakpoints: number;
40
+ notifiedCompleted: boolean;
41
+ notifiedFailed: boolean;
42
+ notifiedWaiting: boolean;
43
+ }
44
+
45
+ /** Duration after mount during which watermarks are seeded silently (no notifications). */
46
+ export const STABILIZATION_WINDOW_MS = 10_000; // 10 seconds
47
+
48
+ export function NotificationProvider({ children }: { children: ReactNode }) {
49
+ const { notifications, notify, dismiss, requestPermission, permission } =
50
+ useNotifications();
51
+ const { data: digest } = usePolling<DigestResponse>("/api/digest", {
52
+ interval: 3000,
53
+ });
54
+ // Permanent watermark: tracks highest-seen state per run across all polls
55
+ const watermarkRef = useRef<Map<string, RunWatermark>>(new Map());
56
+ const mountedAtRef = useRef(Date.now());
57
+
58
+ useEffect(() => {
59
+ if (!digest) return;
60
+
61
+ const watermarks = watermarkRef.current;
62
+ const isStabilizing =
63
+ Date.now() - mountedAtRef.current < STABILIZATION_WINDOW_MS;
64
+
65
+ // During the stabilization window, seed watermarks for every run without
66
+ // firing any notifications. This replaces the old count-based INIT_SKIP
67
+ // approach which could miss runs when there are many active runs and the
68
+ // first N polls didn't cover them all.
69
+ if (isStabilizing) {
70
+ for (const run of digest.runs) {
71
+ watermarks.set(run.runId, {
72
+ status: run.status,
73
+ completedTasks: run.completedTasks,
74
+ pendingBreakpoints: run.pendingBreakpoints || 0,
75
+ notifiedCompleted: run.status === "completed",
76
+ notifiedFailed: run.status === "failed",
77
+ notifiedWaiting: run.status === "waiting",
78
+ });
79
+ }
80
+ return;
81
+ }
82
+
83
+ for (const run of digest.runs) {
84
+ const wm = watermarks.get(run.runId);
85
+
86
+ if (!wm) {
87
+ // Genuinely new run — seed watermark and notify
88
+ watermarks.set(run.runId, {
89
+ status: run.status,
90
+ completedTasks: run.completedTasks,
91
+ pendingBreakpoints: run.pendingBreakpoints || 0,
92
+ notifiedCompleted: run.status === "completed",
93
+ notifiedFailed: run.status === "failed",
94
+ notifiedWaiting: run.status === "waiting",
95
+ });
96
+ notify(
97
+ "New Run Started",
98
+ `${formatShortId(run.runId, 4)} started`,
99
+ "info",
100
+ { href: `/runs/${run.runId}` },
101
+ );
102
+ continue;
103
+ }
104
+
105
+ // Run completed — only notify once ever
106
+ if (run.status === "completed" && !wm.notifiedCompleted) {
107
+ wm.notifiedCompleted = true;
108
+ notify(
109
+ "Run Completed",
110
+ `${formatShortId(run.runId, 4)} finished successfully`,
111
+ "success",
112
+ { href: `/runs/${run.runId}` },
113
+ );
114
+ }
115
+
116
+ // Run failed — only notify once ever
117
+ if (run.status === "failed" && !wm.notifiedFailed) {
118
+ wm.notifiedFailed = true;
119
+ notify(
120
+ "Run Failed",
121
+ `${formatShortId(run.runId, 4)} failed`,
122
+ "error",
123
+ { href: `/runs/${run.runId}` },
124
+ );
125
+ }
126
+
127
+ // Track completed-task watermark (no per-task notification — the
128
+ // terminal "Run Completed" notification already covers this, and
129
+ // per-task notifications flood the panel when many runs are active).
130
+ if (run.completedTasks > wm.completedTasks) {
131
+ wm.completedTasks = run.completedTasks;
132
+ }
133
+
134
+ // Run transitioned to waiting (breakpoint) — only notify once per waiting episode
135
+ // These notifications are persistent (no auto-dismiss) so users cannot miss them
136
+ if (run.status === "waiting" && !wm.notifiedWaiting) {
137
+ wm.notifiedWaiting = true;
138
+ const breakpointTitle = run.breakpointQuestion || "Review required";
139
+ notify(
140
+ `Run ${formatShortId(run.runId, 4)} needs attention`,
141
+ breakpointTitle,
142
+ "warning",
143
+ { href: `/runs/${run.runId}`, persistent: true },
144
+ );
145
+ }
146
+
147
+ // Reset waiting flag when run leaves waiting state (allows re-notification on next breakpoint)
148
+ if (run.status !== "waiting" && wm.notifiedWaiting) {
149
+ wm.notifiedWaiting = false;
150
+ }
151
+
152
+ // Breakpoint resolved — pending count dropped to zero
153
+ if (wm.pendingBreakpoints > 0 && (run.pendingBreakpoints === 0 || run.pendingBreakpoints === undefined)) {
154
+ notify(
155
+ "Breakpoint Resolved",
156
+ `Breakpoint in ${formatShortId(run.runId, 4)} was approved`,
157
+ "success",
158
+ { href: `/runs/${run.runId}` },
159
+ );
160
+ }
161
+ wm.pendingBreakpoints = run.pendingBreakpoints || 0;
162
+
163
+ wm.status = run.status;
164
+ }
165
+ }, [digest, notify]);
166
+
167
+ return (
168
+ <NotificationContext.Provider
169
+ value={{ notify, requestPermission, permission, notifications, dismiss }}
170
+ >
171
+ {children}
172
+ <ToastStack notifications={notifications} onDismiss={dismiss} />
173
+ </NotificationContext.Provider>
174
+ );
175
+ }
@@ -0,0 +1,75 @@
1
+ "use client";
2
+ import { useRouter } from "next/navigation";
3
+ import { cn } from "@/lib/cn";
4
+ import { X, CheckCircle2, XCircle, AlertTriangle, Info, Pin } from "lucide-react";
5
+ import type { AppNotification } from "@/hooks/use-notifications";
6
+
7
+ const iconMap: Record<AppNotification["type"], React.ReactNode> = {
8
+ success: <CheckCircle2 className="h-4 w-4 text-success drop-shadow-[var(--drop-glow-success)]" />,
9
+ error: <XCircle className="h-4 w-4 text-error drop-shadow-[var(--drop-glow-error)]" />,
10
+ warning: <AlertTriangle className="h-4 w-4 text-warning drop-shadow-[var(--drop-glow-warning)]" />,
11
+ info: <Info className="h-4 w-4 text-info drop-shadow-[var(--drop-glow-cyan)]" />,
12
+ };
13
+
14
+ const borderMap: Record<AppNotification["type"], string> = {
15
+ success: "border-l-success shadow-toast-glow-success",
16
+ error: "border-l-error shadow-toast-glow-error",
17
+ warning: "border-l-warning shadow-toast-glow-warning",
18
+ info: "border-l-info shadow-toast-glow-cyan",
19
+ };
20
+
21
+ interface ToastStackProps {
22
+ notifications: AppNotification[];
23
+ onDismiss: (id: string) => void;
24
+ }
25
+
26
+ export function ToastStack({ notifications, onDismiss }: ToastStackProps) {
27
+ const router = useRouter();
28
+
29
+ const handleClick = (notif: AppNotification) => {
30
+ if (notif.href) {
31
+ router.push(notif.href);
32
+ onDismiss(notif.id);
33
+ }
34
+ };
35
+
36
+ return (
37
+ <div role="log" aria-live="assertive" aria-label="Notifications" data-testid="toast-stack" className="fixed bottom-4 right-4 z-50 flex flex-col gap-2 max-w-sm">
38
+ {notifications.map((notif) => (
39
+ <div
40
+ key={notif.id}
41
+ data-testid={`toast-item-${notif.id}`}
42
+ className={cn(
43
+ "rounded-lg border border-[var(--glass-border-subtle)] bg-[var(--glass-bg-heavy)] backdrop-blur-sm p-3 shadow-lg border-l-2",
44
+ "animate-slide-in-right",
45
+ notif.href && "cursor-pointer hover:bg-card-hover",
46
+ notif.persistent && "ring-1 ring-primary/20",
47
+ borderMap[notif.type]
48
+ )}
49
+ onClick={() => handleClick(notif)}
50
+ >
51
+ <div className="flex items-start gap-2">
52
+ <div className="shrink-0 mt-0.5">{iconMap[notif.type]}</div>
53
+ {notif.persistent && <span title="Pinned — won't auto-dismiss" className="shrink-0"><Pin className="h-3 w-3 text-primary/50" /></span>}
54
+ <div className="flex-1 min-w-0">
55
+ <div className="flex items-center gap-1">
56
+ <p className="text-sm font-medium text-foreground">{notif.title}</p>
57
+ {notif.persistent && (
58
+ <Pin className="h-3 w-3 text-warning shrink-0" aria-label="Persistent notification" />
59
+ )}
60
+ </div>
61
+ <p className="text-xs text-foreground-muted mt-0.5 truncate">{notif.body}</p>
62
+ </div>
63
+ <button
64
+ onClick={(e) => { e.stopPropagation(); onDismiss(notif.id); }}
65
+ className="shrink-0 rounded-md p-2 min-h-[44px] min-w-[44px] flex items-center justify-center text-foreground-muted hover:text-primary transition-colors"
66
+ aria-label={`Dismiss ${notif.title}`}
67
+ >
68
+ <X className="h-3.5 w-3.5" />
69
+ </button>
70
+ </div>
71
+ </div>
72
+ ))}
73
+ </div>
74
+ );
75
+ }
File without changes
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import { describe, it, expect } from 'vitest';
3
+ import { render, screen } from '@/test/test-utils';
4
+ import { ParallelGroup } from '../parallel-group';
5
+
6
+ describe('ParallelGroup', () => {
7
+ it('renders without crashing', () => {
8
+ render(
9
+ <ParallelGroup count={3}>
10
+ <div>child</div>
11
+ </ParallelGroup>,
12
+ );
13
+ expect(screen.getByText('parallel')).toBeInTheDocument();
14
+ });
15
+
16
+ it('displays the count label with correct task count', () => {
17
+ render(
18
+ <ParallelGroup count={5}>
19
+ <div>child</div>
20
+ </ParallelGroup>,
21
+ );
22
+ // The label renders as "· 5 tasks" via &middot; entity
23
+ expect(screen.getByText(/5 tasks/)).toBeInTheDocument();
24
+ });
25
+
26
+ it('renders children inside the group', () => {
27
+ render(
28
+ <ParallelGroup count={2}>
29
+ <div data-testid="child-a">Alpha</div>
30
+ <div data-testid="child-b">Beta</div>
31
+ </ParallelGroup>,
32
+ );
33
+ expect(screen.getByTestId('child-a')).toBeInTheDocument();
34
+ expect(screen.getByTestId('child-b')).toBeInTheDocument();
35
+ expect(screen.getByText('Alpha')).toBeInTheDocument();
36
+ expect(screen.getByText('Beta')).toBeInTheDocument();
37
+ });
38
+
39
+ it('renders the GitBranch icon', () => {
40
+ const { container } = render(
41
+ <ParallelGroup count={2}>
42
+ <div>child</div>
43
+ </ParallelGroup>,
44
+ );
45
+ const icon = container.querySelector('[data-lucide="GitBranch"]');
46
+ expect(icon).toBeInTheDocument();
47
+ });
48
+
49
+ it('applies custom className', () => {
50
+ const { container } = render(
51
+ <ParallelGroup count={1} className="my-custom-class">
52
+ <div>child</div>
53
+ </ParallelGroup>,
54
+ );
55
+ const wrapper = container.firstChild as HTMLElement;
56
+ expect(wrapper.className).toContain('my-custom-class');
57
+ });
58
+
59
+ it('always shows "parallel" label in uppercase styling container', () => {
60
+ render(
61
+ <ParallelGroup count={4}>
62
+ <div>child</div>
63
+ </ParallelGroup>,
64
+ );
65
+ const label = screen.getByText('parallel');
66
+ expect(label).toBeInTheDocument();
67
+ });
68
+
69
+ it('renders with count of 1', () => {
70
+ render(
71
+ <ParallelGroup count={1}>
72
+ <div>single child</div>
73
+ </ParallelGroup>,
74
+ );
75
+ expect(screen.getByText(/1 tasks/)).toBeInTheDocument();
76
+ expect(screen.getByText('single child')).toBeInTheDocument();
77
+ });
78
+
79
+ it('renders dashed border container with expected structure', () => {
80
+ const { container } = render(
81
+ <ParallelGroup count={3}>
82
+ <div>child</div>
83
+ </ParallelGroup>,
84
+ );
85
+ const outer = container.firstChild as HTMLElement;
86
+ expect(outer.className).toContain('border-dashed');
87
+ });
88
+ });