@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
package/src/app/page.tsx
CHANGED
|
@@ -1,342 +1,32 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState, useCallback } from 'react'
|
|
4
|
-
import { useAppStore } from '@/stores/use-app-store'
|
|
5
4
|
import { initAudioContext } from '@/lib/tts'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
5
|
+
import { clearStoredAccessKey } from '@/lib/api-client'
|
|
6
|
+
import { safeStorageRemove, safeStorageSet } from '@/lib/safe-storage'
|
|
7
|
+
import { disconnectWs } from '@/lib/ws-client'
|
|
8
|
+
import { useViewRouter } from '@/hooks/use-view-router'
|
|
9
|
+
import { useAppBootstrap } from '@/hooks/use-app-bootstrap'
|
|
10
|
+
|
|
12
11
|
import { AccessKeyGate } from '@/components/auth/access-key-gate'
|
|
13
12
|
import { UserPicker } from '@/components/auth/user-picker'
|
|
14
13
|
import { SetupWizard } from '@/components/auth/setup-wizard'
|
|
15
14
|
import { AppLayout } from '@/components/layout/app-layout'
|
|
16
|
-
import {
|
|
17
|
-
import type { Agent } from '@/types'
|
|
18
|
-
|
|
19
|
-
const AUTH_CHECK_TIMEOUT_MS = isDevelopmentLikeRuntime() ? 20_000 : 8_000
|
|
20
|
-
const POST_AUTH_BOOTSTRAP_TIMEOUT_MS = isDevelopmentLikeRuntime() ? 20_000 : 8_000
|
|
21
|
-
|
|
22
|
-
function FullScreenLoader(props: {
|
|
23
|
-
stage?: string | null
|
|
24
|
-
stalled?: boolean
|
|
25
|
-
onReload?: () => void
|
|
26
|
-
onReset?: () => void
|
|
27
|
-
}) {
|
|
28
|
-
return (
|
|
29
|
-
<div className="h-full flex flex-col items-center justify-center bg-bg overflow-hidden select-none">
|
|
30
|
-
{/* Animated orbital ring */}
|
|
31
|
-
<div className="relative w-[120px] h-[120px] mb-8">
|
|
32
|
-
{/* Outer glow pulse */}
|
|
33
|
-
<div
|
|
34
|
-
className="absolute inset-[-20px] rounded-full"
|
|
35
|
-
style={{
|
|
36
|
-
background: 'radial-gradient(circle, rgba(99,102,241,0.08) 0%, transparent 70%)',
|
|
37
|
-
animation: 'sc-glow 2.5s ease-in-out infinite',
|
|
38
|
-
}}
|
|
39
|
-
/>
|
|
40
|
-
|
|
41
|
-
{/* Orbital ring */}
|
|
42
|
-
<div
|
|
43
|
-
className="absolute inset-0 rounded-full border border-white/[0.06]"
|
|
44
|
-
style={{ animation: 'sc-ring 3s linear infinite' }}
|
|
45
|
-
/>
|
|
46
|
-
|
|
47
|
-
{/* Orbiting dots */}
|
|
48
|
-
{[0, 1, 2, 3, 4, 5].map((i) => (
|
|
49
|
-
<div
|
|
50
|
-
key={i}
|
|
51
|
-
className="absolute inset-0"
|
|
52
|
-
style={{
|
|
53
|
-
animation: `sc-orbit 2.4s cubic-bezier(0.4, 0, 0.2, 1) infinite`,
|
|
54
|
-
animationDelay: `${i * -0.4}s`,
|
|
55
|
-
}}
|
|
56
|
-
>
|
|
57
|
-
<div
|
|
58
|
-
className="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full"
|
|
59
|
-
style={{
|
|
60
|
-
width: i === 0 ? 8 : 6,
|
|
61
|
-
height: i === 0 ? 8 : 6,
|
|
62
|
-
background: i === 0 ? '#818CF8' : `rgba(129, 140, 248, ${0.7 - i * 0.1})`,
|
|
63
|
-
boxShadow: i === 0 ? '0 0 12px rgba(99,102,241,0.5)' : 'none',
|
|
64
|
-
}}
|
|
65
|
-
/>
|
|
66
|
-
</div>
|
|
67
|
-
))}
|
|
68
|
-
|
|
69
|
-
{/* Center logo mark */}
|
|
70
|
-
<div className="absolute inset-0 flex items-center justify-center">
|
|
71
|
-
<div
|
|
72
|
-
className="relative"
|
|
73
|
-
style={{ animation: 'sc-breathe 2.5s ease-in-out infinite' }}
|
|
74
|
-
>
|
|
75
|
-
<svg width="36" height="36" viewBox="0 0 36 36" fill="none">
|
|
76
|
-
{/* Hexagonal claw mark */}
|
|
77
|
-
<path
|
|
78
|
-
d="M18 4L30 11V25L18 32L6 25V11L18 4Z"
|
|
79
|
-
stroke="rgba(129, 140, 248, 0.3)"
|
|
80
|
-
strokeWidth="1"
|
|
81
|
-
fill="none"
|
|
82
|
-
/>
|
|
83
|
-
<path
|
|
84
|
-
d="M18 9L25 13V23L18 27L11 23V13L18 9Z"
|
|
85
|
-
stroke="rgba(129, 140, 248, 0.5)"
|
|
86
|
-
strokeWidth="1.5"
|
|
87
|
-
fill="rgba(99, 102, 241, 0.06)"
|
|
88
|
-
/>
|
|
89
|
-
{/* Claw lines */}
|
|
90
|
-
<path d="M14 15L18 20L22 15" stroke="#818CF8" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
|
91
|
-
<path d="M12 13L18 20L24 13" stroke="rgba(129, 140, 248, 0.3)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
92
|
-
</svg>
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
{/* Brand text */}
|
|
98
|
-
<div
|
|
99
|
-
className="text-[15px] font-display font-700 tracking-[0.15em] uppercase"
|
|
100
|
-
style={{
|
|
101
|
-
background: 'linear-gradient(135deg, rgba(255,255,255,0.6), rgba(129, 140, 248, 0.8))',
|
|
102
|
-
WebkitBackgroundClip: 'text',
|
|
103
|
-
WebkitTextFillColor: 'transparent',
|
|
104
|
-
animation: 'sc-text-fade 2s ease-in-out infinite alternate, fade-up 0.6s var(--ease-spring) 0.2s both',
|
|
105
|
-
}}
|
|
106
|
-
>
|
|
107
|
-
SwarmClaw
|
|
108
|
-
</div>
|
|
109
|
-
|
|
110
|
-
{/* Loading bar */}
|
|
111
|
-
<div className="mt-4 w-[100px] h-[2px] rounded-full bg-white/[0.06] overflow-hidden" style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.3s both' }}>
|
|
112
|
-
<div
|
|
113
|
-
className="h-full rounded-full bg-accent-bright/60"
|
|
114
|
-
style={{ animation: 'sc-progress 1.5s ease-in-out infinite' }}
|
|
115
|
-
/>
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
{props.stage ? (
|
|
119
|
-
<p
|
|
120
|
-
className="mt-4 text-[12px] text-text-3"
|
|
121
|
-
style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.4s both' }}
|
|
122
|
-
>
|
|
123
|
-
{props.stage}
|
|
124
|
-
</p>
|
|
125
|
-
) : null}
|
|
126
|
-
|
|
127
|
-
{props.stalled ? (
|
|
128
|
-
<div
|
|
129
|
-
className="mt-6 max-w-[360px] px-4 text-center"
|
|
130
|
-
style={{ animation: 'fade-up 0.6s var(--ease-spring) 0.5s both' }}
|
|
131
|
-
>
|
|
132
|
-
<p className="text-[12px] text-text-2">
|
|
133
|
-
Startup is taking longer than expected. This usually means the browser kept stale local state while the dev server restarted.
|
|
134
|
-
</p>
|
|
135
|
-
<div className="mt-4 flex items-center justify-center gap-3">
|
|
136
|
-
<button
|
|
137
|
-
type="button"
|
|
138
|
-
onClick={props.onReload}
|
|
139
|
-
className="px-4 py-2 rounded-[12px] border border-white/[0.08] bg-surface text-[12px] text-text-2 transition-colors hover:bg-surface-2"
|
|
140
|
-
>
|
|
141
|
-
Reload
|
|
142
|
-
</button>
|
|
143
|
-
<button
|
|
144
|
-
type="button"
|
|
145
|
-
onClick={props.onReset}
|
|
146
|
-
className="px-4 py-2 rounded-[12px] border border-white/[0.08] bg-transparent text-[12px] text-text-3 transition-colors hover:bg-white/[0.04]"
|
|
147
|
-
>
|
|
148
|
-
Reset Local Session
|
|
149
|
-
</button>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
) : null}
|
|
153
|
-
|
|
154
|
-
{/* Loading animation keyframes */}
|
|
155
|
-
<style>{`
|
|
156
|
-
@keyframes sc-orbit {
|
|
157
|
-
from { transform: rotate(0deg); }
|
|
158
|
-
to { transform: rotate(360deg); }
|
|
159
|
-
}
|
|
160
|
-
@keyframes sc-ring {
|
|
161
|
-
from { transform: rotate(0deg) scale(1); }
|
|
162
|
-
50% { transform: rotate(180deg) scale(1.02); }
|
|
163
|
-
to { transform: rotate(360deg) scale(1); }
|
|
164
|
-
}
|
|
165
|
-
@keyframes sc-breathe {
|
|
166
|
-
0%, 100% { transform: scale(1); opacity: 0.9; }
|
|
167
|
-
50% { transform: scale(1.06); opacity: 1; }
|
|
168
|
-
}
|
|
169
|
-
@keyframes sc-glow {
|
|
170
|
-
0%, 100% { opacity: 0.5; transform: scale(0.9); }
|
|
171
|
-
50% { opacity: 1; transform: scale(1.1); }
|
|
172
|
-
}
|
|
173
|
-
@keyframes sc-text-fade {
|
|
174
|
-
0% { opacity: 0.6; }
|
|
175
|
-
100% { opacity: 1; }
|
|
176
|
-
}
|
|
177
|
-
@keyframes sc-progress {
|
|
178
|
-
0% { width: 0; margin-left: 0; }
|
|
179
|
-
50% { width: 70%; margin-left: 15%; }
|
|
180
|
-
100% { width: 0; margin-left: 100%; }
|
|
181
|
-
}
|
|
182
|
-
`}</style>
|
|
183
|
-
</div>
|
|
184
|
-
)
|
|
185
|
-
}
|
|
15
|
+
import { FullScreenLoader } from '@/components/ui/full-screen-loader'
|
|
186
16
|
|
|
187
17
|
export default function Home() {
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
18
|
+
const {
|
|
19
|
+
hydrated,
|
|
20
|
+
authChecked,
|
|
21
|
+
authenticated,
|
|
22
|
+
setAuthenticated,
|
|
23
|
+
currentUser,
|
|
24
|
+
setupDone,
|
|
25
|
+
setSetupDone,
|
|
26
|
+
agentReady
|
|
27
|
+
} = useAppBootstrap()
|
|
195
28
|
|
|
196
|
-
const [authChecked, setAuthChecked] = useState(false)
|
|
197
|
-
const [authenticated, setAuthenticated] = useState(false)
|
|
198
29
|
const [bootTimedOut, setBootTimedOut] = useState(false)
|
|
199
|
-
const [setupDone, setSetupDone] = useState<boolean | null>(() => {
|
|
200
|
-
if (safeStorageGet('sc_setup_done') === '1') return true
|
|
201
|
-
return null
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
const checkAuth = useCallback(async () => {
|
|
205
|
-
try {
|
|
206
|
-
const res = await fetchWithTimeout('/api/auth', {}, AUTH_CHECK_TIMEOUT_MS)
|
|
207
|
-
const data = await res.json().catch(() => ({}))
|
|
208
|
-
if (data?.authenticated === true) {
|
|
209
|
-
setAuthenticated(true)
|
|
210
|
-
setAuthChecked(true)
|
|
211
|
-
return
|
|
212
|
-
}
|
|
213
|
-
} catch {
|
|
214
|
-
// Fall back to stored-key bootstrap below if the auth probe is unavailable.
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const key = getStoredAccessKey()
|
|
218
|
-
if (!key) {
|
|
219
|
-
setAuthenticated(false)
|
|
220
|
-
setAuthChecked(true)
|
|
221
|
-
return
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
try {
|
|
225
|
-
const res = await fetchWithTimeout('/api/auth', {
|
|
226
|
-
method: 'POST',
|
|
227
|
-
headers: { 'Content-Type': 'application/json' },
|
|
228
|
-
body: JSON.stringify({ key }),
|
|
229
|
-
}, AUTH_CHECK_TIMEOUT_MS)
|
|
230
|
-
if (res.ok) {
|
|
231
|
-
setAuthenticated(true)
|
|
232
|
-
} else {
|
|
233
|
-
clearStoredAccessKey()
|
|
234
|
-
setAuthenticated(false)
|
|
235
|
-
}
|
|
236
|
-
} catch {
|
|
237
|
-
clearStoredAccessKey()
|
|
238
|
-
setAuthenticated(false)
|
|
239
|
-
} finally {
|
|
240
|
-
setAuthChecked(true)
|
|
241
|
-
}
|
|
242
|
-
}, [])
|
|
243
|
-
|
|
244
|
-
// After auth, try to restore username from server settings
|
|
245
|
-
const syncUserFromServer = useCallback(async () => {
|
|
246
|
-
if (currentUser) return // already have a name locally
|
|
247
|
-
try {
|
|
248
|
-
const settings = await api<{ userName?: string }>('GET', '/settings', undefined, {
|
|
249
|
-
timeoutMs: POST_AUTH_BOOTSTRAP_TIMEOUT_MS,
|
|
250
|
-
retries: 0,
|
|
251
|
-
})
|
|
252
|
-
if (settings.userName) {
|
|
253
|
-
setUser(settings.userName)
|
|
254
|
-
}
|
|
255
|
-
} catch { /* ignore */ }
|
|
256
|
-
}, [currentUser, setUser])
|
|
257
|
-
|
|
258
|
-
useEffect(() => {
|
|
259
|
-
hydrate()
|
|
260
|
-
}, [])
|
|
261
|
-
|
|
262
|
-
useEffect(() => {
|
|
263
|
-
if (hydrated) checkAuth()
|
|
264
|
-
}, [hydrated, checkAuth])
|
|
265
|
-
|
|
266
|
-
useEffect(() => {
|
|
267
|
-
if (!authenticated) return
|
|
268
|
-
connectWs()
|
|
269
|
-
syncUserFromServer()
|
|
270
|
-
loadNetworkInfo()
|
|
271
|
-
loadSettings()
|
|
272
|
-
loadSessions()
|
|
273
|
-
return () => { disconnectWs() }
|
|
274
|
-
}, [authenticated])
|
|
275
|
-
|
|
276
|
-
useWs('sessions', loadSessions, 15000)
|
|
277
|
-
|
|
278
|
-
// Auto-select agent's thread on load — resolves a persisted agentId into a session,
|
|
279
|
-
// or falls back to defaultAgentId from settings, then first agent.
|
|
280
|
-
const [agentReady, setAgentReady] = useState(false)
|
|
281
|
-
useEffect(() => {
|
|
282
|
-
if (!authenticated || !currentUser) return
|
|
283
|
-
let cancelled = false
|
|
284
|
-
;(async () => {
|
|
285
|
-
try {
|
|
286
|
-
const agents = await api<Record<string, Agent>>('GET', '/agents', undefined, {
|
|
287
|
-
timeoutMs: POST_AUTH_BOOTSTRAP_TIMEOUT_MS,
|
|
288
|
-
retries: 0,
|
|
289
|
-
})
|
|
290
|
-
if (cancelled) return
|
|
291
|
-
useAppStore.setState({ agents })
|
|
292
|
-
|
|
293
|
-
const { currentAgentId, appSettings } = useAppStore.getState()
|
|
294
|
-
// Priority: persisted agent > settings default > first agent
|
|
295
|
-
const targetId = (currentAgentId && agents[currentAgentId])
|
|
296
|
-
? currentAgentId
|
|
297
|
-
: (appSettings.defaultAgentId && agents[appSettings.defaultAgentId])
|
|
298
|
-
? appSettings.defaultAgentId
|
|
299
|
-
: Object.values(agents)[0]?.id || null
|
|
300
|
-
|
|
301
|
-
if (targetId) {
|
|
302
|
-
await useAppStore.getState().setCurrentAgent(targetId)
|
|
303
|
-
}
|
|
304
|
-
} catch { /* ignore */ }
|
|
305
|
-
if (!cancelled) setAgentReady(true)
|
|
306
|
-
})()
|
|
307
|
-
return () => { cancelled = true }
|
|
308
|
-
}, [authenticated, currentUser])
|
|
309
|
-
|
|
310
|
-
// Check if first-run setup is needed
|
|
311
|
-
useEffect(() => {
|
|
312
|
-
if (!authenticated || !currentUser) return
|
|
313
|
-
let cancelled = false
|
|
314
|
-
;(async () => {
|
|
315
|
-
try {
|
|
316
|
-
const [settingsResult, credsResult] = await Promise.allSettled([
|
|
317
|
-
api<{ setupCompleted?: boolean }>('GET', '/settings', undefined, {
|
|
318
|
-
timeoutMs: POST_AUTH_BOOTSTRAP_TIMEOUT_MS,
|
|
319
|
-
retries: 0,
|
|
320
|
-
}),
|
|
321
|
-
api<Record<string, unknown>>('GET', '/credentials', undefined, {
|
|
322
|
-
timeoutMs: POST_AUTH_BOOTSTRAP_TIMEOUT_MS,
|
|
323
|
-
retries: 0,
|
|
324
|
-
}),
|
|
325
|
-
])
|
|
326
|
-
if (cancelled) return
|
|
327
|
-
const settings = settingsResult.status === 'fulfilled' ? settingsResult.value : {}
|
|
328
|
-
const creds = credsResult.status === 'fulfilled' ? credsResult.value : {}
|
|
329
|
-
const bothFailed = settingsResult.status === 'rejected' && credsResult.status === 'rejected'
|
|
330
|
-
const hasCreds = Object.keys(creds).length > 0
|
|
331
|
-
const done = bothFailed ? true : settings.setupCompleted === true || hasCreds
|
|
332
|
-
if (done) safeStorageSet('sc_setup_done', '1')
|
|
333
|
-
setSetupDone(done)
|
|
334
|
-
} catch {
|
|
335
|
-
if (!cancelled) setSetupDone(true) // on error, skip wizard
|
|
336
|
-
}
|
|
337
|
-
})()
|
|
338
|
-
return () => { cancelled = true }
|
|
339
|
-
}, [authenticated, currentUser])
|
|
340
30
|
|
|
341
31
|
useEffect(() => {
|
|
342
32
|
const handler = () => {
|
|
@@ -351,11 +41,12 @@ export default function Home() {
|
|
|
351
41
|
const handler = () => {
|
|
352
42
|
disconnectWs()
|
|
353
43
|
setAuthenticated(false)
|
|
354
|
-
|
|
44
|
+
// Note: we can't fully control authChecked here unless we pass a setter from useAppBootstrap,
|
|
45
|
+
// but usually auth dropping just sets authenticated to false.
|
|
355
46
|
}
|
|
356
47
|
window.addEventListener('sc_auth_required', handler)
|
|
357
48
|
return () => window.removeEventListener('sc_auth_required', handler)
|
|
358
|
-
}, [])
|
|
49
|
+
}, [setAuthenticated])
|
|
359
50
|
|
|
360
51
|
useViewRouter()
|
|
361
52
|
|
|
@@ -371,8 +62,9 @@ export default function Home() {
|
|
|
371
62
|
|
|
372
63
|
useEffect(() => {
|
|
373
64
|
if (!bootStage) {
|
|
374
|
-
|
|
375
|
-
|
|
65
|
+
// Defer resetting to avoid synchronous state update in effect
|
|
66
|
+
const t = window.setTimeout(() => setBootTimedOut(false), 0)
|
|
67
|
+
return () => window.clearTimeout(t)
|
|
376
68
|
}
|
|
377
69
|
const timer = window.setTimeout(() => setBootTimedOut(true), 15_000)
|
|
378
70
|
return () => window.clearTimeout(timer)
|
package/src/cli/index.js
CHANGED
|
@@ -160,6 +160,25 @@ const COMMAND_GROUPS = [
|
|
|
160
160
|
cmd('pick', 'POST', '/dirs/pick', 'Open native picker (mode=file|folder)', { expectsJsonBody: true }),
|
|
161
161
|
],
|
|
162
162
|
},
|
|
163
|
+
{
|
|
164
|
+
name: 'perf',
|
|
165
|
+
description: 'Inspect or control runtime perf tracing',
|
|
166
|
+
commands: [
|
|
167
|
+
cmd('status', 'GET', '/perf', 'Get current perf tracing status and recent entries'),
|
|
168
|
+
cmd('enable', 'POST', '/perf', 'Enable perf tracing and clear existing entries', {
|
|
169
|
+
expectsJsonBody: true,
|
|
170
|
+
defaultBody: { action: 'enable' },
|
|
171
|
+
}),
|
|
172
|
+
cmd('disable', 'POST', '/perf', 'Disable perf tracing', {
|
|
173
|
+
expectsJsonBody: true,
|
|
174
|
+
defaultBody: { action: 'disable' },
|
|
175
|
+
}),
|
|
176
|
+
cmd('clear', 'POST', '/perf', 'Clear recent perf entries', {
|
|
177
|
+
expectsJsonBody: true,
|
|
178
|
+
defaultBody: { action: 'clear' },
|
|
179
|
+
}),
|
|
180
|
+
],
|
|
181
|
+
},
|
|
163
182
|
{
|
|
164
183
|
name: 'documents',
|
|
165
184
|
description: 'Manage documents',
|
package/src/cli/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Command } from 'commander'
|
|
|
4
4
|
import { pathToFileURL } from 'node:url'
|
|
5
5
|
import fs from 'node:fs'
|
|
6
6
|
import path from 'node:path'
|
|
7
|
+
import { errorMessage, sleep } from '../lib/shared-utils.ts'
|
|
7
8
|
import {
|
|
8
9
|
SETUP_PROVIDERS,
|
|
9
10
|
DEFAULT_AGENTS,
|
|
@@ -74,7 +75,7 @@ function parseMetadata(raw: string | undefined): Record<string, string> | undefi
|
|
|
74
75
|
try {
|
|
75
76
|
parsed = JSON.parse(raw)
|
|
76
77
|
} catch (err) {
|
|
77
|
-
throw new Error(`Invalid --metadata JSON: ${
|
|
78
|
+
throw new Error(`Invalid --metadata JSON: ${errorMessage(err)}`)
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
@@ -93,7 +94,7 @@ function parseJsonValue(raw: string | undefined, label: string): unknown {
|
|
|
93
94
|
try {
|
|
94
95
|
return JSON.parse(raw)
|
|
95
96
|
} catch (err) {
|
|
96
|
-
throw new Error(`Invalid ${label} JSON: ${
|
|
97
|
+
throw new Error(`Invalid ${label} JSON: ${errorMessage(err)}`)
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
|
|
@@ -171,7 +172,7 @@ async function apiRequestWithAccessKey<T = unknown>(
|
|
|
171
172
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
172
173
|
})
|
|
173
174
|
} catch (err) {
|
|
174
|
-
const msg =
|
|
175
|
+
const msg = errorMessage(err)
|
|
175
176
|
throw new Error(`Failed to reach ${ctx.baseUrl}. Is SwarmClaw running? (${msg})`)
|
|
176
177
|
}
|
|
177
178
|
|
|
@@ -269,7 +270,7 @@ async function runWithHandler(command: Command, task: (ctx: CliContext) => Promi
|
|
|
269
270
|
const result = await task(ctx)
|
|
270
271
|
printResult(result, ctx.rawOutput)
|
|
271
272
|
} catch (err) {
|
|
272
|
-
const msg =
|
|
273
|
+
const msg = errorMessage(err)
|
|
273
274
|
console.error(msg)
|
|
274
275
|
process.exitCode = 1
|
|
275
276
|
}
|
|
@@ -413,7 +414,7 @@ async function runInteractiveSetup(ctx: CliContext): Promise<unknown> {
|
|
|
413
414
|
console.log(` FAILED: ${check?.message || 'Unknown error'}`)
|
|
414
415
|
}
|
|
415
416
|
} catch (err: unknown) {
|
|
416
|
-
console.log(` FAILED: ${
|
|
417
|
+
console.log(` FAILED: ${errorMessage(err)}`)
|
|
417
418
|
}
|
|
418
419
|
}
|
|
419
420
|
|
|
@@ -666,6 +667,36 @@ export function buildProgram(): Command {
|
|
|
666
667
|
await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'GET', `/runs/${encodeURIComponent(id)}`))
|
|
667
668
|
})
|
|
668
669
|
|
|
670
|
+
const perf = program.command('perf').description('Inspect or control runtime perf tracing')
|
|
671
|
+
|
|
672
|
+
perf
|
|
673
|
+
.command('status')
|
|
674
|
+
.description('Get current perf tracing status and recent entries')
|
|
675
|
+
.action(async function () {
|
|
676
|
+
await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'GET', '/perf'))
|
|
677
|
+
})
|
|
678
|
+
|
|
679
|
+
perf
|
|
680
|
+
.command('enable')
|
|
681
|
+
.description('Enable perf tracing and clear existing entries')
|
|
682
|
+
.action(async function () {
|
|
683
|
+
await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'POST', '/perf', { action: 'enable' }))
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
perf
|
|
687
|
+
.command('disable')
|
|
688
|
+
.description('Disable perf tracing')
|
|
689
|
+
.action(async function () {
|
|
690
|
+
await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'POST', '/perf', { action: 'disable' }))
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
perf
|
|
694
|
+
.command('clear')
|
|
695
|
+
.description('Clear recent perf entries')
|
|
696
|
+
.action(async function () {
|
|
697
|
+
await runWithHandler(this as Command, (ctx) => apiRequest(ctx, 'POST', '/perf', { action: 'clear' }))
|
|
698
|
+
})
|
|
699
|
+
|
|
669
700
|
const chats = program.command('chats').description('Manage chats')
|
|
670
701
|
program.command('sessions').description('Manage chats (alias)').action(() => chats.help())
|
|
671
702
|
|
|
@@ -1381,12 +1412,12 @@ export async function runCli(argv: string[] = process.argv.slice(2)): Promise<nu
|
|
|
1381
1412
|
|
|
1382
1413
|
// Wait briefly for the hint if the command succeeded
|
|
1383
1414
|
if (hintPromise && code === 0) {
|
|
1384
|
-
await Promise.race([hintPromise,
|
|
1415
|
+
await Promise.race([hintPromise, sleep(2000)])
|
|
1385
1416
|
}
|
|
1386
1417
|
|
|
1387
1418
|
return code
|
|
1388
1419
|
} catch (err) {
|
|
1389
|
-
const msg =
|
|
1420
|
+
const msg = errorMessage(err)
|
|
1390
1421
|
console.error(msg)
|
|
1391
1422
|
return 1
|
|
1392
1423
|
}
|
package/src/cli/spec.js
CHANGED
|
@@ -103,6 +103,15 @@ const COMMAND_GROUPS = {
|
|
|
103
103
|
pick: { description: 'Open native picker (body: {"mode":"file|folder"})', method: 'POST', path: '/dirs/pick' },
|
|
104
104
|
},
|
|
105
105
|
},
|
|
106
|
+
perf: {
|
|
107
|
+
description: 'Inspect or control runtime perf tracing',
|
|
108
|
+
commands: {
|
|
109
|
+
status: { description: 'Get current perf tracing status and recent entries', method: 'GET', path: '/perf' },
|
|
110
|
+
enable: { description: 'Enable perf tracing and clear existing entries', method: 'POST', path: '/perf', staticBody: { action: 'enable' } },
|
|
111
|
+
disable: { description: 'Disable perf tracing', method: 'POST', path: '/perf', staticBody: { action: 'disable' } },
|
|
112
|
+
clear: { description: 'Clear recent perf entries', method: 'POST', path: '/perf', staticBody: { action: 'clear' } },
|
|
113
|
+
},
|
|
114
|
+
},
|
|
106
115
|
documents: {
|
|
107
116
|
description: 'File uploads/downloads and TTS audio',
|
|
108
117
|
commands: {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react'
|
|
4
4
|
import { useAppStore } from '@/stores/use-app-store'
|
|
5
|
+
import { useNow } from '@/hooks/use-now'
|
|
5
6
|
import { useWs } from '@/hooks/use-ws'
|
|
6
7
|
import type { ActivityEntry } from '@/types'
|
|
7
8
|
|
|
@@ -22,8 +23,9 @@ const ACTION_COLORS: Record<string, string> = {
|
|
|
22
23
|
rejected: 'bg-red-500/15 text-red-400',
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
function timeAgo(ts: number) {
|
|
26
|
-
|
|
26
|
+
function timeAgo(ts: number, now: number | null) {
|
|
27
|
+
if (!now) return 'recently'
|
|
28
|
+
const diff = now - ts
|
|
27
29
|
if (diff < 60_000) return 'just now'
|
|
28
30
|
if (diff < 3600_000) return `${Math.floor(diff / 60_000)}m ago`
|
|
29
31
|
if (diff < 86400_000) return `${Math.floor(diff / 3600_000)}h ago`
|
|
@@ -33,11 +35,12 @@ function timeAgo(ts: number) {
|
|
|
33
35
|
const ENTITY_TYPES = ['', 'agent', 'task', 'connector', 'session', 'webhook', 'schedule'] as const
|
|
34
36
|
|
|
35
37
|
export function ActivityFeed() {
|
|
38
|
+
const now = useNow()
|
|
36
39
|
const entries = useAppStore((s) => s.activityEntries)
|
|
37
40
|
const loadActivity = useAppStore((s) => s.loadActivity)
|
|
38
41
|
const [filterType, setFilterType] = useState('')
|
|
39
42
|
|
|
40
|
-
useEffect(() => { loadActivity({ entityType: filterType || undefined, limit: 100 }) }, [filterType])
|
|
43
|
+
useEffect(() => { loadActivity({ entityType: filterType || undefined, limit: 100 }) }, [filterType, loadActivity])
|
|
41
44
|
useWs('activity', () => loadActivity({ entityType: filterType || undefined, limit: 100 }), 10_000)
|
|
42
45
|
|
|
43
46
|
return (
|
|
@@ -87,7 +90,7 @@ export function ActivityFeed() {
|
|
|
87
90
|
</div>
|
|
88
91
|
<p className="text-[13px] text-text-2 leading-[1.4] truncate">{entry.summary}</p>
|
|
89
92
|
</div>
|
|
90
|
-
<span className="text-[11px] text-text-3/50 shrink-0 pt-1">{timeAgo(entry.timestamp)}</span>
|
|
93
|
+
<span className="text-[11px] text-text-3/50 shrink-0 pt-1">{timeAgo(entry.timestamp, now)}</span>
|
|
91
94
|
</div>
|
|
92
95
|
))}
|
|
93
96
|
</div>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState } from 'react'
|
|
3
|
+
import { useEffect, useRef, useState } from 'react'
|
|
4
4
|
import type { Agent } from '@/types'
|
|
5
5
|
import { useAppStore } from '@/stores/use-app-store'
|
|
6
6
|
import { useChatStore } from '@/stores/use-chat-store'
|
|
7
7
|
import { useWs } from '@/hooks/use-ws'
|
|
8
|
+
import { useMountedRef } from '@/hooks/use-mounted-ref'
|
|
8
9
|
import { api } from '@/lib/api-client'
|
|
9
10
|
import { deleteAgent } from '@/lib/agents'
|
|
10
11
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'
|
|
@@ -19,6 +20,7 @@ import { ConfirmDialog } from '@/components/shared/confirm-dialog'
|
|
|
19
20
|
import { useApprovalStore } from '@/stores/use-approval-store'
|
|
20
21
|
import { AgentAvatar } from './agent-avatar'
|
|
21
22
|
import { toast } from 'sonner'
|
|
23
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
22
24
|
|
|
23
25
|
interface Props {
|
|
24
26
|
agent: Agent
|
|
@@ -30,6 +32,7 @@ interface Props {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, onSetDefault }: Props) {
|
|
35
|
+
const mountedRef = useMountedRef()
|
|
33
36
|
const setEditingAgentId = useAppStore((s) => s.setEditingAgentId)
|
|
34
37
|
const setAgentSheetOpen = useAppStore((s) => s.setAgentSheetOpen)
|
|
35
38
|
const loadSessions = useAppStore((s) => s.loadSessions)
|
|
@@ -46,6 +49,7 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
46
49
|
const approvals = useApprovalStore((s) => s.approvals)
|
|
47
50
|
const pendingApprovalCount = Object.values(approvals).filter((a) => a.agentId === agent.id).length
|
|
48
51
|
const [heartbeatPulse, setHeartbeatPulse] = useState(false)
|
|
52
|
+
const heartbeatTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
49
53
|
const monthlyBudget = typeof agent.monthlyBudget === 'number' && agent.monthlyBudget > 0
|
|
50
54
|
? agent.monthlyBudget
|
|
51
55
|
: null
|
|
@@ -65,10 +69,25 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
65
69
|
const canDelegateToAgents = agent.platformAssignScope === 'all'
|
|
66
70
|
const agentDisabled = agent.disabled === true
|
|
67
71
|
useWs(`heartbeat:agent:${agent.id}`, () => {
|
|
72
|
+
if (heartbeatTimerRef.current) {
|
|
73
|
+
clearTimeout(heartbeatTimerRef.current)
|
|
74
|
+
}
|
|
68
75
|
setHeartbeatPulse(true)
|
|
69
|
-
setTimeout(() =>
|
|
76
|
+
heartbeatTimerRef.current = setTimeout(() => {
|
|
77
|
+
if (mountedRef.current) {
|
|
78
|
+
setHeartbeatPulse(false)
|
|
79
|
+
}
|
|
80
|
+
}, 1500)
|
|
70
81
|
})
|
|
71
82
|
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
return () => {
|
|
85
|
+
if (heartbeatTimerRef.current) {
|
|
86
|
+
clearTimeout(heartbeatTimerRef.current)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}, [])
|
|
90
|
+
|
|
72
91
|
const handleClick = () => {
|
|
73
92
|
setEditingAgentId(agent.id)
|
|
74
93
|
setAgentSheetOpen(true)
|
|
@@ -89,6 +108,7 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
89
108
|
const session = await api<{ id: string }>('POST', `/agents/${agent.id}/thread`, { user: 'default' })
|
|
90
109
|
if (!session?.id) throw new Error('Agent thread not available')
|
|
91
110
|
await loadSessions()
|
|
111
|
+
if (!mountedRef.current) return
|
|
92
112
|
setMessages([])
|
|
93
113
|
setCurrentSession(session.id)
|
|
94
114
|
setActiveView('agents')
|
|
@@ -96,7 +116,9 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
96
116
|
} catch (err) {
|
|
97
117
|
console.error('Agent task run failed:', err)
|
|
98
118
|
}
|
|
99
|
-
|
|
119
|
+
if (mountedRef.current) {
|
|
120
|
+
setRunning(false)
|
|
121
|
+
}
|
|
100
122
|
}
|
|
101
123
|
|
|
102
124
|
const [cloning, setCloning] = useState(false)
|
|
@@ -107,9 +129,11 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
107
129
|
await loadAgents()
|
|
108
130
|
toast.success('Agent duplicated')
|
|
109
131
|
} catch (err: unknown) {
|
|
110
|
-
toast.error(
|
|
132
|
+
toast.error(errorMessage(err))
|
|
111
133
|
} finally {
|
|
112
|
-
|
|
134
|
+
if (mountedRef.current) {
|
|
135
|
+
setCloning(false)
|
|
136
|
+
}
|
|
113
137
|
}
|
|
114
138
|
}
|
|
115
139
|
|
|
@@ -117,7 +141,9 @@ export function AgentCard({ agent, isDefault, isRunning, isOnline, isSelected, o
|
|
|
117
141
|
await deleteAgent(agent.id)
|
|
118
142
|
await loadAgents()
|
|
119
143
|
toast.success('Agent moved to trash')
|
|
120
|
-
|
|
144
|
+
if (mountedRef.current) {
|
|
145
|
+
setConfirmDelete(false)
|
|
146
|
+
}
|
|
121
147
|
}
|
|
122
148
|
|
|
123
149
|
return (
|