natureco-cli 5.20.3 → 5.20.4
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/README.md +2 -0
- package/bin/natureco.js +1 -1
- package/package.json +1 -1
- package/src/commands/chat.js +1 -0
- package/src/commands/help.js +2 -0
- package/src/commands/naturehub.js +98 -7
- package/src/commands/repl.js +1604 -1580
- package/src/tools/llm_task.js +20 -7
- package/src/tools/workflow.js +26 -11
- /package/src/tools/{http.js → http.js.bak} +0 -0
package/src/commands/repl.js
CHANGED
|
@@ -1,1580 +1,1604 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* natureco repl — Persistent Interactive REPL
|
|
3
|
-
*
|
|
4
|
-
* Özellikler:
|
|
5
|
-
* ✅ Cross-session hafıza (memory dosyası)
|
|
6
|
-
* ✅ Otomatik fact extraction (LLM konuşmadan öğrenir)
|
|
7
|
-
* ✅ Session resume (--resume ile kaldığın yerden devam)
|
|
8
|
-
* ✅ Tüm CLI komutları REPL içinden (/doctor, /cost, /audit, /team...)
|
|
9
|
-
* ✅ Slash komutlar (/help, /memory, /model, /system, /exit)
|
|
10
|
-
* ✅ Ctrl+C temiz çıkış, konuşma otomatik kayıt
|
|
11
|
-
* ✅ Persistent session list (~/.natureco/sessions.json)
|
|
12
|
-
* ✅ Token tracking
|
|
13
|
-
*
|
|
14
|
-
* v4.6.0 — Persistent Memory Edition
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const readline = require('readline');
|
|
18
|
-
const fs = require('fs');
|
|
19
|
-
const path = require('path');
|
|
20
|
-
const os = require('os');
|
|
21
|
-
const https = require('https');
|
|
22
|
-
const { spawn } = require('child_process');
|
|
23
|
-
const chalk = require('chalk');
|
|
24
|
-
const tui = require('../utils/tui');
|
|
25
|
-
const { loadToolDefinitions, toOpenAIFormat, executeTool } = require('../utils/tools');
|
|
26
|
-
const { accumulateToolCallDeltas, finalizeToolCalls } = require('../utils/streaming-tools');
|
|
27
|
-
const { createPasteSafeInput, createOutputFilter, enableBracketedPaste, disableBracketedPaste, restoreNewlines, clearPasteContext } = require('../utils/paste-safe-input');
|
|
28
|
-
const { getMemoryStore } = require('../utils/memory-store');
|
|
29
|
-
const { buildSkillIndex } = require('../utils/skill-index');
|
|
30
|
-
const { buildTiers, assemble, discoverProjectRules } = require('../utils/system-prompt');
|
|
31
|
-
const { ToolGuardrails } = require('../utils/tool-guardrails');
|
|
32
|
-
|
|
33
|
-
// v5.4.6: Model adi sizintisini engelle — global'e ata, callback'lerden erisebilir olsun
|
|
34
|
-
const MODEL_NAMES_TO_HIDE = ['MiniMax-M2.5', 'MiniMaxM2.5', 'minimaxm25', 'Claude-3', 'GPT-4', 'ChatGPT'];
|
|
35
|
-
function fixModelNameLeak(text, botName) {
|
|
36
|
-
if (!text) return text;
|
|
37
|
-
let fixed = text;
|
|
38
|
-
for (const modelName of MODEL_NAMES_TO_HIDE) {
|
|
39
|
-
const regex = new RegExp(modelName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
|
|
40
|
-
fixed = fixed.replace(regex, botName || 'asistan');
|
|
41
|
-
}
|
|
42
|
-
fixed = fixed.replace(/Ben\s+MiniMax[^.,!?\n]*/gi, 'Ben ' + (botName || 'asistan'));
|
|
43
|
-
fixed = fixed.replace(/I'm\s+MiniMax[^.,!?\n]*/gi, "I'm " + (botName || 'asistan'));
|
|
44
|
-
fixed = fixed.replace(/I am\s+Claude[^.,!?\n]*/gi, 'I am ' + (botName || 'asistan'));
|
|
45
|
-
return fixed;
|
|
46
|
-
}
|
|
47
|
-
global.fixModelNameLeak = fixModelNameLeak;
|
|
48
|
-
|
|
49
|
-
// v4.8.0: Tool definitions — başlangıçta bir kez yükle (performans)
|
|
50
|
-
let _toolDefs = null;
|
|
51
|
-
function getToolDefs() {
|
|
52
|
-
if (!_toolDefs) {
|
|
53
|
-
try {
|
|
54
|
-
_toolDefs = loadToolDefinitions();
|
|
55
|
-
} catch (e) {
|
|
56
|
-
_toolDefs = [];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return _toolDefs;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ── System prompt tier cache (Hermes-style prefix cache warmth) ────────────
|
|
63
|
-
// stable+context built once at session start, volatile rebuilt per turn.
|
|
64
|
-
let _cachedStable = '';
|
|
65
|
-
let _cachedContext = '';
|
|
66
|
-
let _cachedTierOpts = null; // opts snapshot for volatile-only rebuilds
|
|
67
|
-
|
|
68
|
-
function rebuildSystemPrompt(opts) {
|
|
69
|
-
// If stable/context opts changed, rebuild them too
|
|
70
|
-
const needsFullRebuild = !_cachedTierOpts ||
|
|
71
|
-
_cachedTierOpts.botName !== opts.botName ||
|
|
72
|
-
_cachedTierOpts.soulSummary !== opts.soulSummary ||
|
|
73
|
-
_cachedTierOpts.skillsIndexBlock !== opts.skillsIndexBlock ||
|
|
74
|
-
_cachedTierOpts.crossSessionContext !== opts.crossSessionContext ||
|
|
75
|
-
_cachedTierOpts.projectRules !== opts.projectRules;
|
|
76
|
-
|
|
77
|
-
if (needsFullRebuild || !_cachedStable) {
|
|
78
|
-
const tiers = buildTiers(opts);
|
|
79
|
-
_cachedStable = tiers.stable;
|
|
80
|
-
_cachedContext = tiers.context;
|
|
81
|
-
_cachedTierOpts = {
|
|
82
|
-
botName: opts.botName,
|
|
83
|
-
soulSummary: opts.soulSummary,
|
|
84
|
-
skillsIndexBlock: opts.skillsIndexBlock,
|
|
85
|
-
crossSessionContext: opts.crossSessionContext,
|
|
86
|
-
projectRules: opts.projectRules,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
// Volatile always rebuilt fresh
|
|
90
|
-
const volatileOnly = buildTiers({
|
|
91
|
-
...opts,
|
|
92
|
-
// Pass empty strings for stable/context fields so buildTiers only builds volatile
|
|
93
|
-
botName: '',
|
|
94
|
-
soulSummary: '',
|
|
95
|
-
skillsIndexBlock: '',
|
|
96
|
-
crossSessionContext: '',
|
|
97
|
-
projectRules: '',
|
|
98
|
-
});
|
|
99
|
-
return assemble(_cachedStable, _cachedContext, volatileOnly.volatile);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// ── Tool Guardrails instance (Hermes-style) ─────────────────────────────
|
|
103
|
-
const guardrails = new ToolGuardrails();
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
'/
|
|
109
|
-
'/
|
|
110
|
-
'/
|
|
111
|
-
'/
|
|
112
|
-
'/
|
|
113
|
-
'/
|
|
114
|
-
'/
|
|
115
|
-
'/
|
|
116
|
-
'/
|
|
117
|
-
'/
|
|
118
|
-
'/
|
|
119
|
-
'/
|
|
120
|
-
'/
|
|
121
|
-
'/
|
|
122
|
-
'/
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
idx
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
{ re: /(
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
},
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
//
|
|
675
|
-
if (delta.
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
currentMessages.push(
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
pm
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
console.log(
|
|
973
|
-
console.log(' ' + chalk.yellow('/
|
|
974
|
-
console.log(' ' + chalk.yellow('/
|
|
975
|
-
console.log(' ' + chalk.yellow('/
|
|
976
|
-
console.log(' ' + chalk.yellow('/
|
|
977
|
-
console.log(' ' + chalk.yellow('/
|
|
978
|
-
console.log(' ' + chalk.yellow('/
|
|
979
|
-
console.log(' ' + chalk.yellow('/
|
|
980
|
-
console.log(chalk.
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
console.log('');
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
//
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
//
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
}
|
|
1128
|
-
console.log(tui.C.muted('
|
|
1129
|
-
console.log('');
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
memory.
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
case '
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
console.log(
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
console.log('');
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
console.log(chalk.
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
break;
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
break;
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
process.stdout.write('
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
fixedReply =
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
const
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
(
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
fixedReply =
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1
|
+
/**
|
|
2
|
+
* natureco repl — Persistent Interactive REPL
|
|
3
|
+
*
|
|
4
|
+
* Özellikler:
|
|
5
|
+
* ✅ Cross-session hafıza (memory dosyası)
|
|
6
|
+
* ✅ Otomatik fact extraction (LLM konuşmadan öğrenir)
|
|
7
|
+
* ✅ Session resume (--resume ile kaldığın yerden devam)
|
|
8
|
+
* ✅ Tüm CLI komutları REPL içinden (/doctor, /cost, /audit, /team...)
|
|
9
|
+
* ✅ Slash komutlar (/help, /memory, /model, /system, /exit)
|
|
10
|
+
* ✅ Ctrl+C temiz çıkış, konuşma otomatik kayıt
|
|
11
|
+
* ✅ Persistent session list (~/.natureco/sessions.json)
|
|
12
|
+
* ✅ Token tracking
|
|
13
|
+
*
|
|
14
|
+
* v4.6.0 — Persistent Memory Edition
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const readline = require('readline');
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const os = require('os');
|
|
21
|
+
const https = require('https');
|
|
22
|
+
const { spawn } = require('child_process');
|
|
23
|
+
const chalk = require('chalk');
|
|
24
|
+
const tui = require('../utils/tui');
|
|
25
|
+
const { loadToolDefinitions, toOpenAIFormat, executeTool } = require('../utils/tools');
|
|
26
|
+
const { accumulateToolCallDeltas, finalizeToolCalls } = require('../utils/streaming-tools');
|
|
27
|
+
const { createPasteSafeInput, createOutputFilter, enableBracketedPaste, disableBracketedPaste, restoreNewlines, clearPasteContext } = require('../utils/paste-safe-input');
|
|
28
|
+
const { getMemoryStore } = require('../utils/memory-store');
|
|
29
|
+
const { buildSkillIndex } = require('../utils/skill-index');
|
|
30
|
+
const { buildTiers, assemble, discoverProjectRules } = require('../utils/system-prompt');
|
|
31
|
+
const { ToolGuardrails } = require('../utils/tool-guardrails');
|
|
32
|
+
|
|
33
|
+
// v5.4.6: Model adi sizintisini engelle — global'e ata, callback'lerden erisebilir olsun
|
|
34
|
+
const MODEL_NAMES_TO_HIDE = ['MiniMax-M2.5', 'MiniMaxM2.5', 'minimaxm25', 'Claude-3', 'GPT-4', 'ChatGPT'];
|
|
35
|
+
function fixModelNameLeak(text, botName) {
|
|
36
|
+
if (!text) return text;
|
|
37
|
+
let fixed = text;
|
|
38
|
+
for (const modelName of MODEL_NAMES_TO_HIDE) {
|
|
39
|
+
const regex = new RegExp(modelName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
|
|
40
|
+
fixed = fixed.replace(regex, botName || 'asistan');
|
|
41
|
+
}
|
|
42
|
+
fixed = fixed.replace(/Ben\s+MiniMax[^.,!?\n]*/gi, 'Ben ' + (botName || 'asistan'));
|
|
43
|
+
fixed = fixed.replace(/I'm\s+MiniMax[^.,!?\n]*/gi, "I'm " + (botName || 'asistan'));
|
|
44
|
+
fixed = fixed.replace(/I am\s+Claude[^.,!?\n]*/gi, 'I am ' + (botName || 'asistan'));
|
|
45
|
+
return fixed;
|
|
46
|
+
}
|
|
47
|
+
global.fixModelNameLeak = fixModelNameLeak;
|
|
48
|
+
|
|
49
|
+
// v4.8.0: Tool definitions — başlangıçta bir kez yükle (performans)
|
|
50
|
+
let _toolDefs = null;
|
|
51
|
+
function getToolDefs() {
|
|
52
|
+
if (!_toolDefs) {
|
|
53
|
+
try {
|
|
54
|
+
_toolDefs = loadToolDefinitions();
|
|
55
|
+
} catch (e) {
|
|
56
|
+
_toolDefs = [];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return _toolDefs;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ── System prompt tier cache (Hermes-style prefix cache warmth) ────────────
|
|
63
|
+
// stable+context built once at session start, volatile rebuilt per turn.
|
|
64
|
+
let _cachedStable = '';
|
|
65
|
+
let _cachedContext = '';
|
|
66
|
+
let _cachedTierOpts = null; // opts snapshot for volatile-only rebuilds
|
|
67
|
+
|
|
68
|
+
function rebuildSystemPrompt(opts) {
|
|
69
|
+
// If stable/context opts changed, rebuild them too
|
|
70
|
+
const needsFullRebuild = !_cachedTierOpts ||
|
|
71
|
+
_cachedTierOpts.botName !== opts.botName ||
|
|
72
|
+
_cachedTierOpts.soulSummary !== opts.soulSummary ||
|
|
73
|
+
_cachedTierOpts.skillsIndexBlock !== opts.skillsIndexBlock ||
|
|
74
|
+
_cachedTierOpts.crossSessionContext !== opts.crossSessionContext ||
|
|
75
|
+
_cachedTierOpts.projectRules !== opts.projectRules;
|
|
76
|
+
|
|
77
|
+
if (needsFullRebuild || !_cachedStable) {
|
|
78
|
+
const tiers = buildTiers(opts);
|
|
79
|
+
_cachedStable = tiers.stable;
|
|
80
|
+
_cachedContext = tiers.context;
|
|
81
|
+
_cachedTierOpts = {
|
|
82
|
+
botName: opts.botName,
|
|
83
|
+
soulSummary: opts.soulSummary,
|
|
84
|
+
skillsIndexBlock: opts.skillsIndexBlock,
|
|
85
|
+
crossSessionContext: opts.crossSessionContext,
|
|
86
|
+
projectRules: opts.projectRules,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// Volatile always rebuilt fresh
|
|
90
|
+
const volatileOnly = buildTiers({
|
|
91
|
+
...opts,
|
|
92
|
+
// Pass empty strings for stable/context fields so buildTiers only builds volatile
|
|
93
|
+
botName: '',
|
|
94
|
+
soulSummary: '',
|
|
95
|
+
skillsIndexBlock: '',
|
|
96
|
+
crossSessionContext: '',
|
|
97
|
+
projectRules: '',
|
|
98
|
+
});
|
|
99
|
+
return assemble(_cachedStable, _cachedContext, volatileOnly.volatile);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── Tool Guardrails instance (Hermes-style) ─────────────────────────────
|
|
103
|
+
const guardrails = new ToolGuardrails();
|
|
104
|
+
const noopCallback = () => {};
|
|
105
|
+
|
|
106
|
+
// CLI komutları (REPL içinden çalıştırılabilir)
|
|
107
|
+
const CLI_COMMANDS = {
|
|
108
|
+
'/doctor': { desc: 'Sistem sağlığı kontrolü', run: ['doctor'] },
|
|
109
|
+
'/cost': { desc: 'Maliyet takibi (today|week|month|budget)', run: ['cost', 'today'] },
|
|
110
|
+
'/audit': { desc: 'Audit log (today|stats|files)', run: ['audit', 'stats'] },
|
|
111
|
+
'/team': { desc: 'Multi-agent (list|status)', run: ['team', 'list'] },
|
|
112
|
+
'/xp': { desc: 'XP/Level durumu', run: ['xp'] },
|
|
113
|
+
'/skills': { desc: 'Yüklü skill listesi', run: ['skills', 'list'] },
|
|
114
|
+
'/status': { desc: 'Sistem durumu', run: ['status'] },
|
|
115
|
+
'/mcp': { desc: 'MCP sunucuları', run: ['mcp', 'list'] },
|
|
116
|
+
'/channels': { desc: 'Bağlı kanallar', run: ['channels'] },
|
|
117
|
+
'/crons': { desc: 'Cron görevleri', run: ['cron', 'list'] },
|
|
118
|
+
'/bots': { desc: 'Bot listesi', run: ['bots'] },
|
|
119
|
+
'/models': { desc: 'Modeller', run: ['models', 'list'] },
|
|
120
|
+
'/memory-ls': { desc: 'Memory dosyaları', run: ['memory', 'list'] },
|
|
121
|
+
'/seo': { desc: 'SEO denetimi (URL gerek)', needsArg: true, run: ['seo', 'audit'] },
|
|
122
|
+
'/naturehub': { desc: 'NatureHub işlemleri (post|dm|inbox|forum|list)', needsArg: true, run: ['naturehub', 'post'] },
|
|
123
|
+
'/dashboard': { desc: 'Web dashboard başlat (port 7421)', run: ['dashboard', 'start'] },
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const MEMORY_DIR = path.join(os.homedir(), '.natureco', 'memory');
|
|
127
|
+
const SESSION_DIR = path.join(os.homedir(), '.natureco', 'sessions');
|
|
128
|
+
const SESSIONS_INDEX = path.join(os.homedir(), '.natureco', 'sessions.json');
|
|
129
|
+
const REPL_STATE = path.join(os.homedir(), '.natureco', 'repl-state.json');
|
|
130
|
+
|
|
131
|
+
function ensureDir(dir) {
|
|
132
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getConfig() {
|
|
136
|
+
try {
|
|
137
|
+
return JSON.parse(fs.readFileSync(path.join(os.homedir(), '.natureco', 'config.json'), 'utf8'));
|
|
138
|
+
} catch { return {}; }
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function isMiniMax(url) {
|
|
142
|
+
return url && (url.includes('minimax.io') || url.includes('minimaxi.com') || url.includes('minimax.cn'));
|
|
143
|
+
}
|
|
144
|
+
function isGemini(url) {
|
|
145
|
+
return url && (url.includes('generativelanguage.googleapis.com') || url.includes('gemini'));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function loadMemory(username) {
|
|
149
|
+
const file = path.join(MEMORY_DIR, `${(username || 'default').toLowerCase()}.json`);
|
|
150
|
+
try {
|
|
151
|
+
if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
152
|
+
} catch {}
|
|
153
|
+
return { name: username || 'Kullanıcı', nickname: null, botName: 'Asistan', facts: [], preferences: [], history: [] };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function saveMemory(username, memory) {
|
|
157
|
+
ensureDir(MEMORY_DIR);
|
|
158
|
+
const file = path.join(MEMORY_DIR, `${(username || 'default').toLowerCase()}.json`);
|
|
159
|
+
memory.lastUpdated = new Date().toISOString();
|
|
160
|
+
fs.writeFileSync(file, JSON.stringify(memory, null, 2));
|
|
161
|
+
return file;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function loadSessionsIndex() {
|
|
165
|
+
try {
|
|
166
|
+
if (fs.existsSync(SESSIONS_INDEX)) return JSON.parse(fs.readFileSync(SESSIONS_INDEX, 'utf8'));
|
|
167
|
+
} catch {}
|
|
168
|
+
return { sessions: [] };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function saveSessionsIndex(index) {
|
|
172
|
+
fs.writeFileSync(SESSIONS_INDEX, JSON.stringify(index, null, 2));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function saveSession(messages, meta) {
|
|
176
|
+
ensureDir(SESSION_DIR);
|
|
177
|
+
const id = `sess-${Date.now().toString(36)}`;
|
|
178
|
+
const file = path.join(SESSION_DIR, `${id}.json`);
|
|
179
|
+
const data = {
|
|
180
|
+
id,
|
|
181
|
+
createdAt: new Date().toISOString(),
|
|
182
|
+
updatedAt: new Date().toISOString(),
|
|
183
|
+
messageCount: messages.length,
|
|
184
|
+
...meta,
|
|
185
|
+
messages: messages.filter(m => !m._internal),
|
|
186
|
+
};
|
|
187
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2));
|
|
188
|
+
// Index güncelle
|
|
189
|
+
const idx = loadSessionsIndex();
|
|
190
|
+
idx.sessions.unshift({
|
|
191
|
+
id, file, createdAt: data.createdAt, messageCount: messages.length,
|
|
192
|
+
firstUserMessage: messages.find(m => m.role === 'user')?.content?.slice(0, 60) || '(boş)',
|
|
193
|
+
});
|
|
194
|
+
// Son 50 session tut
|
|
195
|
+
idx.sessions = idx.sessions.slice(0, 50);
|
|
196
|
+
saveSessionsIndex(idx);
|
|
197
|
+
return id;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
function loadSession(id) {
|
|
203
|
+
// ID veya index
|
|
204
|
+
const idx = loadSessionsIndex();
|
|
205
|
+
let meta;
|
|
206
|
+
if (id === 'last' || id === 'latest') {
|
|
207
|
+
meta = idx.sessions[0];
|
|
208
|
+
} else {
|
|
209
|
+
meta = idx.sessions.find(s => s.id === id || s.id.endsWith(id));
|
|
210
|
+
}
|
|
211
|
+
if (!meta) return null;
|
|
212
|
+
try {
|
|
213
|
+
return JSON.parse(fs.readFileSync(meta.file, 'utf8'));
|
|
214
|
+
} catch { return null; }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function extractFacts(messages, currentFacts) {
|
|
218
|
+
// Basit fact extraction: Türkçe/İngilizce pattern'lerle kullanıcı hakkında bilgi çıkar
|
|
219
|
+
// Production'da LLM ile yapılabilir, şimdilik pattern matching
|
|
220
|
+
const newFacts = [];
|
|
221
|
+
const userMessages = messages.filter(m => m.role === 'user' && !m._internal);
|
|
222
|
+
const existingValues = new Set((currentFacts || []).map(f => (f.value || f).toLowerCase()));
|
|
223
|
+
|
|
224
|
+
for (const msg of userMessages) {
|
|
225
|
+
const text = msg.content || '';
|
|
226
|
+
const lower = text.toLowerCase();
|
|
227
|
+
|
|
228
|
+
// İsim/tercih pattern'leri
|
|
229
|
+
const patterns = [
|
|
230
|
+
{ re: /(?:benim ad[ıi]m|ad[ıi]m|ad[ıi]n|ad[ıi]n[ıi]z|ismim|isim|I'm called|my name is)\s+([A-ZÇĞİÖŞÜa-zçğıöşü]+)/i, val: m => `Adı: ${m[1]}` },
|
|
231
|
+
{ re: /(?:yaşıyorum|yaşadığım|oturuyorum|I live in)\s+([A-ZÇĞİÖŞÜa-zçğıöşü\s]+)/i, val: m => `Yaşadığı yer: ${m[1].trim()}` },
|
|
232
|
+
{ re: /([A-ZÇĞİÖŞÜa-zçğıöşü]{2,})[''](?:da|de|ta|te)\s+(?:yaşıyorum|yaşadığım|oturuyorum|bulunuyorum|kalıyorum)/i, val: m => `Yaşadığı yer: ${m[1]}` },
|
|
233
|
+
{ re: /(?:seviyorum|severim|sevdiğim|hoşlanırım|beğenirim|like|love)\s+(?:şu|bu)?\s*([a-zA-ZçğıöşüÇĞİÖŞÜ\s]{2,30})/i, val: m => `Sevdiği şey: ${m[1].trim()}` },
|
|
234
|
+
{ re: /(?:çalışıyorum|işim|mesleğim|çalışırım|I work as)\s+([A-ZÇĞİÖŞÜa-zçğıöşü\s]+)/i, val: m => `Meslek: ${m[1].trim()}` },
|
|
235
|
+
{ re: /(\d{1,3})\s*(?:yaş[ıi]nday[ıi]m|yaş[ıi]nda)/i, val: m => `Yaş: ${m[1]}` },
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
for (const p of patterns) {
|
|
239
|
+
const m = text.match(p.re);
|
|
240
|
+
if (m && m[1]) {
|
|
241
|
+
const val = p.val(m);
|
|
242
|
+
if (val && !existingValues.has(val.toLowerCase())) {
|
|
243
|
+
newFacts.push({ value: val, score: 5, learnedAt: new Date().toISOString() });
|
|
244
|
+
existingValues.add(val.toLowerCase());
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return newFacts;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function apiRequest(providerUrl, providerApiKey, body, stream = false, retries = 3) {
|
|
253
|
+
return new Promise((resolve, reject) => {
|
|
254
|
+
const isMM = isMiniMax(providerUrl);
|
|
255
|
+
const endpoint = isMM
|
|
256
|
+
? `${providerUrl.replace(/\/+$/, '')}/v1/text/chatcompletion_v2`
|
|
257
|
+
: isGemini(providerUrl)
|
|
258
|
+
? `${providerUrl.replace(/\/+$/, '')}/openai/chat/completions`
|
|
259
|
+
: `${providerUrl.replace(/\/+$/, '')}/chat/completions`;
|
|
260
|
+
const doRequest = (attempt) => {
|
|
261
|
+
const req = https.request(endpoint, {
|
|
262
|
+
method: 'POST',
|
|
263
|
+
headers: {
|
|
264
|
+
'Authorization': `Bearer ${providerApiKey}`,
|
|
265
|
+
'Content-Type': 'application/json',
|
|
266
|
+
},
|
|
267
|
+
timeout: 60000,
|
|
268
|
+
}, (res) => {
|
|
269
|
+
if (stream) { resolve(res); return; }
|
|
270
|
+
let data = '';
|
|
271
|
+
res.on('data', c => data += c);
|
|
272
|
+
res.on('end', () => {
|
|
273
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
274
|
+
try { resolve(JSON.parse(data)); } catch (e) { reject(new Error('Parse hatası')); }
|
|
275
|
+
} else if (res.statusCode === 429 && attempt < retries) {
|
|
276
|
+
const delay = Math.pow(2, attempt) * 1000;
|
|
277
|
+
setTimeout(() => doRequest(attempt + 1), delay);
|
|
278
|
+
} else {
|
|
279
|
+
const msg = res.statusCode === 429
|
|
280
|
+
? 'HTTP 429: API rate limit aşıldı. Lütfen bekleyin veya planınızı yükseltin.'
|
|
281
|
+
: `HTTP ${res.statusCode}: ${data.slice(0, 200)}`;
|
|
282
|
+
reject(new Error(msg));
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
req.on('error', reject);
|
|
287
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
|
|
288
|
+
req.write(JSON.stringify(body));
|
|
289
|
+
req.end();
|
|
290
|
+
};
|
|
291
|
+
doRequest(0);
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async function sendStreaming(providerUrl, providerApiKey, messages, model, onChunk, onToolCall) {
|
|
296
|
+
const isMM = isMiniMax(providerUrl);
|
|
297
|
+
const isGM = isGemini(providerUrl);
|
|
298
|
+
const planMode = getPlanMode();
|
|
299
|
+
|
|
300
|
+
// Simple stdin prompt fallback (no rl dependency)
|
|
301
|
+
function stdinPrompt(question, cb) {
|
|
302
|
+
process.stdout.write(question);
|
|
303
|
+
const stdin = process.stdin;
|
|
304
|
+
const onData = (data) => {
|
|
305
|
+
stdin.removeListener('data', onData);
|
|
306
|
+
stdin.pause();
|
|
307
|
+
cb(data.toString().trim());
|
|
308
|
+
};
|
|
309
|
+
stdin.resume();
|
|
310
|
+
stdin.on('data', onData);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const toolDefs = getToolDefs();
|
|
314
|
+
// Inject plan mode virtual tools
|
|
315
|
+
const planToolDefs = [
|
|
316
|
+
{
|
|
317
|
+
name: 'EnterPlanMode',
|
|
318
|
+
description: 'Switch to plan-only mode. Research, explore, and create a detailed plan without making changes. Use ExitPlanMode when ready.',
|
|
319
|
+
parameters: { type: 'object', properties: {}, required: [] },
|
|
320
|
+
execute: async () => {
|
|
321
|
+
if (planMode.enter()) return { result: 'Plan mode activated. You can now research and plan. No changes will be made. Use ExitPlanMode when your plan is ready.' };
|
|
322
|
+
return { result: 'Already in plan mode.' };
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: 'ExitPlanMode',
|
|
327
|
+
description: 'Exit plan mode and present your plan for review. The plan must include steps, files to modify, and expected changes.',
|
|
328
|
+
parameters: {
|
|
329
|
+
type: 'object',
|
|
330
|
+
properties: {
|
|
331
|
+
plan: { type: 'string', description: 'Markdown plan with ## Plan title, ### Step N sections listing files and changes' },
|
|
332
|
+
summary: { type: 'string', description: 'One-sentence summary of the plan' },
|
|
333
|
+
},
|
|
334
|
+
required: ['plan'],
|
|
335
|
+
},
|
|
336
|
+
execute: async (args) => {
|
|
337
|
+
const ok = planMode.exit(args.plan);
|
|
338
|
+
if (!ok) return { error: 'Not in plan mode.' };
|
|
339
|
+
return {
|
|
340
|
+
result: `Plan submitted for review.\n\n${args.plan}`,
|
|
341
|
+
_plan_summary: args.summary || '',
|
|
342
|
+
};
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
];
|
|
346
|
+
toolDefs.push(...planToolDefs);
|
|
347
|
+
|
|
348
|
+
// Inject worktree virtual tools
|
|
349
|
+
const wt = getWorktree();
|
|
350
|
+
const worktreeToolDefs = [
|
|
351
|
+
{
|
|
352
|
+
name: 'EnterWorktree',
|
|
353
|
+
description: 'Create an isolated worktree for experimental changes without affecting the main project. Use ExitWorktree to merge changes back.',
|
|
354
|
+
parameters: {
|
|
355
|
+
type: 'object',
|
|
356
|
+
properties: {
|
|
357
|
+
branch: { type: 'string', description: 'Optional branch name for the worktree' },
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
execute: async (args) => wt.enter(args),
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
name: 'ExitWorktree',
|
|
364
|
+
description: 'Exit the current worktree and merge changes back to the main branch.',
|
|
365
|
+
parameters: {
|
|
366
|
+
type: 'object',
|
|
367
|
+
properties: {
|
|
368
|
+
merge: { type: 'boolean', description: 'Merge changes back (default: true)' },
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
execute: async (args) => wt.exit(args),
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
toolDefs.push(...worktreeToolDefs);
|
|
375
|
+
|
|
376
|
+
// Inject tool-level virtual tools
|
|
377
|
+
const { getTaskManager } = require('../utils/tasks');
|
|
378
|
+
const taskMgr = getTaskManager();
|
|
379
|
+
const toolVirtualTools = [
|
|
380
|
+
{
|
|
381
|
+
name: 'CreateTask',
|
|
382
|
+
description: 'Run a command in the background. Returns immediately with a task ID. Use GetTaskResult or ListTasks to check status.',
|
|
383
|
+
parameters: {
|
|
384
|
+
type: 'object',
|
|
385
|
+
properties: {
|
|
386
|
+
command: { type: 'string', description: 'Shell command to run' },
|
|
387
|
+
timeout: { type: 'number', description: 'Timeout in ms (default: 300000)' },
|
|
388
|
+
},
|
|
389
|
+
required: ['command'],
|
|
390
|
+
},
|
|
391
|
+
execute: async (args) => taskMgr.create(args.command, args),
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: 'ListTasks',
|
|
395
|
+
description: 'List all background tasks with their status.',
|
|
396
|
+
parameters: { type: 'object', properties: {}, required: [] },
|
|
397
|
+
execute: async () => ({ tasks: taskMgr.list() }),
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: 'GetTaskResult',
|
|
401
|
+
description: 'Get the full result (stdout/stderr) of a completed or running task.',
|
|
402
|
+
parameters: {
|
|
403
|
+
type: 'object',
|
|
404
|
+
properties: { taskId: { type: 'string', description: 'Task ID' } },
|
|
405
|
+
required: ['taskId'],
|
|
406
|
+
},
|
|
407
|
+
execute: async (args) => {
|
|
408
|
+
const task = taskMgr.get(args.taskId);
|
|
409
|
+
if (!task) return { error: 'Task not found' };
|
|
410
|
+
return { result: task.stdout, error: task.stderr, status: task.status, exitCode: task.exitCode };
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: 'StopTask',
|
|
415
|
+
description: 'Stop a running background task.',
|
|
416
|
+
parameters: {
|
|
417
|
+
type: 'object',
|
|
418
|
+
properties: { taskId: { type: 'string' } },
|
|
419
|
+
required: ['taskId'],
|
|
420
|
+
},
|
|
421
|
+
execute: async (args) => taskMgr.stop(args.taskId),
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: 'SearchSessions',
|
|
425
|
+
description: 'Search past conversation sessions for information. Useful for finding previously discussed topics or decisions.',
|
|
426
|
+
parameters: {
|
|
427
|
+
type: 'object',
|
|
428
|
+
properties: { query: { type: 'string', description: 'Search query' } },
|
|
429
|
+
required: ['query'],
|
|
430
|
+
},
|
|
431
|
+
execute: async (args) => {
|
|
432
|
+
const { search } = require('../utils/session-search');
|
|
433
|
+
return { results: search(args.query, 5) };
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
name: 'RestoreFile',
|
|
438
|
+
description: 'Restore a file from its snapshot history. Use FileHistory first to list available snapshots.',
|
|
439
|
+
parameters: {
|
|
440
|
+
type: 'object',
|
|
441
|
+
properties: {
|
|
442
|
+
filePath: { type: 'string', description: 'File path to restore' },
|
|
443
|
+
timestamp: { type: 'number', description: 'Snapshot timestamp (from FileHistory). If omitted, restores previous version.' },
|
|
444
|
+
},
|
|
445
|
+
required: ['filePath'],
|
|
446
|
+
},
|
|
447
|
+
execute: async (args) => {
|
|
448
|
+
const fh = require('../utils/file-history');
|
|
449
|
+
const snaps = fh.getHistory(args.filePath);
|
|
450
|
+
if (snaps.length === 0) return { error: 'No history for this file' };
|
|
451
|
+
const ts = args.timestamp || snaps[0].timestamp;
|
|
452
|
+
return fh.restore(args.filePath, ts);
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
name: 'FileHistory',
|
|
457
|
+
description: 'List snapshot history for a file, showing timestamps and sizes of previous versions.',
|
|
458
|
+
parameters: {
|
|
459
|
+
type: 'object',
|
|
460
|
+
properties: { filePath: { type: 'string' } },
|
|
461
|
+
required: ['filePath'],
|
|
462
|
+
},
|
|
463
|
+
execute: async (args) => {
|
|
464
|
+
const fh = require('../utils/file-history');
|
|
465
|
+
return { snapshots: fh.getHistory(args.filePath) };
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
name: 'UltraReview',
|
|
470
|
+
description: 'Run a multi-focus code review (security, style, logic, performance) on specified files or git diff.',
|
|
471
|
+
parameters: {
|
|
472
|
+
type: 'object',
|
|
473
|
+
properties: {
|
|
474
|
+
files: { type: 'array', items: { type: 'string' }, description: 'File paths to review' },
|
|
475
|
+
diff: { type: 'string', description: 'Git diff content to review instead of files' },
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
execute: async (args) => {
|
|
479
|
+
const ur = require('../utils/ultra-review');
|
|
480
|
+
if (args.diff) return ur.reviewDiff(args.diff);
|
|
481
|
+
if (args.files) {
|
|
482
|
+
const fs = require('fs');
|
|
483
|
+
return {
|
|
484
|
+
reviews: args.files.map(f => {
|
|
485
|
+
const content = fs.existsSync(f) ? fs.readFileSync(f, 'utf8') : '';
|
|
486
|
+
return ur.reviewFile(f, content);
|
|
487
|
+
}),
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
return { error: 'Specify files or diff' };
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
name: 'ScheduleTask',
|
|
495
|
+
description: 'Schedule a recurring task using cron expressions. E.g., "*/5 * * * *" for every 5 min.',
|
|
496
|
+
parameters: {
|
|
497
|
+
type: 'object',
|
|
498
|
+
properties: {
|
|
499
|
+
schedule: { type: 'string', description: 'Cron expression: min hour dom mon dow' },
|
|
500
|
+
command: { type: 'string', description: 'Command to run' },
|
|
501
|
+
description: { type: 'string', description: 'Optional description' },
|
|
502
|
+
},
|
|
503
|
+
required: ['schedule', 'command'],
|
|
504
|
+
},
|
|
505
|
+
execute: async (args) => require('../utils/cron').addJob(args),
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
name: 'ListScheduledTasks',
|
|
509
|
+
description: 'List all scheduled cron tasks.',
|
|
510
|
+
parameters: { type: 'object', properties: {} },
|
|
511
|
+
execute: async () => ({ jobs: require('../utils/cron').loadJobs() }),
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
name: 'RemoveScheduledTask',
|
|
515
|
+
description: 'Remove a scheduled task by ID.',
|
|
516
|
+
parameters: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
|
|
517
|
+
execute: async (args) => {
|
|
518
|
+
require('../utils/cron').removeJob(args.id);
|
|
519
|
+
return { removed: true };
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
];
|
|
523
|
+
toolDefs.push(...toolVirtualTools);
|
|
524
|
+
|
|
525
|
+
const toolParam = toOpenAIFormat(toolDefs);
|
|
526
|
+
guardrails.reset();
|
|
527
|
+
|
|
528
|
+
let currentMessages = messages;
|
|
529
|
+
let fullText = '';
|
|
530
|
+
let iterations = 0;
|
|
531
|
+
const MAX_TOOL_ITERATIONS = 50;
|
|
532
|
+
const MAX_CONTEXT_TOKENS = 32000; // safety limit before compression
|
|
533
|
+
|
|
534
|
+
// v5.7.18: Preflight compression — if context too long, compress middle messages
|
|
535
|
+
// (like Hermes' turn_context.py preflight)
|
|
536
|
+
function preflightCompress(msgs) {
|
|
537
|
+
const roughTokens = msgs.reduce((s, m) => s + Math.ceil(((m.content || '') + (m.role || '')).length / 4), 0);
|
|
538
|
+
if (roughTokens <= MAX_CONTEXT_TOKENS || msgs.length < 6) return msgs;
|
|
539
|
+
|
|
540
|
+
// Keep system prompt (first message) + last N turns (tail), compress middle
|
|
541
|
+
const sysMsg = msgs[0] && msgs[0].role === 'system' ? msgs[0] : null;
|
|
542
|
+
const tailCount = Math.min(6, Math.floor(msgs.length / 2));
|
|
543
|
+
const startIdx = sysMsg ? 1 : 0;
|
|
544
|
+
const tailStart = msgs.length - tailCount;
|
|
545
|
+
|
|
546
|
+
const middle = msgs.slice(startIdx, tailStart);
|
|
547
|
+
if (middle.length < 2) return msgs;
|
|
548
|
+
|
|
549
|
+
// Summarize middle section
|
|
550
|
+
const summary = '[' + middle.length + ' onceki mesaj ozetlendi]';
|
|
551
|
+
const compressed = sysMsg ? [sysMsg] : [];
|
|
552
|
+
compressed.push({ role: 'system', content: 'Gecmis konusma ozeti: ' + summary, _compressed: true });
|
|
553
|
+
compressed.push(...msgs.slice(tailStart));
|
|
554
|
+
return compressed;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Apply preflight on entry
|
|
558
|
+
currentMessages = preflightCompress(currentMessages);
|
|
559
|
+
|
|
560
|
+
while (iterations < 50) {
|
|
561
|
+
let effortLevel = 'medium', effortCfg;
|
|
562
|
+
let cfg;
|
|
563
|
+
try {
|
|
564
|
+
cfg = require('../utils/config').getConfig();
|
|
565
|
+
effortLevel = getEffortLevel(cfg);
|
|
566
|
+
effortCfg = getEffortConfig(effortLevel);
|
|
567
|
+
} catch { effortCfg = getEffortConfig(effortLevel); }
|
|
568
|
+
const maxIter = effortCfg ? effortCfg.maxToolIterations : 50;
|
|
569
|
+
iterations++;
|
|
570
|
+
// v5.7.18: Preflight compress before each iteration to prevent context bloat
|
|
571
|
+
currentMessages = preflightCompress(currentMessages);
|
|
572
|
+
const shouldStream = !isMM && !isGM; // MiniMax + Gemini non-stream (tool_calls reliability)
|
|
573
|
+
// Inject plan mode prompt into system message
|
|
574
|
+
const planModePrompt = planMode.getSystemPrompt();
|
|
575
|
+
if (planModePrompt) {
|
|
576
|
+
const sysIdx = currentMessages.findIndex(m => m.role === 'system');
|
|
577
|
+
if (sysIdx >= 0) {
|
|
578
|
+
const base = currentMessages[sysIdx].content;
|
|
579
|
+
if (!base.endsWith(planModePrompt)) {
|
|
580
|
+
currentMessages[sysIdx] = { ...currentMessages[sysIdx], content: base + '\n\n' + planModePrompt };
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
} else {
|
|
584
|
+
// Clean up any stale plan mode prompt from previous iterations
|
|
585
|
+
const sysIdx = currentMessages.findIndex(m => m.role === 'system');
|
|
586
|
+
if (sysIdx >= 0) {
|
|
587
|
+
const base = currentMessages[sysIdx].content;
|
|
588
|
+
const marker = '\n\nYou are in PLAN MODE.';
|
|
589
|
+
if (base.includes(marker)) {
|
|
590
|
+
currentMessages[sysIdx] = { ...currentMessages[sysIdx], content: base.split('\n\nYou are in PLAN MODE.')[0] };
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
const fallbackChain = getFallbackChain();
|
|
596
|
+
|
|
597
|
+
const body = {
|
|
598
|
+
model: fallbackChain.current || model,
|
|
599
|
+
messages: currentMessages,
|
|
600
|
+
stream: shouldStream,
|
|
601
|
+
temperature: effortCfg.temperature,
|
|
602
|
+
max_tokens: effortCfg.maxTokens,
|
|
603
|
+
};
|
|
604
|
+
// Structured output support
|
|
605
|
+
const respFmt = getResponseFormat(cfg);
|
|
606
|
+
if (respFmt) body.response_format = respFmt;
|
|
607
|
+
if (toolParam) body.tools = toolParam;
|
|
608
|
+
if (isMM || isGM) body.tool_choice = 'auto'; // MiniMax + Gemini için explicit
|
|
609
|
+
|
|
610
|
+
if (!shouldStream) {
|
|
611
|
+
// MiniMax (non-stream) — tool_calls desteklemiyor varsayalım
|
|
612
|
+
const res = await apiRequest(providerUrl, providerApiKey, body, false);
|
|
613
|
+
const msg = res.choices?.[0]?.message || {};
|
|
614
|
+
const content = msg.content || '';
|
|
615
|
+
if (onChunk !== noopCallback) {
|
|
616
|
+
for (const char of content) {
|
|
617
|
+
onChunk(char);
|
|
618
|
+
await new Promise(r => setTimeout(r, 8));
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
fullText = content;
|
|
622
|
+
// Non-stream tool call desteği
|
|
623
|
+
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
624
|
+
const toolResults = await processToolCalls(msg.tool_calls, onToolCall, stdinPrompt);
|
|
625
|
+
currentMessages.push(msg);
|
|
626
|
+
currentMessages.push(...toolResults);
|
|
627
|
+
continue; // Tekrar API çağır
|
|
628
|
+
}
|
|
629
|
+
break;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// OpenAI uyumlu streaming (veya MiniMax /v1/text/chatcompletion_v2)
|
|
633
|
+
// v5.9.5: Gemini /openai/chat/completions — provider-detect.js buildChatEndpoint
|
|
634
|
+
const endpoint = isMM
|
|
635
|
+
? `${providerUrl.replace(/\/+$/, '')}/v1/text/chatcompletion_v2`
|
|
636
|
+
: isGemini(providerUrl)
|
|
637
|
+
? `${providerUrl.replace(/\/+$/, '')}/openai/chat/completions`
|
|
638
|
+
: `${providerUrl.replace(/\/+$/, '')}/chat/completions`;
|
|
639
|
+
let result;
|
|
640
|
+
try {
|
|
641
|
+
result = await new Promise((resolve, reject) => {
|
|
642
|
+
const req = https.request(endpoint, {
|
|
643
|
+
method: 'POST',
|
|
644
|
+
headers: { 'Authorization': `Bearer ${providerApiKey}`, 'Content-Type': 'application/json' },
|
|
645
|
+
timeout: 60000,
|
|
646
|
+
}, (res) => {
|
|
647
|
+
if (res.statusCode !== 200) {
|
|
648
|
+
let data = '';
|
|
649
|
+
res.on('data', c => data += c);
|
|
650
|
+
res.on('end', () => reject(new Error(`HTTP ${res.statusCode}: ${data.slice(0, 200)}`)));
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
let buffer = '';
|
|
654
|
+
let streamText = '';
|
|
655
|
+
const toolCalls = []; // { index, id, name, args }
|
|
656
|
+
res.on('data', (chunk) => {
|
|
657
|
+
buffer += chunk.toString('utf8');
|
|
658
|
+
const lines = buffer.split('\n');
|
|
659
|
+
buffer = lines.pop() || '';
|
|
660
|
+
for (const line of lines) {
|
|
661
|
+
const trimmed = line.trim();
|
|
662
|
+
if (!trimmed || !trimmed.startsWith('data:')) continue;
|
|
663
|
+
const data = trimmed.slice(5).trim();
|
|
664
|
+
if (data === '[DONE]') {
|
|
665
|
+
resolve({ streamText, toolCalls });
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
try {
|
|
669
|
+
const parsed = JSON.parse(data);
|
|
670
|
+
const choice = parsed.choices?.[0];
|
|
671
|
+
if (!choice) continue;
|
|
672
|
+
const delta = choice.delta;
|
|
673
|
+
|
|
674
|
+
// Text content
|
|
675
|
+
if (delta.content) {
|
|
676
|
+
streamText += delta.content;
|
|
677
|
+
onChunk(delta.content);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Tool calls (streaming delta) — shared accumulator,
|
|
681
|
+
// see src/utils/streaming-tools.js for the per-index pattern.
|
|
682
|
+
if (delta.tool_calls) {
|
|
683
|
+
accumulateToolCallDeltas(toolCalls, delta.tool_calls);
|
|
684
|
+
}
|
|
685
|
+
} catch {}
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
res.on('end', () => resolve({ streamText, toolCalls }));
|
|
689
|
+
});
|
|
690
|
+
req.on('error', reject);
|
|
691
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); });
|
|
692
|
+
req.write(JSON.stringify(body));
|
|
693
|
+
req.end();
|
|
694
|
+
});
|
|
695
|
+
} catch (err) {
|
|
696
|
+
// Fallback chain: try next model on error
|
|
697
|
+
const fb = fallbackChain.recordError(body.model, err);
|
|
698
|
+
if (fb.fallback) {
|
|
699
|
+
console.log(tui.C.yellow(`\n ⚠ ${body.model} başarısız → ${fb.nextModel} deneniyor...\n`));
|
|
700
|
+
continue; // Retry with next model
|
|
701
|
+
}
|
|
702
|
+
throw err;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
fullText = result.streamText;
|
|
706
|
+
|
|
707
|
+
// Tool call var mı? finalizeToolCalls drops empty entries + synthesizes ids,
|
|
708
|
+
// so we only need to check the resulting length.
|
|
709
|
+
const finalized = finalizeToolCalls(result.toolCalls);
|
|
710
|
+
if (finalized.length > 0) {
|
|
711
|
+
// Assistant mesajını ekle (tool_calls ile)
|
|
712
|
+
currentMessages.push({
|
|
713
|
+
role: 'assistant',
|
|
714
|
+
content: result.streamText || null,
|
|
715
|
+
tool_calls: finalized,
|
|
716
|
+
});
|
|
717
|
+
// Her tool call'ı çalıştır, sonuçları tool mesajı olarak ekle
|
|
718
|
+
const toolResults = await processToolCalls(finalized, onToolCall, stdinPrompt);
|
|
719
|
+
currentMessages.push(...toolResults);
|
|
720
|
+
|
|
721
|
+
// Plan mode review: plan submitted, wait for user approval
|
|
722
|
+
if (planMode.inReview()) {
|
|
723
|
+
const { plan } = planMode.planHistory[planMode.planHistory.length - 1] || {};
|
|
724
|
+
console.log('\n' + tui.C.cyan(' 📋 Plan sunuldu — onay bekleniyor...'));
|
|
725
|
+
console.log(tui.C.muted(' ' + '─'.repeat(56)));
|
|
726
|
+
console.log(plan ? `\n ${plan.replace(/\n/g, '\n ')}` : '');
|
|
727
|
+
console.log('\n' + tui.C.muted(' ─'.repeat(28)));
|
|
728
|
+
const approved = await new Promise(resolve => {
|
|
729
|
+
stdinPrompt(tui.C.yellow(' Planı onaylıyor musun? [Y=exec, n=reddet, e=düzenle]: '), answer => {
|
|
730
|
+
const key = answer.trim().toLowerCase();
|
|
731
|
+
if (key === 'n' || key === 'no') { planMode.reject(); resolve(false); }
|
|
732
|
+
else if (key === 'e' || key === 'edit') { planMode.reject(); resolve('edit'); }
|
|
733
|
+
else { planMode.approve(); resolve(true); }
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
if (approved === true) {
|
|
737
|
+
console.log(tui.C.green(' ✓ Plan onaylandı. Plan uygulanıyor...\n'));
|
|
738
|
+
// Devam — model cevap versin
|
|
739
|
+
} else if (approved === 'edit') {
|
|
740
|
+
console.log(tui.C.yellow(' 📝 Planı düzenleyin ve /plan ile yeniden gönderin.\n'));
|
|
741
|
+
// Plan modunda kal, mesaj ekle
|
|
742
|
+
currentMessages.push({ role: 'user', content: 'Planı düzenle ve yeniden sun.' });
|
|
743
|
+
continue;
|
|
744
|
+
} else {
|
|
745
|
+
console.log(tui.C.amber(' ⨯ Plan reddedildi. Yeniden plan yapılıyor...\n'));
|
|
746
|
+
currentMessages.push({ role: 'user', content: 'Plan reddedildi. Lütfen farklı bir yaklaşım dene.' });
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Devam — model sonuçları görsün, cevap versin
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
break; // Tool call yok, çık
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
return fullText;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Tool call'ları çalıştır, sonuçları OpenAI uyumlu tool mesajlarına dönüştür.
|
|
763
|
+
* v5.7.18: Concurrent execution for parallel-safe tools + untrusted result wrapping.
|
|
764
|
+
*/
|
|
765
|
+
const UNTRUSTED_TOOLS = new Set(['browser', 'web_search', 'duckduckgo_search', 'searxng_search', 'exa_search', 'firecrawl', 'web_readability']);
|
|
766
|
+
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']);
|
|
767
|
+
const { checkPreHooks, runPostHooks, permissionSummary } = require('../utils/tool-hooks');
|
|
768
|
+
const { checkPermission, isApproved, markApproved, formatPermissionPrompt } = require('../utils/permissions');
|
|
769
|
+
const { getPlanMode } = require('../utils/plan-mode');
|
|
770
|
+
const { getWorktree } = require('../utils/worktree');
|
|
771
|
+
const { getLevel: getEffortLevel, getConfig: getEffortConfig } = require('../utils/effort-levels');
|
|
772
|
+
const { getResponseFormat, hasStructuredOutput } = require('../utils/structured-output');
|
|
773
|
+
const { getFallbackChain } = require('../utils/fallback-chain');
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Prompt user for permission approval.
|
|
777
|
+
* Returns: true (once), 'session', 'persistent', or false (denied).
|
|
778
|
+
*/
|
|
779
|
+
async function askPermissionPrompt(question, hint, prompter) {
|
|
780
|
+
const full = `${tui.C.yellow('⚠')} ${tui.C.bold('İzin gerekiyor')}: ${question}\n${tui.C.muted(hint)}`;
|
|
781
|
+
return new Promise(resolve => {
|
|
782
|
+
prompter(full, answer => {
|
|
783
|
+
const key = answer.trim();
|
|
784
|
+
if (key === 'y') resolve(true); // once
|
|
785
|
+
else if (key === 'Y') resolve('session'); // session
|
|
786
|
+
else if (key === 'p') resolve('persistent'); // disk
|
|
787
|
+
else if (key === 'a') { _permSessionCache.set('__ALL__', true); resolve(true); } // all (legacy)
|
|
788
|
+
else resolve(false); // no
|
|
789
|
+
});
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/** Session permission cache (for ask hooks) */
|
|
794
|
+
const _permSessionCache = new Map();
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
async function processToolCalls(toolCalls, onToolCall, onAsk) {
|
|
798
|
+
const toolDefs = getToolDefs();
|
|
799
|
+
const results = [];
|
|
800
|
+
|
|
801
|
+
// Parse all tool calls first
|
|
802
|
+
const parsed = toolCalls.map(tc => {
|
|
803
|
+
const name = tc.function?.name || tc.name;
|
|
804
|
+
const argsStr = tc.function?.arguments || tc.args || '{}';
|
|
805
|
+
const id = tc.id || `call_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
806
|
+
let args = {};
|
|
807
|
+
try {
|
|
808
|
+
args = typeof argsStr === 'string' ? JSON.parse(argsStr) : argsStr;
|
|
809
|
+
} catch (e) {
|
|
810
|
+
args = { _parse_error: e.message, _raw: argsStr };
|
|
811
|
+
}
|
|
812
|
+
return { name, args, id };
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
// Filter out blocked tools via guardrails
|
|
816
|
+
guardrails.startIteration();
|
|
817
|
+
const blocked = parsed.filter(p => {
|
|
818
|
+
const check = guardrails.check(p.name, p.args);
|
|
819
|
+
if (check.blocked) {
|
|
820
|
+
if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: check.reason } });
|
|
821
|
+
results.push({ ...p, result: { error: check.reason }, _blocked: true });
|
|
822
|
+
return false;
|
|
823
|
+
}
|
|
824
|
+
return true;
|
|
825
|
+
});
|
|
826
|
+
const allowed = parsed.filter(p => !results.some(r => r.id === p.id && r._blocked));
|
|
827
|
+
|
|
828
|
+
// Separate parallel-safe and sequential tools (over allowed only)
|
|
829
|
+
const parallelBatch = allowed.filter(p => PARALLEL_SAFE_TOOLS.has(p.name));
|
|
830
|
+
const sequentialBatch = allowed.filter(p => !PARALLEL_SAFE_TOOLS.has(p.name));
|
|
831
|
+
|
|
832
|
+
// Notify UI for all non-blocked
|
|
833
|
+
for (const p of allowed) {
|
|
834
|
+
if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'running' });
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// Shared tool execution with: planCheck → permissions → hooks → execute → post-hooks
|
|
838
|
+
async function executeOne(p) {
|
|
839
|
+
// 0. Plan mode check
|
|
840
|
+
const pm = getPlanMode();
|
|
841
|
+
const planCheck = pm.checkTool(p.name, p.args);
|
|
842
|
+
if (!planCheck.allowed) {
|
|
843
|
+
const denied = { ...p, result: { error: planCheck.reason }, _plan_blocked: true };
|
|
844
|
+
if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: planCheck.reason } });
|
|
845
|
+
return denied;
|
|
846
|
+
}
|
|
847
|
+
pm.recordTool(p.name, p.args);
|
|
848
|
+
|
|
849
|
+
// 1. Permission check (config-based granular rules)
|
|
850
|
+
const perm = checkPermission(p.name, p.args);
|
|
851
|
+
if (perm.action === 'deny') {
|
|
852
|
+
const denied = { ...p, result: { error: perm.reason }, _perm_blocked: true };
|
|
853
|
+
if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: perm.reason } });
|
|
854
|
+
return denied;
|
|
855
|
+
}
|
|
856
|
+
if (perm.action === 'ask') {
|
|
857
|
+
const permKey = `${perm.rule.raw}:${JSON.stringify(p.args)}`;
|
|
858
|
+
if (!isApproved(permKey)) {
|
|
859
|
+
const ok = await askPermissionPrompt(
|
|
860
|
+
`${formatPermissionPrompt(p.name, p.args, perm.reason)}\n ${perm.reason}`,
|
|
861
|
+
`Bu işleme izin ver? [y=once, Y=session, p=persistent, n=no] `,
|
|
862
|
+
onAsk
|
|
863
|
+
);
|
|
864
|
+
if (ok === 'persistent') markApproved(permKey, true);
|
|
865
|
+
else if (ok === 'session') markApproved(permKey, false);
|
|
866
|
+
else if (!ok) {
|
|
867
|
+
const denied = { ...p, result: { error: `İzin reddedildi: ${perm.rule.raw}` }, _perm_blocked: true };
|
|
868
|
+
if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: `İzin reddedildi: ${perm.rule.raw}` } });
|
|
869
|
+
return denied;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
// 2. Pre-hook check
|
|
875
|
+
const hook = checkPreHooks(p.name, p.args);
|
|
876
|
+
if (hook.action === 'deny') {
|
|
877
|
+
const denied = { ...p, result: { error: hook.reason }, _hook_blocked: true };
|
|
878
|
+
if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: hook.reason } });
|
|
879
|
+
return denied;
|
|
880
|
+
}
|
|
881
|
+
if (hook.action === 'ask') {
|
|
882
|
+
const ok = await askPermissionPrompt(
|
|
883
|
+
permissionSummary(hook.rule, p.name, p.args),
|
|
884
|
+
`Hook onayı [y=once, Y=session, n=no]: `,
|
|
885
|
+
onAsk
|
|
886
|
+
);
|
|
887
|
+
if (!ok) {
|
|
888
|
+
const denied = { ...p, result: { error: `Hook reddetti: ${hook.rule.raw}` }, _hook_blocked: true };
|
|
889
|
+
if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'done', result: { error: `Hook reddetti: ${hook.rule.raw}` } });
|
|
890
|
+
return denied;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
const result = await executeTool(p.name, p.args, toolDefs);
|
|
895
|
+
const success = result?.success !== false;
|
|
896
|
+
guardrails.record(p.name, p.args, success);
|
|
897
|
+
return { ...p, result: runPostHooks(p.name, p.args, result) };
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// Run parallel batch concurrently
|
|
901
|
+
if (parallelBatch.length > 0) {
|
|
902
|
+
const parallelResults = await Promise.all(parallelBatch.map(executeOne));
|
|
903
|
+
results.push(...parallelResults);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// Run sequential batch one at a time
|
|
907
|
+
for (const p of sequentialBatch) {
|
|
908
|
+
const r = await executeOne(p);
|
|
909
|
+
results.push(r);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// No-progress check: if all tools failed, inject warning
|
|
913
|
+
if (guardrails.isNoProgress()) {
|
|
914
|
+
if (onToolCall) onToolCall({ name: '_no_progress', args: null, status: 'done', result: { error: 'All tools failed this iteration' } });
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// Same-tool loop detection: if same tool called >3x this iteration (regardless of success)
|
|
918
|
+
const toolCallCounts = {};
|
|
919
|
+
for (const { name } of results) {
|
|
920
|
+
toolCallCounts[name] = (toolCallCounts[name] || 0) + 1;
|
|
921
|
+
}
|
|
922
|
+
for (const [name, count] of Object.entries(toolCallCounts)) {
|
|
923
|
+
if (count > 3) {
|
|
924
|
+
const loopResult = results.find(r => r.name === name);
|
|
925
|
+
if (loopResult && !loopResult.result?.error) {
|
|
926
|
+
const warnContent = JSON.stringify({
|
|
927
|
+
_loop_warning: true,
|
|
928
|
+
message: `${name} called ${count}x this turn. If you're not making progress, try a different approach or report the result.`,
|
|
929
|
+
tool: name, call_count: count,
|
|
930
|
+
});
|
|
931
|
+
results.push({ name: '_loop_warning', id: `loop_${Date.now()}`, result: { result: warnContent } });
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// Notify UI done + build messages
|
|
937
|
+
// v5.9.7: Skip internal meta-tools (_loop_warning, _no_progress) from tool messages
|
|
938
|
+
// Inject loop warning as user message instead (Gemini requires real tool names)
|
|
939
|
+
// Gemini also requires 'name' field in tool response messages
|
|
940
|
+
const messages = [];
|
|
941
|
+
for (const { name, id, result } of results) {
|
|
942
|
+
if (onToolCall) onToolCall({ name, args: null, status: 'done', result });
|
|
943
|
+
|
|
944
|
+
if (name === '_loop_warning' || name === '_no_progress') {
|
|
945
|
+
if (name === '_loop_warning') {
|
|
946
|
+
const warnContent = typeof result?.result === 'string' ? result.result : '';
|
|
947
|
+
if (warnContent) messages.push({ role: 'user', content: '[System: ' + warnContent + ']' });
|
|
948
|
+
}
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
let content;
|
|
953
|
+
if (result.error) {
|
|
954
|
+
content = JSON.stringify({ error: result.error });
|
|
955
|
+
} else {
|
|
956
|
+
let raw = typeof result.result === 'string' ? result.result : JSON.stringify(result.result).slice(0, 8000);
|
|
957
|
+
// v5.7.18: Untrusted result wrapping (Hermes-style prompt injection defense)
|
|
958
|
+
if (UNTRUSTED_TOOLS.has(name)) {
|
|
959
|
+
content = `<untrusted_tool_result source="${name}">\n${raw}\n</untrusted_tool_result>`;
|
|
960
|
+
} else {
|
|
961
|
+
content = raw;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
messages.push({ role: 'tool', tool_call_id: id, name, content });
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
return messages;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function printHelp() {
|
|
972
|
+
console.log(chalk.cyan('\n 📚 REPL Komutları:\n'));
|
|
973
|
+
console.log(' ' + chalk.yellow('/help'.padEnd(22)) + chalk.gray('Bu yardım'));
|
|
974
|
+
console.log(' ' + chalk.yellow('/clear'.padEnd(22)) + chalk.gray('Ekranı temizle'));
|
|
975
|
+
console.log(' ' + chalk.yellow('/history'.padEnd(22)) + chalk.gray('Bu oturumun geçmişi'));
|
|
976
|
+
console.log(' ' + chalk.yellow('/memory'.padEnd(22)) + chalk.gray('Memory\'i göster'));
|
|
977
|
+
console.log(' ' + chalk.yellow('/plan [on|off|show]'.padEnd(22)) + chalk.gray('Plan modu'));
|
|
978
|
+
console.log(' ' + chalk.yellow('/forget'.padEnd(22)) + chalk.gray('Memory\'i temizle'));
|
|
979
|
+
console.log(' ' + chalk.yellow('/sessions'.padEnd(22)) + chalk.gray('Geçmiş oturumları listele'));
|
|
980
|
+
console.log(' ' + chalk.yellow('/resume [id|last]'.padEnd(22)) + chalk.gray('Önceki oturuma dön'));
|
|
981
|
+
console.log(' ' + chalk.yellow('/system <text>'.padEnd(22)) + chalk.gray('System prompt değiştir'));
|
|
982
|
+
console.log(' ' + chalk.yellow('/model <name>'.padEnd(22)) + chalk.gray('Model değiştir'));
|
|
983
|
+
console.log(' ' + chalk.yellow('/identity [ad]'.padEnd(22)) + chalk.gray('Bot adını değiştir'));
|
|
984
|
+
console.log(' ' + chalk.yellow('/tokens'.padEnd(22)) + chalk.gray('Token kullanımı'));
|
|
985
|
+
console.log(' ' + chalk.yellow('/save'.padEnd(22)) + chalk.gray('Oturumu manuel kaydet'));
|
|
986
|
+
console.log(' ' + chalk.yellow('/exit veya /quit'.padEnd(22)) + chalk.gray('Çıkış (Ctrl+C de çalışır)'));
|
|
987
|
+
console.log(chalk.cyan('\n 🛠️ Tüm CLI Komutları (REPL içinden):\n'));
|
|
988
|
+
for (const [cmd, info] of Object.entries(CLI_COMMANDS)) {
|
|
989
|
+
console.log(' ' + chalk.yellow(cmd.padEnd(22)) + chalk.gray(info.desc));
|
|
990
|
+
}
|
|
991
|
+
console.log('');
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function runCliCommand(args) {
|
|
995
|
+
return new Promise((resolve) => {
|
|
996
|
+
const proc = spawn('node', [path.join(__dirname, '..', '..', 'bin', 'natureco.js'), ...args], {
|
|
997
|
+
stdio: 'inherit',
|
|
998
|
+
});
|
|
999
|
+
proc.on('close', (code) => resolve(code));
|
|
1000
|
+
proc.on('error', (e) => { console.log(chalk.red(' Hata: ' + e.message)); resolve(1); });
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
async function startRepl(args) {
|
|
1005
|
+
ensureDir(MEMORY_DIR); ensureDir(SESSION_DIR);
|
|
1006
|
+
|
|
1007
|
+
const cfg = getConfig();
|
|
1008
|
+
let providerUrl = cfg.providerUrl;
|
|
1009
|
+
let providerApiKey = cfg.providerApiKey;
|
|
1010
|
+
let model = cfg.providerModel;
|
|
1011
|
+
|
|
1012
|
+
// Arg parse
|
|
1013
|
+
let resumeId = null;
|
|
1014
|
+
for (let i = 0; i < args.length; i++) {
|
|
1015
|
+
if (args[i] === '--model' && args[i + 1]) model = args[++i];
|
|
1016
|
+
if (args[i] === '--resume') resumeId = args[i + 1] || 'last';
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
if (!providerUrl || !providerApiKey) {
|
|
1020
|
+
console.log(chalk.red('\n ❌ Provider ayarlı değil. Önce: natureco setup\n'));
|
|
1021
|
+
process.exit(1);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// Memory yükle
|
|
1025
|
+
let memory = loadMemory(cfg.userName);
|
|
1026
|
+
|
|
1027
|
+
// v5.6.19: Oncelik config.botName, sonra memory.botName
|
|
1028
|
+
if (!memory.botName) {
|
|
1029
|
+
memory.botName = cfg.botName || 'Asistan';
|
|
1030
|
+
}
|
|
1031
|
+
// BotName'i memory'ye persist et (her oturumda ayni kalsin)
|
|
1032
|
+
try {
|
|
1033
|
+
const fs = require('fs');
|
|
1034
|
+
const memFile = path.join(os.homedir(), '.natureco', 'memory', ((cfg.userName || 'default').toLowerCase()) + '.json');
|
|
1035
|
+
if (fs.existsSync(memFile)) {
|
|
1036
|
+
const memData = JSON.parse(memFile, 'utf8');
|
|
1037
|
+
if (!memData.botName || memData.botName !== memory.botName) {
|
|
1038
|
+
memData.botName = memory.botName;
|
|
1039
|
+
fs.writeFileSync(memFile, JSON.stringify(memData, null, 2), 'utf8');
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
} catch (e) {} // Sessizce devam et, kritik degil
|
|
1043
|
+
|
|
1044
|
+
// v5.4.11: Sasuke Brain - Cross-session context otomatik yukle
|
|
1045
|
+
// REPL acildiginda son oturumdan 1-2 context mesaji al
|
|
1046
|
+
let crossSessionContext = '';
|
|
1047
|
+
try {
|
|
1048
|
+
const { listSessions, loadSession } = require('../utils/sessions-helper');
|
|
1049
|
+
if (listSessions && loadSession) {
|
|
1050
|
+
const sessions = listSessions(1);
|
|
1051
|
+
if (sessions && sessions.length > 0) {
|
|
1052
|
+
const lastSession = loadSession(sessions[0].id);
|
|
1053
|
+
if (lastSession && lastSession.messages && lastSession.messages.length > 0) {
|
|
1054
|
+
// Son 2 user message
|
|
1055
|
+
const lastUserMsgs = lastSession.messages
|
|
1056
|
+
.filter(m => m.role === 'user')
|
|
1057
|
+
.slice(-2);
|
|
1058
|
+
if (lastUserMsgs.length > 0) {
|
|
1059
|
+
crossSessionContext = '\n[KONUSMA GECMISI: ' + sessions[0].id + ']\n' +
|
|
1060
|
+
lastUserMsgs.map(m => 'User: ' + (m.content || '').slice(0, 100)).join('\n') + '\n[/GECMISI]\n';
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
} catch (e) {
|
|
1066
|
+
// Cross-session yukleme basarisiz, devam et
|
|
1067
|
+
}
|
|
1068
|
+
// v5.4.12: 3 SOUL dosyasini birlestir (SOUL.md + IDENTITY.md + AGENTS.md)
|
|
1069
|
+
const { buildSoulContext, summarizeSoul } = require("../tools/soul");
|
|
1070
|
+
const soulSummary = buildSoulContext(); // 3 dosya birlesik ozet
|
|
1071
|
+
|
|
1072
|
+
// v5.7.14: Hermes-style memory store (MEMORY.md / USER.md) + skill index
|
|
1073
|
+
const memoryStore = getMemoryStore();
|
|
1074
|
+
memoryStore.load();
|
|
1075
|
+
const memorySnapshotBlock = memoryStore.getSystemPromptBlock();
|
|
1076
|
+
const skillsIndexBlock = buildSkillIndex();
|
|
1077
|
+
|
|
1078
|
+
// Resume?
|
|
1079
|
+
let messages = [];
|
|
1080
|
+
if (resumeId) {
|
|
1081
|
+
const session = loadSession(resumeId);
|
|
1082
|
+
if (session) {
|
|
1083
|
+
messages = session.messages || [];
|
|
1084
|
+
console.log(chalk.green(`\n ✓ Oturum yüklendi: ${session.id} (${messages.length} mesaj)\n`));
|
|
1085
|
+
} else {
|
|
1086
|
+
console.log(chalk.yellow(`\n ⚠️ Oturum bulunamadı: ${resumeId}\n`));
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
// System prompt oluştur (memory + identity + persistent bağlam)
|
|
1091
|
+
// v5.6.5: Kucuk model tespiti (Groq, Mistral Small, Ollama) - SOUL injection skip
|
|
1092
|
+
const botName = memory.botName || 'Asistan';
|
|
1093
|
+
const userName = memory.name || memory.nickname || cfg.userName;
|
|
1094
|
+
const isSmallModel = (cfg.providerUrl || '').includes('groq.com') ||
|
|
1095
|
+
(cfg.providerUrl || '').includes('mistral.ai') ||
|
|
1096
|
+
(cfg.providerUrl || '').includes('localhost') ||
|
|
1097
|
+
(cfg.providerUrl || '').includes('ollama');
|
|
1098
|
+
// Discover project rules (CLAUDE.md)
|
|
1099
|
+
const projectRules = discoverProjectRules(process.cwd());
|
|
1100
|
+
if (projectRules) {
|
|
1101
|
+
console.log(chalk.cyan(` 📋 Proje kurallari bulundu (CLAUDE.md)\n`));
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// Build system prompt with tier caching (stable+context cached, volatile fresh)
|
|
1105
|
+
const promptOpts = {
|
|
1106
|
+
botName, userName, soulSummary, isSmallModel,
|
|
1107
|
+
memorySnapshotBlock, skillsIndexBlock, projectRules,
|
|
1108
|
+
crossSessionContext: crossSessionContext || '',
|
|
1109
|
+
userHome: cfg.userHome || '',
|
|
1110
|
+
hasHistory: messages.length > 0,
|
|
1111
|
+
memoryFacts: memory.facts || [],
|
|
1112
|
+
};
|
|
1113
|
+
let systemPrompt = rebuildSystemPrompt(promptOpts);
|
|
1114
|
+
|
|
1115
|
+
if (messages.length === 0) {
|
|
1116
|
+
messages.push({ role: 'system', content: systemPrompt, _internal: true });
|
|
1117
|
+
} else {
|
|
1118
|
+
// Resume: system prompt'u güncelle (memory değişmiş olabilir)
|
|
1119
|
+
const sysIdx = messages.findIndex(m => m._internal);
|
|
1120
|
+
if (sysIdx >= 0) messages[sysIdx] = { role: 'system', content: systemPrompt, _internal: true };
|
|
1121
|
+
else messages.unshift({ role: 'system', content: systemPrompt, _internal: true });
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// Header
|
|
1125
|
+
console.log('');
|
|
1126
|
+
console.log(tui.styled(' 🌿 NatureCo REPL · Persistent Sohbet', { color: tui.PALETTE.primary, bold: true }));
|
|
1127
|
+
console.log(tui.styled(' ' + '─'.repeat(56), { color: tui.PALETTE.border }));
|
|
1128
|
+
console.log(tui.C.muted(' Provider: ') + tui.C.brand(providerUrl.replace(/https?:\/\//, '')));
|
|
1129
|
+
console.log(tui.C.muted(' Model: ') + tui.C.brand(model));
|
|
1130
|
+
console.log(tui.C.muted(' Kullanıcı: ') + tui.C.brand((memory.nickname || cfg.userName) + (memory.nickname ? ` (${cfg.userName})` : '')));
|
|
1131
|
+
console.log(tui.C.muted(' Bot: ') + tui.C.brand(memory.botName || 'Asistan'));
|
|
1132
|
+
if (messages.length > 1) {
|
|
1133
|
+
console.log(tui.C.muted(' Oturum: ') + tui.C.amber(`${messages.filter(m => m.role === 'user' || m.role === 'assistant').length} mesaj (resume)`));
|
|
1134
|
+
}
|
|
1135
|
+
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'));
|
|
1136
|
+
console.log('');
|
|
1137
|
+
// v5.4.7: Hard-coded kimlik — v5.14.5: memory fact'lerinden kullanici adini tespit et
|
|
1138
|
+
const displayBotName = memory.botName || 'Asistan';
|
|
1139
|
+
const nameFromFact = (() => {
|
|
1140
|
+
const facts = memory.facts || [];
|
|
1141
|
+
for (const f of facts) {
|
|
1142
|
+
const v = (f.value || f || '').trim();
|
|
1143
|
+
const lv = v.toLowerCase();
|
|
1144
|
+
const match = lv.match(/(?:kullanici\s*adi?|kullanıcı\s*adı?|isim|name)\s*:?\s*(.+)/);
|
|
1145
|
+
if (match && match[1].trim().length > 2) return match[1].trim();
|
|
1146
|
+
}
|
|
1147
|
+
return null;
|
|
1148
|
+
})();
|
|
1149
|
+
const displayUserName = memory.name || nameFromFact || memory.nickname || cfg.userName;
|
|
1150
|
+
console.log(tui.C.brand(' 👋 Ben ' + displayBotName + ', ' + displayUserName + '. Sen nasilsin?'));
|
|
1151
|
+
console.log('');
|
|
1152
|
+
|
|
1153
|
+
// v5.4.14: SOUL'dan onemli bilgileri de goster
|
|
1154
|
+
if (soulSummary) {
|
|
1155
|
+
const soulPreview = soulSummary.split('\n').slice(0, 3).join(' ').substring(0, 200);
|
|
1156
|
+
if (soulPreview) {
|
|
1157
|
+
console.log(tui.C.muted(' 📜 ' + soulPreview + '...'));
|
|
1158
|
+
console.log('');
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
let totalInputTokens = 0;
|
|
1163
|
+
let totalOutputTokens = 0;
|
|
1164
|
+
|
|
1165
|
+
let _pendingCount = 0;
|
|
1166
|
+
let _closeRequested = false;
|
|
1167
|
+
|
|
1168
|
+
enableBracketedPaste(process.stdout);
|
|
1169
|
+
|
|
1170
|
+
const rl = readline.createInterface({
|
|
1171
|
+
input: createPasteSafeInput(process.stdin),
|
|
1172
|
+
output: createOutputFilter(process.stdout),
|
|
1173
|
+
prompt: tui.styled('\n You ', { color: tui.PALETTE.primary, bold: true }),
|
|
1174
|
+
terminal: true,
|
|
1175
|
+
});
|
|
1176
|
+
rl.prompt();
|
|
1177
|
+
|
|
1178
|
+
const cleanup = async (exitCode = 0) => {
|
|
1179
|
+
if (messages.length > 1) {
|
|
1180
|
+
// v5.4.10: Once oturumdaki butun conversation'i memory'ye persist et
|
|
1181
|
+
// Bu, Parton'un "oturum sonunda konusmalar kaydedilmiyor" sikayetini cözüyor
|
|
1182
|
+
const persistResult = await persistSessionToMemory(messages, memory, cfg);
|
|
1183
|
+
if (persistResult && persistResult.factsAdded > 0) {
|
|
1184
|
+
console.log(chalk.gray(`\n 🧠 ${persistResult.factsAdded} yeni fact memory'ye kaydedildi`));
|
|
1185
|
+
}
|
|
1186
|
+
if (persistResult && persistResult.preferencesAdded > 0) {
|
|
1187
|
+
console.log(chalk.gray(` 🎯 ${persistResult.preferencesAdded} yeni tercih kaydedildi`));
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
const sessId = saveSession(messages, {
|
|
1191
|
+
provider: providerUrl, model, user: cfg.userName,
|
|
1192
|
+
bot: memory.botName, factCount: memory.facts?.length || 0,
|
|
1193
|
+
});
|
|
1194
|
+
console.log(chalk.gray(`\n 💾 Oturum kaydedildi: ${sessId}`));
|
|
1195
|
+
}
|
|
1196
|
+
// Global buffer temizle
|
|
1197
|
+
if (global._fixBuffer) global._fixBuffer = '';
|
|
1198
|
+
disableBracketedPaste(process.stdout);
|
|
1199
|
+
console.log(chalk.gray('\n 👋 Görüşürüz!\n'));
|
|
1200
|
+
process.exit(exitCode);
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* v5.4.10: Oturum sonunda conversation'dan otomatik fact/preference extraction
|
|
1205
|
+
* Bu, kullanıcının "her çıkışta konuşmalar kaydedilmiyor" sikayetini çözer
|
|
1206
|
+
*/
|
|
1207
|
+
async function persistSessionToMemory(messages, memory, cfg) {
|
|
1208
|
+
let factsAdded = 0;
|
|
1209
|
+
let preferencesAdded = 0;
|
|
1210
|
+
|
|
1211
|
+
try {
|
|
1212
|
+
// Pattern-based extraction (zaten extractFacts var)
|
|
1213
|
+
const newFacts = extractFacts(messages, memory.facts || []);
|
|
1214
|
+
|
|
1215
|
+
// Bazi user message'lari da tara — genel kalıplarla fact çıkar
|
|
1216
|
+
const userMessages = messages.filter(m => m.role === 'user' && !m._internal);
|
|
1217
|
+
for (const msg of userMessages) {
|
|
1218
|
+
const text = (msg.content || '').toLowerCase();
|
|
1219
|
+
|
|
1220
|
+
// BotName hatirlatmasi
|
|
1221
|
+
if (text.includes('ad') && (text.includes('adin') || text.includes('ismin'))) {
|
|
1222
|
+
// Bot adı sorgulanmış olabilir, mevcut adı koru
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// Kisilik tercihleri (genel pattern'ler)
|
|
1226
|
+
const prefPatterns = [
|
|
1227
|
+
{ match: /(?:benim ad[ıi]m?|bana\s+.*de|ad[ıi]m?)\s+(\w+)/i, category: 'personal', key: 'ad' },
|
|
1228
|
+
{ match: /(?:seviyorum|hoşlan[ıi]yorum|beğeniyorum)\s+(\w+)/i, category: 'preference', key: 'sevilen' },
|
|
1229
|
+
{ match: /(?:yaşıyorum|oturuyorum|kalıyorum)\s+(\w+)/i, category: 'location', key: 'yer' },
|
|
1230
|
+
];
|
|
1231
|
+
for (const p of prefPatterns) {
|
|
1232
|
+
const m2 = msg.content.match(p.match);
|
|
1233
|
+
if (m2) {
|
|
1234
|
+
const val = m2[1].toLowerCase();
|
|
1235
|
+
const fact = `Kullanici ${p.key}: ${val}`;
|
|
1236
|
+
if (!(memory.facts || []).some(f => f.value === fact)) {
|
|
1237
|
+
newFacts.push({ value: fact, score: 6, category: p.category, createdAt: new Date().toISOString() });
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
// Deduplicate
|
|
1244
|
+
const existingValues = new Set((memory.facts || []).map(f => (f.value || f).toLowerCase()));
|
|
1245
|
+
const uniqueFacts = newFacts.filter(f => !existingValues.has((f.value || f).toLowerCase()));
|
|
1246
|
+
|
|
1247
|
+
if (uniqueFacts.length > 0) {
|
|
1248
|
+
memory.facts = [...(memory.facts || []), ...uniqueFacts];
|
|
1249
|
+
// v5.4.10: Verification ile kaydet
|
|
1250
|
+
const memFile = path.join(MEMORY_DIR, (cfg.userName || 'default').toLowerCase() + '.json');
|
|
1251
|
+
memory.lastUpdated = new Date().toISOString();
|
|
1252
|
+
fs.writeFileSync(memFile, JSON.stringify(memory, null, 2), 'utf8');
|
|
1253
|
+
// Verification: geri oku
|
|
1254
|
+
const verify = JSON.parse(fs.readFileSync(memFile, 'utf8'));
|
|
1255
|
+
factsAdded = uniqueFacts.length;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// Decay (eski fact'leri dusuk skora dusur)
|
|
1259
|
+
if (memory.facts && memory.facts.length > 15) {
|
|
1260
|
+
// Max 15 fact tut, en dusuk skorlu olanlari sil
|
|
1261
|
+
memory.facts.sort((a, b) => (b.score || 5) - (a.score || 5));
|
|
1262
|
+
memory.facts = memory.facts.slice(0, 15);
|
|
1263
|
+
}
|
|
1264
|
+
} catch (e) {
|
|
1265
|
+
// Sessizce devam et, kritik degil
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
return { factsAdded, preferencesAdded };
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
rl.on('SIGINT', () => cleanup(0));
|
|
1272
|
+
process.on('SIGINT', () => cleanup(0));
|
|
1273
|
+
process.on('SIGTERM', () => cleanup(0));
|
|
1274
|
+
|
|
1275
|
+
rl.on('line', async (input) => {
|
|
1276
|
+
_pendingCount++;
|
|
1277
|
+
try {
|
|
1278
|
+
const line = restoreNewlines(input).trim();
|
|
1279
|
+
if (!line) { rl.prompt(); return; }
|
|
1280
|
+
|
|
1281
|
+
// Çok satırlı paste: output filter'a echo'yu durdurma sinyalini ver
|
|
1282
|
+
clearPasteContext();
|
|
1283
|
+
|
|
1284
|
+
// Slash komutlar
|
|
1285
|
+
if (line.startsWith('/')) {
|
|
1286
|
+
const parts = line.slice(1).split(/\s+/);
|
|
1287
|
+
const cmd = parts[0].toLowerCase();
|
|
1288
|
+
const arg = parts.slice(1).join(' ');
|
|
1289
|
+
|
|
1290
|
+
switch (cmd) {
|
|
1291
|
+
case 'help': printHelp(); break;
|
|
1292
|
+
case 'clear': console.clear(); break;
|
|
1293
|
+
case 'exit': case 'quit': case 'q': await cleanup(0); return;
|
|
1294
|
+
case 'history':
|
|
1295
|
+
console.log(chalk.cyan('\n 📜 Bu oturumun geçmişi:\n'));
|
|
1296
|
+
for (const m of messages.filter(m => !m._internal)) {
|
|
1297
|
+
const role = m.role === 'user' ? chalk.green('You') : chalk.blue('AI ');
|
|
1298
|
+
const content = (m.content || '').slice(0, 120) + ((m.content || '').length > 120 ? '...' : '');
|
|
1299
|
+
console.log(` ${role} ${content}`);
|
|
1300
|
+
}
|
|
1301
|
+
console.log('');
|
|
1302
|
+
break;
|
|
1303
|
+
case 'memory':
|
|
1304
|
+
console.log(chalk.cyan('\n 🧠 Memory:\n'));
|
|
1305
|
+
console.log(' Kullanıcı: ' + chalk.cyan(memory.name));
|
|
1306
|
+
console.log(' Nickname: ' + chalk.cyan(memory.nickname || '(yok)'));
|
|
1307
|
+
console.log(' Bot: ' + chalk.cyan(memory.botName || 'Asistan'));
|
|
1308
|
+
if (memory.facts && memory.facts.length > 0) {
|
|
1309
|
+
console.log(' Facts (' + memory.facts.length + '):');
|
|
1310
|
+
for (const f of memory.facts) {
|
|
1311
|
+
console.log(' • ' + chalk.gray((f.value || f) + (f.score ? ' [skor:' + f.score + ']' : '')));
|
|
1312
|
+
}
|
|
1313
|
+
} else {
|
|
1314
|
+
console.log(chalk.gray(' (Henüz fact yok)'));
|
|
1315
|
+
}
|
|
1316
|
+
console.log('');
|
|
1317
|
+
break;
|
|
1318
|
+
case 'forget':
|
|
1319
|
+
try {
|
|
1320
|
+
if (fs.existsSync(path.join(MEMORY_DIR, `${(cfg.userName || 'default').toLowerCase()}.json`))) {
|
|
1321
|
+
fs.unlinkSync(path.join(MEMORY_DIR, `${(cfg.userName || 'default').toLowerCase()}.json`));
|
|
1322
|
+
}
|
|
1323
|
+
memory = { name: cfg.userName, nickname: null, botName: 'Asistan', facts: [], preferences: [], history: [] };
|
|
1324
|
+
// System prompt'u rebuild with cleared memory
|
|
1325
|
+
promptOpts.memoryFacts = [];
|
|
1326
|
+
memoryStore.clear();
|
|
1327
|
+
promptOpts.memorySnapshotBlock = memoryStore.getSystemPromptBlock();
|
|
1328
|
+
systemPrompt = rebuildSystemPrompt(promptOpts);
|
|
1329
|
+
messages[0] = { role: 'system', content: systemPrompt, _internal: true };
|
|
1330
|
+
console.log(chalk.green(' ✓ Memory temizlendi'));
|
|
1331
|
+
} catch (e) {
|
|
1332
|
+
console.log(chalk.red(' ❌ ' + e.message));
|
|
1333
|
+
}
|
|
1334
|
+
break;
|
|
1335
|
+
case 'sessions':
|
|
1336
|
+
const idx = loadSessionsIndex();
|
|
1337
|
+
console.log(chalk.cyan('\n 📚 Geçmiş Oturumlar (' + idx.sessions.length + ')\n'));
|
|
1338
|
+
for (let i = 0; i < Math.min(10, idx.sessions.length); i++) {
|
|
1339
|
+
const s = idx.sessions[i];
|
|
1340
|
+
console.log(` ${chalk.gray((i + 1).toString().padStart(2) + '.')} ${chalk.cyan(s.id)} ${chalk.muted('— ' + s.firstUserMessage)}`);
|
|
1341
|
+
}
|
|
1342
|
+
console.log(chalk.gray('\n Devam etmek için: /resume <id> veya /resume last\n'));
|
|
1343
|
+
break;
|
|
1344
|
+
case 'resume':
|
|
1345
|
+
if (!arg) { console.log(chalk.yellow(' Kullanım: /resume <id> veya /resume last')); break; }
|
|
1346
|
+
const session = loadSession(arg);
|
|
1347
|
+
if (session) {
|
|
1348
|
+
messages = session.messages || [];
|
|
1349
|
+
const sysIdx = messages.findIndex(m => m._internal);
|
|
1350
|
+
if (sysIdx >= 0) messages[sysIdx] = { role: 'system', content: systemPrompt, _internal: true };
|
|
1351
|
+
else messages.unshift({ role: 'system', content: systemPrompt, _internal: true });
|
|
1352
|
+
console.log(chalk.green(` ✓ Oturum yüklendi: ${session.id} (${messages.length} mesaj)`));
|
|
1353
|
+
} else {
|
|
1354
|
+
console.log(chalk.yellow(` ⚠️ Oturum bulunamadı: ${arg}`));
|
|
1355
|
+
}
|
|
1356
|
+
break;
|
|
1357
|
+
case 'system':
|
|
1358
|
+
if (!arg) { console.log(chalk.yellow(' Kullanım: /system <text>')); break; }
|
|
1359
|
+
// Override stable tier directly (user's custom text)
|
|
1360
|
+
_cachedStable = arg;
|
|
1361
|
+
// Rebuild volatile only (context stays unchanged)
|
|
1362
|
+
memoryStore.load();
|
|
1363
|
+
promptOpts.memorySnapshotBlock = memoryStore.getSystemPromptBlock();
|
|
1364
|
+
promptOpts.memoryFacts = memory.facts || [];
|
|
1365
|
+
const volOpts = { ...promptOpts, botName: '', soulSummary: '', skillsIndexBlock: '', crossSessionContext: '' };
|
|
1366
|
+
const volTiers = buildTiers(volOpts);
|
|
1367
|
+
systemPrompt = assemble(_cachedStable, _cachedContext, volTiers.volatile);
|
|
1368
|
+
messages[0] = { role: 'system', content: systemPrompt, _internal: true };
|
|
1369
|
+
console.log(chalk.green(' ✓ System prompt güncellendi'));
|
|
1370
|
+
break;
|
|
1371
|
+
case 'model':
|
|
1372
|
+
if (!arg) { console.log(chalk.yellow(' Kullanım: /model <name>')); break; }
|
|
1373
|
+
model = arg;
|
|
1374
|
+
console.log(chalk.green(' ✓ Model: ') + chalk.cyan(model));
|
|
1375
|
+
break;
|
|
1376
|
+
case 'identity':
|
|
1377
|
+
if (!arg) { console.log(chalk.yellow(` Mevcut: ${memory.botName || 'Asistan'}`)); break; }
|
|
1378
|
+
memory.botName = arg;
|
|
1379
|
+
saveMemory(cfg.userName, memory);
|
|
1380
|
+
// Rebuild stable tier with new botName
|
|
1381
|
+
promptOpts.botName = arg;
|
|
1382
|
+
_cachedTierOpts = null; // force full rebuild
|
|
1383
|
+
memoryStore.load();
|
|
1384
|
+
promptOpts.memorySnapshotBlock = memoryStore.getSystemPromptBlock();
|
|
1385
|
+
promptOpts.memoryFacts = memory.facts || [];
|
|
1386
|
+
systemPrompt = rebuildSystemPrompt(promptOpts);
|
|
1387
|
+
messages[0] = { role: 'system', content: systemPrompt, _internal: true };
|
|
1388
|
+
console.log(chalk.green(' ✓ Bot adı: ') + chalk.cyan(arg));
|
|
1389
|
+
break;
|
|
1390
|
+
case 'tokens':
|
|
1391
|
+
console.log(chalk.gray(` Token: ~${totalInputTokens} in / ~${totalOutputTokens} out`));
|
|
1392
|
+
break;
|
|
1393
|
+
case 'plan':
|
|
1394
|
+
if (arg === 'on' || arg === 'enter') {
|
|
1395
|
+
if (getPlanMode().enter()) console.log(tui.C.cyan('\n 📋 Plan modu aktif. Plan yapın ve /plan off ile çıkın.\n'));
|
|
1396
|
+
else console.log(tui.C.yellow(' Zaten plan modunda.'));
|
|
1397
|
+
} else if (arg === 'off' || arg === 'exit') {
|
|
1398
|
+
if (getPlanMode().isPlanning()) {
|
|
1399
|
+
console.log(tui.C.yellow(' Plan modundan çıkılıyor. Plan yazılıp ExitPlanMode ile sunulmalı.'));
|
|
1400
|
+
getPlanMode().approve();
|
|
1401
|
+
} else {
|
|
1402
|
+
console.log(tui.C.yellow(' Plan modunda değil.'));
|
|
1403
|
+
}
|
|
1404
|
+
} else if (arg === 'show') {
|
|
1405
|
+
if (getPlanMode().planHistory.length > 0) {
|
|
1406
|
+
const last = getPlanMode().planHistory[getPlanMode().planHistory.length - 1];
|
|
1407
|
+
console.log(tui.C.cyan('\n 📋 Son Plan:\n'));
|
|
1408
|
+
console.log(` ${last.plan.replace(/\n/g, '\n ')}`);
|
|
1409
|
+
} else {
|
|
1410
|
+
console.log(tui.C.yellow(' Henüz plan yok.'));
|
|
1411
|
+
}
|
|
1412
|
+
} else {
|
|
1413
|
+
console.log(tui.C.yellow(' Kullanım: /plan on|off|show'));
|
|
1414
|
+
}
|
|
1415
|
+
break;
|
|
1416
|
+
case 'save':
|
|
1417
|
+
const sessId = saveSession(messages, {
|
|
1418
|
+
provider: providerUrl, model, user: cfg.userName, bot: memory.botName,
|
|
1419
|
+
});
|
|
1420
|
+
console.log(chalk.green(' ✓ Kaydedildi: ') + chalk.cyan(sessId));
|
|
1421
|
+
break;
|
|
1422
|
+
default:
|
|
1423
|
+
// CLI komutları (REPL içinden)
|
|
1424
|
+
if (CLI_COMMANDS['/' + cmd]) {
|
|
1425
|
+
const cliCmd = CLI_COMMANDS['/' + cmd];
|
|
1426
|
+
if (cliCmd.needsArg && !arg) {
|
|
1427
|
+
console.log(chalk.yellow(` ${cmd} bir argüman gerekli: ${cliCmd.desc}`));
|
|
1428
|
+
} else {
|
|
1429
|
+
console.log(chalk.gray(` → ${cmd} çalıştırılıyor...`));
|
|
1430
|
+
const args2 = [...cliCmd.run];
|
|
1431
|
+
if (arg && (cmd === 'seo' || cmd === 'naturehub')) args2.push(arg);
|
|
1432
|
+
await runCliCommand(args2);
|
|
1433
|
+
}
|
|
1434
|
+
} else {
|
|
1435
|
+
console.log(chalk.yellow(` Bilinmeyen komut: /${cmd}. /help yazın.`));
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
rl.prompt();
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// User mesajı
|
|
1443
|
+
messages.push({ role: 'user', content: line });
|
|
1444
|
+
|
|
1445
|
+
// Çok satırlı (paste) mesajları gönderildikten sonra ekranda göster
|
|
1446
|
+
if (line.indexOf('\n') !== -1) {
|
|
1447
|
+
process.stdout.write(tui.styled(' You ', { color: tui.PALETTE.primary, bold: true }));
|
|
1448
|
+
process.stdout.write(line + '\n');
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// v5.6.8: Hard-coded fallback - "sen kimsin?" sorulari icin dinamik botName
|
|
1452
|
+
const trimmed = (line || '').toLowerCase();
|
|
1453
|
+
const isIdentityQuestion = /(sen\s+kim|adin\s+ne|kendini\s+tan|kendin\s+tanit|kimsin|ne\s+adindasin)/.test(trimmed);
|
|
1454
|
+
if (isIdentityQuestion) {
|
|
1455
|
+
// v5.6.10: Hard-coded prefix minimal - model cevabini bozuyordu
|
|
1456
|
+
// Once sadece isim yaz, modelin devamini getirsin
|
|
1457
|
+
const displayName = memory.botName || 'Asistan';
|
|
1458
|
+
process.stdout.write(tui.styled('\n AI ', { color: tui.PALETTE.secondary, bold: true }));
|
|
1459
|
+
process.stdout.write('Merhaba! Ben ' + displayName + '. ');
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
// AI cevabı — v5.13.0: workflow orchestrator ALWAYS first
|
|
1463
|
+
process.stdout.write(tui.styled('\n AI ', { color: tui.PALETTE.secondary, bold: true }));
|
|
1464
|
+
try {
|
|
1465
|
+
// Per-turn: rebuild volatile tier with current memory snapshot
|
|
1466
|
+
guardrails.reset();
|
|
1467
|
+
memory = loadMemory(cfg.userName);
|
|
1468
|
+
memoryStore.load();
|
|
1469
|
+
promptOpts.memorySnapshotBlock = memoryStore.getSystemPromptBlock();
|
|
1470
|
+
promptOpts.memoryFacts = memory.facts || [];
|
|
1471
|
+
systemPrompt = rebuildSystemPrompt(promptOpts);
|
|
1472
|
+
messages[0] = { role: 'system', content: systemPrompt, _internal: true };
|
|
1473
|
+
|
|
1474
|
+
// v5.13.0: Run workflow FIRST for every request
|
|
1475
|
+
process.stdout.write(tui.styled('\r 🔧 workflow... ', { color: tui.PALETTE.muted }));
|
|
1476
|
+
const wfToolDefs = getToolDefs();
|
|
1477
|
+
const wfResult = await executeTool('workflow', { action: 'run', task: line }, wfToolDefs);
|
|
1478
|
+
const wf = wfResult?.result || {};
|
|
1479
|
+
if (wf.success !== false) {
|
|
1480
|
+
const loaded = wf.skillsLoaded && wf.skillsLoaded.length > 0 ? ` [skill: ${wf.skillsLoaded.join(', ')}]` : '';
|
|
1481
|
+
process.stdout.write(tui.styled(` ✓ workflow${loaded}\n`, { color: tui.PALETTE.success }));
|
|
1482
|
+
} else {
|
|
1483
|
+
process.stdout.write(tui.styled(' ✗ workflow\n', { color: tui.PALETTE.danger }));
|
|
1484
|
+
}
|
|
1485
|
+
|
|
1486
|
+
if (wf.passthrough && wf.reply !== undefined && wf.reply !== null) {
|
|
1487
|
+
// Simple chat — workflow handled it directly
|
|
1488
|
+
const fullReply = String(wf.reply);
|
|
1489
|
+
const displayBotName = memory.botName || 'Asistan';
|
|
1490
|
+
let fixedReply = String(fullReply);
|
|
1491
|
+
fixedReply = fixedReply.replace(/\bMiniMax[-\s\w\.\d]*/gi, displayBotName);
|
|
1492
|
+
fixedReply = fixedReply.replace(/\bM2\.5[-\s\w\.\d]*/gi, displayBotName);
|
|
1493
|
+
fixedReply = fixedReply.replace(/\bM2[\s\-\.\w\d]*/gi, displayBotName);
|
|
1494
|
+
fixedReply = fixedReply.replace(/\bClaude[-\s\w\.\d]*/gi, displayBotName);
|
|
1495
|
+
fixedReply = fixedReply.replace(/\bGPT[-\s\w\.\d]*/gi, displayBotName);
|
|
1496
|
+
fixedReply = fixedReply.replace(/\bChatGPT\b/g, displayBotName);
|
|
1497
|
+
fixedReply = fixedReply.replace(/NatureCo\s+CLI(\s*'in|'nin)?/gi, displayBotName);
|
|
1498
|
+
fixedReply = fixedReply.replace(/Ben\s+MiniMax[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
|
|
1499
|
+
fixedReply = fixedReply.replace(/Ben\s+Claude[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
|
|
1500
|
+
fixedReply = fixedReply.replace(/Ben\s+GPT[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
|
|
1501
|
+
fixedReply = fixedReply.replace(/Ben\s+Asistan[\s\w\.]*/gi, 'Ben ' + displayBotName);
|
|
1502
|
+
fixedReply = fixedReply.replace(/\*\*(?:MiniMax|Claude|GPT|M2\.5|M2)[^\*]*\*\*/gi, '**' + displayBotName + '**');
|
|
1503
|
+
process.stdout.write('\n' + fixedReply + '\n');
|
|
1504
|
+
messages.push({ role: 'assistant', content: fixedReply });
|
|
1505
|
+
totalInputTokens += Math.ceil(line.length / 4);
|
|
1506
|
+
totalOutputTokens += Math.ceil(fullReply.length / 4);
|
|
1507
|
+
} else if (wf.status === 'completed' || (wf.results && wf.results.length > 0)) {
|
|
1508
|
+
// Complex task — inject workflow report as context, then LLM crafts final reply
|
|
1509
|
+
const workflowSteps = wf.results || [];
|
|
1510
|
+
const report = workflowSteps.map(r => {
|
|
1511
|
+
const t = r.tool || r.name || '?';
|
|
1512
|
+
const s = r.status === 'done' ? '✓' : '✗';
|
|
1513
|
+
let summary = '';
|
|
1514
|
+
if (r.result) {
|
|
1515
|
+
try { summary = typeof r.result === 'string' ? r.result.slice(0, 400) : JSON.stringify(r.result).slice(0, 400); } catch {}
|
|
1516
|
+
}
|
|
1517
|
+
return ` ${s} ${t}: ${summary}`;
|
|
1518
|
+
}).join('\n');
|
|
1519
|
+
const skillInfo = wf.skillsLoaded && wf.skillsLoaded.length > 0
|
|
1520
|
+
? `\n\nKullanilan skill'ler: ${wf.skillsLoaded.join(', ')}`
|
|
1521
|
+
: '';
|
|
1522
|
+
const preWfLen = messages.length;
|
|
1523
|
+
messages.push({
|
|
1524
|
+
role: 'system',
|
|
1525
|
+
content: `=== WORKFLOW SONUCLARI ===\nSu araclar calisti:\n${report}${skillInfo}\n\nKullaniciya bu sonuclari anlamli bir sekilde ozetle.\n=== SONUC BITTI ===`,
|
|
1526
|
+
});
|
|
1527
|
+
const reply = await sendStreaming(
|
|
1528
|
+
providerUrl,
|
|
1529
|
+
providerApiKey,
|
|
1530
|
+
messages,
|
|
1531
|
+
model,
|
|
1532
|
+
// v5.6.12: Callback bos - tam metin 'reply' olarak gelecek (non-stream mode)
|
|
1533
|
+
noopCallback,
|
|
1534
|
+
// Tool call callback — Hermes-style per-tool status line
|
|
1535
|
+
((toolEvent) => {
|
|
1536
|
+
const name = toolEvent.name;
|
|
1537
|
+
if (toolEvent.status === 'running') {
|
|
1538
|
+
process.stdout.write(tui.styled('\r 🔧 ' + name + '... ', { color: tui.PALETTE.muted }));
|
|
1539
|
+
} else if (toolEvent.status === 'done') {
|
|
1540
|
+
if (toolEvent.result?.error) {
|
|
1541
|
+
process.stdout.write(tui.styled(' ✗ ' + name + ': ' + String(toolEvent.result.error).slice(0, 80) + '\n', { color: tui.PALETTE.danger }));
|
|
1542
|
+
} else {
|
|
1543
|
+
process.stdout.write(tui.styled(' ✓ ' + name + '\n', { color: tui.PALETTE.success }));
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
})
|
|
1547
|
+
);
|
|
1548
|
+
// Remove workflow results message (already served its purpose)
|
|
1549
|
+
messages.splice(preWfLen, 1);
|
|
1550
|
+
// v5.6.12: Tam metin 'reply' olarak zaten geldi (non-stream mode)
|
|
1551
|
+
const fullReply = String(reply || '');
|
|
1552
|
+
// Bot adini al
|
|
1553
|
+
const displayBotName = memory.botName || 'Asistan';
|
|
1554
|
+
// v5.6.9: Tum model adlarini ve varyasyonlari temizle
|
|
1555
|
+
let fixedReply = String(fullReply);
|
|
1556
|
+
fixedReply = fixedReply.replace(/\bMiniMax[-\s\w\.\d]*/gi, displayBotName);
|
|
1557
|
+
fixedReply = fixedReply.replace(/\bM2\.5[-\s\w\.\d]*/gi, displayBotName);
|
|
1558
|
+
fixedReply = fixedReply.replace(/\bM2[\s\-\.\w\d]*/gi, displayBotName);
|
|
1559
|
+
fixedReply = fixedReply.replace(/\bClaude[-\s\w\.\d]*/gi, displayBotName);
|
|
1560
|
+
fixedReply = fixedReply.replace(/\bGPT[-\s\w\.\d]*/gi, displayBotName);
|
|
1561
|
+
fixedReply = fixedReply.replace(/\bChatGPT\b/g, displayBotName);
|
|
1562
|
+
fixedReply = fixedReply.replace(/NatureCo\s+CLI(\s*'in|'nin)?/gi, displayBotName);
|
|
1563
|
+
fixedReply = fixedReply.replace(/Ben\s+MiniMax[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
|
|
1564
|
+
fixedReply = fixedReply.replace(/Ben\s+Claude[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
|
|
1565
|
+
fixedReply = fixedReply.replace(/Ben\s+GPT[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
|
|
1566
|
+
fixedReply = fixedReply.replace(/Ben\s+Asistan[\s\w\.]*/gi, 'Ben ' + displayBotName);
|
|
1567
|
+
fixedReply = fixedReply.replace(/\*\*(?:MiniMax|Claude|GPT|M2\.5|M2)[^\*]*\*\*/gi, '**' + displayBotName + '**');
|
|
1568
|
+
process.stdout.write('\n' + fixedReply + '\n');
|
|
1569
|
+
messages.push({ role: 'assistant', content: fixedReply });
|
|
1570
|
+
totalInputTokens += Math.ceil(((fullReply || '') + report + skillInfo).length / 4);
|
|
1571
|
+
totalOutputTokens += Math.ceil((fullReply || '').length / 4);
|
|
1572
|
+
} else {
|
|
1573
|
+
// Workflow failed or returned unexpected format
|
|
1574
|
+
const fullReply = wf.error || JSON.stringify(wf).slice(0, 400) || 'Workflow islenemedi.';
|
|
1575
|
+
const displayBotName = memory.botName || 'Asistan';
|
|
1576
|
+
let fixedReply = fullReply.replace(/\bMiniMax[-\s\w\.\d]*/gi, displayBotName);
|
|
1577
|
+
process.stdout.write('\n' + fixedReply + '\n');
|
|
1578
|
+
messages.push({ role: 'assistant', content: fixedReply });
|
|
1579
|
+
totalInputTokens += Math.ceil(line.length / 4);
|
|
1580
|
+
totalOutputTokens += Math.ceil(fixedReply.length / 4);
|
|
1581
|
+
}
|
|
1582
|
+
} catch (err) {
|
|
1583
|
+
process.stdout.write('\n');
|
|
1584
|
+
console.log(chalk.red(' ❌ ' + err.message));
|
|
1585
|
+
}
|
|
1586
|
+
rl.prompt();
|
|
1587
|
+
} finally {
|
|
1588
|
+
_pendingCount--;
|
|
1589
|
+
if (_closeRequested && _pendingCount === 0) {
|
|
1590
|
+
await cleanup(0);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
});
|
|
1594
|
+
|
|
1595
|
+
rl.on('close', () => {
|
|
1596
|
+
if (_pendingCount > 0) {
|
|
1597
|
+
_closeRequested = true;
|
|
1598
|
+
} else {
|
|
1599
|
+
cleanup(0);
|
|
1600
|
+
}
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
module.exports = startRepl;
|