@swarmclawai/swarmclaw 0.7.1 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +155 -150
- package/package.json +1 -1
- package/src/app/api/agents/[id]/route.ts +26 -0
- package/src/app/api/agents/[id]/thread/route.ts +37 -9
- package/src/app/api/agents/route.ts +13 -2
- package/src/app/api/auth/route.ts +76 -7
- package/src/app/api/chatrooms/[id]/chat/route.ts +7 -2
- package/src/app/api/{sessions → chats}/[id]/browser/route.ts +5 -1
- package/src/app/api/{sessions → chats}/[id]/chat/route.ts +7 -3
- package/src/app/api/{sessions → chats}/[id]/checkpoints/route.ts +1 -1
- package/src/app/api/chats/[id]/main-loop/route.ts +13 -0
- package/src/app/api/{sessions → chats}/[id]/messages/route.ts +19 -13
- package/src/app/api/{sessions → chats}/[id]/restore/route.ts +1 -1
- package/src/app/api/{sessions → chats}/[id]/route.ts +22 -52
- package/src/app/api/{sessions → chats}/[id]/stop/route.ts +6 -1
- package/src/app/api/{sessions → chats}/route.ts +21 -7
- package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
- package/src/app/api/connectors/doctor/route.ts +13 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/memory/maintenance/route.ts +11 -1
- package/src/app/api/openclaw/agent-files/route.ts +27 -4
- package/src/app/api/openclaw/skills/route.ts +11 -3
- package/src/app/api/plugins/dependencies/route.ts +24 -0
- package/src/app/api/plugins/install/route.ts +15 -92
- package/src/app/api/plugins/route.ts +6 -26
- package/src/app/api/plugins/settings/route.ts +40 -0
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/settings/route.ts +49 -7
- package/src/app/api/tasks/[id]/route.ts +15 -6
- package/src/app/api/tasks/bulk/route.ts +2 -2
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/usage/route.ts +30 -0
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +9 -2
- package/src/cli/index.js +39 -33
- package/src/cli/index.ts +43 -49
- package/src/cli/spec.js +29 -27
- package/src/components/agents/agent-card.tsx +16 -13
- package/src/components/agents/agent-chat-list.tsx +104 -4
- package/src/components/agents/agent-list.tsx +54 -22
- package/src/components/agents/agent-sheet.tsx +209 -18
- package/src/components/agents/cron-job-form.tsx +3 -3
- package/src/components/agents/inspector-panel.tsx +110 -50
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +5 -38
- package/src/components/chat/chat-area.tsx +39 -27
- package/src/components/{sessions/session-card.tsx → chat/chat-card.tsx} +7 -23
- package/src/components/chat/chat-header.tsx +299 -314
- package/src/components/{sessions/session-list.tsx → chat/chat-list.tsx} +11 -14
- package/src/components/chat/chat-tool-toggles.tsx +26 -17
- package/src/components/chat/checkpoint-timeline.tsx +4 -4
- package/src/components/chat/message-bubble.tsx +4 -1
- package/src/components/chat/message-list.tsx +5 -3
- package/src/components/chat/session-debug-panel.tsx +1 -1
- package/src/components/chat/tool-request-banner.tsx +3 -3
- package/src/components/chatrooms/agent-hover-card.tsx +3 -3
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +2 -2
- package/src/components/chatrooms/chatroom-view.tsx +347 -205
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +218 -1
- package/src/components/home/home-view.tsx +129 -5
- package/src/components/layout/app-layout.tsx +392 -182
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/plugins/plugin-list.tsx +487 -254
- package/src/components/plugins/plugin-sheet.tsx +236 -13
- package/src/components/projects/project-detail.tsx +183 -0
- package/src/components/settings/gateway-connection-panel.tsx +1 -1
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/command-palette.tsx +111 -25
- package/src/components/shared/settings/plugin-manager.tsx +20 -4
- package/src/components/shared/settings/section-capability-policy.tsx +105 -0
- package/src/components/shared/settings/section-heartbeat.tsx +78 -1
- package/src/components/shared/settings/section-orchestrator.tsx +3 -3
- package/src/components/shared/settings/section-providers.tsx +1 -1
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
- package/src/components/shared/settings/section-secrets.tsx +6 -6
- package/src/components/shared/settings/section-user-preferences.tsx +1 -1
- package/src/components/shared/settings/section-voice.tsx +5 -1
- package/src/components/shared/settings/section-web-search.tsx +10 -2
- package/src/components/shared/settings/settings-page.tsx +244 -56
- package/src/components/tasks/approvals-panel.tsx +205 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/usage/metrics-dashboard.tsx +147 -1
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +8 -8
- package/src/lib/auth.ts +17 -0
- package/src/lib/chat-streaming-state.test.ts +108 -0
- package/src/lib/chat-streaming-state.ts +108 -0
- package/src/lib/chat.ts +1 -1
- package/src/lib/{sessions.ts → chats.ts} +28 -18
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/providers/claude-cli.ts +1 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +205 -0
- package/src/lib/server/approvals.ts +483 -75
- package/src/lib/server/autonomy-runtime.test.ts +341 -0
- package/src/lib/server/browser-state.test.ts +118 -0
- package/src/lib/server/browser-state.ts +123 -0
- package/src/lib/server/build-llm.test.ts +36 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/capability-router.ts +10 -8
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +134 -0
- package/src/lib/server/chat-execution.ts +285 -165
- package/src/lib/server/chatroom-health.test.ts +26 -0
- package/src/lib/server/chatroom-health.ts +2 -3
- package/src/lib/server/chatroom-helpers.test.ts +67 -2
- package/src/lib/server/chatroom-helpers.ts +48 -8
- package/src/lib/server/connectors/discord.ts +175 -11
- package/src/lib/server/connectors/doctor.test.ts +80 -0
- package/src/lib/server/connectors/doctor.ts +116 -0
- package/src/lib/server/connectors/manager.ts +948 -112
- package/src/lib/server/connectors/policy.test.ts +222 -0
- package/src/lib/server/connectors/policy.ts +452 -0
- package/src/lib/server/connectors/slack.ts +188 -9
- package/src/lib/server/connectors/telegram.ts +65 -15
- package/src/lib/server/connectors/thread-context.test.ts +44 -0
- package/src/lib/server/connectors/thread-context.ts +72 -0
- package/src/lib/server/connectors/types.ts +41 -11
- package/src/lib/server/cost.ts +34 -1
- package/src/lib/server/daemon-state.ts +61 -3
- package/src/lib/server/data-dir.ts +13 -0
- package/src/lib/server/delegation-jobs.test.ts +140 -0
- package/src/lib/server/delegation-jobs.ts +248 -0
- package/src/lib/server/document-utils.test.ts +47 -0
- package/src/lib/server/document-utils.ts +397 -0
- package/src/lib/server/heartbeat-service.ts +14 -40
- package/src/lib/server/heartbeat-source.test.ts +22 -0
- package/src/lib/server/heartbeat-source.ts +7 -0
- package/src/lib/server/identity-continuity.test.ts +77 -0
- package/src/lib/server/identity-continuity.ts +127 -0
- package/src/lib/server/mailbox-utils.ts +347 -0
- package/src/lib/server/main-agent-loop.ts +28 -1103
- package/src/lib/server/memory-db.ts +4 -6
- package/src/lib/server/memory-tiers.ts +40 -0
- package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
- package/src/lib/server/openclaw-agent-resolver.ts +128 -0
- package/src/lib/server/openclaw-exec-config.ts +5 -6
- package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
- package/src/lib/server/openclaw-skills-normalize.ts +136 -0
- package/src/lib/server/openclaw-sync.ts +3 -2
- package/src/lib/server/orchestrator-lg.ts +20 -9
- package/src/lib/server/orchestrator.ts +7 -7
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +207 -0
- package/src/lib/server/plugins.ts +927 -66
- package/src/lib/server/provider-health.ts +38 -6
- package/src/lib/server/queue.ts +13 -28
- package/src/lib/server/scheduler.ts +2 -0
- package/src/lib/server/session-archive-memory.test.ts +85 -0
- package/src/lib/server/session-archive-memory.ts +230 -0
- package/src/lib/server/session-mailbox.ts +8 -18
- package/src/lib/server/session-reset-policy.test.ts +99 -0
- package/src/lib/server/session-reset-policy.ts +311 -0
- package/src/lib/server/session-run-manager.ts +33 -82
- package/src/lib/server/session-tools/autonomy-tools.test.ts +105 -0
- package/src/lib/server/session-tools/calendar.ts +366 -0
- package/src/lib/server/session-tools/canvas.ts +1 -1
- package/src/lib/server/session-tools/chatroom.ts +4 -2
- package/src/lib/server/session-tools/connector.ts +114 -10
- package/src/lib/server/session-tools/context.ts +21 -5
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +74 -28
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +497 -24
- package/src/lib/server/session-tools/discovery.ts +24 -6
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/edit_file.ts +4 -2
- package/src/lib/server/session-tools/email.ts +320 -0
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +93 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +241 -25
- package/src/lib/server/session-tools/git.ts +1 -1
- package/src/lib/server/session-tools/http.ts +1 -1
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +380 -0
- package/src/lib/server/session-tools/index.ts +130 -50
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/memory.ts +172 -3
- package/src/lib/server/session-tools/monitor.ts +151 -8
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.ts +1 -1
- package/src/lib/server/session-tools/openclaw-workspace.ts +1 -1
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +148 -7
- package/src/lib/server/session-tools/plugin-creator.ts +89 -26
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +301 -0
- package/src/lib/server/session-tools/sample-ui.ts +1 -1
- package/src/lib/server/session-tools/sandbox.ts +4 -2
- package/src/lib/server/session-tools/schedule.ts +24 -12
- package/src/lib/server/session-tools/session-info.ts +43 -7
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +31 -17
- package/src/lib/server/session-tools/shell.ts +5 -2
- package/src/lib/server/session-tools/subagent.ts +194 -28
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +42 -12
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +926 -91
- package/src/lib/server/storage.ts +255 -16
- package/src/lib/server/stream-agent-chat.ts +116 -268
- package/src/lib/server/structured-extract.test.ts +72 -0
- package/src/lib/server/structured-extract.ts +373 -0
- package/src/lib/server/task-mention.test.ts +16 -2
- package/src/lib/server/task-mention.ts +61 -10
- package/src/lib/server/tool-aliases.ts +66 -18
- package/src/lib/server/tool-capability-policy.test.ts +9 -9
- package/src/lib/server/tool-capability-policy.ts +38 -27
- package/src/lib/server/tool-retry.ts +2 -0
- package/src/lib/server/watch-jobs.test.ts +173 -0
- package/src/lib/server/watch-jobs.ts +532 -0
- package/src/lib/server/ws-hub.ts +5 -3
- package/src/lib/tool-definitions.ts +4 -0
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +10 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +5 -11
- package/src/stores/use-chat-store.ts +38 -9
- package/src/types/index.ts +352 -47
- package/src/app/api/sessions/[id]/main-loop/route.ts +0 -94
- package/src/components/sessions/new-session-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -24
- package/src/lib/server/session-run-manager.test.ts +0 -23
- /package/src/app/api/{sessions → chats}/[id]/clear/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/deploy/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/devserver/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/edit-resend/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/fork/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/mailbox/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/retry/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/heartbeat/route.ts +0 -0
|
@@ -37,6 +37,7 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
37
37
|
const setCurrentSession = useAppStore((s) => s.setCurrentSession)
|
|
38
38
|
const setActiveView = useAppStore((s) => s.setActiveView)
|
|
39
39
|
const setMessages = useChatStore((s) => s.setMessages)
|
|
40
|
+
const sendMessage = useChatStore((s) => s.sendMessage)
|
|
40
41
|
const togglePinAgent = useAppStore((s) => s.togglePinAgent)
|
|
41
42
|
const [running, setRunning] = useState(false)
|
|
42
43
|
const [dialogOpen, setDialogOpen] = useState(false)
|
|
@@ -61,6 +62,7 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
61
62
|
budget: typeof agent.dailyBudget === 'number' && agent.dailyBudget > 0 ? agent.dailyBudget : null,
|
|
62
63
|
},
|
|
63
64
|
].filter((entry) => entry.budget !== null)
|
|
65
|
+
const canDelegateToAgents = agent.platformAssignScope === 'all'
|
|
64
66
|
useWs(`heartbeat:agent:${agent.id}`, () => {
|
|
65
67
|
setHeartbeatPulse(true)
|
|
66
68
|
setTimeout(() => setHeartbeatPulse(false), 1500)
|
|
@@ -78,19 +80,20 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
const handleConfirmRun = async () => {
|
|
81
|
-
|
|
83
|
+
const task = taskInput.trim()
|
|
84
|
+
if (!task) return
|
|
82
85
|
setDialogOpen(false)
|
|
83
86
|
setRunning(true)
|
|
84
87
|
try {
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
const session = await api<{ id: string }>('POST', `/agents/${agent.id}/thread`, { user: 'default' })
|
|
89
|
+
if (!session?.id) throw new Error('Agent thread not available')
|
|
90
|
+
await loadSessions()
|
|
91
|
+
setMessages([])
|
|
92
|
+
setCurrentSession(session.id)
|
|
93
|
+
setActiveView('agents')
|
|
94
|
+
await sendMessage(task)
|
|
92
95
|
} catch (err) {
|
|
93
|
-
console.error('
|
|
96
|
+
console.error('Agent task run failed:', err)
|
|
94
97
|
}
|
|
95
98
|
setRunning(false)
|
|
96
99
|
}
|
|
@@ -199,7 +202,7 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
199
202
|
default
|
|
200
203
|
</span>
|
|
201
204
|
)}
|
|
202
|
-
{
|
|
205
|
+
{canDelegateToAgents && (
|
|
203
206
|
<button
|
|
204
207
|
onClick={handleRunClick}
|
|
205
208
|
disabled={running}
|
|
@@ -210,7 +213,7 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
210
213
|
{running ? '...' : 'Run'}
|
|
211
214
|
</button>
|
|
212
215
|
)}
|
|
213
|
-
{
|
|
216
|
+
{canDelegateToAgents && (
|
|
214
217
|
<span className="shrink-0 text-[10px] font-600 uppercase tracking-wider text-amber-400/80 bg-amber-400/[0.08] px-2 py-0.5 rounded-[6px] flex items-center gap-1">
|
|
215
218
|
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round"><path d="M16 3h5v5"/><path d="M21 3l-7 7"/><path d="M8 21H3v-5"/><path d="M3 21l7-7"/></svg>
|
|
216
219
|
delegates
|
|
@@ -220,7 +223,7 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
220
223
|
<div className="text-[12px] text-text-3/70 mt-1.5 truncate">{agent.description}</div>
|
|
221
224
|
<div className="flex items-center gap-2 mt-1.5">
|
|
222
225
|
<span className="text-[11px] text-text-3/60 font-mono">{agent.model || agent.provider}</span>
|
|
223
|
-
{agent.
|
|
226
|
+
{agent.plugins?.includes('browser') && (
|
|
224
227
|
<span className="text-[10px] font-600 uppercase tracking-wider text-sky-400/70 bg-sky-400/[0.08] px-1.5 py-0.5 rounded-[5px]">
|
|
225
228
|
browser
|
|
226
229
|
</span>
|
|
@@ -299,7 +302,7 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
299
302
|
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
|
300
303
|
<DialogContent className="sm:max-w-[420px]">
|
|
301
304
|
<DialogHeader>
|
|
302
|
-
<DialogTitle>Run
|
|
305
|
+
<DialogTitle>Run Agent</DialogTitle>
|
|
303
306
|
</DialogHeader>
|
|
304
307
|
<div className="py-3">
|
|
305
308
|
<label className="block text-[12px] font-600 text-text-3 mb-2">Task for {agent.name}</label>
|
|
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } fr
|
|
|
4
4
|
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
|
-
import { fetchMessages } from '@/lib/
|
|
7
|
+
import { fetchMessages } from '@/lib/chats'
|
|
8
8
|
import { api } from '@/lib/api-client'
|
|
9
9
|
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
10
10
|
import type { Agent, Session } from '@/types'
|
|
@@ -58,7 +58,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
58
58
|
.filter(Boolean) as string[]
|
|
59
59
|
if (!sessionIds.length) { toast.error('No chats to delete'); return }
|
|
60
60
|
try {
|
|
61
|
-
await api('DELETE', '/
|
|
61
|
+
await api('DELETE', '/chats', { ids: sessionIds })
|
|
62
62
|
await loadSessions()
|
|
63
63
|
toast.success(`Deleted ${sessionIds.length} chat(s)`)
|
|
64
64
|
setBulkMode(false)
|
|
@@ -135,6 +135,17 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
135
135
|
})
|
|
136
136
|
}, [sortedAgents, chatFilter, sessions, runningAgentIds, streamingSessionId, chatroomActiveAgentIds])
|
|
137
137
|
|
|
138
|
+
const defaultAgent = useMemo(() => {
|
|
139
|
+
const id = appSettings.defaultAgentId
|
|
140
|
+
return id ? agents[id] || null : null
|
|
141
|
+
}, [appSettings.defaultAgentId, agents])
|
|
142
|
+
|
|
143
|
+
const defaultAgentVisible = !!defaultAgent && filteredAgents.some((agent) => agent.id === defaultAgent.id)
|
|
144
|
+
const listAgents = useMemo(
|
|
145
|
+
() => (defaultAgentVisible ? filteredAgents.filter((agent) => agent.id !== defaultAgent?.id) : filteredAgents),
|
|
146
|
+
[defaultAgent?.id, defaultAgentVisible, filteredAgents],
|
|
147
|
+
)
|
|
148
|
+
|
|
138
149
|
// FLIP: animate row position changes
|
|
139
150
|
useLayoutEffect(() => {
|
|
140
151
|
const prevTop = previousTopRef.current
|
|
@@ -258,11 +269,95 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
258
269
|
</div>
|
|
259
270
|
)}
|
|
260
271
|
<div className="flex flex-col gap-0.5 px-2 pb-4">
|
|
261
|
-
{
|
|
272
|
+
{defaultAgentVisible && defaultAgent && (() => {
|
|
273
|
+
const threadSession = defaultAgent.threadSessionId ? sessions[defaultAgent.threadSessionId] as Session | undefined : undefined
|
|
274
|
+
const lastMsg = threadSession?.messages?.at(-1)
|
|
275
|
+
const heartbeatOn = defaultAgent.heartbeatEnabled === true && (defaultAgent.plugins?.length ?? 0) > 0
|
|
276
|
+
const recentlyActive = (threadSession?.lastActiveAt ?? 0) > Date.now() - 30 * 60 * 1000
|
|
277
|
+
const isWorking = runningAgentIds.has(defaultAgent.id) || (threadSession?.active ?? false) || heartbeatOn || recentlyActive || chatroomActiveAgentIds.has(defaultAgent.id)
|
|
278
|
+
const isTyping = streamingSessionId === defaultAgent.threadSessionId
|
|
279
|
+
const preview = lastMsg?.text?.slice(0, 100)?.replace(/\n/g, ' ') || 'Your primary shortcut chat.'
|
|
280
|
+
const isActive = currentAgentId === defaultAgent.id
|
|
281
|
+
|
|
282
|
+
return (
|
|
283
|
+
<div className="mb-2 px-2">
|
|
284
|
+
<div className="px-2 pb-1 text-[10px] font-700 uppercase tracking-[0.12em] text-accent-bright/65">
|
|
285
|
+
Default Agent
|
|
286
|
+
</div>
|
|
287
|
+
<div
|
|
288
|
+
className={`group/row relative w-full text-left py-3.5 px-4 rounded-[14px] cursor-pointer transition-all duration-150 border
|
|
289
|
+
${isActive
|
|
290
|
+
? 'bg-accent-soft border-accent-bright/25'
|
|
291
|
+
: 'bg-accent-soft/40 border-accent-bright/15 hover:bg-accent-soft/55'}`}
|
|
292
|
+
onClick={() => bulkMode ? toggleSelected(defaultAgent.id) : handleSelect(defaultAgent)}
|
|
293
|
+
>
|
|
294
|
+
<div className="flex items-center gap-3">
|
|
295
|
+
{bulkMode && (
|
|
296
|
+
<div className={`w-5 h-5 rounded-[6px] border-2 flex items-center justify-center shrink-0 transition-colors
|
|
297
|
+
${selectedIds.has(defaultAgent.id) ? 'bg-accent-bright border-accent-bright' : 'border-white/20 bg-transparent'}`}>
|
|
298
|
+
{selectedIds.has(defaultAgent.id) && (
|
|
299
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
|
|
300
|
+
<polyline points="20 6 9 17 4 12" />
|
|
301
|
+
</svg>
|
|
302
|
+
)}
|
|
303
|
+
</div>
|
|
304
|
+
)}
|
|
305
|
+
<div className="relative shrink-0">
|
|
306
|
+
<AgentAvatar seed={defaultAgent.avatarSeed || null} avatarUrl={defaultAgent.avatarUrl} name={defaultAgent.name} size={38} />
|
|
307
|
+
<div className={`absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-bg ${
|
|
308
|
+
isWorking ? 'bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.4)]' : 'bg-text-3/30'
|
|
309
|
+
}`} />
|
|
310
|
+
</div>
|
|
311
|
+
<div className="flex-1 min-w-0">
|
|
312
|
+
<div className="flex items-center gap-2">
|
|
313
|
+
<span className="font-display text-[14px] font-700 truncate text-text tracking-[-0.01em]">
|
|
314
|
+
{defaultAgent.name}
|
|
315
|
+
</span>
|
|
316
|
+
<span className="px-1.5 py-0.5 rounded-[6px] bg-accent-bright/12 text-accent-bright text-[9px] font-700 uppercase tracking-[0.08em]">
|
|
317
|
+
Shortcut
|
|
318
|
+
</span>
|
|
319
|
+
</div>
|
|
320
|
+
{isTyping ? (
|
|
321
|
+
<div className="text-[12px] text-accent-bright/80 mt-1 flex items-center gap-1.5">
|
|
322
|
+
<span className="flex gap-0.5">
|
|
323
|
+
<span className="w-1 h-1 rounded-full bg-accent-bright/70 animate-bounce [animation-delay:0ms]" />
|
|
324
|
+
<span className="w-1 h-1 rounded-full bg-accent-bright/70 animate-bounce [animation-delay:150ms]" />
|
|
325
|
+
<span className="w-1 h-1 rounded-full bg-accent-bright/70 animate-bounce [animation-delay:300ms]" />
|
|
326
|
+
</span>
|
|
327
|
+
Typing...
|
|
328
|
+
</div>
|
|
329
|
+
) : (
|
|
330
|
+
<div className="text-[12px] text-text-3/70 mt-1 truncate">
|
|
331
|
+
{preview}
|
|
332
|
+
</div>
|
|
333
|
+
)}
|
|
334
|
+
</div>
|
|
335
|
+
<button
|
|
336
|
+
onClick={async (e) => {
|
|
337
|
+
e.stopPropagation()
|
|
338
|
+
await updateSettings({ defaultAgentId: null })
|
|
339
|
+
toast.success('Default agent cleared')
|
|
340
|
+
}}
|
|
341
|
+
aria-label="Remove as default agent"
|
|
342
|
+
title="Default agent — click to clear"
|
|
343
|
+
className="shrink-0 p-1 rounded-[6px] transition-all bg-transparent border-none cursor-pointer hover:bg-white/[0.06] text-accent-bright"
|
|
344
|
+
style={{ fontFamily: 'inherit' }}
|
|
345
|
+
>
|
|
346
|
+
<svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
347
|
+
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
|
348
|
+
<path d="M9 22V12h6v10" fill="rgba(0,0,0,0.3)" stroke="none" />
|
|
349
|
+
</svg>
|
|
350
|
+
</button>
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
)
|
|
355
|
+
})()}
|
|
356
|
+
{listAgents.map((agent) => {
|
|
262
357
|
const threadSession = agent.threadSessionId ? sessions[agent.threadSessionId] as Session | undefined : undefined
|
|
263
358
|
const lastMsg = threadSession?.messages?.at(-1)
|
|
264
359
|
const isActive = currentAgentId === agent.id
|
|
265
|
-
const heartbeatOn = agent.heartbeatEnabled === true && (agent.
|
|
360
|
+
const heartbeatOn = agent.heartbeatEnabled === true && (agent.plugins?.length ?? 0) > 0
|
|
266
361
|
const recentlyActive = (threadSession?.lastActiveAt ?? 0) > Date.now() - 30 * 60 * 1000
|
|
267
362
|
const isWorking = runningAgentIds.has(agent.id) || (threadSession?.active ?? false) || heartbeatOn || recentlyActive || chatroomActiveAgentIds.has(agent.id)
|
|
268
363
|
const isTyping = streamingSessionId === agent.threadSessionId
|
|
@@ -300,6 +395,11 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
300
395
|
<span className="font-display text-[13.5px] font-600 truncate flex-1 tracking-[-0.01em]">
|
|
301
396
|
{agent.name}
|
|
302
397
|
</span>
|
|
398
|
+
{appSettings.defaultAgentId === agent.id && (
|
|
399
|
+
<span className="px-1.5 py-0.5 rounded-[6px] bg-accent-bright/10 text-accent-bright text-[9px] font-700 uppercase tracking-[0.08em] shrink-0">
|
|
400
|
+
Default
|
|
401
|
+
</span>
|
|
402
|
+
)}
|
|
303
403
|
<span className="text-[10px] text-text-3/60 font-mono shrink-0">
|
|
304
404
|
{(threadSession?.model || agent.model)
|
|
305
405
|
? (threadSession?.model || agent.model)!.split('/').pop()?.split(':')[0]
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useLayoutEffect, useMemo, useRef, useState, useCallback } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
-
import { api } from '@/lib/api-client'
|
|
6
5
|
import { AgentCard } from './agent-card'
|
|
7
6
|
import { TrashList } from './trash-list'
|
|
8
7
|
import { useApprovalStore } from '@/stores/use-approval-store'
|
|
@@ -17,8 +16,6 @@ export function AgentList({ inSidebar }: Props) {
|
|
|
17
16
|
const agents = useAppStore((s) => s.agents)
|
|
18
17
|
const loadAgents = useAppStore((s) => s.loadAgents)
|
|
19
18
|
const sessions = useAppStore((s) => s.sessions)
|
|
20
|
-
const currentUser = useAppStore((s) => s.currentUser)
|
|
21
|
-
const loadSessions = useAppStore((s) => s.loadSessions)
|
|
22
19
|
const setAgentSheetOpen = useAppStore((s) => s.setAgentSheetOpen)
|
|
23
20
|
const activeProjectFilter = useAppStore((s) => s.activeProjectFilter)
|
|
24
21
|
const showTrash = useAppStore((s) => s.showTrash)
|
|
@@ -28,7 +25,7 @@ export function AgentList({ inSidebar }: Props) {
|
|
|
28
25
|
const currentSessionId = useAppStore((s) => s.currentSessionId)
|
|
29
26
|
const approvals = useApprovalStore((s) => s.approvals)
|
|
30
27
|
const [search, setSearch] = useState('')
|
|
31
|
-
const [filter, setFilter] = useState<'all' | '
|
|
28
|
+
const [filter, setFilter] = useState<'all' | 'delegating' | 'solo'>('all')
|
|
32
29
|
|
|
33
30
|
// FLIP animation refs
|
|
34
31
|
const flipPositions = useRef<Map<string, number>>(new Map())
|
|
@@ -37,19 +34,17 @@ export function AgentList({ inSidebar }: Props) {
|
|
|
37
34
|
const currentSession = currentSessionId ? sessions[currentSessionId] : null
|
|
38
35
|
const selectedAgentId = currentSession?.agentId
|
|
39
36
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
const appSettings = useAppStore((s) => s.appSettings)
|
|
38
|
+
const updateSettings = useAppStore((s) => s.updateSettings)
|
|
39
|
+
const defaultAgentId = (appSettings.defaultAgentId && agents[appSettings.defaultAgentId])
|
|
40
|
+
? appSettings.defaultAgentId
|
|
41
|
+
: Object.values(agents)[0]?.id || 'default'
|
|
45
42
|
|
|
46
43
|
const handleSetDefault = useCallback(async (agentId: string) => {
|
|
47
|
-
if (!mainSession) return
|
|
48
44
|
try {
|
|
49
|
-
await
|
|
50
|
-
await loadSessions()
|
|
45
|
+
await updateSettings({ defaultAgentId: agentId })
|
|
51
46
|
} catch { /* ignore */ }
|
|
52
|
-
}, [
|
|
47
|
+
}, [updateSettings])
|
|
53
48
|
|
|
54
49
|
const [loaded, setLoaded] = useState(Object.keys(agents).length > 0)
|
|
55
50
|
useEffect(() => { loadAgents().then(() => setLoaded(true)) }, [])
|
|
@@ -75,7 +70,7 @@ export function AgentList({ inSidebar }: Props) {
|
|
|
75
70
|
const ids = new Set<string>()
|
|
76
71
|
const recentThreshold = now - 30 * 60 * 1000
|
|
77
72
|
for (const a of Object.values(agents)) {
|
|
78
|
-
if (a.heartbeatEnabled === true && (a.
|
|
73
|
+
if (a.heartbeatEnabled === true && (a.plugins?.length ?? 0) > 0) { ids.add(a.id); continue }
|
|
79
74
|
// Check if any session for this agent was active in the last 30 minutes
|
|
80
75
|
for (const s of Object.values(sessions)) {
|
|
81
76
|
if (s.agentId === a.id && (s.lastActiveAt ?? 0) > recentThreshold) { ids.add(a.id); break }
|
|
@@ -93,12 +88,22 @@ export function AgentList({ inSidebar }: Props) {
|
|
|
93
88
|
return counts
|
|
94
89
|
}, [approvals])
|
|
95
90
|
|
|
91
|
+
const delegatingCount = useMemo(
|
|
92
|
+
() => Object.values(agents).filter((agent) => agent.platformAssignScope === 'all' && !agent.trashedAt).length,
|
|
93
|
+
[agents],
|
|
94
|
+
)
|
|
95
|
+
const soloCount = useMemo(
|
|
96
|
+
() => Object.values(agents).filter((agent) => agent.platformAssignScope !== 'all' && !agent.trashedAt).length,
|
|
97
|
+
[agents],
|
|
98
|
+
)
|
|
99
|
+
|
|
96
100
|
const filtered = useMemo(() => {
|
|
97
101
|
return Object.values(agents)
|
|
98
102
|
.filter((p) => {
|
|
99
103
|
if (search && !p.name.toLowerCase().includes(search.toLowerCase())) return false
|
|
100
|
-
|
|
101
|
-
if (filter === '
|
|
104
|
+
const canDelegateToAgents = p.platformAssignScope === 'all'
|
|
105
|
+
if (filter === 'delegating' && !canDelegateToAgents) return false
|
|
106
|
+
if (filter === 'solo' && canDelegateToAgents) return false
|
|
102
107
|
if (activeProjectFilter && p.projectId !== activeProjectFilter) return false
|
|
103
108
|
// Fleet filter
|
|
104
109
|
if (fleetFilter === 'running' && !runningAgentIds.has(p.id)) return false
|
|
@@ -176,7 +181,7 @@ export function AgentList({ inSidebar }: Props) {
|
|
|
176
181
|
</svg>
|
|
177
182
|
}
|
|
178
183
|
title="No agents yet"
|
|
179
|
-
subtitle="Create AI agents and
|
|
184
|
+
subtitle="Create AI agents and enable delegation where needed"
|
|
180
185
|
action={!inSidebar ? { label: '+ New Agent', onClick: () => setAgentSheetOpen(true) } : undefined}
|
|
181
186
|
/>
|
|
182
187
|
)
|
|
@@ -217,15 +222,19 @@ export function AgentList({ inSidebar }: Props) {
|
|
|
217
222
|
})}
|
|
218
223
|
</div>
|
|
219
224
|
<div className="flex gap-1 px-4 pb-2 items-center">
|
|
220
|
-
{([
|
|
225
|
+
{([
|
|
226
|
+
['all', `all (${delegatingCount + soloCount})`],
|
|
227
|
+
['delegating', `delegating (${delegatingCount})`],
|
|
228
|
+
['solo', `solo (${soloCount})`],
|
|
229
|
+
] as const).map(([value, label]) => (
|
|
221
230
|
<button
|
|
222
|
-
key={
|
|
223
|
-
onClick={() => setFilter(
|
|
231
|
+
key={value}
|
|
232
|
+
onClick={() => setFilter(value)}
|
|
224
233
|
className={`px-3 py-1.5 rounded-[8px] text-[11px] font-600 capitalize cursor-pointer transition-all
|
|
225
|
-
${filter ===
|
|
234
|
+
${filter === value ? 'bg-accent-soft text-accent-bright' : 'bg-transparent text-text-3 hover:text-text-2'}`}
|
|
226
235
|
style={{ fontFamily: 'inherit' }}
|
|
227
236
|
>
|
|
228
|
-
{
|
|
237
|
+
{label}
|
|
229
238
|
</button>
|
|
230
239
|
))}
|
|
231
240
|
<div className="flex-1" />
|
|
@@ -240,6 +249,29 @@ export function AgentList({ inSidebar }: Props) {
|
|
|
240
249
|
</svg>
|
|
241
250
|
</button>
|
|
242
251
|
</div>
|
|
252
|
+
{!inSidebar && (
|
|
253
|
+
<div className="mx-4 mb-3 rounded-[14px] border border-white/[0.06] bg-white/[0.02] px-4 py-3">
|
|
254
|
+
<div className="flex flex-col gap-2 lg:flex-row lg:items-center lg:justify-between">
|
|
255
|
+
<div>
|
|
256
|
+
<h3 className="text-[12px] font-700 uppercase tracking-[0.08em] text-text-3/60">Fleet Roles</h3>
|
|
257
|
+
<p className="text-[12px] text-text-3/65 mt-1">
|
|
258
|
+
Delegating agents can hand work to other agents. Solo agents stay on their own thread and tools.
|
|
259
|
+
</p>
|
|
260
|
+
</div>
|
|
261
|
+
<div className="flex flex-wrap gap-2">
|
|
262
|
+
<span className="px-2.5 py-1 rounded-[8px] bg-white/[0.04] text-[11px] font-600 text-text-2">
|
|
263
|
+
Default: {agents[defaultAgentId]?.name || 'Unset'}
|
|
264
|
+
</span>
|
|
265
|
+
<span className="px-2.5 py-1 rounded-[8px] bg-sky-500/10 text-[11px] font-600 text-sky-400">
|
|
266
|
+
{delegatingCount} delegating
|
|
267
|
+
</span>
|
|
268
|
+
<span className="px-2.5 py-1 rounded-[8px] bg-white/[0.04] text-[11px] font-600 text-text-2">
|
|
269
|
+
{soloCount} solo
|
|
270
|
+
</span>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
)}
|
|
243
275
|
<div className="flex flex-col gap-1 px-2 pb-4">
|
|
244
276
|
{filtered.map((p) => (
|
|
245
277
|
<div key={p.id} ref={(el) => { if (el) cardRefs.current.set(p.id, el); else cardRefs.current.delete(p.id) }}>
|