@swarmclawai/swarmclaw 0.7.7 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -14
- package/next.config.ts +13 -2
- package/package.json +4 -2
- package/src/app/api/agents/[id]/thread/route.ts +9 -0
- package/src/app/api/agents/route.ts +4 -0
- package/src/app/api/agents/thread-route.test.ts +133 -0
- package/src/app/api/approvals/route.test.ts +148 -0
- package/src/app/api/canvas/[sessionId]/route.ts +3 -1
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -2
- package/src/app/api/chats/[id]/devserver/route.ts +48 -7
- package/src/app/api/chats/[id]/messages/route.ts +42 -18
- package/src/app/api/chats/[id]/route.ts +1 -1
- package/src/app/api/chats/[id]/stop/route.ts +5 -4
- package/src/app/api/chats/route.ts +23 -2
- package/src/app/api/clawhub/install/route.ts +28 -8
- package/src/app/api/connectors/[id]/route.ts +46 -3
- package/src/app/api/connectors/route.ts +12 -8
- package/src/app/api/external-agents/route.test.ts +165 -0
- package/src/app/api/gateways/[id]/health/route.ts +27 -12
- package/src/app/api/gateways/[id]/route.ts +2 -0
- package/src/app/api/gateways/health-route.test.ts +135 -0
- package/src/app/api/gateways/route.ts +2 -0
- package/src/app/api/mcp-servers/route.test.ts +130 -0
- package/src/app/api/openclaw/deploy/route.ts +38 -5
- package/src/app/api/plugins/install/route.ts +46 -6
- package/src/app/api/plugins/marketplace/route.ts +48 -15
- package/src/app/api/preview-server/route.ts +26 -11
- package/src/app/api/projects/[id]/route.ts +6 -2
- package/src/app/api/projects/route.ts +4 -3
- package/src/app/api/schedules/[id]/run/route.ts +4 -0
- package/src/app/api/schedules/route.test.ts +86 -0
- package/src/app/api/schedules/route.ts +6 -1
- package/src/app/api/secrets/[id]/route.ts +1 -0
- package/src/app/api/secrets/route.ts +2 -1
- package/src/app/api/settings/route.ts +2 -0
- package/src/app/api/setup/check-provider/route.test.ts +19 -0
- package/src/app/api/setup/check-provider/route.ts +40 -10
- package/src/app/api/skills/[id]/route.ts +12 -0
- package/src/app/api/skills/import/route.ts +14 -12
- package/src/app/api/skills/route.ts +13 -1
- package/src/app/api/tasks/[id]/route.ts +10 -1
- package/src/app/api/tasks/import/github/route.test.ts +65 -0
- package/src/app/api/tasks/import/github/route.ts +337 -0
- package/src/app/api/wallets/[id]/approve/route.ts +17 -3
- package/src/app/api/wallets/[id]/route.ts +79 -33
- package/src/app/api/wallets/[id]/send/route.ts +19 -33
- package/src/app/api/wallets/route.ts +78 -61
- package/src/app/api/webhooks/[id]/route.ts +33 -6
- package/src/app/api/webhooks/route.test.ts +272 -0
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-card.tsx +9 -2
- package/src/components/agents/agent-chat-list.tsx +18 -2
- package/src/components/agents/agent-list.tsx +1 -0
- package/src/components/agents/agent-sheet.tsx +257 -38
- package/src/components/agents/inspector-panel.tsx +41 -0
- package/src/components/canvas/canvas-panel.tsx +236 -65
- package/src/components/chat/chat-area.tsx +36 -19
- package/src/components/chat/chat-card.tsx +36 -13
- package/src/components/chat/chat-header.tsx +48 -16
- package/src/components/chat/chat-list.tsx +28 -4
- package/src/components/chat/checkpoint-timeline.tsx +50 -34
- package/src/components/chat/delegation-banner.test.ts +14 -1
- package/src/components/chat/delegation-banner.tsx +1 -1
- package/src/components/chat/message-bubble.tsx +208 -145
- package/src/components/chat/message-list.tsx +48 -19
- package/src/components/chatrooms/chatroom-message.tsx +2 -2
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -2
- package/src/components/connectors/connector-health.tsx +1 -1
- package/src/components/connectors/connector-list.tsx +7 -2
- package/src/components/connectors/connector-sheet.tsx +337 -148
- package/src/components/gateways/gateway-sheet.tsx +2 -2
- package/src/components/layout/app-layout.tsx +40 -23
- package/src/components/mcp-servers/mcp-server-list.tsx +26 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +19 -2
- package/src/components/openclaw/openclaw-deploy-panel.tsx +269 -21
- package/src/components/plugins/plugin-list.tsx +45 -9
- package/src/components/plugins/plugin-sheet.tsx +55 -7
- package/src/components/projects/project-detail.tsx +217 -0
- package/src/components/projects/project-sheet.tsx +176 -4
- package/src/components/providers/provider-list.tsx +2 -1
- package/src/components/providers/provider-sheet.tsx +21 -2
- package/src/components/schedules/schedule-card.tsx +25 -1
- package/src/components/schedules/schedule-sheet.tsx +44 -2
- package/src/components/secrets/secret-sheet.tsx +21 -2
- package/src/components/shared/agent-switch-dialog.tsx +12 -1
- package/src/components/shared/bottom-sheet.tsx +13 -3
- package/src/components/shared/command-palette.tsx +8 -1
- package/src/components/shared/confirm-dialog.tsx +19 -4
- package/src/components/shared/connector-platform-icon.test.ts +28 -0
- package/src/components/shared/connector-platform-icon.tsx +39 -6
- package/src/components/shared/settings/plugin-manager.tsx +29 -6
- package/src/components/shared/settings/section-capability-policy.tsx +45 -3
- package/src/components/shared/settings/section-voice.tsx +11 -3
- package/src/components/skills/skill-list.tsx +25 -0
- package/src/components/skills/skill-sheet.tsx +84 -12
- package/src/components/tasks/approvals-panel.tsx +289 -34
- package/src/components/tasks/task-board.tsx +410 -25
- package/src/components/tasks/task-card.tsx +66 -8
- package/src/components/tasks/task-sheet.tsx +16 -4
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/wallets/wallet-approval-dialog.tsx +4 -2
- package/src/components/wallets/wallet-panel.tsx +435 -90
- package/src/components/wallets/wallet-section.tsx +198 -48
- package/src/components/webhooks/webhook-sheet.tsx +22 -2
- package/src/lib/approval-display.ts +20 -0
- package/src/lib/canvas-content.ts +198 -0
- package/src/lib/chat-artifact-summary.ts +165 -0
- package/src/lib/chat-display.test.ts +91 -0
- package/src/lib/chat-display.ts +58 -0
- package/src/lib/chat-streaming-state.test.ts +47 -1
- package/src/lib/chat-streaming-state.ts +42 -0
- package/src/lib/ollama-model.ts +10 -0
- package/src/lib/openclaw-endpoint.test.ts +8 -0
- package/src/lib/openclaw-endpoint.ts +6 -1
- package/src/lib/plugin-install-cors.ts +46 -0
- package/src/lib/plugin-sources.test.ts +43 -0
- package/src/lib/plugin-sources.ts +77 -0
- package/src/lib/providers/ollama.ts +16 -6
- package/src/lib/providers/openclaw.test.ts +54 -0
- package/src/lib/providers/openclaw.ts +127 -11
- package/src/lib/schedule-dedupe-advanced.test.ts +1335 -0
- package/src/lib/schedule-dedupe.test.ts +66 -1
- package/src/lib/schedule-dedupe.ts +169 -12
- package/src/lib/schedule-origin.test.ts +20 -0
- package/src/lib/schedule-origin.ts +15 -0
- package/src/lib/server/__fixtures__/fake-mcp-stdio-server.mjs +27 -0
- package/src/lib/server/agent-availability.ts +16 -0
- package/src/lib/server/agent-runtime-config.ts +12 -4
- package/src/lib/server/agent-thread-session.test.ts +51 -0
- package/src/lib/server/agent-thread-session.ts +7 -0
- package/src/lib/server/approval-match.ts +205 -0
- package/src/lib/server/approvals-auto-approve.test.ts +538 -1
- package/src/lib/server/approvals.ts +214 -1
- package/src/lib/server/assistant-control.test.ts +29 -0
- package/src/lib/server/assistant-control.ts +23 -0
- package/src/lib/server/build-llm.test.ts +79 -0
- package/src/lib/server/build-llm.ts +14 -4
- package/src/lib/server/canvas-content.test.ts +32 -0
- package/src/lib/server/canvas-content.ts +6 -0
- package/src/lib/server/capability-router.test.ts +33 -0
- package/src/lib/server/capability-router.ts +80 -19
- package/src/lib/server/chat-execution-advanced.test.ts +651 -0
- package/src/lib/server/chat-execution-disabled.test.ts +94 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +157 -0
- package/src/lib/server/chat-execution.ts +378 -73
- package/src/lib/server/clawhub-client.test.ts +14 -8
- package/src/lib/server/connectors/manager-reconnect.test.ts +47 -0
- package/src/lib/server/connectors/manager.test.ts +1147 -0
- package/src/lib/server/connectors/manager.ts +461 -137
- package/src/lib/server/connectors/pairing.ts +26 -5
- package/src/lib/server/connectors/types.ts +2 -0
- package/src/lib/server/connectors/whatsapp.test.ts +134 -0
- package/src/lib/server/connectors/whatsapp.ts +271 -47
- package/src/lib/server/context-manager.ts +6 -1
- package/src/lib/server/daemon-state.ts +84 -47
- package/src/lib/server/data-dir.test.ts +37 -0
- package/src/lib/server/data-dir.ts +20 -1
- package/src/lib/server/delegation-jobs-advanced.test.ts +513 -0
- package/src/lib/server/devserver-launch.test.ts +60 -0
- package/src/lib/server/devserver-launch.ts +85 -0
- package/src/lib/server/elevenlabs.test.ts +247 -1
- package/src/lib/server/elevenlabs.ts +147 -43
- package/src/lib/server/ethereum.ts +590 -0
- package/src/lib/server/eval/agent-regression-advanced.test.ts +302 -0
- package/src/lib/server/eval/agent-regression.test.ts +18 -1
- package/src/lib/server/eval/agent-regression.ts +383 -11
- package/src/lib/server/evm-swap.ts +475 -0
- package/src/lib/server/execution-log.ts +1 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +173 -0
- package/src/lib/server/heartbeat-service.ts +20 -11
- package/src/lib/server/heartbeat-wake.test.ts +112 -0
- package/src/lib/server/heartbeat-wake.ts +338 -57
- package/src/lib/server/main-agent-loop-advanced.test.ts +538 -0
- package/src/lib/server/main-agent-loop.test.ts +260 -0
- package/src/lib/server/main-agent-loop.ts +559 -14
- package/src/lib/server/mcp-client.test.ts +16 -0
- package/src/lib/server/mcp-client.ts +25 -0
- package/src/lib/server/memory-integration.test.ts +719 -0
- package/src/lib/server/memory-policy.test.ts +43 -0
- package/src/lib/server/memory-policy.ts +132 -0
- package/src/lib/server/memory-tiers.test.ts +60 -0
- package/src/lib/server/memory-tiers.ts +16 -0
- package/src/lib/server/ollama-runtime.ts +58 -0
- package/src/lib/server/openclaw-deploy.test.ts +109 -1
- package/src/lib/server/openclaw-deploy.ts +557 -81
- package/src/lib/server/openclaw-gateway.test.ts +131 -0
- package/src/lib/server/openclaw-gateway.ts +10 -4
- package/src/lib/server/openclaw-health.test.ts +35 -0
- package/src/lib/server/openclaw-health.ts +215 -47
- package/src/lib/server/orchestrator-lg.ts +3 -2
- package/src/lib/server/orchestrator.ts +2 -0
- package/src/lib/server/plugins-advanced.test.ts +351 -0
- package/src/lib/server/plugins.ts +211 -6
- package/src/lib/server/project-context.ts +162 -0
- package/src/lib/server/project-utils.ts +150 -0
- package/src/lib/server/queue-advanced.test.ts +528 -0
- package/src/lib/server/queue-followups.test.ts +409 -2
- package/src/lib/server/queue-reconcile.test.ts +128 -0
- package/src/lib/server/queue.ts +527 -68
- package/src/lib/server/scheduler.ts +29 -1
- package/src/lib/server/session-note.test.ts +36 -0
- package/src/lib/server/session-note.ts +42 -0
- package/src/lib/server/session-run-manager.ts +83 -4
- package/src/lib/server/session-tools/canvas.ts +14 -12
- package/src/lib/server/session-tools/connector-inputs.test.ts +37 -0
- package/src/lib/server/session-tools/connector.test.ts +138 -0
- package/src/lib/server/session-tools/connector.ts +366 -54
- package/src/lib/server/session-tools/context.ts +17 -3
- package/src/lib/server/session-tools/crud.ts +484 -84
- package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
- package/src/lib/server/session-tools/delegate-resume.test.ts +50 -0
- package/src/lib/server/session-tools/delegate.ts +102 -10
- package/src/lib/server/session-tools/discovery-approvals.test.ts +142 -0
- package/src/lib/server/session-tools/discovery.ts +80 -12
- package/src/lib/server/session-tools/file-normalize.test.ts +36 -0
- package/src/lib/server/session-tools/file.ts +43 -4
- package/src/lib/server/session-tools/human-loop.ts +35 -5
- package/src/lib/server/session-tools/index.ts +44 -9
- package/src/lib/server/session-tools/manage-connectors.test.ts +139 -0
- package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +564 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +283 -0
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +852 -0
- package/src/lib/server/session-tools/manage-tasks.test.ts +114 -0
- package/src/lib/server/session-tools/memory.test.ts +93 -0
- package/src/lib/server/session-tools/memory.ts +554 -75
- package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
- package/src/lib/server/session-tools/platform-access.test.ts +58 -0
- package/src/lib/server/session-tools/platform.ts +60 -19
- package/src/lib/server/session-tools/plugin-creator.ts +57 -1
- package/src/lib/server/session-tools/primitive-tools.test.ts +6 -0
- package/src/lib/server/session-tools/schedule.ts +6 -1
- package/src/lib/server/session-tools/shell-normalize.test.ts +25 -1
- package/src/lib/server/session-tools/shell.ts +22 -3
- package/src/lib/server/session-tools/wallet-tool.test.ts +254 -0
- package/src/lib/server/session-tools/wallet.ts +1374 -139
- package/src/lib/server/session-tools/web-inputs.test.ts +178 -0
- package/src/lib/server/session-tools/web.ts +621 -70
- package/src/lib/server/skill-discovery.ts +128 -0
- package/src/lib/server/skill-eligibility.test.ts +84 -0
- package/src/lib/server/skill-eligibility.ts +95 -0
- package/src/lib/server/skill-prompt-budget.test.ts +102 -0
- package/src/lib/server/skill-prompt-budget.ts +125 -0
- package/src/lib/server/skills-normalize.test.ts +54 -0
- package/src/lib/server/skills-normalize.ts +372 -26
- package/src/lib/server/solana.ts +214 -29
- package/src/lib/server/storage.ts +65 -36
- package/src/lib/server/stream-agent-chat.test.ts +437 -2
- package/src/lib/server/stream-agent-chat.ts +957 -79
- package/src/lib/server/system-events.ts +1 -1
- package/src/lib/server/tool-aliases.ts +2 -0
- package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
- package/src/lib/server/tool-capability-policy.test.ts +24 -0
- package/src/lib/server/tool-capability-policy.ts +29 -1
- package/src/lib/server/tool-loop-detection.test.ts +105 -0
- package/src/lib/server/tool-loop-detection.ts +260 -0
- package/src/lib/server/tool-planning.test.ts +44 -0
- package/src/lib/server/tool-planning.ts +271 -0
- package/src/lib/server/wallet-execution.test.ts +198 -0
- package/src/lib/server/wallet-portfolio.test.ts +98 -0
- package/src/lib/server/wallet-portfolio.ts +724 -0
- package/src/lib/server/wallet-service.test.ts +57 -0
- package/src/lib/server/wallet-service.ts +213 -0
- package/src/lib/server/watch-jobs-advanced.test.ts +594 -0
- package/src/lib/server/watch-jobs.ts +17 -2
- package/src/lib/server/workspace-context.ts +111 -0
- package/src/lib/skill-save-payload.test.ts +39 -0
- package/src/lib/skill-save-payload.ts +37 -0
- package/src/lib/tasks.ts +28 -0
- package/src/lib/tool-definitions.ts +2 -1
- package/src/lib/tool-event-summary.test.ts +30 -0
- package/src/lib/tool-event-summary.ts +37 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/wallet-transactions.test.ts +75 -0
- package/src/lib/wallet-transactions.ts +43 -0
- package/src/lib/wallet.test.ts +17 -0
- package/src/lib/wallet.ts +183 -0
- package/src/proxy.test.ts +31 -0
- package/src/proxy.ts +34 -2
- package/src/stores/use-chat-store.ts +15 -1
- package/src/types/index.ts +249 -14
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useEffect, useRef, useState } from 'react'
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
5
|
import { createAgent, updateAgent, deleteAgent } from '@/lib/agents'
|
|
6
6
|
import { api } from '@/lib/api-client'
|
|
@@ -18,8 +18,48 @@ import { copyTextToClipboard } from '@/lib/clipboard'
|
|
|
18
18
|
import { SectionLabel } from '@/components/shared/section-label'
|
|
19
19
|
import { SoulLibraryPicker } from './soul-library-picker'
|
|
20
20
|
import { HintTip } from '@/components/shared/hint-tip'
|
|
21
|
+
import { isOllamaCloudModel } from '@/lib/ollama-model'
|
|
21
22
|
|
|
22
23
|
const HB_PRESETS = [1800, 3600, 7200, 21600, 43200] as const
|
|
24
|
+
const FALLBACK_ELEVENLABS_VOICE_ID = 'JBFqnCBsd6RMkjVDRZzb'
|
|
25
|
+
|
|
26
|
+
type AgentSheetSectionId = 'overview' | 'instructions' | 'model' | 'tools'
|
|
27
|
+
type SafeAgentWallet = Omit<AgentWallet, 'encryptedPrivateKey'> & {
|
|
28
|
+
balanceAtomic?: string
|
|
29
|
+
balanceLamports?: number
|
|
30
|
+
balanceFormatted?: string
|
|
31
|
+
balanceSymbol?: string
|
|
32
|
+
isActive?: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function SectionCard({
|
|
36
|
+
title,
|
|
37
|
+
description,
|
|
38
|
+
action,
|
|
39
|
+
children,
|
|
40
|
+
className = '',
|
|
41
|
+
}: {
|
|
42
|
+
title: string
|
|
43
|
+
description?: string
|
|
44
|
+
action?: React.ReactNode
|
|
45
|
+
children: React.ReactNode
|
|
46
|
+
className?: string
|
|
47
|
+
}) {
|
|
48
|
+
return (
|
|
49
|
+
<section className={`mb-8 rounded-[20px] border border-white/[0.06] bg-surface/70 p-5 sm:p-6 ${className}`}>
|
|
50
|
+
<div className="mb-5 flex items-start justify-between gap-4">
|
|
51
|
+
<div>
|
|
52
|
+
<h3 className="font-display text-[17px] font-700 tracking-[-0.02em] text-text">{title}</h3>
|
|
53
|
+
{description && (
|
|
54
|
+
<p className="mt-1 text-[13px] leading-[1.6] text-text-3/75">{description}</p>
|
|
55
|
+
)}
|
|
56
|
+
</div>
|
|
57
|
+
{action}
|
|
58
|
+
</div>
|
|
59
|
+
{children}
|
|
60
|
+
</section>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
23
63
|
|
|
24
64
|
function formatHbDuration(sec: number): string {
|
|
25
65
|
if (sec >= 3600) {
|
|
@@ -106,6 +146,7 @@ export function AgentSheet() {
|
|
|
106
146
|
const credentials = useAppStore((s) => s.credentials)
|
|
107
147
|
const loadCredentials = useAppStore((s) => s.loadCredentials)
|
|
108
148
|
const appSettings = useAppStore((s) => s.appSettings)
|
|
149
|
+
const loadSettings = useAppStore((s) => s.loadSettings)
|
|
109
150
|
const dynamicSkills = useAppStore((s) => s.skills)
|
|
110
151
|
const mcpServers = useAppStore((s) => s.mcpServers)
|
|
111
152
|
const loadSkills = useAppStore((s) => s.loadSkills)
|
|
@@ -157,6 +198,7 @@ export function AgentSheet() {
|
|
|
157
198
|
const [memoryScopeMode, setMemoryScopeMode] = useState<'auto' | 'all' | 'global' | 'agent' | 'session' | 'project'>('auto')
|
|
158
199
|
const [memoryTierPreference, setMemoryTierPreference] = useState<'working' | 'durable' | 'archive' | 'blended'>('blended')
|
|
159
200
|
const [autoRecovery, setAutoRecovery] = useState(false)
|
|
201
|
+
const [disabled, setDisabled] = useState(false)
|
|
160
202
|
const [voiceId, setVoiceId] = useState('')
|
|
161
203
|
const [heartbeatEnabled, setHeartbeatEnabled] = useState(false)
|
|
162
204
|
const [heartbeatIntervalSec, setHeartbeatIntervalSec] = useState('') // '' = default (30m)
|
|
@@ -178,7 +220,7 @@ export function AgentSheet() {
|
|
|
178
220
|
const [dailyBudget, setDailyBudget] = useState('')
|
|
179
221
|
const [monthlyBudget, setMonthlyBudget] = useState('')
|
|
180
222
|
const [budgetAction, setBudgetAction] = useState<'warn' | 'block'>('warn')
|
|
181
|
-
const [
|
|
223
|
+
const [agentWallets, setAgentWallets] = useState<SafeAgentWallet[]>([])
|
|
182
224
|
const [addingKey, setAddingKey] = useState(false)
|
|
183
225
|
const [newKeyName, setNewKeyName] = useState('')
|
|
184
226
|
const [newKeyValue, setNewKeyValue] = useState('')
|
|
@@ -196,6 +238,12 @@ export function AgentSheet() {
|
|
|
196
238
|
const [soulLibraryOpen, setSoulLibraryOpen] = useState(false)
|
|
197
239
|
const promptFileRef = useRef<HTMLInputElement>(null)
|
|
198
240
|
const importFileRef = useRef<HTMLInputElement>(null)
|
|
241
|
+
const sectionRefs = useRef<Record<AgentSheetSectionId, HTMLDivElement | null>>({
|
|
242
|
+
overview: null,
|
|
243
|
+
instructions: null,
|
|
244
|
+
model: null,
|
|
245
|
+
tools: null,
|
|
246
|
+
})
|
|
199
247
|
|
|
200
248
|
const handleFileUpload = (setter: (v: string) => void) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
201
249
|
const file = e.target.files?.[0]
|
|
@@ -206,12 +254,40 @@ export function AgentSheet() {
|
|
|
206
254
|
e.target.value = ''
|
|
207
255
|
}
|
|
208
256
|
|
|
257
|
+
const loadAgentWallets = useCallback(async (agentId: string) => {
|
|
258
|
+
try {
|
|
259
|
+
const wallets = await api<Record<string, SafeAgentWallet>>('GET', `/wallets?agentId=${encodeURIComponent(agentId)}`)
|
|
260
|
+
const matches = Object.values(wallets)
|
|
261
|
+
.filter((wallet) => wallet.agentId === agentId)
|
|
262
|
+
.sort((a, b) => {
|
|
263
|
+
if ((a.isActive ? 1 : 0) !== (b.isActive ? 1 : 0)) return a.isActive ? -1 : 1
|
|
264
|
+
return a.chain.localeCompare(b.chain)
|
|
265
|
+
})
|
|
266
|
+
setAgentWallets(matches)
|
|
267
|
+
} catch {
|
|
268
|
+
setAgentWallets([])
|
|
269
|
+
}
|
|
270
|
+
}, [])
|
|
271
|
+
|
|
209
272
|
const currentProvider = providers.find((p) => p.id === provider)
|
|
210
273
|
const providerCredentials = Object.values(credentials).filter((c) => c.provider === provider)
|
|
211
274
|
const openclawCredentials = Object.values(credentials).filter((c) => c.provider === 'openclaw')
|
|
212
275
|
const openclawGatewayProfiles = gatewayProfiles.filter((item) => item.provider === 'openclaw')
|
|
213
276
|
const editing = editingId ? agents[editingId] : null
|
|
214
277
|
const hasNativeCapabilities = NATIVE_CAPABILITY_PROVIDER_IDS.has(provider)
|
|
278
|
+
const globalVoiceId = typeof appSettings.elevenLabsVoiceId === 'string' ? appSettings.elevenLabsVoiceId.trim() : ''
|
|
279
|
+
const agentVoiceId = voiceId.trim()
|
|
280
|
+
const elevenLabsConfigured = appSettings.elevenLabsApiKeyConfigured === true
|
|
281
|
+
const voiceControlsAvailable = elevenLabsConfigured || appSettings.elevenLabsEnabled === true || !!globalVoiceId || !!agentVoiceId
|
|
282
|
+
const voicePlaybackEnabled = appSettings.elevenLabsEnabled === true
|
|
283
|
+
const effectiveVoiceId = agentVoiceId || globalVoiceId || FALLBACK_ELEVENLABS_VOICE_ID
|
|
284
|
+
const effectiveVoiceSource = agentVoiceId
|
|
285
|
+
? 'Agent override'
|
|
286
|
+
: globalVoiceId
|
|
287
|
+
? 'Global default'
|
|
288
|
+
: 'Built-in fallback'
|
|
289
|
+
const providerSummary = openclawEnabled ? 'OpenClaw gateway' : (currentProvider?.name || provider)
|
|
290
|
+
const modelSummary = openclawEnabled ? (gatewayProfileId ? 'Gateway-managed' : 'default') : (model || 'Select a model')
|
|
215
291
|
|
|
216
292
|
const providerNeedsKey = !editing && (
|
|
217
293
|
(currentProvider?.requiresApiKey && providerCredentials.length === 0 && !addingKey) ||
|
|
@@ -220,6 +296,7 @@ export function AgentSheet() {
|
|
|
220
296
|
|
|
221
297
|
useEffect(() => {
|
|
222
298
|
if (open) {
|
|
299
|
+
loadSettings()
|
|
223
300
|
loadProviders()
|
|
224
301
|
loadGatewayProfiles()
|
|
225
302
|
loadCredentials()
|
|
@@ -254,7 +331,11 @@ export function AgentSheet() {
|
|
|
254
331
|
setFallbackCredentialIds(editing.fallbackCredentialIds || [])
|
|
255
332
|
setCapabilities(Array.isArray(editing.capabilities) ? editing.capabilities : [])
|
|
256
333
|
setCapInput('')
|
|
257
|
-
setOllamaMode(
|
|
334
|
+
setOllamaMode(
|
|
335
|
+
editing.provider === 'ollama' && (Boolean(editing.credentialId) || isOllamaCloudModel(editing.model))
|
|
336
|
+
? 'cloud'
|
|
337
|
+
: 'local'
|
|
338
|
+
)
|
|
258
339
|
setOpenclawEnabled(editing.provider === 'openclaw')
|
|
259
340
|
setProjectId(editing.projectId)
|
|
260
341
|
setAvatarSeed(editing.avatarSeed || crypto.randomUUID().slice(0, 8))
|
|
@@ -263,6 +344,7 @@ export function AgentSheet() {
|
|
|
263
344
|
setMemoryScopeMode(editing.memoryScopeMode || 'auto')
|
|
264
345
|
setMemoryTierPreference(editing.memoryTierPreference || 'blended')
|
|
265
346
|
setAutoRecovery(editing.autoRecovery || false)
|
|
347
|
+
setDisabled(editing.disabled === true)
|
|
266
348
|
setVoiceId(editing.elevenLabsVoiceId || '')
|
|
267
349
|
setHeartbeatEnabled(editing.heartbeatEnabled || false)
|
|
268
350
|
setHeartbeatIntervalSec(parseDurationToSec(editing.heartbeatInterval, editing.heartbeatIntervalSec))
|
|
@@ -288,14 +370,7 @@ export function AgentSheet() {
|
|
|
288
370
|
setDailyBudget(typeof editing.dailyBudget === 'number' && editing.dailyBudget > 0 ? String(editing.dailyBudget) : '')
|
|
289
371
|
setMonthlyBudget(typeof editing.monthlyBudget === 'number' && editing.monthlyBudget > 0 ? String(editing.monthlyBudget) : '')
|
|
290
372
|
setBudgetAction(editing.budgetAction || 'warn')
|
|
291
|
-
|
|
292
|
-
if (editing.walletId) {
|
|
293
|
-
api<Omit<AgentWallet, 'encryptedPrivateKey'> & { balanceLamports?: number; balanceSol?: number }>('GET', `/wallets/${editing.walletId}`)
|
|
294
|
-
.then(setAgentWallet)
|
|
295
|
-
.catch(() => setAgentWallet(null))
|
|
296
|
-
} else {
|
|
297
|
-
setAgentWallet(null)
|
|
298
|
-
}
|
|
373
|
+
void loadAgentWallets(editing.id)
|
|
299
374
|
} else {
|
|
300
375
|
setName('')
|
|
301
376
|
setDescription('')
|
|
@@ -330,6 +405,7 @@ export function AgentSheet() {
|
|
|
330
405
|
setMemoryScopeMode('auto')
|
|
331
406
|
setMemoryTierPreference('blended')
|
|
332
407
|
setAutoRecovery(false)
|
|
408
|
+
setDisabled(false)
|
|
333
409
|
setVoiceId('')
|
|
334
410
|
setHeartbeatEnabled(false)
|
|
335
411
|
setHeartbeatIntervalSec('')
|
|
@@ -351,6 +427,7 @@ export function AgentSheet() {
|
|
|
351
427
|
setDailyBudget('')
|
|
352
428
|
setMonthlyBudget('')
|
|
353
429
|
setBudgetAction('warn')
|
|
430
|
+
setAgentWallets([])
|
|
354
431
|
}
|
|
355
432
|
}
|
|
356
433
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -412,6 +489,10 @@ export function AgentSheet() {
|
|
|
412
489
|
setEditingId(null)
|
|
413
490
|
}
|
|
414
491
|
|
|
492
|
+
const jumpToSection = (sectionId: AgentSheetSectionId) => {
|
|
493
|
+
sectionRefs.current[sectionId]?.scrollIntoView({ behavior: 'smooth', block: 'start' })
|
|
494
|
+
}
|
|
495
|
+
|
|
415
496
|
const applyGatewayProfileSelection = (nextGatewayProfileId: string | null) => {
|
|
416
497
|
setGatewayProfileId(nextGatewayProfileId)
|
|
417
498
|
const gateway = openclawGatewayProfiles.find((item) => item.id === nextGatewayProfileId)
|
|
@@ -514,6 +595,7 @@ export function AgentSheet() {
|
|
|
514
595
|
memoryScopeMode,
|
|
515
596
|
memoryTierPreference,
|
|
516
597
|
autoRecovery,
|
|
598
|
+
disabled,
|
|
517
599
|
elevenLabsVoiceId: voiceId.trim() || null,
|
|
518
600
|
heartbeatEnabled,
|
|
519
601
|
heartbeatInterval: heartbeatIntervalSec ? formatHbDuration(Number(heartbeatIntervalSec)) : null,
|
|
@@ -578,6 +660,7 @@ export function AgentSheet() {
|
|
|
578
660
|
tools: editing.tools,
|
|
579
661
|
plugins: editing.plugins,
|
|
580
662
|
capabilities: editing.capabilities,
|
|
663
|
+
elevenLabsVoiceId: editing.elevenLabsVoiceId || null,
|
|
581
664
|
soul: editing.soul,
|
|
582
665
|
systemPrompt: editing.systemPrompt,
|
|
583
666
|
}],
|
|
@@ -605,11 +688,12 @@ export function AgentSheet() {
|
|
|
605
688
|
if (!importedAgent || typeof importedAgent !== 'object') throw new Error('Invalid agent pack')
|
|
606
689
|
// Strip IDs and timestamps
|
|
607
690
|
const { id: _id, createdAt: _ca, updatedAt: _ua, threadSessionId: _ts, ...agentData } = importedAgent
|
|
691
|
+
void [_id, _ca, _ua, _ts]
|
|
608
692
|
await createAgent({ ...agentData, name: agentData.name || 'Imported Agent' })
|
|
609
693
|
await loadAgents()
|
|
610
694
|
toast.success(data?.kind === 'swarmclaw-agent-pack' ? 'Agent pack imported' : 'Agent imported')
|
|
611
695
|
onClose()
|
|
612
|
-
} catch
|
|
696
|
+
} catch {
|
|
613
697
|
toast.error('Invalid agent JSON file')
|
|
614
698
|
}
|
|
615
699
|
}
|
|
@@ -685,9 +769,18 @@ export function AgentSheet() {
|
|
|
685
769
|
<BottomSheet open={open} onClose={onClose} wide>
|
|
686
770
|
<div className="mb-10 flex items-start justify-between">
|
|
687
771
|
<div>
|
|
688
|
-
<
|
|
689
|
-
|
|
690
|
-
|
|
772
|
+
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
773
|
+
<h2 className="font-display text-[28px] font-700 tracking-[-0.03em]">
|
|
774
|
+
{editing ? 'Edit Agent' : 'New Agent'}
|
|
775
|
+
</h2>
|
|
776
|
+
<span className={`rounded-[999px] px-2.5 py-1 text-[10px] font-700 uppercase tracking-[0.1em] ${
|
|
777
|
+
disabled
|
|
778
|
+
? 'border border-amber-400/20 bg-amber-400/[0.08] text-amber-300'
|
|
779
|
+
: 'border border-emerald-400/20 bg-emerald-400/[0.08] text-emerald-300'
|
|
780
|
+
}`}>
|
|
781
|
+
{disabled ? 'Disabled' : 'Enabled'}
|
|
782
|
+
</span>
|
|
783
|
+
</div>
|
|
691
784
|
<p className="text-[14px] text-text-3">Define an AI agent and optional multi-agent delegation behavior</p>
|
|
692
785
|
</div>
|
|
693
786
|
<div className="flex items-center gap-3 mt-1.5">
|
|
@@ -720,6 +813,56 @@ export function AgentSheet() {
|
|
|
720
813
|
</div>
|
|
721
814
|
</div>
|
|
722
815
|
|
|
816
|
+
<div className="mb-8 rounded-[20px] border border-white/[0.06] bg-white/[0.03] p-4 sm:p-5">
|
|
817
|
+
<div className="grid grid-cols-1 gap-3 md:grid-cols-4">
|
|
818
|
+
<div className="rounded-[14px] border border-white/[0.05] bg-black/10 p-3">
|
|
819
|
+
<p className="text-[11px] font-600 uppercase tracking-[0.08em] text-text-3">Provider</p>
|
|
820
|
+
<p className="mt-1 text-[14px] font-600 text-text">{providerSummary}</p>
|
|
821
|
+
</div>
|
|
822
|
+
<div className="rounded-[14px] border border-white/[0.05] bg-black/10 p-3">
|
|
823
|
+
<p className="text-[11px] font-600 uppercase tracking-[0.08em] text-text-3">Model</p>
|
|
824
|
+
<p className="mt-1 text-[14px] font-600 text-text">{modelSummary}</p>
|
|
825
|
+
</div>
|
|
826
|
+
<div className="rounded-[14px] border border-white/[0.05] bg-black/10 p-3">
|
|
827
|
+
<p className="text-[11px] font-600 uppercase tracking-[0.08em] text-text-3">Voice</p>
|
|
828
|
+
<p className="mt-1 text-[14px] font-600 text-text">{voiceControlsAvailable ? effectiveVoiceSource : 'Not configured'}</p>
|
|
829
|
+
{voiceControlsAvailable && (
|
|
830
|
+
<p className="mt-1 truncate text-[12px] text-text-3/75">{effectiveVoiceId}</p>
|
|
831
|
+
)}
|
|
832
|
+
</div>
|
|
833
|
+
<div className="rounded-[14px] border border-white/[0.05] bg-black/10 p-3">
|
|
834
|
+
<p className="text-[11px] font-600 uppercase tracking-[0.08em] text-text-3">Mode</p>
|
|
835
|
+
<p className="mt-1 text-[14px] font-600 text-text">{canDelegateToAgents ? 'Delegating agent' : 'Solo agent'}</p>
|
|
836
|
+
<p className="mt-1 text-[12px] text-text-3/75">
|
|
837
|
+
{routingTargets.length > 0 ? `${routingTargets.length} route${routingTargets.length === 1 ? '' : 's'} configured` : 'Single primary route'}
|
|
838
|
+
</p>
|
|
839
|
+
</div>
|
|
840
|
+
</div>
|
|
841
|
+
<div className="mt-4 flex flex-wrap gap-2">
|
|
842
|
+
{([
|
|
843
|
+
['overview', 'Overview'],
|
|
844
|
+
['instructions', 'Instructions'],
|
|
845
|
+
['model', 'Model Setup'],
|
|
846
|
+
['tools', 'Tools'],
|
|
847
|
+
] as const).map(([sectionId, label]) => (
|
|
848
|
+
<button
|
|
849
|
+
key={sectionId}
|
|
850
|
+
type="button"
|
|
851
|
+
onClick={() => jumpToSection(sectionId)}
|
|
852
|
+
className="rounded-[10px] border border-white/[0.08] bg-transparent px-3 py-2 text-[12px] font-600 text-text-3 transition-all hover:bg-white/[0.04] hover:text-text-2"
|
|
853
|
+
style={{ fontFamily: 'inherit' }}
|
|
854
|
+
>
|
|
855
|
+
{label}
|
|
856
|
+
</button>
|
|
857
|
+
))}
|
|
858
|
+
</div>
|
|
859
|
+
</div>
|
|
860
|
+
|
|
861
|
+
<div ref={(node) => { sectionRefs.current.overview = node }}>
|
|
862
|
+
<SectionCard
|
|
863
|
+
title="Overview"
|
|
864
|
+
description="Basic identity, defaults, voice, heartbeat, and budget controls for this agent."
|
|
865
|
+
>
|
|
723
866
|
<div className="mb-8">
|
|
724
867
|
<SectionLabel>Name</SectionLabel>
|
|
725
868
|
<input type="text" value={name} onChange={(e) => setName(e.target.value)} placeholder="e.g. SEO Researcher" className={inputClass} style={{ fontFamily: 'inherit' }} />
|
|
@@ -756,7 +899,7 @@ export function AgentSheet() {
|
|
|
756
899
|
setAvatarSeed('')
|
|
757
900
|
toast.success('Avatar image uploaded')
|
|
758
901
|
}
|
|
759
|
-
} catch
|
|
902
|
+
} catch {
|
|
760
903
|
toast.error('Failed to upload image')
|
|
761
904
|
} finally {
|
|
762
905
|
setUploading(false)
|
|
@@ -920,6 +1063,28 @@ export function AgentSheet() {
|
|
|
920
1063
|
<p className="text-[11px] text-text-3/70 mt-1.5">Use working for fast recent context, durable for facts/preferences, and archive for long-lived history.</p>
|
|
921
1064
|
</div>
|
|
922
1065
|
|
|
1066
|
+
{/* Auto-Recovery */}
|
|
1067
|
+
<div className="mb-8">
|
|
1068
|
+
<div className="flex items-center justify-between mb-1.5">
|
|
1069
|
+
<label className="flex items-center gap-2 font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em]">
|
|
1070
|
+
Agent Availability
|
|
1071
|
+
<HintTip text="Disabled agents stay visible, but cannot take chats, heartbeats, schedules, or queued work until re-enabled." />
|
|
1072
|
+
</label>
|
|
1073
|
+
<div
|
|
1074
|
+
onClick={() => setDisabled(!disabled)}
|
|
1075
|
+
className={`w-9 h-5 rounded-full transition-all relative cursor-pointer ${disabled ? 'bg-amber-400' : 'bg-accent-bright'}`}
|
|
1076
|
+
>
|
|
1077
|
+
<div className={`absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all ${disabled ? 'left-0.5' : 'left-[18px]'}`} />
|
|
1078
|
+
</div>
|
|
1079
|
+
</div>
|
|
1080
|
+
<p className="text-[11px] text-text-3/70">
|
|
1081
|
+
{disabled
|
|
1082
|
+
? 'This agent is paused. Existing chats remain viewable, but new work is blocked until you switch it back on.'
|
|
1083
|
+
: 'This agent is active and can accept chats, heartbeats, schedules, and queued work.'}
|
|
1084
|
+
{' '}Gateway and remote runtime shutdown stay managed separately in Providers and OpenClaw Deploy.
|
|
1085
|
+
</p>
|
|
1086
|
+
</div>
|
|
1087
|
+
|
|
923
1088
|
{/* Auto-Recovery */}
|
|
924
1089
|
<div className="mb-8">
|
|
925
1090
|
<div className="flex items-center justify-between mb-1.5">
|
|
@@ -935,20 +1100,58 @@ export function AgentSheet() {
|
|
|
935
1100
|
</div>
|
|
936
1101
|
|
|
937
1102
|
{/* ElevenLabs Voice ID */}
|
|
938
|
-
{
|
|
1103
|
+
{voiceControlsAvailable && (
|
|
939
1104
|
<div className="mb-8">
|
|
940
|
-
<
|
|
941
|
-
|
|
942
|
-
|
|
1105
|
+
<div className="mb-3 flex flex-wrap items-start justify-between gap-3">
|
|
1106
|
+
<div>
|
|
1107
|
+
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em]">
|
|
1108
|
+
Voice & Audio <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
1109
|
+
</label>
|
|
1110
|
+
<p className="mt-1 text-[12px] leading-[1.6] text-text-3/70">
|
|
1111
|
+
Set an agent-specific ElevenLabs voice or inherit the global default configured in Settings.
|
|
1112
|
+
</p>
|
|
1113
|
+
</div>
|
|
1114
|
+
<div className={`rounded-[12px] border px-3 py-2 text-right ${
|
|
1115
|
+
agentVoiceId
|
|
1116
|
+
? 'border-accent-bright/25 bg-accent-soft/20'
|
|
1117
|
+
: 'border-white/[0.06] bg-white/[0.03]'
|
|
1118
|
+
}`}>
|
|
1119
|
+
<p className="text-[10px] font-700 uppercase tracking-[0.08em] text-text-3">
|
|
1120
|
+
{effectiveVoiceSource}
|
|
1121
|
+
</p>
|
|
1122
|
+
<p className="mt-1 max-w-[240px] truncate font-mono text-[12px] text-text-2">{effectiveVoiceId}</p>
|
|
1123
|
+
</div>
|
|
1124
|
+
</div>
|
|
943
1125
|
<input
|
|
944
1126
|
type="text"
|
|
945
1127
|
value={voiceId}
|
|
946
1128
|
onChange={(e) => setVoiceId(e.target.value)}
|
|
947
|
-
placeholder=
|
|
1129
|
+
placeholder={globalVoiceId ? `Leave blank to use ${globalVoiceId}` : 'Leave blank for the global default'}
|
|
948
1130
|
className={inputClass}
|
|
949
1131
|
style={{ fontFamily: 'inherit' }}
|
|
950
1132
|
/>
|
|
951
|
-
<
|
|
1133
|
+
<div className="mt-2 flex flex-wrap items-center gap-2">
|
|
1134
|
+
{agentVoiceId && (
|
|
1135
|
+
<button
|
|
1136
|
+
type="button"
|
|
1137
|
+
onClick={() => setVoiceId('')}
|
|
1138
|
+
className="rounded-[9px] border border-white/[0.08] bg-transparent px-2.5 py-1.5 text-[11px] font-600 text-text-3 transition-all hover:bg-white/[0.04] hover:text-text-2"
|
|
1139
|
+
style={{ fontFamily: 'inherit' }}
|
|
1140
|
+
>
|
|
1141
|
+
Use global default
|
|
1142
|
+
</button>
|
|
1143
|
+
)}
|
|
1144
|
+
{!voicePlaybackEnabled && (
|
|
1145
|
+
<span className="rounded-[9px] border border-amber-400/20 bg-amber-400/[0.08] px-2.5 py-1.5 text-[11px] font-600 text-amber-300">
|
|
1146
|
+
Voice playback is disabled globally
|
|
1147
|
+
</span>
|
|
1148
|
+
)}
|
|
1149
|
+
</div>
|
|
1150
|
+
<p className="text-[11px] text-text-3/70 mt-2">
|
|
1151
|
+
{globalVoiceId
|
|
1152
|
+
? `Global default: ${globalVoiceId}. This agent can override it with a different voice ID.`
|
|
1153
|
+
: 'No global default voice ID is set yet. If left blank, the built-in ElevenLabs fallback will be used.'}
|
|
1154
|
+
</p>
|
|
952
1155
|
</div>
|
|
953
1156
|
)}
|
|
954
1157
|
|
|
@@ -1107,27 +1310,27 @@ export function AgentSheet() {
|
|
|
1107
1310
|
: 'When a configured cap is exceeded, a warning is shown but runs continue.'}
|
|
1108
1311
|
</p>
|
|
1109
1312
|
</div>
|
|
1313
|
+
</SectionCard>
|
|
1314
|
+
</div>
|
|
1110
1315
|
|
|
1111
1316
|
{/* Wallet Section */}
|
|
1112
1317
|
{editingId && (
|
|
1113
1318
|
<WalletSection
|
|
1114
1319
|
agentId={editingId}
|
|
1115
|
-
|
|
1320
|
+
wallets={agentWallets}
|
|
1321
|
+
activeWalletId={editing?.activeWalletId || editing?.walletId || agentWallets.find((wallet) => wallet.isActive)?.id || null}
|
|
1116
1322
|
onWalletCreated={async () => {
|
|
1117
1323
|
await loadAgents()
|
|
1118
|
-
|
|
1119
|
-
try {
|
|
1120
|
-
const wallets = await api<Record<string, Omit<AgentWallet, 'encryptedPrivateKey'>>>('GET', '/wallets')
|
|
1121
|
-
const match = Object.values(wallets).find((w) => w.agentId === editingId)
|
|
1122
|
-
if (match) {
|
|
1123
|
-
const detail = await api<Omit<AgentWallet, 'encryptedPrivateKey'> & { balanceLamports?: number; balanceSol?: number }>('GET', `/wallets/${match.id}`)
|
|
1124
|
-
setAgentWallet(detail)
|
|
1125
|
-
}
|
|
1126
|
-
} catch { /* ignore */ }
|
|
1324
|
+
await loadAgentWallets(editingId)
|
|
1127
1325
|
}}
|
|
1128
1326
|
/>
|
|
1129
1327
|
)}
|
|
1130
1328
|
|
|
1329
|
+
<div ref={(node) => { sectionRefs.current.instructions = node }}>
|
|
1330
|
+
<SectionCard
|
|
1331
|
+
title="Instructions & Continuity"
|
|
1332
|
+
description="Define personality, system behavior, and long-running context this agent should preserve."
|
|
1333
|
+
>
|
|
1131
1334
|
{provider !== 'openclaw' && (
|
|
1132
1335
|
<div className="mb-8">
|
|
1133
1336
|
<label className="flex items-center gap-2 font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
@@ -1322,7 +1525,14 @@ export function AgentSheet() {
|
|
|
1322
1525
|
/>
|
|
1323
1526
|
</div>
|
|
1324
1527
|
</div>
|
|
1528
|
+
</SectionCard>
|
|
1529
|
+
</div>
|
|
1325
1530
|
|
|
1531
|
+
<div ref={(node) => { sectionRefs.current.model = node }}>
|
|
1532
|
+
<SectionCard
|
|
1533
|
+
title="Model Setup"
|
|
1534
|
+
description="Choose the provider, credentials, routing, and gateway preferences this agent should use."
|
|
1535
|
+
>
|
|
1326
1536
|
{/* OpenClaw Gateway Fields */}
|
|
1327
1537
|
{openclawEnabled && (
|
|
1328
1538
|
<div className="mb-8 space-y-5">
|
|
@@ -1411,13 +1621,13 @@ export function AgentSheet() {
|
|
|
1411
1621
|
onClick={async () => {
|
|
1412
1622
|
setSavingKey(true)
|
|
1413
1623
|
try {
|
|
1414
|
-
const cred = await api<
|
|
1624
|
+
const cred = await api<{ id: string }>('POST', '/credentials', { provider: 'openclaw', name: newKeyName.trim() || 'OpenClaw token', apiKey: newKeyValue.trim() })
|
|
1415
1625
|
await loadCredentials()
|
|
1416
1626
|
setCredentialId(cred.id)
|
|
1417
1627
|
setAddingKey(false)
|
|
1418
1628
|
setNewKeyName('')
|
|
1419
1629
|
setNewKeyValue('')
|
|
1420
|
-
} catch (err:
|
|
1630
|
+
} catch (err: unknown) { toast.error(`Failed to save: ${err instanceof Error ? err.message : String(err)}`) }
|
|
1421
1631
|
finally { setSavingKey(false) }
|
|
1422
1632
|
}}
|
|
1423
1633
|
className="px-4 py-1.5 rounded-[8px] bg-accent-bright text-white text-[12px] font-600 cursor-pointer border-none hover:brightness-110 transition-all disabled:opacity-40"
|
|
@@ -1670,13 +1880,13 @@ export function AgentSheet() {
|
|
|
1670
1880
|
onClick={async () => {
|
|
1671
1881
|
setSavingKey(true)
|
|
1672
1882
|
try {
|
|
1673
|
-
const cred = await api<
|
|
1883
|
+
const cred = await api<{ id: string }>('POST', '/credentials', { provider, name: newKeyName.trim() || `${provider} key`, apiKey: newKeyValue.trim() })
|
|
1674
1884
|
await loadCredentials()
|
|
1675
1885
|
setCredentialId(cred.id)
|
|
1676
1886
|
setAddingKey(false)
|
|
1677
1887
|
setNewKeyName('')
|
|
1678
1888
|
setNewKeyValue('')
|
|
1679
|
-
} catch (err:
|
|
1889
|
+
} catch (err: unknown) { toast.error(`Failed to save: ${err instanceof Error ? err.message : String(err)}`) }
|
|
1680
1890
|
finally { setSavingKey(false) }
|
|
1681
1891
|
}}
|
|
1682
1892
|
className="px-4 py-1.5 rounded-[8px] bg-accent-bright text-white text-[12px] font-600 cursor-pointer border-none hover:brightness-110 transition-all disabled:opacity-40"
|
|
@@ -1877,7 +2087,14 @@ export function AgentSheet() {
|
|
|
1877
2087
|
<p className="text-[11px] text-text-3/70 mt-2">No route pool yet. Add one if this agent should switch between cheaper, stronger, or gateway-specific models.</p>
|
|
1878
2088
|
)}
|
|
1879
2089
|
</div>
|
|
2090
|
+
</SectionCard>
|
|
2091
|
+
</div>
|
|
1880
2092
|
|
|
2093
|
+
<div ref={(node) => { sectionRefs.current.tools = node }}>
|
|
2094
|
+
<SectionCard
|
|
2095
|
+
title="Tools & Delegation"
|
|
2096
|
+
description="Enable plugins, skills, MCP tools, and delegation behavior for this agent."
|
|
2097
|
+
>
|
|
1881
2098
|
{/* Plugins — hidden for providers that manage capabilities outside LangGraph */}
|
|
1882
2099
|
{!hasNativeCapabilities && (
|
|
1883
2100
|
<div className="mb-8">
|
|
@@ -2024,7 +2241,7 @@ export function AgentSheet() {
|
|
|
2024
2241
|
</label>
|
|
2025
2242
|
<p className="text-[12px] text-text-3/60 mb-3">Connect external tool servers to this agent via MCP.</p>
|
|
2026
2243
|
<div className="flex flex-wrap gap-2">
|
|
2027
|
-
{Object.values(mcpServers).map((s
|
|
2244
|
+
{Object.values(mcpServers).map((s) => {
|
|
2028
2245
|
const active = mcpServerIds.includes(s.id)
|
|
2029
2246
|
return (
|
|
2030
2247
|
<button
|
|
@@ -2056,7 +2273,7 @@ export function AgentSheet() {
|
|
|
2056
2273
|
</p>
|
|
2057
2274
|
<div className="space-y-4">
|
|
2058
2275
|
{mcpServerIds.map((serverId) => {
|
|
2059
|
-
const server =
|
|
2276
|
+
const server = mcpServers[serverId]
|
|
2060
2277
|
const serverTools = mcpTools[serverId]
|
|
2061
2278
|
if (!server || !serverTools?.length) return null
|
|
2062
2279
|
const safeName = server.name.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
@@ -2121,6 +2338,8 @@ export function AgentSheet() {
|
|
|
2121
2338
|
/>
|
|
2122
2339
|
</div>
|
|
2123
2340
|
)}
|
|
2341
|
+
</SectionCard>
|
|
2342
|
+
</div>
|
|
2124
2343
|
|
|
2125
2344
|
{/* Provider key warning */}
|
|
2126
2345
|
{providerNeedsKey && (
|
|
@@ -4,6 +4,7 @@ import { DEFAULT_HEARTBEAT_INTERVAL_SEC } from '@/lib/heartbeat-defaults'
|
|
|
4
4
|
import { useCallback, useEffect, useState, type ReactNode } from 'react'
|
|
5
5
|
import type { Agent } from '@/types'
|
|
6
6
|
import { useAppStore } from '@/stores/use-app-store'
|
|
7
|
+
import { api } from '@/lib/api-client'
|
|
7
8
|
import { AgentAvatar } from './agent-avatar'
|
|
8
9
|
import { AgentFilesEditor } from './agent-files-editor'
|
|
9
10
|
import { OpenClawSkillsPanel } from './openclaw-skills-panel'
|
|
@@ -11,6 +12,7 @@ import { PermissionPresetSelector } from './permission-preset-selector'
|
|
|
11
12
|
import { ExecConfigPanel } from './exec-config-panel'
|
|
12
13
|
import { SandboxEnvPanel } from './sandbox-env-panel'
|
|
13
14
|
import { CronJobForm } from './cron-job-form'
|
|
15
|
+
import { toast } from 'sonner'
|
|
14
16
|
|
|
15
17
|
interface Props {
|
|
16
18
|
agent: Agent
|
|
@@ -87,6 +89,12 @@ export function InspectorPanel({ agent, onEditAgent, onClearHistory, onDeleteAge
|
|
|
87
89
|
<div className="min-w-0 flex-1">
|
|
88
90
|
<div className="flex items-center gap-2 min-w-0">
|
|
89
91
|
<h3 className="font-display text-[16px] font-700 text-text truncate tracking-[-0.02em]">{agent.name}</h3>
|
|
92
|
+
{agent.disabled === true && (
|
|
93
|
+
<span className="inline-flex items-center gap-1 rounded-[7px] border border-amber-400/15 bg-amber-400/[0.1] px-2 py-0.5 text-[10px] font-700 uppercase tracking-[0.12em] text-amber-300">
|
|
94
|
+
<span className="w-1.5 h-1.5 rounded-full bg-amber-300" />
|
|
95
|
+
Disabled
|
|
96
|
+
</span>
|
|
97
|
+
)}
|
|
90
98
|
{agent.heartbeatEnabled && (
|
|
91
99
|
<span className="inline-flex items-center gap-1 rounded-[7px] border border-emerald-400/15 bg-emerald-400/10 px-2 py-0.5 text-[10px] font-700 uppercase tracking-[0.12em] text-emerald-300">
|
|
92
100
|
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
|
@@ -189,13 +197,32 @@ interface OverviewTabProps {
|
|
|
189
197
|
}
|
|
190
198
|
|
|
191
199
|
function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDeleteChat, isMainChat }: OverviewTabProps) {
|
|
200
|
+
const loadAgents = useAppStore((s) => s.loadAgents)
|
|
201
|
+
const loadSessions = useAppStore((s) => s.loadSessions)
|
|
202
|
+
const [availabilitySaving, setAvailabilitySaving] = useState(false)
|
|
192
203
|
const summaryStats = [
|
|
193
204
|
{ label: 'Provider', value: PROVIDER_LABELS[agent.provider] || agent.provider.replace(/-/g, ' ') },
|
|
194
205
|
{ label: 'Model', value: agent.model || 'Default' },
|
|
195
206
|
{ label: 'Plugins', value: String(agent.plugins?.length ?? 0) },
|
|
196
207
|
{ label: 'Heartbeat', value: agent.heartbeatEnabled ? `Every ${agent.heartbeatIntervalSec ?? DEFAULT_HEARTBEAT_INTERVAL_SEC}s` : 'Off' },
|
|
208
|
+
{ label: 'Status', value: agent.disabled === true ? 'Disabled' : 'Enabled' },
|
|
197
209
|
]
|
|
198
210
|
|
|
211
|
+
const handleToggleAvailability = useCallback(async () => {
|
|
212
|
+
if (availabilitySaving) return
|
|
213
|
+
setAvailabilitySaving(true)
|
|
214
|
+
try {
|
|
215
|
+
const nextDisabled = agent.disabled !== true
|
|
216
|
+
await api('PUT', `/agents/${agent.id}`, { disabled: nextDisabled })
|
|
217
|
+
await Promise.all([loadAgents(), loadSessions()])
|
|
218
|
+
toast.success(nextDisabled ? `${agent.name} disabled` : `${agent.name} enabled`)
|
|
219
|
+
} catch (err: unknown) {
|
|
220
|
+
toast.error(err instanceof Error ? err.message : 'Failed to update agent availability')
|
|
221
|
+
} finally {
|
|
222
|
+
setAvailabilitySaving(false)
|
|
223
|
+
}
|
|
224
|
+
}, [agent.disabled, agent.id, agent.name, availabilitySaving, loadAgents, loadSessions])
|
|
225
|
+
|
|
199
226
|
return (
|
|
200
227
|
<div className="p-4 flex flex-col gap-4">
|
|
201
228
|
<div className={panelCardClass('p-4 bg-[linear-gradient(180deg,rgba(255,255,255,0.04),rgba(255,255,255,0.02))]')}>
|
|
@@ -259,6 +286,20 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
259
286
|
Edit Agent
|
|
260
287
|
</button>
|
|
261
288
|
)}
|
|
289
|
+
<button
|
|
290
|
+
onClick={() => void handleToggleAvailability()}
|
|
291
|
+
disabled={availabilitySaving}
|
|
292
|
+
className={`w-full px-3 py-2.5 rounded-[10px] text-[12px] font-700 border cursor-pointer transition-all text-left disabled:opacity-50 ${
|
|
293
|
+
agent.disabled === true
|
|
294
|
+
? 'text-emerald-300 bg-emerald-400/[0.06] border-emerald-400/[0.12] hover:bg-emerald-400/[0.1]'
|
|
295
|
+
: 'text-amber-300 bg-amber-400/[0.06] border-amber-400/[0.12] hover:bg-amber-400/[0.1]'
|
|
296
|
+
}`}
|
|
297
|
+
style={{ fontFamily: 'inherit' }}
|
|
298
|
+
>
|
|
299
|
+
{availabilitySaving
|
|
300
|
+
? (agent.disabled === true ? 'Enabling Agent...' : 'Disabling Agent...')
|
|
301
|
+
: (agent.disabled === true ? 'Enable Agent' : 'Disable Agent')}
|
|
302
|
+
</button>
|
|
262
303
|
{(onClearHistory || onDeleteAgent || onDeleteChat) && (
|
|
263
304
|
<>
|
|
264
305
|
<SectionLabel>Danger Zone</SectionLabel>
|