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/src/agent/tools.js
CHANGED
|
@@ -7,6 +7,7 @@ const https = require('https');
|
|
|
7
7
|
const { execFile } = require('child_process');
|
|
8
8
|
const { spawn } = require('child_process');
|
|
9
9
|
const research = require('./research');
|
|
10
|
+
const obscura = require('./obscura');
|
|
10
11
|
|
|
11
12
|
const MAX_CMD_OUTPUT = 16000;
|
|
12
13
|
const MAX_FILE_CHARS = 200000;
|
|
@@ -627,7 +628,7 @@ const definitions = [
|
|
|
627
628
|
function: {
|
|
628
629
|
name: 'web_search',
|
|
629
630
|
description:
|
|
630
|
-
'FAST web search with an automatic chain: built-in browser
|
|
631
|
+
'FAST web search with an automatic chain (order is configurable in Ayarlar → Web Arama): built-in browser (real Chromium searching GOOGLE directly with AI Mode — no bot protection; the response may include an `ai` field holding Google\'s own AI answer), Obscura stealth headless browser (anti-detect; searches DuckDuckGo, installed automatically and ACTIVE by default), TinyFish API (only if a key is configured), then Python multi-engine (ddgs / DuckDuckGo+Bing+Mojeek). If the browser hits CAPTCHA/unusual traffic it is skipped for 10 minutes and the next engine takes over. Returns {ai?, results[{title,url,snippet}]}. Use when fresh or external info is needed; skip for things you already know.',
|
|
631
632
|
parameters: {
|
|
632
633
|
type: 'object',
|
|
633
634
|
properties: {
|
|
@@ -758,13 +759,9 @@ async function exec(name, args, ctx) {
|
|
|
758
759
|
case 'web_search': {
|
|
759
760
|
const q = String(args.query || '');
|
|
760
761
|
const n = Number(args.max_results) || 8;
|
|
761
|
-
/*
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
if (!r || !r.ok || !(r.results || []).length) {
|
|
765
|
-
const exa = await exaSearch(q, n, ctx.signal);
|
|
766
|
-
if (exa) return JSON.stringify(exa);
|
|
767
|
-
}
|
|
762
|
+
/* sıralı zincir (obscura/tinyfish/python — dahili tarayıcı engine
|
|
763
|
+
tarafındaki hook'tan gelir); Exa KALDIRILDI */
|
|
764
|
+
const r = await searchChainWeb(q, n, { signal: ctx.signal });
|
|
768
765
|
return JSON.stringify(r);
|
|
769
766
|
}
|
|
770
767
|
case 'deep_search': {
|
|
@@ -772,7 +769,7 @@ async function exec(name, args, ctx) {
|
|
|
772
769
|
Gizli tarayıcı okuması engine.research hook'undan gelir (main process). */
|
|
773
770
|
const r = await research.deepSearch(
|
|
774
771
|
args,
|
|
775
|
-
{ search: (q) =>
|
|
772
|
+
{ search: (q) => searchChainWeb(q, 10, { signal: ctx.signal }) },
|
|
776
773
|
ctx.signal
|
|
777
774
|
);
|
|
778
775
|
return JSON.stringify(r);
|
|
@@ -836,53 +833,101 @@ async function exec(name, args, ctx) {
|
|
|
836
833
|
}
|
|
837
834
|
}
|
|
838
835
|
|
|
839
|
-
/* ----------
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
836
|
+
/* ---------- Sıralı arama zinciri (Ayarlar → Web Arama'dan değiştirilir) ----------
|
|
837
|
+
Motorlar: browser (dahili Chromium → Google) · obscura (stealth headless →
|
|
838
|
+
DuckDuckGo) · tinyfish (API, anahtar gerekir) · python (ddgs/DDG+Bing+Mojeek).
|
|
839
|
+
Sıra + aç/kapa ayarı settings.json'da (searchChain) saklanır; Obscura
|
|
840
|
+
varsayılan AKTİF. Tarayıcı CAPTCHA/trafik verirse 10 dk atlanır. */
|
|
841
|
+
const SEARCH_ENGINE_IDS = ['browser', 'obscura', 'tinyfish', 'python'];
|
|
842
|
+
const DEFAULT_SEARCH_CHAIN = SEARCH_ENGINE_IDS.map((id) => ({ id, on: true }));
|
|
843
|
+
|
|
844
|
+
let _searchChain = DEFAULT_SEARCH_CHAIN.map((x) => ({ ...x }));
|
|
845
|
+
let _obscuraEnabled = true; /* Obscura varsayılan AKTİF */
|
|
846
|
+
let _browserBanUntil = 0;
|
|
847
|
+
|
|
848
|
+
function normalizeSearchChain(list) {
|
|
849
|
+
const arr = Array.isArray(list) ? list : [];
|
|
850
|
+
const rows = [];
|
|
851
|
+
const seen = new Set();
|
|
852
|
+
for (const item of arr) {
|
|
853
|
+
const id = String((item && item.id) || item || '').trim();
|
|
854
|
+
if (!SEARCH_ENGINE_IDS.includes(id) || seen.has(id)) continue;
|
|
855
|
+
seen.add(id);
|
|
856
|
+
rows.push({ id, on: !(item && item.on === false) });
|
|
857
|
+
}
|
|
858
|
+
for (const id of SEARCH_ENGINE_IDS) {
|
|
859
|
+
if (!seen.has(id)) rows.push({ id, on: true }); /* listede eksik motor varsayılan AÇIK */
|
|
860
|
+
}
|
|
861
|
+
/* obscura varsayılan AKTİF; zincir boş kalmasın: hepsi kapalıysa browser'ı aç */
|
|
862
|
+
if (!rows.some((r) => r.on)) {
|
|
863
|
+
const b = rows.find((r) => r.id === 'browser');
|
|
864
|
+
if (b) b.on = true;
|
|
865
|
+
}
|
|
866
|
+
return rows;
|
|
867
|
+
}
|
|
843
868
|
|
|
844
|
-
function
|
|
845
|
-
|
|
869
|
+
function setSearchChain(list) {
|
|
870
|
+
_searchChain = normalizeSearchChain(list);
|
|
871
|
+
return _searchChain;
|
|
846
872
|
}
|
|
847
873
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
874
|
+
function setSearchObscuraEnabled(v) {
|
|
875
|
+
_obscuraEnabled = v !== false;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
function getSearchChain() {
|
|
879
|
+
return _searchChain.map((x) => ({ ...x }));
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
function browserBanned() {
|
|
883
|
+
return Date.now() < _browserBanUntil;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function banBrowser(minutes = 10) {
|
|
887
|
+
_browserBanUntil = Date.now() + minutes * 60 * 1000;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/* Zinciri sırayla koştur: her motor boş/hata dönerse sıradakine geç.
|
|
891
|
+
browser motoru yalnız engine'den gelir (hook); araç-bağımsız çağrılarda atlanır. */
|
|
892
|
+
async function searchChainWeb(query, maxResults, { signal, browser } = {}) {
|
|
893
|
+
const q = String(query || '').trim().slice(0, 400);
|
|
894
|
+
if (!q) return { ok: false, error: 'boş sorgu' };
|
|
895
|
+
const cap = Math.max(1, Math.min(12, Number(maxResults) || 8));
|
|
896
|
+
let out = null;
|
|
897
|
+
const banned = browserBanned();
|
|
898
|
+
for (const row of _searchChain) {
|
|
899
|
+
if (out && out.ok && (out.results || []).length) break;
|
|
900
|
+
if (!row.on) continue;
|
|
901
|
+
try {
|
|
902
|
+
if (row.id === 'browser') {
|
|
903
|
+
if (typeof browser !== 'function' || banned) continue;
|
|
904
|
+
out = await browser();
|
|
905
|
+
if (!out || !out.ok || !(out.results || []).length) {
|
|
906
|
+
banBrowser(); /* tarayıcı sorunlu — 10 dk atla, alternatifler devrede */
|
|
907
|
+
out = null;
|
|
908
|
+
}
|
|
909
|
+
} else if (row.id === 'obscura') {
|
|
910
|
+
if (!_obscuraEnabled) continue;
|
|
911
|
+
out = await obscura.obscuraSearch(q, { maxResults: cap, signal });
|
|
912
|
+
} else if (row.id === 'tinyfish') {
|
|
913
|
+
out = await tinyfishSearch(q, cap, signal);
|
|
914
|
+
} else if (row.id === 'python') {
|
|
915
|
+
out = await webSearchFast(q, { maxResults: cap, signal });
|
|
916
|
+
}
|
|
917
|
+
} catch {
|
|
918
|
+
out = null;
|
|
919
|
+
}
|
|
920
|
+
if (out && out.ok && !(out.results || []).length) out = null; /* boş → sıradaki motor */
|
|
921
|
+
}
|
|
922
|
+
if (out && out.ok && banned && (out.results || []).length) {
|
|
923
|
+
out.note = 'dahili tarayıcı 10 dk askıda (CAPTCHA/trafik) — alternatif motor kullanıldı';
|
|
879
924
|
}
|
|
925
|
+
return out || { ok: false, error: 'web arama başarısız — tüm motorlar boş döndü' };
|
|
880
926
|
}
|
|
881
927
|
|
|
882
|
-
/* ---------- TinyFish (
|
|
928
|
+
/* ---------- TinyFish (anahtar girilirse zincirdeki kendi sırasında) ----------
|
|
883
929
|
GET https://api.search.tinyfish.ai?query=... · Header: X-API-Key
|
|
884
|
-
|
|
885
|
-
hata/sonuç yoksa eski zincir (tarayıcı → python çoklu-motor → Exa) devam eder. */
|
|
930
|
+
Anahtar yoksa bu motor otomatik atlanır; sıradaki motor devreye girer. */
|
|
886
931
|
let _tinyfishKey = null;
|
|
887
932
|
|
|
888
933
|
function setTinyfishKey(key) {
|
|
@@ -936,10 +981,19 @@ module.exports = {
|
|
|
936
981
|
webSearchFast,
|
|
937
982
|
seedScript,
|
|
938
983
|
bundledScriptPath,
|
|
939
|
-
|
|
940
|
-
|
|
984
|
+
/* arama zinciri (sıra + aç/kapa Ayarlar → Web Arama'dan) */
|
|
985
|
+
SEARCH_ENGINE_IDS,
|
|
986
|
+
DEFAULT_SEARCH_CHAIN,
|
|
987
|
+
setSearchChain,
|
|
988
|
+
setSearchObscuraEnabled,
|
|
989
|
+
getSearchChain,
|
|
990
|
+
searchChainWeb,
|
|
991
|
+
browserBanned,
|
|
992
|
+
banBrowser,
|
|
941
993
|
setTinyfishKey,
|
|
942
994
|
tinyfishSearch,
|
|
995
|
+
/* obscura geçişi */
|
|
996
|
+
obscura,
|
|
943
997
|
/* python altyapısı */
|
|
944
998
|
ensurePython,
|
|
945
999
|
findSystemPython,
|
package/src/main.js
CHANGED
|
@@ -7,7 +7,7 @@ const http = require('http');
|
|
|
7
7
|
const dns = require('dns');
|
|
8
8
|
const crypto = require('crypto');
|
|
9
9
|
const { spawn } = require('child_process');
|
|
10
|
-
const Engine = require('./agent/engine');
|
|
10
|
+
const { Engine, OBSERVE_MARK } = require('./agent/engine');
|
|
11
11
|
const { loadBeastConfig, beastDir } = require('./agent/config');
|
|
12
12
|
const bots = require('./agent/bots');
|
|
13
13
|
const mqueue = require('./agent/mqueue');
|
|
@@ -150,7 +150,9 @@ function startNpmUpdateWatch() {
|
|
|
150
150
|
check();
|
|
151
151
|
setInterval(check, 6 * 60 * 60 * 1000);
|
|
152
152
|
}
|
|
153
|
-
const
|
|
153
|
+
const toolsMod = require('./agent/tools');
|
|
154
|
+
const { htmlToText, setSearchChain, setSearchObscuraEnabled, setTinyfishKey } = toolsMod;
|
|
155
|
+
const obscura = require('./agent/obscura');
|
|
154
156
|
const { waToolLine } = require('./agent/watext');
|
|
155
157
|
|
|
156
158
|
/* #3 merkezî log sistemine process-seviye hataları da düşsün */
|
|
@@ -219,8 +221,17 @@ let engine = null;
|
|
|
219
221
|
let settings = loadSettings();
|
|
220
222
|
ensureBeastCode();
|
|
221
223
|
startHealthServer(); /* splash/boot aşamasından itibaren /health ayakta */
|
|
222
|
-
try {
|
|
224
|
+
try { setSearchObscuraEnabled(settings.obscuraEnabled !== false); } catch {} /* Obscura varsayılan AKTİF */
|
|
225
|
+
try { setSearchChain(settings.searchChain); } catch {}
|
|
223
226
|
try { setTinyfishKey(settings.tinyfishKey || null); } catch {}
|
|
227
|
+
/* kurulumda Obscura da kurulsun: yoksa ARKA PLANDA otomatik indir (UI kilitlenmez) */
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
if (!obscura.obscuraInstalled()) {
|
|
230
|
+
obscura.installObscura().then((r) => {
|
|
231
|
+
console.log('[obscura]', r.ok ? 'kuruldu: ' + r.dir : 'kurulamadı: ' + (r.error || '?'));
|
|
232
|
+
}).catch((e) => console.log('[obscura] kurulamadı:', String((e && e.message) || e)));
|
|
233
|
+
}
|
|
234
|
+
}, 4000).unref?.();
|
|
224
235
|
let wa = null;
|
|
225
236
|
let waChats = new Map(); // jid -> aktif session id
|
|
226
237
|
let waHistory = new Map(); // jid -> [sid,...] bu sohbete ait tüm oturumlar
|
|
@@ -1199,6 +1210,75 @@ async function waFlush(jid) {
|
|
|
1199
1210
|
}
|
|
1200
1211
|
}
|
|
1201
1212
|
|
|
1213
|
+
/* Grup göndereninin kimlik etiketi + SAHİP olup olmadığı.
|
|
1214
|
+
Sıra: izin listesindeki isim → @kullanıcı adı → gerçek PN → LID base.
|
|
1215
|
+
hitP.owner → bu kişi izin listesinde SAHİP olarak işaretli. */
|
|
1216
|
+
function waGroupSenderInfo(payload) {
|
|
1217
|
+
const pnDigits = String(payload.participantPn || '').split('@')[0].split(':')[0];
|
|
1218
|
+
const hitP = /^\d+$/.test(pnDigits) ? waFind(pnDigits) : null;
|
|
1219
|
+
const uname = String(payload.participantUsername || '').trim();
|
|
1220
|
+
let label;
|
|
1221
|
+
if (hitP && hitP.name) label = `${hitP.name} (+${pnDigits})`;
|
|
1222
|
+
else if (uname) label = `@${uname}${/^\d+$/.test(pnDigits) ? ' (+' + pnDigits + ')' : ''}`;
|
|
1223
|
+
else if (/^\d+$/.test(pnDigits)) label = '+' + pnDigits;
|
|
1224
|
+
else label = payload.participant ? '+' + String(payload.participant).split('@')[0].split(':')[0] : '';
|
|
1225
|
+
return { label, name: (hitP && hitP.name) || '', isOwner: !!(hitP && hitP.owner) };
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/* Oturum yoksa oluştur (processWaMessage ile aynı kalıp) */
|
|
1229
|
+
function ensureWaSession(jid) {
|
|
1230
|
+
let sid = waChats.get(jid);
|
|
1231
|
+
if (!sid) {
|
|
1232
|
+
const v = engine.createSession();
|
|
1233
|
+
sid = v.id;
|
|
1234
|
+
waChats.set(jid, sid);
|
|
1235
|
+
saveWaChats();
|
|
1236
|
+
if (wa) wa.setWatchJids([...waChats.keys()]);
|
|
1237
|
+
} else {
|
|
1238
|
+
waRememberSession(jid, sid);
|
|
1239
|
+
}
|
|
1240
|
+
return sid;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
/* ---------- GRUP BAĞLAM AKIŞI (mentionOnly + seeAll) ----------
|
|
1244
|
+
Bot grubun tüm konuşmasını okur ama CEVAP ÜRETMEZ; mesajlar sessizce
|
|
1245
|
+
oturum geçmişine bağlam olarak düşer. @mention gelince bot tüm bu
|
|
1246
|
+
bağlamı görerek konuşur. Anti-spam: aynı birleştirme penceresi. */
|
|
1247
|
+
const waCtxQueue = new Map(); // jid -> { timer, payloads[] }
|
|
1248
|
+
|
|
1249
|
+
function waGroupObserve(jid, payload, senderNum) {
|
|
1250
|
+
let q = waCtxQueue.get(jid);
|
|
1251
|
+
if (!q) {
|
|
1252
|
+
q = { payloads: [] };
|
|
1253
|
+
waCtxQueue.set(jid, q);
|
|
1254
|
+
}
|
|
1255
|
+
q.payloads.push({ payload, senderNum });
|
|
1256
|
+
clearTimeout(q.timer);
|
|
1257
|
+
waLog(`grup bağlam: kuyruğa girdi jid=${waPrettyJid(jid)} toplam=${q.payloads.length}`);
|
|
1258
|
+
q.timer = setTimeout(() => {
|
|
1259
|
+
waCtxFlush(jid).catch((e) => waLog(`ctx flush KRASİ: ${String((e && e.stack) || e)}`));
|
|
1260
|
+
}, WA_DEBOUNCE_MS);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
async function waCtxFlush(jid) {
|
|
1264
|
+
const q = waCtxQueue.get(jid);
|
|
1265
|
+
if (!q) return;
|
|
1266
|
+
waCtxQueue.delete(jid);
|
|
1267
|
+
const sid = ensureWaSession(jid);
|
|
1268
|
+
/* her satır gönderen etiketli olur — ajan kimin ne yazdığını izleyebilsin */
|
|
1269
|
+
const lines = [];
|
|
1270
|
+
for (const { payload } of q.payloads) {
|
|
1271
|
+
if (!payload || !payload.text) continue;
|
|
1272
|
+
const gi = waGroupSenderInfo(payload);
|
|
1273
|
+
const who = gi.label || '?';
|
|
1274
|
+
lines.push(`${who}: ${String(payload.text).slice(0, 1500)}`);
|
|
1275
|
+
}
|
|
1276
|
+
if (!lines.length) return;
|
|
1277
|
+
const text = `${OBSERVE_MARK} — WhatsApp grup konuşması (cevap verme, sadece bilgi olarak sakla)]\n` + lines.join('\n').slice(0, 6000);
|
|
1278
|
+
engine.observe(sid, text);
|
|
1279
|
+
waLog(`grup bağlam: oturuma işlendi sid=${sid} satır=${lines.length}`);
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1202
1282
|
/* ---------- WA'dan skill kurulumu ----------
|
|
1203
1283
|
SKILL.md (veya *.skill.md) belgesi atılırsa skills altına kurulur. */
|
|
1204
1284
|
|
|
@@ -1549,7 +1629,15 @@ async function handleWaIncoming(jid, payload, senderNum) {
|
|
|
1549
1629
|
if (isGroup) {
|
|
1550
1630
|
const g = settings.waGroups || {};
|
|
1551
1631
|
if (!g.enabled) return;
|
|
1552
|
-
|
|
1632
|
+
const mentionMode = g.mentionOnly !== false;
|
|
1633
|
+
/* MENTION MODU + seeAll: bot grubun TÜM konuşmasını BAĞLAM olarak görür
|
|
1634
|
+
ama yalnız @mention'da cevap üretir. seeAll VARSAYILAN KAPALI —
|
|
1635
|
+
mention'sız mesajlar tamamen yutulur (gizlilik). */
|
|
1636
|
+
if (mentionMode && !payload.mentioned && g.seeAll) {
|
|
1637
|
+
waGroupObserve(jid, payload, senderNum);
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
if (mentionMode && !payload.mentioned) return;
|
|
1553
1641
|
waLog(`grup mesajı jid=${waPrettyJid(jid)} participant=+${senderNum || '?'} mention=${!!payload.mentioned}`);
|
|
1554
1642
|
} else {
|
|
1555
1643
|
const hit = waFind(senderNum);
|
|
@@ -1725,22 +1813,21 @@ async function processWaMessage(jid, payload, senderNum, requeues = 0) {
|
|
|
1725
1813
|
Kimlik çözümleme sırası: izin listesindeki isim → @kullanıcı adı → gerçek PN → LID.
|
|
1726
1814
|
Ajan böylece grubun içinde mesajın KİMDEN geldiğini görür. */
|
|
1727
1815
|
let groupSender = '';
|
|
1816
|
+
let groupSenderOwner = false;
|
|
1728
1817
|
if (isGroup) {
|
|
1729
|
-
const
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
if (hitP && hitP.name) groupSender = `${hitP.name} (+${pnDigits})`;
|
|
1733
|
-
else if (uname) groupSender = `@${uname}${/^\d+$/.test(pnDigits) ? ' (+' + pnDigits + ')' : ''}`;
|
|
1734
|
-
else if (/^\d+$/.test(pnDigits)) groupSender = '+' + pnDigits;
|
|
1735
|
-
else groupSender = participantName || '';
|
|
1818
|
+
const gi = waGroupSenderInfo(payload);
|
|
1819
|
+
groupSender = gi.label || participantName;
|
|
1820
|
+
groupSenderOwner = gi.isOwner;
|
|
1736
1821
|
}
|
|
1737
|
-
/* #v13.1 rol: SAHİP vs MİSAFİR — ajan kime konuştuğunu net bilsin
|
|
1822
|
+
/* #v13.1 rol: SAHİP vs MİSAFİR — ajan kime konuştuğunu net bilsin.
|
|
1823
|
+
GRUPLARDA DA: gönderen izin listesinde SAHİP olarak işaretliyse ajan
|
|
1824
|
+
bunu görür — sahibinin grup içi talepleri misafir sözünden önceliklidir. */
|
|
1738
1825
|
const isOwner = !isGroup && !!hit.owner;
|
|
1739
|
-
const roleTag = isOwner
|
|
1826
|
+
const roleTag = isOwner || groupSenderOwner
|
|
1740
1827
|
? 'SAHİBİN (talepleri önceliklidir)'
|
|
1741
1828
|
: 'MİSAFİR (izinli ama sahibin sözü önceliklidir)';
|
|
1742
1829
|
const label = isGroup
|
|
1743
|
-
? `Grup ${jid.split('@')[0]}${groupSender ?
|
|
1830
|
+
? `Grup ${jid.split('@')[0]}${groupSender ? ` — gönderen: ${groupSender} — ${groupSenderOwner ? 'SAHİBİN' : 'MİSAFİR (grup üyesi)'}` : ''}`
|
|
1744
1831
|
: hit.name
|
|
1745
1832
|
? `${hit.name} (+${senderNum || '?'}) — ${roleTag}`
|
|
1746
1833
|
: `+${senderNum || '?'} — ${roleTag}`;
|
|
@@ -1748,7 +1835,9 @@ async function processWaMessage(jid, payload, senderNum, requeues = 0) {
|
|
|
1748
1835
|
if (!isGroup && !isOwner) {
|
|
1749
1836
|
text += `\n[NOT: Bu kişi SAHİP DEĞİL, misafirdir. Sahibin ayarlarını/verilerini değiştirme; kalıcı hafızaya misafire özel bilgi yazma.]`;
|
|
1750
1837
|
} else if (isGroup) {
|
|
1751
|
-
text +=
|
|
1838
|
+
text += groupSenderOwner
|
|
1839
|
+
? `\n[NOT: Grup mesajı ama gönderen SAHİBİN — talepleri önceliklidir, misafir gibi temkinli konuşmana gerek yok.]`
|
|
1840
|
+
: `\n[NOT: Grup mesajı — gönderen grubun üyesidir, izin listendeki kişi olmayabilir. Grup üyelerine karşı temkinli konuş.]`;
|
|
1752
1841
|
}
|
|
1753
1842
|
const attachments = [];
|
|
1754
1843
|
|
|
@@ -4040,7 +4129,7 @@ ipcMain.handle('agent:interrupt', (_e, sessionId) => {
|
|
|
4040
4129
|
desktopQueue.delete(String(sessionId));
|
|
4041
4130
|
}
|
|
4042
4131
|
} catch {}
|
|
4043
|
-
return engine.interrupt(sessionId);
|
|
4132
|
+
return engine.interrupt(sessionId, 'kullanıcı sohbetteki durdurma (■) düğmesiyle iptal etti');
|
|
4044
4133
|
});
|
|
4045
4134
|
|
|
4046
4135
|
/* ---------- masaüstü sohbet birleştirme ----------
|
|
@@ -4404,7 +4493,9 @@ ipcMain.handle('memory:get', () => memory.loadAll());
|
|
|
4404
4493
|
/* paralel ajanlar: canlı izleme (#14) */
|
|
4405
4494
|
ipcMain.handle('agents:list', () => engine.listBgJobs());
|
|
4406
4495
|
ipcMain.handle('agents:detail', (_e, id) => engine.bgDetail(id));
|
|
4407
|
-
ipcMain.handle('agents:cancel', (_e, id) => ({
|
|
4496
|
+
ipcMain.handle('agents:cancel', (_e, id) => ({
|
|
4497
|
+
ok: engine.interrupt(String(id || ''), 'kullanıcı Paralel Ajanlar panelinden (×) iptal etti'),
|
|
4498
|
+
}));
|
|
4408
4499
|
ipcMain.handle('ceo:get', () => !!engine.ceoMode);
|
|
4409
4500
|
ipcMain.handle('ceo:set', (_e, v) => {
|
|
4410
4501
|
settings.ceoMode = !!v;
|
|
@@ -4551,12 +4642,16 @@ ipcMain.handle('wherewasi:set', (_e, cfg) => {
|
|
|
4551
4642
|
return settings.whereWasI;
|
|
4552
4643
|
});
|
|
4553
4644
|
|
|
4554
|
-
/* WhatsApp grup ayarı: { enabled, mentionOnly }
|
|
4555
|
-
|
|
4645
|
+
/* WhatsApp grup ayarı: { enabled, mentionOnly, seeAll }
|
|
4646
|
+
seeAll: yalnız mentionOnly modunda anlamlı — bot grubun TÜM konuşmasını
|
|
4647
|
+
bağlam olarak okur ama yine SADECE @mention'da cevap verir.
|
|
4648
|
+
VARSAYILAN KAPALI: herkesin konuşmasını görmesi istenmeyebilir. */
|
|
4649
|
+
ipcMain.handle('wa:groups:get', () => settings.waGroups || { enabled: false, mentionOnly: true, seeAll: false });
|
|
4556
4650
|
ipcMain.handle('wa:groups:set', (_e, cfg) => {
|
|
4557
4651
|
settings.waGroups = {
|
|
4558
4652
|
enabled: !!(cfg && cfg.enabled),
|
|
4559
4653
|
mentionOnly: !(cfg && cfg.mentionOnly === false),
|
|
4654
|
+
seeAll: !!(cfg && cfg.seeAll),
|
|
4560
4655
|
};
|
|
4561
4656
|
saveSettings();
|
|
4562
4657
|
return settings.waGroups;
|
|
@@ -5108,7 +5203,7 @@ ipcMain.handle('beastcode:stop', () => {
|
|
|
5108
5203
|
const sid = bcSessions.get(ideRoot());
|
|
5109
5204
|
if (!sid) return { ok: false };
|
|
5110
5205
|
let r = false;
|
|
5111
|
-
try { r = engine.interrupt(sid); } catch {}
|
|
5206
|
+
try { r = engine.interrupt(sid, 'kullanıcı Beast Code panelinden ■ ile durdurdu'); } catch {}
|
|
5112
5207
|
return { ok: !!r };
|
|
5113
5208
|
});
|
|
5114
5209
|
|
|
@@ -5129,26 +5224,38 @@ ipcMain.handle('think:set', (_e, v) => {
|
|
|
5129
5224
|
return engine.publicState();
|
|
5130
5225
|
});
|
|
5131
5226
|
|
|
5132
|
-
/*
|
|
5133
|
-
ipcMain.handle('
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5227
|
+
/* Obscura stealth headless tarayıcı (Ayarlar → Web Arama) */
|
|
5228
|
+
ipcMain.handle('obscura:get', () => ({
|
|
5229
|
+
installed: obscura.obscuraInstalled(),
|
|
5230
|
+
dir: obscura.obscuraDir(),
|
|
5231
|
+
enabled: settings.obscuraEnabled !== false,
|
|
5232
|
+
}));
|
|
5233
|
+
ipcMain.handle('obscura:install', async () => {
|
|
5234
|
+
try {
|
|
5235
|
+
const r = await obscura.installObscura();
|
|
5236
|
+
return { ok: !!r.ok, dir: r.dir || obscura.obscuraDir(), error: r.error || null };
|
|
5237
|
+
} catch (e) {
|
|
5238
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
5143
5239
|
}
|
|
5144
|
-
const k = settings.exaKey || '';
|
|
5145
|
-
return { ok: true, set: !!k, masked: k ? '••••••••' + k.slice(-4) : '' };
|
|
5146
5240
|
});
|
|
5147
|
-
ipcMain.handle('
|
|
5148
|
-
settings.
|
|
5241
|
+
ipcMain.handle('obscura:setEnabled', (_e, v) => {
|
|
5242
|
+
settings.obscuraEnabled = v !== false;
|
|
5149
5243
|
saveSettings();
|
|
5150
|
-
|
|
5151
|
-
return { ok: true,
|
|
5244
|
+
try { setSearchObscuraEnabled(settings.obscuraEnabled); } catch {}
|
|
5245
|
+
return { ok: true, enabled: settings.obscuraEnabled };
|
|
5246
|
+
});
|
|
5247
|
+
|
|
5248
|
+
/* Arama zinciri sırası (Ayarlar → Web Arama'dan değiştirilir) */
|
|
5249
|
+
ipcMain.handle('searchorder:get', () => ({ chain: toolsMod.getSearchChain() }));
|
|
5250
|
+
ipcMain.handle('searchorder:set', (_e, chain) => {
|
|
5251
|
+
try {
|
|
5252
|
+
const rows = setSearchChain(chain);
|
|
5253
|
+
settings.searchChain = rows;
|
|
5254
|
+
saveSettings();
|
|
5255
|
+
return { ok: true, chain: rows };
|
|
5256
|
+
} catch (e) {
|
|
5257
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
5258
|
+
}
|
|
5152
5259
|
});
|
|
5153
5260
|
|
|
5154
5261
|
/* #TinyFish: anahtar girilirse web_search zincirinin BAŞINDA kullanılır */
|
package/src/preload.js
CHANGED
|
@@ -70,9 +70,11 @@ contextBridge.exposeInMainWorld('beast', {
|
|
|
70
70
|
beastcodeStop: () => ipcRenderer.invoke('beastcode:stop'),
|
|
71
71
|
beastcodeNew: () => ipcRenderer.invoke('beastcode:new'),
|
|
72
72
|
thinkSet: (v) => ipcRenderer.invoke('think:set', v),
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
obscuraGet: () => ipcRenderer.invoke('obscura:get'),
|
|
74
|
+
obscuraInstall: () => ipcRenderer.invoke('obscura:install'),
|
|
75
|
+
obscuraSetEnabled: (v) => ipcRenderer.invoke('obscura:setEnabled', v),
|
|
76
|
+
searchOrderGet: () => ipcRenderer.invoke('searchorder:get'),
|
|
77
|
+
searchOrderSet: (chain) => ipcRenderer.invoke('searchorder:set', chain),
|
|
76
78
|
tinyfishGet: () => ipcRenderer.invoke('tinyfish:get'),
|
|
77
79
|
tinyfishSet: (key) => ipcRenderer.invoke('tinyfish:set', key),
|
|
78
80
|
tinyfishClear: () => ipcRenderer.invoke('tinyfish:clear'),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -111,10 +111,10 @@
|
|
|
111
111
|
lim_saved_toast: 'Limitler kaydedildi',
|
|
112
112
|
lim_unlimited: 'limitsiz',
|
|
113
113
|
tf_h2: 'TinyFish Arama',
|
|
114
|
-
tf_sub: 'Ücretsiz, hızlı web arama API\u2019si. Anahtar girilirse
|
|
114
|
+
tf_sub: 'Ücretsiz, hızlı web arama API\u2019si. Anahtar girilirse zincirde KENDİ SIRASINDA kullanılır; anahtar yoksa motor otomatik atlanır.',
|
|
115
115
|
tf_key_label: 'TinyFish API anahtarı (agent.tinyfish.ai/api-keys)',
|
|
116
116
|
tf_status_set: 'TinyFish aktif — kayıtlı anahtar: ',
|
|
117
|
-
tf_status_unset: 'TinyFish pasif —
|
|
117
|
+
tf_status_unset: 'TinyFish pasif — zincir sıradaki motorla devam eder.',
|
|
118
118
|
tf_empty_toast: 'Anahtar boş — mevcut anahtar korundu',
|
|
119
119
|
tf_saved_toast: 'TinyFish anahtarı kaydedildi',
|
|
120
120
|
tf_cleared_toast: 'TinyFish anahtarı silindi',
|
|
@@ -197,6 +197,7 @@
|
|
|
197
197
|
it_reset_pair: 'Eşlemeyi Sıfırla',
|
|
198
198
|
it_groups_on: 'WhatsApp gruplarında çalış',
|
|
199
199
|
it_groups_mention: 'Sadece @mention edilince cevap ver (grupta botu etiketle)',
|
|
200
|
+
it_groups_seeall: 'Mention modunda grubun TÜM konuşmalarını oku (bağlam biriktirir, yine sadece @mention\'a cevap verir)',
|
|
200
201
|
it_groups_all: 'Grubun her mesajına karış',
|
|
201
202
|
it_allow_label: 'İzinli numaralar',
|
|
202
203
|
it_name_ph: 'İsim (zorunlu)',
|
|
@@ -259,18 +260,30 @@
|
|
|
259
260
|
ws_calls: 'çağrı',
|
|
260
261
|
ws_token: 'token',
|
|
261
262
|
ws_h2: 'Web Arama',
|
|
262
|
-
ws_sub: 'Arama zinciri: 1) dahili tarayıcı (direk Google) 2) python çoklu-motor (ddgs / DDG+Bing+Mojeek)
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
ws_sub: 'Arama zinciri: 1) dahili tarayıcı (direk Google) 2) Obscura (stealth headless → DuckDuckGo) 3) TinyFish (anahtar varsa) 4) python çoklu-motor (ddgs / DDG+Bing+Mojeek) — otomatik. Sıra ve aç/kapa aşağıdan değiştirilir.',
|
|
264
|
+
oc_h2: 'Obscura (Stealth Tarayıcı)',
|
|
265
|
+
oc_sub: 'Rust tabanlı gizli headless tarayıcı (anti-detect + V8, Chromium\u2019suz). Kurulu değilse ilk açılışta otomatik indirilir (%APPDATA%\\beast\\obscura); DuckDuckGo aramasını bot korumasını aşarak yapar.',
|
|
266
|
+
oc_status_ok: 'Obscura kurulu ve hazır.',
|
|
267
|
+
oc_status_missing: 'Obscura kurulu değil — ilk açılışta otomatik indirilir (≈74 MB).',
|
|
268
|
+
oc_install: 'Kur / Güncelle',
|
|
269
|
+
oc_installing: 'İndiriliyor… (≈74 MB, bağlantıya göre birkaç dakika)',
|
|
270
|
+
oc_installed_toast: 'Obscura kuruldu',
|
|
271
|
+
oc_install_fail: 'Kurulamadı: ',
|
|
272
|
+
oc_enabled: 'Obscura arama zincirinde aktif',
|
|
273
|
+
oc_on_toast: 'Obscura aktif',
|
|
274
|
+
oc_off_toast: 'Obscura pasif',
|
|
275
|
+
so_h2: 'Arama Sırası',
|
|
276
|
+
so_sub: 'Motorlar bu sırayla denenir; boş dönen motor atlanıp sıradaki devreye girer. ↑↓ ile sırayı, kutucukla aç/kapa durumunu değiştir.',
|
|
277
|
+
so_engine_browser: 'Dahili Tarayıcı (Google)',
|
|
278
|
+
so_engine_obscura: 'Obscura (Stealth → DuckDuckGo)',
|
|
279
|
+
so_engine_tinyfish: 'TinyFish API (anahtar gerekir)',
|
|
280
|
+
so_engine_python: 'Python Çoklu-Motor (ddgs/DDG/Bing/Mojeek)',
|
|
281
|
+
so_saved_toast: 'Arama sırası kaydedildi',
|
|
265
282
|
ws_save: 'Kaydet',
|
|
266
283
|
ws_clear: 'Anahtarı Sil',
|
|
267
284
|
ws_note: 'Anahtar maskeli tutulur; yenisini yazıp kaydettiğinde eskisinin yerine geçer. Boş kaydet: mevcut anahtar korunur.',
|
|
268
|
-
ws_status_set: 'Exa aktif — kayıtlı anahtar: ',
|
|
269
|
-
ws_status_unset: 'Exa yapılandırılmadı — ücretsiz python çoklu-motor arama kullanılıyor.',
|
|
270
285
|
ws_empty_toast: 'Anahtar boş — mevcut anahtar korundu',
|
|
271
|
-
ws_saved_toast: 'Exa anahtarı kaydedildi',
|
|
272
286
|
ws_fail_toast: 'Kaydedilemedi',
|
|
273
|
-
ws_cleared_toast: 'Exa anahtarı silindi',
|
|
274
287
|
ag_h2: 'Paralel Ajanlar',
|
|
275
288
|
ag_sub: 'CEO\u2019nun devrettiği arka plan işleri — canlı izleme',
|
|
276
289
|
ag_ceo_label: 'CEO modu — konuşan ajan iş yapmaz, sadece emir verir',
|
|
@@ -299,12 +312,11 @@
|
|
|
299
312
|
us_where_on_toast: 'Açılış özeti açık',
|
|
300
313
|
us_where_off_toast: 'Açılış özeti kapandı',
|
|
301
314
|
ws_h2: 'Web Arama',
|
|
302
|
-
|
|
315
|
+
oc_status_ok: 'Obscura kurulu ve hazır.',
|
|
316
|
+
oc_status_missing: 'Obscura kurulu değil — ilk açılışta otomatik indirilir.',
|
|
303
317
|
ws_save: 'Kaydet',
|
|
304
318
|
ws_clear: 'Anahtarı Sil',
|
|
305
319
|
ws_key_note: 'Anahtar maskeli tutulur; yenisini yazıp kaydettiğinde eskisinin yerine geçer. Boş kaydet: mevcut anahtar korunur.',
|
|
306
|
-
ws_active: 'Exa aktif — kayıtlı anahtar: ',
|
|
307
|
-
ws_notcfg: 'Exa yapılandırılmadı — ücretsiz python çoklu-motor arama kullanılıyor.',
|
|
308
320
|
ws_empty: 'Anahtar boş — mevcut anahtar korundu',
|
|
309
321
|
// terminal & tarayıcı & mini
|
|
310
322
|
term_clear: 'Temizle',
|
|
@@ -640,10 +652,10 @@
|
|
|
640
652
|
lim_saved_toast: 'Limits saved',
|
|
641
653
|
lim_unlimited: 'unlimited',
|
|
642
654
|
tf_h2: 'TinyFish Search',
|
|
643
|
-
tf_sub: 'Free, fast web search API. If a key is set it is used
|
|
655
|
+
tf_sub: 'Free, fast web search API. If a key is set it is used in its own slot of the chain; without a key the engine is skipped automatically.',
|
|
644
656
|
tf_key_label: 'TinyFish API key (agent.tinyfish.ai/api-keys)',
|
|
645
657
|
tf_status_set: 'TinyFish active — saved key: ',
|
|
646
|
-
tf_status_unset: 'TinyFish off —
|
|
658
|
+
tf_status_unset: 'TinyFish off — the chain continues with the next engine.',
|
|
647
659
|
tf_empty_toast: 'Key empty — current key kept',
|
|
648
660
|
tf_saved_toast: 'TinyFish key saved',
|
|
649
661
|
tf_cleared_toast: 'TinyFish key deleted',
|
|
@@ -726,6 +738,7 @@
|
|
|
726
738
|
it_reset_pair: 'Reset pairing',
|
|
727
739
|
it_groups_on: 'Work in WhatsApp groups',
|
|
728
740
|
it_groups_mention: 'Reply only when @mentioned (tag the bot in the group)',
|
|
741
|
+
it_groups_seeall: 'In mention mode, read ALL group messages as context (still replies only when @mentioned)',
|
|
729
742
|
it_groups_all: 'Respond to every group message',
|
|
730
743
|
it_allow_label: 'Allowed numbers',
|
|
731
744
|
it_name_ph: 'Name (required)',
|
|
@@ -788,18 +801,30 @@
|
|
|
788
801
|
ws_calls: 'calls',
|
|
789
802
|
ws_token: 'tokens',
|
|
790
803
|
ws_h2: 'Web Search',
|
|
791
|
-
ws_sub: 'Search chain: 1) built-in browser (direct Google) 2) python multi-engine (ddgs / DDG+Bing+Mojeek)
|
|
792
|
-
|
|
793
|
-
|
|
804
|
+
ws_sub: 'Search chain: 1) built-in browser (direct Google) 2) Obscura (stealth headless → DuckDuckGo) 3) TinyFish (if a key is set) 4) python multi-engine (ddgs / DDG+Bing+Mojeek) — automatic. Order and on/off are editable below.',
|
|
805
|
+
oc_h2: 'Obscura (Stealth Browser)',
|
|
806
|
+
oc_sub: 'Rust-based stealth headless browser (anti-detect + V8, no Chromium). If not installed it is auto-downloaded at first launch (%APPDATA%\\beast\\obscura); it searches DuckDuckGo past bot protection.',
|
|
807
|
+
oc_status_ok: 'Obscura is installed and ready.',
|
|
808
|
+
oc_status_missing: 'Obscura is not installed — it is auto-downloaded at first launch (≈74 MB).',
|
|
809
|
+
oc_install: 'Install / Update',
|
|
810
|
+
oc_installing: 'Downloading… (≈74 MB, may take a few minutes)',
|
|
811
|
+
oc_installed_toast: 'Obscura installed',
|
|
812
|
+
oc_install_fail: 'Install failed: ',
|
|
813
|
+
oc_enabled: 'Obscura active in the search chain',
|
|
814
|
+
oc_on_toast: 'Obscura enabled',
|
|
815
|
+
oc_off_toast: 'Obscura disabled',
|
|
816
|
+
so_h2: 'Search Order',
|
|
817
|
+
so_sub: 'Engines are tried in this order; an empty engine is skipped and the next one takes over. Use ↑↓ to reorder, checkbox to enable/disable.',
|
|
818
|
+
so_engine_browser: 'Built-in Browser (Google)',
|
|
819
|
+
so_engine_obscura: 'Obscura (Stealth → DuckDuckGo)',
|
|
820
|
+
so_engine_tinyfish: 'TinyFish API (needs a key)',
|
|
821
|
+
so_engine_python: 'Python Multi-Engine (ddgs/DDG/Bing/Mojeek)',
|
|
822
|
+
so_saved_toast: 'Search order saved',
|
|
794
823
|
ws_save: 'Save',
|
|
795
824
|
ws_clear: 'Delete Key',
|
|
796
825
|
ws_note: 'The key is kept masked; saving a new one replaces the old. Saving empty keeps the current key.',
|
|
797
|
-
ws_status_set: 'Exa active — saved key: ',
|
|
798
|
-
ws_status_unset: 'Exa not configured — using free python multi-engine search.',
|
|
799
826
|
ws_empty_toast: 'Key empty — current key kept',
|
|
800
|
-
ws_saved_toast: 'Exa key saved',
|
|
801
827
|
ws_fail_toast: 'Could not save',
|
|
802
|
-
ws_cleared_toast: 'Exa key deleted',
|
|
803
828
|
ag_h2: 'Parallel Agents',
|
|
804
829
|
ag_sub: 'Background tasks delegated by the CEO — live monitoring',
|
|
805
830
|
ag_ceo_label: 'CEO mode — the talking agent does no work, only gives orders',
|
|
@@ -890,12 +915,11 @@
|
|
|
890
915
|
us_where_on_toast: 'Startup summary on',
|
|
891
916
|
us_where_off_toast: 'Startup summary off',
|
|
892
917
|
ws_h2: 'Web Search',
|
|
893
|
-
|
|
918
|
+
oc_status_ok: 'Obscura is installed and ready.',
|
|
919
|
+
oc_status_missing: 'Obscura is not installed — it is auto-downloaded at first launch.',
|
|
894
920
|
ws_save: 'Save',
|
|
895
921
|
ws_clear: 'Delete Key',
|
|
896
922
|
ws_key_note: 'Key is kept masked; saving a new one replaces the old. Save empty: existing key kept.',
|
|
897
|
-
ws_active: 'Exa active — saved key: ',
|
|
898
|
-
ws_notcfg: 'Exa not configured — free python multi-engine search is used.',
|
|
899
923
|
ws_empty: 'Key empty — existing key kept',
|
|
900
924
|
term_clear: 'Clear',
|
|
901
925
|
term_ph: 'type a command, press Enter…',
|