neoagent 2.5.2-beta.10 → 2.5.2-beta.12
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 +47 -7
- package/server/services/ai/loop/blank_recovery.js +37 -0
- package/server/services/ai/loop/conversation_loop.js +21 -0
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
72a942e4fb37f5f2c49ac77a01ef383d
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"77e2e94772b6eb43759e34ed1ad7da4674e19c
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "2886813404" /* 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("mqgairk8-ba9fc75").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("mqgairk8-ba9fc75").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,"mqgairk8-ba9fc75")){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("mqgairk8-ba9fc75").length===0||n.c){s=1
|
|
140545
140545
|
break}n.c=!0
|
|
140546
140546
|
n.F()
|
|
140547
140547
|
p=4
|
|
@@ -72,10 +72,45 @@ const CANDIDATE_KEYS = [
|
|
|
72
72
|
'mediaPaths',
|
|
73
73
|
'screenshotPath',
|
|
74
74
|
'uiDumpPath',
|
|
75
|
-
'
|
|
76
|
-
'
|
|
75
|
+
'artifact',
|
|
76
|
+
'artifacts',
|
|
77
|
+
'artifactPath',
|
|
78
|
+
'artifactPaths',
|
|
79
|
+
'artifactUrl',
|
|
80
|
+
'artifactUrls',
|
|
81
|
+
'artifactUri',
|
|
82
|
+
'artifactUris',
|
|
83
|
+
'downloadUrl',
|
|
84
|
+
'downloadUrls',
|
|
85
|
+
'downloadUri',
|
|
86
|
+
'downloadUris',
|
|
77
87
|
];
|
|
78
88
|
|
|
89
|
+
const ARTIFACT_CONTAINER_KEYS = new Set([
|
|
90
|
+
'artifact',
|
|
91
|
+
'artifacts',
|
|
92
|
+
'attachment',
|
|
93
|
+
'attachments',
|
|
94
|
+
'deliverable',
|
|
95
|
+
'deliverables',
|
|
96
|
+
'download',
|
|
97
|
+
'downloads',
|
|
98
|
+
'file',
|
|
99
|
+
'files',
|
|
100
|
+
'media',
|
|
101
|
+
'preview',
|
|
102
|
+
'screenshot',
|
|
103
|
+
'screenshots',
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
const CONTAINER_URL_KEYS = new Set(['url', 'urls', 'uri', 'uris', 'href', 'hrefs']);
|
|
107
|
+
|
|
108
|
+
function isExplicitCandidateKey(keyHint = '', parentKeyHint = '') {
|
|
109
|
+
if (CANDIDATE_KEYS.includes(keyHint)) return true;
|
|
110
|
+
if (!CONTAINER_URL_KEYS.has(keyHint)) return false;
|
|
111
|
+
return ARTIFACT_CONTAINER_KEYS.has(parentKeyHint);
|
|
112
|
+
}
|
|
113
|
+
|
|
79
114
|
function inferExtension(candidate = '') {
|
|
80
115
|
return path.extname(String(candidate || '').split('?')[0]).toLowerCase();
|
|
81
116
|
}
|
|
@@ -102,6 +137,7 @@ function normalizePathOrUri(value) {
|
|
|
102
137
|
if (!text) return null;
|
|
103
138
|
if (text.startsWith('/api/artifacts/')) return { uri: text, path: null };
|
|
104
139
|
if (/^https?:\/\//i.test(text)) return { uri: text, path: null };
|
|
140
|
+
if (text.startsWith('//')) return null;
|
|
105
141
|
if (/^[A-Za-z]:\\/.test(text)) return { path: text, uri: null };
|
|
106
142
|
if (path.isAbsolute(text)) return { path: text, uri: null };
|
|
107
143
|
return null;
|
|
@@ -167,23 +203,26 @@ async function extractArtifactsFromResult(toolName, result) {
|
|
|
167
203
|
artifacts.push(artifact);
|
|
168
204
|
}
|
|
169
205
|
|
|
170
|
-
async function visit(value, keyHint = '') {
|
|
206
|
+
async function visit(value, keyHint = '', parentKeyHint = '') {
|
|
171
207
|
if (value == null) return;
|
|
172
208
|
if (typeof value === 'string') {
|
|
173
|
-
const explicit =
|
|
174
|
-
if (explicit
|
|
209
|
+
const explicit = isExplicitCandidateKey(keyHint, parentKeyHint);
|
|
210
|
+
if (explicit && normalizePathOrUri(value)) {
|
|
211
|
+
await pushCandidate(value);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
175
214
|
for (const candidate of scanStringForCandidates(value, { explicit })) {
|
|
176
215
|
await pushCandidate(candidate);
|
|
177
216
|
}
|
|
178
217
|
return;
|
|
179
218
|
}
|
|
180
219
|
if (Array.isArray(value)) {
|
|
181
|
-
for (const item of value) await visit(item, keyHint);
|
|
220
|
+
for (const item of value) await visit(item, keyHint, parentKeyHint);
|
|
182
221
|
return;
|
|
183
222
|
}
|
|
184
223
|
if (typeof value === 'object') {
|
|
185
224
|
for (const [key, nested] of Object.entries(value)) {
|
|
186
|
-
await visit(nested, key);
|
|
225
|
+
await visit(nested, key, keyHint);
|
|
187
226
|
}
|
|
188
227
|
}
|
|
189
228
|
}
|
|
@@ -196,5 +235,6 @@ module.exports = {
|
|
|
196
235
|
extractArtifactsFromResult,
|
|
197
236
|
inferArtifactKind,
|
|
198
237
|
inferMimeType,
|
|
238
|
+
isExplicitCandidateKey,
|
|
199
239
|
normalizePathOrUri,
|
|
200
240
|
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { summarizeForLog } = require('../logFormat');
|
|
4
|
+
|
|
5
|
+
function latestFailedToolExecution(toolExecutions = []) {
|
|
6
|
+
return [...toolExecutions].reverse().find((item) => item && item.ok === false) || null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function shouldContinueAfterBlankToolFailure({
|
|
10
|
+
lastContent = '',
|
|
11
|
+
failedStepCount = 0,
|
|
12
|
+
remainingIterations = 0,
|
|
13
|
+
toolExecutions = [],
|
|
14
|
+
} = {}) {
|
|
15
|
+
return !String(lastContent || '').trim()
|
|
16
|
+
&& Number(failedStepCount || 0) > 0
|
|
17
|
+
&& Number(remainingIterations || 0) > 0
|
|
18
|
+
&& Boolean(latestFailedToolExecution(toolExecutions));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildBlankAfterToolFailureGuidance(toolExecutions = []) {
|
|
22
|
+
const failedExecution = latestFailedToolExecution(toolExecutions);
|
|
23
|
+
if (!failedExecution) return '';
|
|
24
|
+
const toolName = failedExecution.toolName || failedExecution.tool || 'the previous tool';
|
|
25
|
+
const failure = failedExecution.error || failedExecution.summary || failedExecution.status || 'unknown failure';
|
|
26
|
+
return [
|
|
27
|
+
`The previous tool "${toolName}" failed with: ${summarizeForLog(failure, 240)}.`,
|
|
28
|
+
'The latest assistant turn returned no user-facing answer and no tool call, so the task is not terminal.',
|
|
29
|
+
'Continue with the next safe recovery action: retry with corrected arguments, use another available tool, verify from existing evidence, or report a real blocker only if no autonomous path remains.',
|
|
30
|
+
].join(' ');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
buildBlankAfterToolFailureGuidance,
|
|
35
|
+
latestFailedToolExecution,
|
|
36
|
+
shouldContinueAfterBlankToolFailure,
|
|
37
|
+
};
|
|
@@ -56,6 +56,10 @@ const { enforceRateLimits } = require('../rate_limits');
|
|
|
56
56
|
const { ToolRepetitionGuard } = require('../repetitionGuard');
|
|
57
57
|
const { shortenRunId, summarizeForLog } = require('../logFormat');
|
|
58
58
|
const { IterationBudget } = require('./iteration_budget');
|
|
59
|
+
const {
|
|
60
|
+
buildBlankAfterToolFailureGuidance,
|
|
61
|
+
shouldContinueAfterBlankToolFailure,
|
|
62
|
+
} = require('./blank_recovery');
|
|
59
63
|
const {
|
|
60
64
|
shouldRetryMessagingRun,
|
|
61
65
|
shouldSendMessagingErrorFallback,
|
|
@@ -1272,6 +1276,23 @@ async function runConversation(engine, userId, userMessage, options = {}, _model
|
|
|
1272
1276
|
})) {
|
|
1273
1277
|
break;
|
|
1274
1278
|
}
|
|
1279
|
+
if (shouldContinueAfterBlankToolFailure({
|
|
1280
|
+
lastContent,
|
|
1281
|
+
failedStepCount,
|
|
1282
|
+
remainingIterations: iterationBudget.remaining,
|
|
1283
|
+
toolExecutions,
|
|
1284
|
+
})) {
|
|
1285
|
+
engine.recordRunEvent(userId, runId, 'blank_after_tool_failure_continued', {
|
|
1286
|
+
iteration,
|
|
1287
|
+
remainingIterations: iterationBudget.remaining,
|
|
1288
|
+
}, { agentId });
|
|
1289
|
+
messages.push({
|
|
1290
|
+
role: 'system',
|
|
1291
|
+
content: buildBlankAfterToolFailureGuidance(toolExecutions),
|
|
1292
|
+
});
|
|
1293
|
+
lastContent = '';
|
|
1294
|
+
continue;
|
|
1295
|
+
}
|
|
1275
1296
|
const loopDecision = await engine.decideLoopState({
|
|
1276
1297
|
provider,
|
|
1277
1298
|
providerName,
|