beast-agent 2.19.1 → 2.21.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.
@@ -22,6 +22,16 @@
22
22
  </div>
23
23
  <div id="watchPaneList"></div>
24
24
  </div>
25
+ <div id="toolBox">
26
+ <div id="toolBoxHead">
27
+ <span id="toolBoxTitle">TOOLLAR</span>
28
+ <span class="bot-bar-btns">
29
+ <button id="toolFolderBtn" title="Toollar klasörünü aç (%APPDATA%\beast\tools)"><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="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7z"/></svg></button>
30
+ <button id="toolAdd" title="Yeni tool yaz">+</button>
31
+ </span>
32
+ </div>
33
+ <div id="toolList"></div>
34
+ </div>
25
35
  <div id="botBar">
26
36
  <div id="botBarHead">
27
37
  <span id="botBarTitle" data-i18n="bot_head">BOTLAR</span>
@@ -363,10 +373,54 @@
363
373
  </aside>
364
374
  </div>
365
375
 
376
+ <div id="dmOverlay" hidden>
377
+ <div id="dmDialog">
378
+ <div id="dmDialogHead">
379
+ <span id="dmDialogTitle">AJAN DM — OTURUMLAR</span>
380
+ <span class="bot-bar-btns">
381
+ <button id="dmColumnBtn" title="Yan sütunda canlı akış olarak da göster">&#x25A4;&#xFE0E;</button>
382
+ <button id="dmModalClear" title="Tüm DM geçmişini sil"><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="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>
383
+ <button id="dmModalClose" title="Kapat">&#x2715;&#xFE0E;</button>
384
+ </span>
385
+ </div>
386
+ <div id="dmDialogList"></div>
387
+ <div class="dm-hint">Thread = ajan çifti + konu · gelen/giden hepsi kronolojik</div>
388
+ </div>
389
+ </div>
390
+
391
+ <div id="toolOverlay" hidden>
392
+ <div id="toolDialog">
393
+ <div id="toolDialogHead">
394
+ <span id="toolDialogTitle">YENİ TOOL</span>
395
+ <button id="toolClose" title="Kapat">&#x2715;&#xFE0E;</button>
396
+ </div>
397
+ <div class="tool-form">
398
+ <label>Tool adı (ajan tool__<ad> olarak çağırır)</label>
399
+ <input id="toolName" placeholder="örn: gold_emir" spellcheck="false" autocomplete="off" />
400
+ <label>Açıklama — ajan bu metni görüp ne zaman çağıracağını anlar</label>
401
+ <input id="toolDesc" placeholder="örn: GOLD için özel emir açar (side, lot, sl, tp)" spellcheck="false" autocomplete="off" />
402
+ <label>Parametre şeması (JSON)</label>
403
+ <textarea id="toolParams" rows="6" spellcheck="false" placeholder='{"type":"object","properties":{"side":{"type":"string"},"lot":{"type":"number"}},"required":["side"]}'></textarea>
404
+ <label>run.js kodu — stdin: JSON args · stdout: JSON sonuç (console.log)</label>
405
+ <textarea id="toolCode" rows="12" spellcheck="false" placeholder="const args = JSON.parse(...); console.log(JSON.stringify({ ok: true }));"></textarea>
406
+ <div id="toolFormErr" class="sub" style="color:var(--err)" hidden></div>
407
+ </div>
408
+ <div id="toolTestRow">
409
+ <input id="toolTestArgs" placeholder='deneme argümanları (JSON): {"mesaj":"merhaba"}' spellcheck="false" autocomplete="off" />
410
+ <button id="toolTestBtn">&#9654;&#xFE0E; Çalıştır</button>
411
+ </div>
412
+ <pre id="toolTestOut" hidden></pre>
413
+ <div id="toolDialogBtns">
414
+ <button id="toolDeleteBtn" class="tool-danger" hidden>Sil</button>
415
+ <span class="file-spacer"></span>
416
+ <button id="toolSaveBtn" class="tool-primary">Kaydet</button>
417
+ </div>
418
+ </div>
419
+ </div>
420
+
366
421
  <div id="settingsOverlay" hidden>
367
422
  <div id="settingsDialog">
368
- <aside id="setTabs">
369
- <div class="set-tabs-title" data-i18n="set_title">AYARLAR</div>
423
+ <aside id="setTabs"> <div class="set-tabs-title" data-i18n="set_title">AYARLAR</div>
370
424
  <nav class="set-tabs-list">
371
425
  <button class="tab active" data-tab="lang" data-i18n="tab_lang">Dil</button>
372
426
  <button class="tab" data-tab="provider" data-i18n="tab_provider">Provider</button>
@@ -45,9 +45,30 @@ const els = {
45
45
  dmBtn: $('#dmBtn'),
46
46
  dmRailList: $('#dmRailList'),
47
47
  dmClear: $('#dmClear'),
48
+ dmOverlay: $('#dmOverlay'),
49
+ dmDialogList: $('#dmDialogList'),
50
+ dmModalClose: $('#dmModalClose'),
51
+ dmModalClear: $('#dmModalClear'),
52
+ dmColumnBtn: $('#dmColumnBtn'),
48
53
  watchBtn: $('#watchBtn'),
49
54
  watchPaneList: $('#watchPaneList'),
50
55
  watchPaneOpen: $('#watchPaneOpen'),
56
+ toolBoxAdd: $('#toolAdd'),
57
+ toolList: $('#toolList'),
58
+ toolFolderBtn: $('#toolFolderBtn'),
59
+ toolOverlay: $('#toolOverlay'),
60
+ toolClose: $('#toolClose'),
61
+ toolDialogTitle: $('#toolDialogTitle'),
62
+ toolName: $('#toolName'),
63
+ toolDesc: $('#toolDesc'),
64
+ toolParams: $('#toolParams'),
65
+ toolCode: $('#toolCode'),
66
+ toolFormErr: $('#toolFormErr'),
67
+ toolTestArgs: $('#toolTestArgs'),
68
+ toolTestBtn: $('#toolTestBtn'),
69
+ toolTestOut: $('#toolTestOut'),
70
+ toolSaveBtn: $('#toolSaveBtn'),
71
+ toolDeleteBtn: $('#toolDeleteBtn'),
51
72
  watchPaneLog: $('#watchPaneLog'),
52
73
  cronBtn: $('#cronBtn'),
53
74
  watchOverlay: $('#watchOverlay'),
@@ -5003,25 +5024,41 @@ async function loadRailChat(id) {
5003
5024
  }
5004
5025
  }
5005
5026
 
5006
- /* ---------------- AJAN DM sütunu (ajanlar arası konuşma) ----------------
5007
- rail ile aynı mimari: sağda ikinci sütun, dmBtn ile açılır/kapanır.
5008
- DM'ler ajanların agent_dm araç çağrılarından gelir; çiftlere gruplanır. */
5027
+ /* ---------------- AJAN DM (ajanlar arası konuşma) ----------------
5028
+ İki görünüm: ✉ modal (oturum listesi) + yan sütun (canlı akış).
5029
+ Thread anahtarı = ajan çifti + KONU — aynı konudaki mesajlar tek
5030
+ oturumda toplanır, gelen/giden hepsi kronolojik görünür. */
5009
5031
 
5010
5032
  const dmState = {
5011
- dms: [], // { at, from, fromTitle, to, toTitle, text }
5012
- open: new Set(), // açık thread key'leri
5033
+ dms: [], // { at, from, fromTitle, to, toTitle, topic, text }
5034
+ open: new Set(), // açık thread key'leri (modal + sütun ortak)
5013
5035
  };
5014
5036
 
5015
- function dmPairOf(dm) {
5037
+ function dmThreadOf(dm) {
5016
5038
  const a = String(dm.from || '');
5017
5039
  const b = String(dm.to || '');
5018
- return a < b ? a + '|' + b : b + '|' + a;
5040
+ const pair = a < b ? a + '|' + b : b + '|' + a;
5041
+ return pair + '|' + String(dm.topic || '(genel)').toLowerCase();
5019
5042
  }
5020
5043
 
5021
- function dmPairTitle(dm) {
5044
+ function dmThreadTitle(dm) {
5022
5045
  const a = String(dm.fromTitle || 'Ajan');
5023
5046
  const b = String(dm.toTitle || 'Ajan');
5024
- return a < b ? a + ' ↔ ' + b : b + ' ↔ ' + a;
5047
+ const pair = a < b ? a + ' ↔ ' + b : b + ' ↔ ' + a;
5048
+ const topic = String(dm.topic || '(genel)');
5049
+ return topic === '(genel)' ? pair : pair + ' · ' + topic;
5050
+ }
5051
+
5052
+ function dmThreads() {
5053
+ const threads = new Map();
5054
+ for (const dm of dmState.dms) {
5055
+ const key = dmThreadOf(dm);
5056
+ if (!threads.has(key)) threads.set(key, []);
5057
+ threads.get(key).push(dm);
5058
+ }
5059
+ return [...threads.entries()].sort(
5060
+ (x, y) => new Date(y[1][y[1].length - 1].at) - new Date(x[1][x[1].length - 1].at)
5061
+ );
5025
5062
  }
5026
5063
 
5027
5064
  function dmTime(iso) {
@@ -5030,6 +5067,15 @@ function dmTime(iso) {
5030
5067
  return d.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
5031
5068
  }
5032
5069
 
5070
+ function dmMsgHtml(m) {
5071
+ return (
5072
+ `<div class="dmt-msg"><span class="dm-who">${escapeHtml(String(m.fromTitle || 'Ajan'))} → ${escapeHtml(String(m.toTitle || 'Ajan'))}</span>` +
5073
+ `${escapeHtml(String(m.text || ''))}<span class="dm-at">${dmTime(m.at)}</span></div>`
5074
+ );
5075
+ }
5076
+
5077
+ /* --- yan sütun --- */
5078
+
5033
5079
  function renderDmRail() {
5034
5080
  const list = els.dmRailList;
5035
5081
  if (!list) return;
@@ -5037,37 +5083,19 @@ function renderDmRail() {
5037
5083
  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>';
5038
5084
  return;
5039
5085
  }
5040
- /* çiftlere grupla (kronolojik) — en yeni thread üstte */
5041
- const threads = new Map();
5042
- for (const dm of dmState.dms) {
5043
- const key = dmPairOf(dm);
5044
- if (!threads.has(key)) threads.set(key, []);
5045
- threads.get(key).push(dm);
5046
- }
5047
- const rows = [...threads.entries()].sort(
5048
- (x, y) => new Date(y[1][y[1].length - 1].at) - new Date(x[1][x[1].length - 1].at)
5049
- );
5050
5086
  list.innerHTML = '';
5051
- for (const [key, msgs] of rows) {
5087
+ for (const [key, msgs] of dmThreads()) {
5052
5088
  const open = dmState.open.has(key);
5053
5089
  const last = msgs[msgs.length - 1];
5054
5090
  const el = document.createElement('div');
5055
5091
  el.className = 'dmt' + (open ? ' open' : '');
5056
5092
  el.innerHTML =
5057
5093
  '<div class="dmt-head">' +
5058
- `<span class="dmt-title">${escapeHtml(dmPairTitle(last))}</span>` +
5094
+ `<span class="dmt-title">${escapeHtml(dmThreadTitle(last))}</span>` +
5059
5095
  `<span class="dmt-count">${msgs.length}</span>` +
5060
5096
  '</div>' +
5061
5097
  `<div class="dmt-last">${escapeHtml(String(last.text || '').replace(/\s+/g, ' ').slice(0, 90))} · ${dmTime(last.at)}</div>` +
5062
- '<div class="dmt-msgs">' +
5063
- msgs
5064
- .map(
5065
- (m) =>
5066
- `<div class="dmt-msg"><span class="dm-who">${escapeHtml(String(m.fromTitle || 'Ajan'))} → ${escapeHtml(String(m.toTitle || 'Ajan'))}</span>` +
5067
- `${escapeHtml(String(m.text || ''))}<span class="dm-at">${dmTime(m.at)}</span></div>`
5068
- )
5069
- .join('') +
5070
- '</div>';
5098
+ '<div class="dmt-msgs">' + msgs.map(dmMsgHtml).join('') + '</div>';
5071
5099
  el.addEventListener('click', () => {
5072
5100
  if (dmState.open.has(key)) dmState.open.delete(key);
5073
5101
  else dmState.open.add(key);
@@ -5095,18 +5123,263 @@ function toggleDmRail(hide) {
5095
5123
  if (!hide) refreshDmRail();
5096
5124
  }
5097
5125
 
5098
- if (els.dmBtn) {
5099
- els.dmBtn.addEventListener('click', () => {
5100
- toggleDmRail(!$('#dmRail').classList.contains('dm-hidden'));
5126
+ /* --- modal (✉ butonu) --- */
5127
+
5128
+ function renderDmModal() {
5129
+ const list = els.dmDialogList;
5130
+ if (!list) return;
5131
+ if (!dmState.dms.length) {
5132
+ list.innerHTML = '<div class="dm-empty">Henüz ajan DM\u2019i yok.<br>Ajanlar agent_dm aracıyla birbirine mesaj attıkça oturumlar burada birikir.</div>';
5133
+ return;
5134
+ }
5135
+ list.innerHTML = '';
5136
+ for (const [key, msgs] of dmThreads()) {
5137
+ const open = dmState.open.has(key);
5138
+ const last = msgs[msgs.length - 1];
5139
+ const el = document.createElement('div');
5140
+ el.className = 'dmt' + (open ? ' open' : '');
5141
+ el.innerHTML =
5142
+ '<div class="dmt-head">' +
5143
+ `<span class="dmt-title">${escapeHtml(dmThreadTitle(last))}</span>` +
5144
+ `<span class="dmt-count">${msgs.length}</span>` +
5145
+ '</div>' +
5146
+ `<div class="dmt-last">${escapeHtml(String(last.text || '').replace(/\s+/g, ' ').slice(0, 120))} · ${dmTime(last.at)}</div>` +
5147
+ '<div class="dmt-msgs">' + msgs.map(dmMsgHtml).join('') + '</div>';
5148
+ el.addEventListener('click', () => {
5149
+ if (dmState.open.has(key)) dmState.open.delete(key);
5150
+ else dmState.open.add(key);
5151
+ renderDmModal();
5152
+ const again = els.dmDialogList.querySelector('.dmt.open .dmt-msgs');
5153
+ if (again) again.scrollTop = again.scrollHeight;
5154
+ });
5155
+ list.appendChild(el);
5156
+ }
5157
+ }
5158
+
5159
+ async function openDmModal() {
5160
+ if (!els.dmOverlay) return;
5161
+ els.dmOverlay.hidden = false;
5162
+ await refreshDmRail(); // kayıtları tazele
5163
+ renderDmModal();
5164
+ if (els.dmBtn) els.dmBtn.classList.add('on');
5165
+ }
5166
+
5167
+ function closeDmModal() {
5168
+ if (els.dmOverlay) els.dmOverlay.hidden = true;
5169
+ if (els.dmBtn) els.dmBtn.classList.remove('on');
5170
+ }
5171
+
5172
+ async function dmClearAll() {
5173
+ if (!(await uiConfirm('TÜM ajan DM geçmişi silinsin mi?', 'Sil'))) return;
5174
+ try { await beast.agentDmsClear(); } catch {}
5175
+ dmState.dms = [];
5176
+ renderDmRail();
5177
+ renderDmModal();
5178
+ toast('Ajan DM geçmişi silindi');
5179
+ }
5180
+
5181
+ if (els.dmBtn) els.dmBtn.addEventListener('click', openDmModal);
5182
+ if (els.dmModalClose) els.dmModalClose.addEventListener('click', closeDmModal);
5183
+ if (els.dmModalClear) els.dmModalClear.addEventListener('click', dmClearAll);
5184
+ if (els.dmOverlay) {
5185
+ els.dmOverlay.addEventListener('click', (e) => {
5186
+ if (e.target === els.dmOverlay) closeDmModal();
5187
+ });
5188
+ els.dmOverlay.addEventListener('keydown', (e) => {
5189
+ if (e.key === 'Escape') { e.stopPropagation(); closeDmModal(); }
5101
5190
  });
5102
5191
  }
5103
- if (els.dmClear) {
5104
- els.dmClear.addEventListener('click', async () => {
5105
- if (!(await uiConfirm('TÜM ajan DM geçmişi silinsin mi?', 'Sil'))) return;
5106
- try { await beast.agentDmsClear(); } catch {}
5107
- dmState.dms = [];
5108
- renderDmRail();
5109
- toast('Ajan DM geçmişi silindi');
5192
+ if (els.dmClear) els.dmClear.addEventListener('click', dmClearAll);
5193
+ if (els.dmColumnBtn) {
5194
+ els.dmColumnBtn.addEventListener('click', () => {
5195
+ closeDmModal();
5196
+ toggleDmRail(false); // yan sütunda canlı akış
5197
+ });
5198
+ }
5199
+
5200
+ /* ---------------- TOOLS konsolu (kişisel araçlar) ----------------
5201
+ %APPDATA%\beast\tools\<ad>\tool.json + run.js — kullanıcı kendi araçlarını
5202
+ yazar; finance dahil TÜM oturumlar tool__<ad> olarak çağırır. Sol panelde
5203
+ buradan görüntülenir, yazılır, test edilir, silinir. */
5204
+
5205
+ const toolUi = {
5206
+ tools: [],
5207
+ editId: null,
5208
+ logRows: [],
5209
+ };
5210
+
5211
+ const TOOL_EXAMPLE_CODE =
5212
+ "'use strict';\n" +
5213
+ "// KİŞİSEL TOOL — stdin: JSON args · stdout: JSON sonuç\n" +
5214
+ "const fs = require('fs');\n" +
5215
+ "let raw = '';\n" +
5216
+ "try { raw = fs.readFileSync(0, 'utf8'); } catch {}\n" +
5217
+ "let args = {};\n" +
5218
+ "try { args = JSON.parse(raw || '{}'); } catch {}\n" +
5219
+ "// Buraya kendi işini yaz (MT5 köprüsü, özel API, hesap, sinyal…)\n" +
5220
+ "console.log(JSON.stringify({ ok: true, echo: args }));\n";
5221
+
5222
+ let toolRenderTimer = null;
5223
+ function scheduleToolRender() {
5224
+ if (toolRenderTimer) return;
5225
+ toolRenderTimer = setTimeout(() => {
5226
+ toolRenderTimer = null;
5227
+ renderToolBox();
5228
+ }, 500);
5229
+ }
5230
+
5231
+ async function renderToolBox() {
5232
+ const list = els.toolList;
5233
+ if (!list) return;
5234
+ try { toolUi.tools = (await beast.toolsList()) || []; } catch { toolUi.tools = []; }
5235
+ list.textContent = '';
5236
+ if (!toolUi.tools.length) {
5237
+ const d = document.createElement('div');
5238
+ d.className = 'tool-empty';
5239
+ d.textContent = 'Tool yok — + ile kendi aracını yaz (Finance dahil tüm oturumlarda çalışır)';
5240
+ list.appendChild(d);
5241
+ return;
5242
+ }
5243
+ for (const t of toolUi.tools) {
5244
+ const row = document.createElement('div');
5245
+ row.className = 'tool-row';
5246
+ const last = toolUi.logRows.filter((l) => l.toolId === t.id).slice(-1)[0];
5247
+ const st = last ? (last.ok ? 'ok' : 'err') : 'idle';
5248
+ const tip = last
5249
+ ? 'son çalışma ' + dmTime(last.at) + (last.error ? ' · hata: ' + last.error : ' · ' + last.ms + ' ms')
5250
+ : 'hiç çalışmadı';
5251
+ row.innerHTML =
5252
+ '<span class="tool-dot ' + st + '" title="' + escapeHtml(tip) + '"></span>' +
5253
+ '<span class="tool-name" title="' + escapeHtml(t.description || '') + '">' + escapeHtml(t.name) + '</span>' +
5254
+ '<span class="tool-btns">' +
5255
+ '<button class="tb-run" title="Aç & test çalıştır">&#9654;&#xFE0E;</button>' +
5256
+ '<button class="tb-edit" title="Düzenle">&#9998;&#xFE0E;</button>' +
5257
+ '<button class="tb-del" title="Sil">&#10005;&#xFE0E;</button>' +
5258
+ '</span>';
5259
+ row.querySelector('.tb-run').addEventListener('click', () => openToolDialog(t));
5260
+ row.querySelector('.tb-edit').addEventListener('click', () => openToolDialog(t));
5261
+ row.querySelector('.tb-del').addEventListener('click', async () => {
5262
+ if (!(await uiConfirm('"' + t.name + '" toolu silinsin mi? (ajanlar artık çağıramaz)', 'Sil'))) return;
5263
+ const r = await beast.toolDelete(t.id).catch(() => ({ ok: false }));
5264
+ if (r && r.ok) {
5265
+ toast('Tool silindi');
5266
+ renderToolBox();
5267
+ } else toast((r && r.error) || 'Silinemedi');
5268
+ });
5269
+ list.appendChild(row);
5270
+ }
5271
+ }
5272
+
5273
+ function openToolDialog(t) {
5274
+ if (!els.toolOverlay) return;
5275
+ toolUi.editId = t ? t.id : null;
5276
+ els.toolDialogTitle.textContent = t ? 'TOOL · ' + t.name : 'YENİ TOOL';
5277
+ els.toolName.value = t ? t.name : '';
5278
+ els.toolDesc.value = t ? t.description || '' : '';
5279
+ els.toolParams.value = t
5280
+ ? JSON.stringify(t.parameters || { type: 'object', properties: {} }, null, 2)
5281
+ : '{\n "type": "object",\n "properties": {},\n "required": []\n}';
5282
+ els.toolCode.value = t ? t.code || TOOL_EXAMPLE_CODE : TOOL_EXAMPLE_CODE;
5283
+ els.toolTestArgs.value = t && t.parameters && Array.isArray(t.parameters.required) && t.parameters.required.length
5284
+ ? JSON.stringify(Object.fromEntries(t.parameters.required.map((k) => [k, ''])), null, 1)
5285
+ : '';
5286
+ els.toolTestOut.hidden = true;
5287
+ els.toolTestOut.textContent = '';
5288
+ els.toolFormErr.hidden = true;
5289
+ els.toolDeleteBtn.hidden = !t;
5290
+ els.toolOverlay.hidden = false;
5291
+ els.toolName.focus();
5292
+ }
5293
+
5294
+ function closeToolDialog() {
5295
+ if (els.toolOverlay) els.toolOverlay.hidden = true;
5296
+ }
5297
+
5298
+ function toolErr(msg) {
5299
+ if (els.toolFormErr) {
5300
+ els.toolFormErr.textContent = msg;
5301
+ els.toolFormErr.hidden = !msg;
5302
+ }
5303
+ }
5304
+
5305
+ if (els.toolBoxAdd) els.toolBoxAdd.addEventListener('click', () => openToolDialog(null));
5306
+ if (els.toolFolderBtn) els.toolFolderBtn.addEventListener('click', () => beast.toolsOpenFolder());
5307
+ if (els.toolClose) els.toolClose.addEventListener('click', closeToolDialog);
5308
+ if (els.toolOverlay) {
5309
+ els.toolOverlay.addEventListener('click', (e) => {
5310
+ if (e.target === els.toolOverlay) closeToolDialog();
5311
+ });
5312
+ els.toolOverlay.addEventListener('keydown', (e) => {
5313
+ if (e.key === 'Escape') { e.stopPropagation(); closeToolDialog(); }
5314
+ });
5315
+ }
5316
+ if (els.toolSaveBtn) {
5317
+ els.toolSaveBtn.addEventListener('click', async () => {
5318
+ toolErr('');
5319
+ const name = String(els.toolName.value || '').trim();
5320
+ if (!name) return toolErr('Tool adı gerekli');
5321
+ let parameters = null;
5322
+ try {
5323
+ parameters = JSON.parse(els.toolParams.value || '{}');
5324
+ } catch {
5325
+ return toolErr('Parametre şeması geçerli JSON değil');
5326
+ }
5327
+ const code = String(els.toolCode.value || '').trim();
5328
+ if (!code) return toolErr('run.js kodu boş olamaz');
5329
+ const r = await beast
5330
+ .toolSave({ id: toolUi.editId, name, description: String(els.toolDesc.value || ''), parameters, code })
5331
+ .catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
5332
+ if (r && r.ok) {
5333
+ closeToolDialog();
5334
+ toast('Tool kaydedildi — ajanlar tool__' + r.id + ' olarak çağırabilir (Finance dahil)');
5335
+ renderToolBox();
5336
+ } else {
5337
+ toolErr((r && r.error) || 'Kaydedilemedi');
5338
+ }
5339
+ });
5340
+ }
5341
+ if (els.toolDeleteBtn) {
5342
+ els.toolDeleteBtn.addEventListener('click', async () => {
5343
+ if (!toolUi.editId) return;
5344
+ const t = toolUi.tools.find((x) => x.id === toolUi.editId);
5345
+ if (!(await uiConfirm('"' + ((t && t.name) || toolUi.editId) + '" toolu silinsin mi?', 'Sil'))) return;
5346
+ const r = await beast.toolDelete(toolUi.editId).catch(() => ({ ok: false }));
5347
+ if (r && r.ok) {
5348
+ closeToolDialog();
5349
+ toast('Tool silindi');
5350
+ renderToolBox();
5351
+ } else toast((r && r.error) || 'Silinemedi');
5352
+ });
5353
+ }
5354
+ if (els.toolTestBtn) {
5355
+ els.toolTestBtn.addEventListener('click', async () => {
5356
+ toolErr('');
5357
+ let args = {};
5358
+ try {
5359
+ args = JSON.parse(els.toolTestArgs.value || '{}');
5360
+ } catch {
5361
+ return toolErr('Test argümanları geçerli JSON değil');
5362
+ }
5363
+ const id = toolUi.editId || String(els.toolName.value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '_').replace(/^_+|_+$/g, '');
5364
+ if (!id) return toolErr('Önce tool adı gir');
5365
+ /* önce kaydet (yeni tool ilk testte de diskte olsun) */
5366
+ const sr = await beast
5367
+ .toolSave({
5368
+ id: toolUi.editId,
5369
+ name: String(els.toolName.value || id),
5370
+ description: String(els.toolDesc.value || ''),
5371
+ parameters: (() => {
5372
+ try { return JSON.parse(els.toolParams.value || '{}'); } catch { return null; }
5373
+ })() || { type: 'object', properties: {} },
5374
+ code: String(els.toolCode.value || ''),
5375
+ })
5376
+ .catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
5377
+ if (!sr || !sr.ok) return toolErr((sr && sr.error) || 'Tool kaydedilemedi');
5378
+ els.toolTestOut.hidden = false;
5379
+ els.toolTestOut.textContent = 'çalışıyor…';
5380
+ const r = await beast.toolRun(sr.id, args).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
5381
+ els.toolTestOut.textContent = JSON.stringify(r, null, 2).slice(0, 8000);
5382
+ renderToolBox();
5110
5383
  });
5111
5384
  }
5112
5385
 
@@ -5254,7 +5527,7 @@ function onEvent(ev) {
5254
5527
  scheduleAgentsRender();
5255
5528
  return;
5256
5529
  }
5257
- /* AJAN DM: ajanlar arası mesaj — panel açıksa canlı düşer */
5530
+ /* AJAN DM: ajanlar arası mesaj — açık görünümde canlı düşer (modal/sütun) */
5258
5531
  if (ev.type === 'agent-dm') {
5259
5532
  dmState.dms.push({
5260
5533
  at: ev.at,
@@ -5262,12 +5535,28 @@ function onEvent(ev) {
5262
5535
  fromTitle: ev.fromTitle,
5263
5536
  to: ev.to,
5264
5537
  toTitle: ev.toTitle,
5538
+ topic: ev.topic,
5265
5539
  text: ev.text,
5266
5540
  });
5267
5541
  if (dmState.dms.length > 400) dmState.dms.splice(0, dmState.dms.length - 400);
5542
+ if (els.dmOverlay && !els.dmOverlay.hidden) renderDmModal();
5268
5543
  if ($('#dmRail') && !$('#dmRail').classList.contains('dm-hidden')) renderDmRail();
5269
5544
  return;
5270
5545
  }
5546
+ /* KİŞİSEL TOOL çalışma kaydı — TOOLS konsolunun nokta göstergeleri */
5547
+ if (ev.type === 'tool-log') {
5548
+ toolUi.logRows.push({
5549
+ at: ev.at,
5550
+ tool: ev.tool,
5551
+ toolId: ev.toolId,
5552
+ ok: ev.ok,
5553
+ ms: ev.ms,
5554
+ error: ev.error,
5555
+ });
5556
+ if (toolUi.logRows.length > 60) toolUi.logRows.splice(0, toolUi.logRows.length - 60);
5557
+ scheduleToolRender();
5558
+ return;
5559
+ }
5271
5560
  /* OFFLINE MESAJ KUYRUĞU: bağlantı + kuyruk olayları (sessionId filtresinden önce) */
5272
5561
  if (ev.type === 'net' || ev.type === 'netQueue') { onNetEvent(ev); return; }
5273
5562
  /* Sandbox klonlama progress'i (sessionId'siz global olay) */
@@ -6415,6 +6704,7 @@ async function init() {
6415
6704
  };
6416
6705
 
6417
6706
  refreshAgentsPane();
6707
+ renderToolBox(); // TOOLS konsolu: kişisel araçlar sol panelde
6418
6708
 
6419
6709
  els.sendBtn.addEventListener('click', sendCurrent);
6420
6710
  els.stopBtn.addEventListener('click', () => {
@@ -8616,7 +8906,15 @@ async function setFinanceMode(on) {
8616
8906
  const brandSub = document.querySelector('#brand .brand-sub');
8617
8907
  if (brandSub) brandSub.textContent = on ? 'Finance' : 'Agent';
8618
8908
  try { localStorage.setItem('beast.financeMode', on ? '1' : '0'); } catch {}
8619
- beast.financeMode(!!on, activeId).catch(() => {});
8909
+ const fm = await beast.financeMode(!!on, activeId).catch(() => ({}));
8910
+ /* finance modu açıldı ama aktif oturum NORMAL (örn. WhatsApp sohbeti) ise
8911
+ ASLA o oturumu finance'e çevirme — kendine ÖZEL finance oturumu aç */
8912
+ if (on && fm && fm.needNew) {
8913
+ try {
8914
+ const created = await beast.createSession();
8915
+ if (created && created.id) await openSession(created.id);
8916
+ } catch {}
8917
+ }
8620
8918
  /* sohbet geçmişi listesi değişir: finance modunda yalnız finance sohbetleri */
8621
8919
  try {
8622
8920
  await refreshSessions();