beast-agent 2.66.0 → 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 +52 -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
|
@@ -5370,12 +5370,14 @@ const BROWSER_OBSERVE_JS = `(function(){
|
|
|
5370
5370
|
return null;
|
|
5371
5371
|
};
|
|
5372
5372
|
const actions=[];
|
|
5373
|
+
const added=new WeakSet();
|
|
5373
5374
|
for(const e of document.querySelectorAll(selector)){
|
|
5374
5375
|
if(actions.length>=300) break;
|
|
5375
5376
|
if(!safe(e) || !visible(e) || e.matches(':disabled') || e.closest('[aria-disabled="true"]')) continue;
|
|
5376
5377
|
const r=e.getBoundingClientRect(), x=r.x+r.width/2, y=r.y+r.height/2, rname=role(e);
|
|
5377
5378
|
if(!rname || r.width<=0 || r.height<=0 || x<0 || y<0 || x>=innerWidth || y>=innerHeight) continue;
|
|
5378
5379
|
if(rname==='gridcell' && e.querySelector('button,[role="button"]')) continue;
|
|
5380
|
+
added.add(e);
|
|
5379
5381
|
const base={ref:identity(e),role:rname,label:name(e)||rname};
|
|
5380
5382
|
const et=String(e.type||'').toLowerCase();
|
|
5381
5383
|
if(et) base.input_type=et;
|
|
@@ -5400,6 +5402,29 @@ const BROWSER_OBSERVE_JS = `(function(){
|
|
|
5400
5402
|
if(editable) actions.push({...base,kind:'click',value,label:'Open '+base.label});
|
|
5401
5403
|
}
|
|
5402
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
|
+
}
|
|
5403
5428
|
const words=[], walker=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT);
|
|
5404
5429
|
const range=document.createRange(); let node, length=0;
|
|
5405
5430
|
while((node=walker.nextNode()) && length<6000){
|
|
@@ -5568,6 +5593,22 @@ function browserActionJs(kind, args) {
|
|
|
5568
5593
|
res(JSON.stringify({selected:true,target:t.how,value:opt.text.trim()}));
|
|
5569
5594
|
}catch(e){res(JSON.stringify({selected:false,reason:String(e)}));}});})()`;
|
|
5570
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
|
+
}
|
|
5571
5612
|
return `JSON.stringify({ok:false,error:'bilinmeyen eylem'})`;
|
|
5572
5613
|
}
|
|
5573
5614
|
|
|
@@ -5660,7 +5701,7 @@ async function browserAct(kind, args, signal) {
|
|
|
5660
5701
|
/* JEV HIZLI YOLU: TypeSafe ajanı her adımda zaten taze observe yapar —
|
|
5661
5702
|
ağır imza + taze snapshot + uzun bekleme gereksizdir. */
|
|
5662
5703
|
const fast = !!a.fast;
|
|
5663
|
-
const trusted = !!a.trusted && ['click', 'type'].includes(kind) && browser.attached;
|
|
5704
|
+
const trusted = !!a.trusted && ['click', 'type', 'hover'].includes(kind) && browser.attached;
|
|
5664
5705
|
/* DOĞRULAMA: eylem öncesi görsel parmak izi (yalnız anlamlı eylemlerde) */
|
|
5665
5706
|
const verify = ['click', 'type', 'select', 'press'].includes(kind) && !fast && !(a.verify === false);
|
|
5666
5707
|
const sigBefore = verify ? await pageSignature(wc) : null;
|
|
@@ -5671,12 +5712,20 @@ async function browserAct(kind, args, signal) {
|
|
|
5671
5712
|
let point = {};
|
|
5672
5713
|
try { point = JSON.parse(pointRaw); } catch { point = { ok: false, reason: 'koordinat alinamadi' }; }
|
|
5673
5714
|
if (!point.ok) {
|
|
5674
|
-
obj = kind === 'click' ? { clicked: false, ...point }
|
|
5715
|
+
obj = kind === 'click' ? { clicked: false, ...point }
|
|
5716
|
+
: kind === 'hover' ? { hovered: false, ...point }
|
|
5717
|
+
: { typed: false, ...point };
|
|
5675
5718
|
} else if (kind === 'type' && /^(date|time|month|week|datetime-local)$/.test(String(point.input_type || ''))) {
|
|
5676
5719
|
/* native tarih/saat alanı: gerçek klavye yerine programatik değer + event
|
|
5677
5720
|
(tarayıcı segment segment yazmayı reddeder) */
|
|
5678
5721
|
const raw = await wc.executeJavaScript(browserActionJs(kind, a), true);
|
|
5679
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 };
|
|
5680
5729
|
} else {
|
|
5681
5730
|
wc.sendInputEvent({ type: 'mouseMove', x: point.x, y: point.y });
|
|
5682
5731
|
wc.sendInputEvent({ type: 'mouseDown', x: point.x, y: point.y, button: 'left', clickCount: 1 });
|
|
@@ -5741,6 +5790,7 @@ async function browserAct(kind, args, signal) {
|
|
|
5741
5790
|
blog(
|
|
5742
5791
|
kind,
|
|
5743
5792
|
obj.clicked ? obj.target
|
|
5793
|
+
: obj.hovered ? ('üzerine gelindi ' + obj.target)
|
|
5744
5794
|
: obj.typed ? ('"' + obj.value + '" → ' + obj.target)
|
|
5745
5795
|
: obj.selected ? ('"' + obj.value + '" seçildi')
|
|
5746
5796
|
: obj.pressed ? ('tuş ' + obj.pressed)
|