@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,69 +2,241 @@
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
- import { getNotificationBus } from '../../../../../../packages/core/src/infrastructure/services/notifications/notification-bus.js';
16
+ import { resolve } from '../../../lib/server-container.js';
17
+ import { AgentRunStatus, SdlcLifecycle, NotificationEventType, NotificationSeverity, } from '../../../../../../packages/core/src/domain/generated/output.js';
18
+ // Force dynamic — SSE streams must never be statically optimized or cached
19
+ export const dynamic = 'force-dynamic';
20
+ const POLL_INTERVAL_MS = 500;
14
21
  const HEARTBEAT_INTERVAL_MS = 30_000;
15
- function formatSSEEvent(event) {
16
- return `event: notification\ndata: ${JSON.stringify(event)}\n\n`;
17
- }
18
- function formatHeartbeat() {
19
- return ': heartbeat\n\n';
22
+ /**
23
+ * Maps SdlcLifecycle values to agent graph node names so the client
24
+ * can derive the correct FeatureLifecyclePhase via mapPhaseNameToLifecycle().
25
+ */
26
+ const LIFECYCLE_TO_NODE = {
27
+ [SdlcLifecycle.Analyze]: 'analyze',
28
+ [SdlcLifecycle.Requirements]: 'requirements',
29
+ [SdlcLifecycle.Research]: 'research',
30
+ [SdlcLifecycle.Planning]: 'plan',
31
+ [SdlcLifecycle.Implementation]: 'implement',
32
+ [SdlcLifecycle.Review]: 'merge',
33
+ };
34
+ const STATUS_TO_EVENT = {
35
+ [AgentRunStatus.running]: {
36
+ eventType: NotificationEventType.AgentStarted,
37
+ severity: NotificationSeverity.Info,
38
+ },
39
+ [AgentRunStatus.waitingApproval]: {
40
+ eventType: NotificationEventType.WaitingApproval,
41
+ severity: NotificationSeverity.Warning,
42
+ },
43
+ [AgentRunStatus.completed]: {
44
+ eventType: NotificationEventType.AgentCompleted,
45
+ severity: NotificationSeverity.Success,
46
+ },
47
+ [AgentRunStatus.failed]: {
48
+ eventType: NotificationEventType.AgentFailed,
49
+ severity: NotificationSeverity.Error,
50
+ },
51
+ [AgentRunStatus.interrupted]: {
52
+ eventType: NotificationEventType.AgentFailed,
53
+ severity: NotificationSeverity.Warning,
54
+ },
55
+ [AgentRunStatus.cancelled]: {
56
+ eventType: NotificationEventType.AgentFailed,
57
+ severity: NotificationSeverity.Warning,
58
+ },
59
+ };
60
+ /** Map agent graph node name from AgentRun.result to a phase name. */
61
+ function resultToPhase(result) {
62
+ if (!result?.startsWith('node:'))
63
+ return undefined;
64
+ return result.slice(5); // "node:analyze" → "analyze"
20
65
  }
21
66
  export function GET(request) {
22
- const url = new URL(request.url);
23
- const runIdFilter = url.searchParams.get('runId');
24
- const stream = new ReadableStream({
25
- start(controller) {
26
- const encoder = new TextEncoder();
27
- const bus = getNotificationBus();
28
- const onNotification = (event) => {
29
- if (runIdFilter && event.agentRunId !== runIdFilter) {
30
- return;
31
- }
32
- try {
33
- controller.enqueue(encoder.encode(formatSSEEvent(event)));
34
- }
35
- catch {
36
- // Stream may be closed — ignore enqueue errors
37
- }
38
- };
39
- bus.on('notification', onNotification);
40
- // Heartbeat to keep connection alive
41
- const heartbeatInterval = setInterval(() => {
42
- try {
43
- controller.enqueue(encoder.encode(formatHeartbeat()));
44
- }
45
- catch {
46
- // Stream may be closed — ignore enqueue errors
67
+ try {
68
+ const url = new URL(request.url);
69
+ const runIdFilter = url.searchParams.get('runId');
70
+ const stream = new ReadableStream({
71
+ start(controller) {
72
+ const encoder = new TextEncoder();
73
+ // Per-connection cache: featureId last-seen state
74
+ const cache = new Map();
75
+ let stopped = false;
76
+ function enqueue(text) {
77
+ if (stopped)
78
+ return;
79
+ try {
80
+ controller.enqueue(encoder.encode(text));
81
+ }
82
+ catch {
83
+ // Stream may be closed
84
+ }
47
85
  }
48
- }, HEARTBEAT_INTERVAL_MS);
49
- // Cleanup on client disconnect
50
- const cleanup = () => {
51
- bus.removeListener('notification', onNotification);
52
- clearInterval(heartbeatInterval);
53
- try {
54
- controller.close();
86
+ function emitEvent(event) {
87
+ // eslint-disable-next-line no-console
88
+ console.log(`[SSE] emit: ${event.eventType} for "${event.featureName}"${event.phaseName ? ` (${event.phaseName})` : ''}`);
89
+ enqueue(`event: notification\ndata: ${JSON.stringify(event)}\n\n`);
55
90
  }
56
- catch {
57
- // Stream may already be closed
91
+ let pollErrorCount = 0;
92
+ async function poll() {
93
+ if (stopped)
94
+ return;
95
+ try {
96
+ const listFeatures = resolve('ListFeaturesUseCase');
97
+ const agentRunRepo = resolve('IAgentRunRepository');
98
+ const phaseTimingRepo = resolve('IPhaseTimingRepository');
99
+ const features = await listFeatures.execute();
100
+ // Build current state for features with agent runs
101
+ const entries = await Promise.all(features.map(async (feature) => {
102
+ const run = feature.agentRunId
103
+ ? await agentRunRepo.findById(feature.agentRunId)
104
+ : null;
105
+ return { feature, run };
106
+ }));
107
+ for (const { feature, run } of entries) {
108
+ if (!run)
109
+ continue;
110
+ // Apply runId filter if present
111
+ if (runIdFilter && run.id !== runIdFilter)
112
+ continue;
113
+ const prev = cache.get(feature.id);
114
+ if (!prev) {
115
+ // First time seeing this feature — seed cache, don't emit
116
+ const completedPhases = new Set();
117
+ try {
118
+ const timings = await phaseTimingRepo.findByRunId(run.id);
119
+ for (const t of timings) {
120
+ if (t.completedAt)
121
+ completedPhases.add(t.phase);
122
+ }
123
+ }
124
+ catch {
125
+ // Ignore timing errors
126
+ }
127
+ cache.set(feature.id, {
128
+ status: run.status,
129
+ lifecycle: feature.lifecycle,
130
+ completedPhases,
131
+ featureName: feature.name,
132
+ });
133
+ continue;
134
+ }
135
+ // Check for status change
136
+ if (prev.status !== run.status) {
137
+ prev.status = run.status;
138
+ const mapping = STATUS_TO_EVENT[run.status];
139
+ if (mapping) {
140
+ const phase = resultToPhase(run.result);
141
+ emitEvent({
142
+ eventType: mapping.eventType,
143
+ agentRunId: run.id,
144
+ featureId: feature.id,
145
+ featureName: feature.name,
146
+ ...(phase && { phaseName: phase }),
147
+ message: `Agent status: ${run.status}`,
148
+ severity: mapping.severity,
149
+ timestamp: new Date().toISOString(),
150
+ });
151
+ }
152
+ }
153
+ // Check for lifecycle change (agent stays "running" but moves through phases)
154
+ if (prev.lifecycle !== feature.lifecycle) {
155
+ prev.lifecycle = feature.lifecycle;
156
+ const nodeName = LIFECYCLE_TO_NODE[feature.lifecycle];
157
+ if (nodeName) {
158
+ emitEvent({
159
+ eventType: NotificationEventType.PhaseCompleted,
160
+ agentRunId: run.id,
161
+ featureId: feature.id,
162
+ featureName: feature.name,
163
+ phaseName: nodeName,
164
+ message: `Entered ${nodeName} phase`,
165
+ severity: NotificationSeverity.Info,
166
+ timestamp: new Date().toISOString(),
167
+ });
168
+ }
169
+ }
170
+ // Check for new phase completions
171
+ try {
172
+ const timings = await phaseTimingRepo.findByRunId(run.id);
173
+ for (const t of timings) {
174
+ if (t.completedAt && !prev.completedPhases.has(t.phase)) {
175
+ prev.completedPhases.add(t.phase);
176
+ emitEvent({
177
+ eventType: NotificationEventType.PhaseCompleted,
178
+ agentRunId: run.id,
179
+ featureId: feature.id,
180
+ featureName: feature.name,
181
+ phaseName: t.phase,
182
+ message: `Completed ${t.phase} phase`,
183
+ severity: NotificationSeverity.Info,
184
+ timestamp: new Date().toISOString(),
185
+ });
186
+ }
187
+ }
188
+ }
189
+ catch {
190
+ // Ignore timing errors
191
+ }
192
+ }
193
+ pollErrorCount = 0; // Reset on success
194
+ }
195
+ catch (error) {
196
+ pollErrorCount++;
197
+ // Log first few errors, then throttle to avoid spamming
198
+ if (pollErrorCount <= 3 || pollErrorCount % 60 === 0) {
199
+ // eslint-disable-next-line no-console
200
+ console.error(`[SSE /api/agent-events] poll error #${pollErrorCount}:`, error instanceof Error ? error.message : error);
201
+ }
202
+ }
58
203
  }
59
- };
60
- request.signal.addEventListener('abort', cleanup, { once: true });
61
- },
62
- });
63
- return new Response(stream, {
64
- headers: {
65
- 'Content-Type': 'text/event-stream',
66
- 'Cache-Control': 'no-cache',
67
- Connection: 'keep-alive',
68
- },
69
- });
204
+ // First poll immediately, then every POLL_INTERVAL_MS
205
+ void poll();
206
+ const pollInterval = setInterval(() => void poll(), POLL_INTERVAL_MS);
207
+ // Heartbeat to keep connection alive
208
+ const heartbeatInterval = setInterval(() => {
209
+ enqueue(': heartbeat\n\n');
210
+ }, HEARTBEAT_INTERVAL_MS);
211
+ // Cleanup on client disconnect
212
+ const cleanup = () => {
213
+ stopped = true;
214
+ clearInterval(pollInterval);
215
+ clearInterval(heartbeatInterval);
216
+ try {
217
+ controller.close();
218
+ }
219
+ catch {
220
+ // Stream may already be closed
221
+ }
222
+ };
223
+ request.signal.addEventListener('abort', cleanup, { once: true });
224
+ },
225
+ });
226
+ return new Response(stream, {
227
+ headers: {
228
+ 'Content-Type': 'text/event-stream',
229
+ 'Cache-Control': 'no-cache',
230
+ Connection: 'keep-alive',
231
+ },
232
+ });
233
+ }
234
+ catch (error) {
235
+ // eslint-disable-next-line no-console
236
+ console.error('[SSE route] GET handler error:', error);
237
+ return new Response(JSON.stringify({ error: String(error) }), {
238
+ status: 500,
239
+ headers: { 'Content-Type': 'application/json' },
240
+ });
241
+ }
70
242
  }
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,eAAe,CAAC;AAIvB,sFAAsF;AACtF,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAEvC,eAAO,MAAM,QAAQ,EAAE,QAItB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EACjC,QAAQ,GACT,EAAE,QAAQ,CAAC;IACV,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC,2CA0BD"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/app/layout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,6BAA6B,CAAC;AACrC,OAAO,eAAe,CAAC;AAIvB,sFAAsF;AACtF,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAEvC,eAAO,MAAM,QAAQ,EAAE,QAkBtB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EACjC,QAAQ,GACT,EAAE,QAAQ,CAAC;IACV,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC,2CA0BD"}
@@ -8,6 +8,20 @@ export const dynamic = 'force-dynamic';
8
8
  export const metadata = {
9
9
  title: 'Shep AI',
10
10
  description: 'Autonomous AI Native SDLC Platform - Automate the development cycle from idea to deploy',
11
+ icons: [
12
+ {
13
+ rel: 'icon',
14
+ url: '/favicon-light.svg',
15
+ type: 'image/svg+xml',
16
+ media: '(prefers-color-scheme: light)',
17
+ },
18
+ {
19
+ rel: 'icon',
20
+ url: '/favicon-dark.svg',
21
+ type: 'image/svg+xml',
22
+ media: '(prefers-color-scheme: dark)',
23
+ },
24
+ ],
11
25
  };
12
26
  export default function RootLayout({ children, }) {
13
27
  return (_jsxs("html", { lang: "en", suppressHydrationWarning: true, children: [_jsx("head", { children: _jsx("script", { dangerouslySetInnerHTML: {
@@ -4,8 +4,9 @@
4
4
  * Derives UI node state and progress from the Feature domain model
5
5
  * and its associated AgentRun status (mirrors CLI feat ls logic).
6
6
  */
7
+ import { NotificationEventType } from '../../../../../../packages/core/src/domain/generated/index.js';
7
8
  import type { Feature, AgentRun } from '../../../../../../packages/core/src/domain/generated/index.js';
8
- import type { FeatureNodeState } from './feature-node-state-config.js';
9
+ import type { FeatureNodeState, FeatureLifecyclePhase } from './feature-node-state-config.js';
9
10
  /**
10
11
  * Derives the visual node state from a Feature and its optional AgentRun.
11
12
  *
@@ -25,4 +26,7 @@ export declare function deriveNodeState(feature: Feature, agentRun?: AgentRun |
25
26
  * Returns 100 for Maintain lifecycle, 0 if no plan.
26
27
  */
27
28
  export declare function deriveProgress(feature: Feature): number;
29
+ /** Maps a NotificationEventType to the corresponding FeatureNodeState for optimistic UI updates. */
30
+ export declare function mapEventTypeToState(eventType: NotificationEventType): FeatureNodeState;
31
+ export declare function mapPhaseNameToLifecycle(phaseName: string | undefined): FeatureLifecyclePhase | undefined;
28
32
  //# sourceMappingURL=derive-feature-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"derive-feature-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/derive-feature-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,gBAAgB,CA4C9F;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAYvD"}
1
+ {"version":3,"file":"derive-feature-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/derive-feature-state.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAIL,qBAAqB,EACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAE3F;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,gBAAgB,CA4C9F;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAYvD;AAED,oGAAoG;AACpG,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,qBAAqB,GAAG,gBAAgB,CAYtF;AAYD,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,qBAAqB,GAAG,SAAS,CAGnC"}
@@ -4,7 +4,7 @@
4
4
  * Derives UI node state and progress from the Feature domain model
5
5
  * and its associated AgentRun status (mirrors CLI feat ls logic).
6
6
  */
7
- import { SdlcLifecycle, AgentRunStatus, TaskState } from '../../../../../../packages/core/src/domain/generated/index.js';
7
+ import { SdlcLifecycle, AgentRunStatus, TaskState, NotificationEventType, } from '../../../../../../packages/core/src/domain/generated/index.js';
8
8
  /**
9
9
  * Derives the visual node state from a Feature and its optional AgentRun.
10
10
  *
@@ -73,3 +73,31 @@ export function deriveProgress(feature) {
73
73
  const doneCount = tasks.filter((t) => t.state === TaskState.Done).length;
74
74
  return Math.round((doneCount / tasks.length) * 100);
75
75
  }
76
+ /** Maps a NotificationEventType to the corresponding FeatureNodeState for optimistic UI updates. */
77
+ export function mapEventTypeToState(eventType) {
78
+ switch (eventType) {
79
+ case NotificationEventType.AgentStarted:
80
+ case NotificationEventType.PhaseCompleted:
81
+ return 'running';
82
+ case NotificationEventType.WaitingApproval:
83
+ return 'action-required';
84
+ case NotificationEventType.AgentCompleted:
85
+ return 'done';
86
+ case NotificationEventType.AgentFailed:
87
+ return 'error';
88
+ }
89
+ }
90
+ /** Maps an SSE event phaseName to a FeatureLifecyclePhase. Mirrors page.tsx nodeToLifecyclePhase. */
91
+ const phaseNameToLifecycle = {
92
+ analyze: 'requirements',
93
+ requirements: 'requirements',
94
+ research: 'research',
95
+ plan: 'implementation',
96
+ implement: 'implementation',
97
+ merge: 'review',
98
+ };
99
+ export function mapPhaseNameToLifecycle(phaseName) {
100
+ if (!phaseName)
101
+ return undefined;
102
+ return phaseNameToLifecycle[phaseName];
103
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"repository-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/repository-node.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGnE,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,2CA0L5F"}
1
+ {"version":3,"file":"repository-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/repository-node/repository-node.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGnE,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,2CA2L5F"}
@@ -14,7 +14,7 @@ export function RepositoryNode({ data }) {
14
14
  return (_jsxs("div", { className: "group relative", children: [data.showHandles ? (_jsx(Handle, { type: "target", position: Position.Left, isConnectable: false, className: "opacity-0!" })) : null, data.onDelete && data.id ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute top-1/2 -left-10 -translate-y-1/2 opacity-0 transition-opacity group-hover:opacity-100", children: _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { "aria-label": "Remove repository", "data-testid": "repository-node-delete-button", onClick: (e) => {
15
15
  e.stopPropagation();
16
16
  setConfirmOpen(true);
17
- }, className: "bg-card text-muted-foreground hover:border-destructive hover:text-destructive flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border shadow-sm transition-colors", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Remove repository" })] }) }) }), _jsx(AlertDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, children: _jsxs(AlertDialogContent, { size: "sm", children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Remove repository?" }), _jsxs(AlertDialogDescription, { children: ["This will remove ", _jsx("strong", { children: data.name }), " from your workspace. The repository files on disk won't be affected."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Cancel" }), _jsx(AlertDialogAction, { variant: "destructive", onClick: () => data.onDelete?.(data.id), children: "Remove" })] })] }) })] })) : null, _jsxs("div", { role: "button", tabIndex: 0, "data-testid": "repository-node-card", onClick: (e) => {
17
+ }, className: "bg-card text-muted-foreground hover:border-destructive hover:text-destructive flex h-7 w-7 cursor-pointer items-center justify-center rounded-full border shadow-sm transition-colors", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { children: "Remove repository" })] }) }) }), _jsx(AlertDialog, { open: confirmOpen, onOpenChange: setConfirmOpen, children: _jsxs(AlertDialogContent, { size: "sm", children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Remove repository?" }), _jsxs(AlertDialogDescription, { children: ["This will remove ", _jsx("strong", { children: data.name }), " from your workspace. The repository files on disk won't be affected."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Cancel" }), _jsx(AlertDialogAction, { variant: "destructive", onClick: () => data.onDelete?.(data.id), children: "Remove" })] })] }) })] })) : null, _jsxs("div", { role: "button", tabIndex: 0, "data-testid": "repository-node-card", "data-repo-name": data.name, onClick: (e) => {
18
18
  e.stopPropagation();
19
19
  data.onClick?.();
20
20
  }, onKeyDown: (e) => {
@@ -0,0 +1,2 @@
1
+ export { SoundToggle } from './sound-toggle.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/sound-toggle/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1 @@
1
+ export { SoundToggle } from './sound-toggle.js';
@@ -0,0 +1,2 @@
1
+ export declare function SoundToggle(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=sound-toggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sound-toggle.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/sound-toggle/sound-toggle.tsx"],"names":[],"mappings":"AAMA,wBAAgB,WAAW,4CAc1B"}
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Volume2, VolumeOff } from 'lucide-react';
4
+ import { Button } from '../../ui/button.js';
5
+ import { useSoundEnabled } from '../../../hooks/use-sound-enabled.js';
6
+ export function SoundToggle() {
7
+ const { enabled, toggle } = useSoundEnabled();
8
+ return (_jsxs(Button, { variant: "ghost", size: "icon", onClick: toggle, "aria-label": enabled ? 'Mute sounds' : 'Unmute sounds', children: [enabled ? _jsx(Volume2, { className: "h-5 w-5" }) : _jsx(VolumeOff, { className: "h-5 w-5" }), _jsx("span", { className: "sr-only", children: "Toggle sound" })] }));
9
+ }
@@ -0,0 +1,8 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { SoundToggle } from './sound-toggle.js';
3
+ declare const meta: Meta<typeof SoundToggle>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof meta>;
6
+ export declare const Default: Story;
7
+ export declare const InContext: Story;
8
+ //# sourceMappingURL=sound-toggle.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sound-toggle.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/sound-toggle/sound-toggle.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,WAAW,CAOlC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAOvB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { SoundToggle } from './sound-toggle.js';
3
+ const meta = {
4
+ title: 'Composed/SoundToggle',
5
+ component: SoundToggle,
6
+ parameters: {
7
+ layout: 'centered',
8
+ },
9
+ tags: ['autodocs'],
10
+ };
11
+ export default meta;
12
+ export const Default = {
13
+ render: () => _jsx(SoundToggle, {}),
14
+ };
15
+ export const InContext = {
16
+ render: () => (_jsxs("div", { className: "flex items-center gap-4 rounded-lg border p-4", children: [_jsx("span", { className: "text-muted-foreground text-sm", children: "Toggle sound:" }), _jsx(SoundToggle, {})] })),
17
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAgB1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAW5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAmZzF"}
1
+ {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAgB1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAY5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAgazF"}
@@ -14,10 +14,11 @@ import { PrdQuestionnaireDrawer } from '../../common/prd-questionnaire/index.js'
14
14
  import { TechDecisionsDrawer } from '../../common/tech-decisions-review/index.js';
15
15
  import { MergeReviewDrawer } from '../../common/merge-review/index.js';
16
16
  import { NotificationPermissionBanner } from '../../common/notification-permission-banner/index.js';
17
+ import { useSound } from '../../../hooks/use-sound.js';
17
18
  import { ControlCenterEmptyState } from './control-center-empty-state.js';
18
19
  import { useControlCenterState } from './use-control-center-state.js';
19
20
  export function ControlCenterInner({ initialNodes, initialEdges }) {
20
- const { nodes, edges, selectedNode, isCreateDrawerOpen, isDeleting, pendingRepositoryPath, onNodesChange, handleConnect, handleAddFeature, handleAddFeatureToRepo, handleAddFeatureToFeature, handleAddRepository, handleNodeClick, clearSelection, handleCreateFeatureSubmit, handleDeleteFeature, handleDeleteRepository, closeCreateDrawer, pendingParentFeatureId, } = useControlCenterState(initialNodes, initialEdges);
21
+ const { nodes, edges, selectedNode, isCreateDrawerOpen, isDeleting, pendingRepositoryPath, onNodesChange, handleConnect, handleAddFeature, handleAddFeatureToRepo, handleAddFeatureToFeature, handleAddRepository, handleNodeClick, clearSelection, handleCreateFeatureSubmit, handleDeleteFeature, handleDeleteRepository, closeCreateDrawer, selectFeatureById, pendingParentFeatureId, } = useControlCenterState(initialNodes, initialEdges);
21
22
  // Extract feature list for the parent selector in the create drawer
22
23
  const featureOptions = useMemo(() => nodes
23
24
  .filter((n) => n.type === 'featureNode')
@@ -42,6 +43,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
42
43
  const [isLoadingQuestionnaire, setIsLoadingQuestionnaire] = useState(false);
43
44
  // Reject state (shared by both drawers)
44
45
  const [isRejecting, setIsRejecting] = useState(false);
46
+ const rejectSound = useSound('caution', { volume: 0.5 });
45
47
  // Tech decisions drawer state
46
48
  const [techDecisionsData, setTechDecisionsData] = useState(null);
47
49
  const [isLoadingTechDecisions, setIsLoadingTechDecisions] = useState(false);
@@ -94,6 +96,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
94
96
  toast.error(result.error ?? `Failed to reject ${label.toLowerCase()}`);
95
97
  return;
96
98
  }
99
+ rejectSound.play();
97
100
  toast.success(`${label} rejected — agent re-iterating (iteration ${result.iteration})`);
98
101
  if (result.iterationWarning) {
99
102
  toast.warning(`Iteration ${result.iteration} — consider approving or adjusting feedback to avoid excessive iterations`);
@@ -104,7 +107,7 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
104
107
  finally {
105
108
  setIsRejecting(false);
106
109
  }
107
- }, [selectedNode?.featureId, clearSelection]);
110
+ }, [selectedNode?.featureId, clearSelection, rejectSound]);
108
111
  const handlePrdReject = useCallback((feedback) => handleReject(feedback, 'Requirements', () => setPrdSelections({})), [handleReject]);
109
112
  const handleTechDecisionsReject = useCallback((feedback) => handleReject(feedback, 'Plan'), [handleReject]);
110
113
  const handleMergeReject = useCallback((feedback) => handleReject(feedback, 'Merge'), [handleReject]);
@@ -233,6 +236,15 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
233
236
  window.addEventListener('shep:open-create-drawer', handler);
234
237
  return () => window.removeEventListener('shep:open-create-drawer', handler);
235
238
  }, [handleAddFeature]);
239
+ // Listen for notification "Review" clicks to open the relevant drawer
240
+ useEffect(() => {
241
+ const handler = (e) => {
242
+ const featureId = e.detail.featureId;
243
+ selectFeatureById(featureId);
244
+ };
245
+ window.addEventListener('shep:select-feature', handler);
246
+ return () => window.removeEventListener('shep:select-feature', handler);
247
+ }, [selectFeatureById]);
236
248
  if (!hasRepositories) {
237
249
  return (_jsxs(_Fragment, { children: [_jsx(NotificationPermissionBanner, {}), _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }), _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath, workflowDefaults: workflowDefaults, features: featureOptions, initialParentId: pendingParentFeatureId })] }));
238
250
  }
@@ -26,6 +26,7 @@ export interface ControlCenterState {
26
26
  handleDeleteRepository: (repositoryId: string) => Promise<void>;
27
27
  isDeleting: boolean;
28
28
  createFeatureNode: (sourceNodeId: string | null, dataOverride?: Partial<FeatureNodeData>) => string;
29
+ selectFeatureById: (featureId: string) => void;
29
30
  }
30
31
  export declare function useControlCenterState(initialNodes: CanvasNodeType[], initialEdges: Edge[]): ControlCenterState;
31
32
  //# sourceMappingURL=use-control-center-state.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAOhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,4EAA4E;IAC5E,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,yBAAyB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,yBAAyB,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChE,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,KACpC,MAAM,CAAC;CACb;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAmjBpB"}
1
+ {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAYhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,4EAA4E;IAC5E,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3C,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,yBAAyB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,yBAAyB,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChE,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,KACpC,MAAM,CAAC;IACZ,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAChD;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAgtBpB"}