beast-agent 0.27.0 → 0.29.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/src/main.js CHANGED
@@ -150,7 +150,9 @@ function startNpmUpdateWatch() {
150
150
  check();
151
151
  setInterval(check, 6 * 60 * 60 * 1000);
152
152
  }
153
- const { htmlToText, setExaKey, setTinyfishKey } = require('./agent/tools');
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,14 @@ let engine = null;
219
221
  let settings = loadSettings();
220
222
  ensureBeastCode();
221
223
  startHealthServer(); /* splash/boot aşamasından itibaren /health ayakta */
222
- try { setExaKey(settings.exaKey || null); } catch {}
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
+ ilerleme Ayarlar → Web Arama'dan da izlenebilir */
229
+ setTimeout(() => {
230
+ if (!obscura.obscuraInstalled()) startObscuraInstall();
231
+ }, 4000).unref?.();
224
232
  let wa = null;
225
233
  let waChats = new Map(); // jid -> aktif session id
226
234
  let waHistory = new Map(); // jid -> [sid,...] bu sohbete ait tüm oturumlar
@@ -623,10 +631,24 @@ function emitWaEventSafe(ev) {
623
631
 
624
632
 
625
633
 
634
+ /* sürüm: app.getVersion() + package.json yedeği (splash, /help, /version ortak) */
635
+ function beastVersion() {
636
+ try {
637
+ const v = app.getVersion();
638
+ if (v && v !== '0.0.0') return v;
639
+ } catch {}
640
+ try {
641
+ return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version || '?';
642
+ } catch {
643
+ return '?';
644
+ }
645
+ }
646
+
626
647
  function waSlashHelp() {
627
648
  return [
628
649
  '*Beast komutları*',
629
650
  '• */help* – bu liste',
651
+ '• */version* – Beast Agent sürümünü göster',
630
652
  '• */new* – yeni oturum aç (kod verilir)',
631
653
  '• */open* <kod> – o koddaki oturuma geç',
632
654
  '• */sessions* – bu sohbetin oturumları',
@@ -1124,6 +1146,8 @@ async function tryWaSlash(jid, rawText, senderNum) {
1124
1146
  out =
1125
1147
  `*WA:* ${wst.status}${wst.user ? ' (' + wst.user + ')' : ''}\n` +
1126
1148
  `*İzleyici:* ${watchers.list().length} adet\n*Cron:* ${jobs} aktif görev`;
1149
+ } else if (cmd === 'version') {
1150
+ out = `*Beast Agent v${beastVersion()}*\nGüncelleme için: /update (kurulum hazır olunca /update now)`;
1127
1151
  } else {
1128
1152
  out = `Bilinmeyen komut: /${cmd}\nListe için /help yaz.`;
1129
1153
  }
@@ -3662,7 +3686,7 @@ function createSplash() {
3662
3686
  const muted = dark ? '#9a9aa2' : '#707078';
3663
3687
  splash = new BrowserWindow({
3664
3688
  width: 420,
3665
- height: 330,
3689
+ height: 352,
3666
3690
  frame: false,
3667
3691
  resizable: false,
3668
3692
  alwaysOnTop: true,
@@ -3675,6 +3699,7 @@ function createSplash() {
3675
3699
  body{margin:0;height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;background:${bg};font-family:'Segoe UI',sans-serif;color:${fg}}
3676
3700
  .logo{width:84px;height:84px;border-radius:20px;background:${fg};color:${bg};display:flex;align-items:center;justify-content:center;font-weight:900;font-size:44px}
3677
3701
  .t{margin-top:16px;font-size:20px;color:${fg}}.t b{font-weight:900}
3702
+ .v{margin-top:8px;font-size:12.5px;font-weight:800;letter-spacing:.6px;color:${muted};background:${muted}22;padding:2px 12px;border-radius:9px}
3678
3703
  .s{margin-top:6px;font-size:12px;color:${muted}}
3679
3704
  .cmds{margin-top:14px;display:flex;flex-wrap:wrap;gap:6px;justify-content:center;max-width:360px}
3680
3705
  .cmds b{font-size:11.5px;font-weight:800;color:${fg};background:${muted}22;padding:2px 9px;border-radius:6px;letter-spacing:.3px}
@@ -3685,9 +3710,10 @@ function createSplash() {
3685
3710
  </style></head><body>
3686
3711
  <div class="logo">B</div>
3687
3712
  <div class="t"><b>BEAST</b> Agent</div>
3688
- <div class="s">hızlı · hafif · becerikli — v${app.getVersion()}</div>
3713
+ <div class="v">v${beastVersion()}</div>
3714
+ <div class="s">hızlı · hafif · becerikli</div>
3689
3715
  <div class="cmds">
3690
- <b>/help</b><b>/restart</b><b>/change</b><b>/think</b><b>/clear</b><b>/stop</b><b>/usage</b><b>/backup</b><b>/status</b><span>…</span>
3716
+ <b>/help</b><b>/version</b><b>/restart</b><b>/change</b><b>/think</b><b>/clear</b><b>/stop</b><b>/usage</b><b>/backup</b><b>/status</b><span>…</span>
3691
3717
  </div>
3692
3718
  <div class="bar"><i></i></div>
3693
3719
  </body></html>`;
@@ -3819,6 +3845,10 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
3819
3845
  handleRestart(sessionId);
3820
3846
  return true;
3821
3847
  }
3848
+ if (t === '/version') {
3849
+ desktopEcho(sessionId, t, `**Beast Agent v${beastVersion()}**\nGüncelleme: **/update** (yeni sürüm kontrolü) · **/update now** (hemen kur)`);
3850
+ return true;
3851
+ }
3822
3852
  if (t === '/help') {
3823
3853
  desktopEcho(sessionId, '/help', desktopSlashHelp());
3824
3854
  return true;
@@ -3968,6 +3998,7 @@ function desktopSlashHelp() {
3968
3998
  return [
3969
3999
  '**Beast komutları**',
3970
4000
  '**/help** – bu liste',
4001
+ '**/version** – Beast Agent sürümünü göster',
3971
4002
  '**/restart** – uygulamayı yeniden başlat',
3972
4003
  '**/stop** – koşan işleri durdur · **/start** – devam ettir',
3973
4004
  '**/change [n]** – modelleri listele · n. modele geç',
@@ -4070,6 +4101,13 @@ function thinkStatusText() {
4070
4101
  }
4071
4102
 
4072
4103
  /* /restart: uygulama kendini yeniden başlatır (relaunch + exit) */
4104
+ function scheduleAppRestart(delayMs = 800) {
4105
+ setTimeout(() => {
4106
+ try { app.relaunch(); } catch {}
4107
+ try { app.exit(0); } catch {}
4108
+ }, delayMs);
4109
+ }
4110
+
4073
4111
  function handleRestart(sessionId) {
4074
4112
  const sid = String(sessionId || ''); if (win && !win.isDestroyed()) {
4075
4113
  win.webContents.send('agent:event', { sessionId: sid, type: 'message', message: { role: 'user', content: '/restart' } });
@@ -4080,10 +4118,7 @@ function handleRestart(sessionId) {
4080
4118
  });
4081
4119
  win.webContents.send('agent:event', { sessionId: sid, type: 'done', usage: null });
4082
4120
  }
4083
- setTimeout(() => {
4084
- try { app.relaunch(); } catch {}
4085
- try { app.exit(0); } catch {}
4086
- }, 800);
4121
+ scheduleAppRestart(800);
4087
4122
  }
4088
4123
 
4089
4124
  /* Masaüstünde /stop ve /start: engine'e gitmez; tüm sistemde etki eder ve
@@ -4118,7 +4153,7 @@ ipcMain.handle('agent:interrupt', (_e, sessionId) => {
4118
4153
  desktopQueue.delete(String(sessionId));
4119
4154
  }
4120
4155
  } catch {}
4121
- return engine.interrupt(sessionId);
4156
+ return engine.interrupt(sessionId, 'kullanıcı sohbetteki durdurma (■) düğmesiyle iptal etti');
4122
4157
  });
4123
4158
 
4124
4159
  /* ---------- masaüstü sohbet birleştirme ----------
@@ -4230,6 +4265,10 @@ let netFailStreak = 0;
4230
4265
  let chatQueueFlushing = false;
4231
4266
  const chatOfflineQueue = []; // { key, sessionId, text, attachments, at }
4232
4267
 
4268
+ /* LLM retry'ı net izleyicisinden besler: internet kopmuşsa istek,
4269
+ bağlantı dönene kadar bekler — "fetch failed" ile görev ölmez */
4270
+ try { require('./agent/llm').setNetProbe(() => netOnline); } catch {}
4271
+
4233
4272
  /* diskten yükle (app restart sonrası kuyruk korunur) */
4234
4273
  (function chatQueueLoad() {
4235
4274
  try {
@@ -4482,7 +4521,9 @@ ipcMain.handle('memory:get', () => memory.loadAll());
4482
4521
  /* paralel ajanlar: canlı izleme (#14) */
4483
4522
  ipcMain.handle('agents:list', () => engine.listBgJobs());
4484
4523
  ipcMain.handle('agents:detail', (_e, id) => engine.bgDetail(id));
4485
- ipcMain.handle('agents:cancel', (_e, id) => ({ ok: engine.interrupt(String(id || '')) }));
4524
+ ipcMain.handle('agents:cancel', (_e, id) => ({
4525
+ ok: engine.interrupt(String(id || ''), 'kullanıcı Paralel Ajanlar panelinden (×) iptal etti'),
4526
+ }));
4486
4527
  ipcMain.handle('ceo:get', () => !!engine.ceoMode);
4487
4528
  ipcMain.handle('ceo:set', (_e, v) => {
4488
4529
  settings.ceoMode = !!v;
@@ -5190,7 +5231,7 @@ ipcMain.handle('beastcode:stop', () => {
5190
5231
  const sid = bcSessions.get(ideRoot());
5191
5232
  if (!sid) return { ok: false };
5192
5233
  let r = false;
5193
- try { r = engine.interrupt(sid); } catch {}
5234
+ try { r = engine.interrupt(sid, 'kullanıcı Beast Code panelinden ■ ile durdurdu'); } catch {}
5194
5235
  return { ok: !!r };
5195
5236
  });
5196
5237
 
@@ -5211,26 +5252,69 @@ ipcMain.handle('think:set', (_e, v) => {
5211
5252
  return engine.publicState();
5212
5253
  });
5213
5254
 
5214
- /* Exa web arama anahtarı (Ayarlar → Web Arama) */
5215
- ipcMain.handle('exa:get', () => {
5216
- const k = settings.exaKey || '';
5217
- return { set: !!k, masked: k ? '••••••••' + k.slice(-4) : '' };
5255
+ /* Obscura stealth headless tarayıcı (Ayarlar → Web Arama) */
5256
+
5257
+ /* kurulum durumu: ayarlardan çıkılıp dönülsa da main process'te sürer;
5258
+ ilerleme agent:event ile panele, obscura:installState ile sekme açılışına taşınır */
5259
+ let obscuraInstallState = { running: false, pct: 0, phase: '', error: null };
5260
+
5261
+ function pushObscuraProgress() {
5262
+ try {
5263
+ if (win && !win.isDestroyed()) win.webContents.send('agent:event', { type: 'obscura-progress', ...obscuraInstallState });
5264
+ } catch {}
5265
+ }
5266
+
5267
+ function startObscuraInstall() {
5268
+ if (obscuraInstallState.running) return { ok: false, busy: true, ...obscuraInstallState };
5269
+ obscuraInstallState = { running: true, pct: 0, phase: 'hazırlanıyor', error: null };
5270
+ pushObscuraProgress();
5271
+ obscura
5272
+ .installObscura(null, (p) => {
5273
+ obscuraInstallState.pct = Math.max(0, Math.min(100, Math.round(Number(p && p.pct) || 0)));
5274
+ obscuraInstallState.phase = String((p && p.phase) || obscuraInstallState.phase || '');
5275
+ pushObscuraProgress();
5276
+ })
5277
+ .then((r) => {
5278
+ obscuraInstallState = r && r.ok
5279
+ ? { running: false, pct: 100, phase: 'tamamlandı', error: null }
5280
+ : { running: false, pct: 0, phase: 'hata', error: String((r && r.error) || 'bilinmeyen hata') };
5281
+ pushObscuraProgress();
5282
+ console.log('[obscura]', r && r.ok ? 'kuruldu: ' + r.dir : 'kurulamadı: ' + ((r && r.error) || '?'));
5283
+ })
5284
+ .catch((e) => {
5285
+ obscuraInstallState = { running: false, pct: 0, phase: 'hata', error: String((e && e.message) || e) };
5286
+ pushObscuraProgress();
5287
+ console.log('[obscura] kurulamadı:', String((e && e.message) || e));
5288
+ });
5289
+ return { ok: true, started: true, ...obscuraInstallState };
5290
+ }
5291
+
5292
+ ipcMain.handle('obscura:get', () => ({
5293
+ installed: obscura.obscuraInstalled(),
5294
+ dir: obscura.obscuraDir(),
5295
+ enabled: settings.obscuraEnabled !== false,
5296
+ install: { ...obscuraInstallState },
5297
+ }));
5298
+ ipcMain.handle('obscura:install', () => startObscuraInstall());
5299
+ ipcMain.handle('obscura:installState', () => ({ ...obscuraInstallState }));
5300
+ ipcMain.handle('obscura:setEnabled', (_e, v) => {
5301
+ settings.obscuraEnabled = v !== false;
5302
+ saveSettings();
5303
+ try { setSearchObscuraEnabled(settings.obscuraEnabled); } catch {}
5304
+ return { ok: true, enabled: settings.obscuraEnabled };
5218
5305
  });
5219
- ipcMain.handle('exa:set', (_e, key) => {
5220
- const v = String(key || '').trim();
5221
- if (v) {
5222
- settings.exaKey = v;
5306
+
5307
+ /* Arama zinciri sırası (Ayarlar → Web Arama'dan değiştirilir) */
5308
+ ipcMain.handle('searchorder:get', () => ({ chain: toolsMod.getSearchChain() }));
5309
+ ipcMain.handle('searchorder:set', (_e, chain) => {
5310
+ try {
5311
+ const rows = setSearchChain(chain);
5312
+ settings.searchChain = rows;
5223
5313
  saveSettings();
5224
- setExaKey(settings.exaKey);
5314
+ return { ok: true, chain: rows };
5315
+ } catch (e) {
5316
+ return { ok: false, error: String((e && e.message) || e) };
5225
5317
  }
5226
- const k = settings.exaKey || '';
5227
- return { ok: true, set: !!k, masked: k ? '••••••••' + k.slice(-4) : '' };
5228
- });
5229
- ipcMain.handle('exa:clear', () => {
5230
- settings.exaKey = '';
5231
- saveSettings();
5232
- setExaKey(null);
5233
- return { ok: true, set: false, masked: '' };
5234
5318
  });
5235
5319
 
5236
5320
  /* #TinyFish: anahtar girilirse web_search zincirinin BAŞINDA kullanılır */
@@ -6211,8 +6295,11 @@ ipcMain.handle('bots:remove', (_e, id) => {
6211
6295
  saveSettings();
6212
6296
  syncWhitelist();
6213
6297
  log.info('main', `bot silindi: ${id} — bağlı numaralar botsuz (beast'e düşer)`);
6298
+ /* bot silme sonrası sistem KİTLENİYOR (oturum/DM/servis referansları) →
6299
+ güvenli yol: bot kaydı silindikten sonra uygulamayı temiz yeniden başlat */
6300
+ if (String(id || '') !== 'beast') scheduleAppRestart(1200);
6214
6301
  }
6215
- return { ...r, list: r.ok ? botListWithNumbers() : null };
6302
+ return { ...r, list: r.ok ? botListWithNumbers() : null, restarting: r.ok && String(id || '') !== 'beast' };
6216
6303
  });
6217
6304
 
6218
6305
  ipcMain.handle('bots:stats', () => botStats());
package/src/preload.js CHANGED
@@ -70,9 +70,12 @@ 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
- exaGet: () => ipcRenderer.invoke('exa:get'),
74
- exaSet: (key) => ipcRenderer.invoke('exa:set', key),
75
- exaClear: () => ipcRenderer.invoke('exa:clear'),
73
+ obscuraGet: () => ipcRenderer.invoke('obscura:get'),
74
+ obscuraInstall: () => ipcRenderer.invoke('obscura:install'),
75
+ obscuraInstallState: () => ipcRenderer.invoke('obscura:installState'),
76
+ obscuraSetEnabled: (v) => ipcRenderer.invoke('obscura:setEnabled', v),
77
+ searchOrderGet: () => ipcRenderer.invoke('searchorder:get'),
78
+ searchOrderSet: (chain) => ipcRenderer.invoke('searchorder:set', chain),
76
79
  tinyfishGet: () => ipcRenderer.invoke('tinyfish:get'),
77
80
  tinyfishSet: (key) => ipcRenderer.invoke('tinyfish:set', key),
78
81
  tinyfishClear: () => ipcRenderer.invoke('tinyfish:clear'),
@@ -18,6 +18,9 @@
18
18
  tipThink: 'Düşünme (reasoning) seviyesi',
19
19
  tipPickCfg: 'Picker\u2019da görünecek modeller',
20
20
  tipRail: 'Paralel Ajan Konsolu',
21
+ tipNet: 'İnternet durumu',
22
+ tip_net_online: 'İnternet: bağlı',
23
+ tip_net_offline: 'İnternet: yok — bağlantı dönünce işler otomatik sürer',
21
24
  tipWatch: 'İzleyiciler (Watchers)',
22
25
  tipCron: 'Cron Görevler',
23
26
  tip_light: 'Açık tema',
@@ -111,10 +114,10 @@
111
114
  lim_saved_toast: 'Limitler kaydedildi',
112
115
  lim_unlimited: 'limitsiz',
113
116
  tf_h2: 'TinyFish Arama',
114
- tf_sub: 'Ücretsiz, hızlı web arama API\u2019si. Anahtar girilirse web_search zincirinin EN BAŞINDA kullanılır; sonuç yoksa eski zincir (tarayıcı → çoklu-motor → Exa) devam eder.',
117
+ tf_sub: 'Ücretsiz, hızlı web arama API\u2019si. Anahtar girilirse zincirde KENDİ SIRASINDA kullanılır; anahtar yoksa motor otomatik atlanır.',
115
118
  tf_key_label: 'TinyFish API anahtarı (agent.tinyfish.ai/api-keys)',
116
119
  tf_status_set: 'TinyFish aktif — kayıtlı anahtar: ',
117
- tf_status_unset: 'TinyFish pasif — eski arama zinciri kullanılıyor.',
120
+ tf_status_unset: 'TinyFish pasif — zincir sıradaki motorla devam eder.',
118
121
  tf_empty_toast: 'Anahtar boş — mevcut anahtar korundu',
119
122
  tf_saved_toast: 'TinyFish anahtarı kaydedildi',
120
123
  tf_cleared_toast: 'TinyFish anahtarı silindi',
@@ -260,18 +263,37 @@
260
263
  ws_calls: 'çağrı',
261
264
  ws_token: 'token',
262
265
  ws_h2: 'Web Arama',
263
- ws_sub: 'Arama zinciri: 1) dahili tarayıcı (direk Google) 2) python çoklu-motor (ddgs / DDG+Bing+Mojeek) 3) Exa — otomatik. Exa anahtarı yalnızca son çare olarak kullanılır.',
264
- ws_exa_label: 'Exa API anahtarı (exa.ai)',
265
- ws_exa_ph: 'masaüstü görünümde maskeli',
266
+ 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.',
267
+ oc_h2: 'Obscura (Stealth Tarayıcı)',
268
+ 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.',
269
+ oc_status_ok: 'Obscura kurulu ve hazır.',
270
+ oc_status_missing: 'Obscura kurulu değil — ilk açılışta otomatik indirilir (≈74 MB).',
271
+ oc_install: 'Kur / Güncelle',
272
+ oc_install_running: 'Kuruluyor…',
273
+ oc_installing: 'İndiriliyor… (≈74 MB, bağlantıya göre birkaç dakika)',
274
+ oc_phase_prep: 'Hazırlanıyor',
275
+ oc_phase_download: 'İndiriliyor',
276
+ oc_phase_extract: 'Açılıyor',
277
+ oc_phase_verify: 'Doğrulanıyor',
278
+ oc_phase_done: 'Tamamlandı',
279
+ oc_bg_note: 'Kurulum arka planda sürüyor — ayarlardan çıkıp dönsen bile ilerleme burada devam eder.',
280
+ oc_installed_toast: 'Obscura kuruldu',
281
+ oc_install_fail: 'Kurulamadı: ',
282
+ oc_enabled: 'Obscura arama zincirinde aktif',
283
+ oc_on_toast: 'Obscura aktif',
284
+ oc_off_toast: 'Obscura pasif',
285
+ so_h2: 'Arama Sırası',
286
+ 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.',
287
+ so_engine_browser: 'Dahili Tarayıcı (Google)',
288
+ so_engine_obscura: 'Obscura (Stealth → DuckDuckGo)',
289
+ so_engine_tinyfish: 'TinyFish API (anahtar gerekir)',
290
+ so_engine_python: 'Python Çoklu-Motor (ddgs/DDG/Bing/Mojeek)',
291
+ so_saved_toast: 'Arama sırası kaydedildi',
266
292
  ws_save: 'Kaydet',
267
293
  ws_clear: 'Anahtarı Sil',
268
294
  ws_note: 'Anahtar maskeli tutulur; yenisini yazıp kaydettiğinde eskisinin yerine geçer. Boş kaydet: mevcut anahtar korunur.',
269
- ws_status_set: 'Exa aktif — kayıtlı anahtar: ',
270
- ws_status_unset: 'Exa yapılandırılmadı — ücretsiz python çoklu-motor arama kullanılıyor.',
271
295
  ws_empty_toast: 'Anahtar boş — mevcut anahtar korundu',
272
- ws_saved_toast: 'Exa anahtarı kaydedildi',
273
296
  ws_fail_toast: 'Kaydedilemedi',
274
- ws_cleared_toast: 'Exa anahtarı silindi',
275
297
  ag_h2: 'Paralel Ajanlar',
276
298
  ag_sub: 'CEO\u2019nun devrettiği arka plan işleri — canlı izleme',
277
299
  ag_ceo_label: 'CEO modu — konuşan ajan iş yapmaz, sadece emir verir',
@@ -300,12 +322,11 @@
300
322
  us_where_on_toast: 'Açılış özeti açık',
301
323
  us_where_off_toast: 'Açılış özeti kapandı',
302
324
  ws_h2: 'Web Arama',
303
- ws_exa_label: 'Exa API anahtarı (exa.ai)',
325
+ oc_status_ok: 'Obscura kurulu ve hazır.',
326
+ oc_status_missing: 'Obscura kurulu değil — ilk açılışta otomatik indirilir.',
304
327
  ws_save: 'Kaydet',
305
328
  ws_clear: 'Anahtarı Sil',
306
329
  ws_key_note: 'Anahtar maskeli tutulur; yenisini yazıp kaydettiğinde eskisinin yerine geçer. Boş kaydet: mevcut anahtar korunur.',
307
- ws_active: 'Exa aktif — kayıtlı anahtar: ',
308
- ws_notcfg: 'Exa yapılandırılmadı — ücretsiz python çoklu-motor arama kullanılıyor.',
309
330
  ws_empty: 'Anahtar boş — mevcut anahtar korundu',
310
331
  // terminal & tarayıcı & mini
311
332
  term_clear: 'Temizle',
@@ -434,8 +455,9 @@
434
455
  bot_delete: 'Botu Sil',
435
456
  bot_confirm_del: '"${n}" silinsin mi? Bağlı numaralar botsuz duruma düşer (Beast\u2019e yönlendirilir).',
436
457
  bot_deleted: 'Bot silindi',
458
+ bot_deleted_restart: 'Bot silindi — sistem temiz yeniden başlatılıyor…',
437
459
  bot_max_toast: 'En fazla ${n} bot olabilir (1 admin + 4 müşteri)',
438
- bot_name_ph: 'Bot adı (örn: Muhasebe Botu)',
460
+ bot_name_ph: 'Bot adı (harfle başlar, örn: Muhasebe Botu)',
439
461
  bot_icon_ph: 'İkon seç (emoji kopyala-yapıştır)',
440
462
  bot_created: 'Bot oluşturuldu:',
441
463
  bot_mem_sub: 'Bu botun KENDİ hafızası — SOUL (kişilik) · USER (kullanıcı bilgisi) · MEMORY (hafıza kayıtları). Diğer botlardan tamamen izoledir.',
@@ -465,6 +487,7 @@
465
487
  bot_create: 'Botu Oluştur',
466
488
  bot_cancel: 'Vazgeç',
467
489
  bot_name_req: 'Bot adı zorunlu',
490
+ bot_name_letter: 'Bot adı HARF ile başlamalı — ilk karakter harf olmalı',
468
491
  p_preset: 'Hazır sağlayıcı (endpoint otomatik)',
469
492
  p_preset_custom: '— elle gir —',
470
493
  p_preset_hint: 'Hazır seçersen adres otomatik dolar; sadece API key girip modelleri çek.',
@@ -549,6 +572,9 @@
549
572
  tipThink: 'Reasoning level',
550
573
  tipPickCfg: 'Models shown in picker',
551
574
  tipRail: 'Parallel Agents Console',
575
+ tipNet: 'Internet status',
576
+ tip_net_online: 'Internet: connected',
577
+ tip_net_offline: 'Internet: offline — work resumes automatically once the connection returns',
552
578
  tipWatch: 'Watchers',
553
579
  tipCron: 'Cron Jobs',
554
580
  tip_light: 'Light theme',
@@ -641,10 +667,10 @@
641
667
  lim_saved_toast: 'Limits saved',
642
668
  lim_unlimited: 'unlimited',
643
669
  tf_h2: 'TinyFish Search',
644
- tf_sub: 'Free, fast web search API. If a key is set it is used at the TOP of the web_search chain; otherwise the old chain (browser → multi-engine → Exa) continues.',
670
+ 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.',
645
671
  tf_key_label: 'TinyFish API key (agent.tinyfish.ai/api-keys)',
646
672
  tf_status_set: 'TinyFish active — saved key: ',
647
- tf_status_unset: 'TinyFish off — using the old search chain.',
673
+ tf_status_unset: 'TinyFish off — the chain continues with the next engine.',
648
674
  tf_empty_toast: 'Key empty — current key kept',
649
675
  tf_saved_toast: 'TinyFish key saved',
650
676
  tf_cleared_toast: 'TinyFish key deleted',
@@ -790,18 +816,37 @@
790
816
  ws_calls: 'calls',
791
817
  ws_token: 'tokens',
792
818
  ws_h2: 'Web Search',
793
- ws_sub: 'Search chain: 1) built-in browser (direct Google) 2) python multi-engine (ddgs / DDG+Bing+Mojeek) 3) Exa — automatic. The Exa key is only used as a last resort.',
794
- ws_exa_label: 'Exa API key (exa.ai)',
795
- ws_exa_ph: 'masked on desktop view',
819
+ 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.',
820
+ oc_h2: 'Obscura (Stealth Browser)',
821
+ 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.',
822
+ oc_status_ok: 'Obscura is installed and ready.',
823
+ oc_status_missing: 'Obscura is not installed — it is auto-downloaded at first launch (≈74 MB).',
824
+ oc_install: 'Install / Update',
825
+ oc_install_running: 'Installing…',
826
+ oc_installing: 'Downloading… (≈74 MB, may take a few minutes)',
827
+ oc_phase_prep: 'Preparing',
828
+ oc_phase_download: 'Downloading',
829
+ oc_phase_extract: 'Extracting',
830
+ oc_phase_verify: 'Verifying',
831
+ oc_phase_done: 'Done',
832
+ oc_bg_note: 'Installation continues in the background — progress stays here even if you leave the settings.',
833
+ oc_installed_toast: 'Obscura installed',
834
+ oc_install_fail: 'Install failed: ',
835
+ oc_enabled: 'Obscura active in the search chain',
836
+ oc_on_toast: 'Obscura enabled',
837
+ oc_off_toast: 'Obscura disabled',
838
+ so_h2: 'Search Order',
839
+ 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.',
840
+ so_engine_browser: 'Built-in Browser (Google)',
841
+ so_engine_obscura: 'Obscura (Stealth → DuckDuckGo)',
842
+ so_engine_tinyfish: 'TinyFish API (needs a key)',
843
+ so_engine_python: 'Python Multi-Engine (ddgs/DDG/Bing/Mojeek)',
844
+ so_saved_toast: 'Search order saved',
796
845
  ws_save: 'Save',
797
846
  ws_clear: 'Delete Key',
798
847
  ws_note: 'The key is kept masked; saving a new one replaces the old. Saving empty keeps the current key.',
799
- ws_status_set: 'Exa active — saved key: ',
800
- ws_status_unset: 'Exa not configured — using free python multi-engine search.',
801
848
  ws_empty_toast: 'Key empty — current key kept',
802
- ws_saved_toast: 'Exa key saved',
803
849
  ws_fail_toast: 'Could not save',
804
- ws_cleared_toast: 'Exa key deleted',
805
850
  ag_h2: 'Parallel Agents',
806
851
  ag_sub: 'Background tasks delegated by the CEO — live monitoring',
807
852
  ag_ceo_label: 'CEO mode — the talking agent does no work, only gives orders',
@@ -892,12 +937,11 @@
892
937
  us_where_on_toast: 'Startup summary on',
893
938
  us_where_off_toast: 'Startup summary off',
894
939
  ws_h2: 'Web Search',
895
- ws_exa_label: 'Exa API key (exa.ai)',
940
+ oc_status_ok: 'Obscura is installed and ready.',
941
+ oc_status_missing: 'Obscura is not installed — it is auto-downloaded at first launch.',
896
942
  ws_save: 'Save',
897
943
  ws_clear: 'Delete Key',
898
944
  ws_key_note: 'Key is kept masked; saving a new one replaces the old. Save empty: existing key kept.',
899
- ws_active: 'Exa active — saved key: ',
900
- ws_notcfg: 'Exa not configured — free python multi-engine search is used.',
901
945
  ws_empty: 'Key empty — existing key kept',
902
946
  term_clear: 'Clear',
903
947
  term_ph: 'type a command, press Enter…',
@@ -1022,8 +1066,9 @@
1022
1066
  bot_delete: 'Delete Bot',
1023
1067
  bot_confirm_del: 'Delete "${n}"? Linked numbers become botless (routed to Beast).',
1024
1068
  bot_deleted: 'Bot deleted',
1069
+ bot_deleted_restart: 'Bot deleted — system is restarting cleanly…',
1025
1070
  bot_max_toast: 'Maximum ${n} bots (1 admin + 4 customer)',
1026
- bot_name_ph: 'Bot name (e.g. Accounting Bot)',
1071
+ bot_name_ph: 'Bot name (starts with a letter, e.g. Accounting Bot)',
1027
1072
  bot_icon_ph: 'Pick an icon (emoji)',
1028
1073
  bot_created: 'Bot created:',
1029
1074
  bot_mem_sub: 'This bot\u2019s OWN memory — SOUL (personality) · USER (user info) · MEMORY (memory entries). Fully isolated from other bots.',
@@ -1053,6 +1098,7 @@
1053
1098
  bot_create: 'Create Bot',
1054
1099
  bot_cancel: 'Cancel',
1055
1100
  bot_name_req: 'Bot name is required',
1101
+ bot_name_letter: 'Bot name must START with a letter — first character must be a letter',
1056
1102
  p_preset: 'Built-in provider (endpoint auto-filled)',
1057
1103
  p_preset_custom: '— manual —',
1058
1104
  p_preset_hint: 'Pick a built-in provider to auto-fill the URL; just enter your API key and fetch models.',
@@ -77,6 +77,7 @@
77
77
  <button id="termCBtn" class="dd-btn dd-icon-btn" title="CMD terminali" data-i18n-title="tipTermC">&#x276F;&#xFE0E;</button>
78
78
  <button id="browserBtn" class="dd-btn dd-icon-btn" title="Dahili tarayıcı" data-i18n-title="tipBrowser">&#x29C9;</button>
79
79
  <button id="eyeBtn" class="dd-btn dd-icon-btn" title="Ajan tarayıcısı görünür/gizli — göz açıkken aramalar panelde izlenir, kapalıyken gizli çalışır" data-i18n-title="tipEye"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/><line class="eye-slash" x1="4" y1="4" x2="20" y2="20"/></svg></button>
80
+ <span id="netDot" class="dd-btn dd-icon-btn net-on" title="İnternet: bağlı" data-i18n-title="tipNet"><svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><line x1="12" y1="20" x2="12.01" y2="20"/><line class="net-slash" x1="4" y1="4" x2="20" y2="20"/></svg></span>
80
81
  <div class="drag-spacer"></div>
81
82
  </header>
82
83