beast-agent 2.64.2 → 2.65.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": "2.64.2",
4
+ "version": "2.65.1",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -21,7 +21,13 @@ WAIT only when the needed control is absent/disabled, or submitted results are s
21
21
  If Search/Submit is visible and the required fields are ready, CLICK it immediately.
22
22
  Recent WAIT actions are not evidence of loading. Prefer a useful visible control over WAIT.
23
23
  DONE requires visible evidence that ALL requirements are satisfied. If asked to open a result,
24
- a matching link is not enough. BLOCKED means no supported operation can make progress.`;
24
+ a matching link is not enough. BLOCKED means no supported operation can make progress.
25
+ Before the goal itself: if a modal, cookie or consent band, campaign popup,
26
+ app-download prompt or chat overlay is open, dismiss it FIRST with CLICK on its
27
+ close control (X, Kapat, Anladım, Kabul Et, Daha sonra) or PRESS Escape.
28
+ Never start or continue the goal while such a layer covers the page.
29
+ If the last action reported the target is covered by a layer, close that layer
30
+ instead of repeating the same element.`;
25
31
 
26
32
  const VERIFY = `Judge only from the visible evidence on the CURRENT page and the original goal.
27
33
  Answer yes only if every required field, filter, and result is visibly present right now.
@@ -43,6 +49,7 @@ const OPERATION_LABELS = {
43
49
  CLICK: 'Click an element, button, menu option, autocomplete suggestion, or calendar day.',
44
50
  TYPE_TEXT: 'Enter or replace text in an editable field. A small LLM will supply the value from the goal.',
45
51
  SELECT: 'Select an observed dropdown value.',
52
+ PRESS: 'Press a keyboard key (Escape closes modals and popups).',
46
53
  DONE: 'Every requirement is visibly satisfied.',
47
54
  BLOCKED: 'No supported operation can progress.',
48
55
  };
@@ -51,8 +58,8 @@ const DEFAULT_MAX_STEPS = 20;
51
58
  const HARD_MAX_STEPS = 40;
52
59
  const DEFAULT_BUDGET_MS = 120000;
53
60
  const HARD_BUDGET_MS = 300000;
54
- const FILL_SETTLE_MS = 180;
55
- const WAIT_MS = 450;
61
+ const FILL_SETTLE_MS = 90;
62
+ const WAIT_MS = 300;
56
63
 
57
64
  function clamp(n, lo, hi) {
58
65
  return Math.max(lo, Math.min(hi, n));
@@ -134,6 +141,7 @@ function operationsFor(targets, controls) {
134
141
  const operations = {};
135
142
  for (const key of Object.keys(targets)) operations[key] = OPERATION_LABELS[key] || key;
136
143
  for (const [key, value] of Object.entries(controls)) operations[key] = value.label || key;
144
+ operations.PRESS = OPERATION_LABELS.PRESS;
137
145
  for (const key of ['DONE', 'BLOCKED']) operations[key] = OPERATION_LABELS[key];
138
146
  return operations;
139
147
  }
@@ -317,8 +325,8 @@ async function run(deps, options) {
317
325
  if (last && last.kind !== 'wait' && last.kind !== 'scroll' && !last.stale) {
318
326
  if (last.page_changed == null) last.page_changed = prevRev != null ? rev !== prevRev : null;
319
327
  if (last.page_changed === false) {
320
- noChangeStreak++;
321
- if (noChangeStreak >= 3) return finish('blocked', { reason: 'üst üste 3 eylem sayfayı değiştirmedi' });
328
+ if (!last.covered) noChangeStreak++;
329
+ if (noChangeStreak >= 4) return finish('blocked', { reason: 'üst üste ' + noChangeStreak + ' eylem sayfayı değiştirmedi' });
322
330
  } else if (last.page_changed === true) {
323
331
  noChangeStreak = 0;
324
332
  }
@@ -399,14 +407,17 @@ async function run(deps, options) {
399
407
  await deps.wait({ ms: FILL_SETTLE_MS });
400
408
  } else if (kind === 'click') {
401
409
  result = await deps.act('click', { ref: Number(action.ref), fast: true, trusted: true, expect });
402
- await deps.wait({ ms: 120 });
410
+ await deps.wait({ ms: 60 });
403
411
  } else if (kind === 'select') {
404
412
  const value = action.value != null && String(action.value) !== '' ? action.value : baseLabel(action.label);
405
413
  result = await deps.act('select', { ref: Number(action.ref), value: String(value), fast: true, expect });
406
- await deps.wait({ ms: 120 });
414
+ await deps.wait({ ms: 60 });
415
+ } else if (kind === 'press') {
416
+ result = await deps.act('press', { key: action.key || 'Escape', fast: true });
417
+ await deps.wait({ ms: 60 });
407
418
  } else if (kind === 'scroll') {
408
419
  result = await deps.act('scroll', { direction: Number(action.delta) < 0 ? 'up' : 'down', fast: true });
409
- await deps.wait({ ms: 120 });
420
+ await deps.wait({ ms: 60 });
410
421
  } else if (kind === 'wait') {
411
422
  await deps.wait({ ms: WAIT_MS });
412
423
  result = { ok: true, waited: true };
@@ -64,7 +64,7 @@ const DEFAULT_MAX_STEPS = 15;
64
64
  const HARD_MAX_STEPS = 40;
65
65
  const DEFAULT_BUDGET_MS = 180000;
66
66
  const HARD_BUDGET_MS = 420000;
67
- const WAIT_MS = 600;
67
+ const WAIT_MS = 350;
68
68
 
69
69
  function clamp(n, lo, hi) {
70
70
  return Math.max(lo, Math.min(hi, n));
@@ -225,7 +225,7 @@ async function run(deps, options) {
225
225
  if (opts.window) {
226
226
  emit({ type: 'status', status: 'T3SFast PC: pencere öne alınıyor — ' + String(opts.window).slice(0, 60) });
227
227
  try { await deps.act('focus', { title: String(opts.window) }); } catch {}
228
- await deps.wait({ ms: 350 });
228
+ await deps.wait({ ms: 250 });
229
229
  }
230
230
 
231
231
  for (let step = 1; step <= maxSteps; step++) {
@@ -300,7 +300,7 @@ async function run(deps, options) {
300
300
  kind = operation === 'CLICK' ? 'click' : operation === 'DOUBLE_CLICK' ? 'dblclick' : 'rightclick';
301
301
  result = await deps.act(kind, { x: decision.line.x, y: decision.line.y, fast: true });
302
302
  if (!result || result.ok !== false) focusedLabel = baseLabel(decision.line.label);
303
- await deps.wait({ ms: 150 });
303
+ await deps.wait({ ms: 80 });
304
304
  } else if (operation === 'TYPE_TEXT') {
305
305
  kind = 'type';
306
306
  const context = textContext(goal, focusedLabel, page, history);
@@ -321,15 +321,15 @@ async function run(deps, options) {
321
321
  textCalls.push({ field: focusedLabel || '', value: text });
322
322
  }
323
323
  result = await deps.act('type', { text, fast: true });
324
- await deps.wait({ ms: 200 });
324
+ await deps.wait({ ms: 100 });
325
325
  } else if (KEYS[operation]) {
326
326
  kind = 'key';
327
327
  result = await deps.act('key', { combo: KEYS[operation].combo, fast: true });
328
- await deps.wait({ ms: 200 });
328
+ await deps.wait({ ms: 100 });
329
329
  } else if (SCROLLS[operation]) {
330
330
  kind = 'scroll';
331
331
  result = await deps.act('scroll', { x: Math.round(page.w / 2), y: Math.round(page.h / 2), dy: SCROLLS[operation].dy, fast: true });
332
- await deps.wait({ ms: 150 });
332
+ await deps.wait({ ms: 80 });
333
333
  } else if (operation === 'WAIT') {
334
334
  kind = 'wait';
335
335
  await deps.wait({ ms: WAIT_MS });
@@ -2244,7 +2244,7 @@ class Engine {
2244
2244
  ' 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).' +
2245
2245
  (this.ceoMode ? ' (CEO: bu araçları KENDİN ÇAĞIRMA — içinde tarayıcı geçen işi run_background ile paralel ajana devret.)' : ''),
2246
2246
  'Tarayıcı eylemlerinin yanıtındaki recent günlüğünü ve navigated bilgisini takip et; eylem yanıtları zaten taze snapshot içerir — refler tutarsız görünürse yeni snapshot al.',
2247
- 'T3SFAST (Jev) HIZLI AJANLARI: Ayarlar → TypeSafe\'te anahtar VAR ve AÇIKSA çok adımlı tarayıcı işlerini tek tek browser_click/browser_type yerine HER ZAMAN browser_agent\'a BİR doğal-dil hedefiyle ver (arama + form + filtre + tarih seçimi + sonuç açma gibi akışlar saniyeler içinde biter; her adımı TypeSafe Jev seçer, yalnız metin girişini küçük bir LLM yazar; gerçek fare/klavye girdisi + DONE için aynı istekte noul doğrulaması yapılır). "done" durumu hâlâ KANIT DEĞİL — sonucu browser_read/browser_screenshot ile bağımsız doğrulamadan kullanıcıya başarılı deme. Masaüstü işleri için computer_agent\'ı aynı şekilde kullan — pencereyi "window" alanıyla otomatik öne getirt (ör. window:"Notepad"), sonucu computer_look ile doğrula; değişmeyen ekran karelerinde OCR önbelleği devreye girer, hedefi net ver (uygulama + alan + değer). Anahtar yoksa/kapalıysa bu araçlar hata döner — kullanıcıyı Ayarlar → TypeSafe\'e yönlendir. TypeSafe AÇIKKEN elle browser_click/browser_type/browser_scroll/browser_select/browser_press ve computer_act araçları araç listesinden ÇIKARILIR; tarayıcı/masaüstü eylemini YALNIZCA browser_agent/computer_agent\'a tek doğal-dil hedefi vererek yap (okuma/doğrulama için browser_open/browser_read/browser_screenshot/browser_snapshot/browser_wait/computer_look açık kalır).',
2247
+ 'T3SFAST (Jev) HIZLI AJANLARI: Ayarlar → TypeSafe\'te anahtar VAR ve AÇIKSA çok adımlı tarayıcı işlerini tek tek browser_click/browser_type yerine HER ZAMAN browser_agent\'a BİR doğal-dil hedefiyle ver (arama + form + filtre + tarih seçimi + sonuç açma gibi akışlar saniyeler içinde biter; her adımı TypeSafe Jev seçer, yalnız metin girişini küçük bir LLM yazar; gerçek fare/klavye girdisi + DONE için aynı istekte noul doğrulaması yapılır). "done" durumu hâlâ KANIT DEĞİL — sonucu browser_read/browser_screenshot ile bağımsız doğrulamadan kullanıcıya başarılı deme. Masaüstü işleri için computer_agent\'ı aynı şekilde kullan — VARSAYILAN OLARAK arka plan ajanına devredilir: çağrı anında {background:true, backgroundId} döner, ana sohbet beklemez; Jev döngüsü arka planda koşar, bitince 3-5 satırlık rapor otomatik düşer. Pencereyi "window" alanıyla otomatik öne getirt (ör. window:"Notepad"); rapor gelmeden sonucu "yapıldı" sayma. Yalnızca aynı turda sonuca ihtiyacın varsa background:false ver (tur döngü boyunca kilitlenir). Değişmeyen ekran karelerinde OCR önbelleği devreye girer, hedefi net ver (uygulama + alan + değer). Anahtar yoksa/kapalıysa bu araçlar hata döner — kullanıcıyı Ayarlar → TypeSafe\'e yönlendir. TypeSafe AÇIKKEN elle browser_click/browser_type/browser_scroll/browser_select/browser_press ve computer_act araçları araç listesinden ÇIKARILIR; tarayıcı/masaüstü eylemini YALNIZCA browser_agent/computer_agent\'a tek doğal-dil hedefi vererek yap (okuma/doğrulama için browser_open/browser_read/browser_screenshot/browser_snapshot/browser_wait/computer_look açık kalır).',
2248
2248
  '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.',
2249
2249
  '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.',
2250
2250
  '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 (SearXNG + stealth TLS + TinyFish + Python çoklu-motor destekli), script gerektiğinde python_run yaz.',
@@ -5959,6 +5959,35 @@ const skills = require('./skills');
5959
5959
  const ca = args || {};
5960
5960
  const cgoal = String(ca.goal || ca.task || '').trim();
5961
5961
  if (!cgoal) return JSON.stringify({ ok: false, error: 'goal gerekli — ulaşılacak hedefi doğal dille yaz' });
5962
+ const curPc = this.cache.get(String(sessionId || '')) || this._load(String(sessionId || ''));
5963
+ /* T3SFAST PC ARKA PLAN VARSAYILANI: masaüstü döngüsü ana sohbetin turunu
5964
+ KİLİTLEMEZ — iş ayrı bir arka plan oturumuna devredilir; Jev kararları
5965
+ orada akar, bitince 3-5 satırlık rapor bu sohbete otomatik düşer.
5966
+ Zaten arka plan işinin içinden çağrıldıysa (ya da background:false
5967
+ verildiyse) döngü çağıran turda satır içi koşar. */
5968
+ if (ca.background !== false && !(curPc && curPc.bgJob)) {
5969
+ const pcTask =
5970
+ '[MASAÜSTÜ OTOMASYONU — T3SFast PC / Jev]\n' +
5971
+ 'Hedef: ' + JSON.stringify(cgoal) + '\n' +
5972
+ (ca.window ? 'Pencere: ' + JSON.stringify(String(ca.window)) + ' (computer_agent window alanına aynen geçir)\n' : '') +
5973
+ '\nYÖNTEM: computer_agent aracını BİR kez çağır (goal' +
5974
+ (ca.window ? ' + window' : '') +
5975
+ (ca.max_steps ? ' + max_steps:' + Number(ca.max_steps) : '') +
5976
+ (ca.budget_ms ? ' + budget_ms:' + Number(ca.budget_ms) : '') +
5977
+ '). Dönen status "done" olsa bile KANIT DEĞİL — son ekranı computer_look (ya da OCR) ile bağımsız doğrula.\n' +
5978
+ 'RAPOR (3-5 satır): yapılan adımlar, son ekranda görünen sonuç, doğrulama, varsa hata.';
5979
+ const pcBg = this.runBackground(sessionId, pcTask, 'PC: ' + cgoal.slice(0, 60));
5980
+ if (!pcBg.ok) return JSON.stringify(pcBg);
5981
+ return JSON.stringify({
5982
+ ok: true,
5983
+ background: true,
5984
+ backgroundId: pcBg.backgroundId,
5985
+ code: pcBg.code,
5986
+ note:
5987
+ 'masaüstü görevi arka plan ajanına devredildi (T3SFast PC / Jev) — ana sohbet bloklanmaz; ' +
5988
+ 'bitince 3-5 satırlık rapor otomatik düşer. Rapor gelmeden "yapıldı" deme; durumu tasks_list/task_status ile izleyebilirsin.',
5989
+ });
5990
+ }
5962
5991
  const cTextSel = this.modelFor('subagent') || this.sel;
5963
5992
  emitSafe(this, sessionId, { type: 'status', status: 'T3SFast PC: Jev döngüsü başlıyor — ' + cgoal.slice(0, 80) });
5964
5993
  const pcRun = await computeragent.run(
@@ -6717,7 +6746,7 @@ const TOOLS = [
6717
6746
  function: {
6718
6747
  name: 'computer_agent',
6719
6748
  description:
6720
- 'AUTONOMOUS FAST COMPUTER LOOP (T3SFast computer use): give it ONE natural-language goal for the Windows desktop; it OCRs the screen (unchanged frames are served from an OCR cache), picks one operation + OCR text target per step through TypeSafe Jev, uses speculative operation+target heads, retries stale/ineffective actions less, and only calls a small LLM when a field must be typed. Real mouse/keyboard actions are unverified (fast mode) because the loop re-observes the screen right after. Mouse clicks land on the CENTER of the chosen OCR text line; standard flow: CLICK the input (or its label) → TYPE_TEXT → PRESS_ENTER. Useful for desktop dialogs/forms/settings; pass window:"<title part>" to bring the right window to front first. Returns {status: done|blocked|max_steps|aborted|budget, trace, text_calls, focused, screen_text}. DONE is additionally checked by a same-request TypeSafe verification question, but a "done" status is still NOT proof of success: verify the screen independently (computer_look / OCR) before telling the user it is complete. Requires the TypeSafe API key in Settings → TypeSafe (and the TypeSafe switch ON).',
6749
+ 'AUTONOMOUS FAST COMPUTER LOOP (T3SFast computer use): give it ONE natural-language goal for the Windows desktop; it OCRs the screen (unchanged frames are served from an OCR cache), picks one operation + OCR text target per step through TypeSafe Jev, uses speculative operation+target heads, retries stale/ineffective actions less, and only calls a small LLM when a field must be typed. Real mouse/keyboard actions are unverified (fast mode) because the loop re-observes the screen right after. Mouse clicks land on the CENTER of the chosen OCR text line; standard flow: CLICK the input (or its label) → TYPE_TEXT → PRESS_ENTER. Useful for desktop dialogs/forms/settings; pass window:"<title part>" to bring the right window to front first. BACKGROUND BY DEFAULT: from a normal chat the whole loop is delegated to a parallel background agent and this call returns immediately ({background:true, backgroundId, code}) without blocking the conversation; Jev decisions run there and a 3-5 line report drops into this chat automatically when it finishes. Do NOT claim success before that report; track it with tasks_list/task_status. Set background:false only when you need the inline result in this same turn (the turn stays locked for the whole loop). Returns {status: done|blocked|max_steps|aborted|budget, trace, text_calls, focused, screen_text}. DONE is additionally checked by a same-request TypeSafe verification question, but a "done" status is still NOT proof of success: verify the screen independently (computer_look / OCR) before telling the user it is complete. Requires the TypeSafe API key in Settings → TypeSafe (and the TypeSafe switch ON).',
6721
6750
  parameters: {
6722
6751
  type: 'object',
6723
6752
  properties: {
@@ -6729,6 +6758,7 @@ const TOOLS = [
6729
6758
  window: { type: 'string', description: 'Optional window title substring to bring to front before starting (e.g. "Notepad") — avoids clicking the wrong window' },
6730
6759
  max_steps: { type: 'number', description: 'Max actions, default 15 (hard cap 40)' },
6731
6760
  budget_ms: { type: 'number', description: 'Wall-clock budget in ms, default 180000 (hard cap 420000)' },
6761
+ background: { type: 'boolean', description: 'Default true: run the loop in a parallel background agent so this chat is not blocked; the report arrives automatically. Set false to await the loop inline in this turn.' },
6732
6762
  },
6733
6763
  required: ['goal'],
6734
6764
  },
package/src/main.js CHANGED
@@ -4471,8 +4471,10 @@ function browserDialogNote(sinceAt) {
4471
4471
  }
4472
4472
 
4473
4473
  /* SPA/DOM oturması: readyState complete + içerik uzunluğu İKİ turdur sabitse
4474
- dön (en çok maxMs). Böylece hidrasyon bitmeden snapshot/okuma yapılmaz. */
4475
- async function browserSettle(wc, signal, maxMs = 4000) {
4474
+ dön (en çok maxMs). Böylece hidrasyon bitmeden snapshot/okuma yapılmaz.
4475
+ stepMs: hızlı yol 70 ms tarar (eski 300 ms sabiti tur başına ~600 ms
4476
+ ekliyordu); yavaş yol 300 ms'de kalır. */
4477
+ async function browserSettle(wc, signal, maxMs = 4000, stepMs = 300) {
4476
4478
  const t0 = Date.now();
4477
4479
  let lastLen = -1;
4478
4480
  let stable = 0;
@@ -4498,8 +4500,32 @@ async function browserSettle(wc, signal, maxMs = 4000) {
4498
4500
  if (len === 0 && Date.now() - t0 > 1500) return;
4499
4501
  }
4500
4502
  lastLen = len;
4501
- await new Promise((r) => setTimeout(r, 300));
4503
+ await new Promise((r) => setTimeout(r, stepMs));
4504
+ }
4505
+ }
4506
+
4507
+ /* did-navigate sonrası yükleme bitişini bekle: sayfa hazırsa SIFIR bekleme;
4508
+ olay kaçarsa 90 ms poll isLoading false olunca çıkar (maxMs tavan). */
4509
+ async function browserLoadWait(wc, signal, maxMs = 4000) {
4510
+ try {
4511
+ if (typeof wc.isLoading === 'function' && !wc.isLoading()) return;
4512
+ } catch {
4513
+ return;
4502
4514
  }
4515
+ await new Promise((resolve) => {
4516
+ let done = false;
4517
+ const fin = () => {
4518
+ if (done) return;
4519
+ done = true;
4520
+ clearTimeout(t);
4521
+ clearInterval(p);
4522
+ try { wc.removeListener('did-stop-loading', fin); } catch {}
4523
+ resolve();
4524
+ };
4525
+ const t = setTimeout(fin, maxMs);
4526
+ const p = setInterval(() => { try { if (!wc.isLoading()) fin(); } catch { fin(); } }, 90);
4527
+ wc.on('did-stop-loading', fin);
4528
+ });
4503
4529
  }
4504
4530
 
4505
4531
  /* snapshot al (hatayı yut) — navigate/act yanıtlarına gömmek için */
@@ -5150,10 +5176,104 @@ const BROWSER_JS_HELPERS = `
5150
5176
  const el=m[n];
5151
5177
  return (el&&el.isConnected)?el:null;
5152
5178
  }
5179
+ function __norm(s){
5180
+ return String(s==null?'':s).toLowerCase()
5181
+ .replace(/[ıİ]/g,'i').replace(/[şŞ]/g,'s').replace(/[ğĞ]/g,'g')
5182
+ .replace(/[üÜ]/g,'u').replace(/[öÖ]/g,'o').replace(/[çÇ]/g,'c')
5183
+ .replace(/\\s+/g,' ').trim();
5184
+ }
5185
+ /* Çerez onayı + promosyon/modal katmanlarını OTOMATİK kapatır — Jev bu
5186
+ katmanlar için adım harcamaz. Yalnız kapsamı belli kutulara dokunur:
5187
+ çerez/CMP konteynerleri ile promosyon/bülten/uygulama modal katmanları.
5188
+ Form içeren kritik diyaloglara (giriş/arama) dokunulmaz; aynı eleman
5189
+ ikinci kez tıklanmaz (WeakSet). Dönüş: kapatılanların listesi. */
5190
+ function __autoDismiss(){
5191
+ const out=[];
5192
+ const clicked=window.__beDismissed||(window.__beDismissed=new WeakSet());
5193
+ const clickEl=(e,what)=>{
5194
+ try{
5195
+ if(clicked.has(e)) return false;
5196
+ clicked.add(e);
5197
+ try{ e.scrollIntoView({block:'center'}); }catch(_){}
5198
+ if(typeof e.click==='function') e.click();
5199
+ out.push({what:what,label:String(__label(e)||'').slice(0,40)});
5200
+ return true;
5201
+ }catch(_){ return false; }
5202
+ };
5203
+ const VENDOR='#onetrust-accept-btn-handler,#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowAll,#didomi-notice-agree-button,#truste-consent-button,[data-testid="cookie-policy-manage-dialog-accept-button"]';
5204
+ const COOKIE='[id*="cookie" i],[class*="cookie" i],[id*="consent" i],[class*="consent" i],[id*="gdpr" i],[class*="gdpr" i],[class*="onetrust" i],[class*="cookiebot" i],[class*="didomi" i],[class*="usercentrics" i],[class*="quantcast" i],[class*="truste" i],[class*="osano" i],[class*="iubenda" i],[class*="klaro" i],[class*="axeptio" i],[class*="complianz" i]';
5205
+ const PROMO='[role="dialog"],[aria-modal="true"],[class*="modal" i],[id*="modal" i],[class*="popup" i],[id*="popup" i],[class*="lightbox" i],[class*="newsletter" i],[class*="subscribe" i],[class*="promo" i],[class*="campaign" i],[class*="interstitial" i],[class*="smartbanner" i],[class*="app-banner" i],[id*="app-banner" i],[class*="app-download" i],[class*="overlay" i],[id*="overlay" i]';
5206
+ const PROMO_TEXT=['bulten','newsletter','abone','subscribe','indirim','kampanya','firsat','kupon','coupon','discount','promo','sale','special offer','mobil uygulama','uygulamamiz','app download','bildirim','notification','hediye','cekilis','anket','survey','feedback','puan kazan'];
5207
+ const ACCEPT=['accept all','allow all','accept cookies','accept all cookies','agree to all','agree and continue','consent to all','i accept','tumunu kabul','tum cerezleri kabul','tum cerezleri onayla','cerezleri kabul','kabul ediyorum','tumunu onayla','alle akzeptieren','alle cookies akzeptieren','zustimmen','tout accepter',"j'accepte",'accepter et continuer','aceptar todo','aceptar y continuar','aceitar todos','accetta tutti','accetta e continua','kabul et','onayla','accept','agree','allow','anladim','tamam','ok','okay'];
5208
+ const CLOSE=['close','kapat','dismiss','daha sonra','simdi degil','simdi olmaz','not now','maybe later','no thanks','hayir tesekkurler','skip','atla','gec','sonra','later','x','×','✕','✖','╳'];
5209
+ const btnSel='button,[role="button"],a,input[type="submit"],input[type="button"],[aria-label]';
5210
+ const accepts=(lbl)=>{
5211
+ for(const p of ACCEPT){
5212
+ if(lbl===p) return true;
5213
+ if(p.length>=6 && lbl.indexOf(p)>=0 && lbl.length<=p.length+24) return true;
5214
+ }
5215
+ return false;
5216
+ };
5217
+ const closes=(lbl)=>{
5218
+ for(const p of CLOSE){
5219
+ if(lbl===p) return true;
5220
+ if(p.length>=5 && lbl.indexOf(p)>=0 && lbl.length<=p.length+16) return true;
5221
+ }
5222
+ return false;
5223
+ };
5224
+ const controlIn=(s)=>{
5225
+ for(const c of [...s.querySelectorAll(btnSel)]){
5226
+ if(!__vis(c)) continue;
5227
+ const lbl=__norm(__label(c));
5228
+ if(accepts(lbl)||closes(lbl)) return c;
5229
+ }
5230
+ try{
5231
+ const byAttr=s.querySelector('[class*="close" i],[id*="close" i],[class*="dismiss" i],[id*="dismiss" i],[aria-label*="close" i],[aria-label*="kapat" i],[title*="close" i],[title*="kapat" i]');
5232
+ if(byAttr&&__vis(byAttr)) return byAttr;
5233
+ }catch(_){}
5234
+ return null;
5235
+ };
5236
+ /* 1) bilinen CMP düğmeleri */
5237
+ try{
5238
+ for(const sel of VENDOR.split(',')){
5239
+ const el=document.querySelector(sel);
5240
+ if(el&&__vis(el)){ clickEl(el,'cerez'); return out; }
5241
+ }
5242
+ }catch(_){}
5243
+ /* 2) çerez onay katmanları */
5244
+ for(const s of document.querySelectorAll(COOKIE)){
5245
+ if(!__vis(s)) continue;
5246
+ const c=controlIn(s);
5247
+ if(c&&clickEl(c,'cerez')) return out;
5248
+ }
5249
+ /* 3) promosyon/modal katmanları — form içeren kritik diyaloglara dokunma */
5250
+ let promoSeen=false;
5251
+ for(const s of document.querySelectorAll(PROMO)){
5252
+ if(!__vis(s)) continue;
5253
+ const r=s.getBoundingClientRect();
5254
+ if(r.width<150||r.height<80) continue;
5255
+ const txt=__norm(s.innerText||'').slice(0,600);
5256
+ const promo=txt&&PROMO_TEXT.some((k)=>txt.indexOf(k)>=0);
5257
+ if(!promo&&s.querySelector('input:not([type="hidden"]),textarea,select')) continue;
5258
+ promoSeen=true;
5259
+ const c=controlIn(s);
5260
+ if(c&&clickEl(c,'popup')) return out;
5261
+ }
5262
+ /* 4) kapatma yok ama promosyon katmanı var → tek Escape denemesi */
5263
+ if(promoSeen&&!out.length){
5264
+ try{
5265
+ const t=document.activeElement&&document.activeElement!==document.body?document.activeElement:document.body;
5266
+ ['keydown','keyup'].forEach((type)=>t.dispatchEvent(new KeyboardEvent(type,{key:'Escape',code:'Escape',keyCode:27,which:27,bubbles:true,cancelable:true})));
5267
+ out.push({what:'escape',label:'Escape'});
5268
+ }catch(_){}
5269
+ }
5270
+ return out;
5271
+ }
5153
5272
  `;
5154
5273
 
5155
5274
  const BROWSER_SNAPSHOT_JS = `(function(){
5156
5275
  ${BROWSER_JS_HELPERS}
5276
+ const autoDismissed=__autoDismiss();
5157
5277
  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';
5158
5278
  /* açık popup/takvim/dialog varsa içindekiler ÖNCE listelenir (tarih seçici, özel dropdown vb.) */
5159
5279
  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]';
@@ -5188,7 +5308,7 @@ const BROWSER_SNAPSHOT_JS = `(function(){
5188
5308
  lines.push('--- SAYFA ---');
5189
5309
  }
5190
5310
  for(const e of document.querySelectorAll(sel)){ if(i>=100) break; add(e); }
5191
- return JSON.stringify({count:i,title:document.title,url:location.href,snapshot:lines.join('\\n'),boxes:boxes,vw:window.innerWidth,vh:window.innerHeight});
5311
+ return JSON.stringify({count:i,title:document.title,url:location.href,snapshot:lines.join('\\n'),boxes:boxes,vw:window.innerWidth,vh:window.innerHeight,auto_dismissed:autoDismissed.length?autoDismissed:undefined});
5192
5312
  })()`;
5193
5313
 
5194
5314
  /* JEV ULTRAFAST GÖZLEMİ: yapılı element tablosu (kind: click/fill/select),
@@ -5196,7 +5316,9 @@ const BROWSER_SNAPSHOT_JS = `(function(){
5196
5316
  browser_click/browser_type/browser_select ref çözümüyle BİREBİR uyumludur.
5197
5317
  Yalnız görünür + viewport içi + enabled elemanlar; scroll/wait sentetik. */
5198
5318
  const BROWSER_OBSERVE_JS = `(function(){
5319
+ ${BROWSER_JS_HELPERS}
5199
5320
  if(!document.body) return JSON.stringify({url:location.href,title:document.title,w:innerWidth,h:innerHeight,text:'',scroll:{y:0,height:0},actions:[]});
5321
+ const autoDismissed=__autoDismiss();
5200
5322
  window.__beIds=window.__beIds||new WeakMap();
5201
5323
  window.__beNext=window.__beNext||0;
5202
5324
  window.__beMap={};
@@ -5285,6 +5407,7 @@ const BROWSER_OBSERVE_JS = `(function(){
5285
5407
  actions.push({id:'wait',kind:'wait',label:'Wait for the page to update'});
5286
5408
  return JSON.stringify({url:location.href,title:document.title,w:innerWidth,h:innerHeight,text:text,
5287
5409
  scroll:{y:Math.round(scrollY),height:height},actions:actions,omitted_actions:omitted,
5410
+ auto_dismissed:autoDismissed.length?autoDismissed:undefined,
5288
5411
  rev:location.href+'|'+document.title+'|'+actions.length+'|'+text.length+'|'+Math.round(scrollY)+'|'+height});
5289
5412
  })()`;
5290
5413
 
@@ -5507,6 +5630,9 @@ async function browserObserve(signal) {
5507
5630
  const wc = browser.view.webContents;
5508
5631
  const dlgAt = browser.lastDialog ? browser.lastDialog.at : 0;
5509
5632
  try {
5633
+ /* yarım DOM'la Jev kararı verilmesin: sayfa hâlâ yükleniyorsa kısa bekle
5634
+ (sayfa hazırsa SIFIR gecikme — isLoading false anında döner) */
5635
+ try { if (wc.isLoading && wc.isLoading()) await browserLoadWait(wc, signal, 1200); } catch {}
5510
5636
  const raw = await wc.executeJavaScript(BROWSER_OBSERVE_JS, true);
5511
5637
  const obj = JSON.parse(raw);
5512
5638
  return { ok: true, ...obj, ...browserDialogNote(dlgAt) };
@@ -5563,19 +5689,31 @@ async function browserAct(kind, args, signal) {
5563
5689
  try { obj = JSON.parse(raw); } catch { obj = { result: String(raw).slice(0, 300) }; }
5564
5690
  }
5565
5691
 
5566
- // tıklama/form gönderimi sonrası kısa gezinme bekleme
5692
+ // tıklama/form gönderimi sonrası gezinme bekleme: olay ANINDA döner —
5693
+ // hızlı yolda gezinme yoksa yalnız 140 ms beklenir (eski 500 ms ölü süre).
5567
5694
  let navigated = false;
5568
5695
  if (kind === 'click' || (kind === 'type' && a.submit) || kind === 'press') {
5569
5696
  await new Promise((resolve) => {
5570
5697
  let done = false;
5571
- const onNav = () => { navigated = true; setTimeout(fin, 400); };
5572
- const fin = () => { if (!done) { done = true; clearTimeout(t); wc.removeListener('did-navigate', onNav); resolve(); } };
5573
- const t = setTimeout(fin, fast ? 500 : 1800);
5698
+ const onNav = () => { navigated = true; fin(); };
5699
+ const fin = () => {
5700
+ if (done) return;
5701
+ done = true;
5702
+ clearTimeout(t);
5703
+ wc.removeListener('did-navigate', onNav);
5704
+ wc.removeListener('did-navigate-in-page', onNav);
5705
+ resolve();
5706
+ };
5707
+ const t = setTimeout(fin, fast ? 140 : 1800);
5574
5708
  wc.on('did-navigate', onNav);
5709
+ wc.on('did-navigate-in-page', onNav);
5575
5710
  });
5576
5711
  }
5577
- /* SPA: sayfa değiştiyse DOM oturmadan snapshot alınmaz */
5578
- if (navigated) await browserSettle(wc, signal, fast ? 500 : 2500);
5712
+ /* SPA: sayfa değiştiyse yükleme bitişi + kısa DOM oturması (fast: 420 ms tavan) */
5713
+ if (navigated) {
5714
+ await browserLoadWait(wc, signal, fast ? 1200 : 4000);
5715
+ await browserSettle(wc, signal, fast ? 420 : 2500, fast ? 70 : 300);
5716
+ }
5579
5717
 
5580
5718
  /* DOĞRULAMA: eylem sonrası değişim oranı — "tıkladım ama bir şey olmadı" tuzağını yakalar */
5581
5719
  let changed = null;