beast-agent 1.8.0 → 2.0.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/LICENSE +21 -21
- package/README.md +130 -128
- package/bin/beast-agent.js +137 -137
- package/package.json +1 -1
- package/scripts/fix-electron.js +138 -138
- package/scripts/release.js +137 -131
- package/scripts/swap-electron.js +40 -40
- package/src/agent/agentdefs.js +122 -122
- package/src/agent/bots.js +580 -580
- package/src/agent/bus.js +389 -389
- package/src/agent/computeruse.js +200 -200
- package/src/agent/config.js +284 -284
- package/src/agent/discord.js +332 -332
- package/src/agent/engine.js +85 -11
- package/src/agent/kb.js +123 -123
- package/src/agent/llm.js +430 -430
- package/src/agent/logger.js +90 -90
- package/src/agent/mcp.js +427 -0
- package/src/agent/mem0.js +605 -605
- package/src/agent/memory.js +427 -427
- package/src/agent/mqueue.js +124 -124
- package/src/agent/pdf.js +20 -20
- package/src/agent/research.js +133 -133
- package/src/agent/scripts/news.py +113 -113
- package/src/agent/scripts/stealthsearch.py +30 -30
- package/src/agent/scripts/websearch.py +225 -225
- package/src/agent/searxng.js +325 -325
- package/src/agent/seeds/brainstorming/SKILL.md +90 -90
- package/src/agent/seeds/dispatching-parallel-agents/SKILL.md +120 -120
- package/src/agent/seeds/executing-plans/SKILL.md +60 -60
- package/src/agent/seeds/subagent-driven-development/SKILL.md +167 -167
- package/src/agent/seeds/systematic-debugging/SKILL.md +131 -131
- package/src/agent/seeds/test-driven-development/SKILL.md +152 -152
- package/src/agent/seeds/verification-before-completion/SKILL.md +63 -63
- package/src/agent/seeds/writing-plans/SKILL.md +162 -162
- package/src/agent/seeds/writing-skills/SKILL.md +229 -229
- package/src/agent/skills.js +652 -651
- package/src/agent/store.js +378 -378
- package/src/agent/telegram.js +155 -155
- package/src/agent/tokens.js +39 -39
- package/src/agent/usage.js +125 -125
- package/src/agent/watchers.js +312 -312
- package/src/agent/watext.js +80 -80
- package/src/agent/whatsapp.js +555 -555
- package/src/cron.js +255 -255
- package/src/main.js +385 -6
- package/src/preload.js +13 -0
- package/src/renderer/browserPreload.js +73 -73
- package/src/renderer/i18n.js +38 -0
- package/src/renderer/index.html +448 -407
- package/src/renderer/renderer.js +897 -42
- package/src/renderer/style.css +275 -11
package/src/agent/engine.js
CHANGED
|
@@ -14,6 +14,7 @@ const agentdefs = require('./agentdefs');
|
|
|
14
14
|
const memory = require('./memory');
|
|
15
15
|
const mem0 = require('./mem0');
|
|
16
16
|
const skills = require('./skills');
|
|
17
|
+
const mcp = require('./mcp');
|
|
17
18
|
const { estTokens, estMsgTokens } = require('./tokens');
|
|
18
19
|
const log = require('./logger');
|
|
19
20
|
|
|
@@ -1376,8 +1377,10 @@ class Engine {
|
|
|
1376
1377
|
|
|
1377
1378
|
/* Beast Code (IDE paneli): VS Code hızında çalışsın diye SECMET prompt.
|
|
1378
1379
|
OpenCode kodlama disiplini gömülü: bağlam → plan → küçük diff → doğrula.
|
|
1379
|
-
|
|
1380
|
-
|
|
1380
|
+
GENEL AMAÇLI kodlama ajanı — web/mobil gibi tek stack'e zorlanmaz; stack'i
|
|
1381
|
+
projenin kendi dosyaları belirler. Hafıza embedding araması, skills
|
|
1382
|
+
taraması, kişilik/kural blokları YOK — prompt kısa kalır → ilk token
|
|
1383
|
+
hızlı, tur maliyeti düşük. */
|
|
1381
1384
|
buildBcSystem(session) {
|
|
1382
1385
|
const nowD = new Date();
|
|
1383
1386
|
const localDate = nowD.toLocaleDateString('tr-TR');
|
|
@@ -1394,9 +1397,11 @@ class Engine {
|
|
|
1394
1397
|
const proj = this._projectInstructions(session);
|
|
1395
1398
|
return (
|
|
1396
1399
|
'Sen BEAST CODE\u2019sun — IDE panelinde çalışan, OpenCode disiplinli hızlı bir kodlama ajanı. VS Code gibi çevik ol.\n' +
|
|
1400
|
+
'GENEL AMAÇLI KODLAMA AJANISIN — tek bir proje türüne sınırlı değilsin: web, backend/API, mobil, masaüstü, CLI, kütüphane, script, oyun, veri, DevOps, gömülü… İstenen neyse o. İstenmeyen şeyi (ör. site değilken site) kendiliğinden ÜRETME.\n' +
|
|
1397
1401
|
`Çalışma klasörü: ${(session && session.workspace) || this.workspace}\n` +
|
|
1398
1402
|
`Yerel zaman: ${localDate} ${localTime}\n` +
|
|
1399
1403
|
`${modeBlock}\n` +
|
|
1404
|
+
'STACK TESPİTİ: işe başlarken workspace\u2019i tanı — package.json, go.mod, Cargo.toml, requirements.txt/pyproject.toml, pom.xml/build.gradle, *.csproj, pubspec.yaml, Gemfile, composer.json vb. işaret dosyalarına bak (list_dir + glob). Dili, framework\u2019ü, konvansiyonları ve build/test/run komutlarını PROJE belirler; her işte aynı teknolojiye itme, mevcut stack\u2019e uy.\n' +
|
|
1400
1405
|
(proj
|
|
1401
1406
|
? '# PROJE TALİMATLARI (workspace AGENTS/CLAUDE/CONTEXT dosyalarından — daima uy)\n' + proj + '\n'
|
|
1402
1407
|
: '') +
|
|
@@ -1404,12 +1409,11 @@ class Engine {
|
|
|
1404
1409
|
'1) BAĞLAM: değiştirmeden önce ilgili dosyaları OKU — read_file satır numaralı döner (N: içerik); büyük dosyada devamını offset parametresiyle oku, ASLA baştan okuma; bir dosyayı aynı oturumda BİR KEZ okumak yeter — okuduğun içerik oturum SONUNA KADAR bağlamda KALIR, edit yaptıktan sonra dosyayı yeniden okuma YASAK (güncel durum = son okuma + kendi editlerin). İçerik araması için grep (regex), dosya adı için glob kullan; varsayım yapma, mevcut stili/konvansiyonu takip et.\n' +
|
|
1405
1410
|
'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' +
|
|
1406
1411
|
'3) EDİT: VAR OLAN dosyada önce edit_file kullan (old_string/new_string ile yalnız ilgili bölümü değiştir; birden çok eşleşme varsa bağlam ekle ya da replace_all); write_file yalnız YENİ dosya ya da tam yeniden yazım için. Dosya işlemleri için ÖZEL ARAÇLARI kullan (edit_file/write_file/read_file/grep/glob); run_command terminal işlerindir (build, git, kurulum, paket) — dosya düzenlemeyi komut/scripte yedirme, edit_file ile yap. İlgisiz yeniden biçimleme/kayıp boşluk değişikliği YAPMA. edit_file/write_file sonucu additions/deletions döner ve değişiklik diske ANINDA uygulanır — doğrulamak için dosyayı TEKRAR OKUMA YASAK; sonraki editi önceki okuduğun içerik + kendi değişikliklerin üzerinden zincirle.\n' +
|
|
1407
|
-
'4) DOĞRULA: edit sonrası
|
|
1412
|
+
'4) DOĞRULA: edit sonrası projenin KENDİ komutlarıyla derle/test/lint çalıştır (run_command: npm test, go test ./..., cargo test, pytest, mvn test vb. — hangisi geçerliyse); hata varsa DÜZELT ve TEKRAR dene (en fazla 2 doğrulama turu) — kırmızı bırakma. Doğrulama read_file ile DEĞİL run_command ile yapılır.\n' +
|
|
1408
1413
|
'5) RAPOR: 1-3 satır — ne değişti + doğrulama sonucu (ör. "npm test ✓ 154/154"). Uzun açıklama yok.\n' +
|
|
1409
|
-
'
|
|
1410
|
-
'MOBİL UYGULAMA: React Native/Expo ile yap — `npx create-expo-app <ad>` + `npx expo start` (BLOKLAMADAN arka planda; başlangıç çıktısında http://localhost:8081 görünür, panel Telefon Modunda canlı açar, dosya kaydında hot reload çalışır). APK: `npx expo prebuild` + android\\gradlew assembleDebug; iOS: Windows\u2019ta simülatör YOK — gerçek cihazda Expo Go (QR) veya EAS cloud build kullan.\n' +
|
|
1414
|
+
'ÖNİZLEME: panel Preview\u2019da dahili statik sunucuyu KENDİSİ yönetir — statik HTML işinde sunucu başlatma (python -m http.server vs. GEREKMEZ); index.html\u2019i hazır bırak, kullanıcı Preview\u2019a basınca canlı açılır. Yalnız gerçek dev-server gerektiren projelerde (React/Vite/Next/Expo vb.) kendi sunucunu BLOKLAMADAN arka planda başlat ve çalışan adresi (http://localhost:PORT) raporda yaz. file:// protokolü ASLA kullanılmaz.\n' +
|
|
1411
1415
|
'HIZ KURALLARI:\n' +
|
|
1412
|
-
'-
|
|
1416
|
+
'- VAR OLAN dosyayı edit_file ile değiştir, YENİ dosyayı write_file ile yarat; basit dosya oluşturma/düzenleme için Python scripti yazma; python_run yalnız gerçek hesap/veri işleme gerekiyorsa.\n' +
|
|
1413
1417
|
'- Bağımsız araç çağrılarını AYNI TURDA paralel ver (birden çok read_file tek turda).\n' +
|
|
1414
1418
|
'- Dosyayı kullanıcı söylememişse list_dir ile yapıyı görüp kendin karar ver.\n' +
|
|
1415
1419
|
'- Run_command PowerShell ortamında çalışır (Windows): KALICI oturum — cd ve $env değişkenleri çağrılar arasında korunur; büyük çıktıda tam çıktı geçici dosyaya düşer, read_file ile oku. shell:"bash" verirsen git-bash ile bash sözdizimi koşar.\n' +
|
|
@@ -1418,6 +1422,66 @@ class Engine {
|
|
|
1418
1422
|
);
|
|
1419
1423
|
}
|
|
1420
1424
|
|
|
1425
|
+
/* Beast Studio (video paneli): video YAPMA ve DÜZENLEME ajanı. Workspace'teki
|
|
1426
|
+
her şey video proje malzemesi olarak görülür (ham görüntü, ses, görsel,
|
|
1427
|
+
altyazı, çıktı). Medya işi ffprobe + ffmpeg ile yürütür; ikisi de app ile
|
|
1428
|
+
birlikte gelir (ffmpeg-static). Beast Code disiplini korunur: plan →
|
|
1429
|
+
uygula → doğrula. */
|
|
1430
|
+
buildStudioSystem(session) {
|
|
1431
|
+
const nowD = new Date();
|
|
1432
|
+
const localDate = nowD.toLocaleDateString('tr-TR');
|
|
1433
|
+
const localTime = String(nowD.getHours()).padStart(2, '0') + ':00';
|
|
1434
|
+
const mode = String((session && session.bcMode) || 'auto').toLowerCase();
|
|
1435
|
+
const modeBlock =
|
|
1436
|
+
mode === 'plan'
|
|
1437
|
+
? 'ÇALIŞMA MODU: PLAN 🔍 — malzemeyi İNCELE (list_dir + ffprobe), KOMUT ÇALIŞTIRMA; adım adım MONTAJ PLANI ver (hangi dosya, hangi işlem, çıktı adı, nasıl doğrulanır). Kullanıcı /build deyince plan uygulanır.'
|
|
1438
|
+
: mode === 'build'
|
|
1439
|
+
? 'ÇALIŞMA MODU: BUILD 🛠 — son montaj planını SOHBETTEKİ bağlamdan al ve UYGULA: ffmpeg işlerini çalıştır, çıktıları doğrula. Yeni plan açma; en fazla 1 cümlelik yön gösterimi + uygulama.'
|
|
1440
|
+
: 'ÇALIŞMA MODU: OTOMATİK ⚡ — önce 2-4 satırlık mini plan (todo_write), sonra hemen uygula + doğrula.';
|
|
1441
|
+
let ffPath = '';
|
|
1442
|
+
try { ffPath = String(require('ffmpeg-static') || ''); } catch {}
|
|
1443
|
+
let ffprobeHint = '';
|
|
1444
|
+
if (ffPath) {
|
|
1445
|
+
const ffDir = path.dirname(ffPath);
|
|
1446
|
+
ffprobeHint =
|
|
1447
|
+
`Medya araçları KURULU, kurmaya ÇALIŞMA:\n` +
|
|
1448
|
+
`- ffmpeg: "${ffPath}"\n` +
|
|
1449
|
+
`- ffprobe: "${path.join(ffDir, 'ffprobe' + (process.platform === 'win32' ? '.exe' : ''))}" (yoksa ffmpeg -i de meta verir)\n` +
|
|
1450
|
+
`Bunları run_command ile TAM YOLLA çağır (PATH'te değiller). Boşluklu yolları tırnakla.\n`;
|
|
1451
|
+
}
|
|
1452
|
+
const proj = this._projectInstructions(session);
|
|
1453
|
+
return (
|
|
1454
|
+
'Sen BEAST STUDIO\u2019sun — video YAPMA ve DÜZENLEME ajanı. Sol paneldeki klasör senin stüdyondur.\n' +
|
|
1455
|
+
'GÖREV ALANI: video üretme ve düzenleme — kesme/birleştirme (montaj), trim, crop, ölçek, döndürme, hız (slowmo/timelapse), geçiş efektleri, filtre/renk, altyazı basma (srt/ass burn-in), ses ekleme/değiştirme/ses seviyesi/ses temizleme, GIF, thumbnail, format dönüşümü — kısaca kullanıcının istediği HER türlü video/ses işi. Kod yazma projesi DEĞİLDİR — kod istenirse kullanıcı Beast Code\u2019a yönlendirilir.\n' +
|
|
1456
|
+
`Stüdyo klasörü: ${(session && session.workspace) || this.workspace}\n` +
|
|
1457
|
+
`Yerel zaman: ${localDate} ${localTime}\n` +
|
|
1458
|
+
`${modeBlock}\n` +
|
|
1459
|
+
'KLASÖR FARKINDALIĞI: çalışma klasöründeki HER DOSYA video proje MALZEMESİDİR — ham görüntüler, sesler, müzikler, görseller, altyazılar, önceki çıktılar. İşe başlarken list_dir ile malzemeyi gör; isimden tür UYDURMA, ffprobe ile doğrula. Yeni klasör seçilirse (panelde Klasör seç) yeni proje başlar — oradaki malzemeye göre çalış.\n' +
|
|
1460
|
+
ffprobeHint +
|
|
1461
|
+
'MEDYA ANALİZİ: video/ses dosyalarını read_file ile OKUMA (ikili dosya) — ffprobe ile süre, codec, çözünürlük, fps, ses kanalı, bit hızı bilgisini al: ffprobe -v error -show_entries format=duration,size:stream=codec_name,width,height,r_frame_rate,channels -of default=noprint_wrappers=1 "dosya"\n' +
|
|
1462
|
+
'FFMPEG KURALLARI:\n' +
|
|
1463
|
+
'- Çıktıya HER ZAMAN -y ekle (üstüne yaz). Uzun işlerde stderr büyük olur; gerekiyorsa çıktıyı dosyaya düşürüp son 20 satırı oku.\n' +
|
|
1464
|
+
'- Varsayılan kodlama: video libx264 + crf 20, ses aac 192k, mp4 kapsayıcı; kullanıcı kalite belirtirse crf 16-18.\n' +
|
|
1465
|
+
'- Kesme: -ss BAŞ -to BİT (girişten önce hızlı arama için -ss\u2019yi -i\u2019den ÖNCE koy, yeniden kodlamalı kesin işlerde sonra da olur) -i girdi -t SÜRE.\n' +
|
|
1466
|
+
'- Birleştirme: uyumsuz kaynaklarda concat demuxer yerine filtre (concat=n=...:v=1:a=1) kullan veya önce hepsini aynı çözünürlük/fps/codece normalize et.\n' +
|
|
1467
|
+
'- Altyazı: subtitles="dosya.srt":force_style\u2026 (Windows yolunda escaping: C\\:/…); ses: -i ses -map 0:v -map 1:a -c:v copy -shortest.\n' +
|
|
1468
|
+
'ÇIKTI DİSİPLİNİ: üretilen her dosyayı çalışma klasörü içindeki "output" klasörüne yaz (önce mkdir); dosya adları ASCII, boşluksuz ve anlamlı olsun (ör. output\/kesme-01.mp4). Ara dosyaları da output\/tmp altında tut, iş bitince temizle.\n' +
|
|
1469
|
+
'WORKFLOW (her işte bu sıra):\n' +
|
|
1470
|
+
'1) BAĞLAM: list_dir + ffprobe ile malzeme envanteri (paralel çağrılar TEK turda).\n' +
|
|
1471
|
+
'2) PLAN: 2+ adımlı işlerde İLK EYLEM todo_write olsun (2-6 madde); her adım bitince status:"done" ile GÜNCELLE.\n' +
|
|
1472
|
+
'3) UYGULA: her ffmpeg işini run_command ile adım adım çalıştır; büyük işi tek dev komuta gömme — kırıp zincirle.\n' +
|
|
1473
|
+
'4) DOĞRULA: her adımda çıktı gerçekten oluştu mu — ffprobe ile süre/codec kontrolü ETMEDEN "tamam" DEME; hata varsa komutu düzeltip en fazla 2 kez TEKRAR DENE.\n' +
|
|
1474
|
+
'5) RAPOR: 1-3 satır — üretilen dosya(lar) + süre/boyut + ne yapıldı. Dosya yolunu MUTLAKA yaz (kullanıcı panelde oynatır).\n' +
|
|
1475
|
+
'HIZ KURALLARI:\n' +
|
|
1476
|
+
'- Bağımsız ffprobe/ffmpeg çağrılarını AYNI TURDA paralel ver.\n' +
|
|
1477
|
+
'- run_command PowerShell ortamında çalışır (Windows): KALICI oturum — cd korunur.\n' +
|
|
1478
|
+
'- Görüntü üretimi/thumbnail: ffmpeg -ss ortası -i girdi -frames:v 1.\n' +
|
|
1479
|
+
'- Soru sorma; malzemeden emin olamadığında ffprobe\u2019la kendin çöz. Kullanıcı /plan /build /auto ile modu değiştirir.\n' +
|
|
1480
|
+
(proj ? '# PROJE TALİMATLARI (workspace AGENTS/CLAUDE/CONTEXT dosyalarından — daima uy)\n' + proj + '\n' : '') +
|
|
1481
|
+
FORMAT_RULES
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1421
1485
|
async buildSystem(queryText, session) {
|
|
1422
1486
|
/* BOT OTURUMU: non-admin bota bağlıysa global Beast hafızası HIÇ girmez —
|
|
1423
1487
|
o botun kendi SOUL/USER/MEMORY dosyaları kullanılır (tam izolasyon) */
|
|
@@ -2828,6 +2892,10 @@ class Engine {
|
|
|
2828
2892
|
}
|
|
2829
2893
|
|
|
2830
2894
|
async _chatTurn(session, signal, onDelta, toolsList = TOOLS) {
|
|
2895
|
+
/* MCP: %APPDATA%\beast\mcp.json'daki etkin serverların araçlarını şema listesine ekle
|
|
2896
|
+
(bağlı değilse lazy bağlanır; kapalıysa liste değişmez; Beast Code paneli hızlı
|
|
2897
|
+
ilk-token sözü için MCP'siz kalır) */
|
|
2898
|
+
if (!session || !session.bcCode) toolsList = await mcp.mergeTools(toolsList);
|
|
2831
2899
|
/* opencode agent.ts port: özel ajan tanımı — prompt/model/araç/steps */
|
|
2832
2900
|
const adef = this._agentDefFor(session, !!session.bgJob);
|
|
2833
2901
|
/* Beast Code: todo_write açıklaması "3+ adım" kısıtı içerir ve model küçük
|
|
@@ -2849,9 +2917,11 @@ class Engine {
|
|
|
2849
2917
|
const promptText = this._lastUserText(session);
|
|
2850
2918
|
let system = session.bgJob
|
|
2851
2919
|
? this.buildBgSystem(session)
|
|
2852
|
-
: session.
|
|
2853
|
-
? this.
|
|
2854
|
-
:
|
|
2920
|
+
: session.studio
|
|
2921
|
+
? this.buildStudioSystem(session)
|
|
2922
|
+
: session.bcCode
|
|
2923
|
+
? this.buildBcSystem(session)
|
|
2924
|
+
: await this.buildSystem(promptText, session);
|
|
2855
2925
|
// Granül izin: oturumun yetki seviyesine göre araç seti daraltılır.
|
|
2856
2926
|
// Çoklu izin (ör. web+read) seçiliyse kümeler BİRLEŞİR — hepsinin araçları açık olur.
|
|
2857
2927
|
const perms = this.sessionPermFor(session.id);
|
|
@@ -3450,7 +3520,7 @@ class Engine {
|
|
|
3450
3520
|
/* OTOMATİK SKİLL SİSTEMİ: kurulu skill listesi de prompta girer —
|
|
3451
3521
|
ajan "eski skillden daha kolay/better yol buldum" diyebilmesin, KARAR VERİP
|
|
3452
3522
|
skilli GÜNCELLESİN. action: create (yeni) | update (mevcutu iyileştir) | none */
|
|
3453
|
-
|
|
3523
|
+
const skills = require('./skills');
|
|
3454
3524
|
const existing = skills
|
|
3455
3525
|
.scan()
|
|
3456
3526
|
.map((s) => `- ${s.name}: ${(s.description || '').slice(0, 100)}`)
|
|
@@ -3812,7 +3882,7 @@ class Engine {
|
|
|
3812
3882
|
/* onay kapısı: riskli araçta dış onay bekle; reddedilirse araç çalışmaz.
|
|
3813
3883
|
"always" onaylı araçlar doğrudan geçer. */
|
|
3814
3884
|
if (
|
|
3815
|
-
Engine.RISKY_TOOLS.has(name) &&
|
|
3885
|
+
(Engine.RISKY_TOOLS.has(name) || String(name).startsWith('mcp__')) &&
|
|
3816
3886
|
!this.alwaysAllowTools.has(name) &&
|
|
3817
3887
|
this.approvals && typeof this.approvals.request === 'function'
|
|
3818
3888
|
) {
|
|
@@ -3831,6 +3901,10 @@ class Engine {
|
|
|
3831
3901
|
}
|
|
3832
3902
|
/* GERİ ALMA GÜNLÜĞÜ: dosya yazımından ÖNCE eski içerik kayda geçer */
|
|
3833
3903
|
this._journalBefore(sessionId, name, args);
|
|
3904
|
+
/* MCP: dış server araçları (mcp__<server>__<tool>) — tools/call'a köprülenir */
|
|
3905
|
+
if (String(name).startsWith('mcp__')) {
|
|
3906
|
+
return JSON.stringify(await mcp.call(name, args, signal));
|
|
3907
|
+
}
|
|
3834
3908
|
if (name === 'memory_write') {
|
|
3835
3909
|
/* bot oturumu → botun KENDİ hafıza store'una yaz (global Beast hafızasına değil).
|
|
3836
3910
|
mem0 açıkken: hash+semantic dedup'lı store'a gider, MEMORY.md aynası güncellenir. */
|
package/src/agent/kb.js
CHANGED
|
@@ -1,123 +1,123 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/* Beast knowledge store (#3 derin öğrenme katmanı)
|
|
4
|
-
sqlite-vec yerine sıfır bağımlılık: JSON deposu + TF-IDF benzerlik.
|
|
5
|
-
Kayıtlar kaynak taşır → kb_search sonucu citation'lı döner.
|
|
6
|
-
|
|
7
|
-
Depo: %APPDATA%\beast\knowledge.json
|
|
8
|
-
{ items: [{ id, title, source, tags[], text, createdAt }] } */
|
|
9
|
-
|
|
10
|
-
const fs = require('fs');
|
|
11
|
-
const path = require('path');
|
|
12
|
-
const { beastRoot } = require('./memory');
|
|
13
|
-
|
|
14
|
-
function file() {
|
|
15
|
-
return path.join(beastRoot(), 'knowledge.json');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let items = null;
|
|
19
|
-
|
|
20
|
-
function load() {
|
|
21
|
-
if (items) return;
|
|
22
|
-
try {
|
|
23
|
-
const raw = JSON.parse(fs.readFileSync(file(), 'utf8'));
|
|
24
|
-
items = Array.isArray(raw.items) ? raw.items : [];
|
|
25
|
-
} catch {
|
|
26
|
-
items = [];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function save() {
|
|
31
|
-
try {
|
|
32
|
-
fs.mkdirSync(beastRoot(), { recursive: true });
|
|
33
|
-
const tmp = file() + '.tmp';
|
|
34
|
-
fs.writeFileSync(tmp, JSON.stringify({ items }, null, 2));
|
|
35
|
-
fs.renameSync(tmp, file());
|
|
36
|
-
} catch {}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const KB_CAP = 2000;
|
|
40
|
-
|
|
41
|
-
function uid() {
|
|
42
|
-
return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/* memory.js ile aynı Türkçe duyarsız tokenleştirme */
|
|
46
|
-
const TR_FOLD = { ç: 'c', ğ: 'g', ı: 'i', ö: 'o', ş: 's', ü: 'u', â: 'a', î: 'i', û: 'u' };
|
|
47
|
-
function fold(s) {
|
|
48
|
-
return String(s || '').toLowerCase().replace(/[çğıöşüâîû]/g, (ch) => TR_FOLD[ch] || ch);
|
|
49
|
-
}
|
|
50
|
-
function tokenize(q) {
|
|
51
|
-
return (fold(q).match(/[a-z0-9_+#.]+/g) || []).filter((w) => w.length >= 3);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** add(title, text, {source, tags}) → { ok, id } */
|
|
55
|
-
function add(title, text, meta = {}) {
|
|
56
|
-
load();
|
|
57
|
-
const t = String(text || '').trim();
|
|
58
|
-
if (!t) return { ok: false, error: 'metin boş' };
|
|
59
|
-
if (items.length >= KB_CAP) items.splice(0, items.length - KB_CAP + 1); // en eskiyi düşür
|
|
60
|
-
const it = {
|
|
61
|
-
id: uid(),
|
|
62
|
-
title: String(title || '').trim().slice(0, 120) || t.slice(0, 60),
|
|
63
|
-
source: String(meta.source || '').trim().slice(0, 300) || 'agent',
|
|
64
|
-
tags: Array.isArray(meta.tags) ? meta.tags.map((x) => String(x).slice(0, 40)).slice(0, 8) : [],
|
|
65
|
-
text: t.slice(0, 20000),
|
|
66
|
-
createdAt: new Date().toISOString(),
|
|
67
|
-
};
|
|
68
|
-
items.push(it);
|
|
69
|
-
save();
|
|
70
|
-
return { ok: true, id: it.id };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function get(id) {
|
|
74
|
-
load();
|
|
75
|
-
return items.find((x) => x.id === id) ? { ...items.find((x) => x.id === id) } : null;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function count() {
|
|
79
|
-
load();
|
|
80
|
-
return items.length;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** TF-IDF benzerlik araması; sonuçlar citation alanıyla döner */
|
|
84
|
-
function search(query, limit = 5) {
|
|
85
|
-
load();
|
|
86
|
-
const qTokens = tokenize(query);
|
|
87
|
-
if (!qTokens.length || !items.length) return [];
|
|
88
|
-
const N = items.length;
|
|
89
|
-
const docs = items.map((it) => tokenize(it.title + ' ' + it.text));
|
|
90
|
-
/* df */
|
|
91
|
-
const df = new Map();
|
|
92
|
-
for (const toks of docs) {
|
|
93
|
-
for (const t of new Set(toks)) df.set(t, (df.get(t) || 0) + 1);
|
|
94
|
-
}
|
|
95
|
-
const scored = [];
|
|
96
|
-
for (let i = 0; i < N; i++) {
|
|
97
|
-
const toks = docs[i];
|
|
98
|
-
if (!toks.length) continue;
|
|
99
|
-
const tf = new Map();
|
|
100
|
-
for (const t of toks) tf.set(t, (tf.get(t) || 0) + 1);
|
|
101
|
-
let score = 0;
|
|
102
|
-
for (const q of qTokens) {
|
|
103
|
-
const f = tf.get(q) || 0;
|
|
104
|
-
if (!f) continue;
|
|
105
|
-
const idf = Math.log(1 + N / (1 + (df.get(q) || 0)));
|
|
106
|
-
score += (f / toks.length) * idf;
|
|
107
|
-
}
|
|
108
|
-
/* tag/başlık eşleşmesine bonus */
|
|
109
|
-
const hayT = fold(items[i].title + ' ' + (items[i].tags || []).join(' '));
|
|
110
|
-
for (const q of qTokens) if (hayT.includes(q)) score += 0.35;
|
|
111
|
-
if (score > 0) scored.push({ i, score });
|
|
112
|
-
}
|
|
113
|
-
scored.sort((a, b) => b.score - a.score);
|
|
114
|
-
return scored.slice(0, Math.max(1, Math.min(limit, 10))).map(({ i, score }) => ({
|
|
115
|
-
id: items[i].id,
|
|
116
|
-
title: items[i].title,
|
|
117
|
-
snippet: items[i].text.slice(0, 400),
|
|
118
|
-
score: Number(score.toFixed(3)),
|
|
119
|
-
citation: `[${items[i].title}] (${items[i].source}, ${String(items[i].createdAt).slice(0, 10)})`,
|
|
120
|
-
}));
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
module.exports = { add, get, search, count, tokenize };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* Beast knowledge store (#3 derin öğrenme katmanı)
|
|
4
|
+
sqlite-vec yerine sıfır bağımlılık: JSON deposu + TF-IDF benzerlik.
|
|
5
|
+
Kayıtlar kaynak taşır → kb_search sonucu citation'lı döner.
|
|
6
|
+
|
|
7
|
+
Depo: %APPDATA%\beast\knowledge.json
|
|
8
|
+
{ items: [{ id, title, source, tags[], text, createdAt }] } */
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { beastRoot } = require('./memory');
|
|
13
|
+
|
|
14
|
+
function file() {
|
|
15
|
+
return path.join(beastRoot(), 'knowledge.json');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let items = null;
|
|
19
|
+
|
|
20
|
+
function load() {
|
|
21
|
+
if (items) return;
|
|
22
|
+
try {
|
|
23
|
+
const raw = JSON.parse(fs.readFileSync(file(), 'utf8'));
|
|
24
|
+
items = Array.isArray(raw.items) ? raw.items : [];
|
|
25
|
+
} catch {
|
|
26
|
+
items = [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function save() {
|
|
31
|
+
try {
|
|
32
|
+
fs.mkdirSync(beastRoot(), { recursive: true });
|
|
33
|
+
const tmp = file() + '.tmp';
|
|
34
|
+
fs.writeFileSync(tmp, JSON.stringify({ items }, null, 2));
|
|
35
|
+
fs.renameSync(tmp, file());
|
|
36
|
+
} catch {}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const KB_CAP = 2000;
|
|
40
|
+
|
|
41
|
+
function uid() {
|
|
42
|
+
return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/* memory.js ile aynı Türkçe duyarsız tokenleştirme */
|
|
46
|
+
const TR_FOLD = { ç: 'c', ğ: 'g', ı: 'i', ö: 'o', ş: 's', ü: 'u', â: 'a', î: 'i', û: 'u' };
|
|
47
|
+
function fold(s) {
|
|
48
|
+
return String(s || '').toLowerCase().replace(/[çğıöşüâîû]/g, (ch) => TR_FOLD[ch] || ch);
|
|
49
|
+
}
|
|
50
|
+
function tokenize(q) {
|
|
51
|
+
return (fold(q).match(/[a-z0-9_+#.]+/g) || []).filter((w) => w.length >= 3);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** add(title, text, {source, tags}) → { ok, id } */
|
|
55
|
+
function add(title, text, meta = {}) {
|
|
56
|
+
load();
|
|
57
|
+
const t = String(text || '').trim();
|
|
58
|
+
if (!t) return { ok: false, error: 'metin boş' };
|
|
59
|
+
if (items.length >= KB_CAP) items.splice(0, items.length - KB_CAP + 1); // en eskiyi düşür
|
|
60
|
+
const it = {
|
|
61
|
+
id: uid(),
|
|
62
|
+
title: String(title || '').trim().slice(0, 120) || t.slice(0, 60),
|
|
63
|
+
source: String(meta.source || '').trim().slice(0, 300) || 'agent',
|
|
64
|
+
tags: Array.isArray(meta.tags) ? meta.tags.map((x) => String(x).slice(0, 40)).slice(0, 8) : [],
|
|
65
|
+
text: t.slice(0, 20000),
|
|
66
|
+
createdAt: new Date().toISOString(),
|
|
67
|
+
};
|
|
68
|
+
items.push(it);
|
|
69
|
+
save();
|
|
70
|
+
return { ok: true, id: it.id };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function get(id) {
|
|
74
|
+
load();
|
|
75
|
+
return items.find((x) => x.id === id) ? { ...items.find((x) => x.id === id) } : null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function count() {
|
|
79
|
+
load();
|
|
80
|
+
return items.length;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** TF-IDF benzerlik araması; sonuçlar citation alanıyla döner */
|
|
84
|
+
function search(query, limit = 5) {
|
|
85
|
+
load();
|
|
86
|
+
const qTokens = tokenize(query);
|
|
87
|
+
if (!qTokens.length || !items.length) return [];
|
|
88
|
+
const N = items.length;
|
|
89
|
+
const docs = items.map((it) => tokenize(it.title + ' ' + it.text));
|
|
90
|
+
/* df */
|
|
91
|
+
const df = new Map();
|
|
92
|
+
for (const toks of docs) {
|
|
93
|
+
for (const t of new Set(toks)) df.set(t, (df.get(t) || 0) + 1);
|
|
94
|
+
}
|
|
95
|
+
const scored = [];
|
|
96
|
+
for (let i = 0; i < N; i++) {
|
|
97
|
+
const toks = docs[i];
|
|
98
|
+
if (!toks.length) continue;
|
|
99
|
+
const tf = new Map();
|
|
100
|
+
for (const t of toks) tf.set(t, (tf.get(t) || 0) + 1);
|
|
101
|
+
let score = 0;
|
|
102
|
+
for (const q of qTokens) {
|
|
103
|
+
const f = tf.get(q) || 0;
|
|
104
|
+
if (!f) continue;
|
|
105
|
+
const idf = Math.log(1 + N / (1 + (df.get(q) || 0)));
|
|
106
|
+
score += (f / toks.length) * idf;
|
|
107
|
+
}
|
|
108
|
+
/* tag/başlık eşleşmesine bonus */
|
|
109
|
+
const hayT = fold(items[i].title + ' ' + (items[i].tags || []).join(' '));
|
|
110
|
+
for (const q of qTokens) if (hayT.includes(q)) score += 0.35;
|
|
111
|
+
if (score > 0) scored.push({ i, score });
|
|
112
|
+
}
|
|
113
|
+
scored.sort((a, b) => b.score - a.score);
|
|
114
|
+
return scored.slice(0, Math.max(1, Math.min(limit, 10))).map(({ i, score }) => ({
|
|
115
|
+
id: items[i].id,
|
|
116
|
+
title: items[i].title,
|
|
117
|
+
snippet: items[i].text.slice(0, 400),
|
|
118
|
+
score: Number(score.toFixed(3)),
|
|
119
|
+
citation: `[${items[i].title}] (${items[i].source}, ${String(items[i].createdAt).slice(0, 10)})`,
|
|
120
|
+
}));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = { add, get, search, count, tokenize };
|