erosolar-cli 1.7.80 → 1.7.82
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/agents/erosolar-code.rules.json +5 -0
- package/agents/general.rules.json +5 -0
- package/dist/bin/erosolar.js +0 -2
- package/dist/bin/erosolar.js.map +1 -1
- package/dist/contracts/agent-schemas.json +20 -12
- package/dist/contracts/unified-schema.json +1 -1
- package/dist/core/agent.d.ts +36 -3
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +223 -8
- package/dist/core/agent.js.map +1 -1
- package/dist/core/cliTestHarness.d.ts +200 -0
- package/dist/core/cliTestHarness.d.ts.map +1 -0
- package/dist/core/cliTestHarness.js +549 -0
- package/dist/core/cliTestHarness.js.map +1 -0
- package/dist/core/errors/apiKeyErrors.js +1 -1
- package/dist/core/errors/apiKeyErrors.js.map +1 -1
- package/dist/core/isolatedVerifier.js +274 -22
- package/dist/core/isolatedVerifier.js.map +1 -1
- package/dist/core/modelDiscovery.d.ts.map +1 -1
- package/dist/core/modelDiscovery.js +23 -28
- package/dist/core/modelDiscovery.js.map +1 -1
- package/dist/core/multilinePasteHandler.d.ts +35 -0
- package/dist/core/multilinePasteHandler.d.ts.map +1 -0
- package/dist/core/multilinePasteHandler.js +80 -0
- package/dist/core/multilinePasteHandler.js.map +1 -0
- package/dist/core/secretStore.d.ts +9 -0
- package/dist/core/secretStore.d.ts.map +1 -1
- package/dist/core/secretStore.js +52 -2
- package/dist/core/secretStore.js.map +1 -1
- package/dist/core/types.d.ts +6 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/headless/headlessApp.d.ts.map +1 -1
- package/dist/headless/headlessApp.js +16 -0
- package/dist/headless/headlessApp.js.map +1 -1
- package/dist/plugins/providers/google/index.js +3 -2
- package/dist/plugins/providers/google/index.js.map +1 -1
- package/dist/providers/anthropicProvider.d.ts.map +1 -1
- package/dist/providers/anthropicProvider.js +27 -1
- package/dist/providers/anthropicProvider.js.map +1 -1
- package/dist/providers/googleProvider.d.ts.map +1 -1
- package/dist/providers/googleProvider.js +23 -1
- package/dist/providers/googleProvider.js.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.d.ts +2 -1
- package/dist/providers/openaiChatCompletionsProvider.d.ts.map +1 -1
- package/dist/providers/openaiChatCompletionsProvider.js +111 -4
- package/dist/providers/openaiChatCompletionsProvider.js.map +1 -1
- package/dist/providers/openaiResponsesProvider.d.ts.map +1 -1
- package/dist/providers/openaiResponsesProvider.js +39 -18
- package/dist/providers/openaiResponsesProvider.js.map +1 -1
- package/dist/runtime/agentController.d.ts +4 -0
- package/dist/runtime/agentController.d.ts.map +1 -1
- package/dist/runtime/agentController.js +29 -3
- package/dist/runtime/agentController.js.map +1 -1
- package/dist/security/persistence-research.d.ts +0 -2
- package/dist/security/persistence-research.d.ts.map +1 -1
- package/dist/security/persistence-research.js +0 -2
- package/dist/security/persistence-research.js.map +1 -1
- package/dist/security/security-testing-framework.d.ts +0 -2
- package/dist/security/security-testing-framework.d.ts.map +1 -1
- package/dist/security/security-testing-framework.js +0 -2
- package/dist/security/security-testing-framework.js.map +1 -1
- package/dist/shell/bracketedPasteManager.d.ts +8 -5
- package/dist/shell/bracketedPasteManager.d.ts.map +1 -1
- package/dist/shell/bracketedPasteManager.js +27 -43
- package/dist/shell/bracketedPasteManager.js.map +1 -1
- package/dist/shell/composableMessage.d.ts +1 -1
- package/dist/shell/composableMessage.js +2 -2
- package/dist/shell/composableMessage.js.map +1 -1
- package/dist/shell/interactiveShell.d.ts +7 -48
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +144 -340
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/shellApp.d.ts.map +1 -1
- package/dist/shell/shellApp.js +54 -3
- package/dist/shell/shellApp.js.map +1 -1
- package/dist/shell/systemPrompt.d.ts +1 -1
- package/dist/shell/systemPrompt.d.ts.map +1 -1
- package/dist/shell/systemPrompt.js +10 -3
- package/dist/shell/systemPrompt.js.map +1 -1
- package/dist/shell/updateManager.js +4 -2
- package/dist/shell/updateManager.js.map +1 -1
- package/dist/subagents/taskRunner.js +2 -2
- package/dist/subagents/taskRunner.js.map +1 -1
- package/dist/tools/cloudTools.d.ts +0 -2
- package/dist/tools/cloudTools.d.ts.map +1 -1
- package/dist/tools/cloudTools.js +0 -2
- package/dist/tools/cloudTools.js.map +1 -1
- package/dist/tools/fileTools.d.ts.map +1 -1
- package/dist/tools/fileTools.js +31 -3
- package/dist/tools/fileTools.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +10 -2
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +123 -11
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/keyboardShortcuts.d.ts.map +1 -1
- package/dist/ui/keyboardShortcuts.js +12 -2
- package/dist/ui/keyboardShortcuts.js.map +1 -1
- package/dist/ui/persistentPrompt.d.ts +24 -0
- package/dist/ui/persistentPrompt.d.ts.map +1 -1
- package/dist/ui/persistentPrompt.js +86 -4
- package/dist/ui/persistentPrompt.js.map +1 -1
- package/dist/ui/toolDisplay.d.ts.map +1 -1
- package/dist/ui/toolDisplay.js +652 -0
- package/dist/ui/toolDisplay.js.map +1 -1
- package/package.json +10 -10
- package/dist/shell/inputProcessor.d.ts +0 -55
- package/dist/shell/inputProcessor.d.ts.map +0 -1
- package/dist/shell/inputProcessor.js +0 -171
- package/dist/shell/inputProcessor.js.map +0 -1
|
@@ -23,7 +23,6 @@ import { PersistentPrompt, PinnedChatBox } from '../ui/persistentPrompt.js';
|
|
|
23
23
|
import { formatShortcutsHelp } from '../ui/shortcutsHelp.js';
|
|
24
24
|
import { MetricsTracker } from '../alpha-zero/index.js';
|
|
25
25
|
import { listAvailablePlugins } from '../plugins/index.js';
|
|
26
|
-
import { verifyResponse, formatVerificationReport, } from '../core/responseVerifier.js';
|
|
27
26
|
const DROPDOWN_COLORS = [
|
|
28
27
|
theme.primary,
|
|
29
28
|
theme.info,
|
|
@@ -74,7 +73,6 @@ export class InteractiveShell {
|
|
|
74
73
|
workspaceOptions;
|
|
75
74
|
sessionState;
|
|
76
75
|
isProcessing = false;
|
|
77
|
-
isInsideThinkingBlock = false;
|
|
78
76
|
pendingInteraction = null;
|
|
79
77
|
pendingSecretRetry = null;
|
|
80
78
|
bufferedInputLines = [];
|
|
@@ -107,12 +105,8 @@ export class InteractiveShell {
|
|
|
107
105
|
pendingHistoryLoad = null;
|
|
108
106
|
cachedHistory = [];
|
|
109
107
|
activeSessionId = null;
|
|
110
|
-
sessionStartTime = Date.now();
|
|
111
108
|
activeSessionTitle = null;
|
|
112
109
|
sessionResumeNotice = null;
|
|
113
|
-
lastAssistantResponse = null;
|
|
114
|
-
verificationRetryCount = 0;
|
|
115
|
-
maxVerificationRetries = 2;
|
|
116
110
|
customCommands;
|
|
117
111
|
customCommandMap;
|
|
118
112
|
sessionRestoreConfig;
|
|
@@ -173,11 +167,16 @@ export class InteractiveShell {
|
|
|
173
167
|
// Update persistent prompt status bar with file changes
|
|
174
168
|
this.updatePersistentPromptFileChanges();
|
|
175
169
|
});
|
|
176
|
-
// Set up tool status callback to update
|
|
177
|
-
// Uses Claude Code style: single line at bottom that updates in-place
|
|
170
|
+
// Set up tool status callback to update pinned chat box during tool execution
|
|
178
171
|
this.uiAdapter.setToolStatusCallback((status) => {
|
|
179
|
-
|
|
180
|
-
|
|
172
|
+
if (status) {
|
|
173
|
+
this.pinnedChatBox.setStatusMessage(status);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// Clear status but keep processing indicator if still processing
|
|
177
|
+
this.pinnedChatBox.setStatusMessage(null);
|
|
178
|
+
}
|
|
179
|
+
this.pinnedChatBox.forceRender();
|
|
181
180
|
});
|
|
182
181
|
this.skillRepository = new SkillRepository({
|
|
183
182
|
workingDir: this.workingDir,
|
|
@@ -189,8 +188,9 @@ export class InteractiveShell {
|
|
|
189
188
|
this.rl = readline.createInterface({
|
|
190
189
|
input,
|
|
191
190
|
output,
|
|
192
|
-
//
|
|
193
|
-
|
|
191
|
+
// Use empty prompt since PinnedChatBox handles all prompt rendering
|
|
192
|
+
// This prevents duplicate '>' characters from appearing
|
|
193
|
+
prompt: '',
|
|
194
194
|
terminal: true,
|
|
195
195
|
historySize: 100, // Enable native readline history
|
|
196
196
|
});
|
|
@@ -281,10 +281,7 @@ export class InteractiveShell {
|
|
|
281
281
|
this.pinnedChatBox.show();
|
|
282
282
|
this.pinnedChatBox.forceRender();
|
|
283
283
|
if (initialPrompt) {
|
|
284
|
-
// For command-line prompts, show the user's input with separator (Claude Code style)
|
|
285
284
|
display.newLine();
|
|
286
|
-
const cols = Math.min(process.stdout.columns || 80, 72);
|
|
287
|
-
console.log(theme.ui.border('─'.repeat(cols)));
|
|
288
285
|
console.log(`${formatUserPrompt(this.profileLabel || this.profile)}${initialPrompt}`);
|
|
289
286
|
await this.processInputBlock(initialPrompt);
|
|
290
287
|
return;
|
|
@@ -475,9 +472,9 @@ export class InteractiveShell {
|
|
|
475
472
|
inputStream.off('keypress', this.keypressHandler);
|
|
476
473
|
this.keypressHandler = null;
|
|
477
474
|
}
|
|
478
|
-
//
|
|
479
|
-
if (
|
|
480
|
-
|
|
475
|
+
// Restore original stdin emit (cleanup from paste interception)
|
|
476
|
+
if (this.rawDataHandler) {
|
|
477
|
+
this.rawDataHandler(); // This restores the original emit function
|
|
481
478
|
this.rawDataHandler = null;
|
|
482
479
|
}
|
|
483
480
|
// Clear any pending cleanup to prevent hanging
|
|
@@ -489,6 +486,8 @@ export class InteractiveShell {
|
|
|
489
486
|
display.newLine();
|
|
490
487
|
const highlightedEmail = theme.info('support@ero.solar');
|
|
491
488
|
const infoMessage = [
|
|
489
|
+
'Thank you to Anthropic for allowing me to use Claude Code to build erosolar-cli.',
|
|
490
|
+
'',
|
|
492
491
|
`Email ${highlightedEmail} with any bugs or feedback`,
|
|
493
492
|
'GitHub: https://github.com/ErosolarAI/erosolar-by-bo',
|
|
494
493
|
'npm: https://www.npmjs.com/package/erosolar-cli',
|
|
@@ -526,21 +525,31 @@ export class InteractiveShell {
|
|
|
526
525
|
// All pastes (single or multi-line) are captured for confirmation before submit
|
|
527
526
|
this.capturePaste(content, lineCount);
|
|
528
527
|
});
|
|
529
|
-
// Set up raw data interception to catch bracketed paste before readline processes it
|
|
530
|
-
// We
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
if (
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
528
|
+
// Set up raw data interception to catch bracketed paste before readline processes it.
|
|
529
|
+
// We need to actually PREVENT readline from seeing the paste content to avoid echo.
|
|
530
|
+
// Strategy: Replace stdin's 'data' event emission during paste capture.
|
|
531
|
+
const originalEmit = inputStream.emit.bind(inputStream);
|
|
532
|
+
inputStream.emit = (event, ...args) => {
|
|
533
|
+
if (event === 'data' && args[0]) {
|
|
534
|
+
const data = args[0];
|
|
535
|
+
const str = typeof data === 'string' ? data : data.toString();
|
|
536
|
+
const result = this.bracketedPaste.processRawData(str);
|
|
537
|
+
if (result.consumed) {
|
|
538
|
+
// Data was consumed by paste handler - don't pass to readline
|
|
539
|
+
// If there's passThrough data, emit that instead
|
|
540
|
+
if (result.passThrough) {
|
|
541
|
+
return originalEmit('data', Buffer.from(result.passThrough));
|
|
542
|
+
}
|
|
543
|
+
return true; // Event "handled" but not passed to other listeners
|
|
544
|
+
}
|
|
539
545
|
}
|
|
546
|
+
// Pass through all other events and non-paste data normally
|
|
547
|
+
return originalEmit(event, ...args);
|
|
548
|
+
};
|
|
549
|
+
// Store reference for cleanup
|
|
550
|
+
this.rawDataHandler = () => {
|
|
551
|
+
inputStream.emit = originalEmit;
|
|
540
552
|
};
|
|
541
|
-
// Use prependListener to ensure our handler runs before readline's handlers
|
|
542
|
-
// This gives us first look at the raw data including bracketed paste markers
|
|
543
|
-
inputStream.prependListener('data', this.rawDataHandler);
|
|
544
553
|
}
|
|
545
554
|
setupSlashCommandPreviewHandler() {
|
|
546
555
|
const inputStream = input;
|
|
@@ -565,8 +574,8 @@ export class InteractiveShell {
|
|
|
565
574
|
const currentLine = this.rl.line || '';
|
|
566
575
|
const cursorPos = this.rl.cursor || 0;
|
|
567
576
|
this.persistentPrompt.updateInput(currentLine, cursorPos);
|
|
568
|
-
// Sync to pinned chat box for display only
|
|
569
|
-
this.pinnedChatBox.setInput(currentLine
|
|
577
|
+
// Sync to pinned chat box for display only
|
|
578
|
+
this.pinnedChatBox.setInput(currentLine);
|
|
570
579
|
if (this.composableMessage.hasContent()) {
|
|
571
580
|
this.composableMessage.setDraft(currentLine);
|
|
572
581
|
this.updateComposeStatusSummary();
|
|
@@ -825,6 +834,9 @@ export class InteractiveShell {
|
|
|
825
834
|
this.rl.write(newLine); // Write new content
|
|
826
835
|
// Update persistent prompt display
|
|
827
836
|
this.persistentPrompt.updateInput(newLine, newCursor);
|
|
837
|
+
// NOTE: Don't clear pasteJustCaptured here - the counter-based logic in shouldIgnoreLineEvent()
|
|
838
|
+
// will decrement for each readline line event and auto-clear when all are processed.
|
|
839
|
+
// Clearing prematurely causes the remaining readline-echoed lines to pass through.
|
|
828
840
|
// Re-prompt to show the inline content
|
|
829
841
|
this.rl.prompt(true);
|
|
830
842
|
return;
|
|
@@ -842,13 +854,16 @@ export class InteractiveShell {
|
|
|
842
854
|
});
|
|
843
855
|
// Set the prompt to show paste chips, then position cursor after them
|
|
844
856
|
// The user can type additional text after the chips
|
|
845
|
-
this.persistentPrompt.updateInput(
|
|
857
|
+
this.persistentPrompt.updateInput(pasteChips + ' ', pasteChips.length + 1);
|
|
846
858
|
// Update readline's line buffer to include the chips as prefix
|
|
847
859
|
// This ensures typed text appears after the chips
|
|
848
860
|
if (this.rl.line !== undefined) {
|
|
849
|
-
this.rl.line =
|
|
861
|
+
this.rl.line = pasteChips + ' ';
|
|
850
862
|
this.rl.cursor = pasteChips.length + 1;
|
|
851
863
|
}
|
|
864
|
+
// NOTE: Don't clear pasteJustCaptured here - the counter-based logic in shouldIgnoreLineEvent()
|
|
865
|
+
// will decrement for each readline line event (one per pasted line) and auto-clear when done.
|
|
866
|
+
// Clearing prematurely causes remaining readline-echoed lines to pass through and get displayed.
|
|
852
867
|
this.rl.prompt(true); // preserveCursor=true to keep position after chips
|
|
853
868
|
}
|
|
854
869
|
/**
|
|
@@ -1188,9 +1203,6 @@ export class InteractiveShell {
|
|
|
1188
1203
|
case '/discover':
|
|
1189
1204
|
await this.discoverModelsCommand();
|
|
1190
1205
|
break;
|
|
1191
|
-
case '/verify':
|
|
1192
|
-
await this.handleVerifyCommand();
|
|
1193
|
-
break;
|
|
1194
1206
|
default:
|
|
1195
1207
|
if (!(await this.tryCustomSlashCommand(command, input))) {
|
|
1196
1208
|
display.showWarning(`Unknown command "${command}".`);
|
|
@@ -1307,6 +1319,7 @@ export class InteractiveShell {
|
|
|
1307
1319
|
this.baseSystemPrompt = buildInteractiveSystemPrompt(profileConfig.systemPrompt, profileConfig.label, tools);
|
|
1308
1320
|
if (this.rebuildAgent()) {
|
|
1309
1321
|
display.showInfo(`Workspace snapshot refreshed (${this.describeWorkspaceOptions()}).`);
|
|
1322
|
+
this.resetChatBoxAfterModelSwap();
|
|
1310
1323
|
}
|
|
1311
1324
|
else {
|
|
1312
1325
|
display.showWarning('Workspace snapshot refreshed, but the agent failed to rebuild. Run /doctor for details.');
|
|
@@ -1484,7 +1497,9 @@ export class InteractiveShell {
|
|
|
1484
1497
|
}
|
|
1485
1498
|
this.thinkingMode = value;
|
|
1486
1499
|
saveSessionPreferences({ thinkingMode: this.thinkingMode });
|
|
1487
|
-
this.rebuildAgent()
|
|
1500
|
+
if (this.rebuildAgent()) {
|
|
1501
|
+
this.resetChatBoxAfterModelSwap();
|
|
1502
|
+
}
|
|
1488
1503
|
const descriptions = {
|
|
1489
1504
|
concise: 'Hides internal reasoning and responds directly.',
|
|
1490
1505
|
balanced: 'Shows short thoughts only when helpful.',
|
|
@@ -1507,7 +1522,7 @@ export class InteractiveShell {
|
|
|
1507
1522
|
lines.push(theme.bold('Session File Changes'));
|
|
1508
1523
|
lines.push('');
|
|
1509
1524
|
lines.push(`${theme.info('•')} ${summary.files} file${summary.files === 1 ? '' : 's'} modified`);
|
|
1510
|
-
lines.push(`${theme.info('•')} ${theme.success(
|
|
1525
|
+
lines.push(`${theme.info('•')} ${theme.success('+' + summary.additions)} ${theme.error('-' + summary.removals)} lines`);
|
|
1511
1526
|
lines.push('');
|
|
1512
1527
|
// Group changes by file
|
|
1513
1528
|
const fileMap = new Map();
|
|
@@ -1531,7 +1546,7 @@ export class InteractiveShell {
|
|
|
1531
1546
|
if (stats.writes > 0)
|
|
1532
1547
|
operations.push(`${stats.writes} write${stats.writes === 1 ? '' : 's'}`);
|
|
1533
1548
|
const opsText = operations.join(', ');
|
|
1534
|
-
const diffText = `${theme.success(
|
|
1549
|
+
const diffText = `${theme.success('+' + stats.additions)} ${theme.error('-' + stats.removals)}`;
|
|
1535
1550
|
lines.push(` ${theme.dim(path)}`);
|
|
1536
1551
|
lines.push(` ${opsText} • ${diffText}`);
|
|
1537
1552
|
}
|
|
@@ -1541,211 +1556,6 @@ export class InteractiveShell {
|
|
|
1541
1556
|
const summary = this.alphaZeroMetrics.getPerformanceSummary();
|
|
1542
1557
|
display.showSystemMessage(summary);
|
|
1543
1558
|
}
|
|
1544
|
-
/**
|
|
1545
|
-
* Create a verification context for isolated process verification.
|
|
1546
|
-
*
|
|
1547
|
-
* Verification now runs in a completely separate Node.js process for full isolation.
|
|
1548
|
-
* This ensures:
|
|
1549
|
-
* - Separate memory space from main CLI
|
|
1550
|
-
* - Independent event loop
|
|
1551
|
-
* - No shared state
|
|
1552
|
-
* - Errors in verification cannot crash main process
|
|
1553
|
-
*/
|
|
1554
|
-
createVerificationContext() {
|
|
1555
|
-
// Build conversation history for context
|
|
1556
|
-
const conversationHistory = this.cachedHistory
|
|
1557
|
-
.filter(msg => msg.role === 'user' || msg.role === 'assistant')
|
|
1558
|
-
.slice(-10) // Last 10 messages for context
|
|
1559
|
-
.map(msg => `${msg.role}: ${typeof msg.content === 'string' ? msg.content.slice(0, 500) : '[complex content]'}`);
|
|
1560
|
-
return {
|
|
1561
|
-
workingDirectory: this.workingDir,
|
|
1562
|
-
conversationHistory,
|
|
1563
|
-
provider: this.sessionState.provider,
|
|
1564
|
-
model: this.sessionState.model,
|
|
1565
|
-
};
|
|
1566
|
-
}
|
|
1567
|
-
/**
|
|
1568
|
-
* Handle /verify command - verify the last assistant response
|
|
1569
|
-
*/
|
|
1570
|
-
async handleVerifyCommand() {
|
|
1571
|
-
if (!this.lastAssistantResponse) {
|
|
1572
|
-
display.showWarning('No assistant response to verify. Send a message first.');
|
|
1573
|
-
return;
|
|
1574
|
-
}
|
|
1575
|
-
display.showSystemMessage('Verifying last response in isolated process...\n');
|
|
1576
|
-
try {
|
|
1577
|
-
const context = this.createVerificationContext();
|
|
1578
|
-
const report = await verifyResponse(this.lastAssistantResponse, context);
|
|
1579
|
-
const formattedReport = formatVerificationReport(report);
|
|
1580
|
-
display.showSystemMessage(formattedReport);
|
|
1581
|
-
// Show actionable summary
|
|
1582
|
-
if (report.overallVerdict === 'contradicted') {
|
|
1583
|
-
display.showError('Some claims in the response could not be verified!');
|
|
1584
|
-
}
|
|
1585
|
-
else if (report.overallVerdict === 'verified') {
|
|
1586
|
-
display.showInfo('All verifiable claims in the response were verified.');
|
|
1587
|
-
}
|
|
1588
|
-
else if (report.overallVerdict === 'partially_verified') {
|
|
1589
|
-
display.showWarning('Some claims were verified, but not all.');
|
|
1590
|
-
}
|
|
1591
|
-
else {
|
|
1592
|
-
display.showInfo('No verifiable claims found in the response.');
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
catch (err) {
|
|
1596
|
-
display.showError(`Verification failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
/**
|
|
1600
|
-
* Check if a response looks like a completion (claims to be done)
|
|
1601
|
-
* vs. asking follow-up questions or waiting for user input.
|
|
1602
|
-
* Uses LLM to intelligently determine if verification should run.
|
|
1603
|
-
* Only run auto-verification when assistant claims task completion.
|
|
1604
|
-
*/
|
|
1605
|
-
async shouldRunAutoVerification(response) {
|
|
1606
|
-
// Quick pre-filter: very short responses are unlikely to have verifiable claims
|
|
1607
|
-
if (response.length < 100) {
|
|
1608
|
-
return false;
|
|
1609
|
-
}
|
|
1610
|
-
try {
|
|
1611
|
-
// Use LLM to determine if this response contains verifiable completion claims
|
|
1612
|
-
const prompt = `Analyze this AI assistant response and determine if it claims to have COMPLETED a task that can be verified.
|
|
1613
|
-
|
|
1614
|
-
RESPONSE:
|
|
1615
|
-
---
|
|
1616
|
-
${response.slice(0, 2000)}
|
|
1617
|
-
---
|
|
1618
|
-
|
|
1619
|
-
Answer with ONLY "YES" or "NO":
|
|
1620
|
-
- YES: The response claims to have completed something verifiable (created/modified files, ran commands, fixed bugs, implemented features, etc.)
|
|
1621
|
-
- NO: The response is asking questions, requesting clarification, explaining concepts, or hasn't completed any verifiable action yet.
|
|
1622
|
-
|
|
1623
|
-
Answer:`;
|
|
1624
|
-
const agent = this.runtimeSession.createAgent({
|
|
1625
|
-
provider: this.sessionState.provider,
|
|
1626
|
-
model: this.sessionState.model,
|
|
1627
|
-
temperature: 0,
|
|
1628
|
-
maxTokens: 10,
|
|
1629
|
-
systemPrompt: 'You are a classifier. Answer only YES or NO.',
|
|
1630
|
-
});
|
|
1631
|
-
const result = await agent.send(prompt);
|
|
1632
|
-
const answer = result.trim().toUpperCase();
|
|
1633
|
-
return answer.startsWith('YES');
|
|
1634
|
-
}
|
|
1635
|
-
catch {
|
|
1636
|
-
// On error, fall back to not running verification
|
|
1637
|
-
return false;
|
|
1638
|
-
}
|
|
1639
|
-
}
|
|
1640
|
-
/**
|
|
1641
|
-
* Schedule auto-verification after assistant response.
|
|
1642
|
-
* Uses LLM-based semantic analysis to verify ALL claims.
|
|
1643
|
-
* Runs asynchronously to not block the UI.
|
|
1644
|
-
* Only runs when assistant claims completion, not when asking questions.
|
|
1645
|
-
*/
|
|
1646
|
-
scheduleAutoVerification(response) {
|
|
1647
|
-
// Run verification asynchronously after a short delay
|
|
1648
|
-
// This allows the UI to update first
|
|
1649
|
-
setTimeout(async () => {
|
|
1650
|
-
try {
|
|
1651
|
-
// Use LLM to determine if this response should be verified
|
|
1652
|
-
const shouldVerify = await this.shouldRunAutoVerification(response);
|
|
1653
|
-
if (!shouldVerify) {
|
|
1654
|
-
return;
|
|
1655
|
-
}
|
|
1656
|
-
display.showSystemMessage(`\n🔍 Auto-verifying response in isolated process...`);
|
|
1657
|
-
const context = this.createVerificationContext();
|
|
1658
|
-
const report = await verifyResponse(response, context);
|
|
1659
|
-
const formattedReport = formatVerificationReport(report);
|
|
1660
|
-
// Show compact result
|
|
1661
|
-
if (report.summary.total === 0) {
|
|
1662
|
-
display.showInfo('No verifiable claims found in the response.');
|
|
1663
|
-
this.verificationRetryCount = 0;
|
|
1664
|
-
return;
|
|
1665
|
-
}
|
|
1666
|
-
if (report.overallVerdict === 'verified') {
|
|
1667
|
-
display.showInfo(`✅ Verified: ${report.summary.verified}/${report.summary.total} claims confirmed`);
|
|
1668
|
-
// Reset retry count on success
|
|
1669
|
-
this.verificationRetryCount = 0;
|
|
1670
|
-
}
|
|
1671
|
-
else if (report.overallVerdict === 'contradicted' || report.overallVerdict === 'partially_verified') {
|
|
1672
|
-
const failedCount = report.summary.failed;
|
|
1673
|
-
const icon = report.overallVerdict === 'contradicted' ? '❌' : '⚠️';
|
|
1674
|
-
const label = report.overallVerdict === 'contradicted' ? 'Verification failed' : 'Partial verification';
|
|
1675
|
-
display.showError(`${icon} ${label}: ${failedCount} claim${failedCount > 1 ? 's' : ''} could not be verified`);
|
|
1676
|
-
display.showSystemMessage(formattedReport);
|
|
1677
|
-
// Attempt to fix if we have retries left
|
|
1678
|
-
if (this.verificationRetryCount < this.maxVerificationRetries) {
|
|
1679
|
-
this.verificationRetryCount++;
|
|
1680
|
-
this.requestVerificationFix(report);
|
|
1681
|
-
}
|
|
1682
|
-
else {
|
|
1683
|
-
display.showWarning(`Max verification retries (${this.maxVerificationRetries}) reached. Use /verify to check manually.`);
|
|
1684
|
-
this.verificationRetryCount = 0;
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
catch (err) {
|
|
1689
|
-
// Silently ignore verification errors to not disrupt the flow
|
|
1690
|
-
// User can always run /verify manually
|
|
1691
|
-
}
|
|
1692
|
-
}, 500);
|
|
1693
|
-
}
|
|
1694
|
-
/**
|
|
1695
|
-
* Request the AI to fix failed verification claims.
|
|
1696
|
-
* Generates a strategic fix request with context about what failed and why.
|
|
1697
|
-
*/
|
|
1698
|
-
requestVerificationFix(report) {
|
|
1699
|
-
const failedResults = report.results.filter(r => !r.verified && r.confidence === 'high');
|
|
1700
|
-
if (failedResults.length === 0) {
|
|
1701
|
-
return;
|
|
1702
|
-
}
|
|
1703
|
-
// Build detailed failure descriptions with suggested fixes
|
|
1704
|
-
const failureDetails = failedResults.map(r => {
|
|
1705
|
-
const claim = r.claim;
|
|
1706
|
-
const evidence = r.evidence;
|
|
1707
|
-
// Generate specific fix strategy based on claim category
|
|
1708
|
-
let suggestedFix = '';
|
|
1709
|
-
switch (claim.category) {
|
|
1710
|
-
case 'file_op':
|
|
1711
|
-
suggestedFix = `Re-create or update the file at: ${claim.context['path'] || 'specified path'}`;
|
|
1712
|
-
break;
|
|
1713
|
-
case 'code':
|
|
1714
|
-
suggestedFix = 'Fix any type errors or syntax issues, then run the build again';
|
|
1715
|
-
break;
|
|
1716
|
-
case 'command':
|
|
1717
|
-
suggestedFix = 'Re-run the command and verify it completes successfully';
|
|
1718
|
-
break;
|
|
1719
|
-
case 'state':
|
|
1720
|
-
suggestedFix = 'Verify the state change was applied correctly';
|
|
1721
|
-
break;
|
|
1722
|
-
case 'behavior':
|
|
1723
|
-
suggestedFix = 'Test the feature manually or check implementation';
|
|
1724
|
-
break;
|
|
1725
|
-
default:
|
|
1726
|
-
suggestedFix = 'Retry the operation';
|
|
1727
|
-
}
|
|
1728
|
-
return `• ${claim.statement}
|
|
1729
|
-
Evidence: ${evidence.slice(0, 150)}
|
|
1730
|
-
Suggested fix: ${suggestedFix}`;
|
|
1731
|
-
}).join('\n\n');
|
|
1732
|
-
const fixMessage = `🔧 VERIFICATION FAILED - AUTO-RETRY (attempt ${this.verificationRetryCount}/${this.maxVerificationRetries})
|
|
1733
|
-
|
|
1734
|
-
The following claims could not be verified:
|
|
1735
|
-
|
|
1736
|
-
${failureDetails}
|
|
1737
|
-
|
|
1738
|
-
Think through this carefully, then:
|
|
1739
|
-
1. Analyze why each operation failed (check files, errors, state)
|
|
1740
|
-
2. Identify the root cause
|
|
1741
|
-
3. Fix the underlying issue
|
|
1742
|
-
4. Re-execute the failed operation(s)
|
|
1743
|
-
5. Verify the fix worked`;
|
|
1744
|
-
display.showSystemMessage(`\n🔧 Auto-retry: Generating fix strategy for ${failedResults.length} failed claim${failedResults.length > 1 ? 's' : ''}...`);
|
|
1745
|
-
// Queue the fix request
|
|
1746
|
-
this.followUpQueue.push({ type: 'request', text: fixMessage });
|
|
1747
|
-
this.scheduleQueueProcessing();
|
|
1748
|
-
}
|
|
1749
1559
|
showImprovementSuggestions() {
|
|
1750
1560
|
const suggestions = this.alphaZeroMetrics.getImprovementSuggestions();
|
|
1751
1561
|
if (suggestions.length === 0) {
|
|
@@ -2384,6 +2194,7 @@ Think through this carefully, then:
|
|
|
2384
2194
|
display.showInfo(`Switched to ${preset.label}.`);
|
|
2385
2195
|
this.refreshBannerSessionInfo();
|
|
2386
2196
|
this.persistSessionPreference();
|
|
2197
|
+
this.resetChatBoxAfterModelSwap();
|
|
2387
2198
|
}
|
|
2388
2199
|
}
|
|
2389
2200
|
async handleSecretSelection(input) {
|
|
@@ -2444,7 +2255,9 @@ Think through this carefully, then:
|
|
|
2444
2255
|
const deferred = this.pendingSecretRetry;
|
|
2445
2256
|
this.pendingSecretRetry = null;
|
|
2446
2257
|
if (pending.secret.providers.includes(this.sessionState.provider)) {
|
|
2447
|
-
this.rebuildAgent()
|
|
2258
|
+
if (this.rebuildAgent()) {
|
|
2259
|
+
this.resetChatBoxAfterModelSwap();
|
|
2260
|
+
}
|
|
2448
2261
|
}
|
|
2449
2262
|
if (deferred) {
|
|
2450
2263
|
await deferred();
|
|
@@ -2472,20 +2285,25 @@ Think through this carefully, then:
|
|
|
2472
2285
|
return;
|
|
2473
2286
|
}
|
|
2474
2287
|
this.isProcessing = true;
|
|
2475
|
-
this.resetThinkingState(); // Reset thinking block styling state
|
|
2476
2288
|
const requestStartTime = Date.now(); // Alpha Zero 2 timing
|
|
2289
|
+
// Keep persistent prompt visible during processing so users can type follow-up requests
|
|
2290
|
+
// The prompt will show a "processing" indicator but remain interactive
|
|
2291
|
+
this.persistentPrompt.updateStatusBar({ message: '⏳ Processing... (type to queue follow-up)' });
|
|
2477
2292
|
// Update pinned chat box to show processing state
|
|
2293
|
+
// Clear the input display since the request was already submitted
|
|
2294
|
+
// Note: Don't set statusMessage here - the isProcessing flag already shows "⏳ Processing..."
|
|
2478
2295
|
this.pinnedChatBox.setProcessing(true);
|
|
2479
|
-
this.pinnedChatBox.setStatusMessage(null);
|
|
2296
|
+
this.pinnedChatBox.setStatusMessage(null); // Clear any previous status to avoid duplication
|
|
2480
2297
|
this.pinnedChatBox.clearInput();
|
|
2481
|
-
// Add newline so user's submitted input stays visible
|
|
2482
|
-
// (readline already displayed their input, we just need to preserve it)
|
|
2483
|
-
process.stdout.write('\n');
|
|
2484
|
-
// Note: Don't render pinned box during streaming - it interferes with content
|
|
2485
|
-
// The spinner will handle showing activity
|
|
2486
2298
|
this.uiAdapter.startProcessing('Working on your request');
|
|
2487
2299
|
this.setProcessingStatus();
|
|
2488
2300
|
try {
|
|
2301
|
+
display.newLine();
|
|
2302
|
+
// Pinned chat box already shows processing state - skip redundant spinner
|
|
2303
|
+
// which would conflict with the pinned area at terminal bottom
|
|
2304
|
+
// display.showThinking('Working on your request...');
|
|
2305
|
+
// Force render the pinned chat box to ensure it's visible during processing
|
|
2306
|
+
this.pinnedChatBox.forceRender();
|
|
2489
2307
|
// Enable streaming for real-time text output (Claude Code style)
|
|
2490
2308
|
await agent.send(request, true);
|
|
2491
2309
|
await this.awaitPendingCleanup();
|
|
@@ -2507,15 +2325,17 @@ Think through this carefully, then:
|
|
|
2507
2325
|
this.isProcessing = false;
|
|
2508
2326
|
this.uiAdapter.endProcessing('Ready for prompts');
|
|
2509
2327
|
this.setIdleStatus();
|
|
2510
|
-
|
|
2511
|
-
this.pinnedChatBox.clear();
|
|
2512
|
-
this.pinnedChatBox.setProcessing(false);
|
|
2513
|
-
this.pinnedChatBox.setStatusMessage(null);
|
|
2328
|
+
display.newLine();
|
|
2514
2329
|
// Clear the processing status and ensure persistent prompt is visible
|
|
2515
2330
|
this.persistentPrompt.updateStatusBar({ message: undefined });
|
|
2516
2331
|
this.persistentPrompt.show();
|
|
2332
|
+
// Update pinned chat box to show ready state and force render
|
|
2333
|
+
this.pinnedChatBox.setProcessing(false);
|
|
2334
|
+
this.pinnedChatBox.setStatusMessage(null);
|
|
2335
|
+
this.pinnedChatBox.forceRender();
|
|
2517
2336
|
// CRITICAL: Ensure readline prompt is active for user input
|
|
2518
|
-
//
|
|
2337
|
+
// Call ensureReadlineReady to resume stdin if paused and re-enable keypress
|
|
2338
|
+
this.ensureReadlineReady();
|
|
2519
2339
|
this.rl.prompt();
|
|
2520
2340
|
this.scheduleQueueProcessing();
|
|
2521
2341
|
this.refreshQueueIndicators();
|
|
@@ -2731,13 +2551,13 @@ What's the next action?`;
|
|
|
2731
2551
|
// Clear the processing status and ensure persistent prompt is visible
|
|
2732
2552
|
this.persistentPrompt.updateStatusBar({ message: undefined });
|
|
2733
2553
|
this.persistentPrompt.show();
|
|
2734
|
-
//
|
|
2735
|
-
display.clearStreamingStatus();
|
|
2736
|
-
// Update pinned chat box to show ready state
|
|
2554
|
+
// Update pinned chat box to show ready state and force render
|
|
2737
2555
|
this.pinnedChatBox.setProcessing(false);
|
|
2738
2556
|
this.pinnedChatBox.setStatusMessage(null);
|
|
2557
|
+
this.pinnedChatBox.forceRender();
|
|
2739
2558
|
// CRITICAL: Ensure readline prompt is active for user input
|
|
2740
|
-
//
|
|
2559
|
+
// Call ensureReadlineReady to resume stdin if paused and re-enable keypress
|
|
2560
|
+
this.ensureReadlineReady();
|
|
2741
2561
|
this.rl.prompt();
|
|
2742
2562
|
this.scheduleQueueProcessing();
|
|
2743
2563
|
this.refreshQueueIndicators();
|
|
@@ -2918,46 +2738,51 @@ What's the next action?`;
|
|
|
2918
2738
|
display.stopThinking(false);
|
|
2919
2739
|
process.stdout.write('\n'); // Newline after spinner
|
|
2920
2740
|
}
|
|
2921
|
-
|
|
2922
|
-
const styledChunk = this.styleStreamingChunk(chunk);
|
|
2923
|
-
process.stdout.write(styledChunk);
|
|
2741
|
+
process.stdout.write(chunk);
|
|
2924
2742
|
});
|
|
2925
2743
|
},
|
|
2926
2744
|
onAssistantMessage: (content, metadata) => {
|
|
2927
2745
|
const enriched = this.buildDisplayMetadata(metadata);
|
|
2928
2746
|
// Update spinner based on message type
|
|
2929
2747
|
if (metadata.isFinal) {
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2748
|
+
// Skip display if content was already streamed to avoid double-display
|
|
2749
|
+
if (!metadata.wasStreamed) {
|
|
2750
|
+
const parsed = this.splitThinkingResponse(content);
|
|
2751
|
+
if (parsed?.thinking) {
|
|
2752
|
+
const summary = this.extractThoughtSummary(parsed.thinking);
|
|
2753
|
+
if (summary) {
|
|
2754
|
+
display.updateThinking(`💭 ${summary}`);
|
|
2755
|
+
}
|
|
2756
|
+
display.showAssistantMessage(parsed.thinking, { ...enriched, isFinal: false });
|
|
2757
|
+
}
|
|
2758
|
+
const finalContent = parsed?.response?.trim() || content;
|
|
2759
|
+
if (finalContent) {
|
|
2760
|
+
display.showAssistantMessage(finalContent, enriched);
|
|
2761
|
+
}
|
|
2936
2762
|
}
|
|
2937
|
-
//
|
|
2938
|
-
this.lastAssistantResponse = content;
|
|
2939
|
-
// Auto-verify if response contains verifiable claims
|
|
2940
|
-
this.scheduleAutoVerification(content);
|
|
2941
|
-
// Show status line at end (Claude Code style: "Session 5m • Context X% used • Ready for prompts (2s)")
|
|
2763
|
+
// Show status line at end (Claude Code style: "• Context X% used • Ready for prompts (2s)")
|
|
2942
2764
|
display.stopThinking();
|
|
2943
|
-
// Calculate context usage
|
|
2944
|
-
|
|
2945
|
-
let contextInfo = { sessionElapsedMs };
|
|
2765
|
+
// Calculate context usage
|
|
2766
|
+
let contextInfo;
|
|
2946
2767
|
if (enriched.contextWindowTokens && metadata.usage) {
|
|
2947
2768
|
const total = this.totalTokens(metadata.usage);
|
|
2948
2769
|
if (total && total > 0) {
|
|
2949
2770
|
const percentage = Math.round((total / enriched.contextWindowTokens) * 100);
|
|
2950
|
-
contextInfo = {
|
|
2771
|
+
contextInfo = { percentage, tokens: total };
|
|
2951
2772
|
}
|
|
2952
2773
|
}
|
|
2953
2774
|
display.showStatusLine('Ready for prompts', enriched.elapsedMs, contextInfo);
|
|
2954
2775
|
}
|
|
2955
2776
|
else {
|
|
2956
|
-
// Non-final message = narrative text before tool calls
|
|
2957
|
-
//
|
|
2958
|
-
// Don't display it again - just stop the spinner and continue
|
|
2777
|
+
// Non-final message = narrative text before tool calls (Claude Code style)
|
|
2778
|
+
// Stop spinner and show the narrative text directly
|
|
2959
2779
|
display.stopThinking();
|
|
2960
|
-
//
|
|
2780
|
+
// Skip display if content was already streamed to avoid double-display
|
|
2781
|
+
if (!metadata.wasStreamed) {
|
|
2782
|
+
display.showNarrative(content.trim());
|
|
2783
|
+
}
|
|
2784
|
+
// The isProcessing flag already shows "⏳ Processing..." - no need for duplicate status
|
|
2785
|
+
this.pinnedChatBox.forceRender();
|
|
2961
2786
|
return;
|
|
2962
2787
|
}
|
|
2963
2788
|
const cleanup = this.handleContextTelemetry(metadata, enriched);
|
|
@@ -3028,6 +2853,18 @@ What's the next action?`;
|
|
|
3028
2853
|
return false;
|
|
3029
2854
|
}
|
|
3030
2855
|
}
|
|
2856
|
+
/**
|
|
2857
|
+
* Reset the pinned chat box to a fresh state after model/provider swap.
|
|
2858
|
+
* Ensures the input box is properly visible and ready for input,
|
|
2859
|
+
* just like on fresh startup.
|
|
2860
|
+
*/
|
|
2861
|
+
resetChatBoxAfterModelSwap() {
|
|
2862
|
+
this.pinnedChatBox.setStatusMessage(null);
|
|
2863
|
+
this.pinnedChatBox.setProcessing(false);
|
|
2864
|
+
this.pinnedChatBox.show();
|
|
2865
|
+
this.pinnedChatBox.forceRender();
|
|
2866
|
+
this.ensureReadlineReady();
|
|
2867
|
+
}
|
|
3031
2868
|
buildSystemPrompt() {
|
|
3032
2869
|
const providerLabel = this.providerLabel(this.sessionState.provider);
|
|
3033
2870
|
const lines = [
|
|
@@ -3389,6 +3226,27 @@ What's the next action?`;
|
|
|
3389
3226
|
const fileChangesText = `${summary.files} file${summary.files === 1 ? '' : 's'} +${summary.additions} -${summary.removals}`;
|
|
3390
3227
|
this.persistentPrompt.updateStatusBar({ fileChanges: fileChangesText });
|
|
3391
3228
|
}
|
|
3229
|
+
extractThoughtSummary(thought) {
|
|
3230
|
+
// Extract first non-empty line
|
|
3231
|
+
const lines = thought?.split('\n').filter(line => line.trim()) ?? [];
|
|
3232
|
+
if (!lines.length) {
|
|
3233
|
+
return null;
|
|
3234
|
+
}
|
|
3235
|
+
// Remove common thought prefixes
|
|
3236
|
+
const cleaned = lines[0]
|
|
3237
|
+
.trim()
|
|
3238
|
+
.replace(/^(Thinking|Analyzing|Considering|Looking at|Let me)[:.\s]+/i, '')
|
|
3239
|
+
.replace(/^I (should|need to|will|am)[:.\s]+/i, '')
|
|
3240
|
+
.trim();
|
|
3241
|
+
if (!cleaned) {
|
|
3242
|
+
return null;
|
|
3243
|
+
}
|
|
3244
|
+
// Truncate to reasonable length
|
|
3245
|
+
const maxLength = 50;
|
|
3246
|
+
return cleaned.length > maxLength
|
|
3247
|
+
? cleaned.slice(0, maxLength - 3) + '...'
|
|
3248
|
+
: cleaned;
|
|
3249
|
+
}
|
|
3392
3250
|
splitThinkingResponse(content) {
|
|
3393
3251
|
if (!content?.includes('<thinking') && !content?.includes('<response')) {
|
|
3394
3252
|
return null;
|
|
@@ -3411,61 +3269,6 @@ What's the next action?`;
|
|
|
3411
3269
|
response: responseBody ?? '',
|
|
3412
3270
|
};
|
|
3413
3271
|
}
|
|
3414
|
-
/**
|
|
3415
|
-
* Style streaming chunks in real-time (Claude Code style)
|
|
3416
|
-
* Detects <thinking> blocks and applies cyan styling, hides XML tags
|
|
3417
|
-
*/
|
|
3418
|
-
styleStreamingChunk(chunk) {
|
|
3419
|
-
let result = '';
|
|
3420
|
-
let remaining = chunk;
|
|
3421
|
-
while (remaining.length > 0) {
|
|
3422
|
-
if (this.isInsideThinkingBlock) {
|
|
3423
|
-
// Look for </thinking> end tag
|
|
3424
|
-
const endIdx = remaining.indexOf('</thinking>');
|
|
3425
|
-
if (endIdx !== -1) {
|
|
3426
|
-
// End of thinking block found
|
|
3427
|
-
const thinkingContent = remaining.slice(0, endIdx);
|
|
3428
|
-
// Apply cyan thinking styling to content (hide the closing tag)
|
|
3429
|
-
result += theme.thinking.text(thinkingContent);
|
|
3430
|
-
remaining = remaining.slice(endIdx + '</thinking>'.length);
|
|
3431
|
-
this.isInsideThinkingBlock = false;
|
|
3432
|
-
// Add separator and newline after thinking block ends
|
|
3433
|
-
result += `\n${theme.thinking.border('─'.repeat(40))}\n`;
|
|
3434
|
-
}
|
|
3435
|
-
else {
|
|
3436
|
-
// Still inside thinking block, apply cyan styling to all remaining
|
|
3437
|
-
result += theme.thinking.text(remaining);
|
|
3438
|
-
remaining = '';
|
|
3439
|
-
}
|
|
3440
|
-
}
|
|
3441
|
-
else {
|
|
3442
|
-
// Look for <thinking> start tag
|
|
3443
|
-
const startIdx = remaining.indexOf('<thinking>');
|
|
3444
|
-
if (startIdx !== -1) {
|
|
3445
|
-
// Output text before thinking tag normally
|
|
3446
|
-
if (startIdx > 0) {
|
|
3447
|
-
result += remaining.slice(0, startIdx);
|
|
3448
|
-
}
|
|
3449
|
-
// Show thinking header with cyan styling (Claude Code style)
|
|
3450
|
-
result += `${theme.thinking.icon('💭')} ${theme.thinking.label('Thinking')}\n`;
|
|
3451
|
-
remaining = remaining.slice(startIdx + '<thinking>'.length);
|
|
3452
|
-
this.isInsideThinkingBlock = true;
|
|
3453
|
-
}
|
|
3454
|
-
else {
|
|
3455
|
-
// No thinking tag, output normally
|
|
3456
|
-
result += remaining;
|
|
3457
|
-
remaining = '';
|
|
3458
|
-
}
|
|
3459
|
-
}
|
|
3460
|
-
}
|
|
3461
|
-
return result;
|
|
3462
|
-
}
|
|
3463
|
-
/**
|
|
3464
|
-
* Reset thinking block state (call at start of new request)
|
|
3465
|
-
*/
|
|
3466
|
-
resetThinkingState() {
|
|
3467
|
-
this.isInsideThinkingBlock = false;
|
|
3468
|
-
}
|
|
3469
3272
|
persistSessionPreference() {
|
|
3470
3273
|
saveModelPreference(this.profile, {
|
|
3471
3274
|
provider: this.sessionState.provider,
|
|
@@ -3520,6 +3323,7 @@ What's the next action?`;
|
|
|
3520
3323
|
this.persistSessionPreference();
|
|
3521
3324
|
this.refreshBannerSessionInfo();
|
|
3522
3325
|
display.showInfo(`Switched from ${this.providerLabel(oldProvider)}/${oldModel} to ${match.label}/${defaultModel.id}`);
|
|
3326
|
+
this.resetChatBoxAfterModelSwap();
|
|
3523
3327
|
}
|
|
3524
3328
|
else {
|
|
3525
3329
|
// Revert on failure
|