@swarmclawai/swarmclaw 0.7.7 → 0.8.0
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 +12 -14
- package/next.config.ts +13 -2
- package/package.json +4 -2
- package/src/app/api/agents/[id]/thread/route.ts +9 -0
- package/src/app/api/agents/route.ts +4 -0
- package/src/app/api/agents/thread-route.test.ts +133 -0
- package/src/app/api/approvals/route.test.ts +148 -0
- package/src/app/api/canvas/[sessionId]/route.ts +3 -1
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -2
- package/src/app/api/chats/[id]/devserver/route.ts +48 -7
- package/src/app/api/chats/[id]/messages/route.ts +42 -18
- package/src/app/api/chats/[id]/route.ts +1 -1
- package/src/app/api/chats/[id]/stop/route.ts +5 -4
- package/src/app/api/chats/route.ts +23 -2
- package/src/app/api/clawhub/install/route.ts +28 -8
- package/src/app/api/connectors/[id]/route.ts +46 -3
- package/src/app/api/connectors/route.ts +12 -8
- package/src/app/api/external-agents/route.test.ts +165 -0
- package/src/app/api/gateways/[id]/health/route.ts +27 -12
- package/src/app/api/gateways/[id]/route.ts +2 -0
- package/src/app/api/gateways/health-route.test.ts +135 -0
- package/src/app/api/gateways/route.ts +2 -0
- package/src/app/api/mcp-servers/route.test.ts +130 -0
- package/src/app/api/openclaw/deploy/route.ts +38 -5
- package/src/app/api/plugins/install/route.ts +46 -6
- package/src/app/api/plugins/marketplace/route.ts +48 -15
- package/src/app/api/preview-server/route.ts +26 -11
- package/src/app/api/projects/[id]/route.ts +6 -2
- package/src/app/api/projects/route.ts +4 -3
- package/src/app/api/schedules/[id]/run/route.ts +4 -0
- package/src/app/api/schedules/route.test.ts +86 -0
- package/src/app/api/schedules/route.ts +6 -1
- package/src/app/api/secrets/[id]/route.ts +1 -0
- package/src/app/api/secrets/route.ts +2 -1
- package/src/app/api/settings/route.ts +2 -0
- package/src/app/api/setup/check-provider/route.test.ts +19 -0
- package/src/app/api/setup/check-provider/route.ts +40 -10
- package/src/app/api/skills/[id]/route.ts +12 -0
- package/src/app/api/skills/import/route.ts +14 -12
- package/src/app/api/skills/route.ts +13 -1
- package/src/app/api/tasks/[id]/route.ts +10 -1
- package/src/app/api/tasks/import/github/route.test.ts +65 -0
- package/src/app/api/tasks/import/github/route.ts +337 -0
- package/src/app/api/wallets/[id]/approve/route.ts +17 -3
- package/src/app/api/wallets/[id]/route.ts +79 -33
- package/src/app/api/wallets/[id]/send/route.ts +19 -33
- package/src/app/api/wallets/route.ts +78 -61
- package/src/app/api/webhooks/[id]/route.ts +33 -6
- package/src/app/api/webhooks/route.test.ts +272 -0
- package/src/cli/index.js +1 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-card.tsx +9 -2
- package/src/components/agents/agent-chat-list.tsx +18 -2
- package/src/components/agents/agent-list.tsx +1 -0
- package/src/components/agents/agent-sheet.tsx +257 -38
- package/src/components/agents/inspector-panel.tsx +41 -0
- package/src/components/canvas/canvas-panel.tsx +236 -65
- package/src/components/chat/chat-area.tsx +36 -19
- package/src/components/chat/chat-card.tsx +36 -13
- package/src/components/chat/chat-header.tsx +48 -16
- package/src/components/chat/chat-list.tsx +28 -4
- package/src/components/chat/checkpoint-timeline.tsx +50 -34
- package/src/components/chat/delegation-banner.test.ts +14 -1
- package/src/components/chat/delegation-banner.tsx +1 -1
- package/src/components/chat/message-bubble.tsx +208 -145
- package/src/components/chat/message-list.tsx +48 -19
- package/src/components/chatrooms/chatroom-message.tsx +2 -2
- package/src/components/chatrooms/chatroom-sheet.tsx +16 -2
- package/src/components/connectors/connector-health.tsx +1 -1
- package/src/components/connectors/connector-list.tsx +7 -2
- package/src/components/connectors/connector-sheet.tsx +337 -148
- package/src/components/gateways/gateway-sheet.tsx +2 -2
- package/src/components/layout/app-layout.tsx +40 -23
- package/src/components/mcp-servers/mcp-server-list.tsx +26 -5
- package/src/components/mcp-servers/mcp-server-sheet.tsx +19 -2
- package/src/components/openclaw/openclaw-deploy-panel.tsx +269 -21
- package/src/components/plugins/plugin-list.tsx +45 -9
- package/src/components/plugins/plugin-sheet.tsx +55 -7
- package/src/components/projects/project-detail.tsx +217 -0
- package/src/components/projects/project-sheet.tsx +176 -4
- package/src/components/providers/provider-list.tsx +2 -1
- package/src/components/providers/provider-sheet.tsx +21 -2
- package/src/components/schedules/schedule-card.tsx +25 -1
- package/src/components/schedules/schedule-sheet.tsx +44 -2
- package/src/components/secrets/secret-sheet.tsx +21 -2
- package/src/components/shared/agent-switch-dialog.tsx +12 -1
- package/src/components/shared/bottom-sheet.tsx +13 -3
- package/src/components/shared/command-palette.tsx +8 -1
- package/src/components/shared/confirm-dialog.tsx +19 -4
- package/src/components/shared/connector-platform-icon.test.ts +28 -0
- package/src/components/shared/connector-platform-icon.tsx +39 -6
- package/src/components/shared/settings/plugin-manager.tsx +29 -6
- package/src/components/shared/settings/section-capability-policy.tsx +45 -3
- package/src/components/shared/settings/section-voice.tsx +11 -3
- package/src/components/skills/skill-list.tsx +25 -0
- package/src/components/skills/skill-sheet.tsx +84 -12
- package/src/components/tasks/approvals-panel.tsx +289 -34
- package/src/components/tasks/task-board.tsx +410 -25
- package/src/components/tasks/task-card.tsx +66 -8
- package/src/components/tasks/task-sheet.tsx +16 -4
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/wallets/wallet-approval-dialog.tsx +4 -2
- package/src/components/wallets/wallet-panel.tsx +435 -90
- package/src/components/wallets/wallet-section.tsx +198 -48
- package/src/components/webhooks/webhook-sheet.tsx +22 -2
- package/src/lib/approval-display.ts +20 -0
- package/src/lib/canvas-content.ts +198 -0
- package/src/lib/chat-artifact-summary.ts +165 -0
- package/src/lib/chat-display.test.ts +91 -0
- package/src/lib/chat-display.ts +58 -0
- package/src/lib/chat-streaming-state.test.ts +47 -1
- package/src/lib/chat-streaming-state.ts +42 -0
- package/src/lib/ollama-model.ts +10 -0
- package/src/lib/openclaw-endpoint.test.ts +8 -0
- package/src/lib/openclaw-endpoint.ts +6 -1
- package/src/lib/plugin-install-cors.ts +46 -0
- package/src/lib/plugin-sources.test.ts +43 -0
- package/src/lib/plugin-sources.ts +77 -0
- package/src/lib/providers/ollama.ts +16 -6
- package/src/lib/providers/openclaw.test.ts +54 -0
- package/src/lib/providers/openclaw.ts +127 -11
- package/src/lib/schedule-dedupe-advanced.test.ts +1335 -0
- package/src/lib/schedule-dedupe.test.ts +66 -1
- package/src/lib/schedule-dedupe.ts +169 -12
- package/src/lib/schedule-origin.test.ts +20 -0
- package/src/lib/schedule-origin.ts +15 -0
- package/src/lib/server/__fixtures__/fake-mcp-stdio-server.mjs +27 -0
- package/src/lib/server/agent-availability.ts +16 -0
- package/src/lib/server/agent-runtime-config.ts +12 -4
- package/src/lib/server/agent-thread-session.test.ts +51 -0
- package/src/lib/server/agent-thread-session.ts +7 -0
- package/src/lib/server/approval-match.ts +205 -0
- package/src/lib/server/approvals-auto-approve.test.ts +538 -1
- package/src/lib/server/approvals.ts +214 -1
- package/src/lib/server/assistant-control.test.ts +29 -0
- package/src/lib/server/assistant-control.ts +23 -0
- package/src/lib/server/build-llm.test.ts +79 -0
- package/src/lib/server/build-llm.ts +14 -4
- package/src/lib/server/canvas-content.test.ts +32 -0
- package/src/lib/server/canvas-content.ts +6 -0
- package/src/lib/server/capability-router.test.ts +33 -0
- package/src/lib/server/capability-router.ts +80 -19
- package/src/lib/server/chat-execution-advanced.test.ts +651 -0
- package/src/lib/server/chat-execution-disabled.test.ts +94 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +157 -0
- package/src/lib/server/chat-execution.ts +378 -73
- package/src/lib/server/clawhub-client.test.ts +14 -8
- package/src/lib/server/connectors/manager-reconnect.test.ts +47 -0
- package/src/lib/server/connectors/manager.test.ts +1147 -0
- package/src/lib/server/connectors/manager.ts +461 -137
- package/src/lib/server/connectors/pairing.ts +26 -5
- package/src/lib/server/connectors/types.ts +2 -0
- package/src/lib/server/connectors/whatsapp.test.ts +134 -0
- package/src/lib/server/connectors/whatsapp.ts +271 -47
- package/src/lib/server/context-manager.ts +6 -1
- package/src/lib/server/daemon-state.ts +84 -47
- package/src/lib/server/data-dir.test.ts +37 -0
- package/src/lib/server/data-dir.ts +20 -1
- package/src/lib/server/delegation-jobs-advanced.test.ts +513 -0
- package/src/lib/server/devserver-launch.test.ts +60 -0
- package/src/lib/server/devserver-launch.ts +85 -0
- package/src/lib/server/elevenlabs.test.ts +247 -1
- package/src/lib/server/elevenlabs.ts +147 -43
- package/src/lib/server/ethereum.ts +590 -0
- package/src/lib/server/eval/agent-regression-advanced.test.ts +302 -0
- package/src/lib/server/eval/agent-regression.test.ts +18 -1
- package/src/lib/server/eval/agent-regression.ts +383 -11
- package/src/lib/server/evm-swap.ts +475 -0
- package/src/lib/server/execution-log.ts +1 -0
- package/src/lib/server/heartbeat-service-timer.test.ts +173 -0
- package/src/lib/server/heartbeat-service.ts +20 -11
- package/src/lib/server/heartbeat-wake.test.ts +112 -0
- package/src/lib/server/heartbeat-wake.ts +338 -57
- package/src/lib/server/main-agent-loop-advanced.test.ts +538 -0
- package/src/lib/server/main-agent-loop.test.ts +260 -0
- package/src/lib/server/main-agent-loop.ts +559 -14
- package/src/lib/server/mcp-client.test.ts +16 -0
- package/src/lib/server/mcp-client.ts +25 -0
- package/src/lib/server/memory-integration.test.ts +719 -0
- package/src/lib/server/memory-policy.test.ts +43 -0
- package/src/lib/server/memory-policy.ts +132 -0
- package/src/lib/server/memory-tiers.test.ts +60 -0
- package/src/lib/server/memory-tiers.ts +16 -0
- package/src/lib/server/ollama-runtime.ts +58 -0
- package/src/lib/server/openclaw-deploy.test.ts +109 -1
- package/src/lib/server/openclaw-deploy.ts +557 -81
- package/src/lib/server/openclaw-gateway.test.ts +131 -0
- package/src/lib/server/openclaw-gateway.ts +10 -4
- package/src/lib/server/openclaw-health.test.ts +35 -0
- package/src/lib/server/openclaw-health.ts +215 -47
- package/src/lib/server/orchestrator-lg.ts +3 -2
- package/src/lib/server/orchestrator.ts +2 -0
- package/src/lib/server/plugins-advanced.test.ts +351 -0
- package/src/lib/server/plugins.ts +211 -6
- package/src/lib/server/project-context.ts +162 -0
- package/src/lib/server/project-utils.ts +150 -0
- package/src/lib/server/queue-advanced.test.ts +528 -0
- package/src/lib/server/queue-followups.test.ts +409 -2
- package/src/lib/server/queue-reconcile.test.ts +128 -0
- package/src/lib/server/queue.ts +527 -68
- package/src/lib/server/scheduler.ts +29 -1
- package/src/lib/server/session-note.test.ts +36 -0
- package/src/lib/server/session-note.ts +42 -0
- package/src/lib/server/session-run-manager.ts +83 -4
- package/src/lib/server/session-tools/canvas.ts +14 -12
- package/src/lib/server/session-tools/connector-inputs.test.ts +37 -0
- package/src/lib/server/session-tools/connector.test.ts +138 -0
- package/src/lib/server/session-tools/connector.ts +366 -54
- package/src/lib/server/session-tools/context.ts +17 -3
- package/src/lib/server/session-tools/crud.ts +484 -84
- package/src/lib/server/session-tools/delegate-fallback.test.ts +103 -0
- package/src/lib/server/session-tools/delegate-resume.test.ts +50 -0
- package/src/lib/server/session-tools/delegate.ts +102 -10
- package/src/lib/server/session-tools/discovery-approvals.test.ts +142 -0
- package/src/lib/server/session-tools/discovery.ts +80 -12
- package/src/lib/server/session-tools/file-normalize.test.ts +36 -0
- package/src/lib/server/session-tools/file.ts +43 -4
- package/src/lib/server/session-tools/human-loop.ts +35 -5
- package/src/lib/server/session-tools/index.ts +44 -9
- package/src/lib/server/session-tools/manage-connectors.test.ts +139 -0
- package/src/lib/server/session-tools/manage-schedules-advanced.test.ts +564 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +283 -0
- package/src/lib/server/session-tools/manage-tasks-advanced.test.ts +852 -0
- package/src/lib/server/session-tools/manage-tasks.test.ts +114 -0
- package/src/lib/server/session-tools/memory.test.ts +93 -0
- package/src/lib/server/session-tools/memory.ts +554 -75
- package/src/lib/server/session-tools/normalize-tool-args.ts +1 -1
- package/src/lib/server/session-tools/platform-access.test.ts +58 -0
- package/src/lib/server/session-tools/platform.ts +60 -19
- package/src/lib/server/session-tools/plugin-creator.ts +57 -1
- package/src/lib/server/session-tools/primitive-tools.test.ts +6 -0
- package/src/lib/server/session-tools/schedule.ts +6 -1
- package/src/lib/server/session-tools/shell-normalize.test.ts +25 -1
- package/src/lib/server/session-tools/shell.ts +22 -3
- package/src/lib/server/session-tools/wallet-tool.test.ts +254 -0
- package/src/lib/server/session-tools/wallet.ts +1374 -139
- package/src/lib/server/session-tools/web-inputs.test.ts +178 -0
- package/src/lib/server/session-tools/web.ts +621 -70
- package/src/lib/server/skill-discovery.ts +128 -0
- package/src/lib/server/skill-eligibility.test.ts +84 -0
- package/src/lib/server/skill-eligibility.ts +95 -0
- package/src/lib/server/skill-prompt-budget.test.ts +102 -0
- package/src/lib/server/skill-prompt-budget.ts +125 -0
- package/src/lib/server/skills-normalize.test.ts +54 -0
- package/src/lib/server/skills-normalize.ts +372 -26
- package/src/lib/server/solana.ts +214 -29
- package/src/lib/server/storage.ts +65 -36
- package/src/lib/server/stream-agent-chat.test.ts +437 -2
- package/src/lib/server/stream-agent-chat.ts +957 -79
- package/src/lib/server/system-events.ts +1 -1
- package/src/lib/server/tool-aliases.ts +2 -0
- package/src/lib/server/tool-capability-policy-advanced.test.ts +502 -0
- package/src/lib/server/tool-capability-policy.test.ts +24 -0
- package/src/lib/server/tool-capability-policy.ts +29 -1
- package/src/lib/server/tool-loop-detection.test.ts +105 -0
- package/src/lib/server/tool-loop-detection.ts +260 -0
- package/src/lib/server/tool-planning.test.ts +44 -0
- package/src/lib/server/tool-planning.ts +271 -0
- package/src/lib/server/wallet-execution.test.ts +198 -0
- package/src/lib/server/wallet-portfolio.test.ts +98 -0
- package/src/lib/server/wallet-portfolio.ts +724 -0
- package/src/lib/server/wallet-service.test.ts +57 -0
- package/src/lib/server/wallet-service.ts +213 -0
- package/src/lib/server/watch-jobs-advanced.test.ts +594 -0
- package/src/lib/server/watch-jobs.ts +17 -2
- package/src/lib/server/workspace-context.ts +111 -0
- package/src/lib/skill-save-payload.test.ts +39 -0
- package/src/lib/skill-save-payload.ts +37 -0
- package/src/lib/tasks.ts +28 -0
- package/src/lib/tool-definitions.ts +2 -1
- package/src/lib/tool-event-summary.test.ts +30 -0
- package/src/lib/tool-event-summary.ts +37 -0
- package/src/lib/validation/schemas.ts +1 -0
- package/src/lib/wallet-transactions.test.ts +75 -0
- package/src/lib/wallet-transactions.ts +43 -0
- package/src/lib/wallet.test.ts +17 -0
- package/src/lib/wallet.ts +183 -0
- package/src/proxy.test.ts +31 -0
- package/src/proxy.ts +34 -2
- package/src/stores/use-chat-store.ts +15 -1
- package/src/types/index.ts +249 -14
|
@@ -11,6 +11,12 @@ import {
|
|
|
11
11
|
startConnector,
|
|
12
12
|
getConnectorStatus,
|
|
13
13
|
checkConnectorHealth,
|
|
14
|
+
createConnectorReconnectState,
|
|
15
|
+
advanceConnectorReconnectState,
|
|
16
|
+
clearReconnectState,
|
|
17
|
+
getAllReconnectStates,
|
|
18
|
+
getReconnectState,
|
|
19
|
+
setReconnectState,
|
|
14
20
|
} from './connectors/manager'
|
|
15
21
|
import { startHeartbeatService, stopHeartbeatService, getHeartbeatServiceStatus } from './heartbeat-service'
|
|
16
22
|
import { hasOpenClawAgents, ensureGatewayConnected, disconnectGateway, getGateway } from './openclaw-gateway'
|
|
@@ -34,6 +40,7 @@ const QUEUE_CHECK_INTERVAL = 30_000 // 30 seconds
|
|
|
34
40
|
const BROWSER_SWEEP_INTERVAL = 60_000 // 60 seconds
|
|
35
41
|
const BROWSER_MAX_AGE = 10 * 60 * 1000 // 10 minutes idle = orphaned
|
|
36
42
|
const HEALTH_CHECK_INTERVAL = 120_000 // 2 minutes
|
|
43
|
+
const CONNECTOR_HEALTH_CHECK_INTERVAL = 5_000 // 5 seconds
|
|
37
44
|
const MEMORY_CONSOLIDATION_INTERVAL = 6 * 3600_000 // 6 hours
|
|
38
45
|
const MEMORY_CONSOLIDATION_INITIAL_DELAY = 60_000 // 1 minute after daemon start
|
|
39
46
|
const STALE_MULTIPLIER = 4 // session is stale after N × heartbeat interval
|
|
@@ -87,12 +94,12 @@ const ds: {
|
|
|
87
94
|
queueIntervalId: ReturnType<typeof setInterval> | null
|
|
88
95
|
browserSweepId: ReturnType<typeof setInterval> | null
|
|
89
96
|
healthIntervalId: ReturnType<typeof setInterval> | null
|
|
97
|
+
connectorHealthIntervalId: ReturnType<typeof setInterval> | null
|
|
90
98
|
memoryConsolidationTimeoutId: ReturnType<typeof setTimeout> | null
|
|
91
99
|
memoryConsolidationIntervalId: ReturnType<typeof setInterval> | null
|
|
92
100
|
evalSchedulerIntervalId: ReturnType<typeof setInterval> | null
|
|
93
101
|
/** Session IDs we've already alerted as stale (alert-once semantics). */
|
|
94
102
|
staleSessionIds: Set<string>
|
|
95
|
-
connectorRestartState: Map<string, { lastAttemptAt: number; failCount: number; wakeAttempts: number }>
|
|
96
103
|
/** OpenClaw gateway agent IDs currently considered down. */
|
|
97
104
|
openclawDownAgentIds: Set<string>
|
|
98
105
|
/** Per-agent auto-repair state for OpenClaw gateways. */
|
|
@@ -107,11 +114,11 @@ const ds: {
|
|
|
107
114
|
queueIntervalId: null,
|
|
108
115
|
browserSweepId: null,
|
|
109
116
|
healthIntervalId: null,
|
|
117
|
+
connectorHealthIntervalId: null,
|
|
110
118
|
memoryConsolidationTimeoutId: null,
|
|
111
119
|
memoryConsolidationIntervalId: null,
|
|
112
120
|
evalSchedulerIntervalId: null,
|
|
113
121
|
staleSessionIds: new Set<string>(),
|
|
114
|
-
connectorRestartState: new Map<string, { lastAttemptAt: number; failCount: number; wakeAttempts: number }>(),
|
|
115
122
|
openclawDownAgentIds: new Set<string>(),
|
|
116
123
|
openclawRepairState: new Map<string, { attempts: number; lastAttemptAt: number; cooldownUntil: number }>(),
|
|
117
124
|
lastIntegrityCheckAt: null,
|
|
@@ -123,7 +130,6 @@ const ds: {
|
|
|
123
130
|
|
|
124
131
|
// Backfill fields for hot-reloaded daemon state objects from older code versions.
|
|
125
132
|
if (!ds.staleSessionIds) ds.staleSessionIds = new Set<string>()
|
|
126
|
-
if (!ds.connectorRestartState) ds.connectorRestartState = new Map<string, { lastAttemptAt: number; failCount: number; wakeAttempts: number }>()
|
|
127
133
|
if (!ds.openclawDownAgentIds) ds.openclawDownAgentIds = new Set<string>()
|
|
128
134
|
if (!ds.openclawRepairState) ds.openclawRepairState = new Map<string, { attempts: number; lastAttemptAt: number; cooldownUntil: number }>()
|
|
129
135
|
if (ds.lastIntegrityCheckAt === undefined) ds.lastIntegrityCheckAt = null
|
|
@@ -132,6 +138,7 @@ if (ds.lastIntegrityDriftCount === undefined) ds.lastIntegrityDriftCount = 0
|
|
|
132
138
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
133
139
|
if ((ds as any).issueLastAlertAt) delete (ds as any).issueLastAlertAt
|
|
134
140
|
if (ds.healthIntervalId === undefined) ds.healthIntervalId = null
|
|
141
|
+
if (ds.connectorHealthIntervalId === undefined) ds.connectorHealthIntervalId = null
|
|
135
142
|
if (ds.manualStopRequested === undefined) ds.manualStopRequested = false
|
|
136
143
|
if (ds.memoryConsolidationTimeoutId === undefined) ds.memoryConsolidationTimeoutId = null
|
|
137
144
|
if (ds.memoryConsolidationIntervalId === undefined) ds.memoryConsolidationIntervalId = null
|
|
@@ -156,6 +163,7 @@ export function startDaemon(options?: { source?: string; manualStart?: boolean }
|
|
|
156
163
|
startQueueProcessor()
|
|
157
164
|
startBrowserSweep()
|
|
158
165
|
startHealthMonitor()
|
|
166
|
+
startConnectorHealthMonitor()
|
|
159
167
|
startHeartbeatService()
|
|
160
168
|
startMemoryConsolidation()
|
|
161
169
|
startEvalScheduler()
|
|
@@ -173,6 +181,7 @@ export function startDaemon(options?: { source?: string; manualStart?: boolean }
|
|
|
173
181
|
startQueueProcessor()
|
|
174
182
|
startBrowserSweep()
|
|
175
183
|
startHealthMonitor()
|
|
184
|
+
startConnectorHealthMonitor()
|
|
176
185
|
startHeartbeatService()
|
|
177
186
|
startMemoryConsolidation()
|
|
178
187
|
startEvalScheduler()
|
|
@@ -201,6 +210,7 @@ export function stopDaemon(options?: { source?: string; manualStop?: boolean })
|
|
|
201
210
|
stopQueueProcessor()
|
|
202
211
|
stopBrowserSweep()
|
|
203
212
|
stopHealthMonitor()
|
|
213
|
+
stopConnectorHealthMonitor()
|
|
204
214
|
stopHeartbeatService()
|
|
205
215
|
stopMemoryConsolidation()
|
|
206
216
|
stopEvalScheduler()
|
|
@@ -278,7 +288,8 @@ async function sendHealthAlert(text: string) {
|
|
|
278
288
|
}
|
|
279
289
|
|
|
280
290
|
async function runConnectorHealthChecks(now: number) {
|
|
281
|
-
// First,
|
|
291
|
+
// First, collapse dead runtime instances into persisted error state so the
|
|
292
|
+
// daemon can own the restart cadence and backoff policy.
|
|
282
293
|
try {
|
|
283
294
|
await checkConnectorHealth()
|
|
284
295
|
} catch (err: unknown) {
|
|
@@ -289,48 +300,30 @@ async function runConnectorHealthChecks(now: number) {
|
|
|
289
300
|
for (const connector of Object.values(connectors) as Record<string, unknown>[]) {
|
|
290
301
|
if (!connector?.id || typeof connector.id !== 'string') continue
|
|
291
302
|
if (connector.isEnabled !== true) {
|
|
292
|
-
|
|
303
|
+
clearReconnectState(connector.id)
|
|
293
304
|
continue
|
|
294
305
|
}
|
|
295
306
|
|
|
296
307
|
const runtimeStatus = getConnectorStatus(connector.id)
|
|
297
308
|
if (runtimeStatus === 'running') {
|
|
298
|
-
|
|
309
|
+
clearReconnectState(connector.id)
|
|
299
310
|
continue
|
|
300
311
|
}
|
|
301
312
|
|
|
302
|
-
const current =
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
connector.status = 'error'
|
|
310
|
-
connector.lastError = `Auto-restart gave up after ${MAX_WAKE_ATTEMPTS} consecutive failures`
|
|
311
|
-
connector.updatedAt = Date.now()
|
|
312
|
-
connectors[connector.id] = connector
|
|
313
|
-
saveConnectors(connectors)
|
|
314
|
-
ds.connectorRestartState.delete(connector.id)
|
|
315
|
-
createNotification({
|
|
316
|
-
type: 'error',
|
|
317
|
-
title: `Connector "${connector.name}" failed`,
|
|
318
|
-
message: `Auto-restart gave up after ${MAX_WAKE_ATTEMPTS} consecutive failures.`,
|
|
319
|
-
dedupKey: `connector-gave-up:${connector.id}`,
|
|
320
|
-
entityType: 'connector',
|
|
321
|
-
entityId: connector.id,
|
|
322
|
-
})
|
|
313
|
+
const current = getReconnectState(connector.id)
|
|
314
|
+
?? createConnectorReconnectState(
|
|
315
|
+
{ error: typeof connector.lastError === 'string' ? connector.lastError : '' },
|
|
316
|
+
{ initialBackoffMs: CONNECTOR_RESTART_BASE_MS },
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
if (current.exhausted) {
|
|
323
320
|
continue
|
|
324
321
|
}
|
|
325
322
|
|
|
326
|
-
|
|
327
|
-
CONNECTOR_RESTART_MAX_MS,
|
|
328
|
-
CONNECTOR_RESTART_BASE_MS * (2 ** Math.min(6, current.failCount)),
|
|
329
|
-
)
|
|
330
|
-
if ((now - current.lastAttemptAt) < backoffMs) continue
|
|
323
|
+
if (current.nextRetryAt > now) continue
|
|
331
324
|
|
|
332
325
|
// Notify on first detection of a down connector
|
|
333
|
-
if (current.
|
|
326
|
+
if (current.attempts === 0) {
|
|
334
327
|
createNotification({
|
|
335
328
|
type: 'warning',
|
|
336
329
|
title: `Connector "${connector.name}" is down`,
|
|
@@ -341,24 +334,43 @@ async function runConnectorHealthChecks(now: number) {
|
|
|
341
334
|
})
|
|
342
335
|
}
|
|
343
336
|
|
|
344
|
-
current.lastAttemptAt = now
|
|
345
|
-
ds.connectorRestartState.set(connector.id, current)
|
|
346
337
|
try {
|
|
347
338
|
await startConnector(connector.id)
|
|
348
|
-
|
|
339
|
+
clearReconnectState(connector.id)
|
|
349
340
|
await sendHealthAlert(`Connector "${connector.name}" (${connector.platform}) was down and has been auto-restarted.`)
|
|
350
341
|
} catch (err: unknown) {
|
|
351
|
-
current.failCount += 1
|
|
352
|
-
current.wakeAttempts += 1
|
|
353
|
-
ds.connectorRestartState.set(connector.id, current)
|
|
354
342
|
const message = err instanceof Error ? err.message : String(err)
|
|
355
|
-
|
|
343
|
+
const next = advanceConnectorReconnectState(current, message, now, {
|
|
344
|
+
initialBackoffMs: CONNECTOR_RESTART_BASE_MS,
|
|
345
|
+
maxBackoffMs: CONNECTOR_RESTART_MAX_MS,
|
|
346
|
+
maxAttempts: MAX_WAKE_ATTEMPTS,
|
|
347
|
+
})
|
|
348
|
+
setReconnectState(connector.id, next)
|
|
349
|
+
if (next.exhausted) {
|
|
350
|
+
console.warn(`[health] Connector "${connector.name}" exceeded ${MAX_WAKE_ATTEMPTS} auto-restart attempts — giving up until the server restarts or the user retries manually`)
|
|
351
|
+
connector.status = 'error'
|
|
352
|
+
connector.lastError = `Auto-restart gave up after ${MAX_WAKE_ATTEMPTS} attempts: ${message}`
|
|
353
|
+
connector.updatedAt = Date.now()
|
|
354
|
+
connectors[connector.id] = connector
|
|
355
|
+
saveConnectors(connectors)
|
|
356
|
+
notify('connectors')
|
|
357
|
+
createNotification({
|
|
358
|
+
type: 'error',
|
|
359
|
+
title: `Connector "${connector.name}" failed`,
|
|
360
|
+
message: `Auto-restart gave up after ${MAX_WAKE_ATTEMPTS} attempts.`,
|
|
361
|
+
dedupKey: `connector-gave-up:${connector.id}`,
|
|
362
|
+
entityType: 'connector',
|
|
363
|
+
entityId: connector.id,
|
|
364
|
+
})
|
|
365
|
+
} else {
|
|
366
|
+
console.warn(`[health] Connector auto-restart failed for ${connector.name} (attempt ${next.attempts}/${MAX_WAKE_ATTEMPTS}): ${message}`)
|
|
367
|
+
}
|
|
356
368
|
}
|
|
357
369
|
}
|
|
358
370
|
|
|
359
371
|
// Purge restart state for connectors that no longer exist in storage
|
|
360
|
-
for (const id of
|
|
361
|
-
if (!connectors[id])
|
|
372
|
+
for (const id of Object.keys(getAllReconnectStates())) {
|
|
373
|
+
if (!connectors[id] || connectors[id]?.isEnabled !== true) clearReconnectState(id)
|
|
362
374
|
}
|
|
363
375
|
}
|
|
364
376
|
|
|
@@ -429,7 +441,7 @@ async function processWebhookRetries() {
|
|
|
429
441
|
agentId: agent.id,
|
|
430
442
|
parentSessionId: null,
|
|
431
443
|
plugins: agent.plugins || agent.tools || [],
|
|
432
|
-
heartbeatEnabled: (agent.heartbeatEnabled as boolean | undefined) ??
|
|
444
|
+
heartbeatEnabled: (agent.heartbeatEnabled as boolean | undefined) ?? false,
|
|
433
445
|
heartbeatIntervalSec: (agent.heartbeatIntervalSec as number | null | undefined) ?? null,
|
|
434
446
|
}
|
|
435
447
|
sessions[session.id as string] = session
|
|
@@ -776,8 +788,6 @@ async function runHealthChecks() {
|
|
|
776
788
|
|
|
777
789
|
if (sessionsDirty) saveSessions(sessions)
|
|
778
790
|
|
|
779
|
-
await runConnectorHealthChecks(now)
|
|
780
|
-
|
|
781
791
|
// Provider reachability checks
|
|
782
792
|
try {
|
|
783
793
|
await runProviderHealthChecks()
|
|
@@ -851,6 +861,26 @@ function stopHealthMonitor() {
|
|
|
851
861
|
}
|
|
852
862
|
}
|
|
853
863
|
|
|
864
|
+
function startConnectorHealthMonitor() {
|
|
865
|
+
if (ds.connectorHealthIntervalId) return
|
|
866
|
+
|
|
867
|
+
const tick = () => {
|
|
868
|
+
runConnectorHealthChecks(Date.now()).catch((err) => {
|
|
869
|
+
console.error('[daemon] Connector health tick failed:', err instanceof Error ? err.message : String(err))
|
|
870
|
+
})
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
tick()
|
|
874
|
+
ds.connectorHealthIntervalId = setInterval(tick, CONNECTOR_HEALTH_CHECK_INTERVAL)
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function stopConnectorHealthMonitor() {
|
|
878
|
+
if (ds.connectorHealthIntervalId) {
|
|
879
|
+
clearInterval(ds.connectorHealthIntervalId)
|
|
880
|
+
ds.connectorHealthIntervalId = null
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
854
884
|
function runConsolidationTick() {
|
|
855
885
|
import('./memory-consolidation').then(({ runDailyConsolidation }) =>
|
|
856
886
|
runDailyConsolidation().then((stats) => {
|
|
@@ -951,12 +981,16 @@ function stopEvalScheduler() {
|
|
|
951
981
|
}
|
|
952
982
|
|
|
953
983
|
export async function runDaemonHealthCheckNow() {
|
|
954
|
-
await
|
|
984
|
+
await Promise.all([
|
|
985
|
+
runHealthChecks(),
|
|
986
|
+
runConnectorHealthChecks(Date.now()),
|
|
987
|
+
])
|
|
955
988
|
}
|
|
956
989
|
|
|
957
990
|
export function getDaemonStatus() {
|
|
958
991
|
const queue = loadQueue()
|
|
959
992
|
const schedules = loadSchedules()
|
|
993
|
+
const reconnectStates = Object.values(getAllReconnectStates())
|
|
960
994
|
|
|
961
995
|
// Find next scheduled task
|
|
962
996
|
let nextScheduled: number | null = null
|
|
@@ -985,9 +1019,12 @@ export function getDaemonStatus() {
|
|
|
985
1019
|
heartbeat: getHeartbeatServiceStatus(),
|
|
986
1020
|
health: {
|
|
987
1021
|
monitorActive: !!ds.healthIntervalId,
|
|
1022
|
+
connectorMonitorActive: !!ds.connectorHealthIntervalId,
|
|
988
1023
|
staleSessions: ds.staleSessionIds.size,
|
|
989
|
-
connectorsInBackoff:
|
|
1024
|
+
connectorsInBackoff: reconnectStates.filter((state) => !state.exhausted).length,
|
|
1025
|
+
connectorsExhausted: reconnectStates.filter((state) => state.exhausted).length,
|
|
990
1026
|
checkIntervalSec: Math.trunc(HEALTH_CHECK_INTERVAL / 1000),
|
|
1027
|
+
connectorCheckIntervalSec: Math.trunc(CONNECTOR_HEALTH_CHECK_INTERVAL / 1000),
|
|
991
1028
|
integrity: {
|
|
992
1029
|
enabled: loadSettings().integrityMonitorEnabled !== false,
|
|
993
1030
|
lastCheckedAt: ds.lastIntegrityCheckAt,
|
|
@@ -53,4 +53,41 @@ describe('data-dir resolution', () => {
|
|
|
53
53
|
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
54
54
|
}
|
|
55
55
|
})
|
|
56
|
+
|
|
57
|
+
it('uses isolated temp dirs during build bootstrap when DATA_DIR is unset', () => {
|
|
58
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-data-dir-build-'))
|
|
59
|
+
const fakeHome = path.join(tempDir, 'home')
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const env = { ...process.env, HOME: fakeHome, npm_lifecycle_event: 'build:ci' }
|
|
63
|
+
delete env.DATA_DIR
|
|
64
|
+
delete env.WORKSPACE_DIR
|
|
65
|
+
delete env.BROWSER_PROFILES_DIR
|
|
66
|
+
|
|
67
|
+
const result = spawnSync(process.execPath, ['--import', 'tsx', '--input-type=module', '--eval', `
|
|
68
|
+
const modNs = await import('./src/lib/server/data-dir.ts')
|
|
69
|
+
const mod = modNs.default || modNs['module.exports'] || modNs
|
|
70
|
+
console.log(JSON.stringify({
|
|
71
|
+
isBuildBootstrap: mod.IS_BUILD_BOOTSTRAP,
|
|
72
|
+
dataDir: mod.DATA_DIR,
|
|
73
|
+
workspaceDir: mod.WORKSPACE_DIR,
|
|
74
|
+
browserProfilesDir: mod.BROWSER_PROFILES_DIR,
|
|
75
|
+
}))
|
|
76
|
+
`], {
|
|
77
|
+
cwd: repoRoot,
|
|
78
|
+
env,
|
|
79
|
+
encoding: 'utf-8',
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
assert.equal(result.status, 0, result.stderr || result.stdout || 'subprocess failed')
|
|
83
|
+
const payload = extractLastJson(result.stdout || '')
|
|
84
|
+
const expectedDataDir = path.join(os.tmpdir(), 'swarmclaw-build-data')
|
|
85
|
+
assert.equal(payload.isBuildBootstrap, true)
|
|
86
|
+
assert.equal(payload.dataDir, expectedDataDir)
|
|
87
|
+
assert.equal(payload.workspaceDir, path.join(expectedDataDir, 'workspace'))
|
|
88
|
+
assert.equal(payload.browserProfilesDir, path.join(expectedDataDir, 'browser-profiles'))
|
|
89
|
+
} finally {
|
|
90
|
+
fs.rmSync(tempDir, { recursive: true, force: true })
|
|
91
|
+
}
|
|
92
|
+
})
|
|
56
93
|
})
|
|
@@ -2,7 +2,24 @@ import path from 'path'
|
|
|
2
2
|
import os from 'os'
|
|
3
3
|
import fs from 'fs'
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
function isBuildBootstrapEnv(env: NodeJS.ProcessEnv = process.env, argv: string[] = process.argv): boolean {
|
|
6
|
+
if (env.SWARMCLAW_BUILD_MODE === '1') return true
|
|
7
|
+
if (env.NEXT_PHASE === 'phase-production-build') return true
|
|
8
|
+
const lifecycle = env.npm_lifecycle_event?.trim().toLowerCase()
|
|
9
|
+
if (lifecycle === 'build' || lifecycle === 'build:ci' || lifecycle?.startsWith('build:')) return true
|
|
10
|
+
return argv.some((arg) => /\bnext(?:[\\/](?:dist[\\/]bin[\\/])?next)?\b/.test(arg))
|
|
11
|
+
&& argv.some((arg) => /\bbuild\b/.test(arg))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const IS_BUILD_BOOTSTRAP = isBuildBootstrapEnv()
|
|
15
|
+
|
|
16
|
+
function resolveDataDir(): string {
|
|
17
|
+
if (process.env.DATA_DIR) return process.env.DATA_DIR
|
|
18
|
+
if (IS_BUILD_BOOTSTRAP) return path.join(os.tmpdir(), 'swarmclaw-build-data')
|
|
19
|
+
return path.join(process.cwd(), 'data')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const DATA_DIR = resolveDataDir()
|
|
6
23
|
|
|
7
24
|
function supportsChildWrites(dir: string): boolean {
|
|
8
25
|
try {
|
|
@@ -19,6 +36,7 @@ function supportsChildWrites(dir: string): boolean {
|
|
|
19
36
|
// when agents create/modify files. Falls back to data/workspace for Docker/CI.
|
|
20
37
|
function resolveWorkspaceDir(): string {
|
|
21
38
|
if (process.env.WORKSPACE_DIR) return process.env.WORKSPACE_DIR
|
|
39
|
+
if (IS_BUILD_BOOTSTRAP) return path.join(DATA_DIR, 'workspace')
|
|
22
40
|
const external = path.join(os.homedir(), '.swarmclaw', 'workspace')
|
|
23
41
|
if (supportsChildWrites(external)) {
|
|
24
42
|
return external
|
|
@@ -30,6 +48,7 @@ export const WORKSPACE_DIR = resolveWorkspaceDir()
|
|
|
30
48
|
|
|
31
49
|
function resolveBrowserProfilesDir(): string {
|
|
32
50
|
if (process.env.BROWSER_PROFILES_DIR) return process.env.BROWSER_PROFILES_DIR
|
|
51
|
+
if (IS_BUILD_BOOTSTRAP) return path.join(DATA_DIR, 'browser-profiles')
|
|
33
52
|
const external = path.join(os.homedir(), '.swarmclaw', 'browser-profiles')
|
|
34
53
|
if (supportsChildWrites(external)) {
|
|
35
54
|
return external
|