@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
|
@@ -3,7 +3,7 @@ import { tool } from '@langchain/core/tools'
|
|
|
3
3
|
import { StateGraph, MessagesAnnotation, START, END } from '@langchain/langgraph'
|
|
4
4
|
import { ToolNode } from '@langchain/langgraph/prebuilt'
|
|
5
5
|
import { AIMessage } from '@langchain/core/messages'
|
|
6
|
-
import { loadSessions, saveSessions, loadAgents, loadCredentials, loadSettings, loadSecrets, loadTasks,
|
|
6
|
+
import { loadSessions, saveSessions, loadAgents, loadCredentials, loadSettings, loadSecrets, loadTasks, patchTask, upsertTask, decryptKey, loadSkills } from './storage'
|
|
7
7
|
import { WORKSPACE_DIR } from './data-dir'
|
|
8
8
|
import { loadRuntimeSettings, getOrchestratorLoopRecursionLimit } from './runtime-settings'
|
|
9
9
|
import { getMemoryDb } from './memory-db'
|
|
@@ -13,6 +13,7 @@ import { notify } from './ws-hub'
|
|
|
13
13
|
import { pushMainLoopEventToMainSessions } from './main-agent-loop'
|
|
14
14
|
import { buildCurrentDateTimePromptContext } from './prompt-runtime-context'
|
|
15
15
|
import { getPluginManager } from './plugins'
|
|
16
|
+
import { buildBoardTask } from './task-lifecycle'
|
|
16
17
|
import './builtin-plugins'
|
|
17
18
|
import { genId } from '@/lib/id'
|
|
18
19
|
import { NON_LANGGRAPH_PROVIDER_IDS } from '@/lib/provider-sets'
|
|
@@ -145,38 +146,20 @@ async function executeSubTaskViaCli(agent: Agent, task: string, parentSessionId:
|
|
|
145
146
|
return result
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
|
|
149
|
-
orchestrator: Agent
|
|
150
|
-
|
|
151
|
-
sessionId: string
|
|
152
|
-
|
|
153
|
-
)
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
// Build available agents list
|
|
157
|
-
const agentIds = orchestrator.subAgentIds || []
|
|
158
|
-
const agents = agentIds.map((id) => allAgents[id]).filter(Boolean) as Agent[]
|
|
159
|
-
const agentListContext = agents.length
|
|
160
|
-
? '\n\nAvailable agents:\n' + agents.map((a) => {
|
|
161
|
-
const plugins = (a.plugins || a.tools)?.length ? ` [plugins: ${(a.plugins || a.tools)!.join(', ')}]` : ''
|
|
162
|
-
const skills = a.skills?.length ? ` [skills: ${a.skills.join(', ')}]` : ''
|
|
163
|
-
return `- ${a.name}: ${a.description}${plugins}${skills}`
|
|
164
|
-
}).join('\n')
|
|
165
|
-
: '\n\n(No agents available for delegation.)'
|
|
166
|
-
|
|
167
|
-
// Load relevant memories
|
|
149
|
+
function createOrchestratorTools(params: {
|
|
150
|
+
orchestrator: Agent
|
|
151
|
+
agents: Agent[]
|
|
152
|
+
sessionId: string
|
|
153
|
+
availableSecrets: Array<{ name: string; service: string; value: string }>
|
|
154
|
+
}) {
|
|
155
|
+
const { orchestrator, agents, sessionId, availableSecrets } = params
|
|
168
156
|
const db = getMemoryDb()
|
|
169
|
-
const memories = db.getByAgent(orchestrator.id)
|
|
170
|
-
const memoryContext = memories.length
|
|
171
|
-
? '\n\nRelevant memories:\n' + memories.slice(0, 10).map((m) => `[${m.category}] ${m.title}: ${m.content.slice(0, 200)}`).join('\n')
|
|
172
|
-
: ''
|
|
173
157
|
|
|
174
|
-
// Define tools
|
|
175
158
|
const delegateTool = tool(
|
|
176
159
|
async ({ agentName, task: agentTask }) => {
|
|
177
|
-
const agent = agents.find((
|
|
160
|
+
const agent = agents.find((candidate) => candidate.name.toLowerCase() === agentName.toLowerCase())
|
|
178
161
|
if (!agent) {
|
|
179
|
-
return `Agent "${agentName}" not found. Available: ${agents.map((
|
|
162
|
+
return `Agent "${agentName}" not found. Available: ${agents.map((candidate) => candidate.name).join(', ')}`
|
|
180
163
|
}
|
|
181
164
|
console.log(`[orchestrator-lg] Delegating to ${agent.name}: ${agentTask.slice(0, 80)}`)
|
|
182
165
|
getPluginManager().runHook(
|
|
@@ -229,7 +212,7 @@ export async function executeLangGraphOrchestrator(
|
|
|
229
212
|
async ({ query }) => {
|
|
230
213
|
const results = db.search(query, orchestrator.id)
|
|
231
214
|
if (!results.length) return 'No matching memories found.'
|
|
232
|
-
return results.map((
|
|
215
|
+
return results.map((memory) => `[${memory.category}] ${memory.title}: ${memory.content.slice(0, 300)}`).join('\n')
|
|
233
216
|
},
|
|
234
217
|
{
|
|
235
218
|
name: 'search_memory',
|
|
@@ -254,16 +237,13 @@ export async function executeLangGraphOrchestrator(
|
|
|
254
237
|
},
|
|
255
238
|
)
|
|
256
239
|
|
|
257
|
-
// Secrets
|
|
258
|
-
const availableSecrets = getSecretsForOrchestrator(orchestrator.id)
|
|
259
|
-
|
|
260
240
|
const getSecretTool = tool(
|
|
261
241
|
async ({ serviceName }) => {
|
|
262
242
|
const match = availableSecrets.find(
|
|
263
|
-
(
|
|
243
|
+
(secret) => secret.service.toLowerCase() === serviceName.toLowerCase() || secret.name.toLowerCase() === serviceName.toLowerCase(),
|
|
264
244
|
)
|
|
265
245
|
if (!match) {
|
|
266
|
-
return `No secret found for "${serviceName}". Available services: ${availableSecrets.map((
|
|
246
|
+
return `No secret found for "${serviceName}". Available services: ${availableSecrets.map((secret) => secret.service).join(', ') || 'none'}`
|
|
267
247
|
}
|
|
268
248
|
console.log(`[orchestrator-lg] Retrieved secret for service: ${match.service}`)
|
|
269
249
|
return JSON.stringify({ name: match.name, service: match.service, value: match.value })
|
|
@@ -277,23 +257,23 @@ export async function executeLangGraphOrchestrator(
|
|
|
277
257
|
},
|
|
278
258
|
)
|
|
279
259
|
|
|
280
|
-
// Task board tools
|
|
281
260
|
const commentOnTaskTool = tool(
|
|
282
261
|
async ({ taskId, comment }) => {
|
|
283
|
-
const
|
|
284
|
-
|
|
262
|
+
const t = patchTask(taskId, (current) => {
|
|
263
|
+
if (!current) return current
|
|
264
|
+
if (!current.comments) current.comments = []
|
|
265
|
+
const nextComment: TaskComment = {
|
|
266
|
+
id: genId(),
|
|
267
|
+
author: orchestrator.name,
|
|
268
|
+
agentId: orchestrator.id,
|
|
269
|
+
text: comment,
|
|
270
|
+
createdAt: Date.now(),
|
|
271
|
+
}
|
|
272
|
+
current.comments.push(nextComment)
|
|
273
|
+
current.updatedAt = Date.now()
|
|
274
|
+
return current
|
|
275
|
+
})
|
|
285
276
|
if (!t) return `Task "${taskId}" not found.`
|
|
286
|
-
if (!t.comments) t.comments = []
|
|
287
|
-
const c: TaskComment = {
|
|
288
|
-
id: genId(),
|
|
289
|
-
author: orchestrator.name,
|
|
290
|
-
agentId: orchestrator.id,
|
|
291
|
-
text: comment,
|
|
292
|
-
createdAt: Date.now(),
|
|
293
|
-
}
|
|
294
|
-
t.comments.push(c)
|
|
295
|
-
t.updatedAt = Date.now()
|
|
296
|
-
saveTasks(tasks)
|
|
297
277
|
console.log(`[orchestrator-lg] Commented on task "${t.title}": ${comment.slice(0, 80)}`)
|
|
298
278
|
return `Comment added to task "${t.title}".`
|
|
299
279
|
},
|
|
@@ -309,27 +289,19 @@ export async function executeLangGraphOrchestrator(
|
|
|
309
289
|
|
|
310
290
|
const createTaskTool = tool(
|
|
311
291
|
async ({ title, description: desc }) => {
|
|
312
|
-
const
|
|
313
|
-
const
|
|
314
|
-
tasks[id] = {
|
|
315
|
-
id,
|
|
292
|
+
const now = Date.now()
|
|
293
|
+
const taskRecord = buildBoardTask({
|
|
316
294
|
title,
|
|
317
295
|
description: desc,
|
|
318
|
-
status: 'backlog',
|
|
319
296
|
agentId: orchestrator.id,
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
completedAt: null,
|
|
329
|
-
}
|
|
330
|
-
saveTasks(tasks)
|
|
331
|
-
console.log(`[orchestrator-lg] Created backlog task: "${title}" (${id})`)
|
|
332
|
-
return `Task "${title}" created in backlog (id: ${id}). The user can review and queue it.`
|
|
297
|
+
now,
|
|
298
|
+
seed: {
|
|
299
|
+
comments: [],
|
|
300
|
+
},
|
|
301
|
+
})
|
|
302
|
+
upsertTask(taskRecord.id, taskRecord)
|
|
303
|
+
console.log(`[orchestrator-lg] Created backlog task: "${title}" (${taskRecord.id})`)
|
|
304
|
+
return `Task "${title}" created in backlog (id: ${taskRecord.id}). The user can review and queue it.`
|
|
333
305
|
},
|
|
334
306
|
{
|
|
335
307
|
name: 'create_task',
|
|
@@ -341,6 +313,126 @@ export async function executeLangGraphOrchestrator(
|
|
|
341
313
|
},
|
|
342
314
|
)
|
|
343
315
|
|
|
316
|
+
return [delegateTool, storeMemoryTool, searchMemoryTool, getSecretTool, commentOnTaskTool, createTaskTool, markCompleteTool]
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function compileOrchestratorGraph(params: {
|
|
320
|
+
llmWithTools: any
|
|
321
|
+
allTools: ReturnType<typeof createOrchestratorTools>
|
|
322
|
+
checkpointSaver: any
|
|
323
|
+
approvalInterruptsEnabled: boolean
|
|
324
|
+
systemMessage?: string
|
|
325
|
+
enableDelegateFallback?: boolean
|
|
326
|
+
}) {
|
|
327
|
+
const { llmWithTools, allTools, checkpointSaver, approvalInterruptsEnabled, systemMessage, enableDelegateFallback = false } = params
|
|
328
|
+
const toolNode = new ToolNode(allTools)
|
|
329
|
+
let fallbackAttempts = 0
|
|
330
|
+
const maxFallbackAttempts = 2
|
|
331
|
+
|
|
332
|
+
async function agentNode(state: typeof MessagesAnnotation.State) {
|
|
333
|
+
const promptMessages = systemMessage
|
|
334
|
+
? [{ role: 'system' as const, content: systemMessage }, ...state.messages]
|
|
335
|
+
: state.messages
|
|
336
|
+
const response = await llmWithTools.invoke(promptMessages)
|
|
337
|
+
return { messages: [response] }
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function routerNode(state: typeof MessagesAnnotation.State) {
|
|
341
|
+
if (!enableDelegateFallback) return { messages: [] }
|
|
342
|
+
const messages = state.messages
|
|
343
|
+
const lastMsg = messages[messages.length - 1]
|
|
344
|
+
if (lastMsg && typeof (lastMsg as any).content === 'string') {
|
|
345
|
+
const content = (lastMsg as any).content as string
|
|
346
|
+
const isError = content.startsWith('Error:') || content.startsWith('Agent "') && content.includes('not found')
|
|
347
|
+
if (isError && fallbackAttempts < maxFallbackAttempts) {
|
|
348
|
+
fallbackAttempts++
|
|
349
|
+
const failedToolCall = [...messages].reverse().find(
|
|
350
|
+
(message) => (message as any).tool_calls?.some((toolCall: any) => toolCall.name === 'delegate_to_agent'),
|
|
351
|
+
)
|
|
352
|
+
if (failedToolCall) {
|
|
353
|
+
const toolCall = (failedToolCall as any).tool_calls?.find((entry: any) => entry.name === 'delegate_to_agent')
|
|
354
|
+
const failedAgentName = toolCall?.args?.agentName || 'unknown'
|
|
355
|
+
const fallbackHint = `The agent "${failedAgentName}" failed. Try delegating to a different agent with matching capabilities, or re-plan your approach. Fallback attempt ${fallbackAttempts}/${maxFallbackAttempts}.`
|
|
356
|
+
return { messages: [new AIMessage({ content: fallbackHint })] }
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return { messages: [] }
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function shouldContinue(state: typeof MessagesAnnotation.State) {
|
|
364
|
+
const lastMsg = state.messages[state.messages.length - 1]
|
|
365
|
+
const toolCalls = (lastMsg as any)?.tool_calls
|
|
366
|
+
return toolCalls && toolCalls.length > 0 ? 'tools' : END
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function afterRouter() {
|
|
370
|
+
return 'agent'
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const graph = new StateGraph(MessagesAnnotation)
|
|
374
|
+
.addNode('agent', agentNode)
|
|
375
|
+
.addNode('tools', toolNode)
|
|
376
|
+
.addNode('router', routerNode)
|
|
377
|
+
.addEdge(START, 'agent')
|
|
378
|
+
.addConditionalEdges('agent', shouldContinue, { tools: 'tools', [END]: END })
|
|
379
|
+
.addEdge('tools', 'router')
|
|
380
|
+
.addConditionalEdges('router', afterRouter, { agent: 'agent' })
|
|
381
|
+
|
|
382
|
+
const compiledGraph = graph.compile({
|
|
383
|
+
checkpointer: checkpointSaver,
|
|
384
|
+
...(approvalInterruptsEnabled ? { interruptBefore: ['tools'] } : {}),
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
;(compiledGraph as any).__graphStructure = {
|
|
388
|
+
nodes: ['agent', 'tools', 'router'],
|
|
389
|
+
edges: [
|
|
390
|
+
{ from: START, to: 'agent' },
|
|
391
|
+
{ from: 'agent', to: 'tools', condition: 'has_tool_calls' },
|
|
392
|
+
{ from: 'agent', to: END, condition: 'no_tool_calls' },
|
|
393
|
+
{ from: 'tools', to: 'router' },
|
|
394
|
+
{ from: 'router', to: 'agent', condition: enableDelegateFallback ? 'fallback_or_continue' : 'continue' },
|
|
395
|
+
],
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return compiledGraph
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export async function executeLangGraphOrchestrator(
|
|
402
|
+
orchestrator: Agent,
|
|
403
|
+
task: string,
|
|
404
|
+
sessionId: string,
|
|
405
|
+
taskId?: string,
|
|
406
|
+
): Promise<string> {
|
|
407
|
+
const allAgents = loadAgents()
|
|
408
|
+
|
|
409
|
+
// Build available agents list
|
|
410
|
+
const agentIds = orchestrator.subAgentIds || []
|
|
411
|
+
const agents = agentIds.map((id) => allAgents[id]).filter(Boolean) as Agent[]
|
|
412
|
+
const agentListContext = agents.length
|
|
413
|
+
? '\n\nAvailable agents:\n' + agents.map((a) => {
|
|
414
|
+
const plugins = (a.plugins || a.tools)?.length ? ` [plugins: ${(a.plugins || a.tools)!.join(', ')}]` : ''
|
|
415
|
+
const skills = a.skills?.length ? ` [skills: ${a.skills.join(', ')}]` : ''
|
|
416
|
+
return `- ${a.name}: ${a.description}${plugins}${skills}`
|
|
417
|
+
}).join('\n')
|
|
418
|
+
: '\n\n(No agents available for delegation.)'
|
|
419
|
+
|
|
420
|
+
// Load relevant memories
|
|
421
|
+
const db = getMemoryDb()
|
|
422
|
+
const memories = db.getByAgent(orchestrator.id)
|
|
423
|
+
const memoryContext = memories.length
|
|
424
|
+
? '\n\nRelevant memories:\n' + memories.slice(0, 10).map((m) => `[${m.category}] ${m.title}: ${m.content.slice(0, 200)}`).join('\n')
|
|
425
|
+
: ''
|
|
426
|
+
|
|
427
|
+
// Secrets
|
|
428
|
+
const availableSecrets = getSecretsForOrchestrator(orchestrator.id)
|
|
429
|
+
const allTools = createOrchestratorTools({
|
|
430
|
+
orchestrator,
|
|
431
|
+
agents,
|
|
432
|
+
sessionId,
|
|
433
|
+
availableSecrets,
|
|
434
|
+
})
|
|
435
|
+
|
|
344
436
|
// Build secrets context for the system prompt
|
|
345
437
|
const secretsContext = availableSecrets.length
|
|
346
438
|
? '\n\nAvailable secrets (use get_secret tool to retrieve values):\n' + availableSecrets.map((s) => `- ${s.name} (${s.service})`).join('\n')
|
|
@@ -400,100 +492,16 @@ export async function executeLangGraphOrchestrator(
|
|
|
400
492
|
const checkpointSaver = getCheckpointSaver()
|
|
401
493
|
const isStrictMode = settings.capabilityPolicyMode === 'strict'
|
|
402
494
|
const approvalInterruptsEnabled = isStrictMode && settings.approvalsEnabled === true
|
|
403
|
-
const allTools = [delegateTool, storeMemoryTool, searchMemoryTool, getSecretTool, commentOnTaskTool, createTaskTool, markCompleteTool]
|
|
404
495
|
const llmWithTools = llm.bindTools(allTools)
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
async function agentNode(state: typeof MessagesAnnotation.State) {
|
|
413
|
-
const response = await llmWithTools.invoke([
|
|
414
|
-
{ role: 'system' as const, content: systemMessage },
|
|
415
|
-
...state.messages,
|
|
416
|
-
])
|
|
417
|
-
return { messages: [response] }
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// Router node: inspects tool results, decides next step
|
|
421
|
-
function routerNode(state: typeof MessagesAnnotation.State) {
|
|
422
|
-
const messages = state.messages
|
|
423
|
-
const lastMsg = messages[messages.length - 1]
|
|
424
|
-
|
|
425
|
-
// Check if the last tool message contains an error from delegate_to_agent
|
|
426
|
-
if (lastMsg && typeof (lastMsg as any).content === 'string') {
|
|
427
|
-
const content = (lastMsg as any).content as string
|
|
428
|
-
const isError = content.startsWith('Error:') || content.startsWith('Agent "') && content.includes('not found')
|
|
429
|
-
|
|
430
|
-
if (isError && fallbackAttempts < MAX_FALLBACK_ATTEMPTS) {
|
|
431
|
-
fallbackAttempts++
|
|
432
|
-
// Look for a delegate tool call in recent messages and try to find alternative agent
|
|
433
|
-
const failedToolCall = [...messages].reverse().find(
|
|
434
|
-
(m) => (m as any).tool_calls?.some((tc: any) => tc.name === 'delegate_to_agent')
|
|
435
|
-
)
|
|
436
|
-
if (failedToolCall) {
|
|
437
|
-
const tc = (failedToolCall as any).tool_calls?.find((tc: any) => tc.name === 'delegate_to_agent')
|
|
438
|
-
const failedAgentName = tc?.args?.agentName || 'unknown'
|
|
439
|
-
const fallbackHint = `The agent "${failedAgentName}" failed. Try delegating to a different agent with matching capabilities, or re-plan your approach. Fallback attempt ${fallbackAttempts}/${MAX_FALLBACK_ATTEMPTS}.`
|
|
440
|
-
return { messages: [new AIMessage({ content: fallbackHint })] }
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// No fallback needed — pass through
|
|
446
|
-
return { messages: [] }
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// Routing function: after agent node, check if there are tool calls
|
|
450
|
-
function shouldContinue(state: typeof MessagesAnnotation.State) {
|
|
451
|
-
const lastMsg = state.messages[state.messages.length - 1]
|
|
452
|
-
const toolCalls = (lastMsg as any)?.tool_calls
|
|
453
|
-
if (toolCalls && toolCalls.length > 0) {
|
|
454
|
-
return 'tools'
|
|
455
|
-
}
|
|
456
|
-
return END
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// After router, decide whether to go back to agent or end
|
|
460
|
-
function afterRouter(state: typeof MessagesAnnotation.State) {
|
|
461
|
-
const messages = state.messages
|
|
462
|
-
// If router added a fallback hint, route back to agent
|
|
463
|
-
const lastMsg = messages[messages.length - 1]
|
|
464
|
-
if (lastMsg && typeof (lastMsg as any).content === 'string' && (lastMsg as any).content.includes('Fallback attempt')) {
|
|
465
|
-
return 'agent'
|
|
466
|
-
}
|
|
467
|
-
return 'agent'
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// Build the StateGraph
|
|
471
|
-
const graph = new StateGraph(MessagesAnnotation)
|
|
472
|
-
.addNode('agent', agentNode)
|
|
473
|
-
.addNode('tools', toolNode)
|
|
474
|
-
.addNode('router', routerNode)
|
|
475
|
-
.addEdge(START, 'agent')
|
|
476
|
-
.addConditionalEdges('agent', shouldContinue, { tools: 'tools', [END]: END })
|
|
477
|
-
.addEdge('tools', 'router')
|
|
478
|
-
.addConditionalEdges('router', afterRouter, { agent: 'agent' })
|
|
479
|
-
|
|
480
|
-
const compiledGraph = graph.compile({
|
|
481
|
-
checkpointer: checkpointSaver,
|
|
482
|
-
...(approvalInterruptsEnabled ? { interruptBefore: ['tools'] } : {}),
|
|
496
|
+
const compiledGraph = compileOrchestratorGraph({
|
|
497
|
+
llmWithTools,
|
|
498
|
+
allTools,
|
|
499
|
+
checkpointSaver,
|
|
500
|
+
approvalInterruptsEnabled,
|
|
501
|
+
systemMessage,
|
|
502
|
+
enableDelegateFallback: true,
|
|
483
503
|
})
|
|
484
504
|
|
|
485
|
-
// Export graph structure for introspection
|
|
486
|
-
;(compiledGraph as any).__graphStructure = {
|
|
487
|
-
nodes: ['agent', 'tools', 'router'],
|
|
488
|
-
edges: [
|
|
489
|
-
{ from: START, to: 'agent' },
|
|
490
|
-
{ from: 'agent', to: 'tools', condition: 'has_tool_calls' },
|
|
491
|
-
{ from: 'agent', to: END, condition: 'no_tool_calls' },
|
|
492
|
-
{ from: 'tools', to: 'router' },
|
|
493
|
-
{ from: 'router', to: 'agent', condition: 'fallback_or_continue' },
|
|
494
|
-
],
|
|
495
|
-
}
|
|
496
|
-
|
|
497
505
|
// Save initial user message
|
|
498
506
|
saveMessage(sessionId, 'user', task)
|
|
499
507
|
|
|
@@ -574,25 +582,26 @@ export async function executeLangGraphOrchestrator(
|
|
|
574
582
|
// OpenClaw approval bridge not available — fall through
|
|
575
583
|
}
|
|
576
584
|
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
t.pendingApproval = {
|
|
585
|
+
const t = patchTask(taskId, (current) => {
|
|
586
|
+
if (!current) return current
|
|
587
|
+
current.pendingApproval = {
|
|
581
588
|
toolName: pendingCall.name || pendingCall.function?.name || 'unknown',
|
|
582
589
|
args: pendingCall.args || (pendingCall.function?.arguments ? JSON.parse(pendingCall.function.arguments) : {}),
|
|
583
590
|
threadId,
|
|
584
591
|
}
|
|
585
|
-
|
|
586
|
-
|
|
592
|
+
current.updatedAt = Date.now()
|
|
593
|
+
return current
|
|
594
|
+
})
|
|
595
|
+
if (t) {
|
|
587
596
|
notify('tasks')
|
|
588
|
-
const approvalMsg = `[Awaiting approval] Tool: ${t.pendingApproval
|
|
597
|
+
const approvalMsg = `[Awaiting approval] Tool: ${t.pendingApproval?.toolName}\nArgs: ${JSON.stringify(t.pendingApproval?.args).slice(0, 300)}\n\nApprove or reject in the task board.`
|
|
589
598
|
saveMessage(sessionId, 'assistant', approvalMsg)
|
|
590
599
|
notify(`messages:${sessionId}`)
|
|
591
600
|
pushMainLoopEventToMainSessions({
|
|
592
601
|
type: 'pending_approval',
|
|
593
|
-
text: `Task "${t.title}" needs approval: ${t.pendingApproval
|
|
602
|
+
text: `Task "${t.title}" needs approval: ${t.pendingApproval?.toolName}(${JSON.stringify(t.pendingApproval?.args).slice(0, 100)})`,
|
|
594
603
|
})
|
|
595
|
-
console.log(`[orchestrator-lg] Interrupt: waiting for approval of tool "${t.pendingApproval
|
|
604
|
+
console.log(`[orchestrator-lg] Interrupt: waiting for approval of tool "${t.pendingApproval?.toolName}" on task ${taskId}`)
|
|
596
605
|
return approvalMsg
|
|
597
606
|
}
|
|
598
607
|
}
|
|
@@ -628,131 +637,13 @@ export async function resumeLangGraphOrchestrator(
|
|
|
628
637
|
const allAgents = loadAgents()
|
|
629
638
|
const agentIds = orchestrator.subAgentIds || []
|
|
630
639
|
const agents = agentIds.map((id) => allAgents[id]).filter(Boolean) as Agent[]
|
|
631
|
-
|
|
632
|
-
// Recreate the same tools
|
|
633
|
-
const delegateTool = tool(
|
|
634
|
-
async ({ agentName, task: agentTask }) => {
|
|
635
|
-
const agent = agents.find((a) => a.name.toLowerCase() === agentName.toLowerCase())
|
|
636
|
-
if (!agent) return `Agent "${agentName}" not found. Available: ${agents.map((a) => a.name).join(', ')}`
|
|
637
|
-
getPluginManager().runHook(
|
|
638
|
-
'onAgentDelegation',
|
|
639
|
-
{ sourceAgentId: orchestrator.id, targetAgentId: agent.id, task: agentTask },
|
|
640
|
-
{ enabledIds: orchestrator.plugins || [] },
|
|
641
|
-
)
|
|
642
|
-
const result = await executeSubTaskViaCli(agent, agentTask, sessionId)
|
|
643
|
-
saveMessage(sessionId, 'assistant', `Delegated to ${agent.name}: ${agentTask.slice(0, 100)}`, [{
|
|
644
|
-
name: 'delegate_to_agent',
|
|
645
|
-
input: JSON.stringify({ agentName: agent.name, agentId: agent.id, task: agentTask }),
|
|
646
|
-
output: result.slice(0, 2000),
|
|
647
|
-
}])
|
|
648
|
-
return result
|
|
649
|
-
},
|
|
650
|
-
{
|
|
651
|
-
name: 'delegate_to_agent',
|
|
652
|
-
description: 'Delegate a task to one of the available agents.',
|
|
653
|
-
schema: z.object({
|
|
654
|
-
agentName: z.string().describe('Name of the agent to delegate to'),
|
|
655
|
-
task: z.string().describe('The task description for the agent'),
|
|
656
|
-
}),
|
|
657
|
-
},
|
|
658
|
-
)
|
|
659
|
-
|
|
660
|
-
const db = getMemoryDb()
|
|
661
|
-
const storeMemoryTool = tool(
|
|
662
|
-
async ({ category, title, content }) => {
|
|
663
|
-
db.add({ agentId: orchestrator.id, sessionId, category, title, content })
|
|
664
|
-
return 'Memory stored successfully.'
|
|
665
|
-
},
|
|
666
|
-
{
|
|
667
|
-
name: 'store_memory',
|
|
668
|
-
description: 'Store information in long-term memory.',
|
|
669
|
-
schema: z.object({
|
|
670
|
-
category: z.string(), title: z.string(), content: z.string(),
|
|
671
|
-
}),
|
|
672
|
-
},
|
|
673
|
-
)
|
|
674
|
-
|
|
675
|
-
const searchMemoryTool = tool(
|
|
676
|
-
async ({ query }) => {
|
|
677
|
-
const results = db.search(query, orchestrator.id)
|
|
678
|
-
if (!results.length) return 'No matching memories found.'
|
|
679
|
-
return results.map((m) => `[${m.category}] ${m.title}: ${m.content.slice(0, 300)}`).join('\n')
|
|
680
|
-
},
|
|
681
|
-
{
|
|
682
|
-
name: 'search_memory',
|
|
683
|
-
description: 'Search long-term memory.',
|
|
684
|
-
schema: z.object({ query: z.string() }),
|
|
685
|
-
},
|
|
686
|
-
)
|
|
687
|
-
|
|
688
|
-
const markCompleteTool = tool(
|
|
689
|
-
async ({ summary }) => `ORCHESTRATION_COMPLETE: ${summary}`,
|
|
690
|
-
{
|
|
691
|
-
name: 'mark_complete',
|
|
692
|
-
description: 'Signal orchestration is done.',
|
|
693
|
-
schema: z.object({ summary: z.string() }),
|
|
694
|
-
},
|
|
695
|
-
)
|
|
696
|
-
|
|
697
640
|
const availableSecrets = getSecretsForOrchestrator(orchestrator.id)
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
return JSON.stringify({ name: match.name, service: match.service, value: match.value })
|
|
705
|
-
},
|
|
706
|
-
{
|
|
707
|
-
name: 'get_secret',
|
|
708
|
-
description: 'Retrieve a stored credential/secret by service name.',
|
|
709
|
-
schema: z.object({ serviceName: z.string() }),
|
|
710
|
-
},
|
|
711
|
-
)
|
|
712
|
-
|
|
713
|
-
const commentOnTaskTool = tool(
|
|
714
|
-
async ({ taskId, comment }) => {
|
|
715
|
-
const tasks = loadTasks()
|
|
716
|
-
const t = tasks[taskId]
|
|
717
|
-
if (!t) return `Task "${taskId}" not found.`
|
|
718
|
-
if (!t.comments) t.comments = []
|
|
719
|
-
t.comments.push({
|
|
720
|
-
id: genId(),
|
|
721
|
-
author: orchestrator.name,
|
|
722
|
-
agentId: orchestrator.id,
|
|
723
|
-
text: comment,
|
|
724
|
-
createdAt: Date.now(),
|
|
725
|
-
})
|
|
726
|
-
t.updatedAt = Date.now()
|
|
727
|
-
saveTasks(tasks)
|
|
728
|
-
return `Comment added to task "${t.title}".`
|
|
729
|
-
},
|
|
730
|
-
{
|
|
731
|
-
name: 'comment_on_task',
|
|
732
|
-
description: 'Add a comment to a task.',
|
|
733
|
-
schema: z.object({ taskId: z.string(), comment: z.string() }),
|
|
734
|
-
},
|
|
735
|
-
)
|
|
736
|
-
|
|
737
|
-
const createTaskTool = tool(
|
|
738
|
-
async ({ title, description: desc }) => {
|
|
739
|
-
const tasks = loadTasks()
|
|
740
|
-
const id = genId()
|
|
741
|
-
tasks[id] = {
|
|
742
|
-
id, title, description: desc, status: 'backlog',
|
|
743
|
-
agentId: orchestrator.id, sessionId: null, result: null, error: null,
|
|
744
|
-
comments: [], createdAt: Date.now(), updatedAt: Date.now(),
|
|
745
|
-
queuedAt: null, startedAt: null, completedAt: null,
|
|
746
|
-
}
|
|
747
|
-
saveTasks(tasks)
|
|
748
|
-
return `Task "${title}" created in backlog (id: ${id}).`
|
|
749
|
-
},
|
|
750
|
-
{
|
|
751
|
-
name: 'create_task',
|
|
752
|
-
description: 'Create a new task in the backlog.',
|
|
753
|
-
schema: z.object({ title: z.string(), description: z.string() }),
|
|
754
|
-
},
|
|
755
|
-
)
|
|
641
|
+
const allTools = createOrchestratorTools({
|
|
642
|
+
orchestrator,
|
|
643
|
+
agents,
|
|
644
|
+
sessionId,
|
|
645
|
+
availableSecrets,
|
|
646
|
+
})
|
|
756
647
|
|
|
757
648
|
const engine = getOrchestrationEngineConfig(orchestrator)
|
|
758
649
|
const llm = buildChatModel({
|
|
@@ -765,36 +656,13 @@ export async function resumeLangGraphOrchestrator(
|
|
|
765
656
|
const isStrictMode = settings.capabilityPolicyMode === 'strict'
|
|
766
657
|
const approvalInterruptsEnabled = isStrictMode && settings.approvalsEnabled === true
|
|
767
658
|
|
|
768
|
-
const allTools = [delegateTool, storeMemoryTool, searchMemoryTool, getSecretTool, commentOnTaskTool, createTaskTool, markCompleteTool]
|
|
769
659
|
const llmWithTools = llm.bindTools(allTools)
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
776
|
-
function routerNode(state: typeof MessagesAnnotation.State) {
|
|
777
|
-
return { messages: [] }
|
|
778
|
-
}
|
|
779
|
-
function shouldContinue(state: typeof MessagesAnnotation.State) {
|
|
780
|
-
const lastMsg = state.messages[state.messages.length - 1]
|
|
781
|
-
const toolCalls = (lastMsg as any)?.tool_calls
|
|
782
|
-
if (toolCalls && toolCalls.length > 0) return 'tools'
|
|
783
|
-
return END
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
const graphAgent = new StateGraph(MessagesAnnotation)
|
|
787
|
-
.addNode('agent', agentNode)
|
|
788
|
-
.addNode('tools', toolNode)
|
|
789
|
-
.addNode('router', routerNode)
|
|
790
|
-
.addEdge(START, 'agent')
|
|
791
|
-
.addConditionalEdges('agent', shouldContinue, { tools: 'tools', [END]: END })
|
|
792
|
-
.addEdge('tools', 'router')
|
|
793
|
-
.addEdge('router', 'agent')
|
|
794
|
-
.compile({
|
|
795
|
-
checkpointer: checkpointSaver,
|
|
796
|
-
...(approvalInterruptsEnabled ? { interruptBefore: ['tools'] } : {}),
|
|
797
|
-
})
|
|
660
|
+
const graphAgent = compileOrchestratorGraph({
|
|
661
|
+
llmWithTools,
|
|
662
|
+
allTools,
|
|
663
|
+
checkpointSaver,
|
|
664
|
+
approvalInterruptsEnabled,
|
|
665
|
+
})
|
|
798
666
|
|
|
799
667
|
let finalResult = ''
|
|
800
668
|
const runtime = loadRuntimeSettings()
|
|
@@ -838,15 +706,19 @@ export async function resumeLangGraphOrchestrator(
|
|
|
838
706
|
const tasks = loadTasks()
|
|
839
707
|
for (const t of Object.values(tasks)) {
|
|
840
708
|
if (t.pendingApproval?.threadId === threadId || t.id === threadId) {
|
|
841
|
-
t.
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
709
|
+
const updated = patchTask(t.id, (current) => {
|
|
710
|
+
if (!current) return current
|
|
711
|
+
current.pendingApproval = {
|
|
712
|
+
toolName: pendingCall.name || pendingCall.function?.name || 'unknown',
|
|
713
|
+
args: pendingCall.args || {},
|
|
714
|
+
threadId,
|
|
715
|
+
}
|
|
716
|
+
current.updatedAt = Date.now()
|
|
717
|
+
return current
|
|
718
|
+
})
|
|
719
|
+
if (!updated) continue
|
|
848
720
|
notify('tasks')
|
|
849
|
-
return `Waiting for approval: ${
|
|
721
|
+
return `Waiting for approval: ${updated.pendingApproval?.toolName}`
|
|
850
722
|
}
|
|
851
723
|
}
|
|
852
724
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { fileURLToPath, pathToFileURL } from 'url'
|
|
3
|
+
|
|
4
|
+
function normalizeBaseDir(baseDir: string): string {
|
|
5
|
+
const normalized = path.normalize(baseDir)
|
|
6
|
+
if (!normalized) return path.sep
|
|
7
|
+
return normalized
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function baseDirPrefix(baseDir: string): string {
|
|
11
|
+
const normalized = normalizeBaseDir(baseDir)
|
|
12
|
+
return normalized.endsWith(path.sep) ? normalized : `${normalized}${path.sep}`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function resolvePathWithinBaseDir(baseDir: string, targetPath: string): string {
|
|
16
|
+
const normalizedBase = normalizeBaseDir(baseDir)
|
|
17
|
+
const baseUrl = pathToFileURL(baseDirPrefix(normalizedBase))
|
|
18
|
+
const resolved = path.normalize(fileURLToPath(new URL(targetPath.replace(/\\/g, '/'), baseUrl)))
|
|
19
|
+
if (resolved !== normalizedBase && !resolved.startsWith(baseDirPrefix(normalizedBase))) {
|
|
20
|
+
throw new Error('Path traversal not allowed')
|
|
21
|
+
}
|
|
22
|
+
return resolved
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function tryResolvePathWithinBaseDir(baseDir: string, targetPath: string): string | null {
|
|
26
|
+
try {
|
|
27
|
+
return resolvePathWithinBaseDir(baseDir, targetPath)
|
|
28
|
+
} catch {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
}
|