beast-agent 2.44.13 → 2.44.14

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.44.13",
4
+ "version": "2.44.14",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
package/src/main.js CHANGED
@@ -7985,6 +7985,36 @@ function termSend(ev) {
7985
7985
  if (win && !win.isDestroyed()) win.webContents.send('agent:event', ev);
7986
7986
  }
7987
7987
 
7988
+ /* TERMİNAL ÇIKTI COALESCE: alt süreçten gelen data chunk'ları 80ms'de bir TEK
7989
+ 'term-batch' IPC'sinde birleştirilir. Aksi halde saniyede yüzlerce IPC +
7990
+ renderer'da satır satır DOM/layout = "Yanıt vermiyor" donması. */
7991
+ const TERM_BATCH_MS = 80;
7992
+ const TERM_BATCH_BYTES = 48 * 1024;
7993
+ const termBatches = new Map(); // id -> { timer, bytes, events: [{stream,text}] }
7994
+
7995
+ function termQueue(id, stream, text) {
7996
+ const key = String(id || termShellId || '');
7997
+ let b = termBatches.get(key);
7998
+ if (!b) {
7999
+ b = { timer: null, bytes: 0, events: [] };
8000
+ termBatches.set(key, b);
8001
+ }
8002
+ b.events.push({ stream, chunk: String(text) });
8003
+ b.bytes += String(text).length;
8004
+ if (b.bytes >= TERM_BATCH_BYTES) return termFlush(key); // tampon taştı — hemen boşalt
8005
+ if (!b.timer) b.timer = setTimeout(() => termFlush(key), TERM_BATCH_MS);
8006
+ }
8007
+
8008
+ function termFlush(id) {
8009
+ const key = String(id || '');
8010
+ const b = termBatches.get(key);
8011
+ if (!b) return;
8012
+ if (b.timer) { clearTimeout(b.timer); b.timer = null; }
8013
+ termBatches.delete(key);
8014
+ if (!b.events.length) return;
8015
+ termSend({ type: 'term-batch', id: key, events: b.events });
8016
+ }
8017
+
7988
8018
  /* Kalıcı kabuğu başlat: cmd /q /k — girdi pipe'ından satır satır okur,
7989
8019
  prompt yazmaz. chcp 65001 → Türkçe yollar (Masaüstü vb.) doğru çözülür. */
7990
8020
  function termShellSpawn() {
@@ -8009,14 +8039,15 @@ function termShellSpawn() {
8009
8039
  if (termForwarded > TERM_FWD_CAP) {
8010
8040
  if (!termCapNotified) {
8011
8041
  termCapNotified = true;
8042
+ termFlush(termShellId);
8012
8043
  termSend({ type: 'term-out', id: termShellId, stream: 'out', chunk: '\n[beast] çıktı çok büyük — iletim durduruldu (komut sürüyor)\n' });
8013
8044
  }
8014
8045
  return;
8015
8046
  }
8016
- termSend({ type: 'term-out', id: termShellId, stream: 'out', chunk: String(d) });
8047
+ termQueue(termShellId, 'out', String(d));
8017
8048
  });
8018
8049
  child.stderr.on('data', (d) => {
8019
- termSend({ type: 'term-out', id: termShellId, stream: 'err', chunk: String(d) });
8050
+ termQueue(termShellId, 'err', String(d));
8020
8051
  });
8021
8052
  child.on('exit', () => {
8022
8053
  if (termChild === child) termChild = null;
@@ -8041,6 +8072,7 @@ ipcMain.handle('terminal:run', (_e, payload) => {
8041
8072
  if (!termChild || !termChild.stdin.writable) termShellSpawn();
8042
8073
  if (!termChild) return { ok: false, error: 'kabuk başlatılamadı' };
8043
8074
  const id = 't' + Date.now().toString(36) + ++termShellSeq;
8075
+ termFlush(termShellId); // önceki komuttan kalan çıktı sırayı bozmasın
8044
8076
  termShellId = id;
8045
8077
  termForwarded = 0;
8046
8078
  termCapNotified = false;
@@ -8073,12 +8105,14 @@ ipcMain.handle('terminal:run', (_e, payload) => {
8073
8105
  }
8074
8106
  child.stdout.setEncoding('utf8');
8075
8107
  child.stderr.setEncoding('utf8');
8076
- child.stdout.on('data', (d) => termSend({ type: 'term-out', id, stream: 'out', chunk: String(d) }));
8077
- child.stderr.on('data', (d) => termSend({ type: 'term-out', id, stream: 'err', chunk: String(d) }));
8108
+ child.stdout.on('data', (d) => termQueue(id, 'out', String(d)));
8109
+ child.stderr.on('data', (d) => termQueue(id, 'err', String(d)));
8078
8110
  child.on('error', (err) => {
8111
+ termFlush(id);
8079
8112
  termSend({ type: 'term-end', id, code: -1, error: String((err && err.message) || err) });
8080
8113
  });
8081
8114
  child.on('close', (code) => {
8115
+ termFlush(id);
8082
8116
  termSend({ type: 'term-end', id, code: code == null ? -1 : code });
8083
8117
  });
8084
8118
  return { ok: true, id };
@@ -8092,6 +8126,7 @@ ipcMain.handle('terminal:stop', () => {
8092
8126
  try { termChild.kill(); } catch {}
8093
8127
  termChild = null;
8094
8128
  }
8129
+ termFlush(termShellId);
8095
8130
  termSend({ type: 'term-end', id: termShellId, code: 130, error: 'komut durduruldu — kalıcı CMD yeniden hazır' });
8096
8131
  termShellId = null;
8097
8132
  return { ok: true };
@@ -6187,29 +6187,13 @@ function onEvent(ev) {
6187
6187
  if (!busy) break;
6188
6188
  setStatus(ev.status === 'idle' ? (busy ? 'düşünüyor…' : '') : ev.status === 'thinking' ? 'düşünüyor…' : ev.status + '…');
6189
6189
  break;
6190
- case 'term-out': {
6191
- /* KALICI CMD: her komutun sonunda marker satırı gelir —
6192
- __BEAST_EOF__<yol>__BEAST_EOF__ → yazdırılmaz, cwd güncellenir, kilit açılır */
6193
- const chunk = String(ev.chunk || '');
6194
- if (ev.stream === 'err') { termLine('t-err', chunk, false); break; }
6195
- termPendBuf += chunk;
6196
- const parts = termPendBuf.split(/\r?\n/);
6197
- termPendBuf = parts.pop(); /* son yarım satır bekler */
6198
- for (const line of parts) {
6199
- const m = /__BEAST_EOF__(.*)__BEAST_EOF__/.exec(line.trim());
6200
- if (m) {
6201
- const cwd2 = m[1].trim();
6202
- if (cwd2) {
6203
- els.termCwd.textContent = cwd2;
6204
- els.termCwd.title = cwd2;
6205
- }
6206
- termCmdDone({ code: 0, silent: true });
6207
- continue;
6208
- }
6209
- if (line.trim()) termLine('t-out', line, false);
6210
- }
6190
+ case 'term-out':
6191
+ termHandleOut(ev);
6192
+ break;
6193
+ case 'term-batch':
6194
+ /* main 80ms'de bir birleştirilmiş çıktı paketi gönderir (IPC/satır yükü azalır) */
6195
+ for (const sub of (ev.events || [])) termHandleOut({ ...sub, id: ev.id });
6211
6196
  break;
6212
- }
6213
6197
  case 'term-end':
6214
6198
  termCmdDone(ev);
6215
6199
  break;
@@ -6316,6 +6300,7 @@ function termSetHeight(h) {
6316
6300
  function termSetOpen(v) {
6317
6301
  termOpen = !!v;
6318
6302
  els.termPanel.hidden = !v;
6303
+ if (v) termFlushLines(); // panel kapalıyken biriken satırlar şimdi basılır
6319
6304
  els.termResize.hidden = !v || ideModeOn(); /* IDE'de terminal bölme içi — boyutlandırma yok */
6320
6305
  document.body.classList.toggle('term-open', v);
6321
6306
  termSetShell(termShell);
@@ -6344,16 +6329,38 @@ function termScroll(force) {
6344
6329
  if (force || near) b.scrollTop = b.scrollHeight;
6345
6330
  }
6346
6331
 
6347
- function termLine(cls, text, time = true) {
6348
- const el = document.createElement('div');
6349
- el.className = 't-line ' + (cls || '');
6350
- const ts = time ? '<span class="t-time">' + new Date().toTimeString().slice(0, 8) + '</span> ' : '';
6351
- el.innerHTML = ts + escapeHtml(String(text ?? ''));
6352
- els.termOut.appendChild(el);
6332
+ /* satır KUYRUĞU: komut çıktısı hızlı akarken satır satır DOM + scroll/layout
6333
+ renderer'ı kilitliyordu ("Yanıt vermiyor"). Satırlar tampona girer, 100ms'de
6334
+ bir TEK fragment ile DOM'a basılır; panel kapalıysa DOM'a hiç dokunulmaz. */
6335
+ let termLineBuf = [];
6336
+ let termLineTimer = null;
6337
+
6338
+ function termFlushLines() {
6339
+ if (termLineTimer) { clearTimeout(termLineTimer); termLineTimer = null; }
6340
+ if (!termLineBuf.length || !els.termOut) return;
6341
+ const items = termLineBuf.splice(0);
6342
+ const frag = document.createDocumentFragment();
6343
+ for (const it of items) {
6344
+ const el = document.createElement('div');
6345
+ el.className = 't-line ' + (it.cls || '');
6346
+ const ts = it.time ? '<span class="t-time">' + new Date().toTimeString().slice(0, 8) + '</span> ' : '';
6347
+ el.innerHTML = ts + escapeHtml(String(it.text ?? ''));
6348
+ frag.appendChild(el);
6349
+ }
6350
+ els.termOut.appendChild(frag);
6353
6351
  while (els.termOut.childElementCount > TERM_MAX_LINES) els.termOut.removeChild(els.termOut.firstChild);
6354
6352
  termScroll();
6355
6353
  }
6356
6354
 
6355
+ function termLine(cls, text, time = true) {
6356
+ termLineBuf.push({ cls, text, time });
6357
+ const cap = TERM_MAX_LINES * 2;
6358
+ if (termLineBuf.length > cap) termLineBuf.splice(0, termLineBuf.length - cap);
6359
+ if (!termOpen) return; /* panel kapalı — açılınca termFlushLines ile basılır */
6360
+ if (termLineBuf.length >= 300) termFlushLines();
6361
+ else if (!termLineTimer) termLineTimer = setTimeout(termFlushLines, 100);
6362
+ }
6363
+
6357
6364
  function termBanner(cwd) {
6358
6365
  if (termBannerDone) return;
6359
6366
  termBannerDone = true;
@@ -6392,6 +6399,29 @@ function termAgentEvent(ev) {
6392
6399
  }
6393
6400
  }
6394
6401
 
6402
+ /* KALICI CMD çıktısı: her komutun sonunda marker satırı gelir —
6403
+ __BEAST_EOF__<yol>__BEAST_EOF__ → yazdırılmaz, cwd güncellenir, kilit açılır */
6404
+ function termHandleOut(ev) {
6405
+ const chunk = String(ev.chunk || '');
6406
+ if (ev.stream === 'err') { termLine('t-err', chunk, false); return; }
6407
+ termPendBuf += chunk;
6408
+ const parts = termPendBuf.split(/\r?\n/);
6409
+ termPendBuf = parts.pop(); /* son yarım satır bekler */
6410
+ for (const line of parts) {
6411
+ const m = /__BEAST_EOF__(.*)__BEAST_EOF__/.exec(line.trim());
6412
+ if (m) {
6413
+ const cwd2 = m[1].trim();
6414
+ if (cwd2) {
6415
+ els.termCwd.textContent = cwd2;
6416
+ els.termCwd.title = cwd2;
6417
+ }
6418
+ termCmdDone({ code: 0, silent: true });
6419
+ continue;
6420
+ }
6421
+ if (line.trim()) termLine('t-out', line, false);
6422
+ }
6423
+ }
6424
+
6395
6425
  function termCmdDone(ev) {
6396
6426
  termRunning = false;
6397
6427
  els.termStop.hidden = true;
@@ -7712,7 +7742,7 @@ async function init() {
7712
7742
  try { termSetHeight(parseInt(localStorage.getItem('beast.termH')) || 180); } catch {}
7713
7743
  if (els.termCBtn) els.termCBtn.addEventListener('click', () => termToggle('cmd'));
7714
7744
  if (els.termClose) els.termClose.addEventListener('click', () => termSetOpen(false));
7715
- if (els.termClear) els.termClear.addEventListener('click', () => { els.termOut.innerHTML = ''; });
7745
+ if (els.termClear) els.termClear.addEventListener('click', () => { els.termOut.innerHTML = ''; termLineBuf = []; });
7716
7746
  if (els.termStop) els.termStop.addEventListener('click', () => beast.terminalStop().catch(() => {}));
7717
7747
  if (els.termInput) els.termInput.addEventListener('keydown', (e) => {
7718
7748
  if (e.key === 'Enter') {