neoagent 2.5.2-beta.6 → 2.5.2-beta.8
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 +1 @@
|
|
|
1
|
-
|
|
1
|
+
bb621393d8fc51c33384ab5b836db272
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"77e2e94772b6eb43759e34ed1ad7da4674e19c
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "2920927188" /* 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("
|
|
134797
|
+
r=B.b.u("mqfpjvdl-88fa84f").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("
|
|
140509
|
+
if(B.b.u("mqfpjvdl-88fa84f").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,"
|
|
140527
|
+
if(J.bi(j)===0||J.d(j,"mqfpjvdl-88fa84f")){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("
|
|
140544
|
+
s=p}for(;;)switch(s){case 0:if(B.b.u("mqfpjvdl-88fa84f").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,31 @@ 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
|
+
|
|
195
|
+
// Tools that represent concrete forward progress (write, create, send, update, run).
|
|
196
|
+
// Anything NOT in this set is considered read-only for the analysis-paralysis gate.
|
|
197
|
+
// execute_command counts as progress — it can do anything, including modify state.
|
|
198
|
+
function isProgressTool(toolName) {
|
|
199
|
+
if (!toolName) return false;
|
|
200
|
+
// Neutral / bookkeeping — don't count either way
|
|
201
|
+
if (toolName === 'activate_tools' || toolName === 'save_widget_snapshot') return false;
|
|
202
|
+
// Explicitly read-only patterns
|
|
203
|
+
if (/^(list_|search_|read_file|get_file|find_files?|github_list|github_get|github_search|browser_get|browser_read)/.test(toolName)) return false;
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
|
|
171
207
|
function resolveModelCallTimeoutMs(options = {}) {
|
|
172
208
|
const requested = Number(options?.modelCallTimeoutMs);
|
|
173
209
|
if (Number.isFinite(requested) && requested > 0) {
|
|
@@ -2623,6 +2659,8 @@ class AgentEngine {
|
|
|
2623
2659
|
systemSteeringQueue: [],
|
|
2624
2660
|
toolPids: new Set(),
|
|
2625
2661
|
repetitionGuard: new ToolRepetitionGuard(),
|
|
2662
|
+
seenOutputHashes: new Map(),
|
|
2663
|
+
consecutiveReadOnlyIterations: 0,
|
|
2626
2664
|
messagingContext: triggerSource === 'messaging'
|
|
2627
2665
|
? {
|
|
2628
2666
|
platform: options.source || null,
|
|
@@ -3042,6 +3080,21 @@ class AgentEngine {
|
|
|
3042
3080
|
});
|
|
3043
3081
|
messages = steeringAtLoopStart.messages;
|
|
3044
3082
|
messages = sanitizeConversationMessages(messages);
|
|
3083
|
+
|
|
3084
|
+
// Analysis-paralysis gate: fire at the start of every iteration where
|
|
3085
|
+
// the agent has spent N turns only reading/listing/searching without
|
|
3086
|
+
// taking any concrete action. Escalates in urgency each turn.
|
|
3087
|
+
if (analysis.mode === 'execute' || analysis.mode === 'plan_execute') {
|
|
3088
|
+
const readOnlyCount = this.getRunMeta(runId)?.consecutiveReadOnlyIterations || 0;
|
|
3089
|
+
if (readOnlyCount >= 3) {
|
|
3090
|
+
const urgency = readOnlyCount >= 6 ? 'CRITICAL' : 'ACTION REQUIRED';
|
|
3091
|
+
messages.push({
|
|
3092
|
+
role: 'system',
|
|
3093
|
+
content: `${urgency} — ${readOnlyCount} consecutive read-only turns: You have been gathering information for ${readOnlyCount} turns without writing, creating, sending, or running anything. You must take ONE concrete action this turn (create a file, open a PR, run a command that modifies state, send a message) or call task_complete to report what you found and why you cannot proceed. Do not read or list anything further.`,
|
|
3094
|
+
});
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
|
|
3045
3098
|
this.updateRunProgress(runId, {
|
|
3046
3099
|
currentPhase: 'model',
|
|
3047
3100
|
currentStep: `model:${iteration}`,
|
|
@@ -3595,6 +3648,40 @@ class AgentEngine {
|
|
|
3595
3648
|
}
|
|
3596
3649
|
} else {
|
|
3597
3650
|
consecutiveToolFailures = 0;
|
|
3651
|
+
// Output fingerprint guard: steer away from re-fetching data already seen.
|
|
3652
|
+
if (!toolErrorMessage) {
|
|
3653
|
+
const currentRunMeta = this.getRunMeta(runId);
|
|
3654
|
+
const fp = fingerprintOutput(toolName, toolResult);
|
|
3655
|
+
if (fp !== null && currentRunMeta?.seenOutputHashes) {
|
|
3656
|
+
const prior = currentRunMeta.seenOutputHashes.get(fp);
|
|
3657
|
+
if (prior) {
|
|
3658
|
+
messages.push({
|
|
3659
|
+
role: 'system',
|
|
3660
|
+
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.`,
|
|
3661
|
+
});
|
|
3662
|
+
} else {
|
|
3663
|
+
currentRunMeta.seenOutputHashes.set(fp, { toolName, iteration });
|
|
3664
|
+
// External state: persist large read results to disk so the
|
|
3665
|
+
// model can reference them after context compaction without
|
|
3666
|
+
// re-fetching. Only for significant payloads.
|
|
3667
|
+
const persistRaw = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult ?? '');
|
|
3668
|
+
if (persistRaw.length >= 1000 && runId) {
|
|
3669
|
+
const persistPath = `/tmp/run-${runId.slice(0, 8)}-${toolName}.json`;
|
|
3670
|
+
try {
|
|
3671
|
+
require('fs').writeFileSync(persistPath, persistRaw.slice(0, 40000));
|
|
3672
|
+
if (!currentRunMeta.persistedDataPaths) currentRunMeta.persistedDataPaths = [];
|
|
3673
|
+
if (!currentRunMeta.persistedDataPaths.includes(persistPath)) {
|
|
3674
|
+
currentRunMeta.persistedDataPaths.push(persistPath);
|
|
3675
|
+
messages.push({
|
|
3676
|
+
role: 'system',
|
|
3677
|
+
content: `Data from "${toolName}" (iteration ${iteration}) persisted to ${persistPath}. If context compacts and you need this data again, use execute_command with \`cat ${persistPath}\` instead of re-fetching.`,
|
|
3678
|
+
});
|
|
3679
|
+
}
|
|
3680
|
+
} catch { /* non-fatal — disk full or permissions */ }
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3598
3685
|
}
|
|
3599
3686
|
|
|
3600
3687
|
if (toolName === 'send_interim_update') {
|
|
@@ -3656,6 +3743,19 @@ class AgentEngine {
|
|
|
3656
3743
|
}
|
|
3657
3744
|
}
|
|
3658
3745
|
|
|
3746
|
+
// Update analysis-paralysis counter after each iteration's tool calls.
|
|
3747
|
+
// Resets to 0 when any progress tool was called; otherwise increments.
|
|
3748
|
+
if (!directAnswerEligible && response?.toolCalls?.length > 0
|
|
3749
|
+
&& (analysis.mode === 'execute' || analysis.mode === 'plan_execute')) {
|
|
3750
|
+
const iterMeta = this.getRunMeta(runId);
|
|
3751
|
+
if (iterMeta) {
|
|
3752
|
+
const calledProgress = response.toolCalls.some((tc) => isProgressTool(tc.function?.name || ''));
|
|
3753
|
+
iterMeta.consecutiveReadOnlyIterations = calledProgress
|
|
3754
|
+
? 0
|
|
3755
|
+
: (iterMeta.consecutiveReadOnlyIterations || 0) + 1;
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3659
3759
|
if (this.isRunStopped(runId)) break;
|
|
3660
3760
|
if (this.getRunMeta(runId)?.terminalInterim) break;
|
|
3661
3761
|
if (this.getRunMeta(runId)?.widgetSnapshotSaved) break;
|