beast-agent 2.30.5 → 2.30.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/engine.js +95 -22
- package/src/agent/opencode/askperm.js +7 -1
- package/src/main.js +2 -0
- package/src/renderer/i18n.js +8 -8
package/package.json
CHANGED
package/src/agent/engine.js
CHANGED
|
@@ -327,6 +327,7 @@ class Engine {
|
|
|
327
327
|
boyunca "always" desenlerini kural olarak taşır (opencode birebir). */
|
|
328
328
|
this._perm = new AskService((ev) => this.emit(ev));
|
|
329
329
|
this.bcAskApprovals = opts.approvals ? true : false; // onay ayarı → opencode user konfigi gibi "ask" üretir
|
|
330
|
+
this._perm.autoAllow = !this.bcAskApprovals; // güvenlik kapalı: BeastCode/Sandbox izin SORMAZ (deny yine geçerli)
|
|
330
331
|
/* opencode reminders.ts: bu oturumda daha önce plan ajanı koştu mu?
|
|
331
332
|
plan→build geçişinde build-switch.txt synthetic part'ı buna bağlanır */
|
|
332
333
|
this._bcWasPlan = new Map(); // sessionId -> bool
|
|
@@ -1608,11 +1609,13 @@ class Engine {
|
|
|
1608
1609
|
? 'plan'
|
|
1609
1610
|
: 'build';
|
|
1610
1611
|
const ws = (session && session.workspace) || this.workspace;
|
|
1611
|
-
|
|
1612
|
-
|
|
1612
|
+
/* BeastCode/Sandbox onayı ARIZİ değil: güvenlik açıkken yalnız SİLME
|
|
1613
|
+
işlemleri _perm.ask ile sorulur (aşağıda _isDeleteOp) — bash/edit'in
|
|
1614
|
+
tamamına asks koymak "her işlemde sor" demekti, kaldırıldı. */
|
|
1615
|
+
const info = opencode.agents.get(name, { worktree: ws, promptExplore: opencode.PROMPT_EXPLORE });
|
|
1613
1616
|
if (info) return info;
|
|
1614
1617
|
const fallbackName = session.sbSandbox ? 'sandbox' : 'build';
|
|
1615
|
-
return opencode.agents.get(fallbackName, { worktree: ws, promptExplore: opencode.PROMPT_EXPLORE
|
|
1618
|
+
return opencode.agents.get(fallbackName, { worktree: ws, promptExplore: opencode.PROMPT_EXPLORE });
|
|
1616
1619
|
}
|
|
1617
1620
|
|
|
1618
1621
|
/* opencode opencode ajan değişimi: /plan /build — oturumun ajanı döner,
|
|
@@ -1638,6 +1641,21 @@ class Engine {
|
|
|
1638
1641
|
}
|
|
1639
1642
|
}
|
|
1640
1643
|
|
|
1644
|
+
/* Güvenlik ayarı canlı değişimi (Ayarlar → Güvenlik):
|
|
1645
|
+
- kapalı → BeastCode/Sandbox dahil açık izin akışı hiç sormaz (autoAllow)
|
|
1646
|
+
- açık → riskli araçlarda onay ister (approvals köprüsü + bash/edit user kuralı)
|
|
1647
|
+
Bekleyen istekler serbest bırakılır (kapı kalktı; araç devam eder). */
|
|
1648
|
+
setApprovals(on) {
|
|
1649
|
+
this.bcAskApprovals = !!on;
|
|
1650
|
+
this._perm.autoAllow = !this.bcAskApprovals;
|
|
1651
|
+
if (!this.bcAskApprovals) {
|
|
1652
|
+
try {
|
|
1653
|
+
for (const req of this._perm.list()) this._perm.reply(req.id, 'once');
|
|
1654
|
+
} catch {}
|
|
1655
|
+
}
|
|
1656
|
+
return { bcAskApprovals: this.bcAskApprovals, autoAllow: this._perm.autoAllow };
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1641
1659
|
/* ---------- Supermemory (lokal) bellek köprüsü ---------- */
|
|
1642
1660
|
|
|
1643
1661
|
_smConfig() {
|
|
@@ -4126,15 +4144,23 @@ class Engine {
|
|
|
4126
4144
|
if (dups >= DOOM_LOOP_THRESHOLD) {
|
|
4127
4145
|
if (bcAgent) {
|
|
4128
4146
|
try {
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4147
|
+
/* doom_loop bir SİLME değil: onay kartıyla kullanıcıyı yorma —
|
|
4148
|
+
kural değerlendirmesi (deny) işler, ask otomatik geçer */
|
|
4149
|
+
const prevAuto = this._perm.autoAllow;
|
|
4150
|
+
this._perm.autoAllow = true;
|
|
4151
|
+
try {
|
|
4152
|
+
await this._perm.ask({
|
|
4153
|
+
sessionId: sid,
|
|
4154
|
+
ruleset: bcAgent.permission,
|
|
4155
|
+
permission: 'doom_loop',
|
|
4156
|
+
patterns: [name],
|
|
4157
|
+
always: [name],
|
|
4158
|
+
metadata: { callCount: DOOM_LOOP_THRESHOLD, arguments: (tc.function && tc.function.arguments) || '' },
|
|
4159
|
+
tool: { name, args },
|
|
4160
|
+
});
|
|
4161
|
+
} finally {
|
|
4162
|
+
this._perm.autoAllow = prevAuto;
|
|
4163
|
+
}
|
|
4138
4164
|
out = await this._execTool(name, args, ctrl.signal, sid);
|
|
4139
4165
|
} catch (permErr) {
|
|
4140
4166
|
out = JSON.stringify({
|
|
@@ -4883,6 +4909,33 @@ const skills = require('./skills');
|
|
|
4883
4909
|
opencode'da edit+write aynı "edit" iznine takılır — burada da ikisi birlikte */
|
|
4884
4910
|
static RISKY_TOOLS = new Set(['run_command', 'write_file', 'edit_file', 'python_run', 'email_send', 'watcher_add', 'git_commit', 'git_pr_create', 'xlsx_write', 'xlsx_edit']);
|
|
4885
4911
|
|
|
4912
|
+
/* SİLME desenleri — güvenlik AÇIKken onay kapısı YALNIZCA silmelerde sorar
|
|
4913
|
+
(kullanıcı isteği: her işlem değil, sadece silmeler). */
|
|
4914
|
+
static DELETE_RE =
|
|
4915
|
+
/(remove-item|\bri\s|\bdel\s|\bdel\b|\brd\b|\brmdir\b|\berase\b|\brm\s|\bunlink|shutil\s*\.\s*rmtree|os\s*\.\s*(remove|unlink|rmdir)|\brmtree\b|\.unlink\s*\(|fs\s*\.\s*(rm|unlink|rmdir)|rimraf|clear-content|\breg\s+delete|git\s+clean)/i;
|
|
4916
|
+
|
|
4917
|
+
/* Bu araç çağrısı bir SİLME mi? (komut/betik içeriği + araç adı deseni) */
|
|
4918
|
+
_isDeleteOp(name, args) {
|
|
4919
|
+
const n = String(name || '');
|
|
4920
|
+
const a = args || {};
|
|
4921
|
+
if (n === 'run_command' || n === 'panel_run') return Engine.DELETE_RE.test(String(a.command || ''));
|
|
4922
|
+
if (n === 'python_run') return Engine.DELETE_RE.test(String(a.code || ''));
|
|
4923
|
+
return /(delete|remove|unlink|rmdir|rmtree)/i.test(n);
|
|
4924
|
+
}
|
|
4925
|
+
|
|
4926
|
+
/* "Bir daha sorma" anahtarı: aynı silme işlemi için tekrar sorulmaz */
|
|
4927
|
+
_deleteKey(name, args) {
|
|
4928
|
+
const a = args || {};
|
|
4929
|
+
const raw =
|
|
4930
|
+
name === 'run_command' || name === 'panel_run'
|
|
4931
|
+
? String(a.command || '')
|
|
4932
|
+
: name === 'python_run'
|
|
4933
|
+
? String(a.code || '')
|
|
4934
|
+
: String(name || '');
|
|
4935
|
+
const s = raw.replace(/\s+/g, ' ').trim();
|
|
4936
|
+
return (s.length > 200 ? s.slice(0, 200) + '…' : s) || '*';
|
|
4937
|
+
}
|
|
4938
|
+
|
|
4886
4939
|
/* ANA KOD KİLİDİ: yıkıcı işlem desenleri (korumalı yol + bu desen = engel) */
|
|
4887
4940
|
static DESTRUCTIVE_RE =
|
|
4888
4941
|
/(remove-item|\bri\s|\bdel\s|\bdel\b|\brd\b|\brmdir\b|\berase\b|set-content|out-file|add-content|new-item|clear-content|move-item|rename-item|\bren\s|\bcopy-item|\bmove\b|\bmv\s|\bcp\s|>>|[^|]>\s|\bicacls|\btakeown|\bformat\b|\bfsutil|\breg\s+(add|delete)|schtasks|stop-process|\btaskkill|\bkill\b|\brm\s|\bunlink|shutil|os\.remove|os\.unlink|open\([^)]*['"][wa]['"]\)|write_text|to_csv\(|savefig\(|\.write\()/i;
|
|
@@ -4975,13 +5028,18 @@ const skills = require('./skills');
|
|
|
4975
5028
|
}
|
|
4976
5029
|
if (isBc) {
|
|
4977
5030
|
/* opencode permission akışı (tool/external-directory.ts + her aracın
|
|
4978
|
-
ctx.ask'ı):
|
|
4979
|
-
|
|
4980
|
-
ask
|
|
5031
|
+
ctx.ask'ı): deny anında hata döner (asla sorulmaz), allow sessiz geçer.
|
|
5032
|
+
GÜVENLİK AÇIKken yalnız SİLME işlemleri 'permission.asked' ile sorulur;
|
|
5033
|
+
diğer ask kuralları otomatik serbesttir (deny yine geçerlidir). */
|
|
4981
5034
|
const ws = this._sessionWorkspace(sessionId);
|
|
4982
5035
|
const bcAgent = this._bcAgentInfo(bcSess);
|
|
4983
5036
|
const ruleset = bcAgent ? bcAgent.permission : [];
|
|
5037
|
+
const isDel = this.bcAskApprovals && this._isDeleteOp(name, args);
|
|
5038
|
+
const prevAuto = this._perm.autoAllow;
|
|
4984
5039
|
try {
|
|
5040
|
+
/* 1) deny kuralları her durumda işler; ask'ler silme dışında otomatik
|
|
5041
|
+
geçer (güvenlik açıkken kullanıcıyı yormama) */
|
|
5042
|
+
this._perm.autoAllow = true;
|
|
4985
5043
|
const ext = this._bcExternalCheck(name, args, ws);
|
|
4986
5044
|
if (ext) {
|
|
4987
5045
|
await this._perm.ask({ sessionId, ruleset, ...ext, metadata: { tool: name, args: args || {} } });
|
|
@@ -4996,19 +5054,34 @@ const skills = require('./skills');
|
|
|
4996
5054
|
metadata: { args: args || {} },
|
|
4997
5055
|
tool: { name, args: args || {} },
|
|
4998
5056
|
});
|
|
5057
|
+
/* 2) güvenlik AÇIK + SİLME → onay sorusu (tek gerçek ask) */
|
|
5058
|
+
if (isDel) {
|
|
5059
|
+
this._perm.autoAllow = false;
|
|
5060
|
+
const key = this._deleteKey(name, args);
|
|
5061
|
+
await this._perm.ask({
|
|
5062
|
+
sessionId,
|
|
5063
|
+
ruleset: [{ permission: 'delete', pattern: '*', action: 'ask' }],
|
|
5064
|
+
permission: 'delete',
|
|
5065
|
+
patterns: [key],
|
|
5066
|
+
always: [key],
|
|
5067
|
+
metadata: { tool: name, args: args || {} },
|
|
5068
|
+
tool: { name, args: args || {} },
|
|
5069
|
+
});
|
|
5070
|
+
}
|
|
4999
5071
|
} catch (permErr) {
|
|
5000
5072
|
return JSON.stringify({ ok: false, error: AskService.errorText(permErr) });
|
|
5073
|
+
} finally {
|
|
5074
|
+
this._perm.autoAllow = prevAuto;
|
|
5001
5075
|
}
|
|
5002
5076
|
} else {
|
|
5003
|
-
/* onay kapısı:
|
|
5004
|
-
|
|
5005
|
-
izin sistemi
|
|
5077
|
+
/* onay kapısı: güvenlik açıkken YALNIZCA SİLME işlemlerinde dış onay
|
|
5078
|
+
beklenir; reddedilirse araç çalışmaz. "always" onaylılar doğrudan
|
|
5079
|
+
geçer. (BC dışı oturumlar — opencode izin sistemi Beast Code'da koşar) */
|
|
5006
5080
|
if (
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
String(name).startsWith('tool__')) && /* kişisel tool = kullanıcı kodu koşturur */
|
|
5081
|
+
this.approvals &&
|
|
5082
|
+
typeof this.approvals.request === 'function' &&
|
|
5010
5083
|
!this.alwaysAllowTools.has(name) &&
|
|
5011
|
-
this.
|
|
5084
|
+
this._isDeleteOp(name, args)
|
|
5012
5085
|
) {
|
|
5013
5086
|
emitSafe(this, sessionId, { type: 'status', status: `onay bekleniyor: ${name}` });
|
|
5014
5087
|
let ok = false;
|
|
@@ -28,6 +28,9 @@ class AskService {
|
|
|
28
28
|
this.emit = typeof emitFn === 'function' ? emitFn : () => {};
|
|
29
29
|
this.pending = new Map(); // id -> { info, resolve, reject }
|
|
30
30
|
this.approved = []; // oturumlar arası paylaşılan kural listesi (engine'e aşılanır)
|
|
31
|
+
/* Güvenlik ayarı KAPALI: 'ask' kuralı yine de sorulmaz — deny korunur,
|
|
32
|
+
ask otomatik serbesttir (tüm yetki ajanda). Engine ayarlar ile günceller. */
|
|
33
|
+
this.autoAllow = false;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
/* opencode Permission.ask — throws: { kind:'denied'|'rejected'|'corrected', feedback? } */
|
|
@@ -40,6 +43,7 @@ class AskService {
|
|
|
40
43
|
throw { kind: 'denied', permission, pattern };
|
|
41
44
|
}
|
|
42
45
|
if (rule.action === 'allow') continue;
|
|
46
|
+
if (this.autoAllow) continue; /* güvenlik kapalı: sorma, geç */
|
|
43
47
|
needsAsk = true;
|
|
44
48
|
}
|
|
45
49
|
if (!needsAsk) return Promise.resolve();
|
|
@@ -60,12 +64,14 @@ class AskService {
|
|
|
60
64
|
this.emit({ type: 'permission.asked', sessionId: info.sessionID, request: info });
|
|
61
65
|
} catch {}
|
|
62
66
|
/* güvenlik: emit kanalı koptuysa pending asılı kalmasın */
|
|
63
|
-
setTimeout(() => {
|
|
67
|
+
const timer = setTimeout(() => {
|
|
64
68
|
if (this.pending.has(id)) {
|
|
65
69
|
this.pending.delete(id);
|
|
66
70
|
reject({ kind: 'rejected' });
|
|
67
71
|
}
|
|
68
72
|
}, 5 * 60 * 1000);
|
|
73
|
+
/* bekleyen onay süreci/prosesi canlı tutmasın (özellikle test/çıkış) */
|
|
74
|
+
if (timer && typeof timer.unref === 'function') timer.unref();
|
|
69
75
|
});
|
|
70
76
|
}
|
|
71
77
|
|
package/src/main.js
CHANGED
|
@@ -6206,6 +6206,8 @@ ipcMain.handle('sec:set', (_e, cfg) => {
|
|
|
6206
6206
|
if (engine) {
|
|
6207
6207
|
engine.approvals = settings.security.approvals ? approvalsBridge : null;
|
|
6208
6208
|
engine.alwaysAllowTools = new Set(settings.security.alwaysAllow);
|
|
6209
|
+
/* BeastCode + Sandbox izin akışı CANLI güncellenir: kapalıyken hiç sorulmaz */
|
|
6210
|
+
if (typeof engine.setApprovals === 'function') engine.setApprovals(settings.security.approvals);
|
|
6209
6211
|
}
|
|
6210
6212
|
log.info('sec', `güvenlik: onay kapısı ${settings.security.approvals ? 'AÇIK' : 'KAPALI (her şey serbest)'}`);
|
|
6211
6213
|
return { approvals: settings.security.approvals, alwaysAllow: settings.security.alwaysAllow };
|
package/src/renderer/i18n.js
CHANGED
|
@@ -435,14 +435,14 @@
|
|
|
435
435
|
security_h2: 'Güvenlik',
|
|
436
436
|
st_open: 'Aç',
|
|
437
437
|
st_del: 'Sil',
|
|
438
|
-
sec_toggle: '
|
|
439
|
-
sec_sub: 'Kapalıyken
|
|
438
|
+
sec_toggle: 'İzin iste: yalnızca silme işlemlerinde onay sor (Beast Code, Sandbox dahil)',
|
|
439
|
+
sec_sub: 'Kapalıyken (varsayılan) HİÇBİR yerde izin istenmez — Beast Code, Sandbox ve tüm araçlar serbest; tüm yetki ajandadır. Açıkken YALNIZCA SİLME işlemlerinde onay istenir: sohbette onay kartı + WhatsApp\u2019a mesaj gider. Diğer işlemler (yazma, düzenleme, komut çalıştırma, mail…) sorulmaz.',
|
|
440
440
|
sec_note: 'Onay: sohbet kartındaki butonlar ya da /approve · Bu araç için bir daha sorma: /approve always · Reddet: /deny',
|
|
441
441
|
sec_always_h2: 'Bir Daha Sorulmayan Araçlar',
|
|
442
442
|
sec_always_empty: 'Henüz "always" onaylı araç yok.',
|
|
443
443
|
sec_clear_always: 'Listeyi Temizle',
|
|
444
|
-
sec_on_toast: 'Onay
|
|
445
|
-
sec_off_toast: 'Onay
|
|
444
|
+
sec_on_toast: 'Onay açık — yalnızca silme işlemlerinde ajan sorar',
|
|
445
|
+
sec_off_toast: 'Onay kapalı — hiçbir yerde sorulmaz, her şey serbest',
|
|
446
446
|
sec_ok: 'Onayla',
|
|
447
447
|
sec_always: 'Her zaman',
|
|
448
448
|
sec_no: 'Reddet',
|
|
@@ -1147,14 +1147,14 @@
|
|
|
1147
1147
|
security_h2: 'Security',
|
|
1148
1148
|
st_open: 'Open',
|
|
1149
1149
|
st_del: 'Delete',
|
|
1150
|
-
sec_toggle: 'Ask
|
|
1151
|
-
sec_sub: 'When off,
|
|
1150
|
+
sec_toggle: 'Ask permission: only for deletions (including Beast Code and Sandbox)',
|
|
1151
|
+
sec_sub: 'When off (default) NOTHING asks for permission anywhere — Beast Code, Sandbox and all tools are free; the agent has full authority. When on, approval is asked ONLY for deletions: an approval card in chat + a WhatsApp message. Other actions (write, edit, run commands, mail…) never ask.',
|
|
1152
1152
|
sec_note: 'Approve: chat card buttons or /approve · Never ask again for this tool: /approve always · Deny: /deny',
|
|
1153
1153
|
sec_always_h2: 'Tools Never Asked Again',
|
|
1154
1154
|
sec_always_empty: 'No "always" approved tools yet.',
|
|
1155
1155
|
sec_clear_always: 'Clear List',
|
|
1156
|
-
sec_on_toast: 'Approval
|
|
1157
|
-
sec_off_toast: 'Approval
|
|
1156
|
+
sec_on_toast: 'Approval ON — the agent asks only for deletions',
|
|
1157
|
+
sec_off_toast: 'Approval OFF — nothing asks, everything free',
|
|
1158
1158
|
sec_ok: 'Approve',
|
|
1159
1159
|
sec_always: 'Always',
|
|
1160
1160
|
sec_no: 'Deny',
|