@swarmclawai/swarmclaw 0.7.7 → 0.8.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 (281) hide show
  1. package/README.md +12 -14
  2. package/next.config.ts +13 -2
  3. package/package.json +4 -2
  4. package/src/app/api/agents/[id]/thread/route.ts +9 -0
  5. package/src/app/api/agents/route.ts +4 -0
  6. package/src/app/api/agents/thread-route.test.ts +133 -0
  7. package/src/app/api/approvals/route.test.ts +148 -0
  8. package/src/app/api/canvas/[sessionId]/route.ts +3 -1
  9. package/src/app/api/chatrooms/[id]/chat/route.ts +4 -2
  10. package/src/app/api/chats/[id]/devserver/route.ts +48 -7
  11. package/src/app/api/chats/[id]/messages/route.ts +42 -18
  12. package/src/app/api/chats/[id]/route.ts +1 -1
  13. package/src/app/api/chats/[id]/stop/route.ts +5 -4
  14. package/src/app/api/chats/route.ts +23 -2
  15. package/src/app/api/clawhub/install/route.ts +28 -8
  16. package/src/app/api/connectors/[id]/route.ts +46 -3
  17. package/src/app/api/connectors/route.ts +12 -8
  18. package/src/app/api/external-agents/route.test.ts +165 -0
  19. package/src/app/api/gateways/[id]/health/route.ts +27 -12
  20. package/src/app/api/gateways/[id]/route.ts +2 -0
  21. package/src/app/api/gateways/health-route.test.ts +135 -0
  22. package/src/app/api/gateways/route.ts +2 -0
  23. package/src/app/api/mcp-servers/route.test.ts +130 -0
  24. package/src/app/api/openclaw/deploy/route.ts +38 -5
  25. package/src/app/api/plugins/install/route.ts +46 -6
  26. package/src/app/api/plugins/marketplace/route.ts +48 -15
  27. package/src/app/api/preview-server/route.ts +26 -11
  28. package/src/app/api/projects/[id]/route.ts +6 -2
  29. package/src/app/api/projects/route.ts +4 -3
  30. package/src/app/api/schedules/[id]/run/route.ts +4 -0
  31. package/src/app/api/schedules/route.test.ts +86 -0
  32. package/src/app/api/schedules/route.ts +6 -1
  33. package/src/app/api/secrets/[id]/route.ts +1 -0
  34. package/src/app/api/secrets/route.ts +2 -1
  35. package/src/app/api/settings/route.ts +2 -0
  36. package/src/app/api/setup/check-provider/route.test.ts +19 -0
  37. package/src/app/api/setup/check-provider/route.ts +40 -10
  38. package/src/app/api/skills/[id]/route.ts +12 -0
  39. package/src/app/api/skills/import/route.ts +14 -12
  40. package/src/app/api/skills/route.ts +13 -1
  41. package/src/app/api/tasks/[id]/route.ts +10 -1
  42. package/src/app/api/tasks/import/github/route.test.ts +65 -0
  43. package/src/app/api/tasks/import/github/route.ts +337 -0
  44. package/src/app/api/wallets/[id]/approve/route.ts +17 -3
  45. package/src/app/api/wallets/[id]/route.ts +79 -33
  46. package/src/app/api/wallets/[id]/send/route.ts +19 -33
  47. package/src/app/api/wallets/route.ts +78 -61
  48. package/src/app/api/webhooks/[id]/route.ts +33 -6
  49. package/src/app/api/webhooks/route.test.ts +272 -0
  50. package/src/cli/index.js +1 -0
  51. package/src/cli/spec.js +1 -0
  52. package/src/components/agents/agent-card.tsx +9 -2
  53. package/src/components/agents/agent-chat-list.tsx +18 -2
  54. package/src/components/agents/agent-list.tsx +1 -0
  55. package/src/components/agents/agent-sheet.tsx +257 -38
  56. package/src/components/agents/inspector-panel.tsx +41 -0
  57. package/src/components/canvas/canvas-panel.tsx +236 -65
  58. package/src/components/chat/chat-area.tsx +36 -19
  59. package/src/components/chat/chat-card.tsx +36 -13
  60. package/src/components/chat/chat-header.tsx +48 -16
  61. package/src/components/chat/chat-list.tsx +28 -4
  62. package/src/components/chat/checkpoint-timeline.tsx +50 -34
  63. package/src/components/chat/delegation-banner.test.ts +14 -1
  64. package/src/components/chat/delegation-banner.tsx +1 -1
  65. package/src/components/chat/message-bubble.tsx +208 -145
  66. package/src/components/chat/message-list.tsx +48 -19
  67. package/src/components/chatrooms/chatroom-message.tsx +2 -2
  68. package/src/components/chatrooms/chatroom-sheet.tsx +16 -2
  69. package/src/components/connectors/connector-health.tsx +1 -1
  70. package/src/components/connectors/connector-list.tsx +7 -2
  71. package/src/components/connectors/connector-sheet.tsx +337 -148
  72. package/src/components/gateways/gateway-sheet.tsx +2 -2
  73. package/src/components/layout/app-layout.tsx +40 -23
  74. package/src/components/mcp-servers/mcp-server-list.tsx +26 -5
  75. package/src/components/mcp-servers/mcp-server-sheet.tsx +19 -2
  76. package/src/components/openclaw/openclaw-deploy-panel.tsx +269 -21
  77. package/src/components/plugins/plugin-list.tsx +45 -9
  78. package/src/components/plugins/plugin-sheet.tsx +55 -7
  79. package/src/components/projects/project-detail.tsx +217 -0
  80. package/src/components/projects/project-sheet.tsx +176 -4
  81. package/src/components/providers/provider-list.tsx +2 -1
  82. package/src/components/providers/provider-sheet.tsx +21 -2
  83. package/src/components/schedules/schedule-card.tsx +25 -1
  84. package/src/components/schedules/schedule-sheet.tsx +44 -2
  85. package/src/components/secrets/secret-sheet.tsx +21 -2
  86. package/src/components/shared/agent-switch-dialog.tsx +12 -1
  87. package/src/components/shared/bottom-sheet.tsx +13 -3
  88. package/src/components/shared/command-palette.tsx +8 -1
  89. package/src/components/shared/confirm-dialog.tsx +19 -4
  90. package/src/components/shared/connector-platform-icon.test.ts +28 -0
  91. package/src/components/shared/connector-platform-icon.tsx +39 -6
  92. package/src/components/shared/settings/plugin-manager.tsx +29 -6
  93. package/src/components/shared/settings/section-capability-policy.tsx +45 -3
  94. package/src/components/shared/settings/section-voice.tsx +11 -3
  95. package/src/components/skills/skill-list.tsx +25 -0
  96. package/src/components/skills/skill-sheet.tsx +84 -12
  97. package/src/components/tasks/approvals-panel.tsx +289 -34
  98. package/src/components/tasks/task-board.tsx +410 -25
  99. package/src/components/tasks/task-card.tsx +66 -8
  100. package/src/components/tasks/task-sheet.tsx +16 -4
  101. package/src/components/ui/dialog.tsx +2 -2
  102. package/src/components/wallets/wallet-approval-dialog.tsx +4 -2
  103. package/src/components/wallets/wallet-panel.tsx +435 -90
  104. package/src/components/wallets/wallet-section.tsx +198 -48
  105. package/src/components/webhooks/webhook-sheet.tsx +22 -2
  106. package/src/lib/approval-display.ts +20 -0
  107. package/src/lib/canvas-content.ts +198 -0
  108. package/src/lib/chat-artifact-summary.ts +165 -0
  109. package/src/lib/chat-display.test.ts +91 -0
  110. package/src/lib/chat-display.ts +58 -0
  111. package/src/lib/chat-streaming-state.test.ts +47 -1
  112. package/src/lib/chat-streaming-state.ts +42 -0
  113. package/src/lib/ollama-model.ts +10 -0
  114. package/src/lib/openclaw-endpoint.test.ts +8 -0
  115. package/src/lib/openclaw-endpoint.ts +6 -1
  116. package/src/lib/plugin-install-cors.ts +46 -0
  117. package/src/lib/plugin-sources.test.ts +43 -0
  118. package/src/lib/plugin-sources.ts +77 -0
  119. package/src/lib/providers/ollama.ts +16 -6
  120. package/src/lib/providers/openclaw.test.ts +54 -0
  121. package/src/lib/providers/openclaw.ts +127 -11
  122. package/src/lib/schedule-dedupe-advanced.test.ts +1335 -0
  123. package/src/lib/schedule-dedupe.test.ts +66 -1
  124. package/src/lib/schedule-dedupe.ts +169 -12
  125. package/src/lib/schedule-origin.test.ts +20 -0
  126. package/src/lib/schedule-origin.ts +15 -0
  127. package/src/lib/server/__fixtures__/fake-mcp-stdio-server.mjs +27 -0
  128. package/src/lib/server/agent-availability.ts +16 -0
  129. package/src/lib/server/agent-runtime-config.ts +12 -4
  130. package/src/lib/server/agent-thread-session.test.ts +51 -0
  131. package/src/lib/server/agent-thread-session.ts +7 -0
  132. package/src/lib/server/approval-match.ts +205 -0
  133. package/src/lib/server/approvals-auto-approve.test.ts +538 -1
  134. package/src/lib/server/approvals.ts +214 -1
  135. package/src/lib/server/assistant-control.test.ts +29 -0
  136. package/src/lib/server/assistant-control.ts +23 -0
  137. package/src/lib/server/build-llm.test.ts +79 -0
  138. package/src/lib/server/build-llm.ts +14 -4
  139. package/src/lib/server/canvas-content.test.ts +32 -0
  140. package/src/lib/server/canvas-content.ts +6 -0
  141. package/src/lib/server/capability-router.test.ts +33 -0
  142. package/src/lib/server/capability-router.ts +80 -19
  143. package/src/lib/server/chat-execution-advanced.test.ts +651 -0
  144. package/src/lib/server/chat-execution-disabled.test.ts +94 -0
  145. package/src/lib/server/chat-execution-tool-events.test.ts +157 -0
  146. package/src/lib/server/chat-execution.ts +378 -73
  147. package/src/lib/server/clawhub-client.test.ts +14 -8
  148. package/src/lib/server/connectors/manager-reconnect.test.ts +47 -0
  149. package/src/lib/server/connectors/manager.test.ts +1147 -0
  150. package/src/lib/server/connectors/manager.ts +461 -137
  151. package/src/lib/server/connectors/pairing.ts +26 -5
  152. package/src/lib/server/connectors/types.ts +2 -0
  153. package/src/lib/server/connectors/whatsapp.test.ts +134 -0
  154. package/src/lib/server/connectors/whatsapp.ts +271 -47
  155. package/src/lib/server/context-manager.ts +6 -1
  156. package/src/lib/server/daemon-state.ts +84 -47
  157. package/src/lib/server/data-dir.test.ts +37 -0
  158. package/src/lib/server/data-dir.ts +20 -1
  159. package/src/lib/server/delegation-jobs-advanced.test.ts +513 -0
  160. package/src/lib/server/devserver-launch.test.ts +60 -0
  161. package/src/lib/server/devserver-launch.ts +85 -0
  162. package/src/lib/server/elevenlabs.test.ts +247 -1
  163. package/src/lib/server/elevenlabs.ts +147 -43
  164. package/src/lib/server/ethereum.ts +590 -0
  165. package/src/lib/server/eval/agent-regression-advanced.test.ts +302 -0
  166. package/src/lib/server/eval/agent-regression.test.ts +18 -1
  167. package/src/lib/server/eval/agent-regression.ts +383 -11
  168. package/src/lib/server/evm-swap.ts +475 -0
  169. package/src/lib/server/execution-log.ts +1 -0
  170. package/src/lib/server/heartbeat-service-timer.test.ts +173 -0
  171. package/src/lib/server/heartbeat-service.ts +20 -11
  172. package/src/lib/server/heartbeat-wake.test.ts +112 -0
  173. package/src/lib/server/heartbeat-wake.ts +338 -57
  174. package/src/lib/server/main-agent-loop-advanced.test.ts +538 -0
  175. package/src/lib/server/main-agent-loop.test.ts +260 -0
  176. package/src/lib/server/main-agent-loop.ts +559 -14
  177. package/src/lib/server/mcp-client.test.ts +16 -0
  178. package/src/lib/server/mcp-client.ts +25 -0
  179. package/src/lib/server/memory-integration.test.ts +719 -0
  180. package/src/lib/server/memory-policy.test.ts +43 -0
  181. package/src/lib/server/memory-policy.ts +132 -0
  182. package/src/lib/server/memory-tiers.test.ts +60 -0
  183. package/src/lib/server/memory-tiers.ts +16 -0
  184. package/src/lib/server/ollama-runtime.ts +58 -0
  185. package/src/lib/server/openclaw-deploy.test.ts +109 -1
  186. package/src/lib/server/openclaw-deploy.ts +557 -81
  187. package/src/lib/server/openclaw-gateway.test.ts +131 -0
  188. package/src/lib/server/openclaw-gateway.ts +10 -4
  189. package/src/lib/server/openclaw-health.test.ts +35 -0
  190. package/src/lib/server/openclaw-health.ts +215 -47
  191. package/src/lib/server/orchestrator-lg.ts +3 -2
  192. package/src/lib/server/orchestrator.ts +2 -0
  193. package/src/lib/server/plugins-advanced.test.ts +351 -0
  194. package/src/lib/server/plugins.ts +211 -6
  195. package/src/lib/server/project-context.ts +162 -0
  196. package/src/lib/server/project-utils.ts +150 -0
  197. package/src/lib/server/queue-advanced.test.ts +528 -0
  198. package/src/lib/server/queue-followups.test.ts +409 -2
  199. package/src/lib/server/queue-reconcile.test.ts +128 -0
  200. package/src/lib/server/queue.ts +527 -68
  201. package/src/lib/server/scheduler.ts +29 -1
  202. package/src/lib/server/session-note.test.ts +36 -0
  203. package/src/lib/server/session-note.ts +42 -0
  204. package/src/lib/server/session-run-manager.ts +83 -4
  205. package/src/lib/server/session-tools/canvas.ts +14 -12
  206. package/src/lib/server/session-tools/connector-inputs.test.ts +37 -0
  207. package/src/lib/server/session-tools/connector.test.ts +138 -0
  208. package/src/lib/server/session-tools/connector.ts +366 -54
  209. package/src/lib/server/session-tools/context.ts +17 -3
  210. package/src/lib/server/session-tools/crud.ts +484 -84
  211. package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
  212. package/src/lib/server/session-tools/delegate-resume.test.ts +50 -0
  213. package/src/lib/server/session-tools/delegate.ts +102 -10
  214. package/src/lib/server/session-tools/discovery-approvals.test.ts +142 -0
  215. package/src/lib/server/session-tools/discovery.ts +80 -12
  216. package/src/lib/server/session-tools/file-normalize.test.ts +36 -0
  217. package/src/lib/server/session-tools/file.ts +43 -4
  218. package/src/lib/server/session-tools/human-loop.ts +35 -5
  219. package/src/lib/server/session-tools/index.ts +44 -9
  220. package/src/lib/server/session-tools/manage-connectors.test.ts +139 -0
  221. package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +564 -0
  222. package/src/lib/server/session-tools/manage-schedules.test.ts +283 -0
  223. package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +852 -0
  224. package/src/lib/server/session-tools/manage-tasks.test.ts +114 -0
  225. package/src/lib/server/session-tools/memory.test.ts +93 -0
  226. package/src/lib/server/session-tools/memory.ts +554 -75
  227. package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
  228. package/src/lib/server/session-tools/platform-access.test.ts +58 -0
  229. package/src/lib/server/session-tools/platform.ts +60 -19
  230. package/src/lib/server/session-tools/plugin-creator.ts +57 -1
  231. package/src/lib/server/session-tools/primitive-tools.test.ts +6 -0
  232. package/src/lib/server/session-tools/schedule.ts +6 -1
  233. package/src/lib/server/session-tools/shell-normalize.test.ts +25 -1
  234. package/src/lib/server/session-tools/shell.ts +22 -3
  235. package/src/lib/server/session-tools/wallet-tool.test.ts +254 -0
  236. package/src/lib/server/session-tools/wallet.ts +1374 -139
  237. package/src/lib/server/session-tools/web-inputs.test.ts +178 -0
  238. package/src/lib/server/session-tools/web.ts +621 -70
  239. package/src/lib/server/skill-discovery.ts +128 -0
  240. package/src/lib/server/skill-eligibility.test.ts +84 -0
  241. package/src/lib/server/skill-eligibility.ts +95 -0
  242. package/src/lib/server/skill-prompt-budget.test.ts +102 -0
  243. package/src/lib/server/skill-prompt-budget.ts +125 -0
  244. package/src/lib/server/skills-normalize.test.ts +54 -0
  245. package/src/lib/server/skills-normalize.ts +372 -26
  246. package/src/lib/server/solana.ts +214 -29
  247. package/src/lib/server/storage.ts +65 -36
  248. package/src/lib/server/stream-agent-chat.test.ts +437 -2
  249. package/src/lib/server/stream-agent-chat.ts +957 -79
  250. package/src/lib/server/system-events.ts +1 -1
  251. package/src/lib/server/tool-aliases.ts +2 -0
  252. package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
  253. package/src/lib/server/tool-capability-policy.test.ts +24 -0
  254. package/src/lib/server/tool-capability-policy.ts +29 -1
  255. package/src/lib/server/tool-loop-detection.test.ts +105 -0
  256. package/src/lib/server/tool-loop-detection.ts +260 -0
  257. package/src/lib/server/tool-planning.test.ts +44 -0
  258. package/src/lib/server/tool-planning.ts +271 -0
  259. package/src/lib/server/wallet-execution.test.ts +198 -0
  260. package/src/lib/server/wallet-portfolio.test.ts +98 -0
  261. package/src/lib/server/wallet-portfolio.ts +724 -0
  262. package/src/lib/server/wallet-service.test.ts +57 -0
  263. package/src/lib/server/wallet-service.ts +213 -0
  264. package/src/lib/server/watch-jobs-advanced.test.ts +594 -0
  265. package/src/lib/server/watch-jobs.ts +17 -2
  266. package/src/lib/server/workspace-context.ts +111 -0
  267. package/src/lib/skill-save-payload.test.ts +39 -0
  268. package/src/lib/skill-save-payload.ts +37 -0
  269. package/src/lib/tasks.ts +28 -0
  270. package/src/lib/tool-definitions.ts +2 -1
  271. package/src/lib/tool-event-summary.test.ts +30 -0
  272. package/src/lib/tool-event-summary.ts +37 -0
  273. package/src/lib/validation/schemas.ts +1 -0
  274. package/src/lib/wallet-transactions.test.ts +75 -0
  275. package/src/lib/wallet-transactions.ts +43 -0
  276. package/src/lib/wallet.test.ts +17 -0
  277. package/src/lib/wallet.ts +183 -0
  278. package/src/proxy.test.ts +31 -0
  279. package/src/proxy.ts +34 -2
  280. package/src/stores/use-chat-store.ts +15 -1
  281. package/src/types/index.ts +249 -14
@@ -233,9 +233,9 @@ export function GatewaySheet() {
233
233
  params.set('endpoint', endpoint.trim() || 'http://localhost:18789')
234
234
  if (credentialId) params.set('credentialId', credentialId)
235
235
  if (tokenDraft.trim()) params.set('token', tokenDraft.trim())
236
- const result = await api<{ ok: boolean; models: string[]; error?: string; hint?: string }>('GET', `/providers/openclaw/health?${params.toString()}`)
236
+ const result = await api<{ ok: boolean; models: string[]; message?: string; error?: string; hint?: string }>('GET', `/providers/openclaw/health?${params.toString()}`)
237
237
  if (result.ok) {
238
- setCheckMessage(`Connected. ${result.models?.length ? `${result.models.length} model${result.models.length === 1 ? '' : 's'} visible.` : 'Gateway responded normally.'}`)
238
+ setCheckMessage(result.message || `Connected. ${result.models?.length ? `${result.models.length} model${result.models.length === 1 ? '' : 's'} visible.` : 'Gateway responded normally.'}`)
239
239
  } else {
240
240
  setCheckMessage(result.error || result.hint || 'Gateway health check failed.')
241
241
  }
@@ -141,6 +141,7 @@ export function AppLayout() {
141
141
  const execApprovals = useApprovalStore((s) => s.approvals)
142
142
  const loadExecApprovals = useApprovalStore((s) => s.loadApprovals)
143
143
  const pruneExecApprovals = useApprovalStore((s) => s.pruneExpired)
144
+ const appSettings = useAppStore((s) => s.appSettings)
144
145
  const isDesktop = useMediaQuery('(min-width: 768px)')
145
146
  const hasSelectedSession = !!(currentSessionId && sessions[currentSessionId])
146
147
 
@@ -168,11 +169,23 @@ export function AppLayout() {
168
169
  pruneExecApprovals()
169
170
  }, 10000)
170
171
 
171
- const appSettings = useAppStore((s) => s.appSettings)
172
172
  const [agentViewMode, setAgentViewMode] = useState<'chat' | 'config'>('chat')
173
173
  const [profileSheetOpen, setProfileSheetOpen] = useState(false)
174
174
  const [canvasDismissedFor, setCanvasDismissedFor] = useState<string | null>(null)
175
175
 
176
+ const isViewEnabled = useCallback((view: AppView) => {
177
+ if (view === 'projects') return appSettings.projectManagementEnabled !== false
178
+ if (view === 'tasks') return appSettings.taskManagementEnabled !== false
179
+ if (view === 'chatrooms') return plugins['chatroom']?.enabled !== false
180
+ if (view === 'schedules') return plugins['schedule']?.enabled !== false
181
+ if (view === 'memory') return plugins['memory']?.enabled !== false
182
+ if (view === 'connectors') return plugins['connectors']?.enabled !== false
183
+ if (view === 'webhooks') return plugins['http']?.enabled !== false
184
+ if (view === 'wallets') return plugins['wallet']?.enabled !== false
185
+ if (view === 'logs') return plugins['monitor']?.enabled !== false
186
+ return true
187
+ }, [appSettings.projectManagementEnabled, appSettings.taskManagementEnabled, plugins])
188
+
176
189
  const handleShortcutKey = useCallback((e: KeyboardEvent) => {
177
190
  const mod = e.metaKey || e.ctrlKey
178
191
  // Cmd+N / Ctrl+N — jump to the default agent shortcut
@@ -188,8 +201,10 @@ export function AppLayout() {
188
201
  }
189
202
  // Cmd+Shift+T / Ctrl+Shift+T — jump to tasks
190
203
  if (mod && e.shiftKey && e.key.toLowerCase() === 't') {
204
+ const state = useAppStore.getState()
205
+ if (state.appSettings.taskManagementEnabled === false) return
191
206
  e.preventDefault()
192
- useAppStore.getState().setActiveView('tasks')
207
+ state.setActiveView('tasks')
193
208
  }
194
209
  }, [])
195
210
 
@@ -222,6 +237,13 @@ export function AppLayout() {
222
237
  }
223
238
  }, [appSettings.themeHue])
224
239
 
240
+ useEffect(() => {
241
+ if (!isViewEnabled(activeView)) {
242
+ setActiveView('home')
243
+ setSidebarOpen(false)
244
+ }
245
+ }, [activeView, isViewEnabled, setActiveView, setSidebarOpen])
246
+
225
247
  const [pluginSidebarItems, setPluginSidebarItems] = useState<Array<{ id: string; label: string; href: string }>>([])
226
248
 
227
249
  const refreshPluginState = useCallback(() => {
@@ -235,17 +257,6 @@ export function AppLayout() {
235
257
 
236
258
  useWs('plugins', refreshPluginState)
237
259
 
238
- const isViewEnabled = useCallback((view: AppView) => {
239
- if (view === 'chatrooms') return plugins['chatroom']?.enabled !== false
240
- if (view === 'schedules') return plugins['schedule']?.enabled !== false
241
- if (view === 'memory') return plugins['memory']?.enabled !== false
242
- if (view === 'connectors') return plugins['connectors']?.enabled !== false
243
- if (view === 'webhooks') return plugins['http']?.enabled !== false
244
- if (view === 'wallets') return plugins['wallet']?.enabled !== false
245
- if (view === 'logs') return plugins['monitor']?.enabled !== false
246
- return true
247
- }, [plugins])
248
-
249
260
  const [railExpanded, setRailExpanded] = useState(() => {
250
261
  const stored = safeStorageGet(RAIL_EXPANDED_KEY)
251
262
  return stored === null ? true : stored === 'true'
@@ -262,6 +273,7 @@ export function AppLayout() {
262
273
  }
263
274
 
264
275
  const openNewSheet = () => {
276
+ if (!isViewEnabled(activeView)) return
265
277
  if (activeView === 'agents') setAgentSheetOpen(true)
266
278
  else if (activeView === 'schedules') setScheduleSheetOpen(true)
267
279
  else if (activeView === 'tasks') setTaskSheetOpen(true)
@@ -278,6 +290,7 @@ export function AppLayout() {
278
290
  }
279
291
 
280
292
  const handleNavClick = (view: AppView) => {
293
+ if (!isViewEnabled(view)) return
281
294
  if (FULL_WIDTH_VIEWS.has(view)) {
282
295
  setActiveView(view)
283
296
  setSidebarOpen(false)
@@ -491,11 +504,13 @@ export function AppLayout() {
491
504
  </svg>
492
505
  </NavItem>
493
506
  )}
494
- <NavItem view="projects" label="Projects" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('projects')}>
495
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
496
- <path d="M2 20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8l-7-7H4a2 2 0 0 0-2 2v17Z" /><path d="M14 2v7h7" />
497
- </svg>
498
- </NavItem>
507
+ {isViewEnabled('projects') && (
508
+ <NavItem view="projects" label="Projects" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('projects')}>
509
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
510
+ <path d="M2 20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8l-7-7H4a2 2 0 0 0-2 2v17Z" /><path d="M14 2v7h7" />
511
+ </svg>
512
+ </NavItem>
513
+ )}
499
514
  </div>
500
515
 
501
516
  <div className={`flex flex-col gap-0.5 ${railExpanded ? '' : 'items-center'}`}>
@@ -504,11 +519,13 @@ export function AppLayout() {
504
519
  ) : (
505
520
  <div className="my-1 h-px w-6 bg-white/[0.06]" />
506
521
  )}
507
- <NavItem view="tasks" label="Tasks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('tasks')} badge={pendingApprovalCount}>
508
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
509
- <path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2" /><rect x="9" y="3" width="6" height="4" rx="1" /><path d="M9 14l2 2 4-4" />
510
- </svg>
511
- </NavItem>
522
+ {isViewEnabled('tasks') && (
523
+ <NavItem view="tasks" label="Tasks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('tasks')} badge={pendingApprovalCount}>
524
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
525
+ <path d="M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2" /><rect x="9" y="3" width="6" height="4" rx="1" /><path d="M9 14l2 2 4-4" />
526
+ </svg>
527
+ </NavItem>
528
+ )}
512
529
  <NavItem view="approvals" label="Approvals" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('approvals')} badge={pendingApprovalCount}>
513
530
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
514
531
  <path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/>
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useEffect, useState, useRef } from 'react'
4
4
  import { useAppStore } from '@/stores/use-app-store'
5
+ import { ConfirmDialog } from '@/components/shared/confirm-dialog'
5
6
  import { api } from '@/lib/api-client'
6
7
  import { toast } from 'sonner'
7
8
 
@@ -62,6 +63,8 @@ export function McpServerList({ inSidebar }: { inSidebar?: boolean }) {
62
63
  const [invokeResult, setInvokeResult] = useState<McpInvokeResult | null>(null)
63
64
  const [conformanceByServer, setConformanceByServer] = useState<Record<string, McpConformanceResult>>({})
64
65
  const [conformanceLoading, setConformanceLoading] = useState<Record<string, boolean>>({})
66
+ const [confirmDelete, setConfirmDelete] = useState<{ id: string; name: string } | null>(null)
67
+ const [deletingId, setDeletingId] = useState<string | null>(null)
65
68
  const timersRef = useRef<ReturnType<typeof setTimeout>[]>([])
66
69
 
67
70
  useEffect(() => {
@@ -109,17 +112,24 @@ export function McpServerList({ inSidebar }: { inSidebar?: boolean }) {
109
112
  setMcpServerSheetOpen(true)
110
113
  }
111
114
 
112
- const handleDelete = async (e: React.MouseEvent, id: string) => {
115
+ const handleDelete = (e: React.MouseEvent, id: string) => {
113
116
  e.stopPropagation()
114
117
  const server = mcpServers[id]
115
- if (!confirm(`Delete MCP server "${server?.name || id}"?`)) return
116
-
118
+ setConfirmDelete({ id, name: server?.name || id })
119
+ }
120
+
121
+ const handleDeleteConfirm = async () => {
122
+ if (!confirmDelete) return
123
+ setDeletingId(confirmDelete.id)
117
124
  try {
118
- await api('DELETE', `/mcp-servers/${id}`)
125
+ await api('DELETE', `/mcp-servers/${confirmDelete.id}`)
119
126
  toast.success('MCP server deleted')
120
127
  await loadMcpServers()
128
+ setConfirmDelete(null)
121
129
  } catch (err: unknown) {
122
130
  toast.error(err instanceof Error ? err.message : 'Failed to delete server')
131
+ } finally {
132
+ setDeletingId(null)
123
133
  }
124
134
  }
125
135
 
@@ -280,7 +290,7 @@ export function McpServerList({ inSidebar }: { inSidebar?: boolean }) {
280
290
  ) : inspectorError ? (
281
291
  <p className="text-[12px] text-red-300">{inspectorError}</p>
282
292
  ) : (
283
- <div className="space-y-3">
293
+ <div className="space-y-3">
284
294
  {activeConformance && (
285
295
  <div className={`rounded-[10px] border p-3 ${activeConformance.ok ? 'border-emerald-400/20 bg-emerald-500/[0.06]' : 'border-amber-400/20 bg-amber-500/[0.06]'}`}>
286
296
  <p className={`text-[12px] font-600 mb-1 ${activeConformance.ok ? 'text-emerald-300' : 'text-amber-300'}`}>
@@ -441,6 +451,17 @@ export function McpServerList({ inSidebar }: { inSidebar?: boolean }) {
441
451
  </div>
442
452
  </>
443
453
  )}
454
+ <ConfirmDialog
455
+ open={!!confirmDelete}
456
+ title="Delete MCP Server?"
457
+ message={confirmDelete ? `Delete "${confirmDelete.name}"? This will remove the MCP server from the app.` : 'Delete this MCP server?'}
458
+ confirmLabel={deletingId ? 'Deleting...' : 'Delete'}
459
+ confirmDisabled={!!deletingId}
460
+ cancelDisabled={!!deletingId}
461
+ danger
462
+ onConfirm={() => { void handleDeleteConfirm() }}
463
+ onCancel={() => { if (!deletingId) setConfirmDelete(null) }}
464
+ />
444
465
  </div>
445
466
  )
446
467
  }
@@ -3,6 +3,7 @@
3
3
  import { useState } from 'react'
4
4
  import { useAppStore } from '@/stores/use-app-store'
5
5
  import { BottomSheet } from '@/components/shared/bottom-sheet'
6
+ import { ConfirmDialog } from '@/components/shared/confirm-dialog'
6
7
  import { api } from '@/lib/api-client'
7
8
  import { toast } from 'sonner'
8
9
  import type { McpServerConfig, McpTransport } from '@/types'
@@ -25,6 +26,8 @@ function McpServerForm({ editing, onClose, loadMcpServers }: {
25
26
  )
26
27
  const [testing, setTesting] = useState(false)
27
28
  const [testResult, setTestResult] = useState<{ ok: boolean; tools?: string[]; error?: string } | null>(null)
29
+ const [confirmDelete, setConfirmDelete] = useState(false)
30
+ const [deleting, setDeleting] = useState(false)
28
31
 
29
32
  const parseEnv = (text: string): Record<string, string> | undefined => {
30
33
  if (!text.trim()) return undefined
@@ -76,14 +79,17 @@ function McpServerForm({ editing, onClose, loadMcpServers }: {
76
79
 
77
80
  const handleDelete = async () => {
78
81
  if (!editing) return
79
- if (!confirm('Delete this MCP server?')) return
82
+ setDeleting(true)
80
83
  try {
81
84
  await api('DELETE', `/mcp-servers/${editing.id}`)
82
85
  toast.success('MCP server deleted')
83
86
  await loadMcpServers()
87
+ setConfirmDelete(false)
84
88
  onClose()
85
89
  } catch (err: unknown) {
86
90
  toast.error(err instanceof Error ? err.message : 'Failed to delete server')
91
+ } finally {
92
+ setDeleting(false)
87
93
  }
88
94
  }
89
95
 
@@ -216,7 +222,7 @@ function McpServerForm({ editing, onClose, loadMcpServers }: {
216
222
 
217
223
  <div className="flex gap-3 pt-2 border-t border-white/[0.04]">
218
224
  {editing && (
219
- <button onClick={handleDelete} className="py-3.5 px-6 rounded-[14px] border border-red-500/20 bg-transparent text-red-400 text-[15px] font-600 cursor-pointer hover:bg-red-500/10 transition-all" style={{ fontFamily: 'inherit' }}>
225
+ <button onClick={() => setConfirmDelete(true)} className="py-3.5 px-6 rounded-[14px] border border-red-500/20 bg-transparent text-red-400 text-[15px] font-600 cursor-pointer hover:bg-red-500/10 transition-all" style={{ fontFamily: 'inherit' }}>
220
226
  Delete
221
227
  </button>
222
228
  )}
@@ -227,6 +233,17 @@ function McpServerForm({ editing, onClose, loadMcpServers }: {
227
233
  {editing ? 'Save' : 'Create'}
228
234
  </button>
229
235
  </div>
236
+ <ConfirmDialog
237
+ open={confirmDelete}
238
+ title="Delete MCP Server?"
239
+ message={editing ? `Delete "${editing.name}"? This will remove the MCP server from the app.` : 'Delete this MCP server?'}
240
+ confirmLabel={deleting ? 'Deleting...' : 'Delete'}
241
+ confirmDisabled={deleting}
242
+ cancelDisabled={deleting}
243
+ danger
244
+ onConfirm={() => { void handleDelete() }}
245
+ onCancel={() => { if (!deleting) setConfirmDelete(false) }}
246
+ />
230
247
  </>
231
248
  )
232
249
  }