natureco-cli 5.45.1 → 5.46.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/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to NatureCo CLI will be documented in this file.
4
4
 
5
+ ## [5.46.0] - 2026-07-08 — "MEMORY: otomatik hijyen — yazma-anı dedup/çelişki uyarısı + oturum-sonu ipucu"
6
+
7
+ ### Added
8
+ - **Otomatik hafıza hijyeni (lint artık yalnız manuel değil).** `natureco memory lint` güçlüydü ama çoğu kullanıcı hiç çalıştırmaz; hafıza sessizce yinelenen/çelişen kayıtlarla bozulurdu. İki gürültüsüz otomatik katman eklendi:
9
+ - **Yazma-anı (`memory_tree append`, ajanın her kaydında).** Yeni yaprak aynı dala eklenirken mevcut yapraklarla Jaccard benzerliğine bakılır (Urðr lint mantığı, LLM'siz): **(a)** çok-benzer (≥%85) → **tekrar EKLENMEZ** (`deduped:true` + not; bloat önlenir, veri kaybı yok çünkü zaten var); **(b)** aynı konu farklı değer (%50–85) → eklenir **ama** sonuca `warning` düşülür (çelişki; hangi değerin doğru olduğuna karar veremeyiz, veriyi kaybetmeyiz → ajan/kullanıcı uzlaştırır, gerekirse `memory_tree remove`). Uyarı `buildFeedback` üzerinden ajana ulaşır (E2E doğrulandı).
10
+ - **Oturum-sonu ipucu.** `natureco chat` kapanışında hafızada olası yinelenen/çelişen kayıt varsa tek satır hatırlatma: `💡 Hafızada N olası yinelenen/çelişen kayıt — "natureco memory lint" ile gözden geçir.` Sadece bulgu varsa yazılır; hata asla oturumu bozmaz.
11
+ - 4 yeni regresyon testi (dedup atlama + tek kopya, çelişki uyarısı + ikisi de saklanır, alakasız temiz, tool-düzeyi Türkçe recall). **600 test yeşil** (597 + 3 skip).
12
+
13
+ ### Notes
14
+ - Duplicate eşiği bilinçli olarak yüksek (%85) — yalnızca neredeyse-birebir kayıtlar atlanır; "sunucu ip 10.0.0.5" → "10.0.0.9" gibi gerçek güncellemeler çelişki sayılır (atlanmaz, uyarılır), böylece meşru bir değişiklik asla kaybolmaz.
15
+
5
16
  ## [5.45.1] - 2026-07-08 — "FIX: Türkçe İ/i recall hatası — büyük-harfli her Türkçe kelime sessizce kaçıyordu"
6
17
 
7
18
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.45.1",
3
+ "version": "5.46.0",
4
4
  "description": "OpenClaw'dan daha güvenli, daha hızlı, daha ucuz AI agent CLI. Multi-agent, self-evolving skills, audit log, maliyet optimizasyonu ve NatureCo platform-native.",
5
5
  "bin": {
6
6
  "natureco": "bin/natureco.js"
@@ -1243,6 +1243,16 @@ async function startRepl(args) {
1243
1243
  console.log(chalk.gray(` 🎯 ${persistResult.preferencesAdded} yeni tercih kaydedildi`));
1244
1244
  }
1245
1245
 
1246
+ // v5.46: oturum-sonu hafıza-sağlığı ipucu (gürültüsüz). Yinelenen/çelişen kayıt varsa
1247
+ // tek satır hatırlat — kullanıcı çoğu zaman "natureco memory lint"i hiç çalıştırmaz,
1248
+ // hafıza sessizce bozulur. Sadece bulgu varsa yazılır; hata asla oturumu bozmaz.
1249
+ try {
1250
+ const { lintUser } = require('../utils/memory-lint');
1251
+ const { flatFindings, treeFindings } = lintUser(cfg.userName);
1252
+ const n = (flatFindings.length + treeFindings.length);
1253
+ if (n > 0) console.log(chalk.gray(` 💡 Hafızada ${n} olası yinelenen/çelişen kayıt — "natureco memory lint" ile gözden geçir.`));
1254
+ } catch {}
1255
+
1246
1256
  const sessId = saveSession(messages, {
1247
1257
  provider: providerUrl, model, user: cfg.userName,
1248
1258
  bot: memory.botName, factCount: memory.facts?.length || 0,
@@ -18,6 +18,7 @@ const fs = require('fs');
18
18
  const path = require('path');
19
19
  const os = require('os');
20
20
  const { foldTr } = require('../utils/tr-text');
21
+ const { tokens, jaccard } = require('../utils/memory-lint');
21
22
 
22
23
  const ROOTS = [
23
24
  { id: '1-kisisel', title: 'Kişisel & Tercihler', branches: ['Kimlik', 'Tercihler', 'İletişim Kalıpları'] },
@@ -138,13 +139,45 @@ function remove(username, root, query) {
138
139
  return { success: true, removed, root: r.id };
139
140
  }
140
141
 
142
+ // Belirli bir daldaki mevcut yaprakları döndür (yazma-anı hijyen kontrolü için). Türkçe-güvenli dal eşleşmesi.
143
+ function leavesInBranch(txt, branchName) {
144
+ const target = foldTr(branchName).trim();
145
+ const out = [];
146
+ let cur = null;
147
+ for (const line of txt.split('\n')) {
148
+ const h = line.match(/^##\s+(.+?)\s*$/);
149
+ if (h) { cur = foldTr(h[1]).trim(); continue; }
150
+ if (cur === target && /^\s*-\s+\S/.test(line)) out.push(line.trim().replace(/^-\s*/, ''));
151
+ }
152
+ return out;
153
+ }
154
+
141
155
  function append(username, root, branch, content) {
142
156
  ensureTree(username);
143
157
  const r = resolveRoot(root);
144
158
  const p = rootPath(username, r.id);
145
159
  const br = String(branch || 'Genel').trim();
146
- const leaf = '- ' + String(content).replace(/\s+/g, ' ').trim();
160
+ const cleaned = String(content).replace(/\s+/g, ' ').trim();
161
+ const leaf = '- ' + cleaned;
147
162
  let txt = fs.readFileSync(p, 'utf8');
163
+
164
+ // v5.46: yazma-anı hijyen (Urðr lint mantığı, LLM'siz). Aynı dala eklenen yaprağı mevcutlarla
165
+ // Jaccard ile karşılaştır: (a) çok-benzer (≥0.85) → TEKRAR EKLEME (bloat önle, veri kaybı yok
166
+ // çünkü zaten var); (b) aynı konu farklı değer (0.5–0.85) → EKLE ama UYAR (çelişki; hangi
167
+ // değerin doğru olduğuna karar veremeyiz, veriyi kaybetmeyiz → ajan/kullanıcı uzlaştırır).
168
+ let best = { sim: 0, leaf: null };
169
+ try {
170
+ const nt = tokens(cleaned);
171
+ for (const ex of leavesInBranch(txt, br)) {
172
+ const s = jaccard(nt, tokens(ex));
173
+ if (s > best.sim) best = { sim: s, leaf: ex };
174
+ }
175
+ } catch {}
176
+ if (best.sim >= 0.85) {
177
+ return { success: true, deduped: true, root: r.id, branch: br,
178
+ note: `Zaten çok benzer bir kayıt var (%${Math.round(best.sim * 100)}: "${best.leaf}") — tekrar eklenmedi.` };
179
+ }
180
+
148
181
  const bRe = new RegExp(`^##[ \\t]+${br.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[ \\t]*$`, 'mi');
149
182
  const m = txt.match(bRe);
150
183
  if (m) {
@@ -154,7 +187,11 @@ function append(username, root, branch, content) {
154
187
  txt = txt.trimEnd() + `\n\n## ${br}\n${leaf}\n`;
155
188
  }
156
189
  fs.writeFileSync(p, txt, 'utf8');
157
- return { success: true, root: r.id, branch: br, saved: leaf };
190
+ const result = { success: true, root: r.id, branch: br, saved: leaf };
191
+ if (best.sim >= 0.5) {
192
+ result.warning = `Aynı konuda farklı bir kayıt var (%${Math.round(best.sim * 100)}): "${best.leaf}". İkisi de saklandı; doğru olan kalsın, gerekirse memory_tree remove ile eskisini sil.`;
193
+ }
194
+ return result;
158
195
  }
159
196
 
160
197
  module.exports = {
@@ -188,5 +225,5 @@ module.exports = {
188
225
  return { success: false, error: 'bilinmeyen action: ' + p.action + ' (index|read|search|append|remove)' };
189
226
  } catch (e) { return { success: false, error: e.message }; }
190
227
  },
191
- _internal: { ensureTree, buildIndex, buildDigest, readRoot, search, append, getPending, remove, treeDir, rootPath, ROOTS },
228
+ _internal: { ensureTree, buildIndex, buildDigest, readRoot, search, append, getPending, remove, leavesInBranch, treeDir, rootPath, ROOTS },
192
229
  };