@swarmclawai/swarmclaw 0.6.7 → 0.7.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 +82 -39
- package/next.config.ts +31 -6
- package/package.json +3 -2
- package/src/app/api/agents/[id]/thread/route.ts +1 -0
- package/src/app/api/agents/route.ts +19 -5
- package/src/app/api/approvals/route.ts +22 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -0
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/eval/run/route.ts +37 -0
- package/src/app/api/eval/scenarios/route.ts +24 -0
- package/src/app/api/eval/suite/route.ts +29 -0
- package/src/app/api/mcp-servers/[id]/conformance/route.ts +26 -0
- package/src/app/api/mcp-servers/[id]/invoke/route.ts +81 -0
- package/src/app/api/memory/graph/route.ts +46 -0
- package/src/app/api/memory/route.ts +36 -5
- package/src/app/api/notifications/route.ts +3 -0
- package/src/app/api/plugins/install/route.ts +57 -5
- package/src/app/api/plugins/marketplace/route.ts +73 -22
- package/src/app/api/plugins/route.ts +61 -1
- package/src/app/api/plugins/ui/route.ts +34 -0
- package/src/app/api/sessions/[id]/checkpoints/route.ts +31 -0
- package/src/app/api/sessions/[id]/restore/route.ts +36 -0
- package/src/app/api/settings/route.ts +62 -0
- package/src/app/api/setup/doctor/route.ts +22 -5
- package/src/app/api/souls/[id]/route.ts +65 -0
- package/src/app/api/souls/route.ts +70 -0
- package/src/app/api/tasks/[id]/approve/route.ts +4 -3
- package/src/app/api/tasks/[id]/route.ts +16 -3
- package/src/app/api/tasks/route.ts +10 -2
- package/src/app/api/usage/route.ts +9 -2
- package/src/app/globals.css +27 -0
- package/src/app/page.tsx +10 -5
- package/src/cli/index.js +37 -0
- package/src/components/activity/activity-feed.tsx +9 -2
- package/src/components/agents/agent-avatar.tsx +5 -1
- package/src/components/agents/agent-card.tsx +55 -9
- package/src/components/agents/agent-sheet.tsx +112 -34
- package/src/components/agents/inspector-panel.tsx +1 -1
- package/src/components/agents/soul-library-picker.tsx +84 -13
- package/src/components/auth/access-key-gate.tsx +63 -54
- package/src/components/auth/user-picker.tsx +37 -32
- package/src/components/chat/activity-moment.tsx +2 -0
- package/src/components/chat/chat-area.tsx +11 -0
- package/src/components/chat/chat-header.tsx +69 -25
- package/src/components/chat/chat-tool-toggles.tsx +2 -2
- package/src/components/chat/checkpoint-timeline.tsx +112 -0
- package/src/components/chat/code-block.tsx +3 -1
- package/src/components/chat/exec-approval-card.tsx +8 -1
- package/src/components/chat/message-bubble.tsx +164 -4
- package/src/components/chat/message-list.tsx +46 -4
- package/src/components/chat/session-approval-card.tsx +80 -0
- package/src/components/chat/session-debug-panel.tsx +106 -84
- package/src/components/chat/streaming-bubble.tsx +6 -5
- package/src/components/chat/task-approval-card.tsx +78 -0
- package/src/components/chat/thinking-indicator.tsx +48 -12
- package/src/components/chat/tool-call-bubble.tsx +3 -0
- package/src/components/chat/tool-request-banner.tsx +39 -20
- package/src/components/chatrooms/chatroom-list.tsx +11 -4
- package/src/components/chatrooms/chatroom-sheet.tsx +7 -2
- package/src/components/connectors/connector-list.tsx +33 -11
- package/src/components/connectors/connector-sheet.tsx +37 -7
- package/src/components/home/home-view.tsx +54 -24
- package/src/components/input/chat-input.tsx +22 -1
- package/src/components/knowledge/knowledge-list.tsx +17 -18
- package/src/components/knowledge/knowledge-sheet.tsx +9 -5
- package/src/components/layout/app-layout.tsx +87 -19
- package/src/components/mcp-servers/mcp-server-list.tsx +352 -50
- package/src/components/mcp-servers/mcp-server-sheet.tsx +25 -9
- package/src/components/memory/memory-browser.tsx +73 -45
- package/src/components/memory/memory-graph-view.tsx +203 -0
- package/src/components/memory/memory-list.tsx +20 -13
- package/src/components/plugins/plugin-list.tsx +214 -60
- package/src/components/plugins/plugin-sheet.tsx +119 -24
- package/src/components/projects/project-list.tsx +17 -9
- package/src/components/providers/provider-list.tsx +21 -6
- package/src/components/providers/provider-sheet.tsx +42 -25
- package/src/components/runs/run-list.tsx +17 -13
- package/src/components/schedules/schedule-card.tsx +10 -3
- package/src/components/schedules/schedule-list.tsx +2 -2
- package/src/components/schedules/schedule-sheet.tsx +28 -9
- package/src/components/secrets/secret-sheet.tsx +7 -2
- package/src/components/secrets/secrets-list.tsx +18 -5
- package/src/components/sessions/new-session-sheet.tsx +183 -376
- package/src/components/sessions/session-card.tsx +10 -2
- package/src/components/settings/gateway-connection-panel.tsx +9 -8
- package/src/components/shared/command-palette.tsx +13 -5
- package/src/components/shared/empty-state.tsx +20 -8
- package/src/components/shared/hint-tip.tsx +31 -0
- package/src/components/shared/notification-center.tsx +134 -86
- package/src/components/shared/profile-sheet.tsx +4 -0
- package/src/components/shared/settings/plugin-manager.tsx +360 -135
- package/src/components/shared/settings/section-capability-policy.tsx +3 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +149 -4
- package/src/components/skills/clawhub-browser.tsx +1 -0
- package/src/components/skills/skill-list.tsx +31 -12
- package/src/components/skills/skill-sheet.tsx +20 -7
- package/src/components/tasks/approvals-panel.tsx +224 -0
- package/src/components/tasks/task-board.tsx +20 -12
- package/src/components/tasks/task-card.tsx +21 -7
- package/src/components/tasks/task-column.tsx +4 -3
- package/src/components/tasks/task-list.tsx +1 -1
- package/src/components/tasks/task-sheet.tsx +130 -1
- package/src/components/ui/dialog.tsx +1 -0
- package/src/components/ui/sheet.tsx +1 -0
- package/src/components/usage/metrics-dashboard.tsx +72 -48
- package/src/components/wallets/wallet-panel.tsx +65 -41
- package/src/components/wallets/wallet-section.tsx +9 -3
- package/src/components/webhooks/webhook-list.tsx +21 -12
- package/src/components/webhooks/webhook-sheet.tsx +13 -3
- package/src/lib/approval-display.test.ts +45 -0
- package/src/lib/approval-display.ts +62 -0
- package/src/lib/clipboard.ts +38 -0
- package/src/lib/memory.ts +8 -0
- package/src/lib/providers/claude-cli.ts +5 -3
- package/src/lib/providers/index.ts +67 -21
- package/src/lib/runtime-loop.ts +3 -2
- package/src/lib/server/approvals.ts +150 -0
- package/src/lib/server/chat-execution.ts +319 -74
- package/src/lib/server/chatroom-helpers.ts +63 -5
- package/src/lib/server/chatroom-orchestration.ts +74 -0
- package/src/lib/server/clawhub-client.ts +82 -6
- package/src/lib/server/connectors/manager.ts +27 -1
- package/src/lib/server/context-manager.ts +132 -50
- package/src/lib/server/cost.test.ts +73 -0
- package/src/lib/server/cost.ts +165 -34
- package/src/lib/server/daemon-state.ts +112 -1
- package/src/lib/server/data-dir.ts +18 -1
- package/src/lib/server/eval/runner.ts +126 -0
- package/src/lib/server/eval/scenarios.ts +218 -0
- package/src/lib/server/eval/scorer.ts +96 -0
- package/src/lib/server/eval/store.ts +37 -0
- package/src/lib/server/eval/types.ts +48 -0
- package/src/lib/server/execution-log.ts +12 -8
- package/src/lib/server/guardian.ts +34 -0
- package/src/lib/server/heartbeat-service.ts +53 -1
- package/src/lib/server/integrity-monitor.ts +208 -0
- package/src/lib/server/langgraph-checkpoint.ts +10 -0
- package/src/lib/server/link-understanding.ts +55 -0
- package/src/lib/server/llm-response-cache.test.ts +102 -0
- package/src/lib/server/llm-response-cache.ts +227 -0
- package/src/lib/server/main-agent-loop.ts +115 -16
- package/src/lib/server/main-session.ts +6 -3
- package/src/lib/server/mcp-conformance.test.ts +18 -0
- package/src/lib/server/mcp-conformance.ts +233 -0
- package/src/lib/server/memory-db.ts +193 -19
- package/src/lib/server/memory-retrieval.test.ts +56 -0
- package/src/lib/server/mmr.ts +73 -0
- package/src/lib/server/orchestrator-lg.ts +7 -1
- package/src/lib/server/orchestrator.ts +4 -3
- package/src/lib/server/plugins.ts +662 -132
- package/src/lib/server/process-manager.ts +18 -0
- package/src/lib/server/query-expansion.ts +57 -0
- package/src/lib/server/queue.ts +280 -11
- package/src/lib/server/runtime-settings.ts +9 -0
- package/src/lib/server/session-run-manager.test.ts +23 -0
- package/src/lib/server/session-run-manager.ts +32 -2
- package/src/lib/server/session-tools/canvas.ts +85 -50
- package/src/lib/server/session-tools/chatroom.ts +130 -127
- package/src/lib/server/session-tools/connector.ts +233 -454
- package/src/lib/server/session-tools/context-mgmt.ts +87 -105
- package/src/lib/server/session-tools/crud.ts +84 -7
- package/src/lib/server/session-tools/delegate.ts +351 -752
- package/src/lib/server/session-tools/discovery.ts +198 -0
- package/src/lib/server/session-tools/edit_file.ts +82 -0
- package/src/lib/server/session-tools/file-send.test.ts +39 -0
- package/src/lib/server/session-tools/file.ts +257 -425
- package/src/lib/server/session-tools/git.ts +87 -47
- package/src/lib/server/session-tools/http.ts +95 -33
- package/src/lib/server/session-tools/index.ts +217 -138
- package/src/lib/server/session-tools/memory.ts +154 -239
- package/src/lib/server/session-tools/monitor.ts +126 -0
- package/src/lib/server/session-tools/normalize-tool-args.test.ts +61 -0
- package/src/lib/server/session-tools/normalize-tool-args.ts +48 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +82 -99
- package/src/lib/server/session-tools/openclaw-workspace.ts +103 -93
- package/src/lib/server/session-tools/platform.ts +86 -0
- package/src/lib/server/session-tools/plugin-creator.ts +239 -0
- package/src/lib/server/session-tools/sample-ui.ts +97 -0
- package/src/lib/server/session-tools/sandbox.ts +175 -148
- package/src/lib/server/session-tools/schedule.ts +78 -0
- package/src/lib/server/session-tools/session-info.ts +104 -410
- package/src/lib/server/session-tools/shell-normalize.test.ts +43 -0
- package/src/lib/server/session-tools/shell.ts +171 -143
- package/src/lib/server/session-tools/subagent.ts +77 -77
- package/src/lib/server/session-tools/wallet.ts +182 -106
- package/src/lib/server/session-tools/web.ts +181 -327
- package/src/lib/server/storage.ts +36 -0
- package/src/lib/server/stream-agent-chat.ts +348 -242
- package/src/lib/server/task-quality-gate.test.ts +44 -0
- package/src/lib/server/task-quality-gate.ts +67 -0
- package/src/lib/server/task-validation.test.ts +78 -0
- package/src/lib/server/task-validation.ts +67 -2
- package/src/lib/server/tool-aliases.ts +68 -0
- package/src/lib/server/tool-capability-policy.ts +24 -5
- package/src/lib/server/tool-retry.ts +62 -0
- package/src/lib/server/transcript-repair.ts +72 -0
- package/src/lib/setup-defaults.ts +1 -0
- package/src/lib/tasks.ts +7 -1
- package/src/lib/tool-definitions.ts +24 -23
- package/src/lib/validation/schemas.ts +13 -0
- package/src/lib/view-routes.ts +2 -23
- package/src/stores/use-app-store.ts +23 -1
- package/src/types/index.ts +155 -10
|
@@ -14,8 +14,10 @@ import { NATIVE_CAPABILITY_PROVIDER_IDS, NON_LANGGRAPH_PROVIDER_IDS } from '@/li
|
|
|
14
14
|
import { AgentAvatar } from './agent-avatar'
|
|
15
15
|
import { AgentPickerList } from '@/components/shared/agent-picker-list'
|
|
16
16
|
import { randomSoul } from '@/lib/soul-suggestions'
|
|
17
|
+
import { copyTextToClipboard } from '@/lib/clipboard'
|
|
17
18
|
import { SectionLabel } from '@/components/shared/section-label'
|
|
18
19
|
import { SoulLibraryPicker } from './soul-library-picker'
|
|
20
|
+
import { HintTip } from '@/components/shared/hint-tip'
|
|
19
21
|
|
|
20
22
|
const HB_PRESETS = [1800, 3600, 7200, 21600, 43200] as const
|
|
21
23
|
|
|
@@ -109,12 +111,15 @@ export function AgentSheet() {
|
|
|
109
111
|
const [avatarUrl, setAvatarUrl] = useState<string | null>(null)
|
|
110
112
|
const [uploading, setUploading] = useState(false)
|
|
111
113
|
const [thinkingLevel, setThinkingLevel] = useState<'' | 'minimal' | 'low' | 'medium' | 'high'>('')
|
|
114
|
+
const [autoRecovery, setAutoRecovery] = useState(false)
|
|
112
115
|
const [voiceId, setVoiceId] = useState('')
|
|
113
116
|
const [heartbeatEnabled, setHeartbeatEnabled] = useState(false)
|
|
114
117
|
const [heartbeatIntervalSec, setHeartbeatIntervalSec] = useState('') // '' = default (30m)
|
|
115
118
|
const [heartbeatModel, setHeartbeatModel] = useState('')
|
|
116
119
|
const [heartbeatPrompt, setHeartbeatPrompt] = useState('')
|
|
117
120
|
const [budgetEnabled, setBudgetEnabled] = useState(false)
|
|
121
|
+
const [hourlyBudget, setHourlyBudget] = useState('')
|
|
122
|
+
const [dailyBudget, setDailyBudget] = useState('')
|
|
118
123
|
const [monthlyBudget, setMonthlyBudget] = useState('')
|
|
119
124
|
const [budgetAction, setBudgetAction] = useState<'warn' | 'block'>('warn')
|
|
120
125
|
const [agentWallet, setAgentWallet] = useState<(Omit<AgentWallet, 'encryptedPrivateKey'> & { balanceLamports?: number; balanceSol?: number }) | null>(null)
|
|
@@ -193,12 +198,19 @@ export function AgentSheet() {
|
|
|
193
198
|
setAvatarSeed(editing.avatarSeed || crypto.randomUUID().slice(0, 8))
|
|
194
199
|
setAvatarUrl(editing.avatarUrl || null)
|
|
195
200
|
setThinkingLevel(editing.thinkingLevel || '')
|
|
201
|
+
setAutoRecovery(editing.autoRecovery || false)
|
|
196
202
|
setVoiceId(editing.elevenLabsVoiceId || '')
|
|
197
203
|
setHeartbeatEnabled(editing.heartbeatEnabled || false)
|
|
198
204
|
setHeartbeatIntervalSec(parseDurationToSec(editing.heartbeatInterval, editing.heartbeatIntervalSec))
|
|
199
205
|
setHeartbeatModel(editing.heartbeatModel || '')
|
|
200
206
|
setHeartbeatPrompt(editing.heartbeatPrompt || '')
|
|
201
|
-
setBudgetEnabled(
|
|
207
|
+
setBudgetEnabled(
|
|
208
|
+
(typeof editing.hourlyBudget === 'number' && editing.hourlyBudget > 0)
|
|
209
|
+
|| (typeof editing.dailyBudget === 'number' && editing.dailyBudget > 0)
|
|
210
|
+
|| (typeof editing.monthlyBudget === 'number' && editing.monthlyBudget > 0),
|
|
211
|
+
)
|
|
212
|
+
setHourlyBudget(typeof editing.hourlyBudget === 'number' && editing.hourlyBudget > 0 ? String(editing.hourlyBudget) : '')
|
|
213
|
+
setDailyBudget(typeof editing.dailyBudget === 'number' && editing.dailyBudget > 0 ? String(editing.dailyBudget) : '')
|
|
202
214
|
setMonthlyBudget(typeof editing.monthlyBudget === 'number' && editing.monthlyBudget > 0 ? String(editing.monthlyBudget) : '')
|
|
203
215
|
setBudgetAction(editing.budgetAction || 'warn')
|
|
204
216
|
// Load wallet if agent has one
|
|
@@ -235,12 +247,15 @@ export function AgentSheet() {
|
|
|
235
247
|
setProjectId(undefined)
|
|
236
248
|
setAvatarSeed('')
|
|
237
249
|
setThinkingLevel('')
|
|
250
|
+
setAutoRecovery(false)
|
|
238
251
|
setVoiceId('')
|
|
239
252
|
setHeartbeatEnabled(false)
|
|
240
253
|
setHeartbeatIntervalSec('')
|
|
241
254
|
setHeartbeatModel('')
|
|
242
255
|
setHeartbeatPrompt('')
|
|
243
256
|
setBudgetEnabled(false)
|
|
257
|
+
setHourlyBudget('')
|
|
258
|
+
setDailyBudget('')
|
|
244
259
|
setMonthlyBudget('')
|
|
245
260
|
setBudgetAction('warn')
|
|
246
261
|
}
|
|
@@ -311,6 +326,9 @@ export function AgentSheet() {
|
|
|
311
326
|
const url = normalizedEndpoint.trim().replace(/\/+$/, '')
|
|
312
327
|
normalizedEndpoint = /^(https?|wss?):\/\//i.test(url) ? url : `http://${url}`
|
|
313
328
|
}
|
|
329
|
+
const parsedHourlyBudget = budgetEnabled && hourlyBudget ? Number(hourlyBudget) : null
|
|
330
|
+
const parsedDailyBudget = budgetEnabled && dailyBudget ? Number(dailyBudget) : null
|
|
331
|
+
const parsedMonthlyBudget = budgetEnabled && monthlyBudget ? Number(monthlyBudget) : null
|
|
314
332
|
const data = {
|
|
315
333
|
name: name.trim() || 'Unnamed Agent',
|
|
316
334
|
description,
|
|
@@ -334,13 +352,16 @@ export function AgentSheet() {
|
|
|
334
352
|
avatarSeed: avatarSeed.trim() || undefined,
|
|
335
353
|
avatarUrl: avatarUrl || null,
|
|
336
354
|
thinkingLevel: thinkingLevel || undefined,
|
|
355
|
+
autoRecovery,
|
|
337
356
|
elevenLabsVoiceId: voiceId.trim() || null,
|
|
338
357
|
heartbeatEnabled,
|
|
339
358
|
heartbeatInterval: heartbeatIntervalSec ? formatHbDuration(Number(heartbeatIntervalSec)) : null,
|
|
340
359
|
heartbeatIntervalSec: heartbeatIntervalSec ? Number(heartbeatIntervalSec) : null,
|
|
341
360
|
heartbeatModel: heartbeatModel.trim() || null,
|
|
342
361
|
heartbeatPrompt: heartbeatPrompt.trim() || null,
|
|
343
|
-
|
|
362
|
+
hourlyBudget: parsedHourlyBudget && parsedHourlyBudget > 0 ? parsedHourlyBudget : null,
|
|
363
|
+
dailyBudget: parsedDailyBudget && parsedDailyBudget > 0 ? parsedDailyBudget : null,
|
|
364
|
+
monthlyBudget: parsedMonthlyBudget && parsedMonthlyBudget > 0 ? parsedMonthlyBudget : null,
|
|
344
365
|
budgetAction: budgetEnabled ? budgetAction : undefined,
|
|
345
366
|
}
|
|
346
367
|
if (editing) {
|
|
@@ -420,11 +441,14 @@ export function AgentSheet() {
|
|
|
420
441
|
setTestStatus('fail')
|
|
421
442
|
setTestMessage(result.message)
|
|
422
443
|
setTestErrorCode(result.errorCode || null)
|
|
444
|
+
toast.error(result.message || 'Connection test failed')
|
|
423
445
|
return false
|
|
424
446
|
}
|
|
425
447
|
} catch (err: unknown) {
|
|
448
|
+
const msg = err instanceof Error ? err.message : 'Connection test failed'
|
|
426
449
|
setTestStatus('fail')
|
|
427
|
-
setTestMessage(
|
|
450
|
+
setTestMessage(msg)
|
|
451
|
+
toast.error(msg)
|
|
428
452
|
return false
|
|
429
453
|
}
|
|
430
454
|
}
|
|
@@ -532,7 +556,10 @@ export function AgentSheet() {
|
|
|
532
556
|
if (data.url) {
|
|
533
557
|
setAvatarUrl(data.url)
|
|
534
558
|
setAvatarSeed('')
|
|
559
|
+
toast.success('Avatar image uploaded')
|
|
535
560
|
}
|
|
561
|
+
} catch (err: unknown) {
|
|
562
|
+
toast.error('Failed to upload image')
|
|
536
563
|
} finally {
|
|
537
564
|
setUploading(false)
|
|
538
565
|
e.target.value = ''
|
|
@@ -653,8 +680,9 @@ export function AgentSheet() {
|
|
|
653
680
|
|
|
654
681
|
{/* Thinking Level */}
|
|
655
682
|
<div className="mb-8">
|
|
656
|
-
<label className="
|
|
683
|
+
<label className="flex items-center gap-2 font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
657
684
|
Thinking Level <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
685
|
+
<HintTip text="Higher levels produce more thoughtful responses but cost more tokens" />
|
|
658
686
|
</label>
|
|
659
687
|
<select
|
|
660
688
|
value={thinkingLevel}
|
|
@@ -671,6 +699,20 @@ export function AgentSheet() {
|
|
|
671
699
|
<p className="text-[11px] text-text-3/70 mt-1.5">Controls reasoning depth. Anthropic models use extended thinking; OpenAI o-series uses reasoning_effort. Others get system prompt guidance.</p>
|
|
672
700
|
</div>
|
|
673
701
|
|
|
702
|
+
{/* Auto-Recovery */}
|
|
703
|
+
<div className="mb-8">
|
|
704
|
+
<div className="flex items-center justify-between mb-1.5">
|
|
705
|
+
<label className="flex items-center gap-2 font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em]">Guardian Auto-Recovery <HintTip text="Automatically resets the agent's workspace if it gets into a broken state" /></label>
|
|
706
|
+
<div
|
|
707
|
+
onClick={() => setAutoRecovery(!autoRecovery)}
|
|
708
|
+
className={`w-9 h-5 rounded-full transition-all relative cursor-pointer ${autoRecovery ? 'bg-accent-bright' : 'bg-white/[0.08]'}`}
|
|
709
|
+
>
|
|
710
|
+
<div className={`absolute top-0.5 w-4 h-4 rounded-full bg-white transition-all ${autoRecovery ? 'left-[18px]' : 'left-0.5'}`} />
|
|
711
|
+
</div>
|
|
712
|
+
</div>
|
|
713
|
+
<p className="text-[11px] text-text-3/70">If this agent critically fails a task that modifies the workspace, SwarmClaw Guardian will automatically perform a <code className="text-[10px] bg-white/[0.05] px-1 rounded">git reset --hard</code> to restore the last known good state.</p>
|
|
714
|
+
</div>
|
|
715
|
+
|
|
674
716
|
{/* ElevenLabs Voice ID */}
|
|
675
717
|
{appSettings.elevenLabsEnabled && (
|
|
676
718
|
<div className="mb-8">
|
|
@@ -692,7 +734,7 @@ export function AgentSheet() {
|
|
|
692
734
|
{/* Heartbeat Configuration */}
|
|
693
735
|
<div className="mb-8">
|
|
694
736
|
<div className="flex items-center justify-between mb-3">
|
|
695
|
-
<label className="
|
|
737
|
+
<label className="flex items-center gap-2 font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em]">Heartbeat <HintTip text="Periodically runs a background prompt to keep the agent active and aware" /></label>
|
|
696
738
|
<button
|
|
697
739
|
type="button"
|
|
698
740
|
onClick={() => setHeartbeatEnabled(!heartbeatEnabled)}
|
|
@@ -704,7 +746,7 @@ export function AgentSheet() {
|
|
|
704
746
|
{heartbeatEnabled && (
|
|
705
747
|
<div className="space-y-4 mt-3">
|
|
706
748
|
<div>
|
|
707
|
-
<label className="
|
|
749
|
+
<label className="flex items-center gap-1.5 text-[12px] text-text-3/70 mb-1.5">Interval <HintTip text="Minutes between each heartbeat check" /></label>
|
|
708
750
|
<select
|
|
709
751
|
value={heartbeatIntervalSec}
|
|
710
752
|
onChange={(e) => setHeartbeatIntervalSec(e.target.value)}
|
|
@@ -743,10 +785,10 @@ export function AgentSheet() {
|
|
|
743
785
|
<p className="text-[11px] text-text-3/70 mt-1.5">Periodic check-in runs on idle sessions using this agent. Processes pending events and monitors status.</p>
|
|
744
786
|
</div>
|
|
745
787
|
|
|
746
|
-
{/*
|
|
788
|
+
{/* Spend Limits */}
|
|
747
789
|
<div className="mb-8">
|
|
748
790
|
<div className="flex items-center justify-between mb-3">
|
|
749
|
-
<label className="
|
|
791
|
+
<label className="flex items-center gap-2 font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em]">Spend Limits <HintTip text="Set hourly, daily, and monthly API spend limits for this agent" /></label>
|
|
750
792
|
<button
|
|
751
793
|
type="button"
|
|
752
794
|
onClick={() => setBudgetEnabled(!budgetEnabled)}
|
|
@@ -757,24 +799,58 @@ export function AgentSheet() {
|
|
|
757
799
|
</div>
|
|
758
800
|
{budgetEnabled && (
|
|
759
801
|
<div className="space-y-4 mt-3">
|
|
760
|
-
<div>
|
|
761
|
-
<
|
|
762
|
-
|
|
763
|
-
<
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
802
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
|
|
803
|
+
<div>
|
|
804
|
+
<label className="block text-[12px] text-text-3/70 mb-1.5">Hourly cap (USD)</label>
|
|
805
|
+
<div className="relative">
|
|
806
|
+
<span className="absolute left-3.5 top-1/2 -translate-y-1/2 text-text-3/50 text-[14px]">$</span>
|
|
807
|
+
<input
|
|
808
|
+
type="number"
|
|
809
|
+
min="0.01"
|
|
810
|
+
step="0.01"
|
|
811
|
+
value={hourlyBudget}
|
|
812
|
+
onChange={(e) => setHourlyBudget(e.target.value)}
|
|
813
|
+
placeholder="0.50"
|
|
814
|
+
className={`${inputClass} pl-7`}
|
|
815
|
+
style={{ fontFamily: 'inherit' }}
|
|
816
|
+
/>
|
|
817
|
+
</div>
|
|
818
|
+
</div>
|
|
819
|
+
<div>
|
|
820
|
+
<label className="block text-[12px] text-text-3/70 mb-1.5">Daily cap (USD)</label>
|
|
821
|
+
<div className="relative">
|
|
822
|
+
<span className="absolute left-3.5 top-1/2 -translate-y-1/2 text-text-3/50 text-[14px]">$</span>
|
|
823
|
+
<input
|
|
824
|
+
type="number"
|
|
825
|
+
min="0.01"
|
|
826
|
+
step="0.01"
|
|
827
|
+
value={dailyBudget}
|
|
828
|
+
onChange={(e) => setDailyBudget(e.target.value)}
|
|
829
|
+
placeholder="5.00"
|
|
830
|
+
className={`${inputClass} pl-7`}
|
|
831
|
+
style={{ fontFamily: 'inherit' }}
|
|
832
|
+
/>
|
|
833
|
+
</div>
|
|
834
|
+
</div>
|
|
835
|
+
<div>
|
|
836
|
+
<label className="block text-[12px] text-text-3/70 mb-1.5">Monthly cap (USD)</label>
|
|
837
|
+
<div className="relative">
|
|
838
|
+
<span className="absolute left-3.5 top-1/2 -translate-y-1/2 text-text-3/50 text-[14px]">$</span>
|
|
839
|
+
<input
|
|
840
|
+
type="number"
|
|
841
|
+
min="0.01"
|
|
842
|
+
step="0.01"
|
|
843
|
+
value={monthlyBudget}
|
|
844
|
+
onChange={(e) => setMonthlyBudget(e.target.value)}
|
|
845
|
+
placeholder="20.00"
|
|
846
|
+
className={`${inputClass} pl-7`}
|
|
847
|
+
style={{ fontFamily: 'inherit' }}
|
|
848
|
+
/>
|
|
849
|
+
</div>
|
|
774
850
|
</div>
|
|
775
851
|
</div>
|
|
776
852
|
<div>
|
|
777
|
-
<label className="
|
|
853
|
+
<label className="flex items-center gap-1.5 text-[12px] text-text-3/70 mb-1.5">When exceeded <HintTip text="Warn shows an alert but keeps running; Block stops the agent from making API calls" /></label>
|
|
778
854
|
<div className="flex gap-2">
|
|
779
855
|
<button
|
|
780
856
|
type="button"
|
|
@@ -806,8 +882,8 @@ export function AgentSheet() {
|
|
|
806
882
|
)}
|
|
807
883
|
<p className="text-[11px] text-text-3/70 mt-1.5">
|
|
808
884
|
{budgetAction === 'block'
|
|
809
|
-
? '
|
|
810
|
-
: '
|
|
885
|
+
? 'When any configured cap is exceeded, runs are blocked until spend drops below that cap window.'
|
|
886
|
+
: 'When a configured cap is exceeded, a warning is shown but runs continue.'}
|
|
811
887
|
</p>
|
|
812
888
|
</div>
|
|
813
889
|
|
|
@@ -835,6 +911,7 @@ export function AgentSheet() {
|
|
|
835
911
|
<div className="mb-8">
|
|
836
912
|
<label className="flex items-center gap-2 font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
837
913
|
Soul / Personality <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
914
|
+
<HintTip text="The agent's voice and tone — how it talks, not what it knows" />
|
|
838
915
|
{soul !== soulInitial && soulSaveState === 'idle' && (
|
|
839
916
|
<span className="inline-flex items-center gap-1 normal-case tracking-normal text-[10px] text-amber-400 font-600">
|
|
840
917
|
<span className="w-1.5 h-1.5 rounded-full bg-amber-400" />
|
|
@@ -889,7 +966,7 @@ export function AgentSheet() {
|
|
|
889
966
|
{provider !== 'openclaw' && (
|
|
890
967
|
<div className="mb-8">
|
|
891
968
|
<div className="flex items-center gap-2 mb-3">
|
|
892
|
-
<label className="
|
|
969
|
+
<label className="flex items-center gap-2 font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em]">System Prompt <HintTip text="Instructions that tell the agent what it can do, what tools to use, and how to behave" /></label>
|
|
893
970
|
<button onClick={() => promptFileRef.current?.click()} className="shrink-0 px-2 py-1 rounded-[8px] border border-white/[0.08] bg-surface text-[11px] text-text-3 hover:text-text-2 cursor-pointer transition-colors" style={{ fontFamily: 'inherit' }}>Upload .md</button>
|
|
894
971
|
<input ref={promptFileRef} type="file" accept=".md,.txt,.markdown" onChange={handleFileUpload(setSystemPrompt)} className="hidden" />
|
|
895
972
|
</div>
|
|
@@ -1080,9 +1157,11 @@ export function AgentSheet() {
|
|
|
1080
1157
|
<button
|
|
1081
1158
|
type="button"
|
|
1082
1159
|
onClick={() => {
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1160
|
+
void copyTextToClipboard((testDeviceId || openclawDeviceId)!).then((copiedId) => {
|
|
1161
|
+
if (!copiedId) return
|
|
1162
|
+
setConfigCopied(true)
|
|
1163
|
+
setTimeout(() => setConfigCopied(false), 2000)
|
|
1164
|
+
})
|
|
1086
1165
|
}}
|
|
1087
1166
|
className="text-[12px] text-text-3/60 hover:text-text-3/80 transition-colors cursor-pointer bg-transparent border-none"
|
|
1088
1167
|
>
|
|
@@ -1286,11 +1365,11 @@ export function AgentSheet() {
|
|
|
1286
1365
|
</div>
|
|
1287
1366
|
)}
|
|
1288
1367
|
|
|
1289
|
-
{/*
|
|
1368
|
+
{/* Plugins — hidden for providers that manage capabilities outside LangGraph */}
|
|
1290
1369
|
{!hasNativeCapabilities && (
|
|
1291
1370
|
<div className="mb-8">
|
|
1292
|
-
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">
|
|
1293
|
-
<p className="text-[12px] text-text-3/60 mb-3">Enable
|
|
1371
|
+
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">Plugins</label>
|
|
1372
|
+
<p className="text-[12px] text-text-3/60 mb-3">Enable capabilities and plugins for this agent.</p>
|
|
1294
1373
|
<div className="space-y-3">
|
|
1295
1374
|
{AVAILABLE_TOOLS.map((t) => (
|
|
1296
1375
|
<label key={t.id} className="flex items-center gap-3 cursor-pointer">
|
|
@@ -1313,7 +1392,7 @@ export function AgentSheet() {
|
|
|
1313
1392
|
{/* Platform — hidden for providers that manage capabilities outside LangGraph */}
|
|
1314
1393
|
{!hasNativeCapabilities && (
|
|
1315
1394
|
<div className="mb-8">
|
|
1316
|
-
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">Platform</label>
|
|
1395
|
+
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-2">Platform Plugins</label>
|
|
1317
1396
|
<p className="text-[12px] text-text-3/60 mb-3">Allow this agent to manage platform resources directly.</p>
|
|
1318
1397
|
<div className="space-y-3">
|
|
1319
1398
|
{PLATFORM_TOOLS.map((t) => (
|
|
@@ -1601,4 +1680,3 @@ export function AgentSheet() {
|
|
|
1601
1680
|
</>
|
|
1602
1681
|
)
|
|
1603
1682
|
}
|
|
1604
|
-
|
|
@@ -173,7 +173,7 @@ function OverviewTab({ agent, onEditAgent, onClearHistory, onDeleteAgent, onDele
|
|
|
173
173
|
)}
|
|
174
174
|
{agent.tools && agent.tools.length > 0 && (
|
|
175
175
|
<div>
|
|
176
|
-
<label className="block text-[11px] font-600 uppercase tracking-wider text-text-3/50 mb-1">
|
|
176
|
+
<label className="block text-[11px] font-600 uppercase tracking-wider text-text-3/50 mb-1">Plugins</label>
|
|
177
177
|
<div className="flex flex-wrap gap-1">
|
|
178
178
|
{agent.tools.map((tool) => (
|
|
179
179
|
<span key={tool} className="px-2 py-0.5 rounded-[6px] text-[11px] font-600 bg-sky-400/[0.08] text-sky-400/70">
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState, useMemo } from 'react'
|
|
3
|
+
import { useState, useMemo, useEffect } from 'react'
|
|
4
4
|
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
5
5
|
import { SOUL_LIBRARY, SOUL_ARCHETYPES, searchSouls, type SoulTemplate } from '@/lib/soul-library'
|
|
6
|
+
import { api } from '@/lib/api-client'
|
|
6
7
|
|
|
7
8
|
interface SoulLibraryPickerProps {
|
|
8
9
|
open: boolean
|
|
@@ -13,8 +14,48 @@ interface SoulLibraryPickerProps {
|
|
|
13
14
|
export function SoulLibraryPicker({ open, onClose, onSelect }: SoulLibraryPickerProps) {
|
|
14
15
|
const [query, setQuery] = useState('')
|
|
15
16
|
const [archetype, setArchetype] = useState('All')
|
|
17
|
+
const [source, setSource] = useState<'library' | 'forge'>('library')
|
|
18
|
+
const [customSouls, setCustomSouls] = useState<SoulTemplate[]>([])
|
|
19
|
+
const [loading, setLoading] = useState(false)
|
|
16
20
|
|
|
17
|
-
const results = useMemo(() =>
|
|
21
|
+
const results = useMemo(() => {
|
|
22
|
+
if (source === 'library') {
|
|
23
|
+
return searchSouls(query, archetype)
|
|
24
|
+
} else {
|
|
25
|
+
let filtered = customSouls
|
|
26
|
+
if (archetype && archetype !== 'All') {
|
|
27
|
+
filtered = filtered.filter(s => s.archetype === archetype)
|
|
28
|
+
}
|
|
29
|
+
if (query) {
|
|
30
|
+
const q = query.toLowerCase()
|
|
31
|
+
filtered = filtered.filter(s =>
|
|
32
|
+
s.name.toLowerCase().includes(q) ||
|
|
33
|
+
s.soul.toLowerCase().includes(q) ||
|
|
34
|
+
s.tags.some(t => t.toLowerCase().includes(q))
|
|
35
|
+
)
|
|
36
|
+
}
|
|
37
|
+
return filtered
|
|
38
|
+
}
|
|
39
|
+
}, [query, archetype, source, customSouls])
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (open && source === 'forge') {
|
|
43
|
+
const load = async () => {
|
|
44
|
+
setLoading(true)
|
|
45
|
+
try {
|
|
46
|
+
const res = await api<SoulTemplate[]>('GET', '/souls')
|
|
47
|
+
// Filter out the built-in ones from the API result since we show them in 'library' tab
|
|
48
|
+
const libraryIds = new Set(SOUL_LIBRARY.map(s => s.id))
|
|
49
|
+
setCustomSouls(res.filter(s => !libraryIds.has(s.id)))
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error('Failed to load custom souls', err)
|
|
52
|
+
} finally {
|
|
53
|
+
setLoading(false)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
load()
|
|
57
|
+
}
|
|
58
|
+
}, [open, source])
|
|
18
59
|
|
|
19
60
|
const handleSelect = (template: SoulTemplate) => {
|
|
20
61
|
onSelect(template.soul)
|
|
@@ -23,9 +64,25 @@ export function SoulLibraryPicker({ open, onClose, onSelect }: SoulLibraryPicker
|
|
|
23
64
|
|
|
24
65
|
return (
|
|
25
66
|
<BottomSheet open={open} onClose={onClose}>
|
|
26
|
-
<div className="mb-6">
|
|
27
|
-
<
|
|
28
|
-
|
|
67
|
+
<div className="mb-6 flex items-center justify-between">
|
|
68
|
+
<div>
|
|
69
|
+
<h2 className="font-display text-[24px] font-700 tracking-[-0.03em] mb-1">Soul Library</h2>
|
|
70
|
+
<p className="text-[13px] text-text-3">Browse personality templates for your agent</p>
|
|
71
|
+
</div>
|
|
72
|
+
<div className="flex bg-white/[0.04] p-1 rounded-[12px] border border-white/[0.04]">
|
|
73
|
+
<button
|
|
74
|
+
onClick={() => setSource('library')}
|
|
75
|
+
className={`px-3 py-1.5 rounded-[10px] text-[12px] font-600 transition-all ${source === 'library' ? 'bg-white/[0.08] text-text shadow-sm' : 'text-text-3 hover:text-text-2'}`}
|
|
76
|
+
>
|
|
77
|
+
Verified
|
|
78
|
+
</button>
|
|
79
|
+
<button
|
|
80
|
+
onClick={() => setSource('forge')}
|
|
81
|
+
className={`px-3 py-1.5 rounded-[10px] text-[12px] font-600 transition-all ${source === 'forge' ? 'bg-accent-soft text-accent-bright' : 'text-text-3 hover:text-text-2'}`}
|
|
82
|
+
>
|
|
83
|
+
SwarmForge
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
29
86
|
</div>
|
|
30
87
|
|
|
31
88
|
{/* Search */}
|
|
@@ -34,7 +91,7 @@ export function SoulLibraryPicker({ open, onClose, onSelect }: SoulLibraryPicker
|
|
|
34
91
|
type="text"
|
|
35
92
|
value={query}
|
|
36
93
|
onChange={(e) => setQuery(e.target.value)}
|
|
37
|
-
placeholder="Search personalities..."
|
|
94
|
+
placeholder={source === 'library' ? "Search verified personalities..." : "Search SwarmForge / Custom..."}
|
|
38
95
|
className="w-full px-4 py-3 rounded-[14px] border border-white/[0.08] bg-surface text-text text-[14px] outline-none focus-glow"
|
|
39
96
|
style={{ fontFamily: 'inherit' }}
|
|
40
97
|
/>
|
|
@@ -59,31 +116,45 @@ export function SoulLibraryPicker({ open, onClose, onSelect }: SoulLibraryPicker
|
|
|
59
116
|
|
|
60
117
|
{/* Results grid */}
|
|
61
118
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 max-h-[60vh] overflow-y-auto pb-4">
|
|
62
|
-
{
|
|
119
|
+
{loading ? (
|
|
120
|
+
<div className="col-span-2 py-12 flex flex-col items-center gap-3">
|
|
121
|
+
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-accent-bright" />
|
|
122
|
+
<p className="text-[13px] text-text-3">Stoking the forge...</p>
|
|
123
|
+
</div>
|
|
124
|
+
) : results.map((template) => (
|
|
63
125
|
<button
|
|
64
126
|
key={template.id}
|
|
65
127
|
onClick={() => handleSelect(template)}
|
|
66
|
-
className=
|
|
128
|
+
className={`text-left p-4 rounded-[14px] border border-white/[0.06] bg-surface hover:bg-surface-2 transition-all cursor-pointer group
|
|
129
|
+
${source === 'forge' ? 'hover:border-accent-bright/20' : 'hover:border-white/[0.12]'}`}
|
|
67
130
|
style={{ fontFamily: 'inherit' }}
|
|
68
131
|
>
|
|
69
132
|
<div className="flex items-start gap-2 mb-2">
|
|
70
|
-
<h4 className=
|
|
133
|
+
<h4 className={`text-[14px] font-600 text-text transition-colors ${source === 'forge' ? 'group-hover:text-accent-bright' : ''}`}>
|
|
71
134
|
{template.name}
|
|
72
135
|
</h4>
|
|
73
136
|
<span className="px-1.5 py-0.5 rounded-[5px] bg-white/[0.06] text-text-3 text-[10px] font-600 shrink-0">
|
|
74
137
|
{template.archetype}
|
|
75
138
|
</span>
|
|
76
139
|
</div>
|
|
77
|
-
<p className="text-[12px] text-text-3 mb-2">{template.description}</p>
|
|
140
|
+
<p className="text-[12px] text-text-3 mb-2 line-clamp-2">{template.description}</p>
|
|
78
141
|
<p className="text-[11px] text-text-3/60 line-clamp-2 italic">{template.soul}</p>
|
|
79
142
|
</button>
|
|
80
143
|
))}
|
|
81
|
-
{results.length === 0 && (
|
|
82
|
-
<
|
|
144
|
+
{!loading && results.length === 0 && (
|
|
145
|
+
<div className="col-span-2 text-center py-12">
|
|
146
|
+
<div className="w-12 h-12 rounded-full bg-white/[0.03] flex items-center justify-center mx-auto mb-3">
|
|
147
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" className="text-text-3/40"><path d="M12 2a4 4 0 0 1 4 4v2a4 4 0 0 1-8 0V6a4 4 0 0 1 4-4Z"/><path d="M16 14H8a4 4 0 0 0-4 4v2h16v-2a4 4 0 0 0-4-4Z"/></svg>
|
|
148
|
+
</div>
|
|
149
|
+
<p className="text-[14px] font-600 text-text-2">No personalities match</p>
|
|
150
|
+
<p className="text-[12px] text-text-3/50 mt-1">{source === 'forge' ? 'Be the first to forge a custom soul in this category!' : 'Try a different search term.'}</p>
|
|
151
|
+
</div>
|
|
83
152
|
)}
|
|
84
153
|
</div>
|
|
85
154
|
|
|
86
|
-
<p className="text-[11px] text-text-3/50 mt-4 text-center">
|
|
155
|
+
<p className="text-[11px] text-text-3/50 mt-4 text-center">
|
|
156
|
+
{source === 'library' ? `${SOUL_LIBRARY.length} verified templates` : `${customSouls.length} custom souls in your forge`}
|
|
157
|
+
</p>
|
|
87
158
|
</BottomSheet>
|
|
88
159
|
)
|
|
89
160
|
}
|