beast-agent 2.58.1 → 2.58.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.58.1",
4
+ "version": "2.58.2",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -811,9 +811,13 @@ class Engine {
811
811
  payload = squeeze.apply(payload, { cacheKey: String(session.id || '') });
812
812
  }
813
813
  const cands = this._chatCandidates(sel, wasVision);
814
+ /* AYLIK KOTASI DOLAN SAĞLAYICILAR: aynı sağlayıcının diğer modellerini
815
+ denemek boşuna (kota hesap/workspace bazlı) — bu turda atlanır. */
816
+ const quotaOut = new Set();
814
817
  let lastErr = null;
815
818
  for (let i = 0; i < cands.length; i++) {
816
819
  const c = cands[i];
820
+ if (quotaOut.has(String(c.providerId))) continue;
817
821
  let retried413 = false;
818
822
  try {
819
823
  /* provider bazlı girdi limiti: payload aşıyorsa sıkıştırılıp öyle gönderilir.
@@ -903,6 +907,15 @@ class Engine {
903
907
  if (e && (e.name === 'AbortError' || (signal && signal.aborted))) throw e;
904
908
  lastErr = e;
905
909
  const msgStr = String((e && e.message) || '');
910
+ /* AYLIK KOTA: bu sağlayıcı tur boyunca kapalı — kullanıcıya net söyle,
911
+ FALLOUT varsa farklı sağlayıcıya geç */
912
+ if (e && e.usageLimit) {
913
+ quotaOut.add(String(c.providerId));
914
+ emitSafe(this, session.id, {
915
+ type: 'status',
916
+ status: `⛔ ${c.providerName}: aylık kullanım limiti doldu — aynı sağlayıcının diğer modelleri atlanıyor`,
917
+ });
918
+ }
906
919
  /* 413 TPM: sağlayıcı gerçek istek boyutunu söylüyor → kalibre et,
907
920
  aynı modeli yeniden sıkıştırıp BİR KEZ daha dene */
908
921
  const m413 = /Requested\s+([\d,]+)/i.exec(msgStr) || (e && e.status === 413 ? [, 0] : null);
@@ -925,7 +938,11 @@ class Engine {
925
938
  ([400, 404, 422].includes(e.status) ||
926
939
  /\bno endpoints\b/i.test(msgStr) ||
927
940
  /\bimage input\b/i.test(msgStr));
928
- const next = cands[i + 1];
941
+ /* sıradaki aday: kotası dolmuş sağlayıcılar atlanır */
942
+ let next = null;
943
+ for (let j = i + 1; j < cands.length; j++) {
944
+ if (!quotaOut.has(String(cands[j].providerId))) { next = cands[j]; break; }
945
+ }
929
946
  if (!next) break; // zincir bitti — hatayı yukarı fırlat
930
947
  if (imgIssue) {
931
948
  emitSafe(this, session.id, {
@@ -1417,15 +1434,21 @@ class Engine {
1417
1434
  files = fs.readdirSync(this.sessionsDir).filter((f) => f.endsWith('.jsonl'));
1418
1435
  } catch {}
1419
1436
  const out = [];
1437
+ const fin = [];
1420
1438
  for (const f of files) {
1421
1439
  const id = f.replace(/\.jsonl$/, '');
1422
1440
  const v = this._view(this._load(id));
1423
1441
  if (v.isBg) continue;
1424
1442
  if (v.isBotDm) continue; // botlar arası DM — yalnız admin DM Log ekranında
1425
- out.push(v);
1426
- }
1427
- out.sort((a, b) => String(b.updatedAt).localeCompare(String(a.updatedAt)));
1428
- return out.slice(0, 100);
1443
+ /* FİNANS GEÇMİŞİ KAYBOLMAZ: normal sohbet listesi 100 ile sınırlı olsa da
1444
+ finance sohbetleri AYRICA listelenir — 100+ oturumu olan kullanıcıda
1445
+ eski finance sohbetleri listeden düşüp "kaybolmuş" görünmez. */
1446
+ (v.finance ? fin : out).push(v);
1447
+ }
1448
+ const byNew = (a, b) => String(b.updatedAt).localeCompare(String(a.updatedAt));
1449
+ out.sort(byNew);
1450
+ fin.sort(byNew);
1451
+ return out.slice(0, 100).concat(fin.slice(0, 60)).sort(byNew);
1429
1452
  }
1430
1453
 
1431
1454
  /* Panel oturumları: belirli bir bgTitle taşıyan gizli oturumlar (Beast Code,
package/src/agent/llm.js CHANGED
@@ -64,7 +64,27 @@ const HINTS = {
64
64
  504: 'sağlayıcı zaman aşımı',
65
65
  };
66
66
 
67
+ /* AYLIK KOTA / KULLANIM LİMİTİ: 429 gövdesindeki "usage limit / quota" sinyali
68
+ hız limitinden FARKLI — tekrar denemek çözmez, aynı sağlayıcının diğer
69
+ modelleri de kapalıdır. Bu ayrım motorun FALLOUT kararını da besler. */
70
+ function isUsageLimitDetail(detail) {
71
+ return /GoUsageLimit|usage[_ ]?limit|insufficient_quota|monthly limit|kullanım limiti/i.test(
72
+ String(detail || '')
73
+ );
74
+ }
75
+
67
76
  function friendlyError(status, statusText, detail) {
77
+ const d = String(detail || '');
78
+ if (status === 429 && isUsageLimitDetail(d)) {
79
+ let msg = 'HTTP 429 — AYLIK KULLANIM LİMİTİ DOLDU (hız limiti değil)';
80
+ const reset = /Resets?\s+in\s+([^."}]+)/i.exec(d);
81
+ if (reset) msg += ` — sıfırlanma: ${reset[1].trim()} sonra`;
82
+ const link = /(https?:\/\/[^\s"}]+)/.exec(d);
83
+ if (link) msg += `\nDevam için: ${link[1]}`;
84
+ msg +=
85
+ '\n(çözüm: sağlayıcı panelinden bakiyeden kullanımı aç, başka model/sağlayıcı seç ya da sıfırlanmayı bekle)';
86
+ return msg;
87
+ }
68
88
  const hint = HINTS[status];
69
89
  let msg = `HTTP ${status} ${statusText}`;
70
90
  if (hint) msg += ` — ${hint}`;
@@ -72,9 +92,33 @@ function friendlyError(status, statusText, detail) {
72
92
  if ([500, 502, 503, 504].includes(status)) {
73
93
  msg += '\n(geçici olabilir: birkaç saniye sonra tekrar dene ya da model seçiciyi açıp başka bir model dene)';
74
94
  }
95
+ if (isTransientDetail(status, d)) {
96
+ msg += '\n(sağlayıcı geçici hatası — otomatik tekrar denenir; sürerse başka model/sağlayıcı dene)';
97
+ }
75
98
  return msg;
76
99
  }
77
100
 
101
+ /* SAĞLAYICI GEÇİCİ HATASI: bazı sağlayıcılar upstream arızasını HTTP 400
102
+ gövdesinde "server_error" olarak döner — istemci hatası değildir, tekrar
103
+ denenmeli. Bu bayrak olmadan 400 kalıcı sayılıp model boşuna "çökmüş" olur. */
104
+ function isTransientDetail(status, detail) {
105
+ if (status !== 400 && status !== 500) return false;
106
+ return /"(type|code)"\s*:\s*"server_error"|Error from provider|Upstream (request failed|response was not valid JSON)|temporar/i.test(
107
+ String(detail || '')
108
+ );
109
+ }
110
+
111
+ /* HTTP hatasını zengin bayraklarla kur — usageLimit/transient bilgisi retry ve
112
+ FALLOUT katmanlarına taşınır (boşa tekrar/boşa çökme olmaz). */
113
+ function httpError(status, statusText, detail, headers) {
114
+ const err = new Error(friendlyError(status, statusText, detail));
115
+ err.status = status;
116
+ if (isUsageLimitDetail(detail)) err.usageLimit = true;
117
+ if (isTransientDetail(status, detail)) err.transient = true;
118
+ err.retryAfterMs = parseRetryAfter(headers && (headers.get('retry-after-ms') || headers.get('retry-after')));
119
+ return err;
120
+ }
121
+
78
122
  /* opencode cache disiplini (provider/transform.ts:1262-1276 port): destekleyen
79
123
  sağlayıcılara oturum-sabit önbellek anahtarı gönderilir — aynı önek tekrar
80
124
  kullanıldığında girdi tokenleri cache'ten okunur (~%90 ucuz + hızlı TTFT) */
@@ -302,9 +346,15 @@ async function withRetries(fn, { signal, onRetry } = {}) {
302
346
  return await fn();
303
347
  } catch (e) {
304
348
  const aborted = e && (e.name === 'AbortError' || (signal && signal.aborted));
349
+ if (aborted) throw e;
350
+ /* AYLIK KOTA DOLDU: tekrar denemek boşa — hemen fırlat ki motor
351
+ FALLOUT ile sıradaki sağlayıcıya/modele geçebilsin. */
352
+ if (e && e.usageLimit) throw e;
305
353
  const status = e && e.status;
306
- const netErr = !aborted && status === undefined;
307
- const retriable = !aborted && (status === undefined || RETRYABLE_STATUS.has(status));
354
+ const netErr = status === undefined;
355
+ /* 400 gövdesinde "server_error/upstream" diyen sağlayıcı hataları geçicidir
356
+ (transient) — kalıcı 400 gibi davranıp modeli boşuna "çökertme". */
357
+ const retriable = status === undefined || RETRYABLE_STATUS.has(status) || !!(e && e.transient);
308
358
  if (!retriable) throw e;
309
359
  const cap = netErr ? NET_MAX_RETRIES : MAX_RETRIES;
310
360
  if (attempt >= cap) throw e;
@@ -409,12 +459,7 @@ async function streamOnce(sel, body, { signal, onDelta, onRetry } = {}, drops =
409
459
  try {
410
460
  detail = (await r.text()).slice(0, 300);
411
461
  } catch {}
412
- const err = new Error(friendlyError(r.status, r.statusText, detail));
413
- err.status = r.status;
414
- err.retryAfterMs = parseRetryAfter(
415
- r.headers && (r.headers.get('retry-after-ms') || r.headers.get('retry-after'))
416
- );
417
- throw err;
462
+ throw httpError(r.status, r.statusText, detail, r.headers);
418
463
  }
419
464
  return r;
420
465
  },
@@ -590,12 +635,7 @@ async function chatOnce(sel, body, { signal, onRetry, onParamDrop, onEffortDowng
590
635
  try {
591
636
  detail = (await r.text()).slice(0, 300);
592
637
  } catch {}
593
- const err = new Error(friendlyError(r.status, r.statusText, detail));
594
- err.status = r.status;
595
- err.retryAfterMs = parseRetryAfter(
596
- r.headers && (r.headers.get('retry-after-ms') || r.headers.get('retry-after'))
597
- );
598
- throw err;
638
+ throw httpError(r.status, r.statusText, detail, r.headers);
599
639
  }
600
640
  return r;
601
641
  },