beast-agent 0.23.7 → 0.23.9
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 +62 -8
- package/src/agent/skills.js +28 -0
- package/src/main.js +13 -0
- package/src/preload.js +2 -0
- package/src/renderer/i18n.js +2 -0
- package/src/renderer/renderer.js +25 -1
- package/src/renderer/style.css +13 -2
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -220,7 +220,10 @@ class Engine {
|
|
|
220
220
|
this.tokRatio = 1; // gerçek prompt_tokens ile kalibre edilir
|
|
221
221
|
this._codeIndex = new Map(); // kısa oturum kodu -> session id
|
|
222
222
|
/* yansıma: oturumda 5+ yeni araç çağrısında skill taslağı denenir */
|
|
223
|
-
this.reflection = { enabled: opts.reflection !== false, minTools:
|
|
223
|
+
this.reflection = { enabled: opts.reflection !== false, minTools: 3 };
|
|
224
|
+
/* OTOMATİK SKİLL SİSTEMİ: öğrenilen prosedür taslak onayı beklemeden
|
|
225
|
+
kurulu skill olur; mevcut skillin daha iyisi bulunursa üzerine günceller */
|
|
226
|
+
this.autoSkills = opts.autoSkills !== false;
|
|
224
227
|
this.historyTokenBudget = Number(opts.historyTokenBudget) || HISTORY_TOKEN_BUDGET;
|
|
225
228
|
this.browser = opts.browser || null; // dahili tarayıcı kancaları
|
|
226
229
|
this.fileSend = opts.fileSend || null; // #26 dosya gönderim köprüsü (chat/WA)
|
|
@@ -1014,11 +1017,27 @@ class Engine {
|
|
|
1014
1017
|
const firstUser = s.messages.find((m) => m.role === 'user');
|
|
1015
1018
|
const isBg = !!s.bgTitle;
|
|
1016
1019
|
const job = isBg ? this._bgJobs.get(String(s.id)) : null;
|
|
1020
|
+
/* Başlık: ilk user mesajının METNİ. Ekli mesajlarda content dizidir —
|
|
1021
|
+
String() doğrudan "[object Object]" üretir; text parçaları birleştirilir.
|
|
1022
|
+
WhatsApp/Telegram transport etiketi ([WhatsApp — gönderen: ...]) başlıkta
|
|
1023
|
+
gürültüdür — kırpılır, gerçek konuşma metni kalsın. */
|
|
1024
|
+
let title = '';
|
|
1025
|
+
if (!isBg && firstUser) {
|
|
1026
|
+
let t = Array.isArray(firstUser.content)
|
|
1027
|
+
? firstUser.content.filter((p) => p && p.type === 'text').map((p) => String(p.text || '')).join(' ')
|
|
1028
|
+
: String(firstUser.content || '');
|
|
1029
|
+
t = t
|
|
1030
|
+
.replace(/^\s*\[(WhatsApp|Telegram)[^\]]*\]\s*/i, '')
|
|
1031
|
+
.replace(/\[görsel gösterilemedi[^\]]*\]/gi, '')
|
|
1032
|
+
.replace(/\[(resim eki alındı|belge alındı[^\]]*|sesli mesaj[^\]]*)\]/gi, '')
|
|
1033
|
+
.trim();
|
|
1034
|
+
title = (t.split('\n')[0] || '').replace(/\s+/g, ' ').trim().slice(0, 48);
|
|
1035
|
+
}
|
|
1017
1036
|
return {
|
|
1018
1037
|
id: s.id,
|
|
1019
1038
|
code: s.code || '',
|
|
1020
1039
|
botId: s.botId || null,
|
|
1021
|
-
title: (isBg ? s.bgTitle :
|
|
1040
|
+
title: (isBg ? s.bgTitle : title) || 'Yeni Sohbet',
|
|
1022
1041
|
createdAt: s.createdAt,
|
|
1023
1042
|
updatedAt: s.updatedAt,
|
|
1024
1043
|
count: s.messages.length,
|
|
@@ -2463,12 +2482,25 @@ class Engine {
|
|
|
2463
2482
|
);
|
|
2464
2483
|
if (transcript.length < 400) return null; // çok kısa deneyim değmez
|
|
2465
2484
|
|
|
2485
|
+
/* OTOMATİK SKİLL SİSTEMİ: kurulu skill listesi de prompta girer —
|
|
2486
|
+
ajan "eski skillden daha kolay/better yol buldum" diyebilmesin, KARAR VERİP
|
|
2487
|
+
skilli GÜNCELLESİN. action: create (yeni) | update (mevcutu iyileştir) | none */
|
|
2488
|
+
const skills = require('./skills');
|
|
2489
|
+
const existing = skills
|
|
2490
|
+
.scan()
|
|
2491
|
+
.map((s) => `- ${s.name}: ${(s.description || '').slice(0, 100)}`)
|
|
2492
|
+
.join('\n');
|
|
2493
|
+
|
|
2466
2494
|
const prompt =
|
|
2467
|
-
'Aşağıdaki agent oturumunda TEKRARLANABİLİR, yeniden kullanılabilir bir prosedür/bilgi ' +
|
|
2468
|
-
'
|
|
2495
|
+
'Aşağıdaki agent oturumunda TEKRARLANABİLİR, yeniden kullanılabilir bir prosedür/bilgi birikti mi?\n' +
|
|
2496
|
+
'Bir kurulu skill, bu oturumda öğrenilen DAHA KOLAY/DAHA İYİ yöntemle güncellenmeyi hak ediyor mu?\n' +
|
|
2497
|
+
'Kullanıcıya özel geçici detaylar (tarih, şehir, numara gibi) skill\u2019e YAZILMAZ — genel yöntem yazılır.\n\n' +
|
|
2498
|
+
'# KURULU SKİLLER\n' + (existing || '(yok)') + '\n\n' +
|
|
2469
2499
|
'SADECE şu JSON formatında cevap ver, başka metin yok:\n' +
|
|
2470
|
-
'{"
|
|
2471
|
-
'
|
|
2500
|
+
'{"action": "none" | "create" | "update", "name": "kisa-kebab-ad", "description": "tek cümle ne işe yarar", "body": "# Başlık\\n\\nmaddeler halinde adım adım prosedür"}\n\n' +
|
|
2501
|
+
'action=create → yeni skill (body: baştan sona tam prosedür)\n' +
|
|
2502
|
+
'action=update → "name" MEVCUT skillin adı; body o skillin GÜNCELLENMİŞ TAM HALI (eskiden iyi olan adımları koru, yeni kolaylığı işle)\n' +
|
|
2503
|
+
'action=none → değerlenecek bir şey yok\n\n' +
|
|
2472
2504
|
'# OTURUM ÖZETİ\n' + transcript.slice(0, 6000);
|
|
2473
2505
|
|
|
2474
2506
|
const res = await chatOnce(
|
|
@@ -2477,8 +2509,30 @@ class Engine {
|
|
|
2477
2509
|
{}
|
|
2478
2510
|
);
|
|
2479
2511
|
const draft = parseReflectionJson(res.content || '');
|
|
2480
|
-
if (!draft || !draft.
|
|
2481
|
-
|
|
2512
|
+
if (!draft || !draft.name || !draft.body) return null;
|
|
2513
|
+
|
|
2514
|
+
const action = String(draft.action || (draft.create ? 'create' : 'none')).toLowerCase();
|
|
2515
|
+
if (action !== 'create' && action !== 'update') return null;
|
|
2516
|
+
|
|
2517
|
+
if (action === 'update') {
|
|
2518
|
+
/* mevcut skillin GÜNCELLENMİŞ hali — doğrudan üzerine (eski hali .bak) */
|
|
2519
|
+
const r = skills.upsertSkill({
|
|
2520
|
+
name: String(draft.name),
|
|
2521
|
+
description: String(draft.description || ''),
|
|
2522
|
+
body: String(draft.body),
|
|
2523
|
+
});
|
|
2524
|
+
return r.ok ? skills.slugify(draft.name) + ' (güncellendi)' : null;
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
/* create: otomatik skill modu AÇIKSA taslak beklemeden direkt kur */
|
|
2528
|
+
if (this.autoSkills) {
|
|
2529
|
+
const r = skills.upsertSkill({
|
|
2530
|
+
name: String(draft.name),
|
|
2531
|
+
description: String(draft.description || ''),
|
|
2532
|
+
body: String(draft.body),
|
|
2533
|
+
});
|
|
2534
|
+
return r.ok ? skills.slugify(draft.name) : null;
|
|
2535
|
+
}
|
|
2482
2536
|
const r = skills.addDraft({
|
|
2483
2537
|
name: String(draft.name),
|
|
2484
2538
|
description: String(draft.description || ''),
|
package/src/agent/skills.js
CHANGED
|
@@ -487,6 +487,33 @@ function dropDraft(id) {
|
|
|
487
487
|
}
|
|
488
488
|
}
|
|
489
489
|
|
|
490
|
+
/* OTOMATIK SKILL SİSTEMİ: yansımadan doğan prosedürü DOĞRUDAN kurulu skill olarak
|
|
491
|
+
yazar (taslak onayı beklemeden). Aynı isimde skill varsa GÜNCELLENİR:
|
|
492
|
+
eski içerik .bak'a alınır, created korunur, updated damgası vurulur. */
|
|
493
|
+
function upsertSkill({ name, description, body }) {
|
|
494
|
+
try {
|
|
495
|
+
const nm = String(name || '').trim() || 'yetenek';
|
|
496
|
+
const folder = slugify(nm);
|
|
497
|
+
const d = path.join(dir(), folder);
|
|
498
|
+
const file = path.join(d, 'SKILL.md');
|
|
499
|
+
fs.mkdirSync(d, { recursive: true });
|
|
500
|
+
let fm = `---\nname: ${nm}\ndescription: ${String(description || '').trim().slice(0, 160)}\n`;
|
|
501
|
+
let updated = false;
|
|
502
|
+
if (fs.existsSync(file)) {
|
|
503
|
+
const old = fs.readFileSync(file, 'utf8');
|
|
504
|
+
const oldFm = parseFrontmatter(old);
|
|
505
|
+
fm += `created: ${oldFm.created || oldFm.generatedAt || new Date().toISOString()}\n`;
|
|
506
|
+
fs.writeFileSync(file + '.bak', old);
|
|
507
|
+
updated = true;
|
|
508
|
+
}
|
|
509
|
+
fm += `updated: ${new Date().toISOString()}\n---\n\n`;
|
|
510
|
+
fs.writeFileSync(file, fm + String(body || '').trim() + '\n');
|
|
511
|
+
return { ok: true, folder, file, updated };
|
|
512
|
+
} catch (e) {
|
|
513
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
490
517
|
/* Kurulu bir skill'e madde ekler (#3 kural hattı): "## Kurallar" altına yazılır. */
|
|
491
518
|
function appendRuleToSkill(nameOrFolder, ruleText) {
|
|
492
519
|
try {
|
|
@@ -520,4 +547,5 @@ module.exports = {
|
|
|
520
547
|
acceptDraft,
|
|
521
548
|
dropDraft,
|
|
522
549
|
appendRuleToSkill,
|
|
550
|
+
upsertSkill,
|
|
523
551
|
};
|
package/src/main.js
CHANGED
|
@@ -2071,6 +2071,9 @@ function reloadBackend() {
|
|
|
2071
2071
|
thinkLevel: settings.thinkLevel || 0,
|
|
2072
2072
|
fallout: settings.fallout || null,
|
|
2073
2073
|
limits: settings.limits || null,
|
|
2074
|
+
/* OTOMATİK SKİLL SİSTEMİ: ayarlardan kapatılmadıysa öğrenilen prosedürler
|
|
2075
|
+
otomatik skill olur, mevcutların daha iyisi bulunursa güncellenir */
|
|
2076
|
+
autoSkills: settings.autoSkills !== false,
|
|
2074
2077
|
approvals: settings.security && settings.security.approvals ? approvalsBridge : null,
|
|
2075
2078
|
alwaysAllowTools: (settings.security && settings.security.alwaysAllow) || [],
|
|
2076
2079
|
crashFile: FALLOUT_CRASH_FILE,
|
|
@@ -3828,6 +3831,16 @@ ipcMain.handle('memory:save', (_e, { file, content }) => memory.save(file, conte
|
|
|
3828
3831
|
|
|
3829
3832
|
ipcMain.handle('skills:list', () => skillsMod.scan());
|
|
3830
3833
|
|
|
3834
|
+
/* OTOMATİK SKİLL SİSTEMİ: açıkken öğrenilen prosedürler direkt kurulur,
|
|
3835
|
+
mevcut skillin daha iyisi bulunursa güncellenir */
|
|
3836
|
+
ipcMain.handle('skills:auto:get', () => settings.autoSkills !== false);
|
|
3837
|
+
ipcMain.handle('skills:auto:set', (_e, v) => {
|
|
3838
|
+
settings.autoSkills = !!v;
|
|
3839
|
+
saveSettings();
|
|
3840
|
+
if (engine) engine.autoSkills = !!v;
|
|
3841
|
+
return settings.autoSkills;
|
|
3842
|
+
});
|
|
3843
|
+
|
|
3831
3844
|
/* taslak skill'ler (#2 yansıma ürünleri) */
|
|
3832
3845
|
ipcMain.handle('skills:drafts:list', () => skillsMod.listDrafts());
|
|
3833
3846
|
ipcMain.handle('skills:drafts:accept', (_e, id) => {
|
package/src/preload.js
CHANGED
|
@@ -25,6 +25,8 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
25
25
|
clearNotes: (id) => ipcRenderer.invoke('notes:clear', id),
|
|
26
26
|
saveMemory: (file, content) => ipcRenderer.invoke('memory:save', { file, content }),
|
|
27
27
|
listSkills: () => ipcRenderer.invoke('skills:list'),
|
|
28
|
+
skillsGetAuto: () => ipcRenderer.invoke('skills:auto:get'),
|
|
29
|
+
skillsSetAuto: (v) => ipcRenderer.invoke('skills:auto:set', v),
|
|
28
30
|
openSkillsFolder: () => ipcRenderer.invoke('skills:openFolder'),
|
|
29
31
|
draftsList: () => ipcRenderer.invoke('skills:drafts:list'),
|
|
30
32
|
draftAccept: (id) => ipcRenderer.invoke('skills:drafts:accept', id),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -164,6 +164,7 @@
|
|
|
164
164
|
mem_saved: 'Memory kaydedildi — SOUL.md dahil',
|
|
165
165
|
sk_h2: 'Skills',
|
|
166
166
|
sk_sub: 'SKILL.md indeksi — tam metin model tarafından okunur',
|
|
167
|
+
sk_auto: 'Otomatik skill: ajan öğrendiği yeni yöntemleri taslak onayı beklemeden kurar; bir skillin daha kolay/daha iyi yolunu bulursa mevcut skilli kendisi günceller',
|
|
167
168
|
sk_rules_h2: 'Kalıcı Kurallar',
|
|
168
169
|
sk_rules_sub: 'Agent\u2019ın her sohbette uyduğu talimatlar — WA\u2019dan /rule ile de eklenir',
|
|
169
170
|
sk_no_rule: 'Henüz kural yok.',
|
|
@@ -678,6 +679,7 @@
|
|
|
678
679
|
mem_saved: 'Memory saved — including SOUL.md',
|
|
679
680
|
sk_h2: 'Skills',
|
|
680
681
|
sk_sub: 'SKILL.md index — full text is read by the model',
|
|
682
|
+
sk_auto: 'Auto skills: the agent installs learned procedures without draft approval and updates existing skills when it finds an easier or better way',
|
|
681
683
|
sk_rules_h2: 'Persistent Rules',
|
|
682
684
|
sk_rules_sub: 'Instructions the agent follows in every chat — also addable via /rule on WA',
|
|
683
685
|
sk_no_rule: 'No rules yet.',
|
package/src/renderer/renderer.js
CHANGED
|
@@ -458,9 +458,13 @@ function finishToolCard(callId, ok, result) {
|
|
|
458
458
|
|
|
459
459
|
async function renderSessions(list) {
|
|
460
460
|
let waSet = new Set();
|
|
461
|
+
let tgSet = new Set();
|
|
461
462
|
try {
|
|
462
463
|
waSet = new Set(await beast.waListSessions());
|
|
463
464
|
} catch {}
|
|
465
|
+
try {
|
|
466
|
+
tgSet = new Set(await beast.tgListSessions());
|
|
467
|
+
} catch {}
|
|
464
468
|
els.sessList.innerHTML = '';
|
|
465
469
|
/* aktif botun oturumları — botlar arası geçişte liste de o bota göre değişir */
|
|
466
470
|
for (const s of list) {
|
|
@@ -469,6 +473,7 @@ async function renderSessions(list) {
|
|
|
469
473
|
row.className = 'sess' + (s.id === activeId ? ' active' : '');
|
|
470
474
|
row.innerHTML =
|
|
471
475
|
(waSet.has(s.id) ? '<span class="sess-wa" title="WhatsApp">W</span>' : '') +
|
|
476
|
+
(tgSet.has(s.id) ? '<span class="sess-tg" title="Telegram">T</span>' : '') +
|
|
472
477
|
`<span class="sess-title">${escapeHtml(s.title || 'Yeni Sohbet')}</span>` +
|
|
473
478
|
`<span class="sess-code" title="Oturum kodu">${escapeHtml(s.code || '')}</span>` +
|
|
474
479
|
`<button class="sess-del" title="Sil">×</button>`;
|
|
@@ -1250,6 +1255,16 @@ async function renderFalloutPane() {
|
|
|
1250
1255
|
async function renderSkillsPane() {
|
|
1251
1256
|
const pane = $('#tab-skills');
|
|
1252
1257
|
pane.innerHTML = '<h2>' + _t('sk_h2') + '</h2><div class="sub">' + _t('sk_sub') + '</div>';
|
|
1258
|
+
/* OTOMATİK SKİLL SİSTEMİ: öğrenilen prosedürler otomatik kurulur/güncellenir */
|
|
1259
|
+
const auto = await beast.skillsGetAuto().catch(() => true);
|
|
1260
|
+
pane.insertAdjacentHTML(
|
|
1261
|
+
'afterbegin',
|
|
1262
|
+
`<div class="fo-toggles" style="margin-bottom:12px"><label class="lock-row"><input type="checkbox" id="autoSkillsOn" ${auto ? 'checked' : ''}/><span>${_t('sk_auto')}</span></label></div>`
|
|
1263
|
+
);
|
|
1264
|
+
pane.querySelector('#autoSkillsOn').addEventListener('change', async (e) => {
|
|
1265
|
+
await beast.skillsSetAuto(e.target.checked);
|
|
1266
|
+
toast((e.target.checked ? 'Otomatik skill: AÇIK' : 'Otomatik skill: KAPALI'));
|
|
1267
|
+
});
|
|
1253
1268
|
const list = await beast.listSkills();
|
|
1254
1269
|
for (const s of list) {
|
|
1255
1270
|
const row = document.createElement('div');
|
|
@@ -1619,7 +1634,9 @@ async function renderDashboardPane() {
|
|
|
1619
1634
|
let sessions = [];
|
|
1620
1635
|
try { sessions = (await beast.listSessions()) || []; } catch {}
|
|
1621
1636
|
let waSet = new Set();
|
|
1637
|
+
let tgSet = new Set();
|
|
1622
1638
|
try { waSet = new Set(await beast.waListSessions()); } catch {}
|
|
1639
|
+
try { tgSet = new Set(await beast.tgListSessions()); } catch {}
|
|
1623
1640
|
|
|
1624
1641
|
const totalMsgs = sessions.reduce((a, s) => a + (Number(s.count) || 0), 0);
|
|
1625
1642
|
const today = new Date().toDateString();
|
|
@@ -1655,6 +1672,7 @@ async function renderDashboardPane() {
|
|
|
1655
1672
|
row.className = 'usage-row' + (s.id === activeId ? ' dash-active' : '');
|
|
1656
1673
|
row.innerHTML =
|
|
1657
1674
|
(waSet.has(s.id) ? '<span class="sess-wa" title="WhatsApp">W</span>' : '') +
|
|
1675
|
+
(tgSet.has(s.id) ? '<span class="sess-tg" title="Telegram">T</span>' : '') +
|
|
1658
1676
|
`<span class="sess-code" title="Oturum kodu">${escapeHtml(s.code || '')}</span>` +
|
|
1659
1677
|
`<span style="flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">` +
|
|
1660
1678
|
`${escapeHtml(s.title || 'Yeni Sohbet')}</span>` +
|
|
@@ -2630,7 +2648,9 @@ async function renderBotChats(b) {
|
|
|
2630
2648
|
const box = $('#botChatMsgs');
|
|
2631
2649
|
box.innerHTML = '';
|
|
2632
2650
|
let waSet = new Set();
|
|
2651
|
+
let tgSet = new Set();
|
|
2633
2652
|
try { waSet = new Set(await beast.waListSessions()); } catch {}
|
|
2653
|
+
try { tgSet = new Set(await beast.tgListSessions()); } catch {}
|
|
2634
2654
|
const sessions = [];
|
|
2635
2655
|
try {
|
|
2636
2656
|
for (const s of await beast.listSessions()) {
|
|
@@ -2648,7 +2668,11 @@ async function renderBotChats(b) {
|
|
|
2648
2668
|
const row = document.createElement('div');
|
|
2649
2669
|
row.className = 'bot-chat-row';
|
|
2650
2670
|
row.innerHTML =
|
|
2651
|
-
(waSet.has(s.id)
|
|
2671
|
+
(waSet.has(s.id)
|
|
2672
|
+
? '<span class="sess-wa" title="WhatsApp">W</span>'
|
|
2673
|
+
: tgSet.has(s.id)
|
|
2674
|
+
? '<span class="sess-tg" title="Telegram">T</span>'
|
|
2675
|
+
: '<span class="sess-wa" style="opacity:.35">D</span>') +
|
|
2652
2676
|
`<span class="bt">${escapeHtml(s.title || 'Yeni Sohbet')}</span>` +
|
|
2653
2677
|
`<span class="sess-code">${escapeHtml(s.code || '')}</span>`;
|
|
2654
2678
|
row.addEventListener('click', async () => {
|
package/src/renderer/style.css
CHANGED
|
@@ -130,8 +130,19 @@ body {
|
|
|
130
130
|
display: flex;
|
|
131
131
|
align-items: center;
|
|
132
132
|
justify-content: center;
|
|
133
|
-
flex-shrink: 0;
|
|
134
133
|
}
|
|
134
|
+
.sess-tg {
|
|
135
|
+
width: 15px;
|
|
136
|
+
height: 15px;
|
|
137
|
+
border-radius: 4px;
|
|
138
|
+
background: #229ed9;
|
|
139
|
+
color: #fff;
|
|
140
|
+
font-size: 10px;
|
|
141
|
+
font-weight: 800;
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
justify-content: center;
|
|
145
|
+
}
|
|
135
146
|
.sess-title { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; }
|
|
136
147
|
.sess-code {
|
|
137
148
|
color: var(--muted);
|
|
@@ -1525,7 +1536,7 @@ body.browser-open #toast { left: calc((100vw - var(--bw, 480px)) / 2); }
|
|
|
1525
1536
|
|
|
1526
1537
|
#toast {
|
|
1527
1538
|
position: fixed;
|
|
1528
|
-
bottom:
|
|
1539
|
+
bottom: 84px; /* chat input alanının hemen üstünde */
|
|
1529
1540
|
left: 50%;
|
|
1530
1541
|
transform: translateX(-50%) translateY(16px);
|
|
1531
1542
|
opacity: 0;
|