beast-agent 0.15.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 (62) hide show
  1. package/.env.example +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +94 -0
  4. package/assets/app.ico +0 -0
  5. package/assets/tray.png +0 -0
  6. package/bin/beast-agent.js +23 -0
  7. package/config.example.yaml +26 -0
  8. package/eng.traineddata +0 -0
  9. package/package.json +101 -0
  10. package/scripts/swap-electron.js +40 -0
  11. package/src/agent/bus.js +384 -0
  12. package/src/agent/computeruse.js +200 -0
  13. package/src/agent/config.js +284 -0
  14. package/src/agent/engine.js +3118 -0
  15. package/src/agent/kb.js +123 -0
  16. package/src/agent/llm.js +219 -0
  17. package/src/agent/logger.js +90 -0
  18. package/src/agent/memory.js +383 -0
  19. package/src/agent/pdf.js +20 -0
  20. package/src/agent/scripts/__pycache__/websearch.cpython-312.pyc +0 -0
  21. package/src/agent/scripts/news.py +113 -0
  22. package/src/agent/scripts/websearch.py +225 -0
  23. package/src/agent/skills.js +522 -0
  24. package/src/agent/tokens.js +39 -0
  25. package/src/agent/tools.js +911 -0
  26. package/src/agent/usage.js +125 -0
  27. package/src/agent/watchers.js +312 -0
  28. package/src/agent/watext.js +75 -0
  29. package/src/agent/whatsapp.js +494 -0
  30. package/src/cron.js +245 -0
  31. package/src/main.js +3622 -0
  32. package/src/preload.js +122 -0
  33. package/src/renderer/browserPreload.js +73 -0
  34. package/src/renderer/i18n.js +757 -0
  35. package/src/renderer/index.html +227 -0
  36. package/src/renderer/renderer.js +3250 -0
  37. package/src/renderer/style.css +1559 -0
  38. package/tests/approval.test.js +56 -0
  39. package/tests/bg-jobs.test.js +315 -0
  40. package/tests/bus.test.js +46 -0
  41. package/tests/computeruse-context.test.js +69 -0
  42. package/tests/cron.test.js +95 -0
  43. package/tests/engine.test.js +140 -0
  44. package/tests/eval.test.js +91 -0
  45. package/tests/fallout.test.js +188 -0
  46. package/tests/llm.test.js +56 -0
  47. package/tests/memory.test.js +33 -0
  48. package/tests/memoryloop.test.js +26 -0
  49. package/tests/notegen.test.js +63 -0
  50. package/tests/owner.test.js +32 -0
  51. package/tests/python.test.js +75 -0
  52. package/tests/scenarios.json +81 -0
  53. package/tests/sessioncode.test.js +50 -0
  54. package/tests/setup.js +9 -0
  55. package/tests/tokens.test.js +42 -0
  56. package/tests/tools.test.js +84 -0
  57. package/tests/usage.test.js +44 -0
  58. package/tests/v11.test.js +76 -0
  59. package/tests/watchers.test.js +206 -0
  60. package/tests/watext.test.js +53 -0
  61. package/tests/whatsapp.test.js +103 -0
  62. package/tests/wherewasi.test.js +40 -0
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ require('./setup');
4
+ const test = require('node:test');
5
+ const assert = require('node:assert');
6
+ const { waCleanText, waToolLine } = require('../src/agent/watext');
7
+
8
+ /* #16: TÜM markdown işaretleri temizlenir — WhatsApp'ta bold/başlık yok */
9
+ test('markdown işaretleri tamamen temizlenir (#16)', () => {
10
+ assert.equal(waCleanText('**selam** dünya'), 'selam dünya');
11
+ assert.equal(waCleanText('*vurgu* bitti'), 'vurgu bitti');
12
+ assert.equal(waCleanText('***üç***'), 'üç');
13
+ assert.equal(waCleanText('## Başlık\nmetin'), 'Başlık\nmetin');
14
+ assert.ok(!waCleanText('a *b* c #d').includes('*'));
15
+ assert.ok(!waCleanText('a *b* c #d').includes('#'));
16
+ assert.ok(waCleanText('fiyat #kolon başlığı').length > 0); // kareli metin bozulmadan gelir, işaret gider
17
+ });
18
+
19
+ test('başlık kareleri ve alıntı işaretleri gider', () => {
20
+ assert.equal(waCleanText('> alıntı satırı'), 'alıntı satırı');
21
+ assert.match(waToolLine('run_command', { command: 'npm test' }), /Terminal: npm test/);
22
+ });
23
+
24
+ test('liste tireleri madde imine döner', () => {
25
+ assert.equal(waCleanText('- bir\n- iki'), '\u2022 bir\n\u2022 iki');
26
+ assert.equal(waCleanText('* elma\n+ armut'), '\u2022 elma\n\u2022 armut');
27
+ });
28
+
29
+ test('snake_case alt çizgisi korunur, _italik_ temizlenir', () => {
30
+ assert.equal(waCleanText('file_name değişmedi'), 'file_name değişmedi');
31
+ assert.equal(waCleanText('bu _önemli_ bir konu'), 'bu önemli bir konu');
32
+ });
33
+
34
+ test('linkler düz metne döner', () => {
35
+ assert.equal(waCleanText('[site](https://x.com) bak'), 'site (https://x.com) bak');
36
+ });
37
+
38
+ test('kod bloğu fence gider, içerik kalır', () => {
39
+ const out = waCleanText('```js\nconsole.log(1)\n```');
40
+ assert.equal(out, 'console.log(1)');
41
+ });
42
+
43
+ test('yatay çizgiler ve fazla boşluklar silinir', () => {
44
+ assert.equal(waCleanText('üst\n---\nalt'), 'üst\nalt');
45
+ assert.equal(waCleanText('a\n\n\n\nb'), 'a\n\nb');
46
+ });
47
+
48
+ test('araç satırları düz metin ve kırpılır', () => {
49
+ assert.equal(waToolLine('read_file', { path: 'C:\\a.txt' }), '\u203A Dosya oku: C:\\a.txt');
50
+ assert.equal(waToolLine('web_search', { query: 'btc fiyat' }), '\u203A Web arama: btc fiyat');
51
+ assert.match(waToolLine('bilinmeyen_arac', {}), /\u203A bilinmeyen_arac/);
52
+ assert.ok(waToolLine('run_command', { command: 'x'.repeat(500) }).length < 180);
53
+ });
@@ -0,0 +1,103 @@
1
+ 'use strict';
2
+
3
+ require('./setup');
4
+ const test = require('node:test');
5
+ const assert = require('node:assert');
6
+ const { WhatsAppBridge, statusLabel } = require('../src/agent/whatsapp');
7
+
8
+ function makeBridge() {
9
+ const events = [];
10
+ const wa = new WhatsAppBridge({ authDir: '/tmp/x', emit: (ev) => events.push(ev) });
11
+ return { wa, events };
12
+ }
13
+
14
+ test('statusLabel bilinen durumları etiketler', () => {
15
+ assert.equal(statusLabel(2), 'gönderildi ✓');
16
+ assert.equal(statusLabel(3), 'teslim ✓✓');
17
+ assert.equal(statusLabel(4), 'okundu ✓✓');
18
+ assert.equal(statusLabel(0), 'hata');
19
+ });
20
+
21
+ test('statusLabel bilinmeyen durum', () => {
22
+ assert.match(statusLabel(99), /bilinmeyen\(99\)/);
23
+ });
24
+
25
+ test('_trackOutgoing send eventi yayar ve takibe alır', () => {
26
+ const { wa, events } = makeBridge();
27
+ wa._trackOutgoing('90555@s.whatsapp.net', 'merhaba dunya', { key: { id: 'M1' } });
28
+ assert.equal(events.length, 1);
29
+ assert.equal(events[0].type, 'send');
30
+ assert.equal(events[0].id, 'M1');
31
+ assert.ok(wa._tracked.has('M1'));
32
+ assert.equal(wa._tracked.get('M1').preview, 'merhaba dunya');
33
+ });
34
+
35
+ test('_trackOutgoing key yoksa sessizce atlar', () => {
36
+ const { wa, events } = makeBridge();
37
+ wa._trackOutgoing('90555@s.whatsapp.net', 'x', null);
38
+ assert.equal(events.length, 0);
39
+ });
40
+
41
+ test('tick: durum değişince event, aynı durumda tekrar yok', () => {
42
+ const { wa, events } = makeBridge();
43
+ wa._tracked.set('A', { jid: '90555@s.whatsapp.net', preview: 'selam', ts: 1, status: 1, receiptDetail: '' });
44
+ wa._handleStatusUpdates([{ key: { id: 'A' }, update: { status: 3 } }]);
45
+ assert.equal(events.length, 1);
46
+ assert.equal(events[0].type, 'tick');
47
+ assert.equal(events[0].label, 'teslim ✓✓');
48
+ events.length = 0;
49
+ wa._handleStatusUpdates([{ key: { id: 'A' }, update: { status: 3 } }]);
50
+ assert.equal(events.length, 0);
51
+ // okundu'ya yükselir
52
+ wa._handleStatusUpdates([{ key: { id: 'A' }, update: { status: 4 } }]);
53
+ assert.equal(events.length, 1);
54
+ assert.equal(events[0].label, 'okundu ✓✓');
55
+ });
56
+
57
+ test('tick: izlenmeyen mesajlar yok sayılır', () => {
58
+ const { wa, events } = makeBridge();
59
+ wa._handleStatusUpdates([{ key: { id: 'BILINMEYEN' }, update: { status: 4 } }]);
60
+ assert.equal(events.length, 0);
61
+ });
62
+
63
+ test('receipt: okundu zamanı detay olarak gelir, tekrarı bastırılır', () => {
64
+ const { wa, events } = makeBridge();
65
+ wa._tracked.set('C', { jid: '90555@s.whatsapp.net', preview: 's', ts: 1, status: 3, receiptDetail: '' });
66
+ const readAt = 1700000000000;
67
+ wa._handleReceipts([{ key: { id: 'C' }, receipt: { readAt } }]);
68
+ assert.equal(events.length, 1);
69
+ assert.equal(events[0].type, 'receipt');
70
+ assert.match(events[0].detail, /^okundu=/);
71
+ events.length = 0;
72
+ wa._handleReceipts([{ key: { id: 'C' }, receipt: { readAt } }]);
73
+ assert.equal(events.length, 0);
74
+ });
75
+
76
+ test('takip haritası CAP ile sınırlı kalır', () => {
77
+ const { wa } = makeBridge();
78
+ for (let i = 0; i < 600; i++) wa._trackOutgoing(`j${i}@s.whatsapp.net`, `m${i}`, { key: { id: `ID${i}` } });
79
+ assert.ok(wa._tracked.size <= 500);
80
+ assert.ok(!wa._tracked.has('ID0'));
81
+ assert.ok(wa._tracked.has('ID599'));
82
+ });
83
+
84
+ test('setWatchJids sock yokken güvenli', async () => {
85
+ const { wa } = makeBridge();
86
+ wa.setWatchJids(['90555@s.whatsapp.net']);
87
+ assert.equal(wa._watchJids.size, 1);
88
+ await wa._subscribePresence(); // patlamamalı
89
+ });
90
+
91
+ test('presence: composing etiketlenir, bilinmeyen durum yok sayılır', () => {
92
+ const { wa, events } = makeBridge();
93
+ wa._handlePresence({
94
+ id: '90555@s.whatsapp.net',
95
+ presences: { '90555:0@s.whatsapp.net': { lastKnownPresence: 'composing' } },
96
+ });
97
+ assert.equal(events.length, 1);
98
+ assert.equal(events[0].type, 'presence');
99
+ assert.equal(events[0].label, 'yazıyor');
100
+ events.length = 0;
101
+ wa._handlePresence({ id: 'x@s.whatsapp.net', presences: { p: { lastKnownPresence: 'uzaylı' } } });
102
+ assert.equal(events.length, 0);
103
+ });
@@ -0,0 +1,40 @@
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
+ test('#5 nerede kaldım: son oturum + yarım todo döner', async () => {
12
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'beast-wwi-'));
13
+ const eng = new Engine({}, { sessionsDir: dir });
14
+
15
+ // iki oturum: eskisi bugün çalışsın, yenisi en son kalan olsun
16
+ const old1 = eng.createSession();
17
+ const act = eng.createSession();
18
+
19
+ eng.send(act.id, { text: 'rapor hazırla ve maili kontrol et' });
20
+ await new Promise((r) => setTimeout(r, 80)); // _run başlasın bitmesin önemli değil
21
+ eng.interrupt(act.id);
22
+ await new Promise((r) => setTimeout(r, 60));
23
+
24
+ // todo yaz (doğrudan araç yolu yerine state enjeksiyonu)
25
+ eng._execTool('todo_write', { items: [
26
+ { title: 'raporu toparla', status: 'done' },
27
+ { title: 'mail taslağı hazırla', status: 'active' },
28
+ { title: 'tabloyu güncelle', status: 'pending' },
29
+ ] }, null, act.id);
30
+
31
+ const w = eng.lastWhereWasI();
32
+ assert.ok(w, 'özet gelmeli');
33
+ assert.equal(w.sessionId, act.id); // en yeni oturum
34
+ assert.ok(w.code && w.code.length === 6);
35
+ assert.ok(w.pendingTodos.length >= 2, 'yarım todolar listelenmeli');
36
+ assert.equal(w.doneCount, 1);
37
+ assert.ok(w.lastUser.includes('rapor'));
38
+
39
+ fs.rmSync(dir, { recursive: true, force: true });
40
+ });