@swarmclawai/swarmclaw 0.8.4 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/bin/swarmclaw.js +5 -1
- package/bin/worker-cmd.js +73 -0
- package/package.json +2 -1
- package/src/app/api/agents/[id]/route.ts +17 -7
- package/src/app/api/agents/route.ts +21 -8
- package/src/app/api/approvals/route.test.ts +6 -6
- package/src/app/api/approvals/route.ts +2 -1
- package/src/app/api/auth/route.ts +2 -3
- package/src/app/api/chatrooms/[id]/chat/route.test.ts +299 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +3 -2
- package/src/app/api/chatrooms/[id]/route.ts +7 -6
- package/src/app/api/chats/[id]/chat/route.test.ts +496 -0
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/clear/route.ts +9 -9
- package/src/app/api/chats/[id]/devserver/route.ts +2 -1
- package/src/app/api/chats/[id]/edit-resend/route.ts +3 -4
- package/src/app/api/chats/[id]/fork/route.ts +3 -5
- package/src/app/api/chats/[id]/restore/route.ts +6 -7
- package/src/app/api/chats/[id]/retry/route.ts +3 -4
- package/src/app/api/chats/[id]/route.ts +61 -62
- package/src/app/api/chats/route.ts +7 -1
- package/src/app/api/connectors/[id]/route.ts +7 -8
- package/src/app/api/connectors/route.ts +5 -4
- package/src/app/api/eval/run/route.ts +2 -1
- package/src/app/api/eval/suite/route.ts +2 -1
- package/src/app/api/external-agents/route.test.ts +1 -1
- package/src/app/api/external-agents/route.ts +2 -2
- package/src/app/api/files/serve/route.ts +1 -1
- package/src/app/api/gateways/[id]/route.ts +7 -5
- package/src/app/api/gateways/route.ts +1 -1
- package/src/app/api/knowledge/upload/route.ts +1 -1
- package/src/app/api/logs/route.ts +5 -7
- package/src/app/api/memory-images/[filename]/route.ts +2 -3
- package/src/app/api/openclaw/agent-files/route.ts +4 -3
- package/src/app/api/openclaw/approvals/route.ts +3 -4
- package/src/app/api/openclaw/config-sync/route.ts +3 -2
- package/src/app/api/openclaw/cron/route.ts +3 -2
- package/src/app/api/openclaw/dotenv-keys/route.ts +2 -1
- package/src/app/api/openclaw/exec-config/route.ts +3 -2
- package/src/app/api/openclaw/gateway/route.ts +5 -4
- package/src/app/api/openclaw/history/route.ts +3 -2
- package/src/app/api/openclaw/media/route.ts +2 -1
- package/src/app/api/openclaw/permissions/route.ts +3 -2
- package/src/app/api/openclaw/sandbox-env/route.ts +3 -2
- package/src/app/api/openclaw/skills/install/route.ts +2 -1
- package/src/app/api/openclaw/skills/remove/route.ts +2 -1
- package/src/app/api/openclaw/skills/route.ts +3 -2
- package/src/app/api/orchestrator/run/route.ts +5 -14
- package/src/app/api/perf/route.ts +43 -0
- package/src/app/api/plugins/dependencies/route.ts +2 -1
- package/src/app/api/plugins/install/route.ts +2 -1
- package/src/app/api/plugins/marketplace/route.ts +3 -2
- package/src/app/api/plugins/settings/route.ts +2 -1
- package/src/app/api/preview-server/route.ts +11 -10
- package/src/app/api/projects/[id]/route.ts +1 -1
- package/src/app/api/schedules/[id]/route.test.ts +128 -0
- package/src/app/api/schedules/[id]/route.ts +43 -43
- package/src/app/api/schedules/[id]/run/route.ts +11 -62
- package/src/app/api/schedules/route.ts +21 -87
- package/src/app/api/settings/route.ts +2 -0
- package/src/app/api/setup/doctor/route.ts +9 -8
- package/src/app/api/tasks/[id]/approve/route.ts +33 -30
- package/src/app/api/tasks/[id]/route.ts +12 -35
- package/src/app/api/tasks/import/github/route.ts +2 -1
- package/src/app/api/tasks/route.ts +79 -91
- package/src/app/api/wallets/[id]/approve/route.ts +2 -1
- package/src/app/api/wallets/[id]/route.ts +13 -19
- package/src/app/api/wallets/[id]/send/route.ts +2 -1
- package/src/app/api/wallets/route.ts +2 -1
- package/src/app/api/webhooks/[id]/route.ts +2 -1
- package/src/app/api/webhooks/route.test.ts +3 -1
- package/src/app/page.tsx +23 -331
- package/src/cli/index.js +19 -0
- package/src/cli/index.ts +38 -7
- package/src/cli/spec.js +9 -0
- package/src/components/activity/activity-feed.tsx +7 -4
- package/src/components/agents/agent-card.tsx +32 -6
- package/src/components/agents/agent-chat-list.tsx +55 -22
- package/src/components/agents/agent-files-editor.tsx +3 -2
- package/src/components/agents/agent-sheet.tsx +123 -22
- package/src/components/agents/inspector-panel.tsx +1 -1
- package/src/components/agents/openclaw-skills-panel.tsx +2 -1
- package/src/components/agents/trash-list.tsx +1 -1
- package/src/components/auth/access-key-gate.tsx +8 -2
- package/src/components/auth/setup-wizard.tsx +10 -9
- package/src/components/auth/user-picker.tsx +3 -2
- package/src/components/chat/chat-area.tsx +20 -1
- package/src/components/chat/chat-card.tsx +18 -3
- package/src/components/chat/chat-header.tsx +24 -4
- package/src/components/chat/chat-list.tsx +2 -11
- package/src/components/chat/heartbeat-history-panel.tsx +2 -1
- package/src/components/chat/message-bubble.tsx +45 -6
- package/src/components/chat/message-list.tsx +280 -145
- package/src/components/chat/streaming-bubble.tsx +217 -60
- package/src/components/chat/swarm-panel.test.ts +274 -0
- package/src/components/chat/swarm-panel.tsx +410 -0
- package/src/components/chat/swarm-status-card.tsx +346 -0
- package/src/components/chat/tool-call-bubble.tsx +48 -23
- package/src/components/chatrooms/chatroom-list.tsx +8 -5
- package/src/components/chatrooms/chatroom-message.tsx +10 -7
- package/src/components/chatrooms/chatroom-view.tsx +12 -9
- package/src/components/connectors/connector-health.tsx +6 -4
- package/src/components/connectors/connector-list.tsx +16 -11
- package/src/components/connectors/connector-sheet.tsx +12 -6
- package/src/components/home/home-view.tsx +38 -24
- package/src/components/input/chat-input.tsx +10 -1
- package/src/components/layout/app-layout.tsx +2 -38
- package/src/components/layout/sheet-layer.tsx +50 -0
- package/src/components/mcp-servers/mcp-server-list.tsx +37 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +12 -2
- package/src/components/plugins/plugin-list.tsx +8 -4
- package/src/components/plugins/plugin-sheet.tsx +2 -1
- package/src/components/providers/provider-list.tsx +3 -2
- package/src/components/providers/provider-sheet.tsx +2 -1
- package/src/components/runs/run-list.tsx +11 -7
- package/src/components/schedules/schedule-card.tsx +5 -3
- package/src/components/shared/agent-switch-dialog.tsx +1 -1
- package/src/components/shared/attachment-chip.tsx +19 -3
- package/src/components/shared/notification-center.tsx +6 -3
- package/src/components/shared/settings/plugin-manager.tsx +3 -2
- package/src/components/shared/settings/section-embedding.tsx +2 -1
- package/src/components/shared/settings/section-orchestrator.tsx +2 -1
- package/src/components/shared/settings/section-user-preferences.tsx +107 -0
- package/src/components/shared/settings/settings-page.tsx +13 -9
- package/src/components/skills/clawhub-browser.tsx +15 -4
- package/src/components/skills/skill-list.tsx +15 -4
- package/src/components/tasks/approvals-panel.tsx +2 -1
- package/src/components/tasks/task-board.tsx +35 -37
- package/src/components/tasks/task-sheet.tsx +4 -3
- package/src/components/ui/full-screen-loader.tsx +164 -0
- package/src/components/wallets/wallet-approval-dialog.tsx +2 -1
- package/src/components/wallets/wallet-panel.tsx +6 -5
- package/src/components/wallets/wallet-section.tsx +3 -2
- package/src/components/webhooks/webhook-list.tsx +4 -5
- package/src/components/webhooks/webhook-sheet.tsx +6 -6
- package/src/hooks/use-app-bootstrap.ts +202 -0
- package/src/hooks/use-mounted-ref.ts +14 -0
- package/src/hooks/use-now.ts +31 -0
- package/src/hooks/use-openclaw-gateway.ts +2 -1
- package/src/instrumentation.ts +20 -8
- package/src/lib/agent-default-tools.test.ts +52 -0
- package/src/lib/agent-default-tools.ts +40 -0
- package/src/lib/api-client.test.ts +21 -0
- package/src/lib/api-client.ts +6 -11
- package/src/lib/canvas-content.test.ts +360 -0
- package/src/lib/chat-streaming-state.test.ts +49 -2
- package/src/lib/chat-streaming-state.ts +26 -10
- package/src/lib/fetch-timeout.test.ts +54 -0
- package/src/lib/fetch-timeout.ts +60 -3
- package/src/lib/live-tool-events.test.ts +77 -0
- package/src/lib/live-tool-events.ts +73 -0
- package/src/lib/local-observability.test.ts +2 -2
- package/src/lib/openclaw-endpoint.test.ts +1 -1
- package/src/lib/providers/anthropic.ts +12 -16
- package/src/lib/providers/index.ts +4 -2
- package/src/lib/providers/ollama.ts +9 -6
- package/src/lib/providers/openai.ts +11 -14
- package/src/lib/runtime-env.test.ts +8 -8
- package/src/lib/schedule-dedupe-advanced.test.ts +2 -2
- package/src/lib/schedule-dedupe.test.ts +1 -1
- package/src/lib/schedule-dedupe.ts +3 -2
- package/src/lib/server/agent-thread-session.test.ts +6 -6
- package/src/lib/server/agent-thread-session.ts +6 -9
- package/src/lib/server/alert-dispatch.ts +2 -1
- package/src/lib/server/api-routes.test.ts +6 -6
- package/src/lib/server/approval-connector-notify.test.ts +4 -4
- package/src/lib/server/approvals-auto-approve.test.ts +29 -29
- package/src/lib/server/approvals.test.ts +317 -0
- package/src/lib/server/approvals.ts +5 -4
- package/src/lib/server/autonomy-runtime.test.ts +11 -11
- package/src/lib/server/browser-state.ts +2 -2
- package/src/lib/server/capability-router.test.ts +1 -1
- package/src/lib/server/capability-router.ts +3 -2
- package/src/lib/server/chat-execution-advanced.test.ts +15 -2
- package/src/lib/server/chat-execution-connector-delivery.ts +67 -0
- package/src/lib/server/chat-execution-disabled.test.ts +3 -3
- package/src/lib/server/chat-execution-eval-history.test.ts +3 -3
- package/src/lib/server/chat-execution-heartbeat.test.ts +42 -1
- package/src/lib/server/chat-execution-session-sync.test.ts +119 -0
- package/src/lib/server/chat-execution-tool-events.ts +116 -0
- package/src/lib/server/chat-execution-utils.test.ts +479 -0
- package/src/lib/server/chat-execution-utils.ts +533 -0
- package/src/lib/server/chat-execution.ts +153 -748
- package/src/lib/server/chat-streaming-utils.ts +174 -0
- package/src/lib/server/chat-turn-tool-routing.ts +310 -0
- package/src/lib/server/chatroom-session-persistence.test.ts +2 -2
- package/src/lib/server/clawhub-client.ts +2 -1
- package/src/lib/server/collection-helpers.test.ts +92 -0
- package/src/lib/server/collection-helpers.ts +25 -3
- package/src/lib/server/connectors/access.ts +146 -0
- package/src/lib/server/connectors/bluebubbles.test.ts +1 -1
- package/src/lib/server/connectors/bluebubbles.ts +4 -4
- package/src/lib/server/connectors/commands.ts +367 -0
- package/src/lib/server/connectors/connector-routing.test.ts +4 -4
- package/src/lib/server/connectors/delivery.ts +142 -0
- package/src/lib/server/connectors/discord.ts +37 -40
- package/src/lib/server/connectors/email.ts +11 -10
- package/src/lib/server/connectors/googlechat.ts +4 -4
- package/src/lib/server/connectors/inbound-audio-transcription.ts +2 -1
- package/src/lib/server/connectors/ingress-delivery.ts +23 -0
- package/src/lib/server/connectors/manager-roundtrip.test.ts +300 -0
- package/src/lib/server/connectors/manager.test.ts +352 -77
- package/src/lib/server/connectors/manager.ts +134 -673
- package/src/lib/server/connectors/matrix.ts +4 -4
- package/src/lib/server/connectors/message-sentinel.ts +7 -0
- package/src/lib/server/connectors/openclaw.test.ts +1 -1
- package/src/lib/server/connectors/openclaw.ts +8 -10
- package/src/lib/server/connectors/outbox.test.ts +192 -0
- package/src/lib/server/connectors/outbox.ts +369 -0
- package/src/lib/server/connectors/pairing.test.ts +18 -1
- package/src/lib/server/connectors/pairing.ts +49 -4
- package/src/lib/server/connectors/policy.ts +9 -3
- package/src/lib/server/connectors/reconnect-state.ts +71 -0
- package/src/lib/server/connectors/response-media.ts +256 -0
- package/src/lib/server/connectors/runtime-state.ts +67 -0
- package/src/lib/server/connectors/session.test.ts +357 -0
- package/src/lib/server/connectors/session.ts +422 -0
- package/src/lib/server/connectors/signal.ts +7 -7
- package/src/lib/server/connectors/slack.ts +43 -43
- package/src/lib/server/connectors/teams.ts +4 -4
- package/src/lib/server/connectors/telegram.ts +37 -43
- package/src/lib/server/connectors/types.ts +31 -1
- package/src/lib/server/connectors/whatsapp.test.ts +108 -0
- package/src/lib/server/connectors/whatsapp.ts +106 -34
- package/src/lib/server/context-manager.test.ts +409 -0
- package/src/lib/server/cost.test.ts +1 -1
- package/src/lib/server/daemon-policy.ts +78 -0
- package/src/lib/server/daemon-state-connectors.test.ts +167 -0
- package/src/lib/server/daemon-state.test.ts +283 -55
- package/src/lib/server/daemon-state.ts +106 -109
- package/src/lib/server/data-dir.test.ts +5 -5
- package/src/lib/server/data-dir.ts +4 -0
- package/src/lib/server/delegation-jobs-advanced.test.ts +1 -1
- package/src/lib/server/delegation-jobs.test.ts +87 -0
- package/src/lib/server/delegation-jobs.ts +42 -48
- package/src/lib/server/devserver-launch.ts +1 -1
- package/src/lib/server/document-utils.ts +7 -9
- package/src/lib/server/elevenlabs.ts +2 -1
- package/src/lib/server/embeddings.test.ts +105 -0
- package/src/lib/server/ethereum.ts +3 -2
- package/src/lib/server/eval/agent-regression.ts +3 -2
- package/src/lib/server/eval/runner.ts +2 -1
- package/src/lib/server/eval/scorer.ts +2 -1
- package/src/lib/server/evm-swap.ts +2 -1
- package/src/lib/server/gateway/protocol.test.ts +1 -1
- package/src/lib/server/guardian.ts +2 -1
- package/src/lib/server/heartbeat-blocked-suppression.test.ts +151 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +6 -6
- package/src/lib/server/heartbeat-service.test.ts +406 -0
- package/src/lib/server/heartbeat-service.ts +54 -7
- package/src/lib/server/heartbeat-wake.test.ts +19 -0
- package/src/lib/server/heartbeat-wake.ts +17 -16
- package/src/lib/server/integrity-monitor.test.ts +149 -0
- package/src/lib/server/json-utils.ts +22 -0
- package/src/lib/server/knowledge-db.test.ts +13 -13
- package/src/lib/server/link-understanding.ts +2 -1
- package/src/lib/server/llm-response-cache.test.ts +1 -1
- package/src/lib/server/main-agent-loop-advanced.test.ts +65 -3
- package/src/lib/server/main-agent-loop.test.ts +6 -6
- package/src/lib/server/main-agent-loop.ts +21 -7
- package/src/lib/server/mcp-client.test.ts +1 -1
- package/src/lib/server/mcp-conformance.test.ts +1 -1
- package/src/lib/server/mcp-conformance.ts +3 -2
- package/src/lib/server/memory-consolidation.ts +2 -1
- package/src/lib/server/memory-db.test.ts +485 -0
- package/src/lib/server/memory-db.ts +39 -26
- package/src/lib/server/memory-graph.test.ts +2 -2
- package/src/lib/server/memory-policy.test.ts +7 -7
- package/src/lib/server/memory-retrieval.test.ts +1 -1
- package/src/lib/server/openclaw-config-sync.ts +2 -1
- package/src/lib/server/openclaw-deploy.test.ts +1 -1
- package/src/lib/server/openclaw-deploy.ts +8 -12
- package/src/lib/server/openclaw-exec-config.ts +2 -1
- package/src/lib/server/openclaw-gateway.ts +6 -7
- package/src/lib/server/openclaw-skills-normalize.ts +2 -1
- package/src/lib/server/openclaw-sync.ts +7 -5
- package/src/lib/server/orchestrator-lg-structure.test.ts +17 -0
- package/src/lib/server/orchestrator-lg.ts +199 -327
- package/src/lib/server/path-utils.ts +31 -0
- package/src/lib/server/perf.ts +161 -0
- package/src/lib/server/plugins-approval-guidance.ts +115 -0
- package/src/lib/server/plugins.test.ts +1 -1
- package/src/lib/server/plugins.ts +22 -132
- package/src/lib/server/process-manager.ts +5 -8
- package/src/lib/server/provider-health.test.ts +137 -0
- package/src/lib/server/provider-health.ts +3 -3
- package/src/lib/server/provider-model-discovery.ts +3 -12
- package/src/lib/server/queue-followups.test.ts +9 -9
- package/src/lib/server/queue-reconcile.test.ts +2 -2
- package/src/lib/server/queue-recovery.test.ts +269 -0
- package/src/lib/server/queue.test.ts +570 -0
- package/src/lib/server/queue.ts +62 -455
- package/src/lib/server/resolve-image.ts +30 -0
- package/src/lib/server/runtime-settings.test.ts +4 -4
- package/src/lib/server/runtime-storage-write-paths.test.ts +60 -0
- package/src/lib/server/schedule-normalization.test.ts +279 -0
- package/src/lib/server/schedule-service.ts +263 -0
- package/src/lib/server/scheduler.ts +17 -74
- package/src/lib/server/session-mailbox.test.ts +191 -0
- package/src/lib/server/session-run-manager.test.ts +640 -0
- package/src/lib/server/session-run-manager.ts +59 -15
- package/src/lib/server/session-tools/autonomy-tools.test.ts +20 -20
- package/src/lib/server/session-tools/calendar.ts +2 -1
- package/src/lib/server/session-tools/canvas.ts +2 -1
- package/src/lib/server/session-tools/chatroom.ts +2 -1
- package/src/lib/server/session-tools/connector.ts +26 -28
- package/src/lib/server/session-tools/context-mgmt.ts +3 -2
- package/src/lib/server/session-tools/crawl.ts +4 -3
- package/src/lib/server/session-tools/crud.ts +105 -324
- package/src/lib/server/session-tools/delegate-fallback.test.ts +9 -9
- package/src/lib/server/session-tools/delegate.ts +6 -8
- package/src/lib/server/session-tools/discovery-approvals.test.ts +15 -15
- package/src/lib/server/session-tools/discovery.ts +4 -3
- package/src/lib/server/session-tools/document.ts +2 -1
- package/src/lib/server/session-tools/email.ts +2 -1
- package/src/lib/server/session-tools/extract.ts +2 -1
- package/src/lib/server/session-tools/file.ts +4 -3
- package/src/lib/server/session-tools/http.ts +2 -1
- package/src/lib/server/session-tools/human-loop.ts +2 -1
- package/src/lib/server/session-tools/image-gen.ts +4 -3
- package/src/lib/server/session-tools/index.ts +26 -30
- package/src/lib/server/session-tools/mailbox.ts +2 -1
- package/src/lib/server/session-tools/manage-connectors.test.ts +4 -4
- package/src/lib/server/session-tools/manage-schedules.test.ts +12 -12
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +5 -5
- package/src/lib/server/session-tools/manage-tasks.test.ts +2 -2
- package/src/lib/server/session-tools/monitor.ts +2 -1
- package/src/lib/server/session-tools/platform.ts +2 -1
- package/src/lib/server/session-tools/plugin-creator.ts +2 -1
- package/src/lib/server/session-tools/replicate.ts +3 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +6 -6
- package/src/lib/server/session-tools/shell.ts +4 -9
- package/src/lib/server/session-tools/subagent.ts +322 -170
- package/src/lib/server/session-tools/table.ts +6 -5
- package/src/lib/server/session-tools/wallet-tool.test.ts +3 -3
- package/src/lib/server/session-tools/wallet.ts +7 -6
- package/src/lib/server/session-tools/web-browser-config.test.ts +1 -0
- package/src/lib/server/session-tools/web-utils.ts +317 -0
- package/src/lib/server/session-tools/web.ts +62 -328
- package/src/lib/server/skill-prompt-budget.test.ts +1 -1
- package/src/lib/server/skills-normalize.ts +2 -1
- package/src/lib/server/storage-item-access.test.ts +302 -0
- package/src/lib/server/storage.ts +366 -314
- package/src/lib/server/stream-agent-chat.test.ts +82 -3
- package/src/lib/server/stream-agent-chat.ts +146 -510
- package/src/lib/server/stream-continuation.ts +412 -0
- package/src/lib/server/subagent-lineage.test.ts +647 -0
- package/src/lib/server/subagent-lineage.ts +435 -0
- package/src/lib/server/subagent-runtime.test.ts +484 -0
- package/src/lib/server/subagent-runtime.ts +419 -0
- package/src/lib/server/subagent-swarm.test.ts +391 -0
- package/src/lib/server/subagent-swarm.ts +564 -0
- package/src/lib/server/system-events.ts +3 -3
- package/src/lib/server/task-followups.test.ts +491 -0
- package/src/lib/server/task-followups.ts +391 -0
- package/src/lib/server/task-lifecycle.test.ts +205 -0
- package/src/lib/server/task-lifecycle.ts +200 -0
- package/src/lib/server/task-quality-gate.test.ts +1 -1
- package/src/lib/server/task-resume.ts +208 -0
- package/src/lib/server/task-service.test.ts +108 -0
- package/src/lib/server/task-service.ts +264 -0
- package/src/lib/server/task-validation.test.ts +1 -1
- package/src/lib/server/test-utils/run-with-temp-data-dir.ts +42 -0
- package/src/lib/server/tool-capability-policy.test.ts +2 -2
- package/src/lib/server/tool-capability-policy.ts +3 -2
- package/src/lib/server/tool-planning.ts +2 -1
- package/src/lib/server/tool-retry.ts +2 -3
- package/src/lib/server/wake-dispatcher.test.ts +303 -0
- package/src/lib/server/wake-dispatcher.ts +318 -0
- package/src/lib/server/wake-mode.test.ts +161 -0
- package/src/lib/server/wake-mode.ts +174 -0
- package/src/lib/server/wallet-service.ts +8 -9
- package/src/lib/server/watch-jobs.ts +2 -1
- package/src/lib/server/workspace-context.ts +2 -2
- package/src/lib/shared-utils.test.ts +142 -0
- package/src/lib/shared-utils.ts +62 -0
- package/src/lib/tool-event-summary.ts +2 -1
- package/src/lib/view-routes.test.ts +100 -0
- package/src/lib/wallet.test.ts +322 -6
- package/src/proxy.test.ts +4 -4
- package/src/proxy.ts +2 -3
- package/src/stores/set-if-changed.ts +40 -0
- package/src/stores/slices/agent-slice.ts +111 -0
- package/src/stores/slices/auth-slice.ts +25 -0
- package/src/stores/slices/data-slice.ts +301 -0
- package/src/stores/slices/index.ts +7 -0
- package/src/stores/slices/session-slice.ts +112 -0
- package/src/stores/slices/task-slice.ts +63 -0
- package/src/stores/slices/ui-slice.ts +192 -0
- package/src/stores/use-app-store.ts +17 -822
- package/src/stores/use-approval-store.ts +2 -1
- package/src/stores/use-chat-store.ts +8 -1
- package/src/types/index.ts +10 -0
|
@@ -1,39 +1,9 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
|
-
import fs from 'node:fs'
|
|
3
|
-
import os from 'node:os'
|
|
4
|
-
import path from 'node:path'
|
|
5
|
-
import { spawnSync } from 'node:child_process'
|
|
6
2
|
import { describe, it } from 'node:test'
|
|
3
|
+
import { runWithTempDataDir } from '../test-utils/run-with-temp-data-dir'
|
|
7
4
|
|
|
8
5
|
import { sanitizeConnectorOutboundContent } from './manager'
|
|
9
6
|
|
|
10
|
-
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../../..')
|
|
11
|
-
|
|
12
|
-
function runWithTempDataDir(script: string) {
|
|
13
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-manager-test-'))
|
|
14
|
-
try {
|
|
15
|
-
const result = spawnSync(process.execPath, ['--import', 'tsx', '--input-type=module', '--eval', script], {
|
|
16
|
-
cwd: repoRoot,
|
|
17
|
-
env: {
|
|
18
|
-
...process.env,
|
|
19
|
-
DATA_DIR: tempDir,
|
|
20
|
-
WORKSPACE_DIR: path.join(tempDir, 'workspace'),
|
|
21
|
-
},
|
|
22
|
-
encoding: 'utf-8',
|
|
23
|
-
})
|
|
24
|
-
assert.equal(result.status, 0, result.stderr || result.stdout || 'subprocess failed')
|
|
25
|
-
const lines = (result.stdout || '')
|
|
26
|
-
.trim()
|
|
27
|
-
.split('\n')
|
|
28
|
-
.map((line) => line.trim())
|
|
29
|
-
.filter(Boolean)
|
|
30
|
-
const jsonLine = [...lines].reverse().find((line) => line.startsWith('{'))
|
|
31
|
-
return JSON.parse(jsonLine || '{}')
|
|
32
|
-
} finally {
|
|
33
|
-
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
7
|
describe('sanitizeConnectorOutboundContent', () => {
|
|
38
8
|
it('strips hidden control tokens from captions without suppressing the media send itself', () => {
|
|
39
9
|
assert.deepEqual(
|
|
@@ -67,9 +37,9 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
67
37
|
|
|
68
38
|
it('mirrors direct WhatsApp inbound and assistant replies into the session transcript', () => {
|
|
69
39
|
const output = runWithTempDataDir(`
|
|
70
|
-
const storageMod = await import('./src/lib/server/storage
|
|
71
|
-
const managerMod = await import('./src/lib/server/connectors/manager
|
|
72
|
-
const providersMod = await import('./src/lib/providers/index
|
|
40
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
41
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
42
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
73
43
|
const storage = storageMod.default || storageMod
|
|
74
44
|
const manager = managerMod.default || managerMod
|
|
75
45
|
const providers = providersMod.default || providersMod
|
|
@@ -169,11 +139,114 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
169
139
|
assert.equal(output.mainSession.messages[1].historyExcluded, true)
|
|
170
140
|
})
|
|
171
141
|
|
|
142
|
+
it('queues connector heartbeat wakes on the agent thread session and records the system event there', () => {
|
|
143
|
+
const output = runWithTempDataDir(`
|
|
144
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
145
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
146
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
147
|
+
const heartbeatMod = await import('./src/lib/server/heartbeat-wake')
|
|
148
|
+
const systemEventsMod = await import('./src/lib/server/system-events')
|
|
149
|
+
const storage = storageMod.default || storageMod
|
|
150
|
+
const manager = managerMod.default || managerMod
|
|
151
|
+
const providers = providersMod.default || providersMod
|
|
152
|
+
const heartbeat = heartbeatMod.default || heartbeatMod
|
|
153
|
+
const systemEvents = systemEventsMod.default || systemEventsMod
|
|
154
|
+
|
|
155
|
+
const now = Date.now()
|
|
156
|
+
heartbeat.resetHeartbeatWakeStateForTests()
|
|
157
|
+
providers.PROVIDERS['test-provider'] = {
|
|
158
|
+
id: 'test-provider',
|
|
159
|
+
name: 'Test Provider',
|
|
160
|
+
models: ['test-model'],
|
|
161
|
+
requiresApiKey: false,
|
|
162
|
+
requiresEndpoint: false,
|
|
163
|
+
handler: {
|
|
164
|
+
streamChat: async (opts) => {
|
|
165
|
+
opts.write('data: ' + JSON.stringify({ t: 'r', text: 'Heartbeat target check' }) + '\\n')
|
|
166
|
+
return ''
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
storage.saveSettings({})
|
|
172
|
+
storage.saveAgents({
|
|
173
|
+
agent_1: {
|
|
174
|
+
id: 'agent_1',
|
|
175
|
+
name: 'Molly',
|
|
176
|
+
provider: 'test-provider',
|
|
177
|
+
model: 'test-model',
|
|
178
|
+
plugins: [],
|
|
179
|
+
threadSessionId: 'agent_thread',
|
|
180
|
+
createdAt: now,
|
|
181
|
+
updatedAt: now,
|
|
182
|
+
},
|
|
183
|
+
})
|
|
184
|
+
storage.saveConnectors({
|
|
185
|
+
conn_1: {
|
|
186
|
+
id: 'conn_1',
|
|
187
|
+
name: 'WhatsApp',
|
|
188
|
+
platform: 'whatsapp',
|
|
189
|
+
agentId: 'agent_1',
|
|
190
|
+
credentialId: null,
|
|
191
|
+
config: { inboundDebounceMs: 0 },
|
|
192
|
+
isEnabled: true,
|
|
193
|
+
status: 'running',
|
|
194
|
+
createdAt: now,
|
|
195
|
+
updatedAt: now,
|
|
196
|
+
},
|
|
197
|
+
})
|
|
198
|
+
storage.saveSessions({
|
|
199
|
+
agent_thread: {
|
|
200
|
+
id: 'agent_thread',
|
|
201
|
+
name: 'Molly',
|
|
202
|
+
cwd: process.env.WORKSPACE_DIR,
|
|
203
|
+
user: 'default',
|
|
204
|
+
provider: 'test-provider',
|
|
205
|
+
model: 'test-model',
|
|
206
|
+
claudeSessionId: null,
|
|
207
|
+
messages: [],
|
|
208
|
+
createdAt: now,
|
|
209
|
+
lastActiveAt: now,
|
|
210
|
+
sessionType: 'human',
|
|
211
|
+
agentId: 'agent_1',
|
|
212
|
+
plugins: [],
|
|
213
|
+
},
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
const connector = storage.loadConnectors().conn_1
|
|
217
|
+
await manager.routeConnectorMessageForTest(connector, {
|
|
218
|
+
platform: 'whatsapp',
|
|
219
|
+
channelId: '15550001111@s.whatsapp.net',
|
|
220
|
+
senderId: '15550001111@s.whatsapp.net',
|
|
221
|
+
senderName: 'Alice',
|
|
222
|
+
text: 'Did you get this?',
|
|
223
|
+
messageId: 'in-thread-target',
|
|
224
|
+
isGroup: false,
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
const sessions = storage.loadSessions()
|
|
228
|
+
const directSession = Object.values(sessions).find((entry) => entry.id !== 'agent_thread')
|
|
229
|
+
console.log(JSON.stringify({
|
|
230
|
+
wake: heartbeat.snapshotPendingHeartbeatWakesForTests()[0] || null,
|
|
231
|
+
threadEvents: systemEvents.peekSystemEvents('agent_thread'),
|
|
232
|
+
directEvents: directSession ? systemEvents.peekSystemEvents(directSession.id) : [],
|
|
233
|
+
directSessionId: directSession?.id || null,
|
|
234
|
+
}))
|
|
235
|
+
`)
|
|
236
|
+
|
|
237
|
+
assert.equal(output.wake.sessionId, 'agent_thread')
|
|
238
|
+
assert.equal(output.wake.agentId, 'agent_1')
|
|
239
|
+
assert.equal(output.threadEvents.length, 1)
|
|
240
|
+
assert.match(output.threadEvents[0].text, /Inbound message from whatsapp: Did you get this\?/i)
|
|
241
|
+
assert.equal(output.directEvents.length, 0)
|
|
242
|
+
assert.ok(output.directSessionId)
|
|
243
|
+
})
|
|
244
|
+
|
|
172
245
|
it('mirrors same-channel connector_message_tool sends when the agent suppresses visible text', () => {
|
|
173
246
|
const output = runWithTempDataDir(`
|
|
174
|
-
const storageMod = await import('./src/lib/server/storage
|
|
175
|
-
const managerMod = await import('./src/lib/server/connectors/manager
|
|
176
|
-
const providersMod = await import('./src/lib/providers/index
|
|
247
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
248
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
249
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
177
250
|
const storage = storageMod.default || storageMod
|
|
178
251
|
const manager = managerMod.default || managerMod
|
|
179
252
|
const providers = providersMod.default || providersMod
|
|
@@ -276,14 +349,14 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
276
349
|
assert.equal(output.directSession.messages[1].source.messageId, 'wa-out-1')
|
|
277
350
|
assert.equal(output.directSession.connectorContext.lastOutboundMessageId, 'wa-out-1')
|
|
278
351
|
assert.equal(output.mainSession.messages.length, 2)
|
|
279
|
-
assert.equal(output.mainSession.messages.every((entry) => entry.historyExcluded === true), true)
|
|
352
|
+
assert.equal(output.mainSession.messages.every((entry: any) => entry.historyExcluded === true), true)
|
|
280
353
|
})
|
|
281
354
|
|
|
282
355
|
it('accepts WhatsApp allowlist matches through senderIdAlt when the primary sender id is a lid', () => {
|
|
283
356
|
const output = runWithTempDataDir(`
|
|
284
|
-
const storageMod = await import('./src/lib/server/storage
|
|
285
|
-
const managerMod = await import('./src/lib/server/connectors/manager
|
|
286
|
-
const providersMod = await import('./src/lib/providers/index
|
|
357
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
358
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
359
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
287
360
|
const storage = storageMod.default || storageMod
|
|
288
361
|
const manager = managerMod.default || managerMod
|
|
289
362
|
const providers = providersMod.default || providersMod
|
|
@@ -357,14 +430,114 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
357
430
|
assert.equal(output.session.messages[0].source.messageId, 'in-3')
|
|
358
431
|
})
|
|
359
432
|
|
|
433
|
+
it('reuses the same direct WhatsApp session when a contact flips from lid to phone jid', () => {
|
|
434
|
+
const output = runWithTempDataDir(`
|
|
435
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
436
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
437
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
438
|
+
const storage = storageMod.default || storageMod
|
|
439
|
+
const manager = managerMod.default || managerMod
|
|
440
|
+
const providers = providersMod.default || providersMod
|
|
441
|
+
|
|
442
|
+
const now = Date.now()
|
|
443
|
+
providers.PROVIDERS['test-provider'] = {
|
|
444
|
+
id: 'test-provider',
|
|
445
|
+
name: 'Test Provider',
|
|
446
|
+
models: ['test-model'],
|
|
447
|
+
requiresApiKey: false,
|
|
448
|
+
requiresEndpoint: false,
|
|
449
|
+
handler: {
|
|
450
|
+
streamChat: async (opts) => {
|
|
451
|
+
opts.write('data: ' + JSON.stringify({ t: 'r', text: 'Reply: ' + String(opts.message || '').slice(0, 32) }) + '\\n')
|
|
452
|
+
return ''
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
storage.saveSettings({})
|
|
458
|
+
storage.saveAgents({
|
|
459
|
+
agent_1: {
|
|
460
|
+
id: 'agent_1',
|
|
461
|
+
name: 'Molly',
|
|
462
|
+
provider: 'test-provider',
|
|
463
|
+
model: 'test-model',
|
|
464
|
+
plugins: [],
|
|
465
|
+
createdAt: now,
|
|
466
|
+
updatedAt: now,
|
|
467
|
+
},
|
|
468
|
+
})
|
|
469
|
+
storage.saveConnectors({
|
|
470
|
+
conn_1: {
|
|
471
|
+
id: 'conn_1',
|
|
472
|
+
name: 'WhatsApp',
|
|
473
|
+
platform: 'whatsapp',
|
|
474
|
+
agentId: 'agent_1',
|
|
475
|
+
credentialId: null,
|
|
476
|
+
config: { inboundDebounceMs: 0 },
|
|
477
|
+
isEnabled: true,
|
|
478
|
+
status: 'running',
|
|
479
|
+
createdAt: now,
|
|
480
|
+
updatedAt: now,
|
|
481
|
+
},
|
|
482
|
+
})
|
|
483
|
+
storage.saveSessions({})
|
|
484
|
+
|
|
485
|
+
const connector = storage.loadConnectors().conn_1
|
|
486
|
+
await manager.routeConnectorMessageForTest(connector, {
|
|
487
|
+
platform: 'whatsapp',
|
|
488
|
+
channelId: '199900000001@lid',
|
|
489
|
+
channelIdAlt: '15550001111@s.whatsapp.net',
|
|
490
|
+
senderId: '199900000001@lid',
|
|
491
|
+
senderIdAlt: '15550001111@s.whatsapp.net',
|
|
492
|
+
senderName: 'Alice',
|
|
493
|
+
text: 'First lid message',
|
|
494
|
+
messageId: 'in-lid',
|
|
495
|
+
isGroup: false,
|
|
496
|
+
})
|
|
497
|
+
await manager.routeConnectorMessageForTest(connector, {
|
|
498
|
+
platform: 'whatsapp',
|
|
499
|
+
channelId: '15550001111@s.whatsapp.net',
|
|
500
|
+
senderId: '15550001111@s.whatsapp.net',
|
|
501
|
+
senderName: 'Alice',
|
|
502
|
+
text: 'Second phone-jid message',
|
|
503
|
+
messageId: 'in-phone',
|
|
504
|
+
isGroup: false,
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
const directSessions = Object.values(storage.loadSessions())
|
|
508
|
+
.filter((entry) => String(entry.name || '').startsWith('connector:'))
|
|
509
|
+
.map((entry) => ({
|
|
510
|
+
id: entry.id,
|
|
511
|
+
name: entry.name,
|
|
512
|
+
connectorContext: entry.connectorContext,
|
|
513
|
+
messages: entry.messages.map((message) => ({
|
|
514
|
+
role: message.role,
|
|
515
|
+
text: message.text,
|
|
516
|
+
source: message.source || null,
|
|
517
|
+
})),
|
|
518
|
+
}))
|
|
519
|
+
console.log(JSON.stringify({ directSessions }))
|
|
520
|
+
`)
|
|
521
|
+
|
|
522
|
+
assert.equal(output.directSessions.length, 1)
|
|
523
|
+
assert.equal(output.directSessions[0].messages.length, 4)
|
|
524
|
+
assert.equal(output.directSessions[0].connectorContext.channelId, '15550001111@s.whatsapp.net')
|
|
525
|
+
assert.equal(output.directSessions[0].connectorContext.channelIdAlt, '15550001111@s.whatsapp.net')
|
|
526
|
+
assert.equal(output.directSessions[0].connectorContext.senderIdAlt, '15550001111@s.whatsapp.net')
|
|
527
|
+
assert.deepEqual(
|
|
528
|
+
output.directSessions[0].messages.filter((message: { role: string }) => message.role === 'user').map((message: { source: { channelId?: string | null } | null }) => message.source?.channelId),
|
|
529
|
+
['199900000001@lid', '15550001111@s.whatsapp.net'],
|
|
530
|
+
)
|
|
531
|
+
})
|
|
532
|
+
|
|
360
533
|
it('routes send_voice_note to the current connector conversation when an audio file already exists', () => {
|
|
361
534
|
const output = runWithTempDataDir(`
|
|
362
535
|
const fs = await import('node:fs')
|
|
363
536
|
const path = await import('node:path')
|
|
364
|
-
const storageMod = await import('./src/lib/server/storage
|
|
365
|
-
const managerMod = await import('./src/lib/server/connectors/manager
|
|
366
|
-
const pluginsMod = await import('./src/lib/server/plugins
|
|
367
|
-
const toolsMod = await import('./src/lib/server/session-tools/index
|
|
537
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
538
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
539
|
+
const pluginsMod = await import('./src/lib/server/plugins')
|
|
540
|
+
const toolsMod = await import('./src/lib/server/session-tools/index')
|
|
368
541
|
const storage = storageMod.default || storageMod
|
|
369
542
|
const manager = managerMod.default || managerMod
|
|
370
543
|
const plugins = pluginsMod.default || pluginsMod
|
|
@@ -483,9 +656,9 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
483
656
|
|
|
484
657
|
it('restarts a stale connector automatically when an outbound send fails with connection closed', () => {
|
|
485
658
|
const output = runWithTempDataDir(`
|
|
486
|
-
const storageMod = await import('./src/lib/server/storage
|
|
487
|
-
const managerMod = await import('./src/lib/server/connectors/manager
|
|
488
|
-
const pluginsMod = await import('./src/lib/server/plugins
|
|
659
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
660
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
661
|
+
const pluginsMod = await import('./src/lib/server/plugins')
|
|
489
662
|
const storage = storageMod.default || storageMod
|
|
490
663
|
const manager = managerMod.default || managerMod
|
|
491
664
|
const plugins = pluginsMod.default || pluginsMod
|
|
@@ -551,10 +724,10 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
551
724
|
const output = runWithTempDataDir(`
|
|
552
725
|
const fs = await import('node:fs')
|
|
553
726
|
const path = await import('node:path')
|
|
554
|
-
const storageMod = await import('./src/lib/server/storage
|
|
555
|
-
const managerMod = await import('./src/lib/server/connectors/manager
|
|
556
|
-
const pluginsMod = await import('./src/lib/server/plugins
|
|
557
|
-
const toolsMod = await import('./src/lib/server/session-tools/index
|
|
727
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
728
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
729
|
+
const pluginsMod = await import('./src/lib/server/plugins')
|
|
730
|
+
const toolsMod = await import('./src/lib/server/session-tools/index')
|
|
558
731
|
const storage = storageMod.default || storageMod
|
|
559
732
|
const manager = managerMod.default || managerMod
|
|
560
733
|
const plugins = pluginsMod.default || pluginsMod
|
|
@@ -675,9 +848,9 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
675
848
|
|
|
676
849
|
it('keeps direct connector sessions isolated across four inbound senders for the same agent and mirrors their metadata into the main thread', () => {
|
|
677
850
|
const output = runWithTempDataDir(`
|
|
678
|
-
const storageMod = await import('./src/lib/server/storage
|
|
679
|
-
const managerMod = await import('./src/lib/server/connectors/manager
|
|
680
|
-
const providersMod = await import('./src/lib/providers/index
|
|
851
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
852
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
853
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
681
854
|
const storage = storageMod.default || storageMod
|
|
682
855
|
const manager = managerMod.default || managerMod
|
|
683
856
|
const providers = providersMod.default || providersMod
|
|
@@ -777,17 +950,17 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
777
950
|
`)
|
|
778
951
|
|
|
779
952
|
assert.equal(output.directSessions.length, 4)
|
|
780
|
-
assert.deepEqual(output.directSessions.map((entry) => entry.senderName), ['Alice', 'Bob', 'Gran', 'Wayde'])
|
|
781
|
-
assert.equal(output.directSessions.every((entry) => entry.texts.length === 2), true)
|
|
782
|
-
assert.equal(output.directSessions.every((entry) => entry.texts[0].source?.senderName === entry.senderName), true)
|
|
783
|
-
assert.equal(output.directSessions.every((entry) => entry.texts[1].text === `Replying to ${entry.senderName}`), true)
|
|
953
|
+
assert.deepEqual(output.directSessions.map((entry: any) => entry.senderName), ['Alice', 'Bob', 'Gran', 'Wayde'])
|
|
954
|
+
assert.equal(output.directSessions.every((entry: any) => entry.texts.length === 2), true)
|
|
955
|
+
assert.equal(output.directSessions.every((entry: any) => entry.texts[0].source?.senderName === entry.senderName), true)
|
|
956
|
+
assert.equal(output.directSessions.every((entry: any) => entry.texts[1].text === `Replying to ${entry.senderName}`), true)
|
|
784
957
|
assert.equal(output.threadMessages.length, 8)
|
|
785
958
|
assert.deepEqual(
|
|
786
|
-
output.threadMessages.filter((msg) => msg.role === 'user').map((msg) => msg.source?.senderName),
|
|
959
|
+
output.threadMessages.filter((msg: any) => msg.role === 'user').map((msg: any) => msg.source?.senderName),
|
|
787
960
|
['Alice', 'Bob', 'Gran', 'Wayde'],
|
|
788
961
|
)
|
|
789
962
|
assert.deepEqual(
|
|
790
|
-
output.threadMessages.filter((msg) => msg.role === 'assistant').map((msg) => ({
|
|
963
|
+
output.threadMessages.filter((msg: any) => msg.role === 'assistant').map((msg: any) => ({
|
|
791
964
|
text: msg.text,
|
|
792
965
|
senderName: msg.source?.senderName,
|
|
793
966
|
connectorId: msg.source?.connectorId,
|
|
@@ -799,15 +972,15 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
799
972
|
{ text: 'Replying to Wayde', senderName: 'Wayde', connectorId: 'conn_1' },
|
|
800
973
|
],
|
|
801
974
|
)
|
|
802
|
-
assert.equal(output.threadMessages.every((msg) => msg.historyExcluded === true), true)
|
|
975
|
+
assert.equal(output.threadMessages.every((msg: any) => msg.historyExcluded === true), true)
|
|
803
976
|
})
|
|
804
977
|
|
|
805
978
|
it('excludes mirrored connector transcript entries from direct agent-thread history', () => {
|
|
806
979
|
const output = runWithTempDataDir(`
|
|
807
|
-
const storageMod = await import('./src/lib/server/storage
|
|
808
|
-
const managerMod = await import('./src/lib/server/connectors/manager
|
|
809
|
-
const chatExecMod = await import('./src/lib/server/chat-execution
|
|
810
|
-
const providersMod = await import('./src/lib/providers/index
|
|
980
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
981
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
982
|
+
const chatExecMod = await import('./src/lib/server/chat-execution')
|
|
983
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
811
984
|
const storage = storageMod.default || storageMod
|
|
812
985
|
const manager = managerMod.default || managerMod
|
|
813
986
|
const chatExec = chatExecMod.default || chatExecMod
|
|
@@ -911,18 +1084,18 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
911
1084
|
|
|
912
1085
|
assert.equal(output.reply.senderNames.includes('Alice'), false)
|
|
913
1086
|
assert.equal(output.reply.senderNames.includes('Gran'), false)
|
|
914
|
-
assert.equal(output.reply.texts.some((entry) => /Alice|Gran/.test(String(entry))), false)
|
|
1087
|
+
assert.equal(output.reply.texts.some((entry: any) => /Alice|Gran/.test(String(entry))), false)
|
|
915
1088
|
assert.equal(output.reply.historyCount >= 1, true)
|
|
916
|
-
assert.equal(output.threadMessages.some((msg) => msg.historyExcluded === true && msg.source?.connectorId === 'conn_1'), true)
|
|
1089
|
+
assert.equal(output.threadMessages.some((msg: any) => msg.historyExcluded === true && msg.source?.connectorId === 'conn_1'), true)
|
|
917
1090
|
})
|
|
918
1091
|
|
|
919
1092
|
it('creates one reusable connector-sender approval for unknown allowlist senders and allows them after approval', () => {
|
|
920
1093
|
const output = runWithTempDataDir(`
|
|
921
|
-
const storageMod = await import('./src/lib/server/storage
|
|
922
|
-
const managerMod = await import('./src/lib/server/connectors/manager
|
|
923
|
-
const approvalsMod = await import('./src/lib/server/approvals
|
|
924
|
-
const pairingMod = await import('./src/lib/server/connectors/pairing
|
|
925
|
-
const providersMod = await import('./src/lib/providers/index
|
|
1094
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
1095
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
1096
|
+
const approvalsMod = await import('./src/lib/server/approvals')
|
|
1097
|
+
const pairingMod = await import('./src/lib/server/connectors/pairing')
|
|
1098
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
926
1099
|
const storage = storageMod.default || storageMod
|
|
927
1100
|
const manager = managerMod.default || managerMod
|
|
928
1101
|
const approvals = approvalsMod.default || approvalsMod
|
|
@@ -1041,14 +1214,116 @@ describe('sanitizeConnectorOutboundContent', () => {
|
|
|
1041
1214
|
assert.equal(output.third, 'Approved hello to Bob')
|
|
1042
1215
|
assert.equal(output.approvalsAfter.length, 1)
|
|
1043
1216
|
assert.equal(output.approvalsAfter[0].status, 'approved')
|
|
1044
|
-
assert.equal(output.threadMessages.some((msg) => msg.source?.senderName === 'Bob' && msg.historyExcluded === true), true)
|
|
1217
|
+
assert.equal(output.threadMessages.some((msg: any) => msg.source?.senderName === 'Bob' && msg.historyExcluded === true), true)
|
|
1218
|
+
})
|
|
1219
|
+
|
|
1220
|
+
it('allows WhatsApp senders listed in global settings without creating connector approvals', () => {
|
|
1221
|
+
const output = runWithTempDataDir(`
|
|
1222
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
1223
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
1224
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
1225
|
+
const storage = storageMod.default || storageMod
|
|
1226
|
+
const manager = managerMod.default || managerMod
|
|
1227
|
+
const providers = providersMod.default || providersMod
|
|
1228
|
+
|
|
1229
|
+
const now = Date.now()
|
|
1230
|
+
providers.PROVIDERS['test-provider'] = {
|
|
1231
|
+
id: 'test-provider',
|
|
1232
|
+
name: 'Test Provider',
|
|
1233
|
+
models: ['test-model'],
|
|
1234
|
+
requiresApiKey: false,
|
|
1235
|
+
requiresEndpoint: false,
|
|
1236
|
+
handler: {
|
|
1237
|
+
streamChat: async (opts) => {
|
|
1238
|
+
opts.write('data: ' + JSON.stringify({ t: 'r', text: 'Approved hello to Bob' }) + '\\n')
|
|
1239
|
+
return ''
|
|
1240
|
+
},
|
|
1241
|
+
},
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
storage.saveSettings({
|
|
1245
|
+
approvalsEnabled: true,
|
|
1246
|
+
whatsappApprovedContacts: [
|
|
1247
|
+
{ id: 'family', label: 'Family', phone: '+16660002222' },
|
|
1248
|
+
],
|
|
1249
|
+
})
|
|
1250
|
+
storage.saveAgents({
|
|
1251
|
+
agent_1: {
|
|
1252
|
+
id: 'agent_1',
|
|
1253
|
+
name: 'Molly',
|
|
1254
|
+
provider: 'test-provider',
|
|
1255
|
+
model: 'test-model',
|
|
1256
|
+
plugins: [],
|
|
1257
|
+
threadSessionId: 'agent_thread',
|
|
1258
|
+
createdAt: now,
|
|
1259
|
+
updatedAt: now,
|
|
1260
|
+
},
|
|
1261
|
+
})
|
|
1262
|
+
storage.saveConnectors({
|
|
1263
|
+
conn_1: {
|
|
1264
|
+
id: 'conn_1',
|
|
1265
|
+
name: 'WhatsApp',
|
|
1266
|
+
platform: 'whatsapp',
|
|
1267
|
+
agentId: 'agent_1',
|
|
1268
|
+
credentialId: null,
|
|
1269
|
+
config: {
|
|
1270
|
+
inboundDebounceMs: 0,
|
|
1271
|
+
dmPolicy: 'allowlist',
|
|
1272
|
+
allowFrom: '15550001111',
|
|
1273
|
+
},
|
|
1274
|
+
isEnabled: true,
|
|
1275
|
+
status: 'running',
|
|
1276
|
+
createdAt: now,
|
|
1277
|
+
updatedAt: now,
|
|
1278
|
+
},
|
|
1279
|
+
})
|
|
1280
|
+
storage.saveSessions({
|
|
1281
|
+
agent_thread: {
|
|
1282
|
+
id: 'agent_thread',
|
|
1283
|
+
name: 'Molly',
|
|
1284
|
+
cwd: process.env.WORKSPACE_DIR,
|
|
1285
|
+
user: 'default',
|
|
1286
|
+
provider: 'test-provider',
|
|
1287
|
+
model: 'test-model',
|
|
1288
|
+
claudeSessionId: null,
|
|
1289
|
+
messages: [],
|
|
1290
|
+
createdAt: now,
|
|
1291
|
+
lastActiveAt: now,
|
|
1292
|
+
sessionType: 'human',
|
|
1293
|
+
agentId: 'agent_1',
|
|
1294
|
+
plugins: [],
|
|
1295
|
+
},
|
|
1296
|
+
})
|
|
1297
|
+
|
|
1298
|
+
const connector = storage.loadConnectors().conn_1
|
|
1299
|
+
const reply = await manager.routeConnectorMessageForTest(connector, {
|
|
1300
|
+
platform: 'whatsapp',
|
|
1301
|
+
channelId: '16660002222@s.whatsapp.net',
|
|
1302
|
+
senderId: '16660002222@s.whatsapp.net',
|
|
1303
|
+
senderName: 'Bob',
|
|
1304
|
+
text: 'Hello from approved settings contact',
|
|
1305
|
+
messageId: 'in-b-settings',
|
|
1306
|
+
isGroup: false,
|
|
1307
|
+
})
|
|
1308
|
+
const approvals = Object.values(storage.loadApprovals())
|
|
1309
|
+
const thread = storage.loadSessions().agent_thread
|
|
1310
|
+
console.log(JSON.stringify({
|
|
1311
|
+
reply,
|
|
1312
|
+
approvals,
|
|
1313
|
+
threadMessages: thread.messages,
|
|
1314
|
+
}))
|
|
1315
|
+
`)
|
|
1316
|
+
|
|
1317
|
+
assert.equal(output.reply, 'Approved hello to Bob')
|
|
1318
|
+
assert.equal(output.approvals.length, 0)
|
|
1319
|
+
assert.equal(output.threadMessages.some((msg: any) => msg.source?.senderName === 'Bob' && msg.historyExcluded === true), true)
|
|
1045
1320
|
})
|
|
1046
1321
|
|
|
1047
1322
|
it('returns a friendly retry message instead of blank no-response when connector chat aborts', () => {
|
|
1048
1323
|
const output = runWithTempDataDir(`
|
|
1049
|
-
const storageMod = await import('./src/lib/server/storage
|
|
1050
|
-
const managerMod = await import('./src/lib/server/connectors/manager
|
|
1051
|
-
const providersMod = await import('./src/lib/providers/index
|
|
1324
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
1325
|
+
const managerMod = await import('./src/lib/server/connectors/manager')
|
|
1326
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
1052
1327
|
const storage = storageMod.default || storageMod
|
|
1053
1328
|
const manager = managerMod.default || managerMod
|
|
1054
1329
|
const providers = providersMod.default || providersMod
|