neoagent 2.5.2-beta.3 → 2.5.2-beta.5

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.
@@ -9,6 +9,7 @@ const {
9
9
  normalizeOutgoingMessageForPlatform,
10
10
  } = require('../messaging/formatting_guides');
11
11
  const { INTERIM_KINDS, normalizeInterimKind } = require('./interim');
12
+ const { normalizeWhatsAppId } = require('../../utils/whatsapp');
12
13
  const {
13
14
  executeIntegratedTool,
14
15
  getIntegratedToolDefinitions,
@@ -320,6 +321,31 @@ function normalizeMessagingTarget(target = {}) {
320
321
  return { platform, to };
321
322
  }
322
323
 
324
+ function canonicalMessagingAddress(platform, value) {
325
+ const normalizedPlatform = String(platform || '').trim().toLowerCase();
326
+ const raw = String(value || '').trim();
327
+ if (!normalizedPlatform || !raw) return '';
328
+ if (normalizedPlatform !== 'whatsapp') return raw;
329
+
330
+ const lower = raw.toLowerCase();
331
+ const normalizedId = normalizeWhatsAppId(lower);
332
+ if (!normalizedId) return '';
333
+ if (lower.includes('@g.us')) return `group:${normalizedId}`;
334
+ if (lower.includes('@lid')) return `lid:${normalizedId}`;
335
+ return `direct:${normalizedId}`;
336
+ }
337
+
338
+ function isOriginMessagingDelivery({ triggerSource, source, chatId, platform, to }) {
339
+ if (triggerSource !== 'messaging') return true;
340
+ const originPlatform = String(source || '').trim().toLowerCase();
341
+ const targetPlatform = String(platform || '').trim().toLowerCase();
342
+ if (!originPlatform || !targetPlatform || originPlatform !== targetPlatform) return false;
343
+
344
+ const originAddress = canonicalMessagingAddress(originPlatform, chatId);
345
+ const targetAddress = canonicalMessagingAddress(targetPlatform, to);
346
+ return Boolean(originAddress && targetAddress && originAddress === targetAddress);
347
+ }
348
+
323
349
  function buildAndroidUiMatchProperties(extra = {}) {
324
350
  return {
325
351
  x: { type: 'number', description: 'Absolute X coordinate' },
@@ -1623,6 +1649,7 @@ async function executeTool(toolName, args, context, engine) {
1623
1649
  case 'browser_extract': {
1624
1650
  const { provider, backend } = await bc();
1625
1651
  if (!provider) return { error: 'Browser controller not available' };
1652
+ if (!args.selector) return { error: 'browser_extract requires a "selector" argument' };
1626
1653
  return { ...await provider.extract(args.selector, args.attribute, args.all), backend };
1627
1654
  }
1628
1655
 
@@ -1635,7 +1662,9 @@ async function executeTool(toolName, args, context, engine) {
1635
1662
  case 'browser_evaluate': {
1636
1663
  const { provider, backend } = await bc();
1637
1664
  if (!provider) return { error: 'Browser controller not available' };
1638
- return { ...await provider.evaluate(args.script), backend };
1665
+ const script = args.script ?? args.javascript;
1666
+ if (!script) return { error: 'browser_evaluate requires a "script" argument' };
1667
+ return { ...await provider.evaluate(script), backend };
1639
1668
  }
1640
1669
 
1641
1670
  case 'android_start_emulator': {
@@ -2244,7 +2273,18 @@ async function executeTool(toolName, args, context, engine) {
2244
2273
  persistConversation: triggerSource === 'schedule' || triggerSource === 'tasks'
2245
2274
  });
2246
2275
  // Track that the agent explicitly sent a message during this run
2247
- if (!suppressReply && sendResult?.suppressed !== true) {
2276
+ if (
2277
+ !suppressReply
2278
+ && sendResult?.success === true
2279
+ && sendResult?.suppressed !== true
2280
+ && isOriginMessagingDelivery({
2281
+ triggerSource,
2282
+ source: context.source,
2283
+ chatId: context.chatId,
2284
+ platform: args.platform,
2285
+ to: args.to,
2286
+ })
2287
+ ) {
2248
2288
  markProactiveMessageSent({ runState, deliveryState, content: normalizedMessage });
2249
2289
  if (runState && triggerSource === 'messaging') {
2250
2290
  runState.explicitMessageSent = true;
@@ -515,6 +515,13 @@ class MessagingManager extends EventEmitter {
515
515
  }
516
516
 
517
517
  const result = await platform.sendMessage(to, normalizedContent, sendOptions);
518
+ if (result?.success === false) {
519
+ const reason = result.error || result.reason || 'platform rejected the message';
520
+ const error = new Error(`Platform ${platformName} delivery failed: ${reason}`);
521
+ error.code = 'MESSAGING_DELIVERY_FAILED';
522
+ error.deliveryResult = result;
523
+ throw error;
524
+ }
518
525
 
519
526
  db.prepare('INSERT INTO messages (user_id, agent_id, run_id, role, content, platform, platform_chat_id, media_path, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)')
520
527
  .run(userId, agentId, runId, 'assistant', normalizedContent, platformName, to, mediaPath, metadata ? JSON.stringify(metadata) : null);
@@ -250,8 +250,8 @@ class VmBrowserProvider {
250
250
  async typeText(text, options = {}) { return this.#materialize(await this.client.request('POST', '/browser/type-text', { text, ...options })); }
251
251
  async pressKey(key, screenshot = true) { return this.#materialize(await this.client.request('POST', '/browser/press-key', { key, screenshot })); }
252
252
  async scroll(deltaX, deltaY, screenshot = true) { return this.#materialize(await this.client.request('POST', '/browser/scroll', { deltaX, deltaY, screenshot })); }
253
- extract(selector, attribute, all = false) { return this.client.request('POST', '/browser/extract', { selector, attribute, all }); }
254
- evaluate(script) { return this.client.request('POST', '/browser/execute', { code: script }); }
253
+ async extract(selector, attribute, all = false) { return this.client.request('POST', '/browser/extract', { selector, attribute, all }); }
254
+ async evaluate(script) { return this.client.request('POST', '/browser/execute', { code: script }); }
255
255
  async screenshot(options = {}) { return this.#materialize(await this.client.request('POST', '/browser/screenshot', options)); }
256
256
  async screenshotJpeg(quality = 80, options = {}) {
257
257
  const result = await this.client.request('POST', '/browser/screenshot-jpeg', { ...options, quality });
@@ -259,11 +259,11 @@ class VmBrowserProvider {
259
259
  if (!content) throw new Error('VM browser screenshot-jpeg returned no data.');
260
260
  return Buffer.from(content, 'base64');
261
261
  }
262
- launch(options = {}) { return this.client.request('POST', '/browser/launch', options); }
263
- closeBrowser() { return this.client.request('POST', '/browser/close'); }
264
- fill(selector, value) { return this.type(selector, value); }
265
- extractContent(options = {}) { return this.client.request('POST', '/browser/extract', options); }
266
- executeJS(code) { return this.evaluate(code); }
262
+ async launch(options = {}) { return this.client.request('POST', '/browser/launch', options); }
263
+ async closeBrowser() { return this.client.request('POST', '/browser/close'); }
264
+ async fill(selector, value) { return this.type(selector, value); }
265
+ async extractContent(options = {}) { return this.client.request('POST', '/browser/extract', options); }
266
+ async executeJS(code) { return this.evaluate(code); }
267
267
  async getPageInfo() {
268
268
  const status = await this.client.request('GET', '/browser/status');
269
269
  this.headless = status?.headless !== false;