@swarmclawai/swarmclaw 0.7.2 → 0.7.4
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.
- package/README.md +116 -50
- package/bin/package-manager.js +157 -0
- package/bin/package-manager.test.js +90 -0
- package/bin/server-cmd.js +38 -7
- package/bin/swarmclaw.js +54 -4
- package/bin/update-cmd.js +48 -10
- package/bin/update-cmd.test.js +55 -0
- package/package.json +8 -3
- package/scripts/postinstall.mjs +26 -0
- package/src/app/api/agents/[id]/route.ts +43 -0
- package/src/app/api/agents/[id]/thread/route.ts +39 -8
- package/src/app/api/agents/route.ts +35 -2
- package/src/app/api/auth/route.ts +77 -8
- package/src/app/api/chatrooms/[id]/chat/route.ts +22 -6
- package/src/app/api/chatrooms/[id]/pins/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/reactions/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/route.ts +6 -0
- package/src/app/api/chats/[id]/browser/route.ts +5 -1
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/messages/route.ts +19 -13
- package/src/app/api/chats/[id]/route.ts +30 -0
- package/src/app/api/chats/[id]/stop/route.ts +6 -1
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/route.ts +23 -1
- package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
- package/src/app/api/connectors/doctor/route.ts +13 -0
- package/src/app/api/external-agents/[id]/heartbeat/route.ts +33 -0
- package/src/app/api/external-agents/[id]/route.ts +31 -0
- package/src/app/api/external-agents/register/route.ts +3 -0
- package/src/app/api/external-agents/route.ts +66 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/gateways/[id]/health/route.ts +28 -0
- package/src/app/api/gateways/[id]/route.ts +79 -0
- package/src/app/api/gateways/route.ts +57 -0
- package/src/app/api/memory/maintenance/route.ts +11 -1
- package/src/app/api/openclaw/agent-files/route.ts +27 -4
- package/src/app/api/openclaw/gateway/route.ts +10 -7
- package/src/app/api/openclaw/skills/route.ts +12 -4
- package/src/app/api/plugins/dependencies/route.ts +24 -0
- package/src/app/api/plugins/install/route.ts +15 -92
- package/src/app/api/plugins/route.ts +3 -26
- package/src/app/api/plugins/settings/route.ts +17 -12
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/providers/[id]/discover-models/route.ts +27 -0
- package/src/app/api/schedules/[id]/route.ts +38 -9
- package/src/app/api/schedules/route.ts +51 -28
- package/src/app/api/settings/route.ts +55 -17
- package/src/app/api/setup/doctor/route.ts +6 -4
- package/src/app/api/tasks/[id]/route.ts +16 -6
- package/src/app/api/tasks/bulk/route.ts +3 -3
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +135 -17
- package/src/cli/binary.test.js +142 -0
- package/src/cli/index.js +38 -11
- package/src/cli/index.test.js +195 -0
- package/src/cli/index.ts +21 -12
- package/src/cli/server-cmd.test.js +59 -0
- package/src/cli/spec.js +20 -2
- package/src/components/agents/agent-card.tsx +15 -12
- package/src/components/agents/agent-chat-list.tsx +101 -1
- package/src/components/agents/agent-list.tsx +46 -9
- package/src/components/agents/agent-sheet.tsx +456 -23
- package/src/components/agents/inspector-panel.tsx +110 -49
- package/src/components/agents/sandbox-env-panel.tsx +4 -1
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +970 -275
- package/src/components/chat/chat-area.tsx +70 -27
- package/src/components/chat/chat-card.tsx +6 -21
- package/src/components/chat/chat-header.tsx +263 -366
- package/src/components/chat/chat-list.tsx +62 -26
- package/src/components/chat/checkpoint-timeline.tsx +1 -1
- package/src/components/chat/message-list.tsx +145 -19
- package/src/components/chatrooms/chatroom-input.tsx +96 -33
- package/src/components/chatrooms/chatroom-list.tsx +141 -72
- package/src/components/chatrooms/chatroom-message.tsx +7 -6
- package/src/components/chatrooms/chatroom-sheet.tsx +13 -1
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +5 -2
- package/src/components/chatrooms/chatroom-view.tsx +422 -209
- package/src/components/chatrooms/reaction-picker.tsx +38 -33
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +217 -0
- package/src/components/gateways/gateway-sheet.tsx +567 -0
- package/src/components/home/home-view.tsx +128 -4
- package/src/components/input/chat-input.tsx +135 -86
- package/src/components/layout/app-layout.tsx +385 -194
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/memory/memory-browser.tsx +71 -6
- package/src/components/memory/memory-card.tsx +18 -0
- package/src/components/memory/memory-detail.tsx +58 -31
- package/src/components/memory/memory-sheet.tsx +32 -4
- package/src/components/plugins/plugin-list.tsx +15 -3
- package/src/components/plugins/plugin-sheet.tsx +118 -9
- package/src/components/projects/project-detail.tsx +189 -1
- package/src/components/providers/provider-list.tsx +158 -2
- package/src/components/providers/provider-sheet.tsx +81 -70
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/bottom-sheet.tsx +31 -15
- package/src/components/shared/command-palette.tsx +111 -24
- package/src/components/shared/confirm-dialog.tsx +45 -30
- package/src/components/shared/model-combobox.tsx +90 -8
- package/src/components/shared/settings/plugin-manager.tsx +20 -4
- package/src/components/shared/settings/section-capability-policy.tsx +105 -0
- package/src/components/shared/settings/section-heartbeat.tsx +88 -6
- package/src/components/shared/settings/section-orchestrator.tsx +6 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
- package/src/components/shared/settings/section-secrets.tsx +6 -6
- package/src/components/shared/settings/section-user-preferences.tsx +1 -1
- package/src/components/shared/settings/section-voice.tsx +5 -1
- package/src/components/shared/settings/section-web-search.tsx +10 -2
- package/src/components/shared/settings/settings-page.tsx +248 -47
- package/src/components/tasks/approvals-panel.tsx +211 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/usage/metrics-dashboard.tsx +74 -1
- package/src/components/wallets/wallet-approval-dialog.tsx +59 -54
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +7 -7
- package/src/lib/auth.ts +17 -0
- package/src/lib/chat-streaming-state.test.ts +108 -0
- package/src/lib/chat-streaming-state.ts +108 -0
- package/src/lib/heartbeat-defaults.ts +48 -0
- package/src/lib/memory-presentation.ts +59 -0
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/provider-model-discovery-client.ts +29 -0
- package/src/lib/providers/index.ts +12 -5
- package/src/lib/runtime-loop.ts +105 -3
- package/src/lib/safe-storage.ts +6 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/agent-runtime-config.test.ts +141 -0
- package/src/lib/server/agent-runtime-config.ts +277 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +264 -0
- package/src/lib/server/approvals.ts +483 -75
- package/src/lib/server/autonomy-runtime.test.ts +341 -0
- package/src/lib/server/browser-state.test.ts +118 -0
- package/src/lib/server/browser-state.ts +123 -0
- package/src/lib/server/build-llm.test.ts +44 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +219 -0
- package/src/lib/server/chat-execution.ts +402 -125
- package/src/lib/server/chatroom-health.test.ts +26 -0
- package/src/lib/server/chatroom-health.ts +2 -3
- package/src/lib/server/chatroom-helpers.test.ts +74 -2
- package/src/lib/server/chatroom-helpers.ts +144 -11
- package/src/lib/server/chatroom-session-persistence.test.ts +87 -0
- package/src/lib/server/connectors/discord.ts +175 -11
- package/src/lib/server/connectors/doctor.test.ts +80 -0
- package/src/lib/server/connectors/doctor.ts +116 -0
- package/src/lib/server/connectors/manager.ts +994 -130
- package/src/lib/server/connectors/policy.test.ts +222 -0
- package/src/lib/server/connectors/policy.ts +452 -0
- package/src/lib/server/connectors/slack.ts +189 -10
- package/src/lib/server/connectors/telegram.ts +65 -15
- package/src/lib/server/connectors/thread-context.test.ts +44 -0
- package/src/lib/server/connectors/thread-context.ts +72 -0
- package/src/lib/server/connectors/types.ts +41 -11
- package/src/lib/server/daemon-state.ts +62 -3
- package/src/lib/server/data-dir.ts +13 -0
- package/src/lib/server/delegation-jobs.test.ts +140 -0
- package/src/lib/server/delegation-jobs.ts +248 -0
- package/src/lib/server/document-utils.test.ts +47 -0
- package/src/lib/server/document-utils.ts +397 -0
- package/src/lib/server/eval/agent-regression.test.ts +47 -0
- package/src/lib/server/eval/agent-regression.ts +1742 -0
- package/src/lib/server/eval/runner.ts +11 -1
- package/src/lib/server/eval/store.ts +2 -1
- package/src/lib/server/heartbeat-service.ts +23 -43
- package/src/lib/server/heartbeat-source.test.ts +22 -0
- package/src/lib/server/heartbeat-source.ts +7 -0
- package/src/lib/server/identity-continuity.test.ts +77 -0
- package/src/lib/server/identity-continuity.ts +127 -0
- package/src/lib/server/mailbox-utils.ts +347 -0
- package/src/lib/server/main-agent-loop.ts +31 -964
- package/src/lib/server/memory-db.ts +4 -6
- package/src/lib/server/memory-tiers.ts +40 -0
- package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
- package/src/lib/server/openclaw-agent-resolver.ts +128 -0
- package/src/lib/server/openclaw-exec-config.ts +6 -5
- package/src/lib/server/openclaw-gateway.ts +123 -36
- package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
- package/src/lib/server/openclaw-skills-normalize.ts +136 -0
- package/src/lib/server/openclaw-sync.ts +3 -2
- package/src/lib/server/orchestrator-lg.ts +18 -8
- package/src/lib/server/orchestrator.ts +5 -4
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +215 -0
- package/src/lib/server/plugins.ts +832 -69
- package/src/lib/server/provider-health.ts +33 -3
- package/src/lib/server/provider-model-discovery.ts +481 -0
- package/src/lib/server/queue.ts +4 -21
- package/src/lib/server/runtime-settings.test.ts +119 -0
- package/src/lib/server/runtime-settings.ts +12 -92
- package/src/lib/server/schedule-normalization.ts +187 -0
- package/src/lib/server/scheduler.ts +2 -0
- package/src/lib/server/session-archive-memory.test.ts +85 -0
- package/src/lib/server/session-archive-memory.ts +230 -0
- package/src/lib/server/session-mailbox.ts +8 -18
- package/src/lib/server/session-reset-policy.test.ts +99 -0
- package/src/lib/server/session-reset-policy.ts +311 -0
- package/src/lib/server/session-run-manager.ts +33 -80
- package/src/lib/server/session-tools/autonomy-tools.test.ts +128 -0
- package/src/lib/server/session-tools/calendar.ts +2 -12
- package/src/lib/server/session-tools/connector.ts +109 -8
- package/src/lib/server/session-tools/context.ts +14 -2
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +96 -34
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +406 -20
- package/src/lib/server/session-tools/discovery-approvals.test.ts +170 -0
- package/src/lib/server/session-tools/discovery.ts +40 -12
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/email.ts +1 -3
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +98 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +243 -24
- package/src/lib/server/session-tools/http.ts +9 -3
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +1 -3
- package/src/lib/server/session-tools/index.ts +87 -2
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +137 -0
- package/src/lib/server/session-tools/memory.ts +35 -3
- package/src/lib/server/session-tools/monitor.ts +162 -12
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.test.ts +111 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +86 -20
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +142 -4
- package/src/lib/server/session-tools/plugin-creator.ts +95 -25
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +1 -3
- package/src/lib/server/session-tools/sandbox.ts +51 -92
- package/src/lib/server/session-tools/schedule.ts +20 -10
- package/src/lib/server/session-tools/session-info.ts +58 -4
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +54 -17
- package/src/lib/server/session-tools/shell.ts +2 -2
- package/src/lib/server/session-tools/subagent.ts +195 -27
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +13 -10
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +947 -108
- package/src/lib/server/storage.ts +255 -10
- package/src/lib/server/stream-agent-chat.test.ts +61 -0
- package/src/lib/server/stream-agent-chat.ts +185 -25
- package/src/lib/server/structured-extract.test.ts +72 -0
- package/src/lib/server/structured-extract.ts +373 -0
- package/src/lib/server/task-mention.test.ts +16 -2
- package/src/lib/server/task-mention.ts +61 -11
- package/src/lib/server/tool-aliases.ts +80 -12
- package/src/lib/server/tool-capability-policy.ts +7 -1
- package/src/lib/server/tool-retry.ts +2 -0
- package/src/lib/server/watch-jobs.test.ts +173 -0
- package/src/lib/server/watch-jobs.ts +532 -0
- package/src/lib/server/ws-hub.ts +5 -3
- package/src/lib/setup-defaults.ts +352 -11
- package/src/lib/tool-definitions.ts +3 -4
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +62 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +43 -7
- package/src/stores/use-chat-store.ts +31 -2
- package/src/stores/use-chatroom-store.ts +153 -26
- package/src/types/index.ts +470 -44
- package/src/app/api/chats/[id]/main-loop/route.ts +0 -94
- package/src/components/chat/new-chat-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -17
- package/src/lib/server/session-run-manager.test.ts +0 -26
|
@@ -4,6 +4,8 @@ import type { SettingsSectionProps } from './types'
|
|
|
4
4
|
|
|
5
5
|
export function WebSearchSection({ appSettings, patchSettings, inputClass }: SettingsSectionProps) {
|
|
6
6
|
const provider = appSettings.webSearchProvider || 'duckduckgo'
|
|
7
|
+
const hasTavilyKey = appSettings.tavilyApiKeyConfigured === true
|
|
8
|
+
const hasBraveKey = appSettings.braveApiKeyConfigured === true
|
|
7
9
|
|
|
8
10
|
return (
|
|
9
11
|
<div className="mb-10">
|
|
@@ -52,10 +54,13 @@ export function WebSearchSection({ appSettings, patchSettings, inputClass }: Set
|
|
|
52
54
|
type="password"
|
|
53
55
|
value={appSettings.tavilyApiKey || ''}
|
|
54
56
|
onChange={(e) => patchSettings({ tavilyApiKey: e.target.value || null })}
|
|
55
|
-
placeholder=
|
|
57
|
+
placeholder={hasTavilyKey ? 'Stored securely. Enter a new key to replace it.' : 'tvly-...'}
|
|
56
58
|
className={inputClass}
|
|
57
59
|
style={{ fontFamily: 'inherit' }}
|
|
58
60
|
/>
|
|
61
|
+
{hasTavilyKey && (
|
|
62
|
+
<p className="text-[11px] text-emerald-400/90 mt-1.5">Stored securely. Clear the field and save to remove it.</p>
|
|
63
|
+
)}
|
|
59
64
|
<p className="text-[11px] text-text-3/60 mt-2">Get your API key from <a href="https://tavily.com" target="_blank" rel="noopener noreferrer" className="text-accent-bright hover:underline">tavily.com</a></p>
|
|
60
65
|
</div>
|
|
61
66
|
)}
|
|
@@ -67,10 +72,13 @@ export function WebSearchSection({ appSettings, patchSettings, inputClass }: Set
|
|
|
67
72
|
type="password"
|
|
68
73
|
value={appSettings.braveApiKey || ''}
|
|
69
74
|
onChange={(e) => patchSettings({ braveApiKey: e.target.value || null })}
|
|
70
|
-
placeholder=
|
|
75
|
+
placeholder={hasBraveKey ? 'Stored securely. Enter a new key to replace it.' : 'BSA...'}
|
|
71
76
|
className={inputClass}
|
|
72
77
|
style={{ fontFamily: 'inherit' }}
|
|
73
78
|
/>
|
|
79
|
+
{hasBraveKey && (
|
|
80
|
+
<p className="text-[11px] text-emerald-400/90 mt-1.5">Stored securely. Clear the field and save to remove it.</p>
|
|
81
|
+
)}
|
|
74
82
|
<p className="text-[11px] text-text-3/60 mt-2">Get your API key from <a href="https://brave.com/search/api/" target="_blank" rel="noopener noreferrer" className="text-accent-bright hover:underline">brave.com/search/api</a></p>
|
|
75
83
|
</div>
|
|
76
84
|
)}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState, useRef, useCallback } from 'react'
|
|
3
|
+
import { useEffect, useState, useRef, useCallback, useMemo } from 'react'
|
|
4
|
+
import type { ReactNode } from 'react'
|
|
4
5
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
6
|
import { inputClass } from './utils'
|
|
6
7
|
import { UserPreferencesSection } from './section-user-preferences'
|
|
@@ -20,15 +21,29 @@ import { ProvidersSection } from './section-providers'
|
|
|
20
21
|
interface Tab {
|
|
21
22
|
id: string
|
|
22
23
|
label: string
|
|
23
|
-
icon:
|
|
24
|
+
icon: ReactNode
|
|
24
25
|
keywords: string[]
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
interface SettingsSectionDef {
|
|
29
|
+
id: string
|
|
30
|
+
tabId: string
|
|
31
|
+
title: string
|
|
32
|
+
description: string
|
|
33
|
+
keywords: string[]
|
|
34
|
+
render: () => ReactNode
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface SettingsFocusDetail {
|
|
38
|
+
tabId?: string
|
|
39
|
+
sectionId?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
27
42
|
const TABS: Tab[] = [
|
|
28
43
|
{
|
|
29
44
|
id: 'general',
|
|
30
45
|
label: 'General',
|
|
31
|
-
keywords: ['preferences', 'user', 'language', 'default', 'capability', 'policy', 'permissions', 'tools', 'storage', 'uploads', 'disk', 'files', 'cleanup'],
|
|
46
|
+
keywords: ['preferences', 'user', 'language', 'default', 'default agent', 'shortcut', 'capability', 'policy', 'permissions', 'tools', 'storage', 'uploads', 'disk', 'files', 'cleanup'],
|
|
32
47
|
icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" /></svg>,
|
|
33
48
|
},
|
|
34
49
|
{
|
|
@@ -39,8 +54,8 @@ const TABS: Tab[] = [
|
|
|
39
54
|
},
|
|
40
55
|
{
|
|
41
56
|
id: 'agents',
|
|
42
|
-
label: 'Agents &
|
|
43
|
-
keywords: ['orchestrator', 'runtime', 'loop', 'heartbeat', 'delegation', 'agent', 'swarm', 'turns'],
|
|
57
|
+
label: 'Agents & Automation',
|
|
58
|
+
keywords: ['orchestrator', 'runtime', 'loop', 'automation', 'heartbeat', 'delegation', 'agent', 'swarm', 'turns'],
|
|
44
59
|
icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" /><circle cx="9" cy="7" r="4" /><path d="M23 21v-2a4 4 0 0 0-3-3.87" /><path d="M16 3.13a4 4 0 0 1 0 7.75" /></svg>,
|
|
45
60
|
},
|
|
46
61
|
{
|
|
@@ -74,6 +89,8 @@ export function SettingsPage() {
|
|
|
74
89
|
return tab && validTabIds.includes(tab) ? tab : 'general'
|
|
75
90
|
})
|
|
76
91
|
const contentRef = useRef<HTMLDivElement>(null)
|
|
92
|
+
const sectionRefs = useRef<Record<string, HTMLDivElement | null>>({})
|
|
93
|
+
const [pendingSectionId, setPendingSectionId] = useState<string | null>(null)
|
|
77
94
|
|
|
78
95
|
const setActiveTab = useCallback((tab: string) => {
|
|
79
96
|
setActiveTabRaw(tab)
|
|
@@ -100,13 +117,148 @@ export function SettingsPage() {
|
|
|
100
117
|
const [searchQuery, setSearchQuery] = useState('')
|
|
101
118
|
const credList = Object.values(credentials)
|
|
102
119
|
const patchSettings = updateSettings
|
|
103
|
-
const
|
|
120
|
+
const sections = useMemo<SettingsSectionDef[]>(() => {
|
|
121
|
+
const sectionProps = { appSettings, patchSettings, inputClass }
|
|
122
|
+
return [
|
|
123
|
+
{
|
|
124
|
+
id: 'user-preferences',
|
|
125
|
+
tabId: 'general',
|
|
126
|
+
title: 'Profile & Default Chat',
|
|
127
|
+
description: 'User identity, language, and which agent your sidebar shortcut opens.',
|
|
128
|
+
keywords: ['profile', 'default chat', 'default agent', 'shortcut', 'user', 'language', 'main chat'],
|
|
129
|
+
render: () => <UserPreferencesSection {...sectionProps} />,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 'capability-policy',
|
|
133
|
+
tabId: 'general',
|
|
134
|
+
title: 'Capabilities & Permissions',
|
|
135
|
+
description: 'Global controls for tool use, permissions, and execution policy.',
|
|
136
|
+
keywords: ['tools', 'permissions', 'capability', 'policy', 'security', 'approvals'],
|
|
137
|
+
render: () => <CapabilityPolicySection {...sectionProps} />,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'storage',
|
|
141
|
+
tabId: 'general',
|
|
142
|
+
title: 'Storage & Uploads',
|
|
143
|
+
description: 'Manage upload retention, cleanup, and file storage behavior.',
|
|
144
|
+
keywords: ['storage', 'uploads', 'disk', 'cleanup', 'files'],
|
|
145
|
+
render: () => <StorageSection {...sectionProps} />,
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: 'theme',
|
|
149
|
+
tabId: 'appearance',
|
|
150
|
+
title: 'Theme',
|
|
151
|
+
description: 'Adjust theme hue and interface styling.',
|
|
152
|
+
keywords: ['theme', 'appearance', 'color', 'hue'],
|
|
153
|
+
render: () => <ThemeSection {...sectionProps} />,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
id: 'coordination-engine',
|
|
157
|
+
tabId: 'agents',
|
|
158
|
+
title: 'Coordination Engine',
|
|
159
|
+
description: 'Choose the model settings used for delegation-heavy agent work.',
|
|
160
|
+
keywords: ['coordination', 'delegation', 'engine', 'orchestrator'],
|
|
161
|
+
render: () => <OrchestratorSection {...sectionProps} />,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'runtime-loop',
|
|
165
|
+
tabId: 'agents',
|
|
166
|
+
title: 'Automation Limits',
|
|
167
|
+
description: 'Control how far agents can run, recurse, and delegate on their own.',
|
|
168
|
+
keywords: ['automation', 'loop', 'runtime', 'turns', 'autonomy', 'heartbeat'],
|
|
169
|
+
render: () => <RuntimeLoopSection {...sectionProps} />,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
id: 'heartbeat',
|
|
173
|
+
tabId: 'agents',
|
|
174
|
+
title: 'Heartbeat',
|
|
175
|
+
description: 'Configure automatic follow-up checks for active agent chats.',
|
|
176
|
+
keywords: ['heartbeat', 'follow up', 'interval', 'ongoing'],
|
|
177
|
+
render: () => <HeartbeatSection {...sectionProps} />,
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
id: 'embedding',
|
|
181
|
+
tabId: 'memory',
|
|
182
|
+
title: 'Embeddings',
|
|
183
|
+
description: 'Configure providers for embeddings and vector-backed features.',
|
|
184
|
+
keywords: ['embedding', 'vector', 'provider', 'semantic'],
|
|
185
|
+
render: () => <EmbeddingSection {...sectionProps} credList={credList} />,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
id: 'memory',
|
|
189
|
+
tabId: 'memory',
|
|
190
|
+
title: 'Memory Governance',
|
|
191
|
+
description: 'Tune how memory is stored, consolidated, and retrieved.',
|
|
192
|
+
keywords: ['memory', 'consolidation', 'retention', 'governance'],
|
|
193
|
+
render: () => <MemorySection {...sectionProps} />,
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: 'voice',
|
|
197
|
+
tabId: 'memory',
|
|
198
|
+
title: 'Voice',
|
|
199
|
+
description: 'Control speech output and voice provider settings.',
|
|
200
|
+
keywords: ['voice', 'speech', 'tts', 'audio'],
|
|
201
|
+
render: () => <VoiceSection {...sectionProps} />,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 'web-search',
|
|
205
|
+
tabId: 'memory',
|
|
206
|
+
title: 'Web Search',
|
|
207
|
+
description: 'Set defaults for search providers and browsing behavior.',
|
|
208
|
+
keywords: ['web search', 'browse', 'internet', 'search'],
|
|
209
|
+
render: () => <WebSearchSection {...sectionProps} />,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
id: 'providers',
|
|
213
|
+
tabId: 'integrations',
|
|
214
|
+
title: 'Providers',
|
|
215
|
+
description: 'Manage model providers, endpoints, and credentials.',
|
|
216
|
+
keywords: ['providers', 'endpoints', 'openai', 'anthropic', 'ollama', 'models'],
|
|
217
|
+
render: () => <ProvidersSection {...sectionProps} />,
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
id: 'secrets',
|
|
221
|
+
tabId: 'integrations',
|
|
222
|
+
title: 'Secrets',
|
|
223
|
+
description: 'Store encrypted credentials for agents and integrations.',
|
|
224
|
+
keywords: ['secrets', 'credentials', 'api keys', 'tokens'],
|
|
225
|
+
render: () => <SecretsSection {...sectionProps} />,
|
|
226
|
+
},
|
|
227
|
+
]
|
|
228
|
+
}, [appSettings, credList, patchSettings])
|
|
229
|
+
const sectionsByTab = useMemo(() => {
|
|
230
|
+
const map = new Map<string, SettingsSectionDef[]>()
|
|
231
|
+
for (const section of sections) {
|
|
232
|
+
const group = map.get(section.tabId) || []
|
|
233
|
+
group.push(section)
|
|
234
|
+
map.set(section.tabId, group)
|
|
235
|
+
}
|
|
236
|
+
return map
|
|
237
|
+
}, [sections])
|
|
238
|
+
const setSectionRef = useCallback((id: string, node: HTMLDivElement | null) => {
|
|
239
|
+
sectionRefs.current[id] = node
|
|
240
|
+
}, [])
|
|
241
|
+
const focusSection = useCallback((sectionId: string, tabId?: string) => {
|
|
242
|
+
if (tabId && tabId !== activeTab) {
|
|
243
|
+
setPendingSectionId(sectionId)
|
|
244
|
+
setActiveTab(tabId)
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
sectionRefs.current[sectionId]?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
248
|
+
}, [activeTab, setActiveTab])
|
|
249
|
+
|
|
250
|
+
const matchingSections = useMemo(() => {
|
|
251
|
+
if (!searchQuery.trim()) return sections
|
|
252
|
+
const q = searchQuery.toLowerCase()
|
|
253
|
+
return sections.filter((section) =>
|
|
254
|
+
section.title.toLowerCase().includes(q)
|
|
255
|
+
|| section.description.toLowerCase().includes(q)
|
|
256
|
+
|| section.keywords.some((keyword) => keyword.toLowerCase().includes(q)),
|
|
257
|
+
)
|
|
258
|
+
}, [searchQuery, sections])
|
|
104
259
|
|
|
105
260
|
const matchingTabIds = searchQuery
|
|
106
|
-
? new Set(
|
|
107
|
-
const q = searchQuery.toLowerCase()
|
|
108
|
-
return t.label.toLowerCase().includes(q) || t.keywords.some((k) => k.includes(q))
|
|
109
|
-
}).map((t) => t.id))
|
|
261
|
+
? new Set(matchingSections.map((section) => section.tabId))
|
|
110
262
|
: null
|
|
111
263
|
|
|
112
264
|
// Auto-switch to first matching tab when searching
|
|
@@ -118,6 +270,31 @@ export function SettingsPage() {
|
|
|
118
270
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
119
271
|
}, [searchQuery])
|
|
120
272
|
|
|
273
|
+
useEffect(() => {
|
|
274
|
+
if (!pendingSectionId) return
|
|
275
|
+
const frame = window.requestAnimationFrame(() => {
|
|
276
|
+
sectionRefs.current[pendingSectionId]?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
277
|
+
setPendingSectionId(null)
|
|
278
|
+
})
|
|
279
|
+
return () => window.cancelAnimationFrame(frame)
|
|
280
|
+
}, [activeTab, pendingSectionId])
|
|
281
|
+
|
|
282
|
+
useEffect(() => {
|
|
283
|
+
const handleFocus = (event: Event) => {
|
|
284
|
+
const detail = (event as CustomEvent<SettingsFocusDetail>).detail
|
|
285
|
+
if (!detail) return
|
|
286
|
+
if (detail.sectionId) {
|
|
287
|
+
focusSection(detail.sectionId, detail.tabId)
|
|
288
|
+
return
|
|
289
|
+
}
|
|
290
|
+
if (detail.tabId) setActiveTab(detail.tabId)
|
|
291
|
+
}
|
|
292
|
+
window.addEventListener('swarmclaw:settings-focus', handleFocus as EventListener)
|
|
293
|
+
return () => window.removeEventListener('swarmclaw:settings-focus', handleFocus as EventListener)
|
|
294
|
+
}, [focusSection, setActiveTab])
|
|
295
|
+
|
|
296
|
+
const visibleSections = sectionsByTab.get(activeTab) || []
|
|
297
|
+
|
|
121
298
|
return (
|
|
122
299
|
<div className="flex-1 flex h-full min-w-0">
|
|
123
300
|
{/* Tab sidebar */}
|
|
@@ -133,7 +310,7 @@ export function SettingsPage() {
|
|
|
133
310
|
type="text"
|
|
134
311
|
value={searchQuery}
|
|
135
312
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
136
|
-
placeholder="Search settings..."
|
|
313
|
+
placeholder="Search settings or jump to a section..."
|
|
137
314
|
className="w-full pl-8 pr-2 py-1.5 text-[12px] bg-white/[0.04] rounded-[8px] border border-white/[0.06] text-text placeholder:text-text-3/40 outline-none focus:border-white/[0.12] transition-colors"
|
|
138
315
|
style={{ fontFamily: 'inherit' }}
|
|
139
316
|
/>
|
|
@@ -168,49 +345,73 @@ export function SettingsPage() {
|
|
|
168
345
|
{TABS.find((t) => t.id === activeTab)?.label}
|
|
169
346
|
</h3>
|
|
170
347
|
<p className="text-[13px] text-text-3 mt-1">
|
|
171
|
-
{activeTab === 'general' && 'User preferences and global
|
|
348
|
+
{activeTab === 'general' && 'User preferences, default-chat behavior, and global controls.'}
|
|
172
349
|
{activeTab === 'appearance' && 'Customize the look and feel of the interface.'}
|
|
173
|
-
{activeTab === 'agents' && '
|
|
350
|
+
{activeTab === 'agents' && 'Agent coordination, autonomy, delegation, and heartbeat.'}
|
|
174
351
|
{activeTab === 'memory' && 'Embedding, memory governance, voice and web search.'}
|
|
175
|
-
{activeTab === 'integrations' && 'Providers,
|
|
352
|
+
{activeTab === 'integrations' && 'Providers, endpoints, and encrypted credentials.'}
|
|
176
353
|
</p>
|
|
177
354
|
</div>
|
|
178
355
|
|
|
179
|
-
{
|
|
180
|
-
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
356
|
+
{searchQuery && (
|
|
357
|
+
<div className="mb-8 rounded-[16px] border border-white/[0.06] bg-white/[0.02] p-4">
|
|
358
|
+
<div className="flex items-center justify-between gap-3 mb-3">
|
|
359
|
+
<div>
|
|
360
|
+
<p className="text-[12px] font-600 text-text-2">
|
|
361
|
+
{matchingSections.length > 0 ? `${matchingSections.length} matching section${matchingSections.length === 1 ? '' : 's'}` : 'No direct section matches'}
|
|
362
|
+
</p>
|
|
363
|
+
<p className="text-[11px] text-text-3/60">
|
|
364
|
+
Search now lands on individual settings sections instead of only tab names.
|
|
365
|
+
</p>
|
|
366
|
+
</div>
|
|
367
|
+
{searchQuery && (
|
|
368
|
+
<button
|
|
369
|
+
onClick={() => setSearchQuery('')}
|
|
370
|
+
className="px-2.5 py-1.5 rounded-[8px] bg-white/[0.04] text-[11px] text-text-3 hover:text-text hover:bg-white/[0.08] transition-colors border-none cursor-pointer"
|
|
371
|
+
style={{ fontFamily: 'inherit' }}
|
|
372
|
+
>
|
|
373
|
+
Clear
|
|
374
|
+
</button>
|
|
375
|
+
)}
|
|
376
|
+
</div>
|
|
377
|
+
{matchingSections.length > 0 && (
|
|
378
|
+
<div className="flex flex-wrap gap-2">
|
|
379
|
+
{matchingSections.slice(0, 8).map((section) => (
|
|
380
|
+
<button
|
|
381
|
+
key={section.id}
|
|
382
|
+
onClick={() => focusSection(section.id, section.tabId)}
|
|
383
|
+
className="px-3 py-2 rounded-[10px] border border-white/[0.06] bg-transparent text-left hover:bg-white/[0.04] transition-colors cursor-pointer"
|
|
384
|
+
style={{ fontFamily: 'inherit' }}
|
|
385
|
+
>
|
|
386
|
+
<div className="text-[12px] font-600 text-text">{section.title}</div>
|
|
387
|
+
<div className="text-[10px] text-text-3/60">{TABS.find((tab) => tab.id === section.tabId)?.label}</div>
|
|
388
|
+
</button>
|
|
389
|
+
))}
|
|
390
|
+
</div>
|
|
391
|
+
)}
|
|
392
|
+
</div>
|
|
189
393
|
)}
|
|
190
394
|
|
|
191
|
-
{
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
<SecretsSection {...sectionProps} />
|
|
212
|
-
</>
|
|
213
|
-
)}
|
|
395
|
+
{visibleSections.map((section) => (
|
|
396
|
+
<div
|
|
397
|
+
key={section.id}
|
|
398
|
+
ref={(node) => setSectionRef(section.id, node)}
|
|
399
|
+
className="mb-10 scroll-mt-6 last:mb-0"
|
|
400
|
+
>
|
|
401
|
+
<div className="mb-4">
|
|
402
|
+
<div className="text-[11px] uppercase tracking-[0.08em] text-text-3/45 mb-1">
|
|
403
|
+
{TABS.find((tab) => tab.id === section.tabId)?.label}
|
|
404
|
+
</div>
|
|
405
|
+
<h4 className="font-display text-[18px] font-700 tracking-[-0.02em] text-text">
|
|
406
|
+
{section.title}
|
|
407
|
+
</h4>
|
|
408
|
+
<p className="text-[12px] text-text-3 mt-1">
|
|
409
|
+
{section.description}
|
|
410
|
+
</p>
|
|
411
|
+
</div>
|
|
412
|
+
{section.render()}
|
|
413
|
+
</div>
|
|
414
|
+
))}
|
|
214
415
|
</div>
|
|
215
416
|
</div>
|
|
216
417
|
</div>
|