beast-agent 2.65.1 → 2.67.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 +18 -8
- package/src/main.js +63 -2
package/package.json
CHANGED
|
@@ -20,6 +20,8 @@ Submit populated search fields before opening a result; a populated field alone
|
|
|
20
20
|
WAIT only when the needed control is absent/disabled, or submitted results are still loading.
|
|
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
|
+
Category/nav items that reveal a submenu only on hover: choose HOVER on the category first,
|
|
24
|
+
then CLICK the submenu item that becomes visible. HOVER never navigates by itself.
|
|
23
25
|
DONE requires visible evidence that ALL requirements are satisfied. If asked to open a result,
|
|
24
26
|
a matching link is not enough. BLOCKED means no supported operation can make progress.
|
|
25
27
|
Before the goal itself: if a modal, cookie or consent band, campaign popup,
|
|
@@ -50,6 +52,7 @@ const OPERATION_LABELS = {
|
|
|
50
52
|
TYPE_TEXT: 'Enter or replace text in an editable field. A small LLM will supply the value from the goal.',
|
|
51
53
|
SELECT: 'Select an observed dropdown value.',
|
|
52
54
|
PRESS: 'Press a keyboard key (Escape closes modals and popups).',
|
|
55
|
+
HOVER: 'Move the mouse over an element WITHOUT clicking (opens hover menus / mega menus).',
|
|
53
56
|
DONE: 'Every requirement is visibly satisfied.',
|
|
54
57
|
BLOCKED: 'No supported operation can progress.',
|
|
55
58
|
};
|
|
@@ -142,6 +145,7 @@ function operationsFor(targets, controls) {
|
|
|
142
145
|
for (const key of Object.keys(targets)) operations[key] = OPERATION_LABELS[key] || key;
|
|
143
146
|
for (const [key, value] of Object.entries(controls)) operations[key] = value.label || key;
|
|
144
147
|
operations.PRESS = OPERATION_LABELS.PRESS;
|
|
148
|
+
operations.HOVER = OPERATION_LABELS.HOVER;
|
|
145
149
|
for (const key of ['DONE', 'BLOCKED']) operations[key] = OPERATION_LABELS[key];
|
|
146
150
|
return operations;
|
|
147
151
|
}
|
|
@@ -165,10 +169,12 @@ function targetCriteria(candidates) {
|
|
|
165
169
|
istekte gider. Yalnız seçilen operation'ın target başlığı doğrulanır. */
|
|
166
170
|
async function choose(deps, page, goal, history, space) {
|
|
167
171
|
const { elements, targets, controls } = space;
|
|
172
|
+
/* HOVER, CLICK hedeflerini kullanır: kategori/mega menüler önce hover ister */
|
|
173
|
+
const targetOps = targets.CLICK ? Object.assign({ HOVER: targets.CLICK }, targets) : targets;
|
|
168
174
|
const questions = {
|
|
169
175
|
operation: {
|
|
170
176
|
type: 'choice',
|
|
171
|
-
criteria: operationsFor(
|
|
177
|
+
criteria: operationsFor(targetOps, controls),
|
|
172
178
|
instructions: { goal, rules: NEXT_ACTION },
|
|
173
179
|
},
|
|
174
180
|
verification: {
|
|
@@ -176,7 +182,7 @@ async function choose(deps, page, goal, history, space) {
|
|
|
176
182
|
instructions: { goal, rules: [NEXT_ACTION, VERIFY] },
|
|
177
183
|
},
|
|
178
184
|
};
|
|
179
|
-
for (const [operation, candidates] of Object.entries(
|
|
185
|
+
for (const [operation, candidates] of Object.entries(targetOps)) {
|
|
180
186
|
questions[operation.toLowerCase() + '_target'] = {
|
|
181
187
|
type: 'choice',
|
|
182
188
|
criteria: targetCriteria(candidates),
|
|
@@ -199,21 +205,21 @@ async function choose(deps, page, goal, history, space) {
|
|
|
199
205
|
const started = Date.now();
|
|
200
206
|
const result = await (deps.systemOne || systemOne)({ state, questions });
|
|
201
207
|
const answers = (result && result.answers) || {};
|
|
202
|
-
const operationAnswer = validateChoice(answers.operation || {}, Object.keys(operationsFor(
|
|
208
|
+
const operationAnswer = validateChoice(answers.operation || {}, Object.keys(operationsFor(targetOps, controls)));
|
|
203
209
|
const operation = operationAnswer.choice;
|
|
204
210
|
const verifyAnswer = answers.verification && typeof answers.verification === 'object' ? answers.verification : null;
|
|
205
211
|
const verification =
|
|
206
212
|
verifyAnswer && Number.isFinite(Number(verifyAnswer.noul)) ? Number(verifyAnswer.noul) : null;
|
|
207
213
|
let target = null;
|
|
208
214
|
let targetAnswer = null;
|
|
209
|
-
if (
|
|
210
|
-
targetAnswer = validateChoice(answers[operation.toLowerCase() + '_target'] || {}, Object.keys(
|
|
215
|
+
if (targetOps[operation]) {
|
|
216
|
+
targetAnswer = validateChoice(answers[operation.toLowerCase() + '_target'] || {}, Object.keys(targetOps[operation]));
|
|
211
217
|
target = targetAnswer.choice;
|
|
212
218
|
}
|
|
213
219
|
return {
|
|
214
220
|
operation,
|
|
215
221
|
target,
|
|
216
|
-
action: target != null ?
|
|
222
|
+
action: target != null ? targetOps[operation][target] : controls[operation] || { id: operation, kind: operation.toLowerCase(), label: operation },
|
|
217
223
|
confidence: targetAnswer ? targetAnswer.confidence : operationAnswer.confidence,
|
|
218
224
|
target_probability: target != null && targetAnswer ? targetAnswer.probabilities[target] : null,
|
|
219
225
|
verification,
|
|
@@ -322,7 +328,7 @@ async function run(deps, options) {
|
|
|
322
328
|
Değişmeyen sayfada 3. eylemde döngü kendini bloke ilan eder. */
|
|
323
329
|
const rev = page.rev != null ? String(page.rev) : String(page.url || '') + '|' + String(page.title || '');
|
|
324
330
|
const last = history.length ? history[history.length - 1] : null;
|
|
325
|
-
if (last && last.kind !== 'wait' && last.kind !== 'scroll' && !last.stale) {
|
|
331
|
+
if (last && last.kind !== 'wait' && last.kind !== 'scroll' && last.kind !== 'hover' && !last.stale) {
|
|
326
332
|
if (last.page_changed == null) last.page_changed = prevRev != null ? rev !== prevRev : null;
|
|
327
333
|
if (last.page_changed === false) {
|
|
328
334
|
if (!last.covered) noChangeStreak++;
|
|
@@ -374,7 +380,7 @@ async function run(deps, options) {
|
|
|
374
380
|
if (operation === 'BLOCKED') return finish('blocked');
|
|
375
381
|
|
|
376
382
|
const action = decision.action || {};
|
|
377
|
-
const kind = String(action.kind || '');
|
|
383
|
+
const kind = operation === 'HOVER' ? 'hover' : String(action.kind || '');
|
|
378
384
|
const expect = page.url ? { url: page.url } : null;
|
|
379
385
|
let result = null;
|
|
380
386
|
let text = null;
|
|
@@ -408,6 +414,10 @@ async function run(deps, options) {
|
|
|
408
414
|
} else if (kind === 'click') {
|
|
409
415
|
result = await deps.act('click', { ref: Number(action.ref), fast: true, trusted: true, expect });
|
|
410
416
|
await deps.wait({ ms: 60 });
|
|
417
|
+
} else if (kind === 'hover') {
|
|
418
|
+
/* alt menü/mega menü: tıkla değil, üzerine gel — menü açılsın */
|
|
419
|
+
result = await deps.act('hover', { ref: Number(action.ref), fast: true, trusted: true, expect });
|
|
420
|
+
await deps.wait({ ms: 180 });
|
|
411
421
|
} else if (kind === 'select') {
|
|
412
422
|
const value = action.value != null && String(action.value) !== '' ? action.value : baseLabel(action.label);
|
|
413
423
|
result = await deps.act('select', { ref: Number(action.ref), value: String(value), fast: true, expect });
|
package/src/main.js
CHANGED
|
@@ -3818,6 +3818,17 @@ function ensureShortcuts() {
|
|
|
3818
3818
|
/* TTS otomatik seslendirme: kullanıcı jesti olmadan Audio.play() çalışsın */
|
|
3819
3819
|
app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required');
|
|
3820
3820
|
|
|
3821
|
+
/* DAHİLİ TARAYICI CDP (HER AÇILIŞTA AÇIK): Jev (browser_agent) ve toollar
|
|
3822
|
+
(browser_js) daima DAHİLİ panelde çalışsın diye remote debug portu otomatik
|
|
3823
|
+
açılır — bat/argüman gerekmez. Chromium gerçek portu %APPDATA%\Beast Agent\
|
|
3824
|
+
DevToolsActivePort dosyasına yazar; browser_js önce o dosyayı okur, paneli
|
|
3825
|
+
bulamazsa zaten hata verir (yardımcı Chrome'a düşmez). Kullanıcı kendi
|
|
3826
|
+
--remote-debugging-port argümanını verdiyse ona dokunulmaz. */
|
|
3827
|
+
try {
|
|
3828
|
+
const hasPort = process.argv.some((a) => /^--remote-debugging-port(=|$)/.test(String(a)));
|
|
3829
|
+
if (!hasPort) app.commandLine.appendSwitch('remote-debugging-port', '9234');
|
|
3830
|
+
} catch {}
|
|
3831
|
+
|
|
3821
3832
|
app.whenReady().then(() => {
|
|
3822
3833
|
// Tailscale modu: paketli uygulamada Windows ile otomatik başlat (sessiz, tepside)
|
|
3823
3834
|
if (app.isPackaged) {
|
|
@@ -5359,12 +5370,14 @@ const BROWSER_OBSERVE_JS = `(function(){
|
|
|
5359
5370
|
return null;
|
|
5360
5371
|
};
|
|
5361
5372
|
const actions=[];
|
|
5373
|
+
const added=new WeakSet();
|
|
5362
5374
|
for(const e of document.querySelectorAll(selector)){
|
|
5363
5375
|
if(actions.length>=300) break;
|
|
5364
5376
|
if(!safe(e) || !visible(e) || e.matches(':disabled') || e.closest('[aria-disabled="true"]')) continue;
|
|
5365
5377
|
const r=e.getBoundingClientRect(), x=r.x+r.width/2, y=r.y+r.height/2, rname=role(e);
|
|
5366
5378
|
if(!rname || r.width<=0 || r.height<=0 || x<0 || y<0 || x>=innerWidth || y>=innerHeight) continue;
|
|
5367
5379
|
if(rname==='gridcell' && e.querySelector('button,[role="button"]')) continue;
|
|
5380
|
+
added.add(e);
|
|
5368
5381
|
const base={ref:identity(e),role:rname,label:name(e)||rname};
|
|
5369
5382
|
const et=String(e.type||'').toLowerCase();
|
|
5370
5383
|
if(et) base.input_type=et;
|
|
@@ -5389,6 +5402,29 @@ const BROWSER_OBSERVE_JS = `(function(){
|
|
|
5389
5402
|
if(editable) actions.push({...base,kind:'click',value,label:'Open '+base.label});
|
|
5390
5403
|
}
|
|
5391
5404
|
}
|
|
5405
|
+
/* İLAVE TIKLANABİLİRLER: <a>/<button> dışı gerçek hedef olan kategori/menü
|
|
5406
|
+
kutuları (li, custom div/span butonlar). Kısıtlı aday seti + cursor:pointer
|
|
5407
|
+
(ya da onclick) + metin + makul kutu şartı. İçinde gerçek link/buton olan
|
|
5408
|
+
kapsayıcılar atlanır (onlar zaten ana listede). */
|
|
5409
|
+
const extraSel='li,[onclick],[class*="categor" i],[class*="kategor" i],[class*="menu" i],[class*="nav" i],[class*="tab" i],[role="treeitem"],[role="menuitem"]';
|
|
5410
|
+
let extra=0;
|
|
5411
|
+
for(const e of document.querySelectorAll(extraSel)){
|
|
5412
|
+
if(extra>=80 || actions.length>=300) break;
|
|
5413
|
+
if(added.has(e) || e.closest('a[href],button,[role="button"]')) continue;
|
|
5414
|
+
if(e.querySelector('a[href],button,[role="button"]')) continue;
|
|
5415
|
+
if(!visible(e) || e.matches(':disabled') || e.closest('[aria-disabled="true"]')) continue;
|
|
5416
|
+
let pointer=false;
|
|
5417
|
+
try{ pointer=getComputedStyle(e).cursor==='pointer'||e.hasAttribute('onclick'); }catch(err){}
|
|
5418
|
+
if(!pointer) continue;
|
|
5419
|
+
const r=e.getBoundingClientRect(), x=r.x+r.width/2, y=r.y+r.height/2;
|
|
5420
|
+
if(r.width<16||r.height<12||r.width>innerWidth*0.85||r.height>innerHeight*0.4) continue;
|
|
5421
|
+
if(x<0||y<0||x>=innerWidth||y>=innerHeight) continue;
|
|
5422
|
+
const label=String(name(e)||'').replace(/\\s+/g,' ').trim();
|
|
5423
|
+
if(!label||label.length>80) continue;
|
|
5424
|
+
added.add(e);
|
|
5425
|
+
actions.push({ref:identity(e),role:'button',label:label,kind:'click',value:''});
|
|
5426
|
+
extra++;
|
|
5427
|
+
}
|
|
5392
5428
|
const words=[], walker=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT);
|
|
5393
5429
|
const range=document.createRange(); let node, length=0;
|
|
5394
5430
|
while((node=walker.nextNode()) && length<6000){
|
|
@@ -5557,6 +5593,22 @@ function browserActionJs(kind, args) {
|
|
|
5557
5593
|
res(JSON.stringify({selected:true,target:t.how,value:opt.text.trim()}));
|
|
5558
5594
|
}catch(e){res(JSON.stringify({selected:false,reason:String(e)}));}});})()`;
|
|
5559
5595
|
}
|
|
5596
|
+
if (kind === 'hover') {
|
|
5597
|
+
return `(function(){${BROWSER_JS_HELPERS}${resolveTarget}
|
|
5598
|
+
try{
|
|
5599
|
+
const t=__target();
|
|
5600
|
+
if(!t) return JSON.stringify({hovered:false,reason:'eleman bulunamadi (ref eski olabilir)'});
|
|
5601
|
+
const el=t.el;
|
|
5602
|
+
el.scrollIntoView({block:'center'});
|
|
5603
|
+
const r=el.getBoundingClientRect(), x=Math.round(r.x+r.width/2), y=Math.round(r.y+r.height/2);
|
|
5604
|
+
const opt={bubbles:true,cancelable:true,clientX:x,clientY:y};
|
|
5605
|
+
['pointerover','pointerenter','mouseover','mouseenter','mousemove'].forEach(function(type){
|
|
5606
|
+
try{ el.dispatchEvent(new MouseEvent(type,opt)); }catch(e){}
|
|
5607
|
+
});
|
|
5608
|
+
return JSON.stringify({hovered:true,target:t.how});
|
|
5609
|
+
}catch(e){return JSON.stringify({hovered:false,reason:String(e)});}
|
|
5610
|
+
})()`;
|
|
5611
|
+
}
|
|
5560
5612
|
return `JSON.stringify({ok:false,error:'bilinmeyen eylem'})`;
|
|
5561
5613
|
}
|
|
5562
5614
|
|
|
@@ -5649,7 +5701,7 @@ async function browserAct(kind, args, signal) {
|
|
|
5649
5701
|
/* JEV HIZLI YOLU: TypeSafe ajanı her adımda zaten taze observe yapar —
|
|
5650
5702
|
ağır imza + taze snapshot + uzun bekleme gereksizdir. */
|
|
5651
5703
|
const fast = !!a.fast;
|
|
5652
|
-
const trusted = !!a.trusted && ['click', 'type'].includes(kind) && browser.attached;
|
|
5704
|
+
const trusted = !!a.trusted && ['click', 'type', 'hover'].includes(kind) && browser.attached;
|
|
5653
5705
|
/* DOĞRULAMA: eylem öncesi görsel parmak izi (yalnız anlamlı eylemlerde) */
|
|
5654
5706
|
const verify = ['click', 'type', 'select', 'press'].includes(kind) && !fast && !(a.verify === false);
|
|
5655
5707
|
const sigBefore = verify ? await pageSignature(wc) : null;
|
|
@@ -5660,12 +5712,20 @@ async function browserAct(kind, args, signal) {
|
|
|
5660
5712
|
let point = {};
|
|
5661
5713
|
try { point = JSON.parse(pointRaw); } catch { point = { ok: false, reason: 'koordinat alinamadi' }; }
|
|
5662
5714
|
if (!point.ok) {
|
|
5663
|
-
obj = kind === 'click' ? { clicked: false, ...point }
|
|
5715
|
+
obj = kind === 'click' ? { clicked: false, ...point }
|
|
5716
|
+
: kind === 'hover' ? { hovered: false, ...point }
|
|
5717
|
+
: { typed: false, ...point };
|
|
5664
5718
|
} else if (kind === 'type' && /^(date|time|month|week|datetime-local)$/.test(String(point.input_type || ''))) {
|
|
5665
5719
|
/* native tarih/saat alanı: gerçek klavye yerine programatik değer + event
|
|
5666
5720
|
(tarayıcı segment segment yazmayı reddeder) */
|
|
5667
5721
|
const raw = await wc.executeJavaScript(browserActionJs(kind, a), true);
|
|
5668
5722
|
try { obj = JSON.parse(raw); } catch { obj = { result: String(raw).slice(0, 300) }; }
|
|
5723
|
+
} else if (kind === 'hover') {
|
|
5724
|
+
/* GERÇEK fare: yalnız mouseMove — tıklama YOK; kategori/mega menü
|
|
5725
|
+
hover ile açılır, sonraki gözlemde alt menü hedefleri görünür olur. */
|
|
5726
|
+
wc.sendInputEvent({ type: 'mouseMove', x: point.x, y: point.y });
|
|
5727
|
+
await new Promise((r) => setTimeout(r, 260));
|
|
5728
|
+
obj = { hovered: true, trusted: true, target: point.how };
|
|
5669
5729
|
} else {
|
|
5670
5730
|
wc.sendInputEvent({ type: 'mouseMove', x: point.x, y: point.y });
|
|
5671
5731
|
wc.sendInputEvent({ type: 'mouseDown', x: point.x, y: point.y, button: 'left', clickCount: 1 });
|
|
@@ -5730,6 +5790,7 @@ async function browserAct(kind, args, signal) {
|
|
|
5730
5790
|
blog(
|
|
5731
5791
|
kind,
|
|
5732
5792
|
obj.clicked ? obj.target
|
|
5793
|
+
: obj.hovered ? ('üzerine gelindi ' + obj.target)
|
|
5733
5794
|
: obj.typed ? ('"' + obj.value + '" → ' + obj.target)
|
|
5734
5795
|
: obj.selected ? ('"' + obj.value + '" seçildi')
|
|
5735
5796
|
: obj.pressed ? ('tuş ' + obj.pressed)
|