neoagent 2.4.1-beta.31 → 2.4.1-beta.33

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.4.1-beta.31",
3
+ "version": "2.4.1-beta.33",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "AGPL-3.0-only",
6
6
  "main": "server/index.js",
@@ -1 +1 @@
1
- 2351b2d608550a07f9f6f1166c4e2dd1
1
+ 49442e542f5b85df16e4fff2751cb6a3
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"c416acfeb8126e097f758c664aaa3da929e27d
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "2374886575" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "2270904145" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });
@@ -133653,7 +133653,7 @@ r===$&&A.b()
133653
133653
  o.push(A.jM(p,A.j9(!1,new A.a2(B.un,A.ds(new A.cB(B.ho,new A.a7n(r,p),p),p,p),p),!1,B.H,!0),p,p,0,0,0,p))}r=!1
133654
133654
  if(!s.ay)if(!s.ch){r=s.e
133655
133655
  r===$&&A.b()
133656
- r=B.b.t("mpx7ced1-e804d27").length!==0&&r.b}if(r){r=s.d
133656
+ r=B.b.t("mpzv8963-3cb08c9").length!==0&&r.b}if(r){r=s.d
133657
133657
  r===$&&A.b()
133658
133658
  r=r.av&&!r.aP?84:0
133659
133659
  q=s.e
@@ -139137,7 +139137,7 @@ $S:0}
139137
139137
  A.ZM.prototype={}
139138
139138
  A.Su.prototype={
139139
139139
  nc(a){var s=this
139140
- if(B.b.t("mpx7ced1-e804d27").length===0||s.a!=null)return
139140
+ if(B.b.t("mpzv8963-3cb08c9").length===0||s.a!=null)return
139141
139141
  s.AM()
139142
139142
  s.a=A.qe(B.R2,new A.b9L(s))},
139143
139143
  AM(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f
@@ -139155,7 +139155,7 @@ if(!t.f.b(k)){s=1
139155
139155
  break}i=J.T(k,"buildId")
139156
139156
  h=i==null?null:B.b.t(J.p(i))
139157
139157
  j=h==null?"":h
139158
- if(J.bf(j)===0||J.d(j,"mpx7ced1-e804d27")){s=1
139158
+ if(J.bf(j)===0||J.d(j,"mpzv8963-3cb08c9")){s=1
139159
139159
  break}n.b=!0
139160
139160
  n.D()
139161
139161
  p=2
@@ -139172,7 +139172,7 @@ case 2:return A.i(o.at(-1),r)}})
139172
139172
  return A.k($async$AM,r)},
139173
139173
  vu(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1
139174
139174
  var $async$vu=A.h(function(a2,a3){if(a2===1){o.push(a3)
139175
- s=p}for(;;)switch(s){case 0:if(B.b.t("mpx7ced1-e804d27").length===0||n.c){s=1
139175
+ s=p}for(;;)switch(s){case 0:if(B.b.t("mpzv8963-3cb08c9").length===0||n.c){s=1
139176
139176
  break}n.c=!0
139177
139177
  n.D()
139178
139178
  p=4
@@ -274,6 +274,33 @@ function markProactiveMessageSent({ runState, deliveryState, content }) {
274
274
  }
275
275
  }
276
276
 
277
+ function normalizeStoredSettingString(value) {
278
+ if (value == null) return '';
279
+ if (typeof value !== 'string') return String(value || '').trim();
280
+ let current = value.trim();
281
+ for (let i = 0; i < 2; i += 1) {
282
+ if (!current) return '';
283
+ try {
284
+ const parsed = JSON.parse(current);
285
+ if (typeof parsed === 'string') {
286
+ current = parsed.trim();
287
+ continue;
288
+ }
289
+ return '';
290
+ } catch {
291
+ return current;
292
+ }
293
+ }
294
+ return current;
295
+ }
296
+
297
+ function normalizeMessagingTarget(target = {}) {
298
+ const platform = normalizeStoredSettingString(target.platform);
299
+ const to = normalizeStoredSettingString(target.to);
300
+ if (!platform || !to) return null;
301
+ return { platform, to };
302
+ }
303
+
277
304
  function buildAndroidUiMatchProperties(extra = {}) {
278
305
  return {
279
306
  x: { type: 'number', description: 'Absolute X coordinate' },
@@ -2323,8 +2350,8 @@ async function executeTool(toolName, args, context, engine) {
2323
2350
  || null
2324
2351
  );
2325
2352
  const loadDefaultTarget = () => ({
2326
- platform: loadAgentSetting('last_platform'),
2327
- to: loadAgentSetting('last_chat_id')
2353
+ platform: normalizeStoredSettingString(loadAgentSetting('last_platform')),
2354
+ to: normalizeStoredSettingString(loadAgentSetting('last_chat_id'))
2328
2355
  });
2329
2356
 
2330
2357
  let taskConfig = null;
@@ -2336,26 +2363,43 @@ async function executeTool(toolName, args, context, engine) {
2336
2363
  try {
2337
2364
  taskConfig = JSON.parse(task.task_config || '{}');
2338
2365
  taskTarget = {
2339
- platform: taskConfig.notifyPlatform || null,
2340
- to: taskConfig.notifyTo || null
2366
+ platform: normalizeStoredSettingString(taskConfig.notifyPlatform),
2367
+ to: normalizeStoredSettingString(taskConfig.notifyTo)
2341
2368
  };
2342
2369
  } catch { }
2343
2370
  }
2344
2371
  }
2345
2372
 
2346
2373
  const fallbackTarget = loadDefaultTarget();
2374
+ const recentTargets = db.prepare(
2375
+ `SELECT platform, platform_chat_id
2376
+ FROM messages
2377
+ WHERE user_id = ?
2378
+ AND agent_id = ?
2379
+ AND platform IS NOT NULL
2380
+ AND platform_chat_id IS NOT NULL
2381
+ ORDER BY id DESC
2382
+ LIMIT 20`
2383
+ ).all(userId, agentId);
2347
2384
  const candidateTargets = [];
2348
2385
  const seenTargets = new Set();
2349
2386
  const addCandidate = (target) => {
2350
- if (!target?.platform || !target?.to) return;
2351
- const key = `${target.platform}:${target.to}`;
2387
+ const normalizedTarget = normalizeMessagingTarget(target);
2388
+ if (!normalizedTarget) return;
2389
+ const key = `${normalizedTarget.platform}:${normalizedTarget.to}`;
2352
2390
  if (seenTargets.has(key)) return;
2353
2391
  seenTargets.add(key);
2354
- candidateTargets.push(target);
2392
+ candidateTargets.push(normalizedTarget);
2355
2393
  };
2356
2394
 
2357
2395
  addCandidate(taskTarget);
2358
2396
  addCandidate(fallbackTarget);
2397
+ for (const row of recentTargets) {
2398
+ addCandidate({
2399
+ platform: row.platform,
2400
+ to: row.platform_chat_id
2401
+ });
2402
+ }
2359
2403
 
2360
2404
  if (candidateTargets.length === 0) {
2361
2405
  throw new Error('No messaging target is configured for this task run. Connect a platform and send at least one message on this server, or recreate the task after reconnecting.');
@@ -99,9 +99,34 @@ function adaptSpokenFormatting(text) {
99
99
  .trim();
100
100
  }
101
101
 
102
+ function coerceMessageContent(content) {
103
+ if (typeof content === 'string') return content;
104
+ if (content == null) return '';
105
+ if (typeof content === 'number' || typeof content === 'boolean' || typeof content === 'bigint') {
106
+ return String(content);
107
+ }
108
+ if (typeof content !== 'object') return String(content || '');
109
+
110
+ for (const key of ['text', 'content', 'message', 'summary']) {
111
+ if (typeof content[key] === 'string' && content[key].trim()) {
112
+ return content[key];
113
+ }
114
+ }
115
+
116
+ if (content.result != null && content.result !== content) {
117
+ return coerceMessageContent(content.result);
118
+ }
119
+
120
+ try {
121
+ return JSON.stringify(content, null, 2);
122
+ } catch {
123
+ return String(content || '');
124
+ }
125
+ }
126
+
102
127
  function normalizeOutgoingMessageForPlatform(platform, content, options = {}) {
103
128
  const profile = getPlatformFormattingProfile(platform);
104
- let text = String(content || '');
129
+ let text = coerceMessageContent(content);
105
130
 
106
131
  if (options.stripNoResponseMarker !== false) {
107
132
  text = text.replace(/\[NO RESPONSE\]/gi, '');
@@ -104,6 +104,9 @@ class MessagingManager extends EventEmitter {
104
104
  return null;
105
105
  }
106
106
 
107
+ const normalizedIncomingContent = normalizeOutgoingMessageForPlatform(platformName, msg?.content, {
108
+ stripNoResponseMarker: false
109
+ });
107
110
  const agentId = this._agentId(userId, {
108
111
  ...options,
109
112
  agentId: options?.agentId ?? msg?.agentId ?? null,
@@ -135,7 +138,7 @@ class MessagingManager extends EventEmitter {
135
138
  userId,
136
139
  agentId,
137
140
  'user',
138
- msg.content,
141
+ normalizedIncomingContent,
139
142
  platformName,
140
143
  msg.messageId,
141
144
  msg.chatId,
@@ -143,7 +146,7 @@ class MessagingManager extends EventEmitter {
143
146
  msg.timestamp,
144
147
  );
145
148
 
146
- const enrichedMsg = { ...msg, agentId, platform: platformName };
149
+ const enrichedMsg = { ...msg, content: normalizedIncomingContent, agentId, platform: platformName };
147
150
 
148
151
  if (this.isShuttingDown) {
149
152
  return enrichedMsg;
@@ -13,11 +13,58 @@ const { TaskRepository } = require('./task_repository');
13
13
  const { TriggerRegistry } = require('./trigger_registry');
14
14
  const scheduleAdapter = require('./adapters/schedule');
15
15
  const { normalizeJsonObject } = require('./utils');
16
+ const { normalizeOutgoingMessageForPlatform } = require('../messaging/formatting_guides');
16
17
 
17
18
  const MAX_AUTONOMOUS_RETRIES = 1;
18
19
  const MAX_RECURRING_TASK_START_DELAY_MS = 90 * 1000;
19
20
  const INTEGRATION_TRIGGER_POLL_CRON = '* * * * *';
20
21
 
22
+ function normalizeStoredString(value) {
23
+ if (value == null) return '';
24
+ if (typeof value !== 'string') return String(value || '').trim();
25
+ let current = value.trim();
26
+ for (let i = 0; i < 2; i += 1) {
27
+ if (!current) return '';
28
+ try {
29
+ const parsed = JSON.parse(current);
30
+ if (typeof parsed === 'string') {
31
+ current = parsed.trim();
32
+ continue;
33
+ }
34
+ return '';
35
+ } catch {
36
+ return current;
37
+ }
38
+ }
39
+ return current;
40
+ }
41
+
42
+ function normalizeNotifyTarget(target = {}) {
43
+ const platform = normalizeStoredString(target.platform);
44
+ const to = normalizeStoredString(target.to);
45
+ if (!platform || !to) return null;
46
+ return { platform, to };
47
+ }
48
+
49
+ function stringifyTaskResult(result) {
50
+ if (typeof result === 'string') return result;
51
+ if (result == null) return '';
52
+ if (typeof result !== 'object') return String(result);
53
+
54
+ for (const key of ['content', 'message', 'text', 'summary', 'finalResponse', 'final_response']) {
55
+ if (typeof result[key] === 'string' && result[key].trim()) {
56
+ return result[key];
57
+ }
58
+ }
59
+
60
+ if (result.result != null && result.result !== result) {
61
+ const nested = stringifyTaskResult(result.result);
62
+ if (nested) return nested;
63
+ }
64
+
65
+ return '';
66
+ }
67
+
21
68
  class TaskRuntime {
22
69
  constructor(io, agentEngine, app = null) {
23
70
  this.io = io;
@@ -343,6 +390,17 @@ class TaskRuntime {
343
390
  const result = typeof this.agentEngine.runWithModel === 'function'
344
391
  ? await this.agentEngine.runWithModel(userId, finalPrompt, runOptions, normalizedConfig.model || null)
345
392
  : await this.agentEngine.run(userId, finalPrompt, runOptions);
393
+ const fallbackDelivery = await this._deliverTaskResultIfNeeded({
394
+ userId,
395
+ agentId,
396
+ taskId,
397
+ taskConfig: normalizedConfig,
398
+ result,
399
+ deliveryState,
400
+ });
401
+ if (fallbackDelivery && result && typeof result === 'object') {
402
+ result.taskDelivery = fallbackDelivery;
403
+ }
346
404
  this.io.to(`user:${userId}`).emit('tasks:task_complete', { taskId, result });
347
405
  return result;
348
406
  } catch (err) {
@@ -362,7 +420,11 @@ class TaskRuntime {
362
420
  }
363
421
  } catch (err) {
364
422
  console.error(`[Tasks] Task ${taskId} error:`, err.message);
365
- this.io.to(`user:${userId}`).emit('tasks:task_error', { taskId, error: err.message });
423
+ this.io.to(`user:${userId}`).emit('tasks:task_skipped', {
424
+ taskId,
425
+ reason: 'execution_failed',
426
+ timestamp: new Date().toISOString(),
427
+ });
366
428
  return { skipped: false, error: err.message };
367
429
  }
368
430
  }
@@ -486,25 +548,134 @@ class TaskRuntime {
486
548
 
487
549
  _getDefaultNotifyTarget(userId, agentId = null) {
488
550
  const scopedAgentId = resolveAgentId(userId, agentId);
489
- return {
551
+ return normalizeNotifyTarget({
490
552
  platform: this._getAgentSetting(userId, scopedAgentId, 'last_platform'),
491
553
  to: this._getAgentSetting(userId, scopedAgentId, 'last_chat_id'),
492
- };
554
+ });
555
+ }
556
+
557
+ _buildNotifyTargets(userId, agentId, taskConfig = {}) {
558
+ const scopedAgentId = resolveAgentId(userId, agentId);
559
+ const candidates = [
560
+ normalizeNotifyTarget({
561
+ platform: taskConfig.notifyPlatform,
562
+ to: taskConfig.notifyTo,
563
+ }),
564
+ this._getDefaultNotifyTarget(userId, scopedAgentId),
565
+ ...this.taskRepository.listRecentMessageTargets(userId, scopedAgentId).map((row) => normalizeNotifyTarget({
566
+ platform: row.platform,
567
+ to: row.platform_chat_id,
568
+ })),
569
+ ];
570
+
571
+ const unique = [];
572
+ const seen = new Set();
573
+ for (const target of candidates) {
574
+ if (!target) continue;
575
+ const key = `${target.platform}:${target.to}`;
576
+ if (seen.has(key)) continue;
577
+ seen.add(key);
578
+ unique.push(target);
579
+ }
580
+ return unique;
493
581
  }
494
582
 
495
583
  _ensureDefaultNotifyTarget(userId, agentId, taskConfig, taskId) {
496
584
  const normalized = { ...taskConfig };
497
- if (!normalized.callTo && (!normalized.notifyPlatform || !normalized.notifyTo)) {
498
- const notifyTarget = this._getDefaultNotifyTarget(userId, agentId);
499
- if (notifyTarget.platform && notifyTarget.to) {
585
+ const existingTarget = normalizeNotifyTarget({
586
+ platform: normalized.notifyPlatform,
587
+ to: normalized.notifyTo,
588
+ });
589
+ if (existingTarget) {
590
+ normalized.notifyPlatform = existingTarget.platform;
591
+ normalized.notifyTo = existingTarget.to;
592
+ }
593
+ if (!normalized.callTo && !existingTarget) {
594
+ const notifyTarget = this._buildNotifyTargets(userId, agentId, normalized)[0];
595
+ if (notifyTarget) {
500
596
  normalized.notifyPlatform = notifyTarget.platform;
501
597
  normalized.notifyTo = notifyTarget.to;
502
- this.taskRepository.updateTaskConfig(taskId, userId, normalized);
503
598
  }
504
599
  }
600
+ if (
601
+ normalized.notifyPlatform !== taskConfig.notifyPlatform
602
+ || normalized.notifyTo !== taskConfig.notifyTo
603
+ ) {
604
+ this.taskRepository.updateTaskConfig(taskId, userId, normalized);
605
+ }
505
606
  return normalized;
506
607
  }
507
608
 
609
+ async _deliverTaskResultIfNeeded({
610
+ userId,
611
+ agentId,
612
+ taskId,
613
+ taskConfig,
614
+ result,
615
+ deliveryState,
616
+ }) {
617
+ if (deliveryState?.messagingSent || taskConfig.callTo) return null;
618
+ const manager = this.app?.locals?.messagingManager || this.agentEngine?.messagingManager || null;
619
+ if (!manager) return null;
620
+
621
+ const targets = this._buildNotifyTargets(userId, agentId, taskConfig);
622
+ if (!targets.length) return null;
623
+
624
+ let lastError = null;
625
+ for (const target of targets) {
626
+ const message = normalizeOutgoingMessageForPlatform(
627
+ target.platform,
628
+ stringifyTaskResult(result),
629
+ { stripNoResponseMarker: false },
630
+ );
631
+ if (!message || message.toUpperCase() === '[NO RESPONSE]') return null;
632
+
633
+ const status = typeof manager.getPlatformStatus === 'function'
634
+ ? manager.getPlatformStatus(userId, target.platform, { agentId })
635
+ : null;
636
+ if (!status || status.status !== 'connected') {
637
+ lastError = new Error(`Platform ${target.platform} is not connected on this server.`);
638
+ continue;
639
+ }
640
+
641
+ try {
642
+ const sendResult = await manager.sendMessage(userId, target.platform, target.to, message, {
643
+ agentId,
644
+ runId: result?.runId || null,
645
+ persistConversation: true,
646
+ });
647
+ deliveryState.messagingSent = true;
648
+ deliveryState.lastSentMessage = message;
649
+ if (!Array.isArray(deliveryState.sentMessages)) {
650
+ deliveryState.sentMessages = [];
651
+ }
652
+ deliveryState.sentMessages.push(message);
653
+
654
+ if (taskConfig.notifyPlatform !== target.platform || taskConfig.notifyTo !== target.to) {
655
+ this.taskRepository.updateTaskConfig(taskId, userId, {
656
+ ...taskConfig,
657
+ notifyPlatform: target.platform,
658
+ notifyTo: target.to,
659
+ });
660
+ }
661
+
662
+ return {
663
+ sent: true,
664
+ platform: target.platform,
665
+ to: target.to,
666
+ result: sendResult,
667
+ };
668
+ } catch (error) {
669
+ lastError = error;
670
+ }
671
+ }
672
+
673
+ if (lastError) {
674
+ console.error(`[Tasks] Task ${taskId} notification delivery failed:`, lastError.message);
675
+ }
676
+ return null;
677
+ }
678
+
508
679
  _getTaskConversation(userId, taskId, taskName, agentId = null) {
509
680
  const scopedAgentId = resolveAgentId(userId, agentId);
510
681
  const platform = 'tasks';
@@ -131,6 +131,19 @@ class TaskRepository {
131
131
  .run(JSON.stringify(taskConfig), taskId, userId);
132
132
  }
133
133
 
134
+ listRecentMessageTargets(userId, agentId, limit = 20) {
135
+ return db.prepare(
136
+ `SELECT platform, platform_chat_id
137
+ FROM messages
138
+ WHERE user_id = ?
139
+ AND agent_id = ?
140
+ AND platform IS NOT NULL
141
+ AND platform_chat_id IS NOT NULL
142
+ ORDER BY id DESC
143
+ LIMIT ?`
144
+ ).all(userId, agentId, Math.max(1, Math.min(Number(limit) || 20, 100)));
145
+ }
146
+
134
147
  getAgentSetting(userId, agentId, key) {
135
148
  return db.prepare('SELECT value FROM agent_settings WHERE user_id = ? AND agent_id = ? AND key = ?')
136
149
  .get(userId, agentId, key);