@swarmclawai/swarmclaw 0.8.4 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/bin/swarmclaw.js +5 -1
- package/bin/worker-cmd.js +73 -0
- package/package.json +2 -1
- package/src/app/api/agents/[id]/route.ts +17 -7
- package/src/app/api/agents/route.ts +21 -8
- package/src/app/api/approvals/route.test.ts +6 -6
- package/src/app/api/approvals/route.ts +2 -1
- package/src/app/api/auth/route.ts +2 -3
- package/src/app/api/chatrooms/[id]/chat/route.test.ts +299 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +3 -2
- package/src/app/api/chatrooms/[id]/route.ts +7 -6
- package/src/app/api/chats/[id]/chat/route.test.ts +496 -0
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/clear/route.ts +9 -9
- package/src/app/api/chats/[id]/devserver/route.ts +2 -1
- package/src/app/api/chats/[id]/edit-resend/route.ts +3 -4
- package/src/app/api/chats/[id]/fork/route.ts +3 -5
- package/src/app/api/chats/[id]/restore/route.ts +6 -7
- package/src/app/api/chats/[id]/retry/route.ts +3 -4
- package/src/app/api/chats/[id]/route.ts +61 -62
- package/src/app/api/chats/route.ts +7 -1
- package/src/app/api/connectors/[id]/route.ts +7 -8
- package/src/app/api/connectors/route.ts +5 -4
- package/src/app/api/eval/run/route.ts +2 -1
- package/src/app/api/eval/suite/route.ts +2 -1
- package/src/app/api/external-agents/route.test.ts +1 -1
- package/src/app/api/external-agents/route.ts +2 -2
- package/src/app/api/files/serve/route.ts +1 -1
- package/src/app/api/gateways/[id]/route.ts +7 -5
- package/src/app/api/gateways/route.ts +1 -1
- package/src/app/api/knowledge/upload/route.ts +1 -1
- package/src/app/api/logs/route.ts +5 -7
- package/src/app/api/memory-images/[filename]/route.ts +2 -3
- package/src/app/api/openclaw/agent-files/route.ts +4 -3
- package/src/app/api/openclaw/approvals/route.ts +3 -4
- package/src/app/api/openclaw/config-sync/route.ts +3 -2
- package/src/app/api/openclaw/cron/route.ts +3 -2
- package/src/app/api/openclaw/dotenv-keys/route.ts +2 -1
- package/src/app/api/openclaw/exec-config/route.ts +3 -2
- package/src/app/api/openclaw/gateway/route.ts +5 -4
- package/src/app/api/openclaw/history/route.ts +3 -2
- package/src/app/api/openclaw/media/route.ts +2 -1
- package/src/app/api/openclaw/permissions/route.ts +3 -2
- package/src/app/api/openclaw/sandbox-env/route.ts +3 -2
- package/src/app/api/openclaw/skills/install/route.ts +2 -1
- package/src/app/api/openclaw/skills/remove/route.ts +2 -1
- package/src/app/api/openclaw/skills/route.ts +3 -2
- package/src/app/api/orchestrator/run/route.ts +5 -14
- package/src/app/api/perf/route.ts +43 -0
- package/src/app/api/plugins/dependencies/route.ts +2 -1
- package/src/app/api/plugins/install/route.ts +2 -1
- package/src/app/api/plugins/marketplace/route.ts +3 -2
- package/src/app/api/plugins/settings/route.ts +2 -1
- package/src/app/api/preview-server/route.ts +11 -10
- package/src/app/api/projects/[id]/route.ts +1 -1
- package/src/app/api/schedules/[id]/route.test.ts +128 -0
- package/src/app/api/schedules/[id]/route.ts +43 -43
- package/src/app/api/schedules/[id]/run/route.ts +11 -62
- package/src/app/api/schedules/route.ts +21 -87
- package/src/app/api/settings/route.ts +2 -0
- package/src/app/api/setup/doctor/route.ts +9 -8
- package/src/app/api/tasks/[id]/approve/route.ts +33 -30
- package/src/app/api/tasks/[id]/route.ts +12 -35
- package/src/app/api/tasks/import/github/route.ts +2 -1
- package/src/app/api/tasks/route.ts +79 -91
- package/src/app/api/wallets/[id]/approve/route.ts +2 -1
- package/src/app/api/wallets/[id]/route.ts +13 -19
- package/src/app/api/wallets/[id]/send/route.ts +2 -1
- package/src/app/api/wallets/route.ts +2 -1
- package/src/app/api/webhooks/[id]/route.ts +2 -1
- package/src/app/api/webhooks/route.test.ts +3 -1
- package/src/app/page.tsx +23 -331
- package/src/cli/index.js +19 -0
- package/src/cli/index.ts +38 -7
- package/src/cli/spec.js +9 -0
- package/src/components/activity/activity-feed.tsx +7 -4
- package/src/components/agents/agent-card.tsx +32 -6
- package/src/components/agents/agent-chat-list.tsx +55 -22
- package/src/components/agents/agent-files-editor.tsx +3 -2
- package/src/components/agents/agent-sheet.tsx +123 -22
- package/src/components/agents/inspector-panel.tsx +1 -1
- package/src/components/agents/openclaw-skills-panel.tsx +2 -1
- package/src/components/agents/trash-list.tsx +1 -1
- package/src/components/auth/access-key-gate.tsx +8 -2
- package/src/components/auth/setup-wizard.tsx +10 -9
- package/src/components/auth/user-picker.tsx +3 -2
- package/src/components/chat/chat-area.tsx +20 -1
- package/src/components/chat/chat-card.tsx +18 -3
- package/src/components/chat/chat-header.tsx +24 -4
- package/src/components/chat/chat-list.tsx +2 -11
- package/src/components/chat/heartbeat-history-panel.tsx +2 -1
- package/src/components/chat/message-bubble.tsx +45 -6
- package/src/components/chat/message-list.tsx +280 -145
- package/src/components/chat/streaming-bubble.tsx +217 -60
- package/src/components/chat/swarm-panel.test.ts +274 -0
- package/src/components/chat/swarm-panel.tsx +410 -0
- package/src/components/chat/swarm-status-card.tsx +346 -0
- package/src/components/chat/tool-call-bubble.tsx +48 -23
- package/src/components/chatrooms/chatroom-list.tsx +8 -5
- package/src/components/chatrooms/chatroom-message.tsx +10 -7
- package/src/components/chatrooms/chatroom-view.tsx +12 -9
- package/src/components/connectors/connector-health.tsx +6 -4
- package/src/components/connectors/connector-list.tsx +16 -11
- package/src/components/connectors/connector-sheet.tsx +12 -6
- package/src/components/home/home-view.tsx +38 -24
- package/src/components/input/chat-input.tsx +10 -1
- package/src/components/layout/app-layout.tsx +2 -38
- package/src/components/layout/sheet-layer.tsx +50 -0
- package/src/components/mcp-servers/mcp-server-list.tsx +37 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +12 -2
- package/src/components/plugins/plugin-list.tsx +8 -4
- package/src/components/plugins/plugin-sheet.tsx +2 -1
- package/src/components/providers/provider-list.tsx +3 -2
- package/src/components/providers/provider-sheet.tsx +2 -1
- package/src/components/runs/run-list.tsx +11 -7
- package/src/components/schedules/schedule-card.tsx +5 -3
- package/src/components/shared/agent-switch-dialog.tsx +1 -1
- package/src/components/shared/attachment-chip.tsx +19 -3
- package/src/components/shared/notification-center.tsx +6 -3
- package/src/components/shared/settings/plugin-manager.tsx +3 -2
- package/src/components/shared/settings/section-embedding.tsx +2 -1
- package/src/components/shared/settings/section-orchestrator.tsx +2 -1
- package/src/components/shared/settings/section-user-preferences.tsx +107 -0
- package/src/components/shared/settings/settings-page.tsx +13 -9
- package/src/components/skills/clawhub-browser.tsx +15 -4
- package/src/components/skills/skill-list.tsx +15 -4
- package/src/components/tasks/approvals-panel.tsx +2 -1
- package/src/components/tasks/task-board.tsx +35 -37
- package/src/components/tasks/task-sheet.tsx +4 -3
- package/src/components/ui/full-screen-loader.tsx +164 -0
- package/src/components/wallets/wallet-approval-dialog.tsx +2 -1
- package/src/components/wallets/wallet-panel.tsx +6 -5
- package/src/components/wallets/wallet-section.tsx +3 -2
- package/src/components/webhooks/webhook-list.tsx +4 -5
- package/src/components/webhooks/webhook-sheet.tsx +6 -6
- package/src/hooks/use-app-bootstrap.ts +202 -0
- package/src/hooks/use-mounted-ref.ts +14 -0
- package/src/hooks/use-now.ts +31 -0
- package/src/hooks/use-openclaw-gateway.ts +2 -1
- package/src/instrumentation.ts +20 -8
- package/src/lib/agent-default-tools.test.ts +52 -0
- package/src/lib/agent-default-tools.ts +40 -0
- package/src/lib/api-client.test.ts +21 -0
- package/src/lib/api-client.ts +6 -11
- package/src/lib/canvas-content.test.ts +360 -0
- package/src/lib/chat-streaming-state.test.ts +49 -2
- package/src/lib/chat-streaming-state.ts +26 -10
- package/src/lib/fetch-timeout.test.ts +54 -0
- package/src/lib/fetch-timeout.ts +60 -3
- package/src/lib/live-tool-events.test.ts +77 -0
- package/src/lib/live-tool-events.ts +73 -0
- package/src/lib/local-observability.test.ts +2 -2
- package/src/lib/openclaw-endpoint.test.ts +1 -1
- package/src/lib/providers/anthropic.ts +12 -16
- package/src/lib/providers/index.ts +4 -2
- package/src/lib/providers/ollama.ts +9 -6
- package/src/lib/providers/openai.ts +11 -14
- package/src/lib/runtime-env.test.ts +8 -8
- package/src/lib/schedule-dedupe-advanced.test.ts +2 -2
- package/src/lib/schedule-dedupe.test.ts +1 -1
- package/src/lib/schedule-dedupe.ts +3 -2
- package/src/lib/server/agent-thread-session.test.ts +6 -6
- package/src/lib/server/agent-thread-session.ts +6 -9
- package/src/lib/server/alert-dispatch.ts +2 -1
- package/src/lib/server/api-routes.test.ts +6 -6
- package/src/lib/server/approval-connector-notify.test.ts +4 -4
- package/src/lib/server/approvals-auto-approve.test.ts +29 -29
- package/src/lib/server/approvals.test.ts +317 -0
- package/src/lib/server/approvals.ts +5 -4
- package/src/lib/server/autonomy-runtime.test.ts +11 -11
- package/src/lib/server/browser-state.ts +2 -2
- package/src/lib/server/capability-router.test.ts +1 -1
- package/src/lib/server/capability-router.ts +3 -2
- package/src/lib/server/chat-execution-advanced.test.ts +15 -2
- package/src/lib/server/chat-execution-connector-delivery.ts +67 -0
- package/src/lib/server/chat-execution-disabled.test.ts +3 -3
- package/src/lib/server/chat-execution-eval-history.test.ts +3 -3
- package/src/lib/server/chat-execution-heartbeat.test.ts +42 -1
- package/src/lib/server/chat-execution-session-sync.test.ts +119 -0
- package/src/lib/server/chat-execution-tool-events.ts +116 -0
- package/src/lib/server/chat-execution-utils.test.ts +479 -0
- package/src/lib/server/chat-execution-utils.ts +533 -0
- package/src/lib/server/chat-execution.ts +153 -748
- package/src/lib/server/chat-streaming-utils.ts +174 -0
- package/src/lib/server/chat-turn-tool-routing.ts +310 -0
- package/src/lib/server/chatroom-session-persistence.test.ts +2 -2
- package/src/lib/server/clawhub-client.ts +2 -1
- package/src/lib/server/collection-helpers.test.ts +92 -0
- package/src/lib/server/collection-helpers.ts +25 -3
- package/src/lib/server/connectors/access.ts +146 -0
- package/src/lib/server/connectors/bluebubbles.test.ts +1 -1
- package/src/lib/server/connectors/bluebubbles.ts +4 -4
- package/src/lib/server/connectors/commands.ts +367 -0
- package/src/lib/server/connectors/connector-routing.test.ts +4 -4
- package/src/lib/server/connectors/delivery.ts +142 -0
- package/src/lib/server/connectors/discord.ts +37 -40
- package/src/lib/server/connectors/email.ts +11 -10
- package/src/lib/server/connectors/googlechat.ts +4 -4
- package/src/lib/server/connectors/inbound-audio-transcription.ts +2 -1
- package/src/lib/server/connectors/ingress-delivery.ts +23 -0
- package/src/lib/server/connectors/manager-roundtrip.test.ts +300 -0
- package/src/lib/server/connectors/manager.test.ts +352 -77
- package/src/lib/server/connectors/manager.ts +134 -673
- package/src/lib/server/connectors/matrix.ts +4 -4
- package/src/lib/server/connectors/message-sentinel.ts +7 -0
- package/src/lib/server/connectors/openclaw.test.ts +1 -1
- package/src/lib/server/connectors/openclaw.ts +8 -10
- package/src/lib/server/connectors/outbox.test.ts +192 -0
- package/src/lib/server/connectors/outbox.ts +369 -0
- package/src/lib/server/connectors/pairing.test.ts +18 -1
- package/src/lib/server/connectors/pairing.ts +49 -4
- package/src/lib/server/connectors/policy.ts +9 -3
- package/src/lib/server/connectors/reconnect-state.ts +71 -0
- package/src/lib/server/connectors/response-media.ts +256 -0
- package/src/lib/server/connectors/runtime-state.ts +67 -0
- package/src/lib/server/connectors/session.test.ts +357 -0
- package/src/lib/server/connectors/session.ts +422 -0
- package/src/lib/server/connectors/signal.ts +7 -7
- package/src/lib/server/connectors/slack.ts +43 -43
- package/src/lib/server/connectors/teams.ts +4 -4
- package/src/lib/server/connectors/telegram.ts +37 -43
- package/src/lib/server/connectors/types.ts +31 -1
- package/src/lib/server/connectors/whatsapp.test.ts +108 -0
- package/src/lib/server/connectors/whatsapp.ts +106 -34
- package/src/lib/server/context-manager.test.ts +409 -0
- package/src/lib/server/cost.test.ts +1 -1
- package/src/lib/server/daemon-policy.ts +78 -0
- package/src/lib/server/daemon-state-connectors.test.ts +167 -0
- package/src/lib/server/daemon-state.test.ts +283 -55
- package/src/lib/server/daemon-state.ts +106 -109
- package/src/lib/server/data-dir.test.ts +5 -5
- package/src/lib/server/data-dir.ts +4 -0
- package/src/lib/server/delegation-jobs-advanced.test.ts +1 -1
- package/src/lib/server/delegation-jobs.test.ts +87 -0
- package/src/lib/server/delegation-jobs.ts +42 -48
- package/src/lib/server/devserver-launch.ts +1 -1
- package/src/lib/server/document-utils.ts +7 -9
- package/src/lib/server/elevenlabs.ts +2 -1
- package/src/lib/server/embeddings.test.ts +105 -0
- package/src/lib/server/ethereum.ts +3 -2
- package/src/lib/server/eval/agent-regression.ts +3 -2
- package/src/lib/server/eval/runner.ts +2 -1
- package/src/lib/server/eval/scorer.ts +2 -1
- package/src/lib/server/evm-swap.ts +2 -1
- package/src/lib/server/gateway/protocol.test.ts +1 -1
- package/src/lib/server/guardian.ts +2 -1
- package/src/lib/server/heartbeat-blocked-suppression.test.ts +151 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +6 -6
- package/src/lib/server/heartbeat-service.test.ts +406 -0
- package/src/lib/server/heartbeat-service.ts +54 -7
- package/src/lib/server/heartbeat-wake.test.ts +19 -0
- package/src/lib/server/heartbeat-wake.ts +17 -16
- package/src/lib/server/integrity-monitor.test.ts +149 -0
- package/src/lib/server/json-utils.ts +22 -0
- package/src/lib/server/knowledge-db.test.ts +13 -13
- package/src/lib/server/link-understanding.ts +2 -1
- package/src/lib/server/llm-response-cache.test.ts +1 -1
- package/src/lib/server/main-agent-loop-advanced.test.ts +65 -3
- package/src/lib/server/main-agent-loop.test.ts +6 -6
- package/src/lib/server/main-agent-loop.ts +21 -7
- package/src/lib/server/mcp-client.test.ts +1 -1
- package/src/lib/server/mcp-conformance.test.ts +1 -1
- package/src/lib/server/mcp-conformance.ts +3 -2
- package/src/lib/server/memory-consolidation.ts +2 -1
- package/src/lib/server/memory-db.test.ts +485 -0
- package/src/lib/server/memory-db.ts +39 -26
- package/src/lib/server/memory-graph.test.ts +2 -2
- package/src/lib/server/memory-policy.test.ts +7 -7
- package/src/lib/server/memory-retrieval.test.ts +1 -1
- package/src/lib/server/openclaw-config-sync.ts +2 -1
- package/src/lib/server/openclaw-deploy.test.ts +1 -1
- package/src/lib/server/openclaw-deploy.ts +8 -12
- package/src/lib/server/openclaw-exec-config.ts +2 -1
- package/src/lib/server/openclaw-gateway.ts +6 -7
- package/src/lib/server/openclaw-skills-normalize.ts +2 -1
- package/src/lib/server/openclaw-sync.ts +7 -5
- package/src/lib/server/orchestrator-lg-structure.test.ts +17 -0
- package/src/lib/server/orchestrator-lg.ts +199 -327
- package/src/lib/server/path-utils.ts +31 -0
- package/src/lib/server/perf.ts +161 -0
- package/src/lib/server/plugins-approval-guidance.ts +115 -0
- package/src/lib/server/plugins.test.ts +1 -1
- package/src/lib/server/plugins.ts +22 -132
- package/src/lib/server/process-manager.ts +5 -8
- package/src/lib/server/provider-health.test.ts +137 -0
- package/src/lib/server/provider-health.ts +3 -3
- package/src/lib/server/provider-model-discovery.ts +3 -12
- package/src/lib/server/queue-followups.test.ts +9 -9
- package/src/lib/server/queue-reconcile.test.ts +2 -2
- package/src/lib/server/queue-recovery.test.ts +269 -0
- package/src/lib/server/queue.test.ts +570 -0
- package/src/lib/server/queue.ts +62 -455
- package/src/lib/server/resolve-image.ts +30 -0
- package/src/lib/server/runtime-settings.test.ts +4 -4
- package/src/lib/server/runtime-storage-write-paths.test.ts +60 -0
- package/src/lib/server/schedule-normalization.test.ts +279 -0
- package/src/lib/server/schedule-service.ts +263 -0
- package/src/lib/server/scheduler.ts +17 -74
- package/src/lib/server/session-mailbox.test.ts +191 -0
- package/src/lib/server/session-run-manager.test.ts +640 -0
- package/src/lib/server/session-run-manager.ts +59 -15
- package/src/lib/server/session-tools/autonomy-tools.test.ts +20 -20
- package/src/lib/server/session-tools/calendar.ts +2 -1
- package/src/lib/server/session-tools/canvas.ts +2 -1
- package/src/lib/server/session-tools/chatroom.ts +2 -1
- package/src/lib/server/session-tools/connector.ts +26 -28
- package/src/lib/server/session-tools/context-mgmt.ts +3 -2
- package/src/lib/server/session-tools/crawl.ts +4 -3
- package/src/lib/server/session-tools/crud.ts +105 -324
- package/src/lib/server/session-tools/delegate-fallback.test.ts +9 -9
- package/src/lib/server/session-tools/delegate.ts +6 -8
- package/src/lib/server/session-tools/discovery-approvals.test.ts +15 -15
- package/src/lib/server/session-tools/discovery.ts +4 -3
- package/src/lib/server/session-tools/document.ts +2 -1
- package/src/lib/server/session-tools/email.ts +2 -1
- package/src/lib/server/session-tools/extract.ts +2 -1
- package/src/lib/server/session-tools/file.ts +4 -3
- package/src/lib/server/session-tools/http.ts +2 -1
- package/src/lib/server/session-tools/human-loop.ts +2 -1
- package/src/lib/server/session-tools/image-gen.ts +4 -3
- package/src/lib/server/session-tools/index.ts +26 -30
- package/src/lib/server/session-tools/mailbox.ts +2 -1
- package/src/lib/server/session-tools/manage-connectors.test.ts +4 -4
- package/src/lib/server/session-tools/manage-schedules.test.ts +12 -12
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +5 -5
- package/src/lib/server/session-tools/manage-tasks.test.ts +2 -2
- package/src/lib/server/session-tools/monitor.ts +2 -1
- package/src/lib/server/session-tools/platform.ts +2 -1
- package/src/lib/server/session-tools/plugin-creator.ts +2 -1
- package/src/lib/server/session-tools/replicate.ts +3 -2
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +6 -6
- package/src/lib/server/session-tools/shell.ts +4 -9
- package/src/lib/server/session-tools/subagent.ts +322 -170
- package/src/lib/server/session-tools/table.ts +6 -5
- package/src/lib/server/session-tools/wallet-tool.test.ts +3 -3
- package/src/lib/server/session-tools/wallet.ts +7 -6
- package/src/lib/server/session-tools/web-browser-config.test.ts +1 -0
- package/src/lib/server/session-tools/web-utils.ts +317 -0
- package/src/lib/server/session-tools/web.ts +62 -328
- package/src/lib/server/skill-prompt-budget.test.ts +1 -1
- package/src/lib/server/skills-normalize.ts +2 -1
- package/src/lib/server/storage-item-access.test.ts +302 -0
- package/src/lib/server/storage.ts +366 -314
- package/src/lib/server/stream-agent-chat.test.ts +82 -3
- package/src/lib/server/stream-agent-chat.ts +146 -510
- package/src/lib/server/stream-continuation.ts +412 -0
- package/src/lib/server/subagent-lineage.test.ts +647 -0
- package/src/lib/server/subagent-lineage.ts +435 -0
- package/src/lib/server/subagent-runtime.test.ts +484 -0
- package/src/lib/server/subagent-runtime.ts +419 -0
- package/src/lib/server/subagent-swarm.test.ts +391 -0
- package/src/lib/server/subagent-swarm.ts +564 -0
- package/src/lib/server/system-events.ts +3 -3
- package/src/lib/server/task-followups.test.ts +491 -0
- package/src/lib/server/task-followups.ts +391 -0
- package/src/lib/server/task-lifecycle.test.ts +205 -0
- package/src/lib/server/task-lifecycle.ts +200 -0
- package/src/lib/server/task-quality-gate.test.ts +1 -1
- package/src/lib/server/task-resume.ts +208 -0
- package/src/lib/server/task-service.test.ts +108 -0
- package/src/lib/server/task-service.ts +264 -0
- package/src/lib/server/task-validation.test.ts +1 -1
- package/src/lib/server/test-utils/run-with-temp-data-dir.ts +42 -0
- package/src/lib/server/tool-capability-policy.test.ts +2 -2
- package/src/lib/server/tool-capability-policy.ts +3 -2
- package/src/lib/server/tool-planning.ts +2 -1
- package/src/lib/server/tool-retry.ts +2 -3
- package/src/lib/server/wake-dispatcher.test.ts +303 -0
- package/src/lib/server/wake-dispatcher.ts +318 -0
- package/src/lib/server/wake-mode.test.ts +161 -0
- package/src/lib/server/wake-mode.ts +174 -0
- package/src/lib/server/wallet-service.ts +8 -9
- package/src/lib/server/watch-jobs.ts +2 -1
- package/src/lib/server/workspace-context.ts +2 -2
- package/src/lib/shared-utils.test.ts +142 -0
- package/src/lib/shared-utils.ts +62 -0
- package/src/lib/tool-event-summary.ts +2 -1
- package/src/lib/view-routes.test.ts +100 -0
- package/src/lib/wallet.test.ts +322 -6
- package/src/proxy.test.ts +4 -4
- package/src/proxy.ts +2 -3
- package/src/stores/set-if-changed.ts +40 -0
- package/src/stores/slices/agent-slice.ts +111 -0
- package/src/stores/slices/auth-slice.ts +25 -0
- package/src/stores/slices/data-slice.ts +301 -0
- package/src/stores/slices/index.ts +7 -0
- package/src/stores/slices/session-slice.ts +112 -0
- package/src/stores/slices/task-slice.ts +63 -0
- package/src/stores/slices/ui-slice.ts +192 -0
- package/src/stores/use-app-store.ts +17 -822
- package/src/stores/use-approval-store.ts +2 -1
- package/src/stores/use-chat-store.ts +8 -1
- package/src/types/index.ts +10 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AppSettings } from '@/types'
|
|
2
|
+
import { dedup } from '@/lib/shared-utils'
|
|
2
3
|
import { getToolsForCapability, matchToolCapabilitiesForMessage, TOOL_CAPABILITY } from './tool-planning'
|
|
3
4
|
|
|
4
5
|
export type TaskIntent =
|
|
@@ -29,7 +30,7 @@ function containsAny(text: string, terms: string[]): boolean {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
function dedupe(values: string[]): string[] {
|
|
32
|
-
return
|
|
33
|
+
return dedup(values.filter(Boolean))
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
function isMonitoringOrCurrentEventsRequest(text: string): boolean {
|
|
@@ -78,7 +79,7 @@ function normalizeDelegateOrder(value: unknown): DelegateTool[] {
|
|
|
78
79
|
else if (raw === 'gemini') mapped.push('delegate_to_gemini_cli')
|
|
79
80
|
}
|
|
80
81
|
if (!mapped.length) return fallback
|
|
81
|
-
const deduped =
|
|
82
|
+
const deduped = dedup(mapped)
|
|
82
83
|
for (const tool of fallback) {
|
|
83
84
|
if (!deduped.includes(tool)) deduped.push(tool)
|
|
84
85
|
}
|
|
@@ -494,6 +494,11 @@ describe('buildToolDisciplineLines advanced', () => {
|
|
|
494
494
|
const lines = buildToolDisciplineLines(['delegate', 'shell', 'files'])
|
|
495
495
|
assert.ok(lines.some((line) => line.includes('prefer using them directly for straightforward coding')))
|
|
496
496
|
})
|
|
497
|
+
|
|
498
|
+
it('tells research-capable agents to try another enabled acquisition path before manual fallback', () => {
|
|
499
|
+
const lines = buildToolDisciplineLines(['web_search', 'web_fetch', 'http_request', 'shell'])
|
|
500
|
+
assert.ok(lines.some((line) => line.includes('try one other enabled acquisition path') && line.includes('`shell`') && line.includes('`http_request`')))
|
|
501
|
+
})
|
|
497
502
|
})
|
|
498
503
|
|
|
499
504
|
// ---------------------------------------------------------------------------
|
|
@@ -640,12 +645,12 @@ describe('normalizeAssistantArtifactLinks', () => {
|
|
|
640
645
|
// getExplicitRequiredToolNames
|
|
641
646
|
// ---------------------------------------------------------------------------
|
|
642
647
|
describe('getExplicitRequiredToolNames', () => {
|
|
643
|
-
it('
|
|
648
|
+
it('does not force web_search for generic research phrasing when the tool was not explicitly named', () => {
|
|
644
649
|
const result = getExplicitRequiredToolNames(
|
|
645
650
|
'Search the web for the latest news about AI regulation',
|
|
646
651
|
['web_search', 'web_fetch', 'browser'],
|
|
647
652
|
)
|
|
648
|
-
assert.
|
|
653
|
+
assert.deepEqual(result, [])
|
|
649
654
|
})
|
|
650
655
|
|
|
651
656
|
it('returns empty when no tool matches the message', () => {
|
|
@@ -655,4 +660,12 @@ describe('getExplicitRequiredToolNames', () => {
|
|
|
655
660
|
)
|
|
656
661
|
assert.deepEqual(result, [])
|
|
657
662
|
})
|
|
663
|
+
|
|
664
|
+
it('forces shell when the user explicitly asks for curl execution', () => {
|
|
665
|
+
const result = getExplicitRequiredToolNames(
|
|
666
|
+
'Can you run the curl request in the terminal?',
|
|
667
|
+
['files', 'shell'],
|
|
668
|
+
)
|
|
669
|
+
assert.deepEqual(result, ['shell'])
|
|
670
|
+
})
|
|
658
671
|
})
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { MessageToolEvent } from '@/types'
|
|
2
|
+
import { dedupeConsecutiveToolEvents } from './chat-execution-tool-events'
|
|
3
|
+
|
|
4
|
+
function parseToolJsonObject(raw: string): Record<string, unknown> | null {
|
|
5
|
+
const trimmed = raw.trim()
|
|
6
|
+
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) return null
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(trimmed)
|
|
9
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
10
|
+
? parsed as Record<string, unknown>
|
|
11
|
+
: null
|
|
12
|
+
} catch {
|
|
13
|
+
return null
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function summarizeConnectorToolFailure(output: string): string {
|
|
18
|
+
const trimmed = output.trim()
|
|
19
|
+
const withoutPrefix = trimmed.replace(/^Error:\s*/i, '')
|
|
20
|
+
const parsed = parseToolJsonObject(withoutPrefix) || parseToolJsonObject(trimmed)
|
|
21
|
+
if (parsed) {
|
|
22
|
+
const detail = parsed.detail
|
|
23
|
+
if (detail && typeof detail === 'object' && !Array.isArray(detail)) {
|
|
24
|
+
const detailRecord = detail as Record<string, unknown>
|
|
25
|
+
const message = typeof detailRecord.message === 'string' ? detailRecord.message.trim() : ''
|
|
26
|
+
if (message) return message
|
|
27
|
+
const code = typeof detailRecord.code === 'string' ? detailRecord.code.trim() : ''
|
|
28
|
+
const status = typeof detailRecord.status === 'string' ? detailRecord.status.trim() : ''
|
|
29
|
+
if (code && status) return `${code}: ${status}`
|
|
30
|
+
if (code) return code
|
|
31
|
+
if (status) return status
|
|
32
|
+
}
|
|
33
|
+
const message = typeof parsed.message === 'string' ? parsed.message.trim() : ''
|
|
34
|
+
if (message) return message
|
|
35
|
+
const error = typeof parsed.error === 'string' ? parsed.error.trim() : ''
|
|
36
|
+
if (error) return error
|
|
37
|
+
}
|
|
38
|
+
return withoutPrefix.replace(/\s+/g, ' ').trim() || 'Connector delivery failed.'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function connectorToolEventSucceeded(event: MessageToolEvent): boolean {
|
|
42
|
+
if (!event.output) return false
|
|
43
|
+
const parsed = parseToolJsonObject(event.output)
|
|
44
|
+
const status = typeof parsed?.status === 'string' ? parsed.status.trim().toLowerCase() : ''
|
|
45
|
+
return status === 'sent' || status === 'voice_sent' || status === 'scheduled'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const POSITIVE_CONNECTOR_DELIVERY_RE = /\b(?:i(?:'ve| have)?(?: successfully)? sent|i sent|successfully sent|sent to your|voice note (?:has been|was) sent|message (?:has been|was) sent)\b/i
|
|
49
|
+
|
|
50
|
+
export function reconcileConnectorDeliveryText(text: string, events: MessageToolEvent[]): string {
|
|
51
|
+
const trimmed = text.trim()
|
|
52
|
+
if (!trimmed || !POSITIVE_CONNECTOR_DELIVERY_RE.test(trimmed)) return text
|
|
53
|
+
|
|
54
|
+
const connectorEvents = dedupeConsecutiveToolEvents(events).filter((event) => event.name === 'connector_message_tool')
|
|
55
|
+
if (connectorEvents.length === 0) return text
|
|
56
|
+
if (connectorEvents.some((event) => connectorToolEventSucceeded(event))) return text
|
|
57
|
+
|
|
58
|
+
const latestFailure = [...connectorEvents]
|
|
59
|
+
.reverse()
|
|
60
|
+
.find((event) => event.error === true && typeof event.output === 'string' && event.output.trim())
|
|
61
|
+
|
|
62
|
+
const failureSummary = latestFailure?.output
|
|
63
|
+
? summarizeConnectorToolFailure(latestFailure.output)
|
|
64
|
+
: 'I could not confirm that the connector actually sent anything.'
|
|
65
|
+
|
|
66
|
+
return `I couldn't send that through the configured connector. ${failureSummary}`.trim()
|
|
67
|
+
}
|
|
@@ -35,13 +35,13 @@ function runWithTempDataDir(script: string) {
|
|
|
35
35
|
|
|
36
36
|
test('executeSessionChatTurn persists a visible error for disabled agents', () => {
|
|
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 threadMod = await import('./src/lib/server/agent-thread-session
|
|
40
|
+
const threadMod = await import('./src/lib/server/agent-thread-session')
|
|
41
41
|
const ensureAgentThreadSession = threadMod.ensureAgentThreadSession
|
|
42
42
|
|| threadMod.default?.ensureAgentThreadSession
|
|
43
43
|
|| threadMod['module.exports']?.ensureAgentThreadSession
|
|
44
|
-
const execMod = await import('./src/lib/server/chat-execution
|
|
44
|
+
const execMod = await import('./src/lib/server/chat-execution')
|
|
45
45
|
const executeSessionChatTurn = execMod.executeSessionChatTurn
|
|
46
46
|
|| execMod.default?.executeSessionChatTurn
|
|
47
47
|
|| execMod['module.exports']?.executeSessionChatTurn
|
|
@@ -35,10 +35,10 @@ function runWithTempDataDir(script: string) {
|
|
|
35
35
|
|
|
36
36
|
test('executeSessionChatTurn persists internal eval user turns for same-thread recall', () => {
|
|
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 providersMod = await import('./src/lib/providers/index
|
|
41
|
-
const execMod = await import('./src/lib/server/chat-execution
|
|
40
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
41
|
+
const execMod = await import('./src/lib/server/chat-execution')
|
|
42
42
|
const executeSessionChatTurn = execMod.executeSessionChatTurn
|
|
43
43
|
|| execMod.default?.executeSessionChatTurn
|
|
44
44
|
|| execMod['module.exports']?.executeSessionChatTurn
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import assert from 'node:assert/strict'
|
|
2
2
|
import { describe, it } from 'node:test'
|
|
3
3
|
import type { Message } from '@/types'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
buildAgentRuntimeCapabilities,
|
|
6
|
+
buildEnabledToolsAutonomyGuidance,
|
|
7
|
+
buildNoToolsGuidance,
|
|
8
|
+
pruneSuppressedHeartbeatStreamMessage,
|
|
9
|
+
shouldApplySessionFreshnessReset,
|
|
10
|
+
} from './chat-execution'
|
|
5
11
|
|
|
6
12
|
describe('pruneSuppressedHeartbeatStreamMessage', () => {
|
|
7
13
|
it('removes a trailing streaming assistant heartbeat artifact', () => {
|
|
@@ -37,4 +43,39 @@ describe('pruneSuppressedHeartbeatStreamMessage', () => {
|
|
|
37
43
|
assert.equal(shouldApplySessionFreshnessReset('heartbeat'), true)
|
|
38
44
|
assert.equal(shouldApplySessionFreshnessReset('eval'), false)
|
|
39
45
|
})
|
|
46
|
+
|
|
47
|
+
it('does not advertise tool capabilities when no plugins are enabled', () => {
|
|
48
|
+
assert.deepEqual(buildAgentRuntimeCapabilities([]), ['heartbeats', 'autonomous_loop', 'multi_agent_chat'])
|
|
49
|
+
assert.deepEqual(buildAgentRuntimeCapabilities(['web']), ['tools', 'heartbeats', 'autonomous_loop', 'multi_agent_chat'])
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('tells no-tool agents to report missing capability instead of asking for approval', () => {
|
|
53
|
+
const guidance = buildNoToolsGuidance().join('\n')
|
|
54
|
+
assert.match(guidance, /No session tools are enabled/i)
|
|
55
|
+
assert.match(guidance, /Do not imply that a normal read-only action is waiting on user permission or approval/i)
|
|
56
|
+
assert.match(guidance, /state that the capability is not enabled in this session/i)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('tells tool-enabled agents to use enabled tools autonomously before asking for permission', () => {
|
|
60
|
+
const guidance = buildEnabledToolsAutonomyGuidance().join('\n')
|
|
61
|
+
assert.match(guidance, /Enabled session tools are already available for normal use/i)
|
|
62
|
+
assert.match(guidance, /Do not ask the user for permission before using enabled tools/i)
|
|
63
|
+
assert.match(guidance, /attempt that tool path before asking the user to do the work manually/i)
|
|
64
|
+
assert.match(guidance, /Only surface approval or permission as a blocker when a real runtime tool result explicitly requires approval/i)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('tells tool-enabled agents when approvals are disabled platform-wide', () => {
|
|
68
|
+
const guidance = buildEnabledToolsAutonomyGuidance({ approvalsEnabled: false }).join('\n')
|
|
69
|
+
assert.match(guidance, /Approvals are disabled platform-wide in this runtime/i)
|
|
70
|
+
assert.match(guidance, /Do not tell the user that enabled tool use is waiting on approval/i)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('tells tool-enabled agents which approval categories auto-approve', () => {
|
|
74
|
+
const guidance = buildEnabledToolsAutonomyGuidance({
|
|
75
|
+
approvalsEnabled: true,
|
|
76
|
+
approvalAutoApproveCategories: ['tool_access', 'wallet_action'],
|
|
77
|
+
}).join('\n')
|
|
78
|
+
assert.match(guidance, /These approval categories auto-approve in this runtime: tool_access, wallet_action/i)
|
|
79
|
+
assert.match(guidance, /call the tool and let the runtime auto-approve it instead of asking the user first in prose/i)
|
|
80
|
+
})
|
|
40
81
|
})
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { spawnSync } from 'node:child_process'
|
|
6
|
+
import test from 'node:test'
|
|
7
|
+
|
|
8
|
+
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../..')
|
|
9
|
+
|
|
10
|
+
function runWithTempDataDir(script: string) {
|
|
11
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-chat-session-sync-'))
|
|
12
|
+
try {
|
|
13
|
+
const result = spawnSync(process.execPath, ['--import', 'tsx', '--input-type=module', '--eval', script], {
|
|
14
|
+
cwd: repoRoot,
|
|
15
|
+
env: {
|
|
16
|
+
...process.env,
|
|
17
|
+
DATA_DIR: path.join(tempDir, 'data'),
|
|
18
|
+
WORKSPACE_DIR: path.join(tempDir, 'workspace'),
|
|
19
|
+
BROWSER_PROFILES_DIR: path.join(tempDir, 'browser-profiles'),
|
|
20
|
+
},
|
|
21
|
+
encoding: 'utf-8',
|
|
22
|
+
})
|
|
23
|
+
assert.equal(result.status, 0, result.stderr || result.stdout || 'subprocess failed')
|
|
24
|
+
const lines = (result.stdout || '')
|
|
25
|
+
.trim()
|
|
26
|
+
.split('\n')
|
|
27
|
+
.map((line) => line.trim())
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
const jsonLine = [...lines].reverse().find((line) => line.startsWith('{'))
|
|
30
|
+
return JSON.parse(jsonLine || '{}')
|
|
31
|
+
} finally {
|
|
32
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test('executeSessionChatTurn syncs updated agent runtime fields onto its thread session', () => {
|
|
37
|
+
const output = runWithTempDataDir(`
|
|
38
|
+
const storageMod = await import('./src/lib/server/storage')
|
|
39
|
+
const storage = storageMod.default || storageMod['module.exports'] || storageMod
|
|
40
|
+
const providersMod = await import('./src/lib/providers/index')
|
|
41
|
+
const threadMod = await import('./src/lib/server/agent-thread-session')
|
|
42
|
+
const execMod = await import('./src/lib/server/chat-execution')
|
|
43
|
+
const ensureAgentThreadSession = threadMod.ensureAgentThreadSession
|
|
44
|
+
|| threadMod.default?.ensureAgentThreadSession
|
|
45
|
+
|| threadMod['module.exports']?.ensureAgentThreadSession
|
|
46
|
+
const executeSessionChatTurn = execMod.executeSessionChatTurn
|
|
47
|
+
|| execMod.default?.executeSessionChatTurn
|
|
48
|
+
|| execMod['module.exports']?.executeSessionChatTurn
|
|
49
|
+
const providers = providersMod.PROVIDERS
|
|
50
|
+
|| providersMod.default?.PROVIDERS
|
|
51
|
+
|| providersMod['module.exports']?.PROVIDERS
|
|
52
|
+
|
|
53
|
+
providers['test-provider'] = {
|
|
54
|
+
id: 'test-provider',
|
|
55
|
+
name: 'Test Provider',
|
|
56
|
+
models: ['unit'],
|
|
57
|
+
requiresApiKey: false,
|
|
58
|
+
requiresEndpoint: false,
|
|
59
|
+
handler: {
|
|
60
|
+
async streamChat() {
|
|
61
|
+
return 'synced'
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const now = Date.now()
|
|
67
|
+
storage.saveAgents({
|
|
68
|
+
molly: {
|
|
69
|
+
id: 'molly',
|
|
70
|
+
name: 'Molly',
|
|
71
|
+
description: 'Thread session sync test',
|
|
72
|
+
provider: 'openai',
|
|
73
|
+
model: 'old-model',
|
|
74
|
+
credentialId: null,
|
|
75
|
+
apiEndpoint: null,
|
|
76
|
+
fallbackCredentialIds: [],
|
|
77
|
+
disabled: false,
|
|
78
|
+
heartbeatEnabled: false,
|
|
79
|
+
heartbeatIntervalSec: null,
|
|
80
|
+
plugins: ['memory'],
|
|
81
|
+
createdAt: now,
|
|
82
|
+
updatedAt: now,
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const session = ensureAgentThreadSession('molly')
|
|
87
|
+
const agents = storage.loadAgents()
|
|
88
|
+
agents.molly.provider = 'test-provider'
|
|
89
|
+
agents.molly.model = 'unit'
|
|
90
|
+
agents.molly.plugins = []
|
|
91
|
+
agents.molly.heartbeatEnabled = true
|
|
92
|
+
agents.molly.heartbeatIntervalSec = 90
|
|
93
|
+
agents.molly.updatedAt = now + 1
|
|
94
|
+
storage.saveAgents(agents)
|
|
95
|
+
|
|
96
|
+
const result = await executeSessionChatTurn({
|
|
97
|
+
sessionId: session.id,
|
|
98
|
+
message: 'hello',
|
|
99
|
+
runId: 'run-session-sync',
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const persisted = storage.loadSession(session.id)
|
|
103
|
+
console.log(JSON.stringify({
|
|
104
|
+
text: result.text || null,
|
|
105
|
+
provider: persisted?.provider || null,
|
|
106
|
+
model: persisted?.model || null,
|
|
107
|
+
plugins: persisted?.plugins || [],
|
|
108
|
+
heartbeatEnabled: persisted?.heartbeatEnabled ?? null,
|
|
109
|
+
heartbeatIntervalSec: persisted?.heartbeatIntervalSec ?? null,
|
|
110
|
+
}))
|
|
111
|
+
`)
|
|
112
|
+
|
|
113
|
+
assert.equal(output.text, 'synced')
|
|
114
|
+
assert.equal(output.provider, 'test-provider')
|
|
115
|
+
assert.equal(output.model, 'unit')
|
|
116
|
+
assert.deepEqual(output.plugins, [])
|
|
117
|
+
assert.equal(output.heartbeatEnabled, true)
|
|
118
|
+
assert.equal(output.heartbeatIntervalSec, 90)
|
|
119
|
+
})
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { MessageToolEvent, SSEEvent } from '@/types'
|
|
2
|
+
|
|
3
|
+
export function extractEventJson(line: string): SSEEvent | null {
|
|
4
|
+
if (!line.startsWith('data: ')) return null
|
|
5
|
+
try {
|
|
6
|
+
return JSON.parse(line.slice(6).trim()) as SSEEvent
|
|
7
|
+
} catch {
|
|
8
|
+
return null
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isLikelyToolErrorOutput(output: string): boolean {
|
|
13
|
+
const trimmed = String(output || '').trim()
|
|
14
|
+
if (!trimmed) return false
|
|
15
|
+
if (/^(Error(?::|\s*\(exit\b[^)]*\):?)|error:)/i.test(trimmed)) return true
|
|
16
|
+
if (/\b(MCP error|ECONNREFUSED|ETIMEDOUT|ERR_CONNECTION_REFUSED|ENOENT|EACCES)\b/i.test(trimmed)) return true
|
|
17
|
+
if (/\binvalid_type\b/i.test(trimmed) && /\b(issue|issues|expected|required|received|zod)\b/i.test(trimmed)) return true
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(trimmed) as Record<string, unknown>
|
|
20
|
+
const status = typeof parsed.status === 'string' ? parsed.status.trim().toLowerCase() : ''
|
|
21
|
+
if (status === 'error' || status === 'failed') return true
|
|
22
|
+
if (typeof parsed.error === 'string' && parsed.error.trim()) return true
|
|
23
|
+
} catch {
|
|
24
|
+
// Ignore non-JSON tool output.
|
|
25
|
+
}
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function collectToolEvent(ev: SSEEvent, bag: MessageToolEvent[]) {
|
|
30
|
+
if (ev.t === 'tool_call') {
|
|
31
|
+
const previous = bag[bag.length - 1]
|
|
32
|
+
if (
|
|
33
|
+
previous
|
|
34
|
+
&& previous.name === (ev.toolName || 'unknown')
|
|
35
|
+
&& previous.input === (ev.toolInput || '')
|
|
36
|
+
&& previous.toolCallId === (ev.toolCallId || previous.toolCallId)
|
|
37
|
+
&& !previous.output
|
|
38
|
+
) {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
bag.push({
|
|
42
|
+
name: ev.toolName || 'unknown',
|
|
43
|
+
input: ev.toolInput || '',
|
|
44
|
+
toolCallId: ev.toolCallId,
|
|
45
|
+
})
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
if (ev.t === 'tool_result') {
|
|
49
|
+
const idx = ev.toolCallId
|
|
50
|
+
? bag.findLastIndex((event) => event.toolCallId === ev.toolCallId && !event.output)
|
|
51
|
+
: bag.findLastIndex((event) => event.name === (ev.toolName || 'unknown') && !event.output)
|
|
52
|
+
if (idx === -1) return
|
|
53
|
+
const output = ev.toolOutput || ''
|
|
54
|
+
bag[idx] = {
|
|
55
|
+
...bag[idx],
|
|
56
|
+
output,
|
|
57
|
+
error: isLikelyToolErrorOutput(output) || undefined,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function dedupeConsecutiveToolEvents(events: MessageToolEvent[]): MessageToolEvent[] {
|
|
63
|
+
const sameEvent = (left: MessageToolEvent, right: MessageToolEvent): boolean => (
|
|
64
|
+
left.name === right.name
|
|
65
|
+
&& left.input === right.input
|
|
66
|
+
&& (left.output || '') === (right.output || '')
|
|
67
|
+
&& (left.error === true) === (right.error === true)
|
|
68
|
+
)
|
|
69
|
+
const sameBlock = (startA: number, startB: number, size: number): boolean => {
|
|
70
|
+
for (let offset = 0; offset < size; offset += 1) {
|
|
71
|
+
if (!sameEvent(events[startA + offset], events[startB + offset])) return false
|
|
72
|
+
}
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const deduped: MessageToolEvent[] = []
|
|
77
|
+
for (let index = 0; index < events.length;) {
|
|
78
|
+
const remaining = events.length - index
|
|
79
|
+
let collapsed = false
|
|
80
|
+
for (let blockSize = Math.floor(remaining / 2); blockSize >= 1; blockSize -= 1) {
|
|
81
|
+
if (!sameBlock(index, index + blockSize, blockSize)) continue
|
|
82
|
+
for (let offset = 0; offset < blockSize; offset += 1) deduped.push(events[index + offset])
|
|
83
|
+
const blockStart = index
|
|
84
|
+
index += blockSize
|
|
85
|
+
while (index + blockSize <= events.length && sameBlock(blockStart, index, blockSize)) {
|
|
86
|
+
index += blockSize
|
|
87
|
+
}
|
|
88
|
+
collapsed = true
|
|
89
|
+
break
|
|
90
|
+
}
|
|
91
|
+
if (collapsed) continue
|
|
92
|
+
deduped.push(events[index])
|
|
93
|
+
index += 1
|
|
94
|
+
}
|
|
95
|
+
return deduped
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function deriveTerminalRunError(params: {
|
|
99
|
+
errorMessage?: string
|
|
100
|
+
fullResponse: string
|
|
101
|
+
streamErrors: string[]
|
|
102
|
+
toolEvents: MessageToolEvent[]
|
|
103
|
+
internal: boolean
|
|
104
|
+
}): string | undefined {
|
|
105
|
+
if (params.errorMessage) return params.errorMessage
|
|
106
|
+
|
|
107
|
+
if (params.streamErrors.length > 0 && !params.fullResponse.trim()) {
|
|
108
|
+
return params.streamErrors[params.streamErrors.length - 1]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!params.internal && !params.fullResponse.trim() && params.toolEvents.length === 0) {
|
|
112
|
+
return 'Run completed without any response text, tool calls, or explicit error details. Check the provider configuration and try again.'
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return undefined
|
|
116
|
+
}
|