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,188 +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 } = require('../src/agent/engine');
11
-
12
- function tmpDir(prefix) {
13
- return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
14
- }
15
-
16
- /* pA::m1 tek sağlayıcı + çökme kayıt dosyası olan motor */
17
- function makeFalloutEngine({ fallout, emitEvents } = {}) {
18
- const sessionsDir = tmpDir('beast-fall-sess-');
19
- const workspace = tmpDir('beast-fall-ws-');
20
- const crashFile = path.join(tmpDir('beast-fall-crash-'), 'fallout-crash.json');
21
- const chainEntry = {
22
- providerId: 'pA',
23
- providerName: 'ProvA',
24
- model: 'm1',
25
- url: 'https://a.test/v1/chat/completions',
26
- key: 'keyA',
27
- };
28
- const cfg = { chain: [chainEntry], defaultSelection: { ...chainEntry } };
29
- const events = [];
30
- const eng = new Engine(cfg, {
31
- sessionsDir,
32
- workspace,
33
- crashFile,
34
- modelOverride: 'pA::m1',
35
- fallout,
36
- emit: (ev) => events.push(ev),
37
- });
38
- if (emitEvents) emitEvents(events);
39
- return { eng, crashFile, events };
40
- }
41
-
42
- test('fallout: bozuk slotlar elenir, en fazla 10 kalır', () => {
43
- const eng = makeFalloutEngine().eng;
44
- const norm = eng._normalizeFallout({
45
- enabled: true,
46
- slots: [
47
- null,
48
- { providerId: '', model: 'x', key: 'k' },
49
- { providerId: 'p', model: '', key: 'k' },
50
- { providerId: 'p', model: 'm', key: '' },
51
- { providerId: 'pA', providerName: 'ProvA', model: 'm1', key: 'k1' },
52
- ...Array.from({ length: 12 }, (_, i) => ({
53
- providerId: 'p' + i,
54
- providerName: 'P' + i,
55
- model: 'm',
56
- key: 'k',
57
- })),
58
- ],
59
- });
60
- assert.equal(norm.enabled, true);
61
- assert.equal(norm.chain.length, 10); // 1 geçerli + 11 aday ama sınır 10
62
- assert.deepEqual(norm.chain[0], { providerId: 'pA', providerName: 'ProvA', model: 'm1', key: 'k1' });
63
- });
64
-
65
- test('fallout: kapalıyken zincir boş döner', () => {
66
- const { eng } = makeFalloutEngine({
67
- fallout: { enabled: false, slots: [{ providerId: 'pA', model: 'm1', key: 'other' }] },
68
- });
69
- assert.deepEqual(eng._falloutSelections(eng.sel), []);
70
- });
71
-
72
- test('fallout: aynı provider+model farklı key ile listelenir, birebir aynı olan atlanır', () => {
73
- const { eng } = makeFalloutEngine({
74
- fallout: {
75
- enabled: true,
76
- slots: [
77
- { providerId: 'pA', providerName: 'ProvA', model: 'm1', key: 'keyA' }, // aktif ile aynı -> atla
78
- { providerId: 'pA', providerName: 'ProvA', model: 'm1', key: 'keyA2' }, // farklı key -> kalsın
79
- { providerId: 'yok', model: 'm9', key: 'k' }, // kayıtlı değil -> atla
80
- ],
81
- },
82
- });
83
- const sels = eng._falloutSelections(eng.sel);
84
- assert.equal(sels.length, 1);
85
- assert.equal(sels[0].url, 'https://a.test/v1/chat/completions'); // URL zincirden gelir
86
- assert.equal(sels[0].key, 'keyA2');
87
- });
88
-
89
- test('fallout: çökme kaydı yazılır ve silinir', () => {
90
- const { eng, crashFile } = makeFalloutEngine({});
91
- eng._saveCrash('sess1', new Error('patladı'), 'provider');
92
- assert.ok(fs.existsSync(crashFile));
93
- const st = JSON.parse(fs.readFileSync(crashFile, 'utf8'));
94
- assert.equal(st.sessionId, 'sess1');
95
- assert.match(st.error, /patladı/);
96
- assert.equal(st.phase, 'provider');
97
- eng._clearCrash();
98
- assert.ok(!fs.existsSync(crashFile));
99
- });
100
-
101
- function sseResponse(sseText) {
102
- const enc = new TextEncoder();
103
- const stream = new ReadableStream({
104
- start(c) {
105
- c.enqueue(enc.encode(sseText));
106
- c.close();
107
- },
108
- });
109
- return new Response(stream, { status: 200, headers: { 'Content-Type': 'text/event-stream' } });
110
- }
111
-
112
- const SSE_OK =
113
- 'data: {"choices":[{"delta":{"content":"merhaba"}}]}\n\n' +
114
- 'data: {"choices":[{"delta":{},"finish_reason":"stop"}]}\n\n' +
115
- 'data: [DONE]\n\n';
116
-
117
- test('fallout: ilk sağlayıcı 401 çöker → zincirdeki sıradaki key ile devam eder', async () => {
118
- const { eng, crashFile, events } = makeFalloutEngine({
119
- fallout: {
120
- enabled: true,
121
- slots: [{ providerId: 'pA', providerName: 'ProvA', model: 'm1', key: 'keyYedek' }],
122
- },
123
- });
124
-
125
- const calls = [];
126
- const realFetch = globalThis.fetch;
127
- globalThis.fetch = async (url, opts = {}) => {
128
- calls.push({ url: String(url), auth: opts.headers && opts.headers.Authorization });
129
- if (calls.length === 1) {
130
- return new Response(JSON.stringify({ error: { message: 'bad key' } }), {
131
- status: 401,
132
- statusText: 'Unauthorized',
133
- });
134
- }
135
- return sseResponse(SSE_OK);
136
- };
137
- try {
138
- const res = await eng._streamWithFallbacks(
139
- { id: 's1', messages: [{ role: 'user', content: 'selam' }] },
140
- [{ role: 'user', content: 'selam' }],
141
- [],
142
- null,
143
- () => {},
144
- eng.sel,
145
- false
146
- );
147
- assert.equal(res.content, 'merhaba');
148
- } finally {
149
- globalThis.fetch = realFetch;
150
- }
151
-
152
- assert.equal(calls.length, 2);
153
- assert.match(calls[0].auth, /keyA$/);
154
- assert.match(calls[1].auth, /keyYedek$/);
155
- // durumu kaydet adımı çalıştı
156
- assert.ok(fs.existsSync(crashFile), 'çökme kaydı yazılmalı');
157
- // durum mesajı Fallout geçişini haber verdi
158
- const st = events.find((e) => e.type === 'status' && /FALLOUT/.test(e.status));
159
- assert.ok(st, 'FALLOUT durum mesajı beklenirdi');
160
- });
161
-
162
- test('fallout: kapalıysa tek denemede hatayı fırlatır', async () => {
163
- const { eng } = makeFalloutEngine({ fallout: { enabled: false, slots: [] } });
164
- const realFetch = globalThis.fetch;
165
- let n = 0;
166
- globalThis.fetch = async () => {
167
- n++;
168
- return new Response('{}', { status: 401, statusText: 'Unauthorized' });
169
- };
170
- try {
171
- await assert.rejects(
172
- () =>
173
- eng._streamWithFallbacks(
174
- { id: 's1', messages: [] },
175
- [{ role: 'user', content: 'x' }],
176
- [],
177
- null,
178
- () => {},
179
- eng.sel,
180
- false
181
- ),
182
- /HTTP 401/
183
- );
184
- } finally {
185
- globalThis.fetch = realFetch;
186
- }
187
- assert.equal(n, 1);
188
- });
package/tests/llm.test.js DELETED
@@ -1,205 +0,0 @@
1
- 'use strict';
2
-
3
- require('./setup');
4
- const test = require('node:test');
5
- const assert = require('node:assert');
6
- const { withRetries, friendlyError, sumUsage, chatStreamAuto, setNetProbe, isNetworkError } = require('../src/agent/llm');
7
-
8
- test('withRetries: 503 sonrası başarılı isteği tekrarlar', async () => {
9
- let calls = 0;
10
- const r = await withRetries(
11
- async () => {
12
- calls++;
13
- if (calls < 3) {
14
- const e = new Error('HTTP 503');
15
- e.status = 503;
16
- throw e;
17
- }
18
- return 'ok';
19
- },
20
- { onRetry: () => {} }
21
- );
22
- assert.equal(r, 'ok');
23
- assert.equal(calls, 3);
24
- });
25
-
26
- test('withRetries: 400 gibi kalıcı hata beklemeksizin fırlatır', async () => {
27
- let calls = 0;
28
- await assert.rejects(
29
- () =>
30
- withRetries(async () => {
31
- calls++;
32
- const e = new Error('HTTP 400');
33
- e.status = 400;
34
- throw e;
35
- }),
36
- /400/
37
- );
38
- assert.equal(calls, 1);
39
- });
40
-
41
- test('withRetries: ağ hatası (status yok) tekrarlanır', async () => {
42
- let calls = 0;
43
- const r = await withRetries(async () => {
44
- calls++;
45
- if (calls === 1) throw new Error('fetch failed');
46
- return 'ok';
47
- });
48
- assert.equal(r, 'ok');
49
- assert.equal(calls, 2);
50
- });
51
-
52
- test('friendlyError ipuçları içerir', () => {
53
- const m = friendlyError(503, 'Service Unavailable', '{"error":"x"}');
54
- assert.match(m, /kullanılamıyor/);
55
- assert.match(m, /model seçici/);
56
- });
57
-
58
- test('sumUsage: sayısal alanları toplar (devam turu faturalaması)', () => {
59
- const u = sumUsage(
60
- { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
61
- { prompt_tokens: 8, completion_tokens: 3, total_tokens: 11 }
62
- );
63
- assert.deepEqual(u, { prompt_tokens: 18, completion_tokens: 8, total_tokens: 26 });
64
- assert.equal(sumUsage(null, { total_tokens: 3 }).total_tokens, 3);
65
- assert.equal(sumUsage({ total_tokens: 7 }, null).total_tokens, 7);
66
- });
67
-
68
- /* SSE mock sunucusu: finish_reason=length → otomatik devam turu açılır */
69
- function sseServer(script) {
70
- const http = require('node:http');
71
- const calls = [];
72
- const srv = http.createServer((req, res) => {
73
- let body = '';
74
- req.on('data', (c) => (body += c));
75
- req.on('end', () => {
76
- calls.push(JSON.parse(body).messages);
77
- res.writeHead(200, { 'Content-Type': 'text/event-stream' });
78
- for (const ev of script(calls.length, calls[calls.length - 1])) res.write(ev);
79
- res.end();
80
- });
81
- });
82
- return { srv, calls };
83
- }
84
-
85
- test('chatStreamAuto: length kesintisinde kaldığı yerden devam eder', async () => {
86
- const { srv, calls } = sseServer((n) => {
87
- if (n === 1) {
88
- return [
89
- 'data: ' + JSON.stringify({ choices: [{ delta: { content: 'Merhaba dun' } }] }) + '\n\n',
90
- 'data: ' + JSON.stringify({ choices: [{ delta: { content: 'ya basladi' }, finish_reason: 'length' }] }) + '\n\n',
91
- 'data: [DONE]\n\n',
92
- ];
93
- }
94
- return [
95
- 'data: ' + JSON.stringify({ choices: [{ delta: { content: 'yor devam' } }] }) + '\n\n',
96
- 'data: ' + JSON.stringify({ choices: [{ delta: {}, finish_reason: 'stop' }] }) + '\n\n',
97
- 'data: [DONE]\n\n',
98
- ];
99
- });
100
- await new Promise((r) => srv.listen(0, '127.0.0.1', r));
101
- try {
102
- const port = srv.address().port;
103
- const sel = { url: `http://127.0.0.1:${port}/v1/chat/completions`, key: 'k', model: 'm' };
104
- const deltas = [];
105
- const r = await chatStreamAuto(sel, { messages: [{ role: 'user', content: 'selam' }] }, { onDelta: (d) => deltas.push(d) });
106
- assert.equal(r.content, 'Merhaba dunya basladiyor devam');
107
- assert.equal(r.finishReason, 'stop');
108
- assert.equal(calls.length, 2);
109
- // 2. turda devam mesajları eklenmiş olmalı (orijinal dizi kopyalanır)
110
- const m2 = calls[1];
111
- assert.equal(m2[m2.length - 2].role, 'assistant');
112
- assert.equal(m2[m2.length - 1].role, 'user');
113
- assert.match(m2[m2.length - 1].content, /DEVAM/);
114
- // streaming kesintisiz: 3 delta
115
- assert.deepEqual(deltas.join(''), r.content);
116
- } finally {
117
- srv.close();
118
- }
119
- });
120
-
121
- test('chatStreamAuto: normal (stop) yanıtta hiç devam turu açılmaz', async () => {
122
- const { srv, calls } = sseServer(() => [
123
- 'data: ' + JSON.stringify({ choices: [{ delta: { content: 'tam cevap' }, finish_reason: 'stop' }] }) + '\n\n',
124
- 'data: [DONE]\n\n',
125
- ]);
126
- await new Promise((r) => srv.listen(0, '127.0.0.1', r));
127
- try {
128
- const port = srv.address().port;
129
- const sel = { url: `http://127.0.0.1:${port}/v1/chat/completions`, key: 'k', model: 'm' };
130
- const r = await chatStreamAuto(sel, { messages: [{ role: 'user', content: 'selam' }] });
131
- assert.equal(r.content, 'tam cevap');
132
- assert.equal(calls.length, 1);
133
- } finally {
134
- srv.close();
135
- }
136
- });
137
-
138
- /* ---------- ağ kopması dayanıklılığı (#retry) ---------- */
139
-
140
- test('isNetworkError sınıflandırması', () => {
141
- const net = new Error('fetch failed');
142
- assert.equal(isNetworkError(net), true, 'status yok → ağ hatası');
143
- const http = new Error('HTTP 503');
144
- http.status = 503;
145
- assert.equal(isNetworkError(http), false, 'status var → HTTP hatası');
146
- const abort = new Error('iptal');
147
- abort.name = 'AbortError';
148
- assert.equal(isNetworkError(abort), false, 'abort ağ hatası sayılmaz');
149
- assert.equal(isNetworkError(null), false);
150
- });
151
-
152
- test('withRetries: internet kapalıyken probe bekler, dönünce başarır', async () => {
153
- let online = false;
154
- setNetProbe(() => online);
155
- setTimeout(() => { online = true; }, 30);
156
- let calls = 0;
157
- const t0 = Date.now();
158
- const r = await withRetries(async () => {
159
- calls++;
160
- if (calls === 1) throw new Error('fetch failed');
161
- return 'ok';
162
- });
163
- assert.equal(r, 'ok');
164
- assert.equal(calls, 2);
165
- assert.ok(Date.now() - t0 > 2000, 'probe çevrimdışı dediği için bekledi');
166
- setNetProbe(null);
167
- });
168
-
169
- test('akış ortasında bağlantı koparsa: kısmi metinle devam turu açılır (hata balonu yok)', async () => {
170
- const http = require('node:http');
171
- const calls = [];
172
- const srv = http.createServer((req, res) => {
173
- let body = '';
174
- req.on('data', (c) => (body += c));
175
- req.on('end', () => {
176
- calls.push(JSON.parse(body).messages);
177
- if (calls.length === 1) {
178
- /* birkaç delta yaz, sonra soketi ORTADAN kopar */
179
- res.writeHead(200, { 'Content-Type': 'text/event-stream' });
180
- res.write('data: ' + JSON.stringify({ choices: [{ delta: { content: 'Merhaba du' } }] }) + '\n\n');
181
- setTimeout(() => res.destroy(), 30);
182
- return;
183
- }
184
- res.writeHead(200, { 'Content-Type': 'text/event-stream' });
185
- res.write('data: ' + JSON.stringify({ choices: [{ delta: { content: 'nya devam' } }] }) + '\n\n');
186
- res.write('data: ' + JSON.stringify({ choices: [{ delta: {}, finish_reason: 'stop' }] }) + '\n\n');
187
- res.write('data: [DONE]\n\n');
188
- res.end();
189
- });
190
- });
191
- await new Promise((r) => srv.listen(0, '127.0.0.1', r));
192
- try {
193
- const port = srv.address().port;
194
- const sel = { url: `http://127.0.0.1:${port}/v1/chat/completions`, key: 'k', model: 'm' };
195
- const r = await chatStreamAuto(sel, { messages: [{ role: 'user', content: 'selam' }] });
196
- assert.equal(r.content, 'Merhaba dunya devam', 'kısmi + devam birleşti');
197
- assert.equal(r.finishReason, 'stop');
198
- assert.equal(calls.length, 2, 'devam turu açıldı');
199
- const m2 = calls[1];
200
- assert.equal(m2[m2.length - 2].role, 'assistant');
201
- assert.match(m2[m2.length - 1].content, /DEVAM/);
202
- } finally {
203
- srv.close();
204
- }
205
- });
@@ -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
- });