beast-agent 2.15.0 → 2.17.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 +505 -156
- 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/supermemory.js +132 -0
- package/src/main.js +135 -41
- package/src/preload.js +5 -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
|
|
@@ -17,6 +17,7 @@ const research = require('./research');
|
|
|
17
17
|
const agentdefs = require('./agentdefs');
|
|
18
18
|
const memory = require('./memory');
|
|
19
19
|
const mem0 = require('./mem0');
|
|
20
|
+
const supermemory = require('./supermemory');
|
|
20
21
|
const nightref = require('./nightref');
|
|
21
22
|
const skills = require('./skills');
|
|
22
23
|
const mcp = require('./mcp');
|
|
@@ -24,6 +25,10 @@ const apps = require('./apps');
|
|
|
24
25
|
const pdfwrite = require('./pdfwrite');
|
|
25
26
|
const { estTokens, estMsgTokens } = require('./tokens');
|
|
26
27
|
const log = require('./logger');
|
|
28
|
+
/* opencode mantığı (birebir port): ajanlar, izinler, tool seti, promptlar —
|
|
29
|
+
Beast Code (bcCode) oturumları bu çekirdek üzerinden koşar */
|
|
30
|
+
const opencode = require('./opencode');
|
|
31
|
+
const { AskService } = opencode;
|
|
27
32
|
|
|
28
33
|
const MAX_TURNS = 40;
|
|
29
34
|
const SUB_MAX_TURNS = 8;
|
|
@@ -137,15 +142,9 @@ const PERM_TOOL_SETS = {
|
|
|
137
142
|
};
|
|
138
143
|
const PERM_LEVELS = ['all', 'web', 'read', 'chat'];
|
|
139
144
|
|
|
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
|
-
]);
|
|
145
|
+
/* opencode plan ajanı salt-okurluğu artık PERMISSION kurallarından türer
|
|
146
|
+
(opencode/agents.js: edit {"*":"deny"} → Permission.disabled seti düşürür) —
|
|
147
|
+
elle bakılan araç beyaz listesi kalktı (tamamen opencode mantığı) */
|
|
149
148
|
|
|
150
149
|
/* İzin değerini normalize eder: 'all' → ['all'], 'web' → ['web'],
|
|
151
150
|
'web,read' / ['web','read'] → ['web','read'] (sıra PERM_LEVELS'e göre dizilir).
|
|
@@ -277,6 +276,14 @@ class Engine {
|
|
|
277
276
|
/* mem0-native hafıza katmanı: semantik arama + LLM konsolidasyonu (bot başına izole store).
|
|
278
277
|
Ayarlardan kapatılabilir (mem0:false) — kapalıysa klasik keyword hafızası çalışır. */
|
|
279
278
|
this.mem0Enabled = opts.mem0 !== false;
|
|
279
|
+
/* Supermemory (lokal) bellek katmanı — DEFAULT AÇIK: sunucu ayaktaysa
|
|
280
|
+
(npx supermemory local → localhost:6767) hibrit arama + kullanıcı profili
|
|
281
|
+
öncelikli gelir; ayakta değilse klasik hafıza sorunsuz devralır.
|
|
282
|
+
Sağlık önbelleği: probe 60 sn'de bir tazelenir (spam probe yok). */
|
|
283
|
+
this.supermemory = opts.supermemory || null; // { enabled, baseUrl, apiKey, containerTag }
|
|
284
|
+
this._smUp = null; // null = henüz bilinmiyor
|
|
285
|
+
this._smCheckedAt = 0;
|
|
286
|
+
this._smWarned = false; // erişilemez iletisi bir kez basılır
|
|
280
287
|
/* ANA KOD KİLİDİ: agent kendi kaynak koduna dokunamaz (yazma/silme yok, okuma serbest) */
|
|
281
288
|
this.protectedDirs = (opts.protectedDirs || [])
|
|
282
289
|
.map((d) => String(d).replace(/[\\/]+$/, '').toLowerCase().replace(/\//g, '\\'))
|
|
@@ -313,6 +320,15 @@ class Engine {
|
|
|
313
320
|
this.notifyOwnerFail = opts.notifyOwnerFail !== false; // #25 hata mail bildirimi (runtime /notify)
|
|
314
321
|
this.reminders = opts.reminders || null; // hatırlatıcı kancası (main enjekte eder)
|
|
315
322
|
this.watchers = opts.watchers || null; // arka plan izleyici köprüsü (main enjekte eder)
|
|
323
|
+
/* opencode permission Service (birebir port): ask/reply — once|always|reject.
|
|
324
|
+
BC oturumlarında riskli araç onayı bu akışla sorulur; UI 'permission.asked'
|
|
325
|
+
eventi dinler, 'permission:reply' ile yanıtlar. approved listesi oturum
|
|
326
|
+
boyunca "always" desenlerini kural olarak taşır (opencode birebir). */
|
|
327
|
+
this._perm = new AskService((ev) => this.emit(ev));
|
|
328
|
+
this.bcAskApprovals = opts.approvals ? true : false; // onay ayarı → opencode user konfigi gibi "ask" üretir
|
|
329
|
+
/* opencode reminders.ts: bu oturumda daha önce plan ajanı koştu mu?
|
|
330
|
+
plan→build geçişinde build-switch.txt synthetic part'ı buna bağlanır */
|
|
331
|
+
this._bcWasPlan = new Map(); // sessionId -> bool
|
|
316
332
|
/* onay kapısı: riskli araç öncesi dış onay beklenir ({request} async) */
|
|
317
333
|
this.approvals = opts.approvals || null;
|
|
318
334
|
/* "bir daha sorma" onaylanan araçlar — bu setteki riskli araçlar sorulmaz */
|
|
@@ -942,11 +958,10 @@ class Engine {
|
|
|
942
958
|
return lv.effort || null;
|
|
943
959
|
}
|
|
944
960
|
|
|
945
|
-
/*
|
|
961
|
+
/* opencode birebir: düşünme çabası ajan tanımına değil kullanıcı ayarına
|
|
962
|
+
bağlıdır — eski "BC'de low'a sabitle" hız kısıtı kalktı (opencode'da yok) */
|
|
946
963
|
_thinkEffortFor(session) {
|
|
947
|
-
|
|
948
|
-
if (!session || !session.bcCode) return e;
|
|
949
|
-
return e ? 'low' : null;
|
|
964
|
+
return this._thinkEffort();
|
|
950
965
|
}
|
|
951
966
|
|
|
952
967
|
_thinkLabel() {
|
|
@@ -1525,56 +1540,219 @@ class Engine {
|
|
|
1525
1540
|
return out;
|
|
1526
1541
|
}
|
|
1527
1542
|
|
|
1528
|
-
/*
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1543
|
+
/* ---------- opencode mantığı: Beast Code ajan sistemi ----------
|
|
1544
|
+
opencode-dev/packages/opencode/src birebir portu — BC oturumları artık
|
|
1545
|
+
"mod" değil AJAN koşar (agent/agent.ts):
|
|
1546
|
+
build — varsayılan (edit serbest, question/plan_enter allow)
|
|
1547
|
+
plan — salt-okur: edit * deny (yalnız .opencode/plans/*.md yazılabilir)
|
|
1548
|
+
System birleşimi (llm/request.ts:58-66 + prompt.ts:1257-1269):
|
|
1549
|
+
[ ajanın promptu VARSA o, yoksa model-bazlı ana prompt (beast.txt,
|
|
1550
|
+
gpt.txt, anthropic.txt, gemini.txt, kimi.txt, codex.txt, default.txt),
|
|
1551
|
+
environment bloğu, proje talimatları (AGENTS.md), skills kataloğu ] */
|
|
1552
|
+
|
|
1553
|
+
/* BC ajanını çözer (opencode agents.get):
|
|
1554
|
+
- Sandbox oturumları (sbSandbox) → 'sandbox' ajanı (repo disiplini + panel_run)
|
|
1555
|
+
- session.bcAgent ('plan'|'build') — eski bcMode:'plan' oturumları geriye
|
|
1556
|
+
uyumlu plan ajanına düşer
|
|
1557
|
+
Studio/Finance/bgJob kendi ajan dünyalarında koşar — opencode BC sistemine
|
|
1558
|
+
katılmaz (buildStudioSystem/buildFinanceSystem/buildBgSystem öncelikli). */
|
|
1559
|
+
_bcAgentInfo(session) {
|
|
1560
|
+
if (!session || !session.bcCode) return null;
|
|
1561
|
+
if (session.studio || session.finance || session.bgJob) return null;
|
|
1562
|
+
const name = session.sbSandbox
|
|
1563
|
+
? 'sandbox'
|
|
1564
|
+
: session.bcAgent === 'plan' || session.bcAgent === 'build' || session.bcAgent === 'sandbox'
|
|
1565
|
+
? session.bcAgent
|
|
1566
|
+
: String(session.bcMode || '').toLowerCase() === 'plan'
|
|
1567
|
+
? 'plan'
|
|
1568
|
+
: 'build';
|
|
1569
|
+
const ws = (session && session.workspace) || this.workspace;
|
|
1570
|
+
const user = this.bcAskApprovals ? { bash: 'ask', edit: { '*': 'ask' } } : {};
|
|
1571
|
+
const info = opencode.agents.get(name, { worktree: ws, promptExplore: opencode.PROMPT_EXPLORE, user });
|
|
1572
|
+
if (info) return info;
|
|
1573
|
+
const fallbackName = session.sbSandbox ? 'sandbox' : 'build';
|
|
1574
|
+
return opencode.agents.get(fallbackName, { worktree: ws, promptExplore: opencode.PROMPT_EXPLORE, user });
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
/* opencode opencode ajan değişimi: /plan /build — oturumun ajanı döner,
|
|
1578
|
+
plan→build geçişi reminders (build-switch.txt) için işaretlenir */
|
|
1579
|
+
setBcAgent(sessionId, name) {
|
|
1580
|
+
const s = this.cache.get(String(sessionId || ''));
|
|
1581
|
+
if (!s || !s.bcCode) return null;
|
|
1582
|
+
const prev = s.bcAgent || 'build';
|
|
1583
|
+
const next = name === 'plan' ? 'plan' : 'build';
|
|
1584
|
+
if (prev === 'plan' && next === 'build') this._bcWasPlan.set(String(s.id), true);
|
|
1585
|
+
s.bcAgent = next;
|
|
1586
|
+
this.cache.set(s.id, s);
|
|
1587
|
+
this.emit({ type: 'bc-agent', sessionId: s.id, agent: next, prev });
|
|
1588
|
+
return next;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
/* opencode Permission.reply (UI/WA köprüsü): 'once' | 'always' | 'reject' */
|
|
1592
|
+
replyPermission(requestId, action, message) {
|
|
1593
|
+
try {
|
|
1594
|
+
return this._perm.reply(requestId, action === 'always' ? 'always' : action === 'reject' ? 'reject' : 'once', message);
|
|
1595
|
+
} catch (e) {
|
|
1596
|
+
return { ok: false, error: String((e && e.message) || e) };
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
/* ---------- Supermemory (lokal) bellek köprüsü ---------- */
|
|
1601
|
+
|
|
1602
|
+
_smConfig() {
|
|
1603
|
+
const sm = this.supermemory;
|
|
1604
|
+
if (!sm || sm.enabled === false || !sm.baseUrl) return null;
|
|
1605
|
+
return sm;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/* Ayaktayken true — 60 sn cooldown'lu probe (aynı tura 10 kez probe vurmaz) */
|
|
1609
|
+
async _smReady() {
|
|
1610
|
+
const sm = this._smConfig();
|
|
1611
|
+
if (!sm) return false;
|
|
1612
|
+
const now = Date.now();
|
|
1613
|
+
if (this._smUp === true && now - this._smCheckedAt < supermemory.HEALTH_COOLDOWN_MS) return true;
|
|
1614
|
+
if (this._smUp === false && now - this._smCheckedAt < supermemory.HEALTH_COOLDOWN_MS) return false;
|
|
1615
|
+
this._smUp = await supermemory.probe(sm.baseUrl, sm.apiKey);
|
|
1616
|
+
this._smCheckedAt = now;
|
|
1617
|
+
if (!this._smUp && !this._smWarned) {
|
|
1618
|
+
this._smWarned = true;
|
|
1619
|
+
emitSafe(this, null, {
|
|
1620
|
+
type: 'status',
|
|
1621
|
+
status: '🧠 supermemory erişilemedi — klasik hafıza kullanılıyor (açmak için: npx supermemory local)',
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
return this._smUp;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
/* Container tag: bot oturumları kendi izole havuzuna yazar (mem0 deseni) */
|
|
1628
|
+
_smContainer(sessionId) {
|
|
1629
|
+
const sm = this._smConfig();
|
|
1630
|
+
const bctx = this._sessionBotCtx(sessionId ? this.cache.get(String(sessionId)) : null);
|
|
1631
|
+
return (bctx && bctx.id ? 'bot:' + bctx.id : '') || (sm && sm.containerTag) || 'beast';
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
/* YAZ: yerel hafıza HER ZAMEN yazılır (risksiz yedek); supermemory ASENKRON
|
|
1635
|
+
push edilir — turu bloklamaz, hata yutulur (mem0 deseninin aynısı) */
|
|
1636
|
+
_smPush(sessionId, text) {
|
|
1637
|
+
const sm = this._smConfig();
|
|
1638
|
+
if (!sm) return;
|
|
1639
|
+
supermemory
|
|
1640
|
+
.add(sm.baseUrl, sm.apiKey, { content: text, containerTag: this._smContainer(sessionId), metadata: { type: 'conversation' } })
|
|
1641
|
+
.catch(() => {});
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/* ARA: supermemory hibrit arama + profil → yerel sonuçlarla BİRLEŞTİR.
|
|
1645
|
+
Sunucu yoksa/hata yerse eski akış aynen çalışır (fallback). */
|
|
1646
|
+
async _smSearch(sessionId, q, limit) {
|
|
1647
|
+
const sm = this._smConfig();
|
|
1648
|
+
if (!sm || !(await this._smReady())) return null;
|
|
1649
|
+
const container = this._smContainer(sessionId);
|
|
1650
|
+
try {
|
|
1651
|
+
const [hits, prof] = await Promise.all([
|
|
1652
|
+
supermemory.search(sm.baseUrl, sm.apiKey, { q, containerTag: container, limit, mode: 'hybrid' }),
|
|
1653
|
+
supermemory.profile(sm.baseUrl, sm.apiKey, { containerTag: container }).catch(() => null),
|
|
1654
|
+
]);
|
|
1655
|
+
const out = {
|
|
1656
|
+
engine: 'supermemory',
|
|
1657
|
+
results: hits.map((h) => ({ text: h.text, score: h.similarity, source: 'supermemory' })),
|
|
1658
|
+
};
|
|
1659
|
+
if (prof && (prof.static.length || prof.dynamic.length)) out.profile = prof;
|
|
1660
|
+
return out;
|
|
1661
|
+
} catch {
|
|
1662
|
+
this._smUp = false;
|
|
1663
|
+
this._smCheckedAt = Date.now();
|
|
1664
|
+
return null;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
/* opencode request.ts prepare — system birleşimi (birebir sıra) */
|
|
1669
|
+
buildOcSystem(session, agentInfo) {
|
|
1670
|
+
const sid = String(session && session.id);
|
|
1671
|
+
const sel = this.sessionModel.get(sid) || this.modelFor(null) || this.sel || { model: '', providerId: '' };
|
|
1672
|
+
const ws = (session && session.workspace) || this.workspace;
|
|
1673
|
+
const agentPrompt = (agentInfo && agentInfo.prompt) || '';
|
|
1674
|
+
const main = agentPrompt ? [agentPrompt] : opencode.systemprompt.provider(sel.model);
|
|
1547
1675
|
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
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1676
|
+
const skillsBlock = (() => {
|
|
1677
|
+
try {
|
|
1678
|
+
return opencode.systemprompt.skills((agentInfo && agentInfo.permission) || [], () => skills.scan());
|
|
1679
|
+
} catch {
|
|
1680
|
+
return undefined;
|
|
1681
|
+
}
|
|
1682
|
+
})();
|
|
1683
|
+
/* opencode'da birleşim .filter(x => x).join("\n") — ajan promptu varsa
|
|
1684
|
+
model-bazlı dosya YERİNE geçer */
|
|
1685
|
+
return [
|
|
1686
|
+
...main,
|
|
1687
|
+
...opencode.systemprompt.environment({
|
|
1688
|
+
modelId: sel.model,
|
|
1689
|
+
providerId: sel.providerId || '?',
|
|
1690
|
+
directory: ws,
|
|
1691
|
+
worktree: this.workspace,
|
|
1692
|
+
}),
|
|
1693
|
+
...(proj ? [proj] : []),
|
|
1694
|
+
...(skillsBlock ? [skillsBlock] : []),
|
|
1695
|
+
]
|
|
1696
|
+
.filter((x) => x)
|
|
1697
|
+
.join('\n');
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
/* opencode araç izinleri: pattern'ler araç çağrısından türer.
|
|
1701
|
+
bash'ta always: [komut] — opencode tree-sitter deseni basitleştirilmiş. */
|
|
1702
|
+
_bcPermissionFor(name, args, ws) {
|
|
1703
|
+
const a = args || {};
|
|
1704
|
+
const abs = (p) => {
|
|
1705
|
+
try {
|
|
1706
|
+
const raw = String(p || '').trim();
|
|
1707
|
+
if (!raw) return '*';
|
|
1708
|
+
return path.isAbsolute(raw) ? path.resolve(raw) : path.resolve(ws || this.workspace, raw);
|
|
1709
|
+
} catch {
|
|
1710
|
+
return '*';
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
switch (name) {
|
|
1714
|
+
case 'edit_file':
|
|
1715
|
+
case 'write_file':
|
|
1716
|
+
return { permission: 'edit', patterns: [abs(a.path)], always: [] };
|
|
1717
|
+
case 'run_command':
|
|
1718
|
+
return { permission: 'bash', patterns: [String(a.command || '*')], always: [String(a.command || '*')] };
|
|
1719
|
+
case 'read_file':
|
|
1720
|
+
return { permission: 'read', patterns: [abs(a.path)], always: [] };
|
|
1721
|
+
case 'glob':
|
|
1722
|
+
return { permission: 'glob', patterns: [String(a.pattern || '*')], always: [] };
|
|
1723
|
+
case 'grep':
|
|
1724
|
+
return { permission: 'grep', patterns: [String(a.pattern || '*')], always: [] };
|
|
1725
|
+
case 'list_dir':
|
|
1726
|
+
return { permission: 'list', patterns: [abs(a.path)], always: [] };
|
|
1727
|
+
case 'webfetch':
|
|
1728
|
+
return { permission: 'webfetch', patterns: [String(a.url || '*')], always: [] };
|
|
1729
|
+
case 'web_search':
|
|
1730
|
+
return { permission: 'websearch', patterns: [String(a.query || '*')], always: [] };
|
|
1731
|
+
case 'todo_write':
|
|
1732
|
+
return { permission: 'todowrite', patterns: ['*'], always: ['*'] };
|
|
1733
|
+
case 'delegate_task':
|
|
1734
|
+
return { permission: 'task', patterns: [String(a.subagent_type || '*')], always: [] };
|
|
1735
|
+
default:
|
|
1736
|
+
return { permission: name, patterns: ['*'], always: [] };
|
|
1737
|
+
}
|
|
1576
1738
|
}
|
|
1577
1739
|
|
|
1740
|
+
/* opencode external-directory: workspace dışı dosya erişimi ayrı izin ister
|
|
1741
|
+
(tool/external-directory.ts) — read/edit/write kapsar */
|
|
1742
|
+
_bcExternalCheck(name, args, ws) {
|
|
1743
|
+
if (name !== 'read_file' && name !== 'edit_file' && name !== 'write_file') return null;
|
|
1744
|
+
const raw = String((args && args.path) || '').trim();
|
|
1745
|
+
if (!raw) return null;
|
|
1746
|
+
try {
|
|
1747
|
+
const abs = path.isAbsolute(raw) ? path.resolve(raw) : path.resolve(ws || this.workspace, raw);
|
|
1748
|
+
const base = path.resolve(ws || this.workspace);
|
|
1749
|
+
if (path.normalize(abs).toLowerCase().startsWith(path.normalize(base).toLowerCase())) return null;
|
|
1750
|
+
return { permission: 'external_directory', patterns: [abs], always: [] };
|
|
1751
|
+
} catch {
|
|
1752
|
+
return null;
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1578
1756
|
/* Beast Studio (video paneli): video YAPMA ve DÜZENLEME ajanı. Workspace'teki
|
|
1579
1757
|
her şey video proje malzemesi olarak görülür (ham görüntü, ses, görsel,
|
|
1580
1758
|
altyazı, çıktı). Medya işi ffprobe + ffmpeg ile yürütür; ikisi de app ile
|
|
@@ -3211,36 +3389,27 @@ class Engine {
|
|
|
3211
3389
|
session.messages = keep;
|
|
3212
3390
|
}
|
|
3213
3391
|
|
|
3214
|
-
async _chatTurn(session, signal, onDelta, toolsList = TOOLS) {
|
|
3215
|
-
/*
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
toolsList =
|
|
3392
|
+
async _chatTurn(session, signal, onDelta, toolsList = TOOLS, bcOpts = null) {
|
|
3393
|
+
/* opencode mantığı (BC): araç seti opencode builtin registry'si — read/edit/
|
|
3394
|
+
write/bash/glob/grep/list/webfetch/websearch/todowrite/task/skill.
|
|
3395
|
+
MCP/apps/panel_run BC'ye GİRMEZ (opencode disiplini: hızlı ilk token). */
|
|
3396
|
+
const bcAgent = session && session.bcCode ? this._bcAgentInfo(session) : null;
|
|
3397
|
+
if (bcAgent) {
|
|
3398
|
+
toolsList = opencode.toolmap.definitions();
|
|
3399
|
+
} else {
|
|
3400
|
+
/* MCP: %APPDATA%\beast\mcp.json'daki etkin serverların araçlarını şema listesine ekle
|
|
3401
|
+
(bağlı değilse lazy bağlanır; kapalıysa liste değişmez) */
|
|
3402
|
+
toolsList = await mcp.mergeTools(toolsList);
|
|
3403
|
+
/* Beast Apps: kurulu app'lerin araçları (app__<id>__<tool>) modele açılır */
|
|
3404
|
+
toolsList = apps.mergeTools(toolsList);
|
|
3405
|
+
/* panel_run yalnız SANDBOX oturumlarında görünsün — diğer panellerde
|
|
3406
|
+
modelin alet çantasında olmasın (hook olmadan çalışmaz) */
|
|
3407
|
+
if (!(session && session.sbSandbox)) {
|
|
3408
|
+
toolsList = toolsList.filter((t) => !(t && t.function && t.function.name === 'panel_run'));
|
|
3409
|
+
}
|
|
3225
3410
|
}
|
|
3226
3411
|
/* opencode agent.ts port: özel ajan tanımı — prompt/model/araç/steps */
|
|
3227
3412
|
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
3413
|
const promptText = this._lastUserText(session);
|
|
3245
3414
|
let system = session.bgJob
|
|
3246
3415
|
? this.buildBgSystem(session)
|
|
@@ -3248,18 +3417,21 @@ class Engine {
|
|
|
3248
3417
|
? this.buildStudioSystem(session)
|
|
3249
3418
|
: session.finance
|
|
3250
3419
|
? this.buildFinanceSystem(session)
|
|
3251
|
-
:
|
|
3252
|
-
? this.
|
|
3253
|
-
: await this.buildSystem(promptText, session);
|
|
3420
|
+
: bcAgent
|
|
3421
|
+
? this.buildOcSystem(session, bcAgent)
|
|
3422
|
+
: await this.buildSystem(promptText, session);
|
|
3254
3423
|
// Granül izin: oturumun yetki seviyesine göre araç seti daraltılır.
|
|
3255
3424
|
// Çoklu izin (ör. web+read) seçiliyse kümeler BİRLEŞİR — hepsinin araçları açık olur.
|
|
3425
|
+
// BC'de opencode adları Beast adına çevrilerek denetlenir (read → read_file).
|
|
3256
3426
|
const perms = this.sessionPermFor(session.id);
|
|
3257
3427
|
let allowedSet = null;
|
|
3258
3428
|
if (!perms.includes('all')) {
|
|
3259
3429
|
allowedSet = new Set();
|
|
3260
3430
|
for (const p of perms) for (const t of PERM_TOOL_SETS[p] || []) allowedSet.add(t);
|
|
3261
3431
|
}
|
|
3262
|
-
let activeTools = allowedSet
|
|
3432
|
+
let activeTools = allowedSet
|
|
3433
|
+
? toolsList.filter((t) => allowedSet.has(opencode.toolmap.TO_BUILTIN[t.function.name] || t.function.name))
|
|
3434
|
+
: toolsList;
|
|
3263
3435
|
/* Beast Finance: mt5_* araçları YALNIZ finance oturumlarında görünür */
|
|
3264
3436
|
if (FINANCE_TOOL_SET.size && !(session && session.finance)) {
|
|
3265
3437
|
activeTools = activeTools.filter((t) => !FINANCE_TOOL_SET.has(t.function.name));
|
|
@@ -3276,9 +3448,16 @@ class Engine {
|
|
|
3276
3448
|
/* CEO: uygulayıcı araçlar kapalı — her şey paralel ajana devredilir */
|
|
3277
3449
|
activeTools = activeTools.filter((t) => !CEO_EXEC_TOOLS.has(t.function.name));
|
|
3278
3450
|
}
|
|
3279
|
-
if (
|
|
3280
|
-
/* opencode
|
|
3281
|
-
|
|
3451
|
+
if (bcAgent) {
|
|
3452
|
+
/* opencode permission.visibleTools (birebir): ruleset'te pattern:'*'+deny
|
|
3453
|
+
olan araçlar setten düşer — plan ajanında edit/write GERÇEKTEN gizlenir,
|
|
3454
|
+
salt-okurluk prompta kalmaz. opencode'da edit/write/apply_patch aynı
|
|
3455
|
+
"edit" iznine iner. */
|
|
3456
|
+
const hidden = opencode.Permission.disabled(
|
|
3457
|
+
activeTools.map((t) => t.function.name),
|
|
3458
|
+
bcAgent.permission
|
|
3459
|
+
);
|
|
3460
|
+
if (hidden.size) activeTools = activeTools.filter((t) => !hidden.has(t.function.name));
|
|
3282
3461
|
}
|
|
3283
3462
|
if (adef && adef.tools) {
|
|
3284
3463
|
/* özel ajan araç beyaz listesi — tanımda olmayan araç görünmez */
|
|
@@ -3316,7 +3495,25 @@ class Engine {
|
|
|
3316
3495
|
/* opencode port: model bağlamına göre dinamik bütçe + compaction özeti */
|
|
3317
3496
|
const selEarly = this.sessionModel.get(String(session.id)) || this.modelFor(null) || this.sel;
|
|
3318
3497
|
const dynBudget = this._historyBudget(selEarly);
|
|
3319
|
-
|
|
3498
|
+
/* opencode SessionReminders.apply (reminders.ts birebir): plan ajanındaysa
|
|
3499
|
+
plan.txt salt-okur hatırlatması, plan→build geçişinde build-switch.txt
|
|
3500
|
+
son USER mesajına synthetic eklenir. session.messages ASLA değişmez —
|
|
3501
|
+
her tur taze kopyaya uygulanır (opencode her turda DB'den yeniden yükler). */
|
|
3502
|
+
let msgsForPayload = session.messages;
|
|
3503
|
+
if (bcAgent) {
|
|
3504
|
+
msgsForPayload = opencode.systemprompt.applyReminders(
|
|
3505
|
+
session.messages,
|
|
3506
|
+
bcAgent,
|
|
3507
|
+
!!this._bcWasPlan.get(String(session.id))
|
|
3508
|
+
);
|
|
3509
|
+
}
|
|
3510
|
+
let payload = this._buildPayload(system, msgsForPayload, session.notes, dynBudget, session.summary);
|
|
3511
|
+
if (bcAgent && bcOpts && bcOpts.isLastStep) {
|
|
3512
|
+
/* opencode prompt.ts:1178-1181 — isLastStep: MAX_STEPS_PROMPT assistant
|
|
3513
|
+
mesajı olarak akışa eklenir; araçlar kilitlenir (yalnız metin kapanışı) */
|
|
3514
|
+
if (Array.isArray(payload.messages)) payload.messages.push({ role: 'assistant', content: opencode.MAX_STEPS_PROMPT });
|
|
3515
|
+
activeTools = [];
|
|
3516
|
+
}
|
|
3320
3517
|
// Vision model sadece mesajda görsel varsa devreye girer; terminal ise prompt'un
|
|
3321
3518
|
// shell/command işi olduğunda. Aksi halde ana model kullanılır.
|
|
3322
3519
|
let role = this._lastUserHasImage(session) ? 'vision' : null;
|
|
@@ -3365,7 +3562,7 @@ class Engine {
|
|
|
3365
3562
|
type: 'status',
|
|
3366
3563
|
status: `🧠 düşük uyumlu sağlayıcı: geçmişteki düşünme içerikleri temizlendi (${dropped} mesaj) — tur yeniden deneniyor`,
|
|
3367
3564
|
});
|
|
3368
|
-
payload = this._buildPayload(system,
|
|
3565
|
+
payload = this._buildPayload(system, msgsForPayload, session.notes, dynBudget, session.summary);
|
|
3369
3566
|
res = await this._streamWithFallbacks(
|
|
3370
3567
|
session,
|
|
3371
3568
|
payload,
|
|
@@ -3399,7 +3596,7 @@ class Engine {
|
|
|
3399
3596
|
type: 'status',
|
|
3400
3597
|
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
3598
|
});
|
|
3402
|
-
payload = this._buildPayload(system,
|
|
3599
|
+
payload = this._buildPayload(system, msgsForPayload, session.notes, dynBudget, session.summary);
|
|
3403
3600
|
res = await this._streamWithFallbacks(
|
|
3404
3601
|
session,
|
|
3405
3602
|
payload,
|
|
@@ -3477,9 +3674,17 @@ class Engine {
|
|
|
3477
3674
|
doom-loop koruması + kullanıcı ■ kırar. bg ajanlarda kapanış
|
|
3478
3675
|
disiplini için tavan kalır. */
|
|
3479
3676
|
const runDef = this._agentDefFor(session, !!session.bgJob);
|
|
3677
|
+
/* opencode mantığı (BC): ajan çözümleme — build/plan ajanı ruleset'iyle
|
|
3678
|
+
beraber tur boyunca kullanılır (permission ask + disabled filtreleri) */
|
|
3679
|
+
const bcAgent = session.bcCode ? this._bcAgentInfo(session) : null;
|
|
3680
|
+
/* opencode prompt.ts:1178 (birebir): maxSteps = agent.steps ?? Infinity —
|
|
3681
|
+
build/plan ajanlarında steps tanımsızdır → sınırsız; bağlam dolunca
|
|
3682
|
+
compaction özetler, sonsuz döngüyü doom-loop koruması + ■ kırar. */
|
|
3480
3683
|
const maxTurns = session.bgJob
|
|
3481
3684
|
? Math.max(2, Math.min(60, (runDef && runDef.steps) || MAX_TURNS))
|
|
3482
|
-
:
|
|
3685
|
+
: bcAgent
|
|
3686
|
+
? (bcAgent.steps ?? Infinity)
|
|
3687
|
+
: Infinity;
|
|
3483
3688
|
const recentSigs = []; // doom-loop dedektörü: son araç imzaları
|
|
3484
3689
|
/* Beast Code canlı önizleme: run boyunca üretilen eserleri topla —
|
|
3485
3690
|
iş bitince dahili tarayıcıda CANLI açılır (site/dev server/HTML) */
|
|
@@ -3528,9 +3733,12 @@ class Engine {
|
|
|
3528
3733
|
/* opencode runLoop portu: istek kurulmadan ÖNCEKİ mesaj sayısı —
|
|
3529
3734
|
koşan tur SIRASINDA gelen (steer) user mesajlarını saptar */
|
|
3530
3735
|
const preLen = session.messages.length;
|
|
3736
|
+
/* opencode prompt.ts:1178-1181: isLastStep = step >= maxSteps — son
|
|
3737
|
+
turda MAX_STEPS_PROMPT enjekte edilir ve araçlar kilitlenir */
|
|
3738
|
+
const isLastStep = !!bcAgent && maxTurns !== Infinity && turn >= maxTurns - 1;
|
|
3531
3739
|
const res = await this._chatTurn(session, ctrl.signal, (delta) =>
|
|
3532
3740
|
emit({ type: 'token', delta })
|
|
3533
|
-
);
|
|
3741
|
+
, bcAgent ? opencode.toolmap.definitions() : undefined, bcAgent ? { isLastStep } : null);
|
|
3534
3742
|
/* opencode port (processor.ts:477-482): step-finish usage'ı compaction
|
|
3535
3743
|
tetiğine besle — gerçek toplam biliniyorsa tahmine gerek kalmaz */
|
|
3536
3744
|
const uTot =
|
|
@@ -3615,14 +3823,16 @@ class Engine {
|
|
|
3615
3823
|
|
|
3616
3824
|
// Paralel yürütme — bağımsız çağrılar beklemesin
|
|
3617
3825
|
session.toolsSinceReflect = (session.toolsSinceReflect || 0) + res.toolCalls.length;
|
|
3618
|
-
if (!this._knownToolNames) this._knownToolNames = new Set(TOOLS.map((t) => t.function.name));
|
|
3826
|
+
if (!this._knownToolNames) this._knownToolNames = new Set([...TOOLS.map((t) => t.function.name), ...opencode.toolmap.names()]);
|
|
3619
3827
|
await Promise.all(
|
|
3620
3828
|
res.toolCalls.map(async (tc) => {
|
|
3621
3829
|
let name = tc.function && tc.function.name;
|
|
3622
3830
|
/* 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
|
|
3831
|
+
yanlış yazdıysa büyük/küçük harf normalizasyonuyle onar — tur kaybı yok.
|
|
3832
|
+
BC'de havuz opencode builtin seti (read/edit/write/bash...) */
|
|
3624
3833
|
if (name && !this._knownToolNames.has(name)) {
|
|
3625
|
-
const
|
|
3834
|
+
const pool = bcAgent ? opencode.toolmap.definitions() : TOOLS;
|
|
3835
|
+
const fixed = pool.find((t) => t.function.name.toLowerCase() === String(name).trim().toLowerCase());
|
|
3626
3836
|
if (fixed) {
|
|
3627
3837
|
emitSafe(this, sid, {
|
|
3628
3838
|
type: 'status',
|
|
@@ -3638,33 +3848,59 @@ class Engine {
|
|
|
3638
3848
|
emit({ type: 'tool-start', callId: tc.id, name, args });
|
|
3639
3849
|
emit({ type: 'status', status: name });
|
|
3640
3850
|
/* opencode port (processor.ts:29 + 356-380): aynı araç + birebir
|
|
3641
|
-
aynı argüman 3 kez koştuysa 4.'
|
|
3642
|
-
akıtma;
|
|
3851
|
+
aynı argüman 3 kez koştuysa 4.'yi çalıştırma — döngüye para/hız
|
|
3852
|
+
akıtma; BC'de opencode disiplini: doom_loop İZİN ister (defaults:
|
|
3853
|
+
doom_loop ask) — kullanıcı onaylarsa koşmaya devam eder */
|
|
3643
3854
|
const sig = name + '\u0000' + String((tc.function && tc.function.arguments) || '');
|
|
3644
3855
|
const dups = recentSigs.reduce((a, s) => a + (s === sig ? 1 : 0), 0);
|
|
3645
3856
|
recentSigs.push(sig);
|
|
3646
3857
|
let out;
|
|
3647
3858
|
if (dups >= DOOM_LOOP_THRESHOLD) {
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3859
|
+
if (bcAgent) {
|
|
3860
|
+
try {
|
|
3861
|
+
await this._perm.ask({
|
|
3862
|
+
sessionId: sid,
|
|
3863
|
+
ruleset: bcAgent.permission,
|
|
3864
|
+
permission: 'doom_loop',
|
|
3865
|
+
patterns: [name],
|
|
3866
|
+
always: [name],
|
|
3867
|
+
metadata: { callCount: DOOM_LOOP_THRESHOLD, arguments: (tc.function && tc.function.arguments) || '' },
|
|
3868
|
+
tool: { name, args },
|
|
3869
|
+
});
|
|
3870
|
+
out = await this._execTool(name, args, ctrl.signal, sid);
|
|
3871
|
+
} catch (permErr) {
|
|
3872
|
+
out = JSON.stringify({
|
|
3873
|
+
error:
|
|
3874
|
+
`doom-loop: ${name} aynı argümanlarla ${DOOM_LOOP_THRESHOLD} kez koştu. ` +
|
|
3875
|
+
AskService.errorText(permErr) +
|
|
3876
|
+
' Aynı çağrıyı tekrarlamak işe yaramaz — farklı bir yöntem/argüman dene ya da eldeki bilgilerle nihai cevabı ver.',
|
|
3877
|
+
});
|
|
3878
|
+
}
|
|
3879
|
+
} else {
|
|
3880
|
+
out = JSON.stringify({
|
|
3881
|
+
error:
|
|
3882
|
+
`doom-loop: ${name} aynı argümanlarla ${DOOM_LOOP_THRESHOLD} kez çalıştı ve hep aynı sonucu verdi. ` +
|
|
3883
|
+
'Aynı çağrıyı tekrarlamak işe yaramaz — farklı bir yöntem/argüman dene ya da eldeki bilgilerle nihai cevabı ver.',
|
|
3884
|
+
});
|
|
3885
|
+
emitSafe(this, sid, { type: 'status', status: `⛔ doom-loop: ${name} tekrarı engellendi` });
|
|
3886
|
+
}
|
|
3654
3887
|
} else {
|
|
3655
3888
|
out = await this._execTool(name, args, ctrl.signal, sid);
|
|
3656
3889
|
}
|
|
3657
|
-
/* Beast Code eser takibi: yazılan HTML + başlatılan dev server
|
|
3890
|
+
/* Beast Code eser takibi: yazılan HTML + başlatılan dev server.
|
|
3891
|
+
BC'de araçlar opencode adlarıyla gelir: write/edit (filePath),
|
|
3892
|
+
bash (command) — opencode şemasına göre yakalanır */
|
|
3658
3893
|
if (bcArtifacts) {
|
|
3659
3894
|
try {
|
|
3895
|
+
const filePathOc = args && (args.filePath ?? args.path);
|
|
3660
3896
|
if (
|
|
3661
|
-
(name === 'write_file' || name === 'edit_file') &&
|
|
3662
|
-
|
|
3897
|
+
(name === 'write' || name === 'edit' || name === 'write_file' || name === 'edit_file') &&
|
|
3898
|
+
filePathOc && /\.html?$/i.test(String(filePathOc))
|
|
3663
3899
|
) {
|
|
3664
|
-
bcArtifacts.html.push(String(
|
|
3900
|
+
bcArtifacts.html.push(String(filePathOc));
|
|
3665
3901
|
}
|
|
3666
|
-
if (name === 'run_command') {
|
|
3667
|
-
const cmdStr = String((args && args.command) || '');
|
|
3902
|
+
if (name === 'bash' || name === 'run_command') {
|
|
3903
|
+
const cmdStr = String((args && (args.command ?? args.cmd)) || '');
|
|
3668
3904
|
const hay = cmdStr + '\n' + String(out || '').slice(0, 6000);
|
|
3669
3905
|
const m = /https?:\/\/(?:localhost|127\.0\.0\.1)(?::\d{2,5})?[^\s'"<>]*/i.exec(hay);
|
|
3670
3906
|
if (m) {
|
|
@@ -3680,7 +3916,7 @@ class Engine {
|
|
|
3680
3916
|
if (port) bcArtifacts.serverUrl = 'http://localhost:' + port;
|
|
3681
3917
|
}
|
|
3682
3918
|
/* Expo dev server: exp://<LAN-IP>:<port> çıktıdan yakalanır —
|
|
3683
|
-
|
|
3919
|
+
telefon/Expo Go için QR renderer'a canlı iletilir */
|
|
3684
3920
|
const xm = /exp:\/\/[^\s'"<>]+/i.exec(hay);
|
|
3685
3921
|
if (xm) {
|
|
3686
3922
|
const eurl = xm[0].replace(/[)\].,;:'"]+$/, '');
|
|
@@ -3691,7 +3927,7 @@ class Engine {
|
|
|
3691
3927
|
}
|
|
3692
3928
|
}
|
|
3693
3929
|
} catch {}
|
|
3694
|
-
}
|
|
3930
|
+
}
|
|
3695
3931
|
// Ekran görüntüsü gibi araçlar görseli sonraki tura enjekte eder
|
|
3696
3932
|
let injectedImage = null;
|
|
3697
3933
|
/* opencode ctx.metadata portu: edit/write sonucundaki diffView, LLM
|
|
@@ -3709,7 +3945,7 @@ class Engine {
|
|
|
3709
3945
|
}
|
|
3710
3946
|
if (injectedImage || diffView) out = JSON.stringify(obj);
|
|
3711
3947
|
} 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) };
|
|
3948
|
+
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
3949
|
if (diffView) toolMsg.diffView = diffView; /* oturum dosyasına da düşer — geçmiş açılınca diff yeniden çizilir */
|
|
3714
3950
|
session.messages.push(toolMsg);
|
|
3715
3951
|
try {
|
|
@@ -4172,31 +4408,70 @@ const skills = require('./skills');
|
|
|
4172
4408
|
|
|
4173
4409
|
/* ---------- alt-agent ---------- */
|
|
4174
4410
|
|
|
4175
|
-
async _subagent(task, context, parentSignal, sessionId) {
|
|
4411
|
+
async _subagent(task, context, parentSignal, sessionId, subagentType = '') {
|
|
4176
4412
|
const ctrl = new AbortController();
|
|
4177
4413
|
const onAbort = () => ctrl.abort();
|
|
4178
4414
|
if (parentSignal) {
|
|
4179
4415
|
if (parentSignal.aborted) throw new Error('iptal edildi');
|
|
4180
4416
|
parentSignal.addEventListener('abort', onAbort, { once: true });
|
|
4181
4417
|
}
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
).sort((a, b) => String(a.function.name).localeCompare(String(b.function.name))); // önek-cache: sabit sıra
|
|
4418
|
+
/* opencode task aracı (tool/task.ts) subagent_tipi ZORUNLU kılar: BC'den
|
|
4419
|
+
gelen task çağrılarında 'general' | 'explore' ajanları opencode mantığıyla
|
|
4420
|
+
koşar (agent tanımı + permission + opencode builtin seti). Eski
|
|
4421
|
+
delegate_task (BC dışı) davranışı aynen korunur. */
|
|
4422
|
+
const ocType = subagentType === 'explore' || subagentType === 'general' ? subagentType : '';
|
|
4188
4423
|
const role = 'subagent';
|
|
4189
4424
|
const sel = this.modelFor(role) || this.sel;
|
|
4190
4425
|
if (this.roleModels[role]) {
|
|
4191
4426
|
emitSafe(this, sessionId, { type: 'status', status: `rol: ${role} → ${sel.providerName} · ${sel.model}` });
|
|
4192
4427
|
}
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
4428
|
+
let subTools;
|
|
4429
|
+
let system;
|
|
4430
|
+
let ocMode = false;
|
|
4431
|
+
if (ocType) {
|
|
4432
|
+
ocMode = true;
|
|
4433
|
+
const ws = this._sessionWorkspace(sessionId);
|
|
4434
|
+
const agentInfo =
|
|
4435
|
+
opencode.agents.get(ocType, { worktree: ws, promptExplore: opencode.PROMPT_EXPLORE, user: {} }) || null;
|
|
4436
|
+
const ruleset = agentInfo ? agentInfo.permission : [];
|
|
4437
|
+
/* opencode child session: subagent ANA döngüdeki gibi ajan tanımıyla
|
|
4438
|
+
koşar — system = [ajan promptu yoksa model-bazlı prompt, env] */
|
|
4439
|
+
const main = agentInfo && agentInfo.prompt ? [agentInfo.prompt] : opencode.systemprompt.provider(sel.model);
|
|
4440
|
+
system = [
|
|
4441
|
+
...main,
|
|
4442
|
+
...opencode.systemprompt.environment({
|
|
4443
|
+
modelId: sel.model,
|
|
4444
|
+
providerId: sel.providerId || '?',
|
|
4445
|
+
directory: ws,
|
|
4446
|
+
worktree: ws,
|
|
4447
|
+
}),
|
|
4448
|
+
]
|
|
4449
|
+
.filter((x) => x)
|
|
4450
|
+
.join('\n');
|
|
4451
|
+
/* opencode SessionTools.resolve: ajanın permission'ında '*' deny olan
|
|
4452
|
+
araçlar setten düşer — explore: salt-okur (bash dahil okuma amaçlı allow),
|
|
4453
|
+
general: todowrite yok (opencode general tanımı birebir) */
|
|
4454
|
+
const defs = opencode.toolmap.definitions();
|
|
4455
|
+
const hidden = opencode.Permission.disabled(defs.map((t) => t.function.name), ruleset);
|
|
4456
|
+
subTools = defs
|
|
4457
|
+
.filter((t) => !hidden.has(t.function.name))
|
|
4458
|
+
.filter((t) => t.function.name !== 'task')
|
|
4459
|
+
.sort((a, b) => String(a.function.name).localeCompare(String(b.function.name)));
|
|
4460
|
+
} else {
|
|
4461
|
+
subTools = TOOLS.filter(
|
|
4462
|
+
(t) =>
|
|
4463
|
+
t.function.name !== 'delegate_task' &&
|
|
4464
|
+
t.function.name !== 'set_reminder' &&
|
|
4465
|
+
!t.function.name.startsWith('email_')
|
|
4466
|
+
).sort((a, b) => String(a.function.name).localeCompare(String(b.function.name))); // önek-cache: sabit sıra
|
|
4467
|
+
system =
|
|
4468
|
+
`Sen odaklı bir alt-agentsın. Ana ajansın verdiği TEK görevi bitir ve sadece nihai sonucu döndür.\n` +
|
|
4469
|
+
`Windows + PowerShell, çalışma klasörü: ${this.workspace}\n` +
|
|
4470
|
+
`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` +
|
|
4471
|
+
`ARAŞTIRMA SINIRI: 3-5 kaynak yeter; 2-3 denemede bulamazsan kısmi sonucu getir, takılma.\n` +
|
|
4472
|
+
`Kısa çalış, gereksiz soru sorma, sonucu madde madde raporla.\n` +
|
|
4473
|
+
FORMAT_RULES;
|
|
4474
|
+
}
|
|
4200
4475
|
const user = context ? `${task}\n\n# BAĞLAM\n${context}` : task;
|
|
4201
4476
|
const msgs = [
|
|
4202
4477
|
{ role: 'system', content: system },
|
|
@@ -4236,8 +4511,15 @@ const skills = require('./skills');
|
|
|
4236
4511
|
try {
|
|
4237
4512
|
args = JSON.parse((tc.function && tc.function.arguments) || '{}');
|
|
4238
4513
|
} catch {}
|
|
4239
|
-
|
|
4240
|
-
|
|
4514
|
+
let subName = tc.function && tc.function.name;
|
|
4515
|
+
if (ocMode) {
|
|
4516
|
+
/* opencode builtin dilinde çağırır → Beast aracına çevir */
|
|
4517
|
+
const mapped = opencode.toolmap.execMap(subName, args);
|
|
4518
|
+
subName = mapped.name;
|
|
4519
|
+
args = mapped.args;
|
|
4520
|
+
}
|
|
4521
|
+
const out = await this._subExecTool(subName, args, ctrl.signal);
|
|
4522
|
+
msgs.push({ role: 'tool', tool_call_id: tc.id, name: subName, content: out });
|
|
4241
4523
|
}
|
|
4242
4524
|
}
|
|
4243
4525
|
const last = [...msgs].reverse().find((m) => m.role === 'assistant' && m.content);
|
|
@@ -4373,27 +4655,85 @@ const skills = require('./skills');
|
|
|
4373
4655
|
|
|
4374
4656
|
async _execTool(name, args, signal, sessionId) {
|
|
4375
4657
|
try {
|
|
4658
|
+
/* opencode mantığı (BC): model opencode builtin dilinde çağırır —
|
|
4659
|
+
read/edit/write/bash/todowrite/task... execMap Beast'in yerleşik
|
|
4660
|
+
aracına çevirir (implementasyon değişmez, dil opencode olur).
|
|
4661
|
+
Studio/Finance/bgJob kendi dünyasında koşar — bu akışa girmez. */
|
|
4662
|
+
const bcSess = this.cache.get(String(sessionId || '')) || null;
|
|
4663
|
+
const isBc = !!(bcSess && bcSess.bcCode && !bcSess.studio && !bcSess.finance && !bcSess.bgJob);
|
|
4664
|
+
if (isBc) {
|
|
4665
|
+
const mapped = opencode.toolmap.execMap(name, args);
|
|
4666
|
+
name = mapped.name;
|
|
4667
|
+
args = mapped.args;
|
|
4668
|
+
}
|
|
4376
4669
|
/* ANA KOD KİLİDİ: korumalı bölgeye yazma/silme girişimi daha kapıdan geçmez */
|
|
4377
4670
|
const blocked = this._guardTool(name, args);
|
|
4378
4671
|
if (blocked) return blocked;
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
let ok = false;
|
|
4672
|
+
if (isBc) {
|
|
4673
|
+
/* opencode permission akışı (tool/external-directory.ts + her aracın
|
|
4674
|
+
ctx.ask'ı): önce workspace DIŞI erişim izni, sonra araç izni.
|
|
4675
|
+
deny anında hata döner (asla sorulmaz), allow sessiz geçer,
|
|
4676
|
+
ask UI'ya 'permission.asked' eventi ile sorulur */
|
|
4677
|
+
const ws = this._sessionWorkspace(sessionId);
|
|
4678
|
+
const bcAgent = this._bcAgentInfo(bcSess);
|
|
4679
|
+
const ruleset = bcAgent ? bcAgent.permission : [];
|
|
4388
4680
|
try {
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4681
|
+
const ext = this._bcExternalCheck(name, args, ws);
|
|
4682
|
+
if (ext) {
|
|
4683
|
+
await this._perm.ask({ sessionId, ruleset, ...ext, metadata: { tool: name, args: args || {} } });
|
|
4684
|
+
}
|
|
4685
|
+
const perm = this._bcPermissionFor(name, args, ws);
|
|
4686
|
+
await this._perm.ask({
|
|
4687
|
+
sessionId,
|
|
4688
|
+
ruleset,
|
|
4689
|
+
permission: perm.permission,
|
|
4690
|
+
patterns: perm.patterns,
|
|
4691
|
+
always: perm.always,
|
|
4692
|
+
metadata: { args: args || {} },
|
|
4693
|
+
tool: { name, args: args || {} },
|
|
4396
4694
|
});
|
|
4695
|
+
} catch (permErr) {
|
|
4696
|
+
return JSON.stringify({ ok: false, error: AskService.errorText(permErr) });
|
|
4697
|
+
}
|
|
4698
|
+
/* opencode skill aracı: katalogdaki SKILL.md gövdesini modele açar */
|
|
4699
|
+
if (name === 'skill') {
|
|
4700
|
+
const wanted = String((args && args.name) || '').trim();
|
|
4701
|
+
let list = [];
|
|
4702
|
+
try { list = skills.scan(); } catch {}
|
|
4703
|
+
const hit = list.find((s) => s && s.name === wanted) || null;
|
|
4704
|
+
if (!hit) {
|
|
4705
|
+
return JSON.stringify({
|
|
4706
|
+
ok: false,
|
|
4707
|
+
error:
|
|
4708
|
+
'skill bulunamadı: ' + (wanted || '(boş)') +
|
|
4709
|
+
(list.length ? ' — mevcut: ' + list.map((s) => s.name).join(', ') : ''),
|
|
4710
|
+
});
|
|
4711
|
+
}
|
|
4712
|
+
let body = '';
|
|
4713
|
+
try { body = fs.readFileSync(hit.path, 'utf8'); } catch {}
|
|
4714
|
+
return JSON.stringify({ ok: true, name: hit.name, path: hit.path, content: body.slice(0, 24000) });
|
|
4715
|
+
}
|
|
4716
|
+
} else {
|
|
4717
|
+
/* onay kapısı: riskli araçta dış onay bekle; reddedilirse araç çalışmaz.
|
|
4718
|
+
"always" onaylı araçlar doğrudan geçer. (BC dışı oturumlar — opencode
|
|
4719
|
+
izin sistemi yalnız Beast Code oturumlarında koşar) */
|
|
4720
|
+
if (
|
|
4721
|
+
(Engine.RISKY_TOOLS.has(name) || String(name).startsWith('mcp__')) &&
|
|
4722
|
+
!this.alwaysAllowTools.has(name) &&
|
|
4723
|
+
this.approvals && typeof this.approvals.request === 'function'
|
|
4724
|
+
) {
|
|
4725
|
+
emitSafe(this, sessionId, { type: 'status', status: `onay bekleniyor: ${name}` });
|
|
4726
|
+
let ok = false;
|
|
4727
|
+
try {
|
|
4728
|
+
ok = !!(await this.approvals.request({ sessionId, tool: name, args: args || {} }));
|
|
4729
|
+
} catch {}
|
|
4730
|
+
if (!ok) {
|
|
4731
|
+
return JSON.stringify({
|
|
4732
|
+
ok: false,
|
|
4733
|
+
error:
|
|
4734
|
+
'kullanıcı bu işlemi ONAYLAMADI (iptal edildi veya zaman aşımı). İşlemi yeniden deneme; kullanıcıya sormadan alternatif öner.',
|
|
4735
|
+
});
|
|
4736
|
+
}
|
|
4397
4737
|
}
|
|
4398
4738
|
}
|
|
4399
4739
|
/* GERİ ALMA GÜNLÜĞÜ: dosya yazımından ÖNCE eski içerik kayda geçer */
|
|
@@ -4419,6 +4759,9 @@ const skills = require('./skills');
|
|
|
4419
4759
|
return JSON.stringify(await mcp.call(name, args, signal));
|
|
4420
4760
|
}
|
|
4421
4761
|
if (name === 'memory_write') {
|
|
4762
|
+
/* Supermemory (lokal) DEFAULT: her yazım asenkron push edilir —
|
|
4763
|
+
sunucu ayaktaysa graf motoru özet/çelişki/unutma işlerini yürütür */
|
|
4764
|
+
this._smPush(sessionId, args.text);
|
|
4422
4765
|
/* bot oturumu → botun KENDİ hafıza store'una yaz (global Beast hafızasına değil).
|
|
4423
4766
|
mem0 açıkken: hash+semantic dedup'lı store'a gider, MEMORY.md aynası güncellenir. */
|
|
4424
4767
|
const bctx = this._sessionBotCtx(sessionId ? this.cache.get(String(sessionId)) : null);
|
|
@@ -4427,7 +4770,7 @@ const skills = require('./skills');
|
|
|
4427
4770
|
const r = await mem0.add('bot:' + bctx.id, [args.text]).catch(() => null);
|
|
4428
4771
|
if (r && r.ok) {
|
|
4429
4772
|
mem0.syncMirror('bot:' + bctx.id);
|
|
4430
|
-
return JSON.stringify({ ok: true, duplicate: r.skipped > 0, event: r.events[0] && r.events[0].event });
|
|
4773
|
+
return JSON.stringify({ ok: true, duplicate: r.skipped > 0, event: r.events[0] && r.events[0].event, supermemory: !!this._smConfig() });
|
|
4431
4774
|
}
|
|
4432
4775
|
}
|
|
4433
4776
|
return JSON.stringify(this.botMemory.append(bctx.id, args.text));
|
|
@@ -4436,7 +4779,7 @@ const skills = require('./skills');
|
|
|
4436
4779
|
const r = await mem0.add('main', [args.text]).catch(() => null);
|
|
4437
4780
|
if (r && r.ok) {
|
|
4438
4781
|
mem0.syncMirror('main');
|
|
4439
|
-
return JSON.stringify({ ok: true, duplicate: r.skipped > 0, event: r.events[0] && r.events[0].event });
|
|
4782
|
+
return JSON.stringify({ ok: true, duplicate: r.skipped > 0, event: r.events[0] && r.events[0].event, supermemory: !!this._smConfig() });
|
|
4440
4783
|
}
|
|
4441
4784
|
}
|
|
4442
4785
|
return JSON.stringify(memory.append(args.text));
|
|
@@ -4454,6 +4797,12 @@ const skills = require('./skills');
|
|
|
4454
4797
|
const bctx = this._sessionBotCtx(sessionId ? this.cache.get(String(sessionId)) : null);
|
|
4455
4798
|
const q = String(args.query || '');
|
|
4456
4799
|
const limit = clamp(Number(args.limit) || 5, 1, 15);
|
|
4800
|
+
/* Supermemory DEFAULT öncelik: hibrit arama + kullanıcı profili.
|
|
4801
|
+
Erişilemediyse/hata yerse yerel yollar aynen çalışır (fallback). */
|
|
4802
|
+
const smRes = await this._smSearch(sessionId, q, limit);
|
|
4803
|
+
if (smRes && smRes.results.length) {
|
|
4804
|
+
return JSON.stringify({ ok: true, query: q, ...smRes });
|
|
4805
|
+
}
|
|
4457
4806
|
if (bctx && this.botMemory) {
|
|
4458
4807
|
const rows = this.mem0Enabled
|
|
4459
4808
|
? await mem0.search('bot:' + bctx.id, q, { limit }).catch(() => [])
|
|
@@ -4488,8 +4837,8 @@ const skills = require('./skills');
|
|
|
4488
4837
|
if (name === 'delegate_task') {
|
|
4489
4838
|
const task = String(args.task || '').trim();
|
|
4490
4839
|
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);
|
|
4840
|
+
emitSafe(this, sessionId, { type: 'status', status: args.subagent_type === 'explore' ? 'task(explore)' : args.subagent_type ? 'task(general)' : 'delegate_task' });
|
|
4841
|
+
const result = await this._subagent(task, String(args.context || ''), signal, sessionId, String(args.subagent_type || ''));
|
|
4493
4842
|
return JSON.stringify({ ok: true, task, result: String(result).slice(0, 12000) });
|
|
4494
4843
|
}
|
|
4495
4844
|
if (name === 'bot_dm') {
|