beast-agent 2.20.0 → 2.22.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/customtools.js +18 -8
- package/src/agent/engine.js +74 -8
- package/src/agent/skills.js +83 -1
- package/src/main.js +26 -14
- package/src/renderer/index.html +16 -0
- package/src/renderer/renderer.js +144 -52
- package/src/renderer/style.css +78 -8
package/package.json
CHANGED
package/src/agent/customtools.js
CHANGED
|
@@ -40,6 +40,22 @@ function invalidate() {
|
|
|
40
40
|
cache = null;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/* klasör damgası: ajan/kullanıcı klasöre elle müdahale edince (write_file
|
|
44
|
+
vb.) cache BAYATLAR — damga değişince otomatik yeniden taranır */
|
|
45
|
+
let lastStamp = 0;
|
|
46
|
+
function folderStamp() {
|
|
47
|
+
try { return fs.statSync(dir()).mtimeMs; } catch { return 0; }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function ensureFresh() {
|
|
51
|
+
const s = folderStamp();
|
|
52
|
+
if (dirty || !cache || s !== lastStamp) {
|
|
53
|
+
cache = scan();
|
|
54
|
+
dirty = false;
|
|
55
|
+
lastStamp = s;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
43
59
|
function readTool(d) {
|
|
44
60
|
try {
|
|
45
61
|
const json = JSON.parse(fs.readFileSync(path.join(d, 'tool.json'), 'utf8'));
|
|
@@ -81,10 +97,7 @@ function list() {
|
|
|
81
97
|
|
|
82
98
|
/* engine _chatTurn'e OpenAI function formatında şemalar */
|
|
83
99
|
function definitions() {
|
|
84
|
-
|
|
85
|
-
cache = scan();
|
|
86
|
-
dirty = false;
|
|
87
|
-
}
|
|
100
|
+
ensureFresh();
|
|
88
101
|
return cache.map((t) => ({
|
|
89
102
|
type: 'function',
|
|
90
103
|
function: {
|
|
@@ -97,10 +110,7 @@ function definitions() {
|
|
|
97
110
|
}
|
|
98
111
|
|
|
99
112
|
function names() {
|
|
100
|
-
|
|
101
|
-
cache = scan();
|
|
102
|
-
dirty = false;
|
|
103
|
-
}
|
|
113
|
+
ensureFresh();
|
|
104
114
|
return cache.map((t) => 'tool__' + t.id);
|
|
105
115
|
}
|
|
106
116
|
|
package/src/agent/engine.js
CHANGED
|
@@ -2190,7 +2190,52 @@ class Engine {
|
|
|
2190
2190
|
flat[k] = rest;
|
|
2191
2191
|
}
|
|
2192
2192
|
}
|
|
2193
|
-
|
|
2193
|
+
/* emniyet: assistant(tool_calls) ↔ tool sonucu ÇİFTLERİ hizala —
|
|
2194
|
+
yetim tool mesajı / cevapsız tool_call sağlayıcıda
|
|
2195
|
+
"Messages with role 'tool' must be a response to a preceding message
|
|
2196
|
+
with 'tool_calls'" (HTTP 400) verir (tur ortası enjeksiyon/kırpma
|
|
2197
|
+
çifti böldüğünde). */
|
|
2198
|
+
return Engine._alignToolPairs([{ role: 'system', content: system }, ...head, ...flat]);
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
/* assistant(tool_calls) + hemen ardından gelen tool sonuçlarını hizalar:
|
|
2202
|
+
- cevabı GELMEYEN tool_call, assistant'tan DÜŞER (sonuç yok)
|
|
2203
|
+
- karşılıksız tool sonucu (yetim) DÜŞER
|
|
2204
|
+
- hiç sonucu olmayan assistant, düz metin asistana döner
|
|
2205
|
+
- tool_calls'sız mesajların ardından gelen tool'lar da yetimdir → düşer */
|
|
2206
|
+
static _alignToolPairs(msgs) {
|
|
2207
|
+
const out = [];
|
|
2208
|
+
for (let i = 0; i < msgs.length; i++) {
|
|
2209
|
+
const m = msgs[i];
|
|
2210
|
+
if (m && m.role === 'assistant' && Array.isArray(m.tool_calls) && m.tool_calls.length) {
|
|
2211
|
+
const results = [];
|
|
2212
|
+
let j = i + 1;
|
|
2213
|
+
while (j < msgs.length && msgs[j] && msgs[j].role === 'tool') {
|
|
2214
|
+
results.push(msgs[j]);
|
|
2215
|
+
j++;
|
|
2216
|
+
}
|
|
2217
|
+
const answered = new Set(results.map((r) => String(r.tool_call_id || '')));
|
|
2218
|
+
const kept = m.tool_calls.filter((tc) => answered.has(String(tc.id || '')));
|
|
2219
|
+
if (kept.length === m.tool_calls.length) {
|
|
2220
|
+
out.push(m);
|
|
2221
|
+
} else if (kept.length) {
|
|
2222
|
+
out.push({ ...m, tool_calls: kept });
|
|
2223
|
+
} else {
|
|
2224
|
+
const plain = { ...m };
|
|
2225
|
+
delete plain.tool_calls;
|
|
2226
|
+
out.push(plain);
|
|
2227
|
+
}
|
|
2228
|
+
const keepIds = new Set(kept.map((tc) => String(tc.id || '')));
|
|
2229
|
+
for (const r of results) {
|
|
2230
|
+
if (keepIds.has(String(r.tool_call_id || ''))) out.push(r);
|
|
2231
|
+
}
|
|
2232
|
+
i = j - 1;
|
|
2233
|
+
continue;
|
|
2234
|
+
}
|
|
2235
|
+
if (m && m.role === 'tool') continue; // yetim araç sonucu
|
|
2236
|
+
out.push(m);
|
|
2237
|
+
}
|
|
2238
|
+
return out;
|
|
2194
2239
|
}
|
|
2195
2240
|
|
|
2196
2241
|
_lastUserText(session) {
|
|
@@ -5317,23 +5362,25 @@ function emitSafe(engine, sessionId, ev) {
|
|
|
5317
5362
|
|
|
5318
5363
|
/* AJAN DM aracı: paralel/finance ajanlarının birbirine kısa mesaj atması.
|
|
5319
5364
|
Salt metin — hedef ajanın oturumuna [AJAN DM] bloğu olarak düşer; AJAN DM
|
|
5320
|
-
panelinde iki yönlü görünür.
|
|
5365
|
+
panelinde iki yönlü görünür. topic verirsen aynı konudaki mesajlar aynı
|
|
5366
|
+
oturumda toplanır (cevap zinciri tek thread'de devam eder). */
|
|
5321
5367
|
const AGENT_DM_DEF = {
|
|
5322
5368
|
type: 'function',
|
|
5323
5369
|
function: {
|
|
5324
5370
|
name: 'agent_dm',
|
|
5325
5371
|
description:
|
|
5326
|
-
'Send a short DM to another
|
|
5372
|
+
'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.',
|
|
5327
5373
|
parameters: {
|
|
5328
5374
|
type: 'object',
|
|
5329
5375
|
properties: {
|
|
5330
5376
|
to: { type: 'string', description: 'Target agent session id or a title keyword' },
|
|
5377
|
+
topic: { type: 'string', description: 'Short subject label for the conversation thread, e.g. "GOLD pozisyon riski"' },
|
|
5331
5378
|
message: { type: 'string', description: 'Short message (1-3 sentences)' },
|
|
5332
5379
|
},
|
|
5333
5380
|
required: ['to', 'message'],
|
|
5334
5381
|
},
|
|
5335
5382
|
},
|
|
5336
|
-
};
|
|
5383
|
+
};
|
|
5337
5384
|
|
|
5338
5385
|
const TOOLS = [
|
|
5339
5386
|
...tools.definitions,
|
|
@@ -6150,12 +6197,30 @@ Engine.prototype._agentDmSend = function (fromSid, args) {
|
|
|
6150
6197
|
if (String(target) === String(fromSid)) return { ok: false, error: 'kendine DM atılamaz' };
|
|
6151
6198
|
const fromJob = jobs.get(String(fromSid));
|
|
6152
6199
|
const toJob = jobs.get(target);
|
|
6200
|
+
/* FİNANS SINIRI: Beast Finance oturumu agent_dm kullansa bile finance
|
|
6201
|
+
dünyasının DIŞINA ÇIKMAZ — yalnız finance ajanlarına DM atabilir
|
|
6202
|
+
(trader ↔ GOLD işçisi ↔ finance işçileri). Finance dışı hedef reddedilir. */
|
|
6203
|
+
const fromSess = this.cache.get(String(fromSid)) || null;
|
|
6204
|
+
if (fromSess && fromSess.finance) {
|
|
6205
|
+
const targetSess = this.cache.get(String(target)) || null;
|
|
6206
|
+
if (!targetSess || !targetSess.finance) {
|
|
6207
|
+
return {
|
|
6208
|
+
ok: false,
|
|
6209
|
+
error:
|
|
6210
|
+
'Beast Finance ajanı yalnız finance dünyasındaki ajanlara DM atabilir — hedef finance dışı: ' +
|
|
6211
|
+
String((toJob && toJob.title) || to),
|
|
6212
|
+
};
|
|
6213
|
+
}
|
|
6214
|
+
}
|
|
6215
|
+
/* konu etiketi: aynı konudaki DM'ler panelde AYNI oturumda toplanır */
|
|
6216
|
+
const topic = String((args && args.topic) || '').replace(/\s+/g, ' ').trim().slice(0, 60) || '(genel)';
|
|
6153
6217
|
const dm = {
|
|
6154
6218
|
at: new Date().toISOString(),
|
|
6155
6219
|
from: String(fromSid),
|
|
6156
6220
|
fromTitle: (fromJob && fromJob.title) || 'Ajan',
|
|
6157
6221
|
to: String(target),
|
|
6158
6222
|
toTitle: (toJob && toJob.title) || 'Ajan',
|
|
6223
|
+
topic,
|
|
6159
6224
|
text,
|
|
6160
6225
|
};
|
|
6161
6226
|
this._agentDms = this._agentDms || [];
|
|
@@ -6165,15 +6230,16 @@ Engine.prototype._agentDmSend = function (fromSid, args) {
|
|
|
6165
6230
|
fs.writeFileSync(this._agentDmsFile, JSON.stringify({ dms: this._agentDms }, null, 2));
|
|
6166
6231
|
} catch {}
|
|
6167
6232
|
emitSafe(this, target, { type: 'agent-dm', ...dm });
|
|
6168
|
-
/* teslim: hedef meşgulse pending kuyruğunda bekler, done olunca düşer
|
|
6233
|
+
/* teslim: hedef meşgulse pending kuyruğunda bekler, done olunca düşer.
|
|
6234
|
+
Cevap zinciri AYNI thread'den sürsün diye topic geri bildirilir. */
|
|
6169
6235
|
(this._pendingReports = this._pendingReports || []).push({
|
|
6170
6236
|
parentId: target,
|
|
6171
6237
|
text:
|
|
6172
|
-
`[AJAN DM — ${dm.fromTitle}]\n${text}\n` +
|
|
6173
|
-
`(Cevabını agent_dm aracıyla ver — to: "${dm.fromTitle}")`,
|
|
6238
|
+
`[AJAN DM — ${dm.fromTitle} · konu: "${topic}"]\n${text}\n` +
|
|
6239
|
+
`(Cevabını agent_dm aracıyla ver — to: "${dm.fromTitle}", topic: "${topic}")`,
|
|
6174
6240
|
});
|
|
6175
6241
|
this.flushPendingReports(target);
|
|
6176
|
-
return { ok: true, to: target, toTitle: dm.toTitle };
|
|
6242
|
+
return { ok: true, to: target, toTitle: dm.toTitle, topic };
|
|
6177
6243
|
} catch (e) {
|
|
6178
6244
|
return { ok: false, error: String((e && e.message) || e) };
|
|
6179
6245
|
}
|
package/src/agent/skills.js
CHANGED
|
@@ -129,7 +129,8 @@ Get-Content "$env:APPDATA\\beast\\sessions\\bg-jobs.json" | ConvertFrom-Json | %
|
|
|
129
129
|
| \`agent/agentdefs.js\` | Özel ajan tanımları: %APPDATA%\beast\agents\\*.md (frontmatter: model/tools/steps/mode + prompt gövdesi); /agent ile bağlanır, run_background(agent:) ile paralel ajana verilir |
|
|
130
130
|
| \`agent/searxng.js\` | SearXNG entegrasyonu: gömülü Python'a kurulum + 127.0.0.1:8888 başlatıcı + JSON arama motoru |
|
|
131
131
|
| \`agent/bots.js\` | Bot CRUD, 5 haneli bot kodu, izolasyon; ad-ilk-harf doğrulaması |
|
|
132
|
-
| \`agent/skills.js\` | Builtin tohumlar (help, email, python-web-search, pdf, gold-trading + superpowers metodoloji paketi: brainstorming/writing-plans/executing-plans/TDD/paralel-ajan/alt-ajan-geliştirme/sistematik-debugging/doğrulama/skill-yazma) + SKILL.md taraması + taslaklar |
|
|
132
|
+
| \`agent/skills.js\` | Builtin tohumlar (help, email, python-web-search, pdf, gold-trading, tool-yazma + superpowers metodoloji paketi: brainstorming/writing-plans/executing-plans/TDD/paralel-ajan/alt-ajan-geliştirme/sistematik-debugging/doğrulama/skill-yazma) + SKILL.md taraması + taslaklar |
|
|
133
|
+
| \`agent/customtools.js\` | Kişisel toollar: %APPDATA%\\beast\\tools\\<ad>\\tool.json + run.js (stdin JSON → stdout JSON) — tool__<ad> olarak TÜM oturumlara açılır; sol panel TOOLLAR kutusunda yazılır/test edilir. Yeni tool yazarken \`tool-yazma\` skill'ini oku. |
|
|
133
134
|
| \`agent/memory.js\`, \`agent/kb.js\` | Kalıcı hafıza + bilgi bankası |
|
|
134
135
|
| \`cron.js\` (src kökü), \`agent/watchers.js\`, \`agent/bus.js\` | Cron, izleyici, olay merkezi — /stop bunlara DOKUNMAZ |
|
|
135
136
|
| \`agent/usage.js\` | Token/maliyet sayaçları |
|
|
@@ -456,6 +457,87 @@ const { createWorker } = require('tesseract.js');
|
|
|
456
457
|
| read_file boş metin döndü | taranmış PDF → 4. adım (görselleştir) + 5. adım (OCR) |
|
|
457
458
|
| tesseract dil verisi hatası | ilk indirme internet ister; tekrar dene |`,
|
|
458
459
|
},
|
|
460
|
+
{
|
|
461
|
+
folder: 'tool-yazma',
|
|
462
|
+
force: true,
|
|
463
|
+
body: `---
|
|
464
|
+
name: tool-yazma
|
|
465
|
+
description: Kişisel TOOL yazma/düzenleme rehberi — %APPDATA%\\beast\\tools\\<ad>\\ altına tool.json + run.js yazılacak veya mevcut bir tool editlenecekse ÖNCE bu skill'i oku. Tool'un sözleşmesi: stdin JSON argüman → stdout JSON sonuç; ajan tool__<ad> olarak çağırır (Beast Finance dahil tüm oturumlarda çalışır).
|
|
466
|
+
version: 1.0.0
|
|
467
|
+
---
|
|
468
|
+
|
|
469
|
+
# Kişisel TOOL Yazma
|
|
470
|
+
|
|
471
|
+
## TOOL NEDİR / NE ZAMAN YAZILIR
|
|
472
|
+
|
|
473
|
+
Kişisel tool = modelin alet çantasına GİREN gerçek bir araçtır: %APPDATA%\\beast\\tools\\<slug>\\ klasöründe **tool.json** (tanım) + **run.js** (kod) bulunur. Tüm oturumlarda — Beast Finance chat/trader/işçiler dahil — \`tool__<slug>\` adıyla çağrılır.
|
|
474
|
+
|
|
475
|
+
Yaz ZAMAN: mevcut araçlar işi görmüyorsa (örn. broker sembol adlandırması mt5_trade'i düşürüyorsa), özel bir API'ye bağlanman, tekrarlı bir hesap/işlem rutini gerekiyorsa. Basit işler için YAZMA — önce yerleşik araçları (mt5_*, web_search, python_run, run_command) dene.
|
|
476
|
+
|
|
477
|
+
## SÖZLEŞME (birebir uygula)
|
|
478
|
+
|
|
479
|
+
- **stdin**: JSON argümanlar (tek satırda, güvenli parse)
|
|
480
|
+
- **stdout**: SADECE JSON sonuç — son satır JSON'a çevrilmeye çalışılır; günlük/metin yazarsan JSON bozulur, \`console.error\` kullan
|
|
481
|
+
- **çalışma klasörü**: tool'un kendi klasörü (yanına dosya yazabilirsin)
|
|
482
|
+
- **zaman aşımı**: 90 sn — uzun işleri kendin parçalama, \`run_background\`'a devret
|
|
483
|
+
- **sonuç biçimi**: \`{ "ok": true, ... }\` ya da \`{ "ok": false, "error": "sebep" }\` — ok alanı ZORUNLU
|
|
484
|
+
|
|
485
|
+
## YENİ TOOL OLUŞTURMA (2 dosya, write_file ile)
|
|
486
|
+
|
|
487
|
+
\`%APPDATA%\\beast\\tools\\<slug>\\tool.json\`:
|
|
488
|
+
\`\`\`json
|
|
489
|
+
{
|
|
490
|
+
"name": "gold_emir",
|
|
491
|
+
"description": "GOLD için özel emir açar — side (buy/sell), lot, sl, tp alır; sonucu ticket ile döndürür.",
|
|
492
|
+
"parameters": {
|
|
493
|
+
"type": "object",
|
|
494
|
+
"properties": {
|
|
495
|
+
"side": { "type": "string", "description": "buy | sell" },
|
|
496
|
+
"lot": { "type": "number", "description": "0.01-1.0" },
|
|
497
|
+
"sl": { "type": "number" },
|
|
498
|
+
"tp": { "type": "number" }
|
|
499
|
+
},
|
|
500
|
+
"required": ["side", "lot"]
|
|
501
|
+
},
|
|
502
|
+
"updatedAt": "2026-09-09T00:00:00.000Z"
|
|
503
|
+
}
|
|
504
|
+
\`\`\`
|
|
505
|
+
|
|
506
|
+
\`%APPDATA%\\beast\\tools\\<slug>\\run.js\`:
|
|
507
|
+
\`\`\`javascript
|
|
508
|
+
'use strict';
|
|
509
|
+
// stdin: JSON args · stdout: JSON sonuç (SADECE JSON yaz!)
|
|
510
|
+
const fs = require('fs');
|
|
511
|
+
let args = {};
|
|
512
|
+
try { args = JSON.parse(fs.readFileSync(0, 'utf8') || '{}'); } catch {}
|
|
513
|
+
(function main() {
|
|
514
|
+
const { side, lot, sl, tp } = args;
|
|
515
|
+
if (!side || !lot) {
|
|
516
|
+
console.log(JSON.stringify({ ok: false, error: 'side ve lot zorunlu' }));
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
// --- kendi işin burada: HTTP çağrısı, MT5 köprüsü, dosya, hesap... ---
|
|
520
|
+
// Örn: https modülüyle özel endpoint'e istek at, sonucu ok alanıyla sar.
|
|
521
|
+
console.log(JSON.stringify({ ok: true, side, lot, sl: sl || 0, tp: tp || 0, ticket: 0 }));
|
|
522
|
+
})();
|
|
523
|
+
\`\`\`
|
|
524
|
+
|
|
525
|
+
Slug kuralları: a-z, 0-9, alt çizgi, tire — 2-24 karakter (örn: gold_emir, haber_ozet). Hazır taslak: %APPDATA%\\beast\\tools\\ornek_echo klasörünü referans al; yeni tool'u o biçimle yaz.
|
|
526
|
+
|
|
527
|
+
## DÜZENLEME / DOĞRULAMA
|
|
528
|
+
|
|
529
|
+
1. Mevcut toolu değiştirme: \`edit_file\` ile tool.json / run.js düzenle — ASLA tüm klasörü silip baştan yazma.
|
|
530
|
+
2. Kayıttan sonra tool ANINDA aktifleşir (yeniden başlatma gerekmez) — bir sonraki turda \`tool__<slug>\` çağrılabilir.
|
|
531
|
+
3. Doğrula: \`run_command\` ile \`echo \\"{\\\\\\"mesaj\\\\\\":\\\\\\"test\\\\\\"}\\" | node \\"%APPDATA%\\\\beast\\\\tools\\\\<slug>\\\\run.js\\"\` — çıktı JSON olmalı ve \`ok\` alanı içermeli.
|
|
532
|
+
4. Hata çıktısı \`ok:false + error\` olsun — model hatayı okuyup düzeltir; çıplak stack yasak.
|
|
533
|
+
|
|
534
|
+
## GÜVENLİK + STİL
|
|
535
|
+
|
|
536
|
+
- Şifre/API anahtarını ASLA stdout'a, log'a, skill'e yazma.
|
|
537
|
+
- Ağ çağrılarında timeout kullan (\`https\` modülü + \`req.setTimeout\` veya fetch); sonsuz bekleyen script yazma.
|
|
538
|
+
- Tek sorumluluk: bir tool = bir iş. Çok işli dev tool yazma — parçala.
|
|
539
|
+
- Kullanıcıya tool'u nasıl yöneteceğini söyle: sol panelde TOOLLAR kutusunda test/düzenle/sil + 📂 klasör.`,
|
|
540
|
+
},
|
|
459
541
|
];
|
|
460
542
|
|
|
461
543
|
function writeSeed(seed) {
|
package/src/main.js
CHANGED
|
@@ -5063,22 +5063,19 @@ ipcMain.handle('agent:send', (_e, { sessionId, text }) => {
|
|
|
5063
5063
|
}
|
|
5064
5064
|
}
|
|
5065
5065
|
} catch {}
|
|
5066
|
-
/* BEAST FINANCE: finance
|
|
5067
|
-
|
|
5066
|
+
/* BEAST FINANCE: finance modunda yazılan sohbetler KALICI finance etiketi
|
|
5067
|
+
taşır — beast finance'ta kayıtlı kalır; normal modda yazmak etiketi
|
|
5068
|
+
KALDIRMAZ (oturum finance dışına çıkamaz) */
|
|
5068
5069
|
try {
|
|
5069
5070
|
if (sessionId) {
|
|
5070
5071
|
const fsid = String(sessionId);
|
|
5071
5072
|
let fsess = engine.cache.get(fsid);
|
|
5072
5073
|
if (!fsess) { try { fsess = engine._load(fsid); } catch {} }
|
|
5073
|
-
if (fsess && !fsess.botId && !fsess.bgJob) {
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
engine.cache.set(fsid, fsess);
|
|
5079
|
-
} else if (fsess.finance) {
|
|
5080
|
-
engine.unmarkFinance(fsid); /* normal modda yazınca normal sohbete döner */
|
|
5081
|
-
}
|
|
5074
|
+
if (fsess && !fsess.botId && !fsess.bgJob && financeState.mode && !fsess.finance) {
|
|
5075
|
+
engine.markFinance(fsid, false);
|
|
5076
|
+
finApplyTraderFields(fsess);
|
|
5077
|
+
fsess.financeTrader = false; /* chat copilot'ı — trader değil */
|
|
5078
|
+
engine.cache.set(fsid, fsess);
|
|
5082
5079
|
}
|
|
5083
5080
|
}
|
|
5084
5081
|
} catch {}
|
|
@@ -8369,6 +8366,22 @@ try {
|
|
|
8369
8366
|
});
|
|
8370
8367
|
} catch {}
|
|
8371
8368
|
|
|
8369
|
+
/* TOOLS klasörü izleyici: ajan write_file ile tool yazdığında (ya da elle)
|
|
8370
|
+
konsol OTOMATİK güncellenir — restart gerekmez */
|
|
8371
|
+
try {
|
|
8372
|
+
fs.mkdirSync(customtools.dir(), { recursive: true });
|
|
8373
|
+
let toolWatchTimer = null;
|
|
8374
|
+
fs.watch(customtools.dir(), { recursive: true }, () => {
|
|
8375
|
+
clearTimeout(toolWatchTimer);
|
|
8376
|
+
toolWatchTimer = setTimeout(() => {
|
|
8377
|
+
customtools.invalidate();
|
|
8378
|
+
try {
|
|
8379
|
+
if (win && !win.isDestroyed()) win.webContents.send('agent:event', { type: 'tools-changed' });
|
|
8380
|
+
} catch {}
|
|
8381
|
+
}, 400);
|
|
8382
|
+
});
|
|
8383
|
+
} catch {}
|
|
8384
|
+
|
|
8372
8385
|
function financeEnsureBridge() {
|
|
8373
8386
|
const f = finCfg();
|
|
8374
8387
|
const candidates = [String(f.pythonPath || '').trim(), 'python', 'py -3'].filter(Boolean);
|
|
@@ -8744,10 +8757,9 @@ ipcMain.handle('finance:mode', async (_e, payload) => {
|
|
|
8744
8757
|
(eski davranış WhatsApp oturumunu da finance listesine karıştırıyordu) */
|
|
8745
8758
|
needNew = true;
|
|
8746
8759
|
}
|
|
8747
|
-
} else {
|
|
8748
|
-
/* mod kapandı: aktif finance oturumu normal sohbete döner */
|
|
8749
|
-
if (s.finance) engine.unmarkFinance(sid);
|
|
8750
8760
|
}
|
|
8761
|
+
/* mod kapandığında finance oturumları etiketlerini KORUR —
|
|
8762
|
+
beast finance'ta kayıtlı kalırlar (kaybolmazlar) */
|
|
8751
8763
|
engine.cache.set(sid, s);
|
|
8752
8764
|
}
|
|
8753
8765
|
} catch {}
|
package/src/renderer/index.html
CHANGED
|
@@ -373,6 +373,22 @@
|
|
|
373
373
|
</aside>
|
|
374
374
|
</div>
|
|
375
375
|
|
|
376
|
+
<div id="dmOverlay" hidden>
|
|
377
|
+
<div id="dmDialog">
|
|
378
|
+
<div id="dmDialogHead">
|
|
379
|
+
<span id="dmDialogTitle">AJAN DM — OTURUMLAR</span>
|
|
380
|
+
<span class="bot-bar-btns">
|
|
381
|
+
<button id="dmBackBtn" title="Oturum listesine dön" hidden>◀︎ Oturumlar</button>
|
|
382
|
+
<button id="dmColumnBtn" title="Yan sütunda canlı akış olarak da göster">▤︎</button>
|
|
383
|
+
<button id="dmModalClear" title="Tüm DM geçmişini sil"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg></button>
|
|
384
|
+
<button id="dmModalClose" title="Kapat">✕︎</button>
|
|
385
|
+
</span>
|
|
386
|
+
</div>
|
|
387
|
+
<div id="dmDialogList"></div>
|
|
388
|
+
<div class="dm-hint">Thread = ajan çifti + konu · gelen/giden hepsi kronolojik</div>
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
|
|
376
392
|
<div id="toolOverlay" hidden>
|
|
377
393
|
<div id="toolDialog">
|
|
378
394
|
<div id="toolDialogHead">
|
package/src/renderer/renderer.js
CHANGED
|
@@ -45,6 +45,12 @@ const els = {
|
|
|
45
45
|
dmBtn: $('#dmBtn'),
|
|
46
46
|
dmRailList: $('#dmRailList'),
|
|
47
47
|
dmClear: $('#dmClear'),
|
|
48
|
+
dmOverlay: $('#dmOverlay'),
|
|
49
|
+
dmDialogList: $('#dmDialogList'),
|
|
50
|
+
dmModalClose: $('#dmModalClose'),
|
|
51
|
+
dmModalClear: $('#dmModalClear'),
|
|
52
|
+
dmColumnBtn: $('#dmColumnBtn'),
|
|
53
|
+
dmBackBtn: $('#dmBackBtn'),
|
|
48
54
|
watchBtn: $('#watchBtn'),
|
|
49
55
|
watchPaneList: $('#watchPaneList'),
|
|
50
56
|
watchPaneOpen: $('#watchPaneOpen'),
|
|
@@ -5019,25 +5025,41 @@ async function loadRailChat(id) {
|
|
|
5019
5025
|
}
|
|
5020
5026
|
}
|
|
5021
5027
|
|
|
5022
|
-
/* ---------------- AJAN DM
|
|
5023
|
-
|
|
5024
|
-
|
|
5028
|
+
/* ---------------- AJAN DM (ajanlar arası konuşma) ----------------
|
|
5029
|
+
Paralel Ajanlar gibi: ✉ sağdaki SÜTUNU açar/kapatır (oturum listesi,
|
|
5030
|
+
canlı akış). Sütunda bir oturuma tıklayınca SOHBET yalnızca MODAL'da
|
|
5031
|
+
açılır — gelen/giden hepsi kronolojik. Thread = ajan çifti + KONU. */
|
|
5025
5032
|
|
|
5026
5033
|
const dmState = {
|
|
5027
|
-
dms: [], // { at, from, fromTitle, to, toTitle, text }
|
|
5028
|
-
|
|
5034
|
+
dms: [], // { at, from, fromTitle, to, toTitle, topic, text }
|
|
5035
|
+
modalThread: null, // modalde açık oturum key'i (null = oturum listesi)
|
|
5029
5036
|
};
|
|
5030
5037
|
|
|
5031
|
-
function
|
|
5038
|
+
function dmThreadOf(dm) {
|
|
5032
5039
|
const a = String(dm.from || '');
|
|
5033
5040
|
const b = String(dm.to || '');
|
|
5034
|
-
|
|
5041
|
+
const pair = a < b ? a + '|' + b : b + '|' + a;
|
|
5042
|
+
return pair + '|' + String(dm.topic || '(genel)').toLowerCase();
|
|
5035
5043
|
}
|
|
5036
5044
|
|
|
5037
|
-
function
|
|
5045
|
+
function dmThreadTitle(dm) {
|
|
5038
5046
|
const a = String(dm.fromTitle || 'Ajan');
|
|
5039
5047
|
const b = String(dm.toTitle || 'Ajan');
|
|
5040
|
-
|
|
5048
|
+
const pair = a < b ? a + ' ↔ ' + b : b + ' ↔ ' + a;
|
|
5049
|
+
const topic = String(dm.topic || '(genel)');
|
|
5050
|
+
return topic === '(genel)' ? pair : pair + ' · ' + topic;
|
|
5051
|
+
}
|
|
5052
|
+
|
|
5053
|
+
function dmThreads() {
|
|
5054
|
+
const threads = new Map();
|
|
5055
|
+
for (const dm of dmState.dms) {
|
|
5056
|
+
const key = dmThreadOf(dm);
|
|
5057
|
+
if (!threads.has(key)) threads.set(key, []);
|
|
5058
|
+
threads.get(key).push(dm);
|
|
5059
|
+
}
|
|
5060
|
+
return [...threads.entries()].sort(
|
|
5061
|
+
(x, y) => new Date(y[1][y[1].length - 1].at) - new Date(x[1][x[1].length - 1].at)
|
|
5062
|
+
);
|
|
5041
5063
|
}
|
|
5042
5064
|
|
|
5043
5065
|
function dmTime(iso) {
|
|
@@ -5046,51 +5068,34 @@ function dmTime(iso) {
|
|
|
5046
5068
|
return d.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
|
|
5047
5069
|
}
|
|
5048
5070
|
|
|
5071
|
+
function dmMsgHtml(m) {
|
|
5072
|
+
return (
|
|
5073
|
+
`<div class="dmt-msg"><span class="dm-who">${escapeHtml(String(m.fromTitle || 'Ajan'))} → ${escapeHtml(String(m.toTitle || 'Ajan'))}</span>` +
|
|
5074
|
+
`${escapeHtml(String(m.text || ''))}<span class="dm-at">${dmTime(m.at)}</span></div>`
|
|
5075
|
+
);
|
|
5076
|
+
}
|
|
5077
|
+
|
|
5078
|
+
/* --- sağ sütun: OTURUM LİSTESİ (rail gibi) --- */
|
|
5079
|
+
|
|
5049
5080
|
function renderDmRail() {
|
|
5050
5081
|
const list = els.dmRailList;
|
|
5051
5082
|
if (!list) return;
|
|
5052
5083
|
if (!dmState.dms.length) {
|
|
5053
|
-
list.innerHTML = '<div class="dm-empty">Ajanlar arasında DM yok.<br>Ajanlar agent_dm aracıyla birbirine mesaj attıkça burada
|
|
5084
|
+
list.innerHTML = '<div class="dm-empty">Ajanlar arasında DM yok.<br>Ajanlar agent_dm aracıyla birbirine mesaj attıkça oturumlar burada birikir.</div>';
|
|
5054
5085
|
return;
|
|
5055
5086
|
}
|
|
5056
|
-
/* çiftlere grupla (kronolojik) — en yeni thread üstte */
|
|
5057
|
-
const threads = new Map();
|
|
5058
|
-
for (const dm of dmState.dms) {
|
|
5059
|
-
const key = dmPairOf(dm);
|
|
5060
|
-
if (!threads.has(key)) threads.set(key, []);
|
|
5061
|
-
threads.get(key).push(dm);
|
|
5062
|
-
}
|
|
5063
|
-
const rows = [...threads.entries()].sort(
|
|
5064
|
-
(x, y) => new Date(y[1][y[1].length - 1].at) - new Date(x[1][x[1].length - 1].at)
|
|
5065
|
-
);
|
|
5066
5087
|
list.innerHTML = '';
|
|
5067
|
-
for (const [key, msgs] of
|
|
5068
|
-
const open = dmState.open.has(key);
|
|
5088
|
+
for (const [key, msgs] of dmThreads()) {
|
|
5069
5089
|
const last = msgs[msgs.length - 1];
|
|
5070
5090
|
const el = document.createElement('div');
|
|
5071
|
-
el.className = 'dmt'
|
|
5091
|
+
el.className = 'dmt';
|
|
5072
5092
|
el.innerHTML =
|
|
5073
5093
|
'<div class="dmt-head">' +
|
|
5074
|
-
`<span class="dmt-title">${escapeHtml(
|
|
5094
|
+
`<span class="dmt-title">${escapeHtml(dmThreadTitle(last))}</span>` +
|
|
5075
5095
|
`<span class="dmt-count">${msgs.length}</span>` +
|
|
5076
5096
|
'</div>' +
|
|
5077
|
-
`<div class="dmt-last">${escapeHtml(String(last.text || '').replace(/\s+/g, ' ').slice(0, 90))} · ${dmTime(last.at)}</div
|
|
5078
|
-
|
|
5079
|
-
msgs
|
|
5080
|
-
.map(
|
|
5081
|
-
(m) =>
|
|
5082
|
-
`<div class="dmt-msg"><span class="dm-who">${escapeHtml(String(m.fromTitle || 'Ajan'))} → ${escapeHtml(String(m.toTitle || 'Ajan'))}</span>` +
|
|
5083
|
-
`${escapeHtml(String(m.text || ''))}<span class="dm-at">${dmTime(m.at)}</span></div>`
|
|
5084
|
-
)
|
|
5085
|
-
.join('') +
|
|
5086
|
-
'</div>';
|
|
5087
|
-
el.addEventListener('click', () => {
|
|
5088
|
-
if (dmState.open.has(key)) dmState.open.delete(key);
|
|
5089
|
-
else dmState.open.add(key);
|
|
5090
|
-
renderDmRail();
|
|
5091
|
-
const again = els.dmRailList.querySelector('.dmt.open .dmt-msgs');
|
|
5092
|
-
if (again) again.scrollTop = again.scrollHeight;
|
|
5093
|
-
});
|
|
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
|
|
5094
5099
|
list.appendChild(el);
|
|
5095
5100
|
}
|
|
5096
5101
|
}
|
|
@@ -5101,6 +5106,7 @@ async function refreshDmRail() {
|
|
|
5101
5106
|
if (Array.isArray(dms)) dmState.dms = dms;
|
|
5102
5107
|
} catch {}
|
|
5103
5108
|
renderDmRail();
|
|
5109
|
+
if (els.dmOverlay && !els.dmOverlay.hidden) renderDmModal();
|
|
5104
5110
|
}
|
|
5105
5111
|
|
|
5106
5112
|
function toggleDmRail(hide) {
|
|
@@ -5111,18 +5117,97 @@ function toggleDmRail(hide) {
|
|
|
5111
5117
|
if (!hide) refreshDmRail();
|
|
5112
5118
|
}
|
|
5113
5119
|
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5120
|
+
/* --- modal: SOHBET (yalnız burada tam döküm) --- */
|
|
5121
|
+
|
|
5122
|
+
function renderDmModal() {
|
|
5123
|
+
const list = els.dmDialogList;
|
|
5124
|
+
if (!list) return;
|
|
5125
|
+
if (!dmState.dms.length) {
|
|
5126
|
+
list.innerHTML = '<div class="dm-empty">Henüz ajan DM\u2019i yok.<br>Ajanlar agent_dm aracıyla birbirine mesaj attıkça oturumlar birikir.</div>';
|
|
5127
|
+
if (els.dmDialogTitle) els.dmDialogTitle.textContent = 'AJAN DM — OTURUMLAR';
|
|
5128
|
+
if (els.dmBackBtn) els.dmBackBtn.hidden = true;
|
|
5129
|
+
return;
|
|
5130
|
+
}
|
|
5131
|
+
if (dmState.modalThread) {
|
|
5132
|
+
/* TEK OTURUM: tam sohbet dökümü */
|
|
5133
|
+
const msgs = (dmThreads().find(([k]) => k === dmState.modalThread) || [null, []])[1];
|
|
5134
|
+
if (!msgs.length) {
|
|
5135
|
+
dmState.modalThread = null;
|
|
5136
|
+
return renderDmModal();
|
|
5137
|
+
}
|
|
5138
|
+
if (els.dmDialogTitle) els.dmDialogTitle.textContent = dmThreadTitle(msgs[msgs.length - 1]);
|
|
5139
|
+
if (els.dmBackBtn) els.dmBackBtn.hidden = false;
|
|
5140
|
+
list.innerHTML = '<div class="dm-thread-msgs">' + msgs.map(dmMsgHtml).join('') + '</div>';
|
|
5141
|
+
const box = list.querySelector('.dm-thread-msgs');
|
|
5142
|
+
if (box) box.scrollTop = box.scrollHeight;
|
|
5143
|
+
return;
|
|
5144
|
+
}
|
|
5145
|
+
/* OTURUM LİSTESİ */
|
|
5146
|
+
if (els.dmDialogTitle) els.dmDialogTitle.textContent = 'AJAN DM — OTURUMLAR';
|
|
5147
|
+
if (els.dmBackBtn) els.dmBackBtn.hidden = true;
|
|
5148
|
+
list.innerHTML = '';
|
|
5149
|
+
for (const [key, msgs] of dmThreads()) {
|
|
5150
|
+
const last = msgs[msgs.length - 1];
|
|
5151
|
+
const el = document.createElement('div');
|
|
5152
|
+
el.className = 'dmt';
|
|
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
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
|
|
5167
|
+
function openDmModal(threadKey) {
|
|
5168
|
+
if (!els.dmOverlay) return;
|
|
5169
|
+
dmState.modalThread = threadKey || null;
|
|
5170
|
+
els.dmOverlay.hidden = false;
|
|
5171
|
+
refreshDmRail().then(() => renderDmModal());
|
|
5172
|
+
}
|
|
5173
|
+
|
|
5174
|
+
function closeDmModal() {
|
|
5175
|
+
if (els.dmOverlay) els.dmOverlay.hidden = true;
|
|
5176
|
+
if (els.dmBtn) els.dmBtn.classList.remove('on');
|
|
5177
|
+
}
|
|
5178
|
+
|
|
5179
|
+
async function dmClearAll() {
|
|
5180
|
+
if (!(await uiConfirm('TÜM ajan DM geçmişi silinsin mi?', 'Sil'))) return;
|
|
5181
|
+
try { await beast.agentDmsClear(); } catch {}
|
|
5182
|
+
dmState.dms = [];
|
|
5183
|
+
dmState.modalThread = null;
|
|
5184
|
+
renderDmRail();
|
|
5185
|
+
renderDmModal();
|
|
5186
|
+
toast('Ajan DM geçmişi silindi');
|
|
5187
|
+
}
|
|
5188
|
+
|
|
5189
|
+
if (els.dmBtn) els.dmBtn.addEventListener('click', () => toggleDmRail($('#dmRail').classList.contains('dm-hidden')));
|
|
5190
|
+
if (els.dmModalClose) els.dmModalClose.addEventListener('click', closeDmModal);
|
|
5191
|
+
if (els.dmModalClear) els.dmModalClear.addEventListener('click', dmClearAll);
|
|
5192
|
+
if (els.dmBackBtn) {
|
|
5193
|
+
els.dmBackBtn.addEventListener('click', () => {
|
|
5194
|
+
dmState.modalThread = null; // oturum listesine dön
|
|
5195
|
+
renderDmModal();
|
|
5196
|
+
});
|
|
5197
|
+
}
|
|
5198
|
+
if (els.dmOverlay) {
|
|
5199
|
+
els.dmOverlay.addEventListener('click', (e) => {
|
|
5200
|
+
if (e.target === els.dmOverlay) closeDmModal();
|
|
5201
|
+
});
|
|
5202
|
+
els.dmOverlay.addEventListener('keydown', (e) => {
|
|
5203
|
+
if (e.key === 'Escape') { e.stopPropagation(); closeDmModal(); }
|
|
5117
5204
|
});
|
|
5118
5205
|
}
|
|
5119
|
-
if (els.dmClear)
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
renderDmRail();
|
|
5125
|
-
toast('Ajan DM geçmişi silindi');
|
|
5206
|
+
if (els.dmClear) els.dmClear.addEventListener('click', dmClearAll);
|
|
5207
|
+
if (els.dmColumnBtn) {
|
|
5208
|
+
els.dmColumnBtn.addEventListener('click', () => {
|
|
5209
|
+
closeDmModal();
|
|
5210
|
+
toggleDmRail(false); // sütunda canlı akış
|
|
5126
5211
|
});
|
|
5127
5212
|
}
|
|
5128
5213
|
|
|
@@ -5456,7 +5541,7 @@ function onEvent(ev) {
|
|
|
5456
5541
|
scheduleAgentsRender();
|
|
5457
5542
|
return;
|
|
5458
5543
|
}
|
|
5459
|
-
/* AJAN DM: ajanlar arası mesaj —
|
|
5544
|
+
/* AJAN DM: ajanlar arası mesaj — açık görünümde canlı düşer (modal/sütun) */
|
|
5460
5545
|
if (ev.type === 'agent-dm') {
|
|
5461
5546
|
dmState.dms.push({
|
|
5462
5547
|
at: ev.at,
|
|
@@ -5464,9 +5549,11 @@ function onEvent(ev) {
|
|
|
5464
5549
|
fromTitle: ev.fromTitle,
|
|
5465
5550
|
to: ev.to,
|
|
5466
5551
|
toTitle: ev.toTitle,
|
|
5552
|
+
topic: ev.topic,
|
|
5467
5553
|
text: ev.text,
|
|
5468
5554
|
});
|
|
5469
5555
|
if (dmState.dms.length > 400) dmState.dms.splice(0, dmState.dms.length - 400);
|
|
5556
|
+
if (els.dmOverlay && !els.dmOverlay.hidden) renderDmModal();
|
|
5470
5557
|
if ($('#dmRail') && !$('#dmRail').classList.contains('dm-hidden')) renderDmRail();
|
|
5471
5558
|
return;
|
|
5472
5559
|
}
|
|
@@ -5484,6 +5571,11 @@ function onEvent(ev) {
|
|
|
5484
5571
|
scheduleToolRender();
|
|
5485
5572
|
return;
|
|
5486
5573
|
}
|
|
5574
|
+
/* TOOLS klasörü değişti (ajan yeni tool yazdı / elle edit) — konsolu tazele */
|
|
5575
|
+
if (ev.type === 'tools-changed') {
|
|
5576
|
+
scheduleToolRender();
|
|
5577
|
+
return;
|
|
5578
|
+
}
|
|
5487
5579
|
/* OFFLINE MESAJ KUYRUĞU: bağlantı + kuyruk olayları (sessionId filtresinden önce) */
|
|
5488
5580
|
if (ev.type === 'net' || ev.type === 'netQueue') { onNetEvent(ev); return; }
|
|
5489
5581
|
/* Sandbox klonlama progress'i (sessionId'siz global olay) */
|
package/src/renderer/style.css
CHANGED
|
@@ -110,15 +110,16 @@ body {
|
|
|
110
110
|
}
|
|
111
111
|
#newChat:hover { background: rgba(19, 19, 21, 0.14); }
|
|
112
112
|
|
|
113
|
-
#sessList { flex: 1; overflow-y: auto; padding: 4px 8px; }
|
|
113
|
+
#sessList { flex: 1 1 0; min-height: 0; overflow-y: auto; padding: 4px 8px; }
|
|
114
114
|
|
|
115
115
|
/* ---------- İZLEYİCİLER paneli (sol kenar çubuğu — sohbet geçmişinin alt yarısı) ----------
|
|
116
|
-
Yalnız agent (chat) modunda görünür; IDE/Studio/Sandbox'ta sessList ile birlikte gizli.
|
|
116
|
+
Yalnız agent (chat) modunda görünür; IDE/Studio/Sandbox'ta sessList ile birlikte gizli.
|
|
117
|
+
Sohbet geçmişi + izleyiciler + toollar EŞİT yer paylaşır (flex 1 1 0). */
|
|
117
118
|
#watchPane {
|
|
118
119
|
display: none;
|
|
119
120
|
flex-direction: column;
|
|
120
|
-
|
|
121
|
-
min-height:
|
|
121
|
+
flex: 1 1 0;
|
|
122
|
+
min-height: 110px;
|
|
122
123
|
border-top: 1px solid var(--border);
|
|
123
124
|
background: var(--panel);
|
|
124
125
|
}
|
|
@@ -2187,6 +2188,74 @@ body.rail-hidden #dmRailHead { padding-right: 148px; }
|
|
|
2187
2188
|
margin-top: 2px;
|
|
2188
2189
|
}
|
|
2189
2190
|
|
|
2191
|
+
/* ---------- AJAN DM modalı (✉) ---------- */
|
|
2192
|
+
#dmOverlay {
|
|
2193
|
+
position: fixed;
|
|
2194
|
+
inset: 0;
|
|
2195
|
+
background: rgba(0, 0, 0, 0.45);
|
|
2196
|
+
z-index: 300;
|
|
2197
|
+
display: flex;
|
|
2198
|
+
align-items: center;
|
|
2199
|
+
justify-content: center;
|
|
2200
|
+
}
|
|
2201
|
+
#dmOverlay[hidden] { display: none; }
|
|
2202
|
+
#dmDialog {
|
|
2203
|
+
width: min(680px, 92vw);
|
|
2204
|
+
max-height: 86vh;
|
|
2205
|
+
display: flex;
|
|
2206
|
+
flex-direction: column;
|
|
2207
|
+
background: var(--panel);
|
|
2208
|
+
border: 1px solid var(--border);
|
|
2209
|
+
border-radius: 12px;
|
|
2210
|
+
padding: 14px 16px;
|
|
2211
|
+
gap: 10px;
|
|
2212
|
+
}
|
|
2213
|
+
#dmDialogHead {
|
|
2214
|
+
display: flex;
|
|
2215
|
+
align-items: center;
|
|
2216
|
+
justify-content: space-between;
|
|
2217
|
+
font-size: 13px;
|
|
2218
|
+
font-weight: 800;
|
|
2219
|
+
letter-spacing: 1px;
|
|
2220
|
+
color: var(--text);
|
|
2221
|
+
}
|
|
2222
|
+
#dmDialogHead button {
|
|
2223
|
+
border: none;
|
|
2224
|
+
background: none;
|
|
2225
|
+
color: var(--muted);
|
|
2226
|
+
cursor: pointer;
|
|
2227
|
+
font-size: 13px;
|
|
2228
|
+
padding: 2px 6px;
|
|
2229
|
+
border-radius: 6px;
|
|
2230
|
+
display: inline-flex;
|
|
2231
|
+
align-items: center;
|
|
2232
|
+
}
|
|
2233
|
+
#dmDialogHead button:hover { color: var(--accent); background: var(--panel2); }
|
|
2234
|
+
#dmDialogHead .bot-bar-btns { display: flex; gap: 4px; }
|
|
2235
|
+
#dmDialogList {
|
|
2236
|
+
flex: 1 1 auto;
|
|
2237
|
+
min-height: 200px;
|
|
2238
|
+
overflow-y: auto;
|
|
2239
|
+
display: flex;
|
|
2240
|
+
flex-direction: column;
|
|
2241
|
+
align-items: stretch;
|
|
2242
|
+
}
|
|
2243
|
+
/* tek oturum sohbeti: kaydırılabilir blok */
|
|
2244
|
+
.dm-thread-msgs {
|
|
2245
|
+
display: flex;
|
|
2246
|
+
flex-direction: column;
|
|
2247
|
+
gap: 5px;
|
|
2248
|
+
max-height: 56vh;
|
|
2249
|
+
overflow-y: auto;
|
|
2250
|
+
}
|
|
2251
|
+
.dm-hint {
|
|
2252
|
+
flex: none;
|
|
2253
|
+
font-size: 10px;
|
|
2254
|
+
color: var(--muted);
|
|
2255
|
+
text-align: center;
|
|
2256
|
+
letter-spacing: 0.4px;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2190
2259
|
/* PİYASA satırındaki ▶ ajan butonu */
|
|
2191
2260
|
.fin-sym { position: relative; }
|
|
2192
2261
|
.fin-sym .fs-agent {
|
|
@@ -2275,10 +2344,11 @@ body.rail-hidden #dmRailHead { padding-right: 148px; }
|
|
|
2275
2344
|
color: var(--muted);
|
|
2276
2345
|
}
|
|
2277
2346
|
|
|
2278
|
-
/* ---------- sol panel: TOOLS konsolu (kişisel araçlar) ----------
|
|
2347
|
+
/* ---------- sol panel: TOOLS konsolu (kişisel araçlar) ----------
|
|
2348
|
+
Sohbet geçmişi + izleyiciler + toollar EŞİT yer paylaşır (flex 1 1 0). */
|
|
2279
2349
|
#toolBox {
|
|
2280
|
-
flex:
|
|
2281
|
-
|
|
2350
|
+
flex: 1 1 0;
|
|
2351
|
+
min-height: 90px;
|
|
2282
2352
|
display: none;
|
|
2283
2353
|
flex-direction: column;
|
|
2284
2354
|
border-top: 1px solid var(--border);
|
|
@@ -2296,8 +2366,8 @@ body:not(.ide-mode):not(.studio-mode):not(.sandbox-mode) #toolBox { display: fle
|
|
|
2296
2366
|
color: var(--muted);
|
|
2297
2367
|
}
|
|
2298
2368
|
#toolList {
|
|
2369
|
+
flex: 1 1 auto;
|
|
2299
2370
|
min-height: 40px;
|
|
2300
|
-
max-height: 200px;
|
|
2301
2371
|
overflow-y: auto;
|
|
2302
2372
|
display: flex;
|
|
2303
2373
|
flex-direction: column;
|