beast-agent 2.8.0 → 2.8.2

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.8.0",
4
+ "version": "2.8.2",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
package/src/agent/llm.js CHANGED
@@ -10,6 +10,7 @@
10
10
  prompt_cache_key) 400'den otomatik öğrenir → model-caps.json'a kalıcı. */
11
11
 
12
12
  const fs = require('fs');
13
+ const crypto = require('crypto');
13
14
 
14
15
  const RETRYABLE_STATUS = new Set([408, 409, 425, 429, 500, 502, 503, 504]);
15
16
  const MAX_RETRIES = 3; /* sağlayıcı geçici HTTP hataları */
@@ -328,6 +329,24 @@ async function withRetries(fn, { signal, onRetry } = {}) {
328
329
  }
329
330
  }
330
331
 
332
+ /* x-opencode-session: konuşma-başına sabit oturum kimliği — TÜM sağlayıcılara
333
+ standart gönderilir (opencode bununla isteği optimize eder; 09/06'dan
334
+ itibaren headersız istek hata dönebilir). Gerçek sohbetlerde cacheKey =
335
+ session.id; cacheKey'siz yardımcı isteklerde süreç-başına sabit yedek. */
336
+ let _sessionFallback = null;
337
+ function sessionHeaderId(body) {
338
+ const ck = String((body && body.cacheKey) || '').trim();
339
+ if (ck) return ck;
340
+ if (!_sessionFallback) {
341
+ try {
342
+ _sessionFallback = crypto.randomUUID();
343
+ } catch {
344
+ _sessionFallback = 'beast-' + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
345
+ }
346
+ }
347
+ return _sessionFallback;
348
+ }
349
+
331
350
  async function openChat(sel, body, { stream, signal, drops } = {}) {
332
351
  const d = drops || {};
333
352
  const payload = {
@@ -357,6 +376,7 @@ async function openChat(sel, body, { stream, signal, drops } = {}) {
357
376
  Authorization: `Bearer ${sel.key}`,
358
377
  'HTTP-Referer': 'https://localhost/beast',
359
378
  'X-Title': 'Beast Agent',
379
+ 'x-opencode-session': sessionHeaderId(body),
360
380
  },
361
381
  body: JSON.stringify(payload),
362
382
  });
package/src/main.js CHANGED
@@ -6,6 +6,10 @@ const fs = require('fs');
6
6
  const http = require('http');
7
7
  const dns = require('dns');
8
8
  const crypto = require('crypto');
9
+ /* x-opencode-session: opencode (Zen/Go) isteklerinde beklenen oturum kimliği —
10
+ kurulum/probe/model-liste istekleri için süreç-başına sabit değer; sohbet
11
+ isteklerindeki konuşma-başına kimlik agent/llm.js'te üretilir */
12
+ const OPENCODE_SESSION = 'beast-' + crypto.randomUUID();
9
13
  const { spawn } = require('child_process');
10
14
  const { Engine, OBSERVE_MARK } = require('./agent/engine');
11
15
  const { loadBeastConfig, parseEnvFile, beastDir } = require('./agent/config');
@@ -8387,7 +8391,7 @@ async function refreshModelsAll() {
8387
8391
  try {
8388
8392
  const b = String(p.baseUrl).trim().replace(/\/+$/, '');
8389
8393
  const url = /\/v\d+$/.test(b) ? b + '/models' : b + '/v1/models';
8390
- const h = { Authorization: 'Bearer ' + p.key };
8394
+ const h = { Authorization: 'Bearer ' + p.key, 'x-opencode-session': OPENCODE_SESSION };
8391
8395
  try {
8392
8396
  if (new URL(url).hostname === 'api.anthropic.com') {
8393
8397
  h['x-api-key'] = p.key;
@@ -8484,7 +8488,10 @@ function findZenToken() {
8484
8488
 
8485
8489
  async function fetchZenFreeModels() {
8486
8490
  try {
8487
- const res = await fetch(ZEN_BASE + '/models', { signal: AbortSignal.timeout(15000) });
8491
+ const res = await fetch(ZEN_BASE + '/models', {
8492
+ headers: { 'x-opencode-session': OPENCODE_SESSION },
8493
+ signal: AbortSignal.timeout(15000),
8494
+ });
8488
8495
  if (res.ok) {
8489
8496
  const j = await res.json();
8490
8497
  const ids = (j.data || []).map((m) => (typeof m === 'string' ? m : m.id)).filter(Boolean);
@@ -8584,7 +8591,11 @@ async function testZenModel(key, model) {
8584
8591
  try {
8585
8592
  const res = await fetch(ZEN_BASE + '/chat/completions', {
8586
8593
  method: 'POST',
8587
- headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + key },
8594
+ headers: {
8595
+ 'Content-Type': 'application/json',
8596
+ Authorization: 'Bearer ' + key,
8597
+ 'x-opencode-session': OPENCODE_SESSION,
8598
+ },
8588
8599
  body: JSON.stringify({
8589
8600
  model,
8590
8601
  messages: [{ role: 'user', content: 'ping' }],
@@ -8623,7 +8634,11 @@ async function testZenReasoning(key, model) {
8623
8634
  try {
8624
8635
  const res = await fetch(ZEN_BASE + '/chat/completions', {
8625
8636
  method: 'POST',
8626
- headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + key },
8637
+ headers: {
8638
+ 'Content-Type': 'application/json',
8639
+ Authorization: 'Bearer ' + key,
8640
+ 'x-opencode-session': OPENCODE_SESSION,
8641
+ },
8627
8642
  body: JSON.stringify({
8628
8643
  model,
8629
8644
  messages: [{ role: 'user', content: 'ping' }],
@@ -8731,7 +8746,7 @@ ipcMain.handle('custom:fetchModels', async (_e, { baseUrl, key }) => {
8731
8746
  let b = String(baseUrl || '').trim().replace(/\/+$/, '');
8732
8747
  if (!/^https?:\/\//i.test(b)) return { ok: false, error: 'URL http(s) ile başlamalı' };
8733
8748
  const url = /\/v\d+$/.test(b) ? b + '/models' : b + '/v1/models';
8734
- const headers = key ? { Authorization: 'Bearer ' + key } : {};
8749
+ const headers = { 'x-opencode-session': OPENCODE_SESSION, ...(key ? { Authorization: 'Bearer ' + key } : {}) };
8735
8750
  /* Anthropic: /v1/models yerel API — x-api-key + versiyon başlığı ister */
8736
8751
  try {
8737
8752
  if (new URL(url).hostname === 'api.anthropic.com') {
@@ -317,9 +317,13 @@ function onNetEvent(ev) {
317
317
  }
318
318
 
319
319
  const TODO_GLYPH = { done: '✓', active: '▸', pending: '○' };
320
+ const TODO_DONE_HIDE_MS = 2500; /* tüm görevler bitince liste bu kadar sonra otomatik kapanır */
321
+ let todoHideTimer = null;
320
322
 
321
323
  function renderTodos(items) {
322
324
  const list = Array.isArray(items) ? items : [];
325
+ clearTimeout(todoHideTimer);
326
+ todoHideTimer = null;
323
327
  if (!list.length) { els.todoPanel.hidden = true; els.todoPanel.innerHTML = ''; return; }
324
328
  const done = list.filter((t) => t.status === 'done').length;
325
329
  const rows = list
@@ -333,10 +337,23 @@ function renderTodos(items) {
333
337
  `<span class="td-right"><span class="td-count">${done}/${list.length}</span>` +
334
338
  `<button class="td-x" title="${_t('td_hide')}">×</button></span></div>` + rows;
335
339
  els.todoPanel.hidden = false;
340
+ /* tüm görevler bitti → kullanıcı tamamlanan listeyi görsün diye kısa beklet,
341
+ sonra panel otomatik kapanır; yeni görev gelirse zamanlayıcı iptal olur */
342
+ if (done === list.length) {
343
+ todoHideTimer = setTimeout(() => {
344
+ todoHideTimer = null;
345
+ els.todoPanel.hidden = true;
346
+ els.todoPanel.innerHTML = '';
347
+ }, TODO_DONE_HIDE_MS);
348
+ }
336
349
  /* X yalnız BU listeyi gizler — kalıcı kapanma yok: bir sonraki görev
337
350
  güncellemesi (yeni görev eklendi / görev yapıldı) paneli yeniden gösterir */
338
351
  const x = els.todoPanel.querySelector('.td-x');
339
- if (x) x.addEventListener('click', () => { els.todoPanel.hidden = true; });
352
+ if (x) x.addEventListener('click', () => {
353
+ clearTimeout(todoHideTimer);
354
+ todoHideTimer = null;
355
+ els.todoPanel.hidden = true;
356
+ });
340
357
  }
341
358
  function setBusy(b) {
342
359
  busy = b;
@@ -8504,9 +8521,12 @@ function bcToolBoxEnd(callId, ok, result, diff) {
8504
8521
  /* ---------- Beast Code todo kartı ----------
8505
8522
  Inputun (chat) hemen üstünde ortalı kompakt kutu; #bcTodoWrap içinde yaşar,
8506
8523
  scroll'lu çıktı akışını kirletmez. */
8524
+ let bcTodoHideTimer = null;
8507
8525
  function bcRenderTodos(todos) {
8508
8526
  const list = Array.isArray(todos) ? todos : [];
8509
8527
  if (!els.bcTodoWrap) return;
8528
+ clearTimeout(bcTodoHideTimer);
8529
+ bcTodoHideTimer = null;
8510
8530
  bcFlushStream();
8511
8531
  if (!list.length) {
8512
8532
  bcHideTodos();
@@ -8531,9 +8551,18 @@ function bcRenderTodos(todos) {
8531
8551
  row.querySelector('.bc-todotext').textContent = String(t.title || '');
8532
8552
  wrap.appendChild(row);
8533
8553
  }
8554
+ /* tüm görevler bitti → kısa görün, sonra kart otomatik kapanır */
8555
+ if (done === list.length) {
8556
+ bcTodoHideTimer = setTimeout(() => {
8557
+ bcTodoHideTimer = null;
8558
+ bcHideTodos();
8559
+ }, TODO_DONE_HIDE_MS);
8560
+ }
8534
8561
  }
8535
8562
 
8536
8563
  function bcHideTodos() {
8564
+ clearTimeout(bcTodoHideTimer);
8565
+ bcTodoHideTimer = null;
8537
8566
  if (els.bcTodoWrap) {
8538
8567
  els.bcTodoWrap.hidden = true;
8539
8568
  els.bcTodoWrap.innerHTML = '';
@@ -8961,9 +8990,12 @@ function stNoteStream(delta) {
8961
8990
  return true;
8962
8991
  }
8963
8992
 
8993
+ let stTodoHideTimer = null;
8964
8994
  function stRenderTodos(todos) {
8965
8995
  const list = Array.isArray(todos) ? todos : [];
8966
8996
  if (!els.stTodoWrap) return;
8997
+ clearTimeout(stTodoHideTimer);
8998
+ stTodoHideTimer = null;
8967
8999
  stFlushStream();
8968
9000
  if (!list.length) {
8969
9001
  stHideTodos();
@@ -8988,9 +9020,18 @@ function stRenderTodos(todos) {
8988
9020
  row.querySelector('.bc-todotext').textContent = String(t.title || '');
8989
9021
  wrap.appendChild(row);
8990
9022
  }
9023
+ /* tüm görevler bitti → kısa görün, sonra kart otomatik kapanır */
9024
+ if (done === list.length) {
9025
+ stTodoHideTimer = setTimeout(() => {
9026
+ stTodoHideTimer = null;
9027
+ stHideTodos();
9028
+ }, TODO_DONE_HIDE_MS);
9029
+ }
8991
9030
  }
8992
9031
 
8993
9032
  function stHideTodos() {
9033
+ clearTimeout(stTodoHideTimer);
9034
+ stTodoHideTimer = null;
8994
9035
  if (els.stTodoWrap) {
8995
9036
  els.stTodoWrap.hidden = true;
8996
9037
  els.stTodoWrap.innerHTML = '';