@shepai/cli 1.148.0 → 1.149.0-pr464.a84e893

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 (199) hide show
  1. package/dist/src/presentation/web/app/api/agent-events/route.js +1 -1
  2. package/dist/src/presentation/web/app/api/sessions/route.d.ts.map +1 -1
  3. package/dist/src/presentation/web/app/api/sessions/route.js +2 -268
  4. package/dist/src/presentation/web/app/api/sessions-batch/route.d.ts +17 -0
  5. package/dist/src/presentation/web/app/api/sessions-batch/route.d.ts.map +1 -0
  6. package/dist/src/presentation/web/app/api/sessions-batch/route.js +61 -0
  7. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  8. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +18 -2
  9. package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.d.ts +1 -1
  10. package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.d.ts.map +1 -1
  11. package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.js +15 -73
  12. package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.stories.d.ts.map +1 -1
  13. package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.stories.js +18 -17
  14. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  15. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +18 -0
  16. package/dist/src/presentation/web/components/features/control-center/control-center.d.ts.map +1 -1
  17. package/dist/src/presentation/web/components/features/control-center/control-center.js +2 -1
  18. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  19. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +4 -1
  20. package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.d.ts.map +1 -1
  21. package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.js +32 -33
  22. package/dist/src/presentation/web/hooks/sessions-provider.d.ts +12 -0
  23. package/dist/src/presentation/web/hooks/sessions-provider.d.ts.map +1 -0
  24. package/dist/src/presentation/web/hooks/sessions-provider.js +57 -0
  25. package/dist/src/presentation/web/hooks/use-deploy-action.d.ts.map +1 -1
  26. package/dist/src/presentation/web/hooks/use-deploy-action.js +8 -54
  27. package/dist/src/presentation/web/lib/session-scanner.d.ts +27 -0
  28. package/dist/src/presentation/web/lib/session-scanner.d.ts.map +1 -0
  29. package/dist/src/presentation/web/lib/session-scanner.js +255 -0
  30. package/dist/tsconfig.build.tsbuildinfo +1 -1
  31. package/package.json +1 -1
  32. package/web/.next/BUILD_ID +1 -1
  33. package/web/.next/app-path-routes-manifest.json +1 -0
  34. package/web/.next/build-manifest.json +2 -2
  35. package/web/.next/fallback-build-manifest.json +2 -2
  36. package/web/.next/prerender-manifest.json +3 -3
  37. package/web/.next/required-server-files.js +3 -3
  38. package/web/.next/required-server-files.json +3 -3
  39. package/web/.next/routes-manifest.json +6 -0
  40. package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +28 -28
  41. package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
  42. package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
  43. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +28 -28
  44. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  45. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  46. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +36 -36
  47. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  48. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  49. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +36 -36
  50. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  51. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  52. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +26 -26
  53. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  54. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  55. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +28 -28
  56. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  57. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  58. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +36 -36
  59. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  60. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  61. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +36 -36
  62. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  63. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  64. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +26 -26
  65. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  66. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  67. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +26 -26
  68. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  69. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  70. package/web/.next/server/app/_global-error.html +2 -2
  71. package/web/.next/server/app/_global-error.rsc +1 -1
  72. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  73. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  74. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  75. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  76. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  77. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +3 -3
  78. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  79. package/web/.next/server/app/api/attachments/preview/route.js.nft.json +1 -1
  80. package/web/.next/server/app/api/evidence/route.js.nft.json +1 -1
  81. package/web/.next/server/app/api/graph-data/route.js.nft.json +1 -1
  82. package/web/.next/server/app/api/sessions/route.js +2 -3
  83. package/web/.next/server/app/api/sessions/route.js.nft.json +1 -1
  84. package/web/.next/server/app/api/sessions-batch/route/app-paths-manifest.json +3 -0
  85. package/web/.next/server/app/api/sessions-batch/route/build-manifest.json +11 -0
  86. package/web/.next/server/app/api/sessions-batch/route/server-reference-manifest.json +4 -0
  87. package/web/.next/server/app/api/sessions-batch/route.js +7 -0
  88. package/web/.next/server/app/api/sessions-batch/route.js.map +5 -0
  89. package/web/.next/server/app/api/sessions-batch/route.js.nft.json +1 -0
  90. package/web/.next/server/app/api/sessions-batch/route_client-reference-manifest.js +2 -0
  91. package/web/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  92. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  93. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  94. package/web/.next/server/app/skills/page/server-reference-manifest.json +8 -8
  95. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  96. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  97. package/web/.next/server/app/tools/page/server-reference-manifest.json +8 -8
  98. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  99. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  100. package/web/.next/server/app/version/page/server-reference-manifest.json +3 -3
  101. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  102. package/web/.next/server/app-paths-manifest.json +1 -0
  103. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +1 -1
  104. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -1
  105. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_sessions-batch_route_actions_4859f283.js +3 -0
  106. package/web/.next/server/chunks/[root-of-the-server]__0d33c29e._.js +3 -0
  107. package/web/.next/server/chunks/[root-of-the-server]__0d33c29e._.js.map +1 -0
  108. package/web/.next/server/chunks/[root-of-the-server]__2f61738a._.js +3 -0
  109. package/web/.next/server/chunks/[root-of-the-server]__2f61738a._.js.map +1 -0
  110. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  111. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  112. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  113. package/web/.next/server/chunks/ssr/[root-of-the-server]__2138fa7e._.js +2 -2
  114. package/web/.next/server/chunks/ssr/[root-of-the-server]__29580090._.js +1 -1
  115. package/web/.next/server/chunks/ssr/[root-of-the-server]__29580090._.js.map +1 -1
  116. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  117. package/web/.next/server/chunks/ssr/[root-of-the-server]__3ef34e4c._.js +1 -1
  118. package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js +1 -1
  119. package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js.map +1 -1
  120. package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js +1 -1
  121. package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js.map +1 -1
  122. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +3 -3
  123. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  124. package/web/.next/server/chunks/ssr/[root-of-the-server]__c094882b._.js +1 -1
  125. package/web/.next/server/chunks/ssr/[root-of-the-server]__c094882b._.js.map +1 -1
  126. package/web/.next/server/chunks/ssr/[root-of-the-server]__d48c5b11._.js +1 -1
  127. package/web/.next/server/chunks/ssr/[root-of-the-server]__d48c5b11._.js.map +1 -1
  128. package/web/.next/server/chunks/ssr/[root-of-the-server]__dac5dbf1._.js +1 -1
  129. package/web/.next/server/chunks/ssr/[root-of-the-server]__dac5dbf1._.js.map +1 -1
  130. package/web/.next/server/chunks/ssr/[root-of-the-server]__fae8b355._.js +1 -1
  131. package/web/.next/server/chunks/ssr/[root-of-the-server]__fae8b355._.js.map +1 -1
  132. package/web/.next/server/chunks/ssr/_05c23ad9._.js +1 -1
  133. package/web/.next/server/chunks/ssr/_05c23ad9._.js.map +1 -1
  134. package/web/.next/server/chunks/ssr/_0c5f56e3._.js +2 -2
  135. package/web/.next/server/chunks/ssr/_0c5f56e3._.js.map +1 -1
  136. package/web/.next/server/chunks/ssr/_16eb4fec._.js +1 -1
  137. package/web/.next/server/chunks/ssr/_16eb4fec._.js.map +1 -1
  138. package/web/.next/server/chunks/ssr/_1b719e7f._.js +1 -1
  139. package/web/.next/server/chunks/ssr/_1b719e7f._.js.map +1 -1
  140. package/web/.next/server/chunks/ssr/_37e8548b._.js +1 -1
  141. package/web/.next/server/chunks/ssr/_37e8548b._.js.map +1 -1
  142. package/web/.next/server/chunks/ssr/_55d763e2._.js +1 -1
  143. package/web/.next/server/chunks/ssr/_55d763e2._.js.map +1 -1
  144. package/web/.next/server/chunks/ssr/_6256a985._.js +1 -1
  145. package/web/.next/server/chunks/ssr/_6256a985._.js.map +1 -1
  146. package/web/.next/server/chunks/ssr/_64bdfc6f._.js +2 -2
  147. package/web/.next/server/chunks/ssr/_64bdfc6f._.js.map +1 -1
  148. package/web/.next/server/chunks/ssr/_8c875922._.js +4 -0
  149. package/web/.next/server/chunks/ssr/_8c875922._.js.map +1 -0
  150. package/web/.next/server/chunks/ssr/_8fcc39d4._.js +1 -1
  151. package/web/.next/server/chunks/ssr/_b06fea81._.js +3 -0
  152. package/web/.next/server/chunks/ssr/{_fe63a7f9._.js.map → _b06fea81._.js.map} +1 -1
  153. package/web/.next/server/chunks/ssr/_b71645b4._.js +1 -1
  154. package/web/.next/server/chunks/ssr/_b71645b4._.js.map +1 -1
  155. package/web/.next/server/chunks/ssr/_d8575088._.js +1 -1
  156. package/web/.next/server/chunks/ssr/_d8575088._.js.map +1 -1
  157. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  158. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  159. package/web/.next/server/chunks/ssr/{src_presentation_web_7b2fda40._.js → src_presentation_web_3095acd1._.js} +2 -2
  160. package/web/.next/server/chunks/ssr/{src_presentation_web_7b2fda40._.js.map → src_presentation_web_3095acd1._.js.map} +1 -1
  161. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js +1 -1
  162. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js.map +1 -1
  163. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js +1 -1
  164. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js.map +1 -1
  165. package/web/.next/server/chunks/ssr/src_presentation_web_app_actions_open-ide_ts_baaca5d5._.js +1 -1
  166. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  167. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  168. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  169. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  170. package/web/.next/server/pages/500.html +2 -2
  171. package/web/.next/server/server-reference-manifest.js +1 -1
  172. package/web/.next/server/server-reference-manifest.json +44 -44
  173. package/web/.next/static/chunks/{04869f1d3f5d9071.js → 1b1f15f424679455.js} +1 -1
  174. package/web/.next/static/chunks/{9dad6769d10a32df.js → 23c3e86ce0e24e00.js} +1 -1
  175. package/web/.next/static/chunks/36e2fd941864a90e.js +1 -0
  176. package/web/.next/static/chunks/{c731682077fbac4f.js → 3a4705f59cfd89a9.js} +1 -1
  177. package/web/.next/static/chunks/{6f76e63ead3fac2e.js → 51dee46741af1e4b.js} +1 -1
  178. package/web/.next/static/chunks/{48850e202dd814ac.js → 55683e8c2c1639b7.js} +1 -1
  179. package/web/.next/static/chunks/616c959907e661c9.js +1 -0
  180. package/web/.next/static/chunks/{21e82fee1a7e1668.js → 62501e23f82d4115.js} +1 -1
  181. package/web/.next/static/chunks/{7c5131e33516a325.js → 862d6fa007e83cb5.js} +1 -1
  182. package/web/.next/static/chunks/8e2039e569c81a6d.js +1 -0
  183. package/web/.next/static/chunks/a26e8c3319a6b723.js +5 -0
  184. package/web/.next/static/chunks/f946e2a33ee3b18e.js +2 -0
  185. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_ff60e4a5.js +0 -3
  186. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_ff60e4a5.js.map +0 -1
  187. package/web/.next/server/chunks/[externals]__448264a3._.js +0 -3
  188. package/web/.next/server/chunks/ssr/_4533d6f8._.js +0 -4
  189. package/web/.next/server/chunks/ssr/_4533d6f8._.js.map +0 -1
  190. package/web/.next/server/chunks/ssr/_fe63a7f9._.js +0 -3
  191. package/web/.next/static/chunks/0137d4850cab3c45.js +0 -5
  192. package/web/.next/static/chunks/063a24b49d9818a0.js +0 -1
  193. package/web/.next/static/chunks/682563e4503cbd58.js +0 -1
  194. package/web/.next/static/chunks/683b1d85e789c2eb.js +0 -2
  195. package/web/.next/static/chunks/d62ae5e449d87057.js +0 -1
  196. /package/web/.next/server/chunks/{[externals]__448264a3._.js.map → 744ca_web__next-internal_server_app_api_sessions-batch_route_actions_4859f283.js.map} +0 -0
  197. /package/web/.next/static/{zYKuE1zbe1UWwAJv5EVwg → QdGWt290JhTjWmj28lHyG}/_buildManifest.js +0 -0
  198. /package/web/.next/static/{zYKuE1zbe1UWwAJv5EVwg → QdGWt290JhTjWmj28lHyG}/_clientMiddlewareManifest.json +0 -0
  199. /package/web/.next/static/{zYKuE1zbe1UWwAJv5EVwg → QdGWt290JhTjWmj28lHyG}/_ssgManifest.js +0 -0
@@ -1,5 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { FeatureSessionsDropdown } from './feature-sessions-dropdown.js';
3
+ import { SessionsProvider } from '../../../hooks/sessions-provider.js';
4
+ const REPO_PATH = '/home/user/workspaces/my-project';
3
5
  const mockSessions = [
4
6
  {
5
7
  id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
@@ -8,7 +10,7 @@ const mockSessions = [
8
10
  firstMessageAt: new Date(Date.now() - 3_600_000).toISOString(),
9
11
  lastMessageAt: new Date(Date.now() - 1_800_000).toISOString(),
10
12
  createdAt: new Date(Date.now() - 3_600_000).toISOString(),
11
- projectPath: '~/workspaces/my-project',
13
+ projectPath: REPO_PATH,
12
14
  },
13
15
  {
14
16
  id: 'b2c3d4e5-f6a7-8901-bcde-f12345678901',
@@ -17,7 +19,7 @@ const mockSessions = [
17
19
  firstMessageAt: new Date(Date.now() - 86_400_000).toISOString(),
18
20
  lastMessageAt: new Date(Date.now() - 82_800_000).toISOString(),
19
21
  createdAt: new Date(Date.now() - 86_400_000).toISOString(),
20
- projectPath: '~/workspaces/my-project',
22
+ projectPath: REPO_PATH,
21
23
  },
22
24
  {
23
25
  id: 'c3d4e5f6-a7b8-9012-cdef-123456789012',
@@ -26,7 +28,7 @@ const mockSessions = [
26
28
  firstMessageAt: new Date(Date.now() - 172_800_000).toISOString(),
27
29
  lastMessageAt: new Date(Date.now() - 169_200_000).toISOString(),
28
30
  createdAt: new Date(Date.now() - 172_800_000).toISOString(),
29
- projectPath: '~/workspaces/my-project',
31
+ projectPath: REPO_PATH,
30
32
  },
31
33
  ];
32
34
  const mockActiveSession = {
@@ -36,42 +38,41 @@ const mockActiveSession = {
36
38
  firstMessageAt: new Date(Date.now() - 180_000).toISOString(),
37
39
  lastMessageAt: new Date(Date.now() - 60_000).toISOString(), // 1 min ago — active
38
40
  createdAt: new Date(Date.now() - 180_000).toISOString(),
39
- projectPath: '~/workspaces/my-project',
41
+ projectPath: REPO_PATH,
40
42
  };
41
- function createFetchMock(sessions) {
43
+ /**
44
+ * Mock fetch for /api/sessions-batch, then wrap with SessionsProvider.
45
+ */
46
+ function createSessionsMock(sessions) {
42
47
  return (Story) => {
43
48
  const originalFetch = window.fetch;
44
49
  window.fetch = (async (input, init) => {
45
50
  const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
46
- if (url.includes('/api/sessions')) {
47
- return new Response(JSON.stringify({ sessions }), {
51
+ if (url.includes('/api/sessions-batch')) {
52
+ return new Response(JSON.stringify({ sessionsByPath: sessions }), {
48
53
  status: 200,
49
54
  headers: { 'Content-Type': 'application/json' },
50
55
  });
51
56
  }
52
57
  return originalFetch(input, init);
53
58
  });
54
- return _jsx(Story, {});
59
+ return (_jsx(SessionsProvider, { children: _jsx(Story, {}) }));
55
60
  };
56
61
  }
57
62
  const meta = {
58
63
  title: 'Composed/FeatureSessionsDropdown',
59
64
  component: FeatureSessionsDropdown,
60
65
  tags: ['autodocs'],
61
- parameters: {
62
- layout: 'centered',
63
- },
64
- args: {
65
- repositoryPath: '/home/user/workspaces/my-project',
66
- },
66
+ parameters: { layout: 'centered' },
67
+ args: { repositoryPath: REPO_PATH },
67
68
  };
68
69
  export default meta;
69
70
  export const WithSessions = {
70
- decorators: [createFetchMock(mockSessions)],
71
+ decorators: [createSessionsMock({ [REPO_PATH]: mockSessions })],
71
72
  };
72
73
  export const WithActiveSessions = {
73
- decorators: [createFetchMock([mockActiveSession, ...mockSessions])],
74
+ decorators: [createSessionsMock({ [REPO_PATH]: [mockActiveSession, ...mockSessions] })],
74
75
  };
75
76
  export const Empty = {
76
- decorators: [createFetchMock([])],
77
+ decorators: [createSessionsMock({ [REPO_PATH]: [] })],
77
78
  };
@@ -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":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,eAAe,CAAC;AAKpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAwB5E,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,2CAsYzF"}
1
+ {"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,eAAe,CAAC;AAKpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAwB5E,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,2CA0ZzF"}
@@ -197,6 +197,24 @@ export function ControlCenterInner({ initialNodes, initialEdges }) {
197
197
  window.addEventListener('shep:feature-delete-requested', handler);
198
198
  return () => window.removeEventListener('shep:feature-delete-requested', handler);
199
199
  }, [handleDeleteFeature]);
200
+ // Listen for archive requests from the feature drawer.
201
+ useEffect(() => {
202
+ const handler = (e) => {
203
+ const { featureId } = e.detail;
204
+ handleArchiveFeature(featureId);
205
+ };
206
+ window.addEventListener('shep:feature-archive-requested', handler);
207
+ return () => window.removeEventListener('shep:feature-archive-requested', handler);
208
+ }, [handleArchiveFeature]);
209
+ // Listen for unarchive requests from the feature drawer.
210
+ useEffect(() => {
211
+ const handler = (e) => {
212
+ const { featureId } = e.detail;
213
+ handleUnarchiveFeature(featureId);
214
+ };
215
+ window.addEventListener('shep:feature-unarchive-requested', handler);
216
+ return () => window.removeEventListener('shep:feature-unarchive-requested', handler);
217
+ }, [handleUnarchiveFeature]);
200
218
  // Wire callbacks into derived node data (via ref — no re-render).
201
219
  useEffect(() => {
202
220
  setCallbacks({
@@ -1 +1 @@
1
- {"version":3,"file":"control-center.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAG5E,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;IACrB,kEAAkE;IAClE,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,kBAAkB,2CASvF"}
1
+ {"version":3,"file":"control-center.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAI5E,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;IACrB,kEAAkE;IAClE,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,kBAAkB,2CAWvF"}
@@ -1,7 +1,8 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { ReactFlowProvider } from '@xyflow/react';
4
+ import { SessionsProvider } from '../../../hooks/sessions-provider.js';
4
5
  import { ControlCenterInner } from './control-center-inner.js';
5
6
  export function ControlCenter({ initialNodes, initialEdges, drawer }) {
6
- return (_jsxs("div", { "data-testid": "control-center", className: "h-full w-full", children: [_jsx(ReactFlowProvider, { children: _jsx(ControlCenterInner, { initialNodes: initialNodes, initialEdges: initialEdges }) }), _jsx("div", { children: drawer }, "drawer")] }));
7
+ return (_jsxs("div", { "data-testid": "control-center", className: "h-full w-full", children: [_jsx(SessionsProvider, { children: _jsx(ReactFlowProvider, { children: _jsx(ControlCenterInner, { initialNodes: initialNodes, initialEdges: initialEdges }) }) }), _jsx("div", { children: drawer }, "drawer")] }));
7
8
  }
@@ -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":"AAKA,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,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAkBjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,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,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,mBAAmB,EAAE,CACnB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,EACjB,aAAa,CAAC,EAAE,OAAO,EACvB,OAAO,CAAC,EAAE,OAAO,KACd,IAAI,CAAC;IACV,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,sBAAsB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,yDAAyD;IACzD,YAAY,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA+kBpB"}
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":"AAKA,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,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAkBjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,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,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,mBAAmB,EAAE,CACnB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,EACjB,aAAa,CAAC,EAAE,OAAO,EACvB,OAAO,CAAC,EAAE,OAAO,KACd,IAAI,CAAC;IACV,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,sBAAsB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,yDAAyD;IACzD,YAAY,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAilBpB"}
@@ -18,7 +18,7 @@ import { createLogger } from '../../../lib/logger.js';
18
18
  import { mapEventTypeToState, resolveSseEventUpdates, } from '../../common/feature-node/derive-feature-state.js';
19
19
  import { useGraphState } from '../../../hooks/use-graph-state.js';
20
20
  const log = createLogger('[Polling]');
21
- const POLL_INTERVAL_MS = 3_000;
21
+ const POLL_INTERVAL_MS = 15_000;
22
22
  /** Must match the message string emitted by the SSE route in agent-events/route.ts */
23
23
  const METADATA_UPDATED_MESSAGE = 'Feature metadata updated';
24
24
  let nextFeatureId = 0;
@@ -175,6 +175,9 @@ export function useControlCenterState(initialNodes, initialEdges) {
175
175
  useEffect(() => {
176
176
  log.debug(`polling enabled (${POLL_INTERVAL_MS}ms interval)`);
177
177
  const timer = setInterval(async () => {
178
+ // Skip when tab is hidden — no point polling for a user who isn't looking.
179
+ if (document.hidden)
180
+ return;
178
181
  // Skip fetch entirely while a mutation is in-flight — the response
179
182
  // would contain pre-mutation data that reconcile would discard anyway.
180
183
  if (isMutating())
@@ -1 +1 @@
1
- {"version":3,"file":"app-sidebar.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/layouts/app-sidebar/app-sidebar.tsx"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAI/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,EAAE,iBAAiB,CAAC;IAEhC,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,YAAY,EAEZ,cAAc,GACf,EAAE,eAAe,2CA6MjB"}
1
+ {"version":3,"file":"app-sidebar.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/layouts/app-sidebar/app-sidebar.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAI/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,EAAE,iBAAiB,CAAC;IAEhC,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,YAAY,EAEZ,cAAc,GACf,EAAE,eAAe,2CAoMjB"}
@@ -1,7 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { usePathname } from 'next/navigation';
4
- import Link from 'next/link';
5
4
  import { Home, Moon, Sun, Volume2, VolumeOff, Wrench, Puzzle, Settings } from 'lucide-react';
6
5
  import { Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarRail, useSidebar, } from '../../ui/sidebar.js';
7
6
  import { ScrollArea } from '../../ui/scroll-area.js';
@@ -39,39 +38,39 @@ export function AppSidebar({ features, featureFlags, onFeatureClick, }) {
39
38
  'flex min-w-0 flex-1 items-center gap-2 overflow-hidden px-2',
40
39
  'transition-opacity duration-200 ease-out',
41
40
  expandedVisible ? 'opacity-100' : 'opacity-0',
42
- ].join(' '), "aria-hidden": !expandedVisible, children: [_jsx(ShepLogo, { className: "shrink-0", size: 20, variant: versionData.isDev ? 'dev' : 'default' }), _jsx("span", { className: "truncate text-sm font-semibold tracking-tight", children: "Shep" }), _jsx(VersionBadge, { version: versionData.version, branch: versionData.branch || undefined, commitHash: versionData.commitHash || undefined, isDev: versionData.isDev, packageName: versionData.packageName, description: versionData.description, instancePath: versionData.instancePath || undefined })] })) : null, _jsx(SidebarCollapseToggle, { className: "shrink-0 transition-all duration-200" })] }) }), _jsx(SidebarNavItem, { icon: Home, label: "Control Center", href: "/", active: pathname === '/' }), _jsx(SidebarNavItem, { icon: Wrench, label: "Tools", href: "/tools", active: pathname === '/tools' }), featureFlags.skills ? (_jsx(SidebarNavItem, { icon: Puzzle, label: "Skills", href: "/skills", active: pathname === '/skills' })) : null] }) }), _jsx(SidebarContent, { children: showExpanded ? (_jsxs("div", { className: [
41
+ ].join(' '), "aria-hidden": !expandedVisible, children: [_jsx(ShepLogo, { className: "shrink-0", size: 20, variant: versionData.isDev ? 'dev' : 'default' }), _jsx("span", { className: "truncate text-sm font-semibold tracking-tight", children: "Shep" }), _jsx(VersionBadge, { version: versionData.version, branch: versionData.branch || undefined, commitHash: versionData.commitHash || undefined, isDev: versionData.isDev, packageName: versionData.packageName, description: versionData.description, instancePath: versionData.instancePath || undefined })] })) : null, _jsx(SidebarCollapseToggle, { className: "shrink-0 transition-all duration-200" })] }) }), _jsx(SidebarNavItem, { icon: Home, label: "Control Center", href: "/", active: pathname === '/' }), _jsx(SidebarNavItem, { icon: Wrench, label: "Tools", href: "/tools", active: pathname === '/tools' }), featureFlags.skills ? (_jsx(SidebarNavItem, { icon: Puzzle, label: "Skills", href: "/skills", active: pathname === '/skills' })) : null, _jsx(SidebarNavItem, { icon: Settings, label: "Settings", href: "/settings", active: pathname === '/settings' })] }) }), _jsx(SidebarContent, { children: showExpanded ? (_jsxs("div", { className: [
43
42
  'flex min-h-0 flex-1 flex-col overflow-hidden transition-opacity duration-200 ease-out',
44
43
  '[&_[data-sidebar=group-label]]:!mt-0 [&_[data-sidebar=group-label]]:!opacity-100 [&_[data-sidebar=group-label]]:!transition-none',
45
44
  expandedVisible ? 'opacity-100' : 'opacity-0',
46
- ].join(' '), children: [_jsx(SidebarSectionHeader, { label: "Features" }), _jsx(ScrollArea, { className: "min-h-0 flex-1", children: grouped.map(({ key, label, items }) => items.length > 0 ? (_jsx(FeatureStatusGroup, { label: label, count: items.length, children: items.map((feature) => (_jsx(FeatureListItem, { name: feature.name, status: feature.status, startedAt: feature.startedAt, duration: feature.duration, agentType: feature.agentType, modelId: feature.modelId, onClick: onFeatureClick ? () => onFeatureClick(feature.featureId) : undefined }, feature.featureId))) }, key)) : null) })] })) : null }), _jsx(SidebarFooter, { className: "border-t p-2", children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(SidebarMenuButton, { asChild: true, tooltip: "Settings", className: "w-auto flex-none", children: _jsx(Link, { href: '/settings', onClick: () => clickSound.play(), children: _jsx(Settings, { className: "h-4 w-4" }) }) }) }), _jsx(TooltipContent, { side: "top", hidden: collapsed, children: "Settings" })] }) }), !collapsed && _jsx("div", { className: "flex-1" }), !collapsed && (_jsxs(TooltipProvider, { children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(SidebarMenuButton, { className: "w-auto flex-none", onClick: () => {
47
- clickSound.play();
48
- toggleSound();
49
- }, "aria-label": soundEnabled ? 'Mute sounds' : 'Unmute sounds', children: soundEnabled ? (_jsx(Volume2, { className: "h-4 w-4" })) : (_jsx(VolumeOff, { className: "h-4 w-4" })) }) }), _jsx(TooltipContent, { side: "top", children: soundEnabled ? 'Mute sounds' : 'Unmute sounds' })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(SidebarMenuButton, { className: "w-auto flex-none", onClick: (e) => {
50
- const currentResolved = theme === 'system' ? resolvedTheme : theme;
51
- const goingToDark = currentResolved !== 'dark';
52
- const newTheme = theme === 'system'
53
- ? resolvedTheme === 'dark'
54
- ? 'light'
55
- : 'dark'
56
- : theme === 'dark'
57
- ? 'light'
58
- : 'dark';
59
- if (goingToDark) {
60
- toggleOnSound.play();
61
- }
62
- else {
63
- toggleOffSound.play();
64
- }
65
- const prefersReducedMotion = typeof window !== 'undefined' &&
66
- window.matchMedia('(prefers-reduced-motion: reduce)').matches;
67
- if (!('startViewTransition' in document) || prefersReducedMotion) {
68
- setTheme(newTheme);
69
- return;
70
- }
71
- document.documentElement.style.setProperty('--x', `${e.clientX}px`);
72
- document.documentElement.style.setProperty('--y', `${e.clientY}px`);
73
- document.startViewTransition(() => {
74
- setTheme(newTheme);
75
- });
76
- }, "aria-label": `Switch to ${resolvedTheme === 'dark' ? 'light' : 'dark'} mode`, children: [_jsx(Sun, { className: "h-4 w-4 scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" }), _jsx(Moon, { className: "absolute h-4 w-4 scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" })] }) }), _jsx(TooltipContent, { side: "top", children: resolvedTheme === 'dark' ? 'Light mode' : 'Dark mode' })] })] }))] }) }) }) }), _jsx(SidebarRail, {})] }));
45
+ ].join(' '), children: [_jsx(SidebarSectionHeader, { label: "Features" }), _jsx(ScrollArea, { className: "min-h-0 flex-1", children: grouped.map(({ key, label, items }) => items.length > 0 ? (_jsx(FeatureStatusGroup, { label: label, count: items.length, children: items.map((feature) => (_jsx(FeatureListItem, { name: feature.name, status: feature.status, startedAt: feature.startedAt, duration: feature.duration, agentType: feature.agentType, modelId: feature.modelId, onClick: onFeatureClick ? () => onFeatureClick(feature.featureId) : undefined }, feature.featureId))) }, key)) : null) })] })) : null }), _jsx(SidebarFooter, { className: "border-t p-2", children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: _jsx("div", { className: "flex items-center gap-1", children: _jsxs(TooltipProvider, { children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(SidebarMenuButton, { className: "w-auto flex-none", onClick: (e) => {
46
+ const currentResolved = theme === 'system' ? resolvedTheme : theme;
47
+ const goingToDark = currentResolved !== 'dark';
48
+ const newTheme = theme === 'system'
49
+ ? resolvedTheme === 'dark'
50
+ ? 'light'
51
+ : 'dark'
52
+ : theme === 'dark'
53
+ ? 'light'
54
+ : 'dark';
55
+ if (goingToDark) {
56
+ toggleOnSound.play();
57
+ }
58
+ else {
59
+ toggleOffSound.play();
60
+ }
61
+ const prefersReducedMotion = typeof window !== 'undefined' &&
62
+ window.matchMedia('(prefers-reduced-motion: reduce)').matches;
63
+ if (!('startViewTransition' in document) || prefersReducedMotion) {
64
+ setTheme(newTheme);
65
+ return;
66
+ }
67
+ document.documentElement.style.setProperty('--x', `${e.clientX}px`);
68
+ document.documentElement.style.setProperty('--y', `${e.clientY}px`);
69
+ document.startViewTransition(() => {
70
+ setTheme(newTheme);
71
+ });
72
+ }, "aria-label": `Switch to ${resolvedTheme === 'dark' ? 'light' : 'dark'} mode`, children: [_jsx(Sun, { className: "h-4 w-4 scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" }), _jsx(Moon, { className: "absolute h-4 w-4 scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" })] }) }), _jsx(TooltipContent, { side: "top", children: resolvedTheme === 'dark' ? 'Light mode' : 'Dark mode' })] }), !collapsed && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(SidebarMenuButton, { className: "w-auto flex-none", onClick: () => {
73
+ clickSound.play();
74
+ toggleSound();
75
+ }, "aria-label": soundEnabled ? 'Mute sounds' : 'Unmute sounds', children: soundEnabled ? (_jsx(Volume2, { className: "h-4 w-4" })) : (_jsx(VolumeOff, { className: "h-4 w-4" })) }) }), _jsx(TooltipContent, { side: "top", children: soundEnabled ? 'Mute sounds' : 'Unmute sounds' })] }))] }) }) }) }) }), _jsx(SidebarRail, {})] }));
77
76
  }
@@ -0,0 +1,12 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { SessionSummary } from '../components/common/feature-node/feature-sessions-dropdown.js';
3
+ interface SessionsContextValue {
4
+ getSessionsForPath: (path: string) => SessionSummary[];
5
+ hasActiveSessions: (path: string) => boolean;
6
+ }
7
+ export declare function SessionsProvider({ children }: {
8
+ children: ReactNode;
9
+ }): import("react/jsx-runtime").JSX.Element;
10
+ export declare function useSessionsContext(): SessionsContextValue;
11
+ export {};
12
+ //# sourceMappingURL=sessions-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions-provider.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/sessions-provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4DAA4D,CAAC;AAOjG,UAAU,oBAAoB;IAC5B,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,cAAc,EAAE,CAAC;IACvD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C;AAMD,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAoDrE;AAID,wBAAgB,kBAAkB,IAAI,oBAAoB,CAMzD"}
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
4
+ const POLL_INTERVAL_MS = 30_000;
5
+ const ACTIVE_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
6
+ const SessionsContext = createContext(null);
7
+ // ── Provider component ────────────────────────────────────────────────
8
+ export function SessionsProvider({ children }) {
9
+ const [sessionsByPath, setSessionsByPath] = useState(new Map());
10
+ const sessionsByPathRef = useRef(sessionsByPath);
11
+ sessionsByPathRef.current = sessionsByPath;
12
+ const fetchSessions = useCallback(async () => {
13
+ if (document.hidden)
14
+ return;
15
+ try {
16
+ const res = await fetch('/api/sessions-batch');
17
+ if (!res.ok)
18
+ return;
19
+ const data = (await res.json());
20
+ const next = new Map();
21
+ for (const [path, sessions] of Object.entries(data.sessionsByPath)) {
22
+ next.set(path, sessions);
23
+ }
24
+ setSessionsByPath(next);
25
+ }
26
+ catch {
27
+ // Silent — stale data is better than no data
28
+ }
29
+ }, []);
30
+ useEffect(() => {
31
+ void fetchSessions();
32
+ const timer = setInterval(() => void fetchSessions(), POLL_INTERVAL_MS);
33
+ return () => clearInterval(timer);
34
+ }, [fetchSessions]);
35
+ const getSessionsForPath = useCallback((path) => sessionsByPathRef.current.get(path) ?? [],
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ [sessionsByPath]);
38
+ const hasActiveSessions = useCallback((path) => {
39
+ const sessions = sessionsByPathRef.current.get(path);
40
+ if (!sessions)
41
+ return false;
42
+ const now = Date.now();
43
+ return sessions.some((s) => s.lastMessageAt && now - new Date(s.lastMessageAt).getTime() < ACTIVE_THRESHOLD_MS);
44
+ },
45
+ // eslint-disable-next-line react-hooks/exhaustive-deps
46
+ [sessionsByPath]);
47
+ const value = useMemo(() => ({ getSessionsForPath, hasActiveSessions }), [getSessionsForPath, hasActiveSessions]);
48
+ return _jsx(SessionsContext.Provider, { value: value, children: children });
49
+ }
50
+ // ── Consumer hook ─────────────────────────────────────────────────────
51
+ export function useSessionsContext() {
52
+ const ctx = useContext(SessionsContext);
53
+ if (!ctx) {
54
+ return { getSessionsForPath: () => [], hasActiveSessions: () => false };
55
+ }
56
+ return ctx;
57
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAmOlF"}
1
+ {"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAwLlF"}
@@ -27,34 +27,9 @@ export function useDeployAction(input) {
27
27
  mountedRef.current = false;
28
28
  };
29
29
  }, []);
30
- // Fetch status on mount recover running dev servers after page reload
31
- useEffect(() => {
32
- if (!input)
33
- return;
34
- let cancelled = false;
35
- (async () => {
36
- try {
37
- const result = await getDeploymentStatus(input.targetId);
38
- if (cancelled || !mountedRef.current)
39
- return;
40
- if (result && result.state !== 'Stopped') {
41
- log.info(`mount recovery: "${input.targetId}" state=${result.state}, url=${result.url}`);
42
- setStatus(result.state);
43
- setUrl(result.url);
44
- startPolling(input.targetId);
45
- }
46
- }
47
- catch {
48
- // Server container may not be available (e.g., in tests)
49
- log.debug(`mount recovery failed for "${input.targetId}" — ignoring`);
50
- }
51
- })();
52
- return () => {
53
- cancelled = true;
54
- };
55
- // Only run on mount (input identity is stable from the caller)
56
- // eslint-disable-next-line react-hooks/exhaustive-deps
57
- }, [input?.targetId]);
30
+ // Mount recovery removedgetGraphData() already enriches nodes with
31
+ // deployment status (get-graph-data.ts lines 180-200). No need to call
32
+ // a server action per node on every mount.
58
33
  // Cleanup on unmount
59
34
  useEffect(() => {
60
35
  return () => {
@@ -108,32 +83,11 @@ export function useDeployAction(input) {
108
83
  }
109
84
  }, POLL_INTERVAL);
110
85
  }, [stopPolling]);
111
- // Idle poll — when no deployment is known, periodically check if one was
112
- // started externally (e.g., from the drawer while the repo node is mounted).
113
- const IDLE_POLL_INTERVAL = 5000;
114
- useEffect(() => {
115
- if (!input || status !== null)
116
- return;
117
- const timer = setInterval(async () => {
118
- if (!mountedRef.current)
119
- return;
120
- try {
121
- const result = await getDeploymentStatus(input.targetId);
122
- if (!mountedRef.current)
123
- return;
124
- if (result && result.state !== 'Stopped') {
125
- log.info(`idle poll: "${input.targetId}" state=${result.state}, url=${result.url}`);
126
- setStatus(result.state);
127
- setUrl(result.url);
128
- startPolling(input.targetId);
129
- }
130
- }
131
- catch {
132
- // ignore
133
- }
134
- }, IDLE_POLL_INTERVAL);
135
- return () => clearInterval(timer);
136
- }, [input, status, startPolling]);
86
+ // Idle poll removed it was calling getDeploymentStatus every 5s for EVERY
87
+ // node on the canvas (~25 nodes = ~5 server action calls/sec). The mount
88
+ // recovery effect above (line 57) already checks on mount. Deployments
89
+ // started externally (e.g., from the drawer) should update the node via
90
+ // reconcile from the graph-data poll instead.
137
91
  const handleDeploy = useCallback(async () => {
138
92
  if (!input) {
139
93
  log.warn('deploy() called but input is null — no-op');
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared session scanning logic used by both /api/sessions and /api/sessions-batch.
3
+ *
4
+ * Scans Claude Code and Cursor session directories for JSONL session files,
5
+ * parsing headers to extract preview, message count, and timestamps.
6
+ */
7
+ export interface SessionResult {
8
+ id: string;
9
+ agentType: string;
10
+ preview: string | null;
11
+ messageCount: number;
12
+ firstMessageAt: string | null;
13
+ lastMessageAt: string | null;
14
+ createdAt: string | null;
15
+ projectPath: string;
16
+ filePath: string;
17
+ /** mtime for sorting — not sent to client */
18
+ _mtime: number;
19
+ }
20
+ export declare function scanClaudeSessions(repositoryPath: string, limit: number, includeWorktrees?: boolean): Promise<SessionResult[]>;
21
+ export declare function scanCursorSessions(repositoryPath: string, limit: number): Promise<SessionResult[]>;
22
+ /**
23
+ * Scan sessions for a single repository path from all providers.
24
+ * Merges and sorts by recency.
25
+ */
26
+ export declare function scanSessionsForPath(repositoryPath: string, limit: number, includeWorktrees?: boolean): Promise<SessionResult[]>;
27
+ //# sourceMappingURL=session-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-scanner.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/session-scanner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB;AAsID,wBAAsB,kBAAkB,CACtC,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,gBAAgB,UAAQ,GACvB,OAAO,CAAC,aAAa,EAAE,CAAC,CA2C1B;AAiED,wBAAsB,kBAAkB,CACtC,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,EAAE,CAAC,CA6D1B;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,gBAAgB,UAAQ,GACvB,OAAO,CAAC,aAAa,EAAE,CAAC,CAS1B"}