@shepai/cli 1.53.2 → 1.55.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 (298) hide show
  1. package/README.md +22 -8
  2. package/apis/json-schema/NotificationEvent.yaml +4 -0
  3. package/dist/packages/core/src/application/ports/output/services/daemon-service.interface.d.ts +49 -0
  4. package/dist/packages/core/src/application/ports/output/services/daemon-service.interface.d.ts.map +1 -0
  5. package/dist/packages/core/src/application/ports/output/services/daemon-service.interface.js +7 -0
  6. package/dist/packages/core/src/application/ports/output/services/index.d.ts +1 -0
  7. package/dist/packages/core/src/application/ports/output/services/index.d.ts.map +1 -1
  8. package/dist/packages/core/src/domain/generated/output.d.ts +4 -0
  9. package/dist/packages/core/src/domain/generated/output.d.ts.map +1 -1
  10. package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
  11. package/dist/packages/core/src/infrastructure/di/container.js +2 -0
  12. package/dist/packages/core/src/infrastructure/services/browser-opener.service.d.ts +1 -0
  13. package/dist/packages/core/src/infrastructure/services/browser-opener.service.d.ts.map +1 -1
  14. package/dist/packages/core/src/infrastructure/services/browser-opener.service.js +4 -0
  15. package/dist/packages/core/src/infrastructure/services/daemon/daemon-pid.service.d.ts +16 -0
  16. package/dist/packages/core/src/infrastructure/services/daemon/daemon-pid.service.d.ts.map +1 -0
  17. package/dist/packages/core/src/infrastructure/services/daemon/daemon-pid.service.js +72 -0
  18. package/dist/packages/core/src/infrastructure/services/filesystem/shep-directory.service.d.ts +7 -0
  19. package/dist/packages/core/src/infrastructure/services/filesystem/shep-directory.service.d.ts.map +1 -1
  20. package/dist/packages/core/src/infrastructure/services/filesystem/shep-directory.service.js +9 -0
  21. package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.d.ts +4 -0
  22. package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.d.ts.map +1 -1
  23. package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.js +19 -6
  24. package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.d.ts +5 -3
  25. package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.d.ts.map +1 -1
  26. package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.js +21 -13
  27. package/dist/src/presentation/cli/commands/_serve.command.d.ts +32 -0
  28. package/dist/src/presentation/cli/commands/_serve.command.d.ts.map +1 -0
  29. package/dist/src/presentation/cli/commands/_serve.command.js +91 -0
  30. package/dist/src/presentation/cli/commands/daemon/start-daemon.d.ts +27 -0
  31. package/dist/src/presentation/cli/commands/daemon/start-daemon.d.ts.map +1 -0
  32. package/dist/src/presentation/cli/commands/daemon/start-daemon.js +135 -0
  33. package/dist/src/presentation/cli/commands/start.command.d.ts +15 -0
  34. package/dist/src/presentation/cli/commands/start.command.d.ts.map +1 -0
  35. package/dist/src/presentation/cli/commands/start.command.js +33 -0
  36. package/dist/src/presentation/cli/commands/status.command.d.ts +20 -0
  37. package/dist/src/presentation/cli/commands/status.command.d.ts.map +1 -0
  38. package/dist/src/presentation/cli/commands/status.command.js +119 -0
  39. package/dist/src/presentation/cli/commands/stop.command.d.ts +22 -0
  40. package/dist/src/presentation/cli/commands/stop.command.d.ts.map +1 -0
  41. package/dist/src/presentation/cli/commands/stop.command.js +79 -0
  42. package/dist/src/presentation/cli/commands/ui.command.d.ts.map +1 -1
  43. package/dist/src/presentation/cli/commands/ui.command.js +2 -1
  44. package/dist/src/presentation/cli/index.d.ts +5 -2
  45. package/dist/src/presentation/cli/index.d.ts.map +1 -1
  46. package/dist/src/presentation/cli/index.js +23 -4
  47. package/dist/src/presentation/web/app/api/agent-events/health/route.d.ts +13 -0
  48. package/dist/src/presentation/web/app/api/agent-events/health/route.d.ts.map +1 -0
  49. package/dist/src/presentation/web/app/api/agent-events/health/route.js +60 -0
  50. package/dist/src/presentation/web/app/api/agent-events/route.d.ts +8 -4
  51. package/dist/src/presentation/web/app/api/agent-events/route.d.ts.map +1 -1
  52. package/dist/src/presentation/web/app/api/agent-events/route.js +227 -55
  53. package/dist/src/presentation/web/app/layout.d.ts.map +1 -1
  54. package/dist/src/presentation/web/app/layout.js +14 -0
  55. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts +5 -1
  56. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts.map +1 -1
  57. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.js +29 -1
  58. package/dist/src/presentation/web/components/common/repository-node/repository-node.d.ts.map +1 -1
  59. package/dist/src/presentation/web/components/common/repository-node/repository-node.js +1 -1
  60. package/dist/src/presentation/web/components/common/sound-toggle/index.d.ts +2 -0
  61. package/dist/src/presentation/web/components/common/sound-toggle/index.d.ts.map +1 -0
  62. package/dist/src/presentation/web/components/common/sound-toggle/index.js +1 -0
  63. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.d.ts +2 -0
  64. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.d.ts.map +1 -0
  65. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.js +9 -0
  66. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.d.ts +8 -0
  67. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.d.ts.map +1 -0
  68. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.js +17 -0
  69. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  70. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +14 -2
  71. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +1 -0
  72. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  73. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +151 -19
  74. package/dist/src/presentation/web/components/layouts/app-shell/app-shell.d.ts.map +1 -1
  75. package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +2 -1
  76. package/dist/src/presentation/web/dev-server.js +14 -0
  77. package/dist/src/presentation/web/hooks/use-agent-events.d.ts +7 -0
  78. package/dist/src/presentation/web/hooks/use-agent-events.d.ts.map +1 -1
  79. package/dist/src/presentation/web/hooks/use-agent-events.js +135 -47
  80. package/dist/src/presentation/web/hooks/use-notifications.d.ts.map +1 -1
  81. package/dist/src/presentation/web/hooks/use-notifications.js +56 -16
  82. package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts.map +1 -1
  83. package/dist/src/presentation/web/hooks/use-notifications.stories.js +28 -9
  84. package/dist/src/presentation/web/hooks/use-sound-enabled.d.ts +6 -0
  85. package/dist/src/presentation/web/hooks/use-sound-enabled.d.ts.map +1 -0
  86. package/dist/src/presentation/web/hooks/use-sound-enabled.js +27 -0
  87. package/dist/src/presentation/web/hooks/use-sound.d.ts +15 -0
  88. package/dist/src/presentation/web/hooks/use-sound.d.ts.map +1 -0
  89. package/dist/src/presentation/web/hooks/use-sound.js +83 -0
  90. package/dist/src/presentation/web/hooks/use-sound.stories.d.ts +8 -0
  91. package/dist/src/presentation/web/hooks/use-sound.stories.d.ts.map +1 -0
  92. package/dist/src/presentation/web/hooks/use-sound.stories.js +67 -0
  93. package/dist/src/presentation/web/public/agent-events-sw.d.ts +35 -0
  94. package/dist/src/presentation/web/public/agent-events-sw.d.ts.map +1 -0
  95. package/dist/src/presentation/web/public/agent-events-sw.js +140 -0
  96. package/dist/tsconfig.build.tsbuildinfo +1 -1
  97. package/package.json +1 -1
  98. package/web/.next/BUILD_ID +1 -1
  99. package/web/.next/app-path-routes-manifest.json +1 -0
  100. package/web/.next/build-manifest.json +2 -2
  101. package/web/.next/cache/.previewinfo +1 -1
  102. package/web/.next/cache/.rscinfo +1 -1
  103. package/web/.next/cache/.tsbuildinfo +1 -1
  104. package/web/.next/cache/config.json +3 -3
  105. package/web/.next/fallback-build-manifest.json +2 -2
  106. package/web/.next/prerender-manifest.json +3 -3
  107. package/web/.next/required-server-files.js +1 -1
  108. package/web/.next/required-server-files.json +1 -1
  109. package/web/.next/routes-manifest.json +6 -0
  110. package/web/.next/server/app/_global-error.html +2 -2
  111. package/web/.next/server/app/_global-error.rsc +1 -1
  112. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  113. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  114. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  115. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  116. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  117. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  118. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  119. package/web/.next/server/app/api/agent-events/health/route/app-paths-manifest.json +3 -0
  120. package/web/.next/server/app/api/agent-events/health/route/build-manifest.json +11 -0
  121. package/web/.next/server/app/api/agent-events/health/route/server-reference-manifest.json +4 -0
  122. package/web/.next/server/app/api/agent-events/health/route.js +6 -0
  123. package/web/.next/server/app/api/agent-events/health/route.js.map +5 -0
  124. package/web/.next/server/app/api/agent-events/health/route.js.nft.json +1 -0
  125. package/web/.next/server/app/api/agent-events/health/route_client-reference-manifest.js +2 -0
  126. package/web/.next/server/app/api/agent-events/route.js +2 -1
  127. package/web/.next/server/app/api/agent-events/route.js.nft.json +1 -1
  128. package/web/.next/server/app/page/server-reference-manifest.json +14 -14
  129. package/web/.next/server/app/page.js.nft.json +1 -1
  130. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  131. package/web/.next/server/app/version/page.js +1 -1
  132. package/web/.next/server/app/version/page.js.nft.json +1 -1
  133. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  134. package/web/.next/server/app-paths-manifest.json +1 -0
  135. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +6 -0
  136. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -0
  137. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_health_route_actions_4d471160.js +3 -0
  138. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_health_route_actions_4d471160.js.map +1 -0
  139. package/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js +3 -0
  140. package/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js.map +1 -0
  141. package/web/.next/server/chunks/[root-of-the-server]__d9d410a8._.js +3 -0
  142. package/web/.next/server/chunks/[root-of-the-server]__d9d410a8._.js.map +1 -0
  143. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
  144. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js.map +1 -1
  145. package/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +2 -2
  146. package/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js.map +1 -1
  147. 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
  148. package/web/.next/server/chunks/ssr/{_1fafa9db._.js → _1632a21f._.js} +2 -2
  149. package/web/.next/server/chunks/ssr/_1632a21f._.js.map +1 -0
  150. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +1 -1
  151. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js.map +1 -1
  152. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  153. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  154. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
  155. package/web/.next/server/pages/500.html +2 -2
  156. package/web/.next/server/server-reference-manifest.js +1 -1
  157. package/web/.next/server/server-reference-manifest.json +15 -15
  158. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  159. package/web/.next/standalone/src/presentation/web/.next/app-path-routes-manifest.json +1 -0
  160. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  161. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  162. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  163. package/web/.next/standalone/src/presentation/web/.next/routes-manifest.json +6 -0
  164. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  165. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  166. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  167. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  168. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  169. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  170. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  171. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  172. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  173. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route/app-paths-manifest.json +3 -0
  174. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route/build-manifest.json +11 -0
  175. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route/server-reference-manifest.json +4 -0
  176. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route.js +6 -0
  177. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route.js.map +5 -0
  178. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route.js.nft.json +1 -0
  179. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route_client-reference-manifest.js +2 -0
  180. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/route.js +2 -1
  181. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/route.js.nft.json +1 -1
  182. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +14 -14
  183. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js.nft.json +1 -1
  184. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  185. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js +1 -1
  186. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  187. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  188. package/web/.next/standalone/src/presentation/web/.next/server/app-paths-manifest.json +1 -0
  189. package/web/.next/standalone/src/presentation/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +6 -0
  190. 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
  191. package/web/.next/standalone/src/presentation/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js +3 -0
  192. package/web/.next/standalone/src/presentation/web/.next/server/chunks/[root-of-the-server]__d9d410a8._.js +3 -0
  193. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
  194. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +2 -2
  195. 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
  196. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{_1fafa9db._.js → _1632a21f._.js} +2 -2
  197. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +1 -1
  198. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  199. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  200. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  201. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +15 -15
  202. package/web/.next/standalone/src/presentation/web/app/api/agent-events/health/route.ts +69 -0
  203. package/web/.next/standalone/src/presentation/web/app/api/agent-events/route.ts +258 -54
  204. package/web/.next/standalone/src/presentation/web/app/layout.tsx +14 -0
  205. package/web/.next/standalone/src/presentation/web/components/common/feature-node/derive-feature-state.ts +39 -2
  206. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-node.tsx +1 -0
  207. package/web/.next/standalone/src/presentation/web/components/common/sound-toggle/index.ts +1 -0
  208. package/web/.next/standalone/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.tsx +27 -0
  209. package/web/.next/standalone/src/presentation/web/components/common/sound-toggle/sound-toggle.tsx +21 -0
  210. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +15 -1
  211. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +194 -31
  212. package/web/.next/standalone/src/presentation/web/components/layouts/app-shell/app-shell.tsx +3 -1
  213. package/web/.next/standalone/src/presentation/web/dev-server.ts +21 -0
  214. package/web/.next/standalone/src/presentation/web/hooks/use-agent-events.ts +161 -52
  215. package/web/.next/standalone/src/presentation/web/hooks/use-notifications.stories.tsx +39 -10
  216. package/web/.next/standalone/src/presentation/web/hooks/use-notifications.ts +68 -15
  217. package/web/.next/standalone/src/presentation/web/hooks/use-sound-enabled.ts +38 -0
  218. package/web/.next/standalone/src/presentation/web/hooks/use-sound.stories.tsx +109 -0
  219. package/web/.next/standalone/src/presentation/web/hooks/use-sound.ts +101 -0
  220. package/web/.next/standalone/src/presentation/web/public/agent-events-sw.js +164 -0
  221. package/web/.next/standalone/src/presentation/web/public/sounds/button.wav +0 -0
  222. package/web/.next/standalone/src/presentation/web/public/sounds/caution.wav +0 -0
  223. package/web/.next/standalone/src/presentation/web/public/sounds/celebration.wav +0 -0
  224. package/web/.next/standalone/src/presentation/web/public/sounds/disabled.wav +0 -0
  225. package/web/.next/standalone/src/presentation/web/public/sounds/notification.wav +0 -0
  226. package/web/.next/standalone/src/presentation/web/public/sounds/progress_loop.wav +0 -0
  227. package/web/.next/standalone/src/presentation/web/public/sounds/ringtone_loop.wav +0 -0
  228. package/web/.next/standalone/src/presentation/web/public/sounds/select.wav +0 -0
  229. package/web/.next/standalone/src/presentation/web/public/sounds/swipe.wav +0 -0
  230. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_01.wav +0 -0
  231. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_02.wav +0 -0
  232. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_03.wav +0 -0
  233. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_04.wav +0 -0
  234. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_05.wav +0 -0
  235. package/web/.next/standalone/src/presentation/web/public/sounds/tap_01.wav +0 -0
  236. package/web/.next/standalone/src/presentation/web/public/sounds/tap_02.wav +0 -0
  237. package/web/.next/standalone/src/presentation/web/public/sounds/tap_03.wav +0 -0
  238. package/web/.next/standalone/src/presentation/web/public/sounds/tap_04.wav +0 -0
  239. package/web/.next/standalone/src/presentation/web/public/sounds/tap_05.wav +0 -0
  240. package/web/.next/standalone/src/presentation/web/public/sounds/toggle_off.wav +0 -0
  241. package/web/.next/standalone/src/presentation/web/public/sounds/toggle_on.wav +0 -0
  242. package/web/.next/standalone/src/presentation/web/public/sounds/transition_down.wav +0 -0
  243. package/web/.next/standalone/src/presentation/web/public/sounds/transition_up.wav +0 -0
  244. package/web/.next/standalone/src/presentation/web/public/sounds/type_01.wav +0 -0
  245. package/web/.next/standalone/src/presentation/web/public/sounds/type_02.wav +0 -0
  246. package/web/.next/standalone/src/presentation/web/public/sounds/type_03.wav +0 -0
  247. package/web/.next/standalone/src/presentation/web/public/sounds/type_04.wav +0 -0
  248. package/web/.next/standalone/src/presentation/web/public/sounds/type_05.wav +0 -0
  249. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  250. package/web/.next/static/chunks/1f13bc20e938335c.css +2 -0
  251. package/web/.next/static/chunks/3eaa6f2966e27b8f.js +10 -0
  252. package/web/.next/static/chunks/{68fb91b6eca14536.js → 8f4461aee9a05741.js} +2 -2
  253. package/web/.next/static/chunks/9a82d8c76f175235.js +1 -0
  254. package/web/.next/trace +1 -1
  255. package/web/.next/trace-build +1 -1
  256. package/web/.next/types/link.d.ts +1 -0
  257. package/web/.next/types/routes.d.ts +2 -1
  258. package/web/.next/types/validator.ts +9 -0
  259. package/web/public/agent-events-sw.js +164 -0
  260. package/web/public/sounds/button.wav +0 -0
  261. package/web/public/sounds/caution.wav +0 -0
  262. package/web/public/sounds/celebration.wav +0 -0
  263. package/web/public/sounds/disabled.wav +0 -0
  264. package/web/public/sounds/notification.wav +0 -0
  265. package/web/public/sounds/progress_loop.wav +0 -0
  266. package/web/public/sounds/ringtone_loop.wav +0 -0
  267. package/web/public/sounds/select.wav +0 -0
  268. package/web/public/sounds/swipe.wav +0 -0
  269. package/web/public/sounds/swipe_01.wav +0 -0
  270. package/web/public/sounds/swipe_02.wav +0 -0
  271. package/web/public/sounds/swipe_03.wav +0 -0
  272. package/web/public/sounds/swipe_04.wav +0 -0
  273. package/web/public/sounds/swipe_05.wav +0 -0
  274. package/web/public/sounds/tap_01.wav +0 -0
  275. package/web/public/sounds/tap_02.wav +0 -0
  276. package/web/public/sounds/tap_03.wav +0 -0
  277. package/web/public/sounds/tap_04.wav +0 -0
  278. package/web/public/sounds/tap_05.wav +0 -0
  279. package/web/public/sounds/toggle_off.wav +0 -0
  280. package/web/public/sounds/toggle_on.wav +0 -0
  281. package/web/public/sounds/transition_down.wav +0 -0
  282. package/web/public/sounds/transition_up.wav +0 -0
  283. package/web/public/sounds/type_01.wav +0 -0
  284. package/web/public/sounds/type_02.wav +0 -0
  285. package/web/public/sounds/type_03.wav +0 -0
  286. package/web/public/sounds/type_04.wav +0 -0
  287. package/web/public/sounds/type_05.wav +0 -0
  288. package/web/.next/server/chunks/[root-of-the-server]__7d5c8b74._.js +0 -6
  289. package/web/.next/server/chunks/[root-of-the-server]__7d5c8b74._.js.map +0 -1
  290. package/web/.next/server/chunks/ssr/_1fafa9db._.js.map +0 -1
  291. package/web/.next/standalone/src/presentation/web/.next/server/chunks/[root-of-the-server]__7d5c8b74._.js +0 -6
  292. package/web/.next/static/chunks/13d5334b17480a16.js +0 -10
  293. package/web/.next/static/chunks/16dec8021e251230.css +0 -2
  294. package/web/.next/static/chunks/22f30ce91a3c79c1.js +0 -1
  295. /package/web/.next/server/chunks/ssr/{[root-of-the-server]__249c74f6._.js.map → [root-of-the-server]__6e8b5181._.js.map} +0 -0
  296. /package/web/.next/static/{nFvWyY2BF3AIS3FpZBKEb → JmDtIt3S6EfLuRKwlzJrW}/_buildManifest.js +0 -0
  297. /package/web/.next/static/{nFvWyY2BF3AIS3FpZBKEb → JmDtIt3S6EfLuRKwlzJrW}/_clientMiddlewareManifest.json +0 -0
  298. /package/web/.next/static/{nFvWyY2BF3AIS3FpZBKEb → JmDtIt3S6EfLuRKwlzJrW}/_ssgManifest.js +0 -0
@@ -1,53 +1,143 @@
1
1
  'use client';
2
2
  import { useCallback, useEffect, useRef, useState } from 'react';
3
- const BASE_BACKOFF_MS = 1000;
4
- const MAX_BACKOFF_MS = 30_000;
5
- /** Only reset backoff after connection is stable for this long */
6
- const STABLE_CONNECTION_MS = 5_000;
3
+ const SW_PATH = '/agent-events-sw.js';
4
+ /**
5
+ * Hook that receives real-time agent notification events via a Service Worker.
6
+ *
7
+ * The SW maintains a single SSE connection to /api/agent-events and
8
+ * broadcasts events to all open tabs. Falls back to direct EventSource
9
+ * when Service Workers are unavailable.
10
+ */
7
11
  export function useAgentEvents(options) {
8
12
  const [events, setEvents] = useState([]);
9
13
  const [lastEvent, setLastEvent] = useState(null);
10
14
  const [connectionStatus, setConnectionStatus] = useState('disconnected');
11
- const backoffRef = useRef(BASE_BACKOFF_MS);
12
- const reconnectTimerRef = useRef(null);
13
- const stableTimerRef = useRef(null);
14
- const esRef = useRef(null);
15
15
  const runId = options?.runId;
16
- const connect = useCallback(() => {
17
- if (typeof window === 'undefined' || typeof EventSource === 'undefined') {
16
+ const swRef = useRef(null);
17
+ const onMessage = useCallback((event) => {
18
+ const msg = event.data;
19
+ if (!msg || typeof msg !== 'object')
18
20
  return;
21
+ if (msg.type === 'notification') {
22
+ const parsed = msg.data;
23
+ setEvents((prev) => [...prev, parsed]);
24
+ setLastEvent(parsed);
19
25
  }
20
- // Prevent duplicate connections
21
- if (esRef.current) {
26
+ else if (msg.type === 'status') {
27
+ setConnectionStatus(msg.status);
28
+ }
29
+ }, []);
30
+ useEffect(() => {
31
+ if (typeof window === 'undefined')
22
32
  return;
33
+ // ?sse=direct bypasses the Service Worker for debugging
34
+ const bypassSW = typeof location !== 'undefined' && new URLSearchParams(location.search).has('sse');
35
+ // Fallback: direct EventSource when SW not supported or bypassed
36
+ if (!navigator.serviceWorker || bypassSW) {
37
+ return connectDirectEventSource(runId, setEvents, setLastEvent, setConnectionStatus);
23
38
  }
39
+ let cancelled = false;
40
+ const fallbackCleanupRef = { current: undefined };
41
+ function subscribeToWorker(worker) {
42
+ if (cancelled)
43
+ return;
44
+ swRef.current = worker;
45
+ worker.postMessage({ type: 'subscribe', runId });
46
+ setConnectionStatus('connecting');
47
+ }
48
+ // Listen for messages from whatever SW controls this page
49
+ navigator.serviceWorker.addEventListener('message', onMessage);
50
+ // Re-subscribe when SW controller changes (e.g., after SW update via skipWaiting)
51
+ function handleControllerChange() {
52
+ if (cancelled)
53
+ return;
54
+ const newController = navigator.serviceWorker.controller;
55
+ if (newController) {
56
+ swRef.current = newController;
57
+ newController.postMessage({ type: 'subscribe', runId });
58
+ setConnectionStatus('connecting');
59
+ }
60
+ }
61
+ navigator.serviceWorker.addEventListener('controllerchange', handleControllerChange);
62
+ navigator.serviceWorker
63
+ .register(SW_PATH, { scope: '/' })
64
+ .then((registration) => {
65
+ if (cancelled)
66
+ return;
67
+ // Use the active worker, or wait for it to activate
68
+ const sw = registration.active ?? registration.installing ?? registration.waiting;
69
+ if (!sw) {
70
+ // No worker at all — fall back to direct EventSource
71
+ fallbackCleanupRef.current = connectDirectEventSource(runId, setEvents, setLastEvent, setConnectionStatus);
72
+ return;
73
+ }
74
+ if (sw.state === 'activated') {
75
+ subscribeToWorker(sw);
76
+ }
77
+ else {
78
+ sw.addEventListener('statechange', () => {
79
+ if (sw.state === 'activated')
80
+ subscribeToWorker(sw);
81
+ });
82
+ }
83
+ })
84
+ .catch(() => {
85
+ // SW registration failed — fall back to direct EventSource
86
+ if (!cancelled) {
87
+ fallbackCleanupRef.current = connectDirectEventSource(runId, setEvents, setLastEvent, setConnectionStatus);
88
+ }
89
+ });
90
+ return () => {
91
+ cancelled = true;
92
+ navigator.serviceWorker.removeEventListener('message', onMessage);
93
+ navigator.serviceWorker.removeEventListener('controllerchange', handleControllerChange);
94
+ swRef.current?.postMessage({ type: 'unsubscribe' });
95
+ swRef.current = null;
96
+ fallbackCleanupRef.current?.();
97
+ };
98
+ }, [runId, onMessage]);
99
+ return { events, lastEvent, connectionStatus };
100
+ }
101
+ // --- EventSource fallback (identical to the previous implementation) ---
102
+ const BASE_BACKOFF_MS = 1000;
103
+ const MAX_BACKOFF_MS = 30_000;
104
+ const STABLE_CONNECTION_MS = 5_000;
105
+ function connectDirectEventSource(runId, setEvents, setLastEvent, setConnectionStatus) {
106
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
107
+ if (typeof EventSource === 'undefined')
108
+ return () => { };
109
+ let es = null;
110
+ let stopped = false;
111
+ let backoff = BASE_BACKOFF_MS;
112
+ let reconnectTimer = null;
113
+ let stableTimer = null;
114
+ function connect() {
115
+ if (stopped)
116
+ return;
24
117
  const url = runId
25
118
  ? `/api/agent-events?runId=${encodeURIComponent(runId)}`
26
119
  : '/api/agent-events';
27
- const es = new EventSource(url);
28
- esRef.current = es;
120
+ es = new EventSource(url);
29
121
  setConnectionStatus('connecting');
30
122
  es.onopen = () => {
31
123
  setConnectionStatus('connected');
32
- // Only reset backoff after connection stays open for a while
33
- stableTimerRef.current = setTimeout(() => {
34
- stableTimerRef.current = null;
35
- backoffRef.current = BASE_BACKOFF_MS;
124
+ stableTimer = setTimeout(() => {
125
+ stableTimer = null;
126
+ backoff = BASE_BACKOFF_MS;
36
127
  }, STABLE_CONNECTION_MS);
37
128
  };
38
129
  es.onerror = () => {
39
- es.close();
40
- esRef.current = null;
130
+ es?.close();
131
+ es = null;
41
132
  setConnectionStatus('disconnected');
42
- // Cancel stable timer — connection wasn't stable
43
- if (stableTimerRef.current !== null) {
44
- clearTimeout(stableTimerRef.current);
45
- stableTimerRef.current = null;
133
+ if (stableTimer !== null) {
134
+ clearTimeout(stableTimer);
135
+ stableTimer = null;
46
136
  }
47
- const delay = backoffRef.current;
48
- backoffRef.current = Math.min(delay * 2, MAX_BACKOFF_MS);
49
- reconnectTimerRef.current = setTimeout(() => {
50
- reconnectTimerRef.current = null;
137
+ const delay = backoff;
138
+ backoff = Math.min(delay * 2, MAX_BACKOFF_MS);
139
+ reconnectTimer = setTimeout(() => {
140
+ reconnectTimer = null;
51
141
  connect();
52
142
  }, delay);
53
143
  };
@@ -56,23 +146,21 @@ export function useAgentEvents(options) {
56
146
  setEvents((prev) => [...prev, parsed]);
57
147
  setLastEvent(parsed);
58
148
  }));
59
- }, [runId]);
60
- useEffect(() => {
61
- connect();
62
- return () => {
63
- if (reconnectTimerRef.current !== null) {
64
- clearTimeout(reconnectTimerRef.current);
65
- reconnectTimerRef.current = null;
66
- }
67
- if (stableTimerRef.current !== null) {
68
- clearTimeout(stableTimerRef.current);
69
- stableTimerRef.current = null;
70
- }
71
- if (esRef.current) {
72
- esRef.current.close();
73
- esRef.current = null;
74
- }
75
- };
76
- }, [connect]);
77
- return { events, lastEvent, connectionStatus };
149
+ }
150
+ connect();
151
+ return () => {
152
+ stopped = true;
153
+ if (reconnectTimer !== null) {
154
+ clearTimeout(reconnectTimer);
155
+ reconnectTimer = null;
156
+ }
157
+ if (stableTimer !== null) {
158
+ clearTimeout(stableTimer);
159
+ stableTimer = null;
160
+ }
161
+ if (es) {
162
+ es.close();
163
+ es = null;
164
+ }
165
+ };
78
166
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-notifications.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-notifications.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,sBAAsB;IACrC,wBAAwB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,sBAAsB,EAAE,sBAAsB,CAAC;CAChD;AAqBD,wBAAgB,gBAAgB,IAAI,sBAAsB,CAmCzD"}
1
+ {"version":3,"file":"use-notifications.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-notifications.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,sBAAsB;IACrC,wBAAwB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,sBAAsB,EAAE,sBAAsB,CAAC;CAChD;AA4CD,wBAAgB,gBAAgB,IAAI,sBAAsB,CAgEzD"}
@@ -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"}