@swarmclawai/swarmclaw 1.2.0 → 1.2.2

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.
Files changed (241) hide show
  1. package/README.md +19 -0
  2. package/package.json +5 -2
  3. package/skills/coding-agent/SKILL.md +111 -0
  4. package/skills/github/SKILL.md +140 -0
  5. package/skills/nano-banana-pro/SKILL.md +62 -0
  6. package/skills/nano-banana-pro/scripts/generate_image.py +235 -0
  7. package/skills/nano-pdf/SKILL.md +53 -0
  8. package/skills/openai-image-gen/SKILL.md +78 -0
  9. package/skills/openai-image-gen/scripts/gen.py +328 -0
  10. package/skills/resourceful-problem-solving/SKILL.md +49 -0
  11. package/skills/skill-creator/SKILL.md +147 -0
  12. package/skills/skill-creator/scripts/init_skill.py +378 -0
  13. package/skills/skill-creator/scripts/quick_validate.py +159 -0
  14. package/skills/summarize/SKILL.md +77 -0
  15. package/src/app/api/auth/route.ts +20 -5
  16. package/src/app/api/chats/[id]/deploy/route.ts +11 -6
  17. package/src/app/api/chats/[id]/devserver/route.ts +17 -20
  18. package/src/app/api/chats/[id]/messages/route.ts +15 -11
  19. package/src/app/api/chats/[id]/route.ts +9 -10
  20. package/src/app/api/chats/[id]/stop/route.ts +5 -7
  21. package/src/app/api/chats/messages-route.test.ts +8 -6
  22. package/src/app/api/chats/route.ts +9 -10
  23. package/src/app/api/credentials/[id]/route.ts +4 -1
  24. package/src/app/api/extensions/marketplace/route.ts +5 -2
  25. package/src/app/api/ip/route.ts +2 -2
  26. package/src/app/api/memory/maintenance/route.ts +5 -2
  27. package/src/app/api/preview-server/route.ts +15 -12
  28. package/src/app/api/projects/[id]/route.ts +7 -46
  29. package/src/app/api/system/status/route.ts +11 -0
  30. package/src/app/api/upload/route.ts +4 -1
  31. package/src/cli/index.js +7 -0
  32. package/src/cli/spec.js +1 -0
  33. package/src/components/agents/agent-files-editor.tsx +44 -32
  34. package/src/components/agents/personality-builder.tsx +13 -7
  35. package/src/components/agents/trash-list.tsx +1 -1
  36. package/src/components/chat/chat-area.tsx +45 -23
  37. package/src/components/chat/message-bubble.test.ts +35 -0
  38. package/src/components/chat/message-bubble.tsx +20 -9
  39. package/src/components/chat/message-list.tsx +62 -42
  40. package/src/components/chat/swarm-status-card.tsx +10 -3
  41. package/src/components/input/chat-input.tsx +34 -14
  42. package/src/components/layout/daemon-indicator.tsx +7 -8
  43. package/src/components/layout/update-banner.tsx +8 -13
  44. package/src/components/logs/log-list.tsx +1 -1
  45. package/src/components/memory/memory-card.tsx +3 -1
  46. package/src/components/org-chart/org-chart-view.tsx +4 -0
  47. package/src/components/projects/project-list.tsx +4 -2
  48. package/src/components/projects/tabs/overview-tab.tsx +3 -2
  49. package/src/components/secrets/secret-sheet.tsx +1 -1
  50. package/src/components/secrets/secrets-list.tsx +1 -1
  51. package/src/components/shared/agent-switch-dialog.tsx +12 -6
  52. package/src/components/shared/dir-browser.tsx +22 -18
  53. package/src/components/skills/skill-sheet.tsx +2 -3
  54. package/src/components/tasks/task-list.tsx +1 -1
  55. package/src/components/tasks/task-sheet.tsx +1 -1
  56. package/src/hooks/use-openclaw-gateway.ts +46 -27
  57. package/src/instrumentation.ts +10 -7
  58. package/src/lib/chat/assistant-render-id.ts +3 -0
  59. package/src/lib/chat/chat-streaming-state.test.ts +42 -3
  60. package/src/lib/chat/chat-streaming-state.ts +20 -8
  61. package/src/lib/chat/chat.ts +18 -2
  62. package/src/lib/chat/queued-message-queue.test.ts +23 -1
  63. package/src/lib/chat/queued-message-queue.ts +11 -2
  64. package/src/lib/providers/anthropic.ts +6 -3
  65. package/src/lib/providers/claude-cli.ts +9 -3
  66. package/src/lib/providers/cli-utils.test.ts +124 -0
  67. package/src/lib/providers/cli-utils.ts +15 -0
  68. package/src/lib/providers/codex-cli.ts +9 -3
  69. package/src/lib/providers/gemini-cli.ts +6 -2
  70. package/src/lib/providers/index.ts +4 -1
  71. package/src/lib/providers/ollama.ts +5 -2
  72. package/src/lib/providers/openai.ts +8 -5
  73. package/src/lib/providers/opencode-cli.ts +6 -2
  74. package/src/lib/server/activity/activity-log.ts +21 -0
  75. package/src/lib/server/agents/agent-availability.test.ts +10 -5
  76. package/src/lib/server/agents/agent-cascade.ts +79 -59
  77. package/src/lib/server/agents/agent-registry.ts +23 -4
  78. package/src/lib/server/agents/agent-repository.ts +90 -0
  79. package/src/lib/server/agents/delegation-job-repository.ts +53 -0
  80. package/src/lib/server/agents/delegation-jobs.ts +11 -4
  81. package/src/lib/server/agents/guardian-checkpoint-repository.ts +35 -0
  82. package/src/lib/server/agents/guardian.ts +2 -2
  83. package/src/lib/server/agents/main-agent-loop.ts +14 -6
  84. package/src/lib/server/agents/main-loop-state-repository.ts +38 -0
  85. package/src/lib/server/agents/subagent-runtime.ts +9 -6
  86. package/src/lib/server/agents/subagent-swarm.ts +3 -2
  87. package/src/lib/server/agents/task-session.ts +3 -4
  88. package/src/lib/server/approvals/approval-repository.ts +30 -0
  89. package/src/lib/server/autonomy/supervisor-incident-repository.ts +42 -0
  90. package/src/lib/server/autonomy/supervisor-reflection.ts +14 -1
  91. package/src/lib/server/chat-execution/chat-execution-types.ts +38 -0
  92. package/src/lib/server/chat-execution/chat-execution-utils.ts +1 -1
  93. package/src/lib/server/chat-execution/chat-execution.ts +84 -1914
  94. package/src/lib/server/chat-execution/chat-turn-finalization.ts +620 -0
  95. package/src/lib/server/chat-execution/chat-turn-partial-persistence.ts +221 -0
  96. package/src/lib/server/chat-execution/chat-turn-preflight.ts +133 -0
  97. package/src/lib/server/chat-execution/chat-turn-preparation.ts +817 -0
  98. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +296 -0
  99. package/src/lib/server/chat-execution/chat-turn-tool-routing.ts +5 -5
  100. package/src/lib/server/chat-execution/continuation-evaluator.ts +4 -3
  101. package/src/lib/server/chat-execution/continuation-limits.ts +6 -3
  102. package/src/lib/server/chat-execution/message-classifier.test.ts +329 -0
  103. package/src/lib/server/chat-execution/message-classifier.ts +5 -2
  104. package/src/lib/server/chat-execution/post-stream-finalization.ts +5 -2
  105. package/src/lib/server/chat-execution/prompt-builder.ts +22 -1
  106. package/src/lib/server/chat-execution/prompt-sections.ts +55 -13
  107. package/src/lib/server/chat-execution/response-completeness.ts +5 -2
  108. package/src/lib/server/chat-execution/situational-awareness.ts +12 -7
  109. package/src/lib/server/chat-execution/stream-agent-chat.ts +58 -25
  110. package/src/lib/server/chatrooms/chatroom-memory-bridge.ts +6 -3
  111. package/src/lib/server/chatrooms/chatroom-repository.ts +32 -0
  112. package/src/lib/server/connectors/bluebubbles.ts +7 -4
  113. package/src/lib/server/connectors/connector-inbound.ts +16 -13
  114. package/src/lib/server/connectors/connector-lifecycle.ts +11 -8
  115. package/src/lib/server/connectors/connector-outbound.ts +6 -3
  116. package/src/lib/server/connectors/connector-repository.ts +58 -0
  117. package/src/lib/server/connectors/discord.ts +10 -7
  118. package/src/lib/server/connectors/email.ts +17 -14
  119. package/src/lib/server/connectors/googlechat.ts +7 -4
  120. package/src/lib/server/connectors/inbound-audio-transcription.ts +5 -2
  121. package/src/lib/server/connectors/matrix.ts +6 -3
  122. package/src/lib/server/connectors/openclaw.ts +20 -17
  123. package/src/lib/server/connectors/outbox.ts +4 -1
  124. package/src/lib/server/connectors/runtime-state.test.ts +117 -0
  125. package/src/lib/server/connectors/runtime-state.ts +19 -0
  126. package/src/lib/server/connectors/session-consolidation.ts +5 -2
  127. package/src/lib/server/connectors/signal.ts +9 -6
  128. package/src/lib/server/connectors/slack.ts +13 -10
  129. package/src/lib/server/connectors/teams.ts +8 -5
  130. package/src/lib/server/connectors/telegram.ts +15 -12
  131. package/src/lib/server/connectors/whatsapp.ts +32 -29
  132. package/src/lib/server/credentials/credential-repository.ts +7 -0
  133. package/src/lib/server/embeddings.ts +4 -1
  134. package/src/lib/server/gateways/gateway-profile-repository.ts +4 -0
  135. package/src/lib/server/link-understanding.ts +4 -1
  136. package/src/lib/server/memory/memory-abstract.test.ts +59 -0
  137. package/src/lib/server/memory/memory-abstract.ts +59 -0
  138. package/src/lib/server/memory/memory-db.ts +40 -14
  139. package/src/lib/server/missions/mission-repository.ts +74 -0
  140. package/src/lib/server/missions/mission-service/actions.ts +6 -0
  141. package/src/lib/server/missions/mission-service/bindings.ts +9 -0
  142. package/src/lib/server/missions/mission-service/context.ts +4 -0
  143. package/src/lib/server/missions/mission-service/core.ts +2269 -0
  144. package/src/lib/server/missions/mission-service/queries.ts +12 -0
  145. package/src/lib/server/missions/mission-service/recovery.ts +5 -0
  146. package/src/lib/server/missions/mission-service/ticks.ts +9 -0
  147. package/src/lib/server/missions/mission-service.test.ts +9 -2
  148. package/src/lib/server/missions/mission-service.ts +6 -2263
  149. package/src/lib/server/openclaw/gateway.ts +8 -5
  150. package/src/lib/server/persistence/repository-utils.ts +154 -0
  151. package/src/lib/server/persistence/storage-context.ts +51 -0
  152. package/src/lib/server/persistence/transaction.ts +1 -0
  153. package/src/lib/server/project-utils.ts +13 -0
  154. package/src/lib/server/projects/project-repository.ts +36 -0
  155. package/src/lib/server/projects/project-service.ts +79 -0
  156. package/src/lib/server/protocols/protocol-agent-turn.ts +5 -2
  157. package/src/lib/server/protocols/protocol-normalization.test.ts +6 -4
  158. package/src/lib/server/protocols/protocol-run-lifecycle.ts +5 -2
  159. package/src/lib/server/protocols/protocol-step-helpers.ts +4 -1
  160. package/src/lib/server/provider-health.ts +18 -0
  161. package/src/lib/server/query-expansion.ts +4 -1
  162. package/src/lib/server/runtime/alert-dispatch.ts +8 -7
  163. package/src/lib/server/runtime/daemon-policy.ts +1 -1
  164. package/src/lib/server/runtime/daemon-state/core.ts +1570 -0
  165. package/src/lib/server/runtime/daemon-state/health.ts +6 -0
  166. package/src/lib/server/runtime/daemon-state/policy.ts +7 -0
  167. package/src/lib/server/runtime/daemon-state/supervisor.ts +6 -0
  168. package/src/lib/server/runtime/daemon-state.test.ts +48 -0
  169. package/src/lib/server/runtime/daemon-state.ts +3 -1331
  170. package/src/lib/server/runtime/estop-repository.ts +4 -0
  171. package/src/lib/server/runtime/estop.ts +3 -1
  172. package/src/lib/server/runtime/heartbeat-service.test.ts +2 -2
  173. package/src/lib/server/runtime/heartbeat-service.ts +78 -34
  174. package/src/lib/server/runtime/heartbeat-wake.ts +6 -4
  175. package/src/lib/server/runtime/idle-window.ts +6 -3
  176. package/src/lib/server/runtime/network.ts +11 -0
  177. package/src/lib/server/runtime/orchestrator-events.ts +2 -2
  178. package/src/lib/server/runtime/perf.ts +4 -1
  179. package/src/lib/server/runtime/process-manager.ts +7 -4
  180. package/src/lib/server/runtime/queue/claims.ts +4 -0
  181. package/src/lib/server/runtime/queue/core.ts +2079 -0
  182. package/src/lib/server/runtime/queue/execution.ts +7 -0
  183. package/src/lib/server/runtime/queue/followups.ts +4 -0
  184. package/src/lib/server/runtime/queue/queries.ts +12 -0
  185. package/src/lib/server/runtime/queue/recovery.ts +7 -0
  186. package/src/lib/server/runtime/queue-recovery.test.ts +48 -13
  187. package/src/lib/server/runtime/queue-repository.ts +17 -0
  188. package/src/lib/server/runtime/queue.ts +5 -2058
  189. package/src/lib/server/runtime/run-ledger.ts +6 -5
  190. package/src/lib/server/runtime/run-repository.ts +73 -0
  191. package/src/lib/server/runtime/runtime-lock-repository.ts +8 -0
  192. package/src/lib/server/runtime/runtime-settings.ts +1 -1
  193. package/src/lib/server/runtime/runtime-state.ts +99 -0
  194. package/src/lib/server/runtime/scheduler.ts +13 -8
  195. package/src/lib/server/runtime/session-run-manager/cancellation.ts +157 -0
  196. package/src/lib/server/runtime/session-run-manager/drain.ts +246 -0
  197. package/src/lib/server/runtime/session-run-manager/enqueue.ts +287 -0
  198. package/src/lib/server/runtime/session-run-manager/queries.ts +117 -0
  199. package/src/lib/server/runtime/session-run-manager/recovery.ts +238 -0
  200. package/src/lib/server/runtime/session-run-manager/state.ts +441 -0
  201. package/src/lib/server/runtime/session-run-manager/types.ts +74 -0
  202. package/src/lib/server/runtime/session-run-manager.ts +72 -1374
  203. package/src/lib/server/runtime/watch-job-repository.ts +35 -0
  204. package/src/lib/server/runtime/watch-jobs.ts +3 -1
  205. package/src/lib/server/sandbox/bridge-auth-registry.ts +6 -0
  206. package/src/lib/server/sandbox/novnc-auth.ts +10 -0
  207. package/src/lib/server/schedules/schedule-repository.ts +42 -0
  208. package/src/lib/server/session-tools/context.ts +14 -0
  209. package/src/lib/server/session-tools/discovery.ts +9 -6
  210. package/src/lib/server/session-tools/index.ts +3 -1
  211. package/src/lib/server/session-tools/platform.ts +1 -1
  212. package/src/lib/server/session-tools/subagent.ts +23 -2
  213. package/src/lib/server/session-tools/wallet.ts +4 -1
  214. package/src/lib/server/sessions/session-repository.ts +85 -0
  215. package/src/lib/server/settings/settings-repository.ts +25 -0
  216. package/src/lib/server/skills/clawhub-client.ts +4 -1
  217. package/src/lib/server/skills/runtime-skill-resolver.ts +8 -2
  218. package/src/lib/server/skills/skill-discovery.test.ts +2 -2
  219. package/src/lib/server/skills/skill-discovery.ts +2 -2
  220. package/src/lib/server/skills/skill-eligibility.ts +6 -0
  221. package/src/lib/server/skills/skill-repository.ts +14 -0
  222. package/src/lib/server/solana.ts +6 -0
  223. package/src/lib/server/storage-auth.ts +5 -5
  224. package/src/lib/server/storage-normalization.ts +4 -0
  225. package/src/lib/server/storage.ts +32 -32
  226. package/src/lib/server/tasks/task-followups.ts +4 -1
  227. package/src/lib/server/tasks/task-repository.ts +54 -0
  228. package/src/lib/server/tool-loop-detection.ts +8 -3
  229. package/src/lib/server/tool-planning.ts +226 -0
  230. package/src/lib/server/tool-retry.ts +4 -3
  231. package/src/lib/server/usage/usage-repository.ts +30 -0
  232. package/src/lib/server/wallet/wallet-portfolio.ts +29 -0
  233. package/src/lib/server/webhooks/webhook-repository.ts +10 -0
  234. package/src/lib/server/ws-hub.ts +5 -2
  235. package/src/lib/strip-internal-metadata.test.ts +78 -37
  236. package/src/lib/strip-internal-metadata.ts +20 -6
  237. package/src/stores/use-approval-store.ts +7 -1
  238. package/src/stores/use-chat-store.test.ts +54 -0
  239. package/src/stores/use-chat-store.ts +26 -6
  240. package/src/types/index.ts +6 -0
  241. /package/{bundled-skills → skills}/google-workspace/SKILL.md +0 -0
@@ -1,3 +1,4 @@
1
+ import { log } from '@/lib/server/logger'
1
2
  import { Client, GatewayIntentBits, Events, Partials, AttachmentBuilder } from 'discord.js'
2
3
  import fs from 'fs'
3
4
  import path from 'path'
@@ -8,6 +9,8 @@ import { deliverChunkedConnectorText } from './delivery'
8
9
  import { downloadInboundMediaToUpload, inferInboundMediaType } from './media'
9
10
  import { errorMessage } from '@/lib/shared-utils'
10
11
 
12
+ const TAG = 'discord'
13
+
11
14
  function buildDiscordThreadTitle(params: {
12
15
  threadName?: string
13
16
  channelName?: string
@@ -89,7 +92,7 @@ async function hydrateDiscordThreadContext(message: any, inbound: InboundMessage
89
92
  }].filter((entry) => entry.text.trim().length > 0)
90
93
  }
91
94
  } catch (err: unknown) {
92
- console.warn(`[discord] Thread context bootstrap failed: ${errorMessage(err)}`)
95
+ log.warn(TAG, `Thread context bootstrap failed: ${errorMessage(err)}`)
93
96
  }
94
97
  }
95
98
 
@@ -128,7 +131,7 @@ const discord: PlatformConnector = {
128
131
  }
129
132
 
130
133
  client.on(Events.MessageCreate, async (message) => {
131
- console.log(`[discord] Message from ${message.author.username} in ${message.channel.type === 1 ? 'DM' : '#' + ('name' in message.channel ? (message.channel as any).name : message.channelId)}: ${message.content.slice(0, 80)}`)
134
+ log.info(TAG, `Message from ${message.author.username} in ${message.channel.type === 1 ? 'DM' : '#' + ('name' in message.channel ? (message.channel as any).name : message.channelId)}: ${message.content.slice(0, 80)}`)
132
135
  // Ignore bot messages
133
136
  if (message.author.bot) return
134
137
 
@@ -155,7 +158,7 @@ const discord: PlatformConnector = {
155
158
  }
156
159
  } catch (err: unknown) {
157
160
  const errMsg = errorMessage(err)
158
- console.warn(`[discord] Media download failed (${attachment.name || 'file'}):`, errMsg)
161
+ log.warn(TAG, `Media download failed (${attachment.name || 'file'}):`, errMsg)
159
162
  }
160
163
  }
161
164
  media.push({
@@ -215,7 +218,7 @@ const discord: PlatformConnector = {
215
218
  },
216
219
  })
217
220
  } catch (err: any) {
218
- console.error(`[discord] Error handling message:`, err.message)
221
+ log.error(TAG, 'Error handling message:', err.message)
219
222
  try {
220
223
  await message.reply('Sorry, I encountered an error processing your message.')
221
224
  } catch { /* ignore */ }
@@ -223,7 +226,7 @@ const discord: PlatformConnector = {
223
226
  })
224
227
 
225
228
  await client.login(botToken)
226
- console.log(`[discord] Bot logged in as ${client.user?.tag}`)
229
+ log.info(TAG, `Bot logged in as ${client.user?.tag}`)
227
230
 
228
231
  const instance: ConnectorInstance = {
229
232
  connector,
@@ -284,7 +287,7 @@ const discord: PlatformConnector = {
284
287
  },
285
288
  async stop() {
286
289
  client.destroy()
287
- console.log(`[discord] Bot disconnected`)
290
+ log.info(TAG, 'Bot disconnected')
288
291
  },
289
292
  }
290
293
 
@@ -293,7 +296,7 @@ const discord: PlatformConnector = {
293
296
  instance.onCrash?.('Discord session invalidated')
294
297
  })
295
298
  client.on('shardError', (error) => {
296
- console.error(`[discord] Shard error:`, error.message)
299
+ log.error(TAG, 'Shard error:', error.message)
297
300
  })
298
301
 
299
302
  return instance
@@ -5,6 +5,9 @@ import type { Connector } from '@/types'
5
5
  import type { PlatformConnector, ConnectorInstance, InboundMessage } from './types'
6
6
  import { resolveConnectorIngressReply } from './ingress-delivery'
7
7
  import { errorMessage } from '@/lib/shared-utils'
8
+ import { log } from '@/lib/server/logger'
9
+
10
+ const TAG = 'email'
8
11
 
9
12
  interface EmailConfig {
10
13
  imapHost: string
@@ -79,7 +82,7 @@ const email: PlatformConnector = {
79
82
  try {
80
83
  await imap.connect()
81
84
  connected = true
82
- console.log(`[email] IMAP connected to ${config.imapHost}`)
85
+ log.info(TAG, `IMAP connected to ${config.imapHost}`)
83
86
 
84
87
  // Get the current highest UID as highwater mark (don't process old messages)
85
88
  const lock = await imap.getMailboxLock(folder)
@@ -87,14 +90,14 @@ const email: PlatformConnector = {
87
90
  const status = await imap.status(folder, { uidNext: true })
88
91
  // uidNext is the next UID that will be assigned; current highest is uidNext - 1
89
92
  highwaterUid = typeof status.uidNext === 'number' ? status.uidNext - 1 : 0
90
- console.log(`[email] Initial highwater UID: ${highwaterUid} in ${folder}`)
93
+ log.info(TAG, `Initial highwater UID: ${highwaterUid} in ${folder}`)
91
94
  } finally {
92
95
  lock.release()
93
96
  }
94
97
  } catch (err: unknown) {
95
98
  connected = false
96
99
  const msg = errorMessage(err)
97
- console.error(`[email] IMAP connection failed: ${msg}`)
100
+ log.error(TAG, `IMAP connection failed: ${msg}`)
98
101
  throw err
99
102
  }
100
103
  }
@@ -107,7 +110,7 @@ const email: PlatformConnector = {
107
110
  lock = await imap.getMailboxLock(folder)
108
111
  } catch (err: unknown) {
109
112
  const msg = errorMessage(err)
110
- console.error(`[email] Failed to acquire mailbox lock: ${msg}`)
113
+ log.error(TAG, `Failed to acquire mailbox lock: ${msg}`)
111
114
  connected = false
112
115
  return
113
116
  }
@@ -127,7 +130,7 @@ const email: PlatformConnector = {
127
130
  await processMessage(msg)
128
131
  } catch (err: unknown) {
129
132
  const errMsg = errorMessage(err)
130
- console.error(`[email] Error processing message UID ${msg.uid}: ${errMsg}`)
133
+ log.error(TAG, `Error processing message UID ${msg.uid}: ${errMsg}`)
131
134
  }
132
135
  if (msg.uid > highwaterUid) {
133
136
  highwaterUid = msg.uid
@@ -137,7 +140,7 @@ const email: PlatformConnector = {
137
140
  const errMsg = errorMessage(err)
138
141
  // A fetch on an empty range can throw; that's normal
139
142
  if (!errMsg.includes('Nothing to fetch')) {
140
- console.error(`[email] Poll error: ${errMsg}`)
143
+ log.error(TAG, `Poll error: ${errMsg}`)
141
144
  }
142
145
  } finally {
143
146
  lock.release()
@@ -154,7 +157,7 @@ const email: PlatformConnector = {
154
157
 
155
158
  // Filter by subject prefix if configured
156
159
  if (config.subjectPrefix && !subject.startsWith(config.subjectPrefix)) {
157
- console.log(`[email] Skipping message from ${fromAddr} — subject "${subject}" doesn't match prefix "${config.subjectPrefix}"`)
160
+ log.info(TAG, `Skipping message from ${fromAddr} — subject "${subject}" doesn't match prefix "${config.subjectPrefix}"`)
158
161
  return
159
162
  }
160
163
 
@@ -166,11 +169,11 @@ const email: PlatformConnector = {
166
169
  }
167
170
 
168
171
  if (!bodyText.trim()) {
169
- console.log(`[email] Skipping empty message from ${fromAddr}`)
172
+ log.info(TAG, `Skipping empty message from ${fromAddr}`)
170
173
  return
171
174
  }
172
175
 
173
- console.log(`[email] New message from ${fromName} <${fromAddr}>: ${subject}`)
176
+ log.info(TAG, `New message from ${fromName} <${fromAddr}>: ${subject}`)
174
177
 
175
178
  // Use the sender's email as channelId
176
179
  const channelId = fromAddr
@@ -199,7 +202,7 @@ const email: PlatformConnector = {
199
202
  await sendReply(channelId, reply.visibleText)
200
203
  } catch (err: unknown) {
201
204
  const errMsg = errorMessage(err)
202
- console.error(`[email] Error handling message from ${fromAddr}: ${errMsg}`)
205
+ log.error(TAG, `Error handling message from ${fromAddr}: ${errMsg}`)
203
206
  }
204
207
  }
205
208
 
@@ -222,7 +225,7 @@ const email: PlatformConnector = {
222
225
  }
223
226
 
224
227
  await smtp.sendMail(mailOptions)
225
- console.log(`[email] Reply sent to ${to}`)
228
+ log.info(TAG, `Reply sent to ${to}`)
226
229
  }
227
230
 
228
231
  // Connect and start polling
@@ -231,11 +234,11 @@ const email: PlatformConnector = {
231
234
  pollTimer = setInterval(() => {
232
235
  pollForNewMessages().catch((err: unknown) => {
233
236
  const msg = errorMessage(err)
234
- console.error(`[email] Poll interval error: ${msg}`)
237
+ log.error(TAG, `Poll interval error: ${msg}`)
235
238
  })
236
239
  }, pollMs)
237
240
 
238
- console.log(`[email] Connector started — polling every ${config.pollIntervalSec || 60}s`)
241
+ log.info(TAG, `Connector started — polling every ${config.pollIntervalSec || 60}s`)
239
242
 
240
243
  return {
241
244
  connector,
@@ -259,7 +262,7 @@ const email: PlatformConnector = {
259
262
  // Connection may already be closed
260
263
  }
261
264
  connected = false
262
- console.log(`[email] Connector stopped`)
265
+ log.info(TAG, `Connector stopped`)
263
266
  },
264
267
  }
265
268
  },
@@ -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
- console.log(`[googlechat] Bot authenticated via service account`)
35
+ log.info(TAG, 'Bot authenticated via service account')
33
36
  if (allowedSpaces) {
34
- console.log(`[googlechat] Filtering to spaces: ${allowedSpaces.join(', ')}`)
37
+ log.info(TAG, `Filtering to spaces: ${allowedSpaces.join(', ')}`)
35
38
  }
36
- console.log(`[googlechat] Inbound webhook endpoint: /api/connectors/${connector.id}/webhook`)
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
- console.log(`[googlechat] Bot disconnected`)
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
- console.log(`[connector] Inbound audio transcribed via ${attempt.provider}: ${path.basename(localPath)}`)
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
- console.warn(`[connector] Inbound audio transcription failed via ${attempt.provider}: ${reason}`)
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
- console.error(`[matrix] Error handling message:`, err.message)
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
- console.log(`[matrix] Bot connected to ${homeserverUrl}`)
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
- console.log(`[matrix] Bot disconnected`)
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
- console.warn(`[openclaw] Failed to inline media URL, falling back to link send: ${getErrorMessage(err)}`)
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
- console.error(
709
- `[openclaw] Tick missed (${delta}ms > ${toleranceMs}ms), forcing reconnect`,
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
- console.warn(`[openclaw] chat.history poll failed for "${sessionKey}": ${message}`)
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
- console.log(`[openclaw] Reconnecting in ${delay}ms (attempt ${reconnectAttempt})`)
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
- console.warn('[openclaw] Clearing stale stored device token after mismatch')
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
- console.log(`[openclaw] Connected + authenticated (${wsUrl})`)
924
+ log.info(TAG, `Connected + authenticated (${wsUrl})`)
922
925
  })
923
926
  .catch((err: unknown) => {
924
927
  clearConnectHelloTimer()
925
- console.error(`[openclaw] Connect handshake failed: ${getErrorMessage(err)}`)
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
- console.error('[openclaw] Error routing inbound chat event:', message)
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
- console.warn('[openclaw] chat.history is unavailable; disabling history polling fallback')
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
- console.log(`[openclaw] Connecting to ${wsUrl}`)
1065
+ log.info(TAG, `Connecting to ${wsUrl}`)
1063
1066
  ws = new WebSocket(wsUrl)
1064
1067
 
1065
1068
  ws.onopen = () => {
1066
- console.log(`[openclaw] Socket open: ${wsUrl}`)
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
- console.warn(`[openclaw] Connect handshake timed out after ${CONNECT_HELLO_TIMEOUT_MS}ms`)
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
- console.warn('[openclaw] Ignoring malformed gateway frame')
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
- console.log(`[openclaw] Disconnected (code=${event.code}, reason=${reason})`)
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
- console.error('[openclaw] WebSocket error')
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
- console.log('[openclaw] Connector stopped')
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
- console.warn(`[connector-outbox] Worker tick failed: ${errorMessage(err)}`)
157
+ log.warn(TAG, `Worker tick failed: ${errorMessage(err)}`)
155
158
  })
156
159
  }, Math.max(0, delayMs))
157
160
  outboxState.timer.unref?.()
@@ -0,0 +1,117 @@
1
+ import assert from 'node:assert/strict'
2
+ import { describe, it, before, after } from 'node:test'
3
+
4
+ const originalEnv = {
5
+ SWARMCLAW_BUILD_MODE: process.env.SWARMCLAW_BUILD_MODE,
6
+ }
7
+
8
+ let mod: typeof import('@/lib/server/connectors/runtime-state')
9
+
10
+ before(async () => {
11
+ process.env.SWARMCLAW_BUILD_MODE = '1'
12
+ mod = await import('@/lib/server/connectors/runtime-state')
13
+ })
14
+
15
+ after(() => {
16
+ if (originalEnv.SWARMCLAW_BUILD_MODE === undefined) delete process.env.SWARMCLAW_BUILD_MODE
17
+ else process.env.SWARMCLAW_BUILD_MODE = originalEnv.SWARMCLAW_BUILD_MODE
18
+ })
19
+
20
+ // ---------------------------------------------------------------------------
21
+ // getConnectorRuntimeState
22
+ // ---------------------------------------------------------------------------
23
+
24
+ describe('getConnectorRuntimeState', () => {
25
+ it('returns object with all expected Map keys', () => {
26
+ const state = mod.getConnectorRuntimeState()
27
+ assert.ok(state.running instanceof Map)
28
+ assert.ok(state.lastInboundChannelByConnector instanceof Map)
29
+ assert.ok(state.lastInboundTimeByConnector instanceof Map)
30
+ assert.ok(state.locks instanceof Map)
31
+ assert.ok(state.generationCounter instanceof Map)
32
+ assert.ok(state.scheduledFollowups instanceof Map)
33
+ assert.ok(state.recentInboundByKey instanceof Map)
34
+ assert.ok(state.pendingInboundDebounce instanceof Map)
35
+ assert.ok(state.scheduledFollowupByDedupe instanceof Map)
36
+ assert.ok(state.reconnectStates instanceof Map)
37
+ assert.ok(state.recentOutbound instanceof Map)
38
+ assert.ok(state.routeMessageHandlerRef)
39
+ assert.equal(typeof state.routeMessageHandlerRef.current, 'function')
40
+ })
41
+
42
+ it('returns the same instance on repeated calls (singleton)', () => {
43
+ const a = mod.getConnectorRuntimeState()
44
+ const b = mod.getConnectorRuntimeState()
45
+ assert.equal(a, b)
46
+ })
47
+ })
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // pruneConnectorTrackingState
51
+ // ---------------------------------------------------------------------------
52
+
53
+ describe('pruneConnectorTrackingState', () => {
54
+ it('removes stale entries and retains live ones', () => {
55
+ const state = mod.getConnectorRuntimeState()
56
+
57
+ // Seed tracking maps with stale + live IDs
58
+ state.lastInboundChannelByConnector.set('live-1', 'channel-a')
59
+ state.lastInboundChannelByConnector.set('stale-1', 'channel-b')
60
+ state.lastInboundTimeByConnector.set('live-1', Date.now())
61
+ state.lastInboundTimeByConnector.set('stale-2', Date.now())
62
+ state.generationCounter.set('live-1', 5)
63
+ state.generationCounter.set('stale-1', 3)
64
+
65
+ const liveIds = new Set(['live-1'])
66
+ const removed = mod.pruneConnectorTrackingState(liveIds)
67
+
68
+ // Verify stale entries are gone
69
+ assert.equal(state.lastInboundChannelByConnector.has('stale-1'), false)
70
+ assert.equal(state.lastInboundTimeByConnector.has('stale-2'), false)
71
+ assert.equal(state.generationCounter.has('stale-1'), false)
72
+
73
+ // Verify live entries are retained
74
+ assert.equal(state.lastInboundChannelByConnector.get('live-1'), 'channel-a')
75
+ assert.ok(state.lastInboundTimeByConnector.has('live-1'))
76
+ assert.equal(state.generationCounter.get('live-1'), 5)
77
+
78
+ // Removed count: stale-1 from channel + stale-2 from time + stale-1 from gen = 3
79
+ assert.equal(removed, 3)
80
+ })
81
+
82
+ it('removes all entries when liveIds is empty', () => {
83
+ const state = mod.getConnectorRuntimeState()
84
+
85
+ state.lastInboundChannelByConnector.set('a', 'ch')
86
+ state.lastInboundTimeByConnector.set('a', 1)
87
+ state.generationCounter.set('a', 1)
88
+
89
+ const removed = mod.pruneConnectorTrackingState(new Set())
90
+
91
+ assert.equal(state.lastInboundChannelByConnector.size, 0)
92
+ assert.equal(state.lastInboundTimeByConnector.size, 0)
93
+ assert.equal(state.generationCounter.size, 0)
94
+ assert.ok(removed >= 3)
95
+ })
96
+
97
+ it('returns 0 when all IDs are live', () => {
98
+ const state = mod.getConnectorRuntimeState()
99
+
100
+ // Clear first
101
+ state.lastInboundChannelByConnector.clear()
102
+ state.lastInboundTimeByConnector.clear()
103
+ state.generationCounter.clear()
104
+
105
+ state.lastInboundChannelByConnector.set('x', 'ch')
106
+ state.lastInboundTimeByConnector.set('x', 1)
107
+ state.generationCounter.set('x', 1)
108
+
109
+ const removed = mod.pruneConnectorTrackingState(new Set(['x']))
110
+ assert.equal(removed, 0)
111
+
112
+ // Entries still present
113
+ assert.equal(state.lastInboundChannelByConnector.has('x'), true)
114
+ assert.equal(state.lastInboundTimeByConnector.has('x'), true)
115
+ assert.equal(state.generationCounter.has('x'), true)
116
+ })
117
+ })
@@ -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
- console.log(`[session-consolidation] Backfilled allKnownPeerIds on ${migrated} sessions`)
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
- console.log(`[session-consolidation] Pruned ${removedMessages} mirrored connector message(s) from ${cleanedSessions} main session(s)`)
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
  }