beast-agent 0.24.5 → 0.25.1

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": "0.24.5",
4
+ "version": "0.25.1",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -333,41 +333,6 @@ class Engine {
333
333
  else this.sessionTools.delete(id);
334
334
  }
335
335
 
336
- /* ANA SOHBETE DAVETLİ BOTLAR: oturuma guest bot ekle/çıkar (persist) */
337
- setSessionGuests(sessionId, guests) {
338
- const s = this._load(String(sessionId || ''));
339
- if (!s) return false;
340
- const items = (Array.isArray(guests) ? guests : [])
341
- .slice(0, 4)
342
- .map((g) => ({ id: String((g && g.id) || ''), code: String((g && g.code) || ''), name: String((g && g.name) || '').slice(0, 40) }))
343
- .filter((g) => g.id && g.code);
344
- s.guests = items;
345
- try {
346
- const file = this._file(s.id);
347
- const lines = fs.readFileSync(file, 'utf8').split('\n').filter((l) => l.trim());
348
- const kept = lines.filter((l) => {
349
- try { return JSON.parse(l).t !== 'guests'; } catch { return true; }
350
- });
351
- const out = kept.join('\n') + (items.length ? '\n' + JSON.stringify({ t: 'guests', items }) : '') + '\n';
352
- const tmp = file + '.tmp';
353
- fs.writeFileSync(tmp, out);
354
- fs.renameSync(tmp, file);
355
- } catch {}
356
- return true;
357
- }
358
-
359
- /* Davetli botlar system prompt bloğu — ajan bot_dm ile onlara danışır */
360
- _guestsBlock(session) {
361
- const g = Array.isArray(session && session.guests) ? session.guests : [];
362
- if (!g.length) return '';
363
- const lines = g.map((x) => `- ${x.name} (kod: ${x.code})`).join('\n');
364
- return (
365
- `# DAVETLİ BOTLAR\n` +
366
- `Bu sohbete şu botlar davet edildi. Kullanıcı onlara hitap ederse veya uzmanlıkları gereken bir konu olursa ` +
367
- `bot_dm aracıyla (to=5 haneli kod) onlara yaz; aldığın cevabı [BotAdı] etiketiyle kullanıcıya aktar:\n${lines}`
368
- );
369
- }
370
-
371
336
  /* Oturumun bağlı olduğu MÜŞTERİ botu (admin/seasız → null → global hafıza) */
372
337
  _sessionBotCtx(session) {
373
338
  if (!session || !session.botId || typeof this.resolveBot !== 'function') return null;
@@ -947,9 +912,6 @@ class Engine {
947
912
  session.isBotDm = true;
948
913
  session.dmA = String(rec.a || '');
949
914
  session.dmB = String(rec.b || '');
950
- } else if (rec.t === 'guests') {
951
- /* ana sohbete davetli botlar */
952
- session.guests = Array.isArray(rec.items) ? rec.items : [];
953
915
  } else if (rec.t === 'msg') {
954
916
  delete rec.t;
955
917
  session.messages.push(rec);
@@ -1086,7 +1048,6 @@ class Engine {
1086
1048
  count: s.messages.length,
1087
1049
  isBg,
1088
1050
  isBotDm: !!s.isBotDm,
1089
- guests: Array.isArray(s.guests) ? s.guests : [],
1090
1051
  bgStatus: isBg ? (job ? job.status : null) : null,
1091
1052
  };
1092
1053
  }
@@ -1330,11 +1291,11 @@ class Engine {
1330
1291
  : 'Bağımsız alt-işleri delegate_task ile devret; kendi başına halledebileceğin işleri devretme.',
1331
1292
  'Güncel/dış bilgi gerekiyorsa web_search kullan (zincir DAHİLİ tarayıcıyla başlar — gerçek Chromium ile Google); hızlı ham metin okuması için http_fetch kullan.',
1332
1293
  'ARAMA-DİSİPLİN: bir bilgiyi 2-3 denemede bulamazsan TAKILMA — farklı bir açıya/sorguya geç, yine yoksa bulabildiğin kısmi bilgiyle cevap ver ve neyi bulamadığını açıkça söyle. Kapalı/gizli içerik (private profil, login arkası veri) peşinde KOŞMA — bulunamayacağı belliyse hemen vazgeç.',
1333
- 'TARAYICI VARSAYILANI (ZORUNLU): kullanıcı "şu siteyi aç", "bunu ara / google\u2019da ara", "şu sayfaya git" gibi bir web isteği verdiğinde HEP DAHİLİ TARAYICIYI KULLAN (browser_open ile aç → browser_snapshot al → ref numaralarıyla browser_click/browser_type/browser_select ile hareket et, browser_read ile metin oku; sadece görsel yerleşim/grafik gerekiyorsa browser_screenshot çek). Panel ekranın sağında açılır ve kullanıcı da sayfayı anında görür; JS/login/SPA/dinamik içerik için idealdir. Bu kural TÜM oturumları kapsar — masaüstü sohbeti ve çok kullanıcılı botlar (WhatsApp vb.) dahil.' +
1294
+ 'TARAYICI VARSAYILANI (ZORUNLU): kullanıcı "şu siteyi aç", "bunu ara / google\u2019da ara", "şu sayfaya git" gibi bir web isteği verdiğinde HEP DAHİLİ TARAYICIYI KULLAN (browser_open ile aç → açılış yanıtında hazır snapshot gelir → ref numaralarıyla browser_click/browser_type/browser_select ile hareket et; HER eylem yanıtında taze snapshot döner — ayrıca browser_snapshot çağırma, yanıttaki refleri kullan; browser_read ile metin oku; sadece görsel yerleşim/grafik gerekiyorsa browser_screenshot çek). Tarih/saat alanlarını (type=date/time) browser_type ile DÜZ METİN yaz ("15.03.2026", "2026-03-15") — takvimden tıklamaya çalışma, alan programatik ayarlanır. Panel ekranın sağında açılır ve kullanıcı da sayfayı anında görür; JS/login/SPA/dinamik içerik için idealdir. Bu kural TÜM oturumları kapsar — masaüstü sohbeti ve çok kullanıcılı botlar (WhatsApp vb.) dahil.' +
1334
1295
  ' DIŞ TARAYICI İSTİSNASI: kullanıcı AÇIKÇA "chrome\u2019da aç", "firefox\u2019ta aç", "başka tarayıcıda aç", "normal tarayıcıda aç", "kendi tarayıcımda aç" derse O ZAMAN run_command ile `start "" <url>` çalıştır — sistem varsayılan (dış) tarayıcısında açılır. Kullanıcı dış tarayıcı istemedikçe ASLA dış tarayıcı açma.' +
1335
1296
  ' Görselleri GÖREMEMİYORSAN (metin-model) görüntüdeki metni okumak için ocr_read kullan: source:"browser" ile tarayıcı sayfasını, "screen" ile masaüstünü OCR\u2019la okursun (captcha/canvas/görsel metin dahil).' +
1336
1297
  (this.ceoMode ? ' (CEO: bu araçları KENDİN ÇAĞIRMA — içinde tarayıcı geçen işi run_background ile paralel ajana devret.)' : ''),
1337
- 'Tarayıcı eylemlerinin yanıtındaki recent günlüğü ve navigated bilgisini takip et; sayfa değişmişse yeni snapshot al.',
1298
+ 'Tarayıcı eylemlerinin yanıtındaki recent günlüğü ve navigated bilgisini takip et; eylem yanıtları zaten taze snapshot içerir — refler tutarsız görünürse yeni snapshot al.',
1338
1299
  'CONUŞMA ODAĞI SENDE KALSIN: kullanıcı seninle konuşurken iş çıkmışsa — uzun da olsa UFACIK da (tek komutluk dizin listesi, tek dosya okuma, tek arama…) — run_background ile PARALEL ajana devret; ana sohbet hiçbir işi beklemez; bittiğinde özet otomatik düşer.',
1339
1300
  'Python işleri için python_run kullan: küçük betikler inline code ile; tekrarlayan işler %APPDATA%\\beast\\scripts klasöründeki dosyalarla (ör. news.py = RSS haber toplayıcı: args ["--limit","8","--json"]). Python kurulu olmasa bile ilk çağrıda taşınabilir gömülü runtime otomatik iner.',
1340
1301
  'PYTHON DURUMU: makinede sistem Python\'u görünmese bile ŞAŞIRMA ve "python yok" DEME — python_run aracı kendi taşınabilir runtime\'ını (%APPDATA%\\beast\\py\\python.exe) otomatik indirir/kullanır ve bu klasör run_command PATH\'inde önceliklidir; yani run_command içinde de `python` çalışır. Ham Google/Bing scrape yerine önce web_search aracını kullan (zaten Python çoklu-motor + Exa destekli), script gerektiğinde python_run yaz.',
@@ -1540,6 +1501,10 @@ class Engine {
1540
1501
  /* #6 bağlam sıkıştırma: öze dönüşen eski mesajlar bellekten VE diskten
1541
1502
  düşer (compacted işaretiyle); notlar + son pencere her zaman kalır.
1542
1503
  Böylece notesAt=0'dan yeniden başlar, uzun sohbette şişme olmaz. */
1504
+ /* YARIŞ KORUMASI: bu bakım artık done SONRASI arka planda koşuyor —
1505
+ yeni tur başladıysa dosya sıkıştırmayı ERTENELE (not satırı append ile
1506
+ zaten kayıtlı; sıkıştırma sonraki bakımda yapılır, append kaybı olmaz) */
1507
+ if (this.ctrls.has(session.id)) return { ok: true, deferred: true };
1543
1508
  this._compactToNotes(session, cut);
1544
1509
  /* bellek kopyasını da eşele: özetlenen bölge [0..cut) atılır */
1545
1510
  session.messages = session.messages.slice(cut);
@@ -1550,6 +1515,29 @@ class Engine {
1550
1515
  }
1551
1516
  }
1552
1517
 
1518
+ /* Cevap done SONRASI arka planda koşan ev işleri (kullanıcıyı BEKLETMEZ,
1519
+ DURUM BALONUNU DA MEŞGUL ETMEZ — tamamen sessizdir):
1520
+ oturum notları → otomatik memory → skill yansıtma → kişilik kalibrasyonu.
1521
+ Aynı oturum için per-session zincirle sıralanır. */
1522
+ _postRunHousekeeping(session, prevSignal) {
1523
+ const sid = session.id;
1524
+ if (session.bgJob || session.bcCode || session.isBotDm) return;
1525
+ if (prevSignal && prevSignal.aborted) return;
1526
+ if (!this._housekeepingTail) this._housekeepingTail = new Map();
1527
+ const job = this._housekeepingTail.get(sid) || Promise.resolve();
1528
+ const next = job.catch(() => {}).then(async () => {
1529
+ const ctl = new AbortController();
1530
+ try { await this._updateSessionNotes(session, ctl.signal); } catch {}
1531
+ if (ctl.signal.aborted || this.ctrls.has(sid)) return;
1532
+ try { await this._autoMemory(session, ctl.signal); } catch {}
1533
+ if (this.reflection.enabled && !ctl.signal.aborted && !this.ctrls.has(sid)) {
1534
+ try { await this._maybeReflectSkill(session); } catch {}
1535
+ }
1536
+ try { memory.calibratePersona(); } catch {}
1537
+ });
1538
+ this._housekeepingTail.set(sid, next);
1539
+ }
1540
+
1553
1541
  /* ---------- agent loop ---------- */
1554
1542
 
1555
1543
  send(sessionId, payload) {
@@ -2220,8 +2208,6 @@ class Engine {
2220
2208
  /* bot kimliği: bota bağlı oturumlarda kişilik + izolasyon kuralları */
2221
2209
  const botBlock = this._botSystemBlock(session);
2222
2210
  if (botBlock) system += '\n\n' + botBlock;
2223
- const guestBlock = this._guestsBlock(session);
2224
- if (guestBlock) system += '\n\n' + guestBlock;
2225
2211
  if (session.notes) {
2226
2212
  system +=
2227
2213
  `\n\n# OTURUM NOTLARI (oturum ${session.code || '?'} — bu oturumun önceki konuşma özeti; birebir geçmişi buradan hatırla)\n` +
@@ -2316,43 +2302,14 @@ class Engine {
2316
2302
  emit({ type: 'message', message: assistant });
2317
2303
 
2318
2304
  if (!res.toolCalls || !res.toolCalls.length) {
2319
- // cevap tamam pencereden düşecek kısmın oturum notlarını güncelle
2320
- /* #21 bg ajanı: görev sonu ekstra LLM çağrıları YOK — hızlı kapansın.
2321
- Beast Code (bcCode) oturumları da aynı şekilde ANINDA kapansın:
2322
- not/hafıza/skill yansıtma çağrıları yapılmaz → done hemen gelir */
2323
- if (!session.bgJob && !session.bcCode && !session.isBotDm) {
2324
- const r = await this._updateSessionNotes(session, ctrl.signal);
2325
- if (r && r.ok) {
2326
- emitSafe(this, sid, { type: 'status', status: `oturum notları güncellendi (${session.code || ''})` });
2327
- } else if (r && r.reason && !/^bekleme/.test(r.reason)) {
2328
- emitSafe(this, sid, { type: 'status', status: `oturum notu YAZILAMADI: ${r.reason}` });
2329
- }
2330
- }
2331
- /* otomatik memory döngüsü: kalıcı değerli bilgiyi MEMORY.md'ye düşür */
2332
- if (!ctrl.signal.aborted && !session.bgJob && !session.bcCode && !session.isBotDm) {
2333
- try {
2334
- await this._autoMemory(session, ctrl.signal);
2335
- } catch {}
2336
- }
2337
- /* deneyimden skill doğurma (#2): 5+ yeni araç çağrısı biriktiyse yansıt */
2338
- if (this.reflection.enabled && !ctrl.signal.aborted && !session.bgJob && !session.bcCode && !session.isBotDm) {
2339
- try {
2340
- const made = await this._maybeReflectSkill(session);
2341
- if (made) emitSafe(this, sid, { type: 'status', status: `skill taslağı doğdu: ${made}` });
2342
- } catch {}
2343
- }
2344
- this._clearCrash(); // temiz bitiş — çökme kaydını sil
2345
- this._maybeCompact(sid); // oturum dosyası şiştiyse sıkıştır
2305
+ /* cevap tamam done HEMEN: kullanıcı beklemeden yeni iş yazabilsin.
2306
+ Not/memory/skill bakımı artık arka planda (_postRunHousekeeping). */
2307
+ this._clearCrash();
2308
+ this._maybeCompact(sid);
2346
2309
  emit({ type: 'done', usage: res.usage || null, meta: res.meta || null });
2347
2310
  this._bgFinish(sid, 'done');
2348
- this.flushPendingReports(sid); // bekleyen arka plan raporları
2349
- /* #12: yeterli örnek birikmişse üslup kalibrasyonu (sessiz) */
2350
- try {
2351
- const cal = memory.calibratePersona();
2352
- if (cal.ok && cal.traits && cal.traits.length) {
2353
- emitSafe(this, sid, { type: 'status', status: 'kişilik kalibre edildi' });
2354
- }
2355
- } catch {}
2311
+ this.flushPendingReports(sid);
2312
+ this._postRunHousekeeping(session, ctrl.signal);
2356
2313
  return;
2357
2314
  }
2358
2315
 
@@ -2400,8 +2357,7 @@ class Engine {
2400
2357
  );
2401
2358
  }
2402
2359
  emit({ type: 'done', usage: null });
2403
- this._bgFinish(sid, 'error', 'tur limiti doldu (MAX_TURNS)');
2404
- } catch (e) {
2360
+ this._bgFinish(sid, 'error', 'tur limiti doldu (MAX_TURNS)'); } catch (e) {
2405
2361
  const aborted = e && (e.name === 'AbortError' || ctrl.signal.aborted);
2406
2362
  if (aborted) {
2407
2363
  this._clearCrash(); // kullanıcı durdurdu — kurtarma yok
@@ -3112,23 +3068,38 @@ class Engine {
3112
3068
  return JSON.stringify(r);
3113
3069
  }
3114
3070
  if (name === 'web_search') {
3115
- /* SIRA: 0) TinyFish (anahtar girildiyse ZİRVEDE)
3116
- 1) dahili tarayıcı (gerçek Chromium ile DİREK GOOGLEbot koruması yok)
3117
- 2) python çoklu-motor (ddgs / DDG+Bing+Mojeek)
3118
- 3) Exa (anahtar girildiyse, son çare) */ const q = String((args && args.query) || '');
3071
+ /* SIRA: 1) dahili tarayıcı (DİREK GOOGLE — ücretsiz + AI cevabı)
3072
+ 2) TinyFish (tarayıcı sorun çıkarsa HEMEN devreye gireranahtar girildiyse)
3073
+ 3) python çoklu-motor (ddgs / DDG+Bing+Mojeek)
3074
+ 4) Exa (anahtar girildiyse, son çare)
3075
+ Tarayıcıda CAPTCHA/"olağandışı trafik"/boş sonuç algılanırsa 10 dk boyunca
3076
+ tarayıcı ATLANIR — her arama TinyFish'ten başlar, süre dolunca tarayıcı geri döner. */
3077
+ const q = String((args && args.query) || '');
3078
+ const n = Number((args && args.max_results) || 8);
3119
3079
  let r = null;
3120
- try { r = await tools.tinyfishSearch(q, Number((args && args.max_results) || 8), signal); } catch {}
3121
- if (!r || !r.ok || !(r.results || []).length) {
3122
- if (this.browser && typeof this.browser.search === 'function') {
3123
- try { r = await this.browser.search(q, signal); } catch {}
3080
+ let banned = false;
3081
+ const browserUsable = this.browser && typeof this.browser.search === 'function';
3082
+ if (Date.now() < (this._webSearchBrowserBanUntil || 0)) banned = true;
3083
+ if (browserUsable && !banned) {
3084
+ try { r = await this.browser.search(q, signal); } catch {}
3085
+ if (!r || !r.ok || !(r.results || []).length) {
3086
+ /* tarayıcı sorunlu — 10 dk atla, alternatif zincir hemen devrede */
3087
+ this._webSearchBrowserBanUntil = Date.now() + 10 * 60 * 1000;
3088
+ r = null;
3089
+ banned = true;
3124
3090
  }
3125
3091
  }
3092
+ if (!r || !r.ok || !(r.results || []).length) {
3093
+ try { r = await tools.tinyfishSearch(q, n, signal); } catch {}
3094
+ }
3126
3095
  if (!r || !r.ok || !(r.results || []).length) {
3127
3096
  try {
3128
3097
  r = JSON.parse(await tools.exec('web_search', args, { cwd: this._sessionWorkspace(sessionId), signal }));
3129
3098
  } catch {}
3130
3099
  }
3131
- return JSON.stringify(r || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' });
3100
+ const out = r || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' };
3101
+ if (out && out.ok && banned) out.note = 'tarayıcı araması 10 dk askıda (CAPTCHA/trafik) — TinyFish/alternatif zincir kullanıldı';
3102
+ return JSON.stringify(out);
3132
3103
  }
3133
3104
  if (name === 'ocr_read') {
3134
3105
  return JSON.stringify(await this._ocrRead(args || {}, signal));
@@ -3676,7 +3647,7 @@ const TOOLS = [
3676
3647
  function: {
3677
3648
  name: 'browser_open',
3678
3649
  description:
3679
- 'Open a URL in the built-in visible browser panel (JS-rendered pages work). This is the DEFAULT for every "open this site / search this / go to page" request — the user watches the page live in the side panel; ideal for login, SPA and dynamic content. Always follow with browser_snapshot, then act via refs (browser_click/browser_type/browser_select) and read via browser_read. Open the OS default (external) browser ONLY when the user explicitly asks for chrome/firefox/another/normal/my-own browser — in that case use run_command with `start "" <url>` instead of this tool.',
3650
+ 'Open a URL in the built-in visible browser panel (JS-rendered pages work). This is the DEFAULT for every "open this site / search this / go to page" request — the user watches the page live in the side panel; ideal for login, SPA and dynamic content. The response ALREADY includes a fresh snapshot with numbered refs act directly with browser_click/browser_type/browser_select; no separate browser_snapshot needed. Open the OS default (external) browser ONLY when the user explicitly asks for chrome/firefox/another/normal/my-own browser — in that case use run_command with `start "" <url>` instead of this tool.',
3680
3651
  parameters: {
3681
3652
  type: 'object',
3682
3653
  properties: {
@@ -3709,7 +3680,7 @@ const TOOLS = [
3709
3680
  function: {
3710
3681
  name: 'browser_snapshot',
3711
3682
  description:
3712
- 'List all interactive elements of the built-in browser page as numbered refs: [3] <button> "Gönder". ALWAYS start with this; then act using ref numbers. Much more reliable than CSS selectors.',
3683
+ 'List all interactive elements of the built-in browser page as numbered refs: [3] <button> "Gönder". NOTE: every browser action response already includes a fresh snapshot — call this separately only when refs seem stale or you need a re-scan (e.g. after an action whose response had no snapshot). Open popups/calendars/datepickers are listed FIRST. Much more reliable than CSS selectors.',
3713
3684
  parameters: { type: 'object', properties: {}, required: [] },
3714
3685
  },
3715
3686
  },
@@ -3733,7 +3704,7 @@ const TOOLS = [
3733
3704
  function: {
3734
3705
  name: 'browser_click',
3735
3706
  description:
3736
- 'Click an element in the built-in browser. Prefer ref from browser_snapshot (e.g. {"ref":3}); CSS selector or text=X also accepted.',
3707
+ 'Click an element in the built-in browser. Prefer ref from the latest snapshot (e.g. {"ref":3}); CSS selector or text=X also accepted. The response includes a FRESH snapshot with new refs — continue with those directly instead of calling browser_snapshot again.',
3737
3708
  parameters: {
3738
3709
  type: 'object',
3739
3710
  properties: {
@@ -3748,7 +3719,7 @@ const TOOLS = [
3748
3719
  function: {
3749
3720
  name: 'browser_type',
3750
3721
  description:
3751
- 'Type text into an input/textarea/contenteditable in the built-in browser (prefer ref). Set submit=true to press Enter afterwards.',
3722
+ 'Type text into an input/textarea/contenteditable in the built-in browser (prefer ref). Set submit=true to press Enter afterwards. Date/time fields (input type=date/time/month/datetime-local) are set PROGRAMMATICALLY — just send the date as text in any common format ("2026-03-15", "15.03.2026", "15 Mart 2026"); do NOT click the calendar popup. The response includes a FRESH snapshot with new refs.',
3752
3723
  parameters: {
3753
3724
  type: 'object',
3754
3725
  properties: {
@@ -3766,7 +3737,7 @@ const TOOLS = [
3766
3737
  function: {
3767
3738
  name: 'browser_press',
3768
3739
  description:
3769
- 'Press a key (Enter, Tab, Escape, ArrowDown…) on the focused element in the built-in browser; optionally focus a ref first.',
3740
+ 'Press a key (Enter, Tab, Escape, ArrowDown…) on the focused element in the built-in browser; optionally focus a ref first. The response includes a FRESH snapshot with new refs.',
3770
3741
  parameters: {
3771
3742
  type: 'object',
3772
3743
  properties: {
@@ -3781,7 +3752,8 @@ const TOOLS = [
3781
3752
  type: 'function',
3782
3753
  function: {
3783
3754
  name: 'browser_scroll',
3784
- description: 'Scroll the built-in browser page. direction: up | down.',
3755
+ description:
3756
+ 'Scroll the built-in browser page. direction: up | down. The response includes a FRESH snapshot with newly visible refs.',
3785
3757
  parameters: {
3786
3758
  type: 'object',
3787
3759
  properties: {
@@ -3797,7 +3769,7 @@ const TOOLS = [
3797
3769
  function: {
3798
3770
  name: 'browser_select',
3799
3771
  description:
3800
- 'Pick an <option> of a dropdown (<select>) in the built-in browser by value/text (prefer ref).',
3772
+ 'Pick an <option> of a dropdown (<select>) in the built-in browser by value/text (prefer ref). The response includes a FRESH snapshot with new refs. For CUSTOM (JS) dropdowns that are not <select>, click the trigger, then click the [role=option] ref from the snapshot (popups are listed first).',
3801
3773
  parameters: {
3802
3774
  type: 'object',
3803
3775
  properties: {
@@ -620,7 +620,7 @@ const definitions = [
620
620
  function: {
621
621
  name: 'web_search',
622
622
  description:
623
- 'FAST web search with an automatic chain: built-in browser first (real Chromium searching GOOGLE directly with AI Mode — no bot protection; the response may include an `ai` field holding Google\'s own AI answer, ideal for "who/what is X" questions), then Python multi-engine (ddgs / DuckDuckGo+Bing+Mojeek parallel), then Exa API as last resort if a key is configured (Ayarlar → Web Arama). Returns {ai?, results[{title,url,snippet}]}. Use when fresh or external info is needed; skip for things you already know.',
623
+ 'FAST web search with an automatic chain: built-in browser first (real Chromium searching GOOGLE directly with AI Mode — no bot protection; the response may include an `ai` field holding Google\'s own AI answer, ideal for "who/what is X" questions), then TinyFish API (if a key is configured, used IMMEDIATELY whenever the browser hits CAPTCHA/unusual traffic — the browser is then skipped for 10 minutes), then Python multi-engine (ddgs / DuckDuckGo+Bing+Mojeek parallel), then Exa API as last resort if a key is configured (Ayarlar → Web Arama). Returns {ai?, results[{title,url,snippet}]}. Use when fresh or external info is needed; skip for things you already know.',
624
624
  parameters: {
625
625
  type: 'object',
626
626
  properties: {