beast-agent 2.3.4 → 2.4.1
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/perception.js +616 -0
- package/src/main.js +223 -0
- package/src/preload.js +6 -0
- package/src/renderer/i18n.js +100 -12
- package/src/renderer/index.html +2 -0
- package/src/renderer/renderer.js +214 -26
package/src/renderer/renderer.js
CHANGED
|
@@ -1066,6 +1066,7 @@ async function renderActiveSettingsTab() {
|
|
|
1066
1066
|
case 'websearch': await renderWebSearchPane(); break;
|
|
1067
1067
|
case 'mcp': await renderMcpPane(); break;
|
|
1068
1068
|
case 'events': await renderEventsPane(); break;
|
|
1069
|
+
case 'empati': await renderEmpatiPane(); break;
|
|
1069
1070
|
case 'cron': await openCron(); break;
|
|
1070
1071
|
case 'usage': await renderUsagePane(); break;
|
|
1071
1072
|
case 'agents': await refreshAgentsPane(); break;
|
|
@@ -1151,7 +1152,7 @@ function switchTab(name) {
|
|
|
1151
1152
|
document.querySelectorAll('#setTabs .tab').forEach((b) =>
|
|
1152
1153
|
b.classList.toggle('active', b.dataset.tab === name)
|
|
1153
1154
|
);
|
|
1154
|
-
|
|
1155
|
+
for (const p of ['lang', 'provider', 'fallout', 'skills', 'agents', 'tts', 'install', 'email', 'integrations', 'websearch', 'mcp', 'events', 'empati', 'cron', 'usage', 'logs', 'dash', 'sec', 'update']) {
|
|
1155
1156
|
const el = $('#tab-' + p);
|
|
1156
1157
|
if (el) el.hidden = p !== name; // guard: eksik pane tüm sekmeleri kilitlemesin
|
|
1157
1158
|
}
|
|
@@ -1160,6 +1161,7 @@ function switchTab(name) {
|
|
|
1160
1161
|
if (name === 'usage') renderUsagePane();
|
|
1161
1162
|
if (name === 'install') renderInstallPane();
|
|
1162
1163
|
if (name === 'events') renderEventsPane();
|
|
1164
|
+
if (name === 'empati') renderEmpatiPane();
|
|
1163
1165
|
if (name === 'logs') renderLogPane();
|
|
1164
1166
|
if (name === 'dash') renderDashboardPane();
|
|
1165
1167
|
if (name === 'sec') renderSecurityPane();
|
|
@@ -2331,47 +2333,49 @@ async function renderEmailPane() {
|
|
|
2331
2333
|
const PASS_MASK = '••••••••';
|
|
2332
2334
|
let savedPass = '';
|
|
2333
2335
|
let passChanged = false;
|
|
2336
|
+
/* ID'ler mail* önekiyle: empati sekmesindeki em* ID'leriyle ÇAKIŞMASIN —
|
|
2337
|
+
aynı ID iki panelde olursa $() ilkini bulur, empati butonları ölü kalırdı */
|
|
2334
2338
|
pane.innerHTML =
|
|
2335
|
-
'<h2>' + _t('
|
|
2339
|
+
'<h2>' + _t('mail_h2') + '</h2><div class="sub">' + _t('mail_sub') + '</div>' +
|
|
2336
2340
|
`<div class="form-grid" style="grid-template-columns:1fr 1fr;align-items:center;margin-top:10px">
|
|
2337
|
-
<input id="
|
|
2338
|
-
<input id="
|
|
2339
|
-
<input id="
|
|
2340
|
-
<input id="
|
|
2341
|
-
<input id="
|
|
2342
|
-
<button id="
|
|
2341
|
+
<input id="mailHost" class="inp" placeholder="imap.gmail.com" autocomplete="off" />
|
|
2342
|
+
<input id="mailUser" class="inp" placeholder="adres@gmail.com" autocomplete="off" />
|
|
2343
|
+
<input id="mailPass" class="inp" type="password" placeholder="Uygulama Şifresi" autocomplete="off" />
|
|
2344
|
+
<input id="mailSmtpHost" class="inp" placeholder="smtp.gmail.com" autocomplete="off" />
|
|
2345
|
+
<input id="mailSmtpPort" class="inp" placeholder="465" autocomplete="off" />
|
|
2346
|
+
<button id="mailSave" class="btn ghost">${_t('mail_save')}</button>
|
|
2343
2347
|
</div>` +
|
|
2344
|
-
'<div class="sub" style="margin-top:8px">' + _t('
|
|
2348
|
+
'<div class="sub" style="margin-top:8px">' + _t('mail_note') + '</div>';
|
|
2345
2349
|
|
|
2346
2350
|
try {
|
|
2347
2351
|
const em = await beast.getEmail();
|
|
2348
|
-
$('#
|
|
2349
|
-
$('#
|
|
2350
|
-
$('#
|
|
2351
|
-
$('#
|
|
2352
|
+
$('#mailHost').value = em.host || 'imap.gmail.com';
|
|
2353
|
+
$('#mailUser').value = em.user || '';
|
|
2354
|
+
$('#mailSmtpHost').value = em.smtpHost || 'smtp.gmail.com';
|
|
2355
|
+
$('#mailSmtpPort').value = em.smtpPort || 465;
|
|
2352
2356
|
savedPass = em.pass || '';
|
|
2353
2357
|
} catch {}
|
|
2354
|
-
const
|
|
2358
|
+
const mailPass = $('#mailPass');
|
|
2355
2359
|
if (savedPass) {
|
|
2356
2360
|
/* kayıtlı şifre maskelenir; sadece kullanıcı değiştirirse yenisi gönderilir */
|
|
2357
|
-
|
|
2358
|
-
|
|
2361
|
+
mailPass.value = PASS_MASK;
|
|
2362
|
+
mailPass.placeholder = window.I18N ? window.I18N.t('mail_pass_masked') : 'Mevcut şifre korunuyor — değiştirmek için yeniden gir';
|
|
2359
2363
|
}
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
$('#
|
|
2363
|
-
const entered =
|
|
2364
|
+
mailPass.addEventListener('focus', () => { if (mailPass.value === PASS_MASK) mailPass.value = ''; });
|
|
2365
|
+
mailPass.addEventListener('input', () => { passChanged = true; });
|
|
2366
|
+
$('#mailSave').addEventListener('click', async () => {
|
|
2367
|
+
const entered = mailPass.value;
|
|
2364
2368
|
const pass = passChanged ? entered : savedPass;
|
|
2365
2369
|
await beast.setEmail({
|
|
2366
|
-
host: $('#
|
|
2370
|
+
host: $('#mailHost').value.trim() || 'imap.gmail.com',
|
|
2367
2371
|
port: 993,
|
|
2368
|
-
user: $('#
|
|
2372
|
+
user: $('#mailUser').value.trim(),
|
|
2369
2373
|
pass,
|
|
2370
|
-
smtpHost: $('#
|
|
2371
|
-
smtpPort: Number($('#
|
|
2374
|
+
smtpHost: $('#mailSmtpHost').value.trim() || 'smtp.gmail.com',
|
|
2375
|
+
smtpPort: Number($('#mailSmtpPort').value) || 465,
|
|
2372
2376
|
});
|
|
2373
|
-
if (pass && pass !== PASS_MASK) { savedPass = pass;
|
|
2374
|
-
toast(_t('
|
|
2377
|
+
if (pass && pass !== PASS_MASK) { savedPass = pass; mailPass.value = PASS_MASK; passChanged = false; }
|
|
2378
|
+
toast(_t('mail_saved'));
|
|
2375
2379
|
});
|
|
2376
2380
|
}
|
|
2377
2381
|
|
|
@@ -3022,6 +3026,181 @@ async function renderEventsPane() {
|
|
|
3022
3026
|
});
|
|
3023
3027
|
}
|
|
3024
3028
|
|
|
3029
|
+
/* ---------------- EMPATİ LOOP (proaktif algı sekmesi) ---------------- */
|
|
3030
|
+
async function renderEmpatiPane() {
|
|
3031
|
+
const pane = $('#tab-empati');
|
|
3032
|
+
if (!pane) return;
|
|
3033
|
+
/* pane-scope: e-posta sekmesinde eskiden aynı ID'ler vardı; $() global arar
|
|
3034
|
+
ve YANLIŞ butona bağlardı — tüm aramalar bu panelin içinde kalır */
|
|
3035
|
+
const q = (sel) => pane.querySelector(sel);
|
|
3036
|
+
const cfg = await beast.empatiGet().catch(() => null);
|
|
3037
|
+
let events = [];
|
|
3038
|
+
try { events = await beast.empatiEvents(); } catch {}
|
|
3039
|
+
let mem = null;
|
|
3040
|
+
try { mem = await beast.empatiMemory(); } catch {}
|
|
3041
|
+
let models = [];
|
|
3042
|
+
try { models = (await beast.getState()).models || []; } catch {}
|
|
3043
|
+
if (!cfg) {
|
|
3044
|
+
pane.innerHTML = '<h2>' + _t('em_h2') + '</h2><div class="sub">' + _t('em_ipc_err') + '</div>';
|
|
3045
|
+
return;
|
|
3046
|
+
}
|
|
3047
|
+
const modelOpts =
|
|
3048
|
+
'<option value="">' + _t('em_model_main') + '</option>' +
|
|
3049
|
+
models.map((m) =>
|
|
3050
|
+
'<option value="' + escapeHtml(m.sel || '') + '"' + (cfg.filterModel === m.sel ? ' selected' : '') + '>' +
|
|
3051
|
+
escapeHtml((m.providerName || '') + ' · ' + (m.model || '')) + '</option>'
|
|
3052
|
+
).join('');
|
|
3053
|
+
const targetOpts = [
|
|
3054
|
+
['whatsapp', _t('em_notify_wa')],
|
|
3055
|
+
['telegram', _t('em_notify_tg')],
|
|
3056
|
+
['discord', _t('em_notify_dc')],
|
|
3057
|
+
].map(([v, lbl]) =>
|
|
3058
|
+
'<option value="' + v + '"' + (cfg.notifyTarget === v ? ' selected' : '') + '>' + escapeHtml(lbl) + '</option>'
|
|
3059
|
+
).join('');
|
|
3060
|
+
const when = (iso) => {
|
|
3061
|
+
try {
|
|
3062
|
+
return new Date(iso).toLocaleString('tr-TR', { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' });
|
|
3063
|
+
} catch { return String(iso || ''); }
|
|
3064
|
+
};
|
|
3065
|
+
const statusT = { notified: _t('em_st_notified'), queued: _t('em_st_queued'), stored: _t('em_st_stored'), ignored: _t('em_st_ignored') };
|
|
3066
|
+
const lvBadge = (e) => {
|
|
3067
|
+
if (!e.level) return '';
|
|
3068
|
+
const color = e.level === 'high' ? '#e06c75' : '#d9a441';
|
|
3069
|
+
return ' · <b style="color:' + color + '">' + (e.level === 'high' ? _t('em_lv_high') : _t('em_lv_medium')) + '</b>';
|
|
3070
|
+
};
|
|
3071
|
+
let rows = '';
|
|
3072
|
+
for (const e of events) {
|
|
3073
|
+
rows +=
|
|
3074
|
+
'<div class="usage-row" style="align-items:flex-start">' +
|
|
3075
|
+
'<div style="min-width:0;flex:1">' +
|
|
3076
|
+
'<div style="font-size:12px">' + escapeHtml(e.title || '') + '</div>' +
|
|
3077
|
+
'<div class="ur-meta">' + escapeHtml(e.source || '') + ' · ' + when(e.ts) + ' · %' + (e.priority || 0) +
|
|
3078
|
+
(e.reason ? ' · ' + escapeHtml(e.reason) : '') + '</div>' +
|
|
3079
|
+
(e.text ? '<div class="sub" style="font-size:11px">' + escapeHtml(e.text) + '</div>' : '') +
|
|
3080
|
+
'</div>' +
|
|
3081
|
+
'<span style="flex:none;font-size:11px;color:var(--muted)">' + (statusT[e.status] || e.status) + lvBadge(e) + '</span>' +
|
|
3082
|
+
'</div>';
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
pane.innerHTML =
|
|
3086
|
+
'<h2>' + _t('em_h2') + '</h2>' +
|
|
3087
|
+
'<div class="sub">' + _t('em_sub') + '</div>' +
|
|
3088
|
+
(cfg.lastRunAt ? '<div class="sub">' + _t('em_lastrun') + ': ' + when(cfg.lastRunAt) + (cfg.running ? ' · ⏳' : '') + '</div>' : '') +
|
|
3089
|
+
`<div class="fo-toggles" style="margin-top:10px">
|
|
3090
|
+
<label class="lock-row"><input type="checkbox" id="emOn" ${cfg.enabled ? 'checked' : ''}/><span>${_t('em_on')}</span></label>
|
|
3091
|
+
</div>
|
|
3092
|
+
<div id="emDetail" style="${cfg.enabled ? '' : 'opacity:.5'}">
|
|
3093
|
+
<div class="form-grid" style="grid-template-columns:1fr 1fr 1fr;margin-top:8px">
|
|
3094
|
+
<div><label class="sub">${_t('em_interval')}</label><input id="emInterval" class="inp" type="number" min="3" max="1440" value="${cfg.intervalMin}"/></div>
|
|
3095
|
+
<div><label class="sub">${_t('em_min_notify')}</label><input id="emMin" class="inp" type="number" min="0" max="100" value="${cfg.minNotifyPriority}"/></div>
|
|
3096
|
+
<div><label class="sub">${_t('em_cooldown')}</label><input id="emCd" class="inp" type="number" min="0" max="10080" value="${cfg.cooldownMin}"/></div>
|
|
3097
|
+
</div>
|
|
3098
|
+
<div style="margin-top:8px">
|
|
3099
|
+
<label class="sub">${_t('em_model')}</label>
|
|
3100
|
+
<select id="emModel" class="inp">${modelOpts}</select>
|
|
3101
|
+
<div class="sub">${_t('em_model_sub')}</div>
|
|
3102
|
+
</div>
|
|
3103
|
+
<div style="margin-top:8px">
|
|
3104
|
+
<label class="sub">${_t('em_notify')}</label>
|
|
3105
|
+
<select id="emTarget" class="inp"><option value="">${_t('em_notify_auto')}</option>${targetOpts}</select>
|
|
3106
|
+
<div class="sub">${_t('em_notify_sub')}</div>
|
|
3107
|
+
</div>
|
|
3108
|
+
<div style="margin-top:8px">
|
|
3109
|
+
<label class="sub">${_t('em_interests')}</label>
|
|
3110
|
+
<textarea id="emInterests" class="inp" rows="2" placeholder="${_t('em_interests_ph')}">${escapeHtml(cfg.interests || '')}</textarea>
|
|
3111
|
+
<div class="sub">${_t('em_interests_auto')}</div>
|
|
3112
|
+
</div>
|
|
3113
|
+
<label class="lock-row" style="margin-top:8px"><input type="checkbox" id="emNews" ${cfg.newsTopics ? 'checked' : ''}/><span>${_t('em_news')}</span></label>
|
|
3114
|
+
<input id="emTopics" class="inp" style="${cfg.newsTopics ? '' : 'opacity:.5'}" placeholder="${_t('em_news_topics_ph')}" value="${escapeHtml(cfg.newsTopics || '')}" spellcheck="false"/>
|
|
3115
|
+
<div style="display:flex;gap:8px;margin-top:10px;align-items:center">
|
|
3116
|
+
<button id="emSave" class="btn">${_t('mcp_save')}</button>
|
|
3117
|
+
<button id="emScan" class="btn ghost">${_t('em_scan')}</button>
|
|
3118
|
+
<span id="emMsg" class="sub" style="margin:0"></span>
|
|
3119
|
+
</div>
|
|
3120
|
+
<div class="sub" style="margin-top:6px">${_t('em_note')}</div>
|
|
3121
|
+
</div>
|
|
3122
|
+
<h3 style="margin-top:16px;color:var(--muted)">${_t('em_mem_h2')}</h3>
|
|
3123
|
+
<div class="sub">${_t('em_mem_sub')}</div>
|
|
3124
|
+
<div style="margin-top:6px"><label class="sub">${_t('em_mem_learned')}</label>${(() => {
|
|
3125
|
+
const learned = (mem && Array.isArray(mem.learned)) ? mem.learned : [];
|
|
3126
|
+
if (!learned.length) return '<div class="sub" style="margin-top:2px">' + _t('em_mem_no_learned') + '</div>';
|
|
3127
|
+
return '<div style="display:flex;flex-wrap:wrap;gap:4px;margin-top:4px">' +
|
|
3128
|
+
learned.map((x) =>
|
|
3129
|
+
'<span style="font-size:11px;padding:2px 8px;border:1px solid var(--border);border-radius:10px">' +
|
|
3130
|
+
escapeHtml(x.w) + ' <span style="color:var(--muted)">×' + (x.c || 0) + '</span></span>'
|
|
3131
|
+
).join('') + '</div>';
|
|
3132
|
+
})()}</div>
|
|
3133
|
+
<div style="margin-top:8px"><label class="sub">${_t('em_mem_recent')}</label>${(() => {
|
|
3134
|
+
const chats = (mem && Array.isArray(mem.chats)) ? mem.chats : [];
|
|
3135
|
+
if (!chats.length) return '<div class="sub" style="margin-top:2px">' + _t('em_mem_no_recent') + '</div>';
|
|
3136
|
+
return '<div style="margin-top:4px">' + chats.map((c) =>
|
|
3137
|
+
'<div class="usage-row" style="align-items:flex-start">' +
|
|
3138
|
+
'<div style="min-width:0;flex:1">' +
|
|
3139
|
+
'<div style="font-size:12px">' + escapeHtml(c.t || '') + '</div>' +
|
|
3140
|
+
'<div class="ur-meta">' + escapeHtml(c.ch || '') + ' · ' + when(c.ts) + '</div>' +
|
|
3141
|
+
'</div></div>').join('') + '</div>';
|
|
3142
|
+
})()}
|
|
3143
|
+
<button id="emMemClear" class="btn ghost" style="margin-top:8px;font-size:12px">${_t('em_mem_clear')}</button>
|
|
3144
|
+
</div>
|
|
3145
|
+
<h3 style="margin-top:16px;color:var(--muted)">${_t('em_events')}</h3>`;
|
|
3146
|
+
const wrap = document.createElement('div');
|
|
3147
|
+
if (!rows) wrap.innerHTML = '<p class="sub">' + _t('em_no_events') + '</p>';
|
|
3148
|
+
else wrap.innerHTML = rows;
|
|
3149
|
+
pane.appendChild(wrap);
|
|
3150
|
+
|
|
3151
|
+
const emOn = q('#emOn');
|
|
3152
|
+
const emDetail = q('#emDetail');
|
|
3153
|
+
const emNews = q('#emNews');
|
|
3154
|
+
const emTopics = q('#emTopics');
|
|
3155
|
+
if (emOn && emDetail) emOn.addEventListener('change', () => { emDetail.style.opacity = emOn.checked ? '' : '.5'; });
|
|
3156
|
+
if (emNews && emTopics) emNews.addEventListener('change', () => { emTopics.style.opacity = emNews.checked ? '' : '.5'; });
|
|
3157
|
+
const emSaveBtn = q('#emSave');
|
|
3158
|
+
if (emSaveBtn) emSaveBtn.addEventListener('click', async () => {
|
|
3159
|
+
const patch = {
|
|
3160
|
+
enabled: emOn.checked,
|
|
3161
|
+
intervalMin: Number(q('#emInterval').value) || cfg.intervalMin,
|
|
3162
|
+
minNotifyPriority: Number(q('#emMin').value),
|
|
3163
|
+
cooldownMin: Number(q('#emCd').value),
|
|
3164
|
+
notifyTarget: q('#emTarget') ? q('#emTarget').value : '',
|
|
3165
|
+
filterModel: q('#emModel') ? q('#emModel').value : '',
|
|
3166
|
+
interests: q('#emInterests').value.trim(),
|
|
3167
|
+
newsTopics: emNews.checked ? emTopics.value.trim() : '',
|
|
3168
|
+
};
|
|
3169
|
+
const r = await beast.empatiSet(patch).catch(() => null);
|
|
3170
|
+
toast(r ? _t('em_saved') : 'Hata');
|
|
3171
|
+
if (r) renderEmpatiPane();
|
|
3172
|
+
});
|
|
3173
|
+
const emScanBtn = q('#emScan');
|
|
3174
|
+
if (emScanBtn) emScanBtn.addEventListener('click', async () => {
|
|
3175
|
+
emScanBtn.disabled = true; /* çift tık → 'döngü zaten çalışıyor' kilitlenmesin */
|
|
3176
|
+
const msg = q('#emMsg');
|
|
3177
|
+
if (msg) msg.textContent = '⏳';
|
|
3178
|
+
const r = await beast.empatiScan().catch(() => null);
|
|
3179
|
+
if (r && r.ok) {
|
|
3180
|
+
const txt = _t('em_scan_ok')
|
|
3181
|
+
.replace('{raw}', String(r.raw || 0))
|
|
3182
|
+
.replace('{queued}', String(r.queued || 0))
|
|
3183
|
+
.replace('{stored}', String(r.stored || 0));
|
|
3184
|
+
toast(txt);
|
|
3185
|
+
await renderEmpatiPane(); /* olay + hafıza listeleri tazelensin */
|
|
3186
|
+
const m2 = q('#emMsg');
|
|
3187
|
+
if (m2) m2.textContent = txt; /* yeniden çizim mesajı silmesin */
|
|
3188
|
+
} else {
|
|
3189
|
+
const err = (r && r.error) || 'hata';
|
|
3190
|
+
if (msg) msg.textContent = /zaten çalışıyor/.test(err) ? _t('em_scan_busy') : err;
|
|
3191
|
+
toast('Tarama hatası: ' + err);
|
|
3192
|
+
}
|
|
3193
|
+
const b2 = q('#emScan');
|
|
3194
|
+
if (b2) b2.disabled = false;
|
|
3195
|
+
});
|
|
3196
|
+
const emMemClearBtn = q('#emMemClear');
|
|
3197
|
+
if (emMemClearBtn) emMemClearBtn.addEventListener('click', async () => {
|
|
3198
|
+
const r = await beast.empatiMemClear().catch(() => null);
|
|
3199
|
+
toast(r && r.ok ? _t('em_mem_cleared') : 'Hata');
|
|
3200
|
+
if (r && r.ok) renderEmpatiPane();
|
|
3201
|
+
});
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3025
3204
|
async function renderWaAllow() {
|
|
3026
3205
|
const wrap = $('#waAllowChips');
|
|
3027
3206
|
if (!wrap) return;
|
|
@@ -4706,6 +4885,15 @@ function onEvent(ev) {
|
|
|
4706
4885
|
return;
|
|
4707
4886
|
}
|
|
4708
4887
|
if (ev.type === 'install-progress') { updateInstallPct(ev); return; }
|
|
4888
|
+
/* EMPATİ LOOP: proaktif bildirim (masaüstü) + sekme canlı yenileme */
|
|
4889
|
+
if (ev.type === 'proactive') {
|
|
4890
|
+
toast('🫡 ' + (ev.text || ev.title || ''));
|
|
4891
|
+
return;
|
|
4892
|
+
}
|
|
4893
|
+
if (ev.type === 'empati') {
|
|
4894
|
+
if (!els.settingsOverlay.hidden && setTab === 'empati') renderEmpatiPane();
|
|
4895
|
+
return;
|
|
4896
|
+
}
|
|
4709
4897
|
if (ev.type === 'update') {
|
|
4710
4898
|
if (ev.downloaded) toast(_t('up_downloaded') + ' (v' + (ev.version || '?') + ') — /update now');
|
|
4711
4899
|
else if (ev.available && ev.version && ev.version !== ev.current) toast(_t('up_available') + ' (v' + ev.version + ')');
|