beast-agent 2.15.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/src/agent/engine.js +417 -154
  3. package/src/agent/opencode/agents.js +161 -0
  4. package/src/agent/opencode/askperm.js +135 -0
  5. package/src/agent/opencode/index.js +51 -0
  6. package/src/agent/opencode/permission.js +108 -0
  7. package/src/agent/opencode/prompts/anthropic.txt +105 -0
  8. package/src/agent/opencode/prompts/beast.txt +147 -0
  9. package/src/agent/opencode/prompts/build-switch.txt +5 -0
  10. package/src/agent/opencode/prompts/codex.txt +79 -0
  11. package/src/agent/opencode/prompts/default.txt +95 -0
  12. package/src/agent/opencode/prompts/edit.txt +10 -0
  13. package/src/agent/opencode/prompts/explore.txt +18 -0
  14. package/src/agent/opencode/prompts/gemini.txt +155 -0
  15. package/src/agent/opencode/prompts/glob.txt +6 -0
  16. package/src/agent/opencode/prompts/gpt.txt +107 -0
  17. package/src/agent/opencode/prompts/grep.txt +8 -0
  18. package/src/agent/opencode/prompts/kimi.txt +95 -0
  19. package/src/agent/opencode/prompts/max-steps.txt +16 -0
  20. package/src/agent/opencode/prompts/meta.txt +65 -0
  21. package/src/agent/opencode/prompts/plan.txt +26 -0
  22. package/src/agent/opencode/prompts/read.txt +14 -0
  23. package/src/agent/opencode/prompts/skill.txt +5 -0
  24. package/src/agent/opencode/prompts/task.txt +19 -0
  25. package/src/agent/opencode/prompts/todowrite.txt +44 -0
  26. package/src/agent/opencode/prompts/trinity.txt +97 -0
  27. package/src/agent/opencode/prompts/webfetch.txt +13 -0
  28. package/src/agent/opencode/prompts/websearch.txt +14 -0
  29. package/src/agent/opencode/prompts/write.txt +8 -0
  30. package/src/agent/opencode/systemprompt.js +157 -0
  31. package/src/agent/opencode/toolmap.js +316 -0
  32. package/src/main.js +93 -41
  33. package/src/preload.js +2 -0
  34. package/src/renderer/renderer.js +59 -15
package/src/main.js CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ 'use strict';
2
2
 
3
3
  const { app, BrowserWindow, WebContentsView, ipcMain, shell, dialog, Tray, Menu, nativeImage, desktopCapturer, session, net: electronNet, clipboard } = require('electron');
4
4
  const path = require('path');
@@ -1335,6 +1335,18 @@ function resolveApproval(id, ok, always) {
1335
1335
  }
1336
1336
 
1337
1337
  function resolveFirstApproval(ok, always) {
1338
+ /* opencode permission (BC): önce opencode izin istekleri yanıtlanır —
1339
+ cevap 'once' | 'always' | 'reject' üçlüsüdür (permission/reply portu) */
1340
+ if (engine && typeof engine.replyPermission === 'function') {
1341
+ try {
1342
+ const pend = engine._perm && engine._perm.list ? engine._perm.list() : [];
1343
+ if (pend.length) {
1344
+ const action = ok ? (always ? 'always' : 'once') : 'reject';
1345
+ const r = engine.replyPermission(pend[0].id, action);
1346
+ if (r && r.ok) return { ok: true, tool: pend[0].permission };
1347
+ }
1348
+ } catch {}
1349
+ }
1338
1350
  const first = pendingApprovals.keys().next();
1339
1351
  if (first.done) return { ok: false, error: 'bekleyen onay yok' };
1340
1352
  return resolveApproval(first.value, ok, always);
@@ -1677,17 +1689,17 @@ async function tryWaSlash(jid, rawText, senderNum, payload0) {
1677
1689
  `Tekrar kodlamak için: /beastcode`;
1678
1690
  }
1679
1691
  } else if (waBcMode.has(jid) && (cmd === 'plan' || cmd === 'build' || cmd === 'auto')) {
1680
- /* BeastCode modunda çalışma modu: /plan · /build · /auto */
1692
+ /* opencode ajan değişimi (WA): /plan → plan ajanı (salt-okur),
1693
+ /build · /auto → build ajanı (varsayılan; auto opencode'da yoktur) */
1681
1694
  const sid = waChats.get(jid);
1682
1695
  const s = sid ? engine.cache.get(sid) : null;
1683
1696
  if (s && s.bcCode) {
1684
- s.bcMode = cmd;
1685
- engine.cache.set(sid, s);
1686
- out = cmd === 'plan'
1687
- ? '*🔍 PLAN MODU* — dosyaları inceler, kod YAZMAZ; adım adım uygulama planı verir.'
1697
+ const agent = engine.setBcAgent(s.id, cmd === 'plan' ? 'plan' : 'build');
1698
+ out = agent === 'plan'
1699
+ ? '*🔍 PLAN AJANI* (opencode plan) — salt-okur: dosyaları okur/inceler, kod YAZMAZ; adım adım uygulama planı verir. Uygulamak için /build yaz.'
1688
1700
  : cmd === 'build'
1689
- ? '*🛠 BUILD MODU* — son planı uygular: dosyalar, komutlar, doğrulama.'
1690
- : '*⚡ AUTO MOD* — kısa plan + uygulama + doğrulama.';
1701
+ ? '*🛠 BUILD AJANI* (opencode build) — bağlamı inceler, todowrite ile planlar, uygular, doğrular.'
1702
+ : '*⚡ BUILD AJANI* (opencode build) — kısa plan + hemen uygulama + doğrulama.';
1691
1703
  } else {
1692
1704
  out = 'Bu komut yalnız BeastCode modunda çalışır — önce /beastcode yaz.';
1693
1705
  }
@@ -2441,14 +2453,12 @@ async function processWaMessage(jid, payload, senderNum, requeues = 0) {
2441
2453
  }
2442
2454
  engine.setSessionModel(sid, botCfg && !botCfg.admin ? (botCfg.model || null) : null);
2443
2455
  /* BEASTCODE MODU: sohbet uzaktan kodlama modundaysa oturumu Beast Code
2444
- olarak tazele — restart sonrası bcCode/workspace bayrakları dosyadan
2456
+ olarak tazele — restart sonrası bcCode/bcAgent/workspace bayrakları dosyadan
2445
2457
  yüklenmediği için masaüstü bcFlush disipliniyle her mesajda bindirilir */
2446
2458
  if (waBcMode.has(jid)) {
2447
2459
  const bs = engine.cache.get(sid);
2448
2460
  if (bs) {
2449
- bs.bcCode = true;
2450
- bs.workspace = waBcWorkspace();
2451
- engine.cache.set(sid, bs);
2461
+ bcBindOc(bs, waBcWorkspace());
2452
2462
  }
2453
2463
  }
2454
2464
  waLog(`perm=${fmtPerm(perm)} bot=${botId} sid=${sid}${waBcMode.has(jid) ? ' beastcode=1' : ''}`);
@@ -3100,6 +3110,21 @@ function reloadBackend() {
3100
3110
  },
3101
3111
  emit: (ev) => {
3102
3112
  if (win && !win.isDestroyed()) win.webContents.send('agent:event', ev);
3113
+ /* opencode permission (BC): ask kanalı UI'a zaten yukarıda düştü — WA
3114
+ sahibine de tek satır bildir gitsin (/approve · /approve always · /deny) */
3115
+ if (ev.type === 'permission.asked' && ev.request && wa && wa.connected) {
3116
+ try {
3117
+ const own = waOwnerNum();
3118
+ if (own) {
3119
+ const pats = Array.isArray(ev.request.patterns) ? ev.request.patterns.join(', ') : '*';
3120
+ sendWaSafe(
3121
+ own + '@s.whatsapp.net',
3122
+ '\u26A0\uFE0F *\u0130zin bekleniyor* \u2014 `' + String(ev.request.permission) + '`\n' +
3123
+ String(pats).slice(0, 160) + '\n\nOnayla: `/approve`\nBu desen i\u00e7in bir daha sorma: `/approve always`\nReddet: `/deny`'
3124
+ ).catch(() => {});
3125
+ }
3126
+ } catch {}
3127
+ }
3103
3128
  try { empatiRememberFromEvent(ev); } catch {} /* empati hafızası: sohbet → kayıt + ilgi öğrenme */
3104
3129
  empatiFlushInjectsOnDone(ev); /* tur ortasında kuyruğa düşen proaktif bildirimleri iş bitince geçmişe bas */
3105
3130
  flushDesktopOnDone(ev); /* biriken desktop mesajlarını sıraya bas */
@@ -6005,7 +6030,21 @@ ipcMain.handle('sec:set', (_e, cfg) => {
6005
6030
  log.info('sec', `güvenlik: onay kapısı ${settings.security.approvals ? 'AÇIK' : 'KAPALI (her şey serbest)'}`);
6006
6031
  return { approvals: settings.security.approvals, alwaysAllow: settings.security.alwaysAllow };
6007
6032
  });
6008
- ipcMain.handle('approval:respond', (_e, { id, ok, always }) => resolveApproval(id, ok, always));
6033
+ ipcMain.handle('approval:respond', (_e, { id, ok, always }) => resolveApproval(id, ok, always));
6034
+ /* opencode permission reply (BC): UI kartı üçlü cevap verir —
6035
+ 'once' | 'always' | 'reject' (+ opsiyonel geri bildirim) */
6036
+ ipcMain.handle('permission:reply', (_e, payload) => {
6037
+ try {
6038
+ if (!engine || typeof engine.replyPermission !== 'function') return { ok: false, error: 'ajan hazır değil' };
6039
+ return engine.replyPermission(
6040
+ String((payload && payload.requestId) || ''),
6041
+ String((payload && payload.action) || 'once'),
6042
+ String((payload && payload.message) || '')
6043
+ );
6044
+ } catch (e) {
6045
+ return { ok: false, error: String((e && e.message) || e) };
6046
+ }
6047
+ });
6009
6048
 
6010
6049
  /* ---------------- #Update IPC ---------------- */
6011
6050
  ipcMain.handle('update:status', async () => {
@@ -7419,6 +7458,23 @@ ipcMain.handle('terminal:stop', () => {
7419
7458
  panele orada akıtılır. */
7420
7459
  const bcSessions = new Map(); /* klasör yolu → sessionId */
7421
7460
 
7461
+ /* opencode bayrak bindirme (bcCode disiplini): bcCode/bcAgent runtime
7462
+ bayrakları restart sonrası jsonl'den gelmez — her mesajda yeniden bindirilir.
7463
+ Ajan: 'plan' | 'build' | 'sandbox' (bcMode:'plan' oturumları geriye uyumlu
7464
+ plan'a düşer; Sandbox oturumları sandbox ajanına sabitlenir) */
7465
+ function bcBindOc(s, folder, agent) {
7466
+ s.workspace = folder;
7467
+ s.bcCode = true;
7468
+ const known = s.bcAgent === 'plan' || s.bcAgent === 'build' || s.bcAgent === 'sandbox';
7469
+ if (agent) {
7470
+ s.bcAgent = agent;
7471
+ } else if (!known) {
7472
+ s.bcAgent = String(s.bcMode || '').toLowerCase() === 'plan' ? 'plan' : 'build';
7473
+ }
7474
+ engine.cache.set(s.id, s);
7475
+ return s;
7476
+ }
7477
+
7422
7478
  function bcGetSession(folder) {
7423
7479
  let sid = bcSessions.get(folder);
7424
7480
  if (sid) {
@@ -7431,7 +7487,8 @@ function bcGetSession(folder) {
7431
7487
  const s = engine._load(engine.createSession().id);
7432
7488
  s.messages = s.messages || [];
7433
7489
  s.bgTitle = 'Beast Code'; /* _view.isBg → sohbet geçmişi listesinde gizli */
7434
- s.bcCode = true; /* engine: her işte todo planı çıkar (BEAST CODE MODU bloğu) */
7490
+ s.bcCode = true; /* opencode ajan sistemi: build/plan ajanları engine'de koşar */
7491
+ s.bcAgent = 'build'; /* opencode varsayılan ajanı (agent.ts:141) */
7435
7492
  try {
7436
7493
  fs.appendFileSync(
7437
7494
  engine._file(s.id),
@@ -7500,9 +7557,7 @@ function bcFlush(folder) {
7500
7557
  clearTimeout(q.timer);
7501
7558
  return;
7502
7559
  }
7503
- s.workspace = folder;
7504
- s.bcCode = true;
7505
- engine.cache.set(s.id, s);
7560
+ bcBindOc(s, folder);
7506
7561
  const payload = mergedAtts ? { text: merged, attachments: mergedAtts } : merged;
7507
7562
  if (engine.send(s.id, payload, { userAction: true })) {
7508
7563
  bcQueue.delete(folder);
@@ -7543,25 +7598,21 @@ ipcMain.handle('beastcode:send', async (_e, payload) => {
7543
7598
 
7544
7599
  if (!engine.publicState().hasModel) return { ok: false, error: 'model yok — Ayarlar → Provider sekmesinden ekle' };
7545
7600
  /* oturumu HEMEN aç: cevap daima gerçek sessionId taşır — renderer paneli bu id ile
7546
- eşler; boşta/kuyrukta olsa bile id yoksa panel olayları eşleyemez ve ölü kalır */
7547
- const s = bcGetSession(ws);
7548
- s.workspace = ws; /* soldaki klasörde çalış */
7549
- s.bcCode = true; /* todo disiplini + iş sonu hızlı kapanış (engine) */
7550
- engine.cache.set(s.id, s);
7601
+ eşler; boşta/kuyrukta olsa bile id yoksa panel olayları eşleyemez ve ölü kalır */
7602
+ const s = bcBindOc(bcGetSession(ws), ws);
7551
7603
  const busy = engine.isBusy(s.id);
7552
7604
  if (modeM) {
7553
- /* mod değişimi anında uygulanır — meşgulken de (yalnız bayrak; engine çakışmaz) */
7554
- s.bcMode = modeM[1].toLowerCase();
7605
+ /* opencode ajan değişimi (agent.ts: plan/build primary ajanlar) — anında
7606
+ uygulanır, meşgulken de (yalnız ajan bayrağı; koşan tur çakışmaz) */
7607
+ const agent = engine.setBcAgent(s.id, modeM[1].toLowerCase() === 'plan' ? 'plan' : 'build');
7555
7608
  if (win && !win.isDestroyed()) {
7556
- const body = modeM[1].toLowerCase() === 'plan'
7557
- ? 'PLAN MODU — dosyaları okuyup inceler, KOD YAZMAZ; adım adım uygulama planı verir.'
7558
- : modeM[1].toLowerCase() === 'build'
7559
- ? 'BUILD MODU — son planı UYGULAR: dosyaları düzenler, komutları çalıştırır, doğrular.'
7560
- : 'OTOMATİK MOD — önce kısa plan, sonra uygulama + doğrulama (OpenCode disiplini).';
7561
- win.webContents.send('agent:event', { sessionId: s.id, type: 'bc-mode', mode: s.bcMode, body });
7609
+ const body = agent === 'plan'
7610
+ ? 'PLAN AJANI (opencode plan) — salt-okur: dosyaları okur/inceler, KOD YAZMAZ; adım adım uygulama planı verir. Uygulamak için /build.'
7611
+ : 'BUILD AJANI (opencode build) — bağlamı inceler, todowrite ile planlar, uygular, doğrular.';
7612
+ win.webContents.send('agent:event', { sessionId: s.id, type: 'bc-agent', agent, body });
7562
7613
  }
7563
- return { ok: true, sessionId: s.id, mode: s.bcMode };
7564
- }
7614
+ return { ok: true, sessionId: s.id, agent, mode: agent };
7615
+ }
7565
7616
  if (busy) {
7566
7617
  /* OPENCODE STEER: koşan tur varken mesaj kuyrukta BEKLEMEZ — engine.send
7567
7618
  konuşmaya ekler, koşan tur sonraki istekte görür (ana chat ile aynı) */
@@ -7655,8 +7706,7 @@ ipcMain.handle('bc:open', async (_e, payload) => {
7655
7706
  if (s.bgTitle && s.bgTitle !== 'Beast Code') return { ok: false, error: 'bu oturum Beast Code oturumu değil' };
7656
7707
  /* mevcut klasöre bağlan: sonraki mesajlar BU oturumda sürer */
7657
7708
  const ws = ideRoot();
7658
- s.workspace = ws;
7659
- s.bcCode = true;
7709
+ bcBindOc(s, ws);
7660
7710
  bcMarkWs(s, ws);
7661
7711
  engine.cache.set(s.id, s);
7662
7712
  bcSessions.set(ws, s.id);
@@ -7671,7 +7721,8 @@ ipcMain.handle('bc:open', async (_e, payload) => {
7671
7721
  ok: true,
7672
7722
  sessionId: s.id,
7673
7723
  busy: !!engine.isBusy(s.id),
7674
- mode: s.bcMode || '',
7724
+ agent: s.bcAgent || 'build',
7725
+ mode: s.bcAgent || 'build',
7675
7726
  messages: msgs.slice(-200),
7676
7727
  };
7677
7728
  });
@@ -8773,8 +8824,9 @@ function sandboxGetSession(folder) {
8773
8824
  const s = engine._load(engine.createSession().id);
8774
8825
  s.messages = s.messages || [];
8775
8826
  s.bgTitle = 'Beast Sandbox'; /* _view.isBg → sohbet geçmişinde gizli */
8776
- s.bcCode = true; /* todo disiplini + hızlı iş kapanışı (engine) */
8777
- s.sbSandbox = true; /* engine: SANDBOX sistem bloğu — ajan açık kaynak repoda olduğunu bilir */
8827
+ s.bcCode = true; /* opencode ajan sistemi: sandbox ajanı engine'de koşar */
8828
+ s.sbSandbox = true; /* engine: 'sandbox' ajanı çözümlemesi */
8829
+ s.bcAgent = 'sandbox'; /* opencode custom-agent: repo disiplini + panel_run */
8778
8830
  try {
8779
8831
  fs.appendFileSync(
8780
8832
  engine._file(s.id),
@@ -8817,10 +8869,10 @@ function sbFlush(folder) {
8817
8869
  clearTimeout(q.timer);
8818
8870
  return;
8819
8871
  }
8820
- s.workspace = folder;
8821
- s.bcCode = true;
8822
- s.sbSandbox = true;
8823
- engine.cache.set(s.id, s);
8872
+ /* opencode mantığı: Sandbox da ajan sistemiyle koşar — 'sandbox' ajanı
8873
+ (repo disiplini + panel_run izni, opencode/agents.js'te tanımlı) */
8874
+ s.sbSandbox = true; /* engine: sandbox ajanı çözümlemesi + ÇALIŞTIR paneli köprüsü */
8875
+ bcBindOc(s, folder, 'sandbox');
8824
8876
  const payload = mergedAtts ? { text: merged, attachments: mergedAtts } : merged;
8825
8877
  engine.send(s.id, payload, { userAction: true });
8826
8878
  sbQueue.delete(folder);
package/src/preload.js CHANGED
@@ -87,6 +87,8 @@ contextBridge.exposeInMainWorld('beast', {
87
87
  bcOpen: (id) => ipcRenderer.invoke('bc:open', { id }),
88
88
  bcDelete: (id) => ipcRenderer.invoke('bc:delete', { id }),
89
89
  bcDeleteAll: () => ipcRenderer.invoke('bc:deleteAll'),
90
+ /* opencode permission (BC): UI kartı cevabı — 'once' | 'always' | 'reject' */
91
+ permissionReply: (requestId, action, message) => ipcRenderer.invoke('permission:reply', { requestId, action, message }),
90
92
  studioSend: (msg, attachments) => ipcRenderer.invoke('studio:send', { msg, attachments }),
91
93
  studioStop: () => ipcRenderer.invoke('studio:stop'),
92
94
  studioNew: () => ipcRenderer.invoke('studio:new'),
@@ -1,4 +1,4 @@
1
- 'use strict';
1
+ 'use strict';
2
2
 
3
3
  /* Beast renderer — vanilla JS, event-driven via window.beast IPC bridge. */
4
4
 
@@ -762,7 +762,7 @@ function closeChatToolGroup() {
762
762
  function argSummary(name, args) {
763
763
  try {
764
764
  if (name === 'run_command') return String(args.command || '');
765
- if (name === 'read_file' || name === 'write_file' || name === 'edit_file') return String(args.path || '');
765
+ if (name === 'read_file' || name === 'write_file' || name === 'edit_file' || name === 'read' || name === 'write' || name === 'edit') return String(args.path || args.filePath || '');
766
766
  if (name === 'grep') return String(args.pattern || '');
767
767
  if (name === 'glob') return String(args.pattern || '');
768
768
  if (name === 'list_dir') return String(args.path || '.');
@@ -3126,8 +3126,39 @@ async function renderUpdatePane(autoCheck) {
3126
3126
  if (autoCheck) beast.updateCheck().catch(() => {});
3127
3127
  }
3128
3128
 
3129
- /* Sohbete onay kartı düşürür — Onayla / Her zaman / Reddet */function showApprovalCard(ev) {
3130
- streamEl = null;
3129
+ /* opencode permission kartı (BC): izin isteği — Bir kez / Her zaman / Reddet.
3130
+ ev: {id, permission, patterns, metadata, always, tool} */
3131
+ function showPermissionCard(req, inBc) {
3132
+ const toolName = (req.tool && req.tool.name) || req.permission || 'izin';
3133
+ let preview = '';
3134
+ try {
3135
+ preview = JSON.stringify(req.metadata || {}).slice(0, 200);
3136
+ } catch {}
3137
+ const card = document.createElement('div');
3138
+ card.className = 'tool-card';
3139
+ card.innerHTML =
3140
+ `<div class="tool-head"><span class="tool-icon"><svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></span>` +
3141
+ `<span class="tool-name">${escapeHtml(String(req.permission || 'izin'))}</span>` +
3142
+ `<span class="tool-arg">${escapeHtml(String(Array.isArray(req.patterns) ? req.patterns.join(', ') : '*').slice(0, 160))}</span></div>` +
3143
+ `<div class="tool-body open" style="display:flex;gap:8px;align-items:center">` +
3144
+ `<button class="btn ap-ok">${_t('sec_ok')}</button>` +
3145
+ `<button class="btn ghost ap-always">${_t('sec_always')}</button>` +
3146
+ `<button class="btn ghost ap-no">${_t('sec_no')}</button>` +
3147
+ `</div>`;
3148
+ const done = (txt) => {
3149
+ card.querySelector('.tool-body').innerHTML = `<span class="sub">${escapeHtml(txt)}</span>`;
3150
+ };
3151
+ card.querySelector('.ap-ok').addEventListener('click', async () => { await beast.permissionReply(req.id, 'once'); done(_t('sec_ok_done')); });
3152
+ card.querySelector('.ap-always').addEventListener('click', async () => { await beast.permissionReply(req.id, 'always'); done(_t('sec_always_done')); });
3153
+ card.querySelector('.ap-no').addEventListener('click', async () => { await beast.permissionReply(req.id, 'reject'); done(_t('sec_no_done')); });
3154
+ if (inBc && typeof bcLine === 'function') {
3155
+ bcLine('t-dim', '⏳ izin bekleniyor: ' + toolName + (preview ? ' — ' + preview : ''));
3156
+ }
3157
+ els.msgs.appendChild(card);
3158
+ scrollDown(true);
3159
+ }
3160
+
3161
+ /* Sohbete onay kartı düşürür — Onayla / Her zaman / Reddet */function showApprovalCard(ev) { streamEl = null;
3131
3162
  const card = document.createElement('div');
3132
3163
  card.className = 'tool-card';
3133
3164
  card.innerHTML =
@@ -5061,6 +5092,15 @@ function onEvent(ev) {
5061
5092
  return;
5062
5093
  }
5063
5094
  if (ev.type === 'install-progress') { updateInstallPct(ev); return; }
5095
+ /* opencode permission (BC): izin kartı — Bir kez / Her zaman / Reddet.
5096
+ BC oturumunda kart IDE panel akışına düşer; diğerlerinde sohbete */
5097
+ if (ev.type === 'permission.asked') {
5098
+ const req = ev.request || {};
5099
+ const isBc = bcSessionId && ev.sessionId === bcSessionId;
5100
+ if (!isBc && ev.sessionId && ev.sessionId !== activeId) return;
5101
+ showPermissionCard(req, isBc);
5102
+ return;
5103
+ }
5064
5104
  /* Headroom durum değişimi → açık olan sekmeyi tazele (Token Sıkıştırma / Kurulum) */
5065
5105
  if (ev.type === 'headroom-status') {
5066
5106
  try {
@@ -5187,7 +5227,7 @@ function onEvent(ev) {
5187
5227
  setStatus(ev.name + '…');
5188
5228
  termAgentEvent(ev);
5189
5229
  /* ajan dosya yazarsa/editlerse açık IDE sekmesini izlemeye al */
5190
- if ((ev.name === 'write_file' || ev.name === 'edit_file') && ev.args && ev.args.path) codeWriteWatch.set(ev.callId, ev.args.path);
5230
+ if ((ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'write' || ev.name === 'edit') && ev.args && ev.args.path) codeWriteWatch.set(ev.callId, ev.args.path);
5191
5231
  break;
5192
5232
  case 'tool-end':
5193
5233
  finishToolCard(ev.callId, ev.ok, ev.result, ev.diff);
@@ -9388,7 +9428,7 @@ function bcIngest(ev) {
9388
9428
  bcSetBusy(true); /* kuyruktan tetiklenen işte panel kendini kilitler */
9389
9429
  bcToolBoxStart(ev.callId, ev.name, ev.args);
9390
9430
  /* ajan dosya yazarsa/editlerse açık IDE sekmesini izlemeye al */
9391
- if ((ev.name === 'write_file' || ev.name === 'edit_file') && ev.args && ev.args.path) codeWriteWatch.set(ev.callId, ev.args.path);
9431
+ if ((ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'write' || ev.name === 'edit') && ev.args && ev.args.path) codeWriteWatch.set(ev.callId, ev.args.path);
9392
9432
  break;
9393
9433
  case 'tool-end':
9394
9434
  bcToolBoxEnd(ev.callId, ev.ok, ev.result, ev.diff);
@@ -9398,7 +9438,7 @@ function bcIngest(ev) {
9398
9438
  codeReloadIfOpen(wp);
9399
9439
  }
9400
9440
  /* dosya/klasör üreten araçlardan sonra soldaki ağaç otomatik tazelensin */
9401
- if (ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'run_command' || ev.name === 'python_run') {
9441
+ if (ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'write' || ev.name === 'edit' || ev.name === 'run_command' || ev.name === 'python_run') {
9402
9442
  ideRefreshTree();
9403
9443
  }
9404
9444
  break;
@@ -9411,13 +9451,17 @@ function bcIngest(ev) {
9411
9451
  bcQrShow(ev.qr, ev.url);
9412
9452
  break;
9413
9453
  case 'bc-mode':
9414
- /* OpenCode disiplini çalışma modu: başlıkta rozet + panelde bilgi satırı */
9415
- if (els.bcTitle) {
9416
- els.bcTitle.textContent =
9417
- ev.mode === 'plan' ? 'BEAST CODE · PLAN' :
9418
- ev.mode === 'build' ? 'BEAST CODE · BUILD' : 'BEAST CODE';
9454
+ case 'bc-agent':
9455
+ /* opencode ajan değişimi: başlıkta rozet + panelde bilgi satırı */
9456
+ {
9457
+ const agentName = ev.agent || ev.mode || '';
9458
+ if (els.bcTitle) {
9459
+ els.bcTitle.textContent =
9460
+ agentName === 'plan' ? 'BEAST CODE · PLAN' :
9461
+ agentName === 'build' ? 'BEAST CODE · BUILD' : 'BEAST CODE';
9462
+ }
9463
+ bcLine('t-dim', '[' + (ev.body || agentName || 'ajan değişti') + ']');
9419
9464
  }
9420
- bcLine('t-dim', '[' + (ev.body || ev.mode || 'mod değişti') + ']');
9421
9465
  break;
9422
9466
  case 'done':
9423
9467
  bcFlushStream();
@@ -9863,7 +9907,7 @@ function stIngest(ev) {
9863
9907
  break;
9864
9908
  case 'tool-end':
9865
9909
  stToolBoxEnd(ev.callId, ev.ok, ev.result, ev.diff);
9866
- if (ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'run_command' || ev.name === 'python_run') {
9910
+ if (ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'write' || ev.name === 'edit' || ev.name === 'run_command' || ev.name === 'python_run') {
9867
9911
  ideRefreshTree(); /* ffmpeg çıktısı anında ağaca düşer */
9868
9912
  }
9869
9913
  break;
@@ -10094,7 +10138,7 @@ function sbIngest(ev) {
10094
10138
  const ok = ev.ok !== false;
10095
10139
  sbLine(ok ? 't-dim' : 't-err', (ok ? ' \u2713 ' : ' \u2717 ') + String(ev.result || '').replace(/\s+/g, ' ').slice(0, 160));
10096
10140
  /* ajan dosya yazarsa/editlerse soldaki repo ağacı canlı tazelenir */
10097
- if (ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'run_command') ideRefreshTree();
10141
+ if (ev.name === 'write_file' || ev.name === 'edit_file' || ev.name === 'write' || ev.name === 'edit' || ev.name === 'run_command') ideRefreshTree();
10098
10142
  break;
10099
10143
  }
10100
10144
  case 'status':