beast-agent 2.24.5 → 2.30.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.24.5",
4
+ "version": "2.30.1",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -1894,6 +1894,10 @@ class Engine {
1894
1894
  macro:
1895
1895
  'ROL: MAKRO AJANI 🌍 — büyük resim uzmanısın, İşlem AÇMAZSIN (mt5_trade/mt5_pending/mt5_close KULLANMA). Her turda web_search ile güncel makro manşetleri + ekonomik takvim riskleri (faiz, CPI, jeopolitik); DXY/altın/petrol bağıntılarını odak sembollere çevir; sembol başına yön eğilimi + TEMKİN/BEKLE notu ver.',
1896
1896
  })[String((session && session.financeRole) || '')] || '';
1897
+ /* FİNANS EKİBİ: tüm finance ajanları tek DM grubundadır — sohbet grup
1898
+ thread'inde toplanır (ayrı ayrı 1:1 thread'lere dağılmaz) */
1899
+ const finJob = this._bgJobs && this._bgJobs.get(String(session && session.id));
1900
+ const teamLine = finJob && finJob.teamGid ? this._agentTeamPromptLine(finJob.teamGid) : '';
1897
1901
  return (
1898
1902
  (mem.soul ? mem.soul.trim() + '\n\n' : '') +
1899
1903
  'Sen BEAST FİNANS\u2019sın — bilgisayardaki MetaTrader 5 (MT5) terminaline köprüyle bağlı trading ajanı.\n' +
@@ -1902,6 +1906,7 @@ class Engine {
1902
1906
  'GÖREV ALANI: piyasa verisi okuma (fiyat/hesap/pozisyon/geçmiş), teknik değerlendirme, pozisyon yönetimi (aç/kapat/SL/TP), risk disiplini ve kısa karar raporları. Kod yazma işi DEĞİLDİR.\n' +
1903
1907
  modeBlock2 + '\n' +
1904
1908
  (roleBlock ? roleBlock + '\n' : '') +
1909
+ (teamLine || '') +
1905
1910
  'MT5 ARAÇLARI: mt5_status (bağlantı), mt5_account (hesap), mt5_market (canlı fiyat), mt5_positions (açık pozisyonlar), mt5_orders (bekleyen emirler), mt5_history (kapanan işlemler), mt5_trade (piyasa emri), mt5_close (kapat), mt5_modify (SL/TP), mt5_pending (bekleyen emir), mt5_cancel (emir iptal).\n' +
1906
1911
  'VERİ AKIŞI (her değerlendirmede): mt5_account + mt5_positions + mt5_market çağrılarını AYNI turda PARALEL ver; gerekiyorsa mt5_history ile son işlemleri gör.\n' +
1907
1912
  'PARALEL + KOORDİNASYON: uzun araştırma/işleri run_background ile paralel finance işçisine devret (parent finance olduğu için işçi mt5 okuma araçlarını görür); koşan ajanlarla konuşmak için agent_dm (to: ajan başlığındaki anahtar kelime, örn "GOLD"; ortak karar için group: "İSİM" ile grup sohbeti kur — mesaj tüm üyelere düşer). Görevin bitince DM/grup sohbetleri otomatik KAPANIR (geçmiş panelde kalır).\n' +
@@ -2489,7 +2494,6 @@ class Engine {
2489
2494
  korunur — sadece içerik temizlenir, önek cache bozulmaz. Döndürür: kazanılan token */
2490
2495
  _pruneSession(session) {
2491
2496
  try {
2492
- if (this.ctrls.has(session.id)) return 0;
2493
2497
  const msgs = session.messages;
2494
2498
  let total = 0, pruned = 0, turns = 0;
2495
2499
  const targets = [];
@@ -2580,7 +2584,10 @@ class Engine {
2580
2584
  `- kullanıcının hedefleri ve net talepleri\n` +
2581
2585
  `- alınan kararlar, önemli bilgi parçaları (isim, sayı, dosya/klasör yolu, hata ve çözümü)\n` +
2582
2586
  `- tamamlanan işler ve sonuçları\n` +
2583
- `Madde madde yaz, en fazla 120 kelime, başlık/selamlama yok. Alakasız ayrıntıyı at.\n\n` +
2587
+ (session.finance
2588
+ ? `- FİNANS İŞLEMLERİ (KRİTİK, atlama): açılan/kapatılan/güncellenen pozisyonlar — sembol, yön (AL/SAT), lot, giriş/SL/TP, kâr/zarar ve gerekçe; sonraki turlarda birebir hatırlanmalı\n`
2589
+ : '') +
2590
+ `Madde madde yaz, en fazla ${session.finance ? 180 : 120} kelime, başlık/selamlama yok. Alakasız ayrıntıyı at.\n\n` +
2584
2591
  `# SOHBET PARÇASI\n${transcript}`;
2585
2592
  const res = await chatOnce(
2586
2593
  this.sel,
@@ -2682,6 +2689,25 @@ class Engine {
2682
2689
  }
2683
2690
  }
2684
2691
 
2692
+ /* SÜREKLİ ajan DM INBOX (bkz. flushPendingReports): tur aralığında biriken
2693
+ DM'ler yeni tur AÇMAZ; sıradaki planlı turun mesajına tek blok olarak
2694
+ eklenir — ajan-ajan sonsuz DM ping-pong'u ve bağlam şişmesi engellenir. */
2695
+ try {
2696
+ const jobRec = this._bgJobs && this._bgJobs.get(String(sessionId));
2697
+ if (jobRec && Array.isArray(jobRec.dmInbox) && jobRec.dmInbox.length) {
2698
+ const inbox = jobRec.dmInbox.splice(0);
2699
+ const boxText = inbox.join('\n\n').slice(0, 6000);
2700
+ const prefix =
2701
+ `[BEKLEYEN AJAN DM'LERİ — ${inbox.length} mesaj]\n${boxText}\n` +
2702
+ `(Tur aralığında biriken DM'ler. Bu turda değerlendir; yalnızca aksiyon/cevap GEREKİYORSA agent_dm ile TEK kısa cevap ver — teşekkür/onay/ack mesajı YAZMA.)\n\n`;
2703
+ if (typeof msg.content === 'string') {
2704
+ msg.content = prefix + msg.content;
2705
+ } else if (Array.isArray(msg.content) && msg.content[0] && typeof msg.content[0].text === 'string') {
2706
+ msg.content[0] = { ...msg.content[0], text: prefix + msg.content[0].text };
2707
+ }
2708
+ }
2709
+ } catch {}
2710
+
2685
2711
  if (!String(typeof msg.content === 'string' ? msg.content : msg.content[0].text || '').trim()) return false;
2686
2712
  /* #12 kişilik kalibrasyonu: kullanıcı cümlelerini örnek havuzuna düşür (Beast Code hariç — hız) */
2687
2713
  if (!s.bcCode) {
@@ -3509,10 +3535,22 @@ class Engine {
3509
3535
  this._pendingReports = [];
3510
3536
  return;
3511
3537
  }
3538
+ const job = this._bgJobs && this._bgJobs.get(String(sessionId));
3539
+ /* SÜREKLİ ajan (Beast Finance): DM/rapor teslimi YENİ TUR AÇMAZ — aksi
3540
+ halde ajanlar birbirine DM attıkça sonsuz tur döngüsü doğar (bağlam
3541
+ şişer, ajan hiç dinlenmez). Mesaj inbox'a yazılır; bir sonraki PLANLI
3542
+ turda send() tarafından tek blok halinde enjekte edilir. */
3543
+ const continuous = !!(job && job.continuous);
3512
3544
  const rest = [];
3513
3545
  for (const r of this._pendingReports) {
3514
3546
  if (r.parentId !== sessionId) { rest.push(r); continue; }
3515
3547
  if (this._deletedSessions && this._deletedSessions.has(sessionId)) continue;
3548
+ if (continuous) {
3549
+ job.dmInbox = job.dmInbox || [];
3550
+ job.dmInbox.push(String(r.text || '').slice(0, 4000));
3551
+ if (job.dmInbox.length > 20) job.dmInbox.splice(0, job.dmInbox.length - 20);
3552
+ continue;
3553
+ }
3516
3554
  if (this.isBusy(sessionId)) { rest.push(r); continue; }
3517
3555
  let sent = false;
3518
3556
  try { sent = !!this.send(sessionId, { text: r.text }); } catch {}
@@ -3553,8 +3591,10 @@ class Engine {
3553
3591
  _bgTrim(session) {
3554
3592
  const max = Engine.BG_KEEP_MSGS;
3555
3593
  const msgs = session.messages;
3556
- /* bg işleri + Beast Code: uzun geçmiş payload'ı şişirip yavaşlatır */
3557
- if ((!session.bgJob && !session.bcCode) || msgs.length <= max) return;
3594
+ /* bg işleri + Beast Code + SÜREKLİ finance ajanları: uzun geçmiş payload'ı
3595
+ şişirip yavaşlatır — finance ajanları binlerce tur döndüğü için bağlam
3596
+ tavanı onlara da şart */
3597
+ if ((!session.bgJob && !session.bcCode && !session.finance) || msgs.length <= max) return;
3558
3598
  let keep = msgs.slice(-max);
3559
3599
  /* GÜVENLİ SINIR: kesim bir assistant(tool_calls) + tool sonuç çiftini BÖLMESİN.
3560
3600
  Yetim 'tool' mesajı ya da sonuçları pencere dışında kalan tool_calls'lı
@@ -6389,7 +6429,7 @@ Engine.prototype._agentDmSend = function (fromSid, args) {
6389
6429
  parentId: m,
6390
6430
  text:
6391
6431
  `[AJAN DM (grup: "${g.title}") — ${dm.fromTitle} · konu: "${topic}"]\n${text}\n` +
6392
- `(Cevabını agent_dm aracıyla ver — group: "${g.title}", to: "${dm.fromTitle}", topic: "${topic}")`,
6432
+ `(Cevap yalnızca aksiyon/karar GEREKİYORSA ver — agent_dm group: "${g.title}", to: "${dm.fromTitle}", topic: "${topic}"; teşekkür/onay yazma.)`,
6393
6433
  });
6394
6434
  this.flushPendingReports(m);
6395
6435
  }
@@ -6414,7 +6454,7 @@ Engine.prototype._agentDmSend = function (fromSid, args) {
6414
6454
  parentId: target,
6415
6455
  text:
6416
6456
  `[AJAN DM — ${dm.fromTitle} · konu: "${topic}"]\n${text}\n` +
6417
- `(Cevabını agent_dm aracıyla ver — to: "${dm.fromTitle}", topic: "${topic}")`,
6457
+ `(Cevap yalnızca aksiyon/karar GEREKİYORSA ver — agent_dm to: "${dm.fromTitle}", topic: "${topic}"; teşekkür/onay yazma.)`,
6418
6458
  });
6419
6459
  this.flushPendingReports(target);
6420
6460
  }
@@ -6448,18 +6488,30 @@ Engine.prototype._pushAgentDm = function (dm) {
6448
6488
  ortak DM grubuna girer — tartışma ZORUNLU (sistem promptuyla). ---------- */
6449
6489
 
6450
6490
  /* İş başlarken çağrılır: aynı parent / bg-gruptan başka ajan varsa (varsa
6451
- önceden kurulmuştur) hepsini TEK ekip grubuna katar + katılım postu düşer. */
6491
+ önceden kurulmuştur) hepsini TEK ekip grubuna katar + katılım postu düşer.
6492
+ dmGroupId verilirse (finans ekibi gibi hazır grup) parent şartı aranmaz —
6493
+ aynı dmGroupId'li TÜM ajanlar tek gruba toplanır. */
6452
6494
  Engine.prototype._agentTeamJoin = function (job) {
6453
6495
  try {
6454
6496
  if (!job || !job.id) return;
6455
6497
  const parent = String(job.parentId || '');
6456
- if (!parent) return;
6457
- const gid = 'team:' + (job.groupId || parent);
6498
+ const explicit = String(job.dmGroupId || '');
6499
+ if (!parent && !explicit) return;
6500
+ const gid = explicit || 'team:' + (job.groupId || parent);
6458
6501
  const base =
6459
- String(job.task || job.title || 'ortak görev').replace(/\s+/g, ' ').trim().slice(0, 28) || 'ortak görev';
6502
+ String(job.dmGroupTitle || job.task || job.title || 'ortak görev').replace(/\s+/g, ' ').trim().slice(0, 40) ||
6503
+ 'ortak görev';
6460
6504
  let g = this._agentGroups.get(gid);
6461
6505
  if (!g) {
6462
- g = { id: gid, title: base + ' EKİP', members: [], titles: {}, createdAt: nowIso(), closed: false, team: true };
6506
+ g = {
6507
+ id: gid,
6508
+ title: job.dmGroupTitle ? base : base + ' EKİP',
6509
+ members: [],
6510
+ titles: {},
6511
+ createdAt: nowIso(),
6512
+ closed: false,
6513
+ team: true,
6514
+ };
6463
6515
  this._agentGroups.set(gid, g);
6464
6516
  }
6465
6517
  g.closed = false;
@@ -6470,9 +6522,13 @@ Engine.prototype._agentTeamJoin = function (job) {
6470
6522
  if (!g.members.includes(id)) g.members.push(id);
6471
6523
  if (title) g.titles[id] = String(title);
6472
6524
  };
6473
- /* aynı ortak görevdeki DİĞER ajanlar (aborted olanlar hariç) */
6525
+ /* aynı ortak görevdeki / aynı hazır gruptaki DİĞER ajanlar (aborted hariç) */
6474
6526
  const mates = [...this._bgJobs.values()].filter(
6475
- (j) => j && String(j.id) !== String(job.id) && String(j.parentId) === parent && j.status !== 'aborted'
6527
+ (j) =>
6528
+ j &&
6529
+ String(j.id) !== String(job.id) &&
6530
+ j.status !== 'aborted' &&
6531
+ (explicit ? String(j.dmGroupId || '') === explicit : String(j.parentId) === parent)
6476
6532
  );
6477
6533
  add(job.id, job.title);
6478
6534
  for (const m of mates) add(m.id, m.title);
package/src/main.js CHANGED
@@ -8519,7 +8519,7 @@ function finAgentRegisterBg(s, symbols) {
8519
8519
  const f = finCfg();
8520
8520
  const symLabel = symbols && symbols.length ? symbols.join(', ').slice(0, 40) : 'tüm izleme listesi';
8521
8521
  const roleDef = finRoleDef(s.financeRole);
8522
- engine._bgJobs.set(String(s.id), {
8522
+ const job = {
8523
8523
  id: s.id,
8524
8524
  code: s.code,
8525
8525
  title: s.bgTitle || 'Finance Ajanı',
@@ -8529,6 +8529,10 @@ function finAgentRegisterBg(s, symbols) {
8529
8529
  agent: null,
8530
8530
  parentId: '',
8531
8531
  groupId: null,
8532
+ /* FİNANS EKİBİ DM GRUBU: trader + analiz ekibi + sembol işçileri TEK
8533
+ gruba girer — mesajlar ayrı ayrı 1:1 thread'lere dağılmaz */
8534
+ dmGroupId: 'team:finance',
8535
+ dmGroupTitle: 'Beast Finance EKİP',
8532
8536
  status: 'running',
8533
8537
  slot: false, /* eşzamanlılık slotu TÜKETMEZ — ana işleri bloklamaz */
8534
8538
  continuous: true, /* tur sonları işi KAPATMAZ (_bgFinish) */
@@ -8539,8 +8543,12 @@ function finAgentRegisterBg(s, symbols) {
8539
8543
  fixes: 0,
8540
8544
  endedAt: null,
8541
8545
  error: null,
8542
- });
8546
+ };
8547
+ engine._bgJobs.set(String(s.id), job);
8543
8548
  engine._bgEmit();
8549
+ /* otomatik ekip grubuna katıl: grup thread'i ilk ajanla kurulur,
8550
+ sonraki her finance ajanı aynı gruba eklenir (katılım postu düşer) */
8551
+ engine._agentTeamJoin(job);
8544
8552
  } catch {}
8545
8553
  }
8546
8554
 
@@ -8758,15 +8766,18 @@ ipcMain.handle('finance:snapshot', async () => {
8758
8766
  const st = financeEnsureBridge();
8759
8767
  let account = st.account || null;
8760
8768
  let positions = [];
8769
+ let orders = [];
8761
8770
  let symbols = [];
8762
8771
  if (st.running) {
8763
- const [a, p, sy] = await Promise.all([
8772
+ const [a, p, o, sy] = await Promise.all([
8764
8773
  mt5bridge.call('account', {}, 8000).catch(() => null),
8765
8774
  mt5bridge.call('positions', {}, 8000).catch(() => null),
8775
+ mt5bridge.call('orders', {}, 8000).catch(() => null),
8766
8776
  mt5bridge.call('symbols', { symbols: f.symbols || [] }, 10000).catch(() => null),
8767
8777
  ]);
8768
8778
  if (a && a.ok) account = a.data && a.data.account;
8769
8779
  if (p && p.ok) positions = (p.data && p.data.positions) || [];
8780
+ if (o && o.ok) orders = (o.data && o.data.orders) || [];
8770
8781
  if (sy && sy.ok) symbols = (sy.data && sy.data.symbols) || [];
8771
8782
  }
8772
8783
  const busy = financeState.traderSid && engine ? engine.isBusy(financeState.traderSid) : false;
@@ -8775,6 +8786,7 @@ ipcMain.handle('finance:snapshot', async () => {
8775
8786
  bridge: { running: st.running, connected: st.connected, error: st.error, terminal: st.terminal, python: st.python },
8776
8787
  account,
8777
8788
  positions,
8789
+ orders,
8778
8790
  symbols,
8779
8791
  cfg: f,
8780
8792
  roles: FIN_ROLES,
@@ -8999,6 +9011,19 @@ ipcMain.handle('finance:close', async (_e, payload) => {
8999
9011
  return r;
9000
9012
  });
9001
9013
 
9014
+ /* Bekleyen emri panelden iptal et (ajanın mt5_cancel aracıyla aynı köprü) */
9015
+ ipcMain.handle('finance:cancel', async (_e, payload) => {
9016
+ const ticket = Number(payload && payload.ticket);
9017
+ if (!ticket) return { ok: false, error: 'ticket gerekli' };
9018
+ if (!mt5bridge.running) return { ok: false, error: 'MT5 köprüsü bağlı değil' };
9019
+ const r = await mt5bridge.call('cancel', { ticket }, 20000);
9020
+ if (r && r.ok) {
9021
+ financeLog('[panel] bekleyen emir iptal edildi: ticket ' + ticket);
9022
+ finPush('trade', { line: 'EMİR İPTAL (panel): ticket ' + ticket });
9023
+ }
9024
+ return r;
9025
+ });
9026
+
9002
9027
  ipcMain.handle('finance:install', async () => {
9003
9028
  financeState.installTried = false;
9004
9029
  financeTryInstall();
package/src/preload.js CHANGED
@@ -117,6 +117,7 @@ contextBridge.exposeInMainWorld('beast', {
117
117
  financeAgentSpawn: (symbol) => ipcRenderer.invoke('finance:agent:spawn', { symbol }),
118
118
  financeConnect: () => ipcRenderer.invoke('finance:connect'),
119
119
  financeClose: (ticket, volume) => ipcRenderer.invoke('finance:close', { ticket, volume }),
120
+ financeCancel: (ticket) => ipcRenderer.invoke('finance:cancel', { ticket }),
120
121
  financeInstall: () => ipcRenderer.invoke('finance:install'),
121
122
  financeSymbolsList: (filter) => ipcRenderer.invoke('finance:symbols:list', filter),
122
123
  clipRead: () => ipcRenderer.invoke('clip:read'),
@@ -161,7 +161,7 @@
161
161
  <div class="fin-kv"><span>Serbest Marj</span><b id="finFree">—</b></div>
162
162
  </div>
163
163
  </div>
164
- <div class="fin-card">
164
+ <div class="fin-card fin-card-trader">
165
165
  <div class="fin-card-title">TRADE AJANI <span id="finTraderDot" class="fin-dot off" title="Trader durumu"></span></div>
166
166
  <div class="fin-row"><select id="finTraderModel" title="Trader ajanının modeli — farklı API/model seçilebilir"></select></div>
167
167
  <div class="fin-row fin-mini">
@@ -173,7 +173,7 @@
173
173
  <label>Analiz ekibi</label>
174
174
  <div id="finRoles" class="fin-roles" title="Trader&#39;ın yanında koşacak uzman ajanlar — tüm skill + mt5 erişimli, işlem açmazlar"></div>
175
175
  </div>
176
- <div class="fin-row">
176
+ <div class="fin-row fin-btn-row">
177
177
  <button id="finTraderBtn" class="fin-start">&#9654;&#xFE0E; Ajanı Başlat</button>
178
178
  </div>
179
179
  <div id="finTraderStatus">Kapalı</div>
@@ -182,6 +182,10 @@
182
182
  <div class="fin-card-title">POZİSYONLAR <span id="finPosCount"></span></div>
183
183
  <div id="finPosList"><div class="fin-empty">Açık pozisyon yok</div></div>
184
184
  </div>
185
+ <div class="fin-card">
186
+ <div class="fin-card-title">BEKLEYEN EMİRLER <span id="finOrdCount"></span></div>
187
+ <div id="finOrdList"><div class="fin-empty">Bekleyen emir yok</div></div>
188
+ </div>
185
189
  <div class="fin-card">
186
190
  <div class="fin-card-title">
187
191
  PİYASA
@@ -180,6 +180,8 @@ const els = {
180
180
  finTraderStatus: $('#finTraderStatus'),
181
181
  finPosList: $('#finPosList'),
182
182
  finPosCount: $('#finPosCount'),
183
+ finOrdList: $('#finOrdList'),
184
+ finOrdCount: $('#finOrdCount'),
183
185
  finSymList: $('#finSymList'),
184
186
  finLog: $('#finLog'),
185
187
  finSymAdd: $('#finSymAdd'),
@@ -8874,6 +8876,56 @@ function finRenderPositions(list) {
8874
8876
  }
8875
8877
  }
8876
8878
 
8879
+ /* MT5 bekleyen emir tipi → etiket + renk sınıfı (0..7) */
8880
+ const FIN_ORDER_TYPES = {
8881
+ 0: { t: 'BUY', c: 'buy' },
8882
+ 1: { t: 'SELL', c: 'sell' },
8883
+ 2: { t: 'BUY LIMIT', c: 'buy' },
8884
+ 3: { t: 'SELL LIMIT', c: 'sell' },
8885
+ 4: { t: 'BUY STOP', c: 'buy' },
8886
+ 5: { t: 'SELL STOP', c: 'sell' },
8887
+ 6: { t: 'BUY STP-LMT', c: 'buy' },
8888
+ 7: { t: 'SELL STP-LMT', c: 'sell' },
8889
+ };
8890
+
8891
+ function finRenderOrders(list) {
8892
+ if (!els.finOrdList) return;
8893
+ els.finOrdList.textContent = '';
8894
+ if (els.finOrdCount) els.finOrdCount.textContent = list && list.length ? '(' + list.length + ')' : '';
8895
+ if (!list || !list.length) {
8896
+ const d = document.createElement('div');
8897
+ d.className = 'fin-empty';
8898
+ d.textContent = 'Bekleyen emir yok';
8899
+ els.finOrdList.appendChild(d);
8900
+ return;
8901
+ }
8902
+ for (const o of list) {
8903
+ const row = document.createElement('div');
8904
+ row.className = 'fin-pos fin-ord';
8905
+ const ot = FIN_ORDER_TYPES[Number(o.type)] || { t: String(o.type ?? '?'), c: '' };
8906
+ const vol = Number(o.volume_current) || Number(o.volume_initial) || 0;
8907
+ row.innerHTML =
8908
+ '<span class="fp-sym" title="ticket ' + o.ticket + '">' + escapeHtml(String(o.symbol || '?')) + '</span>' +
8909
+ '<span class="fp-dir ' + ot.c + '">' + ot.t + '</span>' +
8910
+ '<span class="fp-meta">' + vol + ' lot @ ' + (Number(o.price_open) || 0) +
8911
+ (o.sl ? ' · SL ' + o.sl : '') + (o.tp ? ' · TP ' + o.tp : '') + '</span>';
8912
+ const btn = document.createElement('button');
8913
+ btn.className = 'fp-close';
8914
+ btn.title = 'Bekleyen emri iptal et';
8915
+ btn.textContent = '✕';
8916
+ btn.addEventListener('click', async () => {
8917
+ const ok = await uiConfirm((o.symbol || '') + ' bekleyen emri iptal edilsin mi? (ticket ' + o.ticket + ')', 'İptal Et');
8918
+ if (!ok) return;
8919
+ const r = await beast.financeCancel(o.ticket).catch((e) => ({ ok: false, error: String((e && e.message) || e) }));
8920
+ if (r && r.ok) toast('Emir iptal edildi');
8921
+ else toast('İptal edilemedi: ' + ((r && r.error) || '?'));
8922
+ finSnapshot();
8923
+ });
8924
+ row.appendChild(btn);
8925
+ els.finOrdList.appendChild(row);
8926
+ }
8927
+ }
8928
+
8877
8929
  function finRenderSymbols(list) {
8878
8930
  if (!els.finSymList) return;
8879
8931
  els.finSymList.textContent = '';
@@ -9038,6 +9090,8 @@ function finRenderTrader(trader, cfg) {
9038
9090
  else els.finTraderDot.classList.add('off');
9039
9091
  }
9040
9092
  if (els.finTraderStatus) {
9093
+ els.finTraderStatus.classList.toggle('on', on);
9094
+ els.finTraderStatus.classList.toggle('busy', !!(on && trader && trader.busy));
9041
9095
  let t = '';
9042
9096
  if (trader && trader.lastAt) {
9043
9097
  const d = new Date(trader.lastAt);
@@ -9087,6 +9141,7 @@ async function finSnapshot() {
9087
9141
  finSetDot(b.connected ? true : b.running ? 'busy' : false, b);
9088
9142
  finRenderAccount(r.account);
9089
9143
  finRenderPositions(r.positions || []);
9144
+ finRenderOrders(r.orders || []);
9090
9145
  finRenderSymbols(r.symbols || []);
9091
9146
  if (Array.isArray(r.roles) && r.roles.length) finRolesCatalog = r.roles;
9092
9147
  finTraderInputsSet(r.cfg);
@@ -4919,16 +4919,36 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
4919
4919
  color: #fff; /* kırmızı zeminde beyaz her iki temada da okunur */
4920
4920
  }
4921
4921
 
4922
+ /* TRADE AJANI kartı — vurgulu görünüm (accent çerçeve + yumuşak zemin) */
4923
+ .fin-card-trader {
4924
+ border-color: var(--accent);
4925
+ box-shadow: 0 0 0 1px var(--accent-dim), 0 2px 10px rgba(0, 0, 0, 0.1);
4926
+ background: linear-gradient(180deg, var(--accent-dim), transparent 46%), var(--panel);
4927
+ }
4928
+ .fin-card-trader .fin-card-title { color: var(--text); }
4929
+ .fin-card-trader .fin-row { margin-bottom: 8px; }
4930
+ .fin-card-trader .fin-btn-row { margin-top: 2px; }
4931
+ .fin-btn-row .fin-start { flex: 1; width: 100%; padding: 7px 14px; }
4932
+
4922
4933
  #finTraderStatus {
4923
- margin-top: 7px;
4924
- font-size: 11px;
4934
+ margin-top: 8px;
4935
+ font-size: 10.5px;
4925
4936
  color: var(--muted);
4937
+ background: var(--panel2);
4938
+ border: 1px solid var(--border);
4939
+ border-radius: 7px;
4940
+ padding: 5px 8px;
4926
4941
  white-space: nowrap;
4927
4942
  overflow: hidden;
4928
4943
  text-overflow: ellipsis;
4929
4944
  }
4945
+ #finTraderStatus::before { content: '● '; font-size: 9px; color: var(--muted); }
4946
+ #finTraderStatus.on { color: var(--text); border-color: var(--accent); }
4947
+ #finTraderStatus.on::before { color: #22c55e; }
4948
+ #finTraderStatus.on.busy { color: var(--accent); }
4949
+ #finTraderStatus.on.busy::before { color: #f59e0b; }
4930
4950
 
4931
- #finPosList, #finSymList { display: flex; flex-direction: column; gap: 5px; }
4951
+ #finPosList, #finOrdList, #finSymList { display: flex; flex-direction: column; gap: 5px; }
4932
4952
  .fin-empty { font-size: 11px; color: var(--muted); padding: 2px 0 4px; }
4933
4953
 
4934
4954
  .fin-pos {
@@ -4967,6 +4987,8 @@ body.finance-mode #composerWrap { margin-right: var(--finW, 400px); }
4967
4987
  border-radius: 5px;
4968
4988
  }
4969
4989
  .fin-pos .fp-close:hover { background: rgba(239, 68, 68, 0.15); color: #ef4444; }
4990
+ /* bekleyen emir satırı: amber sol şerit ile pozisyondan ayrışır */
4991
+ .fin-pos.fin-ord { border-left: 2px solid #f59e0b; }
4970
4992
 
4971
4993
  .fin-sym {
4972
4994
  display: flex;