beast-agent 2.64.1 → 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 +33 -14
- package/src/agent/engine.js +75 -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 };
|
|
@@ -25,19 +25,34 @@ match the goal; prefer specific, unique labels over generic words. Choose only a
|
|
|
25
25
|
|
|
26
26
|
const OPERATION_LABELS = {
|
|
27
27
|
CLICK: 'Click an OCR text line on the screen (button, field, link, menu item).',
|
|
28
|
-
|
|
28
|
+
DOUBLE_CLICK: 'Double-click an OCR text line (open file/folder, select word).',
|
|
29
|
+
RIGHT_CLICK: 'Right-click an OCR text line (context menu).',
|
|
30
|
+
TYPE_TEXT: 'Type text into the control focused by the most recent click. A small LLM supplies the value.',
|
|
29
31
|
DONE: 'Every requirement is visibly satisfied on screen.',
|
|
30
32
|
BLOCKED: 'No supported operation can progress.',
|
|
31
33
|
};
|
|
32
34
|
|
|
35
|
+
const LINE_OPS = new Set(['CLICK', 'DOUBLE_CLICK', 'RIGHT_CLICK']);
|
|
36
|
+
|
|
33
37
|
const KEYS = {
|
|
34
38
|
PRESS_ENTER: { label: 'Press Enter (submit / confirm)', combo: 'enter' },
|
|
35
39
|
PRESS_TAB: { label: 'Press Tab (move to next field)', combo: 'tab' },
|
|
36
40
|
PRESS_ESC: { label: 'Press Escape (close popup / cancel)', combo: 'esc' },
|
|
37
41
|
PRESS_DOWN: { label: 'Press Arrow Down (list / dropdown)', combo: 'down' },
|
|
38
42
|
PRESS_UP: { label: 'Press Arrow Up (list / dropdown)', combo: 'up' },
|
|
43
|
+
PRESS_LEFT: { label: 'Press Arrow Left', combo: 'left' },
|
|
44
|
+
PRESS_RIGHT: { label: 'Press Arrow Right', combo: 'right' },
|
|
39
45
|
PRESS_BACKSPACE: { label: 'Press Backspace (delete one character)', combo: 'backspace' },
|
|
46
|
+
PRESS_DELETE: { label: 'Press Delete', combo: 'delete' },
|
|
40
47
|
SELECT_ALL: { label: 'Select all text in the focused field (Ctrl+A)', combo: 'ctrl+a' },
|
|
48
|
+
CTRL_S: { label: 'Save (Ctrl+S)', combo: 'ctrl+s' },
|
|
49
|
+
CTRL_Z: { label: 'Undo (Ctrl+Z)', combo: 'ctrl+z' },
|
|
50
|
+
CTRL_C: { label: 'Copy (Ctrl+C)', combo: 'ctrl+c' },
|
|
51
|
+
CTRL_V: { label: 'Paste (Ctrl+V)', combo: 'ctrl+v' },
|
|
52
|
+
CTRL_X: { label: 'Cut (Ctrl+X)', combo: 'ctrl+x' },
|
|
53
|
+
CTRL_F: { label: 'Find (Ctrl+F)', combo: 'ctrl+f' },
|
|
54
|
+
ALT_TAB: { label: 'Switch window (Alt+Tab)', combo: 'alt+tab' },
|
|
55
|
+
ALT_F4: { label: 'Close window (Alt+F4)', combo: 'alt+f4' },
|
|
41
56
|
};
|
|
42
57
|
|
|
43
58
|
const SCROLLS = {
|
|
@@ -49,7 +64,7 @@ const DEFAULT_MAX_STEPS = 15;
|
|
|
49
64
|
const HARD_MAX_STEPS = 40;
|
|
50
65
|
const DEFAULT_BUDGET_MS = 180000;
|
|
51
66
|
const HARD_BUDGET_MS = 420000;
|
|
52
|
-
const WAIT_MS =
|
|
67
|
+
const WAIT_MS = 350;
|
|
53
68
|
|
|
54
69
|
function clamp(n, lo, hi) {
|
|
55
70
|
return Math.max(lo, Math.min(hi, n));
|
|
@@ -77,7 +92,11 @@ function lineTargets(lines) {
|
|
|
77
92
|
}
|
|
78
93
|
|
|
79
94
|
function operationSet(clickTargets, focusedLabel) {
|
|
80
|
-
const operations = {
|
|
95
|
+
const operations = {
|
|
96
|
+
CLICK: OPERATION_LABELS.CLICK,
|
|
97
|
+
DOUBLE_CLICK: OPERATION_LABELS.DOUBLE_CLICK,
|
|
98
|
+
RIGHT_CLICK: OPERATION_LABELS.RIGHT_CLICK,
|
|
99
|
+
};
|
|
81
100
|
if (focusedLabel) operations.TYPE_TEXT = OPERATION_LABELS.TYPE_TEXT;
|
|
82
101
|
for (const [key, value] of Object.entries(KEYS)) operations[key] = value.label;
|
|
83
102
|
for (const [key, value] of Object.entries(SCROLLS)) operations[key] = value.label + ' (screen)';
|
|
@@ -139,7 +158,7 @@ async function choose(deps, page, goal, history, focusedLabel) {
|
|
|
139
158
|
verifyAnswer && Number.isFinite(Number(verifyAnswer.noul)) ? Number(verifyAnswer.noul) : null;
|
|
140
159
|
let target = null;
|
|
141
160
|
let targetAnswer = null;
|
|
142
|
-
if (operation
|
|
161
|
+
if (LINE_OPS.has(operation)) {
|
|
143
162
|
targetAnswer = validateChoice(answers.click_target || {}, Object.keys(clickTargets));
|
|
144
163
|
target = targetAnswer.choice;
|
|
145
164
|
} else if (operation === 'TYPE_TEXT') {
|
|
@@ -149,7 +168,7 @@ async function choose(deps, page, goal, history, focusedLabel) {
|
|
|
149
168
|
return {
|
|
150
169
|
operation,
|
|
151
170
|
target,
|
|
152
|
-
line: operation
|
|
171
|
+
line: LINE_OPS.has(operation) ? clickTargets[target] : null,
|
|
153
172
|
confidence: targetAnswer ? targetAnswer.confidence : operationAnswer.confidence,
|
|
154
173
|
target_probability: target != null && targetAnswer ? targetAnswer.probabilities[target] : null,
|
|
155
174
|
verification,
|
|
@@ -206,7 +225,7 @@ async function run(deps, options) {
|
|
|
206
225
|
if (opts.window) {
|
|
207
226
|
emit({ type: 'status', status: 'T3SFast PC: pencere öne alınıyor — ' + String(opts.window).slice(0, 60) });
|
|
208
227
|
try { await deps.act('focus', { title: String(opts.window) }); } catch {}
|
|
209
|
-
await deps.wait({ ms:
|
|
228
|
+
await deps.wait({ ms: 250 });
|
|
210
229
|
}
|
|
211
230
|
|
|
212
231
|
for (let step = 1; step <= maxSteps; step++) {
|
|
@@ -222,7 +241,7 @@ async function run(deps, options) {
|
|
|
222
241
|
OCR maliyeti olmadan önbellekten okuruz (screenObserve → sig). */
|
|
223
242
|
const sig = page.sig != null ? String(page.sig) : String(page.w) + '|' + String(page.h) + '|' + String((page.lines || []).length) + '|' + String(page.text || '').length;
|
|
224
243
|
const last = history.length ? history[history.length - 1] : null;
|
|
225
|
-
if (last &&
|
|
244
|
+
if (last && ['click', 'dblclick', 'rightclick'].includes(last.kind) && !last.failed) {
|
|
226
245
|
if (last.page_changed == null) last.page_changed = prevSig != null ? sig !== prevSig : null;
|
|
227
246
|
if (last.page_changed === false) {
|
|
228
247
|
noChangeStreak++;
|
|
@@ -277,11 +296,11 @@ async function run(deps, options) {
|
|
|
277
296
|
let text = null;
|
|
278
297
|
let kind = '';
|
|
279
298
|
try {
|
|
280
|
-
if (operation
|
|
281
|
-
kind = 'click';
|
|
282
|
-
result = await deps.act(
|
|
299
|
+
if (LINE_OPS.has(operation)) {
|
|
300
|
+
kind = operation === 'CLICK' ? 'click' : operation === 'DOUBLE_CLICK' ? 'dblclick' : 'rightclick';
|
|
301
|
+
result = await deps.act(kind, { x: decision.line.x, y: decision.line.y, fast: true });
|
|
283
302
|
if (!result || result.ok !== false) focusedLabel = baseLabel(decision.line.label);
|
|
284
|
-
await deps.wait({ ms:
|
|
303
|
+
await deps.wait({ ms: 80 });
|
|
285
304
|
} else if (operation === 'TYPE_TEXT') {
|
|
286
305
|
kind = 'type';
|
|
287
306
|
const context = textContext(goal, focusedLabel, page, history);
|
|
@@ -302,15 +321,15 @@ async function run(deps, options) {
|
|
|
302
321
|
textCalls.push({ field: focusedLabel || '', value: text });
|
|
303
322
|
}
|
|
304
323
|
result = await deps.act('type', { text, fast: true });
|
|
305
|
-
await deps.wait({ ms:
|
|
324
|
+
await deps.wait({ ms: 100 });
|
|
306
325
|
} else if (KEYS[operation]) {
|
|
307
326
|
kind = 'key';
|
|
308
327
|
result = await deps.act('key', { combo: KEYS[operation].combo, fast: true });
|
|
309
|
-
await deps.wait({ ms:
|
|
328
|
+
await deps.wait({ ms: 100 });
|
|
310
329
|
} else if (SCROLLS[operation]) {
|
|
311
330
|
kind = 'scroll';
|
|
312
331
|
result = await deps.act('scroll', { x: Math.round(page.w / 2), y: Math.round(page.h / 2), dy: SCROLLS[operation].dy, fast: true });
|
|
313
|
-
await deps.wait({ ms:
|
|
332
|
+
await deps.wait({ ms: 80 });
|
|
314
333
|
} else if (operation === 'WAIT') {
|
|
315
334
|
kind = 'wait';
|
|
316
335
|
await deps.wait({ ms: WAIT_MS });
|
package/src/agent/engine.js
CHANGED
|
@@ -206,6 +206,20 @@ const CEO_EXEC_TOOLS = new Set([
|
|
|
206
206
|
açmasın ve kendini iptal edemesin — işi bitirip rapor yazsın. */
|
|
207
207
|
const BG_HIDDEN_TOOLS = new Set(['run_background', 'tasks_list', 'task_status', 'task_cancel']);
|
|
208
208
|
|
|
209
|
+
/* T3SFAST araç politikası: TypeSafe Jev AÇIKken elle tarayıcı/masaüstü eylem
|
|
210
|
+
araçları listeden düşer — eylemler browser_agent/computer_agent'a gider
|
|
211
|
+
(okuma/doğrulama araçları açık kalır). Anahtar yoksa/kapalıysa liste aynen
|
|
212
|
+
döner; ilgili Jev aracı listede yoksa (kısıtlı izin/beyaz liste) dokunulmaz. */
|
|
213
|
+
const TS_MANUAL_BROWSER_TOOLS = ['browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select'];
|
|
214
|
+
function filterTypesafeTools(tools, tsAvailable) {
|
|
215
|
+
if (!tsAvailable || !Array.isArray(tools)) return tools;
|
|
216
|
+
const has = (n) => tools.some((t) => t && t.function && t.function.name === n);
|
|
217
|
+
const forced = new Set();
|
|
218
|
+
if (has('browser_agent')) for (const n of TS_MANUAL_BROWSER_TOOLS) forced.add(n);
|
|
219
|
+
if (has('computer_agent')) forced.add('computer_act');
|
|
220
|
+
return forced.size ? tools.filter((t) => !forced.has(t.function.name)) : tools;
|
|
221
|
+
}
|
|
222
|
+
|
|
209
223
|
function uid() {
|
|
210
224
|
return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
211
225
|
}
|
|
@@ -2230,7 +2244,7 @@ class Engine {
|
|
|
2230
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).' +
|
|
2231
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.)' : ''),
|
|
2232
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.',
|
|
2233
|
-
'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).',
|
|
2234
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.',
|
|
2235
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.',
|
|
2236
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.',
|
|
@@ -3991,6 +4005,12 @@ class Engine {
|
|
|
3991
4005
|
if (adef && adef.prompt) {
|
|
3992
4006
|
system += `\n\n# AJAN: ${adef.name}\n${adef.prompt}`;
|
|
3993
4007
|
}
|
|
4008
|
+
/* T3SFAST DAYATMA: TypeSafe anahtarı VAR ve AÇIKken elle eylem araçları
|
|
4009
|
+
gizlenir (yalnız ilgili Jev aracı listede varsa): tarayıcı/masaüstü
|
|
4010
|
+
eylemleri browser_agent/computer_agent üzerinden yürür; okuma ve
|
|
4011
|
+
doğrulama araçları (open/read/screenshot/snapshot/wait, computer_look)
|
|
4012
|
+
açık kalır. Anahtar yoksa/kapalıysa hiçbir şey gizlenmez. */
|
|
4013
|
+
activeTools = filterTypesafeTools(activeTools, !typesafe.unavailable());
|
|
3994
4014
|
/* önek-cache disiplini (opencode request.ts:184): araç sırası oturum
|
|
3995
4015
|
boyunca sabit olmalı — alfabetik sıralama sağlayıcı önbelleğini bozmaz */
|
|
3996
4016
|
activeTools = [...activeTools].sort((a, b) =>
|
|
@@ -5906,6 +5926,17 @@ const skills = require('./skills');
|
|
|
5906
5926
|
});
|
|
5907
5927
|
}
|
|
5908
5928
|
if (name === 'computer_act') {
|
|
5929
|
+
/* T3SFAST DAYATMA: TypeSafe açıkken elle masaüstü eylemleri kapalı —
|
|
5930
|
+
eylem computer_agent'a (window alanıyla), doğrulama computer_look'a. */
|
|
5931
|
+
if (!typesafe.unavailable()) {
|
|
5932
|
+
return JSON.stringify({
|
|
5933
|
+
ok: false,
|
|
5934
|
+
redirected: 'computer_agent',
|
|
5935
|
+
error:
|
|
5936
|
+
'TypeSafe açık: elle masaüstü eylemleri kapalıdır — computer_agent {goal, window?} ile yaptır ' +
|
|
5937
|
+
'(ekranı doğrulamak için computer_look açık)',
|
|
5938
|
+
});
|
|
5939
|
+
}
|
|
5909
5940
|
if (!this.computer || typeof this.computer.act !== 'function') {
|
|
5910
5941
|
return JSON.stringify({ ok: false, error: 'fare/klavye erişimi yok' });
|
|
5911
5942
|
}
|
|
@@ -5928,6 +5959,35 @@ const skills = require('./skills');
|
|
|
5928
5959
|
const ca = args || {};
|
|
5929
5960
|
const cgoal = String(ca.goal || ca.task || '').trim();
|
|
5930
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
|
+
}
|
|
5931
5991
|
const cTextSel = this.modelFor('subagent') || this.sel;
|
|
5932
5992
|
emitSafe(this, sessionId, { type: 'status', status: 'T3SFast PC: Jev döngüsü başlıyor — ' + cgoal.slice(0, 80) });
|
|
5933
5993
|
const pcRun = await computeragent.run(
|
|
@@ -6003,6 +6063,17 @@ const skills = require('./skills');
|
|
|
6003
6063
|
return JSON.stringify(r);
|
|
6004
6064
|
}
|
|
6005
6065
|
if (name === 'browser_click' || name === 'browser_type' || name === 'browser_press' || name === 'browser_scroll' || name === 'browser_select') {
|
|
6066
|
+
/* T3SFAST DAYATMA: TypeSafe açıkken elle eylem yolu kapalı — sağlayıcı
|
|
6067
|
+
araç listesi dışından çağırırsa bile Jev'e yönlendirilir. */
|
|
6068
|
+
if (!typesafe.unavailable()) {
|
|
6069
|
+
return JSON.stringify({
|
|
6070
|
+
ok: false,
|
|
6071
|
+
redirected: 'browser_agent',
|
|
6072
|
+
error:
|
|
6073
|
+
'TypeSafe açık: elle tarayıcı eylemleri kapalıdır — hedefi browser_agent {goal, url?} ile TEK çağrıda yaptır ' +
|
|
6074
|
+
'(sayfayı okumak/doğrulamak için browser_read/browser_screenshot/browser_snapshot açık)',
|
|
6075
|
+
});
|
|
6076
|
+
}
|
|
6006
6077
|
if (!this.browser || typeof this.browser.act !== 'function') {
|
|
6007
6078
|
return JSON.stringify({ ok: false, error: 'dahili tarayıcı kullanılamıyor' });
|
|
6008
6079
|
}
|
|
@@ -6675,7 +6746,7 @@ const TOOLS = [
|
|
|
6675
6746
|
function: {
|
|
6676
6747
|
name: 'computer_agent',
|
|
6677
6748
|
description:
|
|
6678
|
-
'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).',
|
|
6679
6750
|
parameters: {
|
|
6680
6751
|
type: 'object',
|
|
6681
6752
|
properties: {
|
|
@@ -6687,6 +6758,7 @@ const TOOLS = [
|
|
|
6687
6758
|
window: { type: 'string', description: 'Optional window title substring to bring to front before starting (e.g. "Notepad") — avoids clicking the wrong window' },
|
|
6688
6759
|
max_steps: { type: 'number', description: 'Max actions, default 15 (hard cap 40)' },
|
|
6689
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.' },
|
|
6690
6762
|
},
|
|
6691
6763
|
required: ['goal'],
|
|
6692
6764
|
},
|
|
@@ -7734,6 +7806,7 @@ module.exports.sanitizeTodoItems = sanitizeTodoItems;
|
|
|
7734
7806
|
module.exports.parseReflectionJson = parseReflectionJson;
|
|
7735
7807
|
module.exports.CEO_EXEC_TOOLS = CEO_EXEC_TOOLS;
|
|
7736
7808
|
module.exports.BG_HIDDEN_TOOLS = BG_HIDDEN_TOOLS;
|
|
7809
|
+
module.exports.filterTypesafeTools = filterTypesafeTools;
|
|
7737
7810
|
module.exports.PERM_TOOL_SETS = PERM_TOOL_SETS;
|
|
7738
7811
|
module.exports.PERM_LEVELS = PERM_LEVELS;
|
|
7739
7812
|
module.exports.normalizePerms = normalizePerms;
|
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;
|