neoagent 2.5.2-beta.9 → 3.0.0
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/README.md +5 -1
- package/docs/integrations.md +11 -7
- package/package.json +2 -2
- 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 +92 -9
- package/server/services/ai/deliverables/selector.js +17 -0
- package/server/services/ai/loop/blank_recovery.js +37 -0
- package/server/services/ai/loop/conversation_loop.js +345 -242
- package/server/services/ai/loop/error_recovery.js +38 -0
- package/server/services/ai/loop/messaging_delivery.js +52 -6
- package/server/services/ai/loop/progress_classification.js +178 -0
- package/server/services/ai/loop/progress_monitor.js +6 -5
- package/server/services/ai/loop/tool_dispatch.js +1 -0
- package/server/services/ai/loopPolicy.js +15 -6
- package/server/services/ai/messagingFallback.js +23 -1
- package/server/services/ai/preModelCompaction.js +31 -0
- package/server/services/ai/repetitionGuard.js +47 -2
- package/server/services/ai/systemPrompt.js +6 -0
- package/server/services/ai/taskAnalysis.js +10 -0
- package/server/services/ai/toolEvidence.js +15 -3
- package/server/services/ai/toolResult.js +29 -0
- package/server/services/ai/toolSelector.js +20 -3
- package/server/services/ai/tools.js +196 -26
- package/server/services/integrations/github/common.js +2 -2
- package/server/services/integrations/github/repos.js +123 -55
- package/server/services/runtime/docker-vm-manager.js +21 -1
- package/server/services/workspace/manager.js +56 -0
package/README.md
CHANGED
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
21
|
<p align="center">
|
|
22
|
-
<
|
|
22
|
+
<a href="https://www.producthunt.com/products/neoagent-2?embed=true&utm_source=badge-featured&utm_medium=badge&utm_campaign=badge-neoagent-2" target="_blank" rel="noopener noreferrer"><img alt="NeoAgent - The next-gen self-hosted AI agent that works beyond chat | Product Hunt" width="250" height="54" src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=1171573&theme=light&t=1781447653039"></a>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
<p align="center">
|
|
26
|
+
<img src="demo.gif" alt="NeoAgent demo" width="100%">
|
|
23
27
|
</p>
|
|
24
28
|
|
|
25
29
|
## 🚀 Install
|
package/docs/integrations.md
CHANGED
|
@@ -36,15 +36,19 @@ or delete data.
|
|
|
36
36
|
|
|
37
37
|
### Setup
|
|
38
38
|
|
|
39
|
-
Google, Microsoft, GitHub, Notion, Slack, Figma, and Spotify require OAuth
|
|
40
|
-
application credentials on the server. The default callback is:
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
PUBLIC_URL/api/integrations/oauth/callback
|
|
44
|
-
|
|
40
|
+
Google, Microsoft, GitHub, Notion, Slack, Figma, and Spotify require OAuth application credentials on the server. The default callback is:
|
|
41
|
+
`PUBLIC_URL/api/integrations/oauth/callback`
|
|
42
|
+
|
|
43
|
+
**GitHub OAuth Configuration:**
|
|
44
|
+
1. Navigate to your GitHub Developer Settings and create a new **OAuth App**.
|
|
45
|
+
2. Set the Authorization callback URL to: `PUBLIC_URL/api/integrations/oauth/callback`.
|
|
46
|
+
3. Generate a **Client ID** and **Client Secret**.
|
|
47
|
+
4. Add these to your server environment configuration:
|
|
48
|
+
- `GITHUB_CLIENT_ID=your_id_here`
|
|
49
|
+
- `GITHUB_CLIENT_SECRET=your_secret_here`
|
|
45
50
|
|
|
46
|
-
Home Assistant and Trello can be configured for a user from the application.
|
|
47
|
-
See [Configuration](configuration.md#official-integrations).
|
|
51
|
+
Home Assistant and Trello can be configured for a user from the application. See Configuration.
|
|
48
52
|
|
|
49
53
|
## Messaging channels
|
|
50
54
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neoagent",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Self-hosted AI agent for long-running tasks, automation, messaging, device control, and local memory",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"main": "server/index.js",
|
|
7
7
|
"engines": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
0aebc22637251b8ff07f20355c6f3771
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"77e2e94772b6eb43759e34ed1ad7da4674e19c
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "4115106509" /* 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("mqh4zb5l-21f53dd").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("mqh4zb5l-21f53dd").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,"mqh4zb5l-21f53dd")){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("mqh4zb5l-21f53dd").length===0||n.c){s=1
|
|
140545
140545
|
break}n.c=!0
|
|
140546
140546
|
n.F()
|
|
140547
140547
|
p=4
|
|
@@ -72,10 +72,78 @@ 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 GENERIC_CANDIDATE_KEYS = new Set([
|
|
90
|
+
'path',
|
|
91
|
+
'paths',
|
|
92
|
+
'file',
|
|
93
|
+
'files',
|
|
94
|
+
'filePath',
|
|
95
|
+
'filePaths',
|
|
96
|
+
'fullPath',
|
|
97
|
+
'fullPaths',
|
|
98
|
+
'downloadUrl',
|
|
99
|
+
'downloadUrls',
|
|
100
|
+
'downloadUri',
|
|
101
|
+
'downloadUris',
|
|
102
|
+
]);
|
|
103
|
+
|
|
104
|
+
const EXPLICIT_CANDIDATE_KEYS = new Set(
|
|
105
|
+
CANDIDATE_KEYS.filter((key) => !GENERIC_CANDIDATE_KEYS.has(key))
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const ARTIFACT_CONTAINER_KEYS = new Set([
|
|
109
|
+
'artifact',
|
|
110
|
+
'artifacts',
|
|
111
|
+
'attachment',
|
|
112
|
+
'attachments',
|
|
113
|
+
'deliverable',
|
|
114
|
+
'deliverables',
|
|
115
|
+
'download',
|
|
116
|
+
'downloads',
|
|
117
|
+
'file',
|
|
118
|
+
'files',
|
|
119
|
+
'media',
|
|
120
|
+
'preview',
|
|
121
|
+
'screenshot',
|
|
122
|
+
'screenshots',
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
const CONTAINER_URL_KEYS = new Set(['url', 'urls', 'uri', 'uris', 'href', 'hrefs']);
|
|
126
|
+
|
|
127
|
+
const EVIDENCE_RESULT_TOOLS = /^(execute_command|github_|list_|search_|read_|get_|find_|http_request|web_search|browser_get|browser_read|code_navigate|query_structured_data|memory_|session_search|recordings_|read_health_data)/;
|
|
128
|
+
|
|
129
|
+
function allowsGenericCandidateKeys(toolName = '') {
|
|
130
|
+
return !EVIDENCE_RESULT_TOOLS.test(String(toolName || ''));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function isExplicitCandidateKey(keyHint = '', parentKeyHint = '', options = {}) {
|
|
134
|
+
if (EXPLICIT_CANDIDATE_KEYS.has(keyHint)) return true;
|
|
135
|
+
if (
|
|
136
|
+
ARTIFACT_CONTAINER_KEYS.has(parentKeyHint)
|
|
137
|
+
&& CANDIDATE_KEYS.includes(keyHint)
|
|
138
|
+
&& (!GENERIC_CANDIDATE_KEYS.has(parentKeyHint) || options.allowGenericKeys === true)
|
|
139
|
+
) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
if (GENERIC_CANDIDATE_KEYS.has(keyHint)) return options.allowGenericKeys === true;
|
|
143
|
+
if (!CONTAINER_URL_KEYS.has(keyHint)) return false;
|
|
144
|
+
return ARTIFACT_CONTAINER_KEYS.has(parentKeyHint);
|
|
145
|
+
}
|
|
146
|
+
|
|
79
147
|
function inferExtension(candidate = '') {
|
|
80
148
|
return path.extname(String(candidate || '').split('?')[0]).toLowerCase();
|
|
81
149
|
}
|
|
@@ -102,8 +170,17 @@ function normalizePathOrUri(value) {
|
|
|
102
170
|
if (!text) return null;
|
|
103
171
|
if (text.startsWith('/api/artifacts/')) return { uri: text, path: null };
|
|
104
172
|
if (/^https?:\/\//i.test(text)) return { uri: text, path: null };
|
|
105
|
-
if (
|
|
106
|
-
if (
|
|
173
|
+
if (text.startsWith('//')) return null;
|
|
174
|
+
if (/^[A-Za-z]:\\/.test(text)) {
|
|
175
|
+
const ext = path.extname(text.split('?')[0]).toLowerCase();
|
|
176
|
+
if (!FILE_EXTENSION_TO_KIND[ext]) return null;
|
|
177
|
+
return { path: text, uri: null };
|
|
178
|
+
}
|
|
179
|
+
if (path.isAbsolute(text)) {
|
|
180
|
+
const ext = path.extname(text.split('?')[0]).toLowerCase();
|
|
181
|
+
if (!FILE_EXTENSION_TO_KIND[ext]) return null;
|
|
182
|
+
return { path: text, uri: null };
|
|
183
|
+
}
|
|
107
184
|
return null;
|
|
108
185
|
}
|
|
109
186
|
|
|
@@ -154,6 +231,7 @@ async function extractArtifactsFromResult(toolName, result) {
|
|
|
154
231
|
const seen = new Set();
|
|
155
232
|
const seenCandidates = new Set();
|
|
156
233
|
const fallbackKind = inferArtifactKind(toolName, 'artifact');
|
|
234
|
+
const allowGenericKeys = allowsGenericCandidateKeys(toolName);
|
|
157
235
|
|
|
158
236
|
async function pushCandidate(candidate) {
|
|
159
237
|
const candidateKey = String(candidate || '').trim();
|
|
@@ -167,23 +245,26 @@ async function extractArtifactsFromResult(toolName, result) {
|
|
|
167
245
|
artifacts.push(artifact);
|
|
168
246
|
}
|
|
169
247
|
|
|
170
|
-
async function visit(value, keyHint = '') {
|
|
248
|
+
async function visit(value, keyHint = '', parentKeyHint = '') {
|
|
171
249
|
if (value == null) return;
|
|
172
250
|
if (typeof value === 'string') {
|
|
173
|
-
const explicit =
|
|
174
|
-
if (explicit)
|
|
251
|
+
const explicit = isExplicitCandidateKey(keyHint, parentKeyHint, { allowGenericKeys });
|
|
252
|
+
if (explicit) {
|
|
253
|
+
if (normalizePathOrUri(value)) await pushCandidate(value);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
175
256
|
for (const candidate of scanStringForCandidates(value, { explicit })) {
|
|
176
257
|
await pushCandidate(candidate);
|
|
177
258
|
}
|
|
178
259
|
return;
|
|
179
260
|
}
|
|
180
261
|
if (Array.isArray(value)) {
|
|
181
|
-
for (const item of value) await visit(item, keyHint);
|
|
262
|
+
for (const item of value) await visit(item, keyHint, parentKeyHint);
|
|
182
263
|
return;
|
|
183
264
|
}
|
|
184
265
|
if (typeof value === 'object') {
|
|
185
266
|
for (const [key, nested] of Object.entries(value)) {
|
|
186
|
-
await visit(nested, key);
|
|
267
|
+
await visit(nested, key, keyHint);
|
|
187
268
|
}
|
|
188
269
|
}
|
|
189
270
|
}
|
|
@@ -193,8 +274,10 @@ async function extractArtifactsFromResult(toolName, result) {
|
|
|
193
274
|
}
|
|
194
275
|
|
|
195
276
|
module.exports = {
|
|
277
|
+
allowsGenericCandidateKeys,
|
|
196
278
|
extractArtifactsFromResult,
|
|
197
279
|
inferArtifactKind,
|
|
198
280
|
inferMimeType,
|
|
281
|
+
isExplicitCandidateKey,
|
|
199
282
|
normalizePathOrUri,
|
|
200
283
|
};
|
|
@@ -47,6 +47,23 @@ async function selectDeliverableWorkflow({
|
|
|
47
47
|
tools = [],
|
|
48
48
|
options = {},
|
|
49
49
|
}) {
|
|
50
|
+
if (!engine || typeof engine.requestStructuredJson !== 'function') {
|
|
51
|
+
return {
|
|
52
|
+
selection: {
|
|
53
|
+
status: 'standard',
|
|
54
|
+
type: null,
|
|
55
|
+
confidence: 0,
|
|
56
|
+
goal: '',
|
|
57
|
+
requestedOutputs: [],
|
|
58
|
+
supportingCapabilities: [],
|
|
59
|
+
},
|
|
60
|
+
usage: 0,
|
|
61
|
+
raw: '',
|
|
62
|
+
skipped: true,
|
|
63
|
+
reason: 'structured selector unavailable',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
50
67
|
const response = await engine.requestStructuredJson({
|
|
51
68
|
provider,
|
|
52
69
|
providerName,
|
|
@@ -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
|
+
};
|