@swarmclawai/swarmclaw 0.5.3 → 0.6.2

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 (224) hide show
  1. package/README.md +53 -9
  2. package/bin/server-cmd.js +1 -0
  3. package/bin/swarmclaw.js +76 -16
  4. package/next.config.ts +11 -1
  5. package/package.json +5 -2
  6. package/scripts/postinstall.mjs +18 -0
  7. package/src/app/api/canvas/[sessionId]/route.ts +31 -0
  8. package/src/app/api/chatrooms/[id]/chat/route.ts +284 -0
  9. package/src/app/api/chatrooms/[id]/members/route.ts +82 -0
  10. package/src/app/api/chatrooms/[id]/pins/route.ts +39 -0
  11. package/src/app/api/chatrooms/[id]/reactions/route.ts +42 -0
  12. package/src/app/api/chatrooms/[id]/route.ts +84 -0
  13. package/src/app/api/chatrooms/route.ts +50 -0
  14. package/src/app/api/connectors/[id]/route.ts +1 -0
  15. package/src/app/api/connectors/route.ts +2 -1
  16. package/src/app/api/credentials/route.ts +2 -3
  17. package/src/app/api/files/open/route.ts +43 -0
  18. package/src/app/api/knowledge/[id]/route.ts +13 -2
  19. package/src/app/api/knowledge/route.ts +8 -1
  20. package/src/app/api/memory/route.ts +8 -0
  21. package/src/app/api/notifications/route.ts +4 -0
  22. package/src/app/api/orchestrator/run/route.ts +1 -1
  23. package/src/app/api/plugins/install/route.ts +2 -2
  24. package/src/app/api/search/route.ts +53 -1
  25. package/src/app/api/sessions/[id]/chat/route.ts +2 -0
  26. package/src/app/api/sessions/[id]/edit-resend/route.ts +1 -1
  27. package/src/app/api/sessions/[id]/fork/route.ts +1 -1
  28. package/src/app/api/sessions/[id]/messages/route.ts +70 -2
  29. package/src/app/api/sessions/[id]/route.ts +4 -0
  30. package/src/app/api/sessions/route.ts +3 -3
  31. package/src/app/api/settings/route.ts +9 -0
  32. package/src/app/api/setup/check-provider/route.ts +3 -16
  33. package/src/app/api/skills/[id]/route.ts +6 -0
  34. package/src/app/api/skills/route.ts +6 -0
  35. package/src/app/api/tasks/[id]/route.ts +12 -0
  36. package/src/app/api/tasks/bulk/route.ts +100 -0
  37. package/src/app/api/tasks/metrics/route.ts +101 -0
  38. package/src/app/api/tasks/route.ts +18 -2
  39. package/src/app/api/tts/route.ts +3 -2
  40. package/src/app/api/tts/stream/route.ts +3 -2
  41. package/src/app/api/uploads/[filename]/route.ts +19 -34
  42. package/src/app/api/uploads/route.ts +94 -0
  43. package/src/app/api/webhooks/[id]/route.ts +15 -1
  44. package/src/app/globals.css +63 -15
  45. package/src/app/page.tsx +142 -13
  46. package/src/cli/index.js +40 -1
  47. package/src/cli/index.test.js +30 -0
  48. package/src/cli/spec.js +42 -0
  49. package/src/components/agents/agent-avatar.tsx +57 -10
  50. package/src/components/agents/agent-card.tsx +50 -17
  51. package/src/components/agents/agent-chat-list.tsx +148 -12
  52. package/src/components/agents/agent-list.tsx +50 -19
  53. package/src/components/agents/agent-sheet.tsx +120 -65
  54. package/src/components/agents/inspector-panel.tsx +81 -6
  55. package/src/components/agents/openclaw-skills-panel.tsx +32 -3
  56. package/src/components/agents/personality-builder.tsx +42 -14
  57. package/src/components/agents/soul-library-picker.tsx +89 -0
  58. package/src/components/auth/access-key-gate.tsx +10 -3
  59. package/src/components/auth/setup-wizard.tsx +2 -2
  60. package/src/components/auth/user-picker.tsx +31 -3
  61. package/src/components/canvas/canvas-panel.tsx +96 -0
  62. package/src/components/chat/activity-moment.tsx +173 -0
  63. package/src/components/chat/chat-area.tsx +46 -22
  64. package/src/components/chat/chat-header.tsx +457 -286
  65. package/src/components/chat/chat-preview-panel.tsx +1 -2
  66. package/src/components/chat/chat-tool-toggles.tsx +1 -1
  67. package/src/components/chat/delegation-banner.tsx +371 -0
  68. package/src/components/chat/file-path-chip.tsx +146 -0
  69. package/src/components/chat/heartbeat-history-panel.tsx +269 -0
  70. package/src/components/chat/markdown-utils.ts +9 -0
  71. package/src/components/chat/message-bubble.tsx +356 -315
  72. package/src/components/chat/message-list.tsx +230 -8
  73. package/src/components/chat/streaming-bubble.tsx +104 -47
  74. package/src/components/chat/suggestions-bar.tsx +1 -1
  75. package/src/components/chat/thinking-indicator.tsx +72 -10
  76. package/src/components/chat/tool-call-bubble.tsx +111 -73
  77. package/src/components/chat/tool-request-banner.tsx +31 -7
  78. package/src/components/chat/transfer-agent-picker.tsx +63 -0
  79. package/src/components/chatrooms/agent-hover-card.tsx +124 -0
  80. package/src/components/chatrooms/chatroom-input.tsx +320 -0
  81. package/src/components/chatrooms/chatroom-list.tsx +130 -0
  82. package/src/components/chatrooms/chatroom-message.tsx +432 -0
  83. package/src/components/chatrooms/chatroom-sheet.tsx +215 -0
  84. package/src/components/chatrooms/chatroom-tool-request-banner.tsx +134 -0
  85. package/src/components/chatrooms/chatroom-typing-bar.tsx +88 -0
  86. package/src/components/chatrooms/chatroom-view.tsx +344 -0
  87. package/src/components/chatrooms/reaction-picker.tsx +273 -0
  88. package/src/components/connectors/connector-list.tsx +168 -90
  89. package/src/components/connectors/connector-sheet.tsx +95 -56
  90. package/src/components/home/home-view.tsx +501 -0
  91. package/src/components/input/chat-input.tsx +107 -43
  92. package/src/components/knowledge/knowledge-list.tsx +31 -1
  93. package/src/components/knowledge/knowledge-sheet.tsx +83 -2
  94. package/src/components/layout/app-layout.tsx +194 -97
  95. package/src/components/layout/update-banner.tsx +2 -2
  96. package/src/components/logs/log-list.tsx +2 -2
  97. package/src/components/mcp-servers/mcp-server-sheet.tsx +1 -1
  98. package/src/components/memory/memory-agent-list.tsx +143 -0
  99. package/src/components/memory/memory-browser.tsx +205 -0
  100. package/src/components/memory/memory-card.tsx +34 -7
  101. package/src/components/memory/memory-detail.tsx +359 -120
  102. package/src/components/memory/memory-sheet.tsx +157 -23
  103. package/src/components/plugins/plugin-list.tsx +1 -1
  104. package/src/components/plugins/plugin-sheet.tsx +1 -1
  105. package/src/components/projects/project-detail.tsx +509 -0
  106. package/src/components/projects/project-list.tsx +195 -59
  107. package/src/components/providers/provider-list.tsx +2 -2
  108. package/src/components/providers/provider-sheet.tsx +3 -3
  109. package/src/components/schedules/schedule-card.tsx +1 -1
  110. package/src/components/schedules/schedule-list.tsx +1 -1
  111. package/src/components/schedules/schedule-sheet.tsx +259 -126
  112. package/src/components/secrets/secret-sheet.tsx +47 -24
  113. package/src/components/secrets/secrets-list.tsx +18 -8
  114. package/src/components/sessions/new-session-sheet.tsx +33 -65
  115. package/src/components/sessions/session-card.tsx +45 -14
  116. package/src/components/sessions/session-list.tsx +35 -18
  117. package/src/components/settings/gateway-disconnect-overlay.tsx +80 -0
  118. package/src/components/shared/agent-picker-list.tsx +90 -0
  119. package/src/components/shared/agent-switch-dialog.tsx +156 -0
  120. package/src/components/shared/attachment-chip.tsx +165 -0
  121. package/src/components/shared/avatar.tsx +10 -1
  122. package/src/components/shared/chatroom-picker-list.tsx +61 -0
  123. package/src/components/shared/check-icon.tsx +12 -0
  124. package/src/components/shared/confirm-dialog.tsx +1 -1
  125. package/src/components/shared/connector-platform-icon.tsx +51 -4
  126. package/src/components/shared/empty-state.tsx +32 -0
  127. package/src/components/shared/file-preview.tsx +34 -0
  128. package/src/components/shared/form-styles.ts +2 -0
  129. package/src/components/shared/icon-button.tsx +16 -2
  130. package/src/components/shared/keyboard-shortcuts-dialog.tsx +116 -0
  131. package/src/components/shared/notification-center.tsx +44 -6
  132. package/src/components/shared/profile-sheet.tsx +115 -0
  133. package/src/components/shared/reply-quote.tsx +26 -0
  134. package/src/components/shared/search-dialog.tsx +31 -15
  135. package/src/components/shared/section-label.tsx +12 -0
  136. package/src/components/shared/settings/plugin-manager.tsx +1 -1
  137. package/src/components/shared/settings/section-embedding.tsx +48 -13
  138. package/src/components/shared/settings/section-orchestrator.tsx +46 -15
  139. package/src/components/shared/settings/section-providers.tsx +1 -1
  140. package/src/components/shared/settings/section-secrets.tsx +1 -1
  141. package/src/components/shared/settings/section-storage.tsx +206 -0
  142. package/src/components/shared/settings/section-theme.tsx +95 -0
  143. package/src/components/shared/settings/section-user-preferences.tsx +57 -0
  144. package/src/components/shared/settings/section-voice.tsx +42 -21
  145. package/src/components/shared/settings/section-web-search.tsx +30 -6
  146. package/src/components/shared/settings/settings-page.tsx +182 -27
  147. package/src/components/shared/settings/settings-sheet.tsx +9 -73
  148. package/src/components/shared/settings/storage-browser.tsx +259 -0
  149. package/src/components/shared/sheet-footer.tsx +33 -0
  150. package/src/components/skills/skill-list.tsx +61 -30
  151. package/src/components/skills/skill-sheet.tsx +81 -2
  152. package/src/components/tasks/task-board.tsx +448 -26
  153. package/src/components/tasks/task-card.tsx +59 -9
  154. package/src/components/tasks/task-column.tsx +62 -3
  155. package/src/components/tasks/task-list.tsx +12 -4
  156. package/src/components/tasks/task-sheet.tsx +416 -74
  157. package/src/components/ui/hover-card.tsx +52 -0
  158. package/src/components/usage/metrics-dashboard.tsx +90 -6
  159. package/src/components/usage/usage-list.tsx +1 -1
  160. package/src/components/webhooks/webhook-sheet.tsx +1 -1
  161. package/src/hooks/use-continuous-speech.ts +10 -4
  162. package/src/hooks/use-view-router.ts +69 -19
  163. package/src/hooks/use-voice-conversation.ts +53 -10
  164. package/src/hooks/use-ws.ts +4 -2
  165. package/src/instrumentation.ts +15 -1
  166. package/src/lib/chat.ts +2 -0
  167. package/src/lib/memory.ts +3 -0
  168. package/src/lib/providers/anthropic.ts +13 -7
  169. package/src/lib/providers/index.ts +1 -0
  170. package/src/lib/providers/openai.ts +13 -7
  171. package/src/lib/server/chat-execution.ts +75 -15
  172. package/src/lib/server/chatroom-helpers.ts +146 -0
  173. package/src/lib/server/connectors/manager.ts +229 -7
  174. package/src/lib/server/context-manager.ts +225 -13
  175. package/src/lib/server/create-notification.ts +14 -2
  176. package/src/lib/server/daemon-state.ts +157 -10
  177. package/src/lib/server/execution-log.ts +1 -0
  178. package/src/lib/server/heartbeat-service.ts +48 -6
  179. package/src/lib/server/heartbeat-wake.ts +110 -0
  180. package/src/lib/server/langgraph-checkpoint.ts +1 -0
  181. package/src/lib/server/main-agent-loop.ts +1 -1
  182. package/src/lib/server/memory-consolidation.ts +105 -0
  183. package/src/lib/server/memory-db.ts +183 -10
  184. package/src/lib/server/mime.ts +51 -0
  185. package/src/lib/server/openclaw-gateway.ts +9 -1
  186. package/src/lib/server/orchestrator-lg.ts +2 -0
  187. package/src/lib/server/orchestrator.ts +5 -2
  188. package/src/lib/server/playwright-proxy.mjs +2 -3
  189. package/src/lib/server/prompt-runtime-context.ts +53 -0
  190. package/src/lib/server/provider-health.ts +125 -0
  191. package/src/lib/server/queue.ts +56 -10
  192. package/src/lib/server/scheduler.ts +8 -0
  193. package/src/lib/server/session-run-manager.ts +4 -0
  194. package/src/lib/server/session-tools/canvas.ts +67 -0
  195. package/src/lib/server/session-tools/chatroom.ts +136 -0
  196. package/src/lib/server/session-tools/connector.ts +83 -9
  197. package/src/lib/server/session-tools/context-mgmt.ts +36 -18
  198. package/src/lib/server/session-tools/crud.ts +21 -0
  199. package/src/lib/server/session-tools/delegate.ts +68 -4
  200. package/src/lib/server/session-tools/git.ts +71 -0
  201. package/src/lib/server/session-tools/http.ts +57 -0
  202. package/src/lib/server/session-tools/index.ts +10 -0
  203. package/src/lib/server/session-tools/memory.ts +7 -1
  204. package/src/lib/server/session-tools/search-providers.ts +16 -8
  205. package/src/lib/server/session-tools/subagent.ts +106 -0
  206. package/src/lib/server/session-tools/web.ts +115 -4
  207. package/src/lib/server/storage.ts +53 -29
  208. package/src/lib/server/stream-agent-chat.ts +185 -57
  209. package/src/lib/server/system-events.ts +49 -0
  210. package/src/lib/server/task-mention.ts +41 -0
  211. package/src/lib/server/ws-hub.ts +11 -0
  212. package/src/lib/sessions.ts +10 -0
  213. package/src/lib/soul-library.ts +103 -0
  214. package/src/lib/soul-suggestions.ts +109 -0
  215. package/src/lib/task-dedupe.ts +26 -0
  216. package/src/lib/tasks.ts +4 -1
  217. package/src/lib/tool-definitions.ts +2 -0
  218. package/src/lib/tts.ts +2 -2
  219. package/src/lib/view-routes.ts +36 -1
  220. package/src/lib/ws-client.ts +14 -4
  221. package/src/stores/use-app-store.ts +41 -3
  222. package/src/stores/use-chat-store.ts +113 -5
  223. package/src/stores/use-chatroom-store.ts +276 -0
  224. package/src/types/index.ts +88 -4
@@ -8,6 +8,11 @@ import { useWs } from '@/hooks/use-ws'
8
8
  import { toast } from 'sonner'
9
9
  import type { Connector, ConnectorPlatform } from '@/types'
10
10
  import { ConnectorPlatformBadge } from '@/components/shared/connector-platform-icon'
11
+ import { AgentPickerList } from '@/components/shared/agent-picker-list'
12
+ import { ChatroomPickerList } from '@/components/shared/chatroom-picker-list'
13
+ import { SheetFooter } from '@/components/shared/sheet-footer'
14
+ import { SectionLabel } from '@/components/shared/section-label'
15
+ import { useChatroomStore } from '@/stores/use-chatroom-store'
11
16
 
12
17
  /** Auto-detect URLs in text and make them clickable links that open in a new tab */
13
18
  function linkify(text: string) {
@@ -229,9 +234,14 @@ export function ConnectorSheet() {
229
234
  const loadAgents = useAppStore((s) => s.loadAgents)
230
235
  const loadCredentials = useAppStore((s) => s.loadCredentials)
231
236
 
237
+ const chatrooms = useChatroomStore((s) => s.chatrooms)
238
+ const loadChatrooms = useChatroomStore((s) => s.loadChatrooms)
239
+
232
240
  const [name, setName] = useState('')
233
241
  const [platform, setPlatform] = useState<ConnectorPlatform>('discord')
234
242
  const [agentId, setAgentId] = useState('')
243
+ const [routeMode, setRouteMode] = useState<'agent' | 'chatroom'>('agent')
244
+ const [chatroomId, setChatroomId] = useState('')
235
245
  const [credentialId, setCredentialId] = useState('')
236
246
  const [config, setConfig] = useState<Record<string, string>>({})
237
247
  const [saving, setSaving] = useState(false)
@@ -252,8 +262,10 @@ export function ConnectorSheet() {
252
262
  if (open) {
253
263
  loadAgents()
254
264
  loadCredentials()
265
+ loadChatrooms()
255
266
  setShowSetup(false)
256
267
  }
268
+ // eslint-disable-next-line react-hooks/exhaustive-deps
257
269
  }, [open])
258
270
 
259
271
  // Sync form fields when editing connector changes (by ID, not reference)
@@ -262,13 +274,17 @@ export function ConnectorSheet() {
262
274
  if (editing) {
263
275
  setName(editing.name)
264
276
  setPlatform(editing.platform)
265
- setAgentId(editing.agentId)
277
+ setAgentId(editing.agentId || '')
278
+ setRouteMode(editing.chatroomId ? 'chatroom' : 'agent')
279
+ setChatroomId(editing.chatroomId || '')
266
280
  setCredentialId(editing.credentialId || '')
267
281
  setConfig(editing.config || {})
268
282
  } else {
269
283
  setName('')
270
284
  setPlatform('discord')
271
285
  setAgentId('')
286
+ setRouteMode('agent')
287
+ setChatroomId('')
272
288
  setCredentialId('')
273
289
  setConfig({})
274
290
  }
@@ -283,10 +299,10 @@ export function ConnectorSheet() {
283
299
  const pollWaStatus = useCallback(async () => {
284
300
  if (!editing) return
285
301
  try {
286
- const data = await api<any>('GET', `/connectors/${editing.id}`)
287
- setQrDataUrl(data.qrDataUrl || null)
288
- setWaAuthenticated(data.authenticated ?? false)
289
- setWaHasCreds(data.hasCredentials ?? false)
302
+ const data = await api<Record<string, unknown>>('GET', `/connectors/${editing.id}`)
303
+ setQrDataUrl((data.qrDataUrl as string | null) || null)
304
+ setWaAuthenticated((data.authenticated as boolean) ?? false)
305
+ setWaHasCreds((data.hasCredentials as boolean) ?? false)
290
306
  if (data.status === 'running' && editing.status !== 'running') {
291
307
  const store = useAppStore.getState()
292
308
  const updated = { ...store.connectors }
@@ -305,19 +321,23 @@ export function ConnectorSheet() {
305
321
  useWs('connectors', pollWaStatus, isWaRunning ? 2000 : undefined)
306
322
 
307
323
  const handleSave = async () => {
308
- if (!agentId) return
324
+ const hasTarget = routeMode === 'agent' ? !!agentId : !!chatroomId
325
+ if (!hasTarget) return
309
326
  setSaving(true)
327
+ const routePayload = routeMode === 'agent'
328
+ ? { agentId, chatroomId: null }
329
+ : { agentId: null, chatroomId }
310
330
  try {
311
331
  if (editing) {
312
- await api('PUT', `/connectors/${editing.id}`, { name, agentId, credentialId: credentialId || null, config })
332
+ await api('PUT', `/connectors/${editing.id}`, { name, ...routePayload, credentialId: credentialId || null, config })
313
333
  } else {
314
- await api('POST', '/connectors', { name: name || `${platformConfig?.label} Bot`, platform, agentId, credentialId: credentialId || null, config })
334
+ await api('POST', '/connectors', { name: name || `${platformConfig?.label} Bot`, platform, ...routePayload, credentialId: credentialId || null, config })
315
335
  }
316
336
  await loadConnectors()
317
337
  setOpen(false)
318
338
  setEditingId(null)
319
- } catch (err: any) {
320
- toast.error(err.message)
339
+ } catch (err: unknown) {
340
+ toast.error(err instanceof Error ? err.message : String(err))
321
341
  } finally {
322
342
  setSaving(false)
323
343
  }
@@ -340,9 +360,9 @@ export function ConnectorSheet() {
340
360
  setQrDataUrl(null)
341
361
  }
342
362
  await loadConnectors()
343
- } catch (err: any) {
363
+ } catch (err: unknown) {
344
364
  setWaConnecting(false)
345
- toast.error(`Failed to ${action}: ${err.message}`)
365
+ toast.error(`Failed to ${action}: ${err instanceof Error ? err.message : String(err)}`)
346
366
  } finally {
347
367
  setActionLoading(false)
348
368
  }
@@ -357,7 +377,7 @@ export function ConnectorSheet() {
357
377
  }
358
378
 
359
379
  const platformConfig = PLATFORMS.find((p) => p.id === platform)!
360
- const agentList = Object.values(agents)
380
+ const agentList = Object.values(agents).sort((a, b) => a.name.localeCompare(b.name))
361
381
  const credList = Object.values(credentials)
362
382
 
363
383
  const inputClass = "w-full px-4 py-3 rounded-[12px] border border-white/[0.08] bg-surface text-text text-[14px] outline-none transition-all placeholder:text-text-3/50 focus:border-white/[0.15]"
@@ -374,7 +394,7 @@ export function ConnectorSheet() {
374
394
  {/* Platform selector (only for new) */}
375
395
  {!editing && (
376
396
  <div className="mb-8">
377
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">Platform</label>
397
+ <SectionLabel>Platform</SectionLabel>
378
398
  <div className="grid grid-cols-2 gap-3">
379
399
  {PLATFORMS.map((p) => (
380
400
  <button
@@ -456,21 +476,51 @@ export function ConnectorSheet() {
456
476
  />
457
477
  </div>
458
478
 
459
- {/* Agent selector */}
479
+ {/* Route mode toggle + target selector */}
460
480
  <div className="mb-6">
461
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">Route to Agent</label>
462
- <p className="text-[12px] text-text-3/60 mb-2">Incoming messages will be handled by this agent</p>
463
- <select
464
- value={agentId || ''}
465
- onChange={(e) => setAgentId(e.target.value)}
466
- className={`${inputClass} appearance-none cursor-pointer`}
467
- style={{ fontFamily: 'inherit' }}
468
- >
469
- <option value="">Select a agent...</option>
470
- {agentList.map((p: any) => (
471
- <option key={p.id} value={p.id}>{p.name}{p.isOrchestrator ? ' (Orchestrator)' : ''}</option>
472
- ))}
473
- </select>
481
+ <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">Route Messages To</label>
482
+ <div className="flex gap-1 mb-3 p-1 rounded-[10px] bg-white/[0.04] border border-white/[0.06]">
483
+ <button
484
+ type="button"
485
+ onClick={() => setRouteMode('agent')}
486
+ className={`flex-1 py-2 px-3 rounded-[8px] text-[13px] font-600 transition-all cursor-pointer border-none ${
487
+ routeMode === 'agent' ? 'bg-accent-soft text-accent-bright' : 'bg-transparent text-text-3 hover:text-text-2'
488
+ }`}
489
+ style={{ fontFamily: 'inherit' }}
490
+ >
491
+ Single Agent
492
+ </button>
493
+ <button
494
+ type="button"
495
+ onClick={() => setRouteMode('chatroom')}
496
+ className={`flex-1 py-2 px-3 rounded-[8px] text-[13px] font-600 transition-all cursor-pointer border-none ${
497
+ routeMode === 'chatroom' ? 'bg-accent-soft text-accent-bright' : 'bg-transparent text-text-3 hover:text-text-2'
498
+ }`}
499
+ style={{ fontFamily: 'inherit' }}
500
+ >
501
+ Chat Room
502
+ </button>
503
+ </div>
504
+ {routeMode === 'agent' ? (
505
+ <>
506
+ <p className="text-[12px] text-text-3/60 mb-2">Incoming messages will be handled by this agent</p>
507
+ <AgentPickerList
508
+ agents={agentList}
509
+ selected={agentId}
510
+ onSelect={(id) => setAgentId(id)}
511
+ showOrchBadge={true}
512
+ />
513
+ </>
514
+ ) : (
515
+ <>
516
+ <p className="text-[12px] text-text-3/60 mb-2">Incoming messages will be routed to a chat room with multiple agents</p>
517
+ <ChatroomPickerList
518
+ chatrooms={Object.values(chatrooms)}
519
+ selected={chatroomId}
520
+ onSelect={(id) => setChatroomId(id)}
521
+ />
522
+ </>
523
+ )}
474
524
  </div>
475
525
 
476
526
  {/* Bot token credential */}
@@ -495,7 +545,7 @@ export function ConnectorSheet() {
495
545
  style={{ fontFamily: 'inherit' }}
496
546
  >
497
547
  <option value="">Select credential...</option>
498
- {credList.map((c: any) => (
548
+ {credList.map((c) => (
499
549
  <option key={c.id} value={c.id}>{c.name} ({c.provider})</option>
500
550
  ))}
501
551
  <option value="__new__">+ Add new key...</option>
@@ -547,7 +597,7 @@ export function ConnectorSheet() {
547
597
  onClick={async () => {
548
598
  setSavingCred(true)
549
599
  try {
550
- const cred = await api<any>('POST', '/credentials', {
600
+ const cred = await api<{ id: string }>('POST', '/credentials', {
551
601
  provider: platform,
552
602
  name: newCredName.trim() || `${platformConfig.label} Bot Token`,
553
603
  apiKey: newCredValue.trim(),
@@ -557,8 +607,8 @@ export function ConnectorSheet() {
557
607
  setShowNewCred(false)
558
608
  setNewCredName('')
559
609
  setNewCredValue('')
560
- } catch (err: any) {
561
- toast.error(`Failed to save: ${err.message}`)
610
+ } catch (err: unknown) {
611
+ toast.error(`Failed to save: ${err instanceof Error ? err.message : String(err)}`)
562
612
  } finally {
563
613
  setSavingCred(false)
564
614
  }
@@ -730,8 +780,8 @@ export function ConnectorSheet() {
730
780
  setQrDataUrl(null)
731
781
  setWaConnecting(true)
732
782
  await loadConnectors()
733
- } catch (err: any) {
734
- toast.error(`Failed to unlink: ${err.message}`)
783
+ } catch (err: unknown) {
784
+ toast.error(`Failed to unlink: ${err instanceof Error ? err.message : String(err)}`)
735
785
  } finally {
736
786
  setActionLoading(false)
737
787
  }
@@ -749,8 +799,8 @@ export function ConnectorSheet() {
749
799
  {editing && editing.platform === 'whatsapp' && (editing.status === 'running' || waConnecting) && !qrDataUrl && !waAuthenticated && (
750
800
  <div className="mb-6 p-5 rounded-[14px] border border-white/[0.06] bg-white/[0.01] text-center">
751
801
  <div className="flex items-center justify-center gap-2 mb-1">
752
- <span className="w-3 h-3 rounded-full border-2 border-[#3B82F6] border-t-transparent animate-spin" />
753
- <span className="text-[13px] font-600 text-[#3B82F6]">
802
+ <span className="w-3 h-3 rounded-full border-2 border-blue-500 border-t-transparent animate-spin" />
803
+ <span className="text-[13px] font-600 text-blue-500">
754
804
  {waHasCreds ? 'Reconnecting...' : 'Waiting for QR code...'}
755
805
  </span>
756
806
  </div>
@@ -771,8 +821,8 @@ export function ConnectorSheet() {
771
821
  setQrDataUrl(null)
772
822
  setWaConnecting(true)
773
823
  await loadConnectors()
774
- } catch (err: any) {
775
- toast.error(`Failed to re-pair: ${err.message}`)
824
+ } catch (err: unknown) {
825
+ toast.error(`Failed to re-pair: ${err instanceof Error ? err.message : String(err)}`)
776
826
  } finally {
777
827
  setActionLoading(false)
778
828
  }
@@ -796,28 +846,17 @@ export function ConnectorSheet() {
796
846
  )}
797
847
 
798
848
  {/* Actions */}
799
- <div className="flex gap-3 pt-4 border-t border-white/[0.04]">
800
- {editing && (
849
+ <SheetFooter
850
+ onCancel={() => { setOpen(false); setEditingId(null) }}
851
+ onSave={handleSave}
852
+ saveLabel={saving ? 'Saving...' : editing ? 'Save' : 'Create Connector'}
853
+ saveDisabled={saving || (routeMode === 'agent' ? !agentId : !chatroomId)}
854
+ left={editing && (
801
855
  <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' }}>
802
856
  Delete
803
857
  </button>
804
858
  )}
805
- <button
806
- onClick={() => { setOpen(false); setEditingId(null) }}
807
- className="flex-1 py-3.5 rounded-[14px] border border-white/[0.08] bg-transparent text-text-2 text-[15px] font-600 cursor-pointer hover:bg-surface-2 transition-all"
808
- style={{ fontFamily: 'inherit' }}
809
- >
810
- Cancel
811
- </button>
812
- <button
813
- onClick={handleSave}
814
- disabled={saving || !agentId}
815
- className="flex-1 py-3.5 rounded-[14px] border-none bg-[#6366F1] text-white text-[15px] font-600 cursor-pointer active:scale-[0.97] disabled:opacity-30 transition-all shadow-[0_4px_20px_rgba(99,102,241,0.25)] hover:brightness-110"
816
- style={{ fontFamily: 'inherit' }}
817
- >
818
- {saving ? 'Saving...' : editing ? 'Save' : 'Create Connector'}
819
- </button>
820
- </div>
859
+ />
821
860
  </BottomSheet>
822
861
  )
823
862
  }