neoagent 2.4.1-beta.41 → 2.4.1-beta.42

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.4.1-beta.41",
3
+ "version": "2.4.1-beta.42",
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
- 10f7a7ce7ccab52934b54e2a50e18e13
1
+ ed88f04225b62218697804faf6ff5b05
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"c416acfeb8126e097f758c664aaa3da929e27d
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "2612315106" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "1217250469" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
41
41
  }
42
42
  });
@@ -134248,7 +134248,7 @@ r===$&&A.b()
134248
134248
  o.push(A.jP(p,A.jb(!1,new A.a2(B.up,A.dl(new A.cw(B.hs,new A.a7z(r,p),p),p,p),p),!1,B.H,!0),p,p,0,0,0,p))}r=!1
134249
134249
  if(!s.ay)if(!s.ch){r=s.e
134250
134250
  r===$&&A.b()
134251
- r=B.b.t("mq59p4rg-7a1f069").length!==0&&r.b}if(r){r=s.d
134251
+ r=B.b.t("mq5hfdrk-acc903b").length!==0&&r.b}if(r){r=s.d
134252
134252
  r===$&&A.b()
134253
134253
  r=r.av&&!r.aQ?84:0
134254
134254
  q=s.e
@@ -139889,7 +139889,7 @@ $S:0}
139889
139889
  A.ZW.prototype={}
139890
139890
  A.SC.prototype={
139891
139891
  nb(a){var s=this
139892
- if(B.b.t("mq59p4rg-7a1f069").length===0||s.a!=null)return
139892
+ if(B.b.t("mq5hfdrk-acc903b").length===0||s.a!=null)return
139893
139893
  s.AR()
139894
139894
  s.a=A.qh(B.Rl,new A.baD(s))},
139895
139895
  AR(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f
@@ -139907,7 +139907,7 @@ if(!t.f.b(k)){s=1
139907
139907
  break}i=J.U(k,"buildId")
139908
139908
  h=i==null?null:B.b.t(J.p(i))
139909
139909
  j=h==null?"":h
139910
- if(J.be(j)===0||J.d(j,"mq59p4rg-7a1f069")){s=1
139910
+ if(J.be(j)===0||J.d(j,"mq5hfdrk-acc903b")){s=1
139911
139911
  break}n.b=!0
139912
139912
  n.D()
139913
139913
  p=2
@@ -139924,7 +139924,7 @@ case 2:return A.i(o.at(-1),r)}})
139924
139924
  return A.k($async$AR,r)},
139925
139925
  vx(){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
139926
139926
  var $async$vx=A.h(function(a2,a3){if(a2===1){o.push(a3)
139927
- s=p}for(;;)switch(s){case 0:if(B.b.t("mq59p4rg-7a1f069").length===0||n.c){s=1
139927
+ s=p}for(;;)switch(s){case 0:if(B.b.t("mq5hfdrk-acc903b").length===0||n.c){s=1
139928
139928
  break}n.c=!0
139929
139929
  n.D()
139930
139930
  p=4
@@ -224,6 +224,14 @@ async function refreshProviderModelList(providerId, apiKey, baseUrl) {
224
224
  return models;
225
225
  } catch (err) {
226
226
  console.warn(`[Models] Failed to refresh ${providerId} models:`, err.message);
227
+ // Always record a lastRefresh so we don't hammer the API on every request.
228
+ // Permanent errors (auth/billing/credits) get a longer backoff.
229
+ const isPermanent = /401|403|unauthorized|forbidden|credits|spending/i.test(err.message);
230
+ const backoff = isPermanent ? 30 * 60 * 1000 : DYNAMIC_REFRESH_INTERVAL;
231
+ providerModelCache.set(cacheKey, {
232
+ models: existing?.models || [],
233
+ lastRefresh: now - DYNAMIC_REFRESH_INTERVAL + backoff,
234
+ });
227
235
  return existing?.models || [];
228
236
  }
229
237
  }
@@ -16,6 +16,8 @@ class OpenRouterProvider extends OpenAICompatibleProvider {
16
16
  this.client = new OpenAI({
17
17
  apiKey: config.apiKey || process.env.OPENROUTER_API_KEY,
18
18
  baseURL: this.baseURL,
19
+ timeout: 90_000, // 90 s — free models can queue; avoids hanging forever
20
+ maxRetries: 0, // engine handles retries; avoid SDK silently re-queuing slow models
19
21
  defaultHeaders: {
20
22
  'HTTP-Referer': 'https://github.com/NeoLabs-Systems/NeoAgent',
21
23
  'X-Title': 'NeoAgent',
@@ -57,6 +59,14 @@ class OpenRouterProvider extends OpenAICompatibleProvider {
57
59
  return params;
58
60
  }
59
61
 
62
+ _extractOpenRouterError(obj) {
63
+ if (!obj) return null;
64
+ const e = obj.error;
65
+ if (!e) return null;
66
+ const msg = e.message || (typeof e === 'string' ? e : JSON.stringify(e));
67
+ return `OpenRouter: ${msg}${e.code ? ` (code ${e.code})` : ''}`;
68
+ }
69
+
60
70
  async chat(messages, tools = [], options = {}) {
61
71
  const model = options.model || this.getDefaultModel();
62
72
  const params = this._buildParams(model, messages, tools, options);
@@ -66,6 +76,12 @@ class OpenRouterProvider extends OpenAICompatibleProvider {
66
76
  } catch (err) {
67
77
  throw new Error(`OpenRouter request failed: ${err?.message || String(err)}`);
68
78
  }
79
+ // OpenRouter returns HTTP 200 even for errors (rate limits, model unavailable, etc.)
80
+ const orErr = this._extractOpenRouterError(response);
81
+ if (orErr) throw new Error(orErr);
82
+ if (!response?.choices?.length) {
83
+ throw new Error(`OpenRouter: model returned no choices (may be rate-limited or unavailable)`);
84
+ }
69
85
  return this.normalizeResponse(response);
70
86
  }
71
87
 
@@ -88,6 +104,9 @@ class OpenRouterProvider extends OpenAICompatibleProvider {
88
104
  let content = '';
89
105
  let finalUsage = null;
90
106
 
107
+ // The OpenAI SDK converts OpenRouter's SSE error events (data.error) into
108
+ // APIErrors before yielding — so we wrap the loop to add context.
109
+ try {
91
110
  for await (const chunk of stream) {
92
111
  if (chunk.usage && (!chunk.choices || chunk.choices.length === 0)) {
93
112
  finalUsage = this.normalizeUsage(chunk.usage);
@@ -127,6 +146,10 @@ class OpenRouterProvider extends OpenAICompatibleProvider {
127
146
  return;
128
147
  }
129
148
  }
149
+ } catch (err) {
150
+ // Re-throw SDK APIErrors (from OpenRouter SSE error events) with OpenRouter context.
151
+ throw new Error(`OpenRouter: ${err?.message || String(err)}`);
152
+ }
130
153
 
131
154
  if (toolCalls.length > 0) {
132
155
  yield { type: 'tool_calls', toolCalls, content, usage: finalUsage };