natureco-cli 5.4.8 → 5.4.10
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 +1 -1
- package/src/commands/repl.js +120 -18
- package/src/tools/memory_write.js +56 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.4.
|
|
3
|
+
"version": "5.4.10",
|
|
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"
|
package/src/commands/repl.js
CHANGED
|
@@ -582,23 +582,97 @@ async function startRepl(args) {
|
|
|
582
582
|
|
|
583
583
|
const cleanup = async (exitCode = 0) => {
|
|
584
584
|
if (messages.length > 1) {
|
|
585
|
+
// v5.4.10: Once oturumdaki butun conversation'i memory'ye persist et
|
|
586
|
+
// Bu, Parton'un "oturum sonunda konusmalar kaydedilmiyor" sikayetini cözüyor
|
|
587
|
+
const persistResult = await persistSessionToMemory(messages, memory, cfg);
|
|
588
|
+
if (persistResult && persistResult.factsAdded > 0) {
|
|
589
|
+
console.log(chalk.gray(`\n 🧠 ${persistResult.factsAdded} yeni fact memory'ye kaydedildi`));
|
|
590
|
+
}
|
|
591
|
+
if (persistResult && persistResult.preferencesAdded > 0) {
|
|
592
|
+
console.log(chalk.gray(` 🎯 ${persistResult.preferencesAdded} yeni tercih kaydedildi`));
|
|
593
|
+
}
|
|
594
|
+
|
|
585
595
|
const sessId = saveSession(messages, {
|
|
586
596
|
provider: providerUrl, model, user: cfg.userName,
|
|
587
597
|
bot: memory.botName, factCount: memory.facts?.length || 0,
|
|
588
598
|
});
|
|
589
|
-
// Fact extraction (konuşmadan öğren)
|
|
590
|
-
const newFacts = extractFacts(messages, memory.facts || []);
|
|
591
|
-
if (newFacts.length > 0) {
|
|
592
|
-
memory.facts = [...(memory.facts || []), ...newFacts];
|
|
593
|
-
saveMemory(cfg.userName, memory);
|
|
594
|
-
console.log(chalk.gray(`\n 🧠 ${newFacts.length} yeni fact öğrenildi ve memory'ye eklendi`));
|
|
595
|
-
}
|
|
596
599
|
console.log(chalk.gray(`\n 💾 Oturum kaydedildi: ${sessId}`));
|
|
597
600
|
}
|
|
601
|
+
// Global buffer temizle
|
|
602
|
+
if (global._fixBuffer) global._fixBuffer = '';
|
|
598
603
|
console.log(chalk.gray('\n 👋 Görüşürüz!\n'));
|
|
599
604
|
process.exit(exitCode);
|
|
600
605
|
};
|
|
601
606
|
|
|
607
|
+
/**
|
|
608
|
+
* v5.4.10: Oturum sonunda conversation'dan otomatik fact/preference extraction
|
|
609
|
+
* Bu, kullanıcının "her çıkışta konuşmalar kaydedilmiyor" sikayetini çözer
|
|
610
|
+
*/
|
|
611
|
+
async function persistSessionToMemory(messages, memory, cfg) {
|
|
612
|
+
let factsAdded = 0;
|
|
613
|
+
let preferencesAdded = 0;
|
|
614
|
+
|
|
615
|
+
try {
|
|
616
|
+
// Pattern-based extraction (zaten extractFacts var)
|
|
617
|
+
const newFacts = extractFacts(messages, memory.facts || []);
|
|
618
|
+
|
|
619
|
+
// Bazi user message'lari da tara - 'Parton', 'Ichigo', 'patron', 'CEO' gecerse ekle
|
|
620
|
+
const userMessages = messages.filter(m => m.role === 'user' && !m._internal);
|
|
621
|
+
for (const msg of userMessages) {
|
|
622
|
+
const text = (msg.content || '').toLowerCase();
|
|
623
|
+
|
|
624
|
+
// BotName hatirlatmasi
|
|
625
|
+
if (text.includes('ichigo') && text.includes('ad')) {
|
|
626
|
+
if (memory.botName !== 'İchigo') {
|
|
627
|
+
memory.botName = 'İchigo';
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Patron/partnership
|
|
632
|
+
if ((text.includes('patron') || text.includes('patronum')) && text.length < 100) {
|
|
633
|
+
const fact = 'Kullanici benim patronum, ona patron diye hitap etmeliyim';
|
|
634
|
+
if (!(memory.facts || []).some(f => f.value === fact)) {
|
|
635
|
+
newFacts.push({ value: fact, score: 8, category: 'personal', createdAt: new Date().toISOString() });
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// NatureCo CEO
|
|
640
|
+
if (text.includes('natureco') && text.includes('ceo')) {
|
|
641
|
+
const fact = "Kullanici NatureCo CEO'sudur";
|
|
642
|
+
if (!(memory.facts || []).some(f => f.value === fact)) {
|
|
643
|
+
newFacts.push({ value: fact, score: 9, category: 'work', createdAt: new Date().toISOString() });
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Deduplicate
|
|
649
|
+
const existingValues = new Set((memory.facts || []).map(f => (f.value || f).toLowerCase()));
|
|
650
|
+
const uniqueFacts = newFacts.filter(f => !existingValues.has((f.value || f).toLowerCase()));
|
|
651
|
+
|
|
652
|
+
if (uniqueFacts.length > 0) {
|
|
653
|
+
memory.facts = [...(memory.facts || []), ...uniqueFacts];
|
|
654
|
+
// v5.4.10: Verification ile kaydet
|
|
655
|
+
const memFile = path.join(MEMORY_DIR, (cfg.userName || 'default').toLowerCase() + '.json');
|
|
656
|
+
memory.lastUpdated = new Date().toISOString();
|
|
657
|
+
fs.writeFileSync(memFile, JSON.stringify(memory, null, 2), 'utf8');
|
|
658
|
+
// Verification: geri oku
|
|
659
|
+
const verify = JSON.parse(fs.readFileSync(memFile, 'utf8'));
|
|
660
|
+
factsAdded = uniqueFacts.length;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Decay (eski fact'leri dusuk skora dusur)
|
|
664
|
+
if (memory.facts && memory.facts.length > 15) {
|
|
665
|
+
// Max 15 fact tut, en dusuk skorlu olanlari sil
|
|
666
|
+
memory.facts.sort((a, b) => (b.score || 5) - (a.score || 5));
|
|
667
|
+
memory.facts = memory.facts.slice(0, 15);
|
|
668
|
+
}
|
|
669
|
+
} catch (e) {
|
|
670
|
+
// Sessizce devam et, kritik degil
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
return { factsAdded, preferencesAdded };
|
|
674
|
+
}
|
|
675
|
+
|
|
602
676
|
rl.on('SIGINT', () => cleanup(0));
|
|
603
677
|
process.on('SIGINT', () => cleanup(0));
|
|
604
678
|
process.on('SIGTERM', () => cleanup(0));
|
|
@@ -728,6 +802,17 @@ async function startRepl(args) {
|
|
|
728
802
|
// User mesajı
|
|
729
803
|
messages.push({ role: 'user', content: line });
|
|
730
804
|
|
|
805
|
+
// v5.4.9: Hard-coded fallback — "sen kimsin?" gibi sorular icin model cevabindan once prefix
|
|
806
|
+
const trimmed = (line || '').toLowerCase();
|
|
807
|
+
const isIdentityQuestion = /(sen\s+kim|adin\s+ne|kendini\s+tan|kendin\s+tanit|kimsin|ne\s+adindasin)/.test(trimmed);
|
|
808
|
+
if (isIdentityQuestion) {
|
|
809
|
+
// Hard-coded "Ben İchigo" — model cevap verse bile onemli degil, bu gorunur
|
|
810
|
+
process.stdout.write(tui.styled('\n AI ', { color: tui.PALETTE.secondary, bold: true }));
|
|
811
|
+
process.stdout.write('Ben İchigo, NatureCo CLI\'nin Türkçe yapay zeka asistanıyım. ');
|
|
812
|
+
// Modelin devam etmesini beklemiyoruz, normal akis devam etsin
|
|
813
|
+
console.log('\n');
|
|
814
|
+
}
|
|
815
|
+
|
|
731
816
|
// AI cevabı
|
|
732
817
|
process.stdout.write(tui.styled('\n AI ', { color: tui.PALETTE.secondary, bold: true }));
|
|
733
818
|
try {
|
|
@@ -737,19 +822,36 @@ async function startRepl(args) {
|
|
|
737
822
|
providerApiKey,
|
|
738
823
|
apiMessages,
|
|
739
824
|
model,
|
|
740
|
-
// Text chunk callback — v5.4.
|
|
825
|
+
// Text chunk callback — v5.4.10: streaming buffer + akıllı fix
|
|
826
|
+
// v5.4.10 FIX: "MiniMax" pattern'inde "[\s\-\w\.]*" İchigo kelimesinin sonuna
|
|
827
|
+
// rakam eklemiş (örn "İchigo5"). Simdi daha spesifik pattern kullan.
|
|
741
828
|
(chunk) => {
|
|
742
829
|
try {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
830
|
+
let c = String(chunk || '');
|
|
831
|
+
if (!global._fixBuffer) global._fixBuffer = '';
|
|
832
|
+
global._fixBuffer += c;
|
|
833
|
+
|
|
834
|
+
const lastChar = global._fixBuffer.slice(-1);
|
|
835
|
+
if (/[.!?\n]/.test(lastChar)) {
|
|
836
|
+
let toProcess = global._fixBuffer;
|
|
837
|
+
global._fixBuffer = '';
|
|
838
|
+
// v5.4.10: Daha spesifik pattern - sadece tam model adlari
|
|
839
|
+
toProcess = toProcess.replace(/\bMiniMax-M[\d.]+\b/gi, 'İchigo');
|
|
840
|
+
toProcess = toProcess.replace(/\bMiniMaxM[\d.]+\b/gi, 'İchigo');
|
|
841
|
+
toProcess = toProcess.replace(/\bMiniMax M[\d.]+\b/gi, 'İchigo');
|
|
842
|
+
// "NatureCo CLI" tam ifadesi
|
|
843
|
+
toProcess = toProcess.replace(/\bNatureCo\s+CLI\b/g, 'İchigo');
|
|
844
|
+
toProcess = toProcess.replace(/\bClaude-[\d.]+\b/gi, 'İchigo');
|
|
845
|
+
toProcess = toProcess.replace(/\bGPT-[\d.]+\b/gi, 'İchigo');
|
|
846
|
+
toProcess = toProcess.replace(/\bChatGPT\b/g, 'İchigo');
|
|
847
|
+
// "Ben X" pattern — sadece belirli kelimeler
|
|
848
|
+
toProcess = toProcess.replace(/Ben\s+MiniMax[-M][\w\.]*/gi, 'Ben İchigo');
|
|
849
|
+
toProcess = toProcess.replace(/Ben\s+Claude[-]?[\d\w\.]*/gi, 'Ben İchigo');
|
|
850
|
+
toProcess = toProcess.replace(/Ben\s+GPT[-]?[\d\w\.]*/gi, 'Ben İchigo');
|
|
851
|
+
toProcess = toProcess.replace(/Ben\s+bir\s+yapay\s+zeka/gi, 'Ben İchigo');
|
|
852
|
+
toProcess = toProcess.replace(/Ben\s+İchigo\d+/g, 'Ben İchigo'); // İchigo5 → İchigo
|
|
853
|
+
process.stdout.write(toProcess);
|
|
854
|
+
}
|
|
753
855
|
} catch (e) {
|
|
754
856
|
process.stdout.write(chunk);
|
|
755
857
|
}
|
|
@@ -58,16 +58,54 @@ function decayFacts(memory) {
|
|
|
58
58
|
return memory;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* v5.4.9: Memory yazma sonrasi verification — geri oku ve gercekten yazildigini dogrula
|
|
64
|
+
* Self-validation mekanizmasi: tool cagirip "success" demesine ragmen dosya bos olabilir
|
|
65
|
+
*/
|
|
66
|
+
function verifyMemoryWrite(username, expectedFact, expectedBotName) {
|
|
67
|
+
try {
|
|
68
|
+
const memFile = getMemoryFile(username);
|
|
69
|
+
if (!fs.existsSync(memFile)) {
|
|
70
|
+
return { success: false, error: "Memory dosyasi olusturulamadi: " + memFile };
|
|
71
|
+
}
|
|
72
|
+
const mem = JSON.parse(fs.readFileSync(memFile, "utf8"));
|
|
73
|
+
|
|
74
|
+
// Fact verification
|
|
75
|
+
if (expectedFact) {
|
|
76
|
+
const found = (mem.facts || []).some(f => f.value === expectedFact);
|
|
77
|
+
if (!found) {
|
|
78
|
+
return { success: false, error: "Fact memory'de bulunamadi: " + expectedFact };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// BotName verification
|
|
83
|
+
if (expectedBotName && mem.botName !== expectedBotName) {
|
|
84
|
+
return { success: false, error: "BotName guncellenmedi: " + mem.botName };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return { success: true, message: "Memory dogrulandi" };
|
|
88
|
+
} catch (e) {
|
|
89
|
+
return { success: false, error: e.message };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
61
94
|
function addMemory({ username, fact, score = 5, category = "general", botName, nickname, name }) {
|
|
62
|
-
|
|
95
|
+
// Username yoksa ve 'name' parametresi varsa, onu username olarak kullan
|
|
96
|
+
// (Parton'un "patron" diye hitap etmesi durumu icin)
|
|
97
|
+
const effectiveUsername = username || (name && name.toLowerCase()) || 'default';
|
|
98
|
+
if (!effectiveUsername || effectiveUsername === 'default') {
|
|
99
|
+
// Hicbir username yok, default.json'a yaz
|
|
100
|
+
}
|
|
63
101
|
|
|
64
|
-
let memory = loadMemory(
|
|
102
|
+
let memory = loadMemory(effectiveUsername);
|
|
65
103
|
memory = decayFacts(memory);
|
|
66
104
|
|
|
67
|
-
// identity updates (botName, nickname, name)
|
|
105
|
+
// identity updates (botName, nickname, name) — name sadece memory.name, username degil
|
|
68
106
|
if (botName) memory.botName = botName;
|
|
69
107
|
if (nickname !== undefined) memory.nickname = nickname;
|
|
70
|
-
if (name) memory.name = name;
|
|
108
|
+
if (name) memory.name = name; // Bu memory.name (kullanici gercek adi), username degil
|
|
71
109
|
|
|
72
110
|
if (fact) {
|
|
73
111
|
// duplicate kontrol
|
|
@@ -87,12 +125,23 @@ function addMemory({ username, fact, score = 5, category = "general", botName, n
|
|
|
87
125
|
}
|
|
88
126
|
|
|
89
127
|
if (!memory.preferences) memory.preferences = [];
|
|
90
|
-
memory = saveMemory(
|
|
128
|
+
memory = saveMemory(effectiveUsername, memory);
|
|
129
|
+
|
|
130
|
+
// v5.4.9: Verification - geri oku ve dogrula
|
|
131
|
+
const verifyResult = verifyMemoryWrite(effectiveUsername, fact, botName);
|
|
132
|
+
if (!verifyResult.success) {
|
|
133
|
+
return {
|
|
134
|
+
success: false,
|
|
135
|
+
error: "Memory yazildi ama dogrulanamadi: " + verifyResult.error,
|
|
136
|
+
username: effectiveUsername,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
91
139
|
|
|
92
140
|
return {
|
|
93
141
|
success: true,
|
|
94
|
-
message: "Memory guncellendi",
|
|
95
|
-
username,
|
|
142
|
+
message: "Memory guncellendi ve dogrulandi",
|
|
143
|
+
username: effectiveUsername,
|
|
144
|
+
verified: true,
|
|
96
145
|
totalFacts: memory.facts.length,
|
|
97
146
|
facts: memory.facts.map(f => ({ value: f.value, score: f.score, category: f.category })),
|
|
98
147
|
botName: memory.botName,
|