beast-agent 0.30.0 → 1.0.1

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.
Files changed (44) hide show
  1. package/package.json +8 -3
  2. package/src/agent/engine.js +463 -21
  3. package/src/agent/llm.js +52 -3
  4. package/src/agent/obscura.js +26 -10
  5. package/src/agent/skills.js +2 -2
  6. package/src/agent/tools.js +519 -7
  7. package/src/main.js +28 -13
  8. package/src/renderer/i18n.js +7 -7
  9. package/src/renderer/index.html +5 -2
  10. package/src/renderer/renderer.js +154 -5
  11. package/src/renderer/style.css +45 -4
  12. package/.env.example +0 -8
  13. package/config.example.yaml +0 -39
  14. package/eng.traineddata +0 -0
  15. package/src/agent/scripts/__pycache__/websearch.cpython-312.pyc +0 -0
  16. package/store/skills.json +0 -5
  17. package/tests/approval.test.js +0 -56
  18. package/tests/bg-jobs.test.js +0 -424
  19. package/tests/bots-name.test.js +0 -42
  20. package/tests/bus.test.js +0 -46
  21. package/tests/computeruse-context.test.js +0 -69
  22. package/tests/cron.test.js +0 -95
  23. package/tests/engine.test.js +0 -203
  24. package/tests/eval.test.js +0 -91
  25. package/tests/fallout.test.js +0 -188
  26. package/tests/llm.test.js +0 -205
  27. package/tests/memory.test.js +0 -33
  28. package/tests/memoryloop.test.js +0 -26
  29. package/tests/notegen.test.js +0 -63
  30. package/tests/obscura.test.js +0 -124
  31. package/tests/owner.test.js +0 -32
  32. package/tests/python.test.js +0 -76
  33. package/tests/research.test.js +0 -116
  34. package/tests/scenarios.json +0 -81
  35. package/tests/sessioncode.test.js +0 -50
  36. package/tests/setup.js +0 -9
  37. package/tests/tokens.test.js +0 -42
  38. package/tests/tools.test.js +0 -84
  39. package/tests/usage.test.js +0 -44
  40. package/tests/v11.test.js +0 -76
  41. package/tests/watchers.test.js +0 -206
  42. package/tests/watext.test.js +0 -53
  43. package/tests/whatsapp.test.js +0 -103
  44. package/tests/wherewasi.test.js +0 -40
@@ -1,33 +0,0 @@
1
- 'use strict';
2
-
3
- require('./setup');
4
- const test = require('node:test');
5
- const assert = require('node:assert');
6
- const memory = require('../src/agent/memory');
7
-
8
- test('append + entries turu', () => {
9
- memory.append('Kullanıcı FxPro MT5 kullanıyor');
10
- memory.append('Beast hızlı olmalı, gereksiz sormaz');
11
- const list = memory.entries();
12
- assert.ok(list.length >= 2);
13
- assert.ok(list.some((l) => l.includes('FxPro')));
14
- });
15
-
16
- test('search ilgili kaydı bulur (Türkçe büyük/küçük + diakritik duyarsız)', () => {
17
- memory.append('Kullanıcının favori editörü VS Code');
18
- const hits = memory.search('favori editör nedir?');
19
- assert.ok(hits.length >= 1);
20
- assert.ok(hits[0].text.toLowerCase().includes('editör') || hits[0].text.toLowerCase().includes('edıtor'));
21
- });
22
-
23
- test('relevantFor alakasız sorguda bile son kayıtları döndürür', () => {
24
- const out = memory.relevantFor('zzz bilinmeyen kelime xyzq');
25
- assert.ok(out.length > 0);
26
- assert.ok(out.startsWith('- '));
27
- });
28
-
29
- test('relevantFor charCap\u0027e uyar', () => {
30
- for (let i = 0; i < 30; i++) memory.append('uzun kayıt numara ' + i + ' ' + 'x'.repeat(200));
31
- const out = memory.relevantFor('kayıt');
32
- assert.ok(out.length <= 2600);
33
- });
@@ -1,26 +0,0 @@
1
- 'use strict';
2
-
3
- require('./setup');
4
- const test = require('node:test');
5
- const assert = require('node:assert');
6
- const memory = require('../src/agent/memory');
7
-
8
- test('#v13 memory döngüsü: append dedup + cap hygiene', () => {
9
- // temiz başlangıç
10
- const f = require('path').join(memory.memDir(), 'MEMORY.md');
11
- require('fs').rmSync(f, { force: true });
12
-
13
- const r1 = memory.append('Kullanıcının projeleri: Beast Agent, GOLD trading');
14
- assert.ok(r1.ok);
15
- // aynı bilgi farklı ifadeyle — dup yakalanmalı (fold+normalize)
16
- const r2 = memory.append('kullanıcının PROJELERİ: beast agent, gold trading!');
17
- assert.ok(r2.ok);
18
- assert.equal(r2.duplicate, true);
19
-
20
- const list = memory.entries().filter((l) => /beast agent/i.test(l));
21
- assert.equal(list.length, 1, 'dup tekilleşmeli');
22
-
23
- // hygiene çalışıyor mu
24
- const h = memory.hygiene({ maxAgeDays: 0 });
25
- assert.ok(h.ok);
26
- });
@@ -1,63 +0,0 @@
1
- 'use strict';
2
-
3
- require('./setup');
4
- const test = require('node:test');
5
- const assert = require('node:assert');
6
- const fs = require('fs');
7
- const os = require('os');
8
- const path = require('path');
9
-
10
- /* chatOnce'ı stub'la: engine yüklenmeden önce llm modülünü patch'le
11
- (engine destructure anında referansı alır) */
12
- const llm = require('../src/agent/llm');
13
- let stubResponse = 'Not: kullanıcı Beast Agent geliştiriyor. Karar: not sistemi çalışmalı.';
14
- let stubCalls = 0;
15
- llm.chatOnce = async () => {
16
- stubCalls++;
17
- return { content: stubResponse };
18
- };
19
-
20
- const { Engine } = require('../src/agent/engine');
21
-
22
- function makeEngine(extra = {}) {
23
- const sessionsDir = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-sess-'));
24
- const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-ws-'));
25
- const eng = new Engine({}, { sessionsDir, workspace, customProviders: [], emit: () => {}, ...extra });
26
- eng.sel = { provider: 'test', providerName: 'Test', model: 'test-model', sel: 'test::test-model' };
27
- return eng;
28
- }
29
-
30
- test('oturum notları: 14+ mesajda not üretilir ve listNotes görünür', async () => {
31
- const eng = makeEngine();
32
- const s = eng.createSession();
33
- const full = eng._load(s.id);
34
- for (let i = 0; i < 20; i++) {
35
- full.messages.push({ role: 'user', content: 'mesaj ' + i });
36
- full.messages.push({ role: 'assistant', content: 'cevap ' + i });
37
- }
38
- const r = await eng._updateSessionNotes(full, undefined);
39
- assert.equal(r.ok, true, 'not üretimi başarılı olmalı, hata: ' + (r && r.reason));
40
- assert.equal(stubCalls, 1);
41
- assert.ok(full.notes && full.notes.length, 'notes yazıldı');
42
-
43
- const list = eng.listNotes();
44
- assert.equal(list.length, 1, 'listNotes notu dönmeli');
45
- assert.equal(list[0].id, s.id);
46
- assert.ok(list[0].notes.includes('Not:'));
47
-
48
- /* diskte notes satırı da olmalı */
49
- const raw = fs.readFileSync(eng._file(s.id), 'utf8');
50
- assert.ok(raw.includes('"t":"notes"'), 'notes satırı dosyada');
51
- });
52
-
53
- test('oturum notları: eşiği doldurmayan oturum bekleme döner, hata değil', async () => {
54
- const eng = makeEngine();
55
- const s = eng.createSession();
56
- const full = eng._load(s.id);
57
- full.messages.push({ role: 'user', content: 'selam' });
58
- full.messages.push({ role: 'assistant', content: 'merhaba' });
59
- const r = await eng._updateSessionNotes(full, undefined);
60
- assert.equal(r.ok, false);
61
- assert.ok(/^bekleme/.test(r.reason));
62
- assert.equal(stubCalls, 1, 'stub çağrısı artmadı');
63
- });
@@ -1,124 +0,0 @@
1
- 'use strict';
2
-
3
- /* Obscura entegrasyonu + sıralı web arama zinciri testleri (ağ çağrısı yok) */
4
-
5
- const test = require('node:test');
6
- const assert = require('node:assert');
7
- const tools = require('../src/agent/tools');
8
- const obscura = require('../src/agent/obscura');
9
-
10
- test('varsayılan zincir: 4 motor, hepsi açık, obscura AKTİF', () => {
11
- const c = tools.getSearchChain();
12
- assert.deepEqual(c.map((x) => x.id), ['browser', 'obscura', 'tinyfish', 'python']);
13
- for (const r of c) assert.strictEqual(r.on, true, r.id + ' varsayılan açık');
14
- });
15
-
16
- test('setSearchChain: sıra yeniden düzenlenir, eksik motorlar sona eklenir (açık)', () => {
17
- const c = tools.setSearchChain([
18
- { id: 'obscura', on: true },
19
- { id: 'python', on: true },
20
- { id: 'browser', on: false },
21
- ]);
22
- assert.deepEqual(c.map((x) => x.id), ['obscura', 'python', 'browser', 'tinyfish']);
23
- assert.strictEqual(c.find((x) => x.id === 'browser').on, false);
24
- assert.strictEqual(c.find((x) => x.id === 'tinyfish').on, true, 'listede olmayan motor varsayılan açık');
25
- tools.setSearchChain(tools.DEFAULT_SEARCH_CHAIN); /* testler arası temizlik */
26
- });
27
-
28
- test('setSearchChain: hepsi kapalıysa tarayıcı zorunlu açılır (zincir boş kalmasın)', () => {
29
- const c = tools.setSearchChain(['obscura', 'tinyfish', 'python', 'browser'].map((id) => ({ id, on: false })));
30
- assert.strictEqual(c.find((x) => x.id === 'browser').on, true);
31
- tools.setSearchChain(tools.DEFAULT_SEARCH_CHAIN);
32
- });
33
-
34
- test('setSearchChain: bilinmeyen id ve tekrar elenir', () => {
35
- const c = tools.setSearchChain([{ id: 'exa', on: true }, { id: 'obscura' }, { id: 'obscura', on: false }, 'python']);
36
- assert.deepEqual(c.map((x) => x.id), ['obscura', 'python', 'browser', 'tinyfish']);
37
- tools.setSearchChain(tools.DEFAULT_SEARCH_CHAIN);
38
- });
39
-
40
- test('searchChainWeb: sırayı uygular, dolu motorda durur, boş tarayıcı banlanır', async () => {
41
- tools.setSearchChain([
42
- { id: 'browser', on: true },
43
- { id: 'obscura', on: false },
44
- { id: 'tinyfish', on: false },
45
- { id: 'python', on: false },
46
- ]);
47
- const calls = [];
48
- const r = await tools.searchChainWeb('test sorgu', 5, {
49
- browser: async () => {
50
- calls.push('browser');
51
- return { ok: true, results: [{ title: 'T', url: 'https://x.com', snippet: 's', engine: 'browser' }] };
52
- },
53
- });
54
- assert.ok(r.ok, 'sonuç geldi');
55
- assert.strictEqual(calls.length, 1, 'zincir ilk dolu motorda durdu');
56
- assert.ok(!tools.browserBanned(), 'başarılı tarayıcı banlanmaz');
57
-
58
- /* tarayıcı boş dönerse 10 dk ban + zincir başarısız (diğer motorlar kapalı) */
59
- const r2 = await tools.searchChainWeb('test 2', 5, {
60
- browser: async () => {
61
- calls.push('browser');
62
- return { ok: true, results: [] };
63
- },
64
- });
65
- assert.strictEqual(r2.ok, false);
66
- assert.ok(tools.browserBanned(), 'boş sonuç tarayıcıyı banladı');
67
- tools.banBrowser(0); /* banı temizle */
68
- const r3 = await tools.searchChainWeb('test 3', 5, {
69
- browser: async () => {
70
- calls.push('browser');
71
- return { ok: true, results: [{ title: 'T3', url: 'https://y.com' }] };
72
- },
73
- });
74
- assert.ok(r3.ok && calls.length === 3, 'ban süresi dolunca tarayıcı geri döner');
75
- tools.setSearchChain(tools.DEFAULT_SEARCH_CHAIN);
76
- });
77
-
78
- test('searchChainWeb: browser hook yoksa obscura/tinyfish/python sırası izlenir', async () => {
79
- tools.setSearchChain([{ id: 'browser', on: true }, { id: 'obscura', on: false }, { id: 'tinyfish', on: false }, { id: 'python', on: false }]);
80
- const r = await tools.searchChainWeb('test', 5, {});
81
- /* tüm motorlar kapalı/atlanmış → normalize browser'ı zorunlu açtı ama hook yok → başarısız */
82
- assert.strictEqual(r.ok, false);
83
- tools.setSearchChain(tools.DEFAULT_SEARCH_CHAIN);
84
- });
85
-
86
- test('searchChainWeb: boş sorgu hata döner', async () => {
87
- const r = await tools.searchChainWeb(' ', 5, {});
88
- assert.strictEqual(r.ok, false);
89
- assert.ok(/boş sorgu/.test(r.error));
90
- });
91
-
92
- test('obscura parseDdgLite: lite tablosundan sonuç çıkarır', () => {
93
- const html =
94
- '<table><tr><td><a rel="nofollow" class="result-link" href="//duckduckgo.com/l/?uddg=https%3A%2F%2Fornek.com%2Fa&amp;rut=1">Örnek A</a></td></tr>' +
95
- '<tr><td><a rel="nofollow" class="result-link" href="https://ornek.com/b">Örnek B</a></td></tr></table>';
96
- const rows = obscura.parseDdgLite(html, 5);
97
- assert.strictEqual(rows.length, 2);
98
- assert.strictEqual(rows[0].url, 'https://ornek.com/a');
99
- assert.strictEqual(rows[0].title, 'Örnek A');
100
- assert.strictEqual(rows[1].url, 'https://ornek.com/b');
101
- });
102
-
103
- test('obscura yolları ve kurulum durumu', () => {
104
- assert.ok(/beast[\\/]obscura$/.test(obscura.obscuraDir()));
105
- assert.ok(/obscura\.exe$/.test(obscura.obscuraExe()));
106
- /* test makinesinde kurulu olabilir — sadece tipleri doğrula */
107
- assert.strictEqual(typeof obscura.obscuraInstalled(), 'boolean');
108
- assert.strictEqual(typeof obscura.obscuraSearch, 'function');
109
- assert.strictEqual(typeof obscura.installObscura, 'function');
110
- });
111
-
112
- test('obscuraSearch: kurulu değilse null döner (zincir atlar)', async () => {
113
- const wasInstalled = obscura.obscuraInstalled();
114
- if (wasInstalled) return; /* kurulu makinede atla */
115
- const r = await obscura.obscuraSearch('sorgu', {});
116
- assert.strictEqual(r, null);
117
- });
118
-
119
- test('Exa sistemden tamamen kalktı', () => {
120
- for (const k of ['setExaKey', 'exaSearch']) assert.ok(!(k in tools), k + ' export yok');
121
- const desc = JSON.stringify(tools.definitions);
122
- assert.ok(!/exa\.ai/i.test(desc), 'araç tanımlarında exa yok');
123
- assert.ok(!/api\.exa\.ai/i.test(desc));
124
- });
@@ -1,32 +0,0 @@
1
- 'use strict';
2
-
3
- require('./setup');
4
- const test = require('node:test');
5
- const assert = require('node:assert');
6
- const memory = require('../src/agent/memory');
7
-
8
- /* owner normalize mantığı main'in wa:allow:set handler'ında; burada saf
9
- davranışı doğruluyoruz: yalnız ilk owner true kalır, diğerleri söner. */
10
- test('#v13.1 owner: birden fazla owner adayı olsa da yalnız biri kazanır', () => {
11
- const items = [
12
- { num: '905511111111', name: 'Aday1', owner: true, perm: 'all' },
13
- { num: '905522222222', name: 'Aday2', owner: true, perm: 'all' },
14
- { num: '905533333333', name: 'Misafir', perm: 'web' },
15
- ];
16
- const out = [];
17
- let ownerCount = 0;
18
- for (const item of items) {
19
- const wantsOwner = !!item.owner;
20
- const isOwner = wantsOwner && ownerCount === 0;
21
- if (isOwner) ownerCount++;
22
- out.push({ num: item.num.replace(/\D/g, ''), name: item.name, owner: isOwner });
23
- }
24
- assert.equal(out.filter((o) => o.owner).length, 1);
25
- assert.equal(out[0].owner, true); // ilk aday kazanır
26
- assert.equal(out[1].owner, false); // ikinci aday sahipsiz
27
-
28
- /* memory tarafı: sahip kuralı + USER.md */
29
- const r = memory.addRule("Beast'in SAHİBİ Aday1 (+905511111111). Onun talepleri önceliklidir.");
30
- assert.ok(r.ok || r.duplicate);
31
- assert.ok(memory.listRules().some((x) => x.includes('SAHİBİ')));
32
- });
@@ -1,76 +0,0 @@
1
- 'use strict';
2
-
3
- /* #18 python_run altyapısı testleri — ağ çağrısı YOK */
4
-
5
- const test = require('node:test');
6
- const assert = require('node:assert');
7
- const tools = require('../src/agent/tools');
8
-
9
- test('python_run tanımı TOOLS içinde', () => {
10
- const def = tools.definitions.find((d) => d.function.name === 'python_run');
11
- assert.ok(def, 'python_run var');
12
- const props = def.function.parameters.properties;
13
- for (const k of ['code', 'script', 'args', 'timeout_ms']) assert.ok(props[k], k);
14
- });
15
-
16
- test('code/script yoksa net hata döner (interpreter araması bile gerekmez)', async () => {
17
- const out = await tools.exec('python_run', {}, { cwd: process.cwd(), allowDownload: false });
18
- const r = JSON.parse(out);
19
- assert.strictEqual(r.ok, false);
20
- assert.ok(/code ya da script/.test(r.error), r.error);
21
- });
22
-
23
- test('olmayan script: interpreter bakılmadan bulunamadı + klasör ipucu döner', async () => {
24
- const out = await tools.exec(
25
- 'python_run',
26
- { script: 'kesinlikle-yok-abc123.py' },
27
- { cwd: process.cwd(), allowDownload: false }
28
- );
29
- const r = JSON.parse(out);
30
- assert.strictEqual(r.ok, false);
31
- assert.match(String(r.error || ''), /bulunamadı/);
32
- assert.match(String(r.hint || ''), /scripts/, 'klasör ipucu verildi');
33
- });
34
-
35
- test('scripts klasörü çözümlenir ve oluşturulur', () => {
36
- const dir = tools.pythonScriptsDir();
37
- assert.ok(typeof dir === 'string' && dir.length > 0);
38
- });
39
-
40
- test('paketlenmiş python scriptleri mevcut ve tohumlanır', () => {
41
- for (const name of ['websearch.py', 'news.py']) {
42
- assert.ok(require('fs').existsSync(tools.bundledScriptPath(name)), 'paket içi: ' + name);
43
- const dest = tools.seedScript(name);
44
- assert.ok(require('fs').existsSync(dest), 'tohumlandı: ' + dest);
45
- }
46
- });
47
-
48
- /* canlı: python hızlı arama yolu (ağ varsa gerçek sonuç, yoksa JS fallback) */
49
- test('webSearchFast: python yolu ya da fallback sonuç döner', async function () {
50
- const r = await tools.webSearchFast('electron builder portable windows', { maxResults: 5 });
51
- /* rate-limit/CAPTCHA turlarında motorlar ok:false dönebilir — kablaj testi;
52
- ağ nazik davranınca sonuç şekli doğrulanır, aksi halde skip */
53
- if (!r || !r.ok || !r.results.length) return this.skip('motorlar bu turda sonuç döndürmedi (ağ/rate-limit) — kablaj doğru');
54
- assert.ok(r.results[0].title && r.results[0].url);
55
- }, { timeout: 40000 });
56
-
57
- /* canlı python varsa (BEAST_PYTHON / sistem / önceden kurulmuş gömülü):
58
- gerçek bir hesap çalıştırılır — ağ kurulumu yapılmaz */
59
- test('canlı interpreter ile inline code çalışır (kuruluysa)', async () => {
60
- let py = null;
61
- try { py = await tools.findSystemPython(); } catch {}
62
- if (!py) {
63
- try { fsAccess(tools.embeddedPythonExe()); } catch { return test.skip('interpreter yok — atlandı'); }
64
- }
65
- const out = await tools.exec(
66
- 'python_run',
67
- { code: 'print(21*2)' },
68
- { cwd: process.cwd(), allowDownload: false }
69
- );
70
- const r = JSON.parse(out);
71
- if (r.ok) assert.ok(/42/.test(r.output), 'çıktı: ' + r.output);
72
- }, { timeout: 30000 });
73
-
74
- function fsAccess(p) {
75
- require('fs').accessSync(p);
76
- }
@@ -1,116 +0,0 @@
1
- 'use strict';
2
-
3
- require('./setup');
4
- const test = require('node:test');
5
- const assert = require('node:assert');
6
-
7
- const research = require('../src/agent/research');
8
-
9
- test('normKey: scheme/www/slash/hash farklarını yok sayar', () => {
10
- assert.equal(research.normKey('http://www.Example.com/a/'), research.normKey('https://example.com/a#x'));
11
- });
12
-
13
- test('mergeResults: round-robin harman + tekilleştirme', () => {
14
- const a = [
15
- { title: 'A1', url: 'https://a.com/1' },
16
- { title: 'A2', url: 'https://a.com/2' },
17
- ];
18
- const b = [
19
- { title: 'B1', url: 'https://a.com/1/' }, // A1 ile aynı (slash)
20
- { title: 'B1x', url: 'http://www.b.com/x' },
21
- ];
22
- const merged = research.mergeResults([a, b], 10);
23
- assert.deepEqual(merged.map((r) => r.url), ['https://a.com/1', 'https://a.com/2', 'http://www.b.com/x']);
24
- assert.equal(merged[2].title, 'B1x'); // ilk gören kazanır, orijinal url korunur
25
- });
26
-
27
- test('normalizeRows: {results} veya dizi + alternatif anahtarlar', () => {
28
- const r1 = research.normalizeRows({ results: [{ name: 'X', href: 'https://x.com', body: 'b' }] });
29
- assert.equal(r1[0].title, 'X');
30
- assert.equal(r1[0].url, 'https://x.com');
31
- const r2 = research.normalizeRows([{ title: 'Y', link: 'https://y.com', description: 'd' }]);
32
- assert.equal(r2[0].url, 'https://y.com');
33
- assert.equal(research.normalizeRows(null).length, 0);
34
- assert.equal(research.normalizeRows([{ title: '', url: 'https://z.com' }]).length, 0); // başlıksız elenir
35
- });
36
-
37
- test('deepSearch: paralel sorgular harmanlanır, ilk N sayfa okunur', async () => {
38
- const searches = {
39
- 'elma': [{ title: 'A', url: 'https://a.com' }, { title: 'B', url: 'https://b.com' }],
40
- 'apple': [{ title: 'B2', url: 'https://b.com' }, { title: 'C', url: 'https://c.com' }],
41
- };
42
- const readCalls = [];
43
- const r = await research.deepSearch(
44
- { queries: ['elma', 'apple'], read_top: 2 },
45
- {
46
- search: async (q) => searches[q],
47
- readPage: async (u) => { readCalls.push(u); return { ok: true, url: u, title: 'T:' + u, content: 'icerik ' + u }; },
48
- }
49
- );
50
- assert.ok(r.ok);
51
- assert.deepEqual(r.queries, ['elma', 'apple']);
52
- assert.equal(r.results.length, 3); // b.com duplike düştü
53
- assert.deepEqual(readCalls, ['https://a.com', 'https://b.com']); // ilk 2 hedef
54
- assert.equal(r.pages.length, 2);
55
- assert.ok(r.pages[0].content.includes('https://a.com'));
56
- assert.match(r.note, /gizli tarayıcı/);
57
- });
58
-
59
- test('deepSearch: ikili dosya hedefleri okumadan atlanır', async () => {
60
- const readCalls = [];
61
- const r = await research.deepSearch(
62
- { queries: ['q'], read_top: 3 },
63
- {
64
- search: async () => [
65
- { title: 'PDF', url: 'https://x.com/doc.pdf' },
66
- { title: 'Sayfa', url: 'https://x.com/page' },
67
- ],
68
- readPage: async (u) => { readCalls.push(u); return { ok: true, url: u, content: 'c' }; },
69
- }
70
- );
71
- assert.deepEqual(readCalls, ['https://x.com/page']);
72
- assert.equal(r.pages.length, 1);
73
- });
74
-
75
- test('deepSearch: readPage yoksa yalnız sonuç listesiyle döner', async () => {
76
- const r = await research.deepSearch(
77
- { queries: ['q'] },
78
- { search: async () => [{ title: 'A', url: 'https://a.com' }] }
79
- );
80
- assert.ok(r.ok);
81
- assert.equal(r.results.length, 1);
82
- assert.equal(r.pages, undefined);
83
- });
84
-
85
- test('deepSearch: bir sorgu çökerse diğerleri kurtarır; boşsa ok:false', async () => {
86
- const r1 = await research.deepSearch(
87
- { queries: ['patlar', 'iyi'] },
88
- {
89
- search: async (q) => { if (q === 'patlar') throw new Error('boom'); return [{ title: 'A', url: 'https://a.com' }]; },
90
- readPage: async () => ({ ok: false }),
91
- }
92
- );
93
- assert.ok(r1.ok && r1.results.length === 1);
94
- const r2 = await research.deepSearch(
95
- { queries: ['yok1', 'yok2'] },
96
- { search: async () => [], readPage: async () => ({ ok: false }) }
97
- );
98
- assert.equal(r2.ok, false);
99
- assert.match(r2.error, /boş/);
100
- });
101
-
102
- test('deepSearch: sorgu yoksa net hata; queries string de kabul edilir', async () => {
103
- const bad = await research.deepSearch({}, { search: async () => [] });
104
- assert.equal(bad.ok, false);
105
- const r = await research.deepSearch(
106
- { queries: 'tek sorgu' },
107
- { search: async () => [{ title: 'A', url: 'https://a.com' }] }
108
- );
109
- assert.deepEqual(r.queries, ['tek sorgu']);
110
- });
111
-
112
- test('SKIP_FILE_RE: pdf/görsel/arşiv yakalanır, sayfa yakalanmaz', () => {
113
- assert.ok(research.SKIP_FILE_RE.test('https://x.com/a.pdf'));
114
- assert.ok(research.SKIP_FILE_RE.test('https://x.com/img.png?w=9'));
115
- assert.ok(!research.SKIP_FILE_RE.test('https://x.com/article'));
116
- });
@@ -1,81 +0,0 @@
1
- {
2
- "note": "Beast eval senaryoları (#4). Her senaryo offline doğrulanabilir olmalı: ya modül fonksiyonu çağrılır ya da prompt/sistem davranışı regex ile kontrol edilir. LLM çağıran uçtan uca testler burada DEĞİL — maliyet ve kararsızlık nedeniyle.",
3
- "scenarios": [
4
- {
5
- "id": "cron-invalid-rejected",
6
- "desc": "Bozuk cron ifadesi kabul edilmez",
7
- "module": "cron",
8
- "call": { "fn": "parseCron", "args": ["61 * * * *"] },
9
- "expectNull": true
10
- },
11
- {
12
- "id": "reminder-daily-schedule",
13
- "desc": "Tekrarlı hatırlatma daily preset doğru cron üretir",
14
- "module": "cron",
15
- "call": { "fn": "reminderSchedule", "args": ["2026-08-27T09:00", "daily"] },
16
- "expectEquals": { "schedule": "0 9 * * *" }
17
- },
18
- {
19
- "id": "watcher-edge-trigger",
20
- "desc": "İzleyici kenar tetiklemeli: koşul doğruyken tek atış",
21
- "module": "watchers",
22
- "manual": true,
23
- "coveredBy": "watchers.test.js"
24
- },
25
- {
26
- "id": "wa-multi-message-merge",
27
- "desc": "Ard arda WA mesajları tek pakette birleşir",
28
- "module": "main",
29
- "manual": true,
30
- "coveredBy": "manuel (wa.log queue→flush→perm zinciri)"
31
- },
32
- {
33
- "id": "session-code-format",
34
- "desc": "Oturum kodu 6 haneli, karıştırılan harfler yok",
35
- "module": "engine",
36
- "coveredBy": "sessioncode.test.js"
37
- },
38
- {
39
- "id": "reflection-json-parse",
40
- "desc": "Yansıma JSON'u fence'li/gömülü olsa da ayrışır",
41
- "module": "engine",
42
- "coveredBy": "v11.test.js"
43
- },
44
- {
45
- "id": "kb-search-citation",
46
- "desc": "kb_search sonucu citation taşır",
47
- "module": "kb",
48
- "check": "citation"
49
- },
50
- {
51
- "id": "memory-hygiene-dedup",
52
- "desc": "Duplike hafıza kayıtları tekilleşir",
53
- "module": "memory",
54
- "coveredBy": "eval.test.js"
55
- },
56
- {
57
- "id": "system-prompt-has-local-time",
58
- "desc": "Sistem promptu yerel tarih/saat bildirir",
59
- "module": "engine",
60
- "regex": "Yerel saat:"
61
- },
62
- {
63
- "id": "system-prompt-rules-injected",
64
- "desc": "Kalıcı kurallar sistem promptuna girer",
65
- "module": "engine",
66
- "check": "rules"
67
- },
68
- {
69
- "id": "usage-cost-math",
70
- "desc": "Maliyet hesabı (1M token fiyatıyla) doğru",
71
- "module": "usage",
72
- "coveredBy": "usage.test.js"
73
- },
74
- {
75
- "id": "bus-price-filter",
76
- "desc": "Fiyat olayı eşiğiyle filtrelenir",
77
- "module": "bus",
78
- "coveredBy": "bus.test.js"
79
- }
80
- ]
81
- }
@@ -1,50 +0,0 @@
1
- 'use strict';
2
-
3
- require('./setup');
4
- const test = require('node:test');
5
- const assert = require('node:assert');
6
- const path = require('path');
7
- const os = require('os');
8
- const fs = require('fs');
9
- const Engine = require('../src/agent/engine');
10
-
11
- function mkEng() {
12
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-code-'));
13
- return new Engine({}, { sessionsDir: dir });
14
- }
15
-
16
- test('oturum kodu: benzersiz, formatta, meta\u2019dan geri yüklenir', () => {
17
- const eng = mkEng();
18
- const a = eng.createSession();
19
- const b = eng.createSession();
20
- assert.match(a.code, /^[23456789ABCDEFGHJKMNPQRSTUVWXYZ]{6}$/);
21
- assert.notEqual(a.code, b.code);
22
-
23
- // cache temizlenmiş gibi: yeni engine aynı dosyadan code okumalı
24
- const eng2 = new Engine({}, { sessionsDir: eng.sessionsDir });
25
- const view = eng2.listSessions().find((s) => s.id === a.id);
26
- assert.equal(view.code, a.code);
27
- });
28
-
29
- test('findByCode: kod ile oturumu bulur, hızlı indeks kullanır', async () => {
30
- const eng = mkEng();
31
- const v = eng.createSession();
32
- // gerçek bir mesaj yaz → dosya oluşsun
33
- eng.send(v.id, { text: 'merhaba' });
34
- await new Promise((r) => setTimeout(r, 30));
35
- const hit = eng.findByCode(v.code);
36
- assert.ok(hit);
37
- assert.equal(hit.id, v.id);
38
- assert.equal(eng.findByCode('ZZZZZZ'), null);
39
- assert.equal(eng.findByCode(v.code.toLowerCase()).id, v.id);
40
- fs.rmSync(eng.sessionsDir, { recursive: true, force: true });
41
- });
42
-
43
- test('ORTAM: yerel tarih/saat/dilim prompta girer', () => {
44
- const eng = mkEng();
45
- const sys = eng.buildSystem('test');
46
- assert.match(sys, /Yerel tarih:/);
47
- assert.match(sys, /Yerel saat:/);
48
- assert.match(sys, /UTC varsayma/);
49
- fs.rmSync(eng.sessionsDir, { recursive: true, force: true });
50
- });
package/tests/setup.js DELETED
@@ -1,9 +0,0 @@
1
- 'use strict';
2
-
3
- /* Ortak test kurulumu: tüm modüller izole BEAST_DATA altına yazsın. */
4
- const fs = require('fs');
5
- const os = require('os');
6
- const path = require('path');
7
-
8
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-test-'));
9
- process.env.BEAST_DATA = path.join(tmp, 'data');
@@ -1,42 +0,0 @@
1
- 'use strict';
2
-
3
- require('./setup');
4
- const test = require('node:test');
5
- const assert = require('node:assert');
6
- const { estTokens, estMsgTokens } = require('../src/agent/tokens');
7
-
8
- test('estTokens boş metin 0', () => {
9
- assert.equal(estTokens(''), 0);
10
- assert.equal(estTokens(null), 0);
11
- });
12
-
13
- test('estTokens uzunlukla monoton artar', () => {
14
- const a = estTokens('hello world');
15
- const b = estTokens('hello world and more words here');
16
- assert.ok(a > 0 && b > a);
17
- });
18
-
19
- test('geniş karakterler (CJK/emoji) daha pahalı', () => {
20
- const ascii = estTokens('aaaa');
21
- const wide = estTokens('\u4e00\u4e01\u4e02\u4e03'); // 4 CJK karakter
22
- assert.ok(wide > ascii);
23
- });
24
-
25
- test('Türkçe karakterler ASCII\u0027den hafif pahalı', () => {
26
- assert.ok(estTokens('ğğğğ') >= estTokens('aaaa'));
27
- });
28
-
29
- test('estMsgTokens mesaj zarfı ekler', () => {
30
- const m = { role: 'user', content: 'selam' };
31
- assert.ok(estMsgTokens(m) > estTokens('selam'));
32
- });
33
-
34
- test('tool_calls içeren mesaj daha ağır', () => {
35
- const base = { role: 'assistant', content: 'x' };
36
- const withTools = {
37
- role: 'assistant',
38
- content: 'x',
39
- tool_calls: [{ id: '1', type: 'function', function: { name: 'run_command', arguments: '{"command":"dir"}' } }],
40
- };
41
- assert.ok(estMsgTokens(withTools) > estMsgTokens(base));
42
- });