@swarmclawai/swarmclaw 0.8.4 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/bin/swarmclaw.js +5 -1
- package/bin/worker-cmd.js +73 -0
- package/package.json +2 -1
- package/src/app/api/agents/[id]/route.ts +17 -7
- package/src/app/api/agents/route.ts +21 -8
- package/src/app/api/approvals/route.test.ts +6 -6
- package/src/app/api/approvals/route.ts +2 -1
- package/src/app/api/auth/route.ts +2 -3
- package/src/app/api/chatrooms/[id]/chat/route.test.ts +299 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +3 -2
- package/src/app/api/chatrooms/[id]/route.ts +7 -6
- package/src/app/api/chats/[id]/chat/route.test.ts +496 -0
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/clear/route.ts +9 -9
- package/src/app/api/chats/[id]/devserver/route.ts +2 -1
- package/src/app/api/chats/[id]/edit-resend/route.ts +3 -4
- package/src/app/api/chats/[id]/fork/route.ts +3 -5
- package/src/app/api/chats/[id]/restore/route.ts +6 -7
- package/src/app/api/chats/[id]/retry/route.ts +3 -4
- package/src/app/api/chats/[id]/route.ts +61 -62
- package/src/app/api/chats/route.ts +7 -1
- package/src/app/api/connectors/[id]/route.ts +7 -8
- package/src/app/api/connectors/route.ts +5 -4
- package/src/app/api/eval/run/route.ts +2 -1
- package/src/app/api/eval/suite/route.ts +2 -1
- package/src/app/api/external-agents/route.test.ts +1 -1
- package/src/app/api/external-agents/route.ts +2 -2
- package/src/app/api/files/serve/route.ts +1 -1
- package/src/app/api/gateways/[id]/route.ts +7 -5
- package/src/app/api/gateways/route.ts +1 -1
- package/src/app/api/knowledge/upload/route.ts +1 -1
- package/src/app/api/logs/route.ts +5 -7
- package/src/app/api/memory-images/[filename]/route.ts +2 -3
- package/src/app/api/openclaw/agent-files/route.ts +4 -3
- package/src/app/api/openclaw/approvals/route.ts +3 -4
- package/src/app/api/openclaw/config-sync/route.ts +3 -2
- package/src/app/api/openclaw/cron/route.ts +3 -2
- package/src/app/api/openclaw/dotenv-keys/route.ts +2 -1
- package/src/app/api/openclaw/exec-config/route.ts +3 -2
- package/src/app/api/openclaw/gateway/route.ts +5 -4
- package/src/app/api/openclaw/history/route.ts +3 -2
- package/src/app/api/openclaw/media/route.ts +2 -1
- package/src/app/api/openclaw/permissions/route.ts +3 -2
- package/src/app/api/openclaw/sandbox-env/route.ts +3 -2
- package/src/app/api/openclaw/skills/install/route.ts +2 -1
- package/src/app/api/openclaw/skills/remove/route.ts +2 -1
- package/src/app/api/openclaw/skills/route.ts +3 -2
- package/src/app/api/orchestrator/run/route.ts +5 -14
- package/src/app/api/perf/route.ts +43 -0
- package/src/app/api/plugins/dependencies/route.ts +2 -1
- package/src/app/api/plugins/install/route.ts +2 -1
- package/src/app/api/plugins/marketplace/route.ts +3 -2
- package/src/app/api/plugins/settings/route.ts +2 -1
- package/src/app/api/preview-server/route.ts +11 -10
- package/src/app/api/projects/[id]/route.ts +1 -1
- package/src/app/api/schedules/[id]/route.test.ts +128 -0
- package/src/app/api/schedules/[id]/route.ts +43 -43
- package/src/app/api/schedules/[id]/run/route.ts +11 -62
- package/src/app/api/schedules/route.ts +21 -87
- package/src/app/api/settings/route.ts +2 -0
- package/src/app/api/setup/doctor/route.ts +9 -8
- package/src/app/api/tasks/[id]/approve/route.ts +33 -30
- package/src/app/api/tasks/[id]/route.ts +12 -35
- package/src/app/api/tasks/import/github/route.ts +2 -1
- package/src/app/api/tasks/route.ts +79 -91
- package/src/app/api/wallets/[id]/approve/route.ts +2 -1
- package/src/app/api/wallets/[id]/route.ts +13 -19
- package/src/app/api/wallets/[id]/send/route.ts +2 -1
- package/src/app/api/wallets/route.ts +2 -1
- package/src/app/api/webhooks/[id]/route.ts +2 -1
- package/src/app/api/webhooks/route.test.ts +3 -1
- package/src/app/page.tsx +23 -331
- package/src/cli/index.js +19 -0
- package/src/cli/index.ts +38 -7
- package/src/cli/spec.js +9 -0
- package/src/components/activity/activity-feed.tsx +7 -4
- package/src/components/agents/agent-card.tsx +32 -6
- package/src/components/agents/agent-chat-list.tsx +55 -22
- package/src/components/agents/agent-files-editor.tsx +3 -2
- package/src/components/agents/agent-sheet.tsx +123 -22
- package/src/components/agents/inspector-panel.tsx +1 -1
- package/src/components/agents/openclaw-skills-panel.tsx +2 -1
- package/src/components/agents/trash-list.tsx +1 -1
- package/src/components/auth/access-key-gate.tsx +8 -2
- package/src/components/auth/setup-wizard.tsx +10 -9
- package/src/components/auth/user-picker.tsx +3 -2
- package/src/components/chat/chat-area.tsx +20 -1
- package/src/components/chat/chat-card.tsx +18 -3
- package/src/components/chat/chat-header.tsx +24 -4
- package/src/components/chat/chat-list.tsx +2 -11
- package/src/components/chat/heartbeat-history-panel.tsx +2 -1
- package/src/components/chat/message-bubble.tsx +45 -6
- package/src/components/chat/message-list.tsx +280 -145
- package/src/components/chat/streaming-bubble.tsx +217 -60
- package/src/components/chat/swarm-panel.test.ts +274 -0
- package/src/components/chat/swarm-panel.tsx +410 -0
- package/src/components/chat/swarm-status-card.tsx +346 -0
- package/src/components/chat/tool-call-bubble.tsx +48 -23
- package/src/components/chatrooms/chatroom-list.tsx +8 -5
- package/src/components/chatrooms/chatroom-message.tsx +10 -7
- package/src/components/chatrooms/chatroom-view.tsx +12 -9
- package/src/components/connectors/connector-health.tsx +6 -4
- package/src/components/connectors/connector-list.tsx +16 -11
- package/src/components/connectors/connector-sheet.tsx +12 -6
- package/src/components/home/home-view.tsx +38 -24
- package/src/components/input/chat-input.tsx +10 -1
- package/src/components/layout/app-layout.tsx +2 -38
- package/src/components/layout/sheet-layer.tsx +50 -0
- package/src/components/mcp-servers/mcp-server-list.tsx +37 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +12 -2
- package/src/components/plugins/plugin-list.tsx +8 -4
- package/src/components/plugins/plugin-sheet.tsx +2 -1
- package/src/components/providers/provider-list.tsx +3 -2
- package/src/components/providers/provider-sheet.tsx +2 -1
- package/src/components/runs/run-list.tsx +11 -7
- package/src/components/schedules/schedule-card.tsx +5 -3
- package/src/components/shared/agent-switch-dialog.tsx +1 -1
- package/src/components/shared/attachment-chip.tsx +19 -3
- package/src/components/shared/notification-center.tsx +6 -3
- package/src/components/shared/settings/plugin-manager.tsx +3 -2
- package/src/components/shared/settings/section-embedding.tsx +2 -1
- package/src/components/shared/settings/section-orchestrator.tsx +2 -1
- package/src/components/shared/settings/section-user-preferences.tsx +107 -0
- package/src/components/shared/settings/settings-page.tsx +13 -9
- package/src/components/skills/clawhub-browser.tsx +15 -4
- package/src/components/skills/skill-list.tsx +15 -4
- package/src/components/tasks/approvals-panel.tsx +2 -1
- package/src/components/tasks/task-board.tsx +35 -37
- package/src/components/tasks/task-sheet.tsx +4 -3
- package/src/components/ui/full-screen-loader.tsx +164 -0
- package/src/components/wallets/wallet-approval-dialog.tsx +2 -1
- package/src/components/wallets/wallet-panel.tsx +6 -5
- package/src/components/wallets/wallet-section.tsx +3 -2
- package/src/components/webhooks/webhook-list.tsx +4 -5
- package/src/components/webhooks/webhook-sheet.tsx +6 -6
- package/src/hooks/use-app-bootstrap.ts +202 -0
- package/src/hooks/use-mounted-ref.ts +14 -0
- package/src/hooks/use-now.ts +31 -0
- package/src/hooks/use-openclaw-gateway.ts +2 -1
- package/src/instrumentation.ts +20 -8
- package/src/lib/agent-default-tools.test.ts +52 -0
- package/src/lib/agent-default-tools.ts +40 -0
- package/src/lib/api-client.test.ts +21 -0
- package/src/lib/api-client.ts +6 -11
- package/src/lib/canvas-content.test.ts +360 -0
- package/src/lib/chat-streaming-state.test.ts +49 -2
- package/src/lib/chat-streaming-state.ts +26 -10
- package/src/lib/fetch-timeout.test.ts +54 -0
- package/src/lib/fetch-timeout.ts +60 -3
- package/src/lib/live-tool-events.test.ts +77 -0
- package/src/lib/live-tool-events.ts +73 -0
- package/src/lib/local-observability.test.ts +2 -2
- package/src/lib/openclaw-endpoint.test.ts +1 -1
- package/src/lib/providers/anthropic.ts +12 -16
- package/src/lib/providers/index.ts +4 -2
- package/src/lib/providers/ollama.ts +9 -6
- package/src/lib/providers/openai.ts +11 -14
- package/src/lib/runtime-env.test.ts +8 -8
- package/src/lib/schedule-dedupe-advanced.test.ts +2 -2
- package/src/lib/schedule-dedupe.test.ts +1 -1
- package/src/lib/schedule-dedupe.ts +3 -2
- package/src/lib/server/agent-thread-session.test.ts +6 -6
- package/src/lib/server/agent-thread-session.ts +6 -9
- package/src/lib/server/alert-dispatch.ts +2 -1
- package/src/lib/server/api-routes.test.ts +6 -6
- package/src/lib/server/approval-connector-notify.test.ts +4 -4
- package/src/lib/server/approvals-auto-approve.test.ts +29 -29
- package/src/lib/server/approvals.test.ts +317 -0
- package/src/lib/server/approvals.ts +5 -4
- package/src/lib/server/autonomy-runtime.test.ts +11 -11
- package/src/lib/server/browser-state.ts +2 -2
- package/src/lib/server/capability-router.test.ts +1 -1
- package/src/lib/server/capability-router.ts +3 -2
- package/src/lib/server/chat-execution-advanced.test.ts +15 -2
- package/src/lib/server/chat-execution-connector-delivery.ts +67 -0
- package/src/lib/server/chat-execution-disabled.test.ts +3 -3
- package/src/lib/server/chat-execution-eval-history.test.ts +3 -3
- package/src/lib/server/chat-execution-heartbeat.test.ts +42 -1
- package/src/lib/server/chat-execution-session-sync.test.ts +119 -0
- package/src/lib/server/chat-execution-tool-events.ts +116 -0
- package/src/lib/server/chat-execution-utils.test.ts +479 -0
- package/src/lib/server/chat-execution-utils.ts +533 -0
- package/src/lib/server/chat-execution.ts +153 -748
- package/src/lib/server/chat-streaming-utils.ts +174 -0
- package/src/lib/server/chat-turn-tool-routing.ts +310 -0
- package/src/lib/server/chatroom-session-persistence.test.ts +2 -2
- package/src/lib/server/clawhub-client.ts +2 -1
- package/src/lib/server/collection-helpers.test.ts +92 -0
- package/src/lib/server/collection-helpers.ts +25 -3
- package/src/lib/server/connectors/access.ts +146 -0
- package/src/lib/server/connectors/bluebubbles.test.ts +1 -1
- package/src/lib/server/connectors/bluebubbles.ts +4 -4
- package/src/lib/server/connectors/commands.ts +367 -0
- package/src/lib/server/connectors/connector-routing.test.ts +4 -4
- package/src/lib/server/connectors/delivery.ts +142 -0
- package/src/lib/server/connectors/discord.ts +37 -40
- package/src/lib/server/connectors/email.ts +11 -10
- package/src/lib/server/connectors/googlechat.ts +4 -4
- package/src/lib/server/connectors/inbound-audio-transcription.ts +2 -1
- package/src/lib/server/connectors/ingress-delivery.ts +23 -0
- package/src/lib/server/connectors/manager-roundtrip.test.ts +300 -0
- package/src/lib/server/connectors/manager.test.ts +352 -77
- package/src/lib/server/connectors/manager.ts +134 -673
- package/src/lib/server/connectors/matrix.ts +4 -4
- package/src/lib/server/connectors/message-sentinel.ts +7 -0
- package/src/lib/server/connectors/openclaw.test.ts +1 -1
- package/src/lib/server/connectors/openclaw.ts +8 -10
- package/src/lib/server/connectors/outbox.test.ts +192 -0
- package/src/lib/server/connectors/outbox.ts +369 -0
- package/src/lib/server/connectors/pairing.test.ts +18 -1
- package/src/lib/server/connectors/pairing.ts +49 -4
- package/src/lib/server/connectors/policy.ts +9 -3
- package/src/lib/server/connectors/reconnect-state.ts +71 -0
- package/src/lib/server/connectors/response-media.ts +256 -0
- package/src/lib/server/connectors/runtime-state.ts +67 -0
- package/src/lib/server/connectors/session.test.ts +357 -0
- package/src/lib/server/connectors/session.ts +422 -0
- package/src/lib/server/connectors/signal.ts +7 -7
- package/src/lib/server/connectors/slack.ts +43 -43
- package/src/lib/server/connectors/teams.ts +4 -4
- package/src/lib/server/connectors/telegram.ts +37 -43
- package/src/lib/server/connectors/types.ts +31 -1
- package/src/lib/server/connectors/whatsapp.test.ts +108 -0
- package/src/lib/server/connectors/whatsapp.ts +106 -34
- package/src/lib/server/context-manager.test.ts +409 -0
- package/src/lib/server/cost.test.ts +1 -1
- package/src/lib/server/daemon-policy.ts +78 -0
- package/src/lib/server/daemon-state-connectors.test.ts +167 -0
- package/src/lib/server/daemon-state.test.ts +283 -55
- package/src/lib/server/daemon-state.ts +106 -109
- package/src/lib/server/data-dir.test.ts +5 -5
- package/src/lib/server/data-dir.ts +4 -0
- package/src/lib/server/delegation-jobs-advanced.test.ts +1 -1
- package/src/lib/server/delegation-jobs.test.ts +87 -0
- package/src/lib/server/delegation-jobs.ts +42 -48
- package/src/lib/server/devserver-launch.ts +1 -1
- package/src/lib/server/document-utils.ts +7 -9
- package/src/lib/server/elevenlabs.ts +2 -1
- package/src/lib/server/embeddings.test.ts +105 -0
- package/src/lib/server/ethereum.ts +3 -2
- package/src/lib/server/eval/agent-regression.ts +3 -2
- package/src/lib/server/eval/runner.ts +2 -1
- package/src/lib/server/eval/scorer.ts +2 -1
- package/src/lib/server/evm-swap.ts +2 -1
- package/src/lib/server/gateway/protocol.test.ts +1 -1
- package/src/lib/server/guardian.ts +2 -1
- package/src/lib/server/heartbeat-blocked-suppression.test.ts +151 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +6 -6
- package/src/lib/server/heartbeat-service.test.ts +406 -0
- package/src/lib/server/heartbeat-service.ts +54 -7
- package/src/lib/server/heartbeat-wake.test.ts +19 -0
- package/src/lib/server/heartbeat-wake.ts +17 -16
- package/src/lib/server/integrity-monitor.test.ts +149 -0
- package/src/lib/server/json-utils.ts +22 -0
- package/src/lib/server/knowledge-db.test.ts +13 -13
- package/src/lib/server/link-understanding.ts +2 -1
- package/src/lib/server/llm-response-cache.test.ts +1 -1
- package/src/lib/server/main-agent-loop-advanced.test.ts +65 -3
- package/src/lib/server/main-agent-loop.test.ts +6 -6
- package/src/lib/server/main-agent-loop.ts +21 -7
- package/src/lib/server/mcp-client.test.ts +1 -1
- package/src/lib/server/mcp-conformance.test.ts +1 -1
- package/src/lib/server/mcp-conformance.ts +3 -2
- package/src/lib/server/memory-consolidation.ts +2 -1
- package/src/lib/server/memory-db.test.ts +485 -0
- package/src/lib/server/memory-db.ts +39 -26
- package/src/lib/server/memory-graph.test.ts +2 -2
- package/src/lib/server/memory-policy.test.ts +7 -7
- package/src/lib/server/memory-retrieval.test.ts +1 -1
- package/src/lib/server/openclaw-config-sync.ts +2 -1
- package/src/lib/server/openclaw-deploy.test.ts +1 -1
- package/src/lib/server/openclaw-deploy.ts +8 -12
- package/src/lib/server/openclaw-exec-config.ts +2 -1
- package/src/lib/server/openclaw-gateway.ts +6 -7
- package/src/lib/server/openclaw-skills-normalize.ts +2 -1
- package/src/lib/server/openclaw-sync.ts +7 -5
- package/src/lib/server/orchestrator-lg-structure.test.ts +17 -0
- package/src/lib/server/orchestrator-lg.ts +199 -327
- package/src/lib/server/path-utils.ts +31 -0
- package/src/lib/server/perf.ts +161 -0
- package/src/lib/server/plugins-approval-guidance.ts +115 -0
- package/src/lib/server/plugins.test.ts +1 -1
- package/src/lib/server/plugins.ts +22 -132
- package/src/lib/server/process-manager.ts +5 -8
- package/src/lib/server/provider-health.test.ts +137 -0
- package/src/lib/server/provider-health.ts +3 -3
- package/src/lib/server/provider-model-discovery.ts +3 -12
- package/src/lib/server/queue-followups.test.ts +9 -9
- package/src/lib/server/queue-reconcile.test.ts +2 -2
- package/src/lib/server/queue-recovery.test.ts +269 -0
- package/src/lib/server/queue.test.ts +570 -0
- package/src/lib/server/queue.ts +62 -455
- package/src/lib/server/resolve-image.ts +30 -0
- package/src/lib/server/runtime-settings.test.ts +4 -4
- package/src/lib/server/runtime-storage-write-paths.test.ts +60 -0
- package/src/lib/server/schedule-normalization.test.ts +279 -0
- package/src/lib/server/schedule-service.ts +263 -0
- package/src/lib/server/scheduler.ts +17 -74
- package/src/lib/server/session-mailbox.test.ts +191 -0
- package/src/lib/server/session-run-manager.test.ts +640 -0
- package/src/lib/server/session-run-manager.ts +59 -15
- package/src/lib/server/session-tools/autonomy-tools.test.ts +20 -20
- package/src/lib/server/session-tools/calendar.ts +2 -1
- package/src/lib/server/session-tools/canvas.ts +2 -1
- package/src/lib/server/session-tools/chatroom.ts +2 -1
- package/src/lib/server/session-tools/connector.ts +26 -28
- package/src/lib/server/session-tools/context-mgmt.ts +3 -2
- package/src/lib/server/session-tools/crawl.ts +4 -3
- package/src/lib/server/session-tools/crud.ts +105 -324
- package/src/lib/server/session-tools/delegate-fallback.test.ts +9 -9
- package/src/lib/server/session-tools/delegate.ts +6 -8
- package/src/lib/server/session-tools/discovery-approvals.test.ts +15 -15
- package/src/lib/server/session-tools/discovery.ts +4 -3
- package/src/lib/server/session-tools/document.ts +2 -1
- package/src/lib/server/session-tools/email.ts +2 -1
- package/src/lib/server/session-tools/extract.ts +2 -1
- package/src/lib/server/session-tools/file.ts +4 -3
- package/src/lib/server/session-tools/http.ts +2 -1
- package/src/lib/server/session-tools/human-loop.ts +2 -1
- package/src/lib/server/session-tools/image-gen.ts +4 -3
- package/src/lib/server/session-tools/index.ts +26 -30
- package/src/lib/server/session-tools/mailbox.ts +2 -1
- package/src/lib/server/session-tools/manage-connectors.test.ts +4 -4
- package/src/lib/server/session-tools/manage-schedules.test.ts +12 -12
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +5 -5
- package/src/lib/server/session-tools/manage-tasks.test.ts +2 -2
- package/src/lib/server/session-tools/monitor.ts +2 -1
- package/src/lib/server/session-tools/platform.ts +2 -1
- package/src/lib/server/session-tools/plugin-creator.ts +2 -1
- package/src/lib/server/session-tools/replicate.ts +3 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +6 -6
- package/src/lib/server/session-tools/shell.ts +4 -9
- package/src/lib/server/session-tools/subagent.ts +322 -170
- package/src/lib/server/session-tools/table.ts +6 -5
- package/src/lib/server/session-tools/wallet-tool.test.ts +3 -3
- package/src/lib/server/session-tools/wallet.ts +7 -6
- package/src/lib/server/session-tools/web-browser-config.test.ts +1 -0
- package/src/lib/server/session-tools/web-utils.ts +317 -0
- package/src/lib/server/session-tools/web.ts +62 -328
- package/src/lib/server/skill-prompt-budget.test.ts +1 -1
- package/src/lib/server/skills-normalize.ts +2 -1
- package/src/lib/server/storage-item-access.test.ts +302 -0
- package/src/lib/server/storage.ts +366 -314
- package/src/lib/server/stream-agent-chat.test.ts +82 -3
- package/src/lib/server/stream-agent-chat.ts +146 -510
- package/src/lib/server/stream-continuation.ts +412 -0
- package/src/lib/server/subagent-lineage.test.ts +647 -0
- package/src/lib/server/subagent-lineage.ts +435 -0
- package/src/lib/server/subagent-runtime.test.ts +484 -0
- package/src/lib/server/subagent-runtime.ts +419 -0
- package/src/lib/server/subagent-swarm.test.ts +391 -0
- package/src/lib/server/subagent-swarm.ts +564 -0
- package/src/lib/server/system-events.ts +3 -3
- package/src/lib/server/task-followups.test.ts +491 -0
- package/src/lib/server/task-followups.ts +391 -0
- package/src/lib/server/task-lifecycle.test.ts +205 -0
- package/src/lib/server/task-lifecycle.ts +200 -0
- package/src/lib/server/task-quality-gate.test.ts +1 -1
- package/src/lib/server/task-resume.ts +208 -0
- package/src/lib/server/task-service.test.ts +108 -0
- package/src/lib/server/task-service.ts +264 -0
- package/src/lib/server/task-validation.test.ts +1 -1
- package/src/lib/server/test-utils/run-with-temp-data-dir.ts +42 -0
- package/src/lib/server/tool-capability-policy.test.ts +2 -2
- package/src/lib/server/tool-capability-policy.ts +3 -2
- package/src/lib/server/tool-planning.ts +2 -1
- package/src/lib/server/tool-retry.ts +2 -3
- package/src/lib/server/wake-dispatcher.test.ts +303 -0
- package/src/lib/server/wake-dispatcher.ts +318 -0
- package/src/lib/server/wake-mode.test.ts +161 -0
- package/src/lib/server/wake-mode.ts +174 -0
- package/src/lib/server/wallet-service.ts +8 -9
- package/src/lib/server/watch-jobs.ts +2 -1
- package/src/lib/server/workspace-context.ts +2 -2
- package/src/lib/shared-utils.test.ts +142 -0
- package/src/lib/shared-utils.ts +62 -0
- package/src/lib/tool-event-summary.ts +2 -1
- package/src/lib/view-routes.test.ts +100 -0
- package/src/lib/wallet.test.ts +322 -6
- package/src/proxy.test.ts +4 -4
- package/src/proxy.ts +2 -3
- package/src/stores/set-if-changed.ts +40 -0
- package/src/stores/slices/agent-slice.ts +111 -0
- package/src/stores/slices/auth-slice.ts +25 -0
- package/src/stores/slices/data-slice.ts +301 -0
- package/src/stores/slices/index.ts +7 -0
- package/src/stores/slices/session-slice.ts +112 -0
- package/src/stores/slices/task-slice.ts +63 -0
- package/src/stores/slices/ui-slice.ts +192 -0
- package/src/stores/use-app-store.ts +17 -822
- package/src/stores/use-approval-store.ts +2 -1
- package/src/stores/use-chat-store.ts +8 -1
- package/src/types/index.ts +10 -0
|
@@ -4,7 +4,8 @@ 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 {
|
|
7
|
+
import { useNow } from '@/hooks/use-now'
|
|
8
|
+
import { useMountedRef } from '@/hooks/use-mounted-ref'
|
|
8
9
|
import { api } from '@/lib/api-client'
|
|
9
10
|
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
10
11
|
import type { Agent, Session } from '@/types'
|
|
@@ -17,12 +18,13 @@ interface Props {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
21
|
+
const mountedRef = useMountedRef()
|
|
22
|
+
const now = useNow()
|
|
20
23
|
const agents = useAppStore((s) => s.agents)
|
|
21
24
|
const sessions = useAppStore((s) => s.sessions)
|
|
22
25
|
const loadAgents = useAppStore((s) => s.loadAgents)
|
|
23
26
|
const currentAgentId = useAppStore((s) => s.currentAgentId)
|
|
24
27
|
const setCurrentAgent = useAppStore((s) => s.setCurrentAgent)
|
|
25
|
-
const setMessages = useChatStore((s) => s.setMessages)
|
|
26
28
|
const setAgentSheetOpen = useAppStore((s) => s.setAgentSheetOpen)
|
|
27
29
|
const tasks = useAppStore((s) => s.tasks)
|
|
28
30
|
const togglePinAgent = useAppStore((s) => s.togglePinAgent)
|
|
@@ -60,17 +62,19 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
60
62
|
try {
|
|
61
63
|
await api('DELETE', '/chats', { ids: sessionIds })
|
|
62
64
|
await loadSessions()
|
|
65
|
+
if (!mountedRef.current) return
|
|
63
66
|
toast.success(`Deleted ${sessionIds.length} chat(s)`)
|
|
64
67
|
setBulkMode(false)
|
|
65
68
|
setSelectedIds(new Set())
|
|
66
69
|
} catch {
|
|
67
70
|
toast.error('Failed to delete chats')
|
|
68
71
|
}
|
|
69
|
-
}, [selectedIds, agents, loadSessions])
|
|
72
|
+
}, [selectedIds, agents, loadSessions, mountedRef])
|
|
70
73
|
|
|
71
74
|
// FLIP animation refs
|
|
72
75
|
const rowRefs = useRef<Map<string, HTMLElement>>(new Map())
|
|
73
76
|
const previousTopRef = useRef<Map<string, number>>(new Map())
|
|
77
|
+
const scrollTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
74
78
|
|
|
75
79
|
const setRowRef = useCallback((id: string, el: HTMLElement | null) => {
|
|
76
80
|
if (el) rowRefs.current.set(id, el)
|
|
@@ -79,6 +83,14 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
79
83
|
|
|
80
84
|
useEffect(() => { loadAgents() }, [loadAgents])
|
|
81
85
|
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
return () => {
|
|
88
|
+
if (scrollTimerRef.current) {
|
|
89
|
+
clearTimeout(scrollTimerRef.current)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}, [])
|
|
93
|
+
|
|
82
94
|
// Build agent list sorted by last activity in their thread session
|
|
83
95
|
const sortedAgents = useMemo(() => {
|
|
84
96
|
return Object.values(agents)
|
|
@@ -98,7 +110,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
98
110
|
// Compute agents active in chatrooms (message in last 30min or currently streaming)
|
|
99
111
|
const chatroomActiveAgentIds = useMemo(() => {
|
|
100
112
|
const set = new Set<string>()
|
|
101
|
-
const cutoff =
|
|
113
|
+
const cutoff = now ? now - 30 * 60 * 1000 : Number.POSITIVE_INFINITY
|
|
102
114
|
for (const chatroom of Object.values(chatrooms)) {
|
|
103
115
|
for (let i = chatroom.messages.length - 1; i >= 0; i--) {
|
|
104
116
|
const msg = chatroom.messages[i]
|
|
@@ -108,7 +120,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
108
120
|
}
|
|
109
121
|
for (const agentId of chatroomStreaming.keys()) set.add(agentId)
|
|
110
122
|
return set
|
|
111
|
-
}, [chatrooms, chatroomStreaming])
|
|
123
|
+
}, [chatrooms, chatroomStreaming, now])
|
|
112
124
|
|
|
113
125
|
// Compute running tasks per agent
|
|
114
126
|
const runningAgentIds = useMemo(() => {
|
|
@@ -122,7 +134,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
122
134
|
// Apply chatFilter
|
|
123
135
|
const filteredAgents = useMemo(() => {
|
|
124
136
|
if (chatFilter === 'all') return sortedAgents
|
|
125
|
-
|
|
137
|
+
if (!now) return sortedAgents
|
|
126
138
|
return sortedAgents.filter((a) => {
|
|
127
139
|
const threadSession = a.threadSessionId ? sessions[a.threadSessionId] as Session | undefined : undefined
|
|
128
140
|
const isRunning = runningAgentIds.has(a.id) || (threadSession?.active ?? false)
|
|
@@ -133,7 +145,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
133
145
|
const lastActive = threadSession?.lastActiveAt || a.updatedAt
|
|
134
146
|
return now - lastActive < 86_400_000
|
|
135
147
|
})
|
|
136
|
-
}, [sortedAgents, chatFilter, sessions, runningAgentIds, streamingSessionId, chatroomActiveAgentIds])
|
|
148
|
+
}, [sortedAgents, chatFilter, sessions, runningAgentIds, streamingSessionId, chatroomActiveAgentIds, now])
|
|
137
149
|
|
|
138
150
|
const defaultAgent = useMemo(() => {
|
|
139
151
|
const id = appSettings.defaultAgentId
|
|
@@ -172,20 +184,13 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
172
184
|
return
|
|
173
185
|
}
|
|
174
186
|
await setCurrentAgent(agent.id)
|
|
175
|
-
// Load messages for the thread
|
|
176
|
-
const state = useAppStore.getState()
|
|
177
|
-
if (state.currentSessionId) {
|
|
178
|
-
try {
|
|
179
|
-
const msgs = await fetchMessages(state.currentSessionId)
|
|
180
|
-
setMessages(msgs)
|
|
181
|
-
} catch (err: unknown) {
|
|
182
|
-
console.error('[agent-chat-list] Failed to load messages:', err instanceof Error ? err.message : String(err))
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
187
|
onSelect?.()
|
|
186
188
|
// Delay scroll so React renders the new messages first
|
|
187
|
-
if (typeof window !== 'undefined') {
|
|
188
|
-
|
|
189
|
+
if (mountedRef.current && typeof window !== 'undefined') {
|
|
190
|
+
if (scrollTimerRef.current) {
|
|
191
|
+
clearTimeout(scrollTimerRef.current)
|
|
192
|
+
}
|
|
193
|
+
scrollTimerRef.current = setTimeout(() => {
|
|
189
194
|
window.dispatchEvent(new CustomEvent('swarmclaw:scroll-bottom'))
|
|
190
195
|
}, 100)
|
|
191
196
|
}
|
|
@@ -218,7 +223,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
218
223
|
}
|
|
219
224
|
|
|
220
225
|
return (
|
|
221
|
-
<div className="flex-1 overflow-y-auto">
|
|
226
|
+
<div className="flex-1 overflow-y-auto" data-testid="agent-chat-list">
|
|
222
227
|
{/* Filter control + bulk mode toggle */}
|
|
223
228
|
{sortedAgents.length > 2 && (
|
|
224
229
|
<div className="flex items-center gap-1 px-4 pt-2.5 pb-1">
|
|
@@ -266,6 +271,8 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
266
271
|
value={search}
|
|
267
272
|
onChange={(e) => setSearch(e.target.value)}
|
|
268
273
|
placeholder="Search agents..."
|
|
274
|
+
aria-label="Search agents"
|
|
275
|
+
data-testid="agent-search"
|
|
269
276
|
className="w-full px-4 py-2.5 rounded-[12px] border border-white/[0.04] bg-surface text-text
|
|
270
277
|
text-[13px] outline-none transition-all duration-200 placeholder:text-text-3/70 focus-glow"
|
|
271
278
|
style={{ fontFamily: 'inherit' }}
|
|
@@ -277,7 +284,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
277
284
|
const threadSession = defaultAgent.threadSessionId ? sessions[defaultAgent.threadSessionId] as Session | undefined : undefined
|
|
278
285
|
const lastMsg = threadSession?.messages?.at(-1)
|
|
279
286
|
const heartbeatOn = defaultAgent.heartbeatEnabled === true && (defaultAgent.plugins?.length ?? 0) > 0
|
|
280
|
-
const recentlyActive = (threadSession?.lastActiveAt ?? 0) >
|
|
287
|
+
const recentlyActive = !!now && (threadSession?.lastActiveAt ?? 0) > now - 30 * 60 * 1000
|
|
281
288
|
const isDisabled = defaultAgent.disabled === true
|
|
282
289
|
const isWorking = !isDisabled && (runningAgentIds.has(defaultAgent.id) || (threadSession?.active ?? false) || heartbeatOn || recentlyActive || chatroomActiveAgentIds.has(defaultAgent.id))
|
|
283
290
|
const isTyping = streamingSessionId === defaultAgent.threadSessionId
|
|
@@ -294,7 +301,20 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
294
301
|
${isActive
|
|
295
302
|
? 'bg-accent-soft border-accent-bright/25'
|
|
296
303
|
: 'bg-accent-soft/40 border-accent-bright/15 hover:bg-accent-soft/55'}`}
|
|
304
|
+
role="button"
|
|
305
|
+
tabIndex={0}
|
|
306
|
+
data-testid="agent-row"
|
|
307
|
+
data-agent-id={defaultAgent.id}
|
|
308
|
+
data-agent-name={defaultAgent.name}
|
|
309
|
+
aria-label={`Open agent chat ${defaultAgent.name}`}
|
|
297
310
|
onClick={() => bulkMode ? toggleSelected(defaultAgent.id) : handleSelect(defaultAgent)}
|
|
311
|
+
onKeyDown={(event) => {
|
|
312
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
313
|
+
event.preventDefault()
|
|
314
|
+
if (bulkMode) toggleSelected(defaultAgent.id)
|
|
315
|
+
else void handleSelect(defaultAgent)
|
|
316
|
+
}
|
|
317
|
+
}}
|
|
298
318
|
>
|
|
299
319
|
<div className="flex items-center gap-3">
|
|
300
320
|
{bulkMode && (
|
|
@@ -368,7 +388,7 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
368
388
|
const lastMsg = threadSession?.messages?.at(-1)
|
|
369
389
|
const isActive = currentAgentId === agent.id
|
|
370
390
|
const heartbeatOn = agent.heartbeatEnabled === true && (agent.plugins?.length ?? 0) > 0
|
|
371
|
-
const recentlyActive = (threadSession?.lastActiveAt ?? 0) >
|
|
391
|
+
const recentlyActive = !!now && (threadSession?.lastActiveAt ?? 0) > now - 30 * 60 * 1000
|
|
372
392
|
const isDisabled = agent.disabled === true
|
|
373
393
|
const isWorking = !isDisabled && (runningAgentIds.has(agent.id) || (threadSession?.active ?? false) || heartbeatOn || recentlyActive || chatroomActiveAgentIds.has(agent.id))
|
|
374
394
|
const isTyping = streamingSessionId === agent.threadSessionId
|
|
@@ -382,7 +402,20 @@ export function AgentChatList({ inSidebar, onSelect }: Props) {
|
|
|
382
402
|
${isActive
|
|
383
403
|
? 'bg-accent-soft/80 border border-accent-bright/20'
|
|
384
404
|
: 'bg-transparent hover:bg-white/[0.02]'}`}
|
|
405
|
+
role="button"
|
|
406
|
+
tabIndex={0}
|
|
407
|
+
data-testid="agent-row"
|
|
408
|
+
data-agent-id={agent.id}
|
|
409
|
+
data-agent-name={agent.name}
|
|
410
|
+
aria-label={`Open agent chat ${agent.name}`}
|
|
385
411
|
onClick={() => bulkMode ? toggleSelected(agent.id) : handleSelect(agent)}
|
|
412
|
+
onKeyDown={(event) => {
|
|
413
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
414
|
+
event.preventDefault()
|
|
415
|
+
if (bulkMode) toggleSelected(agent.id)
|
|
416
|
+
else void handleSelect(agent)
|
|
417
|
+
}
|
|
418
|
+
}}
|
|
386
419
|
>
|
|
387
420
|
<div className="flex items-center gap-2.5">
|
|
388
421
|
{bulkMode && (
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useState } from 'react'
|
|
4
4
|
import { api } from '@/lib/api-client'
|
|
5
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
5
6
|
import { PersonalityBuilder } from './personality-builder'
|
|
6
7
|
|
|
7
8
|
const FILES = ['SOUL.md', 'IDENTITY.md', 'USER.md', 'TOOLS.md', 'HEARTBEAT.md', 'MEMORY.md', 'AGENTS.md'] as const
|
|
@@ -47,7 +48,7 @@ export function AgentFilesEditor({ agentId }: Props) {
|
|
|
47
48
|
return next
|
|
48
49
|
})
|
|
49
50
|
} catch (err: unknown) {
|
|
50
|
-
const message =
|
|
51
|
+
const message = errorMessage(err)
|
|
51
52
|
setFiles((prev) => {
|
|
52
53
|
const next = { ...prev }
|
|
53
54
|
for (const f of FILES) {
|
|
@@ -83,7 +84,7 @@ export function AgentFilesEditor({ agentId }: Props) {
|
|
|
83
84
|
[filename]: { ...prev[filename], saving: false, original: prev[filename].content },
|
|
84
85
|
}))
|
|
85
86
|
} catch (err: unknown) {
|
|
86
|
-
const message =
|
|
87
|
+
const message = errorMessage(err)
|
|
87
88
|
setFiles((prev) => ({
|
|
88
89
|
...prev,
|
|
89
90
|
[filename]: { ...prev[filename], saving: false, error: message },
|
|
@@ -4,6 +4,8 @@ import { useCallback, useEffect, useRef, useState } from 'react'
|
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
5
|
import { createAgent, updateAgent, deleteAgent } from '@/lib/agents'
|
|
6
6
|
import { api } from '@/lib/api-client'
|
|
7
|
+
import { fetchProviderModelDiscovery } from '@/lib/provider-model-discovery-client'
|
|
8
|
+
import { sleep } from '@/lib/shared-utils'
|
|
7
9
|
import { BottomSheet } from '@/components/shared/bottom-sheet'
|
|
8
10
|
import { toast } from 'sonner'
|
|
9
11
|
import { ModelCombobox } from '@/components/shared/model-combobox'
|
|
@@ -19,9 +21,23 @@ import { SectionLabel } from '@/components/shared/section-label'
|
|
|
19
21
|
import { SoulLibraryPicker } from './soul-library-picker'
|
|
20
22
|
import { HintTip } from '@/components/shared/hint-tip'
|
|
21
23
|
import { isOllamaCloudModel } from '@/lib/ollama-model'
|
|
24
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
25
|
+
import { getDefaultAgentPluginIds } from '@/lib/agent-default-tools'
|
|
22
26
|
|
|
23
27
|
const HB_PRESETS = [1800, 3600, 7200, 21600, 43200] as const
|
|
24
28
|
const FALLBACK_ELEVENLABS_VOICE_ID = 'JBFqnCBsd6RMkjVDRZzb'
|
|
29
|
+
const AUTO_SYNC_MODEL_PROVIDER_IDS = new Set<ProviderType>([
|
|
30
|
+
'openai',
|
|
31
|
+
'anthropic',
|
|
32
|
+
'google',
|
|
33
|
+
'deepseek',
|
|
34
|
+
'groq',
|
|
35
|
+
'together',
|
|
36
|
+
'mistral',
|
|
37
|
+
'xai',
|
|
38
|
+
'fireworks',
|
|
39
|
+
'ollama',
|
|
40
|
+
])
|
|
25
41
|
|
|
26
42
|
type AgentSheetSectionId = 'overview' | 'instructions' | 'model' | 'tools'
|
|
27
43
|
type SafeAgentWallet = Omit<AgentWallet, 'encryptedPrivateKey'> & {
|
|
@@ -137,6 +153,12 @@ export function AgentSheet() {
|
|
|
137
153
|
const setEditingId = useAppStore((s) => s.setEditingAgentId)
|
|
138
154
|
const agents = useAppStore((s) => s.agents)
|
|
139
155
|
const loadAgents = useAppStore((s) => s.loadAgents)
|
|
156
|
+
const currentSessionId = useAppStore((s) => s.currentSessionId)
|
|
157
|
+
const currentSession = useAppStore((s) => {
|
|
158
|
+
const id = s.currentSessionId
|
|
159
|
+
return id ? s.sessions[id] : null
|
|
160
|
+
})
|
|
161
|
+
const refreshSession = useAppStore((s) => s.refreshSession)
|
|
140
162
|
const projects = useAppStore((s) => s.projects)
|
|
141
163
|
const loadProjects = useAppStore((s) => s.loadProjects)
|
|
142
164
|
const providers = useAppStore((s) => s.providers)
|
|
@@ -238,6 +260,7 @@ export function AgentSheet() {
|
|
|
238
260
|
const [soulLibraryOpen, setSoulLibraryOpen] = useState(false)
|
|
239
261
|
const promptFileRef = useRef<HTMLInputElement>(null)
|
|
240
262
|
const importFileRef = useRef<HTMLInputElement>(null)
|
|
263
|
+
const lastAutoSyncedModelsKeyRef = useRef<string | null>(null)
|
|
241
264
|
const sectionRefs = useRef<Record<AgentSheetSectionId, HTMLDivElement | null>>({
|
|
242
265
|
overview: null,
|
|
243
266
|
instructions: null,
|
|
@@ -288,12 +311,62 @@ export function AgentSheet() {
|
|
|
288
311
|
: 'Built-in fallback'
|
|
289
312
|
const providerSummary = openclawEnabled ? 'OpenClaw gateway' : (currentProvider?.name || provider)
|
|
290
313
|
const modelSummary = openclawEnabled ? (gatewayProfileId ? 'Gateway-managed' : 'default') : (model || 'Select a model')
|
|
314
|
+
const syncLiveProviderModels = useCallback(async (
|
|
315
|
+
providerId: ProviderType,
|
|
316
|
+
nextCredentialId: string | null,
|
|
317
|
+
nextEndpoint: string | null,
|
|
318
|
+
force = false,
|
|
319
|
+
): Promise<{ synced: boolean; models: string[] } | null> => {
|
|
320
|
+
if (openclawEnabled) return null
|
|
321
|
+
if (!AUTO_SYNC_MODEL_PROVIDER_IDS.has(providerId)) return null
|
|
322
|
+
const providerInfo = providers.find((item) => item.id === providerId)
|
|
323
|
+
if (!providerInfo?.supportsModelDiscovery) return null
|
|
324
|
+
|
|
325
|
+
const result = await fetchProviderModelDiscovery({
|
|
326
|
+
providerId,
|
|
327
|
+
credentialId: nextCredentialId,
|
|
328
|
+
endpoint: nextEndpoint,
|
|
329
|
+
force,
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
if (!result.ok || result.models.length === 0) return { synced: false, models: result.models }
|
|
333
|
+
|
|
334
|
+
const sameModels = providerInfo.models.length === result.models.length
|
|
335
|
+
&& providerInfo.models.every((item, index) => item === result.models[index])
|
|
336
|
+
|
|
337
|
+
if (!sameModels) {
|
|
338
|
+
await api('PUT', `/providers/${providerId}/models`, { models: result.models })
|
|
339
|
+
await loadProviders()
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
setModel((currentModel) => currentModel.trim() || result.models[0] || '')
|
|
343
|
+
return { synced: !sameModels, models: result.models }
|
|
344
|
+
}, [loadProviders, openclawEnabled, providers])
|
|
291
345
|
|
|
292
346
|
const providerNeedsKey = !editing && (
|
|
293
347
|
(currentProvider?.requiresApiKey && providerCredentials.length === 0 && !addingKey) ||
|
|
294
348
|
(provider === 'ollama' && ollamaMode === 'cloud' && providerCredentials.length === 0 && !addingKey)
|
|
295
349
|
)
|
|
296
350
|
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
if (!open) {
|
|
353
|
+
lastAutoSyncedModelsKeyRef.current = null
|
|
354
|
+
return
|
|
355
|
+
}
|
|
356
|
+
if (openclawEnabled) return
|
|
357
|
+
if (!AUTO_SYNC_MODEL_PROVIDER_IDS.has(provider)) return
|
|
358
|
+
if (!currentProvider?.supportsModelDiscovery) return
|
|
359
|
+
|
|
360
|
+
const requiresCredential = currentProvider.requiresApiKey || (provider === 'ollama' && ollamaMode === 'cloud')
|
|
361
|
+
if (requiresCredential && !credentialId) return
|
|
362
|
+
|
|
363
|
+
const syncKey = `${provider}::${credentialId || ''}::${apiEndpoint?.trim() || ''}`
|
|
364
|
+
if (lastAutoSyncedModelsKeyRef.current === syncKey) return
|
|
365
|
+
lastAutoSyncedModelsKeyRef.current = syncKey
|
|
366
|
+
|
|
367
|
+
void syncLiveProviderModels(provider, credentialId, apiEndpoint, false).catch(() => {})
|
|
368
|
+
}, [apiEndpoint, credentialId, currentProvider, ollamaMode, open, openclawEnabled, provider, syncLiveProviderModels])
|
|
369
|
+
|
|
297
370
|
useEffect(() => {
|
|
298
371
|
if (open) {
|
|
299
372
|
loadSettings()
|
|
@@ -390,7 +463,7 @@ export function AgentSheet() {
|
|
|
390
463
|
setRoutingTargets([])
|
|
391
464
|
setPlatformAssignScope('self')
|
|
392
465
|
setAgentAgentIds([])
|
|
393
|
-
setTools(
|
|
466
|
+
setTools(getDefaultAgentPluginIds())
|
|
394
467
|
setSkills([])
|
|
395
468
|
setSkillIds([])
|
|
396
469
|
setMcpDisabledTools([])
|
|
@@ -580,7 +653,7 @@ export function AgentSheet() {
|
|
|
580
653
|
priority: typeof target.priority === 'number' ? target.priority : index + 1,
|
|
581
654
|
})),
|
|
582
655
|
subAgentIds: canDelegateToAgents ? subAgentIds : [],
|
|
583
|
-
tools,
|
|
656
|
+
plugins: tools,
|
|
584
657
|
skills,
|
|
585
658
|
skillIds,
|
|
586
659
|
mcpServerIds,
|
|
@@ -621,6 +694,17 @@ export function AgentSheet() {
|
|
|
621
694
|
toast.success('Agent created')
|
|
622
695
|
}
|
|
623
696
|
await loadAgents()
|
|
697
|
+
if (
|
|
698
|
+
editing
|
|
699
|
+
&& currentSessionId
|
|
700
|
+
&& currentSession?.agentId === editing.id
|
|
701
|
+
&& (
|
|
702
|
+
currentSession.shortcutForAgentId === editing.id
|
|
703
|
+
|| currentSessionId === editing.threadSessionId
|
|
704
|
+
)
|
|
705
|
+
) {
|
|
706
|
+
await refreshSession(currentSessionId)
|
|
707
|
+
}
|
|
624
708
|
setSoulInitial(soul)
|
|
625
709
|
setSoulSaveState('saved')
|
|
626
710
|
setTimeout(() => setSoulSaveState('idle'), 1500)
|
|
@@ -657,8 +741,8 @@ export function AgentSheet() {
|
|
|
657
741
|
gatewayProfileId: editing.gatewayProfileId || null,
|
|
658
742
|
routingStrategy: editing.routingStrategy || null,
|
|
659
743
|
routingTargets: editing.routingTargets || [],
|
|
660
|
-
tools: editing.tools,
|
|
661
|
-
plugins: editing.plugins,
|
|
744
|
+
tools: editing.plugins || editing.tools || [],
|
|
745
|
+
plugins: editing.plugins || editing.tools || [],
|
|
662
746
|
capabilities: editing.capabilities,
|
|
663
747
|
elevenLabsVoiceId: editing.elevenLabsVoiceId || null,
|
|
664
748
|
soul: editing.soul,
|
|
@@ -714,8 +798,19 @@ export function AgentSheet() {
|
|
|
714
798
|
})
|
|
715
799
|
if (result.deviceId) setTestDeviceId(result.deviceId)
|
|
716
800
|
if (result.ok) {
|
|
801
|
+
let syncedModels: string[] = []
|
|
802
|
+
try {
|
|
803
|
+
const synced = await syncLiveProviderModels(provider, credentialId, apiEndpoint, true)
|
|
804
|
+
syncedModels = synced?.models || []
|
|
805
|
+
} catch {
|
|
806
|
+
// Best-effort: a passing connection test should still pass if model sync fails.
|
|
807
|
+
}
|
|
717
808
|
setTestStatus('pass')
|
|
718
|
-
setTestMessage(
|
|
809
|
+
setTestMessage(
|
|
810
|
+
syncedModels.length > 0
|
|
811
|
+
? `${result.message} Synced ${syncedModels.length} live model${syncedModels.length === 1 ? '' : 's'} into the model picker.`
|
|
812
|
+
: result.message,
|
|
813
|
+
)
|
|
719
814
|
return true
|
|
720
815
|
} else {
|
|
721
816
|
setTestStatus('fail')
|
|
@@ -745,7 +840,7 @@ export function AgentSheet() {
|
|
|
745
840
|
if (!passed) return
|
|
746
841
|
if (!openclawEnabled) {
|
|
747
842
|
// Brief pause so the user can see the success state on the button
|
|
748
|
-
await
|
|
843
|
+
await sleep(1500)
|
|
749
844
|
}
|
|
750
845
|
}
|
|
751
846
|
setSaving(true)
|
|
@@ -767,8 +862,8 @@ export function AgentSheet() {
|
|
|
767
862
|
return (
|
|
768
863
|
<>
|
|
769
864
|
<BottomSheet open={open} onClose={onClose} wide>
|
|
770
|
-
<div className="mb-10 flex items-start justify-between">
|
|
771
|
-
<div>
|
|
865
|
+
<div className="mb-10 flex items-start justify-between gap-6 pr-14 sm:pr-20">
|
|
866
|
+
<div className="min-w-0">
|
|
772
867
|
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
773
868
|
<h2 className="font-display text-[28px] font-700 tracking-[-0.03em]">
|
|
774
869
|
{editing ? 'Edit Agent' : 'New Agent'}
|
|
@@ -783,7 +878,7 @@ export function AgentSheet() {
|
|
|
783
878
|
</div>
|
|
784
879
|
<p className="text-[14px] text-text-3">Define an AI agent and optional multi-agent delegation behavior</p>
|
|
785
880
|
</div>
|
|
786
|
-
<div className="flex items-center gap-3
|
|
881
|
+
<div className="mt-1.5 flex shrink-0 items-center gap-3">
|
|
787
882
|
<label className="text-[11px] font-600 text-text-3 uppercase tracking-[0.08em]">OpenClaw</label>
|
|
788
883
|
<button
|
|
789
884
|
type="button"
|
|
@@ -1627,7 +1722,7 @@ export function AgentSheet() {
|
|
|
1627
1722
|
setAddingKey(false)
|
|
1628
1723
|
setNewKeyName('')
|
|
1629
1724
|
setNewKeyValue('')
|
|
1630
|
-
} catch (err: unknown) { toast.error(`Failed to save: ${
|
|
1725
|
+
} catch (err: unknown) { toast.error(`Failed to save: ${errorMessage(err)}`) }
|
|
1631
1726
|
finally { setSavingKey(false) }
|
|
1632
1727
|
}}
|
|
1633
1728
|
className="px-4 py-1.5 rounded-[8px] bg-accent-bright text-white text-[12px] font-600 cursor-pointer border-none hover:brightness-110 transition-all disabled:opacity-40"
|
|
@@ -1877,18 +1972,24 @@ export function AgentSheet() {
|
|
|
1877
1972
|
<button
|
|
1878
1973
|
type="button"
|
|
1879
1974
|
disabled={savingKey || !newKeyValue.trim()}
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1975
|
+
onClick={async () => {
|
|
1976
|
+
setSavingKey(true)
|
|
1977
|
+
try {
|
|
1978
|
+
const cred = await api<{ id: string }>('POST', '/credentials', { provider, name: newKeyName.trim() || `${provider} key`, apiKey: newKeyValue.trim() })
|
|
1979
|
+
await loadCredentials()
|
|
1980
|
+
setCredentialId(cred.id)
|
|
1981
|
+
const synced = await syncLiveProviderModels(provider, cred.id, apiEndpoint, true).catch(() => null)
|
|
1982
|
+
setAddingKey(false)
|
|
1983
|
+
setNewKeyName('')
|
|
1984
|
+
setNewKeyValue('')
|
|
1985
|
+
if (synced?.models.length) {
|
|
1986
|
+
toast.success(`Key saved. Synced ${synced.models.length} model${synced.models.length === 1 ? '' : 's'}.`)
|
|
1987
|
+
} else {
|
|
1988
|
+
toast.success('Key saved')
|
|
1989
|
+
}
|
|
1990
|
+
} catch (err: unknown) { toast.error(`Failed to save: ${errorMessage(err)}`) }
|
|
1991
|
+
finally { setSavingKey(false) }
|
|
1992
|
+
}}
|
|
1892
1993
|
className="px-4 py-1.5 rounded-[8px] bg-accent-bright text-white text-[12px] font-600 cursor-pointer border-none hover:brightness-110 transition-all disabled:opacity-40"
|
|
1893
1994
|
style={{ fontFamily: 'inherit' }}
|
|
1894
1995
|
>
|
|
@@ -65,7 +65,7 @@ export function InspectorPanel({ agent, onEditAgent, onClearHistory, onDeleteAge
|
|
|
65
65
|
if (!visibleTabs.find((t) => t.id === inspectorTab)) {
|
|
66
66
|
setInspectorTab('overview')
|
|
67
67
|
}
|
|
68
|
-
}, [isOpenClaw])
|
|
68
|
+
}, [isOpenClaw])
|
|
69
69
|
|
|
70
70
|
// Close on Escape
|
|
71
71
|
useEffect(() => {
|
|
@@ -5,6 +5,7 @@ import type { OpenClawSkillEntry, SkillAllowlistMode } from '@/types'
|
|
|
5
5
|
import { api } from '@/lib/api-client'
|
|
6
6
|
import { SkillInstallDialog } from './skill-install-dialog'
|
|
7
7
|
import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
8
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
8
9
|
|
|
9
10
|
interface Props {
|
|
10
11
|
agentId: string
|
|
@@ -32,7 +33,7 @@ export function OpenClawSkillsPanel({ agentId, initialMode = 'all', initialAllow
|
|
|
32
33
|
const result = await api<OpenClawSkillEntry[]>('GET', `/openclaw/skills?agentId=${agentId}`)
|
|
33
34
|
setSkills(Array.isArray(result) ? result : [])
|
|
34
35
|
} catch (err: unknown) {
|
|
35
|
-
setError(
|
|
36
|
+
setError(errorMessage(err))
|
|
36
37
|
} finally {
|
|
37
38
|
setLoading(false)
|
|
38
39
|
}
|
|
@@ -12,7 +12,7 @@ export function TrashList() {
|
|
|
12
12
|
const loadAgents = useAppStore((s) => s.loadAgents)
|
|
13
13
|
const [confirmPermanent, setConfirmPermanent] = useState<Agent | null>(null)
|
|
14
14
|
|
|
15
|
-
useEffect(() => { loadTrashedAgents() }, [])
|
|
15
|
+
useEffect(() => { loadTrashedAgents() }, [])
|
|
16
16
|
|
|
17
17
|
const handleRestore = async (id: string) => {
|
|
18
18
|
await api('POST', '/agents/trash', { id })
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect } from 'react'
|
|
4
4
|
import { setStoredAccessKey } from '@/lib/api-client'
|
|
5
|
-
import { fetchWithTimeout } from '@/lib/fetch-timeout'
|
|
5
|
+
import { fetchWithTimeout, isAbortError, isTimeoutError } from '@/lib/fetch-timeout'
|
|
6
6
|
|
|
7
7
|
interface AccessKeyGateProps {
|
|
8
8
|
onAuthenticated: () => void
|
|
@@ -10,6 +10,10 @@ interface AccessKeyGateProps {
|
|
|
10
10
|
|
|
11
11
|
const AUTH_CHECK_TIMEOUT_MS = 8_000
|
|
12
12
|
|
|
13
|
+
function isExpectedAuthCheckError(err: unknown): boolean {
|
|
14
|
+
return isAbortError(err) || isTimeoutError(err)
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
14
18
|
const [key, setKey] = useState('')
|
|
15
19
|
const [error, setError] = useState('')
|
|
@@ -29,7 +33,9 @@ export function AccessKeyGate({ onAuthenticated }: AccessKeyGateProps) {
|
|
|
29
33
|
setFirstTime(true)
|
|
30
34
|
}
|
|
31
35
|
} catch (err) {
|
|
32
|
-
|
|
36
|
+
if (!isExpectedAuthCheckError(err)) {
|
|
37
|
+
console.error('Auth check failed:', err)
|
|
38
|
+
}
|
|
33
39
|
} finally {
|
|
34
40
|
if (!cancelled) setChecking(false)
|
|
35
41
|
}
|
|
@@ -5,6 +5,7 @@ import { api } from '@/lib/api-client'
|
|
|
5
5
|
import { OpenClawDeployPanel } from '@/components/openclaw/openclaw-deploy-panel'
|
|
6
6
|
import { useAppStore } from '@/stores/use-app-store'
|
|
7
7
|
import type { ProviderType, Credential, GatewayProfile } from '@/types'
|
|
8
|
+
import { dedup, errorMessage } from '@/lib/shared-utils'
|
|
8
9
|
import {
|
|
9
10
|
ONBOARDING_PATHS,
|
|
10
11
|
SETUP_PROVIDERS,
|
|
@@ -499,12 +500,12 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
499
500
|
...(current || {}),
|
|
500
501
|
...(nextDeployment || {}),
|
|
501
502
|
}))
|
|
502
|
-
setProviderTags((current) =>
|
|
503
|
+
setProviderTags((current) => dedup([
|
|
503
504
|
...current,
|
|
504
505
|
'onboarding',
|
|
505
506
|
...(nextDeployment?.useCase ? [nextDeployment.useCase] : []),
|
|
506
507
|
...(nextDeployment?.exposure ? [nextDeployment.exposure] : []),
|
|
507
|
-
]))
|
|
508
|
+
]))
|
|
508
509
|
}
|
|
509
510
|
setCheckState('idle')
|
|
510
511
|
setCheckMessage('')
|
|
@@ -544,7 +545,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
544
545
|
return !!result.ok
|
|
545
546
|
} catch (err: unknown) {
|
|
546
547
|
setCheckState('error')
|
|
547
|
-
setCheckMessage(
|
|
548
|
+
setCheckMessage(errorMessage(err))
|
|
548
549
|
setCheckErrorCode(null)
|
|
549
550
|
return false
|
|
550
551
|
}
|
|
@@ -560,7 +561,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
560
561
|
} catch (err: unknown) {
|
|
561
562
|
setDoctorState('error')
|
|
562
563
|
setDoctorReport(null)
|
|
563
|
-
setDoctorError(
|
|
564
|
+
setDoctorError(errorMessage(err))
|
|
564
565
|
}
|
|
565
566
|
}
|
|
566
567
|
|
|
@@ -619,7 +620,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
619
620
|
resetProviderForm()
|
|
620
621
|
setStep('providers')
|
|
621
622
|
} catch (err: unknown) {
|
|
622
|
-
setError(
|
|
623
|
+
setError(errorMessage(err))
|
|
623
624
|
} finally {
|
|
624
625
|
setSaving(false)
|
|
625
626
|
}
|
|
@@ -698,10 +699,10 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
698
699
|
name: configuredProvider.name,
|
|
699
700
|
endpoint: normalizedEndpoint,
|
|
700
701
|
credentialId: configuredProvider.credentialId || null,
|
|
701
|
-
tags:
|
|
702
|
+
tags: dedup([
|
|
702
703
|
'onboarding',
|
|
703
704
|
...(configuredProvider.tags || []),
|
|
704
|
-
])
|
|
705
|
+
]),
|
|
705
706
|
notes: configuredProvider.notes || `Created during setup for ${configuredProvider.name}.`,
|
|
706
707
|
deployment: configuredProvider.deployment || null,
|
|
707
708
|
status: configuredProvider.deployment?.lastVerifiedOk ? 'healthy' : 'pending',
|
|
@@ -765,7 +766,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
765
766
|
setCreatedAgents(created)
|
|
766
767
|
setStep('done')
|
|
767
768
|
} catch (err: unknown) {
|
|
768
|
-
setError(
|
|
769
|
+
setError(errorMessage(err))
|
|
769
770
|
} finally {
|
|
770
771
|
setSaving(false)
|
|
771
772
|
}
|
|
@@ -779,7 +780,7 @@ export function SetupWizard({ onComplete }: SetupWizardProps) {
|
|
|
779
780
|
setCreatedAgents([])
|
|
780
781
|
setStep('done')
|
|
781
782
|
} catch (err: unknown) {
|
|
782
|
-
setError(
|
|
783
|
+
setError(errorMessage(err))
|
|
783
784
|
} finally {
|
|
784
785
|
setSaving(false)
|
|
785
786
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState } from 'react'
|
|
3
|
+
import { useId, useState } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
5
|
import { AgentAvatar } from '@/components/agents/agent-avatar'
|
|
6
6
|
import { api } from '@/lib/api-client'
|
|
@@ -9,7 +9,8 @@ export function UserPicker() {
|
|
|
9
9
|
const setUser = useAppStore((s) => s.setUser)
|
|
10
10
|
const loadSettings = useAppStore((s) => s.loadSettings)
|
|
11
11
|
const [name, setName] = useState('')
|
|
12
|
-
const
|
|
12
|
+
const defaultAvatarSeed = useId().replace(/:/g, '')
|
|
13
|
+
const [avatarSeed, setAvatarSeed] = useState(defaultAvatarSeed)
|
|
13
14
|
|
|
14
15
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
15
16
|
e.preventDefault()
|