@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
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { Connector } from '@/types'
|
|
2
|
+
import { loadSettings } from '../storage'
|
|
3
|
+
import { requestApprovalMaybeAutoApprove } from '../approvals'
|
|
4
|
+
import {
|
|
5
|
+
createOrTouchPairingRequest,
|
|
6
|
+
getWhatsAppApprovedSenderIds,
|
|
7
|
+
isSenderAllowed,
|
|
8
|
+
listStoredAllowedSenders,
|
|
9
|
+
parseAllowFromCsv,
|
|
10
|
+
parsePairingPolicy,
|
|
11
|
+
type PairingPolicy,
|
|
12
|
+
} from './pairing'
|
|
13
|
+
import { isReplyToLastOutbound, resolveConnectorSessionPolicy, textMentionsAlias } from './policy'
|
|
14
|
+
import type { ConnectorAgent, ConnectorSession } from './session'
|
|
15
|
+
import type { InboundMessage } from './types'
|
|
16
|
+
|
|
17
|
+
export interface ResolvedPairingAccess {
|
|
18
|
+
policy: PairingPolicy
|
|
19
|
+
configAllowFrom: string[]
|
|
20
|
+
isAllowed: boolean
|
|
21
|
+
hasAnyApprover: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function evaluateGroupPolicy(params: {
|
|
25
|
+
connector: Connector
|
|
26
|
+
msg: InboundMessage
|
|
27
|
+
session?: ConnectorSession | null
|
|
28
|
+
aliases: string[]
|
|
29
|
+
}): { allowed: boolean; reason: string } {
|
|
30
|
+
const { connector, msg, session, aliases } = params
|
|
31
|
+
if (!msg.isGroup) return { allowed: true, reason: 'dm' }
|
|
32
|
+
const policy = resolveConnectorSessionPolicy(connector, msg, session)
|
|
33
|
+
if (policy.groupPolicy === 'open') return { allowed: true, reason: 'open' }
|
|
34
|
+
if (policy.groupPolicy === 'disabled') return { allowed: false, reason: 'disabled' }
|
|
35
|
+
const mentioned = !!msg.mentionsBot || textMentionsAlias(msg.text || '', aliases)
|
|
36
|
+
const replied = isReplyToLastOutbound(msg, session)
|
|
37
|
+
if (policy.groupPolicy === 'mention') {
|
|
38
|
+
return { allowed: mentioned, reason: mentioned ? 'mentioned' : 'mention_required' }
|
|
39
|
+
}
|
|
40
|
+
const allowed = mentioned || replied
|
|
41
|
+
return { allowed, reason: allowed ? (mentioned ? 'mentioned' : 'reply') : 'reply_or_mention_required' }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function resolvePairingAccess(connector: Connector, msg: InboundMessage): ResolvedPairingAccess {
|
|
45
|
+
const policy = parsePairingPolicy(connector.config?.dmPolicy, 'open')
|
|
46
|
+
const globalWhatsAppAllowFrom = connector.platform === 'whatsapp'
|
|
47
|
+
? getWhatsAppApprovedSenderIds(loadSettings().whatsappApprovedContacts)
|
|
48
|
+
: []
|
|
49
|
+
const configAllowFrom = parseAllowFromCsv([
|
|
50
|
+
connector.config?.allowFrom,
|
|
51
|
+
...globalWhatsAppAllowFrom,
|
|
52
|
+
].filter(Boolean).join(','))
|
|
53
|
+
const stored = listStoredAllowedSenders(connector.id)
|
|
54
|
+
const isAllowed = [
|
|
55
|
+
msg.senderId,
|
|
56
|
+
msg.senderIdAlt,
|
|
57
|
+
]
|
|
58
|
+
.filter((senderId): senderId is string => typeof senderId === 'string' && !!senderId.trim())
|
|
59
|
+
.some((senderId) => isSenderAllowed({
|
|
60
|
+
connectorId: connector.id,
|
|
61
|
+
senderId,
|
|
62
|
+
configAllowFrom,
|
|
63
|
+
}))
|
|
64
|
+
return {
|
|
65
|
+
policy,
|
|
66
|
+
configAllowFrom,
|
|
67
|
+
isAllowed,
|
|
68
|
+
hasAnyApprover: (configAllowFrom.length + stored.length) > 0,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function resolveInboundApprovalSenderId(msg: InboundMessage): string {
|
|
73
|
+
const alt = typeof msg.senderIdAlt === 'string' ? msg.senderIdAlt.trim() : ''
|
|
74
|
+
if (alt) return alt
|
|
75
|
+
return typeof msg.senderId === 'string' ? msg.senderId.trim() : ''
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function buildInboundApprovalSubject(msg: InboundMessage): string {
|
|
79
|
+
const senderName = typeof msg.senderName === 'string' ? msg.senderName.trim() : ''
|
|
80
|
+
const senderId = resolveInboundApprovalSenderId(msg)
|
|
81
|
+
if (senderName && senderId && senderName !== senderId) return `${senderName} (${senderId})`
|
|
82
|
+
return senderName || senderId || 'this sender'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function enforceInboundAccessPolicy(params: {
|
|
86
|
+
connector: Connector
|
|
87
|
+
msg: InboundMessage
|
|
88
|
+
session: ConnectorSession
|
|
89
|
+
agent: ConnectorAgent
|
|
90
|
+
noMessageSentinel: string
|
|
91
|
+
}): Promise<string | null> {
|
|
92
|
+
const { connector, msg, session, agent, noMessageSentinel } = params
|
|
93
|
+
if (msg.isGroup) return null
|
|
94
|
+
const { policy, isAllowed } = resolvePairingAccess(connector, msg)
|
|
95
|
+
if (policy === 'open') return null
|
|
96
|
+
|
|
97
|
+
if (policy === 'disabled') return noMessageSentinel
|
|
98
|
+
if (isAllowed) return null
|
|
99
|
+
|
|
100
|
+
const senderId = resolveInboundApprovalSenderId(msg)
|
|
101
|
+
const senderSubject = buildInboundApprovalSubject(msg)
|
|
102
|
+
const approval = await requestApprovalMaybeAutoApprove({
|
|
103
|
+
category: 'connector_sender',
|
|
104
|
+
title: `Approve ${senderSubject} on ${connector.name}`,
|
|
105
|
+
description: `Allow ${senderSubject} to message ${agent.name} via ${connector.platform}/${connector.name}.`,
|
|
106
|
+
data: {
|
|
107
|
+
connectorId: connector.id,
|
|
108
|
+
connectorName: connector.name,
|
|
109
|
+
platform: connector.platform,
|
|
110
|
+
senderId,
|
|
111
|
+
senderIdRaw: typeof msg.senderId === 'string' ? msg.senderId.trim() : '',
|
|
112
|
+
senderName: typeof msg.senderName === 'string' ? msg.senderName.trim() : '',
|
|
113
|
+
channelId: typeof msg.channelId === 'string' ? msg.channelId.trim() : '',
|
|
114
|
+
policy,
|
|
115
|
+
},
|
|
116
|
+
agentId: agent.id,
|
|
117
|
+
sessionId: session.id,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
if (approval.status === 'approved') return null
|
|
121
|
+
|
|
122
|
+
if (policy === 'allowlist') {
|
|
123
|
+
return [
|
|
124
|
+
`${senderSubject} is pending approval for this connector.`,
|
|
125
|
+
'A SwarmClaw approval request has been created for this sender.',
|
|
126
|
+
'An approved operator can allow this sender in the app or via /pair allow <senderId>.',
|
|
127
|
+
].join('\n')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (policy === 'pairing') {
|
|
131
|
+
const request = createOrTouchPairingRequest({
|
|
132
|
+
connectorId: connector.id,
|
|
133
|
+
senderId,
|
|
134
|
+
senderName: msg.senderName,
|
|
135
|
+
channelId: msg.channelId,
|
|
136
|
+
})
|
|
137
|
+
return [
|
|
138
|
+
`${senderSubject} is pending approval for this connector.`,
|
|
139
|
+
'A SwarmClaw approval request has been created for this sender.',
|
|
140
|
+
`Pairing code: ${request.code}`,
|
|
141
|
+
'Approve in the app, or ask an approved sender to run /pair approve <code>.',
|
|
142
|
+
].join('\n')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return 'This sender is not authorized for this connector.'
|
|
146
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import crypto from 'node:crypto'
|
|
2
2
|
import type { PlatformConnector, ConnectorInstance, InboundMessage, InboundMedia } from './types'
|
|
3
|
-
import {
|
|
3
|
+
import { resolveConnectorIngressReply } from './ingress-delivery'
|
|
4
4
|
|
|
5
5
|
const DEFAULT_TIMEOUT_MS = 10_000
|
|
6
6
|
const DEFAULT_WEBHOOK_PATH = '/api/connectors/{id}/webhook'
|
|
@@ -260,14 +260,14 @@ const bluebubbles: PlatformConnector = {
|
|
|
260
260
|
if (!allowed) return {}
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
-
const
|
|
264
|
-
if (!
|
|
263
|
+
const reply = await resolveConnectorIngressReply(onMessage, inbound)
|
|
264
|
+
if (!reply) return {}
|
|
265
265
|
|
|
266
266
|
await sendBlueBubblesText({
|
|
267
267
|
serverUrl,
|
|
268
268
|
password,
|
|
269
269
|
channelId: inbound.channelId,
|
|
270
|
-
text:
|
|
270
|
+
text: reply.visibleText,
|
|
271
271
|
timeoutMs,
|
|
272
272
|
})
|
|
273
273
|
return {}
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { getProvider } from '@/lib/providers'
|
|
2
|
+
import type { Connector } from '@/types'
|
|
3
|
+
import { loadAgents } from '../storage'
|
|
4
|
+
import { syncSessionArchiveMemory } from '../session-archive-memory'
|
|
5
|
+
import { resolvePairingAccess } from './access'
|
|
6
|
+
import {
|
|
7
|
+
addAllowedSender,
|
|
8
|
+
approvePairingCode,
|
|
9
|
+
createOrTouchPairingRequest,
|
|
10
|
+
listPendingPairingRequests,
|
|
11
|
+
listStoredAllowedSenders,
|
|
12
|
+
} from './pairing'
|
|
13
|
+
import {
|
|
14
|
+
buildConnectorDoctorWarnings,
|
|
15
|
+
resetConnectorSessionRuntime,
|
|
16
|
+
resolveConnectorSessionPolicy,
|
|
17
|
+
} from './policy'
|
|
18
|
+
import {
|
|
19
|
+
applyConnectorRuntimeDefaults,
|
|
20
|
+
applySessionSetting,
|
|
21
|
+
describeSessionControls,
|
|
22
|
+
persistSession,
|
|
23
|
+
pushSessionMessage,
|
|
24
|
+
type ConnectorAgent,
|
|
25
|
+
type ConnectorSession,
|
|
26
|
+
updateSessionConnectorContext,
|
|
27
|
+
} from './session'
|
|
28
|
+
import type { InboundMessage } from './types'
|
|
29
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
30
|
+
|
|
31
|
+
export type ConnectorCommandName =
|
|
32
|
+
| 'help'
|
|
33
|
+
| 'status'
|
|
34
|
+
| 'new'
|
|
35
|
+
| 'reset'
|
|
36
|
+
| 'compact'
|
|
37
|
+
| 'think'
|
|
38
|
+
| 'pair'
|
|
39
|
+
| 'session'
|
|
40
|
+
| 'focus'
|
|
41
|
+
| 'doctor'
|
|
42
|
+
|
|
43
|
+
export interface ParsedConnectorCommand {
|
|
44
|
+
name: ConnectorCommandName
|
|
45
|
+
args: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function parseConnectorCommand(text: string): ParsedConnectorCommand | null {
|
|
49
|
+
const trimmed = text.trim()
|
|
50
|
+
if (!trimmed.startsWith('/')) return null
|
|
51
|
+
const [head, ...rest] = trimmed.split(/\s+/)
|
|
52
|
+
const name = head.slice(1).toLowerCase()
|
|
53
|
+
const args = rest.join(' ').trim()
|
|
54
|
+
switch (name) {
|
|
55
|
+
case 'help':
|
|
56
|
+
case 'status':
|
|
57
|
+
case 'new':
|
|
58
|
+
case 'reset':
|
|
59
|
+
case 'compact':
|
|
60
|
+
case 'think':
|
|
61
|
+
case 'pair':
|
|
62
|
+
case 'session':
|
|
63
|
+
case 'focus':
|
|
64
|
+
case 'doctor':
|
|
65
|
+
return { name, args } as ParsedConnectorCommand
|
|
66
|
+
default:
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function handlePairCommand(params: {
|
|
72
|
+
connector: Connector
|
|
73
|
+
msg: InboundMessage
|
|
74
|
+
args: string
|
|
75
|
+
}): Promise<string> {
|
|
76
|
+
const { connector, msg, args } = params
|
|
77
|
+
const access = resolvePairingAccess(connector, msg)
|
|
78
|
+
const parts = args.split(/\s+/).map((item) => item.trim()).filter(Boolean)
|
|
79
|
+
const subcommand = (parts[0] || 'status').toLowerCase()
|
|
80
|
+
|
|
81
|
+
if (subcommand === 'request') {
|
|
82
|
+
const request = createOrTouchPairingRequest({
|
|
83
|
+
connectorId: connector.id,
|
|
84
|
+
senderId: msg.senderId,
|
|
85
|
+
senderName: msg.senderName,
|
|
86
|
+
channelId: msg.channelId,
|
|
87
|
+
})
|
|
88
|
+
return request.created
|
|
89
|
+
? `Pairing request created. Share this code with an approved user: ${request.code}`
|
|
90
|
+
: `Pairing request is already pending. Your code is: ${request.code}`
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (subcommand === 'list') {
|
|
94
|
+
if (access.hasAnyApprover && !access.isAllowed) {
|
|
95
|
+
return 'Pairing list is restricted to approved senders.'
|
|
96
|
+
}
|
|
97
|
+
const pending = listPendingPairingRequests(connector.id)
|
|
98
|
+
if (!pending.length) return 'No pending pairing requests.'
|
|
99
|
+
const lines = pending.slice(0, 20).map((entry) => {
|
|
100
|
+
const ageMin = Math.max(1, Math.round((Date.now() - entry.updatedAt) / 60_000))
|
|
101
|
+
const sender = entry.senderName ? `${entry.senderName} (${entry.senderId})` : entry.senderId
|
|
102
|
+
return `- ${entry.code} -> ${sender} (${ageMin}m ago)`
|
|
103
|
+
})
|
|
104
|
+
return `Pending pairing requests (${pending.length}):\n${lines.join('\n')}`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (subcommand === 'approve') {
|
|
108
|
+
const code = (parts[1] || '').trim()
|
|
109
|
+
if (!code) return 'Usage: /pair approve <code>'
|
|
110
|
+
if (access.hasAnyApprover && !access.isAllowed) {
|
|
111
|
+
return 'Pairing approvals are restricted to approved senders.'
|
|
112
|
+
}
|
|
113
|
+
const approved = approvePairingCode(connector.id, code)
|
|
114
|
+
if (!approved.ok) return approved.reason || 'Pairing approval failed.'
|
|
115
|
+
const sender = approved.senderName ? `${approved.senderName} (${approved.senderId})` : approved.senderId
|
|
116
|
+
return `Pairing approved: ${sender}`
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (subcommand === 'allow') {
|
|
120
|
+
const senderId = (parts[1] || '').trim()
|
|
121
|
+
if (!senderId) return 'Usage: /pair allow <senderId>'
|
|
122
|
+
if (access.hasAnyApprover && !access.isAllowed) {
|
|
123
|
+
return 'Allowlist updates are restricted to approved senders.'
|
|
124
|
+
}
|
|
125
|
+
const result = addAllowedSender(connector.id, senderId)
|
|
126
|
+
if (!result.normalized) return 'Could not parse senderId.'
|
|
127
|
+
return result.added
|
|
128
|
+
? `Allowed sender: ${result.normalized}`
|
|
129
|
+
: `Sender is already allowed: ${result.normalized}`
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const pending = listPendingPairingRequests(connector.id)
|
|
133
|
+
const stored = listStoredAllowedSenders(connector.id)
|
|
134
|
+
const policyLine = `Policy: ${access.policy}`
|
|
135
|
+
const approvedLine = `You are ${access.isAllowed ? 'approved' : 'not approved'} as ${msg.senderId}`
|
|
136
|
+
return [
|
|
137
|
+
'Pairing controls:',
|
|
138
|
+
policyLine,
|
|
139
|
+
approvedLine,
|
|
140
|
+
`- Stored approvals: ${stored.length}`,
|
|
141
|
+
`- Pending requests: ${pending.length}`,
|
|
142
|
+
'- Commands: /pair request, /pair list, /pair approve <code>, /pair allow <senderId>',
|
|
143
|
+
].join('\n')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function summarizeForCompaction(messages: Array<{ role?: string; text?: string }>): string {
|
|
147
|
+
const preview = messages
|
|
148
|
+
.slice(-8)
|
|
149
|
+
.map((message, index) => {
|
|
150
|
+
const role = (message.role || 'unknown').toUpperCase()
|
|
151
|
+
const body = (message.text || '').replace(/\s+/g, ' ').trim()
|
|
152
|
+
const clipped = body.length > 180 ? `${body.slice(0, 177)}...` : body
|
|
153
|
+
return `${index + 1}. [${role}] ${clipped || '(no text)'}`
|
|
154
|
+
})
|
|
155
|
+
if (!preview.length) return 'No earlier messages to summarize.'
|
|
156
|
+
return preview.join('\n')
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function handleConnectorCommand(params: {
|
|
160
|
+
command: ParsedConnectorCommand
|
|
161
|
+
connector: Connector
|
|
162
|
+
session: ConnectorSession
|
|
163
|
+
msg: InboundMessage
|
|
164
|
+
agentName: string
|
|
165
|
+
inboundText: string
|
|
166
|
+
}): Promise<string> {
|
|
167
|
+
const { command, connector, session, msg, agentName, inboundText } = params
|
|
168
|
+
|
|
169
|
+
if (command.name === 'help') {
|
|
170
|
+
const text = [
|
|
171
|
+
'Connector commands:',
|
|
172
|
+
'/status — Show active session status',
|
|
173
|
+
'/new or /reset — Clear this connector conversation thread',
|
|
174
|
+
'/compact [keepLastN] — Summarize older history and keep recent messages (default 10)',
|
|
175
|
+
'/think <minimal|low|medium|high> — Set connector thread reasoning guidance',
|
|
176
|
+
'/session — Show session controls',
|
|
177
|
+
'/session set <scope|reply|thread|group|idle|maxAge|resetMode|dailyResetAt|timezone|think|model|provider> <value> — Patch this connector session',
|
|
178
|
+
'/focus here|clear — Bind or clear focus on the current thread/topic',
|
|
179
|
+
'/doctor — Show autonomy and safety warnings for this connector/session',
|
|
180
|
+
'/pair — Pairing/access controls (status, request, list, approve, allow)',
|
|
181
|
+
'/help — Show this list',
|
|
182
|
+
].join('\n')
|
|
183
|
+
pushSessionMessage(session, 'user', inboundText)
|
|
184
|
+
pushSessionMessage(session, 'assistant', text)
|
|
185
|
+
persistSession(session)
|
|
186
|
+
return text
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (command.name === 'status') {
|
|
190
|
+
const policy = resolveConnectorSessionPolicy(connector, msg, session)
|
|
191
|
+
const all = Array.isArray(session.messages) ? session.messages : []
|
|
192
|
+
const userCount = all.filter((message: { role?: string }) => message?.role === 'user').length
|
|
193
|
+
const assistantCount = all.filter((message: { role?: string }) => message?.role === 'assistant').length
|
|
194
|
+
const toolsCount = Array.isArray(session.plugins) ? session.plugins.length : 0
|
|
195
|
+
const statusText = [
|
|
196
|
+
`Status for ${connector.platform} / ${connector.name}:`,
|
|
197
|
+
`- Agent: ${agentName}`,
|
|
198
|
+
`- Session: ${session.id}`,
|
|
199
|
+
`- Model: ${session.provider}/${session.model}`,
|
|
200
|
+
`- Messages: ${all.length} (${userCount} user, ${assistantCount} assistant)`,
|
|
201
|
+
`- Tools enabled: ${toolsCount}`,
|
|
202
|
+
`- Channel: ${msg.channelName || msg.channelId}`,
|
|
203
|
+
`- Last active: ${new Date(session.lastActiveAt || session.createdAt || Date.now()).toLocaleString()}`,
|
|
204
|
+
`- Reset mode: ${policy.resetMode}`,
|
|
205
|
+
`- Reply mode: ${policy.replyMode}`,
|
|
206
|
+
`- Scope: ${policy.scope}`,
|
|
207
|
+
].join('\n')
|
|
208
|
+
pushSessionMessage(session, 'user', inboundText)
|
|
209
|
+
pushSessionMessage(session, 'assistant', statusText)
|
|
210
|
+
persistSession(session)
|
|
211
|
+
return statusText
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (command.name === 'new' || command.name === 'reset') {
|
|
215
|
+
const agent = session.agentId ? (loadAgents() as Record<string, ConnectorAgent>)[session.agentId] : undefined
|
|
216
|
+
try { syncSessionArchiveMemory(session, { agent }) } catch { /* best effort */ }
|
|
217
|
+
const cleared = resetConnectorSessionRuntime(session, 'manual_reset')
|
|
218
|
+
const policy = resolveConnectorSessionPolicy(connector, msg, session)
|
|
219
|
+
const providerInfo = policy.providerOverride ? getProvider(policy.providerOverride) : null
|
|
220
|
+
applyConnectorRuntimeDefaults(session, {
|
|
221
|
+
provider: providerInfo?.id || session.provider,
|
|
222
|
+
model: policy.modelOverride || session.model,
|
|
223
|
+
apiEndpoint: providerInfo?.defaultEndpoint || session.apiEndpoint || null,
|
|
224
|
+
thinkingLevel: policy.thinkingLevel || session.connectorThinkLevel || null,
|
|
225
|
+
})
|
|
226
|
+
updateSessionConnectorContext(session, connector, msg, session.name || session.id)
|
|
227
|
+
persistSession(session)
|
|
228
|
+
return `Reset complete for ${connector.platform} channel thread. Cleared ${cleared} message(s).`
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (command.name === 'compact') {
|
|
232
|
+
const keepParsed = Number.parseInt(command.args, 10)
|
|
233
|
+
const keepLastN = Number.isFinite(keepParsed) ? Math.max(4, Math.min(50, keepParsed)) : 10
|
|
234
|
+
const history = Array.isArray(session.messages) ? session.messages : []
|
|
235
|
+
if (history.length <= keepLastN) {
|
|
236
|
+
const text = `Nothing to compact. Current history has ${history.length} message(s), keepLastN=${keepLastN}.`
|
|
237
|
+
pushSessionMessage(session, 'user', inboundText)
|
|
238
|
+
pushSessionMessage(session, 'assistant', text)
|
|
239
|
+
persistSession(session)
|
|
240
|
+
return text
|
|
241
|
+
}
|
|
242
|
+
const oldMessages = history.slice(0, -keepLastN)
|
|
243
|
+
const recentMessages = history.slice(-keepLastN)
|
|
244
|
+
const summary = summarizeForCompaction(oldMessages)
|
|
245
|
+
const summaryMessage = {
|
|
246
|
+
role: 'assistant' as const,
|
|
247
|
+
text: `[Context summary: compacted ${oldMessages.length} message(s)]\n${summary}`,
|
|
248
|
+
time: Date.now(),
|
|
249
|
+
kind: 'system' as const,
|
|
250
|
+
}
|
|
251
|
+
session.messages = [summaryMessage, ...recentMessages]
|
|
252
|
+
session.lastActiveAt = Date.now()
|
|
253
|
+
const text = `Compacted ${oldMessages.length} message(s). Kept ${recentMessages.length} recent message(s) plus a summary.`
|
|
254
|
+
pushSessionMessage(session, 'assistant', text)
|
|
255
|
+
persistSession(session)
|
|
256
|
+
return text
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (command.name === 'think') {
|
|
260
|
+
const requested = command.args.trim().toLowerCase()
|
|
261
|
+
const allowed = new Set(['minimal', 'low', 'medium', 'high'] as const)
|
|
262
|
+
if (!requested) {
|
|
263
|
+
const policy = resolveConnectorSessionPolicy(connector, msg, session)
|
|
264
|
+
const current = typeof policy.thinkingLevel === 'string' && allowed.has(policy.thinkingLevel)
|
|
265
|
+
? policy.thinkingLevel
|
|
266
|
+
: 'medium'
|
|
267
|
+
const text = `Current /think level: ${current}. Usage: /think <minimal|low|medium|high>.`
|
|
268
|
+
pushSessionMessage(session, 'user', inboundText)
|
|
269
|
+
pushSessionMessage(session, 'assistant', text)
|
|
270
|
+
persistSession(session)
|
|
271
|
+
return text
|
|
272
|
+
}
|
|
273
|
+
if (
|
|
274
|
+
requested !== 'minimal'
|
|
275
|
+
&& requested !== 'low'
|
|
276
|
+
&& requested !== 'medium'
|
|
277
|
+
&& requested !== 'high'
|
|
278
|
+
) {
|
|
279
|
+
const text = 'Invalid /think level. Use one of: minimal, low, medium, high.'
|
|
280
|
+
pushSessionMessage(session, 'user', inboundText)
|
|
281
|
+
pushSessionMessage(session, 'assistant', text)
|
|
282
|
+
persistSession(session)
|
|
283
|
+
return text
|
|
284
|
+
}
|
|
285
|
+
session.connectorThinkLevel = requested
|
|
286
|
+
session.lastActiveAt = Date.now()
|
|
287
|
+
const text = `Set /think level to ${requested} for this connector thread.`
|
|
288
|
+
pushSessionMessage(session, 'user', inboundText)
|
|
289
|
+
pushSessionMessage(session, 'assistant', text)
|
|
290
|
+
persistSession(session)
|
|
291
|
+
return text
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (command.name === 'doctor') {
|
|
295
|
+
const warnings = buildConnectorDoctorWarnings({ connector, msg, session })
|
|
296
|
+
const text = warnings.length
|
|
297
|
+
? ['Connector doctor:', ...warnings.map((item) => `- ${item}`)].join('\n')
|
|
298
|
+
: 'Connector doctor: no obvious autonomy or safety issues detected.'
|
|
299
|
+
pushSessionMessage(session, 'user', inboundText)
|
|
300
|
+
pushSessionMessage(session, 'assistant', text)
|
|
301
|
+
persistSession(session)
|
|
302
|
+
return text
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (command.name === 'session') {
|
|
306
|
+
const parts = command.args.split(/\s+/).map((item) => item.trim()).filter(Boolean)
|
|
307
|
+
if (!parts.length || parts[0].toLowerCase() === 'show' || parts[0].toLowerCase() === 'status') {
|
|
308
|
+
const text = describeSessionControls(session, connector, msg)
|
|
309
|
+
pushSessionMessage(session, 'user', inboundText)
|
|
310
|
+
pushSessionMessage(session, 'assistant', text)
|
|
311
|
+
persistSession(session)
|
|
312
|
+
return text
|
|
313
|
+
}
|
|
314
|
+
if (parts[0].toLowerCase() === 'reset') {
|
|
315
|
+
const agent = session.agentId ? (loadAgents() as Record<string, ConnectorAgent>)[session.agentId] : undefined
|
|
316
|
+
try { syncSessionArchiveMemory(session, { agent }) } catch { /* best effort */ }
|
|
317
|
+
const cleared = resetConnectorSessionRuntime(session, 'manual_reset')
|
|
318
|
+
const policy = resolveConnectorSessionPolicy(connector, msg, session)
|
|
319
|
+
const providerInfo = policy.providerOverride ? getProvider(policy.providerOverride) : null
|
|
320
|
+
applyConnectorRuntimeDefaults(session, {
|
|
321
|
+
provider: providerInfo?.id || session.provider,
|
|
322
|
+
model: policy.modelOverride || session.model,
|
|
323
|
+
apiEndpoint: providerInfo?.defaultEndpoint || session.apiEndpoint || null,
|
|
324
|
+
thinkingLevel: policy.thinkingLevel || session.connectorThinkLevel || null,
|
|
325
|
+
})
|
|
326
|
+
updateSessionConnectorContext(session, connector, msg, session.name || session.id)
|
|
327
|
+
persistSession(session)
|
|
328
|
+
return `Connector session reset. Cleared ${cleared} message(s).`
|
|
329
|
+
}
|
|
330
|
+
if (parts[0].toLowerCase() === 'set') {
|
|
331
|
+
const key = parts[1] || ''
|
|
332
|
+
const value = parts.slice(2).join(' ').trim()
|
|
333
|
+
if (!key) return 'Usage: /session set <scope|reply|thread|group|idle|maxAge|resetMode|dailyResetAt|timezone|think|model|provider> <value>'
|
|
334
|
+
try {
|
|
335
|
+
const text = applySessionSetting(session, key, value, msg)
|
|
336
|
+
updateSessionConnectorContext(session, connector, msg, session.name || session.id)
|
|
337
|
+
persistSession(session)
|
|
338
|
+
return text
|
|
339
|
+
} catch (err: unknown) {
|
|
340
|
+
return errorMessage(err)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return 'Usage: /session, /session show, /session set <key> <value>, /session reset'
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (command.name === 'focus') {
|
|
347
|
+
const subcommand = command.args.trim().toLowerCase()
|
|
348
|
+
if (subcommand === 'clear') {
|
|
349
|
+
session.connectorThreadBinding = null
|
|
350
|
+
session.connectorSessionScope = null
|
|
351
|
+
session.connectorContext = { ...(session.connectorContext || {}), threadId: null }
|
|
352
|
+
persistSession(session)
|
|
353
|
+
return 'Cleared connector thread focus.'
|
|
354
|
+
}
|
|
355
|
+
if (!msg.threadId) {
|
|
356
|
+
return 'Focus can only be set from a threaded or topic-bound message.'
|
|
357
|
+
}
|
|
358
|
+
session.connectorThreadBinding = 'strict'
|
|
359
|
+
session.connectorSessionScope = 'thread'
|
|
360
|
+
session.connectorReplyMode = session.connectorReplyMode || 'all'
|
|
361
|
+
session.connectorContext = { ...(session.connectorContext || {}), threadId: msg.threadId }
|
|
362
|
+
persistSession(session)
|
|
363
|
+
return `Focused this connector session on thread ${msg.threadId}.`
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return 'Unknown command.'
|
|
367
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { describe, it } from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import { getPlatform, isNoMessage, formatMediaLine, formatInboundUserText, extractEmbeddedMedia, selectOutboundMediaFiles } from './manager
|
|
4
|
-
import { handleSignalEvent } from './signal
|
|
5
|
-
import type { PlatformConnector } from './types
|
|
6
|
-
import type { InboundMessage, InboundMedia } from './types
|
|
3
|
+
import { getPlatform, isNoMessage, formatMediaLine, formatInboundUserText, extractEmbeddedMedia, selectOutboundMediaFiles } from './manager'
|
|
4
|
+
import { handleSignalEvent } from './signal'
|
|
5
|
+
import type { PlatformConnector } from './types'
|
|
6
|
+
import type { InboundMessage, InboundMedia } from './types'
|
|
7
7
|
import fs from 'node:fs'
|
|
8
8
|
import path from 'node:path'
|
|
9
9
|
import { UPLOAD_DIR } from '../storage'
|