@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
|
@@ -2,18 +2,19 @@ import { z } from 'zod'
|
|
|
2
2
|
import { tool, type StructuredToolInterface } from '@langchain/core/tools'
|
|
3
3
|
import fs from 'fs'
|
|
4
4
|
import path from 'path'
|
|
5
|
-
import { fileURLToPath, pathToFileURL } from 'url'
|
|
6
5
|
import * as cheerio from 'cheerio'
|
|
7
6
|
import { UPLOAD_DIR } from '../storage'
|
|
8
7
|
import type { ToolBuildContext } from './context'
|
|
9
8
|
import { spawnSync } from 'child_process'
|
|
10
9
|
import { safePath, truncate, MAX_OUTPUT, findBinaryOnPath } from './context'
|
|
11
|
-
import { getSearchProvider
|
|
10
|
+
import { getSearchProvider } from './search-providers'
|
|
12
11
|
import { dedupeScreenshotMarkdownLines } from './web-output'
|
|
13
12
|
import { withRetry } from '../tool-retry'
|
|
14
13
|
import type { Plugin, PluginHooks } from '@/types'
|
|
15
14
|
import { getPluginManager } from '../plugins'
|
|
16
15
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
16
|
+
import { dedup, errorMessage, hmrSingleton, sleep } from '@/lib/shared-utils'
|
|
17
|
+
import { resolvePathWithinBaseDir } from '../path-utils'
|
|
17
18
|
import {
|
|
18
19
|
ensureSessionBrowserProfileId,
|
|
19
20
|
getBrowserProfileDir,
|
|
@@ -23,49 +24,27 @@ import {
|
|
|
23
24
|
removeBrowserSessionRecord,
|
|
24
25
|
upsertBrowserSessionRecord,
|
|
25
26
|
} from '../browser-state'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
sections.push(lines.join('\n'))
|
|
48
|
-
continue
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (url) {
|
|
52
|
-
const minimalLines = [`${index + 1}. ${title}`, `URL: ${url}`]
|
|
53
|
-
const minimalCandidate = joinSections([...sections, minimalLines.join('\n')])
|
|
54
|
-
if (minimalCandidate.length <= maxChars) {
|
|
55
|
-
sections.push(minimalLines.join('\n'))
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const omitted = results.length - index
|
|
60
|
-
if (omitted > 0) {
|
|
61
|
-
const remainingNotice = `(${omitted} additional result${omitted === 1 ? '' : 's'} omitted for brevity)`
|
|
62
|
-
const withNotice = joinSections([...sections, remainingNotice])
|
|
63
|
-
if (withNotice.length <= maxChars) sections.push(remainingNotice)
|
|
64
|
-
}
|
|
65
|
-
return truncate(joinSections(sections), maxChars)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return truncate(joinSections(sections), maxChars)
|
|
27
|
+
import {
|
|
28
|
+
buildBrowserConnectionOptions,
|
|
29
|
+
buildBrowserStdioServerParams,
|
|
30
|
+
formatWebSearchResults,
|
|
31
|
+
inferWebActionFromArgs,
|
|
32
|
+
normalizeBrowserActionParams,
|
|
33
|
+
parseJsonArrayValue,
|
|
34
|
+
pickBrowserTargetFromParams,
|
|
35
|
+
pickNonEmptyBrowserString,
|
|
36
|
+
resolveBrowserNavigationTarget,
|
|
37
|
+
sanitizePlaywrightMcpEnv,
|
|
38
|
+
} from './web-utils'
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
buildBrowserConnectionOptions,
|
|
42
|
+
buildBrowserStdioServerParams,
|
|
43
|
+
formatWebSearchResults,
|
|
44
|
+
inferWebActionFromArgs,
|
|
45
|
+
normalizeBrowserActionParams,
|
|
46
|
+
resolveBrowserNavigationTarget,
|
|
47
|
+
sanitizePlaywrightMcpEnv,
|
|
69
48
|
}
|
|
70
49
|
|
|
71
50
|
type BrowserRuntimeEntry = {
|
|
@@ -77,74 +56,25 @@ type BrowserRuntimeEntry = {
|
|
|
77
56
|
refCount: number
|
|
78
57
|
}
|
|
79
58
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
timeouts: {
|
|
99
|
-
action: 15_000,
|
|
100
|
-
navigation: 60_000,
|
|
101
|
-
},
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function buildBrowserStdioServerParams(profileDir: string) {
|
|
106
|
-
const cliCandidates = [
|
|
107
|
-
path.join(process.cwd(), 'node_modules', '@playwright', 'mcp', 'cli.js'),
|
|
108
|
-
path.join(process.cwd(), '[project]', 'node_modules', '@playwright', 'mcp', 'cli.js'),
|
|
109
|
-
]
|
|
110
|
-
const cliPath = cliCandidates.find((candidate) => fs.existsSync(candidate)) || cliCandidates[0]
|
|
111
|
-
const outputDir = path.join(profileDir, 'mcp-output')
|
|
112
|
-
const env = sanitizePlaywrightMcpEnv()
|
|
113
|
-
return {
|
|
114
|
-
command: process.execPath,
|
|
115
|
-
args: [
|
|
116
|
-
cliPath,
|
|
117
|
-
'--headless',
|
|
118
|
-
'--user-data-dir', profileDir,
|
|
119
|
-
'--output-dir', outputDir,
|
|
120
|
-
'--caps', 'vision,pdf',
|
|
121
|
-
'--image-responses', 'allow',
|
|
122
|
-
'--output-mode', 'file',
|
|
123
|
-
'--timeout-action', '15000',
|
|
124
|
-
'--timeout-navigation', '60000',
|
|
125
|
-
],
|
|
126
|
-
env: {
|
|
127
|
-
...env,
|
|
128
|
-
PLAYWRIGHT_MCP_USER_DATA_DIR: profileDir,
|
|
129
|
-
PLAYWRIGHT_MCP_HEADLESS: '1',
|
|
130
|
-
PLAYWRIGHT_MCP_IMAGE_RESPONSES: 'allow',
|
|
131
|
-
PLAYWRIGHT_MCP_OUTPUT_DIR: outputDir,
|
|
132
|
-
PLAYWRIGHT_MCP_OUTPUT_MODE: 'file',
|
|
133
|
-
PLAYWRIGHT_MCP_TIMEOUT_ACTION: '15000',
|
|
134
|
-
PLAYWRIGHT_MCP_TIMEOUT_NAVIGATION: '60000',
|
|
135
|
-
},
|
|
136
|
-
stderr: 'inherit' as const,
|
|
137
|
-
}
|
|
138
|
-
}
|
|
59
|
+
// Stored on globalThis to survive HMR reloads in dev mode —
|
|
60
|
+
// prevents orphaned Chromium processes when web.ts is edited.
|
|
61
|
+
export const activeBrowsers: Map<string, BrowserRuntimeEntry> =
|
|
62
|
+
hmrSingleton('__swarmclaw_active_browsers__', () => new Map<string, BrowserRuntimeEntry>())
|
|
63
|
+
const pendingBrowserInitializations: Map<string, Promise<BrowserRuntimeEntry>> =
|
|
64
|
+
hmrSingleton('__swarmclaw_pending_browser_inits__', () => new Map<string, Promise<BrowserRuntimeEntry>>())
|
|
65
|
+
|
|
66
|
+
// Kill all browsers on process exit to prevent orphaned Chromium processes
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
68
|
+
const _shutdownRegistered = hmrSingleton('__swarmclaw_browser_shutdown_registered__', () => {
|
|
69
|
+
process.on('exit', () => {
|
|
70
|
+
for (const [, entry] of activeBrowsers) {
|
|
71
|
+
try { entry.client?.close?.() } catch { /* ignore */ }
|
|
72
|
+
try { entry.server?.close?.() } catch { /* ignore */ }
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
return true
|
|
76
|
+
})
|
|
139
77
|
|
|
140
|
-
export function sanitizePlaywrightMcpEnv(baseEnv: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
|
|
141
|
-
const env: NodeJS.ProcessEnv = { ...baseEnv }
|
|
142
|
-
for (const key of Object.keys(env)) {
|
|
143
|
-
if (!key.toUpperCase().startsWith('PLAYWRIGHT_MCP_')) continue
|
|
144
|
-
delete env[key]
|
|
145
|
-
}
|
|
146
|
-
return env
|
|
147
|
-
}
|
|
148
78
|
export function sweepOrphanedBrowsers(maxAgeMs = 30 * 60 * 1000): number {
|
|
149
79
|
const now = Date.now(); let cleaned = 0
|
|
150
80
|
for (const [key, entry] of activeBrowsers) {
|
|
@@ -171,213 +101,10 @@ export function cleanupSessionBrowser(sessionId: string): void {
|
|
|
171
101
|
export function getActiveBrowserCount(): number { return activeBrowsers.size }
|
|
172
102
|
export function hasActiveBrowser(sessionId: string): boolean { return activeBrowsers.has(sessionId) }
|
|
173
103
|
|
|
174
|
-
export function inferWebActionFromArgs(params: {
|
|
175
|
-
action?: string
|
|
176
|
-
query?: string
|
|
177
|
-
url?: string
|
|
178
|
-
}): 'search' | 'fetch' | undefined {
|
|
179
|
-
if (params.action === 'search' || params.action === 'fetch') return params.action
|
|
180
|
-
if (typeof params.url === 'string' && /^https?:\/\//i.test(params.url.trim())) return 'fetch'
|
|
181
|
-
if (typeof params.query === 'string' && params.query.trim()) return 'search'
|
|
182
|
-
if (typeof params.url === 'string' && params.url.trim()) return 'search'
|
|
183
|
-
return undefined
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function parseStructuredJsonValue(value: unknown): unknown {
|
|
187
|
-
if (typeof value !== 'string') return value
|
|
188
|
-
const trimmed = value.trim()
|
|
189
|
-
if (!trimmed || (!trimmed.startsWith('{') && !trimmed.startsWith('['))) return value
|
|
190
|
-
try {
|
|
191
|
-
return JSON.parse(trimmed)
|
|
192
|
-
} catch {
|
|
193
|
-
return value
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function parseJsonObjectValue(value: unknown): Record<string, unknown> | null {
|
|
198
|
-
const parsed = parseStructuredJsonValue(value)
|
|
199
|
-
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
200
|
-
? parsed as Record<string, unknown>
|
|
201
|
-
: null
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function parseJsonArrayValue(value: unknown): unknown[] | null {
|
|
205
|
-
const parsed = parseStructuredJsonValue(value)
|
|
206
|
-
return Array.isArray(parsed) ? parsed : null
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function pickNonEmptyBrowserString(...values: unknown[]): string | undefined {
|
|
210
|
-
for (const value of values) {
|
|
211
|
-
if (typeof value !== 'string') continue
|
|
212
|
-
const trimmed = value.trim()
|
|
213
|
-
if (trimmed) return trimmed
|
|
214
|
-
}
|
|
215
|
-
return undefined
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function wrapBrowserEvaluateFunction(code: string): string {
|
|
219
|
-
const trimmed = code.trim()
|
|
220
|
-
if (!trimmed) return trimmed
|
|
221
|
-
if (/^(?:async\s+)?function\b/.test(trimmed)) return trimmed
|
|
222
|
-
if (/^(?:async\s*)?\([^)]*\)\s*=>/.test(trimmed)) return trimmed
|
|
223
|
-
return /[;{}]/.test(trimmed)
|
|
224
|
-
? `() => { ${trimmed} }`
|
|
225
|
-
: `() => (${trimmed})`
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
function wrapBrowserRunCodeFunction(code: string): string {
|
|
229
|
-
const trimmed = code.trim()
|
|
230
|
-
if (!trimmed) return trimmed
|
|
231
|
-
if (/^(?:async\s+)?function\b/.test(trimmed)) return trimmed
|
|
232
|
-
if (/^(?:async\s*)?\([^)]*\)\s*=>/.test(trimmed)) return trimmed
|
|
233
|
-
return /[;{}]/.test(trimmed)
|
|
234
|
-
? `async (page) => { ${trimmed} }`
|
|
235
|
-
: `async (page) => (${trimmed})`
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export function normalizeBrowserActionParams(rawParams: Record<string, unknown>): Record<string, unknown> {
|
|
239
|
-
const normalized = normalizeToolInputArgs(rawParams)
|
|
240
|
-
const action = String(normalized.action || '').trim().toLowerCase()
|
|
241
|
-
const params: Record<string, unknown> = { ...normalized }
|
|
242
|
-
|
|
243
|
-
const parsedFields = parseJsonArrayValue(params.fields)
|
|
244
|
-
if (parsedFields) params.fields = parsedFields
|
|
245
|
-
|
|
246
|
-
const parsedForm = parseJsonObjectValue(params.form)
|
|
247
|
-
if (parsedForm) params.form = parsedForm
|
|
248
|
-
|
|
249
|
-
if (typeof params.selector === 'string' && !pickNonEmptyBrowserString(params.element)) {
|
|
250
|
-
params.element = params.selector
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (action === 'submit_form' && typeof params.selector === 'string' && !pickNonEmptyBrowserString(params.submitElement)) {
|
|
254
|
-
params.submitElement = params.selector
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (action === 'select') {
|
|
258
|
-
const parsedValues = parseJsonArrayValue(params.values ?? params.option ?? params.value)
|
|
259
|
-
if (parsedValues) params.values = parsedValues
|
|
260
|
-
else if (params.values === undefined) {
|
|
261
|
-
const scalar = pickNonEmptyBrowserString(params.option, params.value, params.text)
|
|
262
|
-
if (scalar) params.values = [scalar]
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (action === 'evaluate' && !pickNonEmptyBrowserString(params.function)) {
|
|
267
|
-
const code = pickNonEmptyBrowserString(params.code, params.script, params.javascript, params.js)
|
|
268
|
-
if (code) params.function = wrapBrowserEvaluateFunction(code)
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (action === 'run_code') {
|
|
272
|
-
const code = pickNonEmptyBrowserString(params.code, params.function, params.script, params.javascript, params.js)
|
|
273
|
-
if (code) params.code = wrapBrowserRunCodeFunction(code)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return params
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function pickBrowserTargetFromParams(params: Record<string, unknown>): string | null {
|
|
280
|
-
for (const value of [
|
|
281
|
-
params.url,
|
|
282
|
-
params.filePath,
|
|
283
|
-
params.path,
|
|
284
|
-
params.href,
|
|
285
|
-
params.link,
|
|
286
|
-
params.target,
|
|
287
|
-
params.page,
|
|
288
|
-
]) {
|
|
289
|
-
if (typeof value !== 'string') continue
|
|
290
|
-
const trimmed = value.trim()
|
|
291
|
-
if (trimmed) return trimmed
|
|
292
|
-
}
|
|
293
|
-
return null
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function resolveUploadFilePath(target: string): string | null {
|
|
297
|
-
const normalized = target.replace(/^sandbox:/, '')
|
|
298
|
-
const match = normalized.match(/^\/api\/uploads\/([^?#]+)/)
|
|
299
|
-
if (!match) return null
|
|
300
|
-
let decoded = match[1]
|
|
301
|
-
try {
|
|
302
|
-
decoded = decodeURIComponent(decoded)
|
|
303
|
-
} catch {
|
|
304
|
-
// keep raw segment
|
|
305
|
-
}
|
|
306
|
-
const safeName = decoded.replace(/[^a-zA-Z0-9._-]/g, '')
|
|
307
|
-
const resolved = path.join(UPLOAD_DIR, safeName)
|
|
308
|
-
return fs.existsSync(resolved) ? resolved : null
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function resolveBrowserFileUrlPath(target: string): string | null {
|
|
312
|
-
if (!/^file:/i.test(target)) return null
|
|
313
|
-
try {
|
|
314
|
-
const resolved = fileURLToPath(target)
|
|
315
|
-
return fs.existsSync(resolved) ? resolved : null
|
|
316
|
-
} catch {
|
|
317
|
-
return null
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function tryResolveBrowserLocalPath(cwd: string, target: string): string | null {
|
|
322
|
-
const uploadPath = resolveUploadFilePath(target)
|
|
323
|
-
if (uploadPath) return uploadPath
|
|
324
|
-
|
|
325
|
-
const fileUrlPath = resolveBrowserFileUrlPath(target)
|
|
326
|
-
if (fileUrlPath) return fileUrlPath
|
|
327
|
-
|
|
328
|
-
if (/^(?:https?:|about:|data:)/i.test(target)) return null
|
|
329
|
-
|
|
330
|
-
const normalized = target.replace(/^sandbox:/, '')
|
|
331
|
-
const looksLikePath = normalized.startsWith('/')
|
|
332
|
-
|| normalized.startsWith('./')
|
|
333
|
-
|| normalized.startsWith('../')
|
|
334
|
-
|| normalized.includes('/')
|
|
335
|
-
|| /\.(?:html?|xhtml|txt|md|json|ya?ml|csv|ts|tsx|js|jsx|mjs|cjs|css|png|jpe?g|gif|webp|svg|pdf)$/i.test(normalized)
|
|
336
|
-
if (!looksLikePath) return null
|
|
337
|
-
|
|
338
|
-
const candidates = new Set<string>()
|
|
339
|
-
if (path.isAbsolute(normalized)) candidates.add(normalized)
|
|
340
|
-
try { candidates.add(safePath(cwd, normalized)) } catch { /* ignore */ }
|
|
341
|
-
try { candidates.add(path.resolve(cwd, normalized)) } catch { /* ignore */ }
|
|
342
|
-
|
|
343
|
-
for (const candidate of candidates) {
|
|
344
|
-
if (!candidate || !fs.existsSync(candidate)) continue
|
|
345
|
-
const stat = fs.statSync(candidate)
|
|
346
|
-
if (stat.isDirectory()) {
|
|
347
|
-
const indexPath = path.join(candidate, 'index.html')
|
|
348
|
-
if (fs.existsSync(indexPath)) return indexPath
|
|
349
|
-
return null
|
|
350
|
-
}
|
|
351
|
-
return candidate
|
|
352
|
-
}
|
|
353
|
-
return null
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function localHtmlFileToDataUrl(filePath: string): string | null {
|
|
357
|
-
const ext = path.extname(filePath).toLowerCase()
|
|
358
|
-
if (ext !== '.html' && ext !== '.htm') return null
|
|
359
|
-
try {
|
|
360
|
-
const html = fs.readFileSync(filePath, 'utf8')
|
|
361
|
-
const hasRelativeAssetReferences = /<(?:script|img|source|video|audio)\b[^>]+\b(?:src|poster)\s*=\s*["'](?![a-z]+:|\/\/|#|\/)([^"']+)["']|<link\b[^>]+\bhref\s*=\s*["'](?![a-z]+:|\/\/|#|\/)([^"']+)["']/i.test(html)
|
|
362
|
-
if (hasRelativeAssetReferences) return null
|
|
363
|
-
return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`
|
|
364
|
-
} catch {
|
|
365
|
-
return null
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
export function resolveBrowserNavigationTarget(cwd: string, target: string): string {
|
|
370
|
-
const trimmed = target.trim()
|
|
371
|
-
if (!trimmed) return trimmed
|
|
372
|
-
const localPath = tryResolveBrowserLocalPath(cwd, trimmed)
|
|
373
|
-
if (localPath) return localHtmlFileToDataUrl(localPath) || pathToFileURL(localPath).toString()
|
|
374
|
-
return trimmed.replace(/^sandbox:/, '')
|
|
375
|
-
}
|
|
376
|
-
|
|
377
104
|
/**
|
|
378
105
|
* Unified Web Execution Logic
|
|
379
106
|
*/
|
|
380
|
-
|
|
107
|
+
|
|
381
108
|
async function executeWebAction(args: Record<string, unknown>) {
|
|
382
109
|
const normalized = normalizeToolInputArgs(args)
|
|
383
110
|
const { query, url, maxResults } = normalized as { query?: string; url?: string; maxResults?: number }
|
|
@@ -414,7 +141,7 @@ async function executeWebAction(args: Record<string, unknown>) {
|
|
|
414
141
|
const result = await pdfParse(Buffer.from(arrayBuffer))
|
|
415
142
|
return truncate(result.text, MAX_OUTPUT)
|
|
416
143
|
} catch (err: unknown) {
|
|
417
|
-
return `Error parsing PDF: ${
|
|
144
|
+
return `Error parsing PDF: ${errorMessage(err)}`
|
|
418
145
|
}
|
|
419
146
|
}
|
|
420
147
|
const html = await res.text()
|
|
@@ -426,7 +153,7 @@ async function executeWebAction(args: Record<string, unknown>) {
|
|
|
426
153
|
}
|
|
427
154
|
return `Error: Unknown action "${action}"`
|
|
428
155
|
} catch (err: unknown) {
|
|
429
|
-
return `Error: ${
|
|
156
|
+
return `Error: ${errorMessage(err)}`
|
|
430
157
|
}
|
|
431
158
|
}
|
|
432
159
|
|
|
@@ -557,7 +284,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
557
284
|
inheritedFromSessionId: profileInfo.inheritedFromSessionId,
|
|
558
285
|
status: 'error',
|
|
559
286
|
lastAction: 'browser_restore',
|
|
560
|
-
lastError:
|
|
287
|
+
lastError: errorMessage(err),
|
|
561
288
|
})
|
|
562
289
|
}
|
|
563
290
|
}
|
|
@@ -877,7 +604,14 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
877
604
|
const fileMatch = text.match(/\]\((\.\.\/[^\s)]+|\/[^\s)]+\.(pdf|png|jpg|jpeg|gif|webp|html|mp4|webm))\)/)
|
|
878
605
|
if (fileMatch) {
|
|
879
606
|
const rawPath = fileMatch[1]
|
|
880
|
-
|
|
607
|
+
let srcPath = rawPath
|
|
608
|
+
if (!rawPath.startsWith('/')) {
|
|
609
|
+
try {
|
|
610
|
+
srcPath = resolvePathWithinBaseDir(cwd, rawPath)
|
|
611
|
+
} catch {
|
|
612
|
+
continue
|
|
613
|
+
}
|
|
614
|
+
}
|
|
881
615
|
if (fs.existsSync(srcPath)) {
|
|
882
616
|
const ext = path.extname(srcPath).slice(1).toLowerCase()
|
|
883
617
|
const IMAGE_EXTS = ['png', 'jpg', 'jpeg', 'gif', 'webp']
|
|
@@ -913,7 +647,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
913
647
|
}
|
|
914
648
|
if (isScreenshotTool) parts = dedupeScreenshotMarkdownLines(parts)
|
|
915
649
|
if (savedPaths.length > 0) {
|
|
916
|
-
const unique =
|
|
650
|
+
const unique = dedup(savedPaths)
|
|
917
651
|
parts.push(`Saved to: ${unique.map((p) => path.relative(cwd, p) || '.').join(', ')}`)
|
|
918
652
|
}
|
|
919
653
|
upsertBrowserSessionRecord({
|
|
@@ -938,7 +672,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
938
672
|
})
|
|
939
673
|
return fallback
|
|
940
674
|
} catch (err: unknown) {
|
|
941
|
-
const message =
|
|
675
|
+
const message = errorMessage(err)
|
|
942
676
|
upsertBrowserSessionRecord({
|
|
943
677
|
sessionId: sessionKey,
|
|
944
678
|
profileId: profileInfo.profileId,
|
|
@@ -981,7 +715,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
981
715
|
}
|
|
982
716
|
|
|
983
717
|
const dismissCookieBanners = async (mcpCall: (toolName: string, args: Record<string, unknown>) => Promise<string>) => {
|
|
984
|
-
await
|
|
718
|
+
await sleep(1200)
|
|
985
719
|
const js = `() => {
|
|
986
720
|
const docs = [document];
|
|
987
721
|
const roots = [document];
|
|
@@ -1250,7 +984,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
1250
984
|
try {
|
|
1251
985
|
await callBrowserEvaluate(`async () => { await new Promise(resolve => setTimeout(resolve, ${Math.min(waitMs, 5000)})); }`)
|
|
1252
986
|
} catch {
|
|
1253
|
-
await
|
|
987
|
+
await sleep(waitMs)
|
|
1254
988
|
}
|
|
1255
989
|
|
|
1256
990
|
return {
|
|
@@ -1654,7 +1388,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
1654
1388
|
}
|
|
1655
1389
|
}); }`)
|
|
1656
1390
|
} catch {
|
|
1657
|
-
await
|
|
1391
|
+
await sleep(1200)
|
|
1658
1392
|
}
|
|
1659
1393
|
try { await dismissCookieBanners(callMcpTool) } catch { /* ignore */ }
|
|
1660
1394
|
}
|
|
@@ -1669,7 +1403,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
1669
1403
|
|
|
1670
1404
|
let result = await callMcpTool(mcpTool, args, { saveTo: typeof params.saveTo === 'string' ? params.saveTo : undefined })
|
|
1671
1405
|
if (action === 'navigate' && result.includes('ERR_ABORTED')) {
|
|
1672
|
-
await
|
|
1406
|
+
await sleep(1000)
|
|
1673
1407
|
result = await callMcpTool('browser_snapshot', {})
|
|
1674
1408
|
}
|
|
1675
1409
|
if (action === 'navigate') {
|
|
@@ -1686,7 +1420,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
1686
1420
|
|
|
1687
1421
|
return result
|
|
1688
1422
|
} catch (err: unknown) {
|
|
1689
|
-
return `Error: ${
|
|
1423
|
+
return `Error: ${errorMessage(err)}`
|
|
1690
1424
|
}
|
|
1691
1425
|
},
|
|
1692
1426
|
{
|
|
@@ -1775,7 +1509,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
1775
1509
|
const result = runBrowserCommand(command, parsedArgs)
|
|
1776
1510
|
if (result.status !== 0) return `Error (exit ${result.status}): ${result.stderr || result.stdout || 'unknown'}`
|
|
1777
1511
|
return truncate(result.stdout || '(no output)', MAX_OUTPUT)
|
|
1778
|
-
} catch (err: unknown) { return `Error: ${
|
|
1512
|
+
} catch (err: unknown) { return `Error: ${errorMessage(err)}` }
|
|
1779
1513
|
},
|
|
1780
1514
|
{
|
|
1781
1515
|
name: 'openclaw_browser',
|
|
@@ -4,7 +4,7 @@ import { budgetSkillsForPrompt, buildSkillPromptText, MAX_SKILLS_IN_PROMPT, MAX_
|
|
|
4
4
|
import type { Skill } from '@/types'
|
|
5
5
|
|
|
6
6
|
function makeSkill(id: string, overrides: Partial<Skill> = {}): Skill {
|
|
7
|
-
return {
|
|
7
|
+
return { id: "test-skill-1",
|
|
8
8
|
name: id,
|
|
9
9
|
filename: `${id}.md`,
|
|
10
10
|
content: overrides.content ?? `Instructions for ${id} skill.`,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import type { SkillInstallOption, SkillRequirements, SkillSecuritySummary } from '@/types'
|
|
3
|
+
import { dedup } from '@/lib/shared-utils'
|
|
3
4
|
|
|
4
5
|
export type SkillSourceFormat = 'openclaw' | 'plain'
|
|
5
6
|
|
|
@@ -292,7 +293,7 @@ function pickRuntimeMetadata(frontmatter: Record<string, unknown>): Record<strin
|
|
|
292
293
|
}
|
|
293
294
|
|
|
294
295
|
function uniqueStrings(values: string[]): string[] {
|
|
295
|
-
return
|
|
296
|
+
return dedup(values.map((value) => value.trim()).filter(Boolean))
|
|
296
297
|
}
|
|
297
298
|
|
|
298
299
|
function extractDetectedEnvVars(rawContent: string): string[] {
|