beast-agent 2.63.9 → 2.64.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/main.js CHANGED
@@ -324,6 +324,7 @@ try {
324
324
  typesafeMod.setConfig(() => ({
325
325
  apiKey: settings.typesafe && settings.typesafe.apiKey,
326
326
  model: settings.typesafe && settings.typesafe.model,
327
+ enabled: !(settings.typesafe && settings.typesafe.enabled === false),
327
328
  }));
328
329
  } catch {}
329
330
  let wa = null;
@@ -515,9 +516,10 @@ function parseSettingsText(text) {
515
516
  /* TypeSafe (System One/Jev) — anahtar Ayarlar → TypeSafe sekmesinden girilir;
516
517
  boşken typesafe_decision aracı anlaşılır hata döner */
517
518
  if (!parsed.typesafe || typeof parsed.typesafe !== 'object') {
518
- parsed.typesafe = { apiKey: '', model: 'jev-latest' };
519
+ parsed.typesafe = { apiKey: '', model: 'jev-latest', enabled: true };
519
520
  }
520
521
  if (typeof parsed.typesafe.model !== 'string' || !parsed.typesafe.model.trim()) parsed.typesafe.model = 'jev-latest';
522
+ if (typeof parsed.typesafe.enabled !== 'boolean') parsed.typesafe.enabled = true;
521
523
  return parsed;
522
524
  }
523
525
 
@@ -2375,7 +2377,7 @@ function botToolSet(cfg) {
2375
2377
  ]);
2376
2378
  if (s.web_search) { set.add('web_search'); set.add('http_fetch'); set.add('webfetch'); set.add('deep_search'); }
2377
2379
  if (s.browser) {
2378
- for (const t of ['browser_open', 'browser_read', 'browser_screenshot', 'browser_snapshot', 'browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select', 'browser_wait', 'ocr_read', 'computer_look']) set.add(t);
2380
+ for (const t of ['browser_open', 'browser_read', 'browser_screenshot', 'browser_snapshot', 'browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select', 'browser_wait', 'browser_agent', 'ocr_read', 'computer_look']) set.add(t);
2379
2381
  }
2380
2382
  if (s.email) { set.add('email_list'); set.add('email_read'); set.add('email_send'); }
2381
2383
  if (s.run_command) {
@@ -3395,6 +3397,7 @@ function reloadBackend() {
3395
3397
  computer: {
3396
3398
  look: captureScreenDataUrl,
3397
3399
  act: (op, args) => computerActScaled(op, args),
3400
+ observe: () => screenObserve(),
3398
3401
  },
3399
3402
  ocr: (o) => ocrRead(o),
3400
3403
  email: { list: emailList, read: emailRead, send: emailSend },
@@ -3404,6 +3407,7 @@ function reloadBackend() {
3404
3407
  readText: (s) => browserRead(s),
3405
3408
  screenshot: (s) => browserScreenshot(s),
3406
3409
  snapshot: (s) => browserSnapshot(s),
3410
+ observe: (s) => browserObserve(s),
3407
3411
  act: (k, a, s) => browserAct(k, a, s),
3408
3412
  wait: (a, s) => browserWait(a, s),
3409
3413
  },
@@ -4762,7 +4766,7 @@ async function researchRead(rawUrl, signal) {
4762
4766
  /* Dahili OCR: görsel desteklemeyen modeller için tesseract.js ile metin okuma.
4763
4767
  Dil verisi ilk kullanımda %APPDATA%\beast\tessdata'ya iner, sonra offline çalışır. */
4764
4768
  const _ocrWorkers = new Map(); // lang -> worker (her çağrıda yeniden init olmasın)
4765
- async function ocrRead({ image, lang = 'tur+eng' } = {}) {
4769
+ async function ocrRead({ image, lang = 'tur+eng', boxes = false } = {}) {
4766
4770
  try {
4767
4771
  if (!image) return { ok: false, error: 'görüntü yok' };
4768
4772
  const t = require('tesseract.js');
@@ -4789,17 +4793,79 @@ async function ocrRead({ image, lang = 'tur+eng' } = {}) {
4789
4793
  if (typeof input === 'string' && input.startsWith('data:')) {
4790
4794
  input = Buffer.from(input.split(',')[1] || '', 'base64');
4791
4795
  }
4792
- const { data } = await worker.recognize(input);
4796
+ const { data } = boxes ? await worker.recognize(input, {}, { blocks: true, text: true }) : await worker.recognize(input);
4793
4797
  const text = String((data && data.text) || '')
4794
4798
  .replace(/[ \t]+\n/g, '\n')
4795
4799
  .replace(/\n{3,}/g, '\n\n')
4796
4800
  .trim();
4801
+ if (boxes) {
4802
+ /* T3SFast bilgisayar döngüsü için satır kutuları: OCR koordinatları
4803
+ 1280px görüntü uzayındadır; computer_act bu uzayı bekler. */
4804
+ const lines = [];
4805
+ for (const block of (data && data.blocks) || []) {
4806
+ for (const para of block.paragraphs || []) {
4807
+ for (const ln of para.lines || []) {
4808
+ const bb = (ln && ln.bbox) || {};
4809
+ const label = String((ln && ln.text) || '').replace(/\s+/g, ' ').trim();
4810
+ if (!label || bb.x1 == null) continue;
4811
+ const conf = Number(ln.confidence);
4812
+ if (Number.isFinite(conf) && conf < 45) continue;
4813
+ lines.push({
4814
+ text: label.slice(0, 120),
4815
+ x: Math.round((bb.x0 + bb.x1) / 2),
4816
+ y: Math.round((bb.y0 + bb.y1) / 2),
4817
+ x0: bb.x0,
4818
+ y0: bb.y0,
4819
+ x1: bb.x1,
4820
+ y1: bb.y1,
4821
+ confidence: Math.round(Number.isFinite(conf) ? conf : 0),
4822
+ });
4823
+ }
4824
+ }
4825
+ }
4826
+ return { ok: !!text, chars: text.length, text: text.slice(0, 8000), lines: lines.slice(0, 120), lang: langKey };
4827
+ }
4797
4828
  return { ok: !!text, chars: text.length, text: text.slice(0, 8000), lang: langKey };
4798
4829
  } catch (e) {
4799
4830
  return { ok: false, error: String((e && e.message) || e) };
4800
4831
  }
4801
4832
  }
4802
4833
 
4834
+ /* T3SFast bilgisayar gözlemi: ekranı yakala (1280px, lastScreenCapture
4835
+ güncellenir) → OCR satırları + metin. Dönen kutu merkezleri computer_act'in
4836
+ beklediği görüntü koordinatlarıdır.
4837
+ OCR ÖNBELLEĞİ: ekran karesi öncekiyle aynıysa (ince parmak izi) tesseract
4838
+ hiç çalıştırılmaz — Jev döngüsünün en pahalı adımı atlanır. */
4839
+ const OCR_OBSERVE_CACHE_MS = 15000;
4840
+ let lastScreenObserve = { key: '', at: 0, result: null };
4841
+
4842
+ function screenObserveKey(cells) {
4843
+ return Array.isArray(cells) ? cells.map((n) => Math.round(n)).join(',') : '';
4844
+ }
4845
+
4846
+ async function screenObserve() {
4847
+ const frame = await captureScreenFrame();
4848
+ if (!frame) return { ok: false, error: 'ekran görüntüsü alınamadı' };
4849
+ const cap = lastScreenCapture || {};
4850
+ const key = screenObserveKey(imageSignature(frame.img));
4851
+ const now = Date.now();
4852
+ if (key && lastScreenObserve.key === key && lastScreenObserve.result && now - lastScreenObserve.at < OCR_OBSERVE_CACHE_MS) {
4853
+ return { ...lastScreenObserve.result, cached: true, sig: key };
4854
+ }
4855
+ const ocr = await ocrRead({ image: frame.data, boxes: true });
4856
+ if (!ocr.ok) return { ok: false, error: ocr.error || 'OCR başarısız' };
4857
+ const result = {
4858
+ ok: true,
4859
+ w: cap.imgW || 1280,
4860
+ h: cap.imgH || 720,
4861
+ text: ocr.text,
4862
+ lines: ocr.lines || [],
4863
+ sig: key,
4864
+ };
4865
+ lastScreenObserve = { key, at: now, result };
4866
+ return { ...result };
4867
+ }
4868
+
4803
4869
  /* Dahili tarayıcıyla arama (web_search'ün İLK adımı): gerçek Chromium +
4804
4870
  gerçek cookie'ler olduğu için Google bot koruması uygulamaz.
4805
4871
  aep=1 → AI Modu/AI Overview açık: Google cevabi hazırlanmış şekilde döner,
@@ -5125,9 +5191,107 @@ const BROWSER_SNAPSHOT_JS = `(function(){
5125
5191
  return JSON.stringify({count:i,title:document.title,url:location.href,snapshot:lines.join('\\n'),boxes:boxes,vw:window.innerWidth,vh:window.innerHeight});
5126
5192
  })()`;
5127
5193
 
5194
+ /* JEV ULTRAFAST GÖZLEMİ: yapılı element tablosu (kind: click/fill/select),
5195
+ rol/ad/değer/seçenek bilgileriyle. Refler window.__beMap'e yazılır — mevcut
5196
+ browser_click/browser_type/browser_select ref çözümüyle BİREBİR uyumludur.
5197
+ Yalnız görünür + viewport içi + enabled elemanlar; scroll/wait sentetik. */
5198
+ const BROWSER_OBSERVE_JS = `(function(){
5199
+ if(!document.body) return JSON.stringify({url:location.href,title:document.title,w:innerWidth,h:innerHeight,text:'',scroll:{y:0,height:0},actions:[]});
5200
+ window.__beIds=window.__beIds||new WeakMap();
5201
+ window.__beNext=window.__beNext||0;
5202
+ window.__beMap={};
5203
+ const ids=window.__beIds;
5204
+ const identity=(e)=>{ if(!ids.has(e)) ids.set(e,++window.__beNext); const id=ids.get(e); window.__beMap[id]=e; return id; };
5205
+ const safe=(e)=>!['password','file','hidden'].includes(String(e.type||'').toLowerCase());
5206
+ const visible=(e)=>{ try{ return !e.closest('[aria-hidden="true"],[inert]') && e.checkVisibility({checkOpacity:true,checkVisibilityCSS:true}); }catch(err){ return false; } };
5207
+ const name=(e,seen=new Set())=>{
5208
+ if(!e||seen.has(e)) return '';
5209
+ seen.add(e);
5210
+ const referenced=(e.getAttribute('aria-labelledby')||'').split(/\\s+/).map((id)=>name(document.getElementById(id),seen)).filter(Boolean).join(' ');
5211
+ return referenced || e.getAttribute('aria-label') ||
5212
+ [...(e.labels||[])].map((l)=>name(l,seen)).filter(Boolean).join(' ') ||
5213
+ (['button','submit','reset'].includes(String(e.type||'').toLowerCase()) ? e.value : '') || e.getAttribute('alt') ||
5214
+ (e.tagName==='INPUT' ? '' : [...e.childNodes].map((n)=>n.nodeType===3 ? n.textContent :
5215
+ n.nodeType===1 && n.getAttribute('aria-hidden')!=='true' ? name(n,seen) : '').join(' ').trim()) ||
5216
+ e.getAttribute('title') || e.getAttribute('placeholder') || '';
5217
+ };
5218
+ const roles=['button','link','checkbox','radio','switch','tab','menuitem','menuitemradio',
5219
+ 'option','gridcell','combobox','textbox','searchbox','spinbutton'];
5220
+ const selector='a[href],button,input,textarea,select,summary,[contenteditable="true"],'+
5221
+ roles.map((role)=>'[role="'+role+'"]').join(',');
5222
+ const role=(e)=>{
5223
+ const explicit=e.getAttribute('role');
5224
+ if(roles.includes(explicit)) return explicit;
5225
+ if(e.tagName==='BUTTON' || e.tagName==='SUMMARY') return 'button';
5226
+ if(e.tagName==='A') return 'link';
5227
+ if(e.tagName==='SELECT') return 'combobox';
5228
+ if(e.tagName==='TEXTAREA' || e.isContentEditable) return 'textbox';
5229
+ if(e.tagName==='INPUT'){
5230
+ const t=String(e.type||'').toLowerCase();
5231
+ if(t==='checkbox'||t==='radio') return t;
5232
+ if(['button','submit','reset','image'].includes(t)) return 'button';
5233
+ if(t==='search') return 'searchbox';
5234
+ if(t==='number') return 'spinbutton';
5235
+ if(['text','email','url','tel','date','time','month','week','datetime-local'].includes(t)) return 'textbox';
5236
+ }
5237
+ return null;
5238
+ };
5239
+ const actions=[];
5240
+ for(const e of document.querySelectorAll(selector)){
5241
+ if(actions.length>=300) break;
5242
+ if(!safe(e) || !visible(e) || e.matches(':disabled') || e.closest('[aria-disabled="true"]')) continue;
5243
+ const r=e.getBoundingClientRect(), x=r.x+r.width/2, y=r.y+r.height/2, rname=role(e);
5244
+ if(!rname || r.width<=0 || r.height<=0 || x<0 || y<0 || x>=innerWidth || y>=innerHeight) continue;
5245
+ if(rname==='gridcell' && e.querySelector('button,[role="button"]')) continue;
5246
+ const base={ref:identity(e),role:rname,label:name(e)||rname};
5247
+ const et=String(e.type||'').toLowerCase();
5248
+ if(et) base.input_type=et;
5249
+ for(const key of ['checked','selected','expanded']){
5250
+ const value=e.getAttribute('aria-'+key);
5251
+ if(value!==null) base[key]=value;
5252
+ }
5253
+ if(et==='checkbox'||et==='radio') base.checked=String(e.checked);
5254
+ if(e.tagName==='SELECT'){
5255
+ const current=[...e.selectedOptions].map((o)=>o.label).join(', ');
5256
+ for(const o of e.options){
5257
+ if(o.selected||o.disabled||o.closest('optgroup[disabled]')) continue;
5258
+ actions.push({...base,kind:'select',value:o.value,current_value:current,label:base.label+' → '+o.label});
5259
+ }
5260
+ } else {
5261
+ const editable=!e.readOnly && e.getAttribute('aria-readonly')!=='true' &&
5262
+ (['textbox','searchbox','spinbutton'].includes(rname) ||
5263
+ (rname==='combobox' && ['INPUT','TEXTAREA'].includes(e.tagName)));
5264
+ const value='value' in e ? String(e.value) :
5265
+ e.isContentEditable || rname==='combobox' ? String(e.innerText||'').trim() : '';
5266
+ actions.push({...base,kind:editable?'fill':'click',value});
5267
+ if(editable) actions.push({...base,kind:'click',value,label:'Open '+base.label});
5268
+ }
5269
+ }
5270
+ const words=[], walker=document.createTreeWalker(document.body,NodeFilter.SHOW_TEXT);
5271
+ const range=document.createRange(); let node, length=0;
5272
+ while((node=walker.nextNode()) && length<6000){
5273
+ const value=String(node.textContent||'').trim(), parent=node.parentElement;
5274
+ if(!value || !parent || parent.closest('script,style,noscript,template') || !visible(parent)) continue;
5275
+ range.selectNodeContents(node); const tr=range.getBoundingClientRect();
5276
+ if(tr.width>0 && tr.height>0 && tr.bottom>0 && tr.top<innerHeight && tr.right>0 && tr.left<innerWidth){
5277
+ words.push(value); length+=value.length;
5278
+ }
5279
+ }
5280
+ const text=words.join('\\n').slice(0,6000), height=document.documentElement.scrollHeight;
5281
+ const omitted=Math.max(0,actions.length-250);
5282
+ actions.splice(250);
5283
+ if(scrollY+innerHeight<height-2) actions.push({id:'scroll_down',kind:'scroll',label:'Scroll down',delta:560});
5284
+ if(scrollY>0) actions.push({id:'scroll_up',kind:'scroll',label:'Scroll up',delta:-560});
5285
+ actions.push({id:'wait',kind:'wait',label:'Wait for the page to update'});
5286
+ return JSON.stringify({url:location.href,title:document.title,w:innerWidth,h:innerHeight,text:text,
5287
+ scroll:{y:Math.round(scrollY),height:height},actions:actions,omitted_actions:omitted,
5288
+ rev:location.href+'|'+document.title+'|'+actions.length+'|'+text.length+'|'+Math.round(scrollY)+'|'+height});
5289
+ })()`;
5290
+
5128
5291
  function browserActionJs(kind, args) {
5129
5292
  const sel = JSON.stringify(String(args.selector || ''));
5130
5293
  const ref = JSON.stringify(args.ref === undefined ? null : Number(args.ref));
5294
+ const expect = JSON.stringify(args.expect || null);
5131
5295
 
5132
5296
  const resolveTarget = `
5133
5297
  function __target(){
@@ -5141,11 +5305,19 @@ function browserActionJs(kind, args) {
5141
5305
 
5142
5306
  if (kind === 'click') {
5143
5307
  return `(function(){${BROWSER_JS_HELPERS}${resolveTarget};return new Promise((res)=>{try{
5308
+ const ex=${expect};
5309
+ if(ex&&ex.url&&location.href!==ex.url) return res(JSON.stringify({clicked:false,stale:true,reason:'sayfa degisti — karar bayat'}));
5144
5310
  const t=__target();
5145
5311
  if(!t) return res(JSON.stringify({clicked:false,reason:'eleman bulunamadi (ref eski olabilir - browser_snapshot al)'}));
5146
5312
  const how=t.how,el=t.el;
5147
5313
  el.scrollIntoView({block:'center'});
5148
5314
  setTimeout(()=>{try{
5315
+ const r=el.getBoundingClientRect(), cx=r.x+r.width/2, cy=r.y+r.height/2;
5316
+ const top=document.elementFromPoint(cx,cy);
5317
+ if(top&&top!==el&&!el.contains(top)&&!top.contains(el)&&!(top.labels&&[...top.labels].includes(el))){
5318
+ let cover='';try{cover=__label(top);}catch(e){}
5319
+ return res(JSON.stringify({clicked:false,covered:true,reason:'hedef katman altinda: <'+top.tagName.toLowerCase()+'> "'+cover+'"'}));
5320
+ }
5149
5321
  el.click();
5150
5322
  res(JSON.stringify({clicked:true,target:how+' <'+el.tagName.toLowerCase()+'> "'+__label(el)+'"'}));
5151
5323
  }catch(e){res(JSON.stringify({clicked:false,reason:String(e)}));}},80);
@@ -5175,6 +5347,8 @@ function browserActionJs(kind, args) {
5175
5347
  }
5176
5348
  /*__DT_HELPERS_END__*/
5177
5349
  return new Promise((res)=>{try{
5350
+ const ex=${expect};
5351
+ if(ex&&ex.url&&location.href!==ex.url) return res(JSON.stringify({typed:false,stale:true,reason:'sayfa degisti — karar bayat'}));
5178
5352
  const t=__target();
5179
5353
  if(!t) return res(JSON.stringify({typed:false,reason:'eleman bulunamadi (ref eski olabilir - browser_snapshot al)'}));
5180
5354
  const how=t.how,el=t.el;
@@ -5246,6 +5420,8 @@ function browserActionJs(kind, args) {
5246
5420
  if (kind === 'select') {
5247
5421
  const value = JSON.stringify(String(args.value ?? ''));
5248
5422
  return `(function(){${BROWSER_JS_HELPERS}${resolveTarget};return new Promise((res)=>{try{
5423
+ const ex=${expect};
5424
+ if(ex&&ex.url&&location.href!==ex.url) return res(JSON.stringify({selected:false,stale:true,reason:'sayfa degisti — karar bayat'}));
5249
5425
  const t=__target();
5250
5426
  if(!t) return res(JSON.stringify({selected:false,reason:'eleman bulunamadi'}));
5251
5427
  const el=t.el;
@@ -5261,6 +5437,33 @@ function browserActionJs(kind, args) {
5261
5437
  return `JSON.stringify({ok:false,error:'bilinmeyen eylem'})`;
5262
5438
  }
5263
5439
 
5440
+ /* GÜVENİLİR GİRDİ (trusted) için hedef koordinatı: renderer'a gönderilen
5441
+ gerçek fare/tuş olayları React/canvas/otocomplete widget'larında programatik
5442
+ click'ten güvenilirdir. Aynı zamanda katman (occlusion) kontrolü yapar. */
5443
+ function browserPointJs(args) {
5444
+ const sel = JSON.stringify(String(args.selector || ''));
5445
+ const ref = JSON.stringify(args.ref === undefined ? null : Number(args.ref));
5446
+ return `(function(){${BROWSER_JS_HELPERS}
5447
+ try{
5448
+ let el=null,how='';
5449
+ if(${ref}!==null){el=__resolveRef(${ref});how='ref['+${ref}+']';}
5450
+ else{el=__resolve(${sel});how='selector';}
5451
+ if(!el) return JSON.stringify({ok:false,reason:'eleman bulunamadi (ref eski olabilir)'});
5452
+ el.scrollIntoView({block:'center'});
5453
+ const r=el.getBoundingClientRect(), x=r.x+r.width/2, y=r.y+r.height/2;
5454
+ if(r.width<=0||r.height<=0||x<0||y<0||x>=innerWidth||y>=innerHeight) return JSON.stringify({ok:false,reason:'ekran disi'});
5455
+ const top=document.elementFromPoint(x,y);
5456
+ if(top&&top!==el&&!el.contains(top)&&!top.contains(el)&&!(top.labels&&[...top.labels].includes(el))){
5457
+ let cover='';try{cover=__label(top);}catch(e){}
5458
+ return JSON.stringify({ok:false,covered:true,reason:'hedef katman altinda: <'+top.tagName.toLowerCase()+'> "'+cover+'"'});
5459
+ }
5460
+ const editable=!!(el.isContentEditable||el.tagName==='INPUT'||el.tagName==='TEXTAREA');
5461
+ return JSON.stringify({ok:true,how:how,x:Math.round(x),y:Math.round(y),editable:editable,
5462
+ input_type:String((el.getAttribute&&el.getAttribute('type'))||'').toLowerCase()});
5463
+ }catch(e){return JSON.stringify({ok:false,reason:String(e)});}
5464
+ })()`;
5465
+ }
5466
+
5264
5467
  /* eylem günlüğü — her yanıtın sonuna son hamleler eklenir */
5265
5468
  function blog(kind, detail) {
5266
5469
  if (!Array.isArray(browser.history)) browser.history = [];
@@ -5296,31 +5499,83 @@ async function browserSnapshot(signal) {
5296
5499
  }
5297
5500
  }
5298
5501
 
5502
+ /* Jev döngüsü için yapılı gözlem: elementler (kind/rol/değer/seçenek) + sayfa
5503
+ metni + scroll bilgisi. window.__beMap tazelenir → dönen ref'ler mevcut
5504
+ browser_click/browser_type/browser_select tarafından aynen kullanılabilir. */
5505
+ async function browserObserve(signal) {
5506
+ if (!browser.view || !browser.open) return { ok: false, error: 'tarayıcı açık değil' };
5507
+ const wc = browser.view.webContents;
5508
+ const dlgAt = browser.lastDialog ? browser.lastDialog.at : 0;
5509
+ try {
5510
+ const raw = await wc.executeJavaScript(BROWSER_OBSERVE_JS, true);
5511
+ const obj = JSON.parse(raw);
5512
+ return { ok: true, ...obj, ...browserDialogNote(dlgAt) };
5513
+ } catch (e) {
5514
+ return { ok: false, error: String((e && e.message) || e) };
5515
+ }
5516
+ }
5517
+
5299
5518
  async function browserAct(kind, args, signal) {
5300
5519
  if (!browser.view || !browser.open) return { ok: false, error: 'tarayıcı açık değil' };
5301
5520
  const wc = browser.view.webContents;
5521
+ const a = args || {};
5302
5522
  const dlgAt = browser.lastDialog ? browser.lastDialog.at : 0;
5523
+ /* JEV HIZLI YOLU: TypeSafe ajanı her adımda zaten taze observe yapar —
5524
+ ağır imza + taze snapshot + uzun bekleme gereksizdir. */
5525
+ const fast = !!a.fast;
5526
+ const trusted = !!a.trusted && ['click', 'type'].includes(kind) && browser.attached;
5303
5527
  /* DOĞRULAMA: eylem öncesi görsel parmak izi (yalnız anlamlı eylemlerde) */
5304
- const verify = ['click', 'type', 'select', 'press'].includes(kind) && !(args && args.verify === false);
5528
+ const verify = ['click', 'type', 'select', 'press'].includes(kind) && !fast && !(a.verify === false);
5305
5529
  const sigBefore = verify ? await pageSignature(wc) : null;
5306
5530
  try {
5307
- const raw = await wc.executeJavaScript(browserActionJs(kind, args || {}), true);
5308
5531
  let obj = {};
5309
- try { obj = JSON.parse(raw); } catch { obj = { result: String(raw).slice(0, 300) }; }
5532
+ if (trusted) {
5533
+ const pointRaw = await wc.executeJavaScript(browserPointJs(a), true);
5534
+ let point = {};
5535
+ try { point = JSON.parse(pointRaw); } catch { point = { ok: false, reason: 'koordinat alinamadi' }; }
5536
+ if (!point.ok) {
5537
+ obj = kind === 'click' ? { clicked: false, ...point } : { typed: false, ...point };
5538
+ } else if (kind === 'type' && /^(date|time|month|week|datetime-local)$/.test(String(point.input_type || ''))) {
5539
+ /* native tarih/saat alanı: gerçek klavye yerine programatik değer + event
5540
+ (tarayıcı segment segment yazmayı reddeder) */
5541
+ const raw = await wc.executeJavaScript(browserActionJs(kind, a), true);
5542
+ try { obj = JSON.parse(raw); } catch { obj = { result: String(raw).slice(0, 300) }; }
5543
+ } else {
5544
+ wc.sendInputEvent({ type: 'mouseMove', x: point.x, y: point.y });
5545
+ wc.sendInputEvent({ type: 'mouseDown', x: point.x, y: point.y, button: 'left', clickCount: 1 });
5546
+ wc.sendInputEvent({ type: 'mouseUp', x: point.x, y: point.y, button: 'left', clickCount: 1 });
5547
+ if (kind === 'click') {
5548
+ obj = { clicked: true, trusted: true, target: point.how };
5549
+ } else {
5550
+ /* gerçek klavye: önce mevcut değeri seç, sonra yaz — otocomplete tetiklenir */
5551
+ await new Promise((r) => setTimeout(r, 40));
5552
+ try { wc.selectAll(); } catch {}
5553
+ wc.insertText(String(a.text == null ? '' : a.text));
5554
+ if (a.submit) {
5555
+ wc.sendInputEvent({ type: 'keyDown', keyCode: 'Enter' });
5556
+ wc.sendInputEvent({ type: 'keyUp', keyCode: 'Enter' });
5557
+ }
5558
+ obj = { typed: true, trusted: true, target: point.how, value: String(a.text == null ? '' : a.text) };
5559
+ }
5560
+ }
5561
+ } else {
5562
+ const raw = await wc.executeJavaScript(browserActionJs(kind, a), true);
5563
+ try { obj = JSON.parse(raw); } catch { obj = { result: String(raw).slice(0, 300) }; }
5564
+ }
5310
5565
 
5311
5566
  // tıklama/form gönderimi sonrası kısa gezinme bekleme
5312
5567
  let navigated = false;
5313
- if (kind === 'click' || (kind === 'type' && args && args.submit) || kind === 'press') {
5568
+ if (kind === 'click' || (kind === 'type' && a.submit) || kind === 'press') {
5314
5569
  await new Promise((resolve) => {
5315
5570
  let done = false;
5316
5571
  const onNav = () => { navigated = true; setTimeout(fin, 400); };
5317
5572
  const fin = () => { if (!done) { done = true; clearTimeout(t); wc.removeListener('did-navigate', onNav); resolve(); } };
5318
- const t = setTimeout(fin, 1800);
5573
+ const t = setTimeout(fin, fast ? 500 : 1800);
5319
5574
  wc.on('did-navigate', onNav);
5320
5575
  });
5321
5576
  }
5322
5577
  /* SPA: sayfa değiştiyse DOM oturmadan snapshot alınmaz */
5323
- if (navigated) await browserSettle(wc, signal, 2500);
5578
+ if (navigated) await browserSettle(wc, signal, fast ? 500 : 2500);
5324
5579
 
5325
5580
  /* DOĞRULAMA: eylem sonrası değişim oranı — "tıkladım ama bir şey olmadı" tuzağını yakalar */
5326
5581
  let changed = null;
@@ -5344,8 +5599,9 @@ async function browserAct(kind, args, signal) {
5344
5599
  : (obj.reason || 'tamam')
5345
5600
  );
5346
5601
 
5347
- /* HIZ: eylem cevabına taze snapshot göm — model ayrı browser_snapshot çağırmaz (tur sayısı yarıya iner) */
5348
- const freshSnap = await browserSnapshotNow(wc);
5602
+ /* HIZ: eylem cevabına taze snapshot göm — model ayrı browser_snapshot çağırmaz (tur sayısı yarıya iner).
5603
+ Jev hızlı yolunda snapshot gömülmez; döngü zaten observe eder. */
5604
+ const freshSnap = fast ? null : await browserSnapshotNow(wc);
5349
5605
 
5350
5606
  return {
5351
5607
  ok: true,
@@ -5354,6 +5610,7 @@ async function browserAct(kind, args, signal) {
5354
5610
  url: wc.getURL(),
5355
5611
  title: wc.getTitle(),
5356
5612
  navigated,
5613
+ ...(fast ? { fast: true } : {}),
5357
5614
  ...(changed != null ? { changed, changeRatio } : {}),
5358
5615
  ...(changed === false && ['click', 'type', 'select'].includes(kind)
5359
5616
  ? {
@@ -5365,11 +5622,13 @@ async function browserAct(kind, args, signal) {
5365
5622
  : {}),
5366
5623
  ...browserDialogNote(dlgAt),
5367
5624
  ...(freshSnap ? { snapshot: freshSnap.snapshot, refCount: freshSnap.count } : {}),
5368
- ...(navigated
5369
- ? { note: 'sayfa degisti — yanıtta güncel snapshot var; refler eskiyse yeni browser_snapshot al' }
5370
- : freshSnap
5371
- ? { note: 'yanıtta güncel snapshot (' + freshSnap.count + ' ref) — sonraki hamlede bunları kullan, ayrıca snapshot alma' }
5372
- : {}),
5625
+ ...(fast
5626
+ ? {}
5627
+ : navigated
5628
+ ? { note: 'sayfa degisti — yanıtta güncel snapshot var; refler eskiyse yeni browser_snapshot al' }
5629
+ : freshSnap
5630
+ ? { note: 'yanıtta güncel snapshot (' + freshSnap.count + ' ref) — sonraki hamlede bunları kullan, ayrıca snapshot alma' }
5631
+ : {}),
5373
5632
  recent: recentLog(),
5374
5633
  };
5375
5634
  } catch (e) {
@@ -7153,21 +7412,28 @@ ipcMain.handle('supermemory:get', () => {
7153
7412
  settings'ten CANLI okur (setConfig kancası) — kaydetmek yeterli. */
7154
7413
  ipcMain.handle('typesafe:get', () => {
7155
7414
  const t = settings.typesafe || {};
7156
- return { apiKey: t.apiKey ? SECRET_MASK : '', model: t.model || 'jev-latest', set: !!t.apiKey };
7415
+ return { apiKey: t.apiKey ? SECRET_MASK : '', model: t.model || 'jev-latest', enabled: t.enabled !== false, set: !!t.apiKey };
7157
7416
  });
7158
7417
  ipcMain.handle('typesafe:set', (_e, cfg) => {
7159
7418
  try {
7160
7419
  const incomingKey = String((cfg && cfg.apiKey) || '').trim();
7161
7420
  const prevKey = settings.typesafe && settings.typesafe.apiKey ? String(settings.typesafe.apiKey) : '';
7421
+ const prevEnabled = !(settings.typesafe && settings.typesafe.enabled === false);
7162
7422
  settings.typesafe = {
7163
7423
  /* maskeli anahtar geri gelirse mevcut korunur */
7164
7424
  apiKey: incomingKey === SECRET_MASK ? prevKey : incomingKey,
7165
7425
  model: String((cfg && cfg.model) || 'jev-latest').trim() || 'jev-latest',
7426
+ enabled: cfg && typeof cfg.enabled === 'boolean' ? cfg.enabled : prevEnabled,
7166
7427
  };
7167
7428
  saveSettings();
7168
7429
  return {
7169
7430
  ok: true,
7170
- typesafe: { apiKey: settings.typesafe.apiKey ? SECRET_MASK : '', model: settings.typesafe.model, set: !!settings.typesafe.apiKey },
7431
+ typesafe: {
7432
+ apiKey: settings.typesafe.apiKey ? SECRET_MASK : '',
7433
+ model: settings.typesafe.model,
7434
+ enabled: settings.typesafe.enabled !== false,
7435
+ set: !!settings.typesafe.apiKey,
7436
+ },
7171
7437
  };
7172
7438
  } catch (e) {
7173
7439
  return { ok: false, error: String((e && e.message) || e) };
@@ -16691,7 +16957,7 @@ async function annotateScreenShot(img) {
16691
16957
  /* Ana ekrandan JPEG dataURL yakalar (computer_use ve screen:capture ortak).
16692
16958
  opts.annotate: ızgara + imleç işareti (yalnız ajan computer_look kullanır;
16693
16959
  OCR/komut yolları düz görüntü ister). */
16694
- async function captureScreenDataUrl(opts) {
16960
+ async function captureScreenFrame(opts) {
16695
16961
  try {
16696
16962
  const sources = await desktopCapturer.getSources({
16697
16963
  types: ['screen'],
@@ -16722,12 +16988,17 @@ async function captureScreenDataUrl(opts) {
16722
16988
  }
16723
16989
  if (!jpeg || !jpeg.length) jpeg = img.toJPEG(72);
16724
16990
  if (!jpeg || !jpeg.length) return null;
16725
- return 'data:image/jpeg;base64,' + jpeg.toString('base64');
16991
+ return { data: 'data:image/jpeg;base64,' + jpeg.toString('base64'), img };
16726
16992
  } catch {
16727
16993
  return null;
16728
16994
  }
16729
16995
  }
16730
16996
 
16997
+ async function captureScreenDataUrl(opts) {
16998
+ const frame = await captureScreenFrame(opts);
16999
+ return frame ? frame.data : null;
17000
+ }
17001
+
16731
17002
  /* ---- görsel parmak izi: aksiyon "işe yaradı mı" (verify) ---- */
16732
17003
  function imageSignature(img) {
16733
17004
  try {
@@ -16784,7 +17055,10 @@ async function computerActScaled(op, args) {
16784
17055
  }
16785
17056
  const opName = String(op || '').toLowerCase();
16786
17057
  const scaled = cap && cap.imgW > 0 ? computeruse.scaleArgs(a, cap) : a;
16787
- const verify = CU_VERIFY_OPS.has(opName) && a.verify !== false;
17058
+ /* JEV HIZLI YOLU: T3SFast döngüsü ekranı zaten observe ile izler — ağır
17059
+ imza + 350 ms bekleme gereksiz. */
17060
+ const fast = !!a.fast;
17061
+ const verify = CU_VERIFY_OPS.has(opName) && !fast && a.verify !== false;
16788
17062
  const before = verify ? await screenSignature() : null;
16789
17063
  let r;
16790
17064
  try {
@@ -758,8 +758,10 @@
758
758
  ts_sub: 'Tipli karar motoru: metin üretmeden olasılıklı yanıt (noul/choice/score). Anahtar girilince ajanlar typesafe_decision aracını kullanır; Beast Finance rol→skill eşleştirmesinde "typesafe-ai" seçilirse roller bu kararları kullanır.',
759
759
  ts_key_label: 'TypeSafe API anahtarı (typesafe.ai)',
760
760
  ts_model_label: 'Model (varsayılan: jev-latest)',
761
+ ts_enabled_label: 'TypeSafe açık (browser_agent/T3SFast + typesafe_decision kullanılabilir)',
761
762
  ts_status_set: 'TypeSafe aktif — kayıtlı anahtar: *** · model: ',
762
763
  ts_status_unset: 'TypeSafe pasif — anahtar girmeden typesafe_decision aracı çalışmaz.',
764
+ ts_status_off: 'TypeSafe anahtarı kayıtlı ama AYAR KAPALI — Jev tabanlı araçlar (browser_agent) çalışmaz.',
763
765
  ts_save: 'Kaydet',
764
766
  ts_test: 'Bağlantıyı Test Et',
765
767
  ts_testing: 'Test ediliyor…',
@@ -1513,8 +1515,10 @@
1513
1515
  ts_sub: 'Typed decision engine: probabilistic answers without text generation (noul/choice/score). Once a key is set, agents can use the typesafe_decision tool; when "typesafe-ai" is picked in Beast Finance role→skill matching, those roles use it.',
1514
1516
  ts_key_label: 'TypeSafe API key (typesafe.ai)',
1515
1517
  ts_model_label: 'Model (default: jev-latest)',
1518
+ ts_enabled_label: 'TypeSafe ON (enables browser_agent/T3SFast + typesafe_decision)',
1516
1519
  ts_status_set: 'TypeSafe active — saved key: *** · model: ',
1517
1520
  ts_status_unset: 'TypeSafe inactive — typesafe_decision does nothing without a key.',
1521
+ ts_status_off: 'TypeSafe key saved but the SWITCH IS OFF — Jev-powered tools (browser_agent) will not run.',
1518
1522
  ts_save: 'Save',
1519
1523
  ts_test: 'Test Connection',
1520
1524
  ts_testing: 'Testing…',
@@ -1688,7 +1688,7 @@ async function renderWebSearchPane() {
1688
1688
  async function renderTypesafePane() {
1689
1689
  const pane = $('#tab-typesafe');
1690
1690
  if (!pane) return;
1691
- const ts = await beast.typesafeGet().catch(() => ({ set: false, apiKey: '', model: 'jev-latest' }));
1691
+ const ts = await beast.typesafeGet().catch(() => ({ set: false, apiKey: '', model: 'jev-latest', enabled: true }));
1692
1692
  pane.innerHTML =
1693
1693
  '<h2>' + _t('ts_h2') + '</h2>' +
1694
1694
  '<div class="sub">' + _t('ts_sub') + '</div>' +
@@ -1697,6 +1697,8 @@ async function renderTypesafePane() {
1697
1697
  '<input id="tsKeyInp" class="inp" type="password" placeholder="ts_..." autocomplete="new-password" spellcheck="false" />' +
1698
1698
  '<label class="mem-label" style="margin-top:10px">' + _t('ts_model_label') + '</label>' +
1699
1699
  '<input id="tsModelInp" class="inp" type="text" placeholder="jev-latest" spellcheck="false" />' +
1700
+ '<label class="mem-label" style="margin-top:10px;display:flex;align-items:center;gap:8px;cursor:pointer">' +
1701
+ '<input id="tsEnabled" type="checkbox" /> ' + _t('ts_enabled_label') + '</label>' +
1700
1702
  '<div class="form-grid" style="grid-template-columns:auto auto auto;gap:8px;margin-top:10px">' +
1701
1703
  '<button id="tsSave" class="btn">' + _t('ts_save') + '</button>' +
1702
1704
  '<button id="tsTest" class="btn ghost">' + _t('ts_test') + '</button>' +
@@ -1704,9 +1706,13 @@ async function renderTypesafePane() {
1704
1706
  '<div class="sub" style="margin-top:10px">' + _t('ts_hint') + '</div>';
1705
1707
  const tsSt = $('#tsStatus');
1706
1708
  const tsModel = $('#tsModelInp');
1709
+ const tsEnabled = $('#tsEnabled');
1707
1710
  tsModel.value = ts.model || 'jev-latest';
1711
+ tsEnabled.checked = ts.enabled !== false;
1708
1712
  const setTsSt = (r) => {
1709
- tsSt.textContent = r.set ? _t('ts_status_set') + r.model : _t('ts_status_unset');
1713
+ if (!r.set) tsSt.textContent = _t('ts_status_unset');
1714
+ else if (r.enabled === false) tsSt.textContent = _t('ts_status_off');
1715
+ else tsSt.textContent = _t('ts_status_set') + r.model;
1710
1716
  };
1711
1717
  setTsSt(ts);
1712
1718
  const tsTest = $('#tsTest');
@@ -1724,7 +1730,7 @@ async function renderTypesafePane() {
1724
1730
  $('#tsSave').addEventListener('click', async () => {
1725
1731
  const v = $('#tsKeyInp').value.trim();
1726
1732
  if (!v) { toast(_t('ts_empty_toast')); return; }
1727
- const rr = await beast.typesafeSet({ apiKey: v, model: tsModel.value.trim() }).catch(() => null);
1733
+ const rr = await beast.typesafeSet({ apiKey: v, model: tsModel.value.trim(), enabled: tsEnabled.checked }).catch(() => null);
1728
1734
  $('#tsKeyInp').value = '';
1729
1735
  if (rr && rr.ok) {
1730
1736
  setTsSt(rr.typesafe || {});
@@ -1733,8 +1739,18 @@ async function renderTypesafePane() {
1733
1739
  toast(_t('ts_fail_toast'));
1734
1740
  }
1735
1741
  });
1742
+ /* Anahtar zaten kayıtlıyken aç/kapa: maskeli anahtar geri gönderilir, anahtar korunur */
1743
+ tsEnabled.addEventListener('change', async () => {
1744
+ const rr = await beast.typesafeSet({ apiKey: ts.apiKey || '', model: tsModel.value.trim(), enabled: tsEnabled.checked }).catch(() => null);
1745
+ if (rr && rr.ok) {
1746
+ setTsSt(rr.typesafe || {});
1747
+ toast(_t('ts_saved_toast'));
1748
+ } else {
1749
+ toast(_t('ts_fail_toast'));
1750
+ }
1751
+ });
1736
1752
  $('#tsClear').addEventListener('click', async () => {
1737
- await beast.typesafeSet({ apiKey: '', model: tsModel.value.trim() }).catch(() => {});
1753
+ await beast.typesafeSet({ apiKey: '', model: tsModel.value.trim(), enabled: tsEnabled.checked }).catch(() => {});
1738
1754
  $('#tsKeyInp').value = '';
1739
1755
  setTsSt({ set: false, model: tsModel.value.trim() || 'jev-latest' });
1740
1756
  toast(_t('ts_cleared_toast'));