beast-agent 2.14.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/package.json +1 -1
  2. package/src/agent/engine.js +499 -154
  3. package/src/agent/financetools.js +307 -0
  4. package/src/agent/opencode/agents.js +161 -0
  5. package/src/agent/opencode/askperm.js +135 -0
  6. package/src/agent/opencode/index.js +51 -0
  7. package/src/agent/opencode/permission.js +108 -0
  8. package/src/agent/opencode/prompts/anthropic.txt +105 -0
  9. package/src/agent/opencode/prompts/beast.txt +147 -0
  10. package/src/agent/opencode/prompts/build-switch.txt +5 -0
  11. package/src/agent/opencode/prompts/codex.txt +79 -0
  12. package/src/agent/opencode/prompts/default.txt +95 -0
  13. package/src/agent/opencode/prompts/edit.txt +10 -0
  14. package/src/agent/opencode/prompts/explore.txt +18 -0
  15. package/src/agent/opencode/prompts/gemini.txt +155 -0
  16. package/src/agent/opencode/prompts/glob.txt +6 -0
  17. package/src/agent/opencode/prompts/gpt.txt +107 -0
  18. package/src/agent/opencode/prompts/grep.txt +8 -0
  19. package/src/agent/opencode/prompts/kimi.txt +95 -0
  20. package/src/agent/opencode/prompts/max-steps.txt +16 -0
  21. package/src/agent/opencode/prompts/meta.txt +65 -0
  22. package/src/agent/opencode/prompts/plan.txt +26 -0
  23. package/src/agent/opencode/prompts/read.txt +14 -0
  24. package/src/agent/opencode/prompts/skill.txt +5 -0
  25. package/src/agent/opencode/prompts/task.txt +19 -0
  26. package/src/agent/opencode/prompts/todowrite.txt +44 -0
  27. package/src/agent/opencode/prompts/trinity.txt +97 -0
  28. package/src/agent/opencode/prompts/webfetch.txt +13 -0
  29. package/src/agent/opencode/prompts/websearch.txt +14 -0
  30. package/src/agent/opencode/prompts/write.txt +8 -0
  31. package/src/agent/opencode/systemprompt.js +157 -0
  32. package/src/agent/opencode/toolmap.js +316 -0
  33. package/src/agent/scripts/mt5_bridge.py +381 -0
  34. package/src/agent/tools.js +10 -3
  35. package/src/main.js +503 -69
  36. package/src/mt5bridge.js +215 -0
  37. package/src/preload.js +13 -3
  38. package/src/renderer/index.html +67 -0
  39. package/src/renderer/renderer.js +552 -53
  40. package/src/renderer/style.css +328 -1
  41. package/src/agent/android.js +0 -265
@@ -0,0 +1,307 @@
1
+ 'use strict';
2
+
3
+ /* Beast Finance — MT5 ajan araçları.
4
+ Handler'lar main süreçteki mt5bridge singleton'ına bağlanır; işlem açan
5
+ araçlar settings.finance.allowTrading / maxLot / maxPositions ile sınırlanır.
6
+ main tarafı setConfig/setNotify ile ayar ve bildirim kancasını enjekte eder. */
7
+
8
+ const mt5 = require('../mt5bridge');
9
+
10
+ const NAMES = [
11
+ 'mt5_status',
12
+ 'mt5_account',
13
+ 'mt5_market',
14
+ 'mt5_positions',
15
+ 'mt5_orders',
16
+ 'mt5_history',
17
+ 'mt5_trade',
18
+ 'mt5_close',
19
+ 'mt5_modify',
20
+ 'mt5_pending',
21
+ 'mt5_cancel',
22
+ ];
23
+
24
+ let getCfg = () => ({ allowTrading: false, maxLot: 0.1, maxPositions: 3, symbols: [] });
25
+ let notify = () => {};
26
+
27
+ function setConfig(fn) {
28
+ if (typeof fn === 'function') getCfg = fn;
29
+ }
30
+
31
+ function setNotify(fn) {
32
+ if (typeof fn === 'function') notify = fn;
33
+ }
34
+
35
+ function clampLot(v, maxLot) {
36
+ const n = Number(v);
37
+ if (!isFinite(n) || n <= 0) return 0;
38
+ return Math.min(n, maxLot);
39
+ }
40
+
41
+ async function bcall(method, params, timeoutMs) {
42
+ const r = await mt5.call(method, params, timeoutMs);
43
+ if (!r || r.ok !== true) throw new Error((r && r.error) || 'MT5 hatası');
44
+ return r.data;
45
+ }
46
+
47
+ function noteTrade(kind, data) {
48
+ try { notify({ kind, data, at: Date.now() }); } catch {}
49
+ }
50
+
51
+ function notConnected() {
52
+ return { ok: false, error: 'MT5 köprüsü bağlı değil — terminal açık mı? (panelde ⟳ ile yeniden bağlan)' };
53
+ }
54
+
55
+ const definitions = NAMES.map((name) => {
56
+ const specs = {
57
+ mt5_status: {
58
+ description:
59
+ 'MT5 (MetaTrader 5) köprü durumunu döndürür: köprü çalışıyor mu, terminale bağlı mı, terminal/hesap özeti. Her finance turunda VEYA bağlantı sorunlarında ilk çağrı.',
60
+ parameters: { type: 'object', properties: {} },
61
+ },
62
+ mt5_account: {
63
+ description:
64
+ 'MT5 hesap özeti: bakiye, özkaynak (equity), yüzen kâr/zarar, marj, serbest marj, marj seviyesi, kaldıraç, para birimi, hesap no/sunucu.',
65
+ parameters: { type: 'object', properties: {} },
66
+ },
67
+ mt5_market: {
68
+ description:
69
+ 'Verilen semboller için canlı fiyat: bid/ask, spread, digit, point. symbols verilmezse izleme listesi (watchlist) kullanılır. Örn: ["EURUSD","XAUUSD"].',
70
+ parameters: {
71
+ type: 'object',
72
+ properties: {
73
+ symbols: { type: 'array', items: { type: 'string' }, description: 'Sembol listesi (boşsa watchlist)' },
74
+ },
75
+ },
76
+ },
77
+ mt5_positions: {
78
+ description: 'Açık pozisyonlar: ticket, sembol, yön, lot, açılış fiyatı, güncel fiyat, SL/TP, yüzen kâr/zarar, açılış zamanı.',
79
+ parameters: { type: 'object', properties: {} },
80
+ },
81
+ mt5_orders: {
82
+ description: 'Bekleyen emirler (pending orders): ticket, sembol, tip (limit/stop), lot, fiyat, SL/TP.',
83
+ parameters: { type: 'object', properties: {} },
84
+ },
85
+ mt5_history: {
86
+ description: 'Kapanmış işlemler (deals) özeti: son N günde kapanan pozisyonlar, kâr/zarar, komisyon, swap. days=1 → son 24 saat.',
87
+ parameters: {
88
+ type: 'object',
89
+ properties: { days: { type: 'number', description: 'Kaç gün geriye bakılsın (varsayılan 1, en fazla 90)' } },
90
+ },
91
+ },
92
+ mt5_trade: {
93
+ description:
94
+ 'PİYASA EMRİ AÇAR: mt5_trade {symbol, side:"buy"|"sell", volume, sl?, tp?, comment?}. Lot limiti ve max pozisyon sayısı sistem tarafından zorlanır. Otomatik işlem anahtarı kapalıysa reddedilir. SL/TP vermek ŞIDDETLİ önerilir.',
95
+ parameters: {
96
+ type: 'object',
97
+ properties: {
98
+ symbol: { type: 'string', description: 'Örn: EURUSD' },
99
+ side: { type: 'string', enum: ['buy', 'sell'] },
100
+ volume: { type: 'number', description: 'Lot (max lot sınırına tabi)' },
101
+ sl: { type: 'number', description: 'Stop loss fiyatı (0 = yok)' },
102
+ tp: { type: 'number', description: 'Take profit fiyatı (0 = yok)' },
103
+ comment: { type: 'string', description: 'Kısa işlem notu' },
104
+ },
105
+ required: ['symbol', 'side', 'volume'],
106
+ },
107
+ },
108
+ mt5_close: {
109
+ description: 'Açık pozisyonu kapatır: mt5_close {ticket, volume?}. volume verilirse kısmi kapatır. Risk yönetimi için kullanılır.',
110
+ parameters: {
111
+ type: 'object',
112
+ properties: {
113
+ ticket: { type: 'number', description: 'Pozisyon ticket no' },
114
+ volume: { type: 'number', description: 'Kapatılacak lot (boşsa tamamı)' },
115
+ },
116
+ required: ['ticket'],
117
+ },
118
+ },
119
+ mt5_modify: {
120
+ description: 'Pozisyonun SL/TP seviyelerini günceller: mt5_modify {ticket, sl, tp}. SL’siz pozisyon bırakmamak için kullan.',
121
+ parameters: {
122
+ type: 'object',
123
+ properties: {
124
+ ticket: { type: 'number' },
125
+ sl: { type: 'number', description: 'Yeni SL (0 = kaldır)' },
126
+ tp: { type: 'number', description: 'Yeni TP (0 = kaldır)' },
127
+ },
128
+ required: ['ticket'],
129
+ },
130
+ },
131
+ mt5_pending: {
132
+ description:
133
+ 'BEKLEYEN EMİR koyar: mt5_pending {symbol, type:"buy_limit"|"sell_limit"|"buy_stop"|"sell_stop", volume, price, sl?, tp?}. Otomatik işlem anahtarına tabidir.',
134
+ parameters: {
135
+ type: 'object',
136
+ properties: {
137
+ symbol: { type: 'string' },
138
+ type: { type: 'string', enum: ['buy_limit', 'sell_limit', 'buy_stop', 'sell_stop'] },
139
+ volume: { type: 'number' },
140
+ price: { type: 'number' },
141
+ sl: { type: 'number' },
142
+ tp: { type: 'number' },
143
+ },
144
+ required: ['symbol', 'type', 'volume', 'price'],
145
+ },
146
+ },
147
+ mt5_cancel: {
148
+ description: 'Bekleyen emri iptal eder: mt5_cancel {ticket}.',
149
+ parameters: {
150
+ type: 'object',
151
+ properties: { ticket: { type: 'number' } },
152
+ required: ['ticket'],
153
+ },
154
+ },
155
+ };
156
+ const s = specs[name];
157
+ return { type: 'function', function: { name, ...s } };
158
+ });
159
+
160
+ const handlers = {
161
+ async mt5_status() {
162
+ const st = mt5.status();
163
+ return { ok: true, ...st };
164
+ },
165
+ async mt5_account() {
166
+ if (!mt5.running) return notConnected();
167
+ const data = await bcall('account', {}, 8000);
168
+ return { ok: true, ...data };
169
+ },
170
+ async mt5_market(args) {
171
+ if (!mt5.running) return notConnected();
172
+ const cfg = getCfg();
173
+ let syms = Array.isArray(args.symbols) ? args.symbols.map((s) => String(s).trim()).filter(Boolean) : [];
174
+ if (!syms.length) syms = Array.isArray(cfg.symbols) ? cfg.symbols : [];
175
+ const data = await bcall('symbols', { symbols: syms }, 10000);
176
+ return { ok: true, symbols: data && data.symbols ? data.symbols : [] };
177
+ },
178
+ async mt5_positions() {
179
+ if (!mt5.running) return notConnected();
180
+ const data = await bcall('positions', {}, 8000);
181
+ const list = (data && data.positions) || [];
182
+ return { ok: true, count: list.length, positions: list };
183
+ },
184
+ async mt5_orders() {
185
+ if (!mt5.running) return notConnected();
186
+ const data = await bcall('orders', {}, 8000);
187
+ const list = (data && data.orders) || [];
188
+ return { ok: true, count: list.length, orders: list };
189
+ },
190
+ async mt5_history(args) {
191
+ if (!mt5.running) return notConnected();
192
+ let days = Number(args.days);
193
+ if (!isFinite(days) || days <= 0) days = 1;
194
+ days = Math.min(days, 90);
195
+ const data = await bcall('deals', { days }, 12000);
196
+ const deals = (data && data.deals) || [];
197
+ /* hızlı özet: giriş/çıkış eşleşmesi yapmadan net realized P/L */
198
+ let profit = 0;
199
+ let wins = 0;
200
+ let losses = 0;
201
+ for (const dl of deals) {
202
+ const p = Number(dl.profit) || 0;
203
+ if (String(dl.entry) === '1' || dl.entry === 1 || p !== 0) {
204
+ profit += p + (Number(dl.swap) || 0) + (Number(dl.commission) || 0);
205
+ if (p > 0) wins++;
206
+ else if (p < 0) losses++;
207
+ }
208
+ }
209
+ return {
210
+ ok: true,
211
+ days,
212
+ count: deals.length,
213
+ netProfit: Math.round(profit * 100) / 100,
214
+ wins,
215
+ losses,
216
+ deals: deals.slice(-60),
217
+ };
218
+ },
219
+ async mt5_trade(args) {
220
+ const cfg = getCfg();
221
+ if (!cfg.allowTrading) {
222
+ return { ok: false, error: 'Otomatik işlem KAPALI — analiz/öneri modundasın. İşlem önerisini yaz, açma.' };
223
+ }
224
+ if (!mt5.running) return notConnected();
225
+ const side = String(args.side || '').toLowerCase();
226
+ if (side !== 'buy' && side !== 'sell') return { ok: false, error: "side 'buy' veya 'sell' olmalı" };
227
+ const symbol = String(args.symbol || '').trim().toUpperCase();
228
+ if (!symbol) return { ok: false, error: 'symbol gerekli' };
229
+ const maxLot = Number(cfg.maxLot) || 0.1;
230
+ const vol = clampLot(args.volume, maxLot);
231
+ if (!vol) return { ok: false, error: 'volume gerekli (pozitif sayı)' };
232
+ const pos = await bcall('positions', {}, 8000);
233
+ const maxPos = Number(cfg.maxPositions) || 3;
234
+ const openCount = ((pos && pos.positions) || []).length;
235
+ if (openCount >= maxPos) {
236
+ return { ok: false, error: `max eşzamanlı pozisyon dolu (${openCount}/${maxPos}) — önce bir pozisyon kapat` };
237
+ }
238
+ const data = await bcall('market', {
239
+ symbol,
240
+ side,
241
+ volume: vol,
242
+ sl: Number(args.sl) || 0,
243
+ tp: Number(args.tp) || 0,
244
+ deviation: 20,
245
+ comment: String(args.comment || 'Beast').slice(0, 26),
246
+ }, 20000);
247
+ noteTrade('trade', { symbol, side, volume: vol, result: data && data.result });
248
+ return { ok: true, opened: { symbol, side, volume: vol, sl: Number(args.sl) || 0, tp: Number(args.tp) || 0 }, result: data && data.result };
249
+ },
250
+ async mt5_close(args) {
251
+ const cfg = getCfg();
252
+ if (!mt5.running) return notConnected();
253
+ const ticket = Number(args.ticket);
254
+ if (!ticket) return { ok: false, error: 'ticket gerekli' };
255
+ let vol = Number(args.volume) || 0;
256
+ if (vol > 0) {
257
+ const maxLot = Number(cfg.maxLot) || 0.1;
258
+ if (vol > maxLot) return { ok: false, error: `kısmi kapatma lotu limiti aşıyor (max ${maxLot})` };
259
+ }
260
+ const data = await bcall('close', { ticket, volume: vol || 0 }, 20000);
261
+ noteTrade('close', { ticket, volume: vol || 'all', result: data && data.result });
262
+ return { ok: true, result: data && data.result };
263
+ },
264
+ async mt5_modify(args) {
265
+ if (!mt5.running) return notConnected();
266
+ const ticket = Number(args.ticket);
267
+ if (!ticket) return { ok: false, error: 'ticket gerekli' };
268
+ const data = await bcall('modify', {
269
+ ticket,
270
+ sl: Number(args.sl) || 0,
271
+ tp: Number(args.tp) || 0,
272
+ }, 20000);
273
+ noteTrade('modify', { ticket, sl: Number(args.sl) || 0, tp: Number(args.tp) || 0 });
274
+ return { ok: true, result: data && data.result };
275
+ },
276
+ async mt5_pending(args) {
277
+ const cfg = getCfg();
278
+ if (!cfg.allowTrading) {
279
+ return { ok: false, error: 'Otomatik işlem KAPALI — bekleyen emir de açılamaz. Öneriyi yaz.' };
280
+ }
281
+ if (!mt5.running) return notConnected();
282
+ const maxLot = Number(cfg.maxLot) || 0.1;
283
+ const vol = clampLot(args.volume, maxLot);
284
+ if (!vol) return { ok: false, error: 'volume gerekli' };
285
+ const data = await bcall('pending', {
286
+ symbol: String(args.symbol || '').trim().toUpperCase(),
287
+ type: String(args.type || '').toLowerCase(),
288
+ volume: vol,
289
+ price: Number(args.price) || 0,
290
+ sl: Number(args.sl) || 0,
291
+ tp: Number(args.tp) || 0,
292
+ comment: String(args.comment || 'Beast').slice(0, 26),
293
+ }, 20000);
294
+ noteTrade('pending', { symbol: args.symbol, type: args.type, volume: vol });
295
+ return { ok: true, result: data && data.result };
296
+ },
297
+ async mt5_cancel(args) {
298
+ if (!mt5.running) return notConnected();
299
+ const ticket = Number(args.ticket);
300
+ if (!ticket) return { ok: false, error: 'ticket gerekli' };
301
+ const data = await bcall('cancel', { ticket }, 20000);
302
+ noteTrade('cancel', { ticket });
303
+ return { ok: true, result: data && data.result };
304
+ },
305
+ };
306
+
307
+ module.exports = { definitions, handlers, NAMES, setConfig, setNotify };
@@ -0,0 +1,161 @@
1
+ 'use strict';
2
+
3
+ /* ---------- opencode agent/agent.ts portu (birebir) ----------
4
+ Beast Code (bcCode) oturumları artık "mod" değil AJAN koşar:
5
+ build (varsayılan) · plan (salt-okur) · general/explore (task alt-ajanları).
6
+
7
+ Agent.Info şeması (opencode):
8
+ { name, description, mode: 'subagent'|'primary'|'all', hidden, temperature,
9
+ permission: Ruleset, model?, prompt?, steps?, options }
10
+
11
+ Permission birleşim sırası (opencode birebir): defaults → ajan özel → user.
12
+ Son eşleşen kural kazanır (permission.evaluate findLast). */
13
+
14
+ const path = require('path');
15
+ const Permission = require('./permission');
16
+
17
+ /* opencode SystemPrompt.environment'daki gibi runtime klasörleri ajan
18
+ kurallarına basar: plan ajanı workspace içindeki .opencode/plans/*.md'ye
19
+ yazabilir (opencode: Global.Path.data/plans + .opencode/plans/*.md allow) */
20
+ function planRules(worktree) {
21
+ const ws = String(worktree || '');
22
+ return Permission.fromConfig({
23
+ question: 'allow',
24
+ plan_exit: 'allow',
25
+ task: { general: 'deny' },
26
+ external_directory: { '*': 'ask' },
27
+ edit: {
28
+ '*': 'deny',
29
+ [path.join('.opencode', 'plans', '*.md')]: 'allow',
30
+ ...(ws ? { [path.join(ws, '.opencode', 'plans', '*.md')]: 'allow' } : {}),
31
+ },
32
+ });
33
+ }
34
+
35
+ /* opencode agent.ts defaults (satır 119-136) — birebir.
36
+ Beast'te "question" aracı yok; kural etkisiz kalır, dokunma.
37
+ panel_run: Beast'e özgü (Sandbox ÇALIŞTIR paneli) — default DENY,
38
+ yalnız sandbox ajanında allow edilir (opencode custom-agent deseni). */
39
+ function defaultRuleset() {
40
+ return Permission.fromConfig({
41
+ '*': 'allow',
42
+ doom_loop: 'ask',
43
+ external_directory: { '*': 'ask' },
44
+ question: 'deny',
45
+ plan_enter: 'deny',
46
+ plan_exit: 'deny',
47
+ panel_run: 'deny',
48
+ // github/gitignore Node.gitignore desenine uygun .env koruması
49
+ read: {
50
+ '*': 'allow',
51
+ '*.env': 'ask',
52
+ '*.env.*': 'ask',
53
+ '*.env.example': 'allow',
54
+ },
55
+ });
56
+ }
57
+
58
+ /* Yerleşik ajanlar — opencode agent.ts:140-265 birebir (promptlar
59
+ opencode-dev'den kopyalandı: prompts/explore.txt) */
60
+ function build(opts = {}) {
61
+ const defaults = defaultRuleset();
62
+ const user = Permission.fromConfig(opts.user || {});
63
+ return {
64
+ build: {
65
+ name: 'build',
66
+ description: 'The default agent. Executes tools based on configured permissions.',
67
+ options: {},
68
+ permission: Permission.merge(
69
+ defaults,
70
+ Permission.fromConfig({ question: 'allow', plan_enter: 'allow' }),
71
+ user
72
+ ),
73
+ mode: 'primary',
74
+ native: true,
75
+ },
76
+ plan: {
77
+ name: 'plan',
78
+ description: 'Plan mode. Disallows all edit tools.',
79
+ options: {},
80
+ permission: Permission.merge(defaults, planRules(opts.worktree), user),
81
+ mode: 'primary',
82
+ native: true,
83
+ },
84
+ general: {
85
+ name: 'general',
86
+ description:
87
+ 'General-purpose agent for researching complex questions and executing multi-step tasks. Use this agent to execute multiple units of work in parallel.',
88
+ permission: Permission.merge(defaults, Permission.fromConfig({ todowrite: 'deny' }), user),
89
+ options: {},
90
+ mode: 'subagent',
91
+ native: true,
92
+ },
93
+ explore: {
94
+ name: 'explore',
95
+ permission: Permission.merge(
96
+ defaults,
97
+ Permission.fromConfig({
98
+ '*': 'deny',
99
+ grep: 'allow',
100
+ glob: 'allow',
101
+ list: 'allow',
102
+ bash: 'allow',
103
+ webfetch: 'allow',
104
+ websearch: 'allow',
105
+ read: 'allow',
106
+ external_directory: { '*': 'ask' },
107
+ }),
108
+ user
109
+ ),
110
+ description:
111
+ 'Fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. "src/components/**/*.tsx"), search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?"). When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "very thorough" for comprehensive analysis across multiple locations and naming conventions.',
112
+ prompt: opts.promptExplore || '',
113
+ options: {},
114
+ mode: 'subagent',
115
+ native: true,
116
+ },
117
+ /* opencode custom-agent deseni (mode: 'all'): Beast Sandbox — GitHub'dan
118
+ indirilmiş açık kaynak repo klasöründe koşan build ajanı. Repo disiplini
119
+ + panel_run (ÇALIŞTIR paneli) izni buradan gelir */
120
+ sandbox: {
121
+ name: 'sandbox',
122
+ description: 'Beast Sandbox agent. Works inside an open-source repo cloned from GitHub; follows the repo owner conventions.',
123
+ options: {},
124
+ permission: Permission.merge(
125
+ defaults,
126
+ Permission.fromConfig({
127
+ question: 'allow',
128
+ panel_run: { '*': 'allow' },
129
+ }),
130
+ user
131
+ ),
132
+ prompt: [
133
+ 'BEAST SANDBOX MODUNDASIN: GitHub\u2019dan indirilmi\u015f A\u00c7IK KAYNAK bir repo klas\u00f6r\u00fcndesin (Beast-Sandbox). Bu kodun sahibi de\u011filsin \u2014 hazir bir di\u015f proje; repo sahibinin konvansiyonlarina uy, gereksiz yeniden bi\u00e7imlendirme yapma.',
134
+ '\u0130\u015fe README + paket bildirim dosyalar\u0131yla ba\u015fla (list/glob); ba\u011f\u0131ml\u0131l\u0131klar kurulu de\u011filse \u00f6nce kurulum komutunu \u00e7al\u0131\u015ft\u0131r (npm install vb.).',
135
+ 'UZUN S\u00dcREL\u0130 sunucu/s\u00fcre\u00e7 (npm start, npm run dev, python app.py vb.) bash ile ASLA ba\u015flatma \u2014 tur kilitlenir; kullan\u0131c\u0131 \u00e7al\u0131\u015ft\u0131rmak isterse panel_run arac\u0131yla \u00c7ALI\u015eTIR paneline b\u0131rak (\u00e7\u0131kt\u0131 orada canl\u0131 akar) ve adresi kullan\u0131c\u0131ya bildir. panel_run yaln\u0131z B\u0130R s\u00fcre\u00e7 \u00e7al\u0131\u015ft\u0131r\u0131r.',
136
+ ].join('\n'),
137
+ mode: 'all',
138
+ native: false,
139
+ },
140
+ };
141
+ }
142
+
143
+ /* opencode Agent.get(name) — bilinmeyen isim null döner (çağıran hata basar) */
144
+ function get(name, opts = {}) {
145
+ const agents = build(opts);
146
+ return agents[name] || null;
147
+ }
148
+
149
+ /* opencode Agent.list() — alfabetik, build önce */
150
+ function list(opts = {}) {
151
+ const agents = Object.values(build(opts));
152
+ return agents.sort((a, b) => (a.name === 'build' ? -1 : b.name === 'build' ? 1 : a.name.localeCompare(b.name)));
153
+ }
154
+
155
+ /* opencode Agent.defaultInfo() — görünür primary ajan (build) */
156
+ function defaultInfo(opts = {}) {
157
+ const agents = Object.values(build(opts));
158
+ return agents.find((a) => a.mode !== 'subagent' && a.hidden !== true) || agents[0];
159
+ }
160
+
161
+ module.exports = { get, list, defaultInfo, build, defaultRuleset, planRules };
@@ -0,0 +1,135 @@
1
+ 'use strict';
2
+
3
+ /* ---------- opencode permission Service portu (index.ts:67-167) ----------
4
+ Ask akışı: her pattern için kural değerlendirilir (ruleset + approved) →
5
+ deny → PermissionDeniedError (asla sorulmaz, anında hata)
6
+ allow → geç
7
+ ask → kullanıcıya sorulur
8
+ Sorulacaklar: 'permission.asked' eventi yayınlanır (UI/WA dinler) ve
9
+ promise bekletilir. Cevap (reply):
10
+ 'once' → yalnız bu istek serbest
11
+ 'always' → pattern'ler oturum boyu approved listesine kural olarak eklenir;
12
+ aynı oturumdaki diğer pending'lerden artık allow olanlar da serbest bırakılır
13
+ 'reject' → RejectedError (feedback'siz) veya CorrectedError (feedback'li);
14
+ aynı oturumdaki DİĞER pending istekler de reddedilir (opencode birebir)
15
+
16
+ Hata nesneleri araç çıktısına JSON hata olarak akıtılır; opencode
17
+ core/v1/permission.ts mesajları kullanılır. */
18
+
19
+ const Permission = require('./permission');
20
+
21
+ let seq = 0;
22
+ function newId() {
23
+ return 'prm_' + Date.now().toString(36) + (++seq).toString(36);
24
+ }
25
+
26
+ class AskService {
27
+ constructor(emitFn) {
28
+ this.emit = typeof emitFn === 'function' ? emitFn : () => {};
29
+ this.pending = new Map(); // id -> { info, resolve, reject }
30
+ this.approved = []; // oturumlar arası paylaşılan kural listesi (engine'e aşılanır)
31
+ }
32
+
33
+ /* opencode Permission.ask — throws: { kind:'denied'|'rejected'|'corrected', feedback? } */
34
+ ask({ sessionId, ruleset, permission, patterns, metadata, always, tool }) {
35
+ const pats = Array.isArray(patterns) && patterns.length ? patterns : ['*'];
36
+ let needsAsk = false;
37
+ for (const pattern of pats) {
38
+ const rule = Permission.evaluate(permission, pattern, ruleset || [], this.approved);
39
+ if (rule.action === 'deny') {
40
+ throw { kind: 'denied', permission, pattern };
41
+ }
42
+ if (rule.action === 'allow') continue;
43
+ needsAsk = true;
44
+ }
45
+ if (!needsAsk) return Promise.resolve();
46
+
47
+ const id = newId();
48
+ const info = {
49
+ id,
50
+ sessionID: String(sessionId || ''),
51
+ permission,
52
+ patterns: pats,
53
+ metadata: metadata || {},
54
+ always: Array.isArray(always) ? always : [],
55
+ tool: tool || null,
56
+ };
57
+ return new Promise((resolve, reject) => {
58
+ this.pending.set(id, { info, resolve, reject });
59
+ try {
60
+ this.emit({ type: 'permission.asked', sessionId: info.sessionID, request: info });
61
+ } catch {}
62
+ /* güvenlik: emit kanalı koptuysa pending asılı kalmasın */
63
+ setTimeout(() => {
64
+ if (this.pending.has(id)) {
65
+ this.pending.delete(id);
66
+ reject({ kind: 'rejected' });
67
+ }
68
+ }, 5 * 60 * 1000);
69
+ });
70
+ }
71
+
72
+ /* opencode Permission.reply — UI/WA tarafından çağrılır */
73
+ reply(requestId, action, message) {
74
+ const id = String(requestId || '');
75
+ const existing = this.pending.get(id);
76
+ if (!existing) return { ok: false, error: 'bekleyen izin isteği yok' };
77
+ this.pending.delete(id);
78
+ const { info } = existing;
79
+ try {
80
+ this.emit({ type: 'permission.replied', sessionId: info.sessionID, requestId: id, reply: action });
81
+ } catch {}
82
+
83
+ if (action === 'reject') {
84
+ const err = message ? { kind: 'corrected', feedback: String(message) } : { kind: 'rejected' };
85
+ existing.reject(err);
86
+ /* aynı oturumdaki diğer pending'ler de reddedilir (opencode birebir) */
87
+ for (const [pid, item] of [...this.pending.entries()]) {
88
+ if (item.info.sessionID !== info.sessionID) continue;
89
+ this.pending.delete(pid);
90
+ try {
91
+ this.emit({ type: 'permission.replied', sessionId: item.info.sessionID, requestId: pid, reply: 'reject' });
92
+ } catch {}
93
+ item.reject({ kind: 'rejected' });
94
+ }
95
+ return { ok: true };
96
+ }
97
+
98
+ existing.resolve();
99
+ if (action === 'once') return { ok: true };
100
+
101
+ /* 'always' — pattern bazlı kural yazılır (opencode birebir) */
102
+ for (const pattern of info.always) {
103
+ this.approved.push({ permission: info.permission, pattern, action: 'allow' });
104
+ }
105
+ for (const [pid, item] of [...this.pending.entries()]) {
106
+ if (item.info.sessionID !== info.sessionID) continue;
107
+ const ok = item.info.patterns.every(
108
+ (pattern) => Permission.evaluate(item.info.permission, pattern, this.approved).action === 'allow'
109
+ );
110
+ if (!ok) continue;
111
+ this.pending.delete(pid);
112
+ try {
113
+ this.emit({ type: 'permission.replied', sessionId: item.info.sessionID, requestId: pid, reply: 'always' });
114
+ } catch {}
115
+ item.resolve();
116
+ }
117
+ return { ok: true };
118
+ }
119
+
120
+ /* bekleyen istekler (UI listesi için) */
121
+ list() {
122
+ return [...this.pending.values()].map((x) => x.info);
123
+ }
124
+
125
+ /* hata nesnesini araç çıktısı metnine çevirir (opencode mesajları) */
126
+ static errorText(err) {
127
+ if (!err) return 'Permission denied';
128
+ if (err.kind === 'denied') return 'Permission denied';
129
+ if (err.kind === 'corrected')
130
+ return 'The user rejected the permission request and gave feedback: ' + String(err.feedback || '');
131
+ return Permission.MESSAGES.rejected;
132
+ }
133
+ }
134
+
135
+ module.exports = { AskService };
@@ -0,0 +1,51 @@
1
+ 'use strict';
2
+
3
+ /* ---------- opencode mantığı — Beast Code portu (birleşim) ----------
4
+ opencode-dev/packages/opencode/src çekirdek mimarisinin CommonJS portu:
5
+
6
+ agents.js → agent/agent.ts (build/plan/general/explore + permission)
7
+ permission.js → permission/index.ts (ruleset, evaluate findLast, fromConfig)
8
+ askperm.js → permission Service (ask/reply: once|always|reject)
9
+ systemprompt.js → session/system.ts + reminders.ts
10
+ toolmap.js → tool/registry.ts builtin seti (read/edit/write/bash/...)
11
+ prompts/ → session/prompt/*.txt + tool/*.txt (opencode-dev'den birebir)
12
+
13
+ Kullanım (engine, bcCode oturumlarında):
14
+ opencode.agents.get(name, {worktree}) → Agent.Info
15
+ opencode.system.provider(modelId) → model-bazlı ana prompt
16
+ opencode.toolmap.definitions() → modele sunulacak opencode araç seti
17
+ opencode.toolmap.execMap(name, args) → Beast aracına çevirim
18
+ opencode.Permission → kural motoru
19
+ opencode.AskService → izin isteme/yanıtlama servisi */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+
24
+ const PROMPTS_DIR = path.join(__dirname, 'prompts');
25
+ function loadPrompt(name) {
26
+ try {
27
+ return fs.readFileSync(path.join(PROMPTS_DIR, name), 'utf8').replace(/\s+$/, '');
28
+ } catch {
29
+ return '';
30
+ }
31
+ }
32
+
33
+ const MAX_STEPS_PROMPT = loadPrompt('max-steps.txt');
34
+ const PROMPT_EXPLORE = loadPrompt('explore.txt');
35
+
36
+ const agents = require('./agents');
37
+ const Permission = require('./permission');
38
+ const { AskService } = require('./askperm');
39
+ const systemprompt = require('./systemprompt');
40
+ const toolmap = require('./toolmap');
41
+
42
+ module.exports = {
43
+ agents,
44
+ Permission,
45
+ AskService,
46
+ systemprompt,
47
+ toolmap,
48
+ MAX_STEPS_PROMPT,
49
+ PROMPT_EXPLORE,
50
+ loadPrompt,
51
+ };