@xmanrui/dsh-im 4.3.0 → 4.4.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 (68) hide show
  1. package/lib/client.js +951 -626
  2. package/lib/index.js +233 -233
  3. package/package.json +1 -1
  4. package/plugin-src/client/access-policy-settings.js +351 -0
  5. package/plugin-src/client/channel-card-meta.js +2 -1
  6. package/plugin-src/client/channels/dingtalk/api.js +5 -0
  7. package/plugin-src/client/channels/dingtalk/index.js +1 -0
  8. package/plugin-src/client/channels/feishu/api.js +5 -0
  9. package/plugin-src/client/channels/feishu/index.js +1 -0
  10. package/plugin-src/client/channels/qq/api.js +5 -0
  11. package/plugin-src/client/channels/qq/index.js +1 -0
  12. package/plugin-src/client/channels/shared/token-api.js +5 -0
  13. package/plugin-src/client/channels/shared/token-channel.js +1 -0
  14. package/plugin-src/client/channels/telegram/api.js +2 -17
  15. package/plugin-src/client/channels/telegram/index.js +0 -112
  16. package/plugin-src/client/channels/telegram/styles.js +0 -28
  17. package/plugin-src/client/channels/wecom/api.js +5 -0
  18. package/plugin-src/client/channels/wecom/index.js +1 -0
  19. package/plugin-src/client/channels/weixin/api.js +5 -0
  20. package/plugin-src/client/channels/weixin/index.js +1 -0
  21. package/plugin-src/client/channels/whatsapp/api.js +4 -10
  22. package/plugin-src/client/channels/whatsapp/index.js +1 -125
  23. package/plugin-src/client/channels/whatsapp/styles.js +0 -25
  24. package/plugin-src/client/delivery-settings.js +61 -9
  25. package/plugin-src/client/i18n.js +56 -1
  26. package/plugin-src/client/index.js +1 -0
  27. package/plugin-src/client/styles.js +50 -4
  28. package/plugin-src/host/channels/dingtalk/production.mjs +12 -1
  29. package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
  30. package/plugin-src/host/channels/feishu/production.mjs +39 -1
  31. package/plugin-src/host/channels/feishu/rpc.mjs +19 -2
  32. package/plugin-src/host/channels/qq/production.mjs +12 -1
  33. package/plugin-src/host/channels/qq/rpc.mjs +11 -0
  34. package/plugin-src/host/channels/shared/access-policy-production.mjs +137 -0
  35. package/plugin-src/host/channels/shared/access-policy-rpc.mjs +17 -0
  36. package/plugin-src/host/channels/shared/production.mjs +15 -1
  37. package/plugin-src/host/channels/shared/rpc.mjs +9 -0
  38. package/plugin-src/host/channels/slack/production.mjs +12 -1
  39. package/plugin-src/host/channels/slack/rpc.mjs +10 -0
  40. package/plugin-src/host/channels/telegram/rpc.mjs +2 -49
  41. package/plugin-src/host/channels/wecom/production.mjs +12 -1
  42. package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
  43. package/plugin-src/host/channels/weixin/production.mjs +12 -1
  44. package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
  45. package/plugin-src/host/channels/whatsapp/production.mjs +16 -2
  46. package/plugin-src/host/channels/whatsapp/rpc.mjs +10 -15
  47. package/src/channels/dingtalk/dingtalk-bridge.mjs +61 -9
  48. package/src/channels/dingtalk/dingtalk-runtime.mjs +4 -0
  49. package/src/channels/discord/discord-runtime.mjs +29 -1
  50. package/src/channels/feishu/bridge.mjs +188 -80
  51. package/src/channels/feishu/feishu-runtime.mjs +4 -0
  52. package/src/channels/qq/qq-bridge.mjs +59 -17
  53. package/src/channels/qq/qq-runtime.mjs +20 -2
  54. package/src/channels/shared/access-policy.mjs +210 -0
  55. package/src/channels/shared/bot-workspace-store.mjs +176 -33
  56. package/src/channels/shared/command-permission.mjs +32 -0
  57. package/src/channels/shared/compact-command.mjs +5 -2
  58. package/src/channels/shared/i18n-en/shared-c.mjs +2 -0
  59. package/src/channels/shared/inbound-access.mjs +43 -0
  60. package/src/channels/shared/text-harness-bridge.mjs +47 -4
  61. package/src/channels/shared/workspace-command.mjs +10 -1
  62. package/src/channels/slack/slack-runtime.mjs +4 -0
  63. package/src/channels/telegram/telegram-runtime.mjs +5 -13
  64. package/src/channels/wecom/wecom-bridge.mjs +51 -4
  65. package/src/channels/wecom/wecom-runtime.mjs +4 -0
  66. package/src/channels/weixin/weixin-bridge.mjs +59 -16
  67. package/src/channels/weixin/weixin-runtime.mjs +4 -0
  68. package/src/channels/whatsapp/whatsapp-runtime.mjs +42 -20
@@ -51,6 +51,10 @@ import {
51
51
  messageFailureText,
52
52
  setLastMessageFailure,
53
53
  } from '../shared/message-failure.mjs';
54
+ import {
55
+ COMMAND_PERMISSION_DENIED_MESSAGE,
56
+ evaluateInboundAccess,
57
+ } from '../shared/inbound-access.mjs';
54
58
  import { t } from '../shared/i18n.mjs';
55
59
 
56
60
  const DEFAULT_FILE_UPLOAD_TIMEOUT_MS = 120_000;
@@ -490,6 +494,7 @@ export class WecomHarnessBridge {
490
494
  #harness;
491
495
  #state;
492
496
  #contextEnhancement;
497
+ #accessPolicy;
493
498
  #status;
494
499
  #logger;
495
500
  #replyTimeoutMs;
@@ -512,6 +517,7 @@ export class WecomHarnessBridge {
512
517
  harness,
513
518
  state,
514
519
  contextEnhancement,
520
+ accessPolicy,
515
521
  status = createWecomBridgeStatus(),
516
522
  logger = console,
517
523
  replyTimeoutMs = 600_000,
@@ -530,6 +536,7 @@ export class WecomHarnessBridge {
530
536
  this.#harness = harness;
531
537
  this.#state = state;
532
538
  this.#contextEnhancement = contextEnhancement;
539
+ this.#accessPolicy = accessPolicy;
533
540
  this.#status = status;
534
541
  this.#logger = logger;
535
542
  this.#replyTimeoutMs = replyTimeoutMs;
@@ -557,16 +564,28 @@ export class WecomHarnessBridge {
557
564
  || this.#acceptedMessageIds.has(messageId)) return Promise.resolve();
558
565
 
559
566
  const key = conversationKey(frame);
567
+ const pending = this.#pendingInteractions.get(key);
568
+ const commandMessage = wecomInboundMessage(frame, this.#client);
569
+ const commandText = nonEmptyString(commandMessage.content) ?? '';
570
+ const conversationType = body.chattype === 'single' ? 'direct' : 'group';
571
+ const access = evaluateInboundAccess(this.#accessPolicy, {
572
+ conversationType,
573
+ senderIds: senderId,
574
+ text: commandText,
575
+ hasImages: hasInboundImages(commandMessage),
576
+ hasFiles: hasInboundFiles(commandMessage),
577
+ });
578
+ if (!access.allowed) {
579
+ this.#acceptedMessageIds.set(messageId, null);
580
+ return this.#finishAccessDecision(frame, messageId, chatId, access);
581
+ }
560
582
  this.#acceptedMessageIds.set(messageId, captureContextEnhancement(
561
583
  this.#contextEnhancement,
562
- body.chattype === 'single' ? 'direct' : 'group',
584
+ conversationType,
563
585
  ));
564
586
  if (body.chattype === 'single') {
565
587
  rememberConnectionTestTarget(this.#state, { chatId });
566
588
  }
567
- const pending = this.#pendingInteractions.get(key);
568
- const commandMessage = wecomInboundMessage(frame, this.#client);
569
- const commandText = nonEmptyString(commandMessage.content) ?? '';
570
589
  const batchCommand = isBatchInputCommand(commandText);
571
590
  const batchStatus = this.#batchInputs.status(key);
572
591
  if (batchCommand && body.chattype === 'group') {
@@ -760,6 +779,34 @@ export class WecomHarnessBridge {
760
779
  return task;
761
780
  }
762
781
 
782
+ #finishAccessDecision(frame, messageId, chatId, access) {
783
+ let task;
784
+ task = Promise.resolve().then(async () => {
785
+ if (this.#state.hasSeen(messageId)) return;
786
+ await this.#state.markSeen(messageId);
787
+ if (access.reason === 'command-not-allowed') {
788
+ this.#status.messagesReceived += 1;
789
+ this.#status.lastMessageAt = new Date().toISOString();
790
+ await this.#sendImmediate(frame, chatId, t(COMMAND_PERMISSION_DENIED_MESSAGE));
791
+ this.#status.messagesReplied += 1;
792
+ this.#status.lastReplyAt = new Date().toISOString();
793
+ } else {
794
+ this.#status.messagesRejected += 1;
795
+ this.#status.lastRejectedAt = new Date().toISOString();
796
+ }
797
+ this.#status.lastError = null;
798
+ }).catch((error) => {
799
+ if (this.#signal?.aborted) return;
800
+ this.#status.lastError = error?.message ?? String(error);
801
+ this.#logger.error?.('[dsh-im:wecom] failed to apply inbound access policy', error);
802
+ }).finally(() => {
803
+ this.#acceptedMessageIds.delete(messageId);
804
+ this.#commandTasks.delete(task);
805
+ });
806
+ this.#commandTasks.add(task);
807
+ return task;
808
+ }
809
+
763
810
  async waitForIdle() {
764
811
  await Promise.allSettled([
765
812
  ...this.#queues.values(),
@@ -29,6 +29,7 @@ export class WecomRuntime {
29
29
  #harness;
30
30
  #state;
31
31
  #contextEnhancement;
32
+ #accessPolicy;
32
33
  #logger;
33
34
  #replyTimeoutMs;
34
35
  #connectTimeoutMs;
@@ -47,6 +48,7 @@ export class WecomRuntime {
47
48
  harness,
48
49
  state,
49
50
  contextEnhancement,
51
+ accessPolicy,
50
52
  logger = console,
51
53
  replyTimeoutMs = 600_000,
52
54
  connectTimeoutMs = 20_000,
@@ -61,6 +63,7 @@ export class WecomRuntime {
61
63
  this.#harness = harness;
62
64
  this.#state = state;
63
65
  this.#contextEnhancement = contextEnhancement;
66
+ this.#accessPolicy = accessPolicy;
64
67
  this.#logger = logger;
65
68
  this.#replyTimeoutMs = replyTimeoutMs;
66
69
  this.#connectTimeoutMs = connectTimeoutMs;
@@ -111,6 +114,7 @@ export class WecomRuntime {
111
114
  harness: this.#harness,
112
115
  state: this.#state,
113
116
  contextEnhancement: this.#contextEnhancement,
117
+ accessPolicy: this.#accessPolicy,
114
118
  status: this.#status,
115
119
  logger: this.#logger,
116
120
  replyTimeoutMs: this.#replyTimeoutMs,
@@ -57,6 +57,10 @@ import {
57
57
  messageFailureText,
58
58
  setLastMessageFailure,
59
59
  } from '../shared/message-failure.mjs';
60
+ import {
61
+ COMMAND_PERMISSION_DENIED_MESSAGE,
62
+ evaluateInboundAccess,
63
+ } from '../shared/inbound-access.mjs';
60
64
  import { t } from '../shared/i18n.mjs';
61
65
 
62
66
  const INTERACTION_RESOLVED_TEXT = () => t('这个问题已在其他客户端处理,无需再次回答。');
@@ -260,6 +264,7 @@ export class WeixinHarnessBridge {
260
264
  #harness;
261
265
  #state;
262
266
  #contextEnhancement;
267
+ #accessPolicy;
263
268
  #status;
264
269
  #logger;
265
270
  #replyTimeoutMs;
@@ -292,6 +297,7 @@ export class WeixinHarnessBridge {
292
297
  harness,
293
298
  state,
294
299
  contextEnhancement,
300
+ accessPolicy,
295
301
  status = createWeixinBridgeStatus(),
296
302
  logger = console,
297
303
  replyTimeoutMs = 600_000,
@@ -312,6 +318,7 @@ export class WeixinHarnessBridge {
312
318
  this.#harness = harness;
313
319
  this.#state = state;
314
320
  this.#contextEnhancement = contextEnhancement;
321
+ this.#accessPolicy = accessPolicy;
315
322
  this.#status = status;
316
323
  this.#logger = logger;
317
324
  this.#replyTimeoutMs = replyTimeoutMs;
@@ -332,6 +339,22 @@ export class WeixinHarnessBridge {
332
339
  const sender = nonEmptyString(message?.from_user_id);
333
340
  if (!messageId || !sender || this.#state.hasSeen(messageId)
334
341
  || this.#acceptedMessageIds.has(messageId)) return Promise.resolve();
342
+ const commandText = nonEmptyString(extractWeixinText(message)) ?? '';
343
+ const access = this.#accessPolicy
344
+ ? evaluateInboundAccess(this.#accessPolicy, {
345
+ conversationType: 'direct',
346
+ senderIds: sender,
347
+ text: commandText,
348
+ hasImages: hasWeixinImageItems(message),
349
+ hasFiles: hasWeixinFileItems(message),
350
+ })
351
+ : sender === this.#ownerUserId
352
+ ? { allowed: true, reason: 'legacy-owner' }
353
+ : { allowed: false, reason: 'sender-not-allowed' };
354
+ if (!access.allowed) {
355
+ this.#acceptedMessageIds.set(messageId, null);
356
+ return this.#finishAccessDecision(messageId, sender, message, access);
357
+ }
335
358
  this.#acceptedMessageIds.set(messageId, captureContextEnhancement(
336
359
  this.#contextEnhancement,
337
360
  'direct',
@@ -343,11 +366,9 @@ export class WeixinHarnessBridge {
343
366
  const contextToken = nonEmptyString(message?.context_token) ?? undefined;
344
367
  const runId = nonEmptyString(message?.run_id) ?? undefined;
345
368
  const pending = this.#pendingInteractions.get(key);
346
- const commandText = nonEmptyString(extractWeixinText(message)) ?? '';
347
369
  const batchCommand = isBatchInputCommand(commandText);
348
370
  const batchStatus = this.#batchInputs.status(key);
349
- if (sender === this.#ownerUserId
350
- && (batchCommand || batchStatus.phase === 'collecting')) {
371
+ if (batchCommand || batchStatus.phase === 'collecting') {
351
372
  const exactBatchStart = /^\/batch$/iu.test(commandText);
352
373
  const result = exactBatchStart
353
374
  && batchStatus.phase === 'idle'
@@ -380,7 +401,7 @@ export class WeixinHarnessBridge {
380
401
  : (isModelCommand(commandText)
381
402
  ? runModelCommand
382
403
  : (isPresetCommand(commandText) ? runPresetCommand : null));
383
- if (commandRunner && sender === this.#ownerUserId) {
404
+ if (commandRunner) {
384
405
  let task;
385
406
  task = this.#processFastCommand(
386
407
  message,
@@ -467,12 +488,10 @@ export class WeixinHarnessBridge {
467
488
  alreadyRecorded = false,
468
489
  batchSubmission = null,
469
490
  } = {}) {
470
- const preparedMessage = message.from_user_id === this.#ownerUserId
471
- ? prefetchInboundFiles(
472
- weixinInboundMessage(message, this.#api),
473
- { signal: this.#signal },
474
- )
475
- : undefined;
491
+ const preparedMessage = prefetchInboundFiles(
492
+ weixinInboundMessage(message, this.#api),
493
+ { signal: this.#signal },
494
+ );
476
495
  const previous = this.#queues.get(key) ?? Promise.resolve();
477
496
  const current = previous
478
497
  .catch(() => undefined)
@@ -518,6 +537,36 @@ export class WeixinHarnessBridge {
518
537
  return task;
519
538
  }
520
539
 
540
+ #finishAccessDecision(messageId, sender, message, access) {
541
+ const contextToken = nonEmptyString(message?.context_token) ?? undefined;
542
+ const runId = nonEmptyString(message?.run_id) ?? undefined;
543
+ let task;
544
+ task = Promise.resolve().then(async () => {
545
+ if (this.#state.hasSeen(messageId)) return;
546
+ await this.#state.markSeen(messageId);
547
+ if (access.reason === 'command-not-allowed') {
548
+ this.#status.messagesReceived += 1;
549
+ this.#status.lastMessageAt = new Date().toISOString();
550
+ await this.#send(sender, t(COMMAND_PERMISSION_DENIED_MESSAGE), contextToken, runId);
551
+ this.#status.messagesReplied += 1;
552
+ this.#status.lastReplyAt = new Date().toISOString();
553
+ } else {
554
+ this.#status.messagesRejected += 1;
555
+ this.#status.lastRejectedAt = new Date().toISOString();
556
+ }
557
+ this.#status.lastError = null;
558
+ }).catch((error) => {
559
+ if (this.#signal?.aborted) return;
560
+ this.#status.lastError = error?.message ?? String(error);
561
+ this.#logger.error?.('[dsh-weixin] failed to apply inbound access policy:', error);
562
+ }).finally(() => {
563
+ this.#acceptedMessageIds.delete(messageId);
564
+ this.#commandTasks.delete(task);
565
+ });
566
+ this.#commandTasks.add(task);
567
+ return task;
568
+ }
569
+
521
570
  async waitForIdle() {
522
571
  await Promise.allSettled([
523
572
  ...this.#queues.values(),
@@ -590,12 +639,6 @@ export class WeixinHarnessBridge {
590
639
  this.#status.messagesReceived += 1;
591
640
  this.#status.lastMessageAt = new Date().toISOString();
592
641
  }
593
- if (sender !== this.#ownerUserId) {
594
- this.#status.messagesRejected += 1;
595
- this.#status.lastRejectedAt = new Date().toISOString();
596
- return;
597
- }
598
-
599
642
  const contextToken = typeof message.context_token === 'string' ? message.context_token : undefined;
600
643
  const runId = typeof message.run_id === 'string' ? message.run_id : undefined;
601
644
  let batchSettled = batchSubmission === null;
@@ -109,6 +109,7 @@ export class WeixinRuntime {
109
109
  #harness;
110
110
  #state;
111
111
  #contextEnhancement;
112
+ #accessPolicy;
112
113
  #logger;
113
114
  #replyTimeoutMs;
114
115
  #maxMessageChars;
@@ -126,6 +127,7 @@ export class WeixinRuntime {
126
127
  harness,
127
128
  state,
128
129
  contextEnhancement,
130
+ accessPolicy,
129
131
  logger = console,
130
132
  replyTimeoutMs = 600_000,
131
133
  maxMessageChars = DEFAULT_WEIXIN_MAX_MESSAGE_CHARS,
@@ -140,6 +142,7 @@ export class WeixinRuntime {
140
142
  this.#harness = harness;
141
143
  this.#state = state;
142
144
  this.#contextEnhancement = contextEnhancement;
145
+ this.#accessPolicy = accessPolicy;
143
146
  this.#logger = logger;
144
147
  this.#replyTimeoutMs = replyTimeoutMs;
145
148
  this.#maxMessageChars = maxMessageChars;
@@ -182,6 +185,7 @@ export class WeixinRuntime {
182
185
  harness: this.#harness,
183
186
  state: this.#state,
184
187
  contextEnhancement: this.#contextEnhancement,
188
+ accessPolicy: this.#accessPolicy,
185
189
  status: this.#status,
186
190
  logger: this.#logger,
187
191
  replyTimeoutMs: this.#replyTimeoutMs,
@@ -3,6 +3,7 @@ import { createHash, randomBytes } from 'node:crypto';
3
3
  import {
4
4
  areJidsSameUser,
5
5
  downloadMediaMessage,
6
+ jidDecode,
6
7
  normalizeMessageContent,
7
8
  } from '@whiskeysockets/baileys';
8
9
 
@@ -13,7 +14,6 @@ import { trackOutboundArtifactProviderPromise } from '../shared/semantic/artifac
13
14
  import { createWhatsappBridgeStatus, WhatsappHarnessBridge } from './whatsapp-bridge.mjs';
14
15
  import {
15
16
  WHATSAPP_ACCESS_MODES,
16
- normalizeWhatsappAccessPolicy,
17
17
  } from './config-store.mjs';
18
18
  import { createWhatsappWebSession } from './whatsapp-web-session.mjs';
19
19
 
@@ -37,6 +37,36 @@ const VIEW_ONCE_WRAPPER_KEYS = new Set([
37
37
  'viewOnceMessageV2',
38
38
  'viewOnceMessageV2Extension',
39
39
  ]);
40
+ const WHATSAPP_ACCESS_POLICY_USER_SERVERS = new Set([
41
+ 's.whatsapp.net',
42
+ 'c.us',
43
+ 'lid',
44
+ 'hosted',
45
+ 'hosted.lid',
46
+ ]);
47
+
48
+ function normalizeWhatsappAccessPolicyId(value) {
49
+ if (typeof value !== 'string') return null;
50
+ const candidate = value.trim();
51
+ if (/^\+?\d+$/.test(candidate)) {
52
+ return `${candidate.replace(/^\+/, '')}@s.whatsapp.net`;
53
+ }
54
+ const decoded = jidDecode(candidate);
55
+ if (!decoded || !/^\d+$/.test(decoded.user)
56
+ || !WHATSAPP_ACCESS_POLICY_USER_SERVERS.has(decoded.server)) return null;
57
+ return candidate;
58
+ }
59
+
60
+ export function whatsappAccessPolicyIdsEqual(left, right) {
61
+ const normalizedLeft = normalizeWhatsappAccessPolicyId(left);
62
+ const normalizedRight = normalizeWhatsappAccessPolicyId(right);
63
+ if (!normalizedLeft || !normalizedRight) return false;
64
+ try {
65
+ return areJidsSameUser(normalizedLeft, normalizedRight) === true;
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
40
70
 
41
71
  function hasViewOnceWrapper(content) {
42
72
  let current = content;
@@ -538,12 +568,11 @@ export class WhatsappRuntime {
538
568
  #harness;
539
569
  #state;
540
570
  #contextEnhancement;
571
+ #accessPolicy;
541
572
  #logger;
542
573
  #replyTimeoutMs;
543
574
  #connectTimeoutMs;
544
575
  #mediaUploadTimeoutMs;
545
- #accessMode;
546
- #allowedPrivateNumbers;
547
576
  #createSession;
548
577
  #status = createWhatsappRuntimeStatus();
549
578
  #abortController = null;
@@ -558,6 +587,7 @@ export class WhatsappRuntime {
558
587
  harness,
559
588
  state,
560
589
  contextEnhancement,
590
+ accessPolicy,
561
591
  logger = console,
562
592
  replyTimeoutMs = 600_000,
563
593
  connectTimeoutMs = 30_000,
@@ -572,6 +602,7 @@ export class WhatsappRuntime {
572
602
  this.#harness = harness;
573
603
  this.#state = state;
574
604
  this.#contextEnhancement = contextEnhancement;
605
+ this.#accessPolicy = accessPolicy;
575
606
  this.#logger = logger;
576
607
  this.#replyTimeoutMs = replyTimeoutMs;
577
608
  this.#connectTimeoutMs = connectTimeoutMs;
@@ -583,21 +614,12 @@ export class WhatsappRuntime {
583
614
  WHATSAPP_MEDIA_UPLOAD_TIMEOUT_MS,
584
615
  );
585
616
  this.#createSession = createSession;
586
- this.setAccessPolicy(config);
587
617
  }
588
618
 
589
619
  get status() {
590
620
  return structuredClone(this.#status);
591
621
  }
592
622
 
593
- setAccessPolicy(value) {
594
- const policy = normalizeWhatsappAccessPolicy(value);
595
- this.#accessMode = policy.accessMode;
596
- this.#allowedPrivateNumbers = new Set(policy.allowedNumbers);
597
- this.#config = { ...this.#config, ...policy };
598
- return policy;
599
- }
600
-
601
623
  async start() {
602
624
  if (this.#status.ready && this.#session) return this.status;
603
625
  if (this.#starting) return this.#starting;
@@ -636,14 +658,6 @@ export class WhatsappRuntime {
636
658
  });
637
659
  if (!message || outboundIds.has(message.providerMessageId) || !this.#bridge) return;
638
660
  this.#status.lastCheckedAt = Date.now();
639
- if (!whatsappInboundAllowed(message, {
640
- accessMode: this.#accessMode,
641
- allowedNumbers: this.#allowedPrivateNumbers,
642
- })) {
643
- this.#status.messagesRejected += 1;
644
- this.#status.lastRejectedAt = new Date().toISOString();
645
- return;
646
- }
647
661
  await this.#bridge.accept(message);
648
662
  },
649
663
  onDisconnect: ({ error }) => {
@@ -678,6 +692,14 @@ export class WhatsappRuntime {
678
692
  harness: this.#harness,
679
693
  state: this.#state,
680
694
  contextEnhancement: this.#contextEnhancement,
695
+ accessPolicy: this.#accessPolicy ? {
696
+ botId: this.#accessPolicy.botId,
697
+ getSettings: (...args) => this.#accessPolicy.getSettings(...args),
698
+ ...(typeof this.#accessPolicy.isPrivileged === 'function' ? {
699
+ isPrivileged: (...args) => this.#accessPolicy.isPrivileged(...args),
700
+ } : {}),
701
+ equals: whatsappAccessPolicyIdsEqual,
702
+ } : undefined,
681
703
  status: this.#status,
682
704
  logger: this.#logger,
683
705
  replyTimeoutMs: this.#replyTimeoutMs,