beast-agent 1.2.3 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/engine.js +59 -62
- package/src/agent/tools.js +5 -3
- 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 +147 -22
- package/src/renderer/style.css +32 -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.',
|
|
@@ -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
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
const
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
2959
|
-
"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. */
|
|
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:
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
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 (
|
|
2992
|
-
|
|
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
|
|
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
|
package/src/agent/tools.js
CHANGED
|
@@ -1327,7 +1327,7 @@ const definitions = [
|
|
|
1327
1327
|
function: {
|
|
1328
1328
|
name: 'run_command',
|
|
1329
1329
|
description:
|
|
1330
|
-
'
|
|
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
|
-
|
|
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'),
|
|
@@ -2471,7 +2473,7 @@ async function renderWaAllow() {
|
|
|
2471
2473
|
`<select class="perm-select" title="${_t('bot_bind_title')}">` +
|
|
2472
2474
|
`<option value="">${_t('bot_sel_empty')}</option>` +
|
|
2473
2475
|
botChoices
|
|
2474
|
-
.map((bb) => `<option value="${bb.id}" ${cur === bb.id ? 'selected' : ''}>${
|
|
2476
|
+
.map((bb) => `<option value="${bb.id}" ${cur === bb.id ? 'selected' : ''}>${escapeHtml(bb.name)}</option>`)
|
|
2475
2477
|
.join('') +
|
|
2476
2478
|
`</select>`;
|
|
2477
2479
|
wrap.innerHTML = '';
|
|
@@ -2652,7 +2654,7 @@ async function renderWaAllow() {
|
|
|
2652
2654
|
botSel.innerHTML =
|
|
2653
2655
|
`<option value="">${_t('bot_sel_empty')}</option>` +
|
|
2654
2656
|
botChoices
|
|
2655
|
-
.map((bb) => `<option value="${bb.id}">${
|
|
2657
|
+
.map((bb) => `<option value="${bb.id}">${escapeHtml(bb.name)}${bb.admin ? ' (admin)' : ''}</option>`)
|
|
2656
2658
|
.join('');
|
|
2657
2659
|
if (prev) botSel.value = prev; // render'lar arası seçim korunur
|
|
2658
2660
|
}
|
|
@@ -2817,7 +2819,7 @@ async function renderTgAllow() {
|
|
|
2817
2819
|
for (const bb of botChoices) {
|
|
2818
2820
|
const o = document.createElement('option');
|
|
2819
2821
|
o.value = bb.id;
|
|
2820
|
-
o.textContent =
|
|
2822
|
+
o.textContent = bb.name;
|
|
2821
2823
|
if ((typeof entry === 'object' && entry.bot_id) === bb.id) o.selected = true;
|
|
2822
2824
|
sel.appendChild(o);
|
|
2823
2825
|
}
|
|
@@ -2886,7 +2888,7 @@ async function renderTgAllow() {
|
|
|
2886
2888
|
botSel.innerHTML =
|
|
2887
2889
|
`<option value="">${_t('bot_sel_empty')}</option>` +
|
|
2888
2890
|
botChoices
|
|
2889
|
-
.map((bb) => `<option value="${bb.id}">${
|
|
2891
|
+
.map((bb) => `<option value="${bb.id}">${escapeHtml(bb.name)}${bb.admin ? ' (admin)' : ''}</option>`)
|
|
2890
2892
|
.join('');
|
|
2891
2893
|
if (prev) botSel.value = prev;
|
|
2892
2894
|
}
|
|
@@ -3015,7 +3017,7 @@ async function renderDcAllow() {
|
|
|
3015
3017
|
for (const bb of botChoices) {
|
|
3016
3018
|
const o = document.createElement('option');
|
|
3017
3019
|
o.value = bb.id;
|
|
3018
|
-
o.textContent =
|
|
3020
|
+
o.textContent = bb.name;
|
|
3019
3021
|
if ((typeof entry === 'object' && entry.bot_id) === bb.id) o.selected = true;
|
|
3020
3022
|
sel.appendChild(o);
|
|
3021
3023
|
}
|
|
@@ -3082,7 +3084,7 @@ async function renderDcAllow() {
|
|
|
3082
3084
|
botSel.innerHTML =
|
|
3083
3085
|
`<option value="">${_t('bot_sel_empty')}</option>` +
|
|
3084
3086
|
botChoices
|
|
3085
|
-
.map((bb) => `<option value="${bb.id}">${
|
|
3087
|
+
.map((bb) => `<option value="${bb.id}">${escapeHtml(bb.name)}${bb.admin ? ' (admin)' : ''}</option>`)
|
|
3086
3088
|
.join('');
|
|
3087
3089
|
if (prev) botSel.value = prev;
|
|
3088
3090
|
}
|
|
@@ -3116,7 +3118,86 @@ async function renderDcAllow() {
|
|
|
3116
3118
|
Sol panel altında bot kartları; her bot için sohbet geçmişi + sekmeli yönetim.
|
|
3117
3119
|
İlk bot hep Beast (admin, silinemez). Max 5 bot. */
|
|
3118
3120
|
|
|
3119
|
-
|
|
3121
|
+
/* Bot ikonları: tek renkli SVG (stroke: currentColor — monokrom temaya uyar).
|
|
3122
|
+
Anahtar isimler data-ic/bot.icon olarak saklanır; eski botlardaki emoji
|
|
3123
|
+
LEGACY_BOT_ICONS ile otomatik yeni ikona eşlenir (görüntüde de, seçicide de). */
|
|
3124
|
+
const BOT_ICONS = ['lion', 'rocket', 'diamond', 'star', 'flame', 'calc', 'chart', 'robot', 'fox', 'panda', 'target', 'wrench'];
|
|
3125
|
+
const BOT_ICON_PATHS = {
|
|
3126
|
+
lion:
|
|
3127
|
+
'<circle cx="12" cy="12" r="7.2"/>' +
|
|
3128
|
+
'<path d="M12 4.8V2.6M12 21.4v-2.2M4.8 12H2.6M21.4 12h-2.2M6.8 6.8 5.2 5.2M18.8 5.2l-1.6 1.6M6.8 17.2l-1.6 1.6M18.8 18.8l-1.6-1.6"/>' +
|
|
3129
|
+
'<path d="M9.2 10.6h.01M14.8 10.6h.01M12 13.2l-1.2 1.4h2.4z"/>',
|
|
3130
|
+
rocket:
|
|
3131
|
+
'<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/>' +
|
|
3132
|
+
'<path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/>' +
|
|
3133
|
+
'<path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/>' +
|
|
3134
|
+
'<path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>',
|
|
3135
|
+
diamond:
|
|
3136
|
+
'<path d="M6 3h12l4 6-10 13L2 9z"/>' +
|
|
3137
|
+
'<path d="M11 3 8 9l4 13 4-13-3-6"/>' +
|
|
3138
|
+
'<path d="M2 9h20"/>',
|
|
3139
|
+
star: '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>',
|
|
3140
|
+
flame:
|
|
3141
|
+
'<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/>',
|
|
3142
|
+
calc:
|
|
3143
|
+
'<rect width="16" height="20" x="4" y="2" rx="2"/>' +
|
|
3144
|
+
'<line x1="8" x2="16" y1="6" y2="6"/>' +
|
|
3145
|
+
'<path d="M16 14h.01M16 18h.01M12 10h.01M12 14h.01M12 18h.01M8 10h.01M8 14h.01M8 18h.01"/>',
|
|
3146
|
+
chart:
|
|
3147
|
+
'<path d="M3 3v18h18"/>' +
|
|
3148
|
+
'<path d="M18 17V9"/>' +
|
|
3149
|
+
'<path d="M13 17V5"/>' +
|
|
3150
|
+
'<path d="M8 17v-3"/>',
|
|
3151
|
+
robot:
|
|
3152
|
+
'<path d="M12 8V4H8"/>' +
|
|
3153
|
+
'<rect width="16" height="12" x="4" y="8" rx="2"/>' +
|
|
3154
|
+
'<path d="M2 14h2"/>' +
|
|
3155
|
+
'<path d="M20 14h2"/>' +
|
|
3156
|
+
'<path d="M15 13v2"/>' +
|
|
3157
|
+
'<path d="M9 13v2"/>',
|
|
3158
|
+
fox:
|
|
3159
|
+
'<path d="M5 4l4 3a6.5 6.5 0 0 1 6 0l4-3v7.5c0 4.6-3.1 8.5-7 8.5s-7-3.9-7-8.5z"/>' +
|
|
3160
|
+
'<path d="M9.4 12.8h.01"/>' +
|
|
3161
|
+
'<path d="M14.6 12.8h.01"/>' +
|
|
3162
|
+
'<path d="M12 15.6l-1.1 1.2h2.2z"/>',
|
|
3163
|
+
panda:
|
|
3164
|
+
'<circle cx="12" cy="13" r="7.4"/>' +
|
|
3165
|
+
'<circle cx="5.2" cy="6" r="2.1"/>' +
|
|
3166
|
+
'<circle cx="18.8" cy="6" r="2.1"/>' +
|
|
3167
|
+
'<ellipse cx="9.4" cy="12.6" rx="1.4" ry="1.8" transform="rotate(-14 9.4 12.6)"/>' +
|
|
3168
|
+
'<ellipse cx="14.6" cy="12.6" rx="1.4" ry="1.8" transform="rotate(14 14.6 12.6)"/>' +
|
|
3169
|
+
'<path d="M12 15.8v1.2"/>' +
|
|
3170
|
+
'<path d="M10.8 18.2c.7.6 1.7.6 2.4 0"/>',
|
|
3171
|
+
target:
|
|
3172
|
+
'<circle cx="12" cy="12" r="9"/>' +
|
|
3173
|
+
'<circle cx="12" cy="12" r="5"/>' +
|
|
3174
|
+
'<circle cx="12" cy="12" r="1.5"/>',
|
|
3175
|
+
wrench:
|
|
3176
|
+
'<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>',
|
|
3177
|
+
};
|
|
3178
|
+
const LEGACY_BOT_ICONS = {
|
|
3179
|
+
'🦁': 'lion', '🚀': 'rocket', '💎': 'diamond', '⭐': 'star', '🔥': 'flame', '🧮': 'calc',
|
|
3180
|
+
'📊': 'chart', '🤖': 'robot', '🦊': 'fox', '🐼': 'panda', '🎯': 'target', '🛠️': 'wrench', '🛠': 'wrench',
|
|
3181
|
+
};
|
|
3182
|
+
|
|
3183
|
+
/* ikon anahtarını normalize et: eski emoji → yeni anahtar; bilinmiyorsa '' */
|
|
3184
|
+
function normIconKey(icon) {
|
|
3185
|
+
const k = String(icon || '').trim();
|
|
3186
|
+
return LEGACY_BOT_ICONS[k] || (BOT_ICON_PATHS[k] ? k : '');
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
/* bot ikonu HTML'i: bilinen anahtar → monokrom SVG; eski/emoji → düz metin (geriye uyum) */
|
|
3190
|
+
function botIconSvg(icon, size = 15) {
|
|
3191
|
+
let key = normIconKey(icon);
|
|
3192
|
+
if (!key && !String(icon || '').trim()) key = 'robot';
|
|
3193
|
+
if (!key) return escapeHtml(String(icon));
|
|
3194
|
+
return (
|
|
3195
|
+
`<svg class="bot-svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" ` +
|
|
3196
|
+
'stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">' +
|
|
3197
|
+
BOT_ICON_PATHS[key] +
|
|
3198
|
+
'</svg>'
|
|
3199
|
+
);
|
|
3200
|
+
}
|
|
3120
3201
|
const BOT_SKILLS = [
|
|
3121
3202
|
['email', 'E-posta'],
|
|
3122
3203
|
['browser', 'Dahili tarayıcı'],
|
|
@@ -3162,7 +3243,7 @@ function renderBotCards() {
|
|
|
3162
3243
|
row.className = 'bot-card' + (b.id === activeBotId ? ' active' : '');
|
|
3163
3244
|
row.title = _t('bot_switch_hint');
|
|
3164
3245
|
row.innerHTML =
|
|
3165
|
-
`<span class="bot-ico">${b.icon
|
|
3246
|
+
`<span class="bot-ico">${botIconSvg(b.icon)}</span>` +
|
|
3166
3247
|
`<span class="bot-nm">${escapeHtml(b.name)}</span>` +
|
|
3167
3248
|
`<span class="bot-gear" title="${_t('bot_manage')}">⚙</span>` +
|
|
3168
3249
|
`<span class="bot-tag">${b.admin ? 'ADMIN' : 'BOT'}</span>` +
|
|
@@ -3181,8 +3262,8 @@ function renderBotCards() {
|
|
|
3181
3262
|
function updateBotChip() {
|
|
3182
3263
|
const chip = $('#botChip');
|
|
3183
3264
|
if (!chip) return;
|
|
3184
|
-
const b = botsCache.find((x) => x.id === activeBotId) || { icon: '
|
|
3185
|
-
chip.
|
|
3265
|
+
const b = botsCache.find((x) => x.id === activeBotId) || { icon: 'lion', name: 'Beast' };
|
|
3266
|
+
chip.innerHTML = botIconSvg(b.icon, 14) + ' ' + escapeHtml(b.name);
|
|
3186
3267
|
}
|
|
3187
3268
|
|
|
3188
3269
|
/* BOTLAR ARASI GEÇİŞ: kart tıklaması SADECE aktif botu değiştirir —
|
|
@@ -3214,7 +3295,7 @@ async function switchBot(id) {
|
|
|
3214
3295
|
} catch {}
|
|
3215
3296
|
/* picker, aktif botun kendi modelini göstersin */
|
|
3216
3297
|
applyState();
|
|
3217
|
-
toast(
|
|
3298
|
+
toast(b.name + (b.admin ? '' : ' — ' + _t('bot_switched')));
|
|
3218
3299
|
}
|
|
3219
3300
|
|
|
3220
3301
|
function botOverlaySetOpen(open) {
|
|
@@ -3258,7 +3339,7 @@ function renderBotPage() {
|
|
|
3258
3339
|
}
|
|
3259
3340
|
const b = botsCache.find((x) => x.id === botPageId);
|
|
3260
3341
|
if (!b) { botPageId = null; renderBotPage(); return; }
|
|
3261
|
-
head.querySelector('#botHeadIcon').
|
|
3342
|
+
head.querySelector('#botHeadIcon').innerHTML = botIconSvg(b.icon, 22);
|
|
3262
3343
|
head.querySelector('#botHeadName').textContent = b.name;
|
|
3263
3344
|
$('#botHeadTag').textContent = b.admin ? 'ADMIN' : 'BOT';
|
|
3264
3345
|
/* DM Log sekmesi yalnız admin botta görünür — botlar arası tüm trafiği o izler */
|
|
@@ -3329,7 +3410,7 @@ async function renderBotNotes(pane, b) {
|
|
|
3329
3410
|
const card = document.createElement('div');
|
|
3330
3411
|
card.className = 'notes-card';
|
|
3331
3412
|
card.innerHTML =
|
|
3332
|
-
`<div class="notes-head"><div class="skill-name">${
|
|
3413
|
+
`<div class="notes-head"><div class="skill-name">${botIconSvg(b.icon)} ${escapeHtml(n.code || '?')} — ${escapeHtml(n.title || '')}</div>` +
|
|
3333
3414
|
`<div class="notes-meta">${escapeHtml(when)} · ${n.count || 0} ${_t('bot_stat_msgs')}</div></div>` +
|
|
3334
3415
|
`<div class="notes-body">${escapeHtml(String(n.notes || '').slice(0, 4000))}</div>` +
|
|
3335
3416
|
`<div style="margin-top:8px"><button class="btn ghost notes-del">${_t('notes_del')}</button></div>`;
|
|
@@ -3352,7 +3433,7 @@ function renderBotOverview(pane) {
|
|
|
3352
3433
|
const row = document.createElement('div');
|
|
3353
3434
|
row.className = 'bot-ov-row';
|
|
3354
3435
|
row.innerHTML =
|
|
3355
|
-
`<span class="ico">${b.icon
|
|
3436
|
+
`<span class="ico">${botIconSvg(b.icon, 17)}</span>` +
|
|
3356
3437
|
`<span class="nm">${escapeHtml(b.name)}${b.admin ? ' <span class="bot-tag">ADMIN</span>' : ''}</span>` +
|
|
3357
3438
|
`<span class="m">${_t('bot_stat_numbers')}: ${st.numbers || 0}</span>` +
|
|
3358
3439
|
`<span class="m">${_t('bot_stat_sessions')}: ${st.sessions || 0}</span>` +
|
|
@@ -3427,10 +3508,13 @@ function renderBotSettings(pane, b) {
|
|
|
3427
3508
|
pane.innerHTML = '';
|
|
3428
3509
|
const f = document.createElement('div');
|
|
3429
3510
|
f.className = 'bot-form';
|
|
3430
|
-
const iconBtns = BOT_ICONS.map(
|
|
3511
|
+
const iconBtns = BOT_ICONS.map(
|
|
3512
|
+
(ic) =>
|
|
3513
|
+
`<button type="button" data-ic="${ic}" title="${ic}" class="${normIconKey(b.icon) === ic ? 'on' : ''}">${botIconSvg(ic, 16)}</button>`
|
|
3514
|
+
).join('');
|
|
3431
3515
|
const otherBots = botsCache.filter((x) => x.id !== b.id);
|
|
3432
3516
|
const seeChecks = otherBots
|
|
3433
|
-
.map((x) => `<label><input type="checkbox" data-see="${x.id}" ${((b.seeBots || []).includes(x.id)) ? 'checked' : ''}/> ${x.icon} ${escapeHtml(x.name)}</label>`)
|
|
3517
|
+
.map((x) => `<label><input type="checkbox" data-see="${x.id}" ${((b.seeBots || []).includes(x.id)) ? 'checked' : ''}/> ${botIconSvg(x.icon)} ${escapeHtml(x.name)}</label>`)
|
|
3434
3518
|
.join('') || `<span class="sub">${_t('bot_no_other')}</span>`;
|
|
3435
3519
|
const skillChecks = BOT_SKILLS
|
|
3436
3520
|
.map(([k, lbl]) => `<label><input type="checkbox" data-skill="${k}" ${(b.skills || {})[k] ? 'checked' : ''}/> ${lbl}</label>`)
|
|
@@ -3661,7 +3745,10 @@ function renderBotAdd(pane) {
|
|
|
3661
3745
|
pane.innerHTML = '';
|
|
3662
3746
|
const f = document.createElement('div');
|
|
3663
3747
|
f.className = 'bot-form';
|
|
3664
|
-
const iconBtns = BOT_ICONS.map(
|
|
3748
|
+
const iconBtns = BOT_ICONS.map(
|
|
3749
|
+
(ic, i) =>
|
|
3750
|
+
`<button type="button" data-ic="${ic}" title="${ic}" class="${i === 7 ? 'on' : ''}">${botIconSvg(ic, 16)}</button>`
|
|
3751
|
+
).join('');
|
|
3665
3752
|
f.innerHTML = `
|
|
3666
3753
|
<label class="mem-label">${_t('bot_name')}</label>
|
|
3667
3754
|
<input id="bAddName" class="inp" placeholder="${_t('bot_name_ph')}" maxlength="40"/>
|
|
@@ -3696,7 +3783,7 @@ function renderBotAdd(pane) {
|
|
|
3696
3783
|
if (!name) { toast(_t('bot_name_req')); $('#bAddName').focus(); return; }
|
|
3697
3784
|
/* İLK HARF ZORUNLU: bot adı harf karakteriyle başlamalı */
|
|
3698
3785
|
if (!/^\p{L}/u.test(name)) { toast(_t('bot_name_letter')); $('#bAddName').focus(); return; }
|
|
3699
|
-
const icon = (f.querySelector('#bAddIconPick button.on') || {}).dataset?.ic || '
|
|
3786
|
+
const icon = (f.querySelector('#bAddIconPick button.on') || {}).dataset?.ic || 'robot';
|
|
3700
3787
|
const r = await beast.botsAdd({ name, icon, prompt: $('#bAddPrompt').value });
|
|
3701
3788
|
if (r.ok) {
|
|
3702
3789
|
toast(_t('bot_created') + ' ' + r.bot.name);
|
|
@@ -4167,6 +4254,8 @@ function onEvent(ev) {
|
|
|
4167
4254
|
break;
|
|
4168
4255
|
}
|
|
4169
4256
|
case 'status':
|
|
4257
|
+
/* kuyruktan tetiklenen yeni iş: kilitle kendiliğinden açılmışsa geri kapat */
|
|
4258
|
+
if (ev.status !== 'idle' && !busy) setBusy(true);
|
|
4170
4259
|
if (!busy) break;
|
|
4171
4260
|
setStatus(ev.status === 'idle' ? (busy ? 'düşünüyor…' : '') : ev.status === 'thinking' ? 'düşünüyor…' : ev.status + '…');
|
|
4172
4261
|
break;
|
|
@@ -5922,12 +6011,29 @@ async function setIdeMode(on) {
|
|
|
5922
6011
|
}
|
|
5923
6012
|
if (on) {
|
|
5924
6013
|
ideSplitRestore();
|
|
6014
|
+
setEditorHidden(localStorage.getItem('beast.editorHidden') === '1');
|
|
5925
6015
|
await loadIdeTree();
|
|
5926
6016
|
bcBanner();
|
|
5927
6017
|
codeGutterRender(); /* dosya açık olmasa bile rakamlar görünür */
|
|
5928
6018
|
}
|
|
5929
6019
|
}
|
|
5930
6020
|
|
|
6021
|
+
/* ---------- editör panelini kapat/göster ----------
|
|
6022
|
+
Kapatınca kod bölmesi gider, Beast Code tam genişlik alır; tercih
|
|
6023
|
+
localStorage'da kalıcıdır. Dosya ağacından dosya açınca geri açılır. */
|
|
6024
|
+
function editorHiddenOn() {
|
|
6025
|
+
return document.body.classList.contains('editor-hidden');
|
|
6026
|
+
}
|
|
6027
|
+
|
|
6028
|
+
function setEditorHidden(h) {
|
|
6029
|
+
document.body.classList.toggle('editor-hidden', !!h);
|
|
6030
|
+
if (els.codeShow) els.codeShow.hidden = !h;
|
|
6031
|
+
try { localStorage.setItem('beast.editorHidden', h ? '1' : '0'); } catch {}
|
|
6032
|
+
}
|
|
6033
|
+
|
|
6034
|
+
if (els.codeHide) els.codeHide.addEventListener('click', () => setEditorHidden(true));
|
|
6035
|
+
if (els.codeShow) els.codeShow.addEventListener('click', () => setEditorHidden(false));
|
|
6036
|
+
|
|
5931
6037
|
/* ---------- editor/chat ayırıcı (VS Code gibi sürükle-bırak) ----------
|
|
5932
6038
|
Orantılı model: kaydedilen, Beast Code'un ideRow içindeki PAYI'dır (0-1).
|
|
5933
6039
|
Tarayıcı açılınca/kapanınca/genişlik değişince row yeniden bölünür — editör
|
|
@@ -6215,6 +6321,7 @@ function codeActivate(i) {
|
|
|
6215
6321
|
}
|
|
6216
6322
|
|
|
6217
6323
|
async function codeOpen(rel) {
|
|
6324
|
+
if (editorHiddenOn()) setEditorHidden(false); /* dosya açılınca editör geri gelir */
|
|
6218
6325
|
const idx = codeTabs.findIndex((t) => t.rel === rel);
|
|
6219
6326
|
if (idx >= 0) {
|
|
6220
6327
|
codeActivate(idx);
|
|
@@ -6737,6 +6844,7 @@ function bcIngest(ev) {
|
|
|
6737
6844
|
bcRenderTodos(ev.todos);
|
|
6738
6845
|
break;
|
|
6739
6846
|
case 'tool-start':
|
|
6847
|
+
bcSetBusy(true); /* kuyruktan tetiklenen işte panel kendini kilitler */
|
|
6740
6848
|
bcToolBoxStart(ev.callId, ev.name, ev.args);
|
|
6741
6849
|
/* ajan dosya yazarsa/editlerse açık IDE sekmesini izlemeye al */
|
|
6742
6850
|
if ((ev.name === 'write_file' || ev.name === 'edit_file') && ev.args && ev.args.path) codeWriteWatch.set(ev.callId, ev.args.path);
|
|
@@ -6793,7 +6901,9 @@ function bcIngest(ev) {
|
|
|
6793
6901
|
bcStatusHide();
|
|
6794
6902
|
bcRefreshPreview();
|
|
6795
6903
|
} else {
|
|
6796
|
-
/* düşünüyor… / araç adı / bağlam durumu — input üstünde canlı balon
|
|
6904
|
+
/* düşünüyor… / araç adı / bağlam durumu — input üstünde canlı balon;
|
|
6905
|
+
kuyruktan tetiklenen yeni işte kilit geri kapanır */
|
|
6906
|
+
bcSetBusy(true);
|
|
6797
6907
|
bcStatusShow(ev.status === 'thinking' ? 'düşünüyor…' : String(ev.status || ''));
|
|
6798
6908
|
}
|
|
6799
6909
|
break;
|
|
@@ -6827,10 +6937,17 @@ function bcRunCurrent() {
|
|
|
6827
6937
|
if (r && r.ok && r.mode) {
|
|
6828
6938
|
/* /plan · /build · /auto: mod değişimi — tur AÇILMAZ, kilit anında açılır
|
|
6829
6939
|
(bilgi satırı bc-mode olayıyla düşer, başlık rozeti güncellenir) */
|
|
6830
|
-
bcSessionId = r.sessionId;
|
|
6940
|
+
if (r.sessionId) bcSessionId = r.sessionId;
|
|
6831
6941
|
bcSetBusy(false);
|
|
6942
|
+
} else if (r && r.ok && r.queued) {
|
|
6943
|
+
/* ajan çalışıyor → mesaj kuyrukta; iş bitince toplu gönderilir */
|
|
6944
|
+
if (r.sessionId) bcSessionId = r.sessionId;
|
|
6945
|
+
bcLine('t-dim', '⏳ kuyrukta (' + r.count + ') — iş bitince gönderilir');
|
|
6946
|
+
} else if (r && r.ok && r.pending) {
|
|
6947
|
+
/* boşta: kısa birleşme penceresi — aynı penceredeki mesajlar tek işte gider */
|
|
6948
|
+
if (r.sessionId) bcSessionId = r.sessionId;
|
|
6832
6949
|
} else if (r && r.ok) {
|
|
6833
|
-
bcSessionId = r.sessionId;
|
|
6950
|
+
if (r.sessionId) bcSessionId = r.sessionId;
|
|
6834
6951
|
} else if (r && r.busy) {
|
|
6835
6952
|
/* iş gerçekten sürüyor — kilit açılmaz, bilgi satırı düşer */
|
|
6836
6953
|
bcLine('t-dim', (r && r.error) || 'mesaj sürüyor');
|
|
@@ -6851,7 +6968,15 @@ if (els.bcInput) {
|
|
|
6851
6968
|
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); bcRunCurrent(); }
|
|
6852
6969
|
});
|
|
6853
6970
|
}
|
|
6854
|
-
if (els.bcStop) els.bcStop.addEventListener('click', () =>
|
|
6971
|
+
if (els.bcStop) els.bcStop.addEventListener('click', () => {
|
|
6972
|
+
beast.beastcodeStop().then((r) => {
|
|
6973
|
+
/* kuyrukta bekleyen mesajlar da silindi; engine boştaysa kilidi aç */
|
|
6974
|
+
if (r && r.ok && r.wasBusy === false) {
|
|
6975
|
+
bcSetBusy(false);
|
|
6976
|
+
bcStatusHide();
|
|
6977
|
+
}
|
|
6978
|
+
}).catch(() => {});
|
|
6979
|
+
});
|
|
6855
6980
|
if (els.bcClear) els.bcClear.addEventListener('click', () => {
|
|
6856
6981
|
els.bcOut.innerHTML = '';
|
|
6857
6982
|
bcTools.clear();
|
package/src/renderer/style.css
CHANGED
|
@@ -1857,6 +1857,18 @@ body.browser-open #settingsDialog { width: min(70vw, calc(100vw - var(--bw, 480p
|
|
|
1857
1857
|
}
|
|
1858
1858
|
#botChip:hover { border-color: var(--accent); }
|
|
1859
1859
|
|
|
1860
|
+
/* bot ikonları: tek renkli SVG — currentColor ile temaya uyar */
|
|
1861
|
+
#botChip svg,
|
|
1862
|
+
.bot-ico svg,
|
|
1863
|
+
.ico svg,
|
|
1864
|
+
.skill-name svg,
|
|
1865
|
+
.icon-pick button svg {
|
|
1866
|
+
display: inline-block;
|
|
1867
|
+
vertical-align: -2.5px;
|
|
1868
|
+
}
|
|
1869
|
+
#botHeadIcon svg { vertical-align: -4px; }
|
|
1870
|
+
.icon-pick button svg { display: block; margin: 0 auto; }
|
|
1871
|
+
|
|
1860
1872
|
#botBar {
|
|
1861
1873
|
flex: none;
|
|
1862
1874
|
max-height: 45%;
|
|
@@ -2492,6 +2504,26 @@ body.ide-mode #codePane { display: flex; }
|
|
|
2492
2504
|
background: var(--border);
|
|
2493
2505
|
opacity: 0.55;
|
|
2494
2506
|
}
|
|
2507
|
+
/* editör kapatılabilir: kod bölmesi + ayırıcı gizlenir, Beast Code tam genişlik */
|
|
2508
|
+
body.ide-mode.editor-hidden #codePane,
|
|
2509
|
+
body.ide-mode.editor-hidden #ideSplit { display: none; }
|
|
2510
|
+
body.ide-mode.editor-hidden #ideRow #bcPanel { flex: 1 1 auto; min-width: 0; }
|
|
2511
|
+
#codeHide {
|
|
2512
|
+
flex: none;
|
|
2513
|
+
width: 24px;
|
|
2514
|
+
height: 24px;
|
|
2515
|
+
padding: 0;
|
|
2516
|
+
border: 1px solid var(--border);
|
|
2517
|
+
border-radius: 6px;
|
|
2518
|
+
background: transparent;
|
|
2519
|
+
color: var(--muted);
|
|
2520
|
+
cursor: pointer;
|
|
2521
|
+
font-size: 11px;
|
|
2522
|
+
line-height: 1;
|
|
2523
|
+
}
|
|
2524
|
+
#codeHide:hover { color: var(--text); border-color: var(--accent); background: var(--panel2); }
|
|
2525
|
+
#bcHead #codeShow { width: 26px; padding: 0; }
|
|
2526
|
+
#codeShow[hidden] { display: none; }
|
|
2495
2527
|
#codeTabsBar {
|
|
2496
2528
|
flex: none;
|
|
2497
2529
|
display: flex;
|