@swarmclawai/swarmclaw 0.8.4 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/bin/swarmclaw.js +5 -1
- package/bin/worker-cmd.js +73 -0
- package/package.json +2 -1
- package/src/app/api/agents/[id]/route.ts +17 -7
- package/src/app/api/agents/route.ts +21 -8
- package/src/app/api/approvals/route.test.ts +6 -6
- package/src/app/api/approvals/route.ts +2 -1
- package/src/app/api/auth/route.ts +2 -3
- package/src/app/api/chatrooms/[id]/chat/route.test.ts +299 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +3 -2
- package/src/app/api/chatrooms/[id]/route.ts +7 -6
- package/src/app/api/chats/[id]/chat/route.test.ts +496 -0
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/clear/route.ts +9 -9
- package/src/app/api/chats/[id]/devserver/route.ts +2 -1
- package/src/app/api/chats/[id]/edit-resend/route.ts +3 -4
- package/src/app/api/chats/[id]/fork/route.ts +3 -5
- package/src/app/api/chats/[id]/restore/route.ts +6 -7
- package/src/app/api/chats/[id]/retry/route.ts +3 -4
- package/src/app/api/chats/[id]/route.ts +61 -62
- package/src/app/api/chats/route.ts +7 -1
- package/src/app/api/connectors/[id]/route.ts +7 -8
- package/src/app/api/connectors/route.ts +5 -4
- package/src/app/api/eval/run/route.ts +2 -1
- package/src/app/api/eval/suite/route.ts +2 -1
- package/src/app/api/external-agents/route.test.ts +1 -1
- package/src/app/api/external-agents/route.ts +2 -2
- package/src/app/api/files/serve/route.ts +1 -1
- package/src/app/api/gateways/[id]/route.ts +7 -5
- package/src/app/api/gateways/route.ts +1 -1
- package/src/app/api/knowledge/upload/route.ts +1 -1
- package/src/app/api/logs/route.ts +5 -7
- package/src/app/api/memory-images/[filename]/route.ts +2 -3
- package/src/app/api/openclaw/agent-files/route.ts +4 -3
- package/src/app/api/openclaw/approvals/route.ts +3 -4
- package/src/app/api/openclaw/config-sync/route.ts +3 -2
- package/src/app/api/openclaw/cron/route.ts +3 -2
- package/src/app/api/openclaw/dotenv-keys/route.ts +2 -1
- package/src/app/api/openclaw/exec-config/route.ts +3 -2
- package/src/app/api/openclaw/gateway/route.ts +5 -4
- package/src/app/api/openclaw/history/route.ts +3 -2
- package/src/app/api/openclaw/media/route.ts +2 -1
- package/src/app/api/openclaw/permissions/route.ts +3 -2
- package/src/app/api/openclaw/sandbox-env/route.ts +3 -2
- package/src/app/api/openclaw/skills/install/route.ts +2 -1
- package/src/app/api/openclaw/skills/remove/route.ts +2 -1
- package/src/app/api/openclaw/skills/route.ts +3 -2
- package/src/app/api/orchestrator/run/route.ts +5 -14
- package/src/app/api/perf/route.ts +43 -0
- package/src/app/api/plugins/dependencies/route.ts +2 -1
- package/src/app/api/plugins/install/route.ts +2 -1
- package/src/app/api/plugins/marketplace/route.ts +3 -2
- package/src/app/api/plugins/settings/route.ts +2 -1
- package/src/app/api/preview-server/route.ts +11 -10
- package/src/app/api/projects/[id]/route.ts +1 -1
- package/src/app/api/schedules/[id]/route.test.ts +128 -0
- package/src/app/api/schedules/[id]/route.ts +43 -43
- package/src/app/api/schedules/[id]/run/route.ts +11 -62
- package/src/app/api/schedules/route.ts +21 -87
- package/src/app/api/settings/route.ts +2 -0
- package/src/app/api/setup/doctor/route.ts +9 -8
- package/src/app/api/tasks/[id]/approve/route.ts +33 -30
- package/src/app/api/tasks/[id]/route.ts +12 -35
- package/src/app/api/tasks/import/github/route.ts +2 -1
- package/src/app/api/tasks/route.ts +79 -91
- package/src/app/api/wallets/[id]/approve/route.ts +2 -1
- package/src/app/api/wallets/[id]/route.ts +13 -19
- package/src/app/api/wallets/[id]/send/route.ts +2 -1
- package/src/app/api/wallets/route.ts +2 -1
- package/src/app/api/webhooks/[id]/route.ts +2 -1
- package/src/app/api/webhooks/route.test.ts +3 -1
- package/src/app/page.tsx +23 -331
- package/src/cli/index.js +19 -0
- package/src/cli/index.ts +38 -7
- package/src/cli/spec.js +9 -0
- package/src/components/activity/activity-feed.tsx +7 -4
- package/src/components/agents/agent-card.tsx +32 -6
- package/src/components/agents/agent-chat-list.tsx +55 -22
- package/src/components/agents/agent-files-editor.tsx +3 -2
- package/src/components/agents/agent-sheet.tsx +123 -22
- package/src/components/agents/inspector-panel.tsx +1 -1
- package/src/components/agents/openclaw-skills-panel.tsx +2 -1
- package/src/components/agents/trash-list.tsx +1 -1
- package/src/components/auth/access-key-gate.tsx +8 -2
- package/src/components/auth/setup-wizard.tsx +10 -9
- package/src/components/auth/user-picker.tsx +3 -2
- package/src/components/chat/chat-area.tsx +20 -1
- package/src/components/chat/chat-card.tsx +18 -3
- package/src/components/chat/chat-header.tsx +24 -4
- package/src/components/chat/chat-list.tsx +2 -11
- package/src/components/chat/heartbeat-history-panel.tsx +2 -1
- package/src/components/chat/message-bubble.tsx +45 -6
- package/src/components/chat/message-list.tsx +280 -145
- package/src/components/chat/streaming-bubble.tsx +217 -60
- package/src/components/chat/swarm-panel.test.ts +274 -0
- package/src/components/chat/swarm-panel.tsx +410 -0
- package/src/components/chat/swarm-status-card.tsx +346 -0
- package/src/components/chat/tool-call-bubble.tsx +48 -23
- package/src/components/chatrooms/chatroom-list.tsx +8 -5
- package/src/components/chatrooms/chatroom-message.tsx +10 -7
- package/src/components/chatrooms/chatroom-view.tsx +12 -9
- package/src/components/connectors/connector-health.tsx +6 -4
- package/src/components/connectors/connector-list.tsx +16 -11
- package/src/components/connectors/connector-sheet.tsx +12 -6
- package/src/components/home/home-view.tsx +38 -24
- package/src/components/input/chat-input.tsx +10 -1
- package/src/components/layout/app-layout.tsx +2 -38
- package/src/components/layout/sheet-layer.tsx +50 -0
- package/src/components/mcp-servers/mcp-server-list.tsx +37 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +12 -2
- package/src/components/plugins/plugin-list.tsx +8 -4
- package/src/components/plugins/plugin-sheet.tsx +2 -1
- package/src/components/providers/provider-list.tsx +3 -2
- package/src/components/providers/provider-sheet.tsx +2 -1
- package/src/components/runs/run-list.tsx +11 -7
- package/src/components/schedules/schedule-card.tsx +5 -3
- package/src/components/shared/agent-switch-dialog.tsx +1 -1
- package/src/components/shared/attachment-chip.tsx +19 -3
- package/src/components/shared/notification-center.tsx +6 -3
- package/src/components/shared/settings/plugin-manager.tsx +3 -2
- package/src/components/shared/settings/section-embedding.tsx +2 -1
- package/src/components/shared/settings/section-orchestrator.tsx +2 -1
- package/src/components/shared/settings/section-user-preferences.tsx +107 -0
- package/src/components/shared/settings/settings-page.tsx +13 -9
- package/src/components/skills/clawhub-browser.tsx +15 -4
- package/src/components/skills/skill-list.tsx +15 -4
- package/src/components/tasks/approvals-panel.tsx +2 -1
- package/src/components/tasks/task-board.tsx +35 -37
- package/src/components/tasks/task-sheet.tsx +4 -3
- package/src/components/ui/full-screen-loader.tsx +164 -0
- package/src/components/wallets/wallet-approval-dialog.tsx +2 -1
- package/src/components/wallets/wallet-panel.tsx +6 -5
- package/src/components/wallets/wallet-section.tsx +3 -2
- package/src/components/webhooks/webhook-list.tsx +4 -5
- package/src/components/webhooks/webhook-sheet.tsx +6 -6
- package/src/hooks/use-app-bootstrap.ts +202 -0
- package/src/hooks/use-mounted-ref.ts +14 -0
- package/src/hooks/use-now.ts +31 -0
- package/src/hooks/use-openclaw-gateway.ts +2 -1
- package/src/instrumentation.ts +20 -8
- package/src/lib/agent-default-tools.test.ts +52 -0
- package/src/lib/agent-default-tools.ts +40 -0
- package/src/lib/api-client.test.ts +21 -0
- package/src/lib/api-client.ts +6 -11
- package/src/lib/canvas-content.test.ts +360 -0
- package/src/lib/chat-streaming-state.test.ts +49 -2
- package/src/lib/chat-streaming-state.ts +26 -10
- package/src/lib/fetch-timeout.test.ts +54 -0
- package/src/lib/fetch-timeout.ts +60 -3
- package/src/lib/live-tool-events.test.ts +77 -0
- package/src/lib/live-tool-events.ts +73 -0
- package/src/lib/local-observability.test.ts +2 -2
- package/src/lib/openclaw-endpoint.test.ts +1 -1
- package/src/lib/providers/anthropic.ts +12 -16
- package/src/lib/providers/index.ts +4 -2
- package/src/lib/providers/ollama.ts +9 -6
- package/src/lib/providers/openai.ts +11 -14
- package/src/lib/runtime-env.test.ts +8 -8
- package/src/lib/schedule-dedupe-advanced.test.ts +2 -2
- package/src/lib/schedule-dedupe.test.ts +1 -1
- package/src/lib/schedule-dedupe.ts +3 -2
- package/src/lib/server/agent-thread-session.test.ts +6 -6
- package/src/lib/server/agent-thread-session.ts +6 -9
- package/src/lib/server/alert-dispatch.ts +2 -1
- package/src/lib/server/api-routes.test.ts +6 -6
- package/src/lib/server/approval-connector-notify.test.ts +4 -4
- package/src/lib/server/approvals-auto-approve.test.ts +29 -29
- package/src/lib/server/approvals.test.ts +317 -0
- package/src/lib/server/approvals.ts +5 -4
- package/src/lib/server/autonomy-runtime.test.ts +11 -11
- package/src/lib/server/browser-state.ts +2 -2
- package/src/lib/server/capability-router.test.ts +1 -1
- package/src/lib/server/capability-router.ts +3 -2
- package/src/lib/server/chat-execution-advanced.test.ts +15 -2
- package/src/lib/server/chat-execution-connector-delivery.ts +67 -0
- package/src/lib/server/chat-execution-disabled.test.ts +3 -3
- package/src/lib/server/chat-execution-eval-history.test.ts +3 -3
- package/src/lib/server/chat-execution-heartbeat.test.ts +42 -1
- package/src/lib/server/chat-execution-session-sync.test.ts +119 -0
- package/src/lib/server/chat-execution-tool-events.ts +116 -0
- package/src/lib/server/chat-execution-utils.test.ts +479 -0
- package/src/lib/server/chat-execution-utils.ts +533 -0
- package/src/lib/server/chat-execution.ts +153 -748
- package/src/lib/server/chat-streaming-utils.ts +174 -0
- package/src/lib/server/chat-turn-tool-routing.ts +310 -0
- package/src/lib/server/chatroom-session-persistence.test.ts +2 -2
- package/src/lib/server/clawhub-client.ts +2 -1
- package/src/lib/server/collection-helpers.test.ts +92 -0
- package/src/lib/server/collection-helpers.ts +25 -3
- package/src/lib/server/connectors/access.ts +146 -0
- package/src/lib/server/connectors/bluebubbles.test.ts +1 -1
- package/src/lib/server/connectors/bluebubbles.ts +4 -4
- package/src/lib/server/connectors/commands.ts +367 -0
- package/src/lib/server/connectors/connector-routing.test.ts +4 -4
- package/src/lib/server/connectors/delivery.ts +142 -0
- package/src/lib/server/connectors/discord.ts +37 -40
- package/src/lib/server/connectors/email.ts +11 -10
- package/src/lib/server/connectors/googlechat.ts +4 -4
- package/src/lib/server/connectors/inbound-audio-transcription.ts +2 -1
- package/src/lib/server/connectors/ingress-delivery.ts +23 -0
- package/src/lib/server/connectors/manager-roundtrip.test.ts +300 -0
- package/src/lib/server/connectors/manager.test.ts +352 -77
- package/src/lib/server/connectors/manager.ts +134 -673
- package/src/lib/server/connectors/matrix.ts +4 -4
- package/src/lib/server/connectors/message-sentinel.ts +7 -0
- package/src/lib/server/connectors/openclaw.test.ts +1 -1
- package/src/lib/server/connectors/openclaw.ts +8 -10
- package/src/lib/server/connectors/outbox.test.ts +192 -0
- package/src/lib/server/connectors/outbox.ts +369 -0
- package/src/lib/server/connectors/pairing.test.ts +18 -1
- package/src/lib/server/connectors/pairing.ts +49 -4
- package/src/lib/server/connectors/policy.ts +9 -3
- package/src/lib/server/connectors/reconnect-state.ts +71 -0
- package/src/lib/server/connectors/response-media.ts +256 -0
- package/src/lib/server/connectors/runtime-state.ts +67 -0
- package/src/lib/server/connectors/session.test.ts +357 -0
- package/src/lib/server/connectors/session.ts +422 -0
- package/src/lib/server/connectors/signal.ts +7 -7
- package/src/lib/server/connectors/slack.ts +43 -43
- package/src/lib/server/connectors/teams.ts +4 -4
- package/src/lib/server/connectors/telegram.ts +37 -43
- package/src/lib/server/connectors/types.ts +31 -1
- package/src/lib/server/connectors/whatsapp.test.ts +108 -0
- package/src/lib/server/connectors/whatsapp.ts +106 -34
- package/src/lib/server/context-manager.test.ts +409 -0
- package/src/lib/server/cost.test.ts +1 -1
- package/src/lib/server/daemon-policy.ts +78 -0
- package/src/lib/server/daemon-state-connectors.test.ts +167 -0
- package/src/lib/server/daemon-state.test.ts +283 -55
- package/src/lib/server/daemon-state.ts +106 -109
- package/src/lib/server/data-dir.test.ts +5 -5
- package/src/lib/server/data-dir.ts +4 -0
- package/src/lib/server/delegation-jobs-advanced.test.ts +1 -1
- package/src/lib/server/delegation-jobs.test.ts +87 -0
- package/src/lib/server/delegation-jobs.ts +42 -48
- package/src/lib/server/devserver-launch.ts +1 -1
- package/src/lib/server/document-utils.ts +7 -9
- package/src/lib/server/elevenlabs.ts +2 -1
- package/src/lib/server/embeddings.test.ts +105 -0
- package/src/lib/server/ethereum.ts +3 -2
- package/src/lib/server/eval/agent-regression.ts +3 -2
- package/src/lib/server/eval/runner.ts +2 -1
- package/src/lib/server/eval/scorer.ts +2 -1
- package/src/lib/server/evm-swap.ts +2 -1
- package/src/lib/server/gateway/protocol.test.ts +1 -1
- package/src/lib/server/guardian.ts +2 -1
- package/src/lib/server/heartbeat-blocked-suppression.test.ts +151 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +6 -6
- package/src/lib/server/heartbeat-service.test.ts +406 -0
- package/src/lib/server/heartbeat-service.ts +54 -7
- package/src/lib/server/heartbeat-wake.test.ts +19 -0
- package/src/lib/server/heartbeat-wake.ts +17 -16
- package/src/lib/server/integrity-monitor.test.ts +149 -0
- package/src/lib/server/json-utils.ts +22 -0
- package/src/lib/server/knowledge-db.test.ts +13 -13
- package/src/lib/server/link-understanding.ts +2 -1
- package/src/lib/server/llm-response-cache.test.ts +1 -1
- package/src/lib/server/main-agent-loop-advanced.test.ts +65 -3
- package/src/lib/server/main-agent-loop.test.ts +6 -6
- package/src/lib/server/main-agent-loop.ts +21 -7
- package/src/lib/server/mcp-client.test.ts +1 -1
- package/src/lib/server/mcp-conformance.test.ts +1 -1
- package/src/lib/server/mcp-conformance.ts +3 -2
- package/src/lib/server/memory-consolidation.ts +2 -1
- package/src/lib/server/memory-db.test.ts +485 -0
- package/src/lib/server/memory-db.ts +39 -26
- package/src/lib/server/memory-graph.test.ts +2 -2
- package/src/lib/server/memory-policy.test.ts +7 -7
- package/src/lib/server/memory-retrieval.test.ts +1 -1
- package/src/lib/server/openclaw-config-sync.ts +2 -1
- package/src/lib/server/openclaw-deploy.test.ts +1 -1
- package/src/lib/server/openclaw-deploy.ts +8 -12
- package/src/lib/server/openclaw-exec-config.ts +2 -1
- package/src/lib/server/openclaw-gateway.ts +6 -7
- package/src/lib/server/openclaw-skills-normalize.ts +2 -1
- package/src/lib/server/openclaw-sync.ts +7 -5
- package/src/lib/server/orchestrator-lg-structure.test.ts +17 -0
- package/src/lib/server/orchestrator-lg.ts +199 -327
- package/src/lib/server/path-utils.ts +31 -0
- package/src/lib/server/perf.ts +161 -0
- package/src/lib/server/plugins-approval-guidance.ts +115 -0
- package/src/lib/server/plugins.test.ts +1 -1
- package/src/lib/server/plugins.ts +22 -132
- package/src/lib/server/process-manager.ts +5 -8
- package/src/lib/server/provider-health.test.ts +137 -0
- package/src/lib/server/provider-health.ts +3 -3
- package/src/lib/server/provider-model-discovery.ts +3 -12
- package/src/lib/server/queue-followups.test.ts +9 -9
- package/src/lib/server/queue-reconcile.test.ts +2 -2
- package/src/lib/server/queue-recovery.test.ts +269 -0
- package/src/lib/server/queue.test.ts +570 -0
- package/src/lib/server/queue.ts +62 -455
- package/src/lib/server/resolve-image.ts +30 -0
- package/src/lib/server/runtime-settings.test.ts +4 -4
- package/src/lib/server/runtime-storage-write-paths.test.ts +60 -0
- package/src/lib/server/schedule-normalization.test.ts +279 -0
- package/src/lib/server/schedule-service.ts +263 -0
- package/src/lib/server/scheduler.ts +17 -74
- package/src/lib/server/session-mailbox.test.ts +191 -0
- package/src/lib/server/session-run-manager.test.ts +640 -0
- package/src/lib/server/session-run-manager.ts +59 -15
- package/src/lib/server/session-tools/autonomy-tools.test.ts +20 -20
- package/src/lib/server/session-tools/calendar.ts +2 -1
- package/src/lib/server/session-tools/canvas.ts +2 -1
- package/src/lib/server/session-tools/chatroom.ts +2 -1
- package/src/lib/server/session-tools/connector.ts +26 -28
- package/src/lib/server/session-tools/context-mgmt.ts +3 -2
- package/src/lib/server/session-tools/crawl.ts +4 -3
- package/src/lib/server/session-tools/crud.ts +105 -324
- package/src/lib/server/session-tools/delegate-fallback.test.ts +9 -9
- package/src/lib/server/session-tools/delegate.ts +6 -8
- package/src/lib/server/session-tools/discovery-approvals.test.ts +15 -15
- package/src/lib/server/session-tools/discovery.ts +4 -3
- package/src/lib/server/session-tools/document.ts +2 -1
- package/src/lib/server/session-tools/email.ts +2 -1
- package/src/lib/server/session-tools/extract.ts +2 -1
- package/src/lib/server/session-tools/file.ts +4 -3
- package/src/lib/server/session-tools/http.ts +2 -1
- package/src/lib/server/session-tools/human-loop.ts +2 -1
- package/src/lib/server/session-tools/image-gen.ts +4 -3
- package/src/lib/server/session-tools/index.ts +26 -30
- package/src/lib/server/session-tools/mailbox.ts +2 -1
- package/src/lib/server/session-tools/manage-connectors.test.ts +4 -4
- package/src/lib/server/session-tools/manage-schedules.test.ts +12 -12
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +5 -5
- package/src/lib/server/session-tools/manage-tasks.test.ts +2 -2
- package/src/lib/server/session-tools/monitor.ts +2 -1
- package/src/lib/server/session-tools/platform.ts +2 -1
- package/src/lib/server/session-tools/plugin-creator.ts +2 -1
- package/src/lib/server/session-tools/replicate.ts +3 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +6 -6
- package/src/lib/server/session-tools/shell.ts +4 -9
- package/src/lib/server/session-tools/subagent.ts +322 -170
- package/src/lib/server/session-tools/table.ts +6 -5
- package/src/lib/server/session-tools/wallet-tool.test.ts +3 -3
- package/src/lib/server/session-tools/wallet.ts +7 -6
- package/src/lib/server/session-tools/web-browser-config.test.ts +1 -0
- package/src/lib/server/session-tools/web-utils.ts +317 -0
- package/src/lib/server/session-tools/web.ts +62 -328
- package/src/lib/server/skill-prompt-budget.test.ts +1 -1
- package/src/lib/server/skills-normalize.ts +2 -1
- package/src/lib/server/storage-item-access.test.ts +302 -0
- package/src/lib/server/storage.ts +366 -314
- package/src/lib/server/stream-agent-chat.test.ts +82 -3
- package/src/lib/server/stream-agent-chat.ts +146 -510
- package/src/lib/server/stream-continuation.ts +412 -0
- package/src/lib/server/subagent-lineage.test.ts +647 -0
- package/src/lib/server/subagent-lineage.ts +435 -0
- package/src/lib/server/subagent-runtime.test.ts +484 -0
- package/src/lib/server/subagent-runtime.ts +419 -0
- package/src/lib/server/subagent-swarm.test.ts +391 -0
- package/src/lib/server/subagent-swarm.ts +564 -0
- package/src/lib/server/system-events.ts +3 -3
- package/src/lib/server/task-followups.test.ts +491 -0
- package/src/lib/server/task-followups.ts +391 -0
- package/src/lib/server/task-lifecycle.test.ts +205 -0
- package/src/lib/server/task-lifecycle.ts +200 -0
- package/src/lib/server/task-quality-gate.test.ts +1 -1
- package/src/lib/server/task-resume.ts +208 -0
- package/src/lib/server/task-service.test.ts +108 -0
- package/src/lib/server/task-service.ts +264 -0
- package/src/lib/server/task-validation.test.ts +1 -1
- package/src/lib/server/test-utils/run-with-temp-data-dir.ts +42 -0
- package/src/lib/server/tool-capability-policy.test.ts +2 -2
- package/src/lib/server/tool-capability-policy.ts +3 -2
- package/src/lib/server/tool-planning.ts +2 -1
- package/src/lib/server/tool-retry.ts +2 -3
- package/src/lib/server/wake-dispatcher.test.ts +303 -0
- package/src/lib/server/wake-dispatcher.ts +318 -0
- package/src/lib/server/wake-mode.test.ts +161 -0
- package/src/lib/server/wake-mode.ts +174 -0
- package/src/lib/server/wallet-service.ts +8 -9
- package/src/lib/server/watch-jobs.ts +2 -1
- package/src/lib/server/workspace-context.ts +2 -2
- package/src/lib/shared-utils.test.ts +142 -0
- package/src/lib/shared-utils.ts +62 -0
- package/src/lib/tool-event-summary.ts +2 -1
- package/src/lib/view-routes.test.ts +100 -0
- package/src/lib/wallet.test.ts +322 -6
- package/src/proxy.test.ts +4 -4
- package/src/proxy.ts +2 -3
- package/src/stores/set-if-changed.ts +40 -0
- package/src/stores/slices/agent-slice.ts +111 -0
- package/src/stores/slices/auth-slice.ts +25 -0
- package/src/stores/slices/data-slice.ts +301 -0
- package/src/stores/slices/index.ts +7 -0
- package/src/stores/slices/session-slice.ts +112 -0
- package/src/stores/slices/task-slice.ts +63 -0
- package/src/stores/slices/ui-slice.ts +192 -0
- package/src/stores/use-app-store.ts +17 -822
- package/src/stores/use-approval-store.ts +2 -1
- package/src/stores/use-chat-store.ts +8 -1
- package/src/types/index.ts +10 -0
package/src/lib/server/queue.ts
CHANGED
|
@@ -1,92 +1,49 @@
|
|
|
1
1
|
import { genId } from '@/lib/id'
|
|
2
|
+
import { dedup, hmrSingleton } from '@/lib/shared-utils'
|
|
2
3
|
import fs from 'node:fs'
|
|
3
4
|
import path from 'node:path'
|
|
4
|
-
import { loadTasks, saveTasks, loadQueue, saveQueue, loadAgents, loadSchedules, saveSchedules, loadSessions, saveSessions, loadSettings
|
|
5
|
+
import { loadTasks, saveTasks, loadQueue, saveQueue, loadAgents, loadSchedules, saveSchedules, loadSessions, saveSessions, loadSettings } from './storage'
|
|
5
6
|
import { notify } from './ws-hub'
|
|
7
|
+
import { perf } from './perf'
|
|
6
8
|
import { WORKSPACE_DIR } from './data-dir'
|
|
7
9
|
import { createOrchestratorSession } from './orchestrator'
|
|
8
|
-
import { formatValidationFailure
|
|
9
|
-
import { ensureTaskCompletionReport } from './task-reports'
|
|
10
|
+
import { formatValidationFailure } from './task-validation'
|
|
10
11
|
import { pushMainLoopEventToMainSessions } from './main-agent-loop'
|
|
11
12
|
import { executeSessionChatTurn } from './chat-execution'
|
|
12
13
|
import { extractTaskResult, formatResultBody } from './task-result'
|
|
14
|
+
import {
|
|
15
|
+
collectTaskConnectorFollowupTargets as collectTaskConnectorFollowupTargetsImpl,
|
|
16
|
+
extractLikelyOutputFiles,
|
|
17
|
+
isSendableAttachment,
|
|
18
|
+
maybeResolveUploadMediaPathFromUrl,
|
|
19
|
+
notifyConnectorTaskFollowups,
|
|
20
|
+
resolveExistingOutputFilePath,
|
|
21
|
+
resolveTaskOriginConnectorFollowupTarget as resolveTaskOriginConnectorFollowupTargetImpl,
|
|
22
|
+
type ScheduleTaskMeta,
|
|
23
|
+
type SessionLike,
|
|
24
|
+
type SessionMessageLike,
|
|
25
|
+
} from './task-followups'
|
|
13
26
|
import { getCheckpointSaver } from './langgraph-checkpoint'
|
|
14
27
|
import { cascadeUnblock } from './dag-validation'
|
|
15
28
|
import { performGuardianRollback } from './guardian'
|
|
16
29
|
import { shouldAutoDeleteScheduleAfterTerminalRun } from '@/lib/schedule-origin'
|
|
17
|
-
import type { Agent, BoardTask,
|
|
30
|
+
import type { Agent, BoardTask, Message, Session } from '@/types'
|
|
18
31
|
import { buildAgentDisabledMessage, isAgentDisabled } from './agent-availability'
|
|
32
|
+
import {
|
|
33
|
+
didTaskValidationChange,
|
|
34
|
+
markInvalidCompletedTaskFailed,
|
|
35
|
+
markValidatedTaskCompleted,
|
|
36
|
+
refreshTaskCompletionValidation,
|
|
37
|
+
} from './task-lifecycle'
|
|
19
38
|
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
interface SessionMessageLike {
|
|
24
|
-
role?: string
|
|
25
|
-
text?: string
|
|
26
|
-
time?: number
|
|
27
|
-
kind?: string
|
|
28
|
-
historyExcluded?: boolean
|
|
29
|
-
source?: {
|
|
30
|
-
connectorId?: string
|
|
31
|
-
channelId?: string
|
|
32
|
-
threadId?: string
|
|
33
|
-
}
|
|
34
|
-
toolEvents?: Array<{ name?: string; output?: string }>
|
|
35
|
-
streaming?: boolean
|
|
36
|
-
imageUrl?: string
|
|
37
|
-
}
|
|
39
|
+
export const collectTaskConnectorFollowupTargets = collectTaskConnectorFollowupTargetsImpl
|
|
40
|
+
export const resolveTaskOriginConnectorFollowupTarget = resolveTaskOriginConnectorFollowupTargetImpl
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
user?: string
|
|
42
|
-
cwd?: string
|
|
43
|
-
messages?: SessionMessageLike[]
|
|
44
|
-
connectorContext?: {
|
|
45
|
-
connectorId?: string | null
|
|
46
|
-
channelId?: string | null
|
|
47
|
-
threadId?: string | null
|
|
48
|
-
senderId?: string | null
|
|
49
|
-
senderName?: string | null
|
|
50
|
-
}
|
|
51
|
-
lastActiveAt?: number
|
|
52
|
-
heartbeatEnabled?: boolean | null
|
|
53
|
-
active?: boolean
|
|
54
|
-
currentRunId?: string | null
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
interface ScheduleTaskMeta extends BoardTask {
|
|
58
|
-
user?: string | null
|
|
59
|
-
createdInSessionId?: string | null
|
|
60
|
-
createdByAgentId?: string | null
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface RunningConnectorLike {
|
|
64
|
-
id: string
|
|
65
|
-
platform: string
|
|
66
|
-
agentId: string | null
|
|
67
|
-
supportsSend: boolean
|
|
68
|
-
configuredTargets: string[]
|
|
69
|
-
recentChannelId: string | null
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
interface ConnectorTaskFollowupTarget {
|
|
73
|
-
connectorId: string
|
|
74
|
-
channelId: string
|
|
75
|
-
threadId?: string | null
|
|
76
|
-
}
|
|
42
|
+
// HMR-safe: pin processing flag to globalThis so hot reloads don't reset it
|
|
43
|
+
const _queueState = hmrSingleton('__swarmclaw_queue__', () => ({ processing: false, pendingKick: false }))
|
|
77
44
|
|
|
78
45
|
const DISABLED_AGENT_RETRY_MS = 60_000
|
|
79
46
|
|
|
80
|
-
function sameReasons(a?: string[] | null, b?: string[] | null): boolean {
|
|
81
|
-
const av = Array.isArray(a) ? a : []
|
|
82
|
-
const bv = Array.isArray(b) ? b : []
|
|
83
|
-
if (av.length !== bv.length) return false
|
|
84
|
-
for (let i = 0; i < av.length; i++) {
|
|
85
|
-
if (av[i] !== bv[i]) return false
|
|
86
|
-
}
|
|
87
|
-
return true
|
|
88
|
-
}
|
|
89
|
-
|
|
90
47
|
function normalizeInt(value: unknown, fallback: number, min: number, max: number): number {
|
|
91
48
|
const parsed = typeof value === 'number'
|
|
92
49
|
? value
|
|
@@ -110,7 +67,7 @@ function deriveTaskRoutePreferences(task: BoardTask): {
|
|
|
110
67
|
preferredGatewayUseCase?: string | null
|
|
111
68
|
} {
|
|
112
69
|
const tags = Array.isArray(task.tags)
|
|
113
|
-
?
|
|
70
|
+
? dedup(task.tags.map((tag) => (typeof tag === 'string' ? tag.trim().toLowerCase() : '')).filter(Boolean))
|
|
114
71
|
: []
|
|
115
72
|
const customUseCase = typeof task.customFields?.openclawUseCase === 'string'
|
|
116
73
|
? task.customFields.openclawUseCase
|
|
@@ -561,267 +518,6 @@ function latestAssistantText(session: SessionLike | null | undefined): string {
|
|
|
561
518
|
return ''
|
|
562
519
|
}
|
|
563
520
|
|
|
564
|
-
function isEnabledFlag(value: unknown): boolean {
|
|
565
|
-
if (typeof value === 'boolean') return value
|
|
566
|
-
if (typeof value !== 'string') return false
|
|
567
|
-
const normalized = value.trim().toLowerCase()
|
|
568
|
-
return normalized === '1'
|
|
569
|
-
|| normalized === 'true'
|
|
570
|
-
|| normalized === 'yes'
|
|
571
|
-
|| normalized === 'on'
|
|
572
|
-
|| normalized === 'enabled'
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
function normalizeWhatsappTarget(raw: string): string {
|
|
576
|
-
const trimmed = raw.trim()
|
|
577
|
-
if (!trimmed) return trimmed
|
|
578
|
-
if (trimmed.includes('@')) return trimmed
|
|
579
|
-
let cleaned = trimmed.replace(/[^\d+]/g, '')
|
|
580
|
-
if (cleaned.startsWith('+')) cleaned = cleaned.slice(1)
|
|
581
|
-
if (cleaned.startsWith('0') && cleaned.length >= 10) {
|
|
582
|
-
cleaned = `44${cleaned.slice(1)}`
|
|
583
|
-
}
|
|
584
|
-
cleaned = cleaned.replace(/[^\d]/g, '')
|
|
585
|
-
return cleaned ? `${cleaned}@s.whatsapp.net` : trimmed
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
function fillTaskFollowupTemplate(template: string, data: {
|
|
589
|
-
status: string
|
|
590
|
-
title: string
|
|
591
|
-
summary: string
|
|
592
|
-
taskId: string
|
|
593
|
-
}): string {
|
|
594
|
-
return template
|
|
595
|
-
.replaceAll('{status}', data.status)
|
|
596
|
-
.replaceAll('{title}', data.title)
|
|
597
|
-
.replaceAll('{summary}', data.summary)
|
|
598
|
-
.replaceAll('{taskId}', data.taskId)
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function maybeResolveUploadMediaPathFromUrl(url: string | undefined): string | undefined {
|
|
602
|
-
if (!url || !url.startsWith('/api/uploads/')) return undefined
|
|
603
|
-
const rawName = url.slice('/api/uploads/'.length).split(/[?#]/)[0] || ''
|
|
604
|
-
let decoded: string
|
|
605
|
-
try { decoded = decodeURIComponent(rawName) } catch { decoded = rawName }
|
|
606
|
-
const safeName = decoded.replace(/[^a-zA-Z0-9._-]/g, '')
|
|
607
|
-
if (!safeName) return undefined
|
|
608
|
-
const fullPath = path.join(UPLOAD_DIR, safeName)
|
|
609
|
-
return fs.existsSync(fullPath) ? fullPath : undefined
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
const OUTPUT_FILE_BACKTICK_RE = /`([^`\n]+\.(?:txt|md|json|csv|pdf|png|jpe?g|webp|gif|svg|mp4|webm|mov|zip|tar|gz|log|yml|yaml|xml|html|css|js|ts|tsx|jsx|py|go|rs|java|swift|kt|sql))`/gi
|
|
613
|
-
const OUTPUT_FILE_PATH_RE = /\b((?:\.{1,2}\/|~\/|\/)?[\w./-]+\.(?:txt|md|json|csv|pdf|png|jpe?g|webp|gif|svg|mp4|webm|mov|zip|tar|gz|log|yml|yaml|xml|html|css|js|ts|tsx|jsx|py|go|rs|java|swift|kt|sql))\b/gi
|
|
614
|
-
const MAX_CONNECTOR_ATTACHMENT_BYTES = 25 * 1024 * 1024
|
|
615
|
-
|
|
616
|
-
function extractLikelyOutputFiles(text: string): string[] {
|
|
617
|
-
const out: string[] = []
|
|
618
|
-
const seen = new Set<string>()
|
|
619
|
-
const push = (raw: string) => {
|
|
620
|
-
const value = raw.trim().replace(/^['"]|['"]$/g, '')
|
|
621
|
-
if (!value || /^https?:\/\//i.test(value)) return
|
|
622
|
-
if (value.startsWith('/api/uploads/')) return
|
|
623
|
-
const key = value.toLowerCase()
|
|
624
|
-
if (seen.has(key)) return
|
|
625
|
-
seen.add(key)
|
|
626
|
-
out.push(value)
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
for (const match of text.matchAll(OUTPUT_FILE_BACKTICK_RE)) {
|
|
630
|
-
push(match[1] || '')
|
|
631
|
-
if (out.length >= 8) return out
|
|
632
|
-
}
|
|
633
|
-
for (const match of text.matchAll(OUTPUT_FILE_PATH_RE)) {
|
|
634
|
-
push(match[1] || '')
|
|
635
|
-
if (out.length >= 8) return out
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
return out
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
function resolveExistingOutputFilePath(fileRef: string, cwd: string): string | null {
|
|
642
|
-
const ref = (fileRef || '').trim()
|
|
643
|
-
if (!ref) return null
|
|
644
|
-
if (ref.startsWith('/api/uploads/')) {
|
|
645
|
-
return maybeResolveUploadMediaPathFromUrl(ref) || null
|
|
646
|
-
}
|
|
647
|
-
const withoutFileScheme = ref.replace(/^file:\/\//i, '')
|
|
648
|
-
const candidates = path.isAbsolute(withoutFileScheme)
|
|
649
|
-
? [withoutFileScheme]
|
|
650
|
-
: [
|
|
651
|
-
cwd ? path.resolve(cwd, withoutFileScheme) : '',
|
|
652
|
-
path.resolve(WORKSPACE_DIR, withoutFileScheme),
|
|
653
|
-
].filter(Boolean)
|
|
654
|
-
|
|
655
|
-
for (const candidate of candidates) {
|
|
656
|
-
try {
|
|
657
|
-
const stat = fs.statSync(candidate)
|
|
658
|
-
if (stat.isFile()) return candidate
|
|
659
|
-
} catch {
|
|
660
|
-
// ignore missing candidate
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
return null
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
function isSendableAttachment(filePath: string): boolean {
|
|
667
|
-
try {
|
|
668
|
-
const stat = fs.statSync(filePath)
|
|
669
|
-
return stat.isFile() && stat.size <= MAX_CONNECTOR_ATTACHMENT_BYTES
|
|
670
|
-
} catch {
|
|
671
|
-
return false
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
export function resolveTaskOriginConnectorFollowupTarget(params: {
|
|
676
|
-
task: BoardTask
|
|
677
|
-
sessions: Record<string, SessionLike>
|
|
678
|
-
connectors: Record<string, Connector>
|
|
679
|
-
running: RunningConnectorLike[]
|
|
680
|
-
}): ConnectorTaskFollowupTarget | null {
|
|
681
|
-
const { task, sessions, connectors, running } = params
|
|
682
|
-
const metaTask = task as ScheduleTaskMeta
|
|
683
|
-
const delegatedByAgentId = typeof metaTask.delegatedByAgentId === 'string'
|
|
684
|
-
? metaTask.delegatedByAgentId.trim()
|
|
685
|
-
: ''
|
|
686
|
-
const allowedOwners = new Set([task.agentId, delegatedByAgentId].filter(Boolean))
|
|
687
|
-
const sourceSessionId = typeof metaTask.createdInSessionId === 'string'
|
|
688
|
-
? metaTask.createdInSessionId.trim()
|
|
689
|
-
: ''
|
|
690
|
-
|
|
691
|
-
const runningById = new Map<string, RunningConnectorLike>()
|
|
692
|
-
for (const entry of running) {
|
|
693
|
-
if (!entry?.id) continue
|
|
694
|
-
runningById.set(entry.id, entry)
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
const normalizeTarget = (raw: {
|
|
698
|
-
connectorId?: string | null
|
|
699
|
-
channelId?: string | null
|
|
700
|
-
threadId?: string | null
|
|
701
|
-
}): ConnectorTaskFollowupTarget | null => {
|
|
702
|
-
const connectorId = typeof raw.connectorId === 'string' ? raw.connectorId.trim() : ''
|
|
703
|
-
if (!connectorId) return null
|
|
704
|
-
const connector = connectors[connectorId]
|
|
705
|
-
if (!connector) return null
|
|
706
|
-
const ownerId = typeof connector.agentId === 'string' ? connector.agentId.trim() : ''
|
|
707
|
-
if (ownerId && !allowedOwners.has(ownerId)) return null
|
|
708
|
-
|
|
709
|
-
const runtime = runningById.get(connectorId)
|
|
710
|
-
if (runtime && !runtime.supportsSend) return null
|
|
711
|
-
|
|
712
|
-
const channelId = typeof raw.channelId === 'string' ? raw.channelId.trim() : ''
|
|
713
|
-
if (!channelId) return null
|
|
714
|
-
const normalizedChannelId = connector.platform === 'whatsapp'
|
|
715
|
-
? normalizeWhatsappTarget(channelId)
|
|
716
|
-
: channelId
|
|
717
|
-
const threadId = typeof raw.threadId === 'string' ? raw.threadId.trim() : ''
|
|
718
|
-
return {
|
|
719
|
-
connectorId,
|
|
720
|
-
channelId: normalizedChannelId,
|
|
721
|
-
...(threadId ? { threadId } : {}),
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
const explicitTarget = normalizeTarget({
|
|
726
|
-
connectorId: typeof metaTask.followupConnectorId === 'string' ? metaTask.followupConnectorId : null,
|
|
727
|
-
channelId: typeof metaTask.followupChannelId === 'string' ? metaTask.followupChannelId : null,
|
|
728
|
-
threadId: typeof metaTask.followupThreadId === 'string' ? metaTask.followupThreadId : null,
|
|
729
|
-
})
|
|
730
|
-
if (explicitTarget) return explicitTarget
|
|
731
|
-
|
|
732
|
-
if (!sourceSessionId) return null
|
|
733
|
-
const sourceSession = sessions[sourceSessionId]
|
|
734
|
-
if (!sourceSession) return null
|
|
735
|
-
|
|
736
|
-
const sessionContextTarget = normalizeTarget({
|
|
737
|
-
connectorId: typeof sourceSession.connectorContext?.connectorId === 'string'
|
|
738
|
-
? sourceSession.connectorContext.connectorId
|
|
739
|
-
: null,
|
|
740
|
-
channelId: typeof sourceSession.connectorContext?.channelId === 'string'
|
|
741
|
-
? sourceSession.connectorContext.channelId
|
|
742
|
-
: null,
|
|
743
|
-
threadId: typeof sourceSession.connectorContext?.threadId === 'string'
|
|
744
|
-
? sourceSession.connectorContext.threadId
|
|
745
|
-
: null,
|
|
746
|
-
})
|
|
747
|
-
if (sessionContextTarget) return sessionContextTarget
|
|
748
|
-
|
|
749
|
-
if (!Array.isArray(sourceSession.messages)) return null
|
|
750
|
-
|
|
751
|
-
for (let i = sourceSession.messages.length - 1; i >= 0; i--) {
|
|
752
|
-
const message = sourceSession.messages[i]
|
|
753
|
-
if (!message || message.role !== 'user') continue
|
|
754
|
-
if (message.historyExcluded === true) continue
|
|
755
|
-
|
|
756
|
-
const connectorId = typeof message.source?.connectorId === 'string'
|
|
757
|
-
? message.source.connectorId.trim()
|
|
758
|
-
: ''
|
|
759
|
-
if (!connectorId) continue
|
|
760
|
-
|
|
761
|
-
const connector = connectors[connectorId]
|
|
762
|
-
if (!connector) continue
|
|
763
|
-
const runtime = runningById.get(connectorId)
|
|
764
|
-
const sourceChannel = typeof message.source?.channelId === 'string'
|
|
765
|
-
? message.source.channelId.trim()
|
|
766
|
-
: ''
|
|
767
|
-
const fallbackChannel = runtime?.recentChannelId
|
|
768
|
-
|| runtime?.configuredTargets?.[0]
|
|
769
|
-
|| connector.config?.outboundJid
|
|
770
|
-
|| connector.config?.outboundTarget
|
|
771
|
-
|| ''
|
|
772
|
-
const target = normalizeTarget({
|
|
773
|
-
connectorId,
|
|
774
|
-
channelId: sourceChannel || fallbackChannel,
|
|
775
|
-
threadId: typeof message.source?.threadId === 'string' ? message.source.threadId : null,
|
|
776
|
-
})
|
|
777
|
-
if (target) return target
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
return null
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
export function collectTaskConnectorFollowupTargets(params: {
|
|
784
|
-
task: BoardTask
|
|
785
|
-
sessions: Record<string, SessionLike>
|
|
786
|
-
connectors: Record<string, Connector>
|
|
787
|
-
running: RunningConnectorLike[]
|
|
788
|
-
}): ConnectorTaskFollowupTarget[] {
|
|
789
|
-
const { task, sessions, connectors, running } = params
|
|
790
|
-
const originTarget = resolveTaskOriginConnectorFollowupTarget({ task, sessions, connectors, running })
|
|
791
|
-
if (originTarget) return [originTarget]
|
|
792
|
-
|
|
793
|
-
const targets: ConnectorTaskFollowupTarget[] = []
|
|
794
|
-
const seen = new Set<string>()
|
|
795
|
-
const pushTarget = (target: ConnectorTaskFollowupTarget | null | undefined) => {
|
|
796
|
-
if (!target?.connectorId || !target?.channelId) return
|
|
797
|
-
const key = `${target.connectorId}|${target.channelId}|${target.threadId || ''}`
|
|
798
|
-
if (seen.has(key)) return
|
|
799
|
-
seen.add(key)
|
|
800
|
-
targets.push(target)
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
for (const entry of running) {
|
|
804
|
-
if (!entry.supportsSend || !entry.id) continue
|
|
805
|
-
const connector = connectors[entry.id]
|
|
806
|
-
if (!connector) continue
|
|
807
|
-
if (connector.agentId !== task.agentId) continue
|
|
808
|
-
if (!isEnabledFlag(connector.config?.taskFollowups)) continue
|
|
809
|
-
const channelTargetRaw = entry.configuredTargets[0]
|
|
810
|
-
|| connector.config?.outboundJid
|
|
811
|
-
|| connector.config?.outboundTarget
|
|
812
|
-
|| ''
|
|
813
|
-
if (!channelTargetRaw) continue
|
|
814
|
-
pushTarget({
|
|
815
|
-
connectorId: entry.id,
|
|
816
|
-
channelId: connector.platform === 'whatsapp'
|
|
817
|
-
? normalizeWhatsappTarget(channelTargetRaw)
|
|
818
|
-
: channelTargetRaw,
|
|
819
|
-
})
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
return targets
|
|
823
|
-
}
|
|
824
|
-
|
|
825
521
|
// Task result extraction now uses Zod-validated structured data
|
|
826
522
|
// from ./task-result.ts (extractTaskResult, formatResultBody)
|
|
827
523
|
|
|
@@ -906,7 +602,13 @@ export function reconcileFinishedRunningTasks(): { reconciled: number; deadLette
|
|
|
906
602
|
if (!hasFinishedExecutionSession(session)) continue
|
|
907
603
|
|
|
908
604
|
const fallbackText = latestAssistantText(session)
|
|
909
|
-
if (!fallbackText && !task.result)
|
|
605
|
+
if (!fallbackText && !task.result) {
|
|
606
|
+
task.status = 'failed'
|
|
607
|
+
task.result = 'Agent session finished without producing output.'
|
|
608
|
+
task.updatedAt = now
|
|
609
|
+
tasksDirty = true
|
|
610
|
+
continue
|
|
611
|
+
}
|
|
910
612
|
|
|
911
613
|
applyTaskPolicyDefaults(task)
|
|
912
614
|
const taskResult = extractTaskResult(
|
|
@@ -919,18 +621,13 @@ export function reconcileFinishedRunningTasks(): { reconciled: number; deadLette
|
|
|
919
621
|
task.artifacts = taskResult.artifacts.slice(0, 24)
|
|
920
622
|
task.outputFiles = extractLikelyOutputFiles(enrichedResult).slice(0, 24)
|
|
921
623
|
task.updatedAt = now
|
|
922
|
-
const
|
|
923
|
-
if (report?.relativePath) task.completionReportPath = report.relativePath
|
|
924
|
-
const validation = validateTaskCompletion(task, { report, settings })
|
|
925
|
-
task.validation = validation
|
|
624
|
+
const { validation } = refreshTaskCompletionValidation(task, settings)
|
|
926
625
|
if (!task.comments) task.comments = []
|
|
927
626
|
|
|
928
627
|
if (validation.ok) {
|
|
929
|
-
task
|
|
930
|
-
task.completedAt = now
|
|
628
|
+
markValidatedTaskCompleted(task, { now })
|
|
931
629
|
task.retryScheduledAt = null
|
|
932
630
|
task.deadLetteredAt = null
|
|
933
|
-
task.error = null
|
|
934
631
|
task.checkpoint = {
|
|
935
632
|
...(task.checkpoint || {}),
|
|
936
633
|
lastRunId: sessionId,
|
|
@@ -1057,7 +754,7 @@ function notifyMainChatScheduleResult(task: BoardTask): void {
|
|
|
1057
754
|
const agents = loadAgents()
|
|
1058
755
|
const agent = agents[task.agentId]
|
|
1059
756
|
if (agent?.threadSessionId && sessions[agent.threadSessionId]) {
|
|
1060
|
-
const thread = sessions[agent.threadSessionId] as SessionLike
|
|
757
|
+
const thread = sessions[(agent as any).threadSessionId] as SessionLike
|
|
1061
758
|
const threadLast = Array.isArray(thread.messages) ? thread.messages.at(-1) : null
|
|
1062
759
|
if (!(threadLast?.role === 'assistant' && threadLast?.text === body && typeof threadLast?.time === 'number' && now - threadLast.time < 30_000)) {
|
|
1063
760
|
if (!Array.isArray(thread.messages)) thread.messages = []
|
|
@@ -1080,91 +777,13 @@ function cleanupTerminalOneOffSchedule(task: BoardTask): void {
|
|
|
1080
777
|
|
|
1081
778
|
const schedules = loadSchedules()
|
|
1082
779
|
const schedule = schedules[scheduleId]
|
|
1083
|
-
if (!shouldAutoDeleteScheduleAfterTerminalRun(schedule)) return
|
|
780
|
+
if (!shouldAutoDeleteScheduleAfterTerminalRun(schedule as any)) return
|
|
1084
781
|
|
|
1085
782
|
delete schedules[scheduleId]
|
|
1086
783
|
saveSchedules(schedules)
|
|
1087
784
|
notify('schedules')
|
|
1088
785
|
}
|
|
1089
786
|
|
|
1090
|
-
async function notifyConnectorTaskFollowups(params: {
|
|
1091
|
-
task: BoardTask
|
|
1092
|
-
statusLabel: string
|
|
1093
|
-
summaryText: string
|
|
1094
|
-
imageUrl?: string
|
|
1095
|
-
mediaPath?: string
|
|
1096
|
-
mediaFileName?: string
|
|
1097
|
-
}) {
|
|
1098
|
-
const { task, statusLabel, summaryText, imageUrl, mediaPath, mediaFileName } = params
|
|
1099
|
-
|
|
1100
|
-
const connectors = loadConnectors()
|
|
1101
|
-
const running = (await import('./connectors/manager')).listRunningConnectors()
|
|
1102
|
-
const manager = await import('./connectors/manager')
|
|
1103
|
-
const sessions = loadSessions()
|
|
1104
|
-
const targets = collectTaskConnectorFollowupTargets({
|
|
1105
|
-
task,
|
|
1106
|
-
sessions: sessions as Record<string, SessionLike>,
|
|
1107
|
-
connectors,
|
|
1108
|
-
running: running as RunningConnectorLike[],
|
|
1109
|
-
})
|
|
1110
|
-
if (!targets.length) return
|
|
1111
|
-
const originTarget = resolveTaskOriginConnectorFollowupTarget({
|
|
1112
|
-
task,
|
|
1113
|
-
sessions: sessions as Record<string, SessionLike>,
|
|
1114
|
-
connectors,
|
|
1115
|
-
running: running as RunningConnectorLike[],
|
|
1116
|
-
})
|
|
1117
|
-
const preferredTargetKey = originTarget
|
|
1118
|
-
? `${originTarget.connectorId}|${originTarget.channelId}|${originTarget.threadId || ''}`
|
|
1119
|
-
: ''
|
|
1120
|
-
|
|
1121
|
-
const summary = summaryText.trim().slice(0, 1400)
|
|
1122
|
-
for (const target of targets) {
|
|
1123
|
-
const connector = connectors[target.connectorId]
|
|
1124
|
-
if (!connector) continue
|
|
1125
|
-
|
|
1126
|
-
const template = typeof connector.config?.taskFollowupTemplate === 'string'
|
|
1127
|
-
? connector.config.taskFollowupTemplate.trim()
|
|
1128
|
-
: ''
|
|
1129
|
-
const message = template
|
|
1130
|
-
? fillTaskFollowupTemplate(template, {
|
|
1131
|
-
status: statusLabel,
|
|
1132
|
-
title: task.title || task.id,
|
|
1133
|
-
summary,
|
|
1134
|
-
taskId: task.id,
|
|
1135
|
-
})
|
|
1136
|
-
: [
|
|
1137
|
-
`Task ${statusLabel}: ${task.title}`,
|
|
1138
|
-
summary || 'No summary provided.',
|
|
1139
|
-
].join('\n\n')
|
|
1140
|
-
const targetKey = `${target.connectorId}|${target.channelId}|${target.threadId || ''}`
|
|
1141
|
-
const preferredChannelNote = !template && preferredTargetKey && targetKey === preferredTargetKey
|
|
1142
|
-
? '\n\n(Update sent in the same channel that requested this task.)'
|
|
1143
|
-
: ''
|
|
1144
|
-
const outboundMessage = `${message}${preferredChannelNote}`
|
|
1145
|
-
|
|
1146
|
-
const resolvedMediaPath = mediaPath || maybeResolveUploadMediaPathFromUrl(imageUrl)
|
|
1147
|
-
try {
|
|
1148
|
-
await manager.sendConnectorMessage({
|
|
1149
|
-
connectorId: target.connectorId,
|
|
1150
|
-
channelId: target.channelId,
|
|
1151
|
-
threadId: target.threadId || undefined,
|
|
1152
|
-
text: outboundMessage,
|
|
1153
|
-
...(resolvedMediaPath
|
|
1154
|
-
? {
|
|
1155
|
-
mediaPath: resolvedMediaPath,
|
|
1156
|
-
fileName: mediaFileName || path.basename(resolvedMediaPath),
|
|
1157
|
-
caption: outboundMessage,
|
|
1158
|
-
}
|
|
1159
|
-
: {}),
|
|
1160
|
-
})
|
|
1161
|
-
} catch (err: unknown) {
|
|
1162
|
-
const errMsg = err instanceof Error ? err.message : String(err)
|
|
1163
|
-
console.warn(`[queue] Failed task follow-up send on connector ${target.connectorId}: ${errMsg}`)
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
787
|
/**
|
|
1169
788
|
* Notify agent thread sessions when a task completes or fails.
|
|
1170
789
|
* - Always pushes to the executing agent's thread
|
|
@@ -1213,9 +832,9 @@ function notifyAgentThreadTaskResult(task: BoardTask): void {
|
|
|
1213
832
|
// Get working directory from execution session
|
|
1214
833
|
const execCwd = runSession?.cwd || ''
|
|
1215
834
|
const existingOutputPaths = outputFileRefs
|
|
1216
|
-
.map((fileRef) => resolveExistingOutputFilePath(fileRef, execCwd))
|
|
1217
|
-
.filter((candidate): candidate is string => Boolean(candidate))
|
|
1218
|
-
const firstLocalOutputPath = existingOutputPaths.find((candidate) => isSendableAttachment(candidate))
|
|
835
|
+
.map((fileRef: string) => resolveExistingOutputFilePath(fileRef, execCwd))
|
|
836
|
+
.filter((candidate: any): candidate is string => Boolean(candidate))
|
|
837
|
+
const firstLocalOutputPath = existingOutputPaths.find((candidate: string) => isSendableAttachment(candidate))
|
|
1219
838
|
const followupMediaPath = firstArtifactMediaPath || firstLocalOutputPath || undefined
|
|
1220
839
|
|
|
1221
840
|
const buildMsg = (text: string): Message => {
|
|
@@ -1228,7 +847,7 @@ function notifyAgentThreadTaskResult(task: BoardTask): void {
|
|
|
1228
847
|
const parts = [prefix]
|
|
1229
848
|
if (execCwd) parts.push(`Working directory: \`${execCwd}\``)
|
|
1230
849
|
if (outputFileRefs.length > 0) {
|
|
1231
|
-
parts.push(`Output files:\n${outputFileRefs.slice(0, 8).map((fileRef) => `- \`${fileRef}\``).join('\n')}`)
|
|
850
|
+
parts.push(`Output files:\n${outputFileRefs.slice(0, 8).map((fileRef: string) => `- \`${fileRef}\``).join('\n')}`)
|
|
1232
851
|
}
|
|
1233
852
|
if (task.completionReportPath) parts.push(`Task report: \`${task.completionReportPath}\``)
|
|
1234
853
|
if (resumeLines.length > 0) parts.push(resumeLines.join(' | '))
|
|
@@ -1238,7 +857,7 @@ function notifyAgentThreadTaskResult(task: BoardTask): void {
|
|
|
1238
857
|
|
|
1239
858
|
// 1. Push to executing agent's thread
|
|
1240
859
|
if (agent?.threadSessionId && sessions[agent.threadSessionId]) {
|
|
1241
|
-
const thread = sessions[agent.threadSessionId]
|
|
860
|
+
const thread = sessions[(agent as any).threadSessionId]
|
|
1242
861
|
if (!Array.isArray(thread.messages)) thread.messages = []
|
|
1243
862
|
const body = buildResultBlock(`Task ${statusLabel}: **${taskLink}**`)
|
|
1244
863
|
thread.messages.push(buildMsg(body))
|
|
@@ -1356,42 +975,32 @@ export function validateCompletedTasksQueue() {
|
|
|
1356
975
|
if (task.status !== 'completed') continue
|
|
1357
976
|
checked++
|
|
1358
977
|
|
|
1359
|
-
const
|
|
1360
|
-
|
|
1361
|
-
|
|
978
|
+
const previousValidation = task.validation || null
|
|
979
|
+
const previousReportPath = task.completionReportPath || null
|
|
980
|
+
const { validation } = refreshTaskCompletionValidation(task, settings)
|
|
981
|
+
if (task.completionReportPath !== previousReportPath) {
|
|
1362
982
|
tasksDirty = true
|
|
1363
983
|
}
|
|
1364
|
-
|
|
1365
|
-
const validation = validateTaskCompletion(task, { report, settings })
|
|
1366
|
-
const prevValidation = task.validation || null
|
|
1367
|
-
const validationChanged = !prevValidation
|
|
1368
|
-
|| prevValidation.ok !== validation.ok
|
|
1369
|
-
|| !sameReasons(prevValidation.reasons, validation.reasons)
|
|
984
|
+
const validationChanged = didTaskValidationChange(previousValidation, validation)
|
|
1370
985
|
|
|
1371
986
|
if (validationChanged) {
|
|
1372
|
-
task.validation = validation
|
|
1373
987
|
tasksDirty = true
|
|
1374
988
|
}
|
|
1375
989
|
|
|
1376
990
|
if (validation.ok) {
|
|
1377
991
|
if (!task.completedAt) {
|
|
1378
|
-
task
|
|
1379
|
-
task.updatedAt = now
|
|
992
|
+
markValidatedTaskCompleted(task, { now, preserveCompletedAt: true })
|
|
1380
993
|
tasksDirty = true
|
|
1381
994
|
}
|
|
1382
995
|
continue
|
|
1383
996
|
}
|
|
1384
997
|
|
|
1385
|
-
task
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
id: genId(),
|
|
1392
|
-
author: 'System',
|
|
1393
|
-
text: `Task auto-failed completed-queue validation.\n\n${validation.reasons.map((r) => `- ${r}`).join('\n')}`,
|
|
1394
|
-
createdAt: now,
|
|
998
|
+
markInvalidCompletedTaskFailed(task, validation, {
|
|
999
|
+
now,
|
|
1000
|
+
comment: {
|
|
1001
|
+
author: 'System',
|
|
1002
|
+
text: `Task auto-failed completed-queue validation.\n\n${validation.reasons.map((r) => `- ${r}`).join('\n')}`,
|
|
1003
|
+
},
|
|
1395
1004
|
})
|
|
1396
1005
|
tasksDirty = true
|
|
1397
1006
|
demoted++
|
|
@@ -1502,6 +1111,7 @@ export function dequeueNextRunnableTask(queue: string[], tasks: Record<string, B
|
|
|
1502
1111
|
export async function processNext() {
|
|
1503
1112
|
if (_queueState.processing) return
|
|
1504
1113
|
_queueState.processing = true
|
|
1114
|
+
const endQueuePerf = perf.start('queue', 'processNext')
|
|
1505
1115
|
|
|
1506
1116
|
try {
|
|
1507
1117
|
// Recover orphaned tasks: status is 'queued' but missing from the queue array
|
|
@@ -1722,7 +1332,9 @@ export async function processNext() {
|
|
|
1722
1332
|
console.log(`[queue] Running task "${task.title}" (${taskId}) with ${agent.name}`)
|
|
1723
1333
|
|
|
1724
1334
|
try {
|
|
1335
|
+
const endTaskRunPerf = perf.start('queue', 'executeTaskRun', { taskId, agentName: agent.name })
|
|
1725
1336
|
const result = await executeTaskRun(task, agent, sessionId)
|
|
1337
|
+
endTaskRunPerf()
|
|
1726
1338
|
const t2 = loadTasks()
|
|
1727
1339
|
const settings = loadSettings()
|
|
1728
1340
|
if (t2[taskId]) {
|
|
@@ -1739,20 +1351,15 @@ export async function processNext() {
|
|
|
1739
1351
|
t2[taskId].artifacts = taskResult.artifacts.slice(0, 24)
|
|
1740
1352
|
t2[taskId].outputFiles = extractLikelyOutputFiles(enrichedResult).slice(0, 24)
|
|
1741
1353
|
t2[taskId].updatedAt = Date.now()
|
|
1742
|
-
const
|
|
1743
|
-
if (report?.relativePath) t2[taskId].completionReportPath = report.relativePath
|
|
1744
|
-
const validation = validateTaskCompletion(t2[taskId], { report, settings })
|
|
1745
|
-
t2[taskId].validation = validation
|
|
1354
|
+
const { validation } = refreshTaskCompletionValidation(t2[taskId], settings)
|
|
1746
1355
|
|
|
1747
1356
|
const now = Date.now()
|
|
1748
1357
|
// Add a completion/failure comment from the orchestrator.
|
|
1749
1358
|
if (!t2[taskId].comments) t2[taskId].comments = []
|
|
1750
1359
|
|
|
1751
1360
|
if (validation.ok) {
|
|
1752
|
-
t2[taskId]
|
|
1753
|
-
t2[taskId].completedAt = now
|
|
1361
|
+
markValidatedTaskCompleted(t2[taskId], { now })
|
|
1754
1362
|
t2[taskId].retryScheduledAt = null
|
|
1755
|
-
t2[taskId].error = null
|
|
1756
1363
|
t2[taskId].checkpoint = {
|
|
1757
1364
|
...(t2[taskId].checkpoint || {}),
|
|
1758
1365
|
lastRunId: sessionId,
|
|
@@ -1918,6 +1525,7 @@ export async function processNext() {
|
|
|
1918
1525
|
}
|
|
1919
1526
|
}
|
|
1920
1527
|
} finally {
|
|
1528
|
+
endQueuePerf()
|
|
1921
1529
|
_queueState.processing = false
|
|
1922
1530
|
// If tasks were enqueued while we were processing, kick another round
|
|
1923
1531
|
if (_queueState.pendingKick) {
|
|
@@ -1994,7 +1602,6 @@ export function recoverStalledRunningTasks(): { recovered: number; deadLettered:
|
|
|
1994
1602
|
disableSessionHeartbeat(task.sessionId)
|
|
1995
1603
|
changed = true
|
|
1996
1604
|
if (state === 'retry') {
|
|
1997
|
-
task.retryScheduledAt = Date.now() + 30_000
|
|
1998
1605
|
pushQueueUnique(queue, task.id)
|
|
1999
1606
|
recovered++
|
|
2000
1607
|
pushMainLoopEventToMainSessions({
|