@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,149 @@
|
|
|
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 { after, before, describe, it } from 'node:test'
|
|
6
|
+
|
|
7
|
+
const originalEnv = {
|
|
8
|
+
DATA_DIR: process.env.DATA_DIR,
|
|
9
|
+
WORKSPACE_DIR: process.env.WORKSPACE_DIR,
|
|
10
|
+
SWARMCLAW_BUILD_MODE: process.env.SWARMCLAW_BUILD_MODE,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let tempDir = ''
|
|
14
|
+
let integrityMonitor: typeof import('./integrity-monitor')
|
|
15
|
+
|
|
16
|
+
before(async () => {
|
|
17
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-integrity-'))
|
|
18
|
+
process.env.DATA_DIR = path.join(tempDir, 'data')
|
|
19
|
+
process.env.WORKSPACE_DIR = path.join(tempDir, 'workspace')
|
|
20
|
+
process.env.SWARMCLAW_BUILD_MODE = '1'
|
|
21
|
+
integrityMonitor = await import('./integrity-monitor')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
after(() => {
|
|
25
|
+
if (originalEnv.DATA_DIR === undefined) delete process.env.DATA_DIR
|
|
26
|
+
else process.env.DATA_DIR = originalEnv.DATA_DIR
|
|
27
|
+
if (originalEnv.WORKSPACE_DIR === undefined) delete process.env.WORKSPACE_DIR
|
|
28
|
+
else process.env.WORKSPACE_DIR = originalEnv.WORKSPACE_DIR
|
|
29
|
+
if (originalEnv.SWARMCLAW_BUILD_MODE === undefined) delete process.env.SWARMCLAW_BUILD_MODE
|
|
30
|
+
else process.env.SWARMCLAW_BUILD_MODE = originalEnv.SWARMCLAW_BUILD_MODE
|
|
31
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('integrity-monitor', () => {
|
|
35
|
+
it('returns disabled result when integrityMonitorEnabled is false', () => {
|
|
36
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: false })
|
|
37
|
+
assert.equal(result.enabled, false)
|
|
38
|
+
assert.equal(result.checkedFiles, 0)
|
|
39
|
+
assert.equal(result.drifts.length, 0)
|
|
40
|
+
assert.ok(result.checkedAt > 0)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('returns disabled for string "false"', () => {
|
|
44
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: 'false' })
|
|
45
|
+
assert.equal(result.enabled, false)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('returns disabled for string "0"', () => {
|
|
49
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: '0' })
|
|
50
|
+
assert.equal(result.enabled, false)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('returns disabled for string "off"', () => {
|
|
54
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: 'off' })
|
|
55
|
+
assert.equal(result.enabled, false)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('defaults to enabled when setting is null', () => {
|
|
59
|
+
const result = integrityMonitor.runIntegrityMonitor(null)
|
|
60
|
+
assert.equal(result.enabled, true)
|
|
61
|
+
assert.ok(result.checkedAt > 0)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('defaults to enabled when setting is undefined', () => {
|
|
65
|
+
const result = integrityMonitor.runIntegrityMonitor()
|
|
66
|
+
assert.equal(result.enabled, true)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('enabled with string "true"', () => {
|
|
70
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: 'true' })
|
|
71
|
+
assert.equal(result.enabled, true)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('enabled with string "1"', () => {
|
|
75
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: '1' })
|
|
76
|
+
assert.equal(result.enabled, true)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('enabled run returns result with checkedFiles and drifts array', () => {
|
|
80
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: true })
|
|
81
|
+
assert.equal(result.enabled, true)
|
|
82
|
+
assert.ok(typeof result.checkedFiles === 'number')
|
|
83
|
+
assert.ok(Array.isArray(result.drifts))
|
|
84
|
+
assert.ok(result.checkedAt > 0)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('second run with no changes produces zero drifts', () => {
|
|
88
|
+
// First run establishes baselines
|
|
89
|
+
integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: true })
|
|
90
|
+
// Second run with no changes
|
|
91
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: true })
|
|
92
|
+
assert.equal(result.drifts.length, 0)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('detects file modification as drift', () => {
|
|
96
|
+
// Create a plugin file in the data/plugins dir
|
|
97
|
+
const pluginDir = path.join(process.env.DATA_DIR!, 'plugins')
|
|
98
|
+
fs.mkdirSync(pluginDir, { recursive: true })
|
|
99
|
+
const pluginFile = path.join(pluginDir, 'test-integrity-plugin.js')
|
|
100
|
+
fs.writeFileSync(pluginFile, 'module.exports = { name: "test" }')
|
|
101
|
+
|
|
102
|
+
// First run: baseline
|
|
103
|
+
integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: true })
|
|
104
|
+
|
|
105
|
+
// Modify the file
|
|
106
|
+
fs.writeFileSync(pluginFile, 'module.exports = { name: "modified" }')
|
|
107
|
+
|
|
108
|
+
// Second run: should detect drift
|
|
109
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: true })
|
|
110
|
+
const drift = result.drifts.find((d) => d.filePath === path.resolve(pluginFile))
|
|
111
|
+
assert.ok(drift, 'should detect modified plugin file')
|
|
112
|
+
assert.equal(drift!.type, 'modified')
|
|
113
|
+
assert.ok(drift!.previousHash)
|
|
114
|
+
assert.ok(drift!.nextHash)
|
|
115
|
+
assert.notEqual(drift!.previousHash, drift!.nextHash)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('deleted plugin file is no longer in watch targets (no drift)', () => {
|
|
119
|
+
// pushIfExists skips non-existent files, so deletion means the file
|
|
120
|
+
// simply drops out of the watch targets — no drift is generated.
|
|
121
|
+
const pluginDir = path.join(process.env.DATA_DIR!, 'plugins')
|
|
122
|
+
fs.mkdirSync(pluginDir, { recursive: true })
|
|
123
|
+
const pluginFile = path.join(pluginDir, 'test-delete-plugin.js')
|
|
124
|
+
fs.writeFileSync(pluginFile, 'module.exports = {}')
|
|
125
|
+
|
|
126
|
+
// Baseline
|
|
127
|
+
integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: true })
|
|
128
|
+
|
|
129
|
+
// Delete
|
|
130
|
+
fs.unlinkSync(pluginFile)
|
|
131
|
+
|
|
132
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: true })
|
|
133
|
+
const drift = result.drifts.find((d) => d.filePath === path.resolve(pluginFile))
|
|
134
|
+
assert.equal(drift, undefined, 'deleted file should not appear as drift')
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('new plugin file is baselined without drift on first run', () => {
|
|
138
|
+
const pluginDir = path.join(process.env.DATA_DIR!, 'plugins')
|
|
139
|
+
fs.mkdirSync(pluginDir, { recursive: true })
|
|
140
|
+
const pluginFile = path.join(pluginDir, 'brand-new-plugin.js')
|
|
141
|
+
fs.writeFileSync(pluginFile, 'module.exports = { name: "new" }')
|
|
142
|
+
|
|
143
|
+
const result = integrityMonitor.runIntegrityMonitor({ integrityMonitorEnabled: true })
|
|
144
|
+
// First time seeing the file — establishes baseline, no drift
|
|
145
|
+
const drift = result.drifts.find((d) => d.filePath === path.resolve(pluginFile))
|
|
146
|
+
assert.equal(drift, undefined, 'new file on first run should not produce drift')
|
|
147
|
+
assert.ok(result.checkedFiles > 0)
|
|
148
|
+
})
|
|
149
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function safeJsonParse<T>(raw: unknown, fallback: T): T {
|
|
2
|
+
if (typeof raw !== 'string') return fallback
|
|
3
|
+
const trimmed = raw.trim()
|
|
4
|
+
if (!trimmed) return fallback
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(trimmed) as T
|
|
7
|
+
} catch {
|
|
8
|
+
return fallback
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function safeJsonParseObject<T extends object = Record<string, unknown>>(raw: unknown): T | null {
|
|
13
|
+
const parsed = safeJsonParse<unknown>(raw, null)
|
|
14
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
15
|
+
? parsed as T
|
|
16
|
+
: null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function safeJsonParseArray<T = unknown>(raw: unknown): T[] | null {
|
|
20
|
+
const parsed = safeJsonParse<unknown>(raw, null)
|
|
21
|
+
return Array.isArray(parsed) ? parsed as T[] : null
|
|
22
|
+
}
|
|
@@ -117,17 +117,17 @@ function addRawMemory(data: {
|
|
|
117
117
|
}): MemoryEntry {
|
|
118
118
|
const id = crypto.randomBytes(6).toString('hex')
|
|
119
119
|
const now = Date.now()
|
|
120
|
-
stmts.insert.run(
|
|
121
|
-
id,
|
|
122
|
-
data.agentId || null,
|
|
123
|
-
data.sessionId || null,
|
|
124
|
-
data.
|
|
125
|
-
data.
|
|
126
|
-
data.
|
|
127
|
-
|
|
128
|
-
now,
|
|
129
|
-
now
|
|
130
|
-
)
|
|
120
|
+
stmts.insert.run({
|
|
121
|
+
id: data.id || 'mem-1',
|
|
122
|
+
agentId: data.agentId || null,
|
|
123
|
+
sessionId: data.sessionId || null,
|
|
124
|
+
taskId: data.taskId || null,
|
|
125
|
+
url: data.url || null,
|
|
126
|
+
category: data.category,
|
|
127
|
+
textContent: data.textContent || null,
|
|
128
|
+
createdAt: now,
|
|
129
|
+
updatedAt: now
|
|
130
|
+
})
|
|
131
131
|
return {
|
|
132
132
|
id,
|
|
133
133
|
agentId: data.agentId || null,
|
|
@@ -392,7 +392,7 @@ describe('isolation between knowledge and agent memory', () => {
|
|
|
392
392
|
})
|
|
393
393
|
|
|
394
394
|
it('knowledge entries do not appear in agent-scoped memory list', () => {
|
|
395
|
-
const agentMemories = (stmts.listByAgent.all('agent-xyz'
|
|
395
|
+
const agentMemories = (stmts.listByAgent.all('agent-xyz') as Record<string, unknown>[]).map(rowToEntry)
|
|
396
396
|
for (const e of agentMemories) {
|
|
397
397
|
assert.notEqual(e.category, 'knowledge')
|
|
398
398
|
assert.equal(e.agentId, 'agent-xyz')
|
|
@@ -402,7 +402,7 @@ describe('isolation between knowledge and agent memory', () => {
|
|
|
402
402
|
it('knowledge entries do not appear in agent-scoped search', () => {
|
|
403
403
|
const ftsQuery = buildFtsQuery('quantum entanglement')
|
|
404
404
|
if (!ftsQuery) return
|
|
405
|
-
const agentResults = (stmts.searchByAgent.all(ftsQuery
|
|
405
|
+
const agentResults = (stmts.searchByAgent.all(ftsQuery) as Record<string, unknown>[]).map(rowToEntry)
|
|
406
406
|
for (const e of agentResults) {
|
|
407
407
|
assert.equal(e.agentId, 'agent-xyz')
|
|
408
408
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as cheerio from 'cheerio'
|
|
2
|
+
import { dedup } from '@/lib/shared-utils'
|
|
2
3
|
import { truncate } from './session-tools/context'
|
|
3
4
|
|
|
4
5
|
const BARE_LINK_RE = /https?:\/\/\S+/gi
|
|
@@ -11,7 +12,7 @@ export async function runLinkUnderstanding(message: string): Promise<string[]> {
|
|
|
11
12
|
const links = message.match(BARE_LINK_RE)
|
|
12
13
|
if (!links || links.length === 0) return []
|
|
13
14
|
|
|
14
|
-
const uniqueLinks =
|
|
15
|
+
const uniqueLinks = dedup(links).slice(0, 3) // Limit to first 3 links
|
|
15
16
|
const results: string[] = []
|
|
16
17
|
|
|
17
18
|
for (const url of uniqueLinks) {
|
|
@@ -5,7 +5,7 @@ import path from 'node:path'
|
|
|
5
5
|
import { spawnSync } from 'node:child_process'
|
|
6
6
|
import { describe, it } from 'node:test'
|
|
7
7
|
|
|
8
|
-
import { stripMainLoopMetaForPersistence } from './main-agent-loop
|
|
8
|
+
import { stripMainLoopMetaForPersistence } from './main-agent-loop'
|
|
9
9
|
|
|
10
10
|
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../..')
|
|
11
11
|
|
|
@@ -43,9 +43,9 @@ function runWithTempDataDir(script: string) {
|
|
|
43
43
|
/** Shared setup script that creates one agent and one heartbeat-enabled main session */
|
|
44
44
|
function sessionSetupScript(sessionOverrides?: string, extraSessions?: string): string {
|
|
45
45
|
return `
|
|
46
|
-
const storageMod = await import('./src/lib/server/storage
|
|
46
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
47
47
|
const storage = storageMod.default || storageMod['module.exports'] || storageMod
|
|
48
|
-
const mainLoopMod = await import('./src/lib/server/main-agent-loop
|
|
48
|
+
const mainLoopMod = await import('./src/lib/server/main-agent-loop')
|
|
49
49
|
const mainLoop = mainLoopMod.default || mainLoopMod['module.exports'] || mainLoopMod
|
|
50
50
|
|
|
51
51
|
storage.saveAgents({
|
|
@@ -195,6 +195,68 @@ describe('main-agent-loop advanced', () => {
|
|
|
195
195
|
assert.equal(output.followupOk, null, 'no followup on terminal ack')
|
|
196
196
|
})
|
|
197
197
|
|
|
198
|
+
it('resets metadata miss count when structured metadata returns and keeps terminal acks at zero', () => {
|
|
199
|
+
const meta = heartbeatMetaLine('progress', 'deploy', 'continue')
|
|
200
|
+
const output = runWithTempDataDir(`
|
|
201
|
+
${sessionSetupScript()}
|
|
202
|
+
|
|
203
|
+
const miss1 = mainLoop.handleMainLoopRunResult({
|
|
204
|
+
sessionId: 'main',
|
|
205
|
+
message: 'Continue objective.',
|
|
206
|
+
internal: true,
|
|
207
|
+
source: 'heartbeat',
|
|
208
|
+
resultText: 'Still working without structured metadata.',
|
|
209
|
+
})
|
|
210
|
+
const state1 = mainLoop.getMainLoopStateForSession('main')
|
|
211
|
+
|
|
212
|
+
const miss2 = mainLoop.handleMainLoopRunResult({
|
|
213
|
+
sessionId: 'main',
|
|
214
|
+
message: 'Continue objective.',
|
|
215
|
+
internal: true,
|
|
216
|
+
source: 'heartbeat',
|
|
217
|
+
resultText: 'Another plain-text update without metadata.',
|
|
218
|
+
})
|
|
219
|
+
const state2 = mainLoop.getMainLoopStateForSession('main')
|
|
220
|
+
|
|
221
|
+
const withMeta = mainLoop.handleMainLoopRunResult({
|
|
222
|
+
sessionId: 'main',
|
|
223
|
+
message: 'Continue objective.',
|
|
224
|
+
internal: true,
|
|
225
|
+
source: 'heartbeat',
|
|
226
|
+
resultText: \`Metadata restored.\\n${meta}\`,
|
|
227
|
+
})
|
|
228
|
+
const state3 = mainLoop.getMainLoopStateForSession('main')
|
|
229
|
+
|
|
230
|
+
const terminalAck = mainLoop.handleMainLoopRunResult({
|
|
231
|
+
sessionId: 'main',
|
|
232
|
+
message: 'Continue objective.',
|
|
233
|
+
internal: true,
|
|
234
|
+
source: 'heartbeat',
|
|
235
|
+
resultText: 'HEARTBEAT_OK',
|
|
236
|
+
})
|
|
237
|
+
const state4 = mainLoop.getMainLoopStateForSession('main')
|
|
238
|
+
|
|
239
|
+
console.log(JSON.stringify({
|
|
240
|
+
followupMiss1: miss1,
|
|
241
|
+
followupMiss2: miss2,
|
|
242
|
+
followupWithMeta: withMeta,
|
|
243
|
+
followupTerminalAck: terminalAck,
|
|
244
|
+
missCount1: state1?.metaMissCount ?? -1,
|
|
245
|
+
missCount2: state2?.metaMissCount ?? -1,
|
|
246
|
+
missCount3: state3?.metaMissCount ?? -1,
|
|
247
|
+
missCount4: state4?.metaMissCount ?? -1,
|
|
248
|
+
statusAfterAck: state4?.status ?? null,
|
|
249
|
+
}))
|
|
250
|
+
`)
|
|
251
|
+
|
|
252
|
+
assert.equal(output.missCount1, 1)
|
|
253
|
+
assert.equal(output.missCount2, 2)
|
|
254
|
+
assert.equal(output.missCount3, 0, 'structured metadata resets the miss counter')
|
|
255
|
+
assert.equal(output.missCount4, 0, 'terminal ack should not count as a metadata miss')
|
|
256
|
+
assert.equal(output.statusAfterAck, 'ok')
|
|
257
|
+
assert.equal(output.followupTerminalAck, null)
|
|
258
|
+
})
|
|
259
|
+
|
|
198
260
|
// ─────────────────────────────────────────────────────────────────────
|
|
199
261
|
// 3. Chain reset on error
|
|
200
262
|
// ─────────────────────────────────────────────────────────────────────
|
|
@@ -35,9 +35,9 @@ function runWithTempDataDir(script: string) {
|
|
|
35
35
|
describe('main-agent-loop', () => {
|
|
36
36
|
it('fans out events to durable main sessions and shapes heartbeat prompts', () => {
|
|
37
37
|
const output = runWithTempDataDir(`
|
|
38
|
-
const storageMod = await import('./src/lib/server/storage
|
|
38
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
39
39
|
const storage = storageMod.default || storageMod['module.exports'] || storageMod
|
|
40
|
-
const mainLoopMod = await import('./src/lib/server/main-agent-loop
|
|
40
|
+
const mainLoopMod = await import('./src/lib/server/main-agent-loop')
|
|
41
41
|
const mainLoop = mainLoopMod.default || mainLoopMod['module.exports'] || mainLoopMod
|
|
42
42
|
|
|
43
43
|
storage.saveAgents({
|
|
@@ -113,9 +113,9 @@ describe('main-agent-loop', () => {
|
|
|
113
113
|
|
|
114
114
|
it('updates state from heartbeat metadata and returns a bounded follow-up', () => {
|
|
115
115
|
const output = runWithTempDataDir(`
|
|
116
|
-
const storageMod = await import('./src/lib/server/storage
|
|
116
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
117
117
|
const storage = storageMod.default || storageMod['module.exports'] || storageMod
|
|
118
|
-
const mainLoopMod = await import('./src/lib/server/main-agent-loop
|
|
118
|
+
const mainLoopMod = await import('./src/lib/server/main-agent-loop')
|
|
119
119
|
const mainLoop = mainLoopMod.default || mainLoopMod['module.exports'] || mainLoopMod
|
|
120
120
|
|
|
121
121
|
storage.saveAgents({
|
|
@@ -195,9 +195,9 @@ describe('main-agent-loop', () => {
|
|
|
195
195
|
|
|
196
196
|
it('does not keep chaining when the heartbeat explicitly reports ok', () => {
|
|
197
197
|
const output = runWithTempDataDir(`
|
|
198
|
-
const storageMod = await import('./src/lib/server/storage
|
|
198
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
199
199
|
const storage = storageMod.default || storageMod['module.exports'] || storageMod
|
|
200
|
-
const mainLoopMod = await import('./src/lib/server/main-agent-loop
|
|
200
|
+
const mainLoopMod = await import('./src/lib/server/main-agent-loop')
|
|
201
201
|
const mainLoop = mainLoopMod.default || mainLoopMod['module.exports'] || mainLoopMod
|
|
202
202
|
|
|
203
203
|
storage.saveAgents({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hmrSingleton } from '@/lib/shared-utils'
|
|
1
2
|
import type { GoalContract, Message, MessageToolEvent, Session } from '@/types'
|
|
2
3
|
import { mergeGoalContracts, parseGoalContractFromText, parseMainLoopPlan, parseMainLoopReview } from './autonomy-contract'
|
|
3
4
|
import { enqueueSystemEvent } from './system-events'
|
|
@@ -77,9 +78,7 @@ export interface HandleMainLoopRunResultInput {
|
|
|
77
78
|
|
|
78
79
|
type MainSessionLike = Partial<Session> & Record<string, unknown>
|
|
79
80
|
|
|
80
|
-
const
|
|
81
|
-
const globalScope = globalThis as typeof globalThis & { [globalKey]?: Map<string, MainLoopState> }
|
|
82
|
-
const stateMap = globalScope[globalKey] ?? (globalScope[globalKey] = new Map<string, MainLoopState>())
|
|
81
|
+
const stateMap = hmrSingleton('__swarmclaw_main_loop_state__', () => new Map<string, MainLoopState>())
|
|
83
82
|
|
|
84
83
|
function now(): number {
|
|
85
84
|
return Date.now()
|
|
@@ -506,6 +505,21 @@ export function getMainLoopStateForSession(sessionId: string): MainLoopState | n
|
|
|
506
505
|
return state ? normalizeState(state) : null
|
|
507
506
|
}
|
|
508
507
|
|
|
508
|
+
/**
|
|
509
|
+
* Remove stateMap entries for sessions that no longer exist.
|
|
510
|
+
* Called periodically by the daemon health sweep.
|
|
511
|
+
*/
|
|
512
|
+
export function pruneMainLoopState(liveSessionIds: Set<string>): number {
|
|
513
|
+
let removed = 0
|
|
514
|
+
for (const sessionId of stateMap.keys()) {
|
|
515
|
+
if (!liveSessionIds.has(sessionId)) {
|
|
516
|
+
stateMap.delete(sessionId)
|
|
517
|
+
removed++
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return removed
|
|
521
|
+
}
|
|
522
|
+
|
|
509
523
|
export function setMainLoopStateForSession(sessionId: string, patch: Partial<MainLoopState>): MainLoopState | null {
|
|
510
524
|
const current = getOrCreateState(sessionId)
|
|
511
525
|
if (!current) return null
|
|
@@ -608,15 +622,15 @@ export function handleMainLoopRunResult(input: HandleMainLoopRunResultInput): Ma
|
|
|
608
622
|
state.updatedAt = nowTs
|
|
609
623
|
state.missionTokens += Math.max(0, Math.trunc((input.inputTokens || 0) + (input.outputTokens || 0)))
|
|
610
624
|
state.missionCostUsd += Math.max(0, Number(input.estimatedCost || 0))
|
|
611
|
-
|
|
625
|
+
const cleanedResult = persistedText.trim()
|
|
626
|
+
const waitingForExternal = extractWaitSignal(resultText, toolEvents)
|
|
627
|
+
const gotTerminalAck = /^HEARTBEAT_OK$/i.test(cleanedResult) || /^NO_MESSAGE$/i.test(cleanedResult)
|
|
628
|
+
state.metaMissCount = heartbeat || plan || review || gotTerminalAck ? 0 : state.metaMissCount + 1
|
|
612
629
|
|
|
613
630
|
if (input.internal) {
|
|
614
631
|
state.pendingEvents = []
|
|
615
632
|
}
|
|
616
633
|
|
|
617
|
-
const cleanedResult = persistedText.trim()
|
|
618
|
-
const waitingForExternal = extractWaitSignal(resultText, toolEvents)
|
|
619
|
-
const gotTerminalAck = /^HEARTBEAT_OK$/i.test(cleanedResult) || /^NO_MESSAGE$/i.test(cleanedResult)
|
|
620
634
|
const needsReplan = review?.needs_replan === true || ((review?.confidence ?? 1) < 0.45)
|
|
621
635
|
const limit = followupLimit()
|
|
622
636
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
2
|
import { test } from 'node:test'
|
|
3
|
-
import { runMcpConformanceCheck } from './mcp-conformance
|
|
3
|
+
import { runMcpConformanceCheck } from './mcp-conformance'
|
|
4
4
|
|
|
5
5
|
test('runMcpConformanceCheck reports connect/list failure for unsupported transport', async () => {
|
|
6
6
|
const result = await runMcpConformanceCheck({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { McpServerConfig } from '@/types'
|
|
2
2
|
import { connectMcpServer, disconnectMcpServer } from './mcp-client'
|
|
3
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
3
4
|
|
|
4
5
|
export interface McpConformanceIssue {
|
|
5
6
|
level: 'error' | 'warning'
|
|
@@ -209,7 +210,7 @@ export async function runMcpConformanceCheck(
|
|
|
209
210
|
issues.push({
|
|
210
211
|
level: 'error',
|
|
211
212
|
code: 'smoke_tool_failed',
|
|
212
|
-
message:
|
|
213
|
+
message: errorMessage(err),
|
|
213
214
|
toolName: smokeToolName,
|
|
214
215
|
})
|
|
215
216
|
} finally {
|
|
@@ -220,7 +221,7 @@ export async function runMcpConformanceCheck(
|
|
|
220
221
|
issues.push({
|
|
221
222
|
level: 'error',
|
|
222
223
|
code: 'connect_or_list_failed',
|
|
223
|
-
message:
|
|
224
|
+
message: errorMessage(err),
|
|
224
225
|
})
|
|
225
226
|
} finally {
|
|
226
227
|
if (client && transport) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getMemoryDb } from './memory-db'
|
|
2
2
|
import { HumanMessage } from '@langchain/core/messages'
|
|
3
|
+
import { errorMessage } from '@/lib/shared-utils'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Produce daily digests per agent and prune stale entries.
|
|
@@ -89,7 +90,7 @@ export async function runDailyConsolidation(): Promise<{
|
|
|
89
90
|
|
|
90
91
|
digestsCreated++
|
|
91
92
|
} catch (err: unknown) {
|
|
92
|
-
errors.push(`Agent ${agentId}: ${
|
|
93
|
+
errors.push(`Agent ${agentId}: ${errorMessage(err)}`)
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
|