@swarmclawai/swarmclaw 0.6.6 → 0.6.7
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 +57 -27
- package/package.json +6 -1
- package/src/app/api/agents/[id]/clone/route.ts +40 -0
- package/src/app/api/agents/route.ts +39 -14
- package/src/app/api/chatrooms/[id]/chat/route.ts +17 -1
- package/src/app/api/chatrooms/[id]/moderate/route.ts +150 -0
- package/src/app/api/chatrooms/[id]/route.ts +19 -1
- package/src/app/api/chatrooms/route.ts +12 -2
- package/src/app/api/connectors/[id]/health/route.ts +64 -0
- package/src/app/api/connectors/route.ts +17 -2
- package/src/app/api/knowledge/route.ts +6 -1
- package/src/app/api/openclaw/doctor/route.ts +17 -0
- package/src/app/api/sessions/[id]/chat/route.ts +5 -1
- package/src/app/api/sessions/route.ts +11 -2
- package/src/app/api/tasks/[id]/route.ts +18 -13
- package/src/app/api/tasks/route.ts +20 -1
- package/src/app/api/usage/route.ts +16 -7
- package/src/cli/index.js +5 -0
- package/src/cli/index.ts +223 -39
- package/src/components/agents/agent-card.tsx +37 -6
- package/src/components/agents/agent-chat-list.tsx +78 -2
- package/src/components/agents/agent-sheet.tsx +79 -0
- package/src/components/auth/setup-wizard.tsx +268 -353
- package/src/components/chat/chat-area.tsx +22 -7
- package/src/components/chat/message-bubble.tsx +14 -14
- package/src/components/chat/message-list.tsx +1 -1
- package/src/components/chatrooms/chatroom-message.tsx +164 -22
- package/src/components/chatrooms/chatroom-sheet.tsx +288 -3
- package/src/components/chatrooms/chatroom-view.tsx +62 -17
- package/src/components/connectors/connector-health.tsx +120 -0
- package/src/components/connectors/connector-sheet.tsx +9 -0
- package/src/components/home/home-view.tsx +23 -2
- package/src/components/input/chat-input.tsx +8 -1
- package/src/components/layout/app-layout.tsx +17 -1
- package/src/components/schedules/schedule-list.tsx +55 -9
- package/src/components/schedules/schedule-sheet.tsx +134 -23
- package/src/components/shared/command-palette.tsx +237 -0
- package/src/components/shared/connector-platform-icon.tsx +1 -0
- package/src/components/tasks/task-card.tsx +22 -2
- package/src/components/tasks/task-sheet.tsx +91 -16
- package/src/components/usage/metrics-dashboard.tsx +13 -25
- package/src/hooks/use-swipe.ts +49 -0
- package/src/lib/providers/anthropic.ts +16 -2
- package/src/lib/providers/claude-cli.ts +7 -1
- package/src/lib/providers/index.ts +7 -0
- package/src/lib/providers/ollama.ts +16 -2
- package/src/lib/providers/openai.ts +7 -2
- package/src/lib/providers/openclaw.ts +6 -1
- package/src/lib/providers/provider-defaults.ts +7 -0
- package/src/lib/schedule-templates.ts +115 -0
- package/src/lib/server/alert-dispatch.ts +64 -0
- package/src/lib/server/chat-execution.ts +41 -1
- package/src/lib/server/chatroom-helpers.ts +22 -1
- package/src/lib/server/chatroom-routing.ts +65 -0
- package/src/lib/server/connectors/discord.ts +3 -0
- package/src/lib/server/connectors/email.ts +267 -0
- package/src/lib/server/connectors/manager.ts +159 -3
- package/src/lib/server/connectors/openclaw.ts +3 -0
- package/src/lib/server/connectors/slack.ts +6 -0
- package/src/lib/server/connectors/telegram.ts +18 -0
- package/src/lib/server/connectors/types.ts +2 -0
- package/src/lib/server/connectors/whatsapp.ts +9 -0
- package/src/lib/server/cost.ts +70 -0
- package/src/lib/server/create-notification.ts +2 -0
- package/src/lib/server/daemon-state.ts +124 -0
- package/src/lib/server/dag-validation.ts +115 -0
- package/src/lib/server/memory-db.ts +12 -7
- package/src/lib/server/openclaw-doctor.ts +48 -0
- package/src/lib/server/queue.ts +12 -0
- package/src/lib/server/session-run-manager.ts +22 -1
- package/src/lib/server/session-tools/index.ts +2 -0
- package/src/lib/server/session-tools/memory.ts +22 -3
- package/src/lib/server/session-tools/openclaw-workspace.ts +132 -0
- package/src/lib/server/storage.ts +120 -6
- package/src/lib/setup-defaults.ts +277 -0
- package/src/lib/validation/schemas.ts +69 -0
- package/src/stores/use-app-store.ts +7 -3
- package/src/stores/use-chatroom-store.ts +52 -2
- package/src/types/index.ts +38 -1
- package/tsconfig.json +2 -1
|
@@ -6,7 +6,7 @@ import { useAppStore } from '@/stores/use-app-store'
|
|
|
6
6
|
import { useChatStore } from '@/stores/use-chat-store'
|
|
7
7
|
import { useWs } from '@/hooks/use-ws'
|
|
8
8
|
import { api } from '@/lib/api-client'
|
|
9
|
-
import {
|
|
9
|
+
import { deleteAgent } from '@/lib/agents'
|
|
10
10
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
|
11
11
|
import {
|
|
12
12
|
DropdownMenu,
|
|
@@ -79,11 +79,18 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
79
79
|
setRunning(false)
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
const [cloning, setCloning] = useState(false)
|
|
82
83
|
const handleDuplicate = async () => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
setCloning(true)
|
|
85
|
+
try {
|
|
86
|
+
await api('POST', `/agents/${agent.id}/clone`)
|
|
87
|
+
await loadAgents()
|
|
88
|
+
toast.success('Agent duplicated')
|
|
89
|
+
} catch (err: unknown) {
|
|
90
|
+
toast.error(err instanceof Error ? err.message : String(err))
|
|
91
|
+
} finally {
|
|
92
|
+
setCloning(false)
|
|
93
|
+
}
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
const handleDelete = async () => {
|
|
@@ -140,7 +147,9 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
140
147
|
<DropdownMenuItem onClick={() => { togglePinAgent(agent.id); toast.success(agent.pinned ? 'Agent unpinned' : 'Agent pinned') }}>
|
|
141
148
|
{agent.pinned ? 'Unpin' : 'Pin'}
|
|
142
149
|
</DropdownMenuItem>
|
|
143
|
-
<DropdownMenuItem onClick={handleDuplicate}>
|
|
150
|
+
<DropdownMenuItem onClick={handleDuplicate} disabled={cloning}>
|
|
151
|
+
{cloning ? 'Duplicating...' : 'Duplicate'}
|
|
152
|
+
</DropdownMenuItem>
|
|
144
153
|
{!isDefault && onSetDefault && (
|
|
145
154
|
<DropdownMenuItem onClick={() => { onSetDefault(agent.id); toast.success(`${agent.name} set as default`) }}>Set Default</DropdownMenuItem>
|
|
146
155
|
)}
|
|
@@ -217,6 +226,28 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
217
226
|
<span>Cost: ${agent.totalCost.toFixed(2)}</span>
|
|
218
227
|
)}
|
|
219
228
|
</div>
|
|
229
|
+
{typeof agent.monthlyBudget === 'number' && agent.monthlyBudget > 0 && (
|
|
230
|
+
<div className="mt-2">
|
|
231
|
+
<div className="flex items-center justify-between text-[10px] text-text-3/60 mb-1">
|
|
232
|
+
<span>${(agent.monthlySpend ?? 0).toFixed(2)} / ${agent.monthlyBudget.toFixed(2)}</span>
|
|
233
|
+
<span className={`font-600 ${(agent.monthlySpend ?? 0) >= agent.monthlyBudget ? 'text-red-400' : 'text-text-3/50'}`}>
|
|
234
|
+
{agent.budgetAction === 'block' ? 'hard cap' : 'soft cap'}
|
|
235
|
+
</span>
|
|
236
|
+
</div>
|
|
237
|
+
<div className="h-1 rounded-full bg-white/[0.06] overflow-hidden">
|
|
238
|
+
<div
|
|
239
|
+
className={`h-full rounded-full transition-all duration-300 ${
|
|
240
|
+
(agent.monthlySpend ?? 0) >= agent.monthlyBudget
|
|
241
|
+
? 'bg-red-400'
|
|
242
|
+
: (agent.monthlySpend ?? 0) >= agent.monthlyBudget * 0.8
|
|
243
|
+
? 'bg-amber-400'
|
|
244
|
+
: 'bg-accent'
|
|
245
|
+
}`}
|
|
246
|
+
style={{ width: `${Math.min(100, ((agent.monthlySpend ?? 0) / agent.monthlyBudget) * 100)}%` }}
|
|
247
|
+
/>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
)}
|
|
220
251
|
</div>
|
|
221
252
|
|
|
222
253
|
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
|
@@ -5,6 +5,8 @@ import { useAppStore } from '@/stores/use-app-store'
|
|
|
5
5
|
import { useChatStore } from '@/stores/use-chat-store'
|
|
6
6
|
import { useChatroomStore } from '@/stores/use-chatroom-store'
|
|
7
7
|
import { fetchMessages } from '@/lib/sessions'
|
|
8
|
+
import { api } from '@/lib/api-client'
|
|
9
|
+
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
8
10
|
import type { Agent, Session } from '@/types'
|
|
9
11
|
import { AgentAvatar } from './agent-avatar'
|
|
10
12
|
import { toast } from 'sonner'
|
|
@@ -32,6 +34,39 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
32
34
|
const chatrooms = useChatroomStore((s) => s.chatrooms)
|
|
33
35
|
const chatroomStreaming = useChatroomStore((s) => s.streamingAgents)
|
|
34
36
|
const [search, setSearch] = useState('')
|
|
37
|
+
const [bulkMode, setBulkMode] = useState(false)
|
|
38
|
+
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
|
|
39
|
+
const [confirmBulkDelete, setConfirmBulkDelete] = useState(false)
|
|
40
|
+
const loadSessions = useAppStore((s) => s.loadSessions)
|
|
41
|
+
|
|
42
|
+
const toggleSelected = useCallback((id: string) => {
|
|
43
|
+
setSelectedIds((prev) => {
|
|
44
|
+
const next = new Set(prev)
|
|
45
|
+
if (next.has(id)) next.delete(id)
|
|
46
|
+
else next.add(id)
|
|
47
|
+
return next
|
|
48
|
+
})
|
|
49
|
+
}, [])
|
|
50
|
+
|
|
51
|
+
const handleBulkDelete = useCallback(async () => {
|
|
52
|
+
// Collect session IDs for selected agents
|
|
53
|
+
const sessionIds = [...selectedIds]
|
|
54
|
+
.map((agentId) => {
|
|
55
|
+
const agent = agents[agentId]
|
|
56
|
+
return agent?.threadSessionId
|
|
57
|
+
})
|
|
58
|
+
.filter(Boolean) as string[]
|
|
59
|
+
if (!sessionIds.length) { toast.error('No chats to delete'); return }
|
|
60
|
+
try {
|
|
61
|
+
await api('DELETE', '/sessions', { ids: sessionIds })
|
|
62
|
+
await loadSessions()
|
|
63
|
+
toast.success(`Deleted ${sessionIds.length} chat(s)`)
|
|
64
|
+
setBulkMode(false)
|
|
65
|
+
setSelectedIds(new Set())
|
|
66
|
+
} catch {
|
|
67
|
+
toast.error('Failed to delete chats')
|
|
68
|
+
}
|
|
69
|
+
}, [selectedIds, agents, loadSessions])
|
|
35
70
|
|
|
36
71
|
// FLIP animation refs
|
|
37
72
|
const rowRefs = useRef<Map<string, HTMLElement>>(new Map())
|
|
@@ -169,7 +204,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
169
204
|
|
|
170
205
|
return (
|
|
171
206
|
<div className="flex-1 overflow-y-auto">
|
|
172
|
-
{/* Filter control */}
|
|
207
|
+
{/* Filter control + bulk mode toggle */}
|
|
173
208
|
{sortedAgents.length > 2 && (
|
|
174
209
|
<div className="flex items-center gap-1 px-4 pt-2.5 pb-1">
|
|
175
210
|
{(['all', 'active', 'recent'] as const).map((f) => (
|
|
@@ -185,6 +220,28 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
185
220
|
{f}
|
|
186
221
|
</button>
|
|
187
222
|
))}
|
|
223
|
+
<button
|
|
224
|
+
type="button"
|
|
225
|
+
onClick={() => { setBulkMode(!bulkMode); setSelectedIds(new Set()) }}
|
|
226
|
+
aria-label={bulkMode ? 'Exit selection mode' : 'Select chats'}
|
|
227
|
+
className={`ml-auto label-mono px-2.5 py-1 rounded-[6px] border-none cursor-pointer transition-colors
|
|
228
|
+
${bulkMode ? 'bg-accent-soft text-accent-bright' : 'bg-transparent text-text-3 hover:text-text-2 hover:bg-white/[0.04]'}`}
|
|
229
|
+
>
|
|
230
|
+
{bulkMode ? 'Cancel' : 'Select'}
|
|
231
|
+
</button>
|
|
232
|
+
</div>
|
|
233
|
+
)}
|
|
234
|
+
{/* Bulk action bar */}
|
|
235
|
+
{bulkMode && selectedIds.size > 0 && (
|
|
236
|
+
<div className="flex items-center gap-2 px-4 py-2 bg-white/[0.02] border-b border-white/[0.04]">
|
|
237
|
+
<span className="text-[12px] text-text-2 font-500 flex-1">{selectedIds.size} selected</span>
|
|
238
|
+
<button
|
|
239
|
+
onClick={() => setConfirmBulkDelete(true)}
|
|
240
|
+
className="px-3 py-1.5 rounded-[8px] border-none bg-red-500/10 text-red-400 text-[12px] font-600 cursor-pointer hover:bg-red-500/20 transition-colors"
|
|
241
|
+
style={{ fontFamily: 'inherit' }}
|
|
242
|
+
>
|
|
243
|
+
Delete
|
|
244
|
+
</button>
|
|
188
245
|
</div>
|
|
189
246
|
)}
|
|
190
247
|
{(sortedAgents.length > 5 || search) && (
|
|
@@ -219,9 +276,19 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
219
276
|
${isActive
|
|
220
277
|
? 'bg-accent-soft/80 border border-accent-bright/20'
|
|
221
278
|
: 'bg-transparent hover:bg-white/[0.02]'}`}
|
|
222
|
-
onClick={() => handleSelect(agent)}
|
|
279
|
+
onClick={() => bulkMode ? toggleSelected(agent.id) : handleSelect(agent)}
|
|
223
280
|
>
|
|
224
281
|
<div className="flex items-center gap-2.5">
|
|
282
|
+
{bulkMode && (
|
|
283
|
+
<div className={`w-5 h-5 rounded-[6px] border-2 flex items-center justify-center shrink-0 transition-colors
|
|
284
|
+
${selectedIds.has(agent.id) ? 'bg-accent-bright border-accent-bright' : 'border-white/20 bg-transparent'}`}>
|
|
285
|
+
{selectedIds.has(agent.id) && (
|
|
286
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
|
|
287
|
+
<polyline points="20 6 9 17 4 12" />
|
|
288
|
+
</svg>
|
|
289
|
+
)}
|
|
290
|
+
</div>
|
|
291
|
+
)}
|
|
225
292
|
<div className="relative shrink-0">
|
|
226
293
|
<AgentAvatar seed={agent.avatarSeed || null} avatarUrl={agent.avatarUrl} name={agent.name} size={36} />
|
|
227
294
|
<div className={`absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-bg ${
|
|
@@ -303,6 +370,15 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
303
370
|
)
|
|
304
371
|
})}
|
|
305
372
|
</div>
|
|
373
|
+
<ConfirmDialog
|
|
374
|
+
open={confirmBulkDelete}
|
|
375
|
+
title="Delete Chats"
|
|
376
|
+
message={`Delete ${selectedIds.size} chat(s)? This cannot be undone.`}
|
|
377
|
+
confirmLabel="Delete"
|
|
378
|
+
danger
|
|
379
|
+
onConfirm={() => { setConfirmBulkDelete(false); handleBulkDelete() }}
|
|
380
|
+
onCancel={() => setConfirmBulkDelete(false)}
|
|
381
|
+
/>
|
|
306
382
|
</div>
|
|
307
383
|
)
|
|
308
384
|
}
|
|
@@ -114,6 +114,9 @@ export function AgentSheet() {
|
|
|
114
114
|
const [heartbeatIntervalSec, setHeartbeatIntervalSec] = useState('') // '' = default (30m)
|
|
115
115
|
const [heartbeatModel, setHeartbeatModel] = useState('')
|
|
116
116
|
const [heartbeatPrompt, setHeartbeatPrompt] = useState('')
|
|
117
|
+
const [budgetEnabled, setBudgetEnabled] = useState(false)
|
|
118
|
+
const [monthlyBudget, setMonthlyBudget] = useState('')
|
|
119
|
+
const [budgetAction, setBudgetAction] = useState<'warn' | 'block'>('warn')
|
|
117
120
|
const [agentWallet, setAgentWallet] = useState<(Omit<AgentWallet, 'encryptedPrivateKey'> & { balanceLamports?: number; balanceSol?: number }) | null>(null)
|
|
118
121
|
const [addingKey, setAddingKey] = useState(false)
|
|
119
122
|
const [newKeyName, setNewKeyName] = useState('')
|
|
@@ -195,6 +198,9 @@ export function AgentSheet() {
|
|
|
195
198
|
setHeartbeatIntervalSec(parseDurationToSec(editing.heartbeatInterval, editing.heartbeatIntervalSec))
|
|
196
199
|
setHeartbeatModel(editing.heartbeatModel || '')
|
|
197
200
|
setHeartbeatPrompt(editing.heartbeatPrompt || '')
|
|
201
|
+
setBudgetEnabled(typeof editing.monthlyBudget === 'number' && editing.monthlyBudget > 0)
|
|
202
|
+
setMonthlyBudget(typeof editing.monthlyBudget === 'number' && editing.monthlyBudget > 0 ? String(editing.monthlyBudget) : '')
|
|
203
|
+
setBudgetAction(editing.budgetAction || 'warn')
|
|
198
204
|
// Load wallet if agent has one
|
|
199
205
|
if (editing.walletId) {
|
|
200
206
|
api<Omit<AgentWallet, 'encryptedPrivateKey'> & { balanceLamports?: number; balanceSol?: number }>('GET', `/wallets/${editing.walletId}`)
|
|
@@ -234,6 +240,9 @@ export function AgentSheet() {
|
|
|
234
240
|
setHeartbeatIntervalSec('')
|
|
235
241
|
setHeartbeatModel('')
|
|
236
242
|
setHeartbeatPrompt('')
|
|
243
|
+
setBudgetEnabled(false)
|
|
244
|
+
setMonthlyBudget('')
|
|
245
|
+
setBudgetAction('warn')
|
|
237
246
|
}
|
|
238
247
|
}
|
|
239
248
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -331,6 +340,8 @@ export function AgentSheet() {
|
|
|
331
340
|
heartbeatIntervalSec: heartbeatIntervalSec ? Number(heartbeatIntervalSec) : null,
|
|
332
341
|
heartbeatModel: heartbeatModel.trim() || null,
|
|
333
342
|
heartbeatPrompt: heartbeatPrompt.trim() || null,
|
|
343
|
+
monthlyBudget: budgetEnabled && monthlyBudget ? Number(monthlyBudget) : null,
|
|
344
|
+
budgetAction: budgetEnabled ? budgetAction : undefined,
|
|
334
345
|
}
|
|
335
346
|
if (editing) {
|
|
336
347
|
await updateAgent(editing.id, data)
|
|
@@ -732,6 +743,74 @@ export function AgentSheet() {
|
|
|
732
743
|
<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>
|
|
733
744
|
</div>
|
|
734
745
|
|
|
746
|
+
{/* Monthly Budget */}
|
|
747
|
+
<div className="mb-8">
|
|
748
|
+
<div className="flex items-center justify-between mb-3">
|
|
749
|
+
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em]">Monthly Budget</label>
|
|
750
|
+
<button
|
|
751
|
+
type="button"
|
|
752
|
+
onClick={() => setBudgetEnabled(!budgetEnabled)}
|
|
753
|
+
className={`relative w-10 h-[22px] rounded-full transition-colors duration-200 cursor-pointer ${budgetEnabled ? 'bg-accent' : 'bg-white/[0.12]'}`}
|
|
754
|
+
>
|
|
755
|
+
<span className={`absolute top-[3px] left-[3px] w-4 h-4 rounded-full bg-white transition-transform duration-200 ${budgetEnabled ? 'translate-x-[18px]' : ''}`} />
|
|
756
|
+
</button>
|
|
757
|
+
</div>
|
|
758
|
+
{budgetEnabled && (
|
|
759
|
+
<div className="space-y-4 mt-3">
|
|
760
|
+
<div>
|
|
761
|
+
<label className="block text-[12px] text-text-3/70 mb-1.5">Budget cap (USD)</label>
|
|
762
|
+
<div className="relative">
|
|
763
|
+
<span className="absolute left-3.5 top-1/2 -translate-y-1/2 text-text-3/50 text-[14px]">$</span>
|
|
764
|
+
<input
|
|
765
|
+
type="number"
|
|
766
|
+
min="0.01"
|
|
767
|
+
step="0.01"
|
|
768
|
+
value={monthlyBudget}
|
|
769
|
+
onChange={(e) => setMonthlyBudget(e.target.value)}
|
|
770
|
+
placeholder="10.00"
|
|
771
|
+
className={`${inputClass} pl-7`}
|
|
772
|
+
style={{ fontFamily: 'inherit' }}
|
|
773
|
+
/>
|
|
774
|
+
</div>
|
|
775
|
+
</div>
|
|
776
|
+
<div>
|
|
777
|
+
<label className="block text-[12px] text-text-3/70 mb-1.5">When exceeded</label>
|
|
778
|
+
<div className="flex gap-2">
|
|
779
|
+
<button
|
|
780
|
+
type="button"
|
|
781
|
+
onClick={() => setBudgetAction('warn')}
|
|
782
|
+
className={`flex-1 px-3 py-2.5 rounded-[10px] border text-[13px] font-600 cursor-pointer transition-all ${
|
|
783
|
+
budgetAction === 'warn'
|
|
784
|
+
? 'border-amber-400/40 bg-amber-400/[0.08] text-amber-400'
|
|
785
|
+
: 'border-white/[0.08] bg-transparent text-text-3 hover:bg-white/[0.04]'
|
|
786
|
+
}`}
|
|
787
|
+
style={{ fontFamily: 'inherit' }}
|
|
788
|
+
>
|
|
789
|
+
Warn
|
|
790
|
+
</button>
|
|
791
|
+
<button
|
|
792
|
+
type="button"
|
|
793
|
+
onClick={() => setBudgetAction('block')}
|
|
794
|
+
className={`flex-1 px-3 py-2.5 rounded-[10px] border text-[13px] font-600 cursor-pointer transition-all ${
|
|
795
|
+
budgetAction === 'block'
|
|
796
|
+
? 'border-red-400/40 bg-red-400/[0.08] text-red-400'
|
|
797
|
+
: 'border-white/[0.08] bg-transparent text-text-3 hover:bg-white/[0.04]'
|
|
798
|
+
}`}
|
|
799
|
+
style={{ fontFamily: 'inherit' }}
|
|
800
|
+
>
|
|
801
|
+
Block
|
|
802
|
+
</button>
|
|
803
|
+
</div>
|
|
804
|
+
</div>
|
|
805
|
+
</div>
|
|
806
|
+
)}
|
|
807
|
+
<p className="text-[11px] text-text-3/70 mt-1.5">
|
|
808
|
+
{budgetAction === 'block'
|
|
809
|
+
? 'Cap monthly spend for this agent. When exceeded, chat runs are blocked until the next month.'
|
|
810
|
+
: 'Cap monthly spend for this agent. When exceeded, a warning is shown but runs continue.'}
|
|
811
|
+
</p>
|
|
812
|
+
</div>
|
|
813
|
+
|
|
735
814
|
{/* Wallet Section */}
|
|
736
815
|
{editingId && (
|
|
737
816
|
<WalletSection
|