sam-coder-cli 1.0.58 → 1.0.60
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/bin/agi-animation.js +38 -21
- package/bin/agi-cli.js +228 -2
- package/package.json +1 -1
package/bin/agi-animation.js
CHANGED
|
@@ -168,19 +168,30 @@ function clearScreen() {
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
function getTerminalSize() {
|
|
171
|
+
// Ensure we always have valid numeric values
|
|
172
|
+
const width = Number(process.stdout.columns) || 80;
|
|
173
|
+
const height = Number(process.stdout.rows) || 24;
|
|
174
|
+
|
|
171
175
|
return {
|
|
172
|
-
width:
|
|
173
|
-
height:
|
|
176
|
+
width: Math.max(width, 40), // Minimum width of 40
|
|
177
|
+
height: Math.max(height, 10) // Minimum height of 10
|
|
174
178
|
};
|
|
175
179
|
}
|
|
176
180
|
|
|
177
181
|
function centerText(text, width = getTerminalSize().width) {
|
|
178
|
-
if (!text) return '';
|
|
182
|
+
if (!text || typeof text !== 'string') return '';
|
|
183
|
+
|
|
184
|
+
// Ensure width is a valid number
|
|
185
|
+
const safeWidth = Number(width) || 80;
|
|
186
|
+
|
|
179
187
|
const lines = text.split('\n');
|
|
180
188
|
return lines.map(line => {
|
|
189
|
+
if (!line) return line; // Handle empty lines
|
|
190
|
+
|
|
181
191
|
// Strip ANSI codes to calculate actual text length
|
|
182
192
|
const cleanLine = line.replace(/\x1b\[[0-9;]*m/g, '');
|
|
183
|
-
const
|
|
193
|
+
const lineLength = cleanLine.length || 0;
|
|
194
|
+
const padding = Math.max(0, Math.floor((safeWidth - lineLength) / 2));
|
|
184
195
|
return ' '.repeat(padding) + line;
|
|
185
196
|
}).join('\n');
|
|
186
197
|
}
|
|
@@ -194,6 +205,12 @@ function safeWrite(content) {
|
|
|
194
205
|
}
|
|
195
206
|
}
|
|
196
207
|
|
|
208
|
+
function safeRepeat(str, count) {
|
|
209
|
+
// Ensure count is a valid positive integer
|
|
210
|
+
const safeCount = Math.max(0, Math.floor(Number(count) || 0));
|
|
211
|
+
return str.repeat(safeCount);
|
|
212
|
+
}
|
|
213
|
+
|
|
197
214
|
// Frame interpolation system
|
|
198
215
|
class FrameInterpolator {
|
|
199
216
|
constructor() {
|
|
@@ -329,9 +346,9 @@ async function runAGIAnimation() {
|
|
|
329
346
|
for (let i = 0; i < 30; i++) {
|
|
330
347
|
const dotCount = (i % 4) + 1;
|
|
331
348
|
const dots = '•'.repeat(dotCount) + ' '.repeat(4 - dotCount);
|
|
332
|
-
const frame = '\n'
|
|
349
|
+
const frame = safeRepeat('\n', Math.floor(height / 2) - 2) +
|
|
333
350
|
centerText(dots) +
|
|
334
|
-
'\n'
|
|
351
|
+
safeRepeat('\n', Math.floor(height / 2) - 2);
|
|
335
352
|
voidFrames.push(frame);
|
|
336
353
|
}
|
|
337
354
|
|
|
@@ -358,9 +375,9 @@ async function runAGIAnimation() {
|
|
|
358
375
|
sparkPattern = ` · · ·\n· ·${core}· ·\n · · ·`;
|
|
359
376
|
}
|
|
360
377
|
|
|
361
|
-
const frame = '\n'
|
|
378
|
+
const frame = safeRepeat('\n', Math.floor(height / 2) - 3) +
|
|
362
379
|
centerText(sparkPattern) +
|
|
363
|
-
'\n'
|
|
380
|
+
safeRepeat('\n', Math.floor(height / 2) - 3);
|
|
364
381
|
sparkFrames.push(frame);
|
|
365
382
|
}
|
|
366
383
|
|
|
@@ -390,7 +407,7 @@ async function runAGIAnimation() {
|
|
|
390
407
|
const baseSize = 2 + progress * 4;
|
|
391
408
|
const pulseSize = baseSize + Math.sin(phase * 2) * 1.5;
|
|
392
409
|
|
|
393
|
-
let frame = '\n'
|
|
410
|
+
let frame = safeRepeat('\n', Math.floor(height / 2) - Math.floor(pulseSize) - 2);
|
|
394
411
|
|
|
395
412
|
for (let y = -Math.floor(pulseSize); y <= Math.floor(pulseSize); y++) {
|
|
396
413
|
let line = '';
|
|
@@ -447,7 +464,7 @@ async function runAGIAnimation() {
|
|
|
447
464
|
|
|
448
465
|
for (let i = 0; i < 45; i++) {
|
|
449
466
|
const progress = i / 44;
|
|
450
|
-
let frame = '\n'
|
|
467
|
+
let frame = safeRepeat('\n', Math.floor(height / 2) - layers * 2);
|
|
451
468
|
|
|
452
469
|
for (let layer = 0; layer < layers; layer++) {
|
|
453
470
|
let line = '';
|
|
@@ -525,7 +542,7 @@ async function runAGIAnimation() {
|
|
|
525
542
|
const streamWidth = Math.min(60, Math.floor(width * 0.7));
|
|
526
543
|
|
|
527
544
|
for (let i = 0; i < 30; i++) {
|
|
528
|
-
let frame = '\n'
|
|
545
|
+
let frame = safeRepeat('\n', Math.floor((height - streamHeight) / 2));
|
|
529
546
|
|
|
530
547
|
for (let y = 0; y < streamHeight; y++) {
|
|
531
548
|
let line = '';
|
|
@@ -689,7 +706,7 @@ async function runAGIAnimation() {
|
|
|
689
706
|
// Transition from Matrix Rain to DNA
|
|
690
707
|
clearScreen();
|
|
691
708
|
const matrixToDNAText = centerText('[ DECODING GENETIC ALGORITHMS ]');
|
|
692
|
-
|
|
709
|
+
safeWrite(chalk.greenBright(safeRepeat('\n', Math.floor(height / 2)) + matrixToDNAText));
|
|
693
710
|
await sleep(500);
|
|
694
711
|
|
|
695
712
|
// Fade out transition
|
|
@@ -697,9 +714,9 @@ async function runAGIAnimation() {
|
|
|
697
714
|
clearScreen();
|
|
698
715
|
const opacity = 1 - (i / 10);
|
|
699
716
|
if (opacity > 0.5) {
|
|
700
|
-
safeWrite(chalk.green('\n'
|
|
717
|
+
safeWrite(chalk.green(safeRepeat('\n', Math.floor(height / 2)) + matrixToDNAText));
|
|
701
718
|
} else if (opacity > 0.2) {
|
|
702
|
-
safeWrite(chalk.green.dim('\n'
|
|
719
|
+
safeWrite(chalk.green.dim(safeRepeat('\n', Math.floor(height / 2)) + matrixToDNAText));
|
|
703
720
|
}
|
|
704
721
|
await sleep(frameTime);
|
|
705
722
|
frameCount++;
|
|
@@ -715,7 +732,7 @@ async function runAGIAnimation() {
|
|
|
715
732
|
const helixHeight = Math.min(20, Math.floor(height * 0.6));
|
|
716
733
|
const phase = progress * Math.PI * 4; // Two full rotations
|
|
717
734
|
|
|
718
|
-
let dnaFrame = '\n'
|
|
735
|
+
let dnaFrame = safeRepeat('\n', Math.floor((height - helixHeight) / 2));
|
|
719
736
|
|
|
720
737
|
for (let y = 0; y < helixHeight; y++) {
|
|
721
738
|
const yProgress = y / helixHeight;
|
|
@@ -792,7 +809,7 @@ async function runAGIAnimation() {
|
|
|
792
809
|
|
|
793
810
|
for (let frame = 0; frame < 60; frame++) {
|
|
794
811
|
const progress = frame / 59;
|
|
795
|
-
let circuitFrame = '\n'
|
|
812
|
+
let circuitFrame = safeRepeat('\n', Math.floor((height - circuitHeight) / 2));
|
|
796
813
|
|
|
797
814
|
// Build circuit board progressively
|
|
798
815
|
for (let y = 0; y < circuitHeight; y++) {
|
|
@@ -872,7 +889,7 @@ async function runAGIAnimation() {
|
|
|
872
889
|
const progress = frame / 59;
|
|
873
890
|
const wavePhase = frame * 0.1;
|
|
874
891
|
|
|
875
|
-
let consciousnessFrame = '\n'
|
|
892
|
+
let consciousnessFrame = safeRepeat('\n', Math.floor((height - brainHeight) / 2));
|
|
876
893
|
|
|
877
894
|
// Create brain outline with neural activity
|
|
878
895
|
const brainArt = [
|
|
@@ -981,7 +998,7 @@ async function runAGIAnimation() {
|
|
|
981
998
|
const progress = frame / 59;
|
|
982
999
|
const rotation = progress * Math.PI * 2;
|
|
983
1000
|
|
|
984
|
-
let galaxyFrame = '\n'
|
|
1001
|
+
let galaxyFrame = safeRepeat('\n', Math.floor((height - galaxySize) / 2));
|
|
985
1002
|
|
|
986
1003
|
// Create spiral galaxy
|
|
987
1004
|
for (let y = 0; y < galaxySize; y++) {
|
|
@@ -1051,7 +1068,7 @@ async function runAGIAnimation() {
|
|
|
1051
1068
|
const galaxyToCodeFrames = [];
|
|
1052
1069
|
for (let i = 0; i < 15; i++) {
|
|
1053
1070
|
const progress = i / 14;
|
|
1054
|
-
let transitionFrame = '\n'
|
|
1071
|
+
let transitionFrame = safeRepeat('\n', Math.floor(height / 2) - 2);
|
|
1055
1072
|
|
|
1056
1073
|
// Stars morphing into code characters
|
|
1057
1074
|
const morphChars = ['*', '/', '{', '}', '(', ')', ';', '='];
|
|
@@ -1117,7 +1134,7 @@ async function runAGIAnimation() {
|
|
|
1117
1134
|
|
|
1118
1135
|
for (let frame = 0; frame < 60; frame++) {
|
|
1119
1136
|
const progress = frame / 59;
|
|
1120
|
-
let compilationFrame = '\n'
|
|
1137
|
+
let compilationFrame = safeRepeat('\n', Math.floor((height - codeHeight) / 2));
|
|
1121
1138
|
|
|
1122
1139
|
// Terminal header
|
|
1123
1140
|
compilationFrame += centerText(chalk.gray('┌' + '─'.repeat(codeWidth - 2) + '┐')) + '\n';
|
|
@@ -1405,7 +1422,7 @@ async function runAGIAnimation() {
|
|
|
1405
1422
|
|
|
1406
1423
|
// Final hold with status
|
|
1407
1424
|
clearScreen();
|
|
1408
|
-
let finalDisplay = '\n'
|
|
1425
|
+
let finalDisplay = safeRepeat('\n', Math.max(0, centerY));
|
|
1409
1426
|
for (const line of lines) {
|
|
1410
1427
|
finalDisplay += centerText(line) + '\n';
|
|
1411
1428
|
}
|
package/bin/agi-cli.js
CHANGED
|
@@ -17,6 +17,7 @@ const CONFIG_PATH = path.join(os.homedir(), '.sam-coder-config.json');
|
|
|
17
17
|
let OPENROUTER_API_KEY;
|
|
18
18
|
let MODEL = 'deepseek/deepseek-chat-v3-0324:free';
|
|
19
19
|
let API_BASE_URL = 'https://openrouter.ai/api/v1';
|
|
20
|
+
let SHOW_THOUGHTS = false; // Optional: reveal <think> content in console
|
|
20
21
|
|
|
21
22
|
// Tool/Function definitions for the AI
|
|
22
23
|
const tools = [
|
|
@@ -619,6 +620,143 @@ function extractJsonFromMarkdown(text) {
|
|
|
619
620
|
return null;
|
|
620
621
|
}
|
|
621
622
|
|
|
623
|
+
// Extract and strip <think>...</think> blocks from model output
|
|
624
|
+
function splitThinking(text) {
|
|
625
|
+
if (!text || typeof text !== 'string') {
|
|
626
|
+
return { thought: '', content: text || '' };
|
|
627
|
+
}
|
|
628
|
+
const thinkRegex = /<think>[\s\S]*?<\/think>/gi;
|
|
629
|
+
let combinedThoughts = [];
|
|
630
|
+
let match;
|
|
631
|
+
// Collect all thoughts
|
|
632
|
+
const singleThinkRegex = /<think>([\s\S]*?)<\/think>/i;
|
|
633
|
+
let remaining = text;
|
|
634
|
+
while ((match = remaining.match(singleThinkRegex))) {
|
|
635
|
+
combinedThoughts.push((match[1] || '').trim());
|
|
636
|
+
remaining = remaining.replace(singleThinkRegex, '');
|
|
637
|
+
}
|
|
638
|
+
const visible = remaining.replace(thinkRegex, '').trim();
|
|
639
|
+
return { thought: combinedThoughts.join('\n\n').trim(), content: visible };
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Try to recover tool/function calls embedded in assistant text for thinking models
|
|
643
|
+
function parseInlineToolCalls(text) {
|
|
644
|
+
if (!text || typeof text !== 'string') return null;
|
|
645
|
+
|
|
646
|
+
const candidates = [];
|
|
647
|
+
|
|
648
|
+
// 1) JSON code blocks
|
|
649
|
+
const codeBlockRegex = /```(?:json)?\s*([\s\S]*?)\s*```/gi;
|
|
650
|
+
let m;
|
|
651
|
+
while ((m = codeBlockRegex.exec(text)) !== null) {
|
|
652
|
+
const block = (m[1] || '').trim();
|
|
653
|
+
if (block) candidates.push(block);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// 2) <tool_call>...</tool_call>
|
|
657
|
+
const toolTagRegex = /<tool_call>([\s\S]*?)<\/tool_call>/gi;
|
|
658
|
+
while ((m = toolTagRegex.exec(text)) !== null) {
|
|
659
|
+
const inner = (m[1] || '').trim();
|
|
660
|
+
if (inner) candidates.push(inner);
|
|
661
|
+
}
|
|
662
|
+
// 2b) <function_call>...</function_call>
|
|
663
|
+
const fnTagRegex = /<function_call>([\s\S]*?)<\/function_call>/gi;
|
|
664
|
+
while ((m = fnTagRegex.exec(text)) !== null) {
|
|
665
|
+
const inner = (m[1] || '').trim();
|
|
666
|
+
if (inner) candidates.push(inner);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// 3) General JSON-looking substrings as last resort
|
|
670
|
+
const braceRegex = /\{[\s\S]*?\}/g;
|
|
671
|
+
const braceMatches = text.match(braceRegex) || [];
|
|
672
|
+
braceMatches.forEach(snippet => candidates.push(snippet));
|
|
673
|
+
|
|
674
|
+
const toolCalls = [];
|
|
675
|
+
|
|
676
|
+
for (const candidate of candidates) {
|
|
677
|
+
try {
|
|
678
|
+
const obj = JSON.parse(candidate);
|
|
679
|
+
// OpenAI-style single function_call
|
|
680
|
+
if (obj && obj.function_call && obj.function_call.name) {
|
|
681
|
+
const args = obj.function_call.arguments ?? {};
|
|
682
|
+
toolCalls.push({
|
|
683
|
+
id: `inline-${toolCalls.length + 1}`,
|
|
684
|
+
type: 'function',
|
|
685
|
+
function: {
|
|
686
|
+
name: obj.function_call.name,
|
|
687
|
+
arguments: typeof args === 'string' ? args : JSON.stringify(args)
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
// Anthropic-like tool_use
|
|
693
|
+
if (obj && obj.tool_call && obj.tool_call.name) {
|
|
694
|
+
const args = obj.tool_call.arguments ?? {};
|
|
695
|
+
toolCalls.push({
|
|
696
|
+
id: `inline-${toolCalls.length + 1}`,
|
|
697
|
+
type: 'function',
|
|
698
|
+
function: {
|
|
699
|
+
name: obj.tool_call.name,
|
|
700
|
+
arguments: typeof args === 'string' ? args : JSON.stringify(args)
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
// Array of tool_calls
|
|
706
|
+
if (Array.isArray(obj?.tool_calls)) {
|
|
707
|
+
obj.tool_calls.forEach((tc) => {
|
|
708
|
+
if (tc?.function?.name) {
|
|
709
|
+
const args = tc.function.arguments ?? {};
|
|
710
|
+
toolCalls.push({
|
|
711
|
+
id: tc.id || `inline-${toolCalls.length + 1}`,
|
|
712
|
+
type: 'function',
|
|
713
|
+
function: {
|
|
714
|
+
name: tc.function.name,
|
|
715
|
+
arguments: typeof args === 'string' ? args : JSON.stringify(args)
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
if (toolCalls.length) continue;
|
|
721
|
+
}
|
|
722
|
+
// Direct function structure
|
|
723
|
+
if (obj?.name && (obj.arguments !== undefined || obj.args !== undefined)) {
|
|
724
|
+
const args = obj.arguments ?? obj.args ?? {};
|
|
725
|
+
toolCalls.push({
|
|
726
|
+
id: `inline-${toolCalls.length + 1}`,
|
|
727
|
+
type: 'function',
|
|
728
|
+
function: {
|
|
729
|
+
name: obj.name,
|
|
730
|
+
arguments: typeof args === 'string' ? args : JSON.stringify(args)
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
} catch (_) {
|
|
736
|
+
// ignore parse failures
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
return toolCalls.length ? toolCalls : null;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// Normalize single function_call to tool_calls array if present
|
|
744
|
+
function normalizeToolCallsFromMessage(message) {
|
|
745
|
+
if (!message || typeof message !== 'object') return message;
|
|
746
|
+
if (!message.tool_calls && message.function_call && message.function_call.name) {
|
|
747
|
+
const args = message.function_call.arguments ?? {};
|
|
748
|
+
message.tool_calls = [{
|
|
749
|
+
id: 'fc-1',
|
|
750
|
+
type: 'function',
|
|
751
|
+
function: {
|
|
752
|
+
name: message.function_call.name,
|
|
753
|
+
arguments: typeof args === 'string' ? args : JSON.stringify(args)
|
|
754
|
+
}
|
|
755
|
+
}];
|
|
756
|
+
}
|
|
757
|
+
return message;
|
|
758
|
+
}
|
|
759
|
+
|
|
622
760
|
// Call OpenRouter API with tool calling
|
|
623
761
|
async function callOpenRouter(messages, currentModel, useJson = false) {
|
|
624
762
|
const apiKey = OPENROUTER_API_KEY;
|
|
@@ -675,8 +813,24 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
|
675
813
|
try {
|
|
676
814
|
const response = await callOpenRouter(messages, currentModel);
|
|
677
815
|
const assistantMessage = response.choices[0].message;
|
|
816
|
+
// Handle thinking tags and optionally display them
|
|
817
|
+
if (assistantMessage && typeof assistantMessage.content === 'string') {
|
|
818
|
+
const { thought, content } = splitThinking(assistantMessage.content);
|
|
819
|
+
if (thought && SHOW_THOUGHTS) {
|
|
820
|
+
ui.showInfo(`Thinking:\n${thought}`);
|
|
821
|
+
}
|
|
822
|
+
assistantMessage.content = content;
|
|
823
|
+
}
|
|
824
|
+
normalizeToolCallsFromMessage(assistantMessage);
|
|
678
825
|
messages.push(assistantMessage);
|
|
679
826
|
|
|
827
|
+
// Try inline recovery for thinking models that embed tool calls inside content
|
|
828
|
+
if (!assistantMessage.tool_calls && assistantMessage.content) {
|
|
829
|
+
const recovered = parseInlineToolCalls(assistantMessage.content);
|
|
830
|
+
if (recovered && recovered.length) {
|
|
831
|
+
assistantMessage.tool_calls = recovered;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
680
834
|
if (assistantMessage.tool_calls) {
|
|
681
835
|
const toolResults = await handleToolCalls(assistantMessage.tool_calls, messages);
|
|
682
836
|
messages.push(...toolResults);
|
|
@@ -684,6 +838,14 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
|
684
838
|
ui.startThinking();
|
|
685
839
|
const finalResponseObj = await callOpenRouter(messages, currentModel);
|
|
686
840
|
const finalAssistantMessage = finalResponseObj.choices[0].message;
|
|
841
|
+
if (finalAssistantMessage && typeof finalAssistantMessage.content === 'string') {
|
|
842
|
+
const { thought, content } = splitThinking(finalAssistantMessage.content);
|
|
843
|
+
if (thought && SHOW_THOUGHTS) {
|
|
844
|
+
ui.showInfo(`Thinking:\n${thought}`);
|
|
845
|
+
}
|
|
846
|
+
finalAssistantMessage.content = content;
|
|
847
|
+
}
|
|
848
|
+
normalizeToolCallsFromMessage(finalAssistantMessage);
|
|
687
849
|
messages.push(finalAssistantMessage);
|
|
688
850
|
ui.stopThinking();
|
|
689
851
|
|
|
@@ -692,6 +854,31 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
|
692
854
|
conversation: messages
|
|
693
855
|
};
|
|
694
856
|
} else {
|
|
857
|
+
// Fallback: if no tool_calls were returned, try to parse a JSON action from content (thinking models may embed later)
|
|
858
|
+
const fallbackAction = extractJsonFromMarkdown(assistantMessage.content);
|
|
859
|
+
if (fallbackAction && fallbackAction.type) {
|
|
860
|
+
try {
|
|
861
|
+
const result = await executeAction(fallbackAction);
|
|
862
|
+
messages.push({ role: 'user', content: `Action result (${fallbackAction.type}): ${result}` });
|
|
863
|
+
ui.startThinking();
|
|
864
|
+
const finalResponseObj = await callOpenRouter(messages, currentModel);
|
|
865
|
+
const finalAssistantMessage = finalResponseObj.choices[0].message;
|
|
866
|
+
if (finalAssistantMessage && typeof finalAssistantMessage.content === 'string') {
|
|
867
|
+
const { thought, content } = splitThinking(finalAssistantMessage.content);
|
|
868
|
+
if (thought && SHOW_THOUGHTS) {
|
|
869
|
+
ui.showInfo(`Thinking:\n${thought}`);
|
|
870
|
+
}
|
|
871
|
+
finalAssistantMessage.content = content;
|
|
872
|
+
}
|
|
873
|
+
normalizeToolCallsFromMessage(finalAssistantMessage);
|
|
874
|
+
messages.push(finalAssistantMessage);
|
|
875
|
+
ui.stopThinking();
|
|
876
|
+
return { response: finalAssistantMessage.content, conversation: messages };
|
|
877
|
+
} catch (e) {
|
|
878
|
+
ui.stopThinking();
|
|
879
|
+
// If fallback execution fails, just return original assistant content
|
|
880
|
+
}
|
|
881
|
+
}
|
|
695
882
|
ui.stopThinking();
|
|
696
883
|
return {
|
|
697
884
|
response: assistantMessage.content,
|
|
@@ -818,6 +1005,14 @@ async function processQuery(query, conversation = [], currentModel) {
|
|
|
818
1005
|
while (actionCount < MAX_ACTIONS) {
|
|
819
1006
|
const responseObj = await callOpenRouter(messages, currentModel, true);
|
|
820
1007
|
const assistantMessage = responseObj.choices[0].message;
|
|
1008
|
+
if (assistantMessage && typeof assistantMessage.content === 'string') {
|
|
1009
|
+
const { thought, content } = splitThinking(assistantMessage.content);
|
|
1010
|
+
if (thought && SHOW_THOUGHTS) {
|
|
1011
|
+
ui.showInfo(`Thinking:\n${thought}`);
|
|
1012
|
+
}
|
|
1013
|
+
assistantMessage.content = content;
|
|
1014
|
+
}
|
|
1015
|
+
normalizeToolCallsFromMessage(assistantMessage);
|
|
821
1016
|
messages.push(assistantMessage);
|
|
822
1017
|
|
|
823
1018
|
const actionData = extractJsonFromMarkdown(assistantMessage.content);
|
|
@@ -869,8 +1064,17 @@ async function processQuery(query, conversation = [], currentModel) {
|
|
|
869
1064
|
messages.push(finalMsg);
|
|
870
1065
|
|
|
871
1066
|
const finalResponseObj = await callOpenRouter(messages, currentModel, true);
|
|
872
|
-
|
|
873
|
-
|
|
1067
|
+
const finalAssistantMessage = finalResponseObj.choices[0].message;
|
|
1068
|
+
if (finalAssistantMessage && typeof finalAssistantMessage.content === 'string') {
|
|
1069
|
+
const { thought, content } = splitThinking(finalAssistantMessage.content);
|
|
1070
|
+
if (thought && SHOW_THOUGHTS) {
|
|
1071
|
+
ui.showInfo(`Thinking:\n${thought}`);
|
|
1072
|
+
}
|
|
1073
|
+
finalResponse = content;
|
|
1074
|
+
} else {
|
|
1075
|
+
finalResponse = finalResponseObj.choices[0].message.content;
|
|
1076
|
+
}
|
|
1077
|
+
messages.push(finalAssistantMessage);
|
|
874
1078
|
}
|
|
875
1079
|
|
|
876
1080
|
ui.stopThinking();
|
|
@@ -920,6 +1124,25 @@ async function chat(rl, useToolCalling, initialModel) {
|
|
|
920
1124
|
return;
|
|
921
1125
|
}
|
|
922
1126
|
|
|
1127
|
+
if (input.toLowerCase().startsWith('/thoughts')) {
|
|
1128
|
+
const parts = input.trim().split(/\s+/);
|
|
1129
|
+
const arg = parts[1] ? parts[1].toLowerCase() : '';
|
|
1130
|
+
if (arg !== 'on' && arg !== 'off') {
|
|
1131
|
+
const state = SHOW_THOUGHTS ? 'on' : 'off';
|
|
1132
|
+
ui.showInfo(`Usage: /thoughts on|off (currently ${state})`);
|
|
1133
|
+
rl.prompt();
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
const enable = arg === 'on';
|
|
1137
|
+
SHOW_THOUGHTS = enable;
|
|
1138
|
+
let config = await readConfig() || {};
|
|
1139
|
+
config.showThoughts = enable;
|
|
1140
|
+
await writeConfig(config);
|
|
1141
|
+
ui.showResponse(`Hidden thoughts ${enable ? 'enabled' : 'disabled'}.`);
|
|
1142
|
+
rl.prompt();
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
923
1146
|
if (input.toLowerCase() === '/default-model') {
|
|
924
1147
|
currentModel = 'deepseek/deepseek-chat-v3-0324:free';
|
|
925
1148
|
let config = await readConfig() || {};
|
|
@@ -1039,6 +1262,9 @@ async function start() {
|
|
|
1039
1262
|
console.log(`🚀 Using Pro Plan custom endpoint: ${API_BASE_URL}`);
|
|
1040
1263
|
}
|
|
1041
1264
|
|
|
1265
|
+
// Optional: reveal <think> thoughts if enabled in config or env
|
|
1266
|
+
SHOW_THOUGHTS = (typeof config.showThoughts === 'boolean') ? config.showThoughts : (process.env.SHOW_THOUGHTS === '1');
|
|
1267
|
+
|
|
1042
1268
|
// Check if animation should be shown (can be disabled via config)
|
|
1043
1269
|
const showAnimation = config.showAnimation !== false; // Default to true
|
|
1044
1270
|
|