@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
@@ -2,80 +2,284 @@
2
2
  * SSE API Route: GET /api/agent-events
3
3
  *
4
4
  * Streams agent lifecycle notification events to connected web UI clients
5
- * via Server-Sent Events (SSE). This is the first API route in the web UI.
5
+ * via Server-Sent Events (SSE).
6
6
  *
7
- * - Subscribes to the notification event bus (lazy-initialized singleton)
8
- * - Formats events as SSE data frames (event: notification\ndata: JSON\n\n)
7
+ * Uses DB polling with a per-connection cache so only deltas are sent.
8
+ * This avoids cross-module singleton issues with an in-process event bus.
9
+ *
10
+ * - Polls features + agent runs every 500ms
11
+ * - Compares against cached state and emits only changes
9
12
  * - Sends heartbeat comments every 30 seconds to keep connection alive
10
13
  * - Supports optional ?runId query parameter to filter events
11
- * - Cleans up listeners and intervals on client disconnect
14
+ * - Cleans up intervals on client disconnect
12
15
  */
13
16
 
14
- import { getNotificationBus } from '@shepai/core/infrastructure/services/notifications/notification-bus';
17
+ import { resolve } from '@/lib/server-container';
18
+ import type { IAgentRunRepository } from '@shepai/core/application/ports/output/agents/agent-run-repository.interface';
19
+ import type { IPhaseTimingRepository } from '@shepai/core/application/ports/output/agents/phase-timing-repository.interface';
20
+ import type { Feature, AgentRun } from '@shepai/core/domain/generated/output';
21
+ import {
22
+ AgentRunStatus,
23
+ SdlcLifecycle,
24
+ NotificationEventType,
25
+ NotificationSeverity,
26
+ } from '@shepai/core/domain/generated/output';
15
27
  import type { NotificationEvent } from '@shepai/core/domain/generated/output';
28
+ import type { ListFeaturesUseCase } from '@shepai/core/application/use-cases/features/list-features.use-case';
29
+
30
+ // Force dynamic — SSE streams must never be statically optimized or cached
31
+ export const dynamic = 'force-dynamic';
16
32
 
33
+ const POLL_INTERVAL_MS = 500;
17
34
  const HEARTBEAT_INTERVAL_MS = 30_000;
18
35
 
19
- function formatSSEEvent(event: NotificationEvent): string {
20
- return `event: notification\ndata: ${JSON.stringify(event)}\n\n`;
36
+ interface CachedFeatureState {
37
+ status: AgentRunStatus | null;
38
+ lifecycle: string;
39
+ completedPhases: Set<string>;
40
+ featureName: string;
21
41
  }
22
42
 
23
- function formatHeartbeat(): string {
24
- return ': heartbeat\n\n';
43
+ /**
44
+ * Maps SdlcLifecycle values to agent graph node names so the client
45
+ * can derive the correct FeatureLifecyclePhase via mapPhaseNameToLifecycle().
46
+ */
47
+ const LIFECYCLE_TO_NODE: Partial<Record<SdlcLifecycle, string>> = {
48
+ [SdlcLifecycle.Analyze]: 'analyze',
49
+ [SdlcLifecycle.Requirements]: 'requirements',
50
+ [SdlcLifecycle.Research]: 'research',
51
+ [SdlcLifecycle.Planning]: 'plan',
52
+ [SdlcLifecycle.Implementation]: 'implement',
53
+ [SdlcLifecycle.Review]: 'merge',
54
+ };
55
+
56
+ const STATUS_TO_EVENT: Partial<
57
+ Record<AgentRunStatus, { eventType: NotificationEventType; severity: NotificationSeverity }>
58
+ > = {
59
+ [AgentRunStatus.running]: {
60
+ eventType: NotificationEventType.AgentStarted,
61
+ severity: NotificationSeverity.Info,
62
+ },
63
+ [AgentRunStatus.waitingApproval]: {
64
+ eventType: NotificationEventType.WaitingApproval,
65
+ severity: NotificationSeverity.Warning,
66
+ },
67
+ [AgentRunStatus.completed]: {
68
+ eventType: NotificationEventType.AgentCompleted,
69
+ severity: NotificationSeverity.Success,
70
+ },
71
+ [AgentRunStatus.failed]: {
72
+ eventType: NotificationEventType.AgentFailed,
73
+ severity: NotificationSeverity.Error,
74
+ },
75
+ [AgentRunStatus.interrupted]: {
76
+ eventType: NotificationEventType.AgentFailed,
77
+ severity: NotificationSeverity.Warning,
78
+ },
79
+ [AgentRunStatus.cancelled]: {
80
+ eventType: NotificationEventType.AgentFailed,
81
+ severity: NotificationSeverity.Warning,
82
+ },
83
+ };
84
+
85
+ /** Map agent graph node name from AgentRun.result to a phase name. */
86
+ function resultToPhase(result: string | undefined): string | undefined {
87
+ if (!result?.startsWith('node:')) return undefined;
88
+ return result.slice(5); // "node:analyze" → "analyze"
25
89
  }
26
90
 
27
91
  export function GET(request: Request): Response {
28
- const url = new URL(request.url);
29
- const runIdFilter = url.searchParams.get('runId');
92
+ try {
93
+ const url = new URL(request.url);
94
+ const runIdFilter = url.searchParams.get('runId');
30
95
 
31
- const stream = new ReadableStream<Uint8Array>({
32
- start(controller) {
33
- const encoder = new TextEncoder();
34
- const bus = getNotificationBus();
96
+ const stream = new ReadableStream<Uint8Array>({
97
+ start(controller) {
98
+ const encoder = new TextEncoder();
35
99
 
36
- const onNotification = (event: NotificationEvent) => {
37
- if (runIdFilter && event.agentRunId !== runIdFilter) {
38
- return;
100
+ // Per-connection cache: featureId last-seen state
101
+ const cache = new Map<string, CachedFeatureState>();
102
+ let stopped = false;
103
+
104
+ function enqueue(text: string) {
105
+ if (stopped) return;
106
+ try {
107
+ controller.enqueue(encoder.encode(text));
108
+ } catch {
109
+ // Stream may be closed
110
+ }
39
111
  }
40
112
 
41
- try {
42
- controller.enqueue(encoder.encode(formatSSEEvent(event)));
43
- } catch {
44
- // Stream may be closed ignore enqueue errors
113
+ function emitEvent(event: NotificationEvent) {
114
+ // eslint-disable-next-line no-console
115
+ console.log(
116
+ `[SSE] emit: ${event.eventType} for "${event.featureName}"${event.phaseName ? ` (${event.phaseName})` : ''}`
117
+ );
118
+ enqueue(`event: notification\ndata: ${JSON.stringify(event)}\n\n`);
45
119
  }
46
- };
47
120
 
48
- bus.on('notification', onNotification);
121
+ let pollErrorCount = 0;
49
122
 
50
- // Heartbeat to keep connection alive
51
- const heartbeatInterval = setInterval(() => {
52
- try {
53
- controller.enqueue(encoder.encode(formatHeartbeat()));
54
- } catch {
55
- // Stream may be closed — ignore enqueue errors
56
- }
57
- }, HEARTBEAT_INTERVAL_MS);
58
-
59
- // Cleanup on client disconnect
60
- const cleanup = () => {
61
- bus.removeListener('notification', onNotification);
62
- clearInterval(heartbeatInterval);
63
- try {
64
- controller.close();
65
- } catch {
66
- // Stream may already be closed
123
+ async function poll() {
124
+ if (stopped) return;
125
+
126
+ try {
127
+ const listFeatures = resolve<ListFeaturesUseCase>('ListFeaturesUseCase');
128
+ const agentRunRepo = resolve<IAgentRunRepository>('IAgentRunRepository');
129
+ const phaseTimingRepo = resolve<IPhaseTimingRepository>('IPhaseTimingRepository');
130
+
131
+ const features = await listFeatures.execute();
132
+
133
+ // Build current state for features with agent runs
134
+ const entries: { feature: Feature; run: AgentRun | null }[] = await Promise.all(
135
+ features.map(async (feature) => {
136
+ const run = feature.agentRunId
137
+ ? await agentRunRepo.findById(feature.agentRunId)
138
+ : null;
139
+ return { feature, run };
140
+ })
141
+ );
142
+
143
+ for (const { feature, run } of entries) {
144
+ if (!run) continue;
145
+
146
+ // Apply runId filter if present
147
+ if (runIdFilter && run.id !== runIdFilter) continue;
148
+
149
+ const prev = cache.get(feature.id);
150
+
151
+ if (!prev) {
152
+ // First time seeing this feature — seed cache, don't emit
153
+ const completedPhases = new Set<string>();
154
+ try {
155
+ const timings = await phaseTimingRepo.findByRunId(run.id);
156
+ for (const t of timings) {
157
+ if (t.completedAt) completedPhases.add(t.phase);
158
+ }
159
+ } catch {
160
+ // Ignore timing errors
161
+ }
162
+
163
+ cache.set(feature.id, {
164
+ status: run.status,
165
+ lifecycle: feature.lifecycle,
166
+ completedPhases,
167
+ featureName: feature.name,
168
+ });
169
+ continue;
170
+ }
171
+
172
+ // Check for status change
173
+ if (prev.status !== run.status) {
174
+ prev.status = run.status;
175
+ const mapping = STATUS_TO_EVENT[run.status];
176
+ if (mapping) {
177
+ const phase = resultToPhase(run.result);
178
+ emitEvent({
179
+ eventType: mapping.eventType,
180
+ agentRunId: run.id,
181
+ featureId: feature.id,
182
+ featureName: feature.name,
183
+ ...(phase && { phaseName: phase }),
184
+ message: `Agent status: ${run.status}`,
185
+ severity: mapping.severity,
186
+ timestamp: new Date().toISOString(),
187
+ });
188
+ }
189
+ }
190
+
191
+ // Check for lifecycle change (agent stays "running" but moves through phases)
192
+ if (prev.lifecycle !== feature.lifecycle) {
193
+ prev.lifecycle = feature.lifecycle;
194
+ const nodeName = LIFECYCLE_TO_NODE[feature.lifecycle as SdlcLifecycle];
195
+ if (nodeName) {
196
+ emitEvent({
197
+ eventType: NotificationEventType.PhaseCompleted,
198
+ agentRunId: run.id,
199
+ featureId: feature.id,
200
+ featureName: feature.name,
201
+ phaseName: nodeName,
202
+ message: `Entered ${nodeName} phase`,
203
+ severity: NotificationSeverity.Info,
204
+ timestamp: new Date().toISOString(),
205
+ });
206
+ }
207
+ }
208
+
209
+ // Check for new phase completions
210
+ try {
211
+ const timings = await phaseTimingRepo.findByRunId(run.id);
212
+ for (const t of timings) {
213
+ if (t.completedAt && !prev.completedPhases.has(t.phase)) {
214
+ prev.completedPhases.add(t.phase);
215
+ emitEvent({
216
+ eventType: NotificationEventType.PhaseCompleted,
217
+ agentRunId: run.id,
218
+ featureId: feature.id,
219
+ featureName: feature.name,
220
+ phaseName: t.phase,
221
+ message: `Completed ${t.phase} phase`,
222
+ severity: NotificationSeverity.Info,
223
+ timestamp: new Date().toISOString(),
224
+ });
225
+ }
226
+ }
227
+ } catch {
228
+ // Ignore timing errors
229
+ }
230
+ }
231
+ pollErrorCount = 0; // Reset on success
232
+ } catch (error) {
233
+ pollErrorCount++;
234
+ // Log first few errors, then throttle to avoid spamming
235
+ if (pollErrorCount <= 3 || pollErrorCount % 60 === 0) {
236
+ // eslint-disable-next-line no-console
237
+ console.error(
238
+ `[SSE /api/agent-events] poll error #${pollErrorCount}:`,
239
+ error instanceof Error ? error.message : error
240
+ );
241
+ }
242
+ }
67
243
  }
68
- };
69
-
70
- request.signal.addEventListener('abort', cleanup, { once: true });
71
- },
72
- });
73
-
74
- return new Response(stream, {
75
- headers: {
76
- 'Content-Type': 'text/event-stream',
77
- 'Cache-Control': 'no-cache',
78
- Connection: 'keep-alive',
79
- },
80
- });
244
+
245
+ // First poll immediately, then every POLL_INTERVAL_MS
246
+ void poll();
247
+ const pollInterval = setInterval(() => void poll(), POLL_INTERVAL_MS);
248
+
249
+ // Heartbeat to keep connection alive
250
+ const heartbeatInterval = setInterval(() => {
251
+ enqueue(': heartbeat\n\n');
252
+ }, HEARTBEAT_INTERVAL_MS);
253
+
254
+ // Cleanup on client disconnect
255
+ const cleanup = () => {
256
+ stopped = true;
257
+ clearInterval(pollInterval);
258
+ clearInterval(heartbeatInterval);
259
+ try {
260
+ controller.close();
261
+ } catch {
262
+ // Stream may already be closed
263
+ }
264
+ };
265
+
266
+ request.signal.addEventListener('abort', cleanup, { once: true });
267
+ },
268
+ });
269
+
270
+ return new Response(stream, {
271
+ headers: {
272
+ 'Content-Type': 'text/event-stream',
273
+ 'Cache-Control': 'no-cache',
274
+ Connection: 'keep-alive',
275
+ },
276
+ });
277
+ } catch (error) {
278
+ // eslint-disable-next-line no-console
279
+ console.error('[SSE route] GET handler error:', error);
280
+ return new Response(JSON.stringify({ error: String(error) }), {
281
+ status: 500,
282
+ headers: { 'Content-Type': 'application/json' },
283
+ });
284
+ }
81
285
  }
@@ -11,6 +11,20 @@ export const metadata: Metadata = {
11
11
  title: 'Shep AI',
12
12
  description:
13
13
  'Autonomous AI Native SDLC Platform - Automate the development cycle from idea to deploy',
14
+ icons: [
15
+ {
16
+ rel: 'icon',
17
+ url: '/favicon-light.svg',
18
+ type: 'image/svg+xml',
19
+ media: '(prefers-color-scheme: light)',
20
+ },
21
+ {
22
+ rel: 'icon',
23
+ url: '/favicon-dark.svg',
24
+ type: 'image/svg+xml',
25
+ media: '(prefers-color-scheme: dark)',
26
+ },
27
+ ],
14
28
  };
15
29
 
16
30
  export default function RootLayout({
@@ -5,9 +5,14 @@
5
5
  * and its associated AgentRun status (mirrors CLI feat ls logic).
6
6
  */
7
7
 
8
- import { SdlcLifecycle, AgentRunStatus, TaskState } from '@shepai/core/domain/generated';
8
+ import {
9
+ SdlcLifecycle,
10
+ AgentRunStatus,
11
+ TaskState,
12
+ NotificationEventType,
13
+ } from '@shepai/core/domain/generated';
9
14
  import type { Feature, AgentRun } from '@shepai/core/domain/generated';
10
- import type { FeatureNodeState } from './feature-node-state-config';
15
+ import type { FeatureNodeState, FeatureLifecyclePhase } from './feature-node-state-config';
11
16
 
12
17
  /**
13
18
  * Derives the visual node state from a Feature and its optional AgentRun.
@@ -85,3 +90,35 @@ export function deriveProgress(feature: Feature): number {
85
90
  const doneCount = tasks.filter((t) => t.state === TaskState.Done).length;
86
91
  return Math.round((doneCount / tasks.length) * 100);
87
92
  }
93
+
94
+ /** Maps a NotificationEventType to the corresponding FeatureNodeState for optimistic UI updates. */
95
+ export function mapEventTypeToState(eventType: NotificationEventType): FeatureNodeState {
96
+ switch (eventType) {
97
+ case NotificationEventType.AgentStarted:
98
+ case NotificationEventType.PhaseCompleted:
99
+ return 'running';
100
+ case NotificationEventType.WaitingApproval:
101
+ return 'action-required';
102
+ case NotificationEventType.AgentCompleted:
103
+ return 'done';
104
+ case NotificationEventType.AgentFailed:
105
+ return 'error';
106
+ }
107
+ }
108
+
109
+ /** Maps an SSE event phaseName to a FeatureLifecyclePhase. Mirrors page.tsx nodeToLifecyclePhase. */
110
+ const phaseNameToLifecycle: Record<string, FeatureLifecyclePhase> = {
111
+ analyze: 'requirements',
112
+ requirements: 'requirements',
113
+ research: 'research',
114
+ plan: 'implementation',
115
+ implement: 'implementation',
116
+ merge: 'review',
117
+ };
118
+
119
+ export function mapPhaseNameToLifecycle(
120
+ phaseName: string | undefined
121
+ ): FeatureLifecyclePhase | undefined {
122
+ if (!phaseName) return undefined;
123
+ return phaseNameToLifecycle[phaseName];
124
+ }
@@ -84,6 +84,7 @@ export function RepositoryNode({ data }: { data: RepositoryNodeData; [key: strin
84
84
  role="button"
85
85
  tabIndex={0}
86
86
  data-testid="repository-node-card"
87
+ data-repo-name={data.name}
87
88
  onClick={(e) => {
88
89
  e.stopPropagation();
89
90
  data.onClick?.();
@@ -0,0 +1 @@
1
+ export { SoundToggle } from './sound-toggle';
@@ -0,0 +1,27 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { SoundToggle } from './sound-toggle';
3
+
4
+ const meta: Meta<typeof SoundToggle> = {
5
+ title: 'Composed/SoundToggle',
6
+ component: SoundToggle,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ };
12
+
13
+ export default meta;
14
+ type Story = StoryObj<typeof meta>;
15
+
16
+ export const Default: Story = {
17
+ render: () => <SoundToggle />,
18
+ };
19
+
20
+ export const InContext: Story = {
21
+ render: () => (
22
+ <div className="flex items-center gap-4 rounded-lg border p-4">
23
+ <span className="text-muted-foreground text-sm">Toggle sound:</span>
24
+ <SoundToggle />
25
+ </div>
26
+ ),
27
+ };
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+
3
+ import { Volume2, VolumeOff } from 'lucide-react';
4
+ import { Button } from '@/components/ui/button';
5
+ import { useSoundEnabled } from '@/hooks/use-sound-enabled';
6
+
7
+ export function SoundToggle() {
8
+ const { enabled, toggle } = useSoundEnabled();
9
+
10
+ return (
11
+ <Button
12
+ variant="ghost"
13
+ size="icon"
14
+ onClick={toggle}
15
+ aria-label={enabled ? 'Mute sounds' : 'Unmute sounds'}
16
+ >
17
+ {enabled ? <Volume2 className="h-5 w-5" /> : <VolumeOff className="h-5 w-5" />}
18
+ <span className="sr-only">Toggle sound</span>
19
+ </Button>
20
+ );
21
+ }
@@ -25,6 +25,7 @@ import type { PrdQuestionnaireData } from '@/components/common/prd-questionnaire
25
25
  import { TechDecisionsDrawer } from '@/components/common/tech-decisions-review';
26
26
  import { MergeReviewDrawer } from '@/components/common/merge-review';
27
27
  import { NotificationPermissionBanner } from '@/components/common/notification-permission-banner';
28
+ import { useSound } from '@/hooks/use-sound';
28
29
  import { ControlCenterEmptyState } from './control-center-empty-state';
29
30
  import { useControlCenterState } from './use-control-center-state';
30
31
 
@@ -53,6 +54,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
53
54
  handleDeleteFeature,
54
55
  handleDeleteRepository,
55
56
  closeCreateDrawer,
57
+ selectFeatureById,
56
58
  pendingParentFeatureId,
57
59
  } = useControlCenterState(initialNodes, initialEdges);
58
60
 
@@ -86,6 +88,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
86
88
 
87
89
  // Reject state (shared by both drawers)
88
90
  const [isRejecting, setIsRejecting] = useState(false);
91
+ const rejectSound = useSound('caution', { volume: 0.5 });
89
92
 
90
93
  // Tech decisions drawer state
91
94
  const [techDecisionsData, setTechDecisionsData] = useState<TechDecisionsReviewData | null>(null);
@@ -158,6 +161,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
158
161
  return;
159
162
  }
160
163
 
164
+ rejectSound.play();
161
165
  toast.success(`${label} rejected — agent re-iterating (iteration ${result.iteration})`);
162
166
  if (result.iterationWarning) {
163
167
  toast.warning(
@@ -170,7 +174,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
170
174
  setIsRejecting(false);
171
175
  }
172
176
  },
173
- [selectedNode?.featureId, clearSelection]
177
+ [selectedNode?.featureId, clearSelection, rejectSound]
174
178
  );
175
179
 
176
180
  const handlePrdReject = useCallback(
@@ -325,6 +329,16 @@ export function ControlCenterInner({ initialNodes, initialEdges }: ControlCenter
325
329
  return () => window.removeEventListener('shep:open-create-drawer', handler);
326
330
  }, [handleAddFeature]);
327
331
 
332
+ // Listen for notification "Review" clicks to open the relevant drawer
333
+ useEffect(() => {
334
+ const handler = (e: Event) => {
335
+ const featureId = (e as CustomEvent<{ featureId: string }>).detail.featureId;
336
+ selectFeatureById(featureId);
337
+ };
338
+ window.addEventListener('shep:select-feature', handler);
339
+ return () => window.removeEventListener('shep:select-feature', handler);
340
+ }, [selectFeatureById]);
341
+
328
342
  if (!hasRepositories) {
329
343
  return (
330
344
  <>