beast-agent 2.18.0 → 2.19.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 +1 -1
- package/src/agent/engine.js +155 -10
- package/src/agent/skills.js +1 -1
- package/src/main.js +336 -62
- package/src/preload.js +3 -0
- package/src/renderer/i18n.js +4 -0
- package/src/renderer/index.html +15 -0
- package/src/renderer/renderer.js +192 -1
- package/src/renderer/style.css +148 -0
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -359,6 +359,16 @@ class Engine {
|
|
|
359
359
|
/* #17 kalıcı ajan geçmişi: restart sonrası işler + sohbetler kaybolmasın */
|
|
360
360
|
this._bgJobsFile = path.join(this.sessionsDir, 'bg-jobs.json');
|
|
361
361
|
this._loadBgJobsPersisted();
|
|
362
|
+
/* AJAN DM: paralel/finance ajanlarının birbirine attığı mesajlar
|
|
363
|
+
(AJAN DM paneli buradan beslenir; restart sonrası da durur) */
|
|
364
|
+
this._agentDms = [];
|
|
365
|
+
this._agentDmsFile = path.join(this.sessionsDir, 'agent-dms.json');
|
|
366
|
+
try {
|
|
367
|
+
const raw = JSON.parse(fs.readFileSync(this._agentDmsFile, 'utf8'));
|
|
368
|
+
this._agentDms = Array.isArray(raw.dms) ? raw.dms : [];
|
|
369
|
+
} catch {
|
|
370
|
+
this._agentDms = [];
|
|
371
|
+
}
|
|
362
372
|
/* superyorizyon döngüsü: takılan/uzun süren ajanı CEO'ya bildirir */
|
|
363
373
|
this._supTimer = setInterval(() => {
|
|
364
374
|
try { this._supervise(); } catch {}
|
|
@@ -1835,14 +1845,23 @@ class Engine {
|
|
|
1835
1845
|
const skList = (() => {
|
|
1836
1846
|
try { return skills.scan(); } catch { return []; }
|
|
1837
1847
|
})();
|
|
1848
|
+
/* CHATTEKI BEAST KİMLİĞİ: finance ajanı da sahibin SOUL/USER/KURALLARINI taşır */
|
|
1849
|
+
const mem = memory.loadAll();
|
|
1850
|
+
const rules = memory.listRules();
|
|
1851
|
+
const isWorker = !!(session && session.bgJob && !isTrader);
|
|
1852
|
+
const modeBlock2 = isWorker
|
|
1853
|
+
? 'ROL: FİNANS İŞÇİSİ 🔎 — TEK bir görev için piyasa verisi toplar, analiz eder, RAPOR döndürürsün. mt5_trade/mt5_pending/mt5_close KULLANMA — yalnız okuma araçları (mt5_status/account/market/positions/orders/history). İş bitince kısa final raporu yaz.'
|
|
1854
|
+
: modeBlock;
|
|
1838
1855
|
return (
|
|
1856
|
+
(mem.soul ? mem.soul.trim() + '\n\n' : '') +
|
|
1839
1857
|
'Sen BEAST FİNANS\u2019sın — bilgisayardaki MetaTrader 5 (MT5) terminaline köprüyle bağlı trading ajanı.\n' +
|
|
1840
1858
|
'ORTAM: Beast Finance uygulamasındasın — solda sohbet geçmişi + izleyiciler, ortada bu sohbet, SAĞ panelde MT5 işlem panosu (hesap, pozisyonlar, piyasa, AKIŞ) var.\n' +
|
|
1841
1859
|
'KÖPRÜ: MT5 terminaline Python stdio köprüsüyle bağlısın; panel 3 saniyede bir hesap/pozisyon/piyasa verisini tazeler. mt5_* araç çağrıların ve işlem hareketlerin (açılan/kapanan pozisyonlar) SAĞ paneldeki AKIŞ akışına canlı düşer — panelin orayı izlediğini bil.\n' +
|
|
1842
1860
|
'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' +
|
|
1843
|
-
|
|
1861
|
+
modeBlock2 + '\n' +
|
|
1844
1862
|
'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' +
|
|
1845
1863
|
'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' +
|
|
1864
|
+
'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").\n' +
|
|
1846
1865
|
(symbols ? `İZLEME LİSTESİ: ${symbols}\n` : '') +
|
|
1847
1866
|
(lim.maxLot ? `LİMİTLER: max lot ${lim.maxLot}` + (lim.maxPositions ? ` · max eşzamanlı pozisyon ${lim.maxPositions}` : '') + ' — bunları aşıp araç kullanma; sistem zaten reddeder.\n' : '') +
|
|
1848
1867
|
'RİSK DİSİPLİNİ:\n' +
|
|
@@ -1851,6 +1870,8 @@ class Engine {
|
|
|
1851
1870
|
'- Emin olmadığında İŞLEM YOK — "BEKLE: <sebep>" yaz. Sık işlem > iyi işlem.\n' +
|
|
1852
1871
|
'- Hesap kaldıracı ve serbest marja göre pozisyon boyutunu düşük tut; tek işlemde serbest marjın büyük kısmını riske atma.\n' +
|
|
1853
1872
|
(session && session.financeStrategy ? `SAHİBİNİN STRATEJİ NOTU (önceliklidir):\n${session.financeStrategy}\n` : '') +
|
|
1873
|
+
(mem.user ? `# USER\n${mem.user}\n` : '') +
|
|
1874
|
+
(rules.length ? `# KURALLAR (sahibinin kalıcı talimatları — daima uy)\n${rules.map((r) => '- ' + r).join('\n')}\n` : '') +
|
|
1854
1875
|
(skList.length
|
|
1855
1876
|
? '# SKILLS\nKurulu skill kataloğu: görev bir skill\u2019in tanımına uyarsa skill aracıyla gövdesini (SKILL.md) yükleyip uygula — tekerleği yeniden icat etme. Analysis/rapor/PDF gibi işlerde skill\u2019in prosedürüne uy.\n' +
|
|
1856
1877
|
skList.map((s) => `- ${s.name}: ${s.description} [${s.path}]`).join('\n') + '\n'
|
|
@@ -2797,6 +2818,15 @@ class Engine {
|
|
|
2797
2818
|
if (adef && (adef.mode === 'bg' || adef.mode === 'all')) bg.agentName = adef.name;
|
|
2798
2819
|
}
|
|
2799
2820
|
this.cache.set(bg.id, bg);
|
|
2821
|
+
/* FİNANS İŞÇİSİ: parent finance oturumuyken (Beast Finance sohbeti) açılan
|
|
2822
|
+
arka plan işi mt5_* okuma araçlarını görür ve finance promptuyla koşar —
|
|
2823
|
+
chat Beast'i Finance'tan paralel finance ajanı açabilir */
|
|
2824
|
+
const parentSess = parent ? this.cache.get(parent) || null : null;
|
|
2825
|
+
if (parentSess && parentSess.finance) {
|
|
2826
|
+
bg.finance = true;
|
|
2827
|
+
bg.financeTrader = false; /* işçi — asla otonom trade, yalnız analiz/rapor */
|
|
2828
|
+
bg.financeAuto = false;
|
|
2829
|
+
}
|
|
2800
2830
|
const ttl = String(title || t).replace(/\s+/g, ' ').trim().slice(0, 80) || 'arka plan görevi';
|
|
2801
2831
|
/* #17 cache'teki nesneye de işaret koy — listSessions temiz başlık göstersin */
|
|
2802
2832
|
bg.bgTitle = ttl;
|
|
@@ -3051,10 +3081,19 @@ class Engine {
|
|
|
3051
3081
|
return { ok: true, job, messages: tail.slice(-30) };
|
|
3052
3082
|
}
|
|
3053
3083
|
|
|
3054
|
-
/* İş bitişi kaydı: status=done|error|aborted. Hata olursa üst sohbete haber gider.
|
|
3084
|
+
/* İş bitişi kaydı: status=done|error|aborted. Hata olursa üst sohbete haber gider.
|
|
3085
|
+
SÜREKLİ (continuous) işler — Finance trade ajanları gibi tur tur koşanlar —
|
|
3086
|
+
'done'/'error'da KAPANMAZ: tur sonu = aktivite tazelenir, iş koşmaya devam
|
|
3087
|
+
eder (döngüyü main yönetir). Yalnız 'aborted' (kullanıcı iptali) gerçek bitiştir. */
|
|
3055
3088
|
_bgFinish(sid, status, errorMsg) {
|
|
3056
3089
|
const job = this._bgJobs.get(String(sid));
|
|
3057
3090
|
if (!job || job.status !== 'running') return;
|
|
3091
|
+
if (job.continuous && (status === 'done' || status === 'error')) {
|
|
3092
|
+
job.lastActivityAt = nowIso();
|
|
3093
|
+
if (status === 'error') job.errors = (job.errors || 0) + 1;
|
|
3094
|
+
this._bgEmit();
|
|
3095
|
+
return;
|
|
3096
|
+
}
|
|
3058
3097
|
job.status = status;
|
|
3059
3098
|
job.revive = false;
|
|
3060
3099
|
job.endedAt = nowIso();
|
|
@@ -3122,9 +3161,11 @@ class Engine {
|
|
|
3122
3161
|
job.lastActivityAt = nowIso();
|
|
3123
3162
|
}
|
|
3124
3163
|
|
|
3125
|
-
/* Saf sınıflandırma (test edilebilir): null | 'stuck' | 'long'
|
|
3164
|
+
/* Saf sınıflandırma (test edilebilir): null | 'stuck' | 'long'
|
|
3165
|
+
continuous işler (Finance trade ajanları) denetlenmez — turlar arası
|
|
3166
|
+
boşluk normaldir; döngüyü main yönetir. */
|
|
3126
3167
|
static superviseReason(job, nowMs) {
|
|
3127
|
-
if (!job || job.status !== 'running') return null;
|
|
3168
|
+
if (!job || job.status !== 'running' || job.continuous) return null;
|
|
3128
3169
|
const started = Date.parse(job.startedAt || '') || nowMs;
|
|
3129
3170
|
const lastAct = Date.parse(job.lastActivityAt || '') || started;
|
|
3130
3171
|
const runMin = (nowMs - started) / 60000;
|
|
@@ -3365,6 +3406,7 @@ class Engine {
|
|
|
3365
3406
|
`- Bağımsız araç çağrılarını aynı turda PARALEL ver.\n` +
|
|
3366
3407
|
`- PDF gerekirse pip\u2019ten paket kurma (Türkçe bozar); Node\u2019un kurulu \`pdf-lib\`+fontkit\u2019iyle .js script yazıp \`node\` ile çalıştır. md→pdf çevirme: belge çıktısını doğrudan PDF olarak üret.\n` +
|
|
3367
3408
|
`- ARAŞTIRMA SINIRI: 3-5 kaynak yeter; süre hedefi ~3 dakika. 2-3 denemede bulunamayan bilgiyi BIRAK — bulabildiğin kısmi sonucu raporla ve neyi bulamadığını yaz. Kapalı/gizli içerik peşinde koşma.\n` +
|
|
3409
|
+
`AJAN KOORDİNASYONU: diğer koşan ajanlarla (paralel işler, finance ajanları) konuşman gerekiyorsa agent_dm aracını kullan — to: hedefin başlığındaki anahtar kelime (örn "GOLD", "Trader"), message: 1-3 cümlelik net mesaj. Senden istenen görevde başka bir ajanın zaten yaptığın işe ihtiyacı varsa DM ile haber ver.\n` +
|
|
3368
3410
|
FORMAT_RULES + '\n' +
|
|
3369
3411
|
`SON ÇIKTI: 3-5 satırlık net sonuç raporu, madde madde. Soru sorma, sohbet etme.`
|
|
3370
3412
|
);
|
|
@@ -3421,6 +3463,11 @@ class Engine {
|
|
|
3421
3463
|
);
|
|
3422
3464
|
if (skillDef) toolsList = [...toolsList, skillDef];
|
|
3423
3465
|
}
|
|
3466
|
+
/* AJAN DM: koşan ajan oturumlarına (arka plan işleri + finance
|
|
3467
|
+
ajanları) ajanlar-arası mesajlaşma aracı verilir */
|
|
3468
|
+
if (session && (session.bgJob || (session.finance && session.bgTitle))) {
|
|
3469
|
+
toolsList = [...toolsList, AGENT_DM_DEF];
|
|
3470
|
+
}
|
|
3424
3471
|
/* panel_run yalnız SANDBOX oturumlarında görünsün — diğer panellerde
|
|
3425
3472
|
modelin alet çantasında olmasın (hook olmadan çalışmaz) */
|
|
3426
3473
|
if (!(session && session.sbSandbox)) {
|
|
@@ -3430,12 +3477,12 @@ class Engine {
|
|
|
3430
3477
|
/* opencode agent.ts port: özel ajan tanımı — prompt/model/araç/steps */
|
|
3431
3478
|
const adef = this._agentDefFor(session, !!session.bgJob);
|
|
3432
3479
|
const promptText = this._lastUserText(session);
|
|
3433
|
-
let system = session.
|
|
3434
|
-
? this.
|
|
3435
|
-
: session.
|
|
3436
|
-
? this.
|
|
3437
|
-
: session.
|
|
3438
|
-
? this.
|
|
3480
|
+
let system = session.finance
|
|
3481
|
+
? this.buildFinanceSystem(session) /* finance önceliklidir — bg finance işçileri de finance promptuyla koşar */
|
|
3482
|
+
: session.bgJob
|
|
3483
|
+
? this.buildBgSystem(session)
|
|
3484
|
+
: session.studio
|
|
3485
|
+
? this.buildStudioSystem(session)
|
|
3439
3486
|
: bcAgent
|
|
3440
3487
|
? this.buildOcSystem(session, bcAgent)
|
|
3441
3488
|
: await this.buildSystem(promptText, session);
|
|
@@ -4714,6 +4761,10 @@ const skills = require('./skills');
|
|
|
4714
4761
|
if (name === 'skill') {
|
|
4715
4762
|
return JSON.stringify(this._skillBody(args));
|
|
4716
4763
|
}
|
|
4764
|
+
/* AJAN DM: ajanlar arası mesaj — hedef koşan ajanın oturumuna düşer */
|
|
4765
|
+
if (name === 'agent_dm') {
|
|
4766
|
+
return JSON.stringify(this._agentDmSend(sessionId, args));
|
|
4767
|
+
}
|
|
4717
4768
|
if (isBc) {
|
|
4718
4769
|
/* opencode permission akışı (tool/external-directory.ts + her aracın
|
|
4719
4770
|
ctx.ask'ı): önce workspace DIŞI erişim izni, sonra araç izni.
|
|
@@ -5235,6 +5286,26 @@ function emitSafe(engine, sessionId, ev) {
|
|
|
5235
5286
|
engine.emit({ ...ev, sessionId });
|
|
5236
5287
|
}
|
|
5237
5288
|
|
|
5289
|
+
/* AJAN DM aracı: paralel/finance ajanlarının birbirine kısa mesaj atması.
|
|
5290
|
+
Salt metin — hedef ajanın oturumuna [AJAN DM] bloğu olarak düşer; AJAN DM
|
|
5291
|
+
panelinde iki yönlü görünür. */
|
|
5292
|
+
const AGENT_DM_DEF = {
|
|
5293
|
+
type: 'function',
|
|
5294
|
+
function: {
|
|
5295
|
+
name: 'agent_dm',
|
|
5296
|
+
description:
|
|
5297
|
+
'Send a short DM to another RUNNING agent (parallel agents / finance agents) to coordinate: share findings, ask status, warn about risk, hand off work. `to` = target session id OR a keyword from the agent title (e.g. "GOLD", "Trader"). The message is delivered to the other agent and shown in the Agent DM panel.',
|
|
5298
|
+
parameters: {
|
|
5299
|
+
type: 'object',
|
|
5300
|
+
properties: {
|
|
5301
|
+
to: { type: 'string', description: 'Target agent session id or a title keyword' },
|
|
5302
|
+
message: { type: 'string', description: 'Short message (1-3 sentences)' },
|
|
5303
|
+
},
|
|
5304
|
+
required: ['to', 'message'],
|
|
5305
|
+
},
|
|
5306
|
+
},
|
|
5307
|
+
};
|
|
5308
|
+
|
|
5238
5309
|
const TOOLS = [
|
|
5239
5310
|
...tools.definitions,
|
|
5240
5311
|
{
|
|
@@ -6015,6 +6086,80 @@ Engine.prototype.clearTodos = function (sid) {
|
|
|
6015
6086
|
return { ok: true, count: prev.length };
|
|
6016
6087
|
};
|
|
6017
6088
|
|
|
6089
|
+
/* ---------- AJAN DM: ajanların birbirine mesajı ---------- */
|
|
6090
|
+
|
|
6091
|
+
/* Ajan DM gönder: hedef koşan paralel/finance ajanı (id YA DA başlık
|
|
6092
|
+
anahtar kelimesi). Teslim _pendingReports kuyruğuyla yapılır — hedef
|
|
6093
|
+
meşgulse mesaj done olunca düşer, kaybolmaz. Panel agent-dms.json'dan. */
|
|
6094
|
+
Engine.prototype._agentDmSend = function (fromSid, args) {
|
|
6095
|
+
try {
|
|
6096
|
+
const to = String((args && args.to) || '').trim();
|
|
6097
|
+
const text = String((args && args.message) || '').trim().slice(0, 1200);
|
|
6098
|
+
if (!text) return { ok: false, error: 'mesaj boş' };
|
|
6099
|
+
const jobs = this._bgJobs || new Map();
|
|
6100
|
+
let target = jobs.has(to) ? to : '';
|
|
6101
|
+
if (!target) {
|
|
6102
|
+
const q = to.toLowerCase();
|
|
6103
|
+
for (const [sid, j] of jobs) {
|
|
6104
|
+
if (sid === String(fromSid)) continue;
|
|
6105
|
+
if (j && j.title && String(j.title).toLowerCase().includes(q)) {
|
|
6106
|
+
target = sid;
|
|
6107
|
+
break;
|
|
6108
|
+
}
|
|
6109
|
+
}
|
|
6110
|
+
}
|
|
6111
|
+
if (!target || !jobs.has(target)) {
|
|
6112
|
+
const titles = [...jobs.values()]
|
|
6113
|
+
.filter((j) => j && j.status === 'running' && j.title)
|
|
6114
|
+
.map((j) => j.title)
|
|
6115
|
+
.slice(0, 8);
|
|
6116
|
+
return {
|
|
6117
|
+
ok: false,
|
|
6118
|
+
error: 'hedef ajan bulunamadı: "' + to + '"' + (titles.length ? ' — koşan ajanlar: ' + titles.join(', ') : ''),
|
|
6119
|
+
};
|
|
6120
|
+
}
|
|
6121
|
+
if (String(target) === String(fromSid)) return { ok: false, error: 'kendine DM atılamaz' };
|
|
6122
|
+
const fromJob = jobs.get(String(fromSid));
|
|
6123
|
+
const toJob = jobs.get(target);
|
|
6124
|
+
const dm = {
|
|
6125
|
+
at: new Date().toISOString(),
|
|
6126
|
+
from: String(fromSid),
|
|
6127
|
+
fromTitle: (fromJob && fromJob.title) || 'Ajan',
|
|
6128
|
+
to: String(target),
|
|
6129
|
+
toTitle: (toJob && toJob.title) || 'Ajan',
|
|
6130
|
+
text,
|
|
6131
|
+
};
|
|
6132
|
+
this._agentDms = this._agentDms || [];
|
|
6133
|
+
this._agentDms.push(dm);
|
|
6134
|
+
if (this._agentDms.length > 400) this._agentDms.splice(0, this._agentDms.length - 400);
|
|
6135
|
+
try {
|
|
6136
|
+
fs.writeFileSync(this._agentDmsFile, JSON.stringify({ dms: this._agentDms }, null, 2));
|
|
6137
|
+
} catch {}
|
|
6138
|
+
emitSafe(this, target, { type: 'agent-dm', ...dm });
|
|
6139
|
+
/* teslim: hedef meşgulse pending kuyruğunda bekler, done olunca düşer */
|
|
6140
|
+
(this._pendingReports = this._pendingReports || []).push({
|
|
6141
|
+
parentId: target,
|
|
6142
|
+
text:
|
|
6143
|
+
`[AJAN DM — ${dm.fromTitle}]\n${text}\n` +
|
|
6144
|
+
`(Cevabını agent_dm aracıyla ver — to: "${dm.fromTitle}")`,
|
|
6145
|
+
});
|
|
6146
|
+
this.flushPendingReports(target);
|
|
6147
|
+
return { ok: true, to: target, toTitle: dm.toTitle };
|
|
6148
|
+
} catch (e) {
|
|
6149
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
6150
|
+
}
|
|
6151
|
+
};
|
|
6152
|
+
|
|
6153
|
+
Engine.prototype.agentDmsList = function () {
|
|
6154
|
+
return [...(this._agentDms || [])].slice(-200);
|
|
6155
|
+
};
|
|
6156
|
+
|
|
6157
|
+
Engine.prototype.agentDmsClear = function () {
|
|
6158
|
+
this._agentDms = [];
|
|
6159
|
+
try { fs.rmSync(this._agentDmsFile, { force: true }); } catch {}
|
|
6160
|
+
return { ok: true };
|
|
6161
|
+
};
|
|
6162
|
+
|
|
6018
6163
|
module.exports = Engine;
|
|
6019
6164
|
module.exports.Engine = Engine;
|
|
6020
6165
|
module.exports.sanitizeTodoItems = sanitizeTodoItems;
|
package/src/agent/skills.js
CHANGED
|
@@ -137,7 +137,7 @@ Get-Content "$env:APPDATA\\beast\\sessions\\bg-jobs.json" | ConvertFrom-Json | %
|
|
|
137
137
|
| \`bin/beast-agent.js\` | CLI: \`beast\` (başlat), \`beast update\`, \`beast uninstall\` |
|
|
138
138
|
|
|
139
139
|
## SLASH KOMUTLARI
|
|
140
|
-
/help · /version · /new · /open <kod> · /sessions · /stop · /start · /restart · /change [n] · /model <isim> · /think 0-5 · /clear · /notes · /rule <metin> · /rules · /notify on|off · /screenshot · /approve [always] · /deny · /update [now] · /usage · /backup · /status · /skills
|
|
140
|
+
/help · /version · /new · /open <kod> · /sessions · /stop · /start · /restart · /change [n] · /model <isim> · /think 0-5 · /clear · /autodel [all] (hatırlatmaları sil) · /deltodo [all] (todoları sil) · /cron list|clear|del <id> (WA) · /notes · /rule <metin> · /rules · /notify on|off · /screenshot · /approve [always] · /deny · /update [now] · /usage · /backup · /status · /skills
|
|
141
141
|
|
|
142
142
|
## AYAR SEKMELERİ
|
|
143
143
|
Provider · Fallout · Skills · Paralel Ajanlar · TTS · E-posta · Entegrasyonlar · Web Arama · MCP · Olaylar · Cron · Maliyet · Loglar · Panel · Limitler · Güvenlik · Güncelleme
|
package/src/main.js
CHANGED
|
@@ -5100,6 +5100,57 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
|
|
|
5100
5100
|
desktopEcho(sessionId, '/help', desktopSlashHelp());
|
|
5101
5101
|
return true;
|
|
5102
5102
|
}
|
|
5103
|
+
if (t === '/autodel' || t.startsWith('/autodel ')) {
|
|
5104
|
+
/* /autodel [all] — otomatik hatırlatmaları tek komutla sil (WA ile aynı davranış) */
|
|
5105
|
+
const arg = t.slice(8).trim().toLowerCase();
|
|
5106
|
+
if (arg === 'all' || arg === 'hepsi' || arg === 'hepsini') {
|
|
5107
|
+
const r = cron.clearAll();
|
|
5108
|
+
cronEmit();
|
|
5109
|
+
desktopEcho(sessionId, t, r.ok ? `**Tüm zamanlanmış görevler silindi** (${r.count} adet).` : 'Temizlenemedi.');
|
|
5110
|
+
} else {
|
|
5111
|
+
const r = cron.removeIf(isReminderJob);
|
|
5112
|
+
cronEmit();
|
|
5113
|
+
const left = cron.list().filter((j) => j.enabled).length;
|
|
5114
|
+
desktopEcho(
|
|
5115
|
+
sessionId,
|
|
5116
|
+
t,
|
|
5117
|
+
r.count
|
|
5118
|
+
? `**${r.count} otomatik hatırlatma silindi.** Kalan aktif görev: ${left}`
|
|
5119
|
+
: 'Silinecek otomatik hatırlatma yok — zaten temiz.'
|
|
5120
|
+
);
|
|
5121
|
+
}
|
|
5122
|
+
return true;
|
|
5123
|
+
}
|
|
5124
|
+
if (t === '/deltodo' || t.startsWith('/deltodo ')) {
|
|
5125
|
+
/* /deltodo [all] — todo listelerini temizle (WA ile aynı davranış) */
|
|
5126
|
+
const arg = t.slice(8).trim().toLowerCase();
|
|
5127
|
+
if (arg === 'all' || arg === 'hepsi' || arg === 'hepsini') {
|
|
5128
|
+
let n = 0;
|
|
5129
|
+
let items = 0;
|
|
5130
|
+
try {
|
|
5131
|
+
for (const v of engine.listSessions()) {
|
|
5132
|
+
const r = engine.clearTodos(v.id);
|
|
5133
|
+
if (r && r.ok) {
|
|
5134
|
+
n++;
|
|
5135
|
+
items += r.count || 0;
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5138
|
+
} catch {}
|
|
5139
|
+
desktopEcho(sessionId, t, n ? `**Tüm oturumların todoları temizlendi** (${n} oturum, ${items} madde).` : 'Temizlenecek todo yok.');
|
|
5140
|
+
} else {
|
|
5141
|
+
const r = engine.clearTodos(String(sessionId || ''));
|
|
5142
|
+
desktopEcho(
|
|
5143
|
+
sessionId,
|
|
5144
|
+
t,
|
|
5145
|
+
r.ok
|
|
5146
|
+
? r.count
|
|
5147
|
+
? `**${r.count} todo temizlendi.** (tüm oturumlar: /deltodo all)`
|
|
5148
|
+
: 'Todo listesi zaten boş. (tüm oturumlar: /deltodo all)'
|
|
5149
|
+
: 'Oturum bulunamadı — todo listesi yok.'
|
|
5150
|
+
);
|
|
5151
|
+
}
|
|
5152
|
+
return true;
|
|
5153
|
+
}
|
|
5103
5154
|
if (t === '/rules') {
|
|
5104
5155
|
const rs = memory.listRules();
|
|
5105
5156
|
desktopEcho(sessionId, t, rs.length ? '**Kalıcı kurallar:**\n' + rs.map((r0, i) => `${i + 1}. ${r0}`).join('\n') : 'Kalıcı kural yok — ekle: **/rule <metin>**');
|
|
@@ -5294,6 +5345,8 @@ function desktopSlashHelp() {
|
|
|
5294
5345
|
'**/think 0-5** – düşünme seviyesi (0 kapalı · 5 max)',
|
|
5295
5346
|
'**/agent [isim]** – özel ajan bağla / listele (%APPDATA%\\beast\\agents\\*.md)',
|
|
5296
5347
|
'**/clear** – oturum geçmişini gerçekten sil (kod korunur)',
|
|
5348
|
+
'**/autodel** – tüm otomatik hatırlatmaları tek komutla sil · **/autodel all** – cron görevleri dahil hepsi',
|
|
5349
|
+
'**/deltodo** – bu oturumun todo listesini temizle · **/deltodo all** – tüm oturumların todoları',
|
|
5297
5350
|
'**/notes** – bu oturumun notlarını göster',
|
|
5298
5351
|
'**/rule <metin>** – kalıcı kural ekle · **/rules** – listele',
|
|
5299
5352
|
'**/notify on|off** – hata mail bildirimini aç/kapa',
|
|
@@ -5827,9 +5880,18 @@ ipcMain.handle('memory:get', () => memory.loadAll());
|
|
|
5827
5880
|
/* paralel ajanlar: canlı izleme (#14) */
|
|
5828
5881
|
ipcMain.handle('agents:list', () => engine.listBgJobs());
|
|
5829
5882
|
ipcMain.handle('agents:detail', (_e, id) => engine.bgDetail(id));
|
|
5830
|
-
ipcMain.handle('agents:cancel', (_e, id) =>
|
|
5831
|
-
|
|
5832
|
-
|
|
5883
|
+
ipcMain.handle('agents:cancel', (_e, id) => {
|
|
5884
|
+
const sid = String(id || '');
|
|
5885
|
+
/* Finance ajanı (sürekli iş): interrupt yetmez — döngüyü de kapat */
|
|
5886
|
+
if (financeState && financeState.agents && financeState.agents.has(sid)) {
|
|
5887
|
+
finAgentStop(sid, 'kullanıcı Paralel Ajanlar panelinden (×) iptal etti');
|
|
5888
|
+
return { ok: true };
|
|
5889
|
+
}
|
|
5890
|
+
return { ok: !!engine.interrupt(sid, 'kullanıcı Paralel Ajanlar panelinden (×) iptal etti') };
|
|
5891
|
+
});
|
|
5892
|
+
/* AJAN DM paneli: ajanlar arası mesaj arşivi */
|
|
5893
|
+
ipcMain.handle('agent-dms:list', () => (engine ? engine.agentDmsList() : []));
|
|
5894
|
+
ipcMain.handle('agent-dms:clear', () => (engine ? engine.agentDmsClear() : { ok: false }));
|
|
5833
5895
|
ipcMain.handle('ceo:get', () => !!engine.ceoMode);
|
|
5834
5896
|
ipcMain.handle('ceo:set', (_e, v) => {
|
|
5835
5897
|
settings.ceoMode = !!v;
|
|
@@ -8214,13 +8276,13 @@ const financetools = require('./agent/financetools');
|
|
|
8214
8276
|
|
|
8215
8277
|
const financeState = {
|
|
8216
8278
|
mode: false, /* chat oturumları finance bayrağı alıyor mu */
|
|
8217
|
-
traderSid: null,
|
|
8279
|
+
traderSid: null, /* ANA trader (panel kartı buna bağlı) */
|
|
8218
8280
|
traderOn: false,
|
|
8219
|
-
traderTimer: null,
|
|
8220
8281
|
traderRounds: 0,
|
|
8221
8282
|
lastRoundAt: 0,
|
|
8222
8283
|
installing: false,
|
|
8223
8284
|
installTried: false,
|
|
8285
|
+
agents: new Map(), /* sid -> { symbols, main, round, timer } — AYNI ANDA koşan finance ajanları */
|
|
8224
8286
|
};
|
|
8225
8287
|
|
|
8226
8288
|
function financeDir() {
|
|
@@ -8232,7 +8294,10 @@ function financeDir() {
|
|
|
8232
8294
|
function finCfg() {
|
|
8233
8295
|
if (!settings.finance || typeof settings.finance !== 'object') settings.finance = {};
|
|
8234
8296
|
const f = settings.finance;
|
|
8235
|
-
|
|
8297
|
+
/* SADECE kullanıcının eklediği semboller görünür — artık varsayılan sembol YOK
|
|
8298
|
+
(eski davranış EURUSD/XAUUSD/GBPUSD/BTCUSD'yi geri geri getiriyordu) */
|
|
8299
|
+
if (!Array.isArray(f.symbols)) f.symbols = [];
|
|
8300
|
+
if (typeof f.consultChat !== 'boolean') f.consultChat = true; /* her tur öncesi chat ajanından plan */
|
|
8236
8301
|
if (!Number(f.intervalSec)) f.intervalSec = 120;
|
|
8237
8302
|
if (!Number(f.maxLot)) f.maxLot = 0.1;
|
|
8238
8303
|
if (f.maxPositions == null) f.maxPositions = 3;
|
|
@@ -8334,97 +8399,256 @@ mt5bridge.on('log', (m) => {
|
|
|
8334
8399
|
financeLog('[mt5] ' + line.slice(0, 300));
|
|
8335
8400
|
});
|
|
8336
8401
|
|
|
8337
|
-
/* ----------
|
|
8402
|
+
/* ---------- FINANCE AJANLARI (paralel ajan mimarisi) ----------
|
|
8403
|
+
Her finance ajanı = SÜREKLİ (continuous) paralel ajan: Paralel Ajanlar
|
|
8404
|
+
panelinde canlı görünür, hareketleri (tool-start/status) panelde akar,
|
|
8405
|
+
birden fazla ajan AYNI ANDA koşabilir (ana trader + sembol başına ayrı
|
|
8406
|
+
ajanlar). Döngüyü main yönetir; engine 'done'/'error'da sürekli işi
|
|
8407
|
+
KAPATMAZ (_bgFinish continuous dalı), yalnız kullanıcı iptali bitirir. */
|
|
8338
8408
|
|
|
8339
|
-
function
|
|
8340
|
-
|
|
8341
|
-
|
|
8342
|
-
|
|
8343
|
-
|
|
8344
|
-
|
|
8345
|
-
|
|
8346
|
-
|
|
8409
|
+
function finAgentSession(symbols, isMain) {
|
|
8410
|
+
if (isMain) {
|
|
8411
|
+
const sid = financeState.traderSid;
|
|
8412
|
+
if (sid) {
|
|
8413
|
+
try {
|
|
8414
|
+
const s = engine.cache.get(sid);
|
|
8415
|
+
if (s) return s;
|
|
8416
|
+
} catch {}
|
|
8417
|
+
financeState.traderSid = null;
|
|
8418
|
+
}
|
|
8347
8419
|
}
|
|
8348
8420
|
const s = engine._load(engine.createSession().id);
|
|
8349
8421
|
s.messages = s.messages || [];
|
|
8350
|
-
|
|
8422
|
+
const symLabel = symbols && symbols.length ? symbols.join(', ').slice(0, 40) : 'tüm liste';
|
|
8423
|
+
const title = isMain ? 'Beast Finance · Trader' : 'Finance · ' + symLabel;
|
|
8424
|
+
s.bgTitle = title; /* _view.isBg → sohbet geçmişinde gizli */
|
|
8351
8425
|
try {
|
|
8352
8426
|
fs.appendFileSync(
|
|
8353
8427
|
engine._file(s.id),
|
|
8354
|
-
JSON.stringify({ t: 'meta2', bgOf: '', title
|
|
8428
|
+
JSON.stringify({ t: 'meta2', bgOf: '', title, at: new Date().toISOString() }) + '\n'
|
|
8355
8429
|
);
|
|
8356
8430
|
} catch {}
|
|
8357
8431
|
engine.cache.set(s.id, s);
|
|
8358
|
-
engine.markFinance(s.id, true); /* kalıcı finance etiketi (
|
|
8359
|
-
s.workspace = financeDir(); /*
|
|
8360
|
-
financeState.traderSid = s.id;
|
|
8432
|
+
engine.markFinance(s.id, true); /* kalıcı finance etiketi (mt5_* araçları) */
|
|
8433
|
+
s.workspace = financeDir(); /* ajan kendi klasöründe çalışır */
|
|
8434
|
+
if (isMain) financeState.traderSid = s.id;
|
|
8361
8435
|
return s;
|
|
8362
8436
|
}
|
|
8363
8437
|
|
|
8364
|
-
|
|
8438
|
+
/* Ajanı Paralel Ajanlar paneline kaydet — sürekli (continuous) iş olarak.
|
|
8439
|
+
Panelde canlı hareket akışı + × ile durdurma buradan gelir. */
|
|
8440
|
+
function finAgentRegisterBg(s, symbols) {
|
|
8441
|
+
try {
|
|
8442
|
+
if (!engine || !engine._bgJobs) return;
|
|
8443
|
+
const f = finCfg();
|
|
8444
|
+
const symLabel = symbols && symbols.length ? symbols.join(', ').slice(0, 40) : 'tüm izleme listesi';
|
|
8445
|
+
engine._bgJobs.set(String(s.id), {
|
|
8446
|
+
id: s.id,
|
|
8447
|
+
code: s.code,
|
|
8448
|
+
title: s.bgTitle || 'Finance Ajanı',
|
|
8449
|
+
task: `Beast Finance ajanı (${symLabel}) — ${f.allowTrading ? 'otonom tur tur işlem yönetimi' : 'analiz modunda tur tur tarama'}`,
|
|
8450
|
+
agent: null,
|
|
8451
|
+
parentId: '',
|
|
8452
|
+
groupId: null,
|
|
8453
|
+
status: 'running',
|
|
8454
|
+
slot: false, /* eşzamanlılık slotu TÜKETMEZ — ana işleri bloklamaz */
|
|
8455
|
+
continuous: true, /* tur sonları işi KAPATMAZ (_bgFinish) */
|
|
8456
|
+
startedAt: new Date().toISOString(),
|
|
8457
|
+
lastActivityAt: new Date().toISOString(),
|
|
8458
|
+
lastNudgeAt: null,
|
|
8459
|
+
checks: 0,
|
|
8460
|
+
fixes: 0,
|
|
8461
|
+
endedAt: null,
|
|
8462
|
+
error: null,
|
|
8463
|
+
});
|
|
8464
|
+
engine._bgEmit();
|
|
8465
|
+
} catch {}
|
|
8466
|
+
}
|
|
8467
|
+
|
|
8468
|
+
function finApplyTraderFields(s, symbolsOverride) {
|
|
8365
8469
|
const f = finCfg();
|
|
8366
8470
|
s.finance = true;
|
|
8367
8471
|
s.financeTrader = true;
|
|
8368
8472
|
s.financeAuto = f.allowTrading === true;
|
|
8369
|
-
s.financeSymbols = f.symbols;
|
|
8473
|
+
s.financeSymbols = Array.isArray(symbolsOverride) && symbolsOverride.length ? symbolsOverride : f.symbols;
|
|
8370
8474
|
s.financeStrategy = String(f.strategy || '');
|
|
8371
8475
|
s.financeLimits = { maxLot: f.maxLot, maxPositions: f.maxPositions };
|
|
8372
8476
|
engine.cache.set(String(s.id), s);
|
|
8373
8477
|
}
|
|
8374
8478
|
|
|
8375
|
-
|
|
8479
|
+
/* Yeni finance ajanı aç (ana trader YA DA sembol işçisi) */
|
|
8480
|
+
function finAgentCreate(symbols, isMain) {
|
|
8481
|
+
const f = finCfg();
|
|
8482
|
+
const s = finAgentSession(symbols, isMain);
|
|
8483
|
+
const record = {
|
|
8484
|
+
symbols: symbols && symbols.length ? symbols.slice(0, 10) : [...(f.symbols || [])],
|
|
8485
|
+
main: !!isMain,
|
|
8486
|
+
round: 0,
|
|
8487
|
+
timer: null,
|
|
8488
|
+
};
|
|
8489
|
+
financeState.agents.set(String(s.id), record);
|
|
8490
|
+
finApplyTraderFields(s, record.symbols);
|
|
8491
|
+
finAgentRegisterBg(s, record.symbols);
|
|
8492
|
+
return { s, agent: record };
|
|
8493
|
+
}
|
|
8494
|
+
|
|
8495
|
+
/* Ajanı durdur: timer kapat + oturumu kes + paralel ajan kaydını 'aborted' yaz */
|
|
8496
|
+
function finAgentStop(sid, reason) {
|
|
8497
|
+
sid = String(sid || '');
|
|
8498
|
+
const agent = financeState.agents.get(sid);
|
|
8499
|
+
if (!agent) return false;
|
|
8500
|
+
clearTimeout(agent.timer);
|
|
8501
|
+
financeState.agents.delete(sid);
|
|
8502
|
+
if (sid === String(financeState.traderSid || '')) financeState.traderSid = null;
|
|
8503
|
+
try {
|
|
8504
|
+
if (engine && engine.isBusy(sid)) {
|
|
8505
|
+
engine.interrupt(sid, reason || 'kullanıcı finance ajanını durdurdu');
|
|
8506
|
+
}
|
|
8507
|
+
} catch {}
|
|
8508
|
+
try {
|
|
8509
|
+
const j = engine && engine._bgJobs ? engine._bgJobs.get(sid) : null;
|
|
8510
|
+
if (j && j.status === 'running') {
|
|
8511
|
+
j.status = 'aborted';
|
|
8512
|
+
j.endedAt = new Date().toISOString();
|
|
8513
|
+
j.error = String(reason || 'kullanıcı durdurdu').slice(0, 200);
|
|
8514
|
+
engine._bgEmit();
|
|
8515
|
+
}
|
|
8516
|
+
} catch {}
|
|
8517
|
+
finPush('trader', { state: 'idle', rounds: financeState.traderRounds });
|
|
8518
|
+
return true;
|
|
8519
|
+
}
|
|
8520
|
+
|
|
8521
|
+
function finTraderBrief(agent) {
|
|
8376
8522
|
const f = finCfg();
|
|
8523
|
+
const syms = agent && agent.symbols && agent.symbols.length ? agent.symbols.join(', ') : (f.symbols || []).join(', ');
|
|
8524
|
+
const who = agent && agent.main ? 'TRADER' : 'FINANCE AJANI';
|
|
8377
8525
|
return [
|
|
8378
|
-
|
|
8379
|
-
|
|
8526
|
+
`Beast Finance ${who} başlatıldı — ilk tur: strateji çerçeveni kur ve piyasa taramasını yap.`,
|
|
8527
|
+
`Odak semboller: ${syms || '(boş — mt5_status ile terminale bak, mantıklı semboller seç)'}`,
|
|
8380
8528
|
`Tur aralığı: ${f.intervalSec} sn · Max lot: ${f.maxLot} · Max eşzamanlı pozisyon: ${f.maxPositions}`,
|
|
8381
8529
|
f.allowTrading
|
|
8382
8530
|
? 'Otomatik işlem AÇIK: mt5_trade/mt5_close/mt5_modify/mt5_pending kullanabilirsin (limitler sistemce zorlanır).'
|
|
8383
8531
|
: 'Otomatik işlem KAPALI: SADECE analiz + net işlem önerileri yaz (sembol, yön, giriş, SL, TP, sebep); işlem açma.',
|
|
8384
8532
|
f.strategy ? `Sahibinin strateji notu: ${f.strategy}` : 'Strateji notu yok: trend + destek/direnç + momentum ile temel okuma yap.',
|
|
8385
8533
|
'Bu turda: mt5_status → hesap/pozisyon/fiyat verisi → değerlendirme → kararlar (veya BEKLE: sebep) → kısa rapor.',
|
|
8534
|
+
'Diğer finance/paralel ajanlarla koordinasyon için agent_dm aracı var (to: ajan başlığı anahtar kelimesi).',
|
|
8386
8535
|
].join('\n');
|
|
8387
8536
|
}
|
|
8388
8537
|
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8538
|
+
/* Her tur öncesi CHAT AJANINDAN plan iste: aynı motorun odaklı alt-ajanı
|
|
8539
|
+
(_subagent) hesap/pozisyon/strateji bağlamıyla kısa işlem planı üretir.
|
|
8540
|
+
Plan, tur mesajına [ANA AJAN PLANI] bloğu olarak eklenir. */
|
|
8541
|
+
async function finConsultPlan(f, agent, sid) {
|
|
8542
|
+
if (!engine) return '';
|
|
8543
|
+
let account = null;
|
|
8544
|
+
let positions = [];
|
|
8545
|
+
try {
|
|
8546
|
+
if (mt5bridge.running) {
|
|
8547
|
+
const [ac, ps] = await Promise.all([
|
|
8548
|
+
mt5bridge.call('account', {}, 8000).catch(() => null),
|
|
8549
|
+
mt5bridge.call('positions', {}, 8000).catch(() => null),
|
|
8550
|
+
]);
|
|
8551
|
+
if (ac && ac.ok) account = ac.data && ac.data.account;
|
|
8552
|
+
if (ps && ps.ok) positions = (ps.data && ps.data.positions) || [];
|
|
8553
|
+
}
|
|
8554
|
+
} catch {}
|
|
8555
|
+
const auto = f.allowTrading ? 'AÇIK (işlem açabilir)' : 'KAPALI (yalnız öneri)';
|
|
8556
|
+
const task =
|
|
8557
|
+
'Beast Finance ajanı yeni tura giriyor. SEN mt5_* araçlarını KULLANMA, işlem AÇMA — ' +
|
|
8558
|
+
'sana düşen iş: semboller için bu turun kısa işlem PLANINI üretmek ' +
|
|
8559
|
+
'(sembol başına tek satır: AL/SAT/BEKLE + sebep + giriş/SL/TP fikri). ' +
|
|
8560
|
+
'Güncel piyasa bağlamı gerekiyorsa web_search kullan; kurulu bir skill konuyla ilgiliyse skill aracıyla oku.';
|
|
8561
|
+
const ctx = [
|
|
8562
|
+
`Odak semboller: ${agent && agent.symbols && agent.symbols.length ? agent.symbols.join(', ') : (f.symbols || []).join(', ') || '(boş — ajan kendi bulabilir)'}`,
|
|
8563
|
+
`Otomatik işlem: ${auto} · max lot ${f.maxLot} · max eşzamanlı pozisyon ${f.maxPositions}`,
|
|
8564
|
+
account
|
|
8565
|
+
? `Hesap: bakiye ${account.balance} ${account.currency} · özkaynak ${account.equity ?? '?'} · serbest marj ${account.margin_free ?? '?'} · kaldıraç 1:${account.leverage ?? '?'}`
|
|
8566
|
+
: 'Hesap verisi alınamadı.',
|
|
8567
|
+
positions.length
|
|
8568
|
+
? 'Açık pozisyonlar:\n' +
|
|
8569
|
+
positions
|
|
8570
|
+
.slice(0, 10)
|
|
8571
|
+
.map((p) => {
|
|
8572
|
+
const t = String(p.type ?? '');
|
|
8573
|
+
const side = /sell|1/.test(t) ? 'SATIŞ' : 'ALIŞ';
|
|
8574
|
+
return `- ${p.symbol || '?'} ${side} ${p.volume || '?'} lot @ ${p.price_open || '?'} · kâr/zarar ${p.profit ?? '?'}`;
|
|
8575
|
+
})
|
|
8576
|
+
.join('\n')
|
|
8577
|
+
: 'Açık pozisyon yok.',
|
|
8578
|
+
f.strategy ? `Sahibinin strateji notu: ${f.strategy}` : 'Strateji notu yok — temel teknik okuma (trend + destek/direnç + momentum).',
|
|
8579
|
+
'Plan EN FAZLA ~15 satır; planı işlem yapmadan sadece METİN olarak döndür.',
|
|
8580
|
+
].join('\n');
|
|
8581
|
+
try {
|
|
8582
|
+
const signal = typeof AbortSignal !== 'undefined' && AbortSignal.timeout ? AbortSignal.timeout(90000) : undefined;
|
|
8583
|
+
const res = await engine._subagent(task, ctx, signal, sid || null, '');
|
|
8584
|
+
return String(res || '').trim().slice(0, 4000);
|
|
8585
|
+
} catch {
|
|
8586
|
+
return '';
|
|
8587
|
+
}
|
|
8588
|
+
}
|
|
8589
|
+
|
|
8590
|
+
/* Bir ajanın TEK turu: (opsiyonel) ana ajan planı + tur emri */
|
|
8591
|
+
function finAgentRound(sid) {
|
|
8592
|
+
const agent = financeState.agents.get(String(sid));
|
|
8593
|
+
if (!agent || !engine) return;
|
|
8594
|
+
if (engine.isBusy(sid)) {
|
|
8395
8595
|
/* hâlâ çalışıyor — done eventinde tekrar planlanır */
|
|
8396
8596
|
return;
|
|
8397
8597
|
}
|
|
8398
|
-
|
|
8598
|
+
agent.round += 1;
|
|
8599
|
+
if (agent.main) financeState.traderRounds = agent.round;
|
|
8399
8600
|
const f = finCfg();
|
|
8601
|
+
try {
|
|
8602
|
+
const s = engine.cache.get(String(sid));
|
|
8603
|
+
if (s) finApplyTraderFields(s, agent.symbols);
|
|
8604
|
+
} catch {}
|
|
8400
8605
|
const auto = f.allowTrading
|
|
8401
8606
|
? 'İşlem açabilirsin — limitlere uy, SL\u2019siz pozisyon bırakma.'
|
|
8402
8607
|
: 'Otomatik işlem KAPALI — sadece analiz + öneri.';
|
|
8403
|
-
const
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
8409
|
-
|
|
8410
|
-
|
|
8411
|
-
|
|
8412
|
-
|
|
8413
|
-
|
|
8414
|
-
|
|
8608
|
+
const focus = agent.symbols.length ? `Odak: ${agent.symbols.join(', ')}. ` : '';
|
|
8609
|
+
const round = `FINANCE TUR #${agent.round}: ${focus}hesap + pozisyonlar + fiyatları çek; açık pozisyonları yönet (SL/TP güncelle, hedefe ulaşanı kapat); stratejine göre yeni fırsatları değerlendir. ${auto} Kısa rapor ver.`;
|
|
8610
|
+
const launch = (planBlock) => {
|
|
8611
|
+
if (!financeState.agents.has(String(sid))) return;
|
|
8612
|
+
const ok = engine.send(sid, planBlock + round, { userAction: false });
|
|
8613
|
+
if (ok) {
|
|
8614
|
+
financeState.lastRoundAt = Date.now();
|
|
8615
|
+
if (agent.main) finPush('trader', { state: 'running', round: agent.round });
|
|
8616
|
+
} else {
|
|
8617
|
+
/* stop kapısı/yoğunluk — yarım dakika sonra sessizce tekrar dene */
|
|
8618
|
+
clearTimeout(agent.timer);
|
|
8619
|
+
agent.timer = setTimeout(() => { try { finAgentRound(sid); } catch {} }, 30000);
|
|
8620
|
+
}
|
|
8621
|
+
};
|
|
8622
|
+
if (f.consultChat === false) {
|
|
8623
|
+
/* danışma kapalı: tur doğrudan başlar */
|
|
8624
|
+
launch('');
|
|
8625
|
+
return;
|
|
8415
8626
|
}
|
|
8627
|
+
if (agent.main) finPush('trader', { state: 'consult', round: agent.round });
|
|
8628
|
+
finConsultPlan(f, agent, String(sid))
|
|
8629
|
+
.then((plan) => launch(plan ? `[ANA AJAN PLANI — bu turun varsayılan stratejisi; strateji notuyla çelişirse not önceliklidir]\n${plan}\n\n` : ''))
|
|
8630
|
+
.catch(() => launch(''));
|
|
8416
8631
|
}
|
|
8417
8632
|
|
|
8633
|
+
/* Tur/durum sonları: sürekli ajan döngüsünü besle; kullanıcı iptalinde kapat */
|
|
8418
8634
|
function finFlushOnDone(ev) {
|
|
8419
8635
|
if (!ev || (ev.type !== 'done' && ev.type !== 'error')) return;
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
8636
|
+
const sid = String(ev.sessionId || '');
|
|
8637
|
+
const agent = financeState.agents.get(sid);
|
|
8638
|
+
if (!agent) return;
|
|
8639
|
+
/* Paralel Ajanlar panelinden × (interrupt abort'u) → ajanı gerçekten kapat */
|
|
8640
|
+
if (ev.type === 'done' && ev.aborted) {
|
|
8641
|
+
finAgentStop(sid, String(ev.reason || 'kullanıcı durdurdu'));
|
|
8642
|
+
return;
|
|
8643
|
+
}
|
|
8644
|
+
clearTimeout(agent.timer);
|
|
8423
8645
|
const f = finCfg();
|
|
8424
8646
|
const iv = Math.max(30, Number(f.intervalSec) || 120) * 1000;
|
|
8425
8647
|
financeState.lastRoundAt = Date.now();
|
|
8426
|
-
|
|
8427
|
-
|
|
8648
|
+
if (agent.main) {
|
|
8649
|
+
finPush('trader', { state: 'idle', round: agent.round, nextInSec: iv / 1000 });
|
|
8650
|
+
}
|
|
8651
|
+
agent.timer = setTimeout(() => { try { finAgentRound(sid); } catch {} }, iv);
|
|
8428
8652
|
}
|
|
8429
8653
|
|
|
8430
8654
|
ipcMain.handle('finance:state', async () => {
|
|
@@ -8501,7 +8725,17 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
8501
8725
|
const p = patch || {};
|
|
8502
8726
|
if (p.symbols !== undefined) {
|
|
8503
8727
|
const arr = Array.isArray(p.symbols) ? p.symbols : String(p.symbols).split(/[,\s]+/);
|
|
8504
|
-
|
|
8728
|
+
const next = arr.map((s) => String(s).trim().toUpperCase()).filter(Boolean).slice(0, 20);
|
|
8729
|
+
/* SEMBOL GEÇMİŞİ: eklenen/silinen her sembol hatırlanır — izleme listesi
|
|
8730
|
+
tamamen silinse bile semboller geçmişte kalır, seçicide TEK TIKLA
|
|
8731
|
+
tekrar eklenir (brokere search'e gerek kalmaz). */
|
|
8732
|
+
const hist = Array.isArray(f.symbolHistory) ? f.symbolHistory.slice() : [];
|
|
8733
|
+
for (const s of [...(f.symbols || []), ...next]) {
|
|
8734
|
+
const u = String(s).toUpperCase();
|
|
8735
|
+
if (u && !hist.includes(u)) hist.push(u);
|
|
8736
|
+
}
|
|
8737
|
+
f.symbolHistory = hist.slice(-40);
|
|
8738
|
+
f.symbols = next;
|
|
8505
8739
|
}
|
|
8506
8740
|
if (p.intervalSec !== undefined) f.intervalSec = Math.max(30, Math.min(3600, Math.round(Number(p.intervalSec) || 120)));
|
|
8507
8741
|
if (p.maxLot !== undefined) f.maxLot = Math.max(0.01, Math.min(100, Number(p.maxLot) || 0.1));
|
|
@@ -8531,36 +8765,76 @@ ipcMain.handle('finance:trader:start', async () => {
|
|
|
8531
8765
|
if (!engine) return { ok: false, error: 'ajan hazır değil' };
|
|
8532
8766
|
const f = finCfg();
|
|
8533
8767
|
if (!engine.publicState().hasModel && !f.traderSel) return { ok: false, error: 'model yok — Ayarlar → Provider' };
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8768
|
+
/* ANA trader: varsa aynen sürdür, yoksa yeni sürekli ajan aç */
|
|
8769
|
+
let mainSid = '';
|
|
8770
|
+
let mainAgent = null;
|
|
8771
|
+
for (const [sid, a] of financeState.agents) {
|
|
8772
|
+
if (a.main) {
|
|
8773
|
+
mainSid = sid;
|
|
8774
|
+
mainAgent = a;
|
|
8775
|
+
break;
|
|
8776
|
+
}
|
|
8777
|
+
}
|
|
8778
|
+
if (!mainSid) {
|
|
8779
|
+
const created = finAgentCreate([], true);
|
|
8780
|
+
mainSid = String(created.s.id);
|
|
8781
|
+
mainAgent = created.agent;
|
|
8782
|
+
}
|
|
8783
|
+
try { engine.setSessionModel(mainSid, f.traderSel || null); } catch {}
|
|
8537
8784
|
financeState.traderOn = true;
|
|
8538
|
-
financeState.traderRounds = 0;
|
|
8539
8785
|
try { engine.clearStop(); } catch {}
|
|
8540
|
-
const ok = engine.send(
|
|
8786
|
+
const ok = engine.send(mainSid, finTraderBrief(mainAgent), { userAction: true });
|
|
8541
8787
|
if (!ok) {
|
|
8542
8788
|
financeState.traderOn = false;
|
|
8543
8789
|
return { ok: false, error: 'trader oturumu meşgul — birkaç saniye sonra tekrar dene' };
|
|
8544
8790
|
}
|
|
8545
8791
|
financeState.lastRoundAt = Date.now();
|
|
8546
8792
|
financeLog('[trader] başlatıldı (model: ' + (f.traderSel || 'genel aktif model') + ')');
|
|
8547
|
-
finPush('trader', { state: 'running', round:
|
|
8548
|
-
return { ok: true, sid:
|
|
8793
|
+
finPush('trader', { state: 'running', round: mainAgent.round });
|
|
8794
|
+
return { ok: true, sid: mainSid };
|
|
8549
8795
|
});
|
|
8550
8796
|
|
|
8551
8797
|
ipcMain.handle('finance:trader:stop', async () => {
|
|
8552
8798
|
financeState.traderOn = false;
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8799
|
+
/* ANA trader'ı durdur (sembol işçileri rail × ile ayrı durdurulur) */
|
|
8800
|
+
let mainSid = '';
|
|
8801
|
+
for (const [sid, a] of financeState.agents) {
|
|
8802
|
+
if (a.main) {
|
|
8803
|
+
mainSid = sid;
|
|
8804
|
+
break;
|
|
8805
|
+
}
|
|
8558
8806
|
}
|
|
8807
|
+
const stopped = mainSid ? finAgentStop(mainSid, 'kullanıcı Beast Finance trader ajanını durdurdu') : false;
|
|
8808
|
+
const interrupted = !!stopped;
|
|
8559
8809
|
financeLog('[trader] durduruldu');
|
|
8560
8810
|
finPush('trader', { state: 'stopped' });
|
|
8561
8811
|
return { ok: true, interrupted };
|
|
8562
8812
|
});
|
|
8563
8813
|
|
|
8814
|
+
/* SEMBOL İŞÇİSİ: PİYASA kartındaki ▶ ile sembol başına AYRI finance ajanı —
|
|
8815
|
+
hepsi AYNI ANDA koşar, Paralel Ajanlar panelinde canlı görünür */
|
|
8816
|
+
ipcMain.handle('finance:agent:spawn', async (_e, payload) => {
|
|
8817
|
+
if (!engine) return { ok: false, error: 'ajan hazır değil' };
|
|
8818
|
+
const f = finCfg();
|
|
8819
|
+
if (!engine.publicState().hasModel && !f.traderSel) return { ok: false, error: 'model yok — Ayarlar → Provider' };
|
|
8820
|
+
const symbol = String((payload && payload.symbol) || '').trim().toUpperCase();
|
|
8821
|
+
if (!symbol) return { ok: false, error: 'sembol gerekli' };
|
|
8822
|
+
for (const [, a] of financeState.agents) {
|
|
8823
|
+
if (!a.main && a.symbols.length === 1 && a.symbols[0] === symbol) {
|
|
8824
|
+
return { ok: false, error: symbol + ' için ajan zaten koşuyor (Paralel Ajanlar panelinden durdurabilirsin)' };
|
|
8825
|
+
}
|
|
8826
|
+
}
|
|
8827
|
+
const created = finAgentCreate([symbol], false);
|
|
8828
|
+
try { engine.setSessionModel(created.s.id, f.traderSel || null); } catch {}
|
|
8829
|
+
const ok = engine.send(created.s.id, finTraderBrief(created.agent), { userAction: true });
|
|
8830
|
+
if (!ok) {
|
|
8831
|
+
finAgentStop(String(created.s.id), 'oturum meşgul — ajan başlatılamadı');
|
|
8832
|
+
return { ok: false, error: 'ajan oturumu meşgul — birkaç saniye sonra tekrar dene' };
|
|
8833
|
+
}
|
|
8834
|
+
financeLog('[ajan] ' + symbol + ' işçisi başlatıldı');
|
|
8835
|
+
return { ok: true, sid: created.s.id, title: created.s.bgTitle };
|
|
8836
|
+
});
|
|
8837
|
+
|
|
8564
8838
|
ipcMain.handle('finance:connect', async () => {
|
|
8565
8839
|
mt5bridge.stop();
|
|
8566
8840
|
const st = financeEnsureBridge();
|
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'),
|
package/src/renderer/i18n.js
CHANGED
|
@@ -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',
|
|
@@ -663,6 +665,7 @@
|
|
|
663
665
|
tipThink: 'Reasoning level',
|
|
664
666
|
tipPickCfg: 'Models shown in picker',
|
|
665
667
|
tipRail: 'Parallel Agents Console',
|
|
668
|
+
tipAgentDm: 'Agent DM — agent-to-agent chat',
|
|
666
669
|
tipNet: 'Internet status',
|
|
667
670
|
tip_net_online: 'Internet: connected',
|
|
668
671
|
tip_net_offline: 'Internet: offline — work resumes automatically once the connection returns',
|
|
@@ -704,6 +707,7 @@
|
|
|
704
707
|
tipThemeLight: 'Light theme',
|
|
705
708
|
tipLang: 'Dil değiştir / Switch language',
|
|
706
709
|
labelParallel: 'Parallel Agents',
|
|
710
|
+
labelAgentDm: 'Agent DM',
|
|
707
711
|
set_title: 'SETTINGS',
|
|
708
712
|
tab_lang: 'Language',
|
|
709
713
|
lang_sub: 'Interface language — app UI and voice command language',
|
package/src/renderer/index.html
CHANGED
|
@@ -94,6 +94,7 @@
|
|
|
94
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>
|
|
95
95
|
<button id="sbBtn" class="dd-btn dd-icon-btn" title="Beast Sandbox — GitHub reposu indir & 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>
|
|
96
96
|
<button id="railBtn" class="dd-btn dd-icon-btn" title="Paralel Ajan Konsolu" data-i18n-title="tipRail">▤</button>
|
|
97
|
+
<button id="dmBtn" class="dd-btn dd-icon-btn" title="Ajan DM — ajanlar arası konuşma" data-i18n-title="tipAgentDm">✉︎</button>
|
|
97
98
|
<button id="watchBtn" class="dd-btn dd-icon-btn" title="İzleyiciler (Watchers)" data-i18n-title="tipWatch">◎</button>
|
|
98
99
|
<button id="cronBtn" class="dd-btn dd-icon-btn" title="Cron Görevler" data-i18n-title="tipCron">⏱︎</button>
|
|
99
100
|
<button id="termCBtn" class="dd-btn dd-icon-btn" title="CMD terminali" data-i18n-title="tipTermC">❯︎</button>
|
|
@@ -172,6 +173,7 @@
|
|
|
172
173
|
<div class="fin-card-title">
|
|
173
174
|
PİYASA
|
|
174
175
|
<span class="file-spacer"></span>
|
|
176
|
+
<button id="finWatchClear" title="İzleme listesini TAMAMEN sil — semboller geçmişe düşer, sonra tek tıkla geri eklenir">✕︎</button>
|
|
175
177
|
<button id="finSymAdd" title="Sembol ekle/çıkar — MT5 sembol listesinden seç">+</button>
|
|
176
178
|
</div>
|
|
177
179
|
<div id="finSymList"><div class="fin-empty">—</div></div>
|
|
@@ -346,6 +348,19 @@
|
|
|
346
348
|
<span class="foot-label2" data-i18n="labelParallel">Paralel Ajanlar</span>
|
|
347
349
|
</div>
|
|
348
350
|
</aside>
|
|
351
|
+
|
|
352
|
+
<aside id="dmRail" class="dm-hidden">
|
|
353
|
+
<div id="dmRailHead">
|
|
354
|
+
<span id="dmRailTitle" data-i18n="labelAgentDm">AJAN DM</span>
|
|
355
|
+
<button id="dmClear" title="DM geçmişini temizle" aria-label="DM geçmişini temizle">
|
|
356
|
+
<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>
|
|
357
|
+
</button>
|
|
358
|
+
</div>
|
|
359
|
+
<div id="dmRailList"></div>
|
|
360
|
+
<div id="sideFoot2">
|
|
361
|
+
<span class="foot-label2" data-i18n="labelAgentDm">Ajan DM</span>
|
|
362
|
+
</div>
|
|
363
|
+
</aside>
|
|
349
364
|
</div>
|
|
350
365
|
|
|
351
366
|
<div id="settingsOverlay" hidden>
|
package/src/renderer/renderer.js
CHANGED
|
@@ -42,6 +42,9 @@ 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'),
|
|
@@ -147,6 +150,7 @@ const els = {
|
|
|
147
150
|
finInterval: $('#finInterval'),
|
|
148
151
|
finMaxLot: $('#finMaxLot'),
|
|
149
152
|
finSymBtn: $('#finSymBtn'),
|
|
153
|
+
finWatchClear: $('#finWatchClear'),
|
|
150
154
|
finAllow: $('#finAllow'),
|
|
151
155
|
finTraderBtn: $('#finTraderBtn'),
|
|
152
156
|
finTraderDot: $('#finTraderDot'),
|
|
@@ -4999,6 +5003,113 @@ async function loadRailChat(id) {
|
|
|
4999
5003
|
}
|
|
5000
5004
|
}
|
|
5001
5005
|
|
|
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. */
|
|
5009
|
+
|
|
5010
|
+
const dmState = {
|
|
5011
|
+
dms: [], // { at, from, fromTitle, to, toTitle, text }
|
|
5012
|
+
open: new Set(), // açık thread key'leri
|
|
5013
|
+
};
|
|
5014
|
+
|
|
5015
|
+
function dmPairOf(dm) {
|
|
5016
|
+
const a = String(dm.from || '');
|
|
5017
|
+
const b = String(dm.to || '');
|
|
5018
|
+
return a < b ? a + '|' + b : b + '|' + a;
|
|
5019
|
+
}
|
|
5020
|
+
|
|
5021
|
+
function dmPairTitle(dm) {
|
|
5022
|
+
const a = String(dm.fromTitle || 'Ajan');
|
|
5023
|
+
const b = String(dm.toTitle || 'Ajan');
|
|
5024
|
+
return a < b ? a + ' ↔ ' + b : b + ' ↔ ' + a;
|
|
5025
|
+
}
|
|
5026
|
+
|
|
5027
|
+
function dmTime(iso) {
|
|
5028
|
+
const d = new Date(iso || '');
|
|
5029
|
+
if (!d.getTime()) return '';
|
|
5030
|
+
return d.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
|
|
5031
|
+
}
|
|
5032
|
+
|
|
5033
|
+
function renderDmRail() {
|
|
5034
|
+
const list = els.dmRailList;
|
|
5035
|
+
if (!list) return;
|
|
5036
|
+
if (!dmState.dms.length) {
|
|
5037
|
+
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
|
+
return;
|
|
5039
|
+
}
|
|
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
|
+
list.innerHTML = '';
|
|
5051
|
+
for (const [key, msgs] of rows) {
|
|
5052
|
+
const open = dmState.open.has(key);
|
|
5053
|
+
const last = msgs[msgs.length - 1];
|
|
5054
|
+
const el = document.createElement('div');
|
|
5055
|
+
el.className = 'dmt' + (open ? ' open' : '');
|
|
5056
|
+
el.innerHTML =
|
|
5057
|
+
'<div class="dmt-head">' +
|
|
5058
|
+
`<span class="dmt-title">${escapeHtml(dmPairTitle(last))}</span>` +
|
|
5059
|
+
`<span class="dmt-count">${msgs.length}</span>` +
|
|
5060
|
+
'</div>' +
|
|
5061
|
+
`<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>';
|
|
5071
|
+
el.addEventListener('click', () => {
|
|
5072
|
+
if (dmState.open.has(key)) dmState.open.delete(key);
|
|
5073
|
+
else dmState.open.add(key);
|
|
5074
|
+
renderDmRail();
|
|
5075
|
+
const again = els.dmRailList.querySelector('.dmt.open .dmt-msgs');
|
|
5076
|
+
if (again) again.scrollTop = again.scrollHeight;
|
|
5077
|
+
});
|
|
5078
|
+
list.appendChild(el);
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
|
|
5082
|
+
async function refreshDmRail() {
|
|
5083
|
+
try {
|
|
5084
|
+
const dms = await beast.agentDmsList();
|
|
5085
|
+
if (Array.isArray(dms)) dmState.dms = dms;
|
|
5086
|
+
} catch {}
|
|
5087
|
+
renderDmRail();
|
|
5088
|
+
}
|
|
5089
|
+
|
|
5090
|
+
function toggleDmRail(hide) {
|
|
5091
|
+
const rail = $('#dmRail');
|
|
5092
|
+
if (!rail) return;
|
|
5093
|
+
rail.classList.toggle('dm-hidden', !!hide);
|
|
5094
|
+
if (els.dmBtn) els.dmBtn.classList.toggle('on', !hide);
|
|
5095
|
+
if (!hide) refreshDmRail();
|
|
5096
|
+
}
|
|
5097
|
+
|
|
5098
|
+
if (els.dmBtn) {
|
|
5099
|
+
els.dmBtn.addEventListener('click', () => {
|
|
5100
|
+
toggleDmRail(!$('#dmRail').classList.contains('dm-hidden'));
|
|
5101
|
+
});
|
|
5102
|
+
}
|
|
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');
|
|
5110
|
+
});
|
|
5111
|
+
}
|
|
5112
|
+
|
|
5002
5113
|
function renderAgentsPane() {
|
|
5003
5114
|
const pane = $('#tab-agents');
|
|
5004
5115
|
if (!pane) return;
|
|
@@ -5143,6 +5254,20 @@ function onEvent(ev) {
|
|
|
5143
5254
|
scheduleAgentsRender();
|
|
5144
5255
|
return;
|
|
5145
5256
|
}
|
|
5257
|
+
/* AJAN DM: ajanlar arası mesaj — panel açıksa canlı düşer */
|
|
5258
|
+
if (ev.type === 'agent-dm') {
|
|
5259
|
+
dmState.dms.push({
|
|
5260
|
+
at: ev.at,
|
|
5261
|
+
from: ev.from,
|
|
5262
|
+
fromTitle: ev.fromTitle,
|
|
5263
|
+
to: ev.to,
|
|
5264
|
+
toTitle: ev.toTitle,
|
|
5265
|
+
text: ev.text,
|
|
5266
|
+
});
|
|
5267
|
+
if (dmState.dms.length > 400) dmState.dms.splice(0, dmState.dms.length - 400);
|
|
5268
|
+
if ($('#dmRail') && !$('#dmRail').classList.contains('dm-hidden')) renderDmRail();
|
|
5269
|
+
return;
|
|
5270
|
+
}
|
|
5146
5271
|
/* OFFLINE MESAJ KUYRUĞU: bağlantı + kuyruk olayları (sessionId filtresinden önce) */
|
|
5147
5272
|
if (ev.type === 'net' || ev.type === 'netQueue') { onNetEvent(ev); return; }
|
|
5148
5273
|
/* Sandbox klonlama progress'i (sessionId'siz global olay) */
|
|
@@ -6066,6 +6191,8 @@ const SLASH_COMMANDS = [
|
|
|
6066
6191
|
{ cmd: '/notify', desc: 'hata maili aç/kapa · /notify on|off' },
|
|
6067
6192
|
{ cmd: '/think ', desc: 'düşünme seviyesi · /think 0-5 (0 kapalı)' },
|
|
6068
6193
|
{ cmd: '/clear', desc: 'oturum geçmişini GERÇEKTEN sil (kod korunur)' },
|
|
6194
|
+
{ cmd: '/autodel', desc: 'tüm otomatik hatırlatmaları sil · /autodel all: cron dahil hepsi' },
|
|
6195
|
+
{ cmd: '/deltodo', desc: 'todo listesini temizle · /deltodo all: tüm oturumlar' },
|
|
6069
6196
|
{ cmd: '/start', desc: 'durdurulan servisleri devam ettir' },
|
|
6070
6197
|
{ cmd: '/rule ', desc: 'kalıcı kural ekle' },
|
|
6071
6198
|
{ cmd: '/rules', desc: 'kalıcı kuralları listele' },
|
|
@@ -8342,7 +8469,17 @@ function finRenderSymbols(list) {
|
|
|
8342
8469
|
row.innerHTML =
|
|
8343
8470
|
'<span class="fs-name">' + (s.symbol || '?') + '</span>' +
|
|
8344
8471
|
'<span class="fs-price ' + cls + '">' + bid.toFixed(digits) + '</span>' +
|
|
8345
|
-
'<span class="fs-spread">sp ' + (s.spread != null ? s.spread : '—') + '</span>'
|
|
8472
|
+
'<span class="fs-spread">sp ' + (s.spread != null ? s.spread : '—') + '</span>' +
|
|
8473
|
+
'<button class="fs-agent" title="' + escapeHtml(s.symbol || '') + ' için AYRI finance ajanı başlat (paralel koşar)">▶︎</button>';
|
|
8474
|
+
const spawnBtn = row.querySelector('.fs-agent');
|
|
8475
|
+
if (spawnBtn) {
|
|
8476
|
+
spawnBtn.addEventListener('click', async (e) => {
|
|
8477
|
+
e.stopPropagation();
|
|
8478
|
+
const r = await beast.financeAgentSpawn(s.symbol).catch((err) => ({ ok: false, error: String((err && err.message) || err) }));
|
|
8479
|
+
if (r && r.ok) toast(s.symbol + ' için ajan başladı — Paralel Ajanlar panelinde izle');
|
|
8480
|
+
else toast((r && r.error) || 'Ajan başlatılamadı');
|
|
8481
|
+
});
|
|
8482
|
+
}
|
|
8346
8483
|
els.finSymList.appendChild(row);
|
|
8347
8484
|
}
|
|
8348
8485
|
}
|
|
@@ -8366,6 +8503,10 @@ function finSymBtnUpdate() {
|
|
|
8366
8503
|
function finTraderInputsSet(cfg) {
|
|
8367
8504
|
if (!cfg) return;
|
|
8368
8505
|
if (!finWatchDirty) finWatchSet(cfg.symbols);
|
|
8506
|
+
/* sembol geçmişi (silinenler dahil) — seçicide "SON SEMBOLLER" olarak çıkar */
|
|
8507
|
+
finSymHist = Array.isArray(cfg.symbolHistory)
|
|
8508
|
+
? cfg.symbolHistory.map((s) => String(s).trim().toUpperCase()).filter(Boolean).slice(0, 40)
|
|
8509
|
+
: [];
|
|
8369
8510
|
const ae = document.activeElement;
|
|
8370
8511
|
if (els.finInterval && ae !== els.finInterval) els.finInterval.value = cfg.intervalSec || 120;
|
|
8371
8512
|
if (els.finMaxLot && ae !== els.finMaxLot) els.finMaxLot.value = cfg.maxLot || 0.1;
|
|
@@ -8559,6 +8700,7 @@ if (els.finTraderBtn) {
|
|
|
8559
8700
|
ekle/çıkar. PİYASA kartı yalnız seçili sembolleri gösterir. */
|
|
8560
8701
|
|
|
8561
8702
|
let finWatch = []; /* anlık izleme listesi (snapshot cfg'den senkron) */
|
|
8703
|
+
let finSymHist = []; /* sembol geçmişi — silinenler dahil, seçicide tek tıkla geri ekleme */
|
|
8562
8704
|
let finSymSearchTimer = null;
|
|
8563
8705
|
let finSymRefreshTimer = null;
|
|
8564
8706
|
|
|
@@ -8582,12 +8724,41 @@ function finRenderPickRows(rows, q) {
|
|
|
8582
8724
|
if (!els.finSymPick) return;
|
|
8583
8725
|
els.finSymPick.textContent = '';
|
|
8584
8726
|
const term = String(q || '').trim().toUpperCase();
|
|
8727
|
+
/* arama boşken ÜSTTE geçmiş bölümü: silinen semboller TEK TIKLA geri eklenir */
|
|
8728
|
+
if (!term && finSymHist.length) {
|
|
8729
|
+
const sec = document.createElement('div');
|
|
8730
|
+
sec.className = 'fin-sym-sec';
|
|
8731
|
+
sec.textContent = 'SON SEMBOLLER (sildiklerin dahil — tek tıkla geri ekle)';
|
|
8732
|
+
els.finSymPick.appendChild(sec);
|
|
8733
|
+
for (const name of finSymHist) {
|
|
8734
|
+
const row = document.createElement('div');
|
|
8735
|
+
row.className = 'fin-sym-row';
|
|
8736
|
+
const on = finWatch.includes(name);
|
|
8737
|
+
row.innerHTML =
|
|
8738
|
+
'<span class="fsr-check">' + (on ? '✓' : '') + '</span>' +
|
|
8739
|
+
'<span class="fsr-name">' + escapeHtml(name) + '</span>' +
|
|
8740
|
+
'<span class="fsr-desc">' + (on ? 'izlemede' : 'silinmiş — eklemek için tıkla') + '</span>';
|
|
8741
|
+
if (on) row.classList.add('on');
|
|
8742
|
+
row.addEventListener('click', () => {
|
|
8743
|
+
const added = finToggleWatch(name);
|
|
8744
|
+
row.classList.toggle('on', added);
|
|
8745
|
+
row.querySelector('.fsr-check').textContent = added ? '✓' : '';
|
|
8746
|
+
row.querySelector('.fsr-desc').textContent = added ? 'izlemede' : 'silinmiş — eklemek için tıkla';
|
|
8747
|
+
});
|
|
8748
|
+
els.finSymPick.appendChild(row);
|
|
8749
|
+
}
|
|
8750
|
+
const sep = document.createElement('div');
|
|
8751
|
+
sep.className = 'fin-sym-sec';
|
|
8752
|
+
sep.textContent = 'TÜM MT5 SEMBOLLERİ';
|
|
8753
|
+
els.finSymPick.appendChild(sep);
|
|
8754
|
+
}
|
|
8585
8755
|
const matches = rows.filter((r) => {
|
|
8586
8756
|
if (!term) return true;
|
|
8587
8757
|
return (String(r.name || '').toUpperCase().includes(term) || String(r.desc || '').toUpperCase().includes(term));
|
|
8588
8758
|
});
|
|
8589
8759
|
if (els.finSymCount) els.finSymCount.textContent = matches.length ? matches.length + ' sembol' : '';
|
|
8590
8760
|
if (!matches.length) {
|
|
8761
|
+
if (!term && finSymHist.length) return; /* geçmiş zaten gösterildi */
|
|
8591
8762
|
const d = document.createElement('div');
|
|
8592
8763
|
d.className = 'fin-empty';
|
|
8593
8764
|
d.textContent = term ? 'Eşleşme yok' : 'Sembol listesi boş';
|
|
@@ -8652,6 +8823,26 @@ function finSymPickerClose() {
|
|
|
8652
8823
|
}
|
|
8653
8824
|
|
|
8654
8825
|
if (els.finSymAdd) els.finSymAdd.addEventListener('click', finSymPickerOpen);
|
|
8826
|
+
if (els.finWatchClear) {
|
|
8827
|
+
els.finWatchClear.addEventListener('click', async () => {
|
|
8828
|
+
if (!finWatch.length) {
|
|
8829
|
+
toast('İzleme listesi zaten boş');
|
|
8830
|
+
return;
|
|
8831
|
+
}
|
|
8832
|
+
const ok = await uiConfirm(
|
|
8833
|
+
'İzleme listesindeki TÜM semboller silinsin mi? (' + finWatch.join(', ') + ')\nSilinenler geçmişte kalır — sembol seçicide TEK TIKLA geri ekleyebilirsin.',
|
|
8834
|
+
'Tümünü Sil',
|
|
8835
|
+
'Vazgeç'
|
|
8836
|
+
);
|
|
8837
|
+
if (!ok) return;
|
|
8838
|
+
finWatchSet([]);
|
|
8839
|
+
finSaveCfg({ symbols: [] });
|
|
8840
|
+
finSymBtnUpdate();
|
|
8841
|
+
clearTimeout(finSymRefreshTimer);
|
|
8842
|
+
finSymRefreshTimer = setTimeout(() => finSnapshot(), 700);
|
|
8843
|
+
toast('İzleme listesi temizlendi — semboller geçmişte duruyor');
|
|
8844
|
+
});
|
|
8845
|
+
}
|
|
8655
8846
|
if (els.finSymClose) els.finSymClose.addEventListener('click', finSymPickerClose);
|
|
8656
8847
|
if (els.finSymOverlay) {
|
|
8657
8848
|
els.finSymOverlay.addEventListener('click', (e) => {
|
package/src/renderer/style.css
CHANGED
|
@@ -2072,6 +2072,154 @@ body.browser-open #settingsDialog { width: min(70vw, calc(100vw - var(--bw, 480p
|
|
|
2072
2072
|
letter-spacing: 0.5px;
|
|
2073
2073
|
}
|
|
2074
2074
|
|
|
2075
|
+
/* ---------- sağ sütun 2: AJAN DM (ajanlar arası konuşma) ---------- */
|
|
2076
|
+
|
|
2077
|
+
#dmRail {
|
|
2078
|
+
flex: none;
|
|
2079
|
+
width: 270px;
|
|
2080
|
+
min-width: 270px;
|
|
2081
|
+
min-height: 0;
|
|
2082
|
+
background: var(--panel);
|
|
2083
|
+
border-left: 1px solid var(--border);
|
|
2084
|
+
display: flex;
|
|
2085
|
+
flex-direction: column;
|
|
2086
|
+
}
|
|
2087
|
+
#dmRail.dm-hidden { display: none; }
|
|
2088
|
+
|
|
2089
|
+
#dmRailHead {
|
|
2090
|
+
padding: 12px 12px 10px 16px;
|
|
2091
|
+
font-weight: 800;
|
|
2092
|
+
letter-spacing: 1px;
|
|
2093
|
+
font-size: 13px;
|
|
2094
|
+
color: var(--text);
|
|
2095
|
+
display: flex;
|
|
2096
|
+
align-items: center;
|
|
2097
|
+
gap: 8px;
|
|
2098
|
+
}
|
|
2099
|
+
/* rail gizliyken DM sütunu sağ kenara yaslanır — pencere butonları alanı */
|
|
2100
|
+
body.rail-hidden #dmRailHead { padding-right: 148px; }
|
|
2101
|
+
#dmRailTitle { flex: 1; text-align: left; }
|
|
2102
|
+
#dmClear {
|
|
2103
|
+
flex: none;
|
|
2104
|
+
display: inline-flex;
|
|
2105
|
+
align-items: center;
|
|
2106
|
+
justify-content: center;
|
|
2107
|
+
width: 24px;
|
|
2108
|
+
height: 24px;
|
|
2109
|
+
background: none;
|
|
2110
|
+
border: none;
|
|
2111
|
+
border-radius: 6px;
|
|
2112
|
+
cursor: pointer;
|
|
2113
|
+
color: #111;
|
|
2114
|
+
}
|
|
2115
|
+
#dmClear:hover { background: rgba(0, 0, 0, 0.08); }
|
|
2116
|
+
#dmClear svg { display: block; }
|
|
2117
|
+
|
|
2118
|
+
#dmRailList {
|
|
2119
|
+
flex: 1 1 auto;
|
|
2120
|
+
min-height: 0;
|
|
2121
|
+
overflow-y: auto;
|
|
2122
|
+
overflow-x: hidden;
|
|
2123
|
+
overscroll-behavior: contain;
|
|
2124
|
+
padding: 4px 8px;
|
|
2125
|
+
}
|
|
2126
|
+
.dm-empty {
|
|
2127
|
+
color: var(--muted);
|
|
2128
|
+
font-size: 11.5px;
|
|
2129
|
+
text-align: center;
|
|
2130
|
+
padding: 18px 10px;
|
|
2131
|
+
line-height: 1.5;
|
|
2132
|
+
}
|
|
2133
|
+
.dmt {
|
|
2134
|
+
margin-bottom: 6px;
|
|
2135
|
+
border: 1px solid var(--border);
|
|
2136
|
+
border-radius: 8px;
|
|
2137
|
+
padding: 7px 9px;
|
|
2138
|
+
cursor: pointer;
|
|
2139
|
+
color: var(--muted);
|
|
2140
|
+
min-width: 0;
|
|
2141
|
+
overflow: hidden;
|
|
2142
|
+
}
|
|
2143
|
+
.dmt:hover, .dmt.open { background: var(--panel2); color: var(--text); }
|
|
2144
|
+
.dmt-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
|
|
2145
|
+
.dmt-title {
|
|
2146
|
+
flex: 1; min-width: 0;
|
|
2147
|
+
font-size: 12.5px; font-weight: 700;
|
|
2148
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
2149
|
+
}
|
|
2150
|
+
.dmt-count {
|
|
2151
|
+
flex: none; font-size: 10.5px;
|
|
2152
|
+
background: var(--accent-dim); color: var(--accent);
|
|
2153
|
+
border-radius: 8px; padding: 1px 7px; font-weight: 700;
|
|
2154
|
+
}
|
|
2155
|
+
.dmt-last {
|
|
2156
|
+
font-size: 11px; margin-top: 4px;
|
|
2157
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
2158
|
+
}
|
|
2159
|
+
.dmt-msgs {
|
|
2160
|
+
display: none;
|
|
2161
|
+
margin-top: 6px;
|
|
2162
|
+
max-height: 300px; overflow-y: auto;
|
|
2163
|
+
display: none;
|
|
2164
|
+
flex-direction: column;
|
|
2165
|
+
gap: 5px;
|
|
2166
|
+
}
|
|
2167
|
+
.dmt.open .dmt-msgs { display: flex; }
|
|
2168
|
+
.dmt-msg {
|
|
2169
|
+
font-size: 11px; line-height: 1.5;
|
|
2170
|
+
background: var(--panel2);
|
|
2171
|
+
border-radius: 6px;
|
|
2172
|
+
padding: 5px 7px;
|
|
2173
|
+
word-break: break-word;
|
|
2174
|
+
}
|
|
2175
|
+
.dmt-msg .dm-who {
|
|
2176
|
+
display: block;
|
|
2177
|
+
font-weight: 800;
|
|
2178
|
+
font-size: 10px;
|
|
2179
|
+
letter-spacing: 0.4px;
|
|
2180
|
+
color: var(--accent);
|
|
2181
|
+
margin-bottom: 2px;
|
|
2182
|
+
}
|
|
2183
|
+
.dmt-msg .dm-at {
|
|
2184
|
+
display: block;
|
|
2185
|
+
font-size: 9.5px;
|
|
2186
|
+
color: var(--muted);
|
|
2187
|
+
margin-top: 2px;
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
/* PİYASA satırındaki ▶ ajan butonu */
|
|
2191
|
+
.fin-sym { position: relative; }
|
|
2192
|
+
.fin-sym .fs-agent {
|
|
2193
|
+
position: absolute;
|
|
2194
|
+
right: 4px;
|
|
2195
|
+
top: 50%;
|
|
2196
|
+
transform: translateY(-50%);
|
|
2197
|
+
border: none;
|
|
2198
|
+
background: none;
|
|
2199
|
+
color: var(--muted);
|
|
2200
|
+
cursor: pointer;
|
|
2201
|
+
font-size: 11px;
|
|
2202
|
+
padding: 2px 4px;
|
|
2203
|
+
border-radius: 4px;
|
|
2204
|
+
visibility: hidden;
|
|
2205
|
+
}
|
|
2206
|
+
.fin-sym:hover .fs-agent { visibility: visible; color: var(--accent); }
|
|
2207
|
+
.fin-sym .fs-agent:hover { background: var(--accent-dim); }
|
|
2208
|
+
|
|
2209
|
+
/* sembol seçicide bölüm başlıkları (geçmiş / tüm semboller) */
|
|
2210
|
+
.fin-sym-sec {
|
|
2211
|
+
font-size: 10px;
|
|
2212
|
+
font-weight: 800;
|
|
2213
|
+
letter-spacing: 0.6px;
|
|
2214
|
+
color: var(--accent);
|
|
2215
|
+
padding: 8px 4px 3px;
|
|
2216
|
+
border-bottom: 1px solid var(--border);
|
|
2217
|
+
margin-bottom: 3px;
|
|
2218
|
+
white-space: nowrap;
|
|
2219
|
+
overflow: hidden;
|
|
2220
|
+
text-overflow: ellipsis;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2075
2223
|
/* ---------- BOT SİSTEMİ (sol panel alt bölümü + bot sayfası) ---------- */
|
|
2076
2224
|
|
|
2077
2225
|
/* üst bardaki aktif bot rozeti */
|