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.
@@ -19,6 +19,8 @@ class TelegramPlatform extends BasePlatform {
19
19
  this._contextBuffers = new Map();
20
20
  this._contextMaxSize = 25;
21
21
  this._contextMaxChats = 200;
22
+ this._seenUsers = new Map();
23
+ this._seenGroups = new Map();
22
24
  }
23
25
 
24
26
  async connect() {
@@ -91,6 +93,8 @@ class TelegramPlatform extends BasePlatform {
91
93
  this.status = 'disconnected';
92
94
  this._botUser = null;
93
95
  this._contextBuffers.clear();
96
+ this._seenUsers.clear();
97
+ this._seenGroups.clear();
94
98
  this.emit('disconnected', { manual: true });
95
99
  }
96
100
 
@@ -98,25 +102,6 @@ class TelegramPlatform extends BasePlatform {
98
102
  getStatus() { return this.status; }
99
103
  getAuthInfo() { return this._botUser ? { username: this._botUser.username, id: this._botUser.id } : null; }
100
104
 
101
- _checkAccess(msg) {
102
- const userId = String(msg.from.id);
103
- const chatId = String(msg.chat.id);
104
- const isPrivate = msg.chat.type === 'private';
105
- const userAllowed = super._checkAccess(`user:${userId}`) || super._checkAccess(userId);
106
-
107
- if (isPrivate) {
108
- return {
109
- allowed: this.allowedEntries.size === 0 || userAllowed,
110
- requireMention: false,
111
- };
112
- }
113
-
114
- return {
115
- allowed: userAllowed || super._checkAccess(`group:${chatId}`),
116
- requireMention: true,
117
- };
118
- }
119
-
120
105
  _isMentioned(msg) {
121
106
  if (!this._botUser) return false;
122
107
  const text = msg.text || msg.caption || '';
@@ -195,6 +180,10 @@ class TelegramPlatform extends BasePlatform {
195
180
  const senderDisplayName = [msg.from.first_name, msg.from.last_name].filter(Boolean).join(' ')
196
181
  || senderUsername || userId;
197
182
  const senderName = senderDisplayName;
183
+ this._seenUsers.set(userId, senderDisplayName);
184
+ if (!isPrivate) {
185
+ this._seenGroups.set(rawChatId, msg.chat.title || rawChatId);
186
+ }
198
187
 
199
188
  this._addToContext(rawChatId, {
200
189
  author: senderName,
@@ -202,30 +191,30 @@ class TelegramPlatform extends BasePlatform {
202
191
  mine: false,
203
192
  });
204
193
 
205
- const { allowed, requireMention } = this._checkAccess(msg);
206
-
207
- if (requireMention && !this._isMentioned(msg)) return;
208
-
209
- if (!allowed) {
210
- const suggestions = [
211
- { label: `Add user (${senderName})`, prefixedId: `user:${userId}` },
212
- ];
213
- if (!isPrivate) suggestions.push({
214
- label: `Add group (${msg.chat.title || rawChatId})`,
215
- prefixedId: `group:${rawChatId}`,
216
- });
194
+ const access = this._checkInboundAccess({
195
+ platform: 'telegram',
196
+ senderId: userId,
197
+ chatId: outputChatId,
198
+ isDirect: isPrivate,
199
+ isShared: !isPrivate,
200
+ groupId: !isPrivate ? rawChatId : '',
201
+ channelId: '',
202
+ serverId: '',
203
+ roomId: '',
204
+ roleIds: [],
205
+ phoneNumber: '',
206
+ wasMentioned: isPrivate ? false : this._isMentioned(msg),
207
+ }, {
208
+ senderName,
209
+ meta: msg.chat.title ? `Group: ${msg.chat.title}` : '',
210
+ groupLabel: msg.chat.title || rawChatId,
211
+ });
217
212
 
218
- this.emit('blocked_sender', {
219
- sender: userId,
220
- chatId: outputChatId,
221
- senderName,
222
- groupName: msg.chat.title || null,
223
- suggestions,
224
- });
213
+ if (!access.allowed) {
225
214
  return;
226
215
  }
227
216
 
228
- let content = requireMention ? this._stripMention(text) : text;
217
+ let content = (!isPrivate && this._isMentioned(msg)) ? this._stripMention(text) : text;
229
218
  if (!content && msg.photo) content = `[photo]`;
230
219
  if (!content && msg.document) content = `[document: ${msg.document.file_name || 'file'}]`;
231
220
  if (!content) return;
@@ -234,7 +223,7 @@ class TelegramPlatform extends BasePlatform {
234
223
  ? senderName
235
224
  : `${senderName} in ${msg.chat.title || rawChatId}`;
236
225
 
237
- const channelContext = (!isPrivate && requireMention) ? this._getContext(rawChatId) : null;
226
+ const channelContext = (!isPrivate && this._isMentioned(msg)) ? this._getContext(rawChatId) : null;
238
227
 
239
228
  this.emit('message', {
240
229
  platform: 'telegram',
@@ -282,6 +271,31 @@ class TelegramPlatform extends BasePlatform {
282
271
  await this._bot.telegram.sendChatAction(id, 'typing');
283
272
  } catch {}
284
273
  }
274
+
275
+ async listAccessTargets() {
276
+ const targets = [];
277
+ for (const [userId, label] of this._seenUsers.entries()) {
278
+ targets.push({
279
+ source: 'live',
280
+ bucket: 'directRules',
281
+ scope: 'user',
282
+ value: userId,
283
+ label,
284
+ subtitle: 'Telegram user',
285
+ });
286
+ }
287
+ for (const [groupId, label] of this._seenGroups.entries()) {
288
+ targets.push({
289
+ source: 'live',
290
+ bucket: 'sharedSpaceRules',
291
+ scope: 'group',
292
+ value: groupId,
293
+ label,
294
+ subtitle: 'Telegram group',
295
+ });
296
+ }
297
+ return targets;
298
+ }
285
299
  }
286
300
 
287
301
  module.exports = { TelegramPlatform };
@@ -39,9 +39,7 @@ class TelnyxVoicePlatform extends BasePlatform {
39
39
  this.ttsVoice = resolveTtsVoice(this.ttsProvider, config.ttsVoice);
40
40
  this.ttsModel = resolveTtsModel(this.ttsProvider, config.ttsModel);
41
41
  this.sttModel = resolveSttModel(this.sttProvider, config.sttModel);
42
- this.allowedNumbers = Array.isArray(config.allowedNumbers)
43
- ? config.allowedNumbers
44
- : [];
42
+ this.allowedNumbers = Array.isArray(this.allowedNumbers) ? this.allowedNumbers : [];
45
43
  this.voiceSecret = String(config.voiceSecret || '').replace(/\D/g, '');
46
44
  this.voiceRuntimeManager = config.voiceRuntimeManager || null;
47
45
  this.userId = config.userId || null;
@@ -110,8 +108,21 @@ class TelnyxVoicePlatform extends BasePlatform {
110
108
  getAuthInfo() { return { phoneNumber: this.phoneNumber }; }
111
109
 
112
110
  setAllowedNumbers(numbers) {
113
- this.allowedNumbers = Array.isArray(numbers) ? numbers : [];
114
- console.log(`[TelnyxVoice] Whitelist updated: ${this.allowedNumbers.length} number(s)`);
111
+ return this.setAccessPolicy({
112
+ directPolicy: Array.isArray(numbers) && numbers.length > 0 ? 'allowlist' : 'disabled',
113
+ directRules: (Array.isArray(numbers) ? numbers : []).map((value) => ({
114
+ scope: 'phone_number',
115
+ value,
116
+ })),
117
+ });
118
+ }
119
+
120
+ setAccessPolicy(policy) {
121
+ const normalized = super.setAccessPolicy(policy);
122
+ this.allowedNumbers = normalized.directRules
123
+ .filter((rule) => rule.scope === 'phone_number')
124
+ .map((rule) => rule.value);
125
+ return normalized;
115
126
  }
116
127
 
117
128
  setVoiceSecret(secret) {
@@ -160,13 +171,20 @@ class TelnyxVoicePlatform extends BasePlatform {
160
171
  }
161
172
 
162
173
  _isAllowed(number) {
163
- if (!this.allowedNumbers || !this.allowedNumbers.length) return false;
164
- const normalize = (n) => n.replace(/\D/g, '');
165
- const cn = normalize(number);
166
- return this.allowedNumbers.some(wl => {
167
- const cw = normalize(wl);
168
- return cn === cw || cn.endsWith(cw) || cw.endsWith(cn);
169
- });
174
+ return this.evaluateAccess({
175
+ platform: 'telnyx',
176
+ senderId: this._normalizeNumber(number),
177
+ chatId: this._normalizeNumber(number),
178
+ isDirect: true,
179
+ isShared: false,
180
+ groupId: '',
181
+ channelId: '',
182
+ serverId: '',
183
+ roomId: '',
184
+ roleIds: [],
185
+ phoneNumber: this._normalizeNumber(number),
186
+ wasMentioned: false,
187
+ }).allowed;
170
188
  }
171
189
 
172
190
  _normalizeNumber(n) {
@@ -414,16 +432,27 @@ class TelnyxVoicePlatform extends BasePlatform {
414
432
  if (payload.direction !== 'incoming') break;
415
433
  const caller = payload.from;
416
434
  if (!this._isAllowed(caller)) {
435
+ const blockedInfo = {
436
+ caller,
437
+ ccId,
438
+ meta: 'Caller is not allowed by the access policy.',
439
+ suggestions: [{
440
+ label: `Allow number (${caller})`,
441
+ prefixedId: caller,
442
+ bucket: 'directRules',
443
+ rule: { scope: 'phone_number', value: this._normalizeNumber(caller) },
444
+ }],
445
+ };
417
446
  if (this._isBanned(caller)) {
418
447
  console.log(`[TelnyxVoice] Rejecting banned caller: ${caller}`);
419
448
  await this._rejectCall(ccId);
420
- this.emit('blocked_caller', { caller, ccId });
449
+ this.emit('blocked_caller', blockedInfo);
421
450
  break;
422
451
  }
423
452
  if (!this.voiceSecret) {
424
453
  console.log(`[TelnyxVoice] Blocked non-whitelisted caller (no secret set): ${caller}`);
425
454
  await this._rejectCall(ccId);
426
- this.emit('blocked_caller', { caller, ccId });
455
+ this.emit('blocked_caller', blockedInfo);
427
456
  break;
428
457
  }
429
458
  console.log(`[TelnyxVoice] Non-whitelisted caller ${caller} — awaiting secret code`);
@@ -341,6 +341,33 @@ class WhatsAppPlatform extends BasePlatform {
341
341
  }
342
342
  }
343
343
 
344
+ async listAccessTargets() {
345
+ const [contacts, chats] = await Promise.all([
346
+ this.getContacts(),
347
+ this.getChats(),
348
+ ]);
349
+ return [
350
+ ...contacts
351
+ .filter((item) => !item.isGroup)
352
+ .map((item) => ({
353
+ source: 'live',
354
+ bucket: 'directRules',
355
+ scope: 'phone_number',
356
+ value: String(item.id || '').split('@')[0],
357
+ label: item.name || String(item.id || '').split('@')[0],
358
+ subtitle: 'WhatsApp contact',
359
+ })),
360
+ ...chats.map((chat) => ({
361
+ source: 'live',
362
+ bucket: 'sharedSpaceRules',
363
+ scope: 'group',
364
+ value: chat.id,
365
+ label: chat.name || chat.id,
366
+ subtitle: 'WhatsApp group',
367
+ })),
368
+ ];
369
+ }
370
+
344
371
  getAuthInfo() {
345
372
  return { qrCode: this.qrCode, status: this.status };
346
373
  }