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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.64.1",
4
+ "version": "2.65.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -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 = 180;
55
- const WAIT_MS = 450;
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 >= 3) return finish('blocked', { reason: 'üst üste 3 eylem sayfayı değiştirmedi' });
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: 120 });
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: 120 });
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: 120 });
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
- TYPE_TEXT: 'Type text into the control focused by the most recent CLICK. A small LLM supplies the value.',
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 = 600;
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 = { CLICK: OPERATION_LABELS.CLICK };
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 === 'CLICK') {
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 === 'CLICK' ? clickTargets[target] : null,
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: 350 });
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 && last.kind === 'click' && !last.failed) {
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 === 'CLICK') {
281
- kind = 'click';
282
- result = await deps.act('click', { x: decision.line.x, y: decision.line.y, fast: true });
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: 150 });
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: 200 });
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: 200 });
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: 150 });
332
+ await deps.wait({ ms: 80 });
314
333
  } else if (operation === 'WAIT') {
315
334
  kind = 'wait';
316
335
  await deps.wait({ ms: WAIT_MS });
@@ -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 — pencereyi "window" alanıyla otomatik öne getirt (ör. window:"Notepad"), sonucu computer_look ile doğrula; 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.',
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
- async function browserSettle(wc, signal, maxMs = 4000) {
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, 300));
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ı kısa gezinme bekleme
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; setTimeout(fin, 400); };
5572
- const fin = () => { if (!done) { done = true; clearTimeout(t); wc.removeListener('did-navigate', onNav); resolve(); } };
5573
- const t = setTimeout(fin, fast ? 500 : 1800);
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 oturmadan snapshot alınmaz */
5578
- if (navigated) await browserSettle(wc, signal, fast ? 500 : 2500);
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;