beast-agent 1.2.2 → 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 +1 -1
- package/src/agent/engine.js +69 -37
- package/src/agent/tools.js +28 -4
- package/src/main.js +92 -9
- package/src/renderer/i18n.js +4 -0
- package/src/renderer/index.html +2 -0
- package/src/renderer/renderer.js +44 -4
- package/src/renderer/style.css +20 -0
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -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 =
|
|
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 —
|
|
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/
|
|
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.',
|
|
@@ -1616,16 +1620,27 @@ class Engine {
|
|
|
1616
1620
|
}
|
|
1617
1621
|
return m;
|
|
1618
1622
|
});
|
|
1623
|
+
/* opencode file-state portu: read_file sonuçları bağlamda YAŞAR —
|
|
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ı */
|
|
1619
1629
|
for (let k = 0; k < flat.length; k++) {
|
|
1620
1630
|
const m = flat[k];
|
|
1621
1631
|
if (m.role !== 'tool') continue;
|
|
1622
|
-
const
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1632
|
+
const len = String(m.content || '').length;
|
|
1633
|
+
let content;
|
|
1634
|
+
if (m.name === 'read_file') {
|
|
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 */
|
|
1636
|
+
} else {
|
|
1637
|
+
content = len > TOOL_OUT_KEEP * 2 ? String(m.content).slice(0, TOOL_OUT_KEEP) + '\n…[kırpıldı]' : null;
|
|
1638
|
+
}
|
|
1639
|
+
/* diffView UI-only metadata'dır — sağlayıcıya GİTMEZ */
|
|
1640
|
+
if (content !== null || m.diffView) {
|
|
1626
1641
|
const rest = { ...m };
|
|
1627
1642
|
delete rest.diffView;
|
|
1628
|
-
if (
|
|
1643
|
+
if (content !== null) rest.content = content;
|
|
1629
1644
|
flat[k] = rest;
|
|
1630
1645
|
}
|
|
1631
1646
|
}
|
|
@@ -1870,10 +1885,28 @@ class Engine {
|
|
|
1870
1885
|
const msgs = session.messages;
|
|
1871
1886
|
let total = 0, pruned = 0, turns = 0;
|
|
1872
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
|
+
}
|
|
1873
1902
|
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
1874
1903
|
const m = msgs[i];
|
|
1875
1904
|
if (m.role === 'user') turns++;
|
|
1876
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ı
|
|
1877
1910
|
if (m.role === 'tool' && typeof m.content === 'string' && m.content.length > 400) {
|
|
1878
1911
|
const est = Math.ceil(estTokens(m.content) * this.tokRatio);
|
|
1879
1912
|
total += est;
|
|
@@ -2662,6 +2695,7 @@ class Engine {
|
|
|
2662
2695
|
(job.task ? `GÖREV: ${job.task}\n` : '') +
|
|
2663
2696
|
`Ortam: Windows + PowerShell; çalışma klasörü: ${this.workspace}\n` +
|
|
2664
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` +
|
|
2665
2699
|
(proj ? `PROJE TALİMATLARI (workspace AGENTS/CLAUDE/CONTEXT — daima uy):\n${proj}\n` : '') +
|
|
2666
2700
|
`HIZ KURALLARI:\n` +
|
|
2667
2701
|
`- Döngülü işleri (çok URL/dosya/sayfa, tekrarlı parse-hesap) TEK python_run betiğinde topluca bitir.\n` +
|
|
@@ -2676,7 +2710,7 @@ class Engine {
|
|
|
2676
2710
|
|
|
2677
2711
|
/* uzun bg işlerinde geçmiş şişer → payload büyür, hız düşer. Son N mesaj kalsın
|
|
2678
2712
|
(+ asıl görev mesajı); kopuk tool çifti sınırı hizalanır */
|
|
2679
|
-
static BG_KEEP_MSGS =
|
|
2713
|
+
static BG_KEEP_MSGS = 48;
|
|
2680
2714
|
_bgTrim(session) {
|
|
2681
2715
|
const max = Engine.BG_KEEP_MSGS;
|
|
2682
2716
|
const msgs = session.messages;
|
|
@@ -2911,17 +2945,23 @@ class Engine {
|
|
|
2911
2945
|
let nudged = false; // görev listesi disiplini: run başına en fazla 1 hatırlatma
|
|
2912
2946
|
let wrapNudged = false; // tur limitine yaklaşınca zarif kapanış (bg ajanlar)
|
|
2913
2947
|
let usageTotal = 0; // en yüksek görülen total_tokens — compaction tetiği
|
|
2914
|
-
let
|
|
2915
|
-
/* 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. */
|
|
2916
2954
|
const runDef = this._agentDefFor(session, !!session.bgJob);
|
|
2917
|
-
const maxTurns =
|
|
2955
|
+
const maxTurns = session.bgJob
|
|
2956
|
+
? Math.max(2, Math.min(60, (runDef && runDef.steps) || MAX_TURNS))
|
|
2957
|
+
: Infinity;
|
|
2918
2958
|
const recentSigs = []; // doom-loop dedektörü: son araç imzaları
|
|
2919
2959
|
/* Beast Code canlı önizleme: run boyunca üretilen eserleri topla —
|
|
2920
2960
|
iş bitince dahili tarayıcıda CANLI açılır (site/dev server/HTML) */
|
|
2921
2961
|
const bcArtifacts = session.bcCode ? { html: [], serverUrl: null } : null;
|
|
2922
2962
|
for (let turn = 0; turn < maxTurns; turn++) {
|
|
2923
|
-
/* SÜRE SINIRI YOK
|
|
2924
|
-
"raporu yaz ve bitir" uyarısı alı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. */
|
|
2925
2965
|
if (session.bgJob && turn === maxTurns - 3 && !wrapNudged) {
|
|
2926
2966
|
wrapNudged = true;
|
|
2927
2967
|
const wmsg = {
|
|
@@ -2935,26 +2975,16 @@ class Engine {
|
|
|
2935
2975
|
} catch {}
|
|
2936
2976
|
emit({ type: 'message', message: wmsg });
|
|
2937
2977
|
}
|
|
2938
|
-
/* opencode port (prompt.ts:
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
const wmsg = {
|
|
2943
|
-
role: 'user',
|
|
2944
|
-
content:
|
|
2945
|
-
'[SON TUR] Tur limitine ulaşıldı: YENİ araç çağırma — eldeki bilgilerle NİHAİ cevabını şimdi ver. Eksikler varsa açıkça belirt.',
|
|
2946
|
-
};
|
|
2947
|
-
session.messages.push(wmsg);
|
|
2948
|
-
try {
|
|
2949
|
-
this._append(session, wmsg);
|
|
2950
|
-
} catch {}
|
|
2951
|
-
emit({ type: 'message', message: wmsg });
|
|
2952
|
-
}
|
|
2953
|
-
/* opencode port (prompt.ts:1161 + overflow.ts): bağlam dolduysa
|
|
2954
|
-
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. */
|
|
2955
2982
|
const selNow = this.sessionModel.get(String(sid)) || this.modelFor(null) || this.sel;
|
|
2956
|
-
if (
|
|
2957
|
-
|
|
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;
|
|
2958
2988
|
await this._compactHistory(session, selNow, ctrl.signal);
|
|
2959
2989
|
}
|
|
2960
2990
|
this._bgTrim(session); // #21 bg geçmişini olabildiğince ince tut
|
|
@@ -3112,7 +3142,7 @@ class Engine {
|
|
|
3112
3142
|
}
|
|
3113
3143
|
if (injectedImage || diffView) out = JSON.stringify(obj);
|
|
3114
3144
|
} catch {}
|
|
3115
|
-
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) };
|
|
3116
3146
|
if (diffView) toolMsg.diffView = diffView; /* oturum dosyasına da düşer — geçmiş açılınca diff yeniden çizilir */
|
|
3117
3147
|
session.messages.push(toolMsg);
|
|
3118
3148
|
try {
|
|
@@ -3134,8 +3164,10 @@ class Engine {
|
|
|
3134
3164
|
})
|
|
3135
3165
|
);
|
|
3136
3166
|
}
|
|
3167
|
+
/* yalnız bg ajanlar tavanla buraya düşebilir — ana oturum sınırsız koşar */
|
|
3137
3168
|
emit({ type: 'done', usage: null });
|
|
3138
|
-
this._bgFinish(sid, 'error', 'tur
|
|
3169
|
+
this._bgFinish(sid, 'error', 'bg görevi tur limitine ulaştı (maxTurns)');
|
|
3170
|
+
} catch (e) {
|
|
3139
3171
|
const aborted = e && (e.name === 'AbortError' || ctrl.signal.aborted);
|
|
3140
3172
|
if (aborted) {
|
|
3141
3173
|
this._clearCrash(); // kullanıcı durdurdu — kurtarma yok
|
package/src/agent/tools.js
CHANGED
|
@@ -1302,13 +1302,32 @@ function diffRegion(before, after) {
|
|
|
1302
1302
|
return { before: capDiffText(beforeRegion), after: capDiffText(afterRegion), startLine: start + 1 };
|
|
1303
1303
|
}
|
|
1304
1304
|
|
|
1305
|
+
/* ---------- read_file disk-cache (opencode file-state portu) ----------
|
|
1306
|
+
mtime+size değişmemişse dosya yeniden diskten OKUNMAZ — cache'ten döner.
|
|
1307
|
+
edit_file/write_file kendi yazdıklarından sonra cache'i düşürür; böylece
|
|
1308
|
+
sonraki okuma daima güncel içerik verir. */
|
|
1309
|
+
const _readCache = new Map(); /* abs → { mtimeMs, size, raw } */
|
|
1310
|
+
|
|
1311
|
+
function readCacheGet(abs, st) {
|
|
1312
|
+
const hit = _readCache.get(abs);
|
|
1313
|
+
if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size) return hit.raw;
|
|
1314
|
+
const raw = fs.readFileSync(abs, 'utf8');
|
|
1315
|
+
if (_readCache.size > 200) _readCache.delete(_readCache.keys().next().value);
|
|
1316
|
+
_readCache.set(abs, { mtimeMs: st.mtimeMs, size: st.size, raw });
|
|
1317
|
+
return raw;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
function readCacheDrop(abs) {
|
|
1321
|
+
_readCache.delete(abs);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1305
1324
|
const definitions = [
|
|
1306
1325
|
{
|
|
1307
1326
|
type: 'function',
|
|
1308
1327
|
function: {
|
|
1309
1328
|
name: 'run_command',
|
|
1310
1329
|
description:
|
|
1311
|
-
'
|
|
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.',
|
|
1312
1331
|
parameters: {
|
|
1313
1332
|
type: 'object',
|
|
1314
1333
|
properties: {
|
|
@@ -1335,7 +1354,8 @@ const definitions = [
|
|
|
1335
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' +
|
|
1336
1355
|
'- Any line longer than 2000 characters is truncated.\n' +
|
|
1337
1356
|
'- Call this tool in parallel when you know there are multiple files you want to read.\n' +
|
|
1338
|
-
'- 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.',
|
|
1339
1359
|
parameters: {
|
|
1340
1360
|
type: 'object',
|
|
1341
1361
|
properties: {
|
|
@@ -1383,7 +1403,8 @@ const definitions = [
|
|
|
1383
1403
|
'- The edit will FAIL if `old_string` is not found in the file with an error "oldString not found in content".\n' +
|
|
1384
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' +
|
|
1385
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' +
|
|
1386
|
-
'- 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.',
|
|
1387
1408
|
parameters: {
|
|
1388
1409
|
type: 'object',
|
|
1389
1410
|
properties: {
|
|
@@ -1558,6 +1579,7 @@ async function exec(name, args, ctx) {
|
|
|
1558
1579
|
}
|
|
1559
1580
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
1560
1581
|
fs.writeFileSync(filePath, newS, 'utf8');
|
|
1582
|
+
readCacheDrop(filePath);
|
|
1561
1583
|
const counts = diffLineCounts('', newS);
|
|
1562
1584
|
const out = { ok: true, path: filePath, note: 'Edit applied successfully.', replacements: 1, ...counts };
|
|
1563
1585
|
if (ctx.wantDiff) out.diffView = { path: filePath, before: '', after: capDiffText(newS), startLine: 1, ...counts };
|
|
@@ -1573,6 +1595,7 @@ async function exec(name, args, ctx) {
|
|
|
1573
1595
|
const rep = ocReplace(contentOld, oldN, newN, !!(args.replace_all || args.replaceAll));
|
|
1574
1596
|
const contentNew = rep.result;
|
|
1575
1597
|
fs.writeFileSync(filePath, contentNew, 'utf8');
|
|
1598
|
+
readCacheDrop(filePath); /* sonraki read_file taze içerik okusun */
|
|
1576
1599
|
const counts = diffLineCounts(contentOld, contentNew);
|
|
1577
1600
|
const out = {
|
|
1578
1601
|
ok: true,
|
|
@@ -1612,7 +1635,7 @@ async function exec(name, args, ctx) {
|
|
|
1612
1635
|
return JSON.stringify({ ok: false, error: 'pdf okuma hata: ' + String((e2 && e2.message) || e2) });
|
|
1613
1636
|
}
|
|
1614
1637
|
}
|
|
1615
|
-
let raw =
|
|
1638
|
+
let raw = readCacheGet(abs, st);
|
|
1616
1639
|
/* opencode read.ts port: satır numaralı çıktı (`N: içerik`), offset/limit
|
|
1617
1640
|
penceresi (1-indexed), 2000 satır varsayılan, uzun satır kırpma */
|
|
1618
1641
|
const allLines = raw.split('\n');
|
|
@@ -1661,6 +1684,7 @@ async function exec(name, args, ctx) {
|
|
|
1661
1684
|
const before = existed && !fs.statSync(abs).isDirectory() ? fs.readFileSync(abs, 'utf8') : '';
|
|
1662
1685
|
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
1663
1686
|
fs.writeFileSync(abs, content, 'utf8');
|
|
1687
|
+
readCacheDrop(abs); /* sonraki read_file taze içerik okusun */
|
|
1664
1688
|
const counts = diffLineCounts(before, content);
|
|
1665
1689
|
const out = { ok: true, path: abs, bytes: Buffer.byteLength(content), ...counts };
|
|
1666
1690
|
if (ctx.wantDiff) {
|
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
|
-
|
|
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
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5306
|
-
|
|
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);
|
package/src/renderer/i18n.js
CHANGED
|
@@ -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',
|
package/src/renderer/index.html
CHANGED
|
@@ -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">✕︎</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>▣︎</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>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -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', () =>
|
|
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();
|
package/src/renderer/style.css
CHANGED
|
@@ -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;
|