beast-agent 2.44.4 → 2.44.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 +1 -1
- package/src/agent/engine.js +178 -24
- package/src/agent/watext.js +1 -0
- package/src/main.js +141 -0
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -193,6 +193,7 @@ const CEO_EXEC_TOOLS = new Set([
|
|
|
193
193
|
'browser_click', 'browser_type', 'browser_press', 'browser_scroll', 'browser_select',
|
|
194
194
|
'computer_look', 'computer_act',
|
|
195
195
|
'ocr_read',
|
|
196
|
+
'channel_send', // dış kişilere mesaj: CEO devreder, kendisi atmaz
|
|
196
197
|
'delegate_task', // senkron bekler — CEO hep asenkron run_background kullanır
|
|
197
198
|
]);
|
|
198
199
|
|
|
@@ -343,6 +344,7 @@ class Engine {
|
|
|
343
344
|
this.historyTokenBudget = Number(opts.historyTokenBudget) || HISTORY_TOKEN_BUDGET;
|
|
344
345
|
this.browser = opts.browser || null; // dahili tarayıcı kancaları
|
|
345
346
|
this.fileSend = opts.fileSend || null; // #26 dosya gönderim köprüsü (chat/WA)
|
|
347
|
+
this.channelSend = opts.channelSend || null; // kanal ilk mesaj köprüsü (WA/TG/DC)
|
|
346
348
|
this.notifyOwnerFail = opts.notifyOwnerFail !== false; // #25 hata mail bildirimi (runtime /notify)
|
|
347
349
|
/* ENTEGRASYON BİLDİRİMİ: tool yazım/doğrulama adımları WA/TG/Discord'a düşer */
|
|
348
350
|
this.integrationNotify = typeof opts.integrationNotify === 'function' ? opts.integrationNotify : null;
|
|
@@ -392,6 +394,11 @@ class Engine {
|
|
|
392
394
|
(AJAN DM paneli buradan beslenir; restart sonrası da durur) */
|
|
393
395
|
this._agentDms = [];
|
|
394
396
|
this._agentDmsFile = path.join(this.sessionsDir, 'agent-dms.json');
|
|
397
|
+
/* TOOL İSTEKLERİ: tool_request artık SENKRON BEKLEMEZ — istek Tool botunun
|
|
398
|
+
KENDİ oturumunda arka planda koşar; bitince rapor isteyene düşer.
|
|
399
|
+
toolSid -> [{id, requesterSid, requesterTitle, task, before, at}] */
|
|
400
|
+
this._toolPending = new Map();
|
|
401
|
+
this._toolSessionFile = path.join(this.sessionsDir, 'tool-session.json');
|
|
395
402
|
/* AJAN DM GRUPLARI: ajanların agent_dm ile kurduğu grup sohbetleri
|
|
396
403
|
(id → {id,title,members,titles,createdAt,closed,closedAt}) */
|
|
397
404
|
this._agentGroups = new Map();
|
|
@@ -2002,7 +2009,7 @@ class Engine {
|
|
|
2002
2009
|
'- Grafikte gezinme/ölçek: computer_act ile timeframe, scroll, zoom (arka planda çalışır — kullanıcının faresini çalmaz).\n' +
|
|
2003
2010
|
'- EKİPLE GÖRSEL PAYLAŞIM: chart PNG\'sini agent_dm image:true (son görüntün) ya da image:"<dosya yolu>" (ör. tool__mt5_shot dönen path) ile trader/ekibe gönder — karşı ajan görüntüyü GERÇEKTEN görür, AJAN DM panelinde açılır. Ortak kararlarda grafiği paylaşmak zorunlu disiplindir.\n' +
|
|
2004
2011
|
'- PARALEL EKİP AKIŞI: teknik ajan seviyeleri hesaplar → mt5_ea note ile grafiğe çizer; görsel ajan tool__mt5_shot ile çekip TEYİT eder ve agent_dm ile görseli trader\'a atar; risk ajanı aynı görsel üzerinden SL/TP yerleşimini denetler; trader kararı işleme çevirir. Aynı sembolde koşan ajanlar tek DM grubunda (agent_dm group) toplanır — çelişen hareket etme.\n' +
|
|
2005
|
-
'- EKSİK ARAÇ: özel grafik aracı gerekiyorsa (ör. çoklu timeframe tek görüntü, indikatör paneli okuma) VAR SAYMA — tool_request ile TOOL botuna yazdır (doğrulanınca tool__<ad> olarak ANINDA çağrılır); küçük aracı skill("tool-yazma") prosedürüyle kendin de yazabilirsin.\n' +
|
|
2012
|
+
'- EKSİK ARAÇ: özel grafik aracı gerekiyorsa (ör. çoklu timeframe tek görüntü, indikatör paneli okuma) VAR SAYMA — tool_request ile TOOL botuna yazdır (asenkron çalışır; rapor sohbete düşer, araç doğrulanınca tool__<ad> olarak ANINDA çağrılır); küçük aracı skill("tool-yazma") prosedürüyle kendin de yazabilirsin.\n' +
|
|
2006
2013
|
'VERİ AKIŞI (her değerlendirmede): mt5_account + mt5_positions + mt5_market çağrılarını AYNI turda PARALEL ver; gerekiyorsa mt5_history ile son işlemleri gör.\n' +
|
|
2007
2014
|
'PARALEL + KOORDİNASYON: uzun araştırma/işleri run_background ile paralel finance işçisine devret (parent finance olduğu için işçi mt5 okuma araçlarını görür); koşan ajanlarla konuşmak için agent_dm (to: ajan başlığındaki anahtar kelime, örn "GOLD"; ortak karar için group: "İSİM" ile grup sohbeti kur — mesaj tüm üyelere düşer). Görevin bitince DM/grup sohbetleri otomatik KAPANIR (geçmiş panelde kalır).\n' +
|
|
2008
2015
|
(symbols ? `İZLEME LİSTESİ: ${symbols}\n` : '') +
|
|
@@ -2119,9 +2126,10 @@ class Engine {
|
|
|
2119
2126
|
'Basit soruları araç kullanmadan doğrudan cevapla — hız önceliklidir.',
|
|
2120
2127
|
'Kod/dosya işlerinde: içerik araması grep (regex), dosya adı araması glob, VAR OLAN dosyayı değiştirme edit_file (write_file yalnız yeni dosya/tam yeniden yazım). read_file satır numaralı döner; büyük dosyada devamını offset parametresiyle oku, ASLA baştan okuma; bir dosyayı aynı oturumda BİR KEZ okumak yeter — okunan içerik oturum sonuna kadar bağlamda kalır, dosyayı tekrar okuma. Dosya işlemlerinde özel araçları kullan (edit_file/write_file/read_file/grep/glob); run_command terminal işlerindir (build, git, kurulum, paket) — dosya düzenlemeyi komutla değil edit_file ile yap. edit_file/write_file sonucu additions/deletions döner ve değişiklik diske ANINDA uygulanır — doğrulamak için dosyayı TEKRAR OKUMA; önceki okuduğun içerik + kendi değişikliklerin üzerinden devam et.',
|
|
2121
2128
|
'Kullanıcı bir tarihte/saatte hatırlatılmasını isterse set_reminder kullan; when değerini ORTAMdaki bugüne göre hesapla (yerel saat). "Her sabah/gün/hafta" gibi tekrarlı isteklerde repeat alanını da ver (daily/weekly/monthly/weekdays veya cron).',
|
|
2129
|
+
'İLK MESAJ (channel_send): kullanıcı "X kişiye yaz / haber ver / duyur" derse channel_send ile SEN ilk mesajı at — karşıdan mesaj gelmesini BEKLEME. Yalnız allow listteki kişiler hedeflenebilir: isim (kısmi), telefon numarası, Telegram/Discord ID, @kullanıcı adı, "sahip" ya da "all". Kişi listede yoksa gönderme, kullanıcıya söyle. Telegram/Discord\'da karşı taraf bota daha önce hiç yazmadıysa gönderim başarısız olabilir; sonucu dürüstçe bildir.',
|
|
2122
2130
|
'Kullanıcı kalıcı bir arka plan takibi isterse (fiyat eşiği, pil seviyesi, sayfa değişikliği) watcher_add ile izleyici kur; kurduktan sonra watcher_list ile doğrula ve kullanıcıya koşulu + kontrol sıklığını kısaca bildir.',
|
|
2123
2131
|
'Anlık olay takipleri için (yeni mail, fiyat eşiği, dosya değişimi, webhook) event_subscribe kullan — cron/polling gerekmez; listeyi event_list ile göster, vazgeçirirse event_unsubscribe.',
|
|
2124
|
-
'ARAÇ EKSİKSE ÇEKİNME (CEPHANE): ihtiyacın olan bir araç yoksa/bozuksa tool_request ile TOOL botuna yazdır — tool yazımı onun TEK işidir; task alanına araç adı + ne yapacağı + girdi/çıktı sözleşmesini, context alanına örnek veri/yol/endpoint yaz. Araç doğrulanınca tool__<ad> olarak ANINDA çağrılabilir. Küçük kişisel araçları skill("tool-yazma") prosedürüyle kendin de yazabilirsin — yazdığını run_command ile MUTLAKA doğrula.',
|
|
2132
|
+
'ARAÇ EKSİKSE ÇEKİNME (CEPHANE): ihtiyacın olan bir araç yoksa/bozuksa tool_request ile TOOL botuna yazdır — tool yazımı onun TEK işidir; task alanına araç adı + ne yapacağı + girdi/çıktı sözleşmesini, context alanına örnek veri/yol/endpoint yaz. ASENKRON: istek anında kabul edilir, Tool botu kendi oturumunda arka planda yazar; sen BEKLEME, işine devam et. Araç doğrulanınca tool__<ad> olarak ANINDA çağrılabilir; rapor bitince sohbete otomatik düşer. Küçük kişisel araçları skill("tool-yazma") prosedürüyle kendin de yazabilirsin — yazdığını run_command ile MUTLAKA doğrula.',
|
|
2125
2133
|
'LOG ZEKASI: "hata var mı / ne oldu / neden çalışmadı" sorularında ya da bir iş beklenmedik bittiğinde/hata verdiğini fark ettiğinde log_analyze ile logları tara — top desenleri okuyup en olası KÖK NEDENİ tek cümlede söyle, somut çözüm öner; aynı desen 3+ tekrarlıysa bunu vurgula. Sürekli log gözetimi istenirse watcher_add ile kind:"logs" kur (örn "hata artarsa bağır" → level:"error", windowMin:10, op:"gt", value:2) ve koşulu kısaca bildir.',
|
|
2126
2134
|
'Kullanıcı "artık hep böyle yap / bunu unutma" tarzı kalıcı talimat verirse kural olarak kaydet: sohbette /rule <metin> kullanmasını söyle VEYA kullanıcı isterse event_subscribe ile olaya bağlan (mail/fiyat/dosya/webhook).',
|
|
2127
2135
|
'Kullanıcının mesajında 2+ ayrı iş/hedef varsa (örn "X yap ve sonra Y\u2019i kontrol et") KODLAMAYA/İŞE BAŞLAMADAN önce todo_write ile plan çıkar ve sırayla yürüt; her adımı tamamlarken güncelle. LİSTE DİSİPLİNİ: her adım bittiği AN status:"done" yap; son cevabını vermeden önce tüm maddeler done olmalı — yapılmayacaksa listeden düş. Listeyi yarım bırakma.',
|
|
@@ -3705,7 +3713,7 @@ class Engine {
|
|
|
3705
3713
|
`DOSYA KURALI: dosya işlemlerinde özel araçları kullan — VAR OLAN dosyayı edit_file ile düzenle, yeniyi write_file ile yaz, okuma/arama read_file/grep/glob; run_command terminal işlerindir (build, git, kurulum). Bir dosyayı BİR KEZ oku — içerik bağlamda kalır, tekrar okuma.\n` +
|
|
3706
3714
|
(proj ? `PROJE TALİMATLARI (workspace AGENTS/CLAUDE/CONTEXT — daima uy):\n${proj}\n` : '') +
|
|
3707
3715
|
`HIZ KURALLARI:\n` +
|
|
3708
|
-
`- TOOL İSTEĞİ (CEPHANE): ihtiyacın olan araç yoksa/bozuksa tool_request ile TOOL botuna yazdır (araç doğrulanınca tool__<ad> olarak anında çağrılır); küçük aracı skill("tool-yazma") ile kendin de yaz ve run_command ile doğrula.\n` +
|
|
3716
|
+
`- TOOL İSTEĞİ (CEPHANE): ihtiyacın olan araç yoksa/bozuksa tool_request ile TOOL botuna yazdır (asenkron: istek anında döner, Tool botu arka planda yazar; araç doğrulanınca tool__<ad> olarak anında çağrılır, rapor sohbete düşer); küçük aracı skill("tool-yazma") ile kendin de yaz ve run_command ile doğrula.\n` +
|
|
3709
3717
|
`- Döngülü işleri (çok URL/dosya/sayfa, tekrarlı parse-hesap) TEK python_run betiğinde topluca bitir.\n` +
|
|
3710
3718
|
`- Web için web_search kullan (zincir dahili tarayıcıyla başlar — gerçek Chromium ile Google); tek aramada bulunamazsa veya çok kaynaklı derin araştırma gerekiyorsa deep_search kullan (çoklu sorgu paralel + gizli tarayıcıda tam sayfa okuma). Sayfa açma/göstermenin VARSAYILANI DAHİLİ tarayıcıdır: browser_open → browser_snapshot → browser_click/type/read. Kullanıcı açıkça dış tarayıcı (chrome/firefox/başka/normal/kendi tarayıcım) istediyse run_command ile \`start "" <url>\` çalıştır. Görseli göremiyorsan metni ocr_read ile oku (source:"browser").\n` +
|
|
3711
3719
|
`- Bağımsız araç çağrılarını aynı turda PARALEL ver.\n` +
|
|
@@ -4400,6 +4408,8 @@ class Engine {
|
|
|
4400
4408
|
}
|
|
4401
4409
|
} finally {
|
|
4402
4410
|
this.ctrls.delete(sid);
|
|
4411
|
+
/* Tool botu turu bittiyse bekleyen tool isteklerinin raporu dağıtılır */
|
|
4412
|
+
try { this._toolRequestFinished(sid); } catch {}
|
|
4403
4413
|
emit({ type: 'status', status: 'idle' });
|
|
4404
4414
|
/* bekleyen paralel-ajan raporları HER bitiş yolunda boşaltılır —
|
|
4405
4415
|
hata/durdurma/tur-limiti sonrası bile sonuç raporu kullanıcıya ulaşır.
|
|
@@ -4769,8 +4779,10 @@ const skills = require('./skills');
|
|
|
4769
4779
|
}
|
|
4770
4780
|
|
|
4771
4781
|
/* TOOL İSTEĞİ (cephane): her oturum/bot eksik aracı TOOL botuna yazdırır.
|
|
4772
|
-
Admin şartı YOK
|
|
4773
|
-
|
|
4782
|
+
Admin şartı YOK. ASENKRON akış: istek Tool botunun KENDİ oturumunda
|
|
4783
|
+
arka planda koşar (timeout yok), AJAN DM paneline düşer; rapor bitince
|
|
4784
|
+
isteyen oturuma otomatik iletilir. İstek anında döner — isteyen ajan
|
|
4785
|
+
beklemeden işine devam eder, araç hazır olunca tool__<ad> çağrılır. */
|
|
4774
4786
|
async _toolRequest(args, sessionId, signal) {
|
|
4775
4787
|
const bots = require('./bots');
|
|
4776
4788
|
const task = String((args && args.task) || '').slice(0, 4000);
|
|
@@ -4789,32 +4801,144 @@ const skills = require('./skills');
|
|
|
4789
4801
|
return (b && b.name) || (session && session.bgTitle) || 'Beast';
|
|
4790
4802
|
} catch { return 'Beast'; }
|
|
4791
4803
|
})();
|
|
4792
|
-
const
|
|
4793
|
-
|
|
4804
|
+
const requestId = 'tr' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
|
4805
|
+
/* TOOL BOTUNUN KENDİ OTURUMU: yoksa açılır — botta kalıcı oturum oluşur */
|
|
4806
|
+
const toolSid = this._ensureToolBotSession(target);
|
|
4807
|
+
const text =
|
|
4808
|
+
`[TOOL İSTEĞİ — ${requestId} — gönderen: ${who}]\n` + task + '\n' +
|
|
4794
4809
|
(context ? `\nBAĞLAM (kod/yol/örnek veri/endpoint):\n` + context + '\n' : '') +
|
|
4795
4810
|
`\nHatırlatma: skill("tool-yazma") oku; %APPDATA%\\beast\\tools\\<slug>\\ içine tool.json + run.js yaz; run_command ile TEST DÖNGÜSÜNÜ işlet (çıktı {ok:true} olana kadar hata oku → edit_file ile düzelt → yeniden test; en fazla 5 deneme). Ancak test geçince yayınla; geçmeyen aracı yayınlama. Raporunda adımları yaz: yazıldı → test sonucu → (varsa) düzeltmeler → YAYINDA/BAŞARISIZ + tek satır çağrı örneği.`;
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4811
|
+
/* AJAN DM paneline istek kaydı — kullanıcı Tool botu ile konuşmayı canlı görür */
|
|
4812
|
+
const dm = {
|
|
4813
|
+
at: new Date().toISOString(),
|
|
4814
|
+
from: String(sessionId || 'beast'),
|
|
4815
|
+
fromTitle: who,
|
|
4816
|
+
to: toolSid,
|
|
4817
|
+
toTitle: target.name || 'Tool botu',
|
|
4818
|
+
topic: 'tool isteği',
|
|
4819
|
+
text: task,
|
|
4800
4820
|
};
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4821
|
+
try {
|
|
4822
|
+
this._pushAgentDm(dm);
|
|
4823
|
+
this._persistAgentDms();
|
|
4824
|
+
emitSafe(this, toolSid, { type: 'agent-dm', ...dm });
|
|
4825
|
+
} catch {}
|
|
4826
|
+
const toolSess = this.cache.get(toolSid) || this._load(toolSid);
|
|
4827
|
+
const before = toolSess.messages.length;
|
|
4828
|
+
/* bekleyen kaydı send'DEN ÖNCE düş — tur senkron bitebilir (ör. model
|
|
4829
|
+
yok); _run finally anında raporu dağıtır, kayıt kaçmaz */
|
|
4830
|
+
const list = this._toolPending.get(toolSid) || [];
|
|
4831
|
+
list.push({
|
|
4832
|
+
id: requestId,
|
|
4833
|
+
requesterSid: String(sessionId || ''),
|
|
4834
|
+
requesterTitle: who,
|
|
4835
|
+
responderTitle: target.name || 'Tool botu',
|
|
4836
|
+
task: task.slice(0, 300),
|
|
4837
|
+
before,
|
|
4838
|
+
at: Date.now(),
|
|
4839
|
+
});
|
|
4840
|
+
this._toolPending.set(toolSid, list);
|
|
4841
|
+
const sent = this.send(toolSid, { text });
|
|
4842
|
+
if (!sent) {
|
|
4843
|
+
this._toolPending.set(toolSid, list.filter((x) => x.id !== requestId));
|
|
4844
|
+
return { ok: false, error: 'Tool botu oturumu başlatılamadı (durdurulmuş olabilir)' };
|
|
4845
|
+
}
|
|
4846
|
+
if (!this.ctrls.has(toolSid)) {
|
|
4847
|
+
let s2 = this.cache.get(toolSid) || this._load(toolSid);
|
|
4848
|
+
if (s2.messages.length === before) this._toolRequestFinished(toolSid);
|
|
4849
|
+
}
|
|
4810
4850
|
return {
|
|
4811
4851
|
ok: true,
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4852
|
+
status: 'istek iletildi',
|
|
4853
|
+
tool_bot: target.name || 'Tool botu',
|
|
4854
|
+
request_id: requestId,
|
|
4855
|
+
session_id: 'bot:' + String(target.id || 'tool'),
|
|
4856
|
+
note: 'Tool botu ARKA PLANDA yazıyor; sen beklemeden işine devam et. Araç hazır olunca tool__<ad> olarak ANINDA çağrılır; rapor bitince bu sohbete otomatik düşer. Aynı araç için tekrar istek atma.',
|
|
4815
4857
|
};
|
|
4816
4858
|
}
|
|
4817
4859
|
|
|
4860
|
+
/* Tool botunun KALICI kendi oturumu (yoksa oluşturur) — istekler burada
|
|
4861
|
+
birikir, kullanıcı Tool botu sohbetinden tüm geçmişi görebilir. */
|
|
4862
|
+
_ensureToolBotSession(target) {
|
|
4863
|
+
const want = String((target && target.id) || 'tool');
|
|
4864
|
+
try {
|
|
4865
|
+
const rec = JSON.parse(fs.readFileSync(this._toolSessionFile, 'utf8'));
|
|
4866
|
+
const sid = String((rec && rec.id) || '');
|
|
4867
|
+
if (sid && fs.existsSync(this._file(sid))) {
|
|
4868
|
+
const s = this._load(sid);
|
|
4869
|
+
if (s && !s.isBotDm) {
|
|
4870
|
+
if (String(s.botId || '') !== want) this.setSessionBot(sid, want);
|
|
4871
|
+
return sid;
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
} catch {}
|
|
4875
|
+
const created = this.createSession();
|
|
4876
|
+
const sid = String(created.id);
|
|
4877
|
+
this.setSessionBot(sid, want);
|
|
4878
|
+
this.setSessionPerm(sid, target.perm || 'all');
|
|
4879
|
+
try {
|
|
4880
|
+
fs.writeFileSync(this._toolSessionFile, JSON.stringify({ id: sid, botId: want, at: nowIso() }));
|
|
4881
|
+
} catch {}
|
|
4882
|
+
return sid;
|
|
4883
|
+
}
|
|
4884
|
+
|
|
4885
|
+
/* Tool botunun turu bitti: bekleyen isteklerin raporunu AJAN DM paneline
|
|
4886
|
+
ve isteyen oturuma düşür. _run finally'den çağrılır (tüm bitiş yolları). */
|
|
4887
|
+
_toolRequestFinished(sid) {
|
|
4888
|
+
try {
|
|
4889
|
+
const key = String(sid || '');
|
|
4890
|
+
if (!this._toolPending || !this._toolPending.size) return;
|
|
4891
|
+
const list = this._toolPending.get(key);
|
|
4892
|
+
if (!list || !list.length) return;
|
|
4893
|
+
this._toolPending.delete(key);
|
|
4894
|
+
const s = this.cache.get(key) || this._load(key);
|
|
4895
|
+
const reply = (() => {
|
|
4896
|
+
for (let i = s.messages.length - 1; i >= 0; i--) {
|
|
4897
|
+
const m = s.messages[i];
|
|
4898
|
+
if (m && m.role === 'assistant' && m.content && !(Array.isArray(m.tool_calls) && m.tool_calls.length)) {
|
|
4899
|
+
const txt = typeof m.content === 'string' ? m.content : '';
|
|
4900
|
+
if (txt.trim()) return stripAiDashes(txt.trim());
|
|
4901
|
+
}
|
|
4902
|
+
}
|
|
4903
|
+
return '';
|
|
4904
|
+
})();
|
|
4905
|
+
for (const req of list) {
|
|
4906
|
+
const body = reply
|
|
4907
|
+
? `[TOOL BOTU RAPORU — ${req.id}]\n${reply}`
|
|
4908
|
+
: `[TOOL BOTU] ${req.id} numaralı istek tamamlanamadı (tur yarıda kesildi ya da metin rapor üretilemedi). Tool botu sohbetinden ayrıntıya bakabilirsin.`;
|
|
4909
|
+
const dm = {
|
|
4910
|
+
at: new Date().toISOString(),
|
|
4911
|
+
from: key,
|
|
4912
|
+
fromTitle: req.responderTitle || 'Tool botu',
|
|
4913
|
+
to: req.requesterSid || '',
|
|
4914
|
+
toTitle: req.requesterTitle || 'Beast',
|
|
4915
|
+
topic: 'tool isteği',
|
|
4916
|
+
text: body,
|
|
4917
|
+
};
|
|
4918
|
+
try {
|
|
4919
|
+
this._pushAgentDm(dm);
|
|
4920
|
+
this._persistAgentDms();
|
|
4921
|
+
emitSafe(this, req.requesterSid || key, { type: 'agent-dm', ...dm });
|
|
4922
|
+
} catch {}
|
|
4923
|
+
if (req.requesterSid) {
|
|
4924
|
+
try {
|
|
4925
|
+
if (this.ctrls.has(String(req.requesterSid))) this.send(String(req.requesterSid), { text: body });
|
|
4926
|
+
else this.injectAssistant(String(req.requesterSid), body);
|
|
4927
|
+
} catch {}
|
|
4928
|
+
}
|
|
4929
|
+
try {
|
|
4930
|
+
if (typeof this.integrationNotify === 'function') {
|
|
4931
|
+
this.integrationNotify(
|
|
4932
|
+
'🛠️ Tool botu raporu — isteyen: ' + (req.requesterTitle || 'Beast') + '\n' +
|
|
4933
|
+
'İstenen: ' + String(req.task || '').slice(0, 140) + '\n' +
|
|
4934
|
+
String(reply || 'tamamlanamadı').replace(/\s+/g, ' ').slice(0, 500)
|
|
4935
|
+
);
|
|
4936
|
+
}
|
|
4937
|
+
} catch {}
|
|
4938
|
+
}
|
|
4939
|
+
} catch {}
|
|
4940
|
+
}
|
|
4941
|
+
|
|
4818
4942
|
/* ADMIN İZLEME: tüm botlar arası DM oturumlarının listesi */
|
|
4819
4943
|
listBotDmSessions() {
|
|
4820
4944
|
const bots = require('./bots');
|
|
@@ -4915,6 +5039,7 @@ const skills = require('./skills');
|
|
|
4915
5039
|
(t) =>
|
|
4916
5040
|
t.function.name !== 'delegate_task' &&
|
|
4917
5041
|
t.function.name !== 'set_reminder' &&
|
|
5042
|
+
t.function.name !== 'channel_send' &&
|
|
4918
5043
|
!t.function.name.startsWith('email_')
|
|
4919
5044
|
).sort((a, b) => String(a.function.name).localeCompare(String(b.function.name))); // önek-cache: sabit sıra
|
|
4920
5045
|
system =
|
|
@@ -5436,6 +5561,18 @@ const skills = require('./skills');
|
|
|
5436
5561
|
const r = await this.fileSend(sessionId, p, String((args && args.caption) || ''));
|
|
5437
5562
|
return JSON.stringify(r);
|
|
5438
5563
|
}
|
|
5564
|
+
if (name === 'channel_send') {
|
|
5565
|
+
if (typeof this.channelSend !== 'function') {
|
|
5566
|
+
return JSON.stringify({ ok: false, error: 'kanal gönderim köprüsü yok' });
|
|
5567
|
+
}
|
|
5568
|
+
const r = await this.channelSend({
|
|
5569
|
+
channel: String((args && args.channel) || 'auto'),
|
|
5570
|
+
to: String((args && args.to) || ''),
|
|
5571
|
+
text: String((args && args.text) || ''),
|
|
5572
|
+
sessionId: String(sessionId || ''),
|
|
5573
|
+
});
|
|
5574
|
+
return JSON.stringify(r);
|
|
5575
|
+
}
|
|
5439
5576
|
if (name === 'pdf_write') {
|
|
5440
5577
|
const r = await pdfwrite.write({
|
|
5441
5578
|
outPath: String((args && args.path) || ''),
|
|
@@ -5826,7 +5963,7 @@ const TOOL_REQUEST_DEF = {
|
|
|
5826
5963
|
function: {
|
|
5827
5964
|
name: 'tool_request',
|
|
5828
5965
|
description:
|
|
5829
|
-
'İhtiyaç duyduğun bir araç eksikse ya da bozuksa TOOL botuna yazdırır ve
|
|
5966
|
+
'İhtiyaç duyduğun bir araç eksikse ya da bozuksa TOOL botuna yazdırır. ASENKRON çalışır: istek anında kabul edilir, Tool botu KENDİ oturumunda arka planda aracı yazar ve test eder; sen BEKLEME, işine devam et. Araç hazır olunca tool__<ad> olarak ANINDA çağrılır; Tool botunun raporu bitince bu sohbete otomatik düşer (AJAN DM panelinde de görünür). task: araç adı + ne yapacak + girdi/çıktı sözleşmesi; context: örnek veri, dosya yolu, endpoint veya hata çıktısı. Aynı araç için tekrar istek atma.',
|
|
5830
5967
|
parameters: {
|
|
5831
5968
|
type: 'object',
|
|
5832
5969
|
properties: {
|
|
@@ -6228,6 +6365,23 @@ const TOOLS = [
|
|
|
6228
6365
|
},
|
|
6229
6366
|
},
|
|
6230
6367
|
},
|
|
6368
|
+
{
|
|
6369
|
+
type: 'function',
|
|
6370
|
+
function: {
|
|
6371
|
+
name: 'channel_send',
|
|
6372
|
+
description:
|
|
6373
|
+
"Send a message FROM YOU to an allow-listed person on WhatsApp/Telegram/Discord — the FIRST message, without waiting for them to write first. Use when the user says 'write to X', 'let X know', 'announce to the list'. channel: whatsapp|telegram|discord|auto (default auto = every connected channel where the target matches). to: allow-list name (partial), phone number, Telegram/Discord numeric id, @username, 'owner'/'sahip' for the owner, or 'all' for everyone on the list. text: the message (max ~4000 chars). Only allow-listed people can be targeted; if the name is not on the list the send is refused. Telegram/Discord can only deliver if that person has already started/permitted the bot; report failures honestly.",
|
|
6374
|
+
parameters: {
|
|
6375
|
+
type: 'object',
|
|
6376
|
+
properties: {
|
|
6377
|
+
channel: { type: 'string', enum: ['auto', 'whatsapp', 'telegram', 'discord'], description: 'Channel (default auto)' },
|
|
6378
|
+
to: { type: 'string', description: "Allow-list name, number, id, @username, 'owner'/'sahip' or 'all'" },
|
|
6379
|
+
text: { type: 'string', description: 'Message text to send' },
|
|
6380
|
+
},
|
|
6381
|
+
required: ['to', 'text'],
|
|
6382
|
+
},
|
|
6383
|
+
},
|
|
6384
|
+
},
|
|
6231
6385
|
{
|
|
6232
6386
|
type: 'function',
|
|
6233
6387
|
function: {
|
package/src/agent/watext.js
CHANGED
|
@@ -58,6 +58,7 @@ const TOOL_LABELS = {
|
|
|
58
58
|
todo_write: (a) => `Görev listesi (${(a.todos || []).length})`,
|
|
59
59
|
delegate_task: (a) => `Alt görev: ${String(a.task || '').slice(0, 80)}`,
|
|
60
60
|
set_reminder: (a) => `Hatırlatma${a.repeat ? ' [' + a.repeat + ']' : ''}: ${String(a.message || '').slice(0, 60)}`,
|
|
61
|
+
channel_send: (a) => `Kanal mesajı → ${a.to || ''}${a.channel && a.channel !== 'auto' ? ' (' + a.channel + ')' : ''}: ${String(a.text || '').slice(0, 60)}`,
|
|
61
62
|
watcher_add: (a) => `İzleyici kur: ${a.name || ''}`,
|
|
62
63
|
watcher_list: () => 'İzleyiciler',
|
|
63
64
|
watcher_remove: (a) => `İzleyici sil: ${a.id || ''}`,
|
package/src/main.js
CHANGED
|
@@ -2271,6 +2271,7 @@ function botToolSet(cfg) {
|
|
|
2271
2271
|
'event_list', 'event_subscribe', 'event_unsubscribe',
|
|
2272
2272
|
'watcher_add', 'watcher_list', 'watcher_remove',
|
|
2273
2273
|
'tool_request', /* CEPHANE: eksik aracı TOOL botuna yazdırma hakkı HER botta */
|
|
2274
|
+
'channel_send', /* İLK MESAJ: allow listteki kişilere kendiliğinden yazma hakkı HER botta */
|
|
2274
2275
|
]);
|
|
2275
2276
|
if (s.web_search) { set.add('web_search'); set.add('http_fetch'); set.add('webfetch'); set.add('deep_search'); }
|
|
2276
2277
|
if (s.browser) {
|
|
@@ -3227,6 +3228,7 @@ function reloadBackend() {
|
|
|
3227
3228
|
/* tool yazım/doğrulama adımları → bağlı entegrasyonlara (WA/TG/Discord) */
|
|
3228
3229
|
integrationNotify: integrationBroadcast,
|
|
3229
3230
|
fileSend: deliverFile,
|
|
3231
|
+
channelSend: (args) => channelSendFromAgent(args),
|
|
3230
3232
|
reminders: { add: scheduleReminder },
|
|
3231
3233
|
watchers: {
|
|
3232
3234
|
list: () => watchers.list(),
|
|
@@ -8964,6 +8966,145 @@ function integrationBroadcast(text) {
|
|
|
8964
8966
|
} catch {}
|
|
8965
8967
|
}
|
|
8966
8968
|
|
|
8969
|
+
/* ---------- AJAN İLK MESAJI (channel_send) ----------
|
|
8970
|
+
Allow listteki kişilere WhatsApp/Telegram/Discord'dan SEN İLK MESAJI atar;
|
|
8971
|
+
karşıdan mesaj gelmesini beklemez. Güvenlik: yalnız allow listteki kişiler
|
|
8972
|
+
hedeflenir; müşteri botu (non-admin) yalnız KENDİ bot_id'sine bağlı
|
|
8973
|
+
kişilere yazabilir. */
|
|
8974
|
+
async function channelSendFromAgent(input) {
|
|
8975
|
+
const a = input || {};
|
|
8976
|
+
const body = stripAiDashes(String(a.text || '').trim()).slice(0, 4000);
|
|
8977
|
+
if (!body) return { ok: false, error: 'text gerekli' };
|
|
8978
|
+
const want = String(a.channel || 'auto').toLowerCase();
|
|
8979
|
+
const q = String(a.to || '').trim();
|
|
8980
|
+
if (!q) return { ok: false, error: 'to gerekli (isim, numara, ID, @kullanıcı adı, "sahip" ya da "all")' };
|
|
8981
|
+
const ql = q.toLowerCase();
|
|
8982
|
+
const isAll = ['all', 'herkes', 'hepsi', 'tümü', 'tumu', 'everyone'].includes(ql);
|
|
8983
|
+
const isOwner = ['sahip', 'owner', 'patron'].includes(ql);
|
|
8984
|
+
|
|
8985
|
+
/* oturumun botu: müşteri botuysa hedef kısıtı (izolasyon) */
|
|
8986
|
+
let botId = '';
|
|
8987
|
+
try {
|
|
8988
|
+
const s = engine.cache.get(String(a.sessionId || '')) || engine._load(String(a.sessionId || ''));
|
|
8989
|
+
botId = String((s && s.botId) || '');
|
|
8990
|
+
} catch {}
|
|
8991
|
+
const bot = botId ? bots.get(botId) : null;
|
|
8992
|
+
const unrestricted = !bot || !!bot.admin;
|
|
8993
|
+
const botOk = (e) => unrestricted || String((e && typeof e === 'object' && e.bot_id) || '') === botId;
|
|
8994
|
+
|
|
8995
|
+
const targets = [];
|
|
8996
|
+
const notes = [];
|
|
8997
|
+
const known = [];
|
|
8998
|
+
|
|
8999
|
+
/* WHATSAPP */
|
|
9000
|
+
if (want === 'auto' || want === 'whatsapp' || want === 'wa') {
|
|
9001
|
+
if (wa && wa.connected) {
|
|
9002
|
+
const own = waOwnerNum();
|
|
9003
|
+
const qd = q.replace(/\D/g, '');
|
|
9004
|
+
for (const e of settings.waAllow || []) {
|
|
9005
|
+
if (e === '*') continue;
|
|
9006
|
+
if (!botOk(e)) continue;
|
|
9007
|
+
const pn = waEntryDigits(e);
|
|
9008
|
+
if (!pn) continue;
|
|
9009
|
+
const name = typeof e === 'object' ? String(e.name || '') : '';
|
|
9010
|
+
if (name) known.push(name + ' (whatsapp)');
|
|
9011
|
+
const hit =
|
|
9012
|
+
isAll ||
|
|
9013
|
+
(isOwner && own && pn === own) ||
|
|
9014
|
+
(qd.length >= 6 && (pn === qd || pn.endsWith(qd))) ||
|
|
9015
|
+
(name && name.toLowerCase().includes(ql));
|
|
9016
|
+
if (hit) targets.push({ channel: 'whatsapp', id: pn + '@s.whatsapp.net', label: name || pn });
|
|
9017
|
+
}
|
|
9018
|
+
} else if (want !== 'auto') notes.push('whatsapp bağlı değil');
|
|
9019
|
+
}
|
|
9020
|
+
|
|
9021
|
+
/* TELEGRAM */
|
|
9022
|
+
if (want === 'auto' || want === 'telegram' || want === 'tg') {
|
|
9023
|
+
if (tg && tg.connected) {
|
|
9024
|
+
const owners = tgOwnerIds();
|
|
9025
|
+
for (const e of settings.tgAllow || []) {
|
|
9026
|
+
if (e === '*') continue;
|
|
9027
|
+
if (!botOk(e)) continue;
|
|
9028
|
+
const id = typeof e === 'string' ? e.trim() : String((e && e.id) || '').trim();
|
|
9029
|
+
if (!id || id === '*') continue;
|
|
9030
|
+
const name = typeof e === 'object' ? String(e.name || '') : '';
|
|
9031
|
+
const owner = typeof e === 'object' && !!e.owner;
|
|
9032
|
+
if (name) known.push(name + ' (telegram)');
|
|
9033
|
+
const hit =
|
|
9034
|
+
isAll ||
|
|
9035
|
+
(isOwner && owner && owners.includes(id)) ||
|
|
9036
|
+
id === q ||
|
|
9037
|
+
id.toLowerCase() === ql ||
|
|
9038
|
+
(name && name.toLowerCase().includes(ql));
|
|
9039
|
+
if (hit) targets.push({ channel: 'telegram', id, label: name || id });
|
|
9040
|
+
}
|
|
9041
|
+
} else if (want !== 'auto') notes.push('telegram bağlı değil');
|
|
9042
|
+
}
|
|
9043
|
+
|
|
9044
|
+
/* DISCORD */
|
|
9045
|
+
if (want === 'auto' || want === 'discord' || want === 'dc') {
|
|
9046
|
+
if (dc && dc.connected) {
|
|
9047
|
+
const owners = dcOwnerIds();
|
|
9048
|
+
for (const e of settings.dcAllow || []) {
|
|
9049
|
+
if (e === '*') continue;
|
|
9050
|
+
if (!botOk(e)) continue;
|
|
9051
|
+
const id = typeof e === 'string' ? e.trim() : String((e && e.id) || '').trim();
|
|
9052
|
+
if (!id || id === '*') continue;
|
|
9053
|
+
const name = typeof e === 'object' ? String(e.name || '') : '';
|
|
9054
|
+
const owner = typeof e === 'object' && !!e.owner;
|
|
9055
|
+
if (name) known.push(name + ' (discord)');
|
|
9056
|
+
const hit =
|
|
9057
|
+
isAll ||
|
|
9058
|
+
(isOwner && owner && owners.includes(id)) ||
|
|
9059
|
+
id === q ||
|
|
9060
|
+
id.toLowerCase() === ql ||
|
|
9061
|
+
(name && name.toLowerCase().includes(ql));
|
|
9062
|
+
if (hit) targets.push({ channel: 'discord', id, label: name || id });
|
|
9063
|
+
}
|
|
9064
|
+
} else if (want !== 'auto') notes.push('discord bağlı değil');
|
|
9065
|
+
}
|
|
9066
|
+
|
|
9067
|
+
if (!targets.length) {
|
|
9068
|
+
return {
|
|
9069
|
+
ok: false,
|
|
9070
|
+
error:
|
|
9071
|
+
'allow listte eşleşen hedef yok: "' + q + '"' +
|
|
9072
|
+
(notes.length ? ' (' + notes.join('; ') + ')' : '') +
|
|
9073
|
+
(known.length ? ' — listedekiler: ' + [...new Set(known)].slice(0, 10).join(', ') : ''),
|
|
9074
|
+
};
|
|
9075
|
+
}
|
|
9076
|
+
/* aynı hedefe çift gönderim olmasın */
|
|
9077
|
+
const uniq = [];
|
|
9078
|
+
const seen = new Set();
|
|
9079
|
+
for (const t of targets) {
|
|
9080
|
+
const k = t.channel + '|' + t.id;
|
|
9081
|
+
if (seen.has(k)) continue;
|
|
9082
|
+
seen.add(k);
|
|
9083
|
+
uniq.push(t);
|
|
9084
|
+
}
|
|
9085
|
+
const sent = [];
|
|
9086
|
+
const failed = [];
|
|
9087
|
+
for (const t of uniq) {
|
|
9088
|
+
let ok = false;
|
|
9089
|
+
try {
|
|
9090
|
+
if (t.channel === 'whatsapp') ok = await sendWaSafe(t.id, body);
|
|
9091
|
+
else if (t.channel === 'telegram') ok = await sendTgSafe(t.id, body);
|
|
9092
|
+
else if (t.channel === 'discord') ok = await sendDcSafe(t.id, body);
|
|
9093
|
+
} catch {}
|
|
9094
|
+
if (ok) sent.push({ channel: t.channel, to: t.label });
|
|
9095
|
+
else failed.push({ channel: t.channel, to: t.label, error: 'gönderilemedi (bağlantı yok ya da kişi bota izin vermemiş olabilir)' });
|
|
9096
|
+
}
|
|
9097
|
+
return {
|
|
9098
|
+
ok: sent.length > 0,
|
|
9099
|
+
sent,
|
|
9100
|
+
failed,
|
|
9101
|
+
...(notes.length ? { notes } : {}),
|
|
9102
|
+
note: sent.length
|
|
9103
|
+
? 'İlk mesaj gönderildi: ' + sent.map((x) => x.to + ' (' + x.channel + ')').join(', ')
|
|
9104
|
+
: 'Hiçbir hedefe gönderilemedi' + (notes.length ? ' — ' + notes.join('; ') : ''),
|
|
9105
|
+
};
|
|
9106
|
+
}
|
|
9107
|
+
|
|
8967
9108
|
/* ---- fiyat alarmları (kalıcı) ---- */
|
|
8968
9109
|
function finAlertsLoad() {
|
|
8969
9110
|
const raw = finReadJson(finFile('alerts.json'), []);
|