beast-agent 2.44.14 → 2.46.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.44.14",
4
+ "version": "2.46.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -5,7 +5,11 @@
5
5
  mouse/klavye PowerShell üzerinden user32 API ile sürülür.
6
6
 
7
7
  Güvenlik: yalnızca engine araç çağrılarıyla çalışır; koordinat sınırlı,
8
- hız limiting'li (aksiyon arası min gecikme) ve yazma metni kırpılır. */
8
+ hız limiting'li (aksiyon arası min gecikme) ve yazma metni kırpılır.
9
+
10
+ KOORDİNAT SÖZLEŞMESİ: computer_look görüntüsü 1280px genişliğe ölçeklenir;
11
+ 1280 tabanlı koordinatların GERÇEK ekrana çevrilmesi main.js'teki
12
+ computerActScaled sarmalayıcısında yapılır. Bu modül gerçek koordinat alır. */
9
13
 
10
14
  const { execFile } = require('child_process');
11
15
 
@@ -29,14 +33,141 @@ function ps(script) {
29
33
  });
30
34
  }
31
35
 
32
- /** Sanal masaüstü çözünürlüğü */
33
- async function screenSize() {
36
+ /** Sanal masaüstü çözünürlüğü (30 sn önbellek: her aksiyonda PS açma maliyeti olmasın) */
37
+ let sizeCache = { at: 0, w: 0, h: 0 };
38
+ async function screenSize(force) {
39
+ if (!force && sizeCache.w && Date.now() - sizeCache.at < 30000) {
40
+ return { w: sizeCache.w, h: sizeCache.h };
41
+ }
34
42
  const out = await ps(
35
43
  'Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width.ToString() + "x" + [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height'
36
44
  );
37
45
  const m = out.match(/(\d+)x(\d+)/);
38
46
  if (!m) throw new Error('çözünürlük alınamadı');
39
- return { w: Number(m[1]), h: Number(m[2]) };
47
+ sizeCache = { at: Date.now(), w: Number(m[1]), h: Number(m[2]) };
48
+ return { w: sizeCache.w, h: sizeCache.h };
49
+ }
50
+
51
+ /* koordinat kelepçesi: sanal masaüstünde negatif değerler geçerli (sol/üst monitör) */
52
+ function clampCoord(v) {
53
+ const n = Math.round(Number(v));
54
+ if (!Number.isFinite(n)) return null;
55
+ return Math.max(-32000, Math.min(32000, n));
56
+ }
57
+
58
+ /* computer_look görüntüsü 1280px genişliğe ölçeklenir; modelden gelen 1280
59
+ tabanlı koordinatları GERÇEK ekran koordinatına çevirir (çok monitörde
60
+ ekran ofseti dahil). Saf fonksiyon — test edilebilir. cap:
61
+ { x, y, w, h, imgW, imgH } (main.js captureScreenDataUrl doldurur). */
62
+ function scaleArgs(a, cap) {
63
+ const out = { ...(a || {}) };
64
+ if (!cap || !cap.imgW || !cap.imgH) return out;
65
+ const kx = cap.w / cap.imgW;
66
+ const ky = cap.h / cap.imgH;
67
+ for (const key of ['x', 'x2']) {
68
+ if (out[key] != null && out[key] !== '') out[key] = Math.round(cap.x + Number(out[key]) * kx);
69
+ }
70
+ for (const key of ['y', 'y2']) {
71
+ if (out[key] != null && out[key] !== '') out[key] = Math.round(cap.y + Number(out[key]) * ky);
72
+ }
73
+ return out;
74
+ }
75
+
76
+ const MOUSE_CLASS = `
77
+ Add-Type @"
78
+ using System;
79
+ using System.Runtime.InteropServices;
80
+ public class BMouse {
81
+ [DllImport("user32.dll")] public static extern bool SetCursorPos(int X, int Y);
82
+ [DllImport("user32.dll")] public static extern void mouse_event(uint dwFlags, int dx, int dy, int dwData, UIntPtr dwExtraInfo);
83
+ }
84
+ "@`;
85
+
86
+ const LMB_DOWN = 0x0002;
87
+ const LMB_UP = 0x0004;
88
+ const RMB_DOWN = 0x0008;
89
+ const RMB_UP = 0x0010;
90
+ const WHEEL = 0x0800;
91
+
92
+ /* Tek PS sürecinde: imleci taşı + (gerekiyorsa) tık(lar) — eski sürüm her
93
+ adım için ayrı süreç açıyordu (tık başına 2-3 spawn → ~1 sn gecikme). */
94
+ function mouseScript({ x, y, down, up, dbl, settleMs = 35 }) {
95
+ const ev = (flags) => `[BMouse]::mouse_event(${flags}, 0, 0, 0, [UIntPtr]::Zero)`;
96
+ let body = '';
97
+ if (x != null && y != null) {
98
+ body += `[BMouse]::SetCursorPos(${x}, ${y}) | Out-Null\nStart-Sleep -Milliseconds ${settleMs}\n`;
99
+ }
100
+ if (down && up) {
101
+ body += `${ev(down)}\nStart-Sleep -Milliseconds 45\n${ev(up)}\n`;
102
+ if (dbl) body += `Start-Sleep -Milliseconds 70\n${ev(down)}\nStart-Sleep -Milliseconds 45\n${ev(up)}\n`;
103
+ }
104
+ return `${MOUSE_CLASS}\n${body}`;
105
+ }
106
+
107
+ /* tek çağrıda tekerlek: eski sürüm |dy|*3 ayrı süreç açıyordu (dy=10 → 30 spawn) */
108
+ function wheelScript(x, y, dy) {
109
+ const delta = Math.round(dy * 720); // eski davranış: tik başına ±240 × 3 × dy
110
+ return (
111
+ `${MOUSE_CLASS}\n` +
112
+ `[BMouse]::SetCursorPos(${x}, ${y}) | Out-Null\n` +
113
+ `[BMouse]::mouse_event(${WHEEL}, 0, 0, ${delta}, [UIntPtr]::Zero)\n`
114
+ );
115
+ }
116
+
117
+ /* sürükle-bırak: basılı tut → ara noktalardan geç → bırak */
118
+ function dragScript(x1, y1, x2, y2) {
119
+ const steps = 12;
120
+ return (
121
+ `${MOUSE_CLASS}\n` +
122
+ `[BMouse]::SetCursorPos(${x1}, ${y1}) | Out-Null\n` +
123
+ `Start-Sleep -Milliseconds 60\n` +
124
+ `[BMouse]::mouse_event(${LMB_DOWN}, 0, 0, 0, [UIntPtr]::Zero)\n` +
125
+ `for ($i = 1; $i -le ${steps}; $i++) {\n` +
126
+ ` $nx = [int](${x1} + (${x2} - ${x1}) * $i / ${steps})\n` +
127
+ ` $ny = [int](${y1} + (${y2} - ${y1}) * $i / ${steps})\n` +
128
+ ` [BMouse]::SetCursorPos($nx, $ny) | Out-Null\n` +
129
+ ` Start-Sleep -Milliseconds 18\n` +
130
+ `}\n` +
131
+ `Start-Sleep -Milliseconds 40\n` +
132
+ `[BMouse]::mouse_event(${LMB_UP}, 0, 0, 0, [UIntPtr]::Zero)\n`
133
+ );
134
+ }
135
+
136
+ /* pencere başlığına göre öne getir (menü/pencere odaklama için) */
137
+ function focusScript(titleB64) {
138
+ return `
139
+ Add-Type @"
140
+ using System;
141
+ using System.Text;
142
+ using System.Runtime.InteropServices;
143
+ public class BWin {
144
+ public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
145
+ [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc cb, IntPtr lParam);
146
+ [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr hWnd);
147
+ [DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
148
+ [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
149
+ [DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
150
+ public static IntPtr Found = IntPtr.Zero;
151
+ public static string Needle = "";
152
+ public static bool Cb(IntPtr h, IntPtr l) {
153
+ if (!IsWindowVisible(h)) return true;
154
+ var sb = new StringBuilder(512);
155
+ GetWindowText(h, sb, 512);
156
+ var t = sb.ToString();
157
+ if (t.Length > 0 && t.IndexOf(Needle, StringComparison.OrdinalIgnoreCase) >= 0) { Found = h; return false; }
158
+ return true;
159
+ }
160
+ }
161
+ "@
162
+ $n = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("${titleB64}"))
163
+ [BWin]::Needle = $n
164
+ [BWin]::EnumWindows([BWin+EnumWindowsProc][BWin]::Cb, [IntPtr]::Zero) | Out-Null
165
+ if ([BWin]::Found -ne [IntPtr]::Zero) {
166
+ [BWin]::ShowWindow([BWin]::Found, 9) | Out-Null
167
+ $r = [BWin]::SetForegroundWindow([BWin]::Found)
168
+ if ($r) { 'ok' } else { 'noforeground' }
169
+ } else { 'notfound' }
170
+ `;
40
171
  }
41
172
 
42
173
  /**
@@ -45,36 +176,54 @@ async function screenSize() {
45
176
  * dblclick x y → çift tık
46
177
  * rightclick x y → sağ tık
47
178
  * move x y → imleci taşı
48
- * type text → klavyeyle yaz (Unicode destekli, SendInput WM_CHAR benzeri)
179
+ * hover x y → taşı + menü açılsın diye bekle
180
+ * drag x y x2 y2 → sürükle-bırak
181
+ * focus title → başlığı eşleşen pencereyi öne getir
182
+ * type text → klavyeyle yaz (pano köprüsü; pano geri yüklenir)
49
183
  * key combo → "enter", "ctrl+s", "alt+tab", "win" ...
50
- * scroll dx dy → tekerlek
184
+ * scroll x y dy → tekerlek (tek çağrı)
51
185
  */
52
186
  async function act(op, args = {}) {
53
187
  await pace();
54
- const x = Math.max(0, Math.round(Number(args.x) || 0));
55
- const y = Math.max(0, Math.round(Number(args.y) || 0));
188
+ const x = clampCoord(args.x == null ? 0 : args.x);
189
+ const y = clampCoord(args.y == null ? 0 : args.y);
56
190
  switch (op) {
57
191
  case 'move':
58
- await mouseMove(x, y);
192
+ await ps(mouseScript({ x, y }));
59
193
  return { ok: true };
194
+ case 'hover':
195
+ await ps(mouseScript({ x, y }));
196
+ await sleep(280);
197
+ return { ok: true, at: { x, y } };
60
198
  case 'click':
61
- await mouseMove(x, y);
62
- await clickSimple('left');
199
+ await ps(mouseScript({ x, y, down: LMB_DOWN, up: LMB_UP }));
63
200
  return { ok: true, at: { x, y } };
64
201
  case 'dblclick':
65
- await mouseMove(x, y);
66
- await clickSimple('left');
67
- await sleep(70);
68
- await clickSimple('left');
202
+ await ps(mouseScript({ x, y, down: LMB_DOWN, up: LMB_UP, dbl: true }));
69
203
  return { ok: true, at: { x, y } };
70
204
  case 'rightclick':
71
- await mouseMove(x, y);
72
- await clickSimple('right');
205
+ await ps(mouseScript({ x, y, down: RMB_DOWN, up: RMB_UP }));
73
206
  return { ok: true, at: { x, y } };
207
+ case 'drag': {
208
+ const x2 = clampCoord(args.x2);
209
+ const y2 = clampCoord(args.y2);
210
+ if (x == null || y == null || x2 == null || y2 == null) {
211
+ return { ok: false, error: 'drag için x,y,x2,y2 gerekli' };
212
+ }
213
+ await ps(dragScript(x, y, x2, y2));
214
+ return { ok: true, from: { x, y }, to: { x2, y2 } };
215
+ }
216
+ case 'focus': {
217
+ const title = String(args.title || args.text || '').trim().slice(0, 120);
218
+ if (!title) return { ok: false, error: 'focus için title gerekli' };
219
+ const out = await ps(focusScript(Buffer.from(title, 'utf8').toString('base64')));
220
+ if (out.includes('ok')) return { ok: true, focused: true, title };
221
+ if (out.includes('noforeground')) return { ok: true, focused: false, title, note: 'pencere bulundu ama öne getirilemedi' };
222
+ return { ok: false, error: `pencere bulunamadı: ${title}` };
223
+ }
74
224
  case 'type': {
75
225
  const text = String(args.text ?? '').slice(0, 2000);
76
226
  if (!text) return { ok: false, error: 'boş metin' };
77
- /* Add-Type SendInput tabanlı Unicode typer */
78
227
  await ps(buildTyperScript(text));
79
228
  return { ok: true, chars: text.length };
80
229
  }
@@ -87,11 +236,7 @@ async function act(op, args = {}) {
87
236
  case 'scroll': {
88
237
  const dy = Math.max(-10, Math.min(10, Math.round(Number(args.dy) || 0)));
89
238
  if (!dy) return { ok: false, error: 'dy gerekli (-10..10)' };
90
- await mouseMove(x || 640, y || 360);
91
- const clicks = Math.abs(dy) * 3;
92
- for (let i = 0; i < clicks; i++) {
93
- await wheel(dy > 0 ? 1 : -1); // pozitif dy = aşağı
94
- }
239
+ await ps(wheelScript(x || 640, y || 360, dy));
95
240
  return { ok: true, dy };
96
241
  }
97
242
  default:
@@ -105,38 +250,20 @@ function sleep(ms) {
105
250
 
106
251
  /* ---------- PowerShell snippet üreticileri ---------- */
107
252
 
108
- const MOUSE_MOVE_TPL = `
109
- Add-Type @"
110
- using System;
111
- using System.Runtime.InteropServices;
112
- public class BM {
113
- [DllImport("user32.dll")] public static extern bool SetCursorPos(int X, int Y);
114
- }
115
- "@
116
- [BM]::SetCursorPos(%%X%%, %%Y%%) | Out-Null
117
- `;
118
-
119
- const WHEEL_TPL = `
120
- Add-Type @"
121
- using System;
122
- using System.Runtime.InteropServices;
123
- public class BW {
124
- [DllImport("user32.dll")] public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);
125
- }
126
- "@
127
- [BW]::mouse_event(0x0800, 0, 0, %%DELTA%%, [UIntPtr]::Zero)
128
- `;
129
-
130
- /* Unicode güvenli yazım: SendInput yerine pano + Ctrl+V fallback'u en sağlamı.
131
- Panoyu geri yüklemek yerine tek seferlik kullanıp bırakıyoruz (hız öncelik). */
253
+ /* Unicode güvenli yazım: pano köprüsü. Panoyu ÖNCE kaydet, yapıştırdıktan
254
+ SONRA geri koy — kullanıcının panosu kaybolmaz. */
132
255
  function buildTyperScript(text) {
133
256
  const b64 = Buffer.from(text, 'utf16le').toString('base64');
134
257
  return `
135
258
  $t = [System.Text.Encoding]::Unicode.GetString([Convert]::FromBase64String("${b64}"))
136
259
  Add-Type -AssemblyName System.Windows.Forms
260
+ $prev = $null
261
+ try { $prev = [System.Windows.Forms.Clipboard]::GetText() } catch {}
137
262
  [System.Windows.Forms.Clipboard]::SetText($t)
138
263
  Start-Sleep -Milliseconds 80
139
264
  [System.Windows.Forms.SendKeys]::SendWait("^v")
265
+ Start-Sleep -Milliseconds 150
266
+ if ($prev -ne $null -and $prev -ne "") { try { [System.Windows.Forms.Clipboard]::SetText($prev) } catch {} }
140
267
  `;
141
268
  }
142
269
 
@@ -145,18 +272,19 @@ function buildKeyScript(combo) {
145
272
  const map = {
146
273
  enter: '{ENTER}', tab: '{TAB}', esc: '{ESC}', escape: '{ESC}',
147
274
  backspace: '{BACKSPACE}', delete: '{DELETE}', del: '{DELETE}',
275
+ insert: '{INSERT}', ins: '{INSERT}', capslock: '{CAPSLOCK}',
148
276
  home: '{HOME}', end: '{END}', pgup: '{PGUP}', pgdn: '{PGDN}',
149
277
  up: '{UP}', down: '{DOWN}', left: '{LEFT}', right: '{RIGHT}',
150
- space: ' ', win: '^({ESC})',
278
+ space: ' ',
151
279
  };
152
280
  let out = '';
153
281
  const parts = String(combo).toLowerCase().split('+').map((p) => p.trim());
154
282
  let mods = '';
155
283
  for (const p of parts) {
156
- if (p === 'ctrl') mods += '^';
284
+ if (p === 'ctrl' || p === 'control') mods += '^';
157
285
  else if (p === 'alt') mods += '%';
158
286
  else if (p === 'shift') mods += '+';
159
- else if (p === 'win') out += ''; // win ayrı ele
287
+ else if (p === 'win') mods += '^{ESC}';
160
288
  else if (/^f\d{1,2}$/.test(p)) out += '{' + p.toUpperCase() + '}';
161
289
  else if (map[p]) out += map[p];
162
290
  else out += p.slice(0, 1).toUpperCase(); // tek harf/tuş
@@ -171,30 +299,50 @@ Add-Type -AssemblyName System.Windows.Forms
171
299
  }
172
300
 
173
301
  async function mouseMove(x, y) {
174
- await ps(MOUSE_MOVE_TPL.replace(/%%X%%/g, String(x)).replace(/%%Y%%/g, String(y)));
302
+ await ps(mouseScript({ x: clampCoord(x), y: clampCoord(y) }));
175
303
  }
176
304
 
177
- /* down+up tek script'te — karışık replace zinciri yerine bu kullanılır */
305
+ /* down+up tek script'te */
178
306
  async function clickSimple(button) {
179
- const down = button === 'right' ? 0x0008 : 0x0002;
180
- const up = button === 'right' ? 0x0010 : 0x0004;
181
- await ps(`
182
- Add-Type @"
183
- using System;
184
- using System.Runtime.InteropServices;
185
- public class BC2 {
186
- [DllImport("user32.dll")] public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);
307
+ await ps(mouseScript({ down: button === 'right' ? RMB_DOWN : LMB_DOWN, up: button === 'right' ? RMB_UP : LMB_UP }));
187
308
  }
188
- "@
189
- [BC2]::mouse_event(${down}, 0, 0, 0, [UIntPtr]::Zero)
190
- Start-Sleep -Milliseconds 45
191
- [BC2]::mouse_event(${up}, 0, 0, 0, [UIntPtr]::Zero)
192
- `);
309
+
310
+ /* ---------- GÖRSEL DOĞRULAMA (verify) ----------
311
+ "Aksiyon işe yaradı mı?" sorusunu ucuz bir görsel parmak iziyle yanıtlar:
312
+ kareyi 8x6 hücreye bölüp her hücrenin ortalama parlaklığını çıkarır.
313
+ İki imza arasındaki fark > eşik olan hücre oranı = değişim oranı.
314
+ Animasyon/anti-alias gürültüsüne toleranslıdır (eşik 8/255). */
315
+ function bitmapSignature(bmp, w, h) {
316
+ try {
317
+ if (!bmp || !w || !h) return null;
318
+ const cells = [];
319
+ const cw = Math.max(1, Math.floor(w / 8));
320
+ const ch = Math.max(1, Math.floor(h / 6));
321
+ for (let cy = 0; cy < 6; cy++) {
322
+ for (let cx = 0; cx < 8; cx++) {
323
+ let sum = 0;
324
+ let n = 0;
325
+ for (let y = cy * ch; y < Math.min(h, (cy + 1) * ch); y += 2) {
326
+ for (let x = cx * cw; x < Math.min(w, (cx + 1) * cw); x += 2) {
327
+ const o = (y * w + x) * 4;
328
+ sum += 0.114 * bmp[o] + 0.587 * bmp[o + 1] + 0.299 * bmp[o + 2];
329
+ n++;
330
+ }
331
+ }
332
+ cells.push(n ? sum / n : 0);
333
+ }
334
+ }
335
+ return cells;
336
+ } catch {
337
+ return null;
338
+ }
193
339
  }
194
340
 
195
- async function wheel(dir) {
196
- const delta = dir < 0 ? -240 : 240; // WHEEL_DELTA=120 katları; negatif=yukarı görüntüde aşağı kayar bazı app'lerde
197
- await ps(WHEEL_TPL.replace(/%%DELTA%%/g, String(delta)));
341
+ function signatureDiff(a, b, thresh = 8) {
342
+ if (!a || !b || a.length !== b.length || !a.length) return null;
343
+ let changed = 0;
344
+ for (let i = 0; i < a.length; i++) if (Math.abs(a[i] - b[i]) > thresh) changed++;
345
+ return changed / a.length;
198
346
  }
199
347
 
200
- module.exports = { act, screenSize, clickSimple, mouseMove };
348
+ module.exports = { act, screenSize, scaleArgs, bitmapSignature, signatureDiff, clickSimple, mouseMove };
@@ -155,11 +155,12 @@ const PERM_TOOL_SETS = {
155
155
  'web_search', 'http_fetch', 'webfetch', 'deep_search',
156
156
  'browser_open', 'browser_read', 'browser_snapshot', 'browser_screenshot',
157
157
  'browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select',
158
+ 'browser_wait',
158
159
  'ocr_read', 'tool_request',
159
160
  ]),
160
161
  read: new Set([
161
162
  'web_search', 'http_fetch', 'webfetch', 'deep_search',
162
- 'browser_open', 'browser_read', 'browser_snapshot',
163
+ 'browser_open', 'browser_read', 'browser_snapshot', 'browser_wait',
163
164
  'list_dir', 'read_file', 'grep', 'glob',
164
165
  'git_diff_review', 'repo_map', 'repo_symbols', 'xlsx_read',
165
166
  'tool_request',
@@ -191,6 +192,7 @@ const CEO_EXEC_TOOLS = new Set([
191
192
  'web_search', 'http_fetch', 'webfetch', 'deep_search',
192
193
  'browser_open', 'browser_read', 'browser_screenshot', 'browser_snapshot',
193
194
  'browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select',
195
+ 'browser_wait',
194
196
  'computer_look', 'computer_act',
195
197
  'ocr_read',
196
198
  'channel_send', // dış kişilere mesaj: CEO devreder, kendisi atmaz
@@ -1426,10 +1428,10 @@ class Engine {
1426
1428
  return out.slice(0, 100);
1427
1429
  }
1428
1430
 
1429
- /* Beast Code panel oturumları: bgTitle 'Beast Code' olan gizli oturumlar.
1430
- Soldaki dosya panelinin sohbet geçmişi listesi bunları gösterir —
1431
- ana sohbet listesinden (listSessions) TAMAMEN AYRIDIR. */
1432
- listBcSessions(limit = 100) {
1431
+ /* Panel oturumları: belirli bir bgTitle taşıyan gizli oturumlar (Beast Code,
1432
+ Beast MCP…) — ana sohbet listesinden (listSessions) TAMAMEN AYRIDIR. */
1433
+ _listTitledSessions(title, limit = 100) {
1434
+ const wanted = String(title || '');
1433
1435
  let files = [];
1434
1436
  try {
1435
1437
  files = fs.readdirSync(this.sessionsDir).filter((f) => f.endsWith('.jsonl'));
@@ -1439,7 +1441,7 @@ class Engine {
1439
1441
  const id = f.replace(/\.jsonl$/, '');
1440
1442
  let s;
1441
1443
  try { s = this._load(id); } catch { continue; }
1442
- if (s.bgTitle !== 'Beast Code') continue; // yalnız Beast Code oturumları
1444
+ if (s.bgTitle !== wanted) continue;
1443
1445
  let title = '';
1444
1446
  for (const m of s.messages) {
1445
1447
  if (m.role !== 'user') continue;
@@ -1451,7 +1453,7 @@ class Engine {
1451
1453
  }
1452
1454
  out.push({
1453
1455
  id,
1454
- title: title || 'Beast Code oturumu',
1456
+ title: title || wanted + ' oturumu',
1455
1457
  updatedAt: s.updatedAt,
1456
1458
  createdAt: s.createdAt,
1457
1459
  count: s.messages.length,
@@ -1462,6 +1464,16 @@ class Engine {
1462
1464
  return out.slice(0, Math.max(1, Math.min(200, Number(limit) || 100)));
1463
1465
  }
1464
1466
 
1467
+ /* Beast Code panel oturumları: soldaki dosya panelinin geçmiş listesi gösterir */
1468
+ listBcSessions(limit = 100) {
1469
+ return this._listTitledSessions('Beast Code', limit);
1470
+ }
1471
+
1472
+ /* Beast MCP sohbet oturumları: MCP modundaki sağ sohbet paneli gösterir */
1473
+ listMcpSessions(limit = 100) {
1474
+ return this._listTitledSessions('Beast MCP', limit);
1475
+ }
1476
+
1465
1477
  createSession() {
1466
1478
  const id = uid();
1467
1479
  const code = this._newCode();
@@ -2125,6 +2137,7 @@ class Engine {
2125
2137
  [
2126
2138
  'Basit soruları araç kullanmadan doğrudan cevapla — hız önceliklidir.',
2127
2139
  'Kod/dosya işlerinde: içerik araması grep (regex), dosya adı araması glob, VAR OLAN dosyayı değiştirme edit_file (write_file yalnız yeni dosya/tam yeniden yazım). read_file satır numaralı döner; büyük dosyada devamını offset parametresiyle oku, ASLA baştan okuma; bir dosyayı aynı oturumda BİR KEZ okumak yeter — okunan içerik oturum sonuna kadar bağlamda kalır, dosyayı tekrar okuma. Dosya işlemlerinde özel araçları kullan (edit_file/write_file/read_file/grep/glob); run_command terminal işlerindir (build, git, kurulum, paket) — dosya düzenlemeyi komutla değil edit_file ile yap. edit_file/write_file sonucu additions/deletions döner ve değişiklik diske ANINDA uygulanır — doğrulamak için dosyayı TEKRAR OKUMA; önceki okuduğun içerik + kendi değişikliklerin üzerinden devam et.',
2140
+ 'TERMİNAL DİSİPLİNİ: run_command komutları ETKİLEŞİMSİZDİR — REPL/editör/pager (`node`, `python`, `vi`, `notepad`) ve girdi bekleyen komutlar (mesajsız `git commit`, -y\'siz `npm init`, BatchMode\'suz `ssh`) otomatik reddedilir ya da kilitlenmeden kesilir. Etkileşimsiz bayrakları kullan (-y, -m, --yes, --accept-*), uzun betikleri dosyaya yazıp çalıştır. Uzun süreli sunucuları (`npm run dev/start`, `vite`, `uvicorn`, `flask run`...) run_command ile BAŞLATMA — 10 sn içinde kesilir; Sandbox\'ta panel_run, diğer yerlerde arka plan (`Start-Process ... -RedirectStandardOutput log.txt`) kullan, çıktıyı sonra dosyadan oku.',
2128
2141
  'Kullanıcı bir tarihte/saatte hatırlatılmasını isterse set_reminder kullan; when değerini ORTAMdaki bugüne göre hesapla (yerel saat). "Her sabah/gün/hafta" gibi tekrarlı isteklerde repeat alanını da ver (daily/weekly/monthly/weekdays veya cron).',
2129
2142
  'İLK MESAJ (channel_send): kullanıcı "X kişiye yaz / haber ver / duyur" derse channel_send ile SEN ilk mesajı at — karşıdan mesaj gelmesini BEKLEME. Yalnız allow listteki kişiler hedeflenebilir: isim (kısmi), telefon numarası, Telegram/Discord ID, @kullanıcı adı, "sahip" ya da "all". Kişi listede yoksa gönderme, kullanıcıya söyle. Telegram/Discord\'da karşı taraf bota daha önce hiç yazmadıysa gönderim başarısız olabilir; sonucu dürüstçe bildir.',
2130
2143
  'Kullanıcı kalıcı bir arka plan takibi isterse (fiyat eşiği, pil seviyesi, sayfa değişikliği) watcher_add ile izleyici kur; kurduktan sonra watcher_list ile doğrula ve kullanıcıya koşulu + kontrol sıklığını kısaca bildir.',
@@ -3711,6 +3724,7 @@ class Engine {
3711
3724
  `Ortam: Windows + PowerShell; çalışma klasörü: ${this.workspace}\n` +
3712
3725
  `GENEL AJAN DİSİPLİNİ: görevi A'dan Z'ye yürüt — bağlam topla, uygula, DOĞRULA; yarım bırakma; doğrulama sonucunu rapora yaz.\n` +
3713
3726
  `DOSYA KURALI: dosya işlemlerinde özel araçları kullan — VAR OLAN dosyayı edit_file ile düzenle, yeniyi write_file ile yaz, okuma/arama read_file/grep/glob; run_command terminal işlerindir (build, git, kurulum). Bir dosyayı BİR KEZ oku — içerik bağlamda kalır, tekrar okuma.\n` +
3727
+ `TERMİNAL DİSİPLİNİ: komutlar ETKİLEŞİMSİZ koşar — REPL/editör/pager ve girdi bekleyen komutlar (mesajsız git commit, -y'siz npm init) reddedilir/kesilir; -y, -m, --yes, --accept-* bayraklarını kullan. Uzun süreli sunucuları run_command ile BAŞLATMA (10 sn'de kesilir) — arka planda başlat (panel_run ya da Start-Process + çıktıyı log dosyasına yönlendir).\n` +
3714
3728
  (proj ? `PROJE TALİMATLARI (workspace AGENTS/CLAUDE/CONTEXT — daima uy):\n${proj}\n` : '') +
3715
3729
  `HIZ KURALLARI:\n` +
3716
3730
  `- TOOL İSTEĞİ (CEPHANE): ihtiyacın olan araç yoksa/bozuksa tool_request ile TOOL botuna yazdır (asenkron: istek anında döner, Tool botu arka planda yazar; araç doğrulanınca tool__<ad> olarak anında çağrılır, rapor sohbete düşer); küçük aracı skill("tool-yazma") ile kendin de yaz ve run_command ile doğrula.\n` +
@@ -5139,7 +5153,8 @@ const skills = require('./skills');
5139
5153
  if (!this.browser || typeof this.browser.screenshot !== 'function') {
5140
5154
  return { ok: false, error: 'dahili tarayıcı kullanılamıyor' };
5141
5155
  }
5142
- const shot = await this.browser.screenshot(signal, { sessionId });
5156
+ /* OCR düz görüntü ister — ref etiketleri metni kirletmesin */
5157
+ const shot = await this.browser.screenshot(signal, { sessionId, annotate: false });
5143
5158
  if (!shot || !shot.ok) return { ok: false, error: (shot && shot.error) || 'görüntü alınamadı' };
5144
5159
  image = shot.__injectImage || shot.image || null;
5145
5160
  } else if (src === 'screen') {
@@ -5147,7 +5162,8 @@ const skills = require('./skills');
5147
5162
  if (!this.computer || typeof this.computer.look !== 'function') {
5148
5163
  return { ok: false, error: 'ekran erişimi yok' };
5149
5164
  }
5150
- const shot = await this.computer.look();
5165
+ /* OCR düz görüntü ister — ızgara/imleç etiketleri OCR'a girmesin */
5166
+ const shot = await this.computer.look({ annotate: false });
5151
5167
  image = typeof shot === 'string' ? shot : (shot && (shot.image || shot.dataUrl)) || null;
5152
5168
  } else {
5153
5169
  const abs = path.isAbsolute(src) ? src : path.join(this.workspace, src);
@@ -5737,12 +5753,14 @@ const skills = require('./skills');
5737
5753
  return JSON.stringify({ ok: false, error: 'ekran erişimi yok' });
5738
5754
  }
5739
5755
  emitSafe(this, sessionId, { type: 'status', status: 'ekrana bakıyor' });
5740
- const shot = await this.computer.look();
5756
+ const shot = await this.computer.look({ annotate: true });
5741
5757
  if (!shot) return JSON.stringify({ ok: false, error: 'ekran görüntüsü alınamadı' });
5742
5758
  /* görsel sonraki tura vision mesajı olarak enjekte edilir */
5743
5759
  return JSON.stringify({
5744
5760
  ok: true,
5745
- note: 'ekran görüntüsü alındı — görsel aşağıda; koordinatlar 1280x720 tabanlı',
5761
+ note:
5762
+ 'ekran görüntüsü alındı — görsel aşağıda; 128px ızgara + kırmızı imleç işareti çizili (etiketli koordinatlar görüntü tabanlı). ' +
5763
+ 'computer_act koordinatları 1280px görüntü tabanlı alır ve gerçek ekrana otomatik çevirir',
5746
5764
  __injectImage: shot,
5747
5765
  });
5748
5766
  }
@@ -5817,6 +5835,13 @@ const skills = require('./skills');
5817
5835
  const r = await this.browser.act(name.slice(8), args, signal, { sessionId });
5818
5836
  return JSON.stringify(r);
5819
5837
  }
5838
+ if (name === 'browser_wait') {
5839
+ if (!this.browser || typeof this.browser.wait !== 'function') {
5840
+ return JSON.stringify({ ok: false, error: 'dahili tarayıcı kullanılamıyor' });
5841
+ }
5842
+ const r = await this.browser.wait(args || {}, signal, { sessionId });
5843
+ return JSON.stringify(r);
5844
+ }
5820
5845
  /* Beast Apps: app__<id>__<tool> → apps host'a dispatch */
5821
5846
  if (String(name).startsWith('app__')) {
5822
5847
  return JSON.stringify(await apps.call(name, args || {}, signal));
@@ -6403,7 +6428,7 @@ const TOOLS = [
6403
6428
  function: {
6404
6429
  name: 'computer_look',
6405
6430
  description:
6406
- 'Take a screenshot of the user\u2019s screen and receive it as an image. Use before computer_act to see the GUI; coordinates are on a 1280x720 basis.',
6431
+ 'Take a screenshot of the user\u2019s screen and receive it as an image. Use before computer_act to see the GUI; coordinates are on a 1280px-wide image basis — computer_act converts them to real screen coordinates automatically (multi-monitor offsets included).',
6407
6432
  parameters: { type: 'object', properties: {} },
6408
6433
  },
6409
6434
  },
@@ -6412,19 +6437,23 @@ const TOOLS = [
6412
6437
  function: {
6413
6438
  name: 'computer_act',
6414
6439
  description:
6415
- 'Control mouse/keyboard on the user\u2019s Windows desktop (GUI automation). Ops: click{x,y}, dblclick{x,y}, rightclick{x,y}, move{x,y}, type{text}, key{combo e.g. "ctrl+s","enter","alt+tab"}, scroll{x,y,dy}. Look first with computer_look, act step by step, look again after acting.',
6440
+ 'Control mouse/keyboard on the user\u2019s Windows desktop (GUI automation). Ops: click{x,y}, dblclick{x,y}, rightclick{x,y}, move{x,y}, hover{x,y} (opens hover menus), drag{x,y,x2,y2}, focus{title} (bring a window whose title contains the text to front), type{text}, key{combo e.g. "ctrl+s","enter","alt+tab"}, scroll{x,y,dy}. Coordinates are 1280px-wide image based (as seen in computer_look) — converted automatically. After click/type/key/drag the response reports changed/changeRatio (visual verification); if changed:false a warning means the action probably had no effect — do NOT assume success, re-check with computer_look / focus the right window. Look first with computer_look, act step by step, look again after acting.',
6416
6441
  parameters: {
6417
6442
  type: 'object',
6418
6443
  properties: {
6419
6444
  op: {
6420
6445
  type: 'string',
6421
- enum: ['click', 'dblclick', 'rightclick', 'move', 'type', 'key', 'scroll'],
6446
+ enum: ['click', 'dblclick', 'rightclick', 'move', 'hover', 'drag', 'focus', 'type', 'key', 'scroll'],
6422
6447
  },
6423
6448
  x: { type: 'number' },
6424
6449
  y: { type: 'number' },
6450
+ x2: { type: 'number', description: 'for op=drag: end x' },
6451
+ y2: { type: 'number', description: 'for op=drag: end y' },
6452
+ title: { type: 'string', description: 'for op=focus: window title substring (case-insensitive)' },
6425
6453
  text: { type: 'string', description: 'for op=type (max 2000 chars)' },
6426
6454
  combo: { type: 'string', description: 'for op=key: "enter", "ctrl+s", "alt+tab", "win"' },
6427
6455
  dy: { type: 'number', description: 'for op=scroll: positive = down (-10..10)' },
6456
+ verify: { type: 'boolean', description: 'set false to skip the visual change check' },
6428
6457
  },
6429
6458
  required: ['op'],
6430
6459
  },
@@ -6537,12 +6566,13 @@ const TOOLS = [
6537
6566
  function: {
6538
6567
  name: 'browser_click',
6539
6568
  description:
6540
- 'Click an element in the built-in browser. Prefer ref from the latest snapshot (e.g. {"ref":3}); CSS selector or text=X also accepted. The response includes a FRESH snapshot with new refs — continue with those directly instead of calling browser_snapshot again.',
6569
+ 'Click an element in the built-in browser. Prefer ref from the latest snapshot (e.g. {"ref":3}); CSS selector or text=X also accepted. The response includes a FRESH snapshot with new refs — continue with those directly instead of calling browser_snapshot again. It also reports changed/changeRatio: if changed is false the click probably had no effect (stale ref/hidden element) — take a fresh snapshot and retry, do NOT assume success.',
6541
6570
  parameters: {
6542
6571
  type: 'object',
6543
6572
  properties: {
6544
6573
  ref: { type: 'number', description: 'element number from browser_snapshot' },
6545
6574
  selector: { type: 'string', description: 'CSS selector or text=X alternative' },
6575
+ verify: { type: 'boolean', description: 'set false to skip the visual change check' },
6546
6576
  },
6547
6577
  },
6548
6578
  },
@@ -6552,7 +6582,7 @@ const TOOLS = [
6552
6582
  function: {
6553
6583
  name: 'browser_type',
6554
6584
  description:
6555
- 'Type text into an input/textarea/contenteditable in the built-in browser (prefer ref). Set submit=true to press Enter afterwards. Date/time fields (input type=date/time/month/datetime-local) are set PROGRAMMATICALLY — just send the date as text in any common format ("2026-03-15", "15.03.2026", "15 Mart 2026"); do NOT click the calendar popup. The response includes a FRESH snapshot with new refs.',
6585
+ 'Type text into an input/textarea/contenteditable in the built-in browser (prefer ref). Set submit=true to press Enter afterwards. Date/time fields (input type=date/time/month/datetime-local) are set PROGRAMMATICALLY — just send the date as text in any common format ("2026-03-15", "15.03.2026", "15 Mart 2026"); do NOT click the calendar popup. The response includes a FRESH snapshot with new refs and changed/changeRatio (visual verification).',
6556
6586
  parameters: {
6557
6587
  type: 'object',
6558
6588
  properties: {
@@ -6560,6 +6590,7 @@ const TOOLS = [
6560
6590
  selector: { type: 'string' },
6561
6591
  text: { type: 'string' },
6562
6592
  submit: { type: 'boolean' },
6593
+ verify: { type: 'boolean', description: 'set false to skip the visual change check' },
6563
6594
  },
6564
6595
  required: ['text'],
6565
6596
  },
@@ -6570,12 +6601,13 @@ const TOOLS = [
6570
6601
  function: {
6571
6602
  name: 'browser_press',
6572
6603
  description:
6573
- 'Press a key (Enter, Tab, Escape, ArrowDown…) on the focused element in the built-in browser; optionally focus a ref first. The response includes a FRESH snapshot with new refs.',
6604
+ 'Press a key (Enter, Tab, Escape, ArrowDown…) on the focused element in the built-in browser; optionally focus a ref first. The response includes a FRESH snapshot with new refs and changed/changeRatio (visual verification).',
6574
6605
  parameters: {
6575
6606
  type: 'object',
6576
6607
  properties: {
6577
6608
  key: { type: 'string' },
6578
6609
  ref: { type: 'number', description: 'optional element to focus first' },
6610
+ verify: { type: 'boolean', description: 'set false to skip the visual change check' },
6579
6611
  },
6580
6612
  required: ['key'],
6581
6613
  },
@@ -6602,18 +6634,38 @@ const TOOLS = [
6602
6634
  function: {
6603
6635
  name: 'browser_select',
6604
6636
  description:
6605
- 'Pick an <option> of a dropdown (<select>) in the built-in browser by value/text (prefer ref). The response includes a FRESH snapshot with new refs. For CUSTOM (JS) dropdowns that are not <select>, click the trigger, then click the [role=option] ref from the snapshot (popups are listed first).',
6637
+ 'Pick an <option> of a dropdown (<select>) in the built-in browser by value/text (prefer ref). The response includes a FRESH snapshot with new refs and changed/changeRatio (visual verification). For CUSTOM (JS) dropdowns that are not <select>, click the trigger, then click the [role=option] ref from the snapshot (popups are listed first).',
6606
6638
  parameters: {
6607
6639
  type: 'object',
6608
6640
  properties: {
6609
6641
  ref: { type: 'number' },
6610
6642
  selector: { type: 'string' },
6611
6643
  value: { type: 'string' },
6644
+ verify: { type: 'boolean', description: 'set false to skip the visual change check' },
6612
6645
  },
6613
6646
  required: ['value'],
6614
6647
  },
6615
6648
  },
6616
6649
  },
6650
+ {
6651
+ type: 'function',
6652
+ function: {
6653
+ name: 'browser_wait',
6654
+ description:
6655
+ 'Wait in the built-in browser until a condition is met (or timeout): selector appears (or disappears with gone:true), visible text appears, or a ref becomes valid; ms only = fixed sleep. Use this on SPAs / slow pages AFTER an action instead of blind retries — then act on the fresh snapshot. Timeout default 10000ms (max 30000).',
6656
+ parameters: {
6657
+ type: 'object',
6658
+ properties: {
6659
+ selector: { type: 'string', description: 'CSS selector to wait for' },
6660
+ text: { type: 'string', description: 'visible page text/substring to wait for' },
6661
+ ref: { type: 'number', description: 'ref from snapshot that should become valid' },
6662
+ gone: { type: 'boolean', description: 'wait until selector/text/ref DISAPPEARS instead' },
6663
+ ms: { type: 'number', description: 'fixed wait in ms (max 10000); used alone or as extra delay' },
6664
+ timeout_ms: { type: 'number', description: 'condition timeout, default 10000 (max 30000)' },
6665
+ },
6666
+ },
6667
+ },
6668
+ },
6617
6669
  ];
6618
6670
 
6619
6671
  /* ---------- GÖREV (TODO) ID + GERİ ALMA SİSTEMİ ----------
@@ -60,7 +60,9 @@ After ensuring proper quoting, execute the command.
60
60
  - If the commands depend on each other and must run sequentially, avoid '&&' in this shell because Windows PowerShell (5.1) does not support it. Use PowerShell conditionals such as \`cmd1; if ($?) { cmd2 }\` when later commands must depend on earlier success.
61
61
  - DO NOT use newlines to separate commands (newlines are ok in quoted strings)
62
62
  - The shell is PERSISTENT: working directory and environment variables are preserved between calls.
63
- - Commands run in Windows PowerShell (5.1).`;
63
+ - Commands run in Windows PowerShell (5.1).
64
+ - Commands are NON-INTERACTIVE: no REPLs/editors/pagers (\`node\`, \`python\`, \`vi\`, \`notepad\`), no input-waiting commands (\`git commit\` without -m, \`npm init\` without -y, \`ssh\` without BatchMode). They are rejected or aborted to avoid stalling. Use non-interactive flags (-y, -m, --yes) and write scripts to files instead.
65
+ - NEVER start long-running servers (npm run dev/start, vite, uvicorn, flask run...) with this tool — they are aborted after 10s. Use panel_run (run panel) or a background process (Start-Process with output redirected to a log file).`;
64
66
  }
65
67
 
66
68
  /* opencode tool şemaları — Schema.Struct karşılığı JSON Schema'lar */