erosolar-cli 1.7.395 → 1.7.397
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/dist/browser/BrowserSessionManager.d.ts +3 -1
- package/dist/browser/BrowserSessionManager.d.ts.map +1 -1
- package/dist/browser/BrowserSessionManager.js +24 -4
- package/dist/browser/BrowserSessionManager.js.map +1 -1
- package/dist/contracts/agent-schemas.json +5 -0
- package/dist/contracts/unified-schema.json +2 -1
- package/dist/core/agent.d.ts +5 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +99 -0
- package/dist/core/agent.js.map +1 -1
- package/dist/core/alphaZeroConfig.d.ts +11 -0
- package/dist/core/alphaZeroConfig.d.ts.map +1 -0
- package/dist/core/alphaZeroConfig.js +59 -0
- package/dist/core/alphaZeroConfig.js.map +1 -0
- package/dist/core/alphaZeroEngine.d.ts +8 -0
- package/dist/core/alphaZeroEngine.d.ts.map +1 -1
- package/dist/core/alphaZeroEngine.js +149 -35
- package/dist/core/alphaZeroEngine.js.map +1 -1
- package/dist/core/alphaZeroEnhanced.d.ts +125 -0
- package/dist/core/alphaZeroEnhanced.d.ts.map +1 -0
- package/dist/core/alphaZeroEnhanced.js +386 -0
- package/dist/core/alphaZeroEnhanced.js.map +1 -0
- package/dist/core/alphaZeroOrchestrator.d.ts +17 -0
- package/dist/core/alphaZeroOrchestrator.d.ts.map +1 -1
- package/dist/core/alphaZeroOrchestrator.js +95 -8
- package/dist/core/alphaZeroOrchestrator.js.map +1 -1
- package/dist/core/autonomousVerification.d.ts +103 -0
- package/dist/core/autonomousVerification.d.ts.map +1 -0
- package/dist/core/autonomousVerification.js +583 -0
- package/dist/core/autonomousVerification.js.map +1 -0
- package/dist/core/cliTestHarness.d.ts +5 -0
- package/dist/core/cliTestHarness.d.ts.map +1 -1
- package/dist/core/cliTestHarness.js +14 -3
- package/dist/core/cliTestHarness.js.map +1 -1
- package/dist/core/contextManager.d.ts +10 -0
- package/dist/core/contextManager.d.ts.map +1 -1
- package/dist/core/contextManager.js +18 -0
- package/dist/core/contextManager.js.map +1 -1
- package/dist/core/offsecAlphaZeroEnhanced.d.ts +98 -0
- package/dist/core/offsecAlphaZeroEnhanced.d.ts.map +1 -0
- package/dist/core/offsecAlphaZeroEnhanced.js +441 -0
- package/dist/core/offsecAlphaZeroEnhanced.js.map +1 -0
- package/dist/core/parallelAgentOrchestrator.d.ts +171 -0
- package/dist/core/parallelAgentOrchestrator.d.ts.map +1 -0
- package/dist/core/parallelAgentOrchestrator.js +459 -0
- package/dist/core/parallelAgentOrchestrator.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/shell/interactiveShell.d.ts +33 -0
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +604 -244
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/shell/shellApp.d.ts.map +1 -1
- package/dist/shell/shellApp.js +16 -2
- package/dist/shell/shellApp.js.map +1 -1
- package/dist/shell/terminalInput.d.ts +42 -1
- package/dist/shell/terminalInput.d.ts.map +1 -1
- package/dist/shell/terminalInput.js +276 -6
- package/dist/shell/terminalInput.js.map +1 -1
- package/dist/shell/terminalInputAdapter.d.ts +14 -2
- package/dist/shell/terminalInputAdapter.d.ts.map +1 -1
- package/dist/shell/terminalInputAdapter.js +19 -1
- package/dist/shell/terminalInputAdapter.js.map +1 -1
- package/dist/ui/assistantBlockRenderer.d.ts +28 -0
- package/dist/ui/assistantBlockRenderer.d.ts.map +1 -0
- package/dist/ui/assistantBlockRenderer.js +99 -0
- package/dist/ui/assistantBlockRenderer.js.map +1 -0
- package/dist/ui/display.d.ts +11 -0
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +37 -0
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/unified/layout.d.ts +0 -23
- package/dist/ui/unified/layout.d.ts.map +1 -1
- package/dist/ui/unified/layout.js +11 -114
- package/dist/ui/unified/layout.js.map +1 -1
- package/package.json +35 -21
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { stdin as input, stdout as output, exit } from 'node:process';
|
|
2
2
|
import { exec } from 'node:child_process';
|
|
3
3
|
import { promisify } from 'node:util';
|
|
4
|
+
import { createInterface } from 'node:readline/promises';
|
|
5
|
+
import { AssistantBlockRenderer } from '../ui/assistantBlockRenderer.js';
|
|
4
6
|
import { display } from '../ui/display.js';
|
|
5
7
|
import { theme } from '../ui/theme.js';
|
|
6
8
|
import { getContextWindowTokens } from '../core/contextWindow.js';
|
|
7
|
-
import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, maskSecret, setSecretValue, } from '../core/secretStore.js';
|
|
9
|
+
import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue, listSecretDefinitions, MissingSecretError, maskSecret, setSecretValue, } from '../core/secretStore.js';
|
|
8
10
|
import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, loadFeatureFlags, saveFeatureFlags, toggleFeatureFlag, FEATURE_FLAG_INFO, } from '../core/preferences.js';
|
|
9
11
|
import { getLearningSummary, getRecentLearning, commitLearning, exportAllLearning, getLearningDir, } from '../core/learningPersistence.js';
|
|
10
12
|
import { buildEnabledToolSet, evaluateToolPermissions, getToolToggleOptions, } from '../capabilities/toolRegistry.js';
|
|
@@ -113,6 +115,8 @@ export class InteractiveShell {
|
|
|
113
115
|
ui;
|
|
114
116
|
uiAdapter;
|
|
115
117
|
uiUpdates;
|
|
118
|
+
assistantBlocksEnabled;
|
|
119
|
+
assistantBlockRenderer = null;
|
|
116
120
|
_fileChangeTracker = new FileChangeTracker(); // Reserved for future file tracking features
|
|
117
121
|
alphaZeroMetrics; // Alpha Zero 2 performance tracking
|
|
118
122
|
statusSubscription = null;
|
|
@@ -151,6 +155,13 @@ export class InteractiveShell {
|
|
|
151
155
|
lastUserQuery = '';
|
|
152
156
|
lastFailure = null;
|
|
153
157
|
lastAutoTestRun = null;
|
|
158
|
+
runIdCounter = 0;
|
|
159
|
+
lastRunLog = null;
|
|
160
|
+
lastReflectedRunId = null;
|
|
161
|
+
skipNextAutoReflection = false;
|
|
162
|
+
alphaZeroAutoImproveActive = false;
|
|
163
|
+
alphaZeroAutoImproveIterations = 0;
|
|
164
|
+
alphaZeroAutoImproveMaxIterations = 6;
|
|
154
165
|
// Auto-build tracking
|
|
155
166
|
autoBuildInFlight = false;
|
|
156
167
|
lastAutoBuildRun = null;
|
|
@@ -160,14 +171,19 @@ export class InteractiveShell {
|
|
|
160
171
|
streamingHeartbeatStart = null;
|
|
161
172
|
streamingHeartbeatFrame = 0;
|
|
162
173
|
streamingStatusLabel = null;
|
|
174
|
+
streamingStatusBase = null;
|
|
175
|
+
streamingStatusDetail = null;
|
|
163
176
|
lastStreamingElapsedSeconds = null; // Preserve final elapsed time
|
|
164
177
|
statusLineState = null;
|
|
165
178
|
statusMessageOverride = null;
|
|
179
|
+
latestThoughtSummary = null;
|
|
166
180
|
hasShownThoughtProcess = false;
|
|
167
181
|
promptRefreshTimer = null;
|
|
168
182
|
launchPaletteShown = false;
|
|
169
183
|
version;
|
|
170
184
|
alternateScreenEnabled;
|
|
185
|
+
inputInitialized = false;
|
|
186
|
+
assistantStreamBuffer = '';
|
|
171
187
|
constructor(config) {
|
|
172
188
|
this.profile = config.profile;
|
|
173
189
|
this.profileLabel = config.profileLabel;
|
|
@@ -183,6 +199,7 @@ export class InteractiveShell {
|
|
|
183
199
|
this.sessionRestoreConfig = config.sessionRestore ?? { mode: 'none' };
|
|
184
200
|
this._enabledPlugins = config.enabledPlugins ?? [];
|
|
185
201
|
this.version = config.version ?? '0.0.0';
|
|
202
|
+
this.assistantBlocksEnabled = Boolean(config.assistantBlocksEnabled);
|
|
186
203
|
// Alternate screen disabled - use terminal-native mode for proper scrollback and text selection
|
|
187
204
|
this.alternateScreenEnabled = false;
|
|
188
205
|
this.initializeSessionHistory();
|
|
@@ -278,12 +295,16 @@ export class InteractiveShell {
|
|
|
278
295
|
onToggleAlphaZero: () => this.toggleAlphaZeroMode('shortcut'),
|
|
279
296
|
onClearContext: () => this.handleClearContext(),
|
|
280
297
|
});
|
|
298
|
+
if (this.assistantBlocksEnabled) {
|
|
299
|
+
this.assistantBlockRenderer = new AssistantBlockRenderer({
|
|
300
|
+
write: (text) => this.terminalInput.streamContent(text),
|
|
301
|
+
updates: this.uiUpdates,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
281
304
|
// Initialize Alpha Zero 2 metrics tracking
|
|
282
305
|
this.alphaZeroMetrics = new MetricsTracker(`${this.profile}-${Date.now()}`);
|
|
283
306
|
this.setupStatusTracking();
|
|
284
307
|
this.refreshContextGauge();
|
|
285
|
-
// Start terminal input (sets up handlers)
|
|
286
|
-
this.terminalInput.start();
|
|
287
308
|
// Allow planning tools (e.g., ProposePlan) to open the interactive approval UI just like Codex CLI
|
|
288
309
|
this.registerPlanApprovalBridge();
|
|
289
310
|
// Capture display output into the scrollback/chat log so system messages
|
|
@@ -301,9 +322,6 @@ export class InteractiveShell {
|
|
|
301
322
|
// Stream banner first - this sets up scroll region dynamically
|
|
302
323
|
const banner = this.buildBanner();
|
|
303
324
|
this.terminalInput.streamContent(banner + '\n\n');
|
|
304
|
-
// Render chat box after banner is streamed
|
|
305
|
-
this.refreshControlBar();
|
|
306
|
-
this.renderPromptArea(true);
|
|
307
325
|
this.rebuildAgent();
|
|
308
326
|
this.setupHandlers();
|
|
309
327
|
this.refreshBannerSessionInfo();
|
|
@@ -395,10 +413,8 @@ export class InteractiveShell {
|
|
|
395
413
|
this.sessionResumeNotice = null;
|
|
396
414
|
}
|
|
397
415
|
async start(initialPrompt) {
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
void maybeOfferCliUpdate(this.version);
|
|
401
|
-
}
|
|
416
|
+
await this.runStartupUpdatePrompt();
|
|
417
|
+
this.ensureInputInitialized();
|
|
402
418
|
if (initialPrompt) {
|
|
403
419
|
await this.processInputBlock(initialPrompt);
|
|
404
420
|
return;
|
|
@@ -407,6 +423,79 @@ export class InteractiveShell {
|
|
|
407
423
|
// Ensure the terminal input is visible
|
|
408
424
|
this.renderPromptArea();
|
|
409
425
|
}
|
|
426
|
+
async runStartupUpdatePrompt() {
|
|
427
|
+
if (this.version) {
|
|
428
|
+
try {
|
|
429
|
+
await maybeOfferCliUpdate(this.version, {
|
|
430
|
+
prompt: (context) => this.promptForCliUpdate(context),
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
catch {
|
|
434
|
+
// Ignore update check failures at startup
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
this.showStartupSecretGuidance();
|
|
438
|
+
}
|
|
439
|
+
ensureInputInitialized() {
|
|
440
|
+
if (this.inputInitialized) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
this.terminalInput.start();
|
|
444
|
+
this.refreshControlBar();
|
|
445
|
+
this.renderPromptArea(true);
|
|
446
|
+
this.inputInitialized = true;
|
|
447
|
+
}
|
|
448
|
+
async promptForCliUpdate(context) {
|
|
449
|
+
if (!input.isTTY || !output.isTTY) {
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
const lines = [
|
|
453
|
+
theme.gradient.primary('⬆️ Update available'),
|
|
454
|
+
`${theme.ui.muted('Current')}: ${theme.info(context.currentVersion)}`,
|
|
455
|
+
`${theme.ui.muted('Latest')}: ${theme.success(context.latestVersion)}`,
|
|
456
|
+
'',
|
|
457
|
+
`${theme.primary('>')} Update now (recommended)`,
|
|
458
|
+
`${theme.ui.muted(' Skip for now')}`,
|
|
459
|
+
'',
|
|
460
|
+
'Press Enter to accept the highlighted option, or type Y/n (1/2).',
|
|
461
|
+
];
|
|
462
|
+
display.showSystemMessage(lines.join('\n'));
|
|
463
|
+
const prompt = createInterface({ input, output });
|
|
464
|
+
try {
|
|
465
|
+
const raw = (await prompt.question('> ')).trim().toLowerCase();
|
|
466
|
+
if (!raw || raw === 'y' || raw === 'yes' || raw === '1' || raw === 'update') {
|
|
467
|
+
return true;
|
|
468
|
+
}
|
|
469
|
+
if (raw === 'n' || raw === 'no' || raw === '2' || raw === 'skip') {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
finally {
|
|
475
|
+
prompt.close();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
showStartupSecretGuidance() {
|
|
479
|
+
const definitions = listSecretDefinitions();
|
|
480
|
+
const providerSecret = definitions.find((definition) => definition.providers.includes(this.sessionState.provider));
|
|
481
|
+
const missingProviderKey = providerSecret ? !getSecretValue(providerSecret.id) : false;
|
|
482
|
+
const hasSearchKey = Boolean(getSecretValue('TAVILY_API_KEY')) ||
|
|
483
|
+
Boolean(getSecretValue('BRAVE_SEARCH_API_KEY')) ||
|
|
484
|
+
Boolean(getSecretValue('SERPAPI_API_KEY'));
|
|
485
|
+
if (!missingProviderKey && hasSearchKey) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const lines = [];
|
|
489
|
+
lines.push(theme.gradient.primary('Quick setup needed:'));
|
|
490
|
+
if (missingProviderKey && providerSecret) {
|
|
491
|
+
lines.push(`${theme.primary('•')} Set ${providerSecret.label} to use ${this.providerLabel(this.sessionState.provider)}.`);
|
|
492
|
+
}
|
|
493
|
+
if (!hasSearchKey) {
|
|
494
|
+
lines.push(`${theme.primary('•')} Add a web search API key (Tavily recommended) for web tools.`);
|
|
495
|
+
}
|
|
496
|
+
lines.push(theme.ui.muted('Run /secrets to configure keys now. Stored values override env vars.'));
|
|
497
|
+
display.showSystemMessage(lines.join('\n'));
|
|
498
|
+
}
|
|
410
499
|
showLaunchCommandPalette() {
|
|
411
500
|
// Disabled: Quick commands palette takes up too much space
|
|
412
501
|
// Users can type /help to see available commands
|
|
@@ -463,7 +552,8 @@ export class InteractiveShell {
|
|
|
463
552
|
// Keep adapter queue trimmed so hints stay accurate
|
|
464
553
|
this.terminalInput.dequeue();
|
|
465
554
|
this.followUpQueue.push({ type: 'request', text });
|
|
466
|
-
|
|
555
|
+
// Record the queued action in the pinned recent strip instead of the scroll log
|
|
556
|
+
this.terminalInput.recordRecentAction(`queued: ${text}`);
|
|
467
557
|
this.refreshQueueIndicators();
|
|
468
558
|
this.scheduleQueueProcessing();
|
|
469
559
|
this.handleInputChange('');
|
|
@@ -524,15 +614,30 @@ export class InteractiveShell {
|
|
|
524
614
|
* Execute a command immediately during streaming.
|
|
525
615
|
*/
|
|
526
616
|
async executeImmediateCommand(text) {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
617
|
+
const label = text.trim() || 'command';
|
|
618
|
+
// Keep the action visible in the pinned input area instead of the scroll log
|
|
619
|
+
this.terminalInput.recordRecentAction(label);
|
|
620
|
+
// Surface a lightweight inline status while the command runs
|
|
621
|
+
const previousOverride = this.statusMessageOverride;
|
|
622
|
+
this.statusMessageOverride = `Running ${label}`;
|
|
623
|
+
this.refreshStatusLine(true);
|
|
624
|
+
try {
|
|
625
|
+
await this.processSlashCommand(text);
|
|
626
|
+
}
|
|
627
|
+
finally {
|
|
628
|
+
this.statusMessageOverride = previousOverride;
|
|
629
|
+
this.refreshStatusLine(true);
|
|
630
|
+
}
|
|
530
631
|
}
|
|
531
632
|
/**
|
|
532
633
|
* TerminalInputAdapter change handler
|
|
533
634
|
*/
|
|
534
635
|
handleInputChange(text) {
|
|
636
|
+
const previous = this.currentInput;
|
|
535
637
|
this.currentInput = text;
|
|
638
|
+
if (previous !== text) {
|
|
639
|
+
this.terminalInput.clearInlineCommandPanel();
|
|
640
|
+
}
|
|
536
641
|
if (text.length > 0) {
|
|
537
642
|
this.resetCtrlCSequence();
|
|
538
643
|
}
|
|
@@ -1094,12 +1199,13 @@ export class InteractiveShell {
|
|
|
1094
1199
|
setupHandlers() {
|
|
1095
1200
|
// Handle terminal resize
|
|
1096
1201
|
output.on('resize', () => {
|
|
1202
|
+
if (!this.inputInitialized) {
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1097
1205
|
this.terminalInput.resetContentPosition();
|
|
1098
1206
|
this.terminalInput.handleResize();
|
|
1099
1207
|
this.terminalInput.forceRender();
|
|
1100
1208
|
});
|
|
1101
|
-
// Show initial input UI
|
|
1102
|
-
this.renderPromptArea();
|
|
1103
1209
|
}
|
|
1104
1210
|
/**
|
|
1105
1211
|
* Set up command autocomplete with all available slash commands.
|
|
@@ -1222,9 +1328,13 @@ export class InteractiveShell {
|
|
|
1222
1328
|
alphaZeroHotkey: 'ctrl+shift+a',
|
|
1223
1329
|
alphaZeroLabel: 'AlphaZero RL',
|
|
1224
1330
|
});
|
|
1331
|
+
this.refreshFeatureStatusDisplay();
|
|
1225
1332
|
this.refreshStatusLine();
|
|
1226
1333
|
this.renderPromptArea();
|
|
1227
1334
|
}
|
|
1335
|
+
refreshFeatureStatusDisplay() {
|
|
1336
|
+
this.terminalInput.setFeatureStatus(this.buildFeatureStatusSnapshot());
|
|
1337
|
+
}
|
|
1228
1338
|
writeLocked(content) {
|
|
1229
1339
|
if (!content) {
|
|
1230
1340
|
return;
|
|
@@ -1232,6 +1342,29 @@ export class InteractiveShell {
|
|
|
1232
1342
|
// Route through display stream so scroll regions and streaming locks stay in sync
|
|
1233
1343
|
display.stream(content);
|
|
1234
1344
|
}
|
|
1345
|
+
resetAssistantStreamBuffer() {
|
|
1346
|
+
this.assistantStreamBuffer = '';
|
|
1347
|
+
}
|
|
1348
|
+
appendAssistantStreamChunk(chunk) {
|
|
1349
|
+
if (!this.assistantBlocksEnabled || !chunk) {
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
this.assistantStreamBuffer += chunk;
|
|
1353
|
+
}
|
|
1354
|
+
consumeAssistantStreamBuffer(fallback) {
|
|
1355
|
+
const buffered = this.assistantStreamBuffer;
|
|
1356
|
+
this.assistantStreamBuffer = '';
|
|
1357
|
+
if (buffered.trim()) {
|
|
1358
|
+
return buffered;
|
|
1359
|
+
}
|
|
1360
|
+
return fallback ?? '';
|
|
1361
|
+
}
|
|
1362
|
+
renderAssistantBlock(type, content, metadata) {
|
|
1363
|
+
if (!this.assistantBlockRenderer) {
|
|
1364
|
+
return;
|
|
1365
|
+
}
|
|
1366
|
+
this.assistantBlockRenderer.renderBlock(type, content, metadata);
|
|
1367
|
+
}
|
|
1235
1368
|
isStreamingUiActive() {
|
|
1236
1369
|
return this.streamingHeartbeatStart !== null;
|
|
1237
1370
|
}
|
|
@@ -1280,19 +1413,23 @@ export class InteractiveShell {
|
|
|
1280
1413
|
// Surface meta header (elapsed + context usage) above the divider
|
|
1281
1414
|
// Use streaming elapsed time if available, otherwise fall back to status line state
|
|
1282
1415
|
let elapsedSeconds = null;
|
|
1283
|
-
|
|
1416
|
+
const shouldShowElapsed = this.streamingHeartbeatStart !== null || this.isProcessing;
|
|
1417
|
+
if (this.streamingHeartbeatStart && shouldShowElapsed) {
|
|
1284
1418
|
// Actively streaming - compute live elapsed
|
|
1285
1419
|
elapsedSeconds = Math.max(0, Math.floor((Date.now() - this.streamingHeartbeatStart) / 1000));
|
|
1286
1420
|
}
|
|
1287
|
-
else if (this.lastStreamingElapsedSeconds !== null) {
|
|
1421
|
+
else if (shouldShowElapsed && this.lastStreamingElapsedSeconds !== null) {
|
|
1288
1422
|
// Just finished streaming - use preserved final time
|
|
1289
1423
|
elapsedSeconds = this.lastStreamingElapsedSeconds;
|
|
1290
1424
|
}
|
|
1291
|
-
else if (this.statusLineState) {
|
|
1425
|
+
else if (shouldShowElapsed && this.statusLineState) {
|
|
1292
1426
|
// Fallback to status line state elapsed
|
|
1293
1427
|
elapsedSeconds = Math.max(0, Math.floor((Date.now() - this.statusLineState.startedAt) / 1000));
|
|
1294
1428
|
}
|
|
1295
|
-
const
|
|
1429
|
+
const hasThoughtSummary = !!this.latestThoughtSummary;
|
|
1430
|
+
const thinkingMs = hasThoughtSummary && display.isSpinnerActive()
|
|
1431
|
+
? display.getThinkingElapsedMs()
|
|
1432
|
+
: null;
|
|
1296
1433
|
const tokensUsed = this.latestTokenUsage.used;
|
|
1297
1434
|
const tokenLimit = this.latestTokenUsage.limit ?? this.activeContextWindowTokens;
|
|
1298
1435
|
this.terminalInput.setMetaStatus({
|
|
@@ -1300,7 +1437,7 @@ export class InteractiveShell {
|
|
|
1300
1437
|
tokensUsed,
|
|
1301
1438
|
tokenLimit,
|
|
1302
1439
|
thinkingMs,
|
|
1303
|
-
thinkingHasContent:
|
|
1440
|
+
thinkingHasContent: hasThoughtSummary,
|
|
1304
1441
|
});
|
|
1305
1442
|
// Keep model/provider visible in the controls bar
|
|
1306
1443
|
this.terminalInput.setModelContext({
|
|
@@ -1407,14 +1544,17 @@ export class InteractiveShell {
|
|
|
1407
1544
|
this.stopStreamingHeartbeat();
|
|
1408
1545
|
// Enter global streaming mode - blocks all non-streaming UI output
|
|
1409
1546
|
enterStreamingMode();
|
|
1547
|
+
this.resetAssistantStreamBuffer();
|
|
1548
|
+
this.streamingStatusBase = label;
|
|
1549
|
+
this.streamingStatusDetail = null;
|
|
1550
|
+
this.latestThoughtSummary = null;
|
|
1551
|
+
this.lastStreamingElapsedSeconds = null;
|
|
1410
1552
|
// Set up scroll region for streaming content
|
|
1411
1553
|
this.terminalInput.enterStreamingScrollRegion();
|
|
1412
1554
|
this.uiUpdates.setMode('streaming');
|
|
1413
1555
|
this.streamingHeartbeatStart = Date.now();
|
|
1414
1556
|
this.streamingHeartbeatFrame = 0;
|
|
1415
|
-
|
|
1416
|
-
this.streamingStatusLabel = this.buildStreamingStatus(`${initialFrame} ${label}`, 0);
|
|
1417
|
-
display.updateStreamingStatus(this.streamingStatusLabel);
|
|
1557
|
+
this.rebuildStreamingStatusLabel();
|
|
1418
1558
|
this.refreshStatusLine(true);
|
|
1419
1559
|
// Periodically refresh the pinned input/status region while streaming so
|
|
1420
1560
|
// elapsed time remains visible without interrupting the scroll region.
|
|
@@ -1424,14 +1564,9 @@ export class InteractiveShell {
|
|
|
1424
1564
|
mode: ['streaming', 'processing'],
|
|
1425
1565
|
coalesceKey: 'streaming:heartbeat',
|
|
1426
1566
|
run: () => {
|
|
1427
|
-
const elapsedSeconds = this.streamingHeartbeatStart
|
|
1428
|
-
? Math.max(0, Math.floor((Date.now() - this.streamingHeartbeatStart) / 1000))
|
|
1429
|
-
: 0;
|
|
1430
1567
|
this.streamingHeartbeatFrame =
|
|
1431
1568
|
(this.streamingHeartbeatFrame + 1) % STREAMING_SPINNER_FRAMES.length;
|
|
1432
|
-
|
|
1433
|
-
this.streamingStatusLabel = this.buildStreamingStatus(`${frame} ${label}`, elapsedSeconds);
|
|
1434
|
-
display.updateStreamingStatus(this.streamingStatusLabel);
|
|
1569
|
+
this.rebuildStreamingStatusLabel();
|
|
1435
1570
|
// Update parallel agent display during streaming
|
|
1436
1571
|
const manager = getParallelAgentManager();
|
|
1437
1572
|
if (manager.isRunning()) {
|
|
@@ -1455,6 +1590,9 @@ export class InteractiveShell {
|
|
|
1455
1590
|
this.streamingHeartbeatStart = null;
|
|
1456
1591
|
this.streamingHeartbeatFrame = 0;
|
|
1457
1592
|
this.streamingStatusLabel = null;
|
|
1593
|
+
this.streamingStatusBase = null;
|
|
1594
|
+
this.streamingStatusDetail = null;
|
|
1595
|
+
this.latestThoughtSummary = null;
|
|
1458
1596
|
// Clear streaming label specifically (keeps override and main status if set)
|
|
1459
1597
|
this.terminalInput.setStreamingLabel(null);
|
|
1460
1598
|
// Clear streaming status from display
|
|
@@ -1464,9 +1602,25 @@ export class InteractiveShell {
|
|
|
1464
1602
|
}
|
|
1465
1603
|
buildStreamingStatus(label, _elapsedSeconds) {
|
|
1466
1604
|
// Model + elapsed time already live in the pinned meta header; keep the streaming
|
|
1467
|
-
// status focused on the activity
|
|
1605
|
+
// status focused on the activity and most recent thought summary.
|
|
1468
1606
|
const prefix = theme.info('⏺');
|
|
1469
|
-
|
|
1607
|
+
const parts = [label.trim()];
|
|
1608
|
+
if (this.streamingStatusDetail) {
|
|
1609
|
+
const detail = this.streamingStatusDetail.length > 52
|
|
1610
|
+
? `${this.streamingStatusDetail.slice(0, 51)}…`
|
|
1611
|
+
: this.streamingStatusDetail;
|
|
1612
|
+
parts.push(theme.ui.muted(detail));
|
|
1613
|
+
}
|
|
1614
|
+
return `${prefix} ${parts.join(' · ')}`.trim();
|
|
1615
|
+
}
|
|
1616
|
+
rebuildStreamingStatusLabel() {
|
|
1617
|
+
if (this.streamingHeartbeatStart === null) {
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
const frame = STREAMING_SPINNER_FRAMES[this.streamingHeartbeatFrame];
|
|
1621
|
+
const base = this.streamingStatusBase ?? 'Streaming';
|
|
1622
|
+
this.streamingStatusLabel = this.buildStreamingStatus(`${frame} ${base}`);
|
|
1623
|
+
display.updateStreamingStatus(this.streamingStatusLabel);
|
|
1470
1624
|
}
|
|
1471
1625
|
formatElapsedShort(seconds) {
|
|
1472
1626
|
if (seconds < 60) {
|
|
@@ -1509,7 +1663,14 @@ export class InteractiveShell {
|
|
|
1509
1663
|
this.refreshQueueIndicators();
|
|
1510
1664
|
return;
|
|
1511
1665
|
}
|
|
1666
|
+
if (this.apiKeyGateActive) {
|
|
1667
|
+
this.refreshQueueIndicators();
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1512
1670
|
queueMicrotask(() => {
|
|
1671
|
+
if (this.apiKeyGateActive) {
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1513
1674
|
void this.processQueuedActions();
|
|
1514
1675
|
});
|
|
1515
1676
|
}
|
|
@@ -1517,12 +1678,12 @@ export class InteractiveShell {
|
|
|
1517
1678
|
* Process queued follow-up actions.
|
|
1518
1679
|
*/
|
|
1519
1680
|
async processQueuedActions() {
|
|
1520
|
-
if (this.isDrainingQueue || this.isProcessing || !this.followUpQueue.length) {
|
|
1681
|
+
if (this.apiKeyGateActive || this.isDrainingQueue || this.isProcessing || !this.followUpQueue.length) {
|
|
1521
1682
|
return;
|
|
1522
1683
|
}
|
|
1523
1684
|
this.isDrainingQueue = true;
|
|
1524
1685
|
try {
|
|
1525
|
-
while (!this.isProcessing && this.followUpQueue.length) {
|
|
1686
|
+
while (!this.isProcessing && !this.apiKeyGateActive && this.followUpQueue.length) {
|
|
1526
1687
|
const next = this.followUpQueue.shift();
|
|
1527
1688
|
const remaining = this.followUpQueue.length;
|
|
1528
1689
|
const label = next.type === 'continuous' ? 'continuous command' : 'follow-up';
|
|
@@ -1669,6 +1830,99 @@ export class InteractiveShell {
|
|
|
1669
1830
|
];
|
|
1670
1831
|
return `${playbook.join('\n')}\n\nPrimary user request:\n${request.trim()}`;
|
|
1671
1832
|
}
|
|
1833
|
+
buildRunLogExcerpt(startIndex) {
|
|
1834
|
+
const buffer = this.terminalInput.getScrollbackBuffer();
|
|
1835
|
+
const sinceStart = buffer.slice(Math.max(0, startIndex));
|
|
1836
|
+
const excerpt = sinceStart.slice(-200); // Cap prompt size
|
|
1837
|
+
return excerpt.join('\n').trim();
|
|
1838
|
+
}
|
|
1839
|
+
buildRunLogEntry(request, response, scrollbackStartIndex, meta) {
|
|
1840
|
+
return {
|
|
1841
|
+
id: ++this.runIdCounter,
|
|
1842
|
+
request,
|
|
1843
|
+
response,
|
|
1844
|
+
timestamp: new Date().toISOString(),
|
|
1845
|
+
alphaZero: meta.alphaZeroEngaged,
|
|
1846
|
+
difficult: meta.alphaZeroDifficult,
|
|
1847
|
+
failureType: meta.failureType,
|
|
1848
|
+
outputExcerpt: this.buildRunLogExcerpt(scrollbackStartIndex),
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
buildAlphaZeroReflectionPrompt(runLog, options) {
|
|
1852
|
+
const lines = [];
|
|
1853
|
+
lines.push('AlphaZero Post-Run Self-Reflection (erosolar-cli)');
|
|
1854
|
+
lines.push(`Timestamp: ${runLog.timestamp}`);
|
|
1855
|
+
lines.push(`AlphaZero: ${runLog.alphaZero ? 'on' : 'off'}${runLog.difficult ? ' | difficult' : ''}${runLog.failureType ? ` | signal: ${runLog.failureType}` : ''}`);
|
|
1856
|
+
lines.push('');
|
|
1857
|
+
lines.push('User request:');
|
|
1858
|
+
lines.push(runLog.request);
|
|
1859
|
+
lines.push('');
|
|
1860
|
+
lines.push('Previous run log excerpt:');
|
|
1861
|
+
lines.push(runLog.outputExcerpt || '[empty]');
|
|
1862
|
+
lines.push('');
|
|
1863
|
+
lines.push('Instructions:');
|
|
1864
|
+
lines.push('- Reflect on the log to spot erosolar-cli bugs, UX issues, or reliability gaps.');
|
|
1865
|
+
lines.push('- Propose and apply targeted fixes in this repository only (no user workspace edits).');
|
|
1866
|
+
lines.push('- Prefer small, test-backed changes; run any relevant checks you invoke.');
|
|
1867
|
+
lines.push('- Keep notes concise and finish with applied changes plus follow-ups.');
|
|
1868
|
+
if (options.autoChain) {
|
|
1869
|
+
lines.push('- Continue iterating automatically while meaningful improvements remain.');
|
|
1870
|
+
lines.push('- When no further improvements are possible, reply with NO_MORE_IMPROVEMENTS on its own line.');
|
|
1871
|
+
}
|
|
1872
|
+
return lines.join('\n');
|
|
1873
|
+
}
|
|
1874
|
+
maybeQueueAlphaZeroSelfReflection(runLog, alphaZeroEngaged, allowAutoChain) {
|
|
1875
|
+
if (!alphaZeroEngaged) {
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
if (!isErosolarRepo(this.workingDir)) {
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
if (!runLog.outputExcerpt) {
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
if (this.lastReflectedRunId === runLog.id) {
|
|
1885
|
+
return;
|
|
1886
|
+
}
|
|
1887
|
+
if (allowAutoChain) {
|
|
1888
|
+
if (!this.autoContinueEnabled) {
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
if (this.alphaZeroAutoImproveIterations >= this.alphaZeroAutoImproveMaxIterations) {
|
|
1892
|
+
display.showInfo('AlphaZero auto-improvement limit reached; stopping.');
|
|
1893
|
+
this.alphaZeroAutoImproveActive = false;
|
|
1894
|
+
this.alphaZeroAutoImproveIterations = 0;
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
if (!this.alphaZeroAutoImproveActive) {
|
|
1898
|
+
this.alphaZeroAutoImproveIterations = 0;
|
|
1899
|
+
}
|
|
1900
|
+
this.alphaZeroAutoImproveActive = true;
|
|
1901
|
+
this.alphaZeroAutoImproveIterations++;
|
|
1902
|
+
}
|
|
1903
|
+
const prompt = this.buildAlphaZeroReflectionPrompt(runLog, { autoChain: allowAutoChain });
|
|
1904
|
+
this.lastReflectedRunId = runLog.id;
|
|
1905
|
+
this.skipNextAutoReflection = !allowAutoChain; // Prevent reflection-on-reflection unless auto-chaining
|
|
1906
|
+
this.enqueueFollowUpAction({ type: 'request', text: prompt });
|
|
1907
|
+
display.showInfo(allowAutoChain
|
|
1908
|
+
? 'Auto AlphaZero self-improvement queued (auto-continue enabled).'
|
|
1909
|
+
: 'Queued AlphaZero self-reflection to improve erosolar-cli from the latest run log.');
|
|
1910
|
+
}
|
|
1911
|
+
shouldStopAlphaZeroAutoImprove(responseText, allowAutoChain) {
|
|
1912
|
+
if (!this.alphaZeroAutoImproveActive) {
|
|
1913
|
+
return false;
|
|
1914
|
+
}
|
|
1915
|
+
if (!allowAutoChain) {
|
|
1916
|
+
return true;
|
|
1917
|
+
}
|
|
1918
|
+
if (!responseText) {
|
|
1919
|
+
return false;
|
|
1920
|
+
}
|
|
1921
|
+
const normalized = responseText.toLowerCase();
|
|
1922
|
+
return (normalized.includes('no_more_improvements') ||
|
|
1923
|
+
normalized.includes('no more improvements') ||
|
|
1924
|
+
normalized.includes('stop_auto_improve'));
|
|
1925
|
+
}
|
|
1672
1926
|
async handlePendingInteraction(input) {
|
|
1673
1927
|
if (!this.pendingInteraction) {
|
|
1674
1928
|
return false;
|
|
@@ -1710,170 +1964,197 @@ export class InteractiveShell {
|
|
|
1710
1964
|
this.renderPromptArea();
|
|
1711
1965
|
return;
|
|
1712
1966
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1967
|
+
// Keep the slash action visible in the pinned recent strip
|
|
1968
|
+
this.terminalInput.recordRecentAction(command);
|
|
1969
|
+
const runCommand = async () => {
|
|
1970
|
+
switch (command) {
|
|
1971
|
+
case '/help':
|
|
1972
|
+
case '/?':
|
|
1973
|
+
this.showHelp();
|
|
1974
|
+
break;
|
|
1975
|
+
case '/features':
|
|
1976
|
+
this.showFeaturesMenu(input);
|
|
1977
|
+
break;
|
|
1978
|
+
case '/learn':
|
|
1979
|
+
this.showLearningStatus(input);
|
|
1980
|
+
break;
|
|
1981
|
+
case '/improve':
|
|
1982
|
+
void this.handleImprovementCommand(input);
|
|
1983
|
+
break;
|
|
1984
|
+
case '/model':
|
|
1985
|
+
this.showModelMenu();
|
|
1986
|
+
break;
|
|
1987
|
+
case '/exit':
|
|
1988
|
+
case '/quit':
|
|
1989
|
+
case '/q':
|
|
1990
|
+
this.shutdown();
|
|
1991
|
+
break;
|
|
1992
|
+
case '/secrets':
|
|
1993
|
+
this.showSecretsMenu();
|
|
1994
|
+
break;
|
|
1995
|
+
case '/tools':
|
|
1996
|
+
this.showToolsMenu();
|
|
1997
|
+
break;
|
|
1998
|
+
case '/mcp':
|
|
1999
|
+
await this.showMcpStatus();
|
|
2000
|
+
break;
|
|
2001
|
+
case '/doctor':
|
|
2002
|
+
this.runDoctor();
|
|
2003
|
+
break;
|
|
2004
|
+
case '/checks':
|
|
2005
|
+
await this.runRepoChecksCommand();
|
|
2006
|
+
break;
|
|
2007
|
+
case '/context':
|
|
2008
|
+
await this.refreshWorkspaceContextCommand(input);
|
|
2009
|
+
break;
|
|
2010
|
+
case '/agents':
|
|
2011
|
+
this.showAgentsMenu();
|
|
2012
|
+
break;
|
|
2013
|
+
case '/sessions':
|
|
2014
|
+
await this.handleSessionCommand(input);
|
|
2015
|
+
break;
|
|
2016
|
+
case '/skills':
|
|
2017
|
+
await this.handleSkillsCommand(input);
|
|
2018
|
+
break;
|
|
2019
|
+
case '/thinking':
|
|
2020
|
+
this.handleThinkingCommand(input);
|
|
2021
|
+
break;
|
|
2022
|
+
case '/autocontinue':
|
|
2023
|
+
this.handleAutoContinueCommand(input);
|
|
2024
|
+
break;
|
|
2025
|
+
case '/alphazero':
|
|
2026
|
+
this.handleAlphaZeroCommand(input);
|
|
2027
|
+
break;
|
|
2028
|
+
case '/shortcuts':
|
|
2029
|
+
case '/keys':
|
|
2030
|
+
this.handleShortcutsCommand();
|
|
2031
|
+
break;
|
|
2032
|
+
case '/changes':
|
|
2033
|
+
case '/summary':
|
|
2034
|
+
this.showFileChangeSummary();
|
|
2035
|
+
break;
|
|
2036
|
+
case '/metrics':
|
|
2037
|
+
case '/stats':
|
|
2038
|
+
case '/perf':
|
|
2039
|
+
this.showAlphaZeroMetrics();
|
|
2040
|
+
break;
|
|
2041
|
+
case '/suggestions':
|
|
2042
|
+
case '/improve':
|
|
2043
|
+
this.showImprovementSuggestions();
|
|
2044
|
+
break;
|
|
2045
|
+
case '/plugins':
|
|
2046
|
+
this.showPluginStatus();
|
|
2047
|
+
break;
|
|
2048
|
+
case '/evolve':
|
|
2049
|
+
void this.handleEvolveCommand(input);
|
|
2050
|
+
break;
|
|
2051
|
+
case '/modular':
|
|
2052
|
+
case '/a0':
|
|
2053
|
+
void this.handleModularCommand(input);
|
|
2054
|
+
break;
|
|
2055
|
+
case '/offsec':
|
|
2056
|
+
void this.handleOffsecCommand(input);
|
|
2057
|
+
break;
|
|
2058
|
+
case '/test':
|
|
2059
|
+
case '/tests':
|
|
2060
|
+
void this.handleTestCommand(input);
|
|
2061
|
+
break;
|
|
2062
|
+
case '/provider':
|
|
2063
|
+
await this.handleProviderCommand(input);
|
|
2064
|
+
break;
|
|
2065
|
+
case '/providers':
|
|
2066
|
+
this.showConfiguredProviders();
|
|
2067
|
+
break;
|
|
2068
|
+
case '/local':
|
|
2069
|
+
await this.handleLocalCommand(input);
|
|
2070
|
+
break;
|
|
2071
|
+
case '/discover':
|
|
2072
|
+
await this.discoverModelsCommand();
|
|
2073
|
+
break;
|
|
2074
|
+
// Claude Code style commands
|
|
2075
|
+
case '/rewind':
|
|
2076
|
+
await this.handleRewindCommand(input);
|
|
2077
|
+
break;
|
|
2078
|
+
case '/memory':
|
|
2079
|
+
this.handleMemoryCommand(input);
|
|
2080
|
+
break;
|
|
2081
|
+
case '/vim':
|
|
2082
|
+
this.handleVimCommand();
|
|
2083
|
+
break;
|
|
2084
|
+
case '/output-style':
|
|
2085
|
+
this.handleOutputStyleCommand(input);
|
|
2086
|
+
break;
|
|
2087
|
+
case '/cost':
|
|
2088
|
+
this.handleCostCommand();
|
|
2089
|
+
break;
|
|
2090
|
+
case '/usage':
|
|
2091
|
+
this.handleUsageCommand();
|
|
2092
|
+
break;
|
|
2093
|
+
case '/update':
|
|
2094
|
+
await this.handleUpdateCommand();
|
|
2095
|
+
break;
|
|
2096
|
+
case '/clear':
|
|
2097
|
+
this.handleClearCommand();
|
|
2098
|
+
break;
|
|
2099
|
+
case '/resume':
|
|
2100
|
+
await this.handleResumeCommand(input);
|
|
2101
|
+
break;
|
|
2102
|
+
case '/export':
|
|
2103
|
+
this.handleExportCommand(input);
|
|
2104
|
+
break;
|
|
2105
|
+
case '/review':
|
|
2106
|
+
await this.handleReviewCommand();
|
|
2107
|
+
break;
|
|
2108
|
+
case '/security-review':
|
|
2109
|
+
await this.handleSecurityReviewCommand();
|
|
2110
|
+
break;
|
|
2111
|
+
case '/bug':
|
|
2112
|
+
this.handleBugCommand();
|
|
2113
|
+
break;
|
|
2114
|
+
case '/terminal-setup':
|
|
2115
|
+
this.handleTerminalSetupCommand();
|
|
2116
|
+
break;
|
|
2117
|
+
case '/permissions':
|
|
2118
|
+
this.handlePermissionsCommand();
|
|
2119
|
+
break;
|
|
2120
|
+
case '/init':
|
|
2121
|
+
this.handleInitCommand();
|
|
2122
|
+
break;
|
|
2123
|
+
case '/compact':
|
|
2124
|
+
await this.handleCompactCommand();
|
|
2125
|
+
break;
|
|
2126
|
+
default:
|
|
2127
|
+
if (!(await this.tryCustomSlashCommand(command, input))) {
|
|
2128
|
+
display.showWarning(`Unknown command "${command}".`);
|
|
2129
|
+
}
|
|
2130
|
+
break;
|
|
2131
|
+
}
|
|
2132
|
+
};
|
|
2133
|
+
let capturedOutput = '';
|
|
2134
|
+
try {
|
|
2135
|
+
const { output: outputBuffer } = await display.captureOutput(runCommand);
|
|
2136
|
+
capturedOutput = outputBuffer;
|
|
2137
|
+
}
|
|
2138
|
+
catch (error) {
|
|
2139
|
+
capturedOutput = error?.capturedOutput ?? capturedOutput;
|
|
2140
|
+
display.showError(error instanceof Error ? error.message : String(error), error);
|
|
1874
2141
|
}
|
|
2142
|
+
const panelContent = this.buildInlineCommandPanel(command, capturedOutput);
|
|
2143
|
+
this.terminalInput.setInlineCommandPanel(panelContent);
|
|
1875
2144
|
this.renderPromptArea();
|
|
1876
2145
|
}
|
|
2146
|
+
buildInlineCommandPanel(command, output) {
|
|
2147
|
+
const normalized = output ? output.replace(/\r\n/g, '\n').replace(/\r/g, '\n').trimEnd() : '';
|
|
2148
|
+
if (!normalized) {
|
|
2149
|
+
return null;
|
|
2150
|
+
}
|
|
2151
|
+
const header = theme.ui.muted(command);
|
|
2152
|
+
const body = normalized.split('\n');
|
|
2153
|
+
if (body.length === 1) {
|
|
2154
|
+
return [`${header} ${body[0] ?? ''}`.trimEnd()];
|
|
2155
|
+
}
|
|
2156
|
+
return [header, ...body];
|
|
2157
|
+
}
|
|
1877
2158
|
async tryCustomSlashCommand(command, fullInput) {
|
|
1878
2159
|
const custom = this.customCommandMap.get(command);
|
|
1879
2160
|
if (!custom) {
|
|
@@ -2300,6 +2581,7 @@ export class InteractiveShell {
|
|
|
2300
2581
|
const updated = toggleFeatureFlag(matchedKey, newValue);
|
|
2301
2582
|
const status = updated[matchedKey] ? theme.success('enabled') : theme.ui.muted('disabled');
|
|
2302
2583
|
display.showInfo(`Feature "${FEATURE_FLAG_INFO[matchedKey].label}" is now ${status}.`);
|
|
2584
|
+
this.refreshFeatureStatusDisplay();
|
|
2303
2585
|
display.showInfo('Changes will take effect on next launch or after /features refresh.');
|
|
2304
2586
|
return;
|
|
2305
2587
|
}
|
|
@@ -2312,6 +2594,7 @@ export class InteractiveShell {
|
|
|
2312
2594
|
}
|
|
2313
2595
|
saveFeatureFlags(updated);
|
|
2314
2596
|
display.showInfo(`All features ${newValue ? theme.success('enabled') : theme.ui.muted('disabled')}.`);
|
|
2597
|
+
this.refreshFeatureStatusDisplay();
|
|
2315
2598
|
return;
|
|
2316
2599
|
}
|
|
2317
2600
|
else {
|
|
@@ -3484,9 +3767,7 @@ export class InteractiveShell {
|
|
|
3484
3767
|
}
|
|
3485
3768
|
display.showInfo(`Deleted session "${summary.title}".`);
|
|
3486
3769
|
if (this.activeSessionId === summary.id) {
|
|
3487
|
-
this.
|
|
3488
|
-
this.activeSessionTitle = null;
|
|
3489
|
-
saveSessionPreferences({ lastSessionId: null });
|
|
3770
|
+
this.updateActiveSession(null, true);
|
|
3490
3771
|
}
|
|
3491
3772
|
}
|
|
3492
3773
|
newSessionCommand(title) {
|
|
@@ -3506,6 +3787,7 @@ export class InteractiveShell {
|
|
|
3506
3787
|
clearAutosaveSnapshot(this.profile);
|
|
3507
3788
|
display.showInfo('Started a new empty session.');
|
|
3508
3789
|
this.refreshContextGauge();
|
|
3790
|
+
this.refreshFeatureStatusDisplay();
|
|
3509
3791
|
}
|
|
3510
3792
|
toggleAutosaveCommand(value) {
|
|
3511
3793
|
if (!value) {
|
|
@@ -3823,6 +4105,7 @@ export class InteractiveShell {
|
|
|
3823
4105
|
if (remember) {
|
|
3824
4106
|
saveSessionPreferences({ lastSessionId: summary?.id ?? null });
|
|
3825
4107
|
}
|
|
4108
|
+
this.refreshFeatureStatusDisplay();
|
|
3826
4109
|
}
|
|
3827
4110
|
resolveSessionBySelector(selector) {
|
|
3828
4111
|
const sessions = listSessions(this.profile);
|
|
@@ -4689,8 +4972,10 @@ export class InteractiveShell {
|
|
|
4689
4972
|
if (trimmed.toLowerCase() === 'cancel') {
|
|
4690
4973
|
this.pendingInteraction = null;
|
|
4691
4974
|
this.pendingSecretRetry = null;
|
|
4975
|
+
this.apiKeyGateActive = false;
|
|
4692
4976
|
display.showInfo('Secret unchanged.');
|
|
4693
4977
|
this.renderPromptArea();
|
|
4978
|
+
this.scheduleQueueProcessing();
|
|
4694
4979
|
return;
|
|
4695
4980
|
}
|
|
4696
4981
|
try {
|
|
@@ -4699,6 +4984,7 @@ export class InteractiveShell {
|
|
|
4699
4984
|
this.pendingInteraction = null;
|
|
4700
4985
|
const deferred = this.pendingSecretRetry;
|
|
4701
4986
|
this.pendingSecretRetry = null;
|
|
4987
|
+
this.apiKeyGateActive = false;
|
|
4702
4988
|
if (pending.secret.providers.includes(this.sessionState.provider)) {
|
|
4703
4989
|
if (this.rebuildAgent()) {
|
|
4704
4990
|
this.resetChatBoxAfterModelSwap();
|
|
@@ -4713,8 +4999,10 @@ export class InteractiveShell {
|
|
|
4713
4999
|
display.showError(message);
|
|
4714
5000
|
this.pendingInteraction = null;
|
|
4715
5001
|
this.pendingSecretRetry = null;
|
|
5002
|
+
this.apiKeyGateActive = false;
|
|
4716
5003
|
}
|
|
4717
5004
|
this.renderPromptArea();
|
|
5005
|
+
this.scheduleQueueProcessing();
|
|
4718
5006
|
}
|
|
4719
5007
|
async processRequest(userRequest) {
|
|
4720
5008
|
if (this.isProcessing) {
|
|
@@ -4729,11 +5017,15 @@ export class InteractiveShell {
|
|
|
4729
5017
|
if (!agent) {
|
|
4730
5018
|
return;
|
|
4731
5019
|
}
|
|
5020
|
+
this.hasShownThoughtProcess = false;
|
|
4732
5021
|
const alphaZeroEngaged = this.alphaZeroModeEnabled;
|
|
4733
5022
|
const alphaZeroDifficult = alphaZeroEngaged ? this.isDifficultProblem(userRequest) : false;
|
|
4734
5023
|
const requestForAgent = alphaZeroEngaged
|
|
4735
5024
|
? this.buildAlphaZeroPrompt(userRequest, alphaZeroDifficult)
|
|
4736
5025
|
: userRequest;
|
|
5026
|
+
const skipReflectionForThisRun = this.skipNextAutoReflection;
|
|
5027
|
+
this.skipNextAutoReflection = false;
|
|
5028
|
+
const scrollbackStartIndex = this.terminalInput.getScrollbackBuffer().length;
|
|
4737
5029
|
const alphaZeroStatusId = 'alpha-zero';
|
|
4738
5030
|
let alphaZeroStatusApplied = false;
|
|
4739
5031
|
let alphaZeroTaskStarted = false;
|
|
@@ -4767,6 +5059,8 @@ export class InteractiveShell {
|
|
|
4767
5059
|
this.uiAdapter.startProcessing('Working on your request');
|
|
4768
5060
|
this.setProcessingStatus();
|
|
4769
5061
|
let responseText = '';
|
|
5062
|
+
let detectedFailure = null;
|
|
5063
|
+
let hadUnhandledError = false;
|
|
4770
5064
|
try {
|
|
4771
5065
|
// Start streaming - no header needed, the input area already provides context
|
|
4772
5066
|
this.startStreamingHeartbeat(alphaZeroEngaged ? 'AlphaZero RL' : 'Streaming response');
|
|
@@ -4789,18 +5083,18 @@ export class InteractiveShell {
|
|
|
4789
5083
|
duration: 0,
|
|
4790
5084
|
}));
|
|
4791
5085
|
// AlphaZero: Check for failure in response
|
|
4792
|
-
|
|
5086
|
+
detectedFailure = detectFailure(responseText, {
|
|
4793
5087
|
toolCalls: this.currentToolCalls,
|
|
4794
5088
|
userMessage: userRequest,
|
|
4795
5089
|
});
|
|
4796
5090
|
if (alphaZeroEngaged && alphaZeroTaskStarted && !alphaZeroTaskCompleted) {
|
|
4797
|
-
this.alphaZeroMetrics.completeAlphaZeroTask(!
|
|
5091
|
+
this.alphaZeroMetrics.completeAlphaZeroTask(!detectedFailure);
|
|
4798
5092
|
alphaZeroTaskCompleted = true;
|
|
4799
5093
|
}
|
|
4800
|
-
if (
|
|
4801
|
-
this.lastFailure =
|
|
5094
|
+
if (detectedFailure) {
|
|
5095
|
+
this.lastFailure = detectedFailure;
|
|
4802
5096
|
// Check if we have a recovery strategy
|
|
4803
|
-
const strategy = findRecoveryStrategy(
|
|
5097
|
+
const strategy = findRecoveryStrategy(detectedFailure);
|
|
4804
5098
|
if (strategy) {
|
|
4805
5099
|
display.showSystemMessage(`🔄 Found recovery strategy for this type of issue (success rate: ${Math.round(strategy.successRate * 100)}%)`);
|
|
4806
5100
|
}
|
|
@@ -4824,6 +5118,7 @@ export class InteractiveShell {
|
|
|
4824
5118
|
}
|
|
4825
5119
|
catch (error) {
|
|
4826
5120
|
const handled = this.handleProviderError(error, () => this.processRequest(userRequest));
|
|
5121
|
+
hadUnhandledError = !handled;
|
|
4827
5122
|
if (alphaZeroEngaged && alphaZeroTaskStarted && !alphaZeroTaskCompleted) {
|
|
4828
5123
|
this.alphaZeroMetrics.completeAlphaZeroTask(false);
|
|
4829
5124
|
alphaZeroTaskCompleted = true;
|
|
@@ -4834,6 +5129,24 @@ export class InteractiveShell {
|
|
|
4834
5129
|
}
|
|
4835
5130
|
}
|
|
4836
5131
|
finally {
|
|
5132
|
+
const runLogEntry = this.buildRunLogEntry(userRequest, responseText, scrollbackStartIndex, {
|
|
5133
|
+
alphaZeroEngaged,
|
|
5134
|
+
alphaZeroDifficult,
|
|
5135
|
+
failureType: detectedFailure?.type ?? (hadUnhandledError ? 'unhandled-error' : null),
|
|
5136
|
+
});
|
|
5137
|
+
this.lastRunLog = runLogEntry;
|
|
5138
|
+
const allowAutoChain = alphaZeroEngaged && this.autoContinueEnabled && isErosolarRepo(this.workingDir);
|
|
5139
|
+
let shouldStopAuto = false;
|
|
5140
|
+
if (this.alphaZeroAutoImproveActive) {
|
|
5141
|
+
shouldStopAuto = this.shouldStopAlphaZeroAutoImprove(responseText, allowAutoChain);
|
|
5142
|
+
if (shouldStopAuto) {
|
|
5143
|
+
this.alphaZeroAutoImproveActive = false;
|
|
5144
|
+
this.alphaZeroAutoImproveIterations = 0;
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
if (!skipReflectionForThisRun && !shouldStopAuto) {
|
|
5148
|
+
this.maybeQueueAlphaZeroSelfReflection(runLogEntry, alphaZeroEngaged, allowAutoChain);
|
|
5149
|
+
}
|
|
4837
5150
|
if (alphaZeroEngaged && alphaZeroStatusApplied) {
|
|
4838
5151
|
this.statusTracker.clearOverride(alphaZeroStatusId);
|
|
4839
5152
|
}
|
|
@@ -4884,6 +5197,7 @@ export class InteractiveShell {
|
|
|
4884
5197
|
if (!agent) {
|
|
4885
5198
|
return;
|
|
4886
5199
|
}
|
|
5200
|
+
this.hasShownThoughtProcess = false;
|
|
4887
5201
|
this.isProcessing = true;
|
|
4888
5202
|
this.uiUpdates.setMode('processing');
|
|
4889
5203
|
this.terminalInput.setStreaming(true);
|
|
@@ -4924,6 +5238,7 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
|
|
|
4924
5238
|
}
|
|
4925
5239
|
while (iteration < MAX_ITERATIONS) {
|
|
4926
5240
|
iteration++;
|
|
5241
|
+
this.hasShownThoughtProcess = false;
|
|
4927
5242
|
display.showSystemMessage(`\n📍 Iteration ${iteration}/${MAX_ITERATIONS}`);
|
|
4928
5243
|
this.updateStatusMessage(`Working on iteration ${iteration}...`);
|
|
4929
5244
|
try {
|
|
@@ -5432,6 +5747,7 @@ What's the next action?`;
|
|
|
5432
5747
|
// Send the error to the agent for fixing
|
|
5433
5748
|
display.showThinking('Analyzing build errors');
|
|
5434
5749
|
this.refreshStatusLine(true);
|
|
5750
|
+
this.hasShownThoughtProcess = false;
|
|
5435
5751
|
const response = await this.withStreamingUi('Fixing build errors', () => this.agent.send(prompt, true));
|
|
5436
5752
|
display.stopThinking();
|
|
5437
5753
|
this.refreshStatusLine(true);
|
|
@@ -5461,29 +5777,40 @@ What's the next action?`;
|
|
|
5461
5777
|
};
|
|
5462
5778
|
this.agent = this.runtimeSession.createAgent(selection, {
|
|
5463
5779
|
onStreamChunk: (chunk) => {
|
|
5780
|
+
if (this.assistantBlocksEnabled) {
|
|
5781
|
+
this.appendAssistantStreamChunk(chunk);
|
|
5782
|
+
return;
|
|
5783
|
+
}
|
|
5464
5784
|
// Stream output using clean streamContent() - chat box floats below
|
|
5465
5785
|
this.terminalInput.streamContent(chunk);
|
|
5466
5786
|
},
|
|
5467
5787
|
onStreamFallback: (info) => this.handleStreamingFallback(info),
|
|
5468
5788
|
onAssistantMessage: (content, metadata) => {
|
|
5469
5789
|
const enriched = this.buildDisplayMetadata(metadata);
|
|
5470
|
-
|
|
5790
|
+
const usingBlocks = this.assistantBlocksEnabled && this.assistantBlockRenderer !== null;
|
|
5791
|
+
const sourceContent = usingBlocks && metadata.wasStreamed ? this.consumeAssistantStreamBuffer(content) : content;
|
|
5792
|
+
const parsed = this.splitThinkingResponse(sourceContent);
|
|
5793
|
+
const thinking = parsed?.thinking ?? null;
|
|
5794
|
+
const shouldRenderThoughtBlock = Boolean(thinking) && !this.hasShownThoughtProcess;
|
|
5795
|
+
const responseContent = parsed ? parsed.response?.trim() ?? '' : sourceContent.trim();
|
|
5796
|
+
const narrativeContent = parsed?.response ?? sourceContent;
|
|
5471
5797
|
if (metadata.isFinal) {
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
}
|
|
5480
|
-
display.showAssistantMessage(parsed.thinking, { ...enriched, isFinal: false });
|
|
5481
|
-
}
|
|
5482
|
-
const finalContent = parsed?.response?.trim() || content;
|
|
5483
|
-
if (finalContent) {
|
|
5484
|
-
display.showAssistantMessage(finalContent, enriched);
|
|
5798
|
+
if (thinking) {
|
|
5799
|
+
// Update status + summary, but skip the legacy UI output when blocks are enabled
|
|
5800
|
+
this.presentThoughtProcess(thinking, enriched, {
|
|
5801
|
+
wasStreamed: usingBlocks || metadata.wasStreamed,
|
|
5802
|
+
});
|
|
5803
|
+
if (usingBlocks && shouldRenderThoughtBlock) {
|
|
5804
|
+
this.renderAssistantBlock('thought', thinking, enriched);
|
|
5485
5805
|
}
|
|
5486
5806
|
}
|
|
5807
|
+
if (usingBlocks) {
|
|
5808
|
+
const body = responseContent || sourceContent;
|
|
5809
|
+
this.renderAssistantBlock('response', body, enriched);
|
|
5810
|
+
}
|
|
5811
|
+
else if (!metadata.wasStreamed && responseContent) {
|
|
5812
|
+
display.showAssistantMessage(responseContent, enriched);
|
|
5813
|
+
}
|
|
5487
5814
|
// Status shown in mode controls bar - no separate status line needed
|
|
5488
5815
|
display.stopThinking();
|
|
5489
5816
|
// Update context usage for mode controls display
|
|
@@ -5499,12 +5826,21 @@ What's the next action?`;
|
|
|
5499
5826
|
void this.enforceAutoTests('final-response');
|
|
5500
5827
|
}
|
|
5501
5828
|
else {
|
|
5829
|
+
if (thinking) {
|
|
5830
|
+
this.presentThoughtProcess(thinking, enriched, {
|
|
5831
|
+
wasStreamed: usingBlocks || metadata.wasStreamed,
|
|
5832
|
+
});
|
|
5833
|
+
}
|
|
5502
5834
|
// Non-final message = narrative text before tool calls (Claude Code style)
|
|
5503
5835
|
// Stop spinner and show the narrative text directly
|
|
5504
5836
|
display.stopThinking();
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5837
|
+
if (usingBlocks) {
|
|
5838
|
+
const body = metadata.wasStreamed ? responseContent || sourceContent : narrativeContent;
|
|
5839
|
+
this.renderAssistantBlock('thought', body, enriched);
|
|
5840
|
+
}
|
|
5841
|
+
else if (!metadata.wasStreamed) {
|
|
5842
|
+
const narrative = parsed?.response ?? content;
|
|
5843
|
+
display.showNarrative(narrative.trim());
|
|
5508
5844
|
}
|
|
5509
5845
|
// The isProcessing flag already shows "⏳ Processing..." - no need for duplicate status
|
|
5510
5846
|
this.requestPromptRefresh();
|
|
@@ -5687,6 +6023,24 @@ What's the next action?`;
|
|
|
5687
6023
|
contextWindowTokens: this.activeContextWindowTokens,
|
|
5688
6024
|
};
|
|
5689
6025
|
}
|
|
6026
|
+
presentThoughtProcess(thinking, metadata, options) {
|
|
6027
|
+
if (!thinking || this.hasShownThoughtProcess) {
|
|
6028
|
+
return;
|
|
6029
|
+
}
|
|
6030
|
+
const summary = this.extractThoughtSummary(thinking);
|
|
6031
|
+
if (summary) {
|
|
6032
|
+
display.updateThinking(`💭 ${summary}`);
|
|
6033
|
+
this.latestThoughtSummary = summary;
|
|
6034
|
+
this.streamingStatusDetail = summary;
|
|
6035
|
+
this.terminalInput.recordRecentAction(`💭 ${summary}`);
|
|
6036
|
+
this.rebuildStreamingStatusLabel();
|
|
6037
|
+
this.refreshStatusLine(true);
|
|
6038
|
+
}
|
|
6039
|
+
if (!options?.wasStreamed) {
|
|
6040
|
+
display.showAssistantMessage(thinking, { ...metadata, isFinal: false });
|
|
6041
|
+
}
|
|
6042
|
+
this.hasShownThoughtProcess = true;
|
|
6043
|
+
}
|
|
5690
6044
|
handleContextTelemetry(metadata, displayMetadata) {
|
|
5691
6045
|
if (!metadata.isFinal) {
|
|
5692
6046
|
return null;
|
|
@@ -6047,7 +6401,7 @@ What's the next action?`;
|
|
|
6047
6401
|
}
|
|
6048
6402
|
handleAgentSetupError(error, retryAction, providerOverride) {
|
|
6049
6403
|
this.pendingInteraction = null;
|
|
6050
|
-
const provider = providerOverride
|
|
6404
|
+
const provider = providerOverride === undefined ? this.sessionState.provider : providerOverride;
|
|
6051
6405
|
const apiKeyIssue = detectApiKeyError(error, provider);
|
|
6052
6406
|
if (apiKeyIssue) {
|
|
6053
6407
|
this.handleApiKeyIssue(apiKeyIssue, retryAction);
|
|
@@ -6062,12 +6416,14 @@ What's the next action?`;
|
|
|
6062
6416
|
const detail = detailText ? ` Error: ${detailText}` : '';
|
|
6063
6417
|
const reason = info.reason ? ` (${info.reason.replace(/-/g, ' ')})` : '';
|
|
6064
6418
|
const partialNote = info.partialResponse ? ' Received partial stream before failure.' : '';
|
|
6419
|
+
this.resetAssistantStreamBuffer();
|
|
6065
6420
|
display.showWarning(`Streaming failed${reason}, retrying without streaming.${detail}${partialNote}`);
|
|
6066
6421
|
this.startStreamingHeartbeat('Fallback in progress');
|
|
6067
6422
|
this.requestPromptRefresh(true);
|
|
6068
6423
|
}
|
|
6069
6424
|
handleProviderError(error, retryAction) {
|
|
6070
|
-
const
|
|
6425
|
+
const providerHint = error instanceof MissingSecretError ? null : this.sessionState.provider;
|
|
6426
|
+
const apiKeyIssue = detectApiKeyError(error, providerHint);
|
|
6071
6427
|
if (!apiKeyIssue) {
|
|
6072
6428
|
return false;
|
|
6073
6429
|
}
|
|
@@ -6076,13 +6432,19 @@ What's the next action?`;
|
|
|
6076
6432
|
}
|
|
6077
6433
|
handleApiKeyIssue(info, retryAction) {
|
|
6078
6434
|
const secret = info.secret ?? null;
|
|
6079
|
-
const providerLabel = info.provider
|
|
6435
|
+
const providerLabel = info.provider
|
|
6436
|
+
? this.providerLabel(info.provider)
|
|
6437
|
+
: secret?.providers?.length
|
|
6438
|
+
? this.providerLabel(secret.providers[0])
|
|
6439
|
+
: null;
|
|
6440
|
+
const targetLabel = providerLabel ?? secret?.label ?? 'this tool';
|
|
6441
|
+
this.apiKeyGateActive = !!secret;
|
|
6080
6442
|
if (!secret) {
|
|
6081
6443
|
this.pendingSecretRetry = null;
|
|
6082
6444
|
const guidance = 'Run "/secrets" to configure the required API key or export it (e.g., EXPORT KEY=value) before launching the CLI.';
|
|
6083
6445
|
const baseMessage = info.type === 'missing'
|
|
6084
|
-
? `An API key is required before using ${
|
|
6085
|
-
: `API authentication failed for ${
|
|
6446
|
+
? `An API key is required before using ${targetLabel}.`
|
|
6447
|
+
: `API authentication failed for ${targetLabel}.`;
|
|
6086
6448
|
display.showWarning(`${baseMessage} ${guidance}`.trim());
|
|
6087
6449
|
return;
|
|
6088
6450
|
}
|
|
@@ -6091,8 +6453,8 @@ What's the next action?`;
|
|
|
6091
6453
|
display.showWarning(info.message.trim());
|
|
6092
6454
|
}
|
|
6093
6455
|
const prefix = isMissing
|
|
6094
|
-
? `${secret.label} is required before you can use ${
|
|
6095
|
-
: `${secret.label} appears to be invalid for ${
|
|
6456
|
+
? `${secret.label} is required before you can use ${targetLabel}.`
|
|
6457
|
+
: `${secret.label} appears to be invalid for ${targetLabel}.`;
|
|
6096
6458
|
display.showWarning(prefix);
|
|
6097
6459
|
this.pendingSecretRetry = retryAction ?? null;
|
|
6098
6460
|
this.pendingInteraction = { type: 'secret-input', secret };
|
|
@@ -6134,10 +6496,6 @@ What's the next action?`;
|
|
|
6134
6496
|
buildBanner() {
|
|
6135
6497
|
const terminalWidth = output.columns ?? 100;
|
|
6136
6498
|
const width = Math.min(terminalWidth - 4, 110);
|
|
6137
|
-
// Collect tool categories for display
|
|
6138
|
-
const toolCategories = this.collectToolCategories();
|
|
6139
|
-
// Load feature flags for banner display
|
|
6140
|
-
const featureFlags = loadFeatureFlags();
|
|
6141
6499
|
return renderSessionFrame({
|
|
6142
6500
|
profileLabel: this.profileLabel,
|
|
6143
6501
|
profileName: this.profile,
|
|
@@ -6146,19 +6504,6 @@ What's the next action?`;
|
|
|
6146
6504
|
workspace: this.workingDir,
|
|
6147
6505
|
version: this.version,
|
|
6148
6506
|
width,
|
|
6149
|
-
features: {
|
|
6150
|
-
verification: this.verificationEnabled,
|
|
6151
|
-
autoContinue: this.autoContinueEnabled,
|
|
6152
|
-
thinkingMode: this.thinkingMode,
|
|
6153
|
-
plugins: this._enabledPlugins,
|
|
6154
|
-
tools: toolCategories,
|
|
6155
|
-
sessionId: this.activeSessionId ?? undefined,
|
|
6156
|
-
// Include feature flags
|
|
6157
|
-
alphaZeroDual: featureFlags.alphaZeroDual,
|
|
6158
|
-
autoCompact: featureFlags.autoCompact,
|
|
6159
|
-
mcpEnabled: featureFlags.mcpEnabled,
|
|
6160
|
-
metrics: featureFlags.metrics,
|
|
6161
|
-
},
|
|
6162
6507
|
});
|
|
6163
6508
|
}
|
|
6164
6509
|
/**
|
|
@@ -6189,6 +6534,21 @@ What's the next action?`;
|
|
|
6189
6534
|
}
|
|
6190
6535
|
return categories;
|
|
6191
6536
|
}
|
|
6537
|
+
buildFeatureStatusSnapshot() {
|
|
6538
|
+
const featureFlags = loadFeatureFlags();
|
|
6539
|
+
const toolCategories = this.collectToolCategories();
|
|
6540
|
+
const toolCount = toolCategories.reduce((sum, cat) => sum + cat.count, 0);
|
|
6541
|
+
const pluginCount = this._enabledPlugins.length;
|
|
6542
|
+
return {
|
|
6543
|
+
pluginCount: pluginCount > 0 ? pluginCount : undefined,
|
|
6544
|
+
toolCount: toolCount > 0 ? toolCount : undefined,
|
|
6545
|
+
sessionId: this.activeSessionId,
|
|
6546
|
+
mcpEnabled: featureFlags.mcpEnabled,
|
|
6547
|
+
metricsEnabled: featureFlags.metrics,
|
|
6548
|
+
autoCompact: featureFlags.autoCompact,
|
|
6549
|
+
dualMode: featureFlags.alphaZeroDual,
|
|
6550
|
+
};
|
|
6551
|
+
}
|
|
6192
6552
|
/**
|
|
6193
6553
|
* Extract category from tool name.
|
|
6194
6554
|
*/
|