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.
- package/package.json +1 -1
- package/server/public/.last_build_id +1 -1
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +4 -4
- package/server/services/ai/deliverables/artifact_helpers.js +1 -0
- package/server/services/ai/engine.js +343 -599
- package/server/services/ai/tools.js +42 -2
- package/server/services/messaging/manager.js +7 -0
- package/server/services/runtime/backends/local-vm.js +7 -7
|
@@ -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
|
-
|
|
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 (
|
|
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;
|