@xmanrui/dsh-im 4.22.0 → 4.24.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.
Files changed (148) hide show
  1. package/README.en.md +5 -4
  2. package/README.md +5 -4
  3. package/lib/client.js +2584 -1153
  4. package/lib/index.js +290 -298
  5. package/package.json +14 -2
  6. package/plugin-src/client/channel-card-meta.js +2 -0
  7. package/plugin-src/client/channel-logos.js +11 -0
  8. package/plugin-src/client/channels/dingtalk/api.js +5 -1
  9. package/plugin-src/client/channels/dingtalk/index.js +15 -13
  10. package/plugin-src/client/channels/email/api.js +52 -0
  11. package/plugin-src/client/channels/email/index.js +728 -0
  12. package/plugin-src/client/channels/email/styles.js +51 -0
  13. package/plugin-src/client/channels/feishu/api.js +4 -2
  14. package/plugin-src/client/channels/feishu/index.js +23 -7
  15. package/plugin-src/client/channels/imessage/index.js +2 -1
  16. package/plugin-src/client/channels/office/api.js +2 -0
  17. package/plugin-src/client/channels/office/index.js +6 -5
  18. package/plugin-src/client/channels/qq/api.js +7 -0
  19. package/plugin-src/client/channels/qq/index.js +16 -3
  20. package/plugin-src/client/channels/shared/token-api.js +8 -1
  21. package/plugin-src/client/channels/shared/token-channel.js +28 -3
  22. package/plugin-src/client/channels/slack/index.js +2 -1
  23. package/plugin-src/client/channels/telegram/index.js +3 -0
  24. package/plugin-src/client/channels/telegram/styles.js +12 -0
  25. package/plugin-src/client/channels/telegram/thinking-traces.js +25 -0
  26. package/plugin-src/client/channels/wecom/api.js +7 -1
  27. package/plugin-src/client/channels/wecom/index.js +16 -3
  28. package/plugin-src/client/channels/wecom-app/api.js +6 -1
  29. package/plugin-src/client/channels/wecom-app/index.js +16 -3
  30. package/plugin-src/client/channels/weixin/api.js +2 -1
  31. package/plugin-src/client/channels/weixin/connection-error.js +1 -71
  32. package/plugin-src/client/channels/whatsapp/api.js +5 -0
  33. package/plugin-src/client/channels/whatsapp/index.js +16 -3
  34. package/plugin-src/client/connection-error.js +87 -0
  35. package/plugin-src/client/global-settings.js +81 -1
  36. package/plugin-src/client/i18n.js +69 -1
  37. package/plugin-src/client/index.js +40 -4
  38. package/plugin-src/client/last-message-error.js +2 -1
  39. package/plugin-src/client/styles.js +8 -2
  40. package/plugin-src/host/build.mjs +4 -0
  41. package/plugin-src/host/channels/dingtalk/production.mjs +2 -0
  42. package/plugin-src/host/channels/dingtalk/rpc.mjs +7 -4
  43. package/plugin-src/host/channels/email/availability.mjs +60 -0
  44. package/plugin-src/host/channels/email/index.mjs +36 -0
  45. package/plugin-src/host/channels/email/production.mjs +58 -0
  46. package/plugin-src/host/channels/email/rpc.mjs +163 -0
  47. package/plugin-src/host/channels/feishu/production.mjs +3 -0
  48. package/plugin-src/host/channels/feishu/rpc.mjs +7 -4
  49. package/plugin-src/host/channels/imessage/rpc.mjs +13 -6
  50. package/plugin-src/host/channels/office/rpc.mjs +3 -1
  51. package/plugin-src/host/channels/qq/production.mjs +2 -0
  52. package/plugin-src/host/channels/qq/rpc.mjs +5 -3
  53. package/plugin-src/host/channels/shared/production.mjs +52 -5
  54. package/plugin-src/host/channels/shared/rpc.mjs +14 -3
  55. package/plugin-src/host/channels/shared/startup-error.mjs +4 -3
  56. package/plugin-src/host/channels/shared/startup.mjs +11 -6
  57. package/plugin-src/host/channels/shared/thinking-traces-rpc.mjs +11 -0
  58. package/plugin-src/host/channels/slack/production.mjs +2 -0
  59. package/plugin-src/host/channels/slack/rpc.mjs +5 -3
  60. package/plugin-src/host/channels/wecom/production.mjs +2 -0
  61. package/plugin-src/host/channels/wecom/rpc.mjs +5 -3
  62. package/plugin-src/host/channels/wecom-app/production.mjs +2 -0
  63. package/plugin-src/host/channels/wecom-app/rpc.mjs +5 -3
  64. package/plugin-src/host/channels/weixin/production.mjs +2 -0
  65. package/plugin-src/host/channels/whatsapp/production.mjs +2 -0
  66. package/plugin-src/host/channels/whatsapp/rpc.mjs +5 -3
  67. package/plugin-src/host/delivery-adapter.mjs +11 -0
  68. package/plugin-src/host/image-input-rpc.mjs +24 -0
  69. package/plugin-src/host/inbound-ttl-rpc.mjs +6 -1
  70. package/plugin-src/host/index.mjs +3 -0
  71. package/plugin-src/host/modern-harness-api.mjs +7 -2
  72. package/plugin-src/management-rpc.mjs +12 -2
  73. package/scripts/verify-package.mjs +13 -5
  74. package/src/channels/dingtalk/connection-error.mjs +5 -12
  75. package/src/channels/dingtalk/device-auth.mjs +4 -1
  76. package/src/channels/dingtalk/dingtalk-bridge.mjs +6 -4
  77. package/src/channels/dingtalk/dingtalk-controller.mjs +60 -45
  78. package/src/channels/dingtalk/dingtalk-runtime.mjs +20 -4
  79. package/src/channels/discord/discord-api.mjs +3 -3
  80. package/src/channels/discord/discord-runtime.mjs +18 -12
  81. package/src/channels/email/config-store.mjs +194 -0
  82. package/src/channels/email/email-api.mjs +20 -0
  83. package/src/channels/email/email-bridge.mjs +17 -0
  84. package/src/channels/email/email-controller.mjs +824 -0
  85. package/src/channels/email/email-runtime.mjs +699 -0
  86. package/src/channels/email/harness-client.mjs +7 -0
  87. package/src/channels/email/mail-format.mjs +91 -0
  88. package/src/channels/email/state-store.mjs +176 -0
  89. package/src/channels/email/transport.mjs +64 -0
  90. package/src/channels/email/transports/agent-mail.mjs +604 -0
  91. package/src/channels/email/transports/agently-cli.mjs +288 -0
  92. package/src/channels/email/transports/imap-smtp.mjs +165 -0
  93. package/src/channels/feishu/bridge.mjs +39 -11
  94. package/src/channels/feishu/feishu-channel.mjs +35 -0
  95. package/src/channels/feishu/feishu-runtime.mjs +15 -8
  96. package/src/channels/feishu/group-message-permission-manager.mjs +2 -1
  97. package/src/channels/feishu/live-cot.mjs +260 -0
  98. package/src/channels/feishu/message-utils.mjs +3 -2
  99. package/src/channels/feishu/multi-bot-controller.mjs +56 -47
  100. package/src/channels/feishu/registration-manager.mjs +8 -4
  101. package/src/channels/feishu/repair-manager.mjs +2 -1
  102. package/src/channels/feishu/slash-command-registry.mjs +17 -0
  103. package/src/channels/feishu/step-push-mode.mjs +10 -4
  104. package/src/channels/imessage/runtime.mjs +9 -5
  105. package/src/channels/office/office-controller.mjs +23 -12
  106. package/src/channels/office/office-runtime.mjs +6 -3
  107. package/src/channels/office/office-transport.mjs +1 -0
  108. package/src/channels/qq/qq-bridge.mjs +7 -5
  109. package/src/channels/qq/qq-controller.mjs +56 -42
  110. package/src/channels/qq/qq-runtime.mjs +16 -11
  111. package/src/channels/shared/bot-workspace-store.mjs +29 -10
  112. package/src/channels/shared/connection-error.mjs +191 -0
  113. package/src/channels/shared/connection-test.mjs +3 -1
  114. package/src/channels/shared/conversation-state-store.mjs +46 -1
  115. package/src/channels/shared/diagnostic-details.mjs +95 -0
  116. package/src/channels/shared/harness-client.mjs +287 -57
  117. package/src/channels/shared/i18n-en/diagnostics.mjs +55 -0
  118. package/src/channels/shared/i18n-en/email.mjs +25 -0
  119. package/src/channels/shared/i18n-en/image-input.mjs +17 -0
  120. package/src/channels/shared/i18n-en/shared-a.mjs +1 -0
  121. package/src/channels/shared/i18n-en/shared-c.mjs +1 -1
  122. package/src/channels/shared/i18n-en.mjs +6 -0
  123. package/src/channels/shared/image-input-policy.mjs +35 -0
  124. package/src/channels/shared/image-input-settings-store.mjs +60 -0
  125. package/src/channels/shared/image-input.mjs +124 -0
  126. package/src/channels/shared/image-prompt.mjs +15 -7
  127. package/src/channels/shared/message-failure.mjs +12 -0
  128. package/src/channels/shared/semantic/reply-reference.mjs +3 -3
  129. package/src/channels/shared/text-harness-bridge.mjs +150 -25
  130. package/src/channels/shared/token-bot-controller.mjs +52 -47
  131. package/src/channels/slack/slack-api.mjs +3 -3
  132. package/src/channels/slack/slack-controller.mjs +59 -51
  133. package/src/channels/slack/slack-runtime.mjs +18 -12
  134. package/src/channels/telegram/config-store.mjs +4 -1
  135. package/src/channels/telegram/telegram-api.mjs +2 -2
  136. package/src/channels/telegram/telegram-controller.mjs +13 -1
  137. package/src/channels/telegram/telegram-runtime.mjs +215 -14
  138. package/src/channels/wecom/wecom-bridge.mjs +15 -6
  139. package/src/channels/wecom/wecom-controller.mjs +51 -37
  140. package/src/channels/wecom/wecom-runtime.mjs +10 -6
  141. package/src/channels/wecom-app/wecom-app-bridge.mjs +7 -5
  142. package/src/channels/wecom-app/wecom-app-controller.mjs +50 -37
  143. package/src/channels/wecom-app/wecom-app-runtime.mjs +2 -1
  144. package/src/channels/weixin/connection-error.mjs +26 -92
  145. package/src/channels/weixin/diagnostic-details.mjs +1 -63
  146. package/src/channels/weixin/weixin-bridge.mjs +7 -5
  147. package/src/channels/whatsapp/whatsapp-controller.mjs +34 -17
  148. package/src/channels/whatsapp/whatsapp-runtime.mjs +35 -8
@@ -1,3 +1,4 @@
1
+ import { extractConnectionEvidence, atConnectionStage, createConnectionDiagnostics } from '../shared/connection-error.mjs';
1
2
  import { randomUUID } from 'node:crypto';
2
3
 
3
4
  import { connectionTestMessage } from '../shared/connection-test.mjs';
@@ -39,6 +40,7 @@ export class WhatsappController {
39
40
  #deleteAuth;
40
41
  #deleteState;
41
42
  #logger;
43
+ #diagnostics;
42
44
  #runtimes = new Map();
43
45
  #errors = new Map();
44
46
  #attempts = new Map();
@@ -71,6 +73,14 @@ export class WhatsappController {
71
73
  this.#deleteAuth = deleteAuth;
72
74
  this.#deleteState = deleteState;
73
75
  this.#logger = logger;
76
+ this.#diagnostics = createConnectionDiagnostics({ channel: 'whatsapp', logger });
77
+ }
78
+
79
+ get diagnostics() { return this.#diagnostics; }
80
+
81
+ #failure(error, code, message) {
82
+ const stage = code.startsWith('qr-') ? 'qr.begin' : code === 'activation-failed' ? 'activation' : 'connection.start';
83
+ return this.#diagnostics.report(error, { reuse: true, stage, publicError: { code, message } }).publicError;
74
84
  }
75
85
 
76
86
  async initialize() {
@@ -82,7 +92,7 @@ export class WhatsappController {
82
92
  await this.#startRuntime(config);
83
93
  this.#errors.delete(config.botId);
84
94
  } catch (error) {
85
- this.#errors.set(config.botId, safeError(
95
+ this.#errors.set(config.botId, this.#failure(error,
86
96
  error?.code === 'relink-required' ? 'relink-required' : 'connection-failed',
87
97
  error?.code === 'relink-required'
88
98
  ? t('WhatsApp 关联设备已失效,请移除后重新扫码。')
@@ -191,7 +201,7 @@ export class WhatsappController {
191
201
  await this.#startRuntime(config);
192
202
  this.#errors.delete(botId);
193
203
  } catch (error) {
194
- this.#errors.set(botId, safeError(
204
+ this.#errors.set(botId, this.#failure(error,
195
205
  error?.code === 'relink-required' ? 'relink-required' : 'connection-failed',
196
206
  error?.code === 'relink-required'
197
207
  ? t('WhatsApp 关联设备已失效,请移除后重新扫码。')
@@ -245,7 +255,7 @@ export class WhatsappController {
245
255
  if (this.#closed) throw new Error('WhatsApp controller is closed');
246
256
  const config = this.#configStore.get(botId);
247
257
  if (!config) throw new Error('Unknown WhatsApp bot');
248
- const saved = await this.#configStore.save({ ...config, ...accessPolicy });
258
+ const saved = await atConnectionStage('account.save', () => this.#configStore.save({ ...config, ...accessPolicy }), 'account-config');
249
259
  this.#runtimes.get(botId)?.setAccessPolicy?.(saved);
250
260
  this.#touch();
251
261
  });
@@ -253,24 +263,31 @@ export class WhatsappController {
253
263
  }
254
264
 
255
265
  async deleteBot(botId) {
266
+ const warnings = [];
256
267
  const config = this.#configStore.get(botId);
257
268
  if (!config) throw new Error('Unknown WhatsApp bot');
258
269
  await this.#withBotTransition(botId, async () => {
259
270
  await this.#stopRuntime(botId);
260
271
  try {
261
- await this.#configStore.remove(botId);
272
+ await atConnectionStage('account.remove', () => this.#configStore.remove(botId), 'account-config');
262
273
  } catch (error) {
263
- await this.#startRuntime(config).catch(() => undefined);
264
- throw error;
274
+ if (!this.#configStore.get(botId)) {
275
+ warnings.push(this.#diagnostics.report(error, { operation: 'bot.delete', stage: 'workspace.cleanup', warning: true,
276
+ publicError: { code: 'workspace-cleanup-failed', message: '账号已移除,但本地状态清理失败。' } }).publicError);
277
+ } else {
278
+ await this.#startRuntime(config).catch(() => undefined);
279
+ throw error;
280
+ }
265
281
  }
266
- await Promise.allSettled([
282
+ const cleanup = await Promise.allSettled([
267
283
  this.#deleteAuth(config.authDirectory),
268
284
  this.#deleteState({ botId, config }),
269
285
  ]);
286
+ for (const result of cleanup) if (result.status === 'rejected') warnings.push(this.#diagnostics.report(result.reason, { operation: 'bot.delete', stage: 'state.cleanup', warning: true, publicError: { code: 'cleanup-failed', message: '账号已移除,但本地状态清理失败。' } }).publicError);
270
287
  this.#errors.delete(botId);
271
288
  this.#touch();
272
289
  });
273
- return this.status();
290
+ return { ...this.status(), ...(warnings.length ? { warnings } : {}) };
274
291
  }
275
292
 
276
293
  status() {
@@ -302,7 +319,7 @@ export class WhatsappController {
302
319
  },
303
320
  lastMessageError: publicMessageFailure(runtimeStatus?.lastMessageError),
304
321
  accessPolicy: normalizeWhatsappAccessPolicy(config),
305
- error: structuredClone(this.#errors.get(config.botId) ?? null),
322
+ error: structuredClone(runtimeStatus?.error ?? this.#errors.get(config.botId) ?? null),
306
323
  };
307
324
  });
308
325
  const connectedCount = bots.filter((bot) => bot.connected).length;
@@ -351,13 +368,13 @@ export class WhatsappController {
351
368
  };
352
369
  try {
353
370
  if (record.controller.signal.aborted || this.#closed) throw Object.assign(new Error(), { name: 'AbortError' });
354
- await this.#configStore.save(config);
371
+ await atConnectionStage('account.save', () => this.#configStore.save(config), 'account-config');
355
372
  if (record.controller.signal.aborted || this.#closed) throw Object.assign(new Error(), { name: 'AbortError' });
356
373
  try {
357
374
  await this.#startRuntime(config);
358
375
  this.#errors.delete(botId);
359
376
  } catch (error) {
360
- this.#errors.set(botId, safeError('connection-failed', t('WhatsApp 已绑定,消息连接暂未就绪。')));
377
+ this.#errors.set(botId, this.#failure(error, 'connection-failed', t('WhatsApp 已绑定,消息连接暂未就绪。')));
361
378
  this.#logger.warn?.(`[dsh-im:whatsapp] bot ${botId} did not reconnect after QR binding`);
362
379
  }
363
380
  if (previous?.authDirectory && previous.authDirectory !== config.authDirectory) {
@@ -368,12 +385,12 @@ export class WhatsappController {
368
385
  } catch (error) {
369
386
  if (record.controller.signal.aborted || this.#closed || error?.name === 'AbortError') {
370
387
  await this.#stopRuntime(botId);
371
- if (previous) await this.#configStore.save(previous).catch(() => undefined);
388
+ if (previous) await atConnectionStage('account.save', () => this.#configStore.save(previous), 'account-config').catch(() => undefined);
372
389
  else {
373
- const removed = await this.#configStore.remove(botId).catch(() => null);
390
+ const removed = await atConnectionStage('account.remove', () => this.#configStore.remove(botId), 'account-config').catch(() => null);
374
391
  if (removed) {
375
392
  await this.#deleteState({ botId, config }).catch((cleanupError) => {
376
- this.#logger.warn?.('[dsh-im:whatsapp] cancelled bot state cleanup failed:', cleanupError);
393
+ this.#logger.warn?.('[dsh-im:whatsapp] cancelled bot state cleanup failed:', extractConnectionEvidence(cleanupError).details);
377
394
  });
378
395
  }
379
396
  }
@@ -384,7 +401,7 @@ export class WhatsappController {
384
401
  } else {
385
402
  await this.#deleteAuth(record.authDirectory).catch(() => undefined);
386
403
  record.state = 'failed';
387
- record.error = safeError('activation-failed', t('WhatsApp 已扫码,但无法保存关联设备。'));
404
+ record.error = this.#failure(error, 'activation-failed', t('WhatsApp 已扫码,但无法保存关联设备。'));
388
405
  this.#logger.error?.('[dsh-im:whatsapp] unable to persist linked-device session');
389
406
  }
390
407
  } finally {
@@ -413,11 +430,11 @@ export class WhatsappController {
413
430
  if (this.#closed) throw new Error('WhatsApp controller is closed');
414
431
  await this.#stopRuntime(config.botId);
415
432
  if (this.#closed) throw new Error('WhatsApp controller is closed');
416
- const runtime = await this.#createRuntime({
433
+ const runtime = await atConnectionStage('runtime.prepare', () => this.#createRuntime({
417
434
  botId: config.botId,
418
435
  config,
419
436
  authDir: this.#authPath(config.authDirectory),
420
- });
437
+ }));
421
438
  if (!runtime || typeof runtime.start !== 'function' || typeof runtime.stop !== 'function') {
422
439
  throw new TypeError('createRuntime returned an invalid WhatsApp runtime');
423
440
  }
@@ -1,9 +1,11 @@
1
+ import { createConnectionDiagnostics, atConnectionStage } from '../shared/connection-error.mjs';
1
2
  import { createHash, randomBytes } from 'node:crypto';
2
3
 
3
4
  import {
4
5
  areJidsSameUser,
5
6
  downloadMediaMessage,
6
7
  jidDecode,
8
+ jidNormalizedUser,
7
9
  normalizeMessageContent,
8
10
  } from '@whiskeysockets/baileys';
9
11
 
@@ -14,6 +16,7 @@ import { trackOutboundArtifactProviderPromise } from '../shared/semantic/artifac
14
16
  import { createWhatsappBridgeStatus, WhatsappHarnessBridge } from './whatsapp-bridge.mjs';
15
17
  import {
16
18
  WHATSAPP_ACCESS_MODES,
19
+ normalizeWhatsappAccountJid,
17
20
  } from './config-store.mjs';
18
21
  import { createWhatsappWebSession } from './whatsapp-web-session.mjs';
19
22
 
@@ -289,8 +292,22 @@ export function createWhatsappMediaDownloader({
289
292
  });
290
293
  }
291
294
 
295
+ function whatsappAccountMatcher(accountJid, aliases) {
296
+ // PN and LID are separate identities unless the linked account supplies both.
297
+ const accountJids = new Set(
298
+ [accountJid, ...(Array.isArray(aliases) ? aliases : [])]
299
+ .map((jid) => normalizeWhatsappAccountJid(jidNormalizedUser(jid)))
300
+ .filter(Boolean),
301
+ );
302
+ return (jid) => {
303
+ const normalized = normalizeWhatsappAccountJid(jidNormalizedUser(jid));
304
+ return normalized !== null && accountJids.has(normalized);
305
+ };
306
+ }
307
+
292
308
  export function normalizeWhatsappMessage(message, accountJid, {
293
309
  download = downloadMediaMessage,
310
+ accountAliases = [],
294
311
  } = {}) {
295
312
  const remoteJid = typeof message?.key?.remoteJid === 'string' ? message.key.remoteJid : '';
296
313
  const alternateRemoteJid = typeof message?.key?.remoteJidAlt === 'string'
@@ -300,8 +317,9 @@ export function normalizeWhatsappMessage(message, accountJid, {
300
317
  || remoteJid.endsWith('@newsletter')) return null;
301
318
  const group = remoteJid.endsWith('@g.us');
302
319
  const fromMe = message.key.fromMe === true;
320
+ const matchesAccount = whatsappAccountMatcher(accountJid, accountAliases);
303
321
  const selfChat = fromMe && !group
304
- && [remoteJid, alternateRemoteJid].some((jid) => jid && areJidsSameUser(jid, accountJid));
322
+ && [remoteJid, alternateRemoteJid].some(matchesAccount);
305
323
  if (fromMe && !selfChat && !group) return null;
306
324
  const senderJid = fromMe ? accountJid : group ? message.key.participant : remoteJid;
307
325
  const senderAlternateJid = group && !fromMe ? message.key.participantAlt : alternateRemoteJid;
@@ -310,9 +328,9 @@ export function normalizeWhatsappMessage(message, accountJid, {
310
328
  const content = normalizeMessageContent(message.message);
311
329
  const context = messageContext(content);
312
330
  const mentioned = Array.isArray(context?.mentionedJid)
313
- && context.mentionedJid.some((jid) => areJidsSameUser(jid, accountJid));
331
+ && context.mentionedJid.some(matchesAccount);
314
332
  const replyToSelf = typeof context?.participant === 'string'
315
- && areJidsSameUser(context.participant, accountJid);
333
+ && matchesAccount(context.participant);
316
334
  const image = whatsappImageSource(message, content, download, { viewOnce });
317
335
  const file = whatsappFileSource(message, content, download);
318
336
  const replyTo = whatsappReplyReference(context);
@@ -677,6 +695,7 @@ export class WhatsappRuntime {
677
695
  #contextEnhancement;
678
696
  #accessPolicy;
679
697
  #logger;
698
+ #diagnostics;
680
699
  #replyTimeoutMs;
681
700
  #connectTimeoutMs;
682
701
  #mediaUploadTimeoutMs;
@@ -710,7 +729,7 @@ export class WhatsappRuntime {
710
729
  this.#state = state;
711
730
  this.#contextEnhancement = contextEnhancement;
712
731
  this.#accessPolicy = accessPolicy;
713
- this.#logger = logger;
732
+ this.#logger = logger; this.#diagnostics = createConnectionDiagnostics({ channel: 'whatsapp', logger });
714
733
  this.#replyTimeoutMs = replyTimeoutMs;
715
734
  this.#connectTimeoutMs = connectTimeoutMs;
716
735
  if (!Number.isSafeInteger(mediaUploadTimeoutMs) || mediaUploadTimeoutMs <= 0) {
@@ -738,8 +757,8 @@ export class WhatsappRuntime {
738
757
  await this.stop();
739
758
  this.#status.startedAt = new Date().toISOString();
740
759
  this.#status.connectionState = 'connecting';
741
- this.#status.lastError = null;
742
- await this.#harness.ensureRunning();
760
+ this.#status.lastError = null; this.#status.error = null; this.#diagnostics.clear();
761
+ await atConnectionStage('harness.check', () => this.#harness.ensureRunning());
743
762
  this.#status.harnessReachable = true;
744
763
  const controller = new AbortController();
745
764
  this.#abortController = controller;
@@ -757,7 +776,13 @@ export class WhatsappRuntime {
757
776
  { code: 'relink-required' },
758
777
  )),
759
778
  onMessage: async (raw, context) => {
779
+ const linkedAccount = context?.socket?.user;
760
780
  const message = normalizeWhatsappMessage(raw, this.#config.accountJid, {
781
+ accountAliases: [
782
+ linkedAccount?.id,
783
+ linkedAccount?.lid,
784
+ linkedAccount?.phoneNumber,
785
+ ],
761
786
  download: createWhatsappMediaDownloader({
762
787
  socket: context?.socket,
763
788
  logger: this.#logger,
@@ -771,7 +796,8 @@ export class WhatsappRuntime {
771
796
  if (controller.signal.aborted) return;
772
797
  this.#status.ready = false;
773
798
  this.#status.connectionState = 'failed';
774
- this.#status.lastError = error?.message ?? 'WhatsApp Web connection closed';
799
+ this.#status.error = this.#diagnostics.report(error, { operation: 'connection.monitor', botId: this.#config?.botId, automatic: true }).publicError;
800
+ this.#status.lastError = this.#status.error.message;
775
801
  },
776
802
  });
777
803
  this.#session = session;
@@ -822,7 +848,8 @@ export class WhatsappRuntime {
822
848
  } catch (error) {
823
849
  this.#status.ready = false;
824
850
  this.#status.connectionState = 'failed';
825
- this.#status.lastError = error?.message ?? String(error);
851
+ this.#status.error = this.#diagnostics.report(error, { operation: 'connection.monitor', botId: this.#config?.botId, automatic: true }).publicError;
852
+ this.#status.lastError = this.#status.error.message;
826
853
  await this.stop();
827
854
  throw error;
828
855
  }