@swarmclawai/swarmclaw 0.7.1 → 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.
- package/README.md +155 -150
- package/package.json +1 -1
- package/src/app/api/agents/[id]/route.ts +26 -0
- package/src/app/api/agents/[id]/thread/route.ts +37 -9
- package/src/app/api/agents/route.ts +13 -2
- package/src/app/api/auth/route.ts +76 -7
- package/src/app/api/chatrooms/[id]/chat/route.ts +7 -2
- package/src/app/api/{sessions → chats}/[id]/browser/route.ts +5 -1
- package/src/app/api/{sessions → chats}/[id]/chat/route.ts +7 -3
- package/src/app/api/{sessions → chats}/[id]/checkpoints/route.ts +1 -1
- package/src/app/api/chats/[id]/main-loop/route.ts +13 -0
- package/src/app/api/{sessions → chats}/[id]/messages/route.ts +19 -13
- package/src/app/api/{sessions → chats}/[id]/restore/route.ts +1 -1
- package/src/app/api/{sessions → chats}/[id]/route.ts +22 -52
- package/src/app/api/{sessions → chats}/[id]/stop/route.ts +6 -1
- package/src/app/api/{sessions → chats}/route.ts +21 -7
- 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/files/open/route.ts +16 -14
- 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/skills/route.ts +11 -3
- 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 +6 -26
- package/src/app/api/plugins/settings/route.ts +40 -0
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/settings/route.ts +49 -7
- package/src/app/api/tasks/[id]/route.ts +15 -6
- package/src/app/api/tasks/bulk/route.ts +2 -2
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/usage/route.ts +30 -0
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +9 -2
- package/src/cli/index.js +39 -33
- package/src/cli/index.ts +43 -49
- package/src/cli/spec.js +29 -27
- package/src/components/agents/agent-card.tsx +16 -13
- package/src/components/agents/agent-chat-list.tsx +104 -4
- package/src/components/agents/agent-list.tsx +54 -22
- package/src/components/agents/agent-sheet.tsx +209 -18
- package/src/components/agents/cron-job-form.tsx +3 -3
- package/src/components/agents/inspector-panel.tsx +110 -50
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +5 -38
- package/src/components/chat/chat-area.tsx +39 -27
- package/src/components/{sessions/session-card.tsx → chat/chat-card.tsx} +7 -23
- package/src/components/chat/chat-header.tsx +299 -314
- package/src/components/{sessions/session-list.tsx → chat/chat-list.tsx} +11 -14
- package/src/components/chat/chat-tool-toggles.tsx +26 -17
- package/src/components/chat/checkpoint-timeline.tsx +4 -4
- package/src/components/chat/message-bubble.tsx +4 -1
- package/src/components/chat/message-list.tsx +5 -3
- package/src/components/chat/session-debug-panel.tsx +1 -1
- package/src/components/chat/tool-request-banner.tsx +3 -3
- package/src/components/chatrooms/agent-hover-card.tsx +3 -3
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +2 -2
- package/src/components/chatrooms/chatroom-view.tsx +347 -205
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +218 -1
- package/src/components/home/home-view.tsx +129 -5
- package/src/components/layout/app-layout.tsx +392 -182
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/plugins/plugin-list.tsx +487 -254
- package/src/components/plugins/plugin-sheet.tsx +236 -13
- package/src/components/projects/project-detail.tsx +183 -0
- package/src/components/settings/gateway-connection-panel.tsx +1 -1
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/command-palette.tsx +111 -25
- 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 +78 -1
- package/src/components/shared/settings/section-orchestrator.tsx +3 -3
- package/src/components/shared/settings/section-providers.tsx +1 -1
- 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 +244 -56
- package/src/components/tasks/approvals-panel.tsx +205 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/usage/metrics-dashboard.tsx +147 -1
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +8 -8
- 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/chat.ts +1 -1
- package/src/lib/{sessions.ts → chats.ts} +28 -18
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/providers/claude-cli.ts +1 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +205 -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 +36 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/capability-router.ts +10 -8
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +134 -0
- package/src/lib/server/chat-execution.ts +285 -165
- 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 +67 -2
- package/src/lib/server/chatroom-helpers.ts +48 -8
- 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 +948 -112
- 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 +188 -9
- 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/cost.ts +34 -1
- package/src/lib/server/daemon-state.ts +61 -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/heartbeat-service.ts +14 -40
- 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 +28 -1103
- 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 +5 -6
- 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 +20 -9
- package/src/lib/server/orchestrator.ts +7 -7
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +207 -0
- package/src/lib/server/plugins.ts +927 -66
- package/src/lib/server/provider-health.ts +38 -6
- package/src/lib/server/queue.ts +13 -28
- 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 -82
- package/src/lib/server/session-tools/autonomy-tools.test.ts +105 -0
- package/src/lib/server/session-tools/calendar.ts +366 -0
- package/src/lib/server/session-tools/canvas.ts +1 -1
- package/src/lib/server/session-tools/chatroom.ts +4 -2
- package/src/lib/server/session-tools/connector.ts +114 -10
- package/src/lib/server/session-tools/context.ts +21 -5
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +74 -28
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +497 -24
- package/src/lib/server/session-tools/discovery.ts +24 -6
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/edit_file.ts +4 -2
- package/src/lib/server/session-tools/email.ts +320 -0
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +93 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +241 -25
- package/src/lib/server/session-tools/git.ts +1 -1
- package/src/lib/server/session-tools/http.ts +1 -1
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +380 -0
- package/src/lib/server/session-tools/index.ts +130 -50
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/memory.ts +172 -3
- package/src/lib/server/session-tools/monitor.ts +151 -8
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.ts +1 -1
- package/src/lib/server/session-tools/openclaw-workspace.ts +1 -1
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +148 -7
- package/src/lib/server/session-tools/plugin-creator.ts +89 -26
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +301 -0
- package/src/lib/server/session-tools/sample-ui.ts +1 -1
- package/src/lib/server/session-tools/sandbox.ts +4 -2
- package/src/lib/server/session-tools/schedule.ts +24 -12
- package/src/lib/server/session-tools/session-info.ts +43 -7
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +31 -17
- package/src/lib/server/session-tools/shell.ts +5 -2
- package/src/lib/server/session-tools/subagent.ts +194 -28
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +42 -12
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +926 -91
- package/src/lib/server/storage.ts +255 -16
- package/src/lib/server/stream-agent-chat.ts +116 -268
- 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 -10
- package/src/lib/server/tool-aliases.ts +66 -18
- package/src/lib/server/tool-capability-policy.test.ts +9 -9
- package/src/lib/server/tool-capability-policy.ts +38 -27
- 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/tool-definitions.ts +4 -0
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +10 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +5 -11
- package/src/stores/use-chat-store.ts +38 -9
- package/src/types/index.ts +352 -47
- package/src/app/api/sessions/[id]/main-loop/route.ts +0 -94
- package/src/components/sessions/new-session-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -24
- package/src/lib/server/session-run-manager.test.ts +0 -23
- /package/src/app/api/{sessions → chats}/[id]/clear/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/deploy/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/devserver/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/edit-resend/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/fork/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/mailbox/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/retry/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/heartbeat/route.ts +0 -0
|
@@ -4,6 +4,7 @@ import type { SettingsSectionProps } from './types'
|
|
|
4
4
|
|
|
5
5
|
export function VoiceSection({ appSettings, patchSettings, inputClass }: SettingsSectionProps) {
|
|
6
6
|
const enabled = appSettings.elevenLabsEnabled ?? false
|
|
7
|
+
const hasApiKey = appSettings.elevenLabsApiKeyConfigured === true
|
|
7
8
|
|
|
8
9
|
return (
|
|
9
10
|
<div className="mb-10">
|
|
@@ -37,10 +38,13 @@ export function VoiceSection({ appSettings, patchSettings, inputClass }: Setting
|
|
|
37
38
|
type="password"
|
|
38
39
|
value={appSettings.elevenLabsApiKey || ''}
|
|
39
40
|
onChange={(e) => patchSettings({ elevenLabsApiKey: e.target.value || null })}
|
|
40
|
-
placeholder=
|
|
41
|
+
placeholder={hasApiKey ? 'Stored securely. Enter a new key to replace it.' : 'sk_...'}
|
|
41
42
|
className={inputClass}
|
|
42
43
|
style={{ fontFamily: 'inherit' }}
|
|
43
44
|
/>
|
|
45
|
+
{hasApiKey && (
|
|
46
|
+
<p className="text-[11px] text-emerald-400/90 mt-1.5">Stored securely. Clear the field and save to remove it.</p>
|
|
47
|
+
)}
|
|
44
48
|
</div>
|
|
45
49
|
<div>
|
|
46
50
|
<label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-2">Default Voice ID</label>
|
|
@@ -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'
|
|
@@ -16,20 +17,33 @@ import { EmbeddingSection } from './section-embedding'
|
|
|
16
17
|
import { MemorySection } from './section-memory'
|
|
17
18
|
import { SecretsSection } from './section-secrets'
|
|
18
19
|
import { ProvidersSection } from './section-providers'
|
|
19
|
-
import { PluginManager } from './plugin-manager'
|
|
20
20
|
|
|
21
21
|
interface Tab {
|
|
22
22
|
id: string
|
|
23
23
|
label: string
|
|
24
|
-
icon:
|
|
24
|
+
icon: ReactNode
|
|
25
25
|
keywords: string[]
|
|
26
26
|
}
|
|
27
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
|
+
|
|
28
42
|
const TABS: Tab[] = [
|
|
29
43
|
{
|
|
30
44
|
id: 'general',
|
|
31
45
|
label: 'General',
|
|
32
|
-
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'],
|
|
33
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>,
|
|
34
48
|
},
|
|
35
49
|
{
|
|
@@ -40,8 +54,8 @@ const TABS: Tab[] = [
|
|
|
40
54
|
},
|
|
41
55
|
{
|
|
42
56
|
id: 'agents',
|
|
43
|
-
label: 'Agents &
|
|
44
|
-
keywords: ['orchestrator', 'runtime', 'loop', 'heartbeat', 'delegation', 'agent', 'swarm', 'turns'],
|
|
57
|
+
label: 'Agents & Automation',
|
|
58
|
+
keywords: ['orchestrator', 'runtime', 'loop', 'automation', 'heartbeat', 'delegation', 'agent', 'swarm', 'turns'],
|
|
45
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>,
|
|
46
60
|
},
|
|
47
61
|
{
|
|
@@ -53,7 +67,7 @@ const TABS: Tab[] = [
|
|
|
53
67
|
{
|
|
54
68
|
id: 'integrations',
|
|
55
69
|
label: 'Integrations',
|
|
56
|
-
keywords: ['provider', 'secret', '
|
|
70
|
+
keywords: ['provider', 'secret', 'api', 'key', 'openai', 'anthropic', 'ollama', 'credential'],
|
|
57
71
|
icon: <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><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" /></svg>,
|
|
58
72
|
},
|
|
59
73
|
]
|
|
@@ -75,6 +89,8 @@ export function SettingsPage() {
|
|
|
75
89
|
return tab && validTabIds.includes(tab) ? tab : 'general'
|
|
76
90
|
})
|
|
77
91
|
const contentRef = useRef<HTMLDivElement>(null)
|
|
92
|
+
const sectionRefs = useRef<Record<string, HTMLDivElement | null>>({})
|
|
93
|
+
const [pendingSectionId, setPendingSectionId] = useState<string | null>(null)
|
|
78
94
|
|
|
79
95
|
const setActiveTab = useCallback((tab: string) => {
|
|
80
96
|
setActiveTabRaw(tab)
|
|
@@ -102,12 +118,145 @@ export function SettingsPage() {
|
|
|
102
118
|
const credList = Object.values(credentials)
|
|
103
119
|
const patchSettings = updateSettings
|
|
104
120
|
const sectionProps = { appSettings, patchSettings, inputClass }
|
|
121
|
+
const sections = useMemo<SettingsSectionDef[]>(() => [
|
|
122
|
+
{
|
|
123
|
+
id: 'user-preferences',
|
|
124
|
+
tabId: 'general',
|
|
125
|
+
title: 'Profile & Default Chat',
|
|
126
|
+
description: 'User identity, language, and which agent your sidebar shortcut opens.',
|
|
127
|
+
keywords: ['profile', 'default chat', 'default agent', 'shortcut', 'user', 'language', 'main chat'],
|
|
128
|
+
render: () => <UserPreferencesSection {...sectionProps} />,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: 'capability-policy',
|
|
132
|
+
tabId: 'general',
|
|
133
|
+
title: 'Capabilities & Permissions',
|
|
134
|
+
description: 'Global controls for tool use, permissions, and execution policy.',
|
|
135
|
+
keywords: ['tools', 'permissions', 'capability', 'policy', 'security', 'approvals'],
|
|
136
|
+
render: () => <CapabilityPolicySection {...sectionProps} />,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'storage',
|
|
140
|
+
tabId: 'general',
|
|
141
|
+
title: 'Storage & Uploads',
|
|
142
|
+
description: 'Manage upload retention, cleanup, and file storage behavior.',
|
|
143
|
+
keywords: ['storage', 'uploads', 'disk', 'cleanup', 'files'],
|
|
144
|
+
render: () => <StorageSection {...sectionProps} />,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'theme',
|
|
148
|
+
tabId: 'appearance',
|
|
149
|
+
title: 'Theme',
|
|
150
|
+
description: 'Adjust theme hue and interface styling.',
|
|
151
|
+
keywords: ['theme', 'appearance', 'color', 'hue'],
|
|
152
|
+
render: () => <ThemeSection {...sectionProps} />,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'coordination-engine',
|
|
156
|
+
tabId: 'agents',
|
|
157
|
+
title: 'Coordination Engine',
|
|
158
|
+
description: 'Choose the model settings used for delegation-heavy agent work.',
|
|
159
|
+
keywords: ['coordination', 'delegation', 'engine', 'orchestrator'],
|
|
160
|
+
render: () => <OrchestratorSection {...sectionProps} />,
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: 'runtime-loop',
|
|
164
|
+
tabId: 'agents',
|
|
165
|
+
title: 'Automation Limits',
|
|
166
|
+
description: 'Control how far agents can run, recurse, and delegate on their own.',
|
|
167
|
+
keywords: ['automation', 'loop', 'runtime', 'turns', 'autonomy', 'heartbeat'],
|
|
168
|
+
render: () => <RuntimeLoopSection {...sectionProps} />,
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
id: 'heartbeat',
|
|
172
|
+
tabId: 'agents',
|
|
173
|
+
title: 'Heartbeat',
|
|
174
|
+
description: 'Configure automatic follow-up checks for active agent chats.',
|
|
175
|
+
keywords: ['heartbeat', 'follow up', 'interval', 'ongoing'],
|
|
176
|
+
render: () => <HeartbeatSection {...sectionProps} />,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: 'embedding',
|
|
180
|
+
tabId: 'memory',
|
|
181
|
+
title: 'Embeddings',
|
|
182
|
+
description: 'Configure providers for embeddings and vector-backed features.',
|
|
183
|
+
keywords: ['embedding', 'vector', 'provider', 'semantic'],
|
|
184
|
+
render: () => <EmbeddingSection {...sectionProps} credList={credList} />,
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 'memory',
|
|
188
|
+
tabId: 'memory',
|
|
189
|
+
title: 'Memory Governance',
|
|
190
|
+
description: 'Tune how memory is stored, consolidated, and retrieved.',
|
|
191
|
+
keywords: ['memory', 'consolidation', 'retention', 'governance'],
|
|
192
|
+
render: () => <MemorySection {...sectionProps} />,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: 'voice',
|
|
196
|
+
tabId: 'memory',
|
|
197
|
+
title: 'Voice',
|
|
198
|
+
description: 'Control speech output and voice provider settings.',
|
|
199
|
+
keywords: ['voice', 'speech', 'tts', 'audio'],
|
|
200
|
+
render: () => <VoiceSection {...sectionProps} />,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: 'web-search',
|
|
204
|
+
tabId: 'memory',
|
|
205
|
+
title: 'Web Search',
|
|
206
|
+
description: 'Set defaults for search providers and browsing behavior.',
|
|
207
|
+
keywords: ['web search', 'browse', 'internet', 'search'],
|
|
208
|
+
render: () => <WebSearchSection {...sectionProps} />,
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: 'providers',
|
|
212
|
+
tabId: 'integrations',
|
|
213
|
+
title: 'Providers',
|
|
214
|
+
description: 'Manage model providers, endpoints, and credentials.',
|
|
215
|
+
keywords: ['providers', 'endpoints', 'openai', 'anthropic', 'ollama', 'models'],
|
|
216
|
+
render: () => <ProvidersSection {...sectionProps} />,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: 'secrets',
|
|
220
|
+
tabId: 'integrations',
|
|
221
|
+
title: 'Secrets',
|
|
222
|
+
description: 'Store encrypted credentials for agents and integrations.',
|
|
223
|
+
keywords: ['secrets', 'credentials', 'api keys', 'tokens'],
|
|
224
|
+
render: () => <SecretsSection {...sectionProps} />,
|
|
225
|
+
},
|
|
226
|
+
], [credList, sectionProps])
|
|
227
|
+
const sectionsByTab = useMemo(() => {
|
|
228
|
+
const map = new Map<string, SettingsSectionDef[]>()
|
|
229
|
+
for (const section of sections) {
|
|
230
|
+
const group = map.get(section.tabId) || []
|
|
231
|
+
group.push(section)
|
|
232
|
+
map.set(section.tabId, group)
|
|
233
|
+
}
|
|
234
|
+
return map
|
|
235
|
+
}, [sections])
|
|
236
|
+
const setSectionRef = useCallback((id: string, node: HTMLDivElement | null) => {
|
|
237
|
+
sectionRefs.current[id] = node
|
|
238
|
+
}, [])
|
|
239
|
+
const focusSection = useCallback((sectionId: string, tabId?: string) => {
|
|
240
|
+
if (tabId && tabId !== activeTab) {
|
|
241
|
+
setPendingSectionId(sectionId)
|
|
242
|
+
setActiveTab(tabId)
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
sectionRefs.current[sectionId]?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
246
|
+
}, [activeTab, setActiveTab])
|
|
247
|
+
|
|
248
|
+
const matchingSections = useMemo(() => {
|
|
249
|
+
if (!searchQuery.trim()) return sections
|
|
250
|
+
const q = searchQuery.toLowerCase()
|
|
251
|
+
return sections.filter((section) =>
|
|
252
|
+
section.title.toLowerCase().includes(q)
|
|
253
|
+
|| section.description.toLowerCase().includes(q)
|
|
254
|
+
|| section.keywords.some((keyword) => keyword.toLowerCase().includes(q)),
|
|
255
|
+
)
|
|
256
|
+
}, [searchQuery, sections])
|
|
105
257
|
|
|
106
258
|
const matchingTabIds = searchQuery
|
|
107
|
-
? new Set(
|
|
108
|
-
const q = searchQuery.toLowerCase()
|
|
109
|
-
return t.label.toLowerCase().includes(q) || t.keywords.some((k) => k.includes(q))
|
|
110
|
-
}).map((t) => t.id))
|
|
259
|
+
? new Set(matchingSections.map((section) => section.tabId))
|
|
111
260
|
: null
|
|
112
261
|
|
|
113
262
|
// Auto-switch to first matching tab when searching
|
|
@@ -119,6 +268,31 @@ export function SettingsPage() {
|
|
|
119
268
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
120
269
|
}, [searchQuery])
|
|
121
270
|
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
if (!pendingSectionId) return
|
|
273
|
+
const frame = window.requestAnimationFrame(() => {
|
|
274
|
+
sectionRefs.current[pendingSectionId]?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
275
|
+
setPendingSectionId(null)
|
|
276
|
+
})
|
|
277
|
+
return () => window.cancelAnimationFrame(frame)
|
|
278
|
+
}, [activeTab, pendingSectionId])
|
|
279
|
+
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
const handleFocus = (event: Event) => {
|
|
282
|
+
const detail = (event as CustomEvent<SettingsFocusDetail>).detail
|
|
283
|
+
if (!detail) return
|
|
284
|
+
if (detail.sectionId) {
|
|
285
|
+
focusSection(detail.sectionId, detail.tabId)
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
if (detail.tabId) setActiveTab(detail.tabId)
|
|
289
|
+
}
|
|
290
|
+
window.addEventListener('swarmclaw:settings-focus', handleFocus as EventListener)
|
|
291
|
+
return () => window.removeEventListener('swarmclaw:settings-focus', handleFocus as EventListener)
|
|
292
|
+
}, [focusSection, setActiveTab])
|
|
293
|
+
|
|
294
|
+
const visibleSections = sectionsByTab.get(activeTab) || []
|
|
295
|
+
|
|
122
296
|
return (
|
|
123
297
|
<div className="flex-1 flex h-full min-w-0">
|
|
124
298
|
{/* Tab sidebar */}
|
|
@@ -134,7 +308,7 @@ export function SettingsPage() {
|
|
|
134
308
|
type="text"
|
|
135
309
|
value={searchQuery}
|
|
136
310
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
137
|
-
placeholder="Search settings..."
|
|
311
|
+
placeholder="Search settings or jump to a section..."
|
|
138
312
|
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"
|
|
139
313
|
style={{ fontFamily: 'inherit' }}
|
|
140
314
|
/>
|
|
@@ -169,59 +343,73 @@ export function SettingsPage() {
|
|
|
169
343
|
{TABS.find((t) => t.id === activeTab)?.label}
|
|
170
344
|
</h3>
|
|
171
345
|
<p className="text-[13px] text-text-3 mt-1">
|
|
172
|
-
{activeTab === 'general' && 'User preferences and global
|
|
346
|
+
{activeTab === 'general' && 'User preferences, default-chat behavior, and global controls.'}
|
|
173
347
|
{activeTab === 'appearance' && 'Customize the look and feel of the interface.'}
|
|
174
|
-
{activeTab === 'agents' && '
|
|
348
|
+
{activeTab === 'agents' && 'Agent coordination, autonomy, delegation, and heartbeat.'}
|
|
175
349
|
{activeTab === 'memory' && 'Embedding, memory governance, voice and web search.'}
|
|
176
|
-
{activeTab === 'integrations' && 'Providers,
|
|
350
|
+
{activeTab === 'integrations' && 'Providers, endpoints, and encrypted credentials.'}
|
|
177
351
|
</p>
|
|
178
352
|
</div>
|
|
179
353
|
|
|
180
|
-
{
|
|
181
|
-
|
|
182
|
-
<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
354
|
+
{searchQuery && (
|
|
355
|
+
<div className="mb-8 rounded-[16px] border border-white/[0.06] bg-white/[0.02] p-4">
|
|
356
|
+
<div className="flex items-center justify-between gap-3 mb-3">
|
|
357
|
+
<div>
|
|
358
|
+
<p className="text-[12px] font-600 text-text-2">
|
|
359
|
+
{matchingSections.length > 0 ? `${matchingSections.length} matching section${matchingSections.length === 1 ? '' : 's'}` : 'No direct section matches'}
|
|
360
|
+
</p>
|
|
361
|
+
<p className="text-[11px] text-text-3/60">
|
|
362
|
+
Search now lands on individual settings sections instead of only tab names.
|
|
363
|
+
</p>
|
|
364
|
+
</div>
|
|
365
|
+
{searchQuery && (
|
|
366
|
+
<button
|
|
367
|
+
onClick={() => setSearchQuery('')}
|
|
368
|
+
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"
|
|
369
|
+
style={{ fontFamily: 'inherit' }}
|
|
370
|
+
>
|
|
371
|
+
Clear
|
|
372
|
+
</button>
|
|
373
|
+
)}
|
|
374
|
+
</div>
|
|
375
|
+
{matchingSections.length > 0 && (
|
|
376
|
+
<div className="flex flex-wrap gap-2">
|
|
377
|
+
{matchingSections.slice(0, 8).map((section) => (
|
|
378
|
+
<button
|
|
379
|
+
key={section.id}
|
|
380
|
+
onClick={() => focusSection(section.id, section.tabId)}
|
|
381
|
+
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"
|
|
382
|
+
style={{ fontFamily: 'inherit' }}
|
|
383
|
+
>
|
|
384
|
+
<div className="text-[12px] font-600 text-text">{section.title}</div>
|
|
385
|
+
<div className="text-[10px] text-text-3/60">{TABS.find((tab) => tab.id === section.tabId)?.label}</div>
|
|
386
|
+
</button>
|
|
387
|
+
))}
|
|
388
|
+
</div>
|
|
389
|
+
)}
|
|
390
|
+
</div>
|
|
207
391
|
)}
|
|
208
392
|
|
|
209
|
-
{
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
393
|
+
{visibleSections.map((section) => (
|
|
394
|
+
<div
|
|
395
|
+
key={section.id}
|
|
396
|
+
ref={(node) => setSectionRef(section.id, node)}
|
|
397
|
+
className="mb-10 scroll-mt-6 last:mb-0"
|
|
398
|
+
>
|
|
399
|
+
<div className="mb-4">
|
|
400
|
+
<div className="text-[11px] uppercase tracking-[0.08em] text-text-3/45 mb-1">
|
|
401
|
+
{TABS.find((tab) => tab.id === section.tabId)?.label}
|
|
402
|
+
</div>
|
|
403
|
+
<h4 className="font-display text-[18px] font-700 tracking-[-0.02em] text-text">
|
|
404
|
+
{section.title}
|
|
405
|
+
</h4>
|
|
406
|
+
<p className="text-[12px] text-text-3 mt-1">
|
|
407
|
+
{section.description}
|
|
220
408
|
</p>
|
|
221
|
-
<PluginManager />
|
|
222
409
|
</div>
|
|
223
|
-
|
|
224
|
-
|
|
410
|
+
{section.render()}
|
|
411
|
+
</div>
|
|
412
|
+
))}
|
|
225
413
|
</div>
|
|
226
414
|
</div>
|
|
227
415
|
</div>
|