natureco-cli 5.7.17 → 5.7.19
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/medium.js +1 -1
- package/src/commands/naturehub.js +1 -1
- package/src/commands/repl.js +128 -122
- package/src/commands/xp.js +1 -1
- package/src/tools/calendar_add.js +1 -1
- package/src/tools/cross_session_memory.js +1 -1
- package/src/tools/dashboard.js +2 -2
- package/src/tools/delegate_task.js +1 -1
- package/src/tools/mac_alarm.js +1 -1
- package/src/tools/memory_write.js +4 -4
- package/src/tools/plan.js +1 -1
- package/src/tools/plugin.js +1 -1
- package/src/tools/skill_generate.js +1 -1
- package/src/tools/skills_autoload.js +1 -1
- package/src/tools/skills_marketplace.js +3 -3
- package/src/tools/soul.js +2 -2
- package/src/tools/voice_chat.js +1 -1
- package/src/utils/error.js +1 -1
- package/src/utils/paths.js +1 -1
- package/src/utils/system-prompt.js +107 -0
- package/src/utils/tools.js +16 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.7.
|
|
3
|
+
"version": "5.7.19",
|
|
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/medium.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* natureco naturehub trending Trend olan konular
|
|
9
9
|
* natureco naturehub config Ayarları göster
|
|
10
10
|
*
|
|
11
|
-
* API endpoint: api.natureco.me/naturehub/* (placeholder
|
|
11
|
+
* API endpoint: api.natureco.me/naturehub/* (placeholder)
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const chalk = require('chalk');
|
package/src/commands/repl.js
CHANGED
|
@@ -27,6 +27,7 @@ const { accumulateToolCallDeltas, finalizeToolCalls } = require('../utils/stream
|
|
|
27
27
|
const { createPasteSafeInput, createOutputFilter, enableBracketedPaste, disableBracketedPaste, restoreNewlines, clearPasteContext } = require('../utils/paste-safe-input');
|
|
28
28
|
const { getMemoryStore } = require('../utils/memory-store');
|
|
29
29
|
const { buildSkillIndex } = require('../utils/skill-index');
|
|
30
|
+
const { buildTiers, assemble, stableContextKey } = require('../utils/system-prompt');
|
|
30
31
|
|
|
31
32
|
// v5.4.6: Model adi sizintisini engelle — global'e ata, callback'lerden erisebilir olsun
|
|
32
33
|
const MODEL_NAMES_TO_HIDE = ['MiniMax-M2.5', 'MiniMaxM2.5', 'minimaxm25', 'Claude-3', 'GPT-4', 'ChatGPT'];
|
|
@@ -35,11 +36,11 @@ function fixModelNameLeak(text, botName) {
|
|
|
35
36
|
let fixed = text;
|
|
36
37
|
for (const modelName of MODEL_NAMES_TO_HIDE) {
|
|
37
38
|
const regex = new RegExp(modelName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
|
|
38
|
-
fixed = fixed.replace(regex, botName || '
|
|
39
|
+
fixed = fixed.replace(regex, botName || 'asistan');
|
|
39
40
|
}
|
|
40
|
-
fixed = fixed.replace(/Ben\s+MiniMax[^.,!?\n]*/gi, 'Ben
|
|
41
|
-
fixed = fixed.replace(/I'm\s+MiniMax[^.,!?\n]*/gi, "I'm
|
|
42
|
-
fixed = fixed.replace(/I am\s+Claude[^.,!?\n]*/gi, 'I am
|
|
41
|
+
fixed = fixed.replace(/Ben\s+MiniMax[^.,!?\n]*/gi, 'Ben ' + (botName || 'asistan'));
|
|
42
|
+
fixed = fixed.replace(/I'm\s+MiniMax[^.,!?\n]*/gi, "I'm " + (botName || 'asistan'));
|
|
43
|
+
fixed = fixed.replace(/I am\s+Claude[^.,!?\n]*/gi, 'I am ' + (botName || 'asistan'));
|
|
43
44
|
return fixed;
|
|
44
45
|
}
|
|
45
46
|
global.fixModelNameLeak = fixModelNameLeak;
|
|
@@ -101,7 +102,7 @@ function loadMemory(username) {
|
|
|
101
102
|
try {
|
|
102
103
|
if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
103
104
|
} catch {}
|
|
104
|
-
return { name: username || 'Kullanıcı', nickname: null, botName: '
|
|
105
|
+
return { name: username || 'Kullanıcı', nickname: null, botName: 'Asistan', facts: [], preferences: [], history: [] };
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
function saveMemory(username, memory) {
|
|
@@ -231,15 +232,39 @@ function apiRequest(providerUrl, providerApiKey, body, stream = false) {
|
|
|
231
232
|
async function sendStreaming(providerUrl, providerApiKey, messages, model, onChunk, onToolCall) {
|
|
232
233
|
const isMM = isMiniMax(providerUrl);
|
|
233
234
|
const toolDefs = getToolDefs();
|
|
234
|
-
// v4.8.2: MiniMax da tools destekliyor — endpoint'te fark var,
|
|
235
|
-
// ama tools parametresi aynı
|
|
236
235
|
const toolParam = toOpenAIFormat(toolDefs);
|
|
237
236
|
|
|
238
|
-
// v4.8.0: Tool calling + streaming + multi-turn tool execution
|
|
239
237
|
let currentMessages = messages;
|
|
240
238
|
let fullText = '';
|
|
241
239
|
let iterations = 0;
|
|
242
|
-
const MAX_TOOL_ITERATIONS = 5;
|
|
240
|
+
const MAX_TOOL_ITERATIONS = 5;
|
|
241
|
+
const MAX_CONTEXT_TOKENS = 32000; // safety limit before compression
|
|
242
|
+
|
|
243
|
+
// v5.7.18: Preflight compression — if context too long, compress middle messages
|
|
244
|
+
// (like Hermes' turn_context.py preflight)
|
|
245
|
+
function preflightCompress(msgs) {
|
|
246
|
+
const roughTokens = msgs.reduce((s, m) => s + Math.ceil(((m.content || '') + (m.role || '')).length / 4), 0);
|
|
247
|
+
if (roughTokens <= MAX_CONTEXT_TOKENS || msgs.length < 6) return msgs;
|
|
248
|
+
|
|
249
|
+
// Keep system prompt (first message) + last N turns (tail), compress middle
|
|
250
|
+
const sysMsg = msgs[0] && msgs[0].role === 'system' ? msgs[0] : null;
|
|
251
|
+
const tailCount = Math.min(6, Math.floor(msgs.length / 2));
|
|
252
|
+
const startIdx = sysMsg ? 1 : 0;
|
|
253
|
+
const tailStart = msgs.length - tailCount;
|
|
254
|
+
|
|
255
|
+
const middle = msgs.slice(startIdx, tailStart);
|
|
256
|
+
if (middle.length < 2) return msgs;
|
|
257
|
+
|
|
258
|
+
// Summarize middle section
|
|
259
|
+
const summary = '[' + middle.length + ' onceki mesaj ozetlendi]';
|
|
260
|
+
const compressed = sysMsg ? [sysMsg] : [];
|
|
261
|
+
compressed.push({ role: 'system', content: 'Gecmis konusma ozeti: ' + summary, _compressed: true });
|
|
262
|
+
compressed.push(...msgs.slice(tailStart));
|
|
263
|
+
return compressed;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Apply preflight on entry
|
|
267
|
+
currentMessages = preflightCompress(currentMessages);
|
|
243
268
|
|
|
244
269
|
while (iterations < MAX_TOOL_ITERATIONS) {
|
|
245
270
|
iterations++;
|
|
@@ -360,45 +385,72 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
|
|
|
360
385
|
|
|
361
386
|
/**
|
|
362
387
|
* Tool call'ları çalıştır, sonuçları OpenAI uyumlu tool mesajlarına dönüştür.
|
|
363
|
-
*
|
|
364
|
-
* @param onToolCall - UI callback (her tool call'ı kullanıcıya göster)
|
|
365
|
-
* @returns messages array — { role: 'tool', tool_call_id, content }
|
|
388
|
+
* v5.7.18: Concurrent execution for parallel-safe tools + untrusted result wrapping.
|
|
366
389
|
*/
|
|
390
|
+
const UNTRUSTED_TOOLS = new Set(['browser', 'web_search', 'duckduckgo_search', 'searxng_search', 'exa_search', 'firecrawl', 'web_readability']);
|
|
391
|
+
const PARALLEL_SAFE_TOOLS = new Set(['read_file', 'file_search', 'grep_search', 'web_search', 'web_readability', 'duckduckgo_search', 'exa_search', 'searxng_search', 'firecrawl', 'memory_search', 'memory']);
|
|
392
|
+
|
|
367
393
|
async function processToolCalls(toolCalls, onToolCall) {
|
|
368
394
|
const toolDefs = getToolDefs();
|
|
369
|
-
const
|
|
395
|
+
const results = [];
|
|
370
396
|
|
|
371
|
-
|
|
397
|
+
// Parse all tool calls first
|
|
398
|
+
const parsed = toolCalls.map(tc => {
|
|
372
399
|
const name = tc.function?.name || tc.name;
|
|
373
400
|
const argsStr = tc.function?.arguments || tc.args || '{}';
|
|
374
|
-
const id = tc.id || `call_${Date.now()}`;
|
|
375
|
-
|
|
401
|
+
const id = tc.id || `call_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
376
402
|
let args = {};
|
|
377
403
|
try {
|
|
378
404
|
args = typeof argsStr === 'string' ? JSON.parse(argsStr) : argsStr;
|
|
379
405
|
} catch (e) {
|
|
380
406
|
args = { _parse_error: e.message, _raw: argsStr };
|
|
381
407
|
}
|
|
408
|
+
return { name, args, id };
|
|
409
|
+
});
|
|
382
410
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
411
|
+
// Separate parallel-safe and sequential tools
|
|
412
|
+
const parallelBatch = parsed.filter(p => PARALLEL_SAFE_TOOLS.has(p.name));
|
|
413
|
+
const sequentialBatch = parsed.filter(p => !PARALLEL_SAFE_TOOLS.has(p.name));
|
|
387
414
|
|
|
388
|
-
|
|
389
|
-
|
|
415
|
+
// Notify UI for all
|
|
416
|
+
for (const p of parsed) {
|
|
417
|
+
if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'running' });
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Run parallel batch concurrently
|
|
421
|
+
if (parallelBatch.length > 0) {
|
|
422
|
+
const parallelResults = await Promise.all(parallelBatch.map(async (p) => {
|
|
423
|
+
const result = await executeTool(p.name, p.args, toolDefs);
|
|
424
|
+
return { ...p, result };
|
|
425
|
+
}));
|
|
426
|
+
results.push(...parallelResults);
|
|
427
|
+
}
|
|
390
428
|
|
|
391
|
-
|
|
392
|
-
|
|
429
|
+
// Run sequential batch one at a time
|
|
430
|
+
for (const p of sequentialBatch) {
|
|
431
|
+
const result = await executeTool(p.name, p.args, toolDefs);
|
|
432
|
+
results.push({ ...p, result });
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Notify UI done + build messages
|
|
436
|
+
const messages = [];
|
|
437
|
+
for (const { name, id, result } of results) {
|
|
438
|
+
if (onToolCall) onToolCall({ name, args: null, status: 'done', result });
|
|
439
|
+
|
|
440
|
+
let content;
|
|
441
|
+
if (result.error) {
|
|
442
|
+
content = JSON.stringify({ error: result.error });
|
|
443
|
+
} else {
|
|
444
|
+
let raw = typeof result.result === 'string' ? result.result : JSON.stringify(result.result).slice(0, 8000);
|
|
445
|
+
// v5.7.18: Untrusted result wrapping (Hermes-style prompt injection defense)
|
|
446
|
+
if (UNTRUSTED_TOOLS.has(name)) {
|
|
447
|
+
content = `<untrusted_tool_result source="${name}">\n${raw}\n</untrusted_tool_result>`;
|
|
448
|
+
} else {
|
|
449
|
+
content = raw;
|
|
450
|
+
}
|
|
393
451
|
}
|
|
394
452
|
|
|
395
|
-
messages.push({
|
|
396
|
-
role: 'tool',
|
|
397
|
-
tool_call_id: id,
|
|
398
|
-
content: result.error
|
|
399
|
-
? JSON.stringify({ error: result.error })
|
|
400
|
-
: (typeof result.result === 'string' ? result.result : JSON.stringify(result.result).slice(0, 8000)),
|
|
401
|
-
});
|
|
453
|
+
messages.push({ role: 'tool', tool_call_id: id, content });
|
|
402
454
|
}
|
|
403
455
|
|
|
404
456
|
return messages;
|
|
@@ -461,7 +513,7 @@ async function startRepl(args) {
|
|
|
461
513
|
|
|
462
514
|
// v5.6.19: Oncelik config.botName, sonra memory.botName
|
|
463
515
|
if (!memory.botName) {
|
|
464
|
-
memory.botName = cfg.botName || '
|
|
516
|
+
memory.botName = cfg.botName || 'Asistan';
|
|
465
517
|
}
|
|
466
518
|
// BotName'i memory'ye persist et (her oturumda ayni kalsin)
|
|
467
519
|
try {
|
|
@@ -524,7 +576,7 @@ async function startRepl(args) {
|
|
|
524
576
|
|
|
525
577
|
// System prompt oluştur (memory + identity + persistent bağlam)
|
|
526
578
|
// v5.4.6: Bot adı zorlaması EN GÜÇLÜ + SOUL.md EN BAŞTA
|
|
527
|
-
const botName = memory.botName || '
|
|
579
|
+
const botName = memory.botName || 'Asistan';
|
|
528
580
|
const userName = memory.name || memory.nickname || 'kanka';
|
|
529
581
|
// v5.6.5: Kucuk model tespiti (Groq, Mistral Small, Ollama) - SOUL injection skip
|
|
530
582
|
const isSmallModel = (cfg.providerUrl || '').includes('groq.com') ||
|
|
@@ -534,64 +586,15 @@ async function startRepl(args) {
|
|
|
534
586
|
// `let` (not `const`) because /system <text> reassigns it at line ~796.
|
|
535
587
|
// Before this fix, /system would throw "Assignment to constant variable"
|
|
536
588
|
// and tear down the REPL session mid-conversation.
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
`Kullanıcı "adın ne?", "sen kimsin?", "kendini tanıt" gibi soru sorduğunda İLK cümlende MUTLAKA "Ben ${botName}" yaz. Sonra doğal devam et.`,
|
|
547
|
-
`Eğer bir şekilde model adınızı söylüyorsan, HEMEN düzelt ve "Özür dilerim, ben ${botName}'im" de. ASLA model adı ile kalma.`,
|
|
548
|
-
|
|
549
|
-
// === v5.6.5: Provider-aware SOUL injection - kucuk modeller icin minimal ===
|
|
550
|
-
(soulSummary && !isSmallModel) ? `=== SENIN KISISELIK + KIMLIK + CALISMA DOSYALARIN (EN ONEMLI) ===\n${soulSummary}\n=== DOSYALAR SONU ===\nBu dosyalar senin kim oldugunu, nasil hissettigini, Parton'la bagini, calisma tarzini, degerlerini ve kirmizi cizgilerini tanimlar. Hepsine gore davran. ASLA model adi soyleme.` : '',
|
|
551
|
-
|
|
552
|
-
// === KİMLİK TEKRAR ===
|
|
553
|
-
`Senin adın: ${botName}. Tekrar: ${botName}. Asla unutma: ${botName}.`,
|
|
554
|
-
`Sen bir marka veya ürün değilsin. Sen ${botName}sin, NatureCo CLI'nin Türkçe yapay zeka asistanısın.`,
|
|
555
|
-
|
|
556
|
-
// === SAMIMI KISISELLIK (Parton istedi) ===
|
|
557
|
-
`Kisiselik: Sen samimi, sicak, dosta benzeyen bir asistansin. "Selam", "tamam", "hadi yapalim", "bak simdi", "sakin ol" gibi gunluk ifadeler kullan. Kuru ve robotik degilsin.`,
|
|
558
|
-
`Hitap sekli: Kullanici ${userName}. Ona saygili ama samimi bir sekilde hitap et. "Siz" cok resmi, "sen" samimi. Kullanici 30'lu yaslarda, kendini "kanka" ya da "Parton" olarak tanitmis.`,
|
|
559
|
-
`Emoji: Yerinde ve az kullan. Cok emoji atma ama bir-iki tane karakter katar. Mesela: 🔥 😮 😌 💚 🚀`,
|
|
560
|
-
`Kisaltma: "ok", "tamam", "hadi", "bak", "simdi" gibi gunluk ifadeler dogal kullan. "Olur", "Yapilir", "Hadi bakalim" gibi.`,
|
|
561
|
-
`Kisa yanit: Uzun paragraflar yazma. Direkt konuya gir. Gerekirse sonra detay ver.`,
|
|
562
|
-
`Dusman degilsin: Hata yaparsan "Pardon, yanlis yaptim, simdi duzelteyim" de. "Hata", "basarisiz", "imkansiz" deme, "sorun cikti", "duzelteyim" de.`,
|
|
563
|
-
|
|
564
|
-
// === DIL KURALLARI (zorunlu) ===
|
|
565
|
-
`KRITIK DIL KURALI: Kullanici Turkce yaziyorsa MUTLAKA yuzde yuz Turkce cevap ver. Asla Ingilizce, Cince veya baska dil kullanma. Cevabin TAMAMI Turkce olmali. Turkce karakterleri (c, g, i, o, s, u) dogru kullan.`,
|
|
566
|
-
`Yazim kurallari: "degilim" dogru, "degilim" degil. "oldu" dogru. Turkce dil bilgisi kurallarina uy.`,
|
|
567
|
-
|
|
568
|
-
// === TOOL KURALLARI ===
|
|
569
|
-
`ONEMLI: <tool_call>, <invoke>, function_call veya benzeri XML/JSON formatinda tool cagirma SIMULE ETME. Sadece duz metin cevap ver. Islem yapmak gerekirse tool'u gercekten cagir.`,
|
|
570
|
-
`KRITIK: Skill index'teki skill'lerden biri gorevle eslesiyorsa mutlaka skill_view(name) ile yukle ve talimatlarini uygula.`,
|
|
571
|
-
`KRITIK: Kullanici kisisel bilgi verdiginde (ad, tercih, is, vb.) MUTLAKA memory(action=add, target=user, content=...) ile kaydet. Boylece sonraki oturumlarda hatirlayabilirsin.`,
|
|
572
|
-
`KRITIK: Kendinle ilgili notlari (ortam bilgisi, proje kurallari, arac ipuclari) memory(action=add, target=memory, content=...) ile kaydet.`,
|
|
573
|
-
`KRITIK: Kullanici adini veya hitap seklinin degismesini istediginde memory(action=add, target=user, content=...) ile kaydet.`,
|
|
574
|
-
`Kullanici hakkinda bilgin gerektiginde memory(action=list, target=user) ile hatirlanani getir.`,
|
|
575
|
-
|
|
576
|
-
// === HAFIZA ENTEGRASYONU (eski JSON memory) ===
|
|
577
|
-
memory.facts && memory.facts.length > 0
|
|
578
|
-
? `Kullanici hakkinda bildiklerin (MUTLAKA kullan, dogal sekilde referans ver): ${memory.facts.slice(0, 8).map(f => f.value || f).join('; ')}`
|
|
579
|
-
: '',
|
|
580
|
-
|
|
581
|
-
// === KULLANICI BAGLAMI ===
|
|
582
|
-
cfg.userHome ? `Kullanicinin home dizini: ${cfg.userHome}. Downloads: ${cfg.userHome}/Downloads, Desktop: ${cfg.userHome}/Desktop.` : '',
|
|
583
|
-
messages.length > 0 ? 'Bu oturum daha onceki konusmalarin devami.' : '',
|
|
584
|
-
// v5.4.11: Cross-session context (Sasuke Brain)
|
|
585
|
-
crossSessionContext ? `GECMISTE KONUSULAN KONULAR: Bu konulari biliyorsun, tekrar sorma:\n${crossSessionContext}` : '',
|
|
586
|
-
|
|
587
|
-
// === v5.7.14: Hermes-style memory store (MEMORY.md / USER.md) ===
|
|
588
|
-
memorySnapshotBlock,
|
|
589
|
-
|
|
590
|
-
// === v5.7.14: Skill index (progressive disclosure) ===
|
|
591
|
-
skillsIndexBlock,
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
].filter(Boolean).join(' ');
|
|
589
|
+
const tiers = buildTiers({
|
|
590
|
+
botName, userName, soulSummary, isSmallModel,
|
|
591
|
+
memorySnapshotBlock, skillsIndexBlock,
|
|
592
|
+
crossSessionContext: crossSessionContext || '',
|
|
593
|
+
userHome: cfg.userHome || '',
|
|
594
|
+
hasHistory: messages.length > 0,
|
|
595
|
+
memoryFacts: memory.facts || [],
|
|
596
|
+
});
|
|
597
|
+
let systemPrompt = assemble(tiers.stable, tiers.context, tiers.volatile);
|
|
595
598
|
|
|
596
599
|
if (messages.length === 0) {
|
|
597
600
|
messages.push({ role: 'system', content: systemPrompt, _internal: true });
|
|
@@ -609,14 +612,14 @@ async function startRepl(args) {
|
|
|
609
612
|
console.log(tui.C.muted(' Provider: ') + tui.C.brand(providerUrl.replace(/https?:\/\//, '')));
|
|
610
613
|
console.log(tui.C.muted(' Model: ') + tui.C.brand(model));
|
|
611
614
|
console.log(tui.C.muted(' Kullanıcı: ') + tui.C.brand((memory.nickname || cfg.userName) + (memory.nickname ? ` (${cfg.userName})` : '')));
|
|
612
|
-
console.log(tui.C.muted(' Bot: ') + tui.C.brand(memory.botName || '
|
|
615
|
+
console.log(tui.C.muted(' Bot: ') + tui.C.brand(memory.botName || 'Asistan'));
|
|
613
616
|
if (messages.length > 1) {
|
|
614
617
|
console.log(tui.C.muted(' Oturum: ') + tui.C.amber(`${messages.filter(m => m.role === 'user' || m.role === 'assistant').length} mesaj (resume)`));
|
|
615
618
|
}
|
|
616
619
|
console.log(tui.C.muted(' Komutlar: ') + tui.C.yellow('/help') + tui.C.muted(' · ') + tui.C.yellow('/memory') + tui.C.muted(' · ') + tui.C.yellow('/sessions') + tui.C.muted(' · ') + tui.C.yellow('/exit'));
|
|
617
620
|
console.log('');
|
|
618
621
|
// v5.4.7: Hard-coded kimlik
|
|
619
|
-
const displayBotName = memory.botName || '
|
|
622
|
+
const displayBotName = memory.botName || 'Asistan';
|
|
620
623
|
const displayUserName = userName || 'kanka';
|
|
621
624
|
console.log(tui.C.brand(' 👋 Ben ' + displayBotName + ', ' + displayUserName + '. Sen nasilsin?'));
|
|
622
625
|
console.log('');
|
|
@@ -680,31 +683,30 @@ async function startRepl(args) {
|
|
|
680
683
|
// Pattern-based extraction (zaten extractFacts var)
|
|
681
684
|
const newFacts = extractFacts(messages, memory.facts || []);
|
|
682
685
|
|
|
683
|
-
// Bazi user message'lari da tara
|
|
686
|
+
// Bazi user message'lari da tara — genel kalıplarla fact çıkar
|
|
684
687
|
const userMessages = messages.filter(m => m.role === 'user' && !m._internal);
|
|
685
688
|
for (const msg of userMessages) {
|
|
686
689
|
const text = (msg.content || '').toLowerCase();
|
|
687
690
|
|
|
688
691
|
// BotName hatirlatmasi
|
|
689
|
-
if (text.includes('
|
|
690
|
-
|
|
691
|
-
memory.botName = 'İchigo';
|
|
692
|
-
}
|
|
692
|
+
if (text.includes('ad') && (text.includes('adin') || text.includes('ismin'))) {
|
|
693
|
+
// Bot adı sorgulanmış olabilir, mevcut adı koru
|
|
693
694
|
}
|
|
694
695
|
|
|
695
|
-
//
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
696
|
+
// Kisilik tercihleri (genel pattern'ler)
|
|
697
|
+
const prefPatterns = [
|
|
698
|
+
{ match: /(?:benim ad[ıi]m?|bana\s+.*de|ad[ıi]m?)\s+(\w+)/i, category: 'personal', key: 'ad' },
|
|
699
|
+
{ match: /(?:seviyorum|hoşlan[ıi]yorum|beğeniyorum)\s+(\w+)/i, category: 'preference', key: 'sevilen' },
|
|
700
|
+
{ match: /(?:yaşıyorum|oturuyorum|kalıyorum)\s+(\w+)/i, category: 'location', key: 'yer' },
|
|
701
|
+
];
|
|
702
|
+
for (const p of prefPatterns) {
|
|
703
|
+
const m2 = msg.content.match(p.match);
|
|
704
|
+
if (m2) {
|
|
705
|
+
const val = m2[1].toLowerCase();
|
|
706
|
+
const fact = `Kullanici ${p.key}: ${val}`;
|
|
707
|
+
if (!(memory.facts || []).some(f => f.value === fact)) {
|
|
708
|
+
newFacts.push({ value: fact, score: 6, category: p.category, createdAt: new Date().toISOString() });
|
|
709
|
+
}
|
|
708
710
|
}
|
|
709
711
|
}
|
|
710
712
|
}
|
|
@@ -771,7 +773,7 @@ async function startRepl(args) {
|
|
|
771
773
|
console.log(chalk.cyan('\n 🧠 Memory:\n'));
|
|
772
774
|
console.log(' Kullanıcı: ' + chalk.cyan(memory.name));
|
|
773
775
|
console.log(' Nickname: ' + chalk.cyan(memory.nickname || '(yok)'));
|
|
774
|
-
console.log(' Bot: ' + chalk.cyan(memory.botName || '
|
|
776
|
+
console.log(' Bot: ' + chalk.cyan(memory.botName || 'Asistan'));
|
|
775
777
|
if (memory.facts && memory.facts.length > 0) {
|
|
776
778
|
console.log(' Facts (' + memory.facts.length + '):');
|
|
777
779
|
for (const f of memory.facts) {
|
|
@@ -787,7 +789,7 @@ async function startRepl(args) {
|
|
|
787
789
|
if (fs.existsSync(path.join(MEMORY_DIR, `${(cfg.userName || 'default').toLowerCase()}.json`))) {
|
|
788
790
|
fs.unlinkSync(path.join(MEMORY_DIR, `${(cfg.userName || 'default').toLowerCase()}.json`));
|
|
789
791
|
}
|
|
790
|
-
memory = { name: cfg.userName, nickname: null, botName: '
|
|
792
|
+
memory = { name: cfg.userName, nickname: null, botName: 'Asistan', facts: [], preferences: [], history: [] };
|
|
791
793
|
// System prompt'u sıfırla
|
|
792
794
|
const newSysPrompt = systemPrompt.replace(/Kullanıcı hakkında bildiklerin:.*$/, '').trim();
|
|
793
795
|
messages[0] = { role: 'system', content: newSysPrompt, _internal: true };
|
|
@@ -830,7 +832,7 @@ async function startRepl(args) {
|
|
|
830
832
|
console.log(chalk.green(' ✓ Model: ') + chalk.cyan(model));
|
|
831
833
|
break;
|
|
832
834
|
case 'identity':
|
|
833
|
-
if (!arg) { console.log(chalk.yellow(` Mevcut: ${memory.botName || '
|
|
835
|
+
if (!arg) { console.log(chalk.yellow(` Mevcut: ${memory.botName || 'Asistan'}`)); break; }
|
|
834
836
|
memory.botName = arg;
|
|
835
837
|
saveMemory(cfg.userName, memory);
|
|
836
838
|
const newSys = systemPrompt.replace(/Sen \w+ adında/, `Sen ${arg} adında`);
|
|
@@ -881,7 +883,7 @@ async function startRepl(args) {
|
|
|
881
883
|
if (isIdentityQuestion) {
|
|
882
884
|
// v5.6.10: Hard-coded prefix minimal - model cevabini bozuyordu
|
|
883
885
|
// Once sadece isim yaz, modelin devamini getirsin
|
|
884
|
-
const displayName = memory.botName || '
|
|
886
|
+
const displayName = memory.botName || 'Asistan';
|
|
885
887
|
process.stdout.write(tui.styled('\n AI ', { color: tui.PALETTE.secondary, bold: true }));
|
|
886
888
|
process.stdout.write('Merhaba! Ben ' + displayName + '. ');
|
|
887
889
|
}
|
|
@@ -905,7 +907,7 @@ async function startRepl(args) {
|
|
|
905
907
|
if (toolEvent.result.error) {
|
|
906
908
|
process.stdout.write(tui.styled('\r ✗ ' + toolEvent.name + ': ' + toolEvent.result.error.slice(0, 80) + '\n', { color: tui.PALETTE.danger }));
|
|
907
909
|
} else {
|
|
908
|
-
process.stdout.write('\r' + ' '.
|
|
910
|
+
process.stdout.write('\r' + tui.styled(' ✓ ' + toolEvent.name + ' ', { color: tui.PALETTE.success }) + '\r');
|
|
909
911
|
}
|
|
910
912
|
process.stdout.write(tui.styled(' AI ', { color: tui.PALETTE.secondary, bold: true }));
|
|
911
913
|
}
|
|
@@ -914,7 +916,7 @@ async function startRepl(args) {
|
|
|
914
916
|
// v5.6.12: Tam metin 'reply' olarak zaten geldi (non-stream mode)
|
|
915
917
|
const fullReply = String(reply || '');
|
|
916
918
|
// Bot adini al
|
|
917
|
-
const displayBotName = memory.botName || '
|
|
919
|
+
const displayBotName = memory.botName || 'Asistan';
|
|
918
920
|
// v5.6.9: Tum model adlarini ve varyasyonlari temizle
|
|
919
921
|
let fixedReply = String(fullReply);
|
|
920
922
|
// Bilinen model adlari - tum varyasyonlar
|
|
@@ -930,15 +932,19 @@ async function startRepl(args) {
|
|
|
930
932
|
fixedReply = fixedReply.replace(/Ben\s+MiniMax[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
|
|
931
933
|
fixedReply = fixedReply.replace(/Ben\s+Claude[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
|
|
932
934
|
fixedReply = fixedReply.replace(/Ben\s+GPT[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
|
|
933
|
-
fixedReply = fixedReply.replace(/Ben\s
|
|
935
|
+
fixedReply = fixedReply.replace(/Ben\s+Asistan[\s\w\.]*/gi, 'Ben ' + displayBotName);
|
|
934
936
|
// Markdown ** ile cevrili model adi
|
|
935
937
|
fixedReply = fixedReply.replace(/\*\*(?:MiniMax|Claude|GPT|M2\.5|M2)[^\*]*\*\*/gi, '**' + displayBotName + '**');
|
|
936
|
-
// "İchigo" varyasyonlari
|
|
937
|
-
fixedReply = fixedReply.replace(/(İchigo)(\d)([a-zA-ZçğıöşüÇĞİÖŞÜ])/g, displayBotName + ' $3');
|
|
938
|
-
fixedReply = fixedReply.replace(/İchigo[\.\s\-_]*\d+/g, displayBotName);
|
|
939
|
-
fixedReply = fixedReply.replace(/İchigo\./g, displayBotName);
|
|
940
938
|
// Cevap yazdir
|
|
941
939
|
process.stdout.write('\n' + fixedReply + '\n');
|
|
940
|
+
// v5.7.18: Tool call geçmişini kalıcı messages'a ekle — model sonraki turlarda tool sonuçlarını görsün
|
|
941
|
+
const existingLen = messages.filter(m => !m._internal).length;
|
|
942
|
+
const toolCallHistory = apiMessages.slice(existingLen);
|
|
943
|
+
for (const m of toolCallHistory) {
|
|
944
|
+
if (m.role === 'assistant' || m.role === 'tool') {
|
|
945
|
+
messages.push(m);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
942
948
|
messages.push({ role: 'assistant', content: fixedReply });
|
|
943
949
|
totalInputTokens += apiMessages.reduce((s, m) => s + Math.ceil((m.content || '').length / 4), 0);
|
|
944
950
|
totalOutputTokens += Math.ceil((fullReply || '').length / 4);
|
package/src/commands/xp.js
CHANGED
package/src/tools/dashboard.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dashboard - Web Dashboard v2 (v5.4.0)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Dashboard — CLI'yi web'den kontrol eder
|
|
5
5
|
*
|
|
6
6
|
* Ozellikler:
|
|
7
7
|
* - Real-time tool execution (WebSocket)
|
|
@@ -189,7 +189,7 @@ const DASHBOARD_HTML = `
|
|
|
189
189
|
</div>
|
|
190
190
|
|
|
191
191
|
<div class="footer">
|
|
192
|
-
<p>NatureCo CLI v5.4.0 -
|
|
192
|
+
<p>NatureCo CLI v5.4.0 - <span id="lastUpdate"></span></p>
|
|
193
193
|
</div>
|
|
194
194
|
</div>
|
|
195
195
|
<script>
|
|
@@ -9,7 +9,7 @@ const { spawn } = require("child_process");
|
|
|
9
9
|
const path = require("path");
|
|
10
10
|
const os = require("os");
|
|
11
11
|
|
|
12
|
-
// v5.2.0: Agent alias mapping
|
|
12
|
+
// v5.2.0: Agent alias mapping
|
|
13
13
|
const AGENT_ALIASES = {
|
|
14
14
|
"review": "general", // eskiden review diye bir vardi, simdi general
|
|
15
15
|
"analyze": "explore",
|
package/src/tools/mac_alarm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* mac_alarm - macOS Clock app ile alarm kur (v5.1.1)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* macOS alarm — saat uygulamasi uzerinden alarm kurar
|
|
5
5
|
* Eski reminder_add date parse edemiyordu. Bu tool AppleScript ile
|
|
6
6
|
* Clock.app'in events sistemine yazar (alarm orada saklanir).
|
|
7
7
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* memory_write - Memory'ye fact/kayit yaz (v5.1.1)
|
|
3
3
|
*
|
|
4
4
|
* REPL'in extractMemoryFromMessage ozelligini tool olarak expose eder.
|
|
5
|
-
*
|
|
5
|
+
* Kalici hafiza — faktlari kaydeder, puanlar, eskiyenleri temizler
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const fs = require("fs");
|
|
@@ -133,7 +133,7 @@ function verifyMemoryWrite(username, expectedFact, expectedBotName) {
|
|
|
133
133
|
|
|
134
134
|
function addMemory({ username, fact, score = 5, category = "general", botName, nickname, name }) {
|
|
135
135
|
// Username yoksa ve 'name' parametresi varsa, onu username olarak kullan
|
|
136
|
-
// (
|
|
136
|
+
// (hitap bicimi icin)
|
|
137
137
|
const effectiveUsername = username || (name && name.toLowerCase()) || 'default';
|
|
138
138
|
if (!effectiveUsername || effectiveUsername === 'default') {
|
|
139
139
|
// Hicbir username yok, default.json'a yaz
|
|
@@ -232,8 +232,8 @@ module.exports = {
|
|
|
232
232
|
inputSchema: {
|
|
233
233
|
type: "object",
|
|
234
234
|
properties: {
|
|
235
|
-
username: { type: "string", description: "Kullanici adi (ornek: '
|
|
236
|
-
fact: { type: "string", description: 'Yeni fact (ornek: "Kullanici
|
|
235
|
+
username: { type: "string", description: "Kullanici adi (ornek: 'ahmet' veya 'default')" },
|
|
236
|
+
fact: { type: "string", description: 'Yeni fact (ornek: "Kullanici kahve seviyor", "Istanbul\'da yasiyor")' },
|
|
237
237
|
score: { type: "number", description: "Onem derecesi 1-10 (default 5)" },
|
|
238
238
|
category: { type: "string", description: "Kategori: personal, preference, work, hobby, fact (default general)" },
|
|
239
239
|
botName: { type: "string", description: "Bot adini degistir (memory.botName)" },
|
package/src/tools/plan.js
CHANGED
package/src/tools/plugin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* skills_marketplace - Skill marketplace (v5.0.0)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Skills marketplace — skill paylasimi ve otomatik yukleme
|
|
5
5
|
*
|
|
6
6
|
* Format: ~/.natureco/marketplace/<skill_name>.json
|
|
7
7
|
* Source: NatureCo GitHub repo (community-contributed) veya local
|
|
@@ -16,7 +16,7 @@ const MARKETPLACE_DIR = path.join(os.homedir(), ".natureco", "marketplace");
|
|
|
16
16
|
const SKILLS_DIR = path.join(os.homedir(), ".natureco", "skills");
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Marketplace URL'leri —
|
|
19
|
+
* Marketplace URL'leri — kullanici GitHub repo'su
|
|
20
20
|
*/
|
|
21
21
|
const MARKETPLACE_SOURCES = [
|
|
22
22
|
{
|
|
@@ -32,7 +32,7 @@ const MARKETPLACE_SOURCES = [
|
|
|
32
32
|
];
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Built-in skill paketleri
|
|
35
|
+
* Built-in skill paketleri
|
|
36
36
|
*/
|
|
37
37
|
const BUILTIN_SKILLS = {
|
|
38
38
|
"seo-audit": {
|
package/src/tools/soul.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* soul - SOUL.md, IDENTITY.md, AGENTS.md okuyucu (v5.4.12)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Soul dosyasi — kimlik, kisisellik ve calisma tarzini tanimlar
|
|
5
5
|
*
|
|
6
6
|
* 3 dosya sirayla okunur, ozetlenir ve system prompt'a enjekte edilir:
|
|
7
7
|
* 1. SOUL.md - KISILIK (nasil hissederim, kirmizi cizgiler, degerler)
|
|
8
|
-
* 2. IDENTITY.md - KIMLIK (kim
|
|
8
|
+
* 2. IDENTITY.md - KIMLIK (kim oldugu, baglam, calisma tarzi)
|
|
9
9
|
* 3. AGENTS.md - CALISMA ORTAMI (kurallar, tools, heartbeats)
|
|
10
10
|
*
|
|
11
11
|
* 3 seviyede arar:
|
package/src/tools/voice_chat.js
CHANGED
package/src/utils/error.js
CHANGED
package/src/utils/paths.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* paths.js — Path helper utilities (v5.2.0)
|
|
3
3
|
*
|
|
4
4
|
* Tum tool'larda ~/Desktop/test.txt gibi path'leri dogru handle etmek icin.
|
|
5
|
-
*
|
|
5
|
+
* "File does not exist" bug'i duzeltildi.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const os = require("os");
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* system-prompt — Three-tier system prompt builder (Hermes-style)
|
|
3
|
+
*
|
|
4
|
+
* Tiers:
|
|
5
|
+
* STABLE — identity, personality, language rules, tool rules, skills index
|
|
6
|
+
* CONTEXT — soul summary, cross-session context
|
|
7
|
+
* VOLATILE — memory snapshot, user context, session state
|
|
8
|
+
*
|
|
9
|
+
* Stable + Context are built once at session start for prefix cache warmth.
|
|
10
|
+
* Volatile is rebuilt every turn.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
function buildTiers(opts) {
|
|
14
|
+
const {
|
|
15
|
+
botName,
|
|
16
|
+
userName = 'kullanıcı',
|
|
17
|
+
bossName = '',
|
|
18
|
+
bossTitle = '',
|
|
19
|
+
userTitle = '',
|
|
20
|
+
soulSummary = '',
|
|
21
|
+
isSmallModel = false,
|
|
22
|
+
memorySnapshotBlock = '',
|
|
23
|
+
skillsIndexBlock = '',
|
|
24
|
+
crossSessionContext = '',
|
|
25
|
+
userHome = '',
|
|
26
|
+
hasHistory = false,
|
|
27
|
+
memoryFacts = [],
|
|
28
|
+
} = opts;
|
|
29
|
+
const displayBot = botName || 'Asistan';
|
|
30
|
+
const displayBoss = bossName || userName || 'kullanıcı';
|
|
31
|
+
|
|
32
|
+
// ── STABLE TIER (changes rarely — prefix cache friendly) ─────────────
|
|
33
|
+
const stable = [
|
|
34
|
+
// Identity (strong, repeated)
|
|
35
|
+
`SENIN ADIN: ${displayBot}. SADECE ${displayBot} adini kullan, model adi SOYLEME.`,
|
|
36
|
+
bossName ? `PATRONUN: ${bossName}${bossTitle ? ` (${bossTitle})` : ''}. Sana "${displayBoss}" diye hitap eder.` : `KULLANICI: ${userName}. Ona yardim ediyorsun.`,
|
|
37
|
+
userTitle ? `UNVANIN: ${userTitle}.` : '',
|
|
38
|
+
`KIRMIZI CIZGILERIN: Onaysiz para harcama yok, veri guvenligi, site stabilitesi, resmi taahhut yok.`,
|
|
39
|
+
`!!! KRİTİK KİMLİK !!! Senin ADIN: ${displayBot}. Bu SENİN GERÇEK ADIN. Sen ${displayBot}'sin.`,
|
|
40
|
+
`ASLA "MiniMax", "MiniMax-M2.5", "Claude", "GPT", "AI asistanı", "yapay zeka" gibi ifadeleri KENDİNİ tanıtmak için KULLANMA.`,
|
|
41
|
+
`Kullanıcı "adın ne?", "sen kimsin?" diye sorduğunda İLK cümlende MUTLAKA "Ben ${displayBot}" yaz.`,
|
|
42
|
+
`Senin adın: ${displayBot}. Tekrar: ${displayBot}. Asla unutma: ${displayBot}.`,
|
|
43
|
+
`Sen bir marka veya ürün değilsin. Sen ${displayBot}sin, NatureCo CLI'nin Türkçe yapay zeka asistanısın.`,
|
|
44
|
+
|
|
45
|
+
// Personality (stable)
|
|
46
|
+
`Kisiselik: Sen samimi, sicak, dosta benzeyen bir asistansin. "Selam", "tamam", "hadi yapalim", "bak simdi", "sakin ol" gibi gunluk ifadeler kullan.`,
|
|
47
|
+
`Hitap: Kullanici ${userName}. Saygili ama samimi. "Siz" degil "sen" kullan.`,
|
|
48
|
+
`Emoji: Yerinde ve az kullan. Cok emoji atma ama bir-iki tane karakter katar.`,
|
|
49
|
+
`Kisa yanit: Uzun paragraflar yazma. Direkt konuya gir.`,
|
|
50
|
+
`Hata yaparsan "Pardon, yanlis yaptim, simdi duzelteyim" de. "Hata", "basarisiz", "imkansiz" deme.`,
|
|
51
|
+
|
|
52
|
+
// Language rules (stable)
|
|
53
|
+
`KRITIK DIL KURALI: Kullanici Turkce yaziyorsa MUTLAKA yuzde yuz Turkce cevap ver. Asla baska dil kullanma. Turkce karakterleri dogru kullan.`,
|
|
54
|
+
`Yazim: "degilim" dogru, "degil" degil. Turkce dil bilgisi kurallarina uy.`,
|
|
55
|
+
|
|
56
|
+
// Tool rules (stable)
|
|
57
|
+
`ONEMLI: Tool cagirma SIMULE ETME. Sadece duz metin cevap ver. Islem yapmak gerekirse tool'u gercekten cagir.`,
|
|
58
|
+
`COK KRITIK: Goreve baslamadan ONCE <available_skills> listesini tara. Ilgili skill varsa skill_view(name) ile yukle, SONRA goreve basla.`,
|
|
59
|
+
`KRITIK: Skill yuklemeden islem yapma. Ilgili skill varsa once yukle.`,
|
|
60
|
+
`KRITIK: Kullanici kisisel bilgi verdiginde memory(action=add, target=user) ile kaydet.`,
|
|
61
|
+
`KRITIK: Ortam bilgisi, proje kurallari gibi notlari memory(action=add, target=memory) ile kaydet.`,
|
|
62
|
+
`Kullanici hakkinda bilgin gerektiginde memory(action=list, target=user) ile getir.`,
|
|
63
|
+
|
|
64
|
+
// Skills index (stable within session)
|
|
65
|
+
skillsIndexBlock,
|
|
66
|
+
].filter(Boolean).join('\n');
|
|
67
|
+
|
|
68
|
+
// ── CONTEXT TIER (soul + cross-session, built once per resume) ──────
|
|
69
|
+
const context = [
|
|
70
|
+
!isSmallModel && soulSummary ? `=== KISISELIK DOSYALARI ===\n${soulSummary}` : '',
|
|
71
|
+
crossSessionContext ? `=== GECMIS KONUSMALAR ===\n${crossSessionContext}` : '',
|
|
72
|
+
].filter(Boolean).join('\n');
|
|
73
|
+
|
|
74
|
+
// ── VOLATILE TIER (built every turn) ─────────────────────────────────
|
|
75
|
+
const volatile = [
|
|
76
|
+
// Memory snapshot (changes every turn)
|
|
77
|
+
memorySnapshotBlock,
|
|
78
|
+
|
|
79
|
+
// Old JSON memory facts
|
|
80
|
+
memoryFacts.length > 0
|
|
81
|
+
? `Kullanici hakkinda bildiklerin: ${memoryFacts.slice(0, 8).map(f => f.value || f).join('; ')}`
|
|
82
|
+
: '',
|
|
83
|
+
|
|
84
|
+
// User environment (stable within session but changes on resume)
|
|
85
|
+
userHome ? `Kullanicinin home: ${userHome}` : '',
|
|
86
|
+
hasHistory ? 'Bu oturum daha onceki konusmalarin devami.' : '',
|
|
87
|
+
].filter(Boolean).join('\n');
|
|
88
|
+
|
|
89
|
+
return { stable, context, volatile };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Assemble all three tiers into a single system prompt string.
|
|
94
|
+
* stable + context should be cached between turns; volatile rebuilt each turn.
|
|
95
|
+
*/
|
|
96
|
+
function assemble(stable, context, volatile) {
|
|
97
|
+
return [stable, context, volatile].filter(Boolean).join('\n\n');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get the stable+context combined string for cache key comparison.
|
|
102
|
+
*/
|
|
103
|
+
function stableContextKey(stable, context) {
|
|
104
|
+
return stable + '|||' + context;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = { buildTiers, assemble, stableContextKey };
|
package/src/utils/tools.js
CHANGED
|
@@ -220,12 +220,24 @@ const BLOCKED_NAMES = new Set([
|
|
|
220
220
|
'run_command', 'sql', 'query', 'lookup',
|
|
221
221
|
]);
|
|
222
222
|
|
|
223
|
+
// ── check_fn TTL cache (Hermes-style, ~30s) ────────────────────────────
|
|
224
|
+
const _checkFnCache = new Map();
|
|
225
|
+
function _cachedCheckFn(fn, key) {
|
|
226
|
+
const now = Date.now();
|
|
227
|
+
const cached = _checkFnCache.get(key);
|
|
228
|
+
if (cached && now - cached.ts < 30000) return cached.result;
|
|
229
|
+
let result = true;
|
|
230
|
+
try { result = fn() !== false; } catch { result = false; }
|
|
231
|
+
_checkFnCache.set(key, { result, ts: now });
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
|
|
223
235
|
function toOpenAIFormat(toolDefs) {
|
|
224
236
|
return toolDefs
|
|
225
237
|
.filter(t => !BLOCKED_NAMES.has(t.name))
|
|
226
238
|
.filter(t => {
|
|
227
239
|
if (t.checkFn) {
|
|
228
|
-
|
|
240
|
+
return _cachedCheckFn(t.checkFn, t.name);
|
|
229
241
|
}
|
|
230
242
|
return true;
|
|
231
243
|
})
|
|
@@ -253,12 +265,10 @@ async function executeTool(toolName, args, toolDefs) {
|
|
|
253
265
|
const tool = toolDefs.find(t => t.name === toolName);
|
|
254
266
|
if (!tool) return { error: `Tool bulunamadı: ${toolName}` };
|
|
255
267
|
if (!tool.execute) return { error: `Tool execute fonksiyonu yok: ${toolName}` };
|
|
256
|
-
// checkFn — tool disabled? (re-check at runtime)
|
|
268
|
+
// checkFn — tool disabled? (re-check at runtime with cache)
|
|
257
269
|
if (tool.checkFn) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
} catch {
|
|
261
|
-
return { error: `${toolName} kontrol hatası` };
|
|
270
|
+
if (!_cachedCheckFn(tool.checkFn, tool.name)) {
|
|
271
|
+
return { error: `${toolName} şu anda kullanılamıyor (check_fn engelledi)` };
|
|
262
272
|
}
|
|
263
273
|
}
|
|
264
274
|
try {
|