@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
|
@@ -4,6 +4,7 @@ import type { Plugin, PluginHooks } from '@/types'
|
|
|
4
4
|
import { getPluginManager } from '../plugins'
|
|
5
5
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
6
6
|
import type { ToolBuildContext } from './context'
|
|
7
|
+
import { errorMessage, sleep } from '@/lib/shared-utils'
|
|
7
8
|
|
|
8
9
|
interface ReplicateConfig {
|
|
9
10
|
apiToken: string
|
|
@@ -71,7 +72,7 @@ function formatPrediction(p: Record<string, unknown>): Record<string, unknown> {
|
|
|
71
72
|
async function pollPrediction(token: string, predictionId: string, cfg: ReplicateConfig): Promise<Record<string, unknown>> {
|
|
72
73
|
const deadline = Date.now() + cfg.timeoutMs
|
|
73
74
|
while (Date.now() < deadline) {
|
|
74
|
-
await
|
|
75
|
+
await sleep(cfg.pollingIntervalMs)
|
|
75
76
|
const r = await replicateRequest('GET', `/predictions/${predictionId}`, token)
|
|
76
77
|
if (!r.ok) return { status: 'failed', error: r.error }
|
|
77
78
|
const prediction = r.data as Record<string, unknown>
|
|
@@ -206,7 +207,7 @@ async function executeReplicate(args: Record<string, unknown>): Promise<string>
|
|
|
206
207
|
return `Error: Unknown action "${action}". Use: run, get, cancel, get_model, search, status.`
|
|
207
208
|
}
|
|
208
209
|
} catch (err: unknown) {
|
|
209
|
-
return `Error: ${
|
|
210
|
+
return `Error: ${errorMessage(err)}`
|
|
210
211
|
}
|
|
211
212
|
}
|
|
212
213
|
|
|
@@ -107,7 +107,7 @@ describe('memory tool knowledge actions (source verification)', () => {
|
|
|
107
107
|
it('action enum in memory.ts includes the declared base actions', async () => {
|
|
108
108
|
const fs = await import('fs')
|
|
109
109
|
const src = fs.readFileSync(
|
|
110
|
-
new URL('./memory
|
|
110
|
+
new URL('./memory', import.meta.url).pathname,
|
|
111
111
|
'utf-8',
|
|
112
112
|
)
|
|
113
113
|
|
|
@@ -127,7 +127,7 @@ describe('memory tool knowledge actions (source verification)', () => {
|
|
|
127
127
|
it('action enum does not advertise removed knowledge actions', async () => {
|
|
128
128
|
const fs = await import('fs')
|
|
129
129
|
const src = fs.readFileSync(
|
|
130
|
-
new URL('./memory
|
|
130
|
+
new URL('./memory', import.meta.url).pathname,
|
|
131
131
|
'utf-8',
|
|
132
132
|
)
|
|
133
133
|
|
|
@@ -142,7 +142,7 @@ describe('memory tool knowledge actions (source verification)', () => {
|
|
|
142
142
|
it('declares the narrow OpenClaw-style memory tool names', async () => {
|
|
143
143
|
const fs = await import('fs')
|
|
144
144
|
const src = fs.readFileSync(
|
|
145
|
-
new URL('./memory
|
|
145
|
+
new URL('./memory', import.meta.url).pathname,
|
|
146
146
|
'utf-8',
|
|
147
147
|
)
|
|
148
148
|
|
|
@@ -176,7 +176,7 @@ describe('MCP tool block type wiring', () => {
|
|
|
176
176
|
it('index.ts source has MCP tool block gated on mcpServerIds', async () => {
|
|
177
177
|
const fs = await import('fs')
|
|
178
178
|
const src = fs.readFileSync(
|
|
179
|
-
new URL('./index
|
|
179
|
+
new URL('./index', import.meta.url).pathname,
|
|
180
180
|
'utf-8',
|
|
181
181
|
)
|
|
182
182
|
assert.ok(src.includes('mcpServerIds'), 'index.ts should reference mcpServerIds')
|
|
@@ -199,8 +199,8 @@ describe('context utility functions', () => {
|
|
|
199
199
|
|
|
200
200
|
it('safePath allows valid paths', async () => {
|
|
201
201
|
const { safePath } = await import('./context')
|
|
202
|
-
const result = safePath('/home/user/project', 'src/index
|
|
203
|
-
assert.equal(result, '/home/user/project/src/index
|
|
202
|
+
const result = safePath('/home/user/project', 'src/index')
|
|
203
|
+
assert.equal(result, '/home/user/project/src/index')
|
|
204
204
|
})
|
|
205
205
|
|
|
206
206
|
it('truncate respects max length', async () => {
|
|
@@ -15,7 +15,9 @@ import type { ToolBuildContext } from './context'
|
|
|
15
15
|
import { safePath, truncate, coerceEnvMap, MAX_OUTPUT } from './context'
|
|
16
16
|
import type { Plugin, PluginHooks } from '@/types'
|
|
17
17
|
import { getPluginManager } from '../plugins'
|
|
18
|
+
import { safeJsonParseObject } from '../json-utils'
|
|
18
19
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
20
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
19
21
|
|
|
20
22
|
function resolveShellWorkdir(baseCwd: string, requestedWorkdir?: string): string {
|
|
21
23
|
const raw = typeof requestedWorkdir === 'string' ? requestedWorkdir.trim() : ''
|
|
@@ -54,14 +56,7 @@ function asRecord(value: unknown): Record<string, unknown> | null {
|
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
function parseNestedInput(raw: unknown): Record<string, unknown> | null {
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
return asRecord(JSON.parse(raw))
|
|
60
|
-
} catch {
|
|
61
|
-
return null
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return asRecord(raw)
|
|
59
|
+
return typeof raw === 'string' ? safeJsonParseObject(raw) : asRecord(raw)
|
|
65
60
|
}
|
|
66
61
|
|
|
67
62
|
function pickString(...values: unknown[]): string | undefined {
|
|
@@ -171,7 +166,7 @@ async function executeShellAction(args: Record<string, unknown>, bctx: { cwd: st
|
|
|
171
166
|
default: return `Error: Unknown action "${action}"`
|
|
172
167
|
}
|
|
173
168
|
} catch (err: unknown) {
|
|
174
|
-
return `Error: ${
|
|
169
|
+
return `Error: ${errorMessage(err)}`
|
|
175
170
|
}
|
|
176
171
|
}
|
|
177
172
|
|
|
@@ -1,45 +1,33 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
import { tool, type StructuredToolInterface } from '@langchain/core/tools'
|
|
3
|
-
import { genId } from '@/lib/id'
|
|
4
|
-
import { DEFAULT_DELEGATION_MAX_DEPTH } from '@/lib/runtime-loop'
|
|
5
|
-
import { loadAgents, loadSessions, saveSessions } from '../storage'
|
|
6
|
-
import { enqueueSessionRun } from '../session-run-manager'
|
|
7
|
-
import { loadRuntimeSettings } from '../runtime-settings'
|
|
8
3
|
import type { ToolBuildContext } from './context'
|
|
9
4
|
import type { Plugin, PluginHooks } from '@/types'
|
|
10
5
|
import { getPluginManager } from '../plugins'
|
|
11
6
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
12
|
-
import {
|
|
7
|
+
import { errorMessage, sleep } from '@/lib/shared-utils'
|
|
13
8
|
import {
|
|
14
|
-
appendDelegationCheckpoint,
|
|
15
9
|
cancelDelegationJob,
|
|
16
|
-
completeDelegationJob,
|
|
17
|
-
createDelegationJob,
|
|
18
|
-
failDelegationJob,
|
|
19
10
|
getDelegationJob,
|
|
20
11
|
listDelegationJobs,
|
|
21
12
|
recoverStaleDelegationJobs,
|
|
22
|
-
registerDelegationRuntime,
|
|
23
|
-
startDelegationJob,
|
|
24
13
|
} from '../delegation-jobs'
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
14
|
+
import {
|
|
15
|
+
spawnSubagent,
|
|
16
|
+
getHandle,
|
|
17
|
+
getLineageNodeBySession,
|
|
18
|
+
getAncestors,
|
|
19
|
+
getChildren,
|
|
20
|
+
buildLineageTree,
|
|
21
|
+
cancelSubagentBySession,
|
|
22
|
+
} from '../subagent-runtime'
|
|
23
|
+
import {
|
|
24
|
+
spawnSwarm,
|
|
25
|
+
getSwarm,
|
|
26
|
+
getSwarmSnapshot,
|
|
27
|
+
listSwarms,
|
|
28
|
+
aggregateResults,
|
|
29
|
+
waitForAll,
|
|
30
|
+
} from '../subagent-swarm'
|
|
43
31
|
|
|
44
32
|
export function resolveSubagentBrowserProfileId(
|
|
45
33
|
parentSession: Record<string, unknown> | null | undefined,
|
|
@@ -55,86 +43,44 @@ export function resolveSubagentBrowserProfileId(
|
|
|
55
43
|
return inherited || childSessionId
|
|
56
44
|
}
|
|
57
45
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
cwd?: string
|
|
62
|
-
shareBrowserProfile?: boolean
|
|
63
|
-
}, context: { sessionId?: string; cwd: string }) {
|
|
64
|
-
const runtime = loadRuntimeSettings()
|
|
65
|
-
const maxDepth = runtime.delegationMaxDepth || DEFAULT_DELEGATION_MAX_DEPTH
|
|
66
|
-
const agents = loadAgents()
|
|
67
|
-
const agent = agents[args.agentId]
|
|
68
|
-
if (!agent) throw new Error(`Agent "${args.agentId}" not found.`)
|
|
69
|
-
|
|
70
|
-
const depth = getSessionDepth(context.sessionId, maxDepth)
|
|
71
|
-
if (depth >= maxDepth) throw new Error('Max subagent depth reached.')
|
|
72
|
-
|
|
73
|
-
const sid = genId()
|
|
74
|
-
const now = Date.now()
|
|
75
|
-
const sessions = loadSessions()
|
|
76
|
-
const parent = context.sessionId ? sessions[context.sessionId] : null
|
|
77
|
-
const browserProfileId = resolveSubagentBrowserProfileId(parent, sid, args.shareBrowserProfile === true)
|
|
78
|
-
const nextSession = {
|
|
79
|
-
id: sid,
|
|
80
|
-
name: `subagent-${agent.name}`,
|
|
81
|
-
cwd: args.cwd || context.cwd,
|
|
82
|
-
user: 'agent',
|
|
83
|
-
provider: agent.provider,
|
|
84
|
-
model: agent.model,
|
|
85
|
-
credentialId: agent.credentialId || null,
|
|
86
|
-
messages: [],
|
|
87
|
-
createdAt: now,
|
|
88
|
-
lastActiveAt: now,
|
|
89
|
-
sessionType: 'orchestrated',
|
|
90
|
-
agentId: agent.id,
|
|
91
|
-
parentSessionId: context.sessionId || null,
|
|
92
|
-
plugins: agent.plugins || agent.tools || [],
|
|
93
|
-
browserProfileId,
|
|
94
|
-
}
|
|
95
|
-
sessions[sid] = applyResolvedRoute(nextSession, resolvePrimaryAgentRoute(agent))
|
|
96
|
-
saveSessions(sessions)
|
|
97
|
-
|
|
98
|
-
startDelegationJob(jobId, {
|
|
99
|
-
childSessionId: sid,
|
|
100
|
-
agentId: agent.id,
|
|
101
|
-
agentName: agent.name,
|
|
102
|
-
cwd: args.cwd || context.cwd,
|
|
103
|
-
})
|
|
104
|
-
appendDelegationCheckpoint(jobId, `Created child session ${sid}`, 'running')
|
|
105
|
-
|
|
106
|
-
const run = enqueueSessionRun({
|
|
107
|
-
sessionId: sid,
|
|
108
|
-
message: args.message,
|
|
109
|
-
internal: true,
|
|
110
|
-
source: 'subagent',
|
|
111
|
-
mode: 'followup',
|
|
112
|
-
})
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Action context & helpers
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
113
49
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
run.promise
|
|
119
|
-
.then((result) => {
|
|
120
|
-
const latest = getDelegationJob(jobId)
|
|
121
|
-
if (latest?.status === 'cancelled') return
|
|
122
|
-
appendDelegationCheckpoint(jobId, 'Child session completed', 'completed')
|
|
123
|
-
completeDelegationJob(jobId, result.text.slice(0, 32_000), { childSessionId: sid })
|
|
124
|
-
})
|
|
125
|
-
.catch((err: unknown) => {
|
|
126
|
-
const message = err instanceof Error ? err.message : String(err)
|
|
127
|
-
const latest = getDelegationJob(jobId)
|
|
128
|
-
if (latest?.status === 'cancelled') return
|
|
129
|
-
appendDelegationCheckpoint(jobId, `Child session failed: ${message}`, 'failed')
|
|
130
|
-
failDelegationJob(jobId, message, { childSessionId: sid })
|
|
131
|
-
})
|
|
50
|
+
interface ActionContext {
|
|
51
|
+
sessionId?: string
|
|
52
|
+
cwd: string
|
|
53
|
+
}
|
|
132
54
|
|
|
133
|
-
|
|
55
|
+
function requireString(args: Record<string, unknown>, key: string): string {
|
|
56
|
+
const val = typeof args[key] === 'string' ? (args[key] as string).trim() : ''
|
|
57
|
+
if (!val) throw new Error(`${key} is required.`)
|
|
58
|
+
return val
|
|
134
59
|
}
|
|
135
60
|
|
|
136
|
-
|
|
137
|
-
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Promise-based wait (no polling when handle exists)
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
async function waitForJob(jobId: string, timeoutSec = 30): Promise<string> {
|
|
66
|
+
const timeoutMs = Math.max(1, timeoutSec) * 1000
|
|
67
|
+
|
|
68
|
+
// Try handle-based wait first (instant if already resolved)
|
|
69
|
+
const handle = getHandle(jobId)
|
|
70
|
+
if (handle) {
|
|
71
|
+
const result = await Promise.race([
|
|
72
|
+
handle.promise,
|
|
73
|
+
sleep(timeoutMs).then(() => null),
|
|
74
|
+
])
|
|
75
|
+
if (result) return JSON.stringify(result)
|
|
76
|
+
// Timed out — return current job state with explicit timeout indicator
|
|
77
|
+
const job = getDelegationJob(jobId)
|
|
78
|
+
if (job) return JSON.stringify({ ...job, _timedOut: true })
|
|
79
|
+
return `Error: delegation job "${jobId}" not found.`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Legacy fallback: poll delegation job store
|
|
83
|
+
const timeoutAt = Date.now() + timeoutMs
|
|
138
84
|
while (Date.now() < timeoutAt) {
|
|
139
85
|
const job = getDelegationJob(jobId)
|
|
140
86
|
if (!job) return `Error: delegation job "${jobId}" not found.`
|
|
@@ -147,76 +93,246 @@ async function waitForSubagentJob(jobId: string, timeoutSec = 30): Promise<strin
|
|
|
147
93
|
return latest ? JSON.stringify(latest) : `Error: delegation job "${jobId}" not found.`
|
|
148
94
|
}
|
|
149
95
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
async function executeSubagentAction(args: any, context: { sessionId?: string; cwd: string }) {
|
|
154
|
-
const normalized = normalizeToolInputArgs((args ?? {}) as Record<string, unknown>)
|
|
155
|
-
const action = String(normalized.action || '').trim().toLowerCase()
|
|
156
|
-
const agentId = (normalized.agentId ?? normalized.agent_id) as string | undefined
|
|
157
|
-
const message = normalized.message as string | undefined
|
|
158
|
-
const cwd = normalized.cwd as string | undefined
|
|
159
|
-
const shareBrowserProfile = normalized.shareBrowserProfile === true || normalized.share_browser_profile === true
|
|
160
|
-
const jobId = typeof normalized.jobId === 'string' ? normalized.jobId.trim() : ''
|
|
161
|
-
const waitForCompletion = normalized.waitForCompletion !== false && normalized.background !== true
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Action handlers (dispatch map)
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
162
99
|
|
|
163
|
-
|
|
100
|
+
async function handleStatus(args: Record<string, unknown>): Promise<string> {
|
|
101
|
+
const jobId = requireString(args, 'jobId')
|
|
102
|
+
const job = getDelegationJob(jobId)
|
|
103
|
+
if (!job) return `Error: delegation job "${jobId}" not found.`
|
|
104
|
+
const lineage = job.childSessionId ? getLineageNodeBySession(job.childSessionId) : null
|
|
105
|
+
return JSON.stringify({
|
|
106
|
+
...job,
|
|
107
|
+
lineage: lineage ? {
|
|
108
|
+
id: lineage.id,
|
|
109
|
+
depth: lineage.depth,
|
|
110
|
+
status: lineage.status,
|
|
111
|
+
parentSessionId: lineage.parentSessionId,
|
|
112
|
+
childCount: getChildren(lineage.id).length,
|
|
113
|
+
ancestors: getAncestors(lineage.id).map((a) => ({ id: a.id, agentName: a.agentName, depth: a.depth })),
|
|
114
|
+
} : null,
|
|
115
|
+
})
|
|
116
|
+
}
|
|
164
117
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const job = getDelegationJob(jobId)
|
|
169
|
-
return job ? JSON.stringify(job) : `Error: delegation job "${jobId}" not found.`
|
|
170
|
-
}
|
|
171
|
-
if (action === 'list') {
|
|
172
|
-
return JSON.stringify(listDelegationJobs({ parentSessionId: context.sessionId || null }))
|
|
173
|
-
}
|
|
174
|
-
if (action === 'cancel') {
|
|
175
|
-
if (!jobId) return 'Error: jobId is required.'
|
|
176
|
-
const job = cancelDelegationJob(jobId)
|
|
177
|
-
return job ? JSON.stringify(job) : `Error: delegation job "${jobId}" not found.`
|
|
178
|
-
}
|
|
179
|
-
if (action === 'wait') {
|
|
180
|
-
if (!jobId) return 'Error: jobId is required.'
|
|
181
|
-
const timeoutSec = typeof normalized.timeoutSec === 'number' ? normalized.timeoutSec : 30
|
|
182
|
-
return waitForSubagentJob(jobId, timeoutSec)
|
|
183
|
-
}
|
|
118
|
+
function handleList(_args: Record<string, unknown>, ctx: ActionContext): string {
|
|
119
|
+
return JSON.stringify(listDelegationJobs({ parentSessionId: ctx.sessionId || null }))
|
|
120
|
+
}
|
|
184
121
|
|
|
185
|
-
|
|
186
|
-
|
|
122
|
+
function handleCancel(args: Record<string, unknown>): string {
|
|
123
|
+
const jobId = requireString(args, 'jobId')
|
|
124
|
+
const job = getDelegationJob(jobId)
|
|
125
|
+
if (!job) return `Error: delegation job "${jobId}" not found.`
|
|
126
|
+
if (job.childSessionId) cancelSubagentBySession(job.childSessionId)
|
|
127
|
+
const cancelled = cancelDelegationJob(jobId)
|
|
128
|
+
return cancelled ? JSON.stringify(cancelled) : `Error: delegation job "${jobId}" not found.`
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function handleWait(args: Record<string, unknown>): Promise<string> {
|
|
132
|
+
const jobId = requireString(args, 'jobId')
|
|
133
|
+
const timeoutSec = typeof args.timeoutSec === 'number' ? args.timeoutSec : 30
|
|
134
|
+
return waitForJob(jobId, timeoutSec)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function handleLineage(args: Record<string, unknown>, ctx: ActionContext): string {
|
|
138
|
+
const targetSessionId = (args.sessionId as string) || ctx.sessionId
|
|
139
|
+
if (!targetSessionId) return 'Error: sessionId is required for lineage query.'
|
|
140
|
+
const node = getLineageNodeBySession(targetSessionId)
|
|
141
|
+
if (!node) return JSON.stringify({ lineage: null })
|
|
142
|
+
const tree = buildLineageTree(node.id)
|
|
143
|
+
return JSON.stringify({ lineage: tree })
|
|
144
|
+
}
|
|
187
145
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
146
|
+
async function handleBatch(args: Record<string, unknown>, ctx: ActionContext): Promise<string> {
|
|
147
|
+
const tasks = args.tasks as Array<{ agentId: string; message: string; cwd?: string; shareBrowserProfile?: boolean }> | undefined
|
|
148
|
+
if (!Array.isArray(tasks) || tasks.length === 0) return 'Error: tasks array is required for batch action.'
|
|
149
|
+
for (const t of tasks) {
|
|
150
|
+
if (!t.agentId || !t.message) return 'Error: each task requires agentId and message.'
|
|
151
|
+
}
|
|
152
|
+
const waitForCompletion = args.waitForCompletion !== false && args.background !== true
|
|
153
|
+
|
|
154
|
+
// Use spawnSwarm internally — batch is a simplified interface
|
|
155
|
+
const swarm = spawnSwarm({ tasks }, { sessionId: ctx.sessionId, cwd: ctx.cwd })
|
|
156
|
+
const jobIds = swarm.members
|
|
157
|
+
.filter((m) => !m.spawnError && m.handle)
|
|
158
|
+
.map((m) => m.handle.jobId)
|
|
159
|
+
|
|
160
|
+
if (!waitForCompletion) {
|
|
161
|
+
return JSON.stringify({
|
|
162
|
+
action: 'batch',
|
|
163
|
+
status: 'running',
|
|
164
|
+
jobIds,
|
|
165
|
+
taskCount: tasks.length,
|
|
194
166
|
})
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
167
|
+
}
|
|
168
|
+
const aggregate = await swarm.allSettled
|
|
169
|
+
return JSON.stringify({
|
|
170
|
+
action: 'batch',
|
|
171
|
+
status: 'completed',
|
|
172
|
+
jobIds,
|
|
173
|
+
completed: aggregate.totalCompleted,
|
|
174
|
+
failed: aggregate.totalFailed + aggregate.totalSpawnErrors,
|
|
175
|
+
cancelled: aggregate.totalCancelled,
|
|
176
|
+
timedOut: 0,
|
|
177
|
+
totalDurationMs: aggregate.durationMs,
|
|
178
|
+
results: aggregate.results.map((r) => ({
|
|
179
|
+
jobId: r.jobId,
|
|
180
|
+
agentName: r.agentName,
|
|
181
|
+
status: r.status === 'spawn_error' ? 'failed' : r.status,
|
|
182
|
+
response: r.response?.slice(0, 2000) || null,
|
|
183
|
+
error: r.error,
|
|
184
|
+
})),
|
|
185
|
+
})
|
|
186
|
+
}
|
|
207
187
|
|
|
208
|
-
|
|
209
|
-
|
|
188
|
+
async function handleAggregate(args: Record<string, unknown>): Promise<string> {
|
|
189
|
+
const jobIds = args.jobIds as string[] | undefined
|
|
190
|
+
if (!Array.isArray(jobIds) || jobIds.length === 0) return 'Error: jobIds array is required for aggregate action.'
|
|
191
|
+
return JSON.stringify(aggregateResults(jobIds))
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function handleWaitAll(args: Record<string, unknown>): Promise<string> {
|
|
195
|
+
const jobIds = args.jobIds as string[] | undefined
|
|
196
|
+
if (!Array.isArray(jobIds) || jobIds.length === 0) return 'Error: jobIds array is required for wait_all action.'
|
|
197
|
+
const timeoutSec = typeof args.timeoutSec === 'number' ? args.timeoutSec : 300
|
|
198
|
+
const agg = await waitForAll(jobIds, timeoutSec)
|
|
199
|
+
return JSON.stringify(agg)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function handleSwarm(args: Record<string, unknown>, ctx: ActionContext): Promise<string> {
|
|
203
|
+
const tasks = args.tasks as Array<{ agentId: string; message: string; cwd?: string; shareBrowserProfile?: boolean }> | undefined
|
|
204
|
+
if (!Array.isArray(tasks) || tasks.length === 0) return 'Error: tasks array is required for swarm action.'
|
|
205
|
+
for (const t of tasks) {
|
|
206
|
+
if (!t.agentId || !t.message) return 'Error: each task requires agentId and message.'
|
|
207
|
+
}
|
|
208
|
+
const waitForCompletion = args.waitForCompletion !== false && args.background !== true
|
|
209
|
+
|
|
210
|
+
const swarm = spawnSwarm({ tasks }, { sessionId: ctx.sessionId, cwd: ctx.cwd })
|
|
211
|
+
if (!waitForCompletion) {
|
|
212
|
+
const snapshot = getSwarmSnapshot(swarm.swarmId)
|
|
210
213
|
return JSON.stringify({
|
|
211
|
-
|
|
212
|
-
status:
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
response: result.text.slice(0, 32_000),
|
|
214
|
+
action: 'swarm',
|
|
215
|
+
status: 'running',
|
|
216
|
+
swarmId: swarm.swarmId,
|
|
217
|
+
memberCount: swarm.members.length,
|
|
218
|
+
snapshot,
|
|
217
219
|
})
|
|
220
|
+
}
|
|
221
|
+
const aggregate = await swarm.allSettled
|
|
222
|
+
const snapshot = getSwarmSnapshot(swarm.swarmId)
|
|
223
|
+
return JSON.stringify({
|
|
224
|
+
action: 'swarm',
|
|
225
|
+
...aggregate,
|
|
226
|
+
status: swarm.status,
|
|
227
|
+
snapshot,
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function handleSwarmStatus(args: Record<string, unknown>): string {
|
|
232
|
+
const swarmId = requireString(args, 'swarmId')
|
|
233
|
+
const snapshot = getSwarmSnapshot(swarmId)
|
|
234
|
+
if (!snapshot) return `Error: swarm "${swarmId}" not found.`
|
|
235
|
+
return JSON.stringify(snapshot)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function handleSwarmList(_args: Record<string, unknown>, ctx: ActionContext): string {
|
|
239
|
+
const swarms = listSwarms(ctx.sessionId)
|
|
240
|
+
return JSON.stringify(swarms.map((s) => ({
|
|
241
|
+
swarmId: s.swarmId,
|
|
242
|
+
status: s.status,
|
|
243
|
+
memberCount: s.members.length,
|
|
244
|
+
createdAt: s.createdAt,
|
|
245
|
+
completedAt: s.completedAt,
|
|
246
|
+
})))
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function handleSwarmCancel(args: Record<string, unknown>): string {
|
|
250
|
+
const swarmId = requireString(args, 'swarmId')
|
|
251
|
+
const swarm = getSwarm(swarmId)
|
|
252
|
+
if (!swarm) return `Error: swarm "${swarmId}" not found.`
|
|
253
|
+
swarm.cancelAll()
|
|
254
|
+
const snapshot = getSwarmSnapshot(swarmId)
|
|
255
|
+
return JSON.stringify({ cancelled: true, snapshot })
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function handleStart(args: Record<string, unknown>, ctx: ActionContext): Promise<string> {
|
|
259
|
+
const agentId = (args.agentId ?? args.agent_id) as string | undefined
|
|
260
|
+
const message = args.message as string | undefined
|
|
261
|
+
if (!agentId) return 'Error: agentId is required.'
|
|
262
|
+
if (!message) return 'Error: message is required.'
|
|
263
|
+
|
|
264
|
+
const cwd = args.cwd as string | undefined
|
|
265
|
+
const shareBrowserProfile = args.shareBrowserProfile === true || args.share_browser_profile === true
|
|
266
|
+
const waitForCompletion = args.waitForCompletion !== false && args.background !== true
|
|
267
|
+
|
|
268
|
+
const handle = spawnSubagent(
|
|
269
|
+
{ agentId, message, cwd, shareBrowserProfile, waitForCompletion },
|
|
270
|
+
{ sessionId: ctx.sessionId, cwd: ctx.cwd },
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
if (!waitForCompletion) {
|
|
274
|
+
return JSON.stringify({
|
|
275
|
+
jobId: handle.jobId,
|
|
276
|
+
status: 'running',
|
|
277
|
+
agentId: handle.agentId,
|
|
278
|
+
agentName: handle.agentName,
|
|
279
|
+
sessionId: handle.sessionId,
|
|
280
|
+
lineageId: handle.lineageId,
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const result = await handle.promise
|
|
285
|
+
return JSON.stringify({
|
|
286
|
+
jobId: result.jobId,
|
|
287
|
+
status: result.status,
|
|
288
|
+
agentId: result.agentId,
|
|
289
|
+
agentName: result.agentName,
|
|
290
|
+
sessionId: result.sessionId,
|
|
291
|
+
lineageId: result.lineageId,
|
|
292
|
+
response: result.response,
|
|
293
|
+
depth: result.depth,
|
|
294
|
+
childCount: result.childCount,
|
|
295
|
+
durationMs: result.durationMs,
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
// Dispatch map
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
|
|
303
|
+
type ActionHandler = (args: Record<string, unknown>, ctx: ActionContext) => Promise<string> | string
|
|
304
|
+
const ACTIONS: Record<string, ActionHandler> = {
|
|
305
|
+
status: handleStatus,
|
|
306
|
+
list: handleList,
|
|
307
|
+
cancel: handleCancel,
|
|
308
|
+
wait: handleWait,
|
|
309
|
+
lineage: handleLineage,
|
|
310
|
+
batch: handleBatch,
|
|
311
|
+
aggregate: handleAggregate,
|
|
312
|
+
wait_all: handleWaitAll,
|
|
313
|
+
swarm: handleSwarm,
|
|
314
|
+
swarm_status: handleSwarmStatus,
|
|
315
|
+
swarm_list: handleSwarmList,
|
|
316
|
+
swarm_cancel: handleSwarmCancel,
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Core Subagent Execution Logic — powered by native subagent runtime.
|
|
321
|
+
* Uses dispatch map instead of if-else chain for maintainability.
|
|
322
|
+
*/
|
|
323
|
+
async function executeSubagentAction(args: unknown, context: ActionContext) {
|
|
324
|
+
const normalized = normalizeToolInputArgs((args ?? {}) as Record<string, unknown>)
|
|
325
|
+
const action = String(normalized.action || 'start').trim().toLowerCase()
|
|
326
|
+
|
|
327
|
+
recoverStaleDelegationJobs()
|
|
328
|
+
|
|
329
|
+
try {
|
|
330
|
+
const handler = ACTIONS[action]
|
|
331
|
+
if (handler) return handler(normalized, context)
|
|
332
|
+
// Default: single agent spawn
|
|
333
|
+
return handleStart(normalized, context)
|
|
218
334
|
} catch (err: unknown) {
|
|
219
|
-
return `Error: ${
|
|
335
|
+
return `Error: ${errorMessage(err)}`
|
|
220
336
|
}
|
|
221
337
|
}
|
|
222
338
|
|
|
@@ -226,15 +342,31 @@ async function executeSubagentAction(args: any, context: { sessionId?: string; c
|
|
|
226
342
|
const SubagentPlugin: Plugin = {
|
|
227
343
|
name: 'Core Subagents',
|
|
228
344
|
description: 'Delegate tasks to other specialized agents with resumable job handles.',
|
|
229
|
-
hooks: {
|
|
345
|
+
hooks: {
|
|
346
|
+
getCapabilityDescription: () =>
|
|
347
|
+
'Delegate tasks to other agents (spawn_subagent). Single task: action "start". '
|
|
348
|
+
+ 'Multiple independent tasks: action "batch" with a tasks array. '
|
|
349
|
+
+ 'Event-driven parallel with status tracking: action "swarm" with a tasks array.',
|
|
350
|
+
getOperatingGuidance: () => [
|
|
351
|
+
'SUBAGENT DISPATCH RULES:',
|
|
352
|
+
'- Single task → action "start" with agentId + message.',
|
|
353
|
+
'- 2+ independent tasks that can run in parallel → action "batch" with tasks array [{agentId, message}, ...].',
|
|
354
|
+
'- DO NOT call "start" in a loop when tasks are independent — use "batch" or "swarm" instead.',
|
|
355
|
+
'- Only use subagents when the task genuinely requires another agent\'s specialization or parallel execution.',
|
|
356
|
+
'- If you can answer directly from your own knowledge, do NOT spawn a subagent.',
|
|
357
|
+
],
|
|
358
|
+
} as PluginHooks,
|
|
230
359
|
tools: [
|
|
231
360
|
{
|
|
232
361
|
name: 'spawn_subagent',
|
|
233
|
-
description: 'Delegate
|
|
362
|
+
description: 'Delegate tasks to other agents. '
|
|
363
|
+
+ 'Actions: start (single agent), batch (2+ parallel tasks via "tasks" array), swarm (parallel with status tracking via "tasks" array). '
|
|
364
|
+
+ 'Management: status, list, wait, wait_all, cancel, lineage, aggregate, swarm_status, swarm_list, swarm_cancel. '
|
|
365
|
+
+ 'For multiple independent tasks, prefer "batch" with tasks:[{agentId,message},...] over calling "start" repeatedly.',
|
|
234
366
|
parameters: {
|
|
235
367
|
type: 'object',
|
|
236
368
|
properties: {
|
|
237
|
-
action: { type: 'string', enum: ['start', 'status', 'list', 'wait', 'cancel'] },
|
|
369
|
+
action: { type: 'string', enum: ['start', 'status', 'list', 'wait', 'wait_all', 'cancel', 'lineage', 'batch', 'aggregate', 'swarm', 'swarm_status', 'swarm_list', 'swarm_cancel'] },
|
|
238
370
|
agentId: { type: 'string' },
|
|
239
371
|
message: { type: 'string' },
|
|
240
372
|
cwd: { type: 'string' },
|
|
@@ -243,6 +375,26 @@ const SubagentPlugin: Plugin = {
|
|
|
243
375
|
description: 'When true, inherit the parent session browser profile. Defaults to false so subagents get isolated browser state.',
|
|
244
376
|
},
|
|
245
377
|
jobId: { type: 'string' },
|
|
378
|
+
swarmId: { type: 'string', description: 'Swarm ID for swarm_status/swarm_cancel actions.' },
|
|
379
|
+
jobIds: {
|
|
380
|
+
type: 'array',
|
|
381
|
+
items: { type: 'string' },
|
|
382
|
+
description: 'Array of job IDs for aggregate/wait_all actions.',
|
|
383
|
+
},
|
|
384
|
+
tasks: {
|
|
385
|
+
type: 'array',
|
|
386
|
+
items: {
|
|
387
|
+
type: 'object',
|
|
388
|
+
properties: {
|
|
389
|
+
agentId: { type: 'string' },
|
|
390
|
+
message: { type: 'string' },
|
|
391
|
+
cwd: { type: 'string' },
|
|
392
|
+
shareBrowserProfile: { type: 'boolean' },
|
|
393
|
+
},
|
|
394
|
+
required: ['agentId', 'message'],
|
|
395
|
+
},
|
|
396
|
+
description: 'Array of tasks for batch/swarm action.',
|
|
397
|
+
},
|
|
246
398
|
waitForCompletion: { type: 'boolean' },
|
|
247
399
|
background: { type: 'boolean' },
|
|
248
400
|
timeoutSec: { type: 'number' },
|