@shepai/cli 1.53.1 → 1.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/apis/json-schema/NotificationEvent.yaml +4 -0
  2. package/dist/packages/core/src/domain/generated/output.d.ts +4 -0
  3. package/dist/packages/core/src/domain/generated/output.d.ts.map +1 -1
  4. package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.d.ts +4 -0
  5. package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.d.ts.map +1 -1
  6. package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.js +19 -6
  7. package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.d.ts +5 -3
  8. package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.d.ts.map +1 -1
  9. package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.js +21 -13
  10. package/dist/src/presentation/cli/commands/ui.command.d.ts.map +1 -1
  11. package/dist/src/presentation/cli/commands/ui.command.js +2 -1
  12. package/dist/src/presentation/web/app/api/agent-events/health/route.d.ts +13 -0
  13. package/dist/src/presentation/web/app/api/agent-events/health/route.d.ts.map +1 -0
  14. package/dist/src/presentation/web/app/api/agent-events/health/route.js +60 -0
  15. package/dist/src/presentation/web/app/api/agent-events/route.d.ts +8 -4
  16. package/dist/src/presentation/web/app/api/agent-events/route.d.ts.map +1 -1
  17. package/dist/src/presentation/web/app/api/agent-events/route.js +227 -55
  18. package/dist/src/presentation/web/app/layout.d.ts.map +1 -1
  19. package/dist/src/presentation/web/app/layout.js +14 -0
  20. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts +5 -1
  21. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts.map +1 -1
  22. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.js +29 -1
  23. package/dist/src/presentation/web/components/common/repository-node/repository-node.d.ts.map +1 -1
  24. package/dist/src/presentation/web/components/common/repository-node/repository-node.js +1 -1
  25. package/dist/src/presentation/web/components/common/sound-toggle/index.d.ts +2 -0
  26. package/dist/src/presentation/web/components/common/sound-toggle/index.d.ts.map +1 -0
  27. package/dist/src/presentation/web/components/common/sound-toggle/index.js +1 -0
  28. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.d.ts +2 -0
  29. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.d.ts.map +1 -0
  30. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.js +9 -0
  31. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.d.ts +8 -0
  32. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.d.ts.map +1 -0
  33. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.js +17 -0
  34. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  35. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +14 -2
  36. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +1 -0
  37. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  38. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +151 -19
  39. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +1 -1
  40. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.d.ts +1 -0
  41. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.d.ts.map +1 -1
  42. package/dist/src/presentation/web/components/features/features-canvas/features-canvas.stories.js +31 -1
  43. package/dist/src/presentation/web/components/layouts/app-shell/app-shell.d.ts.map +1 -1
  44. package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +2 -1
  45. package/dist/src/presentation/web/dev-server.js +14 -0
  46. package/dist/src/presentation/web/hooks/use-agent-events.d.ts +7 -0
  47. package/dist/src/presentation/web/hooks/use-agent-events.d.ts.map +1 -1
  48. package/dist/src/presentation/web/hooks/use-agent-events.js +135 -47
  49. package/dist/src/presentation/web/hooks/use-notifications.d.ts.map +1 -1
  50. package/dist/src/presentation/web/hooks/use-notifications.js +56 -16
  51. package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts.map +1 -1
  52. package/dist/src/presentation/web/hooks/use-notifications.stories.js +28 -9
  53. package/dist/src/presentation/web/hooks/use-sound-enabled.d.ts +6 -0
  54. package/dist/src/presentation/web/hooks/use-sound-enabled.d.ts.map +1 -0
  55. package/dist/src/presentation/web/hooks/use-sound-enabled.js +27 -0
  56. package/dist/src/presentation/web/hooks/use-sound.d.ts +15 -0
  57. package/dist/src/presentation/web/hooks/use-sound.d.ts.map +1 -0
  58. package/dist/src/presentation/web/hooks/use-sound.js +83 -0
  59. package/dist/src/presentation/web/hooks/use-sound.stories.d.ts +8 -0
  60. package/dist/src/presentation/web/hooks/use-sound.stories.d.ts.map +1 -0
  61. package/dist/src/presentation/web/hooks/use-sound.stories.js +67 -0
  62. package/dist/src/presentation/web/public/agent-events-sw.d.ts +35 -0
  63. package/dist/src/presentation/web/public/agent-events-sw.d.ts.map +1 -0
  64. package/dist/src/presentation/web/public/agent-events-sw.js +140 -0
  65. package/dist/tsconfig.build.tsbuildinfo +1 -1
  66. package/package.json +1 -1
  67. package/web/.next/BUILD_ID +1 -1
  68. package/web/.next/app-path-routes-manifest.json +1 -0
  69. package/web/.next/build-manifest.json +2 -2
  70. package/web/.next/cache/.previewinfo +1 -1
  71. package/web/.next/cache/.rscinfo +1 -1
  72. package/web/.next/cache/.tsbuildinfo +1 -1
  73. package/web/.next/cache/config.json +3 -3
  74. package/web/.next/fallback-build-manifest.json +2 -2
  75. package/web/.next/prerender-manifest.json +3 -3
  76. package/web/.next/required-server-files.js +1 -1
  77. package/web/.next/required-server-files.json +1 -1
  78. package/web/.next/routes-manifest.json +6 -0
  79. package/web/.next/server/app/_global-error.html +2 -2
  80. package/web/.next/server/app/_global-error.rsc +1 -1
  81. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  82. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  83. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  84. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  85. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  86. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  87. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  88. package/web/.next/server/app/api/agent-events/health/route/app-paths-manifest.json +3 -0
  89. package/web/.next/server/app/api/agent-events/health/route/build-manifest.json +11 -0
  90. package/web/.next/server/app/api/agent-events/health/route/server-reference-manifest.json +4 -0
  91. package/web/.next/server/app/api/agent-events/health/route.js +6 -0
  92. package/web/.next/server/app/api/agent-events/health/route.js.map +5 -0
  93. package/web/.next/server/app/api/agent-events/health/route.js.nft.json +1 -0
  94. package/web/.next/server/app/api/agent-events/health/route_client-reference-manifest.js +2 -0
  95. package/web/.next/server/app/api/agent-events/route.js +2 -1
  96. package/web/.next/server/app/api/agent-events/route.js.nft.json +1 -1
  97. package/web/.next/server/app/page/server-reference-manifest.json +14 -14
  98. package/web/.next/server/app/page.js.nft.json +1 -1
  99. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  100. package/web/.next/server/app/version/page.js +1 -1
  101. package/web/.next/server/app/version/page.js.nft.json +1 -1
  102. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  103. package/web/.next/server/app-paths-manifest.json +1 -0
  104. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +6 -0
  105. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -0
  106. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_health_route_actions_4d471160.js +3 -0
  107. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_health_route_actions_4d471160.js.map +1 -0
  108. package/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js +3 -0
  109. package/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js.map +1 -0
  110. package/web/.next/server/chunks/[root-of-the-server]__d9d410a8._.js +3 -0
  111. package/web/.next/server/chunks/[root-of-the-server]__d9d410a8._.js.map +1 -0
  112. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
  113. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js.map +1 -1
  114. package/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +2 -2
  115. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js → server/chunks/ssr/[root-of-the-server]__6e8b5181._.js} +2 -2
  116. package/web/.next/server/chunks/ssr/{_1fafa9db._.js → _1632a21f._.js} +2 -2
  117. package/web/.next/server/chunks/ssr/_1632a21f._.js.map +1 -0
  118. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +1 -1
  119. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js.map +1 -1
  120. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  121. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  122. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
  123. package/web/.next/server/pages/500.html +2 -2
  124. package/web/.next/server/server-reference-manifest.js +1 -1
  125. package/web/.next/server/server-reference-manifest.json +15 -15
  126. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  127. package/web/.next/standalone/src/presentation/web/.next/app-path-routes-manifest.json +1 -0
  128. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  129. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  130. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  131. package/web/.next/standalone/src/presentation/web/.next/routes-manifest.json +6 -0
  132. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  133. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  134. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  135. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  136. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  137. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  138. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  139. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  140. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  141. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route/app-paths-manifest.json +3 -0
  142. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route/build-manifest.json +11 -0
  143. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route/server-reference-manifest.json +4 -0
  144. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route.js +6 -0
  145. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route.js.map +5 -0
  146. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route.js.nft.json +1 -0
  147. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route_client-reference-manifest.js +2 -0
  148. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/route.js +2 -1
  149. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/route.js.nft.json +1 -1
  150. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +14 -14
  151. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js.nft.json +1 -1
  152. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  153. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js +1 -1
  154. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  155. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  156. package/web/.next/standalone/src/presentation/web/.next/server/app-paths-manifest.json +1 -0
  157. package/web/.next/standalone/src/presentation/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +6 -0
  158. package/web/.next/standalone/src/presentation/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_health_route_actions_4d471160.js +3 -0
  159. package/web/.next/standalone/src/presentation/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js +3 -0
  160. package/web/.next/standalone/src/presentation/web/.next/server/chunks/[root-of-the-server]__d9d410a8._.js +3 -0
  161. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
  162. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +2 -2
  163. package/web/.next/{server/chunks/ssr/[root-of-the-server]__249c74f6._.js → standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6e8b5181._.js} +2 -2
  164. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{_1fafa9db._.js → _1632a21f._.js} +2 -2
  165. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +1 -1
  166. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  167. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  168. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  169. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +15 -15
  170. package/web/.next/standalone/src/presentation/web/app/api/agent-events/health/route.ts +69 -0
  171. package/web/.next/standalone/src/presentation/web/app/api/agent-events/route.ts +258 -54
  172. package/web/.next/standalone/src/presentation/web/app/layout.tsx +14 -0
  173. package/web/.next/standalone/src/presentation/web/components/common/feature-node/derive-feature-state.ts +39 -2
  174. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-node.tsx +1 -0
  175. package/web/.next/standalone/src/presentation/web/components/common/sound-toggle/index.ts +1 -0
  176. package/web/.next/standalone/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.tsx +27 -0
  177. package/web/.next/standalone/src/presentation/web/components/common/sound-toggle/sound-toggle.tsx +21 -0
  178. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +15 -1
  179. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +194 -31
  180. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.stories.tsx +41 -0
  181. package/web/.next/standalone/src/presentation/web/components/features/features-canvas/features-canvas.tsx +1 -1
  182. package/web/.next/standalone/src/presentation/web/components/layouts/app-shell/app-shell.tsx +3 -1
  183. package/web/.next/standalone/src/presentation/web/dev-server.ts +21 -0
  184. package/web/.next/standalone/src/presentation/web/hooks/use-agent-events.ts +161 -52
  185. package/web/.next/standalone/src/presentation/web/hooks/use-notifications.stories.tsx +39 -10
  186. package/web/.next/standalone/src/presentation/web/hooks/use-notifications.ts +68 -15
  187. package/web/.next/standalone/src/presentation/web/hooks/use-sound-enabled.ts +38 -0
  188. package/web/.next/standalone/src/presentation/web/hooks/use-sound.stories.tsx +109 -0
  189. package/web/.next/standalone/src/presentation/web/hooks/use-sound.ts +101 -0
  190. package/web/.next/standalone/src/presentation/web/public/agent-events-sw.js +164 -0
  191. package/web/.next/standalone/src/presentation/web/public/sounds/button.wav +0 -0
  192. package/web/.next/standalone/src/presentation/web/public/sounds/caution.wav +0 -0
  193. package/web/.next/standalone/src/presentation/web/public/sounds/celebration.wav +0 -0
  194. package/web/.next/standalone/src/presentation/web/public/sounds/disabled.wav +0 -0
  195. package/web/.next/standalone/src/presentation/web/public/sounds/notification.wav +0 -0
  196. package/web/.next/standalone/src/presentation/web/public/sounds/progress_loop.wav +0 -0
  197. package/web/.next/standalone/src/presentation/web/public/sounds/ringtone_loop.wav +0 -0
  198. package/web/.next/standalone/src/presentation/web/public/sounds/select.wav +0 -0
  199. package/web/.next/standalone/src/presentation/web/public/sounds/swipe.wav +0 -0
  200. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_01.wav +0 -0
  201. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_02.wav +0 -0
  202. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_03.wav +0 -0
  203. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_04.wav +0 -0
  204. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_05.wav +0 -0
  205. package/web/.next/standalone/src/presentation/web/public/sounds/tap_01.wav +0 -0
  206. package/web/.next/standalone/src/presentation/web/public/sounds/tap_02.wav +0 -0
  207. package/web/.next/standalone/src/presentation/web/public/sounds/tap_03.wav +0 -0
  208. package/web/.next/standalone/src/presentation/web/public/sounds/tap_04.wav +0 -0
  209. package/web/.next/standalone/src/presentation/web/public/sounds/tap_05.wav +0 -0
  210. package/web/.next/standalone/src/presentation/web/public/sounds/toggle_off.wav +0 -0
  211. package/web/.next/standalone/src/presentation/web/public/sounds/toggle_on.wav +0 -0
  212. package/web/.next/standalone/src/presentation/web/public/sounds/transition_down.wav +0 -0
  213. package/web/.next/standalone/src/presentation/web/public/sounds/transition_up.wav +0 -0
  214. package/web/.next/standalone/src/presentation/web/public/sounds/type_01.wav +0 -0
  215. package/web/.next/standalone/src/presentation/web/public/sounds/type_02.wav +0 -0
  216. package/web/.next/standalone/src/presentation/web/public/sounds/type_03.wav +0 -0
  217. package/web/.next/standalone/src/presentation/web/public/sounds/type_04.wav +0 -0
  218. package/web/.next/standalone/src/presentation/web/public/sounds/type_05.wav +0 -0
  219. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  220. package/web/.next/static/chunks/1f13bc20e938335c.css +2 -0
  221. package/web/.next/static/chunks/702384d364482e4b.js +10 -0
  222. package/web/.next/static/chunks/{68fb91b6eca14536.js → 8f4461aee9a05741.js} +2 -2
  223. package/web/.next/static/chunks/9a82d8c76f175235.js +1 -0
  224. package/web/.next/trace +1 -1
  225. package/web/.next/trace-build +1 -1
  226. package/web/.next/types/link.d.ts +1 -0
  227. package/web/.next/types/routes.d.ts +2 -1
  228. package/web/.next/types/validator.ts +9 -0
  229. package/web/public/agent-events-sw.js +164 -0
  230. package/web/public/sounds/button.wav +0 -0
  231. package/web/public/sounds/caution.wav +0 -0
  232. package/web/public/sounds/celebration.wav +0 -0
  233. package/web/public/sounds/disabled.wav +0 -0
  234. package/web/public/sounds/notification.wav +0 -0
  235. package/web/public/sounds/progress_loop.wav +0 -0
  236. package/web/public/sounds/ringtone_loop.wav +0 -0
  237. package/web/public/sounds/select.wav +0 -0
  238. package/web/public/sounds/swipe.wav +0 -0
  239. package/web/public/sounds/swipe_01.wav +0 -0
  240. package/web/public/sounds/swipe_02.wav +0 -0
  241. package/web/public/sounds/swipe_03.wav +0 -0
  242. package/web/public/sounds/swipe_04.wav +0 -0
  243. package/web/public/sounds/swipe_05.wav +0 -0
  244. package/web/public/sounds/tap_01.wav +0 -0
  245. package/web/public/sounds/tap_02.wav +0 -0
  246. package/web/public/sounds/tap_03.wav +0 -0
  247. package/web/public/sounds/tap_04.wav +0 -0
  248. package/web/public/sounds/tap_05.wav +0 -0
  249. package/web/public/sounds/toggle_off.wav +0 -0
  250. package/web/public/sounds/toggle_on.wav +0 -0
  251. package/web/public/sounds/transition_down.wav +0 -0
  252. package/web/public/sounds/transition_up.wav +0 -0
  253. package/web/public/sounds/type_01.wav +0 -0
  254. package/web/public/sounds/type_02.wav +0 -0
  255. package/web/public/sounds/type_03.wav +0 -0
  256. package/web/public/sounds/type_04.wav +0 -0
  257. package/web/public/sounds/type_05.wav +0 -0
  258. package/web/.next/server/chunks/[root-of-the-server]__7d5c8b74._.js +0 -6
  259. package/web/.next/server/chunks/[root-of-the-server]__7d5c8b74._.js.map +0 -1
  260. package/web/.next/server/chunks/ssr/_1fafa9db._.js.map +0 -1
  261. package/web/.next/standalone/src/presentation/web/.next/server/chunks/[root-of-the-server]__7d5c8b74._.js +0 -6
  262. package/web/.next/static/chunks/212e3c682d3b7f50.js +0 -10
  263. package/web/.next/static/chunks/22f30ce91a3c79c1.js +0 -1
  264. package/web/.next/static/chunks/476fe030d242c153.css +0 -2
  265. /package/web/.next/server/chunks/ssr/{[root-of-the-server]__249c74f6._.js.map → [root-of-the-server]__6e8b5181._.js.map} +0 -0
  266. /package/web/.next/static/{EWkET8BUeCcZJxcYQpxDY → RwzxYU9D2fPc_4j5RYu0v}/_buildManifest.js +0 -0
  267. /package/web/.next/static/{EWkET8BUeCcZJxcYQpxDY → RwzxYU9D2fPc_4j5RYu0v}/_clientMiddlewareManifest.json +0 -0
  268. /package/web/.next/static/{EWkET8BUeCcZJxcYQpxDY → RwzxYU9D2fPc_4j5RYu0v}/_ssgManifest.js +0 -0
@@ -1,8 +1,9 @@
1
1
  'use client';
2
- import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { toast } from 'sonner';
4
- import { NotificationSeverity } from '../../../../packages/core/src/domain/generated/output.js';
4
+ import { NotificationEventType, NotificationSeverity } from '../../../../packages/core/src/domain/generated/output.js';
5
5
  import { useAgentEventsContext } from './agent-events-provider.js';
6
+ import { useSound } from './use-sound.js';
6
7
  const SEVERITY_TO_TOAST = {
7
8
  [NotificationSeverity.Success]: 'success',
8
9
  [NotificationSeverity.Error]: 'error',
@@ -11,34 +12,73 @@ const SEVERITY_TO_TOAST = {
11
12
  };
12
13
  function dispatchToast(event) {
13
14
  const method = SEVERITY_TO_TOAST[event.severity] ?? 'info';
14
- toast[method](event.featureName, { description: event.message });
15
+ const isActionable = event.eventType === NotificationEventType.WaitingApproval;
16
+ toast[method](event.featureName, {
17
+ description: event.message,
18
+ ...(isActionable && {
19
+ action: {
20
+ label: 'Review',
21
+ onClick: () => {
22
+ window.dispatchEvent(new CustomEvent('shep:select-feature', { detail: { featureId: event.featureId } }));
23
+ },
24
+ },
25
+ }),
26
+ });
15
27
  }
16
28
  function dispatchBrowserNotification(event) {
17
29
  if (globalThis.Notification?.permission !== 'granted') {
18
30
  return;
19
31
  }
20
- new Notification(event.featureName, { body: event.message });
32
+ new Notification(`Shep: ${event.featureName}`, {
33
+ body: event.message,
34
+ icon: '/favicon-light.svg',
35
+ });
21
36
  }
37
+ const SEVERITY_TO_SOUND = {
38
+ [NotificationSeverity.Success]: 'celebration',
39
+ [NotificationSeverity.Error]: 'caution',
40
+ [NotificationSeverity.Warning]: 'notification',
41
+ [NotificationSeverity.Info]: 'button',
42
+ };
22
43
  export function useNotifications() {
23
- const { lastEvent } = useAgentEventsContext();
44
+ const { events } = useAgentEventsContext();
45
+ const successSound = useSound('celebration', { volume: 0.5 });
46
+ const errorSound = useSound('caution', { volume: 0.5 });
47
+ const warningSound = useSound('notification', { volume: 0.5 });
48
+ const infoSound = useSound('button', { volume: 0.5 });
49
+ const soundsByName = useMemo(() => ({
50
+ celebration: successSound,
51
+ caution: errorSound,
52
+ notification: warningSound,
53
+ button: infoSound,
54
+ }), [successSound, errorSound, warningSound, infoSound]);
24
55
  const [browserPermissionState, setBrowserPermissionState] = useState(() => {
25
56
  if (typeof globalThis.Notification === 'undefined')
26
57
  return 'default';
27
58
  return globalThis.Notification.permission;
28
59
  });
29
- // Track which events we've already dispatched to avoid re-dispatching on re-render
30
- const processedRef = useRef(new Set());
60
+ // Track how many events from the array we've already processed.
61
+ // Using the array index (instead of lastEvent) prevents React batching
62
+ // from silently dropping events when multiple SSE messages arrive together.
63
+ const processedCountRef = useRef(0);
31
64
  useEffect(() => {
32
- if (!lastEvent)
33
- return;
34
- // Create a key for deduplication
35
- const key = `${lastEvent.agentRunId}-${lastEvent.eventType}-${lastEvent.timestamp}`;
36
- if (processedRef.current.has(key))
65
+ if (events.length <= processedCountRef.current)
37
66
  return;
38
- processedRef.current.add(key);
39
- dispatchToast(lastEvent);
40
- dispatchBrowserNotification(lastEvent);
41
- }, [lastEvent]);
67
+ const newEvents = events.slice(processedCountRef.current);
68
+ processedCountRef.current = events.length;
69
+ for (const event of newEvents) {
70
+ // Only notify for actionable events and completion celebrations
71
+ if (event.severity !== NotificationSeverity.Error &&
72
+ event.severity !== NotificationSeverity.Warning &&
73
+ event.severity !== NotificationSeverity.Success) {
74
+ continue;
75
+ }
76
+ dispatchToast(event);
77
+ dispatchBrowserNotification(event);
78
+ const soundName = SEVERITY_TO_SOUND[event.severity];
79
+ soundsByName[soundName]?.play();
80
+ }
81
+ }, [events, soundsByName]);
42
82
  const requestBrowserPermission = useCallback(async () => {
43
83
  if (typeof globalThis.Notification === 'undefined')
44
84
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"use-notifications.stories.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-notifications.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAwIvD,QAAA,MAAM,IAAI,EAAE,IAcX,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC;AAEtB,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC"}
1
+ {"version":3,"file":"use-notifications.stories.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-notifications.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAqKvD,QAAA,MAAM,IAAI,EAAE,IAcX,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC;AAEtB,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC"}
@@ -1,16 +1,35 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useCallback, useMemo, useState } from 'react';
3
3
  import { toast } from 'sonner';
4
4
  import { Toaster } from '../components/ui/sonner.js';
5
5
  import { Button } from '../components/ui/button.js';
6
- function simulateNotification(event) {
7
- const method = event.severity ?? 'info';
8
- toast[method](event.featureName, { description: event.message });
9
- if (globalThis.Notification?.permission === 'granted') {
10
- new Notification(event.featureName, { body: event.message });
11
- }
12
- }
6
+ import { useSound } from './use-sound.js';
7
+ const SEVERITY_SOUNDS = {
8
+ success: 'celebration',
9
+ error: 'caution',
10
+ warning: 'notification',
11
+ info: 'button',
12
+ };
13
13
  function NotificationDemo() {
14
+ const successSound = useSound('celebration', { volume: 0.5 });
15
+ const errorSound = useSound('caution', { volume: 0.5 });
16
+ const warningSound = useSound('notification', { volume: 0.5 });
17
+ const infoSound = useSound('button', { volume: 0.5 });
18
+ const soundsByName = useMemo(() => ({
19
+ celebration: successSound,
20
+ caution: errorSound,
21
+ notification: warningSound,
22
+ button: infoSound,
23
+ }), [successSound, errorSound, warningSound, infoSound]);
24
+ const simulateNotification = useCallback((event) => {
25
+ const method = event.severity ?? 'info';
26
+ toast[method](event.featureName, { description: event.message });
27
+ if (globalThis.Notification?.permission === 'granted') {
28
+ new Notification(event.featureName, { body: event.message });
29
+ }
30
+ const soundName = SEVERITY_SOUNDS[event.severity];
31
+ soundsByName[soundName]?.play();
32
+ }, [soundsByName]);
14
33
  const [browserPermission, setBrowserPermission] = useState(typeof globalThis.Notification !== 'undefined' ? globalThis.Notification.permission : 'default');
15
34
  const requestPermission = async () => {
16
35
  if (typeof globalThis.Notification === 'undefined')
@@ -18,7 +37,7 @@ function NotificationDemo() {
18
37
  const result = await globalThis.Notification.requestPermission();
19
38
  setBrowserPermission(result);
20
39
  };
21
- return (_jsxs("div", { className: "flex w-[420px] flex-col gap-6", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Browser Notification Permission" }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "text-muted-foreground text-sm", children: ["Status: ", _jsx("code", { className: "bg-muted rounded px-1.5 py-0.5", children: browserPermission })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: requestPermission, children: "Request Permission" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Simulate Notification Events" }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Click a button to fire a toast and browser notification." }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => simulateNotification({
40
+ return (_jsxs("div", { className: "flex w-[420px] flex-col gap-6", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Browser Notification Permission" }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "text-muted-foreground text-sm", children: ["Status: ", _jsx("code", { className: "bg-muted rounded px-1.5 py-0.5", children: browserPermission })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: requestPermission, children: "Request Permission" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Simulate Notification Events" }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Click a button to fire a toast, browser notification, and sound." }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => simulateNotification({
22
41
  eventType: 'agent_started',
23
42
  severity: 'info',
24
43
  featureName: 'Auth Module',
@@ -0,0 +1,6 @@
1
+ export interface UseSoundEnabledResult {
2
+ enabled: boolean;
3
+ toggle: () => void;
4
+ }
5
+ export declare function useSoundEnabled(): UseSoundEnabledResult;
6
+ //# sourceMappingURL=use-sound-enabled.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-sound-enabled.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-sound-enabled.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,wBAAgB,eAAe,IAAI,qBAAqB,CAyBvD"}
@@ -0,0 +1,27 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ const STORAGE_KEY = 'shep-sound-enabled';
4
+ const SYNC_EVENT = 'shep:sound-toggle';
5
+ export function useSoundEnabled() {
6
+ const [enabled, setEnabled] = useState(true);
7
+ useEffect(() => {
8
+ const stored = localStorage.getItem(STORAGE_KEY);
9
+ if (stored === 'false')
10
+ setEnabled(false);
11
+ // Sync across all useSoundEnabled instances in the same tab
12
+ const onSync = (e) => {
13
+ setEnabled(e.detail);
14
+ };
15
+ window.addEventListener(SYNC_EVENT, onSync);
16
+ return () => window.removeEventListener(SYNC_EVENT, onSync);
17
+ }, []);
18
+ const toggle = useCallback(() => {
19
+ setEnabled((prev) => {
20
+ const next = !prev;
21
+ localStorage.setItem(STORAGE_KEY, String(next));
22
+ window.dispatchEvent(new CustomEvent(SYNC_EVENT, { detail: next }));
23
+ return next;
24
+ });
25
+ }, []);
26
+ return { enabled, toggle };
27
+ }
@@ -0,0 +1,15 @@
1
+ declare const SOUND_NAMES: readonly ["button", "caution", "celebration", "disabled", "notification", "progress_loop", "ringtone_loop", "select", "swipe", "swipe_01", "swipe_02", "swipe_03", "swipe_04", "swipe_05", "tap_01", "tap_02", "tap_03", "tap_04", "tap_05", "toggle_off", "toggle_on", "transition_down", "transition_up", "type_01", "type_02", "type_03", "type_04", "type_05"];
2
+ export type SoundName = (typeof SOUND_NAMES)[number];
3
+ export interface UseSoundOptions {
4
+ volume?: number;
5
+ loop?: boolean;
6
+ }
7
+ export interface UseSoundResult {
8
+ play: () => void;
9
+ stop: () => void;
10
+ isPlaying: boolean;
11
+ }
12
+ export declare function useSound(name: SoundName, options?: UseSoundOptions): UseSoundResult;
13
+ /** All available sound names for enumeration (e.g. in Storybook). */
14
+ export { SOUND_NAMES };
15
+ //# sourceMappingURL=use-sound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-sound.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-sound.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,WAAW,oWA6BP,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,MAAM,WAAW,eAAe;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,eAAoB,GAAG,cAAc,CAgDvF;AAED,qEAAqE;AACrE,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,83 @@
1
+ 'use client';
2
+ import { useCallback, useEffect, useRef } from 'react';
3
+ import { useSoundEnabled } from './use-sound-enabled.js';
4
+ const SOUND_NAMES = [
5
+ 'button',
6
+ 'caution',
7
+ 'celebration',
8
+ 'disabled',
9
+ 'notification',
10
+ 'progress_loop',
11
+ 'ringtone_loop',
12
+ 'select',
13
+ 'swipe',
14
+ 'swipe_01',
15
+ 'swipe_02',
16
+ 'swipe_03',
17
+ 'swipe_04',
18
+ 'swipe_05',
19
+ 'tap_01',
20
+ 'tap_02',
21
+ 'tap_03',
22
+ 'tap_04',
23
+ 'tap_05',
24
+ 'toggle_off',
25
+ 'toggle_on',
26
+ 'transition_down',
27
+ 'transition_up',
28
+ 'type_01',
29
+ 'type_02',
30
+ 'type_03',
31
+ 'type_04',
32
+ 'type_05',
33
+ ];
34
+ export function useSound(name, options = {}) {
35
+ const { volume = 1, loop = false } = options;
36
+ const { enabled } = useSoundEnabled();
37
+ const audioRef = useRef(null);
38
+ const isPlayingRef = useRef(false);
39
+ useEffect(() => {
40
+ if (typeof window === 'undefined')
41
+ return;
42
+ const audio = new Audio(`/sounds/${name}.wav`);
43
+ audio.preload = 'auto';
44
+ audio.loop = loop;
45
+ audio.volume = Math.max(0, Math.min(1, volume));
46
+ audio.addEventListener('ended', () => {
47
+ isPlayingRef.current = false;
48
+ });
49
+ audioRef.current = audio;
50
+ return () => {
51
+ audio.pause();
52
+ audio.removeAttribute('src');
53
+ audioRef.current = null;
54
+ isPlayingRef.current = false;
55
+ };
56
+ }, [name, loop, volume]);
57
+ const play = useCallback(() => {
58
+ if (!enabled)
59
+ return;
60
+ const audio = audioRef.current;
61
+ if (!audio)
62
+ return;
63
+ audio.currentTime = 0;
64
+ audio.play()?.catch((err) => {
65
+ if (process.env.NODE_ENV === 'development') {
66
+ // eslint-disable-next-line no-console
67
+ console.warn(`[useSound] play("${name}") blocked:`, err.message);
68
+ }
69
+ });
70
+ isPlayingRef.current = true;
71
+ }, [enabled, name]);
72
+ const stop = useCallback(() => {
73
+ const audio = audioRef.current;
74
+ if (!audio)
75
+ return;
76
+ audio.pause();
77
+ audio.currentTime = 0;
78
+ isPlayingRef.current = false;
79
+ }, []);
80
+ return { play, stop, isPlaying: isPlayingRef.current };
81
+ }
82
+ /** All available sound names for enumeration (e.g. in Storybook). */
83
+ export { SOUND_NAMES };
@@ -0,0 +1,8 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ declare const meta: Meta;
3
+ export default meta;
4
+ type Story = StoryObj<{
5
+ volume: number;
6
+ }>;
7
+ export declare const Catalog: Story;
8
+ //# sourceMappingURL=use-sound.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-sound.stories.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-sound.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAsFvD,QAAA,MAAM,IAAI,EAAE,IAcX,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE1C,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Button } from '../components/ui/button.js';
4
+ import { useSound, SOUND_NAMES } from './use-sound.js';
5
+ /* ------------------------------------------------------------------ */
6
+ /* Helpers */
7
+ /* ------------------------------------------------------------------ */
8
+ const CATEGORIES = {
9
+ 'UI Actions': ['button', 'select', 'disabled'],
10
+ Toggles: ['toggle_on', 'toggle_off'],
11
+ Transitions: ['transition_up', 'transition_down'],
12
+ Alerts: ['notification', 'caution', 'celebration'],
13
+ Swipes: ['swipe', 'swipe_01', 'swipe_02', 'swipe_03', 'swipe_04', 'swipe_05'],
14
+ Taps: ['tap_01', 'tap_02', 'tap_03', 'tap_04', 'tap_05'],
15
+ Typing: ['type_01', 'type_02', 'type_03', 'type_04', 'type_05'],
16
+ Loops: ['progress_loop', 'ringtone_loop'],
17
+ };
18
+ /* ------------------------------------------------------------------ */
19
+ /* Single sound player (used per button) */
20
+ /* ------------------------------------------------------------------ */
21
+ function SoundButton({ name, volume }) {
22
+ const isLoop = name.includes('loop');
23
+ const { play, stop } = useSound(name, { volume, loop: isLoop });
24
+ const [playing, setPlaying] = useState(false);
25
+ const handleClick = () => {
26
+ if (isLoop && playing) {
27
+ stop();
28
+ setPlaying(false);
29
+ }
30
+ else {
31
+ play();
32
+ setPlaying(true);
33
+ if (!isLoop) {
34
+ setTimeout(() => setPlaying(false), 2000);
35
+ }
36
+ }
37
+ };
38
+ return (_jsx(Button, { size: "sm", variant: playing ? 'default' : 'outline', onClick: handleClick, className: "min-w-[120px] font-mono text-xs", children: isLoop && playing ? `Stop ${name}` : name }));
39
+ }
40
+ /* ------------------------------------------------------------------ */
41
+ /* Full catalog demo */
42
+ /* ------------------------------------------------------------------ */
43
+ function SoundCatalog({ volume }) {
44
+ return (_jsxs("div", { className: "flex w-[560px] flex-col gap-6", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("h2", { className: "text-base font-semibold", children: "Sound Library" }), _jsxs("p", { className: "text-muted-foreground text-sm", children: [SOUND_NAMES.length, " sounds available at", ' ', _jsx("code", { className: "bg-muted rounded px-1 py-0.5 text-xs", children: "/sounds/*.wav" })] })] }), Object.entries(CATEGORIES).map(([category, sounds]) => (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "text-sm font-semibold", children: category }), _jsx("div", { className: "flex flex-wrap gap-2", children: sounds.map((name) => (_jsx(SoundButton, { name: name, volume: volume }, name))) })] }, category)))] }));
45
+ }
46
+ /* ------------------------------------------------------------------ */
47
+ /* Storybook meta */
48
+ /* ------------------------------------------------------------------ */
49
+ const meta = {
50
+ title: 'Hooks/useSound',
51
+ parameters: { layout: 'centered' },
52
+ tags: ['autodocs'],
53
+ argTypes: {
54
+ volume: {
55
+ control: { type: 'range', min: 0, max: 1, step: 0.1 },
56
+ description: 'Playback volume (0 to 1)',
57
+ defaultValue: 0.5,
58
+ },
59
+ },
60
+ args: {
61
+ volume: 0.5,
62
+ },
63
+ };
64
+ export default meta;
65
+ export const Catalog = {
66
+ render: ({ volume }) => _jsx(SoundCatalog, { volume: volume }),
67
+ };
@@ -0,0 +1,35 @@
1
+ declare function connect(): void;
2
+ declare function disconnect(): void;
3
+ declare function cleanup(): void;
4
+ declare function setStatus(status: any): void;
5
+ declare function broadcast(message: any): Promise<void>;
6
+ /**
7
+ * Service Worker: agent-events-sw.js
8
+ *
9
+ * Maintains a single SSE connection to /api/agent-events and broadcasts
10
+ * NotificationEvent deltas to all connected tabs via postMessage.
11
+ *
12
+ * Messages FROM clients:
13
+ * { type: 'subscribe', runId?: string } — register this tab
14
+ * { type: 'unsubscribe' } — unregister this tab
15
+ *
16
+ * Messages TO clients:
17
+ * { type: 'notification', data: NotificationEvent }
18
+ * { type: 'status', status: 'connected' | 'connecting' | 'disconnected' }
19
+ */
20
+ declare const BASE_BACKOFF_MS: 1000;
21
+ declare const MAX_BACKOFF_MS: 30000;
22
+ declare const STABLE_CONNECTION_MS: 5000;
23
+ /** @type {EventSource | null} */
24
+ declare let eventSource: EventSource | null;
25
+ /** @type {string | undefined} */
26
+ declare let currentRunId: string | undefined;
27
+ declare let backoff: number;
28
+ /** @type {ReturnType<typeof setTimeout> | null} */
29
+ declare let reconnectTimer: ReturnType<typeof setTimeout> | null;
30
+ /** @type {ReturnType<typeof setTimeout> | null} */
31
+ declare let stableTimer: ReturnType<typeof setTimeout> | null;
32
+ /** @type {'connected' | 'connecting' | 'disconnected'} */
33
+ declare let connectionStatus: "connected" | "connecting" | "disconnected";
34
+ declare let subscriberCount: number;
35
+ //# sourceMappingURL=agent-events-sw.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-events-sw.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/public/agent-events-sw.js"],"names":[],"mappings":"AAmFA,iCA6CC;AAED,oCAQC;AAED,iCASC;AAID,8CAGC;AAED,wDAKC;AAnKD;;;;;;;;;;;;;GAaG;AAIH,+BAAwB,IAAI,CAAC;AAC7B,8BAAuB,KAAM,CAAC;AAC9B,oCAA6B,IAAK,CAAC;AAEnC,iCAAiC;AACjC,yBADW,WAAW,GAAG,IAAI,CACN;AACvB,iCAAiC;AACjC,0BADW,MAAM,GAAG,SAAS,CACZ;AACjB,4BAA8B;AAC9B,mDAAmD;AACnD,4BADW,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,IAAI,CACrB;AAC1B,mDAAmD;AACnD,yBADW,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,IAAI,CACxB;AACvB,0DAA0D;AAC1D,8BADW,WAAW,GAAG,YAAY,GAAG,cAAc,CAChB;AACtC,oCAAwB"}
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ /**
3
+ * Service Worker: agent-events-sw.js
4
+ *
5
+ * Maintains a single SSE connection to /api/agent-events and broadcasts
6
+ * NotificationEvent deltas to all connected tabs via postMessage.
7
+ *
8
+ * Messages FROM clients:
9
+ * { type: 'subscribe', runId?: string } — register this tab
10
+ * { type: 'unsubscribe' } — unregister this tab
11
+ *
12
+ * Messages TO clients:
13
+ * { type: 'notification', data: NotificationEvent }
14
+ * { type: 'status', status: 'connected' | 'connecting' | 'disconnected' }
15
+ */
16
+ /* global self, EventSource, setTimeout, clearTimeout */
17
+ const BASE_BACKOFF_MS = 1000;
18
+ const MAX_BACKOFF_MS = 30_000;
19
+ const STABLE_CONNECTION_MS = 5_000;
20
+ /** @type {EventSource | null} */
21
+ let eventSource = null;
22
+ /** @type {string | undefined} */
23
+ let currentRunId;
24
+ let backoff = BASE_BACKOFF_MS;
25
+ /** @type {ReturnType<typeof setTimeout> | null} */
26
+ let reconnectTimer = null;
27
+ /** @type {ReturnType<typeof setTimeout> | null} */
28
+ let stableTimer = null;
29
+ /** @type {'connected' | 'connecting' | 'disconnected'} */
30
+ let connectionStatus = 'disconnected';
31
+ let subscriberCount = 0;
32
+ // --- Lifecycle ---
33
+ self.addEventListener('install', () => {
34
+ self.skipWaiting();
35
+ });
36
+ self.addEventListener('activate', (event) => {
37
+ event.waitUntil(self.clients.claim().then(async () => {
38
+ // After claiming clients, check if there are open tabs that need a connection.
39
+ // This handles the case where the SW updated via skipWaiting() and the old
40
+ // SW's EventSource was closed — we auto-start for existing clients.
41
+ const clients = await self.clients.matchAll({ type: 'window' });
42
+ if (clients.length > 0 && subscriberCount === 0) {
43
+ subscriberCount = clients.length;
44
+ connect();
45
+ }
46
+ }));
47
+ });
48
+ // --- Client messaging ---
49
+ self.addEventListener('message', (event) => {
50
+ const { type, runId } = event.data ?? {};
51
+ if (type === 'subscribe') {
52
+ subscriberCount++;
53
+ currentRunId = runId;
54
+ // Send current status to the new subscriber immediately
55
+ event.source?.postMessage({ type: 'status', status: connectionStatus });
56
+ // Start connection if this is the first subscriber
57
+ if (subscriberCount === 1) {
58
+ connect();
59
+ }
60
+ }
61
+ else if (type === 'unsubscribe') {
62
+ subscriberCount = Math.max(0, subscriberCount - 1);
63
+ // Close connection when no subscribers remain
64
+ if (subscriberCount === 0) {
65
+ disconnect();
66
+ }
67
+ }
68
+ });
69
+ // --- SSE connection management ---
70
+ function connect() {
71
+ if (eventSource)
72
+ return;
73
+ const url = currentRunId
74
+ ? `/api/agent-events?runId=${encodeURIComponent(currentRunId)}`
75
+ : '/api/agent-events';
76
+ eventSource = new EventSource(url);
77
+ setStatus('connecting');
78
+ eventSource.onopen = () => {
79
+ setStatus('connected');
80
+ // Only reset backoff after connection is stable
81
+ stableTimer = setTimeout(() => {
82
+ stableTimer = null;
83
+ backoff = BASE_BACKOFF_MS;
84
+ }, STABLE_CONNECTION_MS);
85
+ };
86
+ eventSource.onerror = () => {
87
+ cleanup();
88
+ setStatus('disconnected');
89
+ // Don't reconnect if no subscribers
90
+ if (subscriberCount <= 0)
91
+ return;
92
+ const delay = backoff;
93
+ backoff = Math.min(delay * 2, MAX_BACKOFF_MS);
94
+ reconnectTimer = setTimeout(() => {
95
+ reconnectTimer = null;
96
+ connect();
97
+ }, delay);
98
+ };
99
+ eventSource.addEventListener('notification', (event) => {
100
+ /** @type {unknown} */
101
+ let data;
102
+ try {
103
+ data = JSON.parse(event.data);
104
+ }
105
+ catch {
106
+ return;
107
+ }
108
+ broadcast({ type: 'notification', data });
109
+ });
110
+ }
111
+ function disconnect() {
112
+ if (reconnectTimer !== null) {
113
+ clearTimeout(reconnectTimer);
114
+ reconnectTimer = null;
115
+ }
116
+ cleanup();
117
+ backoff = BASE_BACKOFF_MS;
118
+ setStatus('disconnected');
119
+ }
120
+ function cleanup() {
121
+ if (stableTimer !== null) {
122
+ clearTimeout(stableTimer);
123
+ stableTimer = null;
124
+ }
125
+ if (eventSource) {
126
+ eventSource.close();
127
+ eventSource = null;
128
+ }
129
+ }
130
+ // --- Helpers ---
131
+ function setStatus(status) {
132
+ connectionStatus = status;
133
+ broadcast({ type: 'status', status });
134
+ }
135
+ async function broadcast(message) {
136
+ const clients = await self.clients.matchAll({ type: 'window' });
137
+ for (const client of clients) {
138
+ client.postMessage(message);
139
+ }
140
+ }