beast-agent 2.2.1 → 2.3.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 +7 -1
- package/src/agent/edgetts.js +201 -0
- package/src/agent/engine.js +93 -1
- package/src/agent/nightref.js +404 -0
- package/src/main.js +937 -57
- package/src/preload.js +9 -2
- package/src/renderer/handsfree.js +633 -0
- package/src/renderer/i18n.js +32 -6
- package/src/renderer/index.html +21 -7
- package/src/renderer/renderer.js +506 -81
- package/src/renderer/style.css +114 -24
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* Beast gece yansıması (night reflection): her gece otomatik hafıza+öğrenme bakımı.
|
|
4
|
+
Kullanıcının istediği üç soru genişletildi:
|
|
5
|
+
1) "Bugün ne öğrendim?" → journal/YYYY-MM-DD.md (episodik günlük; detay buraya,
|
|
6
|
+
MEMORY.md şişmez — uzun vadeli semantik hafıza sade kalır)
|
|
7
|
+
2) "Memoryde gereksiz ne var?" → MEMORY.md satırları için LLM drop/merge kararları
|
|
8
|
+
(satır numarası bazlı — mem0 tarzı anti-hallosinasyon),
|
|
9
|
+
yedek alındıktan sonra uygulanır
|
|
10
|
+
3) "Bağlamı nasıl sıkılaştırırım?" → önce/sonra token-char istatistiği + öneriler,
|
|
11
|
+
reflections/YYYY-MM-DD.json raporu
|
|
12
|
+
Zamanlama: engine 10 dk'da bir tick atar; hedef saat (varsayılan 03:30) geçildiyse ve
|
|
13
|
+
bugün çalışılmadıysa tetiklenir. App gece kapalıysa açılışta yakalama (catch-up) yapar.
|
|
14
|
+
LLM yoksa/olmazsa: LLM adımları atlanır, rapor yine de yazılır (degradasyon). */
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const { estTokens } = require('./tokens');
|
|
19
|
+
|
|
20
|
+
const DEFAULT_AT = '03:30';
|
|
21
|
+
const MAX_DROP_RATIO = 0.4; // tek gecede kayıtların en fazla %40'ı düşer
|
|
22
|
+
const MERGE_MAX = 3; // bir birleştirmede en fazla 3 kaynak satır
|
|
23
|
+
const MERGE_TEXT_MAX = 240; // birleşik satır tavanı (append'in 500 cap'iyle uyumlu)
|
|
24
|
+
const CONSOLIDATE_MIN = 10; // kayıt bundan azsa sıkılaştırmaya değmez
|
|
25
|
+
const TRANSCRIPT_TAIL = 40; // oturum başına son N mesaj
|
|
26
|
+
const DAY_TRANSCRIPT_CAP = 16000; // günün toplam transkript tavanı (char)
|
|
27
|
+
const LEARNINGS_MAX = 8;
|
|
28
|
+
const FACTS_MAX = 2;
|
|
29
|
+
const LLM_TIMEOUT_MS = 120000;
|
|
30
|
+
const KEEP_REPORTS = 90;
|
|
31
|
+
const KEEP_JOURNALS = 120;
|
|
32
|
+
const KEEP_BACKUPS = 14;
|
|
33
|
+
|
|
34
|
+
/* ---------- zamanlama ---------- */
|
|
35
|
+
|
|
36
|
+
function resolveAt(v) {
|
|
37
|
+
const m = /^(\d{1,2}):(\d{2})$/.exec(String(v || '').trim());
|
|
38
|
+
if (!m) return { h: 3, m: 30 };
|
|
39
|
+
const h = Number(m[1]);
|
|
40
|
+
const min = Number(m[2]);
|
|
41
|
+
if (h > 23 || min > 59) return { h: 3, m: 30 };
|
|
42
|
+
return { h, m: min };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function dayKey(d) {
|
|
46
|
+
const x = d instanceof Date ? d : new Date(d);
|
|
47
|
+
if (isNaN(x.getTime())) return '';
|
|
48
|
+
const p = (n) => String(n).padStart(2, '0');
|
|
49
|
+
return `${x.getFullYear()}-${p(x.getMonth() + 1)}-${p(x.getDate())}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function startOfDay(d) {
|
|
53
|
+
const x = d instanceof Date ? new Date(d) : new Date(d || Date.now());
|
|
54
|
+
x.setHours(0, 0, 0, 0);
|
|
55
|
+
return x;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Hedef saat geçildiyse ve bugünün turu atlanmadıysa true.
|
|
59
|
+
lastAt: son yansımanın ISO zamanı (hiç çalışılmadıysa null). */
|
|
60
|
+
function due({ now, lastAt, at } = {}) {
|
|
61
|
+
const d = now ? new Date(now) : new Date();
|
|
62
|
+
const { h, m } = resolveAt(at);
|
|
63
|
+
if (d.getHours() * 60 + d.getMinutes() < h * 60 + m) return false;
|
|
64
|
+
return dayKey(lastAt) !== dayKey(d);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* ---------- state (reflections/last.json) ---------- */
|
|
68
|
+
|
|
69
|
+
function reflectionsDir(memDir_) {
|
|
70
|
+
return path.join(memDir_, 'reflections');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function readLast(memDir_) {
|
|
74
|
+
try {
|
|
75
|
+
const r = JSON.parse(fs.readFileSync(path.join(reflectionsDir(memDir_), 'last.json'), 'utf8'));
|
|
76
|
+
return r && typeof r === 'object' ? r : null;
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function writeLast(memDir_, at) {
|
|
83
|
+
try {
|
|
84
|
+
fs.mkdirSync(reflectionsDir(memDir_), { recursive: true });
|
|
85
|
+
fs.writeFileSync(
|
|
86
|
+
path.join(reflectionsDir(memDir_), 'last.json'),
|
|
87
|
+
JSON.stringify({ at: new Date(at || Date.now()).toISOString(), day: dayKey(at || Date.now()) }, null, 2)
|
|
88
|
+
);
|
|
89
|
+
return true;
|
|
90
|
+
} catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* ---------- karar ayrıştırma ---------- */
|
|
96
|
+
|
|
97
|
+
/* Yansıma cevabından JSON çıkarımı: düz / fence'li / gömülü. Bozuk girişte null. */
|
|
98
|
+
function extractJson(text, validator) {
|
|
99
|
+
const t = String(text || '').replace(/```(?:json)?/gi, '');
|
|
100
|
+
const start = t.indexOf('{');
|
|
101
|
+
if (start < 0) return null;
|
|
102
|
+
for (let end = t.lastIndexOf('}'); end > start; end = t.lastIndexOf('}', end - 1)) {
|
|
103
|
+
try {
|
|
104
|
+
const obj = JSON.parse(t.slice(start, end + 1));
|
|
105
|
+
if (obj && typeof obj === 'object' && (!validator || validator(obj))) return obj;
|
|
106
|
+
} catch {}
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function parseDecision(raw) {
|
|
112
|
+
return extractJson(raw, (o) => Array.isArray(o.drop) || Array.isArray(o.merge) || Array.isArray(o.keep));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function parseLearnings(raw) {
|
|
116
|
+
const o = extractJson(raw, (x) => Array.isArray(x.learnings) || Array.isArray(x.facts));
|
|
117
|
+
if (!o) return { learnings: [], facts: [] };
|
|
118
|
+
const clean = (arr, cap, maxLen) =>
|
|
119
|
+
(Array.isArray(arr) ? arr : [])
|
|
120
|
+
.map((s) => String(s || '').replace(/\s+/g, ' ').trim().slice(0, maxLen))
|
|
121
|
+
.filter(Boolean)
|
|
122
|
+
.slice(0, cap);
|
|
123
|
+
return {
|
|
124
|
+
learnings: clean(o.learnings, LEARNINGS_MAX, 160),
|
|
125
|
+
facts: clean(o.facts, FACTS_MAX, 120),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* ---------- bellek cerrahisi (doğrulanmış drop/merge) ----------
|
|
130
|
+
|
|
131
|
+
LLM yalnızca SATIR NUMARASI verir; metinleri biz uygularız. Kurallar:
|
|
132
|
+
- geçersiz/aralık dışı id yok sayılır (halüsinasyon filtresi)
|
|
133
|
+
- bir satır tek karara bağlanır; karar verilmeyen satır KEEP (güvenli varsayılan)
|
|
134
|
+
- merge metni kaynak satırların toplamından uzun olamaz (uydurma filtresi)
|
|
135
|
+
- drop tavanı: kayıtların en fazla %40'ı (tek gecede felç olmaz)
|
|
136
|
+
- merge sırası: birleşik satır, ilk kaynağın yerine yazılır (kronoloji korunur) */
|
|
137
|
+
function applyMemoryOps(lines, decision, { maxDropRatio = MAX_DROP_RATIO } = {}) {
|
|
138
|
+
const n = (Array.isArray(lines) ? lines : []).length;
|
|
139
|
+
const d = decision && typeof decision === 'object' ? decision : {};
|
|
140
|
+
const validId = (x) => Number.isInteger(x) && x >= 0 && x < n;
|
|
141
|
+
const action = new Array(n).fill('keep');
|
|
142
|
+
const mergedText = new Map(); // ilk kaynak id -> birleşik metin
|
|
143
|
+
|
|
144
|
+
/* 1) merge'ler önce bağlanır (drop'lar merge'lenmiş satırı çalamaz) */
|
|
145
|
+
let mergedCount = 0;
|
|
146
|
+
for (const mg of Array.isArray(d.merge) ? d.merge : []) {
|
|
147
|
+
if (!mg || !Array.isArray(mg.ids)) continue;
|
|
148
|
+
const ids = [...new Set(mg.ids)].filter(validId).sort((a, b) => a - b).slice(0, MERGE_MAX);
|
|
149
|
+
if (ids.length < 2) continue;
|
|
150
|
+
if (ids.some((i) => action[i] !== 'keep')) continue;
|
|
151
|
+
const text = String(mg.text || '').replace(/\s+/g, ' ').trim().slice(0, MERGE_TEXT_MAX);
|
|
152
|
+
if (!text) continue;
|
|
153
|
+
const srcLen = ids.reduce((a, i) => a + String(lines[i]).length, 0);
|
|
154
|
+
if (text.length > srcLen) continue; // kaynakların toplamından uzun = uydurma
|
|
155
|
+
for (const i of ids) action[i] = 'merged';
|
|
156
|
+
mergedText.set(ids[0], text);
|
|
157
|
+
mergedCount++;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* 2) drop'lar tavana göre uygulanır */
|
|
161
|
+
const dropWanted = [...new Set(Array.isArray(d.drop) ? d.drop : [])].filter(validId)
|
|
162
|
+
.filter((i) => action[i] === 'keep')
|
|
163
|
+
.sort((a, b) => a - b); // eskiden yeniye — tavan aşılırsa yeniler kalır
|
|
164
|
+
const maxDrop = Math.max(0, Math.floor(n * maxDropRatio));
|
|
165
|
+
const dropSet = new Set(dropWanted.slice(0, maxDrop));
|
|
166
|
+
for (const i of dropSet) action[i] = 'drop';
|
|
167
|
+
|
|
168
|
+
/* 3) yeni listeyi kur */
|
|
169
|
+
const out = [];
|
|
170
|
+
let dropped = 0;
|
|
171
|
+
for (let i = 0; i < n; i++) {
|
|
172
|
+
if (action[i] === 'drop') { dropped++; continue; }
|
|
173
|
+
if (action[i] === 'merged') {
|
|
174
|
+
if (mergedText.has(i)) out.push(mergedText.get(i));
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
out.push(lines[i]);
|
|
178
|
+
}
|
|
179
|
+
return { lines: out, dropped, merged: mergedCount, dropSkipped: dropWanted.length - dropSet.size };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* ---------- promptlar ---------- */
|
|
183
|
+
|
|
184
|
+
function learningsPrompt(sinceLabel, transcript) {
|
|
185
|
+
return (
|
|
186
|
+
'Sen bir yapay zeka asistanının gece yansıma modülüsün. Aşağıda ' +
|
|
187
|
+
(sinceLabel ? sinceLabel + ' aralığındaki' : 'bugünkü') +
|
|
188
|
+
' oturum transkriptleri var.\n' +
|
|
189
|
+
'GÖREV: Bugün öğrenilenleri çıkar.\n' +
|
|
190
|
+
'KURALLAR:\n' +
|
|
191
|
+
'- learnings: bugün yapılan işlerden/keşiflerden/hatalardan çıkarımlar (en fazla ' + LEARNINGS_MAX + ' madde, her biri en fazla 160 karakter)\n' +
|
|
192
|
+
'- facts: haftalar sonra bile işe yarayacak KALICI bilgiler — kullanıcı tercihi, proje, isim, rutin (en fazla ' + FACTS_MAX + ' madde, her biri en fazla 120 karakter)\n' +
|
|
193
|
+
'- Uydurma yok; değerli şey yoksa boş dizi\n' +
|
|
194
|
+
'SADECE JSON dön: {"learnings":["..."],"facts":["..."]}\n\n' +
|
|
195
|
+
'# OTURUMLAR\n' + transcript
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function consolidatePrompt(lines) {
|
|
200
|
+
const numbered = lines.map((l, i) => i + ': ' + l).join('\n');
|
|
201
|
+
return (
|
|
202
|
+
'Sen bir uzun vadeli hafıza yöneticisisin. Bir agentın MEMORY.md kayıtlarını sıkılaştır.\n' +
|
|
203
|
+
'KURALLAR:\n' +
|
|
204
|
+
'- drop: geçici/çözülmüş/değersiz kayıtlar — tek seferlik detay, "yaptım bildirdim" gürültüsü, bayat bilgi, tekrar\n' +
|
|
205
|
+
'- merge: 2-3 yakın/anlamdaş kaydı TEK daha kısa satırda birleştir; YENİ BİLGİ EKLEME, sadece kaynakların özünü yaz\n' +
|
|
206
|
+
'- Değerli kalıcı bilgi asla düşülmez. Tarih içeren satırların tarihini koru.\n' +
|
|
207
|
+
'- En fazla %40 kayıt düşebilirsin. Şüphede kalacaksan dokunma.\n' +
|
|
208
|
+
'SADECE JSON dön (satır numaralarıyla): {"drop":[0,5],"merge":[{"ids":[2,7],"text":"birleşik öz"}]}\n' +
|
|
209
|
+
'Birleşik metin, kaynak satırların TOPLAM uzunluğundan uzun OLAMAZ.\n\n' +
|
|
210
|
+
'# KAYITLAR (no: metin)\n' + numbered
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* ---------- dosya yazıcıları ---------- */
|
|
215
|
+
|
|
216
|
+
function writeJournalFile(memDir_, dateKey, md) {
|
|
217
|
+
try {
|
|
218
|
+
const dir = path.join(memDir_, 'journal');
|
|
219
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
220
|
+
fs.writeFileSync(path.join(dir, dateKey + '.md'), md, 'utf8');
|
|
221
|
+
return path.join('journal', dateKey + '.md');
|
|
222
|
+
} catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function writeReportFile(memDir_, dateKey, report) {
|
|
228
|
+
try {
|
|
229
|
+
const dir = reflectionsDir(memDir_);
|
|
230
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
231
|
+
fs.writeFileSync(path.join(dir, dateKey + '.json'), JSON.stringify(report, null, 2), 'utf8');
|
|
232
|
+
return path.join('reflections', dateKey + '.json');
|
|
233
|
+
} catch {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function backupMemoryFile(memDir_, dateKey) {
|
|
239
|
+
try {
|
|
240
|
+
const dir = path.join(memDir_, 'backups');
|
|
241
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
242
|
+
fs.writeFileSync(path.join(dir, 'MEMORY-' + dateKey + '.md'), fs.readFileSync(path.join(memDir_, 'MEMORY.md'), 'utf8'));
|
|
243
|
+
return true;
|
|
244
|
+
} catch {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function pruneDir(dir, keep) {
|
|
250
|
+
try {
|
|
251
|
+
const files = fs.readdirSync(dir).filter((f) => /\.(json|md)$/.test(f) && f !== 'last.json').sort();
|
|
252
|
+
const excess = files.length - keep;
|
|
253
|
+
for (let i = 0; i < excess; i++) {
|
|
254
|
+
try { fs.unlinkSync(path.join(dir, files[i])); } catch {}
|
|
255
|
+
}
|
|
256
|
+
} catch {}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* ---------- orkestratör ----------
|
|
260
|
+
|
|
261
|
+
deps: {
|
|
262
|
+
llm: async (prompt) => string — engine chatOnce köprüsü
|
|
263
|
+
memory: modül — entries/save/append
|
|
264
|
+
mem0Enabled: bool — bilgi amaçlı (memory.save store'u yeniden kurar)
|
|
265
|
+
sessions: [{ id, title, updatedAt, transcript }]
|
|
266
|
+
sinceIso: string|null — kapsam başlangıcı
|
|
267
|
+
manual: bool
|
|
268
|
+
now: Date — test enjeksiyonu
|
|
269
|
+
log: fn
|
|
270
|
+
} */
|
|
271
|
+
async function run(deps) {
|
|
272
|
+
const memDir_ = deps.memDir || (deps.memory && typeof deps.memory.memDir === 'function' ? deps.memory.memDir() : null);
|
|
273
|
+
const log = typeof deps.log === 'function' ? deps.log : () => {};
|
|
274
|
+
const now = deps.now ? new Date(deps.now) : new Date();
|
|
275
|
+
const dateKey = dayKey(now);
|
|
276
|
+
const before = deps.memory.entries();
|
|
277
|
+
const charsBefore = before.reduce((a, l) => a + l.length + 2, 0);
|
|
278
|
+
const report = {
|
|
279
|
+
date: dateKey,
|
|
280
|
+
at: now.toISOString(),
|
|
281
|
+
manual: !!deps.manual,
|
|
282
|
+
since: deps.sinceIso || null,
|
|
283
|
+
sessions: Array.isArray(deps.sessions) ? deps.sessions.length : 0,
|
|
284
|
+
learnings: [],
|
|
285
|
+
factsAdded: 0,
|
|
286
|
+
journal: null,
|
|
287
|
+
reportFile: null,
|
|
288
|
+
memory: { before: before.length, after: before.length, dropped: 0, merged: 0, dropSkipped: 0, charsBefore, charsAfter: charsBefore, tokensSaved: 0 },
|
|
289
|
+
notes: [],
|
|
290
|
+
errors: [],
|
|
291
|
+
ok: true,
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/* 1) günün transkripti */
|
|
295
|
+
const transcript = (Array.isArray(deps.sessions) ? deps.sessions : [])
|
|
296
|
+
.map((s) => `[${String(s.title || s.id || 'oturum').slice(0, 60)}]\n${String(s.transcript || '')}`)
|
|
297
|
+
.join('\n\n')
|
|
298
|
+
.slice(0, DAY_TRANSCRIPT_CAP);
|
|
299
|
+
const noLlm = typeof deps.llm !== 'function';
|
|
300
|
+
if (noLlm) report.errors.push('llm yok — öğrenme/sıkılaştırma atlandı');
|
|
301
|
+
|
|
302
|
+
/* 2) bugün ne öğrendim → journal */
|
|
303
|
+
if (!noLlm && transcript.trim().length >= 200) {
|
|
304
|
+
try {
|
|
305
|
+
const sinceLabel = deps.sinceIso ? dayKey(deps.sinceIso) + ' ' + String(deps.sinceIso).slice(11, 16) + ' sonrası' : '';
|
|
306
|
+
const raw = await deps.llm(learningsPrompt(sinceLabel, transcript));
|
|
307
|
+
const parsed = parseLearnings(raw);
|
|
308
|
+
report.learnings = parsed.learnings;
|
|
309
|
+
/* kalıcı factler MEMORY.md'ye — append'in dedup'ı tekrarı engeller */
|
|
310
|
+
for (const f of parsed.facts) {
|
|
311
|
+
const r = deps.memory.append(f);
|
|
312
|
+
if (r && r.ok && !r.duplicate) report.factsAdded++;
|
|
313
|
+
}
|
|
314
|
+
} catch (e) {
|
|
315
|
+
report.errors.push('öğrenme: ' + String((e && e.message) || e).slice(0, 120));
|
|
316
|
+
}
|
|
317
|
+
} else if (transcript.trim().length < 200) {
|
|
318
|
+
report.notes.push('bugün değerli transkript yok — öğrenme adımı atlandı');
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* 3) memory sıkılaştırma: drop/merge kararları → yedek → uygula */
|
|
322
|
+
const entriesNow = deps.memory.entries();
|
|
323
|
+
if (!noLlm && entriesNow.length >= CONSOLIDATE_MIN) {
|
|
324
|
+
try {
|
|
325
|
+
const raw = await deps.llm(consolidatePrompt(entriesNow));
|
|
326
|
+
const decision = parseDecision(raw);
|
|
327
|
+
if (!decision) {
|
|
328
|
+
report.errors.push('sıkılaştırma: karar JSON\u2019i ayrıştırılamadı');
|
|
329
|
+
} else {
|
|
330
|
+
const applied = applyMemoryOps(entriesNow, decision, {});
|
|
331
|
+
if (applied.dropped + applied.merged > 0) {
|
|
332
|
+
backupMemoryFile(memDir_, dateKey);
|
|
333
|
+
/* memory.save → mem0 store'u satırlardan yeniden kurar (store=doğruluk kaynağı) */
|
|
334
|
+
const w = deps.memory.save('MEMORY.md', applied.lines.map((l) => '- ' + l).join('\n') + (applied.lines.length ? '\n' : ''));
|
|
335
|
+
if (w && w.ok) {
|
|
336
|
+
const charsAfter = applied.lines.reduce((a, l) => a + l.length + 2, 0);
|
|
337
|
+
report.memory.after = applied.lines.length;
|
|
338
|
+
report.memory.dropped = applied.dropped;
|
|
339
|
+
report.memory.merged = applied.merged;
|
|
340
|
+
report.memory.dropSkipped = applied.dropSkipped;
|
|
341
|
+
report.memory.charsAfter = charsAfter;
|
|
342
|
+
report.memory.tokensSaved = Math.max(0, estTokens(charsBefore) - estTokens(charsAfter));
|
|
343
|
+
} else {
|
|
344
|
+
report.errors.push('sıkılaştırma: MEMORY.md yazılamadı');
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
report.notes.push('hafıza zaten sıkı — değişiklik gerekmedi');
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
} catch (e) {
|
|
351
|
+
report.errors.push('sıkılaştırma: ' + String((e && e.message) || e).slice(0, 120));
|
|
352
|
+
}
|
|
353
|
+
} else if (entriesNow.length < CONSOLIDATE_MIN) {
|
|
354
|
+
report.notes.push('kayıt sayısı az (' + entriesNow.length + ') — sıkılaştırma atlandı');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* 4) bağlam raporu + journal dosyası */
|
|
358
|
+
const scopeLabel = deps.sinceIso
|
|
359
|
+
? dayKey(deps.sinceIso) + ' ' + String(deps.sinceIso).slice(11, 16) + ' → ' + dayKey(now) + ' ' + String(now.toTimeString ? now.toTimeString().slice(0, 5) : '')
|
|
360
|
+
: 'bugün';
|
|
361
|
+
const m = report.memory;
|
|
362
|
+
const journalMd =
|
|
363
|
+
'# Günlük Yansıma — ' + dateKey + (deps.manual ? ' (elle tetiklendi)' : '') + '\n' +
|
|
364
|
+
'Kapsam: ' + scopeLabel + ' · ' + report.sessions + ' oturum\n\n' +
|
|
365
|
+
'## Bugün öğrendiklerim\n' +
|
|
366
|
+
(report.learnings.length ? report.learnings.map((l) => '- ' + l).join('\n') + '\n' : '- (kayıt yok)\n') +
|
|
367
|
+
(report.factsAdded ? '\nHafızaya eklenen kalıcı bilgi: ' + report.factsAdded + ' madde\n' : '') +
|
|
368
|
+
'\n## Bellek bakımı\n' +
|
|
369
|
+
'- Kayıt: ' + m.before + ' → ' + m.after + ' (düşülen ' + m.dropped + ', ' + m.merged + ' kayıt birleşti)\n' +
|
|
370
|
+
'- Boyut: ' + m.charsBefore + ' → ' + m.charsAfter + ' char (~' + m.tokensSaved + ' token tasarruf)\n' +
|
|
371
|
+
(report.notes.length ? '- Not: ' + report.notes.join('; ') + '\n' : '') +
|
|
372
|
+
(report.errors.length ? '- Hata: ' + report.errors.join('; ') + '\n' : '');
|
|
373
|
+
report.journal = writeJournalFile(memDir_, dateKey, journalMd);
|
|
374
|
+
report.reportFile = writeReportFile(memDir_, dateKey, report);
|
|
375
|
+
writeLast(memDir_, now);
|
|
376
|
+
pruneDir(path.join(memDir_, 'journal'), KEEP_JOURNALS);
|
|
377
|
+
pruneDir(reflectionsDir(memDir_), KEEP_REPORTS);
|
|
378
|
+
pruneDir(path.join(memDir_, 'backups'), KEEP_BACKUPS);
|
|
379
|
+
log('yansıma tamam: ' + m.before + '→' + m.after + ' kayıt, +' + report.factsAdded + ' fact, ' + report.errors.length + ' hata');
|
|
380
|
+
return report;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
module.exports = {
|
|
384
|
+
DEFAULT_AT,
|
|
385
|
+
MAX_DROP_RATIO,
|
|
386
|
+
MERGE_MAX,
|
|
387
|
+
MERGE_TEXT_MAX,
|
|
388
|
+
CONSOLIDATE_MIN,
|
|
389
|
+
TRANSCRIPT_TAIL,
|
|
390
|
+
DAY_TRANSCRIPT_CAP,
|
|
391
|
+
LLM_TIMEOUT_MS,
|
|
392
|
+
resolveAt,
|
|
393
|
+
dayKey,
|
|
394
|
+
startOfDay,
|
|
395
|
+
due,
|
|
396
|
+
readLast,
|
|
397
|
+
writeLast,
|
|
398
|
+
parseDecision,
|
|
399
|
+
parseLearnings,
|
|
400
|
+
applyMemoryOps,
|
|
401
|
+
learningsPrompt,
|
|
402
|
+
consolidatePrompt,
|
|
403
|
+
run,
|
|
404
|
+
};
|