beast-agent 2.15.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 +417 -154
- 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/main.js +93 -41
- package/src/preload.js +2 -0
- package/src/renderer/renderer.js +59 -15
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
|
|
@@ -24,6 +24,10 @@ const apps = require('./apps');
|
|
|
24
24
|
const pdfwrite = require('./pdfwrite');
|
|
25
25
|
const { estTokens, estMsgTokens } = require('./tokens');
|
|
26
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;
|
|
27
31
|
|
|
28
32
|
const MAX_TURNS = 40;
|
|
29
33
|
const SUB_MAX_TURNS = 8;
|
|
@@ -137,15 +141,9 @@ const PERM_TOOL_SETS = {
|
|
|
137
141
|
};
|
|
138
142
|
const PERM_LEVELS = ['all', 'web', 'read', 'chat'];
|
|
139
143
|
|
|
140
|
-
/* opencode plan
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
'read_file', 'list_dir', 'grep', 'glob',
|
|
144
|
-
'web_search', 'http_fetch', 'webfetch', 'deep_search',
|
|
145
|
-
'browser_open', 'browser_read', 'browser_snapshot',
|
|
146
|
-
'todo_write', 'memory_search', 'kb_search', 'ocr_read',
|
|
147
|
-
'git_diff_review', 'repo_map', 'repo_symbols', 'xlsx_read',
|
|
148
|
-
]);
|
|
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ığı) */
|
|
149
147
|
|
|
150
148
|
/* İzin değerini normalize eder: 'all' → ['all'], 'web' → ['web'],
|
|
151
149
|
'web,read' / ['web','read'] → ['web','read'] (sıra PERM_LEVELS'e göre dizilir).
|
|
@@ -313,6 +311,15 @@ class Engine {
|
|
|
313
311
|
this.notifyOwnerFail = opts.notifyOwnerFail !== false; // #25 hata mail bildirimi (runtime /notify)
|
|
314
312
|
this.reminders = opts.reminders || null; // hatırlatıcı kancası (main enjekte eder)
|
|
315
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
|
|
316
323
|
/* onay kapısı: riskli araç öncesi dış onay beklenir ({request} async) */
|
|
317
324
|
this.approvals = opts.approvals || null;
|
|
318
325
|
/* "bir daha sorma" onaylanan araçlar — bu setteki riskli araçlar sorulmaz */
|
|
@@ -942,11 +949,10 @@ class Engine {
|
|
|
942
949
|
return lv.effort || null;
|
|
943
950
|
}
|
|
944
951
|
|
|
945
|
-
/*
|
|
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) */
|
|
946
954
|
_thinkEffortFor(session) {
|
|
947
|
-
|
|
948
|
-
if (!session || !session.bcCode) return e;
|
|
949
|
-
return e ? 'low' : null;
|
|
955
|
+
return this._thinkEffort();
|
|
950
956
|
}
|
|
951
957
|
|
|
952
958
|
_thinkLabel() {
|
|
@@ -1525,56 +1531,151 @@ class Engine {
|
|
|
1525
1531
|
return out;
|
|
1526
1532
|
}
|
|
1527
1533
|
|
|
1528
|
-
/*
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
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);
|
|
1547
1598
|
const proj = this._projectInstructions(session);
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
:
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
'
|
|
1570
|
-
'- Bağımsız araç çağrılarını AYNI TURDA paralel ver (birden çok read_file tek turda).\n' +
|
|
1571
|
-
'- Dosyayı kullanıcı söylememişse list_dir ile yapıyı görüp kendin karar ver.\n' +
|
|
1572
|
-
'- Run_command PowerShell ortamında çalışır (Windows): KALICI oturum — cd ve $env değişkenleri çağrılar arasında korunur; büyük çıktıda tam çıktı geçici dosyaya düşer, read_file ile oku. shell:"bash" verirsen git-bash ile bash sözdizimi koşar.\n' +
|
|
1573
|
-
'- Soru sorma, sohbet etme; uygulayıp özetle. Kullanıcı /plan /build /auto ile modu değiştirir.\n' +
|
|
1574
|
-
FORMAT_RULES
|
|
1575
|
-
);
|
|
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');
|
|
1576
1621
|
}
|
|
1577
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
|
+
}
|
|
1661
|
+
}
|
|
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
|
+
|
|
1578
1679
|
/* Beast Studio (video paneli): video YAPMA ve DÜZENLEME ajanı. Workspace'teki
|
|
1579
1680
|
her şey video proje malzemesi olarak görülür (ham görüntü, ses, görsel,
|
|
1580
1681
|
altyazı, çıktı). Medya işi ffprobe + ffmpeg ile yürütür; ikisi de app ile
|
|
@@ -3211,36 +3312,27 @@ class Engine {
|
|
|
3211
3312
|
session.messages = keep;
|
|
3212
3313
|
}
|
|
3213
3314
|
|
|
3214
|
-
async _chatTurn(session, signal, onDelta, toolsList = TOOLS) {
|
|
3215
|
-
/*
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
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
|
+
}
|
|
3225
3333
|
}
|
|
3226
3334
|
/* opencode agent.ts port: özel ajan tanımı — prompt/model/araç/steps */
|
|
3227
3335
|
const adef = this._agentDefFor(session, !!session.bgJob);
|
|
3228
|
-
/* Beast Code: todo_write açıklaması "3+ adım" kısıtı içerir ve model küçük
|
|
3229
|
-
işlerde atlar — bcCode oturumunda HER işte İLK araç olacak şekilde değiştir */
|
|
3230
|
-
if (session && session.bcCode) {
|
|
3231
|
-
toolsList = toolsList.map((t) =>
|
|
3232
|
-
t && t.function && t.function.name === 'todo_write'
|
|
3233
|
-
? {
|
|
3234
|
-
...t,
|
|
3235
|
-
function: {
|
|
3236
|
-
...t.function,
|
|
3237
|
-
description:
|
|
3238
|
-
'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.',
|
|
3239
|
-
},
|
|
3240
|
-
}
|
|
3241
|
-
: t
|
|
3242
|
-
);
|
|
3243
|
-
}
|
|
3244
3336
|
const promptText = this._lastUserText(session);
|
|
3245
3337
|
let system = session.bgJob
|
|
3246
3338
|
? this.buildBgSystem(session)
|
|
@@ -3248,18 +3340,21 @@ class Engine {
|
|
|
3248
3340
|
? this.buildStudioSystem(session)
|
|
3249
3341
|
: session.finance
|
|
3250
3342
|
? this.buildFinanceSystem(session)
|
|
3251
|
-
:
|
|
3252
|
-
? this.
|
|
3253
|
-
: await this.buildSystem(promptText, session);
|
|
3343
|
+
: bcAgent
|
|
3344
|
+
? this.buildOcSystem(session, bcAgent)
|
|
3345
|
+
: await this.buildSystem(promptText, session);
|
|
3254
3346
|
// Granül izin: oturumun yetki seviyesine göre araç seti daraltılır.
|
|
3255
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).
|
|
3256
3349
|
const perms = this.sessionPermFor(session.id);
|
|
3257
3350
|
let allowedSet = null;
|
|
3258
3351
|
if (!perms.includes('all')) {
|
|
3259
3352
|
allowedSet = new Set();
|
|
3260
3353
|
for (const p of perms) for (const t of PERM_TOOL_SETS[p] || []) allowedSet.add(t);
|
|
3261
3354
|
}
|
|
3262
|
-
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;
|
|
3263
3358
|
/* Beast Finance: mt5_* araçları YALNIZ finance oturumlarında görünür */
|
|
3264
3359
|
if (FINANCE_TOOL_SET.size && !(session && session.finance)) {
|
|
3265
3360
|
activeTools = activeTools.filter((t) => !FINANCE_TOOL_SET.has(t.function.name));
|
|
@@ -3276,9 +3371,16 @@ class Engine {
|
|
|
3276
3371
|
/* CEO: uygulayıcı araçlar kapalı — her şey paralel ajana devredilir */
|
|
3277
3372
|
activeTools = activeTools.filter((t) => !CEO_EXEC_TOOLS.has(t.function.name));
|
|
3278
3373
|
}
|
|
3279
|
-
if (
|
|
3280
|
-
/* opencode
|
|
3281
|
-
|
|
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));
|
|
3282
3384
|
}
|
|
3283
3385
|
if (adef && adef.tools) {
|
|
3284
3386
|
/* özel ajan araç beyaz listesi — tanımda olmayan araç görünmez */
|
|
@@ -3316,7 +3418,25 @@ class Engine {
|
|
|
3316
3418
|
/* opencode port: model bağlamına göre dinamik bütçe + compaction özeti */
|
|
3317
3419
|
const selEarly = this.sessionModel.get(String(session.id)) || this.modelFor(null) || this.sel;
|
|
3318
3420
|
const dynBudget = this._historyBudget(selEarly);
|
|
3319
|
-
|
|
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
|
+
}
|
|
3320
3440
|
// Vision model sadece mesajda görsel varsa devreye girer; terminal ise prompt'un
|
|
3321
3441
|
// shell/command işi olduğunda. Aksi halde ana model kullanılır.
|
|
3322
3442
|
let role = this._lastUserHasImage(session) ? 'vision' : null;
|
|
@@ -3365,7 +3485,7 @@ class Engine {
|
|
|
3365
3485
|
type: 'status',
|
|
3366
3486
|
status: `🧠 düşük uyumlu sağlayıcı: geçmişteki düşünme içerikleri temizlendi (${dropped} mesaj) — tur yeniden deneniyor`,
|
|
3367
3487
|
});
|
|
3368
|
-
payload = this._buildPayload(system,
|
|
3488
|
+
payload = this._buildPayload(system, msgsForPayload, session.notes, dynBudget, session.summary);
|
|
3369
3489
|
res = await this._streamWithFallbacks(
|
|
3370
3490
|
session,
|
|
3371
3491
|
payload,
|
|
@@ -3399,7 +3519,7 @@ class Engine {
|
|
|
3399
3519
|
type: 'status',
|
|
3400
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)`,
|
|
3401
3521
|
});
|
|
3402
|
-
payload = this._buildPayload(system,
|
|
3522
|
+
payload = this._buildPayload(system, msgsForPayload, session.notes, dynBudget, session.summary);
|
|
3403
3523
|
res = await this._streamWithFallbacks(
|
|
3404
3524
|
session,
|
|
3405
3525
|
payload,
|
|
@@ -3477,9 +3597,17 @@ class Engine {
|
|
|
3477
3597
|
doom-loop koruması + kullanıcı ■ kırar. bg ajanlarda kapanış
|
|
3478
3598
|
disiplini için tavan kalır. */
|
|
3479
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. */
|
|
3480
3606
|
const maxTurns = session.bgJob
|
|
3481
3607
|
? Math.max(2, Math.min(60, (runDef && runDef.steps) || MAX_TURNS))
|
|
3482
|
-
:
|
|
3608
|
+
: bcAgent
|
|
3609
|
+
? (bcAgent.steps ?? Infinity)
|
|
3610
|
+
: Infinity;
|
|
3483
3611
|
const recentSigs = []; // doom-loop dedektörü: son araç imzaları
|
|
3484
3612
|
/* Beast Code canlı önizleme: run boyunca üretilen eserleri topla —
|
|
3485
3613
|
iş bitince dahili tarayıcıda CANLI açılır (site/dev server/HTML) */
|
|
@@ -3528,9 +3656,12 @@ class Engine {
|
|
|
3528
3656
|
/* opencode runLoop portu: istek kurulmadan ÖNCEKİ mesaj sayısı —
|
|
3529
3657
|
koşan tur SIRASINDA gelen (steer) user mesajlarını saptar */
|
|
3530
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;
|
|
3531
3662
|
const res = await this._chatTurn(session, ctrl.signal, (delta) =>
|
|
3532
3663
|
emit({ type: 'token', delta })
|
|
3533
|
-
);
|
|
3664
|
+
, bcAgent ? opencode.toolmap.definitions() : undefined, bcAgent ? { isLastStep } : null);
|
|
3534
3665
|
/* opencode port (processor.ts:477-482): step-finish usage'ı compaction
|
|
3535
3666
|
tetiğine besle — gerçek toplam biliniyorsa tahmine gerek kalmaz */
|
|
3536
3667
|
const uTot =
|
|
@@ -3615,14 +3746,16 @@ class Engine {
|
|
|
3615
3746
|
|
|
3616
3747
|
// Paralel yürütme — bağımsız çağrılar beklemesin
|
|
3617
3748
|
session.toolsSinceReflect = (session.toolsSinceReflect || 0) + res.toolCalls.length;
|
|
3618
|
-
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()]);
|
|
3619
3750
|
await Promise.all(
|
|
3620
3751
|
res.toolCalls.map(async (tc) => {
|
|
3621
3752
|
let name = tc.function && tc.function.name;
|
|
3622
3753
|
/* opencode port (llm.ts:296-312 repairToolCall): model aracı adını
|
|
3623
|
-
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...) */
|
|
3624
3756
|
if (name && !this._knownToolNames.has(name)) {
|
|
3625
|
-
const
|
|
3757
|
+
const pool = bcAgent ? opencode.toolmap.definitions() : TOOLS;
|
|
3758
|
+
const fixed = pool.find((t) => t.function.name.toLowerCase() === String(name).trim().toLowerCase());
|
|
3626
3759
|
if (fixed) {
|
|
3627
3760
|
emitSafe(this, sid, {
|
|
3628
3761
|
type: 'status',
|
|
@@ -3638,33 +3771,59 @@ class Engine {
|
|
|
3638
3771
|
emit({ type: 'tool-start', callId: tc.id, name, args });
|
|
3639
3772
|
emit({ type: 'status', status: name });
|
|
3640
3773
|
/* opencode port (processor.ts:29 + 356-380): aynı araç + birebir
|
|
3641
|
-
aynı argüman 3 kez koştuysa 4.'
|
|
3642
|
-
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 */
|
|
3643
3777
|
const sig = name + '\u0000' + String((tc.function && tc.function.arguments) || '');
|
|
3644
3778
|
const dups = recentSigs.reduce((a, s) => a + (s === sig ? 1 : 0), 0);
|
|
3645
3779
|
recentSigs.push(sig);
|
|
3646
3780
|
let out;
|
|
3647
3781
|
if (dups >= DOOM_LOOP_THRESHOLD) {
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
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
|
+
}
|
|
3654
3810
|
} else {
|
|
3655
3811
|
out = await this._execTool(name, args, ctrl.signal, sid);
|
|
3656
3812
|
}
|
|
3657
|
-
/* 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 */
|
|
3658
3816
|
if (bcArtifacts) {
|
|
3659
3817
|
try {
|
|
3818
|
+
const filePathOc = args && (args.filePath ?? args.path);
|
|
3660
3819
|
if (
|
|
3661
|
-
(name === 'write_file' || name === 'edit_file') &&
|
|
3662
|
-
|
|
3820
|
+
(name === 'write' || name === 'edit' || name === 'write_file' || name === 'edit_file') &&
|
|
3821
|
+
filePathOc && /\.html?$/i.test(String(filePathOc))
|
|
3663
3822
|
) {
|
|
3664
|
-
bcArtifacts.html.push(String(
|
|
3823
|
+
bcArtifacts.html.push(String(filePathOc));
|
|
3665
3824
|
}
|
|
3666
|
-
if (name === 'run_command') {
|
|
3667
|
-
const cmdStr = String((args && args.command) || '');
|
|
3825
|
+
if (name === 'bash' || name === 'run_command') {
|
|
3826
|
+
const cmdStr = String((args && (args.command ?? args.cmd)) || '');
|
|
3668
3827
|
const hay = cmdStr + '\n' + String(out || '').slice(0, 6000);
|
|
3669
3828
|
const m = /https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d{2,5})?[^\s'"<>]*/i.exec(hay);
|
|
3670
3829
|
if (m) {
|
|
@@ -3680,7 +3839,7 @@ class Engine {
|
|
|
3680
3839
|
if (port) bcArtifacts.serverUrl = 'http://localhost:' + port;
|
|
3681
3840
|
}
|
|
3682
3841
|
/* Expo dev server: exp://<LAN-IP>:<port> çıktıdan yakalanır —
|
|
3683
|
-
|
|
3842
|
+
telefon/Expo Go için QR renderer'a canlı iletilir */
|
|
3684
3843
|
const xm = /exp:\/\/[^\s'"<>]+/i.exec(hay);
|
|
3685
3844
|
if (xm) {
|
|
3686
3845
|
const eurl = xm[0].replace(/[)\].,;:'"]+$/, '');
|
|
@@ -3691,7 +3850,7 @@ class Engine {
|
|
|
3691
3850
|
}
|
|
3692
3851
|
}
|
|
3693
3852
|
} catch {}
|
|
3694
|
-
}
|
|
3853
|
+
}
|
|
3695
3854
|
// Ekran görüntüsü gibi araçlar görseli sonraki tura enjekte eder
|
|
3696
3855
|
let injectedImage = null;
|
|
3697
3856
|
/* opencode ctx.metadata portu: edit/write sonucundaki diffView, LLM
|
|
@@ -3709,7 +3868,7 @@ class Engine {
|
|
|
3709
3868
|
}
|
|
3710
3869
|
if (injectedImage || diffView) out = JSON.stringify(obj);
|
|
3711
3870
|
} catch {}
|
|
3712
|
-
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) };
|
|
3713
3872
|
if (diffView) toolMsg.diffView = diffView; /* oturum dosyasına da düşer — geçmiş açılınca diff yeniden çizilir */
|
|
3714
3873
|
session.messages.push(toolMsg);
|
|
3715
3874
|
try {
|
|
@@ -4172,31 +4331,70 @@ const skills = require('./skills');
|
|
|
4172
4331
|
|
|
4173
4332
|
/* ---------- alt-agent ---------- */
|
|
4174
4333
|
|
|
4175
|
-
async _subagent(task, context, parentSignal, sessionId) {
|
|
4334
|
+
async _subagent(task, context, parentSignal, sessionId, subagentType = '') {
|
|
4176
4335
|
const ctrl = new AbortController();
|
|
4177
4336
|
const onAbort = () => ctrl.abort();
|
|
4178
4337
|
if (parentSignal) {
|
|
4179
4338
|
if (parentSignal.aborted) throw new Error('iptal edildi');
|
|
4180
4339
|
parentSignal.addEventListener('abort', onAbort, { once: true });
|
|
4181
4340
|
}
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
).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 : '';
|
|
4188
4346
|
const role = 'subagent';
|
|
4189
4347
|
const sel = this.modelFor(role) || this.sel;
|
|
4190
4348
|
if (this.roleModels[role]) {
|
|
4191
4349
|
emitSafe(this, sessionId, { type: 'status', status: `rol: ${role} → ${sel.providerName} · ${sel.model}` });
|
|
4192
4350
|
}
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
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
|
+
}
|
|
4200
4398
|
const user = context ? `${task}\n\n# BAĞLAM\n${context}` : task;
|
|
4201
4399
|
const msgs = [
|
|
4202
4400
|
{ role: 'system', content: system },
|
|
@@ -4236,8 +4434,15 @@ const skills = require('./skills');
|
|
|
4236
4434
|
try {
|
|
4237
4435
|
args = JSON.parse((tc.function && tc.function.arguments) || '{}');
|
|
4238
4436
|
} catch {}
|
|
4239
|
-
|
|
4240
|
-
|
|
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 });
|
|
4241
4446
|
}
|
|
4242
4447
|
}
|
|
4243
4448
|
const last = [...msgs].reverse().find((m) => m.role === 'assistant' && m.content);
|
|
@@ -4373,27 +4578,85 @@ const skills = require('./skills');
|
|
|
4373
4578
|
|
|
4374
4579
|
async _execTool(name, args, signal, sessionId) {
|
|
4375
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
|
+
}
|
|
4376
4592
|
/* ANA KOD KİLİDİ: korumalı bölgeye yazma/silme girişimi daha kapıdan geçmez */
|
|
4377
4593
|
const blocked = this._guardTool(name, args);
|
|
4378
4594
|
if (blocked) return blocked;
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
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 : [];
|
|
4388
4603
|
try {
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
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 || {} },
|
|
4396
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
|
+
}
|
|
4397
4660
|
}
|
|
4398
4661
|
}
|
|
4399
4662
|
/* GERİ ALMA GÜNLÜĞÜ: dosya yazımından ÖNCE eski içerik kayda geçer */
|
|
@@ -4488,8 +4751,8 @@ const skills = require('./skills');
|
|
|
4488
4751
|
if (name === 'delegate_task') {
|
|
4489
4752
|
const task = String(args.task || '').trim();
|
|
4490
4753
|
if (!task) return JSON.stringify({ ok: false, error: 'task gerekli' });
|
|
4491
|
-
emitSafe(this, sessionId, { type: 'status', status: 'delegate_task' });
|
|
4492
|
-
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 || ''));
|
|
4493
4756
|
return JSON.stringify({ ok: true, task, result: String(result).slice(0, 12000) });
|
|
4494
4757
|
}
|
|
4495
4758
|
if (name === 'bot_dm') {
|