beast-agent 2.17.0 → 2.19.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/preload.js CHANGED
@@ -20,6 +20,8 @@ contextBridge.exposeInMainWorld('beast', {
20
20
  agentsList: () => ipcRenderer.invoke('agents:list'),
21
21
  agentsDetail: (id) => ipcRenderer.invoke('agents:detail', id),
22
22
  agentsCancel: (id) => ipcRenderer.invoke('agents:cancel', id),
23
+ agentDmsList: () => ipcRenderer.invoke('agent-dms:list'),
24
+ agentDmsClear: () => ipcRenderer.invoke('agent-dms:clear'),
23
25
  getCeoMode: () => ipcRenderer.invoke('ceo:get'),
24
26
  setCeoMode: (v) => ipcRenderer.invoke('ceo:set', v),
25
27
  listNotes: () => ipcRenderer.invoke('notes:list'),
@@ -106,6 +108,7 @@ contextBridge.exposeInMainWorld('beast', {
106
108
  financeSettings: (patch) => ipcRenderer.invoke('finance:settings', patch),
107
109
  financeTraderStart: () => ipcRenderer.invoke('finance:trader:start'),
108
110
  financeTraderStop: () => ipcRenderer.invoke('finance:trader:stop'),
111
+ financeAgentSpawn: (symbol) => ipcRenderer.invoke('finance:agent:spawn', { symbol }),
109
112
  financeConnect: () => ipcRenderer.invoke('finance:connect'),
110
113
  financeClose: (ticket, volume) => ipcRenderer.invoke('finance:close', { ticket, volume }),
111
114
  financeInstall: () => ipcRenderer.invoke('finance:install'),
@@ -146,6 +149,10 @@ contextBridge.exposeInMainWorld('beast', {
146
149
 
147
150
  watchersToggle: (id) => ipcRenderer.invoke('watchers:toggle', id),
148
151
 
152
+ watchersLogs: () => ipcRenderer.invoke('watchers:logs'),
153
+
154
+ watchersLogsClear: (id) => ipcRenderer.invoke('watchers:logsClear', id),
155
+
149
156
  providerKeys: () => ipcRenderer.invoke('providers:keys'),
150
157
 
151
158
  openPath: (p) => ipcRenderer.invoke('shell:openPath', p),
@@ -18,6 +18,7 @@
18
18
  tipThink: 'Düşünme (reasoning) seviyesi',
19
19
  tipPickCfg: 'Picker\u2019da görünecek modeller',
20
20
  tipRail: 'Paralel Ajan Konsolu',
21
+ tipAgentDm: 'Ajan DM — ajanlar arası konuşma',
21
22
  tipNet: 'İnternet durumu',
22
23
  tip_net_online: 'İnternet: bağlı',
23
24
  tip_net_offline: 'İnternet: yok — bağlantı dönünce işler otomatik sürer',
@@ -61,6 +62,7 @@
61
62
  tipLang: 'Dil değiştir / Switch language',
62
63
  // kenar çubuğu & paneller
63
64
  labelParallel: 'Paralel Ajanlar',
65
+ labelAgentDm: 'Ajan DM',
64
66
  set_title: 'AYARLAR',
65
67
  tab_lang: 'Dil',
66
68
  lang_sub: 'Arayüz dili — uygulama dili ve sesli komut dili buradan değişir',
@@ -542,6 +544,14 @@
542
544
  bot_no_watchers: 'Bu bota ait izleyici yok.',
543
545
  w_pane: 'İZLEYİCİLER',
544
546
  w_empty: 'Kayıtlı izleyici yok.',
547
+ tipWatchLog: 'İzleyici loglarını gör',
548
+ w_log_title: 'İzleyici Logları',
549
+ w_log_clear: 'Seçili izleyicinin loglarını temizle',
550
+ w_log_cleared: 'Loglar temizlendi',
551
+ w_log_empty: 'Bu izleyici için henüz log kaydı yok.',
552
+ w_log_ev_ok: 'kontrol',
553
+ w_log_ev_trigger: 'tetik',
554
+ w_log_ev_error: 'hata',
545
555
  bot_stats_sub: 'Bu botun kullanım özeti.',
546
556
  bot_stat_numbers: 'Numara',
547
557
  bot_stat_sessions: 'Sohbet',
@@ -655,6 +665,7 @@
655
665
  tipThink: 'Reasoning level',
656
666
  tipPickCfg: 'Models shown in picker',
657
667
  tipRail: 'Parallel Agents Console',
668
+ tipAgentDm: 'Agent DM — agent-to-agent chat',
658
669
  tipNet: 'Internet status',
659
670
  tip_net_online: 'Internet: connected',
660
671
  tip_net_offline: 'Internet: offline — work resumes automatically once the connection returns',
@@ -696,6 +707,7 @@
696
707
  tipThemeLight: 'Light theme',
697
708
  tipLang: 'Dil değiştir / Switch language',
698
709
  labelParallel: 'Parallel Agents',
710
+ labelAgentDm: 'Agent DM',
699
711
  set_title: 'SETTINGS',
700
712
  tab_lang: 'Language',
701
713
  lang_sub: 'Interface language — app UI and voice command language',
@@ -1038,6 +1050,14 @@
1038
1050
  cr_toggle_pause: 'Pause',
1039
1051
  w_empty: 'No saved watchers — add from the form above.',
1040
1052
  w_pane: 'WATCHERS',
1053
+ tipWatchLog: 'View watcher logs',
1054
+ w_log_title: 'Watcher Logs',
1055
+ w_log_clear: 'Clear selected watcher logs',
1056
+ w_log_cleared: 'Logs cleared',
1057
+ w_log_empty: 'No log entries for this watcher yet.',
1058
+ w_log_ev_ok: 'check',
1059
+ w_log_ev_trigger: 'trigger',
1060
+ w_log_ev_error: 'error',
1041
1061
  wa_vazgec: 'Cancel',
1042
1062
  wa_kaydet: 'save',
1043
1063
  wa_updated: 'Updated',
@@ -17,7 +17,8 @@
17
17
  <div id="watchPaneHead">
18
18
  <span id="watchPaneTitle" data-i18n="w_pane">İZLEYİCİLER</span>
19
19
  <span class="file-spacer"></span>
20
- <button id="watchPaneOpen" title="İzleyicileri yönet">+</button>
20
+ <button id="watchPaneLog" title="İzleyici loglarını gör" data-i18n-title="tipWatchLog"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/></svg></button>
21
+ <button id="watchPaneOpen" title="İzleyicileri yönet" data-i18n-title="tipWatch">+</button>
21
22
  </div>
22
23
  <div id="watchPaneList"></div>
23
24
  </div>
@@ -93,6 +94,7 @@
93
94
  <button id="finBtn" class="dd-btn dd-icon-btn" title="Beast Finance — MT5 trade paneli (bilgisayardaki MetaTrader 5 ile çalışır)"><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="M3 3v18h18"/><path d="M7 15l4-5 3 3 5-7"/></svg></button>
94
95
  <button id="sbBtn" class="dd-btn dd-icon-btn" title="Beast Sandbox — GitHub reposu indir &amp; içinde çalış" data-i18n-title="tipSandbox"><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="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg></button>
95
96
  <button id="railBtn" class="dd-btn dd-icon-btn" title="Paralel Ajan Konsolu" data-i18n-title="tipRail">&#x25A4;</button>
97
+ <button id="dmBtn" class="dd-btn dd-icon-btn" title="Ajan DM — ajanlar arası konuşma" data-i18n-title="tipAgentDm">&#x2709;&#xFE0E;</button>
96
98
  <button id="watchBtn" class="dd-btn dd-icon-btn" title="İzleyiciler (Watchers)" data-i18n-title="tipWatch">&#x25CE;</button>
97
99
  <button id="cronBtn" class="dd-btn dd-icon-btn" title="Cron Görevler" data-i18n-title="tipCron">&#x23F1;&#xFE0E;</button>
98
100
  <button id="termCBtn" class="dd-btn dd-icon-btn" title="CMD terminali" data-i18n-title="tipTermC">&#x276F;&#xFE0E;</button>
@@ -345,6 +347,19 @@
345
347
  <span class="foot-label2" data-i18n="labelParallel">Paralel Ajanlar</span>
346
348
  </div>
347
349
  </aside>
350
+
351
+ <aside id="dmRail" class="dm-hidden">
352
+ <div id="dmRailHead">
353
+ <span id="dmRailTitle" data-i18n="labelAgentDm">AJAN DM</span>
354
+ <button id="dmClear" title="DM geçmişini temizle" aria-label="DM geçmişini temizle">
355
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#111" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg>
356
+ </button>
357
+ </div>
358
+ <div id="dmRailList"></div>
359
+ <div id="sideFoot2">
360
+ <span class="foot-label2" data-i18n="labelAgentDm">Ajan DM</span>
361
+ </div>
362
+ </aside>
348
363
  </div>
349
364
 
350
365
  <div id="settingsOverlay" hidden>
@@ -528,6 +543,22 @@
528
543
  </div>
529
544
  </div>
530
545
 
546
+ <div id="watchLogOverlay" class="mini-overlay" hidden>
547
+ <div class="mini-dialog">
548
+ <div class="mini-head">
549
+ <span class="mini-title" data-i18n="w_log_title">İzleyici Logları</span>
550
+ <button class="mini-close" id="watchLogClose" title="Kapat" data-i18n-title="btn_close">&#x00D7;</button>
551
+ </div>
552
+ <div class="mini-body">
553
+ <div id="watchLogBar">
554
+ <select id="watchLogSel" class="mini-select"></select>
555
+ <button id="watchLogClear" class="mr-btn" title="Seçili izleyicinin loglarını temizle" data-i18n-title="w_log_clear"><svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg></button>
556
+ </div>
557
+ <div id="watchLogList" class="mini-list"></div>
558
+ </div>
559
+ </div>
560
+ </div>
561
+
531
562
  <div id="finSymOverlay" class="mini-overlay" hidden>
532
563
  <div class="mini-dialog fin-sym-dialog">
533
564
  <div class="mini-head">
@@ -42,9 +42,13 @@ const els = {
42
42
  netDot: $('#netDot'),
43
43
  railBtn: $('#railBtn'),
44
44
  railClear: $('#railClear'),
45
+ dmBtn: $('#dmBtn'),
46
+ dmRailList: $('#dmRailList'),
47
+ dmClear: $('#dmClear'),
45
48
  watchBtn: $('#watchBtn'),
46
49
  watchPaneList: $('#watchPaneList'),
47
50
  watchPaneOpen: $('#watchPaneOpen'),
51
+ watchPaneLog: $('#watchPaneLog'),
48
52
  cronBtn: $('#cronBtn'),
49
53
  watchOverlay: $('#watchOverlay'),
50
54
  cronOverlay: $('#cronOverlay'),
@@ -52,6 +56,11 @@ const els = {
52
56
  cronClose: $('#cronClose'),
53
57
  watchList: $('#watchList'),
54
58
  cronModalList: $('#cronModalList'),
59
+ watchLogOverlay: $('#watchLogOverlay'),
60
+ watchLogClose: $('#watchLogClose'),
61
+ watchLogSel: $('#watchLogSel'),
62
+ watchLogClear: $('#watchLogClear'),
63
+ watchLogList: $('#watchLogList'),
55
64
  browserBar: $('#browserBar'),
56
65
  bbBack: $('#bbBack'),
57
66
  bbFwd: $('#bbFwd'),
@@ -4993,6 +5002,113 @@ async function loadRailChat(id) {
4993
5002
  }
4994
5003
  }
4995
5004
 
5005
+ /* ---------------- AJAN DM sütunu (ajanlar arası konuşma) ----------------
5006
+ rail ile aynı mimari: sağda ikinci sütun, dmBtn ile açılır/kapanır.
5007
+ DM'ler ajanların agent_dm araç çağrılarından gelir; çiftlere gruplanır. */
5008
+
5009
+ const dmState = {
5010
+ dms: [], // { at, from, fromTitle, to, toTitle, text }
5011
+ open: new Set(), // açık thread key'leri
5012
+ };
5013
+
5014
+ function dmPairOf(dm) {
5015
+ const a = String(dm.from || '');
5016
+ const b = String(dm.to || '');
5017
+ return a < b ? a + '|' + b : b + '|' + a;
5018
+ }
5019
+
5020
+ function dmPairTitle(dm) {
5021
+ const a = String(dm.fromTitle || 'Ajan');
5022
+ const b = String(dm.toTitle || 'Ajan');
5023
+ return a < b ? a + ' ↔ ' + b : b + ' ↔ ' + a;
5024
+ }
5025
+
5026
+ function dmTime(iso) {
5027
+ const d = new Date(iso || '');
5028
+ if (!d.getTime()) return '';
5029
+ return d.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
5030
+ }
5031
+
5032
+ function renderDmRail() {
5033
+ const list = els.dmRailList;
5034
+ if (!list) return;
5035
+ if (!dmState.dms.length) {
5036
+ list.innerHTML = '<div class="dm-empty">Ajanlar arasında DM yok.<br>Ajanlar agent_dm aracıyla birbirine mesaj attıkça burada görünür.</div>';
5037
+ return;
5038
+ }
5039
+ /* çiftlere grupla (kronolojik) — en yeni thread üstte */
5040
+ const threads = new Map();
5041
+ for (const dm of dmState.dms) {
5042
+ const key = dmPairOf(dm);
5043
+ if (!threads.has(key)) threads.set(key, []);
5044
+ threads.get(key).push(dm);
5045
+ }
5046
+ const rows = [...threads.entries()].sort(
5047
+ (x, y) => new Date(y[1][y[1].length - 1].at) - new Date(x[1][x[1].length - 1].at)
5048
+ );
5049
+ list.innerHTML = '';
5050
+ for (const [key, msgs] of rows) {
5051
+ const open = dmState.open.has(key);
5052
+ const last = msgs[msgs.length - 1];
5053
+ const el = document.createElement('div');
5054
+ el.className = 'dmt' + (open ? ' open' : '');
5055
+ el.innerHTML =
5056
+ '<div class="dmt-head">' +
5057
+ `<span class="dmt-title">${escapeHtml(dmPairTitle(last))}</span>` +
5058
+ `<span class="dmt-count">${msgs.length}</span>` +
5059
+ '</div>' +
5060
+ `<div class="dmt-last">${escapeHtml(String(last.text || '').replace(/\s+/g, ' ').slice(0, 90))} · ${dmTime(last.at)}</div>` +
5061
+ '<div class="dmt-msgs">' +
5062
+ msgs
5063
+ .map(
5064
+ (m) =>
5065
+ `<div class="dmt-msg"><span class="dm-who">${escapeHtml(String(m.fromTitle || 'Ajan'))} → ${escapeHtml(String(m.toTitle || 'Ajan'))}</span>` +
5066
+ `${escapeHtml(String(m.text || ''))}<span class="dm-at">${dmTime(m.at)}</span></div>`
5067
+ )
5068
+ .join('') +
5069
+ '</div>';
5070
+ el.addEventListener('click', () => {
5071
+ if (dmState.open.has(key)) dmState.open.delete(key);
5072
+ else dmState.open.add(key);
5073
+ renderDmRail();
5074
+ const again = els.dmRailList.querySelector('.dmt.open .dmt-msgs');
5075
+ if (again) again.scrollTop = again.scrollHeight;
5076
+ });
5077
+ list.appendChild(el);
5078
+ }
5079
+ }
5080
+
5081
+ async function refreshDmRail() {
5082
+ try {
5083
+ const dms = await beast.agentDmsList();
5084
+ if (Array.isArray(dms)) dmState.dms = dms;
5085
+ } catch {}
5086
+ renderDmRail();
5087
+ }
5088
+
5089
+ function toggleDmRail(hide) {
5090
+ const rail = $('#dmRail');
5091
+ if (!rail) return;
5092
+ rail.classList.toggle('dm-hidden', !!hide);
5093
+ if (els.dmBtn) els.dmBtn.classList.toggle('on', !hide);
5094
+ if (!hide) refreshDmRail();
5095
+ }
5096
+
5097
+ if (els.dmBtn) {
5098
+ els.dmBtn.addEventListener('click', () => {
5099
+ toggleDmRail(!$('#dmRail').classList.contains('dm-hidden'));
5100
+ });
5101
+ }
5102
+ if (els.dmClear) {
5103
+ els.dmClear.addEventListener('click', async () => {
5104
+ if (!(await uiConfirm('TÜM ajan DM geçmişi silinsin mi?', 'Sil'))) return;
5105
+ try { await beast.agentDmsClear(); } catch {}
5106
+ dmState.dms = [];
5107
+ renderDmRail();
5108
+ toast('Ajan DM geçmişi silindi');
5109
+ });
5110
+ }
5111
+
4996
5112
  function renderAgentsPane() {
4997
5113
  const pane = $('#tab-agents');
4998
5114
  if (!pane) return;
@@ -5137,6 +5253,20 @@ function onEvent(ev) {
5137
5253
  scheduleAgentsRender();
5138
5254
  return;
5139
5255
  }
5256
+ /* AJAN DM: ajanlar arası mesaj — panel açıksa canlı düşer */
5257
+ if (ev.type === 'agent-dm') {
5258
+ dmState.dms.push({
5259
+ at: ev.at,
5260
+ from: ev.from,
5261
+ fromTitle: ev.fromTitle,
5262
+ to: ev.to,
5263
+ toTitle: ev.toTitle,
5264
+ text: ev.text,
5265
+ });
5266
+ if (dmState.dms.length > 400) dmState.dms.splice(0, dmState.dms.length - 400);
5267
+ if ($('#dmRail') && !$('#dmRail').classList.contains('dm-hidden')) renderDmRail();
5268
+ return;
5269
+ }
5140
5270
  /* OFFLINE MESAJ KUYRUĞU: bağlantı + kuyruk olayları (sessionId filtresinden önce) */
5141
5271
  if (ev.type === 'net' || ev.type === 'netQueue') { onNetEvent(ev); return; }
5142
5272
  /* Sandbox klonlama progress'i (sessionId'siz global olay) */
@@ -5611,6 +5741,11 @@ async function renderWatchersPane(force) {
5611
5741
  `</div></div>`;
5612
5742
  const btns = document.createElement('div');
5613
5743
  btns.className = 'wp-btns';
5744
+ const lg = document.createElement('button');
5745
+ lg.className = 'wp-btn';
5746
+ lg.innerHTML = '<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/></svg>';
5747
+ lg.title = _t('w_log_title');
5748
+ lg.addEventListener('click', (e) => { e.stopPropagation(); openWatchLogModal(w.id); });
5614
5749
  const t = document.createElement('button');
5615
5750
  t.className = 'wp-btn';
5616
5751
  t.textContent = w.enabled ? '❚❚' : '▶';
@@ -5632,7 +5767,7 @@ async function renderWatchersPane(force) {
5632
5767
  renderWatchersPane(true);
5633
5768
  renderWatchersModal(true);
5634
5769
  });
5635
- btns.append(t, d);
5770
+ btns.append(lg, t, d);
5636
5771
  row.appendChild(btns);
5637
5772
  row.addEventListener('click', () => openWatchModal());
5638
5773
  list.appendChild(row);
@@ -5669,6 +5804,11 @@ async function renderWatchersModal(force) {
5669
5804
  `</div></div>`;
5670
5805
  const btns = document.createElement('div');
5671
5806
  btns.style.cssText = 'display:flex;gap:6px;flex:none';
5807
+ const lg = document.createElement('button');
5808
+ lg.className = 'mr-btn';
5809
+ lg.innerHTML = '<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/></svg>';
5810
+ lg.title = _t('w_log_title');
5811
+ lg.addEventListener('click', () => openWatchLogModal(w.id));
5672
5812
  const t = document.createElement('button');
5673
5813
  t.className = 'mr-btn';
5674
5814
  t.textContent = w.enabled ? '❚❚' : '▶';
@@ -5679,12 +5819,85 @@ async function renderWatchersModal(force) {
5679
5819
  d.textContent = '×';
5680
5820
  d.title = _t('cr_del');
5681
5821
  d.addEventListener('click', async () => { await beast.watchersRemove(w.id); renderWatchersModal(); });
5682
- btns.append(t, d);
5822
+ btns.append(lg, t, d);
5683
5823
  row.appendChild(btns);
5684
5824
  els.watchList.appendChild(row);
5685
5825
  }
5686
5826
  }
5687
5827
 
5828
+ /* ---------- İZLEYİCİ LOG MODALI ----------
5829
+ Her izleyicinin logu AYRI tutulur (watcher-logs.json) ve AYRI gösterilir:
5830
+ üstteki seçiciyle izleyici seç → yalnız onun denetim kayıtları listelenir. */
5831
+ let watchLogSelId = '';
5832
+ let lastWatchLogKey = '';
5833
+ let lastWatchLogSelKey = '';
5834
+
5835
+ async function renderWatchLogModal(force) {
5836
+ if (!els.watchLogOverlay) return;
5837
+ let rows = []; try { rows = (await beast.watchersList()) || []; } catch {}
5838
+ let logsMap = {}; try { logsMap = (await beast.watchersLogs()) || {}; } catch {}
5839
+ /* seçim: yok olan seçilmişse ilk izleyiciye düş */
5840
+ if (!rows.some((w) => w.id === watchLogSelId)) watchLogSelId = rows.length ? rows[0].id : '';
5841
+ /* seçici — izleyici listesi değişmediyse DOM'a dokunma (odak korunur) */
5842
+ const selKey = JSON.stringify(rows.map((w) => [w.id, w.name, w.enabled]));
5843
+ if (selKey !== lastWatchLogSelKey) {
5844
+ lastWatchLogSelKey = selKey;
5845
+ els.watchLogSel.innerHTML = '';
5846
+ if (!rows.length) {
5847
+ const o = document.createElement('option');
5848
+ o.textContent = _t('w_empty');
5849
+ o.value = '';
5850
+ els.watchLogSel.appendChild(o);
5851
+ } else {
5852
+ for (const w of rows) {
5853
+ const o = document.createElement('option');
5854
+ o.value = w.id;
5855
+ o.textContent = (w.enabled ? '' : '⏸ ') + w.name;
5856
+ els.watchLogSel.appendChild(o);
5857
+ }
5858
+ }
5859
+ els.watchLogSel.value = watchLogSelId;
5860
+ }
5861
+ /* log satırları — yalnız seçili izleyicinin kayıtları */
5862
+ const entries = watchLogSelId ? (logsMap[watchLogSelId] || []) : [];
5863
+ const key = JSON.stringify([watchLogSelId, selKey, entries]);
5864
+ if (!force && key === lastWatchLogKey) return;
5865
+ lastWatchLogKey = key;
5866
+ els.watchLogList.innerHTML = '';
5867
+ if (!watchLogSelId || !entries.length) {
5868
+ els.watchLogList.innerHTML = '<div class="mini-empty">' + _t('w_log_empty') + '</div>';
5869
+ return;
5870
+ }
5871
+ const evBadge = { ok: ['ok', _t('w_log_ev_ok')], trigger: ['trigger', _t('w_log_ev_trigger')], error: ['error', _t('w_log_ev_error')] };
5872
+ for (const en of entries) {
5873
+ const [cls, label] = evBadge[en.ev] || ['ok', en.ev];
5874
+ const row = document.createElement('div');
5875
+ row.className = 'wl-row';
5876
+ row.innerHTML =
5877
+ `<span class="wl-time">${escapeHtml(fmtWhen(en.ts))}</span>` +
5878
+ `<span class="wl-badge ${cls}">${escapeHtml(label)}</span>` +
5879
+ `<span class="wl-main">` +
5880
+ (en.ev === 'error'
5881
+ ? `<span class="wl-err">${escapeHtml(String(en.error || '').slice(0, 120))}</span>`
5882
+ : (en.value !== null && en.value !== undefined ? escapeHtml(String(en.value).slice(0, 120)) : '—')) +
5883
+ `</span>`;
5884
+ els.watchLogList.appendChild(row);
5885
+ }
5886
+ }
5887
+
5888
+ function closeWatchLogModal() {
5889
+ toggleMini(els.watchLogOverlay, true);
5890
+ stopMiniRefresh();
5891
+ }
5892
+
5893
+ async function openWatchLogModal(id) {
5894
+ if (id) watchLogSelId = String(id);
5895
+ lastWatchLogSelKey = ''; /* seçici her açılışta TAZE kurulsun */
5896
+ toggleMini(els.watchLogOverlay, false);
5897
+ await renderWatchLogModal(true);
5898
+ startMiniRefresh();
5899
+ }
5900
+
5688
5901
  let lastCronKey = '';
5689
5902
  async function renderCronModal(force) {
5690
5903
  let jobs = [];
@@ -5746,6 +5959,7 @@ function startMiniRefresh() {
5746
5959
  stopMiniRefresh();
5747
5960
  miniRefreshTimer = setInterval(async () => {
5748
5961
  if (els.watchOverlay && !els.watchOverlay.hidden) await renderWatchersModal();
5962
+ else if (els.watchLogOverlay && !els.watchLogOverlay.hidden) await renderWatchLogModal();
5749
5963
  else if (els.cronOverlay && !els.cronOverlay.hidden) await renderCronModal();
5750
5964
  else stopMiniRefresh();
5751
5965
  }, 4000);
@@ -5976,6 +6190,8 @@ const SLASH_COMMANDS = [
5976
6190
  { cmd: '/notify', desc: 'hata maili aç/kapa · /notify on|off' },
5977
6191
  { cmd: '/think ', desc: 'düşünme seviyesi · /think 0-5 (0 kapalı)' },
5978
6192
  { cmd: '/clear', desc: 'oturum geçmişini GERÇEKTEN sil (kod korunur)' },
6193
+ { cmd: '/autodel', desc: 'tüm otomatik hatırlatmaları sil · /autodel all: cron dahil hepsi' },
6194
+ { cmd: '/deltodo', desc: 'todo listesini temizle · /deltodo all: tüm oturumlar' },
5979
6195
  { cmd: '/start', desc: 'durdurulan servisleri devam ettir' },
5980
6196
  { cmd: '/rule ', desc: 'kalıcı kural ekle' },
5981
6197
  { cmd: '/rules', desc: 'kalıcı kuralları listele' },
@@ -6487,12 +6703,29 @@ async function init() {
6487
6703
  /* cron modülü — olay bağlama */
6488
6704
  if (els.watchBtn) els.watchBtn.addEventListener('click', openWatchModal);
6489
6705
  if (els.watchPaneOpen) els.watchPaneOpen.addEventListener('click', openWatchModal);
6706
+ if (els.watchPaneLog) els.watchPaneLog.addEventListener('click', () => openWatchLogModal());
6490
6707
  if (els.cronBtn) els.cronBtn.addEventListener('click', openCronModal);
6491
6708
  if (els.watchClose) els.watchClose.addEventListener('click', closeWatchModal);
6492
6709
  if (els.cronClose) els.cronClose.addEventListener('click', closeCronModal);
6710
+ if (els.watchLogClose) els.watchLogClose.addEventListener('click', closeWatchLogModal);
6711
+ if (els.watchLogSel) els.watchLogSel.addEventListener('change', () => {
6712
+ watchLogSelId = els.watchLogSel.value;
6713
+ lastWatchLogKey = '';
6714
+ renderWatchLogModal(true);
6715
+ });
6716
+ if (els.watchLogClear) els.watchLogClear.addEventListener('click', async () => {
6717
+ if (!watchLogSelId) return;
6718
+ await beast.watchersLogsClear(watchLogSelId);
6719
+ lastWatchLogKey = '';
6720
+ await renderWatchLogModal(true);
6721
+ toast(_t('w_log_cleared'));
6722
+ });
6493
6723
  if (els.watchOverlay) els.watchOverlay.addEventListener('click', (e) => {
6494
6724
  if (e.target === els.watchOverlay) closeWatchModal();
6495
6725
  });
6726
+ if (els.watchLogOverlay) els.watchLogOverlay.addEventListener('click', (e) => {
6727
+ if (e.target === els.watchLogOverlay) closeWatchLogModal();
6728
+ });
6496
6729
  if (els.cronOverlay) els.cronOverlay.addEventListener('click', (e) => {
6497
6730
  if (e.target === els.cronOverlay) closeCronModal();
6498
6731
  });
@@ -6579,6 +6812,7 @@ async function init() {
6579
6812
  document.addEventListener('keydown', (e) => {
6580
6813
  if (e.key === 'Escape') {
6581
6814
  if ($('#botOverlay') && !$('#botOverlay').hidden) { botOverlaySetOpen(false); return; }
6815
+ if (els.watchLogOverlay && !els.watchLogOverlay.hidden) { closeWatchLogModal(); return; }
6582
6816
  if (els.watchOverlay && !els.watchOverlay.hidden) { closeWatchModal(); return; }
6583
6817
  if (els.cronOverlay && !els.cronOverlay.hidden) { closeCronModal(); return; }
6584
6818
  if (!els.settingsOverlay.hidden) closeSettings();
@@ -8234,7 +8468,17 @@ function finRenderSymbols(list) {
8234
8468
  row.innerHTML =
8235
8469
  '<span class="fs-name">' + (s.symbol || '?') + '</span>' +
8236
8470
  '<span class="fs-price ' + cls + '">' + bid.toFixed(digits) + '</span>' +
8237
- '<span class="fs-spread">sp ' + (s.spread != null ? s.spread : '—') + '</span>';
8471
+ '<span class="fs-spread">sp ' + (s.spread != null ? s.spread : '—') + '</span>' +
8472
+ '<button class="fs-agent" title="' + escapeHtml(s.symbol || '') + ' için AYRI finance ajanı başlat (paralel koşar)">&#9654;&#xFE0E;</button>';
8473
+ const spawnBtn = row.querySelector('.fs-agent');
8474
+ if (spawnBtn) {
8475
+ spawnBtn.addEventListener('click', async (e) => {
8476
+ e.stopPropagation();
8477
+ const r = await beast.financeAgentSpawn(s.symbol).catch((err) => ({ ok: false, error: String((err && err.message) || err) }));
8478
+ if (r && r.ok) toast(s.symbol + ' için ajan başladı — Paralel Ajanlar panelinde izle');
8479
+ else toast((r && r.error) || 'Ajan başlatılamadı');
8480
+ });
8481
+ }
8238
8482
  els.finSymList.appendChild(row);
8239
8483
  }
8240
8484
  }