@swarmclawai/swarmclaw 0.7.8 → 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 (251) hide show
  1. package/README.md +12 -15
  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 +22 -2
  15. package/src/app/api/clawhub/install/route.ts +28 -8
  16. package/src/app/api/connectors/[id]/route.ts +26 -1
  17. package/src/app/api/external-agents/route.test.ts +165 -0
  18. package/src/app/api/gateways/[id]/health/route.ts +27 -12
  19. package/src/app/api/gateways/[id]/route.ts +2 -0
  20. package/src/app/api/gateways/health-route.test.ts +135 -0
  21. package/src/app/api/gateways/route.ts +2 -0
  22. package/src/app/api/mcp-servers/route.test.ts +130 -0
  23. package/src/app/api/openclaw/deploy/route.ts +38 -5
  24. package/src/app/api/plugins/install/route.ts +46 -6
  25. package/src/app/api/plugins/marketplace/route.ts +48 -15
  26. package/src/app/api/preview-server/route.ts +26 -11
  27. package/src/app/api/schedules/[id]/run/route.ts +4 -0
  28. package/src/app/api/schedules/route.test.ts +86 -0
  29. package/src/app/api/schedules/route.ts +6 -1
  30. package/src/app/api/setup/check-provider/route.test.ts +19 -0
  31. package/src/app/api/setup/check-provider/route.ts +40 -10
  32. package/src/app/api/skills/[id]/route.ts +12 -0
  33. package/src/app/api/skills/import/route.ts +14 -12
  34. package/src/app/api/skills/route.ts +13 -1
  35. package/src/app/api/tasks/[id]/route.ts +10 -1
  36. package/src/app/api/tasks/import/github/route.test.ts +65 -0
  37. package/src/app/api/tasks/import/github/route.ts +337 -0
  38. package/src/app/api/wallets/[id]/approve/route.ts +17 -3
  39. package/src/app/api/wallets/[id]/route.ts +79 -33
  40. package/src/app/api/wallets/[id]/send/route.ts +19 -33
  41. package/src/app/api/wallets/route.ts +78 -61
  42. package/src/app/api/webhooks/[id]/route.ts +33 -6
  43. package/src/app/api/webhooks/route.test.ts +272 -0
  44. package/src/cli/index.js +1 -0
  45. package/src/cli/spec.js +1 -0
  46. package/src/components/agents/agent-card.tsx +9 -2
  47. package/src/components/agents/agent-chat-list.tsx +18 -2
  48. package/src/components/agents/agent-list.tsx +1 -0
  49. package/src/components/agents/agent-sheet.tsx +73 -24
  50. package/src/components/agents/inspector-panel.tsx +41 -0
  51. package/src/components/canvas/canvas-panel.tsx +236 -65
  52. package/src/components/chat/chat-card.tsx +36 -13
  53. package/src/components/chat/chat-header.tsx +44 -16
  54. package/src/components/chat/chat-list.tsx +28 -4
  55. package/src/components/chat/checkpoint-timeline.tsx +50 -34
  56. package/src/components/chat/message-bubble.tsx +208 -145
  57. package/src/components/chat/message-list.tsx +48 -19
  58. package/src/components/chatrooms/chatroom-message.tsx +2 -2
  59. package/src/components/chatrooms/chatroom-sheet.tsx +16 -2
  60. package/src/components/connectors/connector-health.tsx +1 -1
  61. package/src/components/connectors/connector-list.tsx +7 -2
  62. package/src/components/connectors/connector-sheet.tsx +337 -148
  63. package/src/components/gateways/gateway-sheet.tsx +2 -2
  64. package/src/components/mcp-servers/mcp-server-list.tsx +26 -5
  65. package/src/components/mcp-servers/mcp-server-sheet.tsx +19 -2
  66. package/src/components/openclaw/openclaw-deploy-panel.tsx +269 -21
  67. package/src/components/plugins/plugin-list.tsx +45 -9
  68. package/src/components/plugins/plugin-sheet.tsx +55 -7
  69. package/src/components/providers/provider-list.tsx +2 -1
  70. package/src/components/providers/provider-sheet.tsx +21 -2
  71. package/src/components/schedules/schedule-card.tsx +25 -1
  72. package/src/components/schedules/schedule-sheet.tsx +44 -2
  73. package/src/components/secrets/secret-sheet.tsx +21 -2
  74. package/src/components/shared/agent-switch-dialog.tsx +12 -1
  75. package/src/components/shared/bottom-sheet.tsx +13 -3
  76. package/src/components/shared/command-palette.tsx +8 -1
  77. package/src/components/shared/confirm-dialog.tsx +19 -4
  78. package/src/components/shared/connector-platform-icon.test.ts +28 -0
  79. package/src/components/shared/connector-platform-icon.tsx +39 -6
  80. package/src/components/shared/settings/plugin-manager.tsx +29 -6
  81. package/src/components/shared/settings/section-capability-policy.tsx +7 -3
  82. package/src/components/skills/skill-list.tsx +25 -0
  83. package/src/components/skills/skill-sheet.tsx +84 -12
  84. package/src/components/tasks/approvals-panel.tsx +191 -95
  85. package/src/components/tasks/task-board.tsx +273 -2
  86. package/src/components/tasks/task-card.tsx +38 -9
  87. package/src/components/ui/dialog.tsx +2 -2
  88. package/src/components/wallets/wallet-approval-dialog.tsx +4 -2
  89. package/src/components/wallets/wallet-panel.tsx +435 -90
  90. package/src/components/wallets/wallet-section.tsx +198 -48
  91. package/src/components/webhooks/webhook-sheet.tsx +22 -2
  92. package/src/lib/approval-display.ts +20 -0
  93. package/src/lib/canvas-content.ts +198 -0
  94. package/src/lib/chat-artifact-summary.ts +165 -0
  95. package/src/lib/chat-display.test.ts +91 -0
  96. package/src/lib/chat-display.ts +58 -0
  97. package/src/lib/chat-streaming-state.test.ts +47 -1
  98. package/src/lib/chat-streaming-state.ts +42 -0
  99. package/src/lib/ollama-model.ts +10 -0
  100. package/src/lib/openclaw-endpoint.test.ts +8 -0
  101. package/src/lib/openclaw-endpoint.ts +6 -1
  102. package/src/lib/plugin-install-cors.ts +46 -0
  103. package/src/lib/plugin-sources.test.ts +43 -0
  104. package/src/lib/plugin-sources.ts +77 -0
  105. package/src/lib/providers/ollama.ts +16 -6
  106. package/src/lib/providers/openclaw.test.ts +54 -0
  107. package/src/lib/providers/openclaw.ts +127 -11
  108. package/src/lib/schedule-dedupe-advanced.test.ts +1335 -0
  109. package/src/lib/schedule-dedupe.test.ts +66 -1
  110. package/src/lib/schedule-dedupe.ts +169 -12
  111. package/src/lib/schedule-origin.test.ts +20 -0
  112. package/src/lib/schedule-origin.ts +15 -0
  113. package/src/lib/server/__fixtures__/fake-mcp-stdio-server.mjs +27 -0
  114. package/src/lib/server/agent-availability.ts +16 -0
  115. package/src/lib/server/agent-runtime-config.ts +12 -4
  116. package/src/lib/server/agent-thread-session.test.ts +51 -0
  117. package/src/lib/server/agent-thread-session.ts +7 -0
  118. package/src/lib/server/approval-match.ts +205 -0
  119. package/src/lib/server/approvals-auto-approve.test.ts +538 -1
  120. package/src/lib/server/approvals.ts +214 -1
  121. package/src/lib/server/assistant-control.test.ts +29 -0
  122. package/src/lib/server/assistant-control.ts +23 -0
  123. package/src/lib/server/build-llm.test.ts +79 -0
  124. package/src/lib/server/build-llm.ts +14 -4
  125. package/src/lib/server/canvas-content.test.ts +32 -0
  126. package/src/lib/server/canvas-content.ts +6 -0
  127. package/src/lib/server/capability-router.test.ts +11 -0
  128. package/src/lib/server/capability-router.ts +26 -1
  129. package/src/lib/server/chat-execution-advanced.test.ts +651 -0
  130. package/src/lib/server/chat-execution-disabled.test.ts +94 -0
  131. package/src/lib/server/chat-execution-tool-events.test.ts +157 -0
  132. package/src/lib/server/chat-execution.ts +353 -72
  133. package/src/lib/server/clawhub-client.test.ts +14 -8
  134. package/src/lib/server/connectors/manager.test.ts +1147 -0
  135. package/src/lib/server/connectors/manager.ts +362 -63
  136. package/src/lib/server/connectors/pairing.ts +26 -5
  137. package/src/lib/server/connectors/types.ts +2 -0
  138. package/src/lib/server/connectors/whatsapp.test.ts +134 -0
  139. package/src/lib/server/connectors/whatsapp.ts +271 -47
  140. package/src/lib/server/context-manager.ts +6 -1
  141. package/src/lib/server/daemon-state.ts +1 -1
  142. package/src/lib/server/data-dir.test.ts +37 -0
  143. package/src/lib/server/data-dir.ts +20 -1
  144. package/src/lib/server/delegation-jobs-advanced.test.ts +513 -0
  145. package/src/lib/server/devserver-launch.test.ts +60 -0
  146. package/src/lib/server/devserver-launch.ts +85 -0
  147. package/src/lib/server/elevenlabs.test.ts +189 -1
  148. package/src/lib/server/elevenlabs.ts +147 -43
  149. package/src/lib/server/ethereum.ts +590 -0
  150. package/src/lib/server/eval/agent-regression-advanced.test.ts +302 -0
  151. package/src/lib/server/eval/agent-regression.test.ts +18 -1
  152. package/src/lib/server/eval/agent-regression.ts +383 -11
  153. package/src/lib/server/evm-swap.ts +475 -0
  154. package/src/lib/server/execution-log.ts +1 -0
  155. package/src/lib/server/heartbeat-service-timer.test.ts +173 -0
  156. package/src/lib/server/heartbeat-service.ts +15 -10
  157. package/src/lib/server/heartbeat-wake.test.ts +112 -0
  158. package/src/lib/server/heartbeat-wake.ts +338 -57
  159. package/src/lib/server/main-agent-loop-advanced.test.ts +538 -0
  160. package/src/lib/server/mcp-client.test.ts +16 -0
  161. package/src/lib/server/mcp-client.ts +25 -0
  162. package/src/lib/server/memory-integration.test.ts +719 -0
  163. package/src/lib/server/memory-policy.test.ts +43 -0
  164. package/src/lib/server/memory-policy.ts +132 -0
  165. package/src/lib/server/memory-tiers.test.ts +60 -0
  166. package/src/lib/server/memory-tiers.ts +16 -0
  167. package/src/lib/server/ollama-runtime.ts +58 -0
  168. package/src/lib/server/openclaw-deploy.test.ts +109 -1
  169. package/src/lib/server/openclaw-deploy.ts +557 -81
  170. package/src/lib/server/openclaw-gateway.test.ts +131 -0
  171. package/src/lib/server/openclaw-gateway.ts +10 -4
  172. package/src/lib/server/openclaw-health.test.ts +35 -0
  173. package/src/lib/server/openclaw-health.ts +215 -47
  174. package/src/lib/server/orchestrator-lg.ts +2 -2
  175. package/src/lib/server/plugins-advanced.test.ts +351 -0
  176. package/src/lib/server/plugins.ts +205 -5
  177. package/src/lib/server/queue-advanced.test.ts +528 -0
  178. package/src/lib/server/queue-followups.test.ts +262 -0
  179. package/src/lib/server/queue-reconcile.test.ts +128 -0
  180. package/src/lib/server/queue.ts +293 -61
  181. package/src/lib/server/scheduler.ts +29 -1
  182. package/src/lib/server/session-note.test.ts +36 -0
  183. package/src/lib/server/session-note.ts +42 -0
  184. package/src/lib/server/session-run-manager.ts +52 -4
  185. package/src/lib/server/session-tools/canvas.ts +14 -12
  186. package/src/lib/server/session-tools/connector.test.ts +138 -0
  187. package/src/lib/server/session-tools/connector.ts +348 -61
  188. package/src/lib/server/session-tools/context.ts +12 -3
  189. package/src/lib/server/session-tools/crud.ts +221 -10
  190. package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
  191. package/src/lib/server/session-tools/delegate.ts +64 -8
  192. package/src/lib/server/session-tools/discovery-approvals.test.ts +142 -0
  193. package/src/lib/server/session-tools/discovery.ts +80 -12
  194. package/src/lib/server/session-tools/file-normalize.test.ts +36 -0
  195. package/src/lib/server/session-tools/file.ts +43 -4
  196. package/src/lib/server/session-tools/human-loop.ts +35 -5
  197. package/src/lib/server/session-tools/index.ts +44 -9
  198. package/src/lib/server/session-tools/manage-connectors.test.ts +139 -0
  199. package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +564 -0
  200. package/src/lib/server/session-tools/manage-schedules.test.ts +283 -0
  201. package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +852 -0
  202. package/src/lib/server/session-tools/memory.test.ts +93 -0
  203. package/src/lib/server/session-tools/memory.ts +546 -79
  204. package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
  205. package/src/lib/server/session-tools/plugin-creator.ts +57 -1
  206. package/src/lib/server/session-tools/primitive-tools.test.ts +6 -0
  207. package/src/lib/server/session-tools/schedule.ts +6 -1
  208. package/src/lib/server/session-tools/shell-normalize.test.ts +25 -1
  209. package/src/lib/server/session-tools/shell.ts +22 -3
  210. package/src/lib/server/session-tools/wallet-tool.test.ts +254 -0
  211. package/src/lib/server/session-tools/wallet.ts +1374 -139
  212. package/src/lib/server/session-tools/web-inputs.test.ts +162 -1
  213. package/src/lib/server/session-tools/web.ts +468 -64
  214. package/src/lib/server/skill-discovery.ts +128 -0
  215. package/src/lib/server/skill-eligibility.test.ts +84 -0
  216. package/src/lib/server/skill-eligibility.ts +95 -0
  217. package/src/lib/server/skill-prompt-budget.test.ts +102 -0
  218. package/src/lib/server/skill-prompt-budget.ts +125 -0
  219. package/src/lib/server/skills-normalize.test.ts +54 -0
  220. package/src/lib/server/skills-normalize.ts +372 -26
  221. package/src/lib/server/solana.ts +214 -29
  222. package/src/lib/server/storage.ts +65 -36
  223. package/src/lib/server/stream-agent-chat.test.ts +419 -9
  224. package/src/lib/server/stream-agent-chat.ts +887 -83
  225. package/src/lib/server/system-events.ts +1 -1
  226. package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
  227. package/src/lib/server/tool-loop-detection.test.ts +105 -0
  228. package/src/lib/server/tool-loop-detection.ts +260 -0
  229. package/src/lib/server/tool-planning.ts +4 -2
  230. package/src/lib/server/wallet-execution.test.ts +198 -0
  231. package/src/lib/server/wallet-portfolio.test.ts +98 -0
  232. package/src/lib/server/wallet-portfolio.ts +724 -0
  233. package/src/lib/server/wallet-service.test.ts +57 -0
  234. package/src/lib/server/wallet-service.ts +213 -0
  235. package/src/lib/server/watch-jobs-advanced.test.ts +594 -0
  236. package/src/lib/server/watch-jobs.ts +17 -2
  237. package/src/lib/server/workspace-context.ts +111 -0
  238. package/src/lib/skill-save-payload.test.ts +39 -0
  239. package/src/lib/skill-save-payload.ts +37 -0
  240. package/src/lib/tasks.ts +28 -0
  241. package/src/lib/tool-event-summary.test.ts +30 -0
  242. package/src/lib/tool-event-summary.ts +37 -0
  243. package/src/lib/validation/schemas.ts +1 -0
  244. package/src/lib/wallet-transactions.test.ts +75 -0
  245. package/src/lib/wallet-transactions.ts +43 -0
  246. package/src/lib/wallet.test.ts +17 -0
  247. package/src/lib/wallet.ts +183 -0
  248. package/src/proxy.test.ts +31 -0
  249. package/src/proxy.ts +34 -2
  250. package/src/stores/use-chat-store.ts +15 -1
  251. package/src/types/index.ts +210 -14
@@ -13,27 +13,33 @@ import type { AppSettings, ApprovalCategory, ApprovalRequest } from '@/types'
13
13
  const CATEGORY_LABELS: Record<string, string> = {
14
14
  tool_access: 'Plugin Access',
15
15
  wallet_transfer: 'Wallet Transfer',
16
+ wallet_action: 'Wallet Action',
16
17
  plugin_scaffold: 'Plugin Creation',
17
18
  plugin_install: 'Plugin Install',
19
+ connector_sender: 'Connector Sender',
18
20
  task_tool: 'Task Plugin Call',
19
21
  }
20
22
 
21
23
  const CATEGORY_ICONS: Record<string, string> = {
22
24
  tool_access: '🔑',
23
25
  wallet_transfer: '💰',
26
+ wallet_action: '✍️',
24
27
  plugin_scaffold: '🔌',
25
28
  plugin_install: '📦',
29
+ connector_sender: '📲',
26
30
  task_tool: '🤖',
27
31
  }
28
32
 
29
33
  type ApprovalScope = 'all' | 'execution' | 'workflow' | 'task'
30
34
 
31
- const AUTO_APPROVE_OPTIONS: Array<{ id: ApprovalCategory; label: string; description: string }> = [
35
+ const AUTO_APPROVE_OPTIONS: Array<{ id: ApprovalCategory; label: string; description: string; risk?: 'high' | 'very-high' }> = [
32
36
  { id: 'tool_access', label: 'Plugin Access', description: 'Auto-enable requested plugins for a chat.' },
33
37
  { id: 'plugin_scaffold', label: 'Plugin Scaffold', description: 'Auto-create plugin files requested by agents.' },
34
38
  { id: 'plugin_install', label: 'Plugin Install', description: 'Auto-install plugins from approved URLs.' },
39
+ { id: 'connector_sender', label: 'Connector Senders', description: 'Auto-approve new connector senders and add them to the allowlist.' },
35
40
  { id: 'human_loop', label: 'Human Approval Requests', description: 'Auto-approve ask-human approval prompts.' },
36
- { id: 'wallet_transfer', label: 'Wallet Transfers', description: 'Auto-approve wallet send requests. High risk.' },
41
+ { id: 'wallet_transfer', label: 'Wallet Transfers', description: 'Auto-approve wallet send requests.', risk: 'high' },
42
+ { id: 'wallet_action', label: 'Wallet Actions', description: 'Auto-approve wallet signatures and arbitrary transaction requests.', risk: 'very-high' },
37
43
  { id: 'task_tool', label: 'Task Tool Calls', description: 'Auto-approve task-level tool approvals.' },
38
44
  ]
39
45
 
@@ -174,31 +180,37 @@ export function ApprovalsPanel() {
174
180
  label: 'Execution',
175
181
  value: sortedExecApprovals.length,
176
182
  tone: 'text-amber-400',
183
+ dotClass: 'bg-amber-400',
177
184
  hint: 'Command approvals from OpenClaw',
178
185
  },
179
186
  {
180
187
  label: 'Workflow',
181
188
  value: sessionApprovals.length,
182
189
  tone: 'text-sky-400',
190
+ dotClass: 'bg-sky-400',
183
191
  hint: 'Agent and plugin governance requests',
184
192
  },
185
193
  {
186
194
  label: 'Task Calls',
187
195
  value: taskApprovals.length,
188
196
  tone: 'text-violet-400',
197
+ dotClass: 'bg-violet-400',
189
198
  hint: 'Tasks waiting on tool approval',
190
199
  },
191
200
  {
192
201
  label: 'Recently Active',
193
202
  value: workflowApprovals.filter((req) => now - req.updatedAt < 60 * 60 * 1000).length,
194
203
  tone: 'text-emerald-400',
204
+ dotClass: 'bg-emerald-400',
195
205
  hint: 'Updated in the last hour',
196
206
  },
197
207
  ]
198
208
 
199
209
  const autoApproved = useMemo(() => new Set(appSettings.approvalAutoApproveCategories || []), [appSettings.approvalAutoApproveCategories])
200
- const approvalsEnabled = appSettings.approvalsEnabled ?? true
210
+ const approvalsEnabled = appSettings.approvalsEnabled ?? false
201
211
  const outboundApprovalEnabled = appSettings.safetyRequireApprovalForOutbound ?? false
212
+ const autoApproveEnabledCount = autoApproved.size
213
+ const autoApproveManualCount = AUTO_APPROVE_OPTIONS.length - autoApproveEnabledCount
202
214
 
203
215
  const saveApprovalSettings = async (patch: Partial<AppSettings>, successMessage: string, key: string) => {
204
216
  try {
@@ -230,117 +242,181 @@ export function ApprovalsPanel() {
230
242
 
231
243
  return (
232
244
  <div className="flex-1 overflow-y-auto px-6 py-8">
233
- <div className="max-w-3xl mx-auto">
234
- <div className="flex items-center justify-between mb-8">
245
+ <div className="max-w-5xl mx-auto space-y-6">
246
+ <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
235
247
  <div>
236
248
  <h1 className="font-display text-[28px] font-700 tracking-[-0.03em] mb-1">Approvals</h1>
237
249
  <p className="text-[13px] text-text-3">Execution, task, and governance requests queued for review</p>
238
250
  </div>
239
- <div className="px-3 py-1.5 rounded-full bg-amber-500/10 border border-amber-500/20 text-amber-400 text-[11px] font-600">
240
- {pendingCount} Pending
251
+ <div className="flex flex-wrap items-center gap-2">
252
+ <div className={`px-3 py-1.5 rounded-full border text-[11px] font-700 ${
253
+ approvalsEnabled
254
+ ? 'bg-amber-500/10 border-amber-500/20 text-amber-300'
255
+ : 'bg-emerald-500/10 border-emerald-500/20 text-emerald-300'
256
+ }`}>
257
+ {approvalsEnabled ? 'Manual approvals enabled' : 'Approvals auto-run'}
258
+ </div>
259
+ <div className="px-3 py-1.5 rounded-full bg-white/[0.04] border border-white/[0.06] text-text-2 text-[11px] font-600">
260
+ {pendingCount} pending
261
+ </div>
241
262
  </div>
242
263
  </div>
243
264
 
244
- <div className="grid grid-cols-2 lg:grid-cols-4 gap-3 mb-6">
265
+ <div className="grid grid-cols-2 xl:grid-cols-4 gap-3">
245
266
  {summaryCards.map((card) => (
246
- <div key={card.label} className="rounded-[14px] border border-white/[0.06] bg-white/[0.02] px-4 py-3.5">
267
+ <div key={card.label} className="relative overflow-hidden rounded-[16px] border border-white/[0.06] bg-white/[0.02] px-4 py-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]">
268
+ <div className="flex items-center justify-between gap-3">
269
+ <div className="text-[11px] font-700 uppercase tracking-[0.08em] text-text-3/65">{card.label}</div>
270
+ <div className={`h-2.5 w-2.5 rounded-full ${card.dotClass}`} />
271
+ </div>
247
272
  <div className={`text-[22px] font-display font-700 tracking-[-0.03em] ${card.tone}`}>
248
273
  {card.value}
249
274
  </div>
250
- <div className="text-[11px] font-600 text-text-2 mt-0.5">{card.label}</div>
251
- <p className="text-[10px] text-text-3/50 mt-1 leading-relaxed">{card.hint}</p>
275
+ <p className="text-[10px] text-text-3/55 mt-1.5 leading-relaxed">{card.hint}</p>
252
276
  </div>
253
277
  ))}
254
278
  </div>
255
279
 
256
- <div className="rounded-[16px] border border-white/[0.06] bg-white/[0.02] p-4 mb-6">
257
- <div className="flex flex-col gap-4">
258
- <div className="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-3">
280
+ <div className="grid gap-4 xl:grid-cols-[320px_minmax(0,1fr)]">
281
+ <div className="rounded-[18px] border border-white/[0.06] bg-white/[0.02] p-5 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]">
282
+ <div className="flex items-center justify-between gap-3 mb-4">
259
283
  <div>
260
- <h2 className="text-[13px] font-700 text-text">Approval Controls</h2>
261
- <p className="text-[12px] text-text-3/70 mt-1 max-w-[640px]">
262
- Control whether actions queue for review, which approval types auto-run, and whether outbound connector sends need explicit confirmation.
263
- </p>
284
+ <div className="text-[11px] font-700 uppercase tracking-[0.08em] text-text-3/65">Approval Mode</div>
285
+ <div className="text-[16px] font-display font-700 tracking-[-0.02em] text-text mt-1">
286
+ {approvalsEnabled ? 'Manual review queue' : 'Auto-run workflow mode'}
287
+ </div>
264
288
  </div>
265
- <div className={`px-3 py-1.5 rounded-full text-[11px] font-700 ${
289
+ <div className={`px-2.5 py-1 rounded-full text-[10px] font-700 border ${
266
290
  approvalsEnabled
267
- ? 'bg-amber-500/10 border border-amber-500/20 text-amber-300'
268
- : 'bg-emerald-500/10 border border-emerald-500/20 text-emerald-300'
291
+ ? 'bg-amber-500/10 border-amber-500/20 text-amber-300'
292
+ : 'bg-emerald-500/10 border-emerald-500/20 text-emerald-300'
269
293
  }`}>
270
- {approvalsEnabled ? 'Manual approvals enabled' : 'Approvals disabled'}
294
+ {approvalsEnabled ? 'On' : 'Off'}
271
295
  </div>
272
296
  </div>
297
+ <p className="text-[12px] text-text-3/70 leading-relaxed">
298
+ {approvalsEnabled
299
+ ? 'Requests pause here until someone approves or rejects them. Auto-approve lets you carve out safe request classes without disabling oversight entirely.'
300
+ : 'Workflow approvals will auto-run by default. Use outbound and category-specific controls below to keep higher-risk actions gated.'}
301
+ </p>
302
+ <div className="grid grid-cols-1 gap-2 mt-5">
303
+ <div className="rounded-[12px] border border-white/[0.06] bg-black/20 px-3.5 py-3">
304
+ <div className="text-[10px] font-700 uppercase tracking-[0.08em] text-text-3/55">Queue</div>
305
+ <div className="mt-1 flex items-center justify-between gap-3">
306
+ <span className="text-[13px] font-600 text-text-2">Pending right now</span>
307
+ <span className="text-[13px] font-700 text-text">{pendingCount}</span>
308
+ </div>
309
+ </div>
310
+ <div className="rounded-[12px] border border-white/[0.06] bg-black/20 px-3.5 py-3">
311
+ <div className="text-[10px] font-700 uppercase tracking-[0.08em] text-text-3/55">Auto-Approve</div>
312
+ <div className="mt-1 flex items-center justify-between gap-3">
313
+ <span className="text-[13px] font-600 text-text-2">Categories enabled</span>
314
+ <span className="text-[13px] font-700 text-text">{autoApproveEnabledCount}</span>
315
+ </div>
316
+ <div className="text-[11px] text-text-3/55 mt-1">{autoApproveManualCount} still require manual review</div>
317
+ </div>
318
+ <div className="rounded-[12px] border border-white/[0.06] bg-black/20 px-3.5 py-3">
319
+ <div className="text-[10px] font-700 uppercase tracking-[0.08em] text-text-3/55">Outbound Sends</div>
320
+ <div className="mt-1 flex items-center justify-between gap-3">
321
+ <span className="text-[13px] font-600 text-text-2">Connector message sends</span>
322
+ <span className={`text-[11px] font-700 ${outboundApprovalEnabled ? 'text-amber-300' : 'text-emerald-300'}`}>
323
+ {outboundApprovalEnabled ? 'Needs approval' : 'Direct send'}
324
+ </span>
325
+ </div>
326
+ </div>
327
+ </div>
328
+ </div>
273
329
 
274
- <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
275
- <div className="rounded-[12px] border border-white/[0.06] bg-black/20 px-4 py-4">
276
- <div className="flex items-center justify-between gap-4">
277
- <div>
278
- <div className="text-[12px] font-600 text-text-2">Platform Approvals</div>
279
- <p className="text-[11px] text-text-3/60 mt-1 leading-relaxed">
280
- Turn this off to auto-approve workflow approvals across the app. Audit records are still kept.
281
- </p>
282
- </div>
283
- <button
284
- type="button"
285
- disabled={savingSetting === 'approvalsEnabled'}
286
- onClick={() => {
287
- const next = !approvalsEnabled
288
- void saveApprovalSettings(
289
- { approvalsEnabled: next },
290
- next ? 'Platform approvals enabled' : 'Platform approvals disabled',
291
- 'approvalsEnabled',
292
- )
293
- }}
294
- className={`relative w-10 h-[22px] rounded-full transition-colors duration-200 cursor-pointer disabled:opacity-50 ${approvalsEnabled ? 'bg-accent' : 'bg-white/[0.12]'}`}
295
- aria-label="Toggle platform approvals"
296
- >
297
- <span className={`absolute top-[3px] left-[3px] w-4 h-4 rounded-full bg-white transition-transform duration-200 ${approvalsEnabled ? 'translate-x-[18px]' : ''}`} />
298
- </button>
330
+ <div className="rounded-[18px] border border-white/[0.06] bg-white/[0.02] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]">
331
+ <div className="flex flex-col gap-4">
332
+ <div className="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-3">
333
+ <div>
334
+ <h2 className="text-[13px] font-700 text-text">Approval Controls</h2>
335
+ <p className="text-[12px] text-text-3/70 mt-1 max-w-[640px]">
336
+ Control whether actions queue for review, which approval types auto-run, and whether outbound connector sends need explicit confirmation.
337
+ </p>
299
338
  </div>
300
339
  </div>
301
340
 
302
- <div className="rounded-[12px] border border-white/[0.06] bg-black/20 px-4 py-4">
303
- <div className="flex items-center justify-between gap-4">
304
- <div>
305
- <div className="text-[12px] font-600 text-text-2">Outbound Send Approvals</div>
306
- <p className="text-[11px] text-text-3/60 mt-1 leading-relaxed">
307
- Require explicit approval before agents send messages or media over connectors.
308
- </p>
341
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
342
+ <div className="rounded-[14px] border border-white/[0.06] bg-black/20 px-4 py-4">
343
+ <div className="flex items-center justify-between gap-4">
344
+ <div>
345
+ <div className="text-[12px] font-600 text-text-2">Platform Approvals</div>
346
+ <p className="text-[11px] text-text-3/60 mt-1 leading-relaxed">
347
+ Turn this off to auto-approve workflow approvals across the app. Audit records are still kept.
348
+ </p>
349
+ </div>
350
+ <button
351
+ type="button"
352
+ disabled={savingSetting === 'approvalsEnabled'}
353
+ onClick={() => {
354
+ const next = !approvalsEnabled
355
+ void saveApprovalSettings(
356
+ { approvalsEnabled: next },
357
+ next ? 'Platform approvals enabled' : 'Platform approvals disabled',
358
+ 'approvalsEnabled',
359
+ )
360
+ }}
361
+ className={`inline-flex h-[22px] w-10 shrink-0 items-center rounded-full border border-white/[0.08] p-[3px] transition-colors duration-200 cursor-pointer disabled:opacity-50 ${
362
+ approvalsEnabled ? 'justify-end bg-accent' : 'justify-start bg-white/[0.16]'
363
+ }`}
364
+ aria-label="Toggle platform approvals"
365
+ >
366
+ <span className="h-4 w-4 rounded-full bg-white shadow-[0_1px_4px_rgba(0,0,0,0.35)]" />
367
+ </button>
309
368
  </div>
310
- <button
311
- type="button"
312
- disabled={savingSetting === 'safetyRequireApprovalForOutbound'}
313
- onClick={() => {
314
- const next = !outboundApprovalEnabled
315
- void saveApprovalSettings(
316
- { safetyRequireApprovalForOutbound: next },
317
- next ? 'Outbound send approvals enabled' : 'Outbound send approvals disabled',
318
- 'safetyRequireApprovalForOutbound',
319
- )
320
- }}
321
- className={`relative w-10 h-[22px] rounded-full transition-colors duration-200 cursor-pointer disabled:opacity-50 ${outboundApprovalEnabled ? 'bg-accent' : 'bg-white/[0.12]'}`}
322
- aria-label="Toggle outbound send approvals"
323
- >
324
- <span className={`absolute top-[3px] left-[3px] w-4 h-4 rounded-full bg-white transition-transform duration-200 ${outboundApprovalEnabled ? 'translate-x-[18px]' : ''}`} />
325
- </button>
326
369
  </div>
327
- </div>
328
- </div>
329
370
 
330
- <div>
331
- <div className="flex items-center justify-between gap-3 mb-2">
332
- <div className="text-[12px] font-600 text-text-2">Auto-Approve Categories</div>
333
- <div className="text-[11px] text-text-3/60">
334
- {autoApproved.size} enabled
371
+ <div className="rounded-[14px] border border-white/[0.06] bg-black/20 px-4 py-4">
372
+ <div className="flex items-center justify-between gap-4">
373
+ <div>
374
+ <div className="text-[12px] font-600 text-text-2">Outbound Send Approvals</div>
375
+ <p className="text-[11px] text-text-3/60 mt-1 leading-relaxed">
376
+ Require explicit approval before agents send messages or media over connectors.
377
+ </p>
378
+ </div>
379
+ <button
380
+ type="button"
381
+ disabled={savingSetting === 'safetyRequireApprovalForOutbound'}
382
+ onClick={() => {
383
+ const next = !outboundApprovalEnabled
384
+ void saveApprovalSettings(
385
+ { safetyRequireApprovalForOutbound: next },
386
+ next ? 'Outbound send approvals enabled' : 'Outbound send approvals disabled',
387
+ 'safetyRequireApprovalForOutbound',
388
+ )
389
+ }}
390
+ className={`inline-flex h-[22px] w-10 shrink-0 items-center rounded-full border border-white/[0.08] p-[3px] transition-colors duration-200 cursor-pointer disabled:opacity-50 ${
391
+ outboundApprovalEnabled ? 'justify-end bg-accent' : 'justify-start bg-white/[0.16]'
392
+ }`}
393
+ aria-label="Toggle outbound send approvals"
394
+ >
395
+ <span className="h-4 w-4 rounded-full bg-white shadow-[0_1px_4px_rgba(0,0,0,0.35)]" />
396
+ </button>
397
+ </div>
335
398
  </div>
336
399
  </div>
337
- <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
400
+
401
+ <div>
402
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 mb-3">
403
+ <div>
404
+ <div className="text-[12px] font-600 text-text-2">Auto-Approve Categories</div>
405
+ <p className="text-[11px] text-text-3/60 mt-1">
406
+ Keep the approval system on, but let low-friction request types flow through automatically.
407
+ </p>
408
+ </div>
409
+ <div className="text-[11px] text-text-3/60">
410
+ {autoApproveEnabledCount} enabled
411
+ </div>
412
+ </div>
413
+ <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-2">
338
414
  {AUTO_APPROVE_OPTIONS.map((option) => {
339
415
  const checked = autoApproved.has(option.id)
340
416
  return (
341
417
  <label
342
418
  key={option.id}
343
- className={`rounded-[12px] border px-3 py-3 cursor-pointer transition-all ${
419
+ className={`rounded-[14px] border px-3 py-3 cursor-pointer transition-all ${
344
420
  checked
345
421
  ? 'border-accent-bright/30 bg-accent-soft/60'
346
422
  : 'border-white/[0.06] bg-black/20 hover:bg-white/[0.04]'
@@ -364,24 +440,35 @@ export function ApprovalsPanel() {
364
440
  className="mt-0.5"
365
441
  />
366
442
  <div>
367
- <div className="text-[12px] font-600 text-text-2">{option.label}</div>
443
+ <div className="flex items-center gap-2 flex-wrap">
444
+ <div className="text-[12px] font-600 text-text-2">{option.label}</div>
445
+ {option.risk && (
446
+ <span className={`px-1.5 py-0.5 rounded-full text-[9px] font-700 uppercase tracking-[0.08em] ${
447
+ option.risk === 'very-high'
448
+ ? 'bg-red-500/10 text-red-300 border border-red-500/20'
449
+ : 'bg-amber-500/10 text-amber-300 border border-amber-500/20'
450
+ }`}>
451
+ {option.risk === 'very-high' ? 'Very high risk' : 'High risk'}
452
+ </span>
453
+ )}
454
+ </div>
368
455
  <p className="text-[11px] text-text-3/60 mt-1 leading-relaxed">{option.description}</p>
369
456
  </div>
370
457
  </div>
371
458
  </label>
372
459
  )
373
460
  })}
461
+ </div>
374
462
  </div>
375
- <p className="text-[11px] text-text-3/60 mt-2">
376
- Use category auto-approval when you still want the approval system on, but you do not want these request types to pause execution.
377
- </p>
378
463
  </div>
379
464
  </div>
380
465
  </div>
381
466
 
382
- <div className="rounded-[16px] border border-white/[0.06] bg-white/[0.02] p-4 mb-6">
383
- <div className="flex flex-col lg:flex-row gap-3 lg:items-center lg:justify-between">
384
- <div className="flex flex-wrap gap-2">
467
+ <div className="rounded-[18px] border border-white/[0.06] bg-white/[0.02] p-4 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]">
468
+ <div className="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
469
+ <div className="flex-1">
470
+ <div className="text-[11px] font-700 uppercase tracking-[0.08em] text-text-3/60 mb-2">Queue Filters</div>
471
+ <div className="flex flex-wrap gap-2">
385
472
  {([
386
473
  ['all', `All (${pendingCount})`],
387
474
  ['execution', `Execution (${sortedExecApprovals.length})`],
@@ -401,10 +488,11 @@ export function ApprovalsPanel() {
401
488
  {label}
402
489
  </button>
403
490
  ))}
491
+ </div>
404
492
  </div>
405
493
 
406
- <div className="flex items-center gap-2">
407
- <div className="text-[11px] text-text-3/60 font-600">
494
+ <div className="flex items-center gap-2 justify-between lg:justify-end">
495
+ <div className="text-[11px] text-text-3/60 font-600 whitespace-nowrap">
408
496
  Showing {visibleCount} of {pendingCount}
409
497
  </div>
410
498
  {workflowCategories.length > 1 && scope !== 'execution' && (
@@ -425,7 +513,7 @@ export function ApprovalsPanel() {
425
513
  </div>
426
514
  </div>
427
515
 
428
- <div className="mt-3">
516
+ <div className="mt-1">
429
517
  <input
430
518
  value={search}
431
519
  onChange={(e) => setSearch(e.target.value)}
@@ -437,7 +525,7 @@ export function ApprovalsPanel() {
437
525
  </div>
438
526
 
439
527
  {filteredExecApprovals.length > 0 && (
440
- <div className="mb-6">
528
+ <div>
441
529
  <h2 className="text-[12px] font-700 uppercase tracking-[0.1em] text-amber-400/90 mb-2">Execution Approvals</h2>
442
530
  <div className="grid grid-cols-1 gap-3">
443
531
  {filteredExecApprovals.map((approval) => (
@@ -548,26 +636,34 @@ export function ApprovalsPanel() {
548
636
  )}
549
637
 
550
638
  {visibleCount === 0 && pendingCount > 0 && (
551
- <div className="rounded-[16px] border border-dashed border-white/[0.08] px-6 py-10 text-center">
639
+ <div className="rounded-[18px] border border-dashed border-white/[0.08] bg-white/[0.015] px-6 py-10 text-center">
552
640
  <p className="text-[13px] font-600 text-text-2 mb-1">No approvals match the current filters</p>
553
641
  <p className="text-[12px] text-text-3/60">Try clearing the search or switching the queue scope.</p>
554
642
  </div>
555
643
  )}
556
644
 
557
645
  {pendingCount === 0 && (
558
- <div className="flex flex-col items-center justify-center p-8 text-center">
559
- <div className="w-16 h-16 rounded-[24px] bg-white/[0.02] border border-white/[0.04] flex items-center justify-center mb-6">
646
+ <div className="rounded-[20px] border border-white/[0.06] bg-white/[0.02] px-8 py-12 text-center shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]">
647
+ <div className="w-16 h-16 rounded-[24px] bg-white/[0.02] border border-white/[0.04] flex items-center justify-center mx-auto mb-6">
560
648
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" className="text-text-3/40">
561
649
  <path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/>
562
650
  <path d="m9 12 2 2 4-4"/>
563
651
  </svg>
564
652
  </div>
565
- <h2 className="font-display text-[18px] font-600 text-text-2 mb-2">No pending approvals</h2>
566
- <p className="text-[13px] text-text-3/60 max-w-[360px]">
653
+ <h2 className="font-display text-[20px] font-600 text-text-2 mb-2">No pending approvals</h2>
654
+ <p className="text-[13px] text-text-3/60 max-w-[420px] mx-auto">
567
655
  {approvalsEnabled
568
656
  ? 'Your swarm is operating autonomously. Actions requiring oversight will appear here.'
569
657
  : 'Approvals are currently disabled, so eligible requests will auto-run instead of queuing here.'}
570
658
  </p>
659
+ <div className={`inline-flex items-center gap-2 mt-5 px-3 py-1.5 rounded-full border text-[11px] font-700 ${
660
+ approvalsEnabled
661
+ ? 'bg-amber-500/10 border-amber-500/20 text-amber-300'
662
+ : 'bg-emerald-500/10 border-emerald-500/20 text-emerald-300'
663
+ }`}>
664
+ <span className={`w-2 h-2 rounded-full ${approvalsEnabled ? 'bg-amber-300' : 'bg-emerald-300'}`} />
665
+ {approvalsEnabled ? 'Manual approvals active' : 'Workflow approvals auto-run'}
666
+ </div>
571
667
  </div>
572
668
  )}
573
669
  </div>