neoagent 3.2.1-beta.1 → 3.2.1-beta.2
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/loop/agent_engine_core.js +11 -0
- package/server/services/ai/loop/model_io.js +12 -4
- package/server/services/ai/providers/claudeCode.js +6 -5
- package/server/services/ai/providers/githubCopilot.js +6 -5
- package/server/services/ai/providers/grokOauth.js +6 -5
- package/server/services/ai/providers/ollama.js +18 -11
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
1c5d94e505331e4ad70cf059dc621b70
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"69c8c61792f04cc809dfef0c910414fb9afc06
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "1866288644" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|
|
@@ -143689,7 +143689,7 @@ if(r){r=s.d
|
|
|
143689
143689
|
r===$&&A.b()
|
|
143690
143690
|
p.push(A.jt(q,A.jw(!1,new A.Y(B.wO,A.cS(new A.cF(B.kE,new A.acS(r,q),q),q,q),q),!1,B.I,!0),q,q,0,0,0,q))}if(!s.ay){r=s.e
|
|
143691
143691
|
r===$&&A.b()
|
|
143692
|
-
r=B.b.t("
|
|
143692
|
+
r=B.b.t("mrwc4gsw-5a86a62").length!==0&&r.b}else r=!1
|
|
143693
143693
|
if(r){r=s.d
|
|
143694
143694
|
r===$&&A.b()
|
|
143695
143695
|
r=r.aU&&!r.ag?84:0
|
|
@@ -149556,7 +149556,7 @@ $S:0}
|
|
|
149556
149556
|
A.a3g.prototype={}
|
|
149557
149557
|
A.WA.prototype={
|
|
149558
149558
|
r6(a){var s=this
|
|
149559
|
-
if(B.b.t("
|
|
149559
|
+
if(B.b.t("mrwc4gsw-5a86a62").length===0||s.a!=null)return
|
|
149560
149560
|
s.Cn()
|
|
149561
149561
|
s.a=A.mJ(B.Y6,new A.bmI(s))},
|
|
149562
149562
|
Cn(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f
|
|
@@ -149574,7 +149574,7 @@ if(!t.f.b(k)){s=1
|
|
|
149574
149574
|
break}i=J.a3(k,"buildId")
|
|
149575
149575
|
h=i==null?null:B.b.t(J.q(i))
|
|
149576
149576
|
j=h==null?"":h
|
|
149577
|
-
if(J.br(j)===0||J.e(j,"
|
|
149577
|
+
if(J.br(j)===0||J.e(j,"mrwc4gsw-5a86a62")){s=1
|
|
149578
149578
|
break}n.b=!0
|
|
149579
149579
|
n.H()
|
|
149580
149580
|
p=2
|
|
@@ -149591,7 +149591,7 @@ case 2:return A.i(o.at(-1),r)}})
|
|
|
149591
149591
|
return A.k($async$Cn,r)},
|
|
149592
149592
|
wV(){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
|
|
149593
149593
|
var $async$wV=A.h(function(a2,a3){if(a2===1){o.push(a3)
|
|
149594
|
-
s=p}for(;;)switch(s){case 0:if(B.b.t("
|
|
149594
|
+
s=p}for(;;)switch(s){case 0:if(B.b.t("mrwc4gsw-5a86a62").length===0||n.c){s=1
|
|
149595
149595
|
break}n.c=!0
|
|
149596
149596
|
n.H()
|
|
149597
149597
|
p=4
|
|
@@ -1119,6 +1119,15 @@ class AgentEngine {
|
|
|
1119
1119
|
|
|
1120
1120
|
await new Promise((resolve) => { runMeta.resumeRun = resolve; });
|
|
1121
1121
|
runMeta.resumeRun = null;
|
|
1122
|
+
if (runMeta.status === 'stopped' || runMeta.status === 'interrupted') {
|
|
1123
|
+
return { action: runMeta.status === 'interrupted' ? 'interrupt' : 'stop' };
|
|
1124
|
+
}
|
|
1125
|
+
const persistedStatus = db.prepare('SELECT status FROM agent_runs WHERE id = ?').get(runId)?.status;
|
|
1126
|
+
if (persistedStatus !== 'running') {
|
|
1127
|
+
const action = persistedStatus === 'interrupted' ? 'interrupt' : 'stop';
|
|
1128
|
+
runMeta.status = persistedStatus || 'stopped';
|
|
1129
|
+
return { action };
|
|
1130
|
+
}
|
|
1122
1131
|
runMeta.abortController = new AbortController();
|
|
1123
1132
|
runMeta.status = 'running';
|
|
1124
1133
|
this.startMessagingProgressSupervisor(runId);
|
|
@@ -1665,6 +1674,7 @@ class AgentEngine {
|
|
|
1665
1674
|
runMeta.stopReason = reason;
|
|
1666
1675
|
runMeta.aborted = true;
|
|
1667
1676
|
runMeta.abortController?.abort(reason);
|
|
1677
|
+
runMeta.resumeRun?.();
|
|
1668
1678
|
this.emit(runMeta.userId, 'run:stopping', { runId });
|
|
1669
1679
|
for (const pid of runMeta.toolPids) {
|
|
1670
1680
|
if (this.runtimeManager && typeof this.runtimeManager.killCommand === 'function') {
|
|
@@ -1708,6 +1718,7 @@ class AgentEngine {
|
|
|
1708
1718
|
runMeta.status = 'stopped';
|
|
1709
1719
|
runMeta.aborted = true;
|
|
1710
1720
|
runMeta.abortController?.abort('Run stopped.');
|
|
1721
|
+
runMeta.resumeRun?.();
|
|
1711
1722
|
this.emit(runMeta.userId, 'run:stopping', { runId });
|
|
1712
1723
|
for (const pid of runMeta.toolPids) {
|
|
1713
1724
|
if (this.runtimeManager && typeof this.runtimeManager.killCommand === 'function') {
|
|
@@ -62,7 +62,11 @@ async function requestStructuredJson(engine, {
|
|
|
62
62
|
}) {
|
|
63
63
|
const startedAt = Date.now();
|
|
64
64
|
const structuredStep = `model:${phase}`;
|
|
65
|
-
const
|
|
65
|
+
const modelAbortController = new AbortController();
|
|
66
|
+
const parentSignal = telemetry?.signal;
|
|
67
|
+
const abortFromParent = () => modelAbortController.abort(parentSignal?.reason);
|
|
68
|
+
if (parentSignal?.aborted) abortFromParent();
|
|
69
|
+
else parentSignal?.addEventListener('abort', abortFromParent, { once: true });
|
|
66
70
|
if (telemetry?.runId) {
|
|
67
71
|
engine.updateRunProgress(telemetry.runId, {
|
|
68
72
|
currentPhase: 'model',
|
|
@@ -86,13 +90,16 @@ async function requestStructuredJson(engine, {
|
|
|
86
90
|
model,
|
|
87
91
|
maxTokens,
|
|
88
92
|
reasoningEffort: reasoningEffort || engine.getReasoningEffort(providerName, {}),
|
|
89
|
-
signal,
|
|
93
|
+
signal: modelAbortController.signal,
|
|
90
94
|
}
|
|
91
95
|
),
|
|
92
|
-
telemetry || {},
|
|
96
|
+
{ ...(telemetry || {}), modelAbortController },
|
|
93
97
|
`${phase} model call`,
|
|
94
98
|
),
|
|
95
|
-
{
|
|
99
|
+
{
|
|
100
|
+
label: `Engine ${model} (structured)`,
|
|
101
|
+
isRetryable: (err) => !modelAbortController.signal.aborted && isTransientError(err),
|
|
102
|
+
}
|
|
96
103
|
);
|
|
97
104
|
completed = true;
|
|
98
105
|
if (telemetry?.runId && telemetry?.userId) {
|
|
@@ -117,6 +124,7 @@ async function requestStructuredJson(engine, {
|
|
|
117
124
|
usage: normalizedUsage?.totalTokens || 0,
|
|
118
125
|
};
|
|
119
126
|
} finally {
|
|
127
|
+
parentSignal?.removeEventListener('abort', abortFromParent);
|
|
120
128
|
const runMeta = telemetry?.runId ? engine.getRunMeta(telemetry.runId) : null;
|
|
121
129
|
if (runMeta?.progressLedger?.currentStep === structuredStep) {
|
|
122
130
|
engine.updateRunProgress(telemetry.runId, {
|
|
@@ -102,7 +102,7 @@ function persistEnvValue(key, value) {
|
|
|
102
102
|
} catch { }
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
async function refreshClaudeCodeAccessToken(refreshToken, fetchImpl = fetch) {
|
|
105
|
+
async function refreshClaudeCodeAccessToken(refreshToken, fetchImpl = fetch, signal = null) {
|
|
106
106
|
if (!refreshToken) return null;
|
|
107
107
|
const response = await fetchImpl(CLAUDE_CODE_TOKEN_URL, {
|
|
108
108
|
method: 'POST',
|
|
@@ -116,6 +116,7 @@ async function refreshClaudeCodeAccessToken(refreshToken, fetchImpl = fetch) {
|
|
|
116
116
|
refresh_token: refreshToken,
|
|
117
117
|
client_id: CLAUDE_CODE_CLIENT_ID,
|
|
118
118
|
}),
|
|
119
|
+
signal,
|
|
119
120
|
});
|
|
120
121
|
|
|
121
122
|
const text = await response.text();
|
|
@@ -209,8 +210,8 @@ class ClaudeCodeProvider extends AnthropicProvider {
|
|
|
209
210
|
});
|
|
210
211
|
}
|
|
211
212
|
|
|
212
|
-
async refreshClient() {
|
|
213
|
-
const refreshed = await refreshClaudeCodeAccessToken(this.refreshToken, this.fetchImpl);
|
|
213
|
+
async refreshClient(signal = null) {
|
|
214
|
+
const refreshed = await refreshClaudeCodeAccessToken(this.refreshToken, this.fetchImpl, signal);
|
|
214
215
|
if (!refreshed?.access) return false;
|
|
215
216
|
this.authToken = refreshed.access;
|
|
216
217
|
this.refreshToken = refreshed.refresh || this.refreshToken;
|
|
@@ -244,7 +245,7 @@ class ClaudeCodeProvider extends AnthropicProvider {
|
|
|
244
245
|
if ((!isAuthenticationError(err) && !isInferenceScopeError(err)) || !this.refreshToken) {
|
|
245
246
|
throw formatClaudeCodeCredentialError(err);
|
|
246
247
|
}
|
|
247
|
-
await this.refreshClient();
|
|
248
|
+
await this.refreshClient(options.signal);
|
|
248
249
|
try {
|
|
249
250
|
return await super.chat(messages, tools, options);
|
|
250
251
|
} catch (retryErr) {
|
|
@@ -260,7 +261,7 @@ class ClaudeCodeProvider extends AnthropicProvider {
|
|
|
260
261
|
if ((!isAuthenticationError(err) && !isInferenceScopeError(err)) || !this.refreshToken) {
|
|
261
262
|
throw formatClaudeCodeCredentialError(err);
|
|
262
263
|
}
|
|
263
|
-
await this.refreshClient();
|
|
264
|
+
await this.refreshClient(options.signal);
|
|
264
265
|
try {
|
|
265
266
|
yield* super.stream(messages, tools, options);
|
|
266
267
|
} catch (retryErr) {
|
|
@@ -26,7 +26,7 @@ class GithubCopilotProvider extends OpenAIProvider {
|
|
|
26
26
|
this._refreshPromise = null;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
async _refreshCopilotToken() {
|
|
29
|
+
async _refreshCopilotToken(signal = null) {
|
|
30
30
|
if (this._refreshPromise) return this._refreshPromise;
|
|
31
31
|
|
|
32
32
|
const now = Math.floor(Date.now() / 1000);
|
|
@@ -46,7 +46,8 @@ class GithubCopilotProvider extends OpenAIProvider {
|
|
|
46
46
|
'Authorization': `token ${this.githubToken}`,
|
|
47
47
|
'Accept': 'application/json',
|
|
48
48
|
'User-Agent': 'NeoAgent/1.0.0'
|
|
49
|
-
}
|
|
49
|
+
},
|
|
50
|
+
signal,
|
|
50
51
|
});
|
|
51
52
|
|
|
52
53
|
if (!res.ok) {
|
|
@@ -79,17 +80,17 @@ class GithubCopilotProvider extends OpenAIProvider {
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
async chat(messages, tools = [], options = {}) {
|
|
82
|
-
await this._refreshCopilotToken();
|
|
83
|
+
await this._refreshCopilotToken(options.signal);
|
|
83
84
|
return super.chat(messages, tools, options);
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
async *stream(messages, tools = [], options = {}) {
|
|
87
|
-
await this._refreshCopilotToken();
|
|
88
|
+
await this._refreshCopilotToken(options.signal);
|
|
88
89
|
yield* super.stream(messages, tools, options);
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
async analyzeImage(options = {}) {
|
|
92
|
-
await this._refreshCopilotToken();
|
|
93
|
+
await this._refreshCopilotToken(options.signal);
|
|
93
94
|
return super.analyzeImage(options);
|
|
94
95
|
}
|
|
95
96
|
}
|
|
@@ -41,7 +41,7 @@ function persistEnvValue(key, value) {
|
|
|
41
41
|
} catch { }
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
async function refreshGrokOAuthAccessToken(refreshToken, fetchImpl = fetch) {
|
|
44
|
+
async function refreshGrokOAuthAccessToken(refreshToken, fetchImpl = fetch, signal = null) {
|
|
45
45
|
if (!refreshToken) return null;
|
|
46
46
|
const response = await fetchImpl(GROK_OAUTH_TOKEN_URL, {
|
|
47
47
|
method: 'POST',
|
|
@@ -54,6 +54,7 @@ async function refreshGrokOAuthAccessToken(refreshToken, fetchImpl = fetch) {
|
|
|
54
54
|
refresh_token: refreshToken,
|
|
55
55
|
client_id: GROK_OAUTH_CLIENT_ID,
|
|
56
56
|
}),
|
|
57
|
+
signal,
|
|
57
58
|
});
|
|
58
59
|
|
|
59
60
|
const text = await response.text();
|
|
@@ -102,8 +103,8 @@ class GrokOAuthProvider extends GrokProvider {
|
|
|
102
103
|
this.fetchImpl = config.fetch || fetch;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
async refreshClient() {
|
|
106
|
-
const refreshed = await refreshGrokOAuthAccessToken(this.refreshToken, this.fetchImpl);
|
|
106
|
+
async refreshClient(signal = null) {
|
|
107
|
+
const refreshed = await refreshGrokOAuthAccessToken(this.refreshToken, this.fetchImpl, signal);
|
|
107
108
|
if (!refreshed?.access) return false;
|
|
108
109
|
this.authToken = refreshed.access;
|
|
109
110
|
this.refreshToken = refreshed.refresh || this.refreshToken;
|
|
@@ -122,7 +123,7 @@ class GrokOAuthProvider extends GrokProvider {
|
|
|
122
123
|
return await super.chat(messages, tools, options);
|
|
123
124
|
} catch (err) {
|
|
124
125
|
if (err?.status !== 401 || !this.refreshToken) throw err;
|
|
125
|
-
await this.refreshClient();
|
|
126
|
+
await this.refreshClient(options.signal);
|
|
126
127
|
return await super.chat(messages, tools, options);
|
|
127
128
|
}
|
|
128
129
|
}
|
|
@@ -132,7 +133,7 @@ class GrokOAuthProvider extends GrokProvider {
|
|
|
132
133
|
yield* super.stream(messages, tools, options);
|
|
133
134
|
} catch (err) {
|
|
134
135
|
if (err?.status !== 401 || !this.refreshToken) throw err;
|
|
135
|
-
await this.refreshClient();
|
|
136
|
+
await this.refreshClient(options.signal);
|
|
136
137
|
yield* super.stream(messages, tools, options);
|
|
137
138
|
}
|
|
138
139
|
}
|
|
@@ -8,22 +8,28 @@ class OllamaProvider extends BaseProvider {
|
|
|
8
8
|
this.models = [];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
async listModels() {
|
|
11
|
+
async listModels(signal = null) {
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
const abortFromParent = () => controller.abort(signal?.reason);
|
|
14
|
+
if (signal?.aborted) abortFromParent();
|
|
15
|
+
else signal?.addEventListener('abort', abortFromParent, { once: true });
|
|
16
|
+
const timer = setTimeout(() => controller.abort(), 5000);
|
|
12
17
|
try {
|
|
13
|
-
const controller = new AbortController();
|
|
14
|
-
const timer = setTimeout(() => controller.abort(), 5000);
|
|
15
18
|
const res = await fetch(`${this.baseUrl}/api/tags`, { signal: controller.signal });
|
|
16
|
-
clearTimeout(timer);
|
|
17
19
|
const data = await res.json();
|
|
18
20
|
this.models = (data.models || []).map(m => m.name);
|
|
19
21
|
return this.models;
|
|
20
|
-
} catch {
|
|
22
|
+
} catch (err) {
|
|
23
|
+
if (signal?.aborted) throw err;
|
|
21
24
|
return [];
|
|
25
|
+
} finally {
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
signal?.removeEventListener('abort', abortFromParent);
|
|
22
28
|
}
|
|
23
29
|
}
|
|
24
30
|
|
|
25
|
-
async ensureModel(model) {
|
|
26
|
-
const models = await this.listModels();
|
|
31
|
+
async ensureModel(model, signal = null) {
|
|
32
|
+
const models = await this.listModels(signal);
|
|
27
33
|
// Normalization: Ollama often adds :latest if no tag is specified
|
|
28
34
|
const normalizedModel = model.includes(':') ? model : `${model}:latest`;
|
|
29
35
|
const found = models.some(m => m === model || m === normalizedModel);
|
|
@@ -42,7 +48,8 @@ class OllamaProvider extends BaseProvider {
|
|
|
42
48
|
const res = await fetch(`${this.baseUrl}/api/pull`, {
|
|
43
49
|
method: 'POST',
|
|
44
50
|
headers: { 'Content-Type': 'application/json' },
|
|
45
|
-
body: JSON.stringify({ name: model, stream: false })
|
|
51
|
+
body: JSON.stringify({ name: model, stream: false }),
|
|
52
|
+
signal,
|
|
46
53
|
});
|
|
47
54
|
if (!res.ok) throw new Error(`Pull failed: ${res.statusText}`);
|
|
48
55
|
console.log(`[Ollama] Model '${model}' pulled successfully.`);
|
|
@@ -54,7 +61,7 @@ class OllamaProvider extends BaseProvider {
|
|
|
54
61
|
message: `Local Ollama model '${model}' is ready.`
|
|
55
62
|
});
|
|
56
63
|
// Refresh local model list
|
|
57
|
-
await this.listModels();
|
|
64
|
+
await this.listModels(signal);
|
|
58
65
|
return true;
|
|
59
66
|
} catch (e) {
|
|
60
67
|
this.onStatus?.({
|
|
@@ -144,7 +151,7 @@ class OllamaProvider extends BaseProvider {
|
|
|
144
151
|
|
|
145
152
|
async chat(messages, tools = [], options = {}) {
|
|
146
153
|
const model = options.model || this.config.model || 'llama3.1';
|
|
147
|
-
await this.ensureModel(model);
|
|
154
|
+
await this.ensureModel(model, options.signal);
|
|
148
155
|
|
|
149
156
|
let res;
|
|
150
157
|
try {
|
|
@@ -183,7 +190,7 @@ class OllamaProvider extends BaseProvider {
|
|
|
183
190
|
|
|
184
191
|
async *stream(messages, tools = [], options = {}) {
|
|
185
192
|
const model = options.model || this.config.model || 'llama3.1';
|
|
186
|
-
await this.ensureModel(model);
|
|
193
|
+
await this.ensureModel(model, options.signal);
|
|
187
194
|
|
|
188
195
|
let res;
|
|
189
196
|
try {
|