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 +1 -1
- package/src/agent/engine.js +94 -15
- package/src/agent/skills.js +85 -27
- package/src/main.js +26 -0
- package/src/preload.js +2 -0
- package/src/renderer/i18n.js +4 -0
- package/src/renderer/renderer.js +92 -3
- package/src/renderer/style.css +15 -0
- package/tests/bg-jobs.test.js +45 -0
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -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(
|
|
1181
|
+
const c = this.ctrls.get(sid);
|
|
1178
1182
|
if (c) {
|
|
1179
1183
|
this._abortReasons = this._abortReasons || new Map();
|
|
1180
|
-
this._abortReasons.set(
|
|
1184
|
+
this._abortReasons.set(sid, 'oturum silindiği için tur iptal edildi');
|
|
1181
1185
|
c.abort();
|
|
1182
|
-
this.ctrls.delete(
|
|
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
|
-
'-
|
|
1228
|
-
'-
|
|
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
|
-
'-
|
|
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'
|
|
2002
|
-
|
|
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
|
|
2143
|
-
|
|
2144
|
-
|
|
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
|
|
package/src/agent/skills.js
CHANGED
|
@@ -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
|
|
67
|
-
version:
|
|
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
|
|
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
|
-
|
|
98
|
+
**"Cron/izleyici var mı"** → \`cron.json\`, \`watchers.json\`.
|
|
73
99
|
|
|
74
|
-
##
|
|
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
|
|
79
|
-
| \`settings.json\` | App ayarları: theme, workspace, modelOverride, customProviders, roleModels, deletedModels, fallout, waTts
|
|
80
|
-
| \`sessions
|
|
81
|
-
| \`
|
|
82
|
-
| \`
|
|
83
|
-
| \`
|
|
84
|
-
| \`
|
|
85
|
-
| \`
|
|
86
|
-
| \`wa-chats.json\`, \`wa.log\` |
|
|
87
|
-
| \`
|
|
88
|
-
| \`
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
100
|
-
- Yeni yetenek gerekiyorsa \`%APPDATA%\\beast\\skills\\<ad
|
|
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'),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -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)',
|
package/src/renderer/renderer.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
});
|
package/src/renderer/style.css
CHANGED
|
@@ -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;
|
package/tests/bg-jobs.test.js
CHANGED
|
@@ -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 = [];
|