@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
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight performance tracing for critical server paths.
|
|
3
|
+
*
|
|
4
|
+
* Emits structured `[perf]` log lines with timing data so workbench tests
|
|
5
|
+
* can measure where time is spent during chat turns, tool calls, queue
|
|
6
|
+
* processing, and storage operations.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* const end = perf.start('chat-execution', 'streamAgentChat', { sessionId })
|
|
10
|
+
* // ... do work ...
|
|
11
|
+
* end({ toolCount: 3, tokens: 1200 })
|
|
12
|
+
*
|
|
13
|
+
* Output:
|
|
14
|
+
* [perf] chat-execution/streamAgentChat 1423ms {sessionId:"abc",toolCount:3,tokens:1200}
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface PerfEntry {
|
|
18
|
+
category: string
|
|
19
|
+
label: string
|
|
20
|
+
durationMs: number
|
|
21
|
+
meta?: Record<string, unknown>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type OnEntryCallback = (entry: PerfEntry) => void
|
|
25
|
+
|
|
26
|
+
// Disabled by default — workbench tests call perf.setEnabled(true) to activate.
|
|
27
|
+
// The SWARMCLAW_PERF env var also enables it for CLI-level benchmarking.
|
|
28
|
+
let _enabled = process.env.SWARMCLAW_PERF === '1'
|
|
29
|
+
let _onEntry: OnEntryCallback | null = null
|
|
30
|
+
const _recentEntries: PerfEntry[] = []
|
|
31
|
+
const MAX_RECENT = 200
|
|
32
|
+
|
|
33
|
+
function emitEntry(entry: PerfEntry): void {
|
|
34
|
+
_recentEntries.push(entry)
|
|
35
|
+
if (_recentEntries.length > MAX_RECENT) _recentEntries.shift()
|
|
36
|
+
|
|
37
|
+
if (_onEntry) {
|
|
38
|
+
try { _onEntry(entry) } catch { /* listener errors are non-critical */ }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const metaStr = entry.meta && Object.keys(entry.meta).length > 0
|
|
42
|
+
? ' ' + JSON.stringify(entry.meta)
|
|
43
|
+
: ''
|
|
44
|
+
console.log(`[perf] ${entry.category}/${entry.label} ${entry.durationMs}ms${metaStr}`)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const _noopEnd = () => 0
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Start a performance measurement. Returns a function that, when called,
|
|
51
|
+
* records the elapsed time and emits a log entry.
|
|
52
|
+
*
|
|
53
|
+
* When perf is disabled (default in production), returns a shared no-op —
|
|
54
|
+
* zero allocation overhead.
|
|
55
|
+
*/
|
|
56
|
+
function start(
|
|
57
|
+
category: string,
|
|
58
|
+
label: string,
|
|
59
|
+
meta?: Record<string, unknown>,
|
|
60
|
+
): (extraMeta?: Record<string, unknown>) => number {
|
|
61
|
+
if (!_enabled) return _noopEnd
|
|
62
|
+
const t0 = performance.now()
|
|
63
|
+
return (extraMeta?: Record<string, unknown>) => {
|
|
64
|
+
const durationMs = Math.round(performance.now() - t0)
|
|
65
|
+
const merged = (meta || extraMeta)
|
|
66
|
+
? { ...meta, ...extraMeta }
|
|
67
|
+
: undefined
|
|
68
|
+
emitEntry({ category, label, durationMs, meta: merged })
|
|
69
|
+
return durationMs
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Measure an async function. Returns the function's result.
|
|
75
|
+
*/
|
|
76
|
+
async function measureAsync<T>(
|
|
77
|
+
category: string,
|
|
78
|
+
label: string,
|
|
79
|
+
fn: () => Promise<T>,
|
|
80
|
+
meta?: Record<string, unknown>,
|
|
81
|
+
): Promise<T> {
|
|
82
|
+
const end = start(category, label, meta)
|
|
83
|
+
try {
|
|
84
|
+
const result = await fn()
|
|
85
|
+
end()
|
|
86
|
+
return result
|
|
87
|
+
} catch (err) {
|
|
88
|
+
end({ error: true })
|
|
89
|
+
throw err
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Measure a synchronous function. Returns the function's result.
|
|
95
|
+
*/
|
|
96
|
+
function measureSync<T>(
|
|
97
|
+
category: string,
|
|
98
|
+
label: string,
|
|
99
|
+
fn: () => T,
|
|
100
|
+
meta?: Record<string, unknown>,
|
|
101
|
+
): T {
|
|
102
|
+
const end = start(category, label, meta)
|
|
103
|
+
try {
|
|
104
|
+
const result = fn()
|
|
105
|
+
end()
|
|
106
|
+
return result
|
|
107
|
+
} catch (err) {
|
|
108
|
+
end({ error: true })
|
|
109
|
+
throw err
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Enable/disable perf tracing at runtime. */
|
|
114
|
+
function setEnabled(enabled: boolean): void { _enabled = enabled }
|
|
115
|
+
|
|
116
|
+
/** Check if perf tracing is enabled. */
|
|
117
|
+
function isEnabled(): boolean { return _enabled }
|
|
118
|
+
|
|
119
|
+
/** Register a callback for every perf entry (useful for tests/workbench). */
|
|
120
|
+
function onEntry(cb: OnEntryCallback | null): void { _onEntry = cb }
|
|
121
|
+
|
|
122
|
+
/** Get recent perf entries (ring buffer, max 200). */
|
|
123
|
+
function getRecentEntries(): readonly PerfEntry[] { return _recentEntries }
|
|
124
|
+
|
|
125
|
+
/** Clear the recent entries buffer. */
|
|
126
|
+
function clearRecentEntries(): void { _recentEntries.length = 0 }
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Wrap a Next.js API route handler with perf timing.
|
|
130
|
+
* Usage: export const GET = withApiPerf('GET /api/agents', handler)
|
|
131
|
+
*/
|
|
132
|
+
function withApiPerf<T extends (...args: unknown[]) => Promise<Response>>(
|
|
133
|
+
label: string,
|
|
134
|
+
handler: T,
|
|
135
|
+
): T {
|
|
136
|
+
return (async (...args: unknown[]) => {
|
|
137
|
+
const end = start('api', label)
|
|
138
|
+
try {
|
|
139
|
+
const response = await handler(...args)
|
|
140
|
+
end({ status: response.status })
|
|
141
|
+
return response
|
|
142
|
+
} catch (err) {
|
|
143
|
+
end({ error: true })
|
|
144
|
+
throw err
|
|
145
|
+
}
|
|
146
|
+
}) as T
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const perf = {
|
|
150
|
+
start,
|
|
151
|
+
measureAsync,
|
|
152
|
+
measureSync,
|
|
153
|
+
setEnabled,
|
|
154
|
+
isEnabled,
|
|
155
|
+
onEntry,
|
|
156
|
+
getRecentEntries,
|
|
157
|
+
clearRecentEntries,
|
|
158
|
+
withApiPerf,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type { PerfEntry }
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { PluginHooks, PluginToolDef } from '@/types'
|
|
2
|
+
import { canonicalizePluginId, expandPluginIds } from './tool-aliases'
|
|
3
|
+
import { dedup } from '@/lib/shared-utils'
|
|
4
|
+
|
|
5
|
+
type ApprovalGuidanceHook = NonNullable<PluginHooks['getApprovalGuidance']>
|
|
6
|
+
|
|
7
|
+
function trimString(value: unknown): string {
|
|
8
|
+
return typeof value === 'string' ? value.trim() : ''
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function normalizeApprovalGuidanceLines(
|
|
12
|
+
value: string | string[] | null | undefined,
|
|
13
|
+
): string[] {
|
|
14
|
+
if (typeof value === 'string') {
|
|
15
|
+
const trimmed = value.trim()
|
|
16
|
+
return trimmed ? [trimmed] : []
|
|
17
|
+
}
|
|
18
|
+
if (!Array.isArray(value)) return []
|
|
19
|
+
return value
|
|
20
|
+
.map((line) => (typeof line === 'string' ? line.trim() : ''))
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function dedupeApprovalGuidanceLines(lines: string[]): string[] {
|
|
25
|
+
return dedup(lines.map((line) => line.trim()).filter(Boolean))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function formatApprovalToolLabel(toolNames: string[]): string {
|
|
29
|
+
const uniqueNames = dedup(toolNames.map((name) => name.trim()).filter(Boolean))
|
|
30
|
+
if (uniqueNames.length === 0) return 'its tools'
|
|
31
|
+
if (uniqueNames.length === 1) return `\`${uniqueNames[0]}\``
|
|
32
|
+
if (uniqueNames.length === 2) return `\`${uniqueNames[0]}\` and \`${uniqueNames[1]}\``
|
|
33
|
+
return `${uniqueNames.slice(0, -1).map((name) => `\`${name}\``).join(', ')}, and \`${uniqueNames.at(-1)}\``
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function buildDefaultPluginApprovalGuidance(params: {
|
|
37
|
+
pluginId: string
|
|
38
|
+
pluginName: string
|
|
39
|
+
tools: PluginToolDef[]
|
|
40
|
+
}): ApprovalGuidanceHook {
|
|
41
|
+
const toolNames = params.tools
|
|
42
|
+
.map((tool) => (typeof tool?.name === 'string' ? tool.name.trim() : ''))
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
const toolLabel = formatApprovalToolLabel(toolNames)
|
|
45
|
+
const matchIds = new Set(
|
|
46
|
+
dedupeApprovalGuidanceLines([
|
|
47
|
+
params.pluginId,
|
|
48
|
+
...toolNames,
|
|
49
|
+
...expandPluginIds([params.pluginId]),
|
|
50
|
+
...toolNames.flatMap((toolName) => expandPluginIds([toolName])),
|
|
51
|
+
]).map((value) => canonicalizePluginId(value) || value.toLowerCase()),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return ({ approval, phase, approved }) => {
|
|
55
|
+
if (approval.category !== 'tool_access') return null
|
|
56
|
+
const requestedIds = [
|
|
57
|
+
trimString(approval.data.pluginId),
|
|
58
|
+
trimString(approval.data.toolId),
|
|
59
|
+
trimString(approval.data.toolName),
|
|
60
|
+
].filter(Boolean)
|
|
61
|
+
const matchesPlugin = requestedIds.some((value) => {
|
|
62
|
+
const candidates = [value, ...expandPluginIds([value])]
|
|
63
|
+
return candidates.some((candidate) => matchIds.has(canonicalizePluginId(candidate) || candidate.toLowerCase()))
|
|
64
|
+
})
|
|
65
|
+
if (!matchesPlugin) return null
|
|
66
|
+
|
|
67
|
+
if (phase === 'connector_reminder') {
|
|
68
|
+
return `Approving this lets the agent use ${toolLabel} from ${params.pluginName}.`
|
|
69
|
+
}
|
|
70
|
+
if (approved === true) {
|
|
71
|
+
return [
|
|
72
|
+
`Access to ${params.pluginName} is approved. Continue with ${toolLabel} on the next turn.`,
|
|
73
|
+
'Do not request the same access again in prose once it has been approved.',
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
if (approved === false) {
|
|
77
|
+
return `Do not request access to ${params.pluginName} again unless the task or required capability materially changes.`
|
|
78
|
+
}
|
|
79
|
+
return [
|
|
80
|
+
`If access to ${params.pluginName} is granted, continue with ${toolLabel} on the next turn.`,
|
|
81
|
+
'Do not ask for the same access again in prose while this approval is pending.',
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function composeApprovalGuidance(
|
|
87
|
+
defaultHook: ApprovalGuidanceHook,
|
|
88
|
+
customHook?: PluginHooks['getApprovalGuidance'],
|
|
89
|
+
): ApprovalGuidanceHook {
|
|
90
|
+
return (ctx) => {
|
|
91
|
+
const combined = dedupeApprovalGuidanceLines([
|
|
92
|
+
...normalizeApprovalGuidanceLines(defaultHook(ctx)),
|
|
93
|
+
...normalizeApprovalGuidanceLines(customHook?.(ctx)),
|
|
94
|
+
])
|
|
95
|
+
return combined.length > 0 ? combined : null
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function buildPluginHooks(
|
|
100
|
+
pluginId: string,
|
|
101
|
+
pluginName: string,
|
|
102
|
+
hooks: PluginHooks | undefined,
|
|
103
|
+
tools: PluginToolDef[] | undefined,
|
|
104
|
+
): PluginHooks {
|
|
105
|
+
const nextHooks: PluginHooks = { ...(hooks || {}) }
|
|
106
|
+
nextHooks.getApprovalGuidance = composeApprovalGuidance(
|
|
107
|
+
buildDefaultPluginApprovalGuidance({
|
|
108
|
+
pluginId,
|
|
109
|
+
pluginName,
|
|
110
|
+
tools: tools || [],
|
|
111
|
+
}),
|
|
112
|
+
hooks?.getApprovalGuidance,
|
|
113
|
+
)
|
|
114
|
+
return nextHooks
|
|
115
|
+
}
|
|
@@ -53,7 +53,7 @@ describe('plugin install helpers', () => {
|
|
|
53
53
|
assert.equal(sanitizePluginFilename('plugin.js'), 'plugin.js')
|
|
54
54
|
assert.equal(sanitizePluginFilename('plugin.mjs'), 'plugin.mjs')
|
|
55
55
|
assert.throws(() => sanitizePluginFilename('../plugin.js'), /Invalid filename/)
|
|
56
|
-
assert.throws(() => sanitizePluginFilename('plugin
|
|
56
|
+
assert.throws(() => sanitizePluginFilename('plugin'), /Filename must end/)
|
|
57
57
|
})
|
|
58
58
|
})
|
|
59
59
|
|
|
@@ -17,6 +17,8 @@ import { log } from './logger'
|
|
|
17
17
|
import { createNotification } from './create-notification'
|
|
18
18
|
import { notify } from './ws-hub'
|
|
19
19
|
import { decryptKey, encryptKey, loadSettings, saveSettings } from './storage'
|
|
20
|
+
import { buildPluginHooks } from './plugins-approval-guidance'
|
|
21
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
20
22
|
|
|
21
23
|
const PLUGINS_DIR = path.join(DATA_DIR, 'plugins')
|
|
22
24
|
const PLUGIN_WORKSPACES_DIR = path.join(PLUGINS_DIR, '.workspaces')
|
|
@@ -107,8 +109,6 @@ type HookRegistrar = {
|
|
|
107
109
|
type HookContext<K extends keyof PluginHooks> =
|
|
108
110
|
PluginHooks[K] extends ((ctx: infer C) => unknown) | undefined ? C : never
|
|
109
111
|
|
|
110
|
-
type ApprovalGuidanceHook = NonNullable<PluginHooks['getApprovalGuidance']>
|
|
111
|
-
|
|
112
112
|
/** Legacy OpenClaw format: activate(ctx)/deactivate() */
|
|
113
113
|
interface OpenClawLegacyPlugin {
|
|
114
114
|
name: string
|
|
@@ -117,116 +117,6 @@ interface OpenClawLegacyPlugin {
|
|
|
117
117
|
deactivate?: () => void
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
function trimString(value: unknown): string {
|
|
121
|
-
return typeof value === 'string' ? value.trim() : ''
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function normalizeApprovalGuidanceLines(
|
|
125
|
-
value: string | string[] | null | undefined,
|
|
126
|
-
): string[] {
|
|
127
|
-
if (typeof value === 'string') {
|
|
128
|
-
const trimmed = value.trim()
|
|
129
|
-
return trimmed ? [trimmed] : []
|
|
130
|
-
}
|
|
131
|
-
if (!Array.isArray(value)) return []
|
|
132
|
-
return value
|
|
133
|
-
.map((line) => (typeof line === 'string' ? line.trim() : ''))
|
|
134
|
-
.filter(Boolean)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function dedupeApprovalGuidanceLines(lines: string[]): string[] {
|
|
138
|
-
return Array.from(new Set(lines.map((line) => line.trim()).filter(Boolean)))
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function formatApprovalToolLabel(toolNames: string[]): string {
|
|
142
|
-
const uniqueNames = Array.from(new Set(toolNames.map((name) => name.trim()).filter(Boolean)))
|
|
143
|
-
if (uniqueNames.length === 0) return 'its tools'
|
|
144
|
-
if (uniqueNames.length === 1) return `\`${uniqueNames[0]}\``
|
|
145
|
-
if (uniqueNames.length === 2) return `\`${uniqueNames[0]}\` and \`${uniqueNames[1]}\``
|
|
146
|
-
return `${uniqueNames.slice(0, -1).map((name) => `\`${name}\``).join(', ')}, and \`${uniqueNames.at(-1)}\``
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function buildDefaultPluginApprovalGuidance(params: {
|
|
150
|
-
pluginId: string
|
|
151
|
-
pluginName: string
|
|
152
|
-
tools: PluginToolDef[]
|
|
153
|
-
}): ApprovalGuidanceHook {
|
|
154
|
-
const toolNames = params.tools
|
|
155
|
-
.map((tool) => (typeof tool?.name === 'string' ? tool.name.trim() : ''))
|
|
156
|
-
.filter(Boolean)
|
|
157
|
-
const toolLabel = formatApprovalToolLabel(toolNames)
|
|
158
|
-
const matchIds = new Set(
|
|
159
|
-
dedupeApprovalGuidanceLines([
|
|
160
|
-
params.pluginId,
|
|
161
|
-
...toolNames,
|
|
162
|
-
...expandPluginIds([params.pluginId]),
|
|
163
|
-
...toolNames.flatMap((toolName) => expandPluginIds([toolName])),
|
|
164
|
-
]).map((value) => canonicalizePluginId(value) || value.toLowerCase()),
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
return ({ approval, phase, approved }) => {
|
|
168
|
-
if (approval.category !== 'tool_access') return null
|
|
169
|
-
const requestedIds = [
|
|
170
|
-
trimString(approval.data.pluginId),
|
|
171
|
-
trimString(approval.data.toolId),
|
|
172
|
-
trimString(approval.data.toolName),
|
|
173
|
-
].filter(Boolean)
|
|
174
|
-
const matchesPlugin = requestedIds.some((value) => {
|
|
175
|
-
const candidates = [value, ...expandPluginIds([value])]
|
|
176
|
-
return candidates.some((candidate) => matchIds.has(canonicalizePluginId(candidate) || candidate.toLowerCase()))
|
|
177
|
-
})
|
|
178
|
-
if (!matchesPlugin) return null
|
|
179
|
-
|
|
180
|
-
if (phase === 'connector_reminder') {
|
|
181
|
-
return `Approving this lets the agent use ${toolLabel} from ${params.pluginName}.`
|
|
182
|
-
}
|
|
183
|
-
if (approved === true) {
|
|
184
|
-
return [
|
|
185
|
-
`Access to ${params.pluginName} is approved. Continue with ${toolLabel} on the next turn.`,
|
|
186
|
-
'Do not request the same access again in prose once it has been approved.',
|
|
187
|
-
]
|
|
188
|
-
}
|
|
189
|
-
if (approved === false) {
|
|
190
|
-
return `Do not request access to ${params.pluginName} again unless the task or required capability materially changes.`
|
|
191
|
-
}
|
|
192
|
-
return [
|
|
193
|
-
`If access to ${params.pluginName} is granted, continue with ${toolLabel} on the next turn.`,
|
|
194
|
-
'Do not ask for the same access again in prose while this approval is pending.',
|
|
195
|
-
]
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function composeApprovalGuidance(
|
|
200
|
-
defaultHook: ApprovalGuidanceHook,
|
|
201
|
-
customHook?: PluginHooks['getApprovalGuidance'],
|
|
202
|
-
): ApprovalGuidanceHook {
|
|
203
|
-
return (ctx) => {
|
|
204
|
-
const combined = dedupeApprovalGuidanceLines([
|
|
205
|
-
...normalizeApprovalGuidanceLines(defaultHook(ctx)),
|
|
206
|
-
...normalizeApprovalGuidanceLines(customHook?.(ctx)),
|
|
207
|
-
])
|
|
208
|
-
return combined.length > 0 ? combined : null
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function buildPluginHooks(
|
|
213
|
-
pluginId: string,
|
|
214
|
-
pluginName: string,
|
|
215
|
-
hooks: PluginHooks | undefined,
|
|
216
|
-
tools: PluginToolDef[] | undefined,
|
|
217
|
-
): PluginHooks {
|
|
218
|
-
const nextHooks: PluginHooks = { ...(hooks || {}) }
|
|
219
|
-
nextHooks.getApprovalGuidance = composeApprovalGuidance(
|
|
220
|
-
buildDefaultPluginApprovalGuidance({
|
|
221
|
-
pluginId,
|
|
222
|
-
pluginName,
|
|
223
|
-
tools: tools || [],
|
|
224
|
-
}),
|
|
225
|
-
hooks?.getApprovalGuidance,
|
|
226
|
-
)
|
|
227
|
-
return nextHooks
|
|
228
|
-
}
|
|
229
|
-
|
|
230
120
|
/**
|
|
231
121
|
* Real OpenClaw plugin format: function export `(api) => {}` or object with `register(api)`.
|
|
232
122
|
* Supports api.registerHook(), api.registerTool(), api.registerCommand(), api.registerService().
|
|
@@ -569,7 +459,7 @@ function normalizePlugin(mod: unknown): Plugin | null {
|
|
|
569
459
|
} catch (err: unknown) {
|
|
570
460
|
log.error('plugins', 'OpenClaw register() failed', {
|
|
571
461
|
pluginName,
|
|
572
|
-
error:
|
|
462
|
+
error: errorMessage(err),
|
|
573
463
|
})
|
|
574
464
|
return null
|
|
575
465
|
}
|
|
@@ -610,7 +500,7 @@ function normalizePlugin(mod: unknown): Plugin | null {
|
|
|
610
500
|
} catch (err: unknown) {
|
|
611
501
|
log.error('plugins', 'OpenClaw activate() failed', {
|
|
612
502
|
pluginName: oc.name,
|
|
613
|
-
error:
|
|
503
|
+
error: errorMessage(err),
|
|
614
504
|
})
|
|
615
505
|
return null
|
|
616
506
|
}
|
|
@@ -643,7 +533,7 @@ function createPluginRequire(): NodeRequire | null {
|
|
|
643
533
|
return createRequire(path.join(process.cwd(), 'package.json'))
|
|
644
534
|
} catch (err: unknown) {
|
|
645
535
|
log.warn('plugins', 'createRequire failed; external plugins disabled', {
|
|
646
|
-
error:
|
|
536
|
+
error: errorMessage(err),
|
|
647
537
|
})
|
|
648
538
|
return null
|
|
649
539
|
}
|
|
@@ -679,7 +569,7 @@ class PluginManager {
|
|
|
679
569
|
})
|
|
680
570
|
watcher.on('error', (err: unknown) => {
|
|
681
571
|
log.warn('plugins', 'Plugin watcher disabled after runtime watch failure', {
|
|
682
|
-
error:
|
|
572
|
+
error: errorMessage(err),
|
|
683
573
|
})
|
|
684
574
|
if (this.watcher === watcher) {
|
|
685
575
|
try { watcher.close() } catch { /* ignore */ }
|
|
@@ -690,7 +580,7 @@ class PluginManager {
|
|
|
690
580
|
this.watcher = watcher
|
|
691
581
|
} catch (err: unknown) {
|
|
692
582
|
log.warn('plugins', 'Failed to watch plugins directory', {
|
|
693
|
-
error:
|
|
583
|
+
error: errorMessage(err),
|
|
694
584
|
})
|
|
695
585
|
}
|
|
696
586
|
}
|
|
@@ -879,7 +769,7 @@ class PluginManager {
|
|
|
879
769
|
try {
|
|
880
770
|
fs.writeFileSync(PLUGIN_FAILURES, JSON.stringify(state, null, 2))
|
|
881
771
|
} catch (err: unknown) {
|
|
882
|
-
log.warn('plugins', 'Failed to persist plugin failure state', { error:
|
|
772
|
+
log.warn('plugins', 'Failed to persist plugin failure state', { error: errorMessage(err) })
|
|
883
773
|
}
|
|
884
774
|
}
|
|
885
775
|
|
|
@@ -903,7 +793,7 @@ class PluginManager {
|
|
|
903
793
|
} catch (err: unknown) {
|
|
904
794
|
log.error('plugins', 'Failed to write plugins config while auto-disabling plugin', {
|
|
905
795
|
pluginId: id,
|
|
906
|
-
error:
|
|
796
|
+
error: errorMessage(err),
|
|
907
797
|
})
|
|
908
798
|
return
|
|
909
799
|
}
|
|
@@ -932,7 +822,7 @@ class PluginManager {
|
|
|
932
822
|
}
|
|
933
823
|
|
|
934
824
|
private markPluginFailure(id: string, stage: string, err: unknown, disableEligible: boolean): void {
|
|
935
|
-
const errorText =
|
|
825
|
+
const errorText = errorMessage(err)
|
|
936
826
|
const state = this.readFailureState()
|
|
937
827
|
const failureKey = this.canonicalPluginId(id)
|
|
938
828
|
const nextCount = (state[failureKey]?.count || 0) + 1
|
|
@@ -966,7 +856,7 @@ class PluginManager {
|
|
|
966
856
|
try {
|
|
967
857
|
this.clearFailureState(id)
|
|
968
858
|
} catch (err: unknown) {
|
|
969
|
-
log.warn('plugins', 'markPluginSuccess failed', { error:
|
|
859
|
+
log.warn('plugins', 'markPluginSuccess failed', { error: errorMessage(err), pluginId: id })
|
|
970
860
|
}
|
|
971
861
|
}
|
|
972
862
|
|
|
@@ -1053,7 +943,7 @@ class PluginManager {
|
|
|
1053
943
|
} catch (err: unknown) {
|
|
1054
944
|
log.error('plugins', 'Failed to load external plugin', {
|
|
1055
945
|
pluginId: file,
|
|
1056
|
-
error:
|
|
946
|
+
error: errorMessage(err),
|
|
1057
947
|
})
|
|
1058
948
|
this.markPluginFailure(file, 'load.require', err, true)
|
|
1059
949
|
}
|
|
@@ -1150,7 +1040,7 @@ class PluginManager {
|
|
|
1150
1040
|
pluginId: id,
|
|
1151
1041
|
pluginName: p.meta.name,
|
|
1152
1042
|
hookName: String(hookName),
|
|
1153
|
-
error:
|
|
1043
|
+
error: errorMessage(err),
|
|
1154
1044
|
})
|
|
1155
1045
|
this.markPluginFailure(id, `hook.${String(hookName)}`, err, true)
|
|
1156
1046
|
}
|
|
@@ -1181,7 +1071,7 @@ class PluginManager {
|
|
|
1181
1071
|
pluginId: id,
|
|
1182
1072
|
pluginName: p.meta.name,
|
|
1183
1073
|
toolName: params.toolName,
|
|
1184
|
-
error:
|
|
1074
|
+
error: errorMessage(err),
|
|
1185
1075
|
})
|
|
1186
1076
|
this.markPluginFailure(id, 'hook.beforeToolExec', err, true)
|
|
1187
1077
|
}
|
|
@@ -1212,7 +1102,7 @@ class PluginManager {
|
|
|
1212
1102
|
pluginId: id,
|
|
1213
1103
|
pluginName: p.meta.name,
|
|
1214
1104
|
hookName,
|
|
1215
|
-
error:
|
|
1105
|
+
error: errorMessage(err),
|
|
1216
1106
|
})
|
|
1217
1107
|
this.markPluginFailure(id, `hook.${String(hookName)}`, err, true)
|
|
1218
1108
|
}
|
|
@@ -1240,7 +1130,7 @@ class PluginManager {
|
|
|
1240
1130
|
log.error('plugins', 'getAgentContext hook failed', {
|
|
1241
1131
|
pluginId: id,
|
|
1242
1132
|
pluginName: p.meta.name,
|
|
1243
|
-
error:
|
|
1133
|
+
error: errorMessage(err),
|
|
1244
1134
|
})
|
|
1245
1135
|
this.markPluginFailure(id, 'hook.getAgentContext', err, true)
|
|
1246
1136
|
}
|
|
@@ -1265,7 +1155,7 @@ class PluginManager {
|
|
|
1265
1155
|
lines.push(`- ${result}`)
|
|
1266
1156
|
}
|
|
1267
1157
|
} catch (err: unknown) {
|
|
1268
|
-
log.error('plugins', 'getCapabilityDescription hook failed', { pluginId: id, error:
|
|
1158
|
+
log.error('plugins', 'getCapabilityDescription hook failed', { pluginId: id, error: errorMessage(err) })
|
|
1269
1159
|
}
|
|
1270
1160
|
}
|
|
1271
1161
|
|
|
@@ -1293,7 +1183,7 @@ class PluginManager {
|
|
|
1293
1183
|
}
|
|
1294
1184
|
}
|
|
1295
1185
|
} catch (err: unknown) {
|
|
1296
|
-
log.error('plugins', 'getOperatingGuidance hook failed', { pluginId: id, error:
|
|
1186
|
+
log.error('plugins', 'getOperatingGuidance hook failed', { pluginId: id, error: errorMessage(err) })
|
|
1297
1187
|
}
|
|
1298
1188
|
}
|
|
1299
1189
|
|
|
@@ -1330,7 +1220,7 @@ class PluginManager {
|
|
|
1330
1220
|
} catch (err: unknown) {
|
|
1331
1221
|
log.error('plugins', 'getApprovalGuidance hook failed', {
|
|
1332
1222
|
pluginId: id,
|
|
1333
|
-
error:
|
|
1223
|
+
error: errorMessage(err),
|
|
1334
1224
|
})
|
|
1335
1225
|
}
|
|
1336
1226
|
}
|
|
@@ -1617,7 +1507,7 @@ class PluginManager {
|
|
|
1617
1507
|
|
|
1618
1508
|
return metas
|
|
1619
1509
|
} catch (err: unknown) {
|
|
1620
|
-
log.error('plugins', 'listPlugins failed', { error:
|
|
1510
|
+
log.error('plugins', 'listPlugins failed', { error: errorMessage(err) })
|
|
1621
1511
|
return []
|
|
1622
1512
|
}
|
|
1623
1513
|
}
|
|
@@ -1714,7 +1604,7 @@ class PluginManager {
|
|
|
1714
1604
|
dependencyInstalledAt: Date.now(),
|
|
1715
1605
|
})
|
|
1716
1606
|
} catch (err: unknown) {
|
|
1717
|
-
const message =
|
|
1607
|
+
const message = errorMessage(err)
|
|
1718
1608
|
this.setMeta(sanitizedFilename, {
|
|
1719
1609
|
packageManager,
|
|
1720
1610
|
dependencyInstallStatus: 'error',
|
|
@@ -1832,7 +1722,7 @@ export function getPluginManager(): PluginManager {
|
|
|
1832
1722
|
}
|
|
1833
1723
|
return _manager
|
|
1834
1724
|
} catch (err: unknown) {
|
|
1835
|
-
log.error('plugins', 'getPluginManager critical failure', { error:
|
|
1725
|
+
log.error('plugins', 'getPluginManager critical failure', { error: errorMessage(err) })
|
|
1836
1726
|
throw err
|
|
1837
1727
|
}
|
|
1838
1728
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { genId } from '@/lib/id'
|
|
2
|
+
import { hmrSingleton, sleep } from '@/lib/shared-utils'
|
|
2
3
|
import { spawn, type ChildProcessWithoutNullStreams } from 'child_process'
|
|
3
4
|
|
|
4
5
|
const MAX_LOG_CHARS = 200_000
|
|
@@ -52,12 +53,11 @@ interface RuntimeState {
|
|
|
52
53
|
exitWaiters: Map<string, Promise<ProcessRecord>>
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
const
|
|
56
|
-
const state: RuntimeState = (globalThis as any)[globalKey] ?? ((globalThis as any)[globalKey] = {
|
|
56
|
+
const state: RuntimeState = hmrSingleton<RuntimeState>('__swarmclaw_process_manager__', () => ({
|
|
57
57
|
records: new Map<string, ProcessRecord>(),
|
|
58
58
|
children: new Map<string, ChildProcessWithoutNullStreams>(),
|
|
59
59
|
exitWaiters: new Map<string, Promise<ProcessRecord>>(),
|
|
60
|
-
})
|
|
60
|
+
}))
|
|
61
61
|
|
|
62
62
|
function now() {
|
|
63
63
|
return Date.now()
|
|
@@ -91,9 +91,6 @@ function normalizeLines(text: string): string[] {
|
|
|
91
91
|
return text.split('\n')
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
async function wait(ms: number): Promise<void> {
|
|
95
|
-
await new Promise((resolve) => setTimeout(resolve, ms))
|
|
96
|
-
}
|
|
97
94
|
|
|
98
95
|
function getShellCommand(command: string): { shell: string; args: string[] } {
|
|
99
96
|
return { shell: '/bin/zsh', args: ['-lc', command] }
|
|
@@ -177,7 +174,7 @@ export async function startManagedProcess(opts: StartProcessOptions): Promise<St
|
|
|
177
174
|
Math.max(100, BACKGROUND_STARTUP_GRACE_MS),
|
|
178
175
|
Math.max(200, timeoutMs),
|
|
179
176
|
)
|
|
180
|
-
await
|
|
177
|
+
await sleep(startupWaitMs)
|
|
181
178
|
const rec = state.records.get(id)
|
|
182
179
|
if (rec && rec.status !== 'running') {
|
|
183
180
|
return {
|
|
@@ -197,7 +194,7 @@ export async function startManagedProcess(opts: StartProcessOptions): Promise<St
|
|
|
197
194
|
|
|
198
195
|
const completed = await Promise.race([
|
|
199
196
|
exitPromise.then((r) => ({ type: 'exit' as const, record: r })),
|
|
200
|
-
|
|
197
|
+
sleep(yieldMs).then(() => ({ type: 'yield' as const })),
|
|
201
198
|
])
|
|
202
199
|
|
|
203
200
|
if (completed.type === 'yield') {
|