beast-agent 2.6.5 → 2.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.6.5",
4
+ "version": "2.6.6",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -265,67 +265,180 @@ function removeRule(textOrIndex) {
265
265
  }
266
266
 
267
267
  /* ---------- bellek hijyeni (#5) ----------
268
- MEMORY.md: dedup + eskime; periyodik çağrıyla şişme engellenir. */
268
+ MEMORY.md: yakın-kayıt dedup + gürültü temizliği + eskime. LLM'siz
269
+ deterministik — gece yansıması 429/limit'e takılsa bile memory sadeleşir. */
269
270
 
270
271
  const MEMORY_CAP = 400; // maksimum kayıt sayısı
271
272
  const MEMORY_MAX_AGE_DAYS = 120; // hiç skor üretmeyen kayıtların ömrü
272
273
 
273
- /** Duplike + çok eski kayıtları temizler; silinen sayısını döner */
274
- function hygiene({ maxAgeDays = MEMORY_MAX_AGE_DAYS, cap = MEMORY_CAP } = {}) {
274
+ /* kelime-kümesi benzerliği: fold + stopword temiz. "adı Batuhan, hitap kanka"
275
+ "kullanıcı Batuhan Bozoklu, hitap 'kanka'" gibi TEKRARLARI yakalar. */
276
+ const MEM_STOP = new Set(
277
+ ('ve veya ya ile için ama fakat da de ki mi mi mu mü bir bu şu o en daha çok az olarak gibi sonra önce yeni son ' +
278
+ 'the a an of in on to for and or is are was were be been at by with as it its this that from').split(' ')
279
+ );
280
+
281
+ function lineTokens(s) {
282
+ const words = fold(s)
283
+ .replace(/[^a-z0-9]+/g, ' ')
284
+ .split(' ')
285
+ .filter((w) => w.length > 1 && !MEM_STOP.has(w) && !/^kullanici/.test(w));
286
+ return new Set(words.slice(0, 30));
287
+ }
288
+
289
+ function linesSimilar(a, b) {
290
+ const A = lineTokens(a);
291
+ const B = lineTokens(b);
292
+ if (!A.size || !B.size) return false;
293
+ let inter = 0;
294
+ for (const t of A) if (B.has(t)) inter++;
295
+ if (!inter) return false;
296
+ const min = Math.min(A.size, B.size);
297
+ return inter / min >= 0.7 || inter / (A.size + B.size - inter) >= 0.6;
298
+ }
299
+
300
+ /* 'X'i sildi / X ile ilgilenmiyor' beyanı — aynı X için eski kurulum satırları düşürülür */
301
+ const RETIRE_DECL_RE = /(indirdikten sonra sildi|bilerek sildi|art[i]?k ilgilenmiyor|ilgilenmiyor)/i;
302
+ const INSTALL_VERB_RE = /(indiriyor|indirdi|indirmek|kuruyor|kurulu|kurulum|y[kü]kl)/i;
303
+
304
+ function retiredToolNames(list) {
305
+ const retired = new Set();
306
+ for (const l of list) {
307
+ if (!RETIRE_DECL_RE.test(l)) continue;
308
+ for (const w of String(l).match(/[A-Za-z][A-Za-z0-9-]{2,}/g) || []) {
309
+ const f = fold(w);
310
+ if (!MEM_STOP.has(f) && f.length >= 4) retired.add(f);
311
+ }
312
+ }
313
+ return retired;
314
+ }
315
+
316
+ /* repo/araç indirme-inceleme günlüğü: kalıcı profil değil, bir kez değdi */
317
+ function isRepoNoise(l) {
318
+ if (RETIRE_DECL_RE.test(l)) return false; /* emeklilik beyanı değerli — korunur */
319
+ if (/(repo|github)/i.test(l) && /(indir|incel|kopya|kar[sş]ıla[sş]tır|ilgilen)/i.test(l)) return true;
320
+ if (/ilgilen/i.test(l) && /(inceliyor|kar[sş]ıla[sş]tır)/i.test(l)) return true;
321
+ return false;
322
+ }
323
+
324
+ /** Duplike + yakın-kayıt + gürültü + çok eski kayıtları temizler.
325
+ deep: LLM'siz temizlik katmanı (yakın-kayıt birleştirme — SON kopya kazanır). */
326
+ function hygiene({ maxAgeDays = MEMORY_MAX_AGE_DAYS, cap = MEMORY_CAP, deep = true } = {}) {
275
327
  const list = entries();
276
328
  if (!list.length) return { ok: true, removed: 0, reason: 'boş' };
277
- const seen = new Set();
278
- const kept = [];
279
- let dupes = 0;
280
- for (const l of list) {
281
- const key = fold(l).replace(/[^a-z0-9]+/g, ' ').trim();
282
- if (seen.has(key)) {
283
- dupes++;
284
- continue;
329
+ let working = list.slice();
330
+ const dropped = [];
331
+
332
+ /* 0) birebir dedup — her modda (ucuz) */
333
+ {
334
+ const seen = new Set();
335
+ working = working.filter((l) => {
336
+ const key = fold(l).replace(/[^a-z0-9]+/g, ' ').trim();
337
+ if (seen.has(key)) {
338
+ dropped.push(l);
339
+ return false;
340
+ }
341
+ seen.add(key);
342
+ return true;
343
+ });
344
+ }
345
+
346
+ if (deep) {
347
+ /* 1) emekli araç beyanları: 'X'i sildi/ilgilenmiyor' → X'in eski kurulum satırları düşer */
348
+ const retired = retiredToolNames(working);
349
+ if (retired.size) {
350
+ working = working.filter((l) => {
351
+ if (RETIRE_DECL_RE.test(l)) return true; /* beyanın kendisi korunur */
352
+ if (!INSTALL_VERB_RE.test(l)) return true;
353
+ for (const t of lineTokens(l)) {
354
+ if (retired.has(t)) {
355
+ dropped.push(l);
356
+ return false;
357
+ }
358
+ }
359
+ return true;
360
+ });
361
+ }
362
+ /* 2) repo/araç gürültüsü */
363
+ working = working.filter((l) => {
364
+ if (isRepoNoise(l)) {
365
+ dropped.push(l);
366
+ return false;
367
+ }
368
+ return true;
369
+ });
370
+ /* 3) yakın-kayıt dedup — aynı şeyin yeniden yazımı TEK satırda; SON kopya kazanır (en taze) */
371
+ const kept2 = [];
372
+ for (const l of working) {
373
+ const idx = kept2.findIndex((k) => linesSimilar(k, l));
374
+ if (idx >= 0) {
375
+ dropped.push(kept2[idx]);
376
+ kept2[idx] = l;
377
+ } else {
378
+ kept2.push(l);
379
+ }
285
380
  }
286
- seen.add(key);
287
- kept.push(l);
381
+ working = kept2;
288
382
  }
383
+
289
384
  let aged = 0;
290
- if (kept.length > cap || maxAgeDays > 0) {
385
+ if (working.length > cap || maxAgeDays > 0) {
291
386
  /* tarih etiketli olmayanlar en eski kabul edilir (append-only başlangıç) */
292
387
  const cut = Date.now() - maxAgeDays * 86400000;
293
388
  const filtered = [];
294
389
  /* kronolojik varsayım: dosya sırası = ekleme sırası */
295
- const overflow = Math.max(0, kept.length - cap);
296
- kept.forEach((l, idx) => {
390
+ const overflow = Math.max(0, working.length - cap);
391
+ working.forEach((l, idx) => {
297
392
  const m = l.match(/\b(\d{4}-\d{2}-\d{2})\b/);
298
393
  const tooOld = m && new Date(m[1]).getTime() < cut;
299
- if (tooOld && idx < kept.length - 20) { // son 20 kayıt asla yaşla silinmez
394
+ if (tooOld && idx < working.length - 20) { // son 20 kayıt asla yaşla silinmez
300
395
  aged++;
301
396
  return;
302
397
  }
303
398
  if (overflow > 0 && idx < overflow) {
304
399
  // kap aşımı: en eskilerden düş ama yine de son 20 korunur
305
- if (idx < kept.length - 20) {
400
+ if (idx < working.length - 20) {
306
401
  aged++;
307
402
  return;
308
403
  }
309
404
  }
310
405
  filtered.push(l);
311
406
  });
312
- return writeKept(filtered, dupes + aged);
407
+ return writeKept(list, filtered, dropped.length + aged);
313
408
  }
314
- return writeKept(kept, dupes);
409
+ return writeKept(list, working, dropped.length + aged);
315
410
  }
316
411
 
317
- function writeKept(kept, removedCount) {
412
+ function writeKept(originalList, kept, removedCount) {
318
413
  try {
319
- fs.writeFileSync(
320
- path.join(memDir(), 'MEMORY.md'),
321
- kept.map((l) => '- ' + l).join('\n') + (kept.length ? '\n' : '')
322
- );
323
- return { ok: true, removed: removedCount, remaining: kept.length };
414
+ /* yedek: hijyen geri alınabilir olsun (nightref backups klasörü ortak) */
415
+ if (removedCount > 0 && originalList.length) {
416
+ try {
417
+ const dir = path.join(memDir(), 'backups');
418
+ fs.mkdirSync(dir, { recursive: true });
419
+ fs.writeFileSync(
420
+ path.join(dir, 'MEMORY-hygiene-' + new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19) + '.md'),
421
+ originalList.map((l) => '- ' + l).join('\n') + '\n',
422
+ 'utf8'
423
+ );
424
+ } catch {}
425
+ }
426
+ /* save() → mem0 store'u satırlardan yeniden kurar (ham yazımda store bayat kalırdı) */
427
+ const w = save('MEMORY.md', kept.map((l) => '- ' + l).join('\n') + (kept.length ? '\n' : ''));
428
+ if (w && !w.ok) return { ok: false, error: w.error || 'yazılamadı' };
429
+ return { ok: true, removed: removedCount, remaining: kept.length, ...(droppedPreview(originalList, kept)) };
324
430
  } catch (e) {
325
431
  return { ok: false, error: String((e && e.message) || e) };
326
432
  }
327
433
  }
328
434
 
435
+ /* silinenleri özet olarak döndür (admin/araç yanıtı + audit) — ilk 40 */
436
+ function droppedPreview(originalList, kept) {
437
+ const keptSet = new Set(kept);
438
+ const gone = originalList.filter((l) => !keptSet.has(l)).slice(0, 40);
439
+ return gone.length ? { dropped: gone } : {};
440
+ }
441
+
329
442
  /* ---------- #12 kişilik kalibrasyonu ----------
330
443
  Kullanıcının konuşma tarzından üslup imzası çıkarır, SOUL.md'ye ekler.
331
444
  İlk hafta mesajlar birikince "dijital ikiz" ton yakalanır. */
@@ -318,6 +318,20 @@ async function run(deps) {
318
318
  report.notes.push('bugün değerli transkript yok — öğrenme adımı atlandı');
319
319
  }
320
320
 
321
+ /* 2b) LLM'siz derin hijyen: yakın-kayıt dedup + gürültü temizliği.
322
+ LLM (429/limit) çökse bile memory her yansımada sadeleşir. */
323
+ if (deps.memory && typeof deps.memory.hygiene === 'function') {
324
+ try {
325
+ const h = deps.memory.hygiene({ deep: true });
326
+ if (h && typeof h.removed === 'number' && h.removed > 0) {
327
+ report.hygiene = { removed: h.removed, remaining: h.remaining || 0 };
328
+ report.notes.push('derin hijyen: ' + h.removed + ' kayıt temizlendi');
329
+ }
330
+ } catch (e) {
331
+ report.errors.push('hijyen: ' + String((e && e.message) || e).slice(0, 100));
332
+ }
333
+ }
334
+
321
335
  /* 3) memory sıkılaştırma: drop/merge kararları → yedek → uygula */
322
336
  const entriesNow = deps.memory.entries();
323
337
  if (!noLlm && entriesNow.length >= CONSOLIDATE_MIN) {
@@ -401,6 +415,7 @@ async function run(deps) {
401
415
  ? '\n## İlgi alanları (günlük çıkarım)\n' + report.interests.labels.map((l) => '- ' + l).join('\n') + '\n'
402
416
  : '') +
403
417
  '\n## Bellek bakımı\n' +
418
+ (report.hygiene ? '- Derin hijyen (LLM\u2019siz): ' + report.hygiene.removed + ' kayıt temizlendi\n' : '') +
404
419
  '- Kayıt: ' + m.before + ' → ' + m.after + ' (düşülen ' + m.dropped + ', ' + m.merged + ' kayıt birleşti)\n' +
405
420
  '- Boyut: ' + m.charsBefore + ' → ' + m.charsAfter + ' char (~' + m.tokensSaved + ' token tasarruf)\n' +
406
421
  (report.notes.length ? '- Not: ' + report.notes.join('; ') + '\n' : '') +