beast-agent 2.64.2 → 2.65.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 +1 -1
- package/src/agent/browseragent.js +19 -8
- package/src/agent/computeragent.js +6 -6
- package/src/agent/engine.js +32 -2
- package/src/main.js +148 -10
package/package.json
CHANGED
|
@@ -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 =
|
|
55
|
-
const WAIT_MS =
|
|
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 >=
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
332
|
+
await deps.wait({ ms: 80 });
|
|
333
333
|
} else if (operation === 'WAIT') {
|
|
334
334
|
kind = 'wait';
|
|
335
335
|
await deps.wait({ ms: WAIT_MS });
|
package/src/agent/engine.js
CHANGED
|
@@ -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 —
|
|
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
|
-
|
|
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,
|
|
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ı
|
|
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;
|
|
5572
|
-
const fin = () => {
|
|
5573
|
-
|
|
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
|
|
5578
|
-
if (navigated)
|
|
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;
|