beast-agent 2.18.0 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/engine.js +155 -10
- package/src/agent/skills.js +1 -1
- package/src/main.js +325 -61
- package/src/preload.js +3 -0
- package/src/renderer/i18n.js +4 -0
- package/src/renderer/index.html +14 -0
- package/src/renderer/renderer.js +137 -1
- package/src/renderer/style.css +134 -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 () => {
|
|
@@ -8531,36 +8755,76 @@ ipcMain.handle('finance:trader:start', async () => {
|
|
|
8531
8755
|
if (!engine) return { ok: false, error: 'ajan hazır değil' };
|
|
8532
8756
|
const f = finCfg();
|
|
8533
8757
|
if (!engine.publicState().hasModel && !f.traderSel) return { ok: false, error: 'model yok — Ayarlar → Provider' };
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8758
|
+
/* ANA trader: varsa aynen sürdür, yoksa yeni sürekli ajan aç */
|
|
8759
|
+
let mainSid = '';
|
|
8760
|
+
let mainAgent = null;
|
|
8761
|
+
for (const [sid, a] of financeState.agents) {
|
|
8762
|
+
if (a.main) {
|
|
8763
|
+
mainSid = sid;
|
|
8764
|
+
mainAgent = a;
|
|
8765
|
+
break;
|
|
8766
|
+
}
|
|
8767
|
+
}
|
|
8768
|
+
if (!mainSid) {
|
|
8769
|
+
const created = finAgentCreate([], true);
|
|
8770
|
+
mainSid = String(created.s.id);
|
|
8771
|
+
mainAgent = created.agent;
|
|
8772
|
+
}
|
|
8773
|
+
try { engine.setSessionModel(mainSid, f.traderSel || null); } catch {}
|
|
8537
8774
|
financeState.traderOn = true;
|
|
8538
|
-
financeState.traderRounds = 0;
|
|
8539
8775
|
try { engine.clearStop(); } catch {}
|
|
8540
|
-
const ok = engine.send(
|
|
8776
|
+
const ok = engine.send(mainSid, finTraderBrief(mainAgent), { userAction: true });
|
|
8541
8777
|
if (!ok) {
|
|
8542
8778
|
financeState.traderOn = false;
|
|
8543
8779
|
return { ok: false, error: 'trader oturumu meşgul — birkaç saniye sonra tekrar dene' };
|
|
8544
8780
|
}
|
|
8545
8781
|
financeState.lastRoundAt = Date.now();
|
|
8546
8782
|
financeLog('[trader] başlatıldı (model: ' + (f.traderSel || 'genel aktif model') + ')');
|
|
8547
|
-
finPush('trader', { state: 'running', round:
|
|
8548
|
-
return { ok: true, sid:
|
|
8783
|
+
finPush('trader', { state: 'running', round: mainAgent.round });
|
|
8784
|
+
return { ok: true, sid: mainSid };
|
|
8549
8785
|
});
|
|
8550
8786
|
|
|
8551
8787
|
ipcMain.handle('finance:trader:stop', async () => {
|
|
8552
8788
|
financeState.traderOn = false;
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8789
|
+
/* ANA trader'ı durdur (sembol işçileri rail × ile ayrı durdurulur) */
|
|
8790
|
+
let mainSid = '';
|
|
8791
|
+
for (const [sid, a] of financeState.agents) {
|
|
8792
|
+
if (a.main) {
|
|
8793
|
+
mainSid = sid;
|
|
8794
|
+
break;
|
|
8795
|
+
}
|
|
8558
8796
|
}
|
|
8797
|
+
const stopped = mainSid ? finAgentStop(mainSid, 'kullanıcı Beast Finance trader ajanını durdurdu') : false;
|
|
8798
|
+
const interrupted = !!stopped;
|
|
8559
8799
|
financeLog('[trader] durduruldu');
|
|
8560
8800
|
finPush('trader', { state: 'stopped' });
|
|
8561
8801
|
return { ok: true, interrupted };
|
|
8562
8802
|
});
|
|
8563
8803
|
|
|
8804
|
+
/* SEMBOL İŞÇİSİ: PİYASA kartındaki ▶ ile sembol başına AYRI finance ajanı —
|
|
8805
|
+
hepsi AYNI ANDA koşar, Paralel Ajanlar panelinde canlı görünür */
|
|
8806
|
+
ipcMain.handle('finance:agent:spawn', async (_e, payload) => {
|
|
8807
|
+
if (!engine) return { ok: false, error: 'ajan hazır değil' };
|
|
8808
|
+
const f = finCfg();
|
|
8809
|
+
if (!engine.publicState().hasModel && !f.traderSel) return { ok: false, error: 'model yok — Ayarlar → Provider' };
|
|
8810
|
+
const symbol = String((payload && payload.symbol) || '').trim().toUpperCase();
|
|
8811
|
+
if (!symbol) return { ok: false, error: 'sembol gerekli' };
|
|
8812
|
+
for (const [, a] of financeState.agents) {
|
|
8813
|
+
if (!a.main && a.symbols.length === 1 && a.symbols[0] === symbol) {
|
|
8814
|
+
return { ok: false, error: symbol + ' için ajan zaten koşuyor (Paralel Ajanlar panelinden durdurabilirsin)' };
|
|
8815
|
+
}
|
|
8816
|
+
}
|
|
8817
|
+
const created = finAgentCreate([symbol], false);
|
|
8818
|
+
try { engine.setSessionModel(created.s.id, f.traderSel || null); } catch {}
|
|
8819
|
+
const ok = engine.send(created.s.id, finTraderBrief(created.agent), { userAction: true });
|
|
8820
|
+
if (!ok) {
|
|
8821
|
+
finAgentStop(String(created.s.id), 'oturum meşgul — ajan başlatılamadı');
|
|
8822
|
+
return { ok: false, error: 'ajan oturumu meşgul — birkaç saniye sonra tekrar dene' };
|
|
8823
|
+
}
|
|
8824
|
+
financeLog('[ajan] ' + symbol + ' işçisi başlatıldı');
|
|
8825
|
+
return { ok: true, sid: created.s.id, title: created.s.bgTitle };
|
|
8826
|
+
});
|
|
8827
|
+
|
|
8564
8828
|
ipcMain.handle('finance:connect', async () => {
|
|
8565
8829
|
mt5bridge.stop();
|
|
8566
8830
|
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>
|
|
@@ -346,6 +347,19 @@
|
|
|
346
347
|
<span class="foot-label2" data-i18n="labelParallel">Paralel Ajanlar</span>
|
|
347
348
|
</div>
|
|
348
349
|
</aside>
|
|
350
|
+
|
|
351
|
+
<aside id="dmRail" class="dm-hidden">
|
|
352
|
+
<div id="dmRailHead">
|
|
353
|
+
<span id="dmRailTitle" data-i18n="labelAgentDm">AJAN DM</span>
|
|
354
|
+
<button id="dmClear" title="DM geçmişini temizle" aria-label="DM geçmişini temizle">
|
|
355
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#111" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg>
|
|
356
|
+
</button>
|
|
357
|
+
</div>
|
|
358
|
+
<div id="dmRailList"></div>
|
|
359
|
+
<div id="sideFoot2">
|
|
360
|
+
<span class="foot-label2" data-i18n="labelAgentDm">Ajan DM</span>
|
|
361
|
+
</div>
|
|
362
|
+
</aside>
|
|
349
363
|
</div>
|
|
350
364
|
|
|
351
365
|
<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'),
|
|
@@ -4999,6 +5002,113 @@ async function loadRailChat(id) {
|
|
|
4999
5002
|
}
|
|
5000
5003
|
}
|
|
5001
5004
|
|
|
5005
|
+
/* ---------------- AJAN DM sütunu (ajanlar arası konuşma) ----------------
|
|
5006
|
+
rail ile aynı mimari: sağda ikinci sütun, dmBtn ile açılır/kapanır.
|
|
5007
|
+
DM'ler ajanların agent_dm araç çağrılarından gelir; çiftlere gruplanır. */
|
|
5008
|
+
|
|
5009
|
+
const dmState = {
|
|
5010
|
+
dms: [], // { at, from, fromTitle, to, toTitle, text }
|
|
5011
|
+
open: new Set(), // açık thread key'leri
|
|
5012
|
+
};
|
|
5013
|
+
|
|
5014
|
+
function dmPairOf(dm) {
|
|
5015
|
+
const a = String(dm.from || '');
|
|
5016
|
+
const b = String(dm.to || '');
|
|
5017
|
+
return a < b ? a + '|' + b : b + '|' + a;
|
|
5018
|
+
}
|
|
5019
|
+
|
|
5020
|
+
function dmPairTitle(dm) {
|
|
5021
|
+
const a = String(dm.fromTitle || 'Ajan');
|
|
5022
|
+
const b = String(dm.toTitle || 'Ajan');
|
|
5023
|
+
return a < b ? a + ' ↔ ' + b : b + ' ↔ ' + a;
|
|
5024
|
+
}
|
|
5025
|
+
|
|
5026
|
+
function dmTime(iso) {
|
|
5027
|
+
const d = new Date(iso || '');
|
|
5028
|
+
if (!d.getTime()) return '';
|
|
5029
|
+
return d.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
|
|
5030
|
+
}
|
|
5031
|
+
|
|
5032
|
+
function renderDmRail() {
|
|
5033
|
+
const list = els.dmRailList;
|
|
5034
|
+
if (!list) return;
|
|
5035
|
+
if (!dmState.dms.length) {
|
|
5036
|
+
list.innerHTML = '<div class="dm-empty">Ajanlar arasında DM yok.<br>Ajanlar agent_dm aracıyla birbirine mesaj attıkça burada görünür.</div>';
|
|
5037
|
+
return;
|
|
5038
|
+
}
|
|
5039
|
+
/* çiftlere grupla (kronolojik) — en yeni thread üstte */
|
|
5040
|
+
const threads = new Map();
|
|
5041
|
+
for (const dm of dmState.dms) {
|
|
5042
|
+
const key = dmPairOf(dm);
|
|
5043
|
+
if (!threads.has(key)) threads.set(key, []);
|
|
5044
|
+
threads.get(key).push(dm);
|
|
5045
|
+
}
|
|
5046
|
+
const rows = [...threads.entries()].sort(
|
|
5047
|
+
(x, y) => new Date(y[1][y[1].length - 1].at) - new Date(x[1][x[1].length - 1].at)
|
|
5048
|
+
);
|
|
5049
|
+
list.innerHTML = '';
|
|
5050
|
+
for (const [key, msgs] of rows) {
|
|
5051
|
+
const open = dmState.open.has(key);
|
|
5052
|
+
const last = msgs[msgs.length - 1];
|
|
5053
|
+
const el = document.createElement('div');
|
|
5054
|
+
el.className = 'dmt' + (open ? ' open' : '');
|
|
5055
|
+
el.innerHTML =
|
|
5056
|
+
'<div class="dmt-head">' +
|
|
5057
|
+
`<span class="dmt-title">${escapeHtml(dmPairTitle(last))}</span>` +
|
|
5058
|
+
`<span class="dmt-count">${msgs.length}</span>` +
|
|
5059
|
+
'</div>' +
|
|
5060
|
+
`<div class="dmt-last">${escapeHtml(String(last.text || '').replace(/\s+/g, ' ').slice(0, 90))} · ${dmTime(last.at)}</div>` +
|
|
5061
|
+
'<div class="dmt-msgs">' +
|
|
5062
|
+
msgs
|
|
5063
|
+
.map(
|
|
5064
|
+
(m) =>
|
|
5065
|
+
`<div class="dmt-msg"><span class="dm-who">${escapeHtml(String(m.fromTitle || 'Ajan'))} → ${escapeHtml(String(m.toTitle || 'Ajan'))}</span>` +
|
|
5066
|
+
`${escapeHtml(String(m.text || ''))}<span class="dm-at">${dmTime(m.at)}</span></div>`
|
|
5067
|
+
)
|
|
5068
|
+
.join('') +
|
|
5069
|
+
'</div>';
|
|
5070
|
+
el.addEventListener('click', () => {
|
|
5071
|
+
if (dmState.open.has(key)) dmState.open.delete(key);
|
|
5072
|
+
else dmState.open.add(key);
|
|
5073
|
+
renderDmRail();
|
|
5074
|
+
const again = els.dmRailList.querySelector('.dmt.open .dmt-msgs');
|
|
5075
|
+
if (again) again.scrollTop = again.scrollHeight;
|
|
5076
|
+
});
|
|
5077
|
+
list.appendChild(el);
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
|
|
5081
|
+
async function refreshDmRail() {
|
|
5082
|
+
try {
|
|
5083
|
+
const dms = await beast.agentDmsList();
|
|
5084
|
+
if (Array.isArray(dms)) dmState.dms = dms;
|
|
5085
|
+
} catch {}
|
|
5086
|
+
renderDmRail();
|
|
5087
|
+
}
|
|
5088
|
+
|
|
5089
|
+
function toggleDmRail(hide) {
|
|
5090
|
+
const rail = $('#dmRail');
|
|
5091
|
+
if (!rail) return;
|
|
5092
|
+
rail.classList.toggle('dm-hidden', !!hide);
|
|
5093
|
+
if (els.dmBtn) els.dmBtn.classList.toggle('on', !hide);
|
|
5094
|
+
if (!hide) refreshDmRail();
|
|
5095
|
+
}
|
|
5096
|
+
|
|
5097
|
+
if (els.dmBtn) {
|
|
5098
|
+
els.dmBtn.addEventListener('click', () => {
|
|
5099
|
+
toggleDmRail(!$('#dmRail').classList.contains('dm-hidden'));
|
|
5100
|
+
});
|
|
5101
|
+
}
|
|
5102
|
+
if (els.dmClear) {
|
|
5103
|
+
els.dmClear.addEventListener('click', async () => {
|
|
5104
|
+
if (!(await uiConfirm('TÜM ajan DM geçmişi silinsin mi?', 'Sil'))) return;
|
|
5105
|
+
try { await beast.agentDmsClear(); } catch {}
|
|
5106
|
+
dmState.dms = [];
|
|
5107
|
+
renderDmRail();
|
|
5108
|
+
toast('Ajan DM geçmişi silindi');
|
|
5109
|
+
});
|
|
5110
|
+
}
|
|
5111
|
+
|
|
5002
5112
|
function renderAgentsPane() {
|
|
5003
5113
|
const pane = $('#tab-agents');
|
|
5004
5114
|
if (!pane) return;
|
|
@@ -5143,6 +5253,20 @@ function onEvent(ev) {
|
|
|
5143
5253
|
scheduleAgentsRender();
|
|
5144
5254
|
return;
|
|
5145
5255
|
}
|
|
5256
|
+
/* AJAN DM: ajanlar arası mesaj — panel açıksa canlı düşer */
|
|
5257
|
+
if (ev.type === 'agent-dm') {
|
|
5258
|
+
dmState.dms.push({
|
|
5259
|
+
at: ev.at,
|
|
5260
|
+
from: ev.from,
|
|
5261
|
+
fromTitle: ev.fromTitle,
|
|
5262
|
+
to: ev.to,
|
|
5263
|
+
toTitle: ev.toTitle,
|
|
5264
|
+
text: ev.text,
|
|
5265
|
+
});
|
|
5266
|
+
if (dmState.dms.length > 400) dmState.dms.splice(0, dmState.dms.length - 400);
|
|
5267
|
+
if ($('#dmRail') && !$('#dmRail').classList.contains('dm-hidden')) renderDmRail();
|
|
5268
|
+
return;
|
|
5269
|
+
}
|
|
5146
5270
|
/* OFFLINE MESAJ KUYRUĞU: bağlantı + kuyruk olayları (sessionId filtresinden önce) */
|
|
5147
5271
|
if (ev.type === 'net' || ev.type === 'netQueue') { onNetEvent(ev); return; }
|
|
5148
5272
|
/* Sandbox klonlama progress'i (sessionId'siz global olay) */
|
|
@@ -6066,6 +6190,8 @@ const SLASH_COMMANDS = [
|
|
|
6066
6190
|
{ cmd: '/notify', desc: 'hata maili aç/kapa · /notify on|off' },
|
|
6067
6191
|
{ cmd: '/think ', desc: 'düşünme seviyesi · /think 0-5 (0 kapalı)' },
|
|
6068
6192
|
{ cmd: '/clear', desc: 'oturum geçmişini GERÇEKTEN sil (kod korunur)' },
|
|
6193
|
+
{ cmd: '/autodel', desc: 'tüm otomatik hatırlatmaları sil · /autodel all: cron dahil hepsi' },
|
|
6194
|
+
{ cmd: '/deltodo', desc: 'todo listesini temizle · /deltodo all: tüm oturumlar' },
|
|
6069
6195
|
{ cmd: '/start', desc: 'durdurulan servisleri devam ettir' },
|
|
6070
6196
|
{ cmd: '/rule ', desc: 'kalıcı kural ekle' },
|
|
6071
6197
|
{ cmd: '/rules', desc: 'kalıcı kuralları listele' },
|
|
@@ -8342,7 +8468,17 @@ function finRenderSymbols(list) {
|
|
|
8342
8468
|
row.innerHTML =
|
|
8343
8469
|
'<span class="fs-name">' + (s.symbol || '?') + '</span>' +
|
|
8344
8470
|
'<span class="fs-price ' + cls + '">' + bid.toFixed(digits) + '</span>' +
|
|
8345
|
-
'<span class="fs-spread">sp ' + (s.spread != null ? s.spread : '—') + '</span>'
|
|
8471
|
+
'<span class="fs-spread">sp ' + (s.spread != null ? s.spread : '—') + '</span>' +
|
|
8472
|
+
'<button class="fs-agent" title="' + escapeHtml(s.symbol || '') + ' için AYRI finance ajanı başlat (paralel koşar)">▶︎</button>';
|
|
8473
|
+
const spawnBtn = row.querySelector('.fs-agent');
|
|
8474
|
+
if (spawnBtn) {
|
|
8475
|
+
spawnBtn.addEventListener('click', async (e) => {
|
|
8476
|
+
e.stopPropagation();
|
|
8477
|
+
const r = await beast.financeAgentSpawn(s.symbol).catch((err) => ({ ok: false, error: String((err && err.message) || err) }));
|
|
8478
|
+
if (r && r.ok) toast(s.symbol + ' için ajan başladı — Paralel Ajanlar panelinde izle');
|
|
8479
|
+
else toast((r && r.error) || 'Ajan başlatılamadı');
|
|
8480
|
+
});
|
|
8481
|
+
}
|
|
8346
8482
|
els.finSymList.appendChild(row);
|
|
8347
8483
|
}
|
|
8348
8484
|
}
|
package/src/renderer/style.css
CHANGED
|
@@ -2072,6 +2072,140 @@ 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
|
+
|
|
2075
2209
|
/* ---------- BOT SİSTEMİ (sol panel alt bölümü + bot sayfası) ---------- */
|
|
2076
2210
|
|
|
2077
2211
|
/* üst bardaki aktif bot rozeti */
|