beast-agent 2.7.0 → 2.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/engine.js +7 -0
- package/src/main.js +95 -26
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -17,6 +17,7 @@ const mem0 = require('./mem0');
|
|
|
17
17
|
const nightref = require('./nightref');
|
|
18
18
|
const skills = require('./skills');
|
|
19
19
|
const mcp = require('./mcp');
|
|
20
|
+
const apps = require('./apps');
|
|
20
21
|
const { estTokens, estMsgTokens } = require('./tokens');
|
|
21
22
|
const log = require('./logger');
|
|
22
23
|
|
|
@@ -3010,6 +3011,8 @@ class Engine {
|
|
|
3010
3011
|
(bağlı değilse lazy bağlanır; kapalıysa liste değişmez; Beast Code paneli hızlı
|
|
3011
3012
|
ilk-token sözü için MCP'siz kalır) */
|
|
3012
3013
|
if (!session || !session.bcCode) toolsList = await mcp.mergeTools(toolsList);
|
|
3014
|
+
/* Beast Apps: kurulu app'lerin araçları (app__<id>__<tool>) modele açılır */
|
|
3015
|
+
toolsList = apps.mergeTools(toolsList);
|
|
3013
3016
|
/* opencode agent.ts port: özel ajan tanımı — prompt/model/araç/steps */
|
|
3014
3017
|
const adef = this._agentDefFor(session, !!session.bgJob);
|
|
3015
3018
|
/* Beast Code: todo_write açıklaması "3+ adım" kısıtı içerir ve model küçük
|
|
@@ -4434,6 +4437,10 @@ const skills = require('./skills');
|
|
|
4434
4437
|
const r = await this.browser.act(name.slice(8), args, signal, { sessionId });
|
|
4435
4438
|
return JSON.stringify(r);
|
|
4436
4439
|
}
|
|
4440
|
+
/* Beast Apps: app__<id>__<tool> → apps host'a dispatch */
|
|
4441
|
+
if (String(name).startsWith('app__')) {
|
|
4442
|
+
return JSON.stringify(await apps.call(name, args || {}, signal));
|
|
4443
|
+
}
|
|
4437
4444
|
/* opencode tool registry portu: edit_file/grep/glob ana ajanın da araçları —
|
|
4438
4445
|
bunlar olmadan model write_file + tam dosya okuma döngüsüne düşer */
|
|
4439
4446
|
if (!(name === 'run_command' || name === 'read_file' || name === 'write_file' || name === 'edit_file' ||
|
package/src/main.js
CHANGED
|
@@ -234,6 +234,7 @@ let waSingleSid = null; // KANAL TEK OTURUMU: WhatsApp için BİR oturum — sil
|
|
|
234
234
|
let waHistory = new Map(); // jid -> [sid,...] bu sohbete ait tüm oturumlar
|
|
235
235
|
let waBcMode = new Set(); // jid -> BeastCode modu AKTİF (WhatsApp'tan uzaktan kodlama)
|
|
236
236
|
let waJidPn = new Map(); // jid -> gerçek telefon numarası (LID fallback için)
|
|
237
|
+
let waLastActiveJid = ''; // en son mesaj gelen WA sohbeti — kanal tek oturumunda cevap/dosya hedefi
|
|
237
238
|
const WA_HISTORY_CAP = 20;
|
|
238
239
|
let tg = null;
|
|
239
240
|
let tgChats = new Map(); // telegram chatId -> aktif session id (hepsi KANAL TEK OTURUMUNA bağlı)
|
|
@@ -1759,6 +1760,19 @@ function ensureWaSession(jid) {
|
|
|
1759
1760
|
return waSingleSid;
|
|
1760
1761
|
}
|
|
1761
1762
|
|
|
1763
|
+
/* Kanal tek oturumunda cevap/dosya hedefi: bu oturuma bağlı sohbetler arasında
|
|
1764
|
+
EN SON mesaj gelen önceliklidir (kullanıcı o pencerede bekliyor); o bilgi
|
|
1765
|
+
yoksa (restart vb.) ilk bağlı sohbet kullanılır. null = bağlı sohbet yok. */
|
|
1766
|
+
function waReplyJid(sid) {
|
|
1767
|
+
try {
|
|
1768
|
+
if (waLastActiveJid && waChats.get(waLastActiveJid) === sid) return waLastActiveJid;
|
|
1769
|
+
} catch {}
|
|
1770
|
+
for (const [j, s] of waChats) {
|
|
1771
|
+
if (s === sid) return j;
|
|
1772
|
+
}
|
|
1773
|
+
return null;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1762
1776
|
/* ---------- GRUP BAĞLAM AKIŞI (mentionOnly + seeAll) ----------
|
|
1763
1777
|
Bot grubun tüm konuşmasını okur ama CEVAP ÜRETMEZ; mesajlar sessizce
|
|
1764
1778
|
oturum geçmişine bağlam olarak düşer. @mention gelince bot tüm bu
|
|
@@ -2311,6 +2325,7 @@ async function processWaMessage(jid, payload, senderNum, requeues = 0) {
|
|
|
2311
2325
|
}
|
|
2312
2326
|
/* KANAL TEK OTURUMU: eskisi silinmedikçe hep aynı oturumdan devam */
|
|
2313
2327
|
sid = ensureWaSession(jid);
|
|
2328
|
+
waLastActiveJid = String(jid); // cevap/dosya hedefi: en son yazan sohbet
|
|
2314
2329
|
// Cevap verilecek — karşı telefonda "yazıyor…" göstergesi (medya işlenene dek sürer)
|
|
2315
2330
|
wa.setComposing(jid, true);
|
|
2316
2331
|
// Kişi bazlı granül izin: all/web/read/chat
|
|
@@ -2998,13 +3013,13 @@ function reloadBackend() {
|
|
|
2998
3013
|
Spam olmasın: sohbet başına en az 7 sn'de bir tek satır. */
|
|
2999
3014
|
if (ev.type === 'tool-start' && wa && wa.connected) {
|
|
3000
3015
|
try {
|
|
3001
|
-
const
|
|
3002
|
-
if (
|
|
3016
|
+
const pingJid = waReplyJid(ev.sessionId);
|
|
3017
|
+
if (pingJid) {
|
|
3003
3018
|
const now = Date.now();
|
|
3004
3019
|
const last = lastWaToolPing.get(ev.sessionId) || 0;
|
|
3005
3020
|
if (now - last >= 7000) {
|
|
3006
3021
|
lastWaToolPing.set(ev.sessionId, now);
|
|
3007
|
-
sendWaSafe(
|
|
3022
|
+
sendWaSafe(pingJid, '\u203A ' + String(waToolLine(ev.name, ev.args || {})).replace(/^\u203A /, '')).catch(() => {});
|
|
3008
3023
|
}
|
|
3009
3024
|
}
|
|
3010
3025
|
} catch {}
|
|
@@ -3017,8 +3032,8 @@ function reloadBackend() {
|
|
|
3017
3032
|
const isInterim = Array.isArray(ev.message.tool_calls) && ev.message.tool_calls.length > 0;
|
|
3018
3033
|
if (mtxt && isInterim) {
|
|
3019
3034
|
try {
|
|
3020
|
-
const
|
|
3021
|
-
if (
|
|
3035
|
+
const interJid = waReplyJid(ev.sessionId);
|
|
3036
|
+
if (interJid) sendWaSafe(interJid, mtxt).catch(() => {});
|
|
3022
3037
|
} catch {}
|
|
3023
3038
|
}
|
|
3024
3039
|
}
|
|
@@ -3042,9 +3057,8 @@ function reloadBackend() {
|
|
|
3042
3057
|
}
|
|
3043
3058
|
// WhatsApp oturumlarının son cevabını geri gönder (metin + opsiyonel ses)
|
|
3044
3059
|
if ((ev.type === 'done' || ev.type === 'error') && wa && wa.connected) {
|
|
3045
|
-
const
|
|
3046
|
-
if (
|
|
3047
|
-
const wajid = hit[0];
|
|
3060
|
+
const wajid = waReplyJid(ev.sessionId);
|
|
3061
|
+
if (wajid) {
|
|
3048
3062
|
(async () => {
|
|
3049
3063
|
try {
|
|
3050
3064
|
if (ev.type === 'error') {
|
|
@@ -3762,8 +3776,9 @@ function setBrowserOpen(v, forceVisible) {
|
|
|
3762
3776
|
// AÇMA — görünürlük: forceVisible true/false ise onu uygula;
|
|
3763
3777
|
// belirtilmemişse kullanıcı tercihi belirler. Tarayıcı gizleme ÖZELLİĞİ
|
|
3764
3778
|
// (settings.browserHide) kapalıysa tercih ne olursa olsun tarayıcı GÖRÜNÜR açılır.
|
|
3765
|
-
//
|
|
3766
|
-
//
|
|
3779
|
+
// AJAN gezinmeleri setBrowserOpenForAgent üzerinden gelir: gizleme özelliği
|
|
3780
|
+
// kapalıysa görünür, özellik + göz kapalıyken gizli çalışır (kullanıcı ekranı
|
|
3781
|
+
// rahatsız edilmez; tarayıcı düğmesi gizli paneli görünür kılar).
|
|
3767
3782
|
browser.open = true;
|
|
3768
3783
|
browser.visible =
|
|
3769
3784
|
forceVisible === true ? true : forceVisible === false ? false : !browserHeadlessPref();
|
|
@@ -3796,13 +3811,15 @@ function browserGate(job) {
|
|
|
3796
3811
|
return p;
|
|
3797
3812
|
}
|
|
3798
3813
|
/* AJAN TARAYICI STRATEJİSİ — TÜM oturumlar (ana sohbet + WhatsApp botları + paralel ajanlar):
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3814
|
+
GİZLEME ÖZELLİĞİ (Ayarlar → Web Arama) KAPALIYSA ajan tarayıcıyı GİZLİ açamaz —
|
|
3815
|
+
dahili panel GÖRÜNÜR açılır, kullanıcı aramayı canlı izler. Özellik açıksa göz
|
|
3816
|
+
durumuna bakılır: göz açık (headless tercihi kapalı) → görünür, göz kapalı →
|
|
3817
|
+
gizli. Mobil önizleme varsa siluet canlı kalsın diye her koşulda görünür.
|
|
3818
|
+
Zaten açıksa (kullanıcı paneli açık tutuyorsa) görünürlüğe dokunulmaz. */
|
|
3803
3819
|
function setBrowserOpenForAgent() {
|
|
3804
|
-
|
|
3805
|
-
|
|
3820
|
+
if (!browser.open) {
|
|
3821
|
+
setBrowserOpen(true, !browserHideEnabled() || !browserHeadlessPref() || browser.mobile);
|
|
3822
|
+
}
|
|
3806
3823
|
}
|
|
3807
3824
|
|
|
3808
3825
|
async function browserNavigate(raw, signal, ctx) {
|
|
@@ -6761,7 +6778,8 @@ ipcMain.handle('cron:runNow', (_e, id) => {
|
|
|
6761
6778
|
/* ---------------- tarayıcı IPC ---------------- */
|
|
6762
6779
|
ipcMain.handle('browser:toggle', () => {
|
|
6763
6780
|
/* gizli çalışan ajan paneli varsa düğme ONU GÖRÜNÜR yapar; değilse aç/kapa.
|
|
6764
|
-
(
|
|
6781
|
+
(ajan gizleme özelliği + göz kapalıyken gizli çalışır — kullanıcı izlemek
|
|
6782
|
+
isterse buradan gösterir) */
|
|
6765
6783
|
if (browser.open && !browser.visible) {
|
|
6766
6784
|
setBrowserOpen(true, true);
|
|
6767
6785
|
return { open: browser.open, visible: browser.visible };
|
|
@@ -8876,8 +8894,42 @@ ipcMain.handle('screen:capture', async () => {
|
|
|
8876
8894
|
});
|
|
8877
8895
|
|
|
8878
8896
|
/* #26 ajanın send_file aracı: dosyayı doğru kanala (WA veya chat) ulaştırır.
|
|
8879
|
-
Paralel ajan işiyse parent sohbete, masaüstünde dosya kartı olarak düşer.
|
|
8897
|
+
Paralel ajan işiyse parent sohbete, masaüstünde dosya kartı olarak düşer.
|
|
8898
|
+
WhatsApp hedefinde resim/video/ses → medya mesajı, PDF/belge → doğru
|
|
8899
|
+
mimetype ile belge mesajı olarak gider; LID adresi patlarsa gerçek numaraya
|
|
8900
|
+
(@s.whatsapp.net) tek kez düşülür, yine başarısızsa ajan'a HATA döner. */
|
|
8880
8901
|
const FILE_IMAGE_EXT = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif']);
|
|
8902
|
+
const FILE_MIME = {
|
|
8903
|
+
'.pdf': 'application/pdf',
|
|
8904
|
+
'.doc': 'application/msword',
|
|
8905
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
8906
|
+
'.xls': 'application/vnd.ms-excel',
|
|
8907
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
8908
|
+
'.ppt': 'application/vnd.ms-powerpoint',
|
|
8909
|
+
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
8910
|
+
'.txt': 'text/plain',
|
|
8911
|
+
'.csv': 'text/csv',
|
|
8912
|
+
'.md': 'text/markdown',
|
|
8913
|
+
'.json': 'application/json',
|
|
8914
|
+
'.html': 'text/html',
|
|
8915
|
+
'.zip': 'application/zip',
|
|
8916
|
+
'.rar': 'application/vnd.rar',
|
|
8917
|
+
'.7z': 'application/x-7z-compressed',
|
|
8918
|
+
'.mp3': 'audio/mpeg',
|
|
8919
|
+
'.m4a': 'audio/mp4',
|
|
8920
|
+
'.wav': 'audio/wav',
|
|
8921
|
+
'.ogg': 'audio/ogg',
|
|
8922
|
+
'.opus': 'audio/ogg; codecs=opus',
|
|
8923
|
+
'.mp4': 'video/mp4',
|
|
8924
|
+
'.mkv': 'video/x-matroska',
|
|
8925
|
+
'.webm': 'video/webm',
|
|
8926
|
+
'.svg': 'image/svg+xml',
|
|
8927
|
+
'.apk': 'application/vnd.android.package-archive',
|
|
8928
|
+
};
|
|
8929
|
+
function mimeForFile(ext) {
|
|
8930
|
+
return FILE_MIME[String(ext || '').toLowerCase()] || 'application/octet-stream';
|
|
8931
|
+
}
|
|
8932
|
+
|
|
8881
8933
|
async function deliverFile(sessionId, filePath, caption) {
|
|
8882
8934
|
try {
|
|
8883
8935
|
const abs = path.isAbsolute(filePath) ? filePath : path.join(engine.workspace || process.cwd(), filePath);
|
|
@@ -8889,17 +8941,34 @@ async function deliverFile(sessionId, filePath, caption) {
|
|
|
8889
8941
|
const job = engine.listBgJobs().find((j) => j.id === sid);
|
|
8890
8942
|
if (job && job.parentId) sid = job.parentId; // paralel ajan → parent sohbet
|
|
8891
8943
|
} catch {}
|
|
8892
|
-
/* WhatsApp hedefi: bu oturum bir WA sohbetine bağlıysa oraya gönder
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
if (s === sid) { jid = j; break; }
|
|
8896
|
-
}
|
|
8944
|
+
/* WhatsApp hedefi: bu oturum bir WA sohbetine bağlıysa oraya gönder.
|
|
8945
|
+
Birden çok sohbet aynı oturuma bağlıysa EN SON yazan kazanır. */
|
|
8946
|
+
const jid = waReplyJid(sid);
|
|
8897
8947
|
if (jid && wa) {
|
|
8898
8948
|
const buf = fs.readFileSync(abs);
|
|
8899
|
-
const
|
|
8900
|
-
|
|
8901
|
-
|
|
8949
|
+
const sendMedia = async (target) =>
|
|
8950
|
+
FILE_IMAGE_EXT.has(ext)
|
|
8951
|
+
? await wa.sendImage(target, buf, caption || name)
|
|
8952
|
+
: await wa.sendFile(target, buf, name, caption, mimeForFile(ext));
|
|
8953
|
+
let ok = false;
|
|
8954
|
+
try { ok = !!(await sendMedia(jid)); } catch {}
|
|
8955
|
+
if (!ok) {
|
|
8956
|
+
/* LID/adres fallback: bilinen gerçek numaraya (@s.whatsapp.net) dene */
|
|
8957
|
+
const pn = waJidPn.get(jid);
|
|
8958
|
+
const pnJid = pn ? `${pn}@s.whatsapp.net` : '';
|
|
8959
|
+
if (pnJid && pnJid !== jid) {
|
|
8960
|
+
try { ok = !!(await sendMedia(pnJid)); } catch {}
|
|
8961
|
+
if (ok) waLog(`dosya fallback → ${waPrettyJid(pnJid)} (lid/pn denemelerinden sonra)`);
|
|
8962
|
+
}
|
|
8963
|
+
}
|
|
8902
8964
|
if (ok) return { ok: true, channel: 'whatsapp', name };
|
|
8965
|
+
/* WA hedefi var ama gönderim başarısız — sessizce "masaüstüne düştü"
|
|
8966
|
+
SANMA: ajan durumu bilsin, kullanıcıya söyleyip tekrar deneyebilsin */
|
|
8967
|
+
waLog(`dosya gönderim HATA → ${waPrettyJid(jid)} "${name}"`);
|
|
8968
|
+
return {
|
|
8969
|
+
ok: false,
|
|
8970
|
+
error: `whatsapp gönderimi başarısız: ${name} — bağlantı/medya hatası; biraz sonra tekrar dene ya da kullanıcıya söyle`,
|
|
8971
|
+
};
|
|
8903
8972
|
}
|
|
8904
8973
|
/* masaüstü: sohbete dosya kartı bas */
|
|
8905
8974
|
if (win && !win.isDestroyed()) {
|