beast-agent 1.2.3 → 1.3.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": "1.2.3",
4
+ "version": "1.3.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -16,10 +16,14 @@ const skills = require('./skills');
16
16
  const { estTokens, estMsgTokens } = require('./tokens');
17
17
  const log = require('./logger');
18
18
 
19
- const MAX_TURNS = 25;
19
+ const MAX_TURNS = 40;
20
20
  const SUB_MAX_TURNS = 8;
21
21
  const HISTORY_TOKEN_BUDGET = 18000;
22
22
  const TOOL_OUT_KEEP = 1200;
23
+ /* read_file sonuçları dosya HAFIZASI'dır: 7200 char'a kırpma modeli aynı
24
+ dosyayı offset offset tekrar okuyor → tur limiti doluyordu. Son okuma
25
+ oturum sonuna kadar tam tutulur (yaklaşık 48k char ≈ orta boy kaynak dosya). */
26
+ const READ_OUT_KEEP = 48000;
23
27
  const USER_MAX = 8000;
24
28
  /* observe() ile enjekte edilen bağlam mesajlarının öneki — ardışık bağlam
25
29
  mesajlarının tek mesajda birleşmesi bu işaretle anlaşılır */
@@ -1385,9 +1389,9 @@ class Engine {
1385
1389
  ? '# PROJE TALİMATLARI (workspace AGENTS/CLAUDE/CONTEXT dosyalarından — daima uy)\n' + proj + '\n'
1386
1390
  : '') +
1387
1391
  'WORKFLOW (her işte bu sıra):\n' +
1388
- '1) BAĞLAM: değiştirmeden önce ilgili dosyaları OKU — read_file satır numaralı döner (N: içerik); büyük dosyada devamını offset parametresiyle oku, ASLA baştan okuma; bir dosyayı aynı oturumda BİR KEZ okumak yeter — içeriği sonraki turlarda da bağlamda KALIR, edit yaptıktan sonra dosyayı yeniden okuma (güncel durum = son okuma + kendi editlerin). İçerik araması için grep (regex), dosya adı için glob kullan; varsayım yapma, mevcut stili/konvansiyonu takip et.\n' +
1392
+ '1) BAĞLAM: değiştirmeden önce ilgili dosyaları OKU — read_file satır numaralı döner (N: içerik); büyük dosyada devamını offset parametresiyle oku, ASLA baştan okuma; bir dosyayı aynı oturumda BİR KEZ okumak yeter — okuduğun içerik oturum SONUNA KADAR bağlamda KALIR, edit yaptıktan sonra dosyayı yeniden okuma YASAK (güncel durum = son okuma + kendi editlerin). İçerik araması için grep (regex), dosya adı için glob kullan; varsayım yapma, mevcut stili/konvansiyonu takip et.\n' +
1389
1393
  '2) PLAN: 2+ adımlı işlerde İLK EYLEM todo_write olsun (2-6 madde); her adım bitince status:"done" ile GÜNCELLE — liste bitene kadar iş bitmiş sayılmaz.\n' +
1390
- '3) EDİT: VAR OLAN dosyada önce edit_file kullan (old_string/new_string ile yalnız ilgili bölümü değiştir; birden çok eşleşme varsa bağlam ekle ya da replace_all); write_file yalnız YENİ dosya ya da tam yeniden yazım için. İlgisiz yeniden biçimleme/kayıt boşluk değişikliği YAPMA. edit_file/write_file sonucu additions/deletions döner ve değişiklik diske ANINDA uygulanır — doğrulamak için dosyayı TEKRAR OKUMA YASAK; sonraki editi önceki okuduğun içerik + kendi değişikliklerin üzerinden zincirle.\n' +
1394
+ '3) EDİT: VAR OLAN dosyada önce edit_file kullan (old_string/new_string ile yalnız ilgili bölümü değiştir; birden çok eşleşme varsa bağlam ekle ya da replace_all); write_file yalnız YENİ dosya ya da tam yeniden yazım için. Dosya işlemleri için ÖZEL ARAÇLARI kullan (edit_file/write_file/read_file/grep/glob); run_command terminal işlerindir (build, git, kurulum, paket) — dosya düzenlemeyi komut/scripte yedirme, edit_file ile yap. İlgisiz yeniden biçimleme/kayıp boşluk değişikliği YAPMA. edit_file/write_file sonucu additions/deletions döner ve değişiklik diske ANINDA uygulanır — doğrulamak için dosyayı TEKRAR OKUMA YASAK; sonraki editi önceki okuduğun içerik + kendi değişikliklerin üzerinden zincirle.\n' +
1391
1395
  '4) DOĞRULA: edit sonrası mümkünse derle/test et/lint çalıştır (run_command); hata varsa DÜZELT ve TEKRAR dene (en fazla 2 doğrulama turu) — kırmızı bırakma. Doğrulama read_file ile DEĞİL run_command ile yapılır.\n' +
1392
1396
  '5) RAPOR: 1-3 satır — ne değişti + doğrulama sonucu (ör. "npm test ✓ 154/154"). Uzun açıklama yok.\n' +
1393
1397
  'CANLI ÖNİZLEME: web sitesi/app üretirsen son adımda ÇALIŞIR halde bırak (dev server ayakta ya da index.html yazılmış) — panel iş bitince ürettiğin şeyi dahili tarayıcıda otomatik canlı açar.\n' +
@@ -1478,13 +1482,13 @@ class Engine {
1478
1482
  '# ARAÇ KURALLARI\n' +
1479
1483
  [
1480
1484
  'Basit soruları araç kullanmadan doğrudan cevapla — hız önceliklidir.',
1481
- '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. 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.',
1485
+ '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.',
1482
1486
  '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).',
1483
1487
  '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.',
1484
1488
  'Anlık olay takipleri için (yeni mail, fiyat eşiği, dosya değişimi, webhook) event_subscribe kullan — cron/polling gerekmez; listeyi event_list ile göster, vazgeçirirse event_unsubscribe.',
1485
1489
  'Kullanıcı "artık hep böyle yap / bunu unutma" tarzı kalıcı talimat verirse kural olarak kaydet: sohbette /rule <metin> kullanmasını söyle VEYA kullanıcı isterse event_subscribe ile olaya bağlan (mail/fiyat/dosya/webhook).',
1486
1490
  'Kullanıcının mesajında 2+ ayrı iş/hedef varsa (örn "X yap ve sonra Y\u2019i kontrol et") KODLAMAYA/İŞE BAŞLAMADAN önce todo_write ile plan çıkar ve sırayla yürüt; her adımı tamamlarken güncelle. LİSTE DİSİPLİNİ: her adım bittiği AN status:"done" yap; son cevabını vermeden önce tüm maddeler done olmalı — yapılmayacaksa listeden düş. Listeyi yarım bırakma.',
1487
- 'HIZ KURALI: Bağımsız işleri AYNI turda birden çok tool_calls ile PARALEL ver. Küçük işleri tek tek çağırma — her ayrı araç turu 5-15 sn LLM gecikmesidir: 3+ küçük komutu TEK run_command\u2019te `;` ile zincirle (örn `git status; node -v; dir`), döngülü/çoklu işleri TEK python_run betiğinde topla, çok dosyalık değişikliği TEK script ile yap. Her küçük işlem için ayrı araç çağrısı açmak yavaşlığın 1 numaralı sebebidir.',
1491
+ 'HIZ KURALI: Bağımsız işleri AYNI turda birden çok tool_calls ile PARALEL ver. Küçük işleri tek tek çağırma — her ayrı araç turu 5-15 sn LLM gecikmesidir: 3+ küçük komutu TEK run_command\u2019te `;` ile zincirle (örn `git status; node -v; dir`), döngülü/çoklu işleri TEK python_run betiğinde topla, çok dosyalık değişikliği TEK script ile DEĞİL AYNI turda PARALEL edit_file çağrılarıyla yap (dosya düzenlemeyi komut/scripte yedirme). Her küçük işlem için ayrı araç çağrısı açmak yavaşlığın 1 numaralı sebebidir.',
1488
1492
  this.ceoMode
1489
1493
  ? 'Bağımsız alt-işleri run_background ile PARALEL ajana devret; işi KENDİN YÜRÜTME — emri ver, takip et, raporla.'
1490
1494
  : 'Bağımsız alt-işleri delegate_task ile devret; kendi başına halledebileceğin işleri devretme.',
@@ -1617,42 +1621,18 @@ class Engine {
1617
1621
  return m;
1618
1622
  });
1619
1623
  /* opencode file-state portu: read_file sonuçları bağlamda YAŞAR —
1620
- 1200 char'a kırpma modeli "unuttu" diye tekrar tekrar okumaya
1621
- zorluyordu. Her dosyanın yalnız EN SON okuması tam tutulur (diskte
1622
- zaten TOOL_OUT_KEEP*6 ile sınırlı); daha eski okumalar stub'a iner. */
1623
- const READ_KEEP = TOOL_OUT_KEEP * 6; /* depolama tavanıyla hizalı */
1624
- const readPathOf = (m) => {
1625
- const s = String(m.content || '');
1626
- try {
1627
- const j = JSON.parse(s);
1628
- if (j && j.path) return String(j.path);
1629
- } catch {}
1630
- const mm = /"path"\s*:\s*"((?:[^"\\]|\\.)*)"/.exec(s.slice(0, 600));
1631
- return mm ? mm[1] : null;
1632
- };
1633
- const lastRead = new Map(); /* dosya yolu → flat'teki en son okuma indeksi */
1634
- for (let k = 0; k < flat.length; k++) {
1635
- const m = flat[k];
1636
- if (m.role === 'tool' && m.name === 'read_file') {
1637
- const p = readPathOf(m);
1638
- if (p) lastRead.set(p, k);
1639
- }
1640
- }
1624
+ kırpma modeli "unuttu" diye tekrar tekrar okumaya zorluyordu.
1625
+ opencode'ta araç çıktıları tam tutulur; eski okumalar stub'a inmez
1626
+ bütçe dolunca en eski birimler doğal olarak pencere dışı kalır
1627
+ (opencode prune mantığıyla aynı). Okumalar READ_OUT_KEEP ile sınırlı. */
1628
+ const READ_KEEP = READ_OUT_KEEP; /* depolama tavanıyla hizalı */
1641
1629
  for (let k = 0; k < flat.length; k++) {
1642
1630
  const m = flat[k];
1643
1631
  if (m.role !== 'tool') continue;
1644
1632
  const len = String(m.content || '').length;
1645
1633
  let content;
1646
1634
  if (m.name === 'read_file') {
1647
- const p = readPathOf(m);
1648
- if (p && lastRead.get(p) !== k) {
1649
- content =
1650
- '[aynı dosyanın daha YENİ bir okuması bağlamda — bu ESKİ okuma bağlam tasarrufu için kırpıldı; en son okumayı esas al]';
1651
- } else if (len > READ_KEEP) {
1652
- content = String(m.content).slice(0, READ_KEEP) + '\n…[kırpıldı — devam için offset ile oku]';
1653
- } else {
1654
- content = null; /* TAM KORUNUR — kırpma yok */
1655
- }
1635
+ content = len > READ_KEEP ? String(m.content).slice(0, READ_KEEP) + '\n…[kırpıldı — devam için offset ile oku]' : null; /* TAM KORUNUR */
1656
1636
  } else {
1657
1637
  content = len > TOOL_OUT_KEEP * 2 ? String(m.content).slice(0, TOOL_OUT_KEEP) + '\n…[kırpıldı]' : null;
1658
1638
  }
@@ -1905,10 +1885,28 @@ class Engine {
1905
1885
  const msgs = session.messages;
1906
1886
  let total = 0, pruned = 0, turns = 0;
1907
1887
  const targets = [];
1888
+ /* her dosyanın EN SON read_file sonucu asla prune edilmez — dosya
1889
+ hafızası oturum sonuna kadar yaşar; eski okumalar serbest */
1890
+ const readPathOf = (m) => {
1891
+ const s = String(m.content || '');
1892
+ const mm = /"path"\s*:\s*"((?:[^"\\]|\\.)*)"/.exec(s.slice(0, 600));
1893
+ return mm ? mm[1] : null;
1894
+ };
1895
+ const lastReadId = new Map();
1896
+ for (const m of msgs) {
1897
+ if (m.role === 'tool' && m.name === 'read_file' && m.tool_call_id) {
1898
+ const p = readPathOf(m);
1899
+ if (p) lastReadId.set(p, m.tool_call_id);
1900
+ }
1901
+ }
1908
1902
  for (let i = msgs.length - 1; i >= 0; i--) {
1909
1903
  const m = msgs[i];
1910
1904
  if (m.role === 'user') turns++;
1911
1905
  if (turns < 2) continue; // en yeni tur dokunulmaz
1906
+ if (
1907
+ m.role === 'tool' && m.name === 'read_file' && m.tool_call_id &&
1908
+ readPathOf(m) && lastReadId.get(readPathOf(m)) === m.tool_call_id
1909
+ ) continue; // son dosya okuması korumalı
1912
1910
  if (m.role === 'tool' && typeof m.content === 'string' && m.content.length > 400) {
1913
1911
  const est = Math.ceil(estTokens(m.content) * this.tokRatio);
1914
1912
  total += est;
@@ -2697,6 +2695,7 @@ class Engine {
2697
2695
  (job.task ? `GÖREV: ${job.task}\n` : '') +
2698
2696
  `Ortam: Windows + PowerShell; çalışma klasörü: ${this.workspace}\n` +
2699
2697
  `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` +
2698
+ `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` +
2700
2699
  (proj ? `PROJE TALİMATLARI (workspace AGENTS/CLAUDE/CONTEXT — daima uy):\n${proj}\n` : '') +
2701
2700
  `HIZ KURALLARI:\n` +
2702
2701
  `- Döngülü işleri (çok URL/dosya/sayfa, tekrarlı parse-hesap) TEK python_run betiğinde topluca bitir.\n` +
@@ -2711,7 +2710,7 @@ class Engine {
2711
2710
 
2712
2711
  /* uzun bg işlerinde geçmiş şişer → payload büyür, hız düşer. Son N mesaj kalsın
2713
2712
  (+ asıl görev mesajı); kopuk tool çifti sınırı hizalanır */
2714
- static BG_KEEP_MSGS = 24;
2713
+ static BG_KEEP_MSGS = 48;
2715
2714
  _bgTrim(session) {
2716
2715
  const max = Engine.BG_KEEP_MSGS;
2717
2716
  const msgs = session.messages;
@@ -2946,17 +2945,23 @@ class Engine {
2946
2945
  let nudged = false; // görev listesi disiplini: run başına en fazla 1 hatırlatma
2947
2946
  let wrapNudged = false; // tur limitine yaklaşınca zarif kapanış (bg ajanlar)
2948
2947
  let usageTotal = 0; // en yüksek görülen total_tokens — compaction tetiği
2949
- let compacted = false; // run başına en fazla 1 compaction
2950
- /* opencode agent.steps port: özel ajanın kendi tur limiti (2-60) */
2948
+ let lastCompactTurn = -99; // opencode: compaction run başına 1 DEĞİL her taşmada tekrar eder
2949
+ /* opencode agent.steps port: özel ajanın kendi tur limiti (2-60).
2950
+ ANA OTURUMDA SERT TUR LİMİTİ YOK (opencode birebir): bağlam dolunca
2951
+ compaction özetler ve işin ORTASINDA devam eder; sonsuz döngüyü
2952
+ doom-loop koruması + kullanıcı ■ kırar. bg ajanlarda kapanış
2953
+ disiplini için tavan kalır. */
2951
2954
  const runDef = this._agentDefFor(session, !!session.bgJob);
2952
- const maxTurns = Math.max(2, Math.min(60, (runDef && runDef.steps) || MAX_TURNS));
2955
+ const maxTurns = session.bgJob
2956
+ ? Math.max(2, Math.min(60, (runDef && runDef.steps) || MAX_TURNS))
2957
+ : Infinity;
2953
2958
  const recentSigs = []; // doom-loop dedektörü: son araç imzaları
2954
2959
  /* Beast Code canlı önizleme: run boyunca üretilen eserleri topla —
2955
2960
  iş bitince dahili tarayıcıda CANLI açılır (site/dev server/HTML) */
2956
2961
  const bcArtifacts = session.bcCode ? { html: [], serverUrl: null } : null;
2957
2962
  for (let turn = 0; turn < maxTurns; turn++) {
2958
- /* SÜRE SINIRI YOK ama sonsuz tur da yok: bg ajan son 3 tura gelirken
2959
- "raporu yaz ve bitir" uyarısı alır; maxTurns sert tavan olarak kalır. */
2963
+ /* SÜRE SINIRI YOK. Ana oturum sınırsız tur koşar (opencode birebir);
2964
+ yalnız bg ajan son 3 tura gelirken "raporu yaz ve bitir" uyarısı alır. */
2960
2965
  if (session.bgJob && turn === maxTurns - 3 && !wrapNudged) {
2961
2966
  wrapNudged = true;
2962
2967
  const wmsg = {
@@ -2970,26 +2975,16 @@ class Engine {
2970
2975
  } catch {}
2971
2976
  emit({ type: 'message', message: wmsg });
2972
2977
  }
2973
- /* opencode port (prompt.ts:1281): ana oturumda son turda zorunlu
2974
- nihai cevaptur limiti sessizce dolup boş 'done' dönmesin */
2975
- if (!session.bgJob && turn === maxTurns - 1 && !wrapNudged) {
2976
- wrapNudged = true;
2977
- const wmsg = {
2978
- role: 'user',
2979
- content:
2980
- '[SON TUR] Tur limitine ulaşıldı: YENİ araç çağırma — eldeki bilgilerle NİHAİ cevabını şimdi ver. Eksikler varsa açıkça belirt.',
2981
- };
2982
- session.messages.push(wmsg);
2983
- try {
2984
- this._append(session, wmsg);
2985
- } catch {}
2986
- emit({ type: 'message', message: wmsg });
2987
- }
2988
- /* opencode port (prompt.ts:1161 + overflow.ts): bağlam dolduysa
2989
- compaction checkpoint — kuyruk korunur, baş özetlenir */
2978
+ /* opencode port (prompt.ts:1161 + overflow.ts + processor.ts "compact"):
2979
+ bağlam dolduysa compaction kuyruk korunur, baş özetlenir ve işin
2980
+ ORTASINDA devam edilir. Run içinde TEKRAR EDELİR; taze compaction'dan
2981
+ hemen sonra hâlâ taşarsa opencode ContextOverflowError gibi güvenli dur. */
2990
2982
  const selNow = this.sessionModel.get(String(sid)) || this.modelFor(null) || this.sel;
2991
- if (!compacted && this._overContext(session, selNow, usageTotal)) {
2992
- compacted = true;
2983
+ if (this._overContext(session, selNow, usageTotal)) {
2984
+ if (turn - lastCompactTurn < 4) {
2985
+ throw new Error('ContextOverflow: compaction sonrası bağlam hâlâ model limitinin üstünde — oturum çok büyük');
2986
+ }
2987
+ lastCompactTurn = turn;
2993
2988
  await this._compactHistory(session, selNow, ctrl.signal);
2994
2989
  }
2995
2990
  this._bgTrim(session); // #21 bg geçmişini olabildiğince ince tut
@@ -3147,7 +3142,7 @@ class Engine {
3147
3142
  }
3148
3143
  if (injectedImage || diffView) out = JSON.stringify(obj);
3149
3144
  } catch {}
3150
- const toolMsg = { role: 'tool', tool_call_id: tc.id, name, content: out.slice(0, TOOL_OUT_KEEP * 6) };
3145
+ const toolMsg = { role: 'tool', tool_call_id: tc.id, name, content: out.slice(0, name === 'read_file' ? READ_OUT_KEEP : TOOL_OUT_KEEP * 6) };
3151
3146
  if (diffView) toolMsg.diffView = diffView; /* oturum dosyasına da düşer — geçmiş açılınca diff yeniden çizilir */
3152
3147
  session.messages.push(toolMsg);
3153
3148
  try {
@@ -3169,8 +3164,10 @@ class Engine {
3169
3164
  })
3170
3165
  );
3171
3166
  }
3167
+ /* yalnız bg ajanlar tavanla buraya düşebilir — ana oturum sınırsız koşar */
3172
3168
  emit({ type: 'done', usage: null });
3173
- this._bgFinish(sid, 'error', 'tur limiti doldu (maxTurns)'); } catch (e) {
3169
+ this._bgFinish(sid, 'error', 'bg görevi tur limitine ulaştı (maxTurns)');
3170
+ } catch (e) {
3174
3171
  const aborted = e && (e.name === 'AbortError' || ctrl.signal.aborted);
3175
3172
  if (aborted) {
3176
3173
  this._clearCrash(); // kullanıcı durdurdu — kurtarma yok
@@ -1327,7 +1327,7 @@ const definitions = [
1327
1327
  function: {
1328
1328
  name: 'run_command',
1329
1329
  description:
1330
- 'Run a PowerShell command on the user\'s Windows machine in the workspace directory and return combined stdout/stderr. Use for shell tasks, builds, git, installs, etc.',
1330
+ 'Executes a given PowerShell command on the user\'s Windows machine in the workspace directory and returns combined stdout/stderr. Use this tool for terminal operations like builds, git, npm installs, docker, etc. DO NOT use it for file operations (reading, writing, editing, searching, finding files) - use the specialized tools for this instead: read_file, write_file, edit_file, grep, glob.',
1331
1331
  parameters: {
1332
1332
  type: 'object',
1333
1333
  properties: {
@@ -1354,7 +1354,8 @@ const definitions = [
1354
1354
  '- Contents are returned with each line prefixed by its line number as `<line>: <content>`. For example, if a file has contents "foo\\n", you will receive "1: foo\\n".\n' +
1355
1355
  '- Any line longer than 2000 characters is truncated.\n' +
1356
1356
  '- Call this tool in parallel when you know there are multiple files you want to read.\n' +
1357
- '- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.',
1357
+ '- Avoid tiny repeated slices (30 line chunks). If you need more context, read a larger window.\n' +
1358
+ '- File contents you read STAY in your context for the WHOLE session. Do NOT re-read a file you already read: chain edits from your previous read plus your own edit_file/write_file results. Re-reading the same file (same or overlapping range) wastes turns and is forbidden unless the file actually changed on disk outside your own edits.',
1358
1359
  parameters: {
1359
1360
  type: 'object',
1360
1361
  properties: {
@@ -1402,7 +1403,8 @@ const definitions = [
1402
1403
  '- The edit will FAIL if `old_string` is not found in the file with an error "oldString not found in content".\n' +
1403
1404
  '- The edit will FAIL if `old_string` is found multiple times in the file with an error "Found multiple matches for oldString. Provide more surrounding lines in old_string to identify the correct match." Either provide a larger string with more surrounding context to make it unique or use `replace_all` to change every instance of `old_string`.\n' +
1404
1405
  '- Use `replace_all` for replacing and renaming strings across the file. This parameter is useful if you want to rename a variable for instance.\n' +
1405
- '- The result includes additions/deletions counts — the change is APPLIED to disk immediately; do NOT read the file again to verify.',
1406
+ '- The result includes additions/deletions counts — the change is APPLIED to disk immediately; do NOT read the file again to verify.\n' +
1407
+ '- Chain follow-up edits from what you already read plus your own previous edits — do NOT re-read the file between edits. Multiple edit_file calls to different files can be issued in PARALLEL in one turn.',
1406
1408
  parameters: {
1407
1409
  type: 'object',
1408
1410
  properties: {
package/src/main.js CHANGED
@@ -2439,6 +2439,7 @@ function reloadBackend() {
2439
2439
  emit: (ev) => {
2440
2440
  if (win && !win.isDestroyed()) win.webContents.send('agent:event', ev);
2441
2441
  flushDesktopOnDone(ev); /* biriken desktop mesajlarını sıraya bas */
2442
+ bcFlushOnDone(ev); /* Beast Code kuyruğunu iş bitiminde boşalt */
2442
2443
  /* WA canlı iş takibi: oturum bir WhatsApp sohbetine bağlıysa araç
2443
2444
  hareketlerini (terminal, web, dosya…) kısa satırla bildir.
2444
2445
  Spam olmasın: sohbet başına en az 7 sn'de bir tek satır. */
@@ -5266,6 +5267,66 @@ function bcPanelEvent(sid, ev) {
5266
5267
  } catch {}
5267
5268
  }
5268
5269
 
5270
+ /* Beast Code mesaj kuyruğu: ajan çalışırken gelen mesajlar birikir, iş bitince
5271
+ TEK pakette (metinler \n ile birleşik) gönderilir. Boştayken de kısa pencere
5272
+ (debounce) vardır — ard arda hızlı mesajlar tek işte birleşir. */
5273
+ const BC_DEBOUNCE_MS = 900;
5274
+ const bcQueue = new Map(); /* klasör yolu → { timer, msgs[] } */
5275
+
5276
+ function bcQueuePush(ws, text) {
5277
+ let q = bcQueue.get(ws);
5278
+ if (!q) {
5279
+ q = { timer: null, msgs: [] };
5280
+ bcQueue.set(ws, q);
5281
+ }
5282
+ q.msgs.push({ text });
5283
+ return q;
5284
+ }
5285
+
5286
+ function bcFlush(folder) {
5287
+ const q = bcQueue.get(folder);
5288
+ if (!q || !q.msgs.length) return;
5289
+ const s = bcGetSession(folder); /* oturum yoksa oluşturur */
5290
+ if (engine.isBusy(s.id)) return; /* hâlâ çalışıyor — done/error eventini bekle */
5291
+ let merged = '';
5292
+ for (const m of q.msgs) if (m.text) merged += (merged ? '\n' : '') + m.text;
5293
+ if (!merged.trim()) {
5294
+ bcQueue.delete(folder);
5295
+ clearTimeout(q.timer);
5296
+ return;
5297
+ }
5298
+ s.workspace = folder;
5299
+ s.bcCode = true;
5300
+ engine.cache.set(s.id, s);
5301
+ if (engine.send(s.id, merged)) {
5302
+ bcQueue.delete(folder);
5303
+ clearTimeout(q.timer);
5304
+ } else {
5305
+ /* yarış: az önce meşgul oldu — kuyruk korunur, kısa süre sonra tekrar denenir */
5306
+ q.retries = (q.retries || 0) + 1;
5307
+ if (q.retries > 5) {
5308
+ bcQueue.delete(folder);
5309
+ clearTimeout(q.timer);
5310
+ try {
5311
+ if (win && !win.isDestroyed()) {
5312
+ win.webContents.send('agent:event', { sessionId: s.id, type: 'error', error: 'Beast Code mesajı gönderilemedi — panele tekrar yaz' });
5313
+ }
5314
+ } catch {}
5315
+ return;
5316
+ }
5317
+ setTimeout(() => { try { bcFlush(folder); } catch {} }, 800);
5318
+ }
5319
+ }
5320
+
5321
+ function bcFlushOnDone(ev) {
5322
+ if (!ev || (ev.type !== 'done' && ev.type !== 'error') || !ev.sessionId) return;
5323
+ for (const [folder, sid] of bcSessions) {
5324
+ if (String(sid) === String(ev.sessionId) && bcQueue.has(folder)) {
5325
+ setTimeout(() => { try { bcFlush(folder); } catch {} }, 150);
5326
+ }
5327
+ }
5328
+ }
5329
+
5269
5330
  ipcMain.handle('beastcode:send', async (_e, payload) => {
5270
5331
  const text = String((payload && payload.msg) || '').trim();
5271
5332
  if (!text) return { ok: false, error: 'boş mesaj' };
@@ -5274,13 +5335,16 @@ ipcMain.handle('beastcode:send', async (_e, payload) => {
5274
5335
  const modeM = /^\/(plan|build|auto)\b/i.exec(text);
5275
5336
 
5276
5337
  if (!engine.publicState().hasModel) return { ok: false, error: 'model yok — Ayarlar → Provider sekmesinden ekle' };
5277
- if (engine.isBusy(bcSessions.get(ws))) return { ok: false, busy: true, error: 'önceki mesaj sürüyor ile durdurabilirsin' };
5338
+ /* oturumu HEMEN aç: cevap daima gerçek sessionId taşır renderer paneli bu id ile
5339
+ eşler; boşta/kuyrukta olsa bile id yoksa panel olayları eşleyemez ve ölü kalır */
5278
5340
  const s = bcGetSession(ws);
5279
5341
  s.workspace = ws; /* soldaki klasörde çalış */
5280
5342
  s.bcCode = true; /* todo disiplini + iş sonu hızlı kapanış (engine) */
5343
+ engine.cache.set(s.id, s);
5344
+ const busy = engine.isBusy(s.id);
5281
5345
  if (modeM) {
5346
+ /* mod değişimi anında uygulanır — meşgulken de (yalnız bayrak; engine çakışmaz) */
5282
5347
  s.bcMode = modeM[1].toLowerCase();
5283
- engine.cache.set(s.id, s);
5284
5348
  if (win && !win.isDestroyed()) {
5285
5349
  const body = modeM[1].toLowerCase() === 'plan'
5286
5350
  ? 'PLAN MODU — dosyaları okuyup inceler, KOD YAZMAZ; adım adım uygulama planı verir.'
@@ -5291,25 +5355,44 @@ ipcMain.handle('beastcode:send', async (_e, payload) => {
5291
5355
  }
5292
5356
  return { ok: true, sessionId: s.id, mode: s.bcMode };
5293
5357
  }
5294
- engine.cache.set(s.id, s);
5295
- const ok = engine.send(s.id, text);
5296
- if (!ok) return { ok: false, error: 'mesaj gönderilemedi' };
5297
- return { ok: true, sessionId: s.id };
5358
+ if (busy) {
5359
+ /* ajan çalışıyor → kuyruğa al; iş bitince (done/error) toplu gider */
5360
+ const q = bcQueuePush(ws, text);
5361
+ return { ok: true, queued: true, count: q.msgs.length, sessionId: s.id };
5362
+ }
5363
+ /* boşta: kısa pencere — hızlı ard arda mesajlar tek işte birleşir */
5364
+ const q = bcQueuePush(ws, text);
5365
+ clearTimeout(q.timer);
5366
+ q.timer = setTimeout(() => { try { bcFlush(ws); } catch {} }, BC_DEBOUNCE_MS);
5367
+ return { ok: true, sessionId: s.id, pending: true };
5298
5368
  });
5299
5369
 
5300
5370
  ipcMain.handle('beastcode:stop', async () => {
5301
5371
  const ws = ideRoot();
5302
5372
  const sid = bcSessions.get(ws);
5303
- if (!sid) return { ok: false };
5373
+ /* durdurma: bekleyen kuyruğu da boşalt (kullanıcı vazgeçti) */
5374
+ const q = bcQueue.get(ws);
5375
+ if (q) {
5376
+ clearTimeout(q.timer);
5377
+ bcQueue.delete(ws);
5378
+ }
5379
+ const wasBusy = sid ? engine.isBusy(sid) : false;
5304
5380
  let r = false;
5305
- try { r = engine.interrupt(sid, 'kullanıcı Beast Code panelinden ■ ile durdurdu'); } catch {}
5306
- return { ok: !!r };
5381
+ if (wasBusy) {
5382
+ try { r = engine.interrupt(sid, 'kullanıcı Beast Code panelinden ■ ile durdurdu'); } catch {}
5383
+ }
5384
+ return { ok: true, wasBusy, interrupted: r };
5307
5385
  });
5308
5386
 
5309
5387
  ipcMain.handle('beastcode:new', async () => {
5310
5388
  const ws = ideRoot();
5311
5389
  const sid = bcSessions.get(ws);
5312
5390
  if (sid && engine.isBusy(sid)) return { ok: false, error: 'mesaj sürüyor — önce ■ ile durdur' };
5391
+ const q = bcQueue.get(ws);
5392
+ if (q) {
5393
+ clearTimeout(q.timer);
5394
+ bcQueue.delete(ws);
5395
+ }
5313
5396
  if (sid) {
5314
5397
  try { engine.deleteSession(sid); } catch {}
5315
5398
  bcSessions.delete(ws);
@@ -509,6 +509,8 @@
509
509
  tipStore: 'Skills Store',
510
510
  tipGithub: 'GitHub Trending — repoları favorile, chate at, ajana araştır',
511
511
  tipIde: 'IDE Modu — dosyalar + chat + preview',
512
+ tipCodeHide: 'Editörü kapat',
513
+ tipCodeShow: 'Editörü göster',
512
514
  ide_files: 'DOSYALAR',
513
515
  ide_pick: 'Klasör seç',
514
516
  ide_refresh: 'Yenile',
@@ -1122,6 +1124,8 @@
1122
1124
  tipStore: 'Skills Store',
1123
1125
  tipGithub: 'GitHub Trending — favorite repos, send to chat, ask the agent',
1124
1126
  tipIde: 'IDE Mode — files + chat + preview',
1127
+ tipCodeHide: 'Hide editor',
1128
+ tipCodeShow: 'Show editor',
1125
1129
  ide_files: 'FILES',
1126
1130
  ide_pick: 'Choose folder',
1127
1131
  ide_refresh: 'Refresh',
@@ -111,6 +111,7 @@
111
111
  <div id="codeTabs"></div>
112
112
  <code id="codePath" title="">—</code>
113
113
  <button id="codeSave" title="Kaydet (Ctrl+S)">Kaydet</button>
114
+ <button id="codeHide" title="Editörü kapat" data-i18n-title="tipCodeHide">&#x2715;&#xFE0E;</button>
114
115
  </div>
115
116
  <div id="codeBody">
116
117
  <div id="codeGutter" aria-hidden="true"></div>
@@ -125,6 +126,7 @@
125
126
  <span id="bcTitle">BEAST CODE</span>
126
127
  <span id="bcCwd" title=""></span>
127
128
  <span class="file-spacer"></span>
129
+ <button id="codeShow" title="Editörü göster" data-i18n-title="tipCodeShow" hidden>&#x25A3;&#xFE0E;</button>
128
130
  <button id="bcNew" title="Yeni oturum" data-i18n-title="bc_new">+</button>
129
131
  <button id="bcClear" title="Çıktıyı temizle" data-i18n-title="bc_clear">Temizle</button>
130
132
  </div>
@@ -83,6 +83,8 @@ const els = {
83
83
  codeGutter: $('#codeGutter'),
84
84
  codePath: $('#codePath'),
85
85
  codeSave: $('#codeSave'),
86
+ codeHide: $('#codeHide'),
87
+ codeShow: $('#codeShow'),
86
88
  ideRow: $('#ideRow'),
87
89
  ideSplit: $('#ideSplit'),
88
90
  bcPanel: $('#bcPanel'),
@@ -4167,6 +4169,8 @@ function onEvent(ev) {
4167
4169
  break;
4168
4170
  }
4169
4171
  case 'status':
4172
+ /* kuyruktan tetiklenen yeni iş: kilitle kendiliğinden açılmışsa geri kapat */
4173
+ if (ev.status !== 'idle' && !busy) setBusy(true);
4170
4174
  if (!busy) break;
4171
4175
  setStatus(ev.status === 'idle' ? (busy ? 'düşünüyor…' : '') : ev.status === 'thinking' ? 'düşünüyor…' : ev.status + '…');
4172
4176
  break;
@@ -5922,12 +5926,29 @@ async function setIdeMode(on) {
5922
5926
  }
5923
5927
  if (on) {
5924
5928
  ideSplitRestore();
5929
+ setEditorHidden(localStorage.getItem('beast.editorHidden') === '1');
5925
5930
  await loadIdeTree();
5926
5931
  bcBanner();
5927
5932
  codeGutterRender(); /* dosya açık olmasa bile rakamlar görünür */
5928
5933
  }
5929
5934
  }
5930
5935
 
5936
+ /* ---------- editör panelini kapat/göster ----------
5937
+ Kapatınca kod bölmesi gider, Beast Code tam genişlik alır; tercih
5938
+ localStorage'da kalıcıdır. Dosya ağacından dosya açınca geri açılır. */
5939
+ function editorHiddenOn() {
5940
+ return document.body.classList.contains('editor-hidden');
5941
+ }
5942
+
5943
+ function setEditorHidden(h) {
5944
+ document.body.classList.toggle('editor-hidden', !!h);
5945
+ if (els.codeShow) els.codeShow.hidden = !h;
5946
+ try { localStorage.setItem('beast.editorHidden', h ? '1' : '0'); } catch {}
5947
+ }
5948
+
5949
+ if (els.codeHide) els.codeHide.addEventListener('click', () => setEditorHidden(true));
5950
+ if (els.codeShow) els.codeShow.addEventListener('click', () => setEditorHidden(false));
5951
+
5931
5952
  /* ---------- editor/chat ayırıcı (VS Code gibi sürükle-bırak) ----------
5932
5953
  Orantılı model: kaydedilen, Beast Code'un ideRow içindeki PAYI'dır (0-1).
5933
5954
  Tarayıcı açılınca/kapanınca/genişlik değişince row yeniden bölünür — editör
@@ -6215,6 +6236,7 @@ function codeActivate(i) {
6215
6236
  }
6216
6237
 
6217
6238
  async function codeOpen(rel) {
6239
+ if (editorHiddenOn()) setEditorHidden(false); /* dosya açılınca editör geri gelir */
6218
6240
  const idx = codeTabs.findIndex((t) => t.rel === rel);
6219
6241
  if (idx >= 0) {
6220
6242
  codeActivate(idx);
@@ -6737,6 +6759,7 @@ function bcIngest(ev) {
6737
6759
  bcRenderTodos(ev.todos);
6738
6760
  break;
6739
6761
  case 'tool-start':
6762
+ bcSetBusy(true); /* kuyruktan tetiklenen işte panel kendini kilitler */
6740
6763
  bcToolBoxStart(ev.callId, ev.name, ev.args);
6741
6764
  /* ajan dosya yazarsa/editlerse açık IDE sekmesini izlemeye al */
6742
6765
  if ((ev.name === 'write_file' || ev.name === 'edit_file') && ev.args && ev.args.path) codeWriteWatch.set(ev.callId, ev.args.path);
@@ -6793,7 +6816,9 @@ function bcIngest(ev) {
6793
6816
  bcStatusHide();
6794
6817
  bcRefreshPreview();
6795
6818
  } else {
6796
- /* düşünüyor… / araç adı / bağlam durumu — input üstünde canlı balon */
6819
+ /* düşünüyor… / araç adı / bağlam durumu — input üstünde canlı balon;
6820
+ kuyruktan tetiklenen yeni işte kilit geri kapanır */
6821
+ bcSetBusy(true);
6797
6822
  bcStatusShow(ev.status === 'thinking' ? 'düşünüyor…' : String(ev.status || ''));
6798
6823
  }
6799
6824
  break;
@@ -6827,10 +6852,17 @@ function bcRunCurrent() {
6827
6852
  if (r && r.ok && r.mode) {
6828
6853
  /* /plan · /build · /auto: mod değişimi — tur AÇILMAZ, kilit anında açılır
6829
6854
  (bilgi satırı bc-mode olayıyla düşer, başlık rozeti güncellenir) */
6830
- bcSessionId = r.sessionId;
6855
+ if (r.sessionId) bcSessionId = r.sessionId;
6831
6856
  bcSetBusy(false);
6857
+ } else if (r && r.ok && r.queued) {
6858
+ /* ajan çalışıyor → mesaj kuyrukta; iş bitince toplu gönderilir */
6859
+ if (r.sessionId) bcSessionId = r.sessionId;
6860
+ bcLine('t-dim', '⏳ kuyrukta (' + r.count + ') — iş bitince gönderilir');
6861
+ } else if (r && r.ok && r.pending) {
6862
+ /* boşta: kısa birleşme penceresi — aynı penceredeki mesajlar tek işte gider */
6863
+ if (r.sessionId) bcSessionId = r.sessionId;
6832
6864
  } else if (r && r.ok) {
6833
- bcSessionId = r.sessionId;
6865
+ if (r.sessionId) bcSessionId = r.sessionId;
6834
6866
  } else if (r && r.busy) {
6835
6867
  /* iş gerçekten sürüyor — kilit açılmaz, bilgi satırı düşer */
6836
6868
  bcLine('t-dim', (r && r.error) || 'mesaj sürüyor');
@@ -6851,7 +6883,15 @@ if (els.bcInput) {
6851
6883
  if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); bcRunCurrent(); }
6852
6884
  });
6853
6885
  }
6854
- if (els.bcStop) els.bcStop.addEventListener('click', () => beast.beastcodeStop().catch(() => {}));
6886
+ if (els.bcStop) els.bcStop.addEventListener('click', () => {
6887
+ beast.beastcodeStop().then((r) => {
6888
+ /* kuyrukta bekleyen mesajlar da silindi; engine boştaysa kilidi aç */
6889
+ if (r && r.ok && r.wasBusy === false) {
6890
+ bcSetBusy(false);
6891
+ bcStatusHide();
6892
+ }
6893
+ }).catch(() => {});
6894
+ });
6855
6895
  if (els.bcClear) els.bcClear.addEventListener('click', () => {
6856
6896
  els.bcOut.innerHTML = '';
6857
6897
  bcTools.clear();
@@ -2492,6 +2492,26 @@ body.ide-mode #codePane { display: flex; }
2492
2492
  background: var(--border);
2493
2493
  opacity: 0.55;
2494
2494
  }
2495
+ /* editör kapatılabilir: kod bölmesi + ayırıcı gizlenir, Beast Code tam genişlik */
2496
+ body.ide-mode.editor-hidden #codePane,
2497
+ body.ide-mode.editor-hidden #ideSplit { display: none; }
2498
+ body.ide-mode.editor-hidden #ideRow #bcPanel { flex: 1 1 auto; min-width: 0; }
2499
+ #codeHide {
2500
+ flex: none;
2501
+ width: 24px;
2502
+ height: 24px;
2503
+ padding: 0;
2504
+ border: 1px solid var(--border);
2505
+ border-radius: 6px;
2506
+ background: transparent;
2507
+ color: var(--muted);
2508
+ cursor: pointer;
2509
+ font-size: 11px;
2510
+ line-height: 1;
2511
+ }
2512
+ #codeHide:hover { color: var(--text); border-color: var(--accent); background: var(--panel2); }
2513
+ #bcHead #codeShow { width: 26px; padding: 0; }
2514
+ #codeShow[hidden] { display: none; }
2495
2515
  #codeTabsBar {
2496
2516
  flex: none;
2497
2517
  display: flex;