beast-agent 0.24.5 → 0.25.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/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.0",
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.',
@@ -2220,8 +2181,6 @@ class Engine {
2220
2181
  /* bot kimliği: bota bağlı oturumlarda kişilik + izolasyon kuralları */
2221
2182
  const botBlock = this._botSystemBlock(session);
2222
2183
  if (botBlock) system += '\n\n' + botBlock;
2223
- const guestBlock = this._guestsBlock(session);
2224
- if (guestBlock) system += '\n\n' + guestBlock;
2225
2184
  if (session.notes) {
2226
2185
  system +=
2227
2186
  `\n\n# OTURUM NOTLARI (oturum ${session.code || '?'} — bu oturumun önceki konuşma özeti; birebir geçmişi buradan hatırla)\n` +
@@ -3112,23 +3071,38 @@ class Engine {
3112
3071
  return JSON.stringify(r);
3113
3072
  }
3114
3073
  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) || '');
3074
+ /* SIRA: 1) dahili tarayıcı (DİREK GOOGLE — ücretsiz + AI cevabı)
3075
+ 2) TinyFish (tarayıcı sorun çıkarsa HEMEN devreye gireranahtar girildiyse)
3076
+ 3) python çoklu-motor (ddgs / DDG+Bing+Mojeek)
3077
+ 4) Exa (anahtar girildiyse, son çare)
3078
+ Tarayıcıda CAPTCHA/"olağandışı trafik"/boş sonuç algılanırsa 10 dk boyunca
3079
+ tarayıcı ATLANIR — her arama TinyFish'ten başlar, süre dolunca tarayıcı geri döner. */
3080
+ const q = String((args && args.query) || '');
3081
+ const n = Number((args && args.max_results) || 8);
3119
3082
  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 {}
3083
+ let banned = false;
3084
+ const browserUsable = this.browser && typeof this.browser.search === 'function';
3085
+ if (Date.now() < (this._webSearchBrowserBanUntil || 0)) banned = true;
3086
+ if (browserUsable && !banned) {
3087
+ try { r = await this.browser.search(q, signal); } catch {}
3088
+ if (!r || !r.ok || !(r.results || []).length) {
3089
+ /* tarayıcı sorunlu — 10 dk atla, alternatif zincir hemen devrede */
3090
+ this._webSearchBrowserBanUntil = Date.now() + 10 * 60 * 1000;
3091
+ r = null;
3092
+ banned = true;
3124
3093
  }
3125
3094
  }
3095
+ if (!r || !r.ok || !(r.results || []).length) {
3096
+ try { r = await tools.tinyfishSearch(q, n, signal); } catch {}
3097
+ }
3126
3098
  if (!r || !r.ok || !(r.results || []).length) {
3127
3099
  try {
3128
3100
  r = JSON.parse(await tools.exec('web_search', args, { cwd: this._sessionWorkspace(sessionId), signal }));
3129
3101
  } catch {}
3130
3102
  }
3131
- return JSON.stringify(r || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' });
3103
+ const out = r || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' };
3104
+ if (out && out.ok && banned) out.note = 'tarayıcı araması 10 dk askıda (CAPTCHA/trafik) — TinyFish/alternatif zincir kullanıldı';
3105
+ return JSON.stringify(out);
3132
3106
  }
3133
3107
  if (name === 'ocr_read') {
3134
3108
  return JSON.stringify(await this._ocrRead(args || {}, signal));
@@ -3676,7 +3650,7 @@ const TOOLS = [
3676
3650
  function: {
3677
3651
  name: 'browser_open',
3678
3652
  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.',
3653
+ '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
3654
  parameters: {
3681
3655
  type: 'object',
3682
3656
  properties: {
@@ -3709,7 +3683,7 @@ const TOOLS = [
3709
3683
  function: {
3710
3684
  name: 'browser_snapshot',
3711
3685
  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.',
3686
+ '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
3687
  parameters: { type: 'object', properties: {}, required: [] },
3714
3688
  },
3715
3689
  },
@@ -3733,7 +3707,7 @@ const TOOLS = [
3733
3707
  function: {
3734
3708
  name: 'browser_click',
3735
3709
  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.',
3710
+ '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
3711
  parameters: {
3738
3712
  type: 'object',
3739
3713
  properties: {
@@ -3748,7 +3722,7 @@ const TOOLS = [
3748
3722
  function: {
3749
3723
  name: 'browser_type',
3750
3724
  description:
3751
- 'Type text into an input/textarea/contenteditable in the built-in browser (prefer ref). Set submit=true to press Enter afterwards.',
3725
+ '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
3726
  parameters: {
3753
3727
  type: 'object',
3754
3728
  properties: {
@@ -3766,7 +3740,7 @@ const TOOLS = [
3766
3740
  function: {
3767
3741
  name: 'browser_press',
3768
3742
  description:
3769
- 'Press a key (Enter, Tab, Escape, ArrowDown…) on the focused element in the built-in browser; optionally focus a ref first.',
3743
+ '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
3744
  parameters: {
3771
3745
  type: 'object',
3772
3746
  properties: {
@@ -3781,7 +3755,8 @@ const TOOLS = [
3781
3755
  type: 'function',
3782
3756
  function: {
3783
3757
  name: 'browser_scroll',
3784
- description: 'Scroll the built-in browser page. direction: up | down.',
3758
+ description:
3759
+ 'Scroll the built-in browser page. direction: up | down. The response includes a FRESH snapshot with newly visible refs.',
3785
3760
  parameters: {
3786
3761
  type: 'object',
3787
3762
  properties: {
@@ -3797,7 +3772,7 @@ const TOOLS = [
3797
3772
  function: {
3798
3773
  name: 'browser_select',
3799
3774
  description:
3800
- 'Pick an <option> of a dropdown (<select>) in the built-in browser by value/text (prefer ref).',
3775
+ '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
3776
  parameters: {
3802
3777
  type: 'object',
3803
3778
  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: {
package/src/main.js CHANGED
@@ -2555,7 +2555,31 @@ function setBrowserOpen(v, forceVisible) {
2555
2555
  browserEmit({ open: true, width: browser.width, url });
2556
2556
  }
2557
2557
 
2558
+ /* --- Paralel ajan trafik düzeni: ajanlar aynı saniyede sorgu atınca Google
2559
+ "olağandışı trafik" uyarısı veriyor. Gezinme/arama isteklerini tek kuyrukta
2560
+ sıraya alıp aralarına min 1.2 sn + rastgele 0-600 ms jitter koyuyoruz.
2561
+ Kuyruğu tutan işin İÇİNDEN yapılan gezinmeler kapıyı atlar (kilitlenme olmaz). --- */
2562
+ const BROWSER_TRAFFIC_GAP = 1200;
2563
+ let __trafficLast = 0;
2564
+ let __trafficTail = Promise.resolve();
2565
+ function browserTrafficWait() {
2566
+ const wait = Math.max(0, __trafficLast + BROWSER_TRAFFIC_GAP + Math.floor(Math.random() * 600) - Date.now());
2567
+ __trafficLast = Date.now() + wait;
2568
+ return wait > 0 ? new Promise((r) => setTimeout(r, wait)) : Promise.resolve();
2569
+ }
2570
+ function browserGate(job) {
2571
+ const p = __trafficTail.then(job, job);
2572
+ __trafficTail = p.catch(() => {});
2573
+ return p;
2574
+ }
2558
2575
  async function browserNavigate(raw, signal) {
2576
+ return browserGate(async () => {
2577
+ await browserTrafficWait();
2578
+ return browserNavigateNow(raw, signal);
2579
+ });
2580
+ }
2581
+
2582
+ async function browserNavigateNow(raw, signal) {
2559
2583
  let url = String(raw || '').trim();
2560
2584
  if (!url) return { ok: false, error: 'boş adres' };
2561
2585
  if (!/^https?:\/\//i.test(url)) {
@@ -2585,7 +2609,21 @@ async function browserNavigate(raw, signal) {
2585
2609
  try { title = wc.getTitle(); finalUrl = wc.getURL() || url; } catch {}
2586
2610
  browserEmit({ open: true, width: browser.width, url: finalUrl });
2587
2611
  flushBrowserStorage(); // oturum çerezleri diske — ani kapanışta kaybolmasın
2588
- return { ok: true, url: finalUrl, title };
2612
+ /* açılışta snapshot da göm model ayrı snapshot çağırmadan ref'lere başlar */
2613
+ let snap = null;
2614
+ try {
2615
+ const sraw = await wc.executeJavaScript(BROWSER_SNAPSHOT_JS, true);
2616
+ const sobj = JSON.parse(sraw);
2617
+ if (sobj && typeof sobj.count === 'number') snap = sobj;
2618
+ } catch {}
2619
+ return {
2620
+ ok: true,
2621
+ url: finalUrl,
2622
+ title,
2623
+ ...(snap
2624
+ ? { snapshot: snap.snapshot, refCount: snap.count, note: 'sayfa acildi — güncel snapshot hazır (' + snap.count + ' ref); ref numarasıyla browser_click/browser_type ile devam et' }
2625
+ : { note: 'sayfa acildi — etkileşimli elemanlar için browser_snapshot al' }),
2626
+ };
2589
2627
  }
2590
2628
 
2591
2629
  async function browserRead(signal) {
@@ -2650,6 +2688,14 @@ async function ocrRead({ image, lang = 'tur+eng' } = {}) {
2650
2688
  yapmak (kullanıcının console'da yaptığı gibi) — sayfa bile değişmeden
2651
2689
  DOMParser ile sonuç çekilir. Fetch olmazsa direk gezinme fallback'i var. */
2652
2690
  async function browserSearch(query, signal) {
2691
+ /* paralel ajan sorguları trafik kapısından sırayla geçer */
2692
+ return browserGate(async () => {
2693
+ await browserTrafficWait();
2694
+ return browserSearchNow(query, signal);
2695
+ });
2696
+ }
2697
+
2698
+ async function browserSearchNow(query, signal) {
2653
2699
  try {
2654
2700
  if (!win || win.isDestroyed()) return null;
2655
2701
  if (signal && signal.aborted) return null;
@@ -2663,7 +2709,7 @@ async function browserSearch(query, signal) {
2663
2709
  let cur = '';
2664
2710
  try { cur = wc.getURL() || ''; } catch {}
2665
2711
  if (!cur.startsWith('https://www.google.com')) {
2666
- await browserNavigate('https://www.google.com/', signal);
2712
+ await browserNavigateNow('https://www.google.com/', signal);
2667
2713
  }
2668
2714
  if (!browser.view || !browser.open) return null;
2669
2715
 
@@ -2733,7 +2779,7 @@ async function browserSearch(query, signal) {
2733
2779
 
2734
2780
  /* fallback: fetch yerine direk gezinme + DOM'dan çek */
2735
2781
  if (!results.length) {
2736
- await browserNavigate(
2782
+ await browserNavigateNow(
2737
2783
  'https://www.google.com/search?q=' + encodeURIComponent(q) + '&num=10&hl=tr&pws=0&aep=1',
2738
2784
  signal
2739
2785
  );
@@ -2789,7 +2835,14 @@ async function browserSearch(query, signal) {
2789
2835
  results = parsed.rows || [];
2790
2836
  }
2791
2837
 
2792
- if (!results.length && !parsed.ai) return null;
2838
+ if (!results.length && !parsed.ai) {
2839
+ /* CAPTCHA / olağandışı trafik sinyali — zincir TinyFish'e kaymalı */
2840
+ let blocked = false;
2841
+ try { blocked = /google\.com\/sorry/i.test(wc.getURL() || ''); } catch {}
2842
+ return blocked
2843
+ ? { ok: false, blocked: true, engine: 'browser-google', query: q, error: 'unusual traffic (CAPTCHA)' }
2844
+ : null;
2845
+ }
2793
2846
  flushBrowserStorage();
2794
2847
  const out = { ok: true, engine: 'browser-google', query: q, results };
2795
2848
  if (parsed.ai) out.ai = parsed.ai;
@@ -2853,26 +2906,37 @@ const BROWSER_JS_HELPERS = `
2853
2906
 
2854
2907
  const BROWSER_SNAPSHOT_JS = `(function(){
2855
2908
  ${BROWSER_JS_HELPERS}
2856
- const sel='a[href],button,input:not([type="hidden"]),textarea,select,[role="button"],[role="link"],[role="tab"],[contenteditable="true"],summary';
2857
- const els=[...document.querySelectorAll(sel)];
2909
+ const sel='a[href],button,input:not([type="hidden"]),textarea,select,[role="button"],[role="link"],[role="tab"],[role="option"],[role="menuitem"],[role="gridcell"],[role="checkbox"],[role="switch"],[role="combobox"],[role="textbox"],[role="searchbox"],[contenteditable="true"],summary';
2910
+ /* açık popup/takvim/dialog varsa içindekiler ÖNCE listelenir (tarih seçici, özel dropdown vb.) */
2911
+ const popSel='[role="dialog"],dialog,[role="listbox"],[role="menu"],.flatpickr-calendar,.ui-datepicker,[class*="datepicker" i],[class*="calendar" i],[class*="dropdown" i],[class*="popup" i]';
2912
+ const pops=[...document.querySelectorAll(popSel)].filter(__vis);
2913
+ const inPop=new Set();
2914
+ for(const p of pops){[...p.querySelectorAll(sel)].forEach(e=>inPop.add(e));}
2858
2915
  window.__beMap={};
2916
+ const seen=new Set();
2859
2917
  const lines=[];
2860
2918
  let i=0;
2861
- for(const e of els){
2862
- if(!__vis(e)) continue;
2863
- if(e.disabled||e.getAttribute('aria-disabled')==='true') continue;
2919
+ function add(e){
2920
+ if(!e||seen.has(e)) return;
2921
+ seen.add(e);
2922
+ if(!__vis(e)||e.disabled||e.getAttribute('aria-disabled')==='true') return;
2864
2923
  i++;
2865
2924
  window.__beMap[i]=e;
2866
2925
  const tag=e.tagName.toLowerCase();
2867
- const type=e.getAttribute('type');
2926
+ const type=(e.getAttribute&&e.getAttribute('type'))||'';
2868
2927
  let s='['+i+'] <'+tag+(type?' type='+type:'')+'>';
2869
2928
  const l=__label(e);
2870
2929
  if(l) s+=' "'+l+'"';
2871
- if(tag==='input'&&(type==='text'||type==='email'||type==='password'||type==='search'||type==='tel'||type==='url'||type==='number')&&e.value) s+=' deger="'+String(e.value).slice(0,30)+'"';
2930
+ if(tag==='input'&&/^(text|email|password|search|tel|url|number|date|time|month|datetime-local)$/.test(type)&&e.value) s+=' deger="'+String(e.value).slice(0,30)+'"';
2872
2931
  if(tag==='select'){s+=' secenekler=['+[...e.options].slice(0,6).map(o=>o.text.trim()).filter(Boolean).join('|').slice(0,60)+']';}
2873
2932
  lines.push(s);
2874
- if(i>=100) break;
2875
2933
  }
2934
+ if(inPop.size){
2935
+ lines.push('--- ACIK POPUP/TAKVIM ICINDEKILER (once bunlari kullan) ---');
2936
+ for(const e of inPop){ add(e); if(i>=60) break; }
2937
+ lines.push('--- SAYFA ---');
2938
+ }
2939
+ for(const e of document.querySelectorAll(sel)){ if(i>=100) break; add(e); }
2876
2940
  return JSON.stringify({count:i,title:document.title,url:location.href,snapshot:lines.join('\\n')});
2877
2941
  })()`;
2878
2942
 
@@ -2905,10 +2969,52 @@ function browserActionJs(kind, args) {
2905
2969
  if (kind === 'type') {
2906
2970
  const text = JSON.stringify(String(args.text ?? ''));
2907
2971
  const submit = args.submit ? 'true' : 'false';
2908
- return `(function(){${BROWSER_JS_HELPERS}${resolveTarget};return new Promise((res)=>{try{
2972
+ return `(function(){${BROWSER_JS_HELPERS}${resolveTarget}
2973
+ /*__DT_HELPERS_START__*/
2974
+ function __norm(s){return String(s).toLowerCase().split('').map(function(c){return {'ş':'s','ğ':'g','ü':'u','ı':'i','ö':'o','ç':'c','İ':'i'}[c]||c;}).join('');}
2975
+ function __parseDate(raw){
2976
+ raw=String(raw||'').trim(); if(!raw) return null;
2977
+ const MON={ocak:1,subat:2,mart:3,nisan:4,mayis:5,haziran:6,temmuz:7,agustos:8,eylul:9,ekim:10,kasim:11,aralik:12,jan:1,feb:2,mar:3,apr:4,may:5,jun:6,jul:7,aug:8,sep:9,oct:10,nov:11,dec:12,january:1,february:2,march:3,april:4,june:6,july:7,august:8,september:9,october:10,november:11,december:12};
2978
+ let m=raw.match(/^(\\d{4})[\\-.\\/,](\\d{1,2})[\\-.\\/,](\\d{1,2})$/);
2979
+ if(m) return [+m[1],+m[2],+m[3]];
2980
+ m=raw.match(/^(\\d{1,2})[\\-.\\/ ](\\d{1,2})[\\-.\\/ ](\\d{2,4})$/);
2981
+ if(m){let d=+m[1],mo=+m[2],y=+m[3];if(y<100)y+=2000;if(mo>12&&d<=12){const t=d;d=mo;mo=t;}return [y,mo,d];}
2982
+ m=raw.match(/^(\\d{1,2})[\\-.\\/ ]+([a-zçğıöşü]+)[\\-.\\/ ]+(\\d{2,4})$/i);
2983
+ if(m){const mo=MON[__norm(m[2])];if(mo){let y=+m[3];if(y<100)y+=2000;return [y,mo,+m[1]];}}
2984
+ return null;
2985
+ }
2986
+ function __parseTime(raw){
2987
+ const m=String(raw||'').trim().match(/^(\\d{1,2})[:.h](\\d{2})/);
2988
+ if(!m) return null;
2989
+ return [Math.min(23,+m[1]),Math.min(59,+m[2])];
2990
+ }
2991
+ /*__DT_HELPERS_END__*/
2992
+ return new Promise((res)=>{try{
2909
2993
  const t=__target();
2910
2994
  if(!t) return res(JSON.stringify({typed:false,reason:'eleman bulunamadi (ref eski olabilir - browser_snapshot al)'}));
2911
2995
  const how=t.how,el=t.el;
2996
+ /* tarih/saat alanları: programatik değer + input/change event — native takvim popup'ı hiç açılmaz */
2997
+ const it=(el.tagName==='INPUT'?(el.type||'').toLowerCase():'');
2998
+ if(it==='date'||it==='month'||it==='time'||it==='datetime-local'){
2999
+ const RAW=${text};
3000
+ let v=null;
3001
+ if(it==='time'){
3002
+ const p=__parseTime(RAW); if(p) v=p.map(function(n){return String(n).padStart(2,'0');}).join(':');
3003
+ } else {
3004
+ const p=__parseDate(RAW);
3005
+ if(p&&p.every(Number.isFinite)&&p[1]>=1&&p[1]<=12&&p[2]>=1&&p[2]<=31){
3006
+ const pad=function(n){return String(n).padStart(2,'0');};
3007
+ const ymd=p[0]+'-'+pad(p[1])+'-'+pad(p[2]);
3008
+ v = it==='month' ? (p[0]+'-'+pad(p[1])) : it==='datetime-local' ? (ymd+'T'+((__parseTime(RAW)||[12,0])).map(pad).join(':')) : ymd;
3009
+ }
3010
+ }
3011
+ if(!v) return res(JSON.stringify({typed:false,inputType:it,reason:'tarih/saat alanı — metin anlaşılamadı. "2026-03-15", "15.03.2026" veya "15 Mart 2026" gibi gönder'}));
3012
+ const setter=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set;
3013
+ setter.call(el,v);
3014
+ el.dispatchEvent(new Event('input',{bubbles:true}));
3015
+ el.dispatchEvent(new Event('change',{bubbles:true}));
3016
+ return res(JSON.stringify({typed:true,target:how+' <input type='+it+'>',value:v,note:'tarih/saat programatik ayarlandi — takvim tiklamak gerekmez'}));
3017
+ }
2912
3018
  el.scrollIntoView({block:'center'});el.focus();
2913
3019
  setTimeout(()=>{try{
2914
3020
  if(el.isContentEditable){el.textContent=${text};}
@@ -3035,6 +3141,14 @@ async function browserAct(kind, args, signal) {
3035
3141
  : (obj.reason || 'tamam')
3036
3142
  );
3037
3143
 
3144
+ /* HIZ: eylem cevabına taze snapshot göm — model ayrı browser_snapshot çağırmaz (tur sayısı yarıya iner) */
3145
+ let freshSnap = null;
3146
+ try {
3147
+ const sraw = await wc.executeJavaScript(BROWSER_SNAPSHOT_JS, true);
3148
+ const sobj = JSON.parse(sraw);
3149
+ if (sobj && typeof sobj.count === 'number') freshSnap = sobj;
3150
+ } catch {}
3151
+
3038
3152
  return {
3039
3153
  ok: true,
3040
3154
  action: kind,
@@ -3042,7 +3156,12 @@ async function browserAct(kind, args, signal) {
3042
3156
  url: wc.getURL(),
3043
3157
  title: wc.getTitle(),
3044
3158
  navigated,
3045
- ...(navigated ? { note: 'sayfa degisti gerekirse yeni browser_snapshot al' } : {}),
3159
+ ...(freshSnap ? { snapshot: freshSnap.snapshot, refCount: freshSnap.count } : {}),
3160
+ ...(navigated
3161
+ ? { note: 'sayfa degisti — yanıtta güncel snapshot var; refler eskiyse yeni browser_snapshot al' }
3162
+ : freshSnap
3163
+ ? { note: 'yanıtta güncel snapshot (' + freshSnap.count + ' ref) — sonraki hamlede bunları kullan, ayrıca snapshot alma' }
3164
+ : {}),
3046
3165
  recent: recentLog(),
3047
3166
  };
3048
3167
  } catch (e) {
@@ -4403,22 +4522,9 @@ ipcMain.handle('terminal:toggle', () => {
4403
4522
  return { ok: true, cwd: (engine && engine.workspace) || settings.workspace || app.getPath('home') };
4404
4523
  });
4405
4524
 
4406
- /* Git Bash kuruluysa yolunu bulur; yoksa null döner */
4407
- function findGitBash() {
4408
- const cands = [];
4409
- if (process.env['ProgramFiles']) cands.push(path.join(process.env['ProgramFiles'], 'Git', 'bin', 'bash.exe'));
4410
- if (process.env['ProgramFiles(x86)']) cands.push(path.join(process.env['ProgramFiles(x86)'], 'Git', 'bin', 'bash.exe'));
4411
- if (process.env['LocalAppData']) cands.push(path.join(process.env['LocalAppData'], 'Programs', 'Git', 'bin', 'bash.exe'));
4412
- cands.push('C:\\Program Files\\Git\\bin\\bash.exe');
4413
- for (const c of cands) {
4414
- try { if (fs.existsSync(c)) return c; } catch {}
4415
- }
4416
- return null;
4417
- }
4418
-
4419
4525
  ipcMain.handle('terminal:run', (_e, payload) => {
4420
4526
  const cmd = String((payload && payload.cmd) || '').trim();
4421
- const shell = String((payload && payload.shell) || 'powershell');
4527
+ const shell = String((payload && payload.shell) || 'cmd');
4422
4528
  if (!cmd) return { ok: false, error: 'boş komut' };
4423
4529
  if (termChild) return { ok: false, error: 'önceki komut sürüyor — ■ ile durdurabilirsin' };
4424
4530
  const cwd = (engine && engine.workspace) || settings.workspace || app.getPath('home');
@@ -4427,12 +4533,8 @@ ipcMain.handle('terminal:run', (_e, payload) => {
4427
4533
  if (shell === 'cmd') {
4428
4534
  file = 'cmd.exe';
4429
4535
  args = ['/d', '/s', '/c', cmd];
4430
- } else if (shell === 'bash') {
4431
- const bash = findGitBash();
4432
- if (!bash) return { ok: false, error: 'Git Bash bulunamadı — Git for Windows kurulu mu? (https://git-scm.com)' };
4433
- file = bash;
4434
- args = ['-c', cmd];
4435
4536
  } else {
4537
+ /* geriye dönük uyumluluk — UI artık yalnız CMD gönderir */
4436
4538
  file = 'powershell.exe';
4437
4539
  args = ['-NoProfile', '-NoLogo', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', cmd];
4438
4540
  }
@@ -5425,10 +5527,61 @@ ipcMain.handle('bots:add', (_e, input) => {
5425
5527
  ipcMain.handle('bots:dm:list', () => engine.listBotDmSessions());
5426
5528
  ipcMain.handle('bots:dm:read', (_e, id) => engine.readBotDm(id));
5427
5529
 
5428
- /* Ana sohbete davetli botlar */
5429
- ipcMain.handle('sessions:guests:set', (_e, { sessionId, guests }) => {
5430
- try { engine.setSessionGuests(sessionId, guests); } catch {}
5431
- return { ok: true };
5530
+ /* GITHUB MODALI Trending / Tüm Repolar: GitHub Search API.
5531
+ opts.mode: 'trend' (varsayılan) | 'all' (tüm repolar yıldız tabanı yok)
5532
+ opts.range: all | 6m | 1m | 2w | 1w (tüm zamanlar / 6 ay / 1 ay / 2 hafta / 1 hafta)
5533
+ opts.order: desc (azalan) | asc (artan) — yıldız sırası
5534
+ opts.q: arama terimi (trend modunda tarih filtresi UYGULANMAZ — her zaman tüm zamanlar)
5535
+ Kimliksiz 60 istek/saat limiti var — 403'te UI bilgi gösterir. */
5536
+ ipcMain.handle('github:trending', async (_e, opts) => {
5537
+ const o = opts && typeof opts === 'object' ? opts : {};
5538
+ const mode = o.mode === 'all' ? 'all' : 'trend';
5539
+ const range = ['all', '6m', '1m', '2w', '1w'].includes(o.range) ? o.range : '2w';
5540
+ const order = o.order === 'asc' ? 'asc' : 'desc';
5541
+ const q = String(o.q || '').trim().slice(0, 120);
5542
+ const days = { all: 0, '6m': 180, '1m': 30, '2w': 14, '1w': 7 }[range];
5543
+ const since = days ? new Date(Date.now() - days * 86400000).toISOString().slice(0, 10) : '';
5544
+ /* aralık başına yıldız tabanı: listeler anlamlı kalsın diye */
5545
+ const floor = { all: 5000, '6m': 200, '1m': 50, '2w': 20, '1w': 10 }[range];
5546
+ let query;
5547
+ let useSort = true; /* 'all' + best eşleşmede sort param gönderilmez */
5548
+ if (mode === 'all') {
5549
+ if (!q) return { ok: true, items: [] };
5550
+ query = q; /* yıldız tabanı YOK — 0 yıldızlı dahil tüm repolar bulunur */
5551
+ if (o.allSort === 'best') useSort = false;
5552
+ } else if (q) {
5553
+ /* arama modu: tarih filtresi YOK — her zaman tüm zamanlar içinde arar,
5554
+ yıldız sırasına göre döner (küçük taban: çöp listelenmesin diye) */
5555
+ query = q + ' stars:>=10';
5556
+ } else if (range === 'all') {
5557
+ query = 'stars:>=' + floor;
5558
+ } else {
5559
+ query = 'created:>=' + since + ' stars:>=' + floor;
5560
+ }
5561
+ let url = 'https://api.github.com/search/repositories?q=' + encodeURIComponent(query).replace(/%20/g, '+') + '&per_page=20';
5562
+ if (useSort) url += '&sort=stars&order=' + order;
5563
+ try {
5564
+ const res = await fetch(url, {
5565
+ headers: { Accept: 'application/vnd.github+json', 'User-Agent': 'beast-agent' },
5566
+ signal: AbortSignal.timeout(15000),
5567
+ });
5568
+ if (res.status === 403 || res.status === 429) {
5569
+ return { ok: false, error: 'GitHub API limiti doldu (403) — bir saat sonra tekrar dene' };
5570
+ }
5571
+ if (!res.ok) return { ok: false, error: 'GitHub API ' + res.status };
5572
+ const data = await res.json();
5573
+ const items = (data.items || []).map((r) => ({
5574
+ full_name: r.full_name,
5575
+ html_url: r.html_url,
5576
+ description: String(r.description || '').slice(0, 240),
5577
+ language: r.language || '',
5578
+ stars: r.stargazers_count || 0,
5579
+ pushed_at: r.pushed_at || '',
5580
+ }));
5581
+ return { ok: true, items };
5582
+ } catch (e) {
5583
+ return { ok: false, error: String((e && e.message) || e) };
5584
+ }
5432
5585
  });
5433
5586
 
5434
5587
  ipcMain.handle('bots:update', (_e, { id, patch }) => {
package/src/preload.js CHANGED
@@ -143,7 +143,7 @@ contextBridge.exposeInMainWorld('beast', {
143
143
  botsAdd: (input) => ipcRenderer.invoke('bots:add', input),
144
144
  botsDmList: () => ipcRenderer.invoke('bots:dm:list'),
145
145
  botsDmRead: (id) => ipcRenderer.invoke('bots:dm:read', id),
146
- sessionSetGuests: (sessionId, guests) => ipcRenderer.invoke('sessions:guests:set', { sessionId, guests }),
146
+ githubTrending: (opts) => ipcRenderer.invoke('github:trending', opts),
147
147
  botsUpdate: (id, patch) => ipcRenderer.invoke('bots:update', { id, patch }),
148
148
  botsRemove: (id) => ipcRenderer.invoke('bots:remove', id),
149
149
  botsStats: () => ipcRenderer.invoke('bots:stats'),
@@ -445,8 +445,6 @@
445
445
  bot_bind_title: 'Bu numarayı hangi bot karşılasın?',
446
446
  bot_sel_empty: '— bot seçme —',
447
447
  bot_code_title: 'Botun benzersiz 5 haneli kodu — botlar arası DM adresi',
448
- bot_vis: 'Ana sohbete davet edilebilir (sohbet görünürlüğü)',
449
- tipGuestBot: 'Konuşmaya bot davet et — davetli botlara ajan bot_dm ile danışır',
450
448
  bot_switch_hint: 'Tıkla: bu botta çalış · ⚙: yönet',
451
449
  bot_switched: 'bottasın — sohbetler bu botun kimliğiyle gider',
452
450
  bot_manage: 'Botu yönet',
@@ -472,6 +470,7 @@
472
470
  q_flushed: '${n} kuyruktaki mesaj gönderiliyor',
473
471
  bot_num_ro_hint: 'Numara ekleme/kişi adı Ayarlar → Entegrasyonlar (WhatsApp izin listesi) üzerinden yapılır; burada yalnızca bağlı numaralar görünür.',
474
472
  tipStore: 'Skills Store',
473
+ tipGithub: 'GitHub Trending — depoları favorile, chate at, ajana araştır',
475
474
  tipIde: 'IDE Modu — dosyalar + chat + preview',
476
475
  ide_files: 'DOSYALAR',
477
476
  ide_pick: 'Klasör seç',
@@ -1022,8 +1021,6 @@
1022
1021
  bot_bind_title: 'Which bot should answer this number?',
1023
1022
  bot_sel_empty: '— no bot —',
1024
1023
  bot_code_title: "This bot's unique 5-digit code — the inter-bot DM address",
1025
- bot_vis: 'Invitable to the main chat (chat visibility)',
1026
- tipGuestBot: 'Invite a bot to this chat — the agent consults invited bots via bot_dm',
1027
1024
  bot_switch_hint: 'Click: work as this bot · ⚙: manage',
1028
1025
  bot_switched: 'active — chats run with this bot\u2019s identity',
1029
1026
  bot_manage: 'Manage bot',
@@ -1049,6 +1046,7 @@
1049
1046
  q_flushed: '${n} queued message(s) being sent',
1050
1047
  bot_num_ro_hint: 'Add numbers / person names via Settings → Integrations (WhatsApp allow list); only linked numbers are shown here.',
1051
1048
  tipStore: 'Skills Store',
1049
+ tipGithub: 'GitHub Trending — favorite repos, send to chat, ask the agent',
1052
1050
  tipIde: 'IDE Mode — files + chat + preview',
1053
1051
  ide_files: 'FILES',
1054
1052
  ide_pick: 'Choose folder',