beast-agent 2.63.3 → 2.63.5

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/src/preload.js CHANGED
@@ -107,6 +107,10 @@ contextBridge.exposeInMainWorld('beast', {
107
107
  /* Supermemory (lokal) bellek katmanı ayarları */
108
108
  supermemoryGet: () => ipcRenderer.invoke('supermemory:get'),
109
109
  supermemorySet: (cfg) => ipcRenderer.invoke('supermemory:set', cfg),
110
+ /* TypeSafe (System One/Jev) — typesafe_decision aracı için API anahtarı */
111
+ typesafeGet: () => ipcRenderer.invoke('typesafe:get'),
112
+ typesafeSet: (cfg) => ipcRenderer.invoke('typesafe:set', cfg),
113
+ typesafeTest: (payload) => ipcRenderer.invoke('typesafe:test', payload),
110
114
  studioSend: (msg, attachments) => ipcRenderer.invoke('studio:send', { msg, attachments }),
111
115
  studioStop: () => ipcRenderer.invoke('studio:stop'),
112
116
  studioNew: () => ipcRenderer.invoke('studio:new'),
@@ -79,6 +79,7 @@
79
79
  tab_email: 'E-posta',
80
80
  tab_integrations: 'Entegrasyonlar',
81
81
  tab_websearch: 'Web Arama',
82
+ tab_typesafe: 'TypeSafe',
82
83
  tab_mcp: 'MCP',
83
84
  mcp_h2: 'MCP Serverlar',
84
85
  mcp_sub: 'Model Context Protocol — dış araç serverları (filesystem, git, fetch, memory…). Kaynak: mcp.json (aşağıda düzenlenir). Araçlar modele mcp__server__tool adıyla açılır, riskli sayılır (onay kapısı açıkken onay ister).',
@@ -752,6 +753,24 @@
752
753
  mail_reply: 'Yanıtla',
753
754
  mail_read_fail: 'Mail okunamadı',
754
755
  mail_need_to: 'Alıcı adresi gerekli',
756
+ /* TypeSafe (System One / Jev) */
757
+ ts_h2: 'TypeSafe (System One · Jev)',
758
+ ts_sub: 'Tipli karar motoru: metin üretmeden olasılıklı yanıt (noul/choice/score). Anahtar girilince ajanlar typesafe_decision aracını kullanır; Beast Finance rol→skill eşleştirmesinde "typesafe-ai" seçilirse roller bu kararları kullanır.',
759
+ ts_key_label: 'TypeSafe API anahtarı (typesafe.ai)',
760
+ ts_model_label: 'Model (varsayılan: jev-latest)',
761
+ ts_status_set: 'TypeSafe aktif — kayıtlı anahtar: *** · model: ',
762
+ ts_status_unset: 'TypeSafe pasif — anahtar girmeden typesafe_decision aracı çalışmaz.',
763
+ ts_save: 'Kaydet',
764
+ ts_test: 'Bağlantıyı Test Et',
765
+ ts_testing: 'Test ediliyor…',
766
+ ts_clear: 'Anahtarı Sil',
767
+ ts_test_ok: 'TypeSafe bağlantısı başarılı — model: ',
768
+ ts_test_fail: 'TypeSafe testi başarısız: ',
769
+ ts_empty_toast: 'Anahtar boş — mevcut anahtar korundu',
770
+ ts_saved_toast: 'TypeSafe anahtarı kaydedildi',
771
+ ts_cleared_toast: 'TypeSafe anahtarı silindi',
772
+ ts_fail_toast: 'TypeSafe kaydedilemedi',
773
+ ts_hint: 'İpucu: Rol→Skill eşleştirmesinde "typesafe-ai" seçilen roller (ve ana trader playbook) kararlarını typesafe_decision ile alır; anahtar yoksa diğer ajanlar chat modeliyle devam eder.',
755
774
  },
756
775
  en: {
757
776
  newChat: '+ New Chat',
@@ -820,6 +839,7 @@
820
839
  tab_email: 'E-mail',
821
840
  tab_integrations: 'Integrations',
822
841
  tab_websearch: 'Web Search',
842
+ tab_typesafe: 'TypeSafe',
823
843
  tab_mcp: 'MCP',
824
844
  mcp_h2: 'MCP Servers',
825
845
  mcp_sub: 'Model Context Protocol — external tool servers (filesystem, git, fetch, memory…). Source: mcp.json (editable below). Tools are exposed to the model as mcp__server__tool and count as risky (approval gate applies when enabled).',
@@ -1488,6 +1508,24 @@
1488
1508
  store_install_fail: 'Install failed',
1489
1509
  store_like_ok: 'Like saved',
1490
1510
  store_login_note: 'Your Beast ID is anonymous; your real Beast Code is never shared.',
1511
+ /* TypeSafe (System One / Jev) */
1512
+ ts_h2: 'TypeSafe (System One · Jev)',
1513
+ ts_sub: 'Typed decision engine: probabilistic answers without text generation (noul/choice/score). Once a key is set, agents can use the typesafe_decision tool; when "typesafe-ai" is picked in Beast Finance role→skill matching, those roles use it.',
1514
+ ts_key_label: 'TypeSafe API key (typesafe.ai)',
1515
+ ts_model_label: 'Model (default: jev-latest)',
1516
+ ts_status_set: 'TypeSafe active — saved key: *** · model: ',
1517
+ ts_status_unset: 'TypeSafe inactive — typesafe_decision does nothing without a key.',
1518
+ ts_save: 'Save',
1519
+ ts_test: 'Test Connection',
1520
+ ts_testing: 'Testing…',
1521
+ ts_clear: 'Delete Key',
1522
+ ts_test_ok: 'TypeSafe connection OK — model: ',
1523
+ ts_test_fail: 'TypeSafe test failed: ',
1524
+ ts_empty_toast: 'Key is empty — existing key kept',
1525
+ ts_saved_toast: 'TypeSafe key saved',
1526
+ ts_cleared_toast: 'TypeSafe key deleted',
1527
+ ts_fail_toast: 'Could not save TypeSafe key',
1528
+ ts_hint: 'Tip: roles (and the main trader playbook) matched to "typesafe-ai" use typesafe_decision for their judgments; without a key other agents keep using the chat model.',
1491
1529
  },
1492
1530
  };
1493
1531
 
@@ -606,6 +606,7 @@
606
606
  <button class="tab" data-tab="email" data-i18n="tab_email">E-posta</button>
607
607
  <button class="tab" data-tab="integrations" data-i18n="tab_integrations">Entegrasyonlar</button>
608
608
  <button class="tab" data-tab="websearch" data-i18n="tab_websearch">Web Arama</button>
609
+ <button class="tab" data-tab="typesafe" data-i18n="tab_typesafe">TypeSafe</button>
609
610
  <button class="tab" data-tab="mcp" data-i18n="tab_mcp">MCP</button>
610
611
  <button class="tab" data-tab="events" data-i18n="tab_events">Olay Merkezi</button>
611
612
  <button class="tab" data-tab="empati" data-i18n="tab_empati">Empati Loop</button>
@@ -634,6 +635,7 @@
634
635
  <div id="tab-email" class="pane" hidden></div>
635
636
  <div id="tab-integrations" class="pane" hidden></div>
636
637
  <div id="tab-websearch" class="pane" hidden></div>
638
+ <div id="tab-typesafe" class="pane" hidden></div>
637
639
  <div id="tab-mcp" class="pane" hidden></div>
638
640
  <div id="tab-events" class="pane" hidden></div>
639
641
  <div id="tab-empati" class="pane" hidden></div>
@@ -1369,6 +1369,7 @@ async function renderActiveSettingsTab() {
1369
1369
  case 'email': await renderEmailPane(); break;
1370
1370
  case 'integrations': await renderIntegrationsPane(); break;
1371
1371
  case 'websearch': await renderWebSearchPane(); break;
1372
+ case 'typesafe': await renderTypesafePane(); break;
1372
1373
  case 'mcp': await renderMcpPane(); break;
1373
1374
  case 'events': await renderEventsPane(); break;
1374
1375
  case 'empati': await renderEmpatiPane(); break;
@@ -1459,7 +1460,7 @@ function switchTab(name) {
1459
1460
  document.querySelectorAll('#setTabs .tab').forEach((b) =>
1460
1461
  b.classList.toggle('active', b.dataset.tab === name)
1461
1462
  );
1462
- for (const p of ['lang', 'provider', 'fallout', 'skills', 'agents', 'tts', 'install', 'email', 'integrations', 'websearch', 'mcp', 'events', 'empati', 'cron', 'agenda', 'usage', 'squeeze', 'logs', 'dash', 'sec', 'update']) {
1463
+ for (const p of ['lang', 'provider', 'fallout', 'skills', 'agents', 'tts', 'install', 'email', 'integrations', 'websearch', 'typesafe', 'mcp', 'events', 'empati', 'cron', 'agenda', 'usage', 'squeeze', 'logs', 'dash', 'sec', 'update']) {
1463
1464
  const el = $('#tab-' + p);
1464
1465
  if (el) el.hidden = p !== name; // guard: eksik pane tüm sekmeleri kilitlemesin
1465
1466
  }
@@ -1478,6 +1479,7 @@ function switchTab(name) {
1478
1479
  if (name === 'update') renderUpdatePane(true);
1479
1480
  if (name === 'agents') refreshAgentsPane();
1480
1481
  if (name === 'websearch') renderWebSearchPane();
1482
+ if (name === 'typesafe') renderTypesafePane();
1481
1483
  if (name === 'mcp') renderMcpPane();
1482
1484
  /* Fallout: her açılışta güncel provider zincirini çek */
1483
1485
  if (name === 'fallout') refreshFalloutPane();
@@ -1680,6 +1682,65 @@ async function renderWebSearchPane() {
1680
1682
  });
1681
1683
  }
1682
1684
 
1685
+ /* TypeSafe sekmesi (System One / Jev): API anahtarı + model + bağlantı testi.
1686
+ Anahtar kayıtlıysa ajanlar typesafe_decision aracıyla tipli kararlar alır;
1687
+ Beast Finance rol→skill eşleştirmesinde "typesafe-ai" seçilirse kullanılır. */
1688
+ async function renderTypesafePane() {
1689
+ const pane = $('#tab-typesafe');
1690
+ if (!pane) return;
1691
+ const ts = await beast.typesafeGet().catch(() => ({ set: false, apiKey: '', model: 'jev-latest' }));
1692
+ pane.innerHTML =
1693
+ '<h2>' + _t('ts_h2') + '</h2>' +
1694
+ '<div class="sub">' + _t('ts_sub') + '</div>' +
1695
+ '<div id="tsStatus" class="sub" style="text-align:left;margin-top:8px"></div>' +
1696
+ '<label class="mem-label">' + _t('ts_key_label') + '</label>' +
1697
+ '<input id="tsKeyInp" class="inp" type="password" placeholder="ts_..." autocomplete="new-password" spellcheck="false" />' +
1698
+ '<label class="mem-label" style="margin-top:10px">' + _t('ts_model_label') + '</label>' +
1699
+ '<input id="tsModelInp" class="inp" type="text" placeholder="jev-latest" spellcheck="false" />' +
1700
+ '<div class="form-grid" style="grid-template-columns:auto auto auto;gap:8px;margin-top:10px">' +
1701
+ '<button id="tsSave" class="btn">' + _t('ts_save') + '</button>' +
1702
+ '<button id="tsTest" class="btn ghost">' + _t('ts_test') + '</button>' +
1703
+ '<button id="tsClear" class="btn ghost">' + _t('ts_clear') + '</button></div>' +
1704
+ '<div class="sub" style="margin-top:10px">' + _t('ts_hint') + '</div>';
1705
+ const tsSt = $('#tsStatus');
1706
+ const tsModel = $('#tsModelInp');
1707
+ tsModel.value = ts.model || 'jev-latest';
1708
+ const setTsSt = (r) => {
1709
+ tsSt.textContent = r.set ? _t('ts_status_set') + r.model : _t('ts_status_unset');
1710
+ };
1711
+ setTsSt(ts);
1712
+ const tsTest = $('#tsTest');
1713
+ const runTest = async () => {
1714
+ tsTest.disabled = true;
1715
+ const old = tsTest.textContent;
1716
+ tsTest.textContent = _t('ts_testing');
1717
+ const r = await beast.typesafeTest({ apiKey: $('#tsKeyInp').value.trim(), model: tsModel.value.trim() }).catch(() => null);
1718
+ tsTest.disabled = false;
1719
+ tsTest.textContent = old;
1720
+ if (r && r.ok) toast(_t('ts_test_ok') + (r.model || ''));
1721
+ else toast(_t('ts_test_fail') + ((r && r.error) || '?'));
1722
+ };
1723
+ tsTest.addEventListener('click', runTest);
1724
+ $('#tsSave').addEventListener('click', async () => {
1725
+ const v = $('#tsKeyInp').value.trim();
1726
+ if (!v) { toast(_t('ts_empty_toast')); return; }
1727
+ const rr = await beast.typesafeSet({ apiKey: v, model: tsModel.value.trim() }).catch(() => null);
1728
+ $('#tsKeyInp').value = '';
1729
+ if (rr && rr.ok) {
1730
+ setTsSt(rr.typesafe || {});
1731
+ toast(_t('ts_saved_toast'));
1732
+ } else {
1733
+ toast(_t('ts_fail_toast'));
1734
+ }
1735
+ });
1736
+ $('#tsClear').addEventListener('click', async () => {
1737
+ await beast.typesafeSet({ apiKey: '', model: tsModel.value.trim() }).catch(() => {});
1738
+ $('#tsKeyInp').value = '';
1739
+ setTsSt({ set: false, model: tsModel.value.trim() || 'jev-latest' });
1740
+ toast(_t('ts_cleared_toast'));
1741
+ });
1742
+ }
1743
+
1683
1744
  async function renderProviderPane() {
1684
1745
  const pane = $('#tab-provider');
1685
1746
  pane.innerHTML =
@@ -10231,16 +10292,19 @@ function finOnEvent(ev) {
10231
10292
  if (ev.maxLot !== undefined) finCfgCache.maxLot = ev.maxLot;
10232
10293
  if (ev.maxPositions !== undefined) finCfgCache.maxPositions = ev.maxPositions;
10233
10294
  if (ev.symbols !== undefined) finCfgCache.symbolLimits = ev.symbols;
10295
+ if (ev.intervalBase !== undefined) finCfgCache.intervalSec = ev.intervalBase;
10234
10296
  finTraderInputsSet(finCfgCache);
10235
10297
  } else {
10236
10298
  const ae2 = document.activeElement;
10237
10299
  if (els.finMinLot && ae2 !== els.finMinLot && ev.minLot !== undefined) els.finMinLot.value = ev.minLot;
10238
10300
  if (els.finMaxLot && ae2 !== els.finMaxLot && ev.maxLot !== undefined) els.finMaxLot.value = ev.maxLot;
10301
+ if (els.finInterval && ae2 !== els.finInterval && ev.intervalBase !== undefined) els.finInterval.value = ev.intervalBase;
10239
10302
  if (ev.symbols !== undefined) finSymLimitsRender(ev.symbols);
10240
10303
  }
10241
10304
  finLogLine('[limit] ' + (ev.note || ('min lot ' + ev.minLot + ' · max lot ' + ev.maxLot + ' · max pozisyon ' + ev.maxPositions)));
10242
10305
  if (ev.symbol) toast('Sembol lot limiti güncellendi: ' + ev.symbol);
10243
- else toast('Trader limitleri güncelledi: min ' + ev.minLot + ' / max ' + ev.maxLot);
10306
+ else if (ev.paceUntil) toast('Trader hız modu: tur ' + ev.intervalSec + ' sn — süre bitince tabana döner');
10307
+ else toast('Trader limitleri güncelledi: min ' + ev.minLot + ' / max ' + ev.maxLot + (ev.intervalSec ? ' · tur ' + ev.intervalSec + ' sn' : ''));
10244
10308
  } else if (ev.fn === 'install') {
10245
10309
  finLogLine('[MT5] paket kurulumu tamamlandı (kod ' + ev.code + ')');
10246
10310
  }
@@ -10951,8 +11015,9 @@ function finSymPickerClose() {
10951
11015
 
10952
11016
  /* ---------- ROL → SKILL eşleştirme modalı ----------
10953
11017
  Roller sunucudan (snapshot.roles), skill listesi KURULU katalogdan gelir —
10954
- yeni skill eklendiğinde modalda otomatik görünür. Seçim finance ayarına
10955
- (roleSkills) yazılır; rol ajanı her turda o skill'leri okumakla yükümlüdür. */
11018
+ yeni skill eklendiğinde modalda otomatik görünür. Rol başına EN FAZLA 2
11019
+ skill seçilir; seçim finance ayarına (roleSkills) yazılır; rol ajanı her
11020
+ turda o skill'leri okumakla yükümlüdür (ör. price-action + typesafe-ai). */
10956
11021
  let finSkillNames = [];
10957
11022
 
10958
11023
  async function finSkillsOpen() {
@@ -10973,7 +11038,7 @@ function finRenderSkills() {
10973
11038
  if (!box) return;
10974
11039
  const cfg = finCfgCache || {};
10975
11040
  const map = cfg.roleSkills && typeof cfg.roleSkills === 'object' ? cfg.roleSkills : {};
10976
- /* ANA KARAR VERİCİ (trader) da bir satır: playbook skill'i zorunlu tek seçim */
11041
+ /* ANA KARAR VERİCİ (trader) da bir satır: playbook skill'leri (en fazla 2) */
10977
11042
  const roles = [
10978
11043
  { id: 'trader', label: 'TRADER (Ana Karar Verici)', desc: 'playbook: hangi setup\'lar serbest, giriş/çıkış/iptal kuralları, seans ve risk disiplini — asıl karar vericinin skill\'i (boş = ajan kendi seçer)' },
10979
11044
  ].concat(Array.isArray(finRolesCatalog) ? finRolesCatalog : []);
@@ -10994,22 +11059,21 @@ function finRenderSkills() {
10994
11059
  head.innerHTML =
10995
11060
  '<b>' + escapeHtml(String(r.label || r.id)) + '</b>' +
10996
11061
  '<span>' + escapeHtml(String(r.desc || '').slice(0, 110)) + '</span>';
10997
- const cur = Array.isArray(map[r.id]) ? map[r.id].map((x) => String(x || '').trim()).filter(Boolean) : [];
10998
- const active = cur.length ? cur[0] : '';
10999
- /* ÇÖP: rolün skill atamasını kaldır — boş bırakmak da geçerli bir seçim
11000
- (o zaman ajan skill'i göreve göre SKILLS kataloğundan kendi seçer) */
11062
+ const cur = Array.isArray(map[r.id]) ? map[r.id].map((x) => String(x || '').trim()).filter(Boolean).slice(0, 2) : [];
11063
+ /* ÇÖP: rolün TÜM skill atamasını kaldır — boş bırakmak da geçerli bir
11064
+ seçim (o zaman ajan skill'i göreve göre SKILLS kataloğundan kendi seçer) */
11001
11065
  const clearBtn = document.createElement('button');
11002
11066
  clearBtn.type = 'button';
11003
11067
  clearBtn.className = 'fin-skill-clear';
11004
- clearBtn.title = active
11005
- ? 'Skill atamasını kaldır (' + active + ') — boş kalırsa ajan göreve göre kendi seçer'
11068
+ clearBtn.title = cur.length
11069
+ ? 'Skill atamalarını kaldır (' + cur.join(', ') + ') — boş kalırsa ajan göreve göre kendi seçer'
11006
11070
  : 'Atama yok — boş bırakılabilir';
11007
11071
  clearBtn.innerHTML =
11008
11072
  '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-2px"><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>';
11009
11073
  clearBtn.addEventListener('click', (e) => {
11010
11074
  e.preventDefault();
11011
11075
  e.stopPropagation();
11012
- const was = active;
11076
+ const was = cur.join(', ');
11013
11077
  finCfgCache = finCfgCache || {};
11014
11078
  finCfgCache.roleSkills = finCfgCache.roleSkills || {};
11015
11079
  finCfgCache.roleSkills[r.id] = [];
@@ -11020,38 +11084,64 @@ function finRenderSkills() {
11020
11084
  finRenderSkills();
11021
11085
  });
11022
11086
  head.appendChild(clearBtn);
11023
- if (!active) {
11087
+ if (!cur.length) {
11024
11088
  const warn = document.createElement('span');
11025
11089
  warn.className = 'fin-skill-req';
11026
11090
  warn.textContent = 'atama yok — ajan göreve göre kendi seçer';
11027
11091
  head.appendChild(warn);
11092
+ } else if (cur.length > 1) {
11093
+ const cnt = document.createElement('span');
11094
+ cnt.className = 'fin-skill-req';
11095
+ cnt.textContent = cur.length + '/2 skill';
11096
+ head.appendChild(cnt);
11028
11097
  }
11029
11098
  row.appendChild(head);
11030
11099
  const chips = document.createElement('div');
11031
11100
  chips.className = 'fin-skill-chips';
11032
11101
  for (const s of finSkillNames) {
11033
- const on = active === s.name;
11102
+ const on = cur.includes(s.name);
11034
11103
  const chip = document.createElement('label');
11035
11104
  chip.className = 'fin-skill-chip' + (on ? ' on' : '');
11036
11105
  chip.title = s.description || s.name;
11037
11106
  const cb = document.createElement('input');
11038
- cb.type = 'radio';
11039
- cb.name = 'fin-skill-role-' + r.id;
11107
+ cb.type = 'checkbox';
11040
11108
  cb.checked = on;
11041
- /* TEK seçim: yeni skill seçilir; kaldırmak için çöp ikonu kullanılır */
11109
+ /* EN FAZLA 2 seçim: yeni skill işaretlenir, sınır aşılırsa uyarır;
11110
+ kaldırmak için işaret kaldırılır ya da çöp ikonu kullanılır */
11042
11111
  cb.addEventListener('change', () => {
11043
- if (!cb.checked) return;
11044
- const next = [s.name];
11045
- /* yerel önbelleğe anında yaz — 3 sn'lik snapshot eski değeri geri getirmesin */
11046
11112
  finCfgCache = finCfgCache || {};
11047
11113
  finCfgCache.roleSkills = finCfgCache.roleSkills || {};
11114
+ const curNow = (Array.isArray(finCfgCache.roleSkills[r.id]) ? finCfgCache.roleSkills[r.id] : []).map((x) => String(x || '').trim()).filter(Boolean);
11115
+ let next;
11116
+ if (cb.checked) {
11117
+ if (curNow.length >= 2) {
11118
+ cb.checked = false;
11119
+ toast((r.label || r.id) + ' → en fazla 2 skill; önce birini kaldır');
11120
+ return;
11121
+ }
11122
+ next = [...curNow, s.name];
11123
+ } else {
11124
+ next = curNow.filter((x) => x !== s.name);
11125
+ }
11126
+ next = next.slice(0, 2);
11127
+ /* yerel önbelleğe anında yaz — 3 sn'lik snapshot eski değeri geri getirmesin */
11048
11128
  finCfgCache.roleSkills[r.id] = next;
11049
- chips.querySelectorAll('.fin-skill-chip').forEach((el) => el.classList.remove('on'));
11050
- chip.classList.add('on');
11129
+ chip.classList.toggle('on', next.includes(s.name));
11051
11130
  const w = head.querySelector('.fin-skill-req');
11052
11131
  if (w) w.remove();
11132
+ if (!next.length) {
11133
+ const warn = document.createElement('span');
11134
+ warn.className = 'fin-skill-req';
11135
+ warn.textContent = 'atama yok — ajan göreve göre kendi seçer';
11136
+ head.appendChild(warn);
11137
+ } else if (next.length > 1) {
11138
+ const cnt = document.createElement('span');
11139
+ cnt.className = 'fin-skill-req';
11140
+ cnt.textContent = next.length + '/2 skill';
11141
+ head.appendChild(cnt);
11142
+ }
11053
11143
  finSaveCfg({ roleSkills: { [r.id]: next } });
11054
- toast((r.label || r.id) + ' → ' + s.name + ' (tek skill)');
11144
+ toast((r.label || r.id) + ' → ' + (next.length ? next.join(' + ') : 'atama kaldırıldı') + ' (' + next.length + '/2)');
11055
11145
  });
11056
11146
  chip.appendChild(cb);
11057
11147
  chip.appendChild(document.createTextNode(s.name));
@@ -11120,6 +11210,12 @@ function finLearnStatsText(st) {
11120
11210
  if (Number(st.streak) >= 2) parts.push(Number(st.streak) + ' ardışık kayıp');
11121
11211
  if (Number(st.givebacks) > 0) parts.push(Number(st.givebacks) + ' kez kâr geri verildi');
11122
11212
  if (st.lastAt) parts.push('son ' + finLearnSign(st.lastNet));
11213
+ /* EN İYİ PERİYOT: bot zaman dilimini buna göre seçer */
11214
+ const tfRows = Object.entries((st && st.byTf) || {}).filter(([, s]) => s && Number(s.trades) > 0);
11215
+ if (tfRows.length) {
11216
+ tfRows.sort((a, b) => (Number(b[1].net) || 0) - (Number(a[1].net) || 0));
11217
+ parts.push('en iyi ' + tfRows[0][0]);
11218
+ }
11123
11219
  return parts.join(' · ');
11124
11220
  }
11125
11221
 
@@ -11268,6 +11364,47 @@ function finLearnDetail(body, sym, d) {
11268
11364
  info.textContent = bits.join(' — ');
11269
11365
  body.appendChild(info);
11270
11366
 
11367
+ /* PERİYOT KIRILIMI: hangi zaman dilimi kazandırıyor — bot işlem periyodunu
11368
+ (timeframe) buna göre SEÇER; negatif periyotta risk düşürülür */
11369
+ const tfRows = Object.entries(st.byTf || {}).filter(([, s]) => s && Number(s.trades) > 0);
11370
+ if (tfRows.length) {
11371
+ const tfHead = document.createElement('div');
11372
+ tfHead.className = 'fin-learn-sec';
11373
+ tfHead.textContent = 'ZAMAN DİLİMLERİ (işlem periyodunu bot seçer — en iyi net üstte)';
11374
+ body.appendChild(tfHead);
11375
+ tfRows.sort((a, b) => (Number(b[1].net) || 0) - (Number(a[1].net) || 0));
11376
+ for (const [tf, s] of tfRows) {
11377
+ const tr = document.createElement('div');
11378
+ tr.className = 'fin-learn-trade ' + (Number(s.net) < 0 ? 'loss' : 'win');
11379
+ const w = Math.round((Number(s.wins) / Number(s.trades)) * 100);
11380
+ tr.textContent = `${tf} · ${s.trades} işlem · %${w} kazanç · net ${finLearnSign(s.net)}` +
11381
+ (Number(s.givebacks) > 0 ? ` · ${Number(s.givebacks)} kâr geri verme` : '') +
11382
+ (Number(s.net) < 0 ? ' · NEGATİF: riski düşür/teyit ara' : '');
11383
+ body.appendChild(tr);
11384
+ }
11385
+ }
11386
+
11387
+ /* YAPILAN YANLIŞLAR: zararla kapananlar otomatik kaydedilir — ajan bunlardan
11388
+ hata dersi (kind:"mistake") çıkarır; tekrar eden kalıp burada görünür */
11389
+ const mistakes = (d && Array.isArray(d.mistakes)) ? d.mistakes : [];
11390
+ if (mistakes.length) {
11391
+ const mHead = document.createElement('div');
11392
+ mHead.className = 'fin-learn-sec';
11393
+ mHead.textContent = 'YAPILAN YANLIŞLAR (' + mistakes.length + ' — ders çıkar, tekrarlama)';
11394
+ body.appendChild(mHead);
11395
+ for (const m of mistakes.slice().reverse()) {
11396
+ const tr = document.createElement('div');
11397
+ tr.className = 'fin-learn-trade loss';
11398
+ const side = String(m.side || '').toLowerCase();
11399
+ const sideTxt = side === 'buy' ? 'AL' : side === 'sell' ? 'SAT' : (side || '?');
11400
+ tr.textContent = `${finLearnTime(m.at)} · ${m.tf || 'periyot?'} · ${sideTxt} · ${finLearnSign(m.net)}` +
11401
+ (m.reason ? ` · ${String(m.reason)}` : '') +
11402
+ (m.giveback ? ' · kâr geri verildi' : '') +
11403
+ (m.mae != null ? ` · MAE ${finLearnSign(m.mae)}` : '');
11404
+ body.appendChild(tr);
11405
+ }
11406
+ }
11407
+
11271
11408
  /* KALICI ÖZET (opencode tarzı compaction): eski derslerin modele
11272
11409
  özetlettirilmiş hâli — ham dersler silinse de bilgi burada kalır */
11273
11410
  const summ = d && d.summary && d.summary.text ? d.summary : null;
@@ -11304,6 +11441,13 @@ function finLearnDetail(body, sym, d) {
11304
11441
  const kind = document.createElement('span');
11305
11442
  kind.className = 'fin-learn-kind k-' + String(n.kind || 'observation');
11306
11443
  kind.textContent = finLearnKindLabel(n.kind);
11444
+ let tfEl = null;
11445
+ if (n.tf) {
11446
+ tfEl = document.createElement('span');
11447
+ tfEl.className = 'fin-learn-kind';
11448
+ tfEl.textContent = String(n.tf);
11449
+ tfEl.title = 'Bu dersin zaman dilimi';
11450
+ }
11307
11451
  const txt = document.createElement('span');
11308
11452
  txt.className = 'fin-learn-note-text';
11309
11453
  txt.textContent = String(n.text || '');
@@ -11326,6 +11470,7 @@ function finLearnDetail(body, sym, d) {
11326
11470
  }
11327
11471
  });
11328
11472
  rowEl.appendChild(kind);
11473
+ if (tfEl) rowEl.appendChild(tfEl);
11329
11474
  rowEl.appendChild(txt);
11330
11475
  rowEl.appendChild(at);
11331
11476
  rowEl.appendChild(del);
@@ -11344,7 +11489,7 @@ function finLearnDetail(body, sym, d) {
11344
11489
  tr.className = 'fin-learn-trade ' + (Number(t.net) < 0 ? 'loss' : 'win');
11345
11490
  const side = String(t.side || '').toLowerCase();
11346
11491
  const sideTxt = side === 'buy' ? 'AL' : side === 'sell' ? 'SAT' : (side || '?');
11347
- tr.textContent = `${finLearnTime(t.at)} · ${sideTxt} · ${finLearnSign(t.net)}` +
11492
+ tr.textContent = `${finLearnTime(t.at)} · ${t.tf ? t.tf + ' · ' : ''}${sideTxt} · ${finLearnSign(t.net)}` +
11348
11493
  (t.mfe != null ? ` · MFE ${finLearnSign(t.mfe)}` : '') +
11349
11494
  (t.mae != null ? ` · MAE ${finLearnSign(t.mae)}` : '') +
11350
11495
  (t.reason ? ` · ${String(t.reason)}` : '');