@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
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import { genId } from '@/lib/id'
|
|
2
|
+
import { hmrSingleton } from '@/lib/shared-utils'
|
|
2
3
|
import type { DelegationJobArtifact, DelegationJobCheckpoint, DelegationJobRecord, DelegationJobStatus } from '@/types'
|
|
3
|
-
import { loadDelegationJobs, upsertDelegationJob } from './storage'
|
|
4
|
+
import { loadDelegationJobs, upsertDelegationJob, patchDelegationJob } from './storage'
|
|
4
5
|
import { notify } from './ws-hub'
|
|
5
6
|
|
|
6
7
|
interface DelegationRuntimeHandle {
|
|
7
8
|
cancel?: () => void
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
-
const runtimeScope = globalThis as typeof globalThis & {
|
|
12
|
-
[runtimeKey]?: Map<string, DelegationRuntimeHandle>
|
|
13
|
-
}
|
|
14
|
-
const runtimeHandles = runtimeScope[runtimeKey] ?? (runtimeScope[runtimeKey] = new Map())
|
|
15
|
-
|
|
16
|
-
function now() {
|
|
17
|
-
return Date.now()
|
|
18
|
-
}
|
|
11
|
+
const runtimeHandles = hmrSingleton('__swarmclaw_delegation_job_runtime__', () => new Map<string, DelegationRuntimeHandle>())
|
|
19
12
|
|
|
20
13
|
function isTerminalStatus(status: DelegationJobStatus | null | undefined): boolean {
|
|
21
14
|
return status === 'completed' || status === 'failed' || status === 'cancelled'
|
|
22
15
|
}
|
|
23
16
|
|
|
17
|
+
function appendCheckpoint(
|
|
18
|
+
existing: DelegationJobCheckpoint[] | undefined,
|
|
19
|
+
checkpoint: DelegationJobCheckpoint,
|
|
20
|
+
): DelegationJobCheckpoint[] {
|
|
21
|
+
return [...(existing || []), checkpoint].slice(-24)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
24
|
function notifyDelegationJobsChanged() {
|
|
25
25
|
notify('delegation_jobs')
|
|
26
26
|
}
|
|
@@ -37,7 +37,7 @@ export interface CreateDelegationJobInput {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export function createDelegationJob(input: CreateDelegationJobInput): DelegationJobRecord {
|
|
40
|
-
const createdAt = now()
|
|
40
|
+
const createdAt = Date.now()
|
|
41
41
|
const job: DelegationJobRecord = {
|
|
42
42
|
id: genId(10),
|
|
43
43
|
kind: input.kind,
|
|
@@ -92,16 +92,16 @@ export function updateDelegationJob(
|
|
|
92
92
|
id: string,
|
|
93
93
|
patch: Partial<DelegationJobRecord>,
|
|
94
94
|
): DelegationJobRecord | null {
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
notifyDelegationJobsChanged()
|
|
104
|
-
return
|
|
95
|
+
const result = patchDelegationJob(id, (current) => {
|
|
96
|
+
if (!current) return null
|
|
97
|
+
return {
|
|
98
|
+
...current,
|
|
99
|
+
...patch,
|
|
100
|
+
updatedAt: Date.now(),
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
if (result) notifyDelegationJobsChanged()
|
|
104
|
+
return result
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
export function appendDelegationCheckpoint(
|
|
@@ -114,10 +114,9 @@ export function appendDelegationCheckpoint(
|
|
|
114
114
|
if (isTerminalStatus(current.status) && status && status !== current.status) {
|
|
115
115
|
return current
|
|
116
116
|
}
|
|
117
|
-
const checkpoints = [...(current.checkpoints || []), { at: now(), note, status }]
|
|
118
117
|
return updateDelegationJob(id, {
|
|
119
118
|
status: isTerminalStatus(current.status) ? current.status : (status ?? current.status),
|
|
120
|
-
checkpoints: checkpoints.
|
|
119
|
+
checkpoints: appendCheckpoint(current.checkpoints, { at: Date.now(), note, status }),
|
|
121
120
|
})
|
|
122
121
|
}
|
|
123
122
|
|
|
@@ -128,7 +127,7 @@ export function startDelegationJob(id: string, patch?: Partial<DelegationJobReco
|
|
|
128
127
|
return updateDelegationJob(id, {
|
|
129
128
|
...patch,
|
|
130
129
|
status: 'running',
|
|
131
|
-
startedAt: now(),
|
|
130
|
+
startedAt: Date.now(),
|
|
132
131
|
})
|
|
133
132
|
}
|
|
134
133
|
|
|
@@ -147,7 +146,7 @@ export function completeDelegationJob(
|
|
|
147
146
|
result,
|
|
148
147
|
resultPreview: result.slice(0, 1000),
|
|
149
148
|
error: null,
|
|
150
|
-
completedAt: now(),
|
|
149
|
+
completedAt: Date.now(),
|
|
151
150
|
})
|
|
152
151
|
}
|
|
153
152
|
|
|
@@ -160,7 +159,7 @@ export function failDelegationJob(id: string, error: string, patch?: Partial<Del
|
|
|
160
159
|
...patch,
|
|
161
160
|
status: 'failed',
|
|
162
161
|
error,
|
|
163
|
-
completedAt: now(),
|
|
162
|
+
completedAt: Date.now(),
|
|
164
163
|
})
|
|
165
164
|
}
|
|
166
165
|
|
|
@@ -174,21 +173,20 @@ export function cancelDelegationJob(id: string): DelegationJobRecord | null {
|
|
|
174
173
|
} catch {
|
|
175
174
|
// best-effort cancel
|
|
176
175
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
note: 'Job cancelled',
|
|
181
|
-
status: 'cancelled',
|
|
182
|
-
}
|
|
183
|
-
return updateDelegationJob(id, {
|
|
176
|
+
// Commit state before deleting handle — if the update fails, the handle
|
|
177
|
+
// stays intact for a future retry rather than being orphaned.
|
|
178
|
+
const result = updateDelegationJob(id, {
|
|
184
179
|
status: 'cancelled',
|
|
185
|
-
completedAt: now(),
|
|
180
|
+
completedAt: Date.now(),
|
|
186
181
|
error: null,
|
|
187
|
-
checkpoints:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
182
|
+
checkpoints: appendCheckpoint(current.checkpoints, {
|
|
183
|
+
at: Date.now(),
|
|
184
|
+
note: 'Job cancelled',
|
|
185
|
+
status: 'cancelled',
|
|
186
|
+
}),
|
|
191
187
|
})
|
|
188
|
+
runtimeHandles.delete(id)
|
|
189
|
+
return result
|
|
192
190
|
}
|
|
193
191
|
|
|
194
192
|
export function cancelDelegationJobsForParentSession(
|
|
@@ -206,16 +204,12 @@ export function cancelDelegationJobsForParentSession(
|
|
|
206
204
|
const checkpoints = Array.isArray(next.checkpoints) ? next.checkpoints : []
|
|
207
205
|
const last = checkpoints[checkpoints.length - 1]
|
|
208
206
|
if (!last || last.note !== note) {
|
|
209
|
-
const checkpoint: DelegationJobCheckpoint = {
|
|
210
|
-
at: now(),
|
|
211
|
-
note,
|
|
212
|
-
status: 'cancelled',
|
|
213
|
-
}
|
|
214
207
|
updateDelegationJob(job.id, {
|
|
215
|
-
checkpoints:
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
208
|
+
checkpoints: appendCheckpoint(next.checkpoints, {
|
|
209
|
+
at: Date.now(),
|
|
210
|
+
note,
|
|
211
|
+
status: 'cancelled',
|
|
212
|
+
}),
|
|
219
213
|
})
|
|
220
214
|
}
|
|
221
215
|
}
|
|
@@ -235,7 +229,7 @@ export function appendDelegationArtifacts(id: string, artifacts: DelegationJobAr
|
|
|
235
229
|
}
|
|
236
230
|
|
|
237
231
|
export function recoverStaleDelegationJobs(maxAgeMs = 15 * 60_000): number {
|
|
238
|
-
const threshold = now() - maxAgeMs
|
|
232
|
+
const threshold = Date.now() - maxAgeMs
|
|
239
233
|
const stale = listDelegationJobs().filter((job) =>
|
|
240
234
|
(job.status === 'queued' || job.status === 'running')
|
|
241
235
|
&& !runtimeHandles.has(job.id)
|
|
@@ -2,11 +2,13 @@ import fs from 'fs'
|
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { spawnSync } from 'child_process'
|
|
4
4
|
import * as cheerio from 'cheerio'
|
|
5
|
+
import { dedup } from '@/lib/shared-utils'
|
|
5
6
|
import { findBinaryOnPath } from './session-tools/context'
|
|
7
|
+
import { safeJsonParse } from './json-utils'
|
|
6
8
|
|
|
7
9
|
const TEXT_EXTENSIONS = new Set([
|
|
8
10
|
'.txt', '.md', '.markdown', '.json', '.jsonl', '.csv', '.tsv',
|
|
9
|
-
'
|
|
11
|
+
'', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.py', '.go', '.rs',
|
|
10
12
|
'.java', '.yaml', '.yml', '.sql', '.xml', '.css', '.scss', '.html', '.htm',
|
|
11
13
|
])
|
|
12
14
|
const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp', '.tif', '.tiff'])
|
|
@@ -113,7 +115,7 @@ function matrixToTable(name: string, matrix: string[][]): StructuredTable {
|
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
function objectsToTable(name: string, rows: Array<Record<string, unknown>>): StructuredTable {
|
|
116
|
-
const headers =
|
|
118
|
+
const headers = dedup(rows.flatMap((row) => Object.keys(row)))
|
|
117
119
|
const normalizedRows = rows.map((row) => {
|
|
118
120
|
const out: Record<string, unknown> = {}
|
|
119
121
|
for (const header of headers) out[header] = normalizeScalar(row[header])
|
|
@@ -264,13 +266,9 @@ export async function extractDocumentArtifact(filePath: string, options?: { maxC
|
|
|
264
266
|
const raw = fs.readFileSync(resolved, 'utf-8')
|
|
265
267
|
text = raw
|
|
266
268
|
method = 'json'
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
tables = [objectsToTable(path.basename(resolved), parsed as Array<Record<string, unknown>>)]
|
|
271
|
-
}
|
|
272
|
-
} catch {
|
|
273
|
-
// keep raw json text only
|
|
269
|
+
const parsed = safeJsonParse<unknown>(raw, null)
|
|
270
|
+
if (Array.isArray(parsed) && parsed.every((row) => row && typeof row === 'object' && !Array.isArray(row))) {
|
|
271
|
+
tables = [objectsToTable(path.basename(resolved), parsed as Array<Record<string, unknown>>)]
|
|
274
272
|
}
|
|
275
273
|
} else if (ext === '.html' || ext === '.htm') {
|
|
276
274
|
const html = fs.readFileSync(resolved, 'utf-8')
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { decryptKey, loadSettings, loadSecrets } from './storage'
|
|
2
|
+
import { dedup } from '@/lib/shared-utils'
|
|
2
3
|
|
|
3
4
|
const DEFAULT_VOICE_ID = 'JBFqnCBsd6RMkjVDRZzb'
|
|
4
5
|
const DEFAULT_MODEL_ID = 'eleven_multilingual_v2'
|
|
@@ -56,7 +57,7 @@ function resolveElevenLabsApiKeyCandidates(): string[] {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
return
|
|
60
|
+
return dedup(candidates)
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
function shouldRetryWithFallbackVoice(voiceId: string, errBody: string): boolean {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { describe, it } from 'node:test'
|
|
3
|
+
|
|
4
|
+
// embeddings.ts exports pure utility functions (cosineSimilarity, serialize/deserialize)
|
|
5
|
+
// that don't need DATA_DIR. getEmbedding needs storage — skip that, test pure logic.
|
|
6
|
+
|
|
7
|
+
// Direct import is safe for the pure math/serialization functions since the module
|
|
8
|
+
// only touches storage lazily in getEmbedding().
|
|
9
|
+
// However the top-level import of ./storage will trigger DB init,
|
|
10
|
+
// so we use dynamic import with SWARMCLAW_BUILD_MODE.
|
|
11
|
+
|
|
12
|
+
let embeddings: typeof import('./embeddings')
|
|
13
|
+
|
|
14
|
+
import { before } from 'node:test'
|
|
15
|
+
|
|
16
|
+
before(async () => {
|
|
17
|
+
process.env.SWARMCLAW_BUILD_MODE = '1'
|
|
18
|
+
embeddings = await import('./embeddings')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('embeddings - cosineSimilarity', () => {
|
|
22
|
+
it('identical vectors have similarity 1', () => {
|
|
23
|
+
const v = [1, 2, 3, 4, 5]
|
|
24
|
+
const sim = embeddings.cosineSimilarity(v, v)
|
|
25
|
+
assert.ok(Math.abs(sim - 1) < 1e-10, `expected ~1, got ${sim}`)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('orthogonal vectors have similarity 0', () => {
|
|
29
|
+
const a = [1, 0, 0]
|
|
30
|
+
const b = [0, 1, 0]
|
|
31
|
+
const sim = embeddings.cosineSimilarity(a, b)
|
|
32
|
+
assert.ok(Math.abs(sim) < 1e-10, `expected ~0, got ${sim}`)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('opposite vectors have similarity -1', () => {
|
|
36
|
+
const a = [1, 2, 3]
|
|
37
|
+
const b = [-1, -2, -3]
|
|
38
|
+
const sim = embeddings.cosineSimilarity(a, b)
|
|
39
|
+
assert.ok(Math.abs(sim + 1) < 1e-10, `expected ~-1, got ${sim}`)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('different-length vectors return 0', () => {
|
|
43
|
+
const sim = embeddings.cosineSimilarity([1, 2], [1, 2, 3])
|
|
44
|
+
assert.equal(sim, 0)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('zero vector returns 0', () => {
|
|
48
|
+
const sim = embeddings.cosineSimilarity([0, 0, 0], [1, 2, 3])
|
|
49
|
+
assert.equal(sim, 0)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('both zero vectors return 0', () => {
|
|
53
|
+
const sim = embeddings.cosineSimilarity([0, 0], [0, 0])
|
|
54
|
+
assert.equal(sim, 0)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('empty vectors return 0', () => {
|
|
58
|
+
const sim = embeddings.cosineSimilarity([], [])
|
|
59
|
+
assert.equal(sim, 0)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe('embeddings - serialize/deserialize roundtrip', () => {
|
|
64
|
+
it('roundtrips a simple embedding', () => {
|
|
65
|
+
const original = [0.1, 0.2, 0.3, -0.5, 1.0]
|
|
66
|
+
const buf = embeddings.serializeEmbedding(original)
|
|
67
|
+
const restored = embeddings.deserializeEmbedding(buf)
|
|
68
|
+
|
|
69
|
+
assert.equal(restored.length, original.length)
|
|
70
|
+
for (let i = 0; i < original.length; i++) {
|
|
71
|
+
assert.ok(Math.abs(restored[i] - original[i]) < 1e-6, `index ${i}: ${restored[i]} vs ${original[i]}`)
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('roundtrips an empty embedding', () => {
|
|
76
|
+
const buf = embeddings.serializeEmbedding([])
|
|
77
|
+
const restored = embeddings.deserializeEmbedding(buf)
|
|
78
|
+
assert.equal(restored.length, 0)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('roundtrips a large embedding (384 dimensions)', () => {
|
|
82
|
+
const original = Array.from({ length: 384 }, (_, i) => Math.sin(i) * 0.5)
|
|
83
|
+
const buf = embeddings.serializeEmbedding(original)
|
|
84
|
+
assert.equal(buf.byteLength, 384 * 4) // Float32 = 4 bytes each
|
|
85
|
+
const restored = embeddings.deserializeEmbedding(buf)
|
|
86
|
+
assert.equal(restored.length, 384)
|
|
87
|
+
for (let i = 0; i < original.length; i++) {
|
|
88
|
+
assert.ok(Math.abs(restored[i] - original[i]) < 1e-6)
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('serialize produces a Buffer', () => {
|
|
93
|
+
const buf = embeddings.serializeEmbedding([1, 2, 3])
|
|
94
|
+
assert.ok(Buffer.isBuffer(buf))
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('preserves special float values', () => {
|
|
98
|
+
const original = [0, -0, Infinity, -Infinity]
|
|
99
|
+
const buf = embeddings.serializeEmbedding(original)
|
|
100
|
+
const restored = embeddings.deserializeEmbedding(buf)
|
|
101
|
+
assert.equal(restored[0], 0)
|
|
102
|
+
assert.equal(restored[2], Infinity)
|
|
103
|
+
assert.equal(restored[3], -Infinity)
|
|
104
|
+
})
|
|
105
|
+
})
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from 'ethers'
|
|
16
16
|
|
|
17
17
|
import { decryptKey, encryptKey } from './storage'
|
|
18
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
18
19
|
|
|
19
20
|
export type EvmNetworkId = 'ethereum' | 'arbitrum' | 'base'
|
|
20
21
|
|
|
@@ -490,7 +491,7 @@ export async function simulateEthereumTransaction(
|
|
|
490
491
|
`estimate gas on ${network.label}`,
|
|
491
492
|
)).toString()
|
|
492
493
|
} catch (err: unknown) {
|
|
493
|
-
callError =
|
|
494
|
+
callError = errorMessage(err)
|
|
494
495
|
}
|
|
495
496
|
|
|
496
497
|
try {
|
|
@@ -499,7 +500,7 @@ export async function simulateEthereumTransaction(
|
|
|
499
500
|
`simulate transaction call on ${network.label}`,
|
|
500
501
|
)
|
|
501
502
|
} catch (err: unknown) {
|
|
502
|
-
if (!callError) callError =
|
|
503
|
+
if (!callError) callError = errorMessage(err)
|
|
503
504
|
}
|
|
504
505
|
|
|
505
506
|
return {
|
|
@@ -5,6 +5,7 @@ import { createHash } from 'node:crypto'
|
|
|
5
5
|
import path from 'node:path'
|
|
6
6
|
import { genId } from '@/lib/id'
|
|
7
7
|
import type { ApprovalRequest, MessageToolEvent, Session } from '@/types'
|
|
8
|
+
import { dedup } from '@/lib/shared-utils'
|
|
8
9
|
import { submitDecision } from '../approvals'
|
|
9
10
|
import { executeSessionChatTurn, type ExecuteChatTurnResult } from '../chat-execution'
|
|
10
11
|
import { WORKSPACE_DIR } from '../data-dir'
|
|
@@ -850,11 +851,11 @@ export function resolveRegressionPlugins(
|
|
|
850
851
|
agent: Record<string, unknown>,
|
|
851
852
|
pluginMode: RegressionPluginMode,
|
|
852
853
|
): RegressionPluginResolution {
|
|
853
|
-
const requiredCanonical =
|
|
854
|
+
const requiredCanonical = dedup(
|
|
854
855
|
normalizePluginList(requiredPlugins)
|
|
855
856
|
.map((plugin) => canonicalizePluginId(plugin))
|
|
856
857
|
.filter(Boolean),
|
|
857
|
-
)
|
|
858
|
+
)
|
|
858
859
|
if (pluginMode === 'scenario') {
|
|
859
860
|
return {
|
|
860
861
|
requiredPlugins: requiredCanonical,
|
|
@@ -9,6 +9,7 @@ import { loadSessions, saveSessions, loadAgents, loadCredentials, decryptKey } f
|
|
|
9
9
|
import { executeSessionChatTurn } from '../chat-execution'
|
|
10
10
|
import { WORKSPACE_DIR } from '../data-dir'
|
|
11
11
|
import type { Session } from '@/types'
|
|
12
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
12
13
|
|
|
13
14
|
export function resolveEvalSessionCwd(runId: string): string {
|
|
14
15
|
const dir = path.join(WORKSPACE_DIR, 'evals', runId)
|
|
@@ -98,7 +99,7 @@ export async function runEvalScenario(scenarioId: string, agentId: string): Prom
|
|
|
98
99
|
run.endedAt = Date.now()
|
|
99
100
|
} catch (err: unknown) {
|
|
100
101
|
run.status = 'failed'
|
|
101
|
-
run.error =
|
|
102
|
+
run.error = errorMessage(err)
|
|
102
103
|
run.endedAt = Date.now()
|
|
103
104
|
} finally {
|
|
104
105
|
// Clean up eval session
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ScoringCriterion, EvalCriterionResult } from './types'
|
|
2
2
|
import type { MessageToolEvent } from '@/types'
|
|
3
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
3
4
|
|
|
4
5
|
export async function scoreCriteria(
|
|
5
6
|
criteria: ScoringCriterion[],
|
|
@@ -84,7 +85,7 @@ export async function scoreCriteria(
|
|
|
84
85
|
criterion: criterion.name,
|
|
85
86
|
score: 0,
|
|
86
87
|
maxScore: criterion.weight,
|
|
87
|
-
evidence: `LLM judge error: ${
|
|
88
|
+
evidence: `LLM judge error: ${errorMessage(err)}`,
|
|
88
89
|
})
|
|
89
90
|
}
|
|
90
91
|
break
|
|
@@ -5,6 +5,7 @@ import type { AgentWallet, WalletAssetBalance } from '@/types'
|
|
|
5
5
|
|
|
6
6
|
import { getEvmNetworkConfig, getProviderForNetwork, type EvmNetworkId } from './ethereum'
|
|
7
7
|
import { getWalletPortfolioSnapshot } from './wallet-service'
|
|
8
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
8
9
|
|
|
9
10
|
const PARASWAP_API_BASE = 'https://api.paraswap.io'
|
|
10
11
|
const PARASWAP_VERSION = '6.2'
|
|
@@ -470,6 +471,6 @@ export async function prepareEvmSwapPlan(input: PrepareEvmSwapPlanInput): Promis
|
|
|
470
471
|
}
|
|
471
472
|
|
|
472
473
|
export function isLikelyRetryableSwapError(err: unknown): boolean {
|
|
473
|
-
const message =
|
|
474
|
+
const message = errorMessage(err)
|
|
474
475
|
return /rate|price|slippage|expired|call exception|execution reverted|insufficient output/i.test(message)
|
|
475
476
|
}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
createGatewayRequestFrame,
|
|
5
5
|
parseGatewayFrame,
|
|
6
6
|
serializeGatewayFrame,
|
|
7
|
-
} from './protocol
|
|
7
|
+
} from './protocol'
|
|
8
8
|
|
|
9
9
|
test('gateway protocol parses request/response/event frames', () => {
|
|
10
10
|
const req = parseGatewayFrame('{"type":"req","id":"1","method":"connect","params":{"foo":"bar"}}')
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execSync } from 'child_process'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import fs from 'fs'
|
|
4
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* OpenClaw Guardian — Auto-Rollback capability.
|
|
@@ -29,6 +30,6 @@ export function performGuardianRollback(cwd: string): { ok: boolean; reason?: st
|
|
|
29
30
|
return { ok: true }
|
|
30
31
|
} catch (err: unknown) {
|
|
31
32
|
console.error('[guardian] Auto-rollback failed:', err)
|
|
32
|
-
return { ok: false, reason: `Git operation failed: ${
|
|
33
|
+
return { ok: false, reason: `Git operation failed: ${errorMessage(err)}` }
|
|
33
34
|
}
|
|
34
35
|
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { describe, it } from 'node:test'
|
|
3
|
+
import {
|
|
4
|
+
stripBlockedItems,
|
|
5
|
+
isHeartbeatContentEffectivelyEmpty,
|
|
6
|
+
buildAgentHeartbeatPrompt,
|
|
7
|
+
} from './heartbeat-service'
|
|
8
|
+
|
|
9
|
+
describe('heartbeat blocked-item suppression', () => {
|
|
10
|
+
describe('stripBlockedItems', () => {
|
|
11
|
+
it('removes checklist items marked (blocked, no update)', () => {
|
|
12
|
+
const input = [
|
|
13
|
+
'# Heartbeat Tasks',
|
|
14
|
+
'## Active',
|
|
15
|
+
'- [ ] Pull SWGOH roster data (blocked, no update)',
|
|
16
|
+
'- [ ] Send daily summary',
|
|
17
|
+
'## Completed',
|
|
18
|
+
'- [x] Do laundry',
|
|
19
|
+
].join('\n')
|
|
20
|
+
|
|
21
|
+
const result = stripBlockedItems(input)
|
|
22
|
+
|
|
23
|
+
assert.ok(!result.includes('SWGOH'), 'blocked item should be stripped')
|
|
24
|
+
assert.ok(result.includes('Send daily summary'), 'non-blocked item should remain')
|
|
25
|
+
assert.ok(result.includes('Do laundry'), 'completed item should remain')
|
|
26
|
+
assert.ok(result.includes('# Heartbeat Tasks'), 'headers should remain')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('removes items with various blocked markers', () => {
|
|
30
|
+
const input = [
|
|
31
|
+
'- [ ] Task A (blocked: awaiting input)',
|
|
32
|
+
'- [ ] Task B (Blocked, pending user decision)',
|
|
33
|
+
'- [ ] Task C (BLOCKED)',
|
|
34
|
+
'- [ ] Task D - normal task',
|
|
35
|
+
'* [ ] Task E (blocked, no update)',
|
|
36
|
+
].join('\n')
|
|
37
|
+
|
|
38
|
+
const result = stripBlockedItems(input)
|
|
39
|
+
|
|
40
|
+
assert.ok(!result.includes('Task A'), 'blocked: variant stripped')
|
|
41
|
+
assert.ok(!result.includes('Task B'), 'Blocked, variant stripped')
|
|
42
|
+
assert.ok(!result.includes('Task C'), 'BLOCKED variant stripped')
|
|
43
|
+
assert.ok(result.includes('Task D'), 'non-blocked task preserved')
|
|
44
|
+
assert.ok(!result.includes('Task E'), 'asterisk list item stripped')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('preserves non-list lines that mention "blocked"', () => {
|
|
48
|
+
const input = [
|
|
49
|
+
'## Notes',
|
|
50
|
+
'Some items are blocked until user responds.',
|
|
51
|
+
'- [ ] Actual blocked task (blocked, no update)',
|
|
52
|
+
].join('\n')
|
|
53
|
+
|
|
54
|
+
const result = stripBlockedItems(input)
|
|
55
|
+
|
|
56
|
+
assert.ok(result.includes('Some items are blocked'), 'prose mentioning blocked should stay')
|
|
57
|
+
assert.ok(!result.includes('Actual blocked task'), 'blocked list item should be stripped')
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('returns empty string for empty input', () => {
|
|
61
|
+
assert.equal(stripBlockedItems(''), '')
|
|
62
|
+
assert.equal(stripBlockedItems(null as unknown as string), '')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('returns content unchanged when no blocked items', () => {
|
|
66
|
+
const input = '- [ ] Task A\n- [ ] Task B\n'
|
|
67
|
+
assert.equal(stripBlockedItems(input), input)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('blocked items + effectively empty', () => {
|
|
72
|
+
it('treats content with only blocked items as effectively empty', () => {
|
|
73
|
+
const input = [
|
|
74
|
+
'# Heartbeat Tasks',
|
|
75
|
+
'## Active',
|
|
76
|
+
'- [ ] Pull SWGOH data (blocked, no update)',
|
|
77
|
+
'## Completed',
|
|
78
|
+
].join('\n')
|
|
79
|
+
|
|
80
|
+
const stripped = stripBlockedItems(input)
|
|
81
|
+
// After stripping, only headers remain — effectively empty
|
|
82
|
+
assert.equal(isHeartbeatContentEffectivelyEmpty(stripped), true)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('treats content with blocked + active items as not empty', () => {
|
|
86
|
+
const input = [
|
|
87
|
+
'# Heartbeat Tasks',
|
|
88
|
+
'## Active',
|
|
89
|
+
'- [ ] Pull SWGOH data (blocked, no update)',
|
|
90
|
+
'- [ ] Send daily summary',
|
|
91
|
+
].join('\n')
|
|
92
|
+
|
|
93
|
+
const stripped = stripBlockedItems(input)
|
|
94
|
+
assert.equal(isHeartbeatContentEffectivelyEmpty(stripped), false)
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
describe('buildAgentHeartbeatPrompt integration', () => {
|
|
99
|
+
it('does not include blocked items in the prompt sent to the LLM', () => {
|
|
100
|
+
const session = {
|
|
101
|
+
id: 'test-session',
|
|
102
|
+
cwd: '/tmp',
|
|
103
|
+
messages: [],
|
|
104
|
+
}
|
|
105
|
+
const agent = {
|
|
106
|
+
id: 'test-agent',
|
|
107
|
+
name: 'Test',
|
|
108
|
+
description: 'Test agent',
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const heartbeatFileContent = [
|
|
112
|
+
'# Heartbeat Tasks',
|
|
113
|
+
'## Active',
|
|
114
|
+
'- [ ] Pull SWGOH roster data (blocked, no update)',
|
|
115
|
+
'- [ ] Check weather forecast',
|
|
116
|
+
'## Completed',
|
|
117
|
+
'- [x] Laundry done',
|
|
118
|
+
].join('\n')
|
|
119
|
+
|
|
120
|
+
const prompt = buildAgentHeartbeatPrompt(session, agent, 'default prompt', heartbeatFileContent)
|
|
121
|
+
|
|
122
|
+
assert.ok(!prompt.includes('SWGOH'), 'blocked SWGOH task should not appear in prompt')
|
|
123
|
+
assert.ok(prompt.includes('Check weather forecast'), 'non-blocked task should appear')
|
|
124
|
+
assert.ok(prompt.includes('Laundry done'), 'completed task should appear')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('produces no HEARTBEAT.md section when all active items are blocked', () => {
|
|
128
|
+
const session = {
|
|
129
|
+
id: 'test-session',
|
|
130
|
+
cwd: '/tmp',
|
|
131
|
+
messages: [],
|
|
132
|
+
}
|
|
133
|
+
const agent = {
|
|
134
|
+
id: 'test-agent',
|
|
135
|
+
name: 'Test',
|
|
136
|
+
description: 'Test agent',
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const heartbeatFileContent = [
|
|
140
|
+
'# Heartbeat Tasks',
|
|
141
|
+
'## Active',
|
|
142
|
+
'- [ ] Task A (blocked, no update)',
|
|
143
|
+
'- [ ] Task B (blocked: awaiting user)',
|
|
144
|
+
].join('\n')
|
|
145
|
+
|
|
146
|
+
const prompt = buildAgentHeartbeatPrompt(session, agent, 'default prompt', heartbeatFileContent)
|
|
147
|
+
|
|
148
|
+
assert.ok(!prompt.includes('HEARTBEAT.md contents:'), 'should not include HEARTBEAT.md section when all items are blocked')
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
})
|
|
@@ -37,9 +37,9 @@ describe('heartbeat-service scheduling', () => {
|
|
|
37
37
|
it('does not fire periodic heartbeats for agents that are explicitly off', () => {
|
|
38
38
|
const output = runWithTempDataDir(`
|
|
39
39
|
const { setTimeout: delay } = await import('node:timers/promises')
|
|
40
|
-
const storageMod = await import('./src/lib/server/storage
|
|
41
|
-
const heartbeatMod = await import('./src/lib/server/heartbeat-service
|
|
42
|
-
const runsMod = await import('./src/lib/server/session-run-manager
|
|
40
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
41
|
+
const heartbeatMod = await import('./src/lib/server/heartbeat-service')
|
|
42
|
+
const runsMod = await import('./src/lib/server/session-run-manager')
|
|
43
43
|
const storage = storageMod.default || storageMod['module.exports'] || storageMod
|
|
44
44
|
const heartbeat = heartbeatMod.default || heartbeatMod['module.exports'] || heartbeatMod
|
|
45
45
|
const runs = runsMod.default || runsMod['module.exports'] || runsMod
|
|
@@ -100,9 +100,9 @@ describe('heartbeat-service scheduling', () => {
|
|
|
100
100
|
it('fires periodic heartbeats only after the service tick window when enabled', () => {
|
|
101
101
|
const output = runWithTempDataDir(`
|
|
102
102
|
const { setTimeout: delay } = await import('node:timers/promises')
|
|
103
|
-
const storageMod = await import('./src/lib/server/storage
|
|
104
|
-
const heartbeatMod = await import('./src/lib/server/heartbeat-service
|
|
105
|
-
const runsMod = await import('./src/lib/server/session-run-manager
|
|
103
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
104
|
+
const heartbeatMod = await import('./src/lib/server/heartbeat-service')
|
|
105
|
+
const runsMod = await import('./src/lib/server/session-run-manager')
|
|
106
106
|
const storage = storageMod.default || storageMod['module.exports'] || storageMod
|
|
107
107
|
const heartbeat = heartbeatMod.default || heartbeatMod['module.exports'] || heartbeatMod
|
|
108
108
|
const runs = runsMod.default || runsMod['module.exports'] || runsMod
|