@swarmclawai/swarmclaw 0.6.7 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -39
- package/next.config.ts +31 -6
- package/package.json +3 -2
- package/src/app/api/agents/[id]/thread/route.ts +1 -0
- package/src/app/api/agents/route.ts +19 -5
- package/src/app/api/approvals/route.ts +22 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -0
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/eval/run/route.ts +37 -0
- package/src/app/api/eval/scenarios/route.ts +24 -0
- package/src/app/api/eval/suite/route.ts +29 -0
- package/src/app/api/mcp-servers/[id]/conformance/route.ts +26 -0
- package/src/app/api/mcp-servers/[id]/invoke/route.ts +81 -0
- package/src/app/api/memory/graph/route.ts +46 -0
- package/src/app/api/memory/route.ts +36 -5
- package/src/app/api/notifications/route.ts +3 -0
- package/src/app/api/plugins/install/route.ts +57 -5
- package/src/app/api/plugins/marketplace/route.ts +73 -22
- package/src/app/api/plugins/route.ts +61 -1
- package/src/app/api/plugins/ui/route.ts +34 -0
- package/src/app/api/sessions/[id]/checkpoints/route.ts +31 -0
- package/src/app/api/sessions/[id]/restore/route.ts +36 -0
- package/src/app/api/settings/route.ts +62 -0
- package/src/app/api/setup/doctor/route.ts +22 -5
- package/src/app/api/souls/[id]/route.ts +65 -0
- package/src/app/api/souls/route.ts +70 -0
- package/src/app/api/tasks/[id]/approve/route.ts +4 -3
- package/src/app/api/tasks/[id]/route.ts +16 -3
- package/src/app/api/tasks/route.ts +10 -2
- package/src/app/api/usage/route.ts +9 -2
- package/src/app/globals.css +27 -0
- package/src/app/page.tsx +10 -5
- package/src/cli/index.js +37 -0
- package/src/components/activity/activity-feed.tsx +9 -2
- package/src/components/agents/agent-avatar.tsx +5 -1
- package/src/components/agents/agent-card.tsx +55 -9
- package/src/components/agents/agent-sheet.tsx +112 -34
- package/src/components/agents/inspector-panel.tsx +1 -1
- package/src/components/agents/soul-library-picker.tsx +84 -13
- package/src/components/auth/access-key-gate.tsx +63 -54
- package/src/components/auth/user-picker.tsx +37 -32
- package/src/components/chat/activity-moment.tsx +2 -0
- package/src/components/chat/chat-area.tsx +11 -0
- package/src/components/chat/chat-header.tsx +69 -25
- package/src/components/chat/chat-tool-toggles.tsx +2 -2
- package/src/components/chat/checkpoint-timeline.tsx +112 -0
- package/src/components/chat/code-block.tsx +3 -1
- package/src/components/chat/exec-approval-card.tsx +8 -1
- package/src/components/chat/message-bubble.tsx +164 -4
- package/src/components/chat/message-list.tsx +46 -4
- package/src/components/chat/session-approval-card.tsx +80 -0
- package/src/components/chat/session-debug-panel.tsx +106 -84
- package/src/components/chat/streaming-bubble.tsx +6 -5
- package/src/components/chat/task-approval-card.tsx +78 -0
- package/src/components/chat/thinking-indicator.tsx +48 -12
- package/src/components/chat/tool-call-bubble.tsx +3 -0
- package/src/components/chat/tool-request-banner.tsx +39 -20
- package/src/components/chatrooms/chatroom-list.tsx +11 -4
- package/src/components/chatrooms/chatroom-sheet.tsx +7 -2
- package/src/components/connectors/connector-list.tsx +33 -11
- package/src/components/connectors/connector-sheet.tsx +37 -7
- package/src/components/home/home-view.tsx +54 -24
- package/src/components/input/chat-input.tsx +22 -1
- package/src/components/knowledge/knowledge-list.tsx +17 -18
- package/src/components/knowledge/knowledge-sheet.tsx +9 -5
- package/src/components/layout/app-layout.tsx +87 -19
- package/src/components/mcp-servers/mcp-server-list.tsx +352 -50
- package/src/components/mcp-servers/mcp-server-sheet.tsx +25 -9
- package/src/components/memory/memory-browser.tsx +73 -45
- package/src/components/memory/memory-graph-view.tsx +203 -0
- package/src/components/memory/memory-list.tsx +20 -13
- package/src/components/plugins/plugin-list.tsx +214 -60
- package/src/components/plugins/plugin-sheet.tsx +119 -24
- package/src/components/projects/project-list.tsx +17 -9
- package/src/components/providers/provider-list.tsx +21 -6
- package/src/components/providers/provider-sheet.tsx +42 -25
- package/src/components/runs/run-list.tsx +17 -13
- package/src/components/schedules/schedule-card.tsx +10 -3
- package/src/components/schedules/schedule-list.tsx +2 -2
- package/src/components/schedules/schedule-sheet.tsx +28 -9
- package/src/components/secrets/secret-sheet.tsx +7 -2
- package/src/components/secrets/secrets-list.tsx +18 -5
- package/src/components/sessions/new-session-sheet.tsx +183 -376
- package/src/components/sessions/session-card.tsx +10 -2
- package/src/components/settings/gateway-connection-panel.tsx +9 -8
- package/src/components/shared/command-palette.tsx +13 -5
- package/src/components/shared/empty-state.tsx +20 -8
- package/src/components/shared/hint-tip.tsx +31 -0
- package/src/components/shared/notification-center.tsx +134 -86
- package/src/components/shared/profile-sheet.tsx +4 -0
- package/src/components/shared/settings/plugin-manager.tsx +360 -135
- package/src/components/shared/settings/section-capability-policy.tsx +3 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +149 -4
- package/src/components/skills/clawhub-browser.tsx +1 -0
- package/src/components/skills/skill-list.tsx +31 -12
- package/src/components/skills/skill-sheet.tsx +20 -7
- package/src/components/tasks/approvals-panel.tsx +224 -0
- package/src/components/tasks/task-board.tsx +20 -12
- package/src/components/tasks/task-card.tsx +21 -7
- package/src/components/tasks/task-column.tsx +4 -3
- package/src/components/tasks/task-list.tsx +1 -1
- package/src/components/tasks/task-sheet.tsx +130 -1
- package/src/components/ui/dialog.tsx +1 -0
- package/src/components/ui/sheet.tsx +1 -0
- package/src/components/usage/metrics-dashboard.tsx +72 -48
- package/src/components/wallets/wallet-panel.tsx +65 -41
- package/src/components/wallets/wallet-section.tsx +9 -3
- package/src/components/webhooks/webhook-list.tsx +21 -12
- package/src/components/webhooks/webhook-sheet.tsx +13 -3
- package/src/lib/approval-display.test.ts +45 -0
- package/src/lib/approval-display.ts +62 -0
- package/src/lib/clipboard.ts +38 -0
- package/src/lib/memory.ts +8 -0
- package/src/lib/providers/claude-cli.ts +5 -3
- package/src/lib/providers/index.ts +67 -21
- package/src/lib/runtime-loop.ts +3 -2
- package/src/lib/server/approvals.ts +150 -0
- package/src/lib/server/chat-execution.ts +319 -74
- package/src/lib/server/chatroom-helpers.ts +63 -5
- package/src/lib/server/chatroom-orchestration.ts +74 -0
- package/src/lib/server/clawhub-client.ts +82 -6
- package/src/lib/server/connectors/manager.ts +27 -1
- package/src/lib/server/context-manager.ts +132 -50
- package/src/lib/server/cost.test.ts +73 -0
- package/src/lib/server/cost.ts +165 -34
- package/src/lib/server/daemon-state.ts +112 -1
- package/src/lib/server/data-dir.ts +18 -1
- package/src/lib/server/eval/runner.ts +126 -0
- package/src/lib/server/eval/scenarios.ts +218 -0
- package/src/lib/server/eval/scorer.ts +96 -0
- package/src/lib/server/eval/store.ts +37 -0
- package/src/lib/server/eval/types.ts +48 -0
- package/src/lib/server/execution-log.ts +12 -8
- package/src/lib/server/guardian.ts +34 -0
- package/src/lib/server/heartbeat-service.ts +53 -1
- package/src/lib/server/integrity-monitor.ts +208 -0
- package/src/lib/server/langgraph-checkpoint.ts +10 -0
- package/src/lib/server/link-understanding.ts +55 -0
- package/src/lib/server/llm-response-cache.test.ts +102 -0
- package/src/lib/server/llm-response-cache.ts +227 -0
- package/src/lib/server/main-agent-loop.ts +115 -16
- package/src/lib/server/main-session.ts +6 -3
- package/src/lib/server/mcp-conformance.test.ts +18 -0
- package/src/lib/server/mcp-conformance.ts +233 -0
- package/src/lib/server/memory-db.ts +193 -19
- package/src/lib/server/memory-retrieval.test.ts +56 -0
- package/src/lib/server/mmr.ts +73 -0
- package/src/lib/server/orchestrator-lg.ts +7 -1
- package/src/lib/server/orchestrator.ts +4 -3
- package/src/lib/server/plugins.ts +662 -132
- package/src/lib/server/process-manager.ts +18 -0
- package/src/lib/server/query-expansion.ts +57 -0
- package/src/lib/server/queue.ts +280 -11
- package/src/lib/server/runtime-settings.ts +9 -0
- package/src/lib/server/session-run-manager.test.ts +23 -0
- package/src/lib/server/session-run-manager.ts +32 -2
- package/src/lib/server/session-tools/canvas.ts +85 -50
- package/src/lib/server/session-tools/chatroom.ts +130 -127
- package/src/lib/server/session-tools/connector.ts +233 -454
- package/src/lib/server/session-tools/context-mgmt.ts +87 -105
- package/src/lib/server/session-tools/crud.ts +84 -7
- package/src/lib/server/session-tools/delegate.ts +351 -752
- package/src/lib/server/session-tools/discovery.ts +198 -0
- package/src/lib/server/session-tools/edit_file.ts +82 -0
- package/src/lib/server/session-tools/file-send.test.ts +39 -0
- package/src/lib/server/session-tools/file.ts +257 -425
- package/src/lib/server/session-tools/git.ts +87 -47
- package/src/lib/server/session-tools/http.ts +95 -33
- package/src/lib/server/session-tools/index.ts +217 -138
- package/src/lib/server/session-tools/memory.ts +154 -239
- package/src/lib/server/session-tools/monitor.ts +126 -0
- package/src/lib/server/session-tools/normalize-tool-args.test.ts +61 -0
- package/src/lib/server/session-tools/normalize-tool-args.ts +48 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +82 -99
- package/src/lib/server/session-tools/openclaw-workspace.ts +103 -93
- package/src/lib/server/session-tools/platform.ts +86 -0
- package/src/lib/server/session-tools/plugin-creator.ts +239 -0
- package/src/lib/server/session-tools/sample-ui.ts +97 -0
- package/src/lib/server/session-tools/sandbox.ts +175 -148
- package/src/lib/server/session-tools/schedule.ts +78 -0
- package/src/lib/server/session-tools/session-info.ts +104 -410
- package/src/lib/server/session-tools/shell-normalize.test.ts +43 -0
- package/src/lib/server/session-tools/shell.ts +171 -143
- package/src/lib/server/session-tools/subagent.ts +77 -77
- package/src/lib/server/session-tools/wallet.ts +182 -106
- package/src/lib/server/session-tools/web.ts +181 -327
- package/src/lib/server/storage.ts +36 -0
- package/src/lib/server/stream-agent-chat.ts +348 -242
- package/src/lib/server/task-quality-gate.test.ts +44 -0
- package/src/lib/server/task-quality-gate.ts +67 -0
- package/src/lib/server/task-validation.test.ts +78 -0
- package/src/lib/server/task-validation.ts +67 -2
- package/src/lib/server/tool-aliases.ts +68 -0
- package/src/lib/server/tool-capability-policy.ts +24 -5
- package/src/lib/server/tool-retry.ts +62 -0
- package/src/lib/server/transcript-repair.ts +72 -0
- package/src/lib/setup-defaults.ts +1 -0
- package/src/lib/tasks.ts +7 -1
- package/src/lib/tool-definitions.ts +24 -23
- package/src/lib/validation/schemas.ts +13 -0
- package/src/lib/view-routes.ts +2 -23
- package/src/stores/use-app-store.ts +23 -1
- package/src/types/index.ts +155 -10
|
@@ -1,341 +1,163 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
-
import { useChatStore } from '@/stores/use-chat-store'
|
|
6
|
-
import { createSession, createCredential } from '@/lib/sessions'
|
|
3
|
+
import { useState, useMemo } from 'react'
|
|
7
4
|
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
8
|
-
import {
|
|
5
|
+
import { SectionLabel } from '@/components/shared/section-label'
|
|
6
|
+
import { useAppStore } from '@/stores/use-app-store'
|
|
7
|
+
import { api } from '@/lib/api-client'
|
|
8
|
+
import { PROVIDERS } from '@/lib/providers'
|
|
9
9
|
import { TOOL_LABELS, TOOL_DESCRIPTIONS } from '@/components/chat/tool-call-bubble'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { inputClass } from '@/components/shared/form-styles'
|
|
10
|
+
import { toast } from 'sonner'
|
|
11
|
+
import { useRouter } from 'next/navigation'
|
|
12
|
+
import { genId } from '@/lib/id'
|
|
14
13
|
import type { ProviderType, SessionTool } from '@/types'
|
|
15
|
-
import { SectionLabel } from '@/components/shared/section-label'
|
|
16
|
-
import { safeStorageGet, safeStorageRemove, safeStorageSet } from '@/lib/safe-storage'
|
|
17
|
-
|
|
18
|
-
export function NewSessionSheet() {
|
|
19
|
-
const open = useAppStore((s) => s.newSessionOpen)
|
|
20
|
-
const setOpen = useAppStore((s) => s.setNewSessionOpen)
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const [model, setModel] = useState('')
|
|
27
|
-
const [credentialId, setCredentialId] = useState<string | null>(null)
|
|
28
|
-
const [endpoint, setEndpoint] = useState('http://localhost:11434')
|
|
29
|
-
const [addingKey, setAddingKey] = useState(false)
|
|
30
|
-
const [newKeyName, setNewKeyName] = useState('')
|
|
31
|
-
const [newKeyValue, setNewKeyValue] = useState('')
|
|
32
|
-
const [ollamaMode, setOllamaMode] = useState<'local' | 'cloud'>('local')
|
|
33
|
-
const [selectedAgentId, setSelectedAgentId] = useState<string | null>(null)
|
|
34
|
-
const [selectedTools, setSelectedTools] = useState<SessionTool[]>([])
|
|
15
|
+
interface Props {
|
|
16
|
+
open: boolean
|
|
17
|
+
onClose: () => void
|
|
18
|
+
}
|
|
35
19
|
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
const credentials = useAppStore((s) => s.credentials)
|
|
39
|
-
const loadCredentials = useAppStore((s) => s.loadCredentials)
|
|
20
|
+
export function NewSessionSheet({ open, onClose }: Props) {
|
|
21
|
+
const router = useRouter()
|
|
40
22
|
const agents = useAppStore((s) => s.agents)
|
|
41
|
-
const
|
|
42
|
-
const currentUser = useAppStore((s) => s.currentUser)
|
|
43
|
-
const updateSessionInStore = useAppStore((s) => s.updateSessionInStore)
|
|
44
|
-
const setCurrentSession = useAppStore((s) => s.setCurrentSession)
|
|
45
|
-
const setMessages = useChatStore((s) => s.setMessages)
|
|
46
|
-
|
|
47
|
-
const currentProvider = providers.find((p) => p.id === provider)
|
|
48
|
-
const providerCredentials = Object.values(credentials).filter((c) => c.provider === provider)
|
|
49
|
-
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (open) {
|
|
52
|
-
loadProviders()
|
|
53
|
-
loadCredentials()
|
|
54
|
-
loadAgents()
|
|
55
|
-
setName('')
|
|
56
|
-
setSelectedDir(null)
|
|
57
|
-
setSelectedFile(null)
|
|
58
|
-
setProvider('claude-cli')
|
|
59
|
-
setModel('')
|
|
60
|
-
setCredentialId(null)
|
|
61
|
-
setEndpoint('http://localhost:11434')
|
|
62
|
-
setAddingKey(false)
|
|
63
|
-
setNewKeyName('')
|
|
64
|
-
setNewKeyValue('')
|
|
65
|
-
setOllamaMode('local')
|
|
66
|
-
// Auto-select last used agent, or default agent if no history
|
|
67
|
-
const agentsList = Object.values(agents)
|
|
68
|
-
const lastAgentId = safeStorageGet('swarmclaw-last-agent')
|
|
69
|
-
const lastAgent = lastAgentId ? agentsList.find((a) => a.id === lastAgentId) : null
|
|
70
|
-
const defaultAgent = lastAgent || agentsList.find((a) => a.id === 'default') || agentsList[0]
|
|
71
|
-
if (defaultAgent) {
|
|
72
|
-
setSelectedAgentId(defaultAgent.id)
|
|
73
|
-
setProvider(defaultAgent.provider || 'claude-cli')
|
|
74
|
-
setModel(defaultAgent.model || '')
|
|
75
|
-
setCredentialId(defaultAgent.credentialId || null)
|
|
76
|
-
if (defaultAgent.apiEndpoint) setEndpoint(defaultAgent.apiEndpoint)
|
|
77
|
-
} else {
|
|
78
|
-
setSelectedAgentId(null)
|
|
79
|
-
}
|
|
80
|
-
setSelectedTools([])
|
|
81
|
-
}
|
|
82
|
-
}, [open])
|
|
23
|
+
const loadSessions = useAppStore((s) => s.loadSessions)
|
|
83
24
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
// Reset ollama mode for non-ollama providers
|
|
92
|
-
if (provider !== 'ollama') {
|
|
93
|
-
setOllamaMode('local')
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Derive endpoint
|
|
97
|
-
if (provider === 'ollama') {
|
|
98
|
-
setEndpoint(ollamaMode === 'local' ? 'http://localhost:11434' : '')
|
|
99
|
-
} else if (currentProvider?.defaultEndpoint) {
|
|
100
|
-
setEndpoint(currentProvider.defaultEndpoint)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Derive credential
|
|
104
|
-
const needsKey = currentProvider?.requiresApiKey || (provider === 'ollama' && ollamaMode === 'cloud')
|
|
105
|
-
if (needsKey && providerCredentials.length > 0) {
|
|
106
|
-
setCredentialId(providerCredentials[0].id)
|
|
107
|
-
} else {
|
|
108
|
-
setCredentialId(null)
|
|
109
|
-
}
|
|
110
|
-
}, [provider, providers, ollamaMode, providerCredentials.length])
|
|
111
|
-
|
|
112
|
-
const handleAddKey = async () => {
|
|
113
|
-
if (!newKeyValue.trim()) return
|
|
114
|
-
const cred = await createCredential(provider, newKeyName || `${provider} key`, newKeyValue)
|
|
115
|
-
await loadCredentials()
|
|
116
|
-
setCredentialId(cred.id)
|
|
117
|
-
setAddingKey(false)
|
|
118
|
-
setNewKeyName('')
|
|
119
|
-
setNewKeyValue('')
|
|
120
|
-
}
|
|
25
|
+
const [selectedAgentId, setSelectedAgentId] = useState<string>('')
|
|
26
|
+
const [provider, setProvider] = useState<ProviderType>('openai')
|
|
27
|
+
const [model, setModel] = useState<string>('')
|
|
28
|
+
const [endpoint, setEndpoint] = useState('')
|
|
29
|
+
const [selectedTools, setSelectedTools] = useState<SessionTool[]>([])
|
|
30
|
+
const [loading, setLoading] = useState(false)
|
|
121
31
|
|
|
122
|
-
const
|
|
32
|
+
const agentList = useMemo(() => Object.values(agents).sort((a, b) => b.updatedAt - a.updatedAt), [agents])
|
|
33
|
+
const currentProvider = PROVIDERS[provider]
|
|
123
34
|
|
|
124
|
-
const
|
|
125
|
-
setSelectedAgentId(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
setCredentialId(p.credentialId || null)
|
|
131
|
-
if (p.apiEndpoint) setEndpoint(p.apiEndpoint)
|
|
132
|
-
if (!name) setName(p.name)
|
|
133
|
-
}
|
|
35
|
+
const reset = () => {
|
|
36
|
+
setSelectedAgentId('')
|
|
37
|
+
setProvider('openai')
|
|
38
|
+
setModel('')
|
|
39
|
+
setEndpoint('')
|
|
40
|
+
setSelectedTools([])
|
|
134
41
|
}
|
|
135
42
|
|
|
136
43
|
const handleCreate = async () => {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
safeStorageRemove('swarmclaw-last-agent')
|
|
160
|
-
}
|
|
161
|
-
updateSessionInStore(s)
|
|
162
|
-
setCurrentSession(s.id)
|
|
163
|
-
setMessages([])
|
|
164
|
-
onClose()
|
|
165
|
-
}
|
|
44
|
+
setLoading(true)
|
|
45
|
+
try {
|
|
46
|
+
const agent = selectedAgentId ? agents[selectedAgentId] : null
|
|
47
|
+
const id = genId(8)
|
|
48
|
+
const now = Date.now()
|
|
49
|
+
|
|
50
|
+
const agentTools = agent?.tools || (selectedTools.length ? selectedTools : undefined)
|
|
51
|
+
|
|
52
|
+
const session = {
|
|
53
|
+
id,
|
|
54
|
+
name: agent ? `Chat with ${agent.name}` : `New Session (${model || provider})`,
|
|
55
|
+
provider: agent ? agent.provider : provider,
|
|
56
|
+
model: agent ? agent.model : model,
|
|
57
|
+
apiEndpoint: agent ? agent.apiEndpoint : (endpoint || undefined),
|
|
58
|
+
credentialId: agent ? agent.credentialId : undefined,
|
|
59
|
+
tools: agentTools || undefined,
|
|
60
|
+
messages: [],
|
|
61
|
+
createdAt: now,
|
|
62
|
+
updatedAt: now,
|
|
63
|
+
active: true,
|
|
64
|
+
agentId: selectedAgentId || undefined,
|
|
65
|
+
}
|
|
166
66
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
67
|
+
await api('POST', '/sessions', session)
|
|
68
|
+
await loadSessions()
|
|
69
|
+
router.push(`/chat?session=${id}`)
|
|
70
|
+
onClose()
|
|
71
|
+
reset()
|
|
72
|
+
} catch (err: unknown) {
|
|
73
|
+
toast.error(err instanceof Error ? err.message : 'Failed to create session')
|
|
74
|
+
} finally {
|
|
75
|
+
setLoading(false)
|
|
172
76
|
}
|
|
173
|
-
return true
|
|
174
77
|
}
|
|
175
78
|
|
|
79
|
+
const inputClass = "w-full py-3 px-4 rounded-[14px] bg-surface border border-white/[0.06] text-text placeholder:text-text-3/50 outline-none focus:border-accent-bright/30 transition-all"
|
|
80
|
+
|
|
176
81
|
return (
|
|
177
|
-
<BottomSheet open={open} onClose={onClose}
|
|
178
|
-
{/* Header */}
|
|
82
|
+
<BottomSheet open={open} onClose={onClose}>
|
|
179
83
|
<div className="mb-10">
|
|
180
|
-
<h2 className="font-display text-[28px] font-700 tracking-[-0.03em] mb-2">New
|
|
181
|
-
<p className="text-[14px] text-text-3">
|
|
84
|
+
<h2 className="font-display text-[28px] font-700 tracking-[-0.03em] mb-2">New Session</h2>
|
|
85
|
+
<p className="text-[14px] text-text-3">Start a new conversation with an agent or a direct model.</p>
|
|
182
86
|
</div>
|
|
183
87
|
|
|
184
|
-
{/* Name */}
|
|
185
88
|
<div className="mb-8">
|
|
186
|
-
<SectionLabel>
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
89
|
+
<SectionLabel>Select Agent</SectionLabel>
|
|
90
|
+
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
|
91
|
+
<button
|
|
92
|
+
onClick={() => setSelectedAgentId('')}
|
|
93
|
+
className={`flex flex-col items-center justify-center gap-2 p-4 rounded-[18px] border transition-all duration-200 cursor-pointer
|
|
94
|
+
${!selectedAgentId
|
|
95
|
+
? 'bg-accent-soft border-accent-bright/25 text-accent-bright shadow-[0_0_25px_rgba(99,102,241,0.12)]'
|
|
96
|
+
: 'bg-surface border-white/[0.06] text-text-3 hover:bg-surface-2 hover:border-white/[0.08]'}`}
|
|
97
|
+
>
|
|
98
|
+
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${!selectedAgentId ? 'bg-accent-bright/20' : 'bg-white/[0.04]'}`}>
|
|
99
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
100
|
+
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" />
|
|
101
|
+
</svg>
|
|
102
|
+
</div>
|
|
103
|
+
<span className="text-[13px] font-600">Direct Model</span>
|
|
104
|
+
</button>
|
|
105
|
+
|
|
106
|
+
{agentList.map((a) => (
|
|
107
|
+
<button
|
|
108
|
+
key={a.id}
|
|
109
|
+
onClick={() => setSelectedAgentId(a.id)}
|
|
110
|
+
className={`flex flex-col items-center justify-center gap-2 p-4 rounded-[18px] border transition-all duration-200 cursor-pointer
|
|
111
|
+
${selectedAgentId === a.id
|
|
112
|
+
? 'bg-accent-soft border-accent-bright/25 text-accent-bright shadow-[0_0_25px_rgba(99,102,241,0.12)]'
|
|
113
|
+
: 'bg-surface border-white/[0.06] text-text-3 hover:bg-surface-2 hover:border-white/[0.08]'}`}
|
|
114
|
+
>
|
|
115
|
+
<div className="w-10 h-10 rounded-full bg-accent-bright/10 overflow-hidden">
|
|
116
|
+
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
117
|
+
<img src={`https://api.dicebear.com/7.x/bottts-neutral/svg?seed=${a.avatarSeed || a.id}`} alt="" />
|
|
118
|
+
</div>
|
|
119
|
+
<span className="text-[13px] font-600 truncate w-full text-center px-1">{a.name}</span>
|
|
120
|
+
</button>
|
|
121
|
+
))}
|
|
208
122
|
</div>
|
|
209
|
-
|
|
123
|
+
</div>
|
|
210
124
|
|
|
211
|
-
{/* Provider/Model/Key/Endpoint — only show when no agent selected */}
|
|
212
125
|
{!selectedAgentId && (
|
|
213
126
|
<>
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
{p.name}
|
|
230
|
-
</button>
|
|
231
|
-
))}
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
234
|
-
|
|
235
|
-
{/* Ollama Mode Toggle */}
|
|
236
|
-
{provider === 'ollama' && (
|
|
237
|
-
<div className="mb-8">
|
|
238
|
-
<SectionLabel>Mode</SectionLabel>
|
|
239
|
-
<div className="flex p-1 rounded-[14px] bg-surface border border-white/[0.06]">
|
|
240
|
-
{(['local', 'cloud'] as const).map((mode) => (
|
|
241
|
-
<button
|
|
242
|
-
key={mode}
|
|
243
|
-
onClick={() => setOllamaMode(mode)}
|
|
244
|
-
className={`flex-1 py-3 rounded-[12px] text-center cursor-pointer transition-all duration-200
|
|
245
|
-
text-[14px] font-600 capitalize
|
|
246
|
-
${ollamaMode === mode
|
|
247
|
-
? 'bg-accent-soft text-accent-bright shadow-[0_0_20px_rgba(99,102,241,0.1)]'
|
|
248
|
-
: 'bg-transparent text-text-3 hover:text-text-2'}`}
|
|
249
|
-
style={{ fontFamily: 'inherit' }}
|
|
250
|
-
>
|
|
251
|
-
{mode}
|
|
252
|
-
</button>
|
|
127
|
+
<div className="grid grid-cols-2 gap-4 mb-6">
|
|
128
|
+
<div>
|
|
129
|
+
<SectionLabel>Provider</SectionLabel>
|
|
130
|
+
<select
|
|
131
|
+
value={provider}
|
|
132
|
+
onChange={(e) => {
|
|
133
|
+
const p = e.target.value as ProviderType
|
|
134
|
+
setProvider(p)
|
|
135
|
+
setModel(PROVIDERS[p].models[0])
|
|
136
|
+
}}
|
|
137
|
+
className={`${inputClass} appearance-none cursor-pointer`}
|
|
138
|
+
style={{ fontFamily: 'inherit' }}
|
|
139
|
+
>
|
|
140
|
+
{Object.values(PROVIDERS).map((p) => (
|
|
141
|
+
<option key={p.id} value={p.id}>{p.name}</option>
|
|
253
142
|
))}
|
|
254
|
-
</
|
|
143
|
+
</select>
|
|
255
144
|
</div>
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
{/* Model */}
|
|
259
|
-
{currentProvider && currentProvider.models.length > 0 && (
|
|
260
|
-
<div className="mb-8">
|
|
145
|
+
<div>
|
|
261
146
|
<SectionLabel>Model</SectionLabel>
|
|
262
|
-
<
|
|
263
|
-
providerId={currentProvider.id}
|
|
147
|
+
<select
|
|
264
148
|
value={model}
|
|
265
|
-
onChange={setModel}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
{/* API Key */}
|
|
274
|
-
{(currentProvider?.requiresApiKey || (currentProvider?.optionalApiKey && ollamaMode === 'cloud')) && (
|
|
275
|
-
<div className="mb-8">
|
|
276
|
-
<SectionLabel>API Key</SectionLabel>
|
|
277
|
-
{providerCredentials.length > 0 && !addingKey ? (
|
|
278
|
-
<select
|
|
279
|
-
value={credentialId || ''}
|
|
280
|
-
onChange={(e) => {
|
|
281
|
-
if (e.target.value === '__add__') {
|
|
282
|
-
setAddingKey(true)
|
|
283
|
-
} else {
|
|
284
|
-
setCredentialId(e.target.value)
|
|
285
|
-
}
|
|
286
|
-
}}
|
|
287
|
-
className={`${inputClass} appearance-none cursor-pointer`}
|
|
288
|
-
style={{ fontFamily: 'inherit' }}
|
|
289
|
-
>
|
|
290
|
-
{providerCredentials.map((c) => (
|
|
291
|
-
<option key={c.id} value={c.id}>{c.name}</option>
|
|
292
|
-
))}
|
|
293
|
-
<option value="__add__">+ Add new key...</option>
|
|
294
|
-
</select>
|
|
295
|
-
) : (
|
|
296
|
-
<div className="space-y-3 p-5 rounded-[16px] bg-surface-2 border border-white/[0.06]">
|
|
297
|
-
<input
|
|
298
|
-
type="text"
|
|
299
|
-
value={newKeyName}
|
|
300
|
-
onChange={(e) => setNewKeyName(e.target.value)}
|
|
301
|
-
placeholder="Key name (optional)"
|
|
302
|
-
className={inputClass}
|
|
303
|
-
style={{ fontFamily: 'inherit' }}
|
|
304
|
-
/>
|
|
305
|
-
<input
|
|
306
|
-
type="password"
|
|
307
|
-
value={newKeyValue}
|
|
308
|
-
onChange={(e) => setNewKeyValue(e.target.value)}
|
|
309
|
-
placeholder="sk-..."
|
|
310
|
-
className={inputClass}
|
|
311
|
-
style={{ fontFamily: 'inherit' }}
|
|
312
|
-
/>
|
|
313
|
-
<div className="flex gap-3 pt-2">
|
|
314
|
-
{providerCredentials.length > 0 && (
|
|
315
|
-
<button
|
|
316
|
-
onClick={() => setAddingKey(false)}
|
|
317
|
-
className="flex-1 py-3 rounded-[14px] border border-white/[0.08] bg-transparent text-text-2 text-[14px] font-600 cursor-pointer hover:bg-surface-2 transition-colors"
|
|
318
|
-
style={{ fontFamily: 'inherit' }}
|
|
319
|
-
>
|
|
320
|
-
Cancel
|
|
321
|
-
</button>
|
|
322
|
-
)}
|
|
323
|
-
<button
|
|
324
|
-
onClick={handleAddKey}
|
|
325
|
-
disabled={!newKeyValue.trim()}
|
|
326
|
-
className="flex-1 py-3 rounded-[14px] border-none bg-accent-bright text-white text-[14px] font-600 cursor-pointer disabled:opacity-30 transition-all hover:brightness-110"
|
|
327
|
-
style={{ fontFamily: 'inherit' }}
|
|
328
|
-
>
|
|
329
|
-
Save Key
|
|
330
|
-
</button>
|
|
331
|
-
</div>
|
|
332
|
-
</div>
|
|
333
|
-
)}
|
|
149
|
+
onChange={(e) => setModel(e.target.value)}
|
|
150
|
+
className={`${inputClass} appearance-none cursor-pointer`}
|
|
151
|
+
style={{ fontFamily: 'inherit' }}
|
|
152
|
+
>
|
|
153
|
+
{currentProvider.models.map((m) => (
|
|
154
|
+
<option key={m} value={m}>{m}</option>
|
|
155
|
+
))}
|
|
156
|
+
</select>
|
|
334
157
|
</div>
|
|
335
|
-
|
|
158
|
+
</div>
|
|
336
159
|
|
|
337
|
-
{
|
|
338
|
-
{currentProvider?.requiresEndpoint && (provider === 'openclaw' || (provider === 'ollama' && ollamaMode === 'local')) && (
|
|
160
|
+
{currentProvider.requiresEndpoint && (
|
|
339
161
|
<div className="mb-8">
|
|
340
162
|
<SectionLabel>{provider === 'openclaw' ? 'OpenClaw Endpoint' : 'Endpoint'}</SectionLabel>
|
|
341
163
|
<input
|
|
@@ -352,46 +174,45 @@ export function NewSessionSheet() {
|
|
|
352
174
|
)}
|
|
353
175
|
</div>
|
|
354
176
|
)}
|
|
355
|
-
|
|
356
|
-
{
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
</div>
|
|
177
|
+
|
|
178
|
+
{/* Plugins (Capability enablement) */}
|
|
179
|
+
<div className="mb-8">
|
|
180
|
+
<label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">
|
|
181
|
+
Plugins <span className="normal-case tracking-normal font-normal text-text-3">(optional)</span>
|
|
182
|
+
</label>
|
|
183
|
+
<p className="text-[12px] text-text-3/60 mb-3">Allow this model to execute commands and access files.</p>
|
|
184
|
+
<div className="flex flex-wrap gap-2.5">
|
|
185
|
+
{([
|
|
186
|
+
{ id: 'shell' as SessionTool, label: 'Shell' },
|
|
187
|
+
{ id: 'files' as SessionTool, label: 'Files' },
|
|
188
|
+
{ id: 'edit_file' as SessionTool, label: 'Edit File' },
|
|
189
|
+
{ id: 'web_search' as SessionTool, label: 'Web Search' },
|
|
190
|
+
{ id: 'web_fetch' as SessionTool, label: 'Web Fetch' },
|
|
191
|
+
{ id: 'claude_code' as SessionTool, label: 'Claude Code' },
|
|
192
|
+
{ id: 'codex_cli' as SessionTool, label: 'Codex CLI' },
|
|
193
|
+
{ id: 'opencode_cli' as SessionTool, label: 'OpenCode CLI' },
|
|
194
|
+
]).map(({ id, label }) => {
|
|
195
|
+
const active = selectedTools.includes(id)
|
|
196
|
+
return (
|
|
197
|
+
<button
|
|
198
|
+
key={id}
|
|
199
|
+
onClick={() => {
|
|
200
|
+
setSelectedTools((prev) =>
|
|
201
|
+
active ? prev.filter((t) => t !== id) : [...prev, id],
|
|
202
|
+
)
|
|
203
|
+
}}
|
|
204
|
+
className={`px-4 py-2.5 rounded-[12px] text-[13px] font-600 border cursor-pointer transition-all duration-200 active:scale-[0.97]
|
|
205
|
+
${active
|
|
206
|
+
? 'bg-accent-soft border-accent-bright/25 text-accent-bright shadow-[0_0_20px_rgba(99,102,241,0.1)]'
|
|
207
|
+
: 'bg-surface border-white/[0.06] text-text-3 hover:bg-surface-2 hover:border-white/[0.08]'}`}
|
|
208
|
+
style={{ fontFamily: 'inherit' }}
|
|
209
|
+
>
|
|
210
|
+
{label}
|
|
211
|
+
</button>
|
|
212
|
+
)
|
|
213
|
+
})}
|
|
393
214
|
</div>
|
|
394
|
-
|
|
215
|
+
</div>
|
|
395
216
|
</>
|
|
396
217
|
)}
|
|
397
218
|
|
|
@@ -403,44 +224,30 @@ export function NewSessionSheet() {
|
|
|
403
224
|
{' / '}
|
|
404
225
|
<span className="text-text-2 font-600">{agents[selectedAgentId].model}</span>
|
|
405
226
|
{agents[selectedAgentId].tools?.length ? (
|
|
406
|
-
<>
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
<span
|
|
410
|
-
{
|
|
227
|
+
<>
|
|
228
|
+
{' + '}
|
|
229
|
+
{agents[selectedAgentId].tools!.map((tool, i) => (
|
|
230
|
+
<span key={tool}>
|
|
231
|
+
{i > 0 && ', '}
|
|
232
|
+
<span className="text-sky-400/70 font-600 cursor-help" title={TOOL_DESCRIPTIONS[tool] || tool}>
|
|
233
|
+
{TOOL_LABELS[tool] || tool.replace(/_/g, ' ')}
|
|
234
|
+
</span>
|
|
411
235
|
</span>
|
|
412
|
-
|
|
413
|
-
|
|
236
|
+
))}
|
|
237
|
+
</>
|
|
414
238
|
) : null}
|
|
415
239
|
</span>
|
|
416
240
|
</div>
|
|
417
241
|
)}
|
|
418
242
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
setSelectedFile(file ?? null)
|
|
428
|
-
if (!name) {
|
|
429
|
-
const dirName = dir.split('/').pop() || ''
|
|
430
|
-
setName(dirName)
|
|
431
|
-
}
|
|
432
|
-
}}
|
|
433
|
-
onClear={() => { setSelectedDir(null); setSelectedFile(null) }}
|
|
434
|
-
/>
|
|
435
|
-
</div>
|
|
436
|
-
|
|
437
|
-
{/* Actions */}
|
|
438
|
-
<SheetFooter
|
|
439
|
-
onCancel={onClose}
|
|
440
|
-
onSave={handleCreate}
|
|
441
|
-
saveLabel="Create Chat"
|
|
442
|
-
saveDisabled={!canCreate()}
|
|
443
|
-
/>
|
|
243
|
+
<button
|
|
244
|
+
onClick={handleCreate}
|
|
245
|
+
disabled={loading || (!selectedAgentId && !model)}
|
|
246
|
+
className="w-full py-4 rounded-[18px] bg-accent-bright text-white font-display text-[15px] font-700 shadow-[0_0_30px_rgba(56,189,248,0.3)] hover:shadow-[0_0_40px_rgba(56,189,248,0.45)] hover:scale-[1.01] active:scale-[0.99] transition-all disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:scale-100 disabled:shadow-none"
|
|
247
|
+
style={{ fontFamily: 'inherit' }}
|
|
248
|
+
>
|
|
249
|
+
{loading ? 'Creating...' : 'Start Session'}
|
|
250
|
+
</button>
|
|
444
251
|
</BottomSheet>
|
|
445
252
|
)
|
|
446
253
|
}
|
|
@@ -6,6 +6,7 @@ import { useAppStore } from '@/stores/use-app-store'
|
|
|
6
6
|
import { useChatStore } from '@/stores/use-chat-store'
|
|
7
7
|
import { ConnectorPlatformBadge, getSessionConnector } from '@/components/shared/connector-platform-icon'
|
|
8
8
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
9
|
+
import { toast } from 'sonner'
|
|
9
10
|
|
|
10
11
|
function timeAgo(ts: number): string {
|
|
11
12
|
if (!ts) return ''
|
|
@@ -46,8 +47,15 @@ export function SessionCard({ session, active, onClick }: Props) {
|
|
|
46
47
|
|
|
47
48
|
const handleDelete = async (e: React.MouseEvent) => {
|
|
48
49
|
e.stopPropagation()
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
if (!confirm(`Delete chat session "${session.name}"?`)) return
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
await api('DELETE', `/sessions/${session.id}`)
|
|
54
|
+
removeSession(session.id)
|
|
55
|
+
toast.success('Session deleted')
|
|
56
|
+
} catch (err: unknown) {
|
|
57
|
+
toast.error(err instanceof Error ? err.message : 'Failed to delete session')
|
|
58
|
+
}
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
const last = session.messages?.length
|