beast-agent 2.14.0 → 2.16.0
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 +499 -154
- package/src/agent/financetools.js +307 -0
- package/src/agent/opencode/agents.js +161 -0
- package/src/agent/opencode/askperm.js +135 -0
- package/src/agent/opencode/index.js +51 -0
- package/src/agent/opencode/permission.js +108 -0
- package/src/agent/opencode/prompts/anthropic.txt +105 -0
- package/src/agent/opencode/prompts/beast.txt +147 -0
- package/src/agent/opencode/prompts/build-switch.txt +5 -0
- package/src/agent/opencode/prompts/codex.txt +79 -0
- package/src/agent/opencode/prompts/default.txt +95 -0
- package/src/agent/opencode/prompts/edit.txt +10 -0
- package/src/agent/opencode/prompts/explore.txt +18 -0
- package/src/agent/opencode/prompts/gemini.txt +155 -0
- package/src/agent/opencode/prompts/glob.txt +6 -0
- package/src/agent/opencode/prompts/gpt.txt +107 -0
- package/src/agent/opencode/prompts/grep.txt +8 -0
- package/src/agent/opencode/prompts/kimi.txt +95 -0
- package/src/agent/opencode/prompts/max-steps.txt +16 -0
- package/src/agent/opencode/prompts/meta.txt +65 -0
- package/src/agent/opencode/prompts/plan.txt +26 -0
- package/src/agent/opencode/prompts/read.txt +14 -0
- package/src/agent/opencode/prompts/skill.txt +5 -0
- package/src/agent/opencode/prompts/task.txt +19 -0
- package/src/agent/opencode/prompts/todowrite.txt +44 -0
- package/src/agent/opencode/prompts/trinity.txt +97 -0
- package/src/agent/opencode/prompts/webfetch.txt +13 -0
- package/src/agent/opencode/prompts/websearch.txt +14 -0
- package/src/agent/opencode/prompts/write.txt +8 -0
- package/src/agent/opencode/systemprompt.js +157 -0
- package/src/agent/opencode/toolmap.js +316 -0
- package/src/agent/scripts/mt5_bridge.py +381 -0
- package/src/agent/tools.js +10 -3
- package/src/main.js +503 -69
- package/src/mt5bridge.js +215 -0
- package/src/preload.js +13 -3
- package/src/renderer/index.html +67 -0
- package/src/renderer/renderer.js +552 -53
- package/src/renderer/style.css +328 -1
- package/src/agent/android.js +0 -265
package/src/agent/engine.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
3
|
/* Beast engine: sessions, frugal context, streaming tool loop.
|
|
4
4
|
v2: token-bazlı bağlam bütçesi (kullanım kalibrasyonlu), paralel tool
|
|
@@ -10,6 +10,9 @@ const llm = require('./llm');
|
|
|
10
10
|
const { chatStream, chatStreamAuto, chatOnce } = llm;
|
|
11
11
|
const { chatCompletionsUrl, beastDir } = require('./config');
|
|
12
12
|
const tools = require('./tools');
|
|
13
|
+
let financetools = null;
|
|
14
|
+
try { financetools = require('./financetools'); } catch {}
|
|
15
|
+
const FINANCE_TOOL_SET = new Set(financetools ? financetools.NAMES : []);
|
|
13
16
|
const research = require('./research');
|
|
14
17
|
const agentdefs = require('./agentdefs');
|
|
15
18
|
const memory = require('./memory');
|
|
@@ -21,6 +24,10 @@ const apps = require('./apps');
|
|
|
21
24
|
const pdfwrite = require('./pdfwrite');
|
|
22
25
|
const { estTokens, estMsgTokens } = require('./tokens');
|
|
23
26
|
const log = require('./logger');
|
|
27
|
+
/* opencode mantığı (birebir port): ajanlar, izinler, tool seti, promptlar —
|
|
28
|
+
Beast Code (bcCode) oturumları bu çekirdek üzerinden koşar */
|
|
29
|
+
const opencode = require('./opencode');
|
|
30
|
+
const { AskService } = opencode;
|
|
24
31
|
|
|
25
32
|
const MAX_TURNS = 40;
|
|
26
33
|
const SUB_MAX_TURNS = 8;
|
|
@@ -134,15 +141,9 @@ const PERM_TOOL_SETS = {
|
|
|
134
141
|
};
|
|
135
142
|
const PERM_LEVELS = ['all', 'web', 'read', 'chat'];
|
|
136
143
|
|
|
137
|
-
/* opencode plan
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
'read_file', 'list_dir', 'grep', 'glob',
|
|
141
|
-
'web_search', 'http_fetch', 'webfetch', 'deep_search',
|
|
142
|
-
'browser_open', 'browser_read', 'browser_snapshot',
|
|
143
|
-
'todo_write', 'memory_search', 'kb_search', 'ocr_read',
|
|
144
|
-
'git_diff_review', 'repo_map', 'repo_symbols', 'xlsx_read',
|
|
145
|
-
]);
|
|
144
|
+
/* opencode plan ajanı salt-okurluğu artık PERMISSION kurallarından türer
|
|
145
|
+
(opencode/agents.js: edit {"*":"deny"} → Permission.disabled seti düşürür) —
|
|
146
|
+
elle bakılan araç beyaz listesi kalktı (tamamen opencode mantığı) */
|
|
146
147
|
|
|
147
148
|
/* İzin değerini normalize eder: 'all' → ['all'], 'web' → ['web'],
|
|
148
149
|
'web,read' / ['web','read'] → ['web','read'] (sıra PERM_LEVELS'e göre dizilir).
|
|
@@ -310,6 +311,15 @@ class Engine {
|
|
|
310
311
|
this.notifyOwnerFail = opts.notifyOwnerFail !== false; // #25 hata mail bildirimi (runtime /notify)
|
|
311
312
|
this.reminders = opts.reminders || null; // hatırlatıcı kancası (main enjekte eder)
|
|
312
313
|
this.watchers = opts.watchers || null; // arka plan izleyici köprüsü (main enjekte eder)
|
|
314
|
+
/* opencode permission Service (birebir port): ask/reply — once|always|reject.
|
|
315
|
+
BC oturumlarında riskli araç onayı bu akışla sorulur; UI 'permission.asked'
|
|
316
|
+
eventi dinler, 'permission:reply' ile yanıtlar. approved listesi oturum
|
|
317
|
+
boyunca "always" desenlerini kural olarak taşır (opencode birebir). */
|
|
318
|
+
this._perm = new AskService((ev) => this.emit(ev));
|
|
319
|
+
this.bcAskApprovals = opts.approvals ? true : false; // onay ayarı → opencode user konfigi gibi "ask" üretir
|
|
320
|
+
/* opencode reminders.ts: bu oturumda daha önce plan ajanı koştu mu?
|
|
321
|
+
plan→build geçişinde build-switch.txt synthetic part'ı buna bağlanır */
|
|
322
|
+
this._bcWasPlan = new Map(); // sessionId -> bool
|
|
313
323
|
/* onay kapısı: riskli araç öncesi dış onay beklenir ({request} async) */
|
|
314
324
|
this.approvals = opts.approvals || null;
|
|
315
325
|
/* "bir daha sorma" onaylanan araçlar — bu setteki riskli araçlar sorulmaz */
|
|
@@ -939,11 +949,10 @@ class Engine {
|
|
|
939
949
|
return lv.effort || null;
|
|
940
950
|
}
|
|
941
951
|
|
|
942
|
-
/*
|
|
952
|
+
/* opencode birebir: düşünme çabası ajan tanımına değil kullanıcı ayarına
|
|
953
|
+
bağlıdır — eski "BC'de low'a sabitle" hız kısıtı kalktı (opencode'da yok) */
|
|
943
954
|
_thinkEffortFor(session) {
|
|
944
|
-
|
|
945
|
-
if (!session || !session.bcCode) return e;
|
|
946
|
-
return e ? 'low' : null;
|
|
955
|
+
return this._thinkEffort();
|
|
947
956
|
}
|
|
948
957
|
|
|
949
958
|
_thinkLabel() {
|
|
@@ -1137,6 +1146,13 @@ class Engine {
|
|
|
1137
1146
|
} else if (rec.t === 'bcws') {
|
|
1138
1147
|
/* Beast Code oturumu: hangi çalışma klasörüne bağlı */
|
|
1139
1148
|
session.bcWs = String(rec.ws || '');
|
|
1149
|
+
} else if (rec.t === 'fin') {
|
|
1150
|
+
/* Beast Finance oturumu: kalıcı izolasyon etiketi (finoff kapatır) */
|
|
1151
|
+
session.finance = true;
|
|
1152
|
+
session.financeTrader = !!rec.trader;
|
|
1153
|
+
} else if (rec.t === 'finoff') {
|
|
1154
|
+
session.finance = false;
|
|
1155
|
+
session.financeTrader = false;
|
|
1140
1156
|
} else if (rec.t === 'msg') {
|
|
1141
1157
|
delete rec.t;
|
|
1142
1158
|
session.messages.push(rec);
|
|
@@ -1273,10 +1289,36 @@ class Engine {
|
|
|
1273
1289
|
count: s.messages.length,
|
|
1274
1290
|
isBg,
|
|
1275
1291
|
isBotDm: !!s.isBotDm,
|
|
1292
|
+
finance: !!s.finance,
|
|
1276
1293
|
bgStatus: isBg ? (job ? job.status : null) : null,
|
|
1277
1294
|
};
|
|
1278
1295
|
}
|
|
1279
1296
|
|
|
1297
|
+
/* Beast Finance etiketi: oturumu finance sohbeti olarak KALICI işaretler
|
|
1298
|
+
(jsonl'e 'fin' satırı düşer — restart sonrası da hatırlanır) */
|
|
1299
|
+
markFinance(id, trader = false) {
|
|
1300
|
+
const s = this._load(String(id));
|
|
1301
|
+
s.finance = true;
|
|
1302
|
+
s.financeTrader = !!trader;
|
|
1303
|
+
try {
|
|
1304
|
+
fs.appendFileSync(this._file(s.id), JSON.stringify({ t: 'fin', trader: !!trader, at: nowIso() }) + '\n');
|
|
1305
|
+
} catch {}
|
|
1306
|
+
this.cache.set(String(s.id), s);
|
|
1307
|
+
return s;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/* Finance etiketini kaldır — oturum normal sohbete döner */
|
|
1311
|
+
unmarkFinance(id) {
|
|
1312
|
+
const s = this._load(String(id));
|
|
1313
|
+
s.finance = false;
|
|
1314
|
+
s.financeTrader = false;
|
|
1315
|
+
try {
|
|
1316
|
+
fs.appendFileSync(this._file(s.id), JSON.stringify({ t: 'finoff', at: nowIso() }) + '\n');
|
|
1317
|
+
} catch {}
|
|
1318
|
+
this.cache.set(String(s.id), s);
|
|
1319
|
+
return s;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1280
1322
|
/* #17 paralel ajan oturumları sol sohbet listesine KARIŞMAZ;
|
|
1281
1323
|
geçmişleri sağdaki Paralel Ajanlar panelinde izlenir */
|
|
1282
1324
|
listSessions() {
|
|
@@ -1489,56 +1531,151 @@ class Engine {
|
|
|
1489
1531
|
return out;
|
|
1490
1532
|
}
|
|
1491
1533
|
|
|
1492
|
-
/*
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1534
|
+
/* ---------- opencode mantığı: Beast Code ajan sistemi ----------
|
|
1535
|
+
opencode-dev/packages/opencode/src birebir portu — BC oturumları artık
|
|
1536
|
+
"mod" değil AJAN koşar (agent/agent.ts):
|
|
1537
|
+
build — varsayılan (edit serbest, question/plan_enter allow)
|
|
1538
|
+
plan — salt-okur: edit * deny (yalnız .opencode/plans/*.md yazılabilir)
|
|
1539
|
+
System birleşimi (llm/request.ts:58-66 + prompt.ts:1257-1269):
|
|
1540
|
+
[ ajanın promptu VARSA o, yoksa model-bazlı ana prompt (beast.txt,
|
|
1541
|
+
gpt.txt, anthropic.txt, gemini.txt, kimi.txt, codex.txt, default.txt),
|
|
1542
|
+
environment bloğu, proje talimatları (AGENTS.md), skills kataloğu ] */
|
|
1543
|
+
|
|
1544
|
+
/* BC ajanını çözer (opencode agents.get):
|
|
1545
|
+
- Sandbox oturumları (sbSandbox) → 'sandbox' ajanı (repo disiplini + panel_run)
|
|
1546
|
+
- session.bcAgent ('plan'|'build') — eski bcMode:'plan' oturumları geriye
|
|
1547
|
+
uyumlu plan ajanına düşer
|
|
1548
|
+
Studio/Finance/bgJob kendi ajan dünyalarında koşar — opencode BC sistemine
|
|
1549
|
+
katılmaz (buildStudioSystem/buildFinanceSystem/buildBgSystem öncelikli). */
|
|
1550
|
+
_bcAgentInfo(session) {
|
|
1551
|
+
if (!session || !session.bcCode) return null;
|
|
1552
|
+
if (session.studio || session.finance || session.bgJob) return null;
|
|
1553
|
+
const name = session.sbSandbox
|
|
1554
|
+
? 'sandbox'
|
|
1555
|
+
: session.bcAgent === 'plan' || session.bcAgent === 'build' || session.bcAgent === 'sandbox'
|
|
1556
|
+
? session.bcAgent
|
|
1557
|
+
: String(session.bcMode || '').toLowerCase() === 'plan'
|
|
1558
|
+
? 'plan'
|
|
1559
|
+
: 'build';
|
|
1560
|
+
const ws = (session && session.workspace) || this.workspace;
|
|
1561
|
+
const user = this.bcAskApprovals ? { bash: 'ask', edit: { '*': 'ask' } } : {};
|
|
1562
|
+
const info = opencode.agents.get(name, { worktree: ws, promptExplore: opencode.PROMPT_EXPLORE, user });
|
|
1563
|
+
if (info) return info;
|
|
1564
|
+
const fallbackName = session.sbSandbox ? 'sandbox' : 'build';
|
|
1565
|
+
return opencode.agents.get(fallbackName, { worktree: ws, promptExplore: opencode.PROMPT_EXPLORE, user });
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
/* opencode opencode ajan değişimi: /plan /build — oturumun ajanı döner,
|
|
1569
|
+
plan→build geçişi reminders (build-switch.txt) için işaretlenir */
|
|
1570
|
+
setBcAgent(sessionId, name) {
|
|
1571
|
+
const s = this.cache.get(String(sessionId || ''));
|
|
1572
|
+
if (!s || !s.bcCode) return null;
|
|
1573
|
+
const prev = s.bcAgent || 'build';
|
|
1574
|
+
const next = name === 'plan' ? 'plan' : 'build';
|
|
1575
|
+
if (prev === 'plan' && next === 'build') this._bcWasPlan.set(String(s.id), true);
|
|
1576
|
+
s.bcAgent = next;
|
|
1577
|
+
this.cache.set(s.id, s);
|
|
1578
|
+
this.emit({ type: 'bc-agent', sessionId: s.id, agent: next, prev });
|
|
1579
|
+
return next;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
/* opencode Permission.reply (UI/WA köprüsü): 'once' | 'always' | 'reject' */
|
|
1583
|
+
replyPermission(requestId, action, message) {
|
|
1584
|
+
try {
|
|
1585
|
+
return this._perm.reply(requestId, action === 'always' ? 'always' : action === 'reject' ? 'reject' : 'once', message);
|
|
1586
|
+
} catch (e) {
|
|
1587
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
/* opencode request.ts prepare — system birleşimi (birebir sıra) */
|
|
1592
|
+
buildOcSystem(session, agentInfo) {
|
|
1593
|
+
const sid = String(session && session.id);
|
|
1594
|
+
const sel = this.sessionModel.get(sid) || this.modelFor(null) || this.sel || { model: '', providerId: '' };
|
|
1595
|
+
const ws = (session && session.workspace) || this.workspace;
|
|
1596
|
+
const agentPrompt = (agentInfo && agentInfo.prompt) || '';
|
|
1597
|
+
const main = agentPrompt ? [agentPrompt] : opencode.systemprompt.provider(sel.model);
|
|
1511
1598
|
const proj = this._projectInstructions(session);
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
:
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
'
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1599
|
+
const skillsBlock = (() => {
|
|
1600
|
+
try {
|
|
1601
|
+
return opencode.systemprompt.skills((agentInfo && agentInfo.permission) || [], () => skills.scan());
|
|
1602
|
+
} catch {
|
|
1603
|
+
return undefined;
|
|
1604
|
+
}
|
|
1605
|
+
})();
|
|
1606
|
+
/* opencode'da birleşim .filter(x => x).join("\n") — ajan promptu varsa
|
|
1607
|
+
model-bazlı dosya YERİNE geçer */
|
|
1608
|
+
return [
|
|
1609
|
+
...main,
|
|
1610
|
+
...opencode.systemprompt.environment({
|
|
1611
|
+
modelId: sel.model,
|
|
1612
|
+
providerId: sel.providerId || '?',
|
|
1613
|
+
directory: ws,
|
|
1614
|
+
worktree: this.workspace,
|
|
1615
|
+
}),
|
|
1616
|
+
...(proj ? [proj] : []),
|
|
1617
|
+
...(skillsBlock ? [skillsBlock] : []),
|
|
1618
|
+
]
|
|
1619
|
+
.filter((x) => x)
|
|
1620
|
+
.join('\n');
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
/* opencode araç izinleri: pattern'ler araç çağrısından türer.
|
|
1624
|
+
bash'ta always: [komut] — opencode tree-sitter deseni basitleştirilmiş. */
|
|
1625
|
+
_bcPermissionFor(name, args, ws) {
|
|
1626
|
+
const a = args || {};
|
|
1627
|
+
const abs = (p) => {
|
|
1628
|
+
try {
|
|
1629
|
+
const raw = String(p || '').trim();
|
|
1630
|
+
if (!raw) return '*';
|
|
1631
|
+
return path.isAbsolute(raw) ? path.resolve(raw) : path.resolve(ws || this.workspace, raw);
|
|
1632
|
+
} catch {
|
|
1633
|
+
return '*';
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
switch (name) {
|
|
1637
|
+
case 'edit_file':
|
|
1638
|
+
case 'write_file':
|
|
1639
|
+
return { permission: 'edit', patterns: [abs(a.path)], always: [] };
|
|
1640
|
+
case 'run_command':
|
|
1641
|
+
return { permission: 'bash', patterns: [String(a.command || '*')], always: [String(a.command || '*')] };
|
|
1642
|
+
case 'read_file':
|
|
1643
|
+
return { permission: 'read', patterns: [abs(a.path)], always: [] };
|
|
1644
|
+
case 'glob':
|
|
1645
|
+
return { permission: 'glob', patterns: [String(a.pattern || '*')], always: [] };
|
|
1646
|
+
case 'grep':
|
|
1647
|
+
return { permission: 'grep', patterns: [String(a.pattern || '*')], always: [] };
|
|
1648
|
+
case 'list_dir':
|
|
1649
|
+
return { permission: 'list', patterns: [abs(a.path)], always: [] };
|
|
1650
|
+
case 'webfetch':
|
|
1651
|
+
return { permission: 'webfetch', patterns: [String(a.url || '*')], always: [] };
|
|
1652
|
+
case 'web_search':
|
|
1653
|
+
return { permission: 'websearch', patterns: [String(a.query || '*')], always: [] };
|
|
1654
|
+
case 'todo_write':
|
|
1655
|
+
return { permission: 'todowrite', patterns: ['*'], always: ['*'] };
|
|
1656
|
+
case 'delegate_task':
|
|
1657
|
+
return { permission: 'task', patterns: [String(a.subagent_type || '*')], always: [] };
|
|
1658
|
+
default:
|
|
1659
|
+
return { permission: name, patterns: ['*'], always: [] };
|
|
1660
|
+
}
|
|
1540
1661
|
}
|
|
1541
1662
|
|
|
1663
|
+
/* opencode external-directory: workspace dışı dosya erişimi ayrı izin ister
|
|
1664
|
+
(tool/external-directory.ts) — read/edit/write kapsar */
|
|
1665
|
+
_bcExternalCheck(name, args, ws) {
|
|
1666
|
+
if (name !== 'read_file' && name !== 'edit_file' && name !== 'write_file') return null;
|
|
1667
|
+
const raw = String((args && args.path) || '').trim();
|
|
1668
|
+
if (!raw) return null;
|
|
1669
|
+
try {
|
|
1670
|
+
const abs = path.isAbsolute(raw) ? path.resolve(raw) : path.resolve(ws || this.workspace, raw);
|
|
1671
|
+
const base = path.resolve(ws || this.workspace);
|
|
1672
|
+
if (path.normalize(abs).toLowerCase().startsWith(path.normalize(base).toLowerCase())) return null;
|
|
1673
|
+
return { permission: 'external_directory', patterns: [abs], always: [] };
|
|
1674
|
+
} catch {
|
|
1675
|
+
return null;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1542
1679
|
/* Beast Studio (video paneli): video YAPMA ve DÜZENLEME ajanı. Workspace'teki
|
|
1543
1680
|
her şey video proje malzemesi olarak görülür (ham görüntü, ses, görsel,
|
|
1544
1681
|
altyazı, çıktı). Medya işi ffprobe + ffmpeg ile yürütür; ikisi de app ile
|
|
@@ -1599,6 +1736,46 @@ class Engine {
|
|
|
1599
1736
|
);
|
|
1600
1737
|
}
|
|
1601
1738
|
|
|
1739
|
+
/* Beast Finance: MT5 trading ajanı — chat copilot'ı VEYA otonom trader.
|
|
1740
|
+
Oturum alanları (main enjekte eder): financeTrader, financeAuto,
|
|
1741
|
+
financeSymbols, financeStrategy, financeLimits */
|
|
1742
|
+
buildFinanceSystem(session) {
|
|
1743
|
+
const nowD = new Date();
|
|
1744
|
+
const localDate = nowD.toLocaleDateString('tr-TR');
|
|
1745
|
+
const localTime = String(nowD.getHours()).padStart(2, '0') + ':00';
|
|
1746
|
+
const isTrader = !!(session && session.financeTrader);
|
|
1747
|
+
const auto = !!(session && session.financeAuto);
|
|
1748
|
+
const symbols = Array.isArray(session && session.financeSymbols) ? session.financeSymbols.join(', ') : String((session && session.financeSymbols) || '');
|
|
1749
|
+
const lim = (session && session.financeLimits) || {};
|
|
1750
|
+
const modeBlock = isTrader
|
|
1751
|
+
? auto
|
|
1752
|
+
? 'ROL: OTONOM TRADER ⚡ — tur tur çalışıyorsun. Her turda: veri çek → değerlendir → GEREKİRSE işlem aç/kapat/SL-TP güncelle → kısa rapor. Sistem seni turlar; sohbette konuşmazsın.'
|
|
1753
|
+
: 'ROL: GÖZLEMCİ ANALİST 🔍 — tur tur piyasa tararsın ama OTOMATİK İŞLEM KAPALI: mt5_trade/mt5_pending KULLANMA. Fırsatları net öneri olarak yaz (sembol, yön, giriş, SL, TP, sebep); sahibi onaylarsa açılır.'
|
|
1754
|
+
: 'ROL: TRADING CO-PİLOT 💬 — sahibinle sohbet ediyorsun. Veri isterse mt5_* araçlarıyla çek, analiz et; işlem istenirse limitler içinde uygula, istenmezse sadece öneri ver.';
|
|
1755
|
+
const proj = this._projectInstructions(session);
|
|
1756
|
+
return (
|
|
1757
|
+
'Sen BEAST FİNANS\u2019sın — bilgisayardaki MetaTrader 5 (MT5) terminaline köprüyle bağlı trading ajanı.\n' +
|
|
1758
|
+
'ORTAM: Beast Finance uygulamasındasın — solda sohbet geçmişi + izleyiciler, ortada bu sohbet, SAĞ panelde MT5 işlem panosu (hesap, pozisyonlar, piyasa, AKIŞ) var.\n' +
|
|
1759
|
+
'KÖPRÜ: MT5 terminaline Python stdio köprüsüyle bağlısın; panel 3 saniyede bir hesap/pozisyon/piyasa verisini tazeler. mt5_* araç çağrıların ve işlem hareketlerin (açılan/kapanan pozisyonlar) SAĞ paneldeki AKIŞ akışına canlı düşer — panelin orayı izlediğini bil.\n' +
|
|
1760
|
+
'GÖREV ALANI: piyasa verisi okuma (fiyat/hesap/pozisyon/geçmiş), teknik değerlendirme, pozisyon yönetimi (aç/kapat/SL/TP), risk disiplini ve kısa karar raporları. Kod yazma işi DEĞİLDİR.\n' +
|
|
1761
|
+
modeBlock + '\n' +
|
|
1762
|
+
'MT5 ARAÇLARI: mt5_status (bağlantı), mt5_account (hesap), mt5_market (canlı fiyat), mt5_positions (açık pozisyonlar), mt5_orders (bekleyen emirler), mt5_history (kapanan işlemler), mt5_trade (piyasa emri), mt5_close (kapat), mt5_modify (SL/TP), mt5_pending (bekleyen emir), mt5_cancel (emir iptal).\n' +
|
|
1763
|
+
'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' +
|
|
1764
|
+
(symbols ? `İZLEME LİSTESİ: ${symbols}\n` : '') +
|
|
1765
|
+
(lim.maxLot ? `LİMİTLER: max lot ${lim.maxLot}` + (lim.maxPositions ? ` · max eşzamanlı pozisyon ${lim.maxPositions}` : '') + ' — bunları aşıp araç kullanma; sistem zaten reddeder.\n' : '') +
|
|
1766
|
+
'RİSK DİSİPLİNİ:\n' +
|
|
1767
|
+
'- SL\u2019siz pozisyon BIRAKMA: açtığın her işlemde mantıklı bir SL koy; SL\u2019siz kalan pozisyonu mt5_modify ile tamamla.\n' +
|
|
1768
|
+
'- Martingale/kademeli lot artışı YASAK; kaybı geri kovalama (revenge trade) YASAK.\n' +
|
|
1769
|
+
'- Emin olmadığında İŞLEM YOK — "BEKLE: <sebep>" yaz. Sık işlem > iyi işlem.\n' +
|
|
1770
|
+
'- Hesap kaldıracı ve serbest marja göre pozisyon boyutunu düşük tut; tek işlemde serbest marjın büyük kısmını riske atma.\n' +
|
|
1771
|
+
(session && session.financeStrategy ? `SAHİBİNİN STRATEJİ NOTU (önceliklidir):\n${session.financeStrategy}\n` : '') +
|
|
1772
|
+
'RAPOR DİSİPLİNİ: kısa ve sayısal — sembol, yön, lot, giriş, SL/TP, sebep tek satırda. Uzun fal açma; tablo/liste kullanabilirsin.\n' +
|
|
1773
|
+
`Yerel zaman: ${localDate} ${localTime} — piyasa saatlerine dikkat et (borsa/metal seansları kapalıysa spread genişler; hafta sonu FX kapalıdır).\n` +
|
|
1774
|
+
(proj ? '# PROJE TALİMATLARI\n' + proj + '\n' : '') +
|
|
1775
|
+
FORMAT_RULES
|
|
1776
|
+
);
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1602
1779
|
async buildSystem(queryText, session) {
|
|
1603
1780
|
/* BOT OTURUMU: non-admin bota bağlıysa global Beast hafızası HIÇ girmez —
|
|
1604
1781
|
o botun kendi SOUL/USER/MEMORY dosyaları kullanılır (tam izolasyon) */
|
|
@@ -3135,53 +3312,53 @@ class Engine {
|
|
|
3135
3312
|
session.messages = keep;
|
|
3136
3313
|
}
|
|
3137
3314
|
|
|
3138
|
-
async _chatTurn(session, signal, onDelta, toolsList = TOOLS) {
|
|
3139
|
-
/*
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
toolsList =
|
|
3315
|
+
async _chatTurn(session, signal, onDelta, toolsList = TOOLS, bcOpts = null) {
|
|
3316
|
+
/* opencode mantığı (BC): araç seti opencode builtin registry'si — read/edit/
|
|
3317
|
+
write/bash/glob/grep/list/webfetch/websearch/todowrite/task/skill.
|
|
3318
|
+
MCP/apps/panel_run BC'ye GİRMEZ (opencode disiplini: hızlı ilk token). */
|
|
3319
|
+
const bcAgent = session && session.bcCode ? this._bcAgentInfo(session) : null;
|
|
3320
|
+
if (bcAgent) {
|
|
3321
|
+
toolsList = opencode.toolmap.definitions();
|
|
3322
|
+
} else {
|
|
3323
|
+
/* MCP: %APPDATA%\beast\mcp.json'daki etkin serverların araçlarını şema listesine ekle
|
|
3324
|
+
(bağlı değilse lazy bağlanır; kapalıysa liste değişmez) */
|
|
3325
|
+
toolsList = await mcp.mergeTools(toolsList);
|
|
3326
|
+
/* Beast Apps: kurulu app'lerin araçları (app__<id>__<tool>) modele açılır */
|
|
3327
|
+
toolsList = apps.mergeTools(toolsList);
|
|
3328
|
+
/* panel_run yalnız SANDBOX oturumlarında görünsün — diğer panellerde
|
|
3329
|
+
modelin alet çantasında olmasın (hook olmadan çalışmaz) */
|
|
3330
|
+
if (!(session && session.sbSandbox)) {
|
|
3331
|
+
toolsList = toolsList.filter((t) => !(t && t.function && t.function.name === 'panel_run'));
|
|
3332
|
+
}
|
|
3149
3333
|
}
|
|
3150
3334
|
/* opencode agent.ts port: özel ajan tanımı — prompt/model/araç/steps */
|
|
3151
3335
|
const adef = this._agentDefFor(session, !!session.bgJob);
|
|
3152
|
-
/* Beast Code: todo_write açıklaması "3+ adım" kısıtı içerir ve model küçük
|
|
3153
|
-
işlerde atlar — bcCode oturumunda HER işte İLK araç olacak şekilde değiştir */
|
|
3154
|
-
if (session && session.bcCode) {
|
|
3155
|
-
toolsList = toolsList.map((t) =>
|
|
3156
|
-
t && t.function && t.function.name === 'todo_write'
|
|
3157
|
-
? {
|
|
3158
|
-
...t,
|
|
3159
|
-
function: {
|
|
3160
|
-
...t.function,
|
|
3161
|
-
description:
|
|
3162
|
-
'Replace the visible task checklist for this chat. Beast Code session: ALWAYS call this tool as your VERY FIRST action for EVERY job (even tiny 1-step jobs) BEFORE any text or other tool, then update statuses (status:"done") as you complete each step. Keep titles short, verb-first.',
|
|
3163
|
-
},
|
|
3164
|
-
}
|
|
3165
|
-
: t
|
|
3166
|
-
);
|
|
3167
|
-
}
|
|
3168
3336
|
const promptText = this._lastUserText(session);
|
|
3169
3337
|
let system = session.bgJob
|
|
3170
3338
|
? this.buildBgSystem(session)
|
|
3171
3339
|
: session.studio
|
|
3172
3340
|
? this.buildStudioSystem(session)
|
|
3173
|
-
: session.
|
|
3174
|
-
? this.
|
|
3175
|
-
:
|
|
3341
|
+
: session.finance
|
|
3342
|
+
? this.buildFinanceSystem(session)
|
|
3343
|
+
: bcAgent
|
|
3344
|
+
? this.buildOcSystem(session, bcAgent)
|
|
3345
|
+
: await this.buildSystem(promptText, session);
|
|
3176
3346
|
// Granül izin: oturumun yetki seviyesine göre araç seti daraltılır.
|
|
3177
3347
|
// Çoklu izin (ör. web+read) seçiliyse kümeler BİRLEŞİR — hepsinin araçları açık olur.
|
|
3348
|
+
// BC'de opencode adları Beast adına çevrilerek denetlenir (read → read_file).
|
|
3178
3349
|
const perms = this.sessionPermFor(session.id);
|
|
3179
3350
|
let allowedSet = null;
|
|
3180
3351
|
if (!perms.includes('all')) {
|
|
3181
3352
|
allowedSet = new Set();
|
|
3182
3353
|
for (const p of perms) for (const t of PERM_TOOL_SETS[p] || []) allowedSet.add(t);
|
|
3183
3354
|
}
|
|
3184
|
-
let activeTools = allowedSet
|
|
3355
|
+
let activeTools = allowedSet
|
|
3356
|
+
? toolsList.filter((t) => allowedSet.has(opencode.toolmap.TO_BUILTIN[t.function.name] || t.function.name))
|
|
3357
|
+
: toolsList;
|
|
3358
|
+
/* Beast Finance: mt5_* araçları YALNIZ finance oturumlarında görünür */
|
|
3359
|
+
if (FINANCE_TOOL_SET.size && !(session && session.finance)) {
|
|
3360
|
+
activeTools = activeTools.filter((t) => !FINANCE_TOOL_SET.has(t.function.name));
|
|
3361
|
+
}
|
|
3185
3362
|
/* bot skill kısıtı: bota verilen yetkiye göre araç seti daraltılır */
|
|
3186
3363
|
const toolLimit = this.sessionTools.get(String(session.id));
|
|
3187
3364
|
if (toolLimit) activeTools = activeTools.filter((t) => toolLimit.has(t.function.name));
|
|
@@ -3194,9 +3371,16 @@ class Engine {
|
|
|
3194
3371
|
/* CEO: uygulayıcı araçlar kapalı — her şey paralel ajana devredilir */
|
|
3195
3372
|
activeTools = activeTools.filter((t) => !CEO_EXEC_TOOLS.has(t.function.name));
|
|
3196
3373
|
}
|
|
3197
|
-
if (
|
|
3198
|
-
/* opencode
|
|
3199
|
-
|
|
3374
|
+
if (bcAgent) {
|
|
3375
|
+
/* opencode permission.visibleTools (birebir): ruleset'te pattern:'*'+deny
|
|
3376
|
+
olan araçlar setten düşer — plan ajanında edit/write GERÇEKTEN gizlenir,
|
|
3377
|
+
salt-okurluk prompta kalmaz. opencode'da edit/write/apply_patch aynı
|
|
3378
|
+
"edit" iznine iner. */
|
|
3379
|
+
const hidden = opencode.Permission.disabled(
|
|
3380
|
+
activeTools.map((t) => t.function.name),
|
|
3381
|
+
bcAgent.permission
|
|
3382
|
+
);
|
|
3383
|
+
if (hidden.size) activeTools = activeTools.filter((t) => !hidden.has(t.function.name));
|
|
3200
3384
|
}
|
|
3201
3385
|
if (adef && adef.tools) {
|
|
3202
3386
|
/* özel ajan araç beyaz listesi — tanımda olmayan araç görünmez */
|
|
@@ -3234,7 +3418,25 @@ class Engine {
|
|
|
3234
3418
|
/* opencode port: model bağlamına göre dinamik bütçe + compaction özeti */
|
|
3235
3419
|
const selEarly = this.sessionModel.get(String(session.id)) || this.modelFor(null) || this.sel;
|
|
3236
3420
|
const dynBudget = this._historyBudget(selEarly);
|
|
3237
|
-
|
|
3421
|
+
/* opencode SessionReminders.apply (reminders.ts birebir): plan ajanındaysa
|
|
3422
|
+
plan.txt salt-okur hatırlatması, plan→build geçişinde build-switch.txt
|
|
3423
|
+
son USER mesajına synthetic eklenir. session.messages ASLA değişmez —
|
|
3424
|
+
her tur taze kopyaya uygulanır (opencode her turda DB'den yeniden yükler). */
|
|
3425
|
+
let msgsForPayload = session.messages;
|
|
3426
|
+
if (bcAgent) {
|
|
3427
|
+
msgsForPayload = opencode.systemprompt.applyReminders(
|
|
3428
|
+
session.messages,
|
|
3429
|
+
bcAgent,
|
|
3430
|
+
!!this._bcWasPlan.get(String(session.id))
|
|
3431
|
+
);
|
|
3432
|
+
}
|
|
3433
|
+
let payload = this._buildPayload(system, msgsForPayload, session.notes, dynBudget, session.summary);
|
|
3434
|
+
if (bcAgent && bcOpts && bcOpts.isLastStep) {
|
|
3435
|
+
/* opencode prompt.ts:1178-1181 — isLastStep: MAX_STEPS_PROMPT assistant
|
|
3436
|
+
mesajı olarak akışa eklenir; araçlar kilitlenir (yalnız metin kapanışı) */
|
|
3437
|
+
if (Array.isArray(payload.messages)) payload.messages.push({ role: 'assistant', content: opencode.MAX_STEPS_PROMPT });
|
|
3438
|
+
activeTools = [];
|
|
3439
|
+
}
|
|
3238
3440
|
// Vision model sadece mesajda görsel varsa devreye girer; terminal ise prompt'un
|
|
3239
3441
|
// shell/command işi olduğunda. Aksi halde ana model kullanılır.
|
|
3240
3442
|
let role = this._lastUserHasImage(session) ? 'vision' : null;
|
|
@@ -3283,7 +3485,7 @@ class Engine {
|
|
|
3283
3485
|
type: 'status',
|
|
3284
3486
|
status: `🧠 düşük uyumlu sağlayıcı: geçmişteki düşünme içerikleri temizlendi (${dropped} mesaj) — tur yeniden deneniyor`,
|
|
3285
3487
|
});
|
|
3286
|
-
payload = this._buildPayload(system,
|
|
3488
|
+
payload = this._buildPayload(system, msgsForPayload, session.notes, dynBudget, session.summary);
|
|
3287
3489
|
res = await this._streamWithFallbacks(
|
|
3288
3490
|
session,
|
|
3289
3491
|
payload,
|
|
@@ -3317,7 +3519,7 @@ class Engine {
|
|
|
3317
3519
|
type: 'status',
|
|
3318
3520
|
status: `\u{1F5BC} bu model görüntü girişini desteklemiyor — sohbetten ${removed} görsel kaldırıldı, mesaj görüntüsüz yanıtlanıyor (/change ile görsel destekli modele geçebilirsin)`,
|
|
3319
3521
|
});
|
|
3320
|
-
payload = this._buildPayload(system,
|
|
3522
|
+
payload = this._buildPayload(system, msgsForPayload, session.notes, dynBudget, session.summary);
|
|
3321
3523
|
res = await this._streamWithFallbacks(
|
|
3322
3524
|
session,
|
|
3323
3525
|
payload,
|
|
@@ -3395,9 +3597,17 @@ class Engine {
|
|
|
3395
3597
|
doom-loop koruması + kullanıcı ■ kırar. bg ajanlarda kapanış
|
|
3396
3598
|
disiplini için tavan kalır. */
|
|
3397
3599
|
const runDef = this._agentDefFor(session, !!session.bgJob);
|
|
3600
|
+
/* opencode mantığı (BC): ajan çözümleme — build/plan ajanı ruleset'iyle
|
|
3601
|
+
beraber tur boyunca kullanılır (permission ask + disabled filtreleri) */
|
|
3602
|
+
const bcAgent = session.bcCode ? this._bcAgentInfo(session) : null;
|
|
3603
|
+
/* opencode prompt.ts:1178 (birebir): maxSteps = agent.steps ?? Infinity —
|
|
3604
|
+
build/plan ajanlarında steps tanımsızdır → sınırsız; bağlam dolunca
|
|
3605
|
+
compaction özetler, sonsuz döngüyü doom-loop koruması + ■ kırar. */
|
|
3398
3606
|
const maxTurns = session.bgJob
|
|
3399
3607
|
? Math.max(2, Math.min(60, (runDef && runDef.steps) || MAX_TURNS))
|
|
3400
|
-
:
|
|
3608
|
+
: bcAgent
|
|
3609
|
+
? (bcAgent.steps ?? Infinity)
|
|
3610
|
+
: Infinity;
|
|
3401
3611
|
const recentSigs = []; // doom-loop dedektörü: son araç imzaları
|
|
3402
3612
|
/* Beast Code canlı önizleme: run boyunca üretilen eserleri topla —
|
|
3403
3613
|
iş bitince dahili tarayıcıda CANLI açılır (site/dev server/HTML) */
|
|
@@ -3446,9 +3656,12 @@ class Engine {
|
|
|
3446
3656
|
/* opencode runLoop portu: istek kurulmadan ÖNCEKİ mesaj sayısı —
|
|
3447
3657
|
koşan tur SIRASINDA gelen (steer) user mesajlarını saptar */
|
|
3448
3658
|
const preLen = session.messages.length;
|
|
3659
|
+
/* opencode prompt.ts:1178-1181: isLastStep = step >= maxSteps — son
|
|
3660
|
+
turda MAX_STEPS_PROMPT enjekte edilir ve araçlar kilitlenir */
|
|
3661
|
+
const isLastStep = !!bcAgent && maxTurns !== Infinity && turn >= maxTurns - 1;
|
|
3449
3662
|
const res = await this._chatTurn(session, ctrl.signal, (delta) =>
|
|
3450
3663
|
emit({ type: 'token', delta })
|
|
3451
|
-
);
|
|
3664
|
+
, bcAgent ? opencode.toolmap.definitions() : undefined, bcAgent ? { isLastStep } : null);
|
|
3452
3665
|
/* opencode port (processor.ts:477-482): step-finish usage'ı compaction
|
|
3453
3666
|
tetiğine besle — gerçek toplam biliniyorsa tahmine gerek kalmaz */
|
|
3454
3667
|
const uTot =
|
|
@@ -3533,14 +3746,16 @@ class Engine {
|
|
|
3533
3746
|
|
|
3534
3747
|
// Paralel yürütme — bağımsız çağrılar beklemesin
|
|
3535
3748
|
session.toolsSinceReflect = (session.toolsSinceReflect || 0) + res.toolCalls.length;
|
|
3536
|
-
if (!this._knownToolNames) this._knownToolNames = new Set(TOOLS.map((t) => t.function.name));
|
|
3749
|
+
if (!this._knownToolNames) this._knownToolNames = new Set([...TOOLS.map((t) => t.function.name), ...opencode.toolmap.names()]);
|
|
3537
3750
|
await Promise.all(
|
|
3538
3751
|
res.toolCalls.map(async (tc) => {
|
|
3539
3752
|
let name = tc.function && tc.function.name;
|
|
3540
3753
|
/* opencode port (llm.ts:296-312 repairToolCall): model aracı adını
|
|
3541
|
-
yanlış yazdıysa büyük/küçük harf normalizasyonuyle onar — tur kaybı yok
|
|
3754
|
+
yanlış yazdıysa büyük/küçük harf normalizasyonuyle onar — tur kaybı yok.
|
|
3755
|
+
BC'de havuz opencode builtin seti (read/edit/write/bash...) */
|
|
3542
3756
|
if (name && !this._knownToolNames.has(name)) {
|
|
3543
|
-
const
|
|
3757
|
+
const pool = bcAgent ? opencode.toolmap.definitions() : TOOLS;
|
|
3758
|
+
const fixed = pool.find((t) => t.function.name.toLowerCase() === String(name).trim().toLowerCase());
|
|
3544
3759
|
if (fixed) {
|
|
3545
3760
|
emitSafe(this, sid, {
|
|
3546
3761
|
type: 'status',
|
|
@@ -3556,33 +3771,59 @@ class Engine {
|
|
|
3556
3771
|
emit({ type: 'tool-start', callId: tc.id, name, args });
|
|
3557
3772
|
emit({ type: 'status', status: name });
|
|
3558
3773
|
/* opencode port (processor.ts:29 + 356-380): aynı araç + birebir
|
|
3559
|
-
aynı argüman 3 kez koştuysa 4.'
|
|
3560
|
-
akıtma;
|
|
3774
|
+
aynı argüman 3 kez koştuysa 4.'yi çalıştırma — döngüye para/hız
|
|
3775
|
+
akıtma; BC'de opencode disiplini: doom_loop İZİN ister (defaults:
|
|
3776
|
+
doom_loop ask) — kullanıcı onaylarsa koşmaya devam eder */
|
|
3561
3777
|
const sig = name + '\u0000' + String((tc.function && tc.function.arguments) || '');
|
|
3562
3778
|
const dups = recentSigs.reduce((a, s) => a + (s === sig ? 1 : 0), 0);
|
|
3563
3779
|
recentSigs.push(sig);
|
|
3564
3780
|
let out;
|
|
3565
3781
|
if (dups >= DOOM_LOOP_THRESHOLD) {
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3782
|
+
if (bcAgent) {
|
|
3783
|
+
try {
|
|
3784
|
+
await this._perm.ask({
|
|
3785
|
+
sessionId: sid,
|
|
3786
|
+
ruleset: bcAgent.permission,
|
|
3787
|
+
permission: 'doom_loop',
|
|
3788
|
+
patterns: [name],
|
|
3789
|
+
always: [name],
|
|
3790
|
+
metadata: { callCount: DOOM_LOOP_THRESHOLD, arguments: (tc.function && tc.function.arguments) || '' },
|
|
3791
|
+
tool: { name, args },
|
|
3792
|
+
});
|
|
3793
|
+
out = await this._execTool(name, args, ctrl.signal, sid);
|
|
3794
|
+
} catch (permErr) {
|
|
3795
|
+
out = JSON.stringify({
|
|
3796
|
+
error:
|
|
3797
|
+
`doom-loop: ${name} aynı argümanlarla ${DOOM_LOOP_THRESHOLD} kez koştu. ` +
|
|
3798
|
+
AskService.errorText(permErr) +
|
|
3799
|
+
' Aynı çağrıyı tekrarlamak işe yaramaz — farklı bir yöntem/argüman dene ya da eldeki bilgilerle nihai cevabı ver.',
|
|
3800
|
+
});
|
|
3801
|
+
}
|
|
3802
|
+
} else {
|
|
3803
|
+
out = JSON.stringify({
|
|
3804
|
+
error:
|
|
3805
|
+
`doom-loop: ${name} aynı argümanlarla ${DOOM_LOOP_THRESHOLD} kez çalıştı ve hep aynı sonucu verdi. ` +
|
|
3806
|
+
'Aynı çağrıyı tekrarlamak işe yaramaz — farklı bir yöntem/argüman dene ya da eldeki bilgilerle nihai cevabı ver.',
|
|
3807
|
+
});
|
|
3808
|
+
emitSafe(this, sid, { type: 'status', status: `⛔ doom-loop: ${name} tekrarı engellendi` });
|
|
3809
|
+
}
|
|
3572
3810
|
} else {
|
|
3573
3811
|
out = await this._execTool(name, args, ctrl.signal, sid);
|
|
3574
3812
|
}
|
|
3575
|
-
/* Beast Code eser takibi: yazılan HTML + başlatılan dev server
|
|
3813
|
+
/* Beast Code eser takibi: yazılan HTML + başlatılan dev server.
|
|
3814
|
+
BC'de araçlar opencode adlarıyla gelir: write/edit (filePath),
|
|
3815
|
+
bash (command) — opencode şemasına göre yakalanır */
|
|
3576
3816
|
if (bcArtifacts) {
|
|
3577
3817
|
try {
|
|
3818
|
+
const filePathOc = args && (args.filePath ?? args.path);
|
|
3578
3819
|
if (
|
|
3579
|
-
(name === 'write_file' || name === 'edit_file') &&
|
|
3580
|
-
|
|
3820
|
+
(name === 'write' || name === 'edit' || name === 'write_file' || name === 'edit_file') &&
|
|
3821
|
+
filePathOc && /\.html?$/i.test(String(filePathOc))
|
|
3581
3822
|
) {
|
|
3582
|
-
bcArtifacts.html.push(String(
|
|
3823
|
+
bcArtifacts.html.push(String(filePathOc));
|
|
3583
3824
|
}
|
|
3584
|
-
if (name === 'run_command') {
|
|
3585
|
-
const cmdStr = String((args && args.command) || '');
|
|
3825
|
+
if (name === 'bash' || name === 'run_command') {
|
|
3826
|
+
const cmdStr = String((args && (args.command ?? args.cmd)) || '');
|
|
3586
3827
|
const hay = cmdStr + '\n' + String(out || '').slice(0, 6000);
|
|
3587
3828
|
const m = /https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d{2,5})?[^\s'"<>]*/i.exec(hay);
|
|
3588
3829
|
if (m) {
|
|
@@ -3598,7 +3839,7 @@ class Engine {
|
|
|
3598
3839
|
if (port) bcArtifacts.serverUrl = 'http://localhost:' + port;
|
|
3599
3840
|
}
|
|
3600
3841
|
/* Expo dev server: exp://<LAN-IP>:<port> çıktıdan yakalanır —
|
|
3601
|
-
|
|
3842
|
+
telefon/Expo Go için QR renderer'a canlı iletilir */
|
|
3602
3843
|
const xm = /exp:\/\/[^\s'"<>]+/i.exec(hay);
|
|
3603
3844
|
if (xm) {
|
|
3604
3845
|
const eurl = xm[0].replace(/[)\].,;:'"]+$/, '');
|
|
@@ -3609,7 +3850,7 @@ class Engine {
|
|
|
3609
3850
|
}
|
|
3610
3851
|
}
|
|
3611
3852
|
} catch {}
|
|
3612
|
-
}
|
|
3853
|
+
}
|
|
3613
3854
|
// Ekran görüntüsü gibi araçlar görseli sonraki tura enjekte eder
|
|
3614
3855
|
let injectedImage = null;
|
|
3615
3856
|
/* opencode ctx.metadata portu: edit/write sonucundaki diffView, LLM
|
|
@@ -3627,7 +3868,7 @@ class Engine {
|
|
|
3627
3868
|
}
|
|
3628
3869
|
if (injectedImage || diffView) out = JSON.stringify(obj);
|
|
3629
3870
|
} catch {}
|
|
3630
|
-
const toolMsg = { role: 'tool', tool_call_id: tc.id, name, content: out.slice(0, name === 'read_file' ? READ_OUT_KEEP : TOOL_OUT_KEEP * 6) };
|
|
3871
|
+
const toolMsg = { role: 'tool', tool_call_id: tc.id, name, content: out.slice(0, (name === 'read' || name === 'read_file') ? READ_OUT_KEEP : TOOL_OUT_KEEP * 6) };
|
|
3631
3872
|
if (diffView) toolMsg.diffView = diffView; /* oturum dosyasına da düşer — geçmiş açılınca diff yeniden çizilir */
|
|
3632
3873
|
session.messages.push(toolMsg);
|
|
3633
3874
|
try {
|
|
@@ -4090,31 +4331,70 @@ const skills = require('./skills');
|
|
|
4090
4331
|
|
|
4091
4332
|
/* ---------- alt-agent ---------- */
|
|
4092
4333
|
|
|
4093
|
-
async _subagent(task, context, parentSignal, sessionId) {
|
|
4334
|
+
async _subagent(task, context, parentSignal, sessionId, subagentType = '') {
|
|
4094
4335
|
const ctrl = new AbortController();
|
|
4095
4336
|
const onAbort = () => ctrl.abort();
|
|
4096
4337
|
if (parentSignal) {
|
|
4097
4338
|
if (parentSignal.aborted) throw new Error('iptal edildi');
|
|
4098
4339
|
parentSignal.addEventListener('abort', onAbort, { once: true });
|
|
4099
4340
|
}
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
).sort((a, b) => String(a.function.name).localeCompare(String(b.function.name))); // önek-cache: sabit sıra
|
|
4341
|
+
/* opencode task aracı (tool/task.ts) subagent_tipi ZORUNLU kılar: BC'den
|
|
4342
|
+
gelen task çağrılarında 'general' | 'explore' ajanları opencode mantığıyla
|
|
4343
|
+
koşar (agent tanımı + permission + opencode builtin seti). Eski
|
|
4344
|
+
delegate_task (BC dışı) davranışı aynen korunur. */
|
|
4345
|
+
const ocType = subagentType === 'explore' || subagentType === 'general' ? subagentType : '';
|
|
4106
4346
|
const role = 'subagent';
|
|
4107
4347
|
const sel = this.modelFor(role) || this.sel;
|
|
4108
4348
|
if (this.roleModels[role]) {
|
|
4109
4349
|
emitSafe(this, sessionId, { type: 'status', status: `rol: ${role} → ${sel.providerName} · ${sel.model}` });
|
|
4110
4350
|
}
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4351
|
+
let subTools;
|
|
4352
|
+
let system;
|
|
4353
|
+
let ocMode = false;
|
|
4354
|
+
if (ocType) {
|
|
4355
|
+
ocMode = true;
|
|
4356
|
+
const ws = this._sessionWorkspace(sessionId);
|
|
4357
|
+
const agentInfo =
|
|
4358
|
+
opencode.agents.get(ocType, { worktree: ws, promptExplore: opencode.PROMPT_EXPLORE, user: {} }) || null;
|
|
4359
|
+
const ruleset = agentInfo ? agentInfo.permission : [];
|
|
4360
|
+
/* opencode child session: subagent ANA döngüdeki gibi ajan tanımıyla
|
|
4361
|
+
koşar — system = [ajan promptu yoksa model-bazlı prompt, env] */
|
|
4362
|
+
const main = agentInfo && agentInfo.prompt ? [agentInfo.prompt] : opencode.systemprompt.provider(sel.model);
|
|
4363
|
+
system = [
|
|
4364
|
+
...main,
|
|
4365
|
+
...opencode.systemprompt.environment({
|
|
4366
|
+
modelId: sel.model,
|
|
4367
|
+
providerId: sel.providerId || '?',
|
|
4368
|
+
directory: ws,
|
|
4369
|
+
worktree: ws,
|
|
4370
|
+
}),
|
|
4371
|
+
]
|
|
4372
|
+
.filter((x) => x)
|
|
4373
|
+
.join('\n');
|
|
4374
|
+
/* opencode SessionTools.resolve: ajanın permission'ında '*' deny olan
|
|
4375
|
+
araçlar setten düşer — explore: salt-okur (bash dahil okuma amaçlı allow),
|
|
4376
|
+
general: todowrite yok (opencode general tanımı birebir) */
|
|
4377
|
+
const defs = opencode.toolmap.definitions();
|
|
4378
|
+
const hidden = opencode.Permission.disabled(defs.map((t) => t.function.name), ruleset);
|
|
4379
|
+
subTools = defs
|
|
4380
|
+
.filter((t) => !hidden.has(t.function.name))
|
|
4381
|
+
.filter((t) => t.function.name !== 'task')
|
|
4382
|
+
.sort((a, b) => String(a.function.name).localeCompare(String(b.function.name)));
|
|
4383
|
+
} else {
|
|
4384
|
+
subTools = TOOLS.filter(
|
|
4385
|
+
(t) =>
|
|
4386
|
+
t.function.name !== 'delegate_task' &&
|
|
4387
|
+
t.function.name !== 'set_reminder' &&
|
|
4388
|
+
!t.function.name.startsWith('email_')
|
|
4389
|
+
).sort((a, b) => String(a.function.name).localeCompare(String(b.function.name))); // önek-cache: sabit sıra
|
|
4390
|
+
system =
|
|
4391
|
+
`Sen odaklı bir alt-agentsın. Ana ajansın verdiği TEK görevi bitir ve sadece nihai sonucu döndür.\n` +
|
|
4392
|
+
`Windows + PowerShell, çalışma klasörü: ${this.workspace}\n` +
|
|
4393
|
+
`HIZ KURALI: döngü gerektiren işleri (çok sayfa okuma, çok URL çekme, tekrarlı hesap/parse) TEK python_run betiğinde topluca yap — her adım için ayrı araç turu harcama; 10 fetch = 1 betik = çok daha hızlı görev.\n` +
|
|
4394
|
+
`ARAŞTIRMA SINIRI: 3-5 kaynak yeter; 2-3 denemede bulamazsan kısmi sonucu getir, takılma.\n` +
|
|
4395
|
+
`Kısa çalış, gereksiz soru sorma, sonucu madde madde raporla.\n` +
|
|
4396
|
+
FORMAT_RULES;
|
|
4397
|
+
}
|
|
4118
4398
|
const user = context ? `${task}\n\n# BAĞLAM\n${context}` : task;
|
|
4119
4399
|
const msgs = [
|
|
4120
4400
|
{ role: 'system', content: system },
|
|
@@ -4154,8 +4434,15 @@ const skills = require('./skills');
|
|
|
4154
4434
|
try {
|
|
4155
4435
|
args = JSON.parse((tc.function && tc.function.arguments) || '{}');
|
|
4156
4436
|
} catch {}
|
|
4157
|
-
|
|
4158
|
-
|
|
4437
|
+
let subName = tc.function && tc.function.name;
|
|
4438
|
+
if (ocMode) {
|
|
4439
|
+
/* opencode builtin dilinde çağırır → Beast aracına çevir */
|
|
4440
|
+
const mapped = opencode.toolmap.execMap(subName, args);
|
|
4441
|
+
subName = mapped.name;
|
|
4442
|
+
args = mapped.args;
|
|
4443
|
+
}
|
|
4444
|
+
const out = await this._subExecTool(subName, args, ctrl.signal);
|
|
4445
|
+
msgs.push({ role: 'tool', tool_call_id: tc.id, name: subName, content: out });
|
|
4159
4446
|
}
|
|
4160
4447
|
}
|
|
4161
4448
|
const last = [...msgs].reverse().find((m) => m.role === 'assistant' && m.content);
|
|
@@ -4291,27 +4578,85 @@ const skills = require('./skills');
|
|
|
4291
4578
|
|
|
4292
4579
|
async _execTool(name, args, signal, sessionId) {
|
|
4293
4580
|
try {
|
|
4581
|
+
/* opencode mantığı (BC): model opencode builtin dilinde çağırır —
|
|
4582
|
+
read/edit/write/bash/todowrite/task... execMap Beast'in yerleşik
|
|
4583
|
+
aracına çevirir (implementasyon değişmez, dil opencode olur).
|
|
4584
|
+
Studio/Finance/bgJob kendi dünyasında koşar — bu akışa girmez. */
|
|
4585
|
+
const bcSess = this.cache.get(String(sessionId || '')) || null;
|
|
4586
|
+
const isBc = !!(bcSess && bcSess.bcCode && !bcSess.studio && !bcSess.finance && !bcSess.bgJob);
|
|
4587
|
+
if (isBc) {
|
|
4588
|
+
const mapped = opencode.toolmap.execMap(name, args);
|
|
4589
|
+
name = mapped.name;
|
|
4590
|
+
args = mapped.args;
|
|
4591
|
+
}
|
|
4294
4592
|
/* ANA KOD KİLİDİ: korumalı bölgeye yazma/silme girişimi daha kapıdan geçmez */
|
|
4295
4593
|
const blocked = this._guardTool(name, args);
|
|
4296
4594
|
if (blocked) return blocked;
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
let ok = false;
|
|
4595
|
+
if (isBc) {
|
|
4596
|
+
/* opencode permission akışı (tool/external-directory.ts + her aracın
|
|
4597
|
+
ctx.ask'ı): önce workspace DIŞI erişim izni, sonra araç izni.
|
|
4598
|
+
deny anında hata döner (asla sorulmaz), allow sessiz geçer,
|
|
4599
|
+
ask UI'ya 'permission.asked' eventi ile sorulur */
|
|
4600
|
+
const ws = this._sessionWorkspace(sessionId);
|
|
4601
|
+
const bcAgent = this._bcAgentInfo(bcSess);
|
|
4602
|
+
const ruleset = bcAgent ? bcAgent.permission : [];
|
|
4306
4603
|
try {
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4604
|
+
const ext = this._bcExternalCheck(name, args, ws);
|
|
4605
|
+
if (ext) {
|
|
4606
|
+
await this._perm.ask({ sessionId, ruleset, ...ext, metadata: { tool: name, args: args || {} } });
|
|
4607
|
+
}
|
|
4608
|
+
const perm = this._bcPermissionFor(name, args, ws);
|
|
4609
|
+
await this._perm.ask({
|
|
4610
|
+
sessionId,
|
|
4611
|
+
ruleset,
|
|
4612
|
+
permission: perm.permission,
|
|
4613
|
+
patterns: perm.patterns,
|
|
4614
|
+
always: perm.always,
|
|
4615
|
+
metadata: { args: args || {} },
|
|
4616
|
+
tool: { name, args: args || {} },
|
|
4314
4617
|
});
|
|
4618
|
+
} catch (permErr) {
|
|
4619
|
+
return JSON.stringify({ ok: false, error: AskService.errorText(permErr) });
|
|
4620
|
+
}
|
|
4621
|
+
/* opencode skill aracı: katalogdaki SKILL.md gövdesini modele açar */
|
|
4622
|
+
if (name === 'skill') {
|
|
4623
|
+
const wanted = String((args && args.name) || '').trim();
|
|
4624
|
+
let list = [];
|
|
4625
|
+
try { list = skills.scan(); } catch {}
|
|
4626
|
+
const hit = list.find((s) => s && s.name === wanted) || null;
|
|
4627
|
+
if (!hit) {
|
|
4628
|
+
return JSON.stringify({
|
|
4629
|
+
ok: false,
|
|
4630
|
+
error:
|
|
4631
|
+
'skill bulunamadı: ' + (wanted || '(boş)') +
|
|
4632
|
+
(list.length ? ' — mevcut: ' + list.map((s) => s.name).join(', ') : ''),
|
|
4633
|
+
});
|
|
4634
|
+
}
|
|
4635
|
+
let body = '';
|
|
4636
|
+
try { body = fs.readFileSync(hit.path, 'utf8'); } catch {}
|
|
4637
|
+
return JSON.stringify({ ok: true, name: hit.name, path: hit.path, content: body.slice(0, 24000) });
|
|
4638
|
+
}
|
|
4639
|
+
} else {
|
|
4640
|
+
/* onay kapısı: riskli araçta dış onay bekle; reddedilirse araç çalışmaz.
|
|
4641
|
+
"always" onaylı araçlar doğrudan geçer. (BC dışı oturumlar — opencode
|
|
4642
|
+
izin sistemi yalnız Beast Code oturumlarında koşar) */
|
|
4643
|
+
if (
|
|
4644
|
+
(Engine.RISKY_TOOLS.has(name) || String(name).startsWith('mcp__')) &&
|
|
4645
|
+
!this.alwaysAllowTools.has(name) &&
|
|
4646
|
+
this.approvals && typeof this.approvals.request === 'function'
|
|
4647
|
+
) {
|
|
4648
|
+
emitSafe(this, sessionId, { type: 'status', status: `onay bekleniyor: ${name}` });
|
|
4649
|
+
let ok = false;
|
|
4650
|
+
try {
|
|
4651
|
+
ok = !!(await this.approvals.request({ sessionId, tool: name, args: args || {} }));
|
|
4652
|
+
} catch {}
|
|
4653
|
+
if (!ok) {
|
|
4654
|
+
return JSON.stringify({
|
|
4655
|
+
ok: false,
|
|
4656
|
+
error:
|
|
4657
|
+
'kullanıcı bu işlemi ONAYLAMADI (iptal edildi veya zaman aşımı). İşlemi yeniden deneme; kullanıcıya sormadan alternatif öner.',
|
|
4658
|
+
});
|
|
4659
|
+
}
|
|
4315
4660
|
}
|
|
4316
4661
|
}
|
|
4317
4662
|
/* GERİ ALMA GÜNLÜĞÜ: dosya yazımından ÖNCE eski içerik kayda geçer */
|
|
@@ -4406,8 +4751,8 @@ const skills = require('./skills');
|
|
|
4406
4751
|
if (name === 'delegate_task') {
|
|
4407
4752
|
const task = String(args.task || '').trim();
|
|
4408
4753
|
if (!task) return JSON.stringify({ ok: false, error: 'task gerekli' });
|
|
4409
|
-
emitSafe(this, sessionId, { type: 'status', status: 'delegate_task' });
|
|
4410
|
-
const result = await this._subagent(task, String(args.context || ''), signal, sessionId);
|
|
4754
|
+
emitSafe(this, sessionId, { type: 'status', status: args.subagent_type === 'explore' ? 'task(explore)' : args.subagent_type ? 'task(general)' : 'delegate_task' });
|
|
4755
|
+
const result = await this._subagent(task, String(args.context || ''), signal, sessionId, String(args.subagent_type || ''));
|
|
4411
4756
|
return JSON.stringify({ ok: true, task, result: String(result).slice(0, 12000) });
|
|
4412
4757
|
}
|
|
4413
4758
|
if (name === 'bot_dm') {
|