beast-agent 2.63.5 → 2.63.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.63.5",
4
+ "version": "2.63.6",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
package/src/main.js CHANGED
@@ -10690,6 +10690,12 @@ function finWakeAgentDm(sid, text) {
10690
10690
  finTradeHoursBlockedLog('olay uyandırması');
10691
10691
  return false;
10692
10692
  }
10693
+ /* TYPESAFE-ONLY: LLM turu BAŞLATILMAZ — olay TS turunu öne çeker; prompt/
10694
+ pendingReports hattı (engine.send) bu modda hiç kullanılmaz */
10695
+ if (finTsAgentMode(agent)) {
10696
+ if (!agent.tsBusy) finTypeSafeKick(id, agent);
10697
+ return true;
10698
+ }
10693
10699
  try {
10694
10700
  const body = agent.role
10695
10701
  ? String(text || '') + "\n(ROL HATIRLATMASI: işlem AÇMA — durumu analiz et; gerekiyorsa agent_dm ile ANA TRADER'a bildir.)"
@@ -12863,6 +12869,441 @@ function finTeamWaitTick(sid) {
12863
12869
  finAgentRound(sid, { noTeamWait: true });
12864
12870
  }
12865
12871
 
12872
+ /* ---------- TYPESAFE-ONLY FİNANS MODU (LLM'SİZ) ----------
12873
+ Rol→Skill eşleştirmesinde "typesafe-ai" seçiliyse ajan LLM HİÇ kullanmaz:
12874
+ tur verisi (fiyat/gösterge/pozisyon/öğrenme geçmişi/ekip yanıtları) YALNIZ
12875
+ TypeSafe System One'a gider; yanıtlar olasılık olarak döner, kararı KOD
12876
+ uygular. Yanıtlar AJAN DM ekip grubuna yazılır — tüm ajanlar birbirinin
12877
+ cevabını görür (son yanıtlar sonraki turların state'ine de girer) ve emirler
12878
+ YALNIZ market buy/sell olarak açılır (bekleyen emir YOK). */
12879
+ const FIN_TS_ACTION_P = 0.6; /* choice: işlem için min olasılık */
12880
+ const FIN_TS_MARGIN_P = 0.12; /* birinci ile ikinci seçenek arası min fark */
12881
+ const FIN_TS_CONFIRM_P = 0.55; /* noul teyit eşiği */
12882
+ const FIN_TS_EXIT_P = 0.62; /* pozisyon kapatma eşiği */
12883
+ const FIN_TS_MAX_SYMBOLS = 3; /* tur başına işlenecek sembol tavanı */
12884
+ const FIN_TS_ROLE_TF = { technic: 'M15', risk: 'H1', macro: 'H4', visual: 'M15' };
12885
+ const FIN_TS_ROLE_CRIT = {
12886
+ technic: {
12887
+ yukari: 'Boğa yapısı — HH/HL, yükseliş trendi, kırılım teyidi',
12888
+ asagi: 'Ayı yapısı — LH/LL, düşüş trendi, kırılım teyidi',
12889
+ yatay: 'Yatay/range — yön teyidi yok',
12890
+ },
12891
+ risk: {
12892
+ uygun: 'Risk limitleri uygun — pozisyon açılabilir',
12893
+ dikkat: 'Sınırda — küçük lot / ek teyit şart',
12894
+ uygundegil: 'Uygun değil — açma, mevcudu küçült',
12895
+ },
12896
+ macro: {
12897
+ pozitif: 'Makro akış pozitif (faiz/veri/jeopolitik destekliyor)',
12898
+ negatif: 'Makro akış negatif (risk iştahı bozuk)',
12899
+ notr: 'Makro etki nötr/belirsiz',
12900
+ },
12901
+ visual: {
12902
+ yukari: 'Grafik yapısı yukarı (formasyon/trend yukarı)',
12903
+ asagi: 'Grafik yapısı aşağı',
12904
+ yatay: 'Yatay/sıkışma — formasyon yok',
12905
+ },
12906
+ };
12907
+
12908
+ function finTsAgentMode(agent) {
12909
+ try {
12910
+ const f = finCfg();
12911
+ const key = agent && agent.main ? 'trader' : String((agent && agent.role) || '');
12912
+ if (!key) return false;
12913
+ const list = f.roleSkills && Array.isArray(f.roleSkills[key]) ? f.roleSkills[key] : [];
12914
+ return list.some((s) => String(s || '').toLowerCase() === 'typesafe-ai');
12915
+ } catch {
12916
+ return false;
12917
+ }
12918
+ }
12919
+
12920
+ function finTsWho(agent, extra) {
12921
+ const role = agent && agent.main ? 'TRADER' : ((finRoleDef(agent && agent.role) || {}).label || 'FİNANS').toUpperCase();
12922
+ return 'TypeSafe · ' + role + (extra ? ' · ' + extra : '');
12923
+ }
12924
+
12925
+ /* TypeSafe yanıtını AJAN DM ekip grubuna yaz (panelde görünür) + state için
12926
+ besleme tamponuna ekle (ajanlar birbirini sonraki turda görür) */
12927
+ function finTsPost(sid, agent, text, topic) {
12928
+ const body = String(text || '').trim();
12929
+ if (!body) return;
12930
+ try {
12931
+ if (engine && typeof engine.agentDmGroupPost === 'function') {
12932
+ engine.agentDmGroupPost({
12933
+ gid: FIN_TEAM_GID,
12934
+ title: FIN_TEAM_TITLE,
12935
+ fromSid: String(sid),
12936
+ fromTitle: finTsWho(agent),
12937
+ topic: String(topic || 'typesafe'),
12938
+ text: body,
12939
+ });
12940
+ }
12941
+ } catch {}
12942
+ try {
12943
+ financeState.tsFeed = Array.isArray(financeState.tsFeed) ? financeState.tsFeed : [];
12944
+ financeState.tsFeed.push({ at: Date.now(), sid: String(sid), who: finTsWho(agent, topic), text: body });
12945
+ while (financeState.tsFeed.length > 30) financeState.tsFeed.shift();
12946
+ } catch {}
12947
+ }
12948
+
12949
+ function finTsFeedText(limit) {
12950
+ const feed = Array.isArray(financeState.tsFeed) ? financeState.tsFeed.slice(-(Math.max(1, Number(limit) || 6))) : [];
12951
+ if (!feed.length) return '(ekip yanıtı yok)';
12952
+ return feed.map((x) => `- ${x.who}: ${String(x.text || '').replace(/\s+/g, ' ').slice(0, 220)}`).join('\n');
12953
+ }
12954
+
12955
+ /* ZAMAN DİLİMİ BOT KARARI: önce öğrenmede en iyi net getiren periyot (>=3
12956
+ işlem), sonra ajanın seçimi, sonra rol varsayılanı */
12957
+ function finTsPickTf(agent, symbol) {
12958
+ try {
12959
+ const sym = String(symbol || '').toUpperCase();
12960
+ const e = finLearnLoad().symbols[sym];
12961
+ const byTf = (e && e.stats && e.stats.byTf) || {};
12962
+ let best = '';
12963
+ let bestNet = -Infinity;
12964
+ for (const [tf, s] of Object.entries(byTf)) {
12965
+ if (!s || Number(s.trades) < 3) continue;
12966
+ const n = Number(s.net) || 0;
12967
+ if (n > bestNet) { bestNet = n; best = tf; }
12968
+ }
12969
+ if (best) return best;
12970
+ } catch {}
12971
+ const role = String((agent && agent.role) || '');
12972
+ return finLearnNormTf(agent && agent.tsTf) || FIN_TS_ROLE_TF[role] || 'M15';
12973
+ }
12974
+
12975
+ function finTsChoice(ans, id) {
12976
+ const a = ans && ans.answers && ans.answers[id];
12977
+ if (!a || a.type !== 'choice') return null;
12978
+ const probs = a.probabilities && typeof a.probabilities === 'object' ? a.probabilities : {};
12979
+ const top = String(a.choice || '');
12980
+ let p = Number(probs[top]);
12981
+ if (!isFinite(p)) { p = 0; for (const v of Object.values(probs)) p = Math.max(p, Number(v) || 0); }
12982
+ let second = 0;
12983
+ for (const [k, v] of Object.entries(probs)) {
12984
+ if (k === top) continue;
12985
+ second = Math.max(second, Number(v) || 0);
12986
+ }
12987
+ return { choice: top, p, second, conf: Number(a.confidence) || 0, probs };
12988
+ }
12989
+
12990
+ function finTsNoul(ans, id) {
12991
+ const a = ans && ans.answers && ans.answers[id];
12992
+ if (!a || a.type !== 'noul') return null;
12993
+ const n = Number(a.noul);
12994
+ return isFinite(n) ? n : null;
12995
+ }
12996
+
12997
+ function finTsScore(ans, id) {
12998
+ const a = ans && ans.answers && ans.answers[id];
12999
+ if (!a || a.type !== 'score') return null;
13000
+ return { score: Number(a.score) || 0, conf: Number(a.confidence) || 0, probs: a.probabilities || {} };
13001
+ }
13002
+
13003
+ function finTsFmtChoice(c) {
13004
+ if (!c) return 'yanıt yok';
13005
+ const probs = Object.entries(c.probs || {}).map(([k, v]) => `${k}=${(Number(v) || 0).toFixed(2)}`).join(' ');
13006
+ return `${c.choice} (p=${c.p.toFixed(2)}, güven=${c.conf.toFixed(2)})` + (probs ? ' [' + probs + ']' : '');
13007
+ }
13008
+
13009
+ function finTsIndLine(ind) {
13010
+ if (!ind || ind.ok !== true) return null;
13011
+ const g = (k) => {
13012
+ const row = ind.indicators && ind.indicators[k];
13013
+ return row && Array.isArray(row.last) ? Number(row.last[row.last.length - 1]) : null;
13014
+ };
13015
+ return {
13016
+ atr14: g('ATR(14)'),
13017
+ ema50: g('EMA(50)'),
13018
+ ema200: g('EMA(200)'),
13019
+ rsi14: g('RSI(14)'),
13020
+ close: Number(ind.close) || null,
13021
+ range20: ind.range20 || null,
13022
+ };
13023
+ }
13024
+
13025
+ function finTsSchedule(sid, agent, sec) {
13026
+ if (!agent || !financeState.agents.has(String(sid))) return;
13027
+ clearTimeout(agent.timer);
13028
+ const s = Math.max(15, Math.min(3600, Math.round(Number(sec) || finPaceSec())));
13029
+ agent.timer = setTimeout(() => { try { finAgentRound(String(sid)); } catch {} }, s * 1000);
13030
+ }
13031
+
13032
+ function finTypeSafeKick(sid, agent) {
13033
+ if (!agent) return;
13034
+ clearTimeout(agent.timer);
13035
+ agent.timer = setTimeout(() => { try { finAgentRound(String(sid)); } catch {} }, 800);
13036
+ }
13037
+
13038
+ /* LLM'siz TypeSafe turu: veri → TypeSafe (noul/choice/score) → kod kararı →
13039
+ AJAN DM yayını. Hata durumunda tur atlanır, LLM'e ASLA düşülmez. */
13040
+ async function finTypeSafeRound(sid, agent) {
13041
+ if (!agent || !engine || agent.tsBusy) return;
13042
+ agent.tsBusy = true;
13043
+ const sidS = String(sid);
13044
+ const isTrader = !!agent.main;
13045
+ const role = String(agent.role || '');
13046
+ const roleLabel = isTrader ? 'TRADER (ana karar verici)' : ((finRoleDef(role) || {}).label || role || 'finans');
13047
+ try {
13048
+ if (!typesafeMod.cfg().apiKey) {
13049
+ const now = Date.now();
13050
+ if (!agent.tsNoKeyAt || now - agent.tsNoKeyAt > 10 * 60 * 1000) {
13051
+ agent.tsNoKeyAt = now;
13052
+ finTsPost(sidS, agent, '⚠ TypeSafe API anahtarı yok — bu rol typesafe-ai modunda ve LLM KULLANILMIYOR; tur atlanıyor. Sahibe söyle: Ayarlar → TypeSafe sekmesinden anahtar girilmeli.', 'anahtar');
13053
+ }
13054
+ return;
13055
+ }
13056
+ if (!mt5bridge.running) {
13057
+ const now = Date.now();
13058
+ if (!agent.tsNoBridgeAt || now - agent.tsNoBridgeAt > 5 * 60 * 1000) {
13059
+ agent.tsNoBridgeAt = now;
13060
+ finTsPost(sidS, agent, '⚠ MT5 köprüsü bağlı değil — TypeSafe turu atlandı.', 'köprü');
13061
+ }
13062
+ return;
13063
+ }
13064
+ const f = finCfg();
13065
+ const symbols = (Array.isArray(agent.symbols) && agent.symbols.length ? agent.symbols : f.symbols || [])
13066
+ .map((x) => String(x || '').trim().toUpperCase()).filter(Boolean).slice(0, FIN_TS_MAX_SYMBOLS);
13067
+ if (!symbols.length) {
13068
+ const now = Date.now();
13069
+ if (!agent.tsNoSymAt || now - agent.tsNoSymAt > 10 * 60 * 1000) {
13070
+ agent.tsNoSymAt = now;
13071
+ finTsPost(sidS, agent, '⚠ İzleme listesi boş — TypeSafe kararı üretilemiyor. Panele sembol ekle.', 'sembol');
13072
+ }
13073
+ return;
13074
+ }
13075
+ agent.round = (Number(agent.round) || 0) + 1;
13076
+ if (isTrader) {
13077
+ financeState.traderRounds = agent.round;
13078
+ finPush('trader', { state: 'running', round: agent.round, mode: 'typesafe' });
13079
+ }
13080
+ /* VERİ: hesap + pozisyonlar + sembol fiyat/gösterge — hepsi KOD ile toplanır,
13081
+ yalnız TypeSafe'e gönderilir; hiçbir LLM çağrısı yapılmaz */
13082
+ const [accR, posR] = await Promise.all([
13083
+ financetools.handlers.mt5_account({}),
13084
+ financetools.handlers.mt5_positions({}),
13085
+ ]);
13086
+ const account = (accR && accR.account) || {};
13087
+ const posList = (posR && posR.positions) || [];
13088
+ const market = {};
13089
+ for (const sym of symbols) {
13090
+ const tf = finTsPickTf(agent, sym);
13091
+ const [mktR, indR] = await Promise.all([
13092
+ financetools.handlers.mt5_market({ symbols: [sym] }),
13093
+ financetools.handlers.mt5_indicators({ symbol: sym, timeframe: tf, count: 300, indicators: ['ATR(14)', 'EMA(50)', 'EMA(200)', 'RSI(14)'] }),
13094
+ ]);
13095
+ market[sym] = {
13096
+ row: (mktR && mktR.symbols && mktR.symbols[0]) || null,
13097
+ tf,
13098
+ ind: finTsIndLine(indR),
13099
+ };
13100
+ }
13101
+ const state = {
13102
+ rol: roleLabel,
13103
+ yerel_saat: new Date().getHours(),
13104
+ hesap: {
13105
+ balance: Number(account.balance) || 0,
13106
+ equity: Number(account.equity) || 0,
13107
+ margin_free: Number(account.margin_free) || 0,
13108
+ acik_pozisyon: posList.length,
13109
+ },
13110
+ limitler: { max_pozisyon: Number(f.maxPositions) || 3, min_lot: Number(f.minLot) || 0.01, max_lot: Number(f.maxLot) || 0.1 },
13111
+ pozisyonlar: posList.map((p) => ({
13112
+ ticket: p.ticket, sembol: p.symbol, yon: Number(p.type) === 0 ? 'buy' : 'sell',
13113
+ hacim: p.volume, giris: p.price_open, sl: p.sl, tp: p.tp, kar: p.profit,
13114
+ })),
13115
+ piyasa: {},
13116
+ ogrenme: finBuildLearnDigest(symbols),
13117
+ ekip_yanitlari: finTsFeedText(6),
13118
+ };
13119
+ for (const sym of symbols) {
13120
+ const d = market[sym];
13121
+ const row = d.row || {};
13122
+ state.piyasa[sym] = {
13123
+ zaman_dilimi: d.tf,
13124
+ bid: Number(row.bid) || null,
13125
+ ask: Number(row.ask) || null,
13126
+ spread: Number(row.spread) || null,
13127
+ gosterge: d.ind,
13128
+ };
13129
+ }
13130
+
13131
+ /* ---------------- TRADER: yön + teyit + kapat kararı ---------------- */
13132
+ if (isTrader) {
13133
+ const posBySym = {};
13134
+ for (const p of posList) {
13135
+ const s = String(p.symbol || '').toUpperCase();
13136
+ if (symbols.includes(s)) posBySym[s] = p;
13137
+ }
13138
+ const questions = {};
13139
+ symbols.forEach((sym, i) => {
13140
+ questions['yon_' + i] = {
13141
+ type: 'choice',
13142
+ instructions: `${sym} ${market[sym].tf} grafiğinde verilen fiyat, gösterge, pozisyon, öğrenme geçmişi ve ekip yanıtlarına göre kısa vadeli doğru aksiyon nedir? Emin değilsen bekle.`,
13143
+ criteria: {
13144
+ buy: 'LONG aç — yükseliş teyidi var (yapı/momentum/EMA eğimi destekliyor)',
13145
+ sell: 'SHORT aç — düşüş teyidi var',
13146
+ bekle: 'İşlem açma — teyit yok, belirsiz ya da riskli',
13147
+ },
13148
+ };
13149
+ questions['teyit_' + i] = {
13150
+ type: 'noul',
13151
+ instructions: `${sym} fiyat yapısı ve momentum, 'yon_' + ${i} kararını gerçekten destekliyor mu?`,
13152
+ criteria: { true: 'Net destekliyor', false: 'Zayıf/çelişkili' },
13153
+ };
13154
+ if (posBySym[sym]) {
13155
+ const pp = posBySym[sym];
13156
+ questions['kapat_' + i] = {
13157
+ type: 'noul',
13158
+ instructions: `${sym} açık pozisyon (${Number(pp.type) === 0 ? 'buy' : 'sell'}, kâr ${Number(pp.profit) || 0}) ŞİMDİ kapatılmalı mı? Kârı koruma, momentum dönüşü veya SL/TP yakınlığını değerlendir.`,
13159
+ criteria: { true: 'Kapat — risk/kâr koruma', false: 'Açık kalsın — tez sürüyor' },
13160
+ };
13161
+ }
13162
+ });
13163
+ let ans = null;
13164
+ try {
13165
+ ans = await finTsAsk(state, questions);
13166
+ } catch (e) {
13167
+ const now = Date.now();
13168
+ if (!agent.tsErrAt || now - agent.tsErrAt > 2 * 60 * 1000) {
13169
+ agent.tsErrAt = now;
13170
+ finTsPost(sidS, agent, '⚠ TypeSafe çağrısı başarısız: ' + String((e && e.message) || e).slice(0, 200), 'hata');
13171
+ }
13172
+ return;
13173
+ }
13174
+ const lines = [];
13175
+ let opened = 0;
13176
+ for (let i = 0; i < symbols.length; i++) {
13177
+ const sym = symbols[i];
13178
+ if (!financeState.agents.has(sidS)) return;
13179
+ const c = finTsChoice(ans, 'yon_' + i);
13180
+ const conf = finTsNoul(ans, 'teyit_' + i);
13181
+ const pos = posBySym[sym];
13182
+ if (pos) {
13183
+ const ex = finTsNoul(ans, 'kapat_' + i);
13184
+ if (ex != null && ex >= FIN_TS_EXIT_P) {
13185
+ const r = await financetools.handlers.mt5_close(
13186
+ { ticket: Number(pos.ticket), reason: `TypeSafe: kapat p=${ex.toFixed(2)}` },
13187
+ { sessionId: sidS }
13188
+ );
13189
+ lines.push(`- ${sym}: KAPAT (p=${ex.toFixed(2)}) ${r && r.ok ? '✓ pozisyon kapatıldı' : '✗ ' + ((r && r.error) || 'hata')}`);
13190
+ } else {
13191
+ lines.push(`- ${sym}: pozisyon açık (kapat=${ex == null ? 'yanıt yok' : ex.toFixed(2)} < ${FIN_TS_EXIT_P}) — tez sürüyor`);
13192
+ }
13193
+ continue;
13194
+ }
13195
+ if (!c || (c.choice !== 'buy' && c.choice !== 'sell')) {
13196
+ lines.push(`- ${sym}: ${finTsFmtChoice(c)} → BEKLE`);
13197
+ continue;
13198
+ }
13199
+ if (!(c.p >= FIN_TS_ACTION_P) || c.p - c.second < FIN_TS_MARGIN_P) {
13200
+ lines.push(`- ${sym}: ${finTsFmtChoice(c)} → eşik/fark altı, BEKLE`);
13201
+ continue;
13202
+ }
13203
+ if (conf == null || conf < FIN_TS_CONFIRM_P) {
13204
+ lines.push(`- ${sym}: ${finTsFmtChoice(c)} · teyit=${conf == null ? 'yok' : conf.toFixed(2)} < ${FIN_TS_CONFIRM_P} → BEKLE`);
13205
+ continue;
13206
+ }
13207
+ if (opened >= 1 || posList.length >= (Number(f.maxPositions) || 3)) {
13208
+ lines.push(`- ${sym}: sinyal güçlü (${c.choice} p=${c.p.toFixed(2)}) ama pozisyon sınırı dolu — bu tur açılmadı`);
13209
+ continue;
13210
+ }
13211
+ /* SL/TP KOD ile: ATR(14) tabanlı — tip ve seviyeler deterministik */
13212
+ const row = market[sym].row || {};
13213
+ const price = c.choice === 'buy' ? Number(row.ask) || Number(row.bid) : Number(row.bid) || Number(row.ask);
13214
+ if (!(price > 0)) { lines.push(`- ${sym}: fiyat okunamadı`); continue; }
13215
+ const atr = Number(market[sym].ind && market[sym].ind.atr14) || 0;
13216
+ const slDist = Math.max(atr > 0 ? atr * 1.5 : price * 0.003, price * 0.0004);
13217
+ const tpDist = Math.max(atr > 0 ? atr * 2.5 : price * 0.006, slDist * 1.2);
13218
+ const digits = Math.max(0, Math.min(8, Number(row.digits) || 2));
13219
+ const rnd = (v) => Number(Number(v).toFixed(digits));
13220
+ const sl = rnd(c.choice === 'buy' ? price - slDist : price + slDist);
13221
+ const tp = rnd(c.choice === 'buy' ? price + tpDist : price - tpDist);
13222
+ const riskPct = Number(f.riskPerTradePct) > 0 ? Number(f.riskPerTradePct) : 0.5;
13223
+ const r = await financetools.handlers.mt5_trade(
13224
+ {
13225
+ symbol: sym,
13226
+ side: c.choice,
13227
+ type: 'market',
13228
+ sl,
13229
+ tp,
13230
+ riskPct,
13231
+ timeframe: market[sym].tf,
13232
+ comment: 'TS',
13233
+ reason: `TypeSafe: yön ${c.choice} p=${c.p.toFixed(2)} fark=${(c.p - c.second).toFixed(2)} teyit=${conf.toFixed(2)}`,
13234
+ },
13235
+ { sessionId: sidS }
13236
+ );
13237
+ if (r && r.ok) {
13238
+ opened += 1;
13239
+ lines.push(`- ${sym}: ⚡ MARKET ${c.choice.toUpperCase()} açıldı (lot ${r.opened && r.opened.volume}, SL ${sl}, TP ${tp}, risk %${riskPct}) · p=${c.p.toFixed(2)} teyit=${conf.toFixed(2)} · tf=${market[sym].tf}`);
13240
+ } else {
13241
+ lines.push(`- ${sym}: ${c.choice.toUpperCase()} sinyali reddedildi — ${(r && r.error) || 'hata'}`);
13242
+ }
13243
+ }
13244
+ finTsPost(
13245
+ sidS,
13246
+ agent,
13247
+ `🤖 TypeSafe karar turu #${agent.round} (LLM yok — girdi yalnız TypeSafe):\n` +
13248
+ 'TF KARARI: ' + symbols.map((s) => `${s}=${market[s].tf}`).join(', ') + '\n' +
13249
+ lines.join('\n') +
13250
+ (opened ? '' : '\nYeni pozisyon açılmadı (eşikler/limitler).'),
13251
+ symbols.join(',')
13252
+ );
13253
+ return;
13254
+ }
13255
+
13256
+ /* ---------------- ROL AJANI: analiz yanıtları (işlem açmaz) ---------------- */
13257
+ const questions = {};
13258
+ const crit = FIN_TS_ROLE_CRIT[role] || FIN_TS_ROLE_CRIT.technic;
13259
+ symbols.forEach((sym, i) => {
13260
+ questions['bias_' + i] = {
13261
+ type: 'choice',
13262
+ instructions: `${sym} ${market[sym].tf} için ${roleLabel} rolünde yön/eğilim değerlendirmen nedir? Verilen fiyat, gösterge, öğrenme geçmişi ve ekip yanıtlarını kullan.`,
13263
+ criteria: crit,
13264
+ };
13265
+ questions['guc_' + i] = {
13266
+ type: 'score',
13267
+ instructions: `${sym} için bu değerlendirmenin gücü nedir? İstatistik ve gösterge teyidi zayıfsa düşük puan ver.`,
13268
+ criteria: ['çok zayıf', 'zayıf', 'orta', 'güçlü', 'çok güçlü'],
13269
+ };
13270
+ questions['risk_' + i] = {
13271
+ type: 'noul',
13272
+ instructions: `${sym} için ${roleLabel} rolünde işlemi engelleyecek bir risk/uygunsuzluk var mı (volatilite, haber, limit, yapı belirsizliği)?`,
13273
+ criteria: { true: 'Risk var — dikkat/engel', false: 'Risk normal' },
13274
+ };
13275
+ });
13276
+ let ans = null;
13277
+ try {
13278
+ ans = await finTsAsk(state, questions);
13279
+ } catch (e) {
13280
+ const now = Date.now();
13281
+ if (!agent.tsErrAt || now - agent.tsErrAt > 2 * 60 * 1000) {
13282
+ agent.tsErrAt = now;
13283
+ finTsPost(sidS, agent, '⚠ TypeSafe çağrısı başarısız: ' + String((e && e.message) || e).slice(0, 200), 'hata');
13284
+ }
13285
+ return;
13286
+ }
13287
+ const lines = symbols.map((sym, i) => {
13288
+ const b = finTsChoice(ans, 'bias_' + i);
13289
+ const g = finTsScore(ans, 'guc_' + i);
13290
+ const rk = finTsNoul(ans, 'risk_' + i);
13291
+ return (
13292
+ `- ${sym} (tf=${market[sym].tf}): ${finTsFmtChoice(b)}` +
13293
+ (g ? ` · güç=${g.score.toFixed(2)}/4 (güven=${g.conf.toFixed(2)})` : '') +
13294
+ (rk == null ? '' : ` · risk=${rk.toFixed(2)}${rk >= 0.55 ? ' ⚠' : ''}`)
13295
+ );
13296
+ });
13297
+ finTsPost(sidS, agent, `🧠 TypeSafe analiz (LLM yok — girdi yalnız TypeSafe):\n` + lines.join('\n'), symbols.join(','));
13298
+ } catch (e) {
13299
+ try { financeLog('[typesafe] tur hatası (' + sidS + '): ' + String((e && e.message) || e)); } catch {}
13300
+ } finally {
13301
+ agent.tsBusy = false;
13302
+ finTsSchedule(sidS, agent, finPaceSec());
13303
+ if (isTrader && financeState.agents.has(sidS)) finPush('trader', { state: 'idle', round: agent.round, mode: 'typesafe' });
13304
+ }
13305
+ }
13306
+
12866
13307
  /* Bir ajanın TEK turu: (opsiyonel) ana ajan planı + tur emri */
12867
13308
  function finAgentRound(sid, opts) {
12868
13309
  const agent = financeState.agents.get(String(sid));
@@ -12880,6 +13321,13 @@ function finAgentRound(sid, opts) {
12880
13321
  /* hâlâ çalışıyor — done eventinde tekrar planlanır */
12881
13322
  return;
12882
13323
  }
13324
+ /* TYPESAFE-ONLY MODU: roleSkills'te typesafe-ai varsa LLM HİÇ kullanılmaz —
13325
+ tur deterministik TypeSafe hattıyla koşar (girdi yalnız TypeSafe'e gider,
13326
+ cevaplar AJAN DM'e düşer, emirler yalnız market buy/sell) */
13327
+ if (finTsAgentMode(agent)) {
13328
+ finTypeSafeRound(String(sid), agent).catch(() => {});
13329
+ return;
13330
+ }
12883
13331
  const f = finCfg();
12884
13332
  /* EKİP BEKLEMESİ SÜRÜYOR: DM uyandırması vb. turu erkene çekemez — tick açar */
12885
13333
  if (agent.main && agent.waitingTeam && !(opts && opts.noTeamWait)) return;
@@ -13399,6 +13847,15 @@ async function financeTraderStart() {
13399
13847
  try { engine.setSessionModel(mainSid, f.traderSel || null); } catch {}
13400
13848
  financeState.traderOn = true;
13401
13849
  try { engine.clearStop(); } catch {}
13850
+ /* TYPESAFE-ONLY: roleSkills.trader'da typesafe-ai varsa LLM'e HİÇ gidilmez */
13851
+ if (finTsAgentMode(mainAgent)) {
13852
+ financeState.lastRoundAt = Date.now();
13853
+ financeLog('[trader] başlatıldı (TYPESAFE-ONLY — LLM kullanılmıyor, girdi yalnız TypeSafe)');
13854
+ finPush('trader', { state: 'running', round: mainAgent.round, mode: 'typesafe' });
13855
+ finTypeSafeKick(mainSid, mainAgent);
13856
+ finTeamStart(f);
13857
+ return { ok: true, sid: mainSid };
13858
+ }
13402
13859
  const ok = engine.send(mainSid, finTraderBrief(mainAgent), { userAction: true });
13403
13860
  if (!ok) {
13404
13861
  financeState.traderOn = false;
@@ -13431,6 +13888,12 @@ function finTeamStart(f) {
13431
13888
  try {
13432
13889
  const created = finAgentCreate([], false, roleId);
13433
13890
  try { engine.setSessionModel(created.s.id, f.traderSel || null); } catch {}
13891
+ /* TYPESAFE-ONLY: bu rolün skill eşleştirmesinde typesafe-ai varsa LLM yok */
13892
+ if (finTsAgentMode(created.agent)) {
13893
+ financeLog('[ekip] ' + roleDef.label + ' başladı (TYPESAFE-ONLY — LLM kullanılmıyor)');
13894
+ finTypeSafeKick(String(created.s.id), created.agent);
13895
+ continue;
13896
+ }
13434
13897
  const ok = engine.send(created.s.id, finTraderBrief(created.agent), { userAction: true });
13435
13898
  if (!ok) {
13436
13899
  finAgentStop(String(created.s.id), 'oturum meşgul — ekip ajanı başlatılamadı');
@@ -856,7 +856,7 @@
856
856
  </div>
857
857
  <div class="mini-body">
858
858
  <div id="finSkillsList" class="mini-list"></div>
859
- <div class="fin-sym-hint">Her rol için TEK skill seçilir — aynı role birden fazla skill seçilemez. Seçili skill'i satırın sağ üstündeki çöp ikonuyla kaldırabilirsin; boş bırakırsan ajan skill'i göreve göre SKILLS kataloğundan kendi seçer. Seçim, koşan ajanlar dahil sonraki turdan itibaren sistem promptuna girer.</div>
859
+ <div class="fin-sym-hint">Her rol için EN FAZLA 2 skill seçilir (ör. price-action + typesafe-ai). Seçili skill'i satırın sağ üstündeki çöp ikonuyla kaldırabilirsin; boş bırakırsan ajan skill'i göreve göre SKILLS kataloğundan kendi seçer. Seçim, koşan ajanlar dahil sonraki turdan itibaren geçerli olur.<br><b>typesafe-ai seçilirse:</b> o ajan LLM KULLANMAZ — tur verisi yalnız TypeSafe System One'a gider (tipli kararlar), cevaplar AJAN DM'de tüm ekibe görünür ve ajanlar birbirinin yanıtını görür; emirler YALNIZ market buy/sell olarak açılır (bekleyen emir yok). Ayarlar → TypeSafe'ten API anahtarı gerekir.</div>
860
860
  </div>
861
861
  </div>
862
862
  </div>