natureco-cli 5.30.0 → 5.32.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 +20 -0
- package/package.json +1 -1
- package/src/commands/repl.js +9 -3
- package/src/tools/agentic-runner.js +6 -2
- package/src/tools/memory_tree.js +156 -0
- package/src/tools/workflow.js +37 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NatureCo CLI will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [5.32.0] - 2026-07-04 — "AĞAÇ-HAFIZA + OTURUMLAR ARASI KALICILIK (Theseus mimarisi)"
|
|
6
|
+
|
|
7
|
+
### ✨ Yeni (Hafıza)
|
|
8
|
+
- **Oturumlar arası hafiza DUZELDI**: eski persist dar regex kaliplariyla (sadece "adim X") fact cikariyordu; keyfi bilgi ("parolam X, hatirla") kayboluyordu. Artik ajan `memory_write` ile ANINDA kaydeder → yeni oturumda hatirlanir (E2E: parola S1→S2 ✓).
|
|
9
|
+
- **Agac-hafiza (`memory_tree`)**: kullanicinin OpenCode/Theseus icin tasarladigi tree-memory mimarisinden uyarlandi. Kok (1-kisisel/2-teknik/3-kararlar) → dal (## baslik) → yaprak. action: index|read|search|append. Duz `facts[]` listesinin "coplুğe donme" sorununu cozer; kategorize + logaritmik erisim.
|
|
10
|
+
- **Proaktif yukleme (Theseus deseni)**: her istekte hafiza agaci sistem prompt'una otomatik enjekte edilir ("BILDIGIN KALICI HAFIZA") → ajan on-demand aramaya guvenmeden baglami ZATEN bilir. (E2E: "NatureCoPixel neyle yapiliyor?"→"Godot" ✓; hafiza ile dosya sistemini ayirt eder.)
|
|
11
|
+
- Tek-primary + "bkz:" capraz referans; credential/secret asla duz metin.
|
|
12
|
+
|
|
13
|
+
Doğrulama: memory_write + memory_tree E2E (S1 kaydet → S2 yeni oturum hatirla); 6 yeni tree testi; **498 test yeşil**, ESLint temiz.
|
|
14
|
+
|
|
15
|
+
## [5.31.0] - 2026-07-04 — "CHAT/CODE ARAYÜZ ZENGİNLEŞTİRME (araç görünürlüğü + düşünme + input alanı)"
|
|
16
|
+
|
|
17
|
+
### ✨ Yeni (UX)
|
|
18
|
+
- **Araç görünürlugu**: agentic akista her arac ekranda gorunur — "🔧 <etiket> · <ozet> ✓/✗" (write_file/edit_file/bash/browser/mac_app_open...). Onceden streaming tool XML'ini gizlerken arac aktivitesi de gorunmuyordu; artik gorunur.
|
|
19
|
+
- **Düsünme gostergesi**: model yanit uretirken "💭 düşünüyor…", ilk token gelince temizlenir.
|
|
20
|
+
- **Gorunur input alani**: REPL prompt'u "💬 Sen ▸" + her girdiden once ince ayirici cizgi (cikti/girdi net ayrilir; readline tek-satir → satir duzenleme bozulmaz).
|
|
21
|
+
- **Gorunur acma yonlendirmesi (full mod)**: "kendi tarayicimda ac / dinlemek istiyorum" → headless `browser` yerine gorunur `open`/`start`/`mac_app_open` kullanilir.
|
|
22
|
+
|
|
23
|
+
Doğrulama: streaming UI E2E (💭 + 🔧 + ✓ gorunuyor); REPL regresyon; **492 test yeşil**.
|
|
24
|
+
|
|
5
25
|
## [5.30.0] - 2026-07-04 — "TAM KONTROL MODU (sahip opt-in: tüm araç+skill + computer-use)"
|
|
6
26
|
|
|
7
27
|
### ✨ Yeni
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.32.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"
|
package/src/commands/repl.js
CHANGED
|
@@ -1178,14 +1178,20 @@ async function startRepl(args) {
|
|
|
1178
1178
|
const rl = readline.createInterface({
|
|
1179
1179
|
input: createPasteSafeInput(process.stdin),
|
|
1180
1180
|
output: createOutputFilter(process.stdout),
|
|
1181
|
-
prompt: tui.styled('
|
|
1181
|
+
prompt: tui.styled(' 💬 Sen ▸ ', { color: tui.PALETTE.primary, bold: true }),
|
|
1182
1182
|
terminal: true,
|
|
1183
1183
|
});
|
|
1184
1184
|
// Pipe/script kullanımında EOF, yanıt hâlâ üretilirken gelir —
|
|
1185
1185
|
// aktif işlem varken kapanışı bekletmek için sayaç + kapalı-rl koruması
|
|
1186
1186
|
let _busy = 0;
|
|
1187
1187
|
let _rlClosed = false;
|
|
1188
|
-
|
|
1188
|
+
// Görünür input alanı: her prompttan önce ince ayırıcı çizgi (readline prompt'u tek-satır
|
|
1189
|
+
// kalir → satir duzenleme/gecmis bozulmaz). Cikti ile girdi arasini net ayirir.
|
|
1190
|
+
const safePrompt = () => {
|
|
1191
|
+
if (_rlClosed) return;
|
|
1192
|
+
try { process.stdout.write(tui.styled('\n ' + '─'.repeat(54) + '\n', { color: tui.PALETTE.muted })); } catch {}
|
|
1193
|
+
rl.prompt();
|
|
1194
|
+
};
|
|
1189
1195
|
safePrompt();
|
|
1190
1196
|
|
|
1191
1197
|
const cleanup = async (exitCode = 0) => {
|
|
@@ -1465,7 +1471,7 @@ async function startRepl(args) {
|
|
|
1465
1471
|
|
|
1466
1472
|
// Çok satırlı (paste) mesajları gönderildikten sonra ekranda göster
|
|
1467
1473
|
if (line.indexOf('\n') !== -1) {
|
|
1468
|
-
process.stdout.write(tui.styled('
|
|
1474
|
+
process.stdout.write(tui.styled(' 💬 Sen ▸ ', { color: tui.PALETTE.primary, bold: true }));
|
|
1469
1475
|
process.stdout.write(line + '\n');
|
|
1470
1476
|
}
|
|
1471
1477
|
|
|
@@ -21,7 +21,7 @@ const os = require('os');
|
|
|
21
21
|
// icinde approvals politikasini uyguluyor (isSafeCommand → direkt; tehlikeli → red;
|
|
22
22
|
// digerleri → allowlist/full moda gore). Yani keyfi/yikici komut calismaz.
|
|
23
23
|
// Diger ~85 arac (discord, telegram, cron, browser...) bilerek DISARIDA.
|
|
24
|
-
const DEFAULT_ALLOWED = ['write_file', 'read_file', 'edit_file', 'skill_view', 'bash', 'file_search', 'list_dir'];
|
|
24
|
+
const DEFAULT_ALLOWED = ['write_file', 'read_file', 'edit_file', 'skill_view', 'bash', 'file_search', 'list_dir', 'memory_write', 'memory_tree'];
|
|
25
25
|
|
|
26
26
|
const TOOL_ALIASES = {
|
|
27
27
|
write_file: 'write_file', create_file: 'write_file', writefile: 'write_file', write: 'write_file', create: 'write_file', save_file: 'write_file', new_file: 'write_file',
|
|
@@ -31,6 +31,8 @@ const TOOL_ALIASES = {
|
|
|
31
31
|
bash: 'bash', run_command: 'bash', shell: 'bash', shell_command: 'bash', exec: 'bash', run_terminal: 'bash', terminal: 'bash', run: 'bash', command: 'bash',
|
|
32
32
|
file_search: 'file_search', glob: 'file_search', find_files: 'file_search', search_files: 'file_search', find: 'file_search',
|
|
33
33
|
list_dir: 'list_dir', ls: 'list_dir', dir: 'list_dir', list_directory: 'list_dir', filesystem: 'list_dir',
|
|
34
|
+
memory_write: 'memory_write', remember: 'memory_write', save_memory: 'memory_write', memorize: 'memory_write', memory_save: 'memory_write', save_fact: 'memory_write',
|
|
35
|
+
memory_tree: 'memory_tree', tree_memory: 'memory_tree', memory: 'memory_tree', hafiza: 'memory_tree',
|
|
34
36
|
};
|
|
35
37
|
|
|
36
38
|
function expandHome(p) {
|
|
@@ -326,7 +328,7 @@ async function executeCall(call, opts = {}) {
|
|
|
326
328
|
* callModel(messages) => Promise<{ content, toolCalls }>
|
|
327
329
|
* Donus: { records, reply, iterations }
|
|
328
330
|
*/
|
|
329
|
-
async function runAgentic({ callModel, systemPrompt, historyMessages, task, toolsDir, loadTool, allowed, execFull, maxIterations = 15 }) {
|
|
331
|
+
async function runAgentic({ callModel, systemPrompt, historyMessages, task, toolsDir, loadTool, allowed, execFull, onEvent, maxIterations = 15 }) {
|
|
330
332
|
const messages = [{ role: 'system', content: systemPrompt }];
|
|
331
333
|
for (const mm of historyMessages || []) messages.push({ role: mm.role, content: mm.content || '' });
|
|
332
334
|
messages.push({ role: 'user', content: task });
|
|
@@ -349,7 +351,9 @@ async function runAgentic({ callModel, systemPrompt, historyMessages, task, tool
|
|
|
349
351
|
messages.push({ role: 'assistant', content: content || '' });
|
|
350
352
|
const feedbacks = [];
|
|
351
353
|
for (const call of calls) {
|
|
354
|
+
if (onEvent) { try { onEvent({ phase: 'start', tool: call.tool, args: call.args }); } catch {} }
|
|
352
355
|
const { records, feedback } = await executeCall(call, { toolsDir, loadTool, allowed: allowedSet, execFull });
|
|
356
|
+
if (onEvent) { try { onEvent({ phase: 'end', tool: call.tool, args: call.args, records }); } catch {} }
|
|
353
357
|
allRecords.push(...records);
|
|
354
358
|
feedbacks.push(feedback);
|
|
355
359
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* memory_tree — Ağaç-hafıza (kök → dal → yaprak). Kullanıcının OpenCode için tasarladığı
|
|
3
|
+
* tree-memory mimarisinden uyarlandı (opencode-hafiza-mimarisi.md).
|
|
4
|
+
*
|
|
5
|
+
* Felsefe: "bilgi saklama" değil "hızlı bilgi bulma". Düz liste zamanla çöplüğe döner;
|
|
6
|
+
* ağaç logaritmik arama sağlar (kök seç → dal seç → yaprak oku, <300 token).
|
|
7
|
+
*
|
|
8
|
+
* Yapı: ~/.natureco/memory/tree/<user>/
|
|
9
|
+
* 0-index.md — yönlendirme (hangi konu hangi kökte)
|
|
10
|
+
* 1-kisisel.md — Kişisel & Tercihler (## dallar altında yapraklar)
|
|
11
|
+
* 2-teknik.md — Teknik & Projeler
|
|
12
|
+
* 3-kararlar.md— Kararlar, Kurallar & Dersler
|
|
13
|
+
*
|
|
14
|
+
* Kurallar (mimariden): tek primary + "bkz:" çapraz referans; yeni bilgi ilgili dalın
|
|
15
|
+
* altına (dosya sonuna değil); credential/secret ASLA düz metin ("bkz: secrets vault").
|
|
16
|
+
*/
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
|
|
21
|
+
const ROOTS = [
|
|
22
|
+
{ id: '1-kisisel', title: 'Kişisel & Tercihler', branches: ['Kimlik', 'Tercihler', 'İletişim Kalıpları'] },
|
|
23
|
+
{ id: '2-teknik', title: 'Teknik & Projeler', branches: ['Projeler', 'Kurulum & Sistem', 'Teknik Referans'] },
|
|
24
|
+
{ id: '3-kararlar', title: 'Kararlar, Kurallar & Dersler', branches: ['Kararlar', 'Kurallar & Kısıtlar', 'Öğrenilen Dersler', 'Tarihli Olaylar'] },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
function treeDir(username) {
|
|
28
|
+
return path.join(os.homedir(), '.natureco', 'memory', 'tree', String(username || 'default').toLowerCase());
|
|
29
|
+
}
|
|
30
|
+
function rootPath(username, id) { return path.join(treeDir(username), id + '.md'); }
|
|
31
|
+
function readSafe(username, id) { try { return fs.readFileSync(rootPath(username, id), 'utf8'); } catch { return ''; } }
|
|
32
|
+
function resolveRoot(root) {
|
|
33
|
+
if (!root) return ROOTS[0];
|
|
34
|
+
const r = String(root).toLowerCase();
|
|
35
|
+
return ROOTS.find((x) => x.id === r || x.id.endsWith(r.replace(/^\d+-/, '')) || x.id.includes(r) || x.title.toLowerCase().includes(r)) || ROOTS[0];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function ensureTree(username) {
|
|
39
|
+
const dir = treeDir(username);
|
|
40
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
41
|
+
for (const r of ROOTS) {
|
|
42
|
+
const p = rootPath(username, r.id);
|
|
43
|
+
if (!fs.existsSync(p)) fs.writeFileSync(p, `# ${r.title}\n\n` + r.branches.map((b) => `## ${b}\n`).join('\n'), 'utf8');
|
|
44
|
+
}
|
|
45
|
+
const idx = path.join(dir, '0-index.md');
|
|
46
|
+
if (!fs.existsSync(idx)) fs.writeFileSync(idx, '# Hafıza İndeksi (kök → dal)\n\n' + ROOTS.map((r) => `- **${r.id}** — ${r.title}: ${r.branches.join(', ')}`).join('\n') + '\n', 'utf8');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Modele enjekte edilen kompakt indeks (dal başlıkları + yaprak sayısı).
|
|
50
|
+
function buildIndex(username) {
|
|
51
|
+
ensureTree(username);
|
|
52
|
+
const lines = [];
|
|
53
|
+
for (const r of ROOTS) {
|
|
54
|
+
const txt = readSafe(username, r.id);
|
|
55
|
+
const branches = (txt.match(/^##\s+(.+)$/gm) || []).map((s) => s.replace(/^##\s+/, '').trim());
|
|
56
|
+
const leaves = (txt.match(/^\s*-\s+\S/gm) || []).length;
|
|
57
|
+
lines.push(`- ${r.id} (${r.title}) — dallar: ${branches.join(' · ') || '(boş)'}${leaves ? ` [${leaves} kayıt]` : ''}`);
|
|
58
|
+
}
|
|
59
|
+
return lines.join('\n');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readRoot(username, id) {
|
|
63
|
+
ensureTree(username);
|
|
64
|
+
try { return fs.readFileSync(rootPath(username, resolveRoot(id).id), 'utf8'); } catch { return ''; }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Theseus deseni: oturum BAŞINDA hafızayı proaktif yükle. Tüm kökler-dallar-yapraklardan
|
|
68
|
+
// (boş dallar hariç) kompakt bir özet çıkar → sistem prompt'una gömülür ki ajan
|
|
69
|
+
// on-demand aramaya güvenmeden bilgiyi ZATEN bilsin. maxChars aşılırsa kırpılır.
|
|
70
|
+
function buildDigest(username, maxChars = 2600) {
|
|
71
|
+
ensureTree(username);
|
|
72
|
+
const parts = [];
|
|
73
|
+
for (const r of ROOTS) {
|
|
74
|
+
const txt = readSafe(username, r.id);
|
|
75
|
+
let branch = '';
|
|
76
|
+
const byBranch = {};
|
|
77
|
+
for (const line of txt.split('\n')) {
|
|
78
|
+
if (line.startsWith('## ')) branch = line.slice(3).trim();
|
|
79
|
+
else if (/^\s*-\s+\S/.test(line)) (byBranch[branch] = byBranch[branch] || []).push(line.trim().replace(/^-\s*/, ''));
|
|
80
|
+
}
|
|
81
|
+
const nonEmpty = Object.entries(byBranch).filter(([, l]) => l.length);
|
|
82
|
+
if (nonEmpty.length) {
|
|
83
|
+
parts.push(`[${r.title}]`);
|
|
84
|
+
for (const [b, l] of nonEmpty) parts.push(` ${b}: ${l.join('; ')}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let out = parts.join('\n');
|
|
88
|
+
if (out.length > maxChars) out = out.slice(0, maxChars) + '\n…(fazlası için memory_tree ile oku)';
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function search(username, query) {
|
|
93
|
+
ensureTree(username);
|
|
94
|
+
const q = String(query || '').toLowerCase().trim();
|
|
95
|
+
if (!q) return [];
|
|
96
|
+
const hits = [];
|
|
97
|
+
for (const r of ROOTS) {
|
|
98
|
+
const txt = readSafe(username, r.id);
|
|
99
|
+
let branch = '';
|
|
100
|
+
for (const line of txt.split('\n')) {
|
|
101
|
+
if (line.startsWith('## ')) branch = line.slice(3).trim();
|
|
102
|
+
else if (line.trim() && !line.startsWith('#') && line.toLowerCase().includes(q)) hits.push(`${r.id}/${branch}: ${line.trim()}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return hits;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function append(username, root, branch, content) {
|
|
109
|
+
ensureTree(username);
|
|
110
|
+
const r = resolveRoot(root);
|
|
111
|
+
const p = rootPath(username, r.id);
|
|
112
|
+
const br = String(branch || 'Genel').trim();
|
|
113
|
+
const leaf = '- ' + String(content).replace(/\s+/g, ' ').trim();
|
|
114
|
+
let txt = fs.readFileSync(p, 'utf8');
|
|
115
|
+
const bRe = new RegExp(`^##[ \\t]+${br.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[ \\t]*$`, 'mi');
|
|
116
|
+
const m = txt.match(bRe);
|
|
117
|
+
if (m) {
|
|
118
|
+
const idx = txt.indexOf(m[0]) + m[0].length;
|
|
119
|
+
txt = txt.slice(0, idx) + '\n' + leaf + txt.slice(idx);
|
|
120
|
+
} else {
|
|
121
|
+
txt = txt.trimEnd() + `\n\n## ${br}\n${leaf}\n`;
|
|
122
|
+
}
|
|
123
|
+
fs.writeFileSync(p, txt, 'utf8');
|
|
124
|
+
return { success: true, root: r.id, branch: br, saved: leaf };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = {
|
|
128
|
+
name: 'memory_tree',
|
|
129
|
+
description: 'Ağaç-hafıza (kök→dal→yaprak): kalıcı bilgiyi kategorize SAKLA/OKU/ARA. Yeni oturumda hatırlanır. action: index|read|search|append',
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
properties: {
|
|
133
|
+
action: { type: 'string', description: 'index | read | search | append' },
|
|
134
|
+
username: { type: 'string', description: 'kullanıcı adı' },
|
|
135
|
+
root: { type: 'string', description: '1-kisisel | 2-teknik | 3-kararlar' },
|
|
136
|
+
branch: { type: 'string', description: 'dal başlığı (append için)' },
|
|
137
|
+
content: { type: 'string', description: 'yaprak metni (append için)' },
|
|
138
|
+
query: { type: 'string', description: 'arama (search için)' },
|
|
139
|
+
},
|
|
140
|
+
required: ['action'],
|
|
141
|
+
},
|
|
142
|
+
async execute(p) {
|
|
143
|
+
const u = p.username || 'default';
|
|
144
|
+
try {
|
|
145
|
+
if (p.action === 'index') return { success: true, index: buildIndex(u) };
|
|
146
|
+
if (p.action === 'read') return { success: true, content: readRoot(u, p.root || '1-kisisel') };
|
|
147
|
+
if (p.action === 'search') return { success: true, results: search(u, p.query || p.content || '') };
|
|
148
|
+
if (p.action === 'append') {
|
|
149
|
+
if (!p.content) return { success: false, error: 'content (yaprak metni) gerekli' };
|
|
150
|
+
return append(u, p.root || '1-kisisel', p.branch || 'Genel', p.content);
|
|
151
|
+
}
|
|
152
|
+
return { success: false, error: 'bilinmeyen action: ' + p.action + ' (index|read|search|append)' };
|
|
153
|
+
} catch (e) { return { success: false, error: e.message }; }
|
|
154
|
+
},
|
|
155
|
+
_internal: { ensureTree, buildIndex, buildDigest, readRoot, search, append, treeDir, rootPath, ROOTS },
|
|
156
|
+
};
|
package/src/tools/workflow.js
CHANGED
|
@@ -222,6 +222,11 @@ async function workflow(params) {
|
|
|
222
222
|
const { runAgentic } = require('./agentic-runner');
|
|
223
223
|
const memCtx = memoryContext();
|
|
224
224
|
const desktop = path.join(os.homedir(), 'Desktop');
|
|
225
|
+
const memUser = cfg.userName || 'default';
|
|
226
|
+
// Theseus deseni: oturum basinda hafiza agacini PROAKTIF yukle (on-demand aramaya
|
|
227
|
+
// guvenme). Digest = kayitli bilgiler (icerik); Index = yapi (kok→dal).
|
|
228
|
+
let treeIndex = '', treeDigest = '';
|
|
229
|
+
try { const mt = require('./memory_tree')._internal; treeDigest = mt.buildDigest(memUser); treeIndex = mt.buildIndex(memUser); } catch {}
|
|
225
230
|
// Tam mod (sahibin opt-in'i): tum arac+skill'ler acilir, keyfi shell (yikici haric).
|
|
226
231
|
const execFull = cfg.agentExec === 'full' || cfg.computerUse === true || String(process.env.NATURECO_AGENT_EXEC || '').toLowerCase() === 'full';
|
|
227
232
|
let fullToolsBlock = '';
|
|
@@ -231,11 +236,11 @@ async function workflow(params) {
|
|
|
231
236
|
'\n\nTAM MOD ACIK — su araclara da ERISIMIN VAR; o an ne gerekiyorsa dogrudan cagir:',
|
|
232
237
|
'- mac_app_open: macOS uygulamasi ac. parametre: appName (orn. "WhatsApp", "Google Chrome", "Spotify")',
|
|
233
238
|
'- mac_app_quit: macOS uygulamasi kapat. parametre: appName',
|
|
234
|
-
'- browser: tarayici otomasyonu (
|
|
239
|
+
'- browser: HEADLESS tarayici otomasyonu (icerik cek/screenshot — kullaniciya GORUNMEZ). parametreler: action, url, script',
|
|
235
240
|
'- computer_use: GUI otomasyonu. parametreler: action ("screenshot"/"click"/"type"/"keypress"/"scroll"), x, y, text, key',
|
|
236
241
|
'- social_open: muzik/video/sosyal ac. parametreler: query, platform (spotify/youtube...)',
|
|
237
242
|
'- macos_screenshot: ekran goruntusu al',
|
|
238
|
-
'
|
|
243
|
+
'\nGORUNUR ACMA (onemli): Kullanici "kendi tarayicimda ac / gorunur ac / dinlemek/izlemek istiyorum" derse `browser` (headless, gorunmez) DEGIL, GORUNUR ac: macOS bash ile `open "https://..."` ya da `open -a "Google Chrome" "https://..."`; Windows `start "" "https://..."`. Uygulama icin `open -a WhatsApp` / mac_app_open. Muzik/video icin dogrudan YouTube/Spotify URL\'sini `open` ile ac.',
|
|
239
244
|
'\nTum arac listesi (isimle cagir; parametre yanlissa <tool_results> duzeltir): ' + allNames.join(', '),
|
|
240
245
|
].join('\n');
|
|
241
246
|
}
|
|
@@ -252,7 +257,11 @@ async function workflow(params) {
|
|
|
252
257
|
'- list_dir: dizin icerigini listele. parametre: path',
|
|
253
258
|
'- bash: kabuk komutu calistir (npm, git, node, python, test, ls, grep/findstr, mkdir...). parametre: command. Guvenli komutlar dogrudan calisir; yikici/tehlikeli komutlar guvenlik politikasiyla engellenir. Icerik aramasi icin grep/findstr kullan.',
|
|
254
259
|
'- skill_view: gorevle ilgili bir skill yukle. parametre: name',
|
|
260
|
+
'- memory_write: HIZLI tek bilgi kaydet (isim, kisa tercih). parametreler: username ("' + memUser + '"), fact, category. YENI oturumda hatirlanir.',
|
|
261
|
+
'- memory_tree: AGAC-HAFIZA — zengin/kategorize kalici bilgi (proje, karar, teknik not, kisisel detay). action: index|read|search|append; username="' + memUser + '"; append icin: root (1-kisisel|2-teknik|3-kararlar) + branch (dal basligi) + content (yaprak). YENI oturumda hatirlanir.',
|
|
255
262
|
'\nKurallar:',
|
|
263
|
+
'- Kullanici kalici bilgi paylasirsa (isim, tercih, parola, proje, karar, onemli detay) ya da "hatirla / not al / kaydet" derse HEMEN KAYDET (username="' + memUser + '"): kisa tek bilgi → memory_write; zengin/kategorize bilgi → memory_tree(append, dogru root/branch). Trivial/gecici seyleri kaydetme.',
|
|
264
|
+
'- Kullaniciya ozel bir sey sorulunca (gecmis, proje, tercih, karar) once memory_tree(search/read) ile ilgili kok/dali OKU (hedefli, tum hafizayi degil). Tek primary: bilgi tek yerde yasar; digerine sadece "bkz:". Credential/secret ASLA duz metin.',
|
|
256
265
|
'- MEVCUT bir dosyayi degistirmeden ONCE read_file ile oku; sonra edit_file ile hedefli degisiklik yap (tum dosyayi write_file ile ezme).',
|
|
257
266
|
'- Bir seyi nerede oldugunu bilmiyorsan once file_search/list_dir/bash(grep) ile kesfet.',
|
|
258
267
|
'- Kod yazdiktan/degistirdikten sonra gerektiginde bash ile calistir/test et (orn. "node dosya.js", "npm test"); hata cikarsa duzelt.',
|
|
@@ -263,6 +272,8 @@ async function workflow(params) {
|
|
|
263
272
|
'- Basit sohbet/selamlasma ise arac cagirma, dogrudan kisa yanit ver.',
|
|
264
273
|
execFull ? '- Kullanici uygulama ac / tarayici kontrol / muzik cal / ekran / GUI istediyse yukaridaki computer-use araclarini KULLAN — "yapamam/engellendi" DEME, dogrudan ilgili araci cagir.' : '',
|
|
265
274
|
fullToolsBlock,
|
|
275
|
+
treeDigest ? ('\n\nBILDIGIN KALICI HAFIZA (bu kullaniciya ait, onceki oturumlardan hatirladiklarin; kullaniciya ozel bir sey sorulursa ONCE BUNU KULLAN — dosya arama, uydurma):\n' + treeDigest) : '',
|
|
276
|
+
treeIndex ? ('\n\nHafiza agaci yapisi (yukarida olmayan detay icin memory_tree(action:read/search) ile ilgili kok/dali oku):\n' + treeIndex) : '',
|
|
266
277
|
skillsIndexBlock ? '\n\n' + skillsIndexBlock : '',
|
|
267
278
|
].filter(Boolean).join('\n');
|
|
268
279
|
|
|
@@ -279,11 +290,14 @@ async function workflow(params) {
|
|
|
279
290
|
|
|
280
291
|
async function callModel(msgs) {
|
|
281
292
|
if (streamOn) {
|
|
282
|
-
|
|
293
|
+
let cleared = false;
|
|
294
|
+
const clearThinking = () => { if (!cleared) { process.stdout.write('\r\x1b[K'); cleared = true; } };
|
|
295
|
+
process.stdout.write('\x1b[2m 💭 düşünüyor…\x1b[0m');
|
|
296
|
+
const sani = makeSanitizeStream(botName, t => { clearThinking(); process.stdout.write(t); });
|
|
283
297
|
const filter = makeStreamFilter(t => sani.push(t), null);
|
|
284
298
|
const body = { model, stream: true, messages: msgs, temperature: 0.3, max_tokens: 16000 };
|
|
285
299
|
const out = await apiCallStream(providerUrl, providerApiKey, body, d => filter.push(d));
|
|
286
|
-
filter.end(); sani.end();
|
|
300
|
+
filter.end(); sani.end(); clearThinking();
|
|
287
301
|
return { content: out.content || '', toolCalls: out.toolCalls || [] };
|
|
288
302
|
}
|
|
289
303
|
const body = { model, stream: false, messages: msgs, temperature: 0.3, max_tokens: 16000 };
|
|
@@ -292,10 +306,28 @@ async function workflow(params) {
|
|
|
292
306
|
return { content: msg.content || '', toolCalls: msg.tool_calls || [] };
|
|
293
307
|
}
|
|
294
308
|
|
|
309
|
+
// Araç aktivitesi gösterimi (TTY streaming): her araç icin "🔧 label · hint ✓/✗"
|
|
310
|
+
const TOOL_LABEL = { write_file: 'dosya yaz', read_file: 'oku', edit_file: 'düzenle', bash: 'komut', file_search: 'ara', list_dir: 'listele', skill_view: 'skill', browser: 'tarayıcı', browser_use: 'tarayıcı', mac_app_open: 'uygulama aç', mac_app_quit: 'uygulama kapat', computer_use: 'GUI', social_open: 'medya aç', macos_screenshot: 'ekran görüntüsü' };
|
|
311
|
+
function briefHint(args) {
|
|
312
|
+
if (!args || typeof args !== 'object') return '';
|
|
313
|
+
const v = args.appName || args.query || args.name || args.url || args.command || args.pattern || args.path || args.action;
|
|
314
|
+
return v ? String(v).replace(/\s+/g, ' ').slice(0, 46) : '';
|
|
315
|
+
}
|
|
316
|
+
const onEvent = streamOn ? (ev) => {
|
|
317
|
+
if (ev.phase === 'start') {
|
|
318
|
+
const label = TOOL_LABEL[ev.tool] || ev.tool;
|
|
319
|
+
const hint = briefHint(ev.args);
|
|
320
|
+
process.stdout.write('\n\x1b[2m 🔧 ' + label + (hint ? ' · ' + hint : '') + '\x1b[0m');
|
|
321
|
+
} else {
|
|
322
|
+
const rec = (ev.records || [])[0] || {};
|
|
323
|
+
process.stdout.write(rec.status === 'done' ? ' \x1b[32m✓\x1b[0m' : ' \x1b[31m✗\x1b[0m');
|
|
324
|
+
}
|
|
325
|
+
} : null;
|
|
326
|
+
|
|
295
327
|
try {
|
|
296
328
|
const { records, reply } = await runAgentic({
|
|
297
329
|
callModel, systemPrompt: sysMsg, historyMessages, task,
|
|
298
|
-
toolsDir: __dirname, execFull, maxIterations: 15,
|
|
330
|
+
toolsDir: __dirname, execFull, onEvent, maxIterations: 15,
|
|
299
331
|
});
|
|
300
332
|
const fileWrites = records.filter(r => r.tool === 'write_file' && r.status === 'done');
|
|
301
333
|
let finalReply = reply || '';
|