@swarmclawai/swarmclaw 0.6.4 → 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 +62 -30
- package/package.json +10 -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 +58 -3
- package/src/app/api/chatrooms/[id]/moderate/route.ts +150 -0
- package/src/app/api/chatrooms/[id]/route.ts +34 -2
- package/src/app/api/chatrooms/route.ts +26 -3
- 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/schedules/[id]/run/route.ts +3 -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 +44 -1
- package/src/app/api/usage/route.ts +16 -7
- package/src/app/api/wallets/[id]/approve/route.ts +62 -0
- package/src/app/api/wallets/[id]/balance-history/route.ts +18 -0
- package/src/app/api/wallets/[id]/route.ts +118 -0
- package/src/app/api/wallets/[id]/send/route.ts +118 -0
- package/src/app/api/wallets/[id]/transactions/route.ts +18 -0
- package/src/app/api/wallets/route.ts +74 -0
- package/src/app/globals.css +8 -0
- package/src/cli/index.js +20 -0
- package/src/cli/index.ts +223 -39
- package/src/cli/spec.js +14 -0
- package/src/components/agents/agent-avatar.tsx +15 -1
- package/src/components/agents/agent-card.tsx +38 -6
- package/src/components/agents/agent-chat-list.tsx +79 -3
- package/src/components/agents/agent-sheet.tsx +191 -26
- package/src/components/auth/setup-wizard.tsx +268 -353
- package/src/components/chat/chat-area.tsx +24 -9
- package/src/components/chat/chat-header.tsx +48 -19
- package/src/components/chat/chat-tool-toggles.tsx +1 -1
- package/src/components/chat/delegation-banner.test.ts +27 -0
- package/src/components/chat/delegation-banner.tsx +109 -23
- package/src/components/chat/message-bubble.tsx +17 -16
- package/src/components/chat/message-list.tsx +6 -5
- package/src/components/chat/streaming-bubble.tsx +3 -2
- package/src/components/chat/thinking-indicator.tsx +3 -2
- package/src/components/chat/transfer-agent-picker.tsx +1 -1
- package/src/components/chatrooms/agent-hover-card.tsx +1 -1
- package/src/components/chatrooms/chatroom-input.tsx +1 -1
- package/src/components/chatrooms/chatroom-message.tsx +165 -23
- package/src/components/chatrooms/chatroom-sheet.tsx +289 -4
- package/src/components/chatrooms/chatroom-typing-bar.tsx +1 -1
- package/src/components/chatrooms/chatroom-view.tsx +62 -17
- package/src/components/connectors/connector-health.tsx +120 -0
- package/src/components/connectors/connector-list.tsx +1 -1
- package/src/components/connectors/connector-sheet.tsx +9 -0
- package/src/components/home/home-view.tsx +25 -3
- package/src/components/input/chat-input.tsx +8 -1
- package/src/components/knowledge/knowledge-list.tsx +1 -1
- package/src/components/knowledge/knowledge-sheet.tsx +1 -1
- package/src/components/layout/app-layout.tsx +35 -4
- package/src/components/memory/memory-agent-list.tsx +1 -1
- package/src/components/memory/memory-browser.tsx +1 -0
- package/src/components/memory/memory-card.tsx +3 -2
- package/src/components/memory/memory-detail.tsx +3 -3
- package/src/components/memory/memory-sheet.tsx +2 -2
- package/src/components/projects/project-detail.tsx +4 -4
- package/src/components/schedules/schedule-list.tsx +55 -9
- package/src/components/schedules/schedule-sheet.tsx +134 -23
- package/src/components/secrets/secret-sheet.tsx +1 -1
- package/src/components/secrets/secrets-list.tsx +1 -1
- package/src/components/sessions/session-card.tsx +1 -1
- package/src/components/shared/agent-picker-list.tsx +1 -1
- package/src/components/shared/agent-switch-dialog.tsx +1 -1
- package/src/components/shared/command-palette.tsx +237 -0
- package/src/components/shared/connector-platform-icon.tsx +1 -0
- package/src/components/shared/settings/section-user-preferences.tsx +4 -4
- package/src/components/skills/skill-list.tsx +1 -1
- package/src/components/skills/skill-sheet.tsx +1 -1
- package/src/components/tasks/task-board.tsx +3 -3
- package/src/components/tasks/task-card.tsx +22 -2
- package/src/components/tasks/task-sheet.tsx +112 -17
- package/src/components/usage/metrics-dashboard.tsx +13 -25
- package/src/components/wallets/wallet-approval-dialog.tsx +99 -0
- package/src/components/wallets/wallet-panel.tsx +616 -0
- package/src/components/wallets/wallet-section.tsx +100 -0
- 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/agent-registry.ts +2 -2
- package/src/lib/server/alert-dispatch.ts +64 -0
- package/src/lib/server/chat-execution.ts +76 -4
- package/src/lib/server/chatroom-health.ts +60 -0
- package/src/lib/server/chatroom-helpers.test.ts +94 -0
- package/src/lib/server/chatroom-helpers.ts +86 -12
- 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/inbound-audio-transcription.test.ts +191 -0
- package/src/lib/server/connectors/inbound-audio-transcription.ts +261 -0
- package/src/lib/server/connectors/manager.ts +239 -5
- 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-text.test.ts +29 -0
- package/src/lib/server/connectors/whatsapp-text.ts +26 -0
- package/src/lib/server/connectors/whatsapp.ts +17 -5
- 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/orchestrator-lg.ts +12 -2
- package/src/lib/server/orchestrator.ts +6 -1
- package/src/lib/server/queue-followups.test.ts +224 -0
- package/src/lib/server/queue.ts +238 -24
- package/src/lib/server/scheduler.ts +3 -0
- package/src/lib/server/session-run-manager.ts +22 -1
- package/src/lib/server/session-tools/chatroom.ts +11 -2
- package/src/lib/server/session-tools/context-mgmt.ts +2 -2
- package/src/lib/server/session-tools/index.ts +8 -2
- package/src/lib/server/session-tools/memory.ts +23 -4
- package/src/lib/server/session-tools/openclaw-workspace.ts +132 -0
- package/src/lib/server/session-tools/shell.ts +1 -1
- package/src/lib/server/session-tools/wallet.ts +124 -0
- package/src/lib/server/session-tools/web.ts +2 -2
- package/src/lib/server/solana.ts +122 -0
- package/src/lib/server/storage.ts +158 -6
- package/src/lib/server/stream-agent-chat.ts +126 -63
- package/src/lib/server/task-mention.test.ts +41 -0
- package/src/lib/server/task-mention.ts +3 -2
- package/src/lib/setup-defaults.ts +277 -0
- package/src/lib/tool-definitions.ts +1 -0
- package/src/lib/validation/schemas.ts +69 -0
- package/src/lib/view-routes.ts +1 -0
- package/src/stores/use-app-store.ts +15 -3
- package/src/stores/use-chatroom-store.ts +52 -2
- package/src/types/index.ts +98 -2
- package/tsconfig.json +2 -1
|
@@ -63,7 +63,7 @@ export function ChatroomTypingBar({ streamingAgents }: Props) {
|
|
|
63
63
|
return (
|
|
64
64
|
<div key={agentId} className="flex gap-2.5 px-4 py-1.5" style={{ animation: 'msg-in 0.2s ease-out both' }}>
|
|
65
65
|
<div className="shrink-0 mt-0.5 w-7">
|
|
66
|
-
<AgentAvatar seed={agent?.avatarSeed || null} name={a.name} size={28} />
|
|
66
|
+
<AgentAvatar seed={agent?.avatarSeed || null} avatarUrl={agent?.avatarUrl} name={a.name} size={28} />
|
|
67
67
|
</div>
|
|
68
68
|
<div className="flex-1 min-w-0">
|
|
69
69
|
<div className="flex items-baseline gap-2 mb-0.5">
|
|
@@ -10,13 +10,35 @@ import { ChatroomTypingBar } from './chatroom-typing-bar'
|
|
|
10
10
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
11
11
|
import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
|
|
12
12
|
import { HeartbeatMoment, ActivityMoment, isNotableTool } from '@/components/chat/activity-moment'
|
|
13
|
-
import type { Chatroom, ChatroomMessage, Agent } from '@/types'
|
|
13
|
+
import type { Chatroom, ChatroomMessage, ChatroomMember, Agent } from '@/types'
|
|
14
14
|
|
|
15
15
|
function navigateToAgent(agentId: string) {
|
|
16
16
|
useAppStore.getState().setActiveView('agents')
|
|
17
17
|
useAppStore.getState().setCurrentAgent(agentId)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function getRoleBadge(role: string) {
|
|
21
|
+
if (role === 'admin') return { label: 'Admin', className: 'bg-purple-500/20 text-purple-400 border-purple-500/30' }
|
|
22
|
+
if (role === 'moderator') return { label: 'Mod', className: 'bg-blue-500/20 text-blue-400 border-blue-500/30' }
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getMemberFromChatroom(chatroom: Chatroom, agentId: string): ChatroomMember | undefined {
|
|
27
|
+
if (chatroom.members?.length) return chatroom.members.find((m) => m.agentId === agentId)
|
|
28
|
+
return undefined
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getMemberRole(chatroom: Chatroom, agentId: string): string {
|
|
32
|
+
const member = getMemberFromChatroom(chatroom, agentId)
|
|
33
|
+
return member?.role || 'member'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isAgentMuted(chatroom: Chatroom, agentId: string): boolean {
|
|
37
|
+
const member = getMemberFromChatroom(chatroom, agentId)
|
|
38
|
+
if (!member?.mutedUntil) return false
|
|
39
|
+
return new Date(member.mutedUntil).getTime() > Date.now()
|
|
40
|
+
}
|
|
41
|
+
|
|
20
42
|
type MomentType = { kind: 'heartbeat' } | { kind: 'tool'; name: string; input: string }
|
|
21
43
|
|
|
22
44
|
/** Subscribe to a single agent heartbeat topic — one hook call per agent */
|
|
@@ -65,6 +87,10 @@ export function ChatroomView() {
|
|
|
65
87
|
const loadChatrooms = useChatroomStore((s) => s.loadChatrooms)
|
|
66
88
|
const setChatroomSheetOpen = useChatroomStore((s) => s.setChatroomSheetOpen)
|
|
67
89
|
const setEditingChatroomId = useChatroomStore((s) => s.setEditingChatroomId)
|
|
90
|
+
const deleteMessage = useChatroomStore((s) => s.deleteMessage)
|
|
91
|
+
const muteAgent = useChatroomStore((s) => s.muteAgent)
|
|
92
|
+
const unmuteAgent = useChatroomStore((s) => s.unmuteAgent)
|
|
93
|
+
const setMemberRole = useChatroomStore((s) => s.setMemberRole)
|
|
68
94
|
const agents = useAppStore((s) => s.agents) as Record<string, Agent>
|
|
69
95
|
const scrollRef = useRef<HTMLDivElement>(null)
|
|
70
96
|
const [pinsExpanded, setPinsExpanded] = useState(false)
|
|
@@ -183,23 +209,37 @@ export function ChatroomView() {
|
|
|
183
209
|
{chatroom.description ? ` · ${chatroom.description}` : ''}
|
|
184
210
|
</p>
|
|
185
211
|
</div>
|
|
186
|
-
{/* Member avatars */}
|
|
212
|
+
{/* Member avatars with role badges */}
|
|
187
213
|
<div className="flex -space-x-1.5 shrink-0">
|
|
188
|
-
{memberAgents.slice(0, 5).map((agent) =>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
>
|
|
195
|
-
<
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
214
|
+
{memberAgents.slice(0, 5).map((agent) => {
|
|
215
|
+
const role = getMemberRole(chatroom, agent.id)
|
|
216
|
+
const badge = getRoleBadge(role)
|
|
217
|
+
const muted = isAgentMuted(chatroom, agent.id)
|
|
218
|
+
return (
|
|
219
|
+
<Tooltip key={agent.id}>
|
|
220
|
+
<TooltipTrigger asChild>
|
|
221
|
+
<button
|
|
222
|
+
onClick={() => navigateToAgent(agent.id)}
|
|
223
|
+
className={`relative transition-all duration-200 hover:scale-110 hover:z-10 hover:-translate-y-0.5 cursor-pointer bg-transparent border-none p-0 ${muted ? 'opacity-40' : ''}`}
|
|
224
|
+
>
|
|
225
|
+
<AgentAvatar seed={agent.avatarSeed} avatarUrl={agent.avatarUrl} name={agent.name} size={22} status={streamingAgents.has(agent.id) ? 'busy' : 'online'} />
|
|
226
|
+
{badge && (
|
|
227
|
+
<span className={`absolute -bottom-1 -right-1 text-[7px] font-700 px-0.5 rounded border ${badge.className}`}>
|
|
228
|
+
{badge.label[0]}
|
|
229
|
+
</span>
|
|
230
|
+
)}
|
|
231
|
+
</button>
|
|
232
|
+
</TooltipTrigger>
|
|
233
|
+
<TooltipContent side="bottom" sideOffset={6}>
|
|
234
|
+
<div className="flex items-center gap-1.5">
|
|
235
|
+
<span>{agent.name}</span>
|
|
236
|
+
{badge && <span className={`text-[9px] font-600 px-1 py-0.5 rounded border ${badge.className}`}>{badge.label}</span>}
|
|
237
|
+
{muted && <span className="text-[9px] text-red-400">Muted</span>}
|
|
238
|
+
</div>
|
|
239
|
+
</TooltipContent>
|
|
240
|
+
</Tooltip>
|
|
241
|
+
)
|
|
242
|
+
})}
|
|
203
243
|
{memberAgents.length > 5 && (
|
|
204
244
|
<div className="w-[22px] h-[22px] rounded-full bg-white/[0.08] flex items-center justify-center text-[9px] text-text-3">
|
|
205
245
|
+{memberAgents.length - 5}
|
|
@@ -320,6 +360,11 @@ export function ChatroomView() {
|
|
|
320
360
|
onReply={(m: ChatroomMessage) => setReplyingTo(m)}
|
|
321
361
|
onTogglePin={togglePin}
|
|
322
362
|
onTransfer={handleTransfer}
|
|
363
|
+
onDeleteMessage={(messageId, targetAgentId) => deleteMessage(messageId, targetAgentId)}
|
|
364
|
+
onMuteAgent={(agentId) => muteAgent(agentId)}
|
|
365
|
+
onUnmuteAgent={(agentId) => unmuteAgent(agentId)}
|
|
366
|
+
onSetRole={(agentId, role) => setMemberRole(agentId, role)}
|
|
367
|
+
chatroom={chatroom}
|
|
323
368
|
pinnedMessageIds={pinnedIds}
|
|
324
369
|
streamingAgentIds={streamingAgentIds}
|
|
325
370
|
messages={chatroom.messages}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect } from 'react'
|
|
4
|
+
import { api } from '@/lib/api-client'
|
|
5
|
+
import type { ConnectorHealthEvent, ConnectorHealthEventType } from '@/types'
|
|
6
|
+
|
|
7
|
+
interface HealthResponse {
|
|
8
|
+
events: ConnectorHealthEvent[]
|
|
9
|
+
uptimePercent: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const EVENT_CONFIG: Record<ConnectorHealthEventType, { color: string; label: string }> = {
|
|
13
|
+
started: { color: 'bg-green-400', label: 'Started' },
|
|
14
|
+
reconnected: { color: 'bg-green-400', label: 'Reconnected' },
|
|
15
|
+
stopped: { color: 'bg-white/30', label: 'Stopped' },
|
|
16
|
+
error: { color: 'bg-red-400', label: 'Error' },
|
|
17
|
+
disconnected: { color: 'bg-amber-400', label: 'Disconnected' },
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function formatTimestamp(ts: string): string {
|
|
21
|
+
const d = new Date(ts)
|
|
22
|
+
const now = new Date()
|
|
23
|
+
const diffMs = now.getTime() - d.getTime()
|
|
24
|
+
const diffMin = Math.floor(diffMs / 60_000)
|
|
25
|
+
const diffHr = Math.floor(diffMs / 3_600_000)
|
|
26
|
+
const diffDay = Math.floor(diffMs / 86_400_000)
|
|
27
|
+
|
|
28
|
+
if (diffMin < 1) return 'just now'
|
|
29
|
+
if (diffMin < 60) return `${diffMin}m ago`
|
|
30
|
+
if (diffHr < 24) return `${diffHr}h ago`
|
|
31
|
+
if (diffDay < 7) return `${diffDay}d ago`
|
|
32
|
+
return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function uptimeBadgeColor(pct: number): string {
|
|
36
|
+
if (pct >= 99) return 'bg-green-500/15 text-green-400 border-green-500/20'
|
|
37
|
+
if (pct >= 95) return 'bg-amber-500/15 text-amber-400 border-amber-500/20'
|
|
38
|
+
return 'bg-red-500/15 text-red-400 border-red-500/20'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function ConnectorHealth({ connectorId }: { connectorId: string }) {
|
|
42
|
+
const [data, setData] = useState<HealthResponse | null>(null)
|
|
43
|
+
const [loading, setLoading] = useState(true)
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
let cancelled = false
|
|
47
|
+
async function load() {
|
|
48
|
+
setLoading(true)
|
|
49
|
+
try {
|
|
50
|
+
const resp = await api<HealthResponse>('GET', `/connectors/${connectorId}/health`)
|
|
51
|
+
if (!cancelled) setData(resp)
|
|
52
|
+
} catch {
|
|
53
|
+
// ignore fetch errors
|
|
54
|
+
} finally {
|
|
55
|
+
if (!cancelled) setLoading(false)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
load()
|
|
59
|
+
return () => { cancelled = true }
|
|
60
|
+
}, [connectorId])
|
|
61
|
+
|
|
62
|
+
if (loading) {
|
|
63
|
+
return (
|
|
64
|
+
<div className="p-4 rounded-[14px] border border-white/[0.06] bg-white/[0.01]">
|
|
65
|
+
<div className="text-[13px] text-text-3 animate-pulse">Loading health data...</div>
|
|
66
|
+
</div>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!data || data.events.length === 0) {
|
|
71
|
+
return (
|
|
72
|
+
<div className="p-4 rounded-[14px] border border-white/[0.06] bg-white/[0.01]">
|
|
73
|
+
<div className="text-[13px] text-text-3">No health events recorded yet.</div>
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Show most recent events first (up to 50)
|
|
79
|
+
const recentEvents = [...data.events].reverse().slice(0, 50)
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div className="p-4 rounded-[14px] border border-white/[0.06] bg-white/[0.01] space-y-4">
|
|
83
|
+
{/* Uptime badge */}
|
|
84
|
+
<div className="flex items-center justify-between">
|
|
85
|
+
<div className="text-[13px] font-600 text-text-2">Health Timeline</div>
|
|
86
|
+
<span className={`px-3 py-1 rounded-[8px] text-[12px] font-600 border ${uptimeBadgeColor(data.uptimePercent)}`}>
|
|
87
|
+
{data.uptimePercent}% uptime
|
|
88
|
+
</span>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
{/* Timeline */}
|
|
92
|
+
<div className="relative pl-5">
|
|
93
|
+
{/* Vertical line */}
|
|
94
|
+
<div className="absolute left-[7px] top-2 bottom-2 w-px bg-white/[0.08]" />
|
|
95
|
+
|
|
96
|
+
<div className="space-y-3">
|
|
97
|
+
{recentEvents.map((ev) => {
|
|
98
|
+
const cfg = EVENT_CONFIG[ev.event] ?? { color: 'bg-white/30', label: ev.event }
|
|
99
|
+
return (
|
|
100
|
+
<div key={ev.id} className="relative flex items-start gap-3">
|
|
101
|
+
{/* Dot */}
|
|
102
|
+
<div className={`absolute left-[-13px] top-[6px] w-[10px] h-[10px] rounded-full ${cfg.color} ring-2 ring-surface shrink-0`} />
|
|
103
|
+
{/* Content */}
|
|
104
|
+
<div className="min-w-0 flex-1">
|
|
105
|
+
<div className="flex items-center gap-2">
|
|
106
|
+
<span className="text-[13px] font-600 text-text-2">{cfg.label}</span>
|
|
107
|
+
<span className="text-[11px] text-text-3">{formatTimestamp(ev.timestamp)}</span>
|
|
108
|
+
</div>
|
|
109
|
+
{ev.message && (
|
|
110
|
+
<p className="text-[12px] text-text-3/70 mt-0.5 leading-[1.4] break-words">{ev.message}</p>
|
|
111
|
+
)}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
)
|
|
115
|
+
})}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
)
|
|
120
|
+
}
|
|
@@ -206,7 +206,7 @@ export function ConnectorList({ inSidebar }: { inSidebar?: boolean }) {
|
|
|
206
206
|
</>
|
|
207
207
|
) : agent ? (
|
|
208
208
|
<>
|
|
209
|
-
<AgentAvatar seed={agent.avatarSeed || null} name={agent.name} size={24} />
|
|
209
|
+
<AgentAvatar seed={agent.avatarSeed || null} avatarUrl={agent.avatarUrl} name={agent.name} size={24} />
|
|
210
210
|
<div className="flex-1 min-w-0">
|
|
211
211
|
<span className="text-[12px] font-600 text-text-2 block truncate">{agent.name}</span>
|
|
212
212
|
<span className="text-[10px] text-text-3/60 block">{agent.provider}/{agent.model}</span>
|
|
@@ -13,6 +13,7 @@ import { ChatroomPickerList } from '@/components/shared/chatroom-picker-list'
|
|
|
13
13
|
import { SheetFooter } from '@/components/shared/sheet-footer'
|
|
14
14
|
import { SectionLabel } from '@/components/shared/section-label'
|
|
15
15
|
import { useChatroomStore } from '@/stores/use-chatroom-store'
|
|
16
|
+
import { ConnectorHealth } from '@/components/connectors/connector-health'
|
|
16
17
|
|
|
17
18
|
/** Auto-detect URLs in text and make them clickable links that open in a new tab */
|
|
18
19
|
function linkify(text: string) {
|
|
@@ -655,6 +656,7 @@ export function ConnectorSheet() {
|
|
|
655
656
|
<span key={i} className="flex items-center gap-1.5 px-3 py-1.5 rounded-[8px] bg-accent-soft/50 border border-accent-bright/20 text-[12px] font-mono text-accent-bright">
|
|
656
657
|
{tag}
|
|
657
658
|
<button
|
|
659
|
+
aria-label={`Remove ${tag}`}
|
|
658
660
|
onClick={() => {
|
|
659
661
|
const next = tags.filter((_, j) => j !== i).join(',')
|
|
660
662
|
setConfig({ ...config, [field.key]: next })
|
|
@@ -860,6 +862,13 @@ export function ConnectorSheet() {
|
|
|
860
862
|
</div>
|
|
861
863
|
)}
|
|
862
864
|
|
|
865
|
+
{/* Health timeline (existing connectors only) */}
|
|
866
|
+
{editing && (
|
|
867
|
+
<div className="mb-6">
|
|
868
|
+
<ConnectorHealth connectorId={editing.id} />
|
|
869
|
+
</div>
|
|
870
|
+
)}
|
|
871
|
+
|
|
863
872
|
{/* Actions */}
|
|
864
873
|
<SheetFooter
|
|
865
874
|
onCancel={() => { setOpen(false); setEditingId(null) }}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useEffect, useMemo, useState } from 'react'
|
|
4
|
+
import { AreaChart, Area, ResponsiveContainer } from 'recharts'
|
|
4
5
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
6
|
import { useChatStore } from '@/stores/use-chat-store'
|
|
6
7
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
@@ -85,6 +86,7 @@ export function HomeView() {
|
|
|
85
86
|
const setTaskSheetOpen = useAppStore((s) => s.setTaskSheetOpen)
|
|
86
87
|
const setMessages = useChatStore((s) => s.setMessages)
|
|
87
88
|
const [todayCost, setTodayCost] = useState(0)
|
|
89
|
+
const [costTrend, setCostTrend] = useState<{ cost: number }[]>([])
|
|
88
90
|
|
|
89
91
|
const allAgents = Object.values(agents).filter((a) => !a.trashedAt)
|
|
90
92
|
const pinnedAgents = allAgents.filter((a) => a.pinned)
|
|
@@ -144,10 +146,11 @@ export function HomeView() {
|
|
|
144
146
|
void loadSchedules()
|
|
145
147
|
void loadNotifications()
|
|
146
148
|
void loadConnectors()
|
|
147
|
-
api<{ records: Array<{ estimatedCost: number }> }>('GET', '/usage?range=
|
|
149
|
+
api<{ records: Array<{ estimatedCost: number }>; timeSeries: Array<{ cost: number }> }>('GET', '/usage?range=7d')
|
|
148
150
|
.then((data) => {
|
|
149
151
|
const total = (data.records || []).reduce((s, r) => s + (r.estimatedCost || 0), 0)
|
|
150
152
|
setTodayCost(total)
|
|
153
|
+
setCostTrend((data.timeSeries || []).map((pt) => ({ cost: pt.cost })))
|
|
151
154
|
})
|
|
152
155
|
.catch(() => {})
|
|
153
156
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -196,13 +199,31 @@ export function HomeView() {
|
|
|
196
199
|
</div>
|
|
197
200
|
|
|
198
201
|
{/* Quick Stats */}
|
|
199
|
-
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-
|
|
202
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-3 mb-4">
|
|
200
203
|
<StatCard label="Agents" value={String(agentCount)} />
|
|
201
204
|
<StatCard label="Active Tasks" value={String(activeTaskCount)} accent={activeTaskCount > 0} />
|
|
202
205
|
<StatCard label="Today's Cost" value={`$${todayCost.toFixed(2)}`} />
|
|
203
206
|
<StatCard label="Connectors" value={`${activeConnectorCount}/${allConnectors.length}`} accent={activeConnectorCount > 0} />
|
|
204
207
|
</div>
|
|
205
208
|
|
|
209
|
+
{/* Cost trend sparkline */}
|
|
210
|
+
{costTrend.length > 1 && (
|
|
211
|
+
<div className="mb-10 px-1">
|
|
212
|
+
<p className="text-[10px] text-text-3/50 uppercase tracking-wider mb-1">7-day cost trend</p>
|
|
213
|
+
<ResponsiveContainer width="100%" height={60}>
|
|
214
|
+
<AreaChart data={costTrend} margin={{ top: 2, right: 0, bottom: 0, left: 0 }}>
|
|
215
|
+
<defs>
|
|
216
|
+
<linearGradient id="costGrad" x1="0" y1="0" x2="0" y2="1">
|
|
217
|
+
<stop offset="0%" stopColor="#818CF8" stopOpacity={0.3} />
|
|
218
|
+
<stop offset="100%" stopColor="#818CF8" stopOpacity={0} />
|
|
219
|
+
</linearGradient>
|
|
220
|
+
</defs>
|
|
221
|
+
<Area type="monotone" dataKey="cost" stroke="#818CF8" strokeWidth={1.5} fill="url(#costGrad)" dot={false} />
|
|
222
|
+
</AreaChart>
|
|
223
|
+
</ResponsiveContainer>
|
|
224
|
+
</div>
|
|
225
|
+
)}
|
|
226
|
+
|
|
206
227
|
{/* Notifications banner */}
|
|
207
228
|
{unreadNotifications.length > 0 && (
|
|
208
229
|
<section className="mb-8">
|
|
@@ -353,7 +374,7 @@ export function HomeView() {
|
|
|
353
374
|
style={{ fontFamily: 'inherit' }}
|
|
354
375
|
>
|
|
355
376
|
<div className="relative">
|
|
356
|
-
<AgentAvatar seed={agent.avatarSeed} name={agent.name} size={36} />
|
|
377
|
+
<AgentAvatar seed={agent.avatarSeed} avatarUrl={agent.avatarUrl} name={agent.name} size={36} />
|
|
357
378
|
<div className={`absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-surface ${
|
|
358
379
|
isTyping ? 'bg-accent-bright animate-pulse'
|
|
359
380
|
: isOnline ? 'bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.4)]'
|
|
@@ -414,6 +435,7 @@ export function HomeView() {
|
|
|
414
435
|
>
|
|
415
436
|
<AgentAvatar
|
|
416
437
|
seed={agent?.avatarSeed}
|
|
438
|
+
avatarUrl={agent?.avatarUrl}
|
|
417
439
|
name={displayName}
|
|
418
440
|
size={28}
|
|
419
441
|
/>
|
|
@@ -8,6 +8,7 @@ import { useAutoResize } from '@/hooks/use-auto-resize'
|
|
|
8
8
|
import { useSpeechRecognition } from '@/hooks/use-speech-recognition'
|
|
9
9
|
import { FilePreview } from '@/components/shared/file-preview'
|
|
10
10
|
import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
|
|
11
|
+
import { toast } from 'sonner'
|
|
11
12
|
import { safeStorageGet, safeStorageRemove, safeStorageSet } from '@/lib/safe-storage'
|
|
12
13
|
|
|
13
14
|
interface Props {
|
|
@@ -18,6 +19,8 @@ interface Props {
|
|
|
18
19
|
|
|
19
20
|
// FilePreview is now imported from @/components/shared/file-preview
|
|
20
21
|
|
|
22
|
+
const MAX_FILE_SIZE = 10 * 1024 * 1024 // 10 MB
|
|
23
|
+
|
|
21
24
|
export function ChatInput({ streaming, onSend, onStop }: Props) {
|
|
22
25
|
const [value, setValue] = useState('')
|
|
23
26
|
const { ref: textareaRef, resize } = useAutoResize()
|
|
@@ -90,6 +93,10 @@ export function ChatInput({ streaming, onSend, onStop }: Props) {
|
|
|
90
93
|
)
|
|
91
94
|
|
|
92
95
|
const uploadAndAdd = useCallback(async (file: File) => {
|
|
96
|
+
if (file.size > MAX_FILE_SIZE) {
|
|
97
|
+
toast.error(`File too large: ${(file.size / 1024 / 1024).toFixed(1)} MB (max 10 MB)`)
|
|
98
|
+
return
|
|
99
|
+
}
|
|
93
100
|
try {
|
|
94
101
|
const result = await uploadImage(file)
|
|
95
102
|
addPendingFile({ file, path: result.path, url: result.url })
|
|
@@ -123,7 +130,7 @@ export function ChatInput({ streaming, onSend, onStop }: Props) {
|
|
|
123
130
|
const hasContent = value.trim().length > 0 || pendingFiles.length > 0
|
|
124
131
|
|
|
125
132
|
return (
|
|
126
|
-
<div className="shrink-0 px-
|
|
133
|
+
<div className="shrink-0 px-4 md:px-12 lg:px-16 pb-4 pt-2 fixed bottom-0 left-0 right-0 z-20 bg-bg/95 backdrop-blur-md md:relative md:z-auto md:bg-transparent md:backdrop-blur-none"
|
|
127
134
|
style={{ paddingBottom: 'max(16px, env(safe-area-inset-bottom))' }}>
|
|
128
135
|
<div>
|
|
129
136
|
{streaming && (
|
|
@@ -183,7 +183,7 @@ export function KnowledgeList() {
|
|
|
183
183
|
<div className="flex items-center gap-1.5">
|
|
184
184
|
<div className="flex items-center -space-x-1.5">
|
|
185
185
|
{scopedAgents.slice(0, 5).map((agent) => (
|
|
186
|
-
<AgentAvatar key={agent.id} seed={agent.avatarSeed} name={agent.name} size={16} className="ring-1 ring-surface" />
|
|
186
|
+
<AgentAvatar key={agent.id} seed={agent.avatarSeed} avatarUrl={agent.avatarUrl} name={agent.name} size={16} className="ring-1 ring-surface" />
|
|
187
187
|
))}
|
|
188
188
|
</div>
|
|
189
189
|
{scopedAgents.length > 5 && (
|
|
@@ -360,7 +360,7 @@ export function KnowledgeSheet() {
|
|
|
360
360
|
}`}
|
|
361
361
|
style={{ fontFamily: 'inherit' }}
|
|
362
362
|
>
|
|
363
|
-
<AgentAvatar seed={agent.avatarSeed} name={agent.name} size={24} />
|
|
363
|
+
<AgentAvatar seed={agent.avatarSeed} avatarUrl={agent.avatarUrl} name={agent.name} size={24} />
|
|
364
364
|
<span className="text-[13px] text-text flex-1 truncate">{agent.name}</span>
|
|
365
365
|
{selected && (
|
|
366
366
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" className="text-accent-bright shrink-0">
|
|
@@ -4,6 +4,7 @@ import { Component, useState, useEffect, useCallback } from 'react'
|
|
|
4
4
|
import type { ReactNode, ErrorInfo } from 'react'
|
|
5
5
|
import { useAppStore } from '@/stores/use-app-store'
|
|
6
6
|
import { useMediaQuery } from '@/hooks/use-media-query'
|
|
7
|
+
import { useSwipe } from '@/hooks/use-swipe'
|
|
7
8
|
import { Avatar } from '@/components/shared/avatar'
|
|
8
9
|
import { SettingsPage } from '@/components/shared/settings/settings-page'
|
|
9
10
|
import { AgentList } from '@/components/agents/agent-list'
|
|
@@ -41,6 +42,7 @@ import { PluginSheet } from '@/components/plugins/plugin-sheet'
|
|
|
41
42
|
import { RunList } from '@/components/runs/run-list'
|
|
42
43
|
import { ActivityFeed } from '@/components/activity/activity-feed'
|
|
43
44
|
import { MetricsDashboard } from '@/components/usage/metrics-dashboard'
|
|
45
|
+
import { WalletPanel } from '@/components/wallets/wallet-panel'
|
|
44
46
|
import { ProjectList } from '@/components/projects/project-list'
|
|
45
47
|
import { ProjectDetail } from '@/components/projects/project-detail'
|
|
46
48
|
import { ProjectSheet } from '@/components/projects/project-sheet'
|
|
@@ -52,6 +54,7 @@ import { HomeView } from '@/components/home/home-view'
|
|
|
52
54
|
import { NetworkBanner } from './network-banner'
|
|
53
55
|
import { UpdateBanner } from './update-banner'
|
|
54
56
|
import { MobileHeader } from './mobile-header'
|
|
57
|
+
import { CommandPalette } from '@/components/shared/command-palette'
|
|
55
58
|
import { DaemonIndicator } from './daemon-indicator'
|
|
56
59
|
import { NotificationCenter } from '@/components/shared/notification-center'
|
|
57
60
|
import { ChatArea } from '@/components/chat/chat-area'
|
|
@@ -193,6 +196,14 @@ export function AppLayout() {
|
|
|
193
196
|
: Object.values(agents)[0]?.id || null
|
|
194
197
|
const isMainChat = activeView === 'agents' && currentAgentId === defaultAgentId
|
|
195
198
|
|
|
199
|
+
const swipeHandlers = useSwipe({
|
|
200
|
+
onSwipe: (dir) => {
|
|
201
|
+
if (dir === 'right') setSidebarOpen(true)
|
|
202
|
+
else setSidebarOpen(false)
|
|
203
|
+
},
|
|
204
|
+
leftSwipeEnabled: sidebarOpen,
|
|
205
|
+
})
|
|
206
|
+
|
|
196
207
|
const currentSession = currentSessionId ? sessions[currentSessionId] : null
|
|
197
208
|
const hasCanvas = !!(currentSession?.canvasContent && canvasDismissedFor !== currentSessionId)
|
|
198
209
|
const canvasAgentName = currentSession?.agentId && agents[currentSession.agentId] ? agents[currentSession.agentId].name : undefined
|
|
@@ -209,7 +220,12 @@ export function AppLayout() {
|
|
|
209
220
|
}
|
|
210
221
|
|
|
211
222
|
return (
|
|
212
|
-
<div
|
|
223
|
+
<div
|
|
224
|
+
className="h-full flex overflow-hidden"
|
|
225
|
+
onTouchStart={swipeHandlers.onTouchStart}
|
|
226
|
+
onTouchMove={swipeHandlers.onTouchMove}
|
|
227
|
+
onTouchEnd={swipeHandlers.onTouchEnd}
|
|
228
|
+
>
|
|
213
229
|
{/* Desktop: Navigation rail (expandable) */}
|
|
214
230
|
{isDesktop && (
|
|
215
231
|
<div
|
|
@@ -398,6 +414,11 @@ export function AppLayout() {
|
|
|
398
414
|
<line x1="18" y1="20" x2="18" y2="10" /><line x1="12" y1="20" x2="12" y2="4" /><line x1="6" y1="20" x2="6" y2="14" />
|
|
399
415
|
</svg>
|
|
400
416
|
</NavItem>
|
|
417
|
+
<NavItem view="wallets" label="Wallets" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('wallets')}>
|
|
418
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
419
|
+
<rect x="2" y="6" width="20" height="14" rx="2" /><path d="M22 10H18a2 2 0 0 0 0 4h4" /><path d="M6 6V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v2" />
|
|
420
|
+
</svg>
|
|
421
|
+
</NavItem>
|
|
401
422
|
<NavItem view="runs" label="Runs" expanded={railExpanded} active={activeView} sidebarOpen={sidebarOpen} onClick={() => handleNavClick('runs')}>
|
|
402
423
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
403
424
|
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
|
|
@@ -519,8 +540,8 @@ export function AppLayout() {
|
|
|
519
540
|
</div>
|
|
520
541
|
)}
|
|
521
542
|
|
|
522
|
-
{/* Desktop: Side panel */}
|
|
523
|
-
{isDesktop && sidebarOpen && (
|
|
543
|
+
{/* Desktop: Side panel (wallets has its own built-in sidebar) */}
|
|
544
|
+
{isDesktop && sidebarOpen && activeView !== 'wallets' && (
|
|
524
545
|
<div
|
|
525
546
|
className="w-[280px] shrink-0 bg-raised border-r border-white/[0.04] flex flex-col h-full"
|
|
526
547
|
style={{ animation: 'panel-in 0.2s cubic-bezier(0.16, 1, 0.3, 1)' }}
|
|
@@ -727,6 +748,8 @@ export function AppLayout() {
|
|
|
727
748
|
<ActivityFeed />
|
|
728
749
|
) : activeView === 'usage' ? (
|
|
729
750
|
<MetricsDashboard />
|
|
751
|
+
) : activeView === 'wallets' ? (
|
|
752
|
+
<WalletPanel />
|
|
730
753
|
) : activeView === 'chatrooms' ? (
|
|
731
754
|
<div className="flex-1 flex h-full min-w-0">
|
|
732
755
|
<div className="w-[280px] shrink-0 border-r border-white/[0.06] flex flex-col">
|
|
@@ -793,6 +816,7 @@ export function AppLayout() {
|
|
|
793
816
|
</div>
|
|
794
817
|
</ErrorBoundary>
|
|
795
818
|
|
|
819
|
+
<CommandPalette />
|
|
796
820
|
<SearchDialog />
|
|
797
821
|
<AgentSwitchDialog />
|
|
798
822
|
<KeyboardShortcutsDialog />
|
|
@@ -886,6 +910,7 @@ const VIEW_DESCRIPTIONS: Record<AppView, string> = {
|
|
|
886
910
|
logs: 'Application logs & error tracking',
|
|
887
911
|
plugins: 'Extend agent capabilities with custom plugins',
|
|
888
912
|
usage: 'Usage metrics, cost tracking & agent performance',
|
|
913
|
+
wallets: 'Agent crypto wallets — hold funds, send SOL, manage spending',
|
|
889
914
|
runs: 'Live run monitoring & history',
|
|
890
915
|
settings: 'Manage providers, API keys & orchestrator engine',
|
|
891
916
|
projects: 'Group agents, tasks & schedules into projects',
|
|
@@ -895,7 +920,7 @@ const VIEW_DESCRIPTIONS: Record<AppView, string> = {
|
|
|
895
920
|
const FULL_WIDTH_VIEWS = new Set<AppView>([
|
|
896
921
|
'home', 'chatrooms', 'schedules', 'secrets', 'providers', 'skills',
|
|
897
922
|
'connectors', 'webhooks', 'mcp_servers', 'knowledge', 'plugins',
|
|
898
|
-
'usage', 'runs', 'logs', 'settings', 'activity', 'projects',
|
|
923
|
+
'usage', 'wallets', 'runs', 'logs', 'settings', 'activity', 'projects',
|
|
899
924
|
])
|
|
900
925
|
|
|
901
926
|
const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents' | 'home'>, { icon: string; title: string; description: string; features: string[] }> = {
|
|
@@ -1007,6 +1032,12 @@ const VIEW_EMPTY_STATES: Record<Exclude<AppView, 'agents' | 'home'>, { icon: str
|
|
|
1007
1032
|
description: 'Audit trail of all entity mutations across the system.',
|
|
1008
1033
|
features: ['Track agent, task, and connector changes', 'Filter by entity type and action', 'Real-time updates via WebSocket', 'Relative timestamps'],
|
|
1009
1034
|
},
|
|
1035
|
+
wallets: {
|
|
1036
|
+
icon: 'wallet',
|
|
1037
|
+
title: 'Wallets',
|
|
1038
|
+
description: 'Agent crypto wallets for autonomous financial operations on Solana.',
|
|
1039
|
+
features: ['Create Solana wallets for agents', 'Per-transaction and daily spending limits', 'User approval for transactions', 'Balance tracking and transaction history'],
|
|
1040
|
+
},
|
|
1010
1041
|
}
|
|
1011
1042
|
|
|
1012
1043
|
function ViewEmptyState({ view }: { view: AppView }) {
|
|
@@ -120,7 +120,7 @@ export function MemoryAgentList() {
|
|
|
120
120
|
{isActive && (
|
|
121
121
|
<div className="absolute left-0 top-2 bottom-2 w-[2.5px] rounded-full bg-accent-bright" />
|
|
122
122
|
)}
|
|
123
|
-
<AgentAvatar seed={agent.avatarSeed || null} name={agent.name} size={28} />
|
|
123
|
+
<AgentAvatar seed={agent.avatarSeed || null} avatarUrl={agent.avatarUrl} name={agent.name} size={28} />
|
|
124
124
|
<span className={`text-[13px] font-600 flex-1 truncate ${isActive ? 'text-accent-bright' : 'text-text-2'}`}>
|
|
125
125
|
{agent.name}
|
|
126
126
|
</span>
|
|
@@ -163,6 +163,7 @@ export function MemoryBrowser() {
|
|
|
163
163
|
active={e.id === selectedMemoryId}
|
|
164
164
|
agentName={showAgent ? (agent?.name || null) : undefined}
|
|
165
165
|
agentAvatarSeed={showAgent ? (agent?.avatarSeed || null) : undefined}
|
|
166
|
+
agentAvatarUrl={showAgent ? (agent?.avatarUrl || null) : undefined}
|
|
166
167
|
onClick={() => setSelectedMemoryId(e.id)}
|
|
167
168
|
/>
|
|
168
169
|
)
|
|
@@ -17,10 +17,11 @@ interface Props {
|
|
|
17
17
|
active?: boolean
|
|
18
18
|
agentName?: string | null
|
|
19
19
|
agentAvatarSeed?: string | null
|
|
20
|
+
agentAvatarUrl?: string | null
|
|
20
21
|
onClick: () => void
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
export function MemoryCard({ entry, active, agentName, agentAvatarSeed, onClick }: Props) {
|
|
24
|
+
export function MemoryCard({ entry, active, agentName, agentAvatarSeed, agentAvatarUrl, onClick }: Props) {
|
|
24
25
|
return (
|
|
25
26
|
<div
|
|
26
27
|
onClick={onClick}
|
|
@@ -73,7 +74,7 @@ export function MemoryCard({ entry, active, agentName, agentAvatarSeed, onClick
|
|
|
73
74
|
)}
|
|
74
75
|
{agentName ? (
|
|
75
76
|
<div className="flex items-center gap-1.5 mt-1.5">
|
|
76
|
-
<AgentAvatar seed={agentAvatarSeed || null} name={agentName} size={16} />
|
|
77
|
+
<AgentAvatar seed={agentAvatarSeed || null} avatarUrl={agentAvatarUrl} name={agentName} size={16} />
|
|
77
78
|
<span className="text-[10px] text-text-3/60 truncate">{agentName}</span>
|
|
78
79
|
</div>
|
|
79
80
|
) : !entry.agentId ? (
|
|
@@ -329,7 +329,7 @@ export function MemoryDetail() {
|
|
|
329
329
|
: 'bg-white/[0.02] border-white/[0.06] text-text-3 hover:text-text-2 hover:bg-white/[0.04]'}`}
|
|
330
330
|
style={{ fontFamily: 'inherit' }}
|
|
331
331
|
>
|
|
332
|
-
<AgentAvatar seed={agent.avatarSeed || null} name={agent.name} size={16} />
|
|
332
|
+
<AgentAvatar seed={agent.avatarSeed || null} avatarUrl={agent.avatarUrl} name={agent.name} size={16} />
|
|
333
333
|
<span className="truncate max-w-[100px]">{agent.name}</span>
|
|
334
334
|
</button>
|
|
335
335
|
))}
|
|
@@ -360,7 +360,7 @@ export function MemoryDetail() {
|
|
|
360
360
|
: 'bg-white/[0.02] border-white/[0.06] text-text-3 hover:text-text-2 hover:bg-white/[0.04]'}`}
|
|
361
361
|
style={{ fontFamily: 'inherit' }}
|
|
362
362
|
>
|
|
363
|
-
<AgentAvatar seed={agent.avatarSeed || null} name={agent.name} size={16} />
|
|
363
|
+
<AgentAvatar seed={agent.avatarSeed || null} avatarUrl={agent.avatarUrl} name={agent.name} size={16} />
|
|
364
364
|
<span className="truncate max-w-[100px]">{agent.name}</span>
|
|
365
365
|
</button>
|
|
366
366
|
)
|
|
@@ -406,7 +406,7 @@ export function MemoryDetail() {
|
|
|
406
406
|
const a = agents[aid]
|
|
407
407
|
return (
|
|
408
408
|
<span key={aid} className="flex items-center gap-1.5 px-2.5 py-1 rounded-[8px] bg-white/[0.03] text-[11px] text-text-3">
|
|
409
|
-
<AgentAvatar seed={a?.avatarSeed || null} name={a?.name || aid} size={16} />
|
|
409
|
+
<AgentAvatar seed={a?.avatarSeed || null} avatarUrl={a?.avatarUrl} name={a?.name || aid} size={16} />
|
|
410
410
|
{a?.name || aid}
|
|
411
411
|
</span>
|
|
412
412
|
)
|
|
@@ -104,7 +104,7 @@ export function MemorySheet() {
|
|
|
104
104
|
: 'bg-white/[0.02] border-white/[0.06] text-text-3 hover:text-text-2 hover:bg-white/[0.04]'}`}
|
|
105
105
|
style={{ fontFamily: 'inherit' }}
|
|
106
106
|
>
|
|
107
|
-
<AgentAvatar seed={agent.avatarSeed || null} name={agent.name} size={20} />
|
|
107
|
+
<AgentAvatar seed={agent.avatarSeed || null} avatarUrl={agent.avatarUrl} name={agent.name} size={20} />
|
|
108
108
|
<span className="truncate max-w-[120px]">{agent.name}</span>
|
|
109
109
|
</button>
|
|
110
110
|
))}
|
|
@@ -140,7 +140,7 @@ export function MemorySheet() {
|
|
|
140
140
|
: 'bg-white/[0.02] border-white/[0.06] text-text-3 hover:text-text-2 hover:bg-white/[0.04]'}`}
|
|
141
141
|
style={{ fontFamily: 'inherit' }}
|
|
142
142
|
>
|
|
143
|
-
<AgentAvatar seed={agent.avatarSeed || null} name={agent.name} size={20} />
|
|
143
|
+
<AgentAvatar seed={agent.avatarSeed || null} avatarUrl={agent.avatarUrl} name={agent.name} size={20} />
|
|
144
144
|
<span className="truncate max-w-[120px]">{agent.name}</span>
|
|
145
145
|
</button>
|
|
146
146
|
)
|