beast-agent 0.30.0 → 1.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.
Files changed (46) hide show
  1. package/package.json +6 -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/scripts/fix-electron.js +0 -126
  16. package/scripts/release.js +0 -121
  17. package/scripts/swap-electron.js +0 -40
  18. package/store/skills.json +0 -5
  19. package/tests/approval.test.js +0 -56
  20. package/tests/bg-jobs.test.js +0 -424
  21. package/tests/bots-name.test.js +0 -42
  22. package/tests/bus.test.js +0 -46
  23. package/tests/computeruse-context.test.js +0 -69
  24. package/tests/cron.test.js +0 -95
  25. package/tests/engine.test.js +0 -203
  26. package/tests/eval.test.js +0 -91
  27. package/tests/fallout.test.js +0 -188
  28. package/tests/llm.test.js +0 -205
  29. package/tests/memory.test.js +0 -33
  30. package/tests/memoryloop.test.js +0 -26
  31. package/tests/notegen.test.js +0 -63
  32. package/tests/obscura.test.js +0 -124
  33. package/tests/owner.test.js +0 -32
  34. package/tests/python.test.js +0 -76
  35. package/tests/research.test.js +0 -116
  36. package/tests/scenarios.json +0 -81
  37. package/tests/sessioncode.test.js +0 -50
  38. package/tests/setup.js +0 -9
  39. package/tests/tokens.test.js +0 -42
  40. package/tests/tools.test.js +0 -84
  41. package/tests/usage.test.js +0 -44
  42. package/tests/v11.test.js +0 -76
  43. package/tests/watchers.test.js +0 -206
  44. package/tests/watext.test.js +0 -53
  45. package/tests/whatsapp.test.js +0 -103
  46. package/tests/wherewasi.test.js +0 -40
@@ -1,42 +0,0 @@
1
- 'use strict';
2
-
3
- /* Bot adı disiplini: İLK KARAKTER HARF ZORUNLU (ekleme + güncelleme) */
4
-
5
- const test = require('node:test');
6
- const assert = require('node:assert');
7
- const fs = require('fs');
8
- const os = require('os');
9
- const path = require('path');
10
-
11
- /* BEAST_DATA modül yüklenmeden önce ayarlanmalı (REG cache'i tek dosyada) */
12
- process.env.BEAST_DATA = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-bots-name-'));
13
- const bots = require('../src/agent/bots');
14
-
15
- test('bot ekleme: ad harf ile başlamalı', () => {
16
- for (const bad of ['1Muhasebe', '9bot', '-abc', '_x', ' 3BoT']) {
17
- const r = bots.add({ name: bad, prompt: '' });
18
- assert.strictEqual(r.ok, false, bad + ' reddedilmeli');
19
- assert.ok(/harf ile başlamalı/.test(r.error), r.error);
20
- }
21
- const okNum = bots.add({ name: 'Muhasebe', prompt: '' });
22
- assert.ok(okNum.ok, 'harf ile başlayan ad kabul: ' + (okNum.error || ''));
23
- const okTr = bots.add({ name: 'Çağrı', prompt: '' });
24
- assert.ok(okTr.ok, 'Türkçe harf (Ç) kabul: ' + (okTr.error || ''));
25
- });
26
-
27
- test('bot güncelleme: geçersiz ad diğer alanlara dokunmadan reddedilir', () => {
28
- const created = bots.add({ name: 'Depo', prompt: 'eski' });
29
- assert.ok(created.ok);
30
- const id = created.bot.id;
31
-
32
- const bad = bots.update(id, { name: '3Depo', prompt: 'yeni' });
33
- assert.strictEqual(bad.ok, false, 'geçersiz ad reddedildi');
34
- const after = bots.get(id);
35
- assert.strictEqual(after.name, 'Depo', 'eski ad korundu');
36
- assert.strictEqual(after.prompt, 'eski', 'prompt değişmedi');
37
-
38
- const good = bots.update(id, { name: 'Anbar2', prompt: 'yeni' });
39
- assert.ok(good.ok, 'harf ile başlayan güncelleme kabul: ' + (good.error || ''));
40
- assert.strictEqual(bots.get(id).prompt, 'yeni');
41
- assert.strictEqual(bots.get(id).name, 'Anbar2');
42
- });
package/tests/bus.test.js DELETED
@@ -1,46 +0,0 @@
1
- 'use strict';
2
-
3
- require('./setup');
4
- const test = require('node:test');
5
- const assert = require('node:assert');
6
-
7
- /* bus saf parçaları: decide/compareOp/emitEvent (hooks.notify stub'lı) */
8
- const bus = require('../src/agent/bus');
9
-
10
- test('bus: sayısal filtre op\u2019ları', () => {
11
- assert.ok(bus.compareOp('lt', 2500, 2600));
12
- assert.ok(bus.compareOp('gte', 2600, 2600));
13
- assert.ok(!bus.compareOp('gt', 2599, 2600));
14
- });
15
-
16
- test('bus: decide — filtre + cooldown mantığı', () => {
17
- const sub = { type: 'price:tick', op: 'lte', value: 2600, cooldownMin: 10, lastNotifiedAt: 0 };
18
- const now = Date.now();
19
- // koşul dışı
20
- assert.equal(bus.decide(sub, { type: 'price:tick', value: 2700 }, now).hit, false);
21
- // yanlış tip
22
- assert.equal(bus.decide(sub, { type: 'mail:new' }, now).hit, false);
23
- // koşul içi ilk kez → tetik
24
- const d1 = bus.decide(sub, { type: 'price:tick', value: 2550 }, now);
25
- assert.ok(d1.hit && !d1.cooled);
26
- // hemen ardından → cooldown'a takılır ama hit'tir
27
- sub.lastNotifiedAt = now;
28
- const d2 = bus.decide(sub, { type: 'price:tick', value: 2500 }, now + 60000);
29
- assert.ok(d2.hit && d2.cooled);
30
- });
31
-
32
- test('bus: addSub doğrulama + emitEvent dağıtımı', () => {
33
- assert.equal(bus.addSub({ type: 'yok', sessionId: 's1' }).ok, false);
34
- assert.equal(bus.addSub({ type: 'price:tick', sessionId: 's1' }).ok, true); // filtresiz genel abonelik
35
-
36
- let notified = 0;
37
- // notify hook'u start ile gelir; emitEvent hooks.notify'i kullanır
38
- const before = bus.listSubs().length;
39
- assert.ok(before >= 1);
40
- // fiyat tick yolla — cooldown zaten 10dk varsayılan; ikinci çağrı cooled olmalı
41
- const fired1 = bus.emitEvent('price:tick', { symbol: 'PAXGUSDT', price: 100 }, Date.now());
42
- const fired2 = bus.emitEvent('price:tick', { symbol: 'PAXGUSDT', price: 101 }, Date.now() + 1000);
43
- assert.ok(fired1 >= fired2); // en az ilk seferde tetiklenmiş olmalı
44
- // temizle
45
- for (const s of bus.listSubs()) bus.removeSub(s.id);
46
- });
@@ -1,69 +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
- /* computeruse: modül surface + koordinat/girdi kırpma (network'süz) */
12
- test('#4 computer use: op doğrulama ve sınırlar', async () => {
13
- const cu = require('../src/agent/computeruse');
14
- const r1 = await cu.act('yokboyleop', {});
15
- assert.equal(r1.ok, false);
16
- const r2 = await cu.act('type', { text: '' });
17
- assert.equal(r2.ok, false);
18
- const r3 = await cu.act('key', { combo: '' });
19
- assert.equal(r3.ok, false);
20
- const r4 = await cu.act('scroll', { dy: 0 });
21
- assert.equal(r4.ok, false);
22
- /* bilinmeyen op dışındaki gerçek aksiyonlar PowerShell ister; çağrılmaz */
23
- });
24
-
25
- /* #6 bağlam sıkıştırma: notlar öze dönüşünce eski mesajlar diskten de düşer */
26
- test('#6 compaction: _compactToNotes dosyayı metaya+notlara+son pencereye indirger', () => {
27
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-cx-'));
28
- const eng = new Engine({}, { sessionsDir: dir });
29
- const s = eng.createSession();
30
- /* createSession bir meta satırı yazdı; cache'i atla — ham dosyaya mesaj ekleyip
31
- YENİ engine ile yüklüyoruz (cache trick yok) */
32
- const file = path.join(dir, s.id + '.jsonl');
33
- const lines = [JSON.stringify({ t: 'meta', id: s.id, code: 'ABC234', createdAt: new Date().toISOString() })];
34
- for (let i = 0; i < 20; i++) {
35
- lines.push(JSON.stringify({ t: 'msg', role: i % 2 ? 'assistant' : 'user', content: 'mesaj-' + i }));
36
- }
37
- fs.writeFileSync(file, lines.join('\n') + '\n');
38
-
39
- const eng2 = new Engine({}, { sessionsDir: dir }); // taze cache — dosyadan okur
40
- const sess = eng2._load(s.id);
41
- assert.equal(sess.messages.length, 20);
42
- sess.notes = 'eski önemli noktalar:\n- karar A\n- karar B';
43
- sess.code = sess.code || 'ABC234';
44
- eng2._compactToNotes(sess, 14);
45
-
46
- // dosyayı yeniden oku
47
- const raw = fs.readFileSync(file, 'utf8').split('\n').filter(Boolean).map((l) => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
48
- const metas = raw.filter((r) => r.t === 'meta');
49
- const notesRows = raw.filter((r) => r.t === 'notes');
50
- const msgRows = raw.filter((r) => r.t === 'msg');
51
- const compacted = raw.filter((r) => r.t === 'compacted');
52
- assert.equal(metas.length, 1);
53
- assert.ok(metas[0].code, 'kod korunmalı');
54
- assert.equal(notesRows.length, 1);
55
- assert.match(notesRows[0].text, /karar A/);
56
- assert.equal(notesRows[0].at, 0); // notesAt sıfırlandı
57
- assert.ok(msgRows.length <= 6, `korunan pencere ≤6=KEEP_RECENT*2 (gerçek ${msgRows.length})`);
58
- assert.ok(msgRows.every((m) => Number(m.content.split('-')[1]) >= 14), 'yalnız cut sonrası kaldı');
59
- assert.equal(compacted.length, 1);
60
-
61
- // yeniden yükleme tutarlı
62
- const reloaded = new Engine({}, { sessionsDir: dir });
63
- const again = reloaded._load(s.id);
64
- assert.equal(again.code, 'ABC234');
65
- assert.ok(again.messages.length <= 12);
66
- assert.match(String(again.notes || ''), /karar A/);
67
-
68
- fs.rmSync(dir, { recursive: true, force: true });
69
- });
@@ -1,95 +0,0 @@
1
- 'use strict';
2
-
3
- require('./setup');
4
- const test = require('node:test');
5
- const assert = require('node:assert');
6
- const cron = require('../src/cron');
7
-
8
- test('geçersiz cron ifadeleri reddedilir', () => {
9
- assert.equal(cron.parseCron('* * * *'), null);
10
- assert.equal(cron.parseCron('61 * * * *'), null);
11
- assert.equal(cron.parseCron('* 25 * * *'), null);
12
- assert.equal(cron.parseCron('a b c d e'), null);
13
- });
14
-
15
- test('geçerli cron ifadeleri ayrışır', () => {
16
- assert.ok(cron.parseCron('* * * * *'));
17
- assert.ok(cron.parseCron('*/15 * * * *'));
18
- assert.ok(cron.parseCron('0 9 * * 1-5'));
19
- assert.ok(cron.parseCron('30 21 1,15 * *'));
20
- });
21
-
22
- /* Sabit tarih: 2026-08-25 Salı 10:00 */
23
- function at(min, h, day, mon) {
24
- return new Date(2026, mon - 1, day, h, min, 0);
25
- }
26
-
27
- test('nextRunFrom sıradaki uyarın anını bulur', () => {
28
- // Her saat başı: 10:00'dan sonraki = 11:00
29
- const n = new Date(cron.nextRunFrom('0 * * * *', at(0, 10, 25, 8)));
30
- assert.equal(n.getHours(), 11);
31
- assert.equal(n.getMinutes(), 0);
32
-
33
- // Günlük 09:00: salı 10:00'dan sonra → çarşamba 09:00
34
- const d = new Date(cron.nextRunFrom('0 9 * * *', at(0, 10, 25, 8)));
35
- assert.equal(d.getDate(), 26);
36
- assert.equal(d.getHours(), 9);
37
-
38
- // Hafta içi (Pzt-Cum): salıdan sonraki aynı gün 21:00
39
- const w = new Date(cron.nextRunFrom('0 21 * * 1-5', at(30, 20, 25, 8)));
40
- assert.equal(w.getDay(), 2); // salı
41
- assert.equal(w.getHours(), 21);
42
- });
43
-
44
- test('add: bozuk schedule reddedilir, düzgün olan nextRun alır', () => {
45
- const bad = cron.add({ name: 'x', schedule: 'yok', prompt: 'p' });
46
- assert.equal(bad.ok, false);
47
- const good = cron.add({ name: 'deneme', schedule: '*/5 * * * *', prompt: 'saati söyle' });
48
- assert.equal(good.ok, true);
49
- assert.ok(good.job.nextRunAt > Date.now());
50
- assert.equal(cron.list().some((j) => j.id === good.job.id), true);
51
- });
52
-
53
- test('toggle + remove çalışır', () => {
54
- const r = cron.add({ name: 't2', schedule: '0 12 * * *', prompt: 'p' });
55
- const id = r.job.id;
56
- const t = cron.toggle(id);
57
- assert.equal(t.job.enabled, false);
58
- assert.equal(t.job.nextRunAt, null);
59
- assert.equal(cron.remove(id).ok, true);
60
- });
61
-
62
- /* ---------- reminderSchedule: tekrarlı hatırlatma ---------- */
63
-
64
- test('reminderSchedule: presetler when\u0027den saat üretir', () => {
65
- // 2026-08-26 Çarşamba 09:00
66
- const w = '2026-08-26T09:00';
67
-
68
- const daily = cron.reminderSchedule(w, 'daily');
69
- assert.equal(daily.ok, true);
70
- assert.equal(daily.schedule, '0 9 * * *');
71
-
72
- const weekdays = cron.reminderSchedule(w, 'weekdays');
73
- assert.equal(weekdays.schedule, '0 9 * * 1-5');
74
-
75
- const weekly = cron.reminderSchedule(w, 'weekly');
76
- assert.equal(weekly.schedule, '0 9 * * 3'); // çarşamba
77
-
78
- const monthly = cron.reminderSchedule(w, 'monthly');
79
- assert.equal(monthly.schedule, '0 9 26 * *');
80
-
81
- // dakika korunmalı
82
- const dm = cron.reminderSchedule('2026-08-26T09:30', 'daily');
83
- assert.equal(dm.schedule, '30 9 * * *');
84
- });
85
-
86
- test('reminderSchedule: doğrudan cron kabul, bozuk ve bilinmeyen reddedilir', () => {
87
- const raw = cron.reminderSchedule(null, '*/30 * * * *');
88
- assert.equal(raw.ok, true);
89
- assert.equal(raw.schedule, '*/30 * * * *');
90
-
91
- assert.equal(cron.reminderSchedule('2026-08-26T09:00', '').ok, false);
92
- assert.equal(cron.reminderSchedule('2026-08-26T09:00', 'hergun').ok, false);
93
- assert.equal(cron.reminderSchedule('zaman değil', 'daily').ok, false);
94
- assert.equal(cron.reminderSchedule(null, '61 * * * *').ok, false);
95
- });
@@ -1,203 +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
- const { Engine, sanitizeTodoItems } = require('../src/agent/engine');
11
-
12
- function makeEngine(extra = {}) {
13
- const sessionsDir = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-sess-'));
14
- const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-ws-'));
15
- return new Engine(
16
- {},
17
- { sessionsDir, workspace, customProviders: [], emit: () => {}, ...extra }
18
- );
19
- }
20
-
21
- /* ---------- sanitizeTodoItems ---------- */
22
-
23
- test('todo: boş/geçersiz girdiler elenir', () => {
24
- const out = sanitizeTodoItems([
25
- { title: 'Planla', status: 'active' },
26
- { title: '' },
27
- null,
28
- 'Sadece string başlık',
29
- { title: 'Planla', status: 'done' }, // duplicate title
30
- { title: 'Bitir', status: 'saçma' },
31
- ]);
32
- assert.deepEqual(out, [
33
- { title: 'Planla', status: 'active' },
34
- { title: 'Sadece string başlık', status: 'pending' },
35
- { title: 'Bitir', status: 'pending' },
36
- ]);
37
- });
38
-
39
- test('todo: 20 ile sınırlı', () => {
40
- const items = Array.from({ length: 50 }, (_, i) => ({ title: 'görev ' + i }));
41
- assert.equal(sanitizeTodoItems(items).length, 20);
42
- });
43
-
44
- /* ---------- payload builder ---------- */
45
-
46
- test('tool_call/tool mesajları birlikte tutulur', () => {
47
- const eng = makeEngine();
48
- const msgs = [
49
- { role: 'user', content: 'merhaba' },
50
- { role: 'assistant', content: '', tool_calls: [{ id: '1', type: 'function', function: { name: 'list_dir', arguments: '{}' } }] },
51
- { role: 'tool', tool_call_id: '1', name: 'list_dir', content: '{"ok":true}' },
52
- { role: 'assistant', content: 'sonuç şu' },
53
- { role: 'user', content: 'tekrar' },
54
- ];
55
- const payload = eng._buildPayload('sys', msgs);
56
- assert.equal(payload[0].role, 'system');
57
- const idxAssistant = payload.findIndex((m) => m.role === 'assistant');
58
- assert.ok(idxAssistant >= 0);
59
- assert.equal(payload[idxAssistant + 1].role, 'tool');
60
- });
61
-
62
- test('token bütçesine uyar: eski mesajlar düşer, son mesaj kalır', () => {
63
- const eng = makeEngine({ historyTokenBudget: 120 });
64
- const big = 'kelime '.repeat(400); // ~700 token
65
- const msgs = [];
66
- for (let i = 0; i < 10; i++) msgs.push({ role: 'user', content: big + ' #' + i });
67
- const payload = eng._buildPayload('sys', msgs);
68
- // system + en az son kullanıcı mesajı
69
- assert.equal(payload[0].role, 'system');
70
- const last = payload[payload.length - 1];
71
- assert.ok(last.role === 'user' && last.content.includes('#9'));
72
- assert.ok(eng._payloadTokens(payload.slice(1)) <= 120 + estSlack(last));
73
- function estSlack(m) {
74
- return Math.ceil((m.content || '').length / 4) * 3; // tek mesaj taşabilir
75
- }
76
- });
77
-
78
- test('browser_open/browser_read kancaları çağrılır', async () => {
79
- const calls = [];
80
- const eng = makeEngine({
81
- browser: {
82
- openUrl: async (u) => { calls.push(['open', u]); return { ok: true, url: u, title: 'T' }; },
83
- readText: async () => { calls.push(['read']); return { ok: true, content: 'sayfa metni' }; },
84
- },
85
- });
86
- const r1 = JSON.parse(await eng._execTool('browser_open', { url: 'https://example.com' }, null, 's1'));
87
- const r2 = JSON.parse(await eng._execTool('browser_read', {}, null, 's1'));
88
- assert.ok(r1.ok && r1.title === 'T');
89
- assert.ok(r2.ok && r2.content.includes('metni'));
90
- assert.deepEqual(calls, [['open', 'https://example.com'], ['read']]);
91
- });
92
-
93
- test('browser kancası yoksa zarif hata döner', async () => {
94
- const eng = makeEngine();
95
- const r = JSON.parse(await eng._execTool('browser_open', { url: 'https://x.com' }, null, 's1'));
96
- assert.equal(r.ok, false);
97
- assert.match(r.error, /tarayıcı/);
98
- });
99
-
100
- test('eski görseller metne indirilir, son mesajın görseli korunur', () => {
101
- const eng = makeEngine();
102
- const img = (u) => [{ type: 'text', text: 'bak' }, { type: 'image_url', image_url: { url: u } }];
103
- const msgs = [
104
- { role: 'user', content: img('data:image/png;base64,AAAA') },
105
- { role: 'assistant', content: 'ok' },
106
- { role: 'user', content: img('data:image/png;base64,BBBB') },
107
- ];
108
- const payload = eng._buildPayload('sys', msgs);
109
- const firstUser = payload.find((m) => m.role === 'user');
110
- assert.equal(typeof firstUser.content, 'string');
111
- assert.ok(firstUser.content.includes('bağlam dışı'));
112
- const lastUser = payload[payload.length - 1];
113
- assert.ok(Array.isArray(lastUser.content));
114
- });
115
-
116
- /* ---------- oturum notlar� (ge�ici haf�za) ---------- */
117
-
118
- test('oturum notlar�: dosyadan y�klenir, mesaj listesine kar��maz', () => {
119
- const eng = makeEngine();
120
- const id = eng.createSession().id;
121
- for (let i = 0; i < 5; i++) {
122
- fs.appendFileSync(eng._file(id), JSON.stringify({ t: 'msg', role: 'user', content: 'mesaj ' + i }) + '\n');
123
- }
124
- fs.appendFileSync(eng._file(id), JSON.stringify({ t: 'notes', text: 'hedef: v7 �al�smas�', at: 3 }) + '\n');
125
- eng.cache.delete(id); // diskten taze y�kle
126
- const s = eng._load(id);
127
- assert.equal(s.notes, 'hedef: v7 �al�smas�');
128
- assert.equal(s.notesAt, 3);
129
- assert.equal(s.messages.length, 5);
130
- });
131
-
132
- test('oturum notlar� varken payload s�k�la��r', () => {
133
- const eng = makeEngine({ historyTokenBudget: 100000 });
134
- const msgs = [];
135
- for (let i = 0; i < 30; i++) msgs.push({ role: 'user', content: 'kisa mesaj #' + i });
136
- const withoutNotes = eng._buildPayload('sys', msgs, null);
137
- const withNotes = eng._buildPayload('sys', msgs, '�zet: test');
138
- assert.ok(withNotes.length < withoutNotes.length);
139
- assert.equal(withNotes[withNotes.length - 1].content, 'kisa mesaj #29'); // son mesaj hep kal�r
140
- });
141
-
142
- /* ---------- observe(): sessiz bağlam enjeksiyonu ---------- */
143
-
144
- test('observe: mesaj geçmişe düşer ama run tetiklenmez', async () => {
145
- const events = [];
146
- const eng = makeEngine({ emit: (ev) => events.push(ev) });
147
- const id = eng.createSession().id;
148
- const ok = eng.observe(id, '[BAĞLAM — grup] ali: selam');
149
- assert.ok(ok);
150
- const s = eng._load(id);
151
- assert.equal(s.messages.length, 1);
152
- assert.equal(s.messages[0].role, 'user');
153
- assert.ok(s.messages[0].content.startsWith('[BAĞLAM'));
154
- // _run tetiklenmedi: LLM hatası/done olayı yok
155
- await new Promise((r) => setTimeout(r, 60));
156
- assert.ok(!events.some((e) => e.type === 'error' || e.type === 'done'));
157
- // observe olayı yayıldı
158
- assert.ok(events.some((e) => e.type === 'observe' && e.sessionId === id));
159
- // boş metin false
160
- assert.equal(eng.observe(id, ' '), false);
161
- });
162
-
163
- test('observe: ardışık bağlam mesajları tek mesajda birleşir', () => {
164
- const eng = makeEngine();
165
- const id = eng.createSession().id;
166
- eng.observe(id, '[BAĞLAM — grup] ali: selam');
167
- eng.observe(id, '[BAĞLAM — grup] ayşe: nasılsın');
168
- const s = eng._load(id);
169
- assert.equal(s.messages.length, 1);
170
- assert.ok(s.messages[0].content.includes('ali: selam'));
171
- assert.ok(s.messages[0].content.includes('ayşe: nasılsın'));
172
- // dosyaya da tek satır yazıldı
173
- const lines = fs.readFileSync(eng._file(id), 'utf8').split('\n').filter((l) => l.trim());
174
- const msgLines = lines.filter((l) => { try { return JSON.parse(l).t === 'msg'; } catch { return false; } });
175
- assert.equal(msgLines.length, 1);
176
- });
177
-
178
- test('observe: normal user mesajıyla birleşmez', () => {
179
- const eng = makeEngine();
180
- const id = eng.createSession().id;
181
- const s = eng._load(id);
182
- s.messages.push({ role: 'user', content: 'gerçek soru' });
183
- fs.appendFileSync(eng._file(id), JSON.stringify({ t: 'msg', role: 'user', content: 'gerçek soru' }) + '\n');
184
- eng.observe(id, '[BAĞLAM — grup] ali: selam');
185
- assert.equal(s.messages.length, 2);
186
- assert.equal(s.messages[0].content, 'gerçek soru');
187
- assert.ok(s.messages[1].content.startsWith('[BAĞLAM'));
188
- });
189
-
190
- test('send: mention mesajı bekleyen bağlam mesajıyla birleşir ve run başlar', async () => {
191
- const events = [];
192
- const eng = makeEngine({ emit: (ev) => events.push(ev) });
193
- const id = eng.createSession().id;
194
- eng.observe(id, '[BAĞLAM — grup] ali: selam\nayşe: nasılsın');
195
- eng.send(id, { text: '@beast bana bugün hava nasıl?' });
196
- const s = eng._load(id);
197
- assert.equal(s.messages.length, 1);
198
- assert.ok(s.messages[0].content.includes('ayşe: nasılsın'));
199
- assert.ok(s.messages[0].content.includes('@beast bana bugün hava nasıl?'));
200
- // run başladı → sağlayıcı yoksa hata olayı düşer (tetiklenme kanıtı)
201
- await new Promise((r) => setTimeout(r, 120));
202
- assert.ok(events.some((e) => e.type === 'error' || e.type === 'done'));
203
- });
@@ -1,91 +0,0 @@
1
- 'use strict';
2
-
3
- /* #4 Eval düzeni: scenarios.json'daki senaryoları koşar.
4
- Yeni modül eklerken buraya senaryo düşür — regresyon kalkanı olur. */
5
-
6
- require('./setup');
7
- const test = require('node:test');
8
- const assert = require('node:assert');
9
- const path = require('path');
10
- const fs = require('fs');
11
-
12
- const SCEN = JSON.parse(fs.readFileSync(path.join(__dirname, 'scenarios.json'), 'utf8'));
13
- const cron = require('../src/cron');
14
- const Engine = require('../src/agent/engine');
15
- const memory = require('../src/agent/memory');
16
- const kb = require('../src/agent/kb');
17
- const bus = require('../src/agent/bus');
18
- const usageMod = require('../src/agent/usage');
19
-
20
- function scenario(id) {
21
- return SCEN.scenarios.find((s) => s.id === id);
22
- }
23
-
24
- test('eval: cron-invalid-rejected', () => {
25
- const s = scenario('cron-invalid-rejected');
26
- assert.equal(cron[s.call.fn](...s.call.args), null);
27
- });
28
-
29
- test('eval: reminder-daily-schedule', () => {
30
- const s = scenario('reminder-daily-schedule');
31
- const r = cron[s.call.fn](...s.call.args);
32
- assert.equal(r.schedule, s.expectEquals.schedule);
33
- });
34
-
35
- test('eval: kb-search-citation', () => {
36
- const s = scenario('kb-search-citation');
37
- kb.add('FxPro GOLD seansları', 'GOLD sembolünde seans 01:00-23:00 arası açıktır', { source: 'test' });
38
- const rows = kb.search('gold seans');
39
- assert.ok(rows.length >= 1);
40
- for (const r of rows) {
41
- assert.ok(r.citation && r.citation.includes('['), 'citation formatı');
42
- assert.ok(r.title && r.snippet !== undefined);
43
- }
44
- });
45
-
46
- test('eval: memory-hygiene-dedup', () => {
47
- const s = scenario('memory-hygiene-dedup');
48
- assert.equal(s.module, 'memory');
49
- memory.append('Kullanıcının adı Batuhan');
50
- memory.append('kullanıcının adı batuhan'); // dup (fold edilmiş)
51
- const r = memory.hygiene({ maxAgeDays: 0 }); // yalnız dedup, yaş silmesi yok
52
- assert.ok(r.ok);
53
- const kept = memory.entries().filter((l) => /batuhan/i.test(l));
54
- assert.ok(kept.length <= 1, 'duplike tekilleşmeli');
55
- // temizlik
56
- for (const l of memory.entries()) if (/batuhan/i.test(l)) memory.removeRule('__none__'); // no-op koruması
57
- });
58
-
59
- test('eval: system-prompt-has-local-time', () => {
60
- const os = require('os');
61
- const eng = new Engine({}, { sessionsDir: fs.mkdtempSync(path.join(os.tmpdir(), 'beast-eval-')) });
62
- const sys = eng.buildSystem('x');
63
- assert.match(sys, new RegExp(scenario('system-prompt-has-local-time').regex));
64
- });
65
-
66
- test('eval: system-prompt-rules-injected', () => {
67
- const os = require('os');
68
- const eng = new Engine({}, { sessionsDir: fs.mkdtempSync(path.join(os.tmpdir(), 'beast-eval2-')) });
69
- memory.addRule('Eval test kuralı ABCXYZ');
70
- const sys = eng.buildSystem('x');
71
- assert.match(sys, /KURALLAR/);
72
- assert.match(sys, /Eval test kuralı ABCXYZ/);
73
- memory.removeRule('Eval test kuralı ABCXYZ');
74
- });
75
-
76
- test('eval: usage-cost-math kısa doğrulama', () => {
77
- usageMod.reset();
78
- usageMod.record({ providerId: 'e', model: 'm', promptTokens: 1000000, completionTokens: 0, costIn: 1, costOut: 0 });
79
- assert.ok(Math.abs(usageMod.report().today.total.cost - 1) < 1e-6);
80
- });
81
-
82
- test('eval: bus-price-filter', () => {
83
- for (const s of bus.listSubs()) bus.removeSub(s.id);
84
- const r = bus.addSub({ type: 'price:tick', sessionId: 'evalsestest', op: 'gte', value: 100, cooldownMin: 0 });
85
- assert.ok(r.ok);
86
- const fired = bus.emitEvent('price:tick', { value: 150 }, Date.now());
87
- assert.equal(fired, 1); // sadece bu abonelik
88
- const notFired = bus.emitEvent('price:tick', { value: 50 }, Date.now());
89
- assert.equal(notFired, 0);
90
- for (const s of bus.listSubs()) bus.removeSub(s.id);
91
- });