@swarmclawai/swarmclaw 0.7.7 → 0.8.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 +12 -14
- package/next.config.ts +13 -2
- package/package.json +4 -2
- package/src/app/api/agents/[id]/thread/route.ts +9 -0
- package/src/app/api/agents/route.ts +4 -0
- package/src/app/api/agents/thread-route.test.ts +133 -0
- package/src/app/api/approvals/route.test.ts +148 -0
- package/src/app/api/canvas/[sessionId]/route.ts +3 -1
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -2
- package/src/app/api/chats/[id]/devserver/route.ts +48 -7
- package/src/app/api/chats/[id]/messages/route.ts +42 -18
- package/src/app/api/chats/[id]/route.ts +1 -1
- package/src/app/api/chats/[id]/stop/route.ts +5 -4
- package/src/app/api/chats/route.ts +23 -2
- package/src/app/api/clawhub/install/route.ts +28 -8
- package/src/app/api/connectors/[id]/route.ts +46 -3
- package/src/app/api/connectors/route.ts +12 -8
- package/src/app/api/external-agents/route.test.ts +165 -0
- package/src/app/api/gateways/[id]/health/route.ts +27 -12
- package/src/app/api/gateways/[id]/route.ts +2 -0
- package/src/app/api/gateways/health-route.test.ts +135 -0
- package/src/app/api/gateways/route.ts +2 -0
- package/src/app/api/mcp-servers/route.test.ts +130 -0
- package/src/app/api/openclaw/deploy/route.ts +38 -5
- package/src/app/api/plugins/install/route.ts +46 -6
- package/src/app/api/plugins/marketplace/route.ts +48 -15
- package/src/app/api/preview-server/route.ts +26 -11
- package/src/app/api/projects/[id]/route.ts +6 -2
- package/src/app/api/projects/route.ts +4 -3
- package/src/app/api/schedules/[id]/run/route.ts +4 -0
- package/src/app/api/schedules/route.test.ts +86 -0
- package/src/app/api/schedules/route.ts +6 -1
- package/src/app/api/secrets/[id]/route.ts +1 -0
- package/src/app/api/secrets/route.ts +2 -1
- package/src/app/api/settings/route.ts +2 -0
- package/src/app/api/setup/check-provider/route.test.ts +19 -0
- package/src/app/api/setup/check-provider/route.ts +40 -10
- package/src/app/api/skills/[id]/route.ts +12 -0
- package/src/app/api/skills/import/route.ts +14 -12
- package/src/app/api/skills/route.ts +13 -1
- package/src/app/api/tasks/[id]/route.ts +10 -1
- package/src/app/api/tasks/import/github/route.test.ts +65 -0
- package/src/app/api/tasks/import/github/route.ts +337 -0
- package/src/app/api/wallets/[id]/approve/route.ts +17 -3
- package/src/app/api/wallets/[id]/route.ts +79 -33
- package/src/app/api/wallets/[id]/send/route.ts +19 -33
- package/src/app/api/wallets/route.ts +78 -61
- package/src/app/api/webhooks/[id]/route.ts +33 -6
- package/src/app/api/webhooks/route.test.ts +272 -0
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-card.tsx +9 -2
- package/src/components/agents/agent-chat-list.tsx +18 -2
- package/src/components/agents/agent-list.tsx +1 -0
- package/src/components/agents/agent-sheet.tsx +257 -38
- package/src/components/agents/inspector-panel.tsx +41 -0
- package/src/components/canvas/canvas-panel.tsx +236 -65
- package/src/components/chat/chat-area.tsx +36 -19
- package/src/components/chat/chat-card.tsx +36 -13
- package/src/components/chat/chat-header.tsx +48 -16
- package/src/components/chat/chat-list.tsx +28 -4
- package/src/components/chat/checkpoint-timeline.tsx +50 -34
- package/src/components/chat/delegation-banner.test.ts +14 -1
- package/src/components/chat/delegation-banner.tsx +1 -1
- package/src/components/chat/message-bubble.tsx +208 -145
- package/src/components/chat/message-list.tsx +48 -19
- package/src/components/chatrooms/chatroom-message.tsx +2 -2
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -2
- package/src/components/connectors/connector-health.tsx +1 -1
- package/src/components/connectors/connector-list.tsx +7 -2
- package/src/components/connectors/connector-sheet.tsx +337 -148
- package/src/components/gateways/gateway-sheet.tsx +2 -2
- package/src/components/layout/app-layout.tsx +40 -23
- package/src/components/mcp-servers/mcp-server-list.tsx +26 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +19 -2
- package/src/components/openclaw/openclaw-deploy-panel.tsx +269 -21
- package/src/components/plugins/plugin-list.tsx +45 -9
- package/src/components/plugins/plugin-sheet.tsx +55 -7
- package/src/components/projects/project-detail.tsx +217 -0
- package/src/components/projects/project-sheet.tsx +176 -4
- package/src/components/providers/provider-list.tsx +2 -1
- package/src/components/providers/provider-sheet.tsx +21 -2
- package/src/components/schedules/schedule-card.tsx +25 -1
- package/src/components/schedules/schedule-sheet.tsx +44 -2
- package/src/components/secrets/secret-sheet.tsx +21 -2
- package/src/components/shared/agent-switch-dialog.tsx +12 -1
- package/src/components/shared/bottom-sheet.tsx +13 -3
- package/src/components/shared/command-palette.tsx +8 -1
- package/src/components/shared/confirm-dialog.tsx +19 -4
- package/src/components/shared/connector-platform-icon.test.ts +28 -0
- package/src/components/shared/connector-platform-icon.tsx +39 -6
- package/src/components/shared/settings/plugin-manager.tsx +29 -6
- package/src/components/shared/settings/section-capability-policy.tsx +45 -3
- package/src/components/shared/settings/section-voice.tsx +11 -3
- package/src/components/skills/skill-list.tsx +25 -0
- package/src/components/skills/skill-sheet.tsx +84 -12
- package/src/components/tasks/approvals-panel.tsx +289 -34
- package/src/components/tasks/task-board.tsx +410 -25
- package/src/components/tasks/task-card.tsx +66 -8
- package/src/components/tasks/task-sheet.tsx +16 -4
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/wallets/wallet-approval-dialog.tsx +4 -2
- package/src/components/wallets/wallet-panel.tsx +435 -90
- package/src/components/wallets/wallet-section.tsx +198 -48
- package/src/components/webhooks/webhook-sheet.tsx +22 -2
- package/src/lib/approval-display.ts +20 -0
- package/src/lib/canvas-content.ts +198 -0
- package/src/lib/chat-artifact-summary.ts +165 -0
- package/src/lib/chat-display.test.ts +91 -0
- package/src/lib/chat-display.ts +58 -0
- package/src/lib/chat-streaming-state.test.ts +47 -1
- package/src/lib/chat-streaming-state.ts +42 -0
- package/src/lib/ollama-model.ts +10 -0
- package/src/lib/openclaw-endpoint.test.ts +8 -0
- package/src/lib/openclaw-endpoint.ts +6 -1
- package/src/lib/plugin-install-cors.ts +46 -0
- package/src/lib/plugin-sources.test.ts +43 -0
- package/src/lib/plugin-sources.ts +77 -0
- package/src/lib/providers/ollama.ts +16 -6
- package/src/lib/providers/openclaw.test.ts +54 -0
- package/src/lib/providers/openclaw.ts +127 -11
- package/src/lib/schedule-dedupe-advanced.test.ts +1335 -0
- package/src/lib/schedule-dedupe.test.ts +66 -1
- package/src/lib/schedule-dedupe.ts +169 -12
- package/src/lib/schedule-origin.test.ts +20 -0
- package/src/lib/schedule-origin.ts +15 -0
- package/src/lib/server/__fixtures__/fake-mcp-stdio-server.mjs +27 -0
- package/src/lib/server/agent-availability.ts +16 -0
- package/src/lib/server/agent-runtime-config.ts +12 -4
- package/src/lib/server/agent-thread-session.test.ts +51 -0
- package/src/lib/server/agent-thread-session.ts +7 -0
- package/src/lib/server/approval-match.ts +205 -0
- package/src/lib/server/approvals-auto-approve.test.ts +538 -1
- package/src/lib/server/approvals.ts +214 -1
- package/src/lib/server/assistant-control.test.ts +29 -0
- package/src/lib/server/assistant-control.ts +23 -0
- package/src/lib/server/build-llm.test.ts +79 -0
- package/src/lib/server/build-llm.ts +14 -4
- package/src/lib/server/canvas-content.test.ts +32 -0
- package/src/lib/server/canvas-content.ts +6 -0
- package/src/lib/server/capability-router.test.ts +33 -0
- package/src/lib/server/capability-router.ts +80 -19
- package/src/lib/server/chat-execution-advanced.test.ts +651 -0
- package/src/lib/server/chat-execution-disabled.test.ts +94 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +157 -0
- package/src/lib/server/chat-execution.ts +378 -73
- package/src/lib/server/clawhub-client.test.ts +14 -8
- package/src/lib/server/connectors/manager-reconnect.test.ts +47 -0
- package/src/lib/server/connectors/manager.test.ts +1147 -0
- package/src/lib/server/connectors/manager.ts +461 -137
- package/src/lib/server/connectors/pairing.ts +26 -5
- package/src/lib/server/connectors/types.ts +2 -0
- package/src/lib/server/connectors/whatsapp.test.ts +134 -0
- package/src/lib/server/connectors/whatsapp.ts +271 -47
- package/src/lib/server/context-manager.ts +6 -1
- package/src/lib/server/daemon-state.ts +84 -47
- package/src/lib/server/data-dir.test.ts +37 -0
- package/src/lib/server/data-dir.ts +20 -1
- package/src/lib/server/delegation-jobs-advanced.test.ts +513 -0
- package/src/lib/server/devserver-launch.test.ts +60 -0
- package/src/lib/server/devserver-launch.ts +85 -0
- package/src/lib/server/elevenlabs.test.ts +247 -1
- package/src/lib/server/elevenlabs.ts +147 -43
- package/src/lib/server/ethereum.ts +590 -0
- package/src/lib/server/eval/agent-regression-advanced.test.ts +302 -0
- package/src/lib/server/eval/agent-regression.test.ts +18 -1
- package/src/lib/server/eval/agent-regression.ts +383 -11
- package/src/lib/server/evm-swap.ts +475 -0
- package/src/lib/server/execution-log.ts +1 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +173 -0
- package/src/lib/server/heartbeat-service.ts +20 -11
- package/src/lib/server/heartbeat-wake.test.ts +112 -0
- package/src/lib/server/heartbeat-wake.ts +338 -57
- package/src/lib/server/main-agent-loop-advanced.test.ts +538 -0
- package/src/lib/server/main-agent-loop.test.ts +260 -0
- package/src/lib/server/main-agent-loop.ts +559 -14
- package/src/lib/server/mcp-client.test.ts +16 -0
- package/src/lib/server/mcp-client.ts +25 -0
- package/src/lib/server/memory-integration.test.ts +719 -0
- package/src/lib/server/memory-policy.test.ts +43 -0
- package/src/lib/server/memory-policy.ts +132 -0
- package/src/lib/server/memory-tiers.test.ts +60 -0
- package/src/lib/server/memory-tiers.ts +16 -0
- package/src/lib/server/ollama-runtime.ts +58 -0
- package/src/lib/server/openclaw-deploy.test.ts +109 -1
- package/src/lib/server/openclaw-deploy.ts +557 -81
- package/src/lib/server/openclaw-gateway.test.ts +131 -0
- package/src/lib/server/openclaw-gateway.ts +10 -4
- package/src/lib/server/openclaw-health.test.ts +35 -0
- package/src/lib/server/openclaw-health.ts +215 -47
- package/src/lib/server/orchestrator-lg.ts +3 -2
- package/src/lib/server/orchestrator.ts +2 -0
- package/src/lib/server/plugins-advanced.test.ts +351 -0
- package/src/lib/server/plugins.ts +211 -6
- package/src/lib/server/project-context.ts +162 -0
- package/src/lib/server/project-utils.ts +150 -0
- package/src/lib/server/queue-advanced.test.ts +528 -0
- package/src/lib/server/queue-followups.test.ts +409 -2
- package/src/lib/server/queue-reconcile.test.ts +128 -0
- package/src/lib/server/queue.ts +527 -68
- package/src/lib/server/scheduler.ts +29 -1
- package/src/lib/server/session-note.test.ts +36 -0
- package/src/lib/server/session-note.ts +42 -0
- package/src/lib/server/session-run-manager.ts +83 -4
- package/src/lib/server/session-tools/canvas.ts +14 -12
- package/src/lib/server/session-tools/connector-inputs.test.ts +37 -0
- package/src/lib/server/session-tools/connector.test.ts +138 -0
- package/src/lib/server/session-tools/connector.ts +366 -54
- package/src/lib/server/session-tools/context.ts +17 -3
- package/src/lib/server/session-tools/crud.ts +484 -84
- package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
- package/src/lib/server/session-tools/delegate-resume.test.ts +50 -0
- package/src/lib/server/session-tools/delegate.ts +102 -10
- package/src/lib/server/session-tools/discovery-approvals.test.ts +142 -0
- package/src/lib/server/session-tools/discovery.ts +80 -12
- package/src/lib/server/session-tools/file-normalize.test.ts +36 -0
- package/src/lib/server/session-tools/file.ts +43 -4
- package/src/lib/server/session-tools/human-loop.ts +35 -5
- package/src/lib/server/session-tools/index.ts +44 -9
- package/src/lib/server/session-tools/manage-connectors.test.ts +139 -0
- package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +564 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +283 -0
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +852 -0
- package/src/lib/server/session-tools/manage-tasks.test.ts +114 -0
- package/src/lib/server/session-tools/memory.test.ts +93 -0
- package/src/lib/server/session-tools/memory.ts +554 -75
- package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
- package/src/lib/server/session-tools/platform-access.test.ts +58 -0
- package/src/lib/server/session-tools/platform.ts +60 -19
- package/src/lib/server/session-tools/plugin-creator.ts +57 -1
- package/src/lib/server/session-tools/primitive-tools.test.ts +6 -0
- package/src/lib/server/session-tools/schedule.ts +6 -1
- package/src/lib/server/session-tools/shell-normalize.test.ts +25 -1
- package/src/lib/server/session-tools/shell.ts +22 -3
- package/src/lib/server/session-tools/wallet-tool.test.ts +254 -0
- package/src/lib/server/session-tools/wallet.ts +1374 -139
- package/src/lib/server/session-tools/web-inputs.test.ts +178 -0
- package/src/lib/server/session-tools/web.ts +621 -70
- package/src/lib/server/skill-discovery.ts +128 -0
- package/src/lib/server/skill-eligibility.test.ts +84 -0
- package/src/lib/server/skill-eligibility.ts +95 -0
- package/src/lib/server/skill-prompt-budget.test.ts +102 -0
- package/src/lib/server/skill-prompt-budget.ts +125 -0
- package/src/lib/server/skills-normalize.test.ts +54 -0
- package/src/lib/server/skills-normalize.ts +372 -26
- package/src/lib/server/solana.ts +214 -29
- package/src/lib/server/storage.ts +65 -36
- package/src/lib/server/stream-agent-chat.test.ts +437 -2
- package/src/lib/server/stream-agent-chat.ts +957 -79
- package/src/lib/server/system-events.ts +1 -1
- package/src/lib/server/tool-aliases.ts +2 -0
- package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
- package/src/lib/server/tool-capability-policy.test.ts +24 -0
- package/src/lib/server/tool-capability-policy.ts +29 -1
- package/src/lib/server/tool-loop-detection.test.ts +105 -0
- package/src/lib/server/tool-loop-detection.ts +260 -0
- package/src/lib/server/tool-planning.test.ts +44 -0
- package/src/lib/server/tool-planning.ts +271 -0
- package/src/lib/server/wallet-execution.test.ts +198 -0
- package/src/lib/server/wallet-portfolio.test.ts +98 -0
- package/src/lib/server/wallet-portfolio.ts +724 -0
- package/src/lib/server/wallet-service.test.ts +57 -0
- package/src/lib/server/wallet-service.ts +213 -0
- package/src/lib/server/watch-jobs-advanced.test.ts +594 -0
- package/src/lib/server/watch-jobs.ts +17 -2
- package/src/lib/server/workspace-context.ts +111 -0
- package/src/lib/skill-save-payload.test.ts +39 -0
- package/src/lib/skill-save-payload.ts +37 -0
- package/src/lib/tasks.ts +28 -0
- package/src/lib/tool-definitions.ts +2 -1
- package/src/lib/tool-event-summary.test.ts +30 -0
- package/src/lib/tool-event-summary.ts +37 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/wallet-transactions.test.ts +75 -0
- package/src/lib/wallet-transactions.ts +43 -0
- package/src/lib/wallet.test.ts +17 -0
- package/src/lib/wallet.ts +183 -0
- package/src/proxy.test.ts +31 -0
- package/src/proxy.ts +34 -2
- package/src/stores/use-chat-store.ts +15 -1
- package/src/types/index.ts +249 -14
|
@@ -10,8 +10,8 @@ import { ChatToolToggles } from './chat-tool-toggles'
|
|
|
10
10
|
import { api } from '@/lib/api-client'
|
|
11
11
|
import {
|
|
12
12
|
ConnectorPlatformIcon,
|
|
13
|
-
CONNECTOR_PLATFORM_META,
|
|
14
13
|
getSessionConnector,
|
|
14
|
+
resolveConnectorPlatformMeta,
|
|
15
15
|
} from '@/components/shared/connector-platform-icon'
|
|
16
16
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
17
17
|
import { ModelCombobox } from '@/components/shared/model-combobox'
|
|
@@ -34,6 +34,23 @@ function Tip({ label, children, side = 'bottom' }: { label: string; children: Re
|
|
|
34
34
|
)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function getAgentWalletIds(agent: { walletIds?: string[]; walletId?: string | null } | null | undefined): string[] {
|
|
38
|
+
const ids = Array.isArray(agent?.walletIds)
|
|
39
|
+
? agent.walletIds.filter((value): value is string => typeof value === 'string' && value.trim().length > 0)
|
|
40
|
+
: []
|
|
41
|
+
const legacy = typeof agent?.walletId === 'string' && agent.walletId.trim()
|
|
42
|
+
? [agent.walletId.trim()]
|
|
43
|
+
: []
|
|
44
|
+
return [...new Set([...ids, ...legacy])]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getAgentActiveWalletId(agent: { activeWalletId?: string | null; walletIds?: string[]; walletId?: string | null } | null | undefined): string | null {
|
|
48
|
+
const walletIds = getAgentWalletIds(agent)
|
|
49
|
+
if (typeof agent?.activeWalletId === 'string' && walletIds.includes(agent.activeWalletId)) return agent.activeWalletId
|
|
50
|
+
if (typeof agent?.walletId === 'string' && walletIds.includes(agent.walletId)) return agent.walletId
|
|
51
|
+
return walletIds[0] || null
|
|
52
|
+
}
|
|
53
|
+
|
|
37
54
|
function HeaderChip({
|
|
38
55
|
children,
|
|
39
56
|
title,
|
|
@@ -129,7 +146,7 @@ export function ChatHeader({ session, streaming, onStop, onMenuToggle, onBack, m
|
|
|
129
146
|
const loadConnectors = useAppStore((s) => s.loadConnectors)
|
|
130
147
|
const agent = session.agentId ? agents[session.agentId] : null
|
|
131
148
|
const connector = getSessionConnector(session, connectors)
|
|
132
|
-
const connectorMeta = connector ?
|
|
149
|
+
const connectorMeta = connector ? resolveConnectorPlatformMeta(connector.platform) : null
|
|
133
150
|
const connectorPresence = connector?.presence
|
|
134
151
|
const providers = useAppStore((s) => s.providers)
|
|
135
152
|
const loadProviders = useAppStore((s) => s.loadProviders)
|
|
@@ -152,8 +169,10 @@ export function ChatHeader({ session, streaming, onStop, onMenuToggle, onBack, m
|
|
|
152
169
|
const renameInputRef = useRef<HTMLInputElement>(null)
|
|
153
170
|
const renameContainerRef = useRef<HTMLSpanElement>(null)
|
|
154
171
|
const setWalletPanelAgentId = useAppStore((s) => s.setWalletPanelAgentId)
|
|
155
|
-
const [walletBalance, setWalletBalance] = useState<number | null>(null)
|
|
172
|
+
const [walletBalance, setWalletBalance] = useState<{ formatted: string; symbol: string; assets?: number } | null>(null)
|
|
156
173
|
const [headerWidgets, setHeaderWidgets] = useState<Array<{ id: string; label: string; icon?: string }>>([])
|
|
174
|
+
const agentWalletIds = useMemo(() => getAgentWalletIds(agent), [agent])
|
|
175
|
+
const activeWalletId = useMemo(() => getAgentActiveWalletId(agent), [agent])
|
|
157
176
|
|
|
158
177
|
useEffect(() => {
|
|
159
178
|
api<Array<{ id: string; label: string; icon?: string }>>('GET', `/plugins/ui?type=header&sessionId=${session.id}`).then(widgets => {
|
|
@@ -162,17 +181,25 @@ export function ChatHeader({ session, streaming, onStop, onMenuToggle, onBack, m
|
|
|
162
181
|
}, [session.id])
|
|
163
182
|
|
|
164
183
|
const fetchWalletBalance = useCallback(async () => {
|
|
165
|
-
if (!
|
|
184
|
+
if (!activeWalletId) {
|
|
166
185
|
setWalletBalance(null)
|
|
167
186
|
return
|
|
168
187
|
}
|
|
169
188
|
try {
|
|
170
|
-
const data = await api<{
|
|
171
|
-
|
|
189
|
+
const data = await api<{ balanceFormatted?: string; balanceSymbol?: string; portfolioSummary?: { nonZeroAssets?: number } }>('GET', `/wallets/${activeWalletId}`)
|
|
190
|
+
if (data.balanceFormatted && data.balanceSymbol) {
|
|
191
|
+
setWalletBalance({
|
|
192
|
+
formatted: data.balanceFormatted,
|
|
193
|
+
symbol: data.balanceSymbol,
|
|
194
|
+
assets: typeof data.portfolioSummary?.nonZeroAssets === 'number' ? data.portfolioSummary.nonZeroAssets : undefined,
|
|
195
|
+
})
|
|
196
|
+
} else {
|
|
197
|
+
setWalletBalance(null)
|
|
198
|
+
}
|
|
172
199
|
} catch {
|
|
173
200
|
setWalletBalance(null)
|
|
174
201
|
}
|
|
175
|
-
}, [
|
|
202
|
+
}, [activeWalletId])
|
|
176
203
|
|
|
177
204
|
useEffect(() => {
|
|
178
205
|
void fetchWalletBalance()
|
|
@@ -237,17 +264,19 @@ export function ChatHeader({ session, streaming, onStop, onMenuToggle, onBack, m
|
|
|
237
264
|
title: 'Open wallets',
|
|
238
265
|
}
|
|
239
266
|
}
|
|
240
|
-
if (
|
|
267
|
+
if (agentWalletIds.length === 0) {
|
|
241
268
|
return {
|
|
242
269
|
label: 'Create wallet',
|
|
243
270
|
title: 'Create wallet',
|
|
244
271
|
}
|
|
245
272
|
}
|
|
246
273
|
return {
|
|
247
|
-
label:
|
|
248
|
-
|
|
274
|
+
label: agentWalletIds.length > 1
|
|
275
|
+
? (walletBalance ? `${walletBalance.formatted} ${walletBalance.symbol}${walletBalance.assets && walletBalance.assets > 1 ? ` +${walletBalance.assets - 1}` : ''} / ${agentWalletIds.length}` : `${agentWalletIds.length} wallets`)
|
|
276
|
+
: (walletBalance ? `${walletBalance.formatted} ${walletBalance.symbol}${walletBalance.assets && walletBalance.assets > 1 ? ` +${walletBalance.assets - 1}` : ''}` : 'Wallet'),
|
|
277
|
+
title: agentWalletIds.length > 1 ? 'View wallets' : 'View wallet',
|
|
249
278
|
}
|
|
250
|
-
}, [agent?.id,
|
|
279
|
+
}, [agent?.id, agentWalletIds, walletBalance])
|
|
251
280
|
|
|
252
281
|
const handleHeaderWidgetClick = (widgetId: string) => {
|
|
253
282
|
if (widgetId === 'wallet-status') {
|
|
@@ -280,12 +309,16 @@ export function ChatHeader({ session, streaming, onStop, onMenuToggle, onBack, m
|
|
|
280
309
|
const fromDelegateOpenCode = session.delegateResumeIds?.opencode
|
|
281
310
|
? { label: 'OpenCode', id: session.delegateResumeIds.opencode, command: `opencode run \"<task>\" --session ${session.delegateResumeIds.opencode}` }
|
|
282
311
|
: null
|
|
312
|
+
const fromDelegateGemini = session.delegateResumeIds?.gemini
|
|
313
|
+
? { label: 'Gemini', id: session.delegateResumeIds.gemini, command: `gemini --resume ${session.delegateResumeIds.gemini} --prompt \"<task>\"` }
|
|
314
|
+
: null
|
|
283
315
|
return fromSessionClaude
|
|
284
316
|
|| fromSessionCodex
|
|
285
317
|
|| fromSessionOpenCode
|
|
286
318
|
|| fromDelegateClaude
|
|
287
319
|
|| fromDelegateCodex
|
|
288
320
|
|| fromDelegateOpenCode
|
|
321
|
+
|| fromDelegateGemini
|
|
289
322
|
|| null
|
|
290
323
|
}, [session.claudeSessionId, session.codexThreadId, session.opencodeSessionId, session.delegateResumeIds])
|
|
291
324
|
|
|
@@ -385,17 +418,16 @@ export function ChatHeader({ session, streaming, onStop, onMenuToggle, onBack, m
|
|
|
385
418
|
setHeartbeatSaving(true)
|
|
386
419
|
try {
|
|
387
420
|
if (session.agentId) {
|
|
388
|
-
// Save
|
|
421
|
+
// Save the cadence without implicitly toggling heartbeat on.
|
|
389
422
|
await api('PUT', `/agents/${session.agentId}`, {
|
|
390
423
|
heartbeatInterval: formatDuration(sec),
|
|
391
424
|
heartbeatIntervalSec: sec,
|
|
392
|
-
heartbeatEnabled: true,
|
|
393
425
|
})
|
|
394
426
|
// Clear stale session-level overrides
|
|
395
427
|
await api('PUT', `/chats/${session.id}`, { heartbeatIntervalSec: null, heartbeatEnabled: null })
|
|
396
428
|
await Promise.all([loadAgents(), loadSessions()])
|
|
397
429
|
} else {
|
|
398
|
-
await api('PUT', `/chats/${session.id}`, { heartbeatIntervalSec: sec
|
|
430
|
+
await api('PUT', `/chats/${session.id}`, { heartbeatIntervalSec: sec })
|
|
399
431
|
await loadSessions()
|
|
400
432
|
}
|
|
401
433
|
} finally {
|
|
@@ -956,7 +988,7 @@ export function ChatHeader({ session, streaming, onStop, onMenuToggle, onBack, m
|
|
|
956
988
|
</button>
|
|
957
989
|
{Array.from(connectorSources.entries()).map(([cid, info]) => {
|
|
958
990
|
const active = connectorFilter === cid
|
|
959
|
-
const meta =
|
|
991
|
+
const meta = resolveConnectorPlatformMeta(info.platform)
|
|
960
992
|
return (
|
|
961
993
|
<button
|
|
962
994
|
key={cid}
|
|
@@ -965,7 +997,7 @@ export function ChatHeader({ session, streaming, onStop, onMenuToggle, onBack, m
|
|
|
965
997
|
active ? 'bg-accent-soft text-accent-bright' : 'text-text-3 hover:bg-white/[0.06]'
|
|
966
998
|
}`}
|
|
967
999
|
>
|
|
968
|
-
<ConnectorPlatformIcon platform={info.platform
|
|
1000
|
+
<ConnectorPlatformIcon platform={info.platform} size={12} />
|
|
969
1001
|
{info.connectorName || meta?.label || info.platform}
|
|
970
1002
|
</button>
|
|
971
1003
|
)
|
|
@@ -9,6 +9,7 @@ import { toast } from 'sonner'
|
|
|
9
9
|
import { Skeleton } from '@/components/shared/skeleton'
|
|
10
10
|
import { EmptyState } from '@/components/shared/empty-state'
|
|
11
11
|
import { Dropdown, DropdownItem } from '@/components/shared/dropdown'
|
|
12
|
+
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
12
13
|
|
|
13
14
|
interface Props {
|
|
14
15
|
inSidebar?: boolean
|
|
@@ -38,6 +39,8 @@ export function ChatList({ inSidebar, onSelect }: Props) {
|
|
|
38
39
|
const [sortMode, setSortMode] = useState<SortMode>('lastActive')
|
|
39
40
|
const [loaded, setLoaded] = useState(Object.keys(sessions).length > 0)
|
|
40
41
|
const [bulkMenuOpen, setBulkMenuOpen] = useState(false)
|
|
42
|
+
const [confirmClearIds, setConfirmClearIds] = useState<string[] | null>(null)
|
|
43
|
+
const [clearing, setClearing] = useState(false)
|
|
41
44
|
|
|
42
45
|
useEffect(() => {
|
|
43
46
|
if (Object.keys(sessions).length > 0 && !loaded) setLoaded(true)
|
|
@@ -116,6 +119,18 @@ export function ChatList({ inSidebar, onSelect }: Props) {
|
|
|
116
119
|
onSelect?.()
|
|
117
120
|
}
|
|
118
121
|
|
|
122
|
+
const handleClearFiltered = async () => {
|
|
123
|
+
if (!confirmClearIds || confirmClearIds.length === 0) return
|
|
124
|
+
setClearing(true)
|
|
125
|
+
try {
|
|
126
|
+
await clearSessions(confirmClearIds)
|
|
127
|
+
toast.success(`${confirmClearIds.length} chat${confirmClearIds.length === 1 ? '' : 's'} deleted`)
|
|
128
|
+
setConfirmClearIds(null)
|
|
129
|
+
} finally {
|
|
130
|
+
setClearing(false)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
119
134
|
// Truly empty — no sessions at all for this user
|
|
120
135
|
if (!allUserSessions.length) {
|
|
121
136
|
// Show skeleton cards while data is loading
|
|
@@ -178,11 +193,9 @@ export function ChatList({ inSidebar, onSelect }: Props) {
|
|
|
178
193
|
</svg>
|
|
179
194
|
</button>
|
|
180
195
|
<Dropdown open={bulkMenuOpen} onClose={() => setBulkMenuOpen(false)}>
|
|
181
|
-
<DropdownItem onClick={
|
|
196
|
+
<DropdownItem onClick={() => {
|
|
182
197
|
setBulkMenuOpen(false)
|
|
183
|
-
|
|
184
|
-
await clearSessions(filtered.map((s) => s.id))
|
|
185
|
-
toast.success(`${filtered.length} chat${filtered.length === 1 ? '' : 's'} deleted`)
|
|
198
|
+
setConfirmClearIds(filtered.map((s) => s.id))
|
|
186
199
|
}}>
|
|
187
200
|
Clear filtered chats
|
|
188
201
|
</DropdownItem>
|
|
@@ -249,6 +262,17 @@ export function ChatList({ inSidebar, onSelect }: Props) {
|
|
|
249
262
|
</p>
|
|
250
263
|
</div>
|
|
251
264
|
)}
|
|
265
|
+
<ConfirmDialog
|
|
266
|
+
open={!!confirmClearIds}
|
|
267
|
+
title="Clear Filtered Chats?"
|
|
268
|
+
message={confirmClearIds ? `Delete ${confirmClearIds.length} chat${confirmClearIds.length === 1 ? '' : 's'} from the current view?` : 'Delete filtered chats?'}
|
|
269
|
+
confirmLabel={clearing ? 'Deleting...' : 'Delete'}
|
|
270
|
+
confirmDisabled={clearing}
|
|
271
|
+
cancelDisabled={clearing}
|
|
272
|
+
danger
|
|
273
|
+
onConfirm={() => { void handleClearFiltered() }}
|
|
274
|
+
onCancel={() => { if (!clearing) setConfirmClearIds(null) }}
|
|
275
|
+
/>
|
|
252
276
|
</div>
|
|
253
277
|
)
|
|
254
278
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react'
|
|
4
4
|
import { api } from '@/lib/api-client'
|
|
5
|
+
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
5
6
|
import { useAppStore } from '@/stores/use-app-store'
|
|
6
7
|
import { toast } from 'sonner'
|
|
7
8
|
|
|
@@ -21,6 +22,7 @@ export function CheckpointTimeline({ sessionId }: Props) {
|
|
|
21
22
|
const [checkpoints, setCheckpoints] = useState<Checkpoint[]>([])
|
|
22
23
|
const [loading, setLoading] = useState(true)
|
|
23
24
|
const [restoringId, setRestoringId] = useState<string | null>(null)
|
|
25
|
+
const [confirmRestore, setConfirmRestore] = useState<Checkpoint | null>(null)
|
|
24
26
|
const loadSessions = useAppStore((s) => s.loadSessions)
|
|
25
27
|
|
|
26
28
|
const load = async () => {
|
|
@@ -40,9 +42,9 @@ export function CheckpointTimeline({ sessionId }: Props) {
|
|
|
40
42
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
41
43
|
}, [sessionId])
|
|
42
44
|
|
|
43
|
-
const handleRestore = async (
|
|
44
|
-
if (!
|
|
45
|
-
|
|
45
|
+
const handleRestore = async () => {
|
|
46
|
+
if (!confirmRestore) return
|
|
47
|
+
const checkpoint = confirmRestore
|
|
46
48
|
setRestoringId(checkpoint.checkpointId)
|
|
47
49
|
try {
|
|
48
50
|
await api('POST', `/chats/${sessionId}/restore`, {
|
|
@@ -52,6 +54,7 @@ export function CheckpointTimeline({ sessionId }: Props) {
|
|
|
52
54
|
toast.success('Session restored successfully')
|
|
53
55
|
await loadSessions()
|
|
54
56
|
await load()
|
|
57
|
+
setConfirmRestore(null)
|
|
55
58
|
} catch (err) {
|
|
56
59
|
toast.error('Failed to restore session')
|
|
57
60
|
console.error(err)
|
|
@@ -74,39 +77,52 @@ export function CheckpointTimeline({ sessionId }: Props) {
|
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
return (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<div className="flex
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
80
|
+
<>
|
|
81
|
+
<div className="flex flex-col gap-3 p-5">
|
|
82
|
+
{checkpoints.map((cp, i) => (
|
|
83
|
+
<div
|
|
84
|
+
key={cp.checkpointId}
|
|
85
|
+
className="group relative flex flex-col gap-2 p-3 rounded-[12px] border border-white/[0.06] bg-white/[0.02] hover:bg-white/[0.04] transition-all"
|
|
86
|
+
>
|
|
87
|
+
<div className="flex items-center justify-between">
|
|
88
|
+
<div className="flex flex-col">
|
|
89
|
+
<span className="text-[11px] font-700 text-accent-bright uppercase tracking-wider">
|
|
90
|
+
{i === 0 ? 'Current State' : `Point ${checkpoints.length - i}`}
|
|
91
|
+
</span>
|
|
92
|
+
<span className="text-[10px] text-text-3 font-mono">
|
|
93
|
+
{new Date(cp.createdAt).toLocaleString()}
|
|
94
|
+
</span>
|
|
95
|
+
</div>
|
|
96
|
+
{i > 0 && (
|
|
97
|
+
<button
|
|
98
|
+
onClick={() => setConfirmRestore(cp)}
|
|
99
|
+
disabled={!!restoringId}
|
|
100
|
+
className="px-3 py-1 rounded-[6px] bg-accent-soft text-accent-bright text-[11px] font-600 border-none cursor-pointer hover:brightness-110 disabled:opacity-50"
|
|
101
|
+
>
|
|
102
|
+
{restoringId === cp.checkpointId ? 'Restoring...' : 'Restore here'}
|
|
103
|
+
</button>
|
|
104
|
+
)}
|
|
91
105
|
</div>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
>
|
|
98
|
-
{restoringId === cp.checkpointId ? 'Restoring...' : 'Restore here'}
|
|
99
|
-
</button>
|
|
106
|
+
|
|
107
|
+
{cp.values && Array.isArray(cp.values.messages) && cp.values.messages.length > 0 && (
|
|
108
|
+
<div className="mt-1 p-2 rounded-[8px] bg-black/20 text-[11px] text-text-3 line-clamp-2 italic">
|
|
109
|
+
Last message: {String((cp.values.messages[cp.values.messages.length - 1] as Record<string, unknown>)?.content ?? 'Empty state')}
|
|
110
|
+
</div>
|
|
100
111
|
)}
|
|
101
112
|
</div>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
))}
|
|
114
|
+
</div>
|
|
115
|
+
<ConfirmDialog
|
|
116
|
+
open={!!confirmRestore}
|
|
117
|
+
title="Restore Session?"
|
|
118
|
+
message="Restore session to this point? This will delete all subsequent history."
|
|
119
|
+
confirmLabel={restoringId ? 'Restoring...' : 'Restore'}
|
|
120
|
+
confirmDisabled={!!restoringId}
|
|
121
|
+
cancelDisabled={!!restoringId}
|
|
122
|
+
danger
|
|
123
|
+
onConfirm={() => { void handleRestore() }}
|
|
124
|
+
onCancel={() => { if (!restoringId) setConfirmRestore(null) }}
|
|
125
|
+
/>
|
|
126
|
+
</>
|
|
111
127
|
)
|
|
112
128
|
}
|
|
@@ -23,5 +23,18 @@ describe('parseTaskCompletion', () => {
|
|
|
23
23
|
assert.equal(parsed?.reportPath, 'data/task-reports/abc12345.md')
|
|
24
24
|
assert.equal(parsed?.workingDir, '/tmp/work')
|
|
25
25
|
})
|
|
26
|
-
})
|
|
27
26
|
|
|
27
|
+
it('captures Gemini resume lines from task completion payloads', () => {
|
|
28
|
+
const text = [
|
|
29
|
+
'Task completed: **[Ship follow-up](#task:task-gemini)**',
|
|
30
|
+
'',
|
|
31
|
+
'Gemini session: `gemini-session-7`',
|
|
32
|
+
'',
|
|
33
|
+
'All done.',
|
|
34
|
+
].join('\n')
|
|
35
|
+
const parsed = parseTaskCompletion(text)
|
|
36
|
+
|
|
37
|
+
assert.ok(parsed)
|
|
38
|
+
assert.equal(parsed?.resumeInfo, 'Gemini session: `gemini-session-7`')
|
|
39
|
+
})
|
|
40
|
+
})
|
|
@@ -169,7 +169,7 @@ export function parseTaskCompletion(text: string): TaskCompletionInfo | null {
|
|
|
169
169
|
}
|
|
170
170
|
} else if (section.startsWith('Task report: ')) {
|
|
171
171
|
reportPath = section.replace('Task report: ', '').replace(/^`|`$/g, '')
|
|
172
|
-
} else if (/^(Claude session|Codex thread|OpenCode session|CLI session):/.test(section)) {
|
|
172
|
+
} else if (/^(Claude session|Codex thread|OpenCode session|Gemini session|CLI session):/.test(section)) {
|
|
173
173
|
resumeInfo = section
|
|
174
174
|
} else if (section.trim()) {
|
|
175
175
|
resultParts.push(section)
|