neoagent 2.2.1-beta.7 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/index.md +1 -0
- package/docs/migration.md +238 -0
- package/lib/manager.js +99 -2
- package/lib/migrations.js +409 -0
- package/package.json +1 -1
- package/server/catalog_sources/store-bundles/skills/productivity/migration/SKILL.md +173 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +72170 -70842
- package/server/routes/auth.js +13 -5
- package/server/routes/integrations.js +22 -0
- package/server/routes/messaging.js +41 -5
- package/server/routes/settings.js +1 -0
- package/server/services/integrations/google/provider.js +20 -2
- package/server/services/integrations/manager.js +79 -8
- package/server/services/messaging/access_policy.js +703 -0
- package/server/services/messaging/access_policy.test.js +228 -0
- package/server/services/messaging/automation.js +32 -95
- package/server/services/messaging/base.js +39 -0
- package/server/services/messaging/discord.js +61 -46
- package/server/services/messaging/http_platforms.js +178 -15
- package/server/services/messaging/manager.js +136 -69
- package/server/services/messaging/telegram.js +54 -40
- package/server/services/messaging/telnyx.js +43 -14
- package/server/services/messaging/whatsapp.js +27 -0
|
@@ -217,6 +217,27 @@ class ConfigurableHttpPlatform extends BasePlatform {
|
|
|
217
217
|
if (!inboundAllowed(this.config, req)) return { handled: false, status: 403, body: 'Forbidden' };
|
|
218
218
|
const msg = genericMessageFromWebhook(this.name, this.config, req);
|
|
219
219
|
if (!msg) return { handled: true, status: 202, body: 'ignored' };
|
|
220
|
+
const access = this._checkInboundAccess({
|
|
221
|
+
platform: this.name,
|
|
222
|
+
senderId: String(msg.sender || ''),
|
|
223
|
+
chatId: String(msg.chatId || ''),
|
|
224
|
+
isDirect: !msg.isGroup,
|
|
225
|
+
isShared: Boolean(msg.isGroup),
|
|
226
|
+
groupId: msg.isGroup ? String(msg.chatId || '') : '',
|
|
227
|
+
channelId: msg.isGroup ? String(msg.chatId || '') : '',
|
|
228
|
+
serverId: '',
|
|
229
|
+
roomId: msg.isGroup ? String(msg.chatId || '') : '',
|
|
230
|
+
roleIds: [],
|
|
231
|
+
phoneNumber: '',
|
|
232
|
+
wasMentioned: false,
|
|
233
|
+
}, {
|
|
234
|
+
senderName: msg.senderName || null,
|
|
235
|
+
meta: msg.isGroup ? `Chat: ${msg.chatId}` : '',
|
|
236
|
+
groupLabel: String(msg.chatId || ''),
|
|
237
|
+
channelLabel: String(msg.chatId || ''),
|
|
238
|
+
roomLabel: String(msg.chatId || ''),
|
|
239
|
+
});
|
|
240
|
+
if (!access.allowed) return { handled: true, status: 202, body: 'blocked' };
|
|
220
241
|
this.emit('message', msg);
|
|
221
242
|
return { handled: true, status: 200, body: 'OK' };
|
|
222
243
|
}
|
|
@@ -318,7 +339,7 @@ class SlackPlatform extends BasePlatform {
|
|
|
318
339
|
? String(event.text).replace(new RegExp(`<@${this._botUserId}>`, 'g'), '').trim()
|
|
319
340
|
: String(event.text);
|
|
320
341
|
if (!content) return { handled: true, status: 202, body: 'ignored' };
|
|
321
|
-
|
|
342
|
+
const message = {
|
|
322
343
|
platform: 'slack',
|
|
323
344
|
chatId: String(event.channel || 'slack'),
|
|
324
345
|
sender: String(event.user || event.bot_id || 'slack'),
|
|
@@ -332,7 +353,28 @@ class SlackPlatform extends BasePlatform {
|
|
|
332
353
|
timestamp: event.event_ts ? new Date(Number(event.event_ts) * 1000).toISOString() : new Date().toISOString(),
|
|
333
354
|
threadTs: event.thread_ts || null,
|
|
334
355
|
rawMessage: body,
|
|
356
|
+
wasMentioned,
|
|
357
|
+
};
|
|
358
|
+
const access = this._checkInboundAccess({
|
|
359
|
+
platform: 'slack',
|
|
360
|
+
senderId: message.sender,
|
|
361
|
+
chatId: message.chatId,
|
|
362
|
+
isDirect: !isGroup,
|
|
363
|
+
isShared: isGroup,
|
|
364
|
+
groupId: isGroup ? message.chatId : '',
|
|
365
|
+
channelId: isGroup ? message.chatId : '',
|
|
366
|
+
serverId: '',
|
|
367
|
+
roomId: '',
|
|
368
|
+
roleIds: [],
|
|
369
|
+
phoneNumber: '',
|
|
370
|
+
wasMentioned,
|
|
371
|
+
}, {
|
|
372
|
+
senderName: message.senderName,
|
|
373
|
+
meta: isGroup ? `Channel: ${message.chatId}` : '',
|
|
374
|
+
channelLabel: message.chatId,
|
|
335
375
|
});
|
|
376
|
+
if (!access.allowed) return { handled: true, status: 202, body: 'blocked' };
|
|
377
|
+
this.emit('message', message);
|
|
336
378
|
return { handled: true, status: 200, body: 'OK' };
|
|
337
379
|
}
|
|
338
380
|
}
|
|
@@ -345,23 +387,43 @@ class GoogleChatPlatform extends ConfigurableHttpPlatform {
|
|
|
345
387
|
async handleWebhook(req) {
|
|
346
388
|
if (!inboundAllowed(this.config, req)) return { handled: false, status: 403, body: 'Forbidden' };
|
|
347
389
|
const body = req.body || {};
|
|
348
|
-
const
|
|
349
|
-
const content =
|
|
390
|
+
const incoming = body.message || body;
|
|
391
|
+
const content = incoming.argumentText || incoming.text || body.text;
|
|
350
392
|
if (!content) return { handled: true, status: 202, body: 'ignored' };
|
|
351
|
-
|
|
393
|
+
const message = {
|
|
352
394
|
platform: 'google_chat',
|
|
353
|
-
chatId: String(
|
|
354
|
-
sender: String(body.user?.name ||
|
|
355
|
-
senderName: body.user?.displayName ||
|
|
356
|
-
senderDisplayName: body.user?.displayName ||
|
|
357
|
-
senderTag: body.user?.name ||
|
|
395
|
+
chatId: String(incoming.space?.name || body.space?.name || this.config.defaultTo || 'google_chat'),
|
|
396
|
+
sender: String(body.user?.name || incoming.sender?.name || 'google_chat'),
|
|
397
|
+
senderName: body.user?.displayName || incoming.sender?.displayName || null,
|
|
398
|
+
senderDisplayName: body.user?.displayName || incoming.sender?.displayName || null,
|
|
399
|
+
senderTag: body.user?.name || incoming.sender?.name || null,
|
|
358
400
|
content: String(content),
|
|
359
401
|
mediaType: null,
|
|
360
402
|
isGroup: true,
|
|
361
|
-
messageId: String(
|
|
403
|
+
messageId: String(incoming.name || crypto.randomUUID()),
|
|
362
404
|
timestamp: new Date().toISOString(),
|
|
363
405
|
rawMessage: body,
|
|
406
|
+
};
|
|
407
|
+
const access = this._checkInboundAccess({
|
|
408
|
+
platform: 'google_chat',
|
|
409
|
+
senderId: message.sender,
|
|
410
|
+
chatId: message.chatId,
|
|
411
|
+
isDirect: false,
|
|
412
|
+
isShared: true,
|
|
413
|
+
groupId: '',
|
|
414
|
+
channelId: '',
|
|
415
|
+
serverId: '',
|
|
416
|
+
roomId: message.chatId,
|
|
417
|
+
roleIds: [],
|
|
418
|
+
phoneNumber: '',
|
|
419
|
+
wasMentioned: false,
|
|
420
|
+
}, {
|
|
421
|
+
senderName: message.senderName,
|
|
422
|
+
meta: `Space: ${message.chatId}`,
|
|
423
|
+
roomLabel: message.chatId,
|
|
364
424
|
});
|
|
425
|
+
if (!access.allowed) return { handled: true, status: 202, body: 'blocked' };
|
|
426
|
+
this.emit('message', message);
|
|
365
427
|
return { handled: true, status: 200, body: { text: 'Received.' } };
|
|
366
428
|
}
|
|
367
429
|
}
|
|
@@ -376,7 +438,7 @@ class TeamsPlatform extends ConfigurableHttpPlatform {
|
|
|
376
438
|
const body = req.body || {};
|
|
377
439
|
const content = body.text || body.message?.text || body.value?.text;
|
|
378
440
|
if (!content) return { handled: true, status: 202, body: 'ignored' };
|
|
379
|
-
|
|
441
|
+
const message = {
|
|
380
442
|
platform: 'teams',
|
|
381
443
|
chatId: String(body.conversation?.id || body.channelData?.channel?.id || this.config.defaultTo || 'teams'),
|
|
382
444
|
sender: String(body.from?.id || 'teams'),
|
|
@@ -389,7 +451,27 @@ class TeamsPlatform extends ConfigurableHttpPlatform {
|
|
|
389
451
|
messageId: String(body.id || crypto.randomUUID()),
|
|
390
452
|
timestamp: body.timestamp || new Date().toISOString(),
|
|
391
453
|
rawMessage: body,
|
|
454
|
+
};
|
|
455
|
+
const access = this._checkInboundAccess({
|
|
456
|
+
platform: 'teams',
|
|
457
|
+
senderId: message.sender,
|
|
458
|
+
chatId: message.chatId,
|
|
459
|
+
isDirect: false,
|
|
460
|
+
isShared: true,
|
|
461
|
+
groupId: '',
|
|
462
|
+
channelId: message.chatId,
|
|
463
|
+
serverId: '',
|
|
464
|
+
roomId: '',
|
|
465
|
+
roleIds: [],
|
|
466
|
+
phoneNumber: '',
|
|
467
|
+
wasMentioned: false,
|
|
468
|
+
}, {
|
|
469
|
+
senderName: message.senderName,
|
|
470
|
+
meta: `Conversation: ${message.chatId}`,
|
|
471
|
+
channelLabel: message.chatId,
|
|
392
472
|
});
|
|
473
|
+
if (!access.allowed) return { handled: true, status: 202, body: 'blocked' };
|
|
474
|
+
this.emit('message', message);
|
|
393
475
|
return { handled: true, status: 200, body: { type: 'message', text: 'Received.' } };
|
|
394
476
|
}
|
|
395
477
|
}
|
|
@@ -473,7 +555,7 @@ class MatrixPlatform extends BasePlatform {
|
|
|
473
555
|
? String(content).replaceAll(this.userId, '').trim()
|
|
474
556
|
: String(content);
|
|
475
557
|
if (!cleanContent) continue;
|
|
476
|
-
|
|
558
|
+
const message = {
|
|
477
559
|
platform: 'matrix',
|
|
478
560
|
chatId: roomId,
|
|
479
561
|
sender: String(event.sender || roomId),
|
|
@@ -486,7 +568,27 @@ class MatrixPlatform extends BasePlatform {
|
|
|
486
568
|
messageId: String(event.event_id || crypto.randomUUID()),
|
|
487
569
|
timestamp: event.origin_server_ts ? new Date(event.origin_server_ts).toISOString() : new Date().toISOString(),
|
|
488
570
|
rawMessage: event,
|
|
571
|
+
};
|
|
572
|
+
const access = this._checkInboundAccess({
|
|
573
|
+
platform: 'matrix',
|
|
574
|
+
senderId: message.sender,
|
|
575
|
+
chatId: message.chatId,
|
|
576
|
+
isDirect: false,
|
|
577
|
+
isShared: true,
|
|
578
|
+
groupId: '',
|
|
579
|
+
channelId: '',
|
|
580
|
+
serverId: '',
|
|
581
|
+
roomId: roomId,
|
|
582
|
+
roleIds: [],
|
|
583
|
+
phoneNumber: '',
|
|
584
|
+
wasMentioned: this.userId ? String(content).includes(this.userId) : false,
|
|
585
|
+
}, {
|
|
586
|
+
senderName: message.senderName,
|
|
587
|
+
meta: `Room: ${roomId}`,
|
|
588
|
+
roomLabel: roomId,
|
|
489
589
|
});
|
|
590
|
+
if (!access.allowed) continue;
|
|
591
|
+
this.emit('message', message);
|
|
490
592
|
}
|
|
491
593
|
}
|
|
492
594
|
}
|
|
@@ -574,7 +676,7 @@ class SignalPlatform extends ConfigurableHttpPlatform {
|
|
|
574
676
|
const dataMessage = envelope.dataMessage || {};
|
|
575
677
|
const content = dataMessage.message || '';
|
|
576
678
|
if (!content) continue;
|
|
577
|
-
|
|
679
|
+
const message = {
|
|
578
680
|
platform: 'signal',
|
|
579
681
|
chatId: String(envelope.sourceNumber || envelope.source || 'signal'),
|
|
580
682
|
sender: String(envelope.sourceNumber || envelope.source || 'signal'),
|
|
@@ -587,7 +689,27 @@ class SignalPlatform extends ConfigurableHttpPlatform {
|
|
|
587
689
|
messageId: String(envelope.timestamp || crypto.randomUUID()),
|
|
588
690
|
timestamp: envelope.timestamp ? new Date(Number(envelope.timestamp)).toISOString() : new Date().toISOString(),
|
|
589
691
|
rawMessage: item,
|
|
692
|
+
};
|
|
693
|
+
const access = this._checkInboundAccess({
|
|
694
|
+
platform: 'signal',
|
|
695
|
+
senderId: message.sender,
|
|
696
|
+
chatId: message.chatId,
|
|
697
|
+
isDirect: !message.isGroup,
|
|
698
|
+
isShared: message.isGroup,
|
|
699
|
+
groupId: message.isGroup ? message.chatId : '',
|
|
700
|
+
channelId: '',
|
|
701
|
+
serverId: '',
|
|
702
|
+
roomId: '',
|
|
703
|
+
roleIds: [],
|
|
704
|
+
phoneNumber: message.sender,
|
|
705
|
+
wasMentioned: false,
|
|
706
|
+
}, {
|
|
707
|
+
senderName: message.senderName,
|
|
708
|
+
meta: message.isGroup ? `Group: ${message.chatId}` : '',
|
|
709
|
+
groupLabel: message.chatId,
|
|
590
710
|
});
|
|
711
|
+
if (!access.allowed) continue;
|
|
712
|
+
this.emit('message', message);
|
|
591
713
|
}
|
|
592
714
|
}
|
|
593
715
|
|
|
@@ -640,7 +762,7 @@ class LinePlatform extends ConfigurableHttpPlatform {
|
|
|
640
762
|
for (const event of events) {
|
|
641
763
|
const content = event.message?.text || '';
|
|
642
764
|
if (!content) continue;
|
|
643
|
-
|
|
765
|
+
const message = {
|
|
644
766
|
platform: 'line',
|
|
645
767
|
chatId: String(event.source?.groupId || event.source?.roomId || event.source?.userId || 'line'),
|
|
646
768
|
sender: String(event.source?.userId || 'line'),
|
|
@@ -652,7 +774,28 @@ class LinePlatform extends ConfigurableHttpPlatform {
|
|
|
652
774
|
messageId: String(event.message?.id || event.webhookEventId || crypto.randomUUID()),
|
|
653
775
|
timestamp: event.timestamp ? new Date(Number(event.timestamp)).toISOString() : new Date().toISOString(),
|
|
654
776
|
rawMessage: event,
|
|
777
|
+
};
|
|
778
|
+
const access = this._checkInboundAccess({
|
|
779
|
+
platform: 'line',
|
|
780
|
+
senderId: message.sender,
|
|
781
|
+
chatId: message.chatId,
|
|
782
|
+
isDirect: !message.isGroup,
|
|
783
|
+
isShared: message.isGroup,
|
|
784
|
+
groupId: event.source?.groupId ? String(event.source.groupId) : '',
|
|
785
|
+
channelId: '',
|
|
786
|
+
serverId: '',
|
|
787
|
+
roomId: event.source?.roomId ? String(event.source.roomId) : '',
|
|
788
|
+
roleIds: [],
|
|
789
|
+
phoneNumber: '',
|
|
790
|
+
wasMentioned: false,
|
|
791
|
+
}, {
|
|
792
|
+
senderName: message.senderName,
|
|
793
|
+
meta: event.source?.groupId ? `Group: ${event.source.groupId}` : (event.source?.roomId ? `Room: ${event.source.roomId}` : ''),
|
|
794
|
+
groupLabel: event.source?.groupId ? String(event.source.groupId) : '',
|
|
795
|
+
roomLabel: event.source?.roomId ? String(event.source.roomId) : '',
|
|
655
796
|
});
|
|
797
|
+
if (!access.allowed) continue;
|
|
798
|
+
this.emit('message', message);
|
|
656
799
|
}
|
|
657
800
|
return { handled: true, status: 200, body: 'OK' };
|
|
658
801
|
}
|
|
@@ -795,7 +938,7 @@ class IrcPlatform extends BasePlatform {
|
|
|
795
938
|
? content.replace(new RegExp(`(^|\\s)${this.nick.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[:,]?\\s*`, 'i'), ' ').trim()
|
|
796
939
|
: content;
|
|
797
940
|
if (!cleanContent) continue;
|
|
798
|
-
|
|
941
|
+
const message = {
|
|
799
942
|
platform: this.name,
|
|
800
943
|
chatId: target,
|
|
801
944
|
sender: nick,
|
|
@@ -807,7 +950,27 @@ class IrcPlatform extends BasePlatform {
|
|
|
807
950
|
isGroup,
|
|
808
951
|
messageId: crypto.randomUUID(),
|
|
809
952
|
timestamp: new Date().toISOString(),
|
|
953
|
+
};
|
|
954
|
+
const access = this._checkInboundAccess({
|
|
955
|
+
platform: this.name,
|
|
956
|
+
senderId: nick,
|
|
957
|
+
chatId: target,
|
|
958
|
+
isDirect: !isGroup,
|
|
959
|
+
isShared: isGroup,
|
|
960
|
+
groupId: '',
|
|
961
|
+
channelId: isGroup ? target : '',
|
|
962
|
+
serverId: '',
|
|
963
|
+
roomId: '',
|
|
964
|
+
roleIds: [],
|
|
965
|
+
phoneNumber: '',
|
|
966
|
+
wasMentioned: !isGroup || new RegExp(`(^|\\s)${this.nick.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\$&')}[:,]?\\b`, 'i').test(content),
|
|
967
|
+
}, {
|
|
968
|
+
senderName: nick,
|
|
969
|
+
meta: isGroup ? `Channel: ${target}` : '',
|
|
970
|
+
channelLabel: target,
|
|
810
971
|
});
|
|
972
|
+
if (!access.allowed) continue;
|
|
973
|
+
this.emit('message', message);
|
|
811
974
|
}
|
|
812
975
|
}
|
|
813
976
|
|
|
@@ -22,29 +22,17 @@ const {
|
|
|
22
22
|
createGenericPlatformClass,
|
|
23
23
|
} = require('./http_platforms');
|
|
24
24
|
const { normalizeOutgoingMessageForPlatform } = require('./formatting_guides');
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'line',
|
|
37
|
-
'mattermost',
|
|
38
|
-
'nextcloud_talk',
|
|
39
|
-
'nostr',
|
|
40
|
-
'synology_chat',
|
|
41
|
-
'tlon',
|
|
42
|
-
'twitch',
|
|
43
|
-
'zalo',
|
|
44
|
-
'zalo_personal',
|
|
45
|
-
'wechat',
|
|
46
|
-
'webchat',
|
|
47
|
-
]);
|
|
25
|
+
const {
|
|
26
|
+
accessPolicyKey,
|
|
27
|
+
legacyWhitelistKey,
|
|
28
|
+
getPlatformAccessCapabilities,
|
|
29
|
+
normalizeAccessPolicy,
|
|
30
|
+
migrateLegacyWhitelist,
|
|
31
|
+
parseStoredAccessPolicy,
|
|
32
|
+
evaluateAccessPolicy,
|
|
33
|
+
summarizeAccessPolicy,
|
|
34
|
+
classifyRecentTarget,
|
|
35
|
+
} = require('./access_policy');
|
|
48
36
|
|
|
49
37
|
const LEGACY_WHATSAPP_AUTH_DIR = path.join(DATA_DIR, 'whatsapp-auth');
|
|
50
38
|
|
|
@@ -70,6 +58,7 @@ class MessagingManager extends EventEmitter {
|
|
|
70
58
|
this.io = io;
|
|
71
59
|
this.voiceRuntimeManager = options.voiceRuntimeManager || null;
|
|
72
60
|
this.platforms = new Map();
|
|
61
|
+
this.accessSuggestions = new Map();
|
|
73
62
|
this.messageHandlers = [];
|
|
74
63
|
this.isShuttingDown = false;
|
|
75
64
|
this.platformTypes = {
|
|
@@ -176,6 +165,41 @@ class MessagingManager extends EventEmitter {
|
|
|
176
165
|
.get(userId, key);
|
|
177
166
|
}
|
|
178
167
|
|
|
168
|
+
_upsertSetting(userId, agentId, key, value) {
|
|
169
|
+
db.prepare(
|
|
170
|
+
`INSERT INTO agent_settings (user_id, agent_id, key, value)
|
|
171
|
+
VALUES (?, ?, ?, ?)
|
|
172
|
+
ON CONFLICT(user_id, agent_id, key) DO UPDATE SET value = excluded.value`
|
|
173
|
+
).run(userId, agentId, key, JSON.stringify(value));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
_accessSuggestionKey(userId, agentId, platformName) {
|
|
177
|
+
return `${userId}:${agentId}:${platformName}:access-suggestions`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
_rememberAccessSuggestions(userId, agentId, platformName, suggestions = []) {
|
|
181
|
+
if (!Array.isArray(suggestions) || suggestions.length === 0) return;
|
|
182
|
+
const key = this._accessSuggestionKey(userId, agentId, platformName);
|
|
183
|
+
const existing = this.accessSuggestions.get(key) || [];
|
|
184
|
+
const merged = [...suggestions, ...existing].filter((item) => item && item.rule && item.bucket);
|
|
185
|
+
const unique = [];
|
|
186
|
+
const seen = new Set();
|
|
187
|
+
for (const item of merged) {
|
|
188
|
+
const id = `${item.bucket}:${item.rule.scope}:${item.rule.value}`;
|
|
189
|
+
if (seen.has(id)) continue;
|
|
190
|
+
seen.add(id);
|
|
191
|
+
unique.push(item);
|
|
192
|
+
if (unique.length >= 24) break;
|
|
193
|
+
}
|
|
194
|
+
this.accessSuggestions.set(key, unique);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
_loadAccessPolicy(userId, agentId, platformName) {
|
|
198
|
+
const policyRow = this._setting(userId, agentId, accessPolicyKey(platformName));
|
|
199
|
+
const legacyRow = this._setting(userId, agentId, legacyWhitelistKey(platformName));
|
|
200
|
+
return parseStoredAccessPolicy(platformName, policyRow?.value, legacyRow?.value);
|
|
201
|
+
}
|
|
202
|
+
|
|
179
203
|
_scopedPlatformAuthDir(userId, agentId, platformName) {
|
|
180
204
|
return path.join(
|
|
181
205
|
AGENT_DATA_DIR,
|
|
@@ -249,6 +273,7 @@ class MessagingManager extends EventEmitter {
|
|
|
249
273
|
config = { ...(config || {}) };
|
|
250
274
|
config.userId = userId;
|
|
251
275
|
config.agentId = agentId;
|
|
276
|
+
config.accessPolicy = this._loadAccessPolicy(userId, agentId, platformName);
|
|
252
277
|
const PlatformClass = this.platformTypes[platformName];
|
|
253
278
|
if (!PlatformClass) throw new Error(`Unknown platform: ${platformName}`);
|
|
254
279
|
|
|
@@ -268,10 +293,6 @@ class MessagingManager extends EventEmitter {
|
|
|
268
293
|
}
|
|
269
294
|
};
|
|
270
295
|
|
|
271
|
-
const wlRow = this._setting(userId, agentId, 'platform_whitelist_telnyx');
|
|
272
|
-
if (wlRow) {
|
|
273
|
-
try { config.allowedNumbers = JSON.parse(wlRow.value); } catch { /* ignore */ }
|
|
274
|
-
}
|
|
275
296
|
const secretRow = this._setting(userId, agentId, 'platform_voice_secret_telnyx');
|
|
276
297
|
if (secretRow) {
|
|
277
298
|
try { config.voiceSecret = JSON.parse(secretRow.value); } catch { config.voiceSecret = secretRow.value; }
|
|
@@ -301,32 +322,6 @@ class MessagingManager extends EventEmitter {
|
|
|
301
322
|
config.voiceRuntimeManager = this.voiceRuntimeManager || null;
|
|
302
323
|
}
|
|
303
324
|
|
|
304
|
-
// Inject saved allowlists for platforms that enforce access in the adapter.
|
|
305
|
-
if (platformName === 'discord') {
|
|
306
|
-
const wlRow = this._setting(userId, agentId, 'platform_whitelist_discord');
|
|
307
|
-
if (wlRow) {
|
|
308
|
-
try { config.allowedIds = JSON.parse(wlRow.value); } catch { /* ignore */ }
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// For Telegram, inject saved allowedIds whitelist
|
|
313
|
-
if (platformName === 'telegram') {
|
|
314
|
-
const wlRow = this._setting(userId, agentId, 'platform_whitelist_telegram');
|
|
315
|
-
if (wlRow) {
|
|
316
|
-
try { config.allowedIds = JSON.parse(wlRow.value); } catch { /* ignore */ }
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (GENERIC_ALLOWLIST_PLATFORMS.has(platformName)) {
|
|
321
|
-
const wlRow = this._setting(userId, agentId, `platform_whitelist_${platformName}`);
|
|
322
|
-
if (wlRow) {
|
|
323
|
-
try {
|
|
324
|
-
const parsed = JSON.parse(wlRow.value);
|
|
325
|
-
if (Array.isArray(parsed)) config.allowedIds = parsed;
|
|
326
|
-
} catch { /* ignore */ }
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
325
|
const storedConfig = JSON.stringify(this._persistableConfig(config) || {});
|
|
331
326
|
|
|
332
327
|
const key = this._key(userId, agentId, platformName);
|
|
@@ -373,22 +368,26 @@ class MessagingManager extends EventEmitter {
|
|
|
373
368
|
|
|
374
369
|
// Telnyx-specific: blocked inbound caller notification
|
|
375
370
|
platform.on('blocked_caller', (info) => {
|
|
371
|
+
this._rememberAccessSuggestions(userId, agentId, platformName, info?.suggestions || []);
|
|
376
372
|
this.io.to(`user:${userId}`).emit('messaging:blocked_sender', {
|
|
377
373
|
platform: platformName,
|
|
378
374
|
sender: info.caller,
|
|
379
375
|
chatId: info.ccId,
|
|
380
|
-
senderName: null
|
|
376
|
+
senderName: null,
|
|
377
|
+
meta: info.meta || '',
|
|
378
|
+
suggestions: info.suggestions || null,
|
|
381
379
|
});
|
|
382
380
|
});
|
|
383
381
|
|
|
384
|
-
//
|
|
382
|
+
// Adapter-level blocked sender notification with suggestions
|
|
385
383
|
platform.on('blocked_sender', (info) => {
|
|
384
|
+
this._rememberAccessSuggestions(userId, agentId, platformName, info?.suggestions || []);
|
|
386
385
|
this.io.to(`user:${userId}`).emit('messaging:blocked_sender', {
|
|
387
386
|
platform: platformName,
|
|
388
387
|
sender: info.sender,
|
|
389
388
|
chatId: info.chatId,
|
|
390
389
|
senderName: info.senderName || null,
|
|
391
|
-
meta: info.guildName ? `Server: ${info.guildName}` : (info.groupName ? `Group: ${info.groupName}` : null),
|
|
390
|
+
meta: info.meta || (info.guildName ? `Server: ${info.guildName}` : (info.groupName ? `Group: ${info.groupName}` : null)),
|
|
392
391
|
suggestions: info.suggestions || null,
|
|
393
392
|
});
|
|
394
393
|
});
|
|
@@ -679,13 +678,88 @@ class MessagingManager extends EventEmitter {
|
|
|
679
678
|
};
|
|
680
679
|
}
|
|
681
680
|
|
|
681
|
+
getAccessPolicy(userId, platformName, options = {}) {
|
|
682
|
+
const agentId = this._agentId(userId, options);
|
|
683
|
+
return this._loadAccessPolicy(userId, agentId, platformName);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
setAccessPolicy(userId, platformName, policy, options = {}) {
|
|
687
|
+
const agentId = this._agentId(userId, options);
|
|
688
|
+
const normalized = normalizeAccessPolicy(platformName, policy);
|
|
689
|
+
this._upsertSetting(userId, agentId, accessPolicyKey(platformName), normalized);
|
|
690
|
+
const key = this._key(userId, agentId, platformName);
|
|
691
|
+
const platform = this.platforms.get(key);
|
|
692
|
+
if (platform?.setAccessPolicy) {
|
|
693
|
+
platform.setAccessPolicy(normalized);
|
|
694
|
+
}
|
|
695
|
+
return normalized;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
evaluateAccess(userId, platformName, context, options = {}) {
|
|
699
|
+
const agentId = this._agentId(userId, options);
|
|
700
|
+
const key = this._key(userId, agentId, platformName);
|
|
701
|
+
const platform = this.platforms.get(key);
|
|
702
|
+
if (platform?.evaluateAccess) {
|
|
703
|
+
return platform.evaluateAccess(context);
|
|
704
|
+
}
|
|
705
|
+
return evaluateAccessPolicy(
|
|
706
|
+
this._loadAccessPolicy(userId, agentId, platformName),
|
|
707
|
+
context,
|
|
708
|
+
platformName,
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
async getAccessCatalog(userId, platformName, options = {}) {
|
|
713
|
+
const agentId = this._agentId(userId, options);
|
|
714
|
+
const key = this._key(userId, agentId, platformName);
|
|
715
|
+
const platform = this.platforms.get(key);
|
|
716
|
+
let discoveredTargets = [];
|
|
717
|
+
if (platform?.listAccessTargets) {
|
|
718
|
+
discoveredTargets = await Promise.resolve(platform.listAccessTargets()).catch(() => []);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const recentRows = db.prepare(
|
|
722
|
+
`SELECT platform_chat_id, metadata
|
|
723
|
+
FROM messages
|
|
724
|
+
WHERE user_id = ? AND agent_id = ? AND platform = ? AND platform_chat_id IS NOT NULL
|
|
725
|
+
ORDER BY id DESC
|
|
726
|
+
LIMIT 40`
|
|
727
|
+
).all(userId, agentId, platformName);
|
|
728
|
+
const recentTargets = recentRows
|
|
729
|
+
.map((row) => {
|
|
730
|
+
let metadata = {};
|
|
731
|
+
try {
|
|
732
|
+
metadata = row.metadata ? JSON.parse(row.metadata) : {};
|
|
733
|
+
} catch {
|
|
734
|
+
metadata = {};
|
|
735
|
+
}
|
|
736
|
+
return classifyRecentTarget(platformName, { ...row, metadata });
|
|
737
|
+
})
|
|
738
|
+
.filter(Boolean);
|
|
739
|
+
|
|
740
|
+
const seen = new Set();
|
|
741
|
+
const unique = (items) => items.filter((item) => {
|
|
742
|
+
const keyValue = `${item.bucket}:${item.scope}:${item.value}`;
|
|
743
|
+
if (seen.has(keyValue)) return false;
|
|
744
|
+
seen.add(keyValue);
|
|
745
|
+
return true;
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
return {
|
|
749
|
+
capabilities: getPlatformAccessCapabilities(platformName),
|
|
750
|
+
discoveredTargets: unique([...(Array.isArray(discoveredTargets) ? discoveredTargets : []), ...recentTargets]),
|
|
751
|
+
suggestedTargets: unique(this.accessSuggestions.get(this._accessSuggestionKey(userId, agentId, platformName)) || []),
|
|
752
|
+
policy: this._loadAccessPolicy(userId, agentId, platformName),
|
|
753
|
+
summary: summarizeAccessPolicy(platformName, this._loadAccessPolicy(userId, agentId, platformName)),
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
|
|
682
757
|
/**
|
|
683
758
|
* Update the allowed-numbers list on a live Telnyx platform instance.
|
|
684
759
|
*/
|
|
685
760
|
updateTelnyxAllowedNumbers(userId, numbers, options = {}) {
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
if (platform?.setAllowedNumbers) platform.setAllowedNumbers(numbers);
|
|
761
|
+
const migrated = migrateLegacyWhitelist('telnyx', numbers);
|
|
762
|
+
return this.setAccessPolicy(userId, 'telnyx', migrated, options);
|
|
689
763
|
}
|
|
690
764
|
|
|
691
765
|
/**
|
|
@@ -710,10 +784,7 @@ class MessagingManager extends EventEmitter {
|
|
|
710
784
|
* Accepts prefixed strings: "user:ID", "guild:ID", "channel:ID"
|
|
711
785
|
*/
|
|
712
786
|
updateDiscordAllowedIds(userId, ids, options = {}) {
|
|
713
|
-
|
|
714
|
-
const platform = this.platforms.get(key);
|
|
715
|
-
if (platform?.setAllowedEntries) platform.setAllowedEntries(ids);
|
|
716
|
-
else if (platform?.setAllowedIds) platform.setAllowedIds(ids); // legacy fallback
|
|
787
|
+
return this.setAccessPolicy(userId, 'discord', migrateLegacyWhitelist('discord', ids), options);
|
|
717
788
|
}
|
|
718
789
|
|
|
719
790
|
/**
|
|
@@ -721,15 +792,11 @@ class MessagingManager extends EventEmitter {
|
|
|
721
792
|
* Accepts prefixed strings: "user:ID", "group:ID"
|
|
722
793
|
*/
|
|
723
794
|
updateTelegramAllowedIds(userId, ids, options = {}) {
|
|
724
|
-
|
|
725
|
-
const platform = this.platforms.get(key);
|
|
726
|
-
if (platform?.setAllowedEntries) platform.setAllowedEntries(ids);
|
|
795
|
+
return this.setAccessPolicy(userId, 'telegram', migrateLegacyWhitelist('telegram', ids), options);
|
|
727
796
|
}
|
|
728
797
|
|
|
729
798
|
updateAllowedEntries(userId, platformName, ids, options = {}) {
|
|
730
|
-
|
|
731
|
-
const platform = this.platforms.get(key);
|
|
732
|
-
if (platform?.setAllowedEntries) platform.setAllowedEntries(ids);
|
|
799
|
+
return this.setAccessPolicy(userId, platformName, migrateLegacyWhitelist(platformName, ids), options);
|
|
733
800
|
}
|
|
734
801
|
}
|
|
735
802
|
|