@swarmclawai/swarmclaw 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +85 -139
  2. package/package.json +1 -1
  3. package/src/app/api/agents/[id]/thread/route.ts +1 -2
  4. package/src/app/api/agents/route.ts +1 -1
  5. package/src/app/api/{sessions → chats}/[id]/checkpoints/route.ts +1 -1
  6. package/src/app/api/{sessions → chats}/[id]/main-loop/route.ts +2 -2
  7. package/src/app/api/{sessions → chats}/[id]/restore/route.ts +1 -1
  8. package/src/app/api/{sessions → chats}/[id]/route.ts +4 -52
  9. package/src/app/api/{sessions → chats}/route.ts +5 -7
  10. package/src/app/api/plugins/route.ts +3 -0
  11. package/src/app/api/plugins/settings/route.ts +35 -0
  12. package/src/app/api/usage/route.ts +30 -0
  13. package/src/cli/index.js +35 -33
  14. package/src/cli/index.ts +40 -39
  15. package/src/cli/spec.js +29 -27
  16. package/src/components/agents/agent-card.tsx +1 -1
  17. package/src/components/agents/agent-chat-list.tsx +3 -3
  18. package/src/components/agents/agent-list.tsx +8 -13
  19. package/src/components/agents/agent-sheet.tsx +2 -2
  20. package/src/components/agents/cron-job-form.tsx +3 -3
  21. package/src/components/agents/inspector-panel.tsx +2 -2
  22. package/src/components/auth/setup-wizard.tsx +5 -38
  23. package/src/components/chat/chat-area.tsx +10 -14
  24. package/src/components/{sessions/session-card.tsx → chat/chat-card.tsx} +3 -3
  25. package/src/components/chat/chat-header.tsx +156 -73
  26. package/src/components/{sessions/session-list.tsx → chat/chat-list.tsx} +4 -5
  27. package/src/components/chat/chat-tool-toggles.tsx +26 -17
  28. package/src/components/chat/checkpoint-timeline.tsx +4 -4
  29. package/src/components/chat/message-bubble.tsx +4 -1
  30. package/src/components/chat/message-list.tsx +2 -2
  31. package/src/components/{sessions/new-session-sheet.tsx → chat/new-chat-sheet.tsx} +6 -6
  32. package/src/components/chat/session-debug-panel.tsx +1 -1
  33. package/src/components/chat/tool-request-banner.tsx +3 -3
  34. package/src/components/chatrooms/agent-hover-card.tsx +3 -3
  35. package/src/components/chatrooms/chatroom-tool-request-banner.tsx +2 -2
  36. package/src/components/connectors/connector-sheet.tsx +1 -1
  37. package/src/components/home/home-view.tsx +1 -1
  38. package/src/components/layout/app-layout.tsx +23 -2
  39. package/src/components/plugins/plugin-list.tsx +475 -254
  40. package/src/components/plugins/plugin-sheet.tsx +124 -10
  41. package/src/components/settings/gateway-connection-panel.tsx +1 -1
  42. package/src/components/shared/command-palette.tsx +0 -1
  43. package/src/components/shared/settings/section-heartbeat.tsx +1 -1
  44. package/src/components/shared/settings/section-providers.tsx +1 -1
  45. package/src/components/shared/settings/settings-page.tsx +1 -12
  46. package/src/components/usage/metrics-dashboard.tsx +73 -0
  47. package/src/components/webhooks/webhook-sheet.tsx +1 -1
  48. package/src/lib/chat.ts +1 -1
  49. package/src/lib/{sessions.ts → chats.ts} +28 -18
  50. package/src/lib/providers/claude-cli.ts +1 -1
  51. package/src/lib/server/approvals.ts +4 -4
  52. package/src/lib/server/capability-router.ts +10 -8
  53. package/src/lib/server/chat-execution.ts +36 -105
  54. package/src/lib/server/chatroom-helpers.ts +3 -3
  55. package/src/lib/server/connectors/manager.ts +4 -4
  56. package/src/lib/server/cost.ts +34 -1
  57. package/src/lib/server/daemon-state.ts +2 -2
  58. package/src/lib/server/heartbeat-service.ts +1 -1
  59. package/src/lib/server/main-agent-loop.ts +25 -160
  60. package/src/lib/server/main-session.ts +6 -13
  61. package/src/lib/server/orchestrator-lg.ts +3 -3
  62. package/src/lib/server/orchestrator.ts +5 -5
  63. package/src/lib/server/plugins.ts +112 -4
  64. package/src/lib/server/provider-health.ts +5 -3
  65. package/src/lib/server/queue.ts +12 -10
  66. package/src/lib/server/session-run-manager.test.ts +9 -6
  67. package/src/lib/server/session-run-manager.ts +1 -3
  68. package/src/lib/server/session-tools/calendar.ts +376 -0
  69. package/src/lib/server/session-tools/canvas.ts +1 -1
  70. package/src/lib/server/session-tools/chatroom.ts +4 -2
  71. package/src/lib/server/session-tools/connector.ts +5 -2
  72. package/src/lib/server/session-tools/context.ts +7 -3
  73. package/src/lib/server/session-tools/crud.ts +14 -6
  74. package/src/lib/server/session-tools/delegate.ts +95 -8
  75. package/src/lib/server/session-tools/discovery.ts +2 -2
  76. package/src/lib/server/session-tools/edit_file.ts +4 -2
  77. package/src/lib/server/session-tools/email.ts +322 -0
  78. package/src/lib/server/session-tools/file.ts +5 -2
  79. package/src/lib/server/session-tools/git.ts +1 -1
  80. package/src/lib/server/session-tools/http.ts +1 -1
  81. package/src/lib/server/session-tools/image-gen.ts +382 -0
  82. package/src/lib/server/session-tools/index.ts +74 -49
  83. package/src/lib/server/session-tools/memory.ts +139 -2
  84. package/src/lib/server/session-tools/monitor.ts +1 -1
  85. package/src/lib/server/session-tools/openclaw-nodes.ts +1 -1
  86. package/src/lib/server/session-tools/openclaw-workspace.ts +1 -1
  87. package/src/lib/server/session-tools/platform.ts +6 -3
  88. package/src/lib/server/session-tools/plugin-creator.ts +3 -3
  89. package/src/lib/server/session-tools/replicate.ts +303 -0
  90. package/src/lib/server/session-tools/sample-ui.ts +1 -1
  91. package/src/lib/server/session-tools/sandbox.ts +4 -2
  92. package/src/lib/server/session-tools/schedule.ts +4 -2
  93. package/src/lib/server/session-tools/session-info.ts +7 -4
  94. package/src/lib/server/session-tools/shell.ts +5 -2
  95. package/src/lib/server/session-tools/subagent.ts +2 -2
  96. package/src/lib/server/session-tools/wallet.ts +29 -2
  97. package/src/lib/server/session-tools/web.ts +44 -5
  98. package/src/lib/server/storage.ts +29 -9
  99. package/src/lib/server/stream-agent-chat.ts +72 -249
  100. package/src/lib/server/tool-aliases.ts +26 -15
  101. package/src/lib/server/tool-capability-policy.test.ts +9 -9
  102. package/src/lib/server/tool-capability-policy.ts +32 -27
  103. package/src/lib/tool-definitions.ts +4 -0
  104. package/src/lib/validation/schemas.ts +3 -1
  105. package/src/stores/use-app-store.ts +5 -5
  106. package/src/stores/use-chat-store.ts +7 -7
  107. package/src/types/index.ts +65 -3
  108. /package/src/app/api/{sessions → chats}/[id]/browser/route.ts +0 -0
  109. /package/src/app/api/{sessions → chats}/[id]/chat/route.ts +0 -0
  110. /package/src/app/api/{sessions → chats}/[id]/clear/route.ts +0 -0
  111. /package/src/app/api/{sessions → chats}/[id]/deploy/route.ts +0 -0
  112. /package/src/app/api/{sessions → chats}/[id]/devserver/route.ts +0 -0
  113. /package/src/app/api/{sessions → chats}/[id]/edit-resend/route.ts +0 -0
  114. /package/src/app/api/{sessions → chats}/[id]/fork/route.ts +0 -0
  115. /package/src/app/api/{sessions → chats}/[id]/mailbox/route.ts +0 -0
  116. /package/src/app/api/{sessions → chats}/[id]/messages/route.ts +0 -0
  117. /package/src/app/api/{sessions → chats}/[id]/retry/route.ts +0 -0
  118. /package/src/app/api/{sessions → chats}/[id]/stop/route.ts +0 -0
  119. /package/src/app/api/{sessions → chats}/heartbeat/route.ts +0 -0
@@ -26,7 +26,7 @@ export function CheckpointTimeline({ sessionId }: Props) {
26
26
  const load = async () => {
27
27
  setLoading(true)
28
28
  try {
29
- const data = await api<Checkpoint[]>('GET', `/sessions/${sessionId}/checkpoints`)
29
+ const data = await api<Checkpoint[]>('GET', `/chats/${sessionId}/checkpoints`)
30
30
  setCheckpoints(data)
31
31
  } catch (err) {
32
32
  console.error('Failed to load checkpoints', err)
@@ -45,7 +45,7 @@ export function CheckpointTimeline({ sessionId }: Props) {
45
45
 
46
46
  setRestoringId(checkpoint.checkpointId)
47
47
  try {
48
- await api('POST', `/sessions/${sessionId}/restore`, {
48
+ await api('POST', `/chats/${sessionId}/restore`, {
49
49
  checkpointId: checkpoint.checkpointId,
50
50
  timestamp: checkpoint.createdAt
51
51
  })
@@ -67,8 +67,8 @@ export function CheckpointTimeline({ sessionId }: Props) {
67
67
  if (checkpoints.length === 0) {
68
68
  return (
69
69
  <div className="p-8 text-center">
70
- <p className="text-text-3 text-[13px]">No checkpoints found for this session.</p>
71
- <p className="text-[11px] text-text-3/50 mt-1">Only LangGraph-orchestrated sessions support time travel.</p>
70
+ <p className="text-text-3 text-[13px]">No checkpoints found for this chat.</p>
71
+ <p className="text-[11px] text-text-3/50 mt-1">Only LangGraph-orchestrated chats support time travel.</p>
72
72
  </div>
73
73
  )
74
74
  }
@@ -243,7 +243,10 @@ export const MessageBubble = memo(function MessageBubble({ message, assistantNam
243
243
  const delegationSource = !isUser && message.kind === 'system' ? parseDelegationSource(message.text || '') : null
244
244
  // Detect task completion system messages (delegated or direct)
245
245
  const taskCompletion = !isUser && message.kind === 'system' ? parseTaskCompletion(message.text || '') : null
246
- const displayText = delegationSource ? delegationSource.rest : message.text
246
+ const rawDisplayText = delegationSource ? delegationSource.rest : message.text
247
+ const displayText = rawDisplayText
248
+ ? rawDisplayText.split('\n').filter((l) => !/\[(MAIN_LOOP_META|MAIN_LOOP_PLAN|MAIN_LOOP_REVIEW|AGENT_HEARTBEAT_META)\]/.test(l)).join('\n').trim()
249
+ : ''
247
250
 
248
251
  const handleCopy = useCallback(() => {
249
252
  void copyTextToClipboard(message.text).then((copiedText) => {
@@ -129,7 +129,7 @@ export function MessageList({ messages, streaming, connectorFilter = null }: Pro
129
129
  if (!msg) return
130
130
  const next = !msg.bookmarked
131
131
  try {
132
- await api('PUT', `/sessions/${sessionId}/messages`, { messageIndex: index, bookmarked: next })
132
+ await api('PUT', `/chats/${sessionId}/messages`, { messageIndex: index, bookmarked: next })
133
133
  const updated = [...messages]
134
134
  updated[index] = { ...updated[index], bookmarked: next }
135
135
  setMessages(updated)
@@ -469,7 +469,7 @@ export function MessageList({ messages, streaming, connectorFilter = null }: Pro
469
469
  type="button"
470
470
  onClick={async () => {
471
471
  try {
472
- await api('DELETE', `/sessions/${sessionId}/messages`, { messageIndex: originalIndex })
472
+ await api('DELETE', `/chats/${sessionId}/messages`, { messageIndex: originalIndex })
473
473
  setMessages(messages.filter((_: Message, idx: number) => idx !== originalIndex))
474
474
  } catch { /* best-effort */ }
475
475
  }}
@@ -17,7 +17,7 @@ interface Props {
17
17
  onClose: () => void
18
18
  }
19
19
 
20
- export function NewSessionSheet({ open, onClose }: Props) {
20
+ export function NewChatSheet({ open, onClose }: Props) {
21
21
  const router = useRouter()
22
22
  const agents = useAppStore((s) => s.agents)
23
23
  const loadSessions = useAppStore((s) => s.loadSessions)
@@ -47,7 +47,7 @@ export function NewSessionSheet({ open, onClose }: Props) {
47
47
  const id = genId(8)
48
48
  const now = Date.now()
49
49
 
50
- const agentTools = agent?.tools || (selectedTools.length ? selectedTools : undefined)
50
+ const agentTools = agent?.plugins || (selectedTools.length ? selectedTools : undefined)
51
51
 
52
52
  const session = {
53
53
  id,
@@ -56,7 +56,7 @@ export function NewSessionSheet({ open, onClose }: Props) {
56
56
  model: agent ? agent.model : model,
57
57
  apiEndpoint: agent ? agent.apiEndpoint : (endpoint || undefined),
58
58
  credentialId: agent ? agent.credentialId : undefined,
59
- tools: agentTools || undefined,
59
+ plugins: agentTools || undefined,
60
60
  messages: [],
61
61
  createdAt: now,
62
62
  updatedAt: now,
@@ -64,7 +64,7 @@ export function NewSessionSheet({ open, onClose }: Props) {
64
64
  agentId: selectedAgentId || undefined,
65
65
  }
66
66
 
67
- await api('POST', '/sessions', session)
67
+ await api('POST', '/chats', session)
68
68
  await loadSessions()
69
69
  router.push(`/chat?session=${id}`)
70
70
  onClose()
@@ -223,10 +223,10 @@ export function NewSessionSheet({ open, onClose }: Props) {
223
223
  Using <span className="text-text-2 font-600">{agents[selectedAgentId].provider}</span>
224
224
  {' / '}
225
225
  <span className="text-text-2 font-600">{agents[selectedAgentId].model}</span>
226
- {agents[selectedAgentId].tools?.length ? (
226
+ {agents[selectedAgentId].plugins?.length ? (
227
227
  <>
228
228
  {' + '}
229
- {agents[selectedAgentId].tools!.map((tool, i) => (
229
+ {agents[selectedAgentId].plugins!.map((tool, i) => (
230
230
  <span key={tool}>
231
231
  {i > 0 && ', '}
232
232
  <span className="text-sky-400/70 font-600 cursor-help" title={TOOL_DESCRIPTIONS[tool] || tool}>
@@ -209,7 +209,7 @@ export function SessionDebugPanel({ messages, open, onClose }: Props) {
209
209
  {currentSessionId ? (
210
210
  <CheckpointTimeline sessionId={currentSessionId} />
211
211
  ) : (
212
- <div className="p-12 text-center text-text-3">No active session</div>
212
+ <div className="p-12 text-center text-text-3">No active chat</div>
213
213
  )}
214
214
  </div>
215
215
  )}
@@ -62,13 +62,13 @@ export function ToolRequestBanner({ text, toolOutputs = [] }: Props) {
62
62
 
63
63
  const handleGrant = async (toolId: string) => {
64
64
  if (!sid || !session) return
65
- const currentTools: string[] = session.tools || []
65
+ const currentTools: string[] = session.plugins || []
66
66
  if (currentTools.includes(toolId)) {
67
67
  setGranted((prev) => new Set(prev).add(toolId))
68
68
  return
69
69
  }
70
70
  const updated = [...currentTools, toolId]
71
- await api('PUT', `/sessions/${sid}`, { tools: updated })
71
+ await api('PUT', `/chats/${sid}`, { plugins: updated })
72
72
  await loadSessions()
73
73
  const newGranted = new Set(granted).add(toolId)
74
74
  setGranted(newGranted)
@@ -108,7 +108,7 @@ export function ToolRequestBanner({ text, toolOutputs = [] }: Props) {
108
108
  return (
109
109
  <div className="max-w-[85%] md:max-w-[72%] flex flex-col gap-2 mt-2">
110
110
  {pluginRequests.map(({ pluginId, reason }) => {
111
- const isGranted = granted.has(pluginId) || (session?.tools || []).includes(pluginId)
111
+ const isGranted = granted.has(pluginId) || (session?.plugins || []).includes(pluginId)
112
112
  const isDenied = denied.has(pluginId)
113
113
  const label = TOOL_LABELS[pluginId] || pluginId
114
114
  return (
@@ -19,7 +19,7 @@ const ALL_TOOL_IDS = [...AVAILABLE_TOOLS, ...PLATFORM_TOOLS].map((t) => t.id)
19
19
  export function AgentHoverCard({ agent, children, status }: Props) {
20
20
  const [showAll, setShowAll] = useState(false)
21
21
  const [busy, setBusy] = useState(false)
22
- const tools = agent.tools ?? []
22
+ const tools = agent.plugins ?? []
23
23
 
24
24
  const displayTools = showAll ? ALL_TOOL_IDS : tools
25
25
 
@@ -27,11 +27,11 @@ export function AgentHoverCard({ agent, children, status }: Props) {
27
27
  if (busy) return
28
28
  setBusy(true)
29
29
  try {
30
- const current = agent.tools || []
30
+ const current = agent.plugins || []
31
31
  const updated = current.includes(toolId)
32
32
  ? current.filter((t) => t !== toolId)
33
33
  : [...current, toolId]
34
- await api('PUT', `/agents/${agent.id}`, { tools: updated })
34
+ await api('PUT', `/agents/${agent.id}`, { plugins: updated })
35
35
  useAppStore.getState().loadAgents()
36
36
  } finally {
37
37
  setBusy(false)
@@ -44,7 +44,7 @@ export function ChatroomToolRequestBanner({ agentId, agentName, text, toolOutput
44
44
  if (toolRequests.length === 0) return null
45
45
 
46
46
  const agent = agents[agentId]
47
- const agentTools: string[] = agent?.tools || []
47
+ const agentTools: string[] = agent?.plugins || []
48
48
 
49
49
  const handleGrant = async (toolId: string) => {
50
50
  if (agentTools.includes(toolId)) {
@@ -52,7 +52,7 @@ export function ChatroomToolRequestBanner({ agentId, agentName, text, toolOutput
52
52
  return
53
53
  }
54
54
  const updated = [...agentTools, toolId]
55
- await api('PUT', `/agents/${agentId}`, { tools: updated })
55
+ await api('PUT', `/agents/${agentId}`, { plugins: updated })
56
56
  await loadAgents()
57
57
  const newGranted = new Set(granted).add(toolId)
58
58
  setGranted(newGranted)
@@ -121,7 +121,7 @@ const PLATFORMS: {
121
121
  tokenHelp: 'Required when your OpenClaw gateway is auth-protected',
122
122
  configFields: [
123
123
  { key: 'wsUrl', label: 'WebSocket URL', placeholder: 'ws://localhost:18789', help: 'OpenClaw gateway WebSocket endpoint (root URL, not /ws)' },
124
- { key: 'sessionKey', label: 'Chat Key Filter', placeholder: 'main', help: 'Optional. If set, only inbound events for this OpenClaw session are processed.' },
124
+ { key: 'sessionKey', label: 'Chat Key Filter', placeholder: 'main', help: 'Optional. If set, only inbound events for this OpenClaw chat are processed.' },
125
125
  { key: 'nodeId', label: 'Client Label', placeholder: 'swarmclaw', help: 'Optional display label shown in OpenClaw presence metadata.' },
126
126
  { key: 'role', label: 'Gateway Role', placeholder: 'operator', help: 'Optional role claim for connect handshake. Default is operator.' },
127
127
  { key: 'scopes', label: 'Scopes (CSV)', placeholder: 'operator.read,operator.write', help: 'Optional comma-separated scopes for OpenClaw connect.' },
@@ -379,7 +379,7 @@ export function HomeView() {
379
379
  <div className="flex gap-3 overflow-x-auto pb-2">
380
380
  {pinnedAgents.map((agent) => {
381
381
  const threadSession = agent.threadSessionId ? sessions[agent.threadSessionId] as Session | undefined : undefined
382
- const heartbeatOn = agent.heartbeatEnabled === true && (agent.tools?.length ?? 0) > 0
382
+ const heartbeatOn = agent.heartbeatEnabled === true && (agent.plugins?.length ?? 0) > 0
383
383
  const recentlyActive = (threadSession?.lastActiveAt ?? 0) > Date.now() - 30 * 60 * 1000
384
384
  const isOnline = runningAgentIds.has(agent.id) || (threadSession?.active ?? false) || heartbeatOn || recentlyActive
385
385
  const isTyping = streamingSessionId === agent.threadSessionId
@@ -92,6 +92,8 @@ export function AppLayout() {
92
92
  const setPluginSheetOpen = useAppStore((s) => s.setPluginSheetOpen)
93
93
  const setProjectSheetOpen = useAppStore((s) => s.setProjectSheetOpen)
94
94
  const tasks = useAppStore((s) => s.tasks)
95
+ const plugins = useAppStore((s) => s.plugins)
96
+ const loadPlugins = useAppStore((s) => s.loadPlugins)
95
97
  const approvals = useAppStore((s) => s.approvals)
96
98
  const loadApprovals = useAppStore((s) => s.loadApprovals)
97
99
  const execApprovals = useApprovalStore((s) => s.approvals)
@@ -178,11 +180,16 @@ export function AppLayout() {
178
180
 
179
181
  const [pluginSidebarItems, setPluginSidebarItems] = useState<Array<{ id: string; label: string; href: string }>>([])
180
182
 
181
- useEffect(() => {
183
+ const refreshPluginState = useCallback(() => {
182
184
  api<Array<{ id: string; label: string; href: string }>>('GET', '/plugins/ui?type=sidebar').then(items => {
183
185
  if (Array.isArray(items)) setPluginSidebarItems(items)
184
186
  }).catch(() => {})
185
- }, [])
187
+ void loadPlugins()
188
+ }, [loadPlugins])
189
+
190
+ useEffect(() => { refreshPluginState() }, [refreshPluginState])
191
+
192
+ useWs('plugins', refreshPluginState)
186
193
 
187
194
  const [railExpanded, setRailExpanded] = useState(() => {
188
195
  const stored = safeStorageGet(RAIL_EXPANDED_KEY)
@@ -384,27 +391,33 @@ export function AppLayout() {
384
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" />
385
392
  </svg>
386
393
  </NavItem>
394
+ {plugins['chatroom']?.enabled !== false && (
387
395
  <NavItem view="chatrooms" label="Chatrooms" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('chatrooms')}>
388
396
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
389
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" />
390
398
  <path d="M8 10h8" /><path d="M8 14h4" />
391
399
  </svg>
392
400
  </NavItem>
401
+ )}
393
402
  <NavItem view="projects" label="Projects" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('projects')}>
394
403
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
395
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" />
396
405
  </svg>
397
406
  </NavItem>
407
+ {plugins['schedule']?.enabled !== false && (
398
408
  <NavItem view="schedules" label="Schedules" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('schedules')}>
399
409
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
400
410
  <circle cx="12" cy="12" r="10" /><polyline points="12 6 12 12 16 14" />
401
411
  </svg>
402
412
  </NavItem>
413
+ )}
414
+ {plugins['memory']?.enabled !== false && (
403
415
  <NavItem view="memory" label="Memory" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('memory')}>
404
416
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
405
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" />
406
418
  </svg>
407
419
  </NavItem>
420
+ )}
408
421
  <NavItem view="tasks" label="Tasks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('tasks')} badge={pendingApprovalCount}>
409
422
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
410
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" />
@@ -431,16 +444,20 @@ export function AppLayout() {
431
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" />
432
445
  </svg>
433
446
  </NavItem>
447
+ {plugins['connectors']?.enabled !== false && (
434
448
  <NavItem view="connectors" label="Connectors" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('connectors')}>
435
449
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
436
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" />
437
451
  </svg>
438
452
  </NavItem>
453
+ )}
454
+ {plugins['http']?.enabled !== false && (
439
455
  <NavItem view="webhooks" label="Webhooks" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('webhooks')}>
440
456
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
441
457
  <path d="M22 12h-4l-3 7L9 5l-3 7H2" />
442
458
  </svg>
443
459
  </NavItem>
460
+ )}
444
461
  <NavItem view="mcp_servers" label="MCP" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('mcp_servers')}>
445
462
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
446
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" />
@@ -461,11 +478,13 @@ export function AppLayout() {
461
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" />
462
479
  </svg>
463
480
  </NavItem>
481
+ {plugins['wallet']?.enabled !== false && (
464
482
  <NavItem view="wallets" label="Wallets" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('wallets')}>
465
483
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
466
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" />
467
485
  </svg>
468
486
  </NavItem>
487
+ )}
469
488
  <NavItem view="runs" label="Runs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('runs')}>
470
489
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
471
490
  <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
@@ -476,11 +495,13 @@ export function AppLayout() {
476
495
  <path d="M12 8v4l3 3" /><circle cx="12" cy="12" r="10" />
477
496
  </svg>
478
497
  </NavItem>
498
+ {plugins['monitor']?.enabled !== false && (
479
499
  <NavItem view="logs" label="Logs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('logs')}>
480
500
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
481
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" />
482
502
  </svg>
483
503
  </NavItem>
504
+ )}
484
505
  </div>
485
506
 
486
507
  <div className="flex-1" />