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