beast-agent 0.26.5 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/engine.js +127 -53
- package/src/agent/obscura.js +245 -0
- package/src/agent/skills.js +14 -10
- package/src/agent/tools.js +105 -51
- package/src/main.js +144 -37
- package/src/preload.js +5 -3
- package/src/renderer/i18n.js +48 -24
- package/src/renderer/renderer.js +132 -44
- package/src/renderer/style.css +11 -0
- package/tests/bg-jobs.test.js +71 -3
- package/tests/engine.test.js +76 -13
- package/tests/obscura.test.js +124 -0
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -19,6 +19,9 @@ const SUB_MAX_TURNS = 8;
|
|
|
19
19
|
const HISTORY_TOKEN_BUDGET = 18000;
|
|
20
20
|
const TOOL_OUT_KEEP = 1200;
|
|
21
21
|
const USER_MAX = 8000;
|
|
22
|
+
/* observe() ile enjekte edilen bağlam mesajlarının öneki — ardışık bağlam
|
|
23
|
+
mesajlarının tek mesajda birleşmesi bu işaretle anlaşılır */
|
|
24
|
+
const OBSERVE_MARK = '[BAĞLAM';
|
|
22
25
|
|
|
23
26
|
/* Oturum notları (geçici hafıza): eski mesajlar bütçeden düşülmeden önce
|
|
24
27
|
önemli noktalar çıkarılır, sistem promptuna küçük blok olarak girer. */
|
|
@@ -1170,6 +1173,8 @@ class Engine {
|
|
|
1170
1173
|
try {
|
|
1171
1174
|
const c = this.ctrls.get(id);
|
|
1172
1175
|
if (c) {
|
|
1176
|
+
this._abortReasons = this._abortReasons || new Map();
|
|
1177
|
+
this._abortReasons.set(String(id), 'oturum silindiği için tur iptal edildi');
|
|
1173
1178
|
c.abort();
|
|
1174
1179
|
this.ctrls.delete(id);
|
|
1175
1180
|
}
|
|
@@ -1179,6 +1184,7 @@ class Engine {
|
|
|
1179
1184
|
const j = this._bgJobs.get(String(id));
|
|
1180
1185
|
j.revive = false;
|
|
1181
1186
|
j.status = 'aborted';
|
|
1187
|
+
j.error = j.error || 'oturum silindi — paralel ajan iptal edildi';
|
|
1182
1188
|
this._clearKicks(id);
|
|
1183
1189
|
}
|
|
1184
1190
|
fs.unlinkSync(this._file(String(id)));
|
|
@@ -1313,7 +1319,7 @@ class Engine {
|
|
|
1313
1319
|
'Tarayıcı eylemlerinin yanıtındaki recent günlüğü ve navigated bilgisini takip et; eylem yanıtları zaten taze snapshot içerir — refler tutarsız görünürse yeni snapshot al.',
|
|
1314
1320
|
'CONUŞMA ODAĞI SENDE KALSIN: kullanıcı seninle konuşurken iş çıkmışsa — uzun da olsa UFACIK da (tek komutluk dizin listesi, tek dosya okuma, tek arama…) — run_background ile PARALEL ajana devret; ana sohbet hiçbir işi beklemez; bittiğinde özet otomatik düşer.',
|
|
1315
1321
|
'Python işleri için python_run kullan: küçük betikler inline code ile; tekrarlayan işler %APPDATA%\\beast\\scripts klasöründeki dosyalarla (ör. news.py = RSS haber toplayıcı: args ["--limit","8","--json"]). Python kurulu olmasa bile ilk çağrıda taşınabilir gömülü runtime otomatik iner.',
|
|
1316
|
-
|
|
1322
|
+
'PYTHON DURUMU: makinede sistem Python\'u görünmese bile ŞAŞIRMA ve "python yok" DEME — python_run aracı kendi taşınabilir runtime\'ını (%APPDATA%\\beast\\py\\python.exe) otomatik indirir/kullanır ve bu klasör run_command PATH\'inde önceliklidir; yani run_command içinde de `python` çalışır. Ham Google/Bing scrape yerine önce web_search aracını kullan (zaten Obscura stealth tarayıcı + TinyFish + Python çoklu-motor destekli), script gerektiğinde python_run yaz.',
|
|
1317
1323
|
'PDF ÇIKTI KURALI: PDF üretirken pip\u2019ten pdf paketi (fpdf, fpdf2, markdown-pdf, weasyprint, reportlab vb.) KURMA/KULLANMA — bunlar Türkçe karakterleri bozar. Doğru kit Node tarafında ZATEN kurulu: `pdf-lib` + `@pdf-lib/fontkit` (Türkçe font gömme) ve `pdfkit`. python_run ile DEĞİL; write_file ile .js script yazıp run_command ile `node script.js` çalıştır. md→pdf çevirici YOKTUR ve kurulmaz: kullanıcıya rapor/özet/belge çıktısı vereceksen .md dosyası gönderme — aynı içeriği DOĞRUDAN pdf-lib/pdfkit ile PDF olarak üret ve send_file ile o PDF\u2019i gönder. Ayrıntılı örnekler: pdf skill\u2019i (SKILL.md).',
|
|
1318
1324
|
'Eski bir hafıza kaydına ihtiyacın olursa memory_search ile ara; kalıcı bilgi/birikim için kb_search kullan, yeni bilgi öğrenirsen kb_add ile kaynak belirt.',
|
|
1319
1325
|
'Bir oturumda 3+ kez memory_write yaptıysan iş bitince memory_hygiene çağır (duplike/eskime temizliği).',
|
|
@@ -1332,7 +1338,8 @@ class Engine {
|
|
|
1332
1338
|
'Görev tanımı kendi başına YETERLİ olsun: tüm bağlam, dosya/URL yolları, kısıtlar ve beklenen çıktı görev metnine yazılır — paralel ajan senin bağlamını göremez.',
|
|
1333
1339
|
'Birden fazla bağımsız iş varsa hepsini aynı anda ayrı run_background çağrılarıyla devret (paralel çalışsınlar).',
|
|
1334
1340
|
'HIZ FAN-OUT: aynı işin farklı adımlarını run_background_many ile TEK SEFERDE paralel aç (örnek: haber taraması → TR / Dünya / Ekonomi ayrı ajan; araştırma+veri çekme+analiz ayrı ajan). Hepsi bitince sana TEK birleşik rapor düşer — hız dramatik artar. Bağımsız 2+ adım görürsen bunu tercih et.',
|
|
1335
|
-
'Takip araçları: tasks_list (genel tablo), task_status (tek ajanın canlı dökümü), task_cancel (iptal). Kullanıcı ilerleme sorarsa buradan raporla.',
|
|
1341
|
+
'Takip araçları: tasks_list (genel tablo), task_status (tek ajanın canlı dökümü), task_cancel (iptal — reason ZORUNLU). Kullanıcı ilerleme sorarsa buradan raporla.',
|
|
1342
|
+
'İPTAL DİSİPLİNİ: bir paralel ajanı task_cancel ile iptal edersen ya da bir işi yarıda bırakırsan, kullanıcıya TEK CÜMLEyle MUTLAKA SEBEBİNİ yaz (neden iptal ettin, ne kayboldu, yerine ne yapacaksın). Sebepsiz iptal YASAK.',
|
|
1336
1343
|
'Bilgi sorularını, sohbeti, planlamayı, hafıza/hatırlatıcı/izleyici kurulumunu YİNE SEN yaparsın — bunlar "iş" değildir.',
|
|
1337
1344
|
].join('\n')
|
|
1338
1345
|
);
|
|
@@ -1611,6 +1618,38 @@ class Engine {
|
|
|
1611
1618
|
return true;
|
|
1612
1619
|
}
|
|
1613
1620
|
|
|
1621
|
+
/* SESSİZ BAĞLAM ENJEKSİYONU: metin oturum geçmişine düşer ama _run()
|
|
1622
|
+
TETİKLENMEZ — ajan "görür", cevap ÜRETMEZ. WhatsApp gruplarında
|
|
1623
|
+
@mention moduyla kullanılır: grubun tüm konuşması bağlam olarak
|
|
1624
|
+
birikir, bot yalnız mention gelince konuşur. Ardışık bağlam
|
|
1625
|
+
mesajları işaret önekli tek user mesajında birleşir (bazı sağlayıcılar
|
|
1626
|
+
art arda user mesajını reddettiği için). */
|
|
1627
|
+
observe(sessionId, text) {
|
|
1628
|
+
const s = this._load(String(sessionId));
|
|
1629
|
+
if (!s) return false;
|
|
1630
|
+
const body = String(text || '').slice(0, USER_MAX);
|
|
1631
|
+
if (!body.trim()) return false;
|
|
1632
|
+
const lastMsg = s.messages[s.messages.length - 1];
|
|
1633
|
+
if (
|
|
1634
|
+
lastMsg && lastMsg.role === 'user' &&
|
|
1635
|
+
typeof lastMsg.content === 'string' &&
|
|
1636
|
+
lastMsg.content.startsWith(OBSERVE_MARK)
|
|
1637
|
+
) {
|
|
1638
|
+
/* önceki bağlam mesajının devamı — tek mesajda birleştir */
|
|
1639
|
+
lastMsg.content = (lastMsg.content + '\n' + body).slice(0, USER_MAX);
|
|
1640
|
+
this._rewriteLastMsg(s.id, lastMsg.content);
|
|
1641
|
+
} else {
|
|
1642
|
+
const msg = { role: 'user', content: body };
|
|
1643
|
+
s.messages.push(msg);
|
|
1644
|
+
try {
|
|
1645
|
+
this._append(s, msg);
|
|
1646
|
+
} catch {}
|
|
1647
|
+
}
|
|
1648
|
+
this.emit({ type: 'observe', sessionId: s.id });
|
|
1649
|
+
this.emit({ type: 'sessions' });
|
|
1650
|
+
return true;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1614
1653
|
/* #14 paralel ajanlar: ANA oturumu hiç bloklamayan arka plan çalıştırıcı.
|
|
1615
1654
|
Ayrı gizli oturum açar, işi orada koşturur; bitince özet bildirim
|
|
1616
1655
|
istenen sohbete düşer. Birden fazla iş aynı anda paralel koşabilir —
|
|
@@ -1812,7 +1851,7 @@ class Engine {
|
|
|
1812
1851
|
if (j.status === 'running' || j.status === 'queued') {
|
|
1813
1852
|
j.status = 'aborted';
|
|
1814
1853
|
j.endedAt = new Date().toISOString();
|
|
1815
|
-
j.error = String(j.error || '') || 'uygulama kapatıldı';
|
|
1854
|
+
j.error = String(j.error || '') || 'uygulama kapatıldı — paralel ajan yarıda kesildi';
|
|
1816
1855
|
migrated = true;
|
|
1817
1856
|
}
|
|
1818
1857
|
j._persisted = true;
|
|
@@ -1905,9 +1944,18 @@ class Engine {
|
|
|
1905
1944
|
this._pendingReports = this._pendingReports || [];
|
|
1906
1945
|
this._pendingReports.push({ parentId: job.parentId, text });
|
|
1907
1946
|
this.flushPendingReports(job.parentId);
|
|
1908
|
-
} else if (status === 'aborted'
|
|
1909
|
-
/*
|
|
1910
|
-
|
|
1947
|
+
} else if (status === 'aborted') {
|
|
1948
|
+
/* İPTAL SEBEBİ ZORUNLU: sebebi kayda yaz ve üst sohbete/gruba bildir */
|
|
1949
|
+
const why = String(errorMsg || job.error || 'sebep belirtilmedi').slice(0, 300);
|
|
1950
|
+
if (grouped) {
|
|
1951
|
+
/* iptal edilen üye gruptan düşmüş sayılır — birleşik raporda SEBEPİYLE görünür */
|
|
1952
|
+
this._bgGroupRecord(job.title, 'aborted', 'İPTAL: ' + why);
|
|
1953
|
+
} else {
|
|
1954
|
+
const text = `[ARKA PLAN İPTAL: ${job.title}]\nSebep: ${why}`;
|
|
1955
|
+
this._pendingReports = this._pendingReports || [];
|
|
1956
|
+
this._pendingReports.push({ parentId: job.parentId, text });
|
|
1957
|
+
this.flushPendingReports(job.parentId);
|
|
1958
|
+
}
|
|
1911
1959
|
}
|
|
1912
1960
|
}
|
|
1913
1961
|
|
|
@@ -2397,8 +2445,21 @@ class Engine {
|
|
|
2397
2445
|
const aborted = e && (e.name === 'AbortError' || ctrl.signal.aborted);
|
|
2398
2446
|
if (aborted) {
|
|
2399
2447
|
this._clearCrash(); // kullanıcı durdurdu — kurtarma yok
|
|
2400
|
-
|
|
2401
|
-
|
|
2448
|
+
/* öz-kurtarma/superyorizon kick'i bekliyorsa bu tur BİTİŞ değildir —
|
|
2449
|
+
işi aborted KAPATMA, kick mesajı yeni turu açar */
|
|
2450
|
+
const kickJob = this._bgJobs && this._bgJobs.get(sid);
|
|
2451
|
+
if (kickJob && kickJob.revive === true) {
|
|
2452
|
+
emit({ type: 'done', aborted: true });
|
|
2453
|
+
} else {
|
|
2454
|
+
/* İPTAL SEBEBİ ZORUNLU: interrupt() tarafından kaydedilir */
|
|
2455
|
+
const why =
|
|
2456
|
+
(this._abortReasons && this._abortReasons.get(sid)) ||
|
|
2457
|
+
(kickJob && kickJob.error) ||
|
|
2458
|
+
'kullanıcı talebiyle iptal edildi';
|
|
2459
|
+
if (this._abortReasons) this._abortReasons.delete(sid);
|
|
2460
|
+
emit({ type: 'done', aborted: true, reason: why });
|
|
2461
|
+
this._bgFinish(sid, 'aborted', why);
|
|
2462
|
+
}
|
|
2402
2463
|
} else {
|
|
2403
2464
|
// FALLOUT: tüm zincir tükendi — durumu kaydet, açılışta kaldığı yerden devam edilir
|
|
2404
2465
|
this._saveCrash(sid, e, 'chain-exhausted');
|
|
@@ -2641,7 +2702,7 @@ class Engine {
|
|
|
2641
2702
|
if (!this.ctrls.has(pairId)) break;
|
|
2642
2703
|
}
|
|
2643
2704
|
if (this.ctrls.has(pairId)) {
|
|
2644
|
-
try { this.interrupt(pairId); } catch {}
|
|
2705
|
+
try { this.interrupt(pairId, 'bot DM zaman aşımına uğradı (120 sn) — tur kesildi'); } catch {}
|
|
2645
2706
|
return { ok: false, error: 'hedef bot zaman aşımına uğradı (120 sn)' };
|
|
2646
2707
|
}
|
|
2647
2708
|
const after = this.cache.get(pairId) || pair;
|
|
@@ -2755,39 +2816,19 @@ class Engine {
|
|
|
2755
2816
|
}
|
|
2756
2817
|
}
|
|
2757
2818
|
|
|
2758
|
-
/* web_search zinciri — web_search VE deep_search aynı zinciri kullanır
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
4)
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
async _webSearchChain(q, n, sessionId, signal) {
|
|
2766
|
-
let r = null;
|
|
2767
|
-
let banned = false;
|
|
2819
|
+
/* web_search zinciri — web_search VE deep_search aynı zinciri kullanır.
|
|
2820
|
+
Sıra + aç/kapa Ayarlar → Web Arama'dan değiştirilir (tools.searchChainWeb):
|
|
2821
|
+
varsayılan: 1) dahili tarayıcı (DİREK GOOGLE) 2) Obscura (stealth headless
|
|
2822
|
+
→ DuckDuckGo; otomatik kurulur, varsayılan AKTİF) 3) TinyFish (anahtar
|
|
2823
|
+
girildiyse) 4) python çoklu-motor. Tarayıcı CAPTCHA/trafik verirse 10 dk
|
|
2824
|
+
atlanır — sıradaki motor hemen devreye girer. */
|
|
2825
|
+
_webSearchChain(q, n, sessionId, signal) {
|
|
2768
2826
|
const browserUsable = this.browser && typeof this.browser.search === 'function';
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
this._webSearchBrowserBanUntil = Date.now() + 10 * 60 * 1000;
|
|
2775
|
-
r = null;
|
|
2776
|
-
banned = true;
|
|
2777
|
-
}
|
|
2778
|
-
}
|
|
2779
|
-
if (!r || !r.ok || !(r.results || []).length) {
|
|
2780
|
-
try { r = await tools.tinyfishSearch(q, n, signal); } catch {}
|
|
2781
|
-
}
|
|
2782
|
-
if (!r || !r.ok || !(r.results || []).length) {
|
|
2783
|
-
try {
|
|
2784
|
-
r = JSON.parse(await tools.exec('web_search', { query: q, max_results: n }, { cwd: this._sessionWorkspace(sessionId), signal }));
|
|
2785
|
-
} catch {}
|
|
2786
|
-
}
|
|
2787
|
-
const out = r || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' };
|
|
2788
|
-
if (out && out.ok && banned) out.note = 'tarayıcı araması 10 dk askıda (CAPTCHA/trafik) — TinyFish/alternatif zincir kullanıldı';
|
|
2789
|
-
return out;
|
|
2790
|
-
}
|
|
2827
|
+
return tools.searchChainWeb(q, n, {
|
|
2828
|
+
signal,
|
|
2829
|
+
browser: browserUsable ? () => this.browser.search(q, signal, { sessionId }) : null,
|
|
2830
|
+
});
|
|
2831
|
+
}
|
|
2791
2832
|
|
|
2792
2833
|
/* Dahili OCR aracı: görüntüden metin çıkarır (görsel desteklemeyen modeller için).
|
|
2793
2834
|
Kaynak: 'browser' (dahili tarayıcı görüntüsü, varsayılan) | 'screen' (masaüstü) | dosya yolu. */
|
|
@@ -3016,8 +3057,11 @@ class Engine {
|
|
|
3016
3057
|
}
|
|
3017
3058
|
if (name === 'task_cancel') {
|
|
3018
3059
|
const id = String(args.id || '');
|
|
3019
|
-
const
|
|
3020
|
-
|
|
3060
|
+
const why = String(args.reason || '').trim() || 'ana ajan (CEO) bu ajanı task_cancel ile iptal etti';
|
|
3061
|
+
const ok = this.interrupt(id, why);
|
|
3062
|
+
return JSON.stringify(ok
|
|
3063
|
+
? { ok: true, id, reason: why, note: 'Ajan iptal edildi — kullanıcıya MUTLAKA iptal sebebini tek cümleyle açıkla: ' + why }
|
|
3064
|
+
: { ok: false, error: 'çalışan ajan yok: ' + id });
|
|
3021
3065
|
}
|
|
3022
3066
|
if (name === 'set_reminder') {
|
|
3023
3067
|
if (!this.reminders || typeof this.reminders.add !== 'function') {
|
|
@@ -3139,10 +3183,8 @@ class Engine {
|
|
|
3139
3183
|
}
|
|
3140
3184
|
if (name === 'deep_search') {
|
|
3141
3185
|
/* agentic derin araştırma: çoklu sorgu + gizli tarayıcıda sayfa okuma.
|
|
3142
|
-
ARAMA zinciri web_search ile BİREBİR AYNI —
|
|
3143
|
-
|
|
3144
|
-
boş dönünce "tüm motorlar boş döndü" hatası veriyordu. Artık önce
|
|
3145
|
-
DAHİLİ TARAYICI (Google) denenir, sonra TinyFish → python → Exa. */
|
|
3186
|
+
ARAMA zinciri web_search ile BİREBİR AYNI — sıralı zincir
|
|
3187
|
+
(tarayıcı → Obscura → TinyFish → python; Ayarlar'dan değiştirilir). */
|
|
3146
3188
|
try {
|
|
3147
3189
|
emitSafe(this, sessionId, { type: 'status', status: 'derin araştırma: çoklu sorgu + gizli sayfa okuma' });
|
|
3148
3190
|
} catch {}
|
|
@@ -3207,9 +3249,34 @@ class Engine {
|
|
|
3207
3249
|
}
|
|
3208
3250
|
}
|
|
3209
3251
|
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3252
|
+
/* İptal SEBEBİ zorunlu: abort edilen her tur/_bgFinish bu haritadan sebebini
|
|
3253
|
+
okur; ajan neden durdurulduğunu kullanıcıya MUTLAKA bildirir. */
|
|
3254
|
+
_abortReason(reason) {
|
|
3255
|
+
const r = String(reason || '').trim();
|
|
3256
|
+
return (r || 'sebep belirtilmedi — iptal talebi').slice(0, 400);
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3259
|
+
interrupt(sessionId, reason) {
|
|
3260
|
+
const sid = String(sessionId || '');
|
|
3261
|
+
const why = this._abortReason(reason);
|
|
3262
|
+
const c = this.ctrls.get(sid);
|
|
3263
|
+
if (!c) {
|
|
3264
|
+
/* koşan tur yok — kuyrukta bekleyen / öz-kurtarma kick'i bekleyen
|
|
3265
|
+
paralel ajanı da SEBEPİYLE kes */
|
|
3266
|
+
const j = this._bgJobs && this._bgJobs.get(sid);
|
|
3267
|
+
if (j && (j.status === 'queued' || j.revive === true)) {
|
|
3268
|
+
j.status = 'aborted';
|
|
3269
|
+
j.revive = false;
|
|
3270
|
+
j.endedAt = nowIso();
|
|
3271
|
+
j.error = why;
|
|
3272
|
+
this._clearKicks(sid);
|
|
3273
|
+
this._bgEmit();
|
|
3274
|
+
return true;
|
|
3275
|
+
}
|
|
3276
|
+
return false;
|
|
3277
|
+
}
|
|
3278
|
+
this._abortReasons = this._abortReasons || new Map();
|
|
3279
|
+
this._abortReasons.set(sid, why);
|
|
3213
3280
|
c.abort();
|
|
3214
3281
|
return true;
|
|
3215
3282
|
}
|
|
@@ -3219,19 +3286,22 @@ class Engine {
|
|
|
3219
3286
|
main katmanında BAĞIMSIZ yaşar — /stop onlara dokunmaz. */
|
|
3220
3287
|
stopAll() {
|
|
3221
3288
|
let aborted = 0;
|
|
3289
|
+
const why = '/stop: kullanıcı tüm ajanları ve turları durdurdu';
|
|
3222
3290
|
/* fan-out gruplarını kapat — yarım grup artık birleşik rapor beklemesin */
|
|
3223
3291
|
if (this._bgGroups) {
|
|
3224
3292
|
for (const g of this._bgGroups.values()) g.dead = true;
|
|
3225
3293
|
}
|
|
3226
3294
|
if (this._bgPendingStart) this._bgPendingStart.clear();
|
|
3295
|
+
this._abortReasons = this._abortReasons || new Map();
|
|
3227
3296
|
for (const [sid, c] of this.ctrls) {
|
|
3228
|
-
try { c.abort(); aborted++; } catch {}
|
|
3297
|
+
try { this._abortReasons.set(sid, why); c.abort(); aborted++; } catch {}
|
|
3229
3298
|
/* abort sonrası _run finally bloğu temizler; paralel ajan kaydını biz işaretleyelim */
|
|
3230
3299
|
if (this._bgJobs.has(sid)) {
|
|
3231
3300
|
const job = this._bgJobs.get(sid);
|
|
3232
3301
|
if (job.status === 'running' || job.status === 'queued') {
|
|
3233
3302
|
job.status = 'aborted';
|
|
3234
3303
|
job.endedAt = nowIso();
|
|
3304
|
+
job.error = job.error || why;
|
|
3235
3305
|
}
|
|
3236
3306
|
job.revive = false; // bekleyen öz-kurtarma gönderimini de öldür
|
|
3237
3307
|
this._clearKicks(sid);
|
|
@@ -3242,6 +3312,7 @@ class Engine {
|
|
|
3242
3312
|
if (j.status === 'queued') {
|
|
3243
3313
|
j.status = 'aborted';
|
|
3244
3314
|
j.endedAt = nowIso();
|
|
3315
|
+
j.error = j.error || why;
|
|
3245
3316
|
aborted++;
|
|
3246
3317
|
}
|
|
3247
3318
|
}
|
|
@@ -3613,13 +3684,15 @@ const TOOLS = [
|
|
|
3613
3684
|
type: 'function',
|
|
3614
3685
|
function: {
|
|
3615
3686
|
name: 'task_cancel',
|
|
3616
|
-
description:
|
|
3687
|
+
description:
|
|
3688
|
+
'Cancel a running background agent by id (from tasks_list). ALWAYS pass reason: a short sentence explaining WHY you are cancelling — it is recorded on the job and you must report it to the user.',
|
|
3617
3689
|
parameters: {
|
|
3618
3690
|
type: 'object',
|
|
3619
3691
|
properties: {
|
|
3620
3692
|
id: { type: 'string' },
|
|
3693
|
+
reason: { type: 'string', description: 'REQUIRED: why you are cancelling this agent (one short sentence)' },
|
|
3621
3694
|
},
|
|
3622
|
-
required: ['id'],
|
|
3695
|
+
required: ['id', 'reason'],
|
|
3623
3696
|
},
|
|
3624
3697
|
},
|
|
3625
3698
|
},
|
|
@@ -3850,3 +3923,4 @@ module.exports.BG_HIDDEN_TOOLS = BG_HIDDEN_TOOLS;
|
|
|
3850
3923
|
module.exports.PERM_TOOL_SETS = PERM_TOOL_SETS;
|
|
3851
3924
|
module.exports.PERM_LEVELS = PERM_LEVELS;
|
|
3852
3925
|
module.exports.normalizePerms = normalizePerms;
|
|
3926
|
+
module.exports.OBSERVE_MARK = OBSERVE_MARK;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* ---------- Obscura entegrasyonu ----------
|
|
4
|
+
Obscura (github.com/h4ckf0r0day/obscura): Rust tabanlı stealth headless
|
|
5
|
+
tarayıcı — anti-detect + V8 JS + gerçek rendering, Chromium'suz.
|
|
6
|
+
Beast kullanımı:
|
|
7
|
+
1) Kurulum: releases'taki Windows stealth zip'i %APPDATA%\beast\obscura'ya
|
|
8
|
+
açılır (obscura.exe + obscura-worker.exe). İlk açılışta OTOMATİK kurulur.
|
|
9
|
+
2) Arama: `obscura fetch https://html.duckduckgo.com/html/?q=...` çıktısı
|
|
10
|
+
DDG sonuç ayrıştırıcısıyla results'a döner — web_search zincirinin
|
|
11
|
+
sıralı bir motoru (Ayarlar → Web Arama'dan sıra değiştirilebilir). */
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const os = require('os');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const https = require('https');
|
|
17
|
+
const { execFile, spawn } = require('child_process');
|
|
18
|
+
|
|
19
|
+
const RELEASE_BASE = 'https://github.com/h4ckf0r0day/obscura/releases/latest/download/';
|
|
20
|
+
const WINDOWS_ZIP = 'obscura-x86_64-windows-stealth.zip';
|
|
21
|
+
const MAX_ZIP_BYTES = 250 * 1024 * 1024;
|
|
22
|
+
const FETCH_TIMEOUT_MS = 25000;
|
|
23
|
+
|
|
24
|
+
function obscuraDir() {
|
|
25
|
+
const base = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
26
|
+
return path.join(base, 'beast', 'obscura');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function obscuraExe() {
|
|
30
|
+
return path.join(obscuraDir(), 'obscura.exe');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function obscuraInstalled() {
|
|
34
|
+
try { return fs.existsSync(obscuraExe()); } catch { return false; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* kurulan varyant işareti: stealth zip'te --stealth bayrağı geçerli */
|
|
38
|
+
function markVariant(v) {
|
|
39
|
+
try { fs.writeFileSync(path.join(obscuraDir(), 'variant.txt'), String(v || 'stealth') + '\n', 'utf8'); } catch {}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readVariant() {
|
|
43
|
+
try { return String(fs.readFileSync(path.join(obscuraDir(), 'variant.txt'), 'utf8')).trim() || 'stealth'; } catch { return 'stealth'; }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* bina sağ mı: --help 10 sn içinde cevap veriyor mu (oturum başına tek deneme) */
|
|
47
|
+
let _okProbe = null;
|
|
48
|
+
function obscuraOk() {
|
|
49
|
+
if (!obscuraInstalled()) return Promise.resolve(false);
|
|
50
|
+
if (_okProbe) return _okProbe;
|
|
51
|
+
_okProbe = new Promise((resolve) => {
|
|
52
|
+
try {
|
|
53
|
+
execFile(obscuraExe(), ['--help'], { timeout: 10000, windowsHide: true }, (err) => resolve(!err));
|
|
54
|
+
} catch {
|
|
55
|
+
resolve(false);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return _okProbe;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function httpsDownload(url, redirectsLeft = 5, signal) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
const req = https.get(url, { headers: { 'User-Agent': 'BeastAgent/1.0 (+obscura bootstrap)' } }, (res) => {
|
|
64
|
+
if ([301, 302, 303, 307, 308].includes(res.statusCode) && res.headers.location && redirectsLeft > 0) {
|
|
65
|
+
res.resume();
|
|
66
|
+
return resolve(httpsDownload(new URL(res.headers.location, url).toString(), redirectsLeft - 1, signal));
|
|
67
|
+
}
|
|
68
|
+
if (res.statusCode !== 200) {
|
|
69
|
+
res.resume();
|
|
70
|
+
return reject(new Error(`indirme başarısız: HTTP ${res.statusCode}`));
|
|
71
|
+
}
|
|
72
|
+
const chunks = [];
|
|
73
|
+
let size = 0;
|
|
74
|
+
res.on('data', (c) => {
|
|
75
|
+
size += c.length;
|
|
76
|
+
if (size > MAX_ZIP_BYTES) {
|
|
77
|
+
req.destroy(new Error('dosya çok büyük'));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
chunks.push(c);
|
|
81
|
+
});
|
|
82
|
+
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
83
|
+
res.on('error', reject);
|
|
84
|
+
});
|
|
85
|
+
req.on('error', reject);
|
|
86
|
+
if (signal) {
|
|
87
|
+
if (signal.aborted) return reject(new Error('iptal'));
|
|
88
|
+
signal.addEventListener('abort', () => req.destroy(new Error('iptal')), { once: true });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function psExpand(zipPath, destDir) {
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
const cmd = `Expand-Archive -LiteralPath "${zipPath}" -DestinationPath "${destDir}" -Force`;
|
|
96
|
+
const child = spawn(
|
|
97
|
+
'powershell.exe',
|
|
98
|
+
['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', cmd],
|
|
99
|
+
{ windowsHide: true }
|
|
100
|
+
);
|
|
101
|
+
let err = '';
|
|
102
|
+
const finish = (ok) => {
|
|
103
|
+
clearTimeout(timer);
|
|
104
|
+
resolve(ok);
|
|
105
|
+
};
|
|
106
|
+
const timer = setTimeout(() => {
|
|
107
|
+
try { spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true }); } catch {}
|
|
108
|
+
finish(false);
|
|
109
|
+
}, 180000);
|
|
110
|
+
child.stderr.setEncoding('utf8');
|
|
111
|
+
child.stderr.on('data', (d) => { err += d; });
|
|
112
|
+
child.on('error', () => finish(false));
|
|
113
|
+
child.on('close', (code) => finish(code === 0 && !/Expand-Archive :/i.test(err)));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Obscura'yı kur / güncelle (yeniden indirip üzerine yazar) */
|
|
118
|
+
async function installObscura(signal) {
|
|
119
|
+
const dest = obscuraDir();
|
|
120
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
121
|
+
const zipPath = path.join(os.tmpdir(), 'beast-obscura.zip');
|
|
122
|
+
const buf = await httpsDownload(RELEASE_BASE + WINDOWS_ZIP, 5, signal);
|
|
123
|
+
fs.writeFileSync(zipPath, buf);
|
|
124
|
+
const ok = await psExpand(zipPath, dest);
|
|
125
|
+
try { fs.unlinkSync(zipPath); } catch {}
|
|
126
|
+
if (!ok || !obscuraInstalled()) {
|
|
127
|
+
return { ok: false, error: 'obscura kurulumu başarısız (zip açılamadı)' };
|
|
128
|
+
}
|
|
129
|
+
markVariant('stealth');
|
|
130
|
+
_okProbe = null; /* yeniden doğrula */
|
|
131
|
+
const good = await obscuraOk();
|
|
132
|
+
return { ok: good, dir: dest, error: good ? null : 'obscura.exe doğrulanamadı' };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* obscura fetch — sayfa HTML'i (abort ile süreç kesilir) */
|
|
136
|
+
function obscuraFetchHtml(url, signal, timeoutMs = FETCH_TIMEOUT_MS) {
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
const args = ['fetch', url, '--dump', 'html', '--quiet', '--timeout', '20'];
|
|
139
|
+
if (readVariant() === 'stealth') args.push('--stealth');
|
|
140
|
+
let settled = false;
|
|
141
|
+
const finish = (err, html) => {
|
|
142
|
+
if (settled) return;
|
|
143
|
+
settled = true;
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
if (signal) signal.removeEventListener('abort', onAbort);
|
|
146
|
+
err ? reject(err) : resolve(String(html || ''));
|
|
147
|
+
};
|
|
148
|
+
let child = null;
|
|
149
|
+
const onAbort = () => {
|
|
150
|
+
try {
|
|
151
|
+
if (child && child.pid) spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true });
|
|
152
|
+
} catch {}
|
|
153
|
+
finish(new Error('obscura fetch iptal edildi'));
|
|
154
|
+
};
|
|
155
|
+
const timer = setTimeout(() => {
|
|
156
|
+
try {
|
|
157
|
+
if (child && child.pid) spawn('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true });
|
|
158
|
+
} catch {}
|
|
159
|
+
finish(new Error('obscura fetch zaman aşımı'));
|
|
160
|
+
}, timeoutMs);
|
|
161
|
+
if (signal) {
|
|
162
|
+
if (signal.aborted) return finish(new Error('iptal'));
|
|
163
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
child = execFile(
|
|
167
|
+
obscuraExe(),
|
|
168
|
+
args,
|
|
169
|
+
{ timeout: timeoutMs, windowsHide: true, maxBuffer: 8 * 1024 * 1024 },
|
|
170
|
+
(err, stdout) => finish(err, stdout)
|
|
171
|
+
);
|
|
172
|
+
} catch (e) {
|
|
173
|
+
finish(e);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* lite.duckduckgo sonuç ayrıştırıcı (tablo düzeni, result-link çıpaları) */
|
|
179
|
+
function parseDdgLite(html, limit = 8) {
|
|
180
|
+
const out = [];
|
|
181
|
+
const re = /<a\s+[^>]*class="[^"]*result-link[^"]*"[^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/g;
|
|
182
|
+
let m;
|
|
183
|
+
while ((m = re.exec(String(html || ''))) && out.length < limit) {
|
|
184
|
+
let href = m[1] || '';
|
|
185
|
+
try {
|
|
186
|
+
const u = new URL(href, 'https://duckduckgo.com');
|
|
187
|
+
const uddg = u.searchParams.get('uddg');
|
|
188
|
+
href = uddg ? decodeURIComponent(uddg) : u.toString();
|
|
189
|
+
} catch {}
|
|
190
|
+
const title = String(m[2] || '').replace(/<[^>]+>/g, '').trim();
|
|
191
|
+
if (!title || !/^https?:\/\//i.test(href)) continue;
|
|
192
|
+
if (out.some((r) => r.url === href)) continue;
|
|
193
|
+
out.push({ title, url: href, snippet: '' });
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* Obscura arama motoru: DDG html (önce) → DDG lite (yedek), stealth ile.
|
|
199
|
+
Döndüremiyorsa null — zincir sıradaki motora geçer. */
|
|
200
|
+
async function obscuraSearch(query, { maxResults = 8, signal } = {}) {
|
|
201
|
+
const q = String(query || '').trim().slice(0, 400);
|
|
202
|
+
if (!q || !obscuraInstalled()) return null;
|
|
203
|
+
const cap = Math.max(1, Math.min(12, Number(maxResults) || 8));
|
|
204
|
+
const targets = [
|
|
205
|
+
'https://html.duckduckgo.com/html/?q=' + encodeURIComponent(q),
|
|
206
|
+
'https://lite.duckduckgo.com/lite/?q=' + encodeURIComponent(q),
|
|
207
|
+
];
|
|
208
|
+
for (const url of targets) {
|
|
209
|
+
let html = '';
|
|
210
|
+
try {
|
|
211
|
+
html = await obscuraFetchHtml(url, signal);
|
|
212
|
+
} catch {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (!html) continue;
|
|
216
|
+
let results = [];
|
|
217
|
+
const { parseDdgResults } = require('./tools'); /* döngüsel require — çağrı anında */
|
|
218
|
+
if (/result__a/.test(html)) results = parseDdgResults(html, cap);
|
|
219
|
+
if (!results.length) results = parseDdgLite(html, cap);
|
|
220
|
+
if (results.length) {
|
|
221
|
+
return {
|
|
222
|
+
ok: true,
|
|
223
|
+
engine: 'obscura',
|
|
224
|
+
query: q,
|
|
225
|
+
count: results.length,
|
|
226
|
+
results: results.map((r) => ({ ...r, engine: 'obscura' })),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
module.exports = {
|
|
234
|
+
ENGINE: 'obscura',
|
|
235
|
+
RELEASE_BASE,
|
|
236
|
+
WINDOWS_ZIP,
|
|
237
|
+
obscuraDir,
|
|
238
|
+
obscuraExe,
|
|
239
|
+
obscuraInstalled,
|
|
240
|
+
obscuraOk,
|
|
241
|
+
installObscura,
|
|
242
|
+
obscuraFetchHtml,
|
|
243
|
+
obscuraSearch,
|
|
244
|
+
parseDdgLite,
|
|
245
|
+
};
|
package/src/agent/skills.js
CHANGED
|
@@ -164,19 +164,27 @@ Send-MailMessage -SmtpServer smtp.gmail.com -Port 587 -UseSsl -From 'adres@gmail
|
|
|
164
164
|
force: true,
|
|
165
165
|
body: `---
|
|
166
166
|
name: python-web-search
|
|
167
|
-
description: VARSAYILAN web arama zinciri — 1) dahili tarayıcı DİREK GOOGLE (gerçek Chromium, bot koruması yok; Google AI cevabi 'ai' alanında hazır gelir) 2) python çoklu-motor (ddgs / DDG+Bing+Mojeek, anahtarsız)
|
|
168
|
-
version: 1.
|
|
167
|
+
description: VARSAYILAN web arama zinciri — 1) dahili tarayıcı DİREK GOOGLE (gerçek Chromium, bot koruması yok; Google AI cevabi 'ai' alanında hazır gelir) 2) Obscura (stealth headless → DuckDuckGo; otomatik kurulur, varsayılan AKTİF) 3) TinyFish (anahtar varsa) 4) python çoklu-motor (ddgs / DDG+Bing+Mojeek, anahtarsız). Sıra Ayarlar → Web Arama'dan değiştirilir. Takılmama kuralları içerir.
|
|
168
|
+
version: 1.4.0
|
|
169
169
|
---
|
|
170
170
|
|
|
171
171
|
# Web Arama Zinciri (VARSAYILAN)
|
|
172
172
|
|
|
173
|
-
web_search aracı çağrıldığında zincir OTOMATİK çalışır — elle motor seçmen gerekmez:
|
|
173
|
+
web_search aracı çağrıldığında zincir OTOMATİK çalışır — elle motor seçmen gerekmez. Sıra Ayarlar → Web Arama sekmesinden değiştirilebilir:
|
|
174
174
|
|
|
175
175
|
## 1. Dahili tarayıcı (ilk tercih — Google AI)
|
|
176
176
|
|
|
177
|
-
Gerçek Chromium olduğu için Google'a bot koruması uygulamaz. Arama aep=1 ile açılır: Google'ın KENDİ AI cevabı yanıtın 'ai' alanında HAZIR gelir — "kimdir/nedir" sorularında önce onu kullan, kaynak linkleri de 'results' alanında gelir. Sağ panelde açılır, kullanıcı da görebilir.
|
|
177
|
+
Gerçek Chromium olduğu için Google'a bot koruması uygulamaz. Arama aep=1 ile açılır: Google'ın KENDİ AI cevabı yanıtın 'ai' alanında HAZIR gelir — "kimdir/nedir" sorularında önce onu kullan, kaynak linkleri de 'results' alanında gelir. Sağ panelde açılır, kullanıcı da görebilir. CAPTCHA/trafik uyarısı gelirse tarayıcı 10 dk atlanır, sıradaki motor devreye girer.
|
|
178
178
|
|
|
179
|
-
## 2.
|
|
179
|
+
## 2. Obscura (stealth headless — DuckDuckGo)
|
|
180
|
+
|
|
181
|
+
Rust tabanlı gizli tarayıcı: anti-detect parmak izi + V8 JS. Kurulu değilse uygulama açılışında OTOMATİK indirilir (%APPDATA%\\beast\\obscura). Varsayılan AKTİF — tarayıcı engellenirse bot korumasını aşarak DuckDuckGo'dan sonuç getirir.
|
|
182
|
+
|
|
183
|
+
## 3. TinyFish API (anahtar varsa)
|
|
184
|
+
|
|
185
|
+
Ayarlar → Web Arama'dan anahtar girildiyse kendi sırasında devreye girer; anahtar yoksa otomatik atlanır.
|
|
186
|
+
|
|
187
|
+
## 4. Python çoklu-motor
|
|
180
188
|
|
|
181
189
|
ddgs kütüphanesi (github.com/deedy5/ddgs — anti-bot korumalı metasearch) veya DuckDuckGo + Bing + Mojeek PARALEL — anahtar gerekmez. Gömülü Python runtime otomatik yönetilir.
|
|
182
190
|
|
|
@@ -187,10 +195,6 @@ web_search {"query": "sorgu", "max_results": 6}
|
|
|
187
195
|
- Çoklu sorgu gerekiyorsa: aynı turda birden ÇOK web_search çağrısını PARALEL ver — toplam süre tek arama kadar olur.
|
|
188
196
|
- Sonuç listesinden 1-2 URL'nin içeriğini okumak için http_fetch kullan.
|
|
189
197
|
|
|
190
|
-
## 3. Exa (son çare)
|
|
191
|
-
|
|
192
|
-
Yukarıdakiler boş dönerse ve Ayarlar → Web Arama'ya Exa anahtarı girildiyse devreye girer.
|
|
193
|
-
|
|
194
198
|
## Toplu / derin işler (python_run)
|
|
195
199
|
|
|
196
200
|
- Haber başlıkları: python_run script=news.py args ["--limit","8","--json"]
|
|
@@ -208,7 +212,7 @@ for q in ["sorgu 1", "sorgu 2", "sorgu 3"]:
|
|
|
208
212
|
|
|
209
213
|
- Bir bilgi 2-3 farklı sorguyla bulunamıyorsa PEŞİNİ BIRAK: bulduğun kısmi bilgiyle devam et, neyi bulamadığını raporda açıkça yaz.
|
|
210
214
|
- Kapalı/gizli içerik (Instagram gizli hesap, private profil, login arkası veri) ARANMAZ — bulunamayacağı belliyse hemen alternatif kaynağa/geçişe geç.
|
|
211
|
-
- DDG boş/engelli dönerse "DuckDuckGo kapandı" DEME: bot koruması devrededir (~1 saatte kendiliğinden açılır) ve zincir otomatik olarak Bing/Mojeek/tarayıcıya geçer — bu normal çalışmadır.
|
|
215
|
+
- DDG boş/engelli dönerse "DuckDuckGo kapandı" DEME: bot koruması devrededir (~1 saatte kendiliğinden açılır) ve zincir otomatik olarak Obscura/Bing/Mojeek/tarayıcıya geçer — bu normal çalışmadır.
|
|
212
216
|
- Araştırma hedefi: ~3 dakika, 3-5 kaynak. Derinleşme = zaman kaybı.
|
|
213
217
|
- Motor hata verirse bir kez farklı sorguyla dene, sonra elindekiyle raporu kapat.`,
|
|
214
218
|
},
|