beast-agent 0.20.0 → 0.22.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/README.md +1 -1
- package/bin/beast-agent.js +16 -6
- package/package.json +3 -2
- package/scripts/release.js +21 -34
- package/src/agent/bots.js +17 -3
- package/src/agent/engine.js +258 -57
- package/src/main.js +332 -20
- package/src/preload.js +10 -0
- package/src/renderer/i18n.js +28 -2
- package/src/renderer/index.html +46 -1
- package/src/renderer/renderer.js +855 -10
- package/src/renderer/style.css +524 -2
package/src/agent/engine.js
CHANGED
|
@@ -83,6 +83,16 @@ const PERM_TOOL_SETS = {
|
|
|
83
83
|
};
|
|
84
84
|
const PERM_LEVELS = ['all', 'web', 'read', 'chat'];
|
|
85
85
|
|
|
86
|
+
/* İzin değerini normalize eder: 'all' → ['all'], 'web' → ['web'],
|
|
87
|
+
'web,read' / ['web','read'] → ['web','read'] (sıra PERM_LEVELS'e göre dizilir).
|
|
88
|
+
Geçersiz değerler atılır; hiçbiri kalmazsa boş dizi döner. */
|
|
89
|
+
function normalizePerms(p) {
|
|
90
|
+
const arr = Array.isArray(p) ? p.map(String) : String(p == null ? '' : p).split(',');
|
|
91
|
+
const picked = arr.map((s) => s.trim()).filter((s) => PERM_LEVELS.includes(s));
|
|
92
|
+
if (picked.includes('all')) return ['all'];
|
|
93
|
+
return PERM_LEVELS.filter((k) => picked.includes(k));
|
|
94
|
+
}
|
|
95
|
+
|
|
86
96
|
/* CEO modu: ana (konuşma) oturumunun KULLANAMAYACAĞI uygulayıcı araçlar.
|
|
87
97
|
Bunların hepsi run_background ile paralel ajana devredilir — CEO sadece
|
|
88
98
|
konuşur, planlar, emir verir ve takip eder. */
|
|
@@ -190,7 +200,7 @@ class Engine {
|
|
|
190
200
|
this.sel = this._resolve(opts.modelOverride) || this.cfg.defaultSelection || null;
|
|
191
201
|
this.roleModels = opts.roleModels || {}; // { vision?, terminal?, coding?, subagent? } // providerId::model string
|
|
192
202
|
this.lockdown = !!opts.lockdown; // varsayılan kısıt (oturum bazlı override edilmezse)
|
|
193
|
-
this.sessionPerm = new Map(); // sessionId -> 'web'|'read'|'chat' (kişi bazlı izin)
|
|
203
|
+
this.sessionPerm = new Map(); // sessionId -> ['web'] | ['web','read'] | ['chat'] (kişi/bot bazlı izin)
|
|
194
204
|
this.sessionTools = new Map(); // sessionId -> Set(araç adları) — bot skill kısıtı
|
|
195
205
|
this.resolveBot = opts.resolveBot || null; // botId -> bot bilgisi (main enjekte eder)
|
|
196
206
|
/* bot oturumu hafıza köprüsü: botun kendi SOUL/USER/MEMORY dosyaları */
|
|
@@ -285,12 +295,13 @@ class Engine {
|
|
|
285
295
|
this.lockdown = !!v;
|
|
286
296
|
}
|
|
287
297
|
|
|
288
|
-
/* Kişi bazlı granül izin — WhatsApp oturumları için.
|
|
298
|
+
/* Kişi/bot bazlı granül izin — WhatsApp oturumları için.
|
|
299
|
+
Tek seviye ('web') ya da çoklu (['web','read']) verilebilir; 'all' kaydı siler. */
|
|
289
300
|
setSessionPerm(sessionId, perm) {
|
|
290
301
|
const id = String(sessionId || '');
|
|
291
302
|
if (!id) return;
|
|
292
|
-
const
|
|
293
|
-
if (
|
|
303
|
+
const arr = normalizePerms(perm);
|
|
304
|
+
if (arr.length && !arr.includes('all')) this.sessionPerm.set(id, arr);
|
|
294
305
|
else this.sessionPerm.delete(id);
|
|
295
306
|
}
|
|
296
307
|
|
|
@@ -347,8 +358,8 @@ class Engine {
|
|
|
347
358
|
|
|
348
359
|
sessionPermFor(sessionId) {
|
|
349
360
|
const p = this.sessionPerm.get(String(sessionId || ''));
|
|
350
|
-
if (p) return p;
|
|
351
|
-
return this.lockdown ? 'chat' : 'all';
|
|
361
|
+
if (p && p.length) return p;
|
|
362
|
+
return this.lockdown ? ['chat'] : ['all'];
|
|
352
363
|
}
|
|
353
364
|
|
|
354
365
|
setRoleModels(map) {
|
|
@@ -413,32 +424,66 @@ class Engine {
|
|
|
413
424
|
return per || this.limits.default || 0;
|
|
414
425
|
}
|
|
415
426
|
|
|
416
|
-
/*
|
|
417
|
-
|
|
418
|
-
|
|
427
|
+
/* Tek mesajı token bütçesine göre kes: string/array içerik kırpılır,
|
|
428
|
+
tool_calls korunur (araç zinciri bozulmasın) */
|
|
429
|
+
_truncateMsgTokens(m, tokenBudget) {
|
|
430
|
+
if (!m) return m;
|
|
431
|
+
const cap = (txt) =>
|
|
432
|
+
String(txt).slice(0, Math.max(200, tokenBudget * 4)) + '\n…[girdi limiti için kırpıldı]';
|
|
433
|
+
if (typeof m.content === 'string') return { ...m, content: cap(m.content) };
|
|
434
|
+
if (Array.isArray(m.content)) {
|
|
435
|
+
return {
|
|
436
|
+
...m,
|
|
437
|
+
content: m.content.map((p) =>
|
|
438
|
+
p && p.type === 'text' ? { ...p, text: cap(p.text) } : p
|
|
439
|
+
),
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
return m;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* Payload limiti aşıyorsa SERT şekilde sıkıştır: system korunur, sondan
|
|
446
|
+
bağlantı pencere alınır, en yeni mesaj tek başına bile taşarsa İÇERİĞİ
|
|
447
|
+
kırpılarak tutulur, hâlâ taşarsa en eskiler atılır. Kullanıcının son
|
|
448
|
+
mesajı asla sessizce düşürülmez. */
|
|
419
449
|
_compressPayload(payload, maxTokens, withNote) {
|
|
420
450
|
if (!Array.isArray(payload) || payload.length < 2) return payload;
|
|
421
451
|
const sys = payload[0];
|
|
422
452
|
const rest = payload.slice(1);
|
|
423
|
-
const
|
|
424
|
-
const
|
|
425
|
-
|
|
453
|
+
const cost = (m) => Math.ceil(estMsgTokens(m) * this.tokRatio);
|
|
454
|
+
const sysCost = cost(sys);
|
|
455
|
+
const over = (arr) => arr.reduce((a, m) => a + cost(m), 0) + sysCost > maxTokens;
|
|
456
|
+
if (!over(rest)) return payload;
|
|
457
|
+
|
|
458
|
+
const budget = Math.max(0, maxTokens - sysCost - 48);
|
|
459
|
+
|
|
460
|
+
/* 1) sondan geriye bağlantı pencere (kesintisiz — kronoloji korunur) */
|
|
426
461
|
const picked = [];
|
|
427
462
|
let used = 0;
|
|
428
463
|
for (let i = rest.length - 1; i >= 0; i--) {
|
|
429
|
-
const
|
|
430
|
-
if (used +
|
|
431
|
-
if (picked.length) break; // sığmayan eski mesajlar atılır
|
|
432
|
-
continue; // en yeni mesaj bile sığmıyorsa onu da at (aşağıda min. 1 tutulur)
|
|
433
|
-
}
|
|
464
|
+
const c = cost(rest[i]);
|
|
465
|
+
if (used + c > budget) break;
|
|
434
466
|
picked.unshift(rest[i]);
|
|
435
|
-
used +=
|
|
467
|
+
used += c;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/* 2) hiçbir mesaj sığmadıysa: EN YENİ mesajı keserek tut */
|
|
471
|
+
if (!picked.length && rest.length) {
|
|
472
|
+
picked.push(this._truncateMsgTokens(rest[rest.length - 1], budget));
|
|
436
473
|
}
|
|
437
|
-
|
|
438
|
-
/* kopuk tool yanıtıyla başlama — assistant/tool_calls grubu bozulmasın */
|
|
474
|
+
|
|
475
|
+
/* 3) kopuk tool yanıtıyla başlama — assistant/tool_calls grubu bozulmasın */
|
|
439
476
|
while (picked.length && picked[0].role === 'tool') picked.shift();
|
|
440
|
-
if (!picked.length) picked.push(rest[rest.length - 1]);
|
|
441
|
-
|
|
477
|
+
if (!picked.length) picked.push(this._truncateMsgTokens(rest[rest.length - 1], budget));
|
|
478
|
+
|
|
479
|
+
/* 4) hâlâ limite taşıyorsa en eskilerini at */
|
|
480
|
+
while (picked.length > 1) {
|
|
481
|
+
const t = picked.reduce((a, m) => a + cost(m), 0) + sysCost + 48;
|
|
482
|
+
if (t <= maxTokens) break;
|
|
483
|
+
picked.shift();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
if (picked.length >= rest.length && !over(rest)) return payload; // zaten sığıyordu
|
|
442
487
|
if (withNote) {
|
|
443
488
|
const dropped = rest.length - picked.length;
|
|
444
489
|
picked.unshift({
|
|
@@ -516,32 +561,53 @@ class Engine {
|
|
|
516
561
|
return list;
|
|
517
562
|
}
|
|
518
563
|
|
|
519
|
-
/* Adayları sırayla dene. Hata olursa durumu kaydet, zincirdeki sonrakine geç.
|
|
564
|
+
/* Adayları sırayla dene. Hata olursa durumu kaydet, zincirdeki sonrakine geç.
|
|
565
|
+
413/TPM "Requested N" hatası → gerçek token sayısı öğrenilir, tahmin
|
|
566
|
+
kalibre edilir ve aynı model BİR KEZ daha sıkı sıkıştırmayla denenir. */
|
|
520
567
|
async _streamWithFallbacks(session, payload, activeTools, signal, onDelta, sel, wasVision) {
|
|
521
568
|
const cands = this._chatCandidates(sel, wasVision);
|
|
522
569
|
let lastErr = null;
|
|
523
570
|
for (let i = 0; i < cands.length; i++) {
|
|
524
571
|
const c = cands[i];
|
|
572
|
+
let retried413 = false;
|
|
525
573
|
try {
|
|
526
|
-
/* provider bazlı girdi limiti: payload aşıyorsa sıkıştırılıp öyle gönderilir
|
|
574
|
+
/* provider bazlı girdi limiti: payload aşıyorsa sıkıştırılıp öyle gönderilir.
|
|
575
|
+
Tahmin: mesajlar + TOOLS şemaları (sağlayıcı onları da sayar) + %30 pay.
|
|
576
|
+
Hedef %15 tamponlu — katı sağlayıcılar (TPM/413) için limit aşılmasın */
|
|
527
577
|
let msgs = payload;
|
|
528
578
|
const lim = this._inputLimitFor(c);
|
|
579
|
+
const toolsCost =
|
|
580
|
+
activeTools && activeTools.length
|
|
581
|
+
? Math.ceil(estTokens(JSON.stringify(activeTools)) * 1.1)
|
|
582
|
+
: 0;
|
|
583
|
+
const rawNow = payload.reduce((a, m) => a + estMsgTokens(m), 0) + toolsCost;
|
|
529
584
|
if (lim > 0) {
|
|
530
|
-
const est = Math.ceil(
|
|
585
|
+
const est = Math.ceil(rawNow * this.tokRatio * 1.3);
|
|
531
586
|
if (est > lim) {
|
|
532
587
|
const before = msgs.length;
|
|
533
|
-
|
|
588
|
+
const target = Math.max(600, Math.round(lim * 0.85) - toolsCost);
|
|
589
|
+
msgs = this._compressPayload(payload, target, this.limits.compress);
|
|
534
590
|
if (i === 0) {
|
|
535
591
|
emitSafe(this, session.id, {
|
|
536
592
|
type: 'status',
|
|
537
593
|
status: `bağlam sıkıştırıldı: ~${est} token → limit ${lim} (${c.providerName}, ${before}→${msgs.length} mesaj)`,
|
|
538
594
|
});
|
|
539
595
|
}
|
|
596
|
+
/* system + araçlar tek başına limiti aşıyorsa kullanıcıya net söyle */
|
|
597
|
+
const floor = Math.ceil(
|
|
598
|
+
(estMsgTokens(msgs[0]) + toolsCost) * this.tokRatio
|
|
599
|
+
);
|
|
600
|
+
if (floor > lim) {
|
|
601
|
+
emitSafe(this, session.id, {
|
|
602
|
+
type: 'status',
|
|
603
|
+
status: `⚠ girdi limiti (${lim}) çok düşük: system + araç tanımları tek başına ~${floor} token — limiti en az ${Math.ceil(floor * 1.15)} yap`,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
540
606
|
}
|
|
541
607
|
}
|
|
542
608
|
const res = await chatStream(
|
|
543
609
|
c,
|
|
544
|
-
{ messages: msgs, tools: activeTools, reasoningEffort: this.
|
|
610
|
+
{ messages: msgs, tools: activeTools, reasoningEffort: this._thinkEffortFor(session) },
|
|
545
611
|
{
|
|
546
612
|
signal,
|
|
547
613
|
onDelta,
|
|
@@ -571,6 +637,23 @@ class Engine {
|
|
|
571
637
|
if (e && (e.name === 'AbortError' || (signal && signal.aborted))) throw e;
|
|
572
638
|
lastErr = e;
|
|
573
639
|
const msgStr = String((e && e.message) || '');
|
|
640
|
+
/* 413 TPM: sağlayıcı gerçek istek boyutunu söylüyor → kalibre et,
|
|
641
|
+
aynı modeli yeniden sıkıştırıp BİR KEZ daha dene */
|
|
642
|
+
const m413 = /Requested\s+([\d,]+)/i.exec(msgStr) || (e && e.status === 413 ? [, 0] : null);
|
|
643
|
+
if (m413 && !retried413) {
|
|
644
|
+
retried413 = true;
|
|
645
|
+
const real = Number(String(m413[1] || '0').replace(/,/g, ''));
|
|
646
|
+
if (real > 0) {
|
|
647
|
+
const r = clamp(real / Math.max(1, rawNow), 0.75, 3);
|
|
648
|
+
if (r > this.tokRatio) this.tokRatio = r; /* tahmin yukarı kilitlenir */
|
|
649
|
+
emitSafe(this, session.id, {
|
|
650
|
+
type: 'status',
|
|
651
|
+
status: `⇩ 413 kalibrasyonu: gerçek ${real} token — daha sıkı sıkıştırıp yeniden deneniyor`,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
i--; /* aynı adayı tekrar dene */
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
574
657
|
const imgIssue =
|
|
575
658
|
wasVision &&
|
|
576
659
|
([400, 404, 422].includes(e.status) ||
|
|
@@ -658,6 +741,13 @@ class Engine {
|
|
|
658
741
|
this.workspace = dir;
|
|
659
742
|
}
|
|
660
743
|
|
|
744
|
+
/* Oturum bazlı çalışma klasörü — Beast Code gibi özel oturumlar sol paneldeki
|
|
745
|
+
klasörde çalışabilsin diye (sess.workspace yoksa global workspace) */
|
|
746
|
+
_sessionWorkspace(sessionId) {
|
|
747
|
+
const s = sessionId ? this.cache.get(String(sessionId)) : null;
|
|
748
|
+
return (s && s.workspace) || this.workspace;
|
|
749
|
+
}
|
|
750
|
+
|
|
661
751
|
setThinkLevel(v) {
|
|
662
752
|
this.thinkLevel = Math.min(5, Math.max(0, Math.round(Number(v) || 0)));
|
|
663
753
|
return this.thinkLevel;
|
|
@@ -668,6 +758,13 @@ class Engine {
|
|
|
668
758
|
return lv.effort || null;
|
|
669
759
|
}
|
|
670
760
|
|
|
761
|
+
/* Oturum bazlı düşünme çabası: Beast Code hız modunda en fazla 'low' */
|
|
762
|
+
_thinkEffortFor(session) {
|
|
763
|
+
const e = this._thinkEffort();
|
|
764
|
+
if (!session || !session.bcCode) return e;
|
|
765
|
+
return e ? 'low' : null;
|
|
766
|
+
}
|
|
767
|
+
|
|
671
768
|
_thinkLabel() {
|
|
672
769
|
const lv = THINK_LEVELS[this.thinkLevel] || THINK_LEVELS[0];
|
|
673
770
|
return lv.label;
|
|
@@ -716,6 +813,27 @@ class Engine {
|
|
|
716
813
|
if (session.code) this._codeIndex.set(session.code, session.id);
|
|
717
814
|
}
|
|
718
815
|
|
|
816
|
+
/* Oturum dosyasındaki son 'msg' satırını (user) güncel içerikle değiştir —
|
|
817
|
+
yanıtsız kalmış user mesajıyla yeni mesaj birleştirildiğinde kullanılır */
|
|
818
|
+
_rewriteLastMsg(id, content) {
|
|
819
|
+
try {
|
|
820
|
+
const file = this._file(String(id));
|
|
821
|
+
const lines = fs.readFileSync(file, 'utf8').split('\n');
|
|
822
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
823
|
+
if (!lines[i].trim()) continue;
|
|
824
|
+
let r;
|
|
825
|
+
try { r = JSON.parse(lines[i]); } catch { continue; }
|
|
826
|
+
if (r.t === 'msg' && r.role === 'user') {
|
|
827
|
+
lines[i] = JSON.stringify({ t: 'msg', role: 'user', content: String(content || '') });
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
const tmp = file + '.tmp';
|
|
832
|
+
fs.writeFileSync(tmp, lines.join('\n'));
|
|
833
|
+
fs.renameSync(tmp, file);
|
|
834
|
+
} catch {}
|
|
835
|
+
}
|
|
836
|
+
|
|
719
837
|
_load(id) {
|
|
720
838
|
if (this.cache.has(id)) return this.cache.get(id);
|
|
721
839
|
const file = this._file(id);
|
|
@@ -822,7 +940,7 @@ class Engine {
|
|
|
822
940
|
for (const v of this.listSessions()) {
|
|
823
941
|
const s = this._load(v.id);
|
|
824
942
|
if (s && s.notes && String(s.notes).trim()) {
|
|
825
|
-
out.push({ id: s.id, code: s.code || '', title: v.title, updatedAt: v.updatedAt, count: v.count, notes: String(s.notes) });
|
|
943
|
+
out.push({ id: s.id, code: s.code || '', title: v.title, updatedAt: v.updatedAt, count: v.count, notes: String(s.notes), botId: s.botId || '' });
|
|
826
944
|
}
|
|
827
945
|
}
|
|
828
946
|
return out;
|
|
@@ -1001,6 +1119,28 @@ class Engine {
|
|
|
1001
1119
|
|
|
1002
1120
|
/* ---------- prompt assembly (the frugal part) ---------- */
|
|
1003
1121
|
|
|
1122
|
+
/* Beast Code (IDE paneli): VS Code hızında çalışsın diye SECMET prompt.
|
|
1123
|
+
Hafıza embedding araması, skills taraması, kişilik/kural blokları YOK —
|
|
1124
|
+
prompt kısa kalır → ilk token hızlı, tur maliyeti düşük. */
|
|
1125
|
+
buildBcSystem(session) {
|
|
1126
|
+
const nowD = new Date();
|
|
1127
|
+
const localDate = nowD.toLocaleDateString('tr-TR');
|
|
1128
|
+
const localTime = nowD.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
|
|
1129
|
+
return (
|
|
1130
|
+
'Sen BEAST CODE\u2019sun — IDE panelinde çalışan hızlı bir kodlama ajanı. VS Code gibi çevik ol.\n' +
|
|
1131
|
+
`Çalışma klasörü: ${(session && session.workspace) || this.workspace}\n` +
|
|
1132
|
+
`Yerel zaman: ${localDate} ${localTime}\n` +
|
|
1133
|
+
'HIZ KURALLARI:\n' +
|
|
1134
|
+
'- İ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' +
|
|
1135
|
+
'- 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' +
|
|
1136
|
+
'- Kurulum, test, build, git gibi işler için run_command kullan (Windows PowerShell ortamı).\n' +
|
|
1137
|
+
'- Bağımsız araç çağrılarını AYNI TURDA paralel ver.\n' +
|
|
1138
|
+
'- Dosyayı kullanıcı söylememişse list_dir ile yapıyı görüp kendin karar ver.\n' +
|
|
1139
|
+
'- Uzun açıklama yok: doğrudan çalış, bitince 1-3 satırlık kısa özet ver. Soru sorma, sohbet etme.\n' +
|
|
1140
|
+
FORMAT_RULES
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1004
1144
|
buildSystem(queryText, session) {
|
|
1005
1145
|
/* BOT OTURUMU: non-admin bota bağlıysa global Beast hafızası HIÇ girmez —
|
|
1006
1146
|
o botun kendi SOUL/USER/MEMORY dosyaları kullanılır (tam izolasyon) */
|
|
@@ -1041,6 +1181,7 @@ class Engine {
|
|
|
1041
1181
|
'# SKILLS\nKullanmadan önce ilgili SKILL.md dosyasını read_file ile oku.\n' + sk.join('\n')
|
|
1042
1182
|
);
|
|
1043
1183
|
}
|
|
1184
|
+
/* Beast Code (bcCode) oturumları buildBcSystem kullanır — buraya düşmez */
|
|
1044
1185
|
const nowD = new Date();
|
|
1045
1186
|
const localDate = nowD.toLocaleDateString('tr-TR');
|
|
1046
1187
|
const localTime = nowD.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
|
|
@@ -1049,7 +1190,7 @@ class Engine {
|
|
|
1049
1190
|
tz = Intl.DateTimeFormat().resolvedOptions().timeZone || '';
|
|
1050
1191
|
} catch {}
|
|
1051
1192
|
parts.push(
|
|
1052
|
-
`# ORTAM\nWindows + PowerShell. Çalışma klasörü: ${this.workspace}\n` +
|
|
1193
|
+
`# ORTAM\nWindows + PowerShell. Çalışma klasörü: ${(session && session.workspace) || this.workspace}\n` +
|
|
1053
1194
|
`Yerel tarih: ${localDate} (${['Pazar','Pazartesi','Salı','Çarşamba','Perşembe','Cuma','Cumartesi'][nowD.getDay()]})\n` +
|
|
1054
1195
|
`Yerel saat: ${localTime}${tz ? ` (saat dilimi: ${tz})` : ''}\n` +
|
|
1055
1196
|
'Tarih/saat gerektiren her işte bu YEREL zamanı esas al; UTC varsayma, kendin dönüşüm yapma. Bu satırlar her mesajda güncellenir.\n' +
|
|
@@ -1327,12 +1468,29 @@ class Engine {
|
|
|
1327
1468
|
}
|
|
1328
1469
|
|
|
1329
1470
|
if (!String(typeof msg.content === 'string' ? msg.content : msg.content[0].text || '').trim()) return false;
|
|
1330
|
-
/* #12 kişilik kalibrasyonu: kullanıcı cümlelerini örnek havuzuna düşür */
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1471
|
+
/* #12 kişilik kalibrasyonu: kullanıcı cümlelerini örnek havuzuna düşür (Beast Code hariç — hız) */
|
|
1472
|
+
if (!s.bcCode) {
|
|
1473
|
+
try { memory.addStyleSample(this._plainText(msg)); } catch {}
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
/* Önceki tur yanıtlanmadan kaldıysa (hata/durdurma sonrası artık user mesajı)
|
|
1477
|
+
katı sağlayıcılar art arda user mesajını reddeder: "messages illegal" (400).
|
|
1478
|
+
Yeni metni o mesajla BİRLEŞTİR — dosyadaki son satır da güncellenir. */
|
|
1479
|
+
const lastMsg = s.messages[s.messages.length - 1];
|
|
1480
|
+
if (
|
|
1481
|
+
lastMsg && lastMsg.role === 'user' &&
|
|
1482
|
+
typeof lastMsg.content === 'string' &&
|
|
1483
|
+
typeof msg.content === 'string'
|
|
1484
|
+
) {
|
|
1485
|
+
lastMsg.content = lastMsg.content + '\n' + msg.content;
|
|
1486
|
+
msg = lastMsg;
|
|
1487
|
+
this._rewriteLastMsg(s.id, lastMsg.content);
|
|
1488
|
+
} else {
|
|
1489
|
+
s.messages.push(msg);
|
|
1490
|
+
try {
|
|
1491
|
+
this._append(s, msg);
|
|
1492
|
+
} catch {}
|
|
1493
|
+
}
|
|
1336
1494
|
this.emit({ type: 'message', sessionId: s.id, message: msg });
|
|
1337
1495
|
this.emit({ type: 'sessions' });
|
|
1338
1496
|
this._run(s).catch(() => {});
|
|
@@ -1871,20 +2029,59 @@ class Engine {
|
|
|
1871
2029
|
_bgTrim(session) {
|
|
1872
2030
|
const max = Engine.BG_KEEP_MSGS;
|
|
1873
2031
|
const msgs = session.messages;
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
2032
|
+
/* bg işleri + Beast Code: uzun geçmiş payload'ı şişirip yavaşlatır */
|
|
2033
|
+
if ((!session.bgJob && !session.bcCode) || msgs.length <= max) return;
|
|
2034
|
+
let keep = msgs.slice(-max);
|
|
2035
|
+
/* GÜVENLİ SINIR: kesim bir assistant(tool_calls) + tool sonuç çiftini BÖLMESİN.
|
|
2036
|
+
Yetim 'tool' mesajı ya da sonuçları pencere dışında kalan tool_calls'lı
|
|
2037
|
+
asistan mesajı sağlayıcıda "messages parameter is illegal" (HTTP 400) verir. */
|
|
2038
|
+
while (keep.length) {
|
|
2039
|
+
const f = keep[0];
|
|
2040
|
+
if (f.role === 'tool') { keep.shift(); continue; }
|
|
2041
|
+
if (f.role === 'assistant' && Array.isArray(f.tool_calls) && f.tool_calls.length) {
|
|
2042
|
+
const need = f.tool_calls.length;
|
|
2043
|
+
let got = 0;
|
|
2044
|
+
for (let i = 1; i < keep.length && keep[i].role === 'tool' && got < need; i++) got++;
|
|
2045
|
+
if (got < need) { keep.shift(); continue; }
|
|
2046
|
+
}
|
|
2047
|
+
break;
|
|
2048
|
+
}
|
|
1877
2049
|
const firstUser = msgs.find((m) => m.role === 'user');
|
|
1878
2050
|
if (firstUser && !keep.includes(firstUser)) keep.unshift(firstUser);
|
|
1879
2051
|
session.messages = keep;
|
|
1880
2052
|
}
|
|
1881
2053
|
|
|
1882
2054
|
async _chatTurn(session, signal, onDelta, toolsList = TOOLS) {
|
|
2055
|
+
/* Beast Code: todo_write açıklaması "3+ adım" kısıtı içerir ve model küçük
|
|
2056
|
+
işlerde atlar — bcCode oturumunda HER işte İLK araç olacak şekilde değiştir */
|
|
2057
|
+
if (session && session.bcCode) {
|
|
2058
|
+
toolsList = toolsList.map((t) =>
|
|
2059
|
+
t && t.function && t.function.name === 'todo_write'
|
|
2060
|
+
? {
|
|
2061
|
+
...t,
|
|
2062
|
+
function: {
|
|
2063
|
+
...t.function,
|
|
2064
|
+
description:
|
|
2065
|
+
'Replace the visible task checklist for this chat. Beast Code session: ALWAYS call this tool as your VERY FIRST action for EVERY job (even tiny 1-step jobs) BEFORE any text or other tool, then update statuses (status:"done") as you complete each step. Keep titles short, verb-first.',
|
|
2066
|
+
},
|
|
2067
|
+
}
|
|
2068
|
+
: t
|
|
2069
|
+
);
|
|
2070
|
+
}
|
|
1883
2071
|
const promptText = this._lastUserText(session);
|
|
1884
|
-
let system = session.bgJob
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
2072
|
+
let system = session.bgJob
|
|
2073
|
+
? this.buildBgSystem(session)
|
|
2074
|
+
: session.bcCode
|
|
2075
|
+
? this.buildBcSystem(session)
|
|
2076
|
+
: this.buildSystem(promptText, session);
|
|
2077
|
+
// Granül izin: oturumun yetki seviyesine göre araç seti daraltılır.
|
|
2078
|
+
// Çoklu izin (ör. web+read) seçiliyse kümeler BİRLEŞİR — hepsinin araçları açık olur.
|
|
2079
|
+
const perms = this.sessionPermFor(session.id);
|
|
2080
|
+
let allowedSet = null;
|
|
2081
|
+
if (!perms.includes('all')) {
|
|
2082
|
+
allowedSet = new Set();
|
|
2083
|
+
for (const p of perms) for (const t of PERM_TOOL_SETS[p] || []) allowedSet.add(t);
|
|
2084
|
+
}
|
|
1888
2085
|
let activeTools = allowedSet ? toolsList.filter((t) => allowedSet.has(t.function.name)) : toolsList;
|
|
1889
2086
|
/* bot skill kısıtı: bota verilen yetkiye göre araç seti daraltılır */
|
|
1890
2087
|
const toolLimit = this.sessionTools.get(String(session.id));
|
|
@@ -1896,17 +2093,16 @@ class Engine {
|
|
|
1896
2093
|
/* CEO: uygulayıcı araçlar kapalı — her şey paralel ajana devredilir */
|
|
1897
2094
|
activeTools = activeTools.filter((t) => !CEO_EXEC_TOOLS.has(t.function.name));
|
|
1898
2095
|
}
|
|
1899
|
-
if (
|
|
2096
|
+
if (perms.length === 1 && perms[0] === 'chat') {
|
|
1900
2097
|
system +=
|
|
1901
2098
|
'\n\n# KISITLI MOD\nTüm araçların (komut, dosya, web, tarayıcı, hafıza) kapalı. Sadece yazarak cevap ver. ' +
|
|
1902
2099
|
'Bilgisayarla ilgili bir işlem istenirse bu modda yapamayacağını kibarca söyle.';
|
|
1903
|
-
} else if (
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
} else if (perm === 'read') {
|
|
2100
|
+
} else if (!perms.includes('all')) {
|
|
2101
|
+
const bits = [];
|
|
2102
|
+
if (perms.includes('web')) bits.push('web ve tarayıcı araçlarına erişimin var');
|
|
2103
|
+
if (perms.includes('read')) bits.push('bilgisayarı SADECE OKUYABİLİRSİN (klasör listeleme, dosya okuma) + web/tarayıcı');
|
|
1908
2104
|
system +=
|
|
1909
|
-
'\n\n# SINIRLI
|
|
2105
|
+
'\n\n# SINIRLI YETKİ\n' + bits.join('; ') + '. ' +
|
|
1910
2106
|
'Dosya yazma, silme ve komut çalıştırma YOK; istenirse yapamayacağını söyle.';
|
|
1911
2107
|
}
|
|
1912
2108
|
/* bot kimliği: bota bağlı oturumlarda kişilik + izolasyon kuralları */
|
|
@@ -1943,8 +2139,8 @@ class Engine {
|
|
|
1943
2139
|
if (actual > 0) {
|
|
1944
2140
|
const predicted = this._payloadTokens(payload);
|
|
1945
2141
|
if (predicted > 50) {
|
|
1946
|
-
const r = clamp(actual / predicted, 0.
|
|
1947
|
-
this.tokRatio = clamp(this.tokRatio * 0.7 + r * 0.3, 0.
|
|
2142
|
+
const r = clamp(actual / predicted, 0.75, 3);
|
|
2143
|
+
this.tokRatio = clamp(this.tokRatio * 0.7 + r * 0.3, 0.75, 3);
|
|
1948
2144
|
}
|
|
1949
2145
|
}
|
|
1950
2146
|
return res;
|
|
@@ -1975,8 +2171,10 @@ class Engine {
|
|
|
1975
2171
|
|
|
1976
2172
|
if (!res.toolCalls || !res.toolCalls.length) {
|
|
1977
2173
|
// cevap tamam — pencereden düşecek kısmın oturum notlarını güncelle
|
|
1978
|
-
/* #21 bg ajanı: görev sonu ekstra LLM çağrıları YOK — hızlı kapansın
|
|
1979
|
-
|
|
2174
|
+
/* #21 bg ajanı: görev sonu ekstra LLM çağrıları YOK — hızlı kapansın.
|
|
2175
|
+
Beast Code (bcCode) oturumları da aynı şekilde ANINDA kapansın:
|
|
2176
|
+
not/hafıza/skill yansıtma çağrıları yapılmaz → done hemen gelir */
|
|
2177
|
+
if (!session.bgJob && !session.bcCode) {
|
|
1980
2178
|
const r = await this._updateSessionNotes(session, ctrl.signal);
|
|
1981
2179
|
if (r && r.ok) {
|
|
1982
2180
|
emitSafe(this, sid, { type: 'status', status: `oturum notları güncellendi (${session.code || ''})` });
|
|
@@ -1985,13 +2183,13 @@ class Engine {
|
|
|
1985
2183
|
}
|
|
1986
2184
|
}
|
|
1987
2185
|
/* otomatik memory döngüsü: kalıcı değerli bilgiyi MEMORY.md'ye düşür */
|
|
1988
|
-
if (!ctrl.signal.aborted && !session.bgJob) {
|
|
2186
|
+
if (!ctrl.signal.aborted && !session.bgJob && !session.bcCode) {
|
|
1989
2187
|
try {
|
|
1990
2188
|
await this._autoMemory(session, ctrl.signal);
|
|
1991
2189
|
} catch {}
|
|
1992
2190
|
}
|
|
1993
2191
|
/* deneyimden skill doğurma (#2): 5+ yeni araç çağrısı biriktiyse yansıt */
|
|
1994
|
-
if (this.reflection.enabled && !ctrl.signal.aborted && !session.bgJob) {
|
|
2192
|
+
if (this.reflection.enabled && !ctrl.signal.aborted && !session.bgJob && !session.bcCode) {
|
|
1995
2193
|
try {
|
|
1996
2194
|
const made = await this._maybeReflectSkill(session);
|
|
1997
2195
|
if (made) emitSafe(this, sid, { type: 'status', status: `skill taslağı doğdu: ${made}` });
|
|
@@ -2625,7 +2823,7 @@ class Engine {
|
|
|
2625
2823
|
}
|
|
2626
2824
|
if (!r || !r.ok || !(r.results || []).length) {
|
|
2627
2825
|
try {
|
|
2628
|
-
r = JSON.parse(await tools.exec('web_search', args, { cwd: this.
|
|
2826
|
+
r = JSON.parse(await tools.exec('web_search', args, { cwd: this._sessionWorkspace(sessionId), signal }));
|
|
2629
2827
|
} catch {}
|
|
2630
2828
|
}
|
|
2631
2829
|
return JSON.stringify(r || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' });
|
|
@@ -2672,7 +2870,7 @@ class Engine {
|
|
|
2672
2870
|
name === 'web_search' || name === 'http_fetch' || name === 'python_run')) {
|
|
2673
2871
|
return JSON.stringify({ ok: false, error: `unknown tool ${name}` });
|
|
2674
2872
|
}
|
|
2675
|
-
return await tools.exec(name, args, { cwd: this.
|
|
2873
|
+
return await tools.exec(name, args, { cwd: this._sessionWorkspace(sessionId), signal });
|
|
2676
2874
|
} catch (e) {
|
|
2677
2875
|
return JSON.stringify({ ok: false, error: String((e && e.message) || e) });
|
|
2678
2876
|
}
|
|
@@ -3301,3 +3499,6 @@ module.exports.sanitizeTodoItems = sanitizeTodoItems;
|
|
|
3301
3499
|
module.exports.parseReflectionJson = parseReflectionJson;
|
|
3302
3500
|
module.exports.CEO_EXEC_TOOLS = CEO_EXEC_TOOLS;
|
|
3303
3501
|
module.exports.BG_HIDDEN_TOOLS = BG_HIDDEN_TOOLS;
|
|
3502
|
+
module.exports.PERM_TOOL_SETS = PERM_TOOL_SETS;
|
|
3503
|
+
module.exports.PERM_LEVELS = PERM_LEVELS;
|
|
3504
|
+
module.exports.normalizePerms = normalizePerms;
|