beast-agent 2.16.0 → 2.17.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "beast-agent",
3
3
  "productName": "Beast Agent",
4
- "version": "2.16.0",
4
+ "version": "2.17.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -17,6 +17,7 @@ const research = require('./research');
17
17
  const agentdefs = require('./agentdefs');
18
18
  const memory = require('./memory');
19
19
  const mem0 = require('./mem0');
20
+ const supermemory = require('./supermemory');
20
21
  const nightref = require('./nightref');
21
22
  const skills = require('./skills');
22
23
  const mcp = require('./mcp');
@@ -275,6 +276,14 @@ class Engine {
275
276
  /* mem0-native hafıza katmanı: semantik arama + LLM konsolidasyonu (bot başına izole store).
276
277
  Ayarlardan kapatılabilir (mem0:false) — kapalıysa klasik keyword hafızası çalışır. */
277
278
  this.mem0Enabled = opts.mem0 !== false;
279
+ /* Supermemory (lokal) bellek katmanı — DEFAULT AÇIK: sunucu ayaktaysa
280
+ (npx supermemory local → localhost:6767) hibrit arama + kullanıcı profili
281
+ öncelikli gelir; ayakta değilse klasik hafıza sorunsuz devralır.
282
+ Sağlık önbelleği: probe 60 sn'de bir tazelenir (spam probe yok). */
283
+ this.supermemory = opts.supermemory || null; // { enabled, baseUrl, apiKey, containerTag }
284
+ this._smUp = null; // null = henüz bilinmiyor
285
+ this._smCheckedAt = 0;
286
+ this._smWarned = false; // erişilemez iletisi bir kez basılır
278
287
  /* ANA KOD KİLİDİ: agent kendi kaynak koduna dokunamaz (yazma/silme yok, okuma serbest) */
279
288
  this.protectedDirs = (opts.protectedDirs || [])
280
289
  .map((d) => String(d).replace(/[\\/]+$/, '').toLowerCase().replace(/\//g, '\\'))
@@ -1588,6 +1597,74 @@ class Engine {
1588
1597
  }
1589
1598
  }
1590
1599
 
1600
+ /* ---------- Supermemory (lokal) bellek köprüsü ---------- */
1601
+
1602
+ _smConfig() {
1603
+ const sm = this.supermemory;
1604
+ if (!sm || sm.enabled === false || !sm.baseUrl) return null;
1605
+ return sm;
1606
+ }
1607
+
1608
+ /* Ayaktayken true — 60 sn cooldown'lu probe (aynı tura 10 kez probe vurmaz) */
1609
+ async _smReady() {
1610
+ const sm = this._smConfig();
1611
+ if (!sm) return false;
1612
+ const now = Date.now();
1613
+ if (this._smUp === true && now - this._smCheckedAt < supermemory.HEALTH_COOLDOWN_MS) return true;
1614
+ if (this._smUp === false && now - this._smCheckedAt < supermemory.HEALTH_COOLDOWN_MS) return false;
1615
+ this._smUp = await supermemory.probe(sm.baseUrl, sm.apiKey);
1616
+ this._smCheckedAt = now;
1617
+ if (!this._smUp && !this._smWarned) {
1618
+ this._smWarned = true;
1619
+ emitSafe(this, null, {
1620
+ type: 'status',
1621
+ status: '🧠 supermemory erişilemedi — klasik hafıza kullanılıyor (açmak için: npx supermemory local)',
1622
+ });
1623
+ }
1624
+ return this._smUp;
1625
+ }
1626
+
1627
+ /* Container tag: bot oturumları kendi izole havuzuna yazar (mem0 deseni) */
1628
+ _smContainer(sessionId) {
1629
+ const sm = this._smConfig();
1630
+ const bctx = this._sessionBotCtx(sessionId ? this.cache.get(String(sessionId)) : null);
1631
+ return (bctx && bctx.id ? 'bot:' + bctx.id : '') || (sm && sm.containerTag) || 'beast';
1632
+ }
1633
+
1634
+ /* YAZ: yerel hafıza HER ZAMEN yazılır (risksiz yedek); supermemory ASENKRON
1635
+ push edilir — turu bloklamaz, hata yutulur (mem0 deseninin aynısı) */
1636
+ _smPush(sessionId, text) {
1637
+ const sm = this._smConfig();
1638
+ if (!sm) return;
1639
+ supermemory
1640
+ .add(sm.baseUrl, sm.apiKey, { content: text, containerTag: this._smContainer(sessionId), metadata: { type: 'conversation' } })
1641
+ .catch(() => {});
1642
+ }
1643
+
1644
+ /* ARA: supermemory hibrit arama + profil → yerel sonuçlarla BİRLEŞTİR.
1645
+ Sunucu yoksa/hata yerse eski akış aynen çalışır (fallback). */
1646
+ async _smSearch(sessionId, q, limit) {
1647
+ const sm = this._smConfig();
1648
+ if (!sm || !(await this._smReady())) return null;
1649
+ const container = this._smContainer(sessionId);
1650
+ try {
1651
+ const [hits, prof] = await Promise.all([
1652
+ supermemory.search(sm.baseUrl, sm.apiKey, { q, containerTag: container, limit, mode: 'hybrid' }),
1653
+ supermemory.profile(sm.baseUrl, sm.apiKey, { containerTag: container }).catch(() => null),
1654
+ ]);
1655
+ const out = {
1656
+ engine: 'supermemory',
1657
+ results: hits.map((h) => ({ text: h.text, score: h.similarity, source: 'supermemory' })),
1658
+ };
1659
+ if (prof && (prof.static.length || prof.dynamic.length)) out.profile = prof;
1660
+ return out;
1661
+ } catch {
1662
+ this._smUp = false;
1663
+ this._smCheckedAt = Date.now();
1664
+ return null;
1665
+ }
1666
+ }
1667
+
1591
1668
  /* opencode request.ts prepare — system birleşimi (birebir sıra) */
1592
1669
  buildOcSystem(session, agentInfo) {
1593
1670
  const sid = String(session && session.id);
@@ -4682,6 +4759,9 @@ const skills = require('./skills');
4682
4759
  return JSON.stringify(await mcp.call(name, args, signal));
4683
4760
  }
4684
4761
  if (name === 'memory_write') {
4762
+ /* Supermemory (lokal) DEFAULT: her yazım asenkron push edilir —
4763
+ sunucu ayaktaysa graf motoru özet/çelişki/unutma işlerini yürütür */
4764
+ this._smPush(sessionId, args.text);
4685
4765
  /* bot oturumu → botun KENDİ hafıza store'una yaz (global Beast hafızasına değil).
4686
4766
  mem0 açıkken: hash+semantic dedup'lı store'a gider, MEMORY.md aynası güncellenir. */
4687
4767
  const bctx = this._sessionBotCtx(sessionId ? this.cache.get(String(sessionId)) : null);
@@ -4690,7 +4770,7 @@ const skills = require('./skills');
4690
4770
  const r = await mem0.add('bot:' + bctx.id, [args.text]).catch(() => null);
4691
4771
  if (r && r.ok) {
4692
4772
  mem0.syncMirror('bot:' + bctx.id);
4693
- return JSON.stringify({ ok: true, duplicate: r.skipped > 0, event: r.events[0] && r.events[0].event });
4773
+ return JSON.stringify({ ok: true, duplicate: r.skipped > 0, event: r.events[0] && r.events[0].event, supermemory: !!this._smConfig() });
4694
4774
  }
4695
4775
  }
4696
4776
  return JSON.stringify(this.botMemory.append(bctx.id, args.text));
@@ -4699,7 +4779,7 @@ const skills = require('./skills');
4699
4779
  const r = await mem0.add('main', [args.text]).catch(() => null);
4700
4780
  if (r && r.ok) {
4701
4781
  mem0.syncMirror('main');
4702
- return JSON.stringify({ ok: true, duplicate: r.skipped > 0, event: r.events[0] && r.events[0].event });
4782
+ return JSON.stringify({ ok: true, duplicate: r.skipped > 0, event: r.events[0] && r.events[0].event, supermemory: !!this._smConfig() });
4703
4783
  }
4704
4784
  }
4705
4785
  return JSON.stringify(memory.append(args.text));
@@ -4717,6 +4797,12 @@ const skills = require('./skills');
4717
4797
  const bctx = this._sessionBotCtx(sessionId ? this.cache.get(String(sessionId)) : null);
4718
4798
  const q = String(args.query || '');
4719
4799
  const limit = clamp(Number(args.limit) || 5, 1, 15);
4800
+ /* Supermemory DEFAULT öncelik: hibrit arama + kullanıcı profili.
4801
+ Erişilemediyse/hata yerse yerel yollar aynen çalışır (fallback). */
4802
+ const smRes = await this._smSearch(sessionId, q, limit);
4803
+ if (smRes && smRes.results.length) {
4804
+ return JSON.stringify({ ok: true, query: q, ...smRes });
4805
+ }
4720
4806
  if (bctx && this.botMemory) {
4721
4807
  const rows = this.mem0Enabled
4722
4808
  ? await mem0.search('bot:' + bctx.id, q, { limit }).catch(() => [])
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+
3
+ /* ---------- Supermemory (lokal) adaptörü ----------
4
+ Beast'in bellek katmanına supermemory (git.new/memory) köprüsü — mem0.js
5
+ deseniyle TAKILABİLİR katman: sunucu (npx supermemory local → localhost:6767)
6
+ ayaktayken daha akıllı bellek (graf motoru, otomatik unutma, çelişki
7
+ çözümü, kullanıcı profili); ayakta değilken engine mevcut hafızaya sorunsuz
8
+ düşer. Zero npm bağımlılık — Node 18+ global fetch.
9
+
10
+ Uçlar (supermemory docs birebir):
11
+ POST /v3/documents { content, containerTag, customId?, metadata? }
12
+ POST /v4/search { q, containerTag, searchMode, limit, include? }
13
+ POST /v4/profile { containerTag, q? }
14
+ Auth: Authorization: Bearer <apiKey> (local key ilk boot'ta basılır) */
15
+
16
+ const BASE_TIMEOUT_MS = 8000;
17
+ const PROBE_TIMEOUT_MS = 2500;
18
+ const HEALTH_COOLDOWN_MS = 60000; // kapalıya aynı dakikada 200 kez vurma
19
+
20
+ function normalizeBase(url) {
21
+ return String(url || '').trim().replace(/\/+$/, '');
22
+ }
23
+
24
+ async function call(baseUrl, apiKey, path, body, timeoutMs) {
25
+ const base = normalizeBase(baseUrl);
26
+ if (!base) throw new Error('supermemory baseUrl boş');
27
+ const ctrl = new AbortController();
28
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs || BASE_TIMEOUT_MS);
29
+ try {
30
+ const res = await fetch(base + path, {
31
+ method: 'POST',
32
+ headers: {
33
+ 'content-type': 'application/json',
34
+ ...(apiKey ? { authorization: 'Bearer ' + apiKey } : {}),
35
+ },
36
+ body: JSON.stringify(body || {}),
37
+ signal: ctrl.signal,
38
+ });
39
+ if (!res.ok) {
40
+ const txt = await res.text().catch(() => '');
41
+ throw new Error('HTTP ' + res.status + (txt ? ': ' + txt.slice(0, 180) : ''));
42
+ }
43
+ return await res.json();
44
+ } finally {
45
+ clearTimeout(timer);
46
+ }
47
+ }
48
+
49
+ /* Sunucu ayakta mı? GET / ile ucuz sağlık kontrolü */
50
+ async function probe(baseUrl, apiKey) {
51
+ const base = normalizeBase(baseUrl);
52
+ if (!base) return false;
53
+ const ctrl = new AbortController();
54
+ const timer = setTimeout(() => ctrl.abort(), PROBE_TIMEOUT_MS);
55
+ try {
56
+ const res = await fetch(base, {
57
+ method: 'GET',
58
+ headers: apiKey ? { authorization: 'Bearer ' + apiKey } : {},
59
+ signal: ctrl.signal,
60
+ });
61
+ return res.status < 500;
62
+ } catch {
63
+ return false;
64
+ } finally {
65
+ clearTimeout(timer);
66
+ }
67
+ }
68
+
69
+ /* Konuşma/belgi yut — arka planda çağrılır (fire-and-forget desenine uygun) */
70
+ async function add(baseUrl, apiKey, { content, containerTag, customId, metadata }) {
71
+ return call(baseUrl, apiKey, '/v3/documents', {
72
+ content: String(content || ''),
73
+ containerTag: String(containerTag || 'beast'),
74
+ ...(customId ? { customId: String(customId) } : {}),
75
+ ...(metadata && typeof metadata === 'object' ? { metadata } : {}),
76
+ });
77
+ }
78
+
79
+ /* Hibrit arama → normalize: [{text, similarity}] — memories varsayılan;
80
+ 'hybrid' RAG + bellek tek sorguda */
81
+ async function search(baseUrl, apiKey, { q, containerTag, limit, mode }) {
82
+ const r = await call(
83
+ baseUrl,
84
+ apiKey,
85
+ '/v4/search',
86
+ {
87
+ q: String(q || ''),
88
+ containerTag: String(containerTag || 'beast'),
89
+ searchMode: mode === 'documents' || mode === 'hybrid' ? mode : 'memories',
90
+ limit: Math.min(20, Math.max(1, Number(limit) || 5)),
91
+ },
92
+ 12000
93
+ );
94
+ const out = [];
95
+ for (const hit of (r && Array.isArray(r.results) ? r.results : []).slice(0, 10)) {
96
+ if (hit && typeof hit.memory === 'string' && hit.memory.trim()) {
97
+ out.push({ text: hit.memory.trim().slice(0, 600), similarity: Number(hit.similarity) || 0 });
98
+ }
99
+ /* hybrid mod: chunk'lar da gelir */
100
+ for (const c of (hit && Array.isArray(hit.chunks) ? hit.chunks : []).slice(0, 3)) {
101
+ const t = c && (c.content || c.text);
102
+ if (typeof t === 'string' && t.trim()) {
103
+ out.push({ text: t.trim().slice(0, 600), similarity: Number(c && c.similarity) || 0 });
104
+ }
105
+ }
106
+ }
107
+ return out.slice(0, Math.max(1, Number(limit) || 5));
108
+ }
109
+
110
+ /* Kullanıcı profili: { static: [], dynamic: [] } — her turda ucuz bağlam */
111
+ async function profile(baseUrl, apiKey, { containerTag, q }) {
112
+ const r = await call(
113
+ baseUrl,
114
+ apiKey,
115
+ '/v4/profile',
116
+ {
117
+ containerTag: String(containerTag || 'beast'),
118
+ ...(q ? { q: String(q) } : {}),
119
+ },
120
+ 12000
121
+ );
122
+ const p = (r && r.profile) || {};
123
+ const arr = (x) =>
124
+ Array.isArray(x)
125
+ ? x.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 12)
126
+ : typeof x === 'string' && x.trim()
127
+ ? [x.trim().slice(0, 300)]
128
+ : [];
129
+ return { static: arr(p.static), dynamic: arr(p.dynamic) };
130
+ }
131
+
132
+ module.exports = { probe, add, search, profile, HEALTH_COOLDOWN_MS };
package/src/main.js CHANGED
@@ -426,6 +426,11 @@ function parseSettingsText(text) {
426
426
  const parsed = JSON.parse(text);
427
427
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('bozuk yapı');
428
428
  if (!parsed.roleModels) parsed.roleModels = {};
429
+ /* Supermemory (lokal) DEFAULT AÇIK — sunucu ayaktayken daha akıllı bellek;
430
+ ayakta değilken klasik hafıza sorunsuz çalışır (engine fallback) */
431
+ if (!parsed.supermemory || typeof parsed.supermemory !== 'object') {
432
+ parsed.supermemory = { enabled: true, baseUrl: 'http://localhost:6767', apiKey: '', containerTag: 'beast' };
433
+ }
429
434
  return parsed;
430
435
  }
431
436
 
@@ -3075,6 +3080,10 @@ function reloadBackend() {
3075
3080
  nightReflectAt: settings.nightReflectAt || null,
3076
3081
  approvals: settings.security && settings.security.approvals ? approvalsBridge : null,
3077
3082
  alwaysAllowTools: (settings.security && settings.security.alwaysAllow) || [],
3083
+ /* Supermemory (lokal) bellek katmanı — default açıktır (parseSettingsText);
3084
+ sunucu ayakta değilse engine klasik hafızaya düşer. Kapamak:
3085
+ settings.json → supermemory.enabled = false */
3086
+ supermemory: settings.supermemory || { enabled: true, baseUrl: 'http://localhost:6767', apiKey: '', containerTag: 'beast' },
3078
3087
  crashFile: FALLOUT_CRASH_FILE,
3079
3088
  notifyOwnerFail: settings.notifyOwnerFail !== false,
3080
3089
  fileSend: deliverFile,
@@ -6030,6 +6039,39 @@ ipcMain.handle('sec:set', (_e, cfg) => {
6030
6039
  log.info('sec', `güvenlik: onay kapısı ${settings.security.approvals ? 'AÇIK' : 'KAPALI (her şey serbest)'}`);
6031
6040
  return { approvals: settings.security.approvals, alwaysAllow: settings.security.alwaysAllow };
6032
6041
  });
6042
+
6043
+ /* Supermemory (lokal) ayarları: engine'e canlı yansıt + sağlık önbelleğini tazele */
6044
+ ipcMain.handle('supermemory:set', (_e, cfg) => {
6045
+ try {
6046
+ const base = String((cfg && cfg.baseUrl) || '').trim() || 'http://localhost:6767';
6047
+ settings.supermemory = {
6048
+ enabled: !!(cfg && cfg.enabled),
6049
+ baseUrl: base.replace(/\/+$/, ''),
6050
+ apiKey: String((cfg && cfg.apiKey) || '').trim(),
6051
+ containerTag: String((cfg && cfg.containerTag) || 'beast').trim() || 'beast',
6052
+ };
6053
+ saveSettings();
6054
+ if (engine) {
6055
+ engine.supermemory = settings.supermemory;
6056
+ engine._smUp = null;
6057
+ engine._smCheckedAt = 0;
6058
+ engine._smWarned = false;
6059
+ }
6060
+ return { ok: true, supermemory: settings.supermemory };
6061
+ } catch (e) {
6062
+ return { ok: false, error: String((e && e.message) || e) };
6063
+ }
6064
+ });
6065
+ ipcMain.handle('supermemory:get', () => {
6066
+ const sm = settings.supermemory || {};
6067
+ return {
6068
+ enabled: sm.enabled !== false,
6069
+ baseUrl: sm.baseUrl || 'http://localhost:6767',
6070
+ apiKey: sm.apiKey || '',
6071
+ containerTag: sm.containerTag || 'beast',
6072
+ up: engine ? !!engine._smUp : null,
6073
+ };
6074
+ });
6033
6075
  ipcMain.handle('approval:respond', (_e, { id, ok, always }) => resolveApproval(id, ok, always));
6034
6076
  /* opencode permission reply (BC): UI kartı üçlü cevap verir —
6035
6077
  'once' | 'always' | 'reject' (+ opsiyonel geri bildirim) */
package/src/preload.js CHANGED
@@ -89,6 +89,9 @@ contextBridge.exposeInMainWorld('beast', {
89
89
  bcDeleteAll: () => ipcRenderer.invoke('bc:deleteAll'),
90
90
  /* opencode permission (BC): UI kartı cevabı — 'once' | 'always' | 'reject' */
91
91
  permissionReply: (requestId, action, message) => ipcRenderer.invoke('permission:reply', { requestId, action, message }),
92
+ /* Supermemory (lokal) bellek katmanı ayarları */
93
+ supermemoryGet: () => ipcRenderer.invoke('supermemory:get'),
94
+ supermemorySet: (cfg) => ipcRenderer.invoke('supermemory:set', cfg),
92
95
  studioSend: (msg, attachments) => ipcRenderer.invoke('studio:send', { msg, attachments }),
93
96
  studioStop: () => ipcRenderer.invoke('studio:stop'),
94
97
  studioNew: () => ipcRenderer.invoke('studio:new'),