@swarmclawai/swarmclaw 0.7.2 → 0.7.3

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 (197) hide show
  1. package/README.md +81 -22
  2. package/package.json +1 -1
  3. package/src/app/api/agents/[id]/route.ts +26 -0
  4. package/src/app/api/agents/[id]/thread/route.ts +36 -7
  5. package/src/app/api/agents/route.ts +12 -1
  6. package/src/app/api/auth/route.ts +76 -7
  7. package/src/app/api/chatrooms/[id]/chat/route.ts +7 -2
  8. package/src/app/api/chats/[id]/browser/route.ts +5 -1
  9. package/src/app/api/chats/[id]/chat/route.ts +7 -3
  10. package/src/app/api/chats/[id]/main-loop/route.ts +7 -88
  11. package/src/app/api/chats/[id]/messages/route.ts +19 -13
  12. package/src/app/api/chats/[id]/route.ts +18 -0
  13. package/src/app/api/chats/[id]/stop/route.ts +6 -1
  14. package/src/app/api/chats/route.ts +16 -0
  15. package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
  16. package/src/app/api/connectors/doctor/route.ts +13 -0
  17. package/src/app/api/files/open/route.ts +16 -14
  18. package/src/app/api/memory/maintenance/route.ts +11 -1
  19. package/src/app/api/openclaw/agent-files/route.ts +27 -4
  20. package/src/app/api/openclaw/skills/route.ts +11 -3
  21. package/src/app/api/plugins/dependencies/route.ts +24 -0
  22. package/src/app/api/plugins/install/route.ts +15 -92
  23. package/src/app/api/plugins/route.ts +3 -26
  24. package/src/app/api/plugins/settings/route.ts +17 -12
  25. package/src/app/api/plugins/ui/route.ts +1 -0
  26. package/src/app/api/settings/route.ts +49 -7
  27. package/src/app/api/tasks/[id]/route.ts +15 -6
  28. package/src/app/api/tasks/bulk/route.ts +2 -2
  29. package/src/app/api/tasks/route.ts +9 -4
  30. package/src/app/api/webhooks/[id]/route.ts +8 -1
  31. package/src/app/page.tsx +9 -2
  32. package/src/cli/index.js +4 -0
  33. package/src/cli/index.ts +3 -10
  34. package/src/components/agents/agent-card.tsx +15 -12
  35. package/src/components/agents/agent-chat-list.tsx +101 -1
  36. package/src/components/agents/agent-list.tsx +46 -9
  37. package/src/components/agents/agent-sheet.tsx +207 -16
  38. package/src/components/agents/inspector-panel.tsx +108 -48
  39. package/src/components/auth/access-key-gate.tsx +36 -97
  40. package/src/components/chat/chat-area.tsx +29 -13
  41. package/src/components/chat/chat-card.tsx +4 -20
  42. package/src/components/chat/chat-header.tsx +255 -353
  43. package/src/components/chat/chat-list.tsx +7 -9
  44. package/src/components/chat/checkpoint-timeline.tsx +1 -1
  45. package/src/components/chat/message-list.tsx +3 -1
  46. package/src/components/chatrooms/chatroom-view.tsx +347 -205
  47. package/src/components/connectors/connector-list.tsx +265 -127
  48. package/src/components/connectors/connector-sheet.tsx +217 -0
  49. package/src/components/home/home-view.tsx +128 -4
  50. package/src/components/layout/app-layout.tsx +383 -194
  51. package/src/components/layout/mobile-header.tsx +26 -8
  52. package/src/components/plugins/plugin-list.tsx +15 -3
  53. package/src/components/plugins/plugin-sheet.tsx +118 -9
  54. package/src/components/projects/project-detail.tsx +183 -0
  55. package/src/components/shared/agent-picker-list.tsx +2 -2
  56. package/src/components/shared/command-palette.tsx +111 -24
  57. package/src/components/shared/settings/plugin-manager.tsx +20 -4
  58. package/src/components/shared/settings/section-capability-policy.tsx +105 -0
  59. package/src/components/shared/settings/section-heartbeat.tsx +77 -0
  60. package/src/components/shared/settings/section-orchestrator.tsx +3 -3
  61. package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
  62. package/src/components/shared/settings/section-secrets.tsx +6 -6
  63. package/src/components/shared/settings/section-user-preferences.tsx +1 -1
  64. package/src/components/shared/settings/section-voice.tsx +5 -1
  65. package/src/components/shared/settings/section-web-search.tsx +10 -2
  66. package/src/components/shared/settings/settings-page.tsx +245 -46
  67. package/src/components/tasks/approvals-panel.tsx +205 -18
  68. package/src/components/tasks/task-board.tsx +242 -46
  69. package/src/components/usage/metrics-dashboard.tsx +74 -1
  70. package/src/components/wallets/wallet-panel.tsx +17 -5
  71. package/src/components/webhooks/webhook-sheet.tsx +7 -7
  72. package/src/lib/auth.ts +17 -0
  73. package/src/lib/chat-streaming-state.test.ts +108 -0
  74. package/src/lib/chat-streaming-state.ts +108 -0
  75. package/src/lib/openclaw-agent-id.test.ts +14 -0
  76. package/src/lib/openclaw-agent-id.ts +31 -0
  77. package/src/lib/server/agent-assignment.test.ts +112 -0
  78. package/src/lib/server/agent-assignment.ts +169 -0
  79. package/src/lib/server/approval-connector-notify.test.ts +253 -0
  80. package/src/lib/server/approvals-auto-approve.test.ts +205 -0
  81. package/src/lib/server/approvals.ts +483 -75
  82. package/src/lib/server/autonomy-runtime.test.ts +341 -0
  83. package/src/lib/server/browser-state.test.ts +118 -0
  84. package/src/lib/server/browser-state.ts +123 -0
  85. package/src/lib/server/build-llm.test.ts +36 -0
  86. package/src/lib/server/build-llm.ts +11 -4
  87. package/src/lib/server/builtin-plugins.ts +34 -0
  88. package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
  89. package/src/lib/server/chat-execution-tool-events.test.ts +134 -0
  90. package/src/lib/server/chat-execution.ts +250 -61
  91. package/src/lib/server/chatroom-health.test.ts +26 -0
  92. package/src/lib/server/chatroom-health.ts +2 -3
  93. package/src/lib/server/chatroom-helpers.test.ts +67 -2
  94. package/src/lib/server/chatroom-helpers.ts +45 -5
  95. package/src/lib/server/connectors/discord.ts +175 -11
  96. package/src/lib/server/connectors/doctor.test.ts +80 -0
  97. package/src/lib/server/connectors/doctor.ts +116 -0
  98. package/src/lib/server/connectors/manager.ts +946 -110
  99. package/src/lib/server/connectors/policy.test.ts +222 -0
  100. package/src/lib/server/connectors/policy.ts +452 -0
  101. package/src/lib/server/connectors/slack.ts +188 -9
  102. package/src/lib/server/connectors/telegram.ts +65 -15
  103. package/src/lib/server/connectors/thread-context.test.ts +44 -0
  104. package/src/lib/server/connectors/thread-context.ts +72 -0
  105. package/src/lib/server/connectors/types.ts +41 -11
  106. package/src/lib/server/daemon-state.ts +59 -1
  107. package/src/lib/server/data-dir.ts +13 -0
  108. package/src/lib/server/delegation-jobs.test.ts +140 -0
  109. package/src/lib/server/delegation-jobs.ts +248 -0
  110. package/src/lib/server/document-utils.test.ts +47 -0
  111. package/src/lib/server/document-utils.ts +397 -0
  112. package/src/lib/server/heartbeat-service.ts +13 -39
  113. package/src/lib/server/heartbeat-source.test.ts +22 -0
  114. package/src/lib/server/heartbeat-source.ts +7 -0
  115. package/src/lib/server/identity-continuity.test.ts +77 -0
  116. package/src/lib/server/identity-continuity.ts +127 -0
  117. package/src/lib/server/mailbox-utils.ts +347 -0
  118. package/src/lib/server/main-agent-loop.ts +27 -967
  119. package/src/lib/server/memory-db.ts +4 -6
  120. package/src/lib/server/memory-tiers.ts +40 -0
  121. package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
  122. package/src/lib/server/openclaw-agent-resolver.ts +128 -0
  123. package/src/lib/server/openclaw-exec-config.ts +5 -6
  124. package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
  125. package/src/lib/server/openclaw-skills-normalize.ts +136 -0
  126. package/src/lib/server/openclaw-sync.ts +3 -2
  127. package/src/lib/server/orchestrator-lg.ts +17 -6
  128. package/src/lib/server/orchestrator.ts +2 -2
  129. package/src/lib/server/playwright-proxy.mjs +27 -3
  130. package/src/lib/server/plugins.test.ts +207 -0
  131. package/src/lib/server/plugins.ts +822 -69
  132. package/src/lib/server/provider-health.ts +33 -3
  133. package/src/lib/server/queue.ts +3 -20
  134. package/src/lib/server/scheduler.ts +2 -0
  135. package/src/lib/server/session-archive-memory.test.ts +85 -0
  136. package/src/lib/server/session-archive-memory.ts +230 -0
  137. package/src/lib/server/session-mailbox.ts +8 -18
  138. package/src/lib/server/session-reset-policy.test.ts +99 -0
  139. package/src/lib/server/session-reset-policy.ts +311 -0
  140. package/src/lib/server/session-run-manager.ts +33 -80
  141. package/src/lib/server/session-tools/autonomy-tools.test.ts +105 -0
  142. package/src/lib/server/session-tools/calendar.ts +2 -12
  143. package/src/lib/server/session-tools/connector.ts +109 -8
  144. package/src/lib/server/session-tools/context.ts +14 -2
  145. package/src/lib/server/session-tools/crawl.ts +447 -0
  146. package/src/lib/server/session-tools/crud.ts +70 -32
  147. package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
  148. package/src/lib/server/session-tools/delegate.ts +406 -20
  149. package/src/lib/server/session-tools/discovery.ts +22 -4
  150. package/src/lib/server/session-tools/document.ts +283 -0
  151. package/src/lib/server/session-tools/email.ts +1 -3
  152. package/src/lib/server/session-tools/extract.ts +137 -0
  153. package/src/lib/server/session-tools/file-normalize.test.ts +93 -0
  154. package/src/lib/server/session-tools/file-send.test.ts +84 -1
  155. package/src/lib/server/session-tools/file.ts +237 -24
  156. package/src/lib/server/session-tools/human-loop.ts +227 -0
  157. package/src/lib/server/session-tools/image-gen.ts +1 -3
  158. package/src/lib/server/session-tools/index.ts +56 -1
  159. package/src/lib/server/session-tools/mailbox.ts +276 -0
  160. package/src/lib/server/session-tools/memory.ts +35 -3
  161. package/src/lib/server/session-tools/monitor.ts +150 -7
  162. package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
  163. package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
  164. package/src/lib/server/session-tools/platform.ts +142 -4
  165. package/src/lib/server/session-tools/plugin-creator.ts +86 -23
  166. package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
  167. package/src/lib/server/session-tools/replicate.ts +1 -3
  168. package/src/lib/server/session-tools/schedule.ts +20 -10
  169. package/src/lib/server/session-tools/session-info.ts +36 -3
  170. package/src/lib/server/session-tools/session-tools-wiring.test.ts +31 -17
  171. package/src/lib/server/session-tools/subagent.ts +193 -27
  172. package/src/lib/server/session-tools/table.ts +587 -0
  173. package/src/lib/server/session-tools/wallet.ts +13 -10
  174. package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
  175. package/src/lib/server/session-tools/web.ts +896 -100
  176. package/src/lib/server/storage.ts +226 -7
  177. package/src/lib/server/stream-agent-chat.ts +46 -21
  178. package/src/lib/server/structured-extract.test.ts +72 -0
  179. package/src/lib/server/structured-extract.ts +373 -0
  180. package/src/lib/server/task-mention.test.ts +16 -2
  181. package/src/lib/server/task-mention.ts +61 -10
  182. package/src/lib/server/tool-aliases.ts +44 -7
  183. package/src/lib/server/tool-capability-policy.ts +6 -0
  184. package/src/lib/server/tool-retry.ts +2 -0
  185. package/src/lib/server/watch-jobs.test.ts +173 -0
  186. package/src/lib/server/watch-jobs.ts +532 -0
  187. package/src/lib/server/ws-hub.ts +5 -3
  188. package/src/lib/validation/schemas.test.ts +26 -0
  189. package/src/lib/validation/schemas.ts +7 -0
  190. package/src/lib/ws-client.ts +14 -12
  191. package/src/proxy.ts +5 -5
  192. package/src/stores/use-app-store.ts +0 -6
  193. package/src/stores/use-chat-store.ts +31 -2
  194. package/src/types/index.ts +287 -44
  195. package/src/components/chat/new-chat-sheet.tsx +0 -253
  196. package/src/lib/server/main-session.ts +0 -17
  197. package/src/lib/server/session-run-manager.test.ts +0 -26
@@ -9,6 +9,7 @@ import { Avatar } from '@/components/shared/avatar'
9
9
  import { SettingsPage } from '@/components/shared/settings/settings-page'
10
10
  import { AgentList } from '@/components/agents/agent-list'
11
11
  import { AgentChatList } from '@/components/agents/agent-chat-list'
12
+ import { AgentAvatar } from '@/components/agents/agent-avatar'
12
13
  import { AgentSheet } from '@/components/agents/agent-sheet'
13
14
  import { ScheduleList } from '@/components/schedules/schedule-list'
14
15
  import { ScheduleSheet } from '@/components/schedules/schedule-sheet'
@@ -71,6 +72,46 @@ const RAIL_EXPANDED_KEY = 'sc_rail_expanded'
71
72
  const STAR_NOTIFICATION_KEY = 'sc_star_notification_v1'
72
73
  const GITHUB_REPO_URL = 'https://github.com/swarmclawai/swarmclaw'
73
74
 
75
+ const VIEW_LABELS: Record<AppView, string> = {
76
+ home: 'Home',
77
+ agents: 'Agents',
78
+ chatrooms: 'Chatrooms',
79
+ schedules: 'Schedules',
80
+ memory: 'Memory',
81
+ tasks: 'Tasks',
82
+ approvals: 'Approvals',
83
+ secrets: 'Secrets',
84
+ providers: 'Providers',
85
+ skills: 'Skills',
86
+ connectors: 'Connectors',
87
+ webhooks: 'Webhooks',
88
+ mcp_servers: 'MCP Servers',
89
+ knowledge: 'Knowledge',
90
+ logs: 'Logs',
91
+ plugins: 'Plugins',
92
+ usage: 'Usage',
93
+ wallets: 'Wallets',
94
+ runs: 'Runs',
95
+ settings: 'Settings',
96
+ projects: 'Projects',
97
+ activity: 'Activity',
98
+ }
99
+
100
+ const CREATE_LABELS: Partial<Record<AppView, string>> = {
101
+ agents: 'Agent',
102
+ schedules: 'Schedule',
103
+ tasks: 'Task',
104
+ secrets: 'Secret',
105
+ providers: 'Provider',
106
+ skills: 'Skill',
107
+ connectors: 'Connector',
108
+ webhooks: 'Webhook',
109
+ mcp_servers: 'MCP Server',
110
+ knowledge: 'Knowledge Entry',
111
+ plugins: 'Plugin',
112
+ projects: 'Project',
113
+ }
114
+
74
115
  export function AppLayout() {
75
116
  const currentUser = useAppStore((s) => s.currentUser)
76
117
  const sessions = useAppStore((s) => s.sessions)
@@ -133,13 +174,15 @@ export function AppLayout() {
133
174
 
134
175
  const handleShortcutKey = useCallback((e: KeyboardEvent) => {
135
176
  const mod = e.metaKey || e.ctrlKey
136
- // Cmd+N / Ctrl+N — new chat
177
+ // Cmd+N / Ctrl+N — jump to the default agent shortcut
137
178
  if (mod && !e.shiftKey && e.key.toLowerCase() === 'n') {
138
179
  e.preventDefault()
139
180
  const state = useAppStore.getState()
140
- const allAgents = Object.values(state.agents).filter((a) => !a.trashedAt)
141
- const target = allAgents.find((a) => a.id === 'default') || allAgents[0]
142
- if (target) void state.setCurrentAgent(target.id)
181
+ const defaultAgentId = state.appSettings.defaultAgentId && state.agents[state.appSettings.defaultAgentId]
182
+ ? state.appSettings.defaultAgentId
183
+ : Object.values(state.agents)[0]?.id || null
184
+ if (defaultAgentId) void state.setCurrentAgent(defaultAgentId)
185
+ else state.setActiveView('agents')
143
186
  return
144
187
  }
145
188
  // Cmd+Shift+T / Ctrl+Shift+T — jump to tasks
@@ -191,6 +234,17 @@ export function AppLayout() {
191
234
 
192
235
  useWs('plugins', refreshPluginState)
193
236
 
237
+ const isViewEnabled = useCallback((view: AppView) => {
238
+ if (view === 'chatrooms') return plugins['chatroom']?.enabled !== false
239
+ if (view === 'schedules') return plugins['schedule']?.enabled !== false
240
+ if (view === 'memory') return plugins['memory']?.enabled !== false
241
+ if (view === 'connectors') return plugins['connectors']?.enabled !== false
242
+ if (view === 'webhooks') return plugins['http']?.enabled !== false
243
+ if (view === 'wallets') return plugins['wallet']?.enabled !== false
244
+ if (view === 'logs') return plugins['monitor']?.enabled !== false
245
+ return true
246
+ }, [plugins])
247
+
194
248
  const [railExpanded, setRailExpanded] = useState(() => {
195
249
  const stored = safeStorageGet(RAIL_EXPANDED_KEY)
196
250
  return stored === null ? true : stored === 'true'
@@ -237,10 +291,11 @@ export function AppLayout() {
237
291
  const agents = useAppStore((s) => s.agents)
238
292
  const currentAgentId = useAppStore((s) => s.currentAgentId)
239
293
  const setCurrentAgent = useAppStore((s) => s.setCurrentAgent)
240
- const defaultAgentId = appSettings.defaultAgentId && agents[appSettings.defaultAgentId]
241
- ? appSettings.defaultAgentId
242
- : Object.values(agents)[0]?.id || null
243
- const isMainChat = activeView === 'agents' && currentAgentId === defaultAgentId
294
+ const defaultAgent = appSettings.defaultAgentId && agents[appSettings.defaultAgentId]
295
+ ? agents[appSettings.defaultAgentId]
296
+ : Object.values(agents)[0] || null
297
+ const defaultAgentId = defaultAgent?.id || null
298
+ const isDefaultChat = activeView === 'agents' && currentAgentId === defaultAgentId
244
299
 
245
300
  const swipeHandlers = useSwipe({
246
301
  onSwipe: (dir) => {
@@ -255,9 +310,11 @@ export function AppLayout() {
255
310
  const hasCanvas = !!(currentSession?.canvasContent && canvasDismissedFor !== currentSessionId)
256
311
  const canvasAgentName = currentSession?.agentId && agents[currentSession.agentId] ? agents[currentSession.agentId].name : undefined
257
312
 
258
- const goToMainChat = async () => {
313
+ const goToDefaultChat = async () => {
259
314
  if (defaultAgentId) {
260
315
  await setCurrentAgent(defaultAgentId)
316
+ } else {
317
+ setActiveView('agents')
261
318
  }
262
319
  setActiveView('agents')
263
320
  setSidebarOpen(false)
@@ -317,32 +374,60 @@ export function AppLayout() {
317
374
  </div>
318
375
  )}
319
376
 
320
- {/* Main Chat shortcut */}
377
+ {/* Default agent shortcut */}
321
378
  {railExpanded ? (
322
- <div className="px-3 mb-2">
379
+ <div className="px-3 mb-2.5">
323
380
  <button
324
- onClick={goToMainChat}
325
- className={`w-full flex items-center gap-2.5 px-3 py-2.5 rounded-[10px] text-[13px] font-600 cursor-pointer transition-all
326
- ${isMainChat
381
+ onClick={goToDefaultChat}
382
+ className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-[12px] text-[13px] font-600 cursor-pointer transition-all text-left
383
+ ${isDefaultChat
327
384
  ? 'bg-accent-bright/15 border border-[#6366F1]/25 text-accent-bright'
328
385
  : 'bg-accent-bright/10 border border-[#6366F1]/20 text-accent-bright hover:bg-accent-bright/15'}`}
329
386
  style={{ fontFamily: 'inherit' }}
330
387
  >
331
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
332
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
333
- </svg>
334
- Main Chat
388
+ {defaultAgent ? (
389
+ <AgentAvatar
390
+ seed={defaultAgent.avatarSeed || null}
391
+ avatarUrl={defaultAgent.avatarUrl}
392
+ name={defaultAgent.name}
393
+ size={28}
394
+ />
395
+ ) : (
396
+ <div className="w-7 h-7 rounded-full bg-accent-bright/15 flex items-center justify-center shrink-0">
397
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
398
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
399
+ </svg>
400
+ </div>
401
+ )}
402
+ <div className="min-w-0">
403
+ <div className="truncate">{defaultAgent?.name || 'Choose Agent'}</div>
404
+ <div className="text-[10px] font-500 text-accent-bright/75 mt-0.5">
405
+ {defaultAgent ? 'Default shortcut' : 'Pick an agent to open its thread'}
406
+ </div>
407
+ </div>
335
408
  </button>
336
409
  </div>
337
410
  ) : (
338
- <RailTooltip label="Main Chat" description="Your persistent assistant chat">
411
+ <RailTooltip
412
+ label={defaultAgent?.name || 'Choose Agent'}
413
+ description={defaultAgent ? 'Open your default agent shortcut chat' : 'Choose an agent thread'}
414
+ >
339
415
  <button
340
- onClick={goToMainChat}
341
- className={`rail-btn self-center mb-2 ${isMainChat ? 'active' : ''}`}
416
+ onClick={goToDefaultChat}
417
+ className={`rail-btn self-center mb-2 ${isDefaultChat ? 'active' : ''}`}
342
418
  >
343
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
344
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
345
- </svg>
419
+ {defaultAgent ? (
420
+ <AgentAvatar
421
+ seed={defaultAgent.avatarSeed || null}
422
+ avatarUrl={defaultAgent.avatarUrl}
423
+ name={defaultAgent.name}
424
+ size={20}
425
+ />
426
+ ) : (
427
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
428
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
429
+ </svg>
430
+ )}
346
431
  </button>
347
432
  </RailTooltip>
348
433
  )}
@@ -380,128 +465,159 @@ export function AppLayout() {
380
465
 
381
466
  <div className="flex-1 min-h-0 flex flex-col overflow-y-auto overscroll-contain touch-pan-y">
382
467
  {/* Nav items */}
383
- <div className={`flex flex-col gap-0.5 ${railExpanded ? 'px-3' : 'items-center'}`}>
384
- <NavItem view="home" label="Home" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('home')}>
385
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
386
- <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /><polyline points="9 22 9 12 15 12 15 22" />
387
- </svg>
388
- </NavItem>
389
- <NavItem view="agents" label="Agents" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('agents')}>
390
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
391
- <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" />
392
- </svg>
393
- </NavItem>
394
- {plugins['chatroom']?.enabled !== false && (
395
- <NavItem view="chatrooms" label="Chatrooms" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('chatrooms')}>
396
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
397
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
398
- <path d="M8 10h8" /><path d="M8 14h4" />
399
- </svg>
400
- </NavItem>
401
- )}
402
- <NavItem view="projects" label="Projects" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('projects')}>
403
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
404
- <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" />
405
- </svg>
406
- </NavItem>
407
- {plugins['schedule']?.enabled !== false && (
408
- <NavItem view="schedules" label="Schedules" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('schedules')}>
409
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
410
- <circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" />
411
- </svg>
412
- </NavItem>
413
- )}
414
- {plugins['memory']?.enabled !== false && (
415
- <NavItem view="memory" label="Memory" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('memory')}>
416
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
417
- <ellipse cx="12" cy="5" rx="9" ry="3" /><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" /><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
418
- </svg>
419
- </NavItem>
420
- )}
421
- <NavItem view="tasks" label="Tasks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('tasks')} badge={pendingApprovalCount}>
422
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
423
- <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" />
424
- </svg>
425
- </NavItem>
426
- <NavItem view="approvals" label="Approvals" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('approvals')} badge={pendingApprovalCount}>
427
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
428
- <path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/>
429
- <path d="m9 12 2 2 4-4"/>
430
- </svg>
431
- </NavItem>
432
- <NavItem view="secrets" label="Secrets" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('secrets')}>
433
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
434
- <rect x="3" y="11" width="18" height="11" rx="2" ry="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" />
435
- </svg>
436
- </NavItem>
437
- <NavItem view="providers" label="Providers" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('providers')}>
438
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
439
- <path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z" />
440
- </svg>
441
- </NavItem>
442
- <NavItem view="skills" label="Skills" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('skills')}>
443
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
444
- <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" /><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
445
- </svg>
446
- </NavItem>
447
- {plugins['connectors']?.enabled !== false && (
448
- <NavItem view="connectors" label="Connectors" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('connectors')}>
449
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
450
- <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3" /><line x1="8" y1="12" x2="16" y2="12" />
451
- </svg>
452
- </NavItem>
453
- )}
454
- {plugins['http']?.enabled !== false && (
455
- <NavItem view="webhooks" label="Webhooks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('webhooks')}>
456
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
457
- <path d="M22 12h-4l-3 7L9 5l-3 7H2" />
458
- </svg>
459
- </NavItem>
460
- )}
461
- <NavItem view="mcp_servers" label="MCP" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('mcp_servers')}>
462
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
463
- <rect x="2" y="2" width="20" height="8" rx="2" /><rect x="2" y="14" width="20" height="8" rx="2" /><line x1="6" y1="6" x2="6.01" y2="6" /><line x1="6" y1="18" x2="6.01" y2="18" />
464
- </svg>
465
- </NavItem>
466
- <NavItem view="knowledge" label="Knowledge" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('knowledge')}>
467
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
468
- <circle cx="12" cy="12" r="10" /><line x1="2" y1="12" x2="22" y2="12" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
469
- </svg>
470
- </NavItem>
471
- <NavItem view="plugins" label="Plugins" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('plugins')}>
472
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
473
- <path d="M12 2v4m0 12v4M2 12h4m12 0h4" /><circle cx="12" cy="12" r="4" /><path d="M8 8L5.5 5.5M16 8l2.5-2.5M8 16l-2.5 2.5M16 16l2.5 2.5" />
474
- </svg>
475
- </NavItem>
476
- <NavItem view="usage" label="Usage" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('usage')}>
477
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
478
- <line x1="18" y1="20" x2="18" y2="10" /><line x1="12" y1="20" x2="12" y2="4" /><line x1="6" y1="20" x2="6" y2="14" />
479
- </svg>
480
- </NavItem>
481
- {plugins['wallet']?.enabled !== false && (
482
- <NavItem view="wallets" label="Wallets" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('wallets')}>
483
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
484
- <rect x="2" y="6" width="20" height="14" rx="2" /><path d="M22 10H18a2 2 0 0 0 0 4h4" /><path d="M6 6V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2" />
485
- </svg>
486
- </NavItem>
487
- )}
488
- <NavItem view="runs" label="Runs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('runs')}>
489
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
490
- <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
491
- </svg>
492
- </NavItem>
493
- <NavItem view="activity" label="Activity" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('activity')}>
494
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
495
- <path d="M12 8v4l3 3" /><circle cx="12" cy="12" r="10" />
496
- </svg>
497
- </NavItem>
498
- {plugins['monitor']?.enabled !== false && (
499
- <NavItem view="logs" label="Logs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('logs')}>
500
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
501
- <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" /><polyline points="14 2 14 8 20 8" /><line x1="16" y1="13" x2="8" y2="13" /><line x1="16" y1="17" x2="8" y2="17" /><polyline points="10 9 9 9 8 9" />
502
- </svg>
503
- </NavItem>
504
- )}
468
+ <div className={`flex flex-col gap-3 ${railExpanded ? 'px-3' : 'items-center'}`}>
469
+ <div className={`flex flex-col gap-0.5 ${railExpanded ? '' : 'items-center'}`}>
470
+ {railExpanded ? (
471
+ <div className="px-3 pb-1 text-[10px] font-700 uppercase tracking-[0.12em] text-text-3/45">Workspace</div>
472
+ ) : (
473
+ <div className="my-1 h-px w-6 bg-white/[0.06]" />
474
+ )}
475
+ <NavItem view="home" label="Home" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('home')}>
476
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
477
+ <path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" /><polyline points="9 22 9 12 15 12 15 22" />
478
+ </svg>
479
+ </NavItem>
480
+ <NavItem view="agents" label="Agents" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('agents')}>
481
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
482
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" />
483
+ </svg>
484
+ </NavItem>
485
+ {isViewEnabled('chatrooms') && (
486
+ <NavItem view="chatrooms" label="Chatrooms" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('chatrooms')}>
487
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
488
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
489
+ <path d="M8 10h8" /><path d="M8 14h4" />
490
+ </svg>
491
+ </NavItem>
492
+ )}
493
+ <NavItem view="projects" label="Projects" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('projects')}>
494
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
495
+ <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" />
496
+ </svg>
497
+ </NavItem>
498
+ </div>
499
+
500
+ <div className={`flex flex-col gap-0.5 ${railExpanded ? '' : 'items-center'}`}>
501
+ {railExpanded ? (
502
+ <div className="px-3 pb-1 text-[10px] font-700 uppercase tracking-[0.12em] text-text-3/45">Execution</div>
503
+ ) : (
504
+ <div className="my-1 h-px w-6 bg-white/[0.06]" />
505
+ )}
506
+ <NavItem view="tasks" label="Tasks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('tasks')} badge={pendingApprovalCount}>
507
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
508
+ <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" />
509
+ </svg>
510
+ </NavItem>
511
+ <NavItem view="approvals" label="Approvals" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('approvals')} badge={pendingApprovalCount}>
512
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
513
+ <path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"/>
514
+ <path d="m9 12 2 2 4-4"/>
515
+ </svg>
516
+ </NavItem>
517
+ {isViewEnabled('schedules') && (
518
+ <NavItem view="schedules" label="Schedules" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('schedules')}>
519
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
520
+ <circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" />
521
+ </svg>
522
+ </NavItem>
523
+ )}
524
+ {isViewEnabled('memory') && (
525
+ <NavItem view="memory" label="Memory" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('memory')}>
526
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
527
+ <ellipse cx="12" cy="5" rx="9" ry="3" /><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" /><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
528
+ </svg>
529
+ </NavItem>
530
+ )}
531
+ <NavItem view="runs" label="Runs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('runs')}>
532
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
533
+ <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
534
+ </svg>
535
+ </NavItem>
536
+ </div>
537
+
538
+ <div className={`flex flex-col gap-0.5 ${railExpanded ? '' : 'items-center'}`}>
539
+ {railExpanded ? (
540
+ <div className="px-3 pb-1 text-[10px] font-700 uppercase tracking-[0.12em] text-text-3/45">Knowledge</div>
541
+ ) : (
542
+ <div className="my-1 h-px w-6 bg-white/[0.06]" />
543
+ )}
544
+ <NavItem view="knowledge" label="Knowledge" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('knowledge')}>
545
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
546
+ <circle cx="12" cy="12" r="10" /><line x1="2" y1="12" x2="22" y2="12" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
547
+ </svg>
548
+ </NavItem>
549
+ <NavItem view="skills" label="Skills" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('skills')}>
550
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
551
+ <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" /><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
552
+ </svg>
553
+ </NavItem>
554
+ {isViewEnabled('connectors') && (
555
+ <NavItem view="connectors" label="Connectors" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('connectors')}>
556
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
557
+ <path d="M15 7h3a5 5 0 0 1 5 5 5 5 0 0 1-5 5h-3m-6 0H6a5 5 0 0 1-5-5 5 5 0 0 1 5-5h3" /><line x1="8" y1="12" x2="16" y2="12" />
558
+ </svg>
559
+ </NavItem>
560
+ )}
561
+ {isViewEnabled('webhooks') && (
562
+ <NavItem view="webhooks" label="Webhooks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('webhooks')}>
563
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
564
+ <path d="M22 12h-4l-3 7L9 5l-3 7H2" />
565
+ </svg>
566
+ </NavItem>
567
+ )}
568
+ <NavItem view="mcp_servers" label="MCP Servers" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('mcp_servers')}>
569
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
570
+ <rect x="2" y="2" width="20" height="8" rx="2" /><rect x="2" y="14" width="20" height="8" rx="2" /><line x1="6" y1="6" x2="6.01" y2="6" /><line x1="6" y1="18" x2="6.01" y2="18" />
571
+ </svg>
572
+ </NavItem>
573
+ <NavItem view="plugins" label="Plugins" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('plugins')}>
574
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
575
+ <path d="M12 2v4m0 12v4M2 12h4m12 0h4" /><circle cx="12" cy="12" r="4" /><path d="M8 8L5.5 5.5M16 8l2.5-2.5M8 16l-2.5 2.5M16 16l2.5 2.5" />
576
+ </svg>
577
+ </NavItem>
578
+ </div>
579
+
580
+ <div className={`flex flex-col gap-0.5 ${railExpanded ? '' : 'items-center'}`}>
581
+ {railExpanded ? (
582
+ <div className="px-3 pb-1 text-[10px] font-700 uppercase tracking-[0.12em] text-text-3/45">System</div>
583
+ ) : (
584
+ <div className="my-1 h-px w-6 bg-white/[0.06]" />
585
+ )}
586
+ <NavItem view="secrets" label="Secrets" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('secrets')}>
587
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
588
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" />
589
+ </svg>
590
+ </NavItem>
591
+ <NavItem view="providers" label="Providers" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('providers')}>
592
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
593
+ <path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z" />
594
+ </svg>
595
+ </NavItem>
596
+ <NavItem view="usage" label="Usage" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('usage')}>
597
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
598
+ <line x1="18" y1="20" x2="18" y2="10" /><line x1="12" y1="20" x2="12" y2="4" /><line x1="6" y1="20" x2="6" y2="14" />
599
+ </svg>
600
+ </NavItem>
601
+ <NavItem view="activity" label="Activity" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('activity')}>
602
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
603
+ <path d="M12 8v4l3 3" /><circle cx="12" cy="12" r="10" />
604
+ </svg>
605
+ </NavItem>
606
+ {isViewEnabled('wallets') && (
607
+ <NavItem view="wallets" label="Wallets" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('wallets')}>
608
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
609
+ <rect x="2" y="6" width="20" height="14" rx="2" /><path d="M22 10H18a2 2 0 0 0 0 4h4" /><path d="M6 6V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2" />
610
+ </svg>
611
+ </NavItem>
612
+ )}
613
+ {isViewEnabled('logs') && (
614
+ <NavItem view="logs" label="Logs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('logs')}>
615
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
616
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" /><polyline points="14 2 14 8 20 8" /><line x1="16" y1="13" x2="8" y2="13" /><line x1="16" y1="17" x2="8" y2="17" /><polyline points="10 9 9 9 8 9" />
617
+ </svg>
618
+ </NavItem>
619
+ )}
620
+ </div>
505
621
  </div>
506
622
 
507
623
  <div className="flex-1" />
@@ -616,7 +732,7 @@ export function AppLayout() {
616
732
  style={{ animation: 'panel-in 0.3s var(--ease-spring)' }}
617
733
  >
618
734
  <div className="flex items-center px-5 pt-5 pb-3 shrink-0">
619
- <h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] capitalize flex-1">{activeView}</h2>
735
+ <h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] flex-1">{VIEW_LABELS[activeView]}</h2>
620
736
  {activeView === 'logs' || activeView === 'usage' || activeView === 'runs' ? null : activeView === 'memory' ? (
621
737
  <button
622
738
  onClick={() => useAppStore.getState().setMemorySheetOpen(true)}
@@ -638,7 +754,7 @@ export function AppLayout() {
638
754
  <line x1="12" y1="5" x2="12" y2="19" />
639
755
  <line x1="5" y1="12" x2="19" y2="12" />
640
756
  </svg>
641
- {activeView === 'agents' ? 'Agent' : activeView === 'schedules' ? 'Schedule' : activeView === 'tasks' ? 'Task' : activeView === 'secrets' ? 'Secret' : activeView === 'providers' ? 'Provider' : activeView === 'skills' ? 'Skill' : activeView === 'connectors' ? 'Connector' : activeView === 'webhooks' ? 'Webhook' : activeView === 'mcp_servers' ? 'MCP Server' : activeView === 'knowledge' ? 'Knowledge' : 'New'}
757
+ {CREATE_LABELS[activeView] || 'New'}
642
758
  </button>
643
759
  )}
644
760
  </div>
@@ -706,32 +822,89 @@ export function AppLayout() {
706
822
  <Avatar user={currentUser!} size="sm" avatarSeed={appSettings.userAvatarSeed} />
707
823
  </button>
708
824
  </div>
709
- {/* View selector tabs */}
710
- <div className="flex px-4 py-2 gap-1 shrink-0 flex-wrap">
711
- {(['agents', 'chatrooms', 'schedules', 'memory', 'tasks', 'secrets', 'providers', 'skills', 'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins', 'usage', 'runs', 'logs'] as AppView[]).map((v) => (
712
- <button
713
- key={v}
714
- onClick={() => setActiveView(v)}
715
- className={`py-2 px-2.5 rounded-[10px] text-[11px] font-600 capitalize cursor-pointer transition-all
716
- ${activeView === v
717
- ? 'bg-accent-soft text-accent-bright'
718
- : 'bg-transparent text-text-3 hover:text-text-2'}`}
719
- style={{ fontFamily: 'inherit' }}
720
- >
721
- {v}
722
- </button>
723
- ))}
724
- {/* Dynamic Plugin Items */}
725
- {pluginSidebarItems.map((item) => (
825
+ {defaultAgent && (
826
+ <div className="px-4 pt-1 pb-3 shrink-0">
726
827
  <button
727
- key={item.id}
728
- onClick={() => window.open(item.href, '_blank')}
729
- className="py-2 px-2.5 rounded-[10px] text-[11px] font-600 capitalize cursor-pointer transition-all bg-emerald-500/[0.05] text-emerald-400/80 hover:text-emerald-400 border border-emerald-400/10"
828
+ onClick={goToDefaultChat}
829
+ className={`w-full flex items-center gap-3 rounded-[14px] border px-4 py-3 text-left transition-all cursor-pointer ${
830
+ isDefaultChat
831
+ ? 'bg-accent-soft border-accent-bright/25 text-accent-bright'
832
+ : 'bg-accent-soft/50 border-accent-bright/15 text-accent-bright hover:bg-accent-soft/65'
833
+ }`}
730
834
  style={{ fontFamily: 'inherit' }}
731
835
  >
732
- {item.label}
836
+ <AgentAvatar
837
+ seed={defaultAgent.avatarSeed || null}
838
+ avatarUrl={defaultAgent.avatarUrl}
839
+ name={defaultAgent.name}
840
+ size={32}
841
+ />
842
+ <div className="min-w-0">
843
+ <div className="text-[13px] font-700 truncate">{defaultAgent.name}</div>
844
+ <div className="text-[11px] text-accent-bright/70">Default shortcut</div>
845
+ </div>
733
846
  </button>
734
- ))}
847
+ </div>
848
+ )}
849
+ <div className="px-4 pb-3 shrink-0 max-h-[260px] overflow-y-auto">
850
+ <div className="space-y-4">
851
+ {([
852
+ { label: 'Workspace', views: ['agents', 'chatrooms', 'projects'] as AppView[] },
853
+ { label: 'Execution', views: ['tasks', 'approvals', 'schedules', 'memory', 'runs'] as AppView[] },
854
+ { label: 'Knowledge', views: ['knowledge', 'skills', 'connectors', 'webhooks', 'mcp_servers', 'plugins'] as AppView[] },
855
+ { label: 'System', views: ['secrets', 'providers', 'usage', 'logs'] as AppView[] },
856
+ ]).map((section) => {
857
+ const visibleViews = section.views.filter((view) => isViewEnabled(view))
858
+ if (!visibleViews.length) return null
859
+ return (
860
+ <div key={section.label}>
861
+ <div className="px-1 pb-2 text-[10px] font-700 uppercase tracking-[0.12em] text-text-3/45">
862
+ {section.label}
863
+ </div>
864
+ <div className="grid grid-cols-2 gap-2">
865
+ {visibleViews.map((view) => (
866
+ <button
867
+ key={view}
868
+ onClick={() => {
869
+ setActiveView(view)
870
+ if (FULL_WIDTH_VIEWS.has(view)) setSidebarOpen(false)
871
+ }}
872
+ className={`rounded-[12px] border px-3 py-2.5 text-left transition-all cursor-pointer ${
873
+ activeView === view
874
+ ? 'bg-accent-soft border-accent-bright/20 text-accent-bright'
875
+ : 'bg-transparent border-white/[0.06] text-text-3 hover:text-text hover:bg-white/[0.04]'
876
+ }`}
877
+ style={{ fontFamily: 'inherit' }}
878
+ >
879
+ <div className="text-[12px] font-600">{VIEW_LABELS[view]}</div>
880
+ <div className="text-[10px] text-current/60 mt-1">{VIEW_DESCRIPTIONS[view]}</div>
881
+ </button>
882
+ ))}
883
+ </div>
884
+ </div>
885
+ )
886
+ })}
887
+ {pluginSidebarItems.length > 0 && (
888
+ <div>
889
+ <div className="px-1 pb-2 text-[10px] font-700 uppercase tracking-[0.12em] text-text-3/45">
890
+ Extensions
891
+ </div>
892
+ <div className="grid grid-cols-2 gap-2">
893
+ {pluginSidebarItems.map((item) => (
894
+ <button
895
+ key={item.id}
896
+ onClick={() => window.open(item.href, '_blank')}
897
+ className="rounded-[12px] border border-emerald-400/10 bg-emerald-500/[0.05] px-3 py-2.5 text-left text-emerald-400/85 hover:text-emerald-300 transition-colors cursor-pointer"
898
+ style={{ fontFamily: 'inherit' }}
899
+ >
900
+ <div className="text-[12px] font-600">{item.label}</div>
901
+ <div className="text-[10px] text-emerald-300/60 mt-1">Open plugin view</div>
902
+ </button>
903
+ ))}
904
+ </div>
905
+ </div>
906
+ )}
907
+ </div>
735
908
  </div>
736
909
  {activeView !== 'logs' && activeView !== 'usage' && activeView !== 'runs' && activeView !== 'settings' && (
737
910
  <div className="px-4 py-2.5 shrink-0">
@@ -745,7 +918,7 @@ export function AppLayout() {
745
918
  shadow-[0_2px_12px_rgba(99,102,241,0.15)]"
746
919
  style={{ fontFamily: 'inherit' }}
747
920
  >
748
- + New {activeView === 'agents' ? 'Agent' : activeView === 'schedules' ? 'Schedule' : activeView === 'tasks' ? 'Task' : activeView === 'secrets' ? 'Secret' : activeView === 'providers' ? 'Provider' : activeView === 'skills' ? 'Skill' : activeView === 'connectors' ? 'Connector' : activeView === 'webhooks' ? 'Webhook' : activeView === 'mcp_servers' ? 'MCP Server' : activeView === 'knowledge' ? 'Knowledge' : activeView === 'plugins' ? 'Plugin' : activeView === 'projects' ? 'Project' : 'Entry'}
921
+ + New {CREATE_LABELS[activeView] || 'Entry'}
749
922
  </button>
750
923
  </div>
751
924
  )}
@@ -811,11 +984,27 @@ export function AppLayout() {
811
984
  <div className="flex-1 flex items-center justify-center px-8">
812
985
  <div className="text-center max-w-[420px]">
813
986
  <h2 className="font-display text-[24px] font-700 text-text mb-2 tracking-[-0.02em]">
814
- Select an Agent
987
+ Open a Chat
815
988
  </h2>
816
989
  <p className="text-[14px] text-text-3">
817
- Choose an agent from the sidebar to start chatting.
990
+ Choose a chat from the sidebar, or jump straight into your default agent shortcut.
818
991
  </p>
992
+ <div className="mt-5 flex items-center justify-center gap-3">
993
+ <button
994
+ onClick={defaultAgent ? goToDefaultChat : () => setAgentSheetOpen(true)}
995
+ className="inline-flex items-center gap-2 px-4 py-2.5 rounded-[12px] border-none bg-accent-bright text-white text-[13px] font-600 cursor-pointer hover:brightness-110 transition-all"
996
+ style={{ fontFamily: 'inherit' }}
997
+ >
998
+ {defaultAgent ? `Open ${defaultAgent.name}` : 'Create Agent'}
999
+ </button>
1000
+ <button
1001
+ onClick={() => setAgentSheetOpen(true)}
1002
+ className="inline-flex items-center gap-2 px-4 py-2.5 rounded-[12px] border border-white/[0.08] bg-transparent text-[13px] font-600 text-text-2 cursor-pointer hover:bg-white/[0.04] transition-all"
1003
+ style={{ fontFamily: 'inherit' }}
1004
+ >
1005
+ Create Agent
1006
+ </button>
1007
+ </div>
819
1008
  </div>
820
1009
  </div>
821
1010
  )}
@@ -865,7 +1054,7 @@ export function AppLayout() {
865
1054
  <div className="flex-1 flex flex-col h-full">
866
1055
  <div className="flex items-center px-6 pt-5 pb-3 shrink-0">
867
1056
  <h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] capitalize flex-1">
868
- {activeView === 'mcp_servers' ? 'MCP Servers' : activeView.replace('_', ' ')}
1057
+ {VIEW_LABELS[activeView]}
869
1058
  </h2>
870
1059
  {activeView !== 'runs' && activeView !== 'logs' && (
871
1060
  <button
@@ -876,7 +1065,7 @@ export function AppLayout() {
876
1065
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
877
1066
  <line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
878
1067
  </svg>
879
- {activeView === 'schedules' ? 'Schedule' : activeView === 'secrets' ? 'Secret' : activeView === 'providers' ? 'Provider' : activeView === 'skills' ? 'Skill' : activeView === 'connectors' ? 'Connector' : activeView === 'webhooks' ? 'Webhook' : activeView === 'mcp_servers' ? 'MCP Server' : activeView === 'knowledge' ? 'Knowledge' : activeView === 'plugins' ? 'Plugin' : 'New'}
1068
+ {CREATE_LABELS[activeView] || 'New'}
880
1069
  </button>
881
1070
  )}
882
1071
  </div>
@@ -981,9 +1170,9 @@ const VIEW_DESCRIPTIONS: Record<AppView, string> = {
981
1170
  chatrooms: 'Multi-agent collaborative chatrooms',
982
1171
  schedules: 'Automated task schedules',
983
1172
  memory: 'Long-term agent memory store',
984
- tasks: 'Task board for orchestrator jobs',
1173
+ tasks: 'Task board for agent work and queued runs',
985
1174
  approvals: 'Pending tool execution approvals',
986
- secrets: 'API keys & credentials for orchestrators',
1175
+ secrets: 'API keys, tokens, and encrypted credentials',
987
1176
  providers: 'LLM providers & custom endpoints',
988
1177
  skills: 'Reusable instruction sets for agents',
989
1178
  connectors: 'Chat platform bridges (Discord, Slack, etc.)',
@@ -995,7 +1184,7 @@ const VIEW_DESCRIPTIONS: Record<AppView, string> = {
995
1184
  usage: 'Usage metrics, cost tracking & agent performance',
996
1185
  wallets: 'Agent crypto wallets — hold funds, send SOL, manage spending',
997
1186
  runs: 'Live run monitoring & history',
998
- settings: 'Manage providers, API keys & orchestrator engine',
1187
+ settings: 'Manage defaults, providers, secrets, and automation settings',
999
1188
  projects: 'Group agents, tasks & schedules into projects',
1000
1189
  activity: 'Audit trail of all entity mutations',
1001
1190
  }
@@ -1016,26 +1205,26 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents' | 'home'>, { icon: str
1016
1205
  schedules: {
1017
1206
  icon: 'clock',
1018
1207
  title: 'Schedules',
1019
- description: 'Automate recurring tasks by scheduling orchestrators to run on a cron, interval, or one-time basis.',
1020
- features: ['Set up cron expressions for precise timing', 'Run orchestrators automatically on intervals', 'Schedule one-time future tasks', 'View execution history and results'],
1208
+ description: 'Automate recurring work with cron, interval, or one-time schedules that launch agent tasks.',
1209
+ features: ['Set up cron expressions for precise timing', 'Run agents automatically on intervals', 'Schedule one-time future tasks', 'View execution history and results'],
1021
1210
  },
1022
1211
  memory: {
1023
1212
  icon: 'database',
1024
1213
  title: 'Memory',
1025
- description: 'Long-term memory store for AI agents. Orchestrators can store and retrieve knowledge across conversations.',
1214
+ description: 'Long-term memory store for AI agents so they can retain useful context across conversations.',
1026
1215
  features: ['Agents store findings and learnings automatically', 'Full-text search across all stored memories', 'Organized by categories and agents', 'Persists across conversations for continuity'],
1027
1216
  },
1028
1217
  tasks: {
1029
1218
  icon: 'clipboard',
1030
1219
  title: 'Task Board',
1031
- description: 'A Trello-style board for managing orchestrator jobs. Create tasks, assign them to orchestrators, and track progress.',
1032
- features: ['Kanban columns: Backlog, Queued, Running, Completed, Failed', 'Assign tasks to specific orchestrator agents', 'Sequential queue ensures orchestrators don\'t conflict', 'View results and logs for completed tasks'],
1220
+ description: 'A kanban board for managing agent work. Create tasks, assign them to agents, and track progress.',
1221
+ features: ['Kanban columns: Backlog, Queued, Running, Completed, Failed', 'Assign tasks to specific agents', 'Track retries, results, and logs', 'Review status without leaving the board'],
1033
1222
  },
1034
1223
  secrets: {
1035
1224
  icon: 'lock',
1036
1225
  title: 'Secrets',
1037
- description: 'Manage API keys and credentials that orchestrators can access during task execution.',
1038
- features: ['Store keys for external services (Gmail, APIs, etc.)', 'Scope secrets globally or to specific orchestrators', 'Encrypted at rest with AES-256-GCM', 'Orchestrators retrieve secrets via the get_secret tool'],
1226
+ description: 'Manage API keys and credentials that agents and integrations can access securely.',
1227
+ features: ['Store keys for external services (Gmail, APIs, etc.)', 'Scope secrets globally or to specific agents', 'Encrypted at rest with AES-256-GCM', 'Agents retrieve secrets through approved tools'],
1039
1228
  },
1040
1229
  providers: {
1041
1230
  icon: 'zap',
@@ -1058,8 +1247,8 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents' | 'home'>, { icon: str
1058
1247
  webhooks: {
1059
1248
  icon: 'webhook',
1060
1249
  title: 'Webhooks',
1061
- description: 'Receive external events over HTTP and trigger orchestrator runs automatically.',
1062
- features: ['Create secure inbound webhook endpoints', 'Filter events by type or source', 'Route each webhook to a specific orchestrator', 'Use x-webhook-secret for request authentication'],
1250
+ description: 'Receive external events over HTTP and route them into agent-driven workflows automatically.',
1251
+ features: ['Create secure inbound webhook endpoints', 'Filter events by type or source', 'Route each webhook to a specific agent', 'Use x-webhook-secret for request authentication'],
1063
1252
  },
1064
1253
  mcp_servers: {
1065
1254
  icon: 'server',
@@ -1070,7 +1259,7 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents' | 'home'>, { icon: str
1070
1259
  knowledge: {
1071
1260
  icon: 'globe',
1072
1261
  title: 'Knowledge Base',
1073
- description: 'A shared knowledge graph accessible by all agents, enabling cross-agent information sharing and orchestration.',
1262
+ description: 'A shared knowledge graph accessible by all agents for cross-workspace information sharing.',
1074
1263
  features: ['Create tagged knowledge entries', 'Agents can store and search knowledge via tools', 'Full-text and vector search', 'Provenance tracking per entry'],
1075
1264
  },
1076
1265
  logs: {
@@ -1100,8 +1289,8 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents' | 'home'>, { icon: str
1100
1289
  settings: {
1101
1290
  icon: 'settings',
1102
1291
  title: 'Settings',
1103
- description: 'Manage providers, API keys & orchestrator engine.',
1104
- features: ['Configure LLM providers', 'Manage API credentials', 'Tune orchestrator settings', 'Set up voice & embedding'],
1292
+ description: 'Manage app defaults, providers, encrypted secrets, and automation settings.',
1293
+ features: ['Choose your default agent shortcut', 'Configure LLM providers and credentials', 'Tune heartbeat and autonomy settings', 'Set up voice, embeddings, and search'],
1105
1294
  },
1106
1295
  projects: {
1107
1296
  icon: 'folder',
@@ -1283,7 +1472,7 @@ function RailTooltip({ label, description, children }: { label: string; descript
1283
1472
  }
1284
1473
 
1285
1474
  function DesktopEmptyState({ userName }: { userName: string | null }) {
1286
- const setNewSessionOpen = useAppStore((s) => s.setNewSessionOpen)
1475
+ const setAgentSheetOpen = useAppStore((s) => s.setAgentSheetOpen)
1287
1476
 
1288
1477
  return (
1289
1478
  <div className="flex-1 flex flex-col items-center justify-center px-8 pb-20 relative overflow-hidden">
@@ -1314,10 +1503,10 @@ function DesktopEmptyState({ userName }: { userName: string | null }) {
1314
1503
  <span className="text-text-2">What would you like to do?</span>
1315
1504
  </h1>
1316
1505
  <p className="text-[15px] text-text-3 mb-12">
1317
- Create a new chat to start chatting
1506
+ Create an agent, then keep working in its persistent thread
1318
1507
  </p>
1319
1508
  <button
1320
- onClick={() => setNewSessionOpen(true)}
1509
+ onClick={() => setAgentSheetOpen(true)}
1321
1510
  className="inline-flex items-center gap-2.5 px-12 py-4 rounded-[16px] border-none bg-accent-bright text-white text-[16px] font-display font-600
1322
1511
  cursor-pointer hover:brightness-110 active:scale-[0.97] transition-all duration-200
1323
1512
  shadow-[0_6px_28px_rgba(99,102,241,0.3)]"
@@ -1327,7 +1516,7 @@ function DesktopEmptyState({ userName }: { userName: string | null }) {
1327
1516
  <line x1="12" y1="5" x2="12" y2="19" />
1328
1517
  <line x1="5" y1="12" x2="19" y2="12" />
1329
1518
  </svg>
1330
- New Chat
1519
+ New Agent
1331
1520
  </button>
1332
1521
  </div>
1333
1522
  </div>