beast-agent 0.29.0 → 0.30.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": "0.29.0",
4
+ "version": "0.30.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -13,6 +13,7 @@ const research = require('./research');
13
13
  const memory = require('./memory');
14
14
  const skills = require('./skills');
15
15
  const { estTokens, estMsgTokens } = require('./tokens');
16
+ const log = require('./logger');
16
17
 
17
18
  const MAX_TURNS = 25;
18
19
  const SUB_MAX_TURNS = 8;
@@ -258,6 +259,8 @@ class Engine {
258
259
  this._bgLimit = Math.max(1, Number(opts.bgLimit || process.env.BEAST_BG_LIMIT) || BG_MAX_CONCURRENT_DEFAULT);
259
260
  this._bgPendingStart = new Map(); // sid -> ilk görev metni (slot açılınca gönderilir)
260
261
  this._bgGroups = new Map(); // groupId -> {parentId,total,results,dead}
262
+ /* silinen oturum işaretleri: rapor/zincir buraya gönderilmez (hayalet oturum yok) */
263
+ this._deletedSessions = new Set();
261
264
  /* #17 kalıcı ajan geçmişi: restart sonrası işler + sohbetler kaybolmasın */
262
265
  this._bgJobsFile = path.join(this.sessionsDir, 'bg-jobs.json');
263
266
  this._loadBgJobsPersisted();
@@ -1173,13 +1176,14 @@ class Engine {
1173
1176
  }
1174
1177
 
1175
1178
  deleteSession(id) {
1179
+ const sid = String(id || '');
1176
1180
  try {
1177
- const c = this.ctrls.get(id);
1181
+ const c = this.ctrls.get(sid);
1178
1182
  if (c) {
1179
1183
  this._abortReasons = this._abortReasons || new Map();
1180
- this._abortReasons.set(String(id), 'oturum silindiği için tur iptal edildi');
1184
+ this._abortReasons.set(sid, 'oturum silindiği için tur iptal edildi');
1181
1185
  c.abort();
1182
- this.ctrls.delete(id);
1186
+ this.ctrls.delete(sid);
1183
1187
  }
1184
1188
  /* #17 silinecek ajanın bekleyen öz-kurtarma zamanlayıcısını iptal et —
1185
1189
  yoksa send() oturum dosyasını yeniden yaratıp işi diriltir */
@@ -1206,6 +1210,14 @@ class Engine {
1206
1210
  this._saveBgJobs();
1207
1211
  this._bgEmit();
1208
1212
  }
1213
+ /* silinme işareti: bekleyen raporlar/rapor zinciri bu oturuma ASLA düşmez */
1214
+ if (this._deletedSessions) {
1215
+ this._deletedSessions.add(sid);
1216
+ if (this._deletedSessions.size > 300) {
1217
+ const first = this._deletedSessions.values().next().value;
1218
+ if (first !== undefined) this._deletedSessions.delete(first);
1219
+ }
1220
+ }
1209
1221
  this.emit({ type: 'sessions' });
1210
1222
  return true;
1211
1223
  }
@@ -1213,23 +1225,37 @@ class Engine {
1213
1225
  /* ---------- prompt assembly (the frugal part) ---------- */
1214
1226
 
1215
1227
  /* Beast Code (IDE paneli): VS Code hızında çalışsın diye SECMET prompt.
1228
+ OpenCode kodlama disiplini gömülü: bağlam → plan → küçük diff → doğrula.
1216
1229
  Hafıza embedding araması, skills taraması, kişilik/kural blokları YOK —
1217
1230
  prompt kısa kalır → ilk token hızlı, tur maliyeti düşük. */
1218
1231
  buildBcSystem(session) {
1219
1232
  const nowD = new Date();
1220
1233
  const localDate = nowD.toLocaleDateString('tr-TR');
1221
1234
  const localTime = nowD.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
1235
+ const mode = String((session && session.bcMode) || 'auto').toLowerCase();
1236
+ const modeBlock =
1237
+ mode === 'plan'
1238
+ ? 'ÇALIŞMA MODU: PLAN 🔍 — dosyaları oku/incele (read_file, list_dir), KOD YAZMA; mevcut yapıyı özetle ve adım adım UYGULAMA PLANI ver (hangi dosya, ne değişecek, nasıl doğrulanır). Kullanıcı /build deyince plan uygulanır.'
1239
+ : mode === 'build'
1240
+ ? 'ÇALIŞMA MODU: BUILD 🛠 — son planı SOHBETTEKİ bağlamdan al ve UYGULA: dosyaları düzenle, komutları çalıştır, doğrula. Yeni plan açma; en fazla 1 cümlelik yön gösterimi + uygulama.'
1241
+ : 'ÇALIŞMA MODU: OTOMATİK ⚡ — önce 2-4 satırlık mini plan (todo_write), sonra hemen uygula + doğrula.';
1222
1242
  return (
1223
- 'Sen BEAST CODE\u2019sun — IDE panelinde çalışan hızlı bir kodlama ajanı. VS Code gibi çevik ol.\n' +
1243
+ 'Sen BEAST CODE\u2019sun — IDE panelinde çalışan, OpenCode disiplinli hızlı bir kodlama ajanı. VS Code gibi çevik ol.\n' +
1224
1244
  `Çalışma klasörü: ${(session && session.workspace) || this.workspace}\n` +
1225
1245
  `Yerel zaman: ${localDate} ${localTime}\n` +
1246
+ `${modeBlock}\n` +
1247
+ 'WORKFLOW (her işte bu sıra):\n' +
1248
+ '1) BAĞLAM: değiştirmeden önce ilgili dosyaları OKU (read_file / list_dir / grep mantığıyla hedefli ara) — varsayım yapma, mevcut stili/konvansiyonu takip et.\n' +
1249
+ '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' +
1250
+ '3) EDİT: küçük, HEDEFLİ diff\u2019ler yaz (write_file ile yalnız ilgili bölümü etkile); dosyayı baştan yazma; ilgisiz yeniden biçimleme/kayıt boşluk değişikliği YAPMA.\n' +
1251
+ '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.\n' +
1252
+ '5) RAPOR: 1-3 satır — ne değişti + doğrulama sonucu (ör. "npm test ✓ 154/154"). Uzun açıklama yok.\n' +
1226
1253
  'HIZ KURALLARI:\n' +
1227
- '- İLK EYLEMİN daima todo_write olsun: işi 2-6 maddelik plana böl; her adım bitince status:"done" ile güncelle.\n' +
1228
- '- Kodu ve dosyaları DOĞRUDAN write_file ile yaz/güncelle. Basit dosya oluşturma/düzenleme için asla Python scripti yazma; python_run yalnız gerçek hesap/veri işleme gerekiyorsa.\n' +
1229
- '- Kurulum, test, build, git gibi işler için run_command kullan (Windows PowerShell ortamı).\n' +
1230
- '- Bağımsız araç çağrılarını AYNI TURDA paralel ver.\n' +
1254
+ '- Kodu ve dosyaları DOĞRUDAN write_file ile yaz; basit dosya oluşturma/düzenleme için Python scripti yazma; python_run yalnız gerçek hesap/veri işleme gerekiyorsa.\n' +
1255
+ '- Bağımsız araç çağrılarını AYNI TURDA paralel ver (birden çok read_file tek turda).\n' +
1231
1256
  '- Dosyayı kullanıcı söylememişse list_dir ile yapıyı görüp kendin karar ver.\n' +
1232
- '- Uzun açıklama yok: doğrudan çalış, bitince 1-3 satırlık kısa özet ver. Soru sorma, sohbet etme.\n' +
1257
+ '- Run_command PowerShell ortamında çalışır (Windows).\n' +
1258
+ '- Soru sorma, sohbet etme; uygulayıp özetle. Kullanıcı /plan /build /auto ile modu değiştirir.\n' +
1233
1259
  FORMAT_RULES
1234
1260
  );
1235
1261
  }
@@ -1801,6 +1827,11 @@ class Engine {
1801
1827
  _bgGroupRecord(title, status, summary) {
1802
1828
  for (const [gid, g] of this._bgGroups) {
1803
1829
  if (g.dead) continue;
1830
+ /* üst oturum silinmişse grup ölü sayılır — hayalet oturuma yazma */
1831
+ if (this._deletedSessions && this._deletedSessions.has(g.parentId)) {
1832
+ g.dead = true;
1833
+ continue;
1834
+ }
1804
1835
  g.results.push({ title, status, summary: String(summary || '').slice(0, 900) });
1805
1836
  if (g.results.length >= g.total) {
1806
1837
  this._bgGroups.delete(gid);
@@ -1998,8 +2029,18 @@ class Engine {
1998
2029
  const reason = Engine.superviseReason(job, now);
1999
2030
  if (!reason) continue;
2000
2031
  /* #16 öz-kurtarma: takılan ajan CEO'yu meşgul etmeden kendini toparlar */
2001
- if (reason === 'stuck' && (job.fixes || 0) < BG_FIX_MAX) {
2002
- this._bgSelfHeal(job, now);
2032
+ if (reason === 'stuck') {
2033
+ if ((job.fixes || 0) < BG_FIX_MAX) {
2034
+ this._bgSelfHeal(job, now);
2035
+ } else {
2036
+ /* öz-kurtarma hakları tükendi + hâlâ takılı → ZORLA KAPAT:
2037
+ ajan sonsuza dek 'running' kalırsa fan-out grubu ASLA tamamlanmaz
2038
+ ve birleşik rapor hiç düşmez — CEO "uyuyor" kalır */
2039
+ this._bgForceClose(
2040
+ job,
2041
+ `superyorizon: ajan ${Math.round((now - Date.parse(job.startedAt)) / 60000)} dk'dır takılı, öz-kurtarma hakları tükendi`
2042
+ );
2043
+ }
2003
2044
  continue;
2004
2045
  }
2005
2046
  job.lastNudgeAt = nowIso();
@@ -2040,6 +2081,26 @@ class Engine {
2040
2081
  this._bgKick(job, text);
2041
2082
  }
2042
2083
 
2084
+ /* Takılı işi ZORLA sonlandır: tur abort edilir, iş kaydı SEBEPİYLE kapanır,
2085
+ grup üyesiyse 'aborted' olarak gruba yazılır → birleşik rapor AKAR,
2086
+ CEO "uyumak" yerine hemen sonucu kullanıcıya aktarır. */
2087
+ _bgForceClose(job, reason) {
2088
+ const sid = String(job.id);
2089
+ job.revive = false; /* kick-in-flight guard'ı devre dışı — bu gerçek bir bitiş */
2090
+ this._clearKicks(sid);
2091
+ this._abortReasons = this._abortReasons || new Map();
2092
+ this._abortReasons.set(sid, this._abortReason(reason));
2093
+ const ctrl = this.ctrls.get(sid);
2094
+ if (ctrl) {
2095
+ try { ctrl.abort(); } catch {}
2096
+ /* _run catch'i sebebi okuyup _bgFinish('aborted') yapacak */
2097
+ } else {
2098
+ /* tur zaten yok — kaydı doğrudan kapat */
2099
+ this._bgFinish(sid, 'aborted', reason);
2100
+ }
2101
+ try { log.info('bg', `iş zorla kapatıldı: ${job.title} — ${reason}`); } catch {}
2102
+ }
2103
+
2043
2104
  /* Arka plan oturumuna müdahale: asılı turu kes (ctrl.abort), sonra
2044
2105
  kurtarma mesajını bas. abort → eski _run sonlanır → send yeni tur açar.
2045
2106
  revive bayrağı: /stop veya silme sonrası bekleyen müdahaleler patlamasın. */
@@ -2093,6 +2154,11 @@ class Engine {
2093
2154
  try {
2094
2155
  const meta = this._bgMeta && this._bgMeta.get(String(bgSessionId));
2095
2156
  if (!meta) return false;
2157
+ /* üst oturum silinmişse rapor düşer — hayalet oturum yaratma */
2158
+ if (this._deletedSessions && this._deletedSessions.has(meta.parentId)) {
2159
+ this._bgMeta.delete(String(bgSessionId));
2160
+ return false;
2161
+ }
2096
2162
  /* Öz-kurtarma/wrap-up kick'i bekliyorsa bu bir ABORT turudur — gerçek
2097
2163
  bitiş DEĞİL; erken "ARKA PLAN BİTTİ (sonuca ulaşmadan)" ilanı basma */
2098
2164
  const pendingKick = this._bgJobs.get(String(bgSessionId));
@@ -2134,14 +2200,19 @@ class Engine {
2134
2200
  }
2135
2201
  }
2136
2202
 
2137
- /* Ana oturum işini bitirince bekleyen arka plan raporlarını boşalt */
2203
+ /* Ana oturum işini bitirince bekleyen arka plan raporlarını boşalt.
2204
+ Güvenli: oturum SİLİNMİŞSE rapor düşer (hayalet oturum yaratılmaz);
2205
+ send başarısızsa (aniden meşgul oldu) rapor sırada kalır, sonra denenir. */
2138
2206
  flushPendingReports(sessionId) {
2139
2207
  if (!this._pendingReports || !this._pendingReports.length) return;
2140
2208
  const rest = [];
2141
2209
  for (const r of this._pendingReports) {
2142
- if (r.parentId === sessionId && !this.isBusy(sessionId)) {
2143
- try { this.send(sessionId, { text: r.text }); } catch {}
2144
- } else rest.push(r);
2210
+ if (r.parentId !== sessionId) { rest.push(r); continue; }
2211
+ if (this._deletedSessions && this._deletedSessions.has(sessionId)) continue;
2212
+ if (this.isBusy(sessionId)) { rest.push(r); continue; }
2213
+ let sent = false;
2214
+ try { sent = !!this.send(sessionId, { text: r.text }); } catch {}
2215
+ if (!sent) rest.push(r);
2145
2216
  }
2146
2217
  this._pendingReports = rest;
2147
2218
  }
@@ -2474,6 +2545,14 @@ class Engine {
2474
2545
  } finally {
2475
2546
  this.ctrls.delete(sid);
2476
2547
  emit({ type: 'status', status: 'idle' });
2548
+ /* bekleyen paralel-ajan raporları HER bitiş yolunda boşaltılır —
2549
+ hata/durdurma/tur-limiti sonrası bile sonuç raporu kullanıcıya ulaşır.
2550
+ DİKKAT: buradan flush'u DOĞRUDAN çağırma! send → _run senkron-hata
2551
+ yolunda (model-yok gibi) await'siz zincirlenir → stack taşması.
2552
+ Macrotask'a ertele: her hop taze stack alır, pop-gönder sırası bozulmaz. */
2553
+ const fl = () => { try { this.flushPendingReports(sid); } catch {} };
2554
+ const t = setTimeout(fl, 0);
2555
+ if (t.unref) t.unref();
2477
2556
  }
2478
2557
  }
2479
2558
 
@@ -61,43 +61,101 @@ function scan() {
61
61
  const SEEDS = [
62
62
  {
63
63
  folder: 'help',
64
+ force: true,
64
65
  body: `---
65
66
  name: help
66
- description: Beast Agent genel yardım — tüm veri yolları (%APPDATA%\\beast), ayar/config dosyaları, memory, cron, watcher, WhatsApp, TTS, araç özeti.
67
- version: 1.0.0
67
+ description: Beast Agent'ın İÇ HARİTASI"kaç bot var", "oturumlar nerede", "x nasıl çalışır" gibi SİSTEM sorularını saniyeler içinde çözer: tüm veri yolları, hazır PowerShell/json one-liner'lar, kod haritası, slash komutları, ayar sekmeleri. Kod tabanını saatlerce taramadan bu haritadan oku.
68
+ version: 2.0.0
68
69
  ---
69
70
 
70
- # Beast Agent Yardım
71
+ # Beast Agent İç Haritası (self-help)
72
+
73
+ ## ALTIN KURAL
74
+ Sistem sorusu geldiğinde KOD TABANINI TARAMA. Aşağıdaki hazır yolu/one-liner'ı kullan — cevap 1-2 saniyede çıkar.
75
+
76
+ ## HIZLI CEVAPLAR (hazır komutlar)
77
+
78
+ **"Kaç bot var / botları listele"**
79
+ \`\`\`powershell
80
+ $b = Get-Content "$env:APPDATA\\beast\\bots.json" | ConvertFrom-Json; $b.bots | Select-Object id,name,admin,code
81
+ "Toplam bot: " + $b.bots.Count + " (admin: Beast)"
82
+ \`\`\`
83
+ (python_run ile: \`import json;d=json.load(open(r'%APPDATA%\\beast\\bots.json',encoding='utf-8'));print(len(d['bots']),[b['name'] for b in d['bots']])\`)
84
+
85
+ **"Kaç oturum var / sohbet geçmişi"**
86
+ \`\`\`powershell
87
+ (Get-ChildItem "$env:APPDATA\\beast\\sessions\\*.jsonl").Count
88
+ \`\`\`
89
+ Kod ↔ dosya eşleşmesi: jsonl'nin ilk satırı \`{"t":"meta","code":"ABC12"}\` taşır.
90
+
91
+ **"Paralel ajanlar ne durumda"** → \`tasks_list\` aracını ÇAĞIR (kod okumaya gerek yok) ya da:
92
+ \`\`\`powershell
93
+ Get-Content "$env:APPDATA\\beast\\sessions\\bg-jobs.json" | ConvertFrom-Json | % jobs | Select title,status,startedAt,error
94
+ \`\`\`
95
+
96
+ **"Maliyet/usage"** → \`/usage\` komutu ya da Ayarlar → Maliyet; kayıt dosyası \`usage.json\`.
71
97
 
72
- Tüm veri tek kökte: \`%APPDATA%\\beast\\\` (test/taşınabilir için \`BEAST_DATA\` env override).
98
+ **"Cron/izleyici var mı"** \`cron.json\`, \`watchers.json\`.
73
99
 
74
- ## Dosya Haritası
100
+ ## DOSYA HARİTASI (%APPDATA%\\beast\\)
75
101
 
76
102
  | Yol | Ne İşe Yarar |
77
103
  |---|---|
78
- | \`config.yaml\` + \`.env\` | Model sağlayıcıları + API anahtarları. Provider tanımı: \`providers.<id>.base_url/key_env/models\`; aktif seçim: \`model.provider/default\`. \`.env\`'de \`<PROVIDER>_API_KEY\` veya \`key_env\` ile eşleşen anahtar |
79
- | \`settings.json\` | App ayarları: theme, workspace, modelOverride, customProviders, roleModels, deletedModels, fallout, waTts (TTS), email (mail credential), waAllow, waLockdown |
80
- | \`sessions\\\` | Sohbet oturumları (mesaj JSON'ları) |
81
- | \`memories\\\` | SOUL.md (kişilik), MEMORY.md (uzun hafıza kayıtları), USER.md (kullanıcı bilgisi) |
82
- | \`skills\\\` | \`**\\\\SKILL.md\` isim+açıklama system prompt'a gider, gövde okunur |
83
- | \`cron.json\` | Zamanlanmış görevler (5 alanlı cron) |
84
- | \`watchers.json\` | Web/batarya izleyicileri |
85
- | \`wa-auth\\\` | WhatsApp Baileys eşleme auth'u SİLME |
86
- | \`wa-chats.json\`, \`wa.log\` | WA sohbet eşlemesi ve log |
87
- | \`bots.json\`, \`bots/<id>/\`, \`whitelist.json\` | Bot sistemi: bot kaydı (max 5, \`beast\` admin silinemez), her botun izole klasörü (config.json, memory.md, yetkiler.json, logs/), numara→bot eşlemesi. Bağlı olmayan numara beast'e düşer |
88
- | \`logs\\\`, \`scripts\\\` | Çalışma logları / kullanıcı scriptleri |
89
-
90
- ## Sağlık Kontrolleri
91
-
92
- - "Model yapılandırılmadı" hatası → \`config.yaml\`/\`.env\` dolu mu bak; yoksa kullanıcıdan Ayarlar → Provider'dan model seçmesini ya da dosyaları doldurmasını iste (\`config.example.yaml\` repo kökünde örnek var).
93
- - Mail sorunları → \`email\` skill'ini oku.
94
- - fallout zinciri → Ayarlar → Fallout; 401'de sıradaki slota otomatik düşer.
95
-
96
- ## Davranış Kuralları
97
-
104
+ | \`config.yaml\` + \`.env\` | Model sağlayıcıları + API anahtarları (\`providers.<id>.base_url/key_env/models\`, aktif: \`model.provider/default\`) |
105
+ | \`settings.json\` | App ayarları: theme, workspace, modelOverride, customProviders, roleModels, deletedModels, fallout, waTts, email, waAllow, **searchChain (web arama sırası)**, **obscuraEnabled**, activeBotId |
106
+ | \`sessions\\*.jsonl\` | Sohbet oturumları (satır JSON: t:meta kod, t:meta2 paralel-ajan işareti, t:msg mesaj, t:todo, t:notes) |
107
+ | \`sessions\\bg-jobs.json\` | Paralel ajan iş kayıtları: id, title, status(queued/running/done/error/aborted), error=İPTAL SEBEBİ, groupId |
108
+ | \`memories\\\` | SOUL.md (kişilik), MEMORY.md (uzun hafıza), USER.md (kullanıcı) |
109
+ | \`skills\\<ad>\\SKILL.md\` | Skill'ler (isim+açıklama prompt'a girer, gövde okunur) |
110
+ | \`bots.json\` | Bot kayıt defteri — max 5 bot, ilk kayıt admin 'Beast' (silinemez). Bot adı HARFLE başlamak ZORUNDA |
111
+ | \`bots/<id>/\` | Botun izole klasörü: config.json, memory.md, yetkiler.json, logs/ |
112
+ | \`whitelist.json\`, \`wa-auth\\\`, \`wa-chats.json\`, \`wa.log\` | WhatsApp izin listesi, Baileys auth (SİLME), sohbet eşlemesi, log |
113
+ | \`cron.json\`, \`watchers.json\`, \`bus.json\` | Zamanlanmış görevler, web/batarya izleyicileri, olay abonelikleri |
114
+ | \`scripts\\\` | Python scriptleri (websearch.py, news.py) |
115
+ | \`obscura\\obscura.exe\` | Obscura stealth tarayıcı (web arama zincirinin 2. motoru; açılışta otomatik kurulur) |
116
+ | \`py\\python.exe\` | Gömülü Python runtime (makinede Python olmasa bile python_run çalışır) |
117
+ | \`usage.json\`, \`logs\\\` | Kullanım sayaçları, çalışma logları |
118
+ | Masaüstü\\Beast-Backups | Şifreli yedekler (.beastbak) /backup ile alınır |
119
+
120
+ ## KOD HARİTASI (src/)
121
+
122
+ | Dosya | Sorumluluk |
123
+ |---|---|
124
+ | \`main.js\` | Electron ana süreç: tüm IPC, WhatsApp/Telegram/Discord entegrasyonları, onay kapısı (/approve), /stop /restart, splash, gizli araştırma tarayıcısı, obscura kurulumu |
125
+ | \`agent/engine.js\` | OTURUM MOTORU: send/_run tur döngüsü, paralel ajanlar (run_background, run_background_many, tasks_list, task_status, task_cancel — task_cancel reason ZORUNLU), superyorizon (takılan işi zorla kapatır), iptal sebep disiplini, flushPendingReports |
126
+ | \`agent/tools.js\` | Araçlar + web_search SIRALI ZİNCİRİ (searchChain: 1 dahili tarayıcı(Google) → 2 Obscura(stealth→DuckDuckGo) → 3 TinyFish(anahtarsa) → 4 python çoklu-motor; sıra Ayarlar→Web Arama'dan değişir), run_command, read/write_file, python_run, http_fetch, deep_search |
127
+ | \`agent/llm.js\` | Sağlayıcı çağrısı + retry: ağ kopmasında 6 deneme + internet dönene kadar bekler; akış ortası kopmada kaldığı yerden devam |
128
+ | \`agent/obscura.js\` | Obscura: otomatik kurulum (GitHub releases, stealth zip) + DuckDuckGo html/lite arama ayrıştırıcı |
129
+ | \`agent/bots.js\` | Bot CRUD, 5 haneli bot kodu, izolasyon; ad-ilk-harf doğrulaması |
130
+ | \`agent/skills.js\` | Builtin tohumlar (help, email, python-web-search, pdf, gold-trading) + SKILL.md taraması + taslaklar |
131
+ | \`agent/memory.js\`, \`agent/kb.js\` | Kalıcı hafıza + bilgi bankası |
132
+ | \`cron.js\` (src kökü), \`agent/watchers.js\`, \`agent/bus.js\` | Cron, izleyici, olay merkezi — /stop bunlara DOKUNMAZ |
133
+ | \`agent/usage.js\` | Token/maliyet sayaçları |
134
+ | \`renderer/\` | Arayüz: renderer.js (tüm paneller), i18n.js (TR/EN), index.html, style.css |
135
+ | \`bin/beast-agent.js\` | CLI: \`beast\` (başlat), \`beast update\`, \`beast uninstall\` |
136
+
137
+ ## SLASH KOMUTLARI
138
+ /help · /version · /new · /open <kod> · /sessions · /stop · /start · /restart · /change [n] · /model <isim> · /think 0-5 · /clear · /notes · /rule <metin> · /rules · /notify on|off · /screenshot · /approve [always] · /deny · /update [now] · /usage · /backup · /status · /skills
139
+
140
+ ## AYAR SEKMELERİ
141
+ Provider · Fallout · Skills · Paralel Ajanlar · TTS · E-posta · Entegrasyonlar · Web Arama (Obscura kurulum + arama sırası) · Olaylar · Cron · Maliyet · Loglar · Panel · Limitler · Güvenlik · Güncelleme
142
+
143
+ ## PARALEL AJAN HIZLI BİLGİ
144
+ - CEO modu: konuşan ajan iş YAPMAZ, run_background/run_background_many ile devreder; görev tanımı kendine yeterli olmalı (ajan CEO bağlamını GÖREMEZ).
145
+ - İş bitince birleşik/bireysel rapor OTOMATİK üst sohbete düşer — takılı iş superyorizon tarafından SEBEPİYLE zorla kapatılır.
146
+ - Canlı izleme: tasks_list/task_status; iptal: task_cancel (reason zorunlu, kullanıcıya sebep yazılır).
147
+
148
+ ## SAĞLIK KONTROLLERİ
149
+ - "Model yapılandırılmadı" → config.yaml/.env dolu mu; Ayarlar → Provider'dan seçim.
150
+ - Mail hatası → \`email\` skill'ini oku.
151
+ - 401 → Fallout zinciri sıradaki slota otomatik düşer.
152
+ - DDG boş/engelli → normaldir; zincir Obscura/Bing/Mojeek'e otomatik geçer.
153
+
154
+ ## DAVRANIŞ KURALLARI
98
155
  - Şifre/API anahtarlarını asla düz metin loglama/tekrarlama.
99
- - settings.json'a el ile yazarken app kapalı olsun (kapanışta ezme riski).
100
- - Yeni yetenek gerekiyorsa \`%APPDATA%\\beast\\skills\\<ad>\\\\SKILL.md\` oluşturmayı önerebilirsin.`,
156
+ - settings.json'a el ile yazarken app kapalı olsun.
157
+ - Yeni yetenek gerekiyorsa \`%APPDATA%\\beast\\skills\\<ad>\\SKILL.md\` oluşturmayı öner.
158
+ - Bu haritadaki yolları ÖNCE dene; kod taraması yalnız haritada olmayan derin sorularda.`,
101
159
  },
102
160
  {
103
161
  folder: 'email',
package/src/main.js CHANGED
@@ -5221,6 +5221,21 @@ ipcMain.handle('beastcode:send', (_e, payload) => {
5221
5221
  const s = bcGetSession(ws);
5222
5222
  s.workspace = ws; /* soldaki klasörde çalış */
5223
5223
  s.bcCode = true; /* todo disiplini + iş sonu hızlı kapanış (engine) */
5224
+ /* OpenCode disiplini — çalışma modu: /plan (incele+planla) · /build (uygula) · /auto */
5225
+ const m = /^\/(plan|build|auto)\b/i.exec(text);
5226
+ if (m) {
5227
+ s.bcMode = m[1].toLowerCase();
5228
+ engine.cache.set(s.id, s);
5229
+ if (win && !win.isDestroyed()) {
5230
+ const body = m[1].toLowerCase() === 'plan'
5231
+ ? 'PLAN MODU — dosyaları okuyup inceler, KOD YAZMAZ; adım adım uygulama planı verir.'
5232
+ : m[1].toLowerCase() === 'build'
5233
+ ? 'BUILD MODU — son planı UYGULAR: dosyaları düzenler, komutları çalıştırır, doğrular.'
5234
+ : 'OTOMATİK MOD — önce kısa plan, sonra uygulama + doğrulama (OpenCode disiplini).';
5235
+ win.webContents.send('agent:event', { sessionId: s.id, type: 'bc-mode', mode: s.bcMode, body });
5236
+ }
5237
+ return { ok: true, sessionId: s.id, mode: s.bcMode };
5238
+ }
5224
5239
  engine.cache.set(s.id, s);
5225
5240
  const ok = engine.send(s.id, text);
5226
5241
  if (!ok) return { ok: false, error: 'mesaj gönderilemedi' };
@@ -5317,6 +5332,17 @@ ipcMain.handle('searchorder:set', (_e, chain) => {
5317
5332
  }
5318
5333
  });
5319
5334
 
5335
+ /* Sohbet listesi ELLE sıralama (sol panelde sürükle-bırak) */
5336
+ ipcMain.handle('sessions:order:get', () => ({
5337
+ order: Array.isArray(settings.sessionOrder) ? settings.sessionOrder : [],
5338
+ }));
5339
+ ipcMain.handle('sessions:order:set', (_e, order) => {
5340
+ const arr = Array.isArray(order) ? order.map(String).filter(Boolean).slice(0, 500) : [];
5341
+ settings.sessionOrder = arr;
5342
+ saveSettings();
5343
+ return { ok: true };
5344
+ });
5345
+
5320
5346
  /* #TinyFish: anahtar girilirse web_search zincirinin BAŞINDA kullanılır */
5321
5347
  ipcMain.handle('tinyfish:get', () => {
5322
5348
  const k = settings.tinyfishKey || '';
package/src/preload.js CHANGED
@@ -76,6 +76,8 @@ contextBridge.exposeInMainWorld('beast', {
76
76
  obscuraSetEnabled: (v) => ipcRenderer.invoke('obscura:setEnabled', v),
77
77
  searchOrderGet: () => ipcRenderer.invoke('searchorder:get'),
78
78
  searchOrderSet: (chain) => ipcRenderer.invoke('searchorder:set', chain),
79
+ sessionsOrderGet: () => ipcRenderer.invoke('sessions:order:get'),
80
+ sessionsOrderSet: (order) => ipcRenderer.invoke('sessions:order:set', order),
79
81
  tinyfishGet: () => ipcRenderer.invoke('tinyfish:get'),
80
82
  tinyfishSet: (key) => ipcRenderer.invoke('tinyfish:set', key),
81
83
  tinyfishClear: () => ipcRenderer.invoke('tinyfish:clear'),
@@ -21,6 +21,7 @@
21
21
  tipNet: 'İnternet durumu',
22
22
  tip_net_online: 'İnternet: bağlı',
23
23
  tip_net_offline: 'İnternet: yok — bağlantı dönünce işler otomatik sürer',
24
+ sess_drag: 'Sürükleyerek sırayı değiştir',
24
25
  tipWatch: 'İzleyiciler (Watchers)',
25
26
  tipCron: 'Cron Görevler',
26
27
  tip_light: 'Açık tema',
@@ -262,6 +263,7 @@
262
263
  us_where_off_toast: 'Açılış özeti kapandı',
263
264
  ws_calls: 'çağrı',
264
265
  ws_token: 'token',
266
+ td_hide: 'Görev listesini gizle (yeni güncellemede yine görünür)',
265
267
  ws_h2: 'Web Arama',
266
268
  ws_sub: 'Arama zinciri: 1) dahili tarayıcı (direk Google) 2) Obscura (stealth headless → DuckDuckGo) 3) TinyFish (anahtar varsa) 4) python çoklu-motor (ddgs / DDG+Bing+Mojeek) — otomatik. Sıra ve aç/kapa aşağıdan değiştirilir.',
267
269
  oc_h2: 'Obscura (Stealth Tarayıcı)',
@@ -575,6 +577,7 @@
575
577
  tipNet: 'Internet status',
576
578
  tip_net_online: 'Internet: connected',
577
579
  tip_net_offline: 'Internet: offline — work resumes automatically once the connection returns',
580
+ sess_drag: 'Drag to reorder',
578
581
  tipWatch: 'Watchers',
579
582
  tipCron: 'Cron Jobs',
580
583
  tip_light: 'Light theme',
@@ -815,6 +818,7 @@
815
818
  us_where_off_toast: 'Startup summary off',
816
819
  ws_calls: 'calls',
817
820
  ws_token: 'tokens',
821
+ td_hide: 'Hide todo list (reappears on next update)',
818
822
  ws_h2: 'Web Search',
819
823
  ws_sub: 'Search chain: 1) built-in browser (direct Google) 2) Obscura (stealth headless → DuckDuckGo) 3) TinyFish (if a key is set) 4) python multi-engine (ddgs / DDG+Bing+Mojeek) — automatic. Order and on/off are editable below.',
820
824
  oc_h2: 'Obscura (Stealth Browser)',
@@ -280,8 +280,14 @@ function renderTodos(items) {
280
280
  })
281
281
  .join('');
282
282
  els.todoPanel.innerHTML =
283
- `<div class="td-head"><span>GÖREVLER</span><span class="td-count">${done}/${list.length}</span></div>` + rows;
283
+ `<div class="td-head"><span>GÖREVLER</span>` +
284
+ `<span class="td-right"><span class="td-count">${done}/${list.length}</span>` +
285
+ `<button class="td-x" title="${_t('td_hide')}">×</button></span></div>` + rows;
284
286
  els.todoPanel.hidden = false;
287
+ /* X yalnız BU listeyi gizler — kalıcı kapanma yok: bir sonraki görev
288
+ güncellemesi (yeni görev eklendi / görev yapıldı) paneli yeniden gösterir */
289
+ const x = els.todoPanel.querySelector('.td-x');
290
+ if (x) x.addEventListener('click', () => { els.todoPanel.hidden = true; });
285
291
  }
286
292
  function setBusy(b) {
287
293
  busy = b;
@@ -482,7 +488,41 @@ function finishToolCard(callId, ok, result) {
482
488
 
483
489
  /* ---------------- sessions sidebar ---------------- */
484
490
 
491
+ /* sol panel sohbet listesi: kullanıcı ELLE sıralayabilir (sürükle-bırak).
492
+ sessionOrder: session id'lerinin tercih sırası — settings.json'da kalıcı.
493
+ Listede olmayan oturumlar engine sırasıyla (son mesaja göre) arkada gelir. */
494
+ let sessionOrder = [];
495
+ let sessionOrderLoaded = null;
496
+ function loadSessionOrder() {
497
+ if (!sessionOrderLoaded) {
498
+ sessionOrderLoaded = beast
499
+ .sessionsOrderGet()
500
+ .then((r) => { sessionOrder = (r && Array.isArray(r.order) ? r.order : []).map(String); })
501
+ .catch(() => {});
502
+ }
503
+ return sessionOrderLoaded;
504
+ }
505
+
506
+ function reorderSessions(dragId, targetId) {
507
+ const idx = (id) => sessionOrder.indexOf(id);
508
+ const dp = idx(dragId);
509
+ const tp = idx(targetId);
510
+ sessionOrder = sessionOrder.filter((x) => x !== dragId);
511
+ const ti = sessionOrder.indexOf(targetId);
512
+ if (ti === -1) {
513
+ /* hedef hiç elle sıralanmamış: listeden görünür konumunu korumak mümkün değil — sona ekle */
514
+ sessionOrder.push(targetId, dragId);
515
+ } else if (dp !== -1 && dp < tp) {
516
+ sessionOrder.splice(ti + 1, 0, dragId); /* aşağı sürüklendi → hedefin ALTINA */
517
+ } else {
518
+ sessionOrder.splice(ti, 0, dragId); /* yukarı sürüklendi → hedefin ÜSTÜNE */
519
+ }
520
+ beast.sessionsOrderSet(sessionOrder).catch(() => {});
521
+ refreshSessions();
522
+ }
523
+
485
524
  async function renderSessions(list) {
525
+ await loadSessionOrder();
486
526
  let waSet = new Set();
487
527
  let tgSet = new Set();
488
528
  try {
@@ -492,13 +532,19 @@ async function renderSessions(list) {
492
532
  tgSet = new Set(await beast.tgListSessions());
493
533
  } catch {}
494
534
  els.sessList.innerHTML = '';
535
+ /* elle sıra: sessionOrder'daki id'ler önde (o sırayla), diğerleri engine sırasıyla arkada */
536
+ const rank = new Map(sessionOrder.map((id, i) => [id, i]));
537
+ const withRank = list.map((s, i) => ({ s, r: rank.has(s.id) ? rank.get(s.id) : 1e6 + i }));
538
+ withRank.sort((a, b) => a.r - b.r);
495
539
  /* aktif botun oturumları — botlar arası geçişte liste de o bota göre değişir.
496
540
  KATI KURAL: her oturum yalnız BAĞLI OLDUĞU botun listesinde görünür;
497
541
  bot kaydı olmayan (eski) oturumlar Beast (varsayılan sahip) altında görünür. */
498
- for (const s of list) {
542
+ for (const { s } of withRank) {
499
543
  if ((s.botId || 'beast') !== activeBotId) continue;
500
544
  const row = document.createElement('div');
501
545
  row.className = 'sess' + (s.id === activeId ? ' active' : '');
546
+ row.title = _t('sess_drag');
547
+ row.draggable = true;
502
548
  row.innerHTML =
503
549
  (waSet.has(s.id) ? '<span class="sess-wa" title="WhatsApp">W</span>' : '') +
504
550
  (tgSet.has(s.id) ? '<span class="sess-tg" title="Telegram">T</span>' : '') +
@@ -516,12 +562,40 @@ async function renderSessions(list) {
516
562
  const created = await beast.createSession();
517
563
  await openSession(created.id);
518
564
  }
565
+ sessionOrder = sessionOrder.filter((x) => x !== s.id);
519
566
  refreshSessions();
520
567
  });
568
+ /* sürükle-bırak: yukarı/aşağı yer değiştirme */
569
+ row.addEventListener('dragstart', (e) => {
570
+ dragSid = s.id;
571
+ row.classList.add('dragging');
572
+ try { e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', s.id); } catch {}
573
+ });
574
+ row.addEventListener('dragend', () => {
575
+ dragSid = null;
576
+ row.classList.remove('dragging');
577
+ els.sessList.querySelectorAll('.drag-over').forEach((x) => x.classList.remove('drag-over'));
578
+ });
579
+ row.addEventListener('dragover', (e) => {
580
+ if (!dragSid || dragSid === s.id) return;
581
+ e.preventDefault();
582
+ try { e.dataTransfer.dropEffect = 'move'; } catch {}
583
+ row.classList.add('drag-over');
584
+ });
585
+ row.addEventListener('dragleave', () => row.classList.remove('drag-over'));
586
+ row.addEventListener('drop', (e) => {
587
+ e.preventDefault();
588
+ row.classList.remove('drag-over');
589
+ if (!dragSid || dragSid === s.id) return;
590
+ reorderSessions(dragSid, s.id);
591
+ dragSid = null;
592
+ });
521
593
  els.sessList.appendChild(row);
522
594
  }
523
595
  }
524
596
 
597
+ let dragSid = null;
598
+
525
599
  async function refreshSessions() {
526
600
  await renderSessions(await beast.listSessions());
527
601
  renderBotCards(); // bot kartlarındaki numara/sayı etiketleri de tazelensin
@@ -6234,6 +6308,15 @@ function bcIngest(ev) {
6234
6308
  case 'tool-end':
6235
6309
  bcToolBoxEnd(ev.callId, ev.ok, ev.result);
6236
6310
  break;
6311
+ case 'bc-mode':
6312
+ /* OpenCode disiplini çalışma modu: başlıkta rozet + panelde bilgi satırı */
6313
+ if (els.bcTitle) {
6314
+ els.bcTitle.textContent =
6315
+ ev.mode === 'plan' ? 'BEAST CODE · PLAN' :
6316
+ ev.mode === 'build' ? 'BEAST CODE · BUILD' : 'BEAST CODE';
6317
+ }
6318
+ bcLine('t-dim', '[' + (ev.body || ev.mode || 'mod değişti') + ']');
6319
+ break;
6237
6320
  case 'done':
6238
6321
  bcFlushStream();
6239
6322
  bcCloseToolGroup();
@@ -6271,7 +6354,12 @@ function bcRunCurrent() {
6271
6354
  /* sürüyor/serbest kararını main'deki gerçek engine.isBusy verir —
6272
6355
  panel bayat kilitli kaldıysa ilk mesajla kendini düzeltir */
6273
6356
  beast.beastcodeSend(msg).then((r) => {
6274
- if (r && r.ok) {
6357
+ if (r && r.ok && r.mode) {
6358
+ /* /plan · /build · /auto: mod değişimi — tur AÇILMAZ, kilit anında açılır
6359
+ (bilgi satırı bc-mode olayıyla düşer, başlık rozeti güncellenir) */
6360
+ bcSessionId = r.sessionId;
6361
+ bcSetBusy(false);
6362
+ } else if (r && r.ok) {
6275
6363
  bcSessionId = r.sessionId;
6276
6364
  } else if (r && r.busy) {
6277
6365
  /* iş gerçekten sürüyor — kilit açılmaz, bilgi satırı düşer */
@@ -6306,6 +6394,7 @@ if (els.bcNew) els.bcNew.addEventListener('click', async () => {
6306
6394
  bcCloseToolGroup();
6307
6395
  bcHideTodos();
6308
6396
  bcFlushStream();
6397
+ if (els.bcTitle) els.bcTitle.textContent = 'BEAST CODE';
6309
6398
  bcLine('t-sys', 'yeni oturum — sonraki mesaj taze başlar');
6310
6399
  }
6311
6400
  });
@@ -119,6 +119,9 @@ body {
119
119
  }
120
120
  .sess:hover { background: var(--panel2); color: var(--text); }
121
121
  .sess.active { background: var(--panel2); color: var(--text); box-shadow: inset 2px 0 0 var(--accent); }
122
+ /* sürükle-bırak sıralama görselleri */
123
+ .sess.dragging { opacity: 0.35; }
124
+ .sess.drag-over { box-shadow: inset 0 2px 0 var(--accent); }
122
125
  .sess-wa {
123
126
  width: 15px;
124
127
  height: 15px;
@@ -934,6 +937,18 @@ body.term-open #settingsOverlay { right: var(--tw, 520px); }
934
937
  margin-bottom: 5px;
935
938
  }
936
939
  #todoPanel .td-count { letter-spacing: 0; font-weight: 700; color: var(--text); }
940
+ #todoPanel .td-right { display: flex; align-items: center; gap: 8px; }
941
+ /* sağ köşe kapatma (×): yalnız bu listeyi gizler — yeni güncellemede yine görünür */
942
+ #todoPanel .td-x {
943
+ border: none;
944
+ background: none;
945
+ color: var(--muted);
946
+ font-size: 14px;
947
+ line-height: 1;
948
+ padding: 0 3px;
949
+ cursor: pointer;
950
+ }
951
+ #todoPanel .td-x:hover { color: var(--err); }
937
952
  #todoPanel .td-row {
938
953
  display: flex;
939
954
  align-items: baseline;
@@ -286,6 +286,51 @@ test('#5 eşzamanlılık limiti: fazlası kuyrukta bekler, slot boşalınca FIFO
286
286
 
287
287
  /* ---------- İPTAL SEBEBİ DİSİPLİNİ ---------- */
288
288
 
289
+ test('üst oturum hata verirse rapor TEK SEFER düşer — sonsuz _run zinciri yok', async () => {
290
+ /* model yok → bg işi error ile biter → rapor üst oturuma flush edilir;
291
+ üst oturum da model-yok ile SYNC çökerse flush zinciri SONSUZA dönmemeli
292
+ (gerçek hata: flush → send → _run sync-throw → finally → flush … stack overflow) */
293
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-flush-'));
294
+ let runCount = 0;
295
+ const eng = new Engine({}, { sessionsDir: dir, emit: () => {} });
296
+ const origRun = Engine.prototype._run;
297
+ eng._run = function (s) { runCount++; return origRun.call(this, s); };
298
+ eng.runBackground('pflush', 'iş', 'Rapor İş');
299
+ await sleep(700);
300
+ assert.ok(runCount <= 4, 'sonsuz _run zinciri yok (runCount=' + runCount + ')');
301
+ /* rapor tam olarak 1 kez üst oturuma gönderilmiş olmalı */
302
+ eng.deleteSession('pflush');
303
+ });
304
+
305
+ test('takılı iş ZORLA kapatılır: grup tamamlanır, birleşik rapor düşer', async () => {
306
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-fc-'));
307
+ const eng = new Engine({}, { sessionsDir: dir, emit: () => {} });
308
+ const sent = [];
309
+ eng.send = (sid, payload) => { sent.push({ sid, text: String((payload && payload.text) || '') }); return true; };
310
+ const r = eng.runBackgroundMany(
311
+ 'pfc',
312
+ [
313
+ { title: 'A1', task: 'görev a' },
314
+ { title: 'B1', task: 'görev b' },
315
+ ],
316
+ 'Zorla Grup'
317
+ );
318
+ assert.ok(r.ok);
319
+ /* A1 done; B1 takılı → force-close */
320
+ await eng.reportBackgroundDone(r.ids[0]);
321
+ const stuck = eng._bgJobs.get(r.ids[1]);
322
+ stuck.fixes = 99; /* öz-kurtarma hakları tükenmiş */
323
+ eng._bgForceClose(stuck, 'test: takıldı');
324
+ await sleep(30);
325
+ const j = eng._bgJobs.get(r.ids[1]);
326
+ assert.strictEqual(j.status, 'aborted', 'zorla kapatılan iş aborted');
327
+ assert.ok(String(j.error || '').includes('test: takıldı'), 'sebep kayda geçti');
328
+ const rep = sent.find((x) => x.sid === 'pfc' && x.text.includes('[ARKA PLAN GRUP BİTTİ'));
329
+ assert.ok(rep, 'birleşik rapor düştü — grup kilitlenmedi');
330
+ assert.ok(/B1/.test(rep.text) && /aborted/.test(rep.text), 'raporda takılan üye SEBEPİYLE görünüyor');
331
+ eng.deleteSession('pfc');
332
+ });
333
+
289
334
  test('iptal SEBEBİ zorunlu: interrupt sebebi iş kaydına ve üst sohbete yazar', async () => {
290
335
  const { eng } = tmpEngine();
291
336
  const sent = [];