beast-agent 2.22.1 → 2.23.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 +207 -66
- package/src/main.js +20 -2
- package/src/renderer/renderer.js +79 -35
- package/src/renderer/style.css +12 -0
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -364,9 +364,17 @@ class Engine {
|
|
|
364
364
|
(AJAN DM paneli buradan beslenir; restart sonrası da durur) */
|
|
365
365
|
this._agentDms = [];
|
|
366
366
|
this._agentDmsFile = path.join(this.sessionsDir, 'agent-dms.json');
|
|
367
|
+
/* AJAN DM GRUPLARI: ajanların agent_dm ile kurduğu grup sohbetleri
|
|
368
|
+
(id → {id,title,members,titles,createdAt,closed,closedAt}) */
|
|
369
|
+
this._agentGroups = new Map();
|
|
367
370
|
try {
|
|
368
371
|
const raw = JSON.parse(fs.readFileSync(this._agentDmsFile, 'utf8'));
|
|
369
372
|
this._agentDms = Array.isArray(raw.dms) ? raw.dms : [];
|
|
373
|
+
if (raw.groups && typeof raw.groups === 'object') {
|
|
374
|
+
for (const g of Object.values(raw.groups)) {
|
|
375
|
+
if (g && g.id && g.title) this._agentGroups.set(String(g.id), g);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
370
378
|
} catch {
|
|
371
379
|
this._agentDms = [];
|
|
372
380
|
}
|
|
@@ -1774,7 +1782,7 @@ class Engine {
|
|
|
1774
1782
|
} catch {
|
|
1775
1783
|
return null;
|
|
1776
1784
|
}
|
|
1777
|
-
}
|
|
1785
|
+
}
|
|
1778
1786
|
|
|
1779
1787
|
/* Beast Studio (video paneli): video YAPMA ve DÜZENLEME ajanı. Workspace'teki
|
|
1780
1788
|
her şey video proje malzemesi olarak görülür (ham görüntü, ses, görsel,
|
|
@@ -1874,7 +1882,7 @@ class Engine {
|
|
|
1874
1882
|
modeBlock2 + '\n' +
|
|
1875
1883
|
'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' +
|
|
1876
1884
|
'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' +
|
|
1877
|
-
'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' +
|
|
1885
|
+
'PARALEL + KOORDİNASYON: uzun araştırma/işleri run_background ile paralel finance işçisine devret (parent finance olduğu için işçi mt5 okuma araçlarını görür); koşan ajanlarla konuşmak için agent_dm (to: ajan başlığındaki anahtar kelime, örn "GOLD"; ortak karar için group: "İSİM" ile grup sohbeti kur — mesaj tüm üyelere düşer). Görevin bitince DM/grup sohbetleri otomatik KAPANIR (geçmiş panelde kalır).\n' +
|
|
1878
1886
|
(symbols ? `İZLEME LİSTESİ: ${symbols}\n` : '') +
|
|
1879
1887
|
(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' : '') +
|
|
1880
1888
|
'RİSK DİSİPLİNİ:\n' +
|
|
@@ -3181,6 +3189,8 @@ class Engine {
|
|
|
3181
3189
|
}
|
|
3182
3190
|
/* #17 öz-kurtarma hakları tükendi → owner'a anlık altyapı uyarısı */
|
|
3183
3191
|
this._notifyOwnerTaskFailed(job, msg);
|
|
3192
|
+
/* AJAN DM: işi kesin bitti — ajanın DM/grup sohbetleri kapanır */
|
|
3193
|
+
try { this._agentDmClose(sid); } catch {}
|
|
3184
3194
|
if (grouped) {
|
|
3185
3195
|
/* #18 hatalı üye de gruba yazılır — birleşik raporda görünecek */
|
|
3186
3196
|
this._bgGroupRecord(job.title, 'error', 'HATA: ' + msg.slice(0, 300));
|
|
@@ -3204,6 +3214,11 @@ class Engine {
|
|
|
3204
3214
|
this._pendingReports.push({ parentId: job.parentId, text });
|
|
3205
3215
|
this.flushPendingReports(job.parentId);
|
|
3206
3216
|
}
|
|
3217
|
+
/* AJAN DM: işi bitti (kullanıcı iptali dahil) — sohbetler kapanır */
|
|
3218
|
+
try { this._agentDmClose(sid); } catch {}
|
|
3219
|
+
} else if (status === 'done') {
|
|
3220
|
+
/* AJAN DM: görev tamamlandı — ajanın DM/grup sohbetleri kapanır */
|
|
3221
|
+
try { this._agentDmClose(sid); } catch {}
|
|
3207
3222
|
}
|
|
3208
3223
|
}
|
|
3209
3224
|
|
|
@@ -3464,7 +3479,7 @@ class Engine {
|
|
|
3464
3479
|
`- Bağımsız araç çağrılarını aynı turda PARALEL ver.\n` +
|
|
3465
3480
|
`- 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` +
|
|
3466
3481
|
`- 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` +
|
|
3467
|
-
`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` +
|
|
3482
|
+
`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. Birden fazla ajanla ORTAK KARAR almanız gerekiyorsa group: "İSİM" vererek grup sohbeti kur — mesajın tüm üyelere düşer, cevaplar aynı gruba gelir. Görevin bitince DM/grup sohbetleri otomatik KAPANIR (geçmiş panelde kalır).\n` +
|
|
3468
3483
|
FORMAT_RULES + '\n' +
|
|
3469
3484
|
`SON ÇIKTI: 3-5 satırlık net sonuç raporu, madde madde. Soru sorma, sohbet etme.`
|
|
3470
3485
|
);
|
|
@@ -3551,7 +3566,7 @@ class Engine {
|
|
|
3551
3566
|
? this.buildStudioSystem(session)
|
|
3552
3567
|
: bcAgent
|
|
3553
3568
|
? this.buildOcSystem(session, bcAgent)
|
|
3554
|
-
: await this.buildSystem(promptText, session);
|
|
3569
|
+
: await this.buildSystem(promptText, session);
|
|
3555
3570
|
// Granül izin: oturumun yetki seviyesine göre araç seti daraltılır.
|
|
3556
3571
|
// Çoklu izin (ör. web+read) seçiliyse kümeler BİRLEŞİR — hepsinin araçları açık olur.
|
|
3557
3572
|
// BC'de opencode adları Beast adına çevrilerek denetlenir (read → read_file).
|
|
@@ -4061,7 +4076,7 @@ class Engine {
|
|
|
4061
4076
|
}
|
|
4062
4077
|
}
|
|
4063
4078
|
} catch {}
|
|
4064
|
-
}
|
|
4079
|
+
}
|
|
4065
4080
|
// Ekran görüntüsü gibi araçlar görseli sonraki tura enjekte eder
|
|
4066
4081
|
let injectedImage = null;
|
|
4067
4082
|
/* opencode ctx.metadata portu: edit/write sonucundaki diffView, LLM
|
|
@@ -5377,18 +5392,19 @@ const AGENT_DM_DEF = {
|
|
|
5377
5392
|
function: {
|
|
5378
5393
|
name: 'agent_dm',
|
|
5379
5394
|
description:
|
|
5380
|
-
'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"). `topic` = short subject label — replies to the same topic stay in the SAME conversation thread, so ALWAYS reuse the topic you were DMed with when replying.',
|
|
5395
|
+
'Send a short DM to another running agent (parallel agents / finance agents) to coordinate: share findings, ask status, warn about risk, hand off work, and MAKE JOINT DECISIONS. `to` = target session id OR a keyword from the agent title (e.g. "GOLD", "Trader"). `topic` = short subject label — replies to the same topic stay in the SAME conversation thread, so ALWAYS reuse the topic you were DMed with when replying. `group` = optional group-chat name (e.g. "GOLD EKIP"): creates or reuses a group conversation, adds the target agent as a member, and your message is delivered to EVERY member — use groups when a decision needs multiple agents.',
|
|
5381
5396
|
parameters: {
|
|
5382
5397
|
type: 'object',
|
|
5383
5398
|
properties: {
|
|
5384
5399
|
to: { type: 'string', description: 'Target agent session id or a title keyword' },
|
|
5385
5400
|
topic: { type: 'string', description: 'Short subject label for the conversation thread, e.g. "GOLD pozisyon riski"' },
|
|
5386
5401
|
message: { type: 'string', description: 'Short message (1-3 sentences)' },
|
|
5402
|
+
group: { type: 'string', description: 'Optional group-chat name — send to ALL members of that group (creates it on first use, adds the target agent)' },
|
|
5387
5403
|
},
|
|
5388
|
-
required: ['
|
|
5404
|
+
required: ['message'],
|
|
5389
5405
|
},
|
|
5390
5406
|
},
|
|
5391
|
-
};
|
|
5407
|
+
};
|
|
5392
5408
|
|
|
5393
5409
|
const TOOLS = [
|
|
5394
5410
|
...tools.definitions,
|
|
@@ -6180,85 +6196,210 @@ Engine.prototype._agentDmSend = function (fromSid, args) {
|
|
|
6180
6196
|
const to = String((args && args.to) || '').trim();
|
|
6181
6197
|
const text = String((args && args.message) || '').trim().slice(0, 1200);
|
|
6182
6198
|
if (!text) return { ok: false, error: 'mesaj boş' };
|
|
6199
|
+
const groupName = String((args && args.group) || '').replace(/\s+/g, ' ').trim().slice(0, 40);
|
|
6183
6200
|
const jobs = this._bgJobs || new Map();
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6201
|
+
const fromJob = jobs.get(String(fromSid));
|
|
6202
|
+
/* gönderici koşan ajan değilse (düz chat oturumu) cache'ten oturum bilgisi */
|
|
6203
|
+
const fromSess = fromJob ? null : this.cache.get(String(fromSid)) || null;
|
|
6204
|
+
const fromTitle =
|
|
6205
|
+
fromJob && fromJob.title
|
|
6206
|
+
? String(fromJob.title)
|
|
6207
|
+
: fromSess
|
|
6208
|
+
? 'Beast · ' + String(fromSess.bgTitle || fromSess.title || fromSess.code || '')
|
|
6209
|
+
: 'Ajan';
|
|
6210
|
+
/* hedef çöz: oturum id YA DA başlık anahtar kelimesi (koşan ajanlar +
|
|
6211
|
+
ANA OTURUM FALLBACK: chat oturumları this.cache'te yaşar — finance
|
|
6212
|
+
trader → ana Beast DM zinciri böyle kapanır). */
|
|
6213
|
+
let target = '';
|
|
6214
|
+
if (to) {
|
|
6215
|
+
target = jobs.has(to) ? to : '';
|
|
6216
|
+
if (!target) {
|
|
6217
|
+
const q = to.toLowerCase();
|
|
6218
|
+
for (const [sid, j] of jobs) {
|
|
6219
|
+
if (sid === String(fromSid)) continue;
|
|
6220
|
+
if (j && j.title && String(j.title).toLowerCase().includes(q)) {
|
|
6221
|
+
target = sid;
|
|
6222
|
+
break;
|
|
6223
|
+
}
|
|
6192
6224
|
}
|
|
6193
6225
|
}
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
const targetSess = this.cache.get(String(target)) || null;
|
|
6214
|
-
if (!targetSess || !targetSess.finance) {
|
|
6226
|
+
if (!target) {
|
|
6227
|
+
const q = to.toLowerCase();
|
|
6228
|
+
for (const [sid, s] of this.cache) {
|
|
6229
|
+
if (sid === String(fromSid)) continue;
|
|
6230
|
+
if (!s || s.bgJob || s.isBotDm) continue;
|
|
6231
|
+
if (s.botId && s.botId !== 'beast') continue;
|
|
6232
|
+
const hay = [s.bgTitle, s.title, s.code].map((x) => String(x || '').toLowerCase());
|
|
6233
|
+
if (hay.some((x) => x && x.includes(q))) {
|
|
6234
|
+
target = sid;
|
|
6235
|
+
break;
|
|
6236
|
+
}
|
|
6237
|
+
}
|
|
6238
|
+
}
|
|
6239
|
+
if (String(target) === String(fromSid)) return { ok: false, error: 'kendine DM atılamaz' };
|
|
6240
|
+
if (!target) {
|
|
6241
|
+
const titles = [...jobs.values()]
|
|
6242
|
+
.filter((j) => j && j.status === 'running' && j.title)
|
|
6243
|
+
.map((j) => j.title)
|
|
6244
|
+
.slice(0, 8);
|
|
6215
6245
|
return {
|
|
6216
6246
|
ok: false,
|
|
6217
|
-
error:
|
|
6218
|
-
'Beast Finance ajanı yalnız finance dünyasındaki ajanlara DM atabilir — hedef finance dışı: ' +
|
|
6219
|
-
String((toJob && toJob.title) || to),
|
|
6247
|
+
error: 'hedef ajan bulunamadı: "' + to + '"' + (titles.length ? ' — koşan ajanlar: ' + titles.join(', ') : ''),
|
|
6220
6248
|
};
|
|
6221
6249
|
}
|
|
6222
|
-
}
|
|
6250
|
+
} else if (!groupName) {
|
|
6251
|
+
return { ok: false, error: 'to (hedef ajan) ya da group (grup adı) gerekli' };
|
|
6252
|
+
}
|
|
6253
|
+
const toJob = target ? jobs.get(target) : null;
|
|
6254
|
+
const mainChatSess = target && !toJob ? this.cache.get(String(target)) || null : null;
|
|
6255
|
+
const toTitle = target
|
|
6256
|
+
? toJob
|
|
6257
|
+
? String((toJob && toJob.title) || 'Ajan')
|
|
6258
|
+
: String((mainChatSess && (mainChatSess.bgTitle || mainChatSess.title)) || 'Beast')
|
|
6259
|
+
: '';
|
|
6223
6260
|
/* konu etiketi: aynı konudaki DM'ler panelde AYNI oturumda toplanır */
|
|
6224
6261
|
const topic = String((args && args.topic) || '').replace(/\s+/g, ' ').trim().slice(0, 60) || '(genel)';
|
|
6225
|
-
const
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6262
|
+
const at = new Date().toISOString();
|
|
6263
|
+
|
|
6264
|
+
/* GRUP SOHBETİ: kur/yeniden kullan, hedefi üye yap, mesaj HER ÜYEYE düşer.
|
|
6265
|
+
Ajanlar ortak kararları burada tartışır; iş bitince sohbet kapanır. */
|
|
6266
|
+
if (groupName) {
|
|
6267
|
+
/* gid Türkçe-kararsız normalize: ALTIN/Altın/altın aynı grup */
|
|
6268
|
+
const gid =
|
|
6269
|
+
'grp:' +
|
|
6270
|
+
groupName
|
|
6271
|
+
.toLowerCase()
|
|
6272
|
+
.replace(/\u0307/g, '')
|
|
6273
|
+
.replace(/ı/g, 'i')
|
|
6274
|
+
.replace(/ğ/g, 'g')
|
|
6275
|
+
.replace(/ü/g, 'u')
|
|
6276
|
+
.replace(/ş/g, 's')
|
|
6277
|
+
.replace(/ö/g, 'o')
|
|
6278
|
+
.replace(/ç/g, 'c');
|
|
6279
|
+
let g = this._agentGroups.get(gid);
|
|
6280
|
+
if (!g) {
|
|
6281
|
+
if (!target) return { ok: false, error: 'yeni grup için to ile en az bir ajan belirt' };
|
|
6282
|
+
g = { id: gid, title: groupName, members: [], titles: {}, createdAt: at, closed: false };
|
|
6283
|
+
this._agentGroups.set(gid, g);
|
|
6284
|
+
}
|
|
6285
|
+
g.titles = g.titles || {};
|
|
6286
|
+
const addMember = (sid, title) => {
|
|
6287
|
+
sid = String(sid);
|
|
6288
|
+
if (!g.members.includes(sid)) g.members.push(sid);
|
|
6289
|
+
if (title) g.titles[sid] = title;
|
|
6290
|
+
};
|
|
6291
|
+
addMember(fromSid, fromTitle);
|
|
6292
|
+
if (target) addMember(target, toTitle);
|
|
6293
|
+
g.closed = false; /* yeni mesaj → sohbet yeniden aktif */
|
|
6294
|
+
delete g.closedAt;
|
|
6295
|
+
const dm = {
|
|
6296
|
+
at,
|
|
6297
|
+
from: String(fromSid),
|
|
6298
|
+
fromTitle,
|
|
6299
|
+
to: target || '',
|
|
6300
|
+
toTitle: target ? toTitle : 'grup',
|
|
6301
|
+
group: gid,
|
|
6302
|
+
groupTitle: g.title,
|
|
6303
|
+
topic,
|
|
6304
|
+
text,
|
|
6305
|
+
};
|
|
6306
|
+
this._pushAgentDm(dm);
|
|
6307
|
+
for (const m of g.members) {
|
|
6308
|
+
if (m === String(fromSid) || !jobs.has(m)) continue;
|
|
6309
|
+
emitSafe(this, m, { type: 'agent-dm', ...dm });
|
|
6310
|
+
(this._pendingReports = this._pendingReports || []).push({
|
|
6311
|
+
parentId: m,
|
|
6312
|
+
text:
|
|
6313
|
+
`[AJAN DM (grup: "${g.title}") — ${dm.fromTitle} · konu: "${topic}"]\n${text}\n` +
|
|
6314
|
+
`(Cevabını agent_dm aracıyla ver — group: "${g.title}", to: "${dm.fromTitle}", topic: "${topic}")`,
|
|
6315
|
+
});
|
|
6316
|
+
this.flushPendingReports(m);
|
|
6317
|
+
}
|
|
6318
|
+
this._persistAgentDms();
|
|
6319
|
+
return {
|
|
6320
|
+
ok: true,
|
|
6321
|
+
group: g.title,
|
|
6322
|
+
members: g.members.map((m) => g.titles[m] || m),
|
|
6323
|
+
...(target ? { to: target, toTitle } : {}),
|
|
6324
|
+
topic,
|
|
6325
|
+
};
|
|
6326
|
+
}
|
|
6327
|
+
|
|
6328
|
+
/* BİREYSEL DM — DÜZ CHAT OTURUMUNA (sahibin sohbet geçmişi) ENJEKTE
|
|
6329
|
+
ETMEYİZ: DM trafik yalnız AJAN DM konsolunda görünür. */
|
|
6330
|
+
const dm = { at, from: String(fromSid), fromTitle, to: String(target), toTitle, topic, text };
|
|
6331
|
+
this._pushAgentDm(dm);
|
|
6332
|
+
this._persistAgentDms();
|
|
6240
6333
|
emitSafe(this, target, { type: 'agent-dm', ...dm });
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6334
|
+
if (jobs.has(target)) {
|
|
6335
|
+
(this._pendingReports = this._pendingReports || []).push({
|
|
6336
|
+
parentId: target,
|
|
6337
|
+
text:
|
|
6338
|
+
`[AJAN DM — ${dm.fromTitle} · konu: "${topic}"]\n${text}\n` +
|
|
6339
|
+
`(Cevabını agent_dm aracıyla ver — to: "${dm.fromTitle}", topic: "${topic}")`,
|
|
6340
|
+
});
|
|
6341
|
+
this.flushPendingReports(target);
|
|
6342
|
+
}
|
|
6250
6343
|
return { ok: true, to: target, toTitle: dm.toTitle, topic };
|
|
6251
6344
|
} catch (e) {
|
|
6252
6345
|
return { ok: false, error: String((e && e.message) || e) };
|
|
6253
6346
|
}
|
|
6254
6347
|
};
|
|
6255
6348
|
|
|
6349
|
+
Engine.prototype._pushAgentDm = function (dm) {
|
|
6350
|
+
this._agentDms = this._agentDms || [];
|
|
6351
|
+
this._agentDms.push(dm);
|
|
6352
|
+
if (this._agentDms.length > 600) this._agentDms.splice(0, this._agentDms.length - 600);
|
|
6353
|
+
};
|
|
6354
|
+
|
|
6355
|
+
Engine.prototype._persistAgentDms = function () {
|
|
6356
|
+
try {
|
|
6357
|
+
const groups = {};
|
|
6358
|
+
for (const [, g] of this._agentGroups || new Map()) groups[g.id] = g;
|
|
6359
|
+
fs.writeFileSync(this._agentDmsFile, JSON.stringify({ dms: this._agentDms, groups }, null, 2));
|
|
6360
|
+
} catch {}
|
|
6361
|
+
};
|
|
6362
|
+
|
|
6363
|
+
/* AJAN İŞİ BİTİNCE: o ajanın bireysel DM'leri kapanır; üyesi olduğu
|
|
6364
|
+
gruplarda DİĞER koşan ajan kalmadıysa grup sohbeti de kapanır.
|
|
6365
|
+
Geçmiş panelde GÖRÜNMEYE DEVAM EDER (silinmez, "Geçmiş" bölümüne düşer). */
|
|
6366
|
+
Engine.prototype._agentDmClose = function (sid) {
|
|
6367
|
+
sid = String(sid);
|
|
6368
|
+
const now = new Date().toISOString();
|
|
6369
|
+
let changed = false;
|
|
6370
|
+
for (const dm of this._agentDms || []) {
|
|
6371
|
+
if (dm.closed || dm.group) continue;
|
|
6372
|
+
if (String(dm.from) === sid || String(dm.to) === sid) {
|
|
6373
|
+
dm.closed = true;
|
|
6374
|
+
dm.closedAt = now;
|
|
6375
|
+
changed = true;
|
|
6376
|
+
}
|
|
6377
|
+
}
|
|
6378
|
+
const jobs = this._bgJobs || new Map();
|
|
6379
|
+
for (const [, g] of this._agentGroups || new Map()) {
|
|
6380
|
+
if (g.closed || !(g.members || []).includes(sid)) continue;
|
|
6381
|
+
const anyRunning = g.members.some((m) => {
|
|
6382
|
+
const j = jobs.get(m);
|
|
6383
|
+
return j && j.status === 'running';
|
|
6384
|
+
});
|
|
6385
|
+
if (!anyRunning) {
|
|
6386
|
+
g.closed = true;
|
|
6387
|
+
g.closedAt = now;
|
|
6388
|
+
changed = true;
|
|
6389
|
+
}
|
|
6390
|
+
}
|
|
6391
|
+
if (changed) this._persistAgentDms();
|
|
6392
|
+
};
|
|
6393
|
+
|
|
6256
6394
|
Engine.prototype.agentDmsList = function () {
|
|
6257
|
-
|
|
6395
|
+
const groups = [];
|
|
6396
|
+
for (const [, g] of this._agentGroups || new Map()) groups.push(g);
|
|
6397
|
+
return { dms: [...(this._agentDms || [])].slice(-600), groups };
|
|
6258
6398
|
};
|
|
6259
6399
|
|
|
6260
6400
|
Engine.prototype.agentDmsClear = function () {
|
|
6261
6401
|
this._agentDms = [];
|
|
6402
|
+
this._agentGroups = new Map();
|
|
6262
6403
|
try { fs.rmSync(this._agentDmsFile, { force: true }); } catch {}
|
|
6263
6404
|
return { ok: true };
|
|
6264
6405
|
};
|
package/src/main.js
CHANGED
|
@@ -8764,6 +8764,15 @@ ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
|
8764
8764
|
}
|
|
8765
8765
|
} catch {}
|
|
8766
8766
|
}
|
|
8767
|
+
if (financeState.mode) {
|
|
8768
|
+
/* OTOMATİK TRADER: finance modu açılır açılmaz trader botu devreye girer
|
|
8769
|
+
(best-effort — model/köprü yoksa sessizce atlanır, log'a düşer) */
|
|
8770
|
+
financeTraderStart()
|
|
8771
|
+
.then((r) => {
|
|
8772
|
+
if (!r || !r.ok) financeLog('[trader] otomatik başlatılamadı: ' + ((r && r.error) || 'bilinmeyen'));
|
|
8773
|
+
})
|
|
8774
|
+
.catch(() => {});
|
|
8775
|
+
}
|
|
8767
8776
|
return { ok: true, mode: financeState.mode, needNew };
|
|
8768
8777
|
});
|
|
8769
8778
|
|
|
@@ -8814,7 +8823,9 @@ ipcMain.handle('finance:settings', async (_e, patch) => {
|
|
|
8814
8823
|
return { ok: true, cfg: f };
|
|
8815
8824
|
});
|
|
8816
8825
|
|
|
8817
|
-
|
|
8826
|
+
/* Trader botu başlat: ANA trader varsa sürdür, yoksa yeni sürekli ajan aç.
|
|
8827
|
+
finance:trader:start'tan VE finance modu AÇILDIĞINDAN otomatik çağrılır. */
|
|
8828
|
+
async function financeTraderStart() {
|
|
8818
8829
|
if (!engine) return { ok: false, error: 'ajan hazır değil' };
|
|
8819
8830
|
const f = finCfg();
|
|
8820
8831
|
if (!engine.publicState().hasModel && !f.traderSel) return { ok: false, error: 'model yok — Ayarlar → Provider' };
|
|
@@ -8828,6 +8839,11 @@ ipcMain.handle('finance:trader:start', async () => {
|
|
|
8828
8839
|
break;
|
|
8829
8840
|
}
|
|
8830
8841
|
}
|
|
8842
|
+
/* zaten koşuyorsa yeniden brief düşürme — sessizce onayla */
|
|
8843
|
+
if (mainSid && financeState.traderOn) {
|
|
8844
|
+
const mj = engine._bgJobs && engine._bgJobs.get(mainSid);
|
|
8845
|
+
if (mj && mj.status === 'running') return { ok: true, sid: mainSid, already: true };
|
|
8846
|
+
}
|
|
8831
8847
|
if (!mainSid) {
|
|
8832
8848
|
const created = finAgentCreate([], true);
|
|
8833
8849
|
mainSid = String(created.s.id);
|
|
@@ -8845,7 +8861,9 @@ ipcMain.handle('finance:trader:start', async () => {
|
|
|
8845
8861
|
financeLog('[trader] başlatıldı (model: ' + (f.traderSel || 'genel aktif model') + ')');
|
|
8846
8862
|
finPush('trader', { state: 'running', round: mainAgent.round });
|
|
8847
8863
|
return { ok: true, sid: mainSid };
|
|
8848
|
-
}
|
|
8864
|
+
}
|
|
8865
|
+
|
|
8866
|
+
ipcMain.handle('finance:trader:start', () => financeTraderStart());
|
|
8849
8867
|
|
|
8850
8868
|
ipcMain.handle('finance:trader:stop', async () => {
|
|
8851
8869
|
financeState.traderOn = false;
|
package/src/renderer/renderer.js
CHANGED
|
@@ -5031,18 +5031,35 @@ async function loadRailChat(id) {
|
|
|
5031
5031
|
açılır — gelen/giden hepsi kronolojik. Thread = ajan çifti + KONU. */
|
|
5032
5032
|
|
|
5033
5033
|
const dmState = {
|
|
5034
|
-
dms: [], // { at, from, fromTitle, to, toTitle, topic, text }
|
|
5034
|
+
dms: [], // { at, from, fromTitle, to, toTitle, topic, text, group?, closed? }
|
|
5035
|
+
groups: [], // { id, title, members, titles, createdAt, closed, closedAt }
|
|
5035
5036
|
modalThread: null, // modalde açık oturum key'i (null = oturum listesi)
|
|
5036
5037
|
};
|
|
5037
5038
|
|
|
5039
|
+
function dmGroupOf(id) {
|
|
5040
|
+
return (dmState.groups || []).find((g) => String(g.id) === String(id)) || null;
|
|
5041
|
+
}
|
|
5042
|
+
|
|
5038
5043
|
function dmThreadOf(dm) {
|
|
5044
|
+
if (dm.group) return 'G|' + String(dm.group);
|
|
5039
5045
|
const a = String(dm.from || '');
|
|
5040
5046
|
const b = String(dm.to || '');
|
|
5041
5047
|
const pair = a < b ? a + '|' + b : b + '|' + a;
|
|
5042
5048
|
return pair + '|' + String(dm.topic || '(genel)').toLowerCase();
|
|
5043
5049
|
}
|
|
5044
5050
|
|
|
5051
|
+
function dmThreadClosed(msgs) {
|
|
5052
|
+
const last = msgs[msgs.length - 1];
|
|
5053
|
+
if (!last) return false;
|
|
5054
|
+
if (last.group) {
|
|
5055
|
+
const g = dmGroupOf(last.group);
|
|
5056
|
+
return !!(g && g.closed);
|
|
5057
|
+
}
|
|
5058
|
+
return !!last.closed;
|
|
5059
|
+
}
|
|
5060
|
+
|
|
5045
5061
|
function dmThreadTitle(dm) {
|
|
5062
|
+
if (dm.group) return '👥 ' + String(dm.groupTitle || 'Grup');
|
|
5046
5063
|
const a = String(dm.fromTitle || 'Ajan');
|
|
5047
5064
|
const b = String(dm.toTitle || 'Ajan');
|
|
5048
5065
|
const pair = a < b ? a + ' ↔ ' + b : b + ' ↔ ' + a;
|
|
@@ -5069,14 +5086,61 @@ function dmTime(iso) {
|
|
|
5069
5086
|
}
|
|
5070
5087
|
|
|
5071
5088
|
function dmMsgHtml(m) {
|
|
5089
|
+
const who = m.group
|
|
5090
|
+
? escapeHtml(String(m.fromTitle || 'Ajan'))
|
|
5091
|
+
: `${escapeHtml(String(m.fromTitle || 'Ajan'))} → ${escapeHtml(String(m.toTitle || 'Ajan'))}`;
|
|
5072
5092
|
return (
|
|
5073
|
-
`<div class="dmt-msg"><span class="dm-who">${
|
|
5093
|
+
`<div class="dmt-msg"><span class="dm-who">${who}</span>` +
|
|
5074
5094
|
`${escapeHtml(String(m.text || ''))}<span class="dm-at">${dmTime(m.at)}</span></div>`
|
|
5075
5095
|
);
|
|
5076
5096
|
}
|
|
5077
5097
|
|
|
5098
|
+
/* bir sohbet kartı (rail + modal ortak): AKTİF/GEÇMİŞ ayrımı için closed
|
|
5099
|
+
bayrağı kartta işaretlenir */
|
|
5100
|
+
function dmThreadCard(key, msgs, onClick) {
|
|
5101
|
+
const last = msgs[msgs.length - 1];
|
|
5102
|
+
const closed = dmThreadClosed(msgs);
|
|
5103
|
+
const el = document.createElement('div');
|
|
5104
|
+
el.className = 'dmt' + (closed ? ' dmt-closed' : '');
|
|
5105
|
+
let membersLine = '';
|
|
5106
|
+
if (last.group) {
|
|
5107
|
+
const g = dmGroupOf(last.group);
|
|
5108
|
+
if (g && Array.isArray(g.members) && g.members.length) {
|
|
5109
|
+
const names = g.members.map((m) => (g.titles && g.titles[m]) || m).slice(0, 4);
|
|
5110
|
+
membersLine = `<div class="dmt-members">👥 ${escapeHtml(names.join(', '))}</div>`;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
el.innerHTML =
|
|
5114
|
+
'<div class="dmt-head">' +
|
|
5115
|
+
`<span class="dmt-title">${escapeHtml(dmThreadTitle(last))}</span>` +
|
|
5116
|
+
`<span class="dmt-count">${closed ? 'kapandı' : msgs.length}</span>` +
|
|
5117
|
+
'</div>' +
|
|
5118
|
+
membersLine +
|
|
5119
|
+
`<div class="dmt-last">${escapeHtml(String(last.text || '').replace(/\s+/g, ' ').slice(0, 90))} · ${dmTime(last.at)}</div>`;
|
|
5120
|
+
el.addEventListener('click', onClick);
|
|
5121
|
+
return el;
|
|
5122
|
+
}
|
|
5123
|
+
|
|
5078
5124
|
/* --- sağ sütun: OTURUM LİSTESİ (rail gibi) --- */
|
|
5079
5125
|
|
|
5126
|
+
/* AKTİF/GEÇMİŞ ayrılmış sohbet listesi (rail + modal ortak) */
|
|
5127
|
+
function dmAppendThreadSections(list, onClickThread) {
|
|
5128
|
+
const all = dmThreads();
|
|
5129
|
+
const act = all.filter(([, msgs]) => !dmThreadClosed(msgs));
|
|
5130
|
+
const past = all.filter(([, msgs]) => dmThreadClosed(msgs));
|
|
5131
|
+
const sec = (label, items) => {
|
|
5132
|
+
if (!items.length) return;
|
|
5133
|
+
const h = document.createElement('div');
|
|
5134
|
+
h.className = 'dm-sec';
|
|
5135
|
+
h.textContent = label;
|
|
5136
|
+
list.appendChild(h);
|
|
5137
|
+
for (const [key, msgs] of items) list.appendChild(dmThreadCard(key, msgs, () => onClickThread(key)));
|
|
5138
|
+
};
|
|
5139
|
+
sec('● AKTİF', act);
|
|
5140
|
+
sec('— GEÇMİŞ', past);
|
|
5141
|
+
return all.length;
|
|
5142
|
+
}
|
|
5143
|
+
|
|
5080
5144
|
function renderDmRail() {
|
|
5081
5145
|
const list = els.dmRailList;
|
|
5082
5146
|
if (!list) return;
|
|
@@ -5085,25 +5149,17 @@ function renderDmRail() {
|
|
|
5085
5149
|
return;
|
|
5086
5150
|
}
|
|
5087
5151
|
list.innerHTML = '';
|
|
5088
|
-
|
|
5089
|
-
const last = msgs[msgs.length - 1];
|
|
5090
|
-
const el = document.createElement('div');
|
|
5091
|
-
el.className = 'dmt';
|
|
5092
|
-
el.innerHTML =
|
|
5093
|
-
'<div class="dmt-head">' +
|
|
5094
|
-
`<span class="dmt-title">${escapeHtml(dmThreadTitle(last))}</span>` +
|
|
5095
|
-
`<span class="dmt-count">${msgs.length}</span>` +
|
|
5096
|
-
'</div>' +
|
|
5097
|
-
`<div class="dmt-last">${escapeHtml(String(last.text || '').replace(/\s+/g, ' ').slice(0, 90))} · ${dmTime(last.at)}</div>`;
|
|
5098
|
-
el.addEventListener('click', () => openDmModal(key)); // sohbet yalnız modalde
|
|
5099
|
-
list.appendChild(el);
|
|
5100
|
-
}
|
|
5152
|
+
dmAppendThreadSections(list, (key) => openDmModal(key)); // sohbet yalnız modalde
|
|
5101
5153
|
}
|
|
5102
5154
|
|
|
5103
5155
|
async function refreshDmRail() {
|
|
5104
5156
|
try {
|
|
5105
|
-
const
|
|
5106
|
-
if (Array.isArray(
|
|
5157
|
+
const res = await beast.agentDmsList();
|
|
5158
|
+
if (Array.isArray(res)) dmState.dms = res;
|
|
5159
|
+
else {
|
|
5160
|
+
dmState.dms = (res && res.dms) || [];
|
|
5161
|
+
dmState.groups = (res && res.groups) || [];
|
|
5162
|
+
}
|
|
5107
5163
|
} catch {}
|
|
5108
5164
|
renderDmRail();
|
|
5109
5165
|
if (els.dmOverlay && !els.dmOverlay.hidden) renderDmModal();
|
|
@@ -5142,26 +5198,14 @@ function renderDmModal() {
|
|
|
5142
5198
|
if (box) box.scrollTop = box.scrollHeight;
|
|
5143
5199
|
return;
|
|
5144
5200
|
}
|
|
5145
|
-
/* OTURUM LİSTESİ */
|
|
5146
|
-
if (els.dmDialogTitle) els.dmDialogTitle.textContent = 'AJAN DM —
|
|
5201
|
+
/* OTURUM LİSTESİ — AKTİF + GEÇMİŞ bölümleri */
|
|
5202
|
+
if (els.dmDialogTitle) els.dmDialogTitle.textContent = 'AJAN DM — AKTİF & GEÇMİŞ';
|
|
5147
5203
|
if (els.dmBackBtn) els.dmBackBtn.hidden = true;
|
|
5148
5204
|
list.innerHTML = '';
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
el.innerHTML =
|
|
5154
|
-
'<div class="dmt-head">' +
|
|
5155
|
-
`<span class="dmt-title">${escapeHtml(dmThreadTitle(last))}</span>` +
|
|
5156
|
-
`<span class="dmt-count">${msgs.length}</span>` +
|
|
5157
|
-
'</div>' +
|
|
5158
|
-
`<div class="dmt-last">${escapeHtml(String(last.text || '').replace(/\s+/g, ' ').slice(0, 120))} · ${dmTime(last.at)}</div>`;
|
|
5159
|
-
el.addEventListener('click', () => {
|
|
5160
|
-
dmState.modalThread = key;
|
|
5161
|
-
renderDmModal();
|
|
5162
|
-
});
|
|
5163
|
-
list.appendChild(el);
|
|
5164
|
-
}
|
|
5205
|
+
dmAppendThreadSections(list, (key) => {
|
|
5206
|
+
dmState.modalThread = key;
|
|
5207
|
+
renderDmModal();
|
|
5208
|
+
});
|
|
5165
5209
|
}
|
|
5166
5210
|
|
|
5167
5211
|
function openDmModal(threadKey) {
|
package/src/renderer/style.css
CHANGED
|
@@ -2157,6 +2157,18 @@ body.rail-hidden #dmRailHead { padding-right: 148px; }
|
|
|
2157
2157
|
font-size: 11px; margin-top: 4px;
|
|
2158
2158
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
2159
2159
|
}
|
|
2160
|
+
/* AJAN DM: AKTİF/GEÇMİŞ bölüm başlıkları + kapalı sohbet görünümü */
|
|
2161
|
+
.dm-sec {
|
|
2162
|
+
font-size: 10.5px; font-weight: 800; letter-spacing: 0.08em;
|
|
2163
|
+
color: var(--muted);
|
|
2164
|
+
padding: 8px 2px 4px;
|
|
2165
|
+
}
|
|
2166
|
+
.dmt.dmt-closed { opacity: 0.62; }
|
|
2167
|
+
.dmt.dmt-closed .dmt-count { background: var(--border); color: var(--muted); }
|
|
2168
|
+
.dmt-members {
|
|
2169
|
+
font-size: 10.5px; color: var(--muted); margin-top: 3px;
|
|
2170
|
+
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
|
2171
|
+
}
|
|
2160
2172
|
.dmt-msgs {
|
|
2161
2173
|
display: none;
|
|
2162
2174
|
margin-top: 6px;
|