@swarmclawai/swarmclaw 1.2.0 → 1.2.1
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 +10 -0
- package/package.json +4 -1
- package/src/app/api/chats/[id]/deploy/route.ts +11 -6
- package/src/app/api/chats/[id]/devserver/route.ts +5 -2
- package/src/app/api/chats/[id]/messages/route.ts +7 -1
- package/src/app/api/credentials/[id]/route.ts +4 -1
- package/src/app/api/extensions/marketplace/route.ts +5 -2
- package/src/app/api/memory/maintenance/route.ts +5 -2
- package/src/app/api/preview-server/route.ts +14 -11
- package/src/app/api/system/status/route.ts +11 -0
- package/src/app/api/upload/route.ts +4 -1
- package/src/cli/index.js +7 -0
- package/src/cli/spec.js +1 -0
- package/src/components/agents/agent-files-editor.tsx +44 -32
- package/src/components/agents/personality-builder.tsx +13 -7
- package/src/components/agents/trash-list.tsx +1 -1
- package/src/components/chat/message-bubble.tsx +1 -0
- package/src/components/chat/message-list.tsx +25 -39
- package/src/components/chat/swarm-status-card.tsx +10 -3
- package/src/components/layout/daemon-indicator.tsx +7 -8
- package/src/components/layout/update-banner.tsx +8 -13
- package/src/components/logs/log-list.tsx +1 -1
- package/src/components/memory/memory-card.tsx +3 -1
- package/src/components/org-chart/org-chart-view.tsx +4 -0
- package/src/components/projects/project-list.tsx +4 -2
- package/src/components/projects/tabs/overview-tab.tsx +3 -2
- package/src/components/secrets/secret-sheet.tsx +1 -1
- package/src/components/secrets/secrets-list.tsx +1 -1
- package/src/components/shared/agent-switch-dialog.tsx +12 -6
- package/src/components/shared/dir-browser.tsx +22 -18
- package/src/components/skills/skill-sheet.tsx +2 -3
- package/src/components/tasks/task-list.tsx +1 -1
- package/src/components/tasks/task-sheet.tsx +1 -1
- package/src/hooks/use-openclaw-gateway.ts +46 -27
- package/src/instrumentation.ts +10 -7
- package/src/lib/chat/chat.ts +18 -2
- package/src/lib/providers/anthropic.ts +6 -3
- package/src/lib/providers/claude-cli.ts +9 -3
- package/src/lib/providers/cli-utils.ts +15 -0
- package/src/lib/providers/codex-cli.ts +9 -3
- package/src/lib/providers/gemini-cli.ts +6 -2
- package/src/lib/providers/index.ts +4 -1
- package/src/lib/providers/ollama.ts +5 -2
- package/src/lib/providers/openai.ts +8 -5
- package/src/lib/providers/opencode-cli.ts +6 -2
- package/src/lib/server/agents/agent-registry.ts +20 -3
- package/src/lib/server/agents/main-agent-loop.ts +4 -3
- package/src/lib/server/autonomy/supervisor-reflection.ts +14 -1
- package/src/lib/server/chat-execution/chat-execution.ts +14 -2
- package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -3
- package/src/lib/server/chat-execution/continuation-limits.ts +6 -3
- package/src/lib/server/chat-execution/message-classifier.ts +5 -2
- package/src/lib/server/chat-execution/post-stream-finalization.ts +4 -1
- package/src/lib/server/chat-execution/prompt-builder.ts +11 -1
- package/src/lib/server/chat-execution/prompt-sections.ts +52 -9
- package/src/lib/server/chat-execution/response-completeness.ts +5 -2
- package/src/lib/server/chat-execution/stream-agent-chat.ts +42 -12
- package/src/lib/server/chatrooms/chatroom-memory-bridge.ts +6 -3
- package/src/lib/server/connectors/bluebubbles.ts +7 -4
- package/src/lib/server/connectors/connector-inbound.ts +16 -13
- package/src/lib/server/connectors/connector-lifecycle.ts +11 -8
- package/src/lib/server/connectors/connector-outbound.ts +6 -3
- package/src/lib/server/connectors/discord.ts +10 -7
- package/src/lib/server/connectors/email.ts +17 -14
- package/src/lib/server/connectors/googlechat.ts +7 -4
- package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -2
- package/src/lib/server/connectors/matrix.ts +6 -3
- package/src/lib/server/connectors/openclaw.ts +20 -17
- package/src/lib/server/connectors/outbox.ts +4 -1
- package/src/lib/server/connectors/runtime-state.ts +19 -0
- package/src/lib/server/connectors/session-consolidation.ts +5 -2
- package/src/lib/server/connectors/signal.ts +9 -6
- package/src/lib/server/connectors/slack.ts +13 -10
- package/src/lib/server/connectors/teams.ts +8 -5
- package/src/lib/server/connectors/telegram.ts +15 -12
- package/src/lib/server/connectors/whatsapp.ts +32 -29
- package/src/lib/server/embeddings.ts +4 -1
- package/src/lib/server/link-understanding.ts +4 -1
- package/src/lib/server/memory/memory-abstract.ts +59 -0
- package/src/lib/server/memory/memory-db.ts +40 -14
- package/src/lib/server/missions/mission-service.ts +6 -3
- package/src/lib/server/openclaw/gateway.ts +8 -5
- package/src/lib/server/project-utils.ts +13 -0
- package/src/lib/server/protocols/protocol-agent-turn.ts +5 -2
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +5 -2
- package/src/lib/server/protocols/protocol-step-helpers.ts +4 -1
- package/src/lib/server/provider-health.ts +18 -0
- package/src/lib/server/query-expansion.ts +4 -1
- package/src/lib/server/runtime/alert-dispatch.ts +7 -6
- package/src/lib/server/runtime/daemon-state.ts +189 -50
- package/src/lib/server/runtime/heartbeat-service.ts +23 -0
- package/src/lib/server/runtime/idle-window.ts +4 -1
- package/src/lib/server/runtime/perf.ts +4 -1
- package/src/lib/server/runtime/process-manager.ts +7 -4
- package/src/lib/server/runtime/queue.ts +31 -28
- package/src/lib/server/runtime/scheduler.ts +9 -6
- package/src/lib/server/runtime/session-run-manager.ts +3 -0
- package/src/lib/server/sandbox/bridge-auth-registry.ts +6 -0
- package/src/lib/server/sandbox/novnc-auth.ts +10 -0
- package/src/lib/server/session-tools/context.ts +14 -0
- package/src/lib/server/session-tools/discovery.ts +9 -6
- package/src/lib/server/session-tools/index.ts +3 -1
- package/src/lib/server/session-tools/platform.ts +1 -1
- package/src/lib/server/session-tools/subagent.ts +23 -2
- package/src/lib/server/session-tools/wallet.ts +4 -1
- package/src/lib/server/skills/clawhub-client.ts +4 -1
- package/src/lib/server/skills/runtime-skill-resolver.ts +8 -2
- package/src/lib/server/skills/skill-eligibility.ts +6 -0
- package/src/lib/server/solana.ts +6 -0
- package/src/lib/server/storage-auth.ts +5 -5
- package/src/lib/server/storage-normalization.ts +4 -0
- package/src/lib/server/storage.ts +19 -8
- package/src/lib/server/tasks/task-followups.ts +4 -1
- package/src/lib/server/tool-loop-detection.ts +8 -3
- package/src/lib/server/tool-planning.ts +226 -0
- package/src/lib/server/tool-retry.ts +4 -3
- package/src/lib/server/wallet/wallet-portfolio.ts +29 -0
- package/src/lib/server/ws-hub.ts +5 -2
- package/src/lib/strip-internal-metadata.test.ts +44 -4
- package/src/lib/strip-internal-metadata.ts +20 -6
- package/src/stores/use-approval-store.ts +7 -1
- package/src/stores/use-chat-store.ts +5 -1
- package/src/types/index.ts +6 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { log } from '@/lib/server/logger'
|
|
1
2
|
import type { PlatformConnector, ConnectorInstance, InboundMessage } from './types'
|
|
2
3
|
import { resolveConnectorIngressReply } from './ingress-delivery'
|
|
3
4
|
|
|
5
|
+
const TAG = 'googlechat'
|
|
6
|
+
|
|
4
7
|
const googlechat: PlatformConnector = {
|
|
5
8
|
async start(connector, botToken, onMessage): Promise<ConnectorInstance> {
|
|
6
9
|
const pkg = 'googleapis'
|
|
@@ -29,11 +32,11 @@ const googlechat: PlatformConnector = {
|
|
|
29
32
|
const handlerKey = `__swarmclaw_googlechat_handler_${connector.id}__`
|
|
30
33
|
let stopped = false
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
log.info(TAG, 'Bot authenticated via service account')
|
|
33
36
|
if (allowedSpaces) {
|
|
34
|
-
|
|
37
|
+
log.info(TAG, `Filtering to spaces: ${allowedSpaces.join(', ')}`)
|
|
35
38
|
}
|
|
36
|
-
|
|
39
|
+
log.info(TAG, `Inbound webhook endpoint: /api/connectors/${connector.id}/webhook`)
|
|
37
40
|
|
|
38
41
|
function cleanInboundText(raw: unknown): string {
|
|
39
42
|
const txt = typeof raw === 'string' ? raw : ''
|
|
@@ -100,7 +103,7 @@ const googlechat: PlatformConnector = {
|
|
|
100
103
|
stopped = true
|
|
101
104
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
102
105
|
delete (globalThis as any)[handlerKey]
|
|
103
|
-
|
|
106
|
+
log.info(TAG, 'Bot disconnected')
|
|
104
107
|
},
|
|
105
108
|
}
|
|
106
109
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { log } from '@/lib/server/logger'
|
|
1
2
|
import fs from 'node:fs'
|
|
2
3
|
import path from 'node:path'
|
|
3
4
|
import { decryptKey, loadCredentials, loadSettings } from '../storage'
|
|
@@ -5,6 +6,8 @@ import { mimeFromPath } from './media'
|
|
|
5
6
|
import type { InboundMessage, InboundMedia } from './types'
|
|
6
7
|
import { errorMessage } from '@/lib/shared-utils'
|
|
7
8
|
|
|
9
|
+
const TAG = 'audio-transcription'
|
|
10
|
+
|
|
8
11
|
const PLACEHOLDER_TEXT = new Set([
|
|
9
12
|
'',
|
|
10
13
|
'(media message)',
|
|
@@ -250,11 +253,11 @@ export async function enrichInboundMessageWithAudioTranscript(params: {
|
|
|
250
253
|
try {
|
|
251
254
|
const transcript = (await attempt.run()).replace(/\s+/g, ' ').trim()
|
|
252
255
|
if (!transcript) continue
|
|
253
|
-
|
|
256
|
+
log.info(TAG, `Inbound audio transcribed via ${attempt.provider}: ${path.basename(localPath)}`)
|
|
254
257
|
return { ...msg, text: transcript }
|
|
255
258
|
} catch (err: unknown) {
|
|
256
259
|
const reason = errorMessage(err)
|
|
257
|
-
|
|
260
|
+
log.warn(TAG, `Inbound audio transcription failed via ${attempt.provider}: ${reason}`)
|
|
258
261
|
}
|
|
259
262
|
}
|
|
260
263
|
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { log } from '@/lib/server/logger'
|
|
1
2
|
import fs from 'fs'
|
|
2
3
|
import path from 'path'
|
|
3
4
|
import { DATA_DIR } from '../data-dir'
|
|
4
5
|
import type { PlatformConnector, ConnectorInstance, InboundMessage } from './types'
|
|
5
6
|
import { resolveConnectorIngressReply } from './ingress-delivery'
|
|
6
7
|
|
|
8
|
+
const TAG = 'matrix'
|
|
9
|
+
|
|
7
10
|
const matrix: PlatformConnector = {
|
|
8
11
|
async start(connector, botToken, onMessage): Promise<ConnectorInstance> {
|
|
9
12
|
const pkg = 'matrix-bot-sdk'
|
|
@@ -52,7 +55,7 @@ const matrix: PlatformConnector = {
|
|
|
52
55
|
if (!reply) return
|
|
53
56
|
await client.sendText(roomId, reply.visibleText)
|
|
54
57
|
} catch (err: any) {
|
|
55
|
-
|
|
58
|
+
log.error(TAG, 'Error handling message:', err.message)
|
|
56
59
|
try {
|
|
57
60
|
await client.sendText(roomId, 'Sorry, I encountered an error processing your message.')
|
|
58
61
|
} catch { /* ignore */ }
|
|
@@ -60,7 +63,7 @@ const matrix: PlatformConnector = {
|
|
|
60
63
|
})
|
|
61
64
|
|
|
62
65
|
await client.start()
|
|
63
|
-
|
|
66
|
+
log.info(TAG, `Bot connected to ${homeserverUrl}`)
|
|
64
67
|
|
|
65
68
|
return {
|
|
66
69
|
connector,
|
|
@@ -69,7 +72,7 @@ const matrix: PlatformConnector = {
|
|
|
69
72
|
},
|
|
70
73
|
async stop() {
|
|
71
74
|
client.stop()
|
|
72
|
-
|
|
75
|
+
log.info(TAG, 'Bot disconnected')
|
|
73
76
|
},
|
|
74
77
|
}
|
|
75
78
|
},
|
|
@@ -12,6 +12,9 @@ import {
|
|
|
12
12
|
type GatewayFrame,
|
|
13
13
|
type GatewayResponseFrame,
|
|
14
14
|
} from '../gateway/protocol'
|
|
15
|
+
import { log } from '@/lib/server/logger'
|
|
16
|
+
|
|
17
|
+
const TAG = 'openclaw'
|
|
15
18
|
|
|
16
19
|
/**
|
|
17
20
|
* OpenClaw gateway connector using the current WS protocol:
|
|
@@ -443,7 +446,7 @@ async function buildOutboundAttachments(options?: OutboundSendOptions): Promise<
|
|
|
443
446
|
})
|
|
444
447
|
return { attachments: [attachment], fallbackUrl: null }
|
|
445
448
|
} catch (err) {
|
|
446
|
-
|
|
449
|
+
log.warn(TAG, `Failed to inline media URL, falling back to link send: ${getErrorMessage(err)}`)
|
|
447
450
|
return { attachments: [], fallbackUrl: mediaUrl }
|
|
448
451
|
}
|
|
449
452
|
}
|
|
@@ -705,8 +708,8 @@ const openclaw: PlatformConnector = {
|
|
|
705
708
|
if (lastTickAtMs <= 0) return
|
|
706
709
|
const delta = Date.now() - lastTickAtMs
|
|
707
710
|
if (delta <= toleranceMs) return
|
|
708
|
-
|
|
709
|
-
`
|
|
711
|
+
log.error(
|
|
712
|
+
TAG, `Tick missed (${delta}ms > ${toleranceMs}ms), forcing reconnect`,
|
|
710
713
|
)
|
|
711
714
|
try { ws.close(4000, 'tick missed') } catch { /* ignore */ }
|
|
712
715
|
}, pollMs)
|
|
@@ -740,7 +743,7 @@ const openclaw: PlatformConnector = {
|
|
|
740
743
|
const previous = historyErrorLogBySession.get(sessionKey) || 0
|
|
741
744
|
if (now - previous < HISTORY_ERROR_LOG_INTERVAL_MS) return
|
|
742
745
|
historyErrorLogBySession.set(sessionKey, now)
|
|
743
|
-
|
|
746
|
+
log.warn(TAG, `chat.history poll failed for "${sessionKey}": ${message}`)
|
|
744
747
|
}
|
|
745
748
|
|
|
746
749
|
function cleanupSocket() {
|
|
@@ -764,7 +767,7 @@ const openclaw: PlatformConnector = {
|
|
|
764
767
|
if (stopped) return
|
|
765
768
|
const delay = Math.min(RECONNECT_BASE_MS * 2 ** reconnectAttempt, RECONNECT_MAX_MS)
|
|
766
769
|
reconnectAttempt++
|
|
767
|
-
|
|
770
|
+
log.info(TAG, `Reconnecting in ${delay}ms (attempt ${reconnectAttempt})`)
|
|
768
771
|
clearReconnectTimer()
|
|
769
772
|
reconnectTimer = setTimeout(() => connect(), delay)
|
|
770
773
|
}
|
|
@@ -838,7 +841,7 @@ const openclaw: PlatformConnector = {
|
|
|
838
841
|
const lowerReason = (reason || '').toLowerCase()
|
|
839
842
|
if (!lowerReason.includes('device token mismatch')) return
|
|
840
843
|
if (!identity.deviceToken) return
|
|
841
|
-
|
|
844
|
+
log.warn(TAG, 'Clearing stale stored device token after mismatch')
|
|
842
845
|
persistIdentityToken(undefined)
|
|
843
846
|
}
|
|
844
847
|
|
|
@@ -918,11 +921,11 @@ const openclaw: PlatformConnector = {
|
|
|
918
921
|
}
|
|
919
922
|
if (tickWatchdogEnabled) startTickWatchdog()
|
|
920
923
|
startHistoryPoller()
|
|
921
|
-
|
|
924
|
+
log.info(TAG, `Connected + authenticated (${wsUrl})`)
|
|
922
925
|
})
|
|
923
926
|
.catch((err: unknown) => {
|
|
924
927
|
clearConnectHelloTimer()
|
|
925
|
-
|
|
928
|
+
log.error(TAG, `Connect handshake failed: ${getErrorMessage(err)}`)
|
|
926
929
|
try { ws?.close(1008, 'connect failed') } catch { /* ignore */ }
|
|
927
930
|
})
|
|
928
931
|
}
|
|
@@ -962,7 +965,7 @@ const openclaw: PlatformConnector = {
|
|
|
962
965
|
await sendChat(inbound.channelId, reply.visibleText)
|
|
963
966
|
} catch (err: unknown) {
|
|
964
967
|
const message = getErrorMessage(err)
|
|
965
|
-
|
|
968
|
+
log.error(TAG, 'Error routing inbound chat event:', message)
|
|
966
969
|
await sendChat(inbound.channelId, `[Error] ${message}`)
|
|
967
970
|
}
|
|
968
971
|
}
|
|
@@ -1025,7 +1028,7 @@ const openclaw: PlatformConnector = {
|
|
|
1025
1028
|
) {
|
|
1026
1029
|
historyPollingUnsupported = true
|
|
1027
1030
|
clearHistoryPollTimer()
|
|
1028
|
-
|
|
1031
|
+
log.warn(TAG, 'chat.history is unavailable; disabling history polling fallback')
|
|
1029
1032
|
return
|
|
1030
1033
|
}
|
|
1031
1034
|
maybeLogHistoryError(sessionKey, message)
|
|
@@ -1059,18 +1062,18 @@ const openclaw: PlatformConnector = {
|
|
|
1059
1062
|
function connect() {
|
|
1060
1063
|
if (stopped) return
|
|
1061
1064
|
cleanupSocket()
|
|
1062
|
-
|
|
1065
|
+
log.info(TAG, `Connecting to ${wsUrl}`)
|
|
1063
1066
|
ws = new WebSocket(wsUrl)
|
|
1064
1067
|
|
|
1065
1068
|
ws.onopen = () => {
|
|
1066
|
-
|
|
1069
|
+
log.info(TAG, `Socket open: ${wsUrl}`)
|
|
1067
1070
|
connectSent = false
|
|
1068
1071
|
connected = false
|
|
1069
1072
|
lastTickAtMs = 0
|
|
1070
1073
|
clearConnectHelloTimer()
|
|
1071
1074
|
connectHelloTimer = setTimeout(() => {
|
|
1072
1075
|
if (stopped || connected) return
|
|
1073
|
-
|
|
1076
|
+
log.warn(TAG, `Connect handshake timed out after ${CONNECT_HELLO_TIMEOUT_MS}ms`)
|
|
1074
1077
|
try { ws?.close(4001, 'connect timeout') } catch { /* ignore */ }
|
|
1075
1078
|
}, CONNECT_HELLO_TIMEOUT_MS)
|
|
1076
1079
|
connectHelloTimer.unref?.()
|
|
@@ -1080,7 +1083,7 @@ const openclaw: PlatformConnector = {
|
|
|
1080
1083
|
ws.onmessage = (event) => {
|
|
1081
1084
|
const frame = parseGatewayFrame(event.data)
|
|
1082
1085
|
if (!frame) {
|
|
1083
|
-
|
|
1086
|
+
log.warn(TAG, 'Ignoring malformed gateway frame')
|
|
1084
1087
|
return
|
|
1085
1088
|
}
|
|
1086
1089
|
|
|
@@ -1124,14 +1127,14 @@ const openclaw: PlatformConnector = {
|
|
|
1124
1127
|
|
|
1125
1128
|
ws.onclose = (event) => {
|
|
1126
1129
|
const reason = event.reason || 'none'
|
|
1127
|
-
|
|
1130
|
+
log.info(TAG, `Disconnected (code=${event.code}, reason=${reason})`)
|
|
1128
1131
|
clearStaleTokenIfNeeded(reason)
|
|
1129
1132
|
cleanupSocket()
|
|
1130
1133
|
if (!stopped) scheduleReconnect()
|
|
1131
1134
|
}
|
|
1132
1135
|
|
|
1133
1136
|
ws.onerror = () => {
|
|
1134
|
-
|
|
1137
|
+
log.error(TAG, 'WebSocket error')
|
|
1135
1138
|
}
|
|
1136
1139
|
}
|
|
1137
1140
|
|
|
@@ -1189,7 +1192,7 @@ const openclaw: PlatformConnector = {
|
|
|
1189
1192
|
async stop() {
|
|
1190
1193
|
stopped = true
|
|
1191
1194
|
cleanupSocket()
|
|
1192
|
-
|
|
1195
|
+
log.info(TAG, 'Connector stopped')
|
|
1193
1196
|
},
|
|
1194
1197
|
}
|
|
1195
1198
|
},
|
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
} from '../storage'
|
|
7
7
|
import { notify } from '../ws-hub'
|
|
8
8
|
import { errorMessage, hmrSingleton } from '@/lib/shared-utils'
|
|
9
|
+
import { log } from '@/lib/server/logger'
|
|
10
|
+
|
|
11
|
+
const TAG = 'connector-outbox'
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
export type ConnectorOutboxStatus =
|
|
@@ -151,7 +154,7 @@ function scheduleTimer(delayMs: number): void {
|
|
|
151
154
|
outboxState.timer = null
|
|
152
155
|
outboxState.dueAt = null
|
|
153
156
|
void runConnectorOutboxNow().catch((err: unknown) => {
|
|
154
|
-
|
|
157
|
+
log.warn(TAG, `Worker tick failed: ${errorMessage(err)}`)
|
|
155
158
|
})
|
|
156
159
|
}, Math.max(0, delayMs))
|
|
157
160
|
outboxState.timer.unref?.()
|
|
@@ -66,4 +66,23 @@ export const connectorRuntimeState = getConnectorRuntimeState()
|
|
|
66
66
|
|
|
67
67
|
export const runningConnectors = connectorRuntimeState.running
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Remove tracking entries for connectors that no longer exist in storage.
|
|
71
|
+
* Called periodically by the daemon health sweep.
|
|
72
|
+
*/
|
|
73
|
+
export function pruneConnectorTrackingState(liveConnectorIds: Set<string>): number {
|
|
74
|
+
const st = getConnectorRuntimeState()
|
|
75
|
+
let removed = 0
|
|
76
|
+
for (const id of st.lastInboundChannelByConnector.keys()) {
|
|
77
|
+
if (!liveConnectorIds.has(id)) { st.lastInboundChannelByConnector.delete(id); removed++ }
|
|
78
|
+
}
|
|
79
|
+
for (const id of st.lastInboundTimeByConnector.keys()) {
|
|
80
|
+
if (!liveConnectorIds.has(id)) { st.lastInboundTimeByConnector.delete(id); removed++ }
|
|
81
|
+
}
|
|
82
|
+
for (const id of st.generationCounter.keys()) {
|
|
83
|
+
if (!liveConnectorIds.has(id)) { st.generationCounter.delete(id); removed++ }
|
|
84
|
+
}
|
|
85
|
+
return removed
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
export type ConnectorThreadSession = Session
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
* One-time migration: backfill allKnownPeerIds on existing connector sessions.
|
|
3
3
|
* Populates the field from existing senderId, senderIdAlt, channelId, channelIdAlt, peerKey.
|
|
4
4
|
*/
|
|
5
|
+
import { log } from '@/lib/server/logger'
|
|
5
6
|
import { loadSessions, loadSettings, saveSettings, upsertStoredItem } from '../storage'
|
|
6
7
|
import type { Session } from '@/types'
|
|
7
8
|
import { isDirectConnectorSession } from './session-kind'
|
|
8
9
|
|
|
10
|
+
const TAG = 'session-consolidation'
|
|
11
|
+
|
|
9
12
|
const ALL_KNOWN_PEER_IDS_MIGRATION_FLAG = '_migration_allKnownPeerIds'
|
|
10
13
|
const THREAD_CONNECTOR_MIRROR_CLEANUP_FLAG = '_migration_pruneThreadConnectorMirrors'
|
|
11
14
|
|
|
@@ -43,7 +46,7 @@ export function backfillAllKnownPeerIds(): { migrated: number; skipped: boolean
|
|
|
43
46
|
updated[ALL_KNOWN_PEER_IDS_MIGRATION_FLAG] = true
|
|
44
47
|
saveSettings(updated)
|
|
45
48
|
if (migrated > 0) {
|
|
46
|
-
|
|
49
|
+
log.info(TAG, `Backfilled allKnownPeerIds on ${migrated} sessions`)
|
|
47
50
|
}
|
|
48
51
|
return { migrated, skipped: false }
|
|
49
52
|
}
|
|
@@ -81,7 +84,7 @@ export function pruneThreadConnectorMirrors(): { cleanedSessions: number; remove
|
|
|
81
84
|
updated[THREAD_CONNECTOR_MIRROR_CLEANUP_FLAG] = true
|
|
82
85
|
saveSettings(updated)
|
|
83
86
|
if (removedMessages > 0) {
|
|
84
|
-
|
|
87
|
+
log.info(TAG, `Pruned ${removedMessages} mirrored connector message(s) from ${cleanedSessions} main session(s)`)
|
|
85
88
|
}
|
|
86
89
|
return { cleanedSessions, removedMessages, skipped: false }
|
|
87
90
|
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { log } from '@/lib/server/logger'
|
|
1
2
|
import { spawn, execSync } from 'child_process'
|
|
2
3
|
import type { ChildProcess } from 'child_process'
|
|
3
4
|
import type { Connector } from '@/types'
|
|
4
5
|
import type { PlatformConnector, ConnectorInstance, InboundMessage, ConnectorIngressResult } from './types'
|
|
5
6
|
import { resolveConnectorIngressReply } from './ingress-delivery'
|
|
6
7
|
|
|
8
|
+
const TAG = 'signal'
|
|
9
|
+
|
|
7
10
|
const signal: PlatformConnector = {
|
|
8
11
|
async start(connector, _botToken, onMessage): Promise<ConnectorInstance> {
|
|
9
12
|
const phoneNumber = connector.config.phoneNumber
|
|
@@ -43,16 +46,16 @@ const signal: PlatformConnector = {
|
|
|
43
46
|
|
|
44
47
|
daemonProc.stderr?.on('data', (chunk: Buffer) => {
|
|
45
48
|
const msg = chunk.toString().trim()
|
|
46
|
-
if (msg)
|
|
49
|
+
if (msg) log.error(TAG, `stderr: ${msg}`)
|
|
47
50
|
})
|
|
48
51
|
|
|
49
52
|
daemonProc.on('exit', (code) => {
|
|
50
53
|
if (!stopped) {
|
|
51
|
-
|
|
54
|
+
log.error(TAG, `daemon exited unexpectedly with code ${code}`)
|
|
52
55
|
}
|
|
53
56
|
})
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
log.info(TAG, `Daemon started in stdio mode for ${phoneNumber}`)
|
|
56
59
|
} else if (mode === 'http') {
|
|
57
60
|
// Poll the signal-cli REST API for incoming messages
|
|
58
61
|
const pollInterval = 2000
|
|
@@ -74,7 +77,7 @@ const signal: PlatformConnector = {
|
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
pollTimer = setInterval(poll, pollInterval)
|
|
77
|
-
|
|
80
|
+
log.info(TAG, `Polling ${httpUrl} for ${phoneNumber} every ${pollInterval}ms`)
|
|
78
81
|
} else {
|
|
79
82
|
throw new Error(`Unknown signalCliMode: ${mode}. Use 'stdio' or 'http'.`)
|
|
80
83
|
}
|
|
@@ -119,7 +122,7 @@ const signal: PlatformConnector = {
|
|
|
119
122
|
daemonProc.kill('SIGTERM')
|
|
120
123
|
daemonProc = null
|
|
121
124
|
}
|
|
122
|
-
|
|
125
|
+
log.info(TAG, `Connector stopped for ${phoneNumber}`)
|
|
123
126
|
},
|
|
124
127
|
}
|
|
125
128
|
},
|
|
@@ -177,7 +180,7 @@ export async function handleSignalEvent(
|
|
|
177
180
|
)
|
|
178
181
|
}
|
|
179
182
|
} catch (err: any) {
|
|
180
|
-
|
|
183
|
+
log.error(TAG, 'Error handling message:', err.message)
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
|
|
@@ -7,6 +7,9 @@ import { resolveConnectorIngressReply } from './ingress-delivery'
|
|
|
7
7
|
import { deliverChunkedConnectorText } from './delivery'
|
|
8
8
|
import { downloadInboundMediaToUpload, inferInboundMediaType, mimeFromPath, isImageMime } from './media'
|
|
9
9
|
import { dedup, errorMessage } from '@/lib/shared-utils'
|
|
10
|
+
import { log } from '@/lib/server/logger'
|
|
11
|
+
|
|
12
|
+
const TAG = 'slack'
|
|
10
13
|
|
|
11
14
|
function normalizeSlackEmoji(input: string): string {
|
|
12
15
|
const raw = input.trim().replace(/^:|:$/g, '')
|
|
@@ -99,7 +102,7 @@ async function hydrateSlackThreadContext(params: {
|
|
|
99
102
|
params.inbound.threadPersonaLabel = params.inbound.threadTitle
|
|
100
103
|
params.inbound.threadHistory = history.length ? history : undefined
|
|
101
104
|
} catch (err: unknown) {
|
|
102
|
-
|
|
105
|
+
log.warn(TAG, `Thread context bootstrap failed: ${errorMessage(err)}`)
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
108
|
|
|
@@ -141,7 +144,7 @@ const slack: PlatformConnector = {
|
|
|
141
144
|
throw new Error('Auth test returned empty — the bot token may be revoked or the app uninstalled')
|
|
142
145
|
}
|
|
143
146
|
botUserId = auth.user_id as string
|
|
144
|
-
|
|
147
|
+
log.info(TAG, `Authenticated as @${auth.user} in workspace "${auth.team}"`)
|
|
145
148
|
} catch (err: any) {
|
|
146
149
|
const hint = err.code === 'slack_webapi_platform_error'
|
|
147
150
|
? '. Check that your Bot Token (xoxb-...) is correct and the app is installed to the workspace.'
|
|
@@ -159,7 +162,7 @@ const slack: PlatformConnector = {
|
|
|
159
162
|
|
|
160
163
|
// Catch global errors so they don't become unhandled rejections
|
|
161
164
|
app.error(async (error) => {
|
|
162
|
-
|
|
165
|
+
log.error(TAG, 'App error:', error)
|
|
163
166
|
})
|
|
164
167
|
|
|
165
168
|
// Optional: restrict to specific channels
|
|
@@ -177,7 +180,7 @@ const slack: PlatformConnector = {
|
|
|
177
180
|
const channelId = msg.channel
|
|
178
181
|
if (allowedChannels && !allowedChannels.includes(channelId)) return
|
|
179
182
|
|
|
180
|
-
|
|
183
|
+
log.info(TAG, `Message in ${channelId} from ${msg.user}: ${(msg.text || '').slice(0, 80)}`)
|
|
181
184
|
|
|
182
185
|
// Get user info for display name
|
|
183
186
|
let senderName = msg.user || 'unknown'
|
|
@@ -213,7 +216,7 @@ const slack: PlatformConnector = {
|
|
|
213
216
|
continue
|
|
214
217
|
}
|
|
215
218
|
} catch (err: any) {
|
|
216
|
-
|
|
219
|
+
log.warn(TAG, `Media download failed (${f?.name || 'file'}):`, err?.message || String(err))
|
|
217
220
|
}
|
|
218
221
|
}
|
|
219
222
|
media.push({
|
|
@@ -262,7 +265,7 @@ const slack: PlatformConnector = {
|
|
|
262
265
|
},
|
|
263
266
|
})
|
|
264
267
|
} catch (err: any) {
|
|
265
|
-
|
|
268
|
+
log.error(TAG, 'Error handling message:', err.message)
|
|
266
269
|
try {
|
|
267
270
|
await say('Sorry, I encountered an error processing your message.')
|
|
268
271
|
} catch { /* ignore */ }
|
|
@@ -320,12 +323,12 @@ const slack: PlatformConnector = {
|
|
|
320
323
|
},
|
|
321
324
|
})
|
|
322
325
|
} catch (err: any) {
|
|
323
|
-
|
|
326
|
+
log.error(TAG, 'Error handling mention:', err.message)
|
|
324
327
|
}
|
|
325
328
|
})
|
|
326
329
|
|
|
327
330
|
await app.start()
|
|
328
|
-
|
|
331
|
+
log.info(TAG, 'Bot connected (socket mode)')
|
|
329
332
|
|
|
330
333
|
let appStopped = false
|
|
331
334
|
|
|
@@ -423,14 +426,14 @@ const slack: PlatformConnector = {
|
|
|
423
426
|
async stop() {
|
|
424
427
|
appStopped = true
|
|
425
428
|
await app.stop()
|
|
426
|
-
|
|
429
|
+
log.info(TAG, 'Bot disconnected')
|
|
427
430
|
},
|
|
428
431
|
}
|
|
429
432
|
|
|
430
433
|
// Bolt emits 'error' on unrecoverable failures (auth revoked, socket closed permanently)
|
|
431
434
|
app.error(async (error) => {
|
|
432
435
|
const errMsg = error.original?.message || error.message || String(error)
|
|
433
|
-
|
|
436
|
+
log.error(TAG, 'App error:', errMsg)
|
|
434
437
|
if (appStopped) return
|
|
435
438
|
appStopped = true
|
|
436
439
|
instance.onCrash?.(`Slack error: ${errMsg}`)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { log } from '@/lib/server/logger'
|
|
1
2
|
import type { PlatformConnector, ConnectorInstance, InboundMessage } from './types'
|
|
2
3
|
import { resolveConnectorIngressReply } from './ingress-delivery'
|
|
3
4
|
|
|
5
|
+
const TAG = 'teams'
|
|
6
|
+
|
|
4
7
|
const teams: PlatformConnector = {
|
|
5
8
|
async start(connector, botToken, onMessage): Promise<ConnectorInstance> {
|
|
6
9
|
const pkg = 'botbuilder'
|
|
@@ -15,7 +18,7 @@ const teams: PlatformConnector = {
|
|
|
15
18
|
})
|
|
16
19
|
|
|
17
20
|
adapter.onTurnError = async (_context: unknown, error: Error) => {
|
|
18
|
-
|
|
21
|
+
log.error(TAG, 'Turn error:', error.message)
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
// Store conversation references for proactive messaging
|
|
@@ -49,7 +52,7 @@ const teams: PlatformConnector = {
|
|
|
49
52
|
if (!reply) return
|
|
50
53
|
await context.sendActivity(reply.visibleText)
|
|
51
54
|
} catch (err: any) {
|
|
52
|
-
|
|
55
|
+
log.error(TAG, 'Error handling message:', err.message)
|
|
53
56
|
try {
|
|
54
57
|
await context.sendActivity('Sorry, I encountered an error processing your message.')
|
|
55
58
|
} catch { /* ignore */ }
|
|
@@ -61,8 +64,8 @@ const teams: PlatformConnector = {
|
|
|
61
64
|
const handlerKey = `__swarmclaw_teams_handler_${connector.id}__`
|
|
62
65
|
;(globalThis as any)[handlerKey] = processActivity
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
log.info(TAG, `Bot registered (appId: ${appId})`)
|
|
68
|
+
log.info(TAG, `Configure your bot's messaging endpoint to POST to /api/connectors/${connector.id}/webhook`)
|
|
66
69
|
|
|
67
70
|
return {
|
|
68
71
|
connector,
|
|
@@ -85,7 +88,7 @@ const teams: PlatformConnector = {
|
|
|
85
88
|
stopped = true
|
|
86
89
|
delete (globalThis as any)[handlerKey]
|
|
87
90
|
conversationReferences.clear()
|
|
88
|
-
|
|
91
|
+
log.info(TAG, 'Bot disconnected')
|
|
89
92
|
},
|
|
90
93
|
}
|
|
91
94
|
},
|
|
@@ -7,6 +7,9 @@ import { resolveConnectorIngressReply } from './ingress-delivery'
|
|
|
7
7
|
import { deliverChunkedConnectorText } from './delivery'
|
|
8
8
|
import { downloadInboundMediaToUpload, inferInboundMediaType, mimeFromPath, isImageMime, isAudioMime } from './media'
|
|
9
9
|
import { errorMessage } from '@/lib/shared-utils'
|
|
10
|
+
import { log } from '@/lib/server/logger'
|
|
11
|
+
|
|
12
|
+
const TAG = 'telegram'
|
|
10
13
|
|
|
11
14
|
const telegram: PlatformConnector = {
|
|
12
15
|
async start(connector, botToken, onMessage): Promise<ConnectorInstance> {
|
|
@@ -20,23 +23,23 @@ const telegram: PlatformConnector = {
|
|
|
20
23
|
|
|
21
24
|
// Log all errors
|
|
22
25
|
bot.catch((err) => {
|
|
23
|
-
|
|
26
|
+
log.error(TAG, 'Bot error:', err.message || err)
|
|
24
27
|
})
|
|
25
28
|
|
|
26
29
|
// Delete any existing webhook so long polling works
|
|
27
30
|
await bot.api.deleteWebhook().catch((err) => {
|
|
28
|
-
|
|
31
|
+
log.error(TAG, 'Failed to delete webhook:', err.message)
|
|
29
32
|
})
|
|
30
33
|
|
|
31
34
|
// Log all incoming updates for debugging
|
|
32
35
|
bot.use(async (ctx, next) => {
|
|
33
|
-
|
|
36
|
+
log.info(TAG, `Update received: chat=${ctx.chat?.id}, from=${ctx.from?.first_name}, hasText=${!!ctx.message?.text}`)
|
|
34
37
|
await next()
|
|
35
38
|
})
|
|
36
39
|
|
|
37
40
|
// Handle /start command (required for new conversations)
|
|
38
41
|
bot.command('start', async (ctx) => {
|
|
39
|
-
|
|
42
|
+
log.info(TAG, `/start from ${ctx.from?.first_name} (chat=${ctx.chat.id})`)
|
|
40
43
|
await ctx.reply('Hello! I\'m ready to chat. Send me a message.')
|
|
41
44
|
})
|
|
42
45
|
|
|
@@ -51,15 +54,15 @@ const telegram: PlatformConnector = {
|
|
|
51
54
|
// that appear as short bracketed strings like [rr], [e], [read] etc.
|
|
52
55
|
const hasMedia = raw.photo || raw.video || raw.audio || raw.voice || raw.document || raw.animation
|
|
53
56
|
if (!hasMedia && /^\[.{1,5}\]$/.test(text.trim())) {
|
|
54
|
-
|
|
57
|
+
log.info(TAG, `Ignoring system event from ${ctx.from.first_name}: ${text}`)
|
|
55
58
|
return
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
|
|
61
|
+
log.info(TAG, `Message from ${ctx.from.first_name} (chat=${chatId}): ${String(text).slice(0, 80)}`)
|
|
59
62
|
|
|
60
63
|
// Filter by allowed chats if configured
|
|
61
64
|
if (allowedChats && !allowedChats.includes(chatId)) {
|
|
62
|
-
|
|
65
|
+
log.info(TAG, `Skipping — chat ${chatId} not in allowed list: ${allowedChats.join(',')}`)
|
|
63
66
|
return
|
|
64
67
|
}
|
|
65
68
|
|
|
@@ -125,7 +128,7 @@ const telegram: PlatformConnector = {
|
|
|
125
128
|
})
|
|
126
129
|
if (stored) media.push(stored)
|
|
127
130
|
} catch (err: any) {
|
|
128
|
-
|
|
131
|
+
log.warn(TAG, `Failed to fetch media ${m.fileId}:`, err?.message || String(err))
|
|
129
132
|
media.push({
|
|
130
133
|
type: m.type,
|
|
131
134
|
fileName: m.fileName,
|
|
@@ -175,7 +178,7 @@ const telegram: PlatformConnector = {
|
|
|
175
178
|
},
|
|
176
179
|
})
|
|
177
180
|
} catch (err: any) {
|
|
178
|
-
|
|
181
|
+
log.error(TAG, 'Error handling message:', err.message)
|
|
179
182
|
try {
|
|
180
183
|
await ctx.reply('Sorry, I encountered an error processing your message.')
|
|
181
184
|
} catch { /* ignore */ }
|
|
@@ -268,7 +271,7 @@ const telegram: PlatformConnector = {
|
|
|
268
271
|
async stop() {
|
|
269
272
|
botRunning = false
|
|
270
273
|
await bot.stop()
|
|
271
|
-
|
|
274
|
+
log.info(TAG, 'Bot stopped')
|
|
272
275
|
},
|
|
273
276
|
}
|
|
274
277
|
|
|
@@ -277,12 +280,12 @@ const telegram: PlatformConnector = {
|
|
|
277
280
|
allowed_updates: ['message', 'edited_message'],
|
|
278
281
|
onStart: (botInfo) => {
|
|
279
282
|
botUsername = botInfo.username || ''
|
|
280
|
-
|
|
283
|
+
log.info(TAG, `Bot started as @${botInfo.username} — polling for updates`)
|
|
281
284
|
},
|
|
282
285
|
}).catch((err: unknown) => {
|
|
283
286
|
botRunning = false
|
|
284
287
|
const errMsg = errorMessage(err)
|
|
285
|
-
|
|
288
|
+
log.error(TAG, 'Polling stopped with error:', errMsg)
|
|
286
289
|
instance.onCrash?.(`Polling stopped: ${errMsg}`)
|
|
287
290
|
})
|
|
288
291
|
|