neoagent 2.5.2-beta.6 → 2.5.2-beta.7

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.5.2-beta.6",
3
+ "version": "2.5.2-beta.7",
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
- 1050e01e6b4a9c529922c7db724d0188
1
+ c71ef84ad1471500295da6f6c6f13299
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"77e2e94772b6eb43759e34ed1ad7da4674e19c
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "1247404091" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "2830694115" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });
@@ -134794,7 +134794,7 @@ r===$&&A.b()
134794
134794
  p.push(A.jP(q,A.j9(!1,new A.a_(B.uG,A.d8(new A.cA(B.jt,new A.a7N(r,q),q),q,q),q),!1,B.H,!0),q,q,0,0,0,q))}r=!1
134795
134795
  if(!s.ay)if(!s.ch){r=s.e
134796
134796
  r===$&&A.b()
134797
- r=B.b.u("mqfmnj9i-24c1816").length!==0&&r.b}if(r){r=s.d
134797
+ r=B.b.u("mqfoxi8l-ab0f828").length!==0&&r.b}if(r){r=s.d
134798
134798
  r===$&&A.b()
134799
134799
  r=r.aP&&!r.ai?84:0
134800
134800
  s=s.e
@@ -140506,7 +140506,7 @@ $S:0}
140506
140506
  A.a_6.prototype={}
140507
140507
  A.SQ.prototype={
140508
140508
  nb(a){var s=this
140509
- if(B.b.u("mqfmnj9i-24c1816").length===0||s.a!=null)return
140509
+ if(B.b.u("mqfoxi8l-ab0f828").length===0||s.a!=null)return
140510
140510
  s.AU()
140511
140511
  s.a=A.on(B.RH,new A.bc8(s))},
140512
140512
  AU(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f
@@ -140524,7 +140524,7 @@ if(!t.f.b(k)){s=1
140524
140524
  break}i=J.a3(k,"buildId")
140525
140525
  h=i==null?null:B.b.u(J.p(i))
140526
140526
  j=h==null?"":h
140527
- if(J.bi(j)===0||J.d(j,"mqfmnj9i-24c1816")){s=1
140527
+ if(J.bi(j)===0||J.d(j,"mqfoxi8l-ab0f828")){s=1
140528
140528
  break}n.b=!0
140529
140529
  n.F()
140530
140530
  p=2
@@ -140541,7 +140541,7 @@ case 2:return A.i(o.at(-1),r)}})
140541
140541
  return A.k($async$AU,r)},
140542
140542
  vE(){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
140543
140543
  var $async$vE=A.h(function(a2,a3){if(a2===1){o.push(a3)
140544
- s=p}for(;;)switch(s){case 0:if(B.b.u("mqfmnj9i-24c1816").length===0||n.c){s=1
140544
+ s=p}for(;;)switch(s){case 0:if(B.b.u("mqfoxi8l-ab0f828").length===0||n.c){s=1
140545
140545
  break}n.c=!0
140546
140546
  n.F()
140547
140547
  p=4
@@ -144,6 +144,7 @@ function normalizeErrorKey(errorMsg) {
144
144
  if (/enoent|no such file/i.test(msg)) return 'enoent';
145
145
  if (/can.?t cd to|no such directory/i.test(msg)) return 'bad_cwd';
146
146
  if (/not found/i.test(msg)) return 'not_found';
147
+ if (/owner_repo.*format|must be.*owner.*repo|owner.*repo.*string|owner.*repo.*combined/i.test(msg)) return 'owner_repo_format';
147
148
  return msg.slice(0, 60);
148
149
  }
149
150
 
@@ -155,10 +156,20 @@ function trackErrorPattern(errorMsg, runMeta) {
155
156
  }
156
157
 
157
158
  function buildErrorPatternGuidance(key, count) {
159
+ // Immediate guidance on first occurrence for high-signal patterns that waste
160
+ // multiple iterations before self-correcting.
161
+ const immediateGuides = {
162
+ eisdir: 'That path is a directory (or a VM-only path like /tmp that read_file cannot reach). Use execute_command with `cat <path>` to read files inside VMs, or list_directory to inspect a directory.',
163
+ owner_repo_format: 'The parameter "owner_repo" expects a single combined string like "NeoLabs-Systems/NeoAgent" — not separate owner/repo fields. Pass the full "owner/repo" as one value.',
164
+ };
165
+ if (immediateGuides[key]) {
166
+ const prefix = count > 1 ? `REPEATED ERROR (${count}×): ` : 'ERROR GUIDANCE: ';
167
+ return `${prefix}${immediateGuides[key]}`;
168
+ }
169
+
158
170
  if (count < 3) return null;
159
171
  const guides = {
160
172
  outside_workspace: 'read_file cannot access /tmp paths. Use execute_command with `cat <path>` instead.',
161
- eisdir: 'That path is a directory, not a file. Use list_directory or execute_command with `ls` to inspect it.',
162
173
  enoent: 'That path does not exist. Use execute_command with `find . -name "..."` to locate the correct path first.',
163
174
  bad_cwd: 'The VM home directory is not ~/. Use absolute paths starting from /tmp or discover the workspace root first.',
164
175
  not_found: 'This path or resource was not found. Try listing the parent directory or checking with a broader search first.',
@@ -168,6 +179,19 @@ function buildErrorPatternGuidance(key, count) {
168
179
  return `REPEATED ERROR (${count}×): ${guide}`;
169
180
  }
170
181
 
182
+ const OUTPUT_FINGERPRINT_TOOLS = /^(list_|search_|read_|get_|find_|github_list|github_get|github_search)/;
183
+
184
+ function fingerprintOutput(toolName, result) {
185
+ if (!toolName || !OUTPUT_FINGERPRINT_TOOLS.test(toolName)) return null;
186
+ const raw = typeof result === 'string' ? result : JSON.stringify(result ?? '');
187
+ if (raw.length < 200) return null;
188
+ // djb2 hash over first 3000 chars — fast, collision-unlikely for our sizes
189
+ let h = 5381;
190
+ const limit = Math.min(raw.length, 3000);
191
+ for (let i = 0; i < limit; i++) h = ((h << 5) + h) ^ raw.charCodeAt(i);
192
+ return h >>> 0;
193
+ }
194
+
171
195
  function resolveModelCallTimeoutMs(options = {}) {
172
196
  const requested = Number(options?.modelCallTimeoutMs);
173
197
  if (Number.isFinite(requested) && requested > 0) {
@@ -2623,6 +2647,7 @@ class AgentEngine {
2623
2647
  systemSteeringQueue: [],
2624
2648
  toolPids: new Set(),
2625
2649
  repetitionGuard: new ToolRepetitionGuard(),
2650
+ seenOutputHashes: new Map(),
2626
2651
  messagingContext: triggerSource === 'messaging'
2627
2652
  ? {
2628
2653
  platform: options.source || null,
@@ -3011,6 +3036,13 @@ class AgentEngine {
3011
3036
  }
3012
3037
  messages = sanitizeConversationMessages(messages);
3013
3038
 
3039
+ if (analysis.mode === 'execute' || analysis.mode === 'plan_execute') {
3040
+ messages.push({
3041
+ role: 'system',
3042
+ content: 'Research budget: after 3 read/list/search tool calls, you must take a concrete action (write, create, send, update) or explain clearly why you cannot. Work on one item at a time — do not queue up more reads.',
3043
+ });
3044
+ }
3045
+
3014
3046
  directAnswerEligible = isDirectAnswerEligibleAnalysis(analysis)
3015
3047
  && Boolean(normalizeOutgoingMessage(analysis.draft_reply));
3016
3048
 
@@ -3595,6 +3627,22 @@ class AgentEngine {
3595
3627
  }
3596
3628
  } else {
3597
3629
  consecutiveToolFailures = 0;
3630
+ // Output fingerprint guard: steer away from re-fetching data already seen.
3631
+ if (!toolErrorMessage) {
3632
+ const currentRunMeta = this.getRunMeta(runId);
3633
+ const fp = fingerprintOutput(toolName, toolResult);
3634
+ if (fp !== null && currentRunMeta?.seenOutputHashes) {
3635
+ const prior = currentRunMeta.seenOutputHashes.get(fp);
3636
+ if (prior) {
3637
+ messages.push({
3638
+ role: 'system',
3639
+ content: `DUPLICATE DATA: This response is identical to what "${prior.toolName}" returned in iteration ${prior.iteration}. You already have this information. Stop fetching and use what you have — proceed to the next concrete action.`,
3640
+ });
3641
+ } else {
3642
+ currentRunMeta.seenOutputHashes.set(fp, { toolName, iteration });
3643
+ }
3644
+ }
3645
+ }
3598
3646
  }
3599
3647
 
3600
3648
  if (toolName === 'send_interim_update') {