beast-agent 0.19.0 → 0.21.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": "0.19.0",
4
+ "version": "0.21.0",
5
5
  "description": "Ultra-fast local agent shell for Windows.",
6
6
  "author": "algokodcom (AlgoKod)",
7
7
  "license": "MIT",
@@ -1,18 +1,17 @@
1
1
  'use strict';
2
2
 
3
3
  /* Beast Agent — Dağıtım / Senkron Sistemi
4
- Tek komutla tam sürüm akışı:
5
- npm run release → otomatik patch bump (0.15.0 → 0.15.1)
6
- npm run release -- minor → 0.16.0
7
- npm release -- 0.17.0 → belirli sürüm
4
+ Tek komutla tam sürüm akışı (EXE/INSTALLER YOK — npm + kaynak dağıtımı):
5
+ npm run release → otomatik patch bump (0.20.0 → 0.20.1)
6
+ npm run release -- minor → 0.21.0
7
+ npm run release -- 0.22.0 → belirli sürüm
8
8
 
9
9
  Adımlar:
10
10
  1) package.json sürümü yükselt
11
11
  2) commit + tag + push (main + tag)
12
- 3) npm run dist (NSIS setup + portable)
13
- 4) GitHub Release + exe upload (gh CLI)
14
- 5) npm publish (NPM_TOKEN env var ise; yoksa atlar ve uyarır)
15
- 6) OneDrive yedek klasörüne kaynak kopyası (beast-v< sürüm >)
12
+ 3) GitHub Release (yalnız kaynak exe/asset yüklenmez)
13
+ 4) npm publish (NPM_TOKEN env var ise; yoksa atlar ve uyarır)
14
+ 5) OneDrive yedek klasörüne kaynak kopyası (beast-v< sürüm >)
16
15
  */
17
16
 
18
17
  const fs = require('fs');
@@ -29,7 +28,8 @@ const warn = (msg) => console.log(' \x1b[33m!\x1b[0m ' + msg);
29
28
  const fail = (msg) => { console.error(' \x1b[31m✗ ' + msg + '\x1b[0m'); process.exit(1); };
30
29
 
31
30
  function run(cmd, opts = {}) {
32
- return execSync(cmd, { cwd: ROOT, stdio: opts.inherit ? 'inherit' : 'pipe', encoding: 'utf8', ...opts }).trim();
31
+ /* stdio:'inherit' iken execSync null döner trim patlamasın */
32
+ return String(execSync(cmd, { cwd: ROOT, stdio: opts.inherit ? 'inherit' : 'pipe', encoding: 'utf8', ...opts }) ?? '').trim();
33
33
  }
34
34
 
35
35
  function bump(version, kind) {
@@ -51,13 +51,13 @@ const tag = 'v' + next;
51
51
  console.log(`\x1b[1mBeast Agent release: ${current} → ${next}\x1b[0m`);
52
52
 
53
53
  /* ---------- 1) sürüm bump ---------- */
54
- step('1/6 sürüm yükselt: ' + current + ' → ' + next);
54
+ step('1/5 sürüm yükselt: ' + current + ' → ' + next);
55
55
  pkg.version = next;
56
56
  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 4) + '\n');
57
57
  ok('package.json → ' + next);
58
58
 
59
59
  /* ---------- 2) commit + push ---------- */
60
- step('2/6 git: commit + tag + push');
60
+ step('2/5 git: commit + tag + push');
61
61
  try {
62
62
  run('git add -A');
63
63
  run(`git commit -m "v${next}"`);
@@ -67,43 +67,30 @@ run('git push origin main');
67
67
  run(`git push origin ${tag}`);
68
68
  ok('pushed: main + ' + tag);
69
69
 
70
- /* ---------- 3) build ---------- */
71
- step('3/6 build: npm run dist (birkaç dakika)');
72
- run('npm run dist', { inherit: true });
73
- const setupExe = path.join(ROOT, 'dist', `BeastAgent-Setup-${next}.exe`);
74
- const portableExe = path.join(ROOT, 'dist', 'BeastAgent.exe');
75
- if (!fs.existsSync(setupExe)) fail('setup exe bulunamadı: ' + setupExe);
76
- if (!fs.existsSync(portableExe)) fail('portable exe bulunamadı: ' + portableExe);
77
- ok('dist hazır: Setup + Portable');
78
-
79
- /* ---------- 4) GitHub release ---------- */
80
- step('4/6 GitHub Release ' + tag);
70
+ /* ---------- 3) GitHub Release (kaynak — exe yok) ---------- */
71
+ step('3/5 GitHub Release ' + tag);
81
72
  const gh = process.env.GH || 'gh';
82
73
  try {
83
74
  const notes = [
84
75
  `## Beast Agent ${tag}`,
85
76
  '',
86
- '- `BeastAgent-Setup-${next}.exe` kurulumlu (önerilen)',
87
- '- \`BeastAgent.exe\` — portable',
88
- '- \`npm i -g beast-agent\` — npm üzerinden',
77
+ '- \`npm i -g beast-agent@\`' + next,
89
78
  '',
79
+ 'Dağıtım npm üzerinden yapılır — exe/installer sürümü kaldırıldı.',
90
80
  'Tam değişiklik listesi: commit geçmişi.',
91
81
  ].join('\n');
92
82
  const notesFile = path.join(ROOT, 'dist', 'release-notes.md');
83
+ fs.mkdirSync(path.dirname(notesFile), { recursive: true });
93
84
  fs.writeFileSync(notesFile, notes);
94
- const assets = [setupExe, setupExe + '.blockmap', portableExe, path.join(ROOT, 'dist', 'latest.yml')]
95
- .filter((f) => fs.existsSync(f))
96
- .map((f) => `"${f}"`)
97
- .join(' ');
98
85
  try { run(`${gh} release delete ${tag} --yes --cleanup-tag`); } catch {}
99
- run(`${gh} release create ${tag} ${assets} --title "Beast Agent ${tag}" --notes-file "${notesFile}"`, { inherit: true });
86
+ run(`${gh} release create ${tag} --title "Beast Agent ${tag}" --notes-file "${notesFile}"`, { inherit: true });
100
87
  ok('release yayında: https://github.com/algokodcom/beast-agent/releases/tag/' + tag);
101
88
  } catch (e) {
102
89
  fail('GitHub release: ' + String(e));
103
90
  }
104
91
 
105
- /* ---------- 5) npm publish ---------- */
106
- step('5/6 npm publish');
92
+ /* ---------- 4) npm publish ---------- */
93
+ step('4/5 npm publish');
107
94
  if (process.env.NPM_TOKEN) {
108
95
  try {
109
96
  run(`npm publish --//registry.npmjs.org/:_authToken=${process.env.NPM_TOKEN}`, { inherit: true });
@@ -116,8 +103,8 @@ if (process.env.NPM_TOKEN) {
116
103
  warn('Elle: set NPM_TOKEN=<token> && npm publish');
117
104
  }
118
105
 
119
- /* ---------- 6) OneDrive kaynak yedeği ---------- */
120
- step('6/6 OneDrive yedek (beast-v' + next + ')');
106
+ /* ---------- 5) OneDrive kaynak yedeği ---------- */
107
+ step('5/5 OneDrive yedek (beast-v' + next + ')');
121
108
  try {
122
109
  const dest = path.join(ONE_DRIVE_DIR, 'beast-v' + next);
123
110
  fs.mkdirSync(dest, { recursive: true });
@@ -0,0 +1,495 @@
1
+ 'use strict';
2
+
3
+ /* Beast Bot Sistemi (BÖLÜM 2 / 3 / 6 / 7)
4
+ - İlk bot hep "Beast" (admin, silinemez). Toplam MAX 5 bot (1 admin + 4 müşteri).
5
+ - Her bot izole klasörde yaşar: bots/<id>/
6
+ config.json → botun temel ayarı (ayna)
7
+ memory.md → botun KENDİ hafızası (diğer botlar göremez)
8
+ yetkiler.json → görebildiği botlar + skill/plugin/tarayıcı yetkileri
9
+ logs/changes.log → yetki/ayar değişiklik günlüğü (her değişiklik loglanır)
10
+ chat/ plugins/ watchers/ → botun izole çalışma alanları
11
+ - WhatsApp numaraları bot_id ile eşlenir; registry bots.json'da durur.
12
+ whitelist.json aynası main tarafında senkronlanır. */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const os = require('os');
17
+ const memory = require('./memory'); // tokenize/scoreEntry yeniden kullanılır (döngü yok)
18
+
19
+ const MAX_BOTS = 5;
20
+
21
+ const ICONS = ['🦁', '🚀', '💎', '⭐', '🔥', '🧮', '📊', '🤖', '🦊', '🐼', '🎯', '🛠️'];
22
+
23
+ /* Her botta varsayılan AÇIK gelen skill'ler (yetki sadece admin tarafından değişir) */
24
+ const DEFAULT_SKILLS = {
25
+ email: true,
26
+ browser: true, // dahili tarayıcı varsayılan TÜM botlarda aktif
27
+ web_search: true,
28
+ run_command: false,
29
+ memory: true,
30
+ kb: true,
31
+ };
32
+
33
+ const SKILL_LIST = [
34
+ ['email', 'E-posta (okuma/gönderme)'],
35
+ ['browser', 'Dahili tarayıcı (panel)'],
36
+ ['web_search', 'Web arama + sayfa okuma'],
37
+ ['run_command', 'Terminal / dosya / ekran'],
38
+ ['memory', 'Kendi hafızası (yazma/arama)'],
39
+ ['kb', 'Bilgi bankası (arama/ekleme)'],
40
+ ];
41
+
42
+ const PLUGIN_LIST = ['fatura_okuyucu', 'crm_plugin', 'rapor_uretici', 'stok_takip', 'toplanti_ozet'];
43
+
44
+ function beastRoot() {
45
+ if (process.env.BEAST_DATA) return process.env.BEAST_DATA;
46
+ return process.env.APPDATA
47
+ ? path.join(process.env.APPDATA, 'beast')
48
+ : path.join(os.homedir(), 'AppData', 'Roaming', 'beast');
49
+ }
50
+
51
+ function registryFile() {
52
+ return path.join(beastRoot(), 'bots.json');
53
+ }
54
+
55
+ function botDir(id) {
56
+ return path.join(beastRoot(), 'bots', String(id || '').replace(/[^a-z0-9_-]/gi, ''));
57
+ }
58
+
59
+ function nowIso() {
60
+ return new Date().toISOString();
61
+ }
62
+
63
+ function uid() {
64
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 7);
65
+ }
66
+
67
+ function foldTr(s) {
68
+ return String(s || '')
69
+ .toLowerCase()
70
+ .replace(/[çğıöşüâîû]/g, (ch) => ({ ç: 'c', ğ: 'g', ı: 'i', ö: 'o', ş: 's', ü: 'u', â: 'a', î: 'i', û: 'u' }[ch] || ch));
71
+ }
72
+
73
+ function slugify(name) {
74
+ const base = foldTr(String(name || 'bot'))
75
+ .replace(/[^a-z0-9]+/g, '-')
76
+ .replace(/^-+|-+$/g, '')
77
+ .slice(0, 18);
78
+ return base || 'bot';
79
+ }
80
+
81
+ function adminBot() {
82
+ return {
83
+ id: 'beast',
84
+ name: 'Beast',
85
+ icon: '🦁',
86
+ admin: true,
87
+ prompt: '',
88
+ perm: 'all',
89
+ skills: { ...DEFAULT_SKILLS, run_command: true },
90
+ seeBots: [], // admin her şeyi görür
91
+ extBrowser: true, // dış tarayıcı yetkisi
92
+ browserDefault: 'dahili',
93
+ extCommand: '',
94
+ plugins: [],
95
+ createdAt: nowIso(),
96
+ };
97
+ }
98
+
99
+ /* ---------- registry ---------- */
100
+
101
+ let REG = null;
102
+
103
+ function loadRegistry() {
104
+ if (REG) return REG;
105
+ try {
106
+ REG = JSON.parse(fs.readFileSync(registryFile(), 'utf8'));
107
+ } catch {
108
+ REG = null;
109
+ }
110
+ if (!REG || !Array.isArray(REG.bots)) REG = { bots: [adminBot()] };
111
+ /* beast her zaman ilk ve admin */
112
+ const beast = REG.bots.find((b) => b && b.id === 'beast');
113
+ if (!beast) REG.bots.unshift(adminBot());
114
+ else Object.assign(beast, { ...adminBot(), ...beast, id: 'beast', admin: true, name: beast.name || 'Beast' });
115
+ saveRegistry();
116
+ ensureDirs();
117
+ return REG;
118
+ }
119
+
120
+ function saveRegistry() {
121
+ try {
122
+ fs.mkdirSync(beastRoot(), { recursive: true });
123
+ fs.writeFileSync(registryFile(), JSON.stringify(REG, null, 2));
124
+ } catch {}
125
+ }
126
+
127
+ function ensureDirs() {
128
+ for (const b of REG.bots) {
129
+ const d = botDir(b.id);
130
+ try {
131
+ for (const sub of ['logs', 'chat', 'plugins', 'watchers']) fs.mkdirSync(path.join(d, sub), { recursive: true });
132
+ const cfgP = path.join(d, 'config.json');
133
+ if (!fs.existsSync(cfgP)) fs.writeFileSync(cfgP, JSON.stringify(botConfigOf(b), null, 2));
134
+ const yetP = path.join(d, 'yetkiler.json');
135
+ if (!fs.existsSync(yetP)) fs.writeFileSync(yetP, JSON.stringify(botPermsOf(b), null, 2));
136
+ /* bot hafızası ayarlardaki yapıyla aynı: SOUL.md + MEMORY.md + USER.md
137
+ (eski memory.md varsa içeriği MEMORY.md'ye taşınır) */
138
+ const legacyP = path.join(d, 'memory.md');
139
+ const legacy = fs.existsSync(legacyP) ? fs.readFileSync(legacyP, 'utf8') : '';
140
+ const memP = path.join(d, 'MEMORY.md');
141
+ if (!fs.existsSync(memP)) fs.writeFileSync(memP, legacy || '');
142
+ for (const f of ['SOUL.md', 'USER.md']) {
143
+ const p = path.join(d, f);
144
+ if (!fs.existsSync(p)) fs.writeFileSync(p, '');
145
+ }
146
+ } catch {}
147
+ }
148
+ }
149
+
150
+ function botConfigOf(b) {
151
+ return {
152
+ id: b.id,
153
+ name: b.name,
154
+ icon: b.icon,
155
+ admin: !!b.admin,
156
+ prompt: b.prompt || '',
157
+ browser: { default: b.browserDefault || 'dahili', extCommand: b.extCommand || '' },
158
+ plugins: b.plugins || [],
159
+ createdAt: b.createdAt,
160
+ };
161
+ }
162
+
163
+ function botPermsOf(b) {
164
+ return {
165
+ id: b.id,
166
+ perm: b.perm || 'all',
167
+ skills: { ...DEFAULT_SKILLS, ...(b.skills || {}) },
168
+ seeBots: b.seeBots || [],
169
+ extBrowser: b.extBrowser !== false,
170
+ };
171
+ }
172
+
173
+ /* ---------- log ---------- */
174
+
175
+ function logChange(id, text) {
176
+ try {
177
+ const d = botDir(id);
178
+ fs.mkdirSync(path.join(d, 'logs'), { recursive: true });
179
+ fs.appendFileSync(path.join(d, 'logs', 'changes.log'), `[${new Date().toISOString()}] ${text}\n`);
180
+ } catch {}
181
+ }
182
+
183
+ /* ---------- public API ---------- */
184
+
185
+ function list() {
186
+ loadRegistry();
187
+ return REG.bots.map((b) => ({ ...b, skills: { ...DEFAULT_SKILLS, ...(b.skills || {}) } }));
188
+ }
189
+
190
+ function get(id) {
191
+ loadRegistry();
192
+ return REG.bots.find((b) => b.id === String(id || '')) || null;
193
+ }
194
+
195
+ function add({ name, icon, prompt }) {
196
+ loadRegistry();
197
+ const n = String(name || '').trim().slice(0, 40);
198
+ if (!n) return { ok: false, error: 'bot adı zorunlu' };
199
+ if (REG.bots.length >= MAX_BOTS) return { ok: false, error: `en fazla ${MAX_BOTS} bot olabilir (1 admin + ${MAX_BOTS - 1} müşteri)` };
200
+ let id = slugify(n);
201
+ while (get(id)) id = slugify(n) + '-' + uid().slice(-4);
202
+ const bot = {
203
+ id,
204
+ name: n,
205
+ icon: ICONS.includes(icon) ? icon : '🤖',
206
+ admin: false,
207
+ prompt: String(prompt || '').slice(0, 4000),
208
+ perm: 'all',
209
+ skills: { ...DEFAULT_SKILLS },
210
+ seeBots: [], // varsayılan: hiçbir bot baka botu göremez
211
+ extBrowser: false, // dış tarayıcı sadece yetkilide
212
+ browserDefault: 'dahili',
213
+ extCommand: '',
214
+ plugins: [],
215
+ createdAt: nowIso(),
216
+ };
217
+ REG.bots.push(bot);
218
+ saveRegistry();
219
+ ensureDirs();
220
+ logChange(id, `bot oluşturuldu (name="${n}", icon=${bot.icon})`);
221
+ return { ok: true, bot: { ...bot } };
222
+ }
223
+
224
+ function update(id, patch) {
225
+ loadRegistry();
226
+ const b = get(id);
227
+ if (!b) return { ok: false, error: 'bot yok' };
228
+ const changes = [];
229
+ if (patch && typeof patch === 'object') {
230
+ if (typeof patch.name === 'string' && patch.name.trim() && patch.name.trim() !== b.name) {
231
+ changes.push(`name: "${b.name}" → "${patch.name.trim().slice(0, 40)}"`);
232
+ b.name = patch.name.trim().slice(0, 40);
233
+ }
234
+ if (typeof patch.icon === 'string' && ICONS.includes(patch.icon) && patch.icon !== b.icon) {
235
+ changes.push(`icon: ${b.icon} → ${patch.icon}`);
236
+ b.icon = patch.icon;
237
+ }
238
+ if (typeof patch.prompt === 'string' && patch.prompt !== b.prompt) {
239
+ changes.push('prompt güncellendi');
240
+ b.prompt = String(patch.prompt).slice(0, 4000);
241
+ }
242
+ if (['all', 'web', 'read', 'chat'].includes(patch.perm) && patch.perm !== b.perm) {
243
+ changes.push(`perm: ${b.perm} → ${patch.perm}`);
244
+ b.perm = patch.perm;
245
+ }
246
+ if (patch.skills && typeof patch.skills === 'object') {
247
+ const merged = { ...DEFAULT_SKILLS, ...(b.skills || {}) };
248
+ for (const k of Object.keys(DEFAULT_SKILLS)) {
249
+ if (typeof patch.skills[k] === 'boolean' && patch.skills[k] !== merged[k]) {
250
+ changes.push(`skill ${k}: ${merged[k]} → ${patch.skills[k]}`);
251
+ merged[k] = patch.skills[k];
252
+ }
253
+ }
254
+ b.skills = merged;
255
+ }
256
+ if (Array.isArray(patch.seeBots)) {
257
+ const valid = patch.seeBots.map(String).filter((x) => x !== b.id && get(x));
258
+ const next = [...new Set(valid)];
259
+ if (JSON.stringify(next) !== JSON.stringify(b.seeBots || [])) {
260
+ changes.push(`görebilir: [${(b.seeBots || []).join(', ') || '—'}] → [${next.join(', ') || '—'}]`);
261
+ b.seeBots = next;
262
+ }
263
+ }
264
+ if (typeof patch.extBrowser === 'boolean' && patch.extBrowser !== b.extBrowser) {
265
+ changes.push(`dış tarayıcı yetkisi: ${b.extBrowser} → ${patch.extBrowser}`);
266
+ b.extBrowser = patch.extBrowser;
267
+ }
268
+ if (['dahili', 'dis'].includes(patch.browserDefault) && patch.browserDefault !== b.browserDefault) {
269
+ changes.push(`varsayılan tarayıcı: ${b.browserDefault} → ${patch.browserDefault}`);
270
+ b.browserDefault = patch.browserDefault;
271
+ }
272
+ if (typeof patch.extCommand === 'string' && patch.extCommand !== b.extCommand) {
273
+ changes.push('dış tarayıcı komutu güncellendi');
274
+ b.extCommand = String(patch.extCommand).slice(0, 200);
275
+ }
276
+ if (Array.isArray(patch.plugins)) {
277
+ const next = [...new Set(patch.plugins.map((p) => String(p).slice(0, 40)).filter((p) => PLUGIN_LIST.includes(p)))];
278
+ if (JSON.stringify(next) !== JSON.stringify(b.plugins || [])) {
279
+ changes.push(`pluginler: [${(b.plugins || []).join(', ') || '—'}] → [${next.join(', ') || '—'}]`);
280
+ b.plugins = next;
281
+ }
282
+ }
283
+ }
284
+ saveRegistry();
285
+ ensureDirs();
286
+ try {
287
+ const d = botDir(b.id);
288
+ fs.writeFileSync(path.join(d, 'config.json'), JSON.stringify(botConfigOf(b), null, 2));
289
+ fs.writeFileSync(path.join(d, 'yetkiler.json'), JSON.stringify(botPermsOf(b), null, 2));
290
+ } catch {}
291
+ for (const c of changes) logChange(b.id, `yetki/ayar değişikliği (admin): ${c}`);
292
+ return { ok: true, bot: { ...b }, changed: changes.length };
293
+ }
294
+
295
+ function remove(id) {
296
+ loadRegistry();
297
+ const b = get(id);
298
+ if (!b) return { ok: false, error: 'bot yok' };
299
+ if (b.admin) return { ok: false, error: 'admin bot silinemez' };
300
+ REG.bots = REG.bots.filter((x) => x.id !== b.id);
301
+ saveRegistry();
302
+ logChange(b.id, `bot SİLİNDİ — bağlı numaralar botsuz duruma düştü (beast'e yönlendirilir)`);
303
+ /* klasör arşivlenir — veri kaybı olmasın */
304
+ try {
305
+ const d = botDir(b.id);
306
+ if (fs.existsSync(d)) fs.renameSync(d, d + '-arsiv-' + Date.now().toString(36));
307
+ } catch {}
308
+ return { ok: true };
309
+ }
310
+
311
+ /* viewer botun target botu görüp göremeyeceği */
312
+ function canSee(viewerId, targetId) {
313
+ const v = get(viewerId);
314
+ if (!v) return false;
315
+ if (v.admin) return true; // admin her şeyi görür
316
+ if (viewerId === targetId) return true;
317
+ return (v.seeBots || []).includes(String(targetId));
318
+ }
319
+
320
+ /* botun hafıza dosyaları — ayarlardaki SOUL/USER/MEMORY üçlüsünün bot izole hali */
321
+ const BOT_MEM_FILES = ['SOUL.md', 'MEMORY.md', 'USER.md'];
322
+
323
+ /* botun MEMORY.md'si (persona bloğuna giden kısım) */
324
+ function readMemory(id, cap = 2000) {
325
+ try {
326
+ return fs.readFileSync(path.join(botDir(id), 'MEMORY.md'), 'utf8').slice(0, cap).trim();
327
+ } catch {
328
+ return '';
329
+ }
330
+ }
331
+
332
+ function readMemoryFiles(id) {
333
+ const d = botDir(id);
334
+ const out = { soul: '', memory: '', user: '' };
335
+ try { out.soul = fs.readFileSync(path.join(d, 'SOUL.md'), 'utf8'); } catch {}
336
+ try { out.memory = fs.readFileSync(path.join(d, 'MEMORY.md'), 'utf8'); } catch {}
337
+ try { out.user = fs.readFileSync(path.join(d, 'USER.md'), 'utf8'); } catch {}
338
+ return out;
339
+ }
340
+
341
+ function writeMemoryFile(id, file, content) {
342
+ const b = get(id);
343
+ if (!b) return { ok: false, error: 'bot yok' };
344
+ if (!BOT_MEM_FILES.includes(file)) return { ok: false, error: 'geçersiz dosya' };
345
+ try {
346
+ const d = botDir(id);
347
+ fs.mkdirSync(d, { recursive: true });
348
+ fs.writeFileSync(path.join(d, file), String(content ?? ''), 'utf8');
349
+ logChange(id, `${file} admin tarafından güncellendi`);
350
+ return { ok: true };
351
+ } catch (e) {
352
+ return { ok: false, error: String((e && e.message) || e) };
353
+ }
354
+ }
355
+
356
+ /* ---------- BOT OTURUMU hafıza operasyonları ----------
357
+ Bot oturumlarında memory_write/memory_search GLOBAL Beast hafızasına DEĞİL,
358
+ botun kendi SOUL/USER/MEMORY dosyalarına gider (tam izolasyon). */
359
+
360
+ function readMem(id, f) {
361
+ if (!BOT_MEM_FILES.includes(f)) return '';
362
+ try {
363
+ return fs.readFileSync(path.join(botDir(id), f), 'utf8');
364
+ } catch {
365
+ return '';
366
+ }
367
+ }
368
+
369
+ function memEntries(id) {
370
+ return readMem(id, 'MEMORY.md')
371
+ .split('\n')
372
+ .map((l) => l.replace(/^[-*]\s*/, '').trim())
373
+ .filter(Boolean);
374
+ }
375
+
376
+ function appendMem(id, text) {
377
+ const t = String(text || '').replace(/\s+/g, ' ').trim().slice(0, 500);
378
+ if (!t) return { ok: false, error: 'empty' };
379
+ const list = memEntries(id);
380
+ const key = foldTr(t).replace(/[^a-z0-9]+/g, ' ').trim();
381
+ if (list.some((l) => foldTr(l).replace(/[^a-z0-9]+/g, ' ').trim() === key)) {
382
+ return { ok: true, duplicate: true };
383
+ }
384
+ try {
385
+ fs.mkdirSync(botDir(id), { recursive: true });
386
+ fs.appendFileSync(path.join(botDir(id), 'MEMORY.md'), '- ' + t + '\n');
387
+ return { ok: true };
388
+ } catch (e) {
389
+ return { ok: false, error: String((e && e.message) || e) };
390
+ }
391
+ }
392
+
393
+ /* botun USER.md'si: bota özel kullanıcı profili (aynı dedup/yenile mantığı) */
394
+ function appendUserMem(id, text) {
395
+ const t = String(text || '').replace(/\s+/g, ' ').trim().slice(0, 300);
396
+ if (!t) return { ok: false, error: 'empty' };
397
+ try {
398
+ const p = path.join(botDir(id), 'USER.md');
399
+ fs.mkdirSync(botDir(id), { recursive: true });
400
+ const lines = readMem(id, 'USER.md')
401
+ .split('\n')
402
+ .map((l) => l.replace(/^[-*]\s*/, '').trim())
403
+ .filter(Boolean);
404
+ /* profil anahtarı: "Konu: ..." satırlarında Konu — değişiklikte güncelle */
405
+ const topicOf = (s) => {
406
+ const c = foldTr(s).indexOf(':');
407
+ return (c > 0 ? foldTr(s).slice(0, c) : foldTr(s)).replace(/[^a-z0-9]+/g, ' ').trim();
408
+ };
409
+ const topic = topicOf(t);
410
+ const idx = lines.findIndex((l) => topicOf(l) === topic);
411
+ if (idx >= 0) {
412
+ if (lines[idx] === t) return { ok: true, duplicate: true };
413
+ lines[idx] = t;
414
+ } else {
415
+ lines.push(t);
416
+ }
417
+ while (lines.length > 60) lines.shift();
418
+ fs.writeFileSync(p, lines.map((l) => '- ' + l).join('\n') + '\n');
419
+ return { ok: true, updated: idx >= 0 };
420
+ } catch (e) {
421
+ return { ok: false, error: String((e && e.message) || e) };
422
+ }
423
+ }
424
+
425
+ function searchMem(id, query, limit = 8) {
426
+ const list = memEntries(id);
427
+ if (!list.length) return [];
428
+ const qTokens = memory.tokenize(query);
429
+ let rows;
430
+ if (!qTokens.length) {
431
+ rows = list.map((text, i) => ({ text, score: 0, i })).slice(-limit).reverse();
432
+ } else {
433
+ rows = list
434
+ .map((text, i) => ({ text, i, score: Number(memory.scoreEntry(text, qTokens).toFixed(3)) }))
435
+ .filter((r) => r.score > 0)
436
+ .sort((a, b) => b.score - a.score)
437
+ .slice(0, limit);
438
+ }
439
+ return rows.map(({ text, score }) => ({ score, text }));
440
+ }
441
+
442
+ function relevantMem(id, query, { maxRelevant = 6, maxRecent = 4, charCap = 2400 } = {}) {
443
+ const list = memEntries(id);
444
+ if (!list.length) return '';
445
+ const qTokens = memory.tokenize(query);
446
+ const picked = new Set();
447
+ if (qTokens.length) {
448
+ list
449
+ .map((text, i) => ({ i, score: memory.scoreEntry(text, qTokens) }))
450
+ .filter((r) => r.score > 0)
451
+ .sort((a, b) => b.score - a.score)
452
+ .slice(0, maxRelevant)
453
+ .forEach((r) => picked.add(r.i));
454
+ }
455
+ for (let i = list.length - 1; i >= 0 && picked.size < maxRelevant + maxRecent; i--) picked.add(i);
456
+ let out = '';
457
+ for (const i of [...picked].sort((a, b) => a - b)) {
458
+ const line = '- ' + list[i];
459
+ if (out.length + line.length > charCap) break;
460
+ out += (out ? '\n' : '') + line;
461
+ }
462
+ return out;
463
+ }
464
+
465
+ function readLog(id, cap = 20000) {
466
+ try {
467
+ return fs.readFileSync(path.join(botDir(id), 'logs', 'changes.log'), 'utf8').slice(-cap);
468
+ } catch {
469
+ return '';
470
+ }
471
+ }
472
+
473
+ module.exports = {
474
+ MAX_BOTS,
475
+ ICONS,
476
+ SKILL_LIST,
477
+ PLUGIN_LIST,
478
+ beastRoot,
479
+ botDir,
480
+ list,
481
+ get,
482
+ add,
483
+ update,
484
+ remove,
485
+ canSee,
486
+ readMemory,
487
+ readMemoryFiles,
488
+ writeMemoryFile,
489
+ readMem,
490
+ appendMem,
491
+ appendUserMem,
492
+ searchMem,
493
+ relevantMem,
494
+ readLog,
495
+ };